chainlesschain 0.165.4 → 0.165.6
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 +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-C__nnraW.js → AIOps-vfoDb6HZ.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CsKWHEZ-.js → ActionButton-Cm5f7p93.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BhIjfjWO.js → Analytics-yRuvgWxa.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BjuXFzWZ.js → AppLayout-BROsC4h1.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-k6whDkZJ.js → Artifacts-CimHApb1.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-C4FBMYRh.js → Audit-DI8kr8zI.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-C_vRrSXk.js → BackgroundAgents-DSLzKy-n.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CngFZeVp.js → Backup-DkH_n2r1.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-B_H9EBRm.js → BaseInput-CKSrUoRA.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DLEC5kt7.js → Chat-ClwfMXUS.js} +3 -3
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CjMaqfL7.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-m3ClR4xh.js → Checkbox-BYTehcQG.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CmLSFrgp.js → Codegen-B8AMcp6J.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DFGQteHN.js → Col-BNSogPo3.js} +1 -1
- package/src/assets/web-panel/assets/{Community-EUUVORHE.js → Community-JxPvGb8O.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-2PSg0Yr7.js → Compact-DF7bFAgE.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CcKur7gB.js → Compliance-Bvhqnwrq.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DK_Fezfa.js → Cowork-BDS0CJvM.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Dbw89xl7.js → Cron-DqvRfxoQ.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CH66h_Ec.js → Crosschain-CDcIvnqS.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D1igRb17.js → DID-D7jaUxU3.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DVAsrobk.js → Dashboard-B5A18wEV.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Dl7W8AQs.js → Dropdown-_6IVkGag.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D5tpaEQC.js → EmailListRenderer-C9PeiKCG.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-O_VQf8gq.js → FamilyGuardDashboard-DXjuEHrX.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BTVcgS9n.js → Federation-B4IAR9zv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BRnkJAIz.js → FormItemContext-CxQeSNx1.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-8OWSCkDj.js → GenericCardRenderer-B8GS-Y3N.js} +1 -1
- package/src/assets/web-panel/assets/{Git-WXLYTQj2.js → Git-BS8SouUX.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BezaUhtc.js → Governance-Dq9aYb5Q.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CuRay3Gu.js → Inference-BrCEe9AJ.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DfWLDW1m.js → KnowledgeGraph-DybOpiY2.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-C55y2pPY.js → Logs-DBpzbRfx.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Dk08Uycg.js → Marketplace-Cc7CCSCu.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C11fSB5V.js → McpTools-DFaIhOfu.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-mr0eZc97.js → Memory-KCqlp9ol.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C0iCSRbb.js → MobileBridge-Bys84ySN.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CY-xMG8Z.js → MobileProjects-B-6qU-je.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Bplqc_Wc.js → Mtc-BgY-fvCq.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-BlzwsoSM.js → MtcAudit-B8pCmqQJ.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BBLmTty6.js → Multisig-BbOmYutA.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DtT1uf1W.js → NLProgramming-BocLQz_M.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DcqQAxTK.js → Notes-CSwbxEop.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-cb0kWoZe.js → NotificationSettings-CqFNSYG3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-kw9HZQNN.js → OrderTableRenderer-C6gMfAk7.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DlTlbkUN.js → Organization-QrDXVdMJ.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-pp1qs-oY.js → Overflow-Cxntotjt.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BESLnyYz.js → P2P-CIdBOwNq.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-B-J1mGuf.js → PdhVaultBrowser-CT5aP8rD.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-B36PeT1_.js → Permissions-DH7OM8XU.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CTK2ad-3.js → PersonalDataHub-CnJqbWce.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-zJNGXsac.js → Pipeline-5C3HG1kH.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-b9fz0Qf0.js → Privacy-B4yIp7xK.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DjZbZgiB.js → ProjectInit-BMvt-2M0.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C-hHJXlD.js → ProjectSettings-CvSTEMRG.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BKBJxEek.js +1 -0
- package/src/assets/web-panel/assets/Providers-BdwUqFWN.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-A0yoQ3-3.js → QrScannerModal-DtnCKlTm.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DrFgFJNC.js → QuickAsk-sTbgwyNr.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-KC8hF1g4.js → Recommend-Bzr14gSb.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DGTPIk6u.js → RemoteSession-C_TF5IaJ.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-QmbwM7Yk.js → Reputation-GCAM4H2K.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BxjJylia.js → Row-Dir0r6OQ.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B7Wda1J6.js → RssFeed-CpGnlecX.js} +2 -2
- package/src/assets/web-panel/assets/{Search-_plQ_YSA.js → Search-DMMamC6u.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CD2YJZfx.js → Security-yqay5yG-.js} +4 -4
- package/src/assets/web-panel/assets/{Services-qQE7NmwE.js → Services-C5i2wh9G.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DgQqkFf7.js → Skeleton-vUqk35yj.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-C4C0oFFt.js → Skills-DzcSkhot.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Px2N2B5c.js → Sla-CB6qbYNr.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DN6C3lQd.js → SpeechSettings-BHUI7cox.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-jm5aH_m7.js → SyncSettings-D-RrsxTJ.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DlkJcv67.js → Tasks-DFxdOJiq.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DyrTHUBs.js → Templates-SKAob_Aq.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-mC434PtL.js → Tenant-BPfsfHQ-.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-o0_s3NgK.js → Terminal-8iw7FpUf.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DAGlAXOw.js → TimelineRenderer-C4Gjj5Su.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BnulS_bs.js → Tokens-COYhN18e.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-suQbVXYM.js → Trigger-B-JzXcf6.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DbIJAPo5.js → Trust-BJ5bg0nI.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CQNpbIHi.js → UkeySign-BG_ByRMf.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D8aWCERk.js → VideoEditing-B3kOnSiU.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CuLpYfZc.js → Wallet-CcoGFnK7.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CsfG8ZMc.js → WebAuthn-DdT8vRC_.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BYsEAZbd.js → WorkflowEditor-CZu3M9N8.js} +1 -1
- package/src/assets/web-panel/assets/{chat-FT4Um3-I.js → chat-DxJt3Qba.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CKbS_Wlt.js → colors-DdYfwJaB.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CFJtzuJM.js → compact-item-BG3SUzwj.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-B9kSRW6K.js → createContext-By51z2-1.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DR-x0Xpi.js → hasIn-B0apZ3KJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjEP7WkH.js → index-3jWkIu5j.js} +1 -1
- package/src/assets/web-panel/assets/{index-7jHr_A_k.js → index-B3rWuPMW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcfxhKXR.js → index-B5rLiEo0.js} +1 -1
- package/src/assets/web-panel/assets/{index-tqSEHPRT.js → index-BBPf3TpM.js} +1 -1
- package/src/assets/web-panel/assets/index-BETiL0a3.js +1 -0
- package/src/assets/web-panel/assets/{index-Cr2FLUsD.js → index-BIrJfqO4.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bt4fS2eL.js → index-BLIbGOLX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwR7yc2h.js → index-BP94CrBV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVPU43b1.js → index-BWF8_mkx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DShSkww-.js → index-BYINoRLK.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bzz0C1BZ.js → index-BZ_0qm4g.js} +1 -1
- package/src/assets/web-panel/assets/{index-KqDaCApw.js → index-BewKYrX9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BW-t9n-q.js → index-BfWYsB9t.js} +1 -1
- package/src/assets/web-panel/assets/index-BhAVHuW8.js +1 -0
- package/src/assets/web-panel/assets/{index-B7osrXqy.js → index-Bissefox.js} +3 -3
- package/src/assets/web-panel/assets/{index-DW4BWjTk.js → index-BkvcEo4p.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdkIhfi-.js → index-Bos5_aMV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTxNy0ZU.js → index-C1C9i79A.js} +1 -1
- package/src/assets/web-panel/assets/{index-QBSeNVO3.js → index-CAhHirVH.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4LfPvWA.js → index-CNIxXMmN.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_enk9I_.js → index-CW93tZDv.js} +1 -1
- package/src/assets/web-panel/assets/{index-WE08gMJP.js → index-CWEuZWCK.js} +1 -1
- package/src/assets/web-panel/assets/{index-r2VI1Ikk.js → index-Cp6e-hGw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dap9c3eD.js → index-CpjZhgNd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWdqx7zI.js → index-CzmQElZy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGbPnC5x.js → index-DCn8IcxV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DiNUEpSQ.js → index-DE76D-kA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtkFqPVA.js → index-DaIz0NAW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGaXUsIs.js → index-Da_WBdLX.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3ZX81mK.js → index-Dz49VOyQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BK3TiKVq.js → index-Q2SCX-MI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJmIQ9XR.js → index-RD6O-0C6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvrCCqJt.js → index-SQqOlR3d.js} +1 -1
- package/src/assets/web-panel/assets/{index-BI5hP_qy.js → index-ZFbps9po.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeSgw-WV.js → index-ZxtP8jG7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMGmg497.js → index-bP_VlDb4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfFCQHL3.js → index-kf57LJ93.js} +1 -1
- package/src/assets/web-panel/assets/{index-OSzjTzGM.js → index-oxRrmFO9.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2JvmYRc.js → index-p1_8SbI1.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Dd7n7VgI.js → initDefaultProps-BWJktcg8.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BwxouRw1.js → motion-BEXBzUFV.js} +1 -1
- package/src/assets/web-panel/assets/{move-DObN0PG9.js → move-BUKwn2sx.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-B3nOUQc7.js → mtc-parser-FTI7btqQ.js} +1 -1
- package/src/assets/web-panel/assets/{omit-o9WU2Nbl.js → omit-CsLtSPH3.js} +1 -1
- package/src/assets/web-panel/assets/parsers-YNLm7sQ0.js +4 -0
- package/src/assets/web-panel/assets/{pickAttrs-CL_vvfQ_.js → pickAttrs-DWZxsT_S.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CYrM2tyK.js → placementArrow-Bcfs43CX.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-k_06cQ96.js → responsiveObserve-a9bcC1p-.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CPKG0pxS.js → slide-BDTzgOXN.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BDDEeLTw.js → statusUtils-Iq1j_HUg.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cmb7RTh7.js → styleChecker-DAZEYxeh.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-e2IedC4N.js → useFlexGapSupport-DVkjyDrh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CE_gxVnm.js → useFs-wXTpNTHt.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BkMIIKMz.js → usePersonalDataHub-CtQM_aZr.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DW886XV1.js → vnode-hjEoG85v.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Dwo_adaf.js → zoom-BHVpyKg_.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 +14 -2
- package/src/commands/artifacts.js +235 -0
- package/src/commands/background-session.js +100 -10
- package/src/commands/cowork.js +86 -57
- package/src/commands/mcp.js +5 -0
- package/src/commands/plugin.js +145 -29
- package/src/commands/remote-control.js +295 -110
- package/src/commands/session-location.js +20 -1
- package/src/commands/session-message.js +171 -0
- package/src/commands/session.js +75 -69
- package/src/data/changelog.json +29 -3
- package/src/gateways/ws/message-dispatcher.js +57 -0
- package/src/harness/mcp-client.js +141 -10
- package/src/lib/artifact-access-ledger.js +1 -0
- package/src/lib/artifact-deletion-ledger.js +1 -0
- package/src/lib/artifact-recovery.js +970 -0
- package/src/lib/artifact-workbench-projection.js +171 -0
- package/src/lib/background-session-transport.js +153 -27
- package/src/lib/dynamic-workflow-runtime.js +408 -1
- package/src/lib/execution-location-target.js +4 -2
- package/src/lib/mcp-client.js +2 -0
- package/src/lib/mcp-headers-helper.js +47 -5
- package/src/lib/mcp-oauth.js +55 -6
- package/src/lib/mcp-stdio-executable-identity.js +16 -0
- package/src/lib/mcp-stdio-native-code-policy.js +74 -0
- package/src/lib/mcp-stdio-package-materialization.js +12 -3
- package/src/lib/mcp-tls.js +230 -0
- package/src/lib/output-styles.js +12 -0
- package/src/lib/plugin-runtime/install.js +614 -71
- package/src/lib/plugin-runtime/marketplace-archive-source.js +331 -0
- package/src/lib/plugin-runtime/marketplace-catalog.js +52 -6
- package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +163 -0
- package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
- package/src/lib/plugin-runtime/remote-source.js +435 -55
- package/src/lib/plugin-security.js +393 -41
- package/src/lib/plugin-source-identity.js +850 -0
- package/src/lib/process-execution-broker/index.js +112 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +57 -3
- package/src/lib/process-execution-broker/workspace-transaction.js +26 -0
- package/src/lib/remote-approval-bridge.js +136 -46
- package/src/lib/remote-control.js +217 -31
- package/src/lib/session-message-fabric.js +1105 -0
- package/src/lib/session-projection.js +171 -12
- package/src/lib/session-runtime-retention.js +155 -0
- package/src/lib/settings-loader.cjs +29 -0
- package/src/repl/agent-repl.js +51 -14
- package/src/repl/permission-prompt.js +31 -8
- package/src/repl/repl-keybindings.js +55 -0
- package/src/runtime/agent-core.js +63 -2
- package/src/runtime/headless-runner.js +47 -66
- package/src/runtime/headless-stream.js +18 -24
- package/src/runtime/mcp-config.js +2 -0
- package/src/runtime/policies/agent-policy.js +2 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +0 -1
- package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +0 -1
- package/src/assets/web-panel/assets/Providers-Dz-CAYYU.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +0 -1
- package/src/assets/web-panel/assets/index-Bizb4hkw.js +0 -1
- package/src/assets/web-panel/assets/index-OHSl-xCh.js +0 -1
- package/src/assets/web-panel/assets/parsers-UEvh_ShA.js +0 -4
|
@@ -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,21 @@ 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
|
+
assertMarketplaceSourceExecutable,
|
|
45
|
+
normalizeMarketplacePackageSource,
|
|
46
|
+
} from "./marketplace-source-adapter.js";
|
|
32
47
|
|
|
33
48
|
export const _deps = {
|
|
34
49
|
// Node 22+ global fetch; injectable for tests.
|
|
@@ -52,6 +67,11 @@ const DEFAULT_TIMEOUT_MS = 15_000;
|
|
|
52
67
|
export const MAX_REGISTRY_DOCUMENT_BYTES = 4 * 1024 * 1024;
|
|
53
68
|
export const MAX_REGISTRY_CACHE_CANDIDATES = 64;
|
|
54
69
|
const SHA256_RE = /^[a-f0-9]{64}$/;
|
|
70
|
+
const CONTROL_RE = /[\u0000-\u001f\u007f]/u;
|
|
71
|
+
const MAX_REGISTRY_SOURCE_CHARS = 4096;
|
|
72
|
+
const MAX_REGISTRY_REF_CHARS = 256;
|
|
73
|
+
const fetchedRegistryAuthorities = new WeakMap();
|
|
74
|
+
const registryResolutionAuthorities = new WeakMap();
|
|
55
75
|
|
|
56
76
|
/** True when `raw` looks like a remote registry/manifest URL (http(s) + .json). */
|
|
57
77
|
export function isRemoteSource(raw) {
|
|
@@ -67,15 +87,19 @@ export function isRemoteSource(raw) {
|
|
|
67
87
|
* explicit opt-in (opts.allowInsecure / CC_PLUGIN_REGISTRY_ALLOW_HTTP=1).
|
|
68
88
|
*/
|
|
69
89
|
export function assertRegistryUrlSafe(url, { allowInsecure = false } = {}) {
|
|
90
|
+
const displayUrl = redactPluginSourceForDisplay(url);
|
|
70
91
|
let u;
|
|
71
92
|
try {
|
|
72
93
|
u = new URL(String(url));
|
|
73
94
|
} catch {
|
|
74
|
-
throw new Error(`invalid registry URL: ${
|
|
95
|
+
throw new Error(`invalid registry URL: ${displayUrl}`);
|
|
96
|
+
}
|
|
97
|
+
if (u.username || u.password) {
|
|
98
|
+
throw new Error("registry URL credentials are not supported");
|
|
75
99
|
}
|
|
76
100
|
if (u.protocol === "https:") return;
|
|
77
101
|
if (u.protocol !== "http:") {
|
|
78
|
-
throw new Error(`registry URL must be http(s): ${
|
|
102
|
+
throw new Error(`registry URL must be http(s): ${displayUrl}`);
|
|
79
103
|
}
|
|
80
104
|
// WHATWG URL keeps the brackets on IPv6 hostnames — strip before comparing.
|
|
81
105
|
const host = u.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
@@ -85,7 +109,7 @@ export function assertRegistryUrlSafe(url, { allowInsecure = false } = {}) {
|
|
|
85
109
|
allowInsecure === true || process.env.CC_PLUGIN_REGISTRY_ALLOW_HTTP === "1";
|
|
86
110
|
if (loopback || optIn) return;
|
|
87
111
|
throw new Error(
|
|
88
|
-
`plain-HTTP registry rejected: ${
|
|
112
|
+
`plain-HTTP registry rejected: ${displayUrl} — a network MITM controls both the ` +
|
|
89
113
|
`source and its sha256, so the integrity check verifies nothing. Use ` +
|
|
90
114
|
`https, or opt in with --allow-insecure-registry / ` +
|
|
91
115
|
`CC_PLUGIN_REGISTRY_ALLOW_HTTP=1 on a trusted network.`,
|
|
@@ -143,6 +167,16 @@ export function resolveRegistryToken(url, { token, config } = {}) {
|
|
|
143
167
|
* documentSha256: string }>}
|
|
144
168
|
*/
|
|
145
169
|
export async function fetchRegistry(url, opts = {}) {
|
|
170
|
+
const displayUrl = redactPluginSourceForDisplay(url);
|
|
171
|
+
assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
|
|
172
|
+
const managedPolicy = resolveManagedPolicy(opts);
|
|
173
|
+
// This gate intentionally runs before transport construction. A blocked or
|
|
174
|
+
// non-allowlisted marketplace must not reach proxy helpers, DNS, or fetch.
|
|
175
|
+
enforcePluginSourcePolicy(url, managedPolicy, {
|
|
176
|
+
action: opts.policyAction || "registry-fetch",
|
|
177
|
+
cwd: opts.cwd,
|
|
178
|
+
kindHint: "registry",
|
|
179
|
+
});
|
|
146
180
|
const {
|
|
147
181
|
token,
|
|
148
182
|
cacheDir,
|
|
@@ -150,7 +184,6 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
150
184
|
allowCache = true,
|
|
151
185
|
offline = false,
|
|
152
186
|
} = opts;
|
|
153
|
-
assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
|
|
154
187
|
const expectedSha256 = normalizeRegistrySha256(
|
|
155
188
|
opts.expectedSha256,
|
|
156
189
|
"expected registry document SHA-256",
|
|
@@ -163,14 +196,21 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
163
196
|
if (!allowCache) {
|
|
164
197
|
throw new Error("offline registry mode requires immutable cache access");
|
|
165
198
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
199
|
+
let cached;
|
|
200
|
+
try {
|
|
201
|
+
cached = readCachedRegistry({
|
|
202
|
+
url,
|
|
203
|
+
cacheDir,
|
|
204
|
+
expectedSha256,
|
|
205
|
+
});
|
|
206
|
+
} catch (error) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`registry ${displayUrl} verified immutable cache rejected: ${publicRegistryCacheErrorMessage(error)}`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
if (cached) return bindFetchedRegistry(cached, url, managedPolicy);
|
|
172
212
|
throw new Error(
|
|
173
|
-
`registry ${
|
|
213
|
+
`registry ${displayUrl} is unavailable in the verified immutable cache` +
|
|
174
214
|
(expectedSha256 ? ` at SHA-256 ${expectedSha256}` : ""),
|
|
175
215
|
);
|
|
176
216
|
}
|
|
@@ -190,37 +230,60 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
190
230
|
try {
|
|
191
231
|
res = await fetchImpl(url, {
|
|
192
232
|
headers,
|
|
193
|
-
redirect: "
|
|
233
|
+
redirect: "manual",
|
|
194
234
|
signal: controller.signal,
|
|
195
235
|
});
|
|
196
236
|
} finally {
|
|
197
237
|
clearTimeout(timer);
|
|
198
238
|
}
|
|
239
|
+
if (
|
|
240
|
+
res?.redirected === true ||
|
|
241
|
+
(Number(res?.status) >= 300 && Number(res?.status) < 400) ||
|
|
242
|
+
responseUrlChanged(url, res?.url)
|
|
243
|
+
) {
|
|
244
|
+
throw publicRegistryFailure(
|
|
245
|
+
"registry redirects are disabled because each hop cannot be policy-validated",
|
|
246
|
+
);
|
|
247
|
+
}
|
|
199
248
|
if (!res.ok) {
|
|
200
|
-
const
|
|
201
|
-
|
|
249
|
+
const status = Number(res.status);
|
|
250
|
+
throw publicRegistryFailure(
|
|
251
|
+
`registry fetch failed: HTTP ${Number.isInteger(status) ? status : "unknown"}`,
|
|
252
|
+
{ cacheFallbackAllowed: status >= 500 },
|
|
202
253
|
);
|
|
203
|
-
error.cacheFallbackAllowed = Number(res.status) >= 500;
|
|
204
|
-
throw error;
|
|
205
254
|
}
|
|
206
|
-
|
|
255
|
+
let bytes;
|
|
256
|
+
try {
|
|
257
|
+
bytes = await readBoundedRegistryResponse(res);
|
|
258
|
+
} catch {
|
|
259
|
+
throw publicRegistryFailure("registry response body is invalid");
|
|
260
|
+
}
|
|
207
261
|
const documentSha256 = sha256(bytes);
|
|
208
262
|
if (expectedSha256 && documentSha256 !== expectedSha256) {
|
|
209
|
-
throw
|
|
263
|
+
throw publicRegistryFailure(
|
|
210
264
|
`registry document SHA-256 mismatch: expected ${expectedSha256}, got ${documentSha256}`,
|
|
211
265
|
);
|
|
212
266
|
}
|
|
213
|
-
|
|
214
|
-
|
|
267
|
+
let registry;
|
|
268
|
+
try {
|
|
269
|
+
const text = decodeRegistryUtf8(bytes);
|
|
270
|
+
registry = validateRegistry(JSON.parse(text), url);
|
|
271
|
+
} catch {
|
|
272
|
+
throw publicRegistryFailure("registry document is invalid");
|
|
273
|
+
}
|
|
215
274
|
if (allowCache) writeImmutableCache(url, cacheDir, bytes, documentSha256);
|
|
216
|
-
return
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
275
|
+
return bindFetchedRegistry(
|
|
276
|
+
{
|
|
277
|
+
registry,
|
|
278
|
+
fromCache: false,
|
|
279
|
+
documentSha256,
|
|
280
|
+
...(networkTransport?.authority
|
|
281
|
+
? { networkAuthority: networkTransport.authority }
|
|
282
|
+
: {}),
|
|
283
|
+
},
|
|
284
|
+
url,
|
|
285
|
+
managedPolicy,
|
|
286
|
+
);
|
|
224
287
|
} catch (err) {
|
|
225
288
|
networkErr = err;
|
|
226
289
|
cacheFallbackAllowed =
|
|
@@ -228,7 +291,12 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
228
291
|
err?.name === "AbortError" ||
|
|
229
292
|
err instanceof TypeError;
|
|
230
293
|
} finally {
|
|
231
|
-
|
|
294
|
+
try {
|
|
295
|
+
await networkTransport?.close();
|
|
296
|
+
} catch {
|
|
297
|
+
// Transport teardown is best-effort and must not override a verified
|
|
298
|
+
// response or surface an implementation error that echoed request URLs.
|
|
299
|
+
}
|
|
232
300
|
}
|
|
233
301
|
|
|
234
302
|
// Only transport failures and server-side outages may fall back. Authentication,
|
|
@@ -240,15 +308,15 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
240
308
|
cacheDir,
|
|
241
309
|
expectedSha256,
|
|
242
310
|
});
|
|
243
|
-
if (cached) return cached;
|
|
311
|
+
if (cached) return bindFetchedRegistry(cached, url, managedPolicy);
|
|
244
312
|
} catch (cacheError) {
|
|
245
313
|
throw new Error(
|
|
246
|
-
`could not fetch registry ${
|
|
314
|
+
`could not fetch registry ${displayUrl}: ${publicRegistryErrorMessage(networkErr)}; verified immutable cache rejected: ${publicRegistryCacheErrorMessage(cacheError)}`,
|
|
247
315
|
);
|
|
248
316
|
}
|
|
249
317
|
}
|
|
250
318
|
throw new Error(
|
|
251
|
-
`could not fetch registry ${
|
|
319
|
+
`could not fetch registry ${displayUrl}: ${publicRegistryErrorMessage(networkErr)}` +
|
|
252
320
|
(allowCache && cacheFallbackAllowed
|
|
253
321
|
? " (no verified immutable cache available)"
|
|
254
322
|
: ""),
|
|
@@ -475,37 +543,204 @@ function sha256(value) {
|
|
|
475
543
|
|
|
476
544
|
/** Validate + normalize a fetched document into a registry object. Throws on garbage. */
|
|
477
545
|
export function validateRegistry(doc, url = "") {
|
|
546
|
+
const displayUrl = redactPluginSourceForDisplay(url);
|
|
478
547
|
if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
|
|
479
|
-
throw new Error(`registry at ${
|
|
548
|
+
throw new Error(`registry at ${displayUrl} is not a JSON object`);
|
|
480
549
|
}
|
|
481
550
|
if (Array.isArray(doc.plugins)) {
|
|
482
551
|
for (const p of doc.plugins) {
|
|
483
|
-
if (
|
|
552
|
+
if (
|
|
553
|
+
!p ||
|
|
554
|
+
typeof p !== "object" ||
|
|
555
|
+
typeof p.name !== "string" ||
|
|
556
|
+
!p.name.trim() ||
|
|
557
|
+
(p.source == null && p.command == null && p.headersHelper == null)
|
|
558
|
+
) {
|
|
484
559
|
throw new Error(
|
|
485
|
-
`registry at ${
|
|
560
|
+
`registry at ${displayUrl} has a plugin entry missing name/source`,
|
|
486
561
|
);
|
|
487
562
|
}
|
|
563
|
+
const normalized = normalizeMarketplacePackageSource(p);
|
|
564
|
+
if (normalized.type === "git") normalizeRegistryPluginSource(p);
|
|
488
565
|
}
|
|
489
566
|
return doc;
|
|
490
567
|
}
|
|
491
568
|
// Single-plugin manifest.
|
|
492
|
-
if (doc.source) {
|
|
569
|
+
if (doc.source != null || doc.command != null || doc.headersHelper != null) {
|
|
570
|
+
const normalized = normalizeMarketplacePackageSource(doc);
|
|
571
|
+
if (normalized.type === "git") normalizeRegistryPluginSource(doc);
|
|
493
572
|
return { plugins: [doc] };
|
|
494
573
|
}
|
|
495
574
|
throw new Error(
|
|
496
|
-
`registry at ${
|
|
575
|
+
`registry at ${displayUrl} must have a "plugins" array or a top-level "source"`,
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function freezeRegistryValue(value) {
|
|
580
|
+
if (!value || typeof value !== "object" || Object.isFrozen(value)) {
|
|
581
|
+
return value;
|
|
582
|
+
}
|
|
583
|
+
for (const child of Object.values(value)) freezeRegistryValue(child);
|
|
584
|
+
return Object.freeze(value);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function bindFetchedRegistry(result, registryUrl, managedPolicy) {
|
|
588
|
+
const registry = freezeRegistryValue(result.registry);
|
|
589
|
+
fetchedRegistryAuthorities.set(
|
|
590
|
+
registry,
|
|
591
|
+
Object.freeze({
|
|
592
|
+
registryUrl: String(registryUrl),
|
|
593
|
+
documentSha256: result.documentSha256 || null,
|
|
594
|
+
managedPolicy,
|
|
595
|
+
}),
|
|
497
596
|
);
|
|
597
|
+
return { ...result, registry };
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/** Validate one catalog entry as a bounded, remote Git source. */
|
|
601
|
+
export function normalizeRegistryPluginSource(entry) {
|
|
602
|
+
if (!entry || typeof entry !== "object") {
|
|
603
|
+
throw new Error("registry plugin entry is invalid");
|
|
604
|
+
}
|
|
605
|
+
if (
|
|
606
|
+
typeof entry.source !== "string" ||
|
|
607
|
+
entry.source !== entry.source.trim() ||
|
|
608
|
+
!entry.source ||
|
|
609
|
+
entry.source.length > MAX_REGISTRY_SOURCE_CHARS ||
|
|
610
|
+
CONTROL_RE.test(entry.source) ||
|
|
611
|
+
entry.source.includes("#")
|
|
612
|
+
) {
|
|
613
|
+
throw new Error("registry plugin source must be a bounded string");
|
|
614
|
+
}
|
|
615
|
+
let ref = null;
|
|
616
|
+
if (entry.ref != null && entry.ref !== "") {
|
|
617
|
+
if (
|
|
618
|
+
typeof entry.ref !== "string" ||
|
|
619
|
+
entry.ref !== entry.ref.trim() ||
|
|
620
|
+
!entry.ref ||
|
|
621
|
+
entry.ref.length > MAX_REGISTRY_REF_CHARS ||
|
|
622
|
+
CONTROL_RE.test(entry.ref) ||
|
|
623
|
+
entry.ref.includes("#") ||
|
|
624
|
+
entry.source.includes("#")
|
|
625
|
+
) {
|
|
626
|
+
throw new Error("registry plugin ref must be a bounded exact string");
|
|
627
|
+
}
|
|
628
|
+
ref = entry.ref;
|
|
629
|
+
}
|
|
630
|
+
const source = ref ? `${entry.source}#${ref}` : entry.source;
|
|
631
|
+
let git;
|
|
632
|
+
try {
|
|
633
|
+
git = parsePluginGitSource(source);
|
|
634
|
+
assertPluginGitTransportSafe(git, {
|
|
635
|
+
allowFile: false,
|
|
636
|
+
requireRemote: true,
|
|
637
|
+
});
|
|
638
|
+
} catch {
|
|
639
|
+
throw new Error("registry plugin source must be a safe remote Git locator");
|
|
640
|
+
}
|
|
641
|
+
return { source, ref, git };
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Issue an unforgeable in-process capability for a selected entry. The entry
|
|
646
|
+
* must be an exact member of a registry object returned by fetchRegistry;
|
|
647
|
+
* callers cannot manufacture registry metadata to downgrade source policy.
|
|
648
|
+
*/
|
|
649
|
+
export function authorizeRegistryPluginEntry(
|
|
650
|
+
registry,
|
|
651
|
+
entry,
|
|
652
|
+
{ registryUrl, cwd } = {},
|
|
653
|
+
) {
|
|
654
|
+
const fetched = fetchedRegistryAuthorities.get(registry);
|
|
655
|
+
if (!fetched || fetched.registryUrl !== String(registryUrl)) {
|
|
656
|
+
throw new Error("registry source authority is missing or mismatched");
|
|
657
|
+
}
|
|
658
|
+
if (!registry.plugins?.some((candidate) => candidate === entry)) {
|
|
659
|
+
throw new Error("registry entry is not bound to the fetched catalog");
|
|
660
|
+
}
|
|
661
|
+
const normalized = inspectRegistryEntrySource(entry);
|
|
662
|
+
if (normalized.sourceType === "git") {
|
|
663
|
+
enforcePluginSourcePolicy(normalized.source, fetched.managedPolicy, {
|
|
664
|
+
action: "registry-resolved-source",
|
|
665
|
+
blockedOnly: true,
|
|
666
|
+
cwd,
|
|
667
|
+
kindHint: "git",
|
|
668
|
+
});
|
|
669
|
+
} else {
|
|
670
|
+
let registryOrigin;
|
|
671
|
+
let archiveOrigin;
|
|
672
|
+
try {
|
|
673
|
+
registryOrigin = new URL(fetched.registryUrl).origin;
|
|
674
|
+
archiveOrigin = new URL(normalized.sourceIdentity).origin;
|
|
675
|
+
} catch {
|
|
676
|
+
throw new Error("registry archive source URL authority is invalid");
|
|
677
|
+
}
|
|
678
|
+
if (archiveOrigin !== registryOrigin) {
|
|
679
|
+
throw new Error("registry archive source must use the registry origin");
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
const authority = Object.freeze({});
|
|
683
|
+
registryResolutionAuthorities.set(
|
|
684
|
+
authority,
|
|
685
|
+
Object.freeze({
|
|
686
|
+
registryUrl: fetched.registryUrl,
|
|
687
|
+
source: normalized.source,
|
|
688
|
+
declaredSource: normalized.sourceIdentity,
|
|
689
|
+
sourceType: normalized.sourceType,
|
|
690
|
+
ref: normalized.ref,
|
|
691
|
+
archiveSha256:
|
|
692
|
+
normalized.sourceType === "archive"
|
|
693
|
+
? normalized.archiveSpec.sha256
|
|
694
|
+
: null,
|
|
695
|
+
payloadSha256: null,
|
|
696
|
+
documentSha256: fetched.documentSha256,
|
|
697
|
+
}),
|
|
698
|
+
);
|
|
699
|
+
return {
|
|
700
|
+
...normalized,
|
|
701
|
+
registryResolutionAuthority: authority,
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/** Consume-only validation used by the installer before any target I/O. */
|
|
706
|
+
export function assertRegistryResolutionAuthority(
|
|
707
|
+
authority,
|
|
708
|
+
{ registryUrl, source, ref } = {},
|
|
709
|
+
) {
|
|
710
|
+
const issued = registryResolutionAuthorities.get(authority);
|
|
711
|
+
if (
|
|
712
|
+
!issued ||
|
|
713
|
+
issued.registryUrl !== String(registryUrl) ||
|
|
714
|
+
(issued.source ?? null) !== (source ?? null) ||
|
|
715
|
+
(issued.ref || null) !== (ref || null)
|
|
716
|
+
) {
|
|
717
|
+
throw new Error("registry source authority is missing or mismatched");
|
|
718
|
+
}
|
|
719
|
+
return issued;
|
|
498
720
|
}
|
|
499
721
|
|
|
500
722
|
/** List installable entries from a registry (for `cc plugin search`). */
|
|
501
723
|
export function listRegistryPlugins(registry) {
|
|
502
|
-
return (registry.plugins || []).map((p) =>
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
724
|
+
return (registry.plugins || []).map((p) => {
|
|
725
|
+
const source = normalizeMarketplacePackageSource(p);
|
|
726
|
+
return {
|
|
727
|
+
name: p.name,
|
|
728
|
+
version: p.version || null,
|
|
729
|
+
source:
|
|
730
|
+
source.type === "archive"
|
|
731
|
+
? source.url
|
|
732
|
+
: source.type === "git"
|
|
733
|
+
? source.source
|
|
734
|
+
: null,
|
|
735
|
+
sourceType: source.type,
|
|
736
|
+
ref: source.type === "git" ? source.ref : null,
|
|
737
|
+
description: p.description || "",
|
|
738
|
+
enabled: source.type !== "dynamic-disabled",
|
|
739
|
+
...(source.type === "dynamic-disabled"
|
|
740
|
+
? { disabledReason: source.reason }
|
|
741
|
+
: {}),
|
|
742
|
+
};
|
|
743
|
+
});
|
|
509
744
|
}
|
|
510
745
|
|
|
511
746
|
/**
|
|
@@ -543,20 +778,30 @@ export function resolvePluginEntry(registry, name) {
|
|
|
543
778
|
* entry: object, fromCache: boolean }>}
|
|
544
779
|
*/
|
|
545
780
|
export async function resolveRemoteSource(url, opts = {}) {
|
|
781
|
+
assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
|
|
782
|
+
const managedPolicy = resolveManagedPolicy(opts);
|
|
546
783
|
const token = resolveRegistryToken(url, opts);
|
|
547
784
|
const { registry, fromCache, documentSha256, networkAuthority } =
|
|
548
785
|
await fetchRegistry(url, {
|
|
549
786
|
...opts,
|
|
550
787
|
token,
|
|
788
|
+
managedPolicy,
|
|
551
789
|
});
|
|
552
790
|
const entry = resolvePluginEntry(registry, opts.name);
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
791
|
+
const authorized = authorizeRegistryPluginEntry(registry, entry, {
|
|
792
|
+
registryUrl: url,
|
|
793
|
+
cwd: opts.cwd,
|
|
794
|
+
});
|
|
795
|
+
const resolvedSource =
|
|
796
|
+
opts.materialize === false
|
|
797
|
+
? authorized
|
|
798
|
+
: await resolveRegistryEntrySource(url, entry, {
|
|
799
|
+
...opts,
|
|
800
|
+
token,
|
|
801
|
+
registryResolutionAuthority: authorized.registryResolutionAuthority,
|
|
802
|
+
});
|
|
556
803
|
return {
|
|
557
|
-
|
|
558
|
-
ref: entry.ref || null,
|
|
559
|
-
sha256: entry.sha256 || null,
|
|
804
|
+
...resolvedSource,
|
|
560
805
|
entry,
|
|
561
806
|
fromCache,
|
|
562
807
|
documentSha256,
|
|
@@ -564,6 +809,141 @@ export async function resolveRemoteSource(url, opts = {}) {
|
|
|
564
809
|
};
|
|
565
810
|
}
|
|
566
811
|
|
|
812
|
+
/** Inspect one selected entry without fetching any candidate source bytes. */
|
|
813
|
+
export function inspectRegistryEntrySource(entry) {
|
|
814
|
+
const packageSource = assertMarketplaceSourceExecutable(entry);
|
|
815
|
+
if (packageSource.type === "archive") {
|
|
816
|
+
return {
|
|
817
|
+
source: null,
|
|
818
|
+
sourceIdentity: packageSource.url,
|
|
819
|
+
sourceType: "archive",
|
|
820
|
+
ref: null,
|
|
821
|
+
sha256: entry.sha256 || null,
|
|
822
|
+
archiveSpec: packageSource,
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
// Preserve the canonical remote Git validation from the registry security
|
|
826
|
+
// boundary rather than accepting the adapter's more general local shape.
|
|
827
|
+
const normalized = normalizeRegistryPluginSource(entry);
|
|
828
|
+
return {
|
|
829
|
+
source: normalized.source,
|
|
830
|
+
sourceIdentity: entry.source,
|
|
831
|
+
sourceType: "git",
|
|
832
|
+
ref: normalized.ref,
|
|
833
|
+
sha256: entry.sha256 || null,
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/** Materialize one already-selected, preflight-approved registry entry. */
|
|
838
|
+
export async function resolveRegistryEntrySource(url, entry, opts = {}) {
|
|
839
|
+
const inspected = inspectRegistryEntrySource(entry);
|
|
840
|
+
const issued = assertRegistryResolutionAuthority(
|
|
841
|
+
opts.registryResolutionAuthority,
|
|
842
|
+
{
|
|
843
|
+
registryUrl: url,
|
|
844
|
+
source: inspected.source,
|
|
845
|
+
ref: inspected.ref,
|
|
846
|
+
},
|
|
847
|
+
);
|
|
848
|
+
if (
|
|
849
|
+
issued.sourceType !== inspected.sourceType ||
|
|
850
|
+
issued.declaredSource !== inspected.sourceIdentity ||
|
|
851
|
+
issued.archiveSha256 !==
|
|
852
|
+
(inspected.sourceType === "archive" ? inspected.archiveSpec.sha256 : null)
|
|
853
|
+
) {
|
|
854
|
+
throw new Error("registry source authority is missing or mismatched");
|
|
855
|
+
}
|
|
856
|
+
if (inspected.sourceType === "archive") {
|
|
857
|
+
const archive = await fetchAndMaterializeMarketplaceArchive({
|
|
858
|
+
registryUrl: url,
|
|
859
|
+
url: inspected.archiveSpec.requestUrl || inspected.archiveSpec.url,
|
|
860
|
+
sha256: inspected.archiveSpec.sha256,
|
|
861
|
+
token: opts.token,
|
|
862
|
+
allowInsecure: opts.allowInsecure === true,
|
|
863
|
+
offline: opts.offline === true,
|
|
864
|
+
artifactCacheDir: opts.artifactCacheDir,
|
|
865
|
+
sourceCacheDir: opts.archiveSourceCacheDir,
|
|
866
|
+
timeoutMs: opts.timeoutMs,
|
|
867
|
+
fetchImpl: opts.archiveFetchImpl,
|
|
868
|
+
resolveHostname: opts.resolveHostname,
|
|
869
|
+
proxyUrl: opts.proxyUrl,
|
|
870
|
+
pacFile: opts.pacFile,
|
|
871
|
+
caFile: opts.caFile,
|
|
872
|
+
});
|
|
873
|
+
const materializedAuthority = Object.freeze({});
|
|
874
|
+
registryResolutionAuthorities.set(
|
|
875
|
+
materializedAuthority,
|
|
876
|
+
Object.freeze({
|
|
877
|
+
...issued,
|
|
878
|
+
source: archive.dir,
|
|
879
|
+
payloadSha256: archive.authority.payloadSha256,
|
|
880
|
+
}),
|
|
881
|
+
);
|
|
882
|
+
return {
|
|
883
|
+
...inspected,
|
|
884
|
+
source: archive.dir,
|
|
885
|
+
archiveAuthority: archive.authority,
|
|
886
|
+
registryResolutionAuthority: materializedAuthority,
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
return {
|
|
890
|
+
...inspected,
|
|
891
|
+
registryResolutionAuthority: opts.registryResolutionAuthority,
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function resolveManagedPolicy(opts) {
|
|
896
|
+
return resolvePluginManagedPolicy(opts);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function responseUrlChanged(requested, effective) {
|
|
900
|
+
if (!effective) return false;
|
|
901
|
+
try {
|
|
902
|
+
const left = new URL(String(requested));
|
|
903
|
+
const right = new URL(String(effective));
|
|
904
|
+
for (const value of [left, right]) {
|
|
905
|
+
value.username = "";
|
|
906
|
+
value.password = "";
|
|
907
|
+
value.hash = "";
|
|
908
|
+
}
|
|
909
|
+
return left.href !== right.href;
|
|
910
|
+
} catch {
|
|
911
|
+
return true;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function publicRegistryErrorMessage(error) {
|
|
916
|
+
if (!error) return "unknown error";
|
|
917
|
+
if (typeof error.publicMessage === "string") return error.publicMessage;
|
|
918
|
+
if (error.name === "AbortError") return "registry request timed out";
|
|
919
|
+
if (error instanceof TypeError) return "registry transport failed";
|
|
920
|
+
return "registry request failed";
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function publicRegistryCacheErrorMessage(error) {
|
|
924
|
+
const message = String(error?.message || "");
|
|
925
|
+
if (message.includes("digest mismatch")) {
|
|
926
|
+
return "registry immutable cache digest mismatch";
|
|
927
|
+
}
|
|
928
|
+
if (message.includes("ambiguous")) {
|
|
929
|
+
return "registry immutable cache is ambiguous";
|
|
930
|
+
}
|
|
931
|
+
if (message.includes("more than")) {
|
|
932
|
+
return "registry immutable cache has too many candidates";
|
|
933
|
+
}
|
|
934
|
+
if (message.includes("single-link regular file")) {
|
|
935
|
+
return "registry immutable cache entry is invalid";
|
|
936
|
+
}
|
|
937
|
+
return "registry immutable cache document is invalid";
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function publicRegistryFailure(message, { cacheFallbackAllowed = false } = {}) {
|
|
941
|
+
const error = new Error(message);
|
|
942
|
+
error.publicMessage = message;
|
|
943
|
+
error.cacheFallbackAllowed = cacheFallbackAllowed;
|
|
944
|
+
return error;
|
|
945
|
+
}
|
|
946
|
+
|
|
567
947
|
/** Best-effort local temp dir helper (kept here so tests can stub fs deps). */
|
|
568
948
|
export function _tmpRoot() {
|
|
569
949
|
return os.tmpdir();
|