Skip to content
Open
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
ac0272c
Add PTY types to shared package
whoiskatrin Dec 18, 2025
99db220
Add PtyManager for container PTY lifecycle
whoiskatrin Dec 18, 2025
4f15ccd
Add PTY handler and route registration
whoiskatrin Dec 18, 2025
020f697
Add PTY message handling to WebSocket adapter
whoiskatrin Dec 18, 2025
6a66c9b
Add PTY methods to transport interface
whoiskatrin Dec 18, 2025
09eca50
Add PtyClient for SDK PTY operations
whoiskatrin Dec 18, 2025
4dede27
Add pty namespace to Sandbox class
whoiskatrin Dec 18, 2025
fbf4fa1
Add E2E tests for PTY workflow
whoiskatrin Dec 18, 2025
d43b3fd
Add changeset for PTY support
whoiskatrin Dec 18, 2025
40258ee
Skip PTY tests when PTY allocation fails
whoiskatrin Dec 19, 2025
405d7c7
Fix pty manager tests
whoiskatrin Dec 19, 2025
b9cc460
fix any types, logger
whoiskatrin Dec 19, 2025
794235b
fix silent logging
whoiskatrin Dec 19, 2025
2534791
fix pty tests for resizing
whoiskatrin Dec 21, 2025
1f311eb
update claude review yml
whoiskatrin Dec 21, 2025
93328a3
revert review change
whoiskatrin Dec 21, 2025
0e2e8ce
update http tests
whoiskatrin Dec 22, 2025
05c2342
more test updates
whoiskatrin Dec 22, 2025
8c0194b
remove the plugin for review
whoiskatrin Dec 22, 2025
f03f896
Add error handling to PTY callbacks and terminal operations
whoiskatrin Dec 22, 2025
2b20949
Improve
whoiskatrin Dec 22, 2025
0a7ca34
add structured exit codes
whoiskatrin Dec 22, 2025
119510e
change fire and forget strategy
whoiskatrin Dec 22, 2025
c9d5f1f
add more e2e tests
whoiskatrin Dec 22, 2025
2e04768
more fixes and tests
whoiskatrin Dec 22, 2025
d256de3
fix ws
whoiskatrin Dec 22, 2025
c85afc7
update error propagation
whoiskatrin Dec 22, 2025
407744d
update resizing tests
whoiskatrin Dec 23, 2025
b81cb3d
add collab terminal example
whoiskatrin Dec 23, 2025
7ddaaa9
Potential fix for code scanning alert no. 40: Insecure randomness
whoiskatrin Dec 23, 2025
76e628e
Potential fix for code scanning alert no. 41: Insecure randomness
whoiskatrin Dec 23, 2025
9c21e86
Update dependency in examples
whoiskatrin Dec 23, 2025
98754c7
Add PTY listeners cleanup
whoiskatrin Dec 23, 2025
7f6bce2
minor nits
whoiskatrin Dec 23, 2025
e213b13
update tests setup
whoiskatrin Dec 23, 2025
edd6a00
Add logging for PTY listener registration errors and improve error ha…
whoiskatrin Dec 23, 2025
76ceaa4
Enhance error handling and logging in WebSocketTransport and PtyHandl…
whoiskatrin Dec 23, 2025
bc159c6
Add connection-specific PTY listener cleanup on WebSocket close
whoiskatrin Dec 23, 2025
3cfc363
Remove outdated comment regarding connection cleanup functions in Web…
whoiskatrin Dec 23, 2025
51ee876
Fix error handling in PTY management by updating kill method to retur…
whoiskatrin Dec 23, 2025
d51f141
implement signal handling for Ctrl+C, Ctrl+Z, and Ctrl+\ in the PTY m…
whoiskatrin Jan 5, 2026
1ea663e
Merge main into pty-support
whoiskatrin Jan 5, 2026
499e3b7
Potential fix for code scanning alert no. 43: Insecure randomness
whoiskatrin Jan 5, 2026
bed6e22
Changes based on review comments
whoiskatrin Jan 8, 2026
b11b99f
Update dependencies and improve PTY handling in collaborative termina…
whoiskatrin Jan 8, 2026
1f0f7f6
Update PTY workflow tests to expect correct HTTP status codes for err…
whoiskatrin Jan 8, 2026
84a20a7
extractPtyId method to retrieve PTY IDs from responses, and update ha…
whoiskatrin Jan 8, 2026
0aa8995
Update PTY workflow tests to expect 'message' field in error response…
whoiskatrin Jan 8, 2026
fd1adc5
Fixed handlers for tests
whoiskatrin Jan 9, 2026
5918151
Use PR-specific Docker build cache scope to avoid cross-PR cache poll…
whoiskatrin Jan 9, 2026
a841501
Add debug logging to router for route registration and matching
whoiskatrin Jan 9, 2026
96edf05
Add INFO-level route logging to debug container caching issues
whoiskatrin Jan 9, 2026
a2898ea
Add retry logic for WebSocket server readiness in e2e tests
whoiskatrin Jan 9, 2026
f5f612a
Remove debug logging added during PTY route investigation
whoiskatrin Jan 9, 2026
099bb36
Fix sync-docs workflow to handle PR bodies with special characters
whoiskatrin Jan 9, 2026
a3f8b02
Fix sync-docs workflow shell escaping for opencode run
whoiskatrin Jan 9, 2026
10e3f36
Merge main into pty-support, resolve sync-docs conflict
whoiskatrin Jan 9, 2026
491b642
Merge remote-tracking branch 'origin/main' into pty-support
ghostwriternr Jan 13, 2026
5c7de26
Fix lint errors and align env type signatures
ghostwriternr Jan 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .changeset/pty-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
'@cloudflare/sandbox': minor
---

