chainlesschain 0.162.183 → 0.162.185
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-BapS7lz4.js → AIOps-Co4ipAKx.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CXYdXEUI.js → ActionButton-BpsoDrDq.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CKR5Lexn.js → Analytics-DMUqeBik.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DATVUOXB.js → AppLayout-C6fD_dXr.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-vb_0ZlV1.js → Artifacts-DW0YlQlV.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BI1l9GWt.js → Audit-DXtHiiFK.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BFXbaUEf.js → BackgroundAgents-BgmF62U-.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DEYBJJ6H.js → Backup-D8vhVLQC.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-COdzeO0M.js → BaseInput-D7nCtY2X.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-oSN6sfM5.js → Chat-CBZLEV2G.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CI_jT0ru.js → ChatBubbleRenderer-CtDb-2Qu.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BezEa4tm.js → Checkbox-DJLkeD4M.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CNEL0Ife.js → Codegen-Cs6xO8Fd.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Bf2Y2Bn1.js → Col-Dtx9wxfT.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BfUlu4Hc.js → Community-DiigJg4n.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BN4xWP9e.js → Compact-B67oMDDL.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bt6BKmEP.js → Compliance-BBDl-Jos.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DKLp8YxO.js → Cowork-Ba98hSsa.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-o7Hhw6ky.js → Cron-DBWQZIz7.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DaNXWJIw.js → Crosschain-OcrKzxAt.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B-Qa2mha.js → DID-mW2eSbjM.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CMX0Qnxw.js → Dashboard-BmsDPmKJ.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BLuLyRu_.js → Dropdown-CVzmCyRx.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CFBAeMij.js → EmailListRenderer-vVgqKzhh.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DWzXQ67V.js → FamilyGuardDashboard-CWY5t_ZJ.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DB1QSLgr.js → Federation-ZGomhjxr.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C-OKCToP.js → FormItemContext-DyaknbLr.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DMJFtFZf.js → GenericCardRenderer-CmIvN1qr.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DsGSujOV.js → Git-DQOJIYkG.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B0iS9NEW.js → Governance-CMiY-EEp.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CH004H7c.js → Inference-D6IXuucf.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Q9MTQ83c.js → KnowledgeGraph-6jkTL0hV.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-B_fyjZK9.js → Logs-N0jD-349.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BL1IVXm-.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BIStF9d1.js → Marketplace-WhYuhBxm.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-pGfal768.js → McpTools-DTeO28xy.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-xbNqpIqB.js → Memory-MSHzPSE-.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-B5EQGrIK.js → MobileBridge-Cyqs3J3W.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-7IN2hWHg.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-a_rmfxJH.js → Mtc-MWBnezNM.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BmKuofUr.js → MtcAudit-DQu35Nu-.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BQbJ-CAN.js → Multisig-CQS_M-DP.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-JtPJKX25.js → NLProgramming-WD16vQvY.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-ZFKznTSG.js → Notes-DqsiSwOq.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-B6aHRBBR.js → NotificationSettings-D3LoxO-P.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DdcsOqEz.js → OrderTableRenderer-C5jPoTd6.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BFDTAUgS.js → Organization-48aMBJso.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-0FoTfbWH.js → Overflow-j-a2XkhR.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CWd90etw.js → P2P-BYpBNYnh.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DtCGC-W-.js → PdhVaultBrowser-CYCsrrLg.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CvQ7nr7T.js → Permissions-ZRXGXDFb.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-rdNPcR-Z.js → PersonalDataHub-6OWUPHyL.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-CmrfiNOs.js → Pipeline-FcjlDK9E.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-D7ep1h_t.js → Privacy-vD_6Tldp.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-C0xKINHS.js → ProjectInit-B_CHOLXp.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-AOmZFp-F.js → ProjectSettings-rEiqQ2da.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BWHSkXZP.js → Projects-Bg8obRAM.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Cb7JCTPL.js → Providers-Dgtt97B4.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BDMehcXC.js → QrScannerModal-DaGVylZO.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BF6_1nvx.js → QuickAsk-DP2_2x3u.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BHfT-A28.js → Recommend-D29yj4ki.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-Ue2wnBLp.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-CsWHLEcj.js → Reputation-CrxIHqeC.js} +1 -1
- package/src/assets/web-panel/assets/{Row-D6srnWhm.js → Row-BgSzJPCn.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Xhr-KyVd.js → RssFeed-AwhYOrP9.js} +3 -3
- package/src/assets/web-panel/assets/{Search-vIbIJvwL.js → Search-D2shpEcm.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bzi5Aiko.js → Security-BtKzhTbT.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BHtVX52P.js → Services-BY0DsUSm.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-C0R8B2jf.js → Skeleton-B3Sb0UI9.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DtT6rWOX.js → Skills-Ne5w0bDs.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-0B37mxVU.js → Sla-C8VkaP4T.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-0V2jE1Bs.js → SpeechSettings-BQdIEYP9.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CYedRzaj.js → SyncSettings-CwFafrUg.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-yJuKoDlR.js +1 -0
- package/src/assets/web-panel/assets/{Templates-BKoHztGT.js → Templates-DYn9qCmx.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant--17nwVNZ.js → Tenant-DkKReHvn.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-r6bd-owt.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-C5jx4AE3.js → TimelineRenderer-Mq2juUC3.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DYJKYa9t.js → Tokens-BnMw7MRi.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DChpWiQ9.js → Trigger-CTzKp4o9.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DfWG52EA.js → Trust-BBrSZm9U.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CMugYcyA.js → UkeySign-D9fDvEMq.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BOSmrHAd.js → VideoEditing-DUi5ve-0.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-D5g3k3Sa.js → Wallet-C8Fi1P8_.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-D-7u4Mkc.js → WebAuthn-s1LppYi2.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CFzZMSUJ.js → WorkflowEditor-DJL2C4lj.js} +1 -1
- package/src/assets/web-panel/assets/{chat-VbR6l6mc.js → chat-D3F2ndOB.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BSZn8EDS.js → colors-B7739-e3.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-y74obMEq.js → compact-item-CcJbFTF4.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C326bjex.js → createContext-CuP_DEFS.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Cj_36GHq.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-D_INJQT_.js → hasIn-6YEXfIik.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuIkfnTF.js → index-4h3lpWJ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBkzrpiL.js → index-BIID4XcP.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7SrC-On.js → index-BOHUo3aM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnSfnSMP.js → index-BYYSLEjn.js} +1 -1
- package/src/assets/web-panel/assets/{index-KF9Zmqln.js → index-Bh4hcNWW.js} +1 -1
- package/src/assets/web-panel/assets/{index-C327pLbw.js → index-BhIKnQAW.js} +1 -1
- package/src/assets/web-panel/assets/{index-E_v1ZCTP.js → index-Bru_kpZl.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bfw2hf2s.js → index-BsdSnHQE.js} +1 -1
- package/src/assets/web-panel/assets/{index-jYaogFE8.js → index-BxSqVMGh.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dlxh9D8D.js → index-C3ZA_h1j.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdyjrCAu.js → index-C3pctdCb.js} +1 -1
- package/src/assets/web-panel/assets/{index-abmn_8Yz.js → index-CRuoRapN.js} +1 -1
- package/src/assets/web-panel/assets/{index-qMA4GYdQ.js → index-CY0zd_jc.js} +1 -1
- package/src/assets/web-panel/assets/index-CezFrAmD.js +1 -0
- package/src/assets/web-panel/assets/index-Cxgguez6.js +1 -0
- package/src/assets/web-panel/assets/{index-Cr3uuz0C.js → index-CxradkS6.js} +19 -19
- package/src/assets/web-panel/assets/{index-DwHSZLXg.js → index-D0K_rFC1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMHqs3Jb.js → index-D0zdPyTD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bnua8IX3.js → index-D3haorUt.js} +1 -1
- package/src/assets/web-panel/assets/{index-BY8-nCgs.js → index-D6Ij_2uw.js} +1 -1
- package/src/assets/web-panel/assets/{index-FQBsuExX.js → index-DDRlBaDt.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-ZCaUJc.js → index-DGsE7MQz.js} +1 -1
- package/src/assets/web-panel/assets/{index-BjcWKg1S.js → index-DHvxXXNA.js} +1 -1
- package/src/assets/web-panel/assets/{index-GA80ir_u.js → index-DV11jpeO.js} +1 -1
- package/src/assets/web-panel/assets/{index-CsovTpx9.js → index-DXdnfDVK.js} +1 -1
- package/src/assets/web-panel/assets/{index-nSn2-C4N.js → index-D_75jYWk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJi3UqBt.js → index-DjI9MkId.js} +1 -1
- package/src/assets/web-panel/assets/{index-gINyI1rh.js → index-DlSvpNMf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfLJ42kv.js → index-Dox44j29.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2NyllnW.js → index-DpEKmjoa.js} +1 -1
- package/src/assets/web-panel/assets/{index-Iw0FfqUz.js → index-Dsy7_x4s.js} +1 -1
- package/src/assets/web-panel/assets/{index-4eyH-eC2.js → index-Gxw09e9R.js} +1 -1
- package/src/assets/web-panel/assets/{index-4PIeTM--.js → index-LfRlSmqe.js} +1 -1
- package/src/assets/web-panel/assets/{index-IZAWt0H8.js → index-Nz-MUHD1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUU2606o.js → index-b9cuvFEv.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzE53HiB.js → index-eR6H6NR7.js} +1 -1
- package/src/assets/web-panel/assets/{index-9PIVuXqo.js → index-fWgUQZ9d.js} +1 -1
- package/src/assets/web-panel/assets/{index-DkH1JvEt.js → index-mfWQRSLn.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNMxIsDg.js → index-yOZYSNmG.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DI0Zaf8-.js → initDefaultProps-CJ1Bq2L3.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BZDKSy3Z.js → motion-rYJN_vzf.js} +1 -1
- package/src/assets/web-panel/assets/{move-DsIW01Vp.js → move-DnDK2gTP.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CohkDhsf.js → mtc-parser-CKnXy7ES.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DpiN5rzg.js → omit-3nRc3hWT.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-qbuqni3y.js → pickAttrs-Ddjj3sLu.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-mBky6714.js → placementArrow-CbsbphQY.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Bas3eQ2K.js → responsiveObserve-D9aZ4VDJ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CBx_SKoK.js → slide-D4pvdFj0.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BlYguJSV.js → statusUtils-C2W-vY-S.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BCYQmRGv.js → styleChecker-Cbq5150d.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Dp5dybLM.js → useFlexGapSupport-BA2B7R82.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DHAMBgA2.js → useFs-CLrwg3xg.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-W6jsSw76.js → usePersonalDataHub-DQeAI5qs.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BkI6aayc.js → vnode-8BDGy-YU.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-C_IMGueq.js → zoom-B2SzFprl.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +74 -1
- package/src/commands/background-session.js +11 -1
- package/src/commands/batch.js +160 -5
- package/src/commands/plugin.js +90 -27
- package/src/commands/session.js +31 -1
- package/src/commands/team.js +212 -6
- package/src/data/changelog.json +26 -0
- package/src/gateways/terminal/PtyManager.js +45 -15
- package/src/gateways/ws/ws-agent-handler.js +15 -1
- package/src/gateways/ws/ws-session-gateway.js +88 -0
- package/src/harness/background-task-command-runner.js +234 -0
- package/src/harness/background-task-manager.js +150 -20
- package/src/harness/background-task-worker.js +17 -11
- package/src/harness/jsonl-session-store.js +57 -6
- package/src/harness/mcp-client.js +65 -43
- package/src/lib/agent-batch.js +4 -1
- package/src/lib/agent-team/team-worktree.js +3 -0
- package/src/lib/async-hook-supervisor.cjs +82 -10
- package/src/lib/async-hook-supervisor.js +50 -4
- package/src/lib/background-agent-supervisor.js +46 -0
- package/src/lib/collaboration-run-store.js +362 -0
- package/src/lib/event-runtime-host.js +12 -10
- package/src/lib/hook-runner.cjs +105 -18
- package/src/lib/hook-runner.js +79 -6
- package/src/lib/hook-shell-command.cjs +148 -0
- package/src/lib/hooks-v2-runtime.js +280 -81
- package/src/lib/hooks-v2-workspace-context.js +258 -0
- package/src/lib/jsonl-session-store.js +2 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +15 -0
- package/src/lib/lsp/lsp-client.js +12 -1
- package/src/lib/plugin-monitor-supervisor.js +53 -16
- package/src/lib/plugin-runtime/bin.js +21 -5
- package/src/lib/plugin-runtime/install.js +249 -57
- package/src/lib/plugin-runtime/mcp.js +21 -2
- package/src/lib/plugin-runtime/monitors.js +19 -3
- package/src/lib/plugin-runtime/sandbox-policy.js +100 -4
- package/src/lib/process-execution-broker/index.js +1151 -44
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1557 -0
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +1378 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +586 -58
- package/src/lib/session-usage.js +202 -2
- package/src/lib/stream-retry.js +29 -0
- package/src/repl/agent-repl.js +40 -3
- package/src/runtime/agent-core.js +304 -42
- package/src/runtime/headless-runner.js +48 -1
- package/src/runtime/headless-stream.js +42 -2
- package/src/workers/background-agent-worker.js +5 -11
- package/src/assets/web-panel/assets/MarkdownRenderer-C0o4FtpD.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-VTc3gT1y.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-B6EEf8hh.js +0 -5
- package/src/assets/web-panel/assets/Tasks-D-R1x8vm.js +0 -1
- package/src/assets/web-panel/assets/Terminal-Dnkos7vW.js +0 -3
- package/src/assets/web-panel/assets/devWarning-D3Bho_6u.js +0 -1
- package/src/assets/web-panel/assets/index-5iAFwYx1.js +0 -1
- package/src/assets/web-panel/assets/index-BfnB7LPx.js +0 -1
|
@@ -50,6 +50,7 @@ export const _deps = {
|
|
|
50
50
|
existsSync: fs.existsSync,
|
|
51
51
|
mkdirSync: fs.mkdirSync,
|
|
52
52
|
rmSync: fs.rmSync,
|
|
53
|
+
renameSync: fs.renameSync,
|
|
53
54
|
readdirSync: fs.readdirSync,
|
|
54
55
|
readFileSync: fs.readFileSync,
|
|
55
56
|
copyFileSync: fs.copyFileSync,
|
|
@@ -59,6 +60,8 @@ export const _deps = {
|
|
|
59
60
|
spawnSync: (...args) => executionBroker.spawnSync(...args),
|
|
60
61
|
};
|
|
61
62
|
|
|
63
|
+
const pendingPluginTransactions = new WeakMap();
|
|
64
|
+
|
|
62
65
|
/**
|
|
63
66
|
* Install a plugin from a local directory into a scope's immutable version dir.
|
|
64
67
|
* @param {string} srcDir directory containing the plugin (with its manifest)
|
|
@@ -111,66 +114,116 @@ export function installFromDirectory(srcDir, opts = {}) {
|
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
const dest = pluginVersionDir(scope, name, version, { cwd: opts.cwd });
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
_deps.rmSync(dest, { recursive: true, force: true });
|
|
117
|
+
const destExists = _deps.existsSync(dest);
|
|
118
|
+
if (destExists && !opts.force) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`${name}@${version} is already installed at ${scope} scope (immutable). ` +
|
|
121
|
+
`Use --force to reinstall, or bump the version.`,
|
|
122
|
+
);
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
const nameDir = pluginNameDir(scope, name, { cwd: opts.cwd });
|
|
126
|
+
_deps.mkdirSync(nameDir, { recursive: true });
|
|
127
|
+
const transactionRoot = _deps.mkdtempSync(path.join(nameDir, ".install-"));
|
|
128
|
+
const staged = path.join(transactionRoot, "staged");
|
|
129
|
+
const backup = destExists ? path.join(transactionRoot, "previous") : null;
|
|
130
|
+
const previousActive = getActiveVersion(name, { scope, cwd: opts.cwd });
|
|
131
|
+
let installedAtDest = false;
|
|
132
|
+
let transactionRetained = false;
|
|
126
133
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
try {
|
|
135
|
+
_deps.mkdirSync(staged, { recursive: true });
|
|
136
|
+
copyDirGuarded(src, staged, staged);
|
|
137
|
+
|
|
138
|
+
// A lock may ONLY exist if THIS installer wrote it. The source is untrusted
|
|
139
|
+
// and may ship its own `.plugin-lock.json` or provenance metadata.
|
|
140
|
+
_deps.rmSync(path.join(staged, LOCK_FILENAME), { force: true });
|
|
141
|
+
_deps.rmSync(path.join(staged, SOURCE_METADATA_FILENAME), { force: true });
|
|
142
|
+
|
|
143
|
+
if (sourceMetadata) {
|
|
144
|
+
_deps.writeFileSync(
|
|
145
|
+
path.join(staged, SOURCE_METADATA_FILENAME),
|
|
146
|
+
JSON.stringify(sourceMetadata, null, 2),
|
|
147
|
+
"utf8",
|
|
148
|
+
);
|
|
149
|
+
}
|
|
133
150
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
151
|
+
// Record the verified signature into the staged immutable version. The
|
|
152
|
+
// manifest and complete component SBOM are re-verified below before any
|
|
153
|
+
// active bytes or pointer can change.
|
|
154
|
+
if (verification) {
|
|
155
|
+
const stagedManifest = path.join(
|
|
156
|
+
staged,
|
|
157
|
+
path.relative(src, manifest.manifestPath),
|
|
158
|
+
);
|
|
159
|
+
writePluginLock(staged, {
|
|
160
|
+
manifestFile: stagedManifest,
|
|
161
|
+
sha256: verification.sha256,
|
|
162
|
+
publicKeySha256: verification.publicKeySha256,
|
|
163
|
+
signatureVerified: verification.signatureVerified === true,
|
|
164
|
+
signatureBase64: verification.signatureBase64,
|
|
165
|
+
publicKeyPem: verification.publicKeyPem,
|
|
166
|
+
sbom: buildPluginSbom(staged),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
validateStagedInstall(staged, { name, version, verification });
|
|
141
170
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
dest
|
|
148
|
-
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
signatureBase64: verification.signatureBase64,
|
|
156
|
-
publicKeyPem: verification.publicKeyPem,
|
|
157
|
-
sbom: buildPluginSbom(dest),
|
|
158
|
-
});
|
|
159
|
-
}
|
|
171
|
+
// Commit the fully validated directory with same-volume renames. A forced
|
|
172
|
+
// same-version reinstall retains the old bytes until the caller finalizes
|
|
173
|
+
// the upgrade transaction.
|
|
174
|
+
if (backup) _deps.renameSync(dest, backup);
|
|
175
|
+
try {
|
|
176
|
+
_deps.renameSync(staged, dest);
|
|
177
|
+
installedAtDest = true;
|
|
178
|
+
} catch (error) {
|
|
179
|
+
if (backup && _deps.existsSync(backup) && !_deps.existsSync(dest)) {
|
|
180
|
+
_deps.renameSync(backup, dest);
|
|
181
|
+
}
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
160
184
|
|
|
161
|
-
|
|
162
|
-
|
|
185
|
+
try {
|
|
186
|
+
setActiveVersion(name, version, { scope, cwd: opts.cwd });
|
|
187
|
+
} catch (error) {
|
|
188
|
+
restoreInstalledBytes({ dest, backup });
|
|
189
|
+
restoreActivePointer(name, previousActive, { scope, cwd: opts.cwd });
|
|
190
|
+
installedAtDest = false;
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
163
193
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
194
|
+
const result = {
|
|
195
|
+
name,
|
|
196
|
+
version,
|
|
197
|
+
scope,
|
|
198
|
+
dir: dest,
|
|
199
|
+
warnings: manifest.warnings,
|
|
200
|
+
signatureVerified: verification?.signatureVerified === true,
|
|
201
|
+
sourceMetadata,
|
|
202
|
+
enabled: isPluginEnabled(name, { scope, cwd: opts.cwd }),
|
|
203
|
+
loadValidated: true,
|
|
204
|
+
};
|
|
205
|
+
if (opts.transactional === true) {
|
|
206
|
+
pendingPluginTransactions.set(result, {
|
|
207
|
+
name,
|
|
208
|
+
version,
|
|
209
|
+
scope,
|
|
210
|
+
cwd: opts.cwd,
|
|
211
|
+
dest,
|
|
212
|
+
backup,
|
|
213
|
+
transactionRoot,
|
|
214
|
+
previousActive,
|
|
215
|
+
});
|
|
216
|
+
transactionRetained = true;
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
} finally {
|
|
220
|
+
if (!transactionRetained) {
|
|
221
|
+
if (installedAtDest && backup && _deps.existsSync(backup)) {
|
|
222
|
+
_deps.rmSync(backup, { recursive: true, force: true });
|
|
223
|
+
}
|
|
224
|
+
_deps.rmSync(transactionRoot, { recursive: true, force: true });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
174
227
|
}
|
|
175
228
|
|
|
176
229
|
/**
|
|
@@ -264,7 +317,7 @@ export function updatePlugin(source, opts = {}) {
|
|
|
264
317
|
if (previousVersion !== version) {
|
|
265
318
|
setActiveVersion(name, version, { scope, cwd: opts.cwd });
|
|
266
319
|
}
|
|
267
|
-
|
|
320
|
+
const result = {
|
|
268
321
|
name,
|
|
269
322
|
version,
|
|
270
323
|
previousVersion,
|
|
@@ -273,6 +326,20 @@ export function updatePlugin(source, opts = {}) {
|
|
|
273
326
|
source: sourceMetadata?.source || null,
|
|
274
327
|
ref: sourceMetadata?.ref || null,
|
|
275
328
|
};
|
|
329
|
+
if (opts.transactional === true && previousVersion !== version) {
|
|
330
|
+
pendingPluginTransactions.set(result, {
|
|
331
|
+
name,
|
|
332
|
+
version,
|
|
333
|
+
scope,
|
|
334
|
+
cwd: opts.cwd,
|
|
335
|
+
dest,
|
|
336
|
+
backup: null,
|
|
337
|
+
transactionRoot: null,
|
|
338
|
+
previousActive: previousVersion,
|
|
339
|
+
pointerOnly: true,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return result;
|
|
276
343
|
}
|
|
277
344
|
|
|
278
345
|
const res = installFromDirectory(dir, {
|
|
@@ -280,8 +347,9 @@ export function updatePlugin(source, opts = {}) {
|
|
|
280
347
|
scope,
|
|
281
348
|
force: true,
|
|
282
349
|
sourceMetadata,
|
|
350
|
+
transactional: opts.transactional === true,
|
|
283
351
|
});
|
|
284
|
-
|
|
352
|
+
const result = {
|
|
285
353
|
...res,
|
|
286
354
|
previousVersion,
|
|
287
355
|
updated: previousVersion !== version,
|
|
@@ -289,9 +357,65 @@ export function updatePlugin(source, opts = {}) {
|
|
|
289
357
|
source: sourceMetadata?.source || null,
|
|
290
358
|
ref: sourceMetadata?.ref || null,
|
|
291
359
|
};
|
|
360
|
+
transferPendingTransaction(res, result);
|
|
361
|
+
return result;
|
|
292
362
|
});
|
|
293
363
|
}
|
|
294
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Commit a transactional update after capability consent and command-layer
|
|
367
|
+
* validation have succeeded. Backup cleanup is best-effort: an active,
|
|
368
|
+
* validated install must not be reported as failed solely because antivirus or
|
|
369
|
+
* another reader briefly holds the hidden rollback directory open.
|
|
370
|
+
*/
|
|
371
|
+
export function finalizePluginUpdate(result) {
|
|
372
|
+
const transaction = pendingPluginTransactions.get(result);
|
|
373
|
+
if (!transaction) return { finalized: false };
|
|
374
|
+
pendingPluginTransactions.delete(result);
|
|
375
|
+
let cleanupPending = false;
|
|
376
|
+
if (transaction.transactionRoot) {
|
|
377
|
+
try {
|
|
378
|
+
_deps.rmSync(transaction.transactionRoot, {
|
|
379
|
+
recursive: true,
|
|
380
|
+
force: true,
|
|
381
|
+
});
|
|
382
|
+
} catch {
|
|
383
|
+
cleanupPending = true;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return { finalized: true, cleanupPending };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Restore the active version and, for a forced same-version reinstall, the
|
|
391
|
+
* exact prior bytes. New rejected versions are removed so `.active` fallback
|
|
392
|
+
* cannot silently select them later.
|
|
393
|
+
*/
|
|
394
|
+
export function rollbackPluginUpdate(result) {
|
|
395
|
+
const transaction = pendingPluginTransactions.get(result);
|
|
396
|
+
if (!transaction) return { rolledBack: false, version: null };
|
|
397
|
+
|
|
398
|
+
restoreActivePointer(
|
|
399
|
+
transaction.name,
|
|
400
|
+
transaction.previousActive,
|
|
401
|
+
transaction,
|
|
402
|
+
);
|
|
403
|
+
if (!transaction.pointerOnly) {
|
|
404
|
+
restoreInstalledBytes(transaction);
|
|
405
|
+
}
|
|
406
|
+
pendingPluginTransactions.delete(result);
|
|
407
|
+
if (transaction.transactionRoot) {
|
|
408
|
+
_deps.rmSync(transaction.transactionRoot, {
|
|
409
|
+
recursive: true,
|
|
410
|
+
force: true,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
rolledBack: true,
|
|
415
|
+
version: transaction.previousActive || null,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
295
419
|
/**
|
|
296
420
|
* Classify a source string into a git URL (+ optional ref), or null when it is
|
|
297
421
|
* not remote-looking. `owner/repo` expands to a GitHub HTTPS URL.
|
|
@@ -530,8 +654,16 @@ export function setActiveVersion(name, version, opts = {}) {
|
|
|
530
654
|
if (!_deps.existsSync(versionDir)) {
|
|
531
655
|
throw new Error(`${name}@${version} is not installed at ${scope} scope`);
|
|
532
656
|
}
|
|
533
|
-
const
|
|
534
|
-
|
|
657
|
+
const nameDir = pluginNameDir(scope, name, { cwd });
|
|
658
|
+
const activeFile = path.join(nameDir, ".active");
|
|
659
|
+
const tempDir = _deps.mkdtempSync(path.join(nameDir, ".active-"));
|
|
660
|
+
const tempFile = path.join(tempDir, "next");
|
|
661
|
+
try {
|
|
662
|
+
_deps.writeFileSync(tempFile, String(version), "utf8");
|
|
663
|
+
_deps.renameSync(tempFile, activeFile);
|
|
664
|
+
} finally {
|
|
665
|
+
_deps.rmSync(tempDir, { recursive: true, force: true });
|
|
666
|
+
}
|
|
535
667
|
return { name, version, scope, active: version };
|
|
536
668
|
}
|
|
537
669
|
|
|
@@ -656,3 +788,63 @@ function copyDirGuarded(src, dst, root) {
|
|
|
656
788
|
}
|
|
657
789
|
}
|
|
658
790
|
}
|
|
791
|
+
|
|
792
|
+
function validateStagedInstall(root, { name, version, verification }) {
|
|
793
|
+
const parsed = parsePluginManifest(root);
|
|
794
|
+
if (
|
|
795
|
+
!parsed.ok ||
|
|
796
|
+
parsed.metadata.name !== name ||
|
|
797
|
+
parsed.metadata.version !== version
|
|
798
|
+
) {
|
|
799
|
+
const details = parsed.errors?.length
|
|
800
|
+
? `: ${parsed.errors.join("; ")}`
|
|
801
|
+
: "";
|
|
802
|
+
throw new Error(`staged plugin failed load validation${details}`);
|
|
803
|
+
}
|
|
804
|
+
if (verification) {
|
|
805
|
+
const signature = verifyInstalledSignature({ root });
|
|
806
|
+
if (!signature.signed) {
|
|
807
|
+
throw new Error(
|
|
808
|
+
`staged plugin failed signature/SBOM validation: ${signature.reason || "unknown error"}`,
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function transferPendingTransaction(from, to) {
|
|
815
|
+
const transaction = pendingPluginTransactions.get(from);
|
|
816
|
+
if (!transaction) return;
|
|
817
|
+
pendingPluginTransactions.delete(from);
|
|
818
|
+
pendingPluginTransactions.set(to, transaction);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function restoreActivePointer(name, version, opts = {}) {
|
|
822
|
+
if (version) {
|
|
823
|
+
setActiveVersion(name, version, opts);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
_deps.rmSync(
|
|
827
|
+
path.join(
|
|
828
|
+
pluginNameDir(opts.scope || "user", name, { cwd: opts.cwd }),
|
|
829
|
+
".active",
|
|
830
|
+
),
|
|
831
|
+
{ force: true },
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function restoreInstalledBytes({ dest, backup, transactionRoot }) {
|
|
836
|
+
if (!backup) {
|
|
837
|
+
_deps.rmSync(dest, { recursive: true, force: true });
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
const rejected = path.join(transactionRoot, "rejected");
|
|
841
|
+
_deps.renameSync(dest, rejected);
|
|
842
|
+
try {
|
|
843
|
+
_deps.renameSync(backup, dest);
|
|
844
|
+
} catch (error) {
|
|
845
|
+
if (_deps.existsSync(rejected) && !_deps.existsSync(dest)) {
|
|
846
|
+
_deps.renameSync(rejected, dest);
|
|
847
|
+
}
|
|
848
|
+
throw error;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
@@ -21,7 +21,10 @@ import {
|
|
|
21
21
|
componentCapabilityDenial,
|
|
22
22
|
networkTargetDenial,
|
|
23
23
|
} from "./capabilities.js";
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
issuePluginWorkspaceAuthority,
|
|
26
|
+
mergePluginSandboxPolicies,
|
|
27
|
+
} from "./sandbox-policy.js";
|
|
25
28
|
|
|
26
29
|
export const _deps = { readFileSync: fs.readFileSync };
|
|
27
30
|
|
|
@@ -89,6 +92,21 @@ export function collectPluginMcpServers(opts = {}) {
|
|
|
89
92
|
if (!p.manifest || p.manifest.ok !== true) continue;
|
|
90
93
|
const m = p.manifest.components?.mcp;
|
|
91
94
|
if (!m) continue;
|
|
95
|
+
const pluginSource = m.absPath || p.manifest.manifestPath;
|
|
96
|
+
let pluginWorkspaceAuthority;
|
|
97
|
+
try {
|
|
98
|
+
pluginWorkspaceAuthority = issuePluginWorkspaceAuthority({
|
|
99
|
+
root: p.root,
|
|
100
|
+
origin: "plugin:mcp",
|
|
101
|
+
pluginId: p.name,
|
|
102
|
+
pluginVersion: p.version,
|
|
103
|
+
pluginSource,
|
|
104
|
+
});
|
|
105
|
+
} catch {
|
|
106
|
+
// The discovery root disappeared or stopped resolving safely. Refuse the
|
|
107
|
+
// component instead of copying an attacker-controlled path downstream.
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
92
110
|
// Component-level capability gate: a plugin that declared a permissions
|
|
93
111
|
// block but under-declared the `mcp` capability its server needs gets its
|
|
94
112
|
// MCP component refused here (mirrors the hooks/monitors/bin/lsp denial).
|
|
@@ -150,7 +168,8 @@ export function collectPluginMcpServers(opts = {}) {
|
|
|
150
168
|
policy: "allow",
|
|
151
169
|
pluginId: p.name,
|
|
152
170
|
pluginVersion: p.version,
|
|
153
|
-
pluginSource
|
|
171
|
+
pluginSource,
|
|
172
|
+
pluginWorkspaceAuthority,
|
|
154
173
|
};
|
|
155
174
|
added++;
|
|
156
175
|
}
|
|
@@ -22,7 +22,10 @@ import fs from "fs";
|
|
|
22
22
|
import { discoverPlugins } from "./scopes.js";
|
|
23
23
|
import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
|
|
24
24
|
import { componentCapabilityDenial } from "./capabilities.js";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
issuePluginWorkspaceAuthority,
|
|
27
|
+
mergePluginSandboxPolicies,
|
|
28
|
+
} from "./sandbox-policy.js";
|
|
26
29
|
|
|
27
30
|
export const _deps = { readFileSync: fs.readFileSync };
|
|
28
31
|
|
|
@@ -55,7 +58,7 @@ export function _resetMonitorWarnings() {
|
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
/** Normalize one raw monitor entry into a validated descriptor, or null. */
|
|
58
|
-
function normalizeMonitor(plugin, raw) {
|
|
61
|
+
function normalizeMonitor(plugin, raw, pluginWorkspaceAuthority) {
|
|
59
62
|
if (!raw || typeof raw !== "object") return null;
|
|
60
63
|
if (typeof raw.name !== "string" || raw.name === "") return null;
|
|
61
64
|
if (typeof raw.command !== "string" || raw.command === "") return null;
|
|
@@ -91,6 +94,7 @@ function normalizeMonitor(plugin, raw) {
|
|
|
91
94
|
intervalMs,
|
|
92
95
|
env,
|
|
93
96
|
cwd: plugin.root,
|
|
97
|
+
pluginWorkspaceAuthority,
|
|
94
98
|
origin: "plugin:monitor",
|
|
95
99
|
pluginId: plugin.name,
|
|
96
100
|
pluginVersion: plugin.version,
|
|
@@ -131,6 +135,18 @@ export function collectPluginMonitors(opts = {}) {
|
|
|
131
135
|
denied.push({ name: p.name, reason: denial.reason });
|
|
132
136
|
continue;
|
|
133
137
|
}
|
|
138
|
+
let pluginWorkspaceAuthority;
|
|
139
|
+
try {
|
|
140
|
+
pluginWorkspaceAuthority = issuePluginWorkspaceAuthority({
|
|
141
|
+
root: p.root,
|
|
142
|
+
origin: "plugin:monitor",
|
|
143
|
+
pluginId: p.name,
|
|
144
|
+
pluginVersion: p.version,
|
|
145
|
+
pluginSource: p.manifest?.manifestPath || "",
|
|
146
|
+
});
|
|
147
|
+
} catch {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
134
150
|
let parsed;
|
|
135
151
|
if (m.absPath) {
|
|
136
152
|
try {
|
|
@@ -159,7 +175,7 @@ export function collectPluginMonitors(opts = {}) {
|
|
|
159
175
|
? parsed
|
|
160
176
|
: [];
|
|
161
177
|
for (const raw of list) {
|
|
162
|
-
const desc = normalizeMonitor(p, raw);
|
|
178
|
+
const desc = normalizeMonitor(p, raw, pluginWorkspaceAuthority);
|
|
163
179
|
if (desc) out.push(desc);
|
|
164
180
|
}
|
|
165
181
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Normalize the small, declarative sandbox contract exposed to plugins.
|
|
3
6
|
*
|
|
@@ -14,6 +17,8 @@ export const PLUGIN_SANDBOX_BOUNDARIES = Object.freeze([
|
|
|
14
17
|
]);
|
|
15
18
|
|
|
16
19
|
const SUPPORTED_BOUNDARIES = new Set(PLUGIN_SANDBOX_BOUNDARIES);
|
|
20
|
+
const PLUGIN_WORKSPACE_AUTHORITY_KIND = "trusted-plugin-workspace";
|
|
21
|
+
const pluginWorkspaceAuthorities = new WeakMap();
|
|
17
22
|
|
|
18
23
|
function invalidPolicy(message) {
|
|
19
24
|
const error = new Error(message);
|
|
@@ -21,6 +26,100 @@ function invalidPolicy(message) {
|
|
|
21
26
|
return error;
|
|
22
27
|
}
|
|
23
28
|
|
|
29
|
+
function normalizedProvenance(value = {}) {
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
origin: typeof value.origin === "string" ? value.origin : "",
|
|
32
|
+
pluginId: typeof value.pluginId === "string" ? value.pluginId : "",
|
|
33
|
+
pluginVersion:
|
|
34
|
+
typeof value.pluginVersion === "string" ? value.pluginVersion : "",
|
|
35
|
+
pluginSource:
|
|
36
|
+
typeof value.pluginSource === "string" ? value.pluginSource : "",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sameProvenance(left, right) {
|
|
41
|
+
return (
|
|
42
|
+
left.origin === right.origin &&
|
|
43
|
+
left.pluginId === right.pluginId &&
|
|
44
|
+
left.pluginVersion === right.pluginVersion &&
|
|
45
|
+
left.pluginSource === right.pluginSource
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Mint an opaque, reusable authority from a plugin root produced by trusted
|
|
51
|
+
* installed-plugin discovery. Manifest/server/monitor data receives only the
|
|
52
|
+
* token: the canonical writable root stays in this module's WeakMap.
|
|
53
|
+
*
|
|
54
|
+
* A token is reusable because MCP reconnects and interval monitors can launch
|
|
55
|
+
* more than once. The process broker still mints a separate one-shot execution
|
|
56
|
+
* contract for every child.
|
|
57
|
+
*/
|
|
58
|
+
export function issuePluginWorkspaceAuthority(value = {}) {
|
|
59
|
+
if (
|
|
60
|
+
typeof value.root !== "string" ||
|
|
61
|
+
value.root.length === 0 ||
|
|
62
|
+
value.root.includes("\0")
|
|
63
|
+
) {
|
|
64
|
+
throw invalidPolicy("trusted plugin workspace root must be a path");
|
|
65
|
+
}
|
|
66
|
+
const requestedRoot = path.resolve(value.root);
|
|
67
|
+
let canonicalRoot;
|
|
68
|
+
let stat;
|
|
69
|
+
try {
|
|
70
|
+
const implementation = fs.realpathSync.native || fs.realpathSync;
|
|
71
|
+
canonicalRoot = implementation.call(fs.realpathSync, requestedRoot);
|
|
72
|
+
stat = fs.statSync(canonicalRoot);
|
|
73
|
+
} catch {
|
|
74
|
+
throw invalidPolicy("trusted plugin workspace root is unavailable");
|
|
75
|
+
}
|
|
76
|
+
if (!stat.isDirectory()) {
|
|
77
|
+
throw invalidPolicy("trusted plugin workspace root must be a directory");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const provenance = normalizedProvenance(value);
|
|
81
|
+
if (
|
|
82
|
+
!provenance.origin.startsWith("plugin:") ||
|
|
83
|
+
!provenance.pluginId ||
|
|
84
|
+
!provenance.pluginVersion
|
|
85
|
+
) {
|
|
86
|
+
throw invalidPolicy("trusted plugin workspace provenance is incomplete");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const authority = Object.freeze({
|
|
90
|
+
contractVersion: 1,
|
|
91
|
+
kind: PLUGIN_WORKSPACE_AUTHORITY_KIND,
|
|
92
|
+
});
|
|
93
|
+
pluginWorkspaceAuthorities.set(
|
|
94
|
+
authority,
|
|
95
|
+
Object.freeze({ root: canonicalRoot, provenance }),
|
|
96
|
+
);
|
|
97
|
+
return authority;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolve an authority only for the exact plugin/component provenance to which
|
|
102
|
+
* the loader bound it. Structurally similar manifest objects are not accepted.
|
|
103
|
+
*/
|
|
104
|
+
export function resolvePluginWorkspaceAuthority(authority, provenance = {}) {
|
|
105
|
+
if (
|
|
106
|
+
!authority ||
|
|
107
|
+
typeof authority !== "object" ||
|
|
108
|
+
authority.contractVersion !== 1 ||
|
|
109
|
+
authority.kind !== PLUGIN_WORKSPACE_AUTHORITY_KIND
|
|
110
|
+
) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const binding = pluginWorkspaceAuthorities.get(authority);
|
|
114
|
+
if (
|
|
115
|
+
!binding ||
|
|
116
|
+
!sameProvenance(binding.provenance, normalizedProvenance(provenance))
|
|
117
|
+
) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return binding.root;
|
|
121
|
+
}
|
|
122
|
+
|
|
24
123
|
/**
|
|
25
124
|
* @param {unknown} raw
|
|
26
125
|
* @param {{ label?: string }} [opts]
|
|
@@ -49,10 +148,7 @@ export function normalizePluginSandboxPolicy(raw, opts = {}) {
|
|
|
49
148
|
|
|
50
149
|
const requiredBoundaries = [];
|
|
51
150
|
for (const boundary of boundaries) {
|
|
52
|
-
if (
|
|
53
|
-
typeof boundary !== "string" ||
|
|
54
|
-
!SUPPORTED_BOUNDARIES.has(boundary)
|
|
55
|
-
) {
|
|
151
|
+
if (typeof boundary !== "string" || !SUPPORTED_BOUNDARIES.has(boundary)) {
|
|
56
152
|
throw invalidPolicy(
|
|
57
153
|
`${label}.requiredBoundaries contains unsupported boundary: ${String(boundary)}`,
|
|
58
154
|
);
|