@whyshu/argent-engine 0.0.0-stage → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/MANIFEST.json +341 -0
  2. package/engine/VERSION +1 -0
  3. package/engine/agents/catalog.json +454 -0
  4. package/engine/configs/_aliases.proposed.json +651 -0
  5. package/engine/configs/_migration-plan.proposed.json +899 -0
  6. package/engine/configs/account.json +272 -0
  7. package/engine/configs/advanced.json +205 -0
  8. package/engine/configs/host.json +149 -0
  9. package/engine/configs/hr.json +27 -0
  10. package/engine/configs/notify.json +264 -0
  11. package/engine/core/cli/argent.mjs +277 -0
  12. package/engine/core/contracts/argent.contracts.json +1065 -0
  13. package/engine/core/contracts/verbs.schema.json +2539 -0
  14. package/engine/core/lib/argent-datadir.mjs +223 -0
  15. package/engine/core/lib/audit.mjs +187 -0
  16. package/engine/core/lib/claim.mjs +258 -0
  17. package/engine/core/lib/config-catalog.mjs +1172 -0
  18. package/engine/core/lib/doctor-customer.mjs +577 -0
  19. package/engine/core/lib/flow-list.mjs +138 -0
  20. package/engine/core/lib/flow-registry.mjs +48 -0
  21. package/engine/core/lib/init.mjs +247 -0
  22. package/engine/core/lib/ledger.mjs +124 -0
  23. package/engine/core/lib/license-activate.mjs +813 -0
  24. package/engine/core/lib/license-device.mjs +314 -0
  25. package/engine/core/lib/license-file.mjs +349 -0
  26. package/engine/core/lib/license-guard.mjs +123 -0
  27. package/engine/core/lib/license-ops.mjs +1645 -0
  28. package/engine/core/lib/license-verdict.mjs +133 -0
  29. package/engine/core/lib/license.mjs +577 -0
  30. package/engine/core/lib/notify/dedup.mjs +101 -0
  31. package/engine/core/lib/notify/feishu.mjs +38 -0
  32. package/engine/core/lib/notify/index.mjs +95 -0
  33. package/engine/core/lib/notify/verb.mjs +327 -0
  34. package/engine/core/lib/notify/webhook.mjs +135 -0
  35. package/engine/core/lib/notify/wecom.mjs +36 -0
  36. package/engine/core/lib/python.mjs +145 -0
  37. package/engine/core/lib/sop.mjs +116 -0
  38. package/engine/core/lib/versions.mjs +205 -0
  39. package/engine/core/mcp/client.mjs +67 -0
  40. package/engine/core/mcp/http.mjs +138 -0
  41. package/engine/core/mcp/server.mjs +702 -0
  42. package/engine/scripts/_engine_rules_probe.py +15 -0
  43. package/engine/scripts/analyze-full-day.py +568 -0
  44. package/engine/scripts/archive/feishu-bridge.js +246 -0
  45. package/engine/scripts/argent-agents.mjs +527 -0
  46. package/engine/scripts/argent-capabilities.mjs +776 -0
  47. package/engine/scripts/argent-catalog.mjs +186 -0
  48. package/engine/scripts/argent-datadir.py +62 -0
  49. package/engine/scripts/argent-doctor.mjs +624 -0
  50. package/engine/scripts/argent-env-write.mjs +302 -0
  51. package/engine/scripts/argent-schedule.mjs +620 -0
  52. package/engine/scripts/argent-setup-check.mjs +369 -0
  53. package/engine/scripts/argent-upgrade.mjs +1046 -0
  54. package/engine/scripts/base-bootstrap.sh +576 -0
  55. package/engine/scripts/billing-mode-daily.sh +13 -0
  56. package/engine/scripts/build-engine-package.mjs +126 -0
  57. package/engine/scripts/capability-packs.lock.json +484 -0
  58. package/engine/scripts/capability_pack.py +1242 -0
  59. package/engine/scripts/check-1.0-criteria.mjs +355 -0
  60. package/engine/scripts/check-agent-catalog.mjs +141 -0
  61. package/engine/scripts/check-billing-mode.sh +378 -0
  62. package/engine/scripts/check-child-sessions.mjs +83 -0
  63. package/engine/scripts/check-config-catalog.mjs +677 -0
  64. package/engine/scripts/check-docs-drift.mjs +522 -0
  65. package/engine/scripts/check-dsh-web-expose.sh +98 -0
  66. package/engine/scripts/check-judgment-drift.mjs +403 -0
  67. package/engine/scripts/check-license.mjs +199 -0
  68. package/engine/scripts/check-model-auth.py +443 -0
  69. package/engine/scripts/check-pack-closure.mjs +661 -0
  70. package/engine/scripts/check-portable-symbols.mjs +116 -0
  71. package/engine/scripts/check-preset-landing.mjs +124 -0
  72. package/engine/scripts/check-production-home.mjs +602 -0
  73. package/engine/scripts/check-role-mapping.mjs +461 -0
  74. package/engine/scripts/check-sample-redaction.mjs +320 -0
  75. package/engine/scripts/check-skill-classification.mjs +188 -0
  76. package/engine/scripts/check-sop-consistency.mjs +431 -0
  77. package/engine/scripts/check-test-env-safety.mjs +627 -0
  78. package/engine/scripts/check-version-consistency.mjs +452 -0
  79. package/engine/scripts/ci-run.sh +558 -0
  80. package/engine/scripts/collect-evidence.mjs +42 -0
  81. package/engine/scripts/config-explain.mjs +278 -0
  82. package/engine/scripts/configure-provider.rb +29 -0
  83. package/engine/scripts/customer-config-migrate.mjs +179 -0
  84. package/engine/scripts/customer-preflight.py +892 -0
  85. package/engine/scripts/customer-readiness.sh +135 -0
  86. package/engine/scripts/daily-report-inbox.py +242 -0
  87. package/engine/scripts/daily-report-reconcile.mjs +312 -0
  88. package/engine/scripts/daily-report.mjs +1310 -0
  89. package/engine/scripts/daily-report.sh +47 -0
  90. package/engine/scripts/data/lip-product-dict.json +306 -0
  91. package/engine/scripts/deliver-customer.mjs +932 -0
  92. package/engine/scripts/delivery-faces.txt +31 -0
  93. package/engine/scripts/desktop-trial.sh +146 -0
  94. package/engine/scripts/dev-window.sh +261 -0
  95. package/engine/scripts/drill-clean-machine.sh +315 -0
  96. package/engine/scripts/dsh-api-client.mjs +215 -0
  97. package/engine/scripts/dsh-backup-scope.sh +83 -0
  98. package/engine/scripts/dsh-offline-pack.mjs +77 -0
  99. package/engine/scripts/dsh-overlay-pin.sh +87 -0
  100. package/engine/scripts/dsh-plane.mjs +153 -0
  101. package/engine/scripts/dsh-portforward.py +223 -0
  102. package/engine/scripts/dsh-preflight.mjs +179 -0
  103. package/engine/scripts/dsh-release-watch-daily.sh +19 -0
  104. package/engine/scripts/dsh-release-watch.mjs +458 -0
  105. package/engine/scripts/dsh-remote-check.sh +99 -0
  106. package/engine/scripts/dsh-restart.sh +179 -0
  107. package/engine/scripts/dsh-restore.sh +111 -0
  108. package/engine/scripts/dsh-scope-rewire.mjs +803 -0
  109. package/engine/scripts/dsh-snapshot.mjs +116 -0
  110. package/engine/scripts/dsh-upgrade-readiness.mjs +165 -0
  111. package/engine/scripts/dsh-upgrade.mjs +612 -0
  112. package/engine/scripts/dsh-url.sh +86 -0
  113. package/engine/scripts/dsh-web-launch.mjs +155 -0
  114. package/engine/scripts/ensure-store-windows.py +509 -0
  115. package/engine/scripts/entitlements.py +458 -0
  116. package/engine/scripts/env_probe.py +411 -0
  117. package/engine/scripts/feishu-card.mjs +252 -0
  118. package/engine/scripts/final-check.mjs +43 -0
  119. package/engine/scripts/fix-skill-frontmatter.py +209 -0
  120. package/engine/scripts/flow-auto-retry.py +454 -0
  121. package/engine/scripts/flow-requirements.json +735 -0
  122. package/engine/scripts/flow-scaffold.py +275 -0
  123. package/engine/scripts/flow-timers-plist.sh +87 -0
  124. package/engine/scripts/flow-timers.sh +184 -0
  125. package/engine/scripts/flow-trigger-wake.mjs +284 -0
  126. package/engine/scripts/flow-trigger.sh +571 -0
  127. package/engine/scripts/flow_billing.py +354 -0
  128. package/engine/scripts/flow_capability_stats.py +314 -0
  129. package/engine/scripts/flow_case.py +194 -0
  130. package/engine/scripts/flow_claim.py +497 -0
  131. package/engine/scripts/flow_config.py +1025 -0
  132. package/engine/scripts/flow_failure.py +588 -0
  133. package/engine/scripts/flow_learn.py +105 -0
  134. package/engine/scripts/flow_ledger.py +235 -0
  135. package/engine/scripts/flow_policy.py +238 -0
  136. package/engine/scripts/flow_preflight.py +339 -0
  137. package/engine/scripts/flow_registry.py +341 -0
  138. package/engine/scripts/flow_retry.py +304 -0
  139. package/engine/scripts/flow_return_to_ops.py +191 -0
  140. package/engine/scripts/flow_source.py +315 -0
  141. package/engine/scripts/flow_sources/__init__.py +53 -0
  142. package/engine/scripts/flow_sources/cases_json.py +106 -0
  143. package/engine/scripts/flow_sources/common.py +428 -0
  144. package/engine/scripts/flow_sources/feishu_base.py +273 -0
  145. package/engine/scripts/flow_stale.py +185 -0
  146. package/engine/scripts/flow_status_reconcile.py +344 -0
  147. package/engine/scripts/flow_triage.py +849 -0
  148. package/engine/scripts/flow_write_verify.py +496 -0
  149. package/engine/scripts/fulfillment.js +586 -0
  150. package/engine/scripts/gen-agent-bundles.mjs +276 -0
  151. package/engine/scripts/gen-preset-rows.mjs +259 -0
  152. package/engine/scripts/init-ml.sh +2278 -0
  153. package/engine/scripts/install-internal-role.sh +190 -0
  154. package/engine/scripts/install-license.mjs +641 -0
  155. package/engine/scripts/install-one-shot.mjs +665 -0
  156. package/engine/scripts/integration_contract.py +220 -0
  157. package/engine/scripts/integrations/wenyao/adapter.py +98 -0
  158. package/engine/scripts/integrations/xzh/adapter.py +75 -0
  159. package/engine/scripts/invoice-mapping.example.json +40 -0
  160. package/engine/scripts/invoice-table.sh +195 -0
  161. package/engine/scripts/invoice_carrier_notice.py +279 -0
  162. package/engine/scripts/invoice_fail_closeout.py +297 -0
  163. package/engine/scripts/invoice_lip_runner.py +1508 -0
  164. package/engine/scripts/invoice_lip_runner.py.bak-20260922-1710-freready +1090 -0
  165. package/engine/scripts/invoice_ready_audit.py +191 -0
  166. package/engine/scripts/invoice_runner.py +675 -0
  167. package/engine/scripts/invoice_stageb.py +516 -0
  168. package/engine/scripts/launchd/com.whyshu.argent-bridge.plist.example +42 -0
  169. package/engine/scripts/launchd/com.whyshu.fulfillment-poll.plist.example +29 -0
  170. package/engine/scripts/lease.py +377 -0
  171. package/engine/scripts/lib/agent-install.mjs +649 -0
  172. package/engine/scripts/lib/argent-datadir.mjs +16 -0
  173. package/engine/scripts/lib/argent-datadir.sh +18 -0
  174. package/engine/scripts/lib/argent_datadir.py +285 -0
  175. package/engine/scripts/lib/argent_filelock.py +507 -0
  176. package/engine/scripts/lib/bootcheck-wiring.mjs +510 -0
  177. package/engine/scripts/lib/cdp_ports.py +197 -0
  178. package/engine/scripts/lib/customer-env.mjs +102 -0
  179. package/engine/scripts/lib/daily-report-core.mjs +517 -0
  180. package/engine/scripts/lib/dep-declaration.mjs +81 -0
  181. package/engine/scripts/lib/deps-migrate.mjs +310 -0
  182. package/engine/scripts/lib/dsh-host-resolve.mjs +109 -0
  183. package/engine/scripts/lib/dsh-scope.mjs +500 -0
  184. package/engine/scripts/lib/dsh-snapshot-path.mjs +35 -0
  185. package/engine/scripts/lib/dsh-versions.mjs +96 -0
  186. package/engine/scripts/lib/dsh-web-probe.mjs +214 -0
  187. package/engine/scripts/lib/is-direct-run.mjs +61 -0
  188. package/engine/scripts/lib/patch-compose.mjs +241 -0
  189. package/engine/scripts/lib/patch-target.mjs +35 -0
  190. package/engine/scripts/lib/plugin-lifecycle.mjs +128 -0
  191. package/engine/scripts/lib/plugin-packages.mjs +332 -0
  192. package/engine/scripts/lib/preset-compat.mjs +127 -0
  193. package/engine/scripts/lib/preset-install.mjs +575 -0
  194. package/engine/scripts/lib/preset-mechanism.mjs +56 -0
  195. package/engine/scripts/lib/preset-sync.mjs +59 -0
  196. package/engine/scripts/lib/profile-retire.mjs +97 -0
  197. package/engine/scripts/lib/python-runtime.mjs +190 -0
  198. package/engine/scripts/lib/registry.mjs +72 -0
  199. package/engine/scripts/lib/repo-root.sh +56 -0
  200. package/engine/scripts/lib/schedule-mechanism.mjs +659 -0
  201. package/engine/scripts/lib/schedule-probe.mjs +835 -0
  202. package/engine/scripts/lib/scope-pointer.mjs +164 -0
  203. package/engine/scripts/lib/settings-panels.mjs +816 -0
  204. package/engine/scripts/lib/stdio_utf8.py +86 -0
  205. package/engine/scripts/lib/wake-auth.mjs +392 -0
  206. package/engine/scripts/lib/webdriver_ipc.py +264 -0
  207. package/engine/scripts/lib/zstd-frames.mjs +104 -0
  208. package/engine/scripts/license-e2e-drill.mjs +426 -0
  209. package/engine/scripts/license-gate.mjs +245 -0
  210. package/engine/scripts/lip_backlog.py +171 -0
  211. package/engine/scripts/lip_table.py +185 -0
  212. package/engine/scripts/local-usage.mjs +214 -0
  213. package/engine/scripts/migrate-providers.mjs +127 -0
  214. package/engine/scripts/migrate-skills.sh +65 -0
  215. package/engine/scripts/ml-oauth-setup.mjs +550 -0
  216. package/engine/scripts/ml-store-ids.json +26 -0
  217. package/engine/scripts/ml_artifact_attribution.py +333 -0
  218. package/engine/scripts/ml_capacity_probe.py +1158 -0
  219. package/engine/scripts/ml_hub_status.py +1556 -0
  220. package/engine/scripts/ml_schema_check.py +115 -0
  221. package/engine/scripts/ml_store_ids.py +139 -0
  222. package/engine/scripts/npm-publish-argent.mjs +499 -0
  223. package/engine/scripts/npm-publish-argent.sh +20 -0
  224. package/engine/scripts/orchestrator.py +13811 -0
  225. package/engine/scripts/pack-plugins.mjs +140 -0
  226. package/engine/scripts/patches/dsh-llm-deepseek--transport-diagnostics.patch +297 -0
  227. package/engine/scripts/patches/dsh-session--append-message-shape.patch +15 -0
  228. package/engine/scripts/patches/dsh-session--tool-result-block-017.patch +28 -0
  229. package/engine/scripts/patches/dsh-session-format-v0-to-v1--permission-preset-origin.patch +11 -0
  230. package/engine/scripts/patches/dsh-session-format-v3-to-v4--turn-outside-replace.patch +20 -0
  231. package/engine/scripts/patches/dsh-session-persistence-jsonl--worker-bundled-migrators.patch +39 -0
  232. package/engine/scripts/patches/dsh-tools--render-return-shape.patch +50 -0
  233. package/engine/scripts/patches/verify-bundled-migrators.mjs +79 -0
  234. package/engine/scripts/patches/verify-hardening.mjs +167 -0
  235. package/engine/scripts/patches/verify-session-migration.mjs +149 -0
  236. package/engine/scripts/patches/verify-transport-diagnostics.mjs +227 -0
  237. package/engine/scripts/plan-role-rename.mjs +125 -0
  238. package/engine/scripts/plugin-bootcheck.mjs +451 -0
  239. package/engine/scripts/plugin-composition.json +15 -0
  240. package/engine/scripts/poc-mcp-host.mjs +455 -0
  241. package/engine/scripts/poll-fulfillment.sh +295 -0
  242. package/engine/scripts/poll-invoice.sh +76 -0
  243. package/engine/scripts/profile-doctor.mjs +686 -0
  244. package/engine/scripts/provider-select.sh +71 -0
  245. package/engine/scripts/provider-switch.sh +146 -0
  246. package/engine/scripts/provider-sync-target.check.mjs +64 -0
  247. package/engine/scripts/python-runtime.sh +78 -0
  248. package/engine/scripts/query_runner.py +310 -0
  249. package/engine/scripts/release-argent.sh +574 -0
  250. package/engine/scripts/repair-unresolved-tools.mjs +178 -0
  251. package/engine/scripts/retire-inventory.mjs +40 -0
  252. package/engine/scripts/run-tests.sh +257 -0
  253. package/engine/scripts/s1-audit.mjs +85 -0
  254. package/engine/scripts/self-sign-license.mjs +179 -0
  255. package/engine/scripts/session-health-scan.mjs +63 -0
  256. package/engine/scripts/session-repair-tool-result-content.mjs +181 -0
  257. package/engine/scripts/setup.sh +22 -0
  258. package/engine/scripts/skill-install.mjs +629 -0
  259. package/engine/scripts/sku_translate.py +148 -0
  260. package/engine/scripts/sync-plugins.sh +90 -0
  261. package/engine/scripts/sync-presets.sh +27 -0
  262. package/engine/scripts/sync-scripts.sh +445 -0
  263. package/engine/scripts/telemetry.py +873 -0
  264. package/engine/scripts/ui-copy-count.mjs +220 -0
  265. package/engine/scripts/usage-anchor.sh +137 -0
  266. package/engine/scripts/usage_report.py +531 -0
  267. package/engine/scripts/validate-preset-schema.mjs +208 -0
  268. package/engine/scripts/vertical-init.mjs +323 -0
  269. package/engine/scripts/vertical-lint.mjs +482 -0
  270. package/engine/scripts/wenyao-browser.sh +74 -0
  271. package/engine/scripts/wenyao-fixture-export.py +116 -0
  272. package/engine/scripts/wenyao-invoice-writeback.mjs +540 -0
  273. package/engine/scripts/wenyao-login.sh +35 -0
  274. package/engine/scripts/wenyao-template-map.json +292 -0
  275. package/engine/scripts/wenyao_attach.py +131 -0
  276. package/engine/scripts/wenyao_download_template.py +144 -0
  277. package/engine/scripts/wenyao_fill_template.py +568 -0
  278. package/engine/scripts/wenyao_login.py +145 -0
  279. package/engine/scripts/wenyao_portal.py +633 -0
  280. package/engine/scripts/wenyao_recon.py +230 -0
  281. package/engine/scripts/wenyao_run.py +503 -0
  282. package/engine/scripts/wenyao_shadow_diff.py +160 -0
  283. package/engine/scripts/wenyao_skill_contract.py +156 -0
  284. package/engine/scripts/wenyao_template_fetch.py +175 -0
  285. package/engine/scripts/win/dsh-web-autostart.ps1 +372 -0
  286. package/engine/scripts/ziniao-auth-probe.sh +268 -0
  287. package/engine/scripts/ziniao-keepalive.sh +217 -0
  288. package/engine/scripts/ziniao-post-upgrade-check.sh +121 -0
  289. package/engine/scripts/ziniao-windows.sh +154 -0
  290. package/engine/skills/README.md +86 -0
  291. package/engine/skills/argent-dev/SKILL.md +41 -0
  292. package/engine/skills/internal.json +37 -0
  293. package/engine/skills/lktop-pm/SKILL.md +35 -0
  294. package/engine/skills/lktopapp-dev/SKILL.md +34 -0
  295. package/engine/skills/ml-config-guide/SKILL.md +50 -0
  296. package/engine/skills/ml-fulfillment-sop/SKILL.md +178 -0
  297. package/engine/skills/ml-fulfillment-sop/references/andes-components.md +69 -0
  298. package/engine/skills/ml-fulfillment-sop/references/cdp-port-mapping.md +35 -0
  299. package/engine/skills/ml-fulfillment-sop/references/feishu-base-fulfillment-schema.md +136 -0
  300. package/engine/skills/ml-fulfillment-sop/references/multi-window-concurrency.md +69 -0
  301. package/engine/skills/ml-fulfillment-sop/references/pitfalls.md +77 -0
  302. package/engine/skills/ml-fulfillment-sop/references/selectors.md +243 -0
  303. package/engine/skills/ml-fulfillment-sop/scripts/fulfillment.js +586 -0
  304. package/engine/skills/ml-fulfillment-sop/scripts/poll-and-execute.sh +271 -0
  305. package/engine/skills/ml-fulfillment-sop/templates/report.md +55 -0
  306. package/engine/skills/ml-invoice/SKILL.md +506 -0
  307. package/engine/skills/ml-invoice/references/pitfalls.md +268 -0
  308. package/engine/skills/ml-invoice/references/portal-flow.md +360 -0
  309. package/engine/skills/ml-invoice/references/template-fill-contract.md +133 -0
  310. package/engine/skills/ml-invoice-xzh/SKILL.md +133 -0
  311. package/engine/skills/ml-invoice-xzh/references/pitfalls.md +230 -0
  312. package/engine/skills/ml-store-daily-report/SKILL.md +224 -0
  313. package/engine/skills/ml-store-daily-report/scripts/README.md +25 -0
  314. package/engine/skills/ml-store-daily-report/scripts/extract.js +691 -0
  315. package/engine/skills/ml-store-daily-report/scripts/generate.js +299 -0
  316. package/engine/skills/pet-dev/SKILL.md +42 -0
  317. package/engine/skills/whyshu-pm/SKILL.md +35 -0
  318. package/engine/skills/whyshu-web-dev/SKILL.md +50 -0
  319. package/engine/version.mjs +1 -0
  320. package/engine/verticals/README.md +103 -0
  321. package/engine/verticals/_engine-words.baseline.txt +237 -0
  322. package/engine/verticals/_engine-words.json +19 -0
  323. package/engine/verticals/mercadolibre/README.md +43 -0
  324. package/engine/verticals/mercadolibre/config.json +709 -0
  325. package/engine/verticals/mercadolibre/flows.json +738 -0
  326. package/engine/verticals/mercadolibre/manifest.json +538 -0
  327. package/engine/verticals/mercadolibre/migrate-machine-state.mjs +138 -0
  328. package/engine/verticals/mercadolibre/preflight.py +143 -0
  329. package/engine/verticals/mercadolibre/prompts/daily-report.failure.md +69 -0
  330. package/engine/verticals/mercadolibre/prompts/daily-report.md +47 -0
  331. package/engine/verticals/mercadolibre/prompts/daily-report.scheduled.md +79 -0
  332. package/engine/verticals/mercadolibre/prompts/full.failure.md +125 -0
  333. package/engine/verticals/mercadolibre/prompts/full.md +91 -0
  334. package/engine/verticals/mercadolibre/prompts/wenyao-invoice.failure.md +88 -0
  335. package/engine/verticals/mercadolibre/prompts/wenyao-invoice.md +64 -0
  336. package/engine/verticals/mercadolibre/prompts/xzh-invoice.failure.md +91 -0
  337. package/engine/verticals/mercadolibre/prompts/xzh-invoice.md +46 -0
  338. package/package.json +22 -4
  339. package/requirements.txt +5 -0
  340. package/README.md +0 -3
