92 lines
4.3 KiB
JavaScript
92 lines
4.3 KiB
JavaScript
require("dotenv").config();
|
|
const express = require("express");
|
|
const path = require("path");
|
|
const fs = require("fs");
|
|
const { handleTask } = require("./modules/task-receiver");
|
|
const { sendNotification } = require("./modules/email-notify");
|
|
const { expandPlan } = require("./modules/plan-expander");
|
|
const { call: rawCall } = require("./modules/model-caller");
|
|
const { executePlan } = require("./modules/executor");
|
|
const { createLogger, readLatestRun, listRuns } = require("./modules/logger");
|
|
const { wrapModelCaller, reset, getCallCount, isBudgetExceeded } = require("./modules/cost-guard");
|
|
const { record: writeBrain, recordMilestone, AGENT_ID } = require("./modules/brain-writer");
|
|
|
|
const safeCall = wrapModelCaller(rawCall);
|
|
const app = express();
|
|
app.use(express.json({ limit: "10mb" }));
|
|
const PORT = process.env.PORT || 3003;
|
|
const PLANS_DIR = path.join(__dirname, "plans");
|
|
if (!fs.existsSync(PLANS_DIR)) fs.mkdirSync(PLANS_DIR, { recursive: true });
|
|
|
|
app.get("/health", (req, res) => {
|
|
res.json({ status: "ok", agent: "zhuyuan-dev-agent", version: "3.0", persona: AGENT_ID, constitution: true, api_calls: getCallCount() });
|
|
});
|
|
|
|
app.post("/task", handleTask);
|
|
|
|
app.get("/logs/latest", (req, res) => {
|
|
res.json(readLatestRun() || { message: "暂无" });
|
|
});
|
|
|
|
app.get("/logs/list", (req, res) => {
|
|
res.json(listRuns(20));
|
|
});
|
|
|
|
app.get("/logs/raw", (req, res) => {
|
|
const file = req.query.file;
|
|
if (!file) return res.status(400).json({ error: "缺少 file" });
|
|
const fpath = path.join(__dirname, "logs", file);
|
|
if (!fs.existsSync(fpath)) return res.status(404).json({ error: "不存在" });
|
|
const content = fs.readFileSync(fpath, "utf-8");
|
|
const lines = content.trim().split("\n").map(l => { try { return JSON.parse(l); } catch(e) { return null; } }).filter(Boolean);
|
|
res.json(lines);
|
|
});
|
|
|
|
app.get("/dashboard", (req, res) => {
|
|
const files = fs.readdirSync(PLANS_DIR).filter(f => f.endsWith(".json") && f !== "queue.json");
|
|
const plans = files.map(f => {
|
|
try { const p = JSON.parse(fs.readFileSync(path.join(PLANS_DIR, f), "utf-8")); return { plan_id: p.plan_id, title: p.title, status: p._status, tasks: p.tasks?.length }; }
|
|
catch(e) { return { file: f, error: "解析失败" }; }
|
|
});
|
|
res.json({ agent: "zhuyuan-dev-agent", version: "3.0", persona: AGENT_ID, constitution: true, api_calls_used: getCallCount(), plans });
|
|
});
|
|
|
|
app.post("/execute", async (req, res) => {
|
|
const { plan_id } = req.body;
|
|
if (!plan_id) return res.status(400).json({ error: "缺少 plan_id" });
|
|
const planFile = path.join(PLANS_DIR, plan_id + ".json");
|
|
if (!fs.existsSync(planFile)) return res.status(404).json({ error: "不存在" });
|
|
const plan = JSON.parse(fs.readFileSync(planFile, "utf-8"));
|
|
const logger = createLogger(plan_id);
|
|
reset();
|
|
try {
|
|
const email = process.env.BINGSHUO_EMAIL;
|
|
if (email) await sendNotification(email, plan_id, "started", { task_count: plan.tasks.length }).catch(()=>{});
|
|
writeBrain(plan_id, { input: "收到规划", decision: "按宪法补全+执行", execution: plan.tasks.length + "个任务" });
|
|
logger.log("expand_start", { task_count: plan.tasks.length });
|
|
const expanded = await expandPlan(plan, safeCall, PLANS_DIR).catch((e) => {
|
|
if (isBudgetExceeded()) logger.log("budget_exceeded", { calls: getCallCount() });
|
|
logger.log("expand_skip", { reason: e.message });
|
|
return plan;
|
|
});
|
|
logger.log("exec_start", { task_count: expanded.tasks.length });
|
|
const summary = await executePlan(expanded, logger.log);
|
|
logger.finish(summary);
|
|
recordMilestone("完成: " + plan_id + " · 成功" + summary.done + "跳过" + (summary.skipped||0));
|
|
writeBrain(plan_id, { learned: "完成"+summary.done+"/"+summary.total, confidence: (summary.done/(summary.total||1)).toFixed(2) });
|
|
if (email) await sendNotification(email, plan_id, summary.failed > 0 ? "failed" : "completed", summary).catch(()=>{});
|
|
res.json(summary);
|
|
} catch (err) {
|
|
logger.log("fatal", { error: err.message });
|
|
logger.finish({ error: err.message });
|
|
writeBrain(plan_id, { failure: err.message, reasoning: "执行崩溃", fix: "等待铸渊审查" });
|
|
if (email) await sendNotification(email, plan_id, "failed", { errors: [err.message] }).catch(()=>{});
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
app.listen(PORT, () => {
|
|
console.log("[Agent v3] " + AGENT_ID + " · 人格体模式 · 端口 " + PORT);
|
|
});
|
|
|