@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,278 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* config-explain.mjs —— **「我改了到底生效没」一眼可见**(只读;2026-10-09,配置地基第三批次)
|
|
4
|
+
*
|
|
5
|
+
* 契约:`docs/Argent-宿主适配契约.md` §7.11「生效来源」可见化 + 冲突告警。
|
|
6
|
+
* 读取器/判据(**唯一实现**):`core/lib/config-catalog.mjs` 的
|
|
7
|
+
* `resolveEffective()`(纯函数)+ `createReader().effectiveOf()`(取各级现值)。
|
|
8
|
+
* 本文件**只打印**:⛔ 不另写一份解析链、⛔ 不写任何配置、⛔ 不联网、⛔ 不改读取顺序。
|
|
9
|
+
*
|
|
10
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
11
|
+
* 它回答的问题
|
|
12
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
13
|
+
* 「很多变量存放在不同地方,读取的时候不是会容易混淆吗?」
|
|
14
|
+
* 「我在设置页改了,但更靠前的那一级还在生效?」
|
|
15
|
+
* 静态提示("两套链不同序")答不了这个 —— 必须**逐项**说清:这一项此刻
|
|
16
|
+
* **哪几级有值** / **谁赢了** / **谁被压住、为什么** / **每一级的落点在哪**。
|
|
17
|
+
*
|
|
18
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
19
|
+
* 用法
|
|
20
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
21
|
+
* node scripts/config-explain.mjs --key <key> # 一项的完整解析(人读)
|
|
22
|
+
* node scripts/config-explain.mjs --key <key> --json # 同上,机器可读
|
|
23
|
+
* node scripts/config-explain.mjs --all # **只列有冲突的项**(≥2 级同时有值)
|
|
24
|
+
* node scripts/config-explain.mjs --all --json # 冲突清单(机器可读)
|
|
25
|
+
* node scripts/config-explain.mjs --list # 全部项一行的总览(谁赢了 + 有无冲突)
|
|
26
|
+
* node scripts/config-explain.mjs --list --conflict # 总览里只留冲突项
|
|
27
|
+
* node scripts/config-explain.mjs # 不带参数 = `--all`
|
|
28
|
+
* node scripts/config-explain.mjs --root <dir> [--data-dir <dir>] # 夹具(只读)
|
|
29
|
+
*
|
|
30
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
31
|
+
* 退出码(**这就是它作为巡检判据的语义**)
|
|
32
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
33
|
+
* `0` **无冲突**(或 `--list` 跑通)—— 没有"有值被压住"的项
|
|
34
|
+
* `2` **有冲突**:查询本身**成功**,2 只是一个信号(`--all` 时有 ≥1 项;`--key` 时该项 conflict)
|
|
35
|
+
* ⇒ 接巡检时:`0` 安静、`2` 值得看一眼;⛔ `2` **不是**报错、也不代表配置坏了
|
|
36
|
+
* `1` **用法/环境错误**:缺 `--key`、key 未声明、目录文件不合法、数据目录读不出来
|
|
37
|
+
* ⚠️ 冲突数是**动态**的(取决于这台机器此刻各级有没有值),所以别把 `2` 接成"红灯告警",
|
|
38
|
+
* 它是"有一项被别处压着,去看看吧"。
|
|
39
|
+
*
|
|
40
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
41
|
+
* 脱敏(硬要求)
|
|
42
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
43
|
+
* `secret: true` 的项:**任何**输出位(生效值 / 被压住的值 / 长度)只出现
|
|
44
|
+
* `❰已设置(长度N)❱`,**永不含明文**(脱敏发生在 L1 的 `resolveEffective()` 里,
|
|
45
|
+
* 本文件拿到的就已经是占位符 —— 不是"打印时记得别打")。回归用诱饵值做子串断言钉住。
|
|
46
|
+
*
|
|
47
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
48
|
+
* 纪律
|
|
49
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
50
|
+
* · **只读**:源码里零写 API(回归 `scripts/tests/config-effective.test.mjs` 静态断言);
|
|
51
|
+
* · **不含业务词**(`vertical-lint` 会扫 `scripts/`):分类/key/归属一律从目录**读**出来;
|
|
52
|
+
* · ⛔ **不碰 `todayChain`、不改任何读取顺序**(那是迁移批次的活)—— 这里只**如实展示**
|
|
53
|
+
* "规范链此刻谁赢",以及"今天代码按登记的旧链先读哪一级、那一级现在有没有值"。
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
import { dirname, resolve } from 'node:path'
|
|
57
|
+
import { fileURLToPath } from 'node:url'
|
|
58
|
+
|
|
59
|
+
import { RESOLUTION_ORDER, createReader, redactSecret, validateCatalog } from '../core/lib/config-catalog.mjs'
|
|
60
|
+
import { isDirectRun } from './lib/is-direct-run.mjs'
|
|
61
|
+
|
|
62
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
63
|
+
const REPO = resolve(HERE, '..')
|
|
64
|
+
|
|
65
|
+
const argv = process.argv.slice(2)
|
|
66
|
+
const has = (f) => argv.includes(f)
|
|
67
|
+
const argOf = (name, dflt = null) => {
|
|
68
|
+
const i = argv.indexOf(name)
|
|
69
|
+
return i >= 0 ? argv[i + 1] ?? dflt : dflt
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 层级 → 人话(只在这里写一份;页面侧的同名映射在 `plugins/argent-skin/dist/client.js`)。 */
|
|
73
|
+
const LEVEL_ZH = {
|
|
74
|
+
env: '进程环境变量',
|
|
75
|
+
envfile: 'env 文件',
|
|
76
|
+
json: 'config.json',
|
|
77
|
+
machine: '机器本地文件',
|
|
78
|
+
server: '服务端(预留)',
|
|
79
|
+
default: '声明 default',
|
|
80
|
+
}
|
|
81
|
+
const zh = (l) => (l ? `${l}(${LEVEL_ZH[l] || '未知层级'})` : '(无)')
|
|
82
|
+
/** 密钥占位形态(与 L1 `redactSecret()` 的输出同形):已是占位就不再脱敏(幂等,否则长度会说谎)。 */
|
|
83
|
+
const SECRET_MARK_RE = /^❰已设置\(长度\d+\)❱$/
|
|
84
|
+
|
|
85
|
+
/** 一行摘要里的生效来源(含"覆盖了谁")。 */
|
|
86
|
+
function fromText(eff) {
|
|
87
|
+
if (!eff.from) return '无值(五级链全未命中)'
|
|
88
|
+
if (eff.from === 'default') return 'default(声明里的默认值)'
|
|
89
|
+
const over = eff.shadowed.map((s) => s.level)
|
|
90
|
+
return zh(eff.from) + (over.length ? `(覆盖了 ${over.join('、')})` : '')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** 把一项的完整解析渲染成文本行(人读;⛔ 不含明文密钥)。 */
|
|
94
|
+
function explainLines(key, eff, opts = {}) {
|
|
95
|
+
const out = []
|
|
96
|
+
out.push(`【${key}】${eff.declared ? '' : ' ⚠️ 目录里没有这一项(未声明)'}`)
|
|
97
|
+
out.push(` 声明链(规范链,恒按它解析) ${eff.chain.join(' → ')}`)
|
|
98
|
+
out.push(` 本项 binding 真会读的级 ${eff.applicable.join(' → ') || '(无)'}`)
|
|
99
|
+
if (eff.todayChain) {
|
|
100
|
+
out.push(` 今日链(todayChain,**登记**的今天代码优先级,本批不按它解析) ${eff.todayChain.join(' → ')}`)
|
|
101
|
+
} else {
|
|
102
|
+
out.push(' 今日链(todayChain) (未登记 ⇒ 与规范链同序)')
|
|
103
|
+
}
|
|
104
|
+
out.push(' 各级现值(**脱敏**;✗ = 本级没值)')
|
|
105
|
+
for (const level of eff.chain) {
|
|
106
|
+
if (!eff.applicable.includes(level)) {
|
|
107
|
+
out.push(` · ${level.padEnd(8)} — 本项 binding 不读这一级${level === 'server' ? '(`server` 来源本批预留、无读取实现)' : ''}`)
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
const detail = (eff.levelDetail && eff.levelDetail[level]) || ''
|
|
111
|
+
const hit = level === eff.from || (eff.present || []).includes(level)
|
|
112
|
+
out.push(` · ${level.padEnd(8)} ${hit ? '✓' : '✗'} ${detail}`)
|
|
113
|
+
}
|
|
114
|
+
out.push(` ➜ 生效级:${zh(eff.from)}`)
|
|
115
|
+
if (eff.from && eff.from !== 'default') out.push(` 落点:${eff.detail || '(未取到落点)'}`)
|
|
116
|
+
if (eff.from) {
|
|
117
|
+
out.push(eff.secret
|
|
118
|
+
? ` 生效值:${String(eff.value)}(密钥项:只给"已设置(长度)",**永不含明文**)`
|
|
119
|
+
: ` 生效值:${JSON.stringify(eff.value)}`)
|
|
120
|
+
}
|
|
121
|
+
if (eff.shadowed.length) {
|
|
122
|
+
for (const s of eff.shadowed) {
|
|
123
|
+
out.push(` ➜ 被压住:${zh(s.level)} —— ${s.why}`)
|
|
124
|
+
out.push(` 它的值:${JSON.stringify(s.value)}${eff.secret ? '(密钥项:只给"已设置(长度)")' : ''}`)
|
|
125
|
+
out.push(` 落点:${(eff.levelDetail && eff.levelDetail[s.level]) || '(未取到落点)'}`)
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
out.push(' ➜ 被压住:(无 —— 只有一个级有值)')
|
|
129
|
+
}
|
|
130
|
+
if (eff.invalid && eff.invalid.length) {
|
|
131
|
+
for (const x of eff.invalid) out.push(` ⚠️ 有值但取不动:${zh(x.level)} —— ${x.why}`)
|
|
132
|
+
}
|
|
133
|
+
if (eff.todayFirst) {
|
|
134
|
+
out.push(` ➜ 今天代码按登记的 todayChain **先读** ${zh(eff.todayFirst)}(当前${eff.todayFirstPresent ? '**有值**' : '无值'})`)
|
|
135
|
+
}
|
|
136
|
+
if (eff.divergent) {
|
|
137
|
+
out.push(' ⚠️ **两套口径答案不同**:规范链给 ' + eff.from + ',今天代码先读 ' + eff.todayFirst
|
|
138
|
+
+ '(它此刻有值)⇒ 按老代码跑出来的值与上面「生效级」不是同一个。迁移(批 3)完成前以运行时为准。')
|
|
139
|
+
}
|
|
140
|
+
if (opts.note !== false) out.push(` ℹ️ ${eff.note}`)
|
|
141
|
+
return out
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function main() {
|
|
145
|
+
const root = resolve(argOf('--root', REPO))
|
|
146
|
+
const dataDir = argOf('--data-dir', null)
|
|
147
|
+
const asJson = has('--json')
|
|
148
|
+
const key = argOf('--key', null)
|
|
149
|
+
const wantAll = has('--all') || (!key && !has('--list'))
|
|
150
|
+
const wantList = has('--list')
|
|
151
|
+
const onlyConflict = has('--conflict')
|
|
152
|
+
|
|
153
|
+
if (key && (wantList || has('--all'))) {
|
|
154
|
+
console.error('✗ `--key` 与 `--all` / `--list` 不能同时用(一次只做一件事)')
|
|
155
|
+
process.exit(1)
|
|
156
|
+
}
|
|
157
|
+
// `--key` 写了却没给值 ⇒ **用法错误**(⛔ 不许静默退化成 `--all` —— 那会让"我查这一项"变成"扫全部"而没人发现)
|
|
158
|
+
if (has('--key') && !key) {
|
|
159
|
+
console.error('✗ `--key` 需要一个 key(如 `--key ml.daily.hour`);看全部:`--list` / 只看冲突:`--all`')
|
|
160
|
+
process.exit(1)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const reader = createReader({ root, ...(dataDir ? { dataDir } : {}) })
|
|
164
|
+
|
|
165
|
+
// 目录形状自检:声明本身不合法 ⇒ 判"环境错误"(exit 1),⛔ 不拿坏目录装作能解释
|
|
166
|
+
const problems = validateCatalog(reader.catalog())
|
|
167
|
+
if (problems.length) {
|
|
168
|
+
if (asJson) console.log(JSON.stringify({ ok: false, error: '配置目录不合法', problems }, null, 2))
|
|
169
|
+
else {
|
|
170
|
+
console.error(`✗ 配置目录不合法(${problems.length} 条)—— 先跑 node scripts/check-config-catalog.mjs --strict`)
|
|
171
|
+
for (const p of problems.slice(0, 10)) console.error(` · ${p}`)
|
|
172
|
+
}
|
|
173
|
+
process.exit(1)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const effOf = (k) => {
|
|
177
|
+
const e = reader.effectiveOf(k)
|
|
178
|
+
// 出门前再过一道脱敏闸:即便 L1 出了岔子,这里也不会把 `secret` 项的真值打出去。
|
|
179
|
+
// ⚠️ **必须幂等**:L1(`resolveEffective()`)已把值换成 `❰已设置(长度N)❱`;若无条件再脱敏一次,
|
|
180
|
+
// N 会变成**占位串自己的长度**(恒 11)—— 实测踩过。用户会拿这个数字去比对"密钥换没换",
|
|
181
|
+
// 那就把「如实展示」变成了如实说谎。判据 = 形态已经是占位就直接放过(含被压住的那几级)。
|
|
182
|
+
if (e.secret) {
|
|
183
|
+
if (e.value !== null && e.value !== undefined && !SECRET_MARK_RE.test(String(e.value))) e.value = redactSecret(e.value)
|
|
184
|
+
for (const s of e.shadowed || []) {
|
|
185
|
+
if (!SECRET_MARK_RE.test(String(s.value))) s.value = redactSecret(s.value)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return e
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (key) {
|
|
192
|
+
const item = reader.all().find((i) => String(i.key) === String(key))
|
|
193
|
+
if (!item) {
|
|
194
|
+
const near = reader.all().map((i) => i.key).filter((k) => k.includes(String(key).slice(0, 4)))
|
|
195
|
+
if (asJson) console.log(JSON.stringify({ ok: false, error: `目录里没有 ${key}(未声明)`, near }, null, 2))
|
|
196
|
+
else {
|
|
197
|
+
console.error(`✗ 目录里没有 ${key} —— 配置目录是**声明真源**,没声明就无法解释它的来路。`)
|
|
198
|
+
if (near.length) console.error(` 相近的 key:${near.slice(0, 8).join('、')}`)
|
|
199
|
+
console.error(' · 看全部:node scripts/config-explain.mjs --list')
|
|
200
|
+
}
|
|
201
|
+
process.exit(1)
|
|
202
|
+
}
|
|
203
|
+
const eff = effOf(key)
|
|
204
|
+
if (asJson) console.log(JSON.stringify({ ok: true, key, effective: eff }, null, 2))
|
|
205
|
+
else {
|
|
206
|
+
console.log(`配置生效来源解释(root=${root}|数据目录=${reader.dataDir()})\n`)
|
|
207
|
+
console.log(explainLines(key, eff).join('\n'))
|
|
208
|
+
console.log('')
|
|
209
|
+
console.log(eff.conflict
|
|
210
|
+
? '结论:⚠️ 有冲突(≥2 级同时有值 ⇒ 有值被压住)。被压住的那一级"改了不生效"。'
|
|
211
|
+
: '结论:✅ 无冲突(只有一个级有值)。')
|
|
212
|
+
}
|
|
213
|
+
process.exit(eff.conflict ? 2 : 0)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (wantList) {
|
|
217
|
+
const items = reader.all().map((it) => effOf(it.key))
|
|
218
|
+
const rows = onlyConflict ? items.filter((e) => e.conflict) : items
|
|
219
|
+
if (asJson) {
|
|
220
|
+
console.log(JSON.stringify({
|
|
221
|
+
ok: true, root, dataDir: reader.dataDir(), resolutionOrder: [...RESOLUTION_ORDER],
|
|
222
|
+
total: items.length, conflicts: items.filter((e) => e.conflict).length,
|
|
223
|
+
items: rows.map((e) => ({
|
|
224
|
+
key: e.key, from: e.from, present: e.present,
|
|
225
|
+
shadowed: e.shadowed.map((s) => s.level), conflict: e.conflict,
|
|
226
|
+
divergent: e.divergent, todayFirst: e.todayFirst, todayFirstPresent: e.todayFirstPresent,
|
|
227
|
+
detail: e.detail, secret: e.secret, note: e.note,
|
|
228
|
+
})),
|
|
229
|
+
}, null, 2))
|
|
230
|
+
} else {
|
|
231
|
+
console.log(`配置生效来源总览(root=${root}|数据目录=${reader.dataDir()})`)
|
|
232
|
+
console.log(` 规范链:${RESOLUTION_ORDER.join(' → ')}|声明 ${items.length} 项|有冲突 ${items.filter((e) => e.conflict).length} 项\n`)
|
|
233
|
+
for (const e of rows) {
|
|
234
|
+
const flag = e.conflict ? '⚠️ 冲突' : ' — '
|
|
235
|
+
console.log(` ${flag} ${String(e.key).padEnd(34)} 生效 ${fromText(e)}${e.divergent ? ' | ⚠️ 今天代码先读 ' + e.todayFirst : ''}`)
|
|
236
|
+
}
|
|
237
|
+
if (onlyConflict && !rows.length) console.log(' (没有冲突项)')
|
|
238
|
+
}
|
|
239
|
+
const conflicts = items.filter((e) => e.conflict).length
|
|
240
|
+
process.exit(conflicts ? 2 : 0)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (wantAll) {
|
|
244
|
+
const all = reader.all().map((it) => effOf(it.key))
|
|
245
|
+
const conflicts = all.filter((e) => e.conflict)
|
|
246
|
+
const divergent = all.filter((e) => e.divergent)
|
|
247
|
+
if (asJson) {
|
|
248
|
+
console.log(JSON.stringify({
|
|
249
|
+
ok: true, root, dataDir: reader.dataDir(), resolutionOrder: [...RESOLUTION_ORDER],
|
|
250
|
+
total: all.length, conflicts: conflicts.length, divergent: divergent.length,
|
|
251
|
+
items: conflicts.map((e) => ({
|
|
252
|
+
key: e.key, from: e.from, present: e.present, shadowed: e.shadowed,
|
|
253
|
+
conflict: true, divergent: e.divergent, detail: e.detail, note: e.note,
|
|
254
|
+
todayChain: e.todayChain, todayFirst: e.todayFirst, todayFirstPresent: e.todayFirstPresent,
|
|
255
|
+
})),
|
|
256
|
+
divergentItems: divergent.map((e) => ({ key: e.key, from: e.from, todayFirst: e.todayFirst, note: e.note })),
|
|
257
|
+
}, null, 2))
|
|
258
|
+
} else {
|
|
259
|
+
console.log(`配置冲突巡检(root=${root}|数据目录=${reader.dataDir()})`)
|
|
260
|
+
console.log(` 规范链:${RESOLUTION_ORDER.join(' → ')}|声明 ${all.length} 项`)
|
|
261
|
+
console.log(` 有冲突 ${conflicts.length} 项(≥1 级有值被压住)|两套口径答案不同 ${divergent.length} 项\n`)
|
|
262
|
+
if (!conflicts.length) {
|
|
263
|
+
console.log(' ✅ 没有任何项被别处压住(每一级的"谁赢"都是唯一的)')
|
|
264
|
+
} else {
|
|
265
|
+
for (const e of conflicts) {
|
|
266
|
+
console.log(explainLines(e.key, e, { note: false }).join('\n'))
|
|
267
|
+
console.log('')
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (divergent.length && !conflicts.length) {
|
|
271
|
+
for (const e of divergent) console.log(` ⚠️ ${e.key}:规范链给 ${e.from},今天代码先读 ${e.todayFirst}`)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
process.exit(conflicts.length ? 2 : 0)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (isDirectRun(import.meta.url)) main()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
# encoding: UTF-8
|
|
4
|
+
# configure-provider.rb — 写入/合并 DSH settings.yaml 的 whyshu 代理 provider 配置
|
|
5
|
+
# 用法: ruby configure-provider.rb <settings.yaml 路径> [api_base]
|
|
6
|
+
require 'yaml'
|
|
7
|
+
|
|
8
|
+
path = ARGV[0] || File.join(Dir.home, '.dsh', 'settings.yaml')
|
|
9
|
+
api_base = ARGV[1] || 'https://whyshu.com/api/argent/v1'
|
|
10
|
+
|
|
11
|
+
doc = File.exist?(path) ? (YAML.load_file(path) || {}) : {}
|
|
12
|
+
# ⚠️ contextWindow 与 maxTokens **成对自洽**(2026-10-02,与 provider-switch.sh 同源):
|
|
13
|
+
# dsh-compaction-basic 的 resolveCompactSpec 要求
|
|
14
|
+
# contextWindow - maxTokens > 0 且 上式 - headroomTokens(默认 65536) > 0,
|
|
15
|
+
# 否则抛 TargetPressureConfigError,而它在 agent/pre-step 被 catch ⇒ **只 warn 一次、
|
|
16
|
+
# 此后每步静默跳过压缩**(比"不压缩"更难发现)。旧值 131072/131072 余量为 0 ⇒ 正是这种失能形态。
|
|
17
|
+
WINDOW = 131_072
|
|
18
|
+
MAX_OUT = 32_768 # 窗口 131072 下输出上限的安全取值(压缩阈值 = 32768)
|
|
19
|
+
doc['llm-deepseek'] = {
|
|
20
|
+
'baseURL' => api_base,
|
|
21
|
+
'apiKeyEnv' => 'WHYSHU_API_KEY',
|
|
22
|
+
'models' => [
|
|
23
|
+
{ 'id' => 'deepseek-v4-pro', 'name' => 'DeepSeek V4 Pro(问述代理)', 'contextWindow' => WINDOW, 'maxTokens' => MAX_OUT },
|
|
24
|
+
{ 'id' => 'deepseek-chat', 'name' => 'DeepSeek Chat(问述代理)', 'contextWindow' => WINDOW, 'maxTokens' => MAX_OUT },
|
|
25
|
+
],
|
|
26
|
+
}
|
|
27
|
+
doc['agent-default-model'] = { 'provider' => 'deepseek-official', 'model' => 'deepseek-v4-pro' }
|
|
28
|
+
File.write(path, YAML.dump(doc))
|
|
29
|
+
puts " ✓ provider 已配置: #{path} → #{api_base}"
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* customer-config-migrate —— **在客户机上**把 gateway.json 迁到新形状(P5 步骤,2026-09-18 准备稿)。
|
|
4
|
+
*
|
|
5
|
+
* 为什么要有它:客户机的 `gateway.json` 是旧形状(只有 2 个角色),新插件要 12 个角色
|
|
6
|
+
* (各自 preset / skills / mentionNames / model)。而**凭据与机器相关**(botAppId/botSecret/
|
|
7
|
+
* chatId 只存在于客户机本机,且 gateway.json 含密钥 ⇒ 绝不能从开发机拷)。
|
|
8
|
+
* 故本脚本:拿仓库里的**结构模板**(`deploy/customer/roles.spec.json`,已剔除凭据)
|
|
9
|
+
* + 客户机**现有** gateway.json 里同角色的凭据 ⇒ 合并出新文件。
|
|
10
|
+
*
|
|
11
|
+
* 安全设计:
|
|
12
|
+
* · **默认 dry-run**(只打印将要发生什么),`--apply` 才落盘;
|
|
13
|
+
* · 落盘前把原文件备份为 `gateway.json.bak-<时间戳>`;
|
|
14
|
+
* · 缺失凭据的角色 → botAppId 留空 = 主 Bot 兜底(并在报告里点名,便于后续补配);
|
|
15
|
+
* · 只写 `gateway.json`;`roles.json` 交给既有生成器(`check-role-mapping.mjs --write`),
|
|
16
|
+
* 避免两处各写一份映射(历史踩过:漏改一处 ⇒ 角色拿不到工具)。
|
|
17
|
+
*
|
|
18
|
+
* 用法(在客户机上,仓库已 pull 到最新):
|
|
19
|
+
* node scripts/customer-config-migrate.mjs # dry-run(默认)
|
|
20
|
+
* node scripts/customer-config-migrate.mjs --apply # 落盘(自动备份)
|
|
21
|
+
* node scripts/customer-config-migrate.mjs --spec <路径> --data-dir <路径>
|
|
22
|
+
* 退出码:0 成功(含 dry-run);2 参数/读取错误。
|
|
23
|
+
*/
|
|
24
|
+
import { readFileSync, writeFileSync, existsSync, copyFileSync } from 'node:fs'
|
|
25
|
+
import { join, dirname, resolve } from 'node:path'
|
|
26
|
+
import { loadRegistryDoc } from './lib/registry.mjs'
|
|
27
|
+
import { fileURLToPath } from 'node:url'
|
|
28
|
+
import { homedir } from 'node:os'
|
|
29
|
+
import { resolveDataDir } from './lib/argent-datadir.mjs'
|
|
30
|
+
|
|
31
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
32
|
+
const REPO = resolve(HERE, '..')
|
|
33
|
+
const argv = process.argv.slice(2)
|
|
34
|
+
const argVal = (n, d) => { const i = argv.indexOf(n); return i > -1 ? argv[i + 1] : d }
|
|
35
|
+
const APPLY = argv.includes('--apply')
|
|
36
|
+
const ENV_ONLY = argv.includes('--env-only') // 只做 env 键迁移(gateway.json 已迁过时用)
|
|
37
|
+
const SPEC = argVal('--spec', join(REPO, 'deploy', 'customer', 'roles.spec.json'))
|
|
38
|
+
const DATA = argVal('--data-dir', resolveDataDir({ env: process.env }).dir) // 缺省 = 内核三级链
|
|
39
|
+
|
|
40
|
+
const KEEP_FROM_EXISTING = ['botAppId', 'botSecret', 'chatId'] // 机器/凭据相关:按角色名保留
|
|
41
|
+
const log = (m) => console.log(m)
|
|
42
|
+
function fail(m) { console.error(`✗ ${m}`); process.exit(2) }
|
|
43
|
+
|
|
44
|
+
const { listLarkProfiles, findBaseReader, parseEnvKeys, applyEnvKeys, realRun } = await import('./lib/customer-env.mjs')
|
|
45
|
+
|
|
46
|
+
if (ENV_ONLY) {
|
|
47
|
+
migrateEnvKeys()
|
|
48
|
+
process.exit(0)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!existsSync(SPEC)) fail(`找不到结构模板:${SPEC}(客户机需先 git pull 到含 deploy/customer/ 的版本)`)
|
|
52
|
+
const spec = JSON.parse(readFileSync(SPEC, 'utf8'))
|
|
53
|
+
const specRoles = spec.roles || []
|
|
54
|
+
if (!specRoles.length) fail('模板里没有 roles')
|
|
55
|
+
|
|
56
|
+
const gwPath = join(DATA, 'gateway.json')
|
|
57
|
+
let cur = { roles: [] }
|
|
58
|
+
if (existsSync(gwPath)) {
|
|
59
|
+
try { cur = JSON.parse(readFileSync(gwPath, 'utf8')) } catch (e) { fail(`现有 gateway.json 解析失败:${e.message}`) }
|
|
60
|
+
} else {
|
|
61
|
+
log(` ⚠️ 未找到现有 ${gwPath} —— 将按模板从零生成(所有角色走主 Bot 兜底)`)
|
|
62
|
+
}
|
|
63
|
+
const curByRole = new Map((cur.roles || []).map((r) => [r.role, r]))
|
|
64
|
+
|
|
65
|
+
log(` ── 客户机配置迁移(${APPLY ? '落盘' : 'dry-run'})──`)
|
|
66
|
+
log(` 模板 ${specRoles.length} 个角色|现有 ${curByRole.size} 个角色|数据目录 ${DATA}`)
|
|
67
|
+
|
|
68
|
+
// --only <role,role,...>:只保留**本客户实际要跑**的角色。
|
|
69
|
+
// 由来(2026-09-28 P10):模板是"全角色舰隊"版(pet-dev / lktop-pm / hr-office… 客户用不到);
|
|
70
|
+
// 全量灌进客户 gateway.json 会给每个角色建一条 Bot 路由(缺凭据的走主 Bot 兜底),
|
|
71
|
+
// 客户群里可能被无关角色回话。按流程注册表声明的角色裁剪更稳。
|
|
72
|
+
const onlyArg = (argVal('--only', '') || '').split(',').map((x) => x.trim()).filter(Boolean)
|
|
73
|
+
const wanted = onlyArg.length ? specRoles.filter((r) => onlyArg.includes(r.role)) : specRoles
|
|
74
|
+
const droppedByFilter = onlyArg.length ? specRoles.filter((r) => !onlyArg.includes(r.role)).map((r) => r.role) : []
|
|
75
|
+
if (onlyArg.length) {
|
|
76
|
+
const missing = onlyArg.filter((r) => !specRoles.some((s2) => s2.role === r))
|
|
77
|
+
if (missing.length) fail(`--only 里的角色不在模板中:${missing.join(', ')}`)
|
|
78
|
+
log(` · --only 裁剪:保留 ${wanted.length} 个(${wanted.map((r) => r.role).join(', ')})`)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const out = []
|
|
82
|
+
const kept = [], fallback = [], added = []
|
|
83
|
+
for (const s of wanted) {
|
|
84
|
+
const prev = curByRole.get(s.role)
|
|
85
|
+
const merged = { ...s }
|
|
86
|
+
for (const k of KEEP_FROM_EXISTING) {
|
|
87
|
+
if (prev && prev[k]) merged[k] = prev[k]
|
|
88
|
+
}
|
|
89
|
+
if (prev && prev.botAppId) kept.push(s.role)
|
|
90
|
+
else if (!prev) added.push(s.role)
|
|
91
|
+
else fallback.push(s.role)
|
|
92
|
+
out.push(merged)
|
|
93
|
+
}
|
|
94
|
+
const droppedRoles = [...curByRole.keys()].filter((r) => !wanted.some((s) => s.role === r))
|
|
95
|
+
|
|
96
|
+
log(` · 保留现有 Bot 凭据:${kept.length ? kept.join(', ') : '(无)'}`)
|
|
97
|
+
log(` · 主 Bot 兜底(未配 bot):${fallback.length ? fallback.join(', ') : '(无)'}`)
|
|
98
|
+
log(` · 新增角色(原配置里没有):${added.length ? added.join(', ') : '(无)'}`)
|
|
99
|
+
if (droppedRoles.length) log(` · 将被移除的角色:${droppedRoles.join(', ')}(新架构已退役/本次未选)`)
|
|
100
|
+
const models = new Set(out.map((r) => r.model || '(继承默认)'))
|
|
101
|
+
log(` · 角色模型:${[...models].join(', ')}`)
|
|
102
|
+
const missingProfiles = [...new Set(out.map((r) => r.larkProfile).filter(Boolean))]
|
|
103
|
+
log(` · larkProfile 引用:${missingProfiles.join(', ') || '(无)'}(需在客户机 lark-cli 里已配置)`)
|
|
104
|
+
|
|
105
|
+
const next = { ...cur, roles: out }
|
|
106
|
+
const body = JSON.stringify(next, null, 2) + '\n'
|
|
107
|
+
|
|
108
|
+
if (!APPLY) {
|
|
109
|
+
log(` [dry-run] 将写入 ${gwPath}(${body.length} bytes);加 --apply 落盘`)
|
|
110
|
+
log(` [dry-run] roles.json 请随后用:node scripts/check-role-mapping.mjs --write`)
|
|
111
|
+
migrateEnvKeys()
|
|
112
|
+
process.exit(0)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const ts = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14)
|
|
116
|
+
if (existsSync(gwPath)) {
|
|
117
|
+
const bak = `${gwPath}.bak-${ts}`
|
|
118
|
+
copyFileSync(gwPath, bak)
|
|
119
|
+
log(` ✅ 已备份 ${bak}`)
|
|
120
|
+
}
|
|
121
|
+
writeFileSync(gwPath, body, 'utf8')
|
|
122
|
+
log(` ✅ 已写入 ${gwPath}(${out.length} 个角色)`)
|
|
123
|
+
log(' 下一步:node scripts/check-role-mapping.mjs --write && node scripts/check-role-mapping.mjs')
|
|
124
|
+
log(' ⚠️ 网关会监听该文件热重载;若角色数变化后行为异常,重启 dsh web。')
|
|
125
|
+
migrateEnvKeys()
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* env 键迁移:把流程部署文件(fulfillment.env)里**缺的**键按**实测**补齐。
|
|
129
|
+
*
|
|
130
|
+
* 为什么不写死取值:各机器 lark-cli 的 profile 命名不一致(开发机 `lip-customer`,
|
|
131
|
+
* 客户机用 appId `cli_aaf7cafb66b85ccc`),且**同名不同 app**:客户机的 `ml-fulfillment`
|
|
132
|
+
* 指向缺 `base:record:read` 的 app(99991672)—— 只能真读一次表才知道谁能用。
|
|
133
|
+
* 探测只读(1 条记录);取值命中不了就**不写**、只报缺口,绝不猜。
|
|
134
|
+
*/
|
|
135
|
+
function migrateEnvKeys() {
|
|
136
|
+
// P2a:env 规范位置是 `verticals/<id>/env/`(旧平铺路径保留给已装机器)。
|
|
137
|
+
// 规则:规范目录存在 ⇒ 写规范位置;否则沿用平铺(老机器零改动)。
|
|
138
|
+
const envName = 'fulfillment.env'
|
|
139
|
+
// P2a:env 规范位置从**注册表声明**派生(引擎里不写业务线名 —— vertical-lint 会拦「引擎不掺业务」)。
|
|
140
|
+
// 规则:注册表里哪条流程声明了这个 env 文件,就用它的目录;规范位置存在就写那儿,否则沿用平铺。
|
|
141
|
+
const _doc = loadRegistryDoc()
|
|
142
|
+
const _declared = Object.values(_doc.flows || {})
|
|
143
|
+
.map((f) => String((f.source || {}).envFile || ''))
|
|
144
|
+
.find((rel) => rel.endsWith(`/${envName}`))
|
|
145
|
+
const _relDir = _declared ? _declared.slice(0, _declared.lastIndexOf('/')) : ''
|
|
146
|
+
const envPath = (_relDir && existsSync(join(DATA, _relDir)))
|
|
147
|
+
? join(DATA, _relDir, envName)
|
|
148
|
+
: join(DATA, envName)
|
|
149
|
+
log('\n── env 键迁移(流程读客户 Base 的身份)──')
|
|
150
|
+
if (!existsSync(envPath)) { log(` ⚠️ 找不到 ${envPath},跳过`); return }
|
|
151
|
+
const before = readFileSync(envPath, 'utf8')
|
|
152
|
+
const keys = parseEnvKeys(before)
|
|
153
|
+
const mode = (process.env.FULL_TABLE_MODE || keys.FULL_TABLE_MODE || 'lip').trim()
|
|
154
|
+
// P2a:经加载器合并(base + 业务线)
|
|
155
|
+
const req = loadRegistryDoc()
|
|
156
|
+
const variant = ((req.flows?.full?.source?.variants) || {})[mode] || {}
|
|
157
|
+
const baseToken = process.env[variant.baseEnv || ''] || keys[variant.baseEnv || ''] || keys.BASE_TOKEN
|
|
158
|
+
const tableId = process.env[variant.tableEnv || ''] || keys[variant.tableEnv || ''] || keys.TABLE_ID
|
|
159
|
+
log(` · 模式 ${mode}:base=${(baseToken || '(缺)').slice(0, 12)}… table=${tableId || '(缺)'}`)
|
|
160
|
+
if (!baseToken || !tableId) { log(' ⚠️ base/table 未解析(先补 FULL_BASE_TOKEN / FULL_TABLE_ID),跳过身份探测'); return }
|
|
161
|
+
|
|
162
|
+
const profiles = listLarkProfiles(realRun)
|
|
163
|
+
log(` · 候选身份 ${profiles.length} 个:${profiles.join(', ') || '(无)'}`)
|
|
164
|
+
const found = findBaseReader({ run: realRun, baseToken, tableId, profiles })
|
|
165
|
+
for (const t of found.tried) {
|
|
166
|
+
log(` ${t.ok ? '✅' : '✗'} ${t.profile}${t.ok ? '' : `(${t.code || ''} ${t.msg || ''})`}`)
|
|
167
|
+
}
|
|
168
|
+
const patch = { FULL_LARK_PROFILE: found.profile, FULL_NOTIFY_MODE: 'final' }
|
|
169
|
+
if (!found.profile) log(' ❌ 没有任何身份能读该表 —— 需先在开放平台给某个 app 申请 base:record:read 并加进该 Base')
|
|
170
|
+
const res = applyEnvKeys(before, patch, { force: argv.includes('--force') })
|
|
171
|
+
for (const k of res.added) log(` ${APPLY ? '✅ 已写' : '[dry-run] 将写'} ${k}=${patch[k]}`)
|
|
172
|
+
for (const s2 of res.skipped) log(` · 跳过 ${s2.key}:${s2.reason}`)
|
|
173
|
+
if (!res.added.length) { log(' · env 无改动'); return }
|
|
174
|
+
if (!APPLY) { log(' [dry-run] 加 --apply 落盘(自动备份,保持 600)'); return }
|
|
175
|
+
const bak = `${envPath}.bak-${new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14)}`
|
|
176
|
+
copyFileSync(envPath, bak)
|
|
177
|
+
writeFileSync(envPath, res.text, { encoding: 'utf8', mode: 0o600 })
|
|
178
|
+
log(` ✅ 已写入 ${envPath}(备份 ${bak.split('/').pop()})`)
|
|
179
|
+
}
|