chainlesschain 0.165.6 → 0.165.8
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 +2 -1
- package/src/assets/web-panel/assets/{AIOps-vfoDb6HZ.js → AIOps-B3gWmXjo.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Cm5f7p93.js → ActionButton-G9pdT-0e.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-yRuvgWxa.js → Analytics-Ca30J7m2.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BROsC4h1.js → AppLayout-BtVTm8PU.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CimHApb1.js → Artifacts-BhViNa6u.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DI8kr8zI.js → Audit-DhKq-FnY.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DSLzKy-n.js → BackgroundAgents-BL0SVbnE.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DkH_n2r1.js → Backup-BWkblnFi.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CKSrUoRA.js → BaseInput-ClaRNrF8.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-ClwfMXUS.js → Chat-sZE2Z77o.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CjMaqfL7.js → ChatBubbleRenderer-BAfKOnRe.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BYTehcQG.js → Checkbox-Cvhay5WM.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B8AMcp6J.js → Codegen-BpejL-4N.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BNSogPo3.js → Col-g2Ki6g_1.js} +1 -1
- package/src/assets/web-panel/assets/{Community-JxPvGb8O.js → Community-D2xKbNo9.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DF7bFAgE.js → Compact-DbL1Pds3.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bvhqnwrq.js → Compliance-DGtZCV4c.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BDS0CJvM.js → Cowork-4kQpoNsI.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DqvRfxoQ.js → Cron-BxTYXXAN.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CDcIvnqS.js → Crosschain-k0J0LT2N.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D7jaUxU3.js → DID-BaqhoPgN.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-B5A18wEV.js → Dashboard-T4RVPIM1.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-_6IVkGag.js → Dropdown-DszOToWq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C9PeiKCG.js → EmailListRenderer-C9KJU7HF.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DXjuEHrX.js → FamilyGuardDashboard-Cmr0JnsA.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B4IAR9zv.js → Federation-BI-_2xZX.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CxQeSNx1.js → FormItemContext-DsnDEPsl.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-B8GS-Y3N.js → GenericCardRenderer-BLncAKKK.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BS8SouUX.js → Git-BPQhGXDn.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Dq9aYb5Q.js → Governance-CyElbUQW.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BrCEe9AJ.js → Inference-CcSntBQD.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DybOpiY2.js → KnowledgeGraph-p6e7HZ2r.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DBpzbRfx.js → Logs-CNEtr-wz.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-Bj9xI0YH.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Cc7CCSCu.js → Marketplace-Dd2notAJ.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DFaIhOfu.js → McpTools-BBsN2CSW.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-KCqlp9ol.js → Memory-Dg2uTvwx.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Bys84ySN.js → MobileBridge-r1YMRiJV.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B-6qU-je.js → MobileProjects-C-89kSm4.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BgY-fvCq.js → Mtc-CrPZcVgf.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-B8pCmqQJ.js → MtcAudit-sxi7hqJb.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BbOmYutA.js → Multisig-CAgOMfF1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BocLQz_M.js → NLProgramming-DafaPts3.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CSwbxEop.js → Notes-CJM-xUW_.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CqFNSYG3.js → NotificationSettings-L0ZjfAh_.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C6gMfAk7.js → OrderTableRenderer-BB1CvCK0.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-QrDXVdMJ.js → Organization-DeLtgAD8.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Cxntotjt.js → Overflow-CKC2b10z.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CIdBOwNq.js → P2P-eyf2rzY1.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CT5aP8rD.js → PdhVaultBrowser-5upNIOW8.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DH7OM8XU.js → Permissions-B8p3W6JK.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CnJqbWce.js → PersonalDataHub-BnRMxTq3.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-5C3HG1kH.js → Pipeline-DjCNI1li.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B4yIp7xK.js → Privacy-u6gXLbWT.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BMvt-2M0.js → ProjectInit-BZAw4tkM.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CvSTEMRG.js → ProjectSettings-B1Tr72q7.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BKBJxEek.js → Projects-DO7HFuBQ.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BdwUqFWN.js → Providers-BjCztyhj.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DtnCKlTm.js → QrScannerModal-ZWyCUbR1.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-sTbgwyNr.js → QuickAsk-DhDjeW4G.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bzr14gSb.js → Recommend-R_CN2MuU.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C_TF5IaJ.js → RemoteSession-uxFcujeu.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-GCAM4H2K.js → Reputation-DX8D7rks.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Dir0r6OQ.js → Row-BYahQILD.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CpGnlecX.js → RssFeed-CUSQ2tm-.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DMMamC6u.js → Search-CnN_S8Cs.js} +1 -1
- package/src/assets/web-panel/assets/{Security-yqay5yG-.js → Security-DcZlh-O4.js} +4 -4
- package/src/assets/web-panel/assets/{Services-C5i2wh9G.js → Services-ZhBOjzIX.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-vUqk35yj.js → Skeleton-D5Qnq_lI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DzcSkhot.js → Skills-BC9cZ3Be.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CB6qbYNr.js → Sla-DauJ3E8_.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-DbNeZ_fh.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-D-RrsxTJ.js → SyncSettings-DUvJbSct.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DFxdOJiq.js → Tasks-BXa-5noQ.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-SKAob_Aq.js → Templates-JxaiUt_3.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BPfsfHQ-.js → Tenant-BW4ERQN8.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-8iw7FpUf.js → Terminal-DltUbdtv.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C4Gjj5Su.js → TimelineRenderer-CMoZz0Tc.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-COYhN18e.js → Tokens-D_VEV60K.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B-JzXcf6.js → Trigger-CCXTCs0R.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BJ5bg0nI.js → Trust-lkGRIPcG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BG_ByRMf.js → UkeySign-BfGeQ9HX.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B3kOnSiU.js → VideoEditing-BEC7pYvc.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CcoGFnK7.js → Wallet-BZdG24o0.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DdT8vRC_.js → WebAuthn-yoEddedp.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CZu3M9N8.js → WorkflowEditor-BXZEprOW.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DxJt3Qba.js → chat-gg0bzZal.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DdYfwJaB.js → colors-DjRVRuin.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BG3SUzwj.js → compact-item-su-dieAc.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-By51z2-1.js → createContext-Ci3S8oKF.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CxbFX9uH.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-B0apZ3KJ.js → hasIn-D8F10heH.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE76D-kA.js → index-11wtYQiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkvcEo4p.js → index-1IgxgALJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bos5_aMV.js → index-1agckWSq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfWYsB9t.js → index-5JqhujnR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAhHirVH.js → index-6Tniqk1s.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYINoRLK.js → index-8Z-8BIlN.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5rLiEo0.js → index-BCI5v0yB.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZ_0qm4g.js → index-BDJntYmJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWEuZWCK.js → index-BGzixOGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLIbGOLX.js → index-BMMOVoLD.js} +1 -1
- package/src/assets/web-panel/assets/{index-bP_VlDb4.js → index-BTFpdBtt.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3rWuPMW.js → index-Ba_HyPnE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Da_WBdLX.js → index-BbF1IHof.js} +1 -1
- package/src/assets/web-panel/assets/{index-RD6O-0C6.js → index-BgT9su_e.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpjZhgNd.js → index-BzPB6kGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q2SCX-MI.js → index-C6ygzplr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIrJfqO4.js → index-CC3ZDjzY.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBPf3TpM.js → index-CEmXRhAE.js} +1 -1
- package/src/assets/web-panel/assets/index-CJGOlduY.js +1 -0
- package/src/assets/web-panel/assets/{index-Cp6e-hGw.js → index-CMPqd5Cv.js} +1 -1
- package/src/assets/web-panel/assets/{index-kf57LJ93.js → index-COWd8uGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW93tZDv.js → index-CPIYSp4H.js} +1 -1
- package/src/assets/web-panel/assets/{index-BewKYrX9.js → index-CR0h6seT.js} +1 -1
- package/src/assets/web-panel/assets/{index-p1_8SbI1.js → index-CUmDhC4j.js} +1 -1
- package/src/assets/web-panel/assets/index-CkBCN5yH.js +1 -0
- package/src/assets/web-panel/assets/{index-ZxtP8jG7.js → index-CusgZPtc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWF8_mkx.js → index-DBAETXft.js} +1 -1
- package/src/assets/web-panel/assets/{index-BP94CrBV.js → index-DIJ2qFOC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNIxXMmN.js → index-Dbq4NMET.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzmQElZy.js → index-DkMEF5VT.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZFbps9po.js → index-DkfQR3hM.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dz49VOyQ.js → index-DnLucOn9.js} +1 -1
- package/src/assets/web-panel/assets/{index-oxRrmFO9.js → index-DugB_aKa.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bissefox.js → index-DyLUarhZ.js} +3 -3
- package/src/assets/web-panel/assets/{index-DaIz0NAW.js → index-T6_6ygJZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-SQqOlR3d.js → index-Tu8eWqh-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCn8IcxV.js → index-W7XUgdVL.js} +1 -1
- package/src/assets/web-panel/assets/{index-3jWkIu5j.js → index-dHZinfko.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1C9i79A.js → index-jyNgp5FI.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BWJktcg8.js → initDefaultProps-Cv4p8e4w.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BEXBzUFV.js → motion-B8EwmMOw.js} +1 -1
- package/src/assets/web-panel/assets/{move-BUKwn2sx.js → move-B8R9F8TK.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-FTI7btqQ.js → mtc-parser-DoFYz5Bp.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CsLtSPH3.js → omit-CHWYLBjI.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DWZxsT_S.js → pickAttrs-CXuPDO77.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Bcfs43CX.js → placementArrow-BaAtUUon.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-a9bcC1p-.js → responsiveObserve-DCc4zcDD.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BDTzgOXN.js → slide-B_lx89oZ.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Iq1j_HUg.js → statusUtils-DE8DPmR0.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DAZEYxeh.js → styleChecker-1ktXOsL_.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DVkjyDrh.js → useFlexGapSupport-xH89-wjh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-wXTpNTHt.js → useFs-DscvfW5p.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CtQM_aZr.js → usePersonalDataHub-RdMKQta7.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-hjEoG85v.js → vnode-Z3wTni2T.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BHVpyKg_.js → zoom-B0iChCsm.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +91 -60
- package/src/commands/git.js +39 -0
- package/src/commands/plugin.js +17 -2
- package/src/commands/session-group.js +131 -0
- package/src/commands/session-location.js +48 -0
- package/src/commands/session.js +11 -0
- package/src/commands/sync-providers.js +4 -1
- package/src/commands/update.js +1 -0
- package/src/data/changelog.json +27 -1
- package/src/gateways/ws/ws-agent-handler.js +20 -12
- package/src/harness/jsonl-session-store.js +279 -37
- package/src/harness/mcp-client.js +701 -91
- package/src/harness/session-index.js +15 -4
- package/src/lib/agent-worktree.js +8 -1
- package/src/lib/auto-mode-safety-classifier.js +3 -17
- package/src/lib/background-agent-keeper-protocol.js +26 -6
- package/src/lib/background-agent-supervisor.js +17 -1
- package/src/lib/browser-evidence.js +1405 -0
- package/src/lib/chrome-connector.js +465 -79
- package/src/lib/claude-project-auto-memory.js +481 -0
- package/src/lib/claude-project-storage-layout.js +104 -0
- package/src/lib/cloud/cloud-client.js +40 -0
- package/src/lib/execution-location-local-supervisor.mjs +226 -0
- package/src/lib/execution-location-runner-lifecycle.js +991 -0
- package/src/lib/execution-location-target-preflight.js +333 -0
- package/src/lib/execution-location-target.js +783 -13
- package/src/lib/file-lines.js +6 -1
- package/src/lib/gateway-client-resilience.js +571 -0
- package/src/lib/gitlab-mr-worktree.js +176 -0
- package/src/lib/hooks-v2-producers.js +119 -15
- package/src/lib/hooks-v2-workspace-context.js +44 -1
- package/src/lib/host-resource-budget.js +295 -0
- package/src/lib/ide-context.js +15 -2
- package/src/lib/llm-config-defaults.js +43 -0
- package/src/lib/mcp-lifecycle-authority.js +887 -0
- package/src/lib/mcp-oauth.js +101 -20
- package/src/lib/mcp-tls.js +103 -3
- package/src/lib/paths.js +260 -32
- package/src/lib/permission-request.js +48 -0
- package/src/lib/plugin-runtime/bin.js +3 -1
- package/src/lib/plugin-runtime/hooks.js +3 -1
- package/src/lib/plugin-runtime/install.js +117 -1
- package/src/lib/plugin-runtime/lsp.js +4 -4
- package/src/lib/plugin-runtime/marketplace-archive-source.js +40 -5
- package/src/lib/plugin-runtime/marketplace-catalog.js +13 -2
- package/src/lib/plugin-runtime/marketplace-command-descriptor.js +343 -0
- package/src/lib/plugin-runtime/marketplace-command-source.js +580 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +33 -8
- package/src/lib/plugin-runtime/mcp.js +3 -1
- package/src/lib/plugin-runtime/monitors.js +3 -1
- package/src/lib/plugin-runtime/remote-source.js +151 -6
- package/src/lib/plugin-runtime/settings.js +3 -1
- package/src/lib/plugin-runtime/trust.js +274 -13
- package/src/lib/process-execution-broker/credential-agent.js +210 -41
- package/src/lib/process-execution-broker/index.js +15 -1
- package/src/lib/process-execution-broker/linux-cgroup-v2.js +391 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +50 -28
- package/src/lib/project-mcp-trust.js +165 -10
- package/src/lib/resume-session.js +9 -6
- package/src/lib/session-core-singletons.js +26 -11
- package/src/lib/session-host-lease.js +52 -16
- package/src/lib/session-lifecycle-scope.js +153 -0
- package/src/lib/session-message-fabric.js +168 -21
- package/src/lib/session-projection.js +244 -4
- package/src/lib/session-store-guard.js +3 -1
- package/src/lib/session-workbench-store.js +466 -0
- package/src/lib/spellcheck.js +211 -0
- package/src/lib/sub-agent-context.js +9 -9
- package/src/lib/sync-cli-db.js +485 -129
- package/src/lib/sync-credentials.js +31 -20
- package/src/lib/web-fetch.js +112 -40
- package/src/lib/web-search.js +127 -61
- package/src/lib/workspace-trust.js +793 -0
- package/src/repl/agent-repl.js +124 -5
- package/src/repl/permission-prompt.js +10 -12
- package/src/runtime/agent-core.js +311 -18
- package/src/runtime/bootstrap.js +37 -13
- package/src/runtime/coding-agent-contract-shared.cjs +11 -4
- package/src/runtime/headless-runner.js +234 -36
- package/src/runtime/headless-stream.js +181 -6
- package/src/runtime/mcp-config.js +298 -8
- package/src/runtime/policies/agent-policy.js +8 -0
- package/src/runtime/status-diagnostics-lite.js +9 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-BHUI7cox.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +0 -1
- package/src/assets/web-panel/assets/index-BETiL0a3.js +0 -1
- package/src/assets/web-panel/assets/index-BhAVHuW8.js +0 -1
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import executionBroker from "../process-execution-broker/index.js";
|
|
5
|
+
import {
|
|
6
|
+
assertMarketplaceCommandModeSupported,
|
|
7
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR,
|
|
8
|
+
MARKETPLACE_COMMAND_SAFE_ENVIRONMENT_KEYS,
|
|
9
|
+
normalizeMarketplaceCommandDescriptor,
|
|
10
|
+
} from "./marketplace-command-descriptor.js";
|
|
11
|
+
import {
|
|
12
|
+
assertSafeInstalledPluginStructure,
|
|
13
|
+
buildMarketplacePayloadSbom,
|
|
14
|
+
} from "./marketplace-artifact-readback.js";
|
|
15
|
+
|
|
16
|
+
export const MARKETPLACE_HEADERS_HELPER_ERROR = Object.freeze({
|
|
17
|
+
INVALID_OUTPUT: "CC_MARKETPLACE_HEADERS_HELPER_OUTPUT_INVALID",
|
|
18
|
+
PROCESS_FAILED: "CC_MARKETPLACE_HEADERS_HELPER_PROCESS_FAILED",
|
|
19
|
+
PROCESS_TIMEOUT: "CC_MARKETPLACE_HEADERS_HELPER_TIMEOUT",
|
|
20
|
+
PROCESS_OUTPUT_LIMIT: "CC_MARKETPLACE_HEADERS_HELPER_OUTPUT_LIMIT",
|
|
21
|
+
PROCESS_START: "CC_MARKETPLACE_HEADERS_HELPER_PROCESS_START_FAILED",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const MARKETPLACE_COMMAND_SOURCE_ERROR = Object.freeze({
|
|
25
|
+
INVALID_OUTPUT: "CC_MARKETPLACE_COMMAND_SOURCE_OUTPUT_INVALID",
|
|
26
|
+
PROCESS_FAILED: "CC_MARKETPLACE_COMMAND_SOURCE_PROCESS_FAILED",
|
|
27
|
+
PROCESS_TIMEOUT: "CC_MARKETPLACE_COMMAND_SOURCE_TIMEOUT",
|
|
28
|
+
PROCESS_OUTPUT_LIMIT: "CC_MARKETPLACE_COMMAND_SOURCE_OUTPUT_LIMIT",
|
|
29
|
+
PROCESS_START: "CC_MARKETPLACE_COMMAND_SOURCE_PROCESS_START_FAILED",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const MARKETPLACE_HEADERS_HELPER_LIMITS = Object.freeze({
|
|
33
|
+
maxHeaders: 64,
|
|
34
|
+
maxHeaderValueBytes: 16 * 1024,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/u;
|
|
38
|
+
const TRANSPORT_OWNED_HEADERS = new Set([
|
|
39
|
+
"accept",
|
|
40
|
+
"connection",
|
|
41
|
+
"content-length",
|
|
42
|
+
"content-type",
|
|
43
|
+
"host",
|
|
44
|
+
"transfer-encoding",
|
|
45
|
+
"upgrade",
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
function runtimeError(code, message) {
|
|
49
|
+
const error = new Error(message);
|
|
50
|
+
error.code = code;
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function outputError(kind, suffix, message) {
|
|
55
|
+
const codes =
|
|
56
|
+
kind === "headers"
|
|
57
|
+
? MARKETPLACE_HEADERS_HELPER_ERROR
|
|
58
|
+
: MARKETPLACE_COMMAND_SOURCE_ERROR;
|
|
59
|
+
return runtimeError(codes[suffix], message);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function hasInvalidHeaderValueCharacter(value) {
|
|
63
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
64
|
+
const code = value.charCodeAt(index);
|
|
65
|
+
if (code <= 0x08 || (code >= 0x0a && code <= 0x1f) || code === 0x7f) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeHeaderMap(value) {
|
|
73
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
74
|
+
throw runtimeError(
|
|
75
|
+
MARKETPLACE_HEADERS_HELPER_ERROR.INVALID_OUTPUT,
|
|
76
|
+
"Marketplace headers helper must emit a JSON object of string headers",
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
80
|
+
const keys = Reflect.ownKeys(descriptors);
|
|
81
|
+
if (
|
|
82
|
+
keys.length > MARKETPLACE_HEADERS_HELPER_LIMITS.maxHeaders ||
|
|
83
|
+
keys.some(
|
|
84
|
+
(key) => typeof key !== "string" || !("value" in descriptors[key]),
|
|
85
|
+
)
|
|
86
|
+
) {
|
|
87
|
+
throw runtimeError(
|
|
88
|
+
MARKETPLACE_HEADERS_HELPER_ERROR.INVALID_OUTPUT,
|
|
89
|
+
"Marketplace headers helper emitted unsupported headers",
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const headers = {};
|
|
93
|
+
for (const name of keys.sort()) {
|
|
94
|
+
const valueAtName = descriptors[name].value;
|
|
95
|
+
if (
|
|
96
|
+
!HEADER_NAME.test(name) ||
|
|
97
|
+
typeof valueAtName !== "string" ||
|
|
98
|
+
hasInvalidHeaderValueCharacter(valueAtName) ||
|
|
99
|
+
Buffer.byteLength(valueAtName, "utf8") >
|
|
100
|
+
MARKETPLACE_HEADERS_HELPER_LIMITS.maxHeaderValueBytes ||
|
|
101
|
+
TRANSPORT_OWNED_HEADERS.has(name.toLowerCase())
|
|
102
|
+
) {
|
|
103
|
+
throw runtimeError(
|
|
104
|
+
MARKETPLACE_HEADERS_HELPER_ERROR.INVALID_OUTPUT,
|
|
105
|
+
"Marketplace headers helper emitted an invalid header",
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
headers[name] = valueAtName;
|
|
109
|
+
}
|
|
110
|
+
return Object.freeze(headers);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function canonicalExecutable(plan, deps, platform) {
|
|
114
|
+
let original;
|
|
115
|
+
let executable;
|
|
116
|
+
try {
|
|
117
|
+
original = deps.lstatSync(plan.executable);
|
|
118
|
+
executable = deps.realpathSync(plan.executable);
|
|
119
|
+
} catch {
|
|
120
|
+
throw runtimeError(
|
|
121
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
122
|
+
"Marketplace command executable is unavailable",
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
if (original.isSymbolicLink() || !original.isFile()) {
|
|
126
|
+
throw runtimeError(
|
|
127
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
128
|
+
"Marketplace command executable must be a regular non-symlink file",
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
let finalStat;
|
|
132
|
+
try {
|
|
133
|
+
finalStat = deps.lstatSync(executable);
|
|
134
|
+
} catch {
|
|
135
|
+
throw runtimeError(
|
|
136
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
137
|
+
"Marketplace command executable is unavailable",
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
if (finalStat.isSymbolicLink() || !finalStat.isFile()) {
|
|
141
|
+
throw runtimeError(
|
|
142
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
143
|
+
"Marketplace command executable changed while validating",
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
if (platform !== "win32" && (finalStat.mode & 0o111) === 0) {
|
|
147
|
+
throw runtimeError(
|
|
148
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
149
|
+
"Marketplace command executable is not executable",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return executable;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function canonicalCwd(plan, deps) {
|
|
156
|
+
let cwd;
|
|
157
|
+
let stat;
|
|
158
|
+
try {
|
|
159
|
+
cwd = deps.realpathSync(plan.cwd);
|
|
160
|
+
stat = deps.lstatSync(cwd);
|
|
161
|
+
} catch {
|
|
162
|
+
throw runtimeError(
|
|
163
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
164
|
+
"Marketplace command working directory is unavailable",
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
168
|
+
throw runtimeError(
|
|
169
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
170
|
+
"Marketplace command working directory must be a real directory",
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
return cwd;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function terminateProcessTree(child, { platform, kill, spawnSync } = {}) {
|
|
177
|
+
const activePlatform = platform || process.platform;
|
|
178
|
+
const processKill = kill || process.kill.bind(process);
|
|
179
|
+
const pid = Number(child?.pid);
|
|
180
|
+
if (activePlatform !== "win32" && Number.isInteger(pid) && pid > 0) {
|
|
181
|
+
try {
|
|
182
|
+
processKill(-pid, "SIGKILL");
|
|
183
|
+
return;
|
|
184
|
+
} catch {
|
|
185
|
+
// The child may already have exited or not own a process group.
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (activePlatform === "win32" && Number.isInteger(pid) && pid > 0) {
|
|
189
|
+
try {
|
|
190
|
+
(spawnSync || executionBroker.spawnSync.bind(executionBroker))(
|
|
191
|
+
"taskkill",
|
|
192
|
+
["/PID", String(pid), "/T", "/F"],
|
|
193
|
+
{
|
|
194
|
+
shell: false,
|
|
195
|
+
windowsHide: true,
|
|
196
|
+
origin: "marketplace:descriptor:taskkill",
|
|
197
|
+
policy: "allow",
|
|
198
|
+
scope: "marketplace",
|
|
199
|
+
},
|
|
200
|
+
);
|
|
201
|
+
return;
|
|
202
|
+
} catch {
|
|
203
|
+
// Fall through to the direct child handle.
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
child?.kill?.("SIGKILL");
|
|
208
|
+
} catch {
|
|
209
|
+
// Best effort: the process may have already exited.
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function sanitizedMarketplaceEnvironment(descriptorEnvironment, platform) {
|
|
214
|
+
const allowed = new Set(MARKETPLACE_COMMAND_SAFE_ENVIRONMENT_KEYS);
|
|
215
|
+
const inherited = {};
|
|
216
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
217
|
+
const normalized = platform === "win32" ? key.toUpperCase() : key;
|
|
218
|
+
if (
|
|
219
|
+
allowed.has(normalized) &&
|
|
220
|
+
typeof value === "string" &&
|
|
221
|
+
value.length <= 8 * 1024
|
|
222
|
+
) {
|
|
223
|
+
// Preserve the actual key spelling. Windows environment lookup is case
|
|
224
|
+
// insensitive, while POSIX callers may require the conventional spelling.
|
|
225
|
+
inherited[key] = value;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return { ...inherited, ...descriptorEnvironment };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Launch a bounded direct-argv Marketplace descriptor. This intentionally
|
|
233
|
+
* never starts a shell and never inherits the host environment.
|
|
234
|
+
*/
|
|
235
|
+
export function runMarketplaceProcessDescriptor(descriptor, options = {}) {
|
|
236
|
+
const kind = options.kind === "headers" ? "headers" : "source";
|
|
237
|
+
const plan = normalizeMarketplaceCommandDescriptor(descriptor, {
|
|
238
|
+
kind,
|
|
239
|
+
allowType: options.allowType === true,
|
|
240
|
+
label:
|
|
241
|
+
kind === "headers"
|
|
242
|
+
? "Marketplace headers helper descriptor"
|
|
243
|
+
: "Marketplace command source descriptor",
|
|
244
|
+
});
|
|
245
|
+
const platform = options.platform || process.platform;
|
|
246
|
+
if (kind === "source")
|
|
247
|
+
assertMarketplaceCommandModeSupported(plan.mode, platform);
|
|
248
|
+
const realpathSync = options.realpathSync || fs.realpathSync.native;
|
|
249
|
+
const deps = {
|
|
250
|
+
lstatSync: options.lstatSync || fs.lstatSync,
|
|
251
|
+
realpathSync,
|
|
252
|
+
};
|
|
253
|
+
const executable = canonicalExecutable(plan, deps, platform);
|
|
254
|
+
const cwd = canonicalCwd(plan, deps);
|
|
255
|
+
const spawn = options.spawn || executionBroker.spawn.bind(executionBroker);
|
|
256
|
+
const spawnOptions = {
|
|
257
|
+
cwd,
|
|
258
|
+
// An empty environment breaks ordinary Windows binaries (notably Node,
|
|
259
|
+
// which needs SystemRoot/temporary-directory state). Retain only the
|
|
260
|
+
// descriptor's narrow runtime whitelist; credentials and arbitrary host
|
|
261
|
+
// configuration remain absent unless explicitly permitted by the schema.
|
|
262
|
+
env: sanitizedMarketplaceEnvironment(plan.env, platform),
|
|
263
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
264
|
+
shell: false,
|
|
265
|
+
windowsHide: true,
|
|
266
|
+
detached: platform !== "win32",
|
|
267
|
+
origin:
|
|
268
|
+
kind === "headers"
|
|
269
|
+
? "marketplace:headers-helper"
|
|
270
|
+
: "marketplace:command-source",
|
|
271
|
+
policy: "allow",
|
|
272
|
+
scope: "marketplace",
|
|
273
|
+
// Descriptor arguments may contain an access token supplied by a local
|
|
274
|
+
// operator. Keep all argv out of broker audit records.
|
|
275
|
+
auditRedactArgIndexes: plan.args.map((_, index) => index),
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
return new Promise((resolve, reject) => {
|
|
279
|
+
let child;
|
|
280
|
+
try {
|
|
281
|
+
child = spawn(executable, [...plan.args], spawnOptions);
|
|
282
|
+
} catch {
|
|
283
|
+
reject(
|
|
284
|
+
outputError(
|
|
285
|
+
kind,
|
|
286
|
+
"PROCESS_START",
|
|
287
|
+
"Marketplace command process could not be started",
|
|
288
|
+
),
|
|
289
|
+
);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (!child?.stdout || !child?.stderr) {
|
|
293
|
+
terminateProcessTree(child, {
|
|
294
|
+
platform,
|
|
295
|
+
kill: options.kill,
|
|
296
|
+
spawnSync: options.spawnSync,
|
|
297
|
+
});
|
|
298
|
+
reject(
|
|
299
|
+
outputError(
|
|
300
|
+
kind,
|
|
301
|
+
"PROCESS_START",
|
|
302
|
+
"Marketplace command process did not expose output streams",
|
|
303
|
+
),
|
|
304
|
+
);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
let state = "running";
|
|
309
|
+
let stdout = "";
|
|
310
|
+
let totalOutputBytes = 0;
|
|
311
|
+
const settle = (callback, value) => {
|
|
312
|
+
if (state === "settled") return;
|
|
313
|
+
state = "settled";
|
|
314
|
+
clearTimeout(timeout);
|
|
315
|
+
callback(value);
|
|
316
|
+
};
|
|
317
|
+
const fail = (error) => {
|
|
318
|
+
if (state !== "running") return;
|
|
319
|
+
state = "terminating";
|
|
320
|
+
clearTimeout(timeout);
|
|
321
|
+
terminateProcessTree(child, {
|
|
322
|
+
platform,
|
|
323
|
+
kill: options.kill,
|
|
324
|
+
spawnSync: options.spawnSync,
|
|
325
|
+
});
|
|
326
|
+
settle(reject, error);
|
|
327
|
+
};
|
|
328
|
+
const appendOutput = (chunk, keep) => {
|
|
329
|
+
const buffer = Buffer.isBuffer(chunk)
|
|
330
|
+
? chunk
|
|
331
|
+
: Buffer.from(String(chunk), "utf8");
|
|
332
|
+
totalOutputBytes += buffer.byteLength;
|
|
333
|
+
if (totalOutputBytes > plan.maxOutputBytes) {
|
|
334
|
+
fail(
|
|
335
|
+
outputError(
|
|
336
|
+
kind,
|
|
337
|
+
"PROCESS_OUTPUT_LIMIT",
|
|
338
|
+
"Marketplace command process exceeded its bounded output limit",
|
|
339
|
+
),
|
|
340
|
+
);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (keep) stdout += buffer.toString("utf8");
|
|
344
|
+
};
|
|
345
|
+
const timeout = setTimeout(
|
|
346
|
+
() =>
|
|
347
|
+
fail(
|
|
348
|
+
outputError(
|
|
349
|
+
kind,
|
|
350
|
+
"PROCESS_TIMEOUT",
|
|
351
|
+
"Marketplace command process exceeded its timeout",
|
|
352
|
+
),
|
|
353
|
+
),
|
|
354
|
+
plan.timeoutMs,
|
|
355
|
+
);
|
|
356
|
+
timeout.unref?.();
|
|
357
|
+
child.stdout.on("data", (chunk) => {
|
|
358
|
+
if (state === "running") appendOutput(chunk, true);
|
|
359
|
+
});
|
|
360
|
+
child.stderr.on("data", (chunk) => {
|
|
361
|
+
if (state === "running") appendOutput(chunk, false);
|
|
362
|
+
});
|
|
363
|
+
child.once("error", () =>
|
|
364
|
+
fail(
|
|
365
|
+
outputError(
|
|
366
|
+
kind,
|
|
367
|
+
"PROCESS_START",
|
|
368
|
+
"Marketplace command process failed to start",
|
|
369
|
+
),
|
|
370
|
+
),
|
|
371
|
+
);
|
|
372
|
+
child.once("close", (code, signal) => {
|
|
373
|
+
if (state !== "running") return;
|
|
374
|
+
if (code !== 0) {
|
|
375
|
+
fail(
|
|
376
|
+
outputError(
|
|
377
|
+
kind,
|
|
378
|
+
"PROCESS_FAILED",
|
|
379
|
+
`Marketplace command process exited unsuccessfully (code ${code ?? "null"}, signal ${signal || "none"})`,
|
|
380
|
+
),
|
|
381
|
+
);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
settle(resolve, stdout);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/** Run a descriptor whose stdout must be a bounded JSON object of HTTP headers. */
|
|
390
|
+
export async function runMarketplaceHeadersHelper(descriptor, options = {}) {
|
|
391
|
+
const stdout = await runMarketplaceProcessDescriptor(descriptor, {
|
|
392
|
+
...options,
|
|
393
|
+
kind: "headers",
|
|
394
|
+
});
|
|
395
|
+
let parsed;
|
|
396
|
+
try {
|
|
397
|
+
parsed = JSON.parse(stdout.trim());
|
|
398
|
+
} catch {
|
|
399
|
+
throw runtimeError(
|
|
400
|
+
MARKETPLACE_HEADERS_HELPER_ERROR.INVALID_OUTPUT,
|
|
401
|
+
"Marketplace headers helper stdout must be one JSON header object",
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
return normalizeHeaderMap(parsed);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function sourcePathFromOutput(value, deps) {
|
|
408
|
+
if (
|
|
409
|
+
!value ||
|
|
410
|
+
typeof value !== "object" ||
|
|
411
|
+
Array.isArray(value) ||
|
|
412
|
+
Object.getPrototypeOf(value) !== Object.prototype ||
|
|
413
|
+
Object.keys(value).length !== 1 ||
|
|
414
|
+
typeof value.source !== "string" ||
|
|
415
|
+
value.source.length === 0 ||
|
|
416
|
+
value.source.includes("\0") ||
|
|
417
|
+
!path.isAbsolute(value.source)
|
|
418
|
+
) {
|
|
419
|
+
throw runtimeError(
|
|
420
|
+
MARKETPLACE_COMMAND_SOURCE_ERROR.INVALID_OUTPUT,
|
|
421
|
+
'Marketplace command source stdout must be {"source": "/absolute/plugin/path"}',
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
let source;
|
|
425
|
+
let original;
|
|
426
|
+
let stat;
|
|
427
|
+
try {
|
|
428
|
+
original = deps.lstatSync(value.source);
|
|
429
|
+
source = deps.realpathSync(value.source);
|
|
430
|
+
stat = deps.lstatSync(source);
|
|
431
|
+
} catch {
|
|
432
|
+
throw runtimeError(
|
|
433
|
+
MARKETPLACE_COMMAND_SOURCE_ERROR.INVALID_OUTPUT,
|
|
434
|
+
"Marketplace command source returned an unavailable plugin directory",
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
if (
|
|
438
|
+
original.isSymbolicLink() ||
|
|
439
|
+
stat.isSymbolicLink() ||
|
|
440
|
+
!stat.isDirectory()
|
|
441
|
+
) {
|
|
442
|
+
throw runtimeError(
|
|
443
|
+
MARKETPLACE_COMMAND_SOURCE_ERROR.INVALID_OUTPUT,
|
|
444
|
+
"Marketplace command source must return a real plugin directory",
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
return source;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Run a Marketplace command source and turn its one JSON response into a
|
|
452
|
+
* validated local plugin directory. `mode: link` is intentionally limited to
|
|
453
|
+
* POSIX at this boundary; the immutable installer still snapshots the payload
|
|
454
|
+
* before activation, so a command cannot make an installed version mutable.
|
|
455
|
+
*/
|
|
456
|
+
export async function runMarketplaceCommandSource(descriptor, options = {}) {
|
|
457
|
+
const plan = normalizeMarketplaceCommandDescriptor(descriptor, {
|
|
458
|
+
kind: "source",
|
|
459
|
+
allowType: options.allowType === true,
|
|
460
|
+
label: "Marketplace command source descriptor",
|
|
461
|
+
});
|
|
462
|
+
const stdout = await runMarketplaceProcessDescriptor(plan, {
|
|
463
|
+
...options,
|
|
464
|
+
kind: "source",
|
|
465
|
+
});
|
|
466
|
+
let parsed;
|
|
467
|
+
try {
|
|
468
|
+
parsed = JSON.parse(stdout.trim());
|
|
469
|
+
} catch {
|
|
470
|
+
throw runtimeError(
|
|
471
|
+
MARKETPLACE_COMMAND_SOURCE_ERROR.INVALID_OUTPUT,
|
|
472
|
+
"Marketplace command source stdout must be one JSON object",
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
const deps = {
|
|
476
|
+
lstatSync: options.lstatSync || fs.lstatSync,
|
|
477
|
+
realpathSync: options.realpathSync || fs.realpathSync.native,
|
|
478
|
+
};
|
|
479
|
+
const source = sourcePathFromOutput(parsed, deps);
|
|
480
|
+
try {
|
|
481
|
+
assertSafeInstalledPluginStructure(source);
|
|
482
|
+
} catch {
|
|
483
|
+
throw runtimeError(
|
|
484
|
+
MARKETPLACE_COMMAND_SOURCE_ERROR.INVALID_OUTPUT,
|
|
485
|
+
"Marketplace command source returned an unsafe plugin structure",
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
let payload;
|
|
489
|
+
try {
|
|
490
|
+
payload = buildMarketplacePayloadSbom(source);
|
|
491
|
+
} catch {
|
|
492
|
+
throw runtimeError(
|
|
493
|
+
MARKETPLACE_COMMAND_SOURCE_ERROR.INVALID_OUTPUT,
|
|
494
|
+
"Marketplace command source payload could not be verified",
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
return Object.freeze({
|
|
498
|
+
source,
|
|
499
|
+
mode: plan.mode,
|
|
500
|
+
payloadSha256: payload.digest,
|
|
501
|
+
fileCount: payload.fileCount,
|
|
502
|
+
totalBytes: payload.totalBytes,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function headersEntries(value) {
|
|
507
|
+
if (!value) return [];
|
|
508
|
+
if (Array.isArray(value)) return value;
|
|
509
|
+
if (typeof value.forEach === "function") {
|
|
510
|
+
const entries = [];
|
|
511
|
+
value.forEach((headerValue, name) => entries.push([name, headerValue]));
|
|
512
|
+
return entries;
|
|
513
|
+
}
|
|
514
|
+
return Object.entries(value);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function mergeHeaders(existing, dynamic) {
|
|
518
|
+
const merged = {};
|
|
519
|
+
const seen = new Set();
|
|
520
|
+
for (const [name, value] of headersEntries(existing)) {
|
|
521
|
+
const normalized = String(name).toLowerCase();
|
|
522
|
+
seen.add(normalized);
|
|
523
|
+
merged[name] = String(value);
|
|
524
|
+
}
|
|
525
|
+
for (const [name, value] of Object.entries(dynamic)) {
|
|
526
|
+
if (!seen.has(name.toLowerCase())) merged[name] = value;
|
|
527
|
+
}
|
|
528
|
+
return merged;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Decorate a fetch implementation so helper headers can leave the process
|
|
533
|
+
* only for the catalog origin. The artifact client invokes this per redirect
|
|
534
|
+
* hop, which naturally drops headers on every cross-origin hop.
|
|
535
|
+
*/
|
|
536
|
+
export function createSameOriginMarketplaceHeaderFetch(
|
|
537
|
+
fetchImpl,
|
|
538
|
+
trustedOrigin,
|
|
539
|
+
dynamicHeaders,
|
|
540
|
+
) {
|
|
541
|
+
if (typeof fetchImpl !== "function") {
|
|
542
|
+
throw runtimeError(
|
|
543
|
+
MARKETPLACE_HEADERS_HELPER_ERROR.PROCESS_START,
|
|
544
|
+
"Marketplace transport is unavailable",
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
const headers = normalizeHeaderMap(dynamicHeaders);
|
|
548
|
+
const origin = new URL(trustedOrigin).origin;
|
|
549
|
+
return (url, options = {}) => {
|
|
550
|
+
let matchesOrigin = false;
|
|
551
|
+
try {
|
|
552
|
+
matchesOrigin = new URL(String(url)).origin === origin;
|
|
553
|
+
} catch {
|
|
554
|
+
// Let the underlying transport preserve its own URL error behavior.
|
|
555
|
+
}
|
|
556
|
+
return fetchImpl(url, {
|
|
557
|
+
...options,
|
|
558
|
+
...(matchesOrigin
|
|
559
|
+
? { headers: mergeHeaders(options.headers, headers) }
|
|
560
|
+
: {}),
|
|
561
|
+
});
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* A non-secret binding fingerprint for short-lived in-memory authority maps.
|
|
567
|
+
* It intentionally hashes header names only; helper values are credentials and
|
|
568
|
+
* must never become cache/provenance/display material.
|
|
569
|
+
*/
|
|
570
|
+
export function marketplaceHeaderNamesFingerprint(headers) {
|
|
571
|
+
return crypto
|
|
572
|
+
.createHash("sha256")
|
|
573
|
+
.update(
|
|
574
|
+
Object.keys(headers || {})
|
|
575
|
+
.map((key) => key.toLowerCase())
|
|
576
|
+
.sort()
|
|
577
|
+
.join("\0"),
|
|
578
|
+
)
|
|
579
|
+
.digest("hex");
|
|
580
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
marketplaceCommandDescriptorIdentity,
|
|
3
|
+
normalizeMarketplaceCommandDescriptor,
|
|
4
|
+
} from "./marketplace-command-descriptor.js";
|
|
5
|
+
|
|
1
6
|
const SHA256_RE = /^[a-f0-9]{64}$/u;
|
|
2
|
-
const
|
|
3
|
-
"command",
|
|
4
|
-
"headers-helper",
|
|
5
|
-
"headershelper",
|
|
6
|
-
]);
|
|
7
|
+
const DISABLED_DYNAMIC_SOURCE_TYPES = new Set(["headers-helper", "headershelper"]);
|
|
7
8
|
|
|
8
9
|
export const MARKETPLACE_DYNAMIC_SOURCE_DISABLED_CODE =
|
|
9
10
|
"CC_MARKETPLACE_DYNAMIC_SOURCE_DISABLED";
|
|
@@ -60,8 +61,10 @@ function disabledDynamicSource(type) {
|
|
|
60
61
|
/**
|
|
61
62
|
* Normalize one registry package source without executing it. Git strings keep
|
|
62
63
|
* the legacy shape. Archive sources require an HTTPS URL and exact compressed
|
|
63
|
-
* byte digest.
|
|
64
|
-
* diagnostics but can never become executable
|
|
64
|
+
* byte digest. Legacy shell-string command/helper adapters remain
|
|
65
|
+
* representable for UI diagnostics but can never become executable authority.
|
|
66
|
+
* A typed `source: { type: "command", executable, args, cwd, ... }` is the
|
|
67
|
+
* one deliberate exception: it is parsed as an exact direct-argv descriptor.
|
|
65
68
|
*/
|
|
66
69
|
export function normalizeMarketplacePackageSource(entry, options = {}) {
|
|
67
70
|
const row = entry && typeof entry === "object" ? entry : {};
|
|
@@ -103,13 +106,35 @@ export function normalizeMarketplacePackageSource(entry, options = {}) {
|
|
|
103
106
|
const type = String(source.type || "")
|
|
104
107
|
.trim()
|
|
105
108
|
.toLowerCase();
|
|
106
|
-
if (
|
|
109
|
+
if (DISABLED_DYNAMIC_SOURCE_TYPES.has(type)) {
|
|
107
110
|
const disabled = disabledDynamicSource(type);
|
|
108
111
|
if (options.forExecution === true) {
|
|
109
112
|
throw sourceError(disabled.code, disabled.reason);
|
|
110
113
|
}
|
|
111
114
|
return disabled;
|
|
112
115
|
}
|
|
116
|
+
if (type === "command") {
|
|
117
|
+
const descriptor = normalizeMarketplaceCommandDescriptor(source, {
|
|
118
|
+
kind: "source",
|
|
119
|
+
allowType: true,
|
|
120
|
+
label: "Marketplace command source descriptor",
|
|
121
|
+
});
|
|
122
|
+
const normalized = {
|
|
123
|
+
type: "command",
|
|
124
|
+
mode: descriptor.mode,
|
|
125
|
+
identity: marketplaceCommandDescriptorIdentity(source),
|
|
126
|
+
};
|
|
127
|
+
// Descriptor values are deliberately retained for the immediate local
|
|
128
|
+
// materializer but are not enumerable, so catalog/listing/provenance JSON
|
|
129
|
+
// cannot accidentally disclose a local argv, path, or environment value.
|
|
130
|
+
for (const [key, value] of Object.entries(descriptor)) {
|
|
131
|
+
Object.defineProperty(normalized, key, {
|
|
132
|
+
value,
|
|
133
|
+
enumerable: false,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return Object.freeze(normalized);
|
|
137
|
+
}
|
|
113
138
|
if (type === "git") {
|
|
114
139
|
const value = bounded(source.url ?? source.source, 4096);
|
|
115
140
|
if (!value) {
|
|
@@ -80,7 +80,9 @@ export function collectPluginMcpServers(opts = {}) {
|
|
|
80
80
|
}
|
|
81
81
|
// An MCP stdio server spawns a command — gate it behind trust so a cloned
|
|
82
82
|
// repo's project plugin can't get a server launched the moment cc starts.
|
|
83
|
-
const { trusted, skipped } = partitionByTrust(plugins
|
|
83
|
+
const { trusted, skipped } = partitionByTrust(plugins, {
|
|
84
|
+
workspaceRoot: opts.cwd,
|
|
85
|
+
});
|
|
84
86
|
warnUntrustedOnce(
|
|
85
87
|
skipped.filter((p) => p.manifest?.components?.mcp).map((p) => p.name),
|
|
86
88
|
"mcp",
|
|
@@ -116,7 +116,9 @@ export function collectPluginMonitors(opts = {}) {
|
|
|
116
116
|
return [];
|
|
117
117
|
}
|
|
118
118
|
// A monitor spawns a command — gate it behind trust.
|
|
119
|
-
const { trusted, skipped } = partitionByTrust(plugins
|
|
119
|
+
const { trusted, skipped } = partitionByTrust(plugins, {
|
|
120
|
+
workspaceRoot: opts.cwd,
|
|
121
|
+
});
|
|
120
122
|
warnUntrustedOnce(
|
|
121
123
|
skipped.filter((p) => p.manifest?.components?.monitors).map((p) => p.name),
|
|
122
124
|
"monitors",
|