chainlesschain 0.162.147 → 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 +2 -2
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-DywJ7xTv.js → AIOps-BJif14yR.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BTlqcY-q.js → ActionButton-CXek6gJY.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-FdG1Mvwy.js → Analytics-BgQhdAJi.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CVsnbvN1.js → AppLayout-DTYl5NtR.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-ITdpJ7vR.js → Audit-BzATQAeR.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BfXqnXo1.js → Backup-BzdPEQhL.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C09ip3_E.js → BaseInput-BM0KVh8E.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-qdkhB42l.js → Chat-D7fHXHkz.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-D2KfETZD.js → ChatBubbleRenderer-Wm34RR-b.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-j6WZCuff.js → Checkbox-CAiSQ9vO.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CGZ-K3uK.js → Codegen-Df40CrEe.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CIlPOAu6.js → Col-DIT2fNFU.js} +1 -1
- package/src/assets/web-panel/assets/{Community-sY-i-hic.js → Community-CSTIbW2k.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DcoEfyL8.js → Compact-DtqXZZUi.js} +1 -1
- package/src/assets/web-panel/assets/Compliance-CUtzw6o7.js +1 -0
- package/src/assets/web-panel/assets/{Cowork-DLY_fkLv.js → Cowork-CezmlnmU.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-D049pZBC.js → Cron-P4BITarg.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DCXdE8M9.js → Crosschain-BRRiYwvq.js} +1 -1
- package/src/assets/web-panel/assets/{DID-JCvPZtjW.js → DID-L5ijB9i4.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Cg2xu3iE.js → Dashboard-CFxro4ob.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DjJLjbLh.js → Dropdown-BmAFCQ71.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DugNcSLR.js → EmailListRenderer-DJBCEuon.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-J6Nb-E30.js → FamilyGuardDashboard-z4oLq6eT.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CQ3Ttpbl.js → Federation-DsVCpHMF.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cz0NKag4.js → FormItemContext-DP3bP5th.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-NYcMP6iz.js → GenericCardRenderer-BAXP2Gc6.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B4hYN18N.js → Git-I3g_bAw9.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DOQPCcC0.js → Governance-jHS5-ioS.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-HtLF746W.js → Inference-CPL7sfx9.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BXrk8rMm.js → KnowledgeGraph-Jf236h4C.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DS0JnD3-.js → Logs-Cm2tcSKg.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-Cqnjihrz.js → Marketplace-CZK82rhi.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Cx3Psk-U.js → McpTools-CZCeji7a.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-BjyysPtj.js → Memory-3l2KVS9k.js} +2 -2
- package/src/assets/web-panel/assets/MobileBridge-D3Arxfeg.css +1 -0
- package/src/assets/web-panel/assets/MobileBridge-VYdpoLh6.js +1 -0
- package/src/assets/web-panel/assets/{MobileProjects-Xwf-fdOQ.js → MobileProjects-CiB9cmm1.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Djkql5m5.js → Mtc-wCFZbBuL.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-D2BAZlu0.js → MtcAudit-1a3THIyG.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-ByqP1ZzH.js → Multisig-DMzLB2hG.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-C_kn0nE2.js → NLProgramming-D9wZbf6l.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-7l7eXHPq.js → Notes-hrFe2ZM-.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BrCDoitw.js → NotificationSettings-CHGX5Jwm.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CRIFE2Tj.js +1 -0
- package/src/assets/web-panel/assets/{Organization-DdoNAxsM.js → Organization-By7FkhtY.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Bw7R6dE3.js → Overflow--khGSzJn.js} +1 -1
- package/src/assets/web-panel/assets/{OverrideContext-C2r4vyBb.js → OverrideContext-BlgL9440.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DJNtBugA.js → P2P-CAG9dH35.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Dwhd5_ue.js → PdhVaultBrowser-B3SQZmK9.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYoSfhPR.js → Permissions-D35ec6JA.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-aYCYJbnH.js → PersonalDataHub-CxBF7hW_.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-DJp6CprF.js → Pipeline-s6EtOO1s.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Cq9HZweR.js → Privacy-BbJYmWmO.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CmF0HsSt.js → ProjectInit-VGQq1jMT.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-P8V5Aea2.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-D589v0q2.js → Providers-mrO47FIK.js} +1 -1
- package/src/assets/web-panel/assets/QrScannerModal-IJF2mHyw.js +1 -0
- package/src/assets/web-panel/assets/{MobileBridge-zJpPuf4E.css → QrScannerModal-NwAHNfiJ.css} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-lvRV2osB.js → QuickAsk-CU0wN_Z0.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-sb84MTpY.js → Recommend-DhN37R6G.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-D_xOX_hu.js +4 -0
- package/src/assets/web-panel/assets/{Reputation-d9Y3vy-W.js → Reputation-D_Ssv7uH.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DMdhLUTp.js → Row-Kx5dKxX7.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-OL-SMDkz.js → RssFeed-BlGwqZkY.js} +2 -2
- package/src/assets/web-panel/assets/{Search-BjStLKq1.js → Search-hLFQzxpb.js} +1 -1
- package/src/assets/web-panel/assets/{Security-MeI411qr.js → Security-DMlvsVt4.js} +2 -2
- package/src/assets/web-panel/assets/{Services-UajosuhT.js → Services-Bd0Qsj2m.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CGpG-QJ1.js → Skeleton-DFclq9X3.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-C6P2RPY-.js → Skills-BNtln2qY.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-B_fPprgw.js → Sla-Dch5H19G.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CqxLjSlQ.js → SpeechSettings-2UfQcU1g.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DixSfFOQ.js → SyncSettings-CD9YQr4i.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-PavzPBxc.js → Tasks-BNu-7MzI.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BYdGRnfX.js → Templates-D1n9CaIR.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BQLgnarD.js → Tenant-DenL3iBW.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-ZzU0Io1Y.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-Bv7uZRM7.js → TimelineRenderer-D87IfukO.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DxhgszRJ.js → Tokens-BkiZc8E3.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-nQYHayuc.js → Trigger-BNRSytGN.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CvqZDuZ4.js → Trust-Bwbd4X2m.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DaR8GV2I.js → UkeySign-B8O2bs0o.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C6Mpsokw.js → VideoEditing-C2fL8zmH.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-B57VRrWc.js → Wallet-BORYDdE1.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BbtEEtpq.js → WebAuthn-C0V5S2FM.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-C5LwvKmH.js → WorkflowEditor-2dR0fcHL.js} +1 -1
- package/src/assets/web-panel/assets/{chat-MzuPR5jh.js → chat-BmuAFAn8.js} +1 -1
- package/src/assets/web-panel/assets/{collapseMotion-mxzzcDsg.js → collapseMotion-DVfhbsdP.js} +1 -1
- package/src/assets/web-panel/assets/{colors-HyW8mi_y.js → colors-CN3smMcK.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DvUSzWAp.js → compact-item-DCnxb4kW.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-4MppZl7X.js → createContext-vlR43pHn.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DxwmsL4j.js +1 -0
- package/src/assets/web-panel/assets/{echarts-jxgIOFUZ.js → echarts-CL0F2SnX.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-D9q3CJ-u.js → hasIn-D04tHYFd.js} +1 -1
- package/src/assets/web-panel/assets/{icons-DP3uiYxy.js → icons-BsDmGpcT.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9I5MuJ0.js → index-0DnaZGkC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Caqtu6Et.js → index-430S68MN.js} +1 -1
- package/src/assets/web-panel/assets/{index-eOQO6KnV.js → index-B78xL3s2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ci6adKh8.js → index-BTPEZTk1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CifKUNtV.js → index-BXRg8GFQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-D7TDMMfu.js → index-BasvsWDM.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNYqw0MO.js → index-BeGDEXFs.js} +1 -1
- package/src/assets/web-panel/assets/{index-m0b6Fj9q.js → index-BqhASEL4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BF2JsbiX.js → index-BudvKQfG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFqvWeGc.js → index-C-6NO5il.js} +1 -1
- package/src/assets/web-panel/assets/{index-SDblbKj6.js → index-C5Sxb1sE.js} +1 -1
- package/src/assets/web-panel/assets/{index-UUMCA2Sq.js → index-CP9m9Ggr.js} +4 -4
- package/src/assets/web-panel/assets/{index--Bm6OqmU.js → index-CTOCnIQ7.js} +1 -1
- package/src/assets/web-panel/assets/index-Cg2ldRfU.js +1 -0
- package/src/assets/web-panel/assets/{index-Dv7ffI2g.js → index-ClhAHDK3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcunsBIx.js → index-CnXebZLL.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvAqCAo9.js → index-CngCC8hC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcMkjb92.js → index-Cr-A0FYJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFGfC5RS.js → index-CsAsCPJ6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDZ6fP5Z.js → index-D28DeAAB.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgIfXOD9.js → index-D5LZVZ0L.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmZ6-suL.js → index-D65_XseV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cs-RKRUM.js → index-D8vwp4qp.js} +1 -1
- package/src/assets/web-panel/assets/{index-B994UQfE.js → index-DDwLgQY9.js} +1 -1
- package/src/assets/web-panel/assets/{index-Btz8pU68.js → index-DIpY0qM5.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5E4DhVc.js → index-DTWg-18U.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2jrnaq3.js → index-DTtRscUa.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmCo2Xdp.js → index-DYAtmqiv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Zvtru2oE.js → index-D_n8_vfI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLAZs1Vd.js → index-DbADHwhr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CucRo4Vz.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-C0DiL97c.js → index-HhNjnCfe.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bl-E4W4q.js → index-ToUh2b1-.js} +1 -1
- package/src/assets/web-panel/assets/{index-ryFLxB1p.js → index-TslMTwNy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGQtFso0.js → index-_FIkN3jd.js} +1 -1
- package/src/assets/web-panel/assets/{index-zLwYpvX0.js → index-hpvvtAZ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dvg5xYuP.js → index-jzR7Ujuw.js} +1 -1
- package/src/assets/web-panel/assets/{index-xvUVuFnA.js → index-ljuBiQW9.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CC--PMsC.js → initDefaultProps-C3wUb4je.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DdrBjRF1.js → motion-D-jYFUNA.js} +1 -1
- package/src/assets/web-panel/assets/{move-D712Gwl2.js → move-tqb8nwJ2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-39y-keKO.js → mtc-parser-DSOjMJMz.js} +1 -1
- package/src/assets/web-panel/assets/{omit-6Y51gDB9.js → omit-CJ7VimIW.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Cy6EHbq9.js → pickAttrs-qXiG1iT3.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BYxdAm0p.js → placementArrow-CkGBcy1Z.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C9R6q_cr.js → responsiveObserve-D3WvTlLO.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CXUJlilB.js → slide-BEE2ftge.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DOtrOeql.js → statusUtils-CsfBpxiG.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-D5r39o92.js → styleChecker-xPrIiJPI.js} +1 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-C5Yd-SCR.js +1 -0
- package/src/assets/web-panel/assets/{useFs-BkrT-Zbc.js → useFs-D_MDS8HI.js} +1 -1
- package/src/assets/web-panel/assets/{useMergedState-Cl2q1fes.js → useMergedState-Dc9sO2d7.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BwnnnZjU.js → usePersonalDataHub-BqJh-usA.js} +1 -1
- package/src/assets/web-panel/assets/{useRefs-DD9q4mOT.js → useRefs-CAeFRzPf.js} +1 -1
- package/src/assets/web-panel/assets/{useState-BK4hy8Ma.js → useState-LwExUYDO.js} +1 -1
- package/src/assets/web-panel/assets/{vendor-BvqAck49.js → vendor-BMxUs6UX.js} +16 -16
- package/src/assets/web-panel/assets/{vnode-SU-N4pum.js → vnode-chm_p-gz.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DHScfpTP.js → zoom-3-YDNz0Z.js} +1 -1
- package/src/assets/web-panel/index.html +3 -3
- 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/serve.js +10 -0
- package/src/commands/team.js +456 -0
- package/src/gateways/remote-session-relay.js +160 -0
- package/src/gateways/ws/message-dispatcher.js +77 -2
- package/src/gateways/ws/remote-session-protocol.js +466 -0
- package/src/gateways/ws/ws-server.js +299 -0
- package/src/harness/golden-transcript.js +65 -0
- package/src/harness/mcp-client.js +6 -2
- package/src/harness/prompt-compressor.js +27 -1
- package/src/harness/remote-command-ledger.js +158 -0
- package/src/harness/remote-session-audit-sink.js +132 -0
- package/src/harness/remote-session-audit.js +110 -0
- package/src/harness/remote-session-crypto.js +217 -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 +238 -0
- 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/harness/remote-session-push.js +104 -0
- package/src/harness/remote-session-registry.js +420 -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/did-manager.js +6 -4
- 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/mcp-oauth.js +23 -6
- 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/session-manager.js +7 -3
- 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/agent-runtime.js +8 -13
- 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/runtime/policies/agent-policy.js +2 -0
- package/src/assets/web-panel/assets/Compliance-T8EtTecW.js +0 -1
- package/src/assets/web-panel/assets/MobileBridge-CYaITZ7w.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CmtqIdwr.js +0 -1
- package/src/assets/web-panel/assets/Projects-DPd5-lNS.js +0 -1
- package/src/assets/web-panel/assets/Terminal-BzOHpq-B.js +0 -3
- package/src/assets/web-panel/assets/devWarning-6YJJMjSH.js +0 -1
- package/src/assets/web-panel/assets/index-D_oeGLr6.js +0 -1
- package/src/assets/web-panel/assets/index-Qa57YG19.js +0 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-DfAD5U4Z.js +0 -1
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified plugin manifest — parse + normalize a ChainlessChain plugin into the
|
|
3
|
+
* full set of components it contributes (Phase 3 of the CLI parity plan).
|
|
4
|
+
*
|
|
5
|
+
* Today the CLI only understands `manifest.skills[]` (harness/plugin-manager.js).
|
|
6
|
+
* A real extension unit bundles more than skills, so this module reads the
|
|
7
|
+
* unified layout:
|
|
8
|
+
*
|
|
9
|
+
* plugin/
|
|
10
|
+
* ├── .chainlesschain-plugin/plugin.json (metadata; legacy top-level plugin.json accepted)
|
|
11
|
+
* ├── skills/<name>/SKILL.md
|
|
12
|
+
* ├── agents/<name>.md
|
|
13
|
+
* ├── hooks/hooks.json
|
|
14
|
+
* ├── .mcp.json
|
|
15
|
+
* ├── .lsp.json
|
|
16
|
+
* ├── monitors/monitors.json
|
|
17
|
+
* ├── bin/<exe>
|
|
18
|
+
* └── settings.json
|
|
19
|
+
*
|
|
20
|
+
* Each component may be declared EXPLICITLY in plugin.json or discovered BY
|
|
21
|
+
* CONVENTION (explicit wins). Every resolved path is checked to stay inside the
|
|
22
|
+
* plugin root — a manifest that points at `../../etc` is rejected, never
|
|
23
|
+
* followed. Pure + `_deps`-injectable so it is fully unit-testable with no real
|
|
24
|
+
* filesystem.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import fs from "fs";
|
|
28
|
+
import path from "path";
|
|
29
|
+
import semver from "semver";
|
|
30
|
+
|
|
31
|
+
export const _deps = {
|
|
32
|
+
existsSync: fs.existsSync,
|
|
33
|
+
readFileSync: fs.readFileSync,
|
|
34
|
+
readdirSync: fs.readdirSync,
|
|
35
|
+
statSync: fs.statSync,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** True when `child` resolves to `parent` or a path strictly inside it. */
|
|
39
|
+
export function isWithin(parent, child) {
|
|
40
|
+
const rel = path.relative(path.resolve(parent), path.resolve(child));
|
|
41
|
+
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Locate the manifest file for a plugin root, preferring the namespaced
|
|
46
|
+
* `.chainlesschain-plugin/plugin.json` over the legacy top-level `plugin.json`.
|
|
47
|
+
* @returns {string|null} absolute manifest path
|
|
48
|
+
*/
|
|
49
|
+
export function findManifestPath(root) {
|
|
50
|
+
const preferred = path.join(root, ".chainlesschain-plugin", "plugin.json");
|
|
51
|
+
if (_deps.existsSync(preferred)) return preferred;
|
|
52
|
+
const legacy = path.join(root, "plugin.json");
|
|
53
|
+
if (_deps.existsSync(legacy)) return legacy;
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse and normalize a plugin at `root`. Never throws — problems are collected
|
|
59
|
+
* in `errors`/`warnings` and `ok` reflects whether it is safe to load.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} root plugin root directory
|
|
62
|
+
* @returns {object} normalized manifest (see module doc)
|
|
63
|
+
*/
|
|
64
|
+
export function parsePluginManifest(root) {
|
|
65
|
+
const errors = [];
|
|
66
|
+
const warnings = [];
|
|
67
|
+
const abs = path.resolve(root);
|
|
68
|
+
|
|
69
|
+
const result = {
|
|
70
|
+
ok: false,
|
|
71
|
+
root: abs,
|
|
72
|
+
manifestPath: null,
|
|
73
|
+
scope: null,
|
|
74
|
+
metadata: {},
|
|
75
|
+
components: emptyComponents(),
|
|
76
|
+
errors,
|
|
77
|
+
warnings,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (!_deps.existsSync(abs)) {
|
|
81
|
+
errors.push(`plugin root does not exist: ${abs}`);
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const manifestPath = findManifestPath(abs);
|
|
86
|
+
if (!manifestPath) {
|
|
87
|
+
errors.push(
|
|
88
|
+
"no manifest found (.chainlesschain-plugin/plugin.json or plugin.json)",
|
|
89
|
+
);
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
result.manifestPath = manifestPath;
|
|
93
|
+
|
|
94
|
+
let manifest;
|
|
95
|
+
try {
|
|
96
|
+
manifest = JSON.parse(_deps.readFileSync(manifestPath, "utf8"));
|
|
97
|
+
} catch (err) {
|
|
98
|
+
errors.push(`manifest is not valid JSON: ${err.message}`);
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
102
|
+
errors.push("manifest must be a JSON object");
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── metadata ──
|
|
107
|
+
const name = typeof manifest.name === "string" ? manifest.name.trim() : "";
|
|
108
|
+
if (!name) errors.push("manifest.name is required");
|
|
109
|
+
else if (!/^[a-zA-Z0-9._@/-]+$/.test(name)) {
|
|
110
|
+
errors.push(`manifest.name has invalid characters: "${name}"`);
|
|
111
|
+
}
|
|
112
|
+
const version =
|
|
113
|
+
typeof manifest.version === "string" ? manifest.version.trim() : "";
|
|
114
|
+
if (!version) errors.push("manifest.version is required");
|
|
115
|
+
else if (!semver.valid(version)) {
|
|
116
|
+
errors.push(`manifest.version is not valid semver: "${version}"`);
|
|
117
|
+
}
|
|
118
|
+
result.metadata = {
|
|
119
|
+
name,
|
|
120
|
+
version,
|
|
121
|
+
description: strOr(manifest.description, ""),
|
|
122
|
+
author: strOr(manifest.author, ""),
|
|
123
|
+
homepage: strOr(manifest.homepage, ""),
|
|
124
|
+
license: strOr(manifest.license, ""),
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// A helper that resolves a plugin-relative path safely, recording an error if
|
|
128
|
+
// it escapes the root or is absolute. Returns the absolute path or null.
|
|
129
|
+
const safeResolve = (rel, label) => {
|
|
130
|
+
if (typeof rel !== "string" || rel === "") {
|
|
131
|
+
errors.push(`${label}: path must be a non-empty string`);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
if (path.isAbsolute(rel)) {
|
|
135
|
+
errors.push(`${label}: absolute paths are not allowed ("${rel}")`);
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const resolved = path.resolve(abs, rel);
|
|
139
|
+
if (!isWithin(abs, resolved)) {
|
|
140
|
+
errors.push(`${label}: path escapes the plugin root ("${rel}")`);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
return resolved;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const c = result.components;
|
|
147
|
+
c.skills = resolveSkills(abs, manifest, safeResolve, warnings);
|
|
148
|
+
c.agents = resolveAgents(abs, manifest, safeResolve, warnings);
|
|
149
|
+
c.hooks = resolveJsonComponent(abs, manifest, safeResolve, {
|
|
150
|
+
field: "hooks",
|
|
151
|
+
conventionPath: ["hooks", "hooks.json"],
|
|
152
|
+
listKey: "hooks",
|
|
153
|
+
});
|
|
154
|
+
c.mcp = resolveJsonComponent(abs, manifest, safeResolve, {
|
|
155
|
+
field: "mcp",
|
|
156
|
+
conventionPath: [".mcp.json"],
|
|
157
|
+
listKey: "mcpServers",
|
|
158
|
+
});
|
|
159
|
+
c.monitors = resolveJsonComponent(abs, manifest, safeResolve, {
|
|
160
|
+
field: "monitors",
|
|
161
|
+
conventionPath: ["monitors", "monitors.json"],
|
|
162
|
+
listKey: "monitors",
|
|
163
|
+
});
|
|
164
|
+
c.lsp = resolveLsp(abs, manifest, safeResolve, errors, warnings);
|
|
165
|
+
c.bin = resolveBin(abs, manifest, safeResolve, warnings);
|
|
166
|
+
c.settings = resolveSettings(abs, manifest, safeResolve);
|
|
167
|
+
|
|
168
|
+
result.ok = errors.length === 0;
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Per-type counts for a parsed manifest (used by validate/list output). */
|
|
173
|
+
export function summarizeComponents(manifest) {
|
|
174
|
+
const c = manifest.components || emptyComponents();
|
|
175
|
+
return {
|
|
176
|
+
skills: c.skills.length,
|
|
177
|
+
agents: c.agents.length,
|
|
178
|
+
hooks: c.hooks ? c.hooks.count || (c.hooks.file ? 1 : 0) : 0,
|
|
179
|
+
mcp: c.mcp ? c.mcp.count || 0 : 0,
|
|
180
|
+
lsp: c.lsp.length,
|
|
181
|
+
monitors: c.monitors ? c.monitors.count || 0 : 0,
|
|
182
|
+
bin: c.bin.length,
|
|
183
|
+
settings: c.settings ? 1 : 0,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ── component resolvers ──────────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
function emptyComponents() {
|
|
190
|
+
return {
|
|
191
|
+
skills: [],
|
|
192
|
+
agents: [],
|
|
193
|
+
hooks: null,
|
|
194
|
+
mcp: null,
|
|
195
|
+
lsp: [],
|
|
196
|
+
monitors: null,
|
|
197
|
+
bin: [],
|
|
198
|
+
settings: null,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function resolveSkills(root, manifest, safeResolve, warnings) {
|
|
203
|
+
const out = [];
|
|
204
|
+
const seen = new Set();
|
|
205
|
+
const push = (name, rel) => {
|
|
206
|
+
const absPath = safeResolve(rel, `skills["${name}"]`);
|
|
207
|
+
if (!absPath) return;
|
|
208
|
+
if (!_deps.existsSync(absPath)) {
|
|
209
|
+
warnings.push(`skill "${name}" path not found: ${rel}`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (seen.has(name)) return;
|
|
213
|
+
seen.add(name);
|
|
214
|
+
out.push({ name, path: rel, absPath });
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
if (Array.isArray(manifest.skills)) {
|
|
218
|
+
for (const entry of manifest.skills) {
|
|
219
|
+
if (entry && typeof entry === "object") {
|
|
220
|
+
if (typeof entry.name === "string" && typeof entry.path === "string") {
|
|
221
|
+
push(entry.name, entry.path);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Convention: skills/<name>/ (each a skill dir, ideally with SKILL.md).
|
|
229
|
+
const dir = path.join(root, "skills");
|
|
230
|
+
if (dirExists(dir)) {
|
|
231
|
+
for (const name of listDirs(dir)) {
|
|
232
|
+
push(name, path.join("skills", name));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return out;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function resolveAgents(root, manifest, safeResolve, warnings) {
|
|
239
|
+
const out = [];
|
|
240
|
+
const seen = new Set();
|
|
241
|
+
const push = (name, rel) => {
|
|
242
|
+
const absPath = safeResolve(rel, `agents["${name}"]`);
|
|
243
|
+
if (!absPath) return;
|
|
244
|
+
if (!_deps.existsSync(absPath)) {
|
|
245
|
+
warnings.push(`agent "${name}" path not found: ${rel}`);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (seen.has(name)) return;
|
|
249
|
+
seen.add(name);
|
|
250
|
+
out.push({ name, path: rel, absPath });
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
if (Array.isArray(manifest.agents)) {
|
|
254
|
+
for (const entry of manifest.agents) {
|
|
255
|
+
if (entry && typeof entry === "object") {
|
|
256
|
+
if (typeof entry.name === "string" && typeof entry.path === "string") {
|
|
257
|
+
push(entry.name, entry.path);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return out;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const dir = path.join(root, "agents");
|
|
265
|
+
if (dirExists(dir)) {
|
|
266
|
+
for (const file of listFiles(dir, /\.(md|json)$/i)) {
|
|
267
|
+
push(file.replace(/\.(md|json)$/i, ""), path.join("agents", file));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return out;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Resolve a JSON-file component (hooks / mcp / monitors). Accepts an inline
|
|
275
|
+
* object in the manifest, an explicit path string, or a convention path.
|
|
276
|
+
*/
|
|
277
|
+
function resolveJsonComponent(root, manifest, safeResolve, opts) {
|
|
278
|
+
const { field, conventionPath, listKey } = opts;
|
|
279
|
+
const inline = manifest[field];
|
|
280
|
+
|
|
281
|
+
// Inline object literal in the manifest (no separate file).
|
|
282
|
+
if (
|
|
283
|
+
inline &&
|
|
284
|
+
typeof inline === "object" &&
|
|
285
|
+
!Array.isArray(inline) &&
|
|
286
|
+
!inline.path
|
|
287
|
+
) {
|
|
288
|
+
return { inline: true, file: null, ...extractList(inline, listKey) };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
let rel = null;
|
|
292
|
+
if (typeof inline === "string") rel = inline;
|
|
293
|
+
else if (
|
|
294
|
+
inline &&
|
|
295
|
+
typeof inline === "object" &&
|
|
296
|
+
typeof inline.path === "string"
|
|
297
|
+
) {
|
|
298
|
+
rel = inline.path;
|
|
299
|
+
} else {
|
|
300
|
+
const conv = path.join(root, ...conventionPath);
|
|
301
|
+
if (_deps.existsSync(conv)) rel = path.join(...conventionPath);
|
|
302
|
+
}
|
|
303
|
+
if (!rel) return null;
|
|
304
|
+
|
|
305
|
+
const absPath = safeResolve(rel, field);
|
|
306
|
+
if (!absPath || !_deps.existsSync(absPath)) return null;
|
|
307
|
+
let content = null;
|
|
308
|
+
try {
|
|
309
|
+
content = JSON.parse(_deps.readFileSync(absPath, "utf8"));
|
|
310
|
+
} catch {
|
|
311
|
+
content = null;
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
inline: false,
|
|
315
|
+
file: rel,
|
|
316
|
+
absPath,
|
|
317
|
+
...extractList(content || {}, listKey),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function extractList(obj, listKey) {
|
|
322
|
+
const val = obj && obj[listKey];
|
|
323
|
+
if (Array.isArray(val)) {
|
|
324
|
+
return {
|
|
325
|
+
count: val.length,
|
|
326
|
+
names: val.map((v) => v?.name || v?.id || null).filter(Boolean),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (val && typeof val === "object") {
|
|
330
|
+
const names = Object.keys(val);
|
|
331
|
+
return { count: names.length, names };
|
|
332
|
+
}
|
|
333
|
+
return { count: 0, names: [] };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function resolveLsp(root, manifest, safeResolve, errors, warnings) {
|
|
337
|
+
let content = manifest.lsp;
|
|
338
|
+
// Convention file `.lsp.json`.
|
|
339
|
+
if (!content) {
|
|
340
|
+
const conv = path.join(root, ".lsp.json");
|
|
341
|
+
if (_deps.existsSync(conv)) {
|
|
342
|
+
try {
|
|
343
|
+
content = JSON.parse(_deps.readFileSync(conv, "utf8"));
|
|
344
|
+
} catch (err) {
|
|
345
|
+
errors.push(`.lsp.json is not valid JSON: ${err.message}`);
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (!content) return [];
|
|
351
|
+
|
|
352
|
+
// Accept either { servers: [...] } or a bare array.
|
|
353
|
+
const servers = Array.isArray(content)
|
|
354
|
+
? content
|
|
355
|
+
: Array.isArray(content.servers)
|
|
356
|
+
? content.servers
|
|
357
|
+
: [];
|
|
358
|
+
const out = [];
|
|
359
|
+
for (const s of servers) {
|
|
360
|
+
if (!s || typeof s !== "object") continue;
|
|
361
|
+
if (typeof s.languageId !== "string" || typeof s.command !== "string") {
|
|
362
|
+
warnings.push("lsp server entry missing languageId/command — skipped");
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
out.push({
|
|
366
|
+
languageId: s.languageId,
|
|
367
|
+
command: s.command,
|
|
368
|
+
args: Array.isArray(s.args) ? s.args.map(String) : [],
|
|
369
|
+
extensions: Array.isArray(s.extensions) ? s.extensions.map(String) : [],
|
|
370
|
+
id: typeof s.id === "string" ? s.id : s.command,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
return out;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function resolveBin(root, manifest, safeResolve, warnings) {
|
|
377
|
+
const out = [];
|
|
378
|
+
const seen = new Set();
|
|
379
|
+
const push = (name, rel) => {
|
|
380
|
+
const absPath = safeResolve(rel, `bin["${name}"]`);
|
|
381
|
+
if (!absPath) return;
|
|
382
|
+
if (!_deps.existsSync(absPath)) {
|
|
383
|
+
warnings.push(`bin "${name}" path not found: ${rel}`);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (seen.has(name)) return;
|
|
387
|
+
seen.add(name);
|
|
388
|
+
out.push({ name, path: rel, absPath });
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
if (
|
|
392
|
+
manifest.bin &&
|
|
393
|
+
typeof manifest.bin === "object" &&
|
|
394
|
+
!Array.isArray(manifest.bin)
|
|
395
|
+
) {
|
|
396
|
+
for (const [name, rel] of Object.entries(manifest.bin)) {
|
|
397
|
+
if (typeof rel === "string") push(name, rel);
|
|
398
|
+
}
|
|
399
|
+
return out;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const dir = path.join(root, "bin");
|
|
403
|
+
if (dirExists(dir)) {
|
|
404
|
+
for (const file of listFiles(dir, null)) {
|
|
405
|
+
push(file, path.join("bin", file));
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return out;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function resolveSettings(root, manifest, safeResolve) {
|
|
412
|
+
let rel = null;
|
|
413
|
+
if (typeof manifest.settings === "string") rel = manifest.settings;
|
|
414
|
+
else {
|
|
415
|
+
const conv = path.join(root, "settings.json");
|
|
416
|
+
if (_deps.existsSync(conv)) rel = "settings.json";
|
|
417
|
+
}
|
|
418
|
+
if (!rel) return null;
|
|
419
|
+
const absPath = safeResolve(rel, "settings");
|
|
420
|
+
if (!absPath || !_deps.existsSync(absPath)) return null;
|
|
421
|
+
return { file: rel, absPath };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ── small fs helpers (all through _deps) ─────────────────────────────────
|
|
425
|
+
|
|
426
|
+
function strOr(v, fallback) {
|
|
427
|
+
return typeof v === "string" ? v : fallback;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function dirExists(dir) {
|
|
431
|
+
try {
|
|
432
|
+
return _deps.existsSync(dir) && _deps.statSync(dir).isDirectory();
|
|
433
|
+
} catch {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function listDirs(dir) {
|
|
439
|
+
try {
|
|
440
|
+
return _deps
|
|
441
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
442
|
+
.filter((e) => e.isDirectory())
|
|
443
|
+
.map((e) => e.name);
|
|
444
|
+
} catch {
|
|
445
|
+
return [];
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function listFiles(dir, pattern) {
|
|
450
|
+
try {
|
|
451
|
+
return _deps
|
|
452
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
453
|
+
.filter((e) => e.isFile() && (!pattern || pattern.test(e.name)))
|
|
454
|
+
.map((e) => e.name);
|
|
455
|
+
} catch {
|
|
456
|
+
return [];
|
|
457
|
+
}
|
|
458
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collect installed plugins' `.mcp.json` servers so a plugin's MCP component
|
|
3
|
+
* reaches the agent's tool surface (Phase 3.3g — plugin MCP → agent chain).
|
|
4
|
+
*
|
|
5
|
+
* A plugin ships MCP servers in the same shape as a project `.mcp.json`: a
|
|
6
|
+
* top-level `mcpServers` (or `servers`) map of name → `{command,args,env,url,…}`.
|
|
7
|
+
* Those are folded into the agent's combined MCP client alongside the user's
|
|
8
|
+
* registered + project servers.
|
|
9
|
+
*
|
|
10
|
+
* SECURITY: an MCP stdio server spawns a command, so a plugin's `.mcp.json`
|
|
11
|
+
* carries the same RCE weight as its hooks/LSP components. Only TRUSTED plugins
|
|
12
|
+
* contribute (user/local scope auto-trusted; project scope must be
|
|
13
|
+
* `cc plugin trust`-ed at its exact version) — an untrusted project plugin from
|
|
14
|
+
* a cloned repo can NOT get a server spawned the moment the agent starts.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from "fs";
|
|
18
|
+
import { discoverPlugins } from "./scopes.js";
|
|
19
|
+
import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
|
|
20
|
+
|
|
21
|
+
export const _deps = { readFileSync: fs.readFileSync };
|
|
22
|
+
|
|
23
|
+
/** Accept `{ mcpServers: {...} }`, `{ servers: {...} }`, or a bare map. */
|
|
24
|
+
function normalizeServerMap(parsed) {
|
|
25
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
26
|
+
const block = parsed.mcpServers || parsed.servers || parsed;
|
|
27
|
+
if (!block || typeof block !== "object" || Array.isArray(block)) return {};
|
|
28
|
+
return block;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Merge every trusted, installed plugin's `.mcp.json` servers into one map,
|
|
33
|
+
* keyed by server name. A later plugin (in discovery precedence) wins on a name
|
|
34
|
+
* clash; callers merging this into their own servers keep their own on a clash.
|
|
35
|
+
*
|
|
36
|
+
* @param {object} [opts] { cwd, scopes }
|
|
37
|
+
* @returns {{ servers: Record<string,object>, sources: string[] }}
|
|
38
|
+
*/
|
|
39
|
+
export function collectPluginMcpServers(opts = {}) {
|
|
40
|
+
let plugins = [];
|
|
41
|
+
try {
|
|
42
|
+
plugins = discoverPlugins({ cwd: opts.cwd, scopes: opts.scopes });
|
|
43
|
+
} catch {
|
|
44
|
+
return { servers: {}, sources: [] };
|
|
45
|
+
}
|
|
46
|
+
// An MCP stdio server spawns a command — gate it behind trust so a cloned
|
|
47
|
+
// repo's project plugin can't get a server launched the moment cc starts.
|
|
48
|
+
const { trusted, skipped } = partitionByTrust(plugins);
|
|
49
|
+
warnUntrustedOnce(
|
|
50
|
+
skipped.filter((p) => p.manifest?.components?.mcp).map((p) => p.name),
|
|
51
|
+
"mcp",
|
|
52
|
+
);
|
|
53
|
+
const servers = {};
|
|
54
|
+
const sources = [];
|
|
55
|
+
for (const p of trusted) {
|
|
56
|
+
if (!p.manifest || p.manifest.ok !== true) continue;
|
|
57
|
+
const m = p.manifest.components?.mcp;
|
|
58
|
+
if (!m) continue;
|
|
59
|
+
let parsed = null;
|
|
60
|
+
if (m.absPath) {
|
|
61
|
+
try {
|
|
62
|
+
parsed = JSON.parse(_deps.readFileSync(m.absPath, "utf8"));
|
|
63
|
+
} catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
} else if (m.inline) {
|
|
67
|
+
// Inline MCP declared directly in the manifest — re-read the raw manifest
|
|
68
|
+
// so we get the server bodies (the normalized component keeps only counts).
|
|
69
|
+
try {
|
|
70
|
+
parsed = JSON.parse(
|
|
71
|
+
_deps.readFileSync(p.manifest.manifestPath, "utf8"),
|
|
72
|
+
);
|
|
73
|
+
parsed = parsed && typeof parsed === "object" ? parsed.mcp : null;
|
|
74
|
+
} catch {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const map = normalizeServerMap(parsed);
|
|
79
|
+
let added = 0;
|
|
80
|
+
for (const [name, cfg] of Object.entries(map)) {
|
|
81
|
+
if (!cfg || typeof cfg !== "object") continue;
|
|
82
|
+
if (name in servers) continue; // first plugin wins on a clash
|
|
83
|
+
servers[name] = cfg;
|
|
84
|
+
added++;
|
|
85
|
+
}
|
|
86
|
+
if (added > 0 && m.absPath) sources.push(m.absPath);
|
|
87
|
+
}
|
|
88
|
+
return { servers, sources };
|
|
89
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collect installed plugins' background-monitor declarations (Phase 3.3i /
|
|
3
|
+
* Phase 6 down payment — plugin Monitor component reaches a real supervisor).
|
|
4
|
+
*
|
|
5
|
+
* A plugin ships monitors in `monitors/monitors.json`:
|
|
6
|
+
* { "monitors": [
|
|
7
|
+
* { "name": "tests", "command": "npm", "args": ["test"],
|
|
8
|
+
* "mode": "interval", "intervalMs": 60000 },
|
|
9
|
+
* { "name": "logtail", "command": "tail", "args": ["-f","app.log"],
|
|
10
|
+
* "mode": "longRunning" }
|
|
11
|
+
* ] }
|
|
12
|
+
* Each monitor is a background process the supervisor spawns to watch logs /
|
|
13
|
+
* files / external state; its output can be injected into the next agent turn.
|
|
14
|
+
*
|
|
15
|
+
* SECURITY: a monitor spawns a command, so — exactly like hooks/LSP/MCP — only
|
|
16
|
+
* TRUSTED plugins contribute (user/local scope auto-trusted; project scope must
|
|
17
|
+
* be `cc plugin trust`-ed at its exact version). An untrusted cloned-repo plugin
|
|
18
|
+
* can NOT get a background process launched.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import fs from "fs";
|
|
22
|
+
import { discoverPlugins } from "./scopes.js";
|
|
23
|
+
import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
|
|
24
|
+
|
|
25
|
+
export const _deps = { readFileSync: fs.readFileSync };
|
|
26
|
+
|
|
27
|
+
const VALID_MODES = new Set(["interval", "longRunning"]);
|
|
28
|
+
|
|
29
|
+
/** Normalize one raw monitor entry into a validated descriptor, or null. */
|
|
30
|
+
function normalizeMonitor(plugin, raw) {
|
|
31
|
+
if (!raw || typeof raw !== "object") return null;
|
|
32
|
+
if (typeof raw.name !== "string" || raw.name === "") return null;
|
|
33
|
+
if (typeof raw.command !== "string" || raw.command === "") return null;
|
|
34
|
+
const mode = VALID_MODES.has(raw.mode) ? raw.mode : "interval";
|
|
35
|
+
const args = Array.isArray(raw.args) ? raw.args.map((a) => String(a)) : [];
|
|
36
|
+
const intervalMs =
|
|
37
|
+
Number.isFinite(raw.intervalMs) && raw.intervalMs > 0
|
|
38
|
+
? Math.floor(raw.intervalMs)
|
|
39
|
+
: null;
|
|
40
|
+
const env =
|
|
41
|
+
raw.env && typeof raw.env === "object" && !Array.isArray(raw.env)
|
|
42
|
+
? raw.env
|
|
43
|
+
: null;
|
|
44
|
+
return {
|
|
45
|
+
plugin: plugin.name,
|
|
46
|
+
scope: plugin.scope,
|
|
47
|
+
version: plugin.version,
|
|
48
|
+
// Namespaced so two plugins' "tests" monitors never collide.
|
|
49
|
+
id: `${plugin.name}:${raw.name}`,
|
|
50
|
+
name: raw.name,
|
|
51
|
+
command: raw.command,
|
|
52
|
+
args,
|
|
53
|
+
mode,
|
|
54
|
+
intervalMs,
|
|
55
|
+
env,
|
|
56
|
+
cwd: plugin.root,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Flatten every trusted, installed plugin's monitor declarations.
|
|
62
|
+
* @param {object} [opts] { cwd, scopes }
|
|
63
|
+
* @returns {Array<object>} validated monitor descriptors
|
|
64
|
+
*/
|
|
65
|
+
export function collectPluginMonitors(opts = {}) {
|
|
66
|
+
let plugins = [];
|
|
67
|
+
try {
|
|
68
|
+
plugins = discoverPlugins({ cwd: opts.cwd, scopes: opts.scopes });
|
|
69
|
+
} catch {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
// A monitor spawns a command — gate it behind trust.
|
|
73
|
+
const { trusted, skipped } = partitionByTrust(plugins);
|
|
74
|
+
warnUntrustedOnce(
|
|
75
|
+
skipped.filter((p) => p.manifest?.components?.monitors).map((p) => p.name),
|
|
76
|
+
"monitors",
|
|
77
|
+
);
|
|
78
|
+
const out = [];
|
|
79
|
+
for (const p of trusted) {
|
|
80
|
+
if (!p.manifest || p.manifest.ok !== true) continue;
|
|
81
|
+
const m = p.manifest.components?.monitors;
|
|
82
|
+
if (!m || !m.absPath) continue;
|
|
83
|
+
let parsed;
|
|
84
|
+
try {
|
|
85
|
+
parsed = JSON.parse(_deps.readFileSync(m.absPath, "utf8"));
|
|
86
|
+
} catch {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const list = Array.isArray(parsed?.monitors)
|
|
90
|
+
? parsed.monitors
|
|
91
|
+
: Array.isArray(parsed)
|
|
92
|
+
? parsed
|
|
93
|
+
: [];
|
|
94
|
+
for (const raw of list) {
|
|
95
|
+
const desc = normalizeMonitor(p, raw);
|
|
96
|
+
if (desc) out.push(desc);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|