chainlesschain 0.162.148 → 0.162.150
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-CBLvrjeG.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-C9pBYocA.js → ActionButton-DYv2GtE0.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BWAkeXxK.js → Analytics-CL6PZ-Ww.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CivFGH6B.js → AppLayout-xuyHt4uA.js} +3 -3
- package/src/assets/web-panel/assets/{Audit-Cm8hRpZm.js → Audit-DOvSVycq.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DXdFMsE8.js → Backup-DMhfTswr.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-uAwSTeql.js → BaseInput-CQxDdm63.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-KU8eeJJE.js → Chat-BEeNhK2a.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-8nul8eTq.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-C6AMDkjd.js → Checkbox-CL04O-ER.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-t2moDxcO.js → Codegen-C50o-n7x.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DCcsRRhN.js → Col-CH-bDkzs.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DtB-8SAC.js → Community-DqAIMvSe.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CZm8THYA.js → Compact-C43fo_my.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-LiQgC7g1.js → Compliance-BERnMrdP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CA8SAxht.js → Cowork-Baw3w1gp.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-CtRaF1wD.js → Cron-BEqAHF1-.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-6-br6Hub.js → Crosschain-CvnmKwK4.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Do2EccrL.js → DID-iMPxgVdt.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-wrXcbzGe.js → Dashboard--4SJX3YR.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-p3FsT245.js → Dropdown-BgLV6xgz.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D95A2L8q.js → EmailListRenderer-Bvb8oUD8.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-cLuJW2zo.js → FamilyGuardDashboard-DpXPjJB5.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Brgq_cbN.js → Federation-BbFfJ1Xi.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DWLCkNgh.js → FormItemContext-DeGq1B-q.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BBqUfQd6.js → GenericCardRenderer-CNW7s9zM.js} +1 -1
- package/src/assets/web-panel/assets/{Git-8F090w4I.js → Git-L1PjW-lT.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CCogEbxb.js → Governance-PK-X58to.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CfLD7v7C.js → Inference-BePWEH-d.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DDuHJewd.js → KnowledgeGraph-LF_sBvdL.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CE8KSEVC.js → Logs-BKkfk9hy.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-DmwpZmhT.js → Marketplace-BNMqUDla.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Bf7AazU8.js → McpTools-w4WPiyz2.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-D0QU_00S.js → Memory-DUKMVGNi.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-yXS9xdhx.js → MobileBridge-BWhiEyTJ.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BuDUoGUP.js → MobileProjects-Ocf5JEkX.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BYyHy28p.js → Mtc-D2B6MMhU.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-CK0aqpiZ.js → MtcAudit-DdYjCq1O.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-mNimKYeb.js → Multisig-Cb1hfuUZ.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-AioAJ0YA.js → NLProgramming-CTeImGp7.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CVVNCLHE.js → Notes-BjYNc7eQ.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-C4ABj-TK.js → NotificationSettings-Dn_NtRXQ.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DIp8Xcbd.js → OrderTableRenderer-DVYYIizh.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-uozl_gWK.js → Organization-Be2Kmr5j.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BCZOM2hK.js → Overflow-Cu_-qRlR.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CArcaiaj.js → P2P-DD2HoGzE.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Sgq2Srr8.js → PdhVaultBrowser-BRI7DVth.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYrQrXnH.js → Permissions-C9Srcs2M.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BzHStAbz.js → PersonalDataHub-CUqcMgo1.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-k7KqxX1M.js → Pipeline-HBrQGLXt.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-IumTUWqx.js → Privacy-D-B0vjwh.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BRyImYTf.js → ProjectInit-eTeLIu9e.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BFIqTBFk.js → ProjectSettings-DrRqCsC6.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Dpid9CDg.js → Projects-1G9DNOhI.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CyyFnUPs.js → Providers-RkTZzpxP.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C82cRx-X.js → QrScannerModal-BSCUgsgC.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DGxDF521.js → QuickAsk-CHgyM3Bz.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Cns-Am1y.js → Recommend-0lry4OZq.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-R7OqAIlg.js → RemoteSession-Cn45UroZ.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-D7mgPIYV.js → Reputation-Q-Zjb707.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B3lEURyd.js → Row-BdM70oda.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-9_UVrpBt.js → RssFeed-DmOAKKap.js} +2 -2
- package/src/assets/web-panel/assets/{Search-ClZeO80q.js → Search-Dwavbm07.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BNNJczEp.js → Security-DukXFCnk.js} +3 -3
- package/src/assets/web-panel/assets/{Services-C5SjrWUj.js → Services-BIlRnITu.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Ci_obQ-g.js → Skeleton-YYVQdhhQ.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DdebN15P.js → Skills-B8_iYHz2.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-OinZP2vi.js → Sla-BdVrhT31.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CxzbNF51.js → SpeechSettings-CD3ggRx7.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D06N_66b.js → SyncSettings-Bp02VZFH.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BdEdW0AZ.js → Tasks-DEVmCEsr.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-F1ctKmPa.js → Templates-B6czWc4N.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CVz1Urot.js → Tenant-BCJLv17r.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-CD132d2Y.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-Doitzjmf.js → TimelineRenderer-ZB-CvkZl.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BpyVRpVA.js → Tokens-BnSY0S-s.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-nWhWYTbU.js → Trigger-VZw9Oy2w.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DK_G-wLx.js → Trust-pZq7Hjd2.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B5yBUojO.js → UkeySign-DnQaqk4q.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C5D51qAe.js → VideoEditing-BEUfmvJV.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CLbL9K7v.js → Wallet-CoZKMXJ2.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DZUelI3V.js → WebAuthn-CBOGVx1I.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-8RPxt4KW.js → WorkflowEditor-huSKn7TT.js} +1 -1
- package/src/assets/web-panel/assets/{chat-4N4tOA3d.js → chat-A_3w6FBO.js} +1 -1
- package/src/assets/web-panel/assets/{colors-zbbGVrua.js → colors-D1Bem9Oy.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-D7iMkbnE.js → compact-item-CJYJj0oO.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CBzPJv-w.js → createContext-BkbFiKT4.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-WDr3_M_N.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-bHdrQSqZ.js → hasIn-CKvUafVY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DbE5D0WL.js → index-AjrSPnvv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCr0geV9.js → index-B8i0lViZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiG1IZao.js → index-B9Svn0zc.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1s0Bz9O.js → index-BHdHSX06.js} +1 -1
- package/src/assets/web-panel/assets/{index-QFObYeo1.js → index-BHdailyo.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnBOpqv3.js → index-BJwlEnYo.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdmMoYN1.js → index-BOoAQjJz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUyjUkY7.js → index-BSgWxAOG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqdPyWm5.js → index-BdutMsne.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmqlJed-.js → index-BiDWxzbn.js} +1 -1
- package/src/assets/web-panel/assets/{index-S74FpAIn.js → index-By5a0T_W.js} +1 -1
- package/src/assets/web-panel/assets/{index-kmh1TxRa.js → index-C-WIY-FQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTIjjXry.js → index-C37f6iey.js} +1 -1
- package/src/assets/web-panel/assets/{index-BS4_Mwk6.js → index-C9bWmTcO.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBNr77E1.js → index-CBaosWRO.js} +1 -1
- package/src/assets/web-panel/assets/{index-QNAG4JtR.js → index-CBlBVJ_m.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bqmx24kh.js → index-CD62EEGo.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQfu1U78.js → index-CUj-l7GM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJ4ZGyW0.js → index-CVS8Ymuq.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ci009ijp.js → index-ChkXUj3f.js} +1 -1
- package/src/assets/web-panel/assets/{index-oDKNgCsP.js → index-Cj0OZ-37.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZb8F8YG.js → index-Co3OFL0t.js} +1 -1
- package/src/assets/web-panel/assets/{index-308gCGh7.js → index-Crp8CbRg.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPxkoKgA.js → index-D22zNXiS.js} +1 -1
- package/src/assets/web-panel/assets/{index-xYCm6W2h.js → index-DTs_D1OL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtbbdLnf.js → index-DfgCdFrN.js} +3 -3
- package/src/assets/web-panel/assets/{index-BsKehmmS.js → index-DjYrecNb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkxK86vf.js → index-DpW9cf3e.js} +1 -1
- package/src/assets/web-panel/assets/index-Dwix3p4g.js +1 -0
- package/src/assets/web-panel/assets/{index-CvRMA9uT.js → index-F--HuPG1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE9j5YgT.js → index-Fuk6t_3K.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7b1hGry.js → index-JEAGMii9.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1YDh7Wr.js → index-PF-mpv8K.js} +1 -1
- package/src/assets/web-panel/assets/index-SHaoZ0CA.js +1 -0
- package/src/assets/web-panel/assets/{index-CQ6NcfWz.js → index-b-sbVdjQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyoNgbXl.js → index-pyvMVX8r.js} +1 -1
- package/src/assets/web-panel/assets/{index-DISWAYce.js → index-qNTtOVi_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4XKpj6c.js → index-tgO8iza6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CahryTX0.js → index-ttDQVhZy.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BD55yNBt.js → initDefaultProps-DtrnfsZH.js} +1 -1
- package/src/assets/web-panel/assets/{motion-B5Bbe77U.js → motion-D4zqSaX3.js} +1 -1
- package/src/assets/web-panel/assets/{move-CTvIqHEH.js → move-CGFGOUQj.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BkNyPQKB.js → mtc-parser-DH2HvkJl.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BaXJXgHA.js → omit-jJ2at5HX.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Bw6-YTxH.js → pickAttrs-CRyMT1Fv.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BLdbkLqJ.js → placementArrow-DZ_CX0Pt.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-IVYkzntU.js → responsiveObserve-wruHk-h5.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DFMFwwtY.js → slide-BGgx8q67.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CURYRtZ1.js → statusUtils-Do5M7gKm.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cfz63s1r.js → styleChecker-CRa-GZpX.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-COJL0KE3.js → useFlexGapSupport-BxPcq6pj.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-YLLojQQf.js → useFs-v5O2ZcJm.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BF_21qUC.js → usePersonalDataHub--z2FU0Px.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-9ZpkA7bb.js → vnode-1emidVKd.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DhtAfhl8.js → zoom-CcJaRyHs.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/a2a.js +19 -4
- package/src/commands/activitypub.js +20 -3
- package/src/commands/agent.js +71 -10
- package/src/commands/audit.js +40 -24
- package/src/commands/codeintel.js +320 -0
- package/src/commands/collab.js +15 -2
- package/src/commands/compliance.js +5 -0
- package/src/commands/config.js +4 -1
- package/src/commands/dao.js +86 -14
- package/src/commands/dev.js +2 -0
- package/src/commands/did.js +7 -1
- package/src/commands/dlp.js +23 -1
- package/src/commands/economy.js +29 -3
- package/src/commands/eval.js +255 -0
- package/src/commands/evomap.js +26 -0
- package/src/commands/governance.js +17 -3
- package/src/commands/hardening.js +18 -0
- package/src/commands/incentive.js +5 -0
- package/src/commands/init.js +46 -2
- package/src/commands/kg.js +4 -0
- package/src/commands/loop.js +6 -1
- package/src/commands/lowcode.js +15 -2
- package/src/commands/marketplace.js +40 -6
- package/src/commands/matrix.js +20 -1
- package/src/commands/memory.js +4 -1
- package/src/commands/mtc.js +7 -1
- package/src/commands/nostr.js +31 -4
- package/src/commands/note.js +7 -4
- package/src/commands/plugin.js +474 -0
- package/src/commands/pqc.js +5 -0
- package/src/commands/reputation.js +10 -1
- package/src/commands/scim.js +6 -0
- package/src/commands/session.js +32 -10
- package/src/commands/siem.js +11 -0
- package/src/commands/sla.js +18 -3
- package/src/commands/social.js +38 -5
- package/src/commands/sso.js +10 -2
- package/src/commands/stress.js +23 -4
- package/src/commands/team.js +463 -0
- package/src/commands/tech.js +2 -0
- package/src/commands/tenant.js +10 -1
- package/src/commands/terraform.js +6 -0
- package/src/commands/update.js +1 -1
- package/src/commands/zkp.js +20 -0
- package/src/gateways/ws/message-dispatcher.js +48 -2
- package/src/gateways/ws/remote-session-protocol.js +140 -42
- package/src/gateways/ws/ws-server.js +1 -1
- package/src/harness/jsonl-session-store.js +12 -5
- package/src/harness/plugin-manager.js +16 -4
- package/src/harness/prompt-compressor.js +27 -1
- package/src/harness/remote-command-ledger.js +189 -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/harness/worktree-isolator.js +16 -6
- package/src/index.js +6 -0
- package/src/lib/__tests__/logger.test.js +5 -2
- package/src/lib/a2a-protocol.js +25 -1
- package/src/lib/activitypub-bridge.js +61 -0
- package/src/lib/agent-core.js +4 -0
- package/src/lib/agent-economy.js +262 -29
- package/src/lib/agent-network.js +7 -1
- 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 +282 -0
- package/src/lib/agent-team/team-worktree.js +236 -0
- package/src/lib/agents.js +18 -0
- package/src/lib/async-hook-supervisor.cjs +391 -0
- package/src/lib/audit-logger.js +7 -6
- package/src/lib/autonomous-developer.js +60 -0
- package/src/lib/chat-core.js +17 -4
- package/src/lib/collaboration-governance.js +56 -0
- package/src/lib/community-governance.js +68 -0
- package/src/lib/compliance-manager.js +63 -0
- package/src/lib/cross-chain.js +5 -0
- package/src/lib/dao-governance.js +151 -2
- package/src/lib/did-manager.js +23 -10
- package/src/lib/dlp-engine.js +70 -0
- package/src/lib/eval/runner.js +251 -0
- package/src/lib/eval/tasks.js +626 -0
- package/src/lib/eval/trend.js +200 -0
- package/src/lib/evolution-system.js +92 -0
- package/src/lib/evomap-federation.js +55 -0
- package/src/lib/evomap-governance.js +94 -0
- package/src/lib/fatal-handler.js +17 -1
- package/src/lib/hardening-manager.js +73 -0
- package/src/lib/hierarchical-memory.js +14 -8
- package/src/lib/knowledge-exporter.js +8 -2
- package/src/lib/knowledge-graph.js +79 -0
- package/src/lib/llm-providers.js +23 -14
- package/src/lib/logger.js +4 -1
- package/src/lib/lsp/__tests__/benchmark.test.js +88 -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 +269 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +333 -0
- package/src/lib/lsp/__tests__/lsp-server-registry.test.js +128 -0
- package/src/lib/lsp/benchmark.js +257 -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 +339 -0
- package/src/lib/lsp/lsp-manager.js +375 -0
- package/src/lib/lsp/lsp-server-registry.js +196 -0
- package/src/lib/matrix-bridge.js +84 -0
- package/src/lib/memory-manager.js +5 -3
- package/src/lib/nostr-bridge.js +53 -0
- package/src/lib/permission-engine.js +22 -4
- package/src/lib/plugin-monitor-supervisor.js +238 -0
- package/src/lib/plugin-runtime/__tests__/remote-source.test.js +256 -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 +396 -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 +152 -0
- package/src/lib/plugin-runtime/reload.js +79 -0
- package/src/lib/plugin-runtime/remote-source.js +240 -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/pqc-manager.js +64 -0
- package/src/lib/reputation-optimizer.js +101 -0
- package/src/lib/sandbox-egress-proxy.js +262 -0
- package/src/lib/sandbox-fs-policy.js +112 -0
- package/src/lib/sandbox-network-policy.js +151 -0
- package/src/lib/sandbox-v2.js +24 -18
- package/src/lib/scim-manager.js +36 -0
- package/src/lib/session-manager.js +5 -2
- package/src/lib/settings-hook-events.cjs +78 -6
- package/src/lib/settings-hooks.cjs +30 -3
- package/src/lib/settings-loader.cjs +35 -1
- package/src/lib/siem-exporter.js +45 -0
- package/src/lib/skill-loader.js +38 -2
- package/src/lib/skill-marketplace.js +83 -0
- package/src/lib/sla-manager.js +88 -0
- package/src/lib/social-manager.js +74 -0
- package/src/lib/stress-tester.js +65 -0
- package/src/lib/tech-learning-engine.js +75 -0
- package/src/lib/telemetry/span-recorder.js +237 -0
- package/src/lib/tenant-saas.js +107 -0
- package/src/lib/terraform-manager.js +67 -0
- package/src/lib/token-incentive.js +180 -29
- package/src/lib/wallet-manager.js +81 -35
- package/src/lib/zkp-engine.js +87 -0
- package/src/repl/agent-repl.js +417 -6
- package/src/runtime/__tests__/auto-pin.test.js +104 -0
- package/src/runtime/agent-core.js +771 -104
- package/src/runtime/auto-pin.js +117 -0
- 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 +388 -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/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,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LSPManager — pools language servers by (projectRoot, languageId), syncs open
|
|
3
|
+
* documents, translates positions, and recovers from server crashes.
|
|
4
|
+
*
|
|
5
|
+
* One `LSPClient` is expensive (spawns a process + indexes the project), so we
|
|
6
|
+
* lazily start and reuse one per (root, language) family. The manager owns:
|
|
7
|
+
* - server lifecycle (start / reuse / bounded crash-loop restart / disposeAll)
|
|
8
|
+
* - textDocument/didOpen + didChange (servers only answer about open docs)
|
|
9
|
+
* - diagnostics collection (servers push them via publishDiagnostics)
|
|
10
|
+
* - 1-based (user) ⇄ 0-based (LSP) position conversion, done HERE only
|
|
11
|
+
*
|
|
12
|
+
* The high-level `code-intelligence.js` calls into this; nothing above the
|
|
13
|
+
* manager should touch raw LSP positions/URIs.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import fs from "fs";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import { LSPClient, pathToFileUri, normalizeUri } from "./lsp-client.js";
|
|
19
|
+
import { languageIdForFile, resolveServer } from "./lsp-server-registry.js";
|
|
20
|
+
|
|
21
|
+
export const _deps = {
|
|
22
|
+
readFileSync: fs.readFileSync,
|
|
23
|
+
existsSync: fs.existsSync,
|
|
24
|
+
createClient: (opts) => new LSPClient(opts),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export class LSPManager {
|
|
28
|
+
/**
|
|
29
|
+
* @param {object} [opts]
|
|
30
|
+
* @param {string} [opts.projectRoot] default root; per-file root inferred otherwise
|
|
31
|
+
* @param {number} [opts.maxRestarts] crashes within the window before a server
|
|
32
|
+
* is quarantined (degrade to text search) instead of re-spawned. Default 3.
|
|
33
|
+
* @param {number} [opts.restartWindowMs] sliding window for the crash count.
|
|
34
|
+
* A server that stays healthy for this long has its crash history pruned and
|
|
35
|
+
* can restart fresh. Default 30000.
|
|
36
|
+
* @param {() => number} [opts.now] injected clock (deterministic crash-loop tests)
|
|
37
|
+
*/
|
|
38
|
+
constructor(opts = {}) {
|
|
39
|
+
this.projectRoot = opts.projectRoot || process.cwd();
|
|
40
|
+
/** key `${root}::${serverId}` → { client, languageId, root, restarts } */
|
|
41
|
+
this._servers = new Map();
|
|
42
|
+
/**
|
|
43
|
+
* key → in-flight `_startServer` promise. A server start awaits a real
|
|
44
|
+
* process spawn + `initialize` handshake, and the read-only agent batch
|
|
45
|
+
* fires several `code_intelligence` calls at once (see agent-core.js), so a
|
|
46
|
+
* bare Map.get(miss)→await→Map.set pool double-spawns when two callers race
|
|
47
|
+
* the same key: the second `_servers.set` orphans the first process (leaked
|
|
48
|
+
* OS process + a still-attached diagnostics listener writing stale state).
|
|
49
|
+
* Sharing one in-flight promise per key collapses the race to a single spawn.
|
|
50
|
+
*/
|
|
51
|
+
this._starting = new Map();
|
|
52
|
+
/** open document uri → { version, languageId } */
|
|
53
|
+
this._openDocs = new Map();
|
|
54
|
+
/** uri → latest diagnostics array */
|
|
55
|
+
this._diagnostics = new Map();
|
|
56
|
+
/** uri → { count, at } publish bookkeeping (for settle/readiness) */
|
|
57
|
+
this._diagMeta = new Map();
|
|
58
|
+
// Crash-loop guard: a language server that dies is lazily re-spawned on the
|
|
59
|
+
// next request (ensureFor), but a server that crashes on startup would then
|
|
60
|
+
// thrash — re-spawned on EVERY request forever. Bound it: at most
|
|
61
|
+
// `maxRestarts` crashes inside a sliding `restartWindowMs`, after which the
|
|
62
|
+
// server is quarantined (callers degrade to text search) until the window
|
|
63
|
+
// clears. `restarts` was previously a dead field; this makes it real.
|
|
64
|
+
this.maxRestarts = Number.isFinite(opts.maxRestarts) ? opts.maxRestarts : 3;
|
|
65
|
+
this.restartWindowMs = Number.isFinite(opts.restartWindowMs)
|
|
66
|
+
? opts.restartWindowMs
|
|
67
|
+
: 30000;
|
|
68
|
+
this._now = typeof opts.now === "function" ? opts.now : () => Date.now();
|
|
69
|
+
/** key → array of recent crash timestamps (survives entry re-spawn) */
|
|
70
|
+
this._crashLog = new Map();
|
|
71
|
+
this._disposed = false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Recent crash timestamps for `key`, pruned to the sliding window. */
|
|
75
|
+
_recentCrashes(key) {
|
|
76
|
+
const cutoff = this._now() - this.restartWindowMs;
|
|
77
|
+
const pruned = (this._crashLog.get(key) || []).filter((t) => t >= cutoff);
|
|
78
|
+
if (pruned.length) this._crashLog.set(key, pruned);
|
|
79
|
+
else this._crashLog.delete(key);
|
|
80
|
+
return pruned;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Ensure a server is running for `filePath` and the file is open. Returns
|
|
85
|
+
* `{ client, uri, languageId }`, or `{ unavailable: true, reason }` when no
|
|
86
|
+
* server is installed (caller degrades to text search).
|
|
87
|
+
*/
|
|
88
|
+
async ensureFor(
|
|
89
|
+
filePath,
|
|
90
|
+
{ root, waitReady = false, readyTimeoutMs = 8000 } = {},
|
|
91
|
+
) {
|
|
92
|
+
if (this._disposed) throw new Error("LSPManager disposed");
|
|
93
|
+
const abs = path.resolve(filePath);
|
|
94
|
+
const languageId = languageIdForFile(abs);
|
|
95
|
+
if (!languageId) {
|
|
96
|
+
return {
|
|
97
|
+
unavailable: true,
|
|
98
|
+
reason: `no language mapping for ${path.extname(abs) || "file"}`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const projectRoot = root || this._inferRoot(abs);
|
|
102
|
+
const resolved = resolveServer(languageId, projectRoot);
|
|
103
|
+
if (!resolved) {
|
|
104
|
+
return {
|
|
105
|
+
unavailable: true,
|
|
106
|
+
reason: `no language server installed for ${languageId} (install one, e.g. typescript-language-server)`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const key = `${projectRoot}::${resolved.id}`;
|
|
111
|
+
let entry = this._servers.get(key);
|
|
112
|
+
if (!entry || !entry.client.running) {
|
|
113
|
+
// A dead/absent server is (re)started — but if it has crash-looped past
|
|
114
|
+
// the cap inside the window, quarantine it so we don't re-spawn on every
|
|
115
|
+
// request. The caller degrades to text search until the window clears.
|
|
116
|
+
const crashes = this._recentCrashes(key);
|
|
117
|
+
if (crashes.length >= this.maxRestarts) {
|
|
118
|
+
const retryInMs = this.restartWindowMs - (this._now() - crashes[0]);
|
|
119
|
+
return {
|
|
120
|
+
unavailable: true,
|
|
121
|
+
reason:
|
|
122
|
+
`${resolved.id} crash-looped (${crashes.length} crashes in ` +
|
|
123
|
+
`${Math.round(this.restartWindowMs / 1000)}s) — quarantined, ` +
|
|
124
|
+
`retrying in ~${Math.max(0, Math.round(retryInMs / 1000))}s`,
|
|
125
|
+
quarantined: true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
entry = await this._getOrStartServer(key, resolved, projectRoot);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const uri = pathToFileUri(abs);
|
|
132
|
+
this._ensureOpen(entry.client, uri, abs, languageId);
|
|
133
|
+
|
|
134
|
+
// One-shot callers (the `cc code-intel` CLI) spawn a cold server per run;
|
|
135
|
+
// the server publishes an empty diagnostics set BEFORE it finishes loading
|
|
136
|
+
// the tsconfig project, so an immediate definition/references/diagnostics
|
|
137
|
+
// query sees a partial project. Waiting for the first publish for THIS doc
|
|
138
|
+
// is a reliable "project loaded far enough" signal. Long-lived callers (the
|
|
139
|
+
// agent session) keep the server warm and pass waitReady:false.
|
|
140
|
+
if (waitReady) {
|
|
141
|
+
await this._waitForFirstPublish(uri, readyTimeoutMs);
|
|
142
|
+
}
|
|
143
|
+
return { client: entry.client, uri, languageId, root: projectRoot };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Resolve once the server has published diagnostics for `uri` at least once, or timeout. */
|
|
147
|
+
_waitForFirstPublish(rawUri, timeoutMs) {
|
|
148
|
+
const uri = normalizeUri(rawUri);
|
|
149
|
+
if ((this._diagMeta.get(uri)?.count || 0) > 0) return Promise.resolve();
|
|
150
|
+
return new Promise((resolve) => {
|
|
151
|
+
const started = Date.now();
|
|
152
|
+
const timer = setInterval(() => {
|
|
153
|
+
if (
|
|
154
|
+
(this._diagMeta.get(uri)?.count || 0) > 0 ||
|
|
155
|
+
Date.now() - started >= timeoutMs
|
|
156
|
+
) {
|
|
157
|
+
clearInterval(timer);
|
|
158
|
+
resolve();
|
|
159
|
+
}
|
|
160
|
+
}, 40);
|
|
161
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Dedupe concurrent starts for `key`: return the in-flight start promise if
|
|
167
|
+
* one exists, otherwise begin one and register it so racing callers share it.
|
|
168
|
+
* The promise self-evicts on settle (resolve OR reject), so a failed start
|
|
169
|
+
* doesn't wedge the key — the next request retries cleanly.
|
|
170
|
+
*/
|
|
171
|
+
_getOrStartServer(key, resolved, projectRoot) {
|
|
172
|
+
const inflight = this._starting.get(key);
|
|
173
|
+
if (inflight) return inflight;
|
|
174
|
+
const p = this._startServer(key, resolved, projectRoot).finally(() => {
|
|
175
|
+
this._starting.delete(key);
|
|
176
|
+
});
|
|
177
|
+
this._starting.set(key, p);
|
|
178
|
+
return p;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async _startServer(key, resolved, projectRoot) {
|
|
182
|
+
const client = _deps.createClient({
|
|
183
|
+
command: resolved.command,
|
|
184
|
+
args: resolved.args,
|
|
185
|
+
rootPath: projectRoot,
|
|
186
|
+
});
|
|
187
|
+
// Collect pushed diagnostics keyed by document, tracking publish count/time
|
|
188
|
+
// so callers can wait for the project to load (first publish) and settle
|
|
189
|
+
// (last publish) rather than racing the initial empty set tsserver emits.
|
|
190
|
+
client.on("notify:textDocument/publishDiagnostics", (params) => {
|
|
191
|
+
if (params && params.uri) {
|
|
192
|
+
const key = normalizeUri(params.uri);
|
|
193
|
+
this._diagnostics.set(key, params.diagnostics || []);
|
|
194
|
+
const prev = this._diagMeta.get(key) || { count: 0 };
|
|
195
|
+
this._diagMeta.set(key, { count: prev.count + 1, at: Date.now() });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
const entry = {
|
|
199
|
+
client,
|
|
200
|
+
serverId: resolved.id,
|
|
201
|
+
root: projectRoot,
|
|
202
|
+
// How many times THIS key has already been (re)started — crash history
|
|
203
|
+
// survives entry replacement via _crashLog, so this reflects it.
|
|
204
|
+
restarts: (this._crashLog.get(key) || []).length,
|
|
205
|
+
};
|
|
206
|
+
// On crash: record the crash timestamp (bounds the re-spawn loop via the
|
|
207
|
+
// crash-loop guard in ensureFor) and drop this project's open docs so they
|
|
208
|
+
// re-sync against the fresh server on the next ensureFor.
|
|
209
|
+
client.on("exit", () => {
|
|
210
|
+
const log = this._crashLog.get(key) || [];
|
|
211
|
+
log.push(this._now());
|
|
212
|
+
this._crashLog.set(key, log);
|
|
213
|
+
// Drop this project's open docs AND their published-diagnostics
|
|
214
|
+
// bookkeeping. Leaving stale _diagMeta/_diagnostics behind makes the
|
|
215
|
+
// readiness waiters resolve on the PRE-CRASH publish — _waitForFirstPublish
|
|
216
|
+
// sees count>0 and returns immediately; waitForDiagnostics sees an old
|
|
217
|
+
// `at` and settles at once — so a query after a crash+restart returns
|
|
218
|
+
// stale results and races the fresh server's initial empty set, the very
|
|
219
|
+
// thing the publish-count machinery exists to prevent. (_openDocs keys are
|
|
220
|
+
// the raw file URI; _diagMeta/_diagnostics use the normalized key.)
|
|
221
|
+
for (const [uri, meta] of this._openDocs) {
|
|
222
|
+
if (meta.root === projectRoot) {
|
|
223
|
+
this._openDocs.delete(uri);
|
|
224
|
+
const nuri = normalizeUri(uri);
|
|
225
|
+
this._diagnostics.delete(nuri);
|
|
226
|
+
this._diagMeta.delete(nuri);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
await client.start();
|
|
231
|
+
this._servers.set(key, entry);
|
|
232
|
+
return entry;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
_ensureOpen(client, uri, abs, languageId) {
|
|
236
|
+
const existing = this._openDocs.get(uri);
|
|
237
|
+
let text = "";
|
|
238
|
+
try {
|
|
239
|
+
text = _deps.readFileSync(abs, "utf8");
|
|
240
|
+
} catch {
|
|
241
|
+
text = "";
|
|
242
|
+
}
|
|
243
|
+
if (!existing) {
|
|
244
|
+
client.notify("textDocument/didOpen", {
|
|
245
|
+
textDocument: { uri, languageId, version: 1, text },
|
|
246
|
+
});
|
|
247
|
+
this._openDocs.set(uri, {
|
|
248
|
+
version: 1,
|
|
249
|
+
languageId,
|
|
250
|
+
root: client.rootPath,
|
|
251
|
+
});
|
|
252
|
+
} else {
|
|
253
|
+
// File may have changed on disk since first open — push the latest text so
|
|
254
|
+
// definitions/diagnostics reflect current state (edit-freshness).
|
|
255
|
+
const version = existing.version + 1;
|
|
256
|
+
client.notify("textDocument/didChange", {
|
|
257
|
+
textDocument: { uri, version },
|
|
258
|
+
contentChanges: [{ text }],
|
|
259
|
+
});
|
|
260
|
+
this._openDocs.set(uri, { ...existing, version });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Notify the server that a document changed on disk (after an edit). */
|
|
265
|
+
async didChangeFile(filePath, { root } = {}) {
|
|
266
|
+
const abs = path.resolve(filePath);
|
|
267
|
+
const uri = pathToFileUri(abs);
|
|
268
|
+
if (!this._openDocs.has(uri)) {
|
|
269
|
+
// Not open yet — opening it (via ensureFor) will read fresh text anyway.
|
|
270
|
+
return this.ensureFor(abs, { root });
|
|
271
|
+
}
|
|
272
|
+
const ready = await this.ensureFor(abs, { root });
|
|
273
|
+
return ready;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Latest diagnostics for a file (may be empty). Server pushes asynchronously. */
|
|
277
|
+
getDiagnostics(filePath) {
|
|
278
|
+
const uri = normalizeUri(pathToFileUri(path.resolve(filePath)));
|
|
279
|
+
return this._diagnostics.get(uri) || [];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Wait for diagnostics for `filePath` to publish AND settle, then return the
|
|
284
|
+
* latest set. tsserver publishes an initial (often empty) set the moment a doc
|
|
285
|
+
* opens, then republishes once the project is loaded — resolving on the first
|
|
286
|
+
* publish would return stale/empty results. So we wait for at least one
|
|
287
|
+
* publish, then a `settleMs` quiet window with no further publishes (bounded
|
|
288
|
+
* by `timeoutMs`), and return whatever was published last.
|
|
289
|
+
*/
|
|
290
|
+
async waitForDiagnostics(
|
|
291
|
+
filePath,
|
|
292
|
+
{ timeoutMs = 3000, settleMs = 600 } = {},
|
|
293
|
+
) {
|
|
294
|
+
const uri = normalizeUri(pathToFileUri(path.resolve(filePath)));
|
|
295
|
+
return new Promise((resolve) => {
|
|
296
|
+
const started = Date.now();
|
|
297
|
+
const timer = setInterval(() => {
|
|
298
|
+
const meta = this._diagMeta.get(uri);
|
|
299
|
+
const elapsed = Date.now() - started;
|
|
300
|
+
const settled =
|
|
301
|
+
meta && meta.count > 0 && Date.now() - meta.at >= settleMs;
|
|
302
|
+
if (settled || elapsed >= timeoutMs) {
|
|
303
|
+
clearInterval(timer);
|
|
304
|
+
resolve(this._diagnostics.get(uri) || []);
|
|
305
|
+
}
|
|
306
|
+
}, 50);
|
|
307
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Infer the project root for a file: nearest ancestor with package.json / .git / go.mod / Cargo.toml. */
|
|
312
|
+
_inferRoot(abs) {
|
|
313
|
+
let dir = path.dirname(abs);
|
|
314
|
+
const markers = [
|
|
315
|
+
"package.json",
|
|
316
|
+
".git",
|
|
317
|
+
"go.mod",
|
|
318
|
+
"Cargo.toml",
|
|
319
|
+
"tsconfig.json",
|
|
320
|
+
"pyproject.toml",
|
|
321
|
+
];
|
|
322
|
+
for (let i = 0; i < 12; i++) {
|
|
323
|
+
for (const m of markers) {
|
|
324
|
+
if (_deps.existsSync(path.join(dir, m))) return dir;
|
|
325
|
+
}
|
|
326
|
+
const parent = path.dirname(dir);
|
|
327
|
+
if (parent === dir) break;
|
|
328
|
+
dir = parent;
|
|
329
|
+
}
|
|
330
|
+
return this.projectRoot;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Shut down every server and clear state. Idempotent. */
|
|
334
|
+
async disposeAll() {
|
|
335
|
+
this._disposed = true;
|
|
336
|
+
const clients = [...this._servers.values()].map((e) => e.client);
|
|
337
|
+
this._servers.clear();
|
|
338
|
+
this._openDocs.clear();
|
|
339
|
+
this._diagnostics.clear();
|
|
340
|
+
this._diagMeta.clear();
|
|
341
|
+
await Promise.all(clients.map((c) => c.stop().catch(() => {})));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
get activeServerCount() {
|
|
345
|
+
return this._servers.size;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** Snapshot of running servers: `[{ serverId, root, pid }]` (for bench / doctor). */
|
|
349
|
+
listServers() {
|
|
350
|
+
const out = [];
|
|
351
|
+
for (const entry of this._servers.values()) {
|
|
352
|
+
out.push({
|
|
353
|
+
serverId: entry.serverId,
|
|
354
|
+
root: entry.root,
|
|
355
|
+
pid: entry.client.pid,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
return out;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ---- position helpers (the ONLY place that converts between coordinate spaces) ----
|
|
363
|
+
|
|
364
|
+
/** 1-based user position → 0-based LSP position. */
|
|
365
|
+
export function toLspPosition({ line, col }) {
|
|
366
|
+
return {
|
|
367
|
+
line: Math.max(0, (line || 1) - 1),
|
|
368
|
+
character: Math.max(0, (col || 1) - 1),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** 0-based LSP position → 1-based user position. */
|
|
373
|
+
export function fromLspPosition(pos) {
|
|
374
|
+
return { line: (pos?.line || 0) + 1, col: (pos?.character || 0) + 1 };
|
|
375
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LSP server registry — maps a language id to the language-server command that
|
|
3
|
+
* serves it, and probes whether that command is actually installed.
|
|
4
|
+
*
|
|
5
|
+
* Design constraint (see optimization plan): the CLI must NOT depend on any
|
|
6
|
+
* language server as an npm dependency (they are heavy and language-specific).
|
|
7
|
+
* Instead we DETECT servers on PATH / `node_modules/.bin`, and when none is
|
|
8
|
+
* available the caller degrades gracefully to text search. Plugins may register
|
|
9
|
+
* extra servers via `.lsp.json`.
|
|
10
|
+
*
|
|
11
|
+
* Pure + injectable: filesystem/exec probing goes through `_deps` so the
|
|
12
|
+
* detection logic is unit-testable without a real toolchain.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from "fs";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import { execFileSync } from "child_process";
|
|
18
|
+
|
|
19
|
+
export const _deps = {
|
|
20
|
+
existsSync: fs.existsSync,
|
|
21
|
+
execFileSync,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Extension → LSP languageId. Kept small and explicit; unknown extensions get
|
|
25
|
+
// no LSP (text-search fallback).
|
|
26
|
+
const EXTENSION_LANGUAGE = {
|
|
27
|
+
".ts": "typescript",
|
|
28
|
+
".tsx": "typescriptreact",
|
|
29
|
+
".mts": "typescript",
|
|
30
|
+
".cts": "typescript",
|
|
31
|
+
".js": "javascript",
|
|
32
|
+
".jsx": "javascriptreact",
|
|
33
|
+
".mjs": "javascript",
|
|
34
|
+
".cjs": "javascript",
|
|
35
|
+
".py": "python",
|
|
36
|
+
".pyi": "python",
|
|
37
|
+
".go": "go",
|
|
38
|
+
".rs": "rust",
|
|
39
|
+
".java": "java",
|
|
40
|
+
".vue": "vue",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// languageId → server definition. `bin` names are tried in order; the first one
|
|
44
|
+
// resolvable wins. `args` is passed verbatim. Several languageIds can share one
|
|
45
|
+
// server family (TS server handles JS too).
|
|
46
|
+
const BUILTIN_SERVERS = {
|
|
47
|
+
typescript: tsServer(),
|
|
48
|
+
typescriptreact: tsServer(),
|
|
49
|
+
javascript: tsServer(),
|
|
50
|
+
javascriptreact: tsServer(),
|
|
51
|
+
python: {
|
|
52
|
+
id: "pyright",
|
|
53
|
+
bins: ["pyright-langserver", "pylsp"],
|
|
54
|
+
// pyright wants --stdio; pylsp defaults to stdio with no args. Resolved per-bin.
|
|
55
|
+
argsFor: (bin) => (bin.startsWith("pyright") ? ["--stdio"] : []),
|
|
56
|
+
},
|
|
57
|
+
go: { id: "gopls", bins: ["gopls"], argsFor: () => [] },
|
|
58
|
+
rust: { id: "rust-analyzer", bins: ["rust-analyzer"], argsFor: () => [] },
|
|
59
|
+
java: { id: "jdtls", bins: ["jdtls"], argsFor: () => [] },
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function tsServer() {
|
|
63
|
+
return {
|
|
64
|
+
id: "typescript-language-server",
|
|
65
|
+
bins: ["typescript-language-server"],
|
|
66
|
+
argsFor: () => ["--stdio"],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Registry of plugin-contributed servers, keyed by languageId. Wins over
|
|
71
|
+
// builtins if a plugin explicitly overrides.
|
|
72
|
+
const pluginServers = new Map();
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Register an extra language server (from a plugin `.lsp.json`).
|
|
76
|
+
* @param {object} def { languageId, extensions?, command, args?, id? }
|
|
77
|
+
*/
|
|
78
|
+
export function registerLanguageServer(def) {
|
|
79
|
+
if (!def || !def.languageId || !def.command) {
|
|
80
|
+
throw new Error("registerLanguageServer requires { languageId, command }");
|
|
81
|
+
}
|
|
82
|
+
pluginServers.set(def.languageId, {
|
|
83
|
+
id: def.id || def.command,
|
|
84
|
+
bins: [def.command],
|
|
85
|
+
argsFor: () => (Array.isArray(def.args) ? def.args : []),
|
|
86
|
+
fromPlugin: true,
|
|
87
|
+
});
|
|
88
|
+
if (Array.isArray(def.extensions)) {
|
|
89
|
+
for (const ext of def.extensions) {
|
|
90
|
+
EXTENSION_LANGUAGE[ext.startsWith(".") ? ext : "." + ext] =
|
|
91
|
+
def.languageId;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Test/reset hook — clears plugin registrations. */
|
|
97
|
+
export function _resetPluginServers() {
|
|
98
|
+
pluginServers.clear();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Map a file path to its LSP languageId, or null if unsupported. */
|
|
102
|
+
export function languageIdForFile(filePath) {
|
|
103
|
+
const ext = path.extname(String(filePath || "")).toLowerCase();
|
|
104
|
+
return EXTENSION_LANGUAGE[ext] || null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Resolve a runnable server command for a languageId within a project root, or
|
|
109
|
+
* null if none is installed. Checks (in order): project `node_modules/.bin`,
|
|
110
|
+
* then PATH (via `resolveBin`).
|
|
111
|
+
*
|
|
112
|
+
* @returns {{ languageId, id, command, args } | null}
|
|
113
|
+
*/
|
|
114
|
+
export function resolveServer(languageId, projectRoot) {
|
|
115
|
+
const def = pluginServers.get(languageId) || BUILTIN_SERVERS[languageId];
|
|
116
|
+
if (!def) return null;
|
|
117
|
+
for (const bin of def.bins) {
|
|
118
|
+
const command = resolveBin(bin, projectRoot);
|
|
119
|
+
if (command) {
|
|
120
|
+
return {
|
|
121
|
+
languageId,
|
|
122
|
+
id: def.id,
|
|
123
|
+
command,
|
|
124
|
+
args: def.argsFor(bin),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Find an executable by name: project-local `node_modules/.bin` first (so a
|
|
133
|
+
* repo-pinned server wins), then the system PATH. Returns an absolute path, or
|
|
134
|
+
* null. On Windows the `.cmd`/`.exe` variants are probed too.
|
|
135
|
+
*/
|
|
136
|
+
export function resolveBin(bin, projectRoot) {
|
|
137
|
+
const isWin = process.platform === "win32";
|
|
138
|
+
const candidates = isWin ? [bin + ".cmd", bin + ".exe", bin] : [bin];
|
|
139
|
+
|
|
140
|
+
// 1) project node_modules/.bin (walk up a couple levels for monorepos)
|
|
141
|
+
if (projectRoot) {
|
|
142
|
+
let dir = projectRoot;
|
|
143
|
+
for (let i = 0; i < 4; i++) {
|
|
144
|
+
const binDir = path.join(dir, "node_modules", ".bin");
|
|
145
|
+
for (const c of candidates) {
|
|
146
|
+
const full = path.join(binDir, c);
|
|
147
|
+
if (_deps.existsSync(full)) return full;
|
|
148
|
+
}
|
|
149
|
+
const parent = path.dirname(dir);
|
|
150
|
+
if (parent === dir) break;
|
|
151
|
+
dir = parent;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 2) PATH
|
|
156
|
+
const pathEnv = process.env.PATH || process.env.Path || "";
|
|
157
|
+
const sep = isWin ? ";" : ":";
|
|
158
|
+
for (const entry of pathEnv.split(sep)) {
|
|
159
|
+
if (!entry) continue;
|
|
160
|
+
for (const c of candidates) {
|
|
161
|
+
const full = path.join(entry, c);
|
|
162
|
+
if (_deps.existsSync(full)) return full;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Report which builtin servers are available on this machine — used by
|
|
170
|
+
* `cc doctor` and `cc code-intel status` so the user sees why LSP is or isn't
|
|
171
|
+
* active without guessing.
|
|
172
|
+
* @returns {Array<{ languageId, id, available, command }>}
|
|
173
|
+
*/
|
|
174
|
+
export function probeServers(projectRoot) {
|
|
175
|
+
const seen = new Set();
|
|
176
|
+
const out = [];
|
|
177
|
+
const all = { ...BUILTIN_SERVERS };
|
|
178
|
+
for (const [lid, def] of pluginServers) all[lid] = def;
|
|
179
|
+
for (const [languageId, def] of Object.entries(all)) {
|
|
180
|
+
if (seen.has(def.id)) continue; // one row per server family
|
|
181
|
+
seen.add(def.id);
|
|
182
|
+
const resolved = resolveServer(languageId, projectRoot);
|
|
183
|
+
out.push({
|
|
184
|
+
languageId,
|
|
185
|
+
id: def.id,
|
|
186
|
+
available: Boolean(resolved),
|
|
187
|
+
command: resolved ? resolved.command : null,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** All extensions the registry can currently map (for docs/diagnostics). */
|
|
194
|
+
export function supportedExtensions() {
|
|
195
|
+
return Object.keys(EXTENSION_LANGUAGE).sort();
|
|
196
|
+
}
|
package/src/lib/matrix-bridge.js
CHANGED
|
@@ -57,6 +57,90 @@ export function ensureMatrixTables(db) {
|
|
|
57
57
|
`);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Rehydrate _rooms/_messages/_spaceChildren from the persisted matrix_rooms/
|
|
62
|
+
* matrix_events tables. The `cc` CLI runs every command in a fresh process, so
|
|
63
|
+
* without this the Maps start empty and a room joined / message sent / space
|
|
64
|
+
* child added in a prior invocation is invisible (`cc matrix rooms`/`messages`/
|
|
65
|
+
* `space children` show nothing; sending to a persisted room can't find it).
|
|
66
|
+
* Call after ensureMatrixTables(db).
|
|
67
|
+
*
|
|
68
|
+
* Caveats reflecting the write side: room `type` is not a persisted column, so
|
|
69
|
+
* spaces are re-identified by their `!space_` roomId prefix (matching
|
|
70
|
+
* createSpace). removeSpaceChild is not persisted (no tombstone event), so a
|
|
71
|
+
* removed child re-appears after rehydration — a separate pre-existing gap,
|
|
72
|
+
* out of scope here.
|
|
73
|
+
*/
|
|
74
|
+
export function loadFromDb(db) {
|
|
75
|
+
if (!db) return 0;
|
|
76
|
+
_rooms.clear();
|
|
77
|
+
_messages.clear();
|
|
78
|
+
_spaceChildren.clear();
|
|
79
|
+
|
|
80
|
+
const roomRows = db.prepare(`SELECT * FROM matrix_rooms`).all();
|
|
81
|
+
for (const row of roomRows) {
|
|
82
|
+
const isSpace =
|
|
83
|
+
typeof row.room_id === "string" && row.room_id.startsWith("!space_");
|
|
84
|
+
const room = {
|
|
85
|
+
id: row.id,
|
|
86
|
+
roomId: row.room_id,
|
|
87
|
+
name: row.name,
|
|
88
|
+
topic: row.topic ?? null,
|
|
89
|
+
isEncrypted: !!row.is_encrypted,
|
|
90
|
+
memberCount: row.member_count ?? 0,
|
|
91
|
+
lastEventAt: row.last_event_at ?? null,
|
|
92
|
+
joinedAt: row.joined_at ?? null,
|
|
93
|
+
status: row.status,
|
|
94
|
+
createdAt: row.created_at ?? null,
|
|
95
|
+
};
|
|
96
|
+
if (isSpace) {
|
|
97
|
+
room.type = "m.space";
|
|
98
|
+
if (!_spaceChildren.has(row.room_id))
|
|
99
|
+
_spaceChildren.set(row.room_id, new Map());
|
|
100
|
+
}
|
|
101
|
+
_rooms.set(row.id, room);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const eventRows = db.prepare(`SELECT * FROM matrix_events`).all();
|
|
105
|
+
for (const row of eventRows) {
|
|
106
|
+
let content = {};
|
|
107
|
+
try {
|
|
108
|
+
content = row.content ? JSON.parse(row.content) : {};
|
|
109
|
+
} catch {
|
|
110
|
+
content = {};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (row.event_type === "m.space.child") {
|
|
114
|
+
// Space child state event: content is { state_key, content: { via } }.
|
|
115
|
+
const childRoomId = content.state_key;
|
|
116
|
+
const via =
|
|
117
|
+
content.content && Array.isArray(content.content.via)
|
|
118
|
+
? content.content.via
|
|
119
|
+
: ["matrix.org"];
|
|
120
|
+
if (childRoomId) {
|
|
121
|
+
if (!_spaceChildren.has(row.room_id))
|
|
122
|
+
_spaceChildren.set(row.room_id, new Map());
|
|
123
|
+
_spaceChildren.get(row.room_id).set(childRoomId, { via });
|
|
124
|
+
}
|
|
125
|
+
continue; // space-child events are not messages
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
_messages.set(row.id, {
|
|
129
|
+
id: row.id,
|
|
130
|
+
eventId: row.event_id,
|
|
131
|
+
roomId: row.room_id,
|
|
132
|
+
sender: row.sender,
|
|
133
|
+
eventType: row.event_type,
|
|
134
|
+
content,
|
|
135
|
+
originServerTs: row.origin_server_ts ?? null,
|
|
136
|
+
isEncrypted: !!row.is_encrypted,
|
|
137
|
+
createdAt: row.created_at ?? null,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return _rooms.size + _messages.size + _spaceChildren.size;
|
|
142
|
+
}
|
|
143
|
+
|
|
60
144
|
/* ── Login ─────────────────────────────────────────────────── */
|
|
61
145
|
|
|
62
146
|
export function login(db, homeserver, userId, password) {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import fs from "fs";
|
|
9
9
|
import path from "path";
|
|
10
|
-
import { likePrefix } from "./sql-like.js";
|
|
10
|
+
import { likePrefix, escapeLike } from "./sql-like.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Atomically write a UTF-8 file: a crash mid-write must not truncate MEMORY.md
|
|
@@ -98,12 +98,14 @@ export function searchMemory(db, query, options = {}) {
|
|
|
98
98
|
if (!query || !query.trim()) return [];
|
|
99
99
|
|
|
100
100
|
const limit = Math.max(1, parseInt(options.limit) || 20);
|
|
101
|
-
|
|
101
|
+
// Escape LIKE wildcards so a query containing % / _ is matched literally
|
|
102
|
+
// rather than as a wildcard (`search "%"` would otherwise match every entry).
|
|
103
|
+
const pattern = `%${escapeLike(query)}%`;
|
|
102
104
|
|
|
103
105
|
return db
|
|
104
106
|
.prepare(
|
|
105
107
|
`SELECT * FROM memory_entries
|
|
106
|
-
WHERE content LIKE ?
|
|
108
|
+
WHERE content LIKE ? ESCAPE '\\'
|
|
107
109
|
ORDER BY importance DESC, created_at DESC
|
|
108
110
|
LIMIT ?`,
|
|
109
111
|
)
|