@whyshu/argent-engine 0.0.0-stage → 0.6.1

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 (340) hide show
  1. package/MANIFEST.json +341 -0
  2. package/engine/VERSION +1 -0
  3. package/engine/agents/catalog.json +454 -0
  4. package/engine/configs/_aliases.proposed.json +651 -0
  5. package/engine/configs/_migration-plan.proposed.json +899 -0
  6. package/engine/configs/account.json +272 -0
  7. package/engine/configs/advanced.json +205 -0
  8. package/engine/configs/host.json +149 -0
  9. package/engine/configs/hr.json +27 -0
  10. package/engine/configs/notify.json +264 -0
  11. package/engine/core/cli/argent.mjs +277 -0
  12. package/engine/core/contracts/argent.contracts.json +1065 -0
  13. package/engine/core/contracts/verbs.schema.json +2539 -0
  14. package/engine/core/lib/argent-datadir.mjs +223 -0
  15. package/engine/core/lib/audit.mjs +187 -0
  16. package/engine/core/lib/claim.mjs +258 -0
  17. package/engine/core/lib/config-catalog.mjs +1172 -0
  18. package/engine/core/lib/doctor-customer.mjs +577 -0
  19. package/engine/core/lib/flow-list.mjs +138 -0
  20. package/engine/core/lib/flow-registry.mjs +48 -0
  21. package/engine/core/lib/init.mjs +247 -0
  22. package/engine/core/lib/ledger.mjs +124 -0
  23. package/engine/core/lib/license-activate.mjs +813 -0
  24. package/engine/core/lib/license-device.mjs +314 -0
  25. package/engine/core/lib/license-file.mjs +349 -0
  26. package/engine/core/lib/license-guard.mjs +123 -0
  27. package/engine/core/lib/license-ops.mjs +1645 -0
  28. package/engine/core/lib/license-verdict.mjs +133 -0
  29. package/engine/core/lib/license.mjs +577 -0
  30. package/engine/core/lib/notify/dedup.mjs +101 -0
  31. package/engine/core/lib/notify/feishu.mjs +38 -0
  32. package/engine/core/lib/notify/index.mjs +95 -0
  33. package/engine/core/lib/notify/verb.mjs +327 -0
  34. package/engine/core/lib/notify/webhook.mjs +135 -0
  35. package/engine/core/lib/notify/wecom.mjs +36 -0
  36. package/engine/core/lib/python.mjs +145 -0
  37. package/engine/core/lib/sop.mjs +116 -0
  38. package/engine/core/lib/versions.mjs +205 -0
  39. package/engine/core/mcp/client.mjs +67 -0
  40. package/engine/core/mcp/http.mjs +138 -0
  41. package/engine/core/mcp/server.mjs +702 -0
  42. package/engine/scripts/_engine_rules_probe.py +15 -0
  43. package/engine/scripts/analyze-full-day.py +568 -0
  44. package/engine/scripts/archive/feishu-bridge.js +246 -0
  45. package/engine/scripts/argent-agents.mjs +527 -0
  46. package/engine/scripts/argent-capabilities.mjs +776 -0
  47. package/engine/scripts/argent-catalog.mjs +186 -0
  48. package/engine/scripts/argent-datadir.py +62 -0
  49. package/engine/scripts/argent-doctor.mjs +624 -0
  50. package/engine/scripts/argent-env-write.mjs +302 -0
  51. package/engine/scripts/argent-schedule.mjs +620 -0
  52. package/engine/scripts/argent-setup-check.mjs +369 -0
  53. package/engine/scripts/argent-upgrade.mjs +1046 -0
  54. package/engine/scripts/base-bootstrap.sh +576 -0
  55. package/engine/scripts/billing-mode-daily.sh +13 -0
  56. package/engine/scripts/build-engine-package.mjs +126 -0
  57. package/engine/scripts/capability-packs.lock.json +484 -0
  58. package/engine/scripts/capability_pack.py +1242 -0
  59. package/engine/scripts/check-1.0-criteria.mjs +355 -0
  60. package/engine/scripts/check-agent-catalog.mjs +141 -0
  61. package/engine/scripts/check-billing-mode.sh +378 -0
  62. package/engine/scripts/check-child-sessions.mjs +83 -0
  63. package/engine/scripts/check-config-catalog.mjs +677 -0
  64. package/engine/scripts/check-docs-drift.mjs +522 -0
  65. package/engine/scripts/check-dsh-web-expose.sh +98 -0
  66. package/engine/scripts/check-judgment-drift.mjs +403 -0
  67. package/engine/scripts/check-license.mjs +199 -0
  68. package/engine/scripts/check-model-auth.py +443 -0
  69. package/engine/scripts/check-pack-closure.mjs +661 -0
  70. package/engine/scripts/check-portable-symbols.mjs +116 -0
  71. package/engine/scripts/check-preset-landing.mjs +124 -0
  72. package/engine/scripts/check-production-home.mjs +602 -0
  73. package/engine/scripts/check-role-mapping.mjs +461 -0
  74. package/engine/scripts/check-sample-redaction.mjs +320 -0
  75. package/engine/scripts/check-skill-classification.mjs +188 -0
  76. package/engine/scripts/check-sop-consistency.mjs +431 -0
  77. package/engine/scripts/check-test-env-safety.mjs +627 -0
  78. package/engine/scripts/check-version-consistency.mjs +452 -0
  79. package/engine/scripts/ci-run.sh +558 -0
  80. package/engine/scripts/collect-evidence.mjs +42 -0
  81. package/engine/scripts/config-explain.mjs +278 -0
  82. package/engine/scripts/configure-provider.rb +29 -0
  83. package/engine/scripts/customer-config-migrate.mjs +179 -0
  84. package/engine/scripts/customer-preflight.py +892 -0
  85. package/engine/scripts/customer-readiness.sh +135 -0
  86. package/engine/scripts/daily-report-inbox.py +242 -0
  87. package/engine/scripts/daily-report-reconcile.mjs +312 -0
  88. package/engine/scripts/daily-report.mjs +1310 -0
  89. package/engine/scripts/daily-report.sh +47 -0
  90. package/engine/scripts/data/lip-product-dict.json +306 -0
  91. package/engine/scripts/deliver-customer.mjs +932 -0
  92. package/engine/scripts/delivery-faces.txt +31 -0
  93. package/engine/scripts/desktop-trial.sh +146 -0
  94. package/engine/scripts/dev-window.sh +261 -0
  95. package/engine/scripts/drill-clean-machine.sh +315 -0
  96. package/engine/scripts/dsh-api-client.mjs +215 -0
  97. package/engine/scripts/dsh-backup-scope.sh +83 -0
  98. package/engine/scripts/dsh-offline-pack.mjs +77 -0
  99. package/engine/scripts/dsh-overlay-pin.sh +87 -0
  100. package/engine/scripts/dsh-plane.mjs +153 -0
  101. package/engine/scripts/dsh-portforward.py +223 -0
  102. package/engine/scripts/dsh-preflight.mjs +179 -0
  103. package/engine/scripts/dsh-release-watch-daily.sh +19 -0
  104. package/engine/scripts/dsh-release-watch.mjs +458 -0
  105. package/engine/scripts/dsh-remote-check.sh +99 -0
  106. package/engine/scripts/dsh-restart.sh +179 -0
  107. package/engine/scripts/dsh-restore.sh +111 -0
  108. package/engine/scripts/dsh-scope-rewire.mjs +803 -0
  109. package/engine/scripts/dsh-snapshot.mjs +116 -0
  110. package/engine/scripts/dsh-upgrade-readiness.mjs +165 -0
  111. package/engine/scripts/dsh-upgrade.mjs +612 -0
  112. package/engine/scripts/dsh-url.sh +86 -0
  113. package/engine/scripts/dsh-web-launch.mjs +155 -0
  114. package/engine/scripts/ensure-store-windows.py +509 -0
  115. package/engine/scripts/entitlements.py +458 -0
  116. package/engine/scripts/env_probe.py +411 -0
  117. package/engine/scripts/feishu-card.mjs +252 -0
  118. package/engine/scripts/final-check.mjs +43 -0
  119. package/engine/scripts/fix-skill-frontmatter.py +209 -0
  120. package/engine/scripts/flow-auto-retry.py +454 -0
  121. package/engine/scripts/flow-requirements.json +735 -0
  122. package/engine/scripts/flow-scaffold.py +275 -0
  123. package/engine/scripts/flow-timers-plist.sh +87 -0
  124. package/engine/scripts/flow-timers.sh +184 -0
  125. package/engine/scripts/flow-trigger-wake.mjs +284 -0
  126. package/engine/scripts/flow-trigger.sh +571 -0
  127. package/engine/scripts/flow_billing.py +354 -0
  128. package/engine/scripts/flow_capability_stats.py +314 -0
  129. package/engine/scripts/flow_case.py +194 -0
  130. package/engine/scripts/flow_claim.py +497 -0
  131. package/engine/scripts/flow_config.py +1025 -0
  132. package/engine/scripts/flow_failure.py +588 -0
  133. package/engine/scripts/flow_learn.py +105 -0
  134. package/engine/scripts/flow_ledger.py +235 -0
  135. package/engine/scripts/flow_policy.py +238 -0
  136. package/engine/scripts/flow_preflight.py +339 -0
  137. package/engine/scripts/flow_registry.py +341 -0
  138. package/engine/scripts/flow_retry.py +304 -0
  139. package/engine/scripts/flow_return_to_ops.py +191 -0
  140. package/engine/scripts/flow_source.py +315 -0
  141. package/engine/scripts/flow_sources/__init__.py +53 -0
  142. package/engine/scripts/flow_sources/cases_json.py +106 -0
  143. package/engine/scripts/flow_sources/common.py +428 -0
  144. package/engine/scripts/flow_sources/feishu_base.py +273 -0
  145. package/engine/scripts/flow_stale.py +185 -0
  146. package/engine/scripts/flow_status_reconcile.py +344 -0
  147. package/engine/scripts/flow_triage.py +849 -0
  148. package/engine/scripts/flow_write_verify.py +496 -0
  149. package/engine/scripts/fulfillment.js +586 -0
  150. package/engine/scripts/gen-agent-bundles.mjs +276 -0
  151. package/engine/scripts/gen-preset-rows.mjs +259 -0
  152. package/engine/scripts/init-ml.sh +2278 -0
  153. package/engine/scripts/install-internal-role.sh +190 -0
  154. package/engine/scripts/install-license.mjs +641 -0
  155. package/engine/scripts/install-one-shot.mjs +665 -0
  156. package/engine/scripts/integration_contract.py +220 -0
  157. package/engine/scripts/integrations/wenyao/adapter.py +98 -0
  158. package/engine/scripts/integrations/xzh/adapter.py +75 -0
  159. package/engine/scripts/invoice-mapping.example.json +40 -0
  160. package/engine/scripts/invoice-table.sh +195 -0
  161. package/engine/scripts/invoice_carrier_notice.py +279 -0
  162. package/engine/scripts/invoice_fail_closeout.py +297 -0
  163. package/engine/scripts/invoice_lip_runner.py +1508 -0
  164. package/engine/scripts/invoice_lip_runner.py.bak-20260922-1710-freready +1090 -0
  165. package/engine/scripts/invoice_ready_audit.py +191 -0
  166. package/engine/scripts/invoice_runner.py +675 -0
  167. package/engine/scripts/invoice_stageb.py +516 -0
  168. package/engine/scripts/launchd/com.whyshu.argent-bridge.plist.example +42 -0
  169. package/engine/scripts/launchd/com.whyshu.fulfillment-poll.plist.example +29 -0
  170. package/engine/scripts/lease.py +377 -0
  171. package/engine/scripts/lib/agent-install.mjs +649 -0
  172. package/engine/scripts/lib/argent-datadir.mjs +16 -0
  173. package/engine/scripts/lib/argent-datadir.sh +18 -0
  174. package/engine/scripts/lib/argent_datadir.py +285 -0
  175. package/engine/scripts/lib/argent_filelock.py +507 -0
  176. package/engine/scripts/lib/bootcheck-wiring.mjs +510 -0
  177. package/engine/scripts/lib/cdp_ports.py +197 -0
  178. package/engine/scripts/lib/customer-env.mjs +102 -0
  179. package/engine/scripts/lib/daily-report-core.mjs +517 -0
  180. package/engine/scripts/lib/dep-declaration.mjs +81 -0
  181. package/engine/scripts/lib/deps-migrate.mjs +310 -0
  182. package/engine/scripts/lib/dsh-host-resolve.mjs +109 -0
  183. package/engine/scripts/lib/dsh-scope.mjs +500 -0
  184. package/engine/scripts/lib/dsh-snapshot-path.mjs +35 -0
  185. package/engine/scripts/lib/dsh-versions.mjs +96 -0
  186. package/engine/scripts/lib/dsh-web-probe.mjs +214 -0
  187. package/engine/scripts/lib/is-direct-run.mjs +61 -0
  188. package/engine/scripts/lib/patch-compose.mjs +241 -0
  189. package/engine/scripts/lib/patch-target.mjs +35 -0
  190. package/engine/scripts/lib/plugin-lifecycle.mjs +128 -0
  191. package/engine/scripts/lib/plugin-packages.mjs +332 -0
  192. package/engine/scripts/lib/preset-compat.mjs +127 -0
  193. package/engine/scripts/lib/preset-install.mjs +575 -0
  194. package/engine/scripts/lib/preset-mechanism.mjs +56 -0
  195. package/engine/scripts/lib/preset-sync.mjs +59 -0
  196. package/engine/scripts/lib/profile-retire.mjs +97 -0
  197. package/engine/scripts/lib/python-runtime.mjs +190 -0
  198. package/engine/scripts/lib/registry.mjs +72 -0
  199. package/engine/scripts/lib/repo-root.sh +56 -0
  200. package/engine/scripts/lib/schedule-mechanism.mjs +659 -0
  201. package/engine/scripts/lib/schedule-probe.mjs +835 -0
  202. package/engine/scripts/lib/scope-pointer.mjs +164 -0
  203. package/engine/scripts/lib/settings-panels.mjs +816 -0
  204. package/engine/scripts/lib/stdio_utf8.py +86 -0
  205. package/engine/scripts/lib/wake-auth.mjs +392 -0
  206. package/engine/scripts/lib/webdriver_ipc.py +264 -0
  207. package/engine/scripts/lib/zstd-frames.mjs +104 -0
  208. package/engine/scripts/license-e2e-drill.mjs +426 -0
  209. package/engine/scripts/license-gate.mjs +245 -0
  210. package/engine/scripts/lip_backlog.py +171 -0
  211. package/engine/scripts/lip_table.py +185 -0
  212. package/engine/scripts/local-usage.mjs +214 -0
  213. package/engine/scripts/migrate-providers.mjs +127 -0
  214. package/engine/scripts/migrate-skills.sh +65 -0
  215. package/engine/scripts/ml-oauth-setup.mjs +550 -0
  216. package/engine/scripts/ml-store-ids.json +26 -0
  217. package/engine/scripts/ml_artifact_attribution.py +333 -0
  218. package/engine/scripts/ml_capacity_probe.py +1158 -0
  219. package/engine/scripts/ml_hub_status.py +1556 -0
  220. package/engine/scripts/ml_schema_check.py +115 -0
  221. package/engine/scripts/ml_store_ids.py +139 -0
  222. package/engine/scripts/npm-publish-argent.mjs +499 -0
  223. package/engine/scripts/npm-publish-argent.sh +20 -0
  224. package/engine/scripts/orchestrator.py +13811 -0
  225. package/engine/scripts/pack-plugins.mjs +140 -0
  226. package/engine/scripts/patches/dsh-llm-deepseek--transport-diagnostics.patch +297 -0
  227. package/engine/scripts/patches/dsh-session--append-message-shape.patch +15 -0
  228. package/engine/scripts/patches/dsh-session--tool-result-block-017.patch +28 -0
  229. package/engine/scripts/patches/dsh-session-format-v0-to-v1--permission-preset-origin.patch +11 -0
  230. package/engine/scripts/patches/dsh-session-format-v3-to-v4--turn-outside-replace.patch +20 -0
  231. package/engine/scripts/patches/dsh-session-persistence-jsonl--worker-bundled-migrators.patch +39 -0
  232. package/engine/scripts/patches/dsh-tools--render-return-shape.patch +50 -0
  233. package/engine/scripts/patches/verify-bundled-migrators.mjs +79 -0
  234. package/engine/scripts/patches/verify-hardening.mjs +167 -0
  235. package/engine/scripts/patches/verify-session-migration.mjs +149 -0
  236. package/engine/scripts/patches/verify-transport-diagnostics.mjs +227 -0
  237. package/engine/scripts/plan-role-rename.mjs +125 -0
  238. package/engine/scripts/plugin-bootcheck.mjs +451 -0
  239. package/engine/scripts/plugin-composition.json +15 -0
  240. package/engine/scripts/poc-mcp-host.mjs +455 -0
  241. package/engine/scripts/poll-fulfillment.sh +295 -0
  242. package/engine/scripts/poll-invoice.sh +76 -0
  243. package/engine/scripts/profile-doctor.mjs +686 -0
  244. package/engine/scripts/provider-select.sh +71 -0
  245. package/engine/scripts/provider-switch.sh +146 -0
  246. package/engine/scripts/provider-sync-target.check.mjs +64 -0
  247. package/engine/scripts/python-runtime.sh +78 -0
  248. package/engine/scripts/query_runner.py +310 -0
  249. package/engine/scripts/release-argent.sh +574 -0
  250. package/engine/scripts/repair-unresolved-tools.mjs +178 -0
  251. package/engine/scripts/retire-inventory.mjs +40 -0
  252. package/engine/scripts/run-tests.sh +257 -0
  253. package/engine/scripts/s1-audit.mjs +85 -0
  254. package/engine/scripts/self-sign-license.mjs +179 -0
  255. package/engine/scripts/session-health-scan.mjs +63 -0
  256. package/engine/scripts/session-repair-tool-result-content.mjs +181 -0
  257. package/engine/scripts/setup.sh +22 -0
  258. package/engine/scripts/skill-install.mjs +629 -0
  259. package/engine/scripts/sku_translate.py +148 -0
  260. package/engine/scripts/sync-plugins.sh +90 -0
  261. package/engine/scripts/sync-presets.sh +27 -0
  262. package/engine/scripts/sync-scripts.sh +445 -0
  263. package/engine/scripts/telemetry.py +873 -0
  264. package/engine/scripts/ui-copy-count.mjs +220 -0
  265. package/engine/scripts/usage-anchor.sh +137 -0
  266. package/engine/scripts/usage_report.py +531 -0
  267. package/engine/scripts/validate-preset-schema.mjs +208 -0
  268. package/engine/scripts/vertical-init.mjs +323 -0
  269. package/engine/scripts/vertical-lint.mjs +482 -0
  270. package/engine/scripts/wenyao-browser.sh +74 -0
  271. package/engine/scripts/wenyao-fixture-export.py +116 -0
  272. package/engine/scripts/wenyao-invoice-writeback.mjs +540 -0
  273. package/engine/scripts/wenyao-login.sh +35 -0
  274. package/engine/scripts/wenyao-template-map.json +292 -0
  275. package/engine/scripts/wenyao_attach.py +131 -0
  276. package/engine/scripts/wenyao_download_template.py +144 -0
  277. package/engine/scripts/wenyao_fill_template.py +568 -0
  278. package/engine/scripts/wenyao_login.py +145 -0
  279. package/engine/scripts/wenyao_portal.py +633 -0
  280. package/engine/scripts/wenyao_recon.py +230 -0
  281. package/engine/scripts/wenyao_run.py +503 -0
  282. package/engine/scripts/wenyao_shadow_diff.py +160 -0
  283. package/engine/scripts/wenyao_skill_contract.py +156 -0
  284. package/engine/scripts/wenyao_template_fetch.py +175 -0
  285. package/engine/scripts/win/dsh-web-autostart.ps1 +372 -0
  286. package/engine/scripts/ziniao-auth-probe.sh +268 -0
  287. package/engine/scripts/ziniao-keepalive.sh +217 -0
  288. package/engine/scripts/ziniao-post-upgrade-check.sh +121 -0
  289. package/engine/scripts/ziniao-windows.sh +154 -0
  290. package/engine/skills/README.md +86 -0
  291. package/engine/skills/argent-dev/SKILL.md +41 -0
  292. package/engine/skills/internal.json +37 -0
  293. package/engine/skills/lktop-pm/SKILL.md +35 -0
  294. package/engine/skills/lktopapp-dev/SKILL.md +34 -0
  295. package/engine/skills/ml-config-guide/SKILL.md +50 -0
  296. package/engine/skills/ml-fulfillment-sop/SKILL.md +178 -0
  297. package/engine/skills/ml-fulfillment-sop/references/andes-components.md +69 -0
  298. package/engine/skills/ml-fulfillment-sop/references/cdp-port-mapping.md +35 -0
  299. package/engine/skills/ml-fulfillment-sop/references/feishu-base-fulfillment-schema.md +136 -0
  300. package/engine/skills/ml-fulfillment-sop/references/multi-window-concurrency.md +69 -0
  301. package/engine/skills/ml-fulfillment-sop/references/pitfalls.md +77 -0
  302. package/engine/skills/ml-fulfillment-sop/references/selectors.md +243 -0
  303. package/engine/skills/ml-fulfillment-sop/scripts/fulfillment.js +586 -0
  304. package/engine/skills/ml-fulfillment-sop/scripts/poll-and-execute.sh +271 -0
  305. package/engine/skills/ml-fulfillment-sop/templates/report.md +55 -0
  306. package/engine/skills/ml-invoice/SKILL.md +506 -0
  307. package/engine/skills/ml-invoice/references/pitfalls.md +268 -0
  308. package/engine/skills/ml-invoice/references/portal-flow.md +360 -0
  309. package/engine/skills/ml-invoice/references/template-fill-contract.md +133 -0
  310. package/engine/skills/ml-invoice-xzh/SKILL.md +133 -0
  311. package/engine/skills/ml-invoice-xzh/references/pitfalls.md +230 -0
  312. package/engine/skills/ml-store-daily-report/SKILL.md +224 -0
  313. package/engine/skills/ml-store-daily-report/scripts/README.md +25 -0
  314. package/engine/skills/ml-store-daily-report/scripts/extract.js +691 -0
  315. package/engine/skills/ml-store-daily-report/scripts/generate.js +299 -0
  316. package/engine/skills/pet-dev/SKILL.md +42 -0
  317. package/engine/skills/whyshu-pm/SKILL.md +35 -0
  318. package/engine/skills/whyshu-web-dev/SKILL.md +50 -0
  319. package/engine/version.mjs +1 -0
  320. package/engine/verticals/README.md +103 -0
  321. package/engine/verticals/_engine-words.baseline.txt +237 -0
  322. package/engine/verticals/_engine-words.json +19 -0
  323. package/engine/verticals/mercadolibre/README.md +43 -0
  324. package/engine/verticals/mercadolibre/config.json +709 -0
  325. package/engine/verticals/mercadolibre/flows.json +738 -0
  326. package/engine/verticals/mercadolibre/manifest.json +538 -0
  327. package/engine/verticals/mercadolibre/migrate-machine-state.mjs +138 -0
  328. package/engine/verticals/mercadolibre/preflight.py +143 -0
  329. package/engine/verticals/mercadolibre/prompts/daily-report.failure.md +69 -0
  330. package/engine/verticals/mercadolibre/prompts/daily-report.md +47 -0
  331. package/engine/verticals/mercadolibre/prompts/daily-report.scheduled.md +79 -0
  332. package/engine/verticals/mercadolibre/prompts/full.failure.md +125 -0
  333. package/engine/verticals/mercadolibre/prompts/full.md +91 -0
  334. package/engine/verticals/mercadolibre/prompts/wenyao-invoice.failure.md +88 -0
  335. package/engine/verticals/mercadolibre/prompts/wenyao-invoice.md +64 -0
  336. package/engine/verticals/mercadolibre/prompts/xzh-invoice.failure.md +91 -0
  337. package/engine/verticals/mercadolibre/prompts/xzh-invoice.md +46 -0
  338. package/package.json +22 -4
  339. package/requirements.txt +5 -0
  340. package/README.md +0 -3
