@whyshu/argent-engine 0.0.0-stage → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/MANIFEST.json +307 -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 +575 -0
  55. package/engine/scripts/billing-mode-daily.sh +12 -0
  56. package/engine/scripts/build-engine-package.mjs +123 -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 +377 -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/config-explain.mjs +278 -0
  81. package/engine/scripts/configure-provider.rb +29 -0
  82. package/engine/scripts/customer-config-migrate.mjs +179 -0
  83. package/engine/scripts/customer-preflight.py +892 -0
  84. package/engine/scripts/customer-readiness.sh +134 -0
  85. package/engine/scripts/daily-report-inbox.py +242 -0
  86. package/engine/scripts/daily-report-reconcile.mjs +312 -0
  87. package/engine/scripts/daily-report.mjs +1310 -0
  88. package/engine/scripts/daily-report.sh +46 -0
  89. package/engine/scripts/data/lip-product-dict.json +306 -0
  90. package/engine/scripts/deliver-customer.mjs +932 -0
  91. package/engine/scripts/delivery-faces.txt +31 -0
  92. package/engine/scripts/desktop-trial.sh +146 -0
  93. package/engine/scripts/dev-window.sh +260 -0
  94. package/engine/scripts/drill-clean-machine.sh +315 -0
  95. package/engine/scripts/dsh-api-client.mjs +215 -0
  96. package/engine/scripts/dsh-backup-scope.sh +83 -0
  97. package/engine/scripts/dsh-offline-pack.mjs +77 -0
  98. package/engine/scripts/dsh-overlay-pin.sh +87 -0
  99. package/engine/scripts/dsh-plane.mjs +153 -0
  100. package/engine/scripts/dsh-portforward.py +223 -0
  101. package/engine/scripts/dsh-preflight.mjs +179 -0
  102. package/engine/scripts/dsh-release-watch-daily.sh +19 -0
  103. package/engine/scripts/dsh-release-watch.mjs +458 -0
  104. package/engine/scripts/dsh-remote-check.sh +99 -0
  105. package/engine/scripts/dsh-restart.sh +179 -0
  106. package/engine/scripts/dsh-restore.sh +111 -0
  107. package/engine/scripts/dsh-scope-rewire.mjs +803 -0
  108. package/engine/scripts/dsh-snapshot.mjs +116 -0
  109. package/engine/scripts/dsh-upgrade-readiness.mjs +165 -0
  110. package/engine/scripts/dsh-upgrade.mjs +612 -0
  111. package/engine/scripts/dsh-url.sh +86 -0
  112. package/engine/scripts/dsh-web-launch.mjs +155 -0
  113. package/engine/scripts/ensure-store-windows.py +509 -0
  114. package/engine/scripts/entitlements.py +458 -0
  115. package/engine/scripts/env_probe.py +411 -0
  116. package/engine/scripts/feishu-card.mjs +252 -0
  117. package/engine/scripts/fix-skill-frontmatter.py +209 -0
  118. package/engine/scripts/flow-auto-retry.py +454 -0
  119. package/engine/scripts/flow-requirements.json +720 -0
  120. package/engine/scripts/flow-scaffold.py +275 -0
  121. package/engine/scripts/flow-timers-plist.sh +86 -0
  122. package/engine/scripts/flow-timers.sh +182 -0
  123. package/engine/scripts/flow-trigger-wake.mjs +284 -0
  124. package/engine/scripts/flow-trigger.sh +571 -0
  125. package/engine/scripts/flow_billing.py +354 -0
  126. package/engine/scripts/flow_capability_stats.py +314 -0
  127. package/engine/scripts/flow_case.py +194 -0
  128. package/engine/scripts/flow_claim.py +497 -0
  129. package/engine/scripts/flow_config.py +1024 -0
  130. package/engine/scripts/flow_failure.py +588 -0
  131. package/engine/scripts/flow_learn.py +105 -0
  132. package/engine/scripts/flow_ledger.py +235 -0
  133. package/engine/scripts/flow_policy.py +238 -0
  134. package/engine/scripts/flow_preflight.py +339 -0
  135. package/engine/scripts/flow_registry.py +341 -0
  136. package/engine/scripts/flow_retry.py +304 -0
  137. package/engine/scripts/flow_return_to_ops.py +191 -0
  138. package/engine/scripts/flow_source.py +315 -0
  139. package/engine/scripts/flow_sources/__init__.py +53 -0
  140. package/engine/scripts/flow_sources/cases_json.py +106 -0
  141. package/engine/scripts/flow_sources/common.py +428 -0
  142. package/engine/scripts/flow_sources/feishu_base.py +273 -0
  143. package/engine/scripts/flow_stale.py +185 -0
  144. package/engine/scripts/flow_status_reconcile.py +344 -0
  145. package/engine/scripts/flow_triage.py +849 -0
  146. package/engine/scripts/flow_write_verify.py +496 -0
  147. package/engine/scripts/fulfillment.js +586 -0
  148. package/engine/scripts/gen-agent-bundles.mjs +276 -0
  149. package/engine/scripts/gen-preset-rows.mjs +259 -0
  150. package/engine/scripts/init-ml.sh +2275 -0
  151. package/engine/scripts/install-internal-role.sh +190 -0
  152. package/engine/scripts/install-license.mjs +641 -0
  153. package/engine/scripts/install-one-shot.mjs +665 -0
  154. package/engine/scripts/integration_contract.py +220 -0
  155. package/engine/scripts/integrations/wenyao/adapter.py +98 -0
  156. package/engine/scripts/integrations/xzh/adapter.py +75 -0
  157. package/engine/scripts/invoice-mapping.example.json +40 -0
  158. package/engine/scripts/invoice-table.sh +194 -0
  159. package/engine/scripts/invoice_carrier_notice.py +279 -0
  160. package/engine/scripts/invoice_fail_closeout.py +297 -0
  161. package/engine/scripts/invoice_lip_runner.py +1508 -0
  162. package/engine/scripts/invoice_lip_runner.py.bak-20260922-1710-freready +1090 -0
  163. package/engine/scripts/invoice_ready_audit.py +191 -0
  164. package/engine/scripts/invoice_runner.py +675 -0
  165. package/engine/scripts/invoice_stageb.py +516 -0
  166. package/engine/scripts/launchd/com.whyshu.argent-bridge.plist.example +42 -0
  167. package/engine/scripts/launchd/com.whyshu.fulfillment-poll.plist.example +29 -0
  168. package/engine/scripts/lease.py +377 -0
  169. package/engine/scripts/lib/agent-install.mjs +649 -0
  170. package/engine/scripts/lib/argent-datadir.mjs +16 -0
  171. package/engine/scripts/lib/argent_datadir.py +262 -0
  172. package/engine/scripts/lib/argent_filelock.py +507 -0
  173. package/engine/scripts/lib/bootcheck-wiring.mjs +510 -0
  174. package/engine/scripts/lib/cdp_ports.py +197 -0
  175. package/engine/scripts/lib/customer-env.mjs +102 -0
  176. package/engine/scripts/lib/daily-report-core.mjs +517 -0
  177. package/engine/scripts/lib/dep-declaration.mjs +81 -0
  178. package/engine/scripts/lib/deps-migrate.mjs +310 -0
  179. package/engine/scripts/lib/dsh-host-resolve.mjs +109 -0
  180. package/engine/scripts/lib/dsh-scope.mjs +500 -0
  181. package/engine/scripts/lib/dsh-snapshot-path.mjs +35 -0
  182. package/engine/scripts/lib/dsh-versions.mjs +96 -0
  183. package/engine/scripts/lib/dsh-web-probe.mjs +214 -0
  184. package/engine/scripts/lib/is-direct-run.mjs +61 -0
  185. package/engine/scripts/lib/patch-compose.mjs +241 -0
  186. package/engine/scripts/lib/patch-target.mjs +35 -0
  187. package/engine/scripts/lib/plugin-lifecycle.mjs +128 -0
  188. package/engine/scripts/lib/plugin-packages.mjs +332 -0
  189. package/engine/scripts/lib/preset-compat.mjs +127 -0
  190. package/engine/scripts/lib/preset-install.mjs +575 -0
  191. package/engine/scripts/lib/preset-mechanism.mjs +56 -0
  192. package/engine/scripts/lib/preset-sync.mjs +59 -0
  193. package/engine/scripts/lib/profile-retire.mjs +97 -0
  194. package/engine/scripts/lib/python-runtime.mjs +190 -0
  195. package/engine/scripts/lib/registry.mjs +72 -0
  196. package/engine/scripts/lib/repo-root.sh +56 -0
  197. package/engine/scripts/lib/schedule-mechanism.mjs +659 -0
  198. package/engine/scripts/lib/schedule-probe.mjs +835 -0
  199. package/engine/scripts/lib/scope-pointer.mjs +164 -0
  200. package/engine/scripts/lib/settings-panels.mjs +816 -0
  201. package/engine/scripts/lib/stdio_utf8.py +86 -0
  202. package/engine/scripts/lib/wake-auth.mjs +392 -0
  203. package/engine/scripts/lib/webdriver_ipc.py +264 -0
  204. package/engine/scripts/lib/zstd-frames.mjs +104 -0
  205. package/engine/scripts/license-e2e-drill.mjs +426 -0
  206. package/engine/scripts/license-gate.mjs +245 -0
  207. package/engine/scripts/lip_backlog.py +171 -0
  208. package/engine/scripts/lip_table.py +185 -0
  209. package/engine/scripts/local-usage.mjs +214 -0
  210. package/engine/scripts/migrate-providers.mjs +127 -0
  211. package/engine/scripts/migrate-skills.sh +65 -0
  212. package/engine/scripts/ml-oauth-setup.mjs +550 -0
  213. package/engine/scripts/ml-store-ids.json +26 -0
  214. package/engine/scripts/ml_artifact_attribution.py +333 -0
  215. package/engine/scripts/ml_capacity_probe.py +1158 -0
  216. package/engine/scripts/ml_hub_status.py +1556 -0
  217. package/engine/scripts/ml_schema_check.py +115 -0
  218. package/engine/scripts/ml_store_ids.py +139 -0
  219. package/engine/scripts/npm-publish-argent.mjs +499 -0
  220. package/engine/scripts/npm-publish-argent.sh +20 -0
  221. package/engine/scripts/orchestrator.py +13811 -0
  222. package/engine/scripts/pack-plugins.mjs +140 -0
  223. package/engine/scripts/patches/dsh-llm-deepseek--transport-diagnostics.patch +297 -0
  224. package/engine/scripts/patches/dsh-session--append-message-shape.patch +15 -0
  225. package/engine/scripts/patches/dsh-session--tool-result-block-017.patch +28 -0
  226. package/engine/scripts/patches/dsh-session-format-v0-to-v1--permission-preset-origin.patch +11 -0
  227. package/engine/scripts/patches/dsh-session-format-v3-to-v4--turn-outside-replace.patch +20 -0
  228. package/engine/scripts/patches/dsh-session-persistence-jsonl--worker-bundled-migrators.patch +39 -0
  229. package/engine/scripts/patches/dsh-tools--render-return-shape.patch +50 -0
  230. package/engine/scripts/patches/verify-bundled-migrators.mjs +79 -0
  231. package/engine/scripts/patches/verify-hardening.mjs +167 -0
  232. package/engine/scripts/patches/verify-session-migration.mjs +149 -0
  233. package/engine/scripts/patches/verify-transport-diagnostics.mjs +227 -0
  234. package/engine/scripts/plan-role-rename.mjs +125 -0
  235. package/engine/scripts/plugin-bootcheck.mjs +451 -0
  236. package/engine/scripts/plugin-composition.json +15 -0
  237. package/engine/scripts/poc-mcp-host.mjs +455 -0
  238. package/engine/scripts/poll-fulfillment.sh +293 -0
  239. package/engine/scripts/poll-invoice.sh +74 -0
  240. package/engine/scripts/profile-doctor.mjs +686 -0
  241. package/engine/scripts/provider-select.sh +70 -0
  242. package/engine/scripts/provider-switch.sh +146 -0
  243. package/engine/scripts/provider-sync-target.check.mjs +64 -0
  244. package/engine/scripts/python-runtime.sh +78 -0
  245. package/engine/scripts/query_runner.py +310 -0
  246. package/engine/scripts/release-argent.sh +574 -0
  247. package/engine/scripts/repair-unresolved-tools.mjs +178 -0
  248. package/engine/scripts/run-tests.sh +256 -0
  249. package/engine/scripts/self-sign-license.mjs +179 -0
  250. package/engine/scripts/session-health-scan.mjs +63 -0
  251. package/engine/scripts/session-repair-tool-result-content.mjs +181 -0
  252. package/engine/scripts/setup.sh +22 -0
  253. package/engine/scripts/skill-install.mjs +629 -0
  254. package/engine/scripts/sku_translate.py +148 -0
  255. package/engine/scripts/sync-plugins.sh +90 -0
  256. package/engine/scripts/sync-presets.sh +27 -0
  257. package/engine/scripts/sync-scripts.sh +445 -0
  258. package/engine/scripts/telemetry.py +873 -0
  259. package/engine/scripts/ui-copy-count.mjs +220 -0
  260. package/engine/scripts/usage-anchor.sh +136 -0
  261. package/engine/scripts/usage_report.py +531 -0
  262. package/engine/scripts/validate-preset-schema.mjs +208 -0
  263. package/engine/scripts/vertical-init.mjs +323 -0
  264. package/engine/scripts/vertical-lint.mjs +482 -0
  265. package/engine/scripts/wenyao-browser.sh +73 -0
  266. package/engine/scripts/wenyao-fixture-export.py +116 -0
  267. package/engine/scripts/wenyao-invoice-writeback.mjs +540 -0
  268. package/engine/scripts/wenyao-login.sh +34 -0
  269. package/engine/scripts/wenyao-template-map.json +292 -0
  270. package/engine/scripts/wenyao_attach.py +131 -0
  271. package/engine/scripts/wenyao_download_template.py +144 -0
  272. package/engine/scripts/wenyao_fill_template.py +568 -0
  273. package/engine/scripts/wenyao_login.py +145 -0
  274. package/engine/scripts/wenyao_portal.py +633 -0
  275. package/engine/scripts/wenyao_recon.py +230 -0
  276. package/engine/scripts/wenyao_run.py +503 -0
  277. package/engine/scripts/wenyao_shadow_diff.py +160 -0
  278. package/engine/scripts/wenyao_skill_contract.py +156 -0
  279. package/engine/scripts/wenyao_template_fetch.py +175 -0
  280. package/engine/scripts/win/dsh-web-autostart.ps1 +372 -0
  281. package/engine/scripts/ziniao-auth-probe.sh +268 -0
  282. package/engine/scripts/ziniao-keepalive.sh +216 -0
  283. package/engine/scripts/ziniao-post-upgrade-check.sh +120 -0
  284. package/engine/scripts/ziniao-windows.sh +153 -0
  285. package/engine/version.mjs +1 -0
  286. package/engine/verticals/README.md +103 -0
  287. package/engine/verticals/_engine-words.baseline.txt +237 -0
  288. package/engine/verticals/_engine-words.json +19 -0
  289. package/engine/verticals/mercadolibre/README.md +43 -0
  290. package/engine/verticals/mercadolibre/config.json +709 -0
  291. package/engine/verticals/mercadolibre/flows.json +706 -0
  292. package/engine/verticals/mercadolibre/manifest.json +538 -0
  293. package/engine/verticals/mercadolibre/migrate-machine-state.mjs +138 -0
  294. package/engine/verticals/mercadolibre/preflight.py +143 -0
  295. package/engine/verticals/mercadolibre/prompts/daily-report.failure.md +69 -0
  296. package/engine/verticals/mercadolibre/prompts/daily-report.md +47 -0
  297. package/engine/verticals/mercadolibre/prompts/daily-report.scheduled.md +79 -0
  298. package/engine/verticals/mercadolibre/prompts/full.failure.md +125 -0
  299. package/engine/verticals/mercadolibre/prompts/full.md +91 -0
  300. package/engine/verticals/mercadolibre/prompts/wenyao-invoice.failure.md +88 -0
  301. package/engine/verticals/mercadolibre/prompts/wenyao-invoice.md +64 -0
  302. package/engine/verticals/mercadolibre/prompts/xzh-invoice.failure.md +91 -0
  303. package/engine/verticals/mercadolibre/prompts/xzh-invoice.md +46 -0
  304. package/package.json +21 -4
  305. package/README.md +0 -3
