@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,214 @@
1
+ #!/usr/bin/env node
2
+ // local-usage.mjs —— 本机**会话级 token 计量**(成本锚点,走官方直连也能测)。
3
+ //
4
+ // ## 为什么需要它
5
+ // 服务端只在**走问述代理**时才有 token 记账。本机(以及"自带 key"的客户机)走官方直连
6
+ // ⇒ 那部分模型成本在服务端**完全不可见**。但 dsh 会把每次模型调用的用量写进会话日志的
7
+ // `assistant/message.data.usage`(`{inputTokens, outputTokens, cacheReadTokens, reasoningTokens}`,
8
+ // 其中 inputTokens 已按适配器约定**扣除**缓存命中)⇒ 直接从磁盘就能算出真实成本。
9
+ //
10
+ // ## 会话日志格式(踩坑记录,别再用现成库重蹈)
11
+ // `~/.dsh/sessions/<workspace-slug>/<session-id>/session.jsonl.zstd` 是**多帧 zstd 顺序拼接**、
12
+ // **没有长度前缀**(实测 frame2 的 magic 紧跟在 frame1 末尾)。
13
+ // · `zlib.zstdDecompressSync(整个文件)` 只解出**第一帧**(首行 header)⇒ 看起来"没有 usage";
14
+ // · `createZstdDecompress()` 流式解码到第二帧直接报 `Unknown frame descriptor`。
15
+ // 正确做法:**扫描 magic(28 b5 2f fd) 切帧,逐帧解压再按行 JSON.parse**(本文件 multizstd())。
16
+ //
17
+ // ## 输出
18
+ // `~/.dsh/argent/usage/local-requests.jsonl`:`{session, kind, seq, ts, model, input, cacheRead, output,
19
+ // reasoning, total}`,按 `session+seq` 去重、增量追加(状态存 `local-usage-state.json`)。
20
+ // **只记用量,不算钱**——计价统一在 `scripts/usage_report.py`(一份费率表,避免两处漂移)。
21
+ //
22
+ // 用法:
23
+ // node scripts/local-usage.mjs # 增量扫描全部会话
24
+ // node scripts/local-usage.mjs --all # 忽略状态,全量重扫
25
+ // node scripts/local-usage.mjs --json # 机器可读汇总
26
+ // node scripts/local-usage.mjs --sessions-root <dir> --out <file> --state <file> --model <name>
27
+ // 退出码:0 正常;1 读盘失败
28
+
29
+ import { zstdDecompressSync } from 'node:zlib'
30
+ import { readFileSync, writeFileSync, appendFileSync, mkdirSync, readdirSync, statSync, existsSync, renameSync } from 'node:fs'
31
+ import { join, dirname } from 'node:path'
32
+ import { homedir } from 'node:os'
33
+ import { resolveDataDir } from './lib/argent-datadir.mjs'
34
+
35
+ const MAGIC = Buffer.from([0x28, 0xb5, 0x2f, 0xfd])
36
+
37
+ function arg(name, fallback) {
38
+ const i = process.argv.indexOf(`--${name}`)
39
+ return i === -1 ? fallback : process.argv[i + 1]
40
+ }
41
+ const has = (name) => process.argv.includes(`--${name}`)
42
+
43
+ const DATA = resolveDataDir({ env: process.env }).dir // 内核三级链(唯一判据)
44
+ const SESSIONS_ROOT = arg('sessions-root', process.env.DSH_SESSIONS_DIR || join(homedir(), '.dsh', 'sessions'))
45
+ const OUT = arg('out', join(DATA, 'usage', 'local-requests.jsonl'))
46
+ const STATE = arg('state', join(DATA, 'usage', 'local-usage-state.json'))
47
+ const MODEL = arg('model', process.env.ARGENT_USAGE_MODEL || 'deepseek-v4-flash')
48
+ const ALL = has('all')
49
+ const JSON_OUT = has('json')
50
+
51
+ /** 多帧 zstd 顺序拼接 → 逐帧解压 → 行数组。解不开的帧跳过(可能是正在写入的尾帧)。 */
52
+ function multizstd(buf) {
53
+ const offs = []
54
+ let i = buf.indexOf(MAGIC)
55
+ while (i !== -1) { offs.push(i); i = buf.indexOf(MAGIC, i + 1) }
56
+ offs.push(buf.length)
57
+ const lines = []
58
+ let bad = 0
59
+ for (let k = 0; k < offs.length - 1; k++) {
60
+ let txt
61
+ try { txt = zstdDecompressSync(buf.subarray(offs[k], offs[k + 1])).toString('utf8') } catch { bad++; continue }
62
+ for (const l of txt.split('\n')) if (l.trim()) lines.push(l)
63
+ }
64
+ return { lines, frames: offs.length - 1, badFrames: bad }
65
+ }
66
+
67
+ /**
68
+ * 流程会话白名单(权威来源,别猜):
69
+ * ① `~/.dsh/argent/flow-dispatch-<flow>.json` 里落盘的 sessionId —— 2026-09-16 起
70
+ * `flow-trigger-wake.mjs` 复用同一个会话跑某个流程(不再每轮建会话);
71
+ * ② 旧架构的角色会话 `gw-*`(含其子代理 `<root>~0040<uuid>`)。
72
+ * 注意:flow 会话是**普通 uuid**,只看"目录名像不像角色"会把全部流程用量误判成对话开销
73
+ * (2026-09-22 实测:修之前流程侧只剩 09-10 之前的旧数据,本机每票成本算成 0)。
74
+ */
75
+ function loadFlowSessions() {
76
+ const ids = new Set()
77
+ try {
78
+ for (const f of readdirSync(DATA)) {
79
+ if (!/^flow-dispatch-.*\.json$/.test(f)) continue
80
+ try {
81
+ const j = JSON.parse(readFileSync(join(DATA, f), 'utf8'))
82
+ if (j?.sessionId) ids.add(String(j.sessionId))
83
+ } catch { /* 坏文件跳过 */ }
84
+ }
85
+ } catch { /* 目录不存在 */ }
86
+ return ids
87
+ }
88
+
89
+ const FLOW_SESSIONS = loadFlowSessions()
90
+
91
+ function kindOf(sessionId, flowIds = FLOW_SESSIONS) {
92
+ const root = sessionId.split('~0040')[0]
93
+ if (root.startsWith('gw-')) return 'flow'
94
+ if (flowIds.has(root)) return 'flow'
95
+ return 'chat'
96
+ }
97
+
98
+ function listSessionFiles(root) {
99
+ const out = []
100
+ if (!existsSync(root)) return out
101
+ for (const ws of readdirSync(root)) {
102
+ const wsDir = join(root, ws)
103
+ let entries
104
+ try { entries = readdirSync(wsDir) } catch { continue }
105
+ for (const sid of entries) {
106
+ // 会话日志命名随格式版本变化(2026-09 实测):旧 `session.jsonl.zstd`、
107
+ // 新 `session.v3.jsonl.zstd`(格式 v3)。写死文件名会让整段用量**静默消失**。
108
+ let sfiles
109
+ try { sfiles = readdirSync(join(wsDir, sid)) } catch { continue }
110
+ for (const name of sfiles) {
111
+ if (!/^session.*\.jsonl\.zstd$/.test(name)) continue
112
+ const f = join(wsDir, sid, name)
113
+ try { if (statSync(f).isFile()) out.push({ session: sid, file: name, path: f, size: statSync(f).size }) } catch { /* 跳过 */ }
114
+ }
115
+ }
116
+ }
117
+ return out
118
+ }
119
+
120
+ function loadState() {
121
+ try { return JSON.parse(readFileSync(STATE, 'utf8')) } catch { return { sessions: {} } }
122
+ }
123
+
124
+ /**
125
+ * 已记账键集合(`session#seq`)。
126
+ * 为什么要它:会话日志**可能同目录两份**(v0→v3 迁移后 `session.jsonl.zstd` 与
127
+ * `session.v3.jsonl.zstd` 并存,实测 17 个会话如此),而"上次读到哪个 seq"是**按会话**记的
128
+ * ⇒ 两份文件互相覆盖状态、每轮各读一遍,实测产生 17462 行重复、把成本虚增。
129
+ * 迁移前后两份日志里的同一事件 seq 相同 ⇒ 按 `session#seq` 去重才是正确语义。
130
+ */
131
+ function loadSeen(path) {
132
+ const seen = new Set()
133
+ try {
134
+ for (const l of readFileSync(path, 'utf8').split('\n')) {
135
+ if (!l.trim()) continue
136
+ try { const r = JSON.parse(l); seen.add(`${r.session}#${r.seq}`) } catch { /* 跳过坏行 */ }
137
+ }
138
+ } catch { /* 首次运行没有文件 */ }
139
+ return seen
140
+ }
141
+
142
+ function main() {
143
+ const state = ALL ? { sessions: {} } : loadState()
144
+ const seen = ALL ? new Set() : loadSeen(OUT)
145
+ const files = listSessionFiles(SESSIONS_ROOT)
146
+ if (!files.length) {
147
+ console.log(` ⚠️ 未找到会话日志:${SESSIONS_ROOT}`)
148
+ return 0
149
+ }
150
+ mkdirSync(dirname(OUT), { recursive: true })
151
+ const rows = []
152
+ let scanned = 0, skipped = 0, badFrames = 0
153
+ for (const f of files) {
154
+ const key = `${f.session}::${f.file}` // 状态按**文件**记(同会话可能两份日志)
155
+ const last = state.sessions[key] ?? -1
156
+ // 文件变小 ⇒ 被轮转/重建,从头读
157
+ const prevSize = state.sessions[`${key}#size`]
158
+ const from = (prevSize !== undefined && f.size < prevSize) ? -1 : last
159
+ let buf
160
+ try { buf = readFileSync(f.path) } catch { skipped++; continue }
161
+ scanned++
162
+ let decoded
163
+ try { decoded = multizstd(buf) } catch { skipped++; continue }
164
+ badFrames += decoded.badFrames
165
+ let maxSeq = from
166
+ for (const line of decoded.lines) {
167
+ if (!line.includes('"usage"')) continue
168
+ let e
169
+ try { e = JSON.parse(line) } catch { continue }
170
+ if (e?.type !== 'assistant/message' || !e.data?.usage) continue
171
+ const seq = Number(e.seq ?? -1)
172
+ if (seq > maxSeq) maxSeq = seq
173
+ if (seq <= from) continue
174
+ const skey = `${f.session}#${seq}`
175
+ if (seen.has(skey)) continue // 幂等:迁移/重建/重扫都不会重复计费
176
+ seen.add(skey)
177
+ const u = e.data.usage
178
+ const input = Number(u.inputTokens || 0), cacheRead = Number(u.cacheReadTokens || 0)
179
+ const output = Number(u.outputTokens || 0), reasoning = Number(u.reasoningTokens || 0)
180
+ rows.push({
181
+ session: f.session, kind: kindOf(f.session), seq, ts: Math.round(Number(e.time || 0)),
182
+ model: MODEL, input, cacheRead, output, reasoning,
183
+ total: input + cacheRead + output + (u.totalTokens !== undefined ? 0 : 0),
184
+ })
185
+ }
186
+ state.sessions[key] = maxSeq
187
+ state.sessions[`${key}#size`] = f.size
188
+ }
189
+ if (rows.length) appendFileSync(OUT, rows.map((r) => JSON.stringify(r)).join('\n') + '\n')
190
+ mkdirSync(dirname(STATE), { recursive: true })
191
+ const tmp = `${STATE}.tmp`
192
+ writeFileSync(tmp, JSON.stringify(state, null, 2) + '\n')
193
+ renameSync(tmp, STATE)
194
+
195
+ const byKind = {}
196
+ for (const r of rows) {
197
+ const k = (byKind[r.kind] ||= { requests: 0, input: 0, cacheRead: 0, output: 0 })
198
+ k.requests++; k.input += r.input; k.cacheRead += r.cacheRead; k.output += r.output
199
+ }
200
+ const summary = { sessionsScanned: scanned, sessionsSkipped: skipped, files: files.length,
201
+ newRequests: rows.length, badFrames, out: OUT, state: STATE, byKind }
202
+ if (JSON_OUT) console.log(JSON.stringify(summary, null, 2))
203
+ else {
204
+ console.log(` 本机计量:新增 ${rows.length} 次请求(扫描 ${scanned}/${files.length} 个会话,坏帧 ${badFrames})`)
205
+ for (const [k, v] of Object.entries(byKind)) {
206
+ console.log(` ${k === 'flow' ? '流程角色' : '对话会话'}:${v.requests} 次|输入(未命中) ${v.input.toLocaleString()}`
207
+ + `|缓存命中 ${v.cacheRead.toLocaleString()}|输出 ${v.output.toLocaleString()}`)
208
+ }
209
+ console.log(` → ${OUT}`)
210
+ }
211
+ return 0
212
+ }
213
+
214
+ process.exit(main())
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * migrate-providers —— 把「劫持官方 provider」的旧配置迁移成**双 provider 并存**(2026-09-30,Argent 0.3)
4
+ *
5
+ * ## 迁移什么
6
+ * 旧形态:`llm-deepseek` 实例(provider id 固定 `deepseek-official`)被配成 `WHYSHU_API_KEY` +
7
+ * `baseURL: https://whyshu.com/api/argent/v1` ⇒ **官方直连被劫持**。
8
+ * 新形态:`llm-deepseek` = 真·官方(api.deepseek.com / DEEPSEEK_API_KEY);
9
+ * `llm-whyshu` = 问述代理(whyshu.com/api/argent/v1 / WHYSHU_API_KEY,provider id `whyshu`)。
10
+ *
11
+ * ## 两条路径(**互斥且各自最小改动**)
12
+ * ① 劫持 → 重写两段:官方段还原 + 问述段接手;
13
+ * ② 未劫持但缺 `llm-whyshu` → **原样保留整份文件**,仅在末尾追加问述段(**绝不重建/剥离任何段**)。
14
+ * (早先版本在 ② 里剥段后未回填 ⇒ 会**删掉用户的官方段**,实测夹具抓到;已废弃该写法。)
15
+ *
16
+ * ## 幂等 + 安全
17
+ * 已就绪 ⇒ 什么都不做;改前备份(带时间戳);改后回读校验非空,失败即从备份恢复。
18
+ *
19
+ * 用法:node scripts/migrate-providers.mjs [--dsh-home ~/.dsh] [--profile web] [--dry-run]
20
+ */
21
+ import { existsSync, readFileSync, writeFileSync, copyFileSync } from 'node:fs'
22
+ import { homedir } from 'node:os'
23
+ import { join } from 'node:path'
24
+
25
+ const arg = (n, d) => { const i = process.argv.indexOf(n); return i > -1 ? process.argv[i + 1] : d }
26
+ const DRY = process.argv.includes('--dry-run')
27
+ // ⛔ 2026-09-30 用户明确要求:**不要动官方 provider**(含它的 baseURL/models)⇒ 本脚本默认**绝不改写
28
+ // `llm-deepseek` 段**,也不重建 settings.yaml;只有显式 `--allow-official-rewrite`(人工确认后)才允许。
29
+ const ALLOW_OFFICIAL = process.argv.includes('--allow-official-rewrite')
30
+ const DSH_HOME = arg('--dsh-home', process.env.DSH_HOME || join(homedir(), '.dsh'))
31
+ const PROFILE = arg('--profile', process.env.DSH_PROFILE || 'web')
32
+ const SETTINGS = join(DSH_HOME, 'settings.yaml')
33
+ const PATCH = join(DSH_HOME, 'profiles', PROFILE, 'cordis.patch.yml')
34
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-')
35
+ const log = (s) => console.log(` ${s}`)
36
+
37
+ const OFFICIAL = { baseURL: 'https://api.deepseek.com', apiKeyEnv: 'DEEPSEEK_API_KEY' }
38
+ const WHYS = { baseURL: 'https://whyshu.com/api/argent/v1', apiKeyEnv: 'WHYSHU_API_KEY' }
39
+ // 上下文窗口声明(2026-10-02 修正):两个 provider **各有各的真实窗口**,不可共用一个数字。
40
+ // · 问述代理 `WHYSHU_CONTEXT_WINDOW = 131072` —— 上游真实窗口。曾声明 1000000(0.3.0 那次
41
+ // 131072→1000000)⇒ dsh 判定"上下文还很空"、迟迟不压缩 ⇒ 每回合重发的上下文越滚越大 ⇒
42
+ // 回合更长 ⇒ 更容易撞上传输层抖动(`DeepSeek Messages transport failed` / TRANSPORT)。
43
+ // · 官方直连 —— 取 dsh 官方目录的默认值(`dsh-llm-deepseek` 的 `DEFAULT_CONTEXT_WINDOW = 1e6`),
44
+ // 与"不声明 models 时"的行为保持一致,避免我们自己引入偏差。
45
+ const WHYSHU_CONTEXT_WINDOW = 131072
46
+ const OFFICIAL_CONTEXT_WINDOW = 1000000
47
+ // ⚠️ `maxTokens` 必须与 `contextWindow` **成对**看(2026-10-02 硬约束,别把两者写成同值):
48
+ // `dsh-compaction-basic` 的 `resolveCompactSpec` 要求
49
+ // contextWindow - maxTokens > 0 且 上式 - headroomTokens(默认 65536) > 0,
50
+ // 否则抛 TargetPressureConfigError,而它在 `agent/pre-step` 里被 catch ⇒ **只 warn 一次、
51
+ // 此后每步静默跳过压缩**(比不压缩更难发现)。适配器默认 maxTokens = 256000。
52
+ // · 问述代理:131072 窗 ⇒ maxTokens 必须 < 65536,取 32768(阈值 = 32768)。
53
+ // · 官方直连:1e6 窗 ⇒ 131072 绰绰有余(阈值 = 800000)。
54
+ const WHYSHU_MAX_TOKENS = 32768
55
+ const OFFICIAL_MAX_TOKENS = 131072
56
+ if (WHYSHU_MAX_TOKENS + 65536 >= WHYSHU_CONTEXT_WINDOW) {
57
+ throw new Error(`migrate-providers: WHYSHU_MAX_TOKENS(${WHYSHU_MAX_TOKENS}) + headroom(65536) 必须小于 WHYSHU_CONTEXT_WINDOW(${WHYSHU_CONTEXT_WINDOW}),否则 compaction-basic 会静默禁用压缩`)
58
+ }
59
+ const names = { official: { 'deepseek-flash': 'DeepSeek-V4.1-Flash(官方直连)', 'deepseek-v4-pro': 'DeepSeek-V4-Pro(官方直连)' },
60
+ whyshu: { 'deepseek-flash': 'DeepSeek V4.1 Flash(问述代理)', 'deepseek-v4-pro': 'DeepSeek V4 Pro(问述代理)' } }
61
+ const block = (ids, nameMap, contextWindow = WHYSHU_CONTEXT_WINDOW, maxTokens = WHYSHU_MAX_TOKENS) => ids.map((id) => ` - id: ${id}\n name: ${nameMap[id]}\n contextWindow: ${contextWindow}\n maxTokens: ${maxTokens}`).join('\n')
62
+
63
+ /** 取顶层段 [start,end)(段 = 一个 `key:` 行 + 其后所有缩进行/空行)。 */
64
+ function section(lines, key) {
65
+ const start = lines.findIndex((l) => l.trimEnd() === `${key}:`)
66
+ if (start < 0) return null
67
+ let end = start + 1
68
+ while (end < lines.length && /^\s{2,}/.test(lines[end])) end += 1
69
+ return { start, end, text: lines.slice(start, end).join('\n') }
70
+ }
71
+
72
+ let text = ''
73
+ try { text = readFileSync(SETTINGS, 'utf8') } catch { text = '' }
74
+ const lines = text.split('\n')
75
+ const off = section(lines, 'llm-deepseek')
76
+ const whys = section(lines, 'llm-whyshu')
77
+ // 劫持判定:**只看 llm-deepseek 段内部**(跨段正则会误判,实测踩过)
78
+ const hijacked = !!off && /whyshu\.com/.test(off.text)
79
+ const offModels = off ? (off.text.match(/ models:\n(?: [^\n]*\n?)*/) || [''])[0].replace(/^ models:\n/, '') : ''
80
+
81
+ const wants = []
82
+ if (hijacked && !ALLOW_OFFICIAL) {
83
+ log('⛔ 检测到 llm-deepseek 指向问述(曾被劫持),但用户要求**不得自动改写官方段** ⇒ 仅报告、不改动。')
84
+ log(' 如需迁移:确认后加 --allow-official-rewrite(会重写官方段为 api.deepseek.com);否则请手工处理。')
85
+ } else if (hijacked) {
86
+ const rest = [...lines.slice(0, off.start), ...(whys ? lines.slice(whys.end) : lines.slice(off.end))]
87
+ const next = `llm-deepseek:\n baseURL: ${OFFICIAL.baseURL}\n apiKeyEnv: ${OFFICIAL.apiKeyEnv}\n models:\n${block(['deepseek-flash', 'deepseek-v4-pro'], names.official, OFFICIAL_CONTEXT_WINDOW, OFFICIAL_MAX_TOKENS)}\n\nllm-whyshu:\n baseURL: ${WHYS.baseURL}\n apiKeyEnv: ${WHYS.apiKeyEnv}\n models:\n${offModels.trimEnd() || block(['deepseek-flash', 'deepseek-v4-pro'], names.whyshu)}\n${rest.join('\n').trim() ? '\n' + rest.join('\n').replace(/^\n+/, '') : ''}`
88
+ wants.push({ file: SETTINGS, next: next.trimEnd() + '\n', what: '迁移:llm-deepseek 还原官方 + 问述段接手(保留其余键)' })
89
+ } else if (!whys && ALLOW_OFFICIAL) {
90
+ // settings.yaml 里的问述段现在是**可选**(provider 配置已在组合条目内)⇒ 默认也不写这个文件
91
+ const add = `\nllm-whyshu:\n baseURL: ${WHYS.baseURL}\n apiKeyEnv: ${WHYS.apiKeyEnv}\n models:\n${block(['deepseek-flash', 'deepseek-v4-pro'], names.whyshu)}\n`
92
+ wants.push({ file: SETTINGS, next: text.trimEnd() + '\n' + add, what: '补建:末尾追加 llm-whyshu 段(原文件其余内容逐字不动)' })
93
+ }
94
+
95
+ if (existsSync(PATCH)) {
96
+ const pt = readFileSync(PATCH, 'utf8')
97
+ if (!/^\s*- id: llm-whyshu\s*$/m.test(pt)) {
98
+ // ⚠️ **必须包在 `- insert:` 里**:补丁文件里的**普通条目是按 id 覆盖已有条目**,
99
+ // 写成普通条目会报 `patch: entry "llm-whyshu" not found` 并被**静默跳过**(实测:
100
+ // 设置页始终只有一个 provider,且 dump-config 才看得到告警)。
101
+ const block = [
102
+ '', '# ── 问述代理 provider(migrate-providers 写入):与官方 deepseek-official 并存 ──',
103
+ '- insert:',
104
+ ' - id: llm-whyshu',
105
+ " name: 'argent-core/llm-whyshu'",
106
+ ' config:',
107
+ ' apiKeyEnv: WHYSHU_API_KEY', '',
108
+ ].join('\n')
109
+ wants.push({ file: PATCH, next: pt.trimEnd() + '\n' + block, what: '装配:insert llm-whyshu 实例' })
110
+ }
111
+ } // ← 闭合 `if (existsSync(PATCH)) {`(2026-09-30 修复:此前被误删 ⇒ SyntaxError)
112
+
113
+ if (!wants.length) { log('✅ 已是双 provider 形态(无需迁移)'); process.exit(0) }
114
+ for (const w of wants) {
115
+ if (DRY) { log(`[dry-run] ${w.what} → ${w.file}`); continue }
116
+ if (existsSync(w.file)) copyFileSync(w.file, `${w.file}.bak-providers-${stamp}`)
117
+ writeFileSync(w.file, w.next)
118
+ const back = readFileSync(w.file, 'utf8')
119
+ const expect = w.file.endsWith('cordis.patch.yml') ? /- id: llm-whyshu/ : /llm-whyshu:/
120
+ if (!back.trim() || !expect.test(back)) {
121
+ const bak = `${w.file}.bak-providers-${stamp}`
122
+ if (existsSync(bak)) copyFileSync(bak, w.file)
123
+ log(`✗ 回读校验失败(缺少 llm-whyshu 段)⇒ 已从备份恢复:${w.file}`); process.exit(1)
124
+ }
125
+ log(`✅ ${w.what}`)
126
+ }
127
+ log('完成;重启 dsh 后两个 provider 并存生效:bash scripts/dsh-restart.sh')
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env bash
2
+ # migrate-skills.sh — 将 hermes 版 ML 技能迁移为 DSH skills 格式
3
+ # 源: ../argent-ml-seller/skills/mercadolibre/*(同工作区)
4
+ # 目标: ./skills/<id>/
5
+ #
6
+ # 转换规则:
7
+ # 1. frontmatter: 保留 name/tags,新增 description(供 DSH 技能目录匹配)
8
+ # 2. references/ scripts/ templates/ 原样复制(skill bundle 资源)
9
+ # 3. 单一事实源: ml-fulfillment-sop 的 scripts/fulfillment_orchestrator.py 不复制
10
+ # (其真身在 scripts/ 根,见 README);SKILL.md 中引用脚本路径改为 ../../scripts/
11
+ set -euo pipefail
12
+
13
+ REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
14
+ SRC="${ARGENT_ML_SRC:-$REPO_ROOT/../argent-ml-seller/skills/mercadolibre}"
15
+ DEST="$REPO_ROOT/skills"
16
+
17
+ [[ -d "$SRC" ]] || { echo "源技能目录不存在: $SRC" >&2; exit 1; }
18
+ mkdir -p "$DEST"
19
+
20
+ migrate_one() {
21
+ local dir="$1" id version name tags
22
+ id=$(basename "$dir")
23
+ # 解析 frontmatter
24
+ name=$(awk '/^name:/{sub(/^name:[ ]*/,""); print; exit}' "$dir/SKILL.md" | tr -d '"')
25
+ version=$(awk '/^version:/{sub(/^version:[ ]*/,""); print; exit}' "$dir/SKILL.md" | tr -d '"')
26
+ tags=$(awk '/^tags:/{sub(/^tags:[ ]*\[/,""); sub(/\].*/,""); print; exit}' "$dir/SKILL.md" | tr -d ' ')
27
+ [[ -n "$name" ]] || name="$id"
28
+ [[ -n "$version" ]] || version="1.0.0"
29
+
30
+ local dest="$DEST/$id"
31
+ mkdir -p "$dest"
32
+ # 复制资源目录(排除脚本副本,单一事实源)
33
+ for sub in references templates; do
34
+ [[ -d "$dir/$sub" ]] && cp -R "$dir/$sub" "$dest/"
35
+ done
36
+ # scripts: ml-fulfillment-sop 的编排器副本不复制(单一事实源在 ../../scripts/)
37
+ if [[ -d "$dir/scripts" ]]; then
38
+ mkdir -p "$dest/scripts"
39
+ for f in "$dir/scripts"/*; do
40
+ b=$(basename "$f")
41
+ case "$b" in
42
+ fulfillment_orchestrator.py) echo " ↦ 跳过 ${id}/scripts/${b}(单一事实源在 scripts/)" ;;
43
+ *) cp -R "$f" "$dest/scripts/" ;;
44
+ esac
45
+ done
46
+ fi
47
+ # SKILL.md: frontmatter 注入 description,正文脚本路径重写
48
+ local desc="美客多${name}技能(Argent on DSH)——${tags:-ML 运营}。原 hermes 版 v${version}。"
49
+ awk -v desc="$desc" '
50
+ BEGIN { infm=1 }
51
+ infm && /^---$/ { n++; if (n==2) { infm=0; print "description: \"" desc "\""; next } }
52
+ { print }
53
+ ' "$dir/SKILL.md" > "$dest/SKILL.md"
54
+ # 正文中引用编排器副本的路径改指仓库根 scripts/ 单一事实源(orchestrator.py)
55
+ sed -i '' 's|scripts/fulfillment_orchestrator.py|../../scripts/orchestrator.py|g' "$dest/SKILL.md" 2>/dev/null || true
56
+ echo "✓ ${id}(${name} v${version})"
57
+ }
58
+
59
+ for d in "$SRC"/*/; do
60
+ migrate_one "$d"
61
+ done
62
+
63
+ echo
64
+ echo "迁移完成 → $DEST"
65
+ echo "提示: 若 ml-fulfillment-sop 需要旧编排器(开发对照),手动从 hermes 版复制。"