@xdxer/dingtalk-agent 0.1.4-beta.8 → 0.1.4

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.
Files changed (254) hide show
  1. package/CHANGELOG.md +233 -0
  2. package/README.en.md +167 -0
  3. package/README.md +101 -248
  4. package/dist/bin/dingtalk-agent.js +1255 -25
  5. package/dist/bin/dingtalk-agent.js.map +1 -1
  6. package/dist/src/actions.js +98 -14
  7. package/dist/src/actions.js.map +1 -1
  8. package/dist/src/agent-audit.js +1385 -0
  9. package/dist/src/agent-audit.js.map +1 -0
  10. package/dist/src/agent-bindings.js +132 -0
  11. package/dist/src/agent-bindings.js.map +1 -0
  12. package/dist/src/agent-definition.js +182 -0
  13. package/dist/src/agent-definition.js.map +1 -0
  14. package/dist/src/agent-enhance.js +711 -0
  15. package/dist/src/agent-enhance.js.map +1 -0
  16. package/dist/src/agent-platform.js +299 -0
  17. package/dist/src/agent-platform.js.map +1 -0
  18. package/dist/src/bootstrap.js +125 -17
  19. package/dist/src/bootstrap.js.map +1 -1
  20. package/dist/src/config.js +1 -7
  21. package/dist/src/config.js.map +1 -1
  22. package/dist/src/development-workspace.js +755 -0
  23. package/dist/src/development-workspace.js.map +1 -0
  24. package/dist/src/doctor.js +74 -15
  25. package/dist/src/doctor.js.map +1 -1
  26. package/dist/src/dws.js +145 -0
  27. package/dist/src/dws.js.map +1 -1
  28. package/dist/src/eval-evidence.js +193 -0
  29. package/dist/src/eval-evidence.js.map +1 -0
  30. package/dist/src/host-detect.js +146 -0
  31. package/dist/src/host-detect.js.map +1 -0
  32. package/dist/src/instruction-path.js +270 -0
  33. package/dist/src/instruction-path.js.map +1 -0
  34. package/dist/src/invocation.js +36 -0
  35. package/dist/src/invocation.js.map +1 -0
  36. package/dist/src/lab.js +679 -0
  37. package/dist/src/lab.js.map +1 -0
  38. package/dist/src/lease.js +100 -0
  39. package/dist/src/lease.js.map +1 -0
  40. package/dist/src/map.js +157 -0
  41. package/dist/src/map.js.map +1 -0
  42. package/dist/src/memory/candidates.js +451 -0
  43. package/dist/src/memory/candidates.js.map +1 -0
  44. package/dist/src/memory/completion-evidence.js +536 -0
  45. package/dist/src/memory/completion-evidence.js.map +1 -0
  46. package/dist/src/memory/operational.js +263 -0
  47. package/dist/src/memory/operational.js.map +1 -0
  48. package/dist/src/memory/remote-state.js +478 -0
  49. package/dist/src/memory/remote-state.js.map +1 -0
  50. package/dist/src/memory/task-checkpoints.js +204 -0
  51. package/dist/src/memory/task-checkpoints.js.map +1 -0
  52. package/dist/src/multica-deploy.js +1538 -0
  53. package/dist/src/multica-deploy.js.map +1 -0
  54. package/dist/src/multica-provider.js +685 -0
  55. package/dist/src/multica-provider.js.map +1 -0
  56. package/dist/src/opencode-evals.js +1547 -0
  57. package/dist/src/opencode-evals.js.map +1 -0
  58. package/dist/src/opencode-isolation.js +124 -0
  59. package/dist/src/opencode-isolation.js.map +1 -0
  60. package/dist/src/opencode-provider.js +532 -0
  61. package/dist/src/opencode-provider.js.map +1 -0
  62. package/dist/src/opencode-workspace.js +208 -0
  63. package/dist/src/opencode-workspace.js.map +1 -0
  64. package/dist/src/perception.js +225 -0
  65. package/dist/src/perception.js.map +1 -0
  66. package/dist/src/personal-event-evals.js +595 -0
  67. package/dist/src/personal-event-evals.js.map +1 -0
  68. package/dist/src/promotion.js +786 -0
  69. package/dist/src/promotion.js.map +1 -0
  70. package/dist/src/remote-semantic-state-live-evals.js +888 -0
  71. package/dist/src/remote-semantic-state-live-evals.js.map +1 -0
  72. package/dist/src/remote-semantic-state-worker.js +38 -0
  73. package/dist/src/remote-semantic-state-worker.js.map +1 -0
  74. package/dist/src/remote-state-evals.js +502 -0
  75. package/dist/src/remote-state-evals.js.map +1 -0
  76. package/dist/src/response-gate.js +51 -0
  77. package/dist/src/response-gate.js.map +1 -0
  78. package/dist/src/robot-evals.js +771 -0
  79. package/dist/src/robot-evals.js.map +1 -0
  80. package/dist/src/sessions.js +66 -105
  81. package/dist/src/sessions.js.map +1 -1
  82. package/dist/src/setup.js +6 -5
  83. package/dist/src/setup.js.map +1 -1
  84. package/dist/src/skill-manager.js +162 -24
  85. package/dist/src/skill-manager.js.map +1 -1
  86. package/dist/src/skills.js +2 -1
  87. package/dist/src/skills.js.map +1 -1
  88. package/dist/src/storage-evals.js +26 -0
  89. package/dist/src/storage-evals.js.map +1 -0
  90. package/dist/src/types.js.map +1 -1
  91. package/dist/src/upgrade.js +23 -27
  92. package/dist/src/upgrade.js.map +1 -1
  93. package/dist/src/version.js +73 -0
  94. package/dist/src/version.js.map +1 -0
  95. package/dist/src/waits.js +5 -1
  96. package/dist/src/waits.js.map +1 -1
  97. package/dist/src/workspace.js +28 -3
  98. package/dist/src/workspace.js.map +1 -1
  99. package/docs/INSTALLATION.md +50 -3
  100. package/docs/SECOND-AGENT-ACCEPTANCE.md +62 -0
  101. package/docs/architecture/agent-memory-topology.png +0 -0
  102. package/docs/architecture/agent-memory-topology.svg +132 -0
  103. package/docs/architecture/general-agent-kernel-topology.png +0 -0
  104. package/docs/architecture/general-agent-kernel-topology.svg +149 -0
  105. package/docs/architecture/provider-bound-development-workspace.png +0 -0
  106. package/docs/architecture/provider-bound-development-workspace.svg +141 -0
  107. package/docs/architecture/task-completion-gate.png +0 -0
  108. package/docs/architecture/task-completion-gate.svg +191 -0
  109. package/docs/assets/agent-delivery-lifecycle.svg +103 -0
  110. package/docs/schemas/agent-audit-load-evidence.schema.json +14 -0
  111. package/docs/schemas/agent-audit.schema.json +92 -0
  112. package/docs/schemas/agent-bindings.schema.json +54 -0
  113. package/docs/schemas/agent-definition.schema.json +78 -0
  114. package/docs/schemas/agent-enhancement-plan.schema.json +88 -0
  115. package/docs/schemas/agent-enhancement-receipt.schema.json +37 -0
  116. package/docs/schemas/agent-platform.schema.json +13 -0
  117. package/docs/schemas/enriched-invocation.schema.json +46 -0
  118. package/docs/schemas/eval-candidate-plan.schema.json +28 -0
  119. package/docs/schemas/eval-candidate-result.schema.json +20 -0
  120. package/docs/schemas/eval-candidate.schema.json +30 -0
  121. package/docs/schemas/invocation.schema.json +19 -0
  122. package/docs/schemas/memory-candidate-proposal.schema.json +18 -0
  123. package/docs/schemas/memory-candidate.schema.json +76 -0
  124. package/docs/schemas/memory-publish-target.schema.json +25 -0
  125. package/docs/schemas/multica-deployment-list.schema.json +29 -0
  126. package/docs/schemas/multica-deployment-operation.schema.json +51 -0
  127. package/docs/schemas/multica-deployment-plan.schema.json +70 -0
  128. package/docs/schemas/multica-deployment-receipt.schema.json +87 -0
  129. package/docs/schemas/multica-deployment-status.schema.json +23 -0
  130. package/docs/schemas/multica-workspace-inspection.schema.json +77 -0
  131. package/docs/schemas/multica-workspace-plan.schema.json +68 -0
  132. package/docs/schemas/multica-workspace-resource-list.schema.json +27 -0
  133. package/docs/schemas/multica-workspace-status.schema.json +34 -0
  134. package/docs/schemas/observation.schema.json +21 -0
  135. package/docs/schemas/operational-memory-provider.schema.json +36 -0
  136. package/docs/schemas/operational-memory-record.schema.json +24 -0
  137. package/docs/schemas/perception-input.schema.json +56 -0
  138. package/docs/schemas/project.schema.json +115 -0
  139. package/docs/schemas/promotion-list.schema.json +36 -0
  140. package/docs/schemas/promotion-plan.schema.json +60 -0
  141. package/docs/schemas/promotion-policy.schema.json +29 -0
  142. package/docs/schemas/promotion-receipt.schema.json +43 -0
  143. package/docs/schemas/promotion-status.schema.json +23 -0
  144. package/docs/schemas/release-readiness.schema.json +66 -0
  145. package/docs/schemas/remote-semantic-state-live-eval.schema.json +60 -0
  146. package/docs/schemas/remote-semantic-state-manifest.schema.json +79 -0
  147. package/docs/schemas/remote-semantic-state-provider.schema.json +98 -0
  148. package/docs/schemas/response-gate.schema.json +20 -0
  149. package/docs/schemas/task-checkpoint.schema.json +71 -0
  150. package/docs/schemas/task-completion-evidence.schema.json +154 -0
  151. package/docs/schemas/workspace-doctor.schema.json +56 -0
  152. package/docs/schemas/workspace-state.schema.json +39 -0
  153. package/evals/README.md +17 -0
  154. package/evals/baselines/2026-07-16/opencode-basic-010-completion-summary.json +123 -0
  155. package/evals/baselines/2026-07-16/opencode-basic-skill-required-summary.json +69 -0
  156. package/evals/baselines/2026-07-16/opencode-multi-surface-summary.json +63 -0
  157. package/evals/baselines/2026-07-16/remote-state-live-summary.json +70 -0
  158. package/evals/baselines/2026-07-17/agent-enhance-opencode-dogfood-summary.json +98 -0
  159. package/evals/baselines/2026-07-17/personal-event-live-readiness-summary.json +68 -0
  160. package/examples/agents/fde-coach/AGENTS.md +26 -0
  161. package/examples/agents/fde-coach/MEMORY.md +3 -0
  162. package/examples/agents/fde-coach/fields/default/field.json +24 -0
  163. package/examples/agents/fde-coach/knowledge/INDEX.md +4 -0
  164. package/examples/agents/fde-coach/skills/fde-coach/SKILL.md +13 -0
  165. package/examples/agents/release-manager/AGENTS.md +26 -0
  166. package/examples/agents/release-manager/MEMORY.md +3 -0
  167. package/examples/agents/release-manager/fields/default/field.json +24 -0
  168. package/examples/agents/release-manager/knowledge/INDEX.md +4 -0
  169. package/examples/agents/release-manager/skills/release-manager/SKILL.md +13 -0
  170. package/lab/README.md +109 -0
  171. package/lab/agent-eval/catalog.json +91 -0
  172. package/lab/agent-eval/classic-failures.json +177 -0
  173. package/lab/agent-eval/completion-gate-regression.json +99 -0
  174. package/lab/agent-eval/personal-event-live.example.json +94 -0
  175. package/lab/agent-eval/remote-semantic-state-live.example.json +70 -0
  176. package/lab/agent-eval/remote-semantic-state-provider.fixture.json +47 -0
  177. package/lab/agent-eval/remote-state-workspace/AGENTS.md +8 -0
  178. package/lab/agent-eval/remote-state-workspace/opencode.json +7 -0
  179. package/lab/agent-eval/remote-state-workspace/skills/remote-state-operator/SKILL.md +13 -0
  180. package/lab/agent-eval/remote-state.example.json +31 -0
  181. package/lab/agent-eval/workspace/AGENTS.md +7 -0
  182. package/lab/agent-eval/workspace/MEMORY.md +4 -0
  183. package/lab/agent-eval/workspace/artifacts/pending-review.md +3 -0
  184. package/lab/agent-eval/workspace/knowledge/INDEX.md +4 -0
  185. package/lab/agent-eval/workspace/opencode.json +20 -0
  186. package/lab/manifest.example.json +27 -0
  187. package/lab/manifest.personal-event.example.json +27 -0
  188. package/lab/project-workspace/README.md +11 -0
  189. package/lab/project-workspace/fake-multica-provider.mjs +277 -0
  190. package/lab/project-workspace/multica-deploy.fixture.json +29 -0
  191. package/lab/project-workspace/multica-readonly.fixture.json +69 -0
  192. package/lab/project-workspace/observation.fixture.json +14 -0
  193. package/lab/project-workspace/opencode-provider-suite.json +65 -0
  194. package/lab/project-workspace/project.fixture.json +44 -0
  195. package/lab/project-workspace/promotion-policy.fixture.json +15 -0
  196. package/lab/robot-eval/pool.example.json +30 -0
  197. package/lab/robot-eval/suite.json +123 -0
  198. package/lab/robot-eval/workspace/AGENTS.md +21 -0
  199. package/lab/robot-eval/workspace/MEMORY.md +3 -0
  200. package/lab/robot-eval/workspace/knowledge/INDEX.md +5 -0
  201. package/lab/robot-eval/workspace/opencode.json +22 -0
  202. package/lab/schemas/agent-eval-catalog.schema.json +47 -0
  203. package/lab/schemas/lab-manifest.schema.json +70 -0
  204. package/lab/schemas/personal-event-eval.schema.json +91 -0
  205. package/lab/schemas/remote-state-eval.schema.json +66 -0
  206. package/lab/schemas/robot-eval-suite.schema.json +184 -0
  207. package/lab/schemas/robot-pool.schema.json +56 -0
  208. package/package.json +28 -9
  209. package/skills/README.md +23 -0
  210. package/skills/core/dingtalk-agent-compose/SKILL.md +151 -0
  211. package/skills/core/dingtalk-agent-compose/assets/AGENTS.template.md +35 -0
  212. package/skills/core/dingtalk-agent-compose/assets/agent.bindings.dingtalk-doc.template.json +13 -0
  213. package/skills/core/dingtalk-agent-compose/assets/agent.bindings.local.template.json +13 -0
  214. package/skills/core/dingtalk-agent-compose/assets/hosts/opencode/opencode.template.json +12 -0
  215. package/skills/core/dingtalk-agent-compose/assets/role-skill.template.md +32 -0
  216. package/skills/core/dingtalk-agent-compose/evals/evals.json +129 -0
  217. package/skills/core/dingtalk-agent-compose/references/agent-definition-contract.md +55 -0
  218. package/skills/core/dingtalk-agent-compose/references/host-loading-contract.md +58 -0
  219. package/skills/core/dingtalk-agent-compose/references/hosts/claude-code.md +48 -0
  220. package/skills/core/dingtalk-agent-compose/references/hosts/opencode.md +77 -0
  221. package/skills/core/dingtalk-agent-compose/references/storage-routing.md +20 -0
  222. package/skills/core/dingtalk-agent-eval/SKILL.md +140 -0
  223. package/skills/core/dingtalk-agent-eval/assets/eval-catalog.template.json +18 -0
  224. package/skills/core/dingtalk-agent-eval/evals/evals.json +83 -0
  225. package/skills/core/dingtalk-agent-eval/references/eval-topology.md +48 -0
  226. package/skills/core/dingtalk-agent-eval/references/evidence-contract.md +52 -0
  227. package/skills/core/dingtalk-agent-eval/references/failure-to-case.md +35 -0
  228. package/skills/core/dingtalk-agent-eval/references/interactive-debug-channels.md +93 -0
  229. package/skills/core/dingtalk-agent-eval/references/local-connector-smoke.md +75 -0
  230. package/skills/core/dingtalk-agent-eval/references/scenario-taxonomy.md +25 -0
  231. package/skills/core/dingtalk-agent-eval/references/storage-modes.md +75 -0
  232. package/skills/core/dingtalk-basic-behavior/SKILL.md +87 -0
  233. package/skills/core/dingtalk-basic-behavior/assets/memory-candidate-proposal.json +10 -0
  234. package/skills/{dingtalk-basic-behavior/assets/task-checkpoint.md → core/dingtalk-basic-behavior/assets/task-checkpoint.json} +2 -21
  235. package/skills/{dingtalk-basic-behavior → core/dingtalk-basic-behavior}/references/action-contract.md +2 -0
  236. package/skills/core/dingtalk-basic-behavior/references/memory-and-evolution.md +53 -0
  237. package/skills/core/dingtalk-basic-behavior/references/perception-and-gates.md +28 -0
  238. package/skills/core/dingtalk-basic-behavior/references/risk-authority-and-privacy.md +62 -0
  239. package/skills/{dingtalk-basic-behavior → core/dingtalk-basic-behavior}/references/task-lifecycle.md +47 -10
  240. package/skills/core/dingtalk-basic-behavior/references/truth-and-recovery.md +65 -0
  241. package/skills/platforms/deap/PLATFORM.md +3 -0
  242. package/skills/platforms/deap/README.md +3 -0
  243. package/skills/platforms/multica-dingtalk/PLATFORM.md +40 -0
  244. package/skills/platforms/multica-dingtalk/dingtalk-agent-boot-multica/SKILL.md +40 -0
  245. package/skills/platforms/multica-dingtalk/dingtalk-agent-deploy-multica/SKILL.md +60 -0
  246. package/skills/platforms/multica-dingtalk/dingtalk-agent-deploy-multica/references/multica-deployment-contract.md +49 -0
  247. package/skills/platforms/multica-dingtalk/dingtalk-agent-deploy-multica/references/promotion-observation-contract.md +49 -0
  248. package/skills/platforms/multica-dingtalk/multica-external/SKILL.md +282 -0
  249. package/skills/platforms/multica-dingtalk/multica-external/scripts/bootstrap.sh +78 -0
  250. package/skills/platforms/multica-dingtalk/multica-external/scripts/multica_ext.py +1180 -0
  251. package/skills/dingtalk-basic-behavior/SKILL.md +0 -86
  252. package/skills/dingtalk-basic-behavior/references/memory-and-evolution.md +0 -27
  253. /package/skills/{dingtalk-basic-behavior → core/dingtalk-basic-behavior}/references/event-to-behavior.md +0 -0
  254. /package/skills/{dingtalk-basic-behavior → core/dingtalk-basic-behavior}/references/runtime-modes.md +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"dws.js","sourceRoot":"","sources":["../../src/dws.ts"],"names":[],"mappings":"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","sourcesContent":["// dws 硬化包装 —— 这一层是「拦」(TypeScript source)。\n//\n// **上面的每个模块都必须走它,不许自己 spawn dws。**\n//\n// ════════════════════════════════════════════════════════════════\n// 【六条工程纪律】—— 每一条都是踩出来的,违反必出错,而且是【静默】出错\n// ════════════════════════════════════════════════════════════════\n//\n// 1. **拿不到就响亮地死,绝不当成\"是空的\"。**\n// 不同接口把数据藏在不同层级。读错层级 → 拿到 undefined → 当成\"表是空的\"\n// → **一整天的数据可以这么无声丢掉,而且不报错。**\n// 「查到 0 条」和「读错地方了」**必须分得开**。分不开就 die。\n//\n// 2. **分页会静默截断。** 默认页大小有上限,超了就悄悄少给你,\n// **而且不一定有信号告诉你被截断了。**\n// **拿不到分页信号 ≠ 没有下一页,那叫「没有信号」。**\n// **半张表算出来的结果是错的,不是\"差不多\"。**\n//\n// 3. **创建类:报错了也可能其实建成功了。**\n// **绝不看返回信封** —— 按名回查 + 内容回读定成败。\n// 推论:**创建类永不盲重试**(会造重复件),失败先按幂等键回查。\n//\n// 4. **写操作报错 ≠ 失败。** 回读实态才定成败。返回 success 也不算验收。\n//\n// 5. **字段读写不对称。** 写进去的形状 ≠ 读回来的形状。**写完必回读。**\n//\n// 6. **markdown 会被重排版。重排版不是漂移。**\n// 判漂移必须剥掉 markdown 符号只看文字流 —— 用行级 diff 会每次都误报,\n// **然后你就会开始无脑 --force,真漂移也一起覆盖掉。**\n//\n// 一句话:**所有这些坑的形状都一样 —— 它不报错,它给你一个看起来很正常的答案。**\n// 这一层做的就是:**把「看起来正常」和「真的正常」分开。**\n\nimport { spawnSync } from 'node:child_process'\n\nexport class Blocked extends Error {}\n\nexport function die(msg): never {\n console.error('[FAIL] ' + msg)\n throw new Blocked(msg)\n}\n\nconst sleep = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms)\n\n/** 跑一条 dws。限流退避;超时不当成\"空\";坏响应响亮地死。 */\nexport function run(args, { allowFail = false, timeout = 180_000, retries = 2 } = {}) {\n let last = ''\n for (let attempt = 0; attempt <= retries; attempt++) {\n const p = spawnSync('dws', [...args, '--format', 'json'],\n { encoding: 'utf8', timeout, maxBuffer: 64 * 1024 * 1024 })\n if (p.error) {\n if ((p.error as NodeJS.ErrnoException).code === 'ENOENT') {\n if (allowFail) return null\n die('dws 跑不起来 —— 先 export PATH=\"$HOME/.local/bin:$PATH\"')\n }\n last = String(p.error); sleep(2000 * (attempt + 1)); continue\n }\n const out = p.stdout || p.stderr || ''\n last = out.slice(0, 300)\n const i = out.indexOf('{')\n if (i < 0) { sleep(2000 * (attempt + 1)); continue }\n let d\n try { d = JSON.parse(out.slice(i, lastBrace(out) + 1)) }\n catch { sleep(2000 * (attempt + 1)); continue }\n const err = d.error || {}\n if (err && (err.code || err.message)) {\n const s = JSON.stringify(err).toLowerCase()\n if (s.includes('limit') || s.includes('frequen') || s.includes('flow')) {\n sleep(3000 * (attempt + 1)); continue // 限流 → 退避\n }\n if (allowFail) return null\n die(`dws 失败 ${args.slice(0, 4).join(' ')}\\n ${JSON.stringify(err).slice(0, 300)}`)\n }\n if (p.status !== 0) {\n if (allowFail) return null\n die(`dws 以退出码 ${p.status} 结束,但没有结构化 error: ${args.slice(0, 4).join(' ')}\\n ${last}`)\n }\n return d\n }\n if (allowFail) return null\n die(`dws 无响应(重试 ${retries} 次) ${args.slice(0, 4).join(' ')}\\n ${last}`)\n}\n\nfunction lastBrace(s) {\n // dws 有时在 JSON 后面粘一行 WARN / INFO —— 从最后一个 } 截断\n for (let i = s.length - 1; i >= 0; i--) if (s[i] === '}') return i\n return s.length - 1\n}\n\n// ──────────────── 三个层级,各读各的 ────────────────\nconst inner = (d) => (d && typeof d.data === 'object' && d.data !== null ? d.data : d)\n\n/** 列表类查询的记录。拿不到就 die —— 绝不当成\"表是空的\"。 */\nexport function records(d) {\n const x = inner(d)\n if (!('records' in x)) {\n die(`返回里没有 records(顶层键=${Object.keys(d)}) —— 接口形状可能变了。` +\n `**绝不把它当成【表是空的】。**`)\n }\n if (x.hasMore || x.partial) {\n die('分页未收敛 —— **部分结果不许当完整结果用。** 半张表算出来的是错的。')\n }\n return x.records || []\n}\n\n/** 文档正文。**层级和列表类查询不一样** —— 两种形状都兜住,拿不到就 die。 */\nexport function markdown(d) {\n const md = inner(d).markdown ?? d.markdown\n if (md === undefined || md === null) {\n die(`拿不到文档正文(顶层键=${Object.keys(d)}) —— **绝不当成【这篇是空的】。**`)\n }\n return md\n}\n\n/** 目录节点。 */\nexport const nodes = (d) => inner(d).nodes || []\n\n/**\n * 待办翻页 —— **这是唯一有分页信号的读法。**\n *\n * ⚠️ **不要把页开大**。超过这个大小,分页信号会消失,\n * 你拿到 undefined、当成 false,然后**静默漏掉剩下的**。\n * 心跳的收件靠它 —— 漏了循环件就是「静默收工且不自知」。\n */\nexport function todosPage(status = 'false', page = 1) {\n const d = run(['todo', 'task', 'list', '--status', status, '--size', '20', '--page', String(page)])\n const r = d.result || {}\n if (!('todoCards' in r)) {\n die(`待办返回里没有 todoCards(result 键=${Object.keys(r)}) —— 绝不当成【没有待办】。`)\n }\n return { cards: r.todoCards || [], more: Boolean(r.hasMore) }\n}\n\n/** 把待办【拉全】。只看第一页会漏掉循环件。 */\nexport function todosAll(status = 'false', maxPages = 20) {\n const out: any[] = []\n for (let page = 1; page <= maxPages; page++) {\n const { cards, more } = todosPage(status, page)\n out.push(...cards)\n if (!more) return out\n }\n die(`待办翻页超过 ${maxPages} 页还没收敛 —— 不许把部分结果当完整。`)\n}\n\n// ──────────────── 创建类:报错也可能成功 → 按名回查 ────────────────\n/** 目录列表可能把扩展名单列,比对时要拼回去。 */\nexport function findChild(folder, name) {\n for (const n of nodes(run(['doc', 'list', '--folder', folder, '--page-size', '50']))) {\n const nm = n.name || '', ext = n.extension || ''\n if (nm === name || (ext && `${nm}.${ext}` === name)) return n.nodeId\n }\n return null\n}\n\n/** 建文档。**不看返回信封** —— 一律按名回查定成败。 */\nexport function createDoc(folder, name, file) {\n const exist = findChild(folder, name)\n if (exist) return exist\n run(['doc', 'create', '--name', name, '--folder', folder,\n '--content-file', file, '--content-format', 'markdown', '--yes'],\n { allowFail: true }) // ← **报错也可能其实成功了**\n sleep(1200)\n const nid = findChild(folder, name)\n if (!nid) die(`建不出文档 ${name} —— 别把它当成【建成功了】。`)\n return nid\n}\n\nexport const updateDoc = (node, file) =>\n run(['doc', 'update', '--node', node, '--content-file', file,\n '--content-format', 'markdown', '--mode', 'overwrite', '--yes'])\n\nexport const readDoc = (node, profile = '') => markdown(run([\n 'doc', 'read', '--node', node, ...profileArgs(profile),\n]))\nexport const docInfo = (node, profile = '') => inner(run([\n 'doc', 'info', '--node', node, ...profileArgs(profile),\n]))\n\n// ──────────────── 漂移判定:重排版【不是】漂移 ────────────────\n/**\n * 剥掉 markdown 语法和转义装饰,只留【文字本身】。\n *\n * 钉钉会重排版你推上去的 markdown。**重排版不是漂移。**\n * 用行级 diff 判漂移 → 每次 sync 都误报 →\n * **然后你就会开始无脑 --force,真正的漂移也一起覆盖掉了。**\n */\nexport function words(s) {\n let t = String(s ?? '')\n for (let i = 0; i < 3; i++) t = unescapeHtml(t) // 可能被多次转义\n t = t.replace(/&#(\\d+);/g, (_, n) => String.fromCharCode(+n))\n t = t.replace(/\\[打开\\]\\([^)]*\\)/g, '') // 钉钉自动加的链接装饰\n return t.replace(/[`*_>#|\\\\~\\-\\s]+/g, '')\n}\n\nfunction unescapeHtml(s) {\n return s.replace(/&(amp|lt|gt|quot|#39);/g, (m, e) =>\n ({ amp: '&', lt: '<', gt: '>', quot: '\"', '#39': \"'\" })[e])\n}\n\nexport const same = (a, b) => words(a) === words(b)\n\n// ──────────────── 数字员工的消息原子动作(typed,目标不可自由拼装) ────────────────\nfunction profileArgs(profile) {\n return profile ? ['--profile', profile] : []\n}\n\nfunction assertReplyTarget(target) {\n const missing: string[] = []\n if (!target?.conversationId) missing.push('conversationId')\n if (!target?.messageId) missing.push('messageId')\n if (!target?.senderOpenDingTalkId) missing.push('senderOpenDingTalkId')\n if (missing.length) die(`固定回复目标缺少 ${missing.join(' / ')}`)\n}\n\n/** ack 没有服务端 UUID,因此绝不自动重试。 */\nexport function ackMessage(target, { emoji = '收到', profile = '' } = {}) {\n assertReplyTarget(target)\n return run([\n 'chat', 'message', 'add-emoji',\n '--conversation-id', target.conversationId,\n '--msg-id', target.messageId,\n '--emoji', emoji,\n ...profileArgs(profile),\n ], { retries: 0 })\n}\n\n/** reply/ask 共用同一传输;uuid 在调用前已持久化,重试也必须复用。 */\nexport function replyMessage(\n target,\n text,\n { uuid = '', profile = '' }: { uuid?: string; profile?: string } = {},\n) {\n assertReplyTarget(target)\n if (!String(text || '').trim()) die('回复正文不能为空')\n if (!uuid) die('回复前必须先持久化 uuid 幂等键')\n return run([\n 'chat', 'message', 'reply',\n '--conversation-id', target.conversationId,\n '--ref-msg-id', target.messageId,\n '--ref-sender', target.senderOpenDingTalkId,\n '--text', text,\n '--uuid', uuid,\n '--ai-tag=true',\n ...profileArgs(profile),\n '--yes',\n ], { retries: 1 })\n}\n\nexport function readMessageEmotions(messageId, profile = '') {\n return run([\n 'chat', 'message', 'list-emotion-replies', '--msg-ids', messageId,\n ...profileArgs(profile),\n ], { allowFail: true, retries: 0 })\n}\n\nexport function querySendStatus(openTaskId, profile = '') {\n return run([\n 'chat', 'message', 'query-send-status', '--open-task-id', openTaskId,\n ...profileArgs(profile),\n ], { allowFail: true, retries: 1 })\n}\n\nexport function readMessagesByIds(messageId, profile = '') {\n return run([\n 'chat', 'message', 'list-by-ids', '--msg-ids', messageId,\n ...profileArgs(profile),\n ], { allowFail: true, retries: 1 })\n}\n\n/** 外发前核对 profile 对应的真实执行身份。 */\nexport function getSelf(profile = '') {\n return run([\n 'contact', 'user', 'get-self', ...profileArgs(profile),\n ], { retries: 1 })\n}\n"]}