@@ -0,0 +1,138 @@
1
+ /**
2
+ * `argent flow list` —— **注册表只读清单**(P1,2026-10-03)。
3
+ *
4
+ * 薄包:全部内容来自 `scripts/flow_registry.py --json`(**唯一真源**:引擎级 base + 各业务线的
5
+ * 合并/冲突检查/别名解析都由它做),本文件**不实现任何合并**,只做三件事:
6
+ * ① 用**已选定的解释器**去调它(`core/lib/python.mjs`,D3:不赌 PATH);
7
+ * ② 把合并结果整理成宿主好用的形状(流程 → 业务线/能力/角色/技能/数据面/触发器);
8
+ * ③ **如实回显**:读不到注册表 ⇒ `2 + python_unavailable|missing_declaration`,**绝不**报"没有流程"。
9
+ *
10
+ * ## 暂停开关:只做**存在性探测**,不重算路径
11
+ * 每条流程的 `trigger.pauseSwitch`(相对数据目录)与 `pauseSwitchLegacy`(数据目录根下的裸名)
12
+ * 都是**注册表声明**;本 verb 只在声明的路径上探一次"在不在"。⚠️ 权威判据仍是
13
+ * `scripts/flow_config.py` / `scripts/flow-trigger.sh`(迁移期两条都查)—— 输出里用
14
+ * `authoritative` 字段点名,免得被当成第二份判据。
15
+ *
16
+ * ## A1/A2
17
+ * · `0` 读到(流程 0 条 ⇒ `empty:true + no_match`,不是错误);
18
+ * · `2` 解释器不可用 / 注册表读不到 / 未知参数。
19
+ */
20
+ import { existsSync } from 'node:fs'
21
+ import { join } from 'node:path'
22
+ import { resolveDataDir } from './argent-datadir.mjs'
23
+ import { pickPython } from './python.mjs'
24
+ import { loadRegistry, resolveFlow } from './flow-registry.mjs'
25
+
26
+ /** 注册表文档 → 宿主友好的流程清单(纯函数,便于夹具)。 */
27
+ export function summarizeFlows(doc, { dataDir = '', exists = existsSync } = {}) {
28
+ const flows = (doc && doc.flows) || {}
29
+ const aliases = (doc && doc.aliases) || {}
30
+ const aliasTo = {}
31
+ for (const [from, to] of Object.entries(aliases)) (aliasTo[to] ||= []).push(from)
32
+ const verticals = {}
33
+ const rows = []
34
+ for (const [id, f] of Object.entries(flows)) {
35
+ const vertical = id.includes('.') ? id.split('.')[0] : null
36
+ const local = id.includes('.') ? id.split('.').slice(1).join('.') : id
37
+ const t = f.trigger || {}
38
+ const declared = t.pauseSwitch ? join(String(dataDir), String(t.pauseSwitch)) : null
39
+ const legacy = t.pauseSwitchLegacy ? join(String(dataDir), String(t.pauseSwitchLegacy)) : null
40
+ const paused = Boolean((declared && exists(declared)) || (legacy && exists(legacy)))
41
+ rows.push({
42
+ id, localId: local, vertical,
43
+ title: f.title || null,
44
+ role: f.role || null,
45
+ capability: f.capability || null,
46
+ skill: f.skill || null,
47
+ sourceKind: (f.source && f.source.kind) || null,
48
+ sourceIdentity: (f.source && f.source.identity) || null,
49
+ trigger: {
50
+ wake: t.wake ?? null, mode: t.mode ?? null, intervalSec: t.intervalSec ?? null,
51
+ pauseSwitch: t.pauseSwitch ?? null, pauseSwitchLegacy: t.pauseSwitchLegacy ?? null,
52
+ promptTemplate: t.promptTemplate ?? null,
53
+ log: t.log ?? null,
54
+ },
55
+ pause: {
56
+ paused, declaredPath: declared, declaredExists: Boolean(declared && exists(declared)),
57
+ legacyPath: legacy, legacyExists: Boolean(legacy && exists(legacy)),
58
+ authoritative: 'scripts/flow_config.py / scripts/flow-trigger.sh(本 verb 只在注册表声明的路径上探存在性,不重算路径)',
59
+ },
60
+ concurrency: f.concurrency || null,
61
+ guards: f.guards || null,
62
+ failuresEnabled: Boolean(f.failures && f.failures.enabled),
63
+ aliases: aliasTo[id] || [],
64
+ requiredFields: Array.isArray(f.required) ? f.required.length : 0,
65
+ })
66
+ if (vertical) verticals[vertical] = true
67
+ }
68
+ rows.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
69
+ return { rows, verticalIds: Object.keys(verticals).sort(), aliases }
70
+ }
71
+
72
+ /**
73
+ * verb 入口:`argent flow list [--json]`。
74
+ * @param {string[]} argv 子命令之后的参数(`list` 已由组 verb 消费)
75
+ * @param {{repo?:string, env?:object, py?:string, pyFailure?:string|null, doc?:object}} [deps]
76
+ */
77
+ export function runFlowListVerb(argv = [], deps = {}) {
78
+ for (const t of argv) {
79
+ if (t === '--json') continue
80
+ if (t === '--help' || t === '-h') return { json: { ok: true, empty: false, code: 0, reason: null, verb: 'flow', subcommand: 'list', usage: FLOW_LIST_USAGE }, human: FLOW_LIST_USAGE, stderr: '', exit: 0 }
81
+ if (t.startsWith('--')) return listFail(2, 'usage_error', `未知参数:${t}(用法:argent flow list [--json])`)
82
+ return listFail(2, 'usage_error', `多余的位置参数:${t}(用法:argent flow list [--json])`)
83
+ }
84
+ const repo = deps.repo || ''
85
+ const env = deps.env || process.env
86
+ const dataDir = deps.dataDir || resolveDataDir({ env }).dir
87
+ // 解释器:显式给了就用(夹具/已解析),否则探一次;探不到 ⇒ 下面的 loadRegistry 会走 py='' 并响亮失败
88
+ let py = deps.py !== undefined ? deps.py : null
89
+ let pyFailure = deps.pyFailure !== undefined ? deps.pyFailure : null
90
+ if (deps.doc === undefined && py === null) {
91
+ const p = pickPython({ env, dataDir, repo })
92
+ py = p.ok ? p.path : ''
93
+ pyFailure = p.ok ? null : p.diagnostics
94
+ }
95
+
96
+ let doc = deps.doc || null
97
+ if (!doc) {
98
+ if (pyFailure) return listFail(2, 'python_unavailable', pyFailure, { dataDir })
99
+ const reg = loadRegistry({ repo, py })
100
+ if (!reg.ok) {
101
+ return listFail(2, reg.reason === 'registry_script_missing' ? 'missing_declaration' : 'python_unavailable', reg.error, { dataDir })
102
+ }
103
+ doc = reg.doc
104
+ }
105
+ const { rows, verticalIds, aliases } = summarizeFlows(doc, { dataDir })
106
+ const json = {
107
+ ok: true, empty: rows.length === 0, code: 0, reason: rows.length ? null : 'no_match', verb: 'flow', subcommand: 'list',
108
+ source: 'scripts/flow_registry.py --json(合并 + 别名解析的唯一真源)',
109
+ dataDir,
110
+ count: rows.length,
111
+ verticals: verticalIds,
112
+ aliases,
113
+ flows: rows,
114
+ note: 'pause 只在注册表声明的路径上做存在性探测(权威判据见 pause.authoritative);'
115
+ + 'guards/concurrency 为注册表原文照录,判据仍在引擎脚本里。',
116
+ }
117
+ const human = [
118
+ `📋 流程清单(${rows.length} 条 | 业务线 ${verticalIds.join('、') || '(无)'})`,
119
+ ...rows.map((r) => ` · ${r.id.padEnd(34)} ${(r.title || '').padEnd(18)} 角色=${r.role || '—'} 能力=${r.capability || '—'}`
120
+ + ` 唤醒=${r.trigger.wake || '—'}${r.pause.paused ? ' ⏸ 已暂停开关' : ''}`),
121
+ rows.length ? null : ' (注册表里 0 条流程 —— 这不是错误,但要确认注册表是否读对了)',
122
+ ].filter(Boolean).join('\n')
123
+ return { json, human, stderr: '', exit: 0 }
124
+ }
125
+
126
+ function listFail(code, reason, error, echo = {}) {
127
+ return { json: { ok: false, empty: false, code, reason, error, verb: 'flow', subcommand: 'list', ...echo }, human: `❌ ${reason}:${error}`, stderr: '', exit: code }
128
+ }
129
+
130
+ /** 供组 verb 复用:别名解析(不复制 `resolveFlow` 的实现)。 */
131
+ export { resolveFlow }
132
+
133
+ export const FLOW_LIST_USAGE = [
134
+ '用法:argent flow list [--json]',
135
+ '',
136
+ '只读清单:流程(规范 id / 标题 / 角色 / 能力 / 技能 / 数据面 / 触发器 / 并发 / 守卫 / 别名)。',
137
+ '真源 = scripts/flow_registry.py --json(本 verb 不实现合并);暂停开关只做存在性探测。',
138
+ ].join('\n')
@@ -0,0 +1,48 @@
1
+ /**
2
+ * 注册表读取(core 侧薄包装)—— **唯一真源仍是 `scripts/flow_registry.py`**
3
+ * (base + 业务线的合并、冲突即 fail、别名解析都由它做;Node 侧入口 `scripts/lib/registry.mjs`)。
4
+ *
5
+ * 本模块只加两件内核需要的事,**不复制任何合并逻辑**:
6
+ * ① 用**已选定的解释器**去调它(不赌 PATH 上恰好有 python3);
7
+ * ② 把失败**分类**(脚本缺失 / 合并或解析失败)—— 2026-10-03 修 D3:此前 `core/lib/sop.mjs`
8
+ * 用空 `catch {}` 吞掉一切失败 ⇒ 注册表读不到时 `sop` 报"未命中"且 exit 0(假象)。
9
+ */
10
+ import { existsSync } from 'node:fs'
11
+ import { join } from 'node:path'
12
+ import { loadRegistryDoc, resolveFlowId } from '../../scripts/lib/registry.mjs'
13
+
14
+ /**
15
+ * 读合并后的注册表(永不抛)。
16
+ * @returns {{ok: true, doc: object} | {ok: false, reason: string, error: string}}
17
+ * reason:`registry_script_missing`(仓库不完整)/ `registry_failed`(合并或解析失败)
18
+ */
19
+ export function loadRegistry({ repo, py }) {
20
+ const script = join(repo, 'scripts', 'flow_registry.py')
21
+ if (!existsSync(script)) {
22
+ return { ok: false, reason: 'registry_script_missing', error: `注册表加载器不存在:${script}(仓库不完整?)` }
23
+ }
24
+ try {
25
+ return { ok: true, doc: loadRegistryDoc({ root: repo, py }) }
26
+ } catch (e) {
27
+ const stderr = String((e && e.stderr) || '').trim()
28
+ const tail = stderr.split('\n').map((l) => l.trim()).filter(Boolean).slice(-3).join(' / ')
29
+ return {
30
+ ok: false,
31
+ reason: 'registry_failed',
32
+ error: `注册表合并/解析失败:${(tail || String((e && e.message) || e)).slice(0, 400)}`,
33
+ }
34
+ }
35
+ }
36
+
37
+ /**
38
+ * 别名 → 规范 id,并给出**局部 id**(台账 caseId 的前缀约定是 `<flow>__<票号>`,用的是局部 id)。
39
+ * 别名解析走 `resolveFlowId`(加载器的唯一实现),本函数不自己维护任何映射。
40
+ */
41
+ export function resolveFlow({ input, doc }) {
42
+ const raw = String(input || '')
43
+ const canonical = resolveFlowId(raw, doc)
44
+ const flows = (doc && doc.flows) || {}
45
+ const known = Object.prototype.hasOwnProperty.call(flows, canonical)
46
+ const local = canonical.includes('.') ? canonical.split('.').slice(1).join('.') : canonical
47
+ return { input: raw, canonical, local, known }
48
+ }
@@ -0,0 +1,247 @@
1
+ /**
2
+ * `argent init` —— **数据目录画像**(P1,2026-10-03)。
3
+ *
4
+ * 客户 agent 装完 CLI 后的第一个问题永远是"我该写哪儿、我这台机器现在能跑什么"。
5
+ * 本 verb 把这件事**一次答完**,且**只读**(除 `--apply` 建目录那一步)。
6
+ *
7
+ * ## 它答什么(每一项都指向唯一判据,本文件不复制判据)
8
+ * | 画像块 | 唯一判据 |
9
+ * |---|---|
10
+ * | 数据目录(含来源/是否隔离/存在性/可写性) | `core/lib/argent-datadir.mjs#resolveDataDir`(判断⑧) |
11
+ * | 解释器候选 | `core/lib/python.mjs#pickPython`(D3 的多候选 + 响亮失败,与 `flow-trigger.sh` 的 `pick_py()` 同源) |
12
+ * | 已装技能 | 技能根的**存在性计数**(`<数据目录>/skills`、`<dsh home>/skills`、仓库 `skills/`);格式判据在 `scripts/fix-skill-frontmatter.py` |
13
+ * | 许可状态 | `core/lib/license-verdict.mjs#licenseVerdict`(判定点唯一在 CLI 进程内,§3.5①) |
14
+ * | 通道可用性 | `core/lib/notify/index.mjs#listChannels`(P2 端口;预留位如实标 `implemented:false`) |
15
+ * | 宿主档事实(dsh 在不在 / 定时器 / 补丁) | **只探测、如实上报**;缺宿主是**设计**不是 FAIL(§4.2 M1 判据③) |
16
+ *
17
+ * ## A1/A2/A3
18
+ * · A1:`0` 画像成功(**缺 dsh / 缺解释器 / 无许可都不算失败** —— 它们是"事实",画像的职责是如实报出来);
19
+ * `2` 用法错误(未知参数 / `--apply` 时目录建不出来 `${datadir_unwritable}`)。
20
+ * · A2:`ok/empty/code/reason` 恒在;回显**实际生效**的数据目录、来源、以及 `--apply` 的 `applied`。
21
+ * · A3:**默认 dry-run**(只画像,**不建目录**);`--apply` 才 `mkdir -p`。dry-run 零副作用。
22
+ *
23
+ * ## ⛔ 它绝不做什么(写进代码与输出,两头都声明)
24
+ * ① 不写 `~/.dsh/settings.yaml`(宿主配置归宿主;改它会让 dsh 自身 schema 重写时丢段);
25
+ * ② 不写/不碰 credentials(`~/.dsh/.credentials.yaml`);
26
+ * ③ **不发明数据目录** —— 只落 `resolveDataDir()` 给出的那一个(另建目录 = 第二份判据,
27
+ * 那正是 `core/lib/argent-datadir.mjs:1-23` 记录过的事故形态:隔离实例写进生产)。
28
+ *
29
+ * ## 默认目录已定(真源在 `core/lib/argent-datadir.mjs`,本文件只**如实呈现**)
30
+ * 实施方案 §P1③ / §6.2 **Q6 选项 B**(2026-10-03 批准、**已落地(两步)**):
31
+ * · **存在即沿用**:`~/.dsh/argent` 已存在 ⇒ 它就是默认目录(老机器**不**被误判隔离);
32
+ * · 否则(新装、无宿主)⇒ `~/.argent`。
33
+ * 本 verb 把两个候选路径 + 谁在用 + 老位置在不在**一次打全**(`dataDir.legacyDir` /
34
+ * `legacyDirExists` / `noHostDefault`)—— 因为判据是"老位置在不在",所以**先在 `~/.argent`
35
+ * 落过数据、之后才出现 `~/.dsh/argent`** 的机器会切回老位置(可能读到空台账);
36
+ * 要钉死目录就用 `ARGENT_DATA_DIR`(或 `DSH_HOME`)显式指定。判据**不在这里**,只在核心里。
37
+ */
38
+ import { existsSync, mkdirSync, readdirSync, statSync } from 'node:fs'
39
+ import { join } from 'node:path'
40
+ import { NO_HOST_DATA_DIR_SEGMENTS, defaultDataDir, describeDataDir, dshHomeOf, legacyDataDir, legacyDataDirExists, osHome, resolveDataDir } from './argent-datadir.mjs'
41
+ import { pickPython } from './python.mjs'
42
+ import { licenseVerdict } from './license-verdict.mjs'
43
+ import { listChannels } from './notify/index.mjs'
44
+
45
+ /** 可写性探测:dry-run **不建目录**,所以用"最近的已存在祖先"判断(只读)。 */
46
+ function nearestExisting(dir) {
47
+ let cur = String(dir)
48
+ const seen = []
49
+ while (cur && cur !== '/' && !existsSync(cur)) { seen.push(cur); cur = join(cur, '..') }
50
+ return { ancestor: existsSync(cur) ? cur : null, missing: seen.length }
51
+ }
52
+
53
+ /** 一个技能根画像(存在性 + 直接子目录里带 SKILL.md 的计数)。 */
54
+ export function skillRootProfile(dir) {
55
+ const out = { dir, exists: false, skills: [], count: 0, skipped: [] }
56
+ if (!existsSync(dir)) return out
57
+ out.exists = true
58
+ let entries = []
59
+ try { entries = readdirSync(dir, { withFileTypes: true }) } catch { return out }
60
+ for (const e of entries) {
61
+ if (!e.isDirectory()) continue
62
+ const has = existsSync(join(dir, e.name, 'SKILL.md'))
63
+ if (has) out.skills.push(e.name)
64
+ else out.skipped.push(e.name)
65
+ }
66
+ out.skills.sort()
67
+ out.count = out.skills.length
68
+ return out
69
+ }
70
+
71
+ /** 许可画像(**不复制判据**:调 licenseVerdict;它自己读 `$ARGENT_DATA_DIR/license.json`)。 */
72
+ export function licenseProfile({ env = process.env, now = Date.now(), verdict = null } = {}) {
73
+ const v = verdict || licenseVerdict({ env, now })
74
+ return {
75
+ state: v.state, exit: v.exit, reason: v.reason || null,
76
+ file: v.path || null, exists: Boolean(v.path && existsSync(v.path)),
77
+ expiresAt: v.expiresAt || null, graceDays: v.graceDays ?? null, graceUntil: v.graceUntil || null,
78
+ allowed: v.allowed ?? null,
79
+ device: v.device ? { present: v.device.present, signed: v.device.signed, state: v.device.state, rebindsLeft: v.device.rebindsLeft ?? null } : null,
80
+ note: v.state === 'denied' ? '许可被拒 ⇒ 写类 verb 在 enforce 下会被拦(默认 observe 只留证据)' : null,
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 数据目录画像(含三级链逐级的命中情况)。
86
+ * @returns {object} 形状与 `resolveDataDir()` 同源 + 画像字段
87
+ */
88
+ export function dataDirProfile({ env = process.env, home = null } = {}) {
89
+ const r = resolveDataDir(home ? { env, home } : { env })
90
+ const noHostDefault = defaultDataDir(env, home || undefined)
91
+ const legacyDir = legacyDataDir(env, home || undefined)
92
+ const legacyExists = legacyDataDirExists(env, home || undefined)
93
+ const newInstallDir = join(osHome(env), ...NO_HOST_DATA_DIR_SEGMENTS)
94
+ const probe = nearestExisting(r.dir)
95
+ return {
96
+ dir: r.dir,
97
+ raw: r.raw,
98
+ source: r.source,
99
+ isolated: r.isolated,
100
+ defaultDir: r.defaultDir,
101
+ dshHome: r.dshHome,
102
+ exists: existsSync(r.dir),
103
+ isDirectory: existsSync(r.dir) && statSync(r.dir).isDirectory(),
104
+ nearestExistingAncestor: probe.ancestor,
105
+ missingLevels: probe.missing,
106
+ // 默认目录的两个候选(判据 = "老位置在不在",真源在 core/lib/argent-datadir.mjs)
107
+ noHostDefault,
108
+ noHostDefaultExists: existsSync(noHostDefault),
109
+ legacyDir,
110
+ legacyDirExists: legacyExists,
111
+ newInstallDir,
112
+ chain: [
113
+ { level: 'ARGENT_DATA_DIR', hit: r.source === 'ARGENT_DATA_DIR', value: env.ARGENT_DATA_DIR ?? null },
114
+ { level: 'DSH_HOME', hit: r.source === 'DSH_HOME', value: env.DSH_HOME ? join(dshHomeOf(env), 'argent') : null },
115
+ { level: 'default', hit: r.source === 'default', value: r.defaultDir },
116
+ ],
117
+ // Q6 已定(选项 B,两步都落地)⇒ 不再是"待拍板",但把两个候选与"谁在用"如实打出来
118
+ defaultDirDecision: {
119
+ id: 'Q6',
120
+ option: 'B',
121
+ landed: true,
122
+ rule: '存在即沿用:`~/.dsh/argent` 已存在 ⇒ 默认就是它;否则新装默认 `~/.argent`',
123
+ using: legacyExists ? 'legacy' : 'new-install',
124
+ legacyDir,
125
+ legacyDirExists: legacyExists,
126
+ newInstallDir,
127
+ note: legacyExists
128
+ ? '老位置存在 ⇒ 沿用(老机器不会被误判隔离;失败回路照常运行)'
129
+ : '老位置不存在 ⇒ 走新装默认 `~/.argent`(宿主无关位置)',
130
+ hazard: '判据是"老位置在不在" ⇒ 先在 `~/.argent` 落过数据、之后才出现 `~/.dsh/argent` 的机器会切回老位置(可能读到空台账);'
131
+ + '要钉死目录用 ARGENT_DATA_DIR / DSH_HOME 显式指定',
132
+ },
133
+ describe: describeDataDir(r),
134
+ }
135
+ }
136
+
137
+ /** 宿主档事实(只探测;缺宿主 = 设计,不是 FAIL)。 */
138
+ export function hostProfile({ env = process.env, repo = '', which = null } = {}) {
139
+ const dshHome = dshHomeOf(env)
140
+ const dshBin = which ? which('dsh') : null
141
+ return {
142
+ dshHome,
143
+ dshHomeExists: existsSync(dshHome),
144
+ dshBin: dshBin || null,
145
+ note: '宿主档事实:缺 dsh **不是 FAIL**(M1 的判据③明写"无 dsh 不报 FAIL");'
146
+ + '定时器/补丁的权威判据在宿主侧生成器(`bash scripts/flow-timers.sh status`、`node scripts/dsh-upgrade.mjs --patches-only`),本 verb 不重算。',
147
+ }
148
+ }
149
+
150
+ /**
151
+ * verb 入口:`argent init [--apply] [--json]`。
152
+ * @param {string[]} argv
153
+ * @param {{repo?:string, env?:object, apply?:boolean, deps?:object}} [deps]
154
+ */
155
+ export function runInitVerb(argv = [], deps = {}) {
156
+ const bool = []
157
+ for (const t of argv) {
158
+ if (t === '--json') continue
159
+ if (t === '--apply' || t === '--dry-run' || t === '--help' || t === '-h') { bool.push(t); continue }
160
+ return initFail(2, 'usage_error', `未知参数:${t}(用法:argent init [--apply] [--json])`)
161
+ }
162
+ if (bool.includes('--help') || bool.includes('-h')) {
163
+ return { json: { ok: true, empty: false, code: 0, reason: null, verb: 'init', usage: INIT_USAGE }, human: INIT_USAGE, stderr: '', exit: 0 }
164
+ }
165
+ const apply = bool.includes('--apply') && !bool.includes('--dry-run')
166
+ const env = deps.env || process.env
167
+ const repo = deps.repo || ''
168
+ const dryRun = !apply
169
+
170
+ const dd = dataDirProfile({ env })
171
+ const py = pickPython({ env, dataDir: dd.dir, repo })
172
+ const lic = licenseProfile({ env, now: deps.now ? deps.now.getTime() : Date.now(), verdict: deps.verdict || null })
173
+ const channels = listChannels()
174
+ const host = hostProfile({ env, repo, which: deps.which || null })
175
+
176
+ const roots = [
177
+ { id: 'data', dir: join(dd.dir, 'skills') },
178
+ { id: 'dsh', dir: join(dd.dshHome, 'skills') },
179
+ ...(repo ? [{ id: 'repo', dir: join(repo, 'skills') }] : []),
180
+ ].map((r) => ({ id: r.id, ...skillRootProfile(r.dir) }))
181
+
182
+ let applied = false
183
+ let created = false
184
+ let writeError = null
185
+ if (apply) {
186
+ try {
187
+ const existed = existsSync(dd.dir)
188
+ mkdirSync(dd.dir, { recursive: true })
189
+ applied = true
190
+ created = !existed
191
+ } catch (e) {
192
+ return initFail(2, 'datadir_unwritable', `数据目录建不出来:${dd.dir}(${(e && e.message) || e})`, { dataDir: dd.dir, dryRun })
193
+ }
194
+ }
195
+
196
+ const json = {
197
+ ok: true, empty: false, code: 0, reason: null, verb: 'init',
198
+ dryRun, applied, created,
199
+ dataDir: { ...dd, existsAfter: existsSync(dd.dir), wouldCreate: !existsSync(dd.dir) },
200
+ interpreter: {
201
+ ok: py.ok, path: py.ok ? py.path : null, source: py.source || null, version: py.version || null,
202
+ candidates: (py.tried || []).map((t) => ({ path: t.path, source: t.source, ok: t.ok, reason: t.reason, version: t.version || null })),
203
+ diagnostics: py.ok ? null : py.diagnostics,
204
+ },
205
+ skills: { roots, total: roots.reduce((n, r) => n + r.count, 0) },
206
+ license: lic,
207
+ channels: { available: channels.filter((c) => c.implemented).map((c) => c.channel), reserved: channels.filter((c) => !c.implemented).map((c) => c.channel), all: channels },
208
+ host,
209
+ // 如实声明"没写什么"(不是客套:这三条是本次交付的红线,写进输出才可被调用方核验)
210
+ writes: {
211
+ settings: false, credentials: false, files: created ? [dd.dir] : [],
212
+ note: '本 verb 只创建数据目录本身(--apply 时);**不写** ~/.dsh/settings.yaml、**不碰** credentials、不改任何宿主配置。',
213
+ },
214
+ // Q6(默认目录)**已定并落地**:不再是"待拍板",把判据与两个候选如实打出来(真源在核心里)
215
+ defaultDirDecision: dd.defaultDirDecision,
216
+ pendingDecisions: [],
217
+ writeError,
218
+ note: dryRun ? 'dry-run(默认):只画像,**未创建任何目录**;要建目录加 --apply' : `已 --apply:数据目录 ${created ? '新建' : '已存在'}(${dd.dir})`,
219
+ }
220
+ const human = [
221
+ `📇 Argent 画像(${dryRun ? 'dry-run,未建目录' : '已 --apply'})`,
222
+ ` 数据目录:${dd.describe}`,
223
+ ` 存在=${dd.existsAfter ?? dd.exists} 来源=${dd.source} 隔离=${dd.isolated} 默认=${dd.defaultDir}`,
224
+ ` 默认目录判据(§6.2 Q6 选项 B,已落地):老位置 ${dd.legacyDir} ${dd.legacyDirExists ? '**在**⇒沿用' : '不在⇒新装用 ' + dd.newInstallDir}`
225
+ + `(${dd.defaultDirDecision.using})`,
226
+ ` 解释器:${py.ok ? `${py.path}(${(py.version || []).join('.')},来源 ${py.source})` : '❌ 不可用'}`,
227
+ py.ok ? null : (py.diagnostics || '').split('\n').map((l) => ` ${l}`).join('\n'),
228
+ ` 技能:${roots.map((r) => `${r.id}=${r.count}${r.exists ? '' : '(缺)'}`).join(' ')}`,
229
+ ` 许可:${lic.state}(${lic.reason || 'ok'})${lic.expiresAt ? ` 到期 ${lic.expiresAt}` : ''}`,
230
+ ` 通道:可用 ${json.channels.available.join('、') || '(无)'}|预留位 ${json.channels.reserved.join('、') || '(无)'}`,
231
+ ` 宿主:dsh home ${host.dshHome}${host.dshHomeExists ? '' : '(不存在)'}(缺宿主不是 FAIL)`,
232
+ ` ⛔ 未写 settings.yaml / credentials;本 verb 只动数据目录本身`,
233
+ ].filter(Boolean).join('\n')
234
+ return { json, human, stderr: '', exit: 0 }
235
+ }
236
+
237
+ function initFail(code, reason, error, echo = {}) {
238
+ return { json: { ok: false, empty: false, code, reason, error, verb: 'init', ...echo }, human: `❌ ${reason}:${error}`, stderr: '', exit: code }
239
+ }
240
+
241
+ export const INIT_USAGE = [
242
+ '用法:argent init [--apply] [--json]',
243
+ '',
244
+ '数据目录画像(只读):数据目录(来源/隔离/存在性)、解释器候选、已装技能、许可状态、通道可用性、宿主档事实。',
245
+ '默认 **dry-run**(不建目录);--apply 才创建数据目录本身。',
246
+ '⛔ 不写 ~/.dsh/settings.yaml、不碰 credentials、不改宿主配置。',
247
+ ].join('\n')
@@ -0,0 +1,124 @@
1
+ /**
2
+ * 台账查询(0.3 P1,2026-10-03)—— **CLI 与 MCP 用同一套**(修 D1)。
3
+ *
4
+ * D1 实测(复现原文):`node core/cli/argent.mjs ledger --tail 3` 在 289849 字节的真实台账上返回
5
+ * {"ok": true, "count": 0, "rows": []}
6
+ * —— "唯一真相"说谎。两处根因:
7
+ * ① `args[args.indexOf('--flow') + 1]`:`--flow` 不存在时 `indexOf` = −1 ⇒ 取到 `args[0]`(`--tail`)当流程名过滤;
8
+ * ② **先 `slice(-tail)` 再 `filter`** ⇒ 即使给了 `--flow`,也只在该尾部窗口内筛。
9
+ * 本模块把顺序钉死:**读全量 → 过滤 → 再取末尾 N 行**;并把"实际生效的过滤条件"
10
+ * (`flow` / `flowPrefix` / `tail` / `matched`)一并回显 —— 空结果必须能一眼分辨
11
+ * "真没有" 与 "被过滤/被截断掉了"(这正是 D1 最贵的形态:看起来空、其实是过滤)。
12
+ *
13
+ * 匹配口径与 `scripts/flow_ledger.py --status --flow` 一致:`caseId` 前缀 `<flow>__`
14
+ * (台账里写的是**局部 id**:`full__…`/`wenyao-invoice__…`;同时容忍规范 id 前缀,防假空)。
15
+ */
16
+ import { existsSync, readFileSync } from 'node:fs'
17
+ import { loadRegistry, resolveFlow } from './flow-registry.mjs'
18
+
19
+ export const DEFAULT_TAIL = 5
20
+
21
+ /** `--tail` 解析:非 ≥1 的整数 ⇒ 回落到缺省,并**说明**(不静默用 `slice(-0)` 把全量当"末尾 0 行")。 */
22
+ export function parseTail(input, dflt = DEFAULT_TAIL) {
23
+ if (input === undefined || input === null || String(input).trim() === '') return { tail: dflt, note: '' }
24
+ const n = Number(String(input).trim())
25
+ if (!Number.isFinite(n) || n < 1) {
26
+ return { tail: dflt, note: `--tail ${JSON.stringify(String(input))} 不是 ≥1 的整数 ⇒ 用默认 ${dflt}` }
27
+ }
28
+ return { tail: Math.floor(n), note: '' }
29
+ }
30
+
31
+ /** 台账文本 → 事件行(坏行保留为 `{raw}`,绝不丢掉证据)。 */
32
+ export function parseLedger(text) {
33
+ return String(text || '').split('\n').map((l) => l.trim()).filter(Boolean)
34
+ .map((l) => { try { return JSON.parse(l) } catch { return { raw: l } } })
35
+ }
36
+
37
+ /** caseId 是否属于该流程:`<prefix>__…`(或恰好等于 prefix)。 */
38
+ export function caseIdMatches(caseId, filter) {
39
+ const cid = String(caseId || '')
40
+ if (!cid || !filter) return false
41
+ return (filter.prefixes || []).some((p) => p && (cid === p || cid.startsWith(`${p}__`)))
42
+ }
43
+
44
+ /**
45
+ * 把 `--flow` 解析成"实际生效的过滤条件"。
46
+ *
47
+ * **best-effort**:台账是只读的"唯一真相",不能因为解释器缺失/注册表坏了就整个查不了;
48
+ * 但失败原因会写进 `note` 回显(**不是**空 catch 吞掉)—— 这是 D3 ③ 的口径。
49
+ */
50
+ export function resolveLedgerFlow(input, { repo, py, pyFailure = null } = {}) {
51
+ const literal = String(input || '')
52
+ const literalFilter = (why) => ({
53
+ flow: literal,
54
+ prefixes: [literal],
55
+ known: false,
56
+ note: `--flow ${literal} 未经注册表解析(${why})⇒ 只按字面 caseId 前缀 \`${literal}__\` 匹配;`
57
+ + '可用流程 id 见 python3 scripts/flow_registry.py --list',
58
+ })
59
+ if (pyFailure) return literalFilter('解释器不可用')
60
+ const reg = loadRegistry({ repo, py })
61
+ if (!reg.ok) return literalFilter(reg.reason)
62
+ const r = resolveFlow({ input: literal, doc: reg.doc })
63
+ if (!r.known) {
64
+ return {
65
+ flow: r.canonical,
66
+ prefixes: [r.local, r.canonical],
67
+ known: false,
68
+ note: `流程 \`${literal}\` 不在注册表里(规范 id \`${r.canonical}\`)⇒ 按前缀匹配;`
69
+ + '可能是拼写错误或该流程未安装,见 python3 scripts/flow_registry.py --list',
70
+ }
71
+ }
72
+ return { flow: r.canonical, prefixes: [r.local, r.canonical], known: true, note: '' }
73
+ }
74
+
75
+ /**
76
+ * 台账查询(纯逻辑 + 注入式读文件;CLI/MCP 共用)。
77
+ * 顺序:**读全量 → 过滤 → 取末尾 N 行**(D1 的根因②)。
78
+ *
79
+ * 2026-10-03(P1 前置④):补 `empty` / `code` / `reason`,让 A2 的真值表在 ledger 上也**判得出来** ——
80
+ * `count === 0 ⇒ empty:true`(`reason:"no_match"`);台账文件不在 ⇒ `ok:false, code:1, reason:"no_ledger"`
81
+ * (**不**降级成 empty:那正是 D1 最贵的形态,把"数据目录不对"伪装成"没有事件")。
82
+ */
83
+ export function queryLedger({ file, flow = '', tailInput = '', resolveFilter = null,
84
+ readFile = (p) => readFileSync(p, 'utf8') } = {}) {
85
+ const t = parseTail(tailInput)
86
+ if (!existsSync(file)) {
87
+ return {
88
+ ok: false, empty: false, code: 1, reason: 'no_ledger',
89
+ error: `台账不存在:${file}`,
90
+ flow: null, flowPrefix: null, flowKnown: null, tail: t.tail, matched: 0, count: 0, rows: [],
91
+ }
92
+ }
93
+ const notes = []
94
+ if (t.note) notes.push(t.note)
95
+
96
+ const flowInput = String(flow || '').trim()
97
+ let filter = null
98
+ if (flowInput) {
99
+ filter = resolveFilter
100
+ ? resolveFilter(flowInput)
101
+ : { flow: flowInput, prefixes: [flowInput], known: false, note: '' }
102
+ if (filter.note) notes.push(filter.note)
103
+ }
104
+
105
+ const rows = parseLedger(readFile(file))
106
+ const matched = filter ? rows.filter((r) => caseIdMatches(r.caseId, filter)) : rows
107
+ const out = matched.slice(-t.tail) // ← 过滤**之后**才截断
108
+ const empty = out.length === 0
109
+ return {
110
+ ok: true,
111
+ empty,
112
+ code: 0,
113
+ // 空结果也必须能判:`no_match` = 真没有(或被过滤/被截断 —— 靠 matched/tail 分辨)
114
+ reason: empty ? 'no_match' : null,
115
+ flow: filter ? filter.flow : null,
116
+ flowPrefix: filter ? (filter.prefixes || [])[0] || null : null,
117
+ flowKnown: filter ? Boolean(filter.known) : null,
118
+ tail: t.tail,
119
+ matched: matched.length, // 过滤后、截断前的行数(分辨"真没有"与"被截断")
120
+ count: out.length,
121
+ rows: out,
122
+ ...(notes.length ? { note: notes.join(';') } : {}),
123
+ }
124
+ }