@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,220 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ui-copy-count —— 「中文长文案」计数(设置页文案收敛的**可复验**判据,2026-09-30)
4
+ *
5
+ * ## 为什么要有它
6
+ * 「文案从 102 条收敛到 ≤20 条」如果只是人工数,下次没人能复验。这里把判据写成脚本:
7
+ *
8
+ * 中文长文案 = 客户端 bundle 里**含中文字符、且字面量总长度 ≥ 阈值**的字符串字面量
9
+ * (单引号 / 双引号;默认阈值 18 —— 取这个阈值是因为它复现了用户给的
10
+ * "当前 bundle 有 102 条"基线;夹具随代码演进,只保证**同口径可比**)
11
+ *
12
+ * ## 两种口径(都必须打印,不藏)
13
+ * · **全部字面量**(缺省)= 与用户给的"基线 102 条"同口径,用于"前后对比";
14
+ * · **仅可见文案**(`--visible-only`)= 排除 `title=` / `title:` 提示行。用户批准的收敛手段
15
+ * 之一就是"转 title= tooltip"⇒ 验收看这个数(否则 tooltip 也算,收敛永远做不完)。
16
+ *
17
+ * ## 三种区间
18
+ * · 整文件(缺省);
19
+ * · `--scope <起>-<止>` 行区间;
20
+ * · `--section <名>` **语义区间**(见 SECTIONS):把"某个页面"从 bundle 里切出来数,
21
+ * 因为整 bundle 混着看板/工作台/引导弹窗,"设置页文案"单独看才是用户要的口径。
22
+ *
23
+ * 用法:
24
+ * node scripts/ui-copy-count.mjs # 全部客户端 bundle
25
+ * node scripts/ui-copy-count.mjs --format json # 机器可读(门禁用)
26
+ * node scripts/ui-copy-count.mjs --threshold 18 # 换阈值
27
+ * node scripts/ui-copy-count.mjs --lines # 逐条列出(行号 + 内容前 60 字)
28
+ * node scripts/ui-copy-count.mjs --visible-only # 只数可见文案(排除 title 提示)
29
+ * node scripts/ui-copy-count.mjs --scope 1516-2534 plugins/argent-skin/dist/client.js
30
+ * node scripts/ui-copy-count.mjs --section argent-settings # 语义区间(推荐)
31
+ * node scripts/ui-copy-count.mjs --section argent-settings --visible-only --lines
32
+ * node scripts/ui-copy-count.mjs --compare <另一份 checkout 根> # 与基线逐文件对比
33
+ * 退出码:0;`--max N` 时超限退 1(可当门禁)。
34
+ *
35
+ * ⚠️ 口径边界:它数的是**字面量**,不含 JSX 文本节点与模板串;只用来做"前后对比",
36
+ * 不是"用户看到多少字"的精确度量。
37
+ */
38
+ import { readFileSync, existsSync, readdirSync } from 'node:fs'
39
+ import { dirname, join, resolve } from 'node:path'
40
+ import { fileURLToPath } from 'node:url'
41
+
42
+ const HERE = dirname(fileURLToPath(import.meta.url))
43
+ const REPO = resolve(HERE, '..')
44
+
45
+ /**
46
+ * 语义区间:把"某个页面"从 bundle 里切出来数。
47
+ * 用**组件边界作锚点**(不是裸行号)—— 代码上下移动时锚点跟着走,裸行号会静默错位。
48
+ * `sections` 里的 `from`/`to` 是正则源码字符串,`to` 命中的行**不含**在区间内。
49
+ */
50
+ const SECTIONS = {
51
+ // argent-skin:设置页「Argent」组件(SettingsPage 起 → 组件结束)
52
+ 'argent-settings': {
53
+ file: 'plugins/argent-skin/dist/client.js',
54
+ from: '^ function SettingsPage\\(\\) \\{',
55
+ to: '^ // ── 品牌文本补丁',
56
+ },
57
+ // argent-skin:计费概览组件(BillingSummary)。2026-10-01 入口收敛 4 → 2 后它**不再是独立
58
+ // section**「Argent 计费」,而是「Argent」页状态条下方的「计费概览」折叠块;组件本体与两个锚点
59
+ // 都没变(`to` 仍是紧随其后的 registerWhenCore 行)⇒ 这个区间继续有效,数字与收敛前可直接比。
60
+ 'argent-billing': {
61
+ file: 'plugins/argent-skin/dist/client.js',
62
+ from: '^ function BillingSummary\\(\\) \\{',
63
+ to: '^ registerWhenCore\\(function \\(\\) \\{$',
64
+ },
65
+ // argent-gateway:整个文件就是「Argent 能力」页(概览页签 + 能力包页签 + 角色页签)
66
+ 'gateway-page': {
67
+ file: 'plugins/argent-gateway/dist/client.js',
68
+ from: '^',
69
+ to: '\\u0000never',
70
+ },
71
+ }
72
+
73
+ const argv = process.argv.slice(2)
74
+ const flag = (name, def = null) => {
75
+ const i = argv.indexOf(name)
76
+ return i >= 0 ? (argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[i + 1] : true) : def
77
+ }
78
+ const THRESHOLD = Number(flag('--threshold', 18))
79
+ const FORMAT = String(flag('--format', 'text'))
80
+ const SHOW_LINES = !!flag('--lines', false)
81
+ // --visible-only:只数**会显示在页面上**的长文案(排除 title= / title: 提示行)。
82
+ // 为什么需要两种口径:用户批准的收敛手段之一就是"改为 title=(tooltip)"⇒ 若把 tooltip 也算进
83
+ // 长文案,收敛永远做不完。所以:**可见文案**是验收口径,**全部字面量**是与历史基线(102 条)
84
+ // 对比的口径 —— 两个数都打印,不藏。
85
+ const VISIBLE_ONLY = !!flag('--visible-only', false)
86
+ const MAX = flag('--max', null)
87
+ const SCOPE = flag('--scope', null)
88
+ const SECTION = flag('--section', null)
89
+ const COMPARE = flag('--compare', null)
90
+
91
+ /** 默认目标:所有插件的客户端 bundle(手写维护的那几个也在内)。 */
92
+ function defaultTargets() {
93
+ const out = []
94
+ for (const d of readdirSync(join(REPO, 'plugins'))) {
95
+ const p = join(REPO, 'plugins', d, 'dist', 'client.js')
96
+ if (existsSync(p)) out.push(p)
97
+ }
98
+ return out
99
+ }
100
+ const targets = argv.filter((a) => a.endsWith('.js')).map((a) => resolve(a))
101
+
102
+ /**
103
+ * 解析语义区间 → 行号区间(含 `from` 命中行,不含 `to` 命中行)。
104
+ * 锚点找不到就**抛错**,绝不静默退化成"整文件"—— 静默退化会让数字看着变好而其实没测到。
105
+ */
106
+ function resolveSection(root, name) {
107
+ const spec = SECTIONS[name]
108
+ if (!spec) throw new Error(`未知语义区间 ${name}(可选:${Object.keys(SECTIONS).join(' / ')})`)
109
+ const file = join(root, spec.file)
110
+ if (!existsSync(file)) throw new Error(`语义区间 ${name} 的文件不存在:${spec.file}`)
111
+ const lines = readFileSync(file, 'utf-8').split('\n')
112
+ const fromRe = new RegExp(spec.from)
113
+ const toRe = new RegExp(spec.to)
114
+ const lo = lines.findIndex((l) => fromRe.test(l)) + 1
115
+ if (!lo) throw new Error(`语义区间 ${name} 找不到起点锚点:${spec.from}(代码结构变了?改脚本里的 SECTIONS)`)
116
+ let hi = lines.length
117
+ for (let i = lo; i < lines.length; i += 1) {
118
+ if (toRe.test(lines[i])) { hi = i; break }
119
+ }
120
+ return { file, lo, hi: Math.max(lo, hi) }
121
+ }
122
+
123
+ const sections = SECTION ? [resolveSection(REPO, String(SECTION))] : []
124
+ const files = sections.length ? sections.map((s) => s.file)
125
+ : (targets.length ? targets : defaultTargets())
126
+
127
+ const CJK = /[\u4e00-\u9fff]/
128
+ const LITERAL = /'([^'\\\n]*(?:\\.[^'\\\n]*)*)'|"([^"\\\n]*(?:\\.[^"\\\n]*)*)"/g
129
+
130
+ /**
131
+ * 这一行是不是**提示(tooltip)行**?
132
+ *
133
+ * ⚠️ 曾经写坏过一次(2026-09-30):早期正则 `/(^|[\s,{])title\s*[:=]/` 里的 `\s` 会让
134
+ * `"…(明细见下方「配置状态」)"` 这种**字符串值内部**的 `… title:…` 命中 —— 结果把可见文案
135
+ * 误判成 tooltip,让"可见文案"这个验收口径**虚低**。所以这里改成:先剥掉字符串字面量,
136
+ * 再在**代码骨架**上匹配 `title` 这个属性名/变量名(必须真的是赋值位置)。
137
+ */
138
+ const STRIPPED = (line) => line.replace(LITERAL, '""')
139
+ function isTitleLine(lines, i) {
140
+ const bare = STRIPPED(lines[i] || '')
141
+ // ① 代码骨架里出现 title 属性/变量赋值:`title:` / `title=` / `fooTitle =`
142
+ // 前缀限定为「行首 / { , ( 空白」—— 避开 `subtitle:` 之外的巧合与 `xxx_title:`。
143
+ if (/(^|[\s,{(])title\s*[:=]/.test(bare)) return true
144
+ if (/(^|[\s,{(])[A-Za-z_$][\w$]*[Tt]itle\s*=/.test(bare)) return true
145
+ // ② 多行 JSX 属性值:本行是裸字符串,且前两行的**骨架**刚开了 `title:`
146
+ const prev = STRIPPED(lines[i - 1] || '')
147
+ const prev2 = STRIPPED(lines[i - 2] || '')
148
+ if (/(^|[\s,{(])title\s*[:=]\s*$/.test(prev) || /(^|[\s,{(])title\s*[:=]\s*$/.test(prev2)) return true
149
+ return false
150
+ }
151
+
152
+ function countFile(file, range) {
153
+ const lines = readFileSync(file, 'utf-8').split('\n')
154
+ const [lo, hi] = range || (SCOPE ? String(SCOPE).split('-').map(Number) : [1, lines.length])
155
+ const cjk = []
156
+ const long = []
157
+ for (let i = 0; i < lines.length; i += 1) {
158
+ const lineNo = i + 1
159
+ if (lineNo < lo || lineNo > hi) continue
160
+ if (VISIBLE_ONLY && isTitleLine(lines, i)) continue
161
+ LITERAL.lastIndex = 0
162
+ let m
163
+ while ((m = LITERAL.exec(lines[i])) !== null) {
164
+ const v = m[1] !== undefined ? m[1] : m[2]
165
+ if (!v || !CJK.test(v)) continue
166
+ cjk.push(lineNo)
167
+ if (v.length >= THRESHOLD) long.push({ line: lineNo, len: v.length, text: v })
168
+ }
169
+ }
170
+ return { file, cjk: cjk.length, long, range: range || (SCOPE ? [lo, hi] : null) }
171
+ }
172
+
173
+ const rangeLabel = sections.length
174
+ ? `${SECTION}(${sections[0].file.replace(`${REPO}/`, '')}:${sections[0].lo}-${sections[0].hi})`
175
+ : (SCOPE ? String(SCOPE) : '整文件')
176
+
177
+ const results = files.map((f, i) => countFile(f, sections[i] ? [sections[i].lo, sections[i].hi] : null))
178
+
179
+ // ── --compare <另一份 checkout 根>:同口径逐文件对比("收敛了多少"的可复验证据)──
180
+ if (COMPARE) {
181
+ const other = resolve(String(COMPARE))
182
+ const otherFiles = sections.length
183
+ ? sections.map((s) => resolveSection(other, String(SECTION)).file)
184
+ : (targets.length ? targets.map((t) => join(other, t.replace(`${REPO}/`, ''))) : defaultTargets().map((t) => join(other, t.replace(`${REPO}/`, ''))))
185
+ const otherRanges = sections.length ? sections.map((s) => resolveSection(other, String(SECTION))) : []
186
+ const before = otherFiles.map((f, i) => countFile(f, otherRanges[i] ? [otherRanges[i].lo, otherRanges[i].hi] : null))
187
+ const sum = (rs) => rs.reduce((a, r) => a + r.long.length, 0)
188
+ console.log(`\n中文长文案**前后对比**(阈值 ≥ ${THRESHOLD};口径:${VISIBLE_ONLY ? '仅可见文案' : '全部字面量(含 title)'};区间:${rangeLabel})\n`)
189
+ console.log(` 基线 ${other}`)
190
+ for (const r of before) console.log(` ${r.file.replace(`${other}/`, r.file.startsWith(other) ? '' : '')}:长文案 ${r.long.length} 条`)
191
+ console.log(` 合计 ${sum(before)} 条\n`)
192
+ console.log(` 现在 ${REPO}`)
193
+ for (const r of results) console.log(` ${r.file.replace(`${REPO}/`, '')}:长文案 ${r.long.length} 条`)
194
+ console.log(` 合计 ${sum(results)} 条`)
195
+ const delta = sum(results) - sum(before)
196
+ console.log(`\n 变化:${delta >= 0 ? '+' : ''}${delta} 条(${sum(before)} → ${sum(results)})\n`)
197
+ } else if (FORMAT === 'json') {
198
+ console.log(JSON.stringify({
199
+ threshold: THRESHOLD,
200
+ visibleOnly: VISIBLE_ONLY,
201
+ scope: rangeLabel,
202
+ section: SECTION || null,
203
+ files: results.map((r) => ({ file: r.file.replace(`${REPO}/`, ''), cjkLiterals: r.cjk, longCopy: r.long.length, range: r.range })),
204
+ totalLongCopy: results.reduce((a, r) => a + r.long.length, 0),
205
+ }, null, 2))
206
+ } else {
207
+ console.log(`\n中文长文案计数(阈值:字面量长度 ≥ ${THRESHOLD};区间:${rangeLabel};口径:${VISIBLE_ONLY ? '仅可见文案(排除 title 提示)' : '全部字面量(含 title)'})\n`)
208
+ for (const r of results) {
209
+ console.log(` ${r.file.replace(`${REPO}/`, '')}:含中文字面量 ${r.cjk} 条,其中长文案 ${r.long.length} 条`)
210
+ if (SHOW_LINES) for (const l of r.long) console.log(` :${l.line} (${l.len}) ${l.text.slice(0, 60)}`)
211
+ }
212
+ console.log(`\n 合计长文案:${results.reduce((a, r) => a + r.long.length, 0)} 条\n`)
213
+ }
214
+ if (MAX !== null && MAX !== true) {
215
+ const total = results.reduce((a, r) => a + r.long.length, 0)
216
+ if (total > Number(MAX)) {
217
+ console.error(` ❌ 长文案 ${total} 条 > 上限 ${MAX}(判据:node scripts/ui-copy-count.mjs --max ${MAX})`)
218
+ process.exit(1)
219
+ }
220
+ }
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env bash
2
+ # usage-anchor —— 成本锚点定时器(每天跑一次,把真实模型成本逐日累积下来)。
3
+ #
4
+ # 为什么要有它:定价要以**实跑一段时间的成本**为锚,而不是某一次抽样。这个定时器每天
5
+ # ① 拉服务端积分流水(走问述代理的机器,含 costYuan 精确值)
6
+ # ② 增量扫描本机会话日志(走官方直连/自带 key 的机器也能测)
7
+ # ③ 刷新权威费率表 ④ 追加当日快照到 ~/.dsh/argent/usage/daily.jsonl
8
+ #
9
+ # 用法:
10
+ # bash scripts/usage-anchor.sh install # 安装/更新 LaunchAgent(每天 03:30 与 15:30)
11
+ # bash scripts/usage-anchor.sh uninstall # 卸载
12
+ # bash scripts/usage-anchor.sh status # 状态 + 最近快照
13
+ # bash scripts/usage-anchor.sh run # 立刻跑一次(前台,肉眼可见)
14
+ # bash scripts/usage-anchor.sh report # 打印当月报告
15
+ #
16
+ # 凭据:脚本会依次尝试进程环境、~/.dsh/argent/whyshu.env(600)、fulfillment.env。
17
+ # 缺 key 时**响亮失败**(exit 1),绝不静默把成本算成 0。
18
+ set -uo pipefail
19
+ REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
20
+ # 解释器解析(2026-09-22 重启事故:默认值写死 ~/.dsh/argent/venv ⇒ 重启后静默空转)
21
+ pick_py() {
22
+ local c
23
+ # 候选(2026-09-22):ARGENT_PY → DSH_HOME/argent/venv → ~/.dsh/argent/venv → 仓库 .venv → PATH
24
+ for c in "${ARGENT_PY:-}" "${DSH_HOME:-$HOME/.dsh}/argent/venv/bin/python3" \
25
+ "$HOME/.dsh/argent/venv/bin/python3" "${REPO:-}/.venv/bin/python3" \
26
+ "$(command -v python3 2>/dev/null || true)"; do
27
+ [ -n "$c" ] || continue
28
+ [ -x "$c" ] || continue
29
+ "$c" -c 'import openpyxl, yaml' 2>/dev/null || continue # 硬要求:配置/发票生成必需
30
+ "$c" -c 'import PIL, playwright' 2>/dev/null || \
31
+ echo "[$(date '+%F %T')] ⚠️ 解释器 $c 缺 pillow/playwright ⇒ 浏览器流程会失败(修:node install-argent.mjs --venv-only)" >&2
32
+ printf '%s' "$c"; return 0
33
+ done
34
+ return 1
35
+ }
36
+ if ! PY="$(pick_py)"; then
37
+ echo "❌ 找不到可用解释器(需含 openpyxl + yaml):ARGENT_PY / ~/.dsh/argent/venv/bin/python3 / <repo>/.venv / PATH 的 python3" >&2
38
+ echo " 修复: python3 -m venv ~/.dsh/argent/venv && ~/.dsh/argent/venv/bin/pip install openpyxl pyyaml" >&2
39
+ exit 2
40
+ fi
41
+ LABEL="com.whyshu.usage-anchor"
42
+ AGENTS="$HOME/Library/LaunchAgents"
43
+ PLIST="$AGENTS/$LABEL.plist"
44
+ LOGDIR="${ARGENT_LOG_DIR:-$HOME/Library/Logs/argent}"
45
+ [ -f "$HOME/.dsh/argent/logdir" ] && LOGDIR="$(cat "$HOME/.dsh/argent/logdir")"
46
+ UID_N="$(id -u)"
47
+
48
+ gen_plist() {
49
+ cat <<PLIST
50
+ <?xml version="1.0" encoding="UTF-8"?>
51
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
52
+ <!-- 由 scripts/usage-anchor.sh 生成(勿手改) -->
53
+ <plist version="1.0">
54
+ <dict>
55
+ <key>Label</key>
56
+ <string>$LABEL</string>
57
+ <key>ProgramArguments</key>
58
+ <array>
59
+ <string>/bin/bash</string>
60
+ <string>$REPO/scripts/usage-anchor.sh</string>
61
+ <string>run</string>
62
+ </array>
63
+ <key>StartCalendarInterval</key>
64
+ <array>
65
+ <dict><key>Hour</key><integer>3</integer><key>Minute</key><integer>30</integer></dict>
66
+ <dict><key>Hour</key><integer>15</integer><key>Minute</key><integer>30</integer></dict>
67
+ </array>
68
+ <key>RunAtLoad</key>
69
+ <false/>
70
+ <key>AbandonProcessGroup</key>
71
+ <true/>
72
+ <key>EnvironmentVariables</key>
73
+ <dict>
74
+ <key>ARGENT_PY</key><string>$PY</string>
75
+ <key>PATH</key><string>$(dirname "$(command -v node || echo /usr/local/bin/node)"):/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
76
+ </dict>
77
+ <key>StandardOutPath</key><string>$LOGDIR/usage-anchor.out.log</string>
78
+ <key>StandardErrorPath</key><string>$LOGDIR/usage-anchor.err.log</string>
79
+ </dict>
80
+ </plist>
81
+ PLIST
82
+ }
83
+
84
+ cmd_run() {
85
+ echo "── 成本锚点 $(date '+%Y-%m-%d %H:%M:%S')"
86
+ "$PY" "$REPO/scripts/usage_report.py" --pull --snapshot || {
87
+ echo " ❌ 拉取失败(检查 ~/.dsh/argent/whyshu.env 里的 WHYSHU_API_KEY)" >&2; return 1; }
88
+ }
89
+
90
+ cmd_report() { "$PY" "$REPO/scripts/usage_report.py"; }
91
+
92
+ cmd_status() {
93
+ echo "── 定时器"
94
+ launchctl list | grep -i "$LABEL" || echo " (未安装:bash scripts/usage-anchor.sh install)"
95
+ echo
96
+ echo "── 最近 7 天快照(~/.dsh/argent/usage/daily.jsonl)"
97
+ "$PY" - <<'PY'
98
+ import json, pathlib
99
+ f = pathlib.Path.home() / ".dsh" / "argent" / "usage" / "daily.jsonl"
100
+ if not f.exists():
101
+ print(" (还没有快照)"); raise SystemExit
102
+ for line in f.read_text(encoding="utf-8").splitlines()[-7:]:
103
+ d = json.loads(line)
104
+ caps = "|".join(f"{k} ¥{v['yuanHiPerTicket']:.3f}/票×{v['tickets']}" for k, v in d.get("perCapability", {}).items())
105
+ print(f" {d['date']} 总成本 ¥{d['costYuan']:.2f}|请求 {d['requests']}|票据窗口内 ¥{d['attributedYuanHi']:.2f}"
106
+ f"|窗口外 ¥{d['overheadYuanHi']:.2f}")
107
+ if caps: print(f" 每票成本:{caps}")
108
+ PY
109
+ echo
110
+ echo "── 最近一次运行日志尾部"
111
+ tail -3 "$LOGDIR/usage-anchor.out.log" 2>/dev/null | sed 's/^/ /' || echo " (无日志)"
112
+ }
113
+
114
+ cmd_install() {
115
+ mkdir -p "$AGENTS" "$LOGDIR"
116
+ gen_plist > "$PLIST"
117
+ launchctl bootout "gui/$UID_N/$LABEL" 2>/dev/null || true
118
+ launchctl bootstrap "gui/$UID_N" "$PLIST" 2>/dev/null || launchctl load -w "$PLIST"
119
+ echo " ✅ 已安装 ${LABEL}(每天 03:30 / 15:30 采集成本;日志 $LOGDIR/usage-anchor.out.log)"
120
+ launchctl list | grep -i "$LABEL" || true
121
+ }
122
+
123
+ cmd_uninstall() {
124
+ launchctl bootout "gui/$UID_N/$LABEL" 2>/dev/null || true
125
+ rm -f "$PLIST"
126
+ echo " 🗑 已卸载 ${LABEL}(历史快照保留在 ~/.dsh/argent/usage/)"
127
+ }
128
+
129
+ case "${1:-}" in
130
+ install) cmd_install ;;
131
+ uninstall) cmd_uninstall ;;
132
+ status) cmd_status ;;
133
+ run) cmd_run ;;
134
+ report) cmd_report ;;
135
+ *) sed -n '2,20p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 2 ;;
136
+ esac