Add PTY (pseudo-terminal) support for interactive terminal sessions.

New `sandbox.pty` namespace with:

- `create()` - Create a new PTY session
- `attach(sessionId)` - Attach PTY to existing session
- `getById(id)` - Reconnect to existing PTY
- `list()` - List all PTY sessions

PTY handles support:

- `write(data)` - Send input
- `resize(cols, rows)` - Resize terminal
- `kill()` - Terminate PTY
- `onData(cb)` - Receive output
- `onExit(cb)` - Handle exit
- `exited` - Promise for exit code
- Async iteration for scripting

Example:

```typescript
const pty = await sandbox.pty.create({ cols: 80, rows: 24 });
pty.onData((data) => terminal.write(data));
pty.write('ls -la\n');
```
1 change: 0 additions & 1 deletion .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ jobs:
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: |
https://github.com/anthropics/claude-plugins-official.git
https://github.com/obra/superpowers-marketplace.git
plugins: |
pr-review-toolkit@claude-plugins-official
Expand Down
17 changes: 17 additions & 0 deletions examples/collaborative-terminal/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Collaborative Terminal Dockerfile
#
# IMPORTANT: PTY support requires sandbox image version 0.7.0 or later.
#
# For local development with PTY support, first build the base image:
# cd ../.. # Go to monorepo root
# docker build --platform linux/amd64 -f packages/sandbox/Dockerfile --target default -t sandbox-pty-local .
#
# The wrangler dev server will then use this Dockerfile which extends sandbox-pty-local.
#
FROM docker.io/cloudflare/sandbox:0.0.0-pr-310-ad66e85

# Create home directory for terminal sessions
RUN mkdir -p /home/user && chmod 777 /home/user

# Expose container port
EXPOSE 3000
165 changes: 165 additions & 0 deletions examples/collaborative-terminal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# Collaborative Terminal

**Google Docs for Bash** - A multi-user terminal where multiple people can see the same PTY output in real-time and take turns sending commands.

This example demonstrates:

- **PTY Support**: Using the Sandbox SDK's PTY API for interactive terminal sessions
- **WebSocket Streaming**: Real-time output broadcast to all connected users
- **Collaborative Workflows**: Multiple users sharing a single terminal session
- **Presence Indicators**: See who's connected and who's typing

## Features

- Create terminal rooms that others can join via shareable link
- Real-time terminal output synchronized across all participants
- User presence list with colored indicators
- "Typing" indicators showing who's sending commands
- Terminal history buffering so new users see previous output
- Automatic cleanup when all users disconnect

## Architecture

```
┌──────────────┐ WebSocket ┌─────────────────┐ PTY API ┌───────────┐
│ Browser │◄──────────────────► Cloudflare │◄──────────────►│ Sandbox │
│ (xterm) │ │ Worker │ │ Container │
└──────────────┘ └─────────────────┘ └───────────┘
│ │
│ │
▼ ▼
User Input Broadcast PTY
─────────► output to all
connected users
```

## Getting Started

### Prerequisites

- Node.js 20+
- Docker (for local development)
- Cloudflare account with container access (beta)

### Important: PTY Support Required

This example requires PTY (pseudo-terminal) support which is available in sandbox image version **0.7.0 or later**. If using an older image, you'll need to build a local image with PTY support:

