chainlesschain 0.162.148 → 0.162.149
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/README.md +1 -1
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-DwpCCp64.js → AIOps-BJif14yR.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-C9pBYocA.js → ActionButton-CXek6gJY.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BWAkeXxK.js → Analytics-BgQhdAJi.js} +2 -2
- package/src/assets/web-panel/assets/{AppLayout-CivFGH6B.js → AppLayout-DTYl5NtR.js} +3 -3
- package/src/assets/web-panel/assets/{Audit-Cm8hRpZm.js → Audit-BzATQAeR.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DXdFMsE8.js → Backup-BzdPEQhL.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-uAwSTeql.js → BaseInput-BM0KVh8E.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-KU8eeJJE.js → Chat-D7fHXHkz.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Wm34RR-b.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-C6AMDkjd.js → Checkbox-CAiSQ9vO.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-t2moDxcO.js → Codegen-Df40CrEe.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DCcsRRhN.js → Col-DIT2fNFU.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DtB-8SAC.js → Community-CSTIbW2k.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CZm8THYA.js → Compact-DtqXZZUi.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-LiQgC7g1.js → Compliance-CUtzw6o7.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CA8SAxht.js → Cowork-CezmlnmU.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-CtRaF1wD.js → Cron-P4BITarg.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-6-br6Hub.js → Crosschain-BRRiYwvq.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Do2EccrL.js → DID-L5ijB9i4.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-wrXcbzGe.js → Dashboard-CFxro4ob.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-p3FsT245.js → Dropdown-BmAFCQ71.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D95A2L8q.js → EmailListRenderer-DJBCEuon.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-cLuJW2zo.js → FamilyGuardDashboard-z4oLq6eT.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Brgq_cbN.js → Federation-DsVCpHMF.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DWLCkNgh.js → FormItemContext-DP3bP5th.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BBqUfQd6.js → GenericCardRenderer-BAXP2Gc6.js} +1 -1
- package/src/assets/web-panel/assets/{Git-8F090w4I.js → Git-I3g_bAw9.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CCogEbxb.js → Governance-jHS5-ioS.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CfLD7v7C.js → Inference-CPL7sfx9.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DDuHJewd.js → KnowledgeGraph-Jf236h4C.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CE8KSEVC.js → Logs-Cm2tcSKg.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-DmwpZmhT.js → Marketplace-CZK82rhi.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Bf7AazU8.js → McpTools-CZCeji7a.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-D0QU_00S.js → Memory-3l2KVS9k.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-yXS9xdhx.js → MobileBridge-VYdpoLh6.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BuDUoGUP.js → MobileProjects-CiB9cmm1.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BYyHy28p.js → Mtc-wCFZbBuL.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-CK0aqpiZ.js → MtcAudit-1a3THIyG.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-mNimKYeb.js → Multisig-DMzLB2hG.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-AioAJ0YA.js → NLProgramming-D9wZbf6l.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CVVNCLHE.js → Notes-hrFe2ZM-.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C4ABj-TK.js → NotificationSettings-CHGX5Jwm.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CRIFE2Tj.js +1 -0
- package/src/assets/web-panel/assets/{Organization-uozl_gWK.js → Organization-By7FkhtY.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BCZOM2hK.js → Overflow--khGSzJn.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CArcaiaj.js → P2P-CAG9dH35.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Sgq2Srr8.js → PdhVaultBrowser-B3SQZmK9.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYrQrXnH.js → Permissions-D35ec6JA.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BzHStAbz.js → PersonalDataHub-CxBF7hW_.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-k7KqxX1M.js → Pipeline-s6EtOO1s.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-IumTUWqx.js → Privacy-BbJYmWmO.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BRyImYTf.js → ProjectInit-VGQq1jMT.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BFIqTBFk.js → ProjectSettings-B1ew3Teh.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CGcNIs_g.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CyyFnUPs.js → Providers-mrO47FIK.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C82cRx-X.js → QrScannerModal-IJF2mHyw.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DGxDF521.js → QuickAsk-CU0wN_Z0.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Cns-Am1y.js → Recommend-DhN37R6G.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-R7OqAIlg.js → RemoteSession-D_xOX_hu.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-D7mgPIYV.js → Reputation-D_Ssv7uH.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B3lEURyd.js → Row-Kx5dKxX7.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-9_UVrpBt.js → RssFeed-BlGwqZkY.js} +2 -2
- package/src/assets/web-panel/assets/{Search-ClZeO80q.js → Search-hLFQzxpb.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BNNJczEp.js → Security-DMlvsVt4.js} +3 -3
- package/src/assets/web-panel/assets/{Services-C5SjrWUj.js → Services-Bd0Qsj2m.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Ci_obQ-g.js → Skeleton-DFclq9X3.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DdebN15P.js → Skills-BNtln2qY.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-OinZP2vi.js → Sla-Dch5H19G.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CxzbNF51.js → SpeechSettings-2UfQcU1g.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D06N_66b.js → SyncSettings-CD9YQr4i.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BdEdW0AZ.js → Tasks-BNu-7MzI.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-F1ctKmPa.js → Templates-D1n9CaIR.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CVz1Urot.js → Tenant-DenL3iBW.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-ZzU0Io1Y.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-Doitzjmf.js → TimelineRenderer-D87IfukO.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BpyVRpVA.js → Tokens-BkiZc8E3.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-nWhWYTbU.js → Trigger-BNRSytGN.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DK_G-wLx.js → Trust-Bwbd4X2m.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B5yBUojO.js → UkeySign-B8O2bs0o.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C5D51qAe.js → VideoEditing-C2fL8zmH.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CLbL9K7v.js → Wallet-BORYDdE1.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DZUelI3V.js → WebAuthn-C0V5S2FM.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-8RPxt4KW.js → WorkflowEditor-2dR0fcHL.js} +1 -1
- package/src/assets/web-panel/assets/{chat-4N4tOA3d.js → chat-BmuAFAn8.js} +1 -1
- package/src/assets/web-panel/assets/{colors-zbbGVrua.js → colors-CN3smMcK.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-D7iMkbnE.js → compact-item-DCnxb4kW.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CBzPJv-w.js → createContext-vlR43pHn.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DxwmsL4j.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-bHdrQSqZ.js → hasIn-D04tHYFd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DbE5D0WL.js → index-0DnaZGkC.js} +1 -1
- package/src/assets/web-panel/assets/{index-308gCGh7.js → index-430S68MN.js} +1 -1
- package/src/assets/web-panel/assets/{index-kmh1TxRa.js → index-B78xL3s2.js} +1 -1
- package/src/assets/web-panel/assets/{index-QFObYeo1.js → index-BTPEZTk1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZb8F8YG.js → index-BXRg8GFQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE9j5YgT.js → index-BasvsWDM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiG1IZao.js → index-BeGDEXFs.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bqmx24kh.js → index-BqhASEL4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCr0geV9.js → index-BudvKQfG.js} +1 -1
- package/src/assets/web-panel/assets/{index-S74FpAIn.js → index-C-6NO5il.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyoNgbXl.js → index-C5Sxb1sE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtbbdLnf.js → index-CP9m9Ggr.js} +3 -3
- package/src/assets/web-panel/assets/{index-CkxK86vf.js → index-CTOCnIQ7.js} +1 -1
- package/src/assets/web-panel/assets/index-Cg2ldRfU.js +1 -0
- package/src/assets/web-panel/assets/{index-BTIjjXry.js → index-ClhAHDK3.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7b1hGry.js → index-CnXebZLL.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPxkoKgA.js → index-CngCC8hC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ci009ijp.js → index-Cr-A0FYJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdmMoYN1.js → index-CsAsCPJ6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBNr77E1.js → index-D28DeAAB.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1s0Bz9O.js → index-D5LZVZ0L.js} +1 -1
- package/src/assets/web-panel/assets/{index-oDKNgCsP.js → index-D65_XseV.js} +1 -1
- package/src/assets/web-panel/assets/{index-QNAG4JtR.js → index-D8vwp4qp.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQfu1U78.js → index-DDwLgQY9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnBOpqv3.js → index-DIpY0qM5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQ6NcfWz.js → index-DTWg-18U.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4XKpj6c.js → index-DTtRscUa.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1YDh7Wr.js → index-DYAtmqiv.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvRMA9uT.js → index-D_n8_vfI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CahryTX0.js → index-DbADHwhr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJ4ZGyW0.js → index-DbVsQBOH.js} +1 -1
- package/src/assets/web-panel/assets/index-Dwzo-dtJ.js +1 -0
- package/src/assets/web-panel/assets/{index-xYCm6W2h.js → index-HhNjnCfe.js} +1 -1
- package/src/assets/web-panel/assets/{index-BS4_Mwk6.js → index-ToUh2b1-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DISWAYce.js → index-TslMTwNy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmqlJed-.js → index-_FIkN3jd.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqdPyWm5.js → index-hpvvtAZ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsKehmmS.js → index-jzR7Ujuw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUyjUkY7.js → index-ljuBiQW9.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BD55yNBt.js → initDefaultProps-C3wUb4je.js} +1 -1
- package/src/assets/web-panel/assets/{motion-B5Bbe77U.js → motion-D-jYFUNA.js} +1 -1
- package/src/assets/web-panel/assets/{move-CTvIqHEH.js → move-tqb8nwJ2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BkNyPQKB.js → mtc-parser-DSOjMJMz.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BaXJXgHA.js → omit-CJ7VimIW.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Bw6-YTxH.js → pickAttrs-qXiG1iT3.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BLdbkLqJ.js → placementArrow-CkGBcy1Z.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-IVYkzntU.js → responsiveObserve-D3WvTlLO.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DFMFwwtY.js → slide-BEE2ftge.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CURYRtZ1.js → statusUtils-CsfBpxiG.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cfz63s1r.js → styleChecker-xPrIiJPI.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-COJL0KE3.js → useFlexGapSupport-C5Yd-SCR.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-YLLojQQf.js → useFs-D_MDS8HI.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BF_21qUC.js → usePersonalDataHub-BqJh-usA.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-9ZpkA7bb.js → vnode-chm_p-gz.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DhtAfhl8.js → zoom-3-YDNz0Z.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/assets/web-panel/remote-session-sw.js +43 -0
- package/src/command-manifest.json +22 -1
- package/src/commands/agent.js +44 -5
- package/src/commands/codeintel.js +252 -0
- package/src/commands/eval.js +248 -0
- package/src/commands/plugin.js +354 -0
- package/src/commands/team.js +456 -0
- package/src/gateways/ws/message-dispatcher.js +45 -2
- package/src/gateways/ws/remote-session-protocol.js +37 -0
- package/src/gateways/ws/ws-server.js +1 -1
- package/src/harness/prompt-compressor.js +27 -1
- package/src/harness/remote-command-ledger.js +158 -0
- package/src/harness/remote-session-push-apns.js +246 -0
- package/src/harness/remote-session-push-errors.js +15 -0
- package/src/harness/remote-session-push-fcm.js +9 -25
- package/src/harness/remote-session-push-huawei.js +174 -0
- package/src/harness/remote-session-push-oppo.js +171 -0
- package/src/harness/remote-session-push-senders.js +42 -0
- package/src/harness/remote-session-push-vivo.js +179 -0
- package/src/harness/remote-session-push-web.js +299 -0
- package/src/harness/remote-session-push-xiaomi.js +99 -0
- package/src/index.js +6 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/agent-sandbox.js +161 -5
- package/src/lib/agent-team/task-lease.js +434 -0
- package/src/lib/agent-team/team-budget.js +165 -0
- package/src/lib/agent-team/team-mailbox.js +106 -0
- package/src/lib/agent-team/team-runner.js +266 -0
- package/src/lib/agent-team/team-worktree.js +204 -0
- package/src/lib/agents.js +18 -0
- package/src/lib/async-hook-supervisor.cjs +305 -0
- package/src/lib/eval/runner.js +163 -0
- package/src/lib/eval/tasks.js +456 -0
- package/src/lib/eval/trend.js +185 -0
- package/src/lib/lsp/__tests__/code-intelligence.integration.test.js +129 -0
- package/src/lib/lsp/__tests__/code-intelligence.test.js +228 -0
- package/src/lib/lsp/__tests__/jsonrpc-stream.test.js +104 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +258 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +264 -0
- package/src/lib/lsp/__tests__/lsp-server-registry.test.js +128 -0
- package/src/lib/lsp/code-intelligence.js +356 -0
- package/src/lib/lsp/jsonrpc-stream.js +139 -0
- package/src/lib/lsp/lsp-client.js +318 -0
- package/src/lib/lsp/lsp-manager.js +323 -0
- package/src/lib/lsp/lsp-server-registry.js +196 -0
- package/src/lib/plugin-monitor-supervisor.js +238 -0
- package/src/lib/plugin-runtime/agents.js +51 -0
- package/src/lib/plugin-runtime/bin.js +100 -0
- package/src/lib/plugin-runtime/hooks.js +100 -0
- package/src/lib/plugin-runtime/install.js +388 -0
- package/src/lib/plugin-runtime/lsp.js +75 -0
- package/src/lib/plugin-runtime/manifest.js +458 -0
- package/src/lib/plugin-runtime/mcp.js +89 -0
- package/src/lib/plugin-runtime/monitors.js +100 -0
- package/src/lib/plugin-runtime/policy.js +144 -0
- package/src/lib/plugin-runtime/reload.js +79 -0
- package/src/lib/plugin-runtime/scopes.js +197 -0
- package/src/lib/plugin-runtime/settings.js +119 -0
- package/src/lib/plugin-runtime/signature.js +107 -0
- package/src/lib/plugin-runtime/skills.js +43 -0
- package/src/lib/plugin-runtime/trust.js +140 -0
- package/src/lib/sandbox-egress-proxy.js +162 -0
- package/src/lib/sandbox-network-policy.js +134 -0
- package/src/lib/sandbox-v2.js +10 -4
- package/src/lib/settings-hook-events.cjs +78 -6
- package/src/lib/settings-hooks.cjs +29 -3
- package/src/lib/settings-loader.cjs +35 -1
- package/src/lib/skill-loader.js +18 -0
- package/src/lib/telemetry/span-recorder.js +237 -0
- package/src/repl/agent-repl.js +400 -4
- package/src/runtime/agent-core.js +546 -52
- package/src/runtime/coding-agent-contract-shared.cjs +82 -8
- package/src/runtime/coding-agent-policy.cjs +32 -7
- package/src/runtime/fallback-model.js +134 -7
- package/src/runtime/headless-runner.js +353 -108
- package/src/runtime/mcp-config.js +46 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DCNfbdlx.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-DIp8Xcbd.js +0 -1
- package/src/assets/web-panel/assets/Projects-Dpid9CDg.js +0 -1
- package/src/assets/web-panel/assets/Terminal-BvAK_Zel.js +0 -3
- package/src/assets/web-panel/assets/devWarning-C2Z5LRgq.js +0 -1
- package/src/assets/web-panel/assets/index-BGp6Yr-Y.js +0 -1
- package/src/assets/web-panel/assets/index-BXiw9dQf.js +0 -1
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin install lifecycle (Phase 3.3d) — put a plugin ONTO disk in an immutable
|
|
3
|
+
* scope version directory, so the component wirings (skills / lsp / hooks) built
|
|
4
|
+
* in 3.2–3.3c actually reach the agent for a real user instead of only for
|
|
5
|
+
* hand-placed fixtures.
|
|
6
|
+
*
|
|
7
|
+
* installFromDirectory(src, {scope}) → validate manifest → copy into
|
|
8
|
+
* <scopeRoot>/<encodedName>/<version>/ → mark that version active
|
|
9
|
+
* listInstalled({cwd}) → discover across scopes
|
|
10
|
+
* uninstall(name, {scope, version}) → remove a version (or the whole plugin)
|
|
11
|
+
* setActiveVersion(name, version) → repoint `.active` (rollback)
|
|
12
|
+
*
|
|
13
|
+
* Version directories are immutable: re-installing the same version refuses
|
|
14
|
+
* unless `force`, so an in-flight session keeps running its bytes. The copy
|
|
15
|
+
* skips symlinks (a symlink could later resolve outside the sandbox) and only
|
|
16
|
+
* writes inside the destination version dir.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import fs from "fs";
|
|
20
|
+
import os from "os";
|
|
21
|
+
import path from "path";
|
|
22
|
+
import { spawnSync } from "child_process";
|
|
23
|
+
import { parsePluginManifest, isWithin } from "./manifest.js";
|
|
24
|
+
import {
|
|
25
|
+
pluginNameDir,
|
|
26
|
+
pluginVersionDir,
|
|
27
|
+
listInstalledVersions,
|
|
28
|
+
activeVersion,
|
|
29
|
+
discoverPlugins,
|
|
30
|
+
} from "./scopes.js";
|
|
31
|
+
import { verifyPluginManifest } from "../plugin-security.js";
|
|
32
|
+
import { writePluginLock } from "./signature.js";
|
|
33
|
+
|
|
34
|
+
export const _deps = {
|
|
35
|
+
existsSync: fs.existsSync,
|
|
36
|
+
mkdirSync: fs.mkdirSync,
|
|
37
|
+
rmSync: fs.rmSync,
|
|
38
|
+
readdirSync: fs.readdirSync,
|
|
39
|
+
copyFileSync: fs.copyFileSync,
|
|
40
|
+
lstatSync: fs.lstatSync,
|
|
41
|
+
writeFileSync: fs.writeFileSync,
|
|
42
|
+
mkdtempSync: fs.mkdtempSync,
|
|
43
|
+
spawnSync,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Install a plugin from a local directory into a scope's immutable version dir.
|
|
48
|
+
* @param {string} srcDir directory containing the plugin (with its manifest)
|
|
49
|
+
* @param {object} opts { scope="user", cwd, force=false }
|
|
50
|
+
* @returns {{ name, version, scope, dir, warnings }}
|
|
51
|
+
*/
|
|
52
|
+
export function installFromDirectory(srcDir, opts = {}) {
|
|
53
|
+
const scope = opts.scope || "user";
|
|
54
|
+
const src = path.resolve(srcDir);
|
|
55
|
+
if (!_deps.existsSync(src)) {
|
|
56
|
+
throw new Error(`source directory does not exist: ${src}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const manifest = parsePluginManifest(src);
|
|
60
|
+
if (!manifest.ok) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`plugin manifest is invalid:\n - ${manifest.errors.join("\n - ")}`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const { name, version } = manifest.metadata;
|
|
66
|
+
|
|
67
|
+
// Optional signature/integrity verification of the manifest at install time
|
|
68
|
+
// (Phase 3.3l). verifyPluginManifest THROWS on any mismatch/failure — a signed
|
|
69
|
+
// install that fails verification must not land on disk (fail-closed). On
|
|
70
|
+
// success we record a `.plugin-lock.json` so load-time requireSignedPlugins
|
|
71
|
+
// can re-check it without the original signature/key files.
|
|
72
|
+
let verification = null;
|
|
73
|
+
const sig = opts.signature;
|
|
74
|
+
if (sig && (sig.sha256 || sig.signatureFile || sig.requireSignature)) {
|
|
75
|
+
verification = verifyPluginManifest({
|
|
76
|
+
manifestFile: manifest.manifestPath,
|
|
77
|
+
expectedSha256: sig.sha256,
|
|
78
|
+
signatureFile: sig.signatureFile,
|
|
79
|
+
publicKeyFile: sig.publicKeyFile,
|
|
80
|
+
requireSignature: sig.requireSignature === true,
|
|
81
|
+
trustedKeySha256: sig.trustedKeySha256 || null,
|
|
82
|
+
requireTrustedKey: sig.requireTrustedKey === true,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const dest = pluginVersionDir(scope, name, version, { cwd: opts.cwd });
|
|
87
|
+
if (_deps.existsSync(dest)) {
|
|
88
|
+
if (!opts.force) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`${name}@${version} is already installed at ${scope} scope (immutable). ` +
|
|
91
|
+
`Use --force to reinstall, or bump the version.`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
_deps.rmSync(dest, { recursive: true, force: true });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
_deps.mkdirSync(dest, { recursive: true });
|
|
98
|
+
copyDirGuarded(src, dest, dest);
|
|
99
|
+
|
|
100
|
+
// Record the verified signature into the installed (immutable) version dir so
|
|
101
|
+
// load-time enforcement can re-check it. The manifest was copied verbatim, so
|
|
102
|
+
// its bytes/sha in `dest` match what we verified in `src`.
|
|
103
|
+
if (verification) {
|
|
104
|
+
const destManifest = path.join(
|
|
105
|
+
dest,
|
|
106
|
+
path.relative(src, manifest.manifestPath),
|
|
107
|
+
);
|
|
108
|
+
writePluginLock(dest, {
|
|
109
|
+
manifestFile: destManifest,
|
|
110
|
+
sha256: verification.sha256,
|
|
111
|
+
publicKeySha256: verification.publicKeySha256,
|
|
112
|
+
signatureVerified: verification.signatureVerified === true,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Make the freshly-installed version active.
|
|
117
|
+
setActiveVersion(name, version, { scope, cwd: opts.cwd });
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
name,
|
|
121
|
+
version,
|
|
122
|
+
scope,
|
|
123
|
+
dir: dest,
|
|
124
|
+
warnings: manifest.warnings,
|
|
125
|
+
signatureVerified: verification?.signatureVerified === true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Install from a source string:
|
|
131
|
+
* - a local directory → copied in directly
|
|
132
|
+
* - a git URL (https://…, git@…, ….git, file://…) → shallow-cloned then installed
|
|
133
|
+
* - GitHub shorthand `owner/repo` → https://github.com/owner/repo.git
|
|
134
|
+
* An optional `#ref` (branch / tag / commit) pins the checkout.
|
|
135
|
+
*/
|
|
136
|
+
export function installFromSource(source, opts = {}) {
|
|
137
|
+
return _withMaterializedSource(source, (dir, info) => {
|
|
138
|
+
const res = installFromDirectory(dir, opts);
|
|
139
|
+
return info ? { ...res, source: info.url, ref: info.ref || null } : res;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Materialize a source string into a local directory (cloning a git source into
|
|
145
|
+
* a temp dir), invoke `fn(dir, gitInfo|null)`, then clean up any temp checkout.
|
|
146
|
+
* Single fetch shared by installFromSource + updatePlugin.
|
|
147
|
+
*/
|
|
148
|
+
function _withMaterializedSource(source, fn) {
|
|
149
|
+
const raw = String(source || "");
|
|
150
|
+
const asDir = path.resolve(raw);
|
|
151
|
+
if (_deps.existsSync(asDir) && _deps.lstatSync(asDir).isDirectory()) {
|
|
152
|
+
return fn(asDir, null);
|
|
153
|
+
}
|
|
154
|
+
const git = parseGitSource(raw);
|
|
155
|
+
if (git) {
|
|
156
|
+
const cloned = fetchGitRepo(git.url, git.ref);
|
|
157
|
+
try {
|
|
158
|
+
return fn(cloned, git);
|
|
159
|
+
} finally {
|
|
160
|
+
try {
|
|
161
|
+
_deps.rmSync(path.dirname(cloned), { recursive: true, force: true });
|
|
162
|
+
} catch {
|
|
163
|
+
/* temp cleanup is best-effort */
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
throw new Error(
|
|
168
|
+
`source not found as a local directory or git URL: ${source}`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Update an installed plugin from a source (local dir or git). Fetches the
|
|
174
|
+
* source ONCE, reads its name+version, and:
|
|
175
|
+
* - a NEW version → installs the new immutable version dir + repoints
|
|
176
|
+
* `.active` (the old version stays on disk for rollback via `cc plugin use`);
|
|
177
|
+
* - the SAME version → no-op unless `--force` reinstalls it.
|
|
178
|
+
* Returns { name, version, previousVersion, updated, reinstalled }.
|
|
179
|
+
*/
|
|
180
|
+
export function updatePlugin(source, opts = {}) {
|
|
181
|
+
const scope = opts.scope || "user";
|
|
182
|
+
return _withMaterializedSource(source, (dir, info) => {
|
|
183
|
+
const manifest = parsePluginManifest(dir);
|
|
184
|
+
if (!manifest.ok) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`plugin manifest is invalid:\n - ${manifest.errors.join("\n - ")}`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
const { name, version } = manifest.metadata;
|
|
190
|
+
const previousVersion = getActiveVersion(name, { scope, cwd: opts.cwd });
|
|
191
|
+
const dest = pluginVersionDir(scope, name, version, { cwd: opts.cwd });
|
|
192
|
+
const sameVersionExists = _deps.existsSync(dest);
|
|
193
|
+
|
|
194
|
+
if (sameVersionExists && !opts.force) {
|
|
195
|
+
// Already at this version — make sure it's the active one, but don't
|
|
196
|
+
// reinstall an immutable dir.
|
|
197
|
+
if (previousVersion !== version) {
|
|
198
|
+
setActiveVersion(name, version, { scope, cwd: opts.cwd });
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
name,
|
|
202
|
+
version,
|
|
203
|
+
previousVersion,
|
|
204
|
+
updated: previousVersion !== version,
|
|
205
|
+
reinstalled: false,
|
|
206
|
+
source: info ? info.url : null,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const res = installFromDirectory(dir, { ...opts, scope, force: true });
|
|
211
|
+
return {
|
|
212
|
+
...res,
|
|
213
|
+
previousVersion,
|
|
214
|
+
updated: previousVersion !== version,
|
|
215
|
+
reinstalled: sameVersionExists,
|
|
216
|
+
source: info ? info.url : null,
|
|
217
|
+
ref: info ? info.ref || null : null,
|
|
218
|
+
};
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Classify a source string into a git URL (+ optional ref), or null when it is
|
|
224
|
+
* not remote-looking. `owner/repo` expands to a GitHub HTTPS URL.
|
|
225
|
+
*/
|
|
226
|
+
export function parseGitSource(raw) {
|
|
227
|
+
const [loc, ref] = String(raw || "").split("#");
|
|
228
|
+
if (!loc) return null;
|
|
229
|
+
if (
|
|
230
|
+
/^(https?|git|ssh|file):\/\//.test(loc) ||
|
|
231
|
+
loc.endsWith(".git") ||
|
|
232
|
+
/^git@/.test(loc)
|
|
233
|
+
) {
|
|
234
|
+
return { url: loc, ref: ref || null };
|
|
235
|
+
}
|
|
236
|
+
if (/^[\w.-]+\/[\w.-]+$/.test(loc)) {
|
|
237
|
+
return { url: `https://github.com/${loc}.git`, ref: ref || null };
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Shallow-clone `url` (optionally at `ref`) into a fresh temp dir and return the
|
|
244
|
+
* checkout path. Uses `git` via spawn WITHOUT a shell (url/ref are argv, not a
|
|
245
|
+
* command line — no injection). Caller removes the temp dir's parent.
|
|
246
|
+
*/
|
|
247
|
+
export function fetchGitRepo(url, ref) {
|
|
248
|
+
const base = _deps.mkdtempSync(path.join(os.tmpdir(), "cc-plugin-git-"));
|
|
249
|
+
const dir = path.join(base, "repo");
|
|
250
|
+
const run = (args) =>
|
|
251
|
+
_deps.spawnSync("git", args, {
|
|
252
|
+
encoding: "utf8",
|
|
253
|
+
timeout: 120000,
|
|
254
|
+
windowsHide: true,
|
|
255
|
+
shell: false,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const cloneArgs = ["clone", "--depth", "1"];
|
|
259
|
+
if (ref) cloneArgs.push("--branch", ref);
|
|
260
|
+
cloneArgs.push(url, dir);
|
|
261
|
+
let res = run(cloneArgs);
|
|
262
|
+
if (res.error && res.error.code === "ENOENT") {
|
|
263
|
+
throw new Error("git is not installed (needed to fetch a remote plugin)");
|
|
264
|
+
}
|
|
265
|
+
if (res.status !== 0) {
|
|
266
|
+
// A commit SHA can't be used with --branch/--depth; retry with a full clone
|
|
267
|
+
// then checkout the ref explicitly.
|
|
268
|
+
if (ref) {
|
|
269
|
+
try {
|
|
270
|
+
_deps.rmSync(dir, { recursive: true, force: true });
|
|
271
|
+
} catch {
|
|
272
|
+
/* ignore */
|
|
273
|
+
}
|
|
274
|
+
const full = run(["clone", url, dir]);
|
|
275
|
+
if (!full.error && full.status === 0) {
|
|
276
|
+
const co = run(["-C", dir, "checkout", ref]);
|
|
277
|
+
if (co.status === 0) return dir;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const reason = (res.stderr || "").trim() || `git exited ${res.status}`;
|
|
281
|
+
throw new Error(`git clone failed for ${url}: ${reason}`);
|
|
282
|
+
}
|
|
283
|
+
return dir;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* List installed plugins (active version per name) across scopes. Uses
|
|
288
|
+
* `skipPolicy` so the ADMIN view shows every plugin on disk — including one an
|
|
289
|
+
* org managed policy blocks from LOADING — otherwise a denied plugin would be
|
|
290
|
+
* invisible and impossible to inspect / uninstall / un-deny.
|
|
291
|
+
*/
|
|
292
|
+
export function listInstalled(opts = {}) {
|
|
293
|
+
return discoverPlugins({
|
|
294
|
+
cwd: opts.cwd,
|
|
295
|
+
scopes: opts.scopes,
|
|
296
|
+
skipPolicy: true,
|
|
297
|
+
}).map((p) => ({
|
|
298
|
+
name: p.name,
|
|
299
|
+
version: p.version,
|
|
300
|
+
scope: p.scope,
|
|
301
|
+
dir: p.root,
|
|
302
|
+
ok: p.manifest?.ok === true,
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Uninstall a plugin. Without `version`, removes the whole plugin (all versions);
|
|
308
|
+
* with `version`, removes just that one and repoints `.active` if needed.
|
|
309
|
+
* @returns {{ removed: string[] }}
|
|
310
|
+
*/
|
|
311
|
+
export function uninstall(name, opts = {}) {
|
|
312
|
+
const scope = opts.scope || "user";
|
|
313
|
+
const cwd = opts.cwd;
|
|
314
|
+
const removed = [];
|
|
315
|
+
|
|
316
|
+
if (opts.version) {
|
|
317
|
+
const dir = pluginVersionDir(scope, name, opts.version, { cwd });
|
|
318
|
+
if (!_deps.existsSync(dir)) {
|
|
319
|
+
throw new Error(
|
|
320
|
+
`${name}@${opts.version} is not installed at ${scope} scope`,
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
_deps.rmSync(dir, { recursive: true, force: true });
|
|
324
|
+
removed.push(opts.version);
|
|
325
|
+
// Repoint .active to the newest remaining version, or clear it.
|
|
326
|
+
const remaining = listInstalledVersions(scope, name, { cwd });
|
|
327
|
+
const activeFile = path.join(
|
|
328
|
+
pluginNameDir(scope, name, { cwd }),
|
|
329
|
+
".active",
|
|
330
|
+
);
|
|
331
|
+
if (remaining.length === 0) {
|
|
332
|
+
_deps.rmSync(pluginNameDir(scope, name, { cwd }), {
|
|
333
|
+
recursive: true,
|
|
334
|
+
force: true,
|
|
335
|
+
});
|
|
336
|
+
} else if (_deps.existsSync(activeFile)) {
|
|
337
|
+
_deps.writeFileSync(activeFile, remaining[0], "utf8");
|
|
338
|
+
}
|
|
339
|
+
return { removed };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const nameDir = pluginNameDir(scope, name, { cwd });
|
|
343
|
+
if (!_deps.existsSync(nameDir)) {
|
|
344
|
+
throw new Error(`${name} is not installed at ${scope} scope`);
|
|
345
|
+
}
|
|
346
|
+
removed.push(...listInstalledVersions(scope, name, { cwd }));
|
|
347
|
+
_deps.rmSync(nameDir, { recursive: true, force: true });
|
|
348
|
+
return { removed };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** Pin a plugin's active version (rollback / switch). */
|
|
352
|
+
export function setActiveVersion(name, version, opts = {}) {
|
|
353
|
+
const scope = opts.scope || "user";
|
|
354
|
+
const cwd = opts.cwd;
|
|
355
|
+
const versionDir = pluginVersionDir(scope, name, version, { cwd });
|
|
356
|
+
if (!_deps.existsSync(versionDir)) {
|
|
357
|
+
throw new Error(`${name}@${version} is not installed at ${scope} scope`);
|
|
358
|
+
}
|
|
359
|
+
const activeFile = path.join(pluginNameDir(scope, name, { cwd }), ".active");
|
|
360
|
+
_deps.writeFileSync(activeFile, String(version), "utf8");
|
|
361
|
+
return { name, version, scope, active: version };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** Which version is active for a plugin at a scope (or null). */
|
|
365
|
+
export function getActiveVersion(name, opts = {}) {
|
|
366
|
+
return activeVersion(opts.scope || "user", name, { cwd: opts.cwd });
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ── guarded recursive copy ────────────────────────────────────────────────
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Copy `src` → `dst`, refusing to write outside `root` and skipping symlinks
|
|
373
|
+
* (which could later resolve outside the plugin sandbox). Directories recurse.
|
|
374
|
+
*/
|
|
375
|
+
function copyDirGuarded(src, dst, root) {
|
|
376
|
+
for (const entry of _deps.readdirSync(src, { withFileTypes: true })) {
|
|
377
|
+
const from = path.join(src, entry.name);
|
|
378
|
+
const to = path.join(dst, entry.name);
|
|
379
|
+
if (!isWithin(root, to)) continue; // never escape the version dir
|
|
380
|
+
if (entry.isSymbolicLink()) continue; // do not copy symlinks
|
|
381
|
+
if (entry.isDirectory()) {
|
|
382
|
+
_deps.mkdirSync(to, { recursive: true });
|
|
383
|
+
copyDirGuarded(from, to, root);
|
|
384
|
+
} else if (entry.isFile()) {
|
|
385
|
+
_deps.copyFileSync(from, to);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply installed plugins' `.lsp.json` language-server contributions to the LSP
|
|
3
|
+
* registry (Phase 3.2 — first plugin component actually wired to the agent).
|
|
4
|
+
*
|
|
5
|
+
* A plugin declares extra language servers via `.lsp.json`; this bridges those
|
|
6
|
+
* declarations into `lsp-server-registry.registerLanguageServer`, so the Phase 2
|
|
7
|
+
* `code_intelligence` agent tool and `cc code-intel` command transparently gain
|
|
8
|
+
* definition/references/diagnostics for the plugin's language — no core change
|
|
9
|
+
* per new language.
|
|
10
|
+
*
|
|
11
|
+
* Idempotent + memoized per project root: the first LSP query (agent or CLI)
|
|
12
|
+
* triggers a one-time scan of the user/project/local plugin scopes.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import path from "path";
|
|
16
|
+
import { discoverPlugins } from "./scopes.js";
|
|
17
|
+
import { registerLanguageServer } from "../lsp/lsp-server-registry.js";
|
|
18
|
+
import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
|
|
19
|
+
|
|
20
|
+
const _loadedRoots = new Set();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Register every installed plugin's LSP servers for `cwd` (once per root).
|
|
24
|
+
* Never throws — a broken plugin manifest must not break code navigation.
|
|
25
|
+
*
|
|
26
|
+
* @param {object} [opts] { cwd, scopes, force }
|
|
27
|
+
* @returns {{ registered: Array<{plugin, languageId, id}> }}
|
|
28
|
+
*/
|
|
29
|
+
export function ensurePluginLspServers(opts = {}) {
|
|
30
|
+
const cwd = path.resolve(opts.cwd || process.cwd());
|
|
31
|
+
if (_loadedRoots.has(cwd) && !opts.force) return { registered: [] };
|
|
32
|
+
_loadedRoots.add(cwd);
|
|
33
|
+
|
|
34
|
+
const registered = [];
|
|
35
|
+
let plugins = [];
|
|
36
|
+
try {
|
|
37
|
+
plugins = discoverPlugins({ cwd, scopes: opts.scopes });
|
|
38
|
+
} catch {
|
|
39
|
+
return { registered };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// An LSP server spawns a binary — gate it behind trust. Untrusted project
|
|
43
|
+
// plugins (e.g. from a cloned repo) don't get to launch processes silently.
|
|
44
|
+
const { trusted, skipped } = partitionByTrust(plugins);
|
|
45
|
+
warnUntrustedOnce(
|
|
46
|
+
skipped
|
|
47
|
+
.filter((p) => (p.manifest?.components?.lsp || []).length > 0)
|
|
48
|
+
.map((p) => p.name),
|
|
49
|
+
"language servers",
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
for (const p of trusted) {
|
|
53
|
+
const servers = p.manifest?.components?.lsp || [];
|
|
54
|
+
for (const s of servers) {
|
|
55
|
+
try {
|
|
56
|
+
registerLanguageServer({
|
|
57
|
+
languageId: s.languageId,
|
|
58
|
+
command: s.command,
|
|
59
|
+
args: s.args,
|
|
60
|
+
extensions: s.extensions,
|
|
61
|
+
id: s.id,
|
|
62
|
+
});
|
|
63
|
+
registered.push({ plugin: p.name, languageId: s.languageId, id: s.id });
|
|
64
|
+
} catch {
|
|
65
|
+
// skip a malformed server entry; other plugins still load
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { registered };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Test hook: forget which roots have been scanned so a re-scan can run. */
|
|
73
|
+
export function _resetPluginLspLoadState() {
|
|
74
|
+
_loadedRoots.clear();
|
|
75
|
+
}
|