@@ -0,0 +1,516 @@
1
+ #!/usr/bin/env python3
2
+ """发票流程阶段 B — 鑫中航登录 + 进入单票导入页(验证用,不上传/不下载标签)
3
+ 用本机 Chrome(Playwright 独立 profile),登录态持久化复用。
4
+
5
+ 用法:
6
+ invoice_stageb.py --login 登录鑫中航 + 进入单票导入页(验证页面可达/元素定位)
7
+ invoice_stageb.py --hs "品名" 海关编码查询(hsbianma.com)
8
+ """
9
+ import json
10
+ import os
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ HOME = Path.home()
15
+ # 数据目录:**唯一判据**在 `scripts/lib/argent_datadir.py`(三级链,与 JS 内核同语义)
16
+ sys.path.insert(0, str(Path(__file__).resolve().parent / 'lib'))
17
+ from argent_datadir import resolve_data_dir # noqa: E402 —— 勿在此复制判据
18
+ DATA = Path(resolve_data_dir()['dir'])
19
+ CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
20
+ PROFILE = str(DATA / "chrome-profile-xinhang")
21
+ TIMEOUT = 15000
22
+
23
+
24
+ def load_ctx():
25
+ m = json.load(open(DATA / "invoice-mapping.json"))
26
+ c = json.load(open(DATA / "config.json"))
27
+ return m, c
28
+
29
+
30
+ def browser():
31
+ from playwright.sync_api import sync_playwright
32
+ p = sync_playwright().start()
33
+ ctx = p.chromium.launch_persistent_context(
34
+ user_data_dir=PROFILE,
35
+ executable_path=CHROME,
36
+ headless=False,
37
+ viewport={"width": 1440, "height": 900},
38
+ )
39
+ return p, ctx
40
+
41
+
42
+ def login_xinhang(page, m, c):
43
+ url = m["xinzhonghang"]["url"]
44
+ user = m["xinzhonghang"]["user"]
45
+ pwd = c.get("xhInvoicePass", "")
46
+ page.goto(url, timeout=TIMEOUT, wait_until="domcontentloaded")
47
+ page.wait_for_timeout(2000)
48
+ # 会话复用探测(2026-09-08):登录 URL 固定含 /Login/(goto 即登录页),
49
+ # 直接导航 home 验证 profile 会话:能进 home = 已登录复用;被弹回 Login = 需填表。
50
+ # 避免每票/每轮重复登录(频繁登录有账号风控风险)。
51
+ base = url.replace("/Login/VueLoginView", "")
52
+ try:
53
+ page.goto(base + "/home/EleIndex?lang=cn", timeout=TIMEOUT,
54
+ wait_until="domcontentloaded")
55
+ page.wait_for_timeout(2500)
56
+ except Exception:
57
+ pass
58
+ cur = page.url or ""
59
+ if "/Login/" not in cur and ("EleIndex" in cur or "Home" in cur
60
+ or "WeclomeIndex" in cur):
61
+ print("✓ 已登录(复用会话)→", cur)
62
+ return True
63
+ # 会话失效被弹回登录页 → 走登录页填表
64
+ if "/Login/" not in cur:
65
+ try:
66
+ page.goto(url, timeout=TIMEOUT, wait_until="domcontentloaded")
67
+ page.wait_for_timeout(1500)
68
+ except Exception:
69
+ pass
70
+ # 定位账号/密码输入(Element UI 登录页:placeholder 最稳;回退可编辑输入框)
71
+ user_input = page.locator(
72
+ "input[placeholder*=账号], input[placeholder*=用户], input[placeholder*=账户], "
73
+ "input[placeholder*=用户名], input[placeholder*=账号]"
74
+ ).first
75
+ if user_input.count() == 0:
76
+ user_input = page.locator("input:not([type=password]):not([readonly])").first
77
+ pwd_input = page.locator("input[placeholder*=密码], input[type=password]").first
78
+ user_input.fill(user)
79
+ pwd_input.fill(pwd)
80
+ # 提交(登录按钮)
81
+ submit = page.locator("button[type=submit], button:has-text('登 录'), button:has-text('登录'), button:has-text('Sign in')").first
82
+ if submit.count():
83
+ submit.click()
84
+ else:
85
+ pwd_input.press("Enter")
86
+ # 等待跳转(登录成功 URL 离开 /Login/)
87
+ try:
88
+ page.wait_for_url(lambda u: "/Login/" not in u, timeout=15000)
89
+ print("✓ 登录成功 →", page.url)
90
+ return True
91
+ except Exception:
92
+ print("⚠ 未检测到跳转,当前 URL:", page.url)
93
+ return False
94
+
95
+
96
+ def step_login():
97
+ m, c = load_ctx()
98
+ p, ctx = browser()
99
+ try:
100
+ page = ctx.pages[0] if ctx.pages else ctx.new_page()
101
+ ok = login_xinhang(page, m, c)
102
+ if ok:
103
+ # 导航到「头程货运管理 → 大货订单管理」入口(探索菜单链接)
104
+ print("— 页面链接(含关键词):")
105
+ for a in page.locator("a").all()[:60]:
106
+ t = (a.inner_text() or "").strip()
107
+ href = a.get_attribute("href") or ""
108
+ if any(k in (t + href) for k in ("头程", "大货", "订单", "单票", "货运")):
109
+ print(f" [{t}] {href}")
110
+ # 尝试直接导航单票导入(常见路径探测)
111
+ for cand in ("/TruckOrder/Import", "/Import", "/BigOrder", "/CargoOrder"):
112
+ try:
113
+ page.goto(m["xinzhonghang"]["url"].replace("/Login/VueLoginView", "") + cand, timeout=8000)
114
+ page.wait_for_timeout(1000)
115
+ print(f" 尝试 {cand} → {page.url}")
116
+ if "login" not in page.url.lower():
117
+ break
118
+ except Exception:
119
+ pass
120
+ page.screenshot(path=str(DATA / "invoice-stageb-login.png"))
121
+ print("截图: ~/.dsh/argent/invoice-stageb-login.png")
122
+ finally:
123
+ p.stop()
124
+
125
+
126
+ def step_hs(name):
127
+ """hsbianma.com 海关编码查询(只读查询,取首条结果)。"""
128
+ p, ctx = browser()
129
+ try:
130
+ page = ctx.pages[0] if ctx.pages else ctx.new_page()
131
+ page.goto("https://www.hsbianma.com/", timeout=TIMEOUT, wait_until="domcontentloaded")
132
+ page.wait_for_timeout(1500)
133
+ box = page.locator("input[type=text], input[placeholder*=搜索], input[placeholder*=编码]").first
134
+ box.fill(name)
135
+ box.press("Enter")
136
+ page.wait_for_timeout(3000)
137
+ # 提取结果表(首条)
138
+ rows = page.locator("table tr").all()[:6]
139
+ for r in rows:
140
+ cells = [c.strip() for c in r.locator("td").all_inner_texts()]
141
+ if cells:
142
+ print(" | ".join(cells[:6]))
143
+ page.screenshot(path=str(DATA / "invoice-stageb-hs.png"))
144
+ finally:
145
+ p.stop()
146
+
147
+
148
+ # ── 阶段 B:鑫中航 upload + download 标签(KB-045,整合 PM 实测 v13/v27 已验证逻辑)──
149
+ # 关键事实(2026-08-29 PM 实测,勿重复摸索):
150
+ # - 搜索框 = textarea#number(class kj-textarea,placeholder「支持多个单号」)——不是 input
151
+ # - 搜索按钮 button#kjSearchBtn;搜索后校验表格数据行(排除表头/「合计」统计行)单行命中再操作
152
+ # - 勾选:目标行内 input[type=checkbox](class kj-ck-row),严禁点 kjCkAll 全选框
153
+ # - 下载弹窗 iframe /Order/SelectLableTypes,select_option(label=「大货标签」) → 点 OK(.layui-layer-btn0)
154
+ # - 下载是 popup 新窗口 /orderhebing/*_PHD.pdf(非 download 事件)→ ctx.request.get(pdf_url) 保存
155
+ # - context 必须 accept_downloads=True;登录态 profile:~/.dsh/argent/chrome-profile-xinhang
156
+ # - SOP(用户要求):上传鑫中航之前必须先回写飞书 FULL任务表「发票文件」字段
157
+ LABELS_DIR = DATA / "invoice-labels"
158
+ LABELS_DIR.mkdir(parents=True, exist_ok=True)
159
+
160
+
161
+ def browser_ac():
162
+ """带 accept_downloads 的持久化 context(登录态复用;下载标签必需)。"""
163
+ from playwright.sync_api import sync_playwright
164
+ p = sync_playwright().start()
165
+ ctx = p.chromium.launch_persistent_context(
166
+ user_data_dir=PROFILE,
167
+ executable_path=CHROME,
168
+ headless=False,
169
+ viewport={"width": 1440, "height": 900},
170
+ accept_downloads=True,
171
+ )
172
+ return p, ctx
173
+
174
+
175
+ def _click_menu(page, text):
176
+ return page.evaluate('''(text) => {
177
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
178
+ let node;
179
+ while ((node = walker.nextNode())) {
180
+ const t = (node.textContent || "").trim();
181
+ if (t.includes(text) && t.length <= 10) {
182
+ const p = node.parentElement;
183
+ (p.closest("a,li") || p).click();
184
+ return "clicked";
185
+ }
186
+ }
187
+ return "notfound";
188
+ }''', text)
189
+
190
+
191
+ def _hover_menu(page, text):
192
+ page.evaluate('''(text) => {
193
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
194
+ let node;
195
+ while ((node = walker.nextNode())) {
196
+ const t = (node.textContent || "").trim();
197
+ if (t.includes(text) && t.length <= 10) {
198
+ const p = node.parentElement;
199
+ const li = p.closest("li") || p.closest("div");
200
+ if (li) li.dispatchEvent(new MouseEvent("mouseover", {bubbles: true}));
201
+ break;
202
+ }
203
+ }
204
+ }''', text)
205
+
206
+
207
+ def _find_order_frame(page, timeout=150):
208
+ """找大货订单页 frame(含「单票导入」或「订单号码」文案)。"""
209
+ import time
210
+ t0 = time.time()
211
+ while time.time() - t0 < timeout:
212
+ for f in page.frames:
213
+ try:
214
+ body = f.evaluate("document.body ? document.body.innerText : ''")
215
+ except Exception:
216
+ body = ""
217
+ if ("单票导入" in body or "订单号码" in body) and len(body) > 1000:
218
+ return f
219
+ time.sleep(3)
220
+ return None
221
+
222
+
223
+ def _search_order(page, frame, order_no):
224
+ """搜索订单号(textarea#number,非 input)→ 返回 (数据行数, 命中数)。"""
225
+ import json
226
+ filled = frame.evaluate('''(target) => {
227
+ const out = {found: false, filled: false, value: ""};
228
+ const ta = document.querySelector("textarea#number") || document.querySelector("textarea.kj-textarea");
229
+ if (ta) {
230
+ out.found = true;
231
+ ta.focus();
232
+ const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
233
+ setter.call(ta, target);
234
+ ta.dispatchEvent(new Event("input", {bubbles: true}));
235
+ ta.dispatchEvent(new Event("change", {bubbles: true}));
236
+ out.value = ta.value;
237
+ out.filled = true;
238
+ }
239
+ return out;
240
+ }''', order_no)
241
+ print(" 订单号码输入:", json.dumps(filled, ensure_ascii=False), flush=True)
242
+ page.wait_for_timeout(800)
243
+ clicked = frame.evaluate('''() => {
244
+ const btn = document.querySelector("button#kjSearchBtn");
245
+ if (btn) { btn.click(); return "clicked#kjSearchBtn"; }
246
+ return "notfound";
247
+ }''')
248
+ print(" 点击搜索:", clicked, flush=True)
249
+ page.wait_for_timeout(8000)
250
+ res = frame.evaluate('''(target) => {
251
+ const out = {dataRows: [], hit: 0};
252
+ document.querySelectorAll("tr").forEach(tr => {
253
+ const t = (tr.innerText || "").trim().replace(/\\s+/g, " ");
254
+ if (!t) return;
255
+ const isHeader = t.startsWith("#") || t.includes("订单号码 参考单号") || t.includes("参考单号 FBAID");
256
+ const isStat = /^合计/.test(t) || /^共\\s*\\d+\\s*条/.test(t) || t.length < 15;
257
+ if (!isHeader && !isStat) {
258
+ out.dataRows.push(t.slice(0, 300));
259
+ if (t.includes(target)) out.hit++;
260
+ }
261
+ });
262
+ return out;
263
+ }''', order_no)
264
+ print(f" 数据行={len(res['dataRows'])} 命中={res['hit']}", flush=True)
265
+ return len(res["dataRows"]), res["hit"]
266
+
267
+
268
+ def _check_target_row(frame, order_no):
269
+ """勾选目标行 checkbox(class kj-ck-row,严禁 kjCkAll)。返回是否成功。"""
270
+ return frame.evaluate('''(target) => {
271
+ const out = {found: false, checked: false};
272
+ const rows = [...document.querySelectorAll("tr")];
273
+ for (const tr of rows) {
274
+ const t = (tr.innerText || "").trim().replace(/\\s+/g, " ");
275
+ if (t.includes(target) && !t.startsWith("#")) {
276
+ const cb = tr.querySelector("input[type=checkbox]");
277
+ if (cb && !cb.id) {
278
+ if (!cb.checked) cb.click();
279
+ out.checked = cb.checked;
280
+ out.found = true;
281
+ }
282
+ break;
283
+ }
284
+ }
285
+ return out;
286
+ }''', order_no)
287
+
288
+
289
+ def download_label(order_no, save_name=None, wait_keep=15):
290
+ """按订单号搜索 → 单行校验 → 勾选 → 选大货标签模板 → 捕获 popup PDF → 保存。
291
+
292
+ 保存名默认 `运单-<order_no>.pdf`;返回保存路径或 None。
293
+ (v13 已验证逻辑入仓;PDF 经 ctx.request.get(pdf_url) 保存。)
294
+ """
295
+ import time
296
+ import traceback
297
+ m, c = load_ctx()
298
+ p, ctx = browser_ac()
299
+ try:
300
+ page = ctx.pages[0] if ctx.pages else ctx.new_page()
301
+ ok = login_xinhang(page, m, c)
302
+ print("登录:", "✓" if ok else "✗", flush=True)
303
+ if not ok:
304
+ return None
305
+ page.wait_for_timeout(3000)
306
+ menu_ok = False
307
+ for attempt in range(3):
308
+ r1 = _click_menu(page, "大货订单管理")
309
+ if r1 == "clicked":
310
+ menu_ok = True
311
+ break
312
+ _hover_menu(page, "头程运单管理")
313
+ time.sleep(1)
314
+ r2 = _click_menu(page, "大货订单管理")
315
+ if r2 == "clicked":
316
+ menu_ok = True
317
+ break
318
+ time.sleep(2)
319
+ if not menu_ok:
320
+ base = m["xinzhonghang"]["url"].replace("/Login/VueLoginView", "")
321
+ page.evaluate("location.hash = '/wuliu/KuaiJianOrderListView'")
322
+ frame = _find_order_frame(page)
323
+ if not frame:
324
+ print("✗ 订单页未就绪", flush=True)
325
+ return None
326
+ print("订单页就绪", flush=True)
327
+ page.wait_for_timeout(4000)
328
+ n_rows, n_hit = _search_order(page, frame, order_no)
329
+ if not (n_rows == 1 and n_hit == 1):
330
+ print("⚠ 未确认单行命中,不勾选不下载(安全保护)", flush=True)
331
+ return None
332
+ chk = _check_target_row(frame, order_no)
333
+ print(" 勾选目标行:", json.dumps(chk, ensure_ascii=False), flush=True)
334
+ page.wait_for_timeout(1500)
335
+ dl = frame.evaluate('''() => {
336
+ const els = [...document.querySelectorAll("button,a")];
337
+ let el = els.find(e => (e.innerText||"").trim() === "下载订单标签");
338
+ if (!el) el = els.find(e => (e.innerText||"").trim() === "下载标签");
339
+ if (el) { el.click(); return (el.innerText||"").trim(); }
340
+ return "notfound";
341
+ }''')
342
+ print(" 点击下载:", dl, flush=True)
343
+ # 弹窗 iframe 轮询等待(最长 25s——Mac mini 实测 5s 固定等待不足)
344
+ iframe_ = None
345
+ import time as _t
346
+ _t0 = _t.time()
347
+ while _t.time() - _t0 < 25:
348
+ for f in page.frames:
349
+ if "SelectLableTypes" in (f.url or ""):
350
+ iframe_ = f
351
+ break
352
+ if iframe_:
353
+ break
354
+ page.wait_for_timeout(2000)
355
+ if not iframe_:
356
+ print("⚠ 未找到 SelectLableTypes iframe", flush=True)
357
+ return None
358
+ print(" 标签类型 iframe 就绪", flush=True)
359
+ try:
360
+ iframe_.locator("select").first.select_option(label="大货标签")
361
+ print(" select_option 大货标签: OK", flush=True)
362
+ except Exception as e:
363
+ print(" select_option 失败:", str(e)[:100], flush=True)
364
+ page.wait_for_timeout(1500)
365
+ pdf_url = {"url": None}
366
+
367
+ def on_response(resp):
368
+ try:
369
+ u = resp.url
370
+ if "orderhebing" in u and ".pdf" in u.lower():
371
+ pdf_url["url"] = u
372
+ except Exception:
373
+ pass
374
+ ctx.on("response", on_response)
375
+ ok_r = frame.evaluate('''() => {
376
+ const el = document.querySelector(".layui-layer-btn0") ||
377
+ [...document.querySelectorAll("a,button")].find(e => (e.innerText||"").trim() === "OK");
378
+ if (el) { el.click(); return "clicked"; }
379
+ return "notfound";
380
+ }''')
381
+ print(" 点击 OK:", ok_r, flush=True)
382
+ page.wait_for_timeout(15000)
383
+ if not pdf_url["url"]:
384
+ print("⚠ 未捕获 PDF URL", flush=True)
385
+ return None
386
+ url = pdf_url["url"]
387
+ print(" PDF URL:", url, flush=True)
388
+ resp = ctx.request.get(url, timeout=60000)
389
+ print(" HTTP:", resp.status, flush=True)
390
+ if not resp.ok:
391
+ print(" 下载 HTTP 失败:", resp.status, flush=True)
392
+ return None
393
+ body = resp.body()
394
+ save_path = str(LABELS_DIR / (save_name or f"运单-{order_no}.pdf"))
395
+ Path(save_path).write_bytes(body)
396
+ print(" 保存:", save_path, "大小:", len(body), flush=True)
397
+ return save_path
398
+ except Exception:
399
+ traceback.print_exc()
400
+ return None
401
+ finally:
402
+ try:
403
+ p.stop()
404
+ except Exception:
405
+ pass
406
+
407
+
408
+ def upload_invoice(invoice_path, order_no=None, wait_keep=20):
409
+ """上传发票到鑫中航单票导入(v27 已验证逻辑入仓)。
410
+
411
+ 前置(SOP):调用前必须先回写飞书 FULL任务表「发票文件」字段(顺序固化)。
412
+ invoice_path: 本地 xlsx 绝对路径;order_no: 订单号(可选,仅日志)。
413
+ """
414
+ import time
415
+ import traceback
416
+ m, c = load_ctx()
417
+ p, ctx = browser()
418
+ try:
419
+ page = ctx.pages[0] if ctx.pages else ctx.new_page()
420
+ ok = login_xinhang(page, m, c)
421
+ print("登录:", "✓" if ok else "✗", flush=True)
422
+ if not ok:
423
+ return False
424
+ page.wait_for_timeout(3000)
425
+ menu_ok = False
426
+ for attempt in range(3):
427
+ r1 = _click_menu(page, "大货订单管理")
428
+ if r1 == "clicked":
429
+ menu_ok = True
430
+ break
431
+ _hover_menu(page, "头程运单管理")
432
+ time.sleep(1)
433
+ r2 = _click_menu(page, "大货订单管理")
434
+ if r2 == "clicked":
435
+ menu_ok = True
436
+ break
437
+ time.sleep(2)
438
+ if not menu_ok:
439
+ base = m["xinzhonghang"]["url"].replace("/Login/VueLoginView", "")
440
+ page.evaluate("location.hash = '/wuliu/KuaiJianOrderListView'")
441
+ frame = _find_order_frame(page)
442
+ if not frame:
443
+ print("✗ 订单页未就绪", flush=True)
444
+ return False
445
+ print("订单页就绪,开始上传", flush=True)
446
+ r = frame.evaluate('''() => {
447
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
448
+ let node;
449
+ while ((node = walker.nextNode())) {
450
+ const t = (node.textContent || "").trim();
451
+ if (t.includes("单票导入")) {
452
+ const p = node.parentElement;
453
+ (p.closest("a,button,li") || p).click();
454
+ return "clicked";
455
+ }
456
+ }
457
+ return "notfound";
458
+ }''')
459
+ print(" 点击单票导入:", r, flush=True)
460
+ page.wait_for_timeout(6000)
461
+ fi = frame.locator("input[type=file]")
462
+ if fi.count():
463
+ fi.first.set_input_files(invoice_path)
464
+ print(" 已选择文件:", invoice_path, flush=True)
465
+ page.wait_for_timeout(6000)
466
+ up = frame.evaluate('''() => {
467
+ const els = [...document.querySelectorAll("a,button")];
468
+ const el = els.find(e => (e.innerText||"").trim() === "开始上传");
469
+ if (el) { el.click(); return "clicked"; }
470
+ return "notfound";
471
+ }''')
472
+ print(" 点击开始上传:", up, flush=True)
473
+ page.wait_for_timeout(15000)
474
+ st = frame.evaluate('''() => {
475
+ const out = {dialogs: []};
476
+ document.querySelectorAll(".layui-layer").forEach(d => {
477
+ out.dialogs.push((d.innerText||"").trim().replace(/\\s+/g," ").slice(0, 300));
478
+ });
479
+ return out;
480
+ }''')
481
+ print(" === 上传结果 ===", flush=True)
482
+ print(json.dumps(st, ensure_ascii=False, indent=1)[:1500], flush=True)
483
+ return True
484
+ except Exception:
485
+ traceback.print_exc()
486
+ return False
487
+ finally:
488
+ try:
489
+ p.stop()
490
+ except Exception:
491
+ pass
492
+
493
+
494
+ if __name__ == "__main__":
495
+ if "--login" in sys.argv:
496
+ step_login()
497
+ elif "--hs" in sys.argv:
498
+ i = sys.argv.index("--hs")
499
+ step_hs(sys.argv[i + 1] if len(sys.argv) > i + 1 else "背包")
500
+ elif "--download-label" in sys.argv:
501
+ i = sys.argv.index("--download-label")
502
+ order = sys.argv[i + 1] if len(sys.argv) > i + 1 else ""
503
+ if not order:
504
+ print("用法: invoice_stageb.py --download-label <订单号> [保存名]")
505
+ else:
506
+ save = sys.argv[i + 2] if len(sys.argv) > i + 2 else None
507
+ print("下载结果:", download_label(order, save))
508
+ elif "--upload" in sys.argv:
509
+ i = sys.argv.index("--upload")
510
+ path = sys.argv[i + 1] if len(sys.argv) > i + 1 else ""
511
+ if not path:
512
+ print("用法: invoice_stageb.py --upload <xlsx绝对路径>")
513
+ else:
514
+ print("上传结果:", upload_invoice(path))
515
+ else:
516
+ print(__doc__)
@@ -0,0 +1,42 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>com.whyshu.argent-bridge</string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string>/opt/homebrew/bin/node</string>
10
+ <string>__USER_HOME__/.argent-dsh/scripts/feishu-bridge.js</string>
11
+ <string>--role</string>
12
+ <string>general</string>
13
+ </array>
14
+ <key>EnvironmentVariables</key>
15
+ <dict>
16
+ <key>PATH</key>
17
+ <string>/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
18
+ <key>HOME</key>
19
+ <string>__USER_HOME__</string>
20
+ <key>USER</key>
21
+ <string>chester</string>
22
+ <key>TMPDIR</key>
23
+ <string>/tmp</string>
24
+ <key>LANG</key>
25
+ <string>zh_CN.UTF-8</string>
26
+ <key>LC_ALL</key>
27
+ <string>zh_CN.UTF-8</string>
28
+ </dict>
29
+ <key>WorkingDirectory</key>
30
+ <string>/tmp</string>
31
+ <key>RunAtLoad</key>
32
+ <true/>
33
+ <key>KeepAlive</key>
34
+ <true/>
35
+ <key>ProcessType</key>
36
+ <string>Background</string>
37
+ <key>StandardOutPath</key>
38
+ <string>__USER_HOME__/.dsh/argent/logs/bridge.log</string>
39
+ <key>StandardErrorPath</key>
40
+ <string>__USER_HOME__/.dsh/argent/logs/bridge.log</string>
41
+ </dict>
42
+ </plist>
@@ -0,0 +1,29 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <!-- KB-086:FULL 轮询 LaunchAgent(绕开 cron /var/at/tabs 权限问题,与 invoice-poll 同机制)
4
+ 部署到 ~/Library/LaunchAgents/com.whyshu.fulfillment-poll.plist(路径按实际用户调整)后:
5
+ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.whyshu.fulfillment-poll.plist -->
6
+ <plist version="1.0">
7
+ <dict>
8
+ <key>Label</key>
9
+ <string>com.whyshu.fulfillment-poll</string>
10
+ <key>ProgramArguments</key>
11
+ <array>
12
+ <string>/bin/bash</string>
13
+ <string>/Users/huangwei/.argent-dsh/scripts/poll-fulfillment.sh</string>
14
+ </array>
15
+ <key>StartInterval</key>
16
+ <integer>300</integer>
17
+ <!-- 2026-09-03 修复:缺此键时 launchd 在 poll 脚本退出后清理其 nohup 后台子进程
18
+ (orchestrator 批量进程启动即被 kill、batch.log 0 字节、就绪记录永不执行);
19
+ 加 AbandonProcessGroup=true 让 spawn 的 orchestrator 存活(与手动 SSH 跑 poll 行为一致) -->
20
+ <key>AbandonProcessGroup</key>
21
+ <true/>
22
+ <key>RunAtLoad</key>
23
+ <true/>
24
+ <key>StandardOutPath</key>
25
+ <string>/Users/huangwei/.dsh/argent/fulfillment-logs/launchd.out.log</string>
26
+ <key>StandardErrorPath</key>
27
+ <string>/Users/huangwei/.dsh/argent/fulfillment-logs/launchd.err.log</string>
28
+ </dict>
29
+ </plist>