chainlesschain 0.162.197 → 0.162.200
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/bin/chainlesschain.js +14 -6
- package/package.json +4 -1
- package/scripts/command-lifecycle-report.mjs +94 -0
- package/src/assets/web-panel/assets/{AIOps-D3Gu1a5d.js → AIOps-DcQ0bWKy.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DrwAoKaz.js → ActionButton-GecwfFPt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C7Q-gd_3.js → Analytics-CD6iSt9v.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DgBtae-n.js → AppLayout-CDEtuEYf.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-pQ6LcA-W.js → Artifacts-DS3jAjeW.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DK61tocB.js → Audit-CcSJA048.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BOKgDFRh.js → BackgroundAgents-DRo9Morb.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Bo9G4vTP.js → Backup-Vw5MrWft.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Do_dE9_v.js → BaseInput-DNHIu823.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-COVvZzOL.js → Chat-DvAsO8-b.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-9aDuObay.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Z9MJMolt.js → Checkbox-CKCa7tvn.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BVzWKVbf.js → Codegen-CIFvZdaT.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C8MnYcbT.js → Col-C79yz7bK.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DQxqw15l.js → Community-DVvZskLJ.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BAUmrh3y.js → Compact-oaew61hV.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-jPAkQ3zK.js → Compliance-DqA7wmRx.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BBQyxbh8.js → Cowork-B-qGadxa.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-W5koq8Z5.js → Cron-CUlCl4sS.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BQhnu5hS.js → Crosschain-CNOlr9DU.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DD7PlI18.js → DID-DJqFCT_3.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-w_ILuwKI.js → Dashboard-CznGpphf.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-qiDUAyAo.js → Dropdown-_cJw218E.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-PDeBcI5H.js → EmailListRenderer-Dr-1BZ9C.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-SuArscRM.js → FamilyGuardDashboard-Dxf4xtSk.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BCovZaUn.js → Federation-DOiG3i_1.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DjlFdVFk.js → FormItemContext-BzbMcFB9.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DoEZ7fcS.js → GenericCardRenderer-VbBrYttP.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CV0-6GPI.js → Git-CLG8E6ED.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B_SFN50w.js → Governance-LGJQfk_q.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-heI0Eo6k.js → Inference-DlQKx9lF.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cz5INS5p.js → KnowledgeGraph-DU8vEouV.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BSFxiHkg.js → Logs-FZIQOdln.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BJNWIrbw.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-0TWTpE7k.js → Marketplace-C_xd-zZI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CV4-wBLm.js → McpTools-q-YktphS.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-x5bH5rw5.js → Memory-Cq9OhDin.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DaBmUu9G.js → MobileBridge-B03_R98T.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-DZrk-qmF.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-B_zKEBif.js → Mtc-BidCKW28.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BCfKD2yg.js → MtcAudit-r-iaMVM7.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DgYfj6c_.js → Multisig-CgTUFvff.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dz09lFUm.js → NLProgramming-B7NvzfAV.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cuj6ugBE.js → Notes-80CWNqJI.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DK2WXjhg.js → NotificationSettings-0H6xdiY3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-D1IAOQoo.js → OrderTableRenderer-CKP4AFWy.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-9fqnPvTw.js → Organization-B8sxCV_h.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-n5xHkhow.js → Overflow-CW9JBBhQ.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-B5AUWwH-.js → P2P-bdSQV7oZ.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CoUDV6dy.js → PdhVaultBrowser-DQad42nW.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-DPRmQf_H.js → Permissions-CueexxTR.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-B37bsbEN.js → PersonalDataHub-fqxflhR8.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-CCAuPqu9.js → Pipeline-KFwwnIMC.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B81msZdv.js → Privacy-ClnD9buY.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B-Py2NbF.js → ProjectInit-CZEz6sm3.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CYsneiRB.js → ProjectSettings-B6NzpmOI.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Y975Mh6R.js → Projects-DYyuwPCs.js} +1 -1
- package/src/assets/web-panel/assets/Providers-C3tz_jdh.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-Bj_6gJ0l.js → QrScannerModal-BzuOBbqB.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BgpBCMPE.js → QuickAsk-CmNO9qSj.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DhUW3YUA.js → Recommend-VJNj3_NF.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DdeyfbkA.js → RemoteSession-CrbdbSBF.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-fxW421xd.js → Reputation-DTyMd45j.js} +1 -1
- package/src/assets/web-panel/assets/{Row-De73w1og.js → Row-DHrQ-Icz.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-3US_Q_lj.js → RssFeed-DqWxY9c5.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DbZ_DU74.js → Search-ctF1Ks3J.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DoIU1vMF.js → Security-BrQz8uo5.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DB_RGqpc.js → Services-D4scVFar.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DnzLaayC.js → Skeleton-oUScaOjv.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-VzVXHENF.js → Skills-CL-y0eLt.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DMfvjCAI.js → Sla-DQExhwXj.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DQQOYWbF.js → SpeechSettings-iNp_I7kN.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Bmx3FG1P.js → SyncSettings-CTDlMo0A.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DOQyfb-2.js → Tasks-4kb8l3sc.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-D6hTRT7c.js → Templates-BXcL9oXW.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CTPQZAzs.js → Tenant-DLE8eS0D.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B7vII3oH.js → Terminal-DGpUZPPK.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BkiFJeXV.js → TimelineRenderer-C1pWXGPC.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BPPdQMNo.js → Tokens-BYfbfqhZ.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BxG8JAEI.js → Trigger-K9YfLOCw.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DHD7nMpM.js → Trust-Ctkq77Hy.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B4BJpU47.js → UkeySign-C6_CSe9J.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CRiGpFx2.js → VideoEditing-Be3rpOWs.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BSDSNiem.js → Wallet-D0UIplxk.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DLTUh5Zv.js → WebAuthn-CF9bM9I3.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-tW2MwjhD.js → WorkflowEditor-D0H1XVPY.js} +1 -1
- package/src/assets/web-panel/assets/{chat-SgsLQ3FC.js → chat-Cgzzbe0o.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BHjjLIKO.js → colors-BwNxEVS0.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-VceErdKq.js → compact-item-D60Z4Hhs.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BZ332MYl.js → createContext-MFk58XB0.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BJiw369i.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CBTjJOjn.js → hasIn-CRj2SvGO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaCCy6lq.js → index--s45_1cb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMVvxIS8.js → index--tquQ2ZG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhXZEUAc.js → index-1T1rhokn.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQSmZGgs.js → index-B-M82_rx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BeiqkxJG.js → index-BE_el1Zs.js} +1 -1
- package/src/assets/web-panel/assets/{index-29wVzi-k.js → index-BhRkR7IO.js} +1 -1
- package/src/assets/web-panel/assets/{index-iYQNU9za.js → index-Bo0y96-K.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRWhLbWF.js → index-CJgu5JUK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPXFLYdW.js → index-CLWwdebO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cr4rjlW5.js → index-CNAK5QG_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaIS_TNX.js → index-CQjJjPRD.js} +1 -1
- package/src/assets/web-panel/assets/{index-1n7xjKDU.js → index-CTDbAfae.js} +1 -1
- package/src/assets/web-panel/assets/{index-BETEjuNr.js → index-CVI_9sRK.js} +1 -1
- package/src/assets/web-panel/assets/{index-By5jBtkV.js → index-CY14oMQK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgPvuIn2.js → index-CbiTJkZf.js} +1 -1
- package/src/assets/web-panel/assets/index-CiMouosn.js +1 -0
- package/src/assets/web-panel/assets/{index-CawpdGTW.js → index-CqWpxMqx.js} +1 -1
- package/src/assets/web-panel/assets/{index-_PHrIO4O.js → index-CvB8lYUR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOtvPdHt.js → index-D51XwtCY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrUgDQkf.js → index-D6HvK_1O.js} +1 -1
- package/src/assets/web-panel/assets/{index-BY1wexYG.js → index-D9jVIwLJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_B9rFQE.js → index-DA4tE98u.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5KAGxNo.js → index-DBwOtXw0.js} +1 -1
- package/src/assets/web-panel/assets/{index-BC1xuQgX.js → index-DKdNzTIb.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2u26AS3.js → index-DRySdmvN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DePOeMDM.js → index-DWzyTev7.js} +1 -1
- package/src/assets/web-panel/assets/index-DXcKlwy7.js +1 -0
- package/src/assets/web-panel/assets/{index-BTlc-Bve.js → index-DYjs7jAP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLc19alH.js → index-DbROzMBp.js} +1 -1
- package/src/assets/web-panel/assets/{index-fU7wNHDB.js → index-DhFtk5PR.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_SePDWj.js → index-Di2AbMLg.js} +1 -1
- package/src/assets/web-panel/assets/{index-vIXOpcjD.js → index-DvmocQsM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcUenmCX.js → index-Dw-o0zEF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUkVN794.js → index-DysPoP38.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtjLcCNU.js → index-LX9pJxm_.js} +1 -1
- package/src/assets/web-panel/assets/{index-B40dQn0G.js → index-OI_fXWx0.js} +3 -3
- package/src/assets/web-panel/assets/{index-FJ3zC9LK.js → index-eDTOX8Xj.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdefYBSw.js → index-gWKDsTzF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpumS98o.js → index-q_GDUVAA.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D1oZNz0D.js → initDefaultProps-C61kZqEd.js} +1 -1
- package/src/assets/web-panel/assets/{motion-C8k6doMO.js → motion-DZnT5U1L.js} +1 -1
- package/src/assets/web-panel/assets/{move-DJ4qSbYt.js → move-L8wzwbel.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CdpJD85j.js → mtc-parser-DLdYSejc.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B0V4slKA.js → omit-D53cbS59.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Di5NiW1F.js → pickAttrs-Bbc5HPtu.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B5OMHgEM.js → placementArrow-BkvmO860.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C6U9IWql.js → responsiveObserve-u2y1ryS1.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BMyTH3rN.js → slide-BBqj7svU.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C7d6BG7Z.js → statusUtils-GN0mqu2d.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CR50QnQy.js → styleChecker-BvwPKoBT.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DBEeoMb8.js → useFlexGapSupport-CCt8tnfY.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-3r099cjv.js → useFs-DXAxl2SV.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-C5IYNdTK.js → usePersonalDataHub-DihlR2dj.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DmEOLRHf.js → vnode-D7BOX2gi.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DnpUiVCB.js → zoom-CVbO-nL7.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +4 -4
- package/src/commands/agent.js +3 -3
- package/src/commands/background-session.js +145 -0
- package/src/commands/mcp.js +244 -7
- package/src/commands/serve.js +1 -1
- package/src/commands/session-show.js +129 -0
- package/src/commands/session.js +97 -145
- package/src/commands/skill.js +87 -50
- package/src/data/changelog.json +40 -1
- package/src/gateways/ws/session-protocol.js +14 -4
- package/src/gateways/ws/ws-agent-handler.js +39 -1
- package/src/harness/jsonl-session-store.js +1547 -127
- package/src/harness/mcp-client.js +2690 -409
- package/src/harness/session-index.js +80 -80
- package/src/harness/session-list-index.js +210 -7
- package/src/lazy-dispatch.js +76 -8
- package/src/lib/abort-utils.js +68 -0
- package/src/lib/agent-core.js +1 -59
- package/src/lib/background-interaction-journal.js +2 -2
- package/src/lib/chat-core.js +24 -6
- package/src/lib/command-lifecycle-report.js +516 -0
- package/src/lib/cowork-mcp-tools.js +47 -2
- package/src/lib/cowork-task-runner.js +11 -3
- package/src/lib/doctor-checkup.js +9 -7
- package/src/lib/learning/skill-improver.js +21 -2
- package/src/lib/mcp-call-ledger-store.js +116 -3
- package/src/lib/mcp-call-ledger.js +59 -14
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-stdio-environment.js +48 -0
- package/src/lib/mcp-stdio-executable-identity.js +1133 -0
- package/src/lib/mcp-stdio-execution-authority.js +363 -0
- package/src/lib/mcp-stdio-package-materialization.js +1424 -0
- package/src/lib/mcp-tool-metadata.js +436 -0
- package/src/lib/mcp-tool-result.js +367 -0
- package/src/lib/observability/index.js +71 -0
- package/src/lib/packer/native-update-state.js +979 -1
- package/src/lib/packer/pack-update-applier.js +1050 -125
- package/src/lib/packer/pkg-config-generator.js +1 -0
- package/src/lib/paths.js +53 -0
- package/src/lib/pr-link-ledger.js +8 -27
- package/src/lib/pr-link-store.js +26 -0
- package/src/lib/process-execution-broker/index.js +192 -35
- package/src/lib/process-execution-broker/platform-sandbox.js +291 -24
- package/src/lib/process-tree-termination.js +253 -0
- package/src/lib/recent-session.js +17 -17
- package/src/lib/secure-file-identity.js +47 -0
- package/src/lib/secure-fs.js +21 -2
- package/src/lib/session-anti-rollback-anchor.js +766 -0
- package/src/lib/session-host-lease.js +492 -0
- package/src/lib/session-host-snapshot.js +9 -5
- package/src/lib/session-persistence-failure.js +109 -0
- package/src/lib/session-picker.js +26 -3
- package/src/lib/session-projection.js +5 -1
- package/src/lib/session-search.js +25 -11
- package/src/lib/session-tail.js +31 -1
- package/src/lib/side-effect-ledger-store.js +2 -2
- package/src/lib/skill-budget.js +222 -0
- package/src/lib/skill-execution-authority.js +385 -0
- package/src/lib/skill-execution-identity.js +39 -2
- package/src/lib/skill-loader.js +322 -86
- package/src/lib/skill-mcp.js +43 -1
- package/src/lib/turn-binding-store.js +2 -2
- package/src/repl/agent-repl.js +483 -77
- package/src/repl/bg-dashboard.js +81 -28
- package/src/repl/chat-repl.js +77 -12
- package/src/runtime/agent-core.js +428 -113
- package/src/runtime/agent-runtime.js +26 -3
- package/src/runtime/cleanup-deadline.js +143 -0
- package/src/runtime/headless-runner.js +494 -149
- package/src/runtime/headless-stream.js +504 -75
- package/src/runtime/mcp-config.js +157 -10
- package/src/runtime/mcp-tool-search.js +0 -0
- package/src/runtime/output-backpressure.js +419 -0
- package/src/runtime/pipe-safety.js +56 -21
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-DqnoFPgR.js +0 -1
- package/src/assets/web-panel/assets/Providers-uO-10aP1.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +0 -1
- package/src/assets/web-panel/assets/index-0O5ePnUq.js +0 -1
- package/src/assets/web-panel/assets/index-ChAzPigF.js +0 -1
- package/src/lib/skill-process-broker.js +0 -85
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
|
|
17
17
|
import fs from "fs";
|
|
18
18
|
import path from "path";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
MCPClient,
|
|
21
|
+
inferTransport,
|
|
22
|
+
redactMcpUrl,
|
|
23
|
+
} from "../harness/mcp-client.js";
|
|
20
24
|
import { projectRootBase } from "../lib/project-root.cjs";
|
|
21
25
|
import {
|
|
22
26
|
discoverIdeServer,
|
|
@@ -36,6 +40,38 @@ import {
|
|
|
36
40
|
import { collectPluginMcpServers } from "../lib/plugin-runtime/mcp.js";
|
|
37
41
|
import { EventRuntimeStore } from "../lib/event-runtime-store.js";
|
|
38
42
|
import { mcpEffectDescriptorFields } from "../lib/mcp-effect-contract.js";
|
|
43
|
+
import {
|
|
44
|
+
admitMcpToolList,
|
|
45
|
+
isMcpToolMetadataError,
|
|
46
|
+
} from "../lib/mcp-tool-metadata.js";
|
|
47
|
+
import { issueMcpStdioExecutionAuthority } from "../lib/mcp-stdio-execution-authority.js";
|
|
48
|
+
|
|
49
|
+
function authorizeStdioServers(
|
|
50
|
+
servers,
|
|
51
|
+
approvalKind,
|
|
52
|
+
sourceForServer,
|
|
53
|
+
{ writeErr = () => {}, failFast = false } = {},
|
|
54
|
+
) {
|
|
55
|
+
for (const [name, config] of Object.entries(servers || {})) {
|
|
56
|
+
if (!config || typeof config !== "object") continue;
|
|
57
|
+
if (inferTransport(config) !== "stdio") continue;
|
|
58
|
+
try {
|
|
59
|
+
config.mcpStdioExecutionAuthority = issueMcpStdioExecutionAuthority({
|
|
60
|
+
serverName: name,
|
|
61
|
+
config,
|
|
62
|
+
approvalKind,
|
|
63
|
+
approvalSource: sourceForServer(name, config),
|
|
64
|
+
});
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (failFast) throw error;
|
|
67
|
+
delete servers[name];
|
|
68
|
+
writeErr(
|
|
69
|
+
` mcp: SKIPPING stdio server "${name}" because its local-code execution authority could not be issued.\n`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return servers;
|
|
74
|
+
}
|
|
39
75
|
|
|
40
76
|
function headersHelperField(value) {
|
|
41
77
|
return typeof value === "string" && value.trim()
|
|
@@ -64,6 +100,51 @@ export function parseMcpServers(raw) {
|
|
|
64
100
|
transport: cfg.transport,
|
|
65
101
|
headers:
|
|
66
102
|
cfg.headers && typeof cfg.headers === "object" ? cfg.headers : {},
|
|
103
|
+
...(Number.isFinite(cfg.maxPayloadBytes)
|
|
104
|
+
? { maxPayloadBytes: cfg.maxPayloadBytes }
|
|
105
|
+
: {}),
|
|
106
|
+
...(Number.isFinite(cfg.maxInboundMessagesPerSecond)
|
|
107
|
+
? {
|
|
108
|
+
maxInboundMessagesPerSecond: cfg.maxInboundMessagesPerSecond,
|
|
109
|
+
}
|
|
110
|
+
: {}),
|
|
111
|
+
...(Number.isFinite(cfg.maxInboundBytesPerMinute)
|
|
112
|
+
? { maxInboundBytesPerMinute: cfg.maxInboundBytesPerMinute }
|
|
113
|
+
: {}),
|
|
114
|
+
...(Number.isFinite(cfg.maxJsonDepth)
|
|
115
|
+
? { maxJsonDepth: cfg.maxJsonDepth }
|
|
116
|
+
: {}),
|
|
117
|
+
...(Number.isFinite(cfg.maxJsonNodes)
|
|
118
|
+
? { maxJsonNodes: cfg.maxJsonNodes }
|
|
119
|
+
: {}),
|
|
120
|
+
...(Number.isFinite(cfg.maxTools) ? { maxTools: cfg.maxTools } : {}),
|
|
121
|
+
...(Number.isFinite(cfg.maxToolDescriptionBytes)
|
|
122
|
+
? { maxToolDescriptionBytes: cfg.maxToolDescriptionBytes }
|
|
123
|
+
: {}),
|
|
124
|
+
...(Number.isFinite(cfg.maxToolSchemaBytes)
|
|
125
|
+
? { maxToolSchemaBytes: cfg.maxToolSchemaBytes }
|
|
126
|
+
: {}),
|
|
127
|
+
...(Number.isFinite(cfg.maxToolSchemaDepth)
|
|
128
|
+
? { maxToolSchemaDepth: cfg.maxToolSchemaDepth }
|
|
129
|
+
: {}),
|
|
130
|
+
...(Number.isFinite(cfg.maxToolSchemaNodes)
|
|
131
|
+
? { maxToolSchemaNodes: cfg.maxToolSchemaNodes }
|
|
132
|
+
: {}),
|
|
133
|
+
...(Number.isFinite(cfg.maxToolDefinitionBytes)
|
|
134
|
+
? { maxToolDefinitionBytes: cfg.maxToolDefinitionBytes }
|
|
135
|
+
: {}),
|
|
136
|
+
...(Number.isFinite(cfg.maxToolMetadataBytes)
|
|
137
|
+
? { maxToolMetadataBytes: cfg.maxToolMetadataBytes }
|
|
138
|
+
: {}),
|
|
139
|
+
...(Number.isFinite(cfg.maxToolResultBytes)
|
|
140
|
+
? { maxToolResultBytes: cfg.maxToolResultBytes }
|
|
141
|
+
: {}),
|
|
142
|
+
...(Number.isFinite(cfg.maxToolResultDepth)
|
|
143
|
+
? { maxToolResultDepth: cfg.maxToolResultDepth }
|
|
144
|
+
: {}),
|
|
145
|
+
...(Number.isFinite(cfg.maxToolResultNodes)
|
|
146
|
+
? { maxToolResultNodes: cfg.maxToolResultNodes }
|
|
147
|
+
: {}),
|
|
67
148
|
...headersHelperField(cfg.headersHelper),
|
|
68
149
|
...(cfg.configScope ? { configScope: cfg.configScope } : {}),
|
|
69
150
|
...(cfg.configSource ? { configSource: cfg.configSource } : {}),
|
|
@@ -83,6 +164,12 @@ export async function loadManagedMcp(settings, deps = {}) {
|
|
|
83
164
|
config.configScope = "managed";
|
|
84
165
|
config.configSource = deps.managedFile || "managed-settings";
|
|
85
166
|
}
|
|
167
|
+
authorizeStdioServers(
|
|
168
|
+
servers,
|
|
169
|
+
"managed-settings",
|
|
170
|
+
(_name, config) => config.configSource,
|
|
171
|
+
{ writeErr: deps.writeErr },
|
|
172
|
+
);
|
|
86
173
|
if (Object.keys(servers).length === 0) return deps.into || null;
|
|
87
174
|
(deps.writeErr || (() => {}))(
|
|
88
175
|
` mcp: ${Object.keys(servers).length} managed server(s) from ${deps.managedFile || "managed settings"}\n`,
|
|
@@ -210,6 +297,9 @@ export async function setupMcpFromConfig(servers, deps = {}) {
|
|
|
210
297
|
) {
|
|
211
298
|
result.instructionsByServer = {};
|
|
212
299
|
}
|
|
300
|
+
if (!Number.isSafeInteger(result._mcpToolMetadataBytes)) {
|
|
301
|
+
result._mcpToolMetadataBytes = 0;
|
|
302
|
+
}
|
|
213
303
|
const {
|
|
214
304
|
mcpClient,
|
|
215
305
|
extraToolDefinitions,
|
|
@@ -290,7 +380,18 @@ export async function setupMcpFromConfig(servers, deps = {}) {
|
|
|
290
380
|
writeErr(line);
|
|
291
381
|
continue;
|
|
292
382
|
}
|
|
293
|
-
|
|
383
|
+
let tools = [];
|
|
384
|
+
let toolMetadataError = null;
|
|
385
|
+
try {
|
|
386
|
+
const admitted = admitMcpToolList(name, res?.tools ?? [], cfg, {
|
|
387
|
+
clientBytesUsed: result._mcpToolMetadataBytes,
|
|
388
|
+
});
|
|
389
|
+
tools = admitted.tools;
|
|
390
|
+
result._mcpToolMetadataBytes += admitted.metadataBytes;
|
|
391
|
+
} catch (error) {
|
|
392
|
+
if (!isMcpToolMetadataError(error)) throw error;
|
|
393
|
+
toolMetadataError = error;
|
|
394
|
+
}
|
|
294
395
|
const resources = Array.isArray(res?.resources) ? res.resources : [];
|
|
295
396
|
const prompts = Array.isArray(res?.prompts) ? res.prompts : [];
|
|
296
397
|
// A server can connect (initialize OK) yet have its tools/list fail — the
|
|
@@ -304,6 +405,11 @@ export async function setupMcpFromConfig(servers, deps = {}) {
|
|
|
304
405
|
if (hint) line += hint;
|
|
305
406
|
writeErr(line);
|
|
306
407
|
}
|
|
408
|
+
if (toolMetadataError) {
|
|
409
|
+
writeErr(
|
|
410
|
+
` mcp: "${name}" connected but tool metadata was rejected: ${toolMetadataError.message}\n`,
|
|
411
|
+
);
|
|
412
|
+
}
|
|
307
413
|
connected.push({
|
|
308
414
|
server: name,
|
|
309
415
|
tools: tools.length,
|
|
@@ -321,6 +427,18 @@ export async function setupMcpFromConfig(servers, deps = {}) {
|
|
|
321
427
|
if (!p || !p.name) continue;
|
|
322
428
|
result.prompts.push({ ...p, server: name });
|
|
323
429
|
}
|
|
430
|
+
const toolResultConfig = Object.freeze({
|
|
431
|
+
...(Number.isFinite(cfg.maxToolResultBytes)
|
|
432
|
+
? { maxToolResultBytes: cfg.maxToolResultBytes }
|
|
433
|
+
: {}),
|
|
434
|
+
...(Number.isFinite(cfg.maxToolResultDepth)
|
|
435
|
+
? { maxToolResultDepth: cfg.maxToolResultDepth }
|
|
436
|
+
: {}),
|
|
437
|
+
...(Number.isFinite(cfg.maxToolResultNodes)
|
|
438
|
+
? { maxToolResultNodes: cfg.maxToolResultNodes }
|
|
439
|
+
: {}),
|
|
440
|
+
});
|
|
441
|
+
const hasToolResultConfig = Object.keys(toolResultConfig).length > 0;
|
|
324
442
|
for (const t of tools) {
|
|
325
443
|
if (!t || !t.name) continue;
|
|
326
444
|
const full = mcpToolName(name, t.name);
|
|
@@ -337,6 +455,7 @@ export async function setupMcpFromConfig(servers, deps = {}) {
|
|
|
337
455
|
kind: "mcp",
|
|
338
456
|
serverName: name,
|
|
339
457
|
toolName: t.name,
|
|
458
|
+
...(hasToolResultConfig ? { toolResultConfig } : {}),
|
|
340
459
|
};
|
|
341
460
|
externalToolDescriptors[full] = {
|
|
342
461
|
name: full,
|
|
@@ -510,6 +629,12 @@ export async function loadMcpConfig(filePath, deps = {}) {
|
|
|
510
629
|
`(expected {"mcpServers": {"name": {"command": "..."}}}).`,
|
|
511
630
|
);
|
|
512
631
|
}
|
|
632
|
+
authorizeStdioServers(
|
|
633
|
+
servers,
|
|
634
|
+
"explicit-config",
|
|
635
|
+
() => path.resolve(filePath),
|
|
636
|
+
{ failFast: true },
|
|
637
|
+
);
|
|
513
638
|
return setupMcpFromConfig(servers, deps);
|
|
514
639
|
}
|
|
515
640
|
|
|
@@ -555,6 +680,13 @@ export async function loadRegisteredMcp(rawDb, deps = {}) {
|
|
|
555
680
|
};
|
|
556
681
|
}
|
|
557
682
|
if (Object.keys(servers).length === 0) return deps.into || null;
|
|
683
|
+
authorizeStdioServers(
|
|
684
|
+
servers,
|
|
685
|
+
"registered-config",
|
|
686
|
+
(name, config) =>
|
|
687
|
+
`${config.configScope || "user"}:${config.configSource || "user-database"}:${name}`,
|
|
688
|
+
{ writeErr: deps.writeErr },
|
|
689
|
+
);
|
|
558
690
|
return setupMcpFromConfig(servers, deps);
|
|
559
691
|
}
|
|
560
692
|
|
|
@@ -786,14 +918,16 @@ export async function loadProjectMcp(opts = {}, deps = {}) {
|
|
|
786
918
|
config.configScope = "project";
|
|
787
919
|
config.configSource = file;
|
|
788
920
|
config.projectPath = path.dirname(file);
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
921
|
+
const needsExecutionAuthority =
|
|
922
|
+
config.headersHelper || inferTransport(config) === "stdio";
|
|
923
|
+
if (
|
|
924
|
+
needsExecutionAuthority &&
|
|
925
|
+
typeof trust.issueProjectMcpWorkspaceAuthority !== "function"
|
|
926
|
+
) {
|
|
927
|
+
delete parsed[name];
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
if (needsExecutionAuthority) {
|
|
797
931
|
config.projectMcpWorkspaceAuthority =
|
|
798
932
|
trust.issueProjectMcpWorkspaceAuthority({
|
|
799
933
|
file,
|
|
@@ -813,6 +947,12 @@ export async function loadProjectMcp(opts = {}, deps = {}) {
|
|
|
813
947
|
` mcp: ${Object.keys(servers).length} server(s) from project .mcp.json ` +
|
|
814
948
|
`(${seenFiles.join(", ")}) — loaded via --project-mcp\n`,
|
|
815
949
|
);
|
|
950
|
+
authorizeStdioServers(
|
|
951
|
+
servers,
|
|
952
|
+
"project-config",
|
|
953
|
+
(name, config) => `${config.configSource}:${name}`,
|
|
954
|
+
{ writeErr },
|
|
955
|
+
);
|
|
816
956
|
return setupMcpFromConfig(servers, deps);
|
|
817
957
|
}
|
|
818
958
|
|
|
@@ -856,6 +996,13 @@ export async function loadPluginMcp(opts = {}, deps = {}) {
|
|
|
856
996
|
writeErr(
|
|
857
997
|
` mcp: ${names.length} server(s) from trusted plugin(s): ${names.join(", ")}\n`,
|
|
858
998
|
);
|
|
999
|
+
authorizeStdioServers(
|
|
1000
|
+
servers,
|
|
1001
|
+
"trusted-plugin",
|
|
1002
|
+
(name, config) =>
|
|
1003
|
+
`${config.pluginId || "plugin"}@${config.pluginVersion || "unknown"}:${config.pluginSource || "unknown"}:${name}`,
|
|
1004
|
+
{ writeErr },
|
|
1005
|
+
);
|
|
859
1006
|
return setupMcpFromConfig(servers, deps);
|
|
860
1007
|
}
|
|
861
1008
|
|
|
Binary file
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_MAX_QUEUED_BYTES = 1024 * 1024;
|
|
4
|
+
const HOST_OUTPUT_DRAIN_TIMEOUT_MS = 30_000;
|
|
5
|
+
|
|
6
|
+
function normalizeDrainTimeoutMs(value) {
|
|
7
|
+
return Number.isFinite(value) &&
|
|
8
|
+
value > 0 &&
|
|
9
|
+
value <= HOST_OUTPUT_DRAIN_TIMEOUT_MS
|
|
10
|
+
? Math.max(1, Math.floor(value))
|
|
11
|
+
: HOST_OUTPUT_DRAIN_TIMEOUT_MS;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function outputError(code, message, details = {}) {
|
|
15
|
+
const error = new Error(message);
|
|
16
|
+
error.code = code;
|
|
17
|
+
error.isOutputBackpressureFailure = true;
|
|
18
|
+
Object.assign(error, details);
|
|
19
|
+
return error;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function chunkBytes(chunk, encoding) {
|
|
23
|
+
if (Buffer.isBuffer(chunk) || ArrayBuffer.isView(chunk)) {
|
|
24
|
+
return chunk.byteLength;
|
|
25
|
+
}
|
|
26
|
+
return Buffer.byteLength(String(chunk), encoding || "utf8");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Honor Writable.write(false) without forcing every existing output call site
|
|
31
|
+
* to become async. Once the native stream applies backpressure, later chunks
|
|
32
|
+
* stay in a bounded host queue until `drain`; producers await `wait()` at their
|
|
33
|
+
* event boundaries, so model/input consumption stops instead of growing the
|
|
34
|
+
* native Writable queue without limit.
|
|
35
|
+
*/
|
|
36
|
+
export function createWritableBackpressureGate(
|
|
37
|
+
stream,
|
|
38
|
+
{
|
|
39
|
+
label = "output",
|
|
40
|
+
maxQueuedBytes = DEFAULT_MAX_QUEUED_BYTES,
|
|
41
|
+
drainTimeoutMs,
|
|
42
|
+
onFailure = null,
|
|
43
|
+
} = {},
|
|
44
|
+
) {
|
|
45
|
+
if (!stream || typeof stream.write !== "function") {
|
|
46
|
+
throw new TypeError(`${label} must be a writable stream`);
|
|
47
|
+
}
|
|
48
|
+
// Capture the native writer before a caller optionally installs this gate as
|
|
49
|
+
// `stream.write`. Looking it up dynamically would recurse after installation.
|
|
50
|
+
const nativeWrite = stream.write.bind(stream);
|
|
51
|
+
const queueLimit =
|
|
52
|
+
Number.isSafeInteger(maxQueuedBytes) && maxQueuedBytes > 0
|
|
53
|
+
? maxQueuedBytes
|
|
54
|
+
: DEFAULT_MAX_QUEUED_BYTES;
|
|
55
|
+
const queue = [];
|
|
56
|
+
const outputDrainTimeoutMs = normalizeDrainTimeoutMs(drainTimeoutMs);
|
|
57
|
+
let queuedBytes = 0;
|
|
58
|
+
let blocked = false;
|
|
59
|
+
let failure = null;
|
|
60
|
+
let waitPromise = null;
|
|
61
|
+
let resolveWait = null;
|
|
62
|
+
let rejectWait = null;
|
|
63
|
+
let disposed = false;
|
|
64
|
+
let backpressureCount = 0;
|
|
65
|
+
let drainTimer = null;
|
|
66
|
+
let blockedAt = null;
|
|
67
|
+
|
|
68
|
+
const clearDrainDeadline = () => {
|
|
69
|
+
if (drainTimer !== null) clearTimeout(drainTimer);
|
|
70
|
+
drainTimer = null;
|
|
71
|
+
blockedAt = null;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const notifyCallback = (callback, error) => {
|
|
75
|
+
if (typeof callback !== "function") return;
|
|
76
|
+
queueMicrotask(() => {
|
|
77
|
+
try {
|
|
78
|
+
callback(error);
|
|
79
|
+
} catch {
|
|
80
|
+
// A write callback is observational and cannot replace gate failure.
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const ensureWait = () => {
|
|
86
|
+
if (waitPromise) return;
|
|
87
|
+
waitPromise = new Promise((resolve, reject) => {
|
|
88
|
+
resolveWait = resolve;
|
|
89
|
+
rejectWait = reject;
|
|
90
|
+
});
|
|
91
|
+
// A caller may rely only on event-driven writes (for example a coalescer
|
|
92
|
+
// timer) and observe the failure later. Mark the promise handled now while
|
|
93
|
+
// preserving rejection for every explicit `wait()` caller.
|
|
94
|
+
waitPromise.catch(() => {});
|
|
95
|
+
};
|
|
96
|
+
const finishWait = () => {
|
|
97
|
+
clearDrainDeadline();
|
|
98
|
+
const resolve = resolveWait;
|
|
99
|
+
waitPromise = null;
|
|
100
|
+
resolveWait = null;
|
|
101
|
+
rejectWait = null;
|
|
102
|
+
resolve?.();
|
|
103
|
+
};
|
|
104
|
+
const fail = (error) => {
|
|
105
|
+
if (failure) return;
|
|
106
|
+
clearDrainDeadline();
|
|
107
|
+
failure = error;
|
|
108
|
+
blocked = false;
|
|
109
|
+
const discarded = queue.splice(0);
|
|
110
|
+
queuedBytes = 0;
|
|
111
|
+
for (const entry of discarded) notifyCallback(entry.callback, error);
|
|
112
|
+
ensureWait();
|
|
113
|
+
rejectWait?.(error);
|
|
114
|
+
try {
|
|
115
|
+
onFailure?.(error);
|
|
116
|
+
} catch {
|
|
117
|
+
// Failure notification must never replace the original stream error.
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const startDrainDeadline = () => {
|
|
121
|
+
if (drainTimer !== null || failure || disposed) return;
|
|
122
|
+
blockedAt = Date.now();
|
|
123
|
+
drainTimer = setTimeout(() => {
|
|
124
|
+
const elapsedMs = Math.max(0, Date.now() - blockedAt);
|
|
125
|
+
fail(
|
|
126
|
+
outputError(
|
|
127
|
+
"CC_OUTPUT_BACKPRESSURE_TIMEOUT",
|
|
128
|
+
`${label} did not drain within ${outputDrainTimeoutMs} ms`,
|
|
129
|
+
{
|
|
130
|
+
timeoutMs: outputDrainTimeoutMs,
|
|
131
|
+
elapsedMs,
|
|
132
|
+
queuedBytes,
|
|
133
|
+
queuedChunks: queue.length,
|
|
134
|
+
},
|
|
135
|
+
),
|
|
136
|
+
);
|
|
137
|
+
}, outputDrainTimeoutMs);
|
|
138
|
+
};
|
|
139
|
+
const writeNative = (chunk, encoding, callback) => {
|
|
140
|
+
let accepted;
|
|
141
|
+
try {
|
|
142
|
+
accepted =
|
|
143
|
+
typeof callback === "function"
|
|
144
|
+
? encoding != null
|
|
145
|
+
? nativeWrite(chunk, encoding, callback)
|
|
146
|
+
: nativeWrite(chunk, callback)
|
|
147
|
+
: encoding != null
|
|
148
|
+
? nativeWrite(chunk, encoding)
|
|
149
|
+
: nativeWrite(chunk);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
const wrapped = outputError(
|
|
152
|
+
error?.code === "EPIPE" ? "EPIPE" : "CC_OUTPUT_STREAM_ERROR",
|
|
153
|
+
`${label} write failed: ${error?.message || error}`,
|
|
154
|
+
{ cause: error },
|
|
155
|
+
);
|
|
156
|
+
notifyCallback(callback, wrapped);
|
|
157
|
+
fail(wrapped);
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
if (accepted === false) {
|
|
161
|
+
blocked = true;
|
|
162
|
+
backpressureCount += 1;
|
|
163
|
+
ensureWait();
|
|
164
|
+
startDrainDeadline();
|
|
165
|
+
}
|
|
166
|
+
return accepted !== false;
|
|
167
|
+
};
|
|
168
|
+
const onDrain = () => {
|
|
169
|
+
if (disposed || failure) return;
|
|
170
|
+
blocked = false;
|
|
171
|
+
while (queue.length > 0 && !blocked && !failure) {
|
|
172
|
+
const entry = queue.shift();
|
|
173
|
+
queuedBytes -= entry.bytes;
|
|
174
|
+
writeNative(entry.chunk, entry.encoding, entry.callback);
|
|
175
|
+
}
|
|
176
|
+
if (!blocked && queue.length === 0 && !failure) finishWait();
|
|
177
|
+
};
|
|
178
|
+
const onError = (cause) => {
|
|
179
|
+
fail(
|
|
180
|
+
outputError(
|
|
181
|
+
cause?.code === "EPIPE" ? "EPIPE" : "CC_OUTPUT_STREAM_ERROR",
|
|
182
|
+
`${label} stream failed: ${cause?.message || cause}`,
|
|
183
|
+
{ cause },
|
|
184
|
+
),
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
stream.on?.("drain", onDrain);
|
|
189
|
+
stream.on?.("error", onError);
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
write(chunk, encoding, callback) {
|
|
193
|
+
if (typeof encoding === "function") {
|
|
194
|
+
callback = encoding;
|
|
195
|
+
encoding = undefined;
|
|
196
|
+
}
|
|
197
|
+
if (disposed || failure) {
|
|
198
|
+
notifyCallback(
|
|
199
|
+
callback,
|
|
200
|
+
failure ||
|
|
201
|
+
outputError(
|
|
202
|
+
"CC_OUTPUT_BACKPRESSURE_DISPOSED",
|
|
203
|
+
`${label} is no longer writable`,
|
|
204
|
+
),
|
|
205
|
+
);
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
if (!blocked && queue.length === 0) {
|
|
209
|
+
return writeNative(chunk, encoding, callback);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const bytes = chunkBytes(chunk, encoding);
|
|
213
|
+
if (queuedBytes + bytes > queueLimit) {
|
|
214
|
+
const overflow = outputError(
|
|
215
|
+
"CC_OUTPUT_BACKPRESSURE_OVERFLOW",
|
|
216
|
+
`${label} backpressure queue exceeded ${queueLimit} bytes`,
|
|
217
|
+
{ queuedBytes, incomingBytes: bytes, maxQueuedBytes: queueLimit },
|
|
218
|
+
);
|
|
219
|
+
notifyCallback(callback, overflow);
|
|
220
|
+
fail(overflow);
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
queue.push({ chunk, encoding, callback, bytes });
|
|
224
|
+
queuedBytes += bytes;
|
|
225
|
+
ensureWait();
|
|
226
|
+
return false;
|
|
227
|
+
},
|
|
228
|
+
async wait() {
|
|
229
|
+
if (failure) throw failure;
|
|
230
|
+
if (blocked || queue.length > 0) {
|
|
231
|
+
ensureWait();
|
|
232
|
+
await waitPromise;
|
|
233
|
+
}
|
|
234
|
+
if (failure) throw failure;
|
|
235
|
+
},
|
|
236
|
+
snapshot() {
|
|
237
|
+
return Object.freeze({
|
|
238
|
+
label,
|
|
239
|
+
blocked,
|
|
240
|
+
queuedBytes,
|
|
241
|
+
queuedChunks: queue.length,
|
|
242
|
+
backpressureCount,
|
|
243
|
+
drainTimeoutMs: outputDrainTimeoutMs,
|
|
244
|
+
blockedForMs:
|
|
245
|
+
blockedAt === null ? 0 : Math.max(0, Date.now() - blockedAt),
|
|
246
|
+
failed: failure != null,
|
|
247
|
+
failureCode: failure?.code || null,
|
|
248
|
+
});
|
|
249
|
+
},
|
|
250
|
+
dispose() {
|
|
251
|
+
if (disposed) return;
|
|
252
|
+
disposed = true;
|
|
253
|
+
stream.removeListener?.("drain", onDrain);
|
|
254
|
+
stream.removeListener?.("error", onError);
|
|
255
|
+
if (!failure && (blocked || queue.length > 0)) {
|
|
256
|
+
fail(
|
|
257
|
+
outputError(
|
|
258
|
+
"CC_OUTPUT_BACKPRESSURE_DISPOSED",
|
|
259
|
+
`${label} disposed before queued output drained`,
|
|
260
|
+
),
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Install a gate directly on a Writable's `write` method. This is intended for
|
|
269
|
+
* readline/TTY owners whose transitive writers all hold the same stream object
|
|
270
|
+
* and cannot each be converted to an async writer. The original method shape
|
|
271
|
+
* is restored exactly when `restore()` runs.
|
|
272
|
+
*/
|
|
273
|
+
export function installWritableBackpressureGate(stream, options = {}) {
|
|
274
|
+
const hadOwnWrite = Object.prototype.hasOwnProperty.call(stream, "write");
|
|
275
|
+
const ownWriteDescriptor = hadOwnWrite
|
|
276
|
+
? Object.getOwnPropertyDescriptor(stream, "write")
|
|
277
|
+
: null;
|
|
278
|
+
const gate = createWritableBackpressureGate(stream, options);
|
|
279
|
+
const installedWrite = (chunk, encoding, callback) =>
|
|
280
|
+
gate.write(chunk, encoding, callback);
|
|
281
|
+
try {
|
|
282
|
+
Object.defineProperty(stream, "write", {
|
|
283
|
+
configurable: true,
|
|
284
|
+
enumerable: ownWriteDescriptor?.enumerable ?? false,
|
|
285
|
+
writable: true,
|
|
286
|
+
value: installedWrite,
|
|
287
|
+
});
|
|
288
|
+
} catch (cause) {
|
|
289
|
+
gate.dispose();
|
|
290
|
+
throw outputError(
|
|
291
|
+
"CC_OUTPUT_BACKPRESSURE_INSTALL_FAILED",
|
|
292
|
+
`${options.label || "output"} write method cannot be gated`,
|
|
293
|
+
{ cause },
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
let restored = false;
|
|
298
|
+
return {
|
|
299
|
+
wait: gate.wait,
|
|
300
|
+
snapshot: gate.snapshot,
|
|
301
|
+
restore() {
|
|
302
|
+
if (restored) return;
|
|
303
|
+
restored = true;
|
|
304
|
+
if (stream.write === installedWrite) {
|
|
305
|
+
if (hadOwnWrite) {
|
|
306
|
+
Object.defineProperty(stream, "write", ownWriteDescriptor);
|
|
307
|
+
} else {
|
|
308
|
+
Reflect.deleteProperty(stream, "write");
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
gate.dispose();
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Install coordinated gates on stdout and stderr for a long-lived TTY owner. */
|
|
317
|
+
export function installOutputBackpressure({
|
|
318
|
+
stdout = process.stdout,
|
|
319
|
+
stderr = process.stderr,
|
|
320
|
+
maxQueuedBytes,
|
|
321
|
+
drainTimeoutMs,
|
|
322
|
+
onFailure,
|
|
323
|
+
} = {}) {
|
|
324
|
+
if (stdout === stderr) {
|
|
325
|
+
const shared = installWritableBackpressureGate(stdout, {
|
|
326
|
+
label: "output",
|
|
327
|
+
maxQueuedBytes,
|
|
328
|
+
drainTimeoutMs,
|
|
329
|
+
onFailure,
|
|
330
|
+
});
|
|
331
|
+
return {
|
|
332
|
+
wait: shared.wait,
|
|
333
|
+
snapshot: () => {
|
|
334
|
+
const current = shared.snapshot();
|
|
335
|
+
return { stdout: current, stderr: current };
|
|
336
|
+
},
|
|
337
|
+
restore: shared.restore,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
const out = installWritableBackpressureGate(stdout, {
|
|
341
|
+
label: "stdout",
|
|
342
|
+
maxQueuedBytes,
|
|
343
|
+
drainTimeoutMs,
|
|
344
|
+
onFailure,
|
|
345
|
+
});
|
|
346
|
+
let err;
|
|
347
|
+
try {
|
|
348
|
+
err = installWritableBackpressureGate(stderr, {
|
|
349
|
+
label: "stderr",
|
|
350
|
+
maxQueuedBytes,
|
|
351
|
+
drainTimeoutMs,
|
|
352
|
+
onFailure,
|
|
353
|
+
});
|
|
354
|
+
} catch (error) {
|
|
355
|
+
out.restore();
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
let restored = false;
|
|
359
|
+
return {
|
|
360
|
+
wait: () => Promise.all([out.wait(), err.wait()]),
|
|
361
|
+
snapshot: () => ({ stdout: out.snapshot(), stderr: err.snapshot() }),
|
|
362
|
+
restore() {
|
|
363
|
+
if (restored) return;
|
|
364
|
+
restored = true;
|
|
365
|
+
out.restore();
|
|
366
|
+
err.restore();
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function injectedWriter(write) {
|
|
372
|
+
return {
|
|
373
|
+
write,
|
|
374
|
+
wait: async () => {},
|
|
375
|
+
snapshot: () => null,
|
|
376
|
+
dispose: () => {},
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Create one coordinator for stdout and stderr. */
|
|
381
|
+
export function createHeadlessOutputBackpressure({
|
|
382
|
+
stdout = process.stdout,
|
|
383
|
+
stderr = process.stderr,
|
|
384
|
+
writeOut = null,
|
|
385
|
+
writeErr = null,
|
|
386
|
+
maxQueuedBytes,
|
|
387
|
+
drainTimeoutMs,
|
|
388
|
+
onFailure,
|
|
389
|
+
} = {}) {
|
|
390
|
+
const out = writeOut
|
|
391
|
+
? injectedWriter(writeOut)
|
|
392
|
+
: createWritableBackpressureGate(stdout, {
|
|
393
|
+
label: "stdout",
|
|
394
|
+
maxQueuedBytes,
|
|
395
|
+
drainTimeoutMs,
|
|
396
|
+
onFailure,
|
|
397
|
+
});
|
|
398
|
+
const err = writeErr
|
|
399
|
+
? injectedWriter(writeErr)
|
|
400
|
+
: createWritableBackpressureGate(stderr, {
|
|
401
|
+
label: "stderr",
|
|
402
|
+
maxQueuedBytes,
|
|
403
|
+
drainTimeoutMs,
|
|
404
|
+
onFailure,
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
writeOut: (chunk, encoding) => out.write(chunk, encoding),
|
|
409
|
+
writeErr: (chunk, encoding) => err.write(chunk, encoding),
|
|
410
|
+
wait: () => Promise.all([out.wait(), err.wait()]),
|
|
411
|
+
snapshot: () => ({ stdout: out.snapshot(), stderr: err.snapshot() }),
|
|
412
|
+
dispose() {
|
|
413
|
+
out.dispose();
|
|
414
|
+
err.dispose();
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export const MAX_OUTPUT_DRAIN_TIMEOUT_MS = HOST_OUTPUT_DRAIN_TIMEOUT_MS;
|