Hermes Agent
An AI agent with advanced tool-calling capabilities, featuring a flexible toolsets system for organizing and managing tools.
Mission control for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence
Claude Code • OpenCode • Codex • Antigravity • Gemini • Pi • Grok • DeepSeek • OMP • Copilot • Terminal - One Dashboard • Any Device
The native Mac app: signed and notarized, Apple Silicon and Intel, macOS 11.3 or later. What it does
English • 简体中文
Codeman is a self-hosted mission control for AI coding agents. It spawns Claude Code, OpenCode, Codex, Antigravity, Gemini, Pi, Grok, DeepSeek Harness, OMP, or GitHub Copilot 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.
On a Mac you can skip the terminal: download Codeman.dmg, and the app installs Codeman for you, opens the one already running, or connects to one on another machine. More in On a Mac: the Codeman app.
codeman agent and an MCP server let one agent start workers in any CLI, prompt them, wait for them and collect their answers
curl -fsSL https://getcodeman.com/install | bash
This installs Node.js, tmux and a build toolchain if missing (node-pty ships no Linux prebuilds, so it compiles from source), clones Codeman to ~/.codeman/app, and builds it. It looks at what is already on the machine, asks at most three questions, then does all the work unattended and ends on the URL with a QR code for your phone. A few things worth knowing:
tailscale serve, so you get https://<machine>.<tailnet>.ts.net with a real certificate and your tailnet as the login, no password needed), any device on your network (0.0.0.0, with a strongly recommended password prompt), or 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. The highlighted default reflects what is already on the machine (Tailscale when it is already connected, your existing binding on a re-run), and a bare Enter never pulls in new software. If another app already owns :443 on your node, Codeman goes under https://<machine>.<tailnet>.ts.net/codeman or on a second port instead of replacing it. A bare codeman web started by hand still defaults to loopback.codeman-<hostname> (which also renames it for SSH, so the default is no); install.sh name does it later.~/.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 status prints the URLs and the QR code again; install.sh update, install.sh tailscale and install.sh uninstall also exist.sudo, one whose sudo rights the installer cannot confirm (outside the sudo, wheel and admin groups, with a password still required) or a Mac account that is not an administrator and has no Homebrew it can write to, the installer downloads Node.js from nodejs.org and the tmux project's own build into ~/.codeman/runtime, each checked against its SHA-256, and takes the source as an archive when there is no working git. --no-admin forces that route and CODEMAN_NO_ADMIN=0 turns it off. Two limits: Linux needs make, a C++ compiler and python3 to be there already, and the tmux download needs macOS 15 or newer. Details in the wiki.curl -fsSL https://getcodeman.com/install | bash -s -- --tailscale --service answers the questions from the command line (--lan, --local, --run, --no-start, --name <n>, --port <n>, --yes too). CI / headless: without a terminal attached, steps that would change your system abort with instructions instead of running silently; set CODEMAN_NONINTERACTIVE=1 to approve them for automation.You'll need at least one AI coding CLI installed — Claude Code, OpenCode, Codex, Antigravity, Gemini CLI, Pi, Grok Build, DeepSeek Harness, OMP, or GitHub Copilot CLI (any combination works; Gemini CLI is enterprise-only since Google's consumer cutover, and Antigravity is its successor). The installer detects whichever of the ten is present; if none is found, it offers to install any of them from a menu (DeepSeek excepted, since its npm package installs only a launcher with no runnable profile), 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.
Prefer Docker Compose? Clone the repo and run one script (Linux, Docker with the Compose v2 plugin):
git clone https://github.com/Ark0N/Codeman.git && cd Codeman bash docker/Start-Codeman.sh
The first run asks three questions (data folder, port, password; Enter takes the default, including a generated password), writes docker/.env for you, builds the image and ends on the URL once Codeman answers. The image already includes Claude Code, Codex, Gemini CLI and OpenCode. Codeman runs in a container and spawns Docker cases as sibling containers through the host socket. To update, use App Settings → Updates, or git pull and run the script again rather than a plain docker compose up, so the rebuilt image, refreshed volumes and entrypoint arrive together. See the Docker deployment guide for Unraid, direct Compose commands, storage and networking options.
Download Codeman.dmg · one universal app for Apple Silicon and Intel · macOS 11.3 or later
A small native app that opens Codeman in its own window. On a Mac without Codeman it installs it for you, with no Terminal and no administrator password, shows the install as a checklist, and offers to install a coding agent if the Mac has none yet. On a Mac that already runs Codeman it finds the server and opens it, or starts it when it is stopped. Under Another machine it connects to Codeman on your Mac mini, a home server or your tailnet, with the password kept in the Keychain, and can start an install over there through SSH. On a MacBook Pro with a PROLED XDR display, whose lid logo lights up, View > Blink Apple Logo blinks the logo by what your agents are doing, with a style of your own for working, for waiting for you and for needing permission; all three stay off until you choose. The app is Rust around macOS's own WebKit, not a bundled browser, so the download is under 5 MB.
Open Codeman.dmg, drag Codeman into Applications and open it from there. macOS asks once whether to open an app downloaded from the internet: click Open.
Codeman.dmg and drag it over the old copy.First launch, known limits and how to build it yourself: Installation in the wiki and packages/desktop/README.md.
To outlive the shell you started it in, without setting anything up:
codeman web -d # detach; logs to ~/.codeman/web.log codeman web --status # is it up, and on which pid codeman web --stop # graceful SIGTERM; agents keep running in tmux
-d waits until the server actually answers before reporting success, and refuses to start a second one on the same data dir (two servers sharing a tmux socket attach to each other's sessions).
To have it come back after a reboot, install it as a service instead. The installer's final menu does this for you (option 2); codeman service is the equivalent for an npm i -g aicodeman install:
codeman service install # systemd user unit (Linux) or LaunchAgent (macOS)
codeman service status
codeman service uninstall
service install writes the unit with your current PATH baked in, which matters more than it sounds: launchd hands a job /usr/bin:/bin:/usr/sbin:/sbin, so a Homebrew or nvm node, tmux or claude is invisible to a hand-written plist. It never copies CODEMAN_PASSWORD into the unit file; add that yourself if the service needs auth.
To write the unit by hand 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, Antigravity, Gemini CLI, Pi, Grok Build, DeepSeek Harness, or OMP). 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 accident; on Codex sessions the bar also shows ⇧← / ⇧→ (Shift+Left / Shift+Right: edit the last queued message / return through the prompt stack)visualViewport 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): the installer can set it up for you (choose Tailscale at the network-access prompt, or runbash ~/.codeman/app/install.sh tailscaleon an existing install). That gives youhttps://<your-machine>.<tailnet>.ts.netwith a real certificate: private to your tailnet, no password required, and PWA install + push notifications work on your phone. The installer ends on that URL with a QR code to scan, andbash ~/.codeman/app/install.sh statusprints it again any time.
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) codeman web -d # detach: survives closing the shell (--status, --stop) codeman service install # systemd/launchd service: comes back after reboots
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. Add Case creates one from scratch, links an existing folder, or clones a GitHub repo straight into one (Clone Repo). |
| CLI / run mode | Claude (default), OpenCode, Codex, Antigravity, Gemini, Pi, Grok, DeepSeek, OMP, Copilot, or Terminal (plain shell). |
| Model | Per-session model (App Settings → Models → New Claude sessions). 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). Prefer a list? App Settings → Appearance → Tabs moves it into a left sidebar with a filter box (Alt+B collapses it) or a vertical rail whose rows sort by activity: blocked on you first, then longest running, then most recently quiet. The rail also has a Search sessions box, named tab groups (a group's menu starts a New session straight into it), and a Focus section of shortcuts to the tabs you keep coming back to.xterm.js terminal; full TUIs render correctly. Type directly and press Enter to send. Shift+Enter inserts a newline..codeman-uploads/ folder in the workspace (it ignores itself in git and is swept after 7 days), and the file's path lands on your prompt, unsent.Ctrl+Shift+V (Deepgram Nova-3, or this machine's Claude Code login with no API key; auto-silence stop).~/ and relative paths and paths broken across lines. An .html report opens as a real page, Markdown opens rendered, and a folder opens as a grid of its files.Shift+drag selects text even while the CLI owns the mouse, right-click copies it, and Auto Copy (opt-in) copies a selection the moment you release it. A program that copies with OSC 52 (Claude Code's /copy, a tmux yank, vim) puts the text on your browser's clipboard too.| 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 → Header & Panels → Scheduling) |
| 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).codeman tui is a full-screen dashboard in the terminal (codeman tui --list to list, codeman tui 2 to attach straight to one).codeman web -d detaches from your shell (--status, --stop); codeman service install makes it a systemd user unit / macOS LaunchAgent that survives reboots. Both verify the server actually answers before reporting success, and both refuse to start a second server on one data dir. See Keep it running in the background.⚠️ 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.
Watch and drive up to six sessions side by side in one window. Click Tiles in the header (or press Ctrl+Shift+G) and your sessions open as a grid of live terminals: every tile takes your keystrokes and shows its agent's logo, model and state in its header. Right-click Tiles to choose 2, 4 or 6 tiles, and drag a tile by its header to move it. Click Tiles again to return to a single session. Desktop only (a window about 1180px wide or more). Full guide: Tile Grid.
Shift+wheel scrolls the tile's local history.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 |
Every tab tells you its state at a glance. A running session keeps its green status dot. When a session stops and waits for input, its tab turns yellow: steady ring, tinted background, yellow dot, with a slow breathing glow on top. When a permission prompt or question is blocking the agent, the tab turns red with a faster pulse. The base tint never blinks off, so even a split-second glance (or a screenshot) reads the true state; the ring stays visible while the tab is selected, and a page reload re-arms pending alerts from the server, so a blocked session can never hide behind a fresh-looking tab.
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)
codeman web -d runs the server detached with a pidfile, ~/.codeman/web.log, and verified startup (it polls the server until it answers, so a port clash never reads as success); codeman service install writes a systemd user unit (Linux) or LaunchAgent (macOS) with your shell's PATH baked in, so an nvm or Homebrew node, tmux and claude are actually found. Secrets are never written into unit filescodeman doctor on the server and lists Node, tmux, every agent CLI and the optional office tools with versions, paths and install hints. Besides the PATH, it also looks in each CLI's usual install directories (~/.local/bin, ~/.npm-global/bin and the like), so most installs are found under a service with a minimal PATH. Admin only in multi-user mode.● 3 uncommitted files, ↑ 2 commits not pushed, ⚠ merge conflicts, ✓ all committed and pushed. Click it for a draggable window listing the staged, not-staged, untracked and conflicted files (grouped under collapsed folders, or as a flat list if you turn that setting off) and the unpushed commits; click a file to see its diff (new files as all additions, deleted files as all removals), with Open file to jump to the viewer. A folder that holds several projects gets one collapsible section per repository found up to two levels down, all collapsed until you open them. Fetch in that window's header runs git fetch for the repositories it lists, on your click or on an interval you turn on (App Settings → Bottom bar, off by default; Session Options → Git overrides both the switch and the interval per session). Sessions open on the same folder share one run and each sees its result. Apart from that fetch Codeman never fetches or changes the repo, so "behind" is as of the last fetch; not shown for Docker or remote sessions.~/codeman-cases/<name> and registers it as a normal case, ready to run an agent in. It preflights the URL while you type (tells you whether it can be cloned anonymously and offers the repo's real branches and tags for the optional branch/tag field), fills the case name in from the URL, and lets you pick which CLI the Run button should use. Public repositories over https://; Codeman never collects or stores credentials~/codeman-cases. The target must be a new or empty folder; system directories, your home folder itself, credential trees such as ~/.ssh, and Codeman's own data folder are refused. Admin only in multi-user mode.CLAUDE_CODE_* vs OPENCODE_* vs CODEX_* vs ANTIGRAVITY_* vs GEMINI_*/GOOGLE_* vs PI_* vs GROK_*/XAI_* vs DSH_*/DEEPSEEK_* vs OMP_* vs COPILOT_*). See docs/opencode-integration.md, docs/pi-integration.md, docs/grok-integration.md, docs/deepseek-integration.md docs/omp-integration.md and docs/copilot-integration.mdPOST /api/model-endpoints; its models are discovered from /v1/models), apply it to a session (POST /api/sessions/:id/custom-model), and the CLI restarts in place on that endpoint. Verified live for Claude, OpenCode, Pi, Grok and OMP; Codex, Gemini and DeepSeek have documented gaps, Antigravity has no mechanism. A toolbar picker is the follow-up. See docs/custom-model-endpoints.mdspawn_worker <name> local, sendmark) and reads the answer back, so Claude plans and reviews in the cloud while the Mac does the bulk: summaries, first drafts, files that should stay here. See docs/local-models.mdhttp:// target works from a phone over HTTPS and through the tunnel, single-page apps route on their own paths, and a frame that reloads recovers itself. A localhost link an agent prints opens as a web tab automatically. See docs/web-tabs.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 → Terminal & Input.html path an agent printed (a test report, a coverage page, a dashboard) and the File Viewer shows it as a real page, with the scripts, styles, images and data files from its folder; the Page pill switches to the source and its Edit button. The page runs sandboxed with an opaque origin, so it cannot read Codeman's cookies, call Codeman's API or read files outside its own folder. Network access stays open, so a dashboard that loads Chart.js or fonts from a CDN works as it would from disk. Broad folders (/, your home, /tmp) are refused, so give a report a folder of its own. A .md path clicked in the terminal opens rendered too~/…, relative to the session folder, or broken across terminal lines are clickable in the terminal and the Response Viewer, and so are folders: a folder opens a grid of its files (renders, screenshots, reports) that you page through with the arrow keys/copy, a tmux yank, vim) puts the text on your browser's clipboard, in the main terminal and in every tile. Clipboard read requests are refused, and replaying a buffer never copies.codeman-bak. App Settings → Agents & CLIs; admin only in multi-user modecodeman notify. Outbound connections only; least-privilege bot setup in docs/discord-bridge.md. See the wiki page~/.codeman/backups/config; the folder, how many to keep (2–500) and how long (days, 0 = no limit) are settings, with Back up now and a verified Restore in Settings → System. Retention only ever deletes folders Codeman made and never the newest. See the wiki pageclaude at spawn and never writes into your settings files, plus Codex limits from its own app-server and the GitHub Copilot monthly premium-request quota (the server asks api.github.com for it every 10 minutes with Copilot's own sign-in token: on by default while Copilot CLI is installed and signed in, and turning off Plan Usage Limits stops it); per device, on for desktops and off for phonescache:until HH:MM) or cold, in the footer (when you have no statusline of your own) and in Session Options → Respawn, with the cost of a re-cache and a note after a compaction. Display only; it survives a restartcodeman:<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.execs into it and never starts, stops, restarts or removes it; one adopted container can back several cases at different directories, and copy an existing case pre-fills the form from a sibling. Admin-only in multi-user mode, since the container's mounts belong to whoever started it..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.realpath + stat probe, then a streamed cat, Range seeking included), so a clicked path opens the file on the machine the agent is on. Nothing is copied to the Codeman host apart from the diffs the Edit history panel keeps; editing and Office previews answer a clear 400 instead of a misleading 404.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: App Settings → System → Remote access → Cloudflare Tunnel
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.
The tunnel Codeman manages (App Settings → System → Remote access) refuses to start without a login (CODEMAN_PASSWORD or multi-user mode) unless CODEMAN_ALLOW_UNAUTHENTICATED_TUNNEL=1 or codeman web --allow-unauthenticated-tunnel acknowledges the exposure, or you accept the browser's confirm, which it asks before every enable while no login is set. CODEMAN_ALLOW_UNAUTHENTICATED_NETWORK covers only the network bind: it does not let the tunnel start.
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. Multi-user mode allows 50 failures per IP instead, plus 10 failed guesses per account from one IP; both are checked before the password, so past either a correct password from that IP also waits for the decay, while the account still logs in from another IP. QR auth has its own separate limiter--dangerously-skip-permissions is only the default. App Settings → Agents & CLIs → Claude → 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_* / ANTIGRAVITY_* / GEMINI_* / GOOGLE_* / PI_* / GROK_* / XAI_* / DSH_* / DEEPSEEK_* / OMP_* / COPILOT_* env-prefix allowlist gates which settings each CLI can receive, and the keys that could redirect a CLI's traffic (base URLs, config homes) are clamped for non-admin usersrealpath before boundary checks (no TOCTOU); .., absolute paths, and symlinks resolving outside the working dir are rejected. Caps: 10 MB text preview / 2 GB raw & download (CODEMAN_MAX_DOWNLOAD_BYTES; bodies stream and answer Range requests, so the cap is a sanity bound rather than memory protection); /api/download blocklists sensitive paths (.env, *credentials*, ~/.ssh/, .aws/credentials). SVG/HTML is served octet-stream + nosniff + attachment so it downloads rather than executes. The one place HTML renders is the File Viewer's page view: a separate /html-view/<capability>/ address scoped to the file's own folder, served under Content-Security-Policy: sandbox without allow-same-origin, so the page has an opaque origin and can never read Codeman's cookies or drive its API; hidden folders and broad roots (/, home, /tmp) are refused, and the capabilities are revoked on logoutContent-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).
codeman tui)A full-screen dashboard for your sessions, in the terminal. Same states as the web UI, because it is a client of the same server:
codeman tui # the dashboard codeman tui --list # numbered session list, then exit (scriptable) codeman tui 2 # attach straight to session 2 of that list
Sessions are grouped NEEDS YOU → WORKING → IDLE → RECENT, longest-waiting first. ↑↓/j/k select, 1-9 and [/] switch between sessions, Enter attaches into the tmux pane (F1 to come back). Inside a pane the bar across the top keeps the session strip visible and Alt+1-Alt+9 switch without leaving. y/n/digit answer a pending permission dialog right from the list, p sends a one-line prompt, n starts a session and opens straight into it, x kills one (y confirms), / searches, g shows the away digest, ? is help, q quits. Below 72 columns it drops the preview pane and becomes a single-column list, so it stays usable in Termius on a phone. With no server running it still starts in attach-only degraded mode.
The web UI remains the primary surface; see docs/tui.md for the full guide.
Ctrl bindings also accept Cmd on macOS.
| Shortcut | Action |
|---|---|
Ctrl+W |
Not a Codeman shortcut: it reaches the terminal (delete the previous word). Close Session has no default key and can be bound in App Settings → Shortcuts |
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) |
Alt/Option+B |
Collapse / expand the session sidebar (sidebar layout only) |
Ctrl+Shift+{ / Ctrl+Shift+} |
Move active tab left / right |
Ctrl/Cmd+C |
Copy selection, or interrupt when nothing is selected |
Ctrl+Shift+C |
Copy selection (never interrupts) |
Ctrl/Cmd+V |
Paste, or upload a clipboard image and paste its path |
Ctrl/Cmd+L |
Clear terminal |
Ctrl+Shift+R |
Restore terminal size |
Ctrl+Shift+V |
Toggle voice input |
Ctrl+Shift+G |
Open or close the tile grid |
Alt+Shift+Arrows |
Focus the tile to the left, right, above or below |
Ctrl+Shift+Arrows |
Move the focused tile one place (into an empty cell, or swap) |
Alt+Shift+Enter |
Zoom the focused tile, or restore the grid |
Ctrl/Cmd + / - |
Font size |
Ctrl/Cmd+? |
Keyboard help |
Shift+Enter |
Insert newline (sent to terminal) |
Shift+drag |
Select text in a pane whose mouse events go to the CLI |
| Right-click | Copy the selection (the native menu stays when nothing is selected) |
Shift+Wheel |
Scroll the local scrollback while the wheel is forwarded to the CLI |
Ctrl+Z |
Swallowed in agent sessions so a running CLI cannot be suspended; normal job control in a shell |
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.
Everything in this section also ships as an agent skill, skills/codeman, in the SKILL.md format Claude Code, Codex, Gemini CLI, OpenCode, Grok, OMP and most other agent CLIs read. Install it once and you never paste API docs into a prompt again. You ask for what you want in plain English, and the agent already sitting inside a Codeman session loads the recipes and drives the API itself. Full guide, with every install route, the recordings and what it will and will not do on its own: The Codeman Skill.
Usually there is nothing to install: since 1.50.0 every new session gets the skill in its folder, where its CLI looks for skills (the web UI row below, on by default). The copy ignores itself in git, so your repository never lists it as a new file. The other routes are for pi, or for a copy that does not depend on that setting.
| How | Command | Scope |
|---|---|---|
| Skills CLI | npx skills add Ark0N/Codeman --skill codeman -g |
Global, for any skills-aware agent (skills.sh); the way to give it to pi |
| Claude Code plugin | /plugin marketplace add Ark0N/Codeman then /plugin install codeman@codeman |
Global, through Claude Code's plugin manager; /plugin update codeman follows releases. Pick this OR a codeman skill install, not both: a Claude Code with both lists the skill twice (codeman and codeman:codeman). |
| Bundled CLI | codeman skill install |
Global (~/.claude/skills/codeman), for npm installs that never cloned the repo |
| Bundled CLI | codeman skill install --case <name> [--mode <mode>] |
One case only; --mode codex (or any other run mode) writes it where that CLI looks for skills |
| Web UI | App Settings → Agents & CLIs → All agent CLIs → Codeman Agent Skill | Auto-injects into each case on session create, where that session's CLI looks: .claude/skills/ for Claude Code, .agents/skills/ for Codex, Gemini CLI, OpenCode, Antigravity, Grok, DeepSeek and OMP; not pi, whose project skills trigger a trust prompt (agentSkillEnabled, SYNCED, on by default since 1.50.0; a Claude Code that also has the plugin lists the skill twice, both work, so drop one if the duplicate bothers you) |
codeman skill uninstall [--case <name>] reverses the CLI installs (with --case, every Codeman-managed copy in that case, whichever CLI's directory it sits in), and never touches a skills/codeman you wrote yourself.
That is the entire interface. No curl, no endpoint names, no session ids. These prompts work as written:
| You say | The skill does |
|---|---|
| "What sessions are running right now?" | Lists them with name, mode and status. Read-only, safe to ask anytime. |
"Start a shell worker on the myapp case, run the test suite, tell me if it passes." |
Spawns, waits on a split completion marker, reads back the exit code, cleans up. |
| "Spin up 3 workers for lint, typecheck and tests. Run them in parallel, report failures." | The fan-out flow: one session per task, all started first, then gathered as each finishes. |
"Have a claude worker on refactor-auth summarize src/session.ts, then close it." |
Spawns, runs the readiness ladder (first-run trust dialog included), send-and-wait, reads the clean transcript answer, deletes. |
"Use the local model to summarize every file in docs/, then check its summaries." |
Starts a worker on the model Codeman installed on this Mac, gives it the files, reads its answer back and reviews it in the cloud. Says why instead when no local model is installed. |
| "Watch session w4 and tell me if it gets stuck on a permission prompt." | Blocks on the blocked signal and surfaces the question to you. It never answers another session's prompt itself. |
One short line is enough. Here a Claude Code lead gets use codeman to start 5 claude workers, one per TODO item, and five real Claude Code workers power on, shown side by side in the tile grid:
The agent deletes every session it started. Watch the tabs appear and disappear in the dashboard while it works. With eight workers, each one gets its own tab, and a lineage line runs back to the lead that spawned it:
You: spin up 3 shell workers, run lint / typecheck / the frontend syntax check in parallel, and tell me which failed.
lint -> 9f2d8e5f dispatched typecheck -> aff9c691 dispatched 3 tabs appear in the dashboard syntax -> be9f1f15 dispatched lint DONE_lint_17909 rc=0 typecheck DONE_typecheck_3409 rc=0 gathered as each one finishes syntax DONE_syntax_18501 rc=0 deleted 9f2d8e5f, aff9c691, be9f1f15 tabs disappear
Those DONE_<task>_<random> strings are the skill's split marker trick, and they are why the fan-out is reliable on hook-less shell sessions: the typed line contains ${M}_17909, so only the command's real output ever contains DONE_17909. An unsplit marker would match the echo of your own keystrokes before the command had even run.
| File | Contents |
|---|---|
SKILL.md |
Safety rules, the ready-made fast path (spawn N workers, task them, collect), and the verb index. Always loaded. |
reference/verbs.md |
The 14 verbs in detail: readiness, send-and-wait, markers, interrupts, cleanup. On demand. |
reference/recipes.md |
8 worked flows: claude, DeepSeek Harness and shell workers, fan-out, blocked-worker watch, messaging fan-out. On demand. |
reference/endpoints.md |
Full endpoint tables, error codes, per-mode signal table, capacity limits. On demand. |
reference/messaging.md |
Talking to claude workers directly via Claude Code cross-session messaging. On demand. |
reference/history.md |
Reading saved Codex conversations offline with codeman history read: selectors, filters, forks, budgets. On demand. |
reference/inspection-and-staging.md |
Experimental and opt-in: bounded pane inspection and guarded submission with codeman agent inspect, stage and submit. On demand. |
Every recipe in there was verified against a live server, and the comments record the failure modes that were measured rather than guessed.
CODEMAN_MUX unset) it only reads saved Codex conversations offline (codeman history read) and refuses everything else, and it never guesses an API URL, so a global install costs an unrelated Claude Code session nothing.⚠️ The web UI injection is on by default since 1.50.0. A settings.json without the agentSkillEnabled key means on. Existing installs that stored off are switched on once at the first 1.50.0 start (every App Settings save on 1.49.0 or earlier wrote the old default, false, whether or not you touched this switch); turning it off afterwards sticks. Turning it off does not remove already-injected copies (a create-time sweep would yank the skill out from under other live sessions sharing that workspace). Remove them per case with codeman skill uninstall --case <name>.
The rest of this section is the manual path: the same operations as raw HTTP, for a CI bot, a shell script, or any agent without skill support.
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). |
\r. Programmatic input is sent as literal text, and Enter fires only when the input contains a carriage return: {"input":"run tests\r"}. Without the \r the text sits on the session's prompt unsubmitted (and a combined wait runs its full timeout on a turn that never started). With useMux, embedded newlines are kept and the lines go in as one paste, so "echo A\necho B\r" runs both commands in a shell; the check that re-presses a stranded Enter cannot reliably recognise a multi-line paste, so put long multi-line content in a file the agent is told to read.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). ⚠️ A 401 replies with the bare string Unauthorized, not the JSON envelope, so piping it into jq throws a parse error instead of showing the failure: check the status before parsing.{ "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/*.200 and wait.timedOut: true when nothing happened in time, so loop over short waits (60s is the default) rather than issuing one long call, because tunnels cut idle connections. wait.timeoutMs tells you the timeout the server actually applied after clamping (600s ceiling).claude and deepseek sessions emit stop; codex emits blocked too. Those come from hooks (Claude Code's own, and the DeepSeek Harness status bridge), except codex's blocked, which Codeman reads off its screen while an approval, question or trust dialog is up (answer it in the terminal). shell and the other external CLIs (opencode/gemini/antigravity/pi/grok/omp) accept only idle, working and exit, and codex adds blocked. Asking for stop explicitly on those is a 400; omitting until is always safe. ⚠️ On a shell session idle fires once, at startup, and never again, so send-and-wait there can only time out; synchronize hook-less sessions with a wait-output marker.{"signal":"exit","immediate":true} (that means not started, not crashed) until its PID exists, and a claude worker in a fresh case then sits on the CLI's trust dialog. Prompt it there and the wait resolves on idle in ~2s looking exactly like a finished turn, while the text sits stuck in the dialog. Recipe 2b below is the sequence that avoids it.# CODEMAN_API_URL is auto-set inside every Codeman session, correct scheme included. # The fallback below fits a stock install; on a --https install set the https:// URL # yourself and add -k to each curl (self-signed cert). 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 # 2b. Wait until that worker is actually READY (see rule 8): composer marker first, # first-run trust dialog only as the fallback. (Probing trust first and sending # a blind Enter misfires on re-runs: the dialog text stays in the buffer forever, # so the probe matches stale text and the Enter lands in a ready composer.) # Match single tokens: TUI text can reach the matcher without its spaces. until [ "$(curl -s "$API/api/sessions/$SID" | jq '.data.pid')" != null ]; do sleep 1; done R=$(curl -sG "$API/api/sessions/$SID/wait-output" --data-urlencode 'match=bypass' \ --data-urlencode 'from=buffer' --data-urlencode 'timeout=5000') if ! jq -e '.data.wait.matched' <<<"$R" >/dev/null; then T=$(curl -sG "$API/api/sessions/$SID/wait-output" --data-urlencode 'match=trust' \ --data-urlencode 'from=buffer' --data-urlencode 'timeout=2000') jq -e '.data.wait.matched' <<<"$T" >/dev/null && \ curl -s -X POST "$API/api/sessions/$SID/input" -H 'Content-Type: application/json' \ -d '{"input":"\r","useMux":true}' # accept the first-run trust dialog curl -sG "$API/api/sessions/$SID/wait-output" --data-urlencode 'match=bypass' \ --data-urlencode 'from=buffer' --data-urlencode 'timeout=45000' >/dev/null fi # 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\r","useMux":true,"clientId":"agent-1","seq":1}' # 4. Send a prompt and BLOCK until that turn is done (registers the wait before # writing, so it can't answer with the previous turn's idle state) curl -s -X POST "$API/api/sessions/$SID/input" \ -H 'Content-Type: application/json' \ -d '{"input":"Run the test suite and summarize failures\r","useMux":true, "clientId":"agent-1","seq":2,"wait":"stop,exit","waitTimeout":60000}' \ | jq '.data.wait' # -> {"signal":"stop","timedOut":false,"waitedMs":41230,...} # (`stop` is the definitive end-of-turn hook. Adding `idle` makes it resolve on a # spinner pause too, and on anything that redraws a ❯ prompt — like a dialog.) # 4b. Timed out? That's a 200, not a failure. Loop over short waits. curl -s "$API/api/sessions/$SID/wait?until=stop,exit&timeout=60000" | jq '.data.wait' # 4c. Or wait for a marker in the output (works for shell sessions too). # ⚠️ Unique per call (tmux repaints replay old screen text), and SPLIT so the # typed line never contains it: your own keystrokes echo into the output # stream, so an unsplit marker matches before the command has run. from=buffer # catches a marker that printed before the wait landed. N=$RANDOM curl -s -X POST "$API/api/sessions/$SID/input" -H 'Content-Type: application/json' \ -d "{\"input\":\"M=DONE; npm test; echo \${M}_$N rc=\$?\r\",\"useMux\":true}" curl -sG "$API/api/sessions/$SID/wait-output" \ --data-urlencode "match=DONE_$N" --data-urlencode 'from=buffer' \ --data-urlencode 'timeout=60000' | jq '.data.wait' # 5. Read the answer. claude / codex / deepseek sessions have last-response: it comes # from the transcript, not the screen, so no TUI frames or repaint noise. # ⚠️ Poll rather than read once: the transcript lands slightly after the stop # signal, so a read right after send-and-wait returns often comes back empty. for _ in $(seq 1 10); do TXT=$(curl -s "$API/api/sessions/$SID/last-response" | jq -r '.data.text') [ -n "$TXT" ] && break; sleep 1 done printf '%s\n' "$TXT" # 5b. Other modes (shell/opencode/gemini/antigravity/pi/grok/omp/copilot) have no transcript: # read the terminal. ⚠️ Use terminal?tail=, NOT /output: the latter's textOutput # is empty for every tmux-backed (i.e. every interactive) session. tail counts # BYTES, and what comes back is terminal data, ANSI included. curl -s "$API/api/sessions/$SID/terminal?tail=8000" | jq -r '.data.terminalBuffer' # 6. Stream live events (session output, agent activity, status) curl -sN "$API/api/events" # Server-Sent Events # 7. 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 # 8. Inspect background sub-agents and their transcripts curl -s "$API/api/subagents" | jq '.data // .' curl -s "$API/api/subagents/$AID/transcript" | jq -r '.data // .' # 9. 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> # show an attachment card for a local file codeman notify "<message>" [--urgent] [--file <path>] # post to this tab's Discord thread (see the Discord bridge) codeman tui --list # numbered session list (plain text when piped) codeman tui 3 # attach to session 3 of that list
codeman agent — session-to-session verbs in every CLI modeThe skill above drives the API with curl and jq from a preamble Codeman seeds per session. Where it does not reach (a pi or shell session, a machine without jq), the same environment is still there (CODEMAN_MUX=1, CODEMAN_SESSION_ID, CODEMAN_API_URL are exported into every pane), so codeman agent packages the same verbs as commands. It is a thin client over the endpoints listed under API: auth and ownership apply to every endpoint, including bounded inspection and guarded input. The inspect, stage and submit commands and their HTTP routes are experimental and opt-in: turn on Experimental agent coordination in App Settings (agentCoordinationEnabled, synced, off by default; until then the routes answer 403 FORBIDDEN naming it). Their commands, flags, paths, payloads and behavior may change or be removed in minor or patch releases, so pin a compatible version when automating them; see Experimental agent coordination. One line in a case's AGENTS.md is enough: "other sessions: codeman agent --help".
codeman agent ls # sessions; * marks this one SID=$(codeman agent spawn scratch-1 --mode claude) # quick-start + wait for the composer where the mode has a ready mark SID=$(codeman agent spawn --working-dir ~/src/app --mode codex) # no case: an existing directory (cwd by default) codeman agent send "$SID" 'review src/, then say DONE' --until stop,exit --timeout 300000 # --wait = default signal set codeman agent read "$SID" # last answer (as the server reads it for that mode) codeman agent read "$SID" --tail 3000 # terminal tail, ANSI stripped (every mode) codeman agent inspect "$SID" --max-bytes 8192 --json # recent evidence + opaque cursor (pane snapshots are labeled) codeman agent stage "$SID" --message-file /tmp/prompt.txt --json # no Enter; known local Codex composer only codeman agent submit "$SID" --stage-id "$STAGE_ID" --confirm-sha "$SHA256" --json # one conditional Enter codeman agent send "$SID" 'run the tests, then print WORKDONE followed by _4711' # hook-less modes (opencode, pi, …): ask for the marker in halves … codeman agent wait "$SID" --match WORKDONE_4711 # … and wait on the joined form, which the prompt's echo never contains codeman agent interrupt "$SID" # a bare ESC, conversation intact codeman agent post "$SID" 'context for later, not a prompt' # stored in its inbox, not typed into the pane codeman agent inbox --wait --timeout 300000 # your own inbox: blocks while nothing is new codeman agent ack <msg-id> # remove what you handled (read never removes) codeman agent rm "$SID" # any session except this one
Rules the commands enforce rather than document: they refuse outside a Codeman session and never guess a URL; send transmits printable text plus Enter only (a control byte such as Ctrl+C is app_exit in opencode — ESC exists solely as interrupt, which never appends Enter); rm refuses an empty id, an unprovable self id and a prefix match in either direction. Ids may be the 8-character prefixes ls prints (resolved through the list; an ambiguous prefix refuses, anything shorter than 8 characters refuses on every verb). A prompt that starts with - goes after -- (send "$SID" -- "- fix the bug"). The echo of the prompt you sent is output too: a --match marker that appears verbatim in the prompt matches at once, before the worker has done anything, so the prompt asks for it in halves. A remote session whose host is asleep answers a fire-and-forget send with buffered (Codeman wakes the host and types the prompt once the pane is back) or dropped (over the wake buffer's cap, nothing will be typed: exit 1). Exit codes: 0 delivered/matched/signal, 1 error, 2 timeout, 3 the worker exited or the wait ended without an answer (delivered:false, ended:true), 4 refused. spawn prints the id alone on stdout (prose goes to stderr), so SID=$(…) captures exactly the id. spawn with no case, or with --working-dir, starts the worker in that existing directory through POST /api/v1/sessions plus POST /api/v1/sessions/:id/interactive (or /shell) instead of quick-start, so no case is created and the directory is never labelled agent scratch; an empty --working-dir refuses (exit 4) rather than falling back to cwd, and so does a cwd that is your home directory (Codeman's hooks would land in ~/.claude), which needs an explicit --working-dir ~; if the start fails it still prints the created id and exits 1. --json prints the envelope's data for every verb, except a directory spawn, which prints { sessionId, workingDir, created: true }, plus ready when a readiness wait ran, readinessError when that call failed, and startupError when the start failed. --until stop on a mode without hook signals is the server's 400, passed through — the marker path (--match) is the answer there, exactly as for the skill.
codeman history read: saved Codex conversations, offlinecodeman history read reads Codex's own saved rollouts ($CODEX_HOME/sessions, else ~/.codex/sessions) straight from disk: no server, no managed session, nothing written. Unlike codeman agent it works anywhere, including on a machine where Codeman is not running.
codeman history read --session-id 019fc604 # one conversation, by native id or 8+ character prefix codeman history read --root-session-id <uuid> --topic auth # a conversation and every sub-agent or fork under it codeman history read --file /path/to/rollout.jsonl --format text
Filters keep whole user/assistant turns (--topic is a literal, case-insensitive match; --after/--before are inclusive UTC dates). A forked or spawned thread's copy of its parent's history is dropped, so every record belongs to the thread that wrote it. Output is JSON by default and carries complete: false whenever the evidence is partial (a file that could not be attributed, a compressed .jsonl.zst rollout, a record a running Codex is still writing, an unknown ancestor). Every bound fails closed and is raisable: --metadata-bytes (attributing the store, default 1 GiB), --max-entries (default 65536), --scan-bytes (the selected transcripts, default 16 MiB) and --max-bytes (output, default 8192). The full reference is skills/codeman/reference/history.md.
codeman mcp): drive Codeman from any MCP clientcodeman mcp is a Model Context Protocol server on stdio, so any MCP client (Claude Code, Claude Desktop, Codex, Gemini CLI, Cursor, ...) can start Codeman sessions in any installed agent CLI, prompt them, wait for them, read their answers and clean up, from inside Codeman or from outside it. Like codeman agent, it is a client of the HTTP API. The MCP server adds no routes of its own, and auth and ownership are unchanged.
# Claude Code claude mcp add codeman -e CODEMAN_API_URL=https://127.0.0.1:3000 -- codeman mcp
{ "mcpServers": { "codeman": { "command": "codeman", "args": ["mcp"], "env": { "CODEMAN_API_URL": "https://127.0.0.1:3000" } } } }
# Codex (~/.codex/config.toml): raise the 60 s default tool timeout for long waits
[mcp_servers.codeman]
command = "codeman"
args = ["mcp"]
env = { CODEMAN_API_URL = "https://127.0.0.1:3000" }
env_vars = ["CODEMAN_MUX", "CODEMAN_SESSION_ID"] # Codex passes MCP servers a minimal env; these let it know the Codeman session it runs in
tool_timeout_sec = 120
The JSON form is what Claude Desktop, Cursor and Gemini CLI take. Add "CODEMAN_PASSWORD" to env when the server has a password (or let it read the data dir's .env). Without the codeman command on PATH (a git checkout run without the installer), use node /path/to/Codeman/dist/index.js mcp.
| Tool | Does |
|---|---|
list_sessions |
Every session you can see, with its run mode, status, folder, and whether these tools may prompt it |
start_session |
A new worker in a case (a fresh scratch folder, or an existing case) in any installed CLI; waits until it can take a prompt |
send_prompt |
Types a one-line prompt and presses Enter; optionally waits for the end of the turn |
wait_for_session |
Waits for a signal (stop, blocked, idle, working, exit) or a literal marker in the output |
read_output |
The last answer, the whole conversation, or the last bytes of the terminal |
interrupt_session |
One Escape: stops the current turn, keeps the conversation |
delete_session |
Closes a worker (its case folder stays) |
Rules the server enforces rather than documents:
CODEMAN_API_URL or --url; without one it still starts and lists its tools, and every call says how to configure it.codeman mcp --allow-any-session lifts the limit, for a user who wants an assistant to drive their existing tabs. It never acts on the session it runs in.timed_out / still_working means call again._meta, server/discover) and the initialize handshake of 2024-11-05 to 2025-11-25.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 — ~230 handlers across 25 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?, wait?, waitTimeout?}: clientId+seq = exactly-once; wait blocks until the turn ends) |
GET |
/api/sessions/:id/terminal |
Read terminal output (?tail=<bytes>, ?full=1); the read path for interactive sessions |
GET |
/api/sessions/:id/output |
Parsed one-shot output (textOutput is empty for tmux-backed sessions) |
GET |
/api/sessions/:id/last-response |
The last answer as clean text, read from the transcript (claude, codex, deepseek) |
GET |
/api/sessions/:id/wait |
Block until a signal fires (?until=stop,idle,exit&timeout=&fresh=); a timeout is a 200 |
GET |
/api/sessions/:id/wait-output |
Block until a literal string appears (?match=&nocase=&from=now|buffer&timeout=) |
POST / GET |
/api/sessions/:id/inbox |
Agent inbox: post {text} / read every pending message (?wait= blocks while nothing is new); POST …/inbox/ack {ids} removes them, receiver only |
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]}) |
POST |
/api/sessions/:id/custom-model |
Restart the session's CLI on a saved custom endpoint ({endpointId, modelId}; {clear: true} returns to the native backend) |
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 / POST |
/api/model-endpoints |
List / save custom OpenAI-compatible endpoints (PUT / DELETE /:id; admin-only in multi-user mode) |
GET |
/api/sessions/:id/run-summary |
Timeline + stats |
Building something on top of Codeman?
docs/extending-codeman.mdis the integration guide: render your own UI as a tab, subscribe to the SSE event stream to react when an agent needs you, drive Codeman from a script, and the traps worth knowing before you start. Codeman has no plugin runtime on purpose, so an integration is just your own process talking HTTP.
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 / Antigravity / Gemini / Pi / Grok / DeepSeek / OMP / GitHub Copilot</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
npm install npx tsx src/index.ts web # Dev mode npm run build # Production build npm test # Run tests (same suite CI runs; browser/mobile/perf suites have their own commands)
See CLAUDE.md for full documentation.
Questions, setup help, and ideas live in GitHub Discussions: the Q&A section answers the most common ones (phone access, overnight runs, updating), and the roadmap gets decided in Ideas. Bugs go to issues; reports usually get a response within a day, and every release credits its reporters and contributors by name. Want to contribute? CONTRIBUTING.md has the map: skins, translations, and docs make great first PRs, and bigger features start life as a Discussion. And if you're proud of your rig, post it in Show and tell.
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, 5.7 kB minified and gzipped, configurable prompt detection, CJK/emoji wide-character support, full state machine with 276 tests. Typing cost does not grow with prompt length.
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.
An AI agent with advanced tool-calling capabilities, featuring a flexible toolsets system for organizing and managing tools.
Turn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.
The open-source app everyone uses to manage agents at work
Unified AI router with 356 providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.
Nueva versión disponible.