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,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeIntelligence — the high-level, agent- and CLI-facing API for semantic
|
|
3
|
+
* code navigation. Wraps LSPManager and normalizes every LSP result shape into
|
|
4
|
+
* a flat `{ file, line, col, endLine?, endCol?, snippet? }` form with 1-based
|
|
5
|
+
* positions and native paths. Callers never see raw LSP URIs or 0-based ranges.
|
|
6
|
+
*
|
|
7
|
+
* Every method degrades gracefully: when no language server is installed for the
|
|
8
|
+
* file, it returns `{ available: false, reason }` instead of throwing, so the
|
|
9
|
+
* agent can fall back to text search (`search_files`).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { LSPManager, toLspPosition, fromLspPosition } from "./lsp-manager.js";
|
|
15
|
+
import { fileUriToPath } from "./lsp-client.js";
|
|
16
|
+
import { ensurePluginLspServers } from "../plugin-runtime/lsp.js";
|
|
17
|
+
|
|
18
|
+
export const _deps = { readFileSync: fs.readFileSync };
|
|
19
|
+
|
|
20
|
+
const SYMBOL_KIND_NAMES = {
|
|
21
|
+
1: "file",
|
|
22
|
+
2: "module",
|
|
23
|
+
3: "namespace",
|
|
24
|
+
4: "package",
|
|
25
|
+
5: "class",
|
|
26
|
+
6: "method",
|
|
27
|
+
7: "property",
|
|
28
|
+
8: "field",
|
|
29
|
+
9: "constructor",
|
|
30
|
+
10: "enum",
|
|
31
|
+
11: "interface",
|
|
32
|
+
12: "function",
|
|
33
|
+
13: "variable",
|
|
34
|
+
14: "constant",
|
|
35
|
+
15: "string",
|
|
36
|
+
16: "number",
|
|
37
|
+
17: "boolean",
|
|
38
|
+
18: "array",
|
|
39
|
+
19: "object",
|
|
40
|
+
20: "key",
|
|
41
|
+
21: "null",
|
|
42
|
+
22: "enum-member",
|
|
43
|
+
23: "struct",
|
|
44
|
+
24: "event",
|
|
45
|
+
25: "operator",
|
|
46
|
+
26: "type-parameter",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const DIAGNOSTIC_SEVERITY = {
|
|
50
|
+
1: "error",
|
|
51
|
+
2: "warning",
|
|
52
|
+
3: "information",
|
|
53
|
+
4: "hint",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export class CodeIntelligence {
|
|
57
|
+
constructor(opts = {}) {
|
|
58
|
+
// Register any plugin-contributed language servers (.lsp.json) for this
|
|
59
|
+
// project before the first query — once per root, best-effort. This is how
|
|
60
|
+
// a plugin extends code_intelligence to a new language with no core change.
|
|
61
|
+
try {
|
|
62
|
+
ensurePluginLspServers({ cwd: opts.projectRoot || process.cwd() });
|
|
63
|
+
} catch {
|
|
64
|
+
/* plugin LSP contributions are best-effort */
|
|
65
|
+
}
|
|
66
|
+
this.manager =
|
|
67
|
+
opts.manager || new LSPManager({ projectRoot: opts.projectRoot });
|
|
68
|
+
this._ownsManager = !opts.manager;
|
|
69
|
+
// One-shot callers (the `cc code-intel` CLI) spawn a cold server per run and
|
|
70
|
+
// must wait for the project to load before querying; the long-lived agent
|
|
71
|
+
// session keeps servers warm and leaves this off for latency.
|
|
72
|
+
this._ensureOpts = opts.coldStart ? { waitReady: true } : {};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_ensure(filePath) {
|
|
76
|
+
return this.manager.ensureFor(filePath, this._ensureOpts);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Resolve the symbol under a 1-based position to its definition location(s). */
|
|
80
|
+
async definition(filePath, line, col) {
|
|
81
|
+
return this._locationQuery("textDocument/definition", filePath, line, col);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Find references to the symbol under a 1-based position. */
|
|
85
|
+
async references(filePath, line, col, { includeDeclaration = true } = {}) {
|
|
86
|
+
const ready = await this._ensure(filePath);
|
|
87
|
+
if (ready.unavailable) return unavailable(ready.reason);
|
|
88
|
+
const result = await ready.client.request("textDocument/references", {
|
|
89
|
+
textDocument: { uri: ready.uri },
|
|
90
|
+
position: toLspPosition({ line, col }),
|
|
91
|
+
context: { includeDeclaration },
|
|
92
|
+
});
|
|
93
|
+
return { available: true, locations: this._normalizeLocations(result) };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Hover / type info at a 1-based position. */
|
|
97
|
+
async hover(filePath, line, col) {
|
|
98
|
+
const ready = await this._ensure(filePath);
|
|
99
|
+
if (ready.unavailable) return unavailable(ready.reason);
|
|
100
|
+
const result = await ready.client.request("textDocument/hover", {
|
|
101
|
+
textDocument: { uri: ready.uri },
|
|
102
|
+
position: toLspPosition({ line, col }),
|
|
103
|
+
});
|
|
104
|
+
return { available: true, contents: extractHoverText(result) };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Symbols declared in one document (hierarchical or flat). */
|
|
108
|
+
async documentSymbols(filePath) {
|
|
109
|
+
const ready = await this._ensure(filePath);
|
|
110
|
+
if (ready.unavailable) return unavailable(ready.reason);
|
|
111
|
+
const result = await ready.client.request("textDocument/documentSymbol", {
|
|
112
|
+
textDocument: { uri: ready.uri },
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
available: true,
|
|
116
|
+
symbols: this._normalizeSymbols(result, ready.uri),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Workspace-wide symbol search by name. */
|
|
121
|
+
async workspaceSymbols(query, { anyFile } = {}) {
|
|
122
|
+
// Need a running server; bootstrap one via any known file in the project.
|
|
123
|
+
const seed = anyFile || (await this._anyProjectFile());
|
|
124
|
+
if (!seed) return unavailable("no indexable file found to start a server");
|
|
125
|
+
const ready = await this.manager.ensureFor(seed, this._ensureOpts);
|
|
126
|
+
if (ready.unavailable) return unavailable(ready.reason);
|
|
127
|
+
const result = await ready.client.request("workspace/symbol", { query });
|
|
128
|
+
return { available: true, symbols: this._normalizeSymbols(result) };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Diagnostics for a file. Prefers PULL diagnostics (`textDocument/diagnostic`,
|
|
133
|
+
* LSP 3.17) — a synchronous request the server answers only after the project
|
|
134
|
+
* is loaded, which avoids the push-model race where tsserver emits an empty
|
|
135
|
+
* set first and republishes real errors later. Falls back to the push model
|
|
136
|
+
* (wait-for-publish + settle) for servers without pull support.
|
|
137
|
+
*/
|
|
138
|
+
async diagnostics(filePath, { timeoutMs = 5000 } = {}) {
|
|
139
|
+
const ready = await this._ensure(filePath);
|
|
140
|
+
if (ready.unavailable) return unavailable(ready.reason);
|
|
141
|
+
const raw = await this._collectDiagnostics(ready, filePath, timeoutMs);
|
|
142
|
+
return {
|
|
143
|
+
available: true,
|
|
144
|
+
diagnostics: raw.map((d) => this._normalizeDiagnostic(d)),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Notify the server a file changed on disk (call after an edit), then re-diagnose. */
|
|
149
|
+
async refreshFile(filePath, { timeoutMs = 5000 } = {}) {
|
|
150
|
+
const ready = await this.manager.didChangeFile(filePath, this._ensureOpts);
|
|
151
|
+
if (ready.unavailable) return unavailable(ready.reason);
|
|
152
|
+
const raw = await this._collectDiagnostics(ready, filePath, timeoutMs);
|
|
153
|
+
return {
|
|
154
|
+
available: true,
|
|
155
|
+
diagnostics: raw.map((d) => this._normalizeDiagnostic(d)),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async _collectDiagnostics(ready, filePath, timeoutMs) {
|
|
160
|
+
const caps = ready.client.serverCapabilities || {};
|
|
161
|
+
if (caps.diagnosticProvider) {
|
|
162
|
+
try {
|
|
163
|
+
const report = await ready.client.request(
|
|
164
|
+
"textDocument/diagnostic",
|
|
165
|
+
{ textDocument: { uri: ready.uri } },
|
|
166
|
+
{ timeoutMs },
|
|
167
|
+
);
|
|
168
|
+
// full report → { kind:"full", items:[...] }; unchanged → keep last push
|
|
169
|
+
if (report && Array.isArray(report.items)) return report.items;
|
|
170
|
+
} catch {
|
|
171
|
+
// server advertised but errored — fall back to the push model
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return this.manager.waitForDiagnostics(filePath, { timeoutMs });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Preview a rename: returns the WorkspaceEdit as a flat list of per-file text
|
|
179
|
+
* edits WITHOUT applying anything. Applying is left to the caller (agent
|
|
180
|
+
* edit_file / a future apply step) so the change stays reviewable.
|
|
181
|
+
*/
|
|
182
|
+
async renamePreview(filePath, line, col, newName) {
|
|
183
|
+
const ready = await this._ensure(filePath);
|
|
184
|
+
if (ready.unavailable) return unavailable(ready.reason);
|
|
185
|
+
const result = await ready.client.request("textDocument/rename", {
|
|
186
|
+
textDocument: { uri: ready.uri },
|
|
187
|
+
position: toLspPosition({ line, col }),
|
|
188
|
+
newName,
|
|
189
|
+
});
|
|
190
|
+
return { available: true, edits: this._normalizeWorkspaceEdit(result) };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async dispose() {
|
|
194
|
+
if (this._ownsManager) await this.manager.disposeAll();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ---- internals ----
|
|
198
|
+
|
|
199
|
+
async _locationQuery(method, filePath, line, col) {
|
|
200
|
+
const ready = await this._ensure(filePath);
|
|
201
|
+
if (ready.unavailable) return unavailable(ready.reason);
|
|
202
|
+
const result = await ready.client.request(method, {
|
|
203
|
+
textDocument: { uri: ready.uri },
|
|
204
|
+
position: toLspPosition({ line, col }),
|
|
205
|
+
});
|
|
206
|
+
return { available: true, locations: this._normalizeLocations(result) };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
_normalizeLocations(result) {
|
|
210
|
+
if (!result) return [];
|
|
211
|
+
const arr = Array.isArray(result) ? result : [result];
|
|
212
|
+
return arr
|
|
213
|
+
.map((loc) => {
|
|
214
|
+
// LocationLink has targetUri/targetRange; Location has uri/range.
|
|
215
|
+
const uri = loc.uri || loc.targetUri;
|
|
216
|
+
const range = loc.range || loc.targetSelectionRange || loc.targetRange;
|
|
217
|
+
if (!uri || !range) return null;
|
|
218
|
+
const file = fileUriToPath(uri);
|
|
219
|
+
const start = fromLspPosition(range.start);
|
|
220
|
+
const end = fromLspPosition(range.end);
|
|
221
|
+
return {
|
|
222
|
+
file,
|
|
223
|
+
line: start.line,
|
|
224
|
+
col: start.col,
|
|
225
|
+
endLine: end.line,
|
|
226
|
+
endCol: end.col,
|
|
227
|
+
snippet: this._lineSnippet(file, start.line),
|
|
228
|
+
};
|
|
229
|
+
})
|
|
230
|
+
.filter(Boolean);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
_normalizeSymbols(result, docUri) {
|
|
234
|
+
if (!Array.isArray(result)) return [];
|
|
235
|
+
const out = [];
|
|
236
|
+
const walk = (sym, container) => {
|
|
237
|
+
// DocumentSymbol has .range/.selectionRange/.children; SymbolInformation
|
|
238
|
+
// has .location.
|
|
239
|
+
const uri = sym.location?.uri || docUri;
|
|
240
|
+
const range = sym.selectionRange || sym.range || sym.location?.range;
|
|
241
|
+
const file = fileUriToPath(uri);
|
|
242
|
+
const start = range ? fromLspPosition(range.start) : { line: 1, col: 1 };
|
|
243
|
+
out.push({
|
|
244
|
+
name: sym.name,
|
|
245
|
+
kind: SYMBOL_KIND_NAMES[sym.kind] || String(sym.kind),
|
|
246
|
+
container: sym.containerName || container || null,
|
|
247
|
+
file,
|
|
248
|
+
line: start.line,
|
|
249
|
+
col: start.col,
|
|
250
|
+
});
|
|
251
|
+
if (Array.isArray(sym.children)) {
|
|
252
|
+
for (const child of sym.children) walk(child, sym.name);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
for (const sym of result) walk(sym, null);
|
|
256
|
+
return out;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
_normalizeDiagnostic(d) {
|
|
260
|
+
const start = fromLspPosition(d.range?.start);
|
|
261
|
+
const end = fromLspPosition(d.range?.end);
|
|
262
|
+
return {
|
|
263
|
+
severity: DIAGNOSTIC_SEVERITY[d.severity] || "error",
|
|
264
|
+
message: d.message,
|
|
265
|
+
line: start.line,
|
|
266
|
+
col: start.col,
|
|
267
|
+
endLine: end.line,
|
|
268
|
+
endCol: end.col,
|
|
269
|
+
source: d.source || null,
|
|
270
|
+
code: d.code != null ? String(d.code) : null,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
_normalizeWorkspaceEdit(result) {
|
|
275
|
+
const edits = [];
|
|
276
|
+
if (!result) return edits;
|
|
277
|
+
if (result.changes) {
|
|
278
|
+
for (const [uri, textEdits] of Object.entries(result.changes)) {
|
|
279
|
+
edits.push({
|
|
280
|
+
file: fileUriToPath(uri),
|
|
281
|
+
edits: textEdits.map(mapTextEdit),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (Array.isArray(result.documentChanges)) {
|
|
286
|
+
for (const dc of result.documentChanges) {
|
|
287
|
+
if (dc.textDocument && Array.isArray(dc.edits)) {
|
|
288
|
+
edits.push({
|
|
289
|
+
file: fileUriToPath(dc.textDocument.uri),
|
|
290
|
+
edits: dc.edits.map(mapTextEdit),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return edits;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
_lineSnippet(file, line) {
|
|
299
|
+
try {
|
|
300
|
+
const text = _deps.readFileSync(file, "utf8");
|
|
301
|
+
const lines = text.split(/\r?\n/);
|
|
302
|
+
return (lines[line - 1] || "").trim();
|
|
303
|
+
} catch {
|
|
304
|
+
return "";
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async _anyProjectFile() {
|
|
309
|
+
// Cheap heuristic: pick the manager's root and look for a common entry file.
|
|
310
|
+
const root = this.manager.projectRoot;
|
|
311
|
+
const candidates = [
|
|
312
|
+
"src/index.ts",
|
|
313
|
+
"src/index.js",
|
|
314
|
+
"index.ts",
|
|
315
|
+
"index.js",
|
|
316
|
+
"main.py",
|
|
317
|
+
"main.go",
|
|
318
|
+
];
|
|
319
|
+
for (const c of candidates) {
|
|
320
|
+
const full = path.join(root, c);
|
|
321
|
+
try {
|
|
322
|
+
_deps.readFileSync(full, "utf8");
|
|
323
|
+
return full;
|
|
324
|
+
} catch {
|
|
325
|
+
/* try next */
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function mapTextEdit(te) {
|
|
333
|
+
const start = fromLspPosition(te.range?.start);
|
|
334
|
+
const end = fromLspPosition(te.range?.end);
|
|
335
|
+
return {
|
|
336
|
+
line: start.line,
|
|
337
|
+
col: start.col,
|
|
338
|
+
endLine: end.line,
|
|
339
|
+
endCol: end.col,
|
|
340
|
+
newText: te.newText,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function extractHoverText(result) {
|
|
345
|
+
if (!result || !result.contents) return "";
|
|
346
|
+
const c = result.contents;
|
|
347
|
+
if (typeof c === "string") return c;
|
|
348
|
+
if (Array.isArray(c)) {
|
|
349
|
+
return c.map((x) => (typeof x === "string" ? x : x.value || "")).join("\n");
|
|
350
|
+
}
|
|
351
|
+
return c.value || "";
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function unavailable(reason) {
|
|
355
|
+
return { available: false, reason: reason || "language server unavailable" };
|
|
356
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LSP transport framing — `Content-Length` header framing for JSON-RPC 2.0
|
|
3
|
+
* over a byte stream (LSP base protocol).
|
|
4
|
+
*
|
|
5
|
+
* The Language Server Protocol frames each JSON-RPC message as:
|
|
6
|
+
*
|
|
7
|
+
* Content-Length: <N>\r\n
|
|
8
|
+
* \r\n
|
|
9
|
+
* <N bytes of UTF-8 JSON>
|
|
10
|
+
*
|
|
11
|
+
* (an optional `Content-Type` header may also appear and is ignored). This
|
|
12
|
+
* module is pure: `encodeMessage` serializes one message, and `MessageBuffer`
|
|
13
|
+
* accumulates arbitrary byte chunks and emits complete decoded messages. No
|
|
14
|
+
* child-process or IO here so it is fully unit-testable.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const HEADER_SEPARATOR = "\r\n\r\n";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Serialize a JSON-RPC message object to a framed Buffer ready to write to a
|
|
21
|
+
* language server's stdin. Length is measured in BYTES (UTF-8), not characters
|
|
22
|
+
* — a message with multibyte content otherwise under-reports its length and
|
|
23
|
+
* corrupts the stream.
|
|
24
|
+
*/
|
|
25
|
+
export function encodeMessage(message) {
|
|
26
|
+
const json = JSON.stringify(message);
|
|
27
|
+
const body = Buffer.from(json, "utf8");
|
|
28
|
+
const header = Buffer.from(`Content-Length: ${body.length}\r\n\r\n`, "ascii");
|
|
29
|
+
return Buffer.concat([header, body]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Incremental decoder. Feed it byte chunks (Buffer or string) as they arrive on
|
|
34
|
+
* stdout; call `read()` (or iterate `readAll()`) to pull out every complete
|
|
35
|
+
* message decoded so far. Partial frames are retained until the rest arrives.
|
|
36
|
+
*
|
|
37
|
+
* Robust against: headers and body split across chunks, multiple messages in
|
|
38
|
+
* one chunk, LF-only line endings from lenient servers, and unknown extra
|
|
39
|
+
* headers (e.g. Content-Type).
|
|
40
|
+
*/
|
|
41
|
+
export class MessageBuffer {
|
|
42
|
+
constructor() {
|
|
43
|
+
/** @type {Buffer} */
|
|
44
|
+
this._buffer = Buffer.alloc(0);
|
|
45
|
+
/** @type {object[]} */
|
|
46
|
+
this._pending = [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Append a raw chunk (Buffer or utf8 string) and parse what is now complete. */
|
|
50
|
+
append(chunk) {
|
|
51
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf8");
|
|
52
|
+
this._buffer = this._buffer.length
|
|
53
|
+
? Buffer.concat([this._buffer, buf])
|
|
54
|
+
: buf;
|
|
55
|
+
this._drain();
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Pull the next decoded message, or null if none are ready. */
|
|
60
|
+
read() {
|
|
61
|
+
return this._pending.length ? this._pending.shift() : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Pull every decoded message ready so far (may be empty). */
|
|
65
|
+
readAll() {
|
|
66
|
+
const out = this._pending;
|
|
67
|
+
this._pending = [];
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** True if a partial (undecoded) frame remains in the buffer. */
|
|
72
|
+
get hasPartial() {
|
|
73
|
+
return this._buffer.length > 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
_drain() {
|
|
77
|
+
// Loop because a single append can complete several queued frames.
|
|
78
|
+
for (;;) {
|
|
79
|
+
const headerEnd = this._findHeaderEnd();
|
|
80
|
+
if (headerEnd < 0) return; // headers incomplete — wait for more bytes
|
|
81
|
+
|
|
82
|
+
const headerText = this._buffer
|
|
83
|
+
.slice(0, headerEnd.index)
|
|
84
|
+
.toString("ascii");
|
|
85
|
+
const contentLength = parseContentLength(headerText);
|
|
86
|
+
if (contentLength == null) {
|
|
87
|
+
// Malformed header block with no Content-Length. Rather than loop
|
|
88
|
+
// forever, drop up to and including the separator and keep going; a
|
|
89
|
+
// conformant server never does this, but a corrupted stream shouldn't
|
|
90
|
+
// deadlock the client.
|
|
91
|
+
this._buffer = this._buffer.slice(headerEnd.bodyStart);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const bodyStart = headerEnd.bodyStart;
|
|
96
|
+
const bodyEnd = bodyStart + contentLength;
|
|
97
|
+
if (this._buffer.length < bodyEnd) return; // body incomplete — wait
|
|
98
|
+
|
|
99
|
+
const body = this._buffer.slice(bodyStart, bodyEnd).toString("utf8");
|
|
100
|
+
this._buffer = this._buffer.slice(bodyEnd);
|
|
101
|
+
try {
|
|
102
|
+
this._pending.push(JSON.parse(body));
|
|
103
|
+
} catch {
|
|
104
|
+
// A body that isn't valid JSON is unrecoverable for this frame, but the
|
|
105
|
+
// stream position is now known (we consumed exactly Content-Length
|
|
106
|
+
// bytes) so subsequent frames stay aligned. Skip the bad frame.
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Locate the end of the header block. Supports both `\r\n\r\n` (spec) and
|
|
113
|
+
* `\n\n` (lenient servers). Returns `{ index, bodyStart }` where `index` is
|
|
114
|
+
* where the header text ends and `bodyStart` is where the body begins, or -1
|
|
115
|
+
* if the separator hasn't arrived yet.
|
|
116
|
+
*/
|
|
117
|
+
_findHeaderEnd() {
|
|
118
|
+
const crlf = this._buffer.indexOf(HEADER_SEPARATOR);
|
|
119
|
+
const lf = this._buffer.indexOf("\n\n");
|
|
120
|
+
if (crlf < 0 && lf < 0) return -1;
|
|
121
|
+
// Pick whichever separator appears first in the stream.
|
|
122
|
+
if (crlf >= 0 && (lf < 0 || crlf <= lf)) {
|
|
123
|
+
return { index: crlf, bodyStart: crlf + HEADER_SEPARATOR.length };
|
|
124
|
+
}
|
|
125
|
+
return { index: lf, bodyStart: lf + 2 };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Parse the `Content-Length` value from a header block; null if absent/invalid. */
|
|
130
|
+
export function parseContentLength(headerText) {
|
|
131
|
+
for (const line of headerText.split(/\r?\n/)) {
|
|
132
|
+
const m = /^content-length:\s*(\d+)\s*$/i.exec(line);
|
|
133
|
+
if (m) {
|
|
134
|
+
const n = Number.parseInt(m[1], 10);
|
|
135
|
+
return Number.isFinite(n) && n >= 0 ? n : null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|