@@ -0,0 +1,258 @@
1
+ /**
2
+ * `argent flow claim` / `argent flow triage` 适配层(P1 首批,2026-10-03)
3
+ * —— 把被包脚本(`scripts/flow_claim.py` / `scripts/flow_triage.py`)的**退出码与输出**
4
+ * 翻译成 P0-A 的机器可判口径(A1 退出码 / A2 JSON / A3 dry-run)。
5
+ *
6
+ * ## 为什么必须有这一层(D4 裁定,2026-10-03)
7
+ * `scripts/flow_claim.py:14` 的 `3` 表示**无票可认领**,而 P0-A A1 把 `3` **唯一**留给"许可宽限放行"
8
+ * (`scripts/check-license.mjs:15-19` 与 `scripts/lease.py:15-21` 已是那个语义)。同一个码两个意思,
9
+ * 客户 agent 就没法用 `$?` 分支 —— 但**不能改脚本**(`scripts/flow-trigger.sh` 的 headless 分支
10
+ * 依赖它)⇒ 翻译放在这一层:
11
+ *
12
+ * | 脚本 `$?` | verb `code` | 条件 | 结论 |
13
+ * |---|---|---|---|
14
+ * | `0` | `0` | 有认领 | `ok:true, empty:false` |
15
+ * | `3` | **`0`** | 无票 | `ok:true, empty:true, reason:"no_ticket"`("没票要跑"不是错误) |
16
+ * | `2` | `1` | stderr 是 `triage 失败/超时` | **源读取失败 = 运行失败**(网络、飞书半开连接) |
17
+ * | `2` | `2` | 其余(未知流程/缺 env/参数) | `reason:"missing_declaration"` / `"usage_error"` |
18
+ * | 其它 | `1` | — | `reason:"internal_error"`;输出不可解析 ⇒ `"bad_output"` |
19
+ *
20
+ * ## A3 dry-run
21
+ * **默认即 dry-run**(真写要显式 `--apply`/`--confirm`);`--dry-run` 出现即恒 dry-run
22
+ * (调用方说了算 —— 能靠参数绕过判据就是红)。零副作用由 `scripts/flow_claim.py` 的 dry-run 分支保证,
23
+ * 判据 = 跑两次 dry-run 后数据目录逐字节不变(`scripts/tests/flow-claim-verb.test.mjs`)。
24
+ *
25
+ * 纪律:本文件是**薄适配层**——不复制认领/分诊的任何判据(幂等键、租约、并发、店铺闸门都在脚本里),
26
+ * 只做"参数搬运 + 退出码翻译 + 回显"。
27
+ */
28
+ import { spawnSync } from 'node:child_process'
29
+ import { join } from 'node:path'
30
+ import { loadRegistry, resolveFlow } from './flow-registry.mjs'
31
+
32
+ /** 需要带值的参数(搬运)。 */
33
+ export const VALUE_FLAGS = ['--flow', '--max', '--lease-min', '--batch', '--per-ip', '--ledger', '--lock', '--cases-from-dir', '--limit']
34
+ /** 布尔参数(搬运)。 */
35
+ export const BOOL_FLAGS = ['--json', '--dry-run', '--no-regenerate']
36
+ /** 本层**自己消费**的参数(不往脚本传)。 */
37
+ export const CONSUMED_FLAGS = ['--apply', '--confirm', '--yes', '--plan-only']
38
+ /** 显式确权(真写)参数。 */
39
+ export const CONFIRM_FLAGS = ['--apply', '--confirm', '--yes']
40
+
41
+ /** 分诊脚本支持的参数子集(`--plan-only` 走这条路)。 */
42
+ const TRIAGE_FLAGS = ['--flow', '--cases-from-dir', '--ledger', '--limit', '--no-regenerate', '--json']
43
+
44
+ /**
45
+ * 解析 argv → 调用计划(纯函数)。
46
+ * @returns {{ok:true, route:'claim'|'triage', childArgs:string[], echo:object, dryRun:boolean, json:boolean}
47
+ * | {ok:false, code:number, reason:string, error:string}}
48
+ */
49
+ export function buildInvocation(argv = []) {
50
+ const args = [...argv]
51
+ const has = (f) => args.includes(f)
52
+ const valOf = (f) => { const i = args.indexOf(f); return i >= 0 ? args[i + 1] : undefined }
53
+
54
+ const flow = valOf('--flow')
55
+ if (!flow) return { ok: false, code: 2, reason: 'usage_error', error: '缺必填参数 --flow(用法:argent flow claim --flow <流程> [--max N] [--cases-from-dir <目录>] [--apply])' }
56
+
57
+ // 单值参数必须带值(否则会把下一个 `--xxx` 当值吃进去,错误会推迟到脚本里才暴露)
58
+ for (const f of VALUE_FLAGS) {
59
+ const i = args.indexOf(f)
60
+ if (i >= 0 && (i + 1 >= args.length || String(args[i + 1]).startsWith('--'))) {
61
+ return { ok: false, code: 2, reason: 'usage_error', error: `参数 ${f} 缺值` }
62
+ }
63
+ }
64
+ const json = has('--json')
65
+ const planOnly = has('--plan-only')
66
+ const dryFlag = has('--dry-run')
67
+ const confirmed = CONFIRM_FLAGS.some((f) => has(f))
68
+ const dryRun = dryFlag || !confirmed // A3:默认 dry-run;--dry-run 恒优先(fail-safe)
69
+ const route = planOnly ? 'triage' : 'claim'
70
+
71
+ const keep = new Set(route === 'triage' ? TRIAGE_FLAGS : [...VALUE_FLAGS, ...BOOL_FLAGS])
72
+ const childArgs = []
73
+ for (let i = 0; i < args.length; i++) {
74
+ const t = args[i]
75
+ if (CONSUMED_FLAGS.includes(t)) continue
76
+ if (!keep.has(t)) {
77
+ if (t.startsWith('--')) childArgs.push(t) // 未知参数照传:脚本的 argparse 才是它自己参数的权威(exit 2)
78
+ continue
79
+ }
80
+ childArgs.push(t)
81
+ if (VALUE_FLAGS.includes(t)) { childArgs.push(args[i + 1]); i++ }
82
+ }
83
+ if (route === 'claim' && dryRun) childArgs.push('--dry-run')
84
+
85
+ const echo = {
86
+ flow,
87
+ max: valOf('--max') !== undefined ? Number(valOf('--max')) : null,
88
+ leaseMin: valOf('--lease-min') !== undefined ? Number(valOf('--lease-min')) : null,
89
+ batch: valOf('--batch') !== undefined ? Number(valOf('--batch')) : null,
90
+ perIp: valOf('--per-ip') !== undefined ? Number(valOf('--per-ip')) : null,
91
+ casesFromDir: valOf('--cases-from-dir') ?? null,
92
+ limit: valOf('--limit') !== undefined ? Number(valOf('--limit')) : null,
93
+ ledger: valOf('--ledger') ?? null,
94
+ lock: valOf('--lock') ?? null,
95
+ dryRun,
96
+ planOnly,
97
+ json,
98
+ }
99
+ return { ok: true, route, childArgs, echo, dryRun, json }
100
+ }
101
+
102
+ /** `--flow` 的实际生效值:规范 id + 局部 id 前缀(回显用;解析不了就如实退化为字面值)。 */
103
+ export function flowEcho(flow, { repo, py, pyFailure = null } = {}) {
104
+ if (pyFailure || !py) {
105
+ return { flow, flowPrefix: null, flowKnown: false, note: '解释器不可用 ⇒ 未做注册表解析,flow 为字面值' }
106
+ }
107
+ const reg = loadRegistry({ repo, py })
108
+ if (!reg.ok) {
109
+ return { flow, flowPrefix: null, flowKnown: false, note: `注册表读不到(${reg.reason})⇒ flow 为字面值` }
110
+ }
111
+ const r = resolveFlow({ input: flow, doc: reg.doc })
112
+ return { flow: r.canonical, flowPrefix: r.local, flowKnown: r.known }
113
+ }
114
+
115
+ /** 解析脚本 stdout 的 JSON(脚本的 `--json` 输出;失败返回 null)。 */
116
+ export function parseChildJson(stdout) {
117
+ const s = String(stdout || '')
118
+ const i = s.indexOf('{')
119
+ if (i < 0) return null
120
+ try { return JSON.parse(s.slice(i)) } catch { return null }
121
+ }
122
+
123
+ /**
124
+ * 退出码翻译(A1;唯一实现,claim/triage 共用)。
125
+ * @param {{status:number|null, stdout:string, stderr:string, signal?:string}} child
126
+ */
127
+ export function classifyChild(child) {
128
+ const status = child.status === null || child.status === undefined ? 1 : Number(child.status)
129
+ const stderr = String(child.stderr || '')
130
+ if (status === 0 || status === 3) return { ok: true, code: 0, scriptStatus: status }
131
+ if (status === 2) {
132
+ // ⚠️ 脚本把"源读取失败/超时"也归在 2 里 —— 按 A1 它属**运行失败**(1),不是用法错误
133
+ if (/triage\s*(失败|超时)/.test(stderr)) return { ok: false, code: 1, reason: 'source_failed', error: stderr.trim().slice(0, 400) }
134
+ if (/flow_source 失败/.test(stderr)) return { ok: false, code: 1, reason: 'source_failed', error: stderr.trim().slice(0, 400) }
135
+ if (/未知流程/.test(stderr)) return { ok: false, code: 2, reason: 'missing_declaration', error: stderr.trim().slice(0, 400) }
136
+ return { ok: false, code: 2, reason: 'usage_error', error: stderr.trim().slice(0, 400) }
137
+ }
138
+ return { ok: false, code: 1, reason: 'internal_error', error: (stderr.trim() || `被包脚本退出码 ${status}`).slice(0, 400) }
139
+ }
140
+
141
+ /** 认领结果 → A2 JSON(纯函数)。 */
142
+ export function shapeClaim(child, { echo, registryEcho }) {
143
+ // ⚠️ base 里**不含** max/leaseMin:那两个要由"命令行给的 ⇒ 脚本回显的生效值"决定(见下方 pick),
144
+ // 先铺 base 再覆盖会让生效值被 echo 的 null 盖掉(实测踩过:max 恒 null)。
145
+ const base = { ...registryEcho, dryRun: echo.dryRun, casesFromDir: echo.casesFromDir }
146
+ const cls = classifyChild(child)
147
+ if (!cls.ok) return { ok: false, empty: false, code: cls.code, reason: cls.reason, error: cls.error, ...base }
148
+ const parsed = parseChildJson(child.stdout)
149
+ if (!parsed) {
150
+ return { ok: false, empty: false, code: 1, reason: 'bad_output',
151
+ error: `被包脚本输出不可解析:${String(child.stdout || child.stderr || '').trim().slice(0, 300)}`, ...base }
152
+ }
153
+ const claimed = Array.isArray(parsed.claimed) ? parsed.claimed.map(String) : []
154
+ const empty = claimed.length === 0
155
+ const pick = (cliVal, childVal) => (cliVal !== null && cliVal !== undefined ? cliVal : (childVal ?? null))
156
+ return {
157
+ ok: true,
158
+ empty,
159
+ code: 0,
160
+ reason: empty ? 'no_ticket' : null,
161
+ claimed,
162
+ count: claimed.length,
163
+ // 实际生效值:命令行给的优先,否则取**脚本回显的生效值**(可能来自流程配置)
164
+ max: pick(echo.max, parsed.max),
165
+ leaseMin: pick(echo.leaseMin, parsed.leaseMin),
166
+ batch: pick(echo.batch, parsed.batch),
167
+ perIp: pick(echo.perIp, parsed.perIp),
168
+ ledger: echo.ledger || parsed.ledger || null,
169
+ lock: echo.lock || parsed.lock || null,
170
+ planOnly: echo.planOnly,
171
+ ...base,
172
+ ...(empty && parsed.reason ? { note: String(parsed.reason).slice(0, 500) } : {}),
173
+ }
174
+ }
175
+
176
+ /** 分诊结果 → A2 JSON(纯函数)。 */
177
+ export function shapeTriage(child, { echo, registryEcho }) {
178
+ const base = { ...registryEcho, limit: echo.limit, casesFromDir: echo.casesFromDir }
179
+ const cls = classifyChild(child)
180
+ if (!cls.ok) return { ok: false, empty: false, code: cls.code, reason: cls.reason, error: cls.error, ...base }
181
+ const parsed = parseChildJson(child.stdout)
182
+ if (!parsed) {
183
+ return { ok: false, empty: false, code: 1, reason: 'bad_output',
184
+ error: `被包脚本输出不可解析:${String(child.stdout || child.stderr || '').trim().slice(0, 300)}`, ...base }
185
+ }
186
+ const plan = parsed.plan || {}
187
+ const ids = (arr) => (arr || []).map((x) => String((x && x.caseId) || '')).filter(Boolean)
188
+ const toDispatch = ids(plan.to_dispatch)
189
+ const empty = toDispatch.length === 0
190
+ const s = parsed.summary || {}
191
+ return {
192
+ ok: true,
193
+ empty,
194
+ code: 0,
195
+ reason: empty ? 'no_ticket' : null,
196
+ counts: {
197
+ toDispatch: s.to_dispatch ?? toDispatch.length,
198
+ awaitRelease: s.await_release ?? (plan.await_release || []).length,
199
+ needFields: s.need_fields ?? (plan.need_fields || []).length,
200
+ inFlight: s.in_flight ?? (plan.in_flight || []).length,
201
+ stale: s.stale ?? (plan.stale || []).length,
202
+ needsHuman: s.needs_human ?? (plan.needs_human || []).length,
203
+ },
204
+ plan: {
205
+ toDispatch,
206
+ stale: ids(plan.stale),
207
+ inFlight: ids(plan.in_flight),
208
+ needFields: ids(plan.need_fields),
209
+ awaitRelease: ids(plan.await_release),
210
+ },
211
+ ...base,
212
+ ...((parsed.notes || []).length ? { notes: (parsed.notes || []).map(String).slice(0, 6) } : {}),
213
+ }
214
+ }
215
+
216
+ /** 人类可读的一行结论(与 JSON 载荷分离;`--json` 时 stdout 只有 JSON)。 */
217
+ export function humanOf(json) {
218
+ if (!json.ok) return `❌ ${json.reason}:${json.error || ''}`.trim()
219
+ if (json.claimed) {
220
+ if (json.empty) return `本轮无票可认领(no_ticket)${json.note ? `:${json.note}` : ''}`
221
+ return json.dryRun
222
+ ? `[dry-run] 将认领 ${json.count} 张(未落台账):${json.claimed.join(', ')}`
223
+ : `✅ 已认领 ${json.count} 张(租约 ${json.leaseMin ?? '?'} 分钟):${json.claimed.join(', ')}`
224
+ }
225
+ const c = json.counts || {}
226
+ if (json.empty) return `本轮无可派发单元(no_ticket)|待放行 ${c.awaitRelease ?? 0}|等人补字段 ${c.needFields ?? 0}|在飞 ${c.inFlight ?? 0}|停滞 ${c.stale ?? 0}`
227
+ return `可派发 ${c.toDispatch ?? 0} 张:${(json.plan?.toDispatch || []).join(', ')}`
228
+ }
229
+
230
+ /** 跑脚本(可注入 spawn 便于夹具)。 */
231
+ export function runChild({ py, script, args, env = process.env, spawn = spawnSync, timeout = 0 }) {
232
+ const r = spawn(py, [script, ...args], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024, env, timeout: timeout || undefined })
233
+ return { status: r.status, stdout: r.stdout || '', stderr: r.stderr || '', signal: r.signal || null }
234
+ }
235
+
236
+ /**
237
+ * verb 入口:`flow claim` / `flow triage`(route 由 argv 决定)。
238
+ * @returns {{json:object, human:string, stderr:string, exit:number}} 信封(CLI 负责打印与退出码)
239
+ */
240
+ export function runFlowVerb(argv, { repo, py, pyFailure = null, env = process.env, spawn = spawnSync } = {}) {
241
+ const built = buildInvocation(argv)
242
+ if (!built.ok) {
243
+ const json = { ok: false, empty: false, code: built.code, reason: built.reason, error: built.error, flow: null, flowPrefix: null, flowKnown: false, dryRun: null, casesFromDir: null }
244
+ return { json, human: humanOf(json), stderr: '', exit: built.code }
245
+ }
246
+ if (pyFailure || !py) {
247
+ const json = { ok: false, empty: false, code: 2, reason: 'python_unavailable', error: String(pyFailure || '解释器不可用'),
248
+ flow: built.echo.flow, flowPrefix: null, flowKnown: false, dryRun: built.dryRun, casesFromDir: built.echo.casesFromDir }
249
+ return { json, human: humanOf(json), stderr: '', exit: 2 }
250
+ }
251
+ const script = join(repo, 'scripts', built.route === 'claim' ? 'flow_claim.py' : 'flow_triage.py')
252
+ const child = runChild({ py, script, args: built.childArgs, env, spawn })
253
+ const registryEcho = flowEcho(built.echo.flow, { repo, py })
254
+ const json = built.route === 'claim'
255
+ ? shapeClaim(child, { echo: built.echo, registryEcho })
256
+ : shapeTriage(child, { echo: built.echo, registryEcho })
257
+ return { json, human: humanOf(json), stderr: String(child.stderr || ''), exit: Number(json.code) }
258
+ }