agentlytics
Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravi…
Self-hosted mission control for AI coding agents: run Claude Code, OpenCode, Codex, and Gemini CLI 24/7, from any devic…
Claude Code • OpenCode • Codex • Gemini • Terminal - One Dashboard • Any Device
English • 简体中文
Codeman is a self-hosted mission control for AI coding agents. It spawns Claude Code, OpenCode, Codex, or Gemini CLI inside persistent tmux sessions, streams the real terminal to any browser, and keeps agents productive after you walk away: it re-prompts on idle, resumes when a usage limit resets, runs scheduled jobs, and shows every background agent working in real time.
Get started in one line (macOS & Linux, Windows via WSL):
curl -fsSL https://getcodeman.com/install | bash
codeman web # Open http://localhost:3000 and start your first session
The installer asks before every system change, and re-running the same line updates in place. Full details: Quick Start - Installation.
curl -fsSL https://getcodeman.com/install | bash
This installs Node.js and tmux if missing, clones Codeman to ~/.codeman/app, and builds it. A few things worth knowing:
0.0.0.0, the default, with a strongly recommended password prompt) or from this machine only (127.0.0.1, safest). Skipping the password on a network bind requires an explicit confirmation and ends with a loud warning. A bare codeman web started by hand still defaults to loopback.~/.codeman/app are stashed (never discarded), and a running service is restarted and verified. If a first install was interrupted, re-running resumes the full setup instead. install.sh update and install.sh uninstall also exist.CODEMAN_NONINTERACTIVE=1 to approve them for automation.You'll need at least one AI coding CLI installed — Claude Code, OpenCode, Codex, or Gemini CLI (any combination works). The installer detects whichever of the four is present; if none is found, it offers to install Claude Code or OpenCode, or you can skip and install one yourself later. After install:
codeman web # Open http://localhost:3000 and start your first session
Sharing with a small team? Start it in multi-user mode instead: each person gets their own login and workspace.
codeman users add alice --admin # create the first admin account codeman web --multiuser # named logins + per-user case spaces
Details in Multi-User Mode below.
The installer's final menu sets this up for you (option 2) and verifies the service actually comes up before claiming success. To configure it manually instead:
Linux (systemd):
mkdir -p ~/.config/systemd/user cat > ~/.config/systemd/user/codeman-web.service << EOF [Unit] Description=Codeman Web Server After=network.target [Service] Type=simple ExecStart=$(which node) $HOME/.codeman/app/dist/index.js web Restart=always RestartSec=10 [Install] WantedBy=default.target EOF systemctl --user daemon-reload systemctl --user enable --now codeman-web loginctl enable-linger $USER
macOS (launchd):
mkdir -p ~/Library/LaunchAgents
cat > ~/Library/LaunchAgents/com.codeman.web.plist << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.codeman.web</string>
<key>ProgramArguments</key>
<array>
<string>$(which node)</string>
<string>$HOME/.codeman/app/dist/index.js</string>
<string>web</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardOutPath</key>
<string>/tmp/codeman.log</string>
<key>StandardErrorPath</key>
<string>/tmp/codeman.log</string>
</dict>
</plist>
EOF
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.codeman.web.plist
wsl bash -c "curl -fsSL https://getcodeman.com/install | bash"
Codeman requires tmux, so Windows users need WSL. If you don't have WSL yet: run wsl --install in an admin PowerShell, reboot, open Ubuntu, then install your preferred AI coding CLI inside WSL (Claude Code, OpenCode, Codex, or Gemini CLI). After installing, http://localhost:3000 is accessible from your Windows browser.
The most responsive AI coding agent experience on any phone. Full xterm.js terminal with local echo, swipe navigation, and a touch-optimized interface designed for real remote work — not a desktop UI crammed onto a small screen.
![]() |
![]() |
| Answering prompts by touch | Accessory bar + dedicated Enter button |
| Terminal Apps | Codeman Mobile |
|---|---|
| 200-300ms input lag over remote | Local echo — instant feedback |
| Tiny text, no context | Full xterm.js terminal |
| No session management | Swipe between sessions |
| No notifications | Push alerts for approvals and idle |
| Manual reconnect | tmux persistence |
| No agent visibility | Background agents in real-time |
| Copy-paste slash commands | One-tap /init, /clear, /compact |
| Password typing on phone | QR code scan — instant auth |
/init, /clear, /compact quick-action buttons above the virtual keyboard; destructive commands require a double-press to confirm, so you never fire one by accidentvisualViewport API)codeman web --https # Open on your phone: https://<your-ip>:3000
localhostworks over plain HTTP. Use--httpswhen accessing from another device, or use Tailscale (recommended) — it provides a private network so you can accesshttp://<tailscale-ip>:3000from your phone without TLS certificates.
Typing passwords on a phone keyboard is miserable. Codeman replaces it with cryptographically secure single-use QR tokens — scan the code displayed on your desktop and your phone is authenticated instantly.
Each QR encodes a URL containing a 6-character short code that maps to a 256-bit secret (crypto.randomBytes(32)) on the server. Tokens auto-rotate every 60 seconds, are atomically consumed on first scan (replays always fail), and use hash-based Map.get() lookup that leaks nothing through response timing. The short code is an opaque pointer — the real secret never appears in browser history, Referer headers, or Cloudflare edge logs.
The security design addresses all 6 critical QR auth flaws identified in "Demystifying the (In)Security of QR Code-based Login" (USENIX Security 2025, which found 47 of the top-100 websites vulnerable): single-use enforcement, short TTL, cryptographic randomness, server-side generation, real-time desktop notification on scan (QRLjacking detection), and IP + User-Agent session binding with manual revocation. Dual-layer rate limiting (per-IP + global) makes brute force infeasible across 62^6 = 56.8 billion possible codes. Full security analysis: docs/qr-auth-plan.md
A start-to-finish walkthrough for driving Codeman from the browser. If you just installed, this is where to begin.
codeman web # localhost:3000 (loopback only — safe default) codeman web --port 8080 # custom port (or set CODEMAN_PORT) codeman web --https # self-signed TLS (only needed for remote access) codeman web -H 0.0.0.0 # bind LAN — REQUIRES CODEMAN_PASSWORD (see Security)
Open the printed URL. The page is a single dashboard; everything below happens there.
Click + New Session (or Quick Start). A session is one AI CLI running in its own tmux-backed terminal. You choose:
| Field | What it does |
|---|---|
| Working directory / case | The folder the agent operates in. A "case" is just a named working dir Codeman remembers. |
| CLI / run mode | Claude (default), OpenCode, Codex, Gemini, or Terminal (plain shell). |
| Model | Per-session model (App Settings → Claude Model). A soft default — /model still works in-session. |
| Effort / Ultracode | Reasoning effort (low–max) or ultracode for dynamic multi-agent workflows. Switchable anytime with /effort. |
Hit start — Codeman spawns the CLI via a real PTY and streams it to your browser over SSE.
Alt+1-9 to jump, Ctrl+Tab for next, drag to reorder (tab order syncs across your devices).xterm.js terminal; full TUIs render correctly. Type directly and press Enter to send. Shift+Enter inserts a newline.Ctrl+Shift+V (Deepgram Nova-3, with auto-silence stop).| Mode | Use it for | Where |
|---|---|---|
| Respawn | Long unattended runs — auto-restarts the CLI on idle/limit, with adaptive timing. Presets: solo-work, overnight-autonomous, … |
Respawn tab |
| Orchestrator | Turn one goal into a phased plan and drive it to completion across agents. | Orchestrator panel |
| Cron | Saved, named jobs on a schedule (once/interval/daily/weekly) that spawn a session and send a prompt when due. |
⏰ Cron button (opt-in: App Settings → Display → Header Displays) |
| Auto-resume | Automatically continue after a subscription rate-limit resets. | Respawn tab (top) |
./scripts/tunnel.sh start opens a Cloudflare tunnel (set CODEMAN_PASSWORD first).sc chooser attaches to any session from a terminal (sc interactive, sc 2 quick-attach, sc -l list).⚠️ Safety: if you're working inside a Codeman-managed session (
echo $CODEMAN_MUX→1), never runtmux kill-session/pkill claudedirectly — use the web UI or./scripts/tmux-manager.sh.
When accessing your coding agent remotely (VPN, Tailscale, SSH tunnel), every keystroke normally takes 200-300ms to round-trip. Codeman implements a Mosh-inspired local echo system that makes typing feel instant regardless of latency.
A pixel-perfect DOM overlay inside xterm.js renders keystrokes at 0ms. Background forwarding silently sends every character to the PTY in 50ms debounced batches, so Tab completion, Ctrl+R history search, and all shell features work normally. When the server echo arrives 200-300ms later, the overlay seamlessly disappears and the real terminal text takes over — the transition is invisible.
<span> at z-index 7 inside .xterm-screen, completely immune to Ink's constant screen redraws (two previous attempts using terminal.write() failed because Ink corrupts injected buffer content)fontFamily, fontSize, fontWeight, and letterSpacing from xterm.js computed styles so overlay text is visually indistinguishable from real terminal outputExtracted as a standalone library:
xterm-zerolag-input— see Published Packages.
Watch background agents work in real-time. Codeman monitors agent activity and displays each agent in a draggable floating window with animated Matrix-style connection lines back to the parent session.
Multi-agent Workflow runs ("ultracode") get the same treatment: a floating run window tracks the whole workflow live, with phases, per-agent token counts, and the current tool of every agent:
Agent Teams — first-class support for Claude Code's native multi-agent teams (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1). TeamWatcher polls ~/.claude/teams/, matches teammates to their lead session, and surfaces them as live subagent windows with team-aware idle detection — so the Respawn Controller won't fire while teammates are still working. See docs/agent-teams/.
The core of autonomous work. When the agent goes idle, the Respawn Controller detects it, sends a continue prompt, cycles context management commands for fresh context, and resumes — running 24+ hours completely unattended.
WATCHING → IDLE DETECTED → SEND UPDATE → /clear → /init → CONTINUE → WATCHING
continue — so an overnight run survives the 5-hour window instead of stalling until morning. Recognizes every Claude Code limit-message format, retries if still limited, survives Codeman restarts, and holds respawn cycles while paused so /clear can't wipe the waiting conversation. Enable per session at the top of the Respawn tabsolo-work (3s idle, 60min), subagent-workflow (45s, 240min), team-lead (90s, 480min), ralph-todo (8s, 480min), overnight-autonomous (10s, 480min)Beyond single-session respawn, the Orchestrator turns a high-level goal into a phased plan and drives it to completion across multiple agents — a state machine that runs idle → planning → approval → executing → verifying → (replanning) → completed.
orchestrator key in state.json, so it survives restartsPOST /api/orchestrator/start → /approve → /status (10 endpoints)Full design:
docs/orchestrator-loop-architecture.md.
Run 20 parallel sessions with full visibility — real-time xterm.js terminals at 60fps, per-session token and cost tracking, tab-based navigation, and one-click management.
Every session runs inside tmux — sessions survive server restarts, network drops, and machine sleep. Auto-recovery on startup with dual redundancy. Ghost session discovery finds orphaned tmux sessions. Managed sessions are environment-tagged so the agent won't kill its own session.
Ctrl/Cmd/Alt+K opens a fuzzy session palette; Browse all sessions opens the Session Manager: one deduped list of everything Codeman knows about (live sessions, past sessions from state and lifecycle history, and Claude transcripts), each row showing its first and most recent prompt.
Running Codeman on multiple hosts (laptop, dev box, NAS)? The browser tab title is codeman:<hostname> so you can tell which backend each tab points at without clicking in:
codeman web # codeman:<os.hostname()> codeman web --title-hostname dev-box # codeman:dev-box (manual override for noisy hostnames)
The title is templated into the served HTML on first byte, so it's correct from the very first paint and works without JavaScript. The same hostname prefix is applied to the tab-flash format (⚠️ (N) codeman:<host>) and to OS-level desktop notifications (codeman:<host>: <event>), so cross-host alerts in the system notification center are also unambiguous.
| Threshold | Action | Result |
|---|---|---|
| 110k tokens | Auto /compact |
Context summarized, work continues |
| 140k tokens | Auto /clear |
Fresh start with /init |
Real-time desktop alerts when sessions need attention — permission_prompt and elicitation_dialog trigger critical red tab blinks, idle_prompt triggers yellow blinks. Click any notification to jump directly to the affected session. Hooks auto-configured per case directory.
Click the chart icon on any session tab to see a timeline of everything that happened — respawn cycles, token milestones, auto-compact triggers, idle/working transitions, hook events, errors, and more.
Terminal-based AI agents (Claude Code's Ink, OpenCode's Bubble Tea) redraw the screen on every state change. Codeman implements a 6-layer anti-flicker pipeline for smooth 60fps output across all sessions:
PTY Output → 16ms Server Batch → DEC 2026 Wrap → SSE → Client rAF → xterm.js (60fps)
CLAUDE_CODE_* vs OPENCODE_* vs CODEX_* vs GEMINI_*/GOOGLE_*). See docs/opencode-integration.md.tar.gz to move it to another machine. See docs/docker-cases.mddocs/remote-sessions.mdlow–max) or enable ultracode (dynamic multi-agent workflows). Soft defaults only — switchable anytime with /effort in-session. Extended-thinking budget is configurable tooCtrl+Shift+V)CODEMAN_GESTURE=1 + App Settings → Displaycodeman:<host> so multi-host setups stay unambiguousRun a case inside its own hardened Docker container instead of directly on your host — for security isolation, reproducible toolchains, and one-click portability.
docker exec into the same container; killing one session never tears the container out from under the others.--cap-drop ALL, no-new-privileges, PID/memory caps, never --privileged or the docker socket; a sealed profile (no host credentials, network off) is one toggle away..tar.gz, docker load it on the other side, and import it into a fresh case.Prerequisite: just Docker (or Podman). The agent base image builds itself automatically on first use, with progress streamed to the UI (or pre-build it with node scripts/build-agent-image.mjs). Full guide: docs/docker-cases.md.
Point a case at another machine and run the agent there, over SSH, with the same dashboard, mobile UI, and autonomy features. Your laptop is just a window onto a session that lives on the remote host.
codeman-* sessions already running on a host (started by that machine's own Codeman, or by another operator) and attach to one. Attached sessions you don't own detach on tab close, never kill.Set it up under New Case → Remote (host, user, identity file, optional jump host). Full design: docs/remote-sessions.md.
Share one Codeman with a small trusted team, each person getting their own login and workspace. Off by default — without the flag, nothing changes.
Enable with codeman web --multiuser (or CODEMAN_MULTIUSER=1). Create the first admin, then manage users from the CLI or the Users tab in App Settings:
codeman users add alice --admin # prompts for a password (or --password-stdin) codeman users add bob # a regular user codeman users list
~/codeman-users/<name>/cases; sessions, cases, search, and real-time events are scoped to their owner. Admins see everything.~/.codeman/users.json; disable, reset (one-time password), or delete an account at any time. Admin actions are audited to ~/.codeman/admin-audit.jsonl.--permission-mode auto (Anthropic's classifier-guarded mode); raw shell sessions, cron launchCommand, and skip-permissions require an explicit per-user grant.⚠️ This separates workspaces; it does not sandbox users from each other. Every session runs as the same OS account, so a determined user's agent can still reach another user's files. For real isolation, pair users with Docker cases or run separate instances under separate OS accounts. See
docs/multi-user-plan.mdand the multi-user section ofdocs/security-architecture.md.
Access Codeman from your phone or any device outside your local network using a free Cloudflare quick tunnel — no port forwarding, no DNS, no static IP required.
Browser (phone/tablet) → Cloudflare Edge (HTTPS) → cloudflared → localhost:3000
Prerequisites: Install cloudflared and set CODEMAN_PASSWORD in your environment.
# Quick start ./scripts/tunnel.sh start # Start tunnel, prints public URL ./scripts/tunnel.sh url # Show current URL ./scripts/tunnel.sh stop # Stop tunnel ./scripts/tunnel.sh status # Service status + URL
The script auto-installs a systemd user service on first run. The tunnel URL is a randomly generated *.trycloudflare.com address that changes each time the tunnel restarts.
# Enable as a persistent service systemctl --user enable codeman-tunnel loginctl enable-linger $USER # Or via the Codeman web UI: Settings → Tunnel → Toggle On
admin or CODEMAN_USERNAME)codeman_session cookie (24h TTL, auto-extends on activity)Always set CODEMAN_PASSWORD before exposing via tunnel — without it, anyone with the URL has full access to your sessions.
Typing a password on a phone keyboard is terrible. Codeman solves this with ephemeral single-use QR tokens — scan the code on your desktop, and your phone is instantly authenticated. No password prompt, no typing, no clipboard.
Desktop displays QR → Phone scans → GET /q/Xk9mQ3 → Server validates
→ Token atomically consumed (single-use) → Session cookie issued → 302 to /
→ Desktop notified: "Device authenticated via QR" → New QR auto-generated
Someone who only has the bare tunnel URL (without the QR) still hits the standard password prompt. The QR is the fast path; the password is the fallback.
The server maintains a rotating pool of short-lived, single-use tokens. Each token consists of a 256-bit secret (crypto.randomBytes(32)) paired with a 6-character base62 short code used as an opaque lookup key in the URL path. The QR code encodes a URL like https://abc-xyz.trycloudflare.com/q/Xk9mQ3 — the short code is a pointer, not the secret itself, so it never leaks through browser history, Referer headers, or Cloudflare edge logs.
Every 60 seconds, the server automatically rotates to a fresh token. The previous token remains valid for a 90-second grace period to handle the race where you scan right as rotation happens — after that, it's dead. Each token is single-use: the moment a phone successfully scans it, the token is atomically consumed and a new one is immediately generated for the desktop display.
The design is informed by "Demystifying the (In)Security of QR Code-based Login" (USENIX Security 2025), which found 47 of the top-100 websites vulnerable to QR auth attacks due to 6 critical design flaws across 42 CVEs. Codeman addresses all six:
| USENIX Flaw | Mitigation |
|---|---|
| Flaw-1: Missing single-use enforcement | Token atomically consumed on first scan — replays always fail |
| Flaw-2: Long-lived tokens | 60s TTL with 90s grace, auto-rotation via timer |
| Flaw-3: Predictable token generation | crypto.randomBytes(32) — 256-bit entropy. Short codes use rejection sampling to eliminate modulo bias |
| Flaw-4: Client-side token generation | Server-side only — tokens never leave the server until embedded in the QR |
| Flaw-5: Missing status notification | Desktop toast: "Device [IP] authenticated via QR (Safari). Not you? [Revoke]" — real-time QRLjacking detection |
| Flaw-6: Inadequate session binding | IP + User-Agent stored for audit. Manual session revocation via API. HttpOnly + Secure + SameSite=lax cookies |
Short codes are stored in a Map<shortCode, TokenRecord>. Validation uses Map.get() — a hash-based O(1) lookup that reveals nothing about the target string through response timing. There is no character-by-character string comparison anywhere in the hot path, eliminating timing side-channel attacks entirely.
QR auth has its own rate limiting, completely independent from password auth:
The URL is kept deliberately short (/q/ path + 6-char code = ~53-56 total characters) to target QR Version 4 (33x33 modules) instead of Version 5 (37x37). Smaller QR codes scan faster on budget phones — modern devices read Version 4 in 100-300ms. The /q/ prefix saves 7 bytes compared to /qr-auth/, which alone is the difference between QR versions.
The QR display auto-refreshes every 60 seconds via SSE with the SVG embedded directly in the event payload (~2-5KB) — no extra HTTP fetch, sub-50ms refresh. A countdown timer shows time remaining. A "Regenerate" button instantly invalidates all existing tokens and creates a fresh one (useful if you suspect the QR was photographed).
When someone authenticates via QR, the desktop shows a notification toast with the device's IP and browser — if it wasn't you, one click revokes all sessions.
| Threat | Why it doesn't work |
|---|---|
| QR screenshot shared | Single-use: consumed on first scan. 60s TTL: expired before the attacker can act. Desktop notification alerts you immediately. |
| Replay attack | Atomic single-use consumption + 60s TTL. Old URLs always return 401. |
| Cloudflare edge logs | Short code is an opaque 6-char lookup key, not the real 256-bit token. Single-use means replaying from logs always fails. |
| Brute force | 56.8 billion combinations, ~2 valid at any time, dual-layer rate limiting blocks well before statistical feasibility. |
| QRLjacking | 60s rotation forces real-time relay. Desktop toast provides instant detection. Self-hosted single-user context makes phishing implausible. |
| Timing attack | Hash-based Map lookup — no string comparison timing leak. |
| Session cookie theft | HttpOnly + Secure + SameSite=lax + 24h TTL. Manual revocation at POST /api/auth/revoke. |
| Platform | Model | Comparison |
|---|---|---|
| Discord | Long-lived token, no confirmation, repeatedly exploited | Codeman: single-use + TTL + notification |
| WhatsApp Web | Phone confirms "Link device?", ~60s rotation | Comparable rotation; WhatsApp adds explicit confirmation (acceptable tradeoff for single-user) |
| Signal | Ephemeral public key, E2E encrypted channel | Stronger crypto, but exploited by Russian state actors in 2025 via social engineering despite it |
Full design rationale, security analysis, and implementation details:
docs/qr-auth-plan.md
By default Codeman launches sessions with --dangerously-skip-permissions, so the web UI is by design a remote-code-execution surface for whoever can reach it — the whole security model exists to control who that is. (The startup permission mode is configurable; see below.) Recent hardening (v0.9.0 + v0.9.5) closes the browser-driven attack paths that bite self-hosted dev tools. Full model: docs/security-architecture.md. Found a vulnerability? See SECURITY.md for private disclosure and the list of known limitations.
127.0.0.1, reachable only from the same machine, so the no-password default is safe out of the box (the guided installer asks about network access and configures the binding + password for you). Binding a non-loopback host without CODEMAN_PASSWORD starts but prints a loud warning with three concrete fixes (set a password, loopback + an authenticated tunnel, or explicitly acknowledge with --allow-unauthenticated-network)CODEMAN_USERNAME (default admin) / CODEMAN_PASSWORD. Success issues an opaque 256-bit codeman_session cookie (randomBytes(32)) — validated server-side, not client-signed, so it can't be forged offline (24h TTL, auto-extend, device-context audit log)429 with Retry-After (15-min decay). A valid cookie or correct password recovers immediately even while an attacker hammers the same IP — important because all tunnel traffic shares one loopback IP. QR auth has its own separate limiter--dangerously-skip-permissions is only the default. App Settings → Claude CLI → Startup Mode can switch new sessions to Anthropic's classifier-guarded auto mode (low-prompt, needs Claude Code 2.1.207+), normal prompting, or an explicit allowed-tools list. In multi-user mode, non-granted users are forced to auto, and shell sessions / skip-permissions require an explicit per-user grantThese run for every request — before auth, even on the default no-password loopback install:
127.0.0.1 is rejected with 403 host not allowed before any handler runs. Allowed: localhost, any IP literal, the bind host, .ts.net / .trycloudflare.com / .cfargotunnel.com, the active managed tunnel, and CODEMAN_ALLOWED_HOSTS (add custom reverse-proxy domains here — comma-separated; exact host or leading-dot .suffix for subdomains)POST/PUT/PATCH/DELETE) the Origin must pass the same allowlist, else 403 cross-site request blocked. A missing Origin is allowed (so curl, the CLI, and Claude Code hooks keep working); only a present-but-foreign or opaque null origin is rejectedtext/plain bodies. The global parser no longer JSON-parses text/plain, closing the CORS "simple request" CSRF vector where a cross-site fetch could smuggle JSON into a write route with no preflight4003 on failure (anti-CSWSH)CLAUDE_CODE_* / OPENCODE_* / CODEX_* / GEMINI_* / GOOGLE_* env-prefix allowlist gates which settings each CLI can receiverealpath before boundary checks (no TOCTOU); .., absolute paths, and symlinks resolving outside the working dir are rejected. Caps: 10 MB text preview / 50 MB raw & download; /api/download blocklists sensitive paths (.env, *credentials*, ~/.ssh/, .aws/credentials). SVG/HTML is served octet-stream + nosniff + attachment so it downloads rather than executesContent-Security-Policy (default-src 'self', every exception enumerated), X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, HSTS over HTTPS, and CORS reflected only for localhost / 127.0.0.1 / ::1overrides; lockfile integrity is checked on every commit/PR (all entries resolve to registry.npmjs.org with sha512 hashes). Public assets are NUL-byte-scanned and node --check-validated in CICODEMAN_INSTANCE scopes both the tmux socket (-L codeman-<name>) and data dir (~/.codeman-<name>) so two instances never attach each other's live sessionsMobile login uses single-use, 60-second QR tokens — see QR Code Authentication above for the full design (it addresses all 6 flaws from USENIX Security 2025's QR-login study).
sc)If you prefer SSH (Termius, Blink, etc.), the sc command is a thumb-friendly session chooser:
sc # Interactive chooser sc 2 # Quick attach to session 2 sc -l # List sessions
Single-digit selection (1-9), color-coded status, token counts, auto-refresh. Detach with Ctrl+A D.
Ctrl bindings also accept Cmd on macOS.
| Shortcut | Action |
|---|---|
Ctrl/Cmd+W |
Kill active session |
Ctrl/Cmd/Option+K |
Find open session or start a new one |
Ctrl/Cmd+Tab |
Next session |
Alt/Option+[ / Alt/Option+] |
Previous / next session |
Alt/Option+1-Alt/Option+9 |
Switch to tab N (physical keys, so macOS Option layouts work) |
Ctrl+Shift+{ / Ctrl+Shift+} |
Move active tab left / right |
Ctrl/Cmd+L |
Clear terminal |
Ctrl+Shift+R |
Restore terminal size |
Ctrl+Shift+V |
Toggle voice input |
Ctrl/Cmd + / - |
Font size |
Ctrl/Cmd+? |
Keyboard help |
Shift+Enter |
Insert newline (sent to terminal) |
Escape |
Close panels & modals |
For AI agents and automation that control Codeman without a browser: an agent that spins up worker sessions, a CI bot, or Claude Code running inside a Codeman session orchestrating other sessions. Everything the UI does is HTTP + a CLI, so an agent can do it too.
When a CLI runs in a Codeman-managed session, these environment variables are set — read them instead of hardcoding anything:
| Variable | Meaning |
|---|---|
CODEMAN_MUX=1 |
You're in a managed tmux session. Never tmux kill-session / pkill claude / pkill tmux — you'll kill yourself or a sibling. |
CODEMAN_API_URL |
Base URL of the API (e.g. https://127.0.0.1:3000). Use it for every call below. |
CODEMAN_SESSION_ID |
Your own session id. Use it to avoid acting on yourself. |
CODEMAN_HOOK_SECRET_FILE |
Path to the hook secret (required on /api/hook-event while a managed tunnel is up). |
clientId and a monotonic per-session seq on POST …/input. The server de-duplicates, so a retry after a dropped connection can't double-deliver a prompt.CODEMAN_PASSWORD is set, send HTTP Basic auth (user admin or CODEMAN_USERNAME) or a codeman_session cookie. The default loopback install is passwordless. A missing Origin header is allowed, so plain curl works; cross-site browser origins are rejected (CSRF guard).{ "success": true, "data": … } (errors: { "success": false, "error", "errorCode" }). A few legacy GETs return bare bodies — handle both (body.data ?? body)./api/v1/* is a stable alias of /api/*.API="${CODEMAN_API_URL:-http://127.0.0.1:3000}"
# (add -u admin:"$CODEMAN_PASSWORD" to each call if a password is set)
# 1. See what's running
curl -s "$API/api/sessions" | jq '.data // .'
# 2. Spin up a worker session (a "case" = named working dir)
curl -s -X POST "$API/api/quick-start" \
-H 'Content-Type: application/json' \
-d '{"caseName":"refactor-auth","mode":"claude","effort":"high"}' | jq
# 3. Send a prompt into a session (exactly-once: clientId + seq)
curl -s -X POST "$API/api/sessions/$SID/input" \
-H 'Content-Type: application/json' \
-d '{"input":"Run the test suite and summarize failures","useMux":true,"clientId":"agent-1","seq":1}'
# 4. Read the terminal back
curl -s "$API/api/sessions/$SID/output" | jq -r '.data // .'
# 5. Stream live events (session output, agent activity, status)
curl -sN "$API/api/events" # Server-Sent Events
# 6. Schedule recurring work (cron-style job)
curl -s -X POST "$API/api/cron/jobs" \
-H 'Content-Type: application/json' \
-d '{"name":"nightly-deps","agentType":"claude","workingDir":"/home/me/proj",
"promptMode":"inline_text","promptText":"Update dependencies and open a PR",
"inputMode":"typed","scheduleType":"daily","dailyTime":"03:00",
"enabled":true,"concurrencyPolicy":"warn_only"}' | jq
# 7. Inspect background sub-agents and their transcripts
curl -s "$API/api/subagents" | jq '.data // .'
curl -s "$API/api/subagents/$AID/transcript" | jq -r '.data // .'
# 8. Whole-system snapshot (sessions, settings, respawn, stats)
curl -s "$API/api/status" | jq
The same operations are available as commands (codeman <cmd>, aliases in parentheses) — handy from a shell tool inside a session:
codeman session start -d /path/to/repo # (s) start a session codeman session list # list sessions codeman session logs <id> # tail output codeman task add "fix the failing test" # (t) queue a task codeman attach <path> # attach a Claude hook context
Codeman registers Claude Code hooks that POST /api/hook-event (permission_prompt, idle_prompt, stop, task_completed, …) so the dashboard reacts in real time. This endpoint is auth-exempt on loopback but, under a managed tunnel, requires the X-Codeman-Hook-Secret header (read it from $CODEMAN_HOOK_SECRET_FILE). You normally don't call this by hand — Codeman wires it up — but it's how the autonomy layers "see" what the agent is doing.
Full endpoint list and request/response shapes follow.
REST over Fastify — ~190 handlers across 20 route modules, plus an SSE stream and a WebSocket terminal channel. All responses use the ApiResponse<T> envelope ({success, data} / {success, error, errorCode}); /api/v1/* is a stable alias. A representative subset:
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/sessions |
List all |
POST |
/api/quick-start |
Create case + start session ({caseName?, mode?, effort?, envOverrides?}) |
POST |
/api/sessions/:id/input |
Send input ({input, useMux?, clientId?, seq?} — clientId+seq = exactly-once) |
GET |
/api/sessions/:id/output |
Read terminal output |
GET |
/api/sessions/unified |
Unified live + history list (Session Manager) — ?q=&limit= |
POST |
/api/sessions/:id/pin |
Pin/unpin in the Session Manager ({pinned}) |
PUT |
/api/session-order |
Sync tab order across devices ({order: [ids]}) |
DELETE |
/api/sessions/:id |
Delete session |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/sessions/:id/respawn/enable |
Enable with config + timer |
POST |
/api/sessions/:id/respawn/stop |
Stop controller |
PUT |
/api/sessions/:id/respawn/config |
Update config |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/orchestrator/start |
Start orchestration from a goal |
POST |
/api/orchestrator/approve |
Approve the generated plan |
GET |
/api/orchestrator/status |
Current phase + progress |
POST |
/api/orchestrator/stop |
Stop and clean up |
| Method | Endpoint | Description |
|---|---|---|
GET / POST |
/api/cron/jobs |
List / create cron jobs |
PUT / DELETE |
/api/cron/jobs/:id |
Update / delete a job |
PUT |
/api/cron/jobs/:id/enabled |
Enable / disable |
POST |
/api/cron/jobs/:id/run |
Run now |
GET |
/api/cron/jobs/:id/runs |
Run history |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/subagents |
List all background agents |
GET |
/api/subagents/:id |
Agent info and status |
GET |
/api/subagents/:id/transcript |
Full activity transcript |
DELETE |
/api/subagents/:id |
Kill agent process |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/events |
SSE stream |
GET |
/api/status |
Full app state |
POST |
/api/hook-event |
Hook callbacks |
GET |
/api/system/update/check |
Check for a new release |
POST |
/api/system/update |
Self-update (git-clone installs) |
POST |
/api/clipboard |
Push text to all connected browsers ({text}) |
GET |
/api/sessions/:id/run-summary |
Timeline + stats |
flowchart TB
subgraph Codeman["CODEMAN"]
subgraph Frontend["Frontend Layer"]
UI["Web UI<br/><small>xterm.js + Agent Windows</small>"]
API["REST API<br/><small>Fastify</small>"]
SSE["SSE Events<br/><small>/api/events</small>"]
end
subgraph Core["Core Layer"]
SM["Session Manager"]
S1["Session (PTY)"]
S2["Session (PTY)"]
RC["Respawn Controller"]
ORC["Orchestrator Loop"]
end
subgraph Detection["Detection Layer"]
SW["Subagent Watcher<br/><small>~/.claude/projects/*/subagents</small>"]
TW["Team Watcher<br/><small>~/.claude/teams/*</small>"]
end
subgraph Persistence["Persistence Layer"]
SCR["Mux Manager<br/><small>(tmux)</small>"]
SS["State Store<br/><small>state.json</small>"]
end
subgraph External["External"]
CLI["AI CLI<br/><small>Claude Code / OpenCode / Codex / Gemini</small>"]
BG["Background Agents<br/><small>(Task tool)</small>"]
end
end
UI <--> API
API <--> SSE
API --> SM
SM --> S1
SM --> S2
SM --> RC
SM --> ORC
SM --> SS
S1 --> SCR
S2 --> SCR
RC --> SCR
ORC --> SCR
SCR --> CLI
SW --> BG
SW --> SSE
TW --> SSE
Loading
npm install npx tsx src/index.ts web # Dev mode npm run build # Production build npm run test:ci # Run tests (the CI suite; browser suites need extra setup)
See CLAUDE.md for full documentation.
The codebase went through a comprehensive 7-phase refactoring that eliminated god objects, centralized configuration, and established modular architecture:
| Phase | What changed | Impact |
|---|---|---|
| Performance | Cached endpoints, SSE adaptive batching, buffer chunking | Sub-16ms terminal latency |
| Route extraction | server.ts split into 15 domain route modules + auth middleware + port interfaces |
−67% server.ts LOC (6,736 → 2,254) |
| Domain splitting | types.ts → 16 domain files, ralph-tracker → 7 files, respawn-controller → 5 files, session → 6 files |
No more god files |
| Frontend modules | app.js → 18 extracted modules across infra, domain & feature layers |
app.js core down to ~3.4K LOC |
| Config consolidation | ~70 scattered magic numbers → 10 domain-focused config files | Zero cross-file duplicates |
| Test infrastructure | Shared mock library, 12 route test files, consolidated MockSession | Testable route handlers via app.inject() |
Full details: docs/archive/code-structure-findings.md
xterm-zerolag-inputInstant keystroke feedback overlay for xterm.js. Eliminates perceived input latency over high-RTT connections by rendering typed characters immediately as a pixel-perfect DOM overlay. Zero dependencies, 6.1 kB gzipped, configurable prompt detection, CJK/emoji wide-character support, full state machine with 175 tests.
npm install xterm-zerolag-input
Codeman follows SemVer. What the version number actually
commits to — and what counts as internal (the HTTP/SSE API, on-disk state,
experimental features) — is spelled out in
docs/versioning-policy.md. If you script against
the HTTP API, pin to an exact version.
MIT — see LICENSE
Track sessions. Visualize agents. Control respawn. Let it run while you sleep.
If Codeman saves you time, a star helps other people find it.
Bug reports and feature ideas are welcome in Issues.
more like this
Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravi…
Skill Claude Code de veille tech francophone. Agrège les flux RSS (Journal du Hacker, Human Coders News…) et produit un…
A to-do list that runs your Claude Code agents — capture anywhere, dispatch in parallel, review from your phone.
search projects, people, and tags