the universal sandbox runtime for agents and humans. pocketenv.io
sandbox openclaw agent claude-code vercel-sandbox deno-sandbox cloudflare-sandbox atproto sprites daytona
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add --ssh option and improve SSH TTY handling

Add a --ssh / -s flag to `start` to connect and open a shell
automatically.
Treat Ctrl+K (0x0b) as a local-only escape in raw TTY mode to teardown
immediately without forwarding to the remote. Treat an SSE close with no
error message as a graceful shell exit (exit code 0) rather than a
connection error. Bump CLI version to 0.2.5.

+30 -7
+1 -1
apps/cli/package.json
··· 4 4 "bin": { 5 5 "pocketenv": "dist/index.js" 6 6 }, 7 - "version": "0.2.4", 7 + "version": "0.2.5", 8 8 "type": "module", 9 9 "keywords": [ 10 10 "sandbox",
+21 -5
apps/cli/src/cmd/ssh/tty.ts
··· 129 129 process.stdin.resume(); 130 130 131 131 // stdin → POST /tty/:id/input 132 + // In raw mode the OS never raises SIGINT — Ctrl+C arrives as a raw byte 133 + // in the data stream and is forwarded to the remote shell as-is. 134 + // We use Ctrl+K (\x0b) as a local-only escape hatch to avoid conflicting 135 + // with Ctrl+C semantics inside the remote shell. 132 136 process.stdin.on("data", (chunk: Buffer) => { 137 + if (chunk.includes(0x0b)) { 138 + // Ctrl+K pressed — tear down immediately without waiting for the server. 139 + teardown(0); 140 + return; 141 + } 133 142 sendInput(ttyUrl, sandbox.id, chunk, authToken); 134 143 }); 135 144 ··· 195 204 es.onerror = (err: ErrorEvent) => { 196 205 // The eventsource package exposes readyState on the EventSource instance. 197 206 if (es && es.readyState === EventSource.CLOSED) { 198 - const detail = err.message ? ` (${err.message})` : ""; 199 - process.stderr.write( 200 - `\r\n${chalk.red(`Terminal connection lost${detail}`)}\r\n`, 201 - ); 202 - teardown(1); 207 + // If the shell exited cleanly the server will close the SSE stream with 208 + // no error message. Treat a message-less close as a graceful exit (code 209 + // 0) rather than a connection error, so the user isn't shown a red 210 + // "connection lost" banner after a normal `exit`. 211 + if (!err.message) { 212 + teardown(0); 213 + } else { 214 + process.stderr.write( 215 + `\r\n${chalk.red(`Terminal connection lost (${err.message})`)}\r\n`, 216 + ); 217 + teardown(1); 218 + } 203 219 } 204 220 }; 205 221
+7 -1
apps/cli/src/cmd/start.ts
··· 3 3 import getAccessToken from "../lib/getAccessToken"; 4 4 import { client } from "../client"; 5 5 import { env } from "../lib/env"; 6 + import connectToSandbox from "./ssh"; 6 7 7 - async function start(name: string) { 8 + async function start(name: string, { ssh }: { ssh?: boolean }) { 8 9 const token = await getAccessToken(); 9 10 10 11 try { ··· 16 17 Authorization: `Bearer ${env.POCKETENV_TOKEN || token}`, 17 18 }, 18 19 }); 20 + 21 + if (ssh) { 22 + await connectToSandbox(name); 23 + return; 24 + } 19 25 20 26 consola.success(`Sandbox ${chalk.greenBright(name)} started`); 21 27 consola.log(
+1
apps/cli/src/index.ts
··· 77 77 program 78 78 .command("start") 79 79 .argument("<sandbox>", "the sandbox to start") 80 + .option("--ssh, -s", "connect to the Sandbox and automatically open a shell") 80 81 .description("start the given sandbox") 81 82 .action(start); 82 83