diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 14e5712d..cebbe8fc 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ { "name": "codeman", "source": "./plugins/codeman", - "description": "Drive Codeman from inside a Claude Code session: spawn worker sessions, prompt them, wait for them, read their answers, clean up. Acts only inside a Codeman-managed session.", + "description": "Drive Codeman from inside a Claude Code session: spawn worker sessions, prompt them, wait for them, read their answers, clean up. Acts inside a Codeman-managed session; outside one it only reads saved Codex conversations offline.", "version": "1.45.0", "author": { "name": "Ark0N", diff --git a/README.md b/README.md index b45a3950..3546ab39 100644 --- a/README.md +++ b/README.md @@ -584,6 +584,8 @@ loginctl enable-linger $USER **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. + ### QR Code Authentication @@ -825,12 +827,14 @@ Those `DONE__` strings are the skill's **split marker** trick, and | [`reference/recipes.md`](skills/codeman/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`](skills/codeman/reference/endpoints.md) | Full endpoint tables, error codes, per-mode signal table, capacity limits. On demand. | | [`reference/messaging.md`](skills/codeman/reference/messaging.md) | Talking to claude workers directly via Claude Code cross-session messaging. On demand. | +| [`reference/history.md`](skills/codeman/reference/history.md) | Reading saved Codex conversations offline with `codeman history read`: selectors, filters, forks, budgets. On demand. | +| [`reference/inspection-and-staging.md`](skills/codeman/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. #### Two things worth knowing -- **It self-gates.** Outside a Codeman session (`CODEMAN_MUX` unset) the skill refuses to act and does not guess an API URL, so a global install costs an unrelated Claude Code session nothing. +- **It self-gates.** It acts inside a Codeman-managed session. Outside one (`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. - **It is deliberately conservative.** Unprompted, it may only spawn sessions, prompt them, and delete ones **it created in that same conversation, by exact id**, through a fail-closed guard that refuses to delete the agent's own session. Deleting a case (which erases a real directory of your code), bulk kills, respawn/ralph/cron/orchestrator changes and settings writes all require you to ask, naming the target. ⚠️ Turning `agentSkillEnabled` back 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 `. @@ -1014,7 +1018,7 @@ Filters keep whole user/assistant turns (`--topic` is a literal, case-insensitiv ### MCP server (`codeman mcp`): drive Codeman from any MCP client -`codeman mcp` is a [Model Context Protocol](https://modelcontextprotocol.io) 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: no new route, auth and ownership unchanged. +`codeman mcp` is a [Model Context Protocol](https://modelcontextprotocol.io) 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. ```bash # Claude Code diff --git a/README.zh-CN.md b/README.zh-CN.md index 6e78e000..add70ccf 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -585,6 +585,8 @@ loginctl enable-linger $USER 通过隧道暴露前**务必设置 `CODEMAN_PASSWORD`** —— 否则任何拿到 URL 的人都能完全访问你的会话。 +Codeman 自己管理的隧道(App Settings → System → Remote access)在没有登录(`CODEMAN_PASSWORD` 或多用户模式)时拒绝启动,除非用 `CODEMAN_ALLOW_UNAUTHENTICATED_TUNNEL=1` 或 `codeman web --allow-unauthenticated-tunnel` 确认这种暴露,或者你在浏览器的确认框里点了同意;没有登录时,每次开启前浏览器都会先问。`CODEMAN_ALLOW_UNAUTHENTICATED_NETWORK` 只管网络绑定,并不能让隧道启动。 + ### 二维码认证 @@ -826,12 +828,14 @@ deleted 9f2d8e5f, aff9c691, be9f1f15 标签消失 | [`reference/recipes.md`](skills/codeman/reference/recipes.md) | 8 个完整流程:claude、DeepSeek Harness 与 shell 工作会话、扇出、盯住被卡住的工作会话、消息扇出。按需加载。 | | [`reference/endpoints.md`](skills/codeman/reference/endpoints.md) | 完整端点表、错误码、各模式的信号表、容量限制。按需加载。 | | [`reference/messaging.md`](skills/codeman/reference/messaging.md) | 通过 Claude Code 跨会话消息直接和 claude 工作会话对话。按需加载。 | +| [`reference/history.md`](skills/codeman/reference/history.md) | 用 `codeman history read` 离线读取已保存的 Codex 对话:选择器、过滤条件、分叉、预算。按需加载。 | +| [`reference/inspection-and-staging.md`](skills/codeman/reference/inspection-and-staging.md) | 实验性功能,需要手动开启:用 `codeman agent inspect`、`stage` 和 `submit` 做有界的窗格检查和受保护的提交。按需加载。 | 里面的每一个配方都在真实服务器上验证过,注释记录的是实测出来而不是猜出来的失败模式。 #### 两件值得知道的事 -- **它会自我门禁。** 不在 Codeman 会话里(`CODEMAN_MUX` 未设置)时,技能拒绝动作,也不去猜 API 地址,所以全局安装对无关的 Claude Code 会话没有任何代价。 +- **它会自我门禁。** 它在 Codeman 管理的会话里工作。不在这样的会话里(`CODEMAN_MUX` 未设置)时,它只会离线读取已保存的 Codex 对话(`codeman history read`),其他一律拒绝,也从不去猜 API 地址,所以全局安装对无关的 Claude Code 会话没有任何代价。 - **它刻意保守。** 未经提示,它只会拉起会话、给它们发提示,并删除**它在同一段对话里自己创建的**会话(按精确 id,经由一个拒绝删除智能体自身会话的失败即关闭守卫)。删除 case(会抹掉一个真实的代码目录)、批量杀会话、改动 respawn/ralph/cron/orchestrator 以及写设置,都需要你开口并指名目标。 ⚠️ 把 `agentSkillEnabled` 关回去**不会删掉已经注入的副本**(在创建时做清扫,会把技能从共用同一个工作区的其他活动会话脚下抽走)。要删就按 case 删:`codeman skill uninstall --case `。 @@ -993,7 +997,7 @@ codeman agent rm "$SID" # 除当前会话外 ### MCP 服务器(`codeman mcp`):从任意 MCP 客户端驱动 Codeman -`codeman mcp` 是一个走 stdio 的 [Model Context Protocol](https://modelcontextprotocol.io) 服务器,因此任何 MCP 客户端(Claude Code、Claude Desktop、Codex、Gemini CLI、Cursor 等)都能在任意已安装的智能体 CLI 里启动 Codeman 会话、给它们发提示、等它们完成、读取回答并清理,无论是在 Codeman 内部还是外部。和 `codeman agent` 一样,它是 HTTP API 的客户端:没有新路由,认证和归属规则不变。 +`codeman mcp` 是一个走 stdio 的 [Model Context Protocol](https://modelcontextprotocol.io) 服务器,因此任何 MCP 客户端(Claude Code、Claude Desktop、Codex、Gemini CLI、Cursor 等)都能在任意已安装的智能体 CLI 里启动 Codeman 会话、给它们发提示、等它们完成、读取回答并清理,无论是在 Codeman 内部还是外部。和 `codeman agent` 一样,它是 HTTP API 的客户端。MCP 服务器自己不新增任何路由,认证和归属规则不变。 ```bash # Claude Code diff --git a/docs/api-reference.md b/docs/api-reference.md index 2c7ef1d6..2c840c87 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -717,7 +717,11 @@ outcome. That covers terminal WebSocket keystrokes (each frame is acknowledged only once it is written, so a frame typed during the lease lands before the one typed after it), Shift+Enter and Ctrl+Enter from `POST .../send-key`, approval answers and Codeman's own managed writes. A held write that is still being sent -holds everything behind it. A tagged `POST /api/v1/sessions/:id/input` (one +holds everything behind it, with one exception: when the hold queue is full (256 +inputs or 1 MiB), a managed write (`writeViaMux`, which `POST .../input` uses for a plain +prompt or `useMux`) is not held and that route falls back to a direct write, which +is refused during the lease itself but can land ahead of input still being written +after it. A tagged `POST /api/v1/sessions/:id/input` (one carrying `clientId` and `seq`) instead answers `409 CONFLICT` ("A guarded submit is in progress on this session; nothing was written") for that span, the lease plus the held input's drain, rather than a `200` for bytes that were never diff --git a/docs/wiki/Codeman-Skill.md b/docs/wiki/Codeman-Skill.md index 6417606e..0033abd5 100644 --- a/docs/wiki/Codeman-Skill.md +++ b/docs/wiki/Codeman-Skill.md @@ -157,6 +157,7 @@ The skill checks for both and says so instead of acting. | [`reference/endpoints.md`](https://github.com/Ark0N/Codeman/blob/master/skills/codeman/reference/endpoints.md) | Endpoint tables, error codes, the signals each mode emits, capacity limits. Loaded on demand. | | [`reference/messaging.md`](https://github.com/Ark0N/Codeman/blob/master/skills/codeman/reference/messaging.md) | Talking to claude workers directly through Claude Code's cross-session messaging. Loaded on demand. | | [`reference/history.md`](https://github.com/Ark0N/Codeman/blob/master/skills/codeman/reference/history.md) | Reading saved Codex conversations offline with `codeman history read`: selectors, filters, forks, budgets. Loaded on demand. | +| [`reference/inspection-and-staging.md`](https://github.com/Ark0N/Codeman/blob/master/skills/codeman/reference/inspection-and-staging.md) | Experimental and opt-in (App Settings, Experimental agent coordination): bounded pane inspection and guarded submission with `codeman agent inspect`, `stage` and `submit`. Loaded on demand. | Only `SKILL.md` sits in the agent's context; the references load when a job needs them, so a quick run stays quick. diff --git a/plugins/codeman/.claude-plugin/plugin.json b/plugins/codeman/.claude-plugin/plugin.json index 8b0133e3..d6c0fc83 100644 --- a/plugins/codeman/.claude-plugin/plugin.json +++ b/plugins/codeman/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "codeman", - "description": "Drive Codeman, the self-hosted session manager for AI coding agents, from inside a Claude Code session: spawn worker sessions, prompt them, wait for them, read their answers, clean up. Acts only inside a Codeman-managed session.", + "description": "Drive Codeman, the self-hosted session manager for AI coding agents, from inside a Claude Code session: spawn worker sessions, prompt them, wait for them, read their answers, clean up. Acts inside a Codeman-managed session; outside one it only reads saved Codex conversations offline.", "version": "1.45.0", "author": { "name": "Ark0N", diff --git a/plugins/codeman/README.md b/plugins/codeman/README.md index 9ea8f05e..06d50222 100644 --- a/plugins/codeman/README.md +++ b/plugins/codeman/README.md @@ -7,7 +7,7 @@ The agent skill for [Codeman](https://getcodeman.com), the self-hosted mission c /plugin install codeman@codeman ``` -The skill acts only inside a Codeman-managed session (`CODEMAN_MUX=1`) and refuses everywhere else, so installing it globally costs nothing for unrelated sessions. +The skill acts inside a Codeman-managed session (`CODEMAN_MUX=1`). Outside one it only reads saved Codex conversations offline (`codeman history read`) and refuses everything else, so installing it globally costs nothing for unrelated sessions. Pick one install route. Codeman can inject the skill into each case itself (App Settings, Agent Skill), and `codeman skill install` writes a user-level copy; a Claude Code that has one of those AND this plugin lists the skill twice, as `codeman` and `codeman:codeman`. Both work, the second is just noise. diff --git a/plugins/codeman/skills/codeman/reference/inspection-and-staging.md b/plugins/codeman/skills/codeman/reference/inspection-and-staging.md index 630088c2..8f454748 100644 --- a/plugins/codeman/skills/codeman/reference/inspection-and-staging.md +++ b/plugins/codeman/skills/codeman/reference/inspection-and-staging.md @@ -96,14 +96,19 @@ failed or was ambiguous, the pane or input changed, or no frame verified in time says the message may already be in the composer: no receipt was issued. Do not stage again on top of it; follow [Recover a stranded draft](#recover-a-stranded-draft) below first. -While a submit's Enter is in flight (milliseconds), `send` and other input to that -session answer `409 CONFLICT` with nothing written; retry the input. +While a submit's Enter is in flight (milliseconds, plus the moment it takes to +write input held behind it), TAGGED input to that session (`clientId` and `seq`, +which `codeman agent send` and `interrupt` always carry) answers `409 CONFLICT` +with nothing written; retry it. Untagged input, terminal keystrokes, `send-key` +and Codeman's own managed writes are held instead and written in arrival order +once the Enter is out. Submit checks the caller, session, hash, expiry, pane lifecycle, input revision, geometry, exact composer and caret, then sends one conditional Enter at the session's input boundary. It first waits until 250 ms have passed since stage typed (Codex turns an Enter that follows typed text within 120 ms into a newline), so submitting right -after stage is fine. Competing Codeman writes are refused during dispatch. Browser/API/key +after stage is fine. Competing Codeman writes are held during dispatch and written +after it, except tagged input, which is refused with `409`. Browser/API/key input and managed restarts invalidate receipts even when the visible text or pid is restored. Direct tmux edits are outside that input serialization; captures remain evidence at the time observed, rather than terminal isolation. diff --git a/skills/codeman/reference/inspection-and-staging.md b/skills/codeman/reference/inspection-and-staging.md index 630088c2..8f454748 100644 --- a/skills/codeman/reference/inspection-and-staging.md +++ b/skills/codeman/reference/inspection-and-staging.md @@ -96,14 +96,19 @@ failed or was ambiguous, the pane or input changed, or no frame verified in time says the message may already be in the composer: no receipt was issued. Do not stage again on top of it; follow [Recover a stranded draft](#recover-a-stranded-draft) below first. -While a submit's Enter is in flight (milliseconds), `send` and other input to that -session answer `409 CONFLICT` with nothing written; retry the input. +While a submit's Enter is in flight (milliseconds, plus the moment it takes to +write input held behind it), TAGGED input to that session (`clientId` and `seq`, +which `codeman agent send` and `interrupt` always carry) answers `409 CONFLICT` +with nothing written; retry it. Untagged input, terminal keystrokes, `send-key` +and Codeman's own managed writes are held instead and written in arrival order +once the Enter is out. Submit checks the caller, session, hash, expiry, pane lifecycle, input revision, geometry, exact composer and caret, then sends one conditional Enter at the session's input boundary. It first waits until 250 ms have passed since stage typed (Codex turns an Enter that follows typed text within 120 ms into a newline), so submitting right -after stage is fine. Competing Codeman writes are refused during dispatch. Browser/API/key +after stage is fine. Competing Codeman writes are held during dispatch and written +after it, except tagged input, which is refused with `409`. Browser/API/key input and managed restarts invalidate receipts even when the visible text or pid is restored. Direct tmux edits are outside that input serialization; captures remain evidence at the time observed, rather than terminal isolation. diff --git a/src/cli-agent.ts b/src/cli-agent.ts index 6c75e737..7886924d 100644 --- a/src/cli-agent.ts +++ b/src/cli-agent.ts @@ -1272,7 +1272,7 @@ export function registerAgentCommands(program: Command): Command { const agent = program .command('agent') .description( - 'Talk to other sessions from inside one (any CLI mode): list, spawn, send, wait, read, interrupt, rm, and the inbox: post, inbox, ack' + 'Talk to other sessions from inside one (any CLI mode): list, spawn, send, wait, read, interrupt, rm, the inbox: post, inbox, ack, and the experimental, opt-in inspect, stage, submit' ); agent