diff --git a/agents/zhuyuan-dev-agent/server.js b/agents/zhuyuan-dev-agent/server.js index 6c74b2d..9e842b9 100644 --- a/agents/zhuyuan-dev-agent/server.js +++ b/agents/zhuyuan-dev-agent/server.js @@ -2,7 +2,7 @@ // @guardian: ICE-GL-ZY001 · 铸渊 // @sovereign: TCS-0002∞ · 冰朔 // @copyright: 国作登字-2026-A-00037559 -// BD001 常驻Agent v5.0 — 零点原核UI风格 +// BD001 常驻Agent v5.3 — 零点原核UI + 步骤解析 + 经验累积 require("dotenv").config(); const express = require("express"); @@ -15,7 +15,7 @@ const { call: rawCall, getStatus: getModelStatus } = require("./modules/model-ca 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 { record: writeBrain, recordMilestone, recordExperience, AGENT_ID } = require("./modules/brain-writer"); const { wakeAndCheck, testConnection } = require("./modules/notion-poller"); const safeCall = wrapModelCaller(rawCall); @@ -26,88 +26,30 @@ const PLANS_DIR = path.join(__dirname, "plans"); if (!fs.existsSync(PLANS_DIR)) fs.mkdirSync(PLANS_DIR, { recursive: true }); const START_TIME = new Date().toISOString(); -app.get("/", (req, res) => { - const html = `BD001 · 铸渊开发Agent - - -

⚔️ BD001 · 铸渊开发Agent

${AGENT_ID} · BS-SG-002 · guanghuyaoming.com
30s
-
-
SYS STATUS
状态加载中…
API 调用
版本
-
NOTION
连接检查中…
数据库
数据
-
WAKE AGENT
🔔
每晚 20:00 · 30s超时
-
PLANS
工单数加载中…
-
EXEC LOG
成功率加载中…
-
MODEL API
主源加载中…
备源
切换
-
-
-`; - res.set('Content-Type','text/html;charset=utf-8').send(html); -}); - -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.0",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.0",persona:AGENT_ID,plans})}); -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"));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);recordMilestone(plan_id+"完成"+s.done+"/"+s.total);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] "+AGENT_ID+" · 零点原核UI · 端口 "+PORT)}); +// ═══ 步骤解析器 v5.3 — 将 Notion steps 文本转为 task 对象 ═══ +function parsePlanSteps(stepsText, planId) { + if (!stepsText) return []; + const lines = stepsText.split("\n").filter(l => l.trim()); + const tasks = []; + let idx = 0; + for (const line of lines) { + const m = line.match(/^\s*(\d+)\.?\s+(.+)/); + if (m) { + idx++; + const desc = m[2].trim(); + let action = "write_file"; + if (/安装|执行|运行|启动|重启|构建|编译/.test(desc)) action = "run_cmd"; + if (/API|接口|对接|请求|调用/.test(desc)) action = "call_api"; + if (/验证|测试|检查|确认|审核/.test(desc)) action = "check"; + tasks.push({ + id: planId + "-t" + idx, + action, + target: "", + description: desc, + context: "从Notion工单提取: " + (stepsText.slice(0, 200)) + }); + } + } + console.log("[StepParser] " + planId + " → " + tasks.length + " tasks"); + return tasks; +} \ No newline at end of file