chainlesschain 0.165.5 → 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-CEeYTeDB.js → AIOps-B3gWmXjo.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BeRUQ3V2.js → ActionButton-G9pdT-0e.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DrV718XF.js → Analytics-Ca30J7m2.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Cd2Frt7Q.js → AppLayout-BtVTm8PU.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CUJMlORU.js → Artifacts-BhViNa6u.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CXJcfT6p.js → Audit-DhKq-FnY.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DOv6Z7H9.js → BackgroundAgents-BL0SVbnE.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BwdDyBI0.js → Backup-BWkblnFi.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BmLn4kYH.js → BaseInput-ClaRNrF8.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-COg8pAW2.js → Chat-sZE2Z77o.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BAfKOnRe.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-BCP4uoU8.js → Checkbox-Cvhay5WM.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-pRt6Y9JI.js → Codegen-BpejL-4N.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DLaprKhS.js → Col-g2Ki6g_1.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DyATsz5f.js → Community-D2xKbNo9.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BXAaupZK.js → Compact-DbL1Pds3.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DpcJBELr.js → Compliance-DGtZCV4c.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DG4EFWyd.js → Cowork-4kQpoNsI.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-F6Oy5n2P.js → Cron-BxTYXXAN.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BOdgtalX.js → Crosschain-k0J0LT2N.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DT1dSkjH.js → DID-BaqhoPgN.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-NBUIqWzC.js → Dashboard-T4RVPIM1.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CSpHeMYB.js → Dropdown-DszOToWq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CEG0xISr.js → EmailListRenderer-C9KJU7HF.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BTZ0BZna.js → FamilyGuardDashboard-Cmr0JnsA.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BuuCTn2V.js → Federation-BI-_2xZX.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-KAVceevk.js → FormItemContext-DsnDEPsl.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DnHpcQvW.js → GenericCardRenderer-BLncAKKK.js} +1 -1
- package/src/assets/web-panel/assets/{Git-C_B_0HwB.js → Git-BPQhGXDn.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DPSZuzuu.js → Governance-CyElbUQW.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BV-06V7q.js → Inference-CcSntBQD.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-wpDuupn6.js → KnowledgeGraph-p6e7HZ2r.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Bseca1_I.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-d3kM4DDL.js → Marketplace-Dd2notAJ.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DQ_xZ23i.js → McpTools-BBsN2CSW.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-CRIeYO4L.js → Memory-Dg2uTvwx.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BsllIa9l.js → MobileBridge-r1YMRiJV.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BUuQy30X.js → MobileProjects-C-89kSm4.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BOFDIzrC.js → Mtc-CrPZcVgf.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BQg-vyvG.js → MtcAudit-sxi7hqJb.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-Bo1jrPdN.js → Multisig-CAgOMfF1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Bmod8alw.js → NLProgramming-DafaPts3.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CGGFL8w-.js → Notes-CJM-xUW_.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BsZvCJxC.js → NotificationSettings-L0ZjfAh_.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C1-Quo_h.js → OrderTableRenderer-BB1CvCK0.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BjOeDYOi.js → Organization-DeLtgAD8.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DwVV3l5A.js → Overflow-CKC2b10z.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Cu3DQT9W.js → P2P-eyf2rzY1.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Cg0gR7dH.js → PdhVaultBrowser-5upNIOW8.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CsAXxRN5.js → Permissions-B8p3W6JK.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-C-nNmnYp.js → PersonalDataHub-BnRMxTq3.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-BOnW7nNc.js → Pipeline-DjCNI1li.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-ClLSyGKt.js → Privacy-u6gXLbWT.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DuLvjjq7.js → ProjectInit-BZAw4tkM.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DQYYbDD9.js → ProjectSettings-B1Tr72q7.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Drw8MBGK.js → Projects-DO7HFuBQ.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-ckqHQe6K.js → Providers-BjCztyhj.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DW5BXk-G.js → QrScannerModal-ZWyCUbR1.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DDLnhNTp.js → QuickAsk-DhDjeW4G.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-PJQ2C91U.js → Recommend-R_CN2MuU.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-s6wmda34.js → RemoteSession-uxFcujeu.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DC2sb-I3.js → Reputation-DX8D7rks.js} +1 -1
- package/src/assets/web-panel/assets/{Row-hPGk1eGj.js → Row-BYahQILD.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-RIvfZql0.js → RssFeed-CUSQ2tm-.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DFg1G1eR.js → Search-CnN_S8Cs.js} +1 -1
- package/src/assets/web-panel/assets/{Security-D6H5p8Dp.js → Security-DcZlh-O4.js} +4 -4
- package/src/assets/web-panel/assets/{Services-nxYiFV_w.js → Services-ZhBOjzIX.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BmgFNAdB.js → Skeleton-D5Qnq_lI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CsaaD2av.js → Skills-BC9cZ3Be.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-yM5y9yvK.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-CFVhEyyr.js → SyncSettings-DUvJbSct.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DdO02Inn.js → Tasks-BXa-5noQ.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Eo2d8Llu.js → Templates-JxaiUt_3.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-ChESOXgy.js → Tenant-BW4ERQN8.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BJ7oDxTI.js → Terminal-DltUbdtv.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-hWX0fUvz.js → TimelineRenderer-CMoZz0Tc.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DVBZQnd-.js → Tokens-D_VEV60K.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-D89cQfVq.js → Trigger-CCXTCs0R.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BIVSb4-V.js → Trust-lkGRIPcG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B58gWopl.js → UkeySign-BfGeQ9HX.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DhHI7bmG.js → VideoEditing-BEC7pYvc.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-C4FheWS_.js → Wallet-BZdG24o0.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DnPRq-Bx.js → WebAuthn-yoEddedp.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-BerRPjpJ.js → WorkflowEditor-BXZEprOW.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BzZ52iKr.js → chat-gg0bzZal.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BI_E4BAn.js → colors-DjRVRuin.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-d50mr3yk.js → compact-item-su-dieAc.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C6FsQ1IH.js → createContext-Ci3S8oKF.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CxbFX9uH.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DLZEMgLP.js → hasIn-D8F10heH.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-aBumtY.js → index-11wtYQiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CjuKZBqJ.js → index-1IgxgALJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-9H_M2jju.js → index-1agckWSq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE0V6cp-.js → index-5JqhujnR.js} +1 -1
- package/src/assets/web-panel/assets/{index-Y2-q7QLm.js → index-6Tniqk1s.js} +1 -1
- package/src/assets/web-panel/assets/{index-DoFtCVZ7.js → index-8Z-8BIlN.js} +1 -1
- package/src/assets/web-panel/assets/{index-KNGgO4Wm.js → index-BCI5v0yB.js} +1 -1
- package/src/assets/web-panel/assets/{index-DY4j5zkF.js → index-BDJntYmJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZjjdQ62.js → index-BGzixOGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTpIOTfR.js → index-BMMOVoLD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BB9SqVSt.js → index-BTFpdBtt.js} +1 -1
- package/src/assets/web-panel/assets/{index-AHr7ryWf.js → index-Ba_HyPnE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Pvf30m8Q.js → index-BbF1IHof.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRN5TRTQ.js → index-BgT9su_e.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUaEHfQ0.js → index-BzPB6kGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdKt9fOd.js → index-C6ygzplr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dec0Kf_Z.js → index-CC3ZDjzY.js} +1 -1
- package/src/assets/web-panel/assets/{index-IPkwSAW_.js → index-CEmXRhAE.js} +1 -1
- package/src/assets/web-panel/assets/index-CJGOlduY.js +1 -0
- package/src/assets/web-panel/assets/{index-C3k8kevG.js → index-CMPqd5Cv.js} +1 -1
- package/src/assets/web-panel/assets/{index-A5CDWP7y.js → index-COWd8uGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-CO8DAF7U.js → index-CPIYSp4H.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVzuawCX.js → index-CR0h6seT.js} +1 -1
- package/src/assets/web-panel/assets/{index-Co7s9LsB.js → index-CUmDhC4j.js} +1 -1
- package/src/assets/web-panel/assets/index-CkBCN5yH.js +1 -0
- package/src/assets/web-panel/assets/{index-CLoTQr0W.js → index-CusgZPtc.js} +1 -1
- package/src/assets/web-panel/assets/{index-B81mo1Et.js → index-DBAETXft.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcSUl9fA.js → index-DIJ2qFOC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q5Tn735s.js → index-Dbq4NMET.js} +1 -1
- package/src/assets/web-panel/assets/{index-GXE0D8Nx.js → index-DkMEF5VT.js} +1 -1
- package/src/assets/web-panel/assets/{index-TWG-_D1M.js → index-DkfQR3hM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAE3hwev.js → index-DnLucOn9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgkoH3_g.js → index-DugB_aKa.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRGx3w6T.js → index-DyLUarhZ.js} +3 -3
- package/src/assets/web-panel/assets/{index-BeM4hfht.js → index-T6_6ygJZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsscwxPE.js → index-Tu8eWqh-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bmr_vxXD.js → index-W7XUgdVL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoNZlzhQ.js → index-dHZinfko.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqkbHHg8.js → index-jyNgp5FI.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D-4Hzgej.js → initDefaultProps-Cv4p8e4w.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D4Wl4xU7.js → motion-B8EwmMOw.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cd5eQvL0.js → move-B8R9F8TK.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-B4ilslCW.js → mtc-parser-DoFYz5Bp.js} +1 -1
- package/src/assets/web-panel/assets/{omit-Ckarx-tf.js → omit-CHWYLBjI.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-QJ0bpCmc.js → pickAttrs-CXuPDO77.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CKHm-tq8.js → placementArrow-BaAtUUon.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CV6a_w_Y.js → responsiveObserve-DCc4zcDD.js} +1 -1
- package/src/assets/web-panel/assets/{slide-mHpeQFgk.js → slide-B_lx89oZ.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DIaQNQWA.js → statusUtils-DE8DPmR0.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-PJpzavV8.js → styleChecker-1ktXOsL_.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BH7zTw8S.js → useFlexGapSupport-xH89-wjh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BDNG3JDd.js → useFs-DscvfW5p.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CY2VWdUg.js → usePersonalDataHub-RdMKQta7.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-D0kw9jhz.js → vnode-Z3wTni2T.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B5g7TN7r.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 +105 -62
- package/src/commands/background-session.js +100 -10
- package/src/commands/git.js +39 -0
- package/src/commands/mcp.js +5 -0
- package/src/commands/plugin.js +160 -29
- package/src/commands/remote-control.js +295 -110
- package/src/commands/session-group.js +131 -0
- package/src/commands/session-location.js +48 -0
- package/src/commands/session-message.js +171 -0
- package/src/commands/session.js +22 -0
- package/src/commands/sync-providers.js +4 -1
- package/src/commands/update.js +1 -0
- package/src/data/changelog.json +41 -2
- 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 +832 -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/background-session-transport.js +153 -27
- 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-client.js +2 -0
- package/src/lib/mcp-headers-helper.js +47 -5
- package/src/lib/mcp-lifecycle-authority.js +887 -0
- package/src/lib/mcp-oauth.js +156 -26
- package/src/lib/mcp-tls.js +330 -0
- package/src/lib/output-styles.js +12 -0
- 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 +730 -71
- package/src/lib/plugin-runtime/lsp.js +4 -4
- package/src/lib/plugin-runtime/marketplace-archive-source.js +366 -0
- package/src/lib/plugin-runtime/marketplace-catalog.js +63 -6
- 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-remote-artifacts.js +1 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +188 -0
- package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
- 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 +583 -58
- package/src/lib/plugin-runtime/settings.js +3 -1
- package/src/lib/plugin-runtime/trust.js +274 -13
- package/src/lib/plugin-security.js +393 -41
- package/src/lib/plugin-source-identity.js +850 -0
- 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/remote-approval-bridge.js +136 -46
- package/src/lib/remote-control.js +217 -31
- 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 +1252 -0
- package/src/lib/session-projection.js +286 -3
- package/src/lib/session-runtime-retention.js +155 -0
- package/src/lib/session-store-guard.js +3 -1
- package/src/lib/session-workbench-store.js +466 -0
- package/src/lib/settings-loader.cjs +29 -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 +175 -19
- package/src/repl/permission-prompt.js +29 -8
- package/src/repl/repl-keybindings.js +55 -0
- package/src/runtime/agent-core.js +362 -20
- package/src/runtime/bootstrap.js +37 -13
- package/src/runtime/coding-agent-contract-shared.cjs +11 -4
- package/src/runtime/headless-runner.js +281 -102
- package/src/runtime/headless-stream.js +199 -30
- package/src/runtime/mcp-config.js +298 -6
- package/src/runtime/policies/agent-policy.js +10 -0
- package/src/runtime/status-diagnostics-lite.js +9 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-4gGkdGgA.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-B4RLp2zs.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-DPy1XMYh.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CagEcIex.js +0 -1
- package/src/assets/web-panel/assets/index-pzT9PpOU.js +0 -1
- package/src/assets/web-panel/assets/index-z1kFadaX.js +0 -1
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
* 2. Single-plugin manifest — one plugin, no selection needed:
|
|
17
17
|
* { "name": "toml-tools", "source": "https://github.com/x/toml.git", "ref": "v1.0.0" }
|
|
18
18
|
*
|
|
19
|
-
* The remote layer is an INDIRECTION: it resolves a name
|
|
20
|
-
* that the existing installer (`installFromSource`)
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* installing works OFFLINE,
|
|
19
|
+
* The remote layer is an INDIRECTION: it resolves a name to a static package
|
|
20
|
+
* source that the existing installer (`installFromSource`) can consume.
|
|
21
|
+
* Static git and digest-bound archive sources both reuse the existing install
|
|
22
|
+
* core. Downloaded registry JSON and archives are cached content-addressed so
|
|
23
|
+
* browsing / installing works OFFLINE, while bearer tokens never become
|
|
24
|
+
* persisted source authority.
|
|
24
25
|
*/
|
|
25
26
|
|
|
26
27
|
import fs from "fs";
|
|
@@ -28,7 +29,26 @@ import os from "os";
|
|
|
28
29
|
import path from "path";
|
|
29
30
|
import crypto from "crypto";
|
|
30
31
|
import { getElectronUserDataDir } from "../paths.js";
|
|
32
|
+
import {
|
|
33
|
+
enforcePluginSourcePolicy,
|
|
34
|
+
resolvePluginManagedPolicy,
|
|
35
|
+
} from "../plugin-security.js";
|
|
36
|
+
import {
|
|
37
|
+
assertPluginGitTransportSafe,
|
|
38
|
+
parsePluginGitSource,
|
|
39
|
+
redactPluginSourceForDisplay,
|
|
40
|
+
} from "../plugin-source-identity.js";
|
|
31
41
|
import { createMarketplaceNetworkTransport } from "./marketplace-network.js";
|
|
42
|
+
import { fetchAndMaterializeMarketplaceArchive } from "./marketplace-archive-source.js";
|
|
43
|
+
import {
|
|
44
|
+
createSameOriginMarketplaceHeaderFetch,
|
|
45
|
+
runMarketplaceCommandSource,
|
|
46
|
+
runMarketplaceHeadersHelper,
|
|
47
|
+
} from "./marketplace-command-source.js";
|
|
48
|
+
import {
|
|
49
|
+
assertMarketplaceSourceExecutable,
|
|
50
|
+
normalizeMarketplacePackageSource,
|
|
51
|
+
} from "./marketplace-source-adapter.js";
|
|
32
52
|
|
|
33
53
|
export const _deps = {
|
|
34
54
|
// Node 22+ global fetch; injectable for tests.
|
|
@@ -52,6 +72,11 @@ const DEFAULT_TIMEOUT_MS = 15_000;
|
|
|
52
72
|
export const MAX_REGISTRY_DOCUMENT_BYTES = 4 * 1024 * 1024;
|
|
53
73
|
export const MAX_REGISTRY_CACHE_CANDIDATES = 64;
|
|
54
74
|
const SHA256_RE = /^[a-f0-9]{64}$/;
|
|
75
|
+
const CONTROL_RE = /[\u0000-\u001f\u007f]/u;
|
|
76
|
+
const MAX_REGISTRY_SOURCE_CHARS = 4096;
|
|
77
|
+
const MAX_REGISTRY_REF_CHARS = 256;
|
|
78
|
+
const fetchedRegistryAuthorities = new WeakMap();
|
|
79
|
+
const registryResolutionAuthorities = new WeakMap();
|
|
55
80
|
|
|
56
81
|
/** True when `raw` looks like a remote registry/manifest URL (http(s) + .json). */
|
|
57
82
|
export function isRemoteSource(raw) {
|
|
@@ -67,15 +92,19 @@ export function isRemoteSource(raw) {
|
|
|
67
92
|
* explicit opt-in (opts.allowInsecure / CC_PLUGIN_REGISTRY_ALLOW_HTTP=1).
|
|
68
93
|
*/
|
|
69
94
|
export function assertRegistryUrlSafe(url, { allowInsecure = false } = {}) {
|
|
95
|
+
const displayUrl = redactPluginSourceForDisplay(url);
|
|
70
96
|
let u;
|
|
71
97
|
try {
|
|
72
98
|
u = new URL(String(url));
|
|
73
99
|
} catch {
|
|
74
|
-
throw new Error(`invalid registry URL: ${
|
|
100
|
+
throw new Error(`invalid registry URL: ${displayUrl}`);
|
|
101
|
+
}
|
|
102
|
+
if (u.username || u.password) {
|
|
103
|
+
throw new Error("registry URL credentials are not supported");
|
|
75
104
|
}
|
|
76
105
|
if (u.protocol === "https:") return;
|
|
77
106
|
if (u.protocol !== "http:") {
|
|
78
|
-
throw new Error(`registry URL must be http(s): ${
|
|
107
|
+
throw new Error(`registry URL must be http(s): ${displayUrl}`);
|
|
79
108
|
}
|
|
80
109
|
// WHATWG URL keeps the brackets on IPv6 hostnames — strip before comparing.
|
|
81
110
|
const host = u.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
@@ -85,7 +114,7 @@ export function assertRegistryUrlSafe(url, { allowInsecure = false } = {}) {
|
|
|
85
114
|
allowInsecure === true || process.env.CC_PLUGIN_REGISTRY_ALLOW_HTTP === "1";
|
|
86
115
|
if (loopback || optIn) return;
|
|
87
116
|
throw new Error(
|
|
88
|
-
`plain-HTTP registry rejected: ${
|
|
117
|
+
`plain-HTTP registry rejected: ${displayUrl} — a network MITM controls both the ` +
|
|
89
118
|
`source and its sha256, so the integrity check verifies nothing. Use ` +
|
|
90
119
|
`https, or opt in with --allow-insecure-registry / ` +
|
|
91
120
|
`CC_PLUGIN_REGISTRY_ALLOW_HTTP=1 on a trusted network.`,
|
|
@@ -143,6 +172,16 @@ export function resolveRegistryToken(url, { token, config } = {}) {
|
|
|
143
172
|
* documentSha256: string }>}
|
|
144
173
|
*/
|
|
145
174
|
export async function fetchRegistry(url, opts = {}) {
|
|
175
|
+
const displayUrl = redactPluginSourceForDisplay(url);
|
|
176
|
+
assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
|
|
177
|
+
const managedPolicy = resolveManagedPolicy(opts);
|
|
178
|
+
// This gate intentionally runs before transport construction. A blocked or
|
|
179
|
+
// non-allowlisted marketplace must not reach proxy helpers, DNS, or fetch.
|
|
180
|
+
enforcePluginSourcePolicy(url, managedPolicy, {
|
|
181
|
+
action: opts.policyAction || "registry-fetch",
|
|
182
|
+
cwd: opts.cwd,
|
|
183
|
+
kindHint: "registry",
|
|
184
|
+
});
|
|
146
185
|
const {
|
|
147
186
|
token,
|
|
148
187
|
cacheDir,
|
|
@@ -150,7 +189,6 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
150
189
|
allowCache = true,
|
|
151
190
|
offline = false,
|
|
152
191
|
} = opts;
|
|
153
|
-
assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
|
|
154
192
|
const expectedSha256 = normalizeRegistrySha256(
|
|
155
193
|
opts.expectedSha256,
|
|
156
194
|
"expected registry document SHA-256",
|
|
@@ -163,24 +201,46 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
163
201
|
if (!allowCache) {
|
|
164
202
|
throw new Error("offline registry mode requires immutable cache access");
|
|
165
203
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
204
|
+
let cached;
|
|
205
|
+
try {
|
|
206
|
+
cached = readCachedRegistry({
|
|
207
|
+
url,
|
|
208
|
+
cacheDir,
|
|
209
|
+
expectedSha256,
|
|
210
|
+
});
|
|
211
|
+
} catch (error) {
|
|
212
|
+
throw new Error(
|
|
213
|
+
`registry ${displayUrl} verified immutable cache rejected: ${publicRegistryCacheErrorMessage(error)}`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
if (cached) return bindFetchedRegistry(cached, url, managedPolicy);
|
|
172
217
|
throw new Error(
|
|
173
|
-
`registry ${
|
|
218
|
+
`registry ${displayUrl} is unavailable in the verified immutable cache` +
|
|
174
219
|
(expectedSha256 ? ` at SHA-256 ${expectedSha256}` : ""),
|
|
175
220
|
);
|
|
176
221
|
}
|
|
177
222
|
|
|
223
|
+
// This descriptor is local operator configuration, never registry metadata.
|
|
224
|
+
// It is intentionally evaluated only for the registry request and later
|
|
225
|
+
// carried in an opaque authority for the selected same-origin archive.
|
|
226
|
+
const marketplaceRequestHeaders = await resolveMarketplaceCatalogHeaders(
|
|
227
|
+
url,
|
|
228
|
+
opts,
|
|
229
|
+
);
|
|
230
|
+
|
|
178
231
|
const networkTransport = createMarketplaceNetworkTransport({
|
|
179
232
|
proxyUrl: opts.proxyUrl,
|
|
180
233
|
pacFile: opts.pacFile,
|
|
181
234
|
caFile: opts.caFile,
|
|
182
235
|
});
|
|
183
|
-
const
|
|
236
|
+
const transportFetch = networkTransport?.fetch || _deps.fetch;
|
|
237
|
+
const fetchImpl = marketplaceRequestHeaders
|
|
238
|
+
? createSameOriginMarketplaceHeaderFetch(
|
|
239
|
+
transportFetch,
|
|
240
|
+
new URL(url).origin,
|
|
241
|
+
marketplaceRequestHeaders,
|
|
242
|
+
)
|
|
243
|
+
: transportFetch;
|
|
184
244
|
let networkErr = null;
|
|
185
245
|
let cacheFallbackAllowed = false;
|
|
186
246
|
try {
|
|
@@ -190,37 +250,61 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
190
250
|
try {
|
|
191
251
|
res = await fetchImpl(url, {
|
|
192
252
|
headers,
|
|
193
|
-
redirect: "
|
|
253
|
+
redirect: "manual",
|
|
194
254
|
signal: controller.signal,
|
|
195
255
|
});
|
|
196
256
|
} finally {
|
|
197
257
|
clearTimeout(timer);
|
|
198
258
|
}
|
|
259
|
+
if (
|
|
260
|
+
res?.redirected === true ||
|
|
261
|
+
(Number(res?.status) >= 300 && Number(res?.status) < 400) ||
|
|
262
|
+
responseUrlChanged(url, res?.url)
|
|
263
|
+
) {
|
|
264
|
+
throw publicRegistryFailure(
|
|
265
|
+
"registry redirects are disabled because each hop cannot be policy-validated",
|
|
266
|
+
);
|
|
267
|
+
}
|
|
199
268
|
if (!res.ok) {
|
|
200
|
-
const
|
|
201
|
-
|
|
269
|
+
const status = Number(res.status);
|
|
270
|
+
throw publicRegistryFailure(
|
|
271
|
+
`registry fetch failed: HTTP ${Number.isInteger(status) ? status : "unknown"}`,
|
|
272
|
+
{ cacheFallbackAllowed: status >= 500 },
|
|
202
273
|
);
|
|
203
|
-
error.cacheFallbackAllowed = Number(res.status) >= 500;
|
|
204
|
-
throw error;
|
|
205
274
|
}
|
|
206
|
-
|
|
275
|
+
let bytes;
|
|
276
|
+
try {
|
|
277
|
+
bytes = await readBoundedRegistryResponse(res);
|
|
278
|
+
} catch {
|
|
279
|
+
throw publicRegistryFailure("registry response body is invalid");
|
|
280
|
+
}
|
|
207
281
|
const documentSha256 = sha256(bytes);
|
|
208
282
|
if (expectedSha256 && documentSha256 !== expectedSha256) {
|
|
209
|
-
throw
|
|
283
|
+
throw publicRegistryFailure(
|
|
210
284
|
`registry document SHA-256 mismatch: expected ${expectedSha256}, got ${documentSha256}`,
|
|
211
285
|
);
|
|
212
286
|
}
|
|
213
|
-
|
|
214
|
-
|
|
287
|
+
let registry;
|
|
288
|
+
try {
|
|
289
|
+
const text = decodeRegistryUtf8(bytes);
|
|
290
|
+
registry = validateRegistry(JSON.parse(text), url);
|
|
291
|
+
} catch {
|
|
292
|
+
throw publicRegistryFailure("registry document is invalid");
|
|
293
|
+
}
|
|
215
294
|
if (allowCache) writeImmutableCache(url, cacheDir, bytes, documentSha256);
|
|
216
|
-
return
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
295
|
+
return bindFetchedRegistry(
|
|
296
|
+
{
|
|
297
|
+
registry,
|
|
298
|
+
fromCache: false,
|
|
299
|
+
documentSha256,
|
|
300
|
+
...(networkTransport?.authority
|
|
301
|
+
? { networkAuthority: networkTransport.authority }
|
|
302
|
+
: {}),
|
|
303
|
+
},
|
|
304
|
+
url,
|
|
305
|
+
managedPolicy,
|
|
306
|
+
marketplaceRequestHeaders,
|
|
307
|
+
);
|
|
224
308
|
} catch (err) {
|
|
225
309
|
networkErr = err;
|
|
226
310
|
cacheFallbackAllowed =
|
|
@@ -228,7 +312,12 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
228
312
|
err?.name === "AbortError" ||
|
|
229
313
|
err instanceof TypeError;
|
|
230
314
|
} finally {
|
|
231
|
-
|
|
315
|
+
try {
|
|
316
|
+
await networkTransport?.close();
|
|
317
|
+
} catch {
|
|
318
|
+
// Transport teardown is best-effort and must not override a verified
|
|
319
|
+
// response or surface an implementation error that echoed request URLs.
|
|
320
|
+
}
|
|
232
321
|
}
|
|
233
322
|
|
|
234
323
|
// Only transport failures and server-side outages may fall back. Authentication,
|
|
@@ -240,21 +329,60 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
240
329
|
cacheDir,
|
|
241
330
|
expectedSha256,
|
|
242
331
|
});
|
|
243
|
-
if (cached)
|
|
332
|
+
if (cached) {
|
|
333
|
+
return bindFetchedRegistry(
|
|
334
|
+
cached,
|
|
335
|
+
url,
|
|
336
|
+
managedPolicy,
|
|
337
|
+
marketplaceRequestHeaders,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
244
340
|
} catch (cacheError) {
|
|
245
341
|
throw new Error(
|
|
246
|
-
`could not fetch registry ${
|
|
342
|
+
`could not fetch registry ${displayUrl}: ${publicRegistryErrorMessage(networkErr)}; verified immutable cache rejected: ${publicRegistryCacheErrorMessage(cacheError)}`,
|
|
247
343
|
);
|
|
248
344
|
}
|
|
249
345
|
}
|
|
250
346
|
throw new Error(
|
|
251
|
-
`could not fetch registry ${
|
|
347
|
+
`could not fetch registry ${displayUrl}: ${publicRegistryErrorMessage(networkErr)}` +
|
|
252
348
|
(allowCache && cacheFallbackAllowed
|
|
253
349
|
? " (no verified immutable cache available)"
|
|
254
350
|
: ""),
|
|
255
351
|
);
|
|
256
352
|
}
|
|
257
353
|
|
|
354
|
+
function configuredMarketplaceHeadersHelper(url, opts) {
|
|
355
|
+
if (opts.headersHelper != null) return opts.headersHelper;
|
|
356
|
+
const marketplace = opts.config?.plugins?.marketplace;
|
|
357
|
+
if (!marketplace || typeof marketplace !== "object") return null;
|
|
358
|
+
let origin = null;
|
|
359
|
+
try {
|
|
360
|
+
origin = new URL(url).origin;
|
|
361
|
+
} catch {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
const perOrigin = marketplace.headersHelpers;
|
|
365
|
+
if (
|
|
366
|
+
perOrigin &&
|
|
367
|
+
typeof perOrigin === "object" &&
|
|
368
|
+
Object.prototype.hasOwnProperty.call(perOrigin, origin)
|
|
369
|
+
) {
|
|
370
|
+
return perOrigin[origin];
|
|
371
|
+
}
|
|
372
|
+
return marketplace.headersHelper ?? null;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async function resolveMarketplaceCatalogHeaders(url, opts) {
|
|
376
|
+
const descriptor = configuredMarketplaceHeadersHelper(url, opts);
|
|
377
|
+
if (descriptor == null) return null;
|
|
378
|
+
return runMarketplaceHeadersHelper(descriptor, {
|
|
379
|
+
platform: opts.platform,
|
|
380
|
+
spawn: opts.headersHelperSpawn,
|
|
381
|
+
spawnSync: opts.headersHelperSpawnSync,
|
|
382
|
+
kill: opts.headersHelperKill,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
258
386
|
async function readBoundedRegistryResponse(response) {
|
|
259
387
|
const contentLength = Number(response?.headers?.get?.("content-length"));
|
|
260
388
|
if (
|
|
@@ -475,37 +603,220 @@ function sha256(value) {
|
|
|
475
603
|
|
|
476
604
|
/** Validate + normalize a fetched document into a registry object. Throws on garbage. */
|
|
477
605
|
export function validateRegistry(doc, url = "") {
|
|
606
|
+
const displayUrl = redactPluginSourceForDisplay(url);
|
|
478
607
|
if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
|
|
479
|
-
throw new Error(`registry at ${
|
|
608
|
+
throw new Error(`registry at ${displayUrl} is not a JSON object`);
|
|
480
609
|
}
|
|
481
610
|
if (Array.isArray(doc.plugins)) {
|
|
482
611
|
for (const p of doc.plugins) {
|
|
483
|
-
if (
|
|
612
|
+
if (
|
|
613
|
+
!p ||
|
|
614
|
+
typeof p !== "object" ||
|
|
615
|
+
typeof p.name !== "string" ||
|
|
616
|
+
!p.name.trim() ||
|
|
617
|
+
(p.source == null && p.command == null && p.headersHelper == null)
|
|
618
|
+
) {
|
|
484
619
|
throw new Error(
|
|
485
|
-
`registry at ${
|
|
620
|
+
`registry at ${displayUrl} has a plugin entry missing name/source`,
|
|
486
621
|
);
|
|
487
622
|
}
|
|
623
|
+
const normalized = normalizeMarketplacePackageSource(p);
|
|
624
|
+
if (normalized.type === "git") normalizeRegistryPluginSource(p);
|
|
488
625
|
}
|
|
489
626
|
return doc;
|
|
490
627
|
}
|
|
491
628
|
// Single-plugin manifest.
|
|
492
|
-
if (doc.source) {
|
|
629
|
+
if (doc.source != null || doc.command != null || doc.headersHelper != null) {
|
|
630
|
+
const normalized = normalizeMarketplacePackageSource(doc);
|
|
631
|
+
if (normalized.type === "git") normalizeRegistryPluginSource(doc);
|
|
493
632
|
return { plugins: [doc] };
|
|
494
633
|
}
|
|
495
634
|
throw new Error(
|
|
496
|
-
`registry at ${
|
|
635
|
+
`registry at ${displayUrl} must have a "plugins" array or a top-level "source"`,
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function freezeRegistryValue(value) {
|
|
640
|
+
if (!value || typeof value !== "object" || Object.isFrozen(value)) {
|
|
641
|
+
return value;
|
|
642
|
+
}
|
|
643
|
+
for (const child of Object.values(value)) freezeRegistryValue(child);
|
|
644
|
+
return Object.freeze(value);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function bindFetchedRegistry(
|
|
648
|
+
result,
|
|
649
|
+
registryUrl,
|
|
650
|
+
managedPolicy,
|
|
651
|
+
marketplaceRequestHeaders = null,
|
|
652
|
+
) {
|
|
653
|
+
const registry = freezeRegistryValue(result.registry);
|
|
654
|
+
fetchedRegistryAuthorities.set(
|
|
655
|
+
registry,
|
|
656
|
+
Object.freeze({
|
|
657
|
+
registryUrl: String(registryUrl),
|
|
658
|
+
documentSha256: result.documentSha256 || null,
|
|
659
|
+
managedPolicy,
|
|
660
|
+
// Credential values live only in this per-process, opaque binding. They
|
|
661
|
+
// never enter catalog, cache, provenance, or result JSON.
|
|
662
|
+
marketplaceRequestHeaders: marketplaceRequestHeaders
|
|
663
|
+
? Object.freeze({ ...marketplaceRequestHeaders })
|
|
664
|
+
: null,
|
|
665
|
+
}),
|
|
666
|
+
);
|
|
667
|
+
return { ...result, registry };
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/** Validate one catalog entry as a bounded, remote Git source. */
|
|
671
|
+
export function normalizeRegistryPluginSource(entry) {
|
|
672
|
+
if (!entry || typeof entry !== "object") {
|
|
673
|
+
throw new Error("registry plugin entry is invalid");
|
|
674
|
+
}
|
|
675
|
+
if (
|
|
676
|
+
typeof entry.source !== "string" ||
|
|
677
|
+
entry.source !== entry.source.trim() ||
|
|
678
|
+
!entry.source ||
|
|
679
|
+
entry.source.length > MAX_REGISTRY_SOURCE_CHARS ||
|
|
680
|
+
CONTROL_RE.test(entry.source) ||
|
|
681
|
+
entry.source.includes("#")
|
|
682
|
+
) {
|
|
683
|
+
throw new Error("registry plugin source must be a bounded string");
|
|
684
|
+
}
|
|
685
|
+
let ref = null;
|
|
686
|
+
if (entry.ref != null && entry.ref !== "") {
|
|
687
|
+
if (
|
|
688
|
+
typeof entry.ref !== "string" ||
|
|
689
|
+
entry.ref !== entry.ref.trim() ||
|
|
690
|
+
!entry.ref ||
|
|
691
|
+
entry.ref.length > MAX_REGISTRY_REF_CHARS ||
|
|
692
|
+
CONTROL_RE.test(entry.ref) ||
|
|
693
|
+
entry.ref.includes("#") ||
|
|
694
|
+
entry.source.includes("#")
|
|
695
|
+
) {
|
|
696
|
+
throw new Error("registry plugin ref must be a bounded exact string");
|
|
697
|
+
}
|
|
698
|
+
ref = entry.ref;
|
|
699
|
+
}
|
|
700
|
+
const source = ref ? `${entry.source}#${ref}` : entry.source;
|
|
701
|
+
let git;
|
|
702
|
+
try {
|
|
703
|
+
git = parsePluginGitSource(source);
|
|
704
|
+
assertPluginGitTransportSafe(git, {
|
|
705
|
+
allowFile: false,
|
|
706
|
+
requireRemote: true,
|
|
707
|
+
});
|
|
708
|
+
} catch {
|
|
709
|
+
throw new Error("registry plugin source must be a safe remote Git locator");
|
|
710
|
+
}
|
|
711
|
+
return { source, ref, git };
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Issue an unforgeable in-process capability for a selected entry. The entry
|
|
716
|
+
* must be an exact member of a registry object returned by fetchRegistry;
|
|
717
|
+
* callers cannot manufacture registry metadata to downgrade source policy.
|
|
718
|
+
*/
|
|
719
|
+
export function authorizeRegistryPluginEntry(
|
|
720
|
+
registry,
|
|
721
|
+
entry,
|
|
722
|
+
{ registryUrl, cwd } = {},
|
|
723
|
+
) {
|
|
724
|
+
const fetched = fetchedRegistryAuthorities.get(registry);
|
|
725
|
+
if (!fetched || fetched.registryUrl !== String(registryUrl)) {
|
|
726
|
+
throw new Error("registry source authority is missing or mismatched");
|
|
727
|
+
}
|
|
728
|
+
if (!registry.plugins?.some((candidate) => candidate === entry)) {
|
|
729
|
+
throw new Error("registry entry is not bound to the fetched catalog");
|
|
730
|
+
}
|
|
731
|
+
const normalized = inspectRegistryEntrySource(entry);
|
|
732
|
+
if (normalized.sourceType === "git") {
|
|
733
|
+
enforcePluginSourcePolicy(normalized.source, fetched.managedPolicy, {
|
|
734
|
+
action: "registry-resolved-source",
|
|
735
|
+
blockedOnly: true,
|
|
736
|
+
cwd,
|
|
737
|
+
kindHint: "git",
|
|
738
|
+
});
|
|
739
|
+
} else if (normalized.sourceType === "archive") {
|
|
740
|
+
let registryOrigin;
|
|
741
|
+
let archiveOrigin;
|
|
742
|
+
try {
|
|
743
|
+
registryOrigin = new URL(fetched.registryUrl).origin;
|
|
744
|
+
archiveOrigin = new URL(normalized.sourceIdentity).origin;
|
|
745
|
+
} catch {
|
|
746
|
+
throw new Error("registry archive source URL authority is invalid");
|
|
747
|
+
}
|
|
748
|
+
if (archiveOrigin !== registryOrigin) {
|
|
749
|
+
throw new Error("registry archive source must use the registry origin");
|
|
750
|
+
}
|
|
751
|
+
} else if (normalized.sourceType !== "command") {
|
|
752
|
+
throw new Error("registry package source type is unsupported");
|
|
753
|
+
}
|
|
754
|
+
const authority = Object.freeze({});
|
|
755
|
+
registryResolutionAuthorities.set(
|
|
756
|
+
authority,
|
|
757
|
+
Object.freeze({
|
|
758
|
+
registryUrl: fetched.registryUrl,
|
|
759
|
+
source: normalized.source,
|
|
760
|
+
declaredSource: normalized.sourceIdentity,
|
|
761
|
+
sourceType: normalized.sourceType,
|
|
762
|
+
ref: normalized.ref,
|
|
763
|
+
archiveSha256:
|
|
764
|
+
normalized.sourceType === "archive"
|
|
765
|
+
? normalized.archiveSpec.sha256
|
|
766
|
+
: null,
|
|
767
|
+
payloadSha256: null,
|
|
768
|
+
documentSha256: fetched.documentSha256,
|
|
769
|
+
commandMode:
|
|
770
|
+
normalized.sourceType === "command" ? normalized.commandSpec.mode : null,
|
|
771
|
+
marketplaceRequestHeaders: fetched.marketplaceRequestHeaders,
|
|
772
|
+
}),
|
|
497
773
|
);
|
|
774
|
+
return {
|
|
775
|
+
...normalized,
|
|
776
|
+
registryResolutionAuthority: authority,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/** Consume-only validation used by the installer before any target I/O. */
|
|
781
|
+
export function assertRegistryResolutionAuthority(
|
|
782
|
+
authority,
|
|
783
|
+
{ registryUrl, source, ref } = {},
|
|
784
|
+
) {
|
|
785
|
+
const issued = registryResolutionAuthorities.get(authority);
|
|
786
|
+
if (
|
|
787
|
+
!issued ||
|
|
788
|
+
issued.registryUrl !== String(registryUrl) ||
|
|
789
|
+
(issued.source ?? null) !== (source ?? null) ||
|
|
790
|
+
(issued.ref || null) !== (ref || null)
|
|
791
|
+
) {
|
|
792
|
+
throw new Error("registry source authority is missing or mismatched");
|
|
793
|
+
}
|
|
794
|
+
return issued;
|
|
498
795
|
}
|
|
499
796
|
|
|
500
797
|
/** List installable entries from a registry (for `cc plugin search`). */
|
|
501
798
|
export function listRegistryPlugins(registry) {
|
|
502
|
-
return (registry.plugins || []).map((p) =>
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
799
|
+
return (registry.plugins || []).map((p) => {
|
|
800
|
+
const source = normalizeMarketplacePackageSource(p);
|
|
801
|
+
return {
|
|
802
|
+
name: p.name,
|
|
803
|
+
version: p.version || null,
|
|
804
|
+
source:
|
|
805
|
+
source.type === "archive"
|
|
806
|
+
? source.url
|
|
807
|
+
: source.type === "git"
|
|
808
|
+
? source.source
|
|
809
|
+
: null,
|
|
810
|
+
sourceType: source.type,
|
|
811
|
+
ref: source.type === "git" ? source.ref : null,
|
|
812
|
+
...(source.type === "command" ? { mode: source.mode } : {}),
|
|
813
|
+
description: p.description || "",
|
|
814
|
+
enabled: source.type !== "dynamic-disabled",
|
|
815
|
+
...(source.type === "dynamic-disabled"
|
|
816
|
+
? { disabledReason: source.reason }
|
|
817
|
+
: {}),
|
|
818
|
+
};
|
|
819
|
+
});
|
|
509
820
|
}
|
|
510
821
|
|
|
511
822
|
/**
|
|
@@ -543,25 +854,239 @@ export function resolvePluginEntry(registry, name) {
|
|
|
543
854
|
* entry: object, fromCache: boolean }>}
|
|
544
855
|
*/
|
|
545
856
|
export async function resolveRemoteSource(url, opts = {}) {
|
|
857
|
+
assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
|
|
858
|
+
const managedPolicy = resolveManagedPolicy(opts);
|
|
546
859
|
const token = resolveRegistryToken(url, opts);
|
|
547
860
|
const { registry, fromCache, documentSha256, networkAuthority } =
|
|
548
861
|
await fetchRegistry(url, {
|
|
549
862
|
...opts,
|
|
550
863
|
token,
|
|
864
|
+
managedPolicy,
|
|
551
865
|
});
|
|
552
866
|
const entry = resolvePluginEntry(registry, opts.name);
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
867
|
+
const authorized = authorizeRegistryPluginEntry(registry, entry, {
|
|
868
|
+
registryUrl: url,
|
|
869
|
+
cwd: opts.cwd,
|
|
870
|
+
});
|
|
871
|
+
const resolvedSource =
|
|
872
|
+
opts.materialize === false
|
|
873
|
+
? authorized
|
|
874
|
+
: await resolveRegistryEntrySource(url, entry, {
|
|
875
|
+
...opts,
|
|
876
|
+
token,
|
|
877
|
+
registryResolutionAuthority: authorized.registryResolutionAuthority,
|
|
878
|
+
});
|
|
879
|
+
const result = {
|
|
880
|
+
...resolvedSource,
|
|
561
881
|
fromCache,
|
|
562
882
|
documentSha256,
|
|
563
883
|
...(networkAuthority ? { networkAuthority } : {}),
|
|
564
884
|
};
|
|
885
|
+
// Command descriptors can contain local paths or credentials. Retain the
|
|
886
|
+
// exact registry entry for the immediate authorized materialization flow,
|
|
887
|
+
// but keep it out of generic result serialization/audit output.
|
|
888
|
+
Object.defineProperty(result, "entry", {
|
|
889
|
+
value: entry,
|
|
890
|
+
enumerable: false,
|
|
891
|
+
});
|
|
892
|
+
return result;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/** Inspect one selected entry without fetching any candidate source bytes. */
|
|
896
|
+
export function inspectRegistryEntrySource(entry) {
|
|
897
|
+
const packageSource = assertMarketplaceSourceExecutable(entry);
|
|
898
|
+
if (packageSource.type === "archive") {
|
|
899
|
+
return {
|
|
900
|
+
source: null,
|
|
901
|
+
sourceIdentity: packageSource.url,
|
|
902
|
+
sourceType: "archive",
|
|
903
|
+
ref: null,
|
|
904
|
+
sha256: entry.sha256 || null,
|
|
905
|
+
archiveSpec: packageSource,
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
if (packageSource.type === "command") {
|
|
909
|
+
const inspected = {
|
|
910
|
+
source: null,
|
|
911
|
+
sourceIdentity: packageSource.identity,
|
|
912
|
+
sourceType: "command",
|
|
913
|
+
ref: null,
|
|
914
|
+
sha256: entry.sha256 || null,
|
|
915
|
+
};
|
|
916
|
+
// The descriptor may contain local credentials. It remains available to
|
|
917
|
+
// the immediate materializer but never spreads into a catalog/result.
|
|
918
|
+
Object.defineProperty(inspected, "commandSpec", {
|
|
919
|
+
value: packageSource,
|
|
920
|
+
enumerable: false,
|
|
921
|
+
});
|
|
922
|
+
return inspected;
|
|
923
|
+
}
|
|
924
|
+
// Preserve the canonical remote Git validation from the registry security
|
|
925
|
+
// boundary rather than accepting the adapter's more general local shape.
|
|
926
|
+
const normalized = normalizeRegistryPluginSource(entry);
|
|
927
|
+
return {
|
|
928
|
+
source: normalized.source,
|
|
929
|
+
sourceIdentity: entry.source,
|
|
930
|
+
sourceType: "git",
|
|
931
|
+
ref: normalized.ref,
|
|
932
|
+
sha256: entry.sha256 || null,
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/** Materialize one already-selected, preflight-approved registry entry. */
|
|
937
|
+
export async function resolveRegistryEntrySource(url, entry, opts = {}) {
|
|
938
|
+
const inspected = inspectRegistryEntrySource(entry);
|
|
939
|
+
const issued = assertRegistryResolutionAuthority(
|
|
940
|
+
opts.registryResolutionAuthority,
|
|
941
|
+
{
|
|
942
|
+
registryUrl: url,
|
|
943
|
+
source: inspected.source,
|
|
944
|
+
ref: inspected.ref,
|
|
945
|
+
},
|
|
946
|
+
);
|
|
947
|
+
if (
|
|
948
|
+
issued.sourceType !== inspected.sourceType ||
|
|
949
|
+
issued.declaredSource !== inspected.sourceIdentity ||
|
|
950
|
+
issued.archiveSha256 !==
|
|
951
|
+
(inspected.sourceType === "archive" ? inspected.archiveSpec.sha256 : null)
|
|
952
|
+
) {
|
|
953
|
+
throw new Error("registry source authority is missing or mismatched");
|
|
954
|
+
}
|
|
955
|
+
if (inspected.sourceType === "archive") {
|
|
956
|
+
const archive = await fetchAndMaterializeMarketplaceArchive({
|
|
957
|
+
registryUrl: url,
|
|
958
|
+
url: inspected.archiveSpec.requestUrl || inspected.archiveSpec.url,
|
|
959
|
+
sha256: inspected.archiveSpec.sha256,
|
|
960
|
+
token: opts.token,
|
|
961
|
+
allowInsecure: opts.allowInsecure === true,
|
|
962
|
+
offline: opts.offline === true,
|
|
963
|
+
artifactCacheDir: opts.artifactCacheDir,
|
|
964
|
+
sourceCacheDir: opts.archiveSourceCacheDir,
|
|
965
|
+
timeoutMs: opts.timeoutMs,
|
|
966
|
+
fetchImpl: opts.archiveFetchImpl,
|
|
967
|
+
resolveHostname: opts.resolveHostname,
|
|
968
|
+
proxyUrl: opts.proxyUrl,
|
|
969
|
+
pacFile: opts.pacFile,
|
|
970
|
+
caFile: opts.caFile,
|
|
971
|
+
requestHeaders: issued.marketplaceRequestHeaders,
|
|
972
|
+
});
|
|
973
|
+
const materializedAuthority = Object.freeze({});
|
|
974
|
+
registryResolutionAuthorities.set(
|
|
975
|
+
materializedAuthority,
|
|
976
|
+
Object.freeze({
|
|
977
|
+
...issued,
|
|
978
|
+
source: archive.dir,
|
|
979
|
+
payloadSha256: archive.authority.payloadSha256,
|
|
980
|
+
}),
|
|
981
|
+
);
|
|
982
|
+
return {
|
|
983
|
+
...inspected,
|
|
984
|
+
source: archive.dir,
|
|
985
|
+
archiveAuthority: archive.authority,
|
|
986
|
+
registryResolutionAuthority: materializedAuthority,
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
if (inspected.sourceType === "command") {
|
|
990
|
+
const command = await runMarketplaceCommandSource(
|
|
991
|
+
{
|
|
992
|
+
executable: inspected.commandSpec.executable,
|
|
993
|
+
args: inspected.commandSpec.args,
|
|
994
|
+
cwd: inspected.commandSpec.cwd,
|
|
995
|
+
env: inspected.commandSpec.env,
|
|
996
|
+
timeoutMs: inspected.commandSpec.timeoutMs,
|
|
997
|
+
maxOutputBytes: inspected.commandSpec.maxOutputBytes,
|
|
998
|
+
mode: inspected.commandSpec.mode,
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
platform: opts.platform,
|
|
1002
|
+
spawn: opts.commandSpawn,
|
|
1003
|
+
spawnSync: opts.commandSpawnSync,
|
|
1004
|
+
kill: opts.commandKill,
|
|
1005
|
+
},
|
|
1006
|
+
);
|
|
1007
|
+
const materializedAuthority = Object.freeze({});
|
|
1008
|
+
registryResolutionAuthorities.set(
|
|
1009
|
+
materializedAuthority,
|
|
1010
|
+
Object.freeze({
|
|
1011
|
+
...issued,
|
|
1012
|
+
source: command.source,
|
|
1013
|
+
payloadSha256: command.payloadSha256,
|
|
1014
|
+
commandMode: command.mode,
|
|
1015
|
+
fileCount: command.fileCount,
|
|
1016
|
+
totalBytes: command.totalBytes,
|
|
1017
|
+
}),
|
|
1018
|
+
);
|
|
1019
|
+
return {
|
|
1020
|
+
...inspected,
|
|
1021
|
+
source: command.source,
|
|
1022
|
+
commandAuthority: Object.freeze({
|
|
1023
|
+
schemaVersion: "cc-plugin-marketplace-command-source/v1",
|
|
1024
|
+
status: "descriptor-executed-and-verified",
|
|
1025
|
+
descriptorIdentity: inspected.sourceIdentity,
|
|
1026
|
+
mode: command.mode,
|
|
1027
|
+
payloadSha256: command.payloadSha256,
|
|
1028
|
+
fileCount: command.fileCount,
|
|
1029
|
+
totalBytes: command.totalBytes,
|
|
1030
|
+
}),
|
|
1031
|
+
registryResolutionAuthority: materializedAuthority,
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
return {
|
|
1035
|
+
...inspected,
|
|
1036
|
+
registryResolutionAuthority: opts.registryResolutionAuthority,
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
function resolveManagedPolicy(opts) {
|
|
1041
|
+
return resolvePluginManagedPolicy(opts);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
function responseUrlChanged(requested, effective) {
|
|
1045
|
+
if (!effective) return false;
|
|
1046
|
+
try {
|
|
1047
|
+
const left = new URL(String(requested));
|
|
1048
|
+
const right = new URL(String(effective));
|
|
1049
|
+
for (const value of [left, right]) {
|
|
1050
|
+
value.username = "";
|
|
1051
|
+
value.password = "";
|
|
1052
|
+
value.hash = "";
|
|
1053
|
+
}
|
|
1054
|
+
return left.href !== right.href;
|
|
1055
|
+
} catch {
|
|
1056
|
+
return true;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
function publicRegistryErrorMessage(error) {
|
|
1061
|
+
if (!error) return "unknown error";
|
|
1062
|
+
if (typeof error.publicMessage === "string") return error.publicMessage;
|
|
1063
|
+
if (error.name === "AbortError") return "registry request timed out";
|
|
1064
|
+
if (error instanceof TypeError) return "registry transport failed";
|
|
1065
|
+
return "registry request failed";
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
function publicRegistryCacheErrorMessage(error) {
|
|
1069
|
+
const message = String(error?.message || "");
|
|
1070
|
+
if (message.includes("digest mismatch")) {
|
|
1071
|
+
return "registry immutable cache digest mismatch";
|
|
1072
|
+
}
|
|
1073
|
+
if (message.includes("ambiguous")) {
|
|
1074
|
+
return "registry immutable cache is ambiguous";
|
|
1075
|
+
}
|
|
1076
|
+
if (message.includes("more than")) {
|
|
1077
|
+
return "registry immutable cache has too many candidates";
|
|
1078
|
+
}
|
|
1079
|
+
if (message.includes("single-link regular file")) {
|
|
1080
|
+
return "registry immutable cache entry is invalid";
|
|
1081
|
+
}
|
|
1082
|
+
return "registry immutable cache document is invalid";
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function publicRegistryFailure(message, { cacheFallbackAllowed = false } = {}) {
|
|
1086
|
+
const error = new Error(message);
|
|
1087
|
+
error.publicMessage = message;
|
|
1088
|
+
error.cacheFallbackAllowed = cacheFallbackAllowed;
|
|
1089
|
+
return error;
|
|
565
1090
|
}
|
|
566
1091
|
|
|
567
1092
|
/** Best-effort local temp dir helper (kept here so tests can stub fs deps). */
|