@whyshu/argent-engine 0.0.0-stage → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANIFEST.json +307 -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 +575 -0
- package/engine/scripts/billing-mode-daily.sh +12 -0
- package/engine/scripts/build-engine-package.mjs +123 -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 +377 -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/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 +134 -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 +46 -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 +260 -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/fix-skill-frontmatter.py +209 -0
- package/engine/scripts/flow-auto-retry.py +454 -0
- package/engine/scripts/flow-requirements.json +720 -0
- package/engine/scripts/flow-scaffold.py +275 -0
- package/engine/scripts/flow-timers-plist.sh +86 -0
- package/engine/scripts/flow-timers.sh +182 -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 +1024 -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 +2275 -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 +194 -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.py +262 -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 +293 -0
- package/engine/scripts/poll-invoice.sh +74 -0
- package/engine/scripts/profile-doctor.mjs +686 -0
- package/engine/scripts/provider-select.sh +70 -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/run-tests.sh +256 -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 +136 -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 +73 -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 +34 -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 +216 -0
- package/engine/scripts/ziniao-post-upgrade-check.sh +120 -0
- package/engine/scripts/ziniao-windows.sh +153 -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 +706 -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 +21 -4
- package/README.md +0 -3
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-billing-mode.sh —— **provider 装配巡检**(只读报告;2026-10-02 重做判据与文案)
|
|
3
|
+
#
|
|
4
|
+
# ⚠️ 文件名保留 `check-billing-mode.sh`(不是"还在用计费模式"):LaunchAgent
|
|
5
|
+
# `com.whyshu.billing-mode-check` 的 plist 与 `scripts/billing-mode-daily.sh` 指的就是这个路径,
|
|
6
|
+
# 改名要同时动 plist/定时器 —— 不值当。文件名的 "billing-mode" 只是历史遗留。
|
|
7
|
+
#
|
|
8
|
+
# ## 历史:旧「计费模式」机制**已移除**
|
|
9
|
+
# 曾经的做法是"切模式"= 改写 `llm-deepseek.baseURL`(官方直连 ↔ 问述代理二选一)——那等于
|
|
10
|
+
# **劫持官方直连 provider**(provider id 仍叫 `deepseek-official`,实际指向问述),用户明确禁止。
|
|
11
|
+
# 现在的形态是:**两个独立 provider 并存**,用户在 dsh 输入框旁的**模型选择器**里选:
|
|
12
|
+
# · `llm-deepseek` 实例(`@deepseek-ai/dsh-llm-deepseek-api-key`)→ provider `deepseek-official`
|
|
13
|
+
# = 真·官方直连(`https://api.deepseek.com` + `DEEPSEEK_API_KEY`,不扣积分)
|
|
14
|
+
# · `llm-whyshu` 实例(`argent-core/llm-whyshu`)→ provider `whyshu`
|
|
15
|
+
# = 问述代理(`https://whyshu.com/api/argent/v1` + `WHYSHU_API_KEY`,扣积分)
|
|
16
|
+
# ⇒ **"某台机器只装官方直连"是合法配置,不是装配损坏**。
|
|
17
|
+
#
|
|
18
|
+
# ## 判据(2026-10-02 重做:**期望值由组合声明推导**,不写死任何 provider 名单)
|
|
19
|
+
# 声明(declared):`~/.dsh/profiles/<profile>/cordis.patch.yml` 里**有**该 id 的条目
|
|
20
|
+
# (普通条目**或** `- insert:` 块里的条目都算)⇒ **才期望它装上**;
|
|
21
|
+
# 没有 ⇒ 未装配属**正常**(如"没登录问述",按设计),**不算缺失**。
|
|
22
|
+
# 在位(mounted) :`dsh --profile <p> --dump-config` 的**组合树**里有该 id(权威判据,来源标 composed)。
|
|
23
|
+
# 基础组合(`@deepseek-ai/dsh-base` 之类)提供的实例也在这里出现。
|
|
24
|
+
# 四态:
|
|
25
|
+
# declared ∧ mounted ⇒ ✓ 在位
|
|
26
|
+
# declared ∧ ¬mounted(组合树读到了)⇒ ✗ **组合声明了但未装配** ⇒ exit 1(唯一的硬失败)
|
|
27
|
+
# ¬declared ∧ mounted ⇒ ✓ 在位(基础组合提供,补丁未声明 —— 合法)
|
|
28
|
+
# ¬declared ∧ ¬mounted ⇒ – 未装配(按设计;**不算缺失**)⇒ exit 0
|
|
29
|
+
# declared ∧ 组合树读不到 ⇒ ⚠ 未确认(**不敢说"没装上"**)⇒ exit 2
|
|
30
|
+
# ¬declared ∧ 组合树读不到 ⇒ – 未声明(什么都没声明 ⇒ 没什么要验的)⇒ exit 0
|
|
31
|
+
#
|
|
32
|
+
# 用法:bash scripts/check-billing-mode.sh [--json]
|
|
33
|
+
# 环境变量(排障/测试用,全是只读输入):
|
|
34
|
+
# ARGENT_CORDIS_PATCH 组合补丁路径(默认 ~/.dsh/profiles/web/cordis.patch.yml)
|
|
35
|
+
# SETTINGS 设置文件路径(默认 ~/.dsh/settings.yaml)
|
|
36
|
+
# ARGENT_PROFILE profile 名(默认 = 组合补丁所在目录名,如 web)
|
|
37
|
+
# ARGENT_DSH_BIN dsh 可执行文件(默认 PATH 里的 dsh;测试可指向假桩,不真起 dsh)
|
|
38
|
+
# ARGENT_DUMP_TIMEOUT dump-config 超时秒数(默认 20)
|
|
39
|
+
# 退出码:0=合法配置(声明的都在位;未声明的按设计视为不装);
|
|
40
|
+
# 1=**组合声明了却没装上**(唯一硬失败);
|
|
41
|
+
# 2=无法判定(声明了但组合树读不到:dsh 缺失/超时/输出不可解析)——"未确认",不是"坏了";
|
|
42
|
+
# 3=调用了写意图参数(拒绝)
|
|
43
|
+
#
|
|
44
|
+
# ## ⛔ 只读熔断(2026-10-01 加固,任务「--fix 死代码」)
|
|
45
|
+
# 本脚本**没有任何写路径**:全文只有 yaml 读两个文件 + 把 `--dump-config` 的**输出经管道读进内存**
|
|
46
|
+
# (**不落任何临时文件**),直接 `exit`;不 open(…,'w')、不调用任何会落盘的命令。
|
|
47
|
+
# 为把这条性质变成"结构上不可能"而不是"靠人记住",这里加**硬熔断**:
|
|
48
|
+
# 任何写意图参数(--fix/--apply/--write/--set/--mode/--provider/--force)⇒ **在读任何文件之前**
|
|
49
|
+
# 就拒绝并 `exit 3`。历史教训:旧版 `--fix` 会改写 `llm-deepseek.baseURL` = **劫持官方直连 provider**
|
|
50
|
+
# (provider id 仍叫 deepseek-official,实际指向问述)—— 用户明确禁止(见 AGENTS.md「LLM provider 设计」
|
|
51
|
+
# 与「不写 ~/.dsh/settings.yaml」)。改 provider 请用输入框旁的**模型选择器**,或
|
|
52
|
+
# `bash scripts/provider-select.sh {status|whyshu|deepseek}`(那条路才是有意为之的写入者)。
|
|
53
|
+
#
|
|
54
|
+
# ## 历史判据备注(保留供排障)
|
|
55
|
+
# · 2026-10-01:客户机上 `llm-deepseek` 由**基础组合**提供、补丁里根本没有它 ⇒ 旧版"只扫补丁"
|
|
56
|
+
# 会误报缺失。修法 = 以**组合树**为首选判据(来源标 `composed`),补丁扫描只作回退。
|
|
57
|
+
# · 2026-09-30:组合补丁里**新增实例的规范写法是 `- insert:` 块**(普通条目是"按 id 覆盖已有条目",
|
|
58
|
+
# 写错了会被静默跳过)⇒ 声明扫描必须**两个位置都看**(顶层 + insert 块)。
|
|
59
|
+
set -uo pipefail
|
|
60
|
+
|
|
61
|
+
# ── ⛔ 写操作熔断:必须在读任何文件之前(放最前面,与 REPO/PATCH/SETTINGS 的求值无关)──
|
|
62
|
+
# ⚠️ `$arg` 后面紧跟中文字符时必须写 `${arg}`:bash 在 CJK locale 下会把全角字符当标识符字符
|
|
63
|
+
# (仓库门禁 scripts/tests/shell-cjk-var.test.py 就是为这个坑立的)。
|
|
64
|
+
for arg in "$@"; do
|
|
65
|
+
case "$arg" in
|
|
66
|
+
--fix|--apply|--write|--set|--set-mode|--mode|--provider|--force)
|
|
67
|
+
echo "⛔ check-billing-mode.sh 是**只读**报告:不接受 ${arg},也不会改任何文件。" >&2
|
|
68
|
+
echo " 改 provider:用 dsh 输入框旁的**模型选择器**," >&2
|
|
69
|
+
echo " 或 bash scripts/provider-select.sh {status|whyshu|deepseek}(改完需重启 dsh)。" >&2
|
|
70
|
+
echo " ⛔ 本脚本绝不写 ~/.dsh/settings.yaml,也不改写官方 provider 的 baseURL。" >&2
|
|
71
|
+
exit 3
|
|
72
|
+
;;
|
|
73
|
+
esac
|
|
74
|
+
done
|
|
75
|
+
|
|
76
|
+
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
77
|
+
PY="${ARGENT_PY:-$HOME/.dsh/argent/venv/bin/python3}"; [ -x "$PY" ] || PY=python3
|
|
78
|
+
PATCH="${ARGENT_CORDIS_PATCH:-$HOME/.dsh/profiles/web/cordis.patch.yml}"
|
|
79
|
+
SETTINGS="${SETTINGS:-$HOME/.dsh/settings.yaml}"
|
|
80
|
+
# profile 名:优先显式给定,否则取组合补丁所在目录名(~/.dsh/profiles/<p>/cordis.patch.yml ⇒ <p>)
|
|
81
|
+
PROFILE="${ARGENT_PROFILE:-$(basename "$(dirname "$PATCH")")}"
|
|
82
|
+
DSH_BIN="${ARGENT_DSH_BIN:-dsh}"
|
|
83
|
+
DUMP_TIMEOUT="${ARGENT_DUMP_TIMEOUT:-20}"
|
|
84
|
+
|
|
85
|
+
# ── 权威判据:组合后的树(`dsh --profile <p> --dump-config`)────────────────
|
|
86
|
+
# 它才是 dsh 真正加载的那份(基础组合 + 补丁 overlay 的结果);基础组合提供的实例(补丁里根本没有)
|
|
87
|
+
# 也能在这里看到。dsh 不可用/超时/输出不可解析 ⇒ 组合树"读不到",由 python 侧按状态区分。
|
|
88
|
+
# 可移植超时:本机 macOS **没有** `timeout`/`gtimeout` ⇒ 纯 shell 实现(后台 + 看门狗 + kill),
|
|
89
|
+
# 且**不落任何临时文件**(stdout 直接进命令替换的内存变量)。
|
|
90
|
+
_run_dump_config() { # $1=dsh 可执行 $2=profile $3=超时秒;成功:stdout=组合树文本
|
|
91
|
+
local bin="$1" prof="$2" secs="$3" pid watcher rc
|
|
92
|
+
"$bin" --profile "$prof" --dump-config 2>/dev/null &
|
|
93
|
+
pid=$!
|
|
94
|
+
# ⚠️ 看门狗必须把 stdout/stderr 重定向到 /dev/null:否则它(及其 `sleep` 子进程)会**继续持有
|
|
95
|
+
# 命令替换的管道** ⇒ 即使主命令早已结束,`$( )` 也要等到 `sleep` 自然醒来才拿到 EOF
|
|
96
|
+
# (实测:每次调用白等满 DUMP_TIMEOUT)。
|
|
97
|
+
( sleep "$secs"; kill -0 "$pid" 2>/dev/null && kill -9 "$pid" 2>/dev/null ) >/dev/null 2>&1 &
|
|
98
|
+
watcher=$!
|
|
99
|
+
wait "$pid"; rc=$?
|
|
100
|
+
kill "$watcher" 2>/dev/null
|
|
101
|
+
wait "$watcher" 2>/dev/null
|
|
102
|
+
return $rc
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
dump_text=""; dump_status="unavailable"; dump_rc=0
|
|
106
|
+
if command -v "$DSH_BIN" >/dev/null 2>&1; then
|
|
107
|
+
dump_text=$(_run_dump_config "$DSH_BIN" "$PROFILE" "$DUMP_TIMEOUT") || dump_rc=$?
|
|
108
|
+
if [ "$dump_rc" -eq 0 ] && [ -n "$dump_text" ]; then
|
|
109
|
+
dump_status="ok"
|
|
110
|
+
elif [ "$dump_rc" -eq 137 ]; then
|
|
111
|
+
dump_status="timeout"; dump_text=""
|
|
112
|
+
else
|
|
113
|
+
dump_status="failed"; dump_text=""
|
|
114
|
+
fi
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# 组合树文本经 **stdin** 交给 python(不写临时文件);状态经 argv 传递。
|
|
118
|
+
PY_SRC=$(cat <<'PY'
|
|
119
|
+
import sys, yaml, json, pathlib
|
|
120
|
+
|
|
121
|
+
patch = sys.argv[1]
|
|
122
|
+
settings = sys.argv[2]
|
|
123
|
+
fmt = sys.argv[3] if len(sys.argv) > 3 else ''
|
|
124
|
+
dump_status = sys.argv[4] if len(sys.argv) > 4 else 'unavailable'
|
|
125
|
+
profile = sys.argv[5] if len(sys.argv) > 5 else ''
|
|
126
|
+
dsh_bin = sys.argv[6] if len(sys.argv) > 6 else 'dsh'
|
|
127
|
+
dump_text = sys.stdin.read() if dump_status == 'ok' else ''
|
|
128
|
+
|
|
129
|
+
# ── YAML:容忍 `!!js` 等自定义标签 ──────────────────────────────────────────
|
|
130
|
+
# 组合树里合法地存在 `disabled: !!js '…'`、`root: !!js dshHomePath('sessions')`;
|
|
131
|
+
# yaml.safe_load 遇到未知标签直接抛错 ⇒ 会被误当成"组合树读不到"。这里只把未知标签
|
|
132
|
+
# 按其**字面内容**收下(本脚本只用 id/name/config,不执行任何表达式)。
|
|
133
|
+
class _Tolerant(yaml.SafeLoader):
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
def _construct_unknown(loader, tag_suffix, node):
|
|
137
|
+
if isinstance(node, yaml.ScalarNode):
|
|
138
|
+
return loader.construct_scalar(node)
|
|
139
|
+
if isinstance(node, yaml.SequenceNode):
|
|
140
|
+
return loader.construct_sequence(node, deep=True)
|
|
141
|
+
return loader.construct_mapping(node, deep=True)
|
|
142
|
+
|
|
143
|
+
_Tolerant.add_multi_constructor(None, _construct_unknown)
|
|
144
|
+
|
|
145
|
+
def load_yaml(text):
|
|
146
|
+
return yaml.load(text, Loader=_Tolerant)
|
|
147
|
+
|
|
148
|
+
# 本脚本认识的 provider 实例(**只用于给出可读的说明文案**,不参与"该不该装"的判断——
|
|
149
|
+
# 该不该装一律由组合声明推导)。其余 `llm-*` 条目照样按同一套判据巡。
|
|
150
|
+
PROVIDERS = {
|
|
151
|
+
'llm-deepseek': '官方直连',
|
|
152
|
+
'llm-whyshu': '问述代理',
|
|
153
|
+
}
|
|
154
|
+
# 官方直连的正式端点(事实常量,不是"期望清单"):baseURL 等于它 = 与不写 baseURL 等价 ⇒ 不告警。
|
|
155
|
+
OFFICIAL_DEEPSEEK_BASE = 'api.deepseek.com'
|
|
156
|
+
|
|
157
|
+
# ── 判据①(权威):组合后的树里找 `id:` 条目 ────────────────────────────────
|
|
158
|
+
composed, composed_note = {}, ''
|
|
159
|
+
|
|
160
|
+
def _walk(node):
|
|
161
|
+
if isinstance(node, dict):
|
|
162
|
+
i = node.get('id')
|
|
163
|
+
if isinstance(i, str) and i.strip():
|
|
164
|
+
composed.setdefault(i.strip(), node) # 先出现者优先(贴近 dsh 的合并顺序)
|
|
165
|
+
for k, v in node.items():
|
|
166
|
+
if k == 'config':
|
|
167
|
+
continue # config 里的 `models[].id` 是模型 id,不是条目 id,别误收
|
|
168
|
+
_walk(v)
|
|
169
|
+
elif isinstance(node, list):
|
|
170
|
+
for v in node:
|
|
171
|
+
_walk(v)
|
|
172
|
+
|
|
173
|
+
if dump_status == 'ok' and dump_text.strip():
|
|
174
|
+
try:
|
|
175
|
+
_walk(load_yaml(dump_text))
|
|
176
|
+
if composed:
|
|
177
|
+
composed_note = f"{len(composed)} 条条目"
|
|
178
|
+
else:
|
|
179
|
+
composed_note = '组合树里没有任何 id 条目(形状异常)'
|
|
180
|
+
composed = {}
|
|
181
|
+
except Exception as e:
|
|
182
|
+
composed = {}
|
|
183
|
+
composed_note = f'组合树解析失败:{e}'
|
|
184
|
+
elif dump_status == 'ok':
|
|
185
|
+
composed_note = '组合树输出为空'
|
|
186
|
+
else:
|
|
187
|
+
composed_note = {'unavailable': 'dsh 不可用(PATH / ARGENT_DSH_BIN 里找不到)',
|
|
188
|
+
'timeout': 'dsh --dump-config 超时',
|
|
189
|
+
'failed': 'dsh --dump-config 执行失败(非 0 退出)'}.get(dump_status, f'未知状态 {dump_status}')
|
|
190
|
+
composed_ok = bool(composed)
|
|
191
|
+
|
|
192
|
+
# ── 判据②:组合**声明**扫描(补丁顶层条目 + `- insert:` 块)—— 期望值的唯一来源 ──
|
|
193
|
+
ok_yaml = True
|
|
194
|
+
doc = []
|
|
195
|
+
try:
|
|
196
|
+
doc = load_yaml(pathlib.Path(patch).read_text(encoding='utf-8')) or []
|
|
197
|
+
except Exception as e:
|
|
198
|
+
ok_yaml = False
|
|
199
|
+
print(f" ⚠️ 组合补丁读取失败(无法推导期望值):{e}")
|
|
200
|
+
if ok_yaml and not isinstance(doc, list):
|
|
201
|
+
ok_yaml = False
|
|
202
|
+
print(" ⚠️ 组合补丁顶层不是列表(形状异常;无法推导期望值)")
|
|
203
|
+
|
|
204
|
+
# 顶层同名条目优先(它才是"覆盖已有条目"的那份),但两个来源都记下来供排障。
|
|
205
|
+
index, sources = {}, {}
|
|
206
|
+
if ok_yaml:
|
|
207
|
+
for e in doc:
|
|
208
|
+
if not isinstance(e, dict):
|
|
209
|
+
continue
|
|
210
|
+
if e.get('id'):
|
|
211
|
+
index.setdefault(str(e['id']), e)
|
|
212
|
+
sources.setdefault(str(e['id']), 'top')
|
|
213
|
+
block = e.get('insert')
|
|
214
|
+
if isinstance(block, list):
|
|
215
|
+
for item in block:
|
|
216
|
+
if isinstance(item, dict) and item.get('id'):
|
|
217
|
+
k = str(item['id'])
|
|
218
|
+
index.setdefault(k, item)
|
|
219
|
+
sources.setdefault(k, 'insert')
|
|
220
|
+
|
|
221
|
+
def entry(eid):
|
|
222
|
+
return index.get(eid)
|
|
223
|
+
|
|
224
|
+
# ── 补充来源:`~/.dsh/settings.yaml` 顶层同名段(键 = 实例 id)────────────────
|
|
225
|
+
# 历史「计费模式」的**改写落点**就在这里(`llm-deepseek: {baseURL: …}`)⇒ 仍要读一眼,
|
|
226
|
+
# 免得只在 settings.yaml 里留着的改写看不见。**只是补充**:组合树(dsh 真正加载的那份)优先。
|
|
227
|
+
settings_cfg = {}
|
|
228
|
+
try:
|
|
229
|
+
_s = load_yaml(pathlib.Path(settings).read_text(encoding='utf-8')) or {}
|
|
230
|
+
if isinstance(_s, dict):
|
|
231
|
+
settings_cfg = _s
|
|
232
|
+
except Exception:
|
|
233
|
+
settings_cfg = {}
|
|
234
|
+
|
|
235
|
+
# ── 当前会话/默认 provider(真源 = dsh 的 `agent-default-model`;来源照旧标注)──
|
|
236
|
+
default, default_src = '', ''
|
|
237
|
+
for name, cfgv in (('composed', (composed.get('agent-default-model') or {}).get('config')),
|
|
238
|
+
('patch', (entry('agent-default-model') or {}).get('config'))):
|
|
239
|
+
if isinstance(cfgv, dict) and str(cfgv.get('provider') or '').strip():
|
|
240
|
+
default, default_src = str(cfgv['provider']).strip(), name
|
|
241
|
+
break
|
|
242
|
+
default_note = {'composed': 'composed(dsh 组合树里的 agent-default-model)',
|
|
243
|
+
'patch': '组合补丁(顶层条目 agent-default-model)'}.get(default_src, '未读到(不猜)')
|
|
244
|
+
|
|
245
|
+
# ── 巡哪些 provider:认识的两位 + 组合里**声明**过的任何 `llm-*` ──────────────
|
|
246
|
+
declared = {k: v for k, v in sources.items() if k in PROVIDERS or k.startswith('llm-')}
|
|
247
|
+
watch = sorted(set(PROVIDERS) | set(declared))
|
|
248
|
+
|
|
249
|
+
inst, missing, unconfirmed, not_declared = {}, [], [], []
|
|
250
|
+
for pid in watch:
|
|
251
|
+
decl = pid in declared
|
|
252
|
+
mnt = pid in composed
|
|
253
|
+
c = composed.get(pid) or {}
|
|
254
|
+
p = entry(pid) or {}
|
|
255
|
+
ccfg, pcfg = (c.get('config') or {}), (p.get('config') or {})
|
|
256
|
+
scfg = settings_cfg.get(pid) or {}
|
|
257
|
+
if not isinstance(scfg, dict):
|
|
258
|
+
scfg = {}
|
|
259
|
+
# baseURL/apiKeyEnv:组合树(权威,含基础组合的 config)→ 补丁条目 → settings.yaml 同名段
|
|
260
|
+
base = ccfg.get('baseURL') or pcfg.get('baseURL') or scfg.get('baseURL') or ''
|
|
261
|
+
akey = ccfg.get('apiKeyEnv') or pcfg.get('apiKeyEnv') or scfg.get('apiKeyEnv') or ''
|
|
262
|
+
disabled = bool(c.get('disabled') if c else p.get('disabled'))
|
|
263
|
+
# 提示(**不改退出码**):`llm-deepseek` 带 baseURL = 官方直连被改指 —— 那正是历史上
|
|
264
|
+
# 「计费模式」切路由留下的**劫持形态**(provider id 仍叫 deepseek-official,实际指向别处)。
|
|
265
|
+
# 合规的官方直连应**无** baseURL、key 用 DEEPSEEK_API_KEY。这里只如实标注,不据此判失败
|
|
266
|
+
# ("声明了却没装上"才是唯一的硬失败)。
|
|
267
|
+
warn = ''
|
|
268
|
+
if pid == 'llm-deepseek' and base and OFFICIAL_DEEPSEEK_BASE not in base:
|
|
269
|
+
warn = ('⚠️ 带 baseURL ⇒ **不是**官方直连(历史「计费模式」改写形态;'
|
|
270
|
+
'官方直连应无 baseURL、key 用 DEEPSEEK_API_KEY)')
|
|
271
|
+
if decl and mnt:
|
|
272
|
+
state = 'ok'
|
|
273
|
+
elif decl and not composed_ok:
|
|
274
|
+
state = 'unconfirmed'
|
|
275
|
+
elif decl:
|
|
276
|
+
state = 'missing'
|
|
277
|
+
elif mnt:
|
|
278
|
+
state = 'ok_base'
|
|
279
|
+
else:
|
|
280
|
+
state = 'absent'
|
|
281
|
+
inst[pid] = {
|
|
282
|
+
'declared': decl,
|
|
283
|
+
'declaredFrom': declared.get(pid, ''),
|
|
284
|
+
'mounted': mnt,
|
|
285
|
+
'from': 'composed' if mnt else (declared.get(pid, '') if decl else ''),
|
|
286
|
+
'plugin': c.get('name', '') or p.get('name', ''),
|
|
287
|
+
'baseURL': base, 'apiKeyEnv': akey,
|
|
288
|
+
'disabled': disabled, 'state': state, 'warning': warn,
|
|
289
|
+
}
|
|
290
|
+
if state == 'missing':
|
|
291
|
+
missing.append(pid)
|
|
292
|
+
elif state == 'unconfirmed':
|
|
293
|
+
unconfirmed.append(pid)
|
|
294
|
+
elif state == 'absent':
|
|
295
|
+
not_declared.append(pid)
|
|
296
|
+
|
|
297
|
+
verdict = 'missing' if missing else ('unconfirmed' if unconfirmed else 'ok')
|
|
298
|
+
|
|
299
|
+
# ── 输出 ────────────────────────────────────────────────────────────────────
|
|
300
|
+
_MODE = {'whyshu': '问述代理,扣积分', 'deepseek-official': '官方直连,不扣积分'}
|
|
301
|
+
mode = f"{default}({_MODE[default]})" if default in _MODE else f"未设置({default or '空'})"
|
|
302
|
+
|
|
303
|
+
if fmt == '--json':
|
|
304
|
+
print(json.dumps({'defaultProvider': default, 'defaultProviderSource': default_src,
|
|
305
|
+
'mode': mode, 'profile': profile,
|
|
306
|
+
'composed': {'ok': composed_ok, 'status': dump_status, 'note': composed_note},
|
|
307
|
+
'declared': declared, 'instances': inst,
|
|
308
|
+
'missing': missing, 'unconfirmed': unconfirmed, 'notDeclared': not_declared,
|
|
309
|
+
'warnings': [f"{k}: {v['warning']}" for k, v in inst.items() if v['warning']],
|
|
310
|
+
'verdict': verdict, 'patchReadable': ok_yaml},
|
|
311
|
+
ensure_ascii=False, indent=2))
|
|
312
|
+
else:
|
|
313
|
+
print(" provider 装配巡检(只读)——两个独立 provider 并存(deepseek-official 官方直连 / whyshu 问述代理),用户在模型选择器里选")
|
|
314
|
+
print(f" 当前默认 provider:{mode}")
|
|
315
|
+
print(f" 默认 provider 来源:{default_note}")
|
|
316
|
+
if composed_ok:
|
|
317
|
+
print(f" 判据来源:组合树 composed(dsh --profile {profile} --dump-config,{composed_note})+ 组合声明(补丁顶层/insert)")
|
|
318
|
+
else:
|
|
319
|
+
print(f" 判据来源:组合声明(补丁顶层/insert)|组合树**未读到**:{composed_note}")
|
|
320
|
+
if declared:
|
|
321
|
+
print(" 组合声明:" + "、".join(f"{k}({v})" for k, v in sorted(declared.items())))
|
|
322
|
+
else:
|
|
323
|
+
print(" 组合声明:(无 llm-* provider 实例 ⇒ 不期望任何 provider,未装配属正常)")
|
|
324
|
+
for pid in watch:
|
|
325
|
+
v = inst[pid]
|
|
326
|
+
if v['state'] in ('ok', 'ok_base'):
|
|
327
|
+
label = PROVIDERS.get(pid, '')
|
|
328
|
+
bu = f"baseURL {v['baseURL']}" if v['baseURL'] else '无 baseURL'
|
|
329
|
+
line = f" ✓ {pid}({(label + ',') if label else ''}{bu})|来源 {v['from']}"
|
|
330
|
+
if v['apiKeyEnv']:
|
|
331
|
+
line += f"|key {v['apiKeyEnv']}"
|
|
332
|
+
if v['state'] == 'ok_base':
|
|
333
|
+
line += '|基础组合提供(组合补丁未声明)'
|
|
334
|
+
if v['disabled']:
|
|
335
|
+
line += ' ⛔ 条目被 disabled ⇒ 实际不会加载'
|
|
336
|
+
if v['warning']:
|
|
337
|
+
line += f" {v['warning']}"
|
|
338
|
+
print(line)
|
|
339
|
+
elif v['state'] == 'missing':
|
|
340
|
+
print(f" ✗ {pid} 组合声明了但未装配(补丁 {v['declaredFrom']} 有它,组合树里没有该 id ⇒ 会被 dsh 静默跳过)")
|
|
341
|
+
elif v['state'] == 'unconfirmed':
|
|
342
|
+
print(f" ? {pid} 组合声明了(补丁 {v['declaredFrom']})但组合树**未读到** ⇒ 未确认(不敢断言「没装上」)")
|
|
343
|
+
else:
|
|
344
|
+
why = '未登录问述即如此,' if pid == 'llm-whyshu' else ''
|
|
345
|
+
if composed_ok:
|
|
346
|
+
print(f" – {pid} 未装配(组合未声明:{why}按设计;**不算缺失**)")
|
|
347
|
+
else:
|
|
348
|
+
print(f" – {pid} 未声明(组合未声明,且组合树未读到 ⇒ 未确认是否有基础组合提供)")
|
|
349
|
+
if missing:
|
|
350
|
+
print(f" ⚠️ **组合声明了但未装配**:{', '.join(missing)}")
|
|
351
|
+
for k in missing:
|
|
352
|
+
v = inst[k]
|
|
353
|
+
print(f" · {k}:组合补丁({v['declaredFrom']})声明了它"
|
|
354
|
+
f"{('(name: ' + v['plugin'] + ')') if v['plugin'] else ''},"
|
|
355
|
+
f"但 dsh --profile {profile} --dump-config 的组合树({composed_note})里没有该 id")
|
|
356
|
+
print(" ⇒ 新增实例必须写在 `- insert:` 块里(普通条目是「按 id 覆盖」,找不到会被静默跳过),")
|
|
357
|
+
print(" 且 name 必须能被 profile 依赖图解析(dsh plugin --profile web list 能看到它)。")
|
|
358
|
+
print(f" 重启前门禁:dsh --profile {profile} --dump-config | grep {' '.join(missing)}")
|
|
359
|
+
elif unconfirmed:
|
|
360
|
+
print(f" ⚠️ **无法判定**(组合树读不到):{', '.join(unconfirmed)}")
|
|
361
|
+
for k in unconfirmed:
|
|
362
|
+
print(f" · {k}:组合补丁({inst[k]['declaredFrom']})声明了它,但组合树没能读到({composed_note})")
|
|
363
|
+
print(" ⇒ 这是「未确认」,**不是**「装配坏了」:确认 dsh 可执行后重跑"
|
|
364
|
+
f"(当前 dsh:{dsh_bin})")
|
|
365
|
+
print(" 重跑:bash scripts/check-billing-mode.sh")
|
|
366
|
+
elif not ok_yaml:
|
|
367
|
+
print(" ⚠️ **无法判定**:组合补丁读不到 ⇒ 推导不出期望值(这条不是「provider 没装上」)")
|
|
368
|
+
else:
|
|
369
|
+
n_ok = len([k for k in watch if inst[k]['state'] in ('ok', 'ok_base')])
|
|
370
|
+
print(f" ✅ 装配与组合声明一致:声明 {len(declared)} 个 / 在位 {n_ok} 个;"
|
|
371
|
+
f"未声明的 provider 按设计视为未装配(**不算缺失**)")
|
|
372
|
+
|
|
373
|
+
sys.exit(1 if missing else (2 if (unconfirmed or not ok_yaml) else 0))
|
|
374
|
+
PY
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
printf '%s' "$dump_text" | "$PY" -c "$PY_SRC" "$PATCH" "$SETTINGS" "${1:-}" "$dump_status" "$PROFILE" "$DSH_BIN"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 检查某 parent session 的子会话关系:
|
|
4
|
+
* 1. 扫 sessions/ 全部目录的**所有版本化文件**的 header
|
|
5
|
+
* 2. 找 origin=subagent && parentSession=<parent> 的子会话(是否存在、哪个文件)
|
|
6
|
+
* 3. 扫 parent 自己的事件,列出 subagent 引用(subagent/descriptor 等)
|
|
7
|
+
* 用法: node scripts/check-child-sessions.mjs <parentSessionId>
|
|
8
|
+
*/
|
|
9
|
+
import { readFileSync, readdirSync, existsSync } from 'node:fs'
|
|
10
|
+
import { join } from 'node:path'
|
|
11
|
+
import { homedir } from 'node:os'
|
|
12
|
+
import { execFileSync } from 'node:child_process'
|
|
13
|
+
|
|
14
|
+
const parent = process.argv[2]
|
|
15
|
+
if (!parent) { console.error('用法: node check-child-sessions.mjs <parentSessionId>'); process.exit(1) }
|
|
16
|
+
|
|
17
|
+
const ROOT = join(process.env.DSH_HOME || join(homedir(), '.dsh'), 'sessions')
|
|
18
|
+
|
|
19
|
+
function readHeader(file) {
|
|
20
|
+
try {
|
|
21
|
+
const out = execFileSync('zstd', ['-dc', file], { maxBuffer: 10 * 1024 * 1024 })
|
|
22
|
+
const first = out.toString('utf8').split('\n')[0]
|
|
23
|
+
return JSON.parse(first)
|
|
24
|
+
} catch { return null }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log('=== 1. 扫描所有 session 的所有版本化文件 ===')
|
|
28
|
+
let count = 0
|
|
29
|
+
const children = []
|
|
30
|
+
for (const proj of readdirSync(ROOT)) {
|
|
31
|
+
const projDir = join(ROOT, proj)
|
|
32
|
+
let dirs = []
|
|
33
|
+
try { dirs = readdirSync(projDir) } catch { continue }
|
|
34
|
+
for (const d of dirs) {
|
|
35
|
+
const dir = join(projDir, d)
|
|
36
|
+
let files = []
|
|
37
|
+
try { files = readdirSync(dir).filter((f) => /^session(\.v\d+)?\.jsonl(\.zstd)?$/.test(f)) } catch { continue }
|
|
38
|
+
for (const f of files) {
|
|
39
|
+
count++
|
|
40
|
+
const h = readHeader(join(dir, f))
|
|
41
|
+
if (!h) { console.log(` ⚠️ 无法读 header: ${proj}/${d}/${f}`); continue }
|
|
42
|
+
if (h.parentSession === parent && h.origin === 'subagent') {
|
|
43
|
+
children.push({ file: `${proj}/${d}/${f}`, id: h.id, createdAt: h.createdAt })
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
console.log(` 扫描 ${count} 个文件`)
|
|
49
|
+
console.log(`\n=== 2. 找到的子会话: ${children.length} 个 ===`)
|
|
50
|
+
for (const c of children) console.log(` ✅ ${c.id}\n → ${c.file}\n createdAt=${c.createdAt}`)
|
|
51
|
+
if (children.length === 0) console.log(' ❌ 没有任何子会话文件')
|
|
52
|
+
|
|
53
|
+
console.log(`\n=== 3. parent(${parent}) 里的 subagent 引用 ===`)
|
|
54
|
+
const parentFile = (() => {
|
|
55
|
+
for (const proj of readdirSync(ROOT)) {
|
|
56
|
+
const dir = join(ROOT, proj, parent)
|
|
57
|
+
if (!existsSync(dir)) continue
|
|
58
|
+
const files = readdirSync(dir).filter((f) => /^session(\.v\d+)?\.jsonl(\.zstd)?$/.test(f))
|
|
59
|
+
return files.sort().map((f) => join(dir, f))
|
|
60
|
+
}
|
|
61
|
+
return []
|
|
62
|
+
})()
|
|
63
|
+
console.log(' parent 文件:', parentFile)
|
|
64
|
+
for (const pf of parentFile) {
|
|
65
|
+
try {
|
|
66
|
+
const out = execFileSync('zstd', ['-dc', pf], { maxBuffer: 500 * 1024 * 1024 }).toString('utf8')
|
|
67
|
+
const refs = new Set()
|
|
68
|
+
const types = {}
|
|
69
|
+
for (const line of out.split('\n')) {
|
|
70
|
+
if (!line.trim()) continue
|
|
71
|
+
let e
|
|
72
|
+
try { e = JSON.parse(line) } catch { continue }
|
|
73
|
+
if (e.type && e.type.includes('subagent')) {
|
|
74
|
+
types[e.type] = (types[e.type] || 0) + 1
|
|
75
|
+
const cid = e.data?.childId || e.data?.childSessionId || e.data?.sessionId
|
|
76
|
+
if (cid) refs.add(cid)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
console.log(` 文件: ${pf}`)
|
|
80
|
+
console.log(' subagent 相关事件类型:', types)
|
|
81
|
+
console.log(' 引用的 child ids:', [...refs])
|
|
82
|
+
} catch (e) { console.log(` 读取失败: ${e.message}`) }
|
|
83
|
+
}
|