chainlesschain 0.163.3 → 0.163.4
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/package.json +3 -2
- package/scripts/command-lifecycle-report.mjs +330 -38
- package/src/assets/web-panel/assets/{AIOps-DIY6KxV-.js → AIOps-DB5X7qqV.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BXH0muPt.js → ActionButton-DxHRtKLk.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D39w8vmg.js → Analytics-DIH5QrxT.js} +2 -2
- package/src/assets/web-panel/assets/{AppLayout-CUU1zvLs.js → AppLayout-uu0oxVz7.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-D8azHO3T.js → Artifacts-BNSMiW9Y.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-D0tAwXTg.js → Audit-DcAzB4_x.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-C29mhJll.js → BackgroundAgents-ClSsvjIc.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-D6Aa1Gro.js → Backup-BMciY0js.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-B1uoHbPQ.js → BaseInput-Hn3b57Sh.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Bs4DLOR5.js → Chat-CoSlC53V.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DUYIBSle.js → ChatBubbleRenderer-CFevJ3So.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CNcBPEa4.js → Checkbox-B4L4Eu4w.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CIY89763.js → Codegen-C77FgRxF.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CsPYFmgo.js → Col-BrLtJLH3.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BBocEge4.js → Community-Be7pXXhA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-TQ8Ux0Lf.js → Compact-DuGPecjo.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DfBsZTft.js → Compliance-CzS66mjP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Cg-I6FVH.js → Cowork-CEhjCoyK.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-DTQzfRTp.js → Cron-D7ir8Vja.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DGxEKGpR.js → Crosschain-BcXmuLxf.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DLZ1RDSr.js → DID-Cto1dtBy.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-XneICKNI.js → Dashboard-B7KjgwOX.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BRU00J1-.js → Dropdown-COMqxijg.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BzCbJuAy.js → EmailListRenderer-BZrya-nf.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D7BRyNBG.js → FamilyGuardDashboard-AmVeMq0J.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D9DeFIhW.js → Federation-kZU8c-27.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-6rAtA9Ko.js → FormItemContext-sOV22v2-.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-8Hqy80pU.js → GenericCardRenderer-BUuLtGCz.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Cmvkj1rt.js → Git-K-ZxsLsH.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BRiQUjjw.js → Governance-D3OEnawI.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CKN_EzVf.js → Inference-Bll0gRmP.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BCDHwpwQ.js → KnowledgeGraph-DBXwjr53.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DK9ovhaB.js → Logs-CEzPwEfP.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CLmCbHx6.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BILPGakp.js → Marketplace-BYyRyOPz.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DTVCOr9-.js → McpTools-DVu8w_rM.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Hzc1iow4.js → Memory-tenuZ_hO.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-GXfgnGYV.js → MobileBridge-Bxgy4ozS.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B-3TJWsS.js → MobileProjects-Dgx1D6sc.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DwSJMWtK.js → Mtc-CRfJ8r8j.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-D2uxf-DA.js → MtcAudit-BBJq4Aiz.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-DG6kbKK0.js → Multisig-B3ii9lai.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-f5p49EDU.js → NLProgramming-FIi-Cubv.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-FUgHyzSD.js → Notes-DIq8MPf1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DCHGee7e.js → NotificationSettings-BFOD9eGd.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Dm6LlIoa.js → OrderTableRenderer--41u7vre.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BawA7qAg.js → Organization-B_0AWztW.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-D6a5wZU1.js → Overflow-B6I7TuXv.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-K8WavwnA.js → P2P-Cdf6qens.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-4JAJYaqx.js → PdhVaultBrowser-B_0FcQJY.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BDWvi8PD.js → Permissions-6nFBjsQp.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-DF_K4E9i.js → PersonalDataHub-nQYV7xpn.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-BYeZlesI.js → Pipeline-CBVod5jm.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BIHlgnIc.js → Privacy-D52jW1Nt.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Cvf-6B5P.js → ProjectInit-Bdx3BYhf.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CnwfJrRz.js → ProjectSettings-b7xr4DUa.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-b-jPXzF4.js → Projects-DhfifCKx.js} +1 -1
- package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-VLf3gAfL.js → QrScannerModal-FQKSmc3e.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-B5vGjRiF.js → QuickAsk-CSTrhzcF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend--UW4iYs-.js → Recommend-CxMABOUh.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DMSPcfKR.js → RemoteSession-Cv2T8yRa.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-kQXlEHmj.js → Reputation-CE1k4Vvu.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CxGu3pit.js → Row-BNgNcAV_.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Bi5lo8MZ.js → RssFeed-5ZiBZ3ph.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Cq_H5RTh.js → Search-mDOIF1UV.js} +1 -1
- package/src/assets/web-panel/assets/{Security-YFbmkaQS.js → Security-CtuCTC-e.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DLOGdaLm.js → Services-DxFO2Zg7.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CteIxb3D.js → Skeleton-CKmKkH9y.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CccopaIZ.js → Skills-Bmzl9qkk.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-AzfEQKX1.js → Sla-9RqaWHOC.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-ekGo5l77.js → SpeechSettings-CGdzFIYw.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BmZmAgYH.js → SyncSettings-CvNuCeBT.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-D7q3Z4uL.js → Tasks-B_sLdcnw.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CX1Tgeka.js → Templates-D9_YijdK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Zc59jF4i.js → Tenant-Cuc7BUSP.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CXOD7y83.js → Terminal-6VYRnJgd.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-B1aBHPBo.js → TimelineRenderer-GtK8xd8Y.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-zALlOq_U.js → Tokens-BZ94Lt5W.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CQfJMXaq.js → Trigger-DEcZgDaX.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BU0RcM-w.js → Trust-CC3z9THe.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-IFZ3HsbH.js → UkeySign-Cvtw8cV3.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BzslgW0v.js → VideoEditing-CL4e6Bn3.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BZAcQNS6.js → Wallet-ycEemP27.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CBK1DMi0.js → WebAuthn-DgZ4X_aw.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-BlMTPzO-.js → WorkflowEditor-D0qMhSv7.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B51-8QVn.js → chat-DsAg01Zn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-504RTByU.js → colors-B0oqAhrQ.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C-GP6IQM.js → compact-item-CmX0Rabz.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DOkIj7ID.js → createContext-Be4cn1Ub.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DN7V-_FW.js → hasIn-D5v12kVq.js} +1 -1
- package/src/assets/web-panel/assets/{index-kt_5rfcZ.js → index-0eWLkQKT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUhQpiYv.js → index-301ogenv.js} +1 -1
- package/src/assets/web-panel/assets/{index-f4Bo4KXm.js → index-5LGY0JbG.js} +1 -1
- package/src/assets/web-panel/assets/{index--n-ZciV3.js → index-7VKDn-t1.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1phyca8.js → index-8WvYGnON.js} +1 -1
- package/src/assets/web-panel/assets/{index-7jIqHbzw.js → index-B1YGjOWC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEMPUVa3.js → index-B3mUHuVc.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bkxhaeta.js → index-BA4oulaP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYopNjcJ.js → index-BBREnn1V.js} +1 -1
- package/src/assets/web-panel/assets/{index-P2mfY27Z.js → index-BBvyHguy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DA2f1UV3.js → index-BKmsS2jj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CP2A226L.js → index-BRIqLy1m.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfiEM64n.js → index-BXGSbelg.js} +3 -3
- package/src/assets/web-panel/assets/{index-B2-x9bS8.js → index-BaE0Ui07.js} +1 -1
- package/src/assets/web-panel/assets/{index-CySTJKeq.js → index-BoAIAZ5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-kAqQXfP6.js → index-BqltWojQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAOpQvl_.js → index-CGiIyTZw.js} +1 -1
- package/src/assets/web-panel/assets/index-CKB0vbJU.js +1 -0
- package/src/assets/web-panel/assets/{index-C4DV_5uA.js → index-CZ2wUEgr.js} +1 -1
- package/src/assets/web-panel/assets/{index-yYTQZypF.js → index-CZpfzcuy.js} +1 -1
- package/src/assets/web-panel/assets/{index-et5JIPHO.js → index-Ckrk3pIV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaA__-q1.js → index-CqoCZ5vR.js} +1 -1
- package/src/assets/web-panel/assets/{index-By6u5IV1.js → index-CyWBir7N.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKTfZyfU.js → index-CyyGTMKd.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_vGU-Js.js → index-D0BSkxYQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cao5DZy7.js → index-D5YVopyr.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3TSohzc.js → index-DKUBj36_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFIFJ5Dr.js → index-DM724NgU.js} +1 -1
- package/src/assets/web-panel/assets/{index-ConjaJro.js → index-DQRsQen0.js} +1 -1
- package/src/assets/web-panel/assets/index-DgoXmwqB.js +1 -0
- package/src/assets/web-panel/assets/{index-C9_48OzU.js → index-DikJ0Qqe.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0k_Nqg9.js → index-Dn2dSdXx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE-hmI2l.js → index-DyUaFWEk.js} +1 -1
- package/src/assets/web-panel/assets/{index-so8eIIxF.js → index-Hfe_dEGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-FcV8J_Qz.js → index-_JjRLSD0.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9LyiN-L.js → index-frSojpHj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CX78k-qf.js → index-jK3ax6Z6.js} +1 -1
- package/src/assets/web-panel/assets/{index-C8UltbXK.js → index-qMuZ-NRR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqcLo5WV.js → index-q_fQ94SE.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CPYEIdpV.js → initDefaultProps-D4VdhJ9_.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Cjim9pxs.js → motion-CZJA0MQW.js} +1 -1
- package/src/assets/web-panel/assets/{move-DuU-cgz3.js → move-BqEs7Sb1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DJHNAkta.js → mtc-parser-BpHEtsu1.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DSQBgdBj.js → omit-BW94tY9W.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CTmT_j5m.js → pickAttrs-BnBjPWP9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Lfm6v-r4.js → placementArrow-uUkjQ_hu.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CgrcPcDi.js → responsiveObserve-B-klUqXK.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CuE3110i.js → slide-CvY1cNAs.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DiK5nbpf.js → statusUtils-Ix5sEDT2.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DPEJdQaZ.js → styleChecker-C8qcXOne.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CnnodGmp.js → useFlexGapSupport-KNF6Kj8f.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DWF84Yav.js → useFs-DGJr345n.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-a5b5Dw2f.js → usePersonalDataHub-BboN99rI.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DAluzXOq.js → vnode-Bgt8Vgte.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Dokg7uAZ.js → zoom-BhJtErgt.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/artifacts.js +98 -0
- package/src/commands/mcp.js +1 -1
- package/src/commands/review.js +221 -22
- package/src/commands/session-mcp-recovery.js +5 -5
- package/src/data/changelog.json +14 -1
- package/src/gateways/ws/ws-agent-handler.js +492 -14
- package/src/harness/jsonl-session-store.js +39 -1
- package/src/harness/mcp-client.js +172 -29
- package/src/harness/prompt-compressor.js +24 -0
- package/src/harness/provider-backed-compaction.js +205 -0
- package/src/lib/app-preview.js +3 -0
- package/src/lib/command-lifecycle-report.js +1340 -145
- package/src/lib/delivery-coordinator.js +30 -5
- package/src/lib/delivery-production-adapter.js +1777 -0
- package/src/lib/delivery-production-runner.js +240 -0
- package/src/lib/durable-security-store.js +29 -17
- package/src/lib/execution-trace/trace-context.cjs +179 -0
- package/src/lib/execution-trace/trace-context.js +8 -175
- package/src/lib/mcp-call-ledger-store.js +58 -6
- package/src/lib/mcp-host-recovery-runtime.js +2 -0
- package/src/lib/mcp-ledger-recovery-admission.js +39 -3
- package/src/lib/mcp-recovery-adjudication.js +256 -19
- package/src/lib/mcp-sandbox-policy.js +29 -0
- package/src/lib/mcp-stdio-capsule-builder-worker.cjs +495 -0
- package/src/lib/mcp-stdio-executable-identity.js +33 -2
- package/src/lib/mcp-stdio-immutable-vfs-resolver.cjs +830 -0
- package/src/lib/mcp-stdio-package-materialization.js +721 -333
- package/src/lib/observability/index.js +2 -0
- package/src/lib/observability/otlp-exporter.js +2 -2
- package/src/lib/packer/pack-update-applier.js +8 -1
- package/src/lib/packer/pkg-config-generator.js +34 -0
- package/src/lib/process-execution-broker/index.js +34 -5
- package/src/lib/process-execution-broker/platform-sandbox.js +82 -1
- package/src/lib/scheduler-kernel/contract.js +315 -0
- package/src/lib/scheduler-kernel/store.js +1345 -0
- package/src/lib/session-host-lease.js +403 -30
- package/src/lib/session-persistence-failure.js +4 -1
- package/src/lib/sub-agent-context.js +8 -0
- package/src/repl/agent-repl.js +285 -38
- package/src/runtime/agent-core.js +1044 -90
- package/src/runtime/headless-runner.js +260 -166
- package/src/runtime/headless-stream.js +254 -25
- package/src/assets/web-panel/assets/MarkdownRenderer-D53pHiHy.js +0 -1
- package/src/assets/web-panel/assets/Providers-CxC7JuYv.js +0 -1
- package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +0 -1
- package/src/assets/web-panel/assets/index-Bya64sZE.js +0 -1
- package/src/assets/web-panel/assets/index-D_FWpDZM.js +0 -1
|
@@ -16,6 +16,8 @@ import crypto from "node:crypto";
|
|
|
16
16
|
import fs from "node:fs";
|
|
17
17
|
import { builtinModules, createRequire } from "node:module";
|
|
18
18
|
import path from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
import { Worker } from "node:worker_threads";
|
|
19
21
|
import { getCacheDir, getMachineSecurityAnchorDir } from "./paths.js";
|
|
20
22
|
import {
|
|
21
23
|
mutateSecurityStore,
|
|
@@ -34,60 +36,43 @@ export const MCP_STDIO_PACKAGE_MATERIALIZATION_CHANGED_CODE =
|
|
|
34
36
|
"CC_MCP_STDIO_PACKAGE_MATERIALIZATION_CHANGED";
|
|
35
37
|
|
|
36
38
|
const MATERIALIZATION_SCHEMA =
|
|
37
|
-
"chainlesschain.mcp-stdio-package-materialization/
|
|
38
|
-
const MATERIALIZATION_VERSION =
|
|
39
|
-
const CAPSULE_SCHEMA = "chainlesschain.mcp-stdio-node-capsule/
|
|
39
|
+
"chainlesschain.mcp-stdio-package-materialization/v3";
|
|
40
|
+
const MATERIALIZATION_VERSION = 3;
|
|
41
|
+
const CAPSULE_SCHEMA = "chainlesschain.mcp-stdio-node-capsule/v2";
|
|
40
42
|
const CAPSULE_RELATIVE_PATH = "capsule/server.cjs";
|
|
41
|
-
const CAPSULE_BUILDER = "esbuild";
|
|
43
|
+
const CAPSULE_BUILDER = "esbuild-wasm";
|
|
42
44
|
const CAPSULE_BUILDER_VERSION = "0.28.1";
|
|
43
|
-
const
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"linux-x64": Object.freeze({
|
|
66
|
-
packageName: "@esbuild/linux-x64",
|
|
67
|
-
relativePath: "bin/esbuild",
|
|
68
|
-
sha256: "0c6588b092a2c291a72bab90659f3c9e0e25e0fe59c9ac12b4dae4d945e5548c",
|
|
69
|
-
}),
|
|
70
|
-
"win32-arm64": Object.freeze({
|
|
71
|
-
packageName: "@esbuild/win32-arm64",
|
|
72
|
-
relativePath: "esbuild.exe",
|
|
73
|
-
sha256: "bfb8798ab678f1ce4a723739f4a3eabab3244d7a04eeb12be2eb9f58095c13ef",
|
|
74
|
-
}),
|
|
75
|
-
"win32-ia32": Object.freeze({
|
|
76
|
-
packageName: "@esbuild/win32-ia32",
|
|
77
|
-
relativePath: "esbuild.exe",
|
|
78
|
-
sha256: "8fa99b6e0945830fce8d7e208fdb21763aa4aea875751f4a0eec7f6e262af1dd",
|
|
79
|
-
}),
|
|
80
|
-
"win32-x64": Object.freeze({
|
|
81
|
-
packageName: "@esbuild/win32-x64",
|
|
82
|
-
relativePath: "esbuild.exe",
|
|
83
|
-
sha256: "ec02ee9b14ab332416fedd10614dfb80eed5304d94f67745067c011934a8c3c3",
|
|
84
|
-
}),
|
|
85
|
-
});
|
|
45
|
+
const CAPSULE_BUILDER_WASM_SHA256 =
|
|
46
|
+
"cc8c5e14db584cd75c6c9fc16e1aae3d5b8e99ab7f333aeee71f59e23fa9f24e";
|
|
47
|
+
const CAPSULE_BUILDER_WASM_BYTES = 13_940_120;
|
|
48
|
+
const CAPSULE_BUILDER_API_SHA256 =
|
|
49
|
+
"4ec4cc1b6f2fdd0a117aa20ab5c49da9868ce7329322082a457927cdc64d89c1";
|
|
50
|
+
const CAPSULE_BUILDER_API_BYTES = 133_235;
|
|
51
|
+
const CAPSULE_RESOLVER_SCHEMA =
|
|
52
|
+
"chainlesschain.mcp-stdio-immutable-vfs-resolver/v1";
|
|
53
|
+
const CAPSULE_WORKER_SCHEMA =
|
|
54
|
+
"chainlesschain.mcp-stdio-capsule-builder-worker/v1";
|
|
55
|
+
const CAPSULE_BUILD_TIMEOUT_MS = 120_000;
|
|
56
|
+
const CAPSULE_WORKER_MAX_OLD_GENERATION_MB = 256;
|
|
57
|
+
// Updated with the checked-in source digest whenever either pinned host module
|
|
58
|
+
// changes. The worker receives source bytes, never a pathname to execute.
|
|
59
|
+
const CAPSULE_BUILDER_WORKER_SHA256 =
|
|
60
|
+
"1386f5ad51ba9e946fbf676d8aba4c8a11b3219916a413ac6fc24578d998ace1";
|
|
61
|
+
const CAPSULE_BUILDER_WORKER_BYTES = 16_293;
|
|
62
|
+
const CAPSULE_RESOLVER_SHA256 =
|
|
63
|
+
"48e1fc27454e6dcfd3018849912699e6ef8417db1716d1c8eff2cd1072a786d2";
|
|
64
|
+
const CAPSULE_RESOLVER_BYTES = 24_417;
|
|
65
|
+
const CAPSULE_STDIN_WRAPPER_SCHEMA =
|
|
66
|
+
"chainlesschain.mcp-stdio-capsule-stdin-wrapper/v1";
|
|
86
67
|
const INDEX_LABEL = "MCP stdio package materialization index";
|
|
87
68
|
const MANIFEST_LABEL = "MCP stdio package materialization manifest";
|
|
88
|
-
const MAX_FILES =
|
|
89
|
-
|
|
90
|
-
|
|
69
|
+
const MAX_FILES = 10_000;
|
|
70
|
+
// Source bytes cross a Worker boundary and are copied into the immutable VFS
|
|
71
|
+
// and browser realm. Keep the external-memory ceiling well below the V8 heap
|
|
72
|
+
// limit because Worker resourceLimits do not bound ArrayBuffer/WASM memory.
|
|
73
|
+
const MAX_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
74
|
+
const MAX_FILE_BYTES = 16 * 1024 * 1024;
|
|
75
|
+
const MAX_CAPSULE_BYTES = 32 * 1024 * 1024;
|
|
91
76
|
const MAX_DEPTH = 64;
|
|
92
77
|
const HASH_CHUNK_BYTES = 1024 * 1024;
|
|
93
78
|
const EXACT_VERSION =
|
|
@@ -96,7 +81,9 @@ const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i;
|
|
|
96
81
|
|
|
97
82
|
export const _deps = {
|
|
98
83
|
fs,
|
|
84
|
+
onVfsSnapshotCaptured: null,
|
|
99
85
|
processBrokerRunSync: null,
|
|
86
|
+
Worker,
|
|
100
87
|
};
|
|
101
88
|
|
|
102
89
|
const require = createRequire(import.meta.url);
|
|
@@ -141,7 +128,7 @@ export function getMcpStdioPackageMaterializationRoot(options = {}) {
|
|
|
141
128
|
return path.resolve(
|
|
142
129
|
options.root ||
|
|
143
130
|
process.env.CC_MCP_PACKAGE_MATERIALIZATION_ROOT ||
|
|
144
|
-
path.join(getCacheDir(), "mcp-stdio-package-materializations-
|
|
131
|
+
path.join(getCacheDir(), "mcp-stdio-package-materializations-v3"),
|
|
145
132
|
);
|
|
146
133
|
}
|
|
147
134
|
|
|
@@ -151,7 +138,7 @@ export function getMcpStdioPackageMaterializationIndexPath(options = {}) {
|
|
|
151
138
|
process.env.CC_MCP_PACKAGE_MATERIALIZATION_INDEX ||
|
|
152
139
|
path.join(
|
|
153
140
|
getMachineSecurityAnchorDir(),
|
|
154
|
-
"mcp-stdio-package-materializations-
|
|
141
|
+
"mcp-stdio-package-materializations-v3.json",
|
|
155
142
|
),
|
|
156
143
|
);
|
|
157
144
|
}
|
|
@@ -523,6 +510,10 @@ function resolvePackageEntrypoint(treeRoot, exact, requestedBin) {
|
|
|
523
510
|
});
|
|
524
511
|
}
|
|
525
512
|
|
|
513
|
+
function materializationFileMode(mode) {
|
|
514
|
+
return Number(mode) & (process.platform === "win32" ? 0o666 : 0o777);
|
|
515
|
+
}
|
|
516
|
+
|
|
526
517
|
function hashFile(file, expectedBytes) {
|
|
527
518
|
let descriptor;
|
|
528
519
|
try {
|
|
@@ -563,13 +554,26 @@ function hashFile(file, expectedBytes) {
|
|
|
563
554
|
) {
|
|
564
555
|
throw new Error("file changed while it was being hashed");
|
|
565
556
|
}
|
|
566
|
-
return
|
|
557
|
+
return Object.freeze({
|
|
558
|
+
sha256: digest.digest("hex"),
|
|
559
|
+
// Node can synthesize pathname and descriptor execute bits differently
|
|
560
|
+
// on Windows. Record the descriptor view used to read the bytes and keep
|
|
561
|
+
// only permissions; the regular-file type is validated independently.
|
|
562
|
+
mode: materializationFileMode(after.mode),
|
|
563
|
+
});
|
|
567
564
|
} finally {
|
|
568
565
|
if (descriptor !== undefined) _deps.fs.closeSync(descriptor);
|
|
569
566
|
}
|
|
570
567
|
}
|
|
571
568
|
|
|
572
|
-
function collectTree(
|
|
569
|
+
function collectTree(
|
|
570
|
+
root,
|
|
571
|
+
{
|
|
572
|
+
maxFiles = MAX_FILES,
|
|
573
|
+
maxTotalBytes = MAX_TOTAL_BYTES,
|
|
574
|
+
maxFileBytes = MAX_FILE_BYTES,
|
|
575
|
+
} = {},
|
|
576
|
+
) {
|
|
573
577
|
const files = [];
|
|
574
578
|
let totalBytes = 0;
|
|
575
579
|
const visit = (directory, depth) => {
|
|
@@ -598,22 +602,23 @@ function collectTree(root) {
|
|
|
598
602
|
);
|
|
599
603
|
}
|
|
600
604
|
const stat = _deps.fs.statSync(absolute, { bigint: true });
|
|
601
|
-
if (stat.size < 0n || stat.size > BigInt(
|
|
605
|
+
if (stat.size < 0n || stat.size > BigInt(maxFileBytes)) {
|
|
602
606
|
throw new Error(
|
|
603
607
|
`materialized file exceeds the size limit: ${relative}`,
|
|
604
608
|
);
|
|
605
609
|
}
|
|
606
610
|
totalBytes += Number(stat.size);
|
|
607
|
-
if (files.length >=
|
|
611
|
+
if (files.length >= maxFiles || totalBytes > maxTotalBytes) {
|
|
608
612
|
throw new Error(
|
|
609
613
|
"materialized dependency tree exceeds its aggregate budget",
|
|
610
614
|
);
|
|
611
615
|
}
|
|
616
|
+
const attested = hashFile(absolute, Number(stat.size));
|
|
612
617
|
files.push({
|
|
613
618
|
path: relative,
|
|
614
619
|
bytes: Number(stat.size),
|
|
615
|
-
mode:
|
|
616
|
-
sha256:
|
|
620
|
+
mode: attested.mode,
|
|
621
|
+
sha256: attested.sha256,
|
|
617
622
|
});
|
|
618
623
|
}
|
|
619
624
|
};
|
|
@@ -648,172 +653,392 @@ function resolveContainedPath(root, relative, label) {
|
|
|
648
653
|
return resolved;
|
|
649
654
|
}
|
|
650
655
|
|
|
651
|
-
function
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
+
function snapshotFileIdentity(stat) {
|
|
657
|
+
return Object.freeze({
|
|
658
|
+
dev: stat.dev,
|
|
659
|
+
ino: stat.ino,
|
|
660
|
+
mode: stat.mode,
|
|
661
|
+
size: stat.size,
|
|
662
|
+
mtimeNs: stat.mtimeNs,
|
|
663
|
+
ctimeNs: stat.ctimeNs,
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function sameSnapshotFileIdentity(left, right) {
|
|
668
|
+
return Boolean(
|
|
669
|
+
left &&
|
|
670
|
+
right &&
|
|
671
|
+
left.dev === right.dev &&
|
|
672
|
+
left.ino === right.ino &&
|
|
673
|
+
left.mode === right.mode &&
|
|
674
|
+
left.size === right.size &&
|
|
675
|
+
left.mtimeNs === right.mtimeNs &&
|
|
676
|
+
left.ctimeNs === right.ctimeNs,
|
|
656
677
|
);
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function openSnapshotInputDescriptor(file) {
|
|
681
|
+
return _deps.fs.openSync(
|
|
682
|
+
file,
|
|
683
|
+
Number(_deps.fs.constants.O_RDONLY) |
|
|
684
|
+
Number(_deps.fs.constants.O_NOFOLLOW || 0) |
|
|
685
|
+
Number(_deps.fs.constants.O_NONBLOCK || 0),
|
|
661
686
|
);
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
let sourceDescriptor;
|
|
667
|
-
let destinationDescriptor;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function snapshotIdentityThroughDescriptor(file) {
|
|
690
|
+
let descriptor;
|
|
668
691
|
try {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
Number(_deps.fs.constants.O_NONBLOCK || 0),
|
|
674
|
-
);
|
|
675
|
-
const before = _deps.fs.fstatSync(sourceDescriptor, { bigint: true });
|
|
676
|
-
if (!before.isFile() || before.size !== BigInt(record.bytes)) {
|
|
677
|
-
throw new Error(`MCP capsule source changed before copy: ${record.path}`);
|
|
692
|
+
descriptor = openSnapshotInputDescriptor(file);
|
|
693
|
+
const stat = _deps.fs.fstatSync(descriptor, { bigint: true });
|
|
694
|
+
if (!stat.isFile()) {
|
|
695
|
+
throw new Error("MCP capsule build input descriptor is not regular");
|
|
678
696
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
697
|
+
return snapshotFileIdentity(stat);
|
|
698
|
+
} finally {
|
|
699
|
+
if (descriptor !== undefined) _deps.fs.closeSync(descriptor);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function readSnapshotInputAttestation(canonicalSnapshotRoot, inputRelative) {
|
|
704
|
+
const realpath = _deps.fs.realpathSync.native || _deps.fs.realpathSync;
|
|
705
|
+
const requested = resolveContainedPath(
|
|
706
|
+
canonicalSnapshotRoot,
|
|
707
|
+
inputRelative,
|
|
708
|
+
"MCP capsule build input",
|
|
709
|
+
);
|
|
710
|
+
const pathBefore = _deps.fs.lstatSync(requested, { bigint: true });
|
|
711
|
+
if (pathBefore.isSymbolicLink() || !pathBefore.isFile()) {
|
|
712
|
+
throw new Error(
|
|
713
|
+
`MCP capsule build input is not one regular file: ${inputRelative}`,
|
|
686
714
|
);
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
715
|
+
}
|
|
716
|
+
const canonicalPath = realpath(requested);
|
|
717
|
+
const reboundRelative = path
|
|
718
|
+
.relative(canonicalSnapshotRoot, canonicalPath)
|
|
719
|
+
.split(path.sep)
|
|
720
|
+
.join("/");
|
|
721
|
+
if (reboundRelative !== inputRelative) {
|
|
722
|
+
throw new Error(
|
|
723
|
+
`MCP capsule build input changed through a path alias: ${inputRelative}`,
|
|
690
724
|
);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
let descriptor;
|
|
728
|
+
try {
|
|
729
|
+
descriptor = openSnapshotInputDescriptor(canonicalPath);
|
|
730
|
+
const descriptorBefore = _deps.fs.fstatSync(descriptor, { bigint: true });
|
|
731
|
+
const pathIdentityBefore = snapshotFileIdentity(pathBefore);
|
|
732
|
+
const descriptorIdentityBefore = snapshotFileIdentity(descriptorBefore);
|
|
733
|
+
// Node's Windows pathname and descriptor stat projections do not share a
|
|
734
|
+
// stable dev/ino namespace on every filesystem. Bind the pathname to the
|
|
735
|
+
// primary reader with a second descriptor instead: descriptor-to-
|
|
736
|
+
// descriptor identity is stable, while pathname metadata is checked only
|
|
737
|
+
// against the later pathname view. This retains file-ID binding without
|
|
738
|
+
// falling back to mutable size/timestamp comparisons.
|
|
739
|
+
const pathnameDescriptorIdentityBefore =
|
|
740
|
+
snapshotIdentityThroughDescriptor(canonicalPath);
|
|
741
|
+
if (
|
|
742
|
+
!descriptorBefore.isFile() ||
|
|
743
|
+
descriptorBefore.size < 0n ||
|
|
744
|
+
descriptorBefore.size > BigInt(MAX_FILE_BYTES) ||
|
|
745
|
+
!sameSnapshotFileIdentity(
|
|
746
|
+
descriptorIdentityBefore,
|
|
747
|
+
pathnameDescriptorIdentityBefore,
|
|
748
|
+
)
|
|
749
|
+
) {
|
|
750
|
+
throw new Error(
|
|
751
|
+
`MCP capsule build input identity changed before read: ${inputRelative}`,
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const bytes = Number(descriptorBefore.size);
|
|
756
|
+
// Slow buffers own their ArrayBuffer, so ownership can be transferred to
|
|
757
|
+
// the isolated builder Worker without detaching an unrelated slab.
|
|
758
|
+
const content = Buffer.allocUnsafeSlow(bytes);
|
|
691
759
|
let offset = 0;
|
|
692
|
-
while (offset <
|
|
760
|
+
while (offset < bytes) {
|
|
693
761
|
const count = _deps.fs.readSync(
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
762
|
+
descriptor,
|
|
763
|
+
content,
|
|
764
|
+
offset,
|
|
765
|
+
bytes - offset,
|
|
698
766
|
offset,
|
|
699
767
|
);
|
|
700
768
|
if (count <= 0) {
|
|
701
|
-
throw new Error(
|
|
702
|
-
|
|
703
|
-
digest.update(chunk.subarray(0, count));
|
|
704
|
-
let written = 0;
|
|
705
|
-
while (written < count) {
|
|
706
|
-
const writeCount = _deps.fs.writeSync(
|
|
707
|
-
destinationDescriptor,
|
|
708
|
-
chunk,
|
|
709
|
-
written,
|
|
710
|
-
count - written,
|
|
711
|
-
offset + written,
|
|
769
|
+
throw new Error(
|
|
770
|
+
`MCP capsule build input ended during read: ${inputRelative}`,
|
|
712
771
|
);
|
|
713
|
-
if (writeCount <= 0) {
|
|
714
|
-
throw new Error(
|
|
715
|
-
`MCP capsule snapshot ended during copy: ${record.path}`,
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
written += writeCount;
|
|
719
772
|
}
|
|
720
773
|
offset += count;
|
|
721
774
|
}
|
|
722
|
-
|
|
775
|
+
|
|
776
|
+
const descriptorAfter = _deps.fs.fstatSync(descriptor, { bigint: true });
|
|
777
|
+
const pathAfter = _deps.fs.lstatSync(requested, { bigint: true });
|
|
778
|
+
const canonicalPathAfter = realpath(requested);
|
|
723
779
|
if (
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
after.mtimeNs !== before.mtimeNs ||
|
|
728
|
-
after.ctimeNs !== before.ctimeNs ||
|
|
729
|
-
digest.digest("hex") !== record.sha256
|
|
780
|
+
pathAfter.isSymbolicLink() ||
|
|
781
|
+
!pathAfter.isFile() ||
|
|
782
|
+
canonicalPathAfter !== canonicalPath
|
|
730
783
|
) {
|
|
731
|
-
throw new Error(
|
|
784
|
+
throw new Error(
|
|
785
|
+
`MCP capsule build input changed during read: ${inputRelative}`,
|
|
786
|
+
);
|
|
732
787
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
788
|
+
const descriptorIdentityAfter = snapshotFileIdentity(descriptorAfter);
|
|
789
|
+
const pathIdentityAfter = snapshotFileIdentity(pathAfter);
|
|
790
|
+
const pathnameDescriptorIdentityAfter =
|
|
791
|
+
snapshotIdentityThroughDescriptor(canonicalPathAfter);
|
|
792
|
+
if (
|
|
793
|
+
!sameSnapshotFileIdentity(
|
|
794
|
+
descriptorIdentityBefore,
|
|
795
|
+
descriptorIdentityAfter,
|
|
796
|
+
) ||
|
|
797
|
+
!sameSnapshotFileIdentity(pathIdentityBefore, pathIdentityAfter) ||
|
|
798
|
+
!sameSnapshotFileIdentity(
|
|
799
|
+
descriptorIdentityAfter,
|
|
800
|
+
pathnameDescriptorIdentityAfter,
|
|
801
|
+
)
|
|
802
|
+
) {
|
|
803
|
+
throw new Error(
|
|
804
|
+
`MCP capsule build input changed during read: ${inputRelative}`,
|
|
805
|
+
);
|
|
738
806
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
807
|
+
return Object.freeze({
|
|
808
|
+
path: inputRelative,
|
|
809
|
+
canonicalPath,
|
|
810
|
+
bytes,
|
|
811
|
+
sha256: sha256(content),
|
|
812
|
+
identity: descriptorIdentityAfter,
|
|
813
|
+
content,
|
|
814
|
+
});
|
|
815
|
+
} finally {
|
|
816
|
+
if (descriptor !== undefined) _deps.fs.closeSync(descriptor);
|
|
743
817
|
}
|
|
744
818
|
}
|
|
745
819
|
|
|
746
|
-
function
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
755
|
-
const observed = collectTree(snapshotRoot);
|
|
820
|
+
function readPinnedCapsuleAsset({
|
|
821
|
+
root,
|
|
822
|
+
relativePath,
|
|
823
|
+
bytes,
|
|
824
|
+
sha256: expectedSha256,
|
|
825
|
+
label,
|
|
826
|
+
}) {
|
|
827
|
+
const observed = readSnapshotInputAttestation(root, relativePath);
|
|
756
828
|
if (
|
|
757
|
-
observed.
|
|
758
|
-
observed.
|
|
759
|
-
observed.
|
|
760
|
-
canonicalJson(observed.files) !== canonicalJson(closure.files)
|
|
829
|
+
observed.bytes !== bytes ||
|
|
830
|
+
observed.sha256 !== expectedSha256 ||
|
|
831
|
+
materializationFileMode(observed.identity.mode) === 0
|
|
761
832
|
) {
|
|
762
|
-
throw new Error(
|
|
763
|
-
"MCP capsule source snapshot does not match its attestation",
|
|
764
|
-
);
|
|
833
|
+
throw new Error(`${label} identity is invalid`);
|
|
765
834
|
}
|
|
766
|
-
return
|
|
835
|
+
return observed;
|
|
767
836
|
}
|
|
768
837
|
|
|
769
|
-
function
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
`${expected.packageName}/package.json`,
|
|
838
|
+
function resolvePinnedCapsuleBuilder() {
|
|
839
|
+
// Vitest can evaluate this module through a non-file URL when desktop tests
|
|
840
|
+
// import the CLI process broker transitively. Resolve host-only assets only
|
|
841
|
+
// when a capsule build actually needs them; production builds still fail
|
|
842
|
+
// closed if the module does not have a file-backed identity.
|
|
843
|
+
const workerPath = fileURLToPath(
|
|
844
|
+
new URL("./mcp-stdio-capsule-builder-worker.cjs", import.meta.url),
|
|
777
845
|
);
|
|
778
|
-
const
|
|
779
|
-
|
|
846
|
+
const resolverPath = fileURLToPath(
|
|
847
|
+
new URL("./mcp-stdio-immutable-vfs-resolver.cjs", import.meta.url),
|
|
780
848
|
);
|
|
849
|
+
const realpath = _deps.fs.realpathSync.native || _deps.fs.realpathSync;
|
|
850
|
+
const packageJsonPath = realpath(
|
|
851
|
+
require.resolve("esbuild-wasm/package.json"),
|
|
852
|
+
);
|
|
853
|
+
const packageRoot = path.dirname(packageJsonPath);
|
|
854
|
+
const packageJsonAsset = readSnapshotInputAttestation(
|
|
855
|
+
packageRoot,
|
|
856
|
+
"package.json",
|
|
857
|
+
);
|
|
858
|
+
let packageJson;
|
|
859
|
+
try {
|
|
860
|
+
packageJson = JSON.parse(packageJsonAsset.content.toString("utf8"));
|
|
861
|
+
} catch {
|
|
862
|
+
throw new Error("MCP capsule builder package identity is invalid");
|
|
863
|
+
}
|
|
781
864
|
if (
|
|
782
|
-
packageJson.name !==
|
|
865
|
+
packageJson.name !== CAPSULE_BUILDER ||
|
|
783
866
|
packageJson.version !== CAPSULE_BUILDER_VERSION
|
|
784
867
|
) {
|
|
785
868
|
throw new Error(
|
|
786
|
-
`MCP capsule builder must be ${
|
|
869
|
+
`MCP capsule builder must be ${CAPSULE_BUILDER}@${CAPSULE_BUILDER_VERSION}`,
|
|
787
870
|
);
|
|
788
871
|
}
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
)
|
|
803
|
-
|
|
804
|
-
|
|
872
|
+
const api = readPinnedCapsuleAsset({
|
|
873
|
+
root: packageRoot,
|
|
874
|
+
relativePath: "lib/browser.js",
|
|
875
|
+
bytes: CAPSULE_BUILDER_API_BYTES,
|
|
876
|
+
sha256: CAPSULE_BUILDER_API_SHA256,
|
|
877
|
+
label: "MCP capsule builder API",
|
|
878
|
+
});
|
|
879
|
+
const wasm = readPinnedCapsuleAsset({
|
|
880
|
+
root: packageRoot,
|
|
881
|
+
relativePath: "esbuild.wasm",
|
|
882
|
+
bytes: CAPSULE_BUILDER_WASM_BYTES,
|
|
883
|
+
sha256: CAPSULE_BUILDER_WASM_SHA256,
|
|
884
|
+
label: "MCP capsule builder WASM",
|
|
885
|
+
});
|
|
886
|
+
const worker = readPinnedCapsuleAsset({
|
|
887
|
+
root: path.dirname(workerPath),
|
|
888
|
+
relativePath: path.basename(workerPath),
|
|
889
|
+
bytes: CAPSULE_BUILDER_WORKER_BYTES,
|
|
890
|
+
sha256: CAPSULE_BUILDER_WORKER_SHA256,
|
|
891
|
+
label: "MCP capsule builder Worker",
|
|
892
|
+
});
|
|
893
|
+
const resolver = readPinnedCapsuleAsset({
|
|
894
|
+
root: path.dirname(resolverPath),
|
|
895
|
+
relativePath: path.basename(resolverPath),
|
|
896
|
+
bytes: CAPSULE_RESOLVER_BYTES,
|
|
897
|
+
sha256: CAPSULE_RESOLVER_SHA256,
|
|
898
|
+
label: "MCP capsule immutable VFS resolver",
|
|
899
|
+
});
|
|
805
900
|
return Object.freeze({
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
901
|
+
apiSource: api.content.toString("utf8"),
|
|
902
|
+
apiBytes: api.bytes,
|
|
903
|
+
apiSha256: api.sha256,
|
|
904
|
+
wasmBytes: wasm.content,
|
|
905
|
+
wasmSha256: wasm.sha256,
|
|
906
|
+
workerSource: worker.content.toString("utf8"),
|
|
907
|
+
workerSha256: worker.sha256,
|
|
908
|
+
resolverSource: resolver.content.toString("utf8"),
|
|
909
|
+
resolverSha256: resolver.sha256,
|
|
809
910
|
});
|
|
810
911
|
}
|
|
811
912
|
|
|
913
|
+
function capsuleWorkerError(payload) {
|
|
914
|
+
const error = new Error(
|
|
915
|
+
String(payload?.message || "MCP capsule builder Worker failed").slice(
|
|
916
|
+
0,
|
|
917
|
+
8_000,
|
|
918
|
+
),
|
|
919
|
+
);
|
|
920
|
+
error.name = String(payload?.name || "Error").slice(0, 200);
|
|
921
|
+
if (typeof payload?.code === "string") error.code = payload.code;
|
|
922
|
+
if (typeof payload?.stack === "string") error.stack = payload.stack;
|
|
923
|
+
return error;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
async function runCapsuleBuilderWorker({
|
|
927
|
+
builder,
|
|
928
|
+
files,
|
|
929
|
+
fileCount,
|
|
930
|
+
totalBytes,
|
|
931
|
+
entryPath,
|
|
932
|
+
vfsRoot,
|
|
933
|
+
banner,
|
|
934
|
+
}) {
|
|
935
|
+
const nonce = crypto.randomBytes(32).toString("hex");
|
|
936
|
+
const transferList = [builder.wasmBytes.buffer];
|
|
937
|
+
for (const record of files) transferList.push(record[1].buffer);
|
|
938
|
+
let worker;
|
|
939
|
+
let timer;
|
|
940
|
+
const listeners = {};
|
|
941
|
+
try {
|
|
942
|
+
worker = new _deps.Worker(builder.workerSource, {
|
|
943
|
+
eval: true,
|
|
944
|
+
resourceLimits: {
|
|
945
|
+
maxOldGenerationSizeMb: CAPSULE_WORKER_MAX_OLD_GENERATION_MB,
|
|
946
|
+
maxYoungGenerationSizeMb: 64,
|
|
947
|
+
stackSizeMb: 8,
|
|
948
|
+
},
|
|
949
|
+
transferList,
|
|
950
|
+
workerData: {
|
|
951
|
+
schema: CAPSULE_WORKER_SCHEMA,
|
|
952
|
+
nonce,
|
|
953
|
+
browserApiSource: builder.apiSource,
|
|
954
|
+
resolverSource: builder.resolverSource,
|
|
955
|
+
wasmBytes: builder.wasmBytes,
|
|
956
|
+
builderVersion: CAPSULE_BUILDER_VERSION,
|
|
957
|
+
files,
|
|
958
|
+
fileCount,
|
|
959
|
+
totalBytes,
|
|
960
|
+
entryPath,
|
|
961
|
+
vfsRoot,
|
|
962
|
+
banner,
|
|
963
|
+
maxOutputBytes: MAX_CAPSULE_BYTES,
|
|
964
|
+
maxMetafileBytes: 16 * 1024 * 1024,
|
|
965
|
+
},
|
|
966
|
+
});
|
|
967
|
+
return await new Promise((resolve, reject) => {
|
|
968
|
+
let settled = false;
|
|
969
|
+
let terminal = null;
|
|
970
|
+
const fail = (error) => {
|
|
971
|
+
if (settled) return;
|
|
972
|
+
settled = true;
|
|
973
|
+
reject(error);
|
|
974
|
+
};
|
|
975
|
+
listeners.message = (message) => {
|
|
976
|
+
if (terminal) {
|
|
977
|
+
fail(
|
|
978
|
+
new Error(
|
|
979
|
+
"MCP capsule builder Worker emitted two terminal messages",
|
|
980
|
+
),
|
|
981
|
+
);
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
if (!message || message.nonce !== nonce) {
|
|
985
|
+
fail(new Error("MCP capsule builder Worker nonce mismatch"));
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
terminal = message;
|
|
989
|
+
};
|
|
990
|
+
listeners.messageerror = () => {
|
|
991
|
+
fail(new Error("MCP capsule builder Worker message was not cloneable"));
|
|
992
|
+
};
|
|
993
|
+
listeners.error = (error) => fail(error);
|
|
994
|
+
listeners.exit = (code) => {
|
|
995
|
+
if (settled) return;
|
|
996
|
+
if (code !== 0) {
|
|
997
|
+
fail(
|
|
998
|
+
new Error(`MCP capsule builder Worker exited with status ${code}`),
|
|
999
|
+
);
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (!terminal) {
|
|
1003
|
+
fail(new Error("MCP capsule builder Worker exited without a result"));
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
if (terminal.ok !== true) {
|
|
1007
|
+
fail(capsuleWorkerError(terminal.error));
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
settled = true;
|
|
1011
|
+
resolve(terminal);
|
|
1012
|
+
};
|
|
1013
|
+
worker.on("message", listeners.message);
|
|
1014
|
+
worker.once("messageerror", listeners.messageerror);
|
|
1015
|
+
worker.once("error", listeners.error);
|
|
1016
|
+
worker.once("exit", listeners.exit);
|
|
1017
|
+
timer = setTimeout(() => {
|
|
1018
|
+
fail(new Error("MCP capsule builder Worker timed out"));
|
|
1019
|
+
}, CAPSULE_BUILD_TIMEOUT_MS);
|
|
1020
|
+
timer.unref?.();
|
|
1021
|
+
});
|
|
1022
|
+
} finally {
|
|
1023
|
+
if (timer) clearTimeout(timer);
|
|
1024
|
+
if (worker) {
|
|
1025
|
+
if (listeners.message) worker.off("message", listeners.message);
|
|
1026
|
+
if (listeners.messageerror) {
|
|
1027
|
+
worker.off("messageerror", listeners.messageerror);
|
|
1028
|
+
}
|
|
1029
|
+
if (listeners.error) worker.off("error", listeners.error);
|
|
1030
|
+
if (listeners.exit) worker.off("exit", listeners.exit);
|
|
1031
|
+
await worker.terminate();
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
812
1036
|
function capsuleRuntimeGuard() {
|
|
813
1037
|
return `;(() => {
|
|
814
1038
|
const Module = require("node:module");
|
|
815
1039
|
const allowed = new Set(Module.builtinModules.flatMap((name) => [name, name.startsWith("node:") ? name : "node:" + name]));
|
|
816
1040
|
const originalLoad = Module._load;
|
|
1041
|
+
const originalResolveFilename = Module._resolveFilename;
|
|
817
1042
|
const blocked = (kind, request) => {
|
|
818
1043
|
const error = new Error("MCP stdio capsule blocked " + kind + (request === undefined ? "" : ": " + String(request)));
|
|
819
1044
|
error.code = kind === "native module loading" ? "CC_MCP_STDIO_NATIVE_MODULE_BLOCKED" : "CC_MCP_STDIO_EXTERNAL_MODULE_BLOCKED";
|
|
@@ -828,6 +1053,15 @@ function capsuleRuntimeGuard() {
|
|
|
828
1053
|
return Reflect.apply(originalLoad, this, arguments);
|
|
829
1054
|
},
|
|
830
1055
|
});
|
|
1056
|
+
Object.defineProperty(Module, "_resolveFilename", {
|
|
1057
|
+
configurable: false,
|
|
1058
|
+
enumerable: false,
|
|
1059
|
+
writable: false,
|
|
1060
|
+
value: function(request) {
|
|
1061
|
+
if (typeof request !== "string" || !allowed.has(request)) blocked("external module resolution", request);
|
|
1062
|
+
return Reflect.apply(originalResolveFilename, this, arguments);
|
|
1063
|
+
},
|
|
1064
|
+
});
|
|
831
1065
|
Object.defineProperty(process, "dlopen", {
|
|
832
1066
|
configurable: false,
|
|
833
1067
|
enumerable: false,
|
|
@@ -837,177 +1071,325 @@ function capsuleRuntimeGuard() {
|
|
|
837
1071
|
})();`;
|
|
838
1072
|
}
|
|
839
1073
|
|
|
840
|
-
function
|
|
1074
|
+
export function esbuildRelativeEntrypointArg(
|
|
1075
|
+
entrypointRelative,
|
|
1076
|
+
canonicalSnapshotRoot,
|
|
1077
|
+
canonicalEntrypoint,
|
|
1078
|
+
) {
|
|
1079
|
+
if (
|
|
1080
|
+
typeof entrypointRelative !== "string" ||
|
|
1081
|
+
entrypointRelative.length === 0 ||
|
|
1082
|
+
entrypointRelative.includes("\0") ||
|
|
1083
|
+
entrypointRelative.includes("\\") ||
|
|
1084
|
+
path.posix.isAbsolute(entrypointRelative) ||
|
|
1085
|
+
/^[A-Za-z]:/.test(entrypointRelative) ||
|
|
1086
|
+
path.posix.normalize(entrypointRelative) !== entrypointRelative ||
|
|
1087
|
+
entrypointRelative === "." ||
|
|
1088
|
+
entrypointRelative === ".." ||
|
|
1089
|
+
entrypointRelative.startsWith("../")
|
|
1090
|
+
) {
|
|
1091
|
+
throw new Error(
|
|
1092
|
+
"MCP capsule entrypoint must be one canonical relative POSIX path",
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
for (const [label, candidate] of [
|
|
1096
|
+
["snapshot root", canonicalSnapshotRoot],
|
|
1097
|
+
["entrypoint", canonicalEntrypoint],
|
|
1098
|
+
]) {
|
|
1099
|
+
if (
|
|
1100
|
+
typeof candidate !== "string" ||
|
|
1101
|
+
candidate.includes("\0") ||
|
|
1102
|
+
!path.isAbsolute(candidate) ||
|
|
1103
|
+
path.normalize(candidate) !== candidate
|
|
1104
|
+
) {
|
|
1105
|
+
throw new Error(`MCP capsule ${label} must be canonical and absolute`);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
const reboundRelative = path
|
|
1109
|
+
.relative(canonicalSnapshotRoot, canonicalEntrypoint)
|
|
1110
|
+
.split(path.sep)
|
|
1111
|
+
.join("/");
|
|
1112
|
+
if (reboundRelative !== entrypointRelative) {
|
|
1113
|
+
throw new Error("MCP capsule entrypoint escaped its canonical snapshot");
|
|
1114
|
+
}
|
|
1115
|
+
// This is a logical source label for esbuild's stdin mode, not a positional
|
|
1116
|
+
// entrypoint. Positional node_modules paths are package-classified by the
|
|
1117
|
+
// native Windows CLI even when they are absolute or repeatedly prefixed.
|
|
1118
|
+
return entrypointRelative;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
async function buildCapsule({
|
|
841
1122
|
treeRoot,
|
|
842
1123
|
entrypointRelative,
|
|
843
1124
|
closure,
|
|
844
1125
|
staging,
|
|
845
|
-
processBrokerRunSync,
|
|
846
|
-
env,
|
|
847
1126
|
}) {
|
|
848
|
-
const snapshotRoot = createAttestedSnapshot(treeRoot, closure, staging);
|
|
849
1127
|
const capsuleRoot = path.join(staging, "capsule");
|
|
850
1128
|
const capsulePath = path.join(staging, ...CAPSULE_RELATIVE_PATH.split("/"));
|
|
851
|
-
const metafilePath = path.join(
|
|
852
|
-
staging,
|
|
853
|
-
`.capsule-meta-${crypto.randomUUID()}.json`,
|
|
854
|
-
);
|
|
855
1129
|
const realpath = _deps.fs.realpathSync.native || _deps.fs.realpathSync;
|
|
856
|
-
const
|
|
1130
|
+
const canonicalTreeRoot = realpath(treeRoot);
|
|
1131
|
+
const vfsRoot = "/chainlesschain-source";
|
|
1132
|
+
const wrapperRelative = "__chainlesschain_capsule_entry__.cjs";
|
|
1133
|
+
const wrapperPath = `${vfsRoot}/${wrapperRelative}`;
|
|
1134
|
+
const maxOutputBytes = MAX_CAPSULE_BYTES;
|
|
1135
|
+
const maxMetafileBytes = 16 * 1024 * 1024;
|
|
857
1136
|
_deps.fs.mkdirSync(capsuleRoot, { recursive: false, mode: 0o700 });
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1137
|
+
const sourceEntrypoint = resolveContainedPath(
|
|
1138
|
+
canonicalTreeRoot,
|
|
1139
|
+
entrypointRelative,
|
|
1140
|
+
"MCP capsule entrypoint",
|
|
1141
|
+
);
|
|
1142
|
+
const canonicalEntrypoint = realpath(sourceEntrypoint);
|
|
1143
|
+
const boundEntrypointRelative = esbuildRelativeEntrypointArg(
|
|
1144
|
+
entrypointRelative,
|
|
1145
|
+
canonicalTreeRoot,
|
|
1146
|
+
canonicalEntrypoint,
|
|
1147
|
+
);
|
|
1148
|
+
if (closure.files.some((record) => record.path === wrapperRelative)) {
|
|
1149
|
+
throw new Error("MCP capsule source collides with its host wrapper");
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const closureByPath = new Map(
|
|
1153
|
+
closure.files.map((record) => [record.path, record]),
|
|
1154
|
+
);
|
|
1155
|
+
const expectedByVfsPath = new Map();
|
|
1156
|
+
const workerFiles = [];
|
|
1157
|
+
for (const record of closure.files) {
|
|
1158
|
+
const observed = readSnapshotInputAttestation(
|
|
1159
|
+
canonicalTreeRoot,
|
|
1160
|
+
record.path,
|
|
863
1161
|
);
|
|
864
|
-
|
|
865
|
-
.
|
|
866
|
-
.
|
|
867
|
-
.
|
|
868
|
-
|
|
869
|
-
throw new Error("MCP capsule entrypoint changed through a path alias");
|
|
870
|
-
}
|
|
871
|
-
const builderEntrypoint = `.${path.sep}${entrypointRelative
|
|
872
|
-
.split("/")
|
|
873
|
-
.join(path.sep)}`;
|
|
874
|
-
const builder = resolveCapsuleBuilderBinary();
|
|
875
|
-
const runThroughProcessBroker =
|
|
876
|
-
processBrokerRunSync || _deps.processBrokerRunSync;
|
|
877
|
-
if (typeof runThroughProcessBroker !== "function") {
|
|
1162
|
+
if (
|
|
1163
|
+
observed.bytes !== record.bytes ||
|
|
1164
|
+
observed.sha256 !== record.sha256 ||
|
|
1165
|
+
materializationFileMode(observed.identity.mode) !== record.mode
|
|
1166
|
+
) {
|
|
878
1167
|
throw new Error(
|
|
879
|
-
|
|
1168
|
+
`MCP capsule build input changed before capture: ${record.path}`,
|
|
880
1169
|
);
|
|
881
1170
|
}
|
|
882
|
-
const
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
1171
|
+
const vfsPath = `${vfsRoot}/${record.path}`;
|
|
1172
|
+
expectedByVfsPath.set(vfsPath, record);
|
|
1173
|
+
workerFiles.push([vfsPath, observed.content]);
|
|
1174
|
+
}
|
|
1175
|
+
if (!closureByPath.has(entrypointRelative)) {
|
|
1176
|
+
throw new Error("MCP capsule entrypoint is outside its closure");
|
|
1177
|
+
}
|
|
1178
|
+
const wrapperSpecifier = `./${boundEntrypointRelative}`;
|
|
1179
|
+
const wrapperSource = Buffer.from(
|
|
1180
|
+
`"use strict";\nrequire(${JSON.stringify(wrapperSpecifier)});\n`,
|
|
1181
|
+
"utf8",
|
|
1182
|
+
);
|
|
1183
|
+
const ownedWrapperSource = Buffer.allocUnsafeSlow(wrapperSource.length);
|
|
1184
|
+
wrapperSource.copy(ownedWrapperSource);
|
|
1185
|
+
const wrapperBytes = ownedWrapperSource.length;
|
|
1186
|
+
const wrapperSha256 = sha256(ownedWrapperSource);
|
|
1187
|
+
expectedByVfsPath.set(wrapperPath, {
|
|
1188
|
+
path: wrapperRelative,
|
|
1189
|
+
bytes: wrapperBytes,
|
|
1190
|
+
sha256: wrapperSha256,
|
|
1191
|
+
wrapper: true,
|
|
1192
|
+
});
|
|
1193
|
+
workerFiles.push([wrapperPath, ownedWrapperSource]);
|
|
1194
|
+
|
|
1195
|
+
const builder = resolvePinnedCapsuleBuilder();
|
|
1196
|
+
if (typeof _deps.onVfsSnapshotCaptured === "function") {
|
|
1197
|
+
await _deps.onVfsSnapshotCaptured(
|
|
1198
|
+
Object.freeze({
|
|
1199
|
+
treeRoot: canonicalTreeRoot,
|
|
1200
|
+
fileCount: closure.fileCount,
|
|
1201
|
+
totalBytes: closure.totalBytes,
|
|
1202
|
+
closureDigest: closure.closureDigest,
|
|
1203
|
+
}),
|
|
909
1204
|
);
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1205
|
+
}
|
|
1206
|
+
const result = await runCapsuleBuilderWorker({
|
|
1207
|
+
builder,
|
|
1208
|
+
files: workerFiles,
|
|
1209
|
+
fileCount: workerFiles.length,
|
|
1210
|
+
totalBytes: closure.totalBytes + wrapperBytes,
|
|
1211
|
+
entryPath: wrapperPath,
|
|
1212
|
+
vfsRoot,
|
|
1213
|
+
banner: capsuleRuntimeGuard(),
|
|
1214
|
+
});
|
|
1215
|
+
if (
|
|
1216
|
+
!(result.output instanceof Uint8Array) ||
|
|
1217
|
+
result.output.byteLength <= 0 ||
|
|
1218
|
+
result.output.byteLength > maxOutputBytes ||
|
|
1219
|
+
!result.metafile ||
|
|
1220
|
+
typeof result.metafile !== "object" ||
|
|
1221
|
+
!result.metafile.inputs ||
|
|
1222
|
+
typeof result.metafile.inputs !== "object" ||
|
|
1223
|
+
!result.metafile.outputs ||
|
|
1224
|
+
typeof result.metafile.outputs !== "object" ||
|
|
1225
|
+
Buffer.byteLength(JSON.stringify(result.metafile)) > maxMetafileBytes ||
|
|
1226
|
+
!Array.isArray(result.warnings) ||
|
|
1227
|
+
result.warnings.length !== 0 ||
|
|
1228
|
+
!result.audit ||
|
|
1229
|
+
result.audit.root !== vfsRoot ||
|
|
1230
|
+
result.audit.fileCount !== workerFiles.length ||
|
|
1231
|
+
!Array.isArray(result.audit.loaded) ||
|
|
1232
|
+
!Array.isArray(result.audit.resolutions)
|
|
1233
|
+
) {
|
|
1234
|
+
throw new Error("MCP capsule builder Worker result is invalid");
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
const metafileInputs = new Map();
|
|
1238
|
+
for (const [input, metadata] of Object.entries(result.metafile.inputs)) {
|
|
1239
|
+
const prefix = "cc-immutable-vfs:";
|
|
1240
|
+
if (!input.startsWith(prefix)) {
|
|
1241
|
+
throw new Error(`MCP capsule build reported a non-VFS input: ${input}`);
|
|
918
1242
|
}
|
|
919
|
-
const
|
|
1243
|
+
const vfsPath = input.slice(prefix.length);
|
|
920
1244
|
if (
|
|
921
|
-
!
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
typeof metafile.outputs !== "object"
|
|
1245
|
+
!vfsPath.startsWith(`${vfsRoot}/`) ||
|
|
1246
|
+
path.posix.normalize(vfsPath) !== vfsPath ||
|
|
1247
|
+
metafileInputs.has(vfsPath) ||
|
|
1248
|
+
!metadata ||
|
|
1249
|
+
typeof metadata !== "object"
|
|
927
1250
|
) {
|
|
1251
|
+
throw new Error(`MCP capsule build reported an unsafe input: ${input}`);
|
|
1252
|
+
}
|
|
1253
|
+
metafileInputs.set(vfsPath, metadata);
|
|
1254
|
+
}
|
|
1255
|
+
const loaded = [...result.audit.loaded];
|
|
1256
|
+
if (
|
|
1257
|
+
new Set(loaded).size !== loaded.length ||
|
|
1258
|
+
loaded.some(
|
|
1259
|
+
(input) =>
|
|
1260
|
+
typeof input !== "string" ||
|
|
1261
|
+
!input.startsWith(`${vfsRoot}/`) ||
|
|
1262
|
+
path.posix.normalize(input) !== input,
|
|
1263
|
+
) ||
|
|
1264
|
+
canonicalJson([...metafileInputs.keys()].sort()) !==
|
|
1265
|
+
canonicalJson([...loaded].sort())
|
|
1266
|
+
) {
|
|
1267
|
+
throw new Error("MCP capsule metafile did not match immutable VFS loads");
|
|
1268
|
+
}
|
|
1269
|
+
const inputs = loaded
|
|
1270
|
+
.filter((input) => input !== wrapperPath)
|
|
1271
|
+
.sort()
|
|
1272
|
+
.map((input) => {
|
|
1273
|
+
const expected = expectedByVfsPath.get(input);
|
|
1274
|
+
const metadata = metafileInputs.get(input);
|
|
1275
|
+
if (!expected || expected.wrapper || metadata?.bytes !== expected.bytes) {
|
|
1276
|
+
throw new Error(`MCP capsule build used an unattested input: ${input}`);
|
|
1277
|
+
}
|
|
1278
|
+
return {
|
|
1279
|
+
path: expected.path,
|
|
1280
|
+
bytes: expected.bytes,
|
|
1281
|
+
sha256: expected.sha256,
|
|
1282
|
+
};
|
|
1283
|
+
});
|
|
1284
|
+
const wrapperMetadata = metafileInputs.get(wrapperPath);
|
|
1285
|
+
if (
|
|
1286
|
+
wrapperMetadata?.bytes !== wrapperBytes ||
|
|
1287
|
+
!inputs.some((input) => input.path === entrypointRelative)
|
|
1288
|
+
) {
|
|
1289
|
+
throw new Error("MCP capsule build did not bind its approved entrypoint");
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
const externalBuiltins = [];
|
|
1293
|
+
if (Object.keys(result.metafile.outputs).length !== 1) {
|
|
1294
|
+
throw new Error("MCP capsule build emitted an invalid output graph");
|
|
1295
|
+
}
|
|
1296
|
+
for (const output of Object.values(result.metafile.outputs)) {
|
|
1297
|
+
if (!output || typeof output !== "object") {
|
|
928
1298
|
throw new Error("MCP capsule build metadata is invalid");
|
|
929
1299
|
}
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
.map((input) => input.split(path.sep).join("/"))
|
|
935
|
-
.sort()
|
|
936
|
-
.map((input) => {
|
|
937
|
-
const absolute = realpath(
|
|
938
|
-
path.resolve(snapshotRoot, ...input.split("/")),
|
|
1300
|
+
for (const imported of output.imports || []) {
|
|
1301
|
+
if (!imported.external || !NODE_BUILTINS.has(imported.path)) {
|
|
1302
|
+
throw new Error(
|
|
1303
|
+
`MCP capsule build retained an external dependency: ${imported.path}`,
|
|
939
1304
|
);
|
|
940
|
-
const relative = path
|
|
941
|
-
.relative(canonicalSnapshotRoot, absolute)
|
|
942
|
-
.split(path.sep)
|
|
943
|
-
.join("/");
|
|
944
|
-
const record = sourceByPath.get(relative);
|
|
945
|
-
if (!record) {
|
|
946
|
-
throw new Error(
|
|
947
|
-
`MCP capsule build used an unattested input: ${input}`,
|
|
948
|
-
);
|
|
949
|
-
}
|
|
950
|
-
return {
|
|
951
|
-
path: relative,
|
|
952
|
-
bytes: record.bytes,
|
|
953
|
-
sha256: record.sha256,
|
|
954
|
-
};
|
|
955
|
-
});
|
|
956
|
-
if (new Set(inputs.map((input) => input.path)).size !== inputs.length) {
|
|
957
|
-
throw new Error("MCP capsule build reported duplicate input aliases");
|
|
958
|
-
}
|
|
959
|
-
if (
|
|
960
|
-
inputs.length === 0 ||
|
|
961
|
-
!inputs.some((input) => input.path === entrypointRelative)
|
|
962
|
-
) {
|
|
963
|
-
throw new Error("MCP capsule build did not bind its approved entrypoint");
|
|
964
|
-
}
|
|
965
|
-
const externalBuiltins = [];
|
|
966
|
-
for (const output of Object.values(metafile.outputs)) {
|
|
967
|
-
for (const imported of output.imports || []) {
|
|
968
|
-
if (!imported.external || !NODE_BUILTINS.has(imported.path)) {
|
|
969
|
-
throw new Error(
|
|
970
|
-
`MCP capsule build retained an external dependency: ${imported.path}`,
|
|
971
|
-
);
|
|
972
|
-
}
|
|
973
|
-
externalBuiltins.push(imported.path);
|
|
974
1305
|
}
|
|
1306
|
+
externalBuiltins.push(imported.path);
|
|
975
1307
|
}
|
|
976
|
-
|
|
1308
|
+
}
|
|
1309
|
+
for (const resolution of result.audit.resolutions) {
|
|
977
1310
|
if (
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1311
|
+
!resolution ||
|
|
1312
|
+
typeof resolution !== "object" ||
|
|
1313
|
+
(resolution.external === true
|
|
1314
|
+
? !NODE_BUILTINS.has(resolution.path)
|
|
1315
|
+
: !expectedByVfsPath.has(resolution.path))
|
|
982
1316
|
) {
|
|
983
|
-
throw new Error("MCP
|
|
1317
|
+
throw new Error("MCP capsule resolver audit is invalid");
|
|
984
1318
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
const output = Buffer.from(
|
|
1322
|
+
result.output.buffer,
|
|
1323
|
+
result.output.byteOffset,
|
|
1324
|
+
result.output.byteLength,
|
|
1325
|
+
);
|
|
1326
|
+
let descriptor;
|
|
1327
|
+
try {
|
|
1328
|
+
descriptor = _deps.fs.openSync(
|
|
1329
|
+
capsulePath,
|
|
1330
|
+
Number(_deps.fs.constants.O_WRONLY) |
|
|
1331
|
+
Number(_deps.fs.constants.O_CREAT) |
|
|
1332
|
+
Number(_deps.fs.constants.O_EXCL) |
|
|
1333
|
+
Number(_deps.fs.constants.O_NOFOLLOW || 0),
|
|
1334
|
+
0o600,
|
|
1335
|
+
);
|
|
1336
|
+
let offset = 0;
|
|
1337
|
+
while (offset < output.length) {
|
|
1338
|
+
const written = _deps.fs.writeSync(
|
|
1339
|
+
descriptor,
|
|
1340
|
+
output,
|
|
1341
|
+
offset,
|
|
1342
|
+
output.length - offset,
|
|
1343
|
+
offset,
|
|
1344
|
+
);
|
|
1345
|
+
if (written <= 0) throw new Error("MCP capsule output write stalled");
|
|
1346
|
+
offset += written;
|
|
991
1347
|
}
|
|
992
|
-
|
|
993
|
-
schema: CAPSULE_SCHEMA,
|
|
994
|
-
relativePath: CAPSULE_RELATIVE_PATH,
|
|
995
|
-
sha256: capsuleClosure.files[0].sha256,
|
|
996
|
-
bytes: capsuleClosure.files[0].bytes,
|
|
997
|
-
closureDigest: capsuleClosure.closureDigest,
|
|
998
|
-
builder: CAPSULE_BUILDER,
|
|
999
|
-
builderVersion: CAPSULE_BUILDER_VERSION,
|
|
1000
|
-
builderPlatform: builder.platform,
|
|
1001
|
-
builderBinarySha256: builder.sha256,
|
|
1002
|
-
nodeTarget: "node22",
|
|
1003
|
-
inputCount: inputs.length,
|
|
1004
|
-
inputDigest: sha256(canonicalJson(inputs)),
|
|
1005
|
-
externalBuiltins: Object.freeze([...new Set(externalBuiltins)].sort()),
|
|
1006
|
-
});
|
|
1348
|
+
_deps.fs.fsyncSync(descriptor);
|
|
1007
1349
|
} finally {
|
|
1008
|
-
if (
|
|
1009
|
-
_deps.fs.rmSync(snapshotRoot, { recursive: true, force: true });
|
|
1350
|
+
if (descriptor !== undefined) _deps.fs.closeSync(descriptor);
|
|
1010
1351
|
}
|
|
1352
|
+
|
|
1353
|
+
const postBuildClosure = collectTree(treeRoot);
|
|
1354
|
+
if (
|
|
1355
|
+
postBuildClosure.fileCount !== closure.fileCount ||
|
|
1356
|
+
postBuildClosure.totalBytes !== closure.totalBytes ||
|
|
1357
|
+
postBuildClosure.closureDigest !== closure.closureDigest ||
|
|
1358
|
+
canonicalJson(postBuildClosure.files) !== canonicalJson(closure.files)
|
|
1359
|
+
) {
|
|
1360
|
+
throw new Error("MCP package dependency closure changed during bundling");
|
|
1361
|
+
}
|
|
1362
|
+
const capsuleClosure = collectTree(capsuleRoot, {
|
|
1363
|
+
maxFiles: 1,
|
|
1364
|
+
maxTotalBytes: MAX_CAPSULE_BYTES,
|
|
1365
|
+
maxFileBytes: MAX_CAPSULE_BYTES,
|
|
1366
|
+
});
|
|
1367
|
+
if (
|
|
1368
|
+
capsuleClosure.fileCount !== 1 ||
|
|
1369
|
+
capsuleClosure.files[0]?.path !== path.basename(capsulePath)
|
|
1370
|
+
) {
|
|
1371
|
+
throw new Error("MCP capsule output is not one regular file");
|
|
1372
|
+
}
|
|
1373
|
+
return Object.freeze({
|
|
1374
|
+
schema: CAPSULE_SCHEMA,
|
|
1375
|
+
relativePath: CAPSULE_RELATIVE_PATH,
|
|
1376
|
+
sha256: capsuleClosure.files[0].sha256,
|
|
1377
|
+
bytes: capsuleClosure.files[0].bytes,
|
|
1378
|
+
closureDigest: capsuleClosure.closureDigest,
|
|
1379
|
+
builder: CAPSULE_BUILDER,
|
|
1380
|
+
builderVersion: CAPSULE_BUILDER_VERSION,
|
|
1381
|
+
builderWasmSha256: builder.wasmSha256,
|
|
1382
|
+
builderApiSha256: builder.apiSha256,
|
|
1383
|
+
builderWorkerSha256: builder.workerSha256,
|
|
1384
|
+
resolverSchema: CAPSULE_RESOLVER_SCHEMA,
|
|
1385
|
+
resolverSha256: builder.resolverSha256,
|
|
1386
|
+
wrapperSchema: CAPSULE_STDIN_WRAPPER_SCHEMA,
|
|
1387
|
+
wrapperSha256,
|
|
1388
|
+
nodeTarget: "node22",
|
|
1389
|
+
inputCount: inputs.length,
|
|
1390
|
+
inputDigest: sha256(canonicalJson(inputs)),
|
|
1391
|
+
externalBuiltins: Object.freeze([...new Set(externalBuiltins)].sort()),
|
|
1392
|
+
});
|
|
1011
1393
|
}
|
|
1012
1394
|
|
|
1013
1395
|
function assertSafeGeneration(root, generation) {
|
|
@@ -1042,15 +1424,19 @@ function validateManifest(manifest) {
|
|
|
1042
1424
|
!/^[a-f0-9]{64}$/.test(manifest.capsule.sha256) ||
|
|
1043
1425
|
!Number.isSafeInteger(manifest.capsule?.bytes) ||
|
|
1044
1426
|
manifest.capsule.bytes <= 0 ||
|
|
1045
|
-
manifest.capsule.bytes >
|
|
1427
|
+
manifest.capsule.bytes > MAX_CAPSULE_BYTES ||
|
|
1046
1428
|
typeof manifest.capsule?.closureDigest !== "string" ||
|
|
1047
1429
|
!/^[a-f0-9]{64}$/.test(manifest.capsule.closureDigest) ||
|
|
1048
1430
|
manifest.capsule?.builder !== CAPSULE_BUILDER ||
|
|
1049
1431
|
manifest.capsule?.builderVersion !== CAPSULE_BUILDER_VERSION ||
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
manifest.capsule?.
|
|
1053
|
-
|
|
1432
|
+
manifest.capsule?.builderWasmSha256 !== CAPSULE_BUILDER_WASM_SHA256 ||
|
|
1433
|
+
manifest.capsule?.builderApiSha256 !== CAPSULE_BUILDER_API_SHA256 ||
|
|
1434
|
+
manifest.capsule?.builderWorkerSha256 !== CAPSULE_BUILDER_WORKER_SHA256 ||
|
|
1435
|
+
manifest.capsule?.resolverSchema !== CAPSULE_RESOLVER_SCHEMA ||
|
|
1436
|
+
manifest.capsule?.resolverSha256 !== CAPSULE_RESOLVER_SHA256 ||
|
|
1437
|
+
manifest.capsule?.wrapperSchema !== CAPSULE_STDIN_WRAPPER_SCHEMA ||
|
|
1438
|
+
typeof manifest.capsule?.wrapperSha256 !== "string" ||
|
|
1439
|
+
!/^[a-f0-9]{64}$/.test(manifest.capsule.wrapperSha256) ||
|
|
1054
1440
|
manifest.capsule?.nodeTarget !== "node22" ||
|
|
1055
1441
|
!Number.isSafeInteger(manifest.capsule?.inputCount) ||
|
|
1056
1442
|
manifest.capsule.inputCount <= 0 ||
|
|
@@ -1163,7 +1549,11 @@ function verifyPublishedGeneration(root, record, expectedFingerprint) {
|
|
|
1163
1549
|
const capsuleRoot = path.dirname(entrypoint);
|
|
1164
1550
|
let capsuleClosure;
|
|
1165
1551
|
try {
|
|
1166
|
-
capsuleClosure = collectTree(capsuleRoot
|
|
1552
|
+
capsuleClosure = collectTree(capsuleRoot, {
|
|
1553
|
+
maxFiles: 1,
|
|
1554
|
+
maxTotalBytes: MAX_CAPSULE_BYTES,
|
|
1555
|
+
maxFileBytes: MAX_CAPSULE_BYTES,
|
|
1556
|
+
});
|
|
1167
1557
|
} catch (cause) {
|
|
1168
1558
|
throw materializationError(
|
|
1169
1559
|
MCP_STDIO_PACKAGE_MATERIALIZATION_CHANGED_CODE,
|
|
@@ -1209,7 +1599,7 @@ function verifyPublishedGeneration(root, record, expectedFingerprint) {
|
|
|
1209
1599
|
});
|
|
1210
1600
|
}
|
|
1211
1601
|
|
|
1212
|
-
export function materializeMcpStdioNpmPackage({
|
|
1602
|
+
export async function materializeMcpStdioNpmPackage({
|
|
1213
1603
|
approvalRecord,
|
|
1214
1604
|
config,
|
|
1215
1605
|
packageSpec,
|
|
@@ -1263,13 +1653,11 @@ export function materializeMcpStdioNpmPackage({
|
|
|
1263
1653
|
const lock = validateNpmLock(treeRoot, source);
|
|
1264
1654
|
const entry = resolvePackageEntrypoint(treeRoot, source, binName);
|
|
1265
1655
|
const closure = collectTree(treeRoot);
|
|
1266
|
-
const capsule = buildCapsule({
|
|
1656
|
+
const capsule = await buildCapsule({
|
|
1267
1657
|
treeRoot,
|
|
1268
1658
|
entrypointRelative: entry.entrypointRelative,
|
|
1269
1659
|
closure,
|
|
1270
1660
|
staging,
|
|
1271
|
-
processBrokerRunSync,
|
|
1272
|
-
env,
|
|
1273
1661
|
});
|
|
1274
1662
|
const generation = sha256(
|
|
1275
1663
|
canonicalJson({
|