Skip to content

Commit 7abb9c8

Browse files
committed
Add interactive terminal (SSH-like) section
- Add section showing how to create a fully interactive terminal - Include raw mode setup, stdin forwarding, resize handling - Reference E2B CLI implementation - Remove incorrect stdout accumulation documentation
1 parent 055b341 commit 7abb9c8

1 file changed

Lines changed: 104 additions & 31 deletions

File tree

docs/sandbox/pty.mdx

Lines changed: 104 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,6 @@ You can disconnect from a PTY session while keeping it running, then reconnect l
180180
- Sharing terminal access between multiple clients
181181
- Implementing terminal session persistence
182182

183-
<Note>
184-
While disconnected, the PTY continues to accumulate output. You can access all output generated during the session (including while disconnected) via the `stdout` property on the terminal handle.
185-
</Note>
186-
187183
<CodeGroup>
188184
```js JavaScript & TypeScript
189185
import { Sandbox } from '@e2b/code-interpreter'
@@ -256,33 +252,6 @@ asyncio.run(main())
256252
```
257253
</CodeGroup>
258254

259-
### Accessing accumulated output
260-
261-
After reconnecting, you can access all output generated during the session (including while disconnected) via the `stdout` property:
262-
263-
<CodeGroup>
264-
```js JavaScript & TypeScript
265-
// After reconnecting...
266-
const reconnected = await sandbox.pty.connect(pid, {
267-
onData: (data) => { /* handle new data */ },
268-
})
269-
270-
// Access all accumulated output from the session
271-
console.log('All output so far:', reconnected.stdout)
272-
```
273-
274-
```python Python
275-
# After reconnecting...
276-
reconnected = await sandbox.pty.connect(
277-
pid,
278-
on_data=lambda data: print(data.decode(), end=''),
279-
)
280-
281-
# Access all accumulated output from the session
282-
print('All output so far:', reconnected.stdout)
283-
```
284-
</CodeGroup>
285-
286255
## Kill the PTY
287256

288257
Terminate the PTY session with `kill()`.
@@ -378,3 +347,107 @@ async def main():
378347
asyncio.run(main())
379348
```
380349
</CodeGroup>
350+
351+
## Interactive terminal (SSH-like)
352+
353+
For a fully interactive terminal experience similar to SSH, you need to:
354+
1. Set your local terminal to raw mode
355+
2. Forward local stdin to the PTY
356+
3. Write PTY output to local stdout
357+
4. Handle terminal resize events
358+
359+
<CodeGroup>
360+
```js JavaScript & TypeScript
361+
import { Sandbox } from '@e2b/code-interpreter'
362+
363+
async function main() {
364+
const sandbox = await Sandbox.create()
365+
366+
// Set terminal to raw mode for character-by-character input
367+
process.stdin.setRawMode(true)
368+
369+
const terminal = await sandbox.pty.create({
370+
cols: process.stdout.columns,
371+
rows: process.stdout.rows,
372+
onData: (data) => process.stdout.write(data),
373+
timeoutMs: 0,
374+
})
375+
376+
// Forward stdin to the PTY
377+
process.stdin.on('data', async (data) => {
378+
await sandbox.pty.sendInput(terminal.pid, data)
379+
})
380+
381+
// Handle terminal resize
382+
process.stdout.on('resize', () => {
383+
sandbox.pty.resize(terminal.pid, {
384+
cols: process.stdout.columns,
385+
rows: process.stdout.rows,
386+
})
387+
})
388+
389+
// Wait for the session to end
390+
try {
391+
await terminal.wait()
392+
} finally {
393+
process.stdin.setRawMode(false)
394+
await sandbox.kill()
395+
}
396+
}
397+
398+
main()
399+
```
400+
401+
```python Python
402+
import asyncio
403+
import sys
404+
import tty
405+
import termios
406+
from e2b_code_interpreter import AsyncSandbox
407+
from e2b.sandbox_async.commands.pty import PtySize
408+
409+
410+
async def main():
411+
sandbox = await AsyncSandbox.create()
412+
413+
# Save terminal settings
414+
old_settings = termios.tcgetattr(sys.stdin)
415+
416+
try:
417+
# Set terminal to raw mode
418+
tty.setraw(sys.stdin.fileno())
419+
420+
terminal = await sandbox.pty.create(
421+
size=PtySize(cols=80, rows=24),
422+
on_data=lambda data: sys.stdout.buffer.write(data) or sys.stdout.flush(),
423+
timeout=0,
424+
)
425+
426+
# Forward stdin to PTY (simplified - production code needs async stdin reading)
427+
async def read_stdin():
428+
loop = asyncio.get_event_loop()
429+
while True:
430+
data = await loop.run_in_executor(None, sys.stdin.buffer.read, 1)
431+
if data:
432+
await sandbox.pty.send_stdin(terminal.pid, data)
433+
434+
stdin_task = asyncio.create_task(read_stdin())
435+
436+
try:
437+
await terminal.wait()
438+
finally:
439+
stdin_task.cancel()
440+
441+
finally:
442+
# Restore terminal settings
443+
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
444+
await sandbox.kill()
445+
446+
447+
asyncio.run(main())
448+
```
449+
</CodeGroup>
450+
451+
<Note>
452+
This is the same pattern used by the [E2B CLI](https://github.com/e2b-dev/E2B/blob/main/packages/cli/src/terminal.ts) to provide interactive sandbox sessions. For production use, consider batching input to reduce network calls.
453+
</Note>

0 commit comments

Comments
 (0)