+
+
+
+ Ghostty PTY
+
+
+ {connectionState === "connecting" ?
: null}
+ {connectionState === "ready" ?
: null}
+ {connectionState === "error" ?
: null}
+
{statusMessage}
+ {exitCode != null ?
exit={exitCode} : null}
+
+
+
{
+ hostRef.current?.querySelector("textarea")?.focus();
+ }}
+ />
+
+ );
+};
+
+export default GhosttyTerminal;
diff --git a/frontend/packages/inspector/vite.config.ts b/frontend/packages/inspector/vite.config.ts
index 5280cd7..6496813 100644
--- a/frontend/packages/inspector/vite.config.ts
+++ b/frontend/packages/inspector/vite.config.ts
@@ -10,6 +10,7 @@ export default defineConfig(({ command }) => ({
"/v1": {
target: "http://localhost:2468",
changeOrigin: true,
+ ws: true,
},
},
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1463987..73f90f5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -17,13 +17,13 @@ importers:
version: 2.7.6
vitest:
specifier: ^3.0.0
- version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
examples/boxlite:
dependencies:
'@boxlite-ai/boxlite':
specifier: latest
- version: 0.2.11
+ version: 0.3.0
'@sandbox-agent/example-shared':
specifier: workspace:*
version: link:../shared
@@ -33,7 +33,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -45,7 +45,7 @@ importers:
dependencies:
'@cloudflare/sandbox':
specifier: latest
- version: 0.7.5
+ version: 0.7.12
hono:
specifier: ^4.12.2
version: 4.12.2
@@ -61,10 +61,10 @@ importers:
devDependencies:
'@cloudflare/workers-types':
specifier: latest
- version: 4.20260304.0
+ version: 4.20260305.1
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
'@types/react':
specifier: ^18.3.3
version: 18.3.27
@@ -73,19 +73,19 @@ importers:
version: 18.3.7(@types/react@18.3.27)
'@vitejs/plugin-react':
specifier: ^4.5.0
- version: 4.7.0(vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))
+ version: 4.7.0(vite@6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))
typescript:
specifier: latest
version: 5.9.3
vite:
specifier: ^6.2.0
- version: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ version: 6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
vitest:
specifier: ^3.0.0
- version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
wrangler:
specifier: latest
- version: 4.68.1(@cloudflare/workers-types@4.20260304.0)
+ version: 4.71.0(@cloudflare/workers-types@4.20260305.1)
examples/computesdk:
dependencies:
@@ -101,7 +101,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -110,13 +110,13 @@ importers:
version: 5.9.3
vitest:
specifier: ^3.0.0
- version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
examples/daytona:
dependencies:
'@daytonaio/sdk':
specifier: latest
- version: 0.145.0(ws@8.19.0)
+ version: 0.149.0(ws@8.19.0)
'@sandbox-agent/example-shared':
specifier: workspace:*
version: link:../shared
@@ -126,7 +126,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -151,7 +151,7 @@ importers:
version: 4.0.1
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -160,7 +160,7 @@ importers:
version: 5.9.3
vitest:
specifier: ^3.0.0
- version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
examples/e2b:
dependencies:
@@ -176,7 +176,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -185,7 +185,7 @@ importers:
version: 5.9.3
vitest:
specifier: ^3.0.0
- version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
examples/file-system:
dependencies:
@@ -201,7 +201,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -220,7 +220,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -245,7 +245,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
esbuild:
specifier: latest
version: 0.27.3
@@ -260,7 +260,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
typescript:
specifier: latest
version: 5.9.3
@@ -276,7 +276,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -294,17 +294,17 @@ importers:
version: link:../../sdks/persist-postgres
pg:
specifier: latest
- version: 8.18.0
+ version: 8.20.0
sandbox-agent:
specifier: workspace:*
version: link:../../sdks/typescript
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
'@types/pg':
specifier: latest
- version: 8.16.0
+ version: 8.18.0
tsx:
specifier: latest
version: 4.21.0
@@ -326,7 +326,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -348,7 +348,7 @@ importers:
version: 4.0.1
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
typescript:
specifier: latest
version: 5.9.3
@@ -364,7 +364,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -383,7 +383,7 @@ importers:
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
esbuild:
specifier: latest
version: 0.27.3
@@ -401,14 +401,14 @@ importers:
version: link:../shared
'@vercel/sandbox':
specifier: latest
- version: 1.7.1
+ version: 1.8.0
sandbox-agent:
specifier: workspace:*
version: link:../../sdks/typescript
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -417,13 +417,16 @@ importers:
version: 5.9.3
vitest:
specifier: ^3.0.0
- version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
frontend/packages/inspector:
dependencies:
'@sandbox-agent/persist-indexeddb':
specifier: workspace:*
version: link:../../../sdks/persist-indexeddb
+ ghostty-web:
+ specifier: ^0.4.0
+ version: 0.4.0
lucide-react:
specifier: ^0.469.0
version: 0.469.0(react@18.3.1)
@@ -442,7 +445,7 @@ importers:
version: 18.3.7(@types/react@18.3.27)
'@vitejs/plugin-react':
specifier: ^4.3.1
- version: 4.7.0(vite@5.4.21(@types/node@25.3.0))
+ version: 4.7.0(vite@5.4.21(@types/node@25.3.5))
fake-indexeddb:
specifier: ^6.2.4
version: 6.2.5
@@ -454,25 +457,25 @@ importers:
version: 5.9.3
vite:
specifier: ^5.4.7
- version: 5.4.21(@types/node@25.3.0)
+ version: 5.4.21(@types/node@25.3.5)
vitest:
specifier: ^3.0.0
- version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
frontend/packages/website:
dependencies:
'@astrojs/react':
specifier: ^4.2.0
- version: 4.4.2(@types/node@25.3.0)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(jiti@1.21.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.8.2)
+ version: 4.4.2(@types/node@25.3.5)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(jiti@1.21.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.8.2)
'@astrojs/sitemap':
specifier: ^3.2.0
version: 3.7.0
'@astrojs/tailwind':
specifier: ^6.0.0
- version: 6.0.2(astro@5.16.15(@types/node@25.3.0)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))
+ version: 6.0.2(astro@5.16.15(@types/node@25.3.5)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))
astro:
specifier: ^5.1.0
- version: 5.16.15(@types/node@25.3.0)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
+ version: 5.16.15(@types/node@25.3.5)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
framer-motion:
specifier: ^12.0.0
version: 12.29.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -556,14 +559,14 @@ importers:
dependencies:
'@daytonaio/sdk':
specifier: latest
- version: 0.145.0(ws@8.19.0)
+ version: 0.149.0(ws@8.19.0)
'@e2b/code-interpreter':
specifier: latest
version: 2.3.3
devDependencies:
'@types/node':
specifier: latest
- version: 25.3.0
+ version: 25.3.5
tsx:
specifier: latest
version: 4.21.0
@@ -614,7 +617,7 @@ importers:
devDependencies:
vitest:
specifier: ^3.0.0
- version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
sdks/cli-shared:
devDependencies:
@@ -662,7 +665,7 @@ importers:
devDependencies:
vitest:
specifier: ^3.0.0
- version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
sdks/gigacode/platforms/darwin-arm64: {}
@@ -784,6 +787,9 @@ importers:
'@types/node':
specifier: ^22.0.0
version: 22.19.7
+ '@types/ws':
+ specifier: ^8.18.1
+ version: 8.18.1
openapi-typescript:
specifier: ^6.7.0
version: 6.7.6
@@ -796,6 +802,9 @@ importers:
vitest:
specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ ws:
+ specifier: ^8.19.0
+ version: 8.19.0
packages:
@@ -1123,20 +1132,20 @@ packages:
'@balena/dockerignore@1.0.2':
resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==}
- '@boxlite-ai/boxlite-darwin-arm64@0.2.11':
- resolution: {integrity: sha512-JjNf6S/+XqooWvFX2Zn9XjmeML/e6Errk0jzG77v8YV0k2nNmt8P1nMANb2kMPbsQn93ap9v74VnYesYdKRoNg==}
+ '@boxlite-ai/boxlite-darwin-arm64@0.3.0':
+ resolution: {integrity: sha512-V0FeD7VTQ+V4LFAwHzSe2K7hl7IjXKS6u1VrWr/H0zJ8GGZTAi6feI1w+QTmLJMgdlJdIufWsJwY/RsjtwwF/Q==}
engines: {node: '>=18.0.0'}
cpu: [arm64]
os: [darwin]
- '@boxlite-ai/boxlite-linux-x64-gnu@0.2.11':
- resolution: {integrity: sha512-H3a8FMc6X4KVsmlQKs2xTIlSh4KhiI52MnXV16OwcC6OWQBBadR1N6GCCKojfwpqn6yIsZc2dxoyy25YTYYf9g==}
+ '@boxlite-ai/boxlite-linux-x64-gnu@0.3.0':
+ resolution: {integrity: sha512-1VkXxzm+3hmuP6XpbZxPsaf+Tv2gwd7iHAH76f2uWulooxRjATnk+Smhud+FuHvLQIvjr8ERAA26vMbST5OgpQ==}
engines: {node: '>=18.0.0'}
cpu: [x64]
os: [linux]
- '@boxlite-ai/boxlite@0.2.11':
- resolution: {integrity: sha512-IJ+jyYdsc1hmZknDtqGpRyMAMxoQfF1VFDVuPhiO59fBmoDEI5u69DzoMtyax4gzL3Q46tjYkVBvJhNtSDaxBw==}
+ '@boxlite-ai/boxlite@0.3.0':
+ resolution: {integrity: sha512-D9sU7PUzFHlgv6aIGf+h5kp0+C2A05RVX73aaSMK2gWjQgf12lJ/SVg3OiMSmhnV0cZ+Q0hTn+EBnDWpe26cqA==}
engines: {node: '>=18.0.0'}
peerDependencies:
playwright-core: '>=1.58.0'
@@ -1181,15 +1190,15 @@ packages:
cpu: [x64]
os: [win32]
- '@cloudflare/containers@0.0.30':
- resolution: {integrity: sha512-i148xBgmyn/pje82ZIyuTr/Ae0BT/YWwa1/GTJcw6DxEjUHAzZLaBCiX446U9OeuJ2rBh/L/9FIzxX5iYNt1AQ==}
+ '@cloudflare/containers@0.1.1':
+ resolution: {integrity: sha512-YTdobRTnTlUOUPMFemufH367A9Z8pDfZ+UboYMLbGpO0VlvEXZDiioSmXPQMHld2vRtkL31mcRii3bcbQU6fdw==}
'@cloudflare/kv-asset-handler@0.4.2':
resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==}
engines: {node: '>=18.0.0'}
- '@cloudflare/sandbox@0.7.5':
- resolution: {integrity: sha512-lOegEUL6eDsHrsxEMxqRcftsp46hn+ilQryCLuSDghHvnCdDAenzyN/E3nVjQdYAZnoh5xsLzis/G295LcZr1w==}
+ '@cloudflare/sandbox@0.7.12':
+ resolution: {integrity: sha512-Frk8S/xZ3jDyQIreu66C4fQtfERmG9ZLQT6iJFfJUJN/aMUvHehRyAy34BNfHTXFZc3/YxGcnRBgitsWI9jArg==}
peerDependencies:
'@openai/agents': ^0.3.3
'@opencode-ai/sdk': ^1.1.40
@@ -1202,47 +1211,47 @@ packages:
'@xterm/xterm':
optional: true
- '@cloudflare/unenv-preset@2.14.0':
- resolution: {integrity: sha512-XKAkWhi1nBdNsSEoNG9nkcbyvfUrSjSf+VYVPfOto3gLTZVc3F4g6RASCMh6IixBKCG2yDgZKQIHGKtjcnLnKg==}
+ '@cloudflare/unenv-preset@2.15.0':
+ resolution: {integrity: sha512-EGYmJaGZKWl+X8tXxcnx4v2bOZSjQeNI5dWFeXivgX9+YCT69AkzHHwlNbVpqtEUTbew8eQurpyOpeN8fg00nw==}
peerDependencies:
unenv: 2.0.0-rc.24
- workerd: ^1.20260218.0
+ workerd: 1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0
peerDependenciesMeta:
workerd:
optional: true
- '@cloudflare/workerd-darwin-64@1.20260302.0':
- resolution: {integrity: sha512-cGtxPByeVrgoqxbmd8qs631wuGwf8yTm/FY44dEW4HdoXrb5jhlE4oWYHFafedkQCvGjY1Vbs3puAiKnuMxTXQ==}
+ '@cloudflare/workerd-darwin-64@1.20260301.1':
+ resolution: {integrity: sha512-+kJvwociLrvy1JV9BAvoSVsMEIYD982CpFmo/yMEvBwxDIjltYsLTE8DLi0mCkGsQ8Ygidv2fD9wavzXeiY7OQ==}
engines: {node: '>=16'}
cpu: [x64]
os: [darwin]
- '@cloudflare/workerd-darwin-arm64@1.20260302.0':
- resolution: {integrity: sha512-WRGqV6RNXM3xoQblJJw1EHKwx9exyhB18cdnToSCUFPObFhk3fzMLoQh7S+nUHUpto6aUrXPVj6R/4G3UPjCxw==}
+ '@cloudflare/workerd-darwin-arm64@1.20260301.1':
+ resolution: {integrity: sha512-PPIetY3e67YBr9O4UhILK8nbm5TqUDl14qx4rwFNrRSBOvlzuczzbd4BqgpAtbGVFxKp1PWpjAnBvGU/OI/tLQ==}
engines: {node: '>=16'}
cpu: [arm64]
os: [darwin]
- '@cloudflare/workerd-linux-64@1.20260302.0':
- resolution: {integrity: sha512-gG423mtUjrmlQT+W2+KisLc6qcGcBLR+QcK5x1gje3bu/dF3oNiYuqY7o58A+sQk6IB849UC4UyNclo1RhP2xw==}
+ '@cloudflare/workerd-linux-64@1.20260301.1':
+ resolution: {integrity: sha512-Gu5vaVTZuYl3cHa+u5CDzSVDBvSkfNyuAHi6Mdfut7TTUdcb3V5CIcR/mXRSyMXzEy9YxEWIfdKMxOMBjupvYQ==}
engines: {node: '>=16'}
cpu: [x64]
os: [linux]
- '@cloudflare/workerd-linux-arm64@1.20260302.0':
- resolution: {integrity: sha512-7M25noGI4WlSBOhrIaY8xZrnn87OQKtJg9YWAO2EFqGjF1Su5QXGaLlQVF4fAKbqTywbHnI8BAuIsIlUSNkhCg==}
+ '@cloudflare/workerd-linux-arm64@1.20260301.1':
+ resolution: {integrity: sha512-igL1pkyCXW6GiGpjdOAvqMi87UW0LMc/+yIQe/CSzuZJm5GzXoAMrwVTkCFnikk6JVGELrM5x0tGYlxa0sk5Iw==}
engines: {node: '>=16'}
cpu: [arm64]
os: [linux]
- '@cloudflare/workerd-windows-64@1.20260302.0':
- resolution: {integrity: sha512-jK1L3ADkiWxFzlqZTq2iHW1Bd2Nzu1fmMWCGZw4sMZ2W1B2WCm2wHwO2SX/py4BgylyEN3wuF+5zagbkNKht9A==}
+ '@cloudflare/workerd-windows-64@1.20260301.1':
+ resolution: {integrity: sha512-Q0wMJ4kcujXILwQKQFc1jaYamVsNvjuECzvRrTI8OxGFMx2yq9aOsswViE4X1gaS2YQQ5u0JGwuGi5WdT1Lt7A==}
engines: {node: '>=16'}
cpu: [x64]
os: [win32]
- '@cloudflare/workers-types@4.20260304.0':
- resolution: {integrity: sha512-oQ0QJpWnCWK9tx5q/ZHQeSsf5EcQWa4KqdDMY/R5Ln0ojFzv6UYO0RWsfDPsoXUAwK671VwaXqAW0Mx0uWz7yw==}
+ '@cloudflare/workers-types@4.20260305.1':
+ resolution: {integrity: sha512-835BZaIcgjuYIUqgOWJSpwQxFSJ8g/X1OCZFLO7bmirM6TGmVgIGwiGItBgkjUXXCPrYzJEldsJkuFuK7ePuMw==}
'@computesdk/cmd@0.4.1':
resolution: {integrity: sha512-hhcYrwMnOpRSwWma3gkUeAVsDFG56nURwSaQx8vCepv0IuUv39bK4mMkgszolnUQrVjBDdW7b3lV+l5B2S8fRA==}
@@ -1262,14 +1271,14 @@ packages:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
- '@daytonaio/api-client@0.145.0':
- resolution: {integrity: sha512-8xLJ1G7C3QJs2KfONcGd4O4ktHtGM4qxWVAcQERHSE1w4hJVrlaUzutMm2qy+HoXtMU1L5h/eFazoxrJ0xWzPw==}
+ '@daytonaio/api-client@0.149.0':
+ resolution: {integrity: sha512-tlqVFnJll4JUAY3Ictwl7kGI3jo6HP+AcHl8FsZg/lSG7t/SdlZVO9iPPt6kjxmY3WN8BYRI1NYtIFFh8SJolw==}
- '@daytonaio/sdk@0.145.0':
- resolution: {integrity: sha512-RZhe5oz9EdC9PP3g95g+jXFkCiQbPJTfSALe9wi4W5n97hA9O6rM5zYRuwB2PJbHA8YC0m2t5pyHRJA9+88r5A==}
+ '@daytonaio/sdk@0.149.0':
+ resolution: {integrity: sha512-yu228ZVj0FFlas/VmoirqZ/QJNKuvSf5AiDVkPUdejEYHyh98s8owSEFKgOwajtBwtrNn+ETSunkWroMRbzvQg==}
- '@daytonaio/toolbox-api-client@0.145.0':
- resolution: {integrity: sha512-Twh8FIoPAen+pjFeW03Fcom0fYT+k2grw8Q18aHdMAKQtXmMvA3+Ntim5ooE8AsHHLpCL6w+9ycdsovvzZOAEg==}
+ '@daytonaio/toolbox-api-client@0.149.0':
+ resolution: {integrity: sha512-6IiZ+RDDQSRjjKMbmmiQj1uG0u8CxCHxX8YwWVq2Oc/6ACPVBLdNlh4p/xBXUGgxbcPo2ewH1F2y1P2FSUU8aA==}
'@e2b/code-interpreter@2.3.3':
resolution: {integrity: sha512-WOpSwc1WpvxyOijf6WMbR76BUuvd2O9ddXgCHHi65lkuy6YgQGq7oyd8PNsT331O9Tqbccjy6uF4xanSdLX1UA==}
@@ -2826,12 +2835,15 @@ packages:
'@types/node@24.10.9':
resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==}
- '@types/node@25.3.0':
- resolution: {integrity: sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==}
+ '@types/node@25.3.5':
+ resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==}
'@types/pg@8.16.0':
resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==}
+ '@types/pg@8.18.0':
+ resolution: {integrity: sha512-gT+oueVQkqnj6ajGJXblFR4iavIXWsGAFCk3dP4Kki5+a9R4NMt0JARdk6s8cUKcfUoqP5dAtDSLU8xYUTFV+Q==}
+
'@types/prop-types@15.7.15':
resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
@@ -2858,6 +2870,9 @@ packages:
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+ '@types/ws@8.18.1':
+ resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
+
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
@@ -2865,8 +2880,8 @@ packages:
resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==}
engines: {node: '>= 20'}
- '@vercel/sandbox@1.7.1':
- resolution: {integrity: sha512-TI9InUQe7sqyO4/TIiGXC/3RHA0hTt5PpFaTWeWunkbKZae26nuPVsd+p10W/WN2THUKE+NPtTJ21dhp1Yw48w==}
+ '@vercel/sandbox@1.8.0':
+ resolution: {integrity: sha512-SbXkg8Fmp8i+I9IdyD4PAAVtxM/KS4ULV4eiEfY/9tab1AF1MPvmEA8/ebvCn7QTWQQ7twwtpJNSPlUVmOBp3w==}
'@vitejs/plugin-react@4.7.0':
resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
@@ -3711,6 +3726,9 @@ packages:
get-tsconfig@4.13.0:
resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
+ ghostty-web@0.4.0:
+ resolution: {integrity: sha512-0puDBik2qapbD/QQBW9o5ZHfXnZBqZWx/ctBiVtKZ6ZLds4NYb+wZuw1cRLXZk9zYovIQ908z3rvFhexAvc5Hg==}
+
github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
@@ -4190,8 +4208,8 @@ packages:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
- miniflare@4.20260302.0:
- resolution: {integrity: sha512-joGFywlo7HdfHXXGOkc6tDCVkwjEncM0mwEsMOLWcl+vDVJPj9HRV7JtEa0+lCpNOLdYw7mZNHYe12xz9KtJOw==}
+ miniflare@4.20260301.1:
+ resolution: {integrity: sha512-fqkHx0QMKswRH9uqQQQOU/RoaS3Wjckxy3CUX3YGJr0ZIMu7ObvI+NovdYi6RIsSPthNtq+3TPmRNxjeRiasog==}
engines: {node: '>=18.0.0'}
hasBin: true
@@ -4423,6 +4441,9 @@ packages:
pg-connection-string@2.11.0:
resolution: {integrity: sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==}
+ pg-connection-string@2.12.0:
+ resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==}
+
pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
@@ -4432,9 +4453,17 @@ packages:
peerDependencies:
pg: '>=8.0'
+ pg-pool@3.13.0:
+ resolution: {integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==}
+ peerDependencies:
+ pg: '>=8.0'
+
pg-protocol@1.11.0:
resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==}
+ pg-protocol@1.13.0:
+ resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==}
+
pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'}
@@ -4448,6 +4477,15 @@ packages:
pg-native:
optional: true
+ pg@8.20.0:
+ resolution: {integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==}
+ engines: {node: '>= 16.0.0'}
+ peerDependencies:
+ pg-native: '>=3.0.1'
+ peerDependenciesMeta:
+ pg-native:
+ optional: true
+
pgpass@1.0.5:
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
@@ -5011,7 +5049,7 @@ packages:
tar@7.5.6:
resolution: {integrity: sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==}
engines: {node: '>=18'}
- deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
+ deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me
tar@7.5.7:
resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==}
@@ -5484,17 +5522,17 @@ packages:
resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==}
engines: {node: '>=18'}
- workerd@1.20260302.0:
- resolution: {integrity: sha512-FhNdC8cenMDllI6bTktFgxP5Bn5ZEnGtofgKipY6pW9jtq708D1DeGI6vGad78KQLBGaDwFy1eThjCoLYgFfog==}
+ workerd@1.20260301.1:
+ resolution: {integrity: sha512-oterQ1IFd3h7PjCfT4znSFOkJCvNQ6YMOyZ40YsnO3nrSpgB4TbJVYWFOnyJAw71/RQuupfVqZZWKvsy8GO3fw==}
engines: {node: '>=16'}
hasBin: true
- wrangler@4.68.1:
- resolution: {integrity: sha512-G+TI3k/olEGBAVkPtUlhAX/DIbL/190fv3aK+r+45/wPclNEymjxCc35T8QGTDhc2fEMXiw51L5bH9aNsBg+yQ==}
+ wrangler@4.71.0:
+ resolution: {integrity: sha512-j6pSGAncOLNQDRzqtp0EqzYj52CldDP7uz/C9cxVrIgqa5p+cc0b4pIwnapZZAGv9E1Loa3tmPD0aXonH7KTkw==}
engines: {node: '>=20.0.0'}
hasBin: true
peerDependencies:
- '@cloudflare/workers-types': ^4.20260302.0
+ '@cloudflare/workers-types': ^4.20260226.1
peerDependenciesMeta:
'@cloudflare/workers-types':
optional: true
@@ -5665,15 +5703,15 @@ snapshots:
dependencies:
prismjs: 1.30.0
- '@astrojs/react@4.4.2(@types/node@25.3.0)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(jiti@1.21.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.8.2)':
+ '@astrojs/react@4.4.2(@types/node@25.3.5)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(jiti@1.21.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.8.2)':
dependencies:
'@types/react': 18.3.27
'@types/react-dom': 18.3.7(@types/react@18.3.27)
- '@vitejs/plugin-react': 4.7.0(vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))
+ '@vitejs/plugin-react': 4.7.0(vite@6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
ultrahtml: 1.6.0
- vite: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ vite: 6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- '@types/node'
- jiti
@@ -5694,9 +5732,9 @@ snapshots:
stream-replace-string: 2.0.0
zod: 3.25.76
- '@astrojs/tailwind@6.0.2(astro@5.16.15(@types/node@25.3.0)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))':
+ '@astrojs/tailwind@6.0.2(astro@5.16.15(@types/node@25.3.5)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))':
dependencies:
- astro: 5.16.15(@types/node@25.3.0)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
+ astro: 5.16.15(@types/node@25.3.5)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
autoprefixer: 10.4.23(postcss@8.5.6)
postcss: 8.5.6
postcss-load-config: 4.0.2(postcss@8.5.6)
@@ -6374,16 +6412,16 @@ snapshots:
'@balena/dockerignore@1.0.2': {}
- '@boxlite-ai/boxlite-darwin-arm64@0.2.11':
+ '@boxlite-ai/boxlite-darwin-arm64@0.3.0':
optional: true
- '@boxlite-ai/boxlite-linux-x64-gnu@0.2.11':
+ '@boxlite-ai/boxlite-linux-x64-gnu@0.3.0':
optional: true
- '@boxlite-ai/boxlite@0.2.11':
+ '@boxlite-ai/boxlite@0.3.0':
optionalDependencies:
- '@boxlite-ai/boxlite-darwin-arm64': 0.2.11
- '@boxlite-ai/boxlite-linux-x64-gnu': 0.2.11
+ '@boxlite-ai/boxlite-darwin-arm64': 0.3.0
+ '@boxlite-ai/boxlite-linux-x64-gnu': 0.3.0
'@bufbuild/protobuf@2.11.0': {}
@@ -6409,37 +6447,37 @@ snapshots:
'@cbor-extract/cbor-extract-win32-x64@2.2.0':
optional: true
- '@cloudflare/containers@0.0.30': {}
+ '@cloudflare/containers@0.1.1': {}
'@cloudflare/kv-asset-handler@0.4.2': {}
- '@cloudflare/sandbox@0.7.5':
+ '@cloudflare/sandbox@0.7.12':
dependencies:
- '@cloudflare/containers': 0.0.30
+ '@cloudflare/containers': 0.1.1
aws4fetch: 1.0.20
- '@cloudflare/unenv-preset@2.14.0(unenv@2.0.0-rc.24)(workerd@1.20260302.0)':
+ '@cloudflare/unenv-preset@2.15.0(unenv@2.0.0-rc.24)(workerd@1.20260301.1)':
dependencies:
unenv: 2.0.0-rc.24
optionalDependencies:
- workerd: 1.20260302.0
+ workerd: 1.20260301.1
- '@cloudflare/workerd-darwin-64@1.20260302.0':
+ '@cloudflare/workerd-darwin-64@1.20260301.1':
optional: true
- '@cloudflare/workerd-darwin-arm64@1.20260302.0':
+ '@cloudflare/workerd-darwin-arm64@1.20260301.1':
optional: true
- '@cloudflare/workerd-linux-64@1.20260302.0':
+ '@cloudflare/workerd-linux-64@1.20260301.1':
optional: true
- '@cloudflare/workerd-linux-arm64@1.20260302.0':
+ '@cloudflare/workerd-linux-arm64@1.20260301.1':
optional: true
- '@cloudflare/workerd-windows-64@1.20260302.0':
+ '@cloudflare/workerd-windows-64@1.20260301.1':
optional: true
- '@cloudflare/workers-types@4.20260304.0': {}
+ '@cloudflare/workers-types@4.20260305.1': {}
'@computesdk/cmd@0.4.1': {}
@@ -6456,18 +6494,18 @@ snapshots:
dependencies:
'@jridgewell/trace-mapping': 0.3.9
- '@daytonaio/api-client@0.145.0':
+ '@daytonaio/api-client@0.149.0':
dependencies:
axios: 1.13.5
transitivePeerDependencies:
- debug
- '@daytonaio/sdk@0.145.0(ws@8.19.0)':
+ '@daytonaio/sdk@0.149.0(ws@8.19.0)':
dependencies:
'@aws-sdk/client-s3': 3.975.0
'@aws-sdk/lib-storage': 3.975.0(@aws-sdk/client-s3@3.975.0)
- '@daytonaio/api-client': 0.145.0
- '@daytonaio/toolbox-api-client': 0.145.0
+ '@daytonaio/api-client': 0.149.0
+ '@daytonaio/toolbox-api-client': 0.149.0
'@iarna/toml': 2.2.5
'@opentelemetry/api': 1.9.0
'@opentelemetry/exporter-trace-otlp-http': 0.207.0(@opentelemetry/api@1.9.0)
@@ -6493,7 +6531,7 @@ snapshots:
- supports-color
- ws
- '@daytonaio/toolbox-api-client@0.145.0':
+ '@daytonaio/toolbox-api-client@0.149.0':
dependencies:
axios: 1.13.5
transitivePeerDependencies:
@@ -7836,7 +7874,7 @@ snapshots:
'@types/better-sqlite3@7.6.13':
dependencies:
- '@types/node': 25.3.0
+ '@types/node': 24.10.9
'@types/chai@5.2.3':
dependencies:
@@ -7851,13 +7889,13 @@ snapshots:
'@types/docker-modem@3.0.6':
dependencies:
- '@types/node': 25.3.0
+ '@types/node': 25.3.5
'@types/ssh2': 1.15.5
'@types/dockerode@4.0.1':
dependencies:
'@types/docker-modem': 3.0.6
- '@types/node': 25.3.0
+ '@types/node': 25.3.5
'@types/ssh2': 1.15.5
'@types/estree@1.0.8': {}
@@ -7892,13 +7930,19 @@ snapshots:
dependencies:
undici-types: 7.16.0
- '@types/node@25.3.0':
+ '@types/node@25.3.5':
dependencies:
undici-types: 7.18.2
'@types/pg@8.16.0':
dependencies:
- '@types/node': 25.3.0
+ '@types/node': 24.10.9
+ pg-protocol: 1.11.0
+ pg-types: 2.2.0
+
+ '@types/pg@8.18.0':
+ dependencies:
+ '@types/node': 25.3.5
pg-protocol: 1.11.0
pg-types: 2.2.0
@@ -7917,7 +7961,7 @@ snapshots:
'@types/sax@1.2.7':
dependencies:
- '@types/node': 25.3.0
+ '@types/node': 24.10.9
'@types/semver@7.7.1': {}
@@ -7927,11 +7971,15 @@ snapshots:
'@types/unist@3.0.3': {}
+ '@types/ws@8.18.1':
+ dependencies:
+ '@types/node': 24.10.9
+
'@ungap/structured-clone@1.3.0': {}
'@vercel/oidc@3.2.0': {}
- '@vercel/sandbox@1.7.1':
+ '@vercel/sandbox@1.8.0':
dependencies:
'@vercel/oidc': 3.2.0
async-retry: 1.3.3
@@ -7946,7 +7994,7 @@ snapshots:
- bare-abort-controller
- react-native-b4a
- '@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@25.3.0))':
+ '@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@25.3.5))':
dependencies:
'@babel/core': 7.28.6
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6)
@@ -7954,11 +8002,11 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-beta.27
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
- vite: 5.4.21(@types/node@25.3.0)
+ vite: 5.4.21(@types/node@25.3.5)
transitivePeerDependencies:
- supports-color
- '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))':
+ '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))':
dependencies:
'@babel/core': 7.28.6
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6)
@@ -7966,7 +8014,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-beta.27
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
- vite: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ vite: 6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- supports-color
@@ -7978,13 +8026,21 @@ snapshots:
chai: 5.3.3
tinyrainbow: 2.0.0
- '@vitest/mocker@3.2.4(vite@5.4.21(@types/node@25.3.0))':
+ '@vitest/mocker@3.2.4(vite@5.4.21(@types/node@22.19.7))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- vite: 5.4.21(@types/node@25.3.0)
+ vite: 5.4.21(@types/node@22.19.7)
+
+ '@vitest/mocker@3.2.4(vite@5.4.21(@types/node@25.3.5))':
+ dependencies:
+ '@vitest/spy': 3.2.4
+ estree-walker: 3.0.3
+ magic-string: 0.30.21
+ optionalDependencies:
+ vite: 5.4.21(@types/node@25.3.5)
'@vitest/pretty-format@3.2.4':
dependencies:
@@ -8071,7 +8127,7 @@ snapshots:
assertion-error@2.0.1: {}
- astro@5.16.15(@types/node@25.3.0)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2):
+ astro@5.16.15(@types/node@25.3.5)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2):
dependencies:
'@astrojs/compiler': 2.13.0
'@astrojs/internal-helpers': 0.7.5
@@ -8128,8 +8184,8 @@ snapshots:
unist-util-visit: 5.1.0
unstorage: 1.17.4(aws4fetch@1.0.20)
vfile: 6.0.3
- vite: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
- vitefu: 1.1.1(vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))
+ vite: 6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ vitefu: 1.1.1(vite@6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))
xxhash-wasm: 1.1.0
yargs-parser: 21.1.1
yocto-spinner: 0.2.3
@@ -8982,6 +9038,8 @@ snapshots:
dependencies:
resolve-pkg-maps: 1.0.0
+ ghostty-web@0.4.0: {}
+
github-from-package@0.0.0: {}
github-slugger@2.0.0: {}
@@ -9648,12 +9706,12 @@ snapshots:
mimic-response@3.1.0: {}
- miniflare@4.20260302.0:
+ miniflare@4.20260301.1:
dependencies:
'@cspotcode/source-map-support': 0.8.1
sharp: 0.34.5
undici: 7.18.2
- workerd: 1.20260302.0
+ workerd: 1.20260301.1
ws: 8.18.0
youch: 4.1.0-beta.10
transitivePeerDependencies:
@@ -9869,14 +9927,22 @@ snapshots:
pg-connection-string@2.11.0: {}
+ pg-connection-string@2.12.0: {}
+
pg-int8@1.0.1: {}
pg-pool@3.11.0(pg@8.18.0):
dependencies:
pg: 8.18.0
+ pg-pool@3.13.0(pg@8.20.0):
+ dependencies:
+ pg: 8.20.0
+
pg-protocol@1.11.0: {}
+ pg-protocol@1.13.0: {}
+
pg-types@2.2.0:
dependencies:
pg-int8: 1.0.1
@@ -9895,6 +9961,16 @@ snapshots:
optionalDependencies:
pg-cloudflare: 1.3.0
+ pg@8.20.0:
+ dependencies:
+ pg-connection-string: 2.12.0
+ pg-pool: 3.13.0(pg@8.20.0)
+ pg-protocol: 1.13.0
+ pg-types: 2.2.0
+ pgpass: 1.0.5
+ optionalDependencies:
+ pg-cloudflare: 1.3.0
+
pgpass@1.0.5:
dependencies:
split2: 4.2.0
@@ -10035,7 +10111,7 @@ snapshots:
'@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0
- '@types/node': 25.3.0
+ '@types/node': 25.3.5
long: 5.3.2
proxy-addr@2.0.7:
@@ -10940,13 +11016,13 @@ snapshots:
- tsx
- yaml
- vite-node@3.2.4(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
+ vite-node@3.2.4(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
dependencies:
cac: 6.7.14
debug: 4.4.3
es-module-lexer: 1.7.0
pathe: 2.0.3
- vite: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ vite: 6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- '@types/node'
- jiti
@@ -10970,13 +11046,13 @@ snapshots:
'@types/node': 22.19.7
fsevents: 2.3.3
- vite@5.4.21(@types/node@25.3.0):
+ vite@5.4.21(@types/node@25.3.5):
dependencies:
esbuild: 0.21.5
postcss: 8.5.6
rollup: 4.56.0
optionalDependencies:
- '@types/node': 25.3.0
+ '@types/node': 25.3.5
fsevents: 2.3.3
vite@6.4.1(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
@@ -10994,7 +11070,7 @@ snapshots:
tsx: 4.21.0
yaml: 2.8.2
- vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
+ vite@6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
dependencies:
esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.3)
@@ -11003,21 +11079,21 @@ snapshots:
rollup: 4.56.0
tinyglobby: 0.2.15
optionalDependencies:
- '@types/node': 25.3.0
+ '@types/node': 25.3.5
fsevents: 2.3.3
jiti: 1.21.7
tsx: 4.21.0
yaml: 2.8.2
- vitefu@1.1.1(vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)):
+ vitefu@1.1.1(vite@6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)):
optionalDependencies:
- vite: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ vite: 6.4.1(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
dependencies:
'@types/chai': 5.2.3
'@vitest/expect': 3.2.4
- '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@25.3.0))
+ '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@22.19.7))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
@@ -11055,11 +11131,11 @@ snapshots:
- tsx
- yaml
- vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
+ vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
dependencies:
'@types/chai': 5.2.3
'@vitest/expect': 3.2.4
- '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@25.3.0))
+ '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@25.3.5))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
@@ -11077,12 +11153,12 @@ snapshots:
tinyglobby: 0.2.15
tinypool: 1.1.1
tinyrainbow: 2.0.0
- vite: 5.4.21(@types/node@25.3.0)
- vite-node: 3.2.4(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
+ vite: 5.4.21(@types/node@25.3.5)
+ vite-node: 3.2.4(@types/node@25.3.5)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/debug': 4.1.12
- '@types/node': 25.3.0
+ '@types/node': 25.3.5
transitivePeerDependencies:
- jiti
- less
@@ -11118,26 +11194,26 @@ snapshots:
dependencies:
string-width: 7.2.0
- workerd@1.20260302.0:
+ workerd@1.20260301.1:
optionalDependencies:
- '@cloudflare/workerd-darwin-64': 1.20260302.0
- '@cloudflare/workerd-darwin-arm64': 1.20260302.0
- '@cloudflare/workerd-linux-64': 1.20260302.0
- '@cloudflare/workerd-linux-arm64': 1.20260302.0
- '@cloudflare/workerd-windows-64': 1.20260302.0
+ '@cloudflare/workerd-darwin-64': 1.20260301.1
+ '@cloudflare/workerd-darwin-arm64': 1.20260301.1
+ '@cloudflare/workerd-linux-64': 1.20260301.1
+ '@cloudflare/workerd-linux-arm64': 1.20260301.1
+ '@cloudflare/workerd-windows-64': 1.20260301.1
- wrangler@4.68.1(@cloudflare/workers-types@4.20260304.0):
+ wrangler@4.71.0(@cloudflare/workers-types@4.20260305.1):
dependencies:
'@cloudflare/kv-asset-handler': 0.4.2
- '@cloudflare/unenv-preset': 2.14.0(unenv@2.0.0-rc.24)(workerd@1.20260302.0)
+ '@cloudflare/unenv-preset': 2.15.0(unenv@2.0.0-rc.24)(workerd@1.20260301.1)
blake3-wasm: 2.1.5
esbuild: 0.27.3
- miniflare: 4.20260302.0
+ miniflare: 4.20260301.1
path-to-regexp: 6.3.0
unenv: 2.0.0-rc.24
- workerd: 1.20260302.0
+ workerd: 1.20260301.1
optionalDependencies:
- '@cloudflare/workers-types': 4.20260304.0
+ '@cloudflare/workers-types': 4.20260305.1
fsevents: 2.3.3
transitivePeerDependencies:
- bufferutil
diff --git a/sdks/acp-http-client/tests/smoke.test.ts b/sdks/acp-http-client/tests/smoke.test.ts
index 2380010..8b92e6c 100644
--- a/sdks/acp-http-client/tests/smoke.test.ts
+++ b/sdks/acp-http-client/tests/smoke.test.ts
@@ -74,6 +74,10 @@ describe("AcpHttpClient integration", () => {
timeoutMs: 30000,
env: {
XDG_DATA_HOME: dataHome,
+ HOME: dataHome,
+ USERPROFILE: dataHome,
+ APPDATA: join(dataHome, "AppData", "Roaming"),
+ LOCALAPPDATA: join(dataHome, "AppData", "Local"),
},
});
baseUrl = handle.baseUrl;
diff --git a/sdks/persist-indexeddb/tests/integration.test.ts b/sdks/persist-indexeddb/tests/integration.test.ts
index a30e70e..064c83d 100644
--- a/sdks/persist-indexeddb/tests/integration.test.ts
+++ b/sdks/persist-indexeddb/tests/integration.test.ts
@@ -60,6 +60,10 @@ describe("IndexedDB persistence end-to-end", () => {
timeoutMs: 30000,
env: {
XDG_DATA_HOME: dataHome,
+ HOME: dataHome,
+ USERPROFILE: dataHome,
+ APPDATA: join(dataHome, "AppData", "Roaming"),
+ LOCALAPPDATA: join(dataHome, "AppData", "Local"),
},
});
baseUrl = handle.baseUrl;
diff --git a/sdks/persist-postgres/tests/integration.test.ts b/sdks/persist-postgres/tests/integration.test.ts
index f453021..9017775 100644
--- a/sdks/persist-postgres/tests/integration.test.ts
+++ b/sdks/persist-postgres/tests/integration.test.ts
@@ -64,6 +64,10 @@ describe("Postgres persistence driver", () => {
timeoutMs: 30000,
env: {
XDG_DATA_HOME: dataHome,
+ HOME: dataHome,
+ USERPROFILE: dataHome,
+ APPDATA: join(dataHome, "AppData", "Roaming"),
+ LOCALAPPDATA: join(dataHome, "AppData", "Local"),
},
});
baseUrl = handle.baseUrl;
diff --git a/sdks/persist-sqlite/tests/integration.test.ts b/sdks/persist-sqlite/tests/integration.test.ts
index fb4b99c..5c4948a 100644
--- a/sdks/persist-sqlite/tests/integration.test.ts
+++ b/sdks/persist-sqlite/tests/integration.test.ts
@@ -55,6 +55,10 @@ describe("SQLite persistence driver", () => {
timeoutMs: 30000,
env: {
XDG_DATA_HOME: dataHome,
+ HOME: dataHome,
+ USERPROFILE: dataHome,
+ APPDATA: join(dataHome, "AppData", "Roaming"),
+ LOCALAPPDATA: join(dataHome, "AppData", "Local"),
},
});
baseUrl = handle.baseUrl;
diff --git a/sdks/typescript/package.json b/sdks/typescript/package.json
index 990f952..b9f3716 100644
--- a/sdks/typescript/package.json
+++ b/sdks/typescript/package.json
@@ -17,8 +17,8 @@
}
},
"dependencies": {
- "acp-http-client": "workspace:*",
- "@sandbox-agent/cli-shared": "workspace:*"
+ "@sandbox-agent/cli-shared": "workspace:*",
+ "acp-http-client": "workspace:*"
},
"files": [
"dist"
@@ -34,10 +34,12 @@
},
"devDependencies": {
"@types/node": "^22.0.0",
+ "@types/ws": "^8.18.1",
"openapi-typescript": "^6.7.0",
"tsup": "^8.0.0",
"typescript": "^5.7.0",
- "vitest": "^3.0.0"
+ "vitest": "^3.0.0",
+ "ws": "^8.19.0"
},
"optionalDependencies": {
"@sandbox-agent/cli": "workspace:*"
diff --git a/sdks/typescript/src/client.ts b/sdks/typescript/src/client.ts
index 77d3622..35d1691 100644
--- a/sdks/typescript/src/client.ts
+++ b/sdks/typescript/src/client.ts
@@ -39,6 +39,20 @@ import {
type McpConfigQuery,
type McpServerConfig,
type ProblemDetails,
+ type ProcessConfig,
+ type ProcessCreateRequest,
+ type ProcessInfo,
+ type ProcessInputRequest,
+ type ProcessInputResponse,
+ type ProcessListResponse,
+ type ProcessLogEntry,
+ type ProcessLogsQuery,
+ type ProcessLogsResponse,
+ type ProcessRunRequest,
+ type ProcessRunResponse,
+ type ProcessSignalQuery,
+ type ProcessTerminalResizeRequest,
+ type ProcessTerminalResizeResponse,
type SessionEvent,
type SessionPersistDriver,
type SessionRecord,
@@ -98,6 +112,27 @@ export interface SessionSendOptions {
}
export type SessionEventListener = (event: SessionEvent) => void;
+export type ProcessLogListener = (entry: ProcessLogEntry) => void;
+export type ProcessLogFollowQuery = Omit
;
+
+export interface AgentQueryOptions {
+ config?: boolean;
+ noCache?: boolean;
+}
+
+export interface ProcessLogSubscription {
+ close(): void;
+ closed: Promise;
+}
+
+export interface ProcessTerminalWebSocketUrlOptions {
+ accessToken?: string;
+}
+
+export interface ProcessTerminalConnectOptions extends ProcessTerminalWebSocketUrlOptions {
+ protocols?: string | string[];
+ WebSocket?: typeof WebSocket;
+}
export class SandboxAgentError extends Error {
readonly status: number;
@@ -674,15 +709,15 @@ export class SandboxAgent {
return this.requestJson("GET", `${API_PREFIX}/health`);
}
- async listAgents(options?: { config?: boolean }): Promise {
+ async listAgents(options?: AgentQueryOptions): Promise {
return this.requestJson("GET", `${API_PREFIX}/agents`, {
- query: options?.config ? { config: "true" } : undefined,
+ query: toAgentQuery(options),
});
}
- async getAgent(agent: string, options?: { config?: boolean }): Promise {
+ async getAgent(agent: string, options?: AgentQueryOptions): Promise {
return this.requestJson("GET", `${API_PREFIX}/agents/${encodeURIComponent(agent)}`, {
- query: options?.config ? { config: "true" } : undefined,
+ query: toAgentQuery(options),
});
}
@@ -771,6 +806,134 @@ export class SandboxAgent {
await this.requestRaw("DELETE", `${API_PREFIX}/config/skills`, { query });
}
+ async getProcessConfig(): Promise {
+ return this.requestJson("GET", `${API_PREFIX}/processes/config`);
+ }
+
+ async setProcessConfig(config: ProcessConfig): Promise {
+ return this.requestJson("POST", `${API_PREFIX}/processes/config`, {
+ body: config,
+ });
+ }
+
+ async createProcess(request: ProcessCreateRequest): Promise {
+ return this.requestJson("POST", `${API_PREFIX}/processes`, {
+ body: request,
+ });
+ }
+
+ async runProcess(request: ProcessRunRequest): Promise {
+ return this.requestJson("POST", `${API_PREFIX}/processes/run`, {
+ body: request,
+ });
+ }
+
+ async listProcesses(): Promise {
+ return this.requestJson("GET", `${API_PREFIX}/processes`);
+ }
+
+ async getProcess(id: string): Promise {
+ return this.requestJson("GET", `${API_PREFIX}/processes/${encodeURIComponent(id)}`);
+ }
+
+ async stopProcess(id: string, query?: ProcessSignalQuery): Promise {
+ return this.requestJson("POST", `${API_PREFIX}/processes/${encodeURIComponent(id)}/stop`, {
+ query,
+ });
+ }
+
+ async killProcess(id: string, query?: ProcessSignalQuery): Promise {
+ return this.requestJson("POST", `${API_PREFIX}/processes/${encodeURIComponent(id)}/kill`, {
+ query,
+ });
+ }
+
+ async deleteProcess(id: string): Promise {
+ await this.requestRaw("DELETE", `${API_PREFIX}/processes/${encodeURIComponent(id)}`);
+ }
+
+ async getProcessLogs(id: string, query: ProcessLogFollowQuery = {}): Promise {
+ return this.requestJson("GET", `${API_PREFIX}/processes/${encodeURIComponent(id)}/logs`, {
+ query,
+ });
+ }
+
+ async followProcessLogs(
+ id: string,
+ listener: ProcessLogListener,
+ query: ProcessLogFollowQuery = {},
+ ): Promise {
+ const abortController = new AbortController();
+ const response = await this.requestRaw(
+ "GET",
+ `${API_PREFIX}/processes/${encodeURIComponent(id)}/logs`,
+ {
+ query: { ...query, follow: true },
+ accept: "text/event-stream",
+ signal: abortController.signal,
+ },
+ );
+
+ if (!response.body) {
+ abortController.abort();
+ throw new Error("SSE stream is not readable in this environment.");
+ }
+
+ const closed = consumeProcessLogSse(response.body, listener, abortController.signal);
+
+ return {
+ close: () => abortController.abort(),
+ closed,
+ };
+ }
+
+ async sendProcessInput(id: string, request: ProcessInputRequest): Promise {
+ return this.requestJson("POST", `${API_PREFIX}/processes/${encodeURIComponent(id)}/input`, {
+ body: request,
+ });
+ }
+
+ async resizeProcessTerminal(
+ id: string,
+ request: ProcessTerminalResizeRequest,
+ ): Promise {
+ return this.requestJson(
+ "POST",
+ `${API_PREFIX}/processes/${encodeURIComponent(id)}/terminal/resize`,
+ {
+ body: request,
+ },
+ );
+ }
+
+ buildProcessTerminalWebSocketUrl(
+ id: string,
+ options: ProcessTerminalWebSocketUrlOptions = {},
+ ): string {
+ return toWebSocketUrl(
+ this.buildUrl(`${API_PREFIX}/processes/${encodeURIComponent(id)}/terminal/ws`, {
+ access_token: options.accessToken ?? this.token,
+ }),
+ );
+ }
+
+ connectProcessTerminalWebSocket(
+ id: string,
+ options: ProcessTerminalConnectOptions = {},
+ ): WebSocket {
+ const WebSocketCtor = options.WebSocket ?? globalThis.WebSocket;
+ if (!WebSocketCtor) {
+ throw new Error("WebSocket API is not available; provide a WebSocket implementation.");
+ }
+
+ return new WebSocketCtor(
+ this.buildProcessTerminalWebSocketUrl(id, {
+ accessToken: options.accessToken,
+ }),
+ options.protocols,
+ );
+ }
+
private async getLiveConnection(agent: string): Promise {
const existing = this.liveConnections.get(agent);
if (existing) {
@@ -1068,6 +1231,17 @@ async function autoAuthenticate(acp: AcpHttpClient, methods: AuthMethod[]): Prom
}
}
+function toAgentQuery(options: AgentQueryOptions | undefined): Record | undefined {
+ if (!options) {
+ return undefined;
+ }
+
+ return {
+ config: options.config,
+ no_cache: options.noCache,
+ };
+}
+
function normalizeSessionInit(
value: Omit | undefined,
): Omit {
@@ -1230,3 +1404,93 @@ async function readProblem(response: Response): Promise,
+ listener: ProcessLogListener,
+ signal: AbortSignal,
+): Promise {
+ const reader = body.getReader();
+ const decoder = new TextDecoder();
+ let buffer = "";
+
+ try {
+ while (!signal.aborted) {
+ const { done, value } = await reader.read();
+ if (done) {
+ return;
+ }
+
+ buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
+
+ let separatorIndex = buffer.indexOf("\n\n");
+ while (separatorIndex !== -1) {
+ const chunk = buffer.slice(0, separatorIndex);
+ buffer = buffer.slice(separatorIndex + 2);
+
+ const entry = parseProcessLogSseChunk(chunk);
+ if (entry) {
+ listener(entry);
+ }
+
+ separatorIndex = buffer.indexOf("\n\n");
+ }
+ }
+ } catch (error) {
+ if (signal.aborted || isAbortError(error)) {
+ return;
+ }
+ throw error;
+ } finally {
+ reader.releaseLock();
+ }
+}
+
+function parseProcessLogSseChunk(chunk: string): ProcessLogEntry | null {
+ if (!chunk.trim()) {
+ return null;
+ }
+
+ let eventName = "message";
+ const dataLines: string[] = [];
+
+ for (const line of chunk.split("\n")) {
+ if (!line || line.startsWith(":")) {
+ continue;
+ }
+
+ if (line.startsWith("event:")) {
+ eventName = line.slice(6).trim();
+ continue;
+ }
+
+ if (line.startsWith("data:")) {
+ dataLines.push(line.slice(5).trimStart());
+ }
+ }
+
+ if (eventName !== "log") {
+ return null;
+ }
+
+ const data = dataLines.join("\n");
+ if (!data.trim()) {
+ return null;
+ }
+
+ return JSON.parse(data) as ProcessLogEntry;
+}
+
+function toWebSocketUrl(url: string): string {
+ const parsed = new URL(url);
+ if (parsed.protocol === "http:") {
+ parsed.protocol = "ws:";
+ } else if (parsed.protocol === "https:") {
+ parsed.protocol = "wss:";
+ }
+ return parsed.toString();
+}
+
+function isAbortError(error: unknown): boolean {
+ return error instanceof Error && error.name === "AbortError";
+}
diff --git a/sdks/typescript/src/generated/openapi.ts b/sdks/typescript/src/generated/openapi.ts
index 91ab56b..a89d796 100644
--- a/sdks/typescript/src/generated/openapi.ts
+++ b/sdks/typescript/src/generated/openapi.ts
@@ -57,6 +57,39 @@ export interface paths {
"/v1/health": {
get: operations["get_v1_health"];
};
+ "/v1/processes": {
+ get: operations["get_v1_processes"];
+ post: operations["post_v1_processes"];
+ };
+ "/v1/processes/config": {
+ get: operations["get_v1_processes_config"];
+ post: operations["post_v1_processes_config"];
+ };
+ "/v1/processes/run": {
+ post: operations["post_v1_processes_run"];
+ };
+ "/v1/processes/{id}": {
+ get: operations["get_v1_process"];
+ delete: operations["delete_v1_process"];
+ };
+ "/v1/processes/{id}/input": {
+ post: operations["post_v1_process_input"];
+ };
+ "/v1/processes/{id}/kill": {
+ post: operations["post_v1_process_kill"];
+ };
+ "/v1/processes/{id}/logs": {
+ get: operations["get_v1_process_logs"];
+ };
+ "/v1/processes/{id}/stop": {
+ post: operations["post_v1_process_stop"];
+ };
+ "/v1/processes/{id}/terminal/resize": {
+ post: operations["post_v1_process_terminal_resize"];
+ };
+ "/v1/processes/{id}/terminal/ws": {
+ get: operations["get_v1_process_terminal_ws"];
+ };
}
export type webhooks = Record;
@@ -230,6 +263,116 @@ export interface components {
type: string;
[key: string]: unknown;
};
+ ProcessConfig: {
+ /** Format: int64 */
+ defaultRunTimeoutMs: number;
+ maxConcurrentProcesses: number;
+ maxInputBytesPerRequest: number;
+ maxLogBytesPerProcess: number;
+ maxOutputBytes: number;
+ /** Format: int64 */
+ maxRunTimeoutMs: number;
+ };
+ ProcessCreateRequest: {
+ args?: string[];
+ command: string;
+ cwd?: string | null;
+ env?: {
+ [key: string]: string;
+ };
+ interactive?: boolean;
+ tty?: boolean;
+ };
+ ProcessInfo: {
+ args: string[];
+ command: string;
+ /** Format: int64 */
+ createdAtMs: number;
+ cwd?: string | null;
+ /** Format: int32 */
+ exitCode?: number | null;
+ /** Format: int64 */
+ exitedAtMs?: number | null;
+ id: string;
+ interactive: boolean;
+ /** Format: int32 */
+ pid?: number | null;
+ status: components["schemas"]["ProcessState"];
+ tty: boolean;
+ };
+ ProcessInputRequest: {
+ data: string;
+ encoding?: string | null;
+ };
+ ProcessInputResponse: {
+ bytesWritten: number;
+ };
+ ProcessListResponse: {
+ processes: components["schemas"]["ProcessInfo"][];
+ };
+ ProcessLogEntry: {
+ data: string;
+ encoding: string;
+ /** Format: int64 */
+ sequence: number;
+ stream: components["schemas"]["ProcessLogsStream"];
+ /** Format: int64 */
+ timestampMs: number;
+ };
+ ProcessLogsQuery: {
+ follow?: boolean | null;
+ /** Format: int64 */
+ since?: number | null;
+ stream?: components["schemas"]["ProcessLogsStream"] | null;
+ tail?: number | null;
+ };
+ ProcessLogsResponse: {
+ entries: components["schemas"]["ProcessLogEntry"][];
+ processId: string;
+ stream: components["schemas"]["ProcessLogsStream"];
+ };
+ /** @enum {string} */
+ ProcessLogsStream: "stdout" | "stderr" | "combined" | "pty";
+ ProcessRunRequest: {
+ args?: string[];
+ command: string;
+ cwd?: string | null;
+ env?: {
+ [key: string]: string;
+ };
+ maxOutputBytes?: number | null;
+ /** Format: int64 */
+ timeoutMs?: number | null;
+ };
+ ProcessRunResponse: {
+ /** Format: int64 */
+ durationMs: number;
+ /** Format: int32 */
+ exitCode?: number | null;
+ stderr: string;
+ stderrTruncated: boolean;
+ stdout: string;
+ stdoutTruncated: boolean;
+ timedOut: boolean;
+ };
+ ProcessSignalQuery: {
+ /** Format: int64 */
+ waitMs?: number | null;
+ };
+ /** @enum {string} */
+ ProcessState: "running" | "exited";
+ ProcessTerminalResizeRequest: {
+ /** Format: int32 */
+ cols: number;
+ /** Format: int32 */
+ rows: number;
+ };
+ ProcessTerminalResizeResponse: {
+ /** Format: int32 */
+ cols: number;
+ /** Format: int32 */
+ rows: number;
+ };
/** @enum {string} */
ServerStatus: "running" | "stopped";
ServerStatusInfo: {
@@ -748,4 +891,417 @@ export interface operations {
};
};
};
+ get_v1_processes: {
+ responses: {
+ /** @description List processes */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessListResponse"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ post_v1_processes: {
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ProcessCreateRequest"];
+ };
+ };
+ responses: {
+ /** @description Started process */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessInfo"];
+ };
+ };
+ /** @description Invalid request */
+ 400: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process limit or state conflict */
+ 409: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ get_v1_processes_config: {
+ responses: {
+ /** @description Current runtime process config */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessConfig"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ post_v1_processes_config: {
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ProcessConfig"];
+ };
+ };
+ responses: {
+ /** @description Updated runtime process config */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessConfig"];
+ };
+ };
+ /** @description Invalid config */
+ 400: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ post_v1_processes_run: {
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ProcessRunRequest"];
+ };
+ };
+ responses: {
+ /** @description One-off command result */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessRunResponse"];
+ };
+ };
+ /** @description Invalid request */
+ 400: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ get_v1_process: {
+ parameters: {
+ path: {
+ /** @description Process ID */
+ id: string;
+ };
+ };
+ responses: {
+ /** @description Process details */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessInfo"];
+ };
+ };
+ /** @description Unknown process */
+ 404: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ delete_v1_process: {
+ parameters: {
+ path: {
+ /** @description Process ID */
+ id: string;
+ };
+ };
+ responses: {
+ /** @description Process deleted */
+ 204: {
+ content: never;
+ };
+ /** @description Unknown process */
+ 404: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process is still running */
+ 409: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ post_v1_process_input: {
+ parameters: {
+ path: {
+ /** @description Process ID */
+ id: string;
+ };
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ProcessInputRequest"];
+ };
+ };
+ responses: {
+ /** @description Input accepted */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessInputResponse"];
+ };
+ };
+ /** @description Invalid request */
+ 400: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process not writable */
+ 409: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Input exceeds configured limit */
+ 413: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ post_v1_process_kill: {
+ parameters: {
+ query?: {
+ /** @description Wait up to N ms for process to exit */
+ waitMs?: number | null;
+ };
+ path: {
+ /** @description Process ID */
+ id: string;
+ };
+ };
+ responses: {
+ /** @description Kill signal sent */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessInfo"];
+ };
+ };
+ /** @description Unknown process */
+ 404: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ get_v1_process_logs: {
+ parameters: {
+ query?: {
+ /** @description stdout|stderr|combined|pty */
+ stream?: components["schemas"]["ProcessLogsStream"] | null;
+ /** @description Tail N entries */
+ tail?: number | null;
+ /** @description Follow via SSE */
+ follow?: boolean | null;
+ /** @description Only entries with sequence greater than this */
+ since?: number | null;
+ };
+ path: {
+ /** @description Process ID */
+ id: string;
+ };
+ };
+ responses: {
+ /** @description Process logs */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessLogsResponse"];
+ };
+ };
+ /** @description Unknown process */
+ 404: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ post_v1_process_stop: {
+ parameters: {
+ query?: {
+ /** @description Wait up to N ms for process to exit */
+ waitMs?: number | null;
+ };
+ path: {
+ /** @description Process ID */
+ id: string;
+ };
+ };
+ responses: {
+ /** @description Stop signal sent */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessInfo"];
+ };
+ };
+ /** @description Unknown process */
+ 404: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ post_v1_process_terminal_resize: {
+ parameters: {
+ path: {
+ /** @description Process ID */
+ id: string;
+ };
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ProcessTerminalResizeRequest"];
+ };
+ };
+ responses: {
+ /** @description Resize accepted */
+ 200: {
+ content: {
+ "application/json": components["schemas"]["ProcessTerminalResizeResponse"];
+ };
+ };
+ /** @description Invalid request */
+ 400: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Unknown process */
+ 404: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Not a terminal process */
+ 409: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
+ get_v1_process_terminal_ws: {
+ parameters: {
+ query?: {
+ /** @description Bearer token alternative for WS auth */
+ access_token?: string | null;
+ };
+ path: {
+ /** @description Process ID */
+ id: string;
+ };
+ };
+ responses: {
+ /** @description WebSocket upgraded */
+ 101: {
+ content: never;
+ };
+ /** @description Invalid websocket frame or upgrade request */
+ 400: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Unknown process */
+ 404: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Not a terminal process */
+ 409: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description Process API unsupported on this platform */
+ 501: {
+ content: {
+ "application/json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
}
diff --git a/sdks/typescript/src/index.ts b/sdks/typescript/src/index.ts
index cb7d8cf..82b5791 100644
--- a/sdks/typescript/src/index.ts
+++ b/sdks/typescript/src/index.ts
@@ -10,6 +10,12 @@ export { AcpRpcError } from "acp-http-client";
export { buildInspectorUrl } from "./inspector.ts";
export type {
+ AgentQueryOptions,
+ ProcessLogFollowQuery,
+ ProcessLogListener,
+ ProcessLogSubscription,
+ ProcessTerminalConnectOptions,
+ ProcessTerminalWebSocketUrlOptions,
SandboxAgentConnectOptions,
SandboxAgentStartOptions,
SessionCreateRequest,
@@ -29,6 +35,7 @@ export type {
AcpServerInfo,
AcpServerListResponse,
AgentInfo,
+ AgentQuery,
AgentInstallRequest,
AgentInstallResponse,
AgentListResponse,
@@ -51,6 +58,27 @@ export type {
McpConfigQuery,
McpServerConfig,
ProblemDetails,
+ ProcessConfig,
+ ProcessCreateRequest,
+ ProcessInfo,
+ ProcessInputRequest,
+ ProcessInputResponse,
+ ProcessListResponse,
+ ProcessLogEntry,
+ ProcessLogsQuery,
+ ProcessLogsResponse,
+ ProcessLogsStream,
+ ProcessRunRequest,
+ ProcessRunResponse,
+ ProcessSignalQuery,
+ ProcessState,
+ ProcessTerminalClientFrame,
+ ProcessTerminalErrorFrame,
+ ProcessTerminalExitFrame,
+ ProcessTerminalReadyFrame,
+ ProcessTerminalResizeRequest,
+ ProcessTerminalResizeResponse,
+ ProcessTerminalServerFrame,
SessionEvent,
SessionPersistDriver,
SessionRecord,
diff --git a/sdks/typescript/src/types.ts b/sdks/typescript/src/types.ts
index 17b321e..aa7a73a 100644
--- a/sdks/typescript/src/types.ts
+++ b/sdks/typescript/src/types.ts
@@ -6,6 +6,7 @@ export type ProblemDetails = components["schemas"]["ProblemDetails"];
export type HealthResponse = JsonResponse;
export type AgentListResponse = JsonResponse;
export type AgentInfo = components["schemas"]["AgentInfo"];
+export type AgentQuery = QueryParams;
export type AgentInstallRequest = JsonRequestBody;
export type AgentInstallResponse = JsonResponse;
@@ -31,6 +32,58 @@ export type McpServerConfig = components["schemas"]["McpServerConfig"];
export type SkillsConfigQuery = QueryParams;
export type SkillsConfig = components["schemas"]["SkillsConfig"];
+export type ProcessConfig = JsonResponse;
+export type ProcessCreateRequest = JsonRequestBody;
+export type ProcessInfo = components["schemas"]["ProcessInfo"];
+export type ProcessInputRequest = JsonRequestBody;
+export type ProcessInputResponse = JsonResponse;
+export type ProcessListResponse = JsonResponse;
+export type ProcessLogEntry = components["schemas"]["ProcessLogEntry"];
+export type ProcessLogsQuery = QueryParams;
+export type ProcessLogsResponse = JsonResponse;
+export type ProcessLogsStream = components["schemas"]["ProcessLogsStream"];
+export type ProcessRunRequest = JsonRequestBody;
+export type ProcessRunResponse = JsonResponse;
+export type ProcessSignalQuery = QueryParams;
+export type ProcessState = components["schemas"]["ProcessState"];
+export type ProcessTerminalResizeRequest = JsonRequestBody;
+export type ProcessTerminalResizeResponse = JsonResponse;
+
+export type ProcessTerminalClientFrame =
+ | {
+ type: "input";
+ data: string;
+ encoding?: string;
+ }
+ | {
+ type: "resize";
+ cols: number;
+ rows: number;
+ }
+ | {
+ type: "close";
+ };
+
+export interface ProcessTerminalReadyFrame {
+ type: "ready";
+ processId: string;
+}
+
+export interface ProcessTerminalExitFrame {
+ type: "exit";
+ exitCode?: number | null;
+}
+
+export interface ProcessTerminalErrorFrame {
+ type: "error";
+ message: string;
+}
+
+export type ProcessTerminalServerFrame =
+ | ProcessTerminalReadyFrame
+ | ProcessTerminalExitFrame
+ | ProcessTerminalErrorFrame;
+
export interface SessionRecord {
id: string;
agent: string;
diff --git a/sdks/typescript/tests/integration.test.ts b/sdks/typescript/tests/integration.test.ts
index 84b0d1a..f210bdb 100644
--- a/sdks/typescript/tests/integration.test.ts
+++ b/sdks/typescript/tests/integration.test.ts
@@ -12,6 +12,7 @@ import {
} from "../src/index.ts";
import { spawnSandboxAgent, isNodeRuntime, type SandboxAgentSpawnHandle } from "../src/spawn.ts";
import { prepareMockAgentDataHome } from "./helpers/mock-agent.ts";
+import WebSocket from "ws";
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -64,6 +65,107 @@ async function waitFor(
throw new Error("timed out waiting for condition");
}
+async function waitForAsync(
+ fn: () => Promise,
+ timeoutMs = 6000,
+ stepMs = 30,
+): Promise {
+ const started = Date.now();
+ while (Date.now() - started < timeoutMs) {
+ const value = await fn();
+ if (value !== undefined && value !== null) {
+ return value;
+ }
+ await sleep(stepMs);
+ }
+ throw new Error("timed out waiting for condition");
+}
+
+function buildTarArchive(entries: Array<{ name: string; content: string }>): Uint8Array {
+ const blocks: Buffer[] = [];
+
+ for (const entry of entries) {
+ const content = Buffer.from(entry.content, "utf8");
+ const header = Buffer.alloc(512, 0);
+
+ writeTarString(header, 0, 100, entry.name);
+ writeTarOctal(header, 100, 8, 0o644);
+ writeTarOctal(header, 108, 8, 0);
+ writeTarOctal(header, 116, 8, 0);
+ writeTarOctal(header, 124, 12, content.length);
+ writeTarOctal(header, 136, 12, Math.floor(Date.now() / 1000));
+ header.fill(0x20, 148, 156);
+ header[156] = "0".charCodeAt(0);
+ writeTarString(header, 257, 6, "ustar");
+ writeTarString(header, 263, 2, "00");
+
+ let checksum = 0;
+ for (const byte of header) {
+ checksum += byte;
+ }
+ writeTarChecksum(header, checksum);
+
+ blocks.push(header);
+ blocks.push(content);
+
+ const remainder = content.length % 512;
+ if (remainder !== 0) {
+ blocks.push(Buffer.alloc(512 - remainder, 0));
+ }
+ }
+
+ blocks.push(Buffer.alloc(1024, 0));
+ return Buffer.concat(blocks);
+}
+
+function writeTarString(buffer: Buffer, offset: number, length: number, value: string): void {
+ const bytes = Buffer.from(value, "utf8");
+ bytes.copy(buffer, offset, 0, Math.min(bytes.length, length));
+}
+
+function writeTarOctal(buffer: Buffer, offset: number, length: number, value: number): void {
+ const rendered = value.toString(8).padStart(length - 1, "0");
+ writeTarString(buffer, offset, length, rendered);
+ buffer[offset + length - 1] = 0;
+}
+
+function writeTarChecksum(buffer: Buffer, checksum: number): void {
+ const rendered = checksum.toString(8).padStart(6, "0");
+ writeTarString(buffer, 148, 6, rendered);
+ buffer[154] = 0;
+ buffer[155] = 0x20;
+}
+
+function decodeSocketPayload(data: unknown): string {
+ if (typeof data === "string") {
+ return data;
+ }
+ if (data instanceof ArrayBuffer) {
+ return Buffer.from(data).toString("utf8");
+ }
+ if (ArrayBuffer.isView(data)) {
+ return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString("utf8");
+ }
+ if (typeof Blob !== "undefined" && data instanceof Blob) {
+ throw new Error("Blob socket payloads are not supported in this test");
+ }
+ throw new Error(`Unsupported socket payload type: ${typeof data}`);
+}
+
+function decodeProcessLogData(data: string, encoding: string): string {
+ if (encoding === "base64") {
+ return Buffer.from(data, "base64").toString("utf8");
+ }
+ return data;
+}
+
+function nodeCommand(source: string): { command: string; args: string[] } {
+ return {
+ command: process.execPath,
+ args: ["-e", source],
+ };
+}
+
describe("Integration: TypeScript SDK flat session API", () => {
let handle: SandboxAgentSpawnHandle;
let baseUrl: string;
@@ -122,6 +224,9 @@ describe("Integration: TypeScript SDK flat session API", () => {
const fetched = await sdk.getSession(session.id);
expect(fetched?.agent).toBe("mock");
+ const acpServers = await sdk.listAcpServers();
+ expect(acpServers.servers.some((server) => server.agent === "mock")).toBe(true);
+
const events = await sdk.getEvents({ sessionId: session.id, limit: 100 });
expect(events.items.length).toBeGreaterThan(0);
expect(events.items.some((event) => event.sender === "client")).toBe(true);
@@ -137,6 +242,64 @@ describe("Integration: TypeScript SDK flat session API", () => {
await sdk.dispose();
});
+ it("covers agent query flags and filesystem HTTP helpers", async () => {
+ const sdk = await SandboxAgent.connect({
+ baseUrl,
+ token,
+ });
+
+ const directory = mkdtempSync(join(tmpdir(), "sdk-fs-"));
+ const nestedDir = join(directory, "nested");
+ const filePath = join(directory, "notes.txt");
+ const movedPath = join(directory, "notes-moved.txt");
+ const uploadDir = join(directory, "uploaded");
+
+ try {
+ const listedAgents = await sdk.listAgents({ config: true, noCache: true });
+ expect(listedAgents.agents.some((agent) => agent.id === "mock")).toBe(true);
+
+ const mockAgent = await sdk.getAgent("mock", { config: true, noCache: true });
+ expect(mockAgent.id).toBe("mock");
+ expect(Array.isArray(mockAgent.configOptions)).toBe(true);
+
+ await sdk.mkdirFs({ path: nestedDir });
+ await sdk.writeFsFile({ path: filePath }, "hello from sdk");
+
+ const bytes = await sdk.readFsFile({ path: filePath });
+ expect(new TextDecoder().decode(bytes)).toBe("hello from sdk");
+
+ const stat = await sdk.statFs({ path: filePath });
+ expect(stat.path).toBe(filePath);
+ expect(stat.size).toBe(bytes.byteLength);
+
+ const entries = await sdk.listFsEntries({ path: directory });
+ expect(entries.some((entry) => entry.path === nestedDir)).toBe(true);
+ expect(entries.some((entry) => entry.path === filePath)).toBe(true);
+
+ const moved = await sdk.moveFs({
+ from: filePath,
+ to: movedPath,
+ overwrite: true,
+ });
+ expect(moved.to).toBe(movedPath);
+
+ const uploadResult = await sdk.uploadFsBatch(
+ buildTarArchive([{ name: "batch.txt", content: "batch upload works" }]),
+ { path: uploadDir },
+ );
+ expect(uploadResult.paths.some((path) => path.endsWith("batch.txt"))).toBe(true);
+
+ const uploaded = await sdk.readFsFile({ path: join(uploadDir, "batch.txt") });
+ expect(new TextDecoder().decode(uploaded)).toBe("batch upload works");
+
+ const deleted = await sdk.deleteFsEntry({ path: movedPath });
+ expect(deleted.path).toBe(movedPath);
+ } finally {
+ rmSync(directory, { recursive: true, force: true });
+ await sdk.dispose();
+ }
+ });
+
it("uses custom fetch for both HTTP helpers and ACP session traffic", async () => {
const defaultFetch = globalThis.fetch;
if (!defaultFetch) {
@@ -168,7 +331,7 @@ describe("Integration: TypeScript SDK flat session API", () => {
expect(seenPaths.some((path) => path.startsWith("/v1/acp/"))).toBe(true);
await sdk.dispose();
- });
+ }, 60_000);
it("requires baseUrl when fetch is not provided", async () => {
await expect(SandboxAgent.connect({ token } as any)).rejects.toThrow(
@@ -320,4 +483,186 @@ describe("Integration: TypeScript SDK flat session API", () => {
await sdk.dispose();
rmSync(directory, { recursive: true, force: true });
});
+
+ it("covers process runtime HTTP helpers, log streaming, and terminal websocket access", async () => {
+ const sdk = await SandboxAgent.connect({
+ baseUrl,
+ token,
+ });
+
+ const originalConfig = await sdk.getProcessConfig();
+ const updatedConfig = await sdk.setProcessConfig({
+ ...originalConfig,
+ maxOutputBytes: originalConfig.maxOutputBytes + 1,
+ });
+ expect(updatedConfig.maxOutputBytes).toBe(originalConfig.maxOutputBytes + 1);
+
+ const runResult = await sdk.runProcess({
+ ...nodeCommand("process.stdout.write('run-stdout'); process.stderr.write('run-stderr');"),
+ timeoutMs: 5_000,
+ });
+ expect(runResult.stdout).toContain("run-stdout");
+ expect(runResult.stderr).toContain("run-stderr");
+
+ let interactiveProcessId: string | undefined;
+ let ttyProcessId: string | undefined;
+ let killProcessId: string | undefined;
+
+ try {
+ const interactiveProcess = await sdk.createProcess({
+ ...nodeCommand(`
+ process.stdin.setEncoding("utf8");
+ process.stdout.write("ready\\n");
+ process.stdin.on("data", (chunk) => {
+ process.stdout.write("echo:" + chunk);
+ });
+ setInterval(() => {}, 1_000);
+ `),
+ interactive: true,
+ });
+ interactiveProcessId = interactiveProcess.id;
+
+ const listed = await sdk.listProcesses();
+ expect(listed.processes.some((process) => process.id === interactiveProcess.id)).toBe(true);
+
+ const fetched = await sdk.getProcess(interactiveProcess.id);
+ expect(fetched.status).toBe("running");
+
+ const initialLogs = await waitForAsync(async () => {
+ const logs = await sdk.getProcessLogs(interactiveProcess.id, { tail: 10 });
+ return logs.entries.some((entry) => decodeProcessLogData(entry.data, entry.encoding).includes("ready"))
+ ? logs
+ : undefined;
+ });
+ expect(
+ initialLogs.entries.some((entry) => decodeProcessLogData(entry.data, entry.encoding).includes("ready")),
+ ).toBe(true);
+
+ const followedLogs: string[] = [];
+ const subscription = await sdk.followProcessLogs(
+ interactiveProcess.id,
+ (entry) => {
+ followedLogs.push(decodeProcessLogData(entry.data, entry.encoding));
+ },
+ { tail: 1 },
+ );
+
+ try {
+ const inputResult = await sdk.sendProcessInput(interactiveProcess.id, {
+ data: Buffer.from("hello over stdin\n", "utf8").toString("base64"),
+ encoding: "base64",
+ });
+ expect(inputResult.bytesWritten).toBeGreaterThan(0);
+
+ await waitFor(() => {
+ const joined = followedLogs.join("");
+ return joined.includes("echo:hello over stdin") ? joined : undefined;
+ });
+ } finally {
+ subscription.close();
+ await subscription.closed;
+ }
+
+ const stopped = await sdk.stopProcess(interactiveProcess.id, { waitMs: 5_000 });
+ expect(stopped.status).toBe("exited");
+
+ await sdk.deleteProcess(interactiveProcess.id);
+ interactiveProcessId = undefined;
+
+ const ttyProcess = await sdk.createProcess({
+ ...nodeCommand(`
+ process.stdin.setEncoding("utf8");
+ process.stdin.on("data", (chunk) => {
+ process.stdout.write(chunk);
+ });
+ setInterval(() => {}, 1_000);
+ `),
+ interactive: true,
+ tty: true,
+ });
+ ttyProcessId = ttyProcess.id;
+
+ const resized = await sdk.resizeProcessTerminal(ttyProcess.id, {
+ cols: 120,
+ rows: 40,
+ });
+ expect(resized.cols).toBe(120);
+ expect(resized.rows).toBe(40);
+
+ const wsUrl = sdk.buildProcessTerminalWebSocketUrl(ttyProcess.id);
+ expect(wsUrl.startsWith("ws://") || wsUrl.startsWith("wss://")).toBe(true);
+
+ const ws = sdk.connectProcessTerminalWebSocket(ttyProcess.id, {
+ WebSocket: WebSocket as unknown as typeof globalThis.WebSocket,
+ });
+ ws.binaryType = "arraybuffer";
+
+ const socketTextFrames: string[] = [];
+ const socketBinaryFrames: string[] = [];
+ ws.addEventListener("message", (event) => {
+ if (typeof event.data === "string") {
+ socketTextFrames.push(event.data);
+ return;
+ }
+ socketBinaryFrames.push(decodeSocketPayload(event.data));
+ });
+
+ await waitFor(() => {
+ const ready = socketTextFrames.find((frame) => frame.includes('"type":"ready"'));
+ return ready;
+ });
+
+ ws.send(JSON.stringify({
+ type: "input",
+ data: "hello tty\n",
+ }));
+
+ await waitFor(() => {
+ const joined = socketBinaryFrames.join("");
+ return joined.includes("hello tty") ? joined : undefined;
+ });
+
+ ws.close();
+ await waitForAsync(async () => {
+ const processInfo = await sdk.getProcess(ttyProcess.id);
+ return processInfo.status === "running" ? processInfo : undefined;
+ });
+
+ const killedTty = await sdk.killProcess(ttyProcess.id, { waitMs: 5_000 });
+ expect(killedTty.status).toBe("exited");
+
+ await sdk.deleteProcess(ttyProcess.id);
+ ttyProcessId = undefined;
+
+ const killProcess = await sdk.createProcess({
+ ...nodeCommand("setInterval(() => {}, 1_000);"),
+ });
+ killProcessId = killProcess.id;
+
+ const killed = await sdk.killProcess(killProcess.id, { waitMs: 5_000 });
+ expect(killed.status).toBe("exited");
+
+ await sdk.deleteProcess(killProcess.id);
+ killProcessId = undefined;
+ } finally {
+ await sdk.setProcessConfig(originalConfig);
+
+ if (interactiveProcessId) {
+ await sdk.killProcess(interactiveProcessId, { waitMs: 5_000 }).catch(() => {});
+ await sdk.deleteProcess(interactiveProcessId).catch(() => {});
+ }
+
+ if (ttyProcessId) {
+ await sdk.killProcess(ttyProcessId, { waitMs: 5_000 }).catch(() => {});
+ await sdk.deleteProcess(ttyProcessId).catch(() => {});
+ }
+
+ if (killProcessId) {
+ await sdk.killProcess(killProcessId, { waitMs: 5_000 }).catch(() => {});
+ await sdk.deleteProcess(killProcessId).catch(() => {});
+ }
+
+ await sdk.dispose();
+ }
+ });
});
diff --git a/server/CLAUDE.md b/server/CLAUDE.md
index 2c217d7..b56223c 100644
--- a/server/CLAUDE.md
+++ b/server/CLAUDE.md
@@ -1,17 +1,17 @@
# Server Instructions
-## ACP v2 Architecture
+## Architecture
- Public API routes are defined in `server/packages/sandbox-agent/src/router.rs`.
-- ACP runtime/process bridge is in `server/packages/sandbox-agent/src/acp_runtime.rs`.
-- `/v2` is the only active API surface for sessions/prompts (`/v2/rpc`).
+- ACP proxy runtime is in `server/packages/sandbox-agent/src/acp_proxy_runtime.rs`.
+- All API endpoints are under `/v1`.
- Keep binary filesystem transfer endpoints as dedicated HTTP APIs:
- - `GET /v2/fs/file`
- - `PUT /v2/fs/file`
- - `POST /v2/fs/upload-batch`
+ - `GET /v1/fs/file`
+ - `PUT /v1/fs/file`
+ - `POST /v1/fs/upload-batch`
- Rationale: host-owned cross-agent-consistent behavior and large binary transfer needs that ACP JSON-RPC is not suited to stream efficiently.
- Maintain ACP variants in parallel only when they share the same underlying filesystem implementation; SDK defaults should still prefer HTTP for large/binary transfers.
-- `/v1/*` must remain hard-removed (`410`) and `/opencode/*` stays disabled (`503`) until Phase 7.
+- `/opencode/*` stays disabled (`503`) until Phase 7.
- Agent install logic (native + ACP agent process + lazy install) is handled by `server/packages/agent-management/`.
## API Contract Rules
@@ -23,14 +23,14 @@
## Tests
-Primary v2 integration coverage:
-- `server/packages/sandbox-agent/tests/v2_api.rs`
-- `server/packages/sandbox-agent/tests/v2_agent_process_matrix.rs`
+Primary v1 integration coverage:
+- `server/packages/sandbox-agent/tests/v1_api.rs`
+- `server/packages/sandbox-agent/tests/v1_agent_process_matrix.rs`
Run:
```bash
-cargo test -p sandbox-agent --test v2_api
-cargo test -p sandbox-agent --test v2_agent_process_matrix
+cargo test -p sandbox-agent --test v1_api
+cargo test -p sandbox-agent --test v1_agent_process_matrix
```
## Migration Docs Sync
diff --git a/server/packages/error/src/lib.rs b/server/packages/error/src/lib.rs
index 9bc1734..d0e8c0c 100644
--- a/server/packages/error/src/lib.rs
+++ b/server/packages/error/src/lib.rs
@@ -17,6 +17,7 @@ pub enum ErrorType {
PermissionDenied,
NotAcceptable,
UnsupportedMediaType,
+ NotFound,
SessionNotFound,
SessionAlreadyExists,
ModeNotSupported,
@@ -37,6 +38,7 @@ impl ErrorType {
Self::PermissionDenied => "urn:sandbox-agent:error:permission_denied",
Self::NotAcceptable => "urn:sandbox-agent:error:not_acceptable",
Self::UnsupportedMediaType => "urn:sandbox-agent:error:unsupported_media_type",
+ Self::NotFound => "urn:sandbox-agent:error:not_found",
Self::SessionNotFound => "urn:sandbox-agent:error:session_not_found",
Self::SessionAlreadyExists => "urn:sandbox-agent:error:session_already_exists",
Self::ModeNotSupported => "urn:sandbox-agent:error:mode_not_supported",
@@ -57,6 +59,7 @@ impl ErrorType {
Self::PermissionDenied => "Permission Denied",
Self::NotAcceptable => "Not Acceptable",
Self::UnsupportedMediaType => "Unsupported Media Type",
+ Self::NotFound => "Not Found",
Self::SessionNotFound => "Session Not Found",
Self::SessionAlreadyExists => "Session Already Exists",
Self::ModeNotSupported => "Mode Not Supported",
@@ -77,6 +80,7 @@ impl ErrorType {
Self::PermissionDenied => 403,
Self::NotAcceptable => 406,
Self::UnsupportedMediaType => 415,
+ Self::NotFound => 404,
Self::SessionNotFound => 404,
Self::SessionAlreadyExists => 409,
Self::ModeNotSupported => 400,
@@ -155,6 +159,8 @@ pub enum SandboxError {
NotAcceptable { message: String },
#[error("unsupported media type: {message}")]
UnsupportedMediaType { message: String },
+ #[error("not found: {resource} {id}")]
+ NotFound { resource: String, id: String },
#[error("session not found: {session_id}")]
SessionNotFound { session_id: String },
#[error("session already exists: {session_id}")]
@@ -180,6 +186,7 @@ impl SandboxError {
Self::PermissionDenied { .. } => ErrorType::PermissionDenied,
Self::NotAcceptable { .. } => ErrorType::NotAcceptable,
Self::UnsupportedMediaType { .. } => ErrorType::UnsupportedMediaType,
+ Self::NotFound { .. } => ErrorType::NotFound,
Self::SessionNotFound { .. } => ErrorType::SessionNotFound,
Self::SessionAlreadyExists { .. } => ErrorType::SessionAlreadyExists,
Self::ModeNotSupported { .. } => ErrorType::ModeNotSupported,
@@ -264,6 +271,12 @@ impl SandboxError {
map.insert("message".to_string(), Value::String(message.clone()));
(None, None, Some(Value::Object(map)))
}
+ Self::NotFound { resource, id } => {
+ let mut map = Map::new();
+ map.insert("resource".to_string(), Value::String(resource.clone()));
+ map.insert("id".to_string(), Value::String(id.clone()));
+ (None, None, Some(Value::Object(map)))
+ }
Self::SessionNotFound { session_id } => (None, Some(session_id.clone()), None),
Self::SessionAlreadyExists { session_id } => (None, Some(session_id.clone()), None),
Self::ModeNotSupported { agent, mode } => {
diff --git a/server/packages/sandbox-agent/Cargo.toml b/server/packages/sandbox-agent/Cargo.toml
index bb8328c..a8ae1db 100644
--- a/server/packages/sandbox-agent/Cargo.toml
+++ b/server/packages/sandbox-agent/Cargo.toml
@@ -55,6 +55,7 @@ insta.workspace = true
tower.workspace = true
tempfile.workspace = true
serial_test = "3.2"
+tokio-tungstenite = "0.24"
[features]
test-utils = ["tempfile"]
diff --git a/server/packages/sandbox-agent/src/lib.rs b/server/packages/sandbox-agent/src/lib.rs
index b5031e1..e84b10b 100644
--- a/server/packages/sandbox-agent/src/lib.rs
+++ b/server/packages/sandbox-agent/src/lib.rs
@@ -3,6 +3,7 @@
mod acp_proxy_runtime;
pub mod cli;
pub mod daemon;
+mod process_runtime;
pub mod router;
pub mod server_logs;
pub mod telemetry;
diff --git a/server/packages/sandbox-agent/src/process_runtime.rs b/server/packages/sandbox-agent/src/process_runtime.rs
new file mode 100644
index 0000000..37f40fa
--- /dev/null
+++ b/server/packages/sandbox-agent/src/process_runtime.rs
@@ -0,0 +1,1086 @@
+use std::collections::{HashMap, VecDeque};
+use std::sync::atomic::{AtomicU64, Ordering};
+use std::sync::Arc;
+use std::time::Instant;
+
+use base64::engine::general_purpose::STANDARD as BASE64;
+use base64::Engine;
+use serde::{Deserialize, Serialize};
+use tokio::io::{AsyncRead, AsyncReadExt, AsyncWriteExt};
+use tokio::process::{Child, ChildStdin, Command};
+use tokio::sync::{broadcast, Mutex, RwLock};
+
+use sandbox_agent_error::SandboxError;
+
+#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "lowercase")]
+pub enum ProcessStatus {
+ Running,
+ Exited,
+}
+
+#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "lowercase")]
+pub enum ProcessStream {
+ Stdout,
+ Stderr,
+ Pty,
+}
+
+#[derive(Debug, Clone)]
+pub struct ProcessStartSpec {
+ pub command: String,
+ pub args: Vec,
+ pub cwd: Option,
+ pub env: HashMap,
+ pub tty: bool,
+ pub interactive: bool,
+}
+
+#[derive(Debug, Clone)]
+pub struct RunSpec {
+ pub command: String,
+ pub args: Vec,
+ pub cwd: Option,
+ pub env: HashMap,
+ pub timeout_ms: Option,
+ pub max_output_bytes: Option,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RunOutput {
+ pub exit_code: Option,
+ pub timed_out: bool,
+ pub stdout: String,
+ pub stderr: String,
+ pub stdout_truncated: bool,
+ pub stderr_truncated: bool,
+ pub duration_ms: u64,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ProcessLogLine {
+ pub sequence: u64,
+ pub stream: ProcessStream,
+ pub timestamp_ms: i64,
+ pub data: String,
+ pub encoding: &'static str,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ProcessSnapshot {
+ pub id: String,
+ pub command: String,
+ pub args: Vec,
+ pub cwd: Option,
+ pub tty: bool,
+ pub interactive: bool,
+ pub status: ProcessStatus,
+ pub pid: Option,
+ pub exit_code: Option,
+ pub created_at_ms: i64,
+ pub exited_at_ms: Option,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ProcessRuntimeConfig {
+ pub max_concurrent_processes: usize,
+ pub default_run_timeout_ms: u64,
+ pub max_run_timeout_ms: u64,
+ pub max_output_bytes: usize,
+ pub max_log_bytes_per_process: usize,
+ pub max_input_bytes_per_request: usize,
+}
+
+impl Default for ProcessRuntimeConfig {
+ fn default() -> Self {
+ Self {
+ max_concurrent_processes: 64,
+ default_run_timeout_ms: 30_000,
+ max_run_timeout_ms: 300_000,
+ max_output_bytes: 1_048_576,
+ max_log_bytes_per_process: 10_485_760,
+ max_input_bytes_per_request: 65_536,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct ProcessRuntime {
+ config: Arc>,
+ inner: Arc,
+}
+
+#[derive(Debug)]
+struct ProcessRuntimeInner {
+ next_id: AtomicU64,
+ processes: RwLock>>,
+}
+
+#[derive(Debug)]
+struct ManagedProcess {
+ id: String,
+ command: String,
+ args: Vec,
+ cwd: Option,
+ tty: bool,
+ interactive: bool,
+ created_at_ms: i64,
+ pid: Option,
+ max_log_bytes: usize,
+ stdin: Mutex