aicodeman 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +264 -90
- package/dist/attachment-magic.d.ts +13 -0
- package/dist/attachment-magic.d.ts.map +1 -1
- package/dist/attachment-magic.js +47 -0
- package/dist/attachment-magic.js.map +1 -1
- package/dist/attachment-registry.d.ts.map +1 -1
- package/dist/attachment-registry.js +13 -2
- package/dist/attachment-registry.js.map +1 -1
- package/dist/config/buffer-limits.d.ts +1 -2
- package/dist/config/buffer-limits.d.ts.map +1 -1
- package/dist/config/buffer-limits.js +1 -2
- package/dist/config/buffer-limits.js.map +1 -1
- package/dist/config/map-limits.d.ts +11 -0
- package/dist/config/map-limits.d.ts.map +1 -1
- package/dist/config/map-limits.js +11 -0
- package/dist/config/map-limits.js.map +1 -1
- package/dist/config/server-timing.d.ts +6 -0
- package/dist/config/server-timing.d.ts.map +1 -1
- package/dist/config/server-timing.js +9 -0
- package/dist/config/server-timing.js.map +1 -1
- package/dist/config/terminal-history.d.ts +11 -6
- package/dist/config/terminal-history.d.ts.map +1 -1
- package/dist/config/terminal-history.js +18 -8
- package/dist/config/terminal-history.js.map +1 -1
- package/dist/cron/cron-input.d.ts +30 -0
- package/dist/cron/cron-input.d.ts.map +1 -0
- package/dist/cron/cron-input.js +7 -0
- package/dist/cron/cron-input.js.map +1 -0
- package/dist/cron/cron-service.d.ts +92 -0
- package/dist/cron/cron-service.d.ts.map +1 -0
- package/dist/cron/cron-service.js +606 -0
- package/dist/cron/cron-service.js.map +1 -0
- package/dist/cron/cron-time.d.ts +29 -0
- package/dist/cron/cron-time.d.ts.map +1 -0
- package/dist/cron/cron-time.js +87 -0
- package/dist/cron/cron-time.js.map +1 -0
- package/dist/document-thumbnailer.d.ts +1 -1
- package/dist/document-thumbnailer.d.ts.map +1 -1
- package/dist/document-thumbnailer.js +11 -2
- package/dist/document-thumbnailer.js.map +1 -1
- package/dist/generated-artifact-attachments.d.ts +26 -0
- package/dist/generated-artifact-attachments.d.ts.map +1 -0
- package/dist/generated-artifact-attachments.js +61 -0
- package/dist/generated-artifact-attachments.js.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/mux-interface.d.ts +31 -5
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/remote-hosts.d.ts +66 -0
- package/dist/remote-hosts.d.ts.map +1 -0
- package/dist/remote-hosts.js +198 -0
- package/dist/remote-hosts.js.map +1 -0
- package/dist/services/unified-session-service.d.ts +115 -0
- package/dist/services/unified-session-service.d.ts.map +1 -0
- package/dist/services/unified-session-service.js +173 -0
- package/dist/services/unified-session-service.js.map +1 -0
- package/dist/session-cli-builder.d.ts +3 -1
- package/dist/session-cli-builder.d.ts.map +1 -1
- package/dist/session-cli-builder.js +26 -7
- package/dist/session-cli-builder.js.map +1 -1
- package/dist/session-pty-exit-breaker.d.ts +66 -0
- package/dist/session-pty-exit-breaker.d.ts.map +1 -0
- package/dist/session-pty-exit-breaker.js +77 -0
- package/dist/session-pty-exit-breaker.js.map +1 -0
- package/dist/session.d.ts +24 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +94 -11
- package/dist/session.js.map +1 -1
- package/dist/state-store.d.ts +14 -0
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js +49 -0
- package/dist/state-store.js.map +1 -1
- package/dist/tmux-manager.d.ts +94 -8
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +222 -26
- package/dist/tmux-manager.js.map +1 -1
- package/dist/types/api.d.ts +11 -0
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/api.js.map +1 -1
- package/dist/types/app-state.d.ts +5 -0
- package/dist/types/app-state.d.ts.map +1 -1
- package/dist/types/app-state.js.map +1 -1
- package/dist/types/cron.d.ts +88 -0
- package/dist/types/cron.d.ts.map +1 -0
- package/dist/types/cron.js +13 -0
- package/dist/types/cron.js.map +1 -0
- package/dist/types/session.d.ts +57 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/web/heic-jpeg-converter.d.ts +24 -0
- package/dist/web/heic-jpeg-converter.d.ts.map +1 -0
- package/dist/web/heic-jpeg-converter.js +76 -0
- package/dist/web/heic-jpeg-converter.js.map +1 -0
- package/dist/web/heic-jpeg-worker.d.ts +44 -0
- package/dist/web/heic-jpeg-worker.d.ts.map +1 -0
- package/dist/web/heic-jpeg-worker.js +78 -0
- package/dist/web/heic-jpeg-worker.js.map +1 -0
- package/dist/web/ports/cron-port.d.ts +9 -0
- package/dist/web/ports/cron-port.d.ts.map +1 -0
- package/dist/web/ports/cron-port.js +6 -0
- package/dist/web/ports/cron-port.js.map +1 -0
- package/dist/web/ports/index.d.ts +1 -0
- package/dist/web/ports/index.d.ts.map +1 -1
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.b0534652.js +38 -0
- package/dist/web/public/app.b0534652.js.br +0 -0
- package/dist/web/public/app.b0534652.js.gz +0 -0
- package/dist/web/public/{constants.1c779517.js → constants.5b18ed64.js} +65 -0
- package/dist/web/public/constants.5b18ed64.js.br +0 -0
- package/dist/web/public/constants.5b18ed64.js.gz +0 -0
- package/dist/web/public/cron-ui.js +328 -0
- package/dist/web/public/cron-ui.js.br +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
- package/dist/web/public/index.html +296 -25
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.63794d0b.js +1 -0
- package/dist/web/public/input-cjk.63794d0b.js.br +0 -0
- package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.3f91acef.js.gz +0 -0
- package/dist/web/public/mobile-handlers.b5a667ef.js.gz +0 -0
- package/dist/web/public/mobile.0644e3ac.css +1 -0
- package/dist/web/public/mobile.0644e3ac.css.br +0 -0
- package/dist/web/public/mobile.0644e3ac.css.gz +0 -0
- package/dist/web/public/notification-manager.ca9dc40d.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.772e92c9.js → panels-ui.ca03853f.js} +25 -17
- package/dist/web/public/panels-ui.ca03853f.js.br +0 -0
- package/dist/web/public/panels-ui.ca03853f.js.gz +0 -0
- package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
- package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
- package/dist/web/public/respawn-ui.2d249da9.js.gz +0 -0
- package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
- package/dist/web/public/session-ui.b51814ef.js +49 -0
- package/dist/web/public/session-ui.b51814ef.js.br +0 -0
- package/dist/web/public/session-ui.b51814ef.js.gz +0 -0
- package/dist/web/public/settings-ui.6c67c125.js +67 -0
- package/dist/web/public/settings-ui.6c67c125.js.br +0 -0
- package/dist/web/public/settings-ui.6c67c125.js.gz +0 -0
- package/dist/web/public/styles.e1ba7849.css +1 -0
- package/dist/web/public/styles.e1ba7849.css.br +0 -0
- package/dist/web/public/styles.e1ba7849.css.gz +0 -0
- package/dist/web/public/subagent-windows.69b60c35.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.dbf141fa.js +3 -0
- package/dist/web/public/terminal-ui.dbf141fa.js.br +0 -0
- package/dist/web/public/terminal-ui.dbf141fa.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +115 -1
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/cron-routes.d.ts +11 -0
- package/dist/web/routes/cron-routes.d.ts.map +1 -0
- package/dist/web/routes/cron-routes.js +70 -0
- package/dist/web/routes/cron-routes.js.map +1 -0
- package/dist/web/routes/index.d.ts +1 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +1 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/respawn-routes.d.ts.map +1 -1
- package/dist/web/routes/respawn-routes.js +3 -0
- package/dist/web/routes/respawn-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +610 -68
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/ws-routes.d.ts.map +1 -1
- package/dist/web/routes/ws-routes.js +56 -17
- package/dist/web/routes/ws-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +113 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +145 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +8 -2
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +75 -13
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-listener-wiring.d.ts +5 -1
- package/dist/web/session-listener-wiring.d.ts.map +1 -1
- package/dist/web/session-listener-wiring.js +23 -1
- package/dist/web/session-listener-wiring.js.map +1 -1
- package/dist/web/sse-events.d.ts +15 -0
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +17 -0
- package/dist/web/sse-events.js.map +1 -1
- package/dist/web/ws-connection-registry.d.ts +77 -0
- package/dist/web/ws-connection-registry.d.ts.map +1 -0
- package/dist/web/ws-connection-registry.js +99 -0
- package/dist/web/ws-connection-registry.js.map +1 -0
- package/package.json +3 -1
- package/dist/web/public/app.a8d73c32.js +0 -38
- package/dist/web/public/app.a8d73c32.js.br +0 -0
- package/dist/web/public/app.a8d73c32.js.gz +0 -0
- package/dist/web/public/constants.1c779517.js.br +0 -0
- package/dist/web/public/constants.1c779517.js.gz +0 -0
- package/dist/web/public/input-cjk.4e950e0a.js +0 -1
- package/dist/web/public/input-cjk.4e950e0a.js.br +0 -0
- package/dist/web/public/input-cjk.4e950e0a.js.gz +0 -0
- package/dist/web/public/mobile.8a9aff7a.css +0 -1
- package/dist/web/public/mobile.8a9aff7a.css.br +0 -0
- package/dist/web/public/mobile.8a9aff7a.css.gz +0 -0
- package/dist/web/public/panels-ui.772e92c9.js.br +0 -0
- package/dist/web/public/panels-ui.772e92c9.js.gz +0 -0
- package/dist/web/public/session-ui.da3e53f2.js +0 -36
- package/dist/web/public/session-ui.da3e53f2.js.br +0 -0
- package/dist/web/public/session-ui.da3e53f2.js.gz +0 -0
- package/dist/web/public/settings-ui.38feb956.js +0 -61
- package/dist/web/public/settings-ui.38feb956.js.br +0 -0
- package/dist/web/public/settings-ui.38feb956.js.gz +0 -0
- package/dist/web/public/styles.1b3e6147.css +0 -1
- package/dist/web/public/styles.1b3e6147.css.br +0 -0
- package/dist/web/public/styles.1b3e6147.css.gz +0 -0
- package/dist/web/public/terminal-ui.865f21f6.js +0 -3
- package/dist/web/public/terminal-ui.865f21f6.js.br +0 -0
- package/dist/web/public/terminal-ui.865f21f6.js.gz +0 -0
package/README.md
CHANGED
|
@@ -45,6 +45,7 @@ codeman web
|
|
|
45
45
|
<summary><strong>Run as a background service</strong></summary>
|
|
46
46
|
|
|
47
47
|
**Linux (systemd):**
|
|
48
|
+
|
|
48
49
|
```bash
|
|
49
50
|
mkdir -p ~/.config/systemd/user
|
|
50
51
|
cat > ~/.config/systemd/user/codeman-web.service << EOF
|
|
@@ -67,6 +68,7 @@ loginctl enable-linger $USER
|
|
|
67
68
|
```
|
|
68
69
|
|
|
69
70
|
**macOS (launchd):**
|
|
71
|
+
|
|
70
72
|
```bash
|
|
71
73
|
mkdir -p ~/Library/LaunchAgents
|
|
72
74
|
cat > ~/Library/LaunchAgents/com.codeman.web.plist << EOF
|
|
@@ -94,6 +96,7 @@ cat > ~/Library/LaunchAgents/com.codeman.web.plist << EOF
|
|
|
94
96
|
EOF
|
|
95
97
|
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.codeman.web.plist
|
|
96
98
|
```
|
|
99
|
+
|
|
97
100
|
</details>
|
|
98
101
|
|
|
99
102
|
<details>
|
|
@@ -104,10 +107,78 @@ wsl bash -c "curl -fsSL https://raw.githubusercontent.com/Ark0N/Codeman/master/i
|
|
|
104
107
|
```
|
|
105
108
|
|
|
106
109
|
Codeman requires tmux, so Windows users need [WSL](https://learn.microsoft.com/en-us/windows/wsl/install). 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](https://docs.anthropic.com/en/docs/claude-code), [OpenCode](https://opencode.ai), or [Codex](https://developers.openai.com/codex/cli)). After installing, `http://localhost:3000` is accessible from your Windows browser.
|
|
110
|
+
|
|
107
111
|
</details>
|
|
108
112
|
|
|
109
113
|
---
|
|
110
114
|
|
|
115
|
+
## Using Codeman — A Human's Guide
|
|
116
|
+
|
|
117
|
+
A start-to-finish walkthrough for driving Codeman from the browser. If you just installed, this is where to begin.
|
|
118
|
+
|
|
119
|
+
### 1. Launch the server
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
codeman web # localhost:3000 (loopback only — safe default)
|
|
123
|
+
codeman web --port 8080 # custom port (or set CODEMAN_PORT)
|
|
124
|
+
codeman web --https # self-signed TLS (only needed for remote access)
|
|
125
|
+
codeman web -H 0.0.0.0 # bind LAN — REQUIRES CODEMAN_PASSWORD (see Security)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Open the printed URL. The page is a single dashboard; everything below happens there.
|
|
129
|
+
|
|
130
|
+
### 2. Create your first session
|
|
131
|
+
|
|
132
|
+
Click **+ New Session** (or **Quick Start**). A session is one AI CLI running in its own tmux-backed terminal. You choose:
|
|
133
|
+
|
|
134
|
+
| Field | What it does |
|
|
135
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
136
|
+
| **Working directory / case** | The folder the agent operates in. A "case" is just a named working dir Codeman remembers. |
|
|
137
|
+
| **CLI / run mode** | `Claude` (default), `OpenCode`, `Codex`, `Gemini`, or `Terminal` (plain shell). |
|
|
138
|
+
| **Model** | Per-session model (App Settings → Claude Model). A soft default — `/model` still works in-session. |
|
|
139
|
+
| **Effort / Ultracode** | Reasoning effort (`low`–`max`) or `ultracode` for dynamic multi-agent workflows. Switchable anytime with `/effort`. |
|
|
140
|
+
|
|
141
|
+
Hit start — Codeman spawns the CLI via a real PTY and streams it to your browser over SSE.
|
|
142
|
+
|
|
143
|
+
### 3. Read the dashboard
|
|
144
|
+
|
|
145
|
+
- **Tabs (top)** — one per session. `Alt+1`-`9` to jump, `Ctrl+Tab` for next, drag to reorder.
|
|
146
|
+
- **Terminal (center)** — a real `xterm.js` terminal; full TUIs render correctly. Type directly and press **Enter** to send. `Shift+Enter` inserts a newline.
|
|
147
|
+
- **Side panels** — Respawn, Ralph, Orchestrator, Cron, Subagents, Settings (toggled from the toolbar).
|
|
148
|
+
|
|
149
|
+
### 4. Talk to the agent
|
|
150
|
+
|
|
151
|
+
- **Type prompts** straight into the terminal — input is delivered exactly-once even across reconnects (a dropped link never loses or double-sends a prompt).
|
|
152
|
+
- **Paste or drag-and-drop images** directly into the session.
|
|
153
|
+
- **Voice input** — `Ctrl+Shift+V` (Deepgram Nova-3, with auto-silence stop).
|
|
154
|
+
- **Attachments** — register external files/docs and preview Office/PDF inline.
|
|
155
|
+
|
|
156
|
+
### 5. Make it autonomous
|
|
157
|
+
|
|
158
|
+
| Mode | Use it for | Where |
|
|
159
|
+
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
160
|
+
| **Respawn** | Long unattended runs — auto-restarts the CLI on idle/limit, with adaptive timing. Presets: `solo-work`, `overnight-autonomous`, … | Respawn tab |
|
|
161
|
+
| **Ralph / Todo** | A self-driving loop that tracks a todo list and keeps working until done. | Ralph tab |
|
|
162
|
+
| **Orchestrator** | Turn one goal into a phased plan and drive it to completion across agents. | Orchestrator panel |
|
|
163
|
+
| **Cron** | Saved, named jobs on a schedule (`once`/`interval`/`daily`/`weekly`) that spawn a session and send a prompt when due. | ⏰ Cron button |
|
|
164
|
+
| **Auto-resume** | Automatically continue after a subscription rate-limit resets. | Respawn tab (top) |
|
|
165
|
+
|
|
166
|
+
### 6. Reach it from anywhere
|
|
167
|
+
|
|
168
|
+
- **Phone/tablet** — the UI is fully touch-optimized; scan the desktop **QR code** to log in without typing a password.
|
|
169
|
+
- **Outside your network** — `./scripts/tunnel.sh start` opens a Cloudflare tunnel (set `CODEMAN_PASSWORD` first).
|
|
170
|
+
- **SSH** — the `sc` chooser attaches to any session from a terminal (`sc` interactive, `sc 2` quick-attach, `sc -l` list).
|
|
171
|
+
|
|
172
|
+
### 7. Operate & maintain
|
|
173
|
+
|
|
174
|
+
- **App Settings** — model, effort, theme/skin, notifications, display toggles, per-CLI options.
|
|
175
|
+
- **Self-update** — git-clone installs update in place from **Settings → Updates**.
|
|
176
|
+
- **Deploy your own changes** — see [Development](#development).
|
|
177
|
+
|
|
178
|
+
> ⚠️ **Safety:** if you're working _inside_ a Codeman-managed session (`echo $CODEMAN_MUX` → `1`), never run `tmux kill-session` / `pkill claude` directly — use the web UI or `./scripts/tmux-manager.sh`.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
111
182
|
## Mobile-Optimized Web UI
|
|
112
183
|
|
|
113
184
|
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.
|
|
@@ -214,7 +285,7 @@ WATCHING → IDLE DETECTED → SEND UPDATE → /clear → /init → CONTINUE →
|
|
|
214
285
|
```
|
|
215
286
|
|
|
216
287
|
- **Multi-layer idle detection** — completion messages, AI-powered idle check, output silence, token stability
|
|
217
|
-
- **Auto-resume on usage limit**
|
|
288
|
+
- **Auto-resume on usage limit** _(opt-in, off by default)_ — when Claude halts on a subscription limit ("You've hit your limit · resets 3pm"), Codeman parses the reset time, waits it out plus a 2-minute safety buffer, then dismisses the rate-limit dialog and sends `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 tab
|
|
218
289
|
- **Circuit breaker** — prevents respawn thrashing when Claude is stuck (CLOSED -> HALF_OPEN -> OPEN states, tracks consecutive no-progress and repeated errors)
|
|
219
290
|
- **Health scoring** — 0-100 health score with component scores for cycle success, circuit breaker state, iteration progress, and stuck recovery
|
|
220
291
|
- **Built-in presets** — `solo-work` (3s idle, 60min), `subagent-workflow` (45s, 240min), `team-lead` (90s, 480min), `ralph-todo` (8s, 480min), `overnight-autonomous` (10s, 480min)
|
|
@@ -260,10 +331,10 @@ The title is templated into the served HTML on first byte, so it's correct from
|
|
|
260
331
|
|
|
261
332
|
### Smart Token Management
|
|
262
333
|
|
|
263
|
-
| Threshold
|
|
264
|
-
|
|
334
|
+
| Threshold | Action | Result |
|
|
335
|
+
| --------------- | --------------- | ---------------------------------- |
|
|
265
336
|
| **110k tokens** | Auto `/compact` | Context summarized, work continues |
|
|
266
|
-
| **140k tokens** | Auto `/clear`
|
|
337
|
+
| **140k tokens** | Auto `/clear` | Fresh start with `/init` |
|
|
267
338
|
|
|
268
339
|
### Notifications
|
|
269
340
|
|
|
@@ -298,8 +369,8 @@ PTY Output → 16ms Server Batch → DEC 2026 Wrap → SSE → Client rAF → xt
|
|
|
298
369
|
- **Effort & Ultracode** — set a per-session default effort (`low`–`max`) or enable **ultracode** (dynamic multi-agent workflows). Soft defaults only — switchable anytime with `/effort` in-session. Extended-thinking budget is configurable too
|
|
299
370
|
- **Voice input** — dictate prompts with Deepgram Nova-3 (Web Speech API fallback): toggle recording, auto-silence stop, live level meter (`Ctrl+Shift+V`)
|
|
300
371
|
- **Image input** — paste or drag-and-drop images straight into a session
|
|
301
|
-
- **Gesture control**
|
|
302
|
-
- **Multi-monitor span**
|
|
372
|
+
- **Gesture control** _(opt-in)_ — a MediaPipe hand-tracking overlay to grab/drag session windows and pinch buttons, hands-free. Enable with `CODEMAN_GESTURE=1` + App Settings → Display
|
|
373
|
+
- **Multi-monitor span** _(macOS)_ — one click opens a browser window maximized across all displays, so floating agent/gesture panels can cross the physical seam
|
|
303
374
|
- **CJK / IME input** — full composition support for Chinese / Japanese / Korean
|
|
304
375
|
- **OS notifications & hostname-aware titles** — desktop alerts and tab titles are prefixed `codeman:<host>` so multi-host setups stay unambiguous
|
|
305
376
|
|
|
@@ -372,14 +443,14 @@ Every **60 seconds**, the server automatically rotates to a fresh token. The pre
|
|
|
372
443
|
|
|
373
444
|
The design is informed by ["Demystifying the (In)Security of QR Code-based Login"](https://www.usenix.org/conference/usenixsecurity25/presentation/zhang-xin) (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:
|
|
374
445
|
|
|
375
|
-
| USENIX Flaw
|
|
376
|
-
|
|
377
|
-
| **Flaw-1**: Missing single-use enforcement | Token atomically consumed on first scan — replays always fail
|
|
378
|
-
| **Flaw-2**: Long-lived tokens
|
|
379
|
-
| **Flaw-3**: Predictable token generation
|
|
380
|
-
| **Flaw-4**: Client-side token generation
|
|
381
|
-
| **Flaw-5**: Missing status notification
|
|
382
|
-
| **Flaw-6**: Inadequate session binding
|
|
446
|
+
| USENIX Flaw | Mitigation |
|
|
447
|
+
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
|
|
448
|
+
| **Flaw-1**: Missing single-use enforcement | Token atomically consumed on first scan — replays always fail |
|
|
449
|
+
| **Flaw-2**: Long-lived tokens | 60s TTL with 90s grace, auto-rotation via timer |
|
|
450
|
+
| **Flaw-3**: Predictable token generation | `crypto.randomBytes(32)` — 256-bit entropy. Short codes use rejection sampling to eliminate modulo bias |
|
|
451
|
+
| **Flaw-4**: Client-side token generation | Server-side only — tokens never leave the server until embedded in the QR |
|
|
452
|
+
| **Flaw-5**: Missing status notification | Desktop toast: _"Device [IP] authenticated via QR (Safari). Not you? [Revoke]"_ — real-time QRLjacking detection |
|
|
453
|
+
| **Flaw-6**: Inadequate session binding | IP + User-Agent stored for audit. Manual session revocation via API. HttpOnly + Secure + SameSite=lax cookies |
|
|
383
454
|
|
|
384
455
|
#### Timing-Safe Lookup
|
|
385
456
|
|
|
@@ -404,23 +475,23 @@ When someone authenticates via QR, the desktop shows a notification toast with t
|
|
|
404
475
|
|
|
405
476
|
#### Threat Coverage
|
|
406
477
|
|
|
407
|
-
| Threat
|
|
408
|
-
|
|
409
|
-
| **QR screenshot shared** | Single-use: consumed on first scan. 60s TTL: expired before the attacker can act. Desktop notification alerts you immediately.
|
|
410
|
-
| **Replay attack**
|
|
411
|
-
| **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.
|
|
412
|
-
| **Brute force**
|
|
413
|
-
| **QRLjacking**
|
|
414
|
-
| **Timing attack**
|
|
415
|
-
| **Session cookie theft** | HttpOnly + Secure + SameSite=lax + 24h TTL. Manual revocation at `POST /api/auth/revoke`.
|
|
478
|
+
| Threat | Why it doesn't work |
|
|
479
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
480
|
+
| **QR screenshot shared** | Single-use: consumed on first scan. 60s TTL: expired before the attacker can act. Desktop notification alerts you immediately. |
|
|
481
|
+
| **Replay attack** | Atomic single-use consumption + 60s TTL. Old URLs always return 401. |
|
|
482
|
+
| **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. |
|
|
483
|
+
| **Brute force** | 56.8 billion combinations, ~2 valid at any time, dual-layer rate limiting blocks well before statistical feasibility. |
|
|
484
|
+
| **QRLjacking** | 60s rotation forces real-time relay. Desktop toast provides instant detection. Self-hosted single-user context makes phishing implausible. |
|
|
485
|
+
| **Timing attack** | Hash-based Map lookup — no string comparison timing leak. |
|
|
486
|
+
| **Session cookie theft** | HttpOnly + Secure + SameSite=lax + 24h TTL. Manual revocation at `POST /api/auth/revoke`. |
|
|
416
487
|
|
|
417
488
|
#### How It Compares
|
|
418
489
|
|
|
419
|
-
| Platform
|
|
420
|
-
|
|
421
|
-
| **Discord**
|
|
422
|
-
| **WhatsApp Web** | Phone confirms "Link device?", ~60s rotation
|
|
423
|
-
| **Signal**
|
|
490
|
+
| Platform | Model | Comparison |
|
|
491
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
492
|
+
| **Discord** | Long-lived token, no confirmation, [repeatedly exploited](https://owasp.org/www-community/attacks/Qrljacking) | Codeman: single-use + TTL + notification |
|
|
493
|
+
| **WhatsApp Web** | Phone confirms "Link device?", ~60s rotation | Comparable rotation; WhatsApp adds explicit confirmation (acceptable tradeoff for single-user) |
|
|
494
|
+
| **Signal** | Ephemeral public key, E2E encrypted channel | Stronger crypto, but [exploited by Russian state actors in 2025](https://cloud.google.com/blog/topics/threat-intelligence/russia-targeting-signal-messenger) via social engineering despite it |
|
|
424
495
|
|
|
425
496
|
> Full design rationale, security analysis, and implementation details: [`docs/qr-auth-plan.md`](docs/qr-auth-plan.md)
|
|
426
497
|
|
|
@@ -428,20 +499,20 @@ When someone authenticates via QR, the desktop shows a notification toast with t
|
|
|
428
499
|
|
|
429
500
|
## Security
|
|
430
501
|
|
|
431
|
-
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
|
|
502
|
+
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. 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`](docs/security-architecture.md). **Found a vulnerability?** See [`SECURITY.md`](SECURITY.md) for private disclosure and the list of known limitations.
|
|
432
503
|
|
|
433
504
|
### Network & access
|
|
434
505
|
|
|
435
|
-
- **Loopback by default** — binds `127.0.0.1`, reachable only from the same machine, so the no-password default is safe out of the box. Binding a non-loopback host without `CODEMAN_PASSWORD`
|
|
506
|
+
- **Loopback by default** — binds `127.0.0.1`, reachable only from the same machine, so the no-password default is safe out of the box. 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`)
|
|
436
507
|
- **Optional auth, real sessions** — HTTP Basic via `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)
|
|
437
|
-
- **Per-IP rate limiting** — 10 failed attempts → `429` with `Retry-After` (15-min decay). A valid cookie or correct password recovers
|
|
508
|
+
- **Per-IP rate limiting** — 10 failed attempts → `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
|
|
438
509
|
|
|
439
510
|
### Always-on browser hardening (v0.9.5)
|
|
440
511
|
|
|
441
512
|
These run for **every** request — before auth, even on the default no-password loopback install:
|
|
442
513
|
|
|
443
514
|
- **Host-header allowlist → blocks DNS rebinding.** A custom domain rebound to `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)
|
|
444
|
-
- **Cross-site Origin / CSRF guard.** On state-changing methods (`POST`/`PUT`/`PATCH`/`DELETE`) the `Origin` must pass the same allowlist, else `403 cross-site request blocked`. A
|
|
515
|
+
- **Cross-site Origin / CSRF guard.** On state-changing methods (`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 rejected
|
|
445
516
|
- **Raw `text/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 preflight
|
|
446
517
|
- **WebSocket origin validation.** The terminal WS upgrade runs the same Host + Origin check and closes with code `4003` on failure (anti-CSWSH)
|
|
447
518
|
- **XSS-escaped agent output.** AI-derived strings (tool names, command arguments, subagent descriptions) are HTML-escaped at every injection site before rendering in the subagent / activity panels
|
|
@@ -479,74 +550,177 @@ Single-digit selection (1-9), color-coded status, token counts, auto-refresh. De
|
|
|
479
550
|
|
|
480
551
|
> Ctrl bindings also accept Cmd on macOS.
|
|
481
552
|
|
|
482
|
-
| Shortcut
|
|
483
|
-
|
|
484
|
-
| `Ctrl/Cmd+W`
|
|
485
|
-
| `Ctrl/Cmd+
|
|
486
|
-
| `
|
|
487
|
-
| `Alt/Option+
|
|
488
|
-
| `
|
|
489
|
-
| `Ctrl/
|
|
490
|
-
| `Ctrl+
|
|
491
|
-
| `Ctrl+Shift+
|
|
492
|
-
| `Ctrl
|
|
493
|
-
| `Ctrl/Cmd
|
|
494
|
-
| `
|
|
495
|
-
| `
|
|
553
|
+
| Shortcut | Action |
|
|
554
|
+
| ------------------------------- | ------------------------------------------------------------- |
|
|
555
|
+
| `Ctrl/Cmd+W` | Kill active session |
|
|
556
|
+
| `Ctrl/Cmd/Option+K` | Find open session or start a new one |
|
|
557
|
+
| `Ctrl/Cmd+Tab` | Next session |
|
|
558
|
+
| `Alt/Option+[` / `Alt/Option+]` | Previous / next session |
|
|
559
|
+
| `Alt/Option+1`-`Alt/Option+9` | Switch to tab N (physical keys, so macOS Option layouts work) |
|
|
560
|
+
| `Ctrl+Shift+{` / `Ctrl+Shift+}` | Move active tab left / right |
|
|
561
|
+
| `Ctrl/Cmd+L` | Clear terminal |
|
|
562
|
+
| `Ctrl+Shift+R` | Restore terminal size |
|
|
563
|
+
| `Ctrl+Shift+V` | Toggle voice input |
|
|
564
|
+
| `Ctrl/Cmd +` / `-` | Font size |
|
|
565
|
+
| `Ctrl/Cmd+?` | Keyboard help |
|
|
566
|
+
| `Shift+Enter` | Insert newline (sent to terminal) |
|
|
567
|
+
| `Escape` | Close panels & modals |
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## Driving Codeman from an Agent — Programmatic Guide
|
|
572
|
+
|
|
573
|
+
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.
|
|
574
|
+
|
|
575
|
+
### Detect that you're inside Codeman
|
|
576
|
+
|
|
577
|
+
When a CLI runs in a Codeman-managed session, these environment variables are set — read them instead of hardcoding anything:
|
|
578
|
+
|
|
579
|
+
| Variable | Meaning |
|
|
580
|
+
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
581
|
+
| `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. |
|
|
582
|
+
| `CODEMAN_API_URL` | Base URL of the API (e.g. `https://127.0.0.1:3000`). Use it for every call below. |
|
|
583
|
+
| `CODEMAN_SESSION_ID` | _Your own_ session id. Use it to avoid acting on yourself. |
|
|
584
|
+
| `CODEMAN_HOOK_SECRET_FILE` | Path to the hook secret (required on `/api/hook-event` while a managed tunnel is up). |
|
|
585
|
+
|
|
586
|
+
### Rules of the road (read before you POST)
|
|
587
|
+
|
|
588
|
+
1. **Single-line input only.** Programmatic input is sent as literal text **+ Enter** in one shot. Multi-line strings break the agent TUI (Ink) — send one line, or split into multiple calls.
|
|
589
|
+
2. **Make input idempotent.** Include a stable `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.
|
|
590
|
+
3. **Auth.** If `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).
|
|
591
|
+
4. **Response envelope.** Most endpoints return `{ "success": true, "data": … }` (errors: `{ "success": false, "error", "errorCode" }`). A few legacy GETs return bare bodies — **handle both** (`body.data ?? body`).
|
|
592
|
+
5. **`/api/v1/*`** is a stable alias of `/api/*`.
|
|
593
|
+
|
|
594
|
+
### Recipes
|
|
595
|
+
|
|
596
|
+
```bash
|
|
597
|
+
API="${CODEMAN_API_URL:-http://127.0.0.1:3000}"
|
|
598
|
+
# (add -u admin:"$CODEMAN_PASSWORD" to each call if a password is set)
|
|
599
|
+
|
|
600
|
+
# 1. See what's running
|
|
601
|
+
curl -s "$API/api/sessions" | jq '.data // .'
|
|
602
|
+
|
|
603
|
+
# 2. Spin up a worker session (a "case" = named working dir)
|
|
604
|
+
curl -s -X POST "$API/api/quick-start" \
|
|
605
|
+
-H 'Content-Type: application/json' \
|
|
606
|
+
-d '{"caseName":"refactor-auth","mode":"claude","effort":"high"}' | jq
|
|
607
|
+
|
|
608
|
+
# 3. Send a prompt into a session (exactly-once: clientId + seq)
|
|
609
|
+
curl -s -X POST "$API/api/sessions/$SID/input" \
|
|
610
|
+
-H 'Content-Type: application/json' \
|
|
611
|
+
-d '{"input":"Run the test suite and summarize failures","useMux":true,"clientId":"agent-1","seq":1}'
|
|
612
|
+
|
|
613
|
+
# 4. Read the terminal back
|
|
614
|
+
curl -s "$API/api/sessions/$SID/output" | jq -r '.data // .'
|
|
615
|
+
|
|
616
|
+
# 5. Stream live events (session output, agent activity, status)
|
|
617
|
+
curl -sN "$API/api/events" # Server-Sent Events
|
|
618
|
+
|
|
619
|
+
# 6. Schedule recurring work (cron-style job)
|
|
620
|
+
curl -s -X POST "$API/api/cron/jobs" \
|
|
621
|
+
-H 'Content-Type: application/json' \
|
|
622
|
+
-d '{"name":"nightly-deps","agentType":"claude","workingDir":"/home/me/proj",
|
|
623
|
+
"promptMode":"inline_text","promptText":"Update dependencies and open a PR",
|
|
624
|
+
"inputMode":"typed","scheduleType":"daily","dailyTime":"03:00",
|
|
625
|
+
"enabled":true,"concurrencyPolicy":"warn_only"}' | jq
|
|
626
|
+
|
|
627
|
+
# 7. Inspect background sub-agents and their transcripts
|
|
628
|
+
curl -s "$API/api/subagents" | jq '.data // .'
|
|
629
|
+
curl -s "$API/api/subagents/$AID/transcript" | jq -r '.data // .'
|
|
630
|
+
|
|
631
|
+
# 8. Whole-system snapshot (sessions, settings, respawn, stats)
|
|
632
|
+
curl -s "$API/api/status" | jq
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### Or use the bundled CLI
|
|
636
|
+
|
|
637
|
+
The same operations are available as commands (`codeman <cmd>`, aliases in parentheses) — handy from a shell tool inside a session:
|
|
638
|
+
|
|
639
|
+
```bash
|
|
640
|
+
codeman session start -d /path/to/repo # (s) start a session
|
|
641
|
+
codeman session list # list sessions
|
|
642
|
+
codeman session logs <id> # tail output
|
|
643
|
+
codeman task add "fix the failing test" # (t) queue a task
|
|
644
|
+
codeman ralph start --min-hours 8 # (r) launch the autonomous loop
|
|
645
|
+
codeman attach <path> # attach a Claude hook context
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
### Hooks (events flowing _back_ to Codeman)
|
|
649
|
+
|
|
650
|
+
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.
|
|
651
|
+
|
|
652
|
+
> Full endpoint list and request/response shapes follow.
|
|
496
653
|
|
|
497
654
|
---
|
|
498
655
|
|
|
499
656
|
## API
|
|
500
657
|
|
|
501
|
-
REST over Fastify — **~
|
|
658
|
+
REST over Fastify — **~160 handlers across 18 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:
|
|
502
659
|
|
|
503
660
|
### Sessions
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
|
507
|
-
| `
|
|
508
|
-
| `
|
|
509
|
-
| `POST`
|
|
661
|
+
|
|
662
|
+
| Method | Endpoint | Description |
|
|
663
|
+
| -------- | -------------------------- | ---------------------------------------------------------------------------------- |
|
|
664
|
+
| `GET` | `/api/sessions` | List all |
|
|
665
|
+
| `POST` | `/api/quick-start` | Create case + start session (`{caseName?, mode?, effort?, envOverrides?}`) |
|
|
666
|
+
| `POST` | `/api/sessions/:id/input` | Send input (`{input, useMux?, clientId?, seq?}` — `clientId`+`seq` = exactly-once) |
|
|
667
|
+
| `GET` | `/api/sessions/:id/output` | Read terminal output |
|
|
668
|
+
| `DELETE` | `/api/sessions/:id` | Delete session |
|
|
510
669
|
|
|
511
670
|
### Respawn
|
|
512
|
-
|
|
513
|
-
|
|
671
|
+
|
|
672
|
+
| Method | Endpoint | Description |
|
|
673
|
+
| ------ | ---------------------------------- | -------------------------- |
|
|
514
674
|
| `POST` | `/api/sessions/:id/respawn/enable` | Enable with config + timer |
|
|
515
|
-
| `POST` | `/api/sessions/:id/respawn/stop`
|
|
516
|
-
| `PUT`
|
|
675
|
+
| `POST` | `/api/sessions/:id/respawn/stop` | Stop controller |
|
|
676
|
+
| `PUT` | `/api/sessions/:id/respawn/config` | Update config |
|
|
517
677
|
|
|
518
678
|
### Ralph / Todo
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
|
522
|
-
| `
|
|
679
|
+
|
|
680
|
+
| Method | Endpoint | Description |
|
|
681
|
+
| ------ | -------------------------------- | ---------------------- |
|
|
682
|
+
| `GET` | `/api/sessions/:id/ralph-state` | Get loop state + todos |
|
|
683
|
+
| `POST` | `/api/sessions/:id/ralph-config` | Configure tracking |
|
|
523
684
|
|
|
524
685
|
### Orchestrator
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
|
528
|
-
| `POST` | `/api/orchestrator/
|
|
529
|
-
| `
|
|
530
|
-
| `
|
|
686
|
+
|
|
687
|
+
| Method | Endpoint | Description |
|
|
688
|
+
| ------ | --------------------------- | ------------------------------- |
|
|
689
|
+
| `POST` | `/api/orchestrator/start` | Start orchestration from a goal |
|
|
690
|
+
| `POST` | `/api/orchestrator/approve` | Approve the generated plan |
|
|
691
|
+
| `GET` | `/api/orchestrator/status` | Current phase + progress |
|
|
692
|
+
| `POST` | `/api/orchestrator/stop` | Stop and clean up |
|
|
693
|
+
|
|
694
|
+
### Cron (scheduled jobs)
|
|
695
|
+
|
|
696
|
+
| Method | Endpoint | Description |
|
|
697
|
+
| ---------------- | ---------------------------- | ----------------------- |
|
|
698
|
+
| `GET` / `POST` | `/api/cron/jobs` | List / create cron jobs |
|
|
699
|
+
| `PUT` / `DELETE` | `/api/cron/jobs/:id` | Update / delete a job |
|
|
700
|
+
| `PUT` | `/api/cron/jobs/:id/enabled` | Enable / disable |
|
|
701
|
+
| `POST` | `/api/cron/jobs/:id/run` | Run now |
|
|
702
|
+
| `GET` | `/api/cron/jobs/:id/runs` | Run history |
|
|
531
703
|
|
|
532
704
|
### Subagents
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
|
536
|
-
| `GET`
|
|
537
|
-
| `GET`
|
|
538
|
-
| `
|
|
705
|
+
|
|
706
|
+
| Method | Endpoint | Description |
|
|
707
|
+
| -------- | ------------------------------- | -------------------------- |
|
|
708
|
+
| `GET` | `/api/subagents` | List all background agents |
|
|
709
|
+
| `GET` | `/api/subagents/:id` | Agent info and status |
|
|
710
|
+
| `GET` | `/api/subagents/:id/transcript` | Full activity transcript |
|
|
711
|
+
| `DELETE` | `/api/subagents/:id` | Kill agent process |
|
|
539
712
|
|
|
540
713
|
### System
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
|
544
|
-
| `GET`
|
|
545
|
-
| `
|
|
546
|
-
| `
|
|
547
|
-
| `
|
|
548
|
-
| `POST` | `/api/
|
|
549
|
-
| `
|
|
714
|
+
|
|
715
|
+
| Method | Endpoint | Description |
|
|
716
|
+
| ------ | ------------------------------- | ---------------------------------------------- |
|
|
717
|
+
| `GET` | `/api/events` | SSE stream |
|
|
718
|
+
| `GET` | `/api/status` | Full app state |
|
|
719
|
+
| `POST` | `/api/hook-event` | Hook callbacks |
|
|
720
|
+
| `GET` | `/api/system/update/check` | Check for a new release |
|
|
721
|
+
| `POST` | `/api/system/update` | Self-update (git-clone installs) |
|
|
722
|
+
| `POST` | `/api/clipboard` | Push text to all connected browsers (`{text}`) |
|
|
723
|
+
| `GET` | `/api/sessions/:id/run-summary` | Timeline + stats |
|
|
550
724
|
|
|
551
725
|
---
|
|
552
726
|
|
|
@@ -624,14 +798,14 @@ See [CLAUDE.md](./CLAUDE.md) for full documentation.
|
|
|
624
798
|
|
|
625
799
|
The codebase went through a comprehensive 7-phase refactoring that eliminated god objects, centralized configuration, and established modular architecture:
|
|
626
800
|
|
|
627
|
-
| Phase
|
|
628
|
-
|
|
629
|
-
| **Performance**
|
|
630
|
-
| **Route extraction**
|
|
631
|
-
| **Domain splitting**
|
|
632
|
-
| **Frontend modules**
|
|
633
|
-
| **Config consolidation** | ~70 scattered magic numbers → 10 domain-focused config files
|
|
634
|
-
| **Test infrastructure**
|
|
801
|
+
| Phase | What changed | Impact |
|
|
802
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------ |
|
|
803
|
+
| **Performance** | Cached endpoints, SSE adaptive batching, buffer chunking | Sub-16ms terminal latency |
|
|
804
|
+
| **Route extraction** | `server.ts` split into 15 domain route modules + auth middleware + port interfaces | **−67%** server.ts LOC (6,736 → 2,254) |
|
|
805
|
+
| **Domain splitting** | `types.ts` → 16 domain files, `ralph-tracker` → 7 files, `respawn-controller` → 5 files, `session` → 6 files | No more god files |
|
|
806
|
+
| **Frontend modules** | `app.js` → 18 extracted modules across infra, domain & feature layers | app.js core down to **~3.4K LOC** |
|
|
807
|
+
| **Config consolidation** | ~70 scattered magic numbers → 10 domain-focused config files | Zero cross-file duplicates |
|
|
808
|
+
| **Test infrastructure** | Shared mock library, 12 route test files, consolidated MockSession | Testable route handlers via `app.inject()` |
|
|
635
809
|
|
|
636
810
|
Full details: [`docs/archive/code-structure-findings.md`](docs/archive/code-structure-findings.md)
|
|
637
811
|
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Parses terminal magic links that request attachment cards.
|
|
3
3
|
*/
|
|
4
|
+
export interface TerminalAttachmentRequest {
|
|
5
|
+
path: string;
|
|
6
|
+
source: 'external' | 'codex-generated';
|
|
7
|
+
}
|
|
8
|
+
export interface ParseTerminalAttachmentOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Enable the Codex `Saved to: file://...` scanner. Only codex-mode sessions
|
|
11
|
+
* may set this — the relaxed codex-generated trust policy must never be
|
|
12
|
+
* reachable from other modes' (prompt-injectable) terminal output.
|
|
13
|
+
*/
|
|
14
|
+
codexArtifacts?: boolean;
|
|
15
|
+
}
|
|
4
16
|
export declare function parseAttachmentMagicLinks(data: string): string[];
|
|
17
|
+
export declare function parseTerminalAttachmentRequests(data: string, options?: ParseTerminalAttachmentOptions): TerminalAttachmentRequest[];
|
|
5
18
|
//# sourceMappingURL=attachment-magic.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment-magic.d.ts","sourceRoot":"","sources":["../src/attachment-magic.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"attachment-magic.d.ts","sourceRoot":"","sources":["../src/attachment-magic.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,GAAG,iBAAiB,CAAC;CACxC;AAED,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAEhE;AAED,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,8BAAmC,GAC3C,yBAAyB,EAAE,CAe7B"}
|
package/dist/attachment-magic.js
CHANGED
|
@@ -2,9 +2,30 @@
|
|
|
2
2
|
* @fileoverview Parses terminal magic links that request attachment cards.
|
|
3
3
|
*/
|
|
4
4
|
import { isAbsolute } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
5
6
|
import { isSupportedAttachmentExtension } from './attachment-registry.js';
|
|
7
|
+
import { stripAnsi } from './utils/index.js';
|
|
6
8
|
const MAGIC_LINK_RE = /codeman:\/\/attach\?([^\s<>"']+)/g;
|
|
9
|
+
const CODEX_SAVED_FILE_RE = /\bSaved to:\s*(file:\/\/[^\s<>"']+)/gi;
|
|
7
10
|
export function parseAttachmentMagicLinks(data) {
|
|
11
|
+
return parseMagicAttachmentRequests(data).map((request) => request.path);
|
|
12
|
+
}
|
|
13
|
+
export function parseTerminalAttachmentRequests(data, options = {}) {
|
|
14
|
+
const results = [];
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
const requests = options.codexArtifacts
|
|
17
|
+
? [...parseMagicAttachmentRequests(data), ...parseCodexGeneratedArtifactRequests(data)]
|
|
18
|
+
: parseMagicAttachmentRequests(data);
|
|
19
|
+
for (const request of requests) {
|
|
20
|
+
const key = `${request.source}:${request.path}`;
|
|
21
|
+
if (seen.has(key))
|
|
22
|
+
continue;
|
|
23
|
+
seen.add(key);
|
|
24
|
+
results.push(request);
|
|
25
|
+
}
|
|
26
|
+
return results;
|
|
27
|
+
}
|
|
28
|
+
function parseMagicAttachmentRequests(data) {
|
|
8
29
|
const results = [];
|
|
9
30
|
const seen = new Set();
|
|
10
31
|
for (const match of data.matchAll(MAGIC_LINK_RE)) {
|
|
@@ -26,6 +47,32 @@ export function parseAttachmentMagicLinks(data) {
|
|
|
26
47
|
// Ignore malformed terminal text. Magic links are advisory.
|
|
27
48
|
}
|
|
28
49
|
}
|
|
50
|
+
return results.map((path) => ({ path, source: 'external' }));
|
|
51
|
+
}
|
|
52
|
+
function parseCodexGeneratedArtifactRequests(data) {
|
|
53
|
+
const results = [];
|
|
54
|
+
const seen = new Set();
|
|
55
|
+
// Codex styles its TUI output — strip ANSI first so a trailing SGR reset
|
|
56
|
+
// (e.g. `...mockup.png\x1b[0m`) doesn't ride into the captured URL and break
|
|
57
|
+
// the extension allowlist check.
|
|
58
|
+
for (const match of stripAnsi(data).matchAll(CODEX_SAVED_FILE_RE)) {
|
|
59
|
+
const rawUrl = trimTrailingPunctuation(match[1] || '');
|
|
60
|
+
try {
|
|
61
|
+
const filePath = fileURLToPath(rawUrl);
|
|
62
|
+
if (!isAbsolute(filePath))
|
|
63
|
+
continue;
|
|
64
|
+
const extension = filePath.split('.').pop()?.toLowerCase() || '';
|
|
65
|
+
if (!isSupportedAttachmentExtension(extension))
|
|
66
|
+
continue;
|
|
67
|
+
if (seen.has(filePath))
|
|
68
|
+
continue;
|
|
69
|
+
seen.add(filePath);
|
|
70
|
+
results.push({ path: filePath, source: 'codex-generated' });
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Ignore malformed terminal text. Generated-artifact links are advisory.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
29
76
|
return results;
|
|
30
77
|
}
|
|
31
78
|
function trimTrailingPunctuation(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment-magic.js","sourceRoot":"","sources":["../src/attachment-magic.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"attachment-magic.js","sourceRoot":"","sources":["../src/attachment-magic.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment-registry.d.ts","sourceRoot":"","sources":["../src/attachment-registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"attachment-registry.d.ts","sourceRoot":"","sources":["../src/attachment-registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAelF,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,UAAU,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,sBAAsB,CAAC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,4BAA6B,SAAQ,uBAAuB;IAC3E,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,2BAA4B,SAAQ,KAAK;IAGlD,QAAQ,CAAC,UAAU,EAAE,MAAM;gBAD3B,OAAO,EAAE,MAAM,EACN,UAAU,GAAE,MAAY;CAIpC;AAMD,cAAM,kBAAkB;IACtB,OAAO,CAAC,gBAAgB,CAAoD;IAE5E,QAAQ,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAexC,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAI1E,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IASjF,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAGtC;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC;AAE3D,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEzE;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,sBAAsB,CAQ3E;AAED,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB;IACD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAOA;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEvF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,gBAAgB,GAAG,4BAA4B,CAe9F;AAED,sDAAsD;AACtD,MAAM,WAAW,iCAAiC;IAChD;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;;OAQG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,wBAAsB,0BAA0B,CAC9C,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,4BAA4B,CAAC,CAoEvC"}
|