@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,455 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* poc-mcp-host.mjs —— **第二宿主 PoC:极简 MCP stdio 宿主**(2026-10-05)
|
|
4
|
+
*
|
|
5
|
+
* ══════════════════════════════════════════════════════════════════════════
|
|
6
|
+
* 一、它要回答的问题(`docs/Argent-1.0-退出判据与版本规则.md` §2.4 **H1**)
|
|
7
|
+
* ══════════════════════════════════════════════════════════════════════════
|
|
8
|
+
* H1 = 「第二个宿主的 L2 六项接口跑通」= 1.0 唯一真"平台"判据。此前**没有任何非 `dsh web`
|
|
9
|
+
* 宿主**六项全绿 ⇒ 本文这个宿主(**不是 dsh**,只是一个 stdio JSON-RPC 客户端)就是被实跑的
|
|
10
|
+
* "第二个宿主":它把 Argent 内核(`core/mcp/server.mjs`)挂上去,逐条量出六项里**哪些真的可达**。
|
|
11
|
+
*
|
|
12
|
+
* 分层口径(**只引用不复述**):`docs/Argent-宿主适配契约.md` = 分层与宿主适配的唯一权威,
|
|
13
|
+
* 六项接口定义见其 §2、能力×宿主矩阵见其 §3。本脚本**不是**那份矩阵的第二份真源 —— 它只打印
|
|
14
|
+
* **实测事实**(initialize / tools/list / tools/call / 版本查询),六项可达性的权威口径以契约 §3 为准。
|
|
15
|
+
*
|
|
16
|
+
* ══════════════════════════════════════════════════════════════════════════
|
|
17
|
+
* 二、纪律(PoC 的硬约束)
|
|
18
|
+
* ══════════════════════════════════════════════════════════════════════════
|
|
19
|
+
* · **只读**:`READ_ONLY_TOOLS` 白名单**硬编码**在代码里(不是配置),请求写类 tool(`argent_claim` /
|
|
20
|
+
* `argent_writeback` / `argent_license` 之外的激活…)⇒ **拒绝调用**并 exit 2。
|
|
21
|
+
* 影子类(`*_shadow`)虽然"不写真台账",但会 `cpSync` 整个数据目录 / 造临时文件 ⇒ **也不在白名单**。
|
|
22
|
+
* · **零副作用**:本脚本自己不建临时文件、不写日志、不碰数据目录(只 spawn 子进程 + 读 stdout)。
|
|
23
|
+
* 证明方式是**外部**的:跑前跑后各拍一次真实数据目录快照再比对 ——
|
|
24
|
+
* `node scripts/check-test-env-safety.mjs --snapshot <f>` → 跑本脚本 → `--verify <f>`(三层判据)。
|
|
25
|
+
* · **子进程环境**:默认继承调用方 `process.env`(宿主就该继承),但**强制** `ARGENT_TELEMETRY_OFF=1`
|
|
26
|
+
* —— PoC 绝不能因为被跑了一次就把事件推到生产告警。要真发遥测得显式 `--allow-telemetry`。
|
|
27
|
+
* · **不依赖 dsh**:本脚本只用 `node:` 内置模块;`env -i` + 临时 HOME 下也能起(见 `--help` 的用法示例)。
|
|
28
|
+
*
|
|
29
|
+
* ══════════════════════════════════════════════════════════════════════════
|
|
30
|
+
* 三、用法与退出码
|
|
31
|
+
* ```
|
|
32
|
+
* node scripts/poc-mcp-host.mjs # 人读摘要(默认 3 个只读工具)
|
|
33
|
+
* node scripts/poc-mcp-host.mjs --json # 机器可读证据(逐条判据 + 耗时)
|
|
34
|
+
* node scripts/poc-mcp-host.mjs --tools argent_version,argent_license
|
|
35
|
+
* node scripts/poc-mcp-host.mjs --expect-tools 14 # tools/list 与冻结口径比对(默认 14)
|
|
36
|
+
* node scripts/poc-mcp-host.mjs --json --full # 证据不瘦身(默认截断长值/长数组,便于安全外贴)
|
|
37
|
+
* env -i HOME=$(mktemp -d) PATH=/usr/bin:/bin:<node 目录> node scripts/poc-mcp-host.mjs --json # 隔离启动
|
|
38
|
+
* ```
|
|
39
|
+
* 退出码:`0` 判据全过 / `1` 有判据不过 / `2` 用法错误(含**请求了写类 tool**)。
|
|
40
|
+
*/
|
|
41
|
+
import { spawn } from 'node:child_process'
|
|
42
|
+
import { readFileSync } from 'node:fs'
|
|
43
|
+
import { homedir } from 'node:os'
|
|
44
|
+
import { dirname, join, resolve } from 'node:path'
|
|
45
|
+
import { fileURLToPath } from 'node:url'
|
|
46
|
+
// 数据目录**唯一判据**(内核件;宿主也照它取,⛔ 不自己拼链)—— 只用于输出脱敏,不做任何业务判断
|
|
47
|
+
import { resolveDataDir } from '../core/lib/argent-datadir.mjs'
|
|
48
|
+
|
|
49
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
50
|
+
const REPO = resolve(HERE, '..')
|
|
51
|
+
const DEFAULT_SERVER = join(REPO, 'core', 'mcp', 'server.mjs')
|
|
52
|
+
const CLI = join(REPO, 'core', 'cli', 'argent.mjs')
|
|
53
|
+
const CLIENT_INFO = { name: 'poc-mcp-host', version: '0.1.0' }
|
|
54
|
+
const PROTOCOL_VERSION = '2024-11-05'
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* **只读 tool 白名单**(硬编码 = 代码级门禁,不是配置)。
|
|
58
|
+
* 判据:该 tool 的 handler 只 `readFileSync` / 跑只读脚本(`--scan` / `--json` / `status`),
|
|
59
|
+
* 不写数据目录、不写生产表、不认领、不产生影子目录。逐个复核于 2026-10-05:
|
|
60
|
+
* · `argent_version` —— 读 `VERSION` + `dsh --version`(子进程,只读)
|
|
61
|
+
* · `argent_license` —— `core/lib/license.mjs#status` ⇒ `scripts/check-license.mjs`(只读,无 write 调用点)
|
|
62
|
+
* · `argent_ledger` —— 读 `flow-ledger.jsonl`
|
|
63
|
+
* · `argent_report` / `argent_failures` —— `scripts/flow_failure.py`(分类 / `--scan`,只读)
|
|
64
|
+
* · `argent_sop` —— `core/lib/sop.mjs`(读 skill 目录 + 提示词)
|
|
65
|
+
* · `argent_doctor` / `argent_preflight` —— `scripts/flow_preflight.py`(门禁体检,不写表、不认领)
|
|
66
|
+
* · `argent_writeback_audit` —— 读 `invoice-writeback/audit.jsonl`
|
|
67
|
+
* ⛔ **不在**白名单:`argent_claim` / `argent_writeback`(写类)、`argent_claim_shadow` /
|
|
68
|
+
* `argent_writeback_shadow`(会 cpSync 数据目录 / 造临时文件 ⇒ 不是"零副作用")。
|
|
69
|
+
*/
|
|
70
|
+
const READ_ONLY_TOOLS = new Set([
|
|
71
|
+
'argent_version', 'argent_license', 'argent_ledger', 'argent_report', 'argent_failures',
|
|
72
|
+
'argent_sop', 'argent_doctor', 'argent_preflight', 'argent_writeback_audit',
|
|
73
|
+
])
|
|
74
|
+
const DEFAULT_TOOLS = ['argent_version', 'argent_license', 'argent_failures']
|
|
75
|
+
/** 各只读 tool 的**最小**入参(不含任何业务词:本文件属 `scripts/`,engine-words 门禁盯着)。 */
|
|
76
|
+
const DEFAULT_ARGS = {
|
|
77
|
+
argent_version: {},
|
|
78
|
+
argent_license: {},
|
|
79
|
+
argent_failures: {},
|
|
80
|
+
argent_report: { error: 'timeout' },
|
|
81
|
+
argent_ledger: { tail: 3 },
|
|
82
|
+
argent_writeback_audit: { tail: 2 },
|
|
83
|
+
}
|
|
84
|
+
/** 冻结口径(门禁 `scripts/check-judgment-drift.mjs` 自报 MCP tools 数);`--expect-tools` 可覆盖。 */
|
|
85
|
+
const FROZEN_TOOLS_COUNT = 14
|
|
86
|
+
|
|
87
|
+
function usage() {
|
|
88
|
+
return readFileSync(fileURLToPath(import.meta.url), 'utf8').split('*/')[0].replace(/^\/\*\*?/, '').trim()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function parseArgs(argv) {
|
|
92
|
+
const o = {
|
|
93
|
+
server: DEFAULT_SERVER, tools: [...DEFAULT_TOOLS], timeoutMs: 30000,
|
|
94
|
+
expectTools: FROZEN_TOOLS_COUNT, json: false, cli: true, telemetryOff: true, full: false,
|
|
95
|
+
}
|
|
96
|
+
for (let i = 0; i < argv.length; i++) {
|
|
97
|
+
const a = argv[i]
|
|
98
|
+
const val = () => {
|
|
99
|
+
const v = argv[i + 1]
|
|
100
|
+
if (v === undefined || v.startsWith('--')) throw new Error(`${a} 缺值`)
|
|
101
|
+
i++
|
|
102
|
+
return v
|
|
103
|
+
}
|
|
104
|
+
if (a === '--json') { o.json = true; continue }
|
|
105
|
+
if (a === '--full') { o.full = true; continue }
|
|
106
|
+
if (a === '--no-cli') { o.cli = false; continue }
|
|
107
|
+
if (a === '--allow-telemetry') { o.telemetryOff = false; continue }
|
|
108
|
+
if (a === '--server') { o.server = resolve(val()); continue }
|
|
109
|
+
if (a === '--timeout') { o.timeoutMs = Number(val()); continue }
|
|
110
|
+
if (a === '--expect-tools') { o.expectTools = Number(val()); continue }
|
|
111
|
+
if (a === '--tools') { o.tools = val().split(',').map((s) => s.trim()).filter(Boolean); continue }
|
|
112
|
+
if (a === '-h' || a === '--help') { console.log(usage()); process.exit(0) }
|
|
113
|
+
throw new Error(`未知参数:${a}`)
|
|
114
|
+
}
|
|
115
|
+
if (!Number.isFinite(o.timeoutMs) || o.timeoutMs <= 0) throw new Error('--timeout 必须是正数')
|
|
116
|
+
if (!Number.isFinite(o.expectTools) || o.expectTools < 0) throw new Error('--expect-tools 必须是非负数')
|
|
117
|
+
return o
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ── 脱敏:把真实家目录/数据目录/仓库路径换成占位符,并遮蔽令牌形态 ─────────────────────
|
|
121
|
+
// 数据目录**只认内核唯一判据**(`core/lib/argent-datadir.mjs`)—— ⛔ 不在这里拼 `~/.dsh/argent`:
|
|
122
|
+
// 那正是"第二份判据",会踩门禁 `scripts/tests/argent-datadir.test.mjs`【3】(2026-10-05 实测踩到)。
|
|
123
|
+
const DATA_DIR = resolveDataDir({ env: process.env }).dir
|
|
124
|
+
const SECRET_RE = /\b(eyJ[A-Za-z0-9._-]{10,}|sk-[A-Za-z0-9._-]{8,})\b/g
|
|
125
|
+
function redact(value) {
|
|
126
|
+
const home = homedir()
|
|
127
|
+
const subs = [
|
|
128
|
+
[REPO, '<REPO>'],
|
|
129
|
+
[DATA_DIR, '<ARGENT_DATA_DIR>'],
|
|
130
|
+
// 家目录是 `/` 或空 ⇒ 不做这条替换(否则每个路径都会被切碎)
|
|
131
|
+
[home && home !== '/' && home.length > 1 ? home : '', '<HOME>'],
|
|
132
|
+
]
|
|
133
|
+
const walk = (v) => {
|
|
134
|
+
if (typeof v === 'string') {
|
|
135
|
+
let s = v
|
|
136
|
+
for (const [from, to] of subs) if (from) s = s.split(from).join(to)
|
|
137
|
+
return s.replace(SECRET_RE, '<REDACTED>')
|
|
138
|
+
}
|
|
139
|
+
if (Array.isArray(v)) return v.map(walk)
|
|
140
|
+
if (v && typeof v === 'object') {
|
|
141
|
+
const out = {}
|
|
142
|
+
for (const [k, x] of Object.entries(v)) out[k] = walk(x)
|
|
143
|
+
return out
|
|
144
|
+
}
|
|
145
|
+
return v
|
|
146
|
+
}
|
|
147
|
+
return walk(value)
|
|
148
|
+
}
|
|
149
|
+
const clip = (s, n = 220) => (String(s).length > n ? `${String(s).slice(0, n)}…(截断,共 ${String(s).length} 字)` : String(s))
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 输出**瘦身**(默认开;`--full` 关):超长字符串截断、超长数组只留前 N 项。
|
|
153
|
+
* 为什么默认开:证据要能贴进回报/工单,而 `argent_failures`/`argent_ledger` 的载荷里可能带成片业务原文
|
|
154
|
+
* ⇒ 默认只带"够判读"的量(结构完整、逐字段仍在,只是长值被截)。要看全量给 `--full`。
|
|
155
|
+
*/
|
|
156
|
+
function shrink(value, { maxStr = 300, maxArr = 20 } = {}) {
|
|
157
|
+
const walk = (v) => {
|
|
158
|
+
if (typeof v === 'string') return clip(v, maxStr)
|
|
159
|
+
if (Array.isArray(v)) {
|
|
160
|
+
const head = v.slice(0, maxArr).map(walk)
|
|
161
|
+
return v.length > maxArr ? [...head, `…共 ${v.length} 项(要看全量加 --full)`] : head
|
|
162
|
+
}
|
|
163
|
+
if (v && typeof v === 'object') {
|
|
164
|
+
const out = {}
|
|
165
|
+
for (const [k, x] of Object.entries(v)) out[k] = walk(x)
|
|
166
|
+
return out
|
|
167
|
+
}
|
|
168
|
+
return v
|
|
169
|
+
}
|
|
170
|
+
return walk(value)
|
|
171
|
+
}
|
|
172
|
+
/** 证据出口:先瘦身(默认)再脱敏 —— 两者都只作用于**副本**,不影响判定用的原值。 */
|
|
173
|
+
const present = (value, { full = false } = {}) => redact(full ? value : shrink(value))
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 起 MCP server 子进程 + 极简 JSON-RPC 客户端(每行一条消息;id 配对;超时即抛)。
|
|
177
|
+
* 只用 `node:` 内置模块 ⇒ 本文件在任何 Node ≥ 18 上都能跑(证明"不依赖 dsh 宿主")。
|
|
178
|
+
*/
|
|
179
|
+
function connect({ serverPath, env, timeoutMs }) {
|
|
180
|
+
const child = spawn(process.execPath, [serverPath], { stdio: ['pipe', 'pipe', 'pipe'], env })
|
|
181
|
+
let buf = ''
|
|
182
|
+
let stderr = ''
|
|
183
|
+
let nextId = 1
|
|
184
|
+
const pending = new Map()
|
|
185
|
+
const done = { exited: false, code: null }
|
|
186
|
+
|
|
187
|
+
child.stdout.setEncoding('utf8')
|
|
188
|
+
child.stdout.on('data', (chunk) => {
|
|
189
|
+
buf += chunk
|
|
190
|
+
let i
|
|
191
|
+
while ((i = buf.indexOf('\n')) >= 0) {
|
|
192
|
+
const line = buf.slice(0, i).trim()
|
|
193
|
+
buf = buf.slice(i + 1)
|
|
194
|
+
if (!line) continue
|
|
195
|
+
let msg = null
|
|
196
|
+
try { msg = JSON.parse(line) } catch { continue }
|
|
197
|
+
if (msg.id !== undefined && pending.has(msg.id)) {
|
|
198
|
+
const p = pending.get(msg.id)
|
|
199
|
+
pending.delete(msg.id)
|
|
200
|
+
clearTimeout(p.timer)
|
|
201
|
+
p.resolve(msg)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
child.stderr.setEncoding('utf8')
|
|
206
|
+
child.stderr.on('data', (c) => { stderr += c })
|
|
207
|
+
child.on('exit', (code) => { done.exited = true; done.code = code })
|
|
208
|
+
|
|
209
|
+
const request = (method, params) => new Promise((res, rej) => {
|
|
210
|
+
const id = nextId++
|
|
211
|
+
const timer = setTimeout(() => {
|
|
212
|
+
pending.delete(id)
|
|
213
|
+
rej(new Error(`${method} 超时(${timeoutMs}ms)—— server stderr 末尾:${clip(stderr.slice(-300))}`))
|
|
214
|
+
}, timeoutMs)
|
|
215
|
+
pending.set(id, { resolve: res, timer })
|
|
216
|
+
child.stdin.write(`${JSON.stringify({ jsonrpc: '2.0', id, method, params })}\n`)
|
|
217
|
+
})
|
|
218
|
+
const notify = (method, params) => child.stdin.write(`${JSON.stringify({ jsonrpc: '2.0', method, params })}\n`)
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
request, notify,
|
|
222
|
+
stderr: () => stderr,
|
|
223
|
+
child,
|
|
224
|
+
close: () => new Promise((res) => {
|
|
225
|
+
if (done.exited) return res(done.code)
|
|
226
|
+
child.once('exit', (code) => res(code))
|
|
227
|
+
child.stdin.end() // server 见 stdin 结束即 exit 0(`process.stdin.on('end')`)
|
|
228
|
+
setTimeout(() => { try { child.kill('SIGKILL') } catch { /* 已退 */ } }, 5000).unref?.()
|
|
229
|
+
}),
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** 拆 `tools/call` 的响应:content[0].text 里是 JSON 字符串(MCP 既有形状)。 */
|
|
234
|
+
function unpack(msg) {
|
|
235
|
+
const text = msg?.result?.content?.[0]?.text
|
|
236
|
+
if (typeof text !== 'string') return { ok: false, parseError: '响应里没有 content[0].text', raw: msg }
|
|
237
|
+
try { const v = JSON.parse(text); return { ok: true, value: v } } catch (e) { return { ok: false, parseError: `content JSON 解析失败:${e.message}`, text } }
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** 跑一条只读 CLI 命令(`--json`),拿机器可读结果;失败不抛(宿主应看到诊断而不是崩)。 */
|
|
241
|
+
function runCli(args, { env, timeoutMs }) {
|
|
242
|
+
return new Promise((res) => {
|
|
243
|
+
const child = spawn(process.execPath, [CLI, ...args], { env, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
244
|
+
let out = ''
|
|
245
|
+
let err = ''
|
|
246
|
+
const timer = setTimeout(() => { try { child.kill('SIGKILL') } catch { /* noop */ } }, timeoutMs)
|
|
247
|
+
child.stdout.setEncoding('utf8')
|
|
248
|
+
child.stdout.on('data', (c) => { out += c })
|
|
249
|
+
child.stderr.setEncoding('utf8')
|
|
250
|
+
child.stderr.on('data', (c) => { err += c })
|
|
251
|
+
child.on('exit', (code) => {
|
|
252
|
+
clearTimeout(timer)
|
|
253
|
+
let json = null
|
|
254
|
+
try { json = JSON.parse(out) } catch { /* 非 JSON */ }
|
|
255
|
+
res({ code, json, out: out.trim(), err: err.trim() })
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function main() {
|
|
261
|
+
const opt = parseArgs(process.argv.slice(2))
|
|
262
|
+
|
|
263
|
+
// ── 写类 tool 硬拒绝(在任何子进程起来**之前**)────────────────────────────────
|
|
264
|
+
const refused = opt.tools.filter((t) => !READ_ONLY_TOOLS.has(t))
|
|
265
|
+
if (refused.length) {
|
|
266
|
+
console.error(`❌ 拒绝调用非只读 tool:${refused.join(', ')}`)
|
|
267
|
+
console.error(` 白名单(只读):${[...READ_ONLY_TOOLS].join(', ')}`)
|
|
268
|
+
console.error(' ⛔ 本 PoC 的纪律是零副作用:写类 tool(含 *_shadow:会 cpSync 数据目录/造临时文件)一律不调。')
|
|
269
|
+
process.exit(2)
|
|
270
|
+
}
|
|
271
|
+
if (!opt.tools.length) { console.error('❌ --tools 不能为空'); process.exit(2) }
|
|
272
|
+
|
|
273
|
+
const env = { ...process.env }
|
|
274
|
+
if (opt.telemetryOff) env.ARGENT_TELEMETRY_OFF = '1'
|
|
275
|
+
const childEnvKeys = Object.keys(env).sort()
|
|
276
|
+
|
|
277
|
+
const report = {
|
|
278
|
+
host: { kind: 'mcp-stdio', client: CLIENT_INFO, hostIsDsh: false, node: process.version, argv: process.argv.slice(2) },
|
|
279
|
+
server: { path: present(opt.server, opt), exists: true, envKeys: childEnvKeys.length, telemetryOff: opt.telemetryOff },
|
|
280
|
+
checks: {},
|
|
281
|
+
readonlyTools: opt.tools,
|
|
282
|
+
verdict: { pass: false, reasons: [] },
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
try { readFileSync(opt.server) } catch (e) {
|
|
286
|
+
report.server.exists = false
|
|
287
|
+
report.verdict.reasons.push(`server 文件读不到:${e.message}`)
|
|
288
|
+
return finish(report, opt)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const c = connect({ serverPath: opt.server, env, timeoutMs: opt.timeoutMs })
|
|
292
|
+
try {
|
|
293
|
+
// ── 判据① initialize(protocolVersion / serverInfo / capabilities)────────────
|
|
294
|
+
const t0 = Date.now()
|
|
295
|
+
const init = await c.request('initialize', {
|
|
296
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
297
|
+
capabilities: { tools: {} },
|
|
298
|
+
clientInfo: CLIENT_INFO,
|
|
299
|
+
})
|
|
300
|
+
const ir = init.result || {}
|
|
301
|
+
report.checks.initialize = {
|
|
302
|
+
ok: Boolean(ir.serverInfo && ir.protocolVersion),
|
|
303
|
+
elapsedMs: Date.now() - t0,
|
|
304
|
+
protocolVersion: ir.protocolVersion || null,
|
|
305
|
+
serverInfo: ir.serverInfo || null,
|
|
306
|
+
capabilities: ir.capabilities || null,
|
|
307
|
+
error: init.error || null,
|
|
308
|
+
}
|
|
309
|
+
if (!report.checks.initialize.ok) report.verdict.reasons.push('① initialize 没回 serverInfo/protocolVersion')
|
|
310
|
+
c.notify('notifications/initialized', {})
|
|
311
|
+
|
|
312
|
+
// ── ping(协议活性;顺带证明"服务端在 listen")────────────────────────────────
|
|
313
|
+
const ping = await c.request('ping', {})
|
|
314
|
+
report.checks.ping = { ok: ping.error === undefined, error: ping.error || null }
|
|
315
|
+
|
|
316
|
+
// ── 判据② tools/list(数量必须与冻结口径一致)────────────────────────────────
|
|
317
|
+
const tl = await c.request('tools/list', {})
|
|
318
|
+
const tools = tl.result?.tools || []
|
|
319
|
+
const names = tools.map((t) => t.name)
|
|
320
|
+
const countOk = opt.expectTools === 0 || tools.length === opt.expectTools
|
|
321
|
+
report.checks.toolsList = {
|
|
322
|
+
ok: Array.isArray(tl.result?.tools) && countOk,
|
|
323
|
+
count: tools.length,
|
|
324
|
+
expected: opt.expectTools || null,
|
|
325
|
+
names,
|
|
326
|
+
schemasPresent: tools.every((t) => t && t.inputSchema && typeof t.inputSchema === 'object'),
|
|
327
|
+
error: tl.error || null,
|
|
328
|
+
}
|
|
329
|
+
if (!Array.isArray(tl.result?.tools)) report.verdict.reasons.push('② tools/list 没有返回 tools 数组')
|
|
330
|
+
else if (!countOk) report.verdict.reasons.push(`② tools/list 数量 ${tools.length} ≠ 冻结口径 ${opt.expectTools}`)
|
|
331
|
+
|
|
332
|
+
// ── 判据③ 只读 tool 实调(逐个贴脱敏结果)────────────────────────────────────
|
|
333
|
+
report.checks.toolCalls = []
|
|
334
|
+
for (const name of opt.tools) {
|
|
335
|
+
const t1 = Date.now()
|
|
336
|
+
const msg = await c.request('tools/call', { name, arguments: DEFAULT_ARGS[name] || {} })
|
|
337
|
+
const un = unpack(msg)
|
|
338
|
+
const value = un.ok ? un.value : null
|
|
339
|
+
// 三层分开判(**不把"业务结论为否"当成"调用失败"** —— 那会把真实判定链误报成红):
|
|
340
|
+
// · callOk「调用真跑通」= RPC 没报错 ∧ handler 没标 isError ∧ 载荷结构化;
|
|
341
|
+
// · degraded「能力降级」= 载荷自报 `python_unavailable` / `code:2`(**是**能力缺口,**算红**);
|
|
342
|
+
// · verdict:`ok`(业务判 true,或该 tool 本就没有 `ok` 字段——如 `argent_version` 只回事实)
|
|
343
|
+
// / `judged`(业务显式判 false,如本机未装许可 ⇒ 判定链照样走通)/ `degraded` / `failed`。
|
|
344
|
+
const rpcOk = msg.error === undefined && msg.result !== undefined
|
|
345
|
+
const isError = msg.result?.isError === true
|
|
346
|
+
const shapeOk = un.ok && value !== null && typeof value === 'object'
|
|
347
|
+
const callOk = Boolean(rpcOk && !isError && shapeOk)
|
|
348
|
+
const degraded = shapeOk && (value.reason === 'python_unavailable' || value.code === 2)
|
|
349
|
+
? redact({ reason: value.reason || null, code: value.code ?? null, error: clip(value.error || '', 200) })
|
|
350
|
+
: null
|
|
351
|
+
const verdict = !callOk ? 'failed' : (degraded ? 'degraded' : (value.ok === false ? 'judged' : 'ok'))
|
|
352
|
+
report.checks.toolCalls.push({
|
|
353
|
+
name,
|
|
354
|
+
ok: callOk && !degraded, // 判据③ 只数「真跑通且没降级」的
|
|
355
|
+
callOk, verdict, rpcOk, isError, elapsedMs: Date.now() - t1,
|
|
356
|
+
result: value === null ? { parseError: un.parseError, text: clip(un.text || '') } : present(value, opt),
|
|
357
|
+
degraded,
|
|
358
|
+
})
|
|
359
|
+
}
|
|
360
|
+
const okCalls = report.checks.toolCalls.filter((x) => x.ok).length
|
|
361
|
+
const judgedCalls = report.checks.toolCalls.filter((x) => x.verdict === 'judged').length
|
|
362
|
+
report.checks.readonlyCalls = {
|
|
363
|
+
ok: okCalls >= 2,
|
|
364
|
+
okCount: okCalls,
|
|
365
|
+
judgedCount: judgedCalls, // 其中"业务判否但判定链走通"的(**不是**失败)
|
|
366
|
+
total: report.checks.toolCalls.length,
|
|
367
|
+
required: 2,
|
|
368
|
+
degradedCount: report.checks.toolCalls.filter((x) => x.degraded).length,
|
|
369
|
+
}
|
|
370
|
+
if (okCalls < 2) report.verdict.reasons.push(`③ 只读 tool 真调成功 ${okCalls} 个(判据要求 ≥ 2)`)
|
|
371
|
+
|
|
372
|
+
// ── 判据④ 版本/能力可查(CLI 面;MCP 面的等价物 = `argent_version` tool)────────
|
|
373
|
+
if (opt.cli) {
|
|
374
|
+
const v = await runCli(['version', '--json'], { env, timeoutMs: opt.timeoutMs })
|
|
375
|
+
const h = await runCli(['--help', '--json'], { env, timeoutMs: opt.timeoutMs })
|
|
376
|
+
report.checks.cliCapability = {
|
|
377
|
+
ok: v.code === 0 && h.code === 0 && Boolean(h.json?.verbs),
|
|
378
|
+
version: { code: v.code, argent: v.json?.argent ?? null, raw: clip(v.out, 160) },
|
|
379
|
+
help: { code: h.code, verbs: (h.json?.verbs || []).map((x) => x.name ?? x), verbCount: (h.json?.verbs || []).length },
|
|
380
|
+
}
|
|
381
|
+
if (!report.checks.cliCapability.ok) report.verdict.reasons.push('④ 版本/能力查询(CLI --json)失败')
|
|
382
|
+
} else {
|
|
383
|
+
report.checks.cliCapability = { ok: true, skipped: true }
|
|
384
|
+
}
|
|
385
|
+
} catch (e) {
|
|
386
|
+
report.verdict.reasons.push(`RPC 层失败:${e.message}`)
|
|
387
|
+
} finally {
|
|
388
|
+
report.server.exitCode = await c.close()
|
|
389
|
+
report.server.stderrTail = clip(c.stderr().trim().slice(-400))
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// ── 六项接口的**实测可达性**(PoC 判定;权威矩阵列见契约 §3,本块只记"这个宿主量到了什么")
|
|
393
|
+
const entryOk = report.checks.toolsList?.ok === true && report.checks.readonlyCalls?.ok === true
|
|
394
|
+
const versionOk = report.checks.cliCapability?.ok === true
|
|
395
|
+
report.hostSurface = [
|
|
396
|
+
{ face: '① 入口/工具暴露', status: entryOk ? '✅' : '❌', how: `initialize + tools/list(${report.checks.toolsList?.count ?? '?'}) + tools/call(成功 ${report.checks.readonlyCalls?.okCount ?? 0} 个只读)` },
|
|
397
|
+
{ face: '② 设置面板', status: '—', why: 'stdio 宿主**无 UI**(设计性缺失)', substitute: `配置文件(数据目录下的 *.json/*.env)+ CLI 只读查询;宿主自己解析` },
|
|
398
|
+
{ face: '③ 通知通道', status: '—', why: '本宿主无投递通道(设计性缺失)', substitute: 'CLI `notify` verb + `core/lib/notify/*`(宿主负责把事实转成自己的通道)' },
|
|
399
|
+
{ face: '④ 会话/权限', status: '—', why: 'stdio 无会话概念、无权限 UI(设计性缺失)', substitute: '宿主进程自身的权限模型(本 PoC = 调用者的 uid/umask)' },
|
|
400
|
+
{ face: '⑤ 后台/定时', status: '—', why: '宿主无 scheduler(设计性缺失)', substitute: '系统定时器(launchd/systemd)+ `scripts/flow-timers.sh`' },
|
|
401
|
+
{ face: '⑥ 升级/版本', status: versionOk ? '✅' : '❌', how: `CLI \`version --json\`(${report.checks.cliCapability?.version?.argent ?? '?'})+ \`--help --json\`(verb ${report.checks.cliCapability?.help?.verbCount ?? '?'})+ MCP \`argent_version\`` },
|
|
402
|
+
]
|
|
403
|
+
|
|
404
|
+
report.verdict.pass = report.verdict.reasons.length === 0
|
|
405
|
+
return finish(report, opt)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function finish(report, opt) {
|
|
409
|
+
if (opt.json) {
|
|
410
|
+
console.log(JSON.stringify(report, null, 2))
|
|
411
|
+
} else {
|
|
412
|
+
printHuman(report)
|
|
413
|
+
}
|
|
414
|
+
process.exit(report.verdict.pass ? 0 : 1)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function printHuman(r) {
|
|
418
|
+
const mark = (b) => (b ? '✅' : '❌')
|
|
419
|
+
console.log('\n【MCP 宿主 PoC】宿主 = 本脚本(极简 stdio JSON-RPC 客户端,**不是 dsh**)')
|
|
420
|
+
console.log(` server = ${r.server.path}(node ${r.host.node};子进程 env ${r.server.envKeys} 键;遥测 ${r.server.telemetryOff ? 'OFF' : 'ON⚠️'})`)
|
|
421
|
+
const i = r.checks.initialize || {}
|
|
422
|
+
console.log(`\n ① initialize ${mark(i.ok)} protocolVersion=${i.protocolVersion} serverInfo=${JSON.stringify(i.serverInfo)} capabilities=${JSON.stringify(i.capabilities)} (${i.elapsedMs}ms)`)
|
|
423
|
+
if (r.checks.ping) console.log(` ping ${mark(r.checks.ping.ok)}`)
|
|
424
|
+
const t = r.checks.toolsList || {}
|
|
425
|
+
console.log(` ② tools/list ${mark(t.ok)} ${t.count} 个(冻结口径 ${t.expected ?? '—'})inputSchema 齐备=${mark(t.schemasPresent)}`)
|
|
426
|
+
console.log(` ${(t.names || []).join(', ')}`)
|
|
427
|
+
const rc = r.checks.readonlyCalls || {}
|
|
428
|
+
console.log(` ③ 只读 tool 实调 ${mark(rc.ok)} 真跑通 ${rc.okCount}/${rc.total}(判据 ≥${rc.required};其中"业务判否但判定链走通" ${rc.judgedCount ?? 0} 个;降级 ${rc.degradedCount ?? 0} 个)`)
|
|
429
|
+
const MARK = { ok: '✅', judged: '🟡', degraded: '🔴', failed: '❌' }
|
|
430
|
+
const NOTE = {
|
|
431
|
+
ok: '业务判 true',
|
|
432
|
+
judged: '业务判 false —— **判定链走通**(不是调用失败;本机真实状态)',
|
|
433
|
+
degraded: '能力降级(诚实诊断,非崩溃)',
|
|
434
|
+
failed: '调用失败',
|
|
435
|
+
}
|
|
436
|
+
for (const c of r.checks.toolCalls || []) {
|
|
437
|
+
console.log(` · ${c.name.padEnd(24)} ${MARK[c.verdict] || '❓'} ${String(c.elapsedMs).padStart(5)}ms ${NOTE[c.verdict] || ''}`)
|
|
438
|
+
console.log(` ${clip(JSON.stringify(c.result), 220)}`)
|
|
439
|
+
if (c.degraded) console.log(` ↳ reason=${c.degraded.reason} code=${c.degraded.code} ${clip(c.degraded.error || '', 140)}`)
|
|
440
|
+
}
|
|
441
|
+
const cc = r.checks.cliCapability || {}
|
|
442
|
+
console.log(` ④ 版本/能力 ${mark(cc.ok)}${cc.skipped ? '(--no-cli 跳过)' : ` CLI version=${cc.version?.argent} | --help --json verb ${cc.help?.verbCount} 个`}`)
|
|
443
|
+
console.log('\n 六项接口可达性(实测;权威口径见 docs/Argent-宿主适配契约.md §3 矩阵)')
|
|
444
|
+
for (const s of r.hostSurface || []) {
|
|
445
|
+
console.log(` ${s.status} ${s.face} ${s.how || `— ${s.why}`}`)
|
|
446
|
+
if (s.substitute) console.log(` 替代:${s.substitute}`)
|
|
447
|
+
}
|
|
448
|
+
console.log(`\n 结论:${r.verdict.pass ? '✅ PoC 判据全过' : `❌ 未过 —— ${r.verdict.reasons.join(';')}`}`)
|
|
449
|
+
console.log(' 零副作用证明(外部判据):跑前 `node scripts/check-test-env-safety.mjs --snapshot <f>`,跑后 `--verify <f>`\n')
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
main().catch((e) => {
|
|
453
|
+
console.error(`❌ ${e.message}`)
|
|
454
|
+
process.exit(2)
|
|
455
|
+
})
|