@tom2012/cc-web
v1.5.73
Published
Self-hosted web UI for Claude Code CLI — run Claude in your browser
Maintainers
Readme
CC Web
A self-hosted web application (distributed as npm package) that provides a browser-based interface for Claude Code CLI sessions. Create projects, each with a persistent terminal running Claude Code, and interact with them through a real-time terminal UI.
Current version: v1.5.73 | GitHub | MIT License
Features
- Project Management: Create, open, start, stop, and delete projects from a dashboard
- Real-time Terminal: Full xterm.js terminal in the browser, connected to Claude Code via WebSocket
- Persistent Sessions: Conversation history stored in each project's
.ccweb/folder — survives uninstall/reinstall (max 20 sessions per project, auto-pruned) - Permission Modes: Run Claude in limited mode (asks before acting) or unlimited mode (
--dangerously-skip-permissions) - Shortcuts Panel: Define reusable prompt commands at project or global level with inheritance
- Session History: Browse past conversations with full message history
- Graph Visualization: Topology graph from
.notebook/graph.yamlwith zoom/pan (layered DAG layout) - File Browser: Browse directories and preview/edit files with zoom-level memory per file
- Auto-restart: Terminals automatically recover from crashes
- Usage Tracking: Monitor Claude Code plan usage directly from the dashboard
- CLI Update:
ccweb updatestops the server and updates to the latest npm version - Localhost Auto-auth: Local access skips login entirely; JWT only required for remote access
- Auto Port Switching: Backend tries ports 3001–3020 and reports the actual port
- Network Access Modes: Local only (127.0.0.1), LAN (private IPs), or public — selectable at startup
- Cloud Backup: Incremental backup to Google Drive, OneDrive, or Dropbox (multi-provider parallel upload, scheduled or manual)
- SkillHub: Browse, search, and download community-shared shortcut commands from GitHub; share your own with one click
- Ambient Sound: Background sounds (singing bowl, rain, wind, stream, etc.) that play when LLM is active, with custom upload support
- Dark/Light Theme: Toggle between themes
- Real-time Activity Push: Dashboard receives project activity updates via WebSocket (no polling), with semantic status badges (Thinking/Writing/Tool)
- Code Splitting: Lazy-loaded routes and heavy components (Office preview, Graph, Chat) — only loaded on demand
- Global State (Zustand): Auth and project state shared across pages without prop drilling
- Error Boundaries: Graceful error handling with toast notifications instead of alerts
Prerequisites
- Node.js >= 18
- Claude Code CLI installed and authenticated (
claudecommand available in PATH)
Quick Start — npm / npx
The fastest way to get running:
# Try without installing (one-time)
npx @tom2012/cc-web
# Or install globally
npm install -g @tom2012/cc-web
ccwebOn first launch you'll be prompted to set a username and password. The server auto-selects an available port (starting from 3001) and opens your browser automatically.
CLI Commands
ccweb # start (interactive prompts)
ccweb start --daemon # start in background, no prompts
ccweb start --foreground # start in foreground, no prompts
ccweb start --local # local only (default, most secure)
ccweb start --lan # allow LAN access
ccweb start --public # allow public access
ccweb stop # stop background server
ccweb status # show PID, port, data location
ccweb open # open browser to running server
ccweb setup # reconfigure admin username / password
ccweb register # register a new user (interactive)
ccweb update # stop server & update to latest version
ccweb enable-autostart # start automatically on login
ccweb disable-autostart # remove auto-start
ccweb logs # tail background log fileAll user data (credentials, projects, sessions) is stored in ~/.ccweb/ and survives package updates.
Auto-start on login: ccweb enable-autostart registers a launchd agent (macOS) or systemd user service (Linux) so the server starts automatically when you log in.
Quick Start — from source (development)
# 1. Clone the repository
git clone https://github.com/zbc0315/cc-web.git
cd cc-web
# 2. Install dependencies
npm run install:all
# 3. First-time setup (creates login credentials)
npm run setup
# 4. Start backend (Terminal 1)
npm run dev:backend
# 5. Start frontend (Terminal 2)
npm run dev:frontendOpen http://localhost:5173 in your browser.
Architecture
Browser (React/Vite :5173 dev | Express :3001 prod)
│
├── REST API ──────────► Express (:3001, auto-switches port if busy)
│ │
└── WebSocket ─────────► ws server (same port)
│
TerminalManager
│
node-pty (PTY, user's $SHELL -ilc "claude")
│
claude / claude --dangerously-skip-permissionsBackend (backend/src/)
| File | Purpose |
|------|---------|
| index.ts | Express + WS server, route mounting, static frontend serving, auto port switching, project config migration |
| auth.ts | JWT middleware, localhost auto-auth (isLocalRequest), generateLocalToken() |
| config.ts | File-based JSON store, .ccweb/ per-project config helpers |
| terminal-manager.ts | PTY lifecycle ($SHELL -ilc "claude"), scrollback buffer (5 MB), auto-restart, activity tracking |
| session-manager.ts | Tails Claude's JSONL files, stores sessions in .ccweb/sessions/, prunes to latest 20 per project |
| usage-terminal.ts | Claude Code OAuth usage stats |
| routes/auth.ts | POST /login, GET /local-token (localhost only) |
| routes/projects.ts | CRUD + start/stop + POST /open (restore from .ccweb/) |
| routes/update.ts | GET /check-running, POST /prepare (save memory → wait idle → stop all, used by in-app update flow) |
| routes/filesystem.ts | Directory browser, file read/write |
| routes/shortcuts.ts | Global shortcut CRUD with inheritance |
| routes/backup.ts | Cloud backup API (providers, OAuth, backup trigger, schedule) |
| routes/sounds.ts | Sound file API: presets, download, upload, streaming |
| routes/skillhub.ts | SkillHub API: fetch skills index, submit via GitHub Issue, download to global shortcuts |
| backup/ | CloudProvider implementations (Google Drive, OneDrive, Dropbox), engine, scheduler |
Frontend (frontend/src/)
| File/Dir | Purpose |
|----------|---------|
| App.tsx | Router with auto-auth PrivateRoute (local token for localhost) |
| pages/LoginPage.tsx | Login form, auto-login on localhost |
| pages/DashboardPage.tsx | Project grid, new/open project, fullscreen toggle, update button |
| pages/ProjectPage.tsx | Three-panel layout: FileTree | WebTerminal | RightPanel |
| pages/SettingsPage.tsx | Settings: cloud accounts, backup strategy, backup history |
| components/SoundPlayer.tsx | Audio playback engine (fade in/out, loop/interval modes) |
| components/SoundSelector.tsx | Sound selection and configuration UI popover |
| components/WebTerminal.tsx | xterm.js terminal with fit addon |
| components/RightPanel.tsx | Three tabs: Shortcuts / History / Graph |
| components/ShortcutPanel.tsx | Project + global shortcuts, dialog editor for add/edit |
| components/GraphPreview.tsx | SVG topology graph of .notebook/graph.yaml (layered DAG, zoom/pan) |
| components/FileTree.tsx | Expandable directory tree |
| components/FilePreviewDialog.tsx | File viewer with plain/rendered/edit modes, zoom memory per file |
| components/UpdateButton.tsx | Version display and update check |
| pages/SkillHubPage.tsx | SkillHub browse, search, tag filter, download page |
| components/OpenProjectDialog.tsx | Open existing project from .ccweb/ folder |
| components/NewProjectDialog.tsx | 3-step wizard: name → folder → permissions |
| lib/api.ts | Typed REST client, dynamic base URL (relative in prod, localhost:3001 in dev) |
| lib/websocket.ts | useProjectWebSocket hook, dynamic WS URL |
| components/ui/ | shadcn/ui components (zinc theme) |
Data Storage
Application data (~/.ccweb/ for npm install, data/ for dev):
data/
├── config.json ← credentials & JWT secret
├── projects.json ← registered project list
└── global-shortcuts.json ← shared shortcut commandsPer-project data (inside each project folder, portable):
your-project/
├── .ccweb/
│ ├── project.json ← project metadata (id, name, mode, created)
│ └── sessions/ ← conversation history (max 20, auto-pruned)
│ └── {timestamp}-{uuid}.json
└── .notebook/ ← structured notes
├── pages/
└── graph.yamlThe .ccweb/ folder travels with the project. If you reinstall CC Web later, use Open Project to point at the folder and all history is restored.
WebSocket Protocol
Client → Server:
| Type | Payload | Purpose |
|------|---------|---------|
| auth | { token } | Authenticate (skipped for localhost) |
| terminal_subscribe | { cols, rows } | Subscribe + replay scrollback |
| terminal_input | { data } | Keystrokes to PTY |
| terminal_resize | { cols, rows } | Resize PTY |
Server → Client:
| Type | Payload | Purpose |
|------|---------|---------|
| connected | { projectId } | Ready |
| status | { status } | running/stopped/restarting |
| terminal_data | { data } | PTY output |
| terminal_subscribed | {} | Subscription confirmed |
| error | { message } | Error |
Localhost WebSocket connections are pre-authenticated — no auth message needed.
REST API
| Method | Endpoint | Purpose |
|--------|----------|---------|
| POST | /api/auth/login | Login, returns JWT |
| GET | /api/auth/local-token | Get local token (localhost only) |
| GET | /api/projects | List all projects |
| POST | /api/projects | Create new project |
| POST | /api/projects/open | Open existing project by folder path |
| DELETE | /api/projects/:id | Delete project |
| PATCH | /api/projects/:id/start | Start project terminal |
| PATCH | /api/projects/:id/stop | Stop project terminal |
| GET | /api/projects/:id/sessions | List sessions |
| GET | /api/projects/:id/sessions/:sid | Get session with messages |
| GET | /api/projects/activity | Terminal activity timestamps |
| GET | /api/projects/usage | Claude Code usage stats |
| GET/POST/PUT/DELETE | /api/shortcuts | Global shortcut CRUD |
| GET | /api/filesystem | Browse directories |
| POST | /api/filesystem/mkdir | Create folder |
| GET/PUT | /api/filesystem/file | Read/write files |
| GET | /api/update/check-running | Check if processes are running |
| POST | /api/update/prepare | Save memory, wait idle, stop all |
| GET/POST/DELETE | /api/backup/providers | Cloud backup provider CRUD |
| GET | /api/backup/auth/:id/url | Get OAuth2 authorization URL |
| GET | /api/backup/auth/callback | OAuth2 redirect callback |
| POST | /api/backup/run/:projectId | Trigger manual backup |
| GET/PUT | /api/backup/schedule | Backup schedule config |
| GET/PUT | /api/backup/excludes | Exclude patterns |
| GET | /api/backup/history | Backup history |
| GET | /api/skillhub/skills | Fetch SkillHub index (cached 5 min) |
| POST | /api/skillhub/submit | Submit skill via GitHub Issue |
| POST | /api/skillhub/download/:id | Download skill as global shortcut |
Server Deployment
# Build everything
npm run build
# Run backend (serves built frontend statically)
cd backend
npm start
# Or use pm2
pm2 start backend/dist/index.js --name cc-web
pm2 save
pm2 startupEnvironment variables:
| Variable | Purpose | Default |
|----------|---------|---------|
| CCWEB_DATA_DIR | Override data directory | data/ relative to backend |
| CCWEB_PORT | Preferred server port | 3001 |
| CCWEB_ACCESS_MODE | Network access mode (local/lan/public) | local |
Build & Release
# Full build (frontend + backend)
npm run build
# Release checklist:
# 1. Bump version in package.json, UpdateButton.tsx, README.md, CLAUDE.md
# 2. Update docs with new features
# 3. npm run build
# 4. git add -A && git commit && git push
# 5. npm publish --registry https://registry.npmjs.org --access=publicDevelopment Guide
Project Structure
cc-web/
├── package.json ← Root scripts + npm package config
├── bin/ccweb.js ← CLI entry point (ccweb command)
├── setup.js ← Interactive credential setup
├── backend/
│ ├── package.json
│ ├── tsconfig.json
│ └── src/ ← TypeScript source
└── frontend/
├── package.json
├── tsconfig.json
├── vite.config.ts
├── tailwind.config.js
└── src/ ← React + TypeScript sourceKey Design Decisions
- PTY-first: Spawns the real
claudeCLI vianode-ptyusing the user's$SHELL -ilc. All Claude Code features (slash commands, MCP, hooks, etc.) work natively. - No database: Pure JSON files, in-memory CRUD. Simple to understand, back up, and debug.
- Per-project
.ccweb/: Data travels with the project folder, survives app reinstall. - Session tailing: Reads Claude Code's native JSONL (
~/.claude/projects/) rather than parsing PTY output. - Scrollback buffer: 5 MB per terminal for client reconnect replay.
- Session pruning: Keeps latest 20 sessions per project, deletes oldest on new session start.
- Zoom memory:
FilePreviewDialogpersists zoom level per file path inlocalStorage. - Auto port switching: Backend tries ports 3001–3020, reports actual port via IPC.
- Localhost auto-auth: Local requests skip JWT verification entirely.
Adding a New API Endpoint
- Add the route handler in
backend/src/routes/*.ts - Auth is already applied — routes are mounted under
authMiddleware - Add the typed call in
frontend/src/lib/api.ts - Call it from your component
Adding a New Frontend Page
- Create
frontend/src/pages/YourPage.tsx - Add a route in
frontend/src/App.tsx - Use existing UI components from
frontend/src/components/ui/
Tech Stack
Backend: Node.js, Express, WebSocket (ws), node-pty, TypeScript Frontend: React 18, Vite, Tailwind CSS, shadcn/ui, xterm.js, TypeScript Auth: JWT (bcryptjs for password hashing)
License
MIT
