zidane 7.2.5 → 8.0.4
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/LICENSE +21 -0
- package/README.md +34 -6
- package/dist/{acp-D-tVyUjw.js → acp-DQDhi2dj.js} +230 -77
- package/dist/acp-DQDhi2dj.js.map +1 -0
- package/dist/acp-cli.d.ts +1 -1
- package/dist/acp-cli.js +9 -66
- package/dist/acp-cli.js.map +1 -1
- package/dist/acp.d.ts +10 -590
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +3 -3
- package/dist/{agent-D0zZnGbt.js → agent-DSLNmBp0.js} +14401 -13214
- package/dist/agent-DSLNmBp0.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +3 -2
- package/dist/aliasing-lVFbm81n.js +217 -0
- package/dist/aliasing-lVFbm81n.js.map +1 -0
- package/dist/{anthropic-C2aole0G.js → anthropic-CD00LhUP.js} +1442 -1264
- package/dist/anthropic-CD00LhUP.js.map +1 -0
- package/dist/artifacts-CV2hUKnL.js.map +1 -1
- package/dist/{atomic-write-B7zr1ddF.js → atomic-write-Ijgf2Mhc.js} +13 -3
- package/dist/atomic-write-Ijgf2Mhc.js.map +1 -0
- package/dist/call-tool-dHlbGOIY.js +70 -0
- package/dist/call-tool-dHlbGOIY.js.map +1 -0
- package/dist/chat/pure.d.ts +372 -6
- package/dist/chat/pure.d.ts.map +1 -1
- package/dist/chat/pure.js +35 -244
- package/dist/chat/pure.js.map +1 -1
- package/dist/{approvals-DKaJZYlm.js → chat-JPJJ8uu5.js} +15879 -14631
- package/dist/chat-JPJJ8uu5.js.map +1 -0
- package/dist/chat.d.ts +4 -6
- package/dist/chat.js +23 -12
- package/dist/cli-shared-C-YCaglL.js +159 -0
- package/dist/cli-shared-C-YCaglL.js.map +1 -0
- package/dist/client-core-BQSyDALT.d.ts +2115 -0
- package/dist/client-core-BQSyDALT.d.ts.map +1 -0
- package/dist/codemode-worker.d.ts +1 -0
- package/dist/codemode-worker.js +155 -0
- package/dist/codemode-worker.js.map +1 -0
- package/dist/codemode.d.ts +352 -0
- package/dist/codemode.d.ts.map +1 -0
- package/dist/codemode.js +5 -0
- package/dist/connection-Dr-Js4yZ.d.ts +28 -0
- package/dist/connection-Dr-Js4yZ.d.ts.map +1 -0
- package/dist/connection-loop-gdh22LTI.js +112 -0
- package/dist/connection-loop-gdh22LTI.js.map +1 -0
- package/dist/context-segments-BHKlagL6.js +1016 -0
- package/dist/context-segments-BHKlagL6.js.map +1 -0
- package/dist/{context-transport-T_6AYIvy.js → context-transport-DSMTUsK1.js} +3 -3
- package/dist/{context-transport-T_6AYIvy.js.map → context-transport-DSMTUsK1.js.map} +1 -1
- package/dist/contexts/daytona.d.ts +19 -8
- package/dist/contexts/daytona.d.ts.map +1 -1
- package/dist/contexts/daytona.js +1 -440
- package/dist/contexts/docker.d.ts +36 -7
- package/dist/contexts/docker.d.ts.map +1 -1
- package/dist/contexts/docker.js +1 -456
- package/dist/contexts/e2b.d.ts +68 -13
- package/dist/contexts/e2b.d.ts.map +1 -1
- package/dist/contexts/e2b.js +1 -382
- package/dist/contexts/sandbox.d.ts +1 -1
- package/dist/contexts/sandbox.js +5 -0
- package/dist/contexts/sandbox.js.map +1 -1
- package/dist/{contexts-CRE9pw07.js → contexts-d7UolYot.js} +1059 -524
- package/dist/contexts-d7UolYot.js.map +1 -0
- package/dist/contexts.d.ts +3 -3
- package/dist/contexts.js +1 -1
- package/dist/cred-keys-GpBg71cP.js +105 -0
- package/dist/cred-keys-GpBg71cP.js.map +1 -0
- package/dist/{auth-BG2rVKSt.js → credentials-JttEuRON.js} +55 -206
- package/dist/credentials-JttEuRON.js.map +1 -0
- package/dist/{cursor-iojRXHF9.js → cursor-CdjaQTBW.js} +3 -3
- package/dist/{cursor-iojRXHF9.js.map → cursor-CdjaQTBW.js.map} +1 -1
- package/dist/daemon-cli.d.ts +1 -1
- package/dist/daemon-cli.js +3 -2
- package/dist/daemon-cli.js.map +1 -1
- package/dist/daytona-DG3omipZ.js +513 -0
- package/dist/daytona-DG3omipZ.js.map +1 -0
- package/dist/defaults-Cx3zvDOV.js +23 -0
- package/dist/defaults-Cx3zvDOV.js.map +1 -0
- package/dist/dispatcher-BMlu2BmZ.js +4941 -0
- package/dist/dispatcher-BMlu2BmZ.js.map +1 -0
- package/dist/docker-BZhMMJUp.js +727 -0
- package/dist/docker-BZhMMJUp.js.map +1 -0
- package/dist/e2b-CEVOdRib.js +480 -0
- package/dist/e2b-CEVOdRib.js.map +1 -0
- package/dist/edit-utils-EGosADZq.js +234 -0
- package/dist/edit-utils-EGosADZq.js.map +1 -0
- package/dist/environment-8wsKE2Ln.js +214 -0
- package/dist/environment-8wsKE2Ln.js.map +1 -0
- package/dist/eval-BNcvle_J.js +1595 -0
- package/dist/eval-BNcvle_J.js.map +1 -0
- package/dist/eval.d.ts +2 -2
- package/dist/eval.js +1 -1489
- package/dist/executor-Cx5-8yqi.js +621 -0
- package/dist/executor-Cx5-8yqi.js.map +1 -0
- package/dist/extension-authoring-DyGGmq9V.js +385 -0
- package/dist/extension-authoring-DyGGmq9V.js.map +1 -0
- package/dist/extensions.d.ts +2 -2
- package/dist/extensions.js +3 -3
- package/dist/{fetch-url-DX75gpL0.js → fetch-url-Ct2TjDWt.js} +4 -4
- package/dist/{fetch-url-DX75gpL0.js.map → fetch-url-Ct2TjDWt.js.map} +1 -1
- package/dist/{format-BF745pLL.js → format-C2NQEVUH.js} +39 -2
- package/dist/format-C2NQEVUH.js.map +1 -0
- package/dist/format-bytes-C7tfc3Zm.js +55 -0
- package/dist/format-bytes-C7tfc3Zm.js.map +1 -0
- package/dist/headless-C_rNUuYC.js +960 -0
- package/dist/headless-C_rNUuYC.js.map +1 -0
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -413
- package/dist/{history-search-C4bk1Wz_.js → history-search-CcYzF8-0.js} +7 -3
- package/dist/history-search-CcYzF8-0.js.map +1 -0
- package/dist/history-search-worker.d.ts +1 -1
- package/dist/history-search-worker.js +1 -1
- package/dist/host-D7NrI03V.js +378 -0
- package/dist/host-D7NrI03V.js.map +1 -0
- package/dist/host-signals-BcP5QKUD.js +118 -0
- package/dist/host-signals-BcP5QKUD.js.map +1 -0
- package/dist/identity-CwfO8K-t.js +26 -0
- package/dist/identity-CwfO8K-t.js.map +1 -0
- package/dist/{index-BgN1T3lk.d.ts → index-BGe1fQT7.d.ts} +10987 -9320
- package/dist/index-BGe1fQT7.d.ts.map +1 -0
- package/dist/index-BVQsIKGn.d.ts +448 -0
- package/dist/index-BVQsIKGn.d.ts.map +1 -0
- package/dist/{index-C6NEd5y_.d.ts → index-CHABJd-L.d.ts} +3386 -502
- package/dist/index-CHABJd-L.d.ts.map +1 -0
- package/dist/{index-C37_ppWv.d.ts → index-D6a7qXpg.d.ts} +2 -3
- package/dist/index-D6a7qXpg.d.ts.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +27 -23
- package/dist/{completion-core-BRo86R_L.js → inline-ghost-BVRQeGoL.js} +50 -2
- package/dist/inline-ghost-BVRQeGoL.js.map +1 -0
- package/dist/ipc/remote.d.ts +313 -0
- package/dist/ipc/remote.d.ts.map +1 -0
- package/dist/ipc/remote.js +4 -0
- package/dist/ipc.d.ts +6 -964
- package/dist/ipc.js +403 -4
- package/dist/ipc.js.map +1 -0
- package/dist/{isolation-Kfoof0MM.d.ts → isolation-D9L650W5.d.ts} +100 -15
- package/dist/isolation-D9L650W5.d.ts.map +1 -0
- package/dist/json-rpc--HXQti1E.js +350 -0
- package/dist/json-rpc--HXQti1E.js.map +1 -0
- package/dist/links-j8rED1sR.js +603 -0
- package/dist/links-j8rED1sR.js.map +1 -0
- package/dist/local-terminal-D8QThwo_.js +252 -0
- package/dist/local-terminal-D8QThwo_.js.map +1 -0
- package/dist/{logger--C1VQjwJ.js → logger-Gtqlayx8.js} +199 -173
- package/dist/logger-Gtqlayx8.js.map +1 -0
- package/dist/logs-view-CWG8aUAG.js.map +1 -1
- package/dist/{mcp-CgNd-f3F.js → mcp-_wecH9Qe.js} +608 -562
- package/dist/mcp-_wecH9Qe.js.map +1 -0
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +2 -2
- package/dist/media-sniff-UKijEaiQ.js.map +1 -1
- package/dist/{messages-2jcOA_5R.js → messages-BqiwcD2Q.js} +1017 -1014
- package/dist/messages-BqiwcD2Q.js.map +1 -0
- package/dist/{oauth-DxHO17JE.js → oauth-Px_pBB9W.js} +5 -5
- package/dist/{oauth-DxHO17JE.js.map → oauth-Px_pBB9W.js.map} +1 -1
- package/dist/{openai-compat-Dy56weK8.js → openai-compat-CFrpH7uq.js} +432 -589
- package/dist/openai-compat-CFrpH7uq.js.map +1 -0
- package/dist/orchestration-BCGmB8Ps.js +511 -0
- package/dist/orchestration-BCGmB8Ps.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -2
- package/dist/output/stream-json.d.ts.map +1 -1
- package/dist/output/stream-json.js +1 -1
- package/dist/output/terminal.d.ts +1 -2
- package/dist/output/terminal.d.ts.map +1 -1
- package/dist/output/terminal.js +13 -7
- package/dist/output/terminal.js.map +1 -1
- package/dist/{plugins-gbI7SBA6.js → plugins-DS7mahzZ.js} +20 -101
- package/dist/plugins-DS7mahzZ.js.map +1 -0
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +1 -2
- package/dist/prelude-Cxo4t74B.js +517 -0
- package/dist/prelude-Cxo4t74B.js.map +1 -0
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -2
- package/dist/project-root-B8zADr4o.js +167 -0
- package/dist/project-root-B8zADr4o.js.map +1 -0
- package/dist/project-user-paths-BSgtrRxl.js +58 -0
- package/dist/project-user-paths-BSgtrRxl.js.map +1 -0
- package/dist/{prompts-Pqc3JdW_.d.ts → prompts-DYcvqShf.d.ts} +14 -11
- package/dist/prompts-DYcvqShf.d.ts.map +1 -0
- package/dist/protocol-DnhqqSmE.js +13 -0
- package/dist/protocol-DnhqqSmE.js.map +1 -0
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +1 -2
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +3 -2
- package/dist/providers/cerebras.js.map +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -2
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +15 -12
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +3 -2
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +3 -3
- package/dist/{providers-BVhnFyiY.js → providers-XUR7F4Hi.js} +4 -4
- package/dist/{providers-BVhnFyiY.js.map → providers-XUR7F4Hi.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +4 -4
- package/dist/registry-D6ShBWJU.js +5234 -0
- package/dist/registry-D6ShBWJU.js.map +1 -0
- package/dist/remote-hosts-BtA_9ZGp.js +97 -0
- package/dist/remote-hosts-BtA_9ZGp.js.map +1 -0
- package/dist/restate.d.ts +2 -3
- package/dist/restate.d.ts.map +1 -1
- package/dist/restate.js +631 -572
- package/dist/restate.js.map +1 -1
- package/dist/run-summary-CklLiZ4y.js +288 -0
- package/dist/run-summary-CklLiZ4y.js.map +1 -0
- package/dist/{sandbox-BH0WQkXn.d.ts → sandbox-C5dR5DwQ.d.ts} +6 -3
- package/dist/sandbox-C5dR5DwQ.d.ts.map +1 -0
- package/dist/session/sqlite.d.ts +1 -2
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +1 -616
- package/dist/session-factory-BUGY5P40.js +1576 -0
- package/dist/session-factory-BUGY5P40.js.map +1 -0
- package/dist/{session-BqRP1hS7.js → session-xh9JXu6i.js} +94 -52
- package/dist/session-xh9JXu6i.js.map +1 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/settings-keys-Ckf29NeM.js +677 -0
- package/dist/settings-keys-Ckf29NeM.js.map +1 -0
- package/dist/shell-path-Dqaoow2J.js +412 -0
- package/dist/shell-path-Dqaoow2J.js.map +1 -0
- package/dist/{skills-BC3dodWp.js → skills-DKqZJm4X.js} +430 -470
- package/dist/skills-DKqZJm4X.js.map +1 -0
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -2
- package/dist/source-mPExaUm8.js +381 -0
- package/dist/source-mPExaUm8.js.map +1 -0
- package/dist/sqlite-DV1DQzN2.js +698 -0
- package/dist/sqlite-DV1DQzN2.js.map +1 -0
- package/dist/{src-CkNQKFKr.js → src-BRMYRJib.js} +903 -1121
- package/dist/src-BRMYRJib.js.map +1 -0
- package/dist/{stdio-DPpBDPx5.js → stdio-C6YyoW3I.js} +4 -4
- package/dist/{stdio-DPpBDPx5.js.map → stdio-C6YyoW3I.js.map} +1 -1
- package/dist/{stdio-loader-Bbx19dMZ.js → stdio-loader-C830Vg-Z.js} +4 -4
- package/dist/{stdio-loader-Bbx19dMZ.js.map → stdio-loader-C830Vg-Z.js.map} +1 -1
- package/dist/system-prompt-MNVHatSD.d.ts.map +1 -1
- package/dist/timeout-DEaqslLO.d.ts.map +1 -1
- package/dist/todo-edits-Dn251YUP.js +1790 -0
- package/dist/todo-edits-Dn251YUP.js.map +1 -0
- package/dist/{turn-operations-DrbJlpq9.js → tool-formatters-IQFDrdrs.js} +1456 -2120
- package/dist/tool-formatters-IQFDrdrs.js.map +1 -0
- package/dist/tool-state-Dku6YqAB.js +989 -0
- package/dist/tool-state-Dku6YqAB.js.map +1 -0
- package/dist/tools/fetch-url.d.ts +1 -2
- package/dist/tools/fetch-url.d.ts.map +1 -1
- package/dist/tools/fetch-url.js +1 -2
- package/dist/tools/prompts.d.ts +1 -1
- package/dist/tools/prompts.js +7 -7
- package/dist/tools/prompts.js.map +1 -1
- package/dist/tools/web-search.d.ts +1 -2
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +2 -2
- package/dist/tools/web-search.js.map +1 -1
- package/dist/tools-_DnT3AGM.js +578 -0
- package/dist/tools-_DnT3AGM.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +4 -2
- package/dist/tui.d.ts +534 -517
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +34933 -27243
- package/dist/tui.js.map +1 -1
- package/dist/turn-operations-B8sokwi5.js +333 -0
- package/dist/turn-operations-B8sokwi5.js.map +1 -0
- package/dist/turn-operations-DQX5ZZni.d.ts +3089 -0
- package/dist/turn-operations-DQX5ZZni.d.ts.map +1 -0
- package/dist/types-C7ZFOztE.d.ts +132 -0
- package/dist/types-C7ZFOztE.d.ts.map +1 -0
- package/dist/types-CH5XpIHw.js +114 -0
- package/dist/types-CH5XpIHw.js.map +1 -0
- package/dist/{types-CWgENVLY.js → types-Cu0ZGDu9.js} +10 -6
- package/dist/types-Cu0ZGDu9.js.map +1 -0
- package/dist/types-IqegbJEE.d.ts +624 -0
- package/dist/types-IqegbJEE.d.ts.map +1 -0
- package/dist/types.d.ts +4 -4
- package/dist/types.js +1 -1
- package/dist/url-D8HCl65b.js +1259 -0
- package/dist/url-D8HCl65b.js.map +1 -0
- package/dist/wasm-BVT-j5cl.js +33 -0
- package/dist/wasm-BVT-j5cl.js.map +1 -0
- package/dist/workspace-exec-stream-SV2R9d5S.js +355 -0
- package/dist/workspace-exec-stream-SV2R9d5S.js.map +1 -0
- package/dist/ws-CIHMvlSh.js +952 -0
- package/dist/ws-CIHMvlSh.js.map +1 -0
- package/dist/ws-client-N7NxzvQp.js +230 -0
- package/dist/ws-client-N7NxzvQp.js.map +1 -0
- package/dist/{xai-CazjStOK.js → xai-CvJTeLrI.js} +165 -5
- package/dist/xai-CvJTeLrI.js.map +1 -0
- package/dist/xdg-zweed0-t.js +117 -0
- package/dist/xdg-zweed0-t.js.map +1 -0
- package/docs/ACP.md +52 -0
- package/docs/ARCHITECTURE.md +103 -6
- package/docs/ARTIFACTS.md +8 -4
- package/docs/CHAT.md +165 -14
- package/docs/EXECUTION_CONTEXT.md +8 -0
- package/docs/EXTENSIONS.md +123 -2
- package/docs/HEADLESS.md +2 -0
- package/docs/IPC.md +775 -64
- package/docs/MACHINES.md +456 -0
- package/docs/PANELS.md +3 -0
- package/docs/README.md +4 -0
- package/docs/RESTATE.md +3 -2
- package/docs/RUN_IN_BACKGROUND.md +10 -2
- package/docs/SERVER.md +1085 -0
- package/docs/SKILL.md +6 -1
- package/docs/TERMINAL.md +75 -0
- package/docs/TUI.md +65 -14
- package/package.json +58 -26
- package/scripts/eval.ts +4 -0
- package/dist/acp-D-tVyUjw.js.map +0 -1
- package/dist/agent-D0zZnGbt.js.map +0 -1
- package/dist/anthropic-C2aole0G.js.map +0 -1
- package/dist/approvals-DKaJZYlm.js.map +0 -1
- package/dist/atomic-write-B7zr1ddF.js.map +0 -1
- package/dist/auth-BG2rVKSt.js.map +0 -1
- package/dist/completion-core-BRo86R_L.js.map +0 -1
- package/dist/contexts/daytona.js.map +0 -1
- package/dist/contexts/docker.js.map +0 -1
- package/dist/contexts/e2b.js.map +0 -1
- package/dist/contexts-CRE9pw07.js.map +0 -1
- package/dist/eval.js.map +0 -1
- package/dist/format-BF745pLL.js.map +0 -1
- package/dist/glob-DluQFSw1.js +0 -270
- package/dist/glob-DluQFSw1.js.map +0 -1
- package/dist/headless.js.map +0 -1
- package/dist/history-search-C4bk1Wz_.js.map +0 -1
- package/dist/index-BgN1T3lk.d.ts.map +0 -1
- package/dist/index-C37_ppWv.d.ts.map +0 -1
- package/dist/index-C6NEd5y_.d.ts.map +0 -1
- package/dist/ipc-BKVIIOqH.js +0 -331
- package/dist/ipc-BKVIIOqH.js.map +0 -1
- package/dist/ipc.d.ts.map +0 -1
- package/dist/isolation-Kfoof0MM.d.ts.map +0 -1
- package/dist/json-rpc-rSWMUh9b.js +0 -192
- package/dist/json-rpc-rSWMUh9b.js.map +0 -1
- package/dist/logger--C1VQjwJ.js.map +0 -1
- package/dist/mcp-CgNd-f3F.js.map +0 -1
- package/dist/messages-2jcOA_5R.js.map +0 -1
- package/dist/openai-compat-Dy56weK8.js.map +0 -1
- package/dist/output-cap-C9zukcCj.js +0 -100
- package/dist/output-cap-C9zukcCj.js.map +0 -1
- package/dist/panels-B6gRk4bf.js +0 -789
- package/dist/panels-B6gRk4bf.js.map +0 -1
- package/dist/plugins-gbI7SBA6.js.map +0 -1
- package/dist/prompts-Pqc3JdW_.d.ts.map +0 -1
- package/dist/run-summary-BU_NgwUT.js +0 -269
- package/dist/run-summary-BU_NgwUT.js.map +0 -1
- package/dist/sandbox-BH0WQkXn.d.ts.map +0 -1
- package/dist/session/sqlite.js.map +0 -1
- package/dist/session-BqRP1hS7.js.map +0 -1
- package/dist/shell-path-DfdXrGg9.js +0 -1549
- package/dist/shell-path-DfdXrGg9.js.map +0 -1
- package/dist/skills-BC3dodWp.js.map +0 -1
- package/dist/src-CkNQKFKr.js.map +0 -1
- package/dist/todo-edits-UOwgxGKu.js +0 -528
- package/dist/todo-edits-UOwgxGKu.js.map +0 -1
- package/dist/tool-formatters-DoRLnfuD.d.ts +0 -759
- package/dist/tool-formatters-DoRLnfuD.d.ts.map +0 -1
- package/dist/transcript-anchors-BeudMbXx.d.ts +0 -1762
- package/dist/transcript-anchors-BeudMbXx.d.ts.map +0 -1
- package/dist/turn-operations-C8bMBDl_.d.ts +0 -1258
- package/dist/turn-operations-C8bMBDl_.d.ts.map +0 -1
- package/dist/turn-operations-DrbJlpq9.js.map +0 -1
- package/dist/types-CWgENVLY.js.map +0 -1
- package/dist/xai-CazjStOK.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-BZhMMJUp.js","names":[],"sources":["../src/contexts/docker-exec.ts","../src/contexts/docker-files.ts","../src/contexts/docker-spawn.ts","../src/contexts/docker-state.ts","../src/contexts/docker-stream.ts","../src/contexts/docker.ts"],"sourcesContent":["/**\n * Output-capped exec transport of the Docker execution context. Internal module.\n */\n\nimport type { DockerContextState } from './docker-state'\nimport type { CappedSlot } from './output-cap'\nimport type { ExecResult, ExecutionHandle } from './types'\nimport { Buffer } from 'node:buffer'\nimport { appendCapped, outputExceededMessage } from './output-cap'\n\nexport type DockerExecCapped = (handle: ExecutionHandle, command: string, options: { cwd?: string, env?: Record<string, string>, timeout?: number } | undefined, maxBuffer: number) => Promise<ExecResult>\n\nexport function createDockerExecCapped(s: DockerContextState): DockerExecCapped {\n const { containers, defaultLimits } = s\n\n /**\n * The exec transport with an explicit output cap. The public `exec`\n * uses the shared 10MB default; `readFile` passes headroom above the\n * whole-read tool ceiling so docker's EFFECTIVE read limit matches\n * every other context (the fileSize probe refuses oversized files\n * before this ever runs — the transport cap is the safety net for\n * probe-failure paths, not the primary bound).\n */\n async function execCapped(handle: ExecutionHandle, command: string, options: { cwd?: string, env?: Record<string, string>, timeout?: number } | undefined, maxBuffer: number): Promise<ExecResult> {\n const ref = containers.get(handle.id)\n if (!ref)\n throw new Error(`Container ${handle.id} not found`)\n\n const execCwd = options?.cwd ?? handle.cwd\n const env = options?.env\n ? Object.entries(options.env).map(([k, v]) => `${k}=${v}`)\n : []\n\n const timeout = options?.timeout ?? defaultLimits?.timeout ?? 30\n\n const exec = await ref.container.exec({\n // Wrap with coreutils `timeout` so a runaway command is killed\n // server-side too — the host-side timer below only detaches the\n // stream; without this the process would keep running in the\n // container until destroy(). Exec-array form avoids re-quoting the\n // command. `timeout` exits 124 on expiry, matching the host\n // sentinel. (Default image oven/bun is Debian-based → coreutils.)\n Cmd: ['timeout', String(timeout), 'sh', '-c', command],\n WorkingDir: execCwd,\n Env: env,\n AttachStdout: true,\n AttachStderr: true,\n })\n\n const stream = await exec.start({ Detach: false })\n\n return new Promise<ExecResult>((resolve) => {\n // Byte-capped accumulation via the shared exec-transport primitive\n // (contexts/output-cap.ts) — the docker `readFile` is `cat` through\n // this very path, so an uncapped sink would buffer a container's\n // multi-GB file straight into HOST memory. On overflow the stream\n // is destroyed and the exec resolves exit 143 with a message\n // (mirroring the process context), so `readFile` THROWS instead of\n // silently returning truncated content — silent truncation would\n // corrupt `edit`'s read-modify-write into data loss.\n const stdoutSlot: CappedSlot = { text: '', bytes: 0 }\n const stderrSlot: CappedSlot = { text: '', bytes: 0 }\n let resolved = false\n\n const overflow = (): void => {\n if (resolved)\n return\n resolved = true\n try {\n stream.destroy?.()\n }\n catch { /* swallow */ }\n resolve({\n stdout: stdoutSlot.text,\n stderr: `${stderrSlot.text ? `${stderrSlot.text}\\n` : ''}${outputExceededMessage(maxBuffer, 'stream detached (server-side timeout reaps the process)')}`,\n exitCode: 143,\n })\n }\n\n const cappedWrite = (slot: CappedSlot) => (chunk: Buffer | string): boolean => {\n const buf = typeof chunk === 'string' ? Buffer.from(chunk, 'utf-8') : chunk\n if (appendCapped(slot, buf, maxBuffer) !== 'ok')\n overflow()\n return true\n }\n\n // Docker's exec stream is a multiplexed protocol — a single byte\n // stream that interleaves stdout/stderr frames. `demuxStream`\n // splits the two onto separate writable sinks. Without it, every\n // byte landed on `stdout` and `stderr` was permanently empty.\n const stdoutSink = {\n write: cappedWrite(stdoutSlot),\n end() {},\n on() {},\n once() {},\n emit() { return true },\n }\n const stderrSink = {\n write: cappedWrite(stderrSlot),\n end() {},\n on() {},\n once() {},\n emit() { return true },\n }\n try {\n ref.docker.modem.demuxStream(stream, stdoutSink, stderrSink)\n }\n catch {\n // Older / different transports may not be multiplexed (TTY mode);\n // fall back to a raw stdout reader so we don't lose output entirely.\n // Same cap: TTY mode is still a host-memory sink.\n stream.on('data', (chunk: Buffer) => {\n cappedWrite(stdoutSlot)(chunk)\n })\n }\n\n // Host-side fallback timer. The server-side `timeout` wrapper is\n // the primary kill mechanism; give it a grace second so the\n // stream-end path (which carries the real exit code) usually wins.\n const timer = setTimeout(() => {\n if (resolved)\n return\n resolved = true\n // Best-effort: detach the stream so we stop accumulating bytes.\n // The server-side `timeout` wrapper kills the process itself.\n try { stream.destroy?.() }\n catch { /* swallow */ }\n resolve({ stdout: stdoutSlot.text, stderr: stderrSlot.text ? `${stderrSlot.text}\\n[timeout]` : '[timeout]', exitCode: 124 })\n }, timeout * 1000 + 1000)\n\n stream.on('end', async () => {\n if (resolved)\n return\n resolved = true\n clearTimeout(timer)\n let exitCode: number\n let inspectNote = ''\n try {\n const inspect = await exec.inspect()\n // `ExitCode: null` means Docker hasn't recorded an exit yet —\n // the outcome is unknown, not success. Use a sentinel so\n // callers don't mistake it for a clean exit.\n if (inspect.ExitCode == null) {\n exitCode = -1\n inspectNote = '[exit code unavailable: exec still marked running by daemon]'\n }\n else {\n exitCode = inspect.ExitCode\n }\n }\n catch (err) {\n exitCode = -1\n inspectNote = `[exit code unavailable: exec inspect failed: ${(err as Error)?.message ?? String(err)}]`\n }\n resolve({\n stdout: stdoutSlot.text,\n stderr: inspectNote ? (stderrSlot.text ? `${stderrSlot.text}\\n${inspectNote}` : inspectNote) : stderrSlot.text,\n exitCode,\n })\n })\n\n stream.on('error', (err: Error) => {\n if (resolved)\n return\n resolved = true\n clearTimeout(timer)\n resolve({ stdout: stdoutSlot.text, stderr: stderrSlot.text ? `${stderrSlot.text}\\n${err.message}` : err.message, exitCode: 1 })\n })\n })\n }\n\n return execCapped\n}\n","/**\n * File methods of the Docker execution context (exec-probed reads,\n * `putArchive`-staged writes). Internal module.\n */\n\nimport type { DockerExecCapped } from './docker-exec'\nimport type { DockerContextState } from './docker-state'\nimport type { ExecutionContext, ExecutionHandle, ListFilesEntry } from './types'\nimport { Buffer } from 'node:buffer'\nimport { posix } from 'node:path'\nimport { alwaysQuote } from '../tools/shell-quote'\nimport { fileSizeViaExec } from './exec-file-size'\nimport { globFilesViaShell } from './glob'\nimport { DEFAULT_MAX_OUTPUT_BUFFER } from './output-cap'\n\n/**\n * `readFile` transport cap: headroom above the whole-read tool ceiling\n * (256MB in tools/whole-read-guard.ts) so tool policy — not the docker\n * transport — decides read limits, keeping refusal behavior consistent\n * across contexts. Still bounded: a probe-failure path can't buffer\n * more than this into host memory.\n */\nexport const READ_FILE_MAX_BUFFER = 512 * 1024 * 1024\n\nexport function dockerFileMethods(s: DockerContextState, execCapped: DockerExecCapped): Required<Pick<ExecutionContext, 'fileSize' | 'readFile' | 'writeFile' | 'listFiles'>> {\n const { containers } = s\n\n return {\n async fileSize(handle: ExecutionHandle, path: string): Promise<number | null> {\n // Shared exec-probed capability (see exec-file-size.ts) — the same\n // implementation as the sandbox contexts, for a CONSISTENT\n // whole-read experience: tools refuse >ceiling files with the same\n // message everywhere, instead of docker surfacing a transport\n // error at a different threshold.\n return fileSizeViaExec(command => execCapped(handle, command, undefined, DEFAULT_MAX_OUTPUT_BUFFER), path)\n },\n\n // Paths are model-supplied — single-quote them (`alwaysQuote`) rather\n // than JSON.stringify: `$(…)` and backticks still expand inside the\n // double quotes JSON produces, which was a command-injection vector.\n async readFile(handle: ExecutionHandle, path: string): Promise<string> {\n // Transport headroom ABOVE the whole-read tool ceiling (256MB,\n // enforced via the fileSize probe BEFORE this runs): without it,\n // docker's effective read limit would be the 10MB exec default —\n // inconsistent with every other context and surfaced as a\n // confusing transport error instead of the uniform refusal.\n const result = await execCapped(handle, `cat ${alwaysQuote(path)}`, undefined, READ_FILE_MAX_BUFFER)\n if (result.exitCode !== 0)\n throw new Error(`Failed to read file: ${result.stderr}`)\n return result.stdout\n },\n\n async writeFile(handle: ExecutionHandle, path: string, content: string | Buffer, encoding: BufferEncoding = 'utf-8'): Promise<void> {\n // ONE path for text and bytes: docker `putArchive` with an in-memory\n // ustar. A printf-through-exec text path was tried and removed: it\n // ships the whole payload inside the `sh -c` argv, which Linux caps\n // at MAX_ARG_STRLEN (128KB) — a larger text file died with\n // \"argument list too long\" (live-verified). putArchive has no size\n // ceiling, needs no shell escaping of the content, and preserves\n // image / PDF bytes that a utf-8 shell write would mangle.\n const bytes = typeof content === 'string' ? Buffer.from(content, encoding) : content\n\n const ref = containers.get(handle.id)\n if (!ref)\n throw new Error(`Container ${handle.id} not found`)\n\n // Resolve relative (model-supplied) paths against the handle cwd —\n // putArchive has no per-call WorkingDir the way exec does. Container\n // paths are POSIX whatever the HOST platform is, hence `posix.isAbsolute`\n // rather than the platform-flavored one (which calls `C:/x` absolute).\n const dest = posix.isAbsolute(path) ? path : posix.join(handle.cwd, path)\n const dir = posix.dirname(dest)\n const name = posix.basename(dest)\n // A trailing slash names a directory, and `basename` would silently\n // retarget it at the parent's last segment — refuse instead, the way\n // the shell refused `> 'dir/'`.\n if (!name || name === '.' || name === '..' || path.endsWith('/'))\n throw new Error(`Failed to write file: invalid path ${path}`)\n\n const mkdir = await s.ctx!.exec(handle, `mkdir -p ${alwaysQuote(dir)}`)\n if (mkdir.exitCode !== 0)\n throw new Error(`Failed to write file: ${mkdir.stderr}`)\n\n // putArchive extracts AS THE DAEMON, so it never touches the target\n // directly: it would land root-owned with the tar header's mode (0644,\n // silently dropping an existing `+x`), and with the API's default\n // `noOverwriteDirNonDir=false` a DIRECTORY at the target gets\n // RemoveAll'd and replaced by the file. Instead the bytes go to a\n // staging file and the SHELL performs the final write: `cat > dest`\n // truncates in place, so the target keeps its mode, its owner and its\n // symlink target, a directory target still fails with \"Is a\n // directory\", and the container user's own permissions still apply.\n // (`mv` would defeat the point — replacing the inode is exactly what\n // loses mode and owner.)\n //\n // Staged in `dir` rather than /tmp so it shares the target's\n // writability domain (readonly rootfs + writable volume mount), and\n // dot-prefixed to stay out of `ls -1p` (this context's own listFiles)\n // and of default shell glob expansion while it exists.\n const stagedName = `.zidane-write-${Math.random().toString(36).slice(2, 10)}.tmp`\n const staged = posix.join(dir, stagedName)\n\n try {\n await ref.container.putArchive(await packTarFile(stagedName, bytes), { path: dir })\n }\n catch (err) {\n throw new Error(`Failed to write file: ${err instanceof Error ? err.message : String(err)}`)\n }\n\n // `rc` is captured BEFORE the cleanup so a failed `rm` cannot mask the\n // real write outcome. That cleanup is BEST EFFORT in both directions:\n // `rm` needs write permission on `dir`, which the exec user can lack\n // even when the write itself succeeded (root-owned parent, or a bind\n // mount owned by another uid), and a server-side timeout kills the shell\n // before it runs at all. Worst case is a stray `.zidane-write-*.tmp`\n // that no exec can unlink — only the daemon could write there.\n const write = await s.ctx!.exec(handle, `cat ${alwaysQuote(staged)} > ${alwaysQuote(dest)}; rc=$?; rm -f ${alwaysQuote(staged)}; exit $rc`)\n if (write.exitCode !== 0) {\n // The shell may have died before reaching its own cleanup, so a failed\n // write retries it once in a fresh exec.\n await s.ctx!.exec(handle, `rm -f ${alwaysQuote(staged)}`).catch(() => {})\n throw new Error(`Failed to write file: ${write.stderr}`)\n }\n },\n\n async listFiles(handle: ExecutionHandle, path: string, options): Promise<ListFilesEntry[]> {\n if (options?.glob)\n return globFilesViaShell(path, command => s.ctx!.exec(handle, command), options?.limit ?? 1000)\n // `-p` suffixes directories with `/` — parity with the process\n // context's dirent-aware listing.\n const result = await s.ctx!.exec(handle, `ls -1p ${alwaysQuote(path)}`)\n // Surface `ls` stderr instead of masking failures as an empty dir —\n // the `list_files` tool differentiates \"No such file\" / \"Not a\n // directory\" / \"Permission denied\" from its catch handler.\n if (result.exitCode !== 0)\n throw new Error(result.stderr.trim() || `ls failed with exit ${result.exitCode}`)\n return result.stdout.trim().split('\\n').filter(Boolean)\n },\n }\n}\n\n/**\n * Pack a single file into an in-memory tar archive for `putArchive`, via\n * `tar-stream` (the same battle-tested packer the dockerode ecosystem uses\n * for build contexts). Optional peer dependency, loaded lazily like\n * dockerode itself so the module graph stays lean for non-docker users.\n */\nexport async function packTarFile(name: string, data: Buffer): Promise<Buffer> {\n let pack: typeof import('tar-stream')['pack']\n try {\n pack = (await import('tar-stream')).pack\n }\n catch {\n throw new Error('tar-stream is required for Docker writeFile. Install it with: bun add tar-stream')\n }\n\n const tar = pack()\n tar.entry({ name }, data)\n tar.finalize()\n\n const chunks: Buffer[] = []\n for await (const chunk of tar)\n chunks.push(chunk as Buffer)\n return Buffer.concat(chunks)\n}\n","/**\n * Container creation (`spawn`) of the Docker execution context. Internal module.\n */\n\nimport type { DockerContextState } from './docker-state'\nimport type { ExecutionContext, ExecutionHandle, SpawnConfig } from './types'\n\n/**\n * Format one {@link ContextMount} as a dockerode `HostConfig.Binds` entry.\n * Supports read-only (`:ro`) and SELinux shared-label (`:z`) options; the two\n * are mutually exclusive (a read-only mount can't also be a shared rw mount).\n */\nfunction formatBindMount(mount: NonNullable<SpawnConfig['mounts']>[number]): string {\n if (!mount.source)\n throw new Error('Docker mount source is required')\n if (!mount.target || !mount.target.startsWith('/'))\n throw new Error(`Docker mount target must be an absolute path: ${mount.target}`)\n if (mount.readonly && mount.shared)\n throw new Error(`Docker mount cannot be both readonly and shared: ${mount.target}`)\n const opts = mount.readonly ? ':ro' : mount.shared ? ':z' : ''\n return `${mount.source}:${mount.target}${opts}`\n}\n\n/** Assemble dockerode `HostConfig.Binds` from the context's `mounts`. */\nexport function buildBindSpecs(mounts: NonNullable<SpawnConfig['mounts']>): string[] {\n return mounts.map(formatBindMount)\n}\n\nexport function dockerSpawnMethods(s: DockerContextState): Required<Pick<ExecutionContext, 'spawn'>> {\n const { config, containers, defaultImage, defaultCwd, defaultEnv, defaultLimits, defaultMounts, defaultName } = s\n\n async function getDockerode() {\n try {\n const Dockerode = (await import('dockerode')).default\n return new Dockerode()\n }\n catch {\n throw new Error('dockerode is required for Docker execution context. Install it with: bun add dockerode')\n }\n }\n\n return {\n async spawn(overrides?: SpawnConfig): Promise<ExecutionHandle> {\n const docker = await getDockerode()\n const id = `docker-${++s.counter}`\n const image = overrides?.image ?? defaultImage\n const cwd = overrides?.cwd ?? defaultCwd\n const mounts = [...defaultMounts, ...(overrides?.mounts ?? [])]\n const name = overrides?.name ?? defaultName\n\n // Pull image if not available\n try {\n await docker.getImage(image).inspect()\n }\n catch {\n await new Promise<void>((resolve, reject) => {\n docker.pull(image, (err: Error | null, stream: NodeJS.ReadableStream) => {\n if (err)\n return reject(err)\n docker.modem.followProgress(stream, (err2: Error | null) => {\n err2 ? reject(err2) : resolve()\n })\n })\n })\n }\n\n const limits = { ...defaultLimits, ...overrides?.limits }\n const hostConfig: Record<string, unknown> = {}\n\n if (limits?.memory) {\n hostConfig.Memory = limits.memory * 1024 * 1024\n }\n if (limits?.cpu) {\n hostConfig.NanoCpus = Number.parseFloat(limits.cpu) * 1e9\n }\n\n // Opt-in hardening (all OFF by default — see ContextHardening).\n const hardening = { ...config?.hardening, ...overrides?.hardening }\n if (hardening.dropAllCapabilities)\n hostConfig.CapDrop = ['ALL']\n if (hardening.noNewPrivileges)\n hostConfig.SecurityOpt = ['no-new-privileges']\n if (hardening.readonlyRootfs)\n hostConfig.ReadonlyRootfs = true\n if (hardening.pidsLimit != null)\n hostConfig.PidsLimit = hardening.pidsLimit\n\n const env = { ...defaultEnv, ...overrides?.env }\n\n const bindSpecs = buildBindSpecs(mounts)\n if (bindSpecs.length > 0) {\n hostConfig.Binds = bindSpecs\n }\n\n const portSpec = overrides?.ports ?? config?.ports\n const exposedPorts: Record<string, Record<string, never>> = {}\n const portBindings: Record<string, Array<{ HostPort: string }>> = {}\n\n if (portSpec?.length) {\n for (const p of portSpec) {\n const key = `${p.container}/${p.proto ?? 'tcp'}`\n exposedPorts[key] = {}\n portBindings[key] = [{ HostPort: p.host == null ? '' : String(p.host) }]\n }\n hostConfig.PortBindings = portBindings\n }\n\n const user = overrides?.user ?? config?.user\n const network = overrides?.network ?? config?.network\n if (network) {\n hostConfig.NetworkMode = network\n }\n\n const labels = { ...config?.labels, ...overrides?.labels }\n\n const container = await docker.createContainer({\n // Suffix with a short random token so a crashed run's leftover container\n // (same prefix) never blocks a fresh one with HTTP 409 name conflict.\n ...(name ? { name: `${name}-${s.counter}-${Math.random().toString(36).slice(2, 8)}` } : {}),\n Image: image,\n Cmd: ['sleep', 'infinity'],\n WorkingDir: cwd,\n Env: Object.entries(env).map(([k, v]) => `${k}=${v}`),\n HostConfig: hostConfig,\n ...(Object.keys(exposedPorts).length ? { ExposedPorts: exposedPorts } : {}),\n ...(user ? { User: user } : {}),\n ...(Object.keys(labels).length ? { Labels: labels } : {}),\n })\n\n try {\n await container.start()\n }\n catch (err) {\n // The container was created but never started — remove it so a\n // failed spawn doesn't leak a stopped container on the daemon.\n await container.remove({ force: true }).catch(() => {})\n throw err\n }\n\n const handle: ExecutionHandle = { id, type: 'docker', cwd }\n containers.set(id, { handle, container, docker })\n\n return handle\n },\n }\n}\n","/**\n * Shared state of the Docker execution context (`createDockerContext`).\n * Internal module.\n */\n\nimport type { ExecutionContext, ExecutionHandle, SpawnConfig } from './types'\n\nexport interface ContainerRef {\n handle: ExecutionHandle\n container: any\n docker: any\n}\n\n/**\n * Per-context state shared by the docker method modules. `counter` is read\n * through this object so reads stay live; `ctx` is the composed context\n * (assigned right after construction) so `ctx.exec` dispatch stays dynamic.\n */\nexport interface DockerContextState {\n config: SpawnConfig | undefined\n counter: number\n containers: Map<string, ContainerRef>\n defaultImage: string\n defaultCwd: string\n defaultEnv: Record<string, string> | undefined\n defaultLimits: SpawnConfig['limits']\n defaultMounts: NonNullable<SpawnConfig['mounts']>\n defaultName: string | undefined\n ctx: ExecutionContext | undefined\n}\n\nexport function createDockerState(config?: SpawnConfig): DockerContextState {\n const containers = new Map<string, ContainerRef>()\n const defaultImage = config?.image ?? 'oven/bun:latest'\n const defaultCwd = config?.cwd ?? '/workspace'\n const defaultEnv = config?.env\n const defaultLimits = config?.limits\n const defaultMounts = config?.mounts ?? []\n const defaultName = config?.name\n return {\n config,\n counter: 0,\n containers,\n defaultImage,\n defaultCwd,\n defaultEnv,\n defaultLimits,\n defaultMounts,\n defaultName,\n ctx: undefined,\n }\n}\n","/**\n * Live stream (`execStream`) and interactive terminal (`openTerminal`) of\n * the Docker execution context, both via the `docker exec` CLI. Internal module.\n */\n\nimport type { DockerExecCapped } from './docker-exec'\nimport type { DockerContextState } from './docker-state'\nimport type { ExecutionContext, ExecutionHandle, StreamHandle, TerminalOptions, TerminalProcess } from './types'\nimport { alwaysQuote } from '../tools/shell-quote'\nimport { endLocalPty, hasLocalPty, localTerminalProcess, spawnLocalPty, TERMINAL_ENV } from './local-terminal'\n\nexport function dockerStreamMethods(s: DockerContextState, execCapped: DockerExecCapped): Required<Pick<ExecutionContext, 'execStream'>> {\n const { containers } = s\n\n return {\n /**\n * Live bidirectional stream to a process in the container — see\n * `ExecutionContext.execStream` for the seam contract.\n *\n * Spawns the `docker exec -i` CLI (see the runtime note in the body for\n * why dockerode's hijack API is off the table), which adds a requirement\n * the rest of this context doesn't have: the docker CLI must be on\n * PATH — everything else here talks straight to the daemon socket.\n * Deliberately NOT wrapped in coreutils `timeout` the way `execCapped`\n * is — this seam's whole purpose is processes that outlive a single\n * request.\n */\n async execStream(\n handle: ExecutionHandle,\n command: string,\n options?: { cwd?: string, env?: Record<string, string>, signal?: AbortSignal },\n ): Promise<StreamHandle> {\n const ref = containers.get(handle.id)\n if (!ref)\n throw new Error(`Container ${handle.id} not found`)\n\n options?.signal?.throwIfAborted()\n\n // Deliberately the `docker exec -i` CLI rather than dockerode, which\n // every other method here uses. Dockerode's hijacked exec (the only\n // way to get a writable stdin out of the HTTP API) rides on\n // docker-modem's HTTP 101 connection-upgrade handling, and that does\n // not work under Bun: the modem either reports the 101 as an\n // unexpected status or its start callback never fires at all. Same\n // code path is fine under Node, so this is a runtime incompatibility\n // rather than a misuse. The CLI gives real OS pipes, split streams,\n // and identical behaviour on both runtimes; it honours DOCKER_HOST\n // and friends, so remote daemons still work.\n const spawnChild = (await import('node:child_process')).spawn\n const { randomBytes } = await import('node:crypto')\n\n // `docker exec` does not propagate a signal to the in-container\n // process when the CLI dies, so killing the local child would leave\n // the server running in the container. Park the real PID in a file\n // and signal it from a second exec. A file rather than a stdout\n // marker, because stdout here is a protocol stream that must not\n // carry our bookkeeping.\n //\n // `setsid -w` puts the command in a fresh session: the inner shell\n // calls setsid() so its `$` IS the process-group id, and signalling\n // `-$pid` (see `kill` below) reaches the whole tree. Without a fresh group,\n // signalling the recorded pid alone hits only the sh that exec'd\n // into the server — wrapper launchers like `npx` fork the real\n // server as a child, which would survive the kill and pile up in a\n // long-lived container across MCP reconnects.\n //\n // The `-w` matters: bare `setsid` forks and its parent exits\n // immediately, so `docker exec` sees its process die and tears the\n // stream down (observed: instant exit 0, stdin closed). `set -m`\n // job control is no alternative either — dash turns it off without\n // a tty and reroutes the background job's stdin to /dev/null. The\n // runtime probe covers images whose setsid lacks `-w`: they fall\n // back to no process group, where the pid-only kill fallback below\n // still reaches the direct child.\n const pidFile = `/tmp/.zidane-stream-${randomBytes(6).toString('hex')}.pid`\n const envPrefix = Object.entries(options?.env ?? {})\n .map(([k, v]) => `${k}=${alwaysQuote(v)} `)\n .join('')\n const leaderScript = `echo $$ > ${pidFile}; exec ${envPrefix ? `env ${envPrefix}` : ''}sh -c ${alwaysQuote(command)}`\n const quotedLeader = alwaysQuote(leaderScript)\n const inner = `if setsid -w true 2>/dev/null; then exec setsid -w sh -c ${quotedLeader}; else exec sh -c ${quotedLeader}; fi`\n\n const child = spawnChild('docker', [\n 'exec',\n '-i',\n '-w',\n options?.cwd ?? handle.cwd,\n ref.container.id,\n 'sh',\n '-c',\n inner,\n ], { stdio: ['pipe', 'pipe', 'pipe'] })\n\n let spawnError: Error | null = null\n child.on('error', (err) => { spawnError = err })\n\n const exited = new Promise<number>((resolveExit) => {\n child.once('close', (code, signalName) => {\n resolveExit(code ?? (signalName === 'SIGTERM' ? 143 : signalName ? 128 : 1))\n })\n })\n\n // Read back the in-container PID the wrapper just recorded. The seam\n // promises a PID that is meaningful INSIDE the context, so returning\n // the local CLI's pid would be a lie.\n let pid = -1\n for (let attempt = 0; attempt < 20 && pid <= 0; attempt++) {\n const probe = await execCapped(handle, `cat ${pidFile} 2>/dev/null || true`, undefined, 4096)\n .catch(() => null)\n const parsed = Number.parseInt(probe?.stdout.trim() ?? '', 10)\n if (Number.isFinite(parsed) && parsed > 0) {\n pid = parsed\n break\n }\n if (child.exitCode !== null)\n break\n await new Promise(r => setTimeout(r, 50))\n }\n\n const kill = async (): Promise<void> => {\n if (pid > 0) {\n // Group first (`setsid -w` above makes `pid` the PGID), pid-only\n // as a fallback for images that took the no-setsid launch branch.\n // No `--` before the negative pid: dash's kill builtin rejects it\n // (\"Illegal number: -\"), while both dash and busybox ash accept\n // the bare `-pgid` after an explicit signal (verified on oven/bun\n // and alpine).\n await execCapped(handle, `kill -TERM -${pid} 2>/dev/null || kill -TERM ${pid} 2>/dev/null || true`, undefined, 4096)\n .catch(() => {})\n }\n await execCapped(handle, `rm -f ${pidFile}`, undefined, 4096).catch(() => {})\n if (child.exitCode === null && child.signalCode === null)\n child.kill('SIGTERM')\n }\n\n if (options?.signal) {\n if (options.signal.aborted) {\n void kill()\n }\n else {\n // Detach on exit — the (typically run-long) signal otherwise\n // retains the `kill` closure and the child handle for the rest\n // of the run, one dead closure per exec.\n const onAbort = () => { void kill() }\n const detach = () => options.signal!.removeEventListener('abort', onAbort)\n options.signal.addEventListener('abort', onAbort, { once: true })\n exited.then(detach, detach)\n }\n }\n\n return {\n pid,\n stdout: child.stdout!,\n stderr: child.stderr!,\n write: async (data: Uint8Array): Promise<void> => {\n if (spawnError)\n throw spawnError\n const stdin = child.stdin\n if (!stdin || stdin.destroyed)\n throw new Error(`execStream: stdin is closed (container ${handle.id})`)\n await new Promise<void>((resolveWrite, rejectWrite) => {\n stdin.write(data, err => err ? rejectWrite(err) : resolveWrite())\n })\n },\n kill,\n exited,\n }\n },\n }\n}\n\nexport function dockerTerminalMethods(s: DockerContextState, execCapped: DockerExecCapped): Pick<ExecutionContext, 'openTerminal'> {\n const { containers } = s\n\n return {\n /**\n * A shell in the container: `docker exec -it` driven by a local PTY, so\n * the CLI sees a real terminal and forwards resizes. Same CLI route as\n * `execStream` (dockerode's interactive exec doesn't work under Bun).\n * Closing the CLI doesn't end the exec'd shell, so the shell records\n * its pid (a session leader under `-t`) and `kill` hangs up its group.\n */\n ...(hasLocalPty()\n ? {\n async openTerminal(handle: ExecutionHandle, options: TerminalOptions): Promise<TerminalProcess> {\n const ref = containers.get(handle.id)\n if (!ref)\n throw new Error(`Container ${handle.id} not found`)\n const { randomBytes } = await import('node:crypto')\n const token = randomBytes(6).toString('hex')\n const pidFile = `/tmp/.zidane-tty-${token}.pid`\n const leader = `echo $$ > ${pidFile}; if command -v bash >/dev/null 2>&1; then exec bash -l; else exec sh -l; fi`\n const envArgs = Object.entries({ ...options.env, ...TERMINAL_ENV }).flatMap(([k, v]) => ['-e', `${k}=${v}`])\n const cliEnv: Record<string, string> = {}\n for (const [k, v] of Object.entries(process.env)) {\n if (typeof v === 'string')\n cliEnv[k] = v\n }\n const pty = spawnLocalPty(\n ['docker', 'exec', '-it', '-w', options.cwd ?? handle.cwd, ...envArgs, ref.container.id, 'sh', '-c', leader],\n { cwd: process.cwd(), env: cliEnv, cols: options.cols, rows: options.rows, onData: options.onData },\n )\n void pty.exited.then(() => execCapped(handle, `rm -f ${pidFile}`, undefined, 4096).catch(() => {}))\n const kill = async (): Promise<void> => {\n const probe = await execCapped(handle, `cat ${pidFile} 2>/dev/null || true`, undefined, 4096).catch(() => null)\n const pid = Number.parseInt(probe?.stdout.trim() ?? '', 10)\n if (Number.isFinite(pid) && pid > 0)\n await execCapped(handle, `kill -HUP -${pid} 2>/dev/null || kill -HUP ${pid} 2>/dev/null || true`, undefined, 4096).catch(() => {})\n await execCapped(handle, `rm -f ${pidFile}`, undefined, 4096).catch(() => {})\n await endLocalPty(pty)\n }\n return localTerminalProcess(pty, { id: `docker-${token}`, localPid: false, kill })\n },\n }\n : {}),\n }\n}\n","/**\n * Docker execution context.\n *\n * Runs tools inside a Docker container via dockerode.\n * Full isolation with configurable resource limits.\n *\n * Requires `dockerode` as an optional peer dependency.\n */\n\nimport type { ContextCapabilities, ContextMount, ExecResult, ExecutionContext, ExecutionHandle, SpawnConfig } from './types'\nimport { execFileSync } from 'node:child_process'\nimport { existsSync, mkdirSync, realpathSync, statSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { posix } from 'node:path'\nimport { createDockerExecCapped } from './docker-exec'\nimport { dockerFileMethods } from './docker-files'\nimport { dockerSpawnMethods } from './docker-spawn'\nimport { createDockerState } from './docker-state'\nimport { dockerStreamMethods, dockerTerminalMethods } from './docker-stream'\nimport { DEFAULT_MAX_OUTPUT_BUFFER } from './output-cap'\n\nexport { packTarFile, READ_FILE_MAX_BUFFER } from './docker-files'\nexport { buildBindSpecs } from './docker-spawn'\n\n/**\n * dockerode surfaces HTTP errors with a `statusCode`. 304 = container\n * already stopped, 404 = already gone — both are fine during teardown.\n */\nfunction isGoneOrAlreadyStopped(err: unknown): boolean {\n const code = (err as { statusCode?: number } | null)?.statusCode\n return code === 304 || code === 404\n}\n\nexport function createDockerContext(config?: SpawnConfig): ExecutionContext {\n const s = createDockerState(config)\n const { containers } = s\n const execCapped = createDockerExecCapped(s)\n const spawnM = dockerSpawnMethods(s)\n const streamM = dockerStreamMethods(s, execCapped)\n const files = dockerFileMethods(s, execCapped)\n\n const ctx: ExecutionContext = {\n type: 'docker',\n\n capabilities: {\n shell: true,\n filesystem: true,\n glob: true,\n network: true,\n gpu: false,\n stream: true,\n } satisfies ContextCapabilities,\n\n spawn: spawnM.spawn,\n\n async exec(handle: ExecutionHandle, command: string, options?: { cwd?: string, env?: Record<string, string>, timeout?: number }): Promise<ExecResult> {\n return execCapped(handle, command, options, DEFAULT_MAX_OUTPUT_BUFFER)\n },\n\n execStream: streamM.execStream,\n\n ...dockerTerminalMethods(s, execCapped),\n\n fileSize: files.fileSize,\n\n readFile: files.readFile,\n\n writeFile: files.writeFile,\n\n listFiles: files.listFiles,\n\n async getMappedPort(handle: ExecutionHandle, containerPort: number): Promise<number | null> {\n const ref = containers.get(handle.id)\n if (!ref)\n return null\n\n const info = await ref.container.inspect()\n const bindings = info?.NetworkSettings?.Ports ?? {}\n\n for (const proto of ['tcp', 'udp']) {\n const list = bindings[`${containerPort}/${proto}`]\n if (Array.isArray(list) && list.length > 0 && list[0]?.HostPort) {\n return Number.parseInt(list[0].HostPort, 10)\n }\n }\n\n return null\n },\n\n async destroy(handle: ExecutionHandle): Promise<void> {\n const ref = containers.get(handle.id)\n if (!ref)\n return\n\n try {\n try {\n await ref.container.stop({ t: 5 })\n }\n catch {\n // 304 (already stopped) / 404 (already gone) are expected here.\n // Any other stop failure must not prevent the remove({force})\n // below — force removal also works on running containers.\n }\n try {\n await ref.container.remove({ force: true })\n }\n catch (err) {\n if (!isGoneOrAlreadyStopped(err))\n throw err\n }\n }\n finally {\n containers.delete(handle.id)\n }\n },\n }\n s.ctx = ctx\n\n return ctx\n}\n\n/**\n * Path-equal bind mounts that make a HOST working directory usable inside a\n * container: the enclosing git worktree (or `cwd` itself outside git) at the\n * same absolute path, plus the git common dir when it lives outside that\n * tree (a linked worktree's `.git` file points there — without it, git fails\n * in the container). Path-equal keeps host-side features (diffs, artifacts,\n * `@`-paths) valid for the same absolute paths the agent sees.\n *\n * The git dir's `hooks/` and `config` (plus `config.worktree` when present or\n * enabled) are overlaid read-only — created empty first if absent: host-side git\n * (zidane's own diff/workspace calls included) would otherwise run hooks or\n * `core.fsmonitor` / `core.hooksPath` planted from inside the container.\n * Deliberate cost: git commands that WRITE config fail inside the container —\n * `git push -u` / `--set-upstream`, `git branch --set-upstream-to`,\n * `git remote add`, `git config …`. Commits, fetches and a plain\n * `git push <remote> <branch>` work; environments that publish a branch must\n * push without recording an upstream.\n *\n * Returns `[]` when `cwd` is not an existing host directory, so a\n * container-only path (`--cwd /workspace`) keeps its historical meaning, and\n * when the tree to mount is `/`, `$HOME`, or an ancestor of `$HOME` (too broad\n * to hand a container read-write). A git root that is too broad falls back to\n * `cwd` alone (no git dir), so a dotfiles repo at `$HOME` doesn't blank every\n * project under it.\n */\nexport function hostWorkspaceMounts(cwd: string): ContextMount[] {\n if (!posix.isAbsolute(cwd) || !isHostDirectory(cwd))\n return []\n const root = gitProbe(cwd, ['rev-parse', '--show-toplevel']) ?? cwd\n const rootMount = workspaceRootMount(cwd, root)\n if (isTooBroadToMount(rootMount.source)) {\n // A repo rooted at `$HOME` (dotfiles) or `/` must not take a project\n // under it down with it: mount `cwd` alone, without the git dir.\n const cwdMount = { source: safeRealpath(cwd), target: cwd }\n return isTooBroadToMount(cwdMount.source) ? [] : [cwdMount]\n }\n const mounts: ContextMount[] = [rootMount]\n const commonDir = gitProbe(cwd, ['rev-parse', '--git-common-dir'])\n if (commonDir) {\n const realCommon = safeRealpath(commonDir)\n let commonTarget: string\n if (isWithin(realCommon, rootMount.source)) {\n commonTarget = posix.join(rootMount.target, posix.relative(rootMount.source, realCommon))\n }\n else {\n mounts.push({ source: commonDir, target: commonDir })\n commonTarget = commonDir\n }\n const overlay = (source: string): void => {\n if (isWithin(source, realCommon))\n mounts.push({ source, target: posix.join(commonTarget, posix.relative(realCommon, source)), readonly: true })\n }\n /*\n * Overlay even when absent (`git init --template=` leaves no `hooks/`):\n * a read-write parent would let the container create it. Materialize an\n * empty one on the host first — a missing bind source would otherwise be\n * created by dockerd as a root-owned dir.\n */\n if (ensureHostPath(posix.join(realCommon, 'hooks'), 'dir'))\n overlay(posix.join(realCommon, 'hooks'))\n if (ensureHostPath(posix.join(realCommon, 'config'), 'file'))\n overlay(posix.join(realCommon, 'config'))\n // Per-worktree config (`extensions.worktreeConfig`) is read by host git too.\n const worktreeConfig = gitProbe(cwd, ['rev-parse', '--git-path', 'config.worktree'])\n if (worktreeConfig) {\n const real = posix.join(safeRealpath(posix.dirname(worktreeConfig)), posix.basename(worktreeConfig))\n if (existsSync(real) || (worktreeConfigEnabled(cwd) && ensureHostPath(real, 'file')))\n overlay(real)\n }\n }\n return mounts\n}\n\n/** Create an empty dir / file at `path` if absent. `false` when it can't exist. */\nfunction ensureHostPath(path: string, kind: 'dir' | 'file'): boolean {\n try {\n if (kind === 'dir')\n mkdirSync(path, { recursive: true })\n else if (!existsSync(path))\n writeFileSync(path, '', { flag: 'a' })\n return true\n }\n catch {\n return existsSync(path)\n }\n}\n\nfunction worktreeConfigEnabled(cwd: string): boolean {\n try {\n return execFileSync('git', ['config', '--bool', '--get', 'extensions.worktreeConfig'], { cwd, timeout: 1_000, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() === 'true'\n }\n catch {\n return false\n }\n}\n\nfunction isTooBroadToMount(source: string): boolean {\n const real = safeRealpath(source)\n return real === '/' || isWithin(safeRealpath(homedir()), real)\n}\n\nfunction safeRealpath(path: string): string {\n try {\n return realpathSync(path)\n }\n catch {\n return path\n }\n}\n\n/*\n * git reports the toplevel as a realpath, but the container's working dir is\n * the unresolved `cwd` (macOS: /tmp -> /private/tmp). Re-express the root\n * through `cwd`'s own prefix so the mount contains it; when a symlink inside\n * the tree makes that impossible, mount `cwd` itself.\n */\nfunction workspaceRootMount(cwd: string, root: string): ContextMount {\n try {\n const realCwd = realpathSync(cwd)\n if (root === cwd || realCwd === cwd)\n return { source: root, target: root }\n const candidate = posix.resolve(cwd, posix.relative(realCwd, root))\n if (isWithin(cwd, candidate) && realpathSync(candidate) === realpathSync(root))\n return { source: root, target: candidate }\n return { source: realCwd, target: cwd }\n }\n catch {\n return { source: root, target: root }\n }\n}\n\nfunction isHostDirectory(path: string): boolean {\n try {\n return statSync(path).isDirectory()\n }\n catch {\n return false\n }\n}\n\nfunction isWithin(child: string, parent: string): boolean {\n const rel = posix.relative(parent, child)\n return rel === '' || (!rel.startsWith('..') && !posix.isAbsolute(rel))\n}\n\nfunction gitProbe(cwd: string, args: string[]): string | null {\n try {\n const out = execFileSync('git', args, { cwd, timeout: 1_000, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()\n return out ? posix.resolve(cwd, out) : null\n }\n catch {\n return null\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import { t as alwaysQuote } from "./shell-quote-BmnhZmdM.js";
|
|
2
|
+
import { r as createLogger, t as consoleSink } from "./logger-Gtqlayx8.js";
|
|
3
|
+
import { t as TERMINAL_ENV, u as capBufferedExecOutput } from "./local-terminal-D8QThwo_.js";
|
|
4
|
+
import { t as StreamQueue } from "./stream-queue-Cd9OJxl7.js";
|
|
5
|
+
import { Buffer } from "node:buffer";
|
|
6
|
+
//#region src/contexts/e2b-sdk.ts
|
|
7
|
+
/**
|
|
8
|
+
* "Effectively unlimited" command timeout for `execStream`. 2^31-1 ms
|
|
9
|
+
* (~24.8 days — far beyond any sandbox lifetime) because both edges are
|
|
10
|
+
* cliffs: `0` is treated by `@connectrpc/connect`'s deadline signal as
|
|
11
|
+
* already expired (instant DeadlineExceeded), and anything above 2^31-1
|
|
12
|
+
* overflows `setTimeout` and fires immediately.
|
|
13
|
+
*/
|
|
14
|
+
const E2B_STREAM_TIMEOUT_MS = 2 ** 31 - 1;
|
|
15
|
+
/**
|
|
16
|
+
* Merge the factory-level {@link E2BProviderOptions.env} with the per-spawn
|
|
17
|
+
* `SpawnConfig.env`, per-spawn winning on key conflicts. Returns `undefined`
|
|
18
|
+
* when the merge is empty so callers can skip setting `envs` on the create
|
|
19
|
+
* options entirely (rather than handing the SDK an empty object).
|
|
20
|
+
*/
|
|
21
|
+
function resolveE2BEnv(optionEnv, perSpawnEnv) {
|
|
22
|
+
const merged = {
|
|
23
|
+
...optionEnv,
|
|
24
|
+
...perSpawnEnv
|
|
25
|
+
};
|
|
26
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/contexts/e2b-stream.ts
|
|
30
|
+
function e2bStreamMethods(get, logger) {
|
|
31
|
+
return {
|
|
32
|
+
/**
|
|
33
|
+
* Live bidirectional stream — see `ExecutionContext.execStream`.
|
|
34
|
+
*
|
|
35
|
+
* Backed by E2B's pipe-based command API rather than `sandbox.pty`.
|
|
36
|
+
* Both can carry stdin, but a PTY would force us to defeat terminal
|
|
37
|
+
* semantics we never wanted (input echo, `\n`→`\r\n` rewriting, the
|
|
38
|
+
* ~4096-byte canonical line cap, and stdout/stderr merged into one
|
|
39
|
+
* stream). Pipes give split streams and byte-faithful framing for free.
|
|
40
|
+
*
|
|
41
|
+
* KNOWN RESIDUAL: `onStdout` / `onStderr` deliver `string`, already
|
|
42
|
+
* decoded by the SDK. A chunk boundary that splits a multibyte
|
|
43
|
+
* codepoint is therefore corrupted before it reaches us — we re-encode
|
|
44
|
+
* to bytes to satisfy the seam, but cannot recover what the decoder
|
|
45
|
+
* dropped. Newline framing and ASCII structure are unaffected, so
|
|
46
|
+
* JSON-RPC still parses; the damage would be confined to non-ASCII
|
|
47
|
+
* inside string values. If that ever bites, `sandbox.pty.create`
|
|
48
|
+
* hands back raw `Uint8Array` and the Daytona PTY path in
|
|
49
|
+
* `./daytona.ts` is the template for adopting it here.
|
|
50
|
+
*/
|
|
51
|
+
async execStream(sandboxId, command, opts) {
|
|
52
|
+
const { sandbox, env } = get(sandboxId);
|
|
53
|
+
opts?.signal?.throwIfAborted();
|
|
54
|
+
const warnBacklog = (which) => (bytes) => logger.warn("execStream consumer is not draining output", {
|
|
55
|
+
sandboxId,
|
|
56
|
+
stream: which,
|
|
57
|
+
bufferedBytes: bytes
|
|
58
|
+
});
|
|
59
|
+
const stdout = new StreamQueue(warnBacklog("stdout"));
|
|
60
|
+
const stderr = new StreamQueue(warnBacklog("stderr"));
|
|
61
|
+
const pidFile = `/tmp/.zidane-stream-${Math.random().toString(36).slice(2, 10)}.pid`;
|
|
62
|
+
const leader = `echo \$\$ > ${pidFile}; exec sh -c ${alwaysQuote(command)}`;
|
|
63
|
+
const wrapped = `if setsid -w true 2>/dev/null; then exec setsid -w sh -c ${alwaysQuote(leader)}; else exec sh -c ${alwaysQuote(leader)}; fi`;
|
|
64
|
+
const handle = await sandbox.commands.run(wrapped, {
|
|
65
|
+
background: true,
|
|
66
|
+
stdin: true,
|
|
67
|
+
timeoutMs: E2B_STREAM_TIMEOUT_MS,
|
|
68
|
+
...opts?.cwd ? { cwd: opts.cwd } : {},
|
|
69
|
+
envs: resolveE2BEnv(env, opts?.env),
|
|
70
|
+
onStdout: (data) => stdout.push(new TextEncoder().encode(data)),
|
|
71
|
+
onStderr: (data) => stderr.push(new TextEncoder().encode(data))
|
|
72
|
+
});
|
|
73
|
+
const pid = handle.pid;
|
|
74
|
+
const exited = handle.wait().then((result) => result?.exitCode ?? 0).catch((err) => typeof err?.exitCode === "number" ? err.exitCode : 137).finally(() => {
|
|
75
|
+
stdout.close();
|
|
76
|
+
stderr.close();
|
|
77
|
+
});
|
|
78
|
+
let commandExited = false;
|
|
79
|
+
exited.then(() => {
|
|
80
|
+
commandExited = true;
|
|
81
|
+
});
|
|
82
|
+
const readLeaderPid = async () => {
|
|
83
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
84
|
+
const probe = await sandbox.commands.run(`cat ${pidFile} 2>/dev/null || true`).catch(() => null);
|
|
85
|
+
const parsed = Number.parseInt(probe?.stdout?.trim() ?? "", 10);
|
|
86
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
87
|
+
if (commandExited) break;
|
|
88
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
89
|
+
}
|
|
90
|
+
return -1;
|
|
91
|
+
};
|
|
92
|
+
const leaderPid = await readLeaderPid();
|
|
93
|
+
const kill = async () => {
|
|
94
|
+
if (leaderPid > 0) await sandbox.commands.run(`kill -TERM -${leaderPid} 2>/dev/null || kill -TERM ${leaderPid} 2>/dev/null || true`).catch(() => {});
|
|
95
|
+
await sandbox.commands.run(`rm -f ${pidFile}`).catch(() => {});
|
|
96
|
+
await sandbox.commands.kill?.(pid).catch(() => {});
|
|
97
|
+
};
|
|
98
|
+
if (opts?.signal) {
|
|
99
|
+
if (opts.signal.aborted) kill();
|
|
100
|
+
else {
|
|
101
|
+
const onAbort = () => {
|
|
102
|
+
kill();
|
|
103
|
+
};
|
|
104
|
+
const detach = () => opts.signal.removeEventListener("abort", onAbort);
|
|
105
|
+
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
106
|
+
exited.then(detach, detach);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
pid,
|
|
111
|
+
stdout,
|
|
112
|
+
stderr,
|
|
113
|
+
write: async (data) => {
|
|
114
|
+
if (!sandbox.commands.sendStdin) throw new Error("E2B SDK is too old: `commands.sendStdin` is required for execStream");
|
|
115
|
+
await sandbox.commands.sendStdin(pid, data);
|
|
116
|
+
},
|
|
117
|
+
kill,
|
|
118
|
+
exited
|
|
119
|
+
};
|
|
120
|
+
} };
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A `TerminalProcess` over an E2B PTY handle. Input is chained so keystrokes
|
|
124
|
+
* sent as separate RPCs can't overtake each other.
|
|
125
|
+
*/
|
|
126
|
+
function e2bTerminal(sandbox, handle) {
|
|
127
|
+
const pty = sandbox.pty;
|
|
128
|
+
const pid = handle.pid;
|
|
129
|
+
const encoder = new TextEncoder();
|
|
130
|
+
let input = Promise.resolve();
|
|
131
|
+
let killed = null;
|
|
132
|
+
return {
|
|
133
|
+
id: String(pid),
|
|
134
|
+
reconnectable: true,
|
|
135
|
+
write: (data) => {
|
|
136
|
+
const bytes = typeof data === "string" ? encoder.encode(data) : data;
|
|
137
|
+
input = input.then(() => pty.sendInput(pid, bytes)).catch(() => {});
|
|
138
|
+
},
|
|
139
|
+
resize: (cols, rows) => {
|
|
140
|
+
pty.resize(pid, {
|
|
141
|
+
cols,
|
|
142
|
+
rows
|
|
143
|
+
}).catch(() => {});
|
|
144
|
+
},
|
|
145
|
+
kill: () => killed ??= pty.kill(pid).then(() => {}, () => {}),
|
|
146
|
+
detach: () => handle.disconnect().catch(() => {}),
|
|
147
|
+
exited: handle.wait().then((result) => result?.exitCode ?? 0, (err) => typeof err?.exitCode === "number" ? err.exitCode : -1)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function e2bTerminalMethods(get) {
|
|
151
|
+
return {
|
|
152
|
+
/**
|
|
153
|
+
* A login shell in the sandbox through `sandbox.pty`. The PTY is a
|
|
154
|
+
* sandbox process, not a connection: it outlives the host and
|
|
155
|
+
* `reconnectTerminal` finds it again by pid. `timeoutMs` is maxed for
|
|
156
|
+
* the same reason as `execStream`'s (the 60 s default would cut the
|
|
157
|
+
* shell; `0` would expire it at once).
|
|
158
|
+
*/
|
|
159
|
+
async openTerminal(sandboxId, options) {
|
|
160
|
+
const { sandbox, env } = get(sandboxId);
|
|
161
|
+
if (!sandbox.pty) throw new Error("E2B SDK is too old: `sandbox.pty` is required for terminals.");
|
|
162
|
+
return e2bTerminal(sandbox, await sandbox.pty.create({
|
|
163
|
+
cols: options.cols,
|
|
164
|
+
rows: options.rows,
|
|
165
|
+
...options.cwd ? { cwd: options.cwd } : {},
|
|
166
|
+
envs: resolveE2BEnv(env, {
|
|
167
|
+
...options.env,
|
|
168
|
+
...TERMINAL_ENV
|
|
169
|
+
}),
|
|
170
|
+
timeoutMs: E2B_STREAM_TIMEOUT_MS,
|
|
171
|
+
onData: options.onData
|
|
172
|
+
}));
|
|
173
|
+
},
|
|
174
|
+
async reconnectTerminal(sandboxId, id, options) {
|
|
175
|
+
const { sandbox } = get(sandboxId);
|
|
176
|
+
const pid = Number.parseInt(id, 10);
|
|
177
|
+
if (!sandbox.pty || !Number.isFinite(pid)) return null;
|
|
178
|
+
const handle = await sandbox.pty.connect(pid, {
|
|
179
|
+
onData: options.onData,
|
|
180
|
+
timeoutMs: E2B_STREAM_TIMEOUT_MS
|
|
181
|
+
}).catch(() => null);
|
|
182
|
+
if (!handle) return null;
|
|
183
|
+
const terminal = e2bTerminal(sandbox, handle);
|
|
184
|
+
terminal.resize(options.cols, options.rows);
|
|
185
|
+
return terminal;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/contexts/e2b.ts
|
|
191
|
+
/** Minimum gap between two keep-alive `setTimeout` calls on one sandbox. */
|
|
192
|
+
const KEEP_ALIVE_THROTTLE_MS = 6e4;
|
|
193
|
+
/** Per-probe timeout for the readiness `true` command. */
|
|
194
|
+
const READINESS_PROBE_TIMEOUT_MS = 5e3;
|
|
195
|
+
/** Pause between readiness probes while the sandbox is still warming up. */
|
|
196
|
+
const READINESS_BACKOFF_MS = 250;
|
|
197
|
+
/**
|
|
198
|
+
* Command timeout for the pregame setup script. E2B's `commands.run` defaults
|
|
199
|
+
* to 60s and offers no true "unlimited" — a `timeoutMs` of 0 is a 0ms deadline
|
|
200
|
+
* (instant timeout), not "off". Setup scripts (`apt`/`pip` installs, builds)
|
|
201
|
+
* routinely exceed 60s, so we pass a deliberately generous 30-minute ceiling.
|
|
202
|
+
* The sandbox's own lifetime independently bounds anything longer.
|
|
203
|
+
*/
|
|
204
|
+
const PREGAME_TIMEOUT_MS = 18e5;
|
|
205
|
+
/**
|
|
206
|
+
* Upload a setup ("pregame") script into a ready sandbox and execute it.
|
|
207
|
+
*
|
|
208
|
+
* Writes the script to `/tmp/<name>`, then `chmod +x`'s and runs it directly
|
|
209
|
+
* (so a `#!/bin/bash` / `#!/usr/bin/env python3` shebang chooses the
|
|
210
|
+
* interpreter) in `cwd` with `envs` applied — the same env the sandbox was
|
|
211
|
+
* created with, so the script sees the run's `--env` / `--pass-env`. A
|
|
212
|
+
* generous {@link PREGAME_TIMEOUT_MS} ceiling is used so long `apt`/`pip`
|
|
213
|
+
* installs aren't killed by E2B's 60s command default. A non-zero exit
|
|
214
|
+
* (whether E2B returns it or raises on it) throws, so the caller can tear the
|
|
215
|
+
* sandbox down rather than prompt against a broken environment.
|
|
216
|
+
*/
|
|
217
|
+
async function runE2BPregame(sandbox, logger, opts) {
|
|
218
|
+
const path = `/tmp/${opts.name}`;
|
|
219
|
+
logger.info("running E2B pregame script", {
|
|
220
|
+
sandboxId: sandbox.sandboxId,
|
|
221
|
+
path
|
|
222
|
+
});
|
|
223
|
+
await sandbox.files.write(path, opts.content);
|
|
224
|
+
const quoted = JSON.stringify(path);
|
|
225
|
+
let result;
|
|
226
|
+
try {
|
|
227
|
+
result = await sandbox.commands.run(`chmod +x ${quoted} && ${quoted}`, {
|
|
228
|
+
cwd: opts.cwd,
|
|
229
|
+
envs: opts.envs,
|
|
230
|
+
timeoutMs: PREGAME_TIMEOUT_MS
|
|
231
|
+
});
|
|
232
|
+
} catch (err) {
|
|
233
|
+
const exitCode = typeof err?.exitCode === "number" ? err.exitCode : 124;
|
|
234
|
+
logger.error("E2B pregame script failed", {
|
|
235
|
+
sandboxId: sandbox.sandboxId,
|
|
236
|
+
exitCode,
|
|
237
|
+
stdout: err?.stdout ?? "",
|
|
238
|
+
stderr: err?.stderr ?? err?.message ?? ""
|
|
239
|
+
});
|
|
240
|
+
throw new Error(`E2B pregame script ${opts.name} failed with exit code ${exitCode}`);
|
|
241
|
+
}
|
|
242
|
+
if (result.exitCode !== 0) {
|
|
243
|
+
logger.error("E2B pregame script failed", {
|
|
244
|
+
sandboxId: sandbox.sandboxId,
|
|
245
|
+
exitCode: result.exitCode,
|
|
246
|
+
stdout: result.stdout,
|
|
247
|
+
stderr: result.stderr
|
|
248
|
+
});
|
|
249
|
+
throw new Error(`E2B pregame script ${opts.name} failed with exit code ${result.exitCode}`);
|
|
250
|
+
}
|
|
251
|
+
logger.info("E2B pregame ready", {
|
|
252
|
+
sandboxId: sandbox.sandboxId,
|
|
253
|
+
stdout: result.stdout,
|
|
254
|
+
stderr: result.stderr
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Block until an E2B sandbox is ready to run commands, or throw if it never
|
|
259
|
+
* becomes ready within the deadline.
|
|
260
|
+
*
|
|
261
|
+
* The E2B control plane can hand back a sandbox id before the sandbox's `envd`
|
|
262
|
+
* is accepting commands; the first real command then absorbs that cold start
|
|
263
|
+
* and may time out. This polls a trivial `true` command on a short backoff,
|
|
264
|
+
* logging a `waiting…` line up front and a `ready` line (with `elapsedMs`) on
|
|
265
|
+
* success. On deadline exhaustion it best-effort kills the sandbox and throws.
|
|
266
|
+
*
|
|
267
|
+
* `now`/`sleep` are injectable purely for deterministic tests; in production
|
|
268
|
+
* they default to `Date.now` and a real timer.
|
|
269
|
+
*/
|
|
270
|
+
async function waitForE2BReady(sandbox, logger, opts) {
|
|
271
|
+
if (opts.timeoutSeconds <= 0) return;
|
|
272
|
+
const now = opts.now ?? Date.now;
|
|
273
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
274
|
+
const start = now();
|
|
275
|
+
const deadline = start + opts.timeoutSeconds * 1e3;
|
|
276
|
+
logger.info("waiting for E2B sandbox to initialize", { sandboxId: sandbox.sandboxId });
|
|
277
|
+
while (true) try {
|
|
278
|
+
await sandbox.commands.run("true", { timeoutMs: READINESS_PROBE_TIMEOUT_MS });
|
|
279
|
+
logger.info("E2B sandbox ready", {
|
|
280
|
+
sandboxId: sandbox.sandboxId,
|
|
281
|
+
elapsedMs: now() - start
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
284
|
+
} catch {
|
|
285
|
+
if (now() >= deadline) {
|
|
286
|
+
if (opts.killOnTimeout !== false) await sandbox.kill().catch(() => {});
|
|
287
|
+
throw new Error(`E2B sandbox ${sandbox.sandboxId} did not become ready within ${opts.timeoutSeconds}s`);
|
|
288
|
+
}
|
|
289
|
+
await sleep(READINESS_BACKOFF_MS);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Resolve the effective E2B template, highest precedence first: the per-spawn
|
|
294
|
+
* `SpawnConfig.sandbox.template`, then the factory `template` option, then the
|
|
295
|
+
* `E2B_TEMPLATE` env var. Returns `undefined` (→ SDK's `base` default) when
|
|
296
|
+
* none is set. The E2B SDK reads `E2B_API_KEY`/`E2B_DOMAIN` from the env but
|
|
297
|
+
* NOT `E2B_TEMPLATE`, so this provider has to honor it itself.
|
|
298
|
+
*/
|
|
299
|
+
function resolveE2BTemplate(perSpawnTemplate, optionTemplate, env = process.env) {
|
|
300
|
+
return perSpawnTemplate || optionTemplate || env.E2B_TEMPLATE || void 0;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Build a {@link SandboxProvider} backed by E2B.
|
|
304
|
+
*
|
|
305
|
+
* The provider owns a registry of live {@link E2BSandbox} instances keyed by
|
|
306
|
+
* sandbox id, so the per-call `exec`/`readFile`/… hooks reuse the same warm
|
|
307
|
+
* connection rather than reconnecting each time.
|
|
308
|
+
*/
|
|
309
|
+
function createE2BProvider(options = {}) {
|
|
310
|
+
const live = /* @__PURE__ */ new Map();
|
|
311
|
+
const logger = options.logger ?? createLogger(consoleSink());
|
|
312
|
+
const readinessTimeoutSeconds = options.readinessTimeoutSeconds ?? 60;
|
|
313
|
+
async function loadSdk() {
|
|
314
|
+
try {
|
|
315
|
+
return (await import("e2b")).Sandbox;
|
|
316
|
+
} catch {
|
|
317
|
+
throw new Error("e2b is required for the E2B sandbox provider. Install it with: bun add e2b");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function get(sandboxId) {
|
|
321
|
+
const entry = live.get(sandboxId);
|
|
322
|
+
if (!entry) throw new Error(`E2B sandbox ${sandboxId} is not tracked by this provider`);
|
|
323
|
+
keepAlive(entry);
|
|
324
|
+
return entry;
|
|
325
|
+
}
|
|
326
|
+
function keepAlive(entry) {
|
|
327
|
+
const seconds = options.keepAliveSeconds;
|
|
328
|
+
if (!seconds || seconds <= 0 || !entry.sandbox.setTimeout) return;
|
|
329
|
+
const now = Date.now();
|
|
330
|
+
if (entry.keptAliveAt !== void 0 && now - entry.keptAliveAt < KEEP_ALIVE_THROTTLE_MS) return;
|
|
331
|
+
entry.keptAliveAt = now;
|
|
332
|
+
entry.sandbox.setTimeout(seconds * 1e3).catch((err) => {
|
|
333
|
+
logger.warn("failed to extend E2B sandbox timeout", {
|
|
334
|
+
sandboxId: entry.sandbox.sandboxId,
|
|
335
|
+
error: err instanceof Error ? err.message : String(err)
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
async function killOwnedSandbox(sandbox) {
|
|
340
|
+
try {
|
|
341
|
+
await sandbox.kill();
|
|
342
|
+
} catch {}
|
|
343
|
+
}
|
|
344
|
+
async function pauseOwnedSandbox(sandbox) {
|
|
345
|
+
try {
|
|
346
|
+
if (sandbox.pause) await sandbox.pause();
|
|
347
|
+
else if (sandbox.betaPause) await sandbox.betaPause();
|
|
348
|
+
else {
|
|
349
|
+
logger.error("cannot pause E2B sandbox: this e2b SDK exposes neither pause() nor betaPause() (expected e2b@^2); leaving it to time out", { sandboxId: sandbox.sandboxId });
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
logger.info("paused E2B sandbox; reconnect later via the sandboxId option (CLI: --sandbox)", { sandboxId: sandbox.sandboxId });
|
|
353
|
+
} catch (err) {
|
|
354
|
+
logger.error("failed to pause E2B sandbox; it will run until its timeout", {
|
|
355
|
+
sandboxId: sandbox.sandboxId,
|
|
356
|
+
error: err instanceof Error ? err.message : String(err)
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const streamM = e2bStreamMethods(get, logger);
|
|
361
|
+
const termM = e2bTerminalMethods(get);
|
|
362
|
+
return {
|
|
363
|
+
name: "e2b",
|
|
364
|
+
async spawn(config) {
|
|
365
|
+
const Sandbox = await loadSdk();
|
|
366
|
+
const sb = config.sandbox ?? {};
|
|
367
|
+
const apiKey = sb.apiKey ?? options.apiKey;
|
|
368
|
+
const domain = sb.domain ?? options.domain;
|
|
369
|
+
const template = resolveE2BTemplate(sb.template, options.template);
|
|
370
|
+
const timeoutSeconds = config.limits?.timeout ?? options.timeoutSeconds;
|
|
371
|
+
const connectId = sb.sandboxId ?? options.sandboxId;
|
|
372
|
+
const envs = resolveE2BEnv(options.env, config.env);
|
|
373
|
+
let sandbox;
|
|
374
|
+
const owned = connectId === void 0;
|
|
375
|
+
if (connectId !== void 0) {
|
|
376
|
+
const connectOpts = {};
|
|
377
|
+
if (apiKey !== void 0) connectOpts.apiKey = apiKey;
|
|
378
|
+
if (domain !== void 0) connectOpts.domain = domain;
|
|
379
|
+
if (timeoutSeconds !== void 0) connectOpts.timeoutMs = timeoutSeconds * 1e3;
|
|
380
|
+
sandbox = await Sandbox.connect(connectId, connectOpts);
|
|
381
|
+
} else {
|
|
382
|
+
const createOpts = {};
|
|
383
|
+
if (apiKey !== void 0) createOpts.apiKey = apiKey;
|
|
384
|
+
if (domain !== void 0) createOpts.domain = domain;
|
|
385
|
+
if (envs) createOpts.envs = envs;
|
|
386
|
+
if (timeoutSeconds !== void 0) createOpts.timeoutMs = timeoutSeconds * 1e3;
|
|
387
|
+
if (options.lifecycle) createOpts.lifecycle = options.lifecycle;
|
|
388
|
+
sandbox = template !== void 0 ? await Sandbox.create(template, createOpts) : await Sandbox.create(createOpts);
|
|
389
|
+
}
|
|
390
|
+
live.set(sandbox.sandboxId, {
|
|
391
|
+
sandbox,
|
|
392
|
+
env: envs,
|
|
393
|
+
owned
|
|
394
|
+
});
|
|
395
|
+
try {
|
|
396
|
+
await waitForE2BReady(sandbox, logger, {
|
|
397
|
+
timeoutSeconds: readinessTimeoutSeconds,
|
|
398
|
+
killOnTimeout: owned
|
|
399
|
+
});
|
|
400
|
+
} catch (err) {
|
|
401
|
+
live.delete(sandbox.sandboxId);
|
|
402
|
+
throw err;
|
|
403
|
+
}
|
|
404
|
+
const requestedCwd = config.cwd ?? options.cwd;
|
|
405
|
+
let cwd;
|
|
406
|
+
if (requestedCwd) {
|
|
407
|
+
cwd = requestedCwd;
|
|
408
|
+
await sandbox.commands.run(`mkdir -p ${JSON.stringify(cwd)}`).catch(() => {});
|
|
409
|
+
} else cwd = await sandbox.commands.run("pwd").then((r) => r.stdout.trim()).catch(() => "") || "/home/user";
|
|
410
|
+
if (options.pregame) try {
|
|
411
|
+
await runE2BPregame(sandbox, logger, {
|
|
412
|
+
name: options.pregame.name,
|
|
413
|
+
content: options.pregame.content,
|
|
414
|
+
cwd,
|
|
415
|
+
envs
|
|
416
|
+
});
|
|
417
|
+
} catch (err) {
|
|
418
|
+
if (owned) await sandbox.kill().catch(() => {});
|
|
419
|
+
live.delete(sandbox.sandboxId);
|
|
420
|
+
throw err;
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
id: sandbox.sandboxId,
|
|
424
|
+
cwd
|
|
425
|
+
};
|
|
426
|
+
},
|
|
427
|
+
execStream: streamM.execStream,
|
|
428
|
+
openTerminal: termM.openTerminal,
|
|
429
|
+
reconnectTerminal: termM.reconnectTerminal,
|
|
430
|
+
async exec(sandboxId, command, opts) {
|
|
431
|
+
const { sandbox, env } = get(sandboxId);
|
|
432
|
+
const timeoutMs = (opts?.timeout ?? 30) * 1e3;
|
|
433
|
+
try {
|
|
434
|
+
const result = await sandbox.commands.run(command, {
|
|
435
|
+
cwd: opts?.cwd,
|
|
436
|
+
envs: resolveE2BEnv(env, opts?.env),
|
|
437
|
+
timeoutMs: timeoutMs > 0 ? timeoutMs : void 0
|
|
438
|
+
});
|
|
439
|
+
return {
|
|
440
|
+
...capBufferedExecOutput(result.stdout, result.stderr),
|
|
441
|
+
exitCode: result.exitCode
|
|
442
|
+
};
|
|
443
|
+
} catch (err) {
|
|
444
|
+
if (typeof err?.exitCode === "number") return {
|
|
445
|
+
...capBufferedExecOutput(err.stdout ?? "", err.stderr ?? err.message ?? ""),
|
|
446
|
+
exitCode: err.exitCode
|
|
447
|
+
};
|
|
448
|
+
return {
|
|
449
|
+
stdout: "",
|
|
450
|
+
stderr: err?.message ?? String(err),
|
|
451
|
+
exitCode: 124
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
async readFile(sandboxId, path) {
|
|
456
|
+
return get(sandboxId).sandbox.files.read(path);
|
|
457
|
+
},
|
|
458
|
+
async writeFile(sandboxId, path, content, encoding = "utf-8") {
|
|
459
|
+
const data = typeof content === "string" ? encoding === "utf-8" || encoding === "utf8" ? content : Buffer.from(content, encoding) : content;
|
|
460
|
+
await get(sandboxId).sandbox.files.write(path, data);
|
|
461
|
+
},
|
|
462
|
+
async listFiles(sandboxId, path) {
|
|
463
|
+
return (await get(sandboxId).sandbox.files.list(path)).map((e) => e.type === "dir" ? `${e.name}/` : e.name);
|
|
464
|
+
},
|
|
465
|
+
async destroy(sandboxId) {
|
|
466
|
+
const entry = live.get(sandboxId);
|
|
467
|
+
if (!entry) return;
|
|
468
|
+
if (entry.owned) {
|
|
469
|
+
const action = options.onDestroy ?? "kill";
|
|
470
|
+
if (action === "pause") await pauseOwnedSandbox(entry.sandbox);
|
|
471
|
+
else if (action === "kill") await killOwnedSandbox(entry.sandbox);
|
|
472
|
+
}
|
|
473
|
+
live.delete(sandboxId);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
//#endregion
|
|
478
|
+
export { resolveE2BEnv as a, waitForE2BReady as i, resolveE2BTemplate as n, runE2BPregame as r, createE2BProvider as t };
|
|
479
|
+
|
|
480
|
+
//# sourceMappingURL=e2b-CEVOdRib.js.map
|