chainlesschain 0.165.6 → 0.165.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/assets/web-panel/assets/{AIOps-vfoDb6HZ.js → AIOps-B3gWmXjo.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Cm5f7p93.js → ActionButton-G9pdT-0e.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-yRuvgWxa.js → Analytics-Ca30J7m2.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BROsC4h1.js → AppLayout-BtVTm8PU.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CimHApb1.js → Artifacts-BhViNa6u.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DI8kr8zI.js → Audit-DhKq-FnY.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DSLzKy-n.js → BackgroundAgents-BL0SVbnE.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DkH_n2r1.js → Backup-BWkblnFi.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CKSrUoRA.js → BaseInput-ClaRNrF8.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-ClwfMXUS.js → Chat-sZE2Z77o.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CjMaqfL7.js → ChatBubbleRenderer-BAfKOnRe.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BYTehcQG.js → Checkbox-Cvhay5WM.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B8AMcp6J.js → Codegen-BpejL-4N.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BNSogPo3.js → Col-g2Ki6g_1.js} +1 -1
- package/src/assets/web-panel/assets/{Community-JxPvGb8O.js → Community-D2xKbNo9.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DF7bFAgE.js → Compact-DbL1Pds3.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bvhqnwrq.js → Compliance-DGtZCV4c.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BDS0CJvM.js → Cowork-4kQpoNsI.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DqvRfxoQ.js → Cron-BxTYXXAN.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CDcIvnqS.js → Crosschain-k0J0LT2N.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D7jaUxU3.js → DID-BaqhoPgN.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-B5A18wEV.js → Dashboard-T4RVPIM1.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-_6IVkGag.js → Dropdown-DszOToWq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C9PeiKCG.js → EmailListRenderer-C9KJU7HF.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DXjuEHrX.js → FamilyGuardDashboard-Cmr0JnsA.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B4IAR9zv.js → Federation-BI-_2xZX.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CxQeSNx1.js → FormItemContext-DsnDEPsl.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-B8GS-Y3N.js → GenericCardRenderer-BLncAKKK.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BS8SouUX.js → Git-BPQhGXDn.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Dq9aYb5Q.js → Governance-CyElbUQW.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BrCEe9AJ.js → Inference-CcSntBQD.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DybOpiY2.js → KnowledgeGraph-p6e7HZ2r.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DBpzbRfx.js → Logs-CNEtr-wz.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-Bj9xI0YH.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Cc7CCSCu.js → Marketplace-Dd2notAJ.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DFaIhOfu.js → McpTools-BBsN2CSW.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-KCqlp9ol.js → Memory-Dg2uTvwx.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Bys84ySN.js → MobileBridge-r1YMRiJV.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B-6qU-je.js → MobileProjects-C-89kSm4.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BgY-fvCq.js → Mtc-CrPZcVgf.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-B8pCmqQJ.js → MtcAudit-sxi7hqJb.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BbOmYutA.js → Multisig-CAgOMfF1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BocLQz_M.js → NLProgramming-DafaPts3.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CSwbxEop.js → Notes-CJM-xUW_.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CqFNSYG3.js → NotificationSettings-L0ZjfAh_.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C6gMfAk7.js → OrderTableRenderer-BB1CvCK0.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-QrDXVdMJ.js → Organization-DeLtgAD8.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Cxntotjt.js → Overflow-CKC2b10z.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CIdBOwNq.js → P2P-eyf2rzY1.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CT5aP8rD.js → PdhVaultBrowser-5upNIOW8.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DH7OM8XU.js → Permissions-B8p3W6JK.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CnJqbWce.js → PersonalDataHub-BnRMxTq3.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-5C3HG1kH.js → Pipeline-DjCNI1li.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B4yIp7xK.js → Privacy-u6gXLbWT.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BMvt-2M0.js → ProjectInit-BZAw4tkM.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CvSTEMRG.js → ProjectSettings-B1Tr72q7.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BKBJxEek.js → Projects-DO7HFuBQ.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BdwUqFWN.js → Providers-BjCztyhj.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DtnCKlTm.js → QrScannerModal-ZWyCUbR1.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-sTbgwyNr.js → QuickAsk-DhDjeW4G.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bzr14gSb.js → Recommend-R_CN2MuU.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C_TF5IaJ.js → RemoteSession-uxFcujeu.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-GCAM4H2K.js → Reputation-DX8D7rks.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Dir0r6OQ.js → Row-BYahQILD.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CpGnlecX.js → RssFeed-CUSQ2tm-.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DMMamC6u.js → Search-CnN_S8Cs.js} +1 -1
- package/src/assets/web-panel/assets/{Security-yqay5yG-.js → Security-DcZlh-O4.js} +4 -4
- package/src/assets/web-panel/assets/{Services-C5i2wh9G.js → Services-ZhBOjzIX.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-vUqk35yj.js → Skeleton-D5Qnq_lI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DzcSkhot.js → Skills-BC9cZ3Be.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CB6qbYNr.js → Sla-DauJ3E8_.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-DbNeZ_fh.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-D-RrsxTJ.js → SyncSettings-DUvJbSct.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DFxdOJiq.js → Tasks-BXa-5noQ.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-SKAob_Aq.js → Templates-JxaiUt_3.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BPfsfHQ-.js → Tenant-BW4ERQN8.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-8iw7FpUf.js → Terminal-DltUbdtv.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C4Gjj5Su.js → TimelineRenderer-CMoZz0Tc.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-COYhN18e.js → Tokens-D_VEV60K.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B-JzXcf6.js → Trigger-CCXTCs0R.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BJ5bg0nI.js → Trust-lkGRIPcG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BG_ByRMf.js → UkeySign-BfGeQ9HX.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B3kOnSiU.js → VideoEditing-BEC7pYvc.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CcoGFnK7.js → Wallet-BZdG24o0.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DdT8vRC_.js → WebAuthn-yoEddedp.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CZu3M9N8.js → WorkflowEditor-BXZEprOW.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DxJt3Qba.js → chat-gg0bzZal.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DdYfwJaB.js → colors-DjRVRuin.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BG3SUzwj.js → compact-item-su-dieAc.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-By51z2-1.js → createContext-Ci3S8oKF.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CxbFX9uH.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-B0apZ3KJ.js → hasIn-D8F10heH.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE76D-kA.js → index-11wtYQiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkvcEo4p.js → index-1IgxgALJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bos5_aMV.js → index-1agckWSq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfWYsB9t.js → index-5JqhujnR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAhHirVH.js → index-6Tniqk1s.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYINoRLK.js → index-8Z-8BIlN.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5rLiEo0.js → index-BCI5v0yB.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZ_0qm4g.js → index-BDJntYmJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWEuZWCK.js → index-BGzixOGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLIbGOLX.js → index-BMMOVoLD.js} +1 -1
- package/src/assets/web-panel/assets/{index-bP_VlDb4.js → index-BTFpdBtt.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3rWuPMW.js → index-Ba_HyPnE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Da_WBdLX.js → index-BbF1IHof.js} +1 -1
- package/src/assets/web-panel/assets/{index-RD6O-0C6.js → index-BgT9su_e.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpjZhgNd.js → index-BzPB6kGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q2SCX-MI.js → index-C6ygzplr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIrJfqO4.js → index-CC3ZDjzY.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBPf3TpM.js → index-CEmXRhAE.js} +1 -1
- package/src/assets/web-panel/assets/index-CJGOlduY.js +1 -0
- package/src/assets/web-panel/assets/{index-Cp6e-hGw.js → index-CMPqd5Cv.js} +1 -1
- package/src/assets/web-panel/assets/{index-kf57LJ93.js → index-COWd8uGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW93tZDv.js → index-CPIYSp4H.js} +1 -1
- package/src/assets/web-panel/assets/{index-BewKYrX9.js → index-CR0h6seT.js} +1 -1
- package/src/assets/web-panel/assets/{index-p1_8SbI1.js → index-CUmDhC4j.js} +1 -1
- package/src/assets/web-panel/assets/index-CkBCN5yH.js +1 -0
- package/src/assets/web-panel/assets/{index-ZxtP8jG7.js → index-CusgZPtc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWF8_mkx.js → index-DBAETXft.js} +1 -1
- package/src/assets/web-panel/assets/{index-BP94CrBV.js → index-DIJ2qFOC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNIxXMmN.js → index-Dbq4NMET.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzmQElZy.js → index-DkMEF5VT.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZFbps9po.js → index-DkfQR3hM.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dz49VOyQ.js → index-DnLucOn9.js} +1 -1
- package/src/assets/web-panel/assets/{index-oxRrmFO9.js → index-DugB_aKa.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bissefox.js → index-DyLUarhZ.js} +3 -3
- package/src/assets/web-panel/assets/{index-DaIz0NAW.js → index-T6_6ygJZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-SQqOlR3d.js → index-Tu8eWqh-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCn8IcxV.js → index-W7XUgdVL.js} +1 -1
- package/src/assets/web-panel/assets/{index-3jWkIu5j.js → index-dHZinfko.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1C9i79A.js → index-jyNgp5FI.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BWJktcg8.js → initDefaultProps-Cv4p8e4w.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BEXBzUFV.js → motion-B8EwmMOw.js} +1 -1
- package/src/assets/web-panel/assets/{move-BUKwn2sx.js → move-B8R9F8TK.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-FTI7btqQ.js → mtc-parser-DoFYz5Bp.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CsLtSPH3.js → omit-CHWYLBjI.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DWZxsT_S.js → pickAttrs-CXuPDO77.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Bcfs43CX.js → placementArrow-BaAtUUon.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-a9bcC1p-.js → responsiveObserve-DCc4zcDD.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BDTzgOXN.js → slide-B_lx89oZ.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Iq1j_HUg.js → statusUtils-DE8DPmR0.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DAZEYxeh.js → styleChecker-1ktXOsL_.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DVkjyDrh.js → useFlexGapSupport-xH89-wjh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-wXTpNTHt.js → useFs-DscvfW5p.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CtQM_aZr.js → usePersonalDataHub-RdMKQta7.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-hjEoG85v.js → vnode-Z3wTni2T.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BHVpyKg_.js → zoom-B0iChCsm.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +91 -60
- package/src/commands/git.js +39 -0
- package/src/commands/plugin.js +17 -2
- package/src/commands/session-group.js +131 -0
- package/src/commands/session-location.js +48 -0
- package/src/commands/session.js +11 -0
- package/src/commands/sync-providers.js +4 -1
- package/src/commands/update.js +1 -0
- package/src/data/changelog.json +27 -1
- package/src/gateways/ws/ws-agent-handler.js +20 -12
- package/src/harness/jsonl-session-store.js +279 -37
- package/src/harness/mcp-client.js +701 -91
- package/src/harness/session-index.js +15 -4
- package/src/lib/agent-worktree.js +8 -1
- package/src/lib/auto-mode-safety-classifier.js +3 -17
- package/src/lib/background-agent-keeper-protocol.js +26 -6
- package/src/lib/background-agent-supervisor.js +17 -1
- package/src/lib/browser-evidence.js +1405 -0
- package/src/lib/chrome-connector.js +465 -79
- package/src/lib/claude-project-auto-memory.js +481 -0
- package/src/lib/claude-project-storage-layout.js +104 -0
- package/src/lib/cloud/cloud-client.js +40 -0
- package/src/lib/execution-location-local-supervisor.mjs +226 -0
- package/src/lib/execution-location-runner-lifecycle.js +991 -0
- package/src/lib/execution-location-target-preflight.js +333 -0
- package/src/lib/execution-location-target.js +783 -13
- package/src/lib/file-lines.js +6 -1
- package/src/lib/gateway-client-resilience.js +571 -0
- package/src/lib/gitlab-mr-worktree.js +176 -0
- package/src/lib/hooks-v2-producers.js +119 -15
- package/src/lib/hooks-v2-workspace-context.js +44 -1
- package/src/lib/host-resource-budget.js +295 -0
- package/src/lib/ide-context.js +15 -2
- package/src/lib/llm-config-defaults.js +43 -0
- package/src/lib/mcp-lifecycle-authority.js +887 -0
- package/src/lib/mcp-oauth.js +101 -20
- package/src/lib/mcp-tls.js +103 -3
- package/src/lib/paths.js +260 -32
- package/src/lib/permission-request.js +48 -0
- package/src/lib/plugin-runtime/bin.js +3 -1
- package/src/lib/plugin-runtime/hooks.js +3 -1
- package/src/lib/plugin-runtime/install.js +117 -1
- package/src/lib/plugin-runtime/lsp.js +4 -4
- package/src/lib/plugin-runtime/marketplace-archive-source.js +40 -5
- package/src/lib/plugin-runtime/marketplace-catalog.js +13 -2
- package/src/lib/plugin-runtime/marketplace-command-descriptor.js +343 -0
- package/src/lib/plugin-runtime/marketplace-command-source.js +580 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +33 -8
- package/src/lib/plugin-runtime/mcp.js +3 -1
- package/src/lib/plugin-runtime/monitors.js +3 -1
- package/src/lib/plugin-runtime/remote-source.js +151 -6
- package/src/lib/plugin-runtime/settings.js +3 -1
- package/src/lib/plugin-runtime/trust.js +274 -13
- package/src/lib/process-execution-broker/credential-agent.js +210 -41
- package/src/lib/process-execution-broker/index.js +15 -1
- package/src/lib/process-execution-broker/linux-cgroup-v2.js +391 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +50 -28
- package/src/lib/project-mcp-trust.js +165 -10
- package/src/lib/resume-session.js +9 -6
- package/src/lib/session-core-singletons.js +26 -11
- package/src/lib/session-host-lease.js +52 -16
- package/src/lib/session-lifecycle-scope.js +153 -0
- package/src/lib/session-message-fabric.js +168 -21
- package/src/lib/session-projection.js +244 -4
- package/src/lib/session-store-guard.js +3 -1
- package/src/lib/session-workbench-store.js +466 -0
- package/src/lib/spellcheck.js +211 -0
- package/src/lib/sub-agent-context.js +9 -9
- package/src/lib/sync-cli-db.js +485 -129
- package/src/lib/sync-credentials.js +31 -20
- package/src/lib/web-fetch.js +112 -40
- package/src/lib/web-search.js +127 -61
- package/src/lib/workspace-trust.js +793 -0
- package/src/repl/agent-repl.js +124 -5
- package/src/repl/permission-prompt.js +10 -12
- package/src/runtime/agent-core.js +311 -18
- package/src/runtime/bootstrap.js +37 -13
- package/src/runtime/coding-agent-contract-shared.cjs +11 -4
- package/src/runtime/headless-runner.js +234 -36
- package/src/runtime/headless-stream.js +181 -6
- package/src/runtime/mcp-config.js +298 -8
- package/src/runtime/policies/agent-policy.js +8 -0
- package/src/runtime/status-diagnostics-lite.js +9 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-BHUI7cox.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +0 -1
- package/src/assets/web-panel/assets/index-BETiL0a3.js +0 -1
- package/src/assets/web-panel/assets/index-BhAVHuW8.js +0 -1
|
@@ -40,6 +40,11 @@ import {
|
|
|
40
40
|
} from "../plugin-source-identity.js";
|
|
41
41
|
import { createMarketplaceNetworkTransport } from "./marketplace-network.js";
|
|
42
42
|
import { fetchAndMaterializeMarketplaceArchive } from "./marketplace-archive-source.js";
|
|
43
|
+
import {
|
|
44
|
+
createSameOriginMarketplaceHeaderFetch,
|
|
45
|
+
runMarketplaceCommandSource,
|
|
46
|
+
runMarketplaceHeadersHelper,
|
|
47
|
+
} from "./marketplace-command-source.js";
|
|
43
48
|
import {
|
|
44
49
|
assertMarketplaceSourceExecutable,
|
|
45
50
|
normalizeMarketplacePackageSource,
|
|
@@ -215,12 +220,27 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
215
220
|
);
|
|
216
221
|
}
|
|
217
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
|
+
|
|
218
231
|
const networkTransport = createMarketplaceNetworkTransport({
|
|
219
232
|
proxyUrl: opts.proxyUrl,
|
|
220
233
|
pacFile: opts.pacFile,
|
|
221
234
|
caFile: opts.caFile,
|
|
222
235
|
});
|
|
223
|
-
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;
|
|
224
244
|
let networkErr = null;
|
|
225
245
|
let cacheFallbackAllowed = false;
|
|
226
246
|
try {
|
|
@@ -283,6 +303,7 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
283
303
|
},
|
|
284
304
|
url,
|
|
285
305
|
managedPolicy,
|
|
306
|
+
marketplaceRequestHeaders,
|
|
286
307
|
);
|
|
287
308
|
} catch (err) {
|
|
288
309
|
networkErr = err;
|
|
@@ -308,7 +329,14 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
308
329
|
cacheDir,
|
|
309
330
|
expectedSha256,
|
|
310
331
|
});
|
|
311
|
-
if (cached)
|
|
332
|
+
if (cached) {
|
|
333
|
+
return bindFetchedRegistry(
|
|
334
|
+
cached,
|
|
335
|
+
url,
|
|
336
|
+
managedPolicy,
|
|
337
|
+
marketplaceRequestHeaders,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
312
340
|
} catch (cacheError) {
|
|
313
341
|
throw new Error(
|
|
314
342
|
`could not fetch registry ${displayUrl}: ${publicRegistryErrorMessage(networkErr)}; verified immutable cache rejected: ${publicRegistryCacheErrorMessage(cacheError)}`,
|
|
@@ -323,6 +351,38 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
323
351
|
);
|
|
324
352
|
}
|
|
325
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
|
+
|
|
326
386
|
async function readBoundedRegistryResponse(response) {
|
|
327
387
|
const contentLength = Number(response?.headers?.get?.("content-length"));
|
|
328
388
|
if (
|
|
@@ -584,7 +644,12 @@ function freezeRegistryValue(value) {
|
|
|
584
644
|
return Object.freeze(value);
|
|
585
645
|
}
|
|
586
646
|
|
|
587
|
-
function bindFetchedRegistry(
|
|
647
|
+
function bindFetchedRegistry(
|
|
648
|
+
result,
|
|
649
|
+
registryUrl,
|
|
650
|
+
managedPolicy,
|
|
651
|
+
marketplaceRequestHeaders = null,
|
|
652
|
+
) {
|
|
588
653
|
const registry = freezeRegistryValue(result.registry);
|
|
589
654
|
fetchedRegistryAuthorities.set(
|
|
590
655
|
registry,
|
|
@@ -592,6 +657,11 @@ function bindFetchedRegistry(result, registryUrl, managedPolicy) {
|
|
|
592
657
|
registryUrl: String(registryUrl),
|
|
593
658
|
documentSha256: result.documentSha256 || null,
|
|
594
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,
|
|
595
665
|
}),
|
|
596
666
|
);
|
|
597
667
|
return { ...result, registry };
|
|
@@ -666,7 +736,7 @@ export function authorizeRegistryPluginEntry(
|
|
|
666
736
|
cwd,
|
|
667
737
|
kindHint: "git",
|
|
668
738
|
});
|
|
669
|
-
} else {
|
|
739
|
+
} else if (normalized.sourceType === "archive") {
|
|
670
740
|
let registryOrigin;
|
|
671
741
|
let archiveOrigin;
|
|
672
742
|
try {
|
|
@@ -678,6 +748,8 @@ export function authorizeRegistryPluginEntry(
|
|
|
678
748
|
if (archiveOrigin !== registryOrigin) {
|
|
679
749
|
throw new Error("registry archive source must use the registry origin");
|
|
680
750
|
}
|
|
751
|
+
} else if (normalized.sourceType !== "command") {
|
|
752
|
+
throw new Error("registry package source type is unsupported");
|
|
681
753
|
}
|
|
682
754
|
const authority = Object.freeze({});
|
|
683
755
|
registryResolutionAuthorities.set(
|
|
@@ -694,6 +766,9 @@ export function authorizeRegistryPluginEntry(
|
|
|
694
766
|
: null,
|
|
695
767
|
payloadSha256: null,
|
|
696
768
|
documentSha256: fetched.documentSha256,
|
|
769
|
+
commandMode:
|
|
770
|
+
normalized.sourceType === "command" ? normalized.commandSpec.mode : null,
|
|
771
|
+
marketplaceRequestHeaders: fetched.marketplaceRequestHeaders,
|
|
697
772
|
}),
|
|
698
773
|
);
|
|
699
774
|
return {
|
|
@@ -734,6 +809,7 @@ export function listRegistryPlugins(registry) {
|
|
|
734
809
|
: null,
|
|
735
810
|
sourceType: source.type,
|
|
736
811
|
ref: source.type === "git" ? source.ref : null,
|
|
812
|
+
...(source.type === "command" ? { mode: source.mode } : {}),
|
|
737
813
|
description: p.description || "",
|
|
738
814
|
enabled: source.type !== "dynamic-disabled",
|
|
739
815
|
...(source.type === "dynamic-disabled"
|
|
@@ -800,13 +876,20 @@ export async function resolveRemoteSource(url, opts = {}) {
|
|
|
800
876
|
token,
|
|
801
877
|
registryResolutionAuthority: authorized.registryResolutionAuthority,
|
|
802
878
|
});
|
|
803
|
-
|
|
879
|
+
const result = {
|
|
804
880
|
...resolvedSource,
|
|
805
|
-
entry,
|
|
806
881
|
fromCache,
|
|
807
882
|
documentSha256,
|
|
808
883
|
...(networkAuthority ? { networkAuthority } : {}),
|
|
809
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;
|
|
810
893
|
}
|
|
811
894
|
|
|
812
895
|
/** Inspect one selected entry without fetching any candidate source bytes. */
|
|
@@ -822,6 +905,22 @@ export function inspectRegistryEntrySource(entry) {
|
|
|
822
905
|
archiveSpec: packageSource,
|
|
823
906
|
};
|
|
824
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
|
+
}
|
|
825
924
|
// Preserve the canonical remote Git validation from the registry security
|
|
826
925
|
// boundary rather than accepting the adapter's more general local shape.
|
|
827
926
|
const normalized = normalizeRegistryPluginSource(entry);
|
|
@@ -869,6 +968,7 @@ export async function resolveRegistryEntrySource(url, entry, opts = {}) {
|
|
|
869
968
|
proxyUrl: opts.proxyUrl,
|
|
870
969
|
pacFile: opts.pacFile,
|
|
871
970
|
caFile: opts.caFile,
|
|
971
|
+
requestHeaders: issued.marketplaceRequestHeaders,
|
|
872
972
|
});
|
|
873
973
|
const materializedAuthority = Object.freeze({});
|
|
874
974
|
registryResolutionAuthorities.set(
|
|
@@ -886,6 +986,51 @@ export async function resolveRegistryEntrySource(url, entry, opts = {}) {
|
|
|
886
986
|
registryResolutionAuthority: materializedAuthority,
|
|
887
987
|
};
|
|
888
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
|
+
}
|
|
889
1034
|
return {
|
|
890
1035
|
...inspected,
|
|
891
1036
|
registryResolutionAuthority: opts.registryResolutionAuthority,
|
|
@@ -37,7 +37,9 @@ export function collectPluginSettings(opts = {}) {
|
|
|
37
37
|
} catch {
|
|
38
38
|
return { env: {}, model: null, sources: [] };
|
|
39
39
|
}
|
|
40
|
-
const { trusted, skipped } = partitionByTrust(plugins
|
|
40
|
+
const { trusted, skipped } = partitionByTrust(plugins, {
|
|
41
|
+
workspaceRoot: opts.cwd,
|
|
42
|
+
});
|
|
41
43
|
warnUntrustedOnce(
|
|
42
44
|
skipped.filter((p) => p.manifest?.components?.settings).map((p) => p.name),
|
|
43
45
|
"settings",
|
|
@@ -22,12 +22,21 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import path from "path";
|
|
25
|
+
import { createHash } from "node:crypto";
|
|
25
26
|
import { getElectronUserDataDir } from "../paths.js";
|
|
26
27
|
import { withFileLock } from "../with-file-lock.js";
|
|
27
28
|
import {
|
|
28
29
|
mutateSecurityStore,
|
|
29
30
|
readSecurityStore,
|
|
30
31
|
} from "../durable-security-store.js";
|
|
32
|
+
import {
|
|
33
|
+
checkRecordedWorkspaceTrust,
|
|
34
|
+
evaluateWorkspaceTrustDecision,
|
|
35
|
+
projectWorkspaceTrustAudit,
|
|
36
|
+
recordWorkspaceTrustConsent,
|
|
37
|
+
resolveCanonicalWorkspaceRepoIdentity,
|
|
38
|
+
revokeRecordedWorkspaceTrust,
|
|
39
|
+
} from "../workspace-trust.js";
|
|
31
40
|
|
|
32
41
|
export const _deps = {
|
|
33
42
|
now: () => new Date().toISOString(),
|
|
@@ -35,10 +44,16 @@ export const _deps = {
|
|
|
35
44
|
// Path of the trust store; injectable so unit tests never touch the real
|
|
36
45
|
// user-data dir.
|
|
37
46
|
storePath: () => path.join(getElectronUserDataDir(), "plugin-trust.json"),
|
|
47
|
+
// Test-only seam. Production uses workspace-trust.js's one shared ledger.
|
|
48
|
+
workspaceTrustStorePath: () => null,
|
|
38
49
|
};
|
|
39
50
|
|
|
40
51
|
const AUTO_TRUSTED_SCOPES = new Set(["user", "local"]);
|
|
41
52
|
|
|
53
|
+
function defaultPluginTrustStorePath() {
|
|
54
|
+
return path.join(getElectronUserDataDir(), "plugin-trust.json");
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
export function loadTrustStore() {
|
|
43
58
|
return readSecurityStore(_deps.storePath(), "plugin trust");
|
|
44
59
|
}
|
|
@@ -47,26 +62,228 @@ function trustKey(scope, name) {
|
|
|
47
62
|
return `${scope}:${name}`;
|
|
48
63
|
}
|
|
49
64
|
|
|
65
|
+
const PLUGIN_WORKSPACE_TRUST_SOURCE = "plugin";
|
|
66
|
+
|
|
67
|
+
function workspaceTrustStoreOptions(options = {}) {
|
|
68
|
+
const sourceStore = options.storePath || _deps.storePath();
|
|
69
|
+
let usesDefaultSourceStore = false;
|
|
70
|
+
try {
|
|
71
|
+
usesDefaultSourceStore =
|
|
72
|
+
path.resolve(sourceStore) === path.resolve(defaultPluginTrustStorePath());
|
|
73
|
+
} catch {
|
|
74
|
+
// Let the source-specific mutation/check fail closed later. This helper
|
|
75
|
+
// must not turn an invalid custom path into the production shared ledger.
|
|
76
|
+
}
|
|
77
|
+
const store =
|
|
78
|
+
options.workspaceTrustStorePath ||
|
|
79
|
+
process.env.CC_WORKSPACE_TRUST_STORE ||
|
|
80
|
+
_deps.workspaceTrustStorePath() ||
|
|
81
|
+
(!usesDefaultSourceStore && typeof sourceStore === "string"
|
|
82
|
+
? path.join(path.dirname(sourceStore), "workspace-trust-v1.json")
|
|
83
|
+
: null);
|
|
84
|
+
return store ? { storePath: store } : {};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function pluginWorkspaceRoot(plugin, options = {}) {
|
|
88
|
+
return (
|
|
89
|
+
options.workspaceRoot ||
|
|
90
|
+
plugin?.workspaceRoot ||
|
|
91
|
+
plugin?.projectRoot ||
|
|
92
|
+
plugin?.cwd ||
|
|
93
|
+
process.cwd()
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function pluginWorkspaceTrustContext(plugin, options = {}) {
|
|
98
|
+
const scope = String(plugin?.scope || "");
|
|
99
|
+
const name = String(plugin?.name || "");
|
|
100
|
+
const version = String(plugin?.version || "");
|
|
101
|
+
if (!scope || !name || !version) {
|
|
102
|
+
throw new Error("plugin trust requires scope, name, and version");
|
|
103
|
+
}
|
|
104
|
+
const identity = resolveCanonicalWorkspaceRepoIdentity(
|
|
105
|
+
pluginWorkspaceRoot(plugin, options),
|
|
106
|
+
);
|
|
107
|
+
const subject = `${scope}:${name}`;
|
|
108
|
+
const fingerprint = createHash("sha256")
|
|
109
|
+
.update(`${scope}\0${name}\0${version}`, "utf8")
|
|
110
|
+
.digest("hex");
|
|
111
|
+
return { identity, subject, fingerprint };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function workspaceTrustKey(plugin, context) {
|
|
115
|
+
const nameDigest = createHash("sha256")
|
|
116
|
+
.update(String(plugin.name), "utf8")
|
|
117
|
+
.digest("hex");
|
|
118
|
+
return `workspace-v1:${context.identity.workspaceId}:${nameDigest}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function pluginDecision(status, context, consent = "explicit") {
|
|
122
|
+
const decision = evaluateWorkspaceTrustDecision({
|
|
123
|
+
identity: context.identity,
|
|
124
|
+
evidence: [
|
|
125
|
+
{
|
|
126
|
+
source: PLUGIN_WORKSPACE_TRUST_SOURCE,
|
|
127
|
+
consent,
|
|
128
|
+
fingerprint: context.fingerprint,
|
|
129
|
+
decision:
|
|
130
|
+
status === "trusted"
|
|
131
|
+
? "allow"
|
|
132
|
+
: status === "first-use"
|
|
133
|
+
? "ask"
|
|
134
|
+
: "deny",
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
return Object.freeze({
|
|
139
|
+
status,
|
|
140
|
+
decision: decision.decision,
|
|
141
|
+
audit: projectWorkspaceTrustAudit(decision),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
50
145
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
146
|
+
* Evaluate one plugin against the shared canonical workspace trust lattice.
|
|
147
|
+
* User/local scope keeps its established local-install consent model; project
|
|
148
|
+
* scope requires both the source-specific record and the shared v1 record.
|
|
53
149
|
*/
|
|
54
|
-
export function
|
|
55
|
-
if (!plugin || !plugin.name)
|
|
56
|
-
|
|
150
|
+
export function checkPluginWorkspaceTrust(plugin, options = {}) {
|
|
151
|
+
if (!plugin || !plugin.name) {
|
|
152
|
+
return Object.freeze({
|
|
153
|
+
status: "changed",
|
|
154
|
+
decision: "deny",
|
|
155
|
+
audit: projectWorkspaceTrustAudit(null),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const scope = String(plugin.scope || "");
|
|
57
159
|
try {
|
|
58
|
-
const
|
|
59
|
-
|
|
160
|
+
const context = pluginWorkspaceTrustContext(plugin, options);
|
|
161
|
+
if (AUTO_TRUSTED_SCOPES.has(scope)) {
|
|
162
|
+
return pluginDecision("trusted", context, "scope-default");
|
|
163
|
+
}
|
|
164
|
+
if (scope !== "project") return pluginDecision("changed", context);
|
|
165
|
+
|
|
166
|
+
const store = loadTrustStore();
|
|
167
|
+
const record = store[workspaceTrustKey(plugin, context)];
|
|
168
|
+
const legacy = store[trustKey(scope, plugin.name)];
|
|
169
|
+
if (!record || typeof record !== "object" || Array.isArray(record)) {
|
|
170
|
+
// Old scope:name records have no canonical workspace identity. They
|
|
171
|
+
// remain visible to listTrust(), but require explicit re-consent.
|
|
172
|
+
const shared = checkRecordedWorkspaceTrust({
|
|
173
|
+
identity: context.identity,
|
|
174
|
+
source: PLUGIN_WORKSPACE_TRUST_SOURCE,
|
|
175
|
+
subject: context.subject,
|
|
176
|
+
evidenceFingerprint: context.fingerprint,
|
|
177
|
+
...workspaceTrustStoreOptions(options),
|
|
178
|
+
});
|
|
179
|
+
if (shared.status === "changed") {
|
|
180
|
+
return Object.freeze({
|
|
181
|
+
status: "changed",
|
|
182
|
+
decision: shared.decision,
|
|
183
|
+
audit: shared.audit,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (legacy?.version) {
|
|
187
|
+
// Match the shared migration contract: a legacy allow is downgraded
|
|
188
|
+
// to `ask`, never silently reused. `isPluginTrusted` still refuses to
|
|
189
|
+
// run it until trustPlugin writes canonical v1 consent.
|
|
190
|
+
return Object.freeze({
|
|
191
|
+
...pluginDecision("first-use", context, "legacy"),
|
|
192
|
+
status: "changed",
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return Object.freeze({
|
|
196
|
+
status: "first-use",
|
|
197
|
+
decision: shared.decision,
|
|
198
|
+
audit: shared.audit,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
const binding = record.workspaceTrust;
|
|
202
|
+
if (
|
|
203
|
+
record.version !== plugin.version ||
|
|
204
|
+
!binding ||
|
|
205
|
+
binding.workspaceId !== context.identity.workspaceId ||
|
|
206
|
+
binding.repositoryId !== context.identity.repositoryId ||
|
|
207
|
+
binding.subject !== context.subject
|
|
208
|
+
) {
|
|
209
|
+
return pluginDecision("changed", context);
|
|
210
|
+
}
|
|
211
|
+
const shared = checkRecordedWorkspaceTrust({
|
|
212
|
+
identity: context.identity,
|
|
213
|
+
source: PLUGIN_WORKSPACE_TRUST_SOURCE,
|
|
214
|
+
subject: context.subject,
|
|
215
|
+
evidenceFingerprint: context.fingerprint,
|
|
216
|
+
...workspaceTrustStoreOptions(options),
|
|
217
|
+
});
|
|
218
|
+
return Object.freeze({
|
|
219
|
+
status: shared.status === "trusted" ? "trusted" : "changed",
|
|
220
|
+
decision: shared.decision,
|
|
221
|
+
audit: shared.audit,
|
|
222
|
+
});
|
|
60
223
|
} catch {
|
|
61
|
-
|
|
62
|
-
|
|
224
|
+
return Object.freeze({
|
|
225
|
+
status: "changed",
|
|
226
|
+
decision: "deny",
|
|
227
|
+
audit: projectWorkspaceTrustAudit(null),
|
|
228
|
+
});
|
|
63
229
|
}
|
|
64
230
|
}
|
|
65
231
|
|
|
232
|
+
/**
|
|
233
|
+
* May this plugin's code-bearing components run?
|
|
234
|
+
* @param {{ scope, name, version }} plugin
|
|
235
|
+
*/
|
|
236
|
+
export function isPluginTrusted(plugin) {
|
|
237
|
+
return checkPluginWorkspaceTrust(plugin).decision === "allow";
|
|
238
|
+
}
|
|
239
|
+
|
|
66
240
|
/** Trust a plugin (records the exact version). */
|
|
67
|
-
export function trustPlugin(
|
|
241
|
+
export function trustPlugin(
|
|
242
|
+
name,
|
|
243
|
+
{ scope = "project", version, workspaceRoot, workspaceTrustStorePath } = {},
|
|
244
|
+
) {
|
|
68
245
|
if (!version) throw new Error("trustPlugin requires a version");
|
|
69
246
|
const file = _deps.storePath();
|
|
247
|
+
const plugin = { scope, name, version, workspaceRoot };
|
|
248
|
+
if (scope === "project") {
|
|
249
|
+
const context = pluginWorkspaceTrustContext(plugin, { workspaceRoot });
|
|
250
|
+
recordWorkspaceTrustConsent({
|
|
251
|
+
identity: context.identity,
|
|
252
|
+
source: PLUGIN_WORKSPACE_TRUST_SOURCE,
|
|
253
|
+
subject: context.subject,
|
|
254
|
+
evidenceFingerprint: context.fingerprint,
|
|
255
|
+
consent: "explicit",
|
|
256
|
+
...workspaceTrustStoreOptions({
|
|
257
|
+
workspaceTrustStorePath,
|
|
258
|
+
storePath: file,
|
|
259
|
+
}),
|
|
260
|
+
});
|
|
261
|
+
return mutateSecurityStore(
|
|
262
|
+
file,
|
|
263
|
+
"plugin trust",
|
|
264
|
+
(store) => {
|
|
265
|
+
store[workspaceTrustKey(plugin, context)] = {
|
|
266
|
+
name,
|
|
267
|
+
scope,
|
|
268
|
+
version,
|
|
269
|
+
trustedAt: _deps.now(),
|
|
270
|
+
workspaceTrust: {
|
|
271
|
+
schemaVersion: context.identity.schemaVersion,
|
|
272
|
+
workspaceId: context.identity.workspaceId,
|
|
273
|
+
repositoryId: context.identity.repositoryId,
|
|
274
|
+
subject: context.subject,
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
return {
|
|
278
|
+
name,
|
|
279
|
+
scope,
|
|
280
|
+
version,
|
|
281
|
+
workspaceId: context.identity.workspaceId,
|
|
282
|
+
};
|
|
283
|
+
},
|
|
284
|
+
{ lock: _deps.withFileLock },
|
|
285
|
+
);
|
|
286
|
+
}
|
|
70
287
|
return mutateSecurityStore(
|
|
71
288
|
file,
|
|
72
289
|
"plugin trust",
|
|
@@ -79,8 +296,38 @@ export function trustPlugin(name, { scope = "project", version } = {}) {
|
|
|
79
296
|
}
|
|
80
297
|
|
|
81
298
|
/** Revoke trust for a plugin at a scope. */
|
|
82
|
-
export function untrustPlugin(
|
|
299
|
+
export function untrustPlugin(
|
|
300
|
+
name,
|
|
301
|
+
{ scope = "project", workspaceRoot, workspaceTrustStorePath } = {},
|
|
302
|
+
) {
|
|
83
303
|
const file = _deps.storePath();
|
|
304
|
+
if (scope === "project") {
|
|
305
|
+
const plugin = { scope, name, version: "trust-revocation", workspaceRoot };
|
|
306
|
+
const context = pluginWorkspaceTrustContext(plugin, { workspaceRoot });
|
|
307
|
+
const result = mutateSecurityStore(
|
|
308
|
+
file,
|
|
309
|
+
"plugin trust",
|
|
310
|
+
(store) => {
|
|
311
|
+
const key = workspaceTrustKey(plugin, context);
|
|
312
|
+
const existed = Object.prototype.hasOwnProperty.call(store, key);
|
|
313
|
+
delete store[key];
|
|
314
|
+
return { name, scope, removed: existed };
|
|
315
|
+
},
|
|
316
|
+
{ lock: _deps.withFileLock },
|
|
317
|
+
);
|
|
318
|
+
// Local removal happens first, so a shared-ledger write failure cannot
|
|
319
|
+
// leave the code-bearing plugin authorized.
|
|
320
|
+
revokeRecordedWorkspaceTrust({
|
|
321
|
+
identity: context.identity,
|
|
322
|
+
source: PLUGIN_WORKSPACE_TRUST_SOURCE,
|
|
323
|
+
subject: context.subject,
|
|
324
|
+
...workspaceTrustStoreOptions({
|
|
325
|
+
workspaceTrustStorePath,
|
|
326
|
+
storePath: file,
|
|
327
|
+
}),
|
|
328
|
+
});
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
84
331
|
return mutateSecurityStore(
|
|
85
332
|
file,
|
|
86
333
|
"plugin trust",
|
|
@@ -97,12 +344,23 @@ export function untrustPlugin(name, { scope = "project" } = {}) {
|
|
|
97
344
|
/** All trust entries (for `cc plugin trust --list`). */
|
|
98
345
|
export function listTrust() {
|
|
99
346
|
return Object.entries(loadTrustStore()).map(([key, v]) => {
|
|
347
|
+
if (key.startsWith("workspace-v1:") && v?.workspaceTrust) {
|
|
348
|
+
return {
|
|
349
|
+
scope: "project",
|
|
350
|
+
name: typeof v.name === "string" ? v.name : null,
|
|
351
|
+
version: v?.version || null,
|
|
352
|
+
trustedAt: v?.trustedAt || null,
|
|
353
|
+
workspaceId: v.workspaceTrust.workspaceId || null,
|
|
354
|
+
repositoryId: v.workspaceTrust.repositoryId || null,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
100
357
|
const idx = key.indexOf(":");
|
|
101
358
|
return {
|
|
102
359
|
scope: key.slice(0, idx),
|
|
103
360
|
name: key.slice(idx + 1),
|
|
104
361
|
version: v?.version || null,
|
|
105
362
|
trustedAt: v?.trustedAt || null,
|
|
363
|
+
legacy: key.startsWith("project:"),
|
|
106
364
|
};
|
|
107
365
|
});
|
|
108
366
|
}
|
|
@@ -111,11 +369,14 @@ export function listTrust() {
|
|
|
111
369
|
* Split discovered plugins into those whose code may run and those gated out.
|
|
112
370
|
* @returns {{ trusted: any[], skipped: any[] }}
|
|
113
371
|
*/
|
|
114
|
-
export function partitionByTrust(plugins) {
|
|
372
|
+
export function partitionByTrust(plugins, options = {}) {
|
|
115
373
|
const trusted = [];
|
|
116
374
|
const skipped = [];
|
|
117
375
|
for (const p of plugins || []) {
|
|
118
|
-
(
|
|
376
|
+
(checkPluginWorkspaceTrust(p, options).decision === "allow"
|
|
377
|
+
? trusted
|
|
378
|
+
: skipped
|
|
379
|
+
).push(p);
|
|
119
380
|
}
|
|
120
381
|
return { trusted, skipped };
|
|
121
382
|
}
|