Files
mcn-short-video/.workbuddy/memory/2026-09-29.md
T
maogeigei 80072ff542 chore(repo): 归集 09-14~09-29 工作产出(技能/知识库/工作台/报告)并收敛临时产物
- skill: 素材库分块定义(00~11)、05_极致事件知识库 4 篇、08_对话风格总纲、mcn-video-prompt 提示词质量门禁与外部语料检索流程

- workbench: mcn-work-shop 新增 cli-backend.js(本地 CLI 接入)、工作台视觉交互规范;移除旧 UI 规范

- docs: 根目录极致事件/素材卡/审计方案报告与热门短视频清单入库

- memory: 补 09-14~09-29 日志与自动化任务记忆

- chore: .gitignore 排除 tools/、.tmp-chrome-*/、_k_test.cjs
2026-09-29 18:56:24 +08:00

28 KiB
Raw Blame History

2026-09-29 工作日志

11:30 卡片回炉 + 素材库补全 · 批次 58/59/60(暂存模式)

本轮闭环(三批连续,全部 apply 成功)

批次 目标标签 来源视频 卡片 主标签分布 apply 结果
58 自嘲反差 待补 15 自嘲反差为主 接受 15 / 退回 0
59 品质对比 37116 / 35227 / 19058 / 17484 / 28244 15 品质对比 5 / 反差 3 / 反常识 2 / 自嘲反差 2 / 细节专业 1 / 价值观冲击 2 接受 15 / 退回 0
60 氛围沉浸 22939 / 20432 / 18972 / 23321 / 23841 15 氛围沉浸 8 / 感官沉浸 2 / 反差 5 接受 15 / 退回 0

关键口径新增(本轮确立)

  • 品质对比口径:同品类两物「高下立判」的可视对比(劣质 vs 优质 / 廉价 vs 高配 / 旧 vs 新 / 常规 vs 超规格),落差须由实物证据呈现,非修辞性夸赞;非同类对比(代价对比、能力对比)→ 归 价值观冲击 或 细节专业。
  • 氛围沉浸口径(本轮补采时确立):环境/氛围本身构成可感知的强包裹感,观众被「拽进」某个时空或情境(夜色压迫 / 烟火滚烫 / 荒野孤绝 / 温度与声音的物理包裹)。判据两条:① 氛围先于事件成立;② 不依赖台词解释。纯情绪激动(不靠环境的)→ 归 感官沉浸 或 反差。
    • 已用子形态:火光破暗 / 伏击张力 / 隔窗压迫 / 圈层奇观 / 黑暗剥夺 / 门外压迫 / 深夜孤寂 / 车厢闷热。

