diff --git a/agents/zhuyuan-dev-agent/server.js b/agents/zhuyuan-dev-agent/server.js
index 653cd91..adb442c 100644
--- a/agents/zhuyuan-dev-agent/server.js
+++ b/agents/zhuyuan-dev-agent/server.js
@@ -26,7 +26,7 @@ const PLANS_DIR = path.join(__dirname, "plans");
if (!fs.existsSync(PLANS_DIR)) fs.mkdirSync(PLANS_DIR, { recursive: true });
const START_TIME = new Date().toISOString();
-// v5.3: step parser - ASCII-only, no regex on Chinese chars
+// v5.3 step parser - ASCII-only keyword matching (no regex on Chinese)
function parsePlanSteps(stepsText, planId) {
if (!stepsText) return [];
var lines = stepsText.split("\n").filter(function(l) { return l.trim(); });
@@ -38,12 +38,135 @@ function parsePlanSteps(stepsText, planId) {
idx++;
var desc = m[2].trim();
var action = "write_file";
- var d = desc;
- if (d.indexOf("API") !== -1 || d.indexOf("Notion") !== -1) action = "call_api";
- if (d.indexOf("curl") !== -1 || d.indexOf("npm") !== -1 || d.indexOf("git") !== -1) action = "run_cmd";
- tasks.push({ id: planId + "-t" + idx, action: action, target: "", description: desc, context: "Notion:" + stepsText.slice(0, 200) });
+ if (desc.indexOf("API")!==-1||desc.indexOf("Notion")!==-1||desc.indexOf("notion")!==-1) action="call_api";
+ if (desc.indexOf("curl")!==-1||desc.indexOf("npm")!==-1||desc.indexOf("git")!==-1||desc.indexOf("pm2")!==-1||desc.indexOf("Nginx")!==-1||desc.indexOf("nginx")!==-1) action="run_cmd";
+ if (desc.indexOf("测试")!==-1||desc.indexOf("验证")!==-1||desc.indexOf("SSE")!==-1||desc.indexOf("Playwright")!==-1) action="check";
+ tasks.push({ id: planId+"-t"+idx, action:action, target:"", description:desc, context:"Notion:"+stepsText.slice(0,200) });
}
}
- console.log("[StepParser] " + planId + " -> " + tasks.length + " tasks");
+ console.log("[StepParser] "+planId+" -> "+tasks.length+" tasks");
return tasks;
-}
\ No newline at end of file
+}
+
+// ═══ Dashboard HTML ═══
+app.get("/", (req, res) => {
+ const html = `
BD001 · 铸渊开发Agent
+
+
+⚔️ BD001 · 铸渊开发Agent
${AGENT_ID} · BS-SG-002 · v5.3
30s
+
+
SYS STATUS—
状态加载中…
API 调用—
版本—
+
+
WAKE AGENT
🔔
每晚 20:00 · 30s超时
+
+
+
+
+
+
+`;
+ res.set('Content-Type','text/html;charset=utf-8').send(html);
+});
+
+// ═══ API Endpoints ═══
+app.get("/health", (req, res) => {
+ let ms = {}; try { ms = getModelStatus ? getModelStatus() : {}; } catch(e) { ms = { error: e.message }; }
+ res.json({ status: "ok", agent: "zhuyuan-dev-agent", version: "5.3", persona: AGENT_ID, constitution: true, api_calls: getCallCount(), started_at: START_TIME, model: { primary: process.env.YUNWU_BASE_URL ? "yunwu.ai" : "unknown", backup: process.env.DEEPSEEK_BASE_URL ? "deepseek" : "unknown", primary_ok: ms.source !== "deepseek" || !ms.fail_count, backup_available: !!process.env.DEEPSEEK_API_KEY, fail_count: ms.fail_count || 0, source: ms.source || "yunwu.ai" } });
+});
+
+app.get("/notion-status", async (req, res) => { try { res.json(await testConnection()); } catch (err) { res.json({ ok: false, error: err.message }); } });
+
+app.post("/wake", async (req, res) => { try { const r = await wakeAndCheck({ log: console.log }); if (r.tasks_found > 0) writeBrain("wake-" + Date.now(), { input: "唤醒", decision: "找到" + r.tasks_found + "个", execution: "标记" + r.tasks_marked + "个" }); res.json(r); } catch (err) { res.status(500).json({ ok: false, error: err.message }); } });
+
+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 f = req.query.file; if (!f) return res.status(400).json({ error: "缺少file" }); const fp = path.join(__dirname, "logs", f); if (!fs.existsSync(fp)) return res.status(404).json({ error: "不存在" }); res.json(fs.readFileSync(fp, "utf-8").trim().split("\n").map(l => { try { return JSON.parse(l); } catch(e) { return null; } }).filter(Boolean)); });
+
+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: "5.3", persona: AGENT_ID, plans }); });
+
+// ═══ Plan Review & Approve ═══
+app.get("/plan/:plan_id", (req, res) => { const planFile = path.join(PLANS_DIR, req.params.plan_id + ".json"); if (!fs.existsSync(planFile)) return res.status(404).json({ error: "不存在" }); try { const p = JSON.parse(fs.readFileSync(planFile, "utf-8")); res.json({ plan_id: p.plan_id, title: p.title, priority: p.priority, steps: p.steps, constraints: p.constraints, status: p._status, tasks: p.tasks, notion_url: p.notion_url }); } catch(e) { res.status(500).json({ error: "解析失败" }); } });
+
+app.post("/plan/:plan_id/approve", async (req, res) => { const planFile = path.join(PLANS_DIR, req.params.plan_id + ".json"); if (!fs.existsSync(planFile)) return res.status(404).json({ error: "不存在" }); const plan = JSON.parse(fs.readFileSync(planFile, "utf-8")); if (plan._status === "running") return res.json({ ok: false, error: "正在执行中" }); if ((!plan.tasks || plan.tasks.length === 0) && plan.steps) { plan.tasks = parsePlanSteps(plan.steps, plan.plan_id); } if (!plan.tasks || plan.tasks.length === 0) return res.json({ ok: false, error: "无任务可执行" }); const logger = createLogger(req.params.plan_id); reset(); plan._status = "running"; fs.writeFileSync(planFile, JSON.stringify(plan, null, 2), "utf-8"); logger.log("approve", { plan_id: req.params.plan_id, task_count: plan.tasks.length }); try { const email = process.env.BINGSHUO_EMAIL; if (email) await sendNotification(email, req.params.plan_id, "started", { task_count: plan.tasks.length }).catch(() => {}); writeBrain(req.params.plan_id, { input: "批准执行", decision: "按规划执行 " + 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"); logger.log("expand_skip", { reason: e.message }); return plan; }); logger.log("exec_start", { task_count: expanded.tasks?.length || 0 }); const summary = await executePlan(expanded, logger.log); logger.finish(summary); plan._status = "done"; fs.writeFileSync(planFile, JSON.stringify(plan, null, 2), "utf-8"); recordExperience(req.params.plan_id, summary); recordMilestone(req.params.plan_id + " · " + summary.done + "/" + (summary.total || 0) + " 步"); if (email) await sendNotification(email, req.params.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 }); plan._status = "error"; fs.writeFileSync(planFile, JSON.stringify(plan, null, 2), "utf-8"); res.status(500).json({ error: err.message }); } });
+
+app.post("/execute", async (req, res) => { const { plan_id } = req.body; if (!plan_id) return res.status(400).json({ error: "缺少plan_id" }); const pf = path.join(PLANS_DIR, plan_id + ".json"); if (!fs.existsSync(pf)) return res.status(404).json({ error: "不存在" }); const plan = JSON.parse(fs.readFileSync(pf, "utf-8")); if ((!plan.tasks || plan.tasks.length === 0) && plan.steps) { plan.tasks = parsePlanSteps(plan.steps, plan.plan_id); } const logger = createLogger(plan_id); reset(); try { const e = process.env.BINGSHUO_EMAIL; if (e) await sendNotification(e, plan_id, "started", { task_count: plan.tasks.length }).catch(() => {}); writeBrain(plan_id, { input: "收到", decision: "执行" }); logger.log("expand_start", { task_count: plan.tasks.length }); const ex = await expandPlan(plan, safeCall, PLANS_DIR).catch((e) => { if (isBudgetExceeded()) logger.log("budget_exceeded"); logger.log("expand_skip", { reason: e.message }); return plan; }); logger.log("exec_start", { task_count: ex.tasks.length }); const s = await executePlan(ex, logger.log); logger.finish(s); recordExperience(plan_id, s); recordMilestone(plan_id + " · " + s.done + "/" + (s.total || 0)); if (e) await sendNotification(e, plan_id, s.failed > 0 ? "failed" : "completed", s).catch(() => {}); res.json(s); } catch (err) { logger.log("fatal", { error: err.message }); logger.finish({ error: err.message }); writeBrain(plan_id, { failure: err.message }); if (process.env.BINGSHUO_EMAIL) await sendNotification(process.env.BINGSHUO_EMAIL, plan_id, "failed", { errors: [err.message] }).catch(() => {}); res.status(500).json({ error: err.message }); } });
+
+app.listen(PORT, () => { console.log("[Agent v5.3] " + AGENT_ID + " · 零点原核UI · 步骤解析 · 经验累积 · 端口 " + PORT); });