@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.
- package/MANIFEST.json +341 -0
- package/engine/VERSION +1 -0
- package/engine/agents/catalog.json +454 -0
- package/engine/configs/_aliases.proposed.json +651 -0
- package/engine/configs/_migration-plan.proposed.json +899 -0
- package/engine/configs/account.json +272 -0
- package/engine/configs/advanced.json +205 -0
- package/engine/configs/host.json +149 -0
- package/engine/configs/hr.json +27 -0
- package/engine/configs/notify.json +264 -0
- package/engine/core/cli/argent.mjs +277 -0
- package/engine/core/contracts/argent.contracts.json +1065 -0
- package/engine/core/contracts/verbs.schema.json +2539 -0
- package/engine/core/lib/argent-datadir.mjs +223 -0
- package/engine/core/lib/audit.mjs +187 -0
- package/engine/core/lib/claim.mjs +258 -0
- package/engine/core/lib/config-catalog.mjs +1172 -0
- package/engine/core/lib/doctor-customer.mjs +577 -0
- package/engine/core/lib/flow-list.mjs +138 -0
- package/engine/core/lib/flow-registry.mjs +48 -0
- package/engine/core/lib/init.mjs +247 -0
- package/engine/core/lib/ledger.mjs +124 -0
- package/engine/core/lib/license-activate.mjs +813 -0
- package/engine/core/lib/license-device.mjs +314 -0
- package/engine/core/lib/license-file.mjs +349 -0
- package/engine/core/lib/license-guard.mjs +123 -0
- package/engine/core/lib/license-ops.mjs +1645 -0
- package/engine/core/lib/license-verdict.mjs +133 -0
- package/engine/core/lib/license.mjs +577 -0
- package/engine/core/lib/notify/dedup.mjs +101 -0
- package/engine/core/lib/notify/feishu.mjs +38 -0
- package/engine/core/lib/notify/index.mjs +95 -0
- package/engine/core/lib/notify/verb.mjs +327 -0
- package/engine/core/lib/notify/webhook.mjs +135 -0
- package/engine/core/lib/notify/wecom.mjs +36 -0
- package/engine/core/lib/python.mjs +145 -0
- package/engine/core/lib/sop.mjs +116 -0
- package/engine/core/lib/versions.mjs +205 -0
- package/engine/core/mcp/client.mjs +67 -0
- package/engine/core/mcp/http.mjs +138 -0
- package/engine/core/mcp/server.mjs +702 -0
- package/engine/scripts/_engine_rules_probe.py +15 -0
- package/engine/scripts/analyze-full-day.py +568 -0
- package/engine/scripts/archive/feishu-bridge.js +246 -0
- package/engine/scripts/argent-agents.mjs +527 -0
- package/engine/scripts/argent-capabilities.mjs +776 -0
- package/engine/scripts/argent-catalog.mjs +186 -0
- package/engine/scripts/argent-datadir.py +62 -0
- package/engine/scripts/argent-doctor.mjs +624 -0
- package/engine/scripts/argent-env-write.mjs +302 -0
- package/engine/scripts/argent-schedule.mjs +620 -0
- package/engine/scripts/argent-setup-check.mjs +369 -0
- package/engine/scripts/argent-upgrade.mjs +1046 -0
- package/engine/scripts/base-bootstrap.sh +576 -0
- package/engine/scripts/billing-mode-daily.sh +13 -0
- package/engine/scripts/build-engine-package.mjs +126 -0
- package/engine/scripts/capability-packs.lock.json +484 -0
- package/engine/scripts/capability_pack.py +1242 -0
- package/engine/scripts/check-1.0-criteria.mjs +355 -0
- package/engine/scripts/check-agent-catalog.mjs +141 -0
- package/engine/scripts/check-billing-mode.sh +378 -0
- package/engine/scripts/check-child-sessions.mjs +83 -0
- package/engine/scripts/check-config-catalog.mjs +677 -0
- package/engine/scripts/check-docs-drift.mjs +522 -0
- package/engine/scripts/check-dsh-web-expose.sh +98 -0
- package/engine/scripts/check-judgment-drift.mjs +403 -0
- package/engine/scripts/check-license.mjs +199 -0
- package/engine/scripts/check-model-auth.py +443 -0
- package/engine/scripts/check-pack-closure.mjs +661 -0
- package/engine/scripts/check-portable-symbols.mjs +116 -0
- package/engine/scripts/check-preset-landing.mjs +124 -0
- package/engine/scripts/check-production-home.mjs +602 -0
- package/engine/scripts/check-role-mapping.mjs +461 -0
- package/engine/scripts/check-sample-redaction.mjs +320 -0
- package/engine/scripts/check-skill-classification.mjs +188 -0
- package/engine/scripts/check-sop-consistency.mjs +431 -0
- package/engine/scripts/check-test-env-safety.mjs +627 -0
- package/engine/scripts/check-version-consistency.mjs +452 -0
- package/engine/scripts/ci-run.sh +558 -0
- package/engine/scripts/collect-evidence.mjs +42 -0
- package/engine/scripts/config-explain.mjs +278 -0
- package/engine/scripts/configure-provider.rb +29 -0
- package/engine/scripts/customer-config-migrate.mjs +179 -0
- package/engine/scripts/customer-preflight.py +892 -0
- package/engine/scripts/customer-readiness.sh +135 -0
- package/engine/scripts/daily-report-inbox.py +242 -0
- package/engine/scripts/daily-report-reconcile.mjs +312 -0
- package/engine/scripts/daily-report.mjs +1310 -0
- package/engine/scripts/daily-report.sh +47 -0
- package/engine/scripts/data/lip-product-dict.json +306 -0
- package/engine/scripts/deliver-customer.mjs +932 -0
- package/engine/scripts/delivery-faces.txt +31 -0
- package/engine/scripts/desktop-trial.sh +146 -0
- package/engine/scripts/dev-window.sh +261 -0
- package/engine/scripts/drill-clean-machine.sh +315 -0
- package/engine/scripts/dsh-api-client.mjs +215 -0
- package/engine/scripts/dsh-backup-scope.sh +83 -0
- package/engine/scripts/dsh-offline-pack.mjs +77 -0
- package/engine/scripts/dsh-overlay-pin.sh +87 -0
- package/engine/scripts/dsh-plane.mjs +153 -0
- package/engine/scripts/dsh-portforward.py +223 -0
- package/engine/scripts/dsh-preflight.mjs +179 -0
- package/engine/scripts/dsh-release-watch-daily.sh +19 -0
- package/engine/scripts/dsh-release-watch.mjs +458 -0
- package/engine/scripts/dsh-remote-check.sh +99 -0
- package/engine/scripts/dsh-restart.sh +179 -0
- package/engine/scripts/dsh-restore.sh +111 -0
- package/engine/scripts/dsh-scope-rewire.mjs +803 -0
- package/engine/scripts/dsh-snapshot.mjs +116 -0
- package/engine/scripts/dsh-upgrade-readiness.mjs +165 -0
- package/engine/scripts/dsh-upgrade.mjs +612 -0
- package/engine/scripts/dsh-url.sh +86 -0
- package/engine/scripts/dsh-web-launch.mjs +155 -0
- package/engine/scripts/ensure-store-windows.py +509 -0
- package/engine/scripts/entitlements.py +458 -0
- package/engine/scripts/env_probe.py +411 -0
- package/engine/scripts/feishu-card.mjs +252 -0
- package/engine/scripts/final-check.mjs +43 -0
- package/engine/scripts/fix-skill-frontmatter.py +209 -0
- package/engine/scripts/flow-auto-retry.py +454 -0
- package/engine/scripts/flow-requirements.json +735 -0
- package/engine/scripts/flow-scaffold.py +275 -0
- package/engine/scripts/flow-timers-plist.sh +87 -0
- package/engine/scripts/flow-timers.sh +184 -0
- package/engine/scripts/flow-trigger-wake.mjs +284 -0
- package/engine/scripts/flow-trigger.sh +571 -0
- package/engine/scripts/flow_billing.py +354 -0
- package/engine/scripts/flow_capability_stats.py +314 -0
- package/engine/scripts/flow_case.py +194 -0
- package/engine/scripts/flow_claim.py +497 -0
- package/engine/scripts/flow_config.py +1025 -0
- package/engine/scripts/flow_failure.py +588 -0
- package/engine/scripts/flow_learn.py +105 -0
- package/engine/scripts/flow_ledger.py +235 -0
- package/engine/scripts/flow_policy.py +238 -0
- package/engine/scripts/flow_preflight.py +339 -0
- package/engine/scripts/flow_registry.py +341 -0
- package/engine/scripts/flow_retry.py +304 -0
- package/engine/scripts/flow_return_to_ops.py +191 -0
- package/engine/scripts/flow_source.py +315 -0
- package/engine/scripts/flow_sources/__init__.py +53 -0
- package/engine/scripts/flow_sources/cases_json.py +106 -0
- package/engine/scripts/flow_sources/common.py +428 -0
- package/engine/scripts/flow_sources/feishu_base.py +273 -0
- package/engine/scripts/flow_stale.py +185 -0
- package/engine/scripts/flow_status_reconcile.py +344 -0
- package/engine/scripts/flow_triage.py +849 -0
- package/engine/scripts/flow_write_verify.py +496 -0
- package/engine/scripts/fulfillment.js +586 -0
- package/engine/scripts/gen-agent-bundles.mjs +276 -0
- package/engine/scripts/gen-preset-rows.mjs +259 -0
- package/engine/scripts/init-ml.sh +2278 -0
- package/engine/scripts/install-internal-role.sh +190 -0
- package/engine/scripts/install-license.mjs +641 -0
- package/engine/scripts/install-one-shot.mjs +665 -0
- package/engine/scripts/integration_contract.py +220 -0
- package/engine/scripts/integrations/wenyao/adapter.py +98 -0
- package/engine/scripts/integrations/xzh/adapter.py +75 -0
- package/engine/scripts/invoice-mapping.example.json +40 -0
- package/engine/scripts/invoice-table.sh +195 -0
- package/engine/scripts/invoice_carrier_notice.py +279 -0
- package/engine/scripts/invoice_fail_closeout.py +297 -0
- package/engine/scripts/invoice_lip_runner.py +1508 -0
- package/engine/scripts/invoice_lip_runner.py.bak-20260922-1710-freready +1090 -0
- package/engine/scripts/invoice_ready_audit.py +191 -0
- package/engine/scripts/invoice_runner.py +675 -0
- package/engine/scripts/invoice_stageb.py +516 -0
- package/engine/scripts/launchd/com.whyshu.argent-bridge.plist.example +42 -0
- package/engine/scripts/launchd/com.whyshu.fulfillment-poll.plist.example +29 -0
- package/engine/scripts/lease.py +377 -0
- package/engine/scripts/lib/agent-install.mjs +649 -0
- package/engine/scripts/lib/argent-datadir.mjs +16 -0
- package/engine/scripts/lib/argent-datadir.sh +18 -0
- package/engine/scripts/lib/argent_datadir.py +285 -0
- package/engine/scripts/lib/argent_filelock.py +507 -0
- package/engine/scripts/lib/bootcheck-wiring.mjs +510 -0
- package/engine/scripts/lib/cdp_ports.py +197 -0
- package/engine/scripts/lib/customer-env.mjs +102 -0
- package/engine/scripts/lib/daily-report-core.mjs +517 -0
- package/engine/scripts/lib/dep-declaration.mjs +81 -0
- package/engine/scripts/lib/deps-migrate.mjs +310 -0
- package/engine/scripts/lib/dsh-host-resolve.mjs +109 -0
- package/engine/scripts/lib/dsh-scope.mjs +500 -0
- package/engine/scripts/lib/dsh-snapshot-path.mjs +35 -0
- package/engine/scripts/lib/dsh-versions.mjs +96 -0
- package/engine/scripts/lib/dsh-web-probe.mjs +214 -0
- package/engine/scripts/lib/is-direct-run.mjs +61 -0
- package/engine/scripts/lib/patch-compose.mjs +241 -0
- package/engine/scripts/lib/patch-target.mjs +35 -0
- package/engine/scripts/lib/plugin-lifecycle.mjs +128 -0
- package/engine/scripts/lib/plugin-packages.mjs +332 -0
- package/engine/scripts/lib/preset-compat.mjs +127 -0
- package/engine/scripts/lib/preset-install.mjs +575 -0
- package/engine/scripts/lib/preset-mechanism.mjs +56 -0
- package/engine/scripts/lib/preset-sync.mjs +59 -0
- package/engine/scripts/lib/profile-retire.mjs +97 -0
- package/engine/scripts/lib/python-runtime.mjs +190 -0
- package/engine/scripts/lib/registry.mjs +72 -0
- package/engine/scripts/lib/repo-root.sh +56 -0
- package/engine/scripts/lib/schedule-mechanism.mjs +659 -0
- package/engine/scripts/lib/schedule-probe.mjs +835 -0
- package/engine/scripts/lib/scope-pointer.mjs +164 -0
- package/engine/scripts/lib/settings-panels.mjs +816 -0
- package/engine/scripts/lib/stdio_utf8.py +86 -0
- package/engine/scripts/lib/wake-auth.mjs +392 -0
- package/engine/scripts/lib/webdriver_ipc.py +264 -0
- package/engine/scripts/lib/zstd-frames.mjs +104 -0
- package/engine/scripts/license-e2e-drill.mjs +426 -0
- package/engine/scripts/license-gate.mjs +245 -0
- package/engine/scripts/lip_backlog.py +171 -0
- package/engine/scripts/lip_table.py +185 -0
- package/engine/scripts/local-usage.mjs +214 -0
- package/engine/scripts/migrate-providers.mjs +127 -0
- package/engine/scripts/migrate-skills.sh +65 -0
- package/engine/scripts/ml-oauth-setup.mjs +550 -0
- package/engine/scripts/ml-store-ids.json +26 -0
- package/engine/scripts/ml_artifact_attribution.py +333 -0
- package/engine/scripts/ml_capacity_probe.py +1158 -0
- package/engine/scripts/ml_hub_status.py +1556 -0
- package/engine/scripts/ml_schema_check.py +115 -0
- package/engine/scripts/ml_store_ids.py +139 -0
- package/engine/scripts/npm-publish-argent.mjs +499 -0
- package/engine/scripts/npm-publish-argent.sh +20 -0
- package/engine/scripts/orchestrator.py +13811 -0
- package/engine/scripts/pack-plugins.mjs +140 -0
- package/engine/scripts/patches/dsh-llm-deepseek--transport-diagnostics.patch +297 -0
- package/engine/scripts/patches/dsh-session--append-message-shape.patch +15 -0
- package/engine/scripts/patches/dsh-session--tool-result-block-017.patch +28 -0
- package/engine/scripts/patches/dsh-session-format-v0-to-v1--permission-preset-origin.patch +11 -0
- package/engine/scripts/patches/dsh-session-format-v3-to-v4--turn-outside-replace.patch +20 -0
- package/engine/scripts/patches/dsh-session-persistence-jsonl--worker-bundled-migrators.patch +39 -0
- package/engine/scripts/patches/dsh-tools--render-return-shape.patch +50 -0
- package/engine/scripts/patches/verify-bundled-migrators.mjs +79 -0
- package/engine/scripts/patches/verify-hardening.mjs +167 -0
- package/engine/scripts/patches/verify-session-migration.mjs +149 -0
- package/engine/scripts/patches/verify-transport-diagnostics.mjs +227 -0
- package/engine/scripts/plan-role-rename.mjs +125 -0
- package/engine/scripts/plugin-bootcheck.mjs +451 -0
- package/engine/scripts/plugin-composition.json +15 -0
- package/engine/scripts/poc-mcp-host.mjs +455 -0
- package/engine/scripts/poll-fulfillment.sh +295 -0
- package/engine/scripts/poll-invoice.sh +76 -0
- package/engine/scripts/profile-doctor.mjs +686 -0
- package/engine/scripts/provider-select.sh +71 -0
- package/engine/scripts/provider-switch.sh +146 -0
- package/engine/scripts/provider-sync-target.check.mjs +64 -0
- package/engine/scripts/python-runtime.sh +78 -0
- package/engine/scripts/query_runner.py +310 -0
- package/engine/scripts/release-argent.sh +574 -0
- package/engine/scripts/repair-unresolved-tools.mjs +178 -0
- package/engine/scripts/retire-inventory.mjs +40 -0
- package/engine/scripts/run-tests.sh +257 -0
- package/engine/scripts/s1-audit.mjs +85 -0
- package/engine/scripts/self-sign-license.mjs +179 -0
- package/engine/scripts/session-health-scan.mjs +63 -0
- package/engine/scripts/session-repair-tool-result-content.mjs +181 -0
- package/engine/scripts/setup.sh +22 -0
- package/engine/scripts/skill-install.mjs +629 -0
- package/engine/scripts/sku_translate.py +148 -0
- package/engine/scripts/sync-plugins.sh +90 -0
- package/engine/scripts/sync-presets.sh +27 -0
- package/engine/scripts/sync-scripts.sh +445 -0
- package/engine/scripts/telemetry.py +873 -0
- package/engine/scripts/ui-copy-count.mjs +220 -0
- package/engine/scripts/usage-anchor.sh +137 -0
- package/engine/scripts/usage_report.py +531 -0
- package/engine/scripts/validate-preset-schema.mjs +208 -0
- package/engine/scripts/vertical-init.mjs +323 -0
- package/engine/scripts/vertical-lint.mjs +482 -0
- package/engine/scripts/wenyao-browser.sh +74 -0
- package/engine/scripts/wenyao-fixture-export.py +116 -0
- package/engine/scripts/wenyao-invoice-writeback.mjs +540 -0
- package/engine/scripts/wenyao-login.sh +35 -0
- package/engine/scripts/wenyao-template-map.json +292 -0
- package/engine/scripts/wenyao_attach.py +131 -0
- package/engine/scripts/wenyao_download_template.py +144 -0
- package/engine/scripts/wenyao_fill_template.py +568 -0
- package/engine/scripts/wenyao_login.py +145 -0
- package/engine/scripts/wenyao_portal.py +633 -0
- package/engine/scripts/wenyao_recon.py +230 -0
- package/engine/scripts/wenyao_run.py +503 -0
- package/engine/scripts/wenyao_shadow_diff.py +160 -0
- package/engine/scripts/wenyao_skill_contract.py +156 -0
- package/engine/scripts/wenyao_template_fetch.py +175 -0
- package/engine/scripts/win/dsh-web-autostart.ps1 +372 -0
- package/engine/scripts/ziniao-auth-probe.sh +268 -0
- package/engine/scripts/ziniao-keepalive.sh +217 -0
- package/engine/scripts/ziniao-post-upgrade-check.sh +121 -0
- package/engine/scripts/ziniao-windows.sh +154 -0
- package/engine/skills/README.md +86 -0
- package/engine/skills/argent-dev/SKILL.md +41 -0
- package/engine/skills/internal.json +37 -0
- package/engine/skills/lktop-pm/SKILL.md +35 -0
- package/engine/skills/lktopapp-dev/SKILL.md +34 -0
- package/engine/skills/ml-config-guide/SKILL.md +50 -0
- package/engine/skills/ml-fulfillment-sop/SKILL.md +178 -0
- package/engine/skills/ml-fulfillment-sop/references/andes-components.md +69 -0
- package/engine/skills/ml-fulfillment-sop/references/cdp-port-mapping.md +35 -0
- package/engine/skills/ml-fulfillment-sop/references/feishu-base-fulfillment-schema.md +136 -0
- package/engine/skills/ml-fulfillment-sop/references/multi-window-concurrency.md +69 -0
- package/engine/skills/ml-fulfillment-sop/references/pitfalls.md +77 -0
- package/engine/skills/ml-fulfillment-sop/references/selectors.md +243 -0
- package/engine/skills/ml-fulfillment-sop/scripts/fulfillment.js +586 -0
- package/engine/skills/ml-fulfillment-sop/scripts/poll-and-execute.sh +271 -0
- package/engine/skills/ml-fulfillment-sop/templates/report.md +55 -0
- package/engine/skills/ml-invoice/SKILL.md +506 -0
- package/engine/skills/ml-invoice/references/pitfalls.md +268 -0
- package/engine/skills/ml-invoice/references/portal-flow.md +360 -0
- package/engine/skills/ml-invoice/references/template-fill-contract.md +133 -0
- package/engine/skills/ml-invoice-xzh/SKILL.md +133 -0
- package/engine/skills/ml-invoice-xzh/references/pitfalls.md +230 -0
- package/engine/skills/ml-store-daily-report/SKILL.md +224 -0
- package/engine/skills/ml-store-daily-report/scripts/README.md +25 -0
- package/engine/skills/ml-store-daily-report/scripts/extract.js +691 -0
- package/engine/skills/ml-store-daily-report/scripts/generate.js +299 -0
- package/engine/skills/pet-dev/SKILL.md +42 -0
- package/engine/skills/whyshu-pm/SKILL.md +35 -0
- package/engine/skills/whyshu-web-dev/SKILL.md +50 -0
- package/engine/version.mjs +1 -0
- package/engine/verticals/README.md +103 -0
- package/engine/verticals/_engine-words.baseline.txt +237 -0
- package/engine/verticals/_engine-words.json +19 -0
- package/engine/verticals/mercadolibre/README.md +43 -0
- package/engine/verticals/mercadolibre/config.json +709 -0
- package/engine/verticals/mercadolibre/flows.json +738 -0
- package/engine/verticals/mercadolibre/manifest.json +538 -0
- package/engine/verticals/mercadolibre/migrate-machine-state.mjs +138 -0
- package/engine/verticals/mercadolibre/preflight.py +143 -0
- package/engine/verticals/mercadolibre/prompts/daily-report.failure.md +69 -0
- package/engine/verticals/mercadolibre/prompts/daily-report.md +47 -0
- package/engine/verticals/mercadolibre/prompts/daily-report.scheduled.md +79 -0
- package/engine/verticals/mercadolibre/prompts/full.failure.md +125 -0
- package/engine/verticals/mercadolibre/prompts/full.md +91 -0
- package/engine/verticals/mercadolibre/prompts/wenyao-invoice.failure.md +88 -0
- package/engine/verticals/mercadolibre/prompts/wenyao-invoice.md +64 -0
- package/engine/verticals/mercadolibre/prompts/xzh-invoice.failure.md +91 -0
- package/engine/verticals/mercadolibre/prompts/xzh-invoice.md +46 -0
- package/package.json +22 -4
- package/requirements.txt +5 -0
- package/README.md +0 -3
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* self-sign-license —— **离线自签**一份许可(演练 / 测试 / 无服务端时跑通离线激活闭环),2026-10-03
|
|
4
|
+
*
|
|
5
|
+
* ## 它解决什么
|
|
6
|
+
* 离线激活闭环是「`license request` 拿设备码 → 服务端签发 v2 许可 → `install --file`」。
|
|
7
|
+
* 没有服务端在场时(开发机、隔离环境、客户现场演练)这条链路跑不通 ⇒ 本工具用**本地密钥**
|
|
8
|
+
* 按**同一份载荷契约**签一份,让闭环可验(`core/lib/license-file.mjs#signLicenseFile` 是唯一实现,
|
|
9
|
+
* 本脚本不重写载荷构造)。
|
|
10
|
+
*
|
|
11
|
+
* ## ⛔ 三条硬约束(都是刻意的摩擦,不是遗漏)
|
|
12
|
+
* ① **密钥必须显式**:只认 `--secret-file`(或 `ARGENT_LICENSE_SECRET`);**不**读本机已装的
|
|
13
|
+
* `license.secret` —— 那会让"拿生产密钥自签"变成一次无意识的默认动作。
|
|
14
|
+
* ② **产物路径必须显式**:必须给 `--out <路径>`;`--out` 落在**本机现役许可位**
|
|
15
|
+
* (`$ARGENT_DATA_DIR/license.json`)时**拒绝**,除非显式 `--allow-live-target`
|
|
16
|
+
* (那条路应该走 `install-license.mjs`,它带备份/读回验收/设备比对)。
|
|
17
|
+
* ③ **不扩权**:`capabilities/verticals/seats/expiresAt/graceDays` 全部来自命令行**显式**参数,
|
|
18
|
+
* 默认最小(无能力、30 天、席位 1)—— 本工具不会"读一份现有许可然后放宽它"。
|
|
19
|
+
*
|
|
20
|
+
* ## ⚠️ 它不是"防伪"的一环
|
|
21
|
+
* 许可签名是 **HMAC-SHA256(对称密钥)**:验签方必须持有同一把密钥 ⇒ 能读 `license.secret` 的人
|
|
22
|
+
* **本来就能自签**。本工具只是把这件事**摆到明面上**(工具化、留痕、口径唯一),不构成新的能力。
|
|
23
|
+
* 真防伪造需要**非对称签名**(Ed25519,客户端只拿公钥)—— 见 §3.6③ 与实施方案的服务端方案。
|
|
24
|
+
*
|
|
25
|
+
* 用法:
|
|
26
|
+
* node scripts/self-sign-license.mjs --user <账号id> --secret-file <密钥文件> --out <许可文件>
|
|
27
|
+
* [--cap <能力id>]… [--vertical <业务线id>]… [--seats N] [--days N | --expires <ISO>] [--grace N]
|
|
28
|
+
* [--instance <提示串>] [--device-code <64hex> | --device-from-local | --no-device]
|
|
29
|
+
* [--sign-v1 | --sign-v2] [--json] [--dry-run] [--force]
|
|
30
|
+
* 缺省:**v2 + 本机设备码**(D1:新签发默认带 `device`);`--no-device` ⇒ v2 但不声明设备(unbound,
|
|
31
|
+
* 缺位一样进签名);`--sign-v1` ⇒ 旧形态(9 项载荷,**不含** device)。
|
|
32
|
+
* 退出码:0 已签(含 dry-run)| 1 写入失败/自验失败 | 2 用法或环境错误
|
|
33
|
+
*/
|
|
34
|
+
import { chmodSync, existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from 'node:fs'
|
|
35
|
+
import { dirname, join, resolve } from 'node:path'
|
|
36
|
+
import { DEVICE_ALGO, currentDevice } from '../core/lib/license-device.mjs'
|
|
37
|
+
import { defaultLicensePath, resolveSecret, signLicenseFile, verifyLicenseFile } from '../core/lib/license-file.mjs'
|
|
38
|
+
|
|
39
|
+
const argv = process.argv.slice(2)
|
|
40
|
+
const has = (f) => argv.includes(f)
|
|
41
|
+
const allOf = (f) => argv.reduce((acc, a, i) => (a === f && argv[i + 1] !== undefined ? [...acc, argv[i + 1]] : acc), [])
|
|
42
|
+
const argOf = (f) => { const i = argv.indexOf(f); return i >= 0 ? argv[i + 1] ?? null : null }
|
|
43
|
+
const die = (m) => { console.error(`✗ ${m}`); process.exit(2) }
|
|
44
|
+
const fail = (m) => { console.error(`✗ ${m}`); process.exit(1) }
|
|
45
|
+
|
|
46
|
+
if (has('--help') || has('-h')) {
|
|
47
|
+
console.log('用法:node scripts/self-sign-license.mjs --user <id> --secret-file <密钥文件> --out <许可文件>')
|
|
48
|
+
console.log(' [--cap <id>]… [--vertical <id>]… [--seats N] [--days N|--expires <ISO>] [--grace N] [--instance <hint>]')
|
|
49
|
+
console.log(' [--device-code <64hex>|--device-from-local|--no-device] [--sign-v1|--sign-v2] [--json] [--dry-run] [--force]')
|
|
50
|
+
console.log('⚠️ 离线**自签**(演练/测试用);真实签发在服务端(whyshu-svr)。签名是 HMAC 对称密钥 ⇒ 不是防伪边界。')
|
|
51
|
+
process.exit(0)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const dryRun = has('--dry-run')
|
|
55
|
+
const jsonOut = has('--json')
|
|
56
|
+
const asJson = jsonOut
|
|
57
|
+
|
|
58
|
+
const userId = String(argOf('--user') || '').trim()
|
|
59
|
+
if (!userId) die('必须给 --user <账号id>(许可里的 userId;空值会被判 malformed)')
|
|
60
|
+
|
|
61
|
+
const secretSrc = argOf('--secret-file') ? resolve(argOf('--secret-file')) : null
|
|
62
|
+
if (!secretSrc && !String(process.env.ARGENT_LICENSE_SECRET || '').trim()) {
|
|
63
|
+
die('必须显式给密钥来源:--secret-file <密钥文件> 或 ARGENT_LICENSE_SECRET'
|
|
64
|
+
+ '(⛔ 不读本机已装的 license.secret:自签必须是**刻意**的动作)')
|
|
65
|
+
}
|
|
66
|
+
if (secretSrc && !existsSync(secretSrc)) die(`密钥文件不存在:${secretSrc}`)
|
|
67
|
+
const key = resolveSecret({ secret: undefined, secretPath: secretSrc || undefined })
|
|
68
|
+
if (!key.secret) die(`读不到密钥:${secretSrc || 'ARGENT_LICENSE_SECRET'}`)
|
|
69
|
+
|
|
70
|
+
const outArg = argOf('--out')
|
|
71
|
+
if (!outArg) die('必须给 --out <许可文件路径>(本工具**不**默认往数据目录写)')
|
|
72
|
+
const out = resolve(outArg)
|
|
73
|
+
const live = defaultLicensePath()
|
|
74
|
+
if (out === live && !has('--allow-live-target')) {
|
|
75
|
+
die(`--out 指向**本机现役许可位**(${live}):这条路请走 \`install-license.mjs\`(带备份 + 读回验收 + 设备比对);`
|
|
76
|
+
+ '确实要直接覆盖请显式加 --allow-live-target')
|
|
77
|
+
}
|
|
78
|
+
if (existsSync(out) && !has('--force') && !dryRun) die(`--out 已存在:${out}(要覆盖请加 --force)`)
|
|
79
|
+
|
|
80
|
+
// 版本(缺省 v2 —— D1:新签发默认带 device)
|
|
81
|
+
const signV1 = has('--sign-v1')
|
|
82
|
+
const signV2 = has('--sign-v2')
|
|
83
|
+
if (signV1 && signV2) die('--sign-v1 与 --sign-v2 只能给一个')
|
|
84
|
+
const version = signV1 ? 1 : 2
|
|
85
|
+
|
|
86
|
+
// 设备声明(v2 才有意义):缺省 = **本机设备码**
|
|
87
|
+
const noDevice = has('--no-device')
|
|
88
|
+
const deviceCodeArg = argOf('--device-code')
|
|
89
|
+
let device = null
|
|
90
|
+
if (version === 2 && !noDevice) {
|
|
91
|
+
if (deviceCodeArg) {
|
|
92
|
+
const code = String(deviceCodeArg).trim()
|
|
93
|
+
if (!/^[0-9a-f]{64}$/i.test(code)) {
|
|
94
|
+
die(`--device-code 必须是 64 位十六进制的**设备码**(\`argent license request\` 打印的那串);`
|
|
95
|
+
+ '⛔ 不要给主机名/MAC/IP(D3:指纹只认硬件 UUID 一项)')
|
|
96
|
+
}
|
|
97
|
+
device = { code: code.toLowerCase(), algo: DEVICE_ALGO, issuedAt: new Date().toISOString() }
|
|
98
|
+
} else {
|
|
99
|
+
const dev = currentDevice({})
|
|
100
|
+
if (!dev.ok) {
|
|
101
|
+
die(`读不到本机设备码(${dev.reason}):${dev.error} ⇒ 要么 --device-code <64hex>,要么显式 --no-device`)
|
|
102
|
+
}
|
|
103
|
+
device = { code: dev.code, algo: dev.algo, issuedAt: dev.issuedAt }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 期限:`--days`(缺省 30)或 `--expires`(显式 ISO)
|
|
108
|
+
const now = new Date()
|
|
109
|
+
const days = argOf('--days') === null ? 30 : Number(argOf('--days'))
|
|
110
|
+
if (!Number.isFinite(days) || days <= 0) die(`--days 必须是正数:${argOf('--days')}`)
|
|
111
|
+
const expiresArg = argOf('--expires')
|
|
112
|
+
let expiresAt = new Date(now.getTime() + days * 86400000).toISOString()
|
|
113
|
+
if (expiresArg) {
|
|
114
|
+
const t = Date.parse(String(expiresArg))
|
|
115
|
+
if (!Number.isFinite(t)) die(`--expires 不是可解析时间:${expiresArg}`)
|
|
116
|
+
expiresAt = new Date(t).toISOString()
|
|
117
|
+
}
|
|
118
|
+
const graceDays = argOf('--grace') === null ? 14 : Number(argOf('--grace'))
|
|
119
|
+
if (!Number.isFinite(graceDays) || graceDays < 0) die(`--grace 必须 ≥ 0:${argOf('--grace')}`)
|
|
120
|
+
|
|
121
|
+
const file = {
|
|
122
|
+
v: version,
|
|
123
|
+
userId,
|
|
124
|
+
capabilities: [...new Set(allOf('--cap').map(String).filter(Boolean))].sort(),
|
|
125
|
+
verticals: [...new Set(allOf('--vertical').map(String).filter(Boolean))].sort(),
|
|
126
|
+
seats: argOf('--seats') === null ? 1 : Number(argOf('--seats')),
|
|
127
|
+
issuedAt: now.toISOString(),
|
|
128
|
+
expiresAt,
|
|
129
|
+
graceDays,
|
|
130
|
+
instanceHint: String(argOf('--instance') || 'self-sign'),
|
|
131
|
+
...(device ? { device } : {}),
|
|
132
|
+
}
|
|
133
|
+
if (!Number.isFinite(file.seats) || file.seats < 1) die(`--seats 必须是正数:${argOf('--seats')}`)
|
|
134
|
+
|
|
135
|
+
const signed = signLicenseFile(file, key.secret, { version })
|
|
136
|
+
if (!signed.ok) fail(`签名失败(${signed.reason}${signed.warning ? `:${signed.warning}` : ''})`)
|
|
137
|
+
// **自验**:产物必须能在**独立入口**(verifyLicenseFile)验过 —— "我签完了"不算数
|
|
138
|
+
const check = verifyLicenseFile(signed.file, { secret: key.secret, now: now.getTime() })
|
|
139
|
+
if (!check.ok) fail(`自验失败(${check.reason}):拒绝写出(别把一份自己都验不过的许可交给别人)`)
|
|
140
|
+
|
|
141
|
+
const result = {
|
|
142
|
+
ok: true, empty: false, code: 0, reason: null,
|
|
143
|
+
tool: 'scripts/self-sign-license.mjs',
|
|
144
|
+
warning: '离线**自签**:真实签发在服务端(whyshu-svr);签名是 HMAC 对称密钥 ⇒ 本工具不构成安全边界',
|
|
145
|
+
out, dryRun, written: false,
|
|
146
|
+
signatureVersion: version,
|
|
147
|
+
deviceSigned: version === 2,
|
|
148
|
+
payload: signed.payload,
|
|
149
|
+
licenseFile: signed.file,
|
|
150
|
+
keySource: secretSrc ? `secret-file:${secretSrc}` : 'env:ARGENT_LICENSE_SECRET',
|
|
151
|
+
verified: { ok: check.ok, reason: check.reason, state: check.inGrace ? 'grace' : (check.ok ? 'valid' : 'denied') },
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!dryRun) {
|
|
155
|
+
try {
|
|
156
|
+
mkdirSync(dirname(out), { recursive: true, mode: 0o700 })
|
|
157
|
+
const tmp = `${out}.tmp`
|
|
158
|
+
writeFileSync(tmp, JSON.stringify(signed.file, null, 2) + '\n', { mode: 0o600 })
|
|
159
|
+
chmodSync(tmp, 0o600)
|
|
160
|
+
renameSync(tmp, out)
|
|
161
|
+
result.written = true
|
|
162
|
+
} catch (e) {
|
|
163
|
+
rmSync(`${out}.tmp`, { force: true })
|
|
164
|
+
fail(`写入失败:${e.message}`)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (asJson) {
|
|
169
|
+
console.log(JSON.stringify(result, null, 2))
|
|
170
|
+
} else {
|
|
171
|
+
console.log(`${dryRun ? '🔎 [dry-run] 会自签' : '✅ 已自签'}离线许可(v${version}):→ ${out}`)
|
|
172
|
+
console.log(` 账号:${userId} | 席位:${file.seats} | 到期:${expiresAt} | 宽限 ${graceDays} 天`)
|
|
173
|
+
console.log(` 能力:${file.capabilities.join(',') || '(空 ⇒ 客户端拒一切能力)'} | 业务线:${file.verticals.join(',') || '(空 = 不限)'}`)
|
|
174
|
+
console.log(` 设备:${device ? `已声明(${device.code.slice(0, 12)}…,进签名)` : '未声明(unbound;缺位也已进签名)'}`)
|
|
175
|
+
console.log(` 自验:${check.reason}(独立入口 verifyLicenseFile)`)
|
|
176
|
+
console.log(` ⚠️ ${result.warning}`)
|
|
177
|
+
console.log(' 下一步:node scripts/install-license.mjs --file <该文件> [--dry-run]')
|
|
178
|
+
}
|
|
179
|
+
process.exit(0)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* session 全面体检(dry-run):用当前 dsh 的 adoptSessionEvent 逐事件校验,
|
|
4
|
+
* **不 fail-fast**,列出全部不合格事件 + 上下文。用于评估一个会话的"修复面"。
|
|
5
|
+
* 用法:node scripts/session-health-scan.mjs <解压后的session.jsonl>
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from 'node:fs'
|
|
8
|
+
import { requireDshScope } from './lib/dsh-scope.mjs'
|
|
9
|
+
|
|
10
|
+
// 0.6.0 F1 修复批(2026-10-14):体检**必须**在真宿主树上跑 ⇒ 拿不到 scope 就响亮失败
|
|
11
|
+
// (⛔ 不用 passthrough 假路径继续 —— 旧写法在 npx 宿主上会 import 一个不存在的树)。
|
|
12
|
+
let SCOPE
|
|
13
|
+
try { SCOPE = requireDshScope({ what: '会话体检(session-health-scan)' }) }
|
|
14
|
+
catch (e) { console.error(` ❌ ${e.message}`); process.exit(1) }
|
|
15
|
+
const { adoptSessionEvent, foldSurface } = await import(`${SCOPE}/dsh-session/lib/index.js`)
|
|
16
|
+
|
|
17
|
+
const file = process.argv[2]
|
|
18
|
+
if (!file) { console.error('用法: node scripts/session-health-scan.mjs <session.jsonl>'); process.exit(1) }
|
|
19
|
+
|
|
20
|
+
const lines = readFileSync(file, 'utf8').split('\n').filter((l) => l.trim())
|
|
21
|
+
const parsed = lines.map((l) => JSON.parse(l))
|
|
22
|
+
// 存储态日志第一行是 header(无 type),事件本身**不带 seq**(位置即序号)。
|
|
23
|
+
// foldSurface 要求 seq 连续 ⇒ 这里去掉 header 行、按行号补 seq(2026-09-29 修:
|
|
24
|
+
// 原实现直接把 parsed 喂给 foldSurface,一律报 "seq undefined is not contiguous")。
|
|
25
|
+
// header 行的形态实测是 `{type:'session', version:N, id, createdAt}`(**type 是字符串**,
|
|
26
|
+
// 且**没有 seq**)——别用"type 不是字符串"来认它(2026-09-29 实测踩过)。
|
|
27
|
+
const header = parsed[0] && (parsed[0].type === 'session' || typeof parsed[0].seq !== 'number') ? parsed[0] : null
|
|
28
|
+
const events = (header ? parsed.slice(1) : parsed).map((ev, i) => (
|
|
29
|
+
typeof ev.seq === 'number' ? ev : { ...ev, seq: i }
|
|
30
|
+
))
|
|
31
|
+
if (header) console.log(`header: version=${header.version ?? '?'} id=${String(header.id || '').slice(0, 24)}`)
|
|
32
|
+
console.log(`总事件: ${events.length}`)
|
|
33
|
+
|
|
34
|
+
const bad = []
|
|
35
|
+
for (const [i, ev] of events.entries()) {
|
|
36
|
+
try {
|
|
37
|
+
adoptSessionEvent(structuredClone(ev))
|
|
38
|
+
} catch (e) {
|
|
39
|
+
bad.push({ idx: i, seq: ev.seq, type: ev.type, err: e.message.slice(0, 160) })
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(`\n=== 不合格事件: ${bad.length} 个 ===`)
|
|
44
|
+
for (const b of bad.slice(0, 40)) {
|
|
45
|
+
console.log(` idx=${b.idx} seq=${b.seq} type=${b.type}`)
|
|
46
|
+
console.log(` → ${b.err}`)
|
|
47
|
+
}
|
|
48
|
+
if (bad.length > 40) console.log(` …还有 ${bad.length - 40} 个`)
|
|
49
|
+
|
|
50
|
+
// 再跑 foldSurface(引用完整性)
|
|
51
|
+
try {
|
|
52
|
+
foldSurface(events.map((e) => structuredClone(e)))
|
|
53
|
+
console.log('\n✅ foldSurface(surface 折叠)通过')
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.log(`\n❌ foldSurface 失败: ${e.message.slice(0, 200)}`)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 统计问题类型
|
|
59
|
+
import { createHash } from 'node:crypto'
|
|
60
|
+
const byType = {}
|
|
61
|
+
for (const b of bad) { const k = `${b.type}: ${b.err.slice(0, 60)}`; byType[k] = (byType[k] || 0) + 1 }
|
|
62
|
+
console.log('\n=== 问题分类 ===')
|
|
63
|
+
for (const [k, v] of Object.entries(byType).sort((a, b) => b[1] - a[1])) console.log(` ${v}× ${k}`)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 会话修复器:把 tool/result 事件里 content 为**非数组**的 tool-result block 改写成
|
|
4
|
+
* ContentBlock 数组(`[{type:'text',text}]`),让被毒化的会话重新可加载。
|
|
5
|
+
*
|
|
6
|
+
* ## 为什么需要它(2026-09-18 事故)
|
|
7
|
+
* 插件工具的 `output.render` 返回裸字符串 ⇒ dsh-tools 把 render 返回值**直接**当作
|
|
8
|
+
* tool-result 的 content ⇒ 会话日志里落一条 `content:"…"`。运行中的会话不会立刻发现
|
|
9
|
+
* (surface 折叠只读内容),但**从磁盘加载时会跑 schema 校验**:
|
|
10
|
+
* `dsh-session` → `assertMessageEventShape()` → "message must contain one tool-result block"
|
|
11
|
+
* ⇒ 该会话**打不开**(Failed to load history: … is corrupt)。
|
|
12
|
+
* **surface replace 救不了它** —— 校验针对**事件本身**,不是 surface 视图(2026-09-18 实测踩过)。
|
|
13
|
+
*
|
|
14
|
+
* ## 安全设计(改这段代码前务必读完)
|
|
15
|
+
* ① 会话日志是**多帧 zstd**,dsh **逐帧校验**、且**首帧是 header 行、必须恰好一行**
|
|
16
|
+
* ⇒ **绝不整体重压成单帧**;只重写"包含目标事件的那一帧",其余帧字节**原样保留**。
|
|
17
|
+
* ② 帧边界 = 扫描 zstd magic + 逐切片试解压(压缩数据里偶发假 magic,解不出来的自动并入前一帧)。
|
|
18
|
+
* ③ 只改目标 block 的 content;该行以外的所有行保持**原字节**(逐行比对验收)。
|
|
19
|
+
* ④ 写盘前用 **dsh 自己的校验器**终验:`adoptSessionEvent()`(逐事件 schema)+ `foldSurface()`(surface 折叠)。
|
|
20
|
+
* ⑤ 先写 `.tmp` 校验通过再 `rename` 原子替换;调用方负责先备份(默认 dry-run,不加 `--apply` 不落盘)。
|
|
21
|
+
* ⑥ **改之前先确认目标会话没有被运行中的服务打开**(否则会与服务写入竞争)。
|
|
22
|
+
*
|
|
23
|
+
* ## 用法
|
|
24
|
+
* # 扫描所有会话,只报告(默认 dry-run)
|
|
25
|
+
* node scripts/session-repair-tool-result-content.mjs --scan
|
|
26
|
+
* # 扫描并修复(原子替换,建议先停掉/别打开这些会话)
|
|
27
|
+
* node scripts/session-repair-tool-result-content.mjs --scan --apply
|
|
28
|
+
* # 只处理指定会话(id 或 session 文件路径)
|
|
29
|
+
* node scripts/session-repair-tool-result-content.mjs --session session-06732069-... [--apply]
|
|
30
|
+
*/
|
|
31
|
+
import { readFileSync, writeFileSync, renameSync, existsSync, readdirSync, statSync } from 'node:fs'
|
|
32
|
+
import { zstdCompressSync } from 'node:zlib'
|
|
33
|
+
import { FRAME_COMPRESS_OPTS, decodeSessionLines, splitFrames as splitFramesImpl } from './lib/zstd-frames.mjs'
|
|
34
|
+
import { join } from 'node:path'
|
|
35
|
+
import { homedir } from 'node:os'
|
|
36
|
+
import { requireDshScope } from './lib/dsh-scope.mjs'
|
|
37
|
+
|
|
38
|
+
const argv = process.argv.slice(2)
|
|
39
|
+
const APPLY = argv.includes('--apply')
|
|
40
|
+
const SCAN = argv.includes('--scan') || !argv.includes('--session')
|
|
41
|
+
const sessionArg = (() => { const i = argv.indexOf('--session'); return i === -1 ? null : argv[i + 1] })()
|
|
42
|
+
// 0.6.0 F1 修复批(2026-10-14):**必须**有 scope 才能干活 ⇒ 拿不到就响亮失败(⛔ 不用假路径继续)。
|
|
43
|
+
// 旧写法 `const SCOPE = resolveDshScope()` 在 npx 宿主上会拿到一个**不存在**的路径 ⇒ 下面那条
|
|
44
|
+
// 动态 import 报 ERR_MODULE_NOT_FOUND(看着像"dsh 坏了",其实是探错树)。
|
|
45
|
+
let SCOPE
|
|
46
|
+
try { SCOPE = requireDshScope({ what: '会话修复(session-repair-tool-result-content)' }) }
|
|
47
|
+
catch (e) { console.error(` ❌ ${e.message}`); process.exit(1) }
|
|
48
|
+
const DSH_HOME = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
49
|
+
|
|
50
|
+
const { adoptSessionEvent, foldSurface } = await import(`${SCOPE}/dsh-session/lib/index.js`)
|
|
51
|
+
|
|
52
|
+
const MAGIC = Buffer.from([0x28, 0xb5, 0x2f, 0xfd])
|
|
53
|
+
// 与 dsh 写帧参数一致(常量也放共享库里,避免本地副本再次漂移)
|
|
54
|
+
const CHECKSUM = FRAME_COMPRESS_OPTS
|
|
55
|
+
|
|
56
|
+
let pass = 0, fail = 0
|
|
57
|
+
const ok = (c, n) => { c ? (pass++, console.log(` ✅ ${n}`)) : (fail++, console.log(` ❌ ${n}`)) }
|
|
58
|
+
|
|
59
|
+
// 切帧/解码实现见 lib/zstd-frames.mjs(与升级门禁共用一份;本文件此前是它的唯一副本)
|
|
60
|
+
const splitFrames = (buf) => splitFramesImpl(buf)
|
|
61
|
+
const decodeLines = (buf) => {
|
|
62
|
+
const { frames, lines } = decodeSessionLines(buf)
|
|
63
|
+
return { frames, lines }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** 用 dsh 自己的校验器验收(逐事件 schema + surface 折叠)。 */
|
|
67
|
+
function validateWithDsh(lines) {
|
|
68
|
+
const events = lines.map((l) => JSON.parse(l.raw))
|
|
69
|
+
for (const [i, ev] of events.entries()) {
|
|
70
|
+
if (ev.seq !== i) throw new Error(`事件下标 ${i} 与 seq ${ev.seq} 不一致`)
|
|
71
|
+
adoptSessionEvent(structuredClone(ev))
|
|
72
|
+
}
|
|
73
|
+
const fold = foldSurface(events.map((e) => structuredClone(e)))
|
|
74
|
+
const bad = fold.nodes.filter((q) => {
|
|
75
|
+
const ev = events[q]
|
|
76
|
+
return ev.type === 'tool/result' && !Array.isArray(ev.data.message.content[0].content)
|
|
77
|
+
})
|
|
78
|
+
return { events: events.length, surfaceNodes: fold.nodes.length, badSurfaceContent: bad.length }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** 找出全部非法 tool-result(content 非数组)。 */
|
|
82
|
+
function findPoison(lines) {
|
|
83
|
+
const hits = []
|
|
84
|
+
for (const line of lines) {
|
|
85
|
+
if (!line.raw.includes('"tool/result"')) continue
|
|
86
|
+
let ev
|
|
87
|
+
try { ev = JSON.parse(line.raw) } catch { continue }
|
|
88
|
+
if (ev.type !== 'tool/result') continue
|
|
89
|
+
const block = ev.data?.message?.content?.[0]
|
|
90
|
+
if (block?.type !== 'tool-result' || Array.isArray(block.content)) continue
|
|
91
|
+
hits.push({ line, ev, block })
|
|
92
|
+
}
|
|
93
|
+
return hits
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function repairFile(path) {
|
|
97
|
+
console.log('='.repeat(74))
|
|
98
|
+
console.log(path)
|
|
99
|
+
const buf = readFileSync(path)
|
|
100
|
+
const { frames, lines } = decodeLines(buf)
|
|
101
|
+
const hits = findPoison(lines)
|
|
102
|
+
const health = validateWithDsh(lines)
|
|
103
|
+
console.log(` 帧 ${frames.length} 个|事件行 ${lines.length}|非法 tool-result ${hits.length} 条`)
|
|
104
|
+
if (hits.length === 0) {
|
|
105
|
+
ok(health.badSurfaceContent === 0 && health.events === lines.length,
|
|
106
|
+
`dsh 校验通过,无需修复(事件 ${health.events}|surface 节点 ${health.surfaceNodes}|非数组 content ${health.badSurfaceContent})`)
|
|
107
|
+
return { repaired: 0 }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const touchedFrames = new Set()
|
|
111
|
+
for (const { line, ev, block } of hits) {
|
|
112
|
+
const wasKind = Array.isArray(block.content) ? 'array' : typeof block.content
|
|
113
|
+
const text = typeof block.content === 'string' ? block.content : JSON.stringify(block.content)
|
|
114
|
+
block.content = [{ type: 'text', text }]
|
|
115
|
+
line.raw = JSON.stringify(ev)
|
|
116
|
+
touchedFrames.add(line.frame)
|
|
117
|
+
console.log(` 🔧 seq ${ev.seq}(turn ${ev.data.turn} step ${ev.data.step}):content ${wasKind} → ContentBlock[](帧 ${line.frame})`)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const rebuilt = new Map()
|
|
121
|
+
for (const f of touchedFrames) {
|
|
122
|
+
const raws = lines.filter((l) => l.frame === f).map((l) => l.raw)
|
|
123
|
+
rebuilt.set(f, zstdCompressSync(Buffer.from(raws.join('\n') + '\n', 'utf8'), CHECKSUM))
|
|
124
|
+
}
|
|
125
|
+
const out = Buffer.concat(frames.map((fr, i) => rebuilt.get(i) ?? buf.subarray(fr.start, fr.end)))
|
|
126
|
+
|
|
127
|
+
// 验收:结构 + dsh 校验器 + 未改动行逐字节一致
|
|
128
|
+
const redecoded = decodeLines(out)
|
|
129
|
+
const verdict = validateWithDsh(redecoded.lines)
|
|
130
|
+
const fixedSeqs = new Set(hits.map((h) => h.ev.seq))
|
|
131
|
+
const differ = redecoded.lines.map((l, i) => i).filter((i) => {
|
|
132
|
+
const seq = JSON.parse(lines[i].raw).seq
|
|
133
|
+
return !fixedSeqs.has(seq) && lines[i].raw !== redecoded.lines[i].raw
|
|
134
|
+
})
|
|
135
|
+
ok(verdict.badSurfaceContent === 0, `dsh 校验器通过(事件 ${verdict.events}|surface 节点 ${verdict.surfaceNodes}|surface 上非数组 content ${verdict.badSurfaceContent})`)
|
|
136
|
+
ok(differ.length === 0, `除目标行外逐字节一致${differ.length ? `(差异行 ${differ.slice(0, 5)})` : ''}`)
|
|
137
|
+
if (verdict.badSurfaceContent > 0 || differ.length > 0) { fail++; console.log(' ❌ 验收未通过,不写盘'); return { repaired: 0 } }
|
|
138
|
+
|
|
139
|
+
const tmp = `${path}.repair.tmp`
|
|
140
|
+
writeFileSync(tmp, out)
|
|
141
|
+
if (APPLY) { renameSync(tmp, path); console.log(` ✅ 已原子替换(${buf.length} → ${out.length} 字节)`) }
|
|
142
|
+
else console.log(` (dry-run:已写出 ${tmp},未替换原文件;加 --apply 生效)`)
|
|
143
|
+
return { repaired: hits.length }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** 收集目标文件。 */
|
|
147
|
+
let files = []
|
|
148
|
+
if (sessionArg) {
|
|
149
|
+
if (existsSync(sessionArg)) files = [sessionArg]
|
|
150
|
+
else {
|
|
151
|
+
const root = join(DSH_HOME, 'sessions')
|
|
152
|
+
for (const ws of readdirSync(root)) {
|
|
153
|
+
const p = join(root, ws, sessionArg, 'session.v3.jsonl.zstd')
|
|
154
|
+
if (existsSync(p)) files.push(p)
|
|
155
|
+
}
|
|
156
|
+
if (files.length === 0) { console.error(`❌ 找不到会话 ${sessionArg}`); process.exit(1) }
|
|
157
|
+
}
|
|
158
|
+
} else if (SCAN) {
|
|
159
|
+
const root = join(DSH_HOME, 'sessions')
|
|
160
|
+
for (const ws of readdirSync(root)) {
|
|
161
|
+
const wsDir = join(root, ws)
|
|
162
|
+
if (!statSync(wsDir).isDirectory()) continue
|
|
163
|
+
for (const id of readdirSync(wsDir)) {
|
|
164
|
+
const p = join(wsDir, id, 'session.v3.jsonl.zstd')
|
|
165
|
+
if (existsSync(p)) files.push(p)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log(`扫描 ${files.length} 个会话文件${APPLY ? '(--apply:会写盘)' : '(dry-run:只报告,不改文件)'}\n`)
|
|
171
|
+
let total = 0
|
|
172
|
+
for (const f of files) {
|
|
173
|
+
try { total += repairFile(f).repaired } catch (err) { console.log(` ❌ ${f}: ${err.message}`); fail++ }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log('\n' + '='.repeat(74))
|
|
177
|
+
console.log(`合计需要修复的 tool-result:${total} 条|校验通过 ${pass} / 失败 ${fail}`)
|
|
178
|
+
if (fail > 0) process.exit(1)
|
|
179
|
+
if (total === 0) console.log('✅ 没有发现被毒化的会话')
|
|
180
|
+
else if (!APPLY) console.log('✅ dry-run 完成;确认无误后加 --apply 落盘(记得先备份)')
|
|
181
|
+
else console.log('✅ 修复完成:这些会话现在可以正常加载了')
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ============================================================
|
|
3
|
+
# setup.sh — ML 卖家角色初始化(薄封装)
|
|
4
|
+
# 核心逻辑已迁移到 init-ml.sh(同目录),本脚本保持向后兼容:
|
|
5
|
+
# 老用户执行 setup.sh 等效于执行 init-ml.sh
|
|
6
|
+
#
|
|
7
|
+
# 用法:
|
|
8
|
+
# setup.sh 完整初始化(等价 init-ml.sh)
|
|
9
|
+
# setup.sh --refresh-stores 仅刷新店铺选项
|
|
10
|
+
# setup.sh --reconfig 强制重新配置
|
|
11
|
+
# ============================================================
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
INIT_ML="$SCRIPT_DIR/init-ml.sh"
|
|
16
|
+
|
|
17
|
+
if [ ! -f "$INIT_ML" ]; then
|
|
18
|
+
echo "✗ 未找到 init-ml.sh(${INIT_ML}),请确认脚本完整分发" >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
exec bash "$INIT_ML" "$@"
|