@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,31 @@
1
+ # 投递面清单 —— **唯一真源**(2026-10-08 立;缺口㉔ 收口的同批改动)
2
+ #
3
+ # 为什么要有这个文件:面清单此前**散在投递脚本里**,于是"加目录忘了进面"踩了三次 ——
4
+ # · 缺口⑳(2026-10-02)`core/`:L1 内核资产不在面里 ⇒ 只走 sync 的机器插件 `DATA_DIR=''`(只读退化);
5
+ # · 缺口㉓(2026-10-04)`skills/`:L0 技能资产不在面里 ⇒ 归类判据 `skills/internal.json` 拿不到;
6
+ # · 缺口㉔(2026-10-08)`deploy/`:部署/交付件不在面里 ⇒ `deploy/customer/roles.spec.json`
7
+ # (`check-role-mapping.mjs` 第 ⑥ 项**真读**它)漂移了一整批。
8
+ # 三次的形态完全一样:**脚本看起来正常,克隆拿不到**。故清单必须只有一份,且**同一份**被两个投递口读:
9
+ # · `scripts/sync-scripts.sh` —— 第一件事:本机安装克隆 `~/.argent-dsh`;
10
+ # · `scripts/deliver-customer.mjs` —— 第三件事:客户机 / 远端克隆(rsync over ssh + 双端 sha 核对)。
11
+ # (第二件事是 `scripts/release-argent.sh`:目标是**下载服务器**,面不同,**不读**本文件,语义不动。)
12
+ #
13
+ # 格式:一行一个面(相对仓库根的目录名);`#` 起注释与空行忽略;顺序即投递顺序。
14
+ # 加新面 = **只改这一个文件**(⛔ 不要在同步脚本里再抄一份)。若该面需要"点名核对",
15
+ # 另在 `sync-scripts.sh` 的对应核对段补清单(核对与面清单是两件事:面清单决定"投什么",
16
+ # 点名清单决定"投完逐项核到哪几个关键件")。
17
+ #
18
+ # ⚠️ 缺口㉕(2026-10-09,**第四次**同形态):`configs/` 是**配置目录**(引擎级可配项声明,
19
+ # `core/lib/config-catalog.mjs` 从 `<根>/configs/*.json` 读;业务线级那份在 `verticals/<id>/config.json`,
20
+ # 随 `verticals/` 面走)。它不在面里 ⇒ 只走 sync 的机器上**引擎级可配项整体消失**:
21
+ # 门禁 CLI 报"一项都没有"、设置页少一半分类,而**没有任何报错**(`loadCatalog()` 读不到就当空目录)。
22
+ # 形态与前三次(core/ ⇒ 插件只读退化;skills/ ⇒ 归类门禁退化;deploy/ ⇒ 客户模板少查一个面)完全一样:
23
+ # **脚本看起来正常,克隆拿不到**。故 `configs/` 与它们同待遇:进面 + 逐文件 sha 核对 + 计数一眼可见。
24
+ scripts
25
+ core
26
+ configs
27
+ skills
28
+ verticals
29
+ deploy
30
+ agents
31
+ presets
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env bash
2
+ # 官方桌面端「隔离试跑」——**绝不碰 `~/.dsh`**(2026-10-01 事故后固化)。
3
+ #
4
+ # ## 为什么需要它(背景事故)
5
+ # 官方桌面端(`/Applications/DeepSeek Harness.app`,内置 dsh 0.2.0-rc.2)能承载 Argent,
6
+ # 但首次"手动隔离启动"时**误写了生产一行**:当时只设了 `DSH_HOME=~/.dsh-desktop-trial`,
7
+ # 而 `plugins/argent-flow` 的 `dataDir` 只认 `ARGENT_DATA_DIR`、**不看 `DSH_HOME`**
8
+ # ⇒ 启动横幅把一行追加进了生产 `~/.dsh/argent/flow-watch.log`。
9
+ # 更反直觉的是:**只隔离 `ARGENT_DATA_DIR` 而不放暂停开关更危险** —— 隔离目录里没有
10
+ # `PAUSE_FLOW_WATCH`,失败回路会**真的去扫/派发生产流程**(抢客户机的活)。
11
+ #
12
+ # ## 本脚本钉住的四条
13
+ # ① `DSH_HOME` = **独立 home**(用户 2026-10-01 决定:home 策略 = 独立,不共享生产 `~/.dsh`);
14
+ # ② `ARGENT_DATA_DIR = $DSH_HOME/argent`(三级链的第二级,显式写死便于核对);
15
+ # ③ 隔离数据目录里**放 `PAUSE_FLOW_WATCH`**(缺它 = 泵会跑);
16
+ # 叠加 2026-10-01 之后插件内的**安全阀**(隔离目录默认暂停,除非 `ARGENT_ALLOW_ISOLATED_PUMP=1`)
17
+ # —— 本脚本**刻意不设**那个开关;
18
+ # ④ `--check` 能**脚本化验收**:打印将要使用的 home/数据目录/暂停开关,并比对 `~/.dsh` 指纹。
19
+ #
20
+ # ## 用法
21
+ # bash scripts/desktop-trial.sh --check # 只体检 + 指纹比对(不启动 App,不写文件)
22
+ # bash scripts/desktop-trial.sh --fingerprint # 只打印 ~/.dsh 指纹(可重定向保存)
23
+ # bash scripts/desktop-trial.sh --refresh-baseline # 把当前指纹记为基线(--check 之后拿它比对)
24
+ # bash scripts/desktop-trial.sh [--] [App 参数…] # 隔离启动(前台;窗口/日志都看得见)
25
+ #
26
+ # 退出码:0 正常;1 用法/环境错误(含"拒绝在共享生产 home 上启动");App 的退出码原样透传。
27
+ set -euo pipefail
28
+
29
+ APP="${ARGENT_DESKTOP_APP:-/Applications/DeepSeek Harness.app/Contents/MacOS/DeepSeek Harness}"
30
+ PROD_HOME="$HOME/.dsh" # 生产 home:**只读指纹,绝不写**
31
+ TRIAL_HOME="${ARGENT_DESKTOP_TRIAL_HOME:-$HOME/.dsh-desktop-trial}"
32
+ MODE="run"; APP_ARGS=()
33
+
34
+ # ── 参数 ────────────────────────────────────────────────────────────────────
35
+ while [ $# -gt 0 ]; do
36
+ case "$1" in
37
+ --check) MODE="check"; shift ;;
38
+ --fingerprint) MODE="fingerprint"; shift ;;
39
+ --refresh-baseline) MODE="baseline"; shift ;;
40
+ --home) TRIAL_HOME="${2:?--home 需要一个目录}"; shift 2 ;;
41
+ --) shift; APP_ARGS+=("$@"); break ;;
42
+ -h|--help) sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
43
+ *) APP_ARGS+=("$1"); shift ;;
44
+ esac
45
+ done
46
+
47
+ # ── ① home 策略:独立(用户 2026-10-01 决定)────────────────────────────────
48
+ # 关键:**不**用 `${DSH_HOME:-…}` 继承调用者的 DSH_HOME —— 会话里 DSH_HOME 常常就指向
49
+ # 生产 `~/.dsh`,继承它等于"隔离脚本把实例送去生产"(这正是要防的事故形态)。
50
+ if [ -n "${DSH_HOME:-}" ] && [ "$DSH_HOME" != "$TRIAL_HOME" ]; then
51
+ echo "❌ 拒绝启动:环境里的 DSH_HOME=$DSH_HOME 与试用 home(${TRIAL_HOME})不一致。" >&2
52
+ echo " 隔离试跑必须用**独立 home**(用户 2026-10-01 决定);要复用别的目录请显式:" >&2
53
+ echo " unset DSH_HOME && bash scripts/desktop-trial.sh # 用默认 $HOME/.dsh-desktop-trial" >&2
54
+ echo " bash scripts/desktop-trial.sh --home <独立目录> # 换一个独立目录" >&2
55
+ exit 1
56
+ fi
57
+ if [ "$TRIAL_HOME" = "$PROD_HOME" ]; then
58
+ echo "❌ 拒绝启动:试用 home 不能是生产 ${PROD_HOME}(隔离试跑的红线)。" >&2
59
+ exit 1
60
+ fi
61
+ export DSH_HOME="$TRIAL_HOME"
62
+
63
+ # ── ② 数据目录(三级链第二级)+ ③ 暂停开关 ─────────────────────────────────
64
+ export ARGENT_DATA_DIR="$DSH_HOME/argent"
65
+ PAUSE_SWITCH="$ARGENT_DATA_DIR/PAUSE_FLOW_WATCH"
66
+
67
+ # ── `~/.dsh` 指纹:启动前后"逐字节没变"的可核对证据 ────────────────────────
68
+ # 取的都是**事故会碰到**的东西:凭据、会话数、生产 flow-watch.log 的字节/行数、用例数、设置文件。
69
+ file_sha() { [ -f "$1" ] && shasum -a 256 "$1" | awk '{print $1}' || echo '(无)'; }
70
+ file_lines() { [ -f "$1" ] && wc -l < "$1" | tr -d ' ' || echo 0; }
71
+ fingerprint_lines() {
72
+ printf 'credentials.yaml sha256 : %s\n' "$(file_sha "$PROD_HOME/.credentials.yaml")"
73
+ printf 'sessions 条目数 : %s\n' "$( [ -d "$PROD_HOME/sessions" ] && ls -1 "$PROD_HOME/sessions" 2>/dev/null | wc -l | tr -d ' ' || echo 0 )"
74
+ printf 'argent/flow-watch.log : %s 行\n' "$(file_lines "$PROD_HOME/argent/flow-watch.log")"
75
+ printf 'argent/flow-cases 条目数 : %s\n' "$( [ -d "$PROD_HOME/argent/flow-cases" ] && ls -1 "$PROD_HOME/argent/flow-cases" 2>/dev/null | wc -l | tr -d ' ' || echo 0 )"
76
+ # 下面三个是**最容易被误写的小配置文件**(2026-10-02 实测:测试夹具曾把 roles.json / skin.json 写进生产)
77
+ printf 'argent/config.json sha256: %s\n' "$(file_sha "$PROD_HOME/argent/config.json")"
78
+ printf 'argent/roles.json sha256 : %s\n' "$(file_sha "$PROD_HOME/argent/roles.json")"
79
+ printf 'argent/skin.json sha256 : %s\n' "$(file_sha "$PROD_HOME/argent/skin.json")"
80
+ }
81
+ FINGERPRINT_FILE="$TRIAL_HOME/dsh-fingerprint.baseline"
82
+ fingerprint_hash() { fingerprint_lines | shasum -a 256 | awk '{print $1}'; }
83
+
84
+ if [ "$MODE" = "fingerprint" ]; then
85
+ fingerprint_lines
86
+ echo "指纹 sha256 : $(fingerprint_hash)"
87
+ exit 0
88
+ fi
89
+
90
+ if [ "$MODE" = "baseline" ]; then
91
+ { fingerprint_lines; echo "指纹 sha256 : $(fingerprint_hash)"; } > "$FINGERPRINT_FILE"
92
+ echo "🔖 已记基线:$FINGERPRINT_FILE"
93
+ exit 0
94
+ fi
95
+
96
+ # ── 体检(--check):打印将要用什么 + 校验指纹,**不启动 App** ──────────────
97
+ echo "── 官方桌面端隔离试跑体检 ──────────────────────────────────────────"
98
+ echo " App 二进制 : $APP"
99
+ if [ -x "$APP" ]; then echo " App 存在 : ✅ 可执行"; else echo " App 存在 : ❌ 未安装/不可执行"; fi
100
+ echo " DSH_HOME(独立) : $DSH_HOME"
101
+ echo " ARGENT_DATA_DIR : $ARGENT_DATA_DIR"
102
+ echo " 生产 home(只读) : $PROD_HOME"
103
+ if [ -f "$PAUSE_SWITCH" ]; then
104
+ echo " PAUSE_FLOW_WATCH : ✅ 存在(失败回路已暂停)"
105
+ else
106
+ echo " PAUSE_FLOW_WATCH : ⚠️ 不存在 —— 启动时会自动创建(缺它泵可能跑)"
107
+ fi
108
+ if [ "${ARGENT_ALLOW_ISOLATED_PUMP:-}" = "1" ]; then
109
+ echo " 隔离放行开关 : ⚠️ ARGENT_ALLOW_ISOLATED_PUMP=1(泵会在隔离目录里真跑;确认它扫的是隔离源)"
110
+ else
111
+ echo " 隔离放行开关 : ✅ 未设(插件安全阀:隔离数据目录 ⇒ 失败回路默认暂停)"
112
+ fi
113
+ echo " App 参数 : ${APP_ARGS[*]:-(无)}"
114
+
115
+ echo
116
+ echo "── 生产 $PROD_HOME 指纹(应始终不变)────────────────────────────"
117
+ fingerprint_lines
118
+ NOW_HASH="$(fingerprint_hash)"
119
+ echo "指纹 sha256 : $NOW_HASH"
120
+ if [ -f "$FINGERPRINT_FILE" ]; then
121
+ if grep -q "$NOW_HASH" "$FINGERPRINT_FILE"; then
122
+ echo " ✅ 与基线一致(${FINGERPRINT_FILE})—— 生产数据目录未被动过"
123
+ else
124
+ echo " ❌ 与基线**不一致**(${FINGERPRINT_FILE}):"
125
+ diff <(grep -v '指纹 sha256' "$FINGERPRINT_FILE") <(fingerprint_lines) | sed 's/^/ /' || true
126
+ fi
127
+ else
128
+ echo " ·(还没有基线;先跑:bash scripts/desktop-trial.sh --refresh-baseline)"
129
+ fi
130
+
131
+ if [ "$MODE" = "check" ]; then
132
+ exit 0
133
+ fi
134
+
135
+ # ── 启动(隔离)────────────────────────────────────────────────────────────
136
+ if [ ! -x "$APP" ]; then
137
+ echo "未安装官方桌面端:$APP" >&2
138
+ echo "(可用 ARGENT_DESKTOP_APP=<路径> 指定;App 内 CLI:Contents/Resources/runtime/cli/bin/dsh)" >&2
139
+ exit 1
140
+ fi
141
+ mkdir -p "$ARGENT_DATA_DIR"
142
+ [ -f "$PAUSE_SWITCH" ] || : > "$PAUSE_SWITCH" # 缺这条会误扫/派发生产流程
143
+ echo "🔒 隔离启动:home=${DSH_HOME}|数据目录=${ARGENT_DATA_DIR}|失败回路=暂停(${PAUSE_SWITCH})"
144
+ echo " 生产指纹(启动前):$(fingerprint_hash)"
145
+ echo " 启动后再核对:bash scripts/desktop-trial.sh --check"
146
+ exec "$APP" ${APP_ARGS[@]+"${APP_ARGS[@]}"}
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env bash
2
+ # dev-window.sh —— **开发/测试窗口闸**(2026-09-23,用户口径)
3
+ #
4
+ # 用户原话:「你在开发测试时,就应该暂停 cron 或者失败回路任何试图与你撞车的触发点。」
5
+ # 本脚本把这句话变成一条命令:**开窗 = 暂停所有会与你抢店铺窗口的自动触发点**,
6
+ # 关窗 = 原样恢复。它只碰"开关文件",不动 launchd 作业(卸载/重装作业反而容易留下半残状态)。
7
+ #
8
+ # 用法:
9
+ # bash scripts/dev-window.sh status # 看当前:谁在跑、哪些触发点被暂停、店铺锁被谁占着
10
+ # bash scripts/dev-window.sh on [--notes "…"] # 开窗:暂停所有流程的轮询开关 + 失败回路
11
+ # bash scripts/dev-window.sh off # 关窗:只恢复**本窗口创建**的那些开关
12
+ #
13
+ # 为什么需要它(2026-09-23 §8.57 事故):我在改代码/实跑期间,失败回路把一个角色会话唤醒、
14
+ # 角色手动派了同一张票 ⇒ 两个执行者同店、后到者让紫鸟换调试端口、把先到者的标签页关掉
15
+ # ⇒ 双方都往生产表里写「失败」,看起来像平台故障。PAUSE_POLL 只挡 cron 自动认领,
16
+ # **挡不住手动派发** ⇒ 三个触发点必须一起停:
17
+ # ① 各流程的 cron 轮询开关(launchd 定时器读的那个文件,逐流程一份)
18
+ # ② 失败回路(PAUSE_FLOW_WATCH,plugins/argent-flow 的泵每轮读)
19
+ # ③ 手动派发 —— 开关文件管不了,由**店铺级执行者锁**(orchestrator 取窗口前非阻塞抢锁)兜住
20
+ #
21
+ # 口径与红线:
22
+ # - **只暂停、不卸载**:开关文件删除即恢复,绝不 launchctl bootout(半残状态比暂停更贵);
23
+ # - **只恢复自己建的**:关窗按标记文件里记录的清单删,不误删别人设的开关;
24
+ # - 标记文件带 expiresAt:超时后 status 会**响亮提醒**(防止"以为在开发窗口里"的静默停摆);
25
+ # - 开窗前会检查"有没有执行者正在跑",有就拒绝开窗(先等它跑完,别在别人跑的时候暂停触发点)。
26
+ set -uo pipefail
27
+
28
+ REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
29
+ TTL_MIN_DEFAULT=240
30
+
31
+ pick_py() {
32
+ local c
33
+ for c in "${ARGENT_PY:-}" "${DSH_HOME:-$HOME/.dsh}/argent/venv/bin/python3" \
34
+ "$HOME/.dsh/argent/venv/bin/python3" "$REPO/.venv/bin/python3" \
35
+ "$(command -v python3 2>/dev/null || true)"; do
36
+ [ -n "$c" ] && [ -x "$c" ] && { printf '%s' "$c"; return 0; }
37
+ done
38
+ return 1
39
+ }
40
+ PY="$(pick_py)" || { echo "❌ 找不到 python3" >&2; exit 2; }
41
+
42
+ # 数据目录:**唯一判据**在 Python 单一实现(scripts/lib/argent_datadir.py,经 CLI 包装
43
+ # scripts/argent-datadir.py)——⛔ 不在 bash 里重写三级链(`${ARGENT_DATA_DIR:-$HOME/.dsh/argent}`
44
+ # 那种写法 = 隔离实例一半写隔离目录、一半写生产)。取不到 ⇒ 响亮停住(fail-closed,不猜目录)。
45
+ DATA_DIR="$("$PY" "$REPO/scripts/argent-datadir.py" 2>/dev/null || true)"
46
+ if [ -z "$DATA_DIR" ]; then
47
+ echo "[dev-window] ❌ 数据目录判据不可用($REPO/scripts/argent-datadir.py 取不到)⇒ 停住,不猜目录" >&2
48
+ exit 2
49
+ fi
50
+ MARKER="$DATA_DIR/DEV_WINDOW.json"
51
+ WATCH_PAUSE="$DATA_DIR/PAUSE_FLOW_WATCH"
52
+
53
+ MODE="${1:-status}"; shift || true
54
+ NOTES=""; TTL_MIN="$TTL_MIN_DEFAULT"
55
+ while [ $# -gt 0 ]; do
56
+ case "$1" in
57
+ --notes) NOTES="${2:-}"; shift 2;;
58
+ --ttl-min) TTL_MIN="${2:-$TTL_MIN_DEFAULT}"; shift 2;;
59
+ -h|--help) sed -n '2,30p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0;;
60
+ *) shift;;
61
+ esac
62
+ done
63
+
64
+ flow_ids() { "$PY" "$REPO/scripts/flow_config.py" --list 2>/dev/null | awk '/^ [a-z]/{print $1}'; }
65
+ pause_of() { "$PY" "$REPO/scripts/flow_config.py" --flow "$1" --get paths.pauseSwitch 2>/dev/null; }
66
+ running_executors() { pgrep -fl "orchestrator\.py" 2>/dev/null | grep -v "pgrep" || true; }
67
+
68
+ show_store_locks() {
69
+ "$PY" - "$@" <<'PY'
70
+ import fcntl, glob, json, os, sys
71
+ lock_dir = os.environ.get('ARGENT_LOCK_DIR') or '/tmp'
72
+ locks = sorted(glob.glob(os.path.join(lock_dir, 'ziniao-store-*.lock')))
73
+ if not locks:
74
+ print(' (无店铺锁文件)')
75
+ for p in locks:
76
+ try:
77
+ f = open(p, 'a+')
78
+ fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
79
+ except OSError:
80
+ try:
81
+ f.seek(0); holder = (f.read() or '').strip()
82
+ except Exception:
83
+ holder = ''
84
+ print(f' 🔴 被占用 {os.path.basename(p)} ← {holder or "(无持有者信息)"}')
85
+ continue
86
+ try:
87
+ f.seek(0); holder = (f.read() or '').strip()
88
+ finally:
89
+ try: fcntl.flock(f.fileno(), fcntl.LOCK_UN); f.close()
90
+ except Exception: pass
91
+ print(f' ⚪ 空闲 {os.path.basename(p)}' + (f'(上次持有者 {holder})' if holder else ''))
92
+ PY
93
+ }
94
+
95
+ case "$MODE" in
96
+ status)
97
+ echo "── 执行者进程 ──"
98
+ if [ -n "$(running_executors)" ]; then running_executors | sed 's/^/ 🏃 /'; else echo " (无)"; fi
99
+ echo "── 店铺级执行者锁(${ARGENT_LOCK_DIR:-/tmp}/ziniao-store-*.lock)──"
100
+ show_store_locks
101
+ echo "── 开发窗口标记 ──"
102
+ if [ -f "$MARKER" ]; then
103
+ "$PY" - "$MARKER" <<'PY'
104
+ import json, sys, time, datetime
105
+ d = json.load(open(sys.argv[1], encoding='utf-8'))
106
+ exp = d.get('expiresAt', '')
107
+ late = ''
108
+ try:
109
+ if exp and datetime.datetime.fromisoformat(exp) < datetime.datetime.now():
110
+ late = ' ⚠️ **已过期**——别忘了 `dev-window.sh off`'
111
+ except Exception:
112
+ pass
113
+ print(f" 🧪 开窗中:owner={d.get('owner','?')} since={d.get('since','?')} expiresAt={exp}{late}")
114
+ if d.get('notes'): print(f" 备注:{d['notes']}")
115
+ for it in d.get('created', []):
116
+ print(f" - {it.get('flow','(watch)'):<20} {it.get('path','')}")
117
+ PY
118
+ else
119
+ echo " (无开发窗口)"
120
+ fi
121
+ echo "── 各流程轮询开关(存在=已暂停)──"
122
+ for f in $(flow_ids); do
123
+ p="$(pause_of "$f")"; [ -n "$p" ] || continue
124
+ if [ -f "$p" ]; then echo " ⏸ $f $(basename "$p")"; else echo " ▶️ $f 运行中"; fi
125
+ done
126
+ if [ -f "$WATCH_PAUSE" ]; then echo " ⏸ 失败回路 PAUSE_FLOW_WATCH"; else echo " ▶️ 失败回路 运行中"; fi
127
+ ;;
128
+ on)
129
+ if [ -f "$MARKER" ]; then
130
+ echo "❌ 已有开发窗口(${MARKER})——先执行 dev-window.sh status 看,再 off 关。" >&2
131
+ echo " 为什么拒绝二次开窗:第二次会把「开窗前就存在」记到自己头上,关窗时该恢复的被当成别人的留下。" >&2
132
+ exit 1
133
+ fi
134
+ if [ -n "$(running_executors)" ]; then
135
+ echo "❌ 有执行者正在跑,拒绝开窗(先等它跑完):" >&2
136
+ running_executors | sed 's/^/ /' >&2
137
+ exit 1
138
+ fi
139
+ held="$("$PY" - <<'PY'
140
+ import fcntl, glob, os
141
+ held = []
142
+ lock_dir = os.environ.get('ARGENT_LOCK_DIR') or '/tmp'
143
+ for p in sorted(glob.glob(os.path.join(lock_dir, 'ziniao-store-*.lock'))):
144
+ try:
145
+ f = open(p, 'a+')
146
+ fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
147
+ fcntl.flock(f.fileno(), fcntl.LOCK_UN); f.close()
148
+ except OSError:
149
+ held.append(os.path.basename(p))
150
+ print('\n'.join(held))
151
+ PY
152
+ )"
153
+ if [ -n "$held" ]; then
154
+ echo "❌ 有店铺执行者锁被占用(别的执行者在跑同一家店),拒绝开窗:" >&2
155
+ echo "$held" | sed 's/^/ /' >&2
156
+ exit 1
157
+ fi
158
+ mkdir -p "$DATA_DIR"
159
+ # **全有或全无**(2026-09-23 自测抓到):解析不出 pauseSwitch 的流程若被静默跳过,
160
+ # 就变成"以为暂停了、其实还在跑"——正是本脚本要消灭的静默停摆。
161
+ # 故先只读解析一遍:任何一条解析不出来 ⇒ 报错退出、**一个开关都不建**。
162
+ if ! "$PY" - "$REPO" <<'PY'
163
+ import os, subprocess, sys
164
+ repo = sys.argv[1]
165
+ py = sys.executable
166
+ out = subprocess.run([py, os.path.join(repo, 'scripts', 'flow_config.py'), '--list'],
167
+ capture_output=True, text=True)
168
+ flows = [ln.split()[0] for ln in out.stdout.splitlines() if ln.startswith(' ') and ln.split()]
169
+ bad = []
170
+ for fid in flows:
171
+ r = subprocess.run([py, os.path.join(repo, 'scripts', 'flow_config.py'), '--flow', fid,
172
+ '--get', 'paths.pauseSwitch'], capture_output=True, text=True)
173
+ if r.returncode != 0 or not r.stdout.strip():
174
+ bad.append((fid, (r.stderr or r.stdout or '').strip().splitlines()[-1:] or ['(无输出)']))
175
+ if bad:
176
+ print('❌ 有流程的暂停开关解析不出来 —— **拒绝开窗**(一个开关都不建):', file=sys.stderr)
177
+ for fid, why in bad:
178
+ print(f' {fid}: {why[0][:160]}', file=sys.stderr)
179
+ print(' 修法:确认部署 env 就位(如 ~/.dsh/argent/fulfillment.env)后重试;'
180
+ '详见 docs/本机接班运行清单.md §8.58', file=sys.stderr)
181
+ sys.exit(2)
182
+ print(f'({len(flows)} 个流程的暂停开关均可解析)')
183
+ PY
184
+ then
185
+ exit 2
186
+ fi
187
+ "$PY" - "$DATA_DIR" "$MARKER" "$WATCH_PAUSE" "$TTL_MIN" "$NOTES" "$REPO" <<'PY'
188
+ import datetime, json, os, pathlib, subprocess, sys
189
+ data_dir, marker, watch_pause, ttl_min, notes, repo = sys.argv[1:7]
190
+ py = sys.executable
191
+ out = subprocess.run([py, os.path.join(repo, 'scripts', 'flow_config.py'), '--list'],
192
+ capture_output=True, text=True).stdout
193
+ flows = [ln.split()[0] for ln in out.splitlines() if ln.startswith(' ') and ln.split()]
194
+ created = []
195
+ for fid in flows:
196
+ p = subprocess.run([py, os.path.join(repo, 'scripts', 'flow_config.py'), '--flow', fid,
197
+ '--get', 'paths.pauseSwitch'], capture_output=True, text=True).stdout.strip()
198
+ if not p:
199
+ continue
200
+ existed = os.path.exists(p)
201
+ pathlib.Path(p).parent.mkdir(parents=True, exist_ok=True)
202
+ pathlib.Path(p).touch()
203
+ created.append({'flow': fid, 'path': p, 'preExisting': existed})
204
+ watch_existed = os.path.exists(watch_pause)
205
+ pathlib.Path(watch_pause).touch()
206
+ now = datetime.datetime.now()
207
+ doc = {
208
+ 'version': 1,
209
+ 'owner': os.environ.get('ARGENT_ACTOR') or f'pid{os.getpid()}',
210
+ 'since': now.isoformat(timespec='seconds'),
211
+ 'expiresAt': (now + datetime.timedelta(minutes=int(ttl_min))).isoformat(timespec='seconds'),
212
+ 'notes': notes,
213
+ 'created': created,
214
+ 'watch': {'path': watch_pause, 'preExisting': watch_existed},
215
+ }
216
+ pathlib.Path(marker).write_text(json.dumps(doc, ensure_ascii=False, indent=2), encoding='utf-8')
217
+ print(f"🧪 开发窗口已开:{len(created)} 个流程轮询开关 + 失败回路 已暂停(TTL {ttl_min} 分钟)")
218
+ for it in created:
219
+ print(f" ⏸ {it['flow']:<20} {it['path']}" + ('(原本就存在,关窗时保留)' if it['preExisting'] else ''))
220
+ print(f" ⏸ 失败回路 {watch_pause}" + ('(原本就存在,关窗时保留)' if watch_existed else ''))
221
+ print(f" 标记:{marker}")
222
+ print(" ⚠️ live 票派发不归开关管——由**店铺级执行者锁**兜底;收工请执行 dev-window.sh off")
223
+ PY
224
+ ;;
225
+ off)
226
+ if [ ! -f "$MARKER" ]; then
227
+ echo "ℹ️ 没有开发窗口标记(${MARKER}):本脚本只恢复自己建的开关,不猜。"
228
+ echo " 若确实要恢复全部暂停,请逐个删:$(flow_ids | tr '\n' ' ') 的 pauseSwitch + PAUSE_FLOW_WATCH"
229
+ exit 0
230
+ fi
231
+ "$PY" - "$MARKER" <<'PY'
232
+ import json, os, sys
233
+ d = json.load(open(sys.argv[1], encoding='utf-8'))
234
+ removed, kept = [], []
235
+ for it in d.get('created', []):
236
+ p = it.get('path')
237
+ if not p or not os.path.exists(p):
238
+ continue
239
+ if it.get('preExisting'):
240
+ kept.append((it.get('flow', '?'), p)); continue
241
+ os.remove(p); removed.append((it.get('flow', '?'), p))
242
+ w = d.get('watch') or {}
243
+ if w.get('path') and os.path.exists(w['path']):
244
+ if w.get('preExisting'):
245
+ kept.append(('(watch)', w['path']))
246
+ else:
247
+ os.remove(w['path']); removed.append(('失败回路', w['path']))
248
+ os.remove(sys.argv[1])
249
+ print(f"🔓 开发窗口已关:恢复 {len(removed)} 个开关,保留 {len(kept)} 个(开窗前就存在/别人设的)")
250
+ for f, p in removed:
251
+ print(f" ▶️ {f:<20} {p}")
252
+ for f, p in kept:
253
+ print(f" ⏸ 保留 {f:<16} {p}")
254
+ PY
255
+ ;;
256
+ *)
257
+ echo "用法:bash scripts/dev-window.sh status | on [--notes …] [--ttl-min N] | off" >&2
258
+ exit 2
259
+ ;;
260
+ esac