1
+ {"version":3,"file":"dws.js","sourceRoot":"","sources":["../../src/dws.ts"],"names":[],"mappings":"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","sourcesContent":["// dws 硬化包装 —— 这一层是「拦」(TypeScript source)。\n//\n// **上面的每个模块都必须走它,不许自己 spawn dws。**\n//\n// ════════════════════════════════════════════════════════════════\n// 【六条工程纪律】—— 每一条都是踩出来的,违反必出错,而且是【静默】出错\n// ════════════════════════════════════════════════════════════════\n//\n// 1. **拿不到就响亮地死,绝不当成\"是空的\"。**\n// 不同接口把数据藏在不同层级。读错层级 → 拿到 undefined → 当成\"表是空的\"\n// → **一整天的数据可以这么无声丢掉,而且不报错。**\n// 「查到 0 条」和「读错地方了」**必须分得开**。分不开就 die。\n//\n// 2. **分页会静默截断。** 默认页大小有上限,超了就悄悄少给你,\n// **而且不一定有信号告诉你被截断了。**\n// **拿不到分页信号 ≠ 没有下一页,那叫「没有信号」。**\n// **半张表算出来的结果是错的,不是\"差不多\"。**\n//\n// 3. **创建类:报错了也可能其实建成功了。**\n// **绝不看返回信封** —— 按名回查 + 内容回读定成败。\n// 推论:**创建类永不盲重试**(会造重复件),失败先按幂等键回查。\n//\n// 4. **写操作报错 ≠ 失败。** 回读实态才定成败。返回 success 也不算验收。\n//\n// 5. **字段读写不对称。** 写进去的形状 ≠ 读回来的形状。**写完必回读。**\n//\n// 6. **markdown 会被重排版。重排版不是漂移。**\n// 判漂移必须剥掉 markdown 符号只看文字流 —— 用行级 diff 会每次都误报,\n// **然后你就会开始无脑 --force,真漂移也一起覆盖掉。**\n//\n// 一句话:**所有这些坑的形状都一样 —— 它不报错,它给你一个看起来很正常的答案。**\n// 这一层做的就是:**把「看起来正常」和「真的正常」分开。**\n\nimport { spawnSync } from 'node:child_process'\n\nexport class Blocked extends Error {}\n\nexport function die(msg): never {\n console.error('[FAIL] ' + msg)\n throw new Blocked(msg)\n}\n\nconst sleep = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms)\n\n/** 跑一条 dws。限流退避;超时不当成\"空\";坏响应响亮地死。 */\nexport function run(args, { allowFail = false, timeout = 180_000, retries = 2 } = {}) {\n let last = ''\n for (let attempt = 0; attempt <= retries; attempt++) {\n const p = spawnSync('dws', [...args, '--format', 'json'],\n { encoding: 'utf8', timeout, maxBuffer: 64 * 1024 * 1024 })\n if (p.error) {\n if ((p.error as NodeJS.ErrnoException).code === 'ENOENT') {\n if (allowFail) return null\n die('dws 跑不起来 —— 先 export PATH=\"$HOME/.local/bin:$PATH\"')\n }\n last = String(p.error); sleep(2000 * (attempt + 1)); continue\n }\n const out = p.stdout || p.stderr || ''\n last = out.slice(0, 300)\n const i = out.indexOf('{')\n if (i < 0) { sleep(2000 * (attempt + 1)); continue }\n let d\n try { d = JSON.parse(out.slice(i, lastBrace(out) + 1)) }\n catch { sleep(2000 * (attempt + 1)); continue }\n const err = d.error || {}\n if (err && (err.code || err.message)) {\n const s = JSON.stringify(err).toLowerCase()\n if (s.includes('limit') || s.includes('frequen') || s.includes('flow')) {\n sleep(3000 * (attempt + 1)); continue // 限流 → 退避\n }\n if (allowFail) return null\n die(`dws 失败 ${args.slice(0, 4).join(' ')}\\n ${JSON.stringify(err).slice(0, 300)}`)\n }\n if (p.status !== 0) {\n if (allowFail) return null\n die(`dws 以退出码 ${p.status} 结束,但没有结构化 error: ${args.slice(0, 4).join(' ')}\\n ${last}`)\n }\n return d\n }\n if (allowFail) return null\n die(`dws 无响应(重试 ${retries} 次) ${args.slice(0, 4).join(' ')}\\n ${last}`)\n}\n\n/**\n * 启动 dev connect daemon。\n *\n * dws 当前即使收到 `--format json`,成功时仍可能只输出:\n * `connect daemon started (pid=..., log=...)`。这不是通用 JSON 契约,只有这个\n * 已知启动动作允许接受;健康与否仍必须由调用方随后执行 status 回读确认。\n */\nexport function startConnectDaemon(\n args: string[],\n { timeout = 60_000 } = {},\n): Record<string, any> {\n if (args[0] !== 'dev' || args[1] !== 'connect' || !args.includes('--daemon')) {\n die('startConnectDaemon 只允许执行 dev connect --daemon')\n }\n if (!args.includes('--unified-app-id') && !args.includes('--robot-client-id')) {\n die('dev connect daemon 必须固定 unified-app-id 或 robot-client-id')\n }\n if (args.includes('--robot-client-secret')) {\n die('dev connect daemon 禁止通过命令行传机器人 Secret')\n }\n\n const p = spawnSync('dws', [...args, '--format', 'json'], {\n encoding: 'utf8', timeout, maxBuffer: 64 * 1024 * 1024,\n })\n if (p.error) {\n if ((p.error as NodeJS.ErrnoException).code === 'ENOENT') {\n die('dws 跑不起来 —— 先 export PATH=\"$HOME/.local/bin:$PATH\"')\n }\n die(`dws dev connect daemon 启动失败: ${String(p.error)}`)\n }\n const out = p.stdout || p.stderr || ''\n const i = out.indexOf('{')\n if (i >= 0) {\n try {\n const data = JSON.parse(out.slice(i, lastBrace(out) + 1))\n const err = data?.error || {}\n if (err.code || err.message) {\n die(`dws 失败 dev connect daemon\\n ${JSON.stringify(err).slice(0, 300)}`)\n }\n if (p.status !== 0) {\n die(`dws dev connect daemon 以退出码 ${p.status} 结束`)\n }\n return data\n } catch (error) {\n if (error instanceof Blocked) throw error\n die('dws dev connect daemon 返回了无法解析的 JSON')\n }\n }\n if (p.status === 0 && /connect daemon started\\s*\\(pid\\s*=?\\s*\\d+\\)/i.test(out)) {\n const pid = Number(out.match(/pid\\s*=?\\s*(\\d+)/i)?.[1] || 0)\n return { data: { started: true, pid, responseMode: 'daemon-text' } }\n }\n const summary = out.trim().replace(/\\s+/g, ' ').slice(0, 300)\n die(`dws dev connect daemon 未返回已知成功响应${p.status === null ? '' : ` (exit=${p.status})`}: ${summary}`)\n}\n\n/** 停止已知 connector;命令回包只说明“已受理”,调用方仍须 status 回读。 */\nexport function stopConnectDaemon(\n args: string[],\n { timeout = 30_000 } = {},\n): Record<string, any> {\n if (args[0] !== 'dev' || args[1] !== 'connect' || args[2] !== 'stop') {\n die('stopConnectDaemon 只允许执行 dev connect stop')\n }\n if (!args.includes('--robot-client-id') && !args.includes('--unified-app-id')) {\n die('dev connect stop 必须固定 robot-client-id 或 unified-app-id')\n }\n if (args.includes('--robot-client-secret')) {\n die('dev connect stop 禁止通过命令行传机器人 Secret')\n }\n const p = spawnSync('dws', [...args, '--format', 'json'], {\n encoding: 'utf8', timeout, maxBuffer: 64 * 1024 * 1024,\n })\n if (p.error) die(`dws dev connect stop 执行失败: ${String(p.error)}`)\n const out = p.stdout || p.stderr || ''\n const i = out.indexOf('{')\n if (i >= 0) {\n try {\n const data = JSON.parse(out.slice(i, lastBrace(out) + 1))\n const err = data?.error || {}\n if (err.code || err.message) {\n die(`dws 失败 dev connect stop\\n ${JSON.stringify(err).slice(0, 300)}`)\n }\n if (p.status !== 0) die(`dws dev connect stop 以退出码 ${p.status} 结束`)\n return data\n } catch (error) {\n if (error instanceof Blocked) throw error\n die('dws dev connect stop 返回了无法解析的 JSON')\n }\n }\n if (p.status === 0 && /(connect daemon stopped|connector (?:is )?not running|no connector heartbeat)/i.test(out)) {\n return { data: { stopAccepted: true, responseMode: 'daemon-text' } }\n }\n const summary = out.trim().replace(/\\s+/g, ' ').slice(0, 300)\n die(`dws dev connect stop 未返回已知响应${p.status === null ? '' : ` (exit=${p.status})`}: ${summary}`)\n}\n\nfunction lastBrace(s) {\n // dws 有时在 JSON 后面粘一行 WARN / INFO —— 从最后一个 } 截断\n for (let i = s.length - 1; i >= 0; i--) if (s[i] === '}') return i\n return s.length - 1\n}\n\n// ──────────────── 三个层级,各读各的 ────────────────\nconst inner = (d) => (d && typeof d.data === 'object' && d.data !== null ? d.data : d)\n\n/** 列表类查询的记录。拿不到就 die —— 绝不当成\"表是空的\"。 */\nexport function records(d) {\n const x = inner(d)\n if (!('records' in x)) {\n die(`返回里没有 records(顶层键=${Object.keys(d)}) —— 接口形状可能变了。` +\n `**绝不把它当成【表是空的】。**`)\n }\n if (x.hasMore || x.partial) {\n die('分页未收敛 —— **部分结果不许当完整结果用。** 半张表算出来的是错的。')\n }\n return x.records || []\n}\n\n/** 文档正文。**层级和列表类查询不一样** —— 两种形状都兜住,拿不到就 die。 */\nexport function markdown(d) {\n const md = inner(d).markdown ?? d.markdown\n if (md === undefined || md === null) {\n die(`拿不到文档正文(顶层键=${Object.keys(d)}) —— **绝不当成【这篇是空的】。**`)\n }\n return md\n}\n\n/** 目录节点。 */\nexport const nodes = (d) => inner(d).nodes || []\n\n/**\n * 待办翻页 —— **这是唯一有分页信号的读法。**\n *\n * ⚠️ **不要把页开大**。超过这个大小,分页信号会消失,\n * 你拿到 undefined、当成 false,然后**静默漏掉剩下的**。\n * 心跳的收件靠它 —— 漏了循环件就是「静默收工且不自知」。\n */\nexport function todosPage(status = 'false', page = 1) {\n const d = run(['todo', 'task', 'list', '--status', status, '--size', '20', '--page', String(page)])\n const r = d.result || {}\n if (!('todoCards' in r)) {\n die(`待办返回里没有 todoCards(result 键=${Object.keys(r)}) —— 绝不当成【没有待办】。`)\n }\n return { cards: r.todoCards || [], more: Boolean(r.hasMore) }\n}\n\n/** 把待办【拉全】。只看第一页会漏掉循环件。 */\nexport function todosAll(status = 'false', maxPages = 20) {\n const out: any[] = []\n for (let page = 1; page <= maxPages; page++) {\n const { cards, more } = todosPage(status, page)\n out.push(...cards)\n if (!more) return out\n }\n die(`待办翻页超过 ${maxPages} 页还没收敛 —— 不许把部分结果当完整。`)\n}\n\n// ──────────────── 创建类:报错也可能成功 → 按名回查 ────────────────\n/** 目录列表可能把扩展名单列,比对时要拼回去。 */\nexport function findChild(folder, name) {\n for (const n of nodes(run(['doc', 'list', '--folder', folder, '--page-size', '50']))) {\n const nm = n.name || '', ext = n.extension || ''\n if (nm === name || (ext && `${nm}.${ext}` === name)) return n.nodeId\n }\n return null\n}\n\n/** 建文档。**不看返回信封** —— 一律按名回查定成败。 */\nexport function createDoc(folder, name, file) {\n const exist = findChild(folder, name)\n if (exist) return exist\n run(['doc', 'create', '--name', name, '--folder', folder,\n '--content-file', file, '--content-format', 'markdown', '--yes'],\n { allowFail: true }) // ← **报错也可能其实成功了**\n sleep(1200)\n const nid = findChild(folder, name)\n if (!nid) die(`建不出文档 ${name} —— 别把它当成【建成功了】。`)\n return nid\n}\n\nexport const updateDoc = (node, file) =>\n run(['doc', 'update', '--node', node, '--content-file', file,\n '--content-format', 'markdown', '--mode', 'overwrite', '--yes'])\n\n/** 候选发布只追加唯一 marker;写错误不重试,调用方必须全文回读。 */\nexport const appendDoc = (node: string, file: string, profile = '') =>\n run(['doc', 'update', '--node', node, '--content-file', file,\n '--content-format', 'markdown', '--mode', 'append', ...profileArgs(profile), '--yes'],\n { allowFail: true, retries: 0 })\n\nexport const readDoc = (node, profile = '') => markdown(run([\n 'doc', 'read', '--node', node, ...profileArgs(profile),\n]))\nexport const docInfo = (node, profile = '') => inner(run([\n 'doc', 'info', '--node', node, ...profileArgs(profile),\n]))\n\n// ──────────────── 漂移判定:重排版【不是】漂移 ────────────────\n/**\n * 剥掉 markdown 语法和转义装饰,只留【文字本身】。\n *\n * 钉钉会重排版你推上去的 markdown。**重排版不是漂移。**\n * 用行级 diff 判漂移 → 每次 sync 都误报 →\n * **然后你就会开始无脑 --force,真正的漂移也一起覆盖掉了。**\n */\nexport function words(s) {\n let t = String(s ?? '')\n for (let i = 0; i < 3; i++) t = unescapeHtml(t) // 可能被多次转义\n t = t.replace(/&#(\\d+);/g, (_, n) => String.fromCharCode(+n))\n t = t.replace(/\\[打开\\]\\([^)]*\\)/g, '') // 钉钉自动加的链接装饰\n return t.replace(/[`*_>#|\\\\~\\-\\s]+/g, '')\n}\n\nfunction unescapeHtml(s) {\n return s.replace(/&(amp|lt|gt|quot|#39);/g, (m, e) =>\n ({ amp: '&', lt: '<', gt: '>', quot: '\"', '#39': \"'\" })[e])\n}\n\nexport const same = (a, b) => words(a) === words(b)\n\n// ──────────────── 数字员工的消息原子动作(typed,目标不可自由拼装) ────────────────\nfunction profileArgs(profile) {\n return profile ? ['--profile', profile] : []\n}\n\nfunction assertReplyTarget(target) {\n const missing: string[] = []\n if (!target?.conversationId) missing.push('conversationId')\n if (!target?.messageId) missing.push('messageId')\n if (!target?.senderOpenDingTalkId) missing.push('senderOpenDingTalkId')\n if (missing.length) die(`固定回复目标缺少 ${missing.join(' / ')}`)\n}\n\n/** ack 没有服务端 UUID,因此绝不自动重试。 */\nexport function ackMessage(target, { emoji = '收到', profile = '' } = {}) {\n assertReplyTarget(target)\n return run([\n 'chat', 'message', 'add-emoji',\n '--conversation-id', target.conversationId,\n '--msg-id', target.messageId,\n '--emoji', emoji,\n ...profileArgs(profile),\n ], { retries: 0 })\n}\n\n/** reply/ask 共用同一传输;uuid 在调用前已持久化,重试也必须复用。 */\nexport function replyMessage(\n target,\n text,\n { uuid = '', profile = '' }: { uuid?: string; profile?: string } = {},\n) {\n assertReplyTarget(target)\n if (!String(text || '').trim()) die('回复正文不能为空')\n if (!uuid) die('回复前必须先持久化 uuid 幂等键')\n return run([\n 'chat', 'message', 'reply',\n '--conversation-id', target.conversationId,\n '--ref-msg-id', target.messageId,\n '--ref-sender', target.senderOpenDingTalkId,\n '--text', text,\n '--uuid', uuid,\n '--ai-tag=true',\n ...profileArgs(profile),\n '--yes',\n ], { retries: 1 })\n}\n\nexport function readMessageEmotions(messageId, profile = '') {\n return run([\n 'chat', 'message', 'list-emotion-replies', '--msg-ids', messageId,\n ...profileArgs(profile),\n ], { allowFail: true, retries: 0 })\n}\n\nexport function querySendStatus(openTaskId, profile = '') {\n return run([\n 'chat', 'message', 'query-send-status', '--open-task-id', openTaskId,\n ...profileArgs(profile),\n ], { allowFail: true, retries: 1 })\n}\n\nexport function readMessagesByIds(messageId, profile = '') {\n return run([\n 'chat', 'message', 'list-by-ids', '--msg-ids', messageId,\n ...profileArgs(profile),\n ], { allowFail: true, retries: 1 })\n}\n\n/** 外发前核对 profile 对应的真实执行身份。 */\nexport function getSelf(profile = '') {\n return run([\n 'contact', 'user', 'get-self', ...profileArgs(profile),\n ], { retries: 1 })\n}\n\n// ──────────────── Operational Memory:AI 表格宽表原子能力 ────────────────\nexport function queryAiTableRecords({\n baseId, tableId, filters = null, recordIds = [], profile = '',\n}: {\n baseId: string\n tableId: string\n filters?: Record<string, unknown> | null\n recordIds?: string[]\n profile?: string\n}) {\n const args = [\n 'aitable', 'record', 'query', '--base-id', baseId, '--table-id', tableId,\n ]\n if (recordIds.length) args.push('--record-ids', recordIds.join(','))\n else if (filters) args.push('--filters', JSON.stringify(filters), '--limit', '100')\n args.push(...profileArgs(profile))\n return records(run(args, { retries: 1 }))\n}\n\n/** AI 表格 Base 目录回读;调用方仍需继续核对固定 tableId。 */\nexport function getAiTableBase(baseId: string, profile = ''): Record<string, any> {\n return inner(run([\n 'aitable', 'base', 'get', '--base-id', baseId, ...profileArgs(profile),\n ], { retries: 1 }))\n}\n\n/** AI 表格表结构回读;写入前用它确认固定 tableId 与 fieldId。 */\nexport function getAiTableTables(\n baseId: string,\n tableIds: string[],\n profile = '',\n): Record<string, any> {\n if (!tableIds.length) die('AI 表格结构回读必须固定至少一个 tableId')\n return inner(run([\n 'aitable', 'table', 'get', '--base-id', baseId,\n '--table-ids', tableIds.join(','), ...profileArgs(profile),\n ], { retries: 1 }))\n}\n\n/** 写错误可能实际成功,因此不重试、不直接下结论;调用方必须按 key/recordId 回读。 */\nexport function createAiTableRecord(\n baseId: string,\n tableId: string,\n cells: Record<string, unknown>,\n profile = '',\n): { accepted: boolean; recordIds: string[]; data: unknown } {\n const data = run([\n 'aitable', 'record', 'create', '--base-id', baseId, '--table-id', tableId,\n '--records', JSON.stringify([{ cells }]), ...profileArgs(profile), '--yes',\n ], { allowFail: true, retries: 0 })\n const ids = data ? inner(data).newRecordIds : []\n return {\n accepted: Boolean(data),\n recordIds: Array.isArray(ids) ? ids.map(String).filter(Boolean) : [],\n data,\n }\n}\n\n/** 写错误可能实际成功,因此不重试;调用方必须按 recordId 回读。 */\nexport function updateAiTableRecord(\n baseId: string,\n tableId: string,\n recordId: string,\n cells: Record<string, unknown>,\n profile = '',\n): { accepted: boolean; data: unknown } {\n const data = run([\n 'aitable', 'record', 'update', '--base-id', baseId, '--table-id', tableId,\n '--records', JSON.stringify([{ recordId, cells }]), ...profileArgs(profile), '--yes',\n ], { allowFail: true, retries: 0 })\n return { accepted: Boolean(data), data }\n}\n"]}
@@ -0,0 +1,193 @@
1
+ import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, } from 'node:fs';
2
+ import { createHash } from 'node:crypto';
3
+ import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
4
+ import { bootstrap } from './bootstrap.js';
5
+ /**
6
+ * Canonicalize an evidence/tool path even when the final file does not exist yet.
7
+ * Resolving the nearest existing ancestor also normalizes macOS /var -> /private/var
8
+ * aliases, which prevents a valid system-temp sandbox path from being misclassified.
9
+ */
10
+ export function canonicalEvalPath(input) {
11
+ let cursor = resolve(input);
12
+ const missing = [];
13
+ while (!existsSync(cursor)) {
14
+ const parent = dirname(cursor);
15
+ if (parent === cursor)
16
+ break;
17
+ missing.unshift(basename(cursor));
18
+ cursor = parent;
19
+ }
20
+ const existing = existsSync(cursor) ? realpathSync(cursor) : cursor;
21
+ return resolve(existing, ...missing);
22
+ }
23
+ export function isWorkspacePathInside(workspace, input) {
24
+ const rel = relative(canonicalEvalPath(workspace), canonicalEvalPath(input));
25
+ return rel === '' ||
26
+ (rel !== '..' && !rel.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`));
27
+ }
28
+ export function relativeWorkspacePath(workspace, input) {
29
+ return relative(canonicalEvalPath(workspace), canonicalEvalPath(input))
30
+ .split('\\').join('/') || '.';
31
+ }
32
+ export function evaluateWorkspaceEvidence(workspace, expectations = []) {
33
+ const root = realpathSync(resolve(workspace));
34
+ return expectations.map((expectation) => {
35
+ try {
36
+ if (expectation.type === 'agentDefinition') {
37
+ const definition = bootstrap(root).definition;
38
+ const skills = definition.skills.map((item) => item.name);
39
+ const missing = expectation.requiredSkills.filter((name) => !skills.includes(name));
40
+ const passed = definition.status === expectation.status && missing.length === 0;
41
+ return {
42
+ id: expectation.id,
43
+ surface: expectation.surface,
44
+ type: expectation.type,
45
+ passed,
46
+ detail: `status=${definition.status}; skills=${skills.join(',')}; missing=${missing.join(',') || '-'}`,
47
+ hash: definition.hash,
48
+ };
49
+ }
50
+ const path = safeEvidencePath(root, expectation.path);
51
+ const rel = relative(root, path).split('\\').join('/');
52
+ if (expectation.type === 'pathAbsent') {
53
+ return {
54
+ id: expectation.id, surface: expectation.surface, type: expectation.type,
55
+ passed: !existsSync(path), path: rel,
56
+ detail: existsSync(path) ? `unexpected path exists: ${rel}` : `absent: ${rel}`,
57
+ };
58
+ }
59
+ if (!existsSync(path)) {
60
+ return {
61
+ id: expectation.id, surface: expectation.surface, type: expectation.type,
62
+ passed: false, path: rel, detail: `missing: ${rel}`,
63
+ };
64
+ }
65
+ // Existing symlinks are resolved after the lexical check so they cannot escape the sandbox.
66
+ safeEvidencePath(root, expectation.path, true);
67
+ const stat = lstatSync(path);
68
+ if (stat.isSymbolicLink())
69
+ throw new Error(`不接受符号链接证据: ${rel}`);
70
+ if (expectation.type === 'pathExists') {
71
+ const kindMatched = expectation.kind === undefined ||
72
+ (expectation.kind === 'file' ? stat.isFile() : stat.isDirectory());
73
+ const bytes = stat.isFile() ? stat.size : 0;
74
+ const sizeMatched = expectation.minBytes === undefined || bytes >= expectation.minBytes;
75
+ return {
76
+ id: expectation.id, surface: expectation.surface, type: expectation.type,
77
+ passed: kindMatched && sizeMatched, path: rel,
78
+ detail: `kind=${stat.isFile() ? 'file' : stat.isDirectory() ? 'directory' : 'other'}; bytes=${bytes}`,
79
+ hash: stat.isFile() ? sha256(readFileSync(path)) : undefined,
80
+ };
81
+ }
82
+ if (!stat.isFile())
83
+ throw new Error(`文本/JSON 证据必须是文件: ${rel}`);
84
+ const body = readFileSync(path, 'utf8');
85
+ if (expectation.type === 'textIncludesAll' || expectation.type === 'textExcludesAll') {
86
+ const matches = expectation.values.filter((value) => body.includes(value));
87
+ const passed = expectation.type === 'textIncludesAll'
88
+ ? matches.length === expectation.values.length
89
+ : matches.length === 0;
90
+ return {
91
+ id: expectation.id, surface: expectation.surface, type: expectation.type,
92
+ passed, path: rel, hash: sha256(body),
93
+ detail: `matched=${JSON.stringify(matches)}; expected=${JSON.stringify(expectation.values)}`,
94
+ };
95
+ }
96
+ if (expectation.type !== 'jsonEquals')
97
+ throw new Error(`未知证据断言: ${expectation.type}`);
98
+ const parsed = JSON.parse(body);
99
+ const actual = jsonPointer(parsed, expectation.pointer);
100
+ const passed = stable(actual) === stable(expectation.value);
101
+ return {
102
+ id: expectation.id, surface: expectation.surface, type: expectation.type,
103
+ passed, path: rel, hash: sha256(body),
104
+ detail: `pointer=${expectation.pointer}; actual=${stable(actual)}; expected=${stable(expectation.value)}`,
105
+ };
106
+ }
107
+ catch (error) {
108
+ return {
109
+ id: expectation.id,
110
+ surface: expectation.surface,
111
+ type: expectation.type,
112
+ passed: false,
113
+ detail: error.message,
114
+ ...('path' in expectation ? { path: expectation.path } : {}),
115
+ };
116
+ }
117
+ });
118
+ }
119
+ export function snapshotWorkspaceEvidence(workspace, output, expectations = []) {
120
+ const root = realpathSync(resolve(workspace));
121
+ const snapshots = [];
122
+ const seen = new Set();
123
+ for (const expectation of expectations) {
124
+ if (!('path' in expectation) || expectation.type === 'pathAbsent')
125
+ continue;
126
+ const source = safeEvidencePath(root, expectation.path, true);
127
+ if (!existsSync(source))
128
+ continue;
129
+ const stat = lstatSync(source);
130
+ if (!stat.isFile() || stat.isSymbolicLink())
131
+ continue;
132
+ const rel = relative(root, source).split('\\').join('/');
133
+ const key = `${expectation.surface}:${rel}`;
134
+ if (seen.has(key))
135
+ continue;
136
+ seen.add(key);
137
+ const destination = join(output, expectation.surface, rel);
138
+ mkdirSync(dirname(destination), { recursive: true });
139
+ copyFileSync(source, destination);
140
+ const body = readFileSync(source);
141
+ snapshots.push({
142
+ surface: expectation.surface,
143
+ path: `${expectation.surface}/${rel}`,
144
+ hash: sha256(body),
145
+ bytes: body.byteLength,
146
+ });
147
+ }
148
+ return snapshots;
149
+ }
150
+ function safeEvidencePath(root, input, resolveExisting = false) {
151
+ if (!input || isAbsolute(input) || input.split(/[\\/]/).includes('..')) {
152
+ throw new Error(`证据路径必须是 Workspace 内相对路径: ${input}`);
153
+ }
154
+ const candidate = resolve(root, input);
155
+ const lexical = relative(root, candidate);
156
+ if (lexical === '..' || lexical.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`)) {
157
+ throw new Error(`证据路径越界: ${input}`);
158
+ }
159
+ if (resolveExisting && existsSync(candidate)) {
160
+ const actual = realpathSync(candidate);
161
+ const rel = relative(root, actual);
162
+ if (rel === '..' || rel.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`)) {
163
+ throw new Error(`证据路径通过链接越界: ${input}`);
164
+ }
165
+ }
166
+ return candidate;
167
+ }
168
+ function jsonPointer(value, pointer) {
169
+ if (pointer === '')
170
+ return value;
171
+ if (!pointer.startsWith('/'))
172
+ throw new Error(`JSON pointer 必须以 / 开头: ${pointer}`);
173
+ return pointer.slice(1).split('/').reduce((current, token) => {
174
+ const key = token.replace(/~1/g, '/').replace(/~0/g, '~');
175
+ if (current === null || current === undefined || typeof current !== 'object' || !(key in current)) {
176
+ throw new Error(`JSON pointer 不存在: ${pointer}`);
177
+ }
178
+ return current[key];
179
+ }, value);
180
+ }
181
+ function stable(value) {
182
+ if (Array.isArray(value))
183
+ return `[${value.map(stable).join(',')}]`;
184
+ if (value && typeof value === 'object') {
185
+ return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b))
186
+ .map(([key, item]) => `${JSON.stringify(key)}:${stable(item)}`).join(',')}}`;
187
+ }
188
+ return JSON.stringify(value) ?? 'undefined';
189
+ }
190
+ function sha256(value) {
191
+ return createHash('sha256').update(value).digest('hex');
192
+ }
193
+ //# sourceMappingURL=eval-evidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval-evidence.js","sourceRoot":"","sources":["../../src/eval-evidence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync,\n} from 'node:fs'\nimport { createHash } from 'node:crypto'\nimport { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { bootstrap } from './bootstrap.js'\n\nexport type EvalEvidenceSurface = 'filesystem' | 'workspace' | 'artifact'\n\ntype PathExpectation = {\n id: string\n surface: EvalEvidenceSurface\n type: 'pathExists'\n path: string\n kind?: 'file' | 'directory'\n minBytes?: number\n}\n\ntype AbsentExpectation = {\n id: string\n surface: EvalEvidenceSurface\n type: 'pathAbsent'\n path: string\n}\n\ntype TextExpectation = {\n id: string\n surface: EvalEvidenceSurface\n type: 'textIncludesAll' | 'textExcludesAll'\n path: string\n values: string[]\n}\n\ntype JsonExpectation = {\n id: string\n surface: EvalEvidenceSurface\n type: 'jsonEquals'\n path: string\n pointer: string\n value: unknown\n}\n\ntype DefinitionExpectation = {\n id: string\n surface: 'workspace'\n type: 'agentDefinition'\n status: 'ready' | 'partial'\n requiredSkills: string[]\n}\n\nexport type EvalEvidenceExpectation = PathExpectation | AbsentExpectation |\n TextExpectation | JsonExpectation | DefinitionExpectation\n\nexport interface EvalEvidenceResult {\n id: string\n surface: EvalEvidenceSurface\n type: EvalEvidenceExpectation['type']\n passed: boolean\n detail: string\n path?: string\n hash?: string\n}\n\n/**\n * Canonicalize an evidence/tool path even when the final file does not exist yet.\n * Resolving the nearest existing ancestor also normalizes macOS /var -> /private/var\n * aliases, which prevents a valid system-temp sandbox path from being misclassified.\n */\nexport function canonicalEvalPath(input: string): string {\n let cursor = resolve(input)\n const missing: string[] = []\n while (!existsSync(cursor)) {\n const parent = dirname(cursor)\n if (parent === cursor) break\n missing.unshift(basename(cursor))\n cursor = parent\n }\n const existing = existsSync(cursor) ? realpathSync(cursor) : cursor\n return resolve(existing, ...missing)\n}\n\nexport function isWorkspacePathInside(workspace: string, input: string): boolean {\n const rel = relative(canonicalEvalPath(workspace), canonicalEvalPath(input))\n return rel === '' ||\n (rel !== '..' && !rel.startsWith(`..${process.platform === 'win32' ? '\\\\' : '/'}`))\n}\n\nexport function relativeWorkspacePath(workspace: string, input: string): string {\n return relative(canonicalEvalPath(workspace), canonicalEvalPath(input))\n .split('\\\\').join('/') || '.'\n}\n\nexport function evaluateWorkspaceEvidence(\n workspace: string,\n expectations: EvalEvidenceExpectation[] = [],\n): EvalEvidenceResult[] {\n const root = realpathSync(resolve(workspace))\n return expectations.map((expectation) => {\n try {\n if (expectation.type === 'agentDefinition') {\n const definition = bootstrap(root).definition\n const skills = definition.skills.map((item) => item.name)\n const missing = expectation.requiredSkills.filter((name) => !skills.includes(name))\n const passed = definition.status === expectation.status && missing.length === 0\n return {\n id: expectation.id,\n surface: expectation.surface,\n type: expectation.type,\n passed,\n detail: `status=${definition.status}; skills=${skills.join(',')}; missing=${missing.join(',') || '-'}`,\n hash: definition.hash,\n }\n }\n\n const path = safeEvidencePath(root, expectation.path)\n const rel = relative(root, path).split('\\\\').join('/')\n if (expectation.type === 'pathAbsent') {\n return {\n id: expectation.id, surface: expectation.surface, type: expectation.type,\n passed: !existsSync(path), path: rel,\n detail: existsSync(path) ? `unexpected path exists: ${rel}` : `absent: ${rel}`,\n }\n }\n if (!existsSync(path)) {\n return {\n id: expectation.id, surface: expectation.surface, type: expectation.type,\n passed: false, path: rel, detail: `missing: ${rel}`,\n }\n }\n\n // Existing symlinks are resolved after the lexical check so they cannot escape the sandbox.\n safeEvidencePath(root, expectation.path, true)\n const stat = lstatSync(path)\n if (stat.isSymbolicLink()) throw new Error(`不接受符号链接证据: ${rel}`)\n if (expectation.type === 'pathExists') {\n const kindMatched = expectation.kind === undefined ||\n (expectation.kind === 'file' ? stat.isFile() : stat.isDirectory())\n const bytes = stat.isFile() ? stat.size : 0\n const sizeMatched = expectation.minBytes === undefined || bytes >= expectation.minBytes\n return {\n id: expectation.id, surface: expectation.surface, type: expectation.type,\n passed: kindMatched && sizeMatched, path: rel,\n detail: `kind=${stat.isFile() ? 'file' : stat.isDirectory() ? 'directory' : 'other'}; bytes=${bytes}`,\n hash: stat.isFile() ? sha256(readFileSync(path)) : undefined,\n }\n }\n if (!stat.isFile()) throw new Error(`文本/JSON 证据必须是文件: ${rel}`)\n const body = readFileSync(path, 'utf8')\n if (expectation.type === 'textIncludesAll' || expectation.type === 'textExcludesAll') {\n const matches = expectation.values.filter((value) => body.includes(value))\n const passed = expectation.type === 'textIncludesAll'\n ? matches.length === expectation.values.length\n : matches.length === 0\n return {\n id: expectation.id, surface: expectation.surface, type: expectation.type,\n passed, path: rel, hash: sha256(body),\n detail: `matched=${JSON.stringify(matches)}; expected=${JSON.stringify(expectation.values)}`,\n }\n }\n if (expectation.type !== 'jsonEquals') throw new Error(`未知证据断言: ${expectation.type}`)\n const parsed = JSON.parse(body) as unknown\n const actual = jsonPointer(parsed, expectation.pointer)\n const passed = stable(actual) === stable(expectation.value)\n return {\n id: expectation.id, surface: expectation.surface, type: expectation.type,\n passed, path: rel, hash: sha256(body),\n detail: `pointer=${expectation.pointer}; actual=${stable(actual)}; expected=${stable(expectation.value)}`,\n }\n } catch (error) {\n return {\n id: expectation.id,\n surface: expectation.surface,\n type: expectation.type,\n passed: false,\n detail: (error as Error).message,\n ...('path' in expectation ? { path: expectation.path } : {}),\n }\n }\n })\n}\n\nexport function snapshotWorkspaceEvidence(\n workspace: string,\n output: string,\n expectations: EvalEvidenceExpectation[] = [],\n): Array<{ surface: EvalEvidenceSurface; path: string; hash: string; bytes: number }> {\n const root = realpathSync(resolve(workspace))\n const snapshots: Array<{\n surface: EvalEvidenceSurface\n path: string\n hash: string\n bytes: number\n }> = []\n const seen = new Set<string>()\n for (const expectation of expectations) {\n if (!('path' in expectation) || expectation.type === 'pathAbsent') continue\n const source = safeEvidencePath(root, expectation.path, true)\n if (!existsSync(source)) continue\n const stat = lstatSync(source)\n if (!stat.isFile() || stat.isSymbolicLink()) continue\n const rel = relative(root, source).split('\\\\').join('/')\n const key = `${expectation.surface}:${rel}`\n if (seen.has(key)) continue\n seen.add(key)\n const destination = join(output, expectation.surface, rel)\n mkdirSync(dirname(destination), { recursive: true })\n copyFileSync(source, destination)\n const body = readFileSync(source)\n snapshots.push({\n surface: expectation.surface,\n path: `${expectation.surface}/${rel}`,\n hash: sha256(body),\n bytes: body.byteLength,\n })\n }\n return snapshots\n}\n\nfunction safeEvidencePath(root: string, input: string, resolveExisting = false): string {\n if (!input || isAbsolute(input) || input.split(/[\\\\/]/).includes('..')) {\n throw new Error(`证据路径必须是 Workspace 内相对路径: ${input}`)\n }\n const candidate = resolve(root, input)\n const lexical = relative(root, candidate)\n if (lexical === '..' || lexical.startsWith(`..${process.platform === 'win32' ? '\\\\' : '/'}`)) {\n throw new Error(`证据路径越界: ${input}`)\n }\n if (resolveExisting && existsSync(candidate)) {\n const actual = realpathSync(candidate)\n const rel = relative(root, actual)\n if (rel === '..' || rel.startsWith(`..${process.platform === 'win32' ? '\\\\' : '/'}`)) {\n throw new Error(`证据路径通过链接越界: ${input}`)\n }\n }\n return candidate\n}\n\nfunction jsonPointer(value: unknown, pointer: string): unknown {\n if (pointer === '') return value\n if (!pointer.startsWith('/')) throw new Error(`JSON pointer 必须以 / 开头: ${pointer}`)\n return pointer.slice(1).split('/').reduce((current: any, token) => {\n const key = token.replace(/~1/g, '/').replace(/~0/g, '~')\n if (current === null || current === undefined || typeof current !== 'object' || !(key in current)) {\n throw new Error(`JSON pointer 不存在: ${pointer}`)\n }\n return current[key]\n }, value as any)\n}\n\nfunction stable(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stable).join(',')}]`\n if (value && typeof value === 'object') {\n return `{${Object.entries(value as Record<string, unknown>).sort(([a], [b]) => a.localeCompare(b))\n .map(([key, item]) => `${JSON.stringify(key)}:${stable(item)}`).join(',')}}`\n }\n return JSON.stringify(value) ?? 'undefined'\n}\n\nfunction sha256(value: string | Buffer): string {\n return createHash('sha256').update(value).digest('hex')\n}\n"]}
@@ -0,0 +1,146 @@
1
+ import { existsSync, readdirSync, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { findExecutable } from './doctor.js';
6
+ /** 版本探测的硬上限:Host CLI 卡住不能拖住 doctor。 */
7
+ const VERSION_TIMEOUT_MS = 5_000;
8
+ const HOSTS = [
9
+ {
10
+ name: 'claude-code',
11
+ label: 'Claude Code',
12
+ command: 'claude',
13
+ // Claude Code 读 ~/.claude/skills 下的 symlink,不读 ~/.agents/skills。
14
+ discovery: 'symlink',
15
+ configRoots: (context) => [claudeHome(context)],
16
+ skillRoot: (context) => join(claudeHome(context), 'skills'),
17
+ },
18
+ {
19
+ name: 'codex',
20
+ label: 'Codex',
21
+ command: 'codex',
22
+ // Codex 与 OpenCode 共用 ~/.agents/skills,因此二者的可见性永远同状态。
23
+ discovery: 'shared-agent-skills',
24
+ configRoots: (context) => [
25
+ overrideDir(context, context.env.CODEX_HOME) || join(context.home, '.codex'),
26
+ ],
27
+ skillRoot: (context) => join(context.home, '.agents', 'skills'),
28
+ },
29
+ {
30
+ name: 'opencode',
31
+ label: 'OpenCode',
32
+ command: 'opencode',
33
+ discovery: 'shared-agent-skills',
34
+ configRoots: (context) => [
35
+ join(overrideDir(context, context.env.XDG_CONFIG_HOME) || join(context.home, '.config'), 'opencode'),
36
+ join(context.home, '.opencode'),
37
+ ],
38
+ skillRoot: (context) => join(context.home, '.agents', 'skills'),
39
+ },
40
+ ];
41
+ function claudeHome(context) {
42
+ return overrideDir(context, context.env.CLAUDE_CONFIG_DIR) || join(context.home, '.claude');
43
+ }
44
+ /**
45
+ * 探测本机装了哪些 coding agent CLI,以及内置 Skill 对它们是否可见。
46
+ *
47
+ * 这是只读观测:除 `<cli> --version` 外不执行任何命令,不写盘,也不改变任何 Host 的配置。
48
+ * 它给出选择依据,不替用户选 Host,也不等于该 Host 已经能跑通一次真实 Run。
49
+ */
50
+ export function detectHosts(options = {}) {
51
+ const env = options.env || process.env;
52
+ const home = options.home || env.HOME || homedir();
53
+ // 调用方钉住的 home 与 env 描述的 home 不一致时,env 的目录覆盖属于另一台环境,不予采信。
54
+ const context = {
55
+ home,
56
+ env,
57
+ trustEnvDirs: !options.home || !env.HOME || env.HOME === options.home,
58
+ };
59
+ const probeVersion = options.probeVersion !== false;
60
+ const watched = options.skills?.length ? new Set(options.skills) : null;
61
+ const roots = new Map();
62
+ for (const spec of HOSTS) {
63
+ const root = spec.skillRoot(context);
64
+ roots.set(root, [...(roots.get(root) || []), spec.name]);
65
+ }
66
+ return HOSTS.map((spec) => {
67
+ const root = spec.skillRoot(context);
68
+ const path = safeFind(spec.command, env);
69
+ const present = listSkills(root);
70
+ return {
71
+ name: spec.name,
72
+ label: spec.label,
73
+ cli: {
74
+ command: spec.command,
75
+ found: Boolean(path),
76
+ path,
77
+ version: path && probeVersion ? probeCliVersion(path, env) : '',
78
+ },
79
+ configRoots: spec.configRoots(context).filter((candidate) => isDirectory(candidate)),
80
+ skillExposureRoot: root,
81
+ skillExposure: {
82
+ root,
83
+ exists: isDirectory(root),
84
+ discovery: spec.discovery,
85
+ sharedWith: (roots.get(root) || []).filter((name) => name !== spec.name),
86
+ skillCount: present.length,
87
+ skills: watched ? present.filter((name) => watched.has(name)) : present,
88
+ },
89
+ };
90
+ });
91
+ }
92
+ /**
93
+ * 环境变量覆盖只在 `trustEnvDirs` 成立时生效;空串、纯空白或不存在的目录一律退回默认路径。
94
+ * 覆盖来自另一个 home 上下文时必须失效,否则被钉住的探测会报出调用方的真实配置目录。
95
+ */
96
+ function overrideDir(context, value) {
97
+ if (!context.trustEnvDirs)
98
+ return '';
99
+ const trimmed = String(value || '').trim();
100
+ return trimmed && isDirectory(trimmed) ? trimmed : '';
101
+ }
102
+ /** PATH 为空或不可解析时返回空串;探测不能让 doctor 崩溃。 */
103
+ function safeFind(command, env) {
104
+ try {
105
+ return findExecutable(command, env);
106
+ }
107
+ catch {
108
+ return '';
109
+ }
110
+ }
111
+ /** 超时、非零退出、二进制损坏都记为空版本;只记录,不上报为 Host 缺失。 */
112
+ function probeCliVersion(path, env) {
113
+ try {
114
+ const run = spawnSync(path, ['--version'], {
115
+ encoding: 'utf8', env, timeout: VERSION_TIMEOUT_MS, windowsHide: true,
116
+ });
117
+ if (run.error || run.status !== 0)
118
+ return '';
119
+ return String(run.stdout || '').split(/\r?\n/, 1)[0]?.trim() || '';
120
+ }
121
+ catch {
122
+ return '';
123
+ }
124
+ }
125
+ /** 只有含 SKILL.md 的目录才算一个可被发现的 Skill;散落文件与空目录不计入。 */
126
+ function listSkills(root) {
127
+ try {
128
+ return readdirSync(root, { withFileTypes: true })
129
+ .filter((entry) => entry.isDirectory() || entry.isSymbolicLink())
130
+ .map((entry) => entry.name)
131
+ .filter((name) => existsSync(join(root, name, 'SKILL.md')))
132
+ .sort();
133
+ }
134
+ catch {
135
+ return [];
136
+ }
137
+ }
138
+ function isDirectory(path) {
139
+ try {
140
+ return statSync(path).isDirectory();
141
+ }
142
+ catch {
143
+ return false;
144
+ }
145
+ }
146
+ //# sourceMappingURL=host-detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-detect.js","sourceRoot":"","sources":["../../src/host-detect.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { homedir } from 'node:os'\nimport { spawnSync } from 'node:child_process'\n\nimport { findExecutable } from './doctor.js'\n\nexport type HostName = 'claude-code' | 'codex' | 'opencode'\n\n/** 二进制是否在 PATH 上。这只回答\"能不能启动这个 Host\",不回答它能不能看见 Skill。 */\nexport interface HostCli {\n command: string\n found: boolean\n path: string\n /** `<cli> --version` 的首行;探测失败、超时或不可执行时为空串,不抛错也不降级成 found=false。 */\n version: string\n}\n\n/**\n * Skill 对该 Host 的可见性。与 `cli.found` 是两条独立轨迹:\n * 装了 CLI 不代表 Skill 已暴露,暴露了 Skill 也不代表本机装过 CLI。\n */\nexport interface HostSkillExposure {\n root: string\n exists: boolean\n discovery: 'symlink' | 'shared-agent-skills'\n /** 与该 Host 共用同一个 exposure root 的其它 Host;共用意味着它们的可见性天然同状态。 */\n sharedWith: HostName[]\n /** root 下含 SKILL.md 的目录总数,包含与本项目无关的第三方 Skill。 */\n skillCount: number\n /** 其中命中 `skills` 关注名单的目录名,按字典序;未指定名单时等于全部。 */\n skills: string[]\n}\n\nexport interface HostDetection {\n name: HostName\n label: string\n cli: HostCli\n /** 本机真实存在的配置目录;不存在的候选路径不进入该数组。 */\n configRoots: string[]\n /** `skillExposure.root` 的平铺别名,便于调用方直接引用。 */\n skillExposureRoot: string\n skillExposure: HostSkillExposure\n}\n\nexport interface HostDetectOptions {\n home?: string\n env?: NodeJS.ProcessEnv\n /** 默认 true。置 false 只做文件系统与 PATH 观测,不 spawn 任何子进程。 */\n probeVersion?: boolean\n /** 关注的 Skill 名单;缺省时列出 exposure root 下的全部 Skill。 */\n skills?: string[]\n}\n\n/**\n * 解析出的路径上下文。`env` 只在它确实描述 `home` 这台环境时才参与解析:\n * 调用方钉住一个与 `env.HOME` 不同的 home 时,环境变量属于另一个上下文,必须失效。\n */\ninterface HostContext {\n home: string\n env: NodeJS.ProcessEnv\n /** true 表示可以采信 CLAUDE_CONFIG_DIR / CODEX_HOME / XDG_CONFIG_HOME。 */\n trustEnvDirs: boolean\n}\n\ninterface HostSpec {\n name: HostName\n label: string\n command: string\n discovery: 'symlink' | 'shared-agent-skills'\n configRoots: (context: HostContext) => string[]\n skillRoot: (context: HostContext) => string\n}\n\n/** 版本探测的硬上限:Host CLI 卡住不能拖住 doctor。 */\nconst VERSION_TIMEOUT_MS = 5_000\n\nconst HOSTS: HostSpec[] = [\n {\n name: 'claude-code',\n label: 'Claude Code',\n command: 'claude',\n // Claude Code 读 ~/.claude/skills 下的 symlink,不读 ~/.agents/skills。\n discovery: 'symlink',\n configRoots: (context) => [claudeHome(context)],\n skillRoot: (context) => join(claudeHome(context), 'skills'),\n },\n {\n name: 'codex',\n label: 'Codex',\n command: 'codex',\n // Codex 与 OpenCode 共用 ~/.agents/skills,因此二者的可见性永远同状态。\n discovery: 'shared-agent-skills',\n configRoots: (context) => [\n overrideDir(context, context.env.CODEX_HOME) || join(context.home, '.codex'),\n ],\n skillRoot: (context) => join(context.home, '.agents', 'skills'),\n },\n {\n name: 'opencode',\n label: 'OpenCode',\n command: 'opencode',\n discovery: 'shared-agent-skills',\n configRoots: (context) => [\n join(\n overrideDir(context, context.env.XDG_CONFIG_HOME) || join(context.home, '.config'),\n 'opencode',\n ),\n join(context.home, '.opencode'),\n ],\n skillRoot: (context) => join(context.home, '.agents', 'skills'),\n },\n]\n\nfunction claudeHome(context: HostContext): string {\n return overrideDir(context, context.env.CLAUDE_CONFIG_DIR) || join(context.home, '.claude')\n}\n\n/**\n * 探测本机装了哪些 coding agent CLI,以及内置 Skill 对它们是否可见。\n *\n * 这是只读观测:除 `<cli> --version` 外不执行任何命令,不写盘,也不改变任何 Host 的配置。\n * 它给出选择依据,不替用户选 Host,也不等于该 Host 已经能跑通一次真实 Run。\n */\nexport function detectHosts(options: HostDetectOptions = {}): HostDetection[] {\n const env = options.env || process.env\n const home = options.home || env.HOME || homedir()\n // 调用方钉住的 home 与 env 描述的 home 不一致时,env 的目录覆盖属于另一台环境,不予采信。\n const context: HostContext = {\n home,\n env,\n trustEnvDirs: !options.home || !env.HOME || env.HOME === options.home,\n }\n const probeVersion = options.probeVersion !== false\n const watched = options.skills?.length ? new Set(options.skills) : null\n const roots = new Map<string, HostName[]>()\n for (const spec of HOSTS) {\n const root = spec.skillRoot(context)\n roots.set(root, [...(roots.get(root) || []), spec.name])\n }\n return HOSTS.map((spec) => {\n const root = spec.skillRoot(context)\n const path = safeFind(spec.command, env)\n const present = listSkills(root)\n return {\n name: spec.name,\n label: spec.label,\n cli: {\n command: spec.command,\n found: Boolean(path),\n path,\n version: path && probeVersion ? probeCliVersion(path, env) : '',\n },\n configRoots: spec.configRoots(context).filter((candidate) => isDirectory(candidate)),\n skillExposureRoot: root,\n skillExposure: {\n root,\n exists: isDirectory(root),\n discovery: spec.discovery,\n sharedWith: (roots.get(root) || []).filter((name) => name !== spec.name),\n skillCount: present.length,\n skills: watched ? present.filter((name) => watched.has(name)) : present,\n },\n }\n })\n}\n\n/**\n * 环境变量覆盖只在 `trustEnvDirs` 成立时生效;空串、纯空白或不存在的目录一律退回默认路径。\n * 覆盖来自另一个 home 上下文时必须失效,否则被钉住的探测会报出调用方的真实配置目录。\n */\nfunction overrideDir(context: HostContext, value: string | undefined): string {\n if (!context.trustEnvDirs) return ''\n const trimmed = String(value || '').trim()\n return trimmed && isDirectory(trimmed) ? trimmed : ''\n}\n\n/** PATH 为空或不可解析时返回空串;探测不能让 doctor 崩溃。 */\nfunction safeFind(command: string, env: NodeJS.ProcessEnv): string {\n try {\n return findExecutable(command, env)\n } catch {\n return ''\n }\n}\n\n/** 超时、非零退出、二进制损坏都记为空版本;只记录,不上报为 Host 缺失。 */\nfunction probeCliVersion(path: string, env: NodeJS.ProcessEnv): string {\n try {\n const run = spawnSync(path, ['--version'], {\n encoding: 'utf8', env, timeout: VERSION_TIMEOUT_MS, windowsHide: true,\n })\n if (run.error || run.status !== 0) return ''\n return String(run.stdout || '').split(/\\r?\\n/, 1)[0]?.trim() || ''\n } catch {\n return ''\n }\n}\n\n/** 只有含 SKILL.md 的目录才算一个可被发现的 Skill;散落文件与空目录不计入。 */\nfunction listSkills(root: string): string[] {\n try {\n return readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() || entry.isSymbolicLink())\n .map((entry) => entry.name)\n .filter((name) => existsSync(join(root, name, 'SKILL.md')))\n .sort()\n } catch {\n return []\n }\n}\n\nfunction isDirectory(path: string): boolean {\n try {\n return statSync(path).isDirectory()\n } catch {\n return false\n }\n}\n"]}