技术要点复现(本轮再次验证)

  • apply-pending 的 list→dict 坑:模型返回若为 list,cmd_apply_pending 的 res.get(str(idx)) 会报 AttributeError。解法:先 out={str(i):c for i,c in enumerate(d,1)} 写 out/polish_result/pending_00NN.json 再 apply。本轮三批均先用此转换(批 60 模型直接返回 dict,同样按 str 键规整后写入)。
  • build-pending <n> 载荷路径:载荷落在 out/pending_tasks/batch_00NN.json(含 items[].{id,card_name,card,polish,lock,tag_name,std,sim,neg}),不产 out/_bNN_payload.txt(早期批次才有该文件,勿据此判断失败)。
  • _tmp_bNN_spec.py 模式已稳定(连续多批一次通过):从 md 正则抽 15 卡 → 内置 Q 字典手写问法 → CARD_RE = r"```text\n(### [^\n]+\n.*?)\n```"、TAG_RE = r"事件标签[^:]*:\([^`(]+)(主)`"→ 落盘 spec;断言len(cards)==15、len(order)==15`。
  • tempprompt 路径:C:\Users\maidou\AppData\Local\Temp\_prompt60.txt / _raw60.txt。
  • 告警判定:批 60 的 15 条 fiction 告警全部为第⑤类误报(std 骨架「…」内的检索意图摘要短语被照搬进引号)→ 只告警不拦截,无需退回。

当前进度与缺口

  • 暂存已积累 17 批(43–60,缺 53),卡片数约 255 张,均待 13_import_pending.py 统一入库。
  • 5_tag_coverage.py / 7_dispatch_next.py 读 state.json 不反映暂存批 → 显示的仍是回炉基线数字(氛围沉浸 25、品质对比 19)。
  • 缺口排序(跳难度极限):品质对比 81 / 预见式服务 81 / 氛围沉浸 75 / 感官沉浸 74 / 细节专业 68 / 自嘲反差 56 / 视觉冲击 33 / 价值观冲击 7。
  • ⛔ 无候选:食材极致(现有 8、候选 0)、预见式服务(现有 19、候选 2)→ 关键词捞不到,需重建候选池。
  • 已取批 61(品质对比):24819 / 28003 / 31772 / 28296 / 31813,待拉详情并产卡。

15:35 CLI 接入技术侦查结论(WorkBuddy CLI --serve + runs 空转)

目标

用户指令:放弃 Dify,改用 WorkBuddy CLI 调 doubao-seed-2-1-pro 执行 AI创作;对话框要流式输出。

已确认可用(踩坑记录)

  • 入口:C:/Users/maidou/AppData/Local/Programs/WorkBuddy/resources/app.asar.unpacked/cli/bin/codebuddy 无扩展名 node 脚本 → Python 必须 [NODE_EXE, CLI, ...],直接 Popen 报 WinError 193。
  • 启动:--serve --port <P> --auth none --session-id <id> --permission-mode bypassPermissions --model <m> (--auth none 后仍需 Bearer;日志打印的 Password 就是 Bearer 值)。
  • 官方文档:https://www.codebuddy.cn/docs/cli/http-api (中文)/ http://www.workbuddy.ai/docs/cli/http-api
    • ⭐ 所有 /api/v1/* 请求必须带 X-CodeBuddy-Request: 1(豁免:/auth/login、/health? 实测 health 401、/api/openapi.json、/api/docs)
    • ⭐ Bearer 值 = 启动打印的 Password(不是 login 返回的 token,虽然实测两者相同)
    • 文档给的 POST /runs body 字段表:id✅ type✅(message|action) payload.text source.{platform,sender.id,conversation.id,conversation.type} timeoutMs
    • 错误码表:403=Missing required header(缺 X-CodeBuddy-Request),401=AUTH_REQUIRED
  • 实测通过:/api/v1/health /api/v1/info /api/v1/sessions /api/v1/stats/session /api/v1/sessions/live
  • 实测 POST /api/v1/runs → 202 {"data":{"runId":...,"status":"accepted"}}(按官方 body 逐字复刻也成功)

⛔ 核心故障:run 被受理但 Agent 从不执行

证据链(三种模型 × 两种 body 变体 × 带/不带 X-CodeBuddy-Request,全部一致):

  1. POST /runs → 202 accepted
  2. GET /runs/{id} → 恒 active:true(30 分钟超时也不变)
  3. GET /runs/{id}/stream → 连响应头都不返回(不是 404/超时,是挂住)
  4. GET /sessions/{id}/replay → 只有 user_message_chunk,无任何 assistant 事件;snapshot.busy=false
  5. GET /stats/session → apiDuration:0、tokenUsageByModel:{} → 一个 token 都没消耗,模型调用从未发出
  6. GET /workers、GET /metrics → 超时(HTTP 层部分阻塞)
  7. codebuddy -p "..." --model glm-5.1 → 4 分钟超时,输出 0 字节

结论:Agent 运行时(agentManager / 模型客户端)在独立起的 --serve 进程里从未激活。 代码线索(bundle codebuddy-headless.js):AgentManager.init() 里 this.prewarmStateService?.isStandby() ? onActivated(startAgentBuild) : startAgentBuild() → 怀疑独立 serve 的 prewarm 状态未激活导致 agent 永不 build。

✅ 排除的假设

  • ❌ 不是企业模型问题:glm-5.1(官方模型)空转表现完全相同。
  • ❌ 不是 X-CodeBuddy-Request 缺失:补上后行为不变(仍 202 + 空转)。
  • ❌ 不是 body 格式:official schema 写法({id,type,source,payload})也有 400→最终 accepted。
  • ❌ 不是端口/进程冲突:每次换新端口,服务确实起来且能响应。
  • ❌ 不是 --session-id 缺失或 --model 缺失:都加过,无效。

有关 API 全景(从 bundle 提取,99 条)

/api/v1/acp/connect + GET/POST /api/v1/acp(有状态 JSON-RPC over SSE,是唯一自带完整对话能力的入口) /api/v1/jobs(POST 派发独立 agent 实例,另一条执行路径,未测) /api/v1/daemon/{status,start,stop,restart}、/api/v1/workers /api/v1/scheduled-tasks、/api/v1/plugins、/api/v1/settings、/api/v1/fs/*、/api/v1/pty

本机环境事实(新增)

  • 12134 端口 = WorkBuddy daemon(PID 33720),多路 ESTABLISHED,正常常驻,禁止杀。
  • CLI 日志:C:/Users/maidou/.codebuddy/logs/memwatch/cli-memwatch-<pid>.log(活跃);~/.codebuddy/logs/{date}/ 只有 8 月的(已废弃)。
  • CLI 的 PathUtils.getHomeDir() → C:/Users/maidou/.codebuddy;Config 却读 D:/.workbuddy/settings.json。
  • --debug 的日志目录 ~/.codebuddy/debug/ 不存在(未生成)。

15:55 根因确认(重大):CLI 内部 IPC 端口 12134 被 WorkBuddy prewarm 池占用

⭐ 决定性证据

codebuddy -p "你好" --debug 首次抓到了被吞掉的真实异常:

Unhandled rejection Error: listen EADDRINUSE: address already in use 127.0.0.1:12134
    at Server.setupListenHandle [as _listen2] (node:net:1940:16)

→ 12134 是 CLI 自己的内部 IPC 端口(每次 CLI 启动都要 listen)。 netstat 显示被 PID 33720 独占 LISTENING;Python 实测 bind 报 WinError 10013(权限不允许)。

占用者身份(PowerShell Get-CimInstance 实测)

PID=33720 PPID=21628  WorkBuddy.exe ...codebuddy --prewarm --prewarm-id wb-pool-1790566628102-f40096
PID=32328 PPID=21628  WorkBuddy.exe ...codebuddy --prewarm --prewarm-id wb-pool-1790579518962-fabb5b

→ 父进程 21628 = 用户正在运行的 WorkBuddy 桌面主程序,这两个是它 spawn 的预热池。 → cbc-prewarm list 返回 No prewarm processes found(不走 cbc-prewarm 注册表,是主程序直管,故无法用官方工具优雅停掉)。

完整根因链

WorkBuddy 主程序(21628) spawn prewarm 池(33720/32328) 占用 127.0.0.1:12134
   ↓  手动起 codebuddy --serve / -p
EADDRINUSE 127.0.0.1:12134  →  内部 IPC 服务起不来
   ↓
HTTP 层照常工作(health/sessions/info/auth 全部正常响应,故极具迷惑性)
   ↓
Agent 运行时初始化失败 → span "cli" status:error / "Error in agent run"(耗时 1ms)
   ↓
POST /runs → 202 accepted 但恒 active:true、tokenUsage 全 0、无任何 assistant 输出
POST /jobs → state:working/tempo:active/alive:true/pid 但 detail 永停 "starting…"、transcript 空
ACP session/prompt → 有 requestId/traceId 但 stopReason: refusal
codebuddy -p → EADDRINUSE 后卡死,超时零输出

⭐ 第三条诊断利器:GET /api/v1/traces(新发现)

  • GET /api/v1/traces?limit=5 → trace 列表,含 status/errorCount/firstErrorMessage/firstErrorWhere/totalTokens
  • GET /api/v1/traces/{traceId} → 完整 spans + bottleneck + errorSummary
  • 实测关键 span:mcp_tools × 5 全 ok(39/32/14/7/7 ms),随后 cli(type:agent) status:error, duration:1ms, error:"Error in agent run",totalTokens:0
  • terminalTitleGenerator(auxiliary) 同时报同错 → 证明是共性依赖故障而非单 agent 问题

ACP 通道完整协议(已实测跑通,代码来自 bundle DetachedSseBridge)

  1. POST /api/v1/acp/connect(带 X-CodeBuddy-Request + Bearer)→ {connectionId, sessionToken}
  2. 之后所有请求必须带 header acp-connection-id: <cid>
  3. ⭐ Accept: application/json, text/event-stream 必须同时含两者,否则 406 "Not Acceptable: Client must accept both application/json and text/event-stream"
  4. POST /api/v1/acp — JSON-RPC 2.0:
    • initialize params {protocolVersion:1, clientInfo:{name,version}, clientCapabilities:{fs,terminal}}
    • session/new params {cwd: "<path>", mcpServers: []} ← ⚠️ 字段名是 cwd,不是 workingDirectory(传错报 Invalid input: expected string, received undefined)
    • session/prompt params {sessionId, prompt:[{type:"text", text:"..."}]}
    • 其他方法:authenticate(methodId: iOA/external/internal/selfhosted)、session/load、session/resume、session/cancel、session/set_model、session/set_mode、session/set_config_option
  5. 响应是 SSE 流(:ok 心跳 + event: message + data: {jsonrpc...})
  6. sessionUpdate 枚举:agent_message_chunk/agent_thought_chunk/tool_call/tool_call_update/session_end/session_info_update/model_update/mode_update/current_mode_update/config_option_update/available_commands_update/plan/interruption_request
  7. 实测:initialize ✅、session/new ✅(拿到 sessionId + config_option_update 流)、session/prompt ✅(返回 stopReason:refusal + requestId + traceId,说明协议链路完全正确,只是底层 agent 因 12134 故障而拒答)

POST /api/v1/jobs(B 通道,实测)

  • 请求体:{prompt, cwd, model, effort, permissionMode, agent, name, bash, sourceSessionId, bgIsolation}
  • 响应:{data:{id, sessionId, state, tempo, detail, intent, cwd, kind, alive, settled, pid, webUrl}}
  • 实测:创建成功(state:working/tempo:active/alive:true/pid:40664),但 detail 永停 starting…、transcript.updates:[] 空 → 同样受 12134 故障影响
  • 查询:GET /api/v1/jobs、GET /api/v1/jobs/{id}、GET /api/v1/jobs/{id}/transcript、GET /api/v1/jobs/{id}/stream(SSE)

结论与待决策

  • ✅ CLI 接入方案本身完全可行:--serve HTTP 层 99 条路由全部正常;ACP 协议链路(connect → initialize → session/new → prompt → SSE 流)已完整跑通。
  • ⛔ 唯一阻塞 = 12134 被 WorkBuddy prewarm 池占用。这是本机「WorkBuddy 桌面程序常开」与「手动调 CLI」共存的端口冲突。
  • 待用户决策的解法(涉及杀掉用户正在运行的 WorkBuddy 子进程,必须先确认): A. 临时 Stop-Process -Id 33720,32328(主程序会重建池;新开 WorkBuddy 会话慢几秒;不丢数据)→ 重跑 CLI 验证 B. 查 CLI 是否有可配置的内部 IPC 端口(bundle 里 12134 非硬编码,疑似运行时派生,暂未找到覆盖入口) C. 改用 WorkBuddy 自带的 daemon 通道(12134 那个 server 本身)而非新起 CLI 进程

17:00-18:20 CLI 接入方案 · 根因修正与去留复盘

⛔ 推翻前序误判:「端口冲突 12134」是错的

  • 12134 是 WorkBuddy 宿主进程自己的 gateway 端口(PID 33720 = prewarm 进程,kind:"interactive",isCurrent:true = 我当前会话所在进程),与 CLI 启动无关。
  • 该端口提供完整 CodeBuddy HTTP Server:GET / 返回「CodeBuddy Remote Control」页;带 Authorization: Bearer <CODEBUDDY_GATEWAY_PASSWORD> 后 /api/v1/health、/api/v1/info、/api/v1/jobs、/api/v1/traces 全部 200,auth/status = {authEnabled:true, authenticated:true}。
  • 12134 不是硬编码:搜遍 product*.json / bundle 无此字面量。主 server 端口 = parseInt(process.env.SERVER__PORT) || config.get("cell.server", {port:3000}).port;Gateway 复用它(endpointProvider.get())。

✅ 真实根因:独立启动的 CLI 没有登录态

  • 决定性证据(ACP session/new 返回): {"code":-32000,"message":"Authentication required","data":{"details":"Authentication required. Please use /login command to sign in to your account","category":"auth"}} codebuddy.ai/outcome: "FAILED_MODEL_REQUEST"
  • 换 --serve 也一样:此时 session/new 直接报 Authentication required。
  • 认证凭据不落盘:全盘搜无 .credentials.json;D:/.workbuddy/keyblob 是加密 blob(static-v1 + wrapped ciphertext)。凭据只由宿主进程注入环境变量传递: CODEBUDDY_GATEWAY_PASSWORD、CODEBUDDY_AUTH_TOKEN / CODEBUDDY_API_KEY(CustomTokenAuthenticationStorageImpl:三者皆无 → AuthenticationStoragePriority.Disabled)。
  • 产品配置 ACC_PRODUCT_CONFIG_PATH 指向 %TEMP%\workbuddy-product-spill-*\acc-product-config-v3.json,authentication.type = "cli-external-link"(需要走登录流程,非固定 key)。

⛔ 另一条误判:--prewarm 端口占用不是 named pipe

resolvePrewarmIpcPath() 在 win32 = \\.\pipe\codebuddy-prewarm-<id>(命名管道),不是 TCP。Prewarm IPC 只管「激活」,不占 TCP 端口。

🔑 bundle 分流(重要,之前踩过)

bin/codebuddy launcher 按环境变量选 bundle:

  • CODEBUDDY_FORCE_LITE_WB_BUNDLE=1 → codebuddy-lite-wb.mjs(WorkBuddy 特供精简包,宿主 spawn sidecar/prewarm 时注入;优先级最高)
  • CODEBUDDY_FORCE_HEADLESS_BUNDLE=1 → codebuddy-headless.js(官方 headless)
  • 含 --print → 自动 headless
  • 均缺失 → dist/codebuddy(full TUI bundle,该产物在本安装里不存在 → Cannot find module '../dist/codebuddy') → 自己测 CLI 时必须显式设 CODEBUDDY_FORCE_HEADLESS_BUNDLE=1,否则会误用 wb 特供包或报模块找不到。

⛔⛔ 决定去留的硬约束:宿主 gateway 不可被外部发现

事实 实测
端口每轮轮换 12134 → 13116 → 10261(同一台机、同一天内)
密码每轮轮换 gcoMpIuQ… → xJzG3hgI…
无落盘注册表 D:/.workbuddy/ 下无 worker/registry/daemon 文件;cbc-prewarm list = No prewarm processes found
唯一发现通道 环境变量 CODEBUDDY_SERVICE_PROXY_URL=http://127.0.0.1:<port>/internal/hooks/services/invoke
该变量的可见范围 仅 WorkBuddy 派生的进程树内(我当前 Bash 里可见;用户双击 start.bat 起的进程看不到)
ACP 会话语义 宿主 gateway session/new 返回当前活跃会话 ID(非新会话)→ prompt 自会话阻塞

三条路实测结论

方案 结论
A 独立起 CLI(--serve / -p / ACP) ❌ 缺登录态,FAILED_MODEL_REQUEST
B 复用宿主 gateway ⚠️ 协议层全通(connect/initialize/session/new 均 ✅,认证 ✅),但端口+密码随机轮换且不可从外部发现 → 工作台(用户手动启动)拿不到
C 官方非交互 key ✅ 存在 CODEBUDDY_API_KEY 环境变量通道(bundle 内 eT="CODEBUDDY_API_KEY"),与桌面程序完全解耦 —— 唯一无耦合的官方路径,待用户提供 key

环境事实(本轮)

  • 沙箱会回收后台服务:run_in_background 起的 serve 在任务结束即死;测试须前台 + timeout,或单次脚本内自包含。
  • 宿主重启会使 App PID 变化(20760 → 22976),gateway 随之换端口。
  • 清理:_start_serve*.py/_probe*.py/_test*.py/_acp_*.py/_clean_*.py/_keep_auth.py 等 30+ 临时脚本 + _*.log/_*.txt 待删。

18:20-18:45 A 方案落地:工作台接入本地 CodeBuddy CLI(stdio 直连)

决议

用户拍板走 A 方案(官方 CODEBUDDY_API_KEY 非交互认证),不再挖宿主 gateway(B/D 路已证不可靠)。

⭐ 官方认证矩阵(来源 codebuddy.ai/docs/cli/iam,是权威)

场景 环境变量 获取地址
个人开发者 CODEBUDDY_API_KEY 中国版 https://copilot.tencent.com/profile/ / 国际版 https://www.codebuddy.ai/profile/keys
已有 OAuth token CODEBUDDY_AUTH_TOKEN 直接填
企业 OAuth apiKeyHelper(settings.json) 建应用拿 Client ID/Secret
  • 优先级:CODEBUDDY_AUTH_TOKEN > apiKeyHelper > CODEBUDDY_API_KEY
  • ⚠️ 必配 CODEBUDDY_INTERNET_ENVIRONMENT:中国版 internal/iOA ioa/国际版不设(最常漏,漏了就连错端点)
  • 优先级链实现(getAuthenticationAttributesSync):env CODEBUDDY_AUTH_TOKEN → ACC_PRODUCT_CONFIG_V3/V2 JSON → settings.json 的 env 字段 → ACC_PRODUCT_CONFIG_PATH 文件
  • 可落盘:settings.json 的 env.CODEBUDDY_AUTH_TOKEN 是官方支持的持久化位置
  • ⛔ 官方 HTTP API 文档(docs/cli/http-api)没有 API Key 机制 —— 那是网关密码认证,别搞混

⭐⭐ 本次最大技术收获:CLI 端口冲突的真正机理

SERVER__PORT 环境变量继承导致 EADDRINUSE。

  • codebuddy(连 -p 打印模式也)内部会起 HTTP server:listen(parseInt(process.env.SERVER__PORT) || config.get("cell.server",{port:3000}).port)
  • 在 WorkBuddy 进程树内启动 → 继承宿主的 SERVER__PORT → 去 listen 宿主已占端口 → EADDRINUSE → 静默卡死
  • 迷惑性极强:HTTP 层(health/info/sessions/auth)全正常,只有 agent 执行环节挂 → 极易误判成「prewarm 占端口」
  • 官方自己也这么处理:CLI 派生子进程时 delete el.SERVER__PORT, delete el.SERVER__HOST
  • 验证:删掉这两个变量后,从「卡死 110s」→「秒退 RC=0」

⭐ bundle 分流坑(bin/codebuddy launcher)

环境变量 命中产物
CODEBUDDY_FORCE_LITE_WB_BUNDLE=1(宿主注入,优先级最高) codebuddy-lite-wb.mjs(WorkBuddy 特供精简包)
CODEBUDDY_FORCE_HEADLESS_BUNDLE=1 codebuddy-headless.js(官方 headless,该用的)
含 --print 自动 headless
都没有 dist/codebuddy → 本安装不存在,报 Cannot find module '../dist/codebuddy'

⭐ 宿主 gateway 不可用(结论钉死,别再挖)

事实 实测
端口每轮轮换 12134 → 13116 → 10261
密码每轮轮换 gcoMpIuQ… → xJzG3hgI…
无落盘注册表 D:/.workbuddy/ 无 worker/registry/daemon 文件
唯一发现通道 环境变量 CODEBUDDY_SERVICE_PROXY_URL,仅 WorkBuddy 派生进程树内可见
凭据保护是故意设计 宿主用 4 个环境变量交付凭据,CLI 读后立即 delete;凭据不落明文盘(keyblob 是加密 blob)

交付物

文件 说明
mcn-work-shop/cli-backend.js 新增。runCli():spawn CLI + stream-json 解析 + 文本增量回调;buildChildEnv() 含三条防护
mcn-work-shop/server.js 新增 /api/ai/status(自检,?probe=1 真跑);/api/ai/clarify 加 CLI 分支;aiBackend() 支持 MCN_AI_BACKEND 覆盖
mcn-work-shop/load-config.js ai 配置块 + 深合并(浅合并会把 cli 子字段整体顶掉)
mcn-work-shop/config.json 新增 ai 段(backend 暂留 dify 保证不回归)
mcn-work-shop/docs/AI链路-本地CLI接入.md 权威文档:配置步骤/字段说明/三条防护/输出协议/排障速查

stream-json 协议(--output-format stream-json --include-partial-messages)

  • type:"system" subtype:"init" → session_id、apiKeySource
  • type:"stream_event" → event.content_block_delta → delta.type==="text_delta" → delta.text(增量)
  • type:"assistant" → 无增量流时的整段兜底
  • type:"result" → 终态;is_error:true 时真实原因在 errors[],正文作废
  • ⚠️ 未认证时 CLI 把报错当 assistant 文本发出来 → 需 isCliBoilerplate() 拦截,否则英文报错会显示成"AI 的回答"

测试证据(端到端,均已通过)

  • /api/ai/status → backend:cli、cli.found:true、resolved.kind:"bundled"、credential:"(未配置)"、ready:false
  • /api/ai/status?probe=1 → 真跑 CLI,11s 返回可操作中文错误(含 sessionId)
  • /api/ai/clarify(stream) → SSE 正常发 {error} + [DONE],不卡死
  • ⇒ 除凭据外全链路已验证;填 key 即可出内容

剩余动作(待用户)

  1. 去 https://copilot.tencent.com/profile/ 拿 API Key
  2. config.json:ai.backend → "cli",ai.cli.apiKey → 你的 key
  3. 自检 curl "http://localhost:8900/api/ai/status?probe=1"
  4. (待定) 前端 data-pages.js 的 sendTurn 是否需适配(SSE 协议未变,理论上零改动,待实测)

清理

本轮 91 个临时文件(_*.py/_*.log/_*.txt/_debug/ 等,均在根目录与 mcn-work-shop 下)已全部分批删除。


18:20-18:45 A 方案落地:工作台接入本地 CodeBuddy CLI(stdio 直连)

决议

用户拍板走 A 方案(官方 CODEBUDDY_API_KEY 非交互认证),不再挖宿主 gateway(B/D 路已证不可靠)。

⭐ 官方认证矩阵(来源 codebuddy.ai/docs/cli/iam,是权威)

场景 环境变量 获取地址
个人开发者 CODEBUDDY_API_KEY 中国版 https://copilot.tencent.com/profile/ / 国际版 https://www.codebuddy.ai/profile/keys
已有 OAuth token CODEBUDDY_AUTH_TOKEN 直接填
企业 OAuth apiKeyHelper(settings.json) 建应用拿 Client ID/Secret
  • 优先级:CODEBUDDY_AUTH_TOKEN > apiKeyHelper > CODEBUDDY_API_KEY
  • ⚠️ 必配 CODEBUDDY_INTERNET_ENVIRONMENT:中国版 internal/iOA ioa/国际版不设(最常漏,漏了就连错端点)
  • 优先级链实现(getAuthenticationAttributesSync):env CODEBUDDY_AUTH_TOKEN → ACC_PRODUCT_CONFIG_V3/V2 JSON → settings.json 的 env 字段 → ACC_PRODUCT_CONFIG_PATH 文件
  • 可落盘:settings.json 的 env.CODEBUDDY_AUTH_TOKEN 是官方支持的持久化位置
  • ⛔ 官方 HTTP API 文档(docs/cli/http-api)没有 API Key 机制 —— 那是网关密码认证,别搞混

⭐⭐ 本次最大技术收获:CLI 端口冲突的真正机理

SERVER__PORT 环境变量继承导致 EADDRINUSE。

  • codebuddy(连 -p 打印模式也)内部会起 HTTP server:listen(parseInt(process.env.SERVER__PORT) || config.get("cell.server",{port:3000}).port)
  • 在 WorkBuddy 进程树内启动 → 继承宿主的 SERVER__PORT → 去 listen 宿主已占端口 → EADDRINUSE → 静默卡死
  • 迷惑性极强:HTTP 层(health/info/sessions/auth)全正常,只有 agent 执行环节挂 → 极易误判成「prewarm 占端口」
  • 官方自己也这么处理:CLI 派生子进程时 delete el.SERVER__PORT, delete el.SERVER__HOST
  • 验证:删掉这两个变量后,从「卡死 110s」→「秒退 RC=0」

⭐ bundle 分流坑(bin/codebuddy launcher)

环境变量 命中产物
CODEBUDDY_FORCE_LITE_WB_BUNDLE=1(宿主注入,优先级最高) codebuddy-lite-wb.mjs(WorkBuddy 特供精简包)
CODEBUDDY_FORCE_HEADLESS_BUNDLE=1 codebuddy-headless.js(官方 headless,该用的)
含 --print 自动 headless
都没有 dist/codebuddy → 本安装不存在,报 Cannot find module '../dist/codebuddy'

⭐ 宿主 gateway 不可用(结论钉死,别再挖)

事实 实测
端口每轮轮换 12134 → 13116 → 10261
密码每轮轮换 gcoMpIuQ… → xJzG3hgI…
无落盘注册表 D:/.workbuddy/ 无 worker/registry/daemon 文件
唯一发现通道 环境变量 CODEBUDDY_SERVICE_PROXY_URL,仅 WorkBuddy 派生进程树内可见
凭据保护是故意设计 宿主用 4 个环境变量交付凭据,CLI 读后立即 delete;凭据不落明文盘(keyblob 是加密 blob)

交付物

文件 说明
mcn-work-shop/cli-backend.js 新增。runCli():spawn CLI + stream-json 解析 + 文本增量回调;buildChildEnv() 含三条防护
mcn-work-shop/server.js 新增 /api/ai/status(自检,?probe=1 真跑);/api/ai/clarify 加 CLI 分支;aiBackend() 支持 MCN_AI_BACKEND 覆盖
mcn-work-shop/load-config.js ai 配置块 + 深合并(浅合并会把 cli 子字段整体顶掉)
mcn-work-shop/config.json 新增 ai 段(backend 暂留 dify 保证不回归)
mcn-work-shop/docs/AI链路-本地CLI接入.md 权威文档:配置步骤/字段说明/三条防护/输出协议/排障速查

stream-json 协议(--output-format stream-json --include-partial-messages)

  • type:"system" subtype:"init" → session_id、apiKeySource
  • type:"stream_event" → event.content_block_delta → delta.type==="text_delta" → delta.text(增量)
  • type:"assistant" → 无增量流时的整段兜底
  • type:"result" → 终态;is_error:true 时真实原因在 errors[],正文作废
  • ⚠️ 未认证时 CLI 把报错当 assistant 文本发出来 → 需 isCliBoilerplate() 拦截,否则英文报错会显示成"AI 的回答"

测试证据(端到端,均已通过)

  • /api/ai/status → backend:cli、cli.found:true、resolved.kind:"bundled"、credential:"(未配置)"、ready:false
  • /api/ai/status?probe=1 → 真跑 CLI,11s 返回可操作中文错误(含 sessionId)
  • /api/ai/clarify(stream) → SSE 正常发 {error} + [DONE],不卡死
  • ⇒ 除凭据外全链路已验证;填 key 即可出内容

剩余动作(待用户)

  1. 去 https://copilot.tencent.com/profile/ 拿 API Key
  2. config.json:ai.backend → "cli",ai.cli.apiKey → 你的 key
  3. 自检 curl "http://localhost:8900/api/ai/status?probe=1"
  4. (待定) 前端 data-pages.js 的 sendTurn 是否需适配(SSE 协议未变,理论上零改动,待实测)

清理

本轮 91 个临时文件(_*.py/_*.log/_*.txt/_debug/ 等,均在根目录与 mcn-work-shop 下)已全部分批删除。