chainlesschain 0.162.182 → 0.162.184
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-DzZ5_xoj.js → AIOps-DzAl5fGx.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DeLXddIg.js → ActionButton-DAHtMGeC.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-rvDCmet1.js → Analytics-BHQaexv5.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Yt6QVDnp.js → AppLayout-Phz_pD0n.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BS112K5L.js → Artifacts-DHt47kCA.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-0wJuQrNu.js → Audit--Y_8FULl.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-B90PMgbn.js → BackgroundAgents-N1zchH7D.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-TkgMglQQ.js → Backup-B_BoyN30.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CQVS9p1l.js → BaseInput-Gl4uQHcT.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-YTjQHoC7.js → Chat-Cg-eIyAZ.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BzeUi3JH.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-5bwaOabq.js → Checkbox-D0MGR2wA.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BFmNQ_Xe.js → Codegen-BdObJEFn.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BT_u4Dzp.js → Col--ghxXT3z.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CJP52u5b.js → Community-BUFeXOqz.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BpzhJE9i.js → Compact-DhQO2_Mr.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Cg7rfslp.js → Compliance-3HoTqKYd.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-JisxL6Yy.js → Cowork-B3zmO4VW.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Bu7Ug_MC.js → Cron-CJDrZOcc.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CvuqoLdj.js → Crosschain-SMl15obd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-C2u9G8Pu.js → DID-CgTrTWrl.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BFt5Yv3d.js → Dashboard-dXFKaKol.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DIaT9Wxq.js → Dropdown-qQHEhCvI.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D_A601Y4.js → EmailListRenderer-C_ej0MWH.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard--IyQ8I8w.js → FamilyGuardDashboard-D6ajoE7I.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BihWHxuY.js → Federation-Cio2y49h.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BB8dOXc_.js → FormItemContext-ClJz1cYp.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DQYhj1HH.js → GenericCardRenderer-Dr_8Maca.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B-2QZxGf.js → Git-CevLwp5u.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CusSb-nG.js → Governance-CW9YyQUY.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BAjmvPPL.js → Inference-CQqeAyER.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DoNYWKYq.js → KnowledgeGraph-cuGVxHrw.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-B8fKWD-n.js → Logs-DIuHri5d.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BlJm1J7E.js → MarkdownRenderer-R91Qw5JA.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CyOscbSL.js → Marketplace-CY1VhFGx.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Ex6Om5da.js → McpTools-BBAbi1vt.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-CDcD8l8r.js → Memory-BWYGHxJA.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-xfBqoFDg.js → MobileBridge-CPIFkW7A.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BwObMMA2.js → MobileProjects-DQyWxCho.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CIWd36OV.js → Mtc-Bo3GnQbx.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-CPX0ouuA.js → MtcAudit-C8-c4rSW.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BDJsIUuW.js → Multisig-ChqMTtoq.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DEkbuD3N.js → NLProgramming-C48TyhvI.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DJpHRhH1.js → Notes-zogUFWxp.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C1UZ-Qd5.js → NotificationSettings-v-K9XpSB.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-P94JGGNz.js +1 -0
- package/src/assets/web-panel/assets/{Organization-Bbhj0fac.js → Organization-Boi1dQfR.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-1H04x3kx.js → Overflow-DM5YWwgA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CDpPe3et.js → P2P-C57LKpKK.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-MUX0Bpvw.js → PdhVaultBrowser-BFkPqu6Q.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-A07-0Ci1.js → Permissions-D6fB9YSp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DhhDWxAZ.js → PersonalDataHub-Bp7EefEo.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-CyfbJfe-.js → Pipeline-BVln3hZf.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CNFps3ED.js → Privacy-Bf8P3nF6.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit--T-u3Dr5.js → ProjectInit-DM6wA2E2.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DJmrK2fC.js → ProjectSettings-CGbc5Iho.js} +2 -2
- package/src/assets/web-panel/assets/Projects-D0p9EOxm.js +1 -0
- package/src/assets/web-panel/assets/{Providers-DLzzbOZd.js → Providers-B2kJcxS6.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CexOomKS.js → QrScannerModal-Cxu7dXYy.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-QklGi19_.js → QuickAsk-DTlrTPms.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bb9kwSw8.js → Recommend-Cm3X-cCj.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DZuVNNN5.js → RemoteSession-DNb2kjvt.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-Dr5cLP0N.js → Reputation-CZUFFjc1.js} +1 -1
- package/src/assets/web-panel/assets/{Row-oCQHYv5t.js → Row-DAPgloxA.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-IFDEykIR.js → RssFeed-dsx9S793.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CspVIxDc.js → Search-DnnC8kz-.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bm66o_DH.js → Security-VuZknRMK.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DvanNqXl.js → Services-B3XNX8Vx.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CHjEe9_O.js → Skeleton-Dl1nVtCj.js} +1 -1
- package/src/assets/web-panel/assets/Skills-CY09CZ_W.js +1 -0
- package/src/assets/web-panel/assets/{Sla-DxZm1491.js → Sla-HzU56n1z.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DWxEEclC.js → SpeechSettings-CRle7Jl2.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-FIvOFw5p.js → SyncSettings-DWadz7Sl.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-CNVTDGg8.js +1 -0
- package/src/assets/web-panel/assets/{Templates-CaNjzV_t.js → Templates-BBAgvrlM.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DjlP6TmH.js → Tenant-BsbbvGN8.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-uscpZI4q.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-DoZ8y7y8.js → TimelineRenderer-mTyjNE6r.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-390cg2TC.js → Tokens-Bz97oBgs.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CLrtCj5_.js → Trigger-BTRKC140.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DpESfSPn.js → Trust-B4A_KuyX.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CN6279O0.js → UkeySign-DES9TmLU.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DPtAUlSe.js → VideoEditing-ClXs7Qxp.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-nKOMV_1n.js → Wallet-IzZqFz-7.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-HuXlSOc4.js → WebAuthn-BtOP04g_.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CUfFjARi.js → WorkflowEditor-_0cqnopY.js} +1 -1
- package/src/assets/web-panel/assets/{chat-6Eq0PcZJ.js → chat-ax7193uU.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BP_Sf6Iy.js → colors-Bu4WGJah.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BufMKIh5.js → compact-item-BX1dlTW0.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BBMXxWSW.js → createContext-CktA_Dj7.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-x877iKKS.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-fUK_tRO2.js → hasIn-D5wd817l.js} +1 -1
- package/src/assets/web-panel/assets/{index-t8N1tSeX.js → index-5PYYkr8S.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLpIs_P_.js → index-A4uBPQCz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVsqvI4M.js → index-BAtBNfkr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bkg4ZePE.js → index-BLLsUNDc.js} +1 -1
- package/src/assets/web-panel/assets/{index-cpEli44X.js → index-BQtoRyTO.js} +1 -1
- package/src/assets/web-panel/assets/{index-wKTLbKyc.js → index-BRfXoRuh.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7Etk0Te.js → index-Bad2CYeH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CwSf5pnf.js → index-BcGdL3sG.js} +1 -1
- package/src/assets/web-panel/assets/{index-3FuwBewR.js → index-C3HmzCLt.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHy6yNiF.js → index-C8DkWyNr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgiECHu2.js → index-C8tLR2TG.js} +1 -1
- package/src/assets/web-panel/assets/{index-C44atv4k.js → index-CP8ELJBc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW_G-MXw.js → index-CUForzsG.js} +1 -1
- package/src/assets/web-panel/assets/{index-pum7HYBA.js → index-CYRE0NJA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWKoBU8R.js → index-CeVZaJSC.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4_MuGkd.js → index-CfzxR61U.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dyngbd29.js → index-Ci3y1ZAv.js} +19 -19
- package/src/assets/web-panel/assets/{index-fzaMtSJ8.js → index-CjEJjl5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cp8vhyZy.js → index-Cv7bVWJ5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPdUNk1i.js → index-D-o4Rwjk.js} +1 -1
- package/src/assets/web-panel/assets/index-D7pfPouB.js +1 -0
- package/src/assets/web-panel/assets/{index-CeASi4fr.js → index-D9op8poE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DASluSqY.js → index-DF1TysIF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DocdRcbd.js → index-DYo9S5OT.js} +1 -1
- package/src/assets/web-panel/assets/{index-2A5ph8b2.js → index-D_cycOET.js} +1 -1
- package/src/assets/web-panel/assets/{index-wV6iGdpY.js → index-Db3CpWTr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMLHv389.js → index-DkD1K-fF.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9WYSn4m.js → index-DmPJ6T6F.js} +1 -1
- package/src/assets/web-panel/assets/{index-uCbEN7cY.js → index-DmVUeWUk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmkMXwnR.js → index-Dzdr0zDf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZ6Yt17L.js → index-PBLzwYnu.js} +1 -1
- package/src/assets/web-panel/assets/{index-CE2DdTP2.js → index-bFCaM7Eq.js} +1 -1
- package/src/assets/web-panel/assets/index-e4i-VLBd.js +1 -0
- package/src/assets/web-panel/assets/{index-GwdRUZ-x.js → index-h3KJZr95.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2vS66eI.js → index-iHcPtQ2e.js} +1 -1
- package/src/assets/web-panel/assets/{index-6GmBa4Kg.js → index-jzlzm4VK.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9vU25ij.js → index-o14NNnxw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGjmILDm.js → index-tYPSfGbd.js} +1 -1
- package/src/assets/web-panel/assets/{index-LvCBfTLo.js → index-ytp2H5Zx.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-V5Fo95FM.js → initDefaultProps-D2Iut83B.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BYWU0hUT.js → motion-BuaJVdK9.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cr1M-J2h.js → move-CaEuM92y.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BysQKi2N.js → mtc-parser-wuKNBwys.js} +1 -1
- package/src/assets/web-panel/assets/{omit-D1zHU8GC.js → omit-7a3eklaq.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CUArxtfi.js → pickAttrs-Dn4z7KvJ.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ChoYEys0.js → placementArrow-DVn_qT4-.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BQRs3Mo7.js → responsiveObserve-Dodz1OMQ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-B8zbsCkg.js → slide-CxoGskbp.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C2U8Gs6h.js → statusUtils-DBh_epQH.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Dd8MgL_V.js → styleChecker-RXQ4gG5P.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-hknqY5NH.js → useFlexGapSupport-1Wxm0HEG.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CLTAmcfw.js → useFs-CI_ySNxJ.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-ByegHbiB.js → usePersonalDataHub-C6973rZP.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DqlAy5DP.js → vnode-CalynwKr.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DiXiztQn.js → zoom-CM1cd-Q0.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +74 -1
- package/src/commands/agent.js +9 -0
- package/src/commands/background-session.js +60 -1
- package/src/commands/batch.js +160 -5
- package/src/commands/plugin.js +318 -43
- package/src/commands/session.js +44 -1
- package/src/commands/team.js +212 -6
- package/src/data/changelog.json +26 -0
- package/src/harness/jsonl-session-store.js +59 -6
- package/src/harness/mcp-client.js +65 -43
- package/src/lib/agent-batch.js +4 -1
- package/src/lib/agent-team/team-runner.js +19 -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 +49 -0
- package/src/lib/collaboration-run-store.js +362 -0
- package/src/lib/hook-runner.cjs +98 -17
- package/src/lib/hook-runner.js +77 -6
- package/src/lib/hook-shell-command.cjs +148 -0
- package/src/lib/hooks-v2-runtime.js +188 -77
- package/src/lib/jsonl-session-store.js +2 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +18 -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 +1 -1
- package/src/lib/plugin-runtime/install.js +465 -54
- 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 +99 -0
- package/src/lib/plugin-runtime/scopes.js +13 -2
- package/src/lib/plugin-usage-attribution.js +44 -0
- package/src/lib/process-execution-broker/index.js +705 -13
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1347 -0
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +1260 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +1831 -157
- package/src/lib/session-usage.js +278 -1
- package/src/lib/stream-retry.js +29 -0
- package/src/repl/agent-repl.js +38 -9
- package/src/runtime/agent-core.js +32 -4
- package/src/runtime/headless-runner.js +46 -1
- package/src/runtime/headless-stream.js +54 -3
- package/src/runtime/mcp-config.js +15 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-PR074hUN.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-GlJtqLjT.js +0 -1
- package/src/assets/web-panel/assets/Projects-Qx_MdX9k.js +0 -1
- package/src/assets/web-panel/assets/Skills-Cb8jWjMX.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CyuLnDFR.js +0 -1
- package/src/assets/web-panel/assets/Terminal-DwzGMniO.js +0 -3
- package/src/assets/web-panel/assets/devWarning-DgVKduH2.js +0 -1
- package/src/assets/web-panel/assets/index-DOZTSoS7.js +0 -1
- package/src/assets/web-panel/assets/index-DhpvjpqI.js +0 -1
|
@@ -26,22 +26,33 @@ import {
|
|
|
26
26
|
listInstalledVersions,
|
|
27
27
|
activeVersion,
|
|
28
28
|
discoverPlugins,
|
|
29
|
+
DISABLED_FILENAME,
|
|
29
30
|
} from "./scopes.js";
|
|
30
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
enforcePluginPolicy,
|
|
33
|
+
verifyPluginManifest,
|
|
34
|
+
} from "../plugin-security.js";
|
|
31
35
|
import {
|
|
32
36
|
writePluginLock,
|
|
33
37
|
buildPluginSbom,
|
|
34
38
|
LOCK_FILENAME,
|
|
39
|
+
readPluginLock,
|
|
40
|
+
verifyInstalledSignature,
|
|
35
41
|
} from "./signature.js";
|
|
42
|
+
import { loadManagedPluginPolicy, filterByManagedPolicy } from "./policy.js";
|
|
43
|
+
import { managedSettingsPath } from "../settings-loader.cjs";
|
|
36
44
|
import executionBroker from "../process-execution-broker/index.js";
|
|
37
45
|
|
|
38
46
|
export const MAX_LISTED_PLUGIN_VERSIONS = 64;
|
|
47
|
+
export const SOURCE_METADATA_FILENAME = ".plugin-source.json";
|
|
39
48
|
|
|
40
49
|
export const _deps = {
|
|
41
50
|
existsSync: fs.existsSync,
|
|
42
51
|
mkdirSync: fs.mkdirSync,
|
|
43
52
|
rmSync: fs.rmSync,
|
|
53
|
+
renameSync: fs.renameSync,
|
|
44
54
|
readdirSync: fs.readdirSync,
|
|
55
|
+
readFileSync: fs.readFileSync,
|
|
45
56
|
copyFileSync: fs.copyFileSync,
|
|
46
57
|
lstatSync: fs.lstatSync,
|
|
47
58
|
writeFileSync: fs.writeFileSync,
|
|
@@ -49,6 +60,8 @@ export const _deps = {
|
|
|
49
60
|
spawnSync: (...args) => executionBroker.spawnSync(...args),
|
|
50
61
|
};
|
|
51
62
|
|
|
63
|
+
const pendingPluginTransactions = new WeakMap();
|
|
64
|
+
|
|
52
65
|
/**
|
|
53
66
|
* Install a plugin from a local directory into a scope's immutable version dir.
|
|
54
67
|
* @param {string} srcDir directory containing the plugin (with its manifest)
|
|
@@ -69,6 +82,17 @@ export function installFromDirectory(srcDir, opts = {}) {
|
|
|
69
82
|
);
|
|
70
83
|
}
|
|
71
84
|
const { name, version } = manifest.metadata;
|
|
85
|
+
const sourceMetadata = normalizeSourceMetadata(opts.sourceMetadata);
|
|
86
|
+
if (opts.managedPolicy) {
|
|
87
|
+
enforcePluginPolicy(
|
|
88
|
+
{
|
|
89
|
+
name,
|
|
90
|
+
source: opts.policySource || sourceMetadata?.source || null,
|
|
91
|
+
action: "install",
|
|
92
|
+
},
|
|
93
|
+
opts.managedPolicy,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
72
96
|
|
|
73
97
|
// Optional signature/integrity verification of the manifest at install time
|
|
74
98
|
// (Phase 3.3l). verifyPluginManifest THROWS on any mismatch/failure — a signed
|
|
@@ -90,55 +114,116 @@ export function installFromDirectory(srcDir, opts = {}) {
|
|
|
90
114
|
}
|
|
91
115
|
|
|
92
116
|
const dest = pluginVersionDir(scope, name, version, { cwd: opts.cwd });
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
_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
|
+
);
|
|
101
123
|
}
|
|
102
124
|
|
|
103
|
-
|
|
104
|
-
|
|
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;
|
|
105
133
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// "immutable" version dir and defeat load-time requireSignedPlugins.
|
|
110
|
-
_deps.rmSync(path.join(dest, LOCK_FILENAME), { force: true });
|
|
134
|
+
try {
|
|
135
|
+
_deps.mkdirSync(staged, { recursive: true });
|
|
136
|
+
copyDirGuarded(src, staged, staged);
|
|
111
137
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const destManifest = path.join(
|
|
117
|
-
dest,
|
|
118
|
-
path.relative(src, manifest.manifestPath),
|
|
119
|
-
);
|
|
120
|
-
writePluginLock(dest, {
|
|
121
|
-
manifestFile: destManifest,
|
|
122
|
-
sha256: verification.sha256,
|
|
123
|
-
publicKeySha256: verification.publicKeySha256,
|
|
124
|
-
signatureVerified: verification.signatureVerified === true,
|
|
125
|
-
signatureBase64: verification.signatureBase64,
|
|
126
|
-
publicKeyPem: verification.publicKeyPem,
|
|
127
|
-
sbom: buildPluginSbom(dest),
|
|
128
|
-
});
|
|
129
|
-
}
|
|
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 });
|
|
130
142
|
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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 });
|
|
170
|
+
|
|
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
|
+
}
|
|
184
|
+
|
|
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
|
+
}
|
|
193
|
+
|
|
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
|
+
}
|
|
142
227
|
}
|
|
143
228
|
|
|
144
229
|
/**
|
|
@@ -150,8 +235,21 @@ export function installFromDirectory(srcDir, opts = {}) {
|
|
|
150
235
|
*/
|
|
151
236
|
export function installFromSource(source, opts = {}) {
|
|
152
237
|
return _withMaterializedSource(source, (dir, info) => {
|
|
153
|
-
const
|
|
154
|
-
|
|
238
|
+
const sourceMetadata =
|
|
239
|
+
normalizeSourceMetadata(opts.sourceMetadata) ||
|
|
240
|
+
normalizeSourceMetadata(
|
|
241
|
+
info
|
|
242
|
+
? { type: "git", source: info.url, ref: info.ref || null }
|
|
243
|
+
: { type: "local", source: path.resolve(String(source || "")) },
|
|
244
|
+
);
|
|
245
|
+
const res = installFromDirectory(dir, { ...opts, sourceMetadata });
|
|
246
|
+
return info
|
|
247
|
+
? {
|
|
248
|
+
...res,
|
|
249
|
+
source: sourceMetadata?.source || null,
|
|
250
|
+
ref: sourceMetadata?.ref || null,
|
|
251
|
+
}
|
|
252
|
+
: res;
|
|
155
253
|
});
|
|
156
254
|
}
|
|
157
255
|
|
|
@@ -202,6 +300,13 @@ export function updatePlugin(source, opts = {}) {
|
|
|
202
300
|
);
|
|
203
301
|
}
|
|
204
302
|
const { name, version } = manifest.metadata;
|
|
303
|
+
const sourceMetadata =
|
|
304
|
+
normalizeSourceMetadata(opts.sourceMetadata) ||
|
|
305
|
+
normalizeSourceMetadata(
|
|
306
|
+
info
|
|
307
|
+
? { type: "git", source: info.url, ref: info.ref || null }
|
|
308
|
+
: { type: "local", source: path.resolve(String(source || "")) },
|
|
309
|
+
);
|
|
205
310
|
const previousVersion = getActiveVersion(name, { scope, cwd: opts.cwd });
|
|
206
311
|
const dest = pluginVersionDir(scope, name, version, { cwd: opts.cwd });
|
|
207
312
|
const sameVersionExists = _deps.existsSync(dest);
|
|
@@ -212,28 +317,105 @@ export function updatePlugin(source, opts = {}) {
|
|
|
212
317
|
if (previousVersion !== version) {
|
|
213
318
|
setActiveVersion(name, version, { scope, cwd: opts.cwd });
|
|
214
319
|
}
|
|
215
|
-
|
|
320
|
+
const result = {
|
|
216
321
|
name,
|
|
217
322
|
version,
|
|
218
323
|
previousVersion,
|
|
219
324
|
updated: previousVersion !== version,
|
|
220
325
|
reinstalled: false,
|
|
221
|
-
source:
|
|
326
|
+
source: sourceMetadata?.source || null,
|
|
327
|
+
ref: sourceMetadata?.ref || null,
|
|
222
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;
|
|
223
343
|
}
|
|
224
344
|
|
|
225
|
-
const res = installFromDirectory(dir, {
|
|
226
|
-
|
|
345
|
+
const res = installFromDirectory(dir, {
|
|
346
|
+
...opts,
|
|
347
|
+
scope,
|
|
348
|
+
force: true,
|
|
349
|
+
sourceMetadata,
|
|
350
|
+
transactional: opts.transactional === true,
|
|
351
|
+
});
|
|
352
|
+
const result = {
|
|
227
353
|
...res,
|
|
228
354
|
previousVersion,
|
|
229
355
|
updated: previousVersion !== version,
|
|
230
356
|
reinstalled: sameVersionExists,
|
|
231
|
-
source:
|
|
232
|
-
ref:
|
|
357
|
+
source: sourceMetadata?.source || null,
|
|
358
|
+
ref: sourceMetadata?.ref || null,
|
|
233
359
|
};
|
|
360
|
+
transferPendingTransaction(res, result);
|
|
361
|
+
return result;
|
|
234
362
|
});
|
|
235
363
|
}
|
|
236
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
|
+
|
|
237
419
|
/**
|
|
238
420
|
* Classify a source string into a git URL (+ optional ref), or null when it is
|
|
239
421
|
* not remote-looking. `owner/repo` expands to a GitHub HTTPS URL.
|
|
@@ -319,10 +501,26 @@ export function fetchGitRepo(url, ref) {
|
|
|
319
501
|
* invisible and impossible to inspect / uninstall / un-deny.
|
|
320
502
|
*/
|
|
321
503
|
export function listInstalled(opts = {}) {
|
|
504
|
+
let managed = null;
|
|
505
|
+
let managedError = null;
|
|
506
|
+
try {
|
|
507
|
+
managed = loadManagedPluginPolicy({
|
|
508
|
+
env: opts.env,
|
|
509
|
+
managedSettingsFile: opts.managedSettingsFile,
|
|
510
|
+
});
|
|
511
|
+
} catch (error) {
|
|
512
|
+
managedError = error;
|
|
513
|
+
}
|
|
514
|
+
const policySource = managedSettingsPath({
|
|
515
|
+
env: opts.env,
|
|
516
|
+
managedSettingsFile: opts.managedSettingsFile,
|
|
517
|
+
});
|
|
518
|
+
|
|
322
519
|
return discoverPlugins({
|
|
323
520
|
cwd: opts.cwd,
|
|
324
521
|
scopes: opts.scopes,
|
|
325
522
|
skipPolicy: true,
|
|
523
|
+
includeDisabled: true,
|
|
326
524
|
}).map((p) => {
|
|
327
525
|
const allVersions = listInstalledVersions(p.scope, p.name, {
|
|
328
526
|
cwd: opts.cwd,
|
|
@@ -335,6 +533,16 @@ export function listInstalled(opts = {}) {
|
|
|
335
533
|
) {
|
|
336
534
|
versions[versions.length - 1] = p.version;
|
|
337
535
|
}
|
|
536
|
+
const lock = readPluginLock(p.root);
|
|
537
|
+
const signature = verifyInstalledSignature(p);
|
|
538
|
+
const policyResult =
|
|
539
|
+
managed && !managedError
|
|
540
|
+
? filterByManagedPolicy([p], managed)
|
|
541
|
+
: { kept: [p], dropped: [] };
|
|
542
|
+
const policyDrop = policyResult.dropped[0] || null;
|
|
543
|
+
const sbomFiles = Array.isArray(lock?.sbom?.files)
|
|
544
|
+
? lock.sbom.files.slice(0, 100000)
|
|
545
|
+
: [];
|
|
338
546
|
return {
|
|
339
547
|
name: p.name,
|
|
340
548
|
version: p.version,
|
|
@@ -342,6 +550,45 @@ export function listInstalled(opts = {}) {
|
|
|
342
550
|
scope: p.scope,
|
|
343
551
|
dir: p.root,
|
|
344
552
|
ok: p.manifest?.ok === true,
|
|
553
|
+
enabled: p.enabled !== false,
|
|
554
|
+
source: readSourceMetadata(p.root),
|
|
555
|
+
integrity: {
|
|
556
|
+
signature: {
|
|
557
|
+
present: Boolean(lock?.signatureBase64 && lock?.publicKeyPem),
|
|
558
|
+
verified: signature.signed === true,
|
|
559
|
+
reason: signature.signed ? null : signature.reason || "unsigned",
|
|
560
|
+
manifestSha256: lock?.sha256 || null,
|
|
561
|
+
publicKeySha256:
|
|
562
|
+
signature.publicKeySha256 || lock?.publicKeySha256 || null,
|
|
563
|
+
},
|
|
564
|
+
sbom: {
|
|
565
|
+
present: Boolean(lock?.sbom),
|
|
566
|
+
digest: lock?.sbom?.digest || null,
|
|
567
|
+
fileCount: sbomFiles.length,
|
|
568
|
+
totalBytes: Math.min(
|
|
569
|
+
Number.MAX_SAFE_INTEGER,
|
|
570
|
+
sbomFiles.reduce(
|
|
571
|
+
(sum, file) =>
|
|
572
|
+
sum +
|
|
573
|
+
(Number.isFinite(Number(file?.bytes)) && Number(file.bytes) > 0
|
|
574
|
+
? Number(file.bytes)
|
|
575
|
+
: 0),
|
|
576
|
+
0,
|
|
577
|
+
),
|
|
578
|
+
),
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
policy: {
|
|
582
|
+
managed: Boolean(managed) || Boolean(managedError),
|
|
583
|
+
source: policySource,
|
|
584
|
+
allowed: !managedError && !policyDrop,
|
|
585
|
+
reason: managedError
|
|
586
|
+
? managedError.message
|
|
587
|
+
: policyDrop?.reason || (managed ? "allowed by managed policy" : ""),
|
|
588
|
+
requireSigned:
|
|
589
|
+
managed?.requireSignedPlugins === true ||
|
|
590
|
+
managed?.requireSignedPlugins === "require",
|
|
591
|
+
},
|
|
345
592
|
};
|
|
346
593
|
});
|
|
347
594
|
}
|
|
@@ -407,16 +654,120 @@ export function setActiveVersion(name, version, opts = {}) {
|
|
|
407
654
|
if (!_deps.existsSync(versionDir)) {
|
|
408
655
|
throw new Error(`${name}@${version} is not installed at ${scope} scope`);
|
|
409
656
|
}
|
|
410
|
-
const
|
|
411
|
-
|
|
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
|
+
}
|
|
412
667
|
return { name, version, scope, active: version };
|
|
413
668
|
}
|
|
414
669
|
|
|
670
|
+
/** Enable or disable one scoped plugin without deleting immutable versions. */
|
|
671
|
+
export function setPluginEnabled(name, enabled, opts = {}) {
|
|
672
|
+
const scope = opts.scope || "user";
|
|
673
|
+
const cwd = opts.cwd;
|
|
674
|
+
const nameDir = pluginNameDir(scope, name, { cwd });
|
|
675
|
+
if (
|
|
676
|
+
!_deps.existsSync(nameDir) ||
|
|
677
|
+
listInstalledVersions(scope, name, { cwd }).length === 0
|
|
678
|
+
) {
|
|
679
|
+
throw new Error(`${name} is not installed at ${scope} scope`);
|
|
680
|
+
}
|
|
681
|
+
const marker = path.join(nameDir, DISABLED_FILENAME);
|
|
682
|
+
if (enabled) {
|
|
683
|
+
_deps.rmSync(marker, { force: true });
|
|
684
|
+
} else {
|
|
685
|
+
_deps.writeFileSync(
|
|
686
|
+
marker,
|
|
687
|
+
JSON.stringify(
|
|
688
|
+
{
|
|
689
|
+
disabled: true,
|
|
690
|
+
reason: String(opts.reason || "disabled by user").slice(0, 256),
|
|
691
|
+
},
|
|
692
|
+
null,
|
|
693
|
+
2,
|
|
694
|
+
),
|
|
695
|
+
"utf8",
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
return { name, scope, enabled: Boolean(enabled) };
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/** Current lifecycle state for one scoped plugin. */
|
|
702
|
+
export function isPluginEnabled(name, opts = {}) {
|
|
703
|
+
const scope = opts.scope || "user";
|
|
704
|
+
return !_deps.existsSync(
|
|
705
|
+
path.join(pluginNameDir(scope, name, { cwd: opts.cwd }), DISABLED_FILENAME),
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
|
|
415
709
|
/** Which version is active for a plugin at a scope (or null). */
|
|
416
710
|
export function getActiveVersion(name, opts = {}) {
|
|
417
711
|
return activeVersion(opts.scope || "user", name, { cwd: opts.cwd });
|
|
418
712
|
}
|
|
419
713
|
|
|
714
|
+
function readSourceMetadata(versionDir) {
|
|
715
|
+
const file = path.join(versionDir, SOURCE_METADATA_FILENAME);
|
|
716
|
+
if (!_deps.existsSync(file)) return null;
|
|
717
|
+
try {
|
|
718
|
+
return normalizeSourceMetadata(
|
|
719
|
+
JSON.parse(_deps.readFileSync(file, "utf8")),
|
|
720
|
+
);
|
|
721
|
+
} catch {
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function normalizeSourceMetadata(value) {
|
|
727
|
+
if (!value || typeof value !== "object") return null;
|
|
728
|
+
const type = ["local", "git", "registry"].includes(value.type)
|
|
729
|
+
? value.type
|
|
730
|
+
: "git";
|
|
731
|
+
const source = sanitizeSource(value.source, type === "local");
|
|
732
|
+
if (!source) return null;
|
|
733
|
+
const metadata = {
|
|
734
|
+
version: 1,
|
|
735
|
+
type,
|
|
736
|
+
source,
|
|
737
|
+
ref: cleanBounded(value.ref, 256),
|
|
738
|
+
};
|
|
739
|
+
const registry = sanitizeSource(value.registry);
|
|
740
|
+
const resolvedSource = sanitizeSource(value.resolvedSource);
|
|
741
|
+
const packageName = cleanBounded(value.package, 256);
|
|
742
|
+
if (registry) metadata.registry = registry;
|
|
743
|
+
if (resolvedSource) metadata.resolvedSource = resolvedSource;
|
|
744
|
+
if (packageName) metadata.package = packageName;
|
|
745
|
+
if (value.offline === true) metadata.offline = true;
|
|
746
|
+
return metadata;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function sanitizeSource(value, preservePath = false) {
|
|
750
|
+
const raw = cleanBounded(value, 4096);
|
|
751
|
+
if (!raw) return null;
|
|
752
|
+
if (preservePath) return raw;
|
|
753
|
+
try {
|
|
754
|
+
const parsed = new URL(raw);
|
|
755
|
+
parsed.username = "";
|
|
756
|
+
parsed.password = "";
|
|
757
|
+
parsed.search = "";
|
|
758
|
+
parsed.hash = "";
|
|
759
|
+
return parsed.toString();
|
|
760
|
+
} catch {
|
|
761
|
+
return raw;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function cleanBounded(value, max) {
|
|
766
|
+
if (typeof value !== "string") return null;
|
|
767
|
+
const clean = value.replace(/\p{Cc}/gu, "").trim();
|
|
768
|
+
return clean ? clean.slice(0, max) : null;
|
|
769
|
+
}
|
|
770
|
+
|
|
420
771
|
// ── guarded recursive copy ────────────────────────────────────────────────
|
|
421
772
|
|
|
422
773
|
/**
|
|
@@ -437,3 +788,63 @@ function copyDirGuarded(src, dst, root) {
|
|
|
437
788
|
}
|
|
438
789
|
}
|
|
439
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
|
}
|