@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,355 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * check-1.0-criteria.mjs —— 《Argent 1.0 退出判据与版本规则》§2 的**计数自洽**门禁(只读)
4
+ *
5
+ * ## 为什么有它(2026-10-05 实测到真漂移)
6
+ * §2.0「八面总览」是**手写的汇总**(每面条数 + 🔴/🟡/✅ 计数 + 卡点一句话),
7
+ * 而**唯一真源**是 §2.1–§2.8 的判据行。两处靠人抄 ⇒ 已经漂过两次:
8
+ * · 运营面写「🔴 1 / 🟡 4 / ✅ 0 | 无只读运营视图(只能 `mongosh`)」——
9
+ * 判据行实为 **🟡 5**(O2 的只读视图早已落地,服务端只读接口也已上线);
10
+ * · 文档与官网面写「官网仍是 0.9.9 口径」—— P2 行的那两条事实依据(落地页 0.9.9 /
11
+ * 售卖已退役角色)已被官网改版替换。
12
+ * ⇒ 本门禁把「抄」变成「算」:**只读解析文档**,逐面复算并比对。
13
+ *
14
+ * ## 判据(唯一真源 = §2.1–§2.8 的判据行;本脚本不改任何文件)
15
+ * | # | 判据 | 说明 |
16
+ * |---|---|---|
17
+ * | ① | 判据条数 == **40** | {@link EXPECTED_TOTAL},与 §2.0 合计行、文末图例「1.0 = 40 条全 ✅」同源 |
18
+ * | ② | 每面:实得条数 == §2.0 声明条数 | 少一行/多一行当场点名 |
19
+ * | ③ | 每面:实得 🔴/🟡/✅ == §2.0 声明计数 | 本门禁的**主判据** |
20
+ * | ④ | 八面计数之和 == §2.0 合计行 | 合计行不是"另算一份",是八面之和 |
21
+ * | ⑤ | 每面「主要卡点」非空 | 不许留白,也不许只写占位符 `—` |
22
+ * | ⑥ | 每行「现状」格的首个符号 ∈ {🔴,🟡,✅} | `—` 仅限**显式写明不适用**的行(单独报告、不计数) |
23
+ * | ⑦ | 判据行 ID = 面字母 + 从 1 起的连续编号 | 缺行 / 串面 / 重复号当场点名 |
24
+ *
25
+ * ## 用法
26
+ * ```bash
27
+ * node scripts/check-1.0-criteria.mjs # 只报告(**默认恒 exit 0**)
28
+ * node scripts/check-1.0-criteria.mjs --strict # 有问题 ⇒ exit 1(CI 判据 `1.0-criteria` 用这个)
29
+ * node scripts/check-1.0-criteria.mjs --json # 机器可读(给别的脚本/记录用)
30
+ * node scripts/check-1.0-criteria.mjs --file <p> # 换文档(夹具/自测用;默认见 {@link DEFAULT_DOC})
31
+ * ```
32
+ * 退出码:`0` 检查跑完(默认模式即便发现问题也是 0)|`1` `--strict` 且有问题 |`2` 用法/环境错误(文档读不到)。
33
+ *
34
+ * ## 纪律
35
+ * · **只读**:不写文件、不读凭据、不联网、不碰数据目录(数据目录判据只有 `core/lib/argent-datadir.mjs` 一份,
36
+ * 本脚本根本不需要它 —— 它只读仓库里的一份 Markdown);
37
+ * · **同一份判据进 CI**:`bash scripts/ci-run.sh --only 1.0-criteria`;清单见 `docs/CI-自建runner.md` §2。
38
+ */
39
+ import { readFileSync } from 'node:fs'
40
+ import { dirname, resolve } from 'node:path'
41
+ import { fileURLToPath } from 'node:url'
42
+
43
+ const HERE = dirname(fileURLToPath(import.meta.url))
44
+ const REPO = resolve(HERE, '..')
45
+
46
+ /** 1.0 的判据总条数(八面之和;改它必须同批改 §2.0 合计行与文末图例 —— 本门禁会一起核)。 */
47
+ export const EXPECTED_TOTAL = 40
48
+
49
+ /** 允许的状态符号(顺序 = 报告顺序)。 */
50
+ export const STATUS = ['🔴', '🟡', '✅']
51
+
52
+ /** 八面的字母顺序(§2.1 → §2.8)。 */
53
+ export const FACE_ORDER = ['D', 'L', 'C', 'H', 'G', 'O', 'B', 'P']
54
+
55
+ /** 默认文档(相对仓库根)。 */
56
+ export const DEFAULT_DOC = 'docs/Argent-1.0-退出判据与版本规则.md'
57
+
58
+ // ══════════════════════════════════════════════════════════════════════════
59
+ // 一、解析(纯函数:吃文本、吐结构 —— 便于夹具自测与复用)
60
+ // ══════════════════════════════════════════════════════════════════════════
61
+
62
+ /** Markdown 表格行 → 单元格数组(去首尾竖线;**不**处理转义 —— 本表约定单元格内不写裸 `|`)。 */
63
+ export function splitRow(line) {
64
+ let s = String(line).trim()
65
+ if (!s.startsWith('|')) return null
66
+ s = s.slice(1)
67
+ if (s.endsWith('|')) s = s.slice(0, -1)
68
+ return s.split('|').map((c) => c.trim())
69
+ }
70
+
71
+ /** 去掉单元格里的粗体标记,便于取首符号/判空。 */
72
+ const plain = (s) => String(s == null ? '' : s).replace(/\*\*/g, '').trim()
73
+
74
+ /** 取「现状」格的首个状态符号:返回 `{ kind: 'status'|'na'|'illegal'|'empty', symbol }`。 */
75
+ export function leadingStatus(cell) {
76
+ const c = plain(cell)
77
+ if (c === '') return { kind: 'empty', symbol: '' }
78
+ const ch = Array.from(c)[0]
79
+ if (STATUS.includes(ch)) return { kind: 'status', symbol: ch }
80
+ if (ch === '—' || ch === '-') {
81
+ // 图例允许「显式写明 `—` 及其理由」的行 ⇒ 单独报告,不计数;但**必须**有理由文字。
82
+ const reason = c.replace(/^[—\-]\s*/, '').trim()
83
+ return { kind: 'na', symbol: ch, reason }
84
+ }
85
+ return { kind: 'illegal', symbol: ch }
86
+ }
87
+
88
+ /** 取「现状」格里的 `符号 + 数字` 对(缺的类别按 0 算,便于两种书写风格都能读)。 */
89
+ export function countsOf(cell) {
90
+ const out = { '🔴': 0, '🟡': 0, '✅': 0 }
91
+ const re = /(🔴|🟡|✅)\s*\*{0,2}\s*(\d+)/gu
92
+ let m
93
+ while ((m = re.exec(String(cell))) !== null) out[m[1]] = Number(m[2])
94
+ return out
95
+ }
96
+
97
+ const sameCounts = (a, b) => STATUS.every((s) => (a[s] || 0) === (b[s] || 0))
98
+ const fmtCounts = (c) => STATUS.map((s) => `${s} ${c[s] || 0}`).join(' / ')
99
+
100
+ /**
101
+ * 解析整份文档。
102
+ * 返回 `{ faces, overview, legend }`:
103
+ * · `faces[]` = §2.1–§2.8 的判据行(**唯一真源**)+ 逐面实得计数;
104
+ * · `overview` = §2.0 八面总览(声明条数/计数/卡点)+ 合计行;
105
+ * · `legend` = 文末图例里的「1.0 = N 条全 ✅」。
106
+ */
107
+ export function parseDoc(text) {
108
+ const lines = String(text).split('\n')
109
+
110
+ // ── 定位八面的小节标题(`### 2.<n> <名>面(<字母>)`)────────────────────
111
+ const heads = []
112
+ lines.forEach((line, i) => {
113
+ const m = /^###\s+2\.(\d+)\s+(.+?)面(([A-Z]))\s*$/.exec(line.trim())
114
+ if (m) heads.push({ line: i, idx: Number(m[1]), name: m[2], letter: m[3] })
115
+ })
116
+
117
+ const faces = []
118
+ for (let h = 0; h < heads.length; h++) {
119
+ const head = heads[h]
120
+ // 小节范围 = 本标题之后、**下一个任意级标题**之前(§2.8 后面紧跟 `---` 与 `## 3.`;
121
+ // ⛔ 不能用"下一个判据小节标题"当边界 —— 那样最后一面会一路吃到 §6 决策表的 `Q*` 行)。
122
+ let end = lines.length
123
+ for (let i = head.line + 1; i < lines.length; i++) {
124
+ if (/^#{1,4}\s/.test(lines[i].trim())) { end = i; break }
125
+ }
126
+ const rows = []
127
+ for (let i = head.line + 1; i < end; i++) {
128
+ const t = lines[i].trim()
129
+ if (!t.startsWith('|')) continue
130
+ const c = splitRow(t)
131
+ if (!c || c.length < 5) continue
132
+ const m = /^([A-Z])(\d+)$/.exec(plain(c[0]))
133
+ if (!m) continue
134
+ // 判据行布局:`| ID | 判据 | 现状 | 缺口 | 验收 |` ⇒ 现状 = c[2]
135
+ rows.push({ lineNo: i + 1, id: `${m[1]}${m[2]}`, letter: m[1], num: Number(m[2]), cell: c[2] })
136
+ }
137
+ const tally = { '🔴': 0, '🟡': 0, '✅': 0 }
138
+ let na = 0
139
+ const statuses = []
140
+ for (const r of rows) {
141
+ const st = leadingStatus(r.cell)
142
+ r.status = st
143
+ if (st.kind === 'status') tally[st.symbol]++
144
+ else if (st.kind === 'na') na++
145
+ statuses.push(st)
146
+ }
147
+ faces.push({ ...head, rows, tally, na, statuses, count: rows.length })
148
+ }
149
+
150
+ // ── 定位 §2.0 八面总览 + 合计行 ─────────────────────────────────────────
151
+ const oStart = lines.findIndex((l) => /^###\s+2\.0\s/.test(l.trim()))
152
+ let oEnd = lines.length
153
+ for (let i = Math.max(oStart, 0) + 1; i < lines.length; i++) {
154
+ if (/^#{2,3}\s/.test(lines[i].trim())) { oEnd = i; break }
155
+ }
156
+ const overview = { rows: [], total: null }
157
+ if (oStart >= 0) {
158
+ for (let i = oStart + 1; i < oEnd; i++) {
159
+ const t = lines[i].trim()
160
+ if (!t.startsWith('|')) continue
161
+ const c = splitRow(t)
162
+ if (!c || c.length < 5) continue
163
+ if (/^\**合计\**$/.test(plain(c[1]))) {
164
+ overview.total = { lineNo: i + 1, declaredTotal: Number(plain(c[2]).replace(/[^0-9]/g, '')), counts: countsOf(c[3]), blockers: plain(c[4]) }
165
+ continue
166
+ }
167
+ if (!/^\d+$/.test(plain(c[0]))) continue
168
+ overview.rows.push({
169
+ lineNo: i + 1,
170
+ index: Number(plain(c[0])),
171
+ name: plain(c[1]),
172
+ declaredCount: Number(plain(c[2]).replace(/[^0-9]/g, '')),
173
+ counts: countsOf(c[3]),
174
+ blockers: plain(c[4]),
175
+ })
176
+ }
177
+ }
178
+
179
+ // ── 文末图例:「1.0 = N 条全 ✅」────────────────────────────────────────
180
+ const lg = /1\.0\s*=\s*(\d+)\s*条全\s*✅/.exec(String(text))
181
+ const legend = lg ? { lineNo: lines.findIndex((l) => l.includes(lg[0])) + 1, total: Number(lg[1]) } : null
182
+
183
+ return { faces, overview, legend }
184
+ }
185
+
186
+ // ══════════════════════════════════════════════════════════════════════════
187
+ // 二、判据(吃解析结果、吐问题清单)
188
+ // ══════════════════════════════════════════════════════════════════════════
189
+
190
+ /** 逐条跑 ①–⑦,返回 `{ problems, notes }`(problems 为空 = 自洽)。 */
191
+ export function checkDoc(parsed, { expectedTotal = EXPECTED_TOTAL } = {}) {
192
+ const problems = []
193
+ const notes = []
194
+ const add = (rule, where, msg) => problems.push({ rule, where, msg })
195
+ const { faces, overview, legend } = parsed
196
+
197
+ // 结构面:八个小节都在、顺序对
198
+ if (faces.length !== FACE_ORDER.length) {
199
+ add('结构', '§2.1–§2.8', `判据小节数 ${faces.length} ≠ ${FACE_ORDER.length}(八面各一节)`)
200
+ }
201
+ faces.forEach((f, i) => {
202
+ const want = FACE_ORDER[i]
203
+ if (want && f.letter !== want) add('结构', `§2.${f.idx}`, `第 ${i + 1} 面的字母是 ${f.letter},期望 ${want}(八面顺序 = ${FACE_ORDER.join('/')})`)
204
+ })
205
+
206
+ // ① 条数 40
207
+ const actualTotal = faces.reduce((a, f) => a + f.count, 0)
208
+ if (actualTotal !== expectedTotal) add('①条数', '§2.1–§2.8', `判据行实得 ${actualTotal} 条 ≠ 期望 ${expectedTotal} 条`)
209
+
210
+ // 图例(同源常量)
211
+ if (legend && legend.total !== expectedTotal) {
212
+ add('①条数', `第 ${legend.lineNo} 行(图例)`, `图例写「${legend.total} 条全 ✅」≠ ${expectedTotal}`)
213
+ }
214
+
215
+ // ②/③/⑤/⑥/⑦ 逐面
216
+ overview.rows.forEach((o, i) => {
217
+ const f = faces[i]
218
+ const label = `${o.index} ${o.name}`
219
+ if (!f) { add('结构', `§2.0 第 ${o.lineNo} 行`, `${label}:没有对应的判据小节`); return }
220
+ if (f.name !== o.name.replace(/(.*?)$/, '')) {
221
+ notes.push(`§2.0「${o.name}」↔ §2.${f.idx}「${f.name}面」名字不同(按顺序配对,仅供人工确认)`)
222
+ }
223
+ if (f.count !== o.declaredCount) {
224
+ add('②条数', `§2.0 第 ${o.lineNo} 行`, `${label}:声明 ${o.declaredCount} 条,判据行实得 ${f.count} 条`)
225
+ }
226
+ if (!sameCounts(f.tally, o.counts)) {
227
+ add('③计数', `§2.0 第 ${o.lineNo} 行`, `${label}:声明 ${fmtCounts(o.counts)},判据行实得 ${fmtCounts(f.tally)}`)
228
+ }
229
+ if (o.blockers === '' || /^[—\-]+$/.test(o.blockers)) {
230
+ add('⑤卡点', `§2.0 第 ${o.lineNo} 行`, `${label}:主要卡点为空(不许留白/占位 —)`)
231
+ }
232
+ if (f.na > 0) notes.push(`${label}:有 ${f.na} 行显式标「—」(不计入 🔴/🟡/✅,按图例须写明理由)`)
233
+ })
234
+
235
+ // ④ 合计行 = 八面之和
236
+ const sum = { '🔴': 0, '🟡': 0, '✅': 0 }
237
+ faces.forEach((f) => STATUS.forEach((s) => { sum[s] += f.tally[s] }))
238
+ if (!overview.total) {
239
+ add('④合计', '§2.0', '找不到「合计」行')
240
+ } else {
241
+ const t = overview.total
242
+ if (t.declaredTotal !== actualTotal) add('④合计', `§2.0 第 ${t.lineNo} 行`, `合计行声明 ${t.declaredTotal} 条 ≠ 八面实得 ${actualTotal} 条`)
243
+ if (!sameCounts(sum, t.counts)) add('④合计', `§2.0 第 ${t.lineNo} 行`, `合计行声明 ${fmtCounts(t.counts)},八面之和 ${fmtCounts(sum)}`)
244
+ }
245
+
246
+ // ⑥ 状态符号合法 + ⑦ ID 连续
247
+ faces.forEach((f) => {
248
+ const seen = new Set()
249
+ f.rows.forEach((r, i) => {
250
+ if (r.status.kind === 'illegal') add('⑥符号', `第 ${r.lineNo} 行(${r.id})`, `「现状」格首符号「${r.status.symbol}」不在允许集 ${STATUS.join('/')} 内`)
251
+ if (r.status.kind === 'empty') add('⑥符号', `第 ${r.lineNo} 行(${r.id})`, '「现状」格为空')
252
+ if (r.status.kind === 'na' && !r.status.reason) add('⑥符号', `第 ${r.lineNo} 行(${r.id})`, '标了「—」但没写不适用理由')
253
+ if (r.letter !== f.letter) add('⑦编号', `第 ${r.lineNo} 行(${r.id})`, `行 ID 字母 ${r.letter} ≠ 本节面字母 ${f.letter}`)
254
+ if (r.num !== i + 1) add('⑦编号', `第 ${r.lineNo} 行(${r.id})`, `本节第 ${i + 1} 行的编号是 ${r.num}(应为 ${i + 1},缺行或串号)`)
255
+ if (seen.has(r.id)) add('⑦编号', `第 ${r.lineNo} 行(${r.id})`, '行 ID 重复')
256
+ seen.add(r.id)
257
+ })
258
+ })
259
+
260
+ return { problems, notes, actualTotal, sum }
261
+ }
262
+
263
+ // ══════════════════════════════════════════════════════════════════════════
264
+ // 三、CLI
265
+ // ══════════════════════════════════════════════════════════════════════════
266
+
267
+ function main(argv) {
268
+ const has = (f) => argv.includes(f)
269
+ const val = (f) => { const i = argv.indexOf(f); return i >= 0 ? argv[i + 1] : null }
270
+ if (has('-h') || has('--help')) {
271
+ console.log('用法:node scripts/check-1.0-criteria.mjs [--strict] [--json] [--file <文档>]')
272
+ console.log('判据:① 条数 40 ② 每面条数 ③ 每面 🔴/🟡/✅ ④ 合计 = 八面之和 ⑤ 卡点非空 ⑥ 状态符号合法 ⑦ ID 连续')
273
+ console.log('退出码:0 跑完(默认只报告)|1 --strict 且有问题|2 用法/环境错误')
274
+ return 0
275
+ }
276
+ const unknown = argv.filter((a, i) => a.startsWith('-') && !['--strict', '--json', '--file', '-h', '--help'].includes(a)
277
+ && !(i > 0 && ['--file'].includes(argv[i - 1])))
278
+ if (unknown.length) { console.error(`✗ 未知参数:${unknown.join(' ')}`); return 2 }
279
+
280
+ const docArg = val('--file')
281
+ const docRel = docArg || DEFAULT_DOC
282
+ const docAbs = resolve(REPO, docRel)
283
+ let text
284
+ try {
285
+ text = readFileSync(docAbs, 'utf8')
286
+ } catch (e) {
287
+ console.error(`✗ 读不到文档:${docAbs}(${(e && e.message) || e})`)
288
+ console.error(' ⇒ 用法错误/环境错误 exit 2(本门禁读的是仓库里的判据文档,⛔ 不猜路径、不跳过后报绿)')
289
+ return 2
290
+ }
291
+
292
+ const parsed = parseDoc(text)
293
+ const { problems, notes, actualTotal, sum } = checkDoc(parsed)
294
+ const strict = has('--strict')
295
+
296
+ if (has('--json')) {
297
+ console.log(JSON.stringify({
298
+ schema: 'argent-1.0-criteria/1',
299
+ doc: docRel,
300
+ expectedTotal: EXPECTED_TOTAL,
301
+ actualTotal,
302
+ ok: problems.length === 0,
303
+ faces: parsed.faces.map((f, i) => ({
304
+ letter: f.letter, name: f.name, section: `2.${f.idx}`,
305
+ count: f.count, tally: f.tally, na: f.na,
306
+ declared: parsed.overview.rows[i] ? { count: parsed.overview.rows[i].declaredCount, tally: parsed.overview.rows[i].counts } : null,
307
+ })),
308
+ declaredTotal: parsed.overview.total ? parsed.overview.total.declaredTotal : null,
309
+ sum,
310
+ problems, notes,
311
+ }, null, 2))
312
+ return problems.length === 0 ? 0 : (strict ? 1 : 0)
313
+ }
314
+
315
+ console.log(`1.0 判据文档一致性检查 —— ${docRel}`)
316
+ console.log(' 唯一真源 = §2.1–§2.8 的判据行;本检查只读、不改文件')
317
+ console.log('')
318
+ console.log(` §2.0 复算(逐面:声明 vs 判据行实得)`)
319
+ parsed.faces.forEach((f, i) => {
320
+ const o = parsed.overview.rows[i]
321
+ const declared = o ? `${o.declaredCount} 条 | ${fmtCounts(o.counts)}` : '(§2.0 缺这一行)'
322
+ const actual = `${f.count} 条 | ${fmtCounts(f.tally)}${f.na ? ` | — ${f.na}` : ''}`
323
+ const okRow = o && o.declaredCount === f.count && sameCounts(o.counts, f.tally)
324
+ console.log(` ${okRow ? '✅' : '❌'} ${String(i + 1).padStart(2)} ${f.name}(${f.letter}) 声明 ${declared} → 实得 ${actual}`)
325
+ })
326
+ const t = parsed.overview.total
327
+ console.log(` ${t ? (t.declaredTotal === actualTotal && sameCounts(t.counts, sum) ? '✅' : '❌') : '❌'} 合计 声明 ${t ? `${t.declaredTotal} 条 | ${fmtCounts(t.counts)}` : '(缺合计行)'} → 实得 ${actualTotal} 条 | ${fmtCounts(sum)}`)
328
+ console.log('')
329
+ console.log(` 判据条数:${actualTotal} 条(期望 ${EXPECTED_TOTAL})`)
330
+ console.log(` 合计:${fmtCounts(sum)}`)
331
+
332
+ if (notes.length) {
333
+ console.log('')
334
+ console.log(' ℹ️ 提示(不判红):')
335
+ for (const n of notes) console.log(` · ${n}`)
336
+ }
337
+ if (problems.length) {
338
+ console.log('')
339
+ console.log(` ❌ 发现 ${problems.length} 个不一致(唯一真源 = 判据行 ⇒ 改 §2.0,或改判据行并说明为什么):`)
340
+ for (const p of problems) console.log(` · [${p.rule}] ${p.where}:${p.msg}`)
341
+ console.log('')
342
+ console.log(strict
343
+ ? ` 结论:❌ 不一致(${problems.length} 个问题)⇒ --strict 退出码 1`
344
+ : ` 结论:❌ 不一致(${problems.length} 个问题)⇒ 默认只报告、退出码 0;加 --strict 才非零`)
345
+ return strict ? 1 : 0
346
+ }
347
+ console.log('')
348
+ console.log(' 结论:✅ 一致(① 40 条 ② 逐面条数 ③ 逐面计数 ④ 合计 = 八面之和 ⑤ 卡点非空 ⑥ 符号合法 ⑦ ID 连续)')
349
+ return 0
350
+ }
351
+
352
+ const invoked = process.argv[1] ? resolve(process.argv[1]) : ''
353
+ if (invoked === resolve(fileURLToPath(import.meta.url))) {
354
+ process.exit(main(process.argv.slice(2)))
355
+ }
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * check-agent-catalog.mjs —— **智能体目录**门禁(A 批,2026-10-10)。
4
+ *
5
+ * ## 它防的是什么
6
+ * `agents/catalog.json` 是**货架声明**(用户从智能体库选一个装)。声明错了不会当场报错,
7
+ * 而是在客户机上表现为"装了这个却少那个"(角色没落、技能拿不到、定时器没装、档位对不上)——
8
+ * 属于典型的**静默交付缺口**。本门禁把"声明 ↔ 现实"的每一条交叉引用都变成红/绿。
9
+ *
10
+ * ## 判据(每条都能被 lint 出来,见 `scripts/argent-agents.mjs#validateCatalog`)
11
+ * | # | 判据 | 违反 |
12
+ * |---|---|---|
13
+ * | C0 | 目录文档/条目**无未知字段**(schema 白名单) | ❌ |
14
+ * | C1 | `capability` 与 `capabilities` **二选一**;能力必须在册 | ❌ |
15
+ * | C2 | `flows` ⊆ 已声明能力名下的规范 id 子集;能力有流程而 flows 为空 ⇒ 红 | ❌ |
16
+ * | C3 | `roles` 含恒装入口 `argent-general`;每个角色归属得上(capability / rolesFor / flow.role);**反向**:每条 flow 的角色必须在 roles 里、且**同能力其它 flow 的角色不得混入** | ❌ |
17
+ * | C4 | `plugins` 含共享壳 core+skin;未知插件 ⇒ 红 | ❌ |
18
+ * | C5 | `skills` 真实存在 ∧ ∈ 已声明能力包的技能集 | ❌ |
19
+ * | C6 | `integrations` 在目录文档的集成说明表里登记 | ❌ |
20
+ * | C7 | `credentials[]` 只有 `{slot,target,why}`;**禁止键 / 密钥形态串 ⇒ 红**(⛔ 凭据槽不带值) | ❌ |
21
+ * | C8 | `minPlan` 合法 ∧ 不低于各能力包档位下限 | ❌ |
22
+ * | C9 | `scheduler` 与 `flows` 一致(无流程 ⇒ 不许说要有定时器;有定时器 ⇒ 必须有流程) | ❌ |
23
+ * | C10 | **锁 ↔ 活注册表**:能力包锁里每条能力的 `flows` / 每个 flow 的 `role` 必须与 `flow_registry.py --json` **逐条一致**(锁过期 ⇒ 红,防"按过期锁做子集过滤") | ❌ |
24
+ * | C11 | 每个在册能力都被至少一个智能体覆盖(否则货架上买不到) | ⚠️ |
25
+ * | C12 | 能力声明的**宿主资产**(`pack.hostAssets`):枚举合法 + `required` 与 flow 数自洽 + 与目录的 `scheduler` 逐条一致 | ❌ |
26
+ *
27
+ * ## 用法与退出码
28
+ * ```
29
+ * node scripts/check-agent-catalog.mjs # 门禁
30
+ * node scripts/check-agent-catalog.mjs --json # 机器可读
31
+ * node scripts/check-agent-catalog.mjs --root <dir>
32
+ * ```
33
+ * · exit `0` 全部合规(可带 ⚠️);`1` 有 ❌;`2` 用法错误 / 目录或注册表读不到。
34
+ *
35
+ * ## 纪律
36
+ * · **只读**:不写任何文件、不动 `~/.dsh`、不联网;
37
+ * · `validateCatalog()` 是**唯一实现**(安装器 `--agent` 复用同一函数,⛔ 不另写一份判据);
38
+ * · `flow_registry.py --json` 是**流程/角色的唯一真源**(本脚本不自己合并 `verticals/<id>/flows.json`)。
39
+ */
40
+ import { existsSync } from 'node:fs'
41
+ import { dirname, join, resolve } from 'node:path'
42
+ import { fileURLToPath } from 'node:url'
43
+ import { isDirectRun } from './lib/is-direct-run.mjs'
44
+ import { loadRegistryDoc } from './lib/registry.mjs'
45
+ import { CATALOG_REL, buildContext, loadCatalog, validateCatalog, validateHostAssets } from './argent-agents.mjs'
46
+
47
+ const HERE = dirname(fileURLToPath(import.meta.url))
48
+ const SELF_ROOT = resolve(HERE, '..')
49
+
50
+ /**
51
+ * 跑全部判据。
52
+ * @param {{root?: string, registryDoc?: object}} [opts] `registryDoc` 可注入(测试夹具用;缺省真读注册表)。
53
+ * @returns {{errors: string[], warnings: string[], stats: object}}
54
+ */
55
+ export function checkCatalog(opts = {}) {
56
+ const root = opts.root || SELF_ROOT
57
+ const errors = []
58
+ const warnings = []
59
+ const ctxWarn = (m) => errors.push(m)
60
+ const ctx = buildContext(root, ctxWarn)
61
+
62
+ if (!existsSync(join(root, CATALOG_REL))) {
63
+ return { errors: [`缺目录声明:${CATALOG_REL}`], warnings, stats: { agents: 0, capabilities: 0, flows: 0, presets: 0 } }
64
+ }
65
+ const doc = loadCatalog(root)
66
+ if (!doc) return { errors: [`${CATALOG_REL} 读不出/不是合法 JSON`], warnings, stats: { agents: 0, capabilities: 0, flows: 0, presets: 0 } }
67
+
68
+ // C10:锁 ↔ 活注册表(唯一真源 = flow_registry.py 的合并结果)
69
+ let reg = opts.registryDoc
70
+ if (!reg) {
71
+ try {
72
+ reg = loadRegistryDoc({ root })
73
+ } catch (e) {
74
+ errors.push(`读不到合并注册表(flow_registry.py --json):${String(e.message).split('\n')[0]} —— C10(锁是否过期)无从判定`)
75
+ reg = null
76
+ }
77
+ }
78
+ const flowRole = {}
79
+ if (reg) {
80
+ const regFlows = reg.flows || {}
81
+ // `flows.json#role` 是**短角色名**(`ml-fulfillment`),preset id = `argent-<短名>`
82
+ // (唯一派生规则见 `scripts/capability_pack.py#manifest()`)⇒ 统一归一到 preset id 再比,
83
+ // 否则交叉校验拿短名去比 preset 清单(恒不命中 ⇒ 判据形同虚设)。
84
+ const presetIdOfRole = (r) => (String(r || '').startsWith('argent-') ? String(r) : `argent-${r}`)
85
+ for (const [fid, spec] of Object.entries(regFlows)) flowRole[fid] = presetIdOfRole((spec || {}).role)
86
+ for (const [cap, c] of Object.entries(ctx.capabilities)) {
87
+ const lockFlows = [...(c.flows || [])].sort()
88
+ const liveFlows = Object.keys(regFlows).filter((f) => String((regFlows[f] || {}).capability || '') === cap).sort()
89
+ if (lockFlows.join(',') !== liveFlows.join(',')) {
90
+ errors.push(`能力包锁里 \`${cap}\` 的 flows 与活注册表不一致(锁过期)——锁:[${lockFlows.join(', ')}]|注册表:[${liveFlows.join(', ')}]`
91
+ + `\n 修法:node install-argent.mjs --stamp(或 python3 scripts/capability_pack.py --stamp)后重新提交`
92
+ + `\n ⚠️ 别手改锁:它是 --stamp 的产物,手改会被下一次 --stamp 抹掉`)
93
+ }
94
+ }
95
+ for (const f of Object.keys(ctx.flows)) {
96
+ if (!(f in regFlows)) {
97
+ warnings.push(`flow \`${f}\` 出现在能力包锁/业务线文件里,但**合并注册表**里没有它(锁过期或业务线文件是坏树)`)
98
+ continue
99
+ }
100
+ const declared = (ctx.flows[f] || {}).role
101
+ const live = flowRole[f]
102
+ if (declared && live && declared !== live) {
103
+ errors.push(`flow \`${f}\` 的角色声明不一致:业务线文件写 \`${declared}\`,合并注册表写 \`${live}\``)
104
+ }
105
+ }
106
+ }
107
+
108
+ const res = validateCatalog(doc, ctx, reg ? { flowRole } : {})
109
+ errors.push(...res.errors)
110
+ warnings.push(...res.warnings)
111
+ // C12:能力声明的**宿主资产**字段自洽(枚举 / required 与 flow 数 / 与目录 scheduler 一致)
112
+ const ha = validateHostAssets(ctx)
113
+ errors.push(...ha.errors)
114
+ warnings.push(...ha.warnings)
115
+
116
+ const ids = (doc.agents || []).map((a) => a && a.id).filter(Boolean)
117
+ const stats = { agents: ids.length, capabilities: Object.keys(ctx.capabilities).length, flows: Object.keys(ctx.flows).length, presets: Object.keys(ctx.presets).length, ids }
118
+ return { errors, warnings, stats }
119
+ }
120
+
121
+ // ── CLI ───────────────────────────────────────────────────────────────
122
+ if (isDirectRun(import.meta.url)) {
123
+ const argv = process.argv.slice(2)
124
+ if (argv.includes('-h') || argv.includes('--help')) {
125
+ console.log('用法: node scripts/check-agent-catalog.mjs [--json] [--root <dir>]')
126
+ process.exit(0)
127
+ }
128
+ const rootIdx = argv.indexOf('--root')
129
+ const root = rootIdx >= 0 ? argv[rootIdx + 1] : SELF_ROOT
130
+ if (!root || !existsSync(root)) { console.error(`✗ --root 指向的目录不存在:${root}`); process.exit(2) }
131
+ const { errors, warnings, stats } = checkCatalog({ root })
132
+ if (argv.includes('--json')) {
133
+ console.log(JSON.stringify({ ok: errors.length === 0, errors, warnings, stats }, null, 2))
134
+ } else {
135
+ console.log(`智能体目录门禁:${stats.agents} 个智能体 / ${stats.capabilities} 个能力 / ${stats.flows} 条 flow / ${stats.presets} 个角色(${CATALOG_REL})`)
136
+ for (const e of errors) console.log(` ❌ ${e}`)
137
+ for (const w of warnings) console.log(` ⚠️ ${w}`)
138
+ console.log(`\n结果: ${errors.length ? `❌ FAIL ${errors.length} 项` : '✅ PASS'}${warnings.length ? `(另有 ${warnings.length} 项警告)` : ''}`)
139
+ }
140
+ process.exit(errors.length ? 1 : 0)
141
+ }