Copied from planning phase - full specification for PTY wrapper, WebSocket protocol, SQLite schema, and deployment setup.
3.6 KiB
3.6 KiB
claude-remote
self-hosted remote control for claude code, pure bun stack.
summary
wrap claude cli in PTY, stream output to server, approve prompts from phone.
tech stack
- bun-pty (native PTY via FFI to Rust)
- bun:sqlite (persistence)
- Bun.serve() (HTTP + WebSocket + SSE)
- no frameworks
project location
/home/jtm/projects/agentry/repos/claude-remote/
structure
claude-remote/
├── src/
│ ├── cli.ts # PTY wrapper, connects to server
│ ├── server.ts # HTTP + WebSocket + SSE
│ ├── db.ts # SQLite schema + queries
│ ├── auth.ts # HMAC signing
│ └── types.ts # shared types
├── public/
│ └── index.html # mobile dashboard
├── docs/
│ └── plan.md # this plan (moved here)
├── CLAUDE.md
├── package.json
├── Dockerfile
├── compose.yml
└── justfile
sqlite schema
CREATE TABLE devices (
id INTEGER PRIMARY KEY,
secret TEXT NOT NULL UNIQUE,
name TEXT,
created_at INTEGER NOT NULL,
last_seen INTEGER NOT NULL
);
CREATE TABLE sessions (
id INTEGER PRIMARY KEY,
device_id INTEGER NOT NULL,
started_at INTEGER NOT NULL,
ended_at INTEGER,
cwd TEXT,
command TEXT
);
CREATE TABLE prompts (
id INTEGER PRIMARY KEY,
session_id INTEGER NOT NULL,
created_at INTEGER NOT NULL,
prompt_text TEXT NOT NULL,
response TEXT,
responded_at INTEGER
);
CREATE TABLE output_log (
id INTEGER PRIMARY KEY,
session_id INTEGER NOT NULL,
timestamp INTEGER NOT NULL,
line TEXT NOT NULL
);
api
REST:
GET /- dashboardGET /api/sessions- list sessionsPOST /api/prompts/:id/approve- approvePOST /api/prompts/:id/reject- reject
WebSocket /ws (CLI <-> Server):
- cli sends: auth, output, resize, exit
- server sends: input, resize, ping
SSE /events (Server -> Dashboard):
- session_start, session_end, output, prompt, prompt_response
implementation phases
phase 1: foundation (~300 lines)
- create project structure
src/types.ts- message typessrc/db.ts- sqlite schema + queriessrc/auth.ts- device secret + HMAC
phase 2: server (~250 lines)
src/server.ts- Bun.serve with routes- WebSocket handler for CLI
- SSE endpoint for viewers
- REST endpoints for approval
phase 3: cli wrapper (~150 lines)
src/cli.ts- spawn claude with bun-pty- WebSocket client to server
- forward PTY output, handle resize
phase 4: dashboard (~300 lines)
public/index.html- mobile-first- SSE listener
- terminal output display
- approve/reject buttons
phase 5: deployment (~100 lines)
- Dockerfile (oven/bun)
- compose.yml with volume
- justfile commands
- error handling + reconnection
key decisions
- bun-pty over node-pty: pure FFI, pre-built binaries
- SQLite over in-memory: persistence across restarts
- SSE over WebSocket for dashboard: simpler, auto-reconnect
- HMAC over TLS certs: simpler deployment
- plain text output over xterm.js: mobile-friendly, simpler
verification
bun run src/cli.ts- starts PTY wrapper- visit
http://localhost:3000- see dashboard - type in CLI - output appears in dashboard
- trigger approval prompt - approve from dashboard
docker compose up- deploys successfully
dependencies
{
"dependencies": {
"bun-pty": "^0.4.8"
}
}
that's it. one external dep.
CLAUDE.md notes
- if bun-pty needs changes, fork and contribute upstream
- use bun:sqlite, not better-sqlite3
- no frameworks (no express, no hono)
- target ~1000-1500 lines total