```bash
# From the monorepo root
cd ../..
docker build -f packages/sandbox/Dockerfile --target default -t sandbox-local .

# Then update examples/collaborative-terminal/Dockerfile to use:
# FROM sandbox-local
```

### Installation

```bash
cd examples/collaborative-terminal
npm install
```

### Development

```bash
npm run dev
```

This starts a local development server. Open http://localhost:5173 to access the app.

### Deploy

```bash
npm run deploy
```

## Usage

1. **Create a Room**: Click "Create New Room" to start a new terminal session
2. **Share the Link**: Click "Copy Link" to share the room with others
3. **Join a Room**: Enter a room ID to join an existing session
4. **Start Terminal**: Click "Start Terminal Session" to launch bash
5. **Collaborate**: All connected users see the same terminal output and can send commands

## How It Works

### Backend (Worker)

The Cloudflare Worker manages:

1. **Room State**: Tracks connected users and active PTY sessions per room
2. **WebSocket Connections**: Handles real-time communication with clients
3. **PTY Lifecycle**: Creates/destroys PTY sessions via the Sandbox SDK
4. **Output Broadcasting**: Forwards PTY output to all connected WebSocket clients

```typescript
// Create PTY and subscribe to output
const pty = await sandbox.pty.create({
cols: 80,
rows: 24,
command: ['/bin/bash']
});

pty.onData((data) => {
// Broadcast to all connected users
broadcast(roomId, { type: 'pty_output', data });
});
```

### Frontend (React + xterm.js)

The React app provides:

1. **Terminal Rendering**: Uses xterm.js for terminal emulation
2. **WebSocket Client**: Connects to the worker for real-time updates
3. **User Management**: Displays connected users with presence indicators
4. **Input Handling**: Forwards keystrokes to the shared PTY

```typescript
// Handle terminal input
term.onData((data) => {
ws.send(JSON.stringify({ type: 'pty_input', data }));
});

// Handle PTY output from server
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'pty_output') {
term.write(msg.data);
}
};
```

## API Reference

### WebSocket Messages

**Client → Server:**

| Type | Description | Fields |
| ------------ | ------------------ | -------------- |
| `start_pty` | Create PTY session | `cols`, `rows` |
| `pty_input` | Send input to PTY | `data` |
| `pty_resize` | Resize terminal | `cols`, `rows` |

**Server → Client:**

| Type | Description | Fields |
| ------------- | ------------------- | ---------------------------- |
| `connected` | Initial connection | `userId`, `users`, `history` |
| `user_joined` | User joined room | `user`, `users` |
| `user_left` | User left room | `userId`, `users` |
| `pty_started` | PTY session created | `ptyId` |
| `pty_output` | Terminal output | `data` |
| `pty_exit` | PTY session ended | `exitCode` |
| `user_typing` | User sent input | `user` |

## Customization Ideas

- **Access Control**: Add authentication to restrict who can join/type
- **Command History**: Store and replay command history
- **Multiple Terminals**: Support multiple PTY sessions per room
- **Recording**: Record sessions for playback
- **Chat**: Add a sidebar chat for discussion
36 changes: 36 additions & 0 deletions examples/collaborative-terminal/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#09090b" />
<meta name="description" content="Real-time collaborative terminal sharing. Like Google Docs, but for your shell. Powered by Cloudflare Sandboxes." />
<meta property="og:title" content="Collaborative Terminal" />
<meta property="og:description" content="Real-time terminal sharing. Code together, debug together, ship together." />
<meta property="og:type" content="website" />
<title>Collaborative Terminal | Cloudflare Sandbox</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
<style>
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
background: #09090b;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#root {
min-height: 100vh;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
41 changes: 41 additions & 0 deletions examples/collaborative-terminal/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "@cloudflare/sandbox-collaborative-terminal-example",
"version": "1.0.0",
"description": "Collaborative terminal - Google Docs for bash using PTY support",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"deploy": "vite build && wrangler deploy",
"types": "wrangler types env.d.ts --include-runtime false",
"typecheck": "tsc --noEmit"
},
"keywords": [
"sandbox",
"pty",
"terminal",
"collaborative"
],
"author": "",
"license": "ISC",
"type": "module",
"dependencies": {
"@xterm/addon-fit": "^0.10.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/addon-webgl": "^0.19.0",
"@xterm/xterm": "^5.5.0",
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@cloudflare/sandbox": "*",
"@cloudflare/vite-plugin": "^1.15.2",
"@cloudflare/workers-types": "^4.20251126.0",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"typescript": "^5.9.3",
"vite": "^7.2.4",
"wrangler": "^4.50.0"
}
}
Loading