clarc/docs/plan.md

157 lines
3.6 KiB
Markdown

# 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
```sql
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 /` - dashboard
- `GET /api/sessions` - list sessions
- `POST /api/prompts/:id/approve` - approve
- `POST /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)
1. create project structure
2. `src/types.ts` - message types
3. `src/db.ts` - sqlite schema + queries
4. `src/auth.ts` - device secret + HMAC
### phase 2: server (~250 lines)
5. `src/server.ts` - Bun.serve with routes
6. WebSocket handler for CLI
7. SSE endpoint for viewers
8. REST endpoints for approval
### phase 3: cli wrapper (~150 lines)
9. `src/cli.ts` - spawn claude with bun-pty
10. WebSocket client to server
11. forward PTY output, handle resize
### phase 4: dashboard (~300 lines)
12. `public/index.html` - mobile-first
13. SSE listener
14. terminal output display
15. approve/reject buttons
### phase 5: deployment (~100 lines)
16. Dockerfile (oven/bun)
17. compose.yml with volume
18. justfile commands
19. 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
1. `bun run src/cli.ts` - starts PTY wrapper
2. visit `http://localhost:7200` - see dashboard
3. type in CLI - output appears in dashboard
4. trigger approval prompt - approve from dashboard
5. `docker compose up` - deploys successfully
## dependencies
```json
{
"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