@wemind/cli 0.0.0-stage → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +205 -2
- package/dist/cli.js +4537 -0
- package/dist/cli.js.map +1 -0
- package/package.json +53 -4
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","names":["readJson","resolveKey","AG","CATALOG","mirrorRealKiro","readJson","syncCredentialFile","syncCredentials","loadCatalog","syncKiroSettings","syncOpencodeBase","settingsBase","syncSettingsBase","syncSettings","dlog","AGENT","REQ_TIMEOUT","PROMPT_IDLE_MS","PROMPT_CAP_MS","FIRST_FRAME_MS","SESSION_MAP_FILE","child","initialized","idCounter","pending","assembling","stdoutBuf","lastFrameAt","frameSeq","currentOnProgress","answerStarted","thoughtStarted","seenUpdateTypes","seenTools","isAlive","reset","keyOf","loadSessionMap","saveSessionMap","handleReverseRequest","handleFrame","onStdout","request","promptRequest","ensureProcess","applyModel","ensureSession","log","sleep","sleep"],"sources":["../../wechat-ilink-core/dist/config.js","../../wechat-ilink-core/dist/api/types.js","../../wechat-ilink-core/dist/api/error-codes.js","../../wechat-ilink-core/dist/api/client.js","../../wechat-ilink-core/dist/api/transport-errors.js","../../wechat-ilink-core/dist/api/session-guard.js","../../wechat-ilink-core/dist/store/accounts.js","../../wechat-ilink-core/dist/store/account.js","../../wechat-ilink-core/dist/store/allow-from.js","../../wechat-ilink-core/dist/cdn/image.js","../src/brain-agents.ts","../src/brain-config.ts","../src/agent-mcp.ts","../src/mcp-sandbox.ts","../src/spawn-agent.ts","../src/session-map.ts","../src/acp-brain.ts","../src/image-reply.ts","../src/md-reply.ts","../src/worker-sandbox.ts","../src/worker-acp.ts","../src/task-manager.ts","../src/index.ts","../src/pm2-runner.ts","../src/proc-identity.ts","../src/watchdog-runner.ts","../src/brain-check.ts","../src/version.ts","../src/manage.ts","../src/cli.ts"],"sourcesContent":["/**\n * iLink 应用标识。\n * 原 fork 从 package.json 的 ilink_appid 字段读取(向上查找);拆为 workspace 多包后,\n * 该耦合不再可靠(mcp-server 打包后会找到错误的 package.json),固化为常量,并允许 env 覆盖。\n */\nexport const ILINK_APP_ID = process.env.ILINK_APP_ID?.trim() || \"bot\";\n","/**\n * iLink Bot API 协议类型(镜像服务端 proto:GetUpdatesReq/Resp、WeixinMessage、SendMessageReq 等)。\n *\n * 来源与依据:\n * - 字段与 proto 含义对照官方 Tencent/openclaw-weixin `src/api/types.ts` 整理(社区/官方最权威的公开实现)。\n * - 错误码(ret / errcode)语义见 `error-codes.ts`。\n * - 标 `[实测]` 的注释为 WeMind 在真机使用中验证/纠正的结论(详见笔记「迭代记录与待办」)。\n *\n * 约定:\n * - 全部走 JSON over HTTP;proto 里的 bytes 字段在 JSON 中是 **base64 字符串**。\n * - 协议字段绝大多数可选(服务端按场景下发),故统一用 `?`;本地自有类型(AccountData/ServerState/QrLoginState)按实际必填。\n */\n/** 消息方向(proto: MessageType)。USER=入站(用户发来),BOT=出站(机器人发出)。 */\nexport const MessageType = {\n NONE: 0,\n USER: 1,\n BOT: 2,\n};\n/** 消息项类型(proto: MessageItemType)。一条消息的 item_list 里每项是其中一种。 */\nexport const MessageItemType = {\n NONE: 0,\n TEXT: 1,\n IMAGE: 2,\n VOICE: 3,\n FILE: 4,\n VIDEO: 5,\n};\n/**\n * 消息状态(proto: MessageState)。\n * - NEW=0:新消息。\n * - GENERATING=1:生成中(流式/进行中)。\n * - FINISH=2:本回合结束。\n * [实测] 关键:发送进度/中间消息用 **GENERATING**,仅最终答案用 **FINISH**——\n * FINISH 会结束当前回合(关闭该 context 的发送窗口),若进度也用 FINISH 会提前关掉会话。\n * 详见笔记里程碑 21/23 与 README「进度提示与连续发送上限」。\n */\nexport const MessageState = {\n NEW: 0,\n GENERATING: 1,\n FINISH: 2,\n};\n/** 打字状态(proto: TypingStatus)。1=显示「正在输入」,2=取消。配合 sendTyping 使用。 */\nexport const TypingStatus = {\n TYPING: 1,\n CANCEL: 2,\n};\n/** 上传媒体类型(proto: UploadMediaType)。getUploadUrl 的 media_type 取值。 */\nexport const UploadMediaType = {\n IMAGE: 1,\n VIDEO: 2,\n FILE: 3,\n VOICE: 4,\n};\n","/**\n * iLink Bot API 错误码定义与分类。\n *\n * 权威来源:逆向自官方 Tencent/openclaw-weixin 的「iLink Bot API 协议参考」\n * (corespeed-io/wechatbot `docs/protocol.md` 的 Error Codes 表,社区逆向、目前最权威的公开文档)。\n *\n * 两个字段,语义不同:\n * - `ret` :发送/上传等 RPC 的返回码(sendmessage / getuploadurl 等响应体)。\n * - `errcode`:长轮询/会话类接口的错误码(getupdates 等)。\n *\n * 已知错误码(截至文档):\n * | 码 | 字段 | 含义 | 处理 |\n * |---------------|----------|------------------------------|----------------------------------------|\n * | 0 | ret | 成功 | — |\n * | -2 | ret | 参数错误 (Parameter error) | 检查请求参数 |\n * | -14 | errcode | 会话过期 (Session expired) | 重新登录(本项目对该账号冷却 1 小时) |\n *\n * ⚠️ 重要澄清(避免重蹈覆辙):\n * - `ret = -2` 官方定义是**参数错误**,**不是**\"context_token 按时间过期\"。\n * 常见诱因:context_token 失效/与当前会话不匹配、必填字段缺失/格式不对。\n * - context_token 官方说明:按 `(accountId, userId)` **缓存、持久化、重用**,\n * **仅在会话过期(-14)/重新登录时清除**——**没有**文档化的时间 TTL。\n * - 真正\"按时间/会话过期\"的是 `errcode = -14`(会话级),与 -2 无关。\n */\n/** 发送/上传 RPC 的 `ret` 码。 */\nexport const ILINK_RET = {\n OK: 0,\n /** 参数错误:检查请求(最常见为 context_token 失效/不匹配;也可能被复用为发送限流——短时连续发送过多,见 README「连续发送上限」)。 */\n PARAM_ERROR: -2,\n};\n/** 长轮询/会话类接口的 `errcode` 码。 */\nexport const ILINK_ERRCODE = {\n /** 会话过期,需重新登录。 */\n SESSION_EXPIRED: -14,\n};\n/** 把一个 ret/errcode 数值分类。 */\nexport function classifyILinkCode(code) {\n if (code === undefined || code === null || code === 0)\n return \"ok\";\n if (code === ILINK_ERRCODE.SESSION_EXPIRED)\n return \"session_expired\";\n if (code === ILINK_RET.PARAM_ERROR)\n return \"param_error\";\n return \"unknown\";\n}\n/** 返回可读标签 + 处理建议,用于日志与提示。 */\nexport function describeILinkCode(code) {\n switch (classifyILinkCode(code)) {\n case \"ok\":\n return { kind: \"ok\", label: `ok(${code ?? 0})`, hint: \"\" };\n case \"session_expired\":\n return { kind: \"session_expired\", label: \"会话过期(-14)\", hint: \"需重新登录\" };\n case \"param_error\":\n return {\n kind: \"param_error\",\n label: \"参数错误(-2)\",\n hint: \"检查请求参数(最常见:context_token 失效/不匹配;或发送限流——短时连续发送过多)\",\n };\n default:\n return { kind: \"unknown\", label: `未知错误码(${code})`, hint: \"对照 iLink 协议文档排查\" };\n }\n}\n/** iLink 接口返回非 0 ret/errcode 时抛出的结构化错误,携带原始码与分类,便于上层按类型处理。 */\nexport class ILinkError extends Error {\n code;\n kind;\n constructor(op, code, errmsg) {\n const { kind, label, hint } = describeILinkCode(code);\n super(`${op} failed: ${label}${hint ? ` — ${hint}` : \"\"}${errmsg ? ` (${errmsg})` : \"\"}`);\n this.name = \"ILinkError\";\n this.code = code;\n this.kind = kind;\n }\n}\n","import crypto from \"node:crypto\";\nimport { ILINK_APP_ID } from \"../config.js\";\nimport { ILinkError } from \"./error-codes.js\";\nconst DEFAULT_LONG_POLL_TIMEOUT_MS = 35_000;\nconst DEFAULT_API_TIMEOUT_MS = 15_000;\nconst QR_LONG_POLL_TIMEOUT_MS = 35_000;\n// 对外声明的 iLink 客户端版本:模仿官方 openclaw-weixin 的版本,\n// 避免后端因客户端版本过低而拒绝/快速过期二维码。需随官方更新——故支持 env 覆盖,免改代码。\nconst ILINK_CLIENT_VERSION = process.env.ILINK_CLIENT_VERSION?.trim() || \"2.4.3\";\nconst CHANNEL_VERSION = ILINK_CLIENT_VERSION;\n/**\n * iLink-App-ClientVersion: uint32 编码 0x00MMNNPP(major<<16 | minor<<8 | patch)。\n * 编码方式借鉴自官方 Tencent/openclaw-weixin(MIT)。\n */\nexport function buildClientVersion(version) {\n const [major = 0, minor = 0, patch = 0] = version.split(\".\").map((p) => parseInt(p, 10) || 0);\n return ((major & 0xff) << 16) | ((minor & 0xff) << 8) | (patch & 0xff);\n}\nconst ILINK_APP_CLIENT_VERSION = String(buildClientVersion(ILINK_CLIENT_VERSION));\n/** iLink 公共请求头:所有 GET/POST 都需携带,缺失会导致服务端拒绝连接。 */\nfunction buildCommonHeaders() {\n return {\n \"iLink-App-Id\": ILINK_APP_ID,\n \"iLink-App-ClientVersion\": ILINK_APP_CLIENT_VERSION,\n };\n}\nfunction ensureTrailingSlash(url) {\n return url.endsWith(\"/\") ? url : `${url}/`;\n}\nfunction randomWechatUin() {\n const uint32 = crypto.randomBytes(4).readUInt32BE(0);\n return Buffer.from(String(uint32), \"utf-8\").toString(\"base64\");\n}\nfunction buildHeaders(opts) {\n const headers = {\n \"Content-Type\": \"application/json\",\n AuthorizationType: \"ilink_bot_token\",\n \"Content-Length\": String(Buffer.byteLength(opts.body, \"utf-8\")),\n \"X-WECHAT-UIN\": randomWechatUin(),\n ...buildCommonHeaders(),\n };\n if (opts.token?.trim()) {\n headers.Authorization = `Bearer ${opts.token.trim()}`;\n }\n return headers;\n}\nasync function apiFetch(params) {\n const base = ensureTrailingSlash(params.baseUrl);\n const url = new URL(params.endpoint, base);\n const hdrs = buildHeaders({ token: params.token, body: params.body });\n const controller = new AbortController();\n const t = setTimeout(() => controller.abort(), params.timeoutMs);\n try {\n const res = await fetch(url.toString(), {\n method: \"POST\",\n headers: hdrs,\n body: params.body,\n signal: controller.signal,\n });\n const rawText = await res.text(); // 读完响应体后再 clearTimeout:否则慢 body 不受超时保护、可能无限阻塞(长轮询尤甚)\n clearTimeout(t);\n if (!res.ok) {\n throw new Error(`${params.label} ${res.status}: ${rawText}`);\n }\n return rawText;\n }\n catch (err) {\n clearTimeout(t);\n throw err;\n }\n}\n/** 防御性解析 API JSON 响应:失败抛带 label 与响应片段的清晰错误,而非裸 SyntaxError(畸形/代理 HTML/截断响应不致以晦涩错误打断调用方)。 */\nfunction parseJsonResp(raw, label) {\n try {\n return JSON.parse(raw);\n }\n catch {\n throw new Error(`${label}: response not JSON (${raw.slice(0, 120)})`);\n }\n}\nexport function buildBaseInfo() {\n return { channel_version: CHANNEL_VERSION };\n}\nexport async function getUpdates(params) {\n const timeout = params.timeoutMs ?? DEFAULT_LONG_POLL_TIMEOUT_MS;\n try {\n const rawText = await apiFetch({\n baseUrl: params.baseUrl,\n endpoint: \"ilink/bot/getupdates\",\n body: JSON.stringify({\n get_updates_buf: params.updatesBuf ?? \"\",\n base_info: buildBaseInfo(),\n }),\n token: params.token,\n timeoutMs: timeout,\n label: \"getUpdates\",\n });\n return parseJsonResp(rawText, \"getUpdates\");\n }\n catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n return { ret: 0, msgs: [], get_updates_buf: params.updatesBuf };\n }\n throw err;\n }\n}\nexport async function sendMessage(params) {\n const raw = await apiFetch({\n baseUrl: params.baseUrl,\n endpoint: \"ilink/bot/sendmessage\",\n body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),\n token: params.token,\n timeoutMs: params.timeoutMs ?? DEFAULT_API_TIMEOUT_MS,\n label: \"sendMessage\",\n });\n // iLink 即使 HTTP 200 也可能在响应体返回非 0 ret/errcode 表示失败。语义见 error-codes.ts:\n // ret=-2 参数错误(最常见 context_token 失效/不匹配);errcode=-14 会话过期。\n // 不校验会导致\"假成功\"——消息其实没送达。仅在明确拿到非 0 错误码时抛 ILinkError(携带码+分类,供上层按类型处理)。\n let code;\n let errmsg;\n try {\n const resp = raw\n ? JSON.parse(raw)\n : null;\n code = resp?.ret ?? resp?.errcode;\n errmsg = resp?.errmsg;\n }\n catch {\n /* 空/非 JSON 的成功响应,忽略 */\n }\n if (code !== undefined && code !== 0) {\n throw new ILinkError(\"sendMessage\", code, errmsg);\n }\n}\n/** 取 CDN 上传地址(发图/发文件用)。返回 upload_full_url 或 upload_param。 */\nexport async function getUploadUrl(params) {\n const raw = await apiFetch({\n baseUrl: params.baseUrl,\n endpoint: \"ilink/bot/getuploadurl\",\n body: JSON.stringify({\n filekey: params.filekey,\n media_type: params.media_type,\n to_user_id: params.to_user_id,\n rawsize: params.rawsize,\n rawfilemd5: params.rawfilemd5,\n filesize: params.filesize,\n no_need_thumb: params.no_need_thumb,\n aeskey: params.aeskey,\n base_info: buildBaseInfo(),\n }),\n token: params.token,\n timeoutMs: params.timeoutMs ?? DEFAULT_API_TIMEOUT_MS,\n label: \"getUploadUrl\",\n });\n return parseJsonResp(raw, \"getUploadUrl\");\n}\nexport async function fetchQRCode(apiBaseUrl) {\n // 对齐官方:get_bot_qrcode 为 POST,body 带 local_token_list,并走全套请求头。\n const rawText = await apiFetch({\n baseUrl: apiBaseUrl,\n endpoint: \"ilink/bot/get_bot_qrcode?bot_type=3\",\n body: JSON.stringify({ local_token_list: [] }),\n timeoutMs: DEFAULT_API_TIMEOUT_MS,\n label: \"fetchQRCode\",\n });\n return parseJsonResp(rawText, \"fetchQRCode\");\n}\nexport async function pollQRStatus(apiBaseUrl, qrcode) {\n const base = ensureTrailingSlash(apiBaseUrl);\n const url = new URL(`ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(qrcode)}`, base);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), QR_LONG_POLL_TIMEOUT_MS);\n try {\n const response = await fetch(url.toString(), {\n headers: buildCommonHeaders(),\n signal: controller.signal,\n });\n if (!response.ok) {\n const body = await response.text().catch(() => \"(unreadable)\");\n clearTimeout(timer);\n throw new Error(`Failed to poll QR status: ${response.status} ${response.statusText} ${body}`);\n }\n const text = await response.text();\n clearTimeout(timer);\n return parseJsonResp(text, \"pollQRStatus\");\n }\n catch (err) {\n clearTimeout(timer);\n if (err instanceof Error && err.name === \"AbortError\") {\n return { status: \"wait\" };\n }\n throw err;\n }\n}\nexport function generateId(prefix) {\n return `${prefix}:${Date.now()}-${crypto.randomBytes(4).toString(\"hex\")}`;\n}\nconst DEFAULT_CONFIG_TIMEOUT_MS = 15_000;\nexport async function getConfig(params) {\n const rawText = await apiFetch({\n baseUrl: params.baseUrl,\n endpoint: \"ilink/bot/getconfig\",\n body: JSON.stringify({\n ilink_user_id: params.ilinkUserId,\n context_token: params.contextToken,\n base_info: buildBaseInfo(),\n }),\n token: params.token,\n timeoutMs: DEFAULT_CONFIG_TIMEOUT_MS,\n label: \"getConfig\",\n });\n return parseJsonResp(rawText, \"getConfig\");\n}\nexport async function sendTyping(params) {\n await apiFetch({\n baseUrl: params.baseUrl,\n endpoint: \"ilink/bot/sendtyping\",\n body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),\n token: params.token,\n timeoutMs: DEFAULT_CONFIG_TIMEOUT_MS,\n label: \"sendTyping\",\n });\n}\n/** 通知服务端该 bot 客户端上线(best-effort,失败不阻断)。 */\nexport async function notifyStart(params) {\n try {\n await apiFetch({\n baseUrl: params.baseUrl,\n endpoint: \"ilink/bot/msg/notifystart\",\n body: JSON.stringify({ base_info: buildBaseInfo() }),\n token: params.token,\n timeoutMs: DEFAULT_CONFIG_TIMEOUT_MS,\n label: \"notifyStart\",\n });\n }\n catch (err) {\n console.error(`[wemind/wechat-ilink-core/ilink] notifyStart failed (ignored, best-effort): ${String(err)}`);\n }\n}\n/** 通知服务端该 bot 客户端下线(best-effort,失败不阻断)。 */\nexport async function notifyStop(params) {\n try {\n await apiFetch({\n baseUrl: params.baseUrl,\n endpoint: \"ilink/bot/msg/notifystop\",\n body: JSON.stringify({ base_info: buildBaseInfo() }),\n token: params.token,\n timeoutMs: DEFAULT_CONFIG_TIMEOUT_MS,\n label: \"notifyStop\",\n });\n }\n catch (err) {\n console.error(`[wemind/wechat-ilink-core/ilink] notifyStop failed (ignored, best-effort): ${String(err)}`);\n }\n}\n","/**\n * 传输层错误分类:把 fetch/网络层抛出的错误归类,判定是否可重试。\n * 与 error-codes.ts(iLink 业务 ret/errcode)正交——这里只管「网络传输」层面的失败。\n * 借鉴 wxclawbot-cli(MIT)client.ts 的 classifyTransportError 思路。\n */\n/** 归类一个传输层错误。timeout(AbortError)一律可重试。 */\nexport function classifyTransportError(err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n return { kind: \"timeout\", retryable: true, message: err.message || \"request timeout\" };\n }\n const message = err instanceof Error ? err.message : String(err);\n const code = err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n typeof err.code === \"string\"\n ? err.code\n : undefined;\n const cause = err instanceof Error && err.cause != null ? String(err.cause) : undefined;\n const s = `${message} ${code ?? \"\"} ${cause ?? \"\"}`.toLowerCase();\n if (s.includes(\"enotfound\") || s.includes(\"dns\"))\n return { kind: \"dns\", retryable: true, message, code };\n if (s.includes(\"econnreset\") || s.includes(\"socket hang up\"))\n return { kind: \"connection\", retryable: true, message, code };\n // TLS/证书错误判定必须排在 \"fetch failed\" 兜底之前:Node fetch 的网络失败顶层恒为 \"TypeError: fetch failed\",\n // 真正的 cert/tls 原因在 err.cause 里(已并入 s)。若放在 network 分支之后,会被 \"fetch failed\" 先匹配成可重试 → 此分支成死代码。\n if (s.includes(\"cert\") || s.includes(\"tls\") || s.includes(\"ssl\"))\n return { kind: \"tls\", retryable: false, message, code };\n if (s.includes(\"econnrefused\") ||\n s.includes(\"ehostunreach\") ||\n s.includes(\"enetunreach\") ||\n s.includes(\"etimedout\") ||\n s.includes(\"network\") ||\n s.includes(\"fetch failed\")) {\n return { kind: \"network\", retryable: true, message, code };\n }\n return { kind: \"unknown\", retryable: false, message, code };\n}\n","/**\n * 会话守卫:处理服务端 errcode -14(bot 会话超时)。\n * 命中后对该账号进入 1 小时冷却,期间快速返回而非反复长轮询空转。\n * 逻辑借鉴自官方 Tencent/openclaw-weixin(MIT),按 MCP 请求驱动场景精简。\n */\n/** 服务端返回的会话超时错误码。 */\nexport const SESSION_EXPIRED_ERRCODE = -14;\nconst PAUSE_MS = 60 * 60 * 1000;\nconst pauseUntil = new Map();\n/** 命中 -14 后暂停该账号一小时。 */\nexport function pauseSession(accountId) {\n pauseUntil.set(accountId, Date.now() + PAUSE_MS);\n}\n/** 距离冷却结束的剩余毫秒数(未暂停时为 0)。 */\nexport function getRemainingPauseMs(accountId) {\n const until = pauseUntil.get(accountId);\n if (until === undefined)\n return 0;\n const remaining = until - Date.now();\n if (remaining <= 0) {\n pauseUntil.delete(accountId);\n return 0;\n }\n return remaining;\n}\nexport function isSessionPaused(accountId) {\n return getRemainingPauseMs(accountId) > 0;\n}\n/** 重新登录成功后清除冷却。 */\nexport function clearSessionPause(accountId) {\n pauseUntil.delete(accountId);\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n/**\n * 账号注册表:管理 ~/.wemind 下的多账号清单。\n * - 每个账号一个独立子目录(key = 由 botId 派生的唯一标识,见 deriveAccountKey)。\n * - accounts.json 记录 key → 元数据(botId/userId/baseUrl/label/addedAt)与默认账号。\n * label 为人类可读别名(默认等于 key,可改),folder/key 始终唯一且由微信 botId 派生。\n */\n/** WeMind 根目录(所有账号子目录与注册表的父目录)。可用 WEMIND_HOME 覆盖。 */\nexport const WEMIND_ROOT = process.env.WEMIND_HOME?.trim() || path.join(os.homedir(), \".wemind\");\nconst REGISTRY_FILE = path.join(WEMIND_ROOT, \"accounts.json\");\n/** 把 botId(如 feead781eb44@im.bot)净化成可作目录名的唯一 key(feead781eb44)。 */\nexport function deriveAccountKey(botId) {\n const head = (botId || \"\").split(\"@\")[0] || botId || \"\";\n const cleaned = head.replace(/[^a-zA-Z0-9_-]/g, \"\").trim();\n return cleaned || \"account\";\n}\n/** 某账号的状态目录:~/.wemind/<key>。 */\nexport function accountDir(key) {\n return path.join(WEMIND_ROOT, key);\n}\nexport function loadRegistry() {\n let raw;\n try {\n raw = fs.readFileSync(REGISTRY_FILE, \"utf-8\");\n }\n catch {\n return { accounts: {} }; // 不存在:正常(首个账号登记前)\n }\n try {\n const j = JSON.parse(raw);\n if (j && typeof j === \"object\" && j.accounts)\n return j;\n throw new Error(\"registry shape invalid\");\n }\n catch (e) {\n // 存在但损坏:不要当空注册表直接覆盖(紧接的 save 会抹掉所有已登记账号);备份坏文件 + 记日志\n console.error(`[wemind/wechat-ilink-core/accounts] ${REGISTRY_FILE} parse failed, backing up: ${String(e)}`);\n try {\n fs.renameSync(REGISTRY_FILE, `${REGISTRY_FILE}.corrupt-${Date.now()}`);\n }\n catch {\n /* best-effort */\n }\n return { accounts: {} };\n }\n}\n/** 原子写注册表:临时文件 + rename,避免写途崩溃/并发损坏。 */\nexport function saveRegistry(reg) {\n fs.mkdirSync(WEMIND_ROOT, { recursive: true });\n const tmp = `${REGISTRY_FILE}.tmp-${process.pid}-${Date.now()}`;\n fs.writeFileSync(tmp, JSON.stringify(reg, null, 2), \"utf-8\");\n fs.renameSync(tmp, REGISTRY_FILE);\n}\nexport function listAccounts() {\n const reg = loadRegistry();\n return Object.values(reg.accounts).sort((a, b) => a.addedAt - b.addedAt);\n}\nexport function getDefaultKey() {\n const reg = loadRegistry();\n if (reg.default && reg.accounts[reg.default])\n return reg.default;\n const keys = Object.keys(reg.accounts);\n return keys.length ? keys[0] : undefined;\n}\nexport function setDefaultKey(key) {\n const reg = loadRegistry();\n if (!reg.accounts[key])\n throw new Error(`account not found: ${key}`);\n reg.default = key;\n saveRegistry(reg);\n}\n/** 登记/更新一个账号;首个账号自动设为默认。 */\nexport function registerAccount(meta) {\n const reg = loadRegistry();\n const existing = reg.accounts[meta.key];\n const full = {\n key: meta.key,\n botId: meta.botId,\n userId: meta.userId,\n baseUrl: meta.baseUrl,\n label: meta.label || existing?.label || meta.key,\n addedAt: existing?.addedAt ?? meta.addedAt ?? Date.now(),\n };\n reg.accounts[meta.key] = full;\n if (!reg.default)\n reg.default = meta.key;\n saveRegistry(reg);\n return full;\n}\n/** 移除注册表中的账号条目(不删除磁盘目录,由调用方决定)。 */\nexport function unregisterAccount(key) {\n const reg = loadRegistry();\n delete reg.accounts[key];\n if (reg.default === key) {\n const rest = Object.keys(reg.accounts);\n reg.default = rest.length ? rest[0] : undefined;\n }\n saveRegistry(reg);\n}\n/** 设置/修改某账号的人类可读别名。 */\nexport function setLabel(key, label) {\n const reg = loadRegistry();\n if (!reg.accounts[key])\n throw new Error(`account not found: ${key}`);\n reg.accounts[key].label = label;\n saveRegistry(reg);\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { WEMIND_ROOT } from \"./accounts.js\";\n/**\n * 数据目录解析(按优先级):\n * 1. WECHAT_DATA_DIR:显式指定完整路径(最高优先级,便于任意自定义/多账号)。\n * 2. 否则 ~/.wemind/<account>,其中 <account> 取自 WECHAT_ACCOUNT,默认 \"default\"。\n * account.json / state.json / brain-sessions.json / brain-mcps.json / brain-home 等都落在此目录下。\n * 多账号即「不同 WECHAT_ACCOUNT(或 WECHAT_DATA_DIR)→ 不同目录 → 互相隔离」。\n */\nexport const ACCOUNT_KEY = process.env.WECHAT_ACCOUNT?.trim() || \"default\";\nexport const DATA_DIR = process.env.WECHAT_DATA_DIR?.trim() || path.join(WEMIND_ROOT, ACCOUNT_KEY);\nfunction ensureDir(dir) {\n fs.mkdirSync(dir, { recursive: true });\n}\nfunction readJson(filePath) {\n let raw;\n try {\n raw = fs.readFileSync(filePath, \"utf-8\");\n }\n catch {\n return null; // 文件不存在:正常(首次运行)\n }\n try {\n return JSON.parse(raw);\n }\n catch (e) {\n // 文件存在但解析失败(写途崩溃/外部破坏):不静默清空,备份坏文件 + 记日志,便于排查与人工恢复\n console.error(`[wemind/wechat-ilink-core/store] ${filePath} parse failed, backing up and treating as empty: ${String(e)}`);\n try {\n fs.renameSync(filePath, `${filePath}.corrupt-${Date.now()}`);\n }\n catch {\n /* best-effort */\n }\n return null;\n }\n}\n/** 原子写:先写临时文件再 rename 覆盖(同盘 rename 原子),避免写途崩溃/并发产生半截损坏文件。 */\nfunction writeJson(filePath, data) {\n ensureDir(path.dirname(filePath));\n const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;\n fs.writeFileSync(tmp, JSON.stringify(data, null, 2), \"utf-8\");\n fs.renameSync(tmp, filePath);\n}\nexport function loadAccount() {\n return readJson(path.join(DATA_DIR, \"account.json\"));\n}\nlet warnedWinPerms = false;\nexport function saveAccount(account) {\n const file = path.join(DATA_DIR, \"account.json\");\n writeJson(file, account);\n if (process.platform === \"win32\") {\n // Windows 上 fs.chmod 实际是 no-op,无法用 0o600 限制访问。\n // 不做无效操作,改为一次性提醒(详见 README 的安全说明)。\n if (!warnedWinPerms) {\n warnedWinPerms = true;\n console.error(`[wemind/wechat-ilink-core] warning: ${file} stores the bot token in plaintext; ` +\n `Windows cannot restrict it via chmod. Protect ${DATA_DIR} with folder permissions or disk encryption.`);\n }\n }\n else {\n try {\n fs.chmodSync(file, 0o600);\n }\n catch {\n /* best-effort */\n }\n }\n}\nexport function clearAccount() {\n try {\n fs.unlinkSync(path.join(DATA_DIR, \"account.json\"));\n }\n catch {\n /* ignore */\n }\n}\nexport function loadState() {\n const defaultState = {\n updatesBuf: \"\",\n contextTokens: {},\n lastMessageId: 0,\n };\n return readJson(path.join(DATA_DIR, \"state.json\")) ?? defaultState;\n}\nexport function saveState(state) {\n writeJson(path.join(DATA_DIR, \"state.json\"), state);\n}\nexport function resetState() {\n saveState({ updatesBuf: \"\", contextTokens: {}, lastMessageId: 0 });\n}\nexport function loadQrState() {\n return readJson(path.join(DATA_DIR, \"qr_login.json\"));\n}\nexport function saveQrState(state) {\n writeJson(path.join(DATA_DIR, \"qr_login.json\"), state);\n}\nexport function clearQrState() {\n try {\n fs.unlinkSync(path.join(DATA_DIR, \"qr_login.json\"));\n }\n catch {\n /* ignore */\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { DATA_DIR } from \"./account.js\";\nfunction filePath(dir = DATA_DIR) {\n return path.join(dir, \"allowFrom.json\");\n}\nexport function readAllowFrom(dir = DATA_DIR) {\n let raw;\n try {\n raw = fs.readFileSync(filePath(dir), \"utf-8\");\n }\n catch {\n return []; // 不存在:正常(daemon 启动会种入本人)\n }\n try {\n const j = JSON.parse(raw);\n return Array.isArray(j.allowFrom)\n ? j.allowFrom.filter((x) => typeof x === \"string\" && !!x)\n : [];\n }\n catch (e) {\n // 存在但损坏:本清单是安全闸,损坏返回 [] 是 fail-closed(全员锁死),必须可见 → 记日志 + 备份坏文件\n console.error(`[wemind/wechat-ilink-core/allow-from] ${filePath(dir)} parse failed, backing up: ${String(e)}`);\n try {\n fs.renameSync(filePath(dir), `${filePath(dir)}.corrupt-${Date.now()}`);\n }\n catch {\n /* best-effort */\n }\n return [];\n }\n}\n/** 原子写:临时文件 + rename,避免写途崩溃/并发损坏这个安全闸文件。 */\nfunction writeAllowFrom(list, dir = DATA_DIR) {\n fs.mkdirSync(dir, { recursive: true });\n const uniq = [...new Set(list.filter((x) => typeof x === \"string\" && !!x))];\n const fp = filePath(dir);\n const tmp = `${fp}.tmp-${process.pid}-${Date.now()}`;\n fs.writeFileSync(tmp, JSON.stringify({ version: 1, allowFrom: uniq }, null, 2), \"utf-8\");\n fs.renameSync(tmp, fp);\n}\n/** 加入授权用户(去重)。返回更新后的清单。 */\nexport function addAllowFrom(userId, dir = DATA_DIR) {\n const list = readAllowFrom(dir);\n if (userId && !list.includes(userId))\n list.push(userId);\n writeAllowFrom(list, dir);\n return list;\n}\n/** 移除授权用户。返回更新后的清单。 */\nexport function removeAllowFrom(userId, dir = DATA_DIR) {\n const list = readAllowFrom(dir).filter((x) => x !== userId);\n writeAllowFrom(list, dir);\n return list;\n}\n/** 该 userId 是否被授权。 */\nexport function isAllowed(userId, dir = DATA_DIR) {\n return !!userId && readAllowFrom(dir).includes(userId);\n}\n","import { createCipheriv, createDecipheriv, createHash, randomBytes } from \"node:crypto\";\nimport { getUploadUrl, sendMessage, generateId } from \"../api/client.js\";\nimport { ILinkError } from \"../api/error-codes.js\";\nimport { MessageType, MessageState, MessageItemType, UploadMediaType, } from \"../api/types.js\";\n/**\n * 收图:从 iLink CDN 下载并 AES-128-ECB 解密图片。\n * 机制借鉴官方 Tencent/openclaw-weixin(MIT)的 cdn/pic-decrypt + cdn-url。\n */\nconst CDN_BASE_URL = process.env.WECHAT_CDN_BASE_URL?.trim() || \"https://novac2c.cdn.weixin.qq.com/c2c\";\nexport function decryptAesEcb(ciphertext, key) {\n const d = createDecipheriv(\"aes-128-ecb\", key, null); // PKCS7 默认\n return Buffer.concat([d.update(ciphertext), d.final()]);\n}\n/** 解析图片 AES key → 16 字节。aeskey 为 hex;media.aes_key 为 base64(可能是 base64(hex 字符串))。 */\nexport function resolveKey(img) {\n if (img.aeskey && /^[0-9a-fA-F]{32}$/.test(img.aeskey))\n return Buffer.from(img.aeskey, \"hex\");\n const b64 = img.media?.aes_key;\n if (b64) {\n const decoded = Buffer.from(b64, \"base64\");\n if (decoded.length === 16)\n return decoded;\n if (decoded.length === 32 && /^[0-9a-fA-F]{32}$/.test(decoded.toString(\"ascii\"))) {\n return Buffer.from(decoded.toString(\"ascii\"), \"hex\");\n }\n }\n return null;\n}\n/** 下载并解密一张图片,返回明文 Buffer(失败抛错)。 */\nexport async function downloadImage(img) {\n const url = img.media?.full_url ||\n (img.media?.encrypt_query_param\n ? `${CDN_BASE_URL}/download?encrypted_query_param=${encodeURIComponent(img.media.encrypt_query_param)}`\n : \"\");\n if (!url)\n throw new Error(\"image: 无 full_url/encrypt_query_param\");\n const key = resolveKey(img);\n if (!key)\n throw new Error(\"image: 无法解析 aes key\");\n const res = await fetch(url, { signal: AbortSignal.timeout(20000) });\n if (!res.ok)\n throw new Error(`image: CDN 下载失败 ${res.status}`);\n const ciphertext = Buffer.from(await res.arrayBuffer());\n return decryptAesEcb(ciphertext, key);\n}\n// ===== 发图:AES-128-ECB 加密 + CDN 上传 + 拼 image_item 发送 =====\n// 机制借鉴官方 Tencent/openclaw-weixin(MIT)的 cdn/aes-ecb + cdn/upload + cdn/cdn-upload + messaging/send。\n/** AES-128-ECB 加密(PKCS7 默认)。 */\nexport function encryptAesEcb(plaintext, key) {\n const c = createCipheriv(\"aes-128-ecb\", key, null);\n return Buffer.concat([c.update(plaintext), c.final()]);\n}\n/** AES-128-ECB 密文大小(PKCS7 补齐到 16 字节边界)。 */\nexport function aesEcbPaddedSize(plaintextSize) {\n return Math.ceil((plaintextSize + 1) / 16) * 16;\n}\n/** 加密并上传一段 buffer 到 CDN,返回下载用的 encrypt_query_param(响应头 x-encrypted-param)。 */\nasync function uploadBufferToCdn(params) {\n const { buf, aeskey, filekey, uploadFullUrl, uploadParam } = params;\n const ciphertext = encryptAesEcb(buf, aeskey);\n const url = uploadFullUrl?.trim() ||\n (uploadParam\n ? `${CDN_BASE_URL}/upload?encrypted_query_param=${encodeURIComponent(uploadParam)}&filekey=${encodeURIComponent(filekey)}`\n : \"\");\n if (!url)\n throw new Error(\"upload: 无 upload_full_url/upload_param\");\n let lastErr;\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/octet-stream\" },\n body: new Uint8Array(ciphertext),\n signal: AbortSignal.timeout(30000),\n });\n if (res.status >= 400 && res.status < 500)\n throw new Error(`CDN 上传客户端错误 ${res.status}`); // 不重试\n if (res.status !== 200)\n throw new Error(`CDN 上传服务端错误 ${res.status}`);\n const dp = res.headers.get(\"x-encrypted-param\");\n if (!dp)\n throw new Error(\"CDN 上传响应缺 x-encrypted-param\");\n return dp;\n }\n catch (e) {\n lastErr = e;\n if (e instanceof Error && e.message.includes(\"客户端错误\"))\n throw e;\n if (attempt < 3)\n continue;\n }\n }\n throw lastErr instanceof Error ? lastErr : new Error(\"CDN 上传失败\");\n}\n/** getUploadUrl + 加密上传,返回媒体引用所需信息。media_type 见 UploadMediaType。 */\nasync function uploadMediaBytes(params) {\n const { baseUrl, token, to, data, mediaType } = params;\n const rawsize = data.length;\n const rawfilemd5 = createHash(\"md5\").update(data).digest(\"hex\");\n const filesize = aesEcbPaddedSize(rawsize);\n const filekey = randomBytes(16).toString(\"hex\");\n const aeskey = randomBytes(16);\n const up = await getUploadUrl({\n baseUrl,\n token,\n filekey,\n media_type: mediaType,\n to_user_id: to,\n rawsize,\n rawfilemd5,\n filesize,\n no_need_thumb: true,\n aeskey: aeskey.toString(\"hex\"),\n });\n const code = up.ret ?? up.errcode;\n if (code !== undefined && code !== 0)\n throw new ILinkError(\"getUploadUrl\", code, up.errmsg);\n const downloadParam = await uploadBufferToCdn({\n buf: data,\n aeskey,\n filekey,\n uploadFullUrl: up.upload_full_url,\n uploadParam: up.upload_param,\n });\n return { downloadParam, aeskeyHex: aeskey.toString(\"hex\"), filesize, rawsize };\n}\n/** 拼 CDN media 引用(消息里给接收方下载+解密用)。 */\nfunction buildMediaRef(downloadParam, aeskeyHex) {\n return {\n encrypt_query_param: downloadParam,\n aes_key: Buffer.from(aeskeyHex).toString(\"base64\"),\n encrypt_type: 1,\n };\n}\nconst IMAGE_EXTS = new Set([\".png\", \".jpg\", \".jpeg\", \".gif\", \".webp\", \".bmp\"]);\nconst VIDEO_EXTS = new Set([\".mp4\", \".mov\", \".webm\", \".mkv\", \".avi\"]);\n/** 按文件名扩展名判定媒体类别(借鉴 wxclawbot media.ts classifyMedia)。其余一律按文件发送。 */\nexport function classifyMediaByName(name) {\n const i = name.lastIndexOf(\".\");\n const ext = i >= 0 ? name.slice(i).toLowerCase() : \"\";\n if (IMAGE_EXTS.has(ext))\n return \"image\";\n if (VIDEO_EXTS.has(ext))\n return \"video\";\n return \"file\";\n}\nconst UPLOAD_TYPE_BY_KIND = {\n image: UploadMediaType.IMAGE,\n video: UploadMediaType.VIDEO,\n file: UploadMediaType.FILE,\n};\n/** 主动发一张图片给用户(需 context_token,与发文本相同)。image 为图片明文 buffer。 */\nexport async function sendImage(params) {\n const { baseUrl, token, to, image, contextToken } = params;\n const { downloadParam, aeskeyHex, filesize } = await uploadMediaBytes({\n baseUrl,\n token,\n to,\n data: image,\n mediaType: UploadMediaType.IMAGE,\n });\n const req = {\n msg: {\n from_user_id: \"\",\n to_user_id: to,\n client_id: generateId(\"img\"),\n message_type: MessageType.BOT,\n message_state: MessageState.FINISH,\n item_list: [\n {\n type: MessageItemType.IMAGE,\n image_item: { media: buildMediaRef(downloadParam, aeskeyHex), mid_size: filesize },\n },\n ],\n context_token: contextToken,\n },\n };\n await sendMessage({ baseUrl, token, body: req });\n}\n/**\n * 主动发一个文件/视频/图片给用户(需 context_token)。\n * 类别按 fileName 扩展名自动判定(image/video/file),也可用 kind 显式指定。\n * 借鉴 wxclawbot media.ts 的类型识别与 item 组装;上传链路复用 WeMind 自有实现。\n */\nexport async function sendFile(params) {\n const { baseUrl, token, to, data, fileName, contextToken } = params;\n const kind = params.kind ?? classifyMediaByName(fileName);\n const { downloadParam, aeskeyHex, filesize, rawsize } = await uploadMediaBytes({\n baseUrl,\n token,\n to,\n data,\n mediaType: UPLOAD_TYPE_BY_KIND[kind],\n });\n const media = buildMediaRef(downloadParam, aeskeyHex);\n let item;\n if (kind === \"image\") {\n item = { type: MessageItemType.IMAGE, image_item: { media, mid_size: filesize } };\n }\n else if (kind === \"video\") {\n item = { type: MessageItemType.VIDEO, video_item: { media, video_size: filesize } };\n }\n else {\n item = {\n type: MessageItemType.FILE,\n file_item: { media, file_name: fileName, len: String(rawsize) },\n };\n }\n const req = {\n msg: {\n from_user_id: \"\",\n to_user_id: to,\n client_id: generateId(\"media\"),\n message_type: MessageType.BOT,\n message_state: MessageState.FINISH,\n item_list: [item],\n context_token: contextToken,\n },\n };\n await sendMessage({ baseUrl, token, body: req });\n}\n","import path from \"node:path\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\n\nimport type { BrainConfig } from \"./brain-config.js\";\n\n/**\n * ACP agent 预设与解析。\n *\n * WeMind 的大脑通过**标准 ACP(Agent Client Protocol,JSON-RPC over stdio)**驱动一个\n * agent CLI。ACP 是通用协议(kiro-cli、gemini-cli 等都支持),因此接入任意兼容 ACP 的\n * agent 不需要改协议代码——只需告诉我们三件事:① 二进制名;② 进入 ACP 模式的启动参数;\n * ③ 该 agent 读取「配置/数据目录」的环境变量名(我们把它指向 brain 沙箱)。\n *\n * 选择与覆盖(优先级从高到低):\n * ① 环境变量(一次性覆盖;临时试别的 agent 用它,别写进 profile):\n * - `WEMIND_BRAIN` 选预设(默认 `auto`:自动探测已安装的预设,见 AUTO_PROBE_ORDER)\n * - `WEMIND_BRAIN_BIN` 覆盖二进制(绝对路径或 PATH 上的名字)\n * - `WEMIND_BRAIN_ARGS` 覆盖启动参数(空格分隔)\n * - `WEMIND_BRAIN_HOME_ENV` 覆盖「配置目录环境变量名」\n * - `WEMIND_BRAIN_SHELL` 是否经 shell 启动(1/true)\n * - `WEMIND_BRAIN_INHERIT` 是否继承真实配置目录的记忆/规则(默认 1;0 关闭)\n * - `WEMIND_BRAIN_REAL_HOME` 覆盖被继承的真实配置目录(默认取预设 realHome)\n * - `WEMIND_BRAIN_INHERIT_DIRS` 覆盖继承的子目录清单(逗号/空格分隔)\n * - `KIRO_CLI_BIN` 旧的 kiro 专用覆盖(仅 kiro 预设、且未设 WEMIND_BRAIN_BIN 时仍生效)\n * ② 账号级持久配置(`<数据目录>/brain.json`,见 brain-config.ts):`agent` / `bin` / `args` /\n * `shell` / `homeEnvVar`。落盘后任何入口重启都能复现,切换用 `wemind brain set <预设>`。\n * ③ `auto`(未配置时的兜底):按 AUTO_PROBE_ORDER 取第一个二进制可解析的预设;一个都没有\n * 则标记 unavailable,由启动校验明确报错(不再「静默跑起来、第一条消息才炸」)。\n * 另:未知的 `WEMIND_BRAIN` 值不再静默回退 kiro,而是 `known:false`,交由启动校验报错。\n */\n\nexport type AcpAgentPreset = {\n name: string;\n bin: string; // PATH 上的二进制名(非 Windows)\n binWin?: string; // Windows 下可执行名(默认在 bin 后加 .exe)\n fallbackBinPaths?: string[]; // PATH 上找不到时的已知安装位置(如官方安装器的固定目录)\n args: string[]; // 进入 ACP 模式的启动参数\n homeEnvVar?: string; // agent 读取其配置/数据目录的环境变量名;指向 brain 沙箱。留空=不设置\n shell?: boolean; // 是否经 shell 启动(npx/.cmd/.bat 类适配器在 Windows 需要 true)\n // —— 继承「记忆/规则/技能」配置(让微信大脑与日常使用读到同样内容)——\n realHome?: string; // 该 agent 真实配置目录(被继承的来源)。不同 agent 布局不同,故下沉到预设\n inheritDirs?: string[]; // 从 realHome junction 进沙箱的「知识/记忆」类子目录(不含 MCP/会话目录)\n inheritFiles?: string[]; // 从 realHome 拷进沙箱配置目录的「单文件」记忆(如 AGENTS.md;目录式记忆用 inheritDirs)\n // —— 沙箱约定(不同 agent 的配置目录位置与 MCP 文件格式不同)——\n configSubdir?: string; // agent 配置目录相对沙箱根的子路径(kiro 空=沙箱根;opencode \"opencode\")\n mcpFormat?: \"kiro\" | \"opencode\"; // MCP 配置文件的组织格式(默认 kiro)\n mcpConfigFile?: string; // MCP 配置文件相对「配置目录」的路径(默认 settings/mcp.json)\n catalogFile?: string; // 可选 MCP 目录来源文件相对 realHome 的路径(默认 settings/mcp.json)\n env?: Record<string, string>; // 额外注入的环境变量;值里 %HOME% → 沙箱根目录\n // —— 协议/能力差异(按 agent 覆盖;默认保持 kiro/opencode 旧行为)——\n restore?: \"load\" | \"resume\"; // 恢复会话的方式:load(默认)或 resume(如 DSH)\n firstFrameMs?: number; // 首帧超时覆盖(默认 25s;慢启动/committed-only agent 可放宽)\n heartbeatMs?: number; // 静默期心跳间隔覆盖(默认 15s;有 thought/tool/usage 帧的 agent 通常不需要)\n images?: boolean; // 是否接受图片输入(默认 true;DSH 官方 0.2.1-rc.2 为 false)\n credentialFiles?: string[]; // 需要从 realHome 同步进沙箱的凭据文件(相对配置目录;新者胜)\n identityFiles?: string[]; // 必须与 realHome 保持一致的标识文件(相对配置目录;内容不同即覆盖)\n settingsBase?: string; // 以 realHome 下该文件为基底生成沙箱设置(剥离 MCP/插件/自定义供应商并关自动更新)\n model?: string; // 需要 session/set_config_option 选择的模型值(如 DSH 的 [\"deepseek-account\",\"deepseek-flash\"])\n mcpViaAcp?: boolean; // MCP 走 ACP session/new|resume 的 mcpServers(不写配置文件;如 DSH)\n};\n\n/**\n * 已知 ACP agent 预设。\n * - `kiro` 为默认且**已实测**。\n * - 其余均为**示例(未实测)**:参数/包名以各自官方为准,接入前请核对(见 docs/接入其它-ACP-Agent.md)。\n * - 区别两类:① **原生 ACP**(CLI 自带 ACP 模式,直接起,如 kiro、gemini);\n * ② **需适配器**(CLI 本身不说 ACP,要起一个适配器进程,如 Claude Code、Codex)。\n */\nexport const ACP_AGENT_PRESETS: Record<string, AcpAgentPreset> = {\n // —— 原生 ACP ——\n kiro: {\n name: \"kiro\",\n bin: \"kiro-cli\",\n binWin: \"kiro-cli.exe\",\n args: [\"acp\", \"--trust-all-tools\"],\n homeEnvVar: \"KIRO_HOME\",\n realHome: path.join(os.homedir(), \".kiro\"),\n inheritDirs: [\"steering\", \"memories\", \"skills\", \"agents\", \"powers\", \"tasks\", \"extensions\"],\n },\n // Gemini CLI 实验性 ACP(未实测,flag 以官方为准)。注:Gemini 的「记忆」是 ~/.gemini/GEMINI.md 等文件而非目录,\n // 与 kiro 的目录式不同,故未预置 inheritDirs;需要时按其布局用 WEMIND_BRAIN_REAL_HOME/_INHERIT_DIRS 配置或填此处。\n gemini: {\n name: \"gemini\",\n bin: \"gemini\",\n binWin: \"gemini.cmd\",\n args: [\"--experimental-acp\"],\n homeEnvVar: \"GEMINI_CLI_HOME\",\n shell: true,\n },\n // OpenCode:原生 ACP(`opencode acp`)。\n // - Windows 上是 npm shim(PATH 只有 opencode.cmd),故 shell:true + 裸名 binWin。\n // - 配置目录走 XDG_CONFIG_HOME(指向沙箱 → 配置落在 <沙箱>/opencode/),可真正替换默认 global 目录实现隔离;\n // (OPENCODE_CONFIG_DIR 只「追加」不替换 global,无法隔离,故不用它。)\n // - MCP 配置文件是 opencode.json 的 mcp.servers(格式与 kiro 的 settings/mcp.json 不同)。\n // - 记忆/技能为目录式 skills/agents/commands + 单文件 AGENTS.md。\n // 详见 docs/接入-OpenCode.md(含 M1 实测)。\n opencode: {\n name: \"opencode\",\n bin: \"opencode\",\n binWin: \"opencode\",\n args: [\"acp\"],\n shell: true,\n homeEnvVar: \"XDG_CONFIG_HOME\",\n realHome: path.join(os.homedir(), \".config\", \"opencode\"),\n inheritDirs: [\"skills\", \"agents\", \"commands\"],\n inheritFiles: [\"AGENTS.md\"],\n configSubdir: \"opencode\",\n mcpFormat: \"opencode\",\n mcpConfigFile: \"opencode.json\",\n catalogFile: \"opencode.json\",\n env: { OPENCODE_DISABLE_PROJECT_CONFIG: \"1\" }, // 不从 cwd 祖先目录发现用户 opencode.json\n },\n // DeepSeek Harness(官方 ACP profile)。\n // - 桌面端自带 `dsh.cmd`(只有 .cmd,没有无扩展名 dsh),故 binWin 必须写全;\n // - DSH_HOME 指向 brain 沙箱;`.credentials.yaml` 由沙箱同步(桌面登录态,对应 deepseek-account 路由);\n // - 无 session/load,用 session/resume;image:false(收图需上层拦截);\n // - 默认模型路由是 deepseek-official(需 API key),这里显式选 deepseek-account 才能复用桌面登录。\n // 详见 docs/接入-DeepSeek.md(含 0.2.1-rc.2 真机实测)。\n deepseek: {\n name: \"deepseek\",\n bin: \"dsh\",\n binWin: \"dsh.cmd\",\n args: [\"--profile\", \"acp\"],\n shell: true,\n homeEnvVar: \"DSH_HOME\",\n realHome: path.join(os.homedir(), \".dsh\"),\n inheritDirs: [\"skills\"],\n inheritFiles: [\"AGENTS.md\"],\n credentialFiles: [\".credentials.yaml\"],\n restore: \"resume\",\n firstFrameMs: 60_000,\n heartbeatMs: 60_000,\n images: false,\n model: '[\"deepseek-account\",\"deepseek-flash\"]',\n mcpViaAcp: true, // 不写 mcp.json;激活集转成 ACP mcpServers 传给 session/new|resume\n },\n\n // Qoder CLI(官方原生 ACP,qodercli 1.1.78 实测)。\n // - 直接起 qodercli.exe:`qoder` 是分发器(cmd → powershell → qodercli),多一层管道且会与 IDE 命令混淆;\n // - QODER_CONFIG_DIR 指向 <沙箱>/qoder:与 Qoder Desktop 共用的 ~/.qoder 绝不写入;\n // - 登录态要 .auth/user + .auth/machine_id 两个文件(只带 user 会 Authentication required);\n // - MCP 在 settings.json 的 mcpServers(与其它设置同文件),改后需重启进程 → 加载/卸载 MCP 的 restart 已覆盖;\n // - --yolo 下工具调用不发反向请求;capabilities 有 load/resume/image;\n // - providers(BYOK,含明文密钥)与 model 不继承,见 docs/接入-Qoder.md §5。\n qoder: {\n name: \"qoder\",\n bin: \"qodercli\",\n binWin: \"qodercli.exe\",\n // 官方安装器的固定位置;pm2/服务里 PATH 可能不含它,故直接兜底\n fallbackBinPaths:\n process.platform === \"win32\"\n ? [path.join(os.homedir(), \".qoder\", \"bin\", \"qodercli\", \"qodercli.exe\")]\n : [],\n args: [\"--acp\", \"--yolo\"],\n env: { QODER_CONFIG_DIR: `%HOME%${path.sep}qoder` },\n realHome: path.join(os.homedir(), \".qoder\"),\n // memory/ 是 Qoder agent 的用户画像/偏好记忆(实测存在),与日常使用共用一份(junction);\n // skills/agents/commands/AGENTS.md 在 Qoder 1.1.78 的本机不存在 → 当前为空操作,保留以便上游新增后自动生效\n inheritDirs: [\"memory\", \"skills\", \"agents\", \"commands\"],\n inheritFiles: [\"AGENTS.md\"],\n configSubdir: \"qoder\",\n mcpFormat: \"kiro\",\n mcpConfigFile: \"settings.json\",\n catalogFile: \"settings.json\",\n settingsBase: \"settings.json\",\n credentialFiles: [\".auth/user\"],\n identityFiles: [\".auth/machine_id\"],\n },\n\n // —— 需适配器(CLI 不原生支持 ACP,经第三方适配器桥接;未实测)——\n // Claude Code:Zed 官方适配器 @zed-industries/claude-code-acp(npx 运行;需 Claude Code 登录/ANTHROPIC_API_KEY)\n \"claude-code\": {\n name: \"claude-code\",\n bin: \"npx\",\n binWin: \"npx.cmd\",\n args: [\"-y\", \"@zed-industries/claude-code-acp\"],\n shell: true,\n },\n // Codex:社区适配器 codex-acp(agentclientprotocol/codex-acp,需自行安装其二进制;Codex 本身暂不原生支持 ACP)\n codex: { name: \"codex\", bin: \"codex-acp\", binWin: \"codex-acp.exe\", args: [] },\n};\n\nfunction onPath(exe: string): string | null {\n for (const dir of (process.env.PATH ?? \"\").split(path.delimiter)) {\n if (dir && fs.existsSync(path.join(dir, exe))) return path.join(dir, exe);\n }\n return null;\n}\n\n/**\n * `auto` 的探测顺序:按序取第一个「二进制能在 PATH 上解析到」的预设。\n * 只收纳**原生 ACP 且二进制可直接判定**的预设——适配器类(`claude-code` 走 `npx`、\n * `codex` 走 `codex-acp`)不参与自动探测(`npx` 几乎总在 PATH 上,会把它们误选出来)。\n */\nexport const AUTO_PROBE_ORDER = [\"kiro\", \"opencode\", \"gemini\", \"deepseek\", \"qoder\"] as const;\n\n/** 预设在该平台的「可执行文件名」(用于 PATH 探测)。 */\nfunction presetExe(preset: AcpAgentPreset): string {\n return process.platform === \"win32\" ? (preset.binWin ?? `${preset.bin}.exe`) : preset.bin;\n}\n\n/** 预设的二进制是否能在 PATH 上解析到(只判定、不启动)。 */\nexport function presetAvailable(key: string): boolean {\n const preset = ACP_AGENT_PRESETS[key];\n return !!preset && onPath(presetExe(preset)) !== null;\n}\n\n/**\n * 解析一个 bin 说明(绝对/相对路径,或 PATH 上的裸名)到实际文件;解析不到返回 null。\n * 裸名在 Windows 上会补试 `.exe`/`.cmd`,覆盖 npm shim 的常见布局。\n */\nfunction locateBin(bin: string): string | null {\n if (!bin) return null;\n if (bin.includes(\"/\") || bin.includes(path.sep)) return fs.existsSync(bin) ? bin : null;\n const candidates = [bin];\n if (process.platform === \"win32\" && !/\\.[A-Za-z0-9]+$/.test(bin))\n candidates.push(`${bin}.exe`, `${bin}.cmd`);\n for (const c of candidates) {\n const hit = onPath(c);\n if (hit) return hit;\n }\n return null;\n}\n\n/** daemon 因「大脑配置错误」退出时用的退出码;看门狗据此停止自动重启(配置错误需人工修,重拉无益)。 */\nexport const BRAIN_CONFIG_EXIT_CODE = 78;\n\nexport type ResolvedAgent = {\n name: string;\n bin: string;\n args: string[];\n homeEnvVar?: string;\n shell: boolean;\n inherit: boolean;\n realHome?: string;\n inheritDirs: string[];\n inheritFiles: string[];\n configSubdir: string;\n mcpFormat: \"kiro\" | \"opencode\";\n mcpConfigFile: string;\n catalogFile: string;\n env: Record<string, string>;\n restore?: \"load\" | \"resume\";\n firstFrameMs?: number;\n heartbeatMs?: number;\n images: boolean;\n credentialFiles: string[];\n identityFiles: string[];\n settingsBase?: string;\n model?: string;\n mcpViaAcp: boolean;\n /** 请求的原始值(来自 env / brain.json / 缺省 `auto`)。 */\n requestedKey: string;\n /** `requestedKey` 的来源。 */\n source: \"env\" | \"file\" | \"auto\";\n /** `requestedKey` 是否为已知预设(未知 → false,不再静默回退 kiro)。 */\n known: boolean;\n /** 二进制是否解析到了实际文件(找不到 → false)。 */\n available: boolean;\n /** 解析到的二进制绝对路径(`available` 时才有)。 */\n binPath: string | null;\n};\n\n/** 解析当前要用的 ACP agent:环境变量 > 账号 brain.json > auto。 */\nexport function resolveAgent(cfg?: BrainConfig | null): ResolvedAgent {\n const envKey = process.env.WEMIND_BRAIN?.trim();\n const fileKey = cfg?.agent?.trim();\n const requestedKey = (envKey || fileKey || \"auto\").toLowerCase();\n const source: ResolvedAgent[\"source\"] = envKey ? \"env\" : fileKey ? \"file\" : \"auto\";\n\n // 选预设:auto → 探测第一个可用;未知值 → 标 known:false(结构上退回 kiro 兜底,交启动校验报错)。\n let known = true;\n let key = requestedKey;\n if (key === \"auto\") {\n key = AUTO_PROBE_ORDER.find((k) => presetAvailable(k)) ?? \"kiro\";\n } else if (!ACP_AGENT_PRESETS[key]) {\n known = false;\n key = \"kiro\";\n }\n const preset = ACP_AGENT_PRESETS[key];\n\n // brain.json 的 bin/args/shell/homeEnvVar 是**给落盘那个 agent** 的启动覆盖;\n // 环境变量临时切到别的预设时不能沿用(否则会拿 deepseek 的 dsh.cmd 去起 qoder)。\n const overrideCfg =\n envKey && cfg?.agent?.trim() && cfg.agent.trim().toLowerCase() !== requestedKey ? null : cfg;\n\n const shellEnv = process.env.WEMIND_BRAIN_SHELL?.trim();\n const shell =\n shellEnv !== undefined && shellEnv !== \"\"\n ? shellEnv === \"1\" || shellEnv.toLowerCase() === \"true\"\n : (overrideCfg?.shell ?? preset.shell ?? false);\n\n // 二进制:env 覆盖 > (kiro 旧变量) > brain.json 覆盖 > PATH 解析 > 预设裸名。\n // 经 shell 启动时用裸名(让 shell 从 PATH 解析,避免带空格的全路径在 shell 下出问题)。\n const explicitBin =\n process.env.WEMIND_BRAIN_BIN?.trim() ||\n (preset.name === \"kiro\" ? process.env.KIRO_CLI_BIN?.trim() : undefined) ||\n overrideCfg?.bin?.trim() ||\n \"\";\n const exe = presetExe(preset);\n const binPath = explicitBin\n ? locateBin(explicitBin)\n : (onPath(exe) ?? (preset.fallbackBinPaths ?? []).find((p) => fs.existsSync(p)) ?? null);\n const bin =\n explicitBin ||\n (shell ? (process.platform === \"win32\" ? exe : preset.bin) : binPath || preset.bin);\n const available = known && binPath !== null;\n\n const argsOverride = process.env.WEMIND_BRAIN_ARGS?.trim();\n const args = argsOverride\n ? argsOverride.split(/\\s+/)\n : overrideCfg?.args && overrideCfg.args.length\n ? overrideCfg.args\n : preset.args;\n\n const homeEnvVar =\n process.env.WEMIND_BRAIN_HOME_ENV?.trim() ||\n overrideCfg?.homeEnvVar?.trim() ||\n preset.homeEnvVar;\n\n // 继承「记忆/规则/技能」:来源目录与子目录清单按 agent 而异(下沉到预设),env 可覆盖\n const inherit =\n (process.env.WEMIND_BRAIN_INHERIT ?? process.env.WECHAT_BRAIN_INHERIT ?? \"1\") !== \"0\";\n const realHome =\n process.env.WEMIND_BRAIN_REAL_HOME?.trim() ||\n process.env.WECHAT_REAL_KIRO_HOME?.trim() ||\n preset.realHome;\n const dirsEnv = process.env.WEMIND_BRAIN_INHERIT_DIRS?.trim();\n const inheritDirs = dirsEnv\n ? dirsEnv.split(/[,\\s]+/).filter(Boolean)\n : (preset.inheritDirs ?? []);\n\n return {\n name: preset.name,\n bin,\n args,\n homeEnvVar,\n shell,\n inherit,\n realHome,\n inheritDirs,\n inheritFiles: preset.inheritFiles ?? [],\n configSubdir: preset.configSubdir ?? \"\",\n mcpFormat: preset.mcpFormat ?? \"kiro\",\n mcpConfigFile: preset.mcpConfigFile ?? \"settings/mcp.json\",\n catalogFile: preset.catalogFile ?? \"settings/mcp.json\",\n env: preset.env ?? {},\n restore: preset.restore,\n firstFrameMs: preset.firstFrameMs,\n heartbeatMs: preset.heartbeatMs,\n images: preset.images ?? true,\n credentialFiles: preset.credentialFiles ?? [],\n identityFiles: preset.identityFiles ?? [],\n settingsBase: preset.settingsBase,\n model: preset.model,\n mcpViaAcp: preset.mcpViaAcp ?? false,\n requestedKey,\n source,\n known,\n available,\n binPath,\n };\n}\n\n/**\n * 校验解析后的大脑是否可用:可用返回 null,否则返回一句面向用户的说明。\n * 供 daemon 启动校验(早失败、说人话)与 `wemind brain` / `wemind brain check` 复用。\n */\nexport function agentProblem(ag: ResolvedAgent): string | null {\n if (!ag.known)\n return `未知大脑预设「${ag.requestedKey}」。可选:${Object.keys(ACP_AGENT_PRESETS).join(\"、\")}(自定义二进制可用 WEMIND_BRAIN_BIN 或 brain set --bin 指定)`;\n if (ag.requestedKey === \"auto\" && !ag.available)\n return `自动探测未找到可用的大脑(已试:${AUTO_PROBE_ORDER.join(\"、\")})。请安装其中之一,或运行 \\`wemind brain set <预设>\\``;\n if (!ag.available)\n return ag.name === \"deepseek\"\n ? `找不到 DeepSeek 的「${ag.bin}」。桌面端通常有 dsh.cmd;把它的绝对路径传给 \\`wemind brain set deepseek --bin \"<...>\\\\dsh.cmd\"\\``\n : ag.name === \"qoder\"\n ? `找不到 Qoder CLI 的「${ag.bin}」。官方安装会放在 ~/.qoder/bin/qodercli/ 并加入 PATH;确认后重开终端,或 \\`wemind brain set qoder --bin \"<...>\\\\qodercli.exe\"\\``\n : `找不到大脑「${ag.name}」的二进制「${ag.bin}」。安装它,或运行 \\`wemind brain set <预设>\\` 切换`;\n return null;\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { DATA_DIR } from \"@wemind/wechat-ilink-core\";\n\n/**\n * 账号级「大脑」持久配置(brain.json)。\n *\n * 背景:此前「用哪个大脑」只存在于进程环境变量 `WEMIND_BRAIN`(见 brain-agents.ts),\n * 是个**临时态**——换个入口重启(登录自启、pm2、看门狗、`cli.js start`、另一个 shell)就丢,\n * 静默退回默认预设;而默认预设(kiro)的二进制不一定装了 → 直到第一条消息才报错\n * (历史上表现为「⏳ 响应中断了,正在自动重试…」)。\n *\n * 这里把选择**按账号落盘**,成为单一事实来源:\n * - 任何入口重启都能复现(不依赖启动者的 shell);\n * - 切换是一等命令(`wemind brain set <预设>`),而不是「改环境变量 + 记得重开终端」;\n * - 环境变量 `WEMIND_BRAIN*` 仍为**一次性覆盖**(优先级更高),临时试别的 agent 不受影响。\n *\n * 文件形如 `~/.wemind/<账号>/brain.json`:\n * ```json\n * { \"agent\": \"opencode\" }\n * ```\n * 可选 `bin` / `args` / `shell` / `homeEnvVar`,用于覆盖预设的启动细节。\n */\n\nexport type BrainConfig = {\n /** 预设名(见 brain-agents.ts 的 ACP_AGENT_PRESETS),或 `auto`(自动探测)。 */\n agent: string;\n /** 覆盖二进制(绝对路径或 PATH 上的名字)。 */\n bin?: string;\n /** 覆盖 ACP 启动参数。 */\n args?: string[];\n /** 覆盖是否经 shell 启动。 */\n shell?: boolean;\n /** 覆盖「配置目录环境变量名」(指向沙箱)。 */\n homeEnvVar?: string;\n};\n\n/** brain.json 的完整路径(默认当前账号数据目录)。 */\nexport function brainConfigPath(dir: string = DATA_DIR): string {\n return path.join(dir, \"brain.json\");\n}\n\n/**\n * 读取账号的大脑配置;缺失/损坏/无有效 agent 一律返回 null(幂等、不抛)。\n * 字段做了宽松归一化:args 允许字符串(空格分隔)或数组;未知字段忽略。\n */\nexport function loadBrainConfig(dir: string = DATA_DIR): BrainConfig | null {\n let j: any;\n try {\n j = JSON.parse(fs.readFileSync(brainConfigPath(dir), \"utf-8\"));\n } catch {\n return null;\n }\n if (!j || typeof j !== \"object\" || Array.isArray(j)) return null;\n const agent = typeof j.agent === \"string\" ? j.agent.trim() : \"\";\n if (!agent) return null;\n const cfg: BrainConfig = { agent };\n if (typeof j.bin === \"string\" && j.bin.trim()) cfg.bin = j.bin.trim();\n if (Array.isArray(j.args))\n cfg.args = j.args.filter((x: unknown): x is string => typeof x === \"string\");\n else if (typeof j.args === \"string\" && j.args.trim()) cfg.args = j.args.trim().split(/\\s+/);\n if (typeof j.shell === \"boolean\") cfg.shell = j.shell;\n if (typeof j.homeEnvVar === \"string\" && j.homeEnvVar.trim()) cfg.homeEnvVar = j.homeEnvVar.trim();\n return cfg;\n}\n\n/** 原子写入账号的大脑配置(临时文件 + rename,避免读到半截)。 */\nexport function saveBrainConfig(cfg: BrainConfig, dir: string = DATA_DIR): void {\n fs.mkdirSync(dir, { recursive: true });\n const fp = brainConfigPath(dir);\n const tmp = `${fp}.tmp`;\n fs.writeFileSync(tmp, JSON.stringify(cfg, null, 2) + \"\\n\", \"utf-8\");\n fs.renameSync(tmp, fp);\n}\n\n/** 清除账号的大脑配置(回到自动探测/默认);返回是否确有文件被删。 */\nexport function clearBrainConfig(dir: string = DATA_DIR): boolean {\n try {\n fs.unlinkSync(brainConfigPath(dir));\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * MCP 配置的「格式」适配:不同 ACP agent 把 MCP server 定义放在不同文件、不同结构里。\n * 本模块只做纯数据的组织/解析(无文件 IO),便于单测;沙箱模块负责落盘。\n *\n * - `kiro`:独立文件 `settings/mcp.json`,结构 `{ \"mcpServers\": { 名: {command,args,env} } }`\n * - `opencode`:主配置 `opencode.json`,结构 `{ \"mcp\": { \"servers\": { 名: {type,command,environment} } } }`(与 model 等共处一文件)\n */\nexport type McpFormat = \"kiro\" | \"opencode\";\n\nfunction isObj(v: unknown): v is Record<string, unknown> {\n return !!v && typeof v === \"object\" && !Array.isArray(v);\n}\n\n/** 从配置对象读出 server 定义表(按格式);缺失/异常一律返回空表。 */\nexport function readMcpServers(obj: unknown, format: McpFormat): Record<string, unknown> {\n if (!isObj(obj)) return {};\n const servers =\n format === \"opencode\"\n ? isObj((obj as any).mcp)\n ? (obj as any).mcp.servers\n : undefined\n : (obj as any).mcpServers;\n return isObj(servers) ? servers : {};\n}\n\n/** 把 server 定义表写回配置对象(按格式),返回**新对象**(不改原对象),并保留其它键(如 opencode 的 model)。 */\nexport function withMcpServers(\n obj: unknown,\n servers: Record<string, unknown>,\n format: McpFormat,\n): Record<string, unknown> {\n const base: Record<string, unknown> = isObj(obj) ? { ...obj } : {};\n if (format === \"opencode\") {\n const mcp: Record<string, unknown> = isObj(base.mcp) ? { ...(base.mcp as object) } : {};\n mcp.servers = servers;\n base.mcp = mcp;\n } else {\n base.mcpServers = servers;\n }\n return base;\n}\n\n/**\n * 宽松解析 JSONC(去 // 与块注释、去尾随逗号)。仅用于把用户可能带注释的\n * opencode.jsonc 读成对象;失败返回 null(调用方按「无配置」处理,不抛)。\n */\nexport function parseJsonc(text: string): Record<string, unknown> | null {\n try {\n return JSON.parse(text) as Record<string, unknown>;\n } catch {\n /* 继续做宽松清洗 */\n }\n try {\n const cleaned = text\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\") // 块注释\n .replace(/(^|[^:])\\/\\/.*$/gm, \"$1\") // 行注释(避开 http:// 里的 //)\n .replace(/,(\\s*[}\\]])/g, \"$1\"); // 尾随逗号\n const parsed = JSON.parse(cleaned);\n return isObj(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\n// ---- ACP 传输:把 catalog 条目转成 session/new 的 mcpServers(mcpViaAcp 预设用) ----\n\n/** ACP `session/new` 的 stdio MCP 声明(env 是 `{name,value}[]`,与 kiro/opencode 的 map 不同)。 */\nexport type AcpMcpServer = {\n name: string;\n command: string;\n args: string[];\n env: Array<{ name: string; value: string }>;\n};\n\n/** ACP `session/new` 的 HTTP MCP 声明(官方 DSH 已广告 http:true;SSE 不支持)。 */\nexport type AcpMcpHttpServer = {\n name: string;\n type: \"http\";\n url: string;\n headers: Array<{ name: string; value: string }>;\n};\n\nfunction envEntries(map: unknown): Array<{ name: string; value: string }> {\n if (!isObj(map)) return [];\n return Object.entries(map)\n .filter(([, v]) => v !== undefined && v !== null)\n .map(([name, v]) => ({ name, value: String(v) }));\n}\n\n/** 该 catalog 条目能否转成 ACP 声明(addMcp 加载前的预校验也用它)。 */\nexport function isConvertibleToAcp(entry: unknown): boolean {\n if (!isObj(entry)) return false;\n if (typeof entry.command === \"string\" && entry.command) return true; // kiro local\n if (Array.isArray(entry.command) && typeof entry.command[0] === \"string\" && entry.command[0])\n return true; // opencode local\n if (typeof entry.url === \"string\" && entry.url) return true; // remote/http\n return false;\n}\n\n/**\n * 把 catalog 的 server 定义表转成 ACP `mcpServers`(stdio / HTTP)。\n * - kiro `{ command, args, env }` → stdio;\n * - opencode `{ type:\"local\", command:[...], environment }` → stdio(拆 command[0]/args);\n * - `{ url }`(kiro 远程 / opencode remote)→ HTTP(SSE 不被 ACP 支持,无法从定义识别,由服务端拒绝)。\n * 返回转换结果与**被跳过的名字**(结构不认识/明显残缺)——调用方记日志,避免坏条目把整个 session 拖挂。\n */\nexport function toAcpMcpServers(\n servers: Record<string, unknown>,\n opts: { exclude?: Iterable<string> } = {},\n): { servers: Array<AcpMcpServer | AcpMcpHttpServer>; skipped: string[] } {\n const exclude = new Set(opts.exclude ?? []);\n const out: Array<AcpMcpServer | AcpMcpHttpServer> = [];\n const skipped: string[] = [];\n for (const [name, entry] of Object.entries(servers)) {\n if (exclude.has(name)) continue;\n if (!isObj(entry)) {\n skipped.push(name);\n continue;\n }\n // opencode local:command 是数组\n if (Array.isArray(entry.command)) {\n const cmd = entry.command[0];\n if (typeof cmd !== \"string\" || !cmd) {\n skipped.push(name);\n continue;\n }\n out.push({\n name,\n command: cmd,\n args: entry.command.slice(1).map(String),\n env: envEntries(entry.environment ?? entry.env),\n });\n continue;\n }\n // kiro local:command 是字符串\n if (typeof entry.command === \"string\" && entry.command) {\n out.push({\n name,\n command: entry.command,\n args: Array.isArray(entry.args) ? entry.args.map(String) : [],\n env: envEntries(entry.env),\n });\n continue;\n }\n // 远程:url → HTTP(header 兼容 map 与 {name,value}[] 两种写法)\n if (typeof entry.url === \"string\" && entry.url) {\n const rawHeaders = entry.headers;\n const headers = isObj(rawHeaders)\n ? envEntries(rawHeaders)\n : Array.isArray(rawHeaders)\n ? rawHeaders\n .filter(\n (h): h is { name: string; value: string } =>\n isObj(h) && typeof (h as any).name === \"string\",\n )\n .map((h) => ({\n name: String((h as any).name),\n value: String((h as any).value ?? \"\"),\n }))\n : [];\n out.push({ name, type: \"http\", url: entry.url, headers });\n continue;\n }\n skipped.push(name);\n }\n return { servers: out, skipped };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\nimport { DATA_DIR } from \"@wemind/wechat-ilink-core\";\nimport { resolveAgent } from \"./brain-agents.js\";\nimport { loadBrainConfig } from \"./brain-config.js\";\nimport { readMcpServers, withMcpServers, parseJsonc, toAcpMcpServers } from \"./agent-mcp.js\";\n\n/**\n * brain 的 MCP 沙箱:brain 跑在独立配置目录下,默认不加载任何 MCP。\n * 通过对话指令(加载/卸载/列出)动态调整「激活集」,从全局 MCP 目录取定义写入沙箱。\n * DATA_DIR 从 core 导入(支持 WECHAT_DATA_DIR 覆盖,便于多账号隔离)。\n *\n * 按 agent 适配(见 brain-agents.ts 预设与 docs/接入-OpenCode.md):\n * - 配置目录:kiro = 沙箱根(KIRO_HOME);opencode = <沙箱>/opencode(XDG_CONFIG_HOME)。\n * - MCP 文件:kiro = settings/mcp.json({mcpServers});opencode = opencode.json(mcp.servers)。\n * kiro 行为与改造前完全一致(默认值即 kiro 约定)。\n */\n\nconst AG = resolveAgent(loadBrainConfig());\n\n/** brain 专用沙箱根目录(由 agent 的 homeEnvVar 指向它)。 */\nexport const BRAIN_HOME =\n process.env.WECHAT_BRAIN_HOME?.trim() || path.join(DATA_DIR, \"brain-home\");\n\n/** 该 agent 在沙箱里的「配置目录」(kiro=沙箱根;opencode=<沙箱>/opencode)。 */\nexport const BRAIN_CONFIG_DIR = path.join(BRAIN_HOME, AG.configSubdir);\n\n/** MCP 配置文件(相对配置目录,按 agent 的 mcpConfigFile)。 */\nconst BRAIN_MCP_JSON = path.join(BRAIN_CONFIG_DIR, AG.mcpConfigFile);\n\n/** 激活集持久化文件。 */\nconst ACTIVE_FILE = path.join(DATA_DIR, \"brain-mcps.json\");\n\n/** 「目录」:可选 MCP 的定义来源。默认取所用 agent 真实配置目录(realHome)下的\n * catalogFile(kiro: settings/mcp.json / opencode: opencode.json);可用\n * `WEMIND_MCP_CATALOG` / `WECHAT_MCP_CATALOG` 覆盖为任意路径。 */\nconst CATALOG =\n process.env.WEMIND_MCP_CATALOG?.trim() ||\n process.env.WECHAT_MCP_CATALOG?.trim() ||\n path.join(AG.realHome || path.join(os.homedir(), \".kiro\"), AG.catalogFile);\n\n/** 永不允许 brain 加载(防回环)。 */\nconst FORBIDDEN = new Set([\"wechat\"]);\n\n/**\n * 让 brain 继承所用 agent 真实配置目录的「知识/记忆/技能」:\n * - 目录式(inheritDirs,如 kiro 的 steering/memories/skills、opencode 的 skills/agents/commands)用\n * 目录 junction(Windows)/ symlink 指向真实目录;\n * - 单文件式(inheritFiles,如 opencode 的 AGENTS.md)直接拷入配置目录。\n * 使微信里的大脑和你日常使用读到同样的记忆、规则、技能。\n * **MCP(settings/opencode.json 的 mcp 段)与 brain 自有会话(sessions)不镜像**:MCP 必须走沙箱(排除 wechat 防回环),会话不能混。\n */\nfunction mirrorRealKiro(): void {\n if (!AG.inherit || !AG.realHome) return;\n if (path.resolve(BRAIN_HOME) === path.resolve(AG.realHome)) return; // 安全:别把自己链给自己\n try {\n fs.mkdirSync(BRAIN_CONFIG_DIR, { recursive: true });\n } catch {\n /* ignore */\n }\n for (const d of AG.inheritDirs) {\n const target = path.join(AG.realHome, d);\n const link = path.join(BRAIN_CONFIG_DIR, d);\n if (!fs.existsSync(target)) continue;\n let present = false;\n try {\n fs.lstatSync(link);\n present = true;\n } catch {\n /* 不存在 */\n }\n if (present) {\n // 已是链接 → 不动;是早期自建的「空」真实目录 → 删掉改建 junction;非空真实目录 → 保留不动(避免误删数据)\n try {\n const st = fs.lstatSync(link);\n if (st.isSymbolicLink()) continue; // 已是链接 → 不动\n if (st.isDirectory()) {\n if (fs.readdirSync(link).length === 0) {\n fs.rmdirSync(link); // 空目录 → 删掉,下面改建 junction\n } else {\n // 非空真实目录(如 kiro 自建 agents 脚手架):把真实目录里「缺失」的文件补拷进来,补全继承(不删已有)\n for (const f of fs.readdirSync(target)) {\n const dst = path.join(link, f);\n const src = path.join(target, f);\n try {\n if (!fs.existsSync(dst) && fs.statSync(src).isFile()) fs.copyFileSync(src, dst);\n } catch {\n /* ignore */\n }\n }\n continue;\n }\n } else continue;\n } catch {\n continue;\n }\n }\n try {\n fs.symlinkSync(target, link, process.platform === \"win32\" ? \"junction\" : \"dir\");\n } catch {\n /* 权限不足等:best-effort 忽略 */\n }\n }\n // 单文件式记忆(如 opencode 的 AGENTS.md):真实目录有、沙箱没有才拷(不覆盖沙箱已有)\n for (const f of AG.inheritFiles) {\n const src = path.join(AG.realHome, f);\n const dst = path.join(BRAIN_CONFIG_DIR, f);\n try {\n if (fs.existsSync(src) && !fs.existsSync(dst)) fs.copyFileSync(src, dst);\n } catch {\n /* ignore */\n }\n }\n}\n\nfunction readJson(file: string): Record<string, unknown> | null {\n try {\n return JSON.parse(fs.readFileSync(file, \"utf-8\")) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/**\n * 把 agent 的凭据文件同步进沙箱(如 DSH 的 `.credentials.yaml`、Qoder 的 `.auth/user`)。\n * - `newer`:首次复制;真实 home 的 token 刷新后覆盖沙箱;沙箱自己刷新过则保留沙箱版本。\n * - `always`:内容不同即覆盖(标识类文件,如 Qoder 的 `.auth/machine_id`——agent 在缺凭据时会自造一个,\n * 它比真实文件新,「新者胜」会把错的留下,导致永远认证失败)。\n * 这是敏感文件,仅放在账号沙箱内;Windows 无 POSIX 600,建议专用账号 + 目录 ACL。\n */\nfunction syncCredentialFile(file: string, mode: \"newer\" | \"always\"): void {\n if (!AG.realHome) return;\n const src = path.join(AG.realHome, file);\n const dst = path.join(BRAIN_CONFIG_DIR, file);\n try {\n if (!fs.existsSync(src)) return;\n fs.mkdirSync(path.dirname(dst), { recursive: true });\n if (mode === \"always\") {\n const same = fs.existsSync(dst) && fs.readFileSync(src).equals(fs.readFileSync(dst));\n if (!same) fs.copyFileSync(src, dst);\n return;\n }\n const s = fs.statSync(src);\n const d = fs.existsSync(dst) ? fs.statSync(dst) : null;\n if (!d || s.mtimeMs > d.mtimeMs + 1000) fs.copyFileSync(src, dst);\n } catch {\n /* best-effort */\n }\n}\n\nfunction syncCredentials(): void {\n for (const f of AG.credentialFiles) syncCredentialFile(f, \"newer\");\n for (const f of AG.identityFiles) syncCredentialFile(f, \"always\");\n}\n\nfunction loadCatalog(): Record<string, unknown> {\n return readMcpServers(readJson(CATALOG), AG.mcpFormat);\n}\n\n/** 目录中可选的 server 名(排除禁用项)。 */\nexport function catalogNames(): string[] {\n return Object.keys(loadCatalog()).filter((n) => !FORBIDDEN.has(n));\n}\n\nexport function loadActive(): string[] {\n const j = readJson(ACTIVE_FILE);\n const a = j?.active;\n return Array.isArray(a) ? (a as string[]).filter((x) => typeof x === \"string\") : [];\n}\n\nfunction saveActive(list: string[]): void {\n fs.mkdirSync(DATA_DIR, { recursive: true });\n fs.writeFileSync(ACTIVE_FILE, JSON.stringify({ active: list }, null, 2), \"utf-8\");\n}\n\n/** 按激活集从目录取定义,写入沙箱 MCP 配置(默认空)。保留配置里其它键(如 opencode 的 model)。\n * `mcpViaAcp` 预设(DSH)不写文件——激活集经 activeMcpServers() 转成 ACP mcpServers 随 session 下发。 */\nexport function applySandbox(): void {\n if (AG.mcpViaAcp) return;\n const cat = loadCatalog();\n const active = loadActive().filter((n) => cat[n] && !FORBIDDEN.has(n));\n const servers: Record<string, unknown> = {};\n for (const n of active) servers[n] = cat[n];\n const existing = readJson(BRAIN_MCP_JSON) ?? {};\n const next = withMcpServers(existing, servers, AG.mcpFormat);\n fs.mkdirSync(path.dirname(BRAIN_MCP_JSON), { recursive: true });\n fs.writeFileSync(BRAIN_MCP_JSON, JSON.stringify(next, null, 2), \"utf-8\");\n}\n\n/** brain 当前应挂载的 ACP mcpServers(mcpViaAcp 预设用):激活集 → catalog 定义 → ACP 声明。\n * 跳过不可转换的坏条目并记日志——官方 DSH 的 session/new 遇到起不来的 MCP 会回滚整个会话。 */\nexport function activeMcpServers() {\n const cat = loadCatalog();\n const active = loadActive().filter((n) => cat[n] && !FORBIDDEN.has(n));\n const picked: Record<string, unknown> = {};\n for (const n of active) picked[n] = cat[n];\n const { servers, skipped } = toAcpMcpServers(picked, { exclude: FORBIDDEN });\n if (skipped.length)\n console.error(`[wemind/wechat-daemon/mcp] 跳过无法转换的 MCP:${skipped.join(\", \")}`);\n return servers;\n}\n\n/** kiro:继承真实 settings/ 下除 mcp.json 外的文件(cli.json 含模型/免确认等),让大脑与日常使用同款配置。 */\nfunction syncKiroSettings(): void {\n const realSettings = path.join(AG.realHome!, \"settings\");\n const brainSettings = path.join(BRAIN_CONFIG_DIR, \"settings\");\n if (!fs.existsSync(realSettings)) return;\n try {\n fs.mkdirSync(brainSettings, { recursive: true });\n } catch {\n /* ignore */\n }\n let names: string[] = [];\n try {\n names = fs.readdirSync(realSettings);\n } catch {\n return;\n }\n for (const f of names) {\n if (f === \"mcp.json\") continue; // MCP 必须用沙箱 curated 版本(防 wechat 回环)\n try {\n const src = path.join(realSettings, f);\n if (fs.statSync(src).isFile()) fs.copyFileSync(src, path.join(brainSettings, f));\n } catch {\n /* best-effort */\n }\n }\n}\n\n/** opencode:把用户全局 opencode.json(c)(去掉 mcp 段)作为基底写进沙箱,继承 model 等设置;mcp 仍由 applySandbox 权威覆盖。 */\nfunction syncOpencodeBase(): void {\n const src = [\"opencode.json\", \"opencode.jsonc\"]\n .map((f) => path.join(AG.realHome!, f))\n .find((f) => fs.existsSync(f));\n if (!src) return;\n let parsed: Record<string, unknown> | null = null;\n try {\n const raw = fs.readFileSync(src, \"utf-8\");\n parsed = src.endsWith(\".jsonc\")\n ? parseJsonc(raw)\n : (JSON.parse(raw) as Record<string, unknown>);\n } catch {\n return;\n }\n if (!parsed || typeof parsed !== \"object\") return;\n delete (parsed as Record<string, unknown>).mcp; // MCP 必须走沙箱 curated(防 wechat 回环)\n fs.mkdirSync(BRAIN_CONFIG_DIR, { recursive: true });\n fs.writeFileSync(BRAIN_MCP_JSON, JSON.stringify(parsed, null, 2), \"utf-8\");\n}\n\n/** Qoder 这类「设置与 MCP 同文件」的 agent:以真实 settings.json 为基底生成沙箱设置。\n * 剥离必须由沙箱权威决定的键:\n * - `mcpServers`:MCP 只能来自沙箱 curated(防 wechat 回环);\n * - `enabledPlugins`:插件可自带 MCP,且沙箱不继承 `plugins/`;\n * - `providers` / `model`:自定义供应商(BYOK)含明文密钥,`model` 又可能指向已剥离的供应商。\n * 并强制 `general.enableAutoUpdate=false`:否则每个沙箱都会各下一份约 210 MB 的 CLI。 */\nfunction settingsBase(): Record<string, unknown> {\n let parsed: unknown = null;\n try {\n parsed = JSON.parse(fs.readFileSync(path.join(AG.realHome!, AG.settingsBase!), \"utf-8\"));\n } catch {\n /* 缺失/损坏 → 空基底 */\n }\n const base =\n parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? { ...(parsed as Record<string, unknown>) }\n : {};\n delete base.mcpServers;\n delete base.enabledPlugins;\n delete base.providers;\n delete base.model;\n const general = base.general;\n base.general = {\n ...(general && typeof general === \"object\" && !Array.isArray(general)\n ? (general as Record<string, unknown>)\n : {}),\n enableAutoUpdate: false,\n };\n return base;\n}\n\nfunction syncSettingsBase(): void {\n fs.mkdirSync(BRAIN_CONFIG_DIR, { recursive: true });\n fs.writeFileSync(BRAIN_MCP_JSON, JSON.stringify(settingsBase(), null, 2), \"utf-8\");\n}\n\n/** 继承真实配置目录的「非 MCP」设置(模型/免确认等),让大脑与日常使用同款配置;mcp 段保持沙箱权威。\n * `mcpViaAcp` 预设没有文件式 MCP/settings 可继承,跳过(模型走 preset.model + set_config_option)。 */\nfunction syncSettings(): void {\n if (AG.mcpViaAcp) return;\n if (AG.settingsBase && AG.realHome) {\n syncSettingsBase(); // 不依赖 inherit:沙箱硬化(关自动更新)与隔离始终生效\n return;\n }\n if (!AG.inherit || !AG.realHome) return;\n if (AG.mcpFormat === \"opencode\") syncOpencodeBase();\n else syncKiroSettings();\n}\n\n/** 启动时确保沙箱存在并与激活集一致。 */\nexport function ensureBrainHome(): void {\n syncSettings(); // 先同步「非 MCP」设置(模型/免确认等),让大脑与日常使用同款配置\n applySandbox(); // 再写 curated mcp(权威,保持 MCP 隔离防回环)\n mirrorRealKiro(); // 继承真实配置目录的记忆/steering/skills(sessions 仍隔离)\n syncCredentials(); // 登录态/凭据(如 DSH 的 .credentials.yaml、Qoder 的 .auth/*)\n}\n\nexport function addMcp(name: string): string {\n if (FORBIDDEN.has(name)) return `不允许加载「${name}」(防回环)。`;\n const cat = loadCatalog();\n if (!cat[name]) return `未找到 MCP「${name}」。可选:${catalogNames().join(\", \") || \"(无)\"}`;\n // 预校验:坏条目不进激活集(官方 DSH 的 session/new 会因起不来的 MCP 回滚整会话)\n if (AG.mcpViaAcp && !toAcpMcpServers({ [name]: cat[name] }).servers.length)\n return `MCP「${name}」的定义无法转换(需要 command 或 url),已拒绝加载。`;\n const a = loadActive();\n if (!a.includes(name)) {\n a.push(name);\n saveActive(a);\n }\n applySandbox();\n return `已加载 MCP「${name}」。当前激活:${a.join(\", \")}(下一条消息生效)`;\n}\n\nexport function removeMcp(name: string): string {\n const a = loadActive();\n if (!a.includes(name)) return `「${name}」当前未激活。`;\n const next = a.filter((x) => x !== name);\n saveActive(next);\n applySandbox();\n return `已卸载 MCP「${name}」。当前激活:${next.join(\", \") || \"(空)\"}`;\n}\n\nexport function listMcp(): string {\n const avail = catalogNames();\n const active = loadActive();\n return `可选 MCP:${avail.join(\", \") || \"(无)\"}\\n已激活:${active.join(\", \") || \"(空,默认不加载)\"}`;\n}\n","import {\n spawn,\n type ChildProcessWithoutNullStreams,\n type SpawnOptionsWithoutStdio,\n} from \"node:child_process\";\n\n/*\n * 启动 ACP agent 进程的**统一入口**。\n *\n * 为什么不是 `spawn(bin, args[], { shell: true })`:\n * Node 20+ 对这种写法报 DEP0190——`shell:true` 时 `args` 只是用空格拼进命令串、不做任何转义\n * (既是注入面,也会在 daemon.log 里每次冷启动刷两行弃用警告)。\n * 正确做法是**自己把整条命令拼成一个字符串**再交给 shell,参数由我们逐条加引号。\n * 仓库里的 pm2-runner.ts 出于同样原因用了同样的写法。\n *\n * 为什么仍然要经 shell:\n * Windows 上 npm shim(opencode / npx / gemini)都是 `.cmd`,而 Node 20+ **禁止**无 shell\n * 直接 spawn `.cmd`/`.bat`(EINVAL,CVE-2024-27980 之后的安全限制),所以只能让 cmd.exe\n * 去解析 PATH。POSIX 上同一份代码走 `/bin/sh -c`,正好覆盖两侧。\n *\n * 注:既然经 shell,cmd 的元字符语义就生效(参数必须逐条加引号;`%VAR%` 仍会被展开),\n * 这与改动前的行为一致,不引入新语义。\n */\n\n/** 启动所需的最小 agent 描述(`ResolvedAgent` 的子集,避免反向依赖 brain-agents)。 */\nexport type SpawnableAgent = {\n bin: string;\n args: readonly string[];\n shell: boolean;\n};\n\n/** cmd 需要加引号的字符:空白 + 元字符(不加引号时 cmd 会自行解释:`&` 断命令、`|` 管道…)。 */\nconst CMD_NEEDS_QUOTE = /[\\s\"&|<>^()%!]/;\n\n/**\n * cmd.exe(MS CRT)参数转义:无空白/元字符则裸写;否则用双引号包裹,\n * 并按其规则处理引号前的前导反斜杠与结尾反斜杠。\n * 注:引号挡得住元字符,但挡不住 `%VAR%` 展开(cmd 命令行无法转义 `%`),与改动前行为一致。\n */\nexport function quoteCmdArg(arg: string): string {\n if (arg === \"\") return '\"\"';\n if (!CMD_NEEDS_QUOTE.test(arg)) return arg;\n return `\"${arg.replace(/(\\\\*)\"/g, '$1$1\\\\\"').replace(/(\\\\+)$/, \"$1$1\")}\"`;\n}\n\n/** POSIX sh 参数转义:安全字符集裸写,否则单引号包裹(内部单引号写成 `'\\''`)。 */\nexport function quoteShArg(arg: string): string {\n if (arg === \"\") return \"''\";\n if (!/[^\\w@%+=:,./-]/.test(arg)) return arg;\n return `'${arg.replace(/'/g, \"'\\\\''\")}'`;\n}\n\n/** 把 `bin + args` 拼成一条交给 shell 的命令行(`platform` 仅为可测性暴露)。 */\nexport function buildShellLine(\n bin: string,\n args: readonly string[],\n platform: NodeJS.Platform = process.platform,\n): string {\n const quote = platform === \"win32\" ? quoteCmdArg : quoteShArg;\n return [bin, ...args].map(quote).join(\" \");\n}\n\n/**\n * 启动 agent:`shell=true` 的走「整串命令行 + shell」(见文件头注释),其余直接 spawn。\n * 默认 `windowsHide: true`(不闪控制台窗口),其余选项透传。\n */\nexport function spawnAgent(\n agent: SpawnableAgent,\n opts: SpawnOptionsWithoutStdio = {},\n): ChildProcessWithoutNullStreams {\n const base: SpawnOptionsWithoutStdio = { windowsHide: true, ...opts };\n if (!agent.shell) return spawn(agent.bin, agent.args, base);\n return spawn(buildShellLine(agent.bin, agent.args), { ...base, shell: true });\n}\n","/**\n * brainstorm/worker 共用的 session 映射键规则:`<agent>:<key>`。\n * 换大脑后各 agent 的会话互不覆盖,切回原大脑仍能 session/load 回原上下文。\n */\nexport function sessionKey(agent: string, key: string): string {\n return `${agent}:${key}`;\n}\n\n/**\n * 把磁盘上的映射归一成 `<agent>:<key>` 形式。\n * - 已带前缀的条目原样保留(其它 agent 的会话不动);\n * - 旧格式(裸 key)由「当时那个 agent」写入、已无从得知是哪个 → 归给当前 agent;\n * 若同名的新格式条目已存在,则保留新格式(它的归属确定),只丢掉裸条目。\n * migrated=true 时调用方应立即回写,避免下次换 agent 时旧条目再被继承一次。\n */\nexport function normalizeSessionMap(\n raw: unknown,\n agent: string,\n): { map: Record<string, string>; migrated: boolean } {\n const map: Record<string, string> = {};\n const legacy: Array<[string, string]> = [];\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return { map, migrated: false };\n for (const [k, v] of Object.entries(raw as Record<string, unknown>)) {\n if (typeof v !== \"string\") continue;\n if (k.includes(\":\")) map[k] = v;\n else legacy.push([k, v]);\n }\n for (const [k, v] of legacy) {\n const nk = sessionKey(agent, k);\n if (!map[nk]) map[nk] = v;\n }\n return { map, migrated: legacy.length > 0 };\n}\n","import { spawnSync, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\n\nimport { DATA_DIR } from \"@wemind/wechat-ilink-core\";\nimport { BRAIN_HOME, activeMcpServers } from \"./mcp-sandbox.js\";\nimport { resolveAgent } from \"./brain-agents.js\";\nimport { loadBrainConfig } from \"./brain-config.js\";\nimport { spawnAgent } from \"./spawn-agent.js\";\nimport { normalizeSessionMap, sessionKey } from \"./session-map.js\";\n\n/**\n * ACP 大脑:维护一个常驻的 ACP agent 进程(JSON-RPC over stdio,换行分隔)。\n * 通过标准 ACP 协议驱动,agent 可配置(默认 kiro-cli,见 brain-agents.ts),任意兼容 ACP 的 CLI 均可接入。\n * - 每用户一个 ACP session(原生多轮记忆);session 映射持久化到磁盘。\n * - 自愈:session/prompt 超时/失败 → 杀进程重建;重建后用 session/load 恢复上下文。\n * - 诊断日志([wemind/wechat-daemon/acp] 前缀,进 daemon.log):发出请求、收帧、超时时已收 chunk 数与最后收帧时间差。\n * 跑在 brain 沙箱目录下(默认空 MCP),通过 agent 配置的环境变量把该目录传给 agent。\n */\n\nfunction dlog(...a: unknown[]): void {\n console.error(\"[wemind/wechat-daemon/acp]\", ...a);\n}\n\nconst AGENT = resolveAgent(loadBrainConfig()); // 当前 ACP agent(env > 账号 brain.json > auto)\nconst REQ_TIMEOUT = 30_000; // initialize / session/new / session/load\nconst PROMPT_IDLE_MS = Number(process.env.WECHAT_BRAIN_IDLE_MS) || 120_000; // 多久没收到任何帧才算\"卡死\"。重任务单次慢抓取可静默较久(直连 kiro-cli 无此限制),放宽到 2min 兜底真死\nconst PROMPT_CAP_MS = Number(process.env.WECHAT_BRAIN_CAP_MS) || 900_000; // 单回合硬上限 15min。实测重任务(联网核实)需 ~6.5min;context_token 无时间 TTL(官方),长回合用本消息 token 仍可送达。卡死由 idle/首帧超时兜底\nconst FIRST_FRAME_MS =\n Number(process.env.WECHAT_BRAIN_FIRST_FRAME_MS) || AGENT.firstFrameMs || 25_000; // 预设可覆盖;DSH 实测首帧 1–4s,兜底 60s\nconst SESSION_MAP_FILE = path.join(DATA_DIR, \"brain-sessions.json\");\nconst BRAIN_PID_FILE = path.join(DATA_DIR, \"brain.pid\"); // 标记当前账号的大脑 kiro-cli pid,便于与用户自己的会话区分/清理\n\ntype Pending = {\n resolve: (v: unknown) => void;\n reject: (e: Error) => void;\n clear: () => void;\n method: string;\n};\n\nlet child: ChildProcessWithoutNullStreams | null = null;\nlet initialized = false;\nlet idCounter = 0;\nconst pending = new Map<number, Pending>();\nconst userSessions = new Map<string, string>(); // 本进程已建立的 session\nconst assembling = new Map<string, string[]>(); // sessionId -> 当前回合文本块\nlet stdoutBuf = \"\";\nlet lastFrameAt = 0; // 最近一次收到任何 stdout 帧的时间\nlet frameSeq = 0; // 收到帧的累计序号(用于判定本回合是否出过帧)\ntype ProgressEvent =\n | { type: \"tool\"; kind?: string; purpose?: string; title?: string }\n | { type: \"answer\" }\n | { type: \"thinking\" }\n | { type: \"activity\" };\nlet currentOnProgress: ((ev: ProgressEvent) => void) | null = null; // 当前回合进度回调\nlet answerStarted = false; // 当前回合是否已通知\"开始写回复\"\nlet thoughtStarted = false; // 当前回合是否已通知\"正在思考\"\nconst seenUpdateTypes = new Set<string>(); // 诊断:本回合出现过的 sessionUpdate 类型\nconst seenTools = new Set<string>(); // 当前回合已上报的工具标题,去重\nconst hintedSessions = new Set<string>(); // 已注入「可发图」约定提示的 session(每会话首轮注入一次)\n\nfunction isAlive(): boolean {\n return !!child && child.exitCode === null && !child.killed;\n}\n\n/** 杀进程(按 PID 进程树)并清空内部态;不清磁盘 session 映射(留给重建后 load)。 */\nfunction reset(): void {\n const c = child;\n child = null;\n initialized = false;\n if (c?.pid) {\n try {\n if (process.platform === \"win32\")\n spawnSync(\"taskkill\", [\"/PID\", String(c.pid), \"/T\", \"/F\"], {\n stdio: \"ignore\",\n windowsHide: true, // 避免瞬时闪出控制台窗口\n });\n else c.kill();\n } catch {\n /* ignore */\n }\n }\n try {\n fs.unlinkSync(BRAIN_PID_FILE);\n } catch {\n /* ignore */\n }\n for (const p of pending.values()) {\n p.clear();\n p.reject(new Error(\"acp process reset\"));\n }\n pending.clear();\n userSessions.clear();\n assembling.clear();\n stdoutBuf = \"\";\n}\n\n// ---- 磁盘 session 映射(`<agent>:<userId>` -> sessionId),跨进程重建保上下文 ----\nconst keyOf = (userId: string): string => sessionKey(AGENT.name, userId);\n\nfunction loadSessionMap(): Record<string, string> {\n let raw: unknown;\n try {\n raw = JSON.parse(fs.readFileSync(SESSION_MAP_FILE, \"utf-8\"));\n } catch {\n return {};\n }\n const { map, migrated } = normalizeSessionMap(raw, AGENT.name);\n if (migrated) saveSessionMap(map); // 立即落盘,避免下次换 agent 时旧条目再被“继承”一次\n return map;\n}\nfunction saveSessionMap(m: Record<string, string>): void {\n fs.mkdirSync(DATA_DIR, { recursive: true });\n fs.writeFileSync(SESSION_MAP_FILE, JSON.stringify(m, null, 2), \"utf-8\");\n}\n\nfunction handleReverseRequest(f: Record<string, any>): void {\n const method = String(f.method);\n dlog(`← reverse request ${method}#${f.id}`);\n if (method === \"session/request_permission\") {\n const options = Array.isArray(f.params?.options) ? f.params.options : [];\n // 默认拒绝(安全);可信部署可显式开启一次性放行\n const allow =\n process.env.WEMIND_ACP_AUTO_ALLOW === \"1\"\n ? options.find((o: any) => o?.kind === \"allow_once\" || o?.kind === \"allow_always\")\n : undefined;\n const result = allow\n ? { outcome: { outcome: \"selected\", optionId: allow.optionId } }\n : { outcome: { outcome: \"cancelled\" } };\n child?.stdin.write(JSON.stringify({ jsonrpc: \"2.0\", id: f.id, result }) + \"\\n\");\n dlog(`permission ${allow ? \"allowed\" : \"rejected\"}`);\n return;\n }\n // 未知反向请求也要回错误,否则对端会挂起\n child?.stdin.write(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: f.id,\n error: { code: -32601, message: \"method not supported\" },\n }) + \"\\n\",\n );\n}\n\nfunction handleFrame(f: Record<string, any>): void {\n lastFrameAt = Date.now();\n frameSeq += 1;\n if (typeof f.id === \"number\" && (f.result !== undefined || f.error !== undefined)) {\n const p = pending.get(f.id);\n if (!p) return;\n p.clear();\n pending.delete(f.id);\n if (f.error) {\n dlog(`← ${p.method}#${f.id} ERROR ${JSON.stringify(f.error).slice(0, 200)}`);\n p.reject(new Error(JSON.stringify(f.error)));\n } else p.resolve(f.result);\n return;\n }\n // agent -> client 的反向请求(带 method + id);必须应答,否则会挂到超时\n if (f.method && f.id !== undefined) {\n handleReverseRequest(f);\n return;\n }\n if (f.method === \"session/update\") {\n const u = f.params?.update;\n if (u?.sessionUpdate) seenUpdateTypes.add(String(u.sessionUpdate)); // 诊断\n // 任意 session/update 帧都视为\"活动\" → 让 daemon 静默刷新心跳基线(流式期间不误报\"仍在处理\")\n if (currentOnProgress) {\n try {\n currentOnProgress({ type: \"activity\" });\n } catch {\n /* ignore */\n }\n }\n if (u?.sessionUpdate === \"agent_message_chunk\" && u.content?.type === \"text\") {\n const arr = assembling.get(f.params.sessionId);\n if (arr) arr.push(String(u.content.text ?? \"\"));\n if (!answerStarted && currentOnProgress) {\n answerStarted = true;\n try {\n currentOnProgress({ type: \"answer\" });\n } catch {\n /* ignore */\n }\n }\n } else if (u?.sessionUpdate === \"agent_thought_chunk\") {\n if (!thoughtStarted && currentOnProgress) {\n thoughtStarted = true;\n try {\n currentOnProgress({ type: \"thinking\" });\n } catch {\n /* ignore */\n }\n }\n } else if (\n (u?.sessionUpdate === \"tool_call\" || u?.sessionUpdate === \"tool_call_update\") &&\n currentOnProgress\n ) {\n const tid = String(u.toolCallId ?? u.title ?? \"\"); // 每个工具调用只上报一次\n if (tid && !seenTools.has(tid)) {\n seenTools.add(tid);\n const purpose =\n typeof u.rawInput?.__tool_use_purpose === \"string\"\n ? u.rawInput.__tool_use_purpose\n : undefined;\n try {\n currentOnProgress({ type: \"tool\", kind: u.kind, purpose, title: u.title });\n } catch {\n /* ignore */\n }\n }\n }\n }\n}\n\nfunction onStdout(d: Buffer): void {\n stdoutBuf += d.toString();\n let i: number;\n while ((i = stdoutBuf.indexOf(\"\\n\")) >= 0) {\n const line = stdoutBuf.slice(0, i).trim();\n stdoutBuf = stdoutBuf.slice(i + 1);\n if (!line) continue;\n let f: Record<string, any>;\n try {\n f = JSON.parse(line);\n } catch {\n continue;\n }\n handleFrame(f);\n }\n}\n\nfunction request(method: string, params: unknown, timeoutMs: number): Promise<any> {\n return new Promise((resolve, reject) => {\n if (!child) {\n reject(new Error(\"no acp process\"));\n return;\n }\n const id = ++idCounter;\n dlog(`→ ${method}#${id}`);\n const t = setTimeout(() => {\n pending.delete(id);\n reject(new Error(`${method} timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n pending.set(id, { resolve, reject, clear: () => clearTimeout(t), method });\n child.stdin.write(JSON.stringify({ jsonrpc: \"2.0\", id, method, params }) + \"\\n\");\n });\n}\n\n/**\n * session/prompt 专用:空闲超时(PROMPT_IDLE_MS 内无任何帧才判卡死)+ 硬上限(PROMPT_CAP_MS)。\n * 只要 brain 还在出帧(工具进度/正文),就不打断——避免误杀\"慢但在干活\"的长查询。\n */\nfunction promptRequest(params: unknown): Promise<any> {\n return new Promise((resolve, reject) => {\n if (!child) {\n reject(new Error(\"no acp process\"));\n return;\n }\n const id = ++idCounter;\n const startedAt = Date.now();\n lastFrameAt = Date.now(); // 重置空闲基线:从本次 prompt 发出算起,避免用上一回合的旧时间戳误判\n const startSeq = frameSeq; // 记录起点,用于判定本回合是否出过帧\n dlog(\n `→ session/prompt#${id} (idle=${PROMPT_IDLE_MS}ms cap=${PROMPT_CAP_MS}ms firstFrame=${FIRST_FRAME_MS}ms)`,\n );\n const iv = setInterval(() => {\n const idle = Date.now() - lastFrameAt;\n const total = Date.now() - startedAt;\n const noFrameYet = frameSeq === startSeq; // 本回合从未出过帧\n const firstFrameTimeout = noFrameYet && total > FIRST_FRAME_MS;\n if (firstFrameTimeout || idle > PROMPT_IDLE_MS || total > PROMPT_CAP_MS) {\n if (pending.delete(id)) {\n clearInterval(iv);\n const why = firstFrameTimeout ? \"no-first-frame\" : idle > PROMPT_IDLE_MS ? \"idle\" : \"cap\";\n reject(new Error(`session/prompt ${why} timeout (idle=${idle}ms total=${total}ms)`));\n }\n }\n }, 5000);\n pending.set(id, { resolve, reject, clear: () => clearInterval(iv), method: \"session/prompt\" });\n child.stdin.write(\n JSON.stringify({ jsonrpc: \"2.0\", id, method: \"session/prompt\", params }) + \"\\n\",\n );\n });\n}\n\nasync function ensureProcess(): Promise<void> {\n if (isAlive() && initialized) return;\n reset();\n const env: NodeJS.ProcessEnv = { ...process.env };\n if (AGENT.homeEnvVar) env[AGENT.homeEnvVar] = BRAIN_HOME; // 把 brain 沙箱目录传给 agent\n // 预设声明的额外环境变量(%HOME% → 沙箱根),如 opencode 的 OPENCODE_DISABLE_PROJECT_CONFIG\n for (const [k, v] of Object.entries(AGENT.env ?? {})) env[k] = v.replaceAll(\"%HOME%\", BRAIN_HOME);\n const proc = spawnAgent(AGENT, { cwd: BRAIN_HOME, env });\n child = proc;\n dlog(`spawn ${AGENT.name} acp pid=${proc.pid} bin=${AGENT.bin} args=[${AGENT.args.join(\" \")}]`);\n try {\n fs.writeFileSync(BRAIN_PID_FILE, String(proc.pid), \"utf-8\");\n } catch {\n /* best-effort */\n }\n proc.stdout.on(\"data\", onStdout);\n proc.stderr.on(\"data\", () => {\n /* ignore */\n });\n // 只在\"退出的就是当前 child\"时才重置,避免旧进程迟到的 exit 误杀新进程\n proc.on(\"exit\", (code) => {\n dlog(`acp exited code=${code}`);\n if (child === proc) reset();\n });\n proc.on(\"error\", (e) => {\n dlog(`acp error ${String(e)}`);\n if (child === proc) reset();\n });\n await request(\"initialize\", { protocolVersion: 1, clientCapabilities: {} }, REQ_TIMEOUT);\n initialized = true;\n dlog(\"initialized\");\n}\n\n/** 按预设选择模型路由(如 DSH 的 deepseek-account);没有 model 的 agent 跳过。 */\nasync function applyModel(sid: string): Promise<void> {\n if (!AGENT.model) return;\n try {\n await request(\n \"session/set_config_option\",\n { sessionId: sid, configId: \"model\", value: AGENT.model },\n REQ_TIMEOUT,\n );\n dlog(`model set ${AGENT.model}`);\n } catch (e) {\n dlog(`model set failed (${String(e)})`);\n throw e;\n }\n}\n\nasync function ensureSession(userId: string): Promise<string> {\n const existing = userSessions.get(userId);\n if (existing) return existing;\n // mcpViaAcp 预设(DSH):激活集随 session 下发;其余 agent 传空(走各自沙箱配置文件)\n const mcpServers = AGENT.mcpViaAcp ? activeMcpServers() : [];\n const map = loadSessionMap();\n const saved = map[keyOf(userId)];\n if (saved) {\n // 预设指定 resume(DSH);其余仍先 load,失败再试 resume,最后 new\n const methods: Array<\"load\" | \"resume\"> =\n AGENT.restore === \"resume\" ? [\"resume\"] : [\"load\", \"resume\"];\n for (const method of methods) {\n try {\n await request(\n `session/${method}`,\n { sessionId: saved, cwd: BRAIN_HOME, mcpServers },\n REQ_TIMEOUT,\n );\n await applyModel(saved);\n userSessions.set(userId, saved);\n dlog(`session/${method} ok ${saved} mcp=[${mcpServers.map((s: any) => s.name).join(\",\")}]`);\n return saved;\n } catch (e) {\n dlog(`session/${method} failed (${String(e)})`);\n }\n }\n }\n const res = (await request(\"session/new\", { cwd: BRAIN_HOME, mcpServers }, REQ_TIMEOUT)) as {\n sessionId?: string;\n };\n if (!res?.sessionId) throw new Error(\"session/new returned no sessionId\");\n await applyModel(res.sessionId);\n userSessions.set(userId, res.sessionId);\n map[keyOf(userId)] = res.sessionId;\n saveSessionMap(map);\n dlog(`session/new ok ${res.sessionId} mcp=[${mcpServers.map((s: any) => s.name).join(\",\")}]`);\n return res.sessionId;\n}\n\n/** 向大脑(配置的 ACP agent)提问(常驻 + 原生多轮)。失败即重建进程(下条用 session/load 恢复上下文)。 */\nexport async function askBrain(\n userId: string,\n message: string,\n onProgress?: (ev: ProgressEvent) => void,\n images?: Array<{ data: string; mimeType: string }>,\n): Promise<string> {\n await ensureProcess();\n const sid = await ensureSession(userId);\n assembling.set(sid, []);\n currentOnProgress = onProgress ?? null;\n answerStarted = false;\n thoughtStarted = false;\n seenUpdateTypes.clear();\n seenTools.clear();\n const t0 = Date.now();\n const prompt: unknown[] = [];\n if (images?.length && !AGENT.images) {\n prompt.push({\n type: \"text\",\n text: \"(用户发来图片,但当前大脑不支持图片输入;请让用户改用文字,或切换到支持图片的大脑。不要臆测图片内容。)\",\n });\n }\n if (AGENT.images) {\n for (const im of images ?? [])\n prompt.push({ type: \"image\", mimeType: im.mimeType, data: im.data });\n }\n if (!hintedSessions.has(sid)) {\n hintedSessions.add(sid);\n prompt.push({\n type: \"text\",\n text: \"(系统提示:你正在通过微信和用户对话。需要给用户发图片时,在回复里用 Markdown 图片语法 ,系统会自动把它作为图片消息发出(你本身不会文生图,可发联网搜到的图、本地已有的图、或自己用代码渲染保存的图表)。重要:服务器从中国大陆下载该图,请选「可直接下载的直链」——优先 raw.githubusercontent.com 等原始文件直链;避免 wikipedia/wikimedia(大陆被墙)以及 pixabay/unsplash 等带热链保护、直链常返回 403 的图床。拿不准时优先把图先下载到本地再用本地绝对路径引用,或直接发链接让用户自己点。其余正常用文字/Markdown 回复。此提示只出现一次,无需理会本句。)\",\n });\n }\n prompt.push({ type: \"text\", text: message || \"(用户发来一张图片,请查看并回应)\" });\n try {\n await promptRequest({ sessionId: sid, prompt });\n const txt = (assembling.get(sid) ?? []).join(\"\").trim();\n dlog(\n `prompt ok ${Date.now() - t0}ms chunks=${(assembling.get(sid) ?? []).length} updateTypes=[${[...seenUpdateTypes].join(\",\")}]`,\n );\n return txt;\n } catch (e) {\n const chunks = (assembling.get(sid) ?? []).length;\n const sinceFrame = lastFrameAt ? Date.now() - lastFrameAt : -1;\n dlog(\n `prompt FAIL ${Date.now() - t0}ms chunks=${chunks} lastFrameAgo=${sinceFrame}ms err=${String(e)} -> rebuild`,\n );\n reset(); // 自愈:杀掉可能假死的进程,下条消息重建并 session/load 恢复\n throw e;\n } finally {\n currentOnProgress = null;\n assembling.delete(sid);\n }\n}\n\n/** 用户主动开新会话:丢弃旧 session(含磁盘映射),重建一个全新 session。 */\nexport async function newSession(userId: string): Promise<void> {\n const map = loadSessionMap();\n delete map[keyOf(userId)]; // 只清当前 agent 的;换大脑前那个 agent 的上下文仍保留\n saveSessionMap(map);\n userSessions.delete(userId);\n await ensureProcess();\n await ensureSession(userId);\n}\n\n/** MCP 激活集变更后调用:重启 acp 进程(重建后会 session/load 恢复上下文)。 */\nexport function restart(): void {\n reset();\n}\n\n/** 当前大脑是否接受图片输入(预设能力;默认 true)。 */\nexport function supportsImages(): boolean {\n return AGENT.images;\n}\n\n/** 当前预设的静默期心跳覆盖(默认 undefined → 15s)。 */\nexport function heartbeatMs(): number | undefined {\n return AGENT.heartbeatMs;\n}\n","import fs from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * 从 brain 的文本回复里提取「要作为图片发送」的引用,并返回去掉这些引用后的纯文本。\n * 覆盖三种来源:\n * A. Markdown 图片 ``,src 可为 http(s) URL 或本地绝对路径;\n * B. brain 按约定输出的也是上述 Markdown 语法(见 acp-brain 注入的系统提示);\n * C. 回复中裸露的图片 URL(http(s) 且以图片扩展名结尾)。\n */\n\nconst IMG_EXT = /\\.(png|jpe?g|gif|webp|bmp)(\\?[^\\s)]*)?$/i;\nconst MAX_BYTES = 10 * 1024 * 1024;\n\nexport interface ExtractedImage {\n src: string;\n}\n\nexport function extractImages(text: string): { images: ExtractedImage[]; text: string } {\n const images: ExtractedImage[] = [];\n const seen = new Set<string>();\n const push = (src: string) => {\n const s = src.trim();\n if (s && !seen.has(s)) {\n seen.add(s);\n images.push({ src: s });\n }\n };\n\n let out = text ?? \"\";\n // A/B:Markdown 图片 \n out = out.replace(/!\\[[^\\]]*\\]\\(\\s*<?([^)\\s>]+)>?(?:\\s+\"[^\"]*\")?\\s*\\)/g, (_m, src: string) => {\n push(src);\n return \"\";\n });\n // C:裸图片 URL(排除 markdown 链接/图片里的,即前面不是 `(`)\n out = out.replace(\n /(?<!\\()(https?:\\/\\/[^\\s)]+\\.(?:png|jpe?g|gif|webp|bmp)(?:\\?[^\\s)]*)?)/gi,\n (m: string) => {\n push(m);\n return \"\";\n },\n );\n\n out = out\n .replace(/[ \\t]+\\n/g, \"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n return { images, text: out };\n}\n\n/** 把图片引用解析成字节:http(s) 下载,或本地文件读取。带大小/类型校验。 */\nexport async function resolveImageBytes(src: string): Promise<Buffer> {\n if (/^https?:\\/\\//i.test(src)) {\n const res = await fetch(src, {\n signal: AbortSignal.timeout(15000),\n // 不少图床(pixabay 等)对无 UA 的直链返回 403,带上浏览器 UA 规避热链拦截\n headers: {\n \"User-Agent\":\n \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36\",\n },\n });\n if (!res.ok) throw new Error(`下载图片失败 ${res.status}`);\n const buf = Buffer.from(await res.arrayBuffer());\n if (buf.length > MAX_BYTES) throw new Error(`图片过大 ${buf.length}`);\n const ct = res.headers.get(\"content-type\") || \"\";\n if (!ct.startsWith(\"image/\") && !IMG_EXT.test(src)) throw new Error(`非图片内容(${ct})`);\n return buf;\n }\n const p = src.startsWith(\"file://\") ? fileURLToPath(src) : src;\n if (!IMG_EXT.test(p)) throw new Error(`非图片文件(仅按图片扩展名发送): ${p}`);\n const stat = fs.statSync(p);\n if (!stat.isFile()) throw new Error(\"不是文件\");\n if (stat.size > MAX_BYTES) throw new Error(`图片过大 ${stat.size}`);\n return fs.readFileSync(p);\n}\n","/**\n * 长回复转 Markdown 文件:超过阈值的回复正文,作为 .md 文件发送(一条消息),\n * 比拆成多条文本更不易触发 iLink 连续发送上限、且阅读体验完整。\n * 文件名由内容生成一个合适的标题。\n */\n\n/** 超过此字符数的回复转 .md 文件发送。可用 WECHAT_REPLY_FILE_THRESHOLD 覆盖。 */\nexport const LONG_REPLY_THRESHOLD = Number(process.env.WECHAT_REPLY_FILE_THRESHOLD) || 1500;\n\nconst ILLEGAL_FILENAME = /[\\\\/:*?\"<>|\\r\\n\\t]/g;\n\n/** 由内容生成一个合适的标题:取首个 Markdown 标题或首个非空行,清洗为合法文件名片段。 */\nexport function generateTitle(text: string): string {\n let first = \"\";\n for (const ln of text.split(/\\r?\\n/)) {\n const t = ln.trim();\n if (t) {\n first = t;\n break;\n }\n }\n // 去掉行首的 Markdown 标记(标题井号、列表符、有序号)\n first = first\n .replace(/^#{1,6}\\s*/, \"\")\n .replace(/^[-*+]\\s+/, \"\")\n .replace(/^\\d+[.)]\\s+/, \"\")\n .replace(/[*_`~]/g, \"\");\n first = first.replace(ILLEGAL_FILENAME, \" \").replace(/\\s+/g, \" \").trim();\n if (first.length > 40) first = first.slice(0, 40).trim();\n return first || \"回复\";\n}\n\n/**\n * 构造长回复的 md 文件(不落盘,直接内存 buffer)。\n * 若正文未以 Markdown 标题开头,则前置一个由内容生成的 H1,便于打开后阅读。\n */\nexport function buildMdReply(text: string): { fileName: string; data: Buffer } {\n const title = generateTitle(text);\n const body = /^\\s*#/.test(text) ? text : `# ${title}\\n\\n${text}`;\n return { fileName: `${title}.md`, data: Buffer.from(body, \"utf-8\") };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\nimport { DATA_DIR } from \"@wemind/wechat-ilink-core\";\nimport { resolveAgent } from \"./brain-agents.js\";\nimport { loadBrainConfig } from \"./brain-config.js\";\nimport { readMcpServers, withMcpServers, parseJsonc, toAcpMcpServers } from \"./agent-mcp.js\";\n\n/**\n * worker 沙箱:与 brain 沙箱(mcp-sandbox.ts)并列,但 MCP 策略相反。\n *\n * worker 是无人值守的任务执行机,默认**继承真实配置目录的全部 MCP**(仅排除 wechat 防回环),\n * 而非 brain 的「默认空 + 对话中按需加载」——因为没人在旁边逐个加载工具,任务需要开箱即用的完整能力。\n * 记忆/技能/规则(mirrorRealKiro)与 settings(syncSettings,含模型/信任)的继承与 brain 一致。\n *\n * 按 agent 适配(同 mcp-sandbox.ts):配置目录、MCP 格式、设置继承方式均随 agent 而变。\n *\n * 设计依据:docs/方案C-MVP-远程human-in-the-loop.md §8.4。\n * 与 mcp-sandbox.ts 有少量重复(mirrorRealKiro/syncSettings),刻意复制以隔离演进、不动已稳定的 brain 沙箱。\n */\n\nconst AG = resolveAgent(loadBrainConfig());\n\n/** worker 专用沙箱根(由 agent 的 homeEnvVar 指向它)。 */\nexport const WORKER_HOME =\n process.env.WEMIND_WORKER_HOME?.trim() || path.join(DATA_DIR, \"worker-home\");\n\n/** 该 agent 在 worker 沙箱里的「配置目录」(kiro=沙箱根;opencode=<沙箱>/opencode)。 */\nexport const WORKER_CONFIG_DIR = path.join(WORKER_HOME, AG.configSubdir);\n\nconst WORKER_MCP_JSON = path.join(WORKER_CONFIG_DIR, AG.mcpConfigFile);\n\n/** MCP「目录」:可继承的 MCP 定义来源(同 brain,默认取 agent 真实配置目录的 catalogFile)。 */\nconst CATALOG =\n process.env.WEMIND_MCP_CATALOG?.trim() ||\n process.env.WECHAT_MCP_CATALOG?.trim() ||\n path.join(AG.realHome || path.join(os.homedir(), \".kiro\"), AG.catalogFile);\n\n/** worker 不继承的 MCP:wechat 始终强制排除(防回环),外加 WEMIND_WORKER_MCP_EXCLUDE 指定的。 */\nfunction excludeSet(): Set<string> {\n const raw = process.env.WEMIND_WORKER_MCP_EXCLUDE ?? \"wechat\";\n const s = new Set(\n raw\n .split(/[,\\s]+/)\n .map((x) => x.trim())\n .filter(Boolean),\n );\n s.add(\"wechat\"); // 强制:worker 加载 wechat 会用同一 bot token 长轮询、与 daemon 抢消息(坑①回环)\n return s;\n}\n\nfunction readJson(file: string): Record<string, unknown> | null {\n try {\n return JSON.parse(fs.readFileSync(file, \"utf-8\")) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/** 同 brain:把凭据文件(如 DSH 的 `.credentials.yaml`、Qoder 的 `.auth/user`)同步进 worker 沙箱。\n * `newer`=新者胜;`always`=内容不同即覆盖(标识类文件必须与真实目录一致,见 mcp-sandbox.ts)。 */\nfunction syncCredentialFile(file: string, mode: \"newer\" | \"always\"): void {\n if (!AG.realHome) return;\n const src = path.join(AG.realHome, file);\n const dst = path.join(WORKER_CONFIG_DIR, file);\n try {\n if (!fs.existsSync(src)) return;\n fs.mkdirSync(path.dirname(dst), { recursive: true });\n if (mode === \"always\") {\n const same = fs.existsSync(dst) && fs.readFileSync(src).equals(fs.readFileSync(dst));\n if (!same) fs.copyFileSync(src, dst);\n return;\n }\n const s = fs.statSync(src);\n const d = fs.existsSync(dst) ? fs.statSync(dst) : null;\n if (!d || s.mtimeMs > d.mtimeMs + 1000) fs.copyFileSync(src, dst);\n } catch {\n /* best-effort */\n }\n}\n\nfunction syncCredentials(): void {\n for (const f of AG.credentialFiles) syncCredentialFile(f, \"newer\");\n for (const f of AG.identityFiles) syncCredentialFile(f, \"always\");\n}\n\nfunction loadCatalog(): Record<string, unknown> {\n return readMcpServers(readJson(CATALOG), AG.mcpFormat);\n}\n\n/** worker 继承全量 MCP:复制 catalog 全部 server,去掉排除集与 disabled:true 的。\n * `mcpViaAcp` 预设(DSH)不写文件——全量集合经 workerMcpServers() 转成 ACP mcpServers 随 session 下发。 */\nexport function applyWorkerSandbox(): void {\n if (AG.mcpViaAcp) return;\n const cat = loadCatalog();\n const ex = excludeSet();\n const servers: Record<string, unknown> = {};\n for (const [name, def] of Object.entries(cat)) {\n if (ex.has(name)) continue;\n if (def && typeof def === \"object\" && (def as { disabled?: unknown }).disabled === true)\n continue; // 跳过用户已禁用的\n servers[name] = def;\n }\n const existing = readJson(WORKER_MCP_JSON) ?? {};\n const next = withMcpServers(existing, servers, AG.mcpFormat);\n fs.mkdirSync(path.dirname(WORKER_MCP_JSON), { recursive: true });\n fs.writeFileSync(WORKER_MCP_JSON, JSON.stringify(next, null, 2), \"utf-8\");\n}\n\n/** worker 当前应挂载的 ACP mcpServers(mcpViaAcp 预设用):全量 catalog(除排除集/disabled/坏条目)→ ACP 声明。 */\nexport function workerMcpServers() {\n const cat = loadCatalog();\n const ex = excludeSet();\n const picked: Record<string, unknown> = {};\n for (const [name, def] of Object.entries(cat)) {\n if (ex.has(name)) continue;\n if (def && typeof def === \"object\" && (def as { disabled?: unknown }).disabled === true)\n continue;\n picked[name] = def;\n }\n const { servers, skipped } = toAcpMcpServers(picked, { exclude: ex });\n if (skipped.length)\n console.error(`[wemind/wechat-daemon/mcp] worker 跳过无法转换的 MCP:${skipped.join(\", \")}`);\n return servers;\n}\n\n/** kiro:继承真实 settings/ 下除 mcp.json 外的文件(cli.json 含模型/免确认等)。逻辑同 brain 的 syncKiroSettings。 */\nfunction syncKiroSettings(): void {\n const realSettings = path.join(AG.realHome!, \"settings\");\n const workerSettings = path.join(WORKER_CONFIG_DIR, \"settings\");\n if (!fs.existsSync(realSettings)) return;\n try {\n fs.mkdirSync(workerSettings, { recursive: true });\n } catch {\n /* ignore */\n }\n let names: string[] = [];\n try {\n names = fs.readdirSync(realSettings);\n } catch {\n return;\n }\n for (const f of names) {\n if (f === \"mcp.json\") continue; // MCP 用 worker curated 版本(applyWorkerSandbox 全量除排除集)\n try {\n const src = path.join(realSettings, f);\n if (fs.statSync(src).isFile()) fs.copyFileSync(src, path.join(workerSettings, f));\n } catch {\n /* best-effort */\n }\n }\n}\n\n/** opencode:把用户全局 opencode.json(c)(去 mcp 段)作基底写入 worker 沙箱,继承 model 等;mcp 由 applyWorkerSandbox 覆盖。 */\nfunction syncOpencodeBase(): void {\n const src = [\"opencode.json\", \"opencode.jsonc\"]\n .map((f) => path.join(AG.realHome!, f))\n .find((f) => fs.existsSync(f));\n if (!src) return;\n let parsed: Record<string, unknown> | null = null;\n try {\n const raw = fs.readFileSync(src, \"utf-8\");\n parsed = src.endsWith(\".jsonc\")\n ? parseJsonc(raw)\n : (JSON.parse(raw) as Record<string, unknown>);\n } catch {\n return;\n }\n if (!parsed || typeof parsed !== \"object\") return;\n delete (parsed as Record<string, unknown>).mcp;\n fs.mkdirSync(WORKER_CONFIG_DIR, { recursive: true });\n fs.writeFileSync(WORKER_MCP_JSON, JSON.stringify(parsed, null, 2), \"utf-8\");\n}\n\n/** Qoder 这类「设置与 MCP 同文件」的 agent:与 brain 相同的基底疗法(剥离 MCP/插件/自定义供应商 + 关自动更新)。 */\nfunction settingsBase(): Record<string, unknown> {\n let parsed: unknown = null;\n try {\n parsed = JSON.parse(fs.readFileSync(path.join(AG.realHome!, AG.settingsBase!), \"utf-8\"));\n } catch {\n /* 缺失/损坏 → 空基底 */\n }\n const base =\n parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? { ...(parsed as Record<string, unknown>) }\n : {};\n delete base.mcpServers;\n delete base.enabledPlugins;\n delete base.providers;\n delete base.model;\n const general = base.general;\n base.general = {\n ...(general && typeof general === \"object\" && !Array.isArray(general)\n ? (general as Record<string, unknown>)\n : {}),\n enableAutoUpdate: false,\n };\n return base;\n}\n\nfunction syncSettingsBase(): void {\n fs.mkdirSync(WORKER_CONFIG_DIR, { recursive: true });\n fs.writeFileSync(WORKER_MCP_JSON, JSON.stringify(settingsBase(), null, 2), \"utf-8\");\n}\n\nfunction syncSettings(): void {\n if (AG.mcpViaAcp) return; // 无文件式 settings/MCP 可继承;模型走 preset.model\n if (AG.settingsBase && AG.realHome) {\n syncSettingsBase(); // 不依赖 inherit:沙箱硬化(关自动更新)与隔离始终生效\n return;\n }\n if (!AG.inherit || !AG.realHome) return;\n if (AG.mcpFormat === \"opencode\") syncOpencodeBase();\n else syncKiroSettings();\n}\n\n/** 用 junction/symlink 把真实配置目录的记忆/技能/规则类子目录链入 worker 配置目录;单文件记忆直接拷。逻辑同 brain 的 mirrorRealKiro。 */\nfunction mirrorRealKiro(): void {\n if (!AG.inherit || !AG.realHome) return;\n if (path.resolve(WORKER_HOME) === path.resolve(AG.realHome)) return; // 安全:别把自己链给自己\n try {\n fs.mkdirSync(WORKER_CONFIG_DIR, { recursive: true });\n } catch {\n /* ignore */\n }\n for (const d of AG.inheritDirs) {\n const target = path.join(AG.realHome, d);\n const link = path.join(WORKER_CONFIG_DIR, d);\n if (!fs.existsSync(target)) continue;\n let present = false;\n try {\n fs.lstatSync(link);\n present = true;\n } catch {\n /* 不存在 */\n }\n if (present) {\n try {\n const st = fs.lstatSync(link);\n if (st.isSymbolicLink()) continue; // 已是链接 → 不动\n if (st.isDirectory()) {\n if (fs.readdirSync(link).length === 0) {\n fs.rmdirSync(link); // 空目录 → 删掉,下面改建 junction\n } else {\n // 非空真实目录:补拷真实目录里缺失的文件(不删已有)\n for (const f of fs.readdirSync(target)) {\n const dst = path.join(link, f);\n const src = path.join(target, f);\n try {\n if (!fs.existsSync(dst) && fs.statSync(src).isFile()) fs.copyFileSync(src, dst);\n } catch {\n /* ignore */\n }\n }\n continue;\n }\n } else continue;\n } catch {\n continue;\n }\n }\n try {\n fs.symlinkSync(target, link, process.platform === \"win32\" ? \"junction\" : \"dir\");\n } catch {\n /* 权限不足等:best-effort 忽略 */\n }\n }\n for (const f of AG.inheritFiles) {\n const src = path.join(AG.realHome, f);\n const dst = path.join(WORKER_CONFIG_DIR, f);\n try {\n if (fs.existsSync(src) && !fs.existsSync(dst)) fs.copyFileSync(src, dst);\n } catch {\n /* ignore */\n }\n }\n}\n\n/** 启动时确保 worker 沙箱就绪:继承 settings(除 mcp)→ 写全量 MCP(除排除集)→ 链入记忆/技能。 */\nexport function ensureWorkerHome(): void {\n syncSettings(); // 先同步「非 MCP」设置(模型/免确认等)\n applyWorkerSandbox(); // 再写全量 MCP(除 wechat 等排除集)\n mirrorRealKiro(); // 链入记忆/steering/skills(sessions 仍隔离)\n syncCredentials(); // 凭据(如 DSH 的 .credentials.yaml、Qoder 的 .auth/*)\n}\n","import { spawnSync, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs\";\n\nimport { DATA_DIR } from \"@wemind/wechat-ilink-core\";\nimport { WORKER_HOME, workerMcpServers } from \"./worker-sandbox.js\";\nimport { resolveAgent } from \"./brain-agents.js\";\nimport { loadBrainConfig } from \"./brain-config.js\";\nimport { spawnAgent } from \"./spawn-agent.js\";\nimport { normalizeSessionMap, sessionKey } from \"./session-map.js\";\n\n/**\n * worker ACP 客户端:维护一个常驻的 ACP agent 进程,专跑「漫长任务」(与 acp-brain 的对话大脑并存、互不干扰)。\n * 约 70% 复制自 acp-brain.ts,差异:\n * - **独立单例**(独立 child/pending/session),与 brain 进程并存;\n * - **单任务 session**(MVP 单任务,固定 key=\"task\");持久化到 worker-sessions.json,重建后 session/load 续上下文;\n * - **cwd = 任务工作目录**(WEMIND_WORKER_CWD,默认用户主目录),而 KIRO_HOME 指向 WORKER_HOME 沙箱(继承全量 MCP 除 wechat + 记忆);\n * - **更宽松超时**(漫长任务,env 可调);\n * - 去掉 brain 的「可发图」首轮提示(worker 是任务执行,非聊天发图场景)。\n * 自愈:session/prompt 超时/失败 → 杀进程;下次 askWorker 重建并 session/load 恢复(→ 用户回「继续」即可续跑)。\n */\n\nfunction dlog(...a: unknown[]): void {\n console.error(\"[wemind/wechat-daemon/worker]\", ...a);\n}\n\nconst AGENT = resolveAgent(loadBrainConfig()); // 复用同一 ACP agent 解析(env > 账号 brain.json > auto)\nconst WORKER_CWD = process.env.WEMIND_WORKER_CWD?.trim() || os.homedir(); // 任务工作目录(按 kiro 约定,MVP 默认主目录)\nconst REQ_TIMEOUT = 30_000; // initialize / session/new / session/load\nconst PROMPT_IDLE_MS = Number(process.env.WEMIND_WORKER_IDLE_MS) || 180_000; // 无任何帧多久算\"卡死\"(worker 比 brain 宽松:3min)\nconst PROMPT_CAP_MS = Number(process.env.WEMIND_WORKER_CAP_MS) || 1_800_000; // 单轮硬上限 30min(漫长任务)\nconst FIRST_FRAME_MS =\n Number(process.env.WEMIND_WORKER_FIRST_FRAME_MS) || AGENT.firstFrameMs || 30_000; // 预设可覆盖\nconst SESSION_MAP_FILE = path.join(DATA_DIR, \"worker-sessions.json\");\nconst WORKER_PID_FILE = path.join(DATA_DIR, \"worker.pid\"); // 标记 worker kiro-cli pid,便于与 brain/用户会话区分、精确清理\nconst TASK_KEY = \"task\"; // MVP 单任务的固定 session key\n\n// 本任务的工作目录:本地派发可指定(终端当前目录);微信派发为 undefined → 用 WORKER_CWD 默认。\n// spawn 时生效(子进程 cwd 不可中途改),stopWorker 时清。\nlet pendingCwd: string | undefined;\n/** 设置「下次(本任务首轮)spawn worker 用的 cwd」。本地命令派发传入终端当前目录;不传/空则用默认 WORKER_CWD。 */\nexport function setWorkerCwd(cwd?: string): void {\n pendingCwd = cwd?.trim() || undefined;\n}\n/** 本任务有效 cwd(指定优先,否则默认)。 */\nfunction effectiveCwd(): string {\n return pendingCwd ?? WORKER_CWD;\n}\n\ntype Pending = {\n resolve: (v: unknown) => void;\n reject: (e: Error) => void;\n clear: () => void;\n method: string;\n};\n\nlet child: ChildProcessWithoutNullStreams | null = null;\nlet initialized = false;\nlet idCounter = 0;\nconst pending = new Map<number, Pending>();\nconst taskSessions = new Map<string, string>(); // key -> sessionId(MVP 仅 TASK_KEY)\nconst assembling = new Map<string, string[]>(); // sessionId -> 当前轮文本块\nlet stdoutBuf = \"\";\nlet lastFrameAt = 0;\nlet frameSeq = 0;\nexport type WorkerProgressEvent =\n | { type: \"tool\"; kind?: string; purpose?: string; title?: string }\n | { type: \"answer\" }\n | { type: \"thinking\" }\n | { type: \"activity\" };\nlet currentOnProgress: ((ev: WorkerProgressEvent) => void) | null = null;\nlet answerStarted = false;\nlet thoughtStarted = false;\nconst seenUpdateTypes = new Set<string>();\nconst seenTools = new Set<string>();\n\nexport function isWorkerAlive(): boolean {\n return !!child && child.exitCode === null && !child.killed;\n}\n\n/** 杀进程(按 PID 进程树)并清空内部态;不清磁盘 session 映射(留给重建后 load)。 */\nfunction reset(): void {\n const c = child;\n child = null;\n initialized = false;\n if (c?.pid) {\n try {\n if (process.platform === \"win32\")\n spawnSync(\"taskkill\", [\"/PID\", String(c.pid), \"/T\", \"/F\"], {\n stdio: \"ignore\",\n windowsHide: true, // 避免瞬时闪出控制台窗口\n });\n else c.kill();\n } catch {\n /* ignore */\n }\n }\n try {\n fs.unlinkSync(WORKER_PID_FILE);\n } catch {\n /* ignore */\n }\n for (const p of pending.values()) {\n p.clear();\n p.reject(new Error(\"worker process reset\"));\n }\n pending.clear();\n taskSessions.clear();\n assembling.clear();\n stdoutBuf = \"\";\n}\n\n// ---- 磁盘 session 映射(`<agent>:<taskKey>` -> sessionId),与 brain 同样按 agent 分命名空间 ----\nconst keyOf = (key: string): string => sessionKey(AGENT.name, key);\n\nfunction loadSessionMap(): Record<string, string> {\n let raw: unknown;\n try {\n raw = JSON.parse(fs.readFileSync(SESSION_MAP_FILE, \"utf-8\"));\n } catch {\n return {};\n }\n const { map, migrated } = normalizeSessionMap(raw, AGENT.name);\n if (migrated) saveSessionMap(map); // 立即落盘,避免下次换 agent 时旧条目再被“继承”一次\n return map;\n}\nfunction saveSessionMap(m: Record<string, string>): void {\n fs.mkdirSync(DATA_DIR, { recursive: true });\n fs.writeFileSync(SESSION_MAP_FILE, JSON.stringify(m, null, 2), \"utf-8\");\n}\n\nfunction handleReverseRequest(f: Record<string, any>): void {\n const method = String(f.method);\n dlog(`← reverse request ${method}#${f.id}`);\n if (method === \"session/request_permission\") {\n const options = Array.isArray(f.params?.options) ? f.params.options : [];\n const allow =\n process.env.WEMIND_ACP_AUTO_ALLOW === \"1\"\n ? options.find((o: any) => o?.kind === \"allow_once\" || o?.kind === \"allow_always\")\n : undefined;\n const result = allow\n ? { outcome: { outcome: \"selected\", optionId: allow.optionId } }\n : { outcome: { outcome: \"cancelled\" } };\n child?.stdin.write(JSON.stringify({ jsonrpc: \"2.0\", id: f.id, result }) + \"\\n\");\n dlog(`permission ${allow ? \"allowed\" : \"rejected\"}`);\n return;\n }\n child?.stdin.write(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: f.id,\n error: { code: -32601, message: \"method not supported\" },\n }) + \"\\n\",\n );\n}\n\nfunction handleFrame(f: Record<string, any>): void {\n lastFrameAt = Date.now();\n frameSeq += 1;\n if (typeof f.id === \"number\" && (f.result !== undefined || f.error !== undefined)) {\n const p = pending.get(f.id);\n if (!p) return;\n p.clear();\n pending.delete(f.id);\n if (f.error) {\n dlog(`← ${p.method}#${f.id} ERROR ${JSON.stringify(f.error).slice(0, 200)}`);\n p.reject(new Error(JSON.stringify(f.error)));\n } else p.resolve(f.result);\n return;\n }\n if (f.method && f.id !== undefined) {\n handleReverseRequest(f);\n return;\n }\n if (f.method === \"session/update\") {\n const u = f.params?.update;\n if (u?.sessionUpdate) seenUpdateTypes.add(String(u.sessionUpdate));\n if (currentOnProgress) {\n try {\n currentOnProgress({ type: \"activity\" });\n } catch {\n /* ignore */\n }\n }\n if (u?.sessionUpdate === \"agent_message_chunk\" && u.content?.type === \"text\") {\n const arr = assembling.get(f.params.sessionId);\n if (arr) arr.push(String(u.content.text ?? \"\"));\n if (!answerStarted && currentOnProgress) {\n answerStarted = true;\n try {\n currentOnProgress({ type: \"answer\" });\n } catch {\n /* ignore */\n }\n }\n } else if (u?.sessionUpdate === \"agent_thought_chunk\") {\n if (!thoughtStarted && currentOnProgress) {\n thoughtStarted = true;\n try {\n currentOnProgress({ type: \"thinking\" });\n } catch {\n /* ignore */\n }\n }\n } else if (\n (u?.sessionUpdate === \"tool_call\" || u?.sessionUpdate === \"tool_call_update\") &&\n currentOnProgress\n ) {\n const tid = String(u.toolCallId ?? u.title ?? \"\");\n if (tid && !seenTools.has(tid)) {\n seenTools.add(tid);\n const purpose =\n typeof u.rawInput?.__tool_use_purpose === \"string\"\n ? u.rawInput.__tool_use_purpose\n : undefined;\n try {\n currentOnProgress({ type: \"tool\", kind: u.kind, purpose, title: u.title });\n } catch {\n /* ignore */\n }\n }\n }\n }\n}\n\nfunction onStdout(d: Buffer): void {\n stdoutBuf += d.toString();\n let i: number;\n while ((i = stdoutBuf.indexOf(\"\\n\")) >= 0) {\n const line = stdoutBuf.slice(0, i).trim();\n stdoutBuf = stdoutBuf.slice(i + 1);\n if (!line) continue;\n let f: Record<string, any>;\n try {\n f = JSON.parse(line);\n } catch {\n continue;\n }\n handleFrame(f);\n }\n}\n\nfunction request(method: string, params: unknown, timeoutMs: number): Promise<any> {\n return new Promise((resolve, reject) => {\n if (!child) {\n reject(new Error(\"no worker process\"));\n return;\n }\n const id = ++idCounter;\n dlog(`→ ${method}#${id}`);\n const t = setTimeout(() => {\n pending.delete(id);\n reject(new Error(`${method} timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n pending.set(id, { resolve, reject, clear: () => clearTimeout(t), method });\n child.stdin.write(JSON.stringify({ jsonrpc: \"2.0\", id, method, params }) + \"\\n\");\n });\n}\n\n/** session/prompt 专用:空闲超时 + 首帧超时 + 硬上限(漫长任务,比 brain 宽松)。 */\nfunction promptRequest(params: unknown): Promise<any> {\n return new Promise((resolve, reject) => {\n if (!child) {\n reject(new Error(\"no worker process\"));\n return;\n }\n const id = ++idCounter;\n const startedAt = Date.now();\n lastFrameAt = Date.now();\n const startSeq = frameSeq;\n dlog(\n `→ session/prompt#${id} (idle=${PROMPT_IDLE_MS}ms cap=${PROMPT_CAP_MS}ms firstFrame=${FIRST_FRAME_MS}ms)`,\n );\n const iv = setInterval(() => {\n const idle = Date.now() - lastFrameAt;\n const total = Date.now() - startedAt;\n const noFrameYet = frameSeq === startSeq;\n const firstFrameTimeout = noFrameYet && total > FIRST_FRAME_MS;\n if (firstFrameTimeout || idle > PROMPT_IDLE_MS || total > PROMPT_CAP_MS) {\n if (pending.delete(id)) {\n clearInterval(iv);\n const why = firstFrameTimeout ? \"no-first-frame\" : idle > PROMPT_IDLE_MS ? \"idle\" : \"cap\";\n reject(new Error(`session/prompt ${why} timeout (idle=${idle}ms total=${total}ms)`));\n }\n }\n }, 5000);\n pending.set(id, { resolve, reject, clear: () => clearInterval(iv), method: \"session/prompt\" });\n child.stdin.write(\n JSON.stringify({ jsonrpc: \"2.0\", id, method: \"session/prompt\", params }) + \"\\n\",\n );\n });\n}\n\nasync function ensureProcess(): Promise<void> {\n if (isWorkerAlive() && initialized) return;\n reset();\n const env: NodeJS.ProcessEnv = { ...process.env };\n if (AGENT.homeEnvVar) env[AGENT.homeEnvVar] = WORKER_HOME; // 配置目录(MCP/记忆)走 WORKER_HOME 沙箱\n // 预设声明的额外环境变量(%HOME% → 沙箱根)\n for (const [k, v] of Object.entries(AGENT.env ?? {}))\n env[k] = v.replaceAll(\"%HOME%\", WORKER_HOME);\n const cwd = effectiveCwd();\n const proc = spawnAgent(AGENT, {\n cwd, // 任务工作目录(本地派发可指定,否则默认 WORKER_CWD),区别于 KIRO_HOME 沙箱\n env,\n });\n child = proc;\n dlog(`spawn ${AGENT.name} worker pid=${proc.pid} cwd=${cwd} home=${WORKER_HOME}`);\n try {\n fs.writeFileSync(WORKER_PID_FILE, String(proc.pid), \"utf-8\");\n } catch {\n /* best-effort */\n }\n proc.stdout.on(\"data\", onStdout);\n proc.stderr.on(\"data\", () => {\n /* ignore */\n });\n proc.on(\"exit\", (code) => {\n dlog(`worker exited code=${code}`);\n if (child === proc) reset();\n });\n proc.on(\"error\", (e) => {\n dlog(`worker error ${String(e)}`);\n if (child === proc) reset();\n });\n await request(\"initialize\", { protocolVersion: 1, clientCapabilities: {} }, REQ_TIMEOUT);\n initialized = true;\n dlog(\"initialized\");\n}\n\nasync function applyModel(sid: string): Promise<void> {\n if (!AGENT.model) return;\n try {\n await request(\n \"session/set_config_option\",\n { sessionId: sid, configId: \"model\", value: AGENT.model },\n REQ_TIMEOUT,\n );\n dlog(`model set ${AGENT.model}`);\n } catch (e) {\n dlog(`model set failed (${String(e)})`);\n throw e;\n }\n}\n\nasync function ensureSession(key: string): Promise<string> {\n const existing = taskSessions.get(key);\n if (existing) return existing;\n // mcpViaAcp 预设(DSH):worker 继承全量 MCP(除排除集),随 session 下发;其余 agent 传空\n const mcpServers = AGENT.mcpViaAcp ? workerMcpServers() : [];\n const map = loadSessionMap();\n const saved = map[keyOf(key)];\n if (saved) {\n const methods: Array<\"load\" | \"resume\"> =\n AGENT.restore === \"resume\" ? [\"resume\"] : [\"load\", \"resume\"];\n for (const method of methods) {\n try {\n await request(\n `session/${method}`,\n { sessionId: saved, cwd: effectiveCwd(), mcpServers },\n REQ_TIMEOUT,\n );\n await applyModel(saved);\n taskSessions.set(key, saved);\n dlog(`session/${method} ok ${saved} mcp=[${mcpServers.map((s: any) => s.name).join(\",\")}]`);\n return saved;\n } catch (e) {\n dlog(`session/${method} failed (${String(e)})`);\n }\n }\n }\n const res = (await request(\"session/new\", { cwd: effectiveCwd(), mcpServers }, REQ_TIMEOUT)) as {\n sessionId?: string;\n };\n if (!res?.sessionId) throw new Error(\"session/new returned no sessionId\");\n await applyModel(res.sessionId);\n taskSessions.set(key, res.sessionId);\n map[keyOf(key)] = res.sessionId;\n saveSessionMap(map);\n dlog(`session/new ok ${res.sessionId} mcp=[${mcpServers.map((s: any) => s.name).join(\",\")}]`);\n return res.sessionId;\n}\n\n/**\n * 向 worker 发一轮 prompt(任务派发或注入续跑),等这一轮 ACP 响应结束,返回拼接文本。\n * 失败即重建进程(下一轮 session/load 恢复上下文)→ 配合「继续」实现中断恢复。\n */\nexport async function askWorker(\n instruction: string,\n onProgress?: (ev: WorkerProgressEvent) => void,\n): Promise<string> {\n await ensureProcess();\n const sid = await ensureSession(TASK_KEY);\n assembling.set(sid, []);\n currentOnProgress = onProgress ?? null;\n answerStarted = false;\n thoughtStarted = false;\n seenUpdateTypes.clear();\n seenTools.clear();\n const t0 = Date.now();\n const prompt: unknown[] = [{ type: \"text\", text: instruction || \"(继续)\" }];\n try {\n await promptRequest({ sessionId: sid, prompt });\n const txt = (assembling.get(sid) ?? []).join(\"\").trim();\n dlog(\n `prompt ok ${Date.now() - t0}ms chunks=${(assembling.get(sid) ?? []).length} updateTypes=[${[...seenUpdateTypes].join(\",\")}]`,\n );\n return txt;\n } catch (e) {\n const chunks = (assembling.get(sid) ?? []).length;\n const sinceFrame = lastFrameAt ? Date.now() - lastFrameAt : -1;\n dlog(\n `prompt FAIL ${Date.now() - t0}ms chunks=${chunks} lastFrameAgo=${sinceFrame}ms err=${String(e)} -> rebuild`,\n );\n reset();\n throw e;\n } finally {\n currentOnProgress = null;\n assembling.delete(sid);\n }\n}\n\n/** 开新任务会话:丢弃旧 session(含磁盘映射),下次 askWorker 全新开始。 */\nexport function newWorkerSession(): void {\n const map = loadSessionMap();\n delete map[keyOf(TASK_KEY)]; // 只清当前 agent 的,换大脑前的任务上下文保留\n saveSessionMap(map);\n taskSessions.delete(TASK_KEY);\n}\n\n/** 停止 worker:杀进程、清状态、丢弃任务 session(任务结束)。 */\nexport function stopWorker(): void {\n reset();\n newWorkerSession();\n pendingCwd = undefined; // 任务结束,清掉本任务 cwd(下个任务重新指定或用默认)\n}\n","import { askWorker, stopWorker, setWorkerCwd, type WorkerProgressEvent } from \"./worker-acp.js\";\n\nconst log = (...a: unknown[]) => console.error(\"[wemind/wechat-daemon/task]\", ...a);\n\ntype TaskStatus = \"running\" | \"waiting\";\ntype ActiveTask = {\n to: string;\n status: TaskStatus;\n injectResolver?: (text: string | null) => void; // waiting 态等待注入;stop 时 resolve(null) 退出\n pendingInjects?: string[]; // running 期间暂存的注入(下次 waiting 一并消费,按序累积不覆盖)\n startedAt: number; // 派发时间\n turns: number; // 已完成轮次\n lastOutput?: string; // 最近一轮推送内容(供本地 task-status 查看)\n};\n\nlet activeTask: ActiveTask | null = null;\n\n// 依赖注入:把「推送文本给用户」交给 index 提供(内部走 sendOrdered + 最新 token),避免 task-manager 直接依赖发送层/account。\ntype PushFn = (to: string, text: string) => void;\nlet push: PushFn = () => {};\nexport function setTaskPush(fn: PushFn): void {\n push = fn;\n}\n\n// 依赖注入:发「正在输入中」(typing 不占连续发送上限),让 worker 跑一轮期间用户知道在干活。\nlet typing: (to: string) => void = () => {};\nexport function setTaskTyping(fn: (to: string) => void): void {\n typing = fn;\n}\n\n/** 任务状态快照(落盘供本地 `task-status` 读)。 */\nexport type TaskStateSnapshot = {\n active: boolean;\n status: TaskStatus | \"idle\";\n to?: string;\n startedAt?: number;\n updatedAt: number;\n turns: number;\n lastOutput?: string;\n};\n// 依赖注入:把状态快照落盘交给 index(写 task-state.json),task-manager 不直接碰文件、保持可测。\nlet stateSink: (s: TaskStateSnapshot) => void = () => {};\nexport function setTaskStateSink(fn: (s: TaskStateSnapshot) => void): void {\n stateSink = fn;\n}\n/** 用当前 activeTask 构造快照并落盘(在派发/每轮/waiting/停止等关键节点调用)。 */\nfunction emitState(): void {\n const t = activeTask;\n const snap: TaskStateSnapshot = t\n ? {\n active: true,\n status: t.status,\n to: t.to,\n startedAt: t.startedAt,\n updatedAt: Date.now(),\n turns: t.turns,\n lastOutput: t.lastOutput,\n }\n : { active: false, status: \"idle\", updatedAt: Date.now(), turns: 0 };\n try {\n stateSink(snap);\n } catch {\n /* best-effort */\n }\n}\n\nconst PROGRESS = (process.env.WEMIND_WORKER_PROGRESS ?? \"0\") === \"1\"; // 默认安静:只在轮次结束/中断推送\n\n/** 是否存在活跃任务(可选限定某用户)。 */\nexport function hasActiveTask(to?: string): boolean {\n return !!activeTask && (to === undefined || activeTask.to === to);\n}\n\n/** worker 是否正等该用户回复(waiting 态)。路由用:仅此时「无前缀」消息才默认注入 worker。 */\nexport function isAwaiting(to: string): boolean {\n return !!activeTask && activeTask.to === to && activeTask.status === \"waiting\";\n}\n\n/** 派发新任务(后台异步执行,立即返回回执文案,不阻塞调用方)。 */\nexport function dispatchTask(to: string, instruction: string, cwd?: string): string {\n if (activeTask) return \"已有任务在跑,先回复「结束任务」再派发新任务。\";\n setWorkerCwd(cwd); // 本地派发可指定 cwd(终端当前目录);微信派发 cwd=undefined → 用默认 WORKER_CWD\n void runTask(to, instruction);\n return \"✅ 已派给工作机执行(任务运行中)。需要你决策或中断时我会主动找你;随时可回复「结束任务」终止。\";\n}\n\n/** 注入用户消息:waiting → 直接喂下一轮;running → 暂存为 pendingInject。 */\nexport function injectTask(to: string, text: string): void {\n if (!activeTask || activeTask.to !== to) return;\n if (activeTask.status === \"waiting\" && activeTask.injectResolver) {\n const r = activeTask.injectResolver;\n activeTask.injectResolver = undefined;\n r(text);\n } else {\n (activeTask.pendingInjects ??= []).push(text); // running 期间:按序累积,等这一步跑完一并处理(不覆盖前一条)\n push(to, \"📝 已记下,等当前这步跑完就处理。\");\n }\n}\n\n/** 结束任务:停 worker、清状态、唤醒退出循环。 */\nexport function stopTask(to: string): string {\n if (!activeTask || activeTask.to !== to) {\n // daemon 重启后内存无任务,但 task-state.json 可能残留 active/waiting(幽灵任务)→ 仍要落盘 idle\n emitState();\n return \"当前没有正在运行的任务。\";\n }\n const task = activeTask;\n activeTask = null; // 先清,runTask 循环靠它感知被 stop\n if (task.injectResolver) {\n const r = task.injectResolver;\n task.injectResolver = undefined;\n r(null);\n }\n stopWorker();\n emitState(); // → idle\n return \"🛑 任务已结束。\";\n}\n\n/** 等待用户注入(waiting 态);若有暂存的 pendingInject 直接消费。 */\nfunction waitForInject(task: ActiveTask): Promise<string | null> {\n if (task.pendingInjects?.length) {\n const t = task.pendingInjects.join(\"\\n\"); // 多条暂存按序合并喂下一轮,一条不丢\n task.pendingInjects = undefined;\n return Promise.resolve(t);\n }\n return new Promise((resolve) => {\n task.injectResolver = resolve;\n });\n}\n\nfunction reason(e: unknown): string {\n const s = String(e instanceof Error ? e.message : e);\n if (/no-first-frame|idle|cap|timeout/i.test(s)) return \"超时/卡住\";\n return \"执行出错\";\n}\n\n/** 后台任务循环:每轮 askWorker → 推送本轮输出 → 等注入 → 续跑。绝不在 workerLoop 里 await 它。 */\nasync function runTask(to: string, firstInstruction: string): Promise<void> {\n activeTask = { to, status: \"running\", startedAt: Date.now(), turns: 0 };\n emitState();\n let instruction: string | null = firstInstruction;\n while (activeTask && instruction !== null) {\n typing(to); // 立即一次「正在输入」\n const ti = setInterval(() => typing(to), 5000); // 每 5s 续命(小于客户端 typing 自动过期,防闪烁;不占连续发送上限),表明 worker 在干活\n try {\n const onProgress = PROGRESS\n ? (ev: WorkerProgressEvent) => {\n if (ev.type === \"tool\") push(to, \"🔧 worker 正在调用工具…\");\n }\n : undefined;\n const out = await askWorker(instruction, onProgress);\n if (!activeTask) break; // 期间被 stop\n const text = `🤖【任务】${out || \"(本轮无输出,可回复「继续」推进或「结束任务」终止)\"}`;\n activeTask.turns += 1;\n activeTask.lastOutput = text;\n push(to, text);\n } catch (e) {\n if (!activeTask) break; // 被 stop 导致的 reject,不算中断\n log(\"worker turn failed:\", String(e));\n const text = `⚠️ 任务中断了(${reason(e)})。回复「继续」我从中断处恢复,或「结束任务」终止。`;\n activeTask.lastOutput = text;\n push(to, text);\n } finally {\n clearInterval(ti); // 本轮结束停 typing\n }\n if (!activeTask) break;\n activeTask.status = \"waiting\";\n emitState(); // → waiting(task-status 可见「等你回复」)\n instruction = await waitForInject(activeTask); // null = stop\n if (activeTask) {\n activeTask.status = \"running\";\n emitState();\n }\n }\n emitState(); // 循环结束(多为 idle)\n log(\"runTask loop ended\");\n}\n","#!/usr/bin/env node\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n getUpdates,\n sendMessage,\n sendTyping,\n getConfig,\n notifyStart,\n generateId,\n SESSION_EXPIRED_ERRCODE,\n isSessionPaused,\n pauseSession,\n getRemainingPauseMs,\n loadAccount,\n loadState,\n saveState,\n DATA_DIR,\n MessageItemType,\n MessageState,\n MessageType,\n downloadImage,\n sendImage,\n sendFile,\n ILinkError,\n classifyTransportError,\n isAllowed,\n addAllowFrom,\n readAllowFrom,\n} from \"@wemind/wechat-ilink-core\";\nimport type {\n AccountData,\n ServerState,\n WeixinMessage,\n SendMessageReq,\n ImageItem,\n} from \"@wemind/wechat-ilink-core\";\nimport { askBrain, newSession, restart, supportsImages, heartbeatMs } from \"./acp-brain.js\";\nimport { resolveAgent, agentProblem, BRAIN_CONFIG_EXIT_CODE } from \"./brain-agents.js\";\nimport { loadBrainConfig } from \"./brain-config.js\";\nimport { ensureBrainHome, addMcp, removeMcp, listMcp } from \"./mcp-sandbox.js\";\nimport { extractImages, resolveImageBytes } from \"./image-reply.js\";\nimport { LONG_REPLY_THRESHOLD, buildMdReply } from \"./md-reply.js\";\nimport {\n dispatchTask,\n injectTask,\n stopTask,\n hasActiveTask,\n isAwaiting,\n setTaskPush,\n setTaskTyping,\n setTaskStateSink,\n type TaskStateSnapshot,\n} from \"./task-manager.js\";\nimport { ensureWorkerHome } from \"./worker-sandbox.js\";\n\nconst log = (...a: unknown[]) => console.error(\"[wemind/wechat-daemon]\", ...a);\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/** 分类大脑错误:backend=模型后端瞬时错误(可多重试);timeout=超时/卡死;other=其它。 */\nfunction classifyBrainError(e: unknown): \"timeout\" | \"backend\" | \"other\" {\n const s = String(e instanceof Error ? e.message : e);\n if (\n /-32603|internal error|please try again|response stream|overloaded|rate.?limit|\\b(429|503)\\b/i.test(\n s,\n )\n )\n return \"backend\";\n if (/idle timeout|no-first-frame|cap timeout|timeout/i.test(s)) return \"timeout\";\n return \"other\";\n}\n/** 按错误类型给用户的失败提示。 */\nfunction brainErrorMessage(e: unknown): string {\n switch (classifyBrainError(e)) {\n case \"backend\":\n return \"(模型后端临时出错了,自动重试多次仍失败,请稍后把消息再发一次)\";\n case \"timeout\":\n return \"(这次处理超时/卡住被中断了,重试也没成功——可把任务拆小一点再发)\";\n default:\n return \"(处理出错了,请把消息再发一次试试)\";\n }\n}\n\nfunction extractText(msg: WeixinMessage): string {\n return (msg.item_list ?? [])\n .map((it) => {\n if (it.type === MessageItemType.TEXT) return it.text_item?.text ?? \"\";\n if (it.type === MessageItemType.VOICE) return it.voice_item?.text ?? \"\"; // 微信侧语音转文字(省事路)\n return \"\";\n })\n .filter((s) => s)\n .join(\"\\n\")\n .trim();\n}\n\n/** 工具标题中文化(无 purpose 时的兜底)。 */\nfunction zhTool(t: string): string {\n return t\n .replace(/^Searching the web for:?\\s*/i, \"联网搜索:\")\n .replace(/^Running:?\\s*/i, \"执行:\")\n .replace(/^Reading\\s+/i, \"读取 \")\n .replace(/^Writing\\s+/i, \"写入 \")\n .replace(/^Finding\\s+/i, \"查找 \");\n}\n\n/** 按工具类型给图标。 */\nfunction toolIcon(kind?: string): string {\n switch (kind) {\n case \"read\":\n return \"📄\";\n case \"search\":\n return \"🔍\";\n case \"execute\":\n return \"⌨️\";\n case \"fetch\":\n return \"🌐\";\n case \"edit\":\n case \"write\":\n return \"✏️\";\n case \"delete\":\n return \"🗑️\";\n default:\n return \"🔧\";\n }\n}\n\ntype Ctrl =\n | { kind: \"list\" }\n | { kind: \"new\" }\n | { kind: \"add\"; name: string }\n | { kind: \"remove\"; name: string };\n\n/** 解析控制指令(命中则由 daemon 直接处理,不转发给 brain)。 */\nexport function parseControl(text: string): Ctrl | null {\n const t = text.trim();\n if (/^(\\/?mcp|列出\\s*mcp|mcp\\s*list)$/i.test(t)) return { kind: \"list\" };\n if (/^(新会话|重置|清空上下文|\\/new|new\\s*session)$/i.test(t)) return { kind: \"new\" };\n let m = t.match(/^(?:加载|load)\\s+(\\S+)$/i);\n if (m) return { kind: \"add\", name: m[1] };\n m = t.match(/^(?:卸载|unload)\\s+(\\S+)$/i);\n if (m) return { kind: \"remove\", name: m[1] };\n return null;\n}\n\ntype TaskCmd = { kind: \"toWorker\"; text: string } | { kind: \"stop\" };\n/** 解析「明确发给 worker」指令:任务:xxx / 任务 xxx / >xxx(toWorker:无任务则启动、有任务则注入);结束任务/停止任务 → stop。 */\nexport function parseTaskControl(text: string): TaskCmd | null {\n const t = text.trim();\n if (/^(结束任务|停止任务|\\/stop)$/i.test(t)) return { kind: \"stop\" };\n let m = t.match(/^(?:任务|task)[::]\\s*([\\s\\S]+)$/i);\n if (m) return { kind: \"toWorker\", text: m[1].trim() };\n m = t.match(/^>\\s*([\\s\\S]+)$/); // 快捷别名:> 开头即明确发给 worker\n if (m) return { kind: \"toWorker\", text: m[1].trim() };\n return null;\n}\n\n/** 解析「明确问大脑」逃逸前缀:问大脑 xxx / 问大脑:xxx。命中返回去前缀后的文本(强制走 brain,即使 worker 在等你)。 */\nexport function parseBrainEscape(text: string): string | null {\n const m = text.trim().match(/^问大脑[::]?\\s*([\\s\\S]+)$/);\n return m ? m[1].trim() : null;\n}\n\nasync function handleControl(c: Ctrl, userId: string): Promise<string> {\n if (c.kind === \"list\") return listMcp();\n if (c.kind === \"new\") {\n await newSession(userId);\n return \"已开启新会话(上下文已清空)。\";\n }\n if (c.kind === \"add\") {\n const r = addMcp(c.name);\n restart(); // 重启 acp 进程,使其按新沙箱 mcp.json 重新加载\n return r;\n }\n const r = removeMcp(c.name);\n restart();\n return r;\n}\n\nasync function setTyping(\n account: AccountData,\n to: string,\n ctx: string | undefined,\n status: 1 | 2,\n): Promise<void> {\n if (!ctx) return;\n try {\n const cfg = await getConfig({\n baseUrl: account.baseUrl,\n token: account.botToken,\n ilinkUserId: to,\n contextToken: ctx,\n });\n await sendTyping({\n baseUrl: account.baseUrl,\n token: account.botToken,\n body: { ilink_user_id: to, typing_ticket: cfg.typing_ticket, status },\n });\n } catch (e) {\n // typing 是体验增强(best-effort 不阻断),但失败要留痕:区分「偶发网络失败」与「ticket 拿不到」\n log(`setTyping(${status}) failed:`, String(e));\n }\n}\n\nasync function reply(\n account: AccountData,\n to: string,\n ctx: string | undefined,\n text: string,\n state: number = MessageState.FINISH,\n fallbackToken?: string,\n): Promise<void> {\n const send = (token: string | undefined) => {\n const req: SendMessageReq = {\n msg: {\n from_user_id: \"\",\n to_user_id: to,\n client_id: generateId(\"wemind/wechat-daemon\"),\n message_type: MessageType.BOT,\n message_state: state,\n item_list: [{ type: MessageItemType.TEXT, text_item: { text } }],\n context_token: token,\n },\n };\n return sendMessage({ baseUrl: account.baseUrl, token: account.botToken, body: req });\n };\n try {\n await send(ctx);\n } catch (e) {\n // ret=-2 既可能是「限流」(rapid sends)也可能是「context_token 失效/不匹配」(参数错误)。\n // 两者都靠:退避 ~1.5s 后重试一次——限流靠等待恢复,失效 token 则换用该用户最新缓存 token。\n if (e instanceof ILinkError && e.kind === \"param_error\") {\n log(\"send -2(限流或token失效) → 退避 1.5s 后重试\");\n await new Promise((r) => setTimeout(r, 1500));\n await send(fallbackToken && fallbackToken !== ctx ? fallbackToken : ctx);\n return;\n }\n // 传输层错误(网络抖动 fetch failed/ECONNRESET/超时等):可重试类退避 1.5s 后重试一次\n if (!(e instanceof ILinkError)) {\n const t = classifyTransportError(e);\n if (t.retryable) {\n log(`send 传输错误(${t.kind}) → 退避 1.5s 后重试`);\n await new Promise((r) => setTimeout(r, 1500));\n await send(ctx);\n return;\n }\n }\n throw e;\n }\n}\n\n/** 统一的发送错误处理:按错误类型分类记录;会话过期则暂停账号。 */\nfunction handleSendError(e: unknown, account: AccountData): void {\n if (e instanceof ILinkError) {\n log(\"send error:\", e.message);\n if (e.kind === \"session_expired\") {\n pauseSession(account.botId);\n log(\"会话过期(-14) on send → 该账号暂停 1h,待重新登录\");\n }\n } else {\n const t = classifyTransportError(e);\n log(`send error(传输/${t.kind}${t.retryable ? \",可重试\" : \"\"}):`, t.message);\n }\n}\n\n// 串行发送队列:保证多条消息(进度/答案)按入队顺序送达,不并发乱序\nlet replyChain: Promise<void> = Promise.resolve();\nlet lastSendAt = 0;\n// 两条发送之间的最小间隔:iLink 对 bot 发送有速率限流(rapid sends → ret=-2 \"rate limited\",导致消息丢失,\n// 见 NousResearch/hermes-agent#21011)。全局节流主动规避。\nconst MIN_SEND_GAP_MS = Number(process.env.WECHAT_MIN_SEND_GAP_MS) || 1500;\n\n// iLink 在用户回复前能连续发送的消息数上限(实测约 10,可调)。\nconst CONSEC_SEND_LIMIT = Number(process.env.WECHAT_CONSEC_SEND_LIMIT) || 10;\n// 静默期心跳:默认 15s;预设可放宽(如 DSH committed-only,但仍会发 thought/usage 帧)。\nconst HEARTBEAT_MS = Number(process.env.WECHAT_BRAIN_HEARTBEAT_MS) || heartbeatMs() || 15_000;\n// 「距该用户上一条消息以来」bot 已连续发送的条数 + 是否处于\"等待继续\"段。按用户(to)维护、**跨回合持续**,\n// 仅在该用户发来任何消息时重置(镜像服务端\"任何用户消息都重置连续发送配额\"的规则)。\n// 这样即使 A 任务期间你发了 B、A 又继续发了几条,B 任务接手时计数是延续的,不会误以为\"还能再发10条\"而撞上限。\ntype SendBudget = { count: number; awaiting: boolean };\nconst sendBudget = new Map<string, SendBudget>();\nfunction budgetOf(to: string): SendBudget {\n let b = sendBudget.get(to);\n if (!b) {\n b = { count: 0, awaiting: false };\n sendBudget.set(to, b);\n }\n return b;\n}\n// 当前进行中回合(worker 串行,只有一个):记录发往用户与所用 token(token 可被「继续」更新)。\nlet activeTurn: { from: string; token: string | undefined } | null = null;\nfunction sendOrdered(\n account: AccountData,\n to: string,\n ctx: string | undefined,\n text: string,\n state: number = MessageState.FINISH,\n fallbackToken?: string,\n): Promise<void> {\n replyChain = replyChain.then(async () => {\n const wait = MIN_SEND_GAP_MS - (Date.now() - lastSendAt);\n if (wait > 0) await sleep(wait);\n await reply(account, to, ctx, text, state, fallbackToken).catch((e) =>\n handleSendError(e, account),\n );\n lastSendAt = Date.now();\n });\n return replyChain;\n}\n\n/** 把长回复作为 .md 文件发送(走同一串行链+节流);发文件失败则兜底回退为文本。 */\nfunction sendMdOrdered(\n account: AccountData,\n to: string,\n ctx: string | undefined,\n finalText: string,\n fallbackToken?: string,\n): Promise<void> {\n replyChain = replyChain.then(async () => {\n const wait = MIN_SEND_GAP_MS - (Date.now() - lastSendAt);\n if (wait > 0) await sleep(wait);\n const { fileName, data } = buildMdReply(finalText);\n try {\n await sendFile({\n baseUrl: account.baseUrl,\n token: account.botToken,\n to,\n data,\n fileName,\n kind: \"file\",\n contextToken: ctx,\n });\n log(`final as md file: ${fileName} (${finalText.length} chars)`);\n } catch (e) {\n log(\"send md file failed, fallback to text:\", String(e));\n await reply(account, to, ctx, finalText, MessageState.FINISH, fallbackToken).catch((er) =>\n handleSendError(er, account),\n );\n }\n lastSendAt = Date.now();\n });\n return replyChain;\n}\n\n/** 从解密后的字节嗅探图片 MIME。 */\nfunction sniffMime(b: Buffer): string {\n const h = b.subarray(0, 4).toString(\"hex\");\n if (h.startsWith(\"89504e47\")) return \"image/png\";\n if (h.startsWith(\"47494638\")) return \"image/gif\";\n if (h.startsWith(\"52494646\")) return \"image/webp\";\n return \"image/jpeg\";\n}\n\nasync function handleMessage(\n account: AccountData,\n ctx: string | undefined,\n to: string,\n text: string,\n imageItems: ImageItem[],\n createTimeMs?: number,\n latestToken?: () => string | undefined,\n): Promise<void> {\n const msgAge = () => (createTimeMs ? `${Math.round((Date.now() - createTimeMs) / 1000)}s` : \"?\"); // 这条消息已存在多久(诊断用)\n // activeTurn 只存当前回合的 from/token;连续发送计数用按用户、跨回合的 budgetOf(to)(不在回合开始清零)。\n activeTurn = { from: to, token: ctx };\n const cx = () => activeTurn?.token ?? ctx;\n await setTyping(account, to, cx(), 1);\n // 处理期间每 5s 续发\"正在输入\"(sendtyping 不计入连续消息上限),避免长任务时指示消失、用户以为卡了。\n // 间隔需小于客户端对 typing 的自动过期(约 5–8s),否则中间会出现指示闪烁的空洞。\n const keepAlive = setInterval(() => {\n void setTyping(account, to, cx(), 1).catch(() => {});\n }, 5000);\n\n const t0 = Date.now();\n let lastProgressAt = Date.now();\n // iLink 限制 bot 在用户回复前连续发送的消息数(实测约 10,超出被 ret=-2 拒)。计数按用户跨回合维护(budgetOf)。\n // 当已发到「上限-2」(默认第8条)后,下一条进度改为「即将达上限」提示并进入等待,把最后一条额度留给最终答案;\n // 用户回复「继续」(或发任何消息)会重置计数恢复发送(见 poller)。\n const sendProgress = (msg: string) => {\n lastProgressAt = Date.now();\n const b = budgetOf(to);\n if (b.awaiting) return; // 等待「继续」期间不发任何进度\n // 发出一条文本会把客户端刚显示的「正在输入」顶掉 → 消息送达后立刻补发一次 typing,\n // 否则快回合(回复 < 旧 8s 续发间隔)全程看不到指示。\n const sendWithTyping = (text: string) => {\n void sendOrdered(account, to, cx(), text, MessageState.GENERATING)\n .then(() => setTyping(account, to, cx(), 1))\n .catch(() => {});\n };\n if (b.count >= CONSEC_SEND_LIMIT - 2) {\n b.count += 1;\n b.awaiting = true;\n sendWithTyping(\n \"⚠️ 接近微信连续消息上限,我先暂停进度提示、继续在后台处理;完成后会把结果直接发给你。想立刻恢复进度可回复「继续」。\",\n );\n return;\n }\n b.count += 1;\n sendWithTyping(msg);\n };\n // 即时回执:收到即给一条友好提示,保证用户看到的第一条文本不是 15s 兜底心跳。\n // (有图片时,下面的\"🖼️ 收到 N 张图片…\"已充当即时回执,不再重复发)\n if (imageItems.length === 0) {\n const t = cx();\n log(`turn start to=${to} msgAge=${msgAge()} usingCtx=…${t ? t.slice(-12) : \"NONE\"}`);\n sendProgress(\"🤖 收到,正在处理…\");\n }\n // 心跳:仅当 brain 真正静默(无任何帧)超过 HEARTBEAT_MS 才提示——首条用友好措辞,后续才带计时\n let hbCount = 0;\n let answerNoticeSent = false; // \"正在整理回复\"只发一次\n const heartbeat = setInterval(() => {\n if (Date.now() - lastProgressAt >= HEARTBEAT_MS) {\n hbCount += 1;\n sendProgress(\n hbCount === 1\n ? \"🤔 还在思考/检索中…\"\n : `⏳ 仍在处理中…(已 ${Math.round((Date.now() - t0) / 1000)} 秒)`,\n );\n }\n }, HEARTBEAT_MS);\n\n // 收图:下载并解密(失败的跳过)\n const images: Array<{ data: string; mimeType: string }> = [];\n if (imageItems.length > 0) {\n sendProgress(`🖼️ 收到 ${imageItems.length} 张图片,下载识别中…`);\n for (const im of imageItems) {\n try {\n const buf = await downloadImage(im);\n images.push({ data: buf.toString(\"base64\"), mimeType: sniffMime(buf) });\n } catch (e) {\n log(\"image download failed:\", String(e));\n }\n }\n }\n\n // 当前大脑不接受图片(如 DSH 0.2.1-rc.2 image:false):不要让它以 -32602 失败重试\n if (images.length > 0 && !supportsImages()) {\n clearInterval(keepAlive);\n clearInterval(heartbeat);\n await setTyping(account, to, cx(), 2);\n budgetOf(to).count += 1;\n try {\n await sendOrdered(\n account,\n to,\n cx(),\n \"🖼️ 当前大脑暂不支持看图,请改用文字描述,或切换到支持图片的大脑。\",\n MessageState.FINISH,\n );\n log(\"image unsupported by current brain; rejected gracefully\");\n } finally {\n activeTurn = null;\n }\n return;\n }\n\n // 一次尝试:带进度(步骤编号每次尝试重置)。进度消息节流:避免一回合刷十几条触发 iLink 发送限流(疑似 ret=-2 主因)\n const attempt = (promptText: string, imgs: typeof images): Promise<string> => {\n let step = 0;\n let lastToolProgressAt = 0;\n return askBrain(\n to,\n promptText,\n (ev) => {\n if (ev.type === \"tool\") {\n lastProgressAt = Date.now(); // 工具活动刷新心跳基线\n // 首条立即发(让你知道开始干活了),之后每 15s 最多一条,避免刷屏 + 触发限流\n if (Date.now() - lastToolProgressAt >= 15000) {\n lastToolProgressAt = Date.now();\n step += 1; // 只在真正发出时递增,保证步骤号连续(不因节流跳号)\n const desc = ev.purpose || (ev.title ? zhTool(ev.title) : \"\") || \"调用工具\";\n sendProgress(`${toolIcon(ev.kind)} 步骤 ${step}:${desc}`);\n }\n } else if (ev.type === \"thinking\") {\n lastProgressAt = Date.now();\n } else if (ev.type === \"answer\") {\n if (!answerNoticeSent) {\n answerNoticeSent = true;\n sendProgress(\"✍️ 正在整理回复…\");\n }\n } else {\n // activity:brain 产出任意帧 → 静默刷新心跳基线,避免流式输出期间误报\"仍在处理\"\n lastProgressAt = Date.now();\n }\n },\n imgs,\n );\n };\n\n // 带分类的重试:backend(模型后端瞬时错误 -32603 等)最多 3 次;timeout/other 最多 2 次\n const runWithRetry = async (promptText: string, imgs: typeof images): Promise<string> => {\n let n = 0;\n while (true) {\n n++;\n try {\n return await attempt(promptText, imgs);\n } catch (e) {\n const cat = classifyBrainError(e);\n log(`brain error (try ${n}, ${cat}): ${String(e)}`);\n const maxAttempts = cat === \"backend\" ? 3 : 2;\n if (n < maxAttempts) {\n sendProgress(\n cat === \"backend\" ? \"⚠️ 模型后端临时出错,正在重试…\" : \"⏳ 响应中断了,正在自动重试…\",\n );\n continue;\n }\n throw e;\n }\n }\n };\n\n let answer: string;\n try {\n answer = await runWithRetry(text, images);\n } catch (e2) {\n answer = brainErrorMessage(e2);\n } finally {\n clearInterval(keepAlive);\n clearInterval(heartbeat);\n }\n await setTyping(account, to, cx(), 2);\n // 发图:从回复里提取图片引用(markdown  或裸图片 URL),逐个作为图片消息发出,再发剩余文本\n const { images: replyImages, text: cleaned } = extractImages(answer || \"\");\n const failedImgs: string[] = [];\n for (const im of replyImages) {\n try {\n const buf = await resolveImageBytes(im.src);\n await sendImage({\n baseUrl: account.baseUrl,\n token: account.botToken,\n to,\n image: buf,\n contextToken: cx(),\n });\n log(`sent image: ${im.src.slice(0, 80)}`);\n } catch (e) {\n log(\"send image failed:\", im.src, String(e));\n failedImgs.push(im.src);\n }\n }\n let finalText = cleaned;\n if (failedImgs.length) {\n const note = `(有 ${failedImgs.length} 张图片我没能下载下来,附原链接:\\n${failedImgs.join(\"\\n\")})`;\n finalText = finalText ? `${finalText}\\n\\n${note}` : note;\n }\n if (!finalText && !replyImages.length) finalText = \"(没有内容)\";\n if (finalText) {\n budgetOf(to).count += 1; // 最终答案占一条额度(等待段预留的最后一条)\n log(`final send: msgAge=${msgAge()} sent=${budgetOf(to).count} len=${finalText.length}`);\n const ft = latestToken ? latestToken() : undefined;\n if (finalText.length > LONG_REPLY_THRESHOLD) {\n await sendMdOrdered(account, to, cx(), finalText, ft); // 长回复 → .md 文件(一条消息)\n } else {\n await sendOrdered(account, to, cx(), finalText, MessageState.FINISH, ft);\n }\n }\n log(`replied to ${to} (msgAge=${msgAge()}): ${(answer || \"\").slice(0, 60)}`);\n activeTurn = null; // 回合结束(计数 budgetOf 不清零,留待用户下条消息重置)\n}\n\n// ===== 解耦:拉取器(poller) 与 处理器(worker) 通过队列协作 =====\n// 大脑(ACP session)严格串行,但「拉取消息/抓 token」无需串行。把两者解耦:\n// poller 持续 getUpdates、实时更新 token、把消息入队,永不被大脑阻塞;\n// worker 串行从队列取消息喂大脑(尊重 kiro-cli 串行约束),发送时取最新 token,规避 ret=-2。\ntype QueuedMsg = {\n from: string;\n text: string;\n imageItems: ImageItem[];\n createTimeMs?: number;\n contextToken?: string;\n};\nconst msgQueue: QueuedMsg[] = [];\nlet wakeWorker: (() => void) | null = null;\nfunction notifyWorker(): void {\n const w = wakeWorker;\n wakeWorker = null;\n if (w) w();\n}\n\n/** 发件人鉴权:账号本人(owner,始终允许,作安全兜底) 或 在 allowFrom 授权清单中。 */\nfunction isAuthorized(from: string, owner: string): boolean {\n return !!from && (from === owner || isAllowed(from));\n}\n\nasync function pollerLoop(account: AccountData, owner: string, state: ServerState): Promise<void> {\n while (true) {\n if (isSessionPaused(account.botId)) {\n await sleep(Math.min(getRemainingPauseMs(account.botId), 60_000) || 60_000);\n continue;\n }\n let resp;\n try {\n resp = await getUpdates({\n baseUrl: account.baseUrl,\n token: account.botToken,\n updatesBuf: state.updatesBuf,\n timeoutMs: 25_000,\n });\n } catch (e) {\n log(\"getUpdates error:\", String(e));\n await sleep(2000);\n continue;\n }\n try {\n if (resp.errcode === SESSION_EXPIRED_ERRCODE) {\n log(\"session expired (-14); pausing 1h. Send a WeChat message to revive after re-login.\");\n pauseSession(account.botId);\n continue;\n }\n if (resp.errcode) {\n log(\"server error:\", resp.errcode, resp.errmsg);\n await sleep(2000);\n continue;\n }\n if (resp.get_updates_buf) state.updatesBuf = resp.get_updates_buf;\n\n const news = (resp.msgs ?? []).filter(\n (m) =>\n m.message_type === MessageType.USER && m.message_id && m.message_id > state.lastMessageId,\n );\n // 先登记本批所有最新 token(worker 发送时取最新)\n for (const m of news) {\n const f = m.from_user_id ?? \"\";\n if (isAuthorized(f, owner))\n log(\n `recv msg id=${m.message_id} ctx=…${m.context_token ? m.context_token.slice(-12) : \"NONE\"} age=${m.create_time_ms ? Math.round((Date.now() - m.create_time_ms) / 1000) + \"s\" : \"?\"}`,\n );\n if (m.context_token) state.contextTokens[`${account.botId}:${f}`] = m.context_token;\n }\n let enq = false;\n for (const msg of news) {\n state.lastMessageId = Math.max(state.lastMessageId, msg.message_id ?? 0);\n const from = msg.from_user_id ?? \"\";\n if (!isAuthorized(from, owner)) {\n log(\"ignore unauthorized sender:\", from);\n continue;\n }\n const text = extractText(msg);\n const voiceItem = (msg.item_list ?? []).find(\n (it) => it.type === MessageItemType.VOICE,\n )?.voice_item;\n if (voiceItem)\n log(\n `recv voice: text=${voiceItem.text ? `\"${voiceItem.text.slice(0, 30)}\"` : \"(空,无微信转写)\"} playtime=${voiceItem.playtime ?? \"?\"}`,\n );\n const imageItems = (msg.item_list ?? [])\n .filter((it) => it.type === MessageItemType.IMAGE && it.image_item)\n .map((it) => it.image_item as ImageItem);\n if (!text && imageItems.length === 0) {\n log(\"skip empty/unsupported message\");\n continue;\n }\n // 服务端按「距用户上一条消息」计连续发送数 → 任何来自 owner 的消息都重置服务端配额,故同步重置该用户计数 + 退出等待段。\n const b = budgetOf(from);\n const wasAwaiting = b.awaiting;\n b.count = 0;\n b.awaiting = false;\n // 「继续」且当前回合正处于「等待继续」段:换用本消息新 token 恢复,且仅作恢复信号、不入队\n if (text.trim() === \"继续\" && activeTurn && activeTurn.from === from && wasAwaiting) {\n activeTurn.token = msg.context_token ?? activeTurn.token;\n log(\"收到「继续」(等待中) → 恢复进度、重置计数、换新 token\");\n continue;\n }\n msgQueue.push({\n from,\n text,\n imageItems,\n createTimeMs: msg.create_time_ms,\n contextToken: msg.context_token,\n });\n enq = true;\n }\n saveState(state);\n if (enq) notifyWorker();\n } catch (e) {\n log(\"poller error:\", e instanceof Error ? e.stack || e.message : String(e));\n }\n }\n}\n\nasync function workerLoop(account: AccountData, state: ServerState): Promise<void> {\n while (true) {\n if (isSessionPaused(account.botId)) {\n await sleep(Math.min(getRemainingPauseMs(account.botId), 30_000) || 30_000);\n continue;\n }\n if (msgQueue.length === 0) {\n await new Promise<void>((r) => {\n wakeWorker = r;\n if (msgQueue.length > 0) {\n wakeWorker = null;\n r();\n }\n });\n continue;\n }\n const item = msgQueue.shift() as QueuedMsg;\n const from = item.from;\n const token = item.contextToken; // 用这条消息自己带的 context_token 回复(每条消息各有各的 token)\n const latestToken = () => state.contextTokens[`${account.botId}:${from}`]; // 该用户最新缓存 token(仅作 -2 参数错误时的兜底)\n\n // 控制指令:单独处理\n const ctrl = item.text ? parseControl(item.text) : null;\n if (ctrl) {\n let r: string;\n try {\n r = await handleControl(ctrl, from);\n } catch (e) {\n r = \"处理指令出错:\" + String(e);\n }\n log(\"control:\", JSON.stringify(ctrl), \"->\", r.replace(/\\n/g, \" | \"));\n await reply(account, from, token, r, MessageState.FINISH, latestToken()).catch((e) =>\n handleSendError(e, account),\n );\n continue;\n }\n\n // ── 任务路由 ──\n // 1) 结束任务 / 明确发给 worker(任务:、>)\n const tcmd = item.text ? parseTaskControl(item.text) : null;\n if (tcmd?.kind === \"stop\") {\n const r = stopTask(from);\n log(\"task: stop ->\", r);\n await reply(account, from, token, r, MessageState.FINISH, latestToken()).catch((e) =>\n handleSendError(e, account),\n );\n continue;\n }\n if (tcmd?.kind === \"toWorker\") {\n // 明确发给 worker:无活跃任务 → 启动;有 → 注入/追加(running 暂存、waiting 喂下一轮)\n if (hasActiveTask(from)) {\n log(\"task: inject(explicit):\", tcmd.text.slice(0, 40));\n injectTask(from, tcmd.text);\n } else {\n const r = dispatchTask(from, tcmd.text);\n await reply(account, from, token, r, MessageState.FINISH, latestToken()).catch((e) =>\n handleSendError(e, account),\n );\n }\n continue;\n }\n // 2) 明确「问大脑」逃逸前缀 → 去前缀后强制走 brain(即使 worker 在等你)\n const brainEscape = item.text ? parseBrainEscape(item.text) : null;\n // 3) 无前缀 + worker 正等你回 + 非图片 → 默认注入 worker;其余(running / 无任务 / 逃逸 / 图片)→ brain\n if (!brainEscape && item.imageItems.length === 0 && isAwaiting(from)) {\n log(\"task: inject(awaiting):\", item.text.slice(0, 40));\n injectTask(from, item.text);\n continue;\n }\n\n const brainText = brainEscape ?? item.text;\n try {\n await handleMessage(\n account,\n token,\n from,\n brainText,\n item.imageItems,\n item.createTimeMs,\n latestToken,\n );\n } catch (e) {\n log(\"handleMessage error:\", e instanceof Error ? e.stack || e.message : String(e));\n await reply(\n account,\n from,\n token,\n \"处理这条消息时出错了,请再发一次试试。\",\n MessageState.FINISH,\n latestToken(),\n ).catch((e2) => handleSendError(e2, account));\n }\n }\n}\n\n// ===== 本地命令入口(文件桥):CLI 写 task-inbox/*.json,daemon 扫描分发到 task-manager =====\nconst TASK_INBOX_DIR = path.join(DATA_DIR, \"task-inbox\");\nconst TASK_STATE_FILE = path.join(DATA_DIR, \"task-state.json\");\nconst TASK_SCAN_MS = Number(process.env.WEMIND_TASK_SCAN_MS) || 1000;\n\n/** 扫描 task-inbox:按文件名(时间戳)升序逐个读、分发到 task-manager(dispatch/reply/stop)、删文件。本地命令默认作用于 owner。 */\nfunction scanTaskInbox(owner: string): void {\n let files: string[];\n try {\n files = fs.readdirSync(TASK_INBOX_DIR).filter((f) => f.endsWith(\".json\"));\n } catch {\n return;\n } // 目录不存在/读失败:跳过\n if (!files.length) return;\n files.sort(); // 文件名带时间戳前缀,字典序即时间序\n for (const f of files) {\n const fp = path.join(TASK_INBOX_DIR, f);\n let cmd: { kind?: string; text?: string; cwd?: string };\n try {\n cmd = JSON.parse(fs.readFileSync(fp, \"utf-8\"));\n } catch {\n try {\n fs.unlinkSync(fp);\n } catch {\n /* ignore */\n }\n continue;\n } // 坏文件丢弃\n try {\n fs.unlinkSync(fp);\n } catch {\n /* ignore */\n } // 先删,避免重复处理\n try {\n if (cmd.kind === \"dispatch\") {\n log(\n \"local task dispatch:\",\n dispatchTask(owner, String(cmd.text ?? \"\"), cmd.cwd ? String(cmd.cwd) : undefined).slice(\n 0,\n 50,\n ),\n );\n } else if (cmd.kind === \"reply\") {\n injectTask(owner, String(cmd.text ?? \"\"));\n log(\"local task reply:\", String(cmd.text ?? \"\").slice(0, 40));\n } else if (cmd.kind === \"stop\") {\n log(\"local task stop:\", stopTask(owner));\n } else {\n log(\"unknown task-inbox cmd:\", cmd.kind);\n }\n } catch (e) {\n log(\"task-inbox dispatch error:\", String(e));\n }\n }\n}\n\nexport async function runDaemon(): Promise<void> {\n // 启动即校验大脑:早失败、说人话——避免「进程看着正常、直到第一条消息才炸」\n // (历史上表现为反复「⏳ 响应中断了,正在自动重试…」,实为默认大脑的二进制没装)。\n const agent = resolveAgent(loadBrainConfig());\n const brainProblem = agentProblem(agent);\n if (brainProblem) {\n log(`FATAL 大脑不可用:${brainProblem}`);\n process.exit(BRAIN_CONFIG_EXIT_CODE); // 看门狗据此停止自动重启(配置错误重拉无益)\n }\n log(\n `brain=${agent.name} bin=${agent.bin} args=[${agent.args.join(\" \")}] shell=${agent.shell} (source: ${agent.source})`,\n );\n const account = loadAccount();\n if (!account?.botToken) {\n log(\"not logged in; run the MCP login_qrcode flow first.\");\n process.exit(1);\n }\n const owner = process.env.WECHAT_OWNER_ID?.trim() || account.userId; // 账号本人:始终授权(安全兜底)\n if (owner) addAllowFrom(owner, DATA_DIR); // 种入授权清单(自动迁移老账号);新登录在 finalize 已配对\n // 写 PID 文件,供管理命令(logout/delete)精确定位并停止本账号的 daemon\n const pidFile = path.join(DATA_DIR, \"daemon.pid\");\n try {\n fs.writeFileSync(pidFile, String(process.pid), \"utf-8\");\n } catch {\n /* best-effort */\n }\n const cleanupPid = () => {\n try {\n fs.unlinkSync(pidFile);\n } catch {\n /* ignore */\n }\n };\n process.on(\"exit\", cleanupPid);\n process.on(\"SIGINT\", () => {\n cleanupPid();\n process.exit(0);\n });\n process.on(\"SIGTERM\", () => {\n cleanupPid();\n process.exit(0);\n });\n // 防御:任何未捕获的 promise 拒绝/异常(多为瞬时网络 fetch failed 或回合内 send 被拒)只记录、不退出,\n // 避免 daemon 崩溃丢掉在途回合、用户卡在心跳。此前的 code=1 崩溃即源于此类未捕获拒绝。\n process.on(\"unhandledRejection\", (reason) => {\n log(\n \"unhandledRejection:\",\n reason instanceof Error ? reason.stack || reason.message : String(reason),\n );\n });\n process.on(\"uncaughtException\", (err) => {\n log(\"uncaughtException:\", err?.stack || String(err));\n });\n ensureBrainHome(); // 准备 brain 沙箱(默认空 MCP)\n ensureWorkerHome(); // 准备 worker 沙箱(继承全量 MCP 除 wechat + 记忆/技能/settings)\n log(`up. bot=${account.botId} owner=${owner} allowFrom=${readAllowFrom(DATA_DIR).length}`);\n await notifyStart({ baseUrl: account.baseUrl, token: account.botToken }).catch(() => {});\n\n const state: ServerState = loadState();\n // worker 任务推送:用该用户最新 token 经串行发送层发出(与 brain 共用 sendOrdered,保证不乱序)\n setTaskPush((to, text) => {\n const tok = state.contextTokens[`${account.botId}:${to}`];\n // task 推送也占用连续发送额度(与 brain 回合共用 budgetOf):task 在后台运行,可能与 brain 回合并发,\n // 若不计数,本地 budget 会低估服务端真实连续发送数 → brain 的 sendProgress 误判「还能发」→ 撞 ret=-2 限流、结果丢失。\n // 注:task 推送是「本轮结果」(必须送达),故只累加计数、不被 awaiting 暂停(暂停只作用于可丢弃的 brain 进度提示)。\n budgetOf(to).count += 1;\n void sendOrdered(account, to, tok, text, MessageState.FINISH, tok);\n });\n // worker 跑一轮期间持续「正在输入中」(typing 不占连续发送上限),避免长任务静默让用户以为卡了\n setTaskTyping((to) => {\n void setTyping(account, to, state.contextTokens[`${account.botId}:${to}`], 1);\n });\n // 任务状态落盘:worker 状态快照原子写 task-state.json,供本地 `manage.js task-status` 读\n setTaskStateSink((snap: TaskStateSnapshot) => {\n try {\n const tmp = `${TASK_STATE_FILE}.tmp-${process.pid}`;\n fs.writeFileSync(tmp, JSON.stringify(snap, null, 2), \"utf-8\");\n fs.renameSync(tmp, TASK_STATE_FILE);\n } catch {\n /* best-effort */\n }\n });\n // 本地命令入口:启动清空收件箱(作废重启前残留命令、防重放),再起独立定时器扫描分发(不挂 poller,避免被 25s 长轮询阻塞)\n try {\n fs.rmSync(TASK_INBOX_DIR, { recursive: true, force: true });\n } catch {\n /* ignore */\n }\n try {\n fs.mkdirSync(TASK_INBOX_DIR, { recursive: true });\n } catch {\n /* ignore */\n }\n setInterval(() => scanTaskInbox(owner), TASK_SCAN_MS);\n // 解耦运行:拉取器持续抓消息(永不被大脑阻塞),处理器串行喂大脑\n void pollerLoop(account, owner, state);\n await workerLoop(account, state);\n}\n\n// daemon 本体由 cli.ts 的 `wemind daemon` 调用 runDaemon() 启动,此处不再顶层自启。\n","// pm2 封装:`wemind start` / `stop` 委托 pm2 做常驻(看门狗 / 开机自启 / 多账号),即「方案 a」。\n// 后续迭代到「方案 b」(自带轻量看门狗、去 pm2 依赖)时替换本模块即可,CLI 其余部分不动。\nimport { spawnSync, type SpawnSyncReturns } from \"node:child_process\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\n/*\n * 统一用「单条命令字符串 + shell」调用 pm2,而不是 spawnSync(cmd, args[], { shell: true })。\n * 后者在 Node 20+ 会触发 DEP0190 弃用警告(shell:true 同时传 args 数组、参数仅拼接不转义)。\n * 这里命令中只拼入受控值:固定的 cliPath(import.meta 解析得到)、账号 key 派生的 name 与日志路径,\n * 且路径都加引号处理空格——无任何外部不可信输入,无注入风险。\n */\nfunction pm2Run(\n cmd: string,\n opts: { quiet?: boolean; env?: NodeJS.ProcessEnv } = {},\n): SpawnSyncReturns<Buffer> {\n return spawnSync(`pm2 ${cmd}`, {\n shell: true,\n stdio: opts.quiet ? \"ignore\" : \"inherit\",\n env: opts.env ?? process.env,\n windowsHide: true, // 避免瞬时闪出控制台窗口\n });\n}\n\n/** pm2 是否已全局安装、可用。 */\nexport function pm2Available(): boolean {\n try {\n return pm2Run(\"-v\", { quiet: true }).status === 0;\n } catch {\n return false;\n }\n}\n\n/** 账号的 daemon 日志路径,与 run-daemon.ps1 / ecosystem 方式一致:`~/.wemind/<key>/daemon.log`。 */\nfunction daemonLogPath(key: string): string {\n const root = process.env.WEMIND_HOME?.trim() || path.join(os.homedir(), \".wemind\");\n return path.join(root, key, \"daemon.log\");\n}\n\n/** 用 pm2 启动某账号的 daemon:app 名 `wemind-<key>`,注入 WECHAT_ACCOUNT,日志归一到账号 daemon.log。 */\nexport function pm2Start(key: string, cliPath: string): { ok: boolean; msg: string } {\n if (!pm2Available()) {\n return {\n ok: false,\n msg: \"未检测到 pm2。请先安装:npm i -g pm2,再重试 `wemind start`。\",\n };\n }\n const name = `wemind-${key}`;\n const log = daemonLogPath(key);\n // 已存在同名进程则先删,确保按最新参数重启\n pm2Run(`delete ${name}`, { quiet: true });\n // stdout/stderr 都导向账号 daemon.log(与非 pm2 方式统一,看日志一个地方),并注入 WECHAT_ACCOUNT。\n const r = pm2Run(\n `start \"${cliPath}\" --name ${name} --interpreter node --output \"${log}\" --error \"${log}\" --merge-logs -- daemon`,\n { env: { ...process.env, WECHAT_ACCOUNT: key } },\n );\n if (r.status !== 0) return { ok: false, msg: `pm2 启动失败(exit=${r.status})。` };\n pm2Run(\"save\", { quiet: true });\n return {\n ok: true,\n msg:\n `已用 pm2 启动 ${name}(日志:${log})。\\n` +\n \" 开机自启(首次):执行 `pm2 startup`——Linux/macOS 按提示操作;Windows 用计划任务跑 `pm2 resurrect`。\",\n };\n}\n\n/** 停止某账号的 daemon(pm2 delete)。 */\nexport function pm2Stop(key: string): { ok: boolean; msg: string } {\n if (!pm2Available()) return { ok: false, msg: \"未检测到 pm2。\" };\n const name = `wemind-${key}`;\n const r = pm2Run(`delete ${name}`, { quiet: true });\n pm2Run(\"save\", { quiet: true });\n return r.status === 0\n ? { ok: true, msg: `已停止 ${name}。` }\n : { ok: false, msg: `${name} 未在运行,或停止失败。` };\n}\n","// 进程身份校验:pid 会复用(尤其**重启后**),只凭「pid 还活着」(process.kill(pid, 0))就杀进程、\n// 或判定「daemon 在跑」,可能**误杀别人的进程**(陈旧 pid 文件 + pid 被复用)或**误判**\n// (自启看似成功、其实压根没起来)。这里改用「命令行里有没有我们自己的特征串」来确认身份。\n//\n// 原则:查不到命令行 → 一律判否(保守,宁可不杀)。\n// 成本:只有 Windows 需要经 PowerShell 查 CIM(约 1~2s),所以调用方**先 isAlive() 过滤**——\n// 正常路径(陈旧 pid 早就不存在了)零开销,只有「确实有活着的 pid」时才付这笔查询成本。\nimport { spawnSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\n/** pid 是否活着(零成本,仅探活;不代表是我们的进程)。 */\nexport function isAlive(pid: number | null | undefined): boolean {\n if (pid === null || pid === undefined || !Number.isInteger(pid) || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/** 归一化:小写 + 反斜杠统一成正斜杠,便于跨平台、跨相对/绝对路径匹配。 */\nfunction normalize(s: string): string {\n return s.replaceAll(\"\\\\\", \"/\").toLowerCase();\n}\n\n/**\n * 取文件名。**不依赖平台**:`/` 与 `\\` 都当分隔符——\n * POSIX 的 `path.basename` 不认 `\\`,会把 Windows 风格路径整条留下(CI 上踩过)。\n */\nfunction baseName(p: string): string {\n const i = Math.max(p.lastIndexOf(\"/\"), p.lastIndexOf(\"\\\\\"));\n return i >= 0 ? p.slice(i + 1) : p;\n}\n\n/** 某条命令行是否命中「任一 marker 组合」(组合内每个 token 都必须出现、大小写与斜杠不敏感)。 */\nexport function matchesMarkers(line: string | null | undefined, markerSets: string[][]): boolean {\n if (!line) return false;\n const hay = normalize(line);\n return markerSets.some((set) => set.length > 0 && set.every((t) => hay.includes(normalize(t))));\n}\n\nfunction parsePipePairs(stdout: string, out: Map<number, string>): void {\n for (const raw of stdout.split(/\\r?\\n/)) {\n const i = raw.indexOf(\"|\");\n if (i <= 0) continue;\n const pid = Number.parseInt(raw.slice(0, i).trim(), 10);\n if (Number.isInteger(pid) && pid > 0) out.set(pid, raw.slice(i + 1));\n }\n}\n\n/**\n * 批量查询 pid → 命令行。查不到(不存在 / 无权限 / 平台不支持)的 pid 不会出现在结果里。\n * 只查询「活着的」pid(调用方传一堆陈旧 pid 也不会白白起进程)。\n */\nexport function commandLines(pids: Array<number | null | undefined>): Map<number, string> {\n const out = new Map<number, string>();\n const list = [...new Set(pids.filter((p): p is number => isAlive(p)))];\n if (!list.length) return out;\n try {\n if (process.platform === \"win32\") {\n // wmic 已被新版 Windows 移除 → 用系统自带的 Windows PowerShell 5.1 + CIM;一次查询多个 pid。\n const ps = path.join(\n process.env.SystemRoot ?? \"C:\\\\Windows\",\n \"System32\",\n \"WindowsPowerShell\",\n \"v1.0\",\n \"powershell.exe\",\n );\n const filter = list.map((p) => `ProcessId=${p}`).join(\" or \");\n const script = `Get-CimInstance Win32_Process -Filter \"${filter}\" | ForEach-Object { \"$($_.ProcessId)|$($_.CommandLine)\" }`;\n const r = spawnSync(ps, [\"-NoProfile\", \"-NonInteractive\", \"-Command\", script], {\n encoding: \"utf8\",\n windowsHide: true,\n timeout: 15000,\n });\n parsePipePairs(r.stdout ?? \"\", out);\n } else if (process.platform === \"linux\") {\n for (const p of list) {\n try {\n out.set(p, fs.readFileSync(`/proc/${p}/cmdline`, \"utf-8\").replaceAll(\"\\0\", \" \").trim());\n } catch {\n /* 读不到就当查不到 */\n }\n }\n } else {\n // darwin 等:用 ps\n for (const p of list) {\n const r = spawnSync(\"ps\", [\"-o\", \"command=\", \"-p\", String(p)], {\n encoding: \"utf8\",\n timeout: 5000,\n });\n const line = (r.stdout ?? \"\").trim();\n if (line) out.set(p, line);\n }\n }\n } catch {\n /* 查询失败 → 返回空(判否,保守) */\n }\n return out;\n}\n\n/** 该 pid 是否「活着**且确实是我们自己的**进程」(命令行命中 markerSets 之一)。 */\nexport function isOurProcess(pid: number | null | undefined, markerSets: string[][]): boolean {\n if (!isAlive(pid)) return false;\n return matchesMarkers(commandLines([pid]).get(pid as number), markerSets);\n}\n\n/**\n * 我们自己的 CLI / wrapper 进程特征:命令行里同时含 `wemind` + `cli.js`。\n *\n * 这串必须同时覆盖**三种**安装方式(少一种 → 对应场景下 stop/list/单实例判断全失效):\n * - 源码仓库:`<node> D:\\repo\\WeMind\\packages\\wechat-daemon\\dist\\cli.js daemon`\n * (由 marker 组合里的 `wemind` 命中仓库名 WeMind;注意大小写不敏感比较在下面已处理);\n * - 全局/本地安装:`<node> …/node_modules/@wemind/cli/dist/cli.js daemon`\n * (npm 的 bin shim 最终 exec 的正是这个路径;Windows 上可能走 `…\\@wemind\\cli\\dist\\cli.js` 短路径。\n * 两种形态都命中 marker 里的 `wemind`——它同时是 scope 名与命令名);\n * - 源码开发期的 PowerShell wrapper:`pwsh -File …\\scripts\\run-daemon.ps1`。\n *\n * 注意**不能**再用 `wechat-daemon`:那是源码目录名,发布到 npm 后包目录叫 `@wemind/cli`,\n * 用目录名做特征会让「装出来的 wemind」永远匹配不上,从而 list 永远显示「已停止」。\n */\nexport const CLI_PID_MARKERS: string[][] = [[\"wemind\", \"cli.js\"], [\"run-daemon.ps1\"]];\n\n/**\n * 大脑进程特征:`<bin> <args…>`(如 `opencode acp`、`kiro-cli acp`)。\n * 取 bin 的**文件名**并去掉扩展名,兼顾 shell=true(命令行是原始命令)与 `--bin` 绝对路径两种情况;\n * args 是关键区分位——用户自己的 `opencode serve`(本机 OpenCode 服务)不含 `acp`,不会被误判。\n * bin 为空(配置异常)时返回空数组 → matchesMarkers 恒判否,绝不误杀。\n */\nexport function brainPidMarkers(bin: string, args: string[]): string[][] {\n const name = baseName(bin ?? \"\").replace(/\\.(exe|cmd|bat|ps1)$/i, \"\");\n if (!name) return []; // bin 为空 / 只剩目录(配置异常)→ 恒判否,绝不误杀\n return [[name, ...args].filter((t) => t !== \"\")];\n}\n","// 自带看门狗(方案 b):`wemind start --runner builtin` 用它常驻,不依赖 pm2。\n// - runWatchdog:看门狗循环本体(由 `cli.js _watchdog` 调用),单实例守卫 + 循环 spawn daemon、崩溃 5s 重拉。\n// - watchdogStart:从 manage 脱离式启动看门狗进程。\n// 单实例用 wrapper.pid + 存活检查(零依赖、复用现有停止句柄);竞态由「start 前先 stop」进一步压低。\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { DATA_DIR, accountDir } from \"@wemind/wechat-ilink-core\";\nimport { BRAIN_CONFIG_EXIT_CODE } from \"./brain-agents.js\";\nimport { CLI_PID_MARKERS, isOurProcess } from \"./proc-identity.js\";\n\nconst RESTART_DELAY_MS = 5000;\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nfunction readPidFile(file: string): number | null {\n try {\n const n = Number.parseInt(fs.readFileSync(file, \"utf-8\").trim(), 10);\n return Number.isInteger(n) && n > 0 ? n : null;\n } catch {\n return null;\n }\n}\n\n/** 单实例守卫:已有存活**且确认是我们**的看门狗返回 false;否则写自己 pid、返回 true。 */\nfunction acquireSingleton(wrapperPidFile: string): boolean {\n const old = readPidFile(wrapperPidFile);\n // 身份校验:陈旧 pid + 重启后 pid 复用时会误判「已有实例」→ 看门狗直接退出、daemon 静默不起。\n if (old && isOurProcess(old, CLI_PID_MARKERS)) return false;\n fs.writeFileSync(wrapperPidFile, String(process.pid), \"ascii\");\n return true;\n}\n\n/** 看门狗循环本体(由 `cli.js _watchdog` 调用,常驻):单实例守卫 → 循环拉起 daemon、退出 5s 重拉。 */\nexport async function runWatchdog(): Promise<void> {\n const dataDir = DATA_DIR; // 本进程已设 WECHAT_ACCOUNT,core 据此解析到账号目录\n fs.mkdirSync(dataDir, { recursive: true });\n const log = path.join(dataDir, \"daemon.log\");\n const wrapperPidFile = path.join(dataDir, \"wrapper.pid\");\n\n if (!acquireSingleton(wrapperPidFile)) {\n fs.appendFileSync(\n log,\n `[wemind/wechat-daemon/wrapper] 已有实例在跑,本次退出。${new Date().toISOString()}\\n`,\n );\n return;\n }\n const cleanup = () => {\n try {\n if (readPidFile(wrapperPidFile) === process.pid) fs.unlinkSync(wrapperPidFile);\n } catch {\n /* ignore */\n }\n };\n process.on(\"exit\", cleanup);\n process.on(\"SIGINT\", () => {\n cleanup();\n process.exit(0);\n });\n process.on(\"SIGTERM\", () => {\n cleanup();\n process.exit(0);\n });\n\n const cliPath = process.argv[1]; // 当前 cli.js(本进程即 `node cli.js _watchdog`)\n for (;;) {\n fs.appendFileSync(\n log,\n `[wemind/wechat-daemon/wrapper] start daemon ${new Date().toISOString()}\\n`,\n );\n const logFd = fs.openSync(log, \"a\");\n const child: ChildProcess = spawn(process.execPath, [cliPath, \"daemon\"], {\n env: process.env, // WECHAT_ACCOUNT 已在本进程环境中\n stdio: [\"ignore\", logFd, logFd], // stdout/stderr 都并入 daemon.log(与 run-daemon.ps1 一致)\n windowsHide: true, // 不弹出控制台窗口(Windows):日志已重定向到 daemon.log,无需可见窗口\n });\n const code = await new Promise<number>((resolve) => {\n child.on(\"exit\", (c) => resolve(c ?? -1));\n child.on(\"error\", () => resolve(-1));\n });\n try {\n fs.closeSync(logFd);\n } catch {\n /* ignore */\n }\n if (code === BRAIN_CONFIG_EXIT_CODE) {\n // 大脑配置错误:重拉无益(需人工修),停在看门狗循环外,避免每 5s 刷一条。\n fs.appendFileSync(\n log,\n `[wemind/wechat-daemon/wrapper] 大脑配置错误(exit ${code}),已停止自动重启。修好后执行:wemind start <账号>\\n`,\n );\n return;\n }\n fs.appendFileSync(\n log,\n `[wemind/wechat-daemon/wrapper] daemon exited (code=${code}), restart in 5s ${new Date().toISOString()}\\n`,\n );\n await sleep(RESTART_DELAY_MS);\n }\n}\n\n/** 从 manage 脱离式启动某账号的看门狗(方案 b:builtin runner)。 */\nexport function watchdogStart(key: string, cliPath: string): { ok: boolean; msg: string } {\n const child = spawn(process.execPath, [cliPath, \"_watchdog\"], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: true,\n env: { ...process.env, WECHAT_ACCOUNT: key },\n });\n child.unref();\n return {\n ok: true,\n msg: `已启动自带看门狗(账号 ${key},看门狗 pid ${child.pid})。日志:${path.join(accountDir(key), \"daemon.log\")}`,\n };\n}\n","import { spawnSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nimport type { ResolvedAgent } from \"./brain-agents.js\";\nimport { spawnAgent } from \"./spawn-agent.js\";\n\n/**\n * 「大脑连通性自检」:真正起一个 ACP agent 进程并跑完 initialize / session/new 握手,\n * 用来证明「这个大脑不只是找得到,而且能说 ACP」——比单纯「文件存在」更硬的证据。\n *\n * 用**临时沙箱目录**(避免污染真实配置与账号沙箱),结束后杀进程、删临时目录。\n * 供 `wemind brain check` 使用;不影响正在运行的 daemon(各起各的进程)。\n */\nexport type CheckResult = { ok: boolean; detail: string; ms: number };\n\nexport async function checkAgent(ag: ResolvedAgent, timeoutMs = 60_000): Promise<CheckResult> {\n const started = Date.now();\n const home = fs.mkdtempSync(path.join(os.tmpdir(), \"wemind-brain-check-\"));\n const env: NodeJS.ProcessEnv = { ...process.env };\n if (ag.homeEnvVar) env[ag.homeEnvVar] = home; // 把「配置目录」指向临时沙箱\n for (const [k, v] of Object.entries(ag.env ?? {})) env[k] = v.replaceAll(\"%HOME%\", home);\n\n // 需要登录的 agent(DSH / Qoder)缺凭据会在 session/new 报认证失败——把凭据带进临时沙箱,\n // 否则自检会把「没登录」误报成「二进制不可用」。标识类文件(如 Qoder 的 machine_id)必须一致。\n const cfgDir = path.join(home, ag.configSubdir);\n for (const f of [...ag.credentialFiles, ...ag.identityFiles]) {\n try {\n const src = path.join(ag.realHome ?? \"\", f);\n if (!ag.realHome || !fs.existsSync(src)) continue;\n fs.mkdirSync(path.dirname(path.join(cfgDir, f)), { recursive: true });\n fs.copyFileSync(src, path.join(cfgDir, f));\n } catch {\n /* best-effort */\n }\n }\n\n const proc = spawnAgent(ag, { cwd: home, env });\n\n let buf = \"\";\n let seq = 0;\n const pending = new Map<number, (frame: any) => void>();\n const settleAll = (msg: string): void => {\n for (const [id, done] of pending) {\n pending.delete(id);\n done({ id, error: { message: msg } });\n }\n };\n\n proc.stdout!.on(\"data\", (d) => {\n buf += d.toString();\n let i: number;\n while ((i = buf.indexOf(\"\\n\")) >= 0) {\n const line = buf.slice(0, i).trim();\n buf = buf.slice(i + 1);\n if (!line) continue;\n let frame: any;\n try {\n frame = JSON.parse(line);\n } catch {\n continue;\n }\n if (typeof frame.id === \"number\" && pending.has(frame.id)) {\n const done = pending.get(frame.id)!;\n pending.delete(frame.id);\n done(frame);\n }\n }\n });\n const stderrTail: string[] = [];\n proc.stderr!.on(\"data\", (d) => {\n stderrTail.push(d.toString());\n if (stderrTail.length > 20) stderrTail.shift();\n });\n proc.on(\"error\", (e) => settleAll(`无法启动:${String(e)}`));\n proc.on(\"exit\", (code) => settleAll(`进程已退出(code=${code})`));\n\n const req = (method: string, params: unknown): Promise<any> =>\n new Promise((resolve, reject) => {\n const id = ++seq;\n const t = setTimeout(() => {\n pending.delete(id);\n reject(new Error(`${method} 超时(${timeoutMs}ms)`));\n }, timeoutMs);\n pending.set(id, (frame) => {\n clearTimeout(t);\n if (frame?.error)\n reject(\n new Error(typeof frame.error === \"string\" ? frame.error : JSON.stringify(frame.error)),\n );\n else resolve(frame?.result);\n });\n proc.stdin!.write(JSON.stringify({ jsonrpc: \"2.0\", id, method, params }) + \"\\n\");\n });\n\n try {\n await req(\"initialize\", { protocolVersion: 1, clientCapabilities: {} });\n const s = (await req(\"session/new\", { cwd: home, mcpServers: [] })) as { sessionId?: string };\n return {\n ok: true,\n detail: `ACP 握手成功(initialize + session/new,sessionId=${s?.sessionId ?? \"?\"})`,\n ms: Date.now() - started,\n };\n } catch (e) {\n const tail = stderrTail.join(\"\").trim().slice(-400);\n return {\n ok: false,\n detail: `${e instanceof Error ? e.message : String(e)}${tail ? `\\n stderr: ${tail}` : \"\"}`,\n ms: Date.now() - started,\n };\n } finally {\n try {\n if (process.platform === \"win32\" && proc.pid)\n spawnSync(\"taskkill\", [\"/PID\", String(proc.pid), \"/T\", \"/F\"], {\n stdio: \"ignore\",\n windowsHide: true,\n });\n else proc.kill();\n } catch {\n /* ignore */\n }\n try {\n fs.rmSync(home, { recursive: true, force: true });\n } catch {\n /* ignore */\n }\n }\n}\n","/**\n * CLI 版本号(`wemind version` / `wemind --version` / help 头部)。\n * 与 package.json 的 version 必须一致——单测 version.test.ts 会读 package.json 校验,\n * 避免「发了新版本、CLI 还报旧版本号」这种排查成本极高的小漂移。\n */\nexport const CLI_VERSION = \"1.1.0\";\n\n/**\n * npm 包名(与 package.json 的 name 一致,单测校验)。\n * 命令名是 `wemind`,但**包名**是 `@wemind/cli`——两者不同,所以 `npx` 不能靠「唯一 bin」自动解析,\n * 必须写全 `npx @wemind/cli`(见 README 与 docs/发布-wemind-npm.md)。\n */\nexport const CLI_PKG_NAME = \"@wemind/cli\";\n\n/** 单行版本串(供 --version 输出):命令名 + 版本,包名放括号里便于反馈问题时对上 npm 页面。 */\nexport function versionLine(): string {\n return `wemind ${CLI_VERSION} (${CLI_PKG_NAME}, node ${process.versions.node}, ${process.platform}-${process.arch})`;\n}\n","#!/usr/bin/env node\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { spawn, spawnSync } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n WEMIND_ROOT,\n accountDir,\n deriveAccountKey,\n listAccounts,\n registerAccount,\n unregisterAccount,\n setLabel,\n getDefaultKey,\n setDefaultKey,\n fetchQRCode,\n pollQRStatus,\n saveQrState,\n clearSessionPause,\n notifyStart,\n notifyStop,\n addAllowFrom,\n removeAllowFrom,\n readAllowFrom,\n type AccountData,\n} from \"@wemind/wechat-ilink-core\";\nimport { pm2Start, pm2Stop } from \"./pm2-runner.js\";\nimport { watchdogStart } from \"./watchdog-runner.js\";\nimport {\n CLI_PID_MARKERS,\n brainPidMarkers,\n commandLines,\n isAlive,\n isOurProcess,\n matchesMarkers,\n} from \"./proc-identity.js\";\nimport {\n ACP_AGENT_PRESETS,\n AUTO_PROBE_ORDER,\n resolveAgent,\n agentProblem,\n presetAvailable,\n type ResolvedAgent,\n} from \"./brain-agents.js\";\nimport {\n loadBrainConfig,\n saveBrainConfig,\n clearBrainConfig,\n brainConfigPath,\n type BrainConfig,\n} from \"./brain-config.js\";\nimport { checkAgent } from \"./brain-check.js\";\nimport { CLI_VERSION, versionLine } from \"./version.js\";\n\n/**\n * WeMind 多账号管理 CLI。\n * 账号 key = 由微信 botId 派生的唯一标识;每个账号独立目录 ~/.wemind/<key>。\n * label = 人类可读别名(可改、可重复);管理命令可用 key 或 label 选择账号。\n * 推荐用 `login` 一条命令完成:自刷新二维码 → 扫码 → 自动归档(→ 可选自动启动)。\n */\n\nconst BASE = process.env.WEMIND_ILINK_BASE?.trim() || \"https://ilinkai.weixin.qq.com\";\nconst STAGING = path.join(WEMIND_ROOT, \".staging\");\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nfunction readAccount(dir: string): AccountData | null {\n try {\n return JSON.parse(fs.readFileSync(path.join(dir, \"account.json\"), \"utf-8\")) as AccountData;\n } catch {\n return null;\n }\n}\nfunction readPid(dir: string, name: string): number | null {\n try {\n const n = parseInt(fs.readFileSync(path.join(dir, name), \"utf-8\").trim(), 10);\n return Number.isFinite(n) ? n : null;\n } catch {\n return null;\n }\n}\n/**\n * 该 pid 是否「活着**且确实是我们自己的** CLI 进程」。\n * 只判存活不够:pid 会复用(尤其重启后),陈旧 pid 文件可能指向**别人的**进程 → 会误杀/误判。\n */\nfunction aliveOurs(pid: number | null, markers: string[][] = CLI_PID_MARKERS): boolean {\n return isOurProcess(pid, markers);\n}\nfunction killPid(pid: number): void {\n if (process.platform === \"win32\")\n spawnSync(\"taskkill\", [\"/PID\", String(pid), \"/T\", \"/F\"], {\n stdio: \"ignore\",\n windowsHide: true, // 避免瞬时闪出控制台窗口\n });\n else {\n try {\n process.kill(pid);\n } catch {\n /* ignore */\n }\n }\n}\nfunction openFile(p: string): void {\n try {\n if (process.platform === \"win32\")\n spawn(\"cmd\", [\"/c\", \"start\", \"\", p], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: true,\n }).unref();\n else\n spawn(process.platform === \"darwin\" ? \"open\" : \"xdg-open\", [p], {\n detached: true,\n stdio: \"ignore\",\n }).unref();\n } catch {\n /* ignore */\n }\n}\n\n/** key 或 label → key(label 唯一匹配才行)。 */\nfunction resolveKey(arg: string): string {\n if (!arg) throw new Error(\"需要 <key 或 label>\");\n const accs = listAccounts();\n if (accs.some((a) => a.key === arg)) return arg;\n const byLabel = accs.filter((a) => a.label === arg);\n if (byLabel.length === 1) return byLabel[0].key;\n if (byLabel.length > 1)\n throw new Error(\n `label「${arg}」对应多个账号,请改用 key:${byLabel.map((a) => a.key).join(\", \")}`,\n );\n throw new Error(`未找到账号:${arg}(既不是 key 也不是 label)`);\n}\n\n/** 启动某账号的 daemon(方案 a:委托 pm2 常驻,跑 `wemind daemon`,注入 WECHAT_ACCOUNT)。 */\nfunction startDaemon(key: string, runner?: string): void {\n // 用 process.argv[1](即正在执行的 cli.js 绝对路径),而不是 dirname(import.meta.url):\n // 发布后 tsdown 把 manage.ts 内联进 dist/cli.js,两者恰好相同;但若将来拆成多 chunk,\n // argv[1] 才是「用户装的那个 bin」,dirname(import.meta.url) 可能是 dist/chunks/ 下。\n const cliPath =\n process.argv[1] || path.join(path.dirname(fileURLToPath(import.meta.url)), \"cli.js\");\n // start 前先停现有任何 runner(pm2 app / builtin 看门狗),避免同账号双跑抢 token\n stopDaemon(key);\n const useRunner = (runner || process.env.WEMIND_RUNNER || \"builtin\").toLowerCase();\n console.log((useRunner === \"pm2\" ? pm2Start(key, cliPath) : watchdogStart(key, cliPath)).msg);\n}\n\n/** 停止某账号的常驻:先 pm2 delete(方案 a),再按 pid 兜底杀 wrapper/daemon/brain(兼容直接启动的)。 */\nfunction stopDaemon(key: string): string {\n pm2Stop(key); // best-effort:停掉 pm2 托管的 app(若有)\n const dir = accountDir(key);\n const ag = resolveAgent(loadBrainConfig(dir));\n const cand = [\n { pid: readPid(dir, \"wrapper.pid\"), name: \"wrapper\", markers: CLI_PID_MARKERS },\n { pid: readPid(dir, \"daemon.pid\"), name: \"daemon\", markers: CLI_PID_MARKERS },\n { pid: readPid(dir, \"brain.pid\"), name: \"brain\", markers: brainPidMarkers(ag.bin, ag.args) },\n ];\n // 只杀「活着**且身份确认**」的:pid 会被复用,陈旧 pid 文件可能指向别人的进程 → 绝不误杀。\n // 命令行一次批量查(Windows 下较慢);正常情况(陈旧 pid 早已不存在)根本不会发起查询。\n const lines = commandLines(cand.map((c) => c.pid));\n const stopped: string[] = [];\n const skipped: string[] = [];\n for (const c of cand) {\n if (!c.pid || !isAlive(c.pid)) continue;\n if (matchesMarkers(lines.get(c.pid), c.markers)) {\n killPid(c.pid);\n stopped.push(`${c.name} ${c.pid}`);\n } else {\n skipped.push(`${c.name} ${c.pid}`); // 活着的 pid 但不是我们的(pid 复用)→ 不杀\n }\n }\n for (const f of [\"wrapper.pid\", \"daemon.pid\", \"brain.pid\"]) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n const parts: string[] = [];\n if (stopped.length) parts.push(`已停止 ${stopped.join(\" + \")}`);\n if (skipped.length)\n parts.push(`跳过 ${skipped.join(\" + \")}(非本程序,未杀;已清陈旧 pid 文件)`);\n return parts.length ? parts.join(\";\") : \"无运行中的进程\";\n}\n\n/** 把 STAGING 的登录按 botId 归档 + 登记。 */\nfunction finalizeStaging(label?: string): { key: string; msg: string } {\n const acc = readAccount(STAGING);\n if (!acc?.botId) throw new Error(`staging 无有效登录(${STAGING})`);\n const key = deriveAccountKey(acc.botId);\n const dest = accountDir(key);\n if (fs.existsSync(dest)) {\n fs.rmSync(STAGING, { recursive: true, force: true });\n throw new Error(`账号目录已存在: ${dest}(该微信号此前已登录过;如需重登请先 delete ${key})`);\n }\n fs.renameSync(STAGING, dest);\n for (const f of [\n \"qrcode.png\",\n \"qrcode.txt\",\n \"qr_login.json\",\n \"poll.out\",\n \"poll.err\",\n \"auto.out\",\n \"auto.err\",\n ]) {\n try {\n fs.unlinkSync(path.join(dest, f));\n } catch {\n /* ignore */\n }\n }\n const meta = registerAccount({\n key,\n botId: acc.botId,\n userId: acc.userId,\n baseUrl: acc.baseUrl,\n label: label || key,\n });\n if (acc.userId) addAllowFrom(acc.userId, dest); // 配对:扫码登录者自动授权\n return { key, msg: `已归档:key=${key} label=${meta.label} botId=${acc.botId}\\n目录:${dest}` };\n}\n\n/**\n * 懒加载 `qrcode`:只有扫码登录要画二维码,`list` / `brain` / `task` / `daemon` 全用不上。\n * 顶层 `import` 会让**每次**启动都付约 36ms(实测)解析 qrcode + yargs 依赖树;\n * 这里在第一次画码时才加载,之后同进程内复用(登录过程会因过期刷新多次)。\n * 用函数声明而非 `const QRCode = await import(...)`:后者在 ESM 顶层要多一层 TDZ 心智负担。\n */\nlet qrcodeMod: typeof import(\"qrcode\") | undefined;\n\nasync function loadQrCode(): Promise<typeof import(\"qrcode\")> {\n if (!qrcodeMod) {\n try {\n qrcodeMod = await import(\"qrcode\");\n } catch (e) {\n throw new Error(\n `无法加载二维码库 qrcode(依赖缺失或安装损坏):${e instanceof Error ? e.message : String(e)}\\n` +\n \"请重试安装:npm i -g @wemind/cli\",\n );\n }\n }\n return qrcodeMod;\n}\n\n/** 一条命令完成扫码登录:自刷新二维码 + 终端码 + 自动开图 + 自动归档(→ 可选自动启动)。 */\nasync function cmdLogin(\n label: string | undefined,\n autoStart: boolean,\n runner?: string,\n): Promise<void> {\n const QRCode = await loadQrCode(); // 只有登录路径才加载 qrcode\n fs.rmSync(STAGING, { recursive: true, force: true });\n fs.mkdirSync(STAGING, { recursive: true });\n const png = path.join(STAGING, \"qrcode.png\");\n const overall = Date.now() + 15 * 60 * 1000;\n let code = \"\";\n let opened = false;\n\n const refresh = async (): Promise<void> => {\n const qr = await fetchQRCode(BASE);\n code = qr.qrcode;\n saveQrState({ qrcode: qr.qrcode, qrcodeUrl: qr.qrcode_img_content, createdAt: Date.now() });\n await QRCode.toFile(png, qr.qrcode_img_content, { width: 480, margin: 2 });\n const term = await QRCode.toString(qr.qrcode_img_content, { type: \"terminal\", small: true });\n console.log(\"\\n用要登录的微信扫下面的二维码(过期会自动刷新):\\n\");\n console.log(term);\n console.log(\"图片(已自动打开): \" + png);\n console.log(\"链接: \" + qr.qrcode_img_content + \"\\n\");\n if (!opened) {\n openFile(png);\n opened = true;\n }\n };\n\n await refresh();\n console.log(\"等待扫码确认…(最多 15 分钟,Ctrl+C 取消)\");\n while (Date.now() < overall) {\n let s;\n try {\n s = await pollQRStatus(BASE, code);\n } catch {\n await sleep(2000);\n continue;\n }\n if (s.status === \"confirmed\") {\n if (!s.bot_token || !s.ilink_bot_id || !s.ilink_user_id)\n throw new Error(\"confirmed 但数据不全\");\n fs.writeFileSync(\n path.join(STAGING, \"account.json\"),\n JSON.stringify(\n {\n botToken: s.bot_token,\n botId: s.ilink_bot_id,\n userId: s.ilink_user_id,\n baseUrl: s.baseurl ?? BASE,\n savedAt: Date.now(),\n },\n null,\n 2,\n ),\n \"utf-8\",\n );\n clearSessionPause(s.ilink_bot_id);\n await notifyStart({ baseUrl: s.baseurl ?? BASE, token: s.bot_token }).catch(() => {});\n for (const f of [\"qrcode.png\", \"qr_login.json\"]) {\n try {\n fs.unlinkSync(path.join(STAGING, f));\n } catch {\n /* ignore */\n }\n }\n console.log(\"\\n登录成功,归档中…\");\n const { key, msg } = finalizeStaging(label);\n console.log(msg);\n if (autoStart) {\n startDaemon(key, runner);\n console.log(`已启动 daemon(账号 ${key})。`);\n } else {\n console.log(`启动:wemind start ${key}`);\n }\n return;\n }\n if (s.status === \"expired\") {\n console.log(\"二维码过期,自动刷新…\");\n await refresh();\n continue;\n }\n await sleep(1500);\n }\n throw new Error(\"超时(15 分钟)未完成登录\");\n}\n\nfunction cmdList(): string {\n const def = getDefaultKey();\n const accs = listAccounts();\n if (!accs.length) return \"(无已登记账号)。用 `login` 添加。\";\n // 先收集各账号的 pid + 大脑特征,再**一次性**批量查命令行(Windows 下每次查询较慢);\n // 只凭「pid 活着」显示状态会在重启后 pid 复用时骗人,所以要带身份校验。\n const entries = accs.map((a) => {\n const dir = accountDir(a.key);\n const ag = resolveAgent(loadBrainConfig(dir));\n return {\n a,\n dir,\n dpid: readPid(dir, \"daemon.pid\"),\n bpid: readPid(dir, \"brain.pid\"),\n brainMarkers: brainPidMarkers(ag.bin, ag.args),\n };\n });\n const lines = commandLines(entries.flatMap((e) => [e.dpid, e.bpid]));\n const aliveOursIn = (pid: number | null, markers: string[][]): boolean =>\n !!pid && matchesMarkers(lines.get(pid), markers);\n return entries\n .map((e) => {\n const running = aliveOursIn(e.dpid, CLI_PID_MARKERS) ? `运行中(pid ${e.dpid})` : \"已停止\";\n const brainPid = aliveOursIn(e.bpid, e.brainMarkers) ? ` brain(pid ${e.bpid})` : \"\";\n return `- ${e.a.key}${e.a.key === def ? \" [默认]\" : \"\"} label=${e.a.label} botId=${e.a.botId} agent=${agentLabel(e.dir)} ${running}${brainPid}`;\n })\n .join(\"\\n\");\n}\n\n// ===== 大脑(ACP agent)选择:账号级持久配置 brain.json,见 brain-config.ts =====\nconst BRAIN_SRC_LABEL: Record<ResolvedAgent[\"source\"], string> = {\n env: \"环境变量 WEMIND_BRAIN*\",\n file: \"账号 brain.json\",\n auto: \"自动探测(未配置)\",\n};\n\n/** list 用:账号「配置 / 自动探测」得到的大脑标签(不看当前 shell 的 env,避免误导)。 */\nfunction agentLabel(dir: string): string {\n const cfg = loadBrainConfig(dir);\n if (cfg?.agent && cfg.agent.toLowerCase() !== \"auto\") return cfg.agent;\n const picked = AUTO_PROBE_ORDER.find((k) => presetAvailable(k));\n return picked ? `${picked}(自动)` : \"(无可用)\";\n}\n\n/** `wemind brain`(show):打印某账号大脑的「配置」与「生效」情况。 */\nfunction describeBrain(key: string): string {\n const dir = accountDir(key);\n const cfg = loadBrainConfig(dir);\n const ag = resolveAgent(cfg);\n const problem = agentProblem(ag);\n const cfgLine = cfg\n ? `brain.json → agent=${cfg.agent}${cfg.bin ? ` bin=${cfg.bin}` : \"\"}${cfg.args ? ` args=[${cfg.args.join(\" \")}]` : \"\"}${cfg.shell !== undefined ? ` shell=${cfg.shell}` : \"\"}`\n : \"(未设置 brain.json)\";\n return [\n `账号 ${key}`,\n ` 配置:${cfgLine}`,\n ` 请求:${ag.requestedKey} 来源:${BRAIN_SRC_LABEL[ag.source]}`,\n ` 生效:name=${ag.name} bin=${ag.bin} args=[${ag.args.join(\" \")}] shell=${ag.shell} home_env=${ag.homeEnvVar ?? \"(无)\"}`,\n ` 可用:${problem ? `❌ ${problem}` : `✅ ${ag.binPath ?? ag.bin}`}`,\n ` 预设:${Object.keys(ACP_AGENT_PRESETS).join(\"、\")}`,\n ` 自动探测顺序:${AUTO_PROBE_ORDER.join(\" → \")}`,\n ].join(\"\\n\");\n}\n\n/** `wemind brain set <预设|auto>`:把选择按账号落盘(可选 --bin/--args/--shell;--restart 立即重启 daemon)。 */\nfunction cmdBrainSet(\n agentName: string,\n key: string,\n opts: { bin?: string; args?: string; shell?: boolean; restart: boolean },\n): string {\n const agent = agentName.trim().toLowerCase();\n if (agent !== \"auto\" && !ACP_AGENT_PRESETS[agent])\n throw new Error(\n `未知预设「${agentName}」。可选:auto、${Object.keys(ACP_AGENT_PRESETS).join(\"、\")}(自定义二进制用 --bin 搭配任一预设)`,\n );\n const cfg: BrainConfig = { agent };\n if (opts.bin) cfg.bin = opts.bin;\n if (opts.args !== undefined) cfg.args = opts.args.trim() ? opts.args.trim().split(/\\s+/) : [];\n if (opts.shell !== undefined) cfg.shell = opts.shell;\n const dir = accountDir(key);\n saveBrainConfig(cfg, dir);\n const lines = [\n `已设置 ${key} 的大脑:agent=${agent}${cfg.bin ? ` bin=${cfg.bin}` : \"\"}${cfg.args ? ` args=[${cfg.args.join(\" \")}]` : \"\"}`,\n `配置文件:${brainConfigPath(dir)}`,\n ];\n if (opts.restart) {\n const dpid = readPid(dir, \"daemon.pid\");\n if (aliveOurs(dpid)) {\n startDaemon(key);\n lines.push(`已重启 daemon(账号 ${key})。`);\n } else {\n lines.push(`daemon 未在运行,未重启(用 wemind start ${key} 启动)。`);\n }\n } else {\n lines.push(`(daemon 需重启才生效:wemind start ${key})`);\n }\n return lines.join(\"\\n\");\n}\n\n/** `wemind brain unset`:清除账号级配置,回退到自动探测/默认。 */\nfunction cmdBrainUnset(key: string): string {\n const ok = clearBrainConfig(accountDir(key));\n return ok\n ? `${key} 已清除 brain.json(回退到自动探测/默认)。daemon 需重启才生效。`\n : `${key} 未设置 brain.json(无需清除)。`;\n}\n\n/** `wemind brain check`:真起一个进程跑 ACP 握手,验证大脑「不仅找得到、而且能说 ACP」。 */\nasync function cmdBrainCheck(key: string): Promise<string> {\n const ag = resolveAgent(loadBrainConfig(accountDir(key)));\n const problem = agentProblem(ag);\n if (problem) return `❌ ${key} 的大脑不可用:${problem}`;\n const r = await checkAgent(ag);\n return `${r.ok ? \"✅\" : \"❌\"} ${key} 大脑自检(${ag.name},bin=${ag.bin}):${r.detail}(${r.ms}ms)`;\n}\n\n/** brain 子命令派发:show(默认)/ set / unset / check。 */\nasync function cmdBrain(rest: string[]): Promise<string> {\n const argv = [...rest];\n const takeFlag = (name: string): string | undefined => {\n const i = argv.indexOf(name);\n if (i < 0) return undefined;\n const v = argv[i + 1];\n argv.splice(i, 2);\n return v;\n };\n const bin = takeFlag(\"--bin\");\n const argsOverride = takeFlag(\"--args\");\n const restart = argv.includes(\"--restart\");\n if (restart) argv.splice(argv.indexOf(\"--restart\"), 1);\n const shell = argv.includes(\"--shell\") ? true : argv.includes(\"--no-shell\") ? false : undefined;\n for (const f of [\"--shell\", \"--no-shell\"]) {\n const i = argv.indexOf(f);\n if (i >= 0) argv.splice(i, 1);\n }\n const pos = argv.filter((x) => x && !x.startsWith(\"--\"));\n const first = (pos[0] || \"\").toLowerCase();\n const SUBCOMMANDS = new Set([\"show\", \"set\", \"unset\", \"check\"]);\n if (first && !SUBCOMMANDS.has(first)) {\n // 省略子命令 → 视为 `brain show <key|label>`(如 `wemind brain 主号`)。\n return describeBrain(pickKey(pos[0]));\n }\n const sub = first || \"show\";\n switch (sub) {\n case \"show\":\n return describeBrain(pickKey(pos[1]));\n case \"set\": {\n if (!pos[1])\n throw new Error(\n '用法: brain set <预设|auto> [--bin X] [--args \"…\"] [--shell|--no-shell] [--restart] [<key|label>]',\n );\n return cmdBrainSet(pos[1], pickKey(pos[2]), { bin, args: argsOverride, shell, restart });\n }\n case \"unset\":\n return cmdBrainUnset(pickKey(pos[1]));\n case \"check\":\n return await cmdBrainCheck(pickKey(pos[1]));\n default:\n throw new Error(`未知 brain 子命令「${sub}」(可用:show / set / unset / check)`);\n }\n}\n\nfunction cmdAdopt(key: string, label?: string): string {\n if (!key) throw new Error(\"用法: adopt <key> [--label 名]\");\n const acc = readAccount(accountDir(key));\n if (!acc?.botId) throw new Error(`${accountDir(key)} 无有效 account.json`);\n const meta = registerAccount({\n key,\n botId: acc.botId,\n userId: acc.userId,\n baseUrl: acc.baseUrl,\n label: label || key,\n });\n if (acc.userId) addAllowFrom(acc.userId, accountDir(key)); // 接管时把账号本人种入授权清单\n return `已登记:key=${key} label=${meta.label} botId=${acc.botId}`;\n}\n\nasync function cmdLogout(sel: string): Promise<string> {\n const key = resolveKey(sel);\n const dir = accountDir(key);\n const acc = readAccount(dir);\n const stopMsg = stopDaemon(key);\n if (acc?.botToken) {\n try {\n await notifyStop({ baseUrl: acc.baseUrl, token: acc.botToken });\n } catch {\n /* ignore */\n }\n }\n for (const f of [\"account.json\", \"state.json\", \"qr_login.json\", \"qrcode.png\", \"qrcode.txt\"]) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n return `账号 ${key} 已登出(${stopMsg})。目录与会话保留,可重新登录。`;\n}\n\nfunction cmdDelete(sel: string): string {\n const key = resolveKey(sel);\n const stopMsg = stopDaemon(key);\n try {\n fs.rmSync(accountDir(key), { recursive: true, force: true });\n } catch (e) {\n throw new Error(`删除目录失败: ${String(e)}`);\n }\n unregisterAccount(key);\n return `账号 ${key} 已删除(${stopMsg},已移除目录与注册)。`;\n}\n\nfunction cmdStart(sel: string, runner?: string): string {\n const key = resolveKey(sel);\n startDaemon(key, runner);\n return `已启动 daemon(账号 ${key})。用 list 查看运行状态。`;\n}\n\n// ===== 本地任务命令(文件桥):写 task-inbox/*.json,由 daemon 扫描执行;状态读 task-state.json =====\nfunction taskInboxDir(key: string): string {\n return path.join(accountDir(key), \"task-inbox\");\n}\n/** 原子写一个命令文件到收件箱(临时文件 + rename,避免 daemon 读到半截)。 */\nfunction writeTaskCmd(key: string, obj: Record<string, unknown>): void {\n const dir = taskInboxDir(key);\n fs.mkdirSync(dir, { recursive: true });\n const fp = path.join(dir, `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);\n const tmp = `${fp}.tmp`;\n fs.writeFileSync(tmp, JSON.stringify({ ...obj, ts: Date.now() }, null, 2), \"utf-8\");\n fs.renameSync(tmp, fp);\n}\n/** 选账号:显式 key/label 优先,否则用默认账号。 */\nfunction pickKey(sel?: string): string {\n if (sel) return resolveKey(sel);\n const d = getDefaultKey();\n if (!d) throw new Error(\"无默认账号,请指定 <key|label> 或先 login\");\n return d;\n}\n/** 预检 daemon 存活(本地命令需 daemon 在跑才有人消费收件箱)。 */\nfunction requireDaemonAlive(key: string): void {\n const pid = readPid(accountDir(key), \"daemon.pid\");\n if (!aliveOurs(pid))\n throw new Error(\n `账号 ${key} 的 daemon 未运行。先启动:wemind start ${key}(用 wemind list 查看状态)`,\n );\n}\n\nasync function cmdTask(text: string, cwd: string | undefined, sel?: string): Promise<string> {\n if (!text) throw new Error('用法: task \"<任务描述>\" [--cwd 目录] [<key|label>]');\n const key = pickKey(sel);\n requireDaemonAlive(key);\n const useCwd = cwd || process.cwd();\n writeTaskCmd(key, { kind: \"dispatch\", text, cwd: useCwd });\n // 短轮询 task-state.json 确认已派发(最多 ~3s)\n const stateFile = path.join(accountDir(key), \"task-state.json\");\n for (let i = 0; i < 15; i++) {\n await sleep(200);\n try {\n if (JSON.parse(fs.readFileSync(stateFile, \"utf-8\")).active)\n return `已派发任务(worker 运行中,cwd=${useCwd})。\\n用 task-status 看进展、task-reply 回复、task-stop 结束。`;\n } catch {\n /* ignore */\n }\n }\n return `已投递任务(cwd=${useCwd})。daemon 处理中,用 task-status 查看。`;\n}\nfunction cmdTaskReply(text: string, sel?: string): string {\n if (!text) throw new Error('用法: task-reply \"<内容>\" [<key|label>]');\n const key = pickKey(sel);\n requireDaemonAlive(key);\n writeTaskCmd(key, { kind: \"reply\", text });\n return `已发送给任务:${text.slice(0, 40)}`;\n}\nfunction cmdTaskStop(sel?: string): string {\n const key = pickKey(sel);\n requireDaemonAlive(key);\n writeTaskCmd(key, { kind: \"stop\" });\n return \"已发送「结束任务」指令。\";\n}\nfunction cmdTaskStatus(sel?: string): string {\n const key = pickKey(sel);\n const stateFile = path.join(accountDir(key), \"task-state.json\");\n let s: {\n active?: boolean;\n status?: string;\n turns?: number;\n startedAt?: number;\n lastOutput?: string;\n };\n try {\n s = JSON.parse(fs.readFileSync(stateFile, \"utf-8\"));\n } catch {\n return `账号 ${key}:无任务状态(task-state.json 不存在,daemon 可能未跑过任务)。`;\n }\n if (!s.active) return `账号 ${key}:当前无活跃任务(idle)。`;\n const since = s.startedAt ? `${Math.round((Date.now() - s.startedAt) / 1000)}s` : \"?\";\n return [\n `账号 ${key} 任务状态:`,\n ` 状态:${s.status} 轮次:${s.turns ?? 0} 已运行:${since}`,\n ` 最近输出:${s.lastOutput ? s.lastOutput.slice(0, 300) : \"(无)\"}`,\n ].join(\"\\n\");\n}\n\nexport async function runManage(argv: string[]): Promise<void> {\n const cmd = argv.shift();\n const rest = [...argv];\n const li = rest.indexOf(\"--label\");\n let label: string | undefined;\n if (li >= 0) {\n label = rest[li + 1];\n rest.splice(li, 2);\n }\n const si = rest.indexOf(\"--start\");\n const autoStart = si >= 0;\n if (autoStart) rest.splice(si, 1);\n const ci = rest.indexOf(\"--cwd\");\n let cwd: string | undefined;\n if (ci >= 0) {\n cwd = rest[ci + 1];\n rest.splice(ci, 2);\n }\n const ri = rest.indexOf(\"--runner\");\n let runner: string | undefined;\n if (ri >= 0) {\n runner = rest[ri + 1];\n rest.splice(ri, 2);\n }\n\n switch (cmd) {\n case \"version\":\n case \"--version\":\n case \"-v\":\n console.log(versionLine());\n break;\n case \"login\":\n await cmdLogin(label, autoStart, runner);\n break;\n case \"list\":\n console.log(cmdList());\n break;\n case \"brain\":\n console.log(await cmdBrain(rest));\n break;\n case \"adopt\":\n console.log(cmdAdopt(rest[0], label));\n break;\n case \"start\":\n console.log(cmdStart(rest[0], runner));\n break;\n case \"stop\": {\n const k = resolveKey(rest[0]);\n console.log(stopDaemon(k));\n break;\n }\n case \"task\":\n console.log(await cmdTask(rest[0], cwd, rest[1]));\n break;\n case \"task-reply\":\n console.log(cmdTaskReply(rest[0], rest[1]));\n break;\n case \"task-status\":\n console.log(cmdTaskStatus(rest[0]));\n break;\n case \"task-stop\":\n console.log(cmdTaskStop(rest[0]));\n break;\n case \"label\": {\n const k = resolveKey(rest[0]);\n setLabel(k, rest.slice(1).join(\" \"));\n console.log(`已设置 ${k} 的别名为「${rest.slice(1).join(\" \")}」`);\n break;\n }\n case \"set-default\": {\n const k = resolveKey(rest[0]);\n setDefaultKey(k);\n console.log(`默认账号 = ${k}`);\n break;\n }\n case \"logout\":\n console.log(await cmdLogout(rest[0]));\n break;\n case \"delete\":\n console.log(cmdDelete(rest[0]));\n break;\n case \"finalize\": {\n const { msg } = finalizeStaging(label);\n console.log(msg);\n break;\n } // 兼容旧流程(staging 已有登录时归档)\n case \"allow\": {\n const k = resolveKey(rest[0]);\n const uid = rest[1];\n if (!uid) throw new Error(\"用法: allow <key|label> <userId>\");\n const list = addAllowFrom(uid, accountDir(k));\n console.log(`已授权 ${uid} → ${k}\\n当前清单(${list.length}):${list.join(\", \")}`);\n break;\n }\n case \"revoke\": {\n const k = resolveKey(rest[0]);\n const uid = rest[1];\n if (!uid) throw new Error(\"用法: revoke <key|label> <userId>\");\n const list = removeAllowFrom(uid, accountDir(k));\n console.log(`已撤销 ${uid} → ${k}\\n当前清单(${list.length}):${list.join(\", \") || \"(空)\"}`);\n break;\n }\n case \"allowlist\": {\n const k = resolveKey(rest[0]);\n const list = readAllowFrom(accountDir(k));\n console.log(\n `${k} 授权用户(${list.length}):\\n${list.map((u) => \" - \" + u).join(\"\\n\") || \" (空)\"}`,\n );\n break;\n }\n default:\n console.log(`WeMind 多账号管理(wemind v${CLI_VERSION})\n用法: wemind <command> 其余:wemind version | help\n\n login [--label 名] [--start] 一条命令完成扫码登录:自刷新二维码 + 自动归档(--start 顺带启动 daemon)\n list 列出账号、别名、运行状态\n start <key|label> 启动某账号的 daemon\n stop <key|label> 停止某账号的 daemon\n brain [show|set|unset|check] [<key|label>] 查看/切换/清除/自检账号的大脑(ACP agent)\n set: brain set <预设|auto> [--bin X] [--args \"…\"] [--restart] [<key|label>]\n 大脑选择按账号持久化在 brain.json;环境变量 WEMIND_BRAIN* 为一次性覆盖\n task \"<描述>\" [--cwd 目录] [<key|label>] 本地派发任务给 worker(默认 cwd=当前终端目录)\n task-reply \"<内容>\" [<key|label>] 回复/注入当前任务(「task-reply 继续」= 续跑)\n task-status [<key|label>] 查看当前任务状态与最近输出\n task-stop [<key|label>] 结束当前任务\n logout <key|label> 停 daemon + 通知下线 + 清凭证(保留目录,可重登)\n delete <key|label> 停 daemon + 删除目录 + 移除注册\n label <key|label> <新别名> 改别名\n set-default <key|label> 设默认账号\n adopt <key> [--label 名] 把已存在的账号目录登记进注册表\n allowlist <key|label> 查看该账号的授权用户清单(allowFrom)\n allow <key|label> <userId> 授权某用户可驱动该账号 bot\n revoke <key|label> <userId> 撤销某用户的授权\n\n说明:key = 由微信 botId 派生的唯一标识(目录名);label = 人类可读别名。多数命令可用 key 或 label。`);\n }\n}\n// 入口由 cli.ts 调用 runManage(argv);此处不再顶层自启。\n","#!/usr/bin/env node\n/**\n * wemind —— 完整应用的命令行入口(bin)。\n * - 子命令 `daemon`:跑 daemon 本体(长轮询 + 大脑 + worker),通常由 pm2 调用,用户不直接用。\n * - 其余子命令(login/list/start/stop/task…):交给 manage 派发。\n */\nimport { runDaemon } from \"./index.js\";\nimport { runManage } from \"./manage.js\";\nimport { runWatchdog } from \"./watchdog-runner.js\";\n\nconst argv = process.argv.slice(2);\n\nif (argv[0] === \"daemon\") {\n runDaemon().catch((err: unknown) => {\n console.error(\"[wemind/wechat-daemon] fatal:\", err);\n process.exit(1);\n });\n} else if (argv[0] === \"_watchdog\") {\n runWatchdog().catch((err: unknown) => {\n console.error(\"[wemind/wechat-daemon/watchdog] fatal:\", err);\n process.exit(1);\n });\n} else {\n runManage(argv).catch((e: unknown) => {\n console.error(\"error:\", e instanceof Error ? e.message : e);\n process.exit(1);\n });\n}\n"],"mappings":"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"}
|