cofluxd 0.12.0 → 0.14.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 CHANGED
@@ -34,14 +34,22 @@ CLI 还会取自身与 worker 的持久 release floor 较大值拒绝远端降
34
34
  跑在 coflux 终端里的 claude/codex 可以用下面几条,把工作外化成用户在 web/手机上**看得见、能接管**的东西——而不是在自己的 Bash 里后台起一个谁也看不见的进程:
35
35
 
36
36
  ```sh
37
- cofluxd terminal new --title "跑单测" --cmd "pnpm test" # 开真实终端,用户可接管
37
+ cofluxd terminal new --title "跑单测" --cmd "pnpm test" # 作业终端:跑完即退,带退出码
38
+ cofluxd terminal new --title "调试 shell" # 会话终端:不带命令 = 常驻登录 shell
38
39
  cofluxd terminal list # 本工作区的终端 + 状态/退出码
39
40
  cofluxd terminal read <taskId> [--lines N] # 读终端内容(纯文本,已退出也能读)
41
+ cofluxd terminal wait <taskId> [--timeout <秒>] # 阻塞到退出,打印退出码
42
+ cofluxd terminal send <taskId> --text "y" --enter # 往终端输入;用户正在接管时被拒
43
+ cofluxd progress "复现了,正在定位" # 播报进度:显示在工作区卡片上
40
44
  cofluxd notify "需要你定一下用哪个方案" # 叫人:侧栏转「等待交互」
41
45
  cofluxd ports # 端口 + 可直接打开的预览 URL
42
46
  ```
43
47
 
44
- 不需要任何凭证:daemon 用调用方 pid 反查进程树确认它属于哪个会话,**coflux 会话之外的进程一律拒绝**,权限也天然限定在该会话所属的工作区内。
48
+ `terminal new` 带不带 `--cmd` 是两种终端:带命令是**作业终端**,命令包成脚本交登录 shell 跑,跑完终端退出并带退出码,输出另落一份日志供 `read` 回读(代价是命令的 stdout 是管道而非 tty,颜色/进度条/全屏程序都没有);不带命令是**会话终端**,等价于用户在侧栏点「新建终端」——工作区目录下的默认登录 shell,stdin/stdout 都是真 tty,不会自己退出,直到 agent 或用户输入 `exit`。会话终端没有命令日志,`read` 读的是当前画面(一屏),首次 `send` 前要先 `read` 等提示符。
49
+
50
+ 不需要任何凭证:daemon 用调用方 pid 反查进程树确认它属于哪个会话,**coflux 会话之外的进程一律拒绝**,权限也天然限定在该会话所属的工作区内。**local-first**:send/read/wait/notify/progress 在 daemon 本地闭环、不经中心(归属与退出码来自 daemon 自己的会话账本,内容来自本地命令日志或 sessiond 快照);只有 new/list/ports 由 daemon 代问中心——Task 要落库广播、预览 URL 由中心生成。早于 daemon 升级开出来的终端缺归属信息,本地命令会明确拒绝,重开即可。
51
+
52
+ 每个 coflux 开出来的 PTY 会话里还注入了一组 `COFLUX_*` 环境变量(由 supervisor 组装,中心只下发 id):`COFLUX_DEVICE_ID` / `COFLUX_PROJECT_ID`(目录工作区为空串)/ `COFLUX_WORKSPACE_ID` / `COFLUX_TASK_ID` / `COFLUX_SESSION_ID` / `COFLUX_MCP_URL`。agent 读它们就知道自己在哪台设备、哪个项目/工作区/终端,值与中心 MCP `list_*` 返回的 id 完全一致,可直接传给 MCP tools。本地命令与 MCP 的分工只有一条规则:**本地能闭环的一律本地命令**(本工作区内的开终端/读/等/输入/播报/叫人/端口);只有跨出本工作区——开子工作区、跨工作区/跨设备读写、或从 coflux 之外接入——才用中心的 `coflux` MCP(`claude mcp add --transport http coflux "$COFLUX_MCP_URL"`,一次 OAuth 授权)。supervisor 不走热升级,旧机器要 `cofluxd update && cofluxd restart` 之后会话里才有这些变量;skill 里写了变量为空时的降级分支。
45
53
 
46
54
  配套的 skill 在 `skills/coflux/SKILL.md`(随包分发),装给 Claude Code:
47
55
 
package/cofluxd.mjs CHANGED
@@ -943,14 +943,16 @@ async function cmdHook() {
943
943
  // **看得见、能接管**的 coflux 实体——而不是在自己的 Bash 里后台起一个谁也看不见的进程。
944
944
  //
945
945
  // 不需要任何凭证:daemon 用调用方 pid 反查进程树确认它属于哪个会话,树外一律拒。
946
+ // local-first(plan 094):send/read/wait/notify/progress 在 daemon 本地闭环,不经中心;只有
947
+ // new/list/ports 由 daemon 代问中心(Task 要落库广播、预览 URL 由中心生成)。
946
948
  // 与 `hook` 子命令的约定**相反**:这些命令必须写 stdout——输出就是给 agent 读的返回值。
947
949
  // 也刻意不做自动重试:terminal new 有副作用,重试会开出两个终端,失败就把错误交给 agent。
948
950
 
949
951
  const AGENT_TIMEOUT_MS = 30_000;
950
952
  const DEFAULT_READ_LINES = 200;
951
- // wait 的循环必须在 CLI 侧:单次 agentPost 撑不起长等待(daemon 控制 WS 有自己的往返超时)。
952
- // 默认 30 分钟——编码任务常跑很久;轮询走 terminal.liststatus 来自 sessionExit 事件链,
953
- // 不受快照 ~2s 延迟影响),3 秒一次对本机 loopback 是零负担。
953
+ // wait 的循环必须在 CLI 侧:单次 agentPost 25 秒的 loopback 应答上限。默认 30 分钟——编码任务
954
+ // 常跑很久;轮询走 terminal.statusdaemon 本地账本直接答,不经中心),3 秒一次对本机 loopback
955
+ // 是零负担。
954
956
  const DEFAULT_WAIT_TIMEOUT_S = 1800;
955
957
  const WAIT_POLL_MS = 3000;
956
958
 
@@ -994,11 +996,20 @@ function tailLines(text, n) {
994
996
  async function cmdTerminal(values) {
995
997
  const sub = positionals[1];
996
998
  if (sub === "new") {
997
- const command = values.cmd;
998
- if (!command) die(`terminal new 需要 --cmd "<命令>"`);
999
+ // 两种终端只看「命令是否为空」(plan 101):--cmd 缺省与 --cmd= 空白等价,都开会话终端
1000
+ // ——工作区目录下的默认登录 shell,stdin/stdout 都是真 tty,不自动退出。带命令的仍是作业
1001
+ // 终端:命令跑完终端退出并带退出码,语义一字不变。空白在这里统一收敛成空串,好让中心的
1002
+ // 「默认标题取命令首行」落到它自己的兜底。
1003
+ const command = (values.cmd ?? "").trim() ? values.cmd : "";
999
1004
  const result = await agentPost({ action: "terminal.new", title: values.title || "", command });
1000
1005
  console.log(`已开终端 ${result.taskId}(用户可在 coflux 侧栏看到并随时接管)`);
1001
- console.log(`看输出:cofluxd terminal read ${result.taskId}`);
1006
+ if (command) {
1007
+ console.log(`看输出:cofluxd terminal read ${result.taskId}`);
1008
+ } else {
1009
+ console.log(`会话终端:常驻的登录 shell(全 tty),不会自己退出`);
1010
+ console.log(`先等提示符:cofluxd terminal read ${result.taskId}`);
1011
+ console.log(`再输命令:cofluxd terminal send ${result.taskId} --text "<命令>" --enter(送 exit 才结束)`);
1012
+ }
1002
1013
  } else if (sub === "list") {
1003
1014
  const { terminals } = await agentPost({ action: "terminal.list" });
1004
1015
  if (!terminals.length) return void console.log("本工作区暂无终端");
@@ -1030,9 +1041,8 @@ async function cmdTerminal(values) {
1030
1041
  const timeoutSec = Number.isFinite(requested) && requested > 0 ? requested : DEFAULT_WAIT_TIMEOUT_S;
1031
1042
  const deadline = Date.now() + timeoutSec * 1000;
1032
1043
  for (;;) {
1033
- const { terminals } = await agentPost({ action: "terminal.list" });
1034
- const t = terminals.find((x) => x.taskId === taskId);
1035
- if (!t) die(`本工作区没有终端 ${taskId}(用 cofluxd terminal list 查)`);
1044
+ // taskId 直接问本地账本;目标不存在/不在本工作区时 daemon 回可读错误,agentPost 直接 die。
1045
+ const t = await agentPost({ action: "terminal.status", taskId });
1036
1046
  if (t.status === "exited") {
1037
1047
  const exit = t.exitCode === undefined || t.exitCode === null ? "" : ` exit=${t.exitCode}`;
1038
1048
  return void console.log(`# exited${exit}`);
@@ -1084,8 +1094,12 @@ const HELP = `cofluxd —— coflux daemon 管理
1084
1094
 
1085
1095
  以下几条供**跑在 coflux 终端里的 agent** 调用,把工作变成用户看得见、能接管的东西:
1086
1096
 
1087
- cofluxd terminal new --cmd "<命令>" [--title "<标题>"]
1088
- 开一个真实终端跑命令,用户在 coflux 侧栏能看到并随时接管
1097
+ cofluxd terminal new [--cmd "<命令>"] [--title "<标题>"]
1098
+ 开一个真实终端,用户在 coflux 侧栏能看到并随时接管
1099
+ 带 --cmd = 作业终端:命令在登录 shell 里跑完即退出并带退出码,输出另
1100
+ 落一份日志供 read 回读(代价:stdout 是管道,不是 tty)
1101
+ 不带 --cmd = 会话终端:工作区目录下的常驻登录 shell,stdin/stdout 都是
1102
+ 真 tty(能跑 vim/htop、有颜色),先 read 等提示符再 send,送 exit 才结束
1089
1103
  cofluxd terminal list 列出本工作区的终端(含 status / 退出码)
1090
1104
  cofluxd terminal read <taskId> [--lines N]
1091
1105
  读某个终端的内容(纯文本,默认最后 200 行;终端已退出也能读)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cofluxd",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "coflux daemon 管理 CLI:装/起/停/升级 Rust daemon(supervisor + worker)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,128 +1,294 @@
1
1
  ---
2
2
  name: coflux
3
- description: 当你运行在 coflux 终端里时,把长任务、并行工作和求助外化成用户在 coflux web/手机上看得见、能随时接管的真实终端。适用于跑测试/构建/dev server 等耗时命令、需要用户接管或决策、想给用户一个可点开的预览 URL 的场景。
3
+ description: When you run inside a coflux terminal, this skill documents the local cofluxd commands that open terminals the user can watch and take over from the coflux web/mobile app, report progress, call the user and hand out preview URLs, plus the center MCP for reaching other workspaces and devices. Your coordinates (device / project / workspace / terminal) arrive in a <coflux-session> block at session start, or via the COFLUX_* environment variables. Inside your own workspace always use the zero-credential local cofluxd commands (open, read, wait, send, report progress, call the user, get preview URLs); use the center's coflux MCP only to leave this workspace (child workspaces, other workspaces or devices). Use when the user should be able to watch, step into or stop a command (interactive steps, dev servers, a job they are waiting on), when the user has to decide something, when you want to hand the user a clickable preview URL, or when you need an isolated child workspace for parallel work.
4
4
  ---
5
5
 
6
- # coflux 里工作
6
+ # Working inside coflux
7
7
 
8
- 你可能正跑在 coflux 的一个终端里。coflux 让用户在浏览器和手机上盯着各台机器上的
9
- agent 干活,随时接管。这套命令让你把自己的工作**变成用户看得见的东西**。
8
+ You may be running inside a coflux terminal. coflux lets the user watch agents working on many
9
+ machines from a browser or a phone and take over at any time. This skill gives you terminals the
10
+ user can see and take over, a progress line and a call button on the workspace card, preview URLs,
11
+ and a way to operate the other workspaces and devices under the account when you need to.
10
12
 
11
- 先判断自己在不在 coflux 里:任意一条命令返回「不在 coflux 终端里」就说明不在,
12
- 这时忘掉这个 skill,照常用你自己的工具。
13
+ Two tracks, one rule: **whatever closes locally uses local commands; only leaving this
14
+ workspace goes through MCP.**
13
15
 
14
- ## 什么时候用
16
+ | Track | Credentials | Reach | Use for |
17
+ |---|---|---|---|
18
+ | Local commands `cofluxd terminal/progress/notify/ports` | none (the daemon identifies you by process tree) | **the workspace you are in** | open, read, wait, send, report progress, call the user, preview URLs: the default, fastest, no network dependency |
19
+ | Center MCP `coflux` | one OAuth authorization by the user in the host | **the whole account**: every device, project, workspace and terminal | child workspaces (git worktrees), cross-workspace / cross-device access, joining from outside coflux |
15
20
 
16
- **用 `cofluxd terminal new` 而不是自己后台起进程**——只要这条命令满足任一条:
21
+ Of the local commands, `send`/`read`/`wait`/`notify`/`progress` complete entirely inside the
22
+ local daemon and never touch the center; `new`/`list`/`ports` are relayed to the center by the
23
+ daemon on your behalf (terminals must appear in the user's sidebar, preview URLs are minted by
24
+ the center). You only ever talk to the local daemon.
17
25
 
18
- - 要跑超过十几秒(测试、构建、安装依赖、迁移)
19
- - 会一直跑下去(dev server、watch、日志跟随)
20
- - 用户可能想接管(需要交互、可能要中途叫停、失败了要人去调)
26
+ ## Figure out where you are first
21
27
 
22
- 这类工作在你自己的 Bash 里后台跑,用户**什么也看不见**:看不到它在跑、接管不了、
23
- 出问题只能等你转述。开成 coflux 终端,它就是侧栏里一个有标题的条目,用户能点进去、
24
- 能接管、能自己敲命令。
28
+ With the coflux plugin installed, Claude Code and Codex receive a `<coflux-session>` block at
29
+ session start (and again after context compaction). Your coordinates are in it; use them
30
+ directly. Without that block (no plugin, hand-wired hooks, hook not trusted yet), read the
31
+ environment:
25
32
 
26
- **不要用**在一次性的快命令上(`ls`、`grep`、`git status`、读文件)——你自己的工具更快,
27
- 给用户开一堆一秒就结束的终端只是噪音。
33
+ ```sh
34
+ env | grep '^COFLUX_'
35
+ ```
28
36
 
29
- ## 命令
37
+ - **`COFLUX_WORKSPACE_ID` is non-empty** (or the `<coflux-session>` block is present) → you are in
38
+ a coflux terminal with an up-to-date daemon. The variables below are your coordinates; pass
39
+ these ids to MCP tools directly instead of guessing from `list_*`:
30
40
 
31
- ### 开终端跑命令
41
+ | Variable | Meaning |
42
+ |---|---|
43
+ | `COFLUX_DEVICE_ID` | id of the device you run on (the id in `list_devices`) |
44
+ | `COFLUX_PROJECT_ID` | owning project id; empty string for a directory workspace without a repository |
45
+ | `COFLUX_WORKSPACE_ID` | owning workspace id (the id in `list_workspaces`) |
46
+ | `COFLUX_TASK_ID` | id of this terminal (the taskId / terminalId used by local commands and `read_terminal`) |
47
+ | `COFLUX_SESSION_ID` | id of this PTY session |
48
+ | `COFLUX_MCP_URL` | the center's MCP URL; the user configures MCP with it |
32
49
 
33
- ```sh
34
- cofluxd terminal new --title "跑单测" --cmd "pnpm -C tests test"
35
- ```
50
+ - **Variables empty or absent** → treat yourself as outside coflux: forget this skill and use your
51
+ own tools as usual (if the user configured the coflux MCP in the host, the MCP tools still work;
52
+ you just have no "where am I" coordinates). If the user insists you are inside a coflux terminal,
53
+ this machine's daemon has not been upgraded: tell the user to run `cofluxd update && cofluxd restart`;
54
+ after reopening the terminal the variables and the local commands are there.
55
+
56
+ ## When to open a terminal
57
+
58
+ A coflux terminal is a process the user can see: a titled entry in their sidebar that they can
59
+ open, take over and type into, whose output you can read back at any time.
60
+ Whether a command runs in your own Bash or in a coflux terminal is your call; a coflux terminal is
61
+ worth it when the user's view of the process matters:
62
+
63
+ - the user may want to step in: interactive steps, confirmations, something they may need to stop
64
+ midway or rescue when it fails
65
+ - it keeps running and the user will want to find it later (dev server, watch mode, log tailing)
66
+ - you want to hand the user something to look at (a test run they asked to watch, a build they are
67
+ waiting on)
36
68
 
37
- `--title` 是用户在侧栏看到的名字,**认真起**:写「跑单测」「起 dev server」,
38
- 别写「terminal 1」。命令在当前工作区目录下、用登录 shell 执行。
69
+ **Do not use it** for quick one-shot commands (`ls`, `grep`, `git status`, reading files): your
70
+ own tools are faster, and a pile of one-second terminals is just noise to the user.
39
71
 
40
- 命令跑完终端就退出,任务转 `exited` 并带上退出码——这是你判断成没成的依据。
41
- 所以别指望在同一个终端里接着跑第二条命令,要么写成 `a && b`,要么再开一个。
72
+ ## Local commands
42
73
 
43
- 命令的输出会同时落一份日志供你回读,代价是它的 stdout 是管道而不是 tty——多数程序会因此
44
- 关掉颜色和进度条。极少数程序在非 tty 下行为不同(比如不输出进度、切成 CI 模式),如果你
45
- 依赖那种行为,自己在 Bash 里跑。
74
+ ### Open a terminal
46
75
 
47
- ### 看跑到哪了
76
+ There are two kinds, told apart by one single thing: **whether you pass a command**.
48
77
 
49
78
  ```sh
50
- cofluxd terminal list # 本工作区所有终端:id、状态、退出码、标题
51
- cofluxd terminal read <taskId> # 某个终端的内容(纯文本,默认最后 200 行)
52
- cofluxd terminal read <taskId> --lines 50
79
+ cofluxd terminal new --title="Run unit tests" --cmd="pnpm -C tests test" # job terminal
80
+ cofluxd terminal new --title="Debug shell" # session terminal
53
81
  ```
54
82
 
55
- `list` 的状态是 `running` / `exited` / `idle`;`exited` 会带 `exit=<码>`。
56
- **终端已经退出也能 read**——「命令跑完了看输出」正是最常用的场景。
83
+ `--title` is the name the user sees in the sidebar; **name it properly**: "Run unit tests",
84
+ "Start dev server", never "terminal 1". Either kind runs in the current workspace directory.
85
+
86
+ Always write `--cmd=<value>` and `--title=<value>` with the `=`, never separated by a space: a
87
+ value that starts with `-` is otherwise taken for another option and the call fails outright.
88
+
89
+ **Job terminal — with `--cmd=...`.** It runs that one command under the login shell (command line
90
+ capped at 16 KB). The terminal exits when the command finishes and the task becomes `exited` with
91
+ the exit code: that is how you tell success from failure. So do not expect to run a second command
92
+ in the same terminal: write `a && b`, or open another one. The output is also written to a local
93
+ log for you to read back (roughly the last 1 MB is kept). The cost is that the command's stdout is
94
+ a pipe rather than a tty: most programs turn off colors and progress bars, full-screen programs
95
+ (vim, htop, less) do not work at all, and a few switch to a different "CI" behavior.
96
+
97
+ **Session terminal — no `--cmd` at all.** You get exactly what the user gets by clicking "new
98
+ terminal" in the sidebar: the default login shell in the workspace directory, with stdin **and**
99
+ stdout on a real tty. It runs nothing by itself and **never exits on its own** — it lives until
100
+ `exit` is typed into it (by you with `send`, or by the user), or the user stops it. Reach for it
101
+ when you need several commands in the same shell, a TUI or a program whose colors and progress
102
+ bars matter, or simply a terminal the user can step into and keep using. There is no command log
103
+ for it: `read` returns the current screen (one screenful, no history), so you judge how it went
104
+ from what is on screen, and `wait` is only meaningful after you have sent `exit`.
105
+
106
+ Driving a session terminal:
107
+
108
+ 1. `cofluxd terminal new --title="Debug shell"` → prints a taskId.
109
+ 2. `cofluxd terminal read <taskId>` until you see the shell prompt. The shell needs a moment to
110
+ start and the first read can come back empty — **never `send` before you have seen a prompt**.
111
+ 3. `cofluxd terminal send <taskId> --text="pnpm build" --enter`, then `read` again to see what
112
+ happened. One send per command; nothing signals you when a command finished, so read until the
113
+ prompt is back. To make that unambiguous, end the command with a marker of your own
114
+ (`pnpm build; echo DONE-$?`) and read until the marker shows up.
115
+ 4. `cofluxd terminal send <taskId> --text="exit" --enter` when you are done; the terminal then
116
+ becomes `exited` with the shell's exit code.
117
+
118
+ The new terminal has the same `COFLUX_*` variables (pointing at its own task/session ids, same
119
+ workspace as you).
120
+
121
+ ### See how far it got
57
122
 
58
- 内容有最多约 2 秒的延迟(来自中心的定期快照),所以刚 `new` 完立刻 `read` 可能是空的。
123
+ ```sh
124
+ cofluxd terminal list # every terminal in this workspace: id, state, exit code, title
125
+ cofluxd terminal read <taskId> # a terminal's content (plain text, last 200 lines by default)
126
+ cofluxd terminal read <taskId> --lines 50
127
+ ```
59
128
 
60
- ### 等命令跑完
129
+ `list` states are `running` / `exited` / `idle`; `exited` carries `exit=<code>`.
130
+ **An exited terminal can still be read**: "the command finished, look at the output" is the most
131
+ common case. `read` reads the local log of a job terminal; terminals that have no log (session
132
+ terminals, and the ones the user opened) return the current screen instead — one screenful, no
133
+ history, and empty for the first moments after opening. Both are immediate.
134
+
135
+ ### Wait for a command to finish
61
136
 
62
137
  ```sh
63
- cofluxd terminal wait <taskId> # 阻塞到该终端退出,打印退出码(默认最长等 30 分钟)
64
- cofluxd terminal wait <taskId> --timeout 300 # 自定超时(秒);超时会明确报错并非零退出
138
+ cofluxd terminal wait <taskId> # block until that terminal exits and print the exit code (default cap 30 minutes)
139
+ cofluxd terminal wait <taskId> --timeout 300 # custom timeout in seconds; a timeout fails loudly with a non-zero exit
65
140
  ```
66
141
 
67
- 要等一条命令跑完就用 `wait`,**别自己写轮询循环**——它一条命令阻塞到位,退出码直接给你。
68
- 超时不代表命令失败,只是还没跑完:`read` 看看现场再决定继续等还是处理。
142
+ To wait for a command use `wait`; **do not write your own polling loop**. One command blocks
143
+ until done and hands you the exit code. A timeout does not mean the command failed, only that it
144
+ is still running: `read` to see where it is, then decide whether to keep waiting or act.
145
+
146
+ `wait` **always exits 0** once the terminal is done: it reports that the command finished, not
147
+ whether it succeeded. Read the result off its output line `# exited exit=<code>` (`list` shows the
148
+ same). A non-zero exit from `wait` itself means the wait timed out or the id was wrong.
149
+
150
+ **Do not `wait` on a session terminal** unless you have already sent it `exit`: it never finishes
151
+ by itself, so the wait can only end in the 30-minute timeout — a timeout there means the shell is
152
+ still sitting at its prompt, nothing more. Its exit code, when it finally exits, is the shell's and
153
+ not any command's: check what a command did by reading the screen.
69
154
 
70
- ### 往终端里输入
155
+ **Keep working, and be woken up when it finishes.** `wait` blocks, so run it as a backgrounded Bash
156
+ call of your own:
157
+
158
+ 1. `cofluxd terminal new --title="Run the test suite" --cmd="pnpm -C tests test"` → prints a taskId.
159
+ 2. Run `cofluxd terminal wait <taskId>` as a backgrounded Bash call, then go do something else.
160
+ 3. The host wakes you when that call exits. Check its output for `# exited exit=<code>`, then
161
+ `cofluxd terminal read <taskId>` to see what actually happened.
162
+
163
+ That gets you both halves at once: the user watches (and can take over) a real terminal, and you are
164
+ still told the moment it is over, instead of blocking or polling for it.
165
+
166
+ ### Type into a terminal
71
167
 
72
168
  ```sh
73
- cofluxd terminal send <taskId> --text "y" --enter # 输入一行并回车
74
- cofluxd terminal send <taskId> --enter # 只按一个回车
169
+ cofluxd terminal send <taskId> --text "y" --enter # type a line and press Enter
170
+ cofluxd terminal send <taskId> --enter # just press Enter
75
171
  ```
76
172
 
77
- 用在命令要交互确认(y/N、选项)、或想在跑完的同一 shell 里补一条命令的时候。纪律:
173
+ For interactive confirmations (y/N, menus), or to add a command in the same shell after the
174
+ previous one finished. Discipline:
78
175
 
79
- - **先 `read` `send`**:看清终端现在在等什么再输入,别盲打。
80
- - **用户正在接管时会被拒**——这不是错误,是设计:人永远优先。被拒就停手,
81
- 要沟通用 `notify`,别重试。
82
- - send 超时后**不要直接重发**:先 `read` 确认刚才那次到底进没进去,重复输入比丢输入更糟。
176
+ - **`read` before `send`**: see what the terminal is waiting for before typing; never type blind.
177
+ On a freshly opened session terminal this also means waiting for the shell prompt to appear.
178
+ - **Refused while the user is taking over**: that is not an error, it is by design; humans always
179
+ win. Stop when refused; use `notify` to communicate, do not retry.
180
+ - **After a send timeout do not resend right away**: `read` first to check whether the input
181
+ actually landed; duplicated input is worse than lost input.
182
+ - A single text is capped at 64 KB; this is an interactive input channel, not a file transfer.
83
183
 
84
- ### 播报进度
184
+ ### Report progress
85
185
 
86
186
  ```sh
87
- cofluxd progress "复现了,正在定位 relay 重连的时序"
187
+ cofluxd progress "Reproduced; narrowing down the relay reconnect timing"
88
188
  ```
89
189
 
90
- 一句话告诉用户你干到哪了,显示在工作区卡片上,被下一条覆盖。在关键节点更新:复现了、
91
- 定位到了、修完在验、卡在哪。它**不打扰用户**,和 `notify` 是两条信道:
190
+ One sentence telling the user how far you are, shown on the workspace card and replaced by the
191
+ next one. Update it at milestones: reproduced, located, fixed and verifying, stuck on X. It
192
+ **does not interrupt the user**; it is a different channel from `notify`:
92
193
 
93
- - `progress` = 播报(用户扫一眼就知道进展,不需要回应)
94
- - `notify` = 叫人(工作区转「等待交互」,用户该来看看了)
194
+ - `progress` = broadcast (the user glances and knows the state, no response needed)
195
+ - `notify` = call the user (the workspace turns "waiting for interaction", the user should come and look)
95
196
 
96
- 拿不准用哪个:不需要用户做任何事就用 `progress`。
197
+ If unsure: when the user does not have to do anything, use `progress`.
97
198
 
98
- ### 叫人
199
+ ### Call the user
99
200
 
100
201
  ```sh
101
- cofluxd notify "两个方案都能走通,需要你定一下用哪个"
202
+ cofluxd notify "Both approaches work; I need you to pick one"
102
203
  ```
103
204
 
104
- 用户的侧栏里这个工作区会转成「等待交互」并显示这句话——他在手机上也看得到。
105
- 用在你**真的卡住**的时候:需要用户决策、要密码/权限、发现了必须人来判断的问题。
106
- 一句话说清要什么,别写成日志。
205
+ The user's sidebar switches this workspace to "waiting for interaction" and shows this sentence;
206
+ they see it on the phone too. Use it when you are **really stuck**: a decision is needed, a
207
+ password or a permission, a problem only a human can judge. One sentence saying what you need;
208
+ do not write a log.
107
209
 
108
- (你正常的提问和权限请求已经会自动反映到侧栏状态上,不需要额外 notify。
109
- 这条是给「你要说的事,用户光看状态图标猜不出来」准备的。)
210
+ (Your normal questions and permission prompts already show up in the sidebar state; they need no
211
+ extra notify. This is for "what you have to say cannot be guessed from the status icon".)
110
212
 
111
- ### 给用户可点开的预览
213
+ ### Hand the user a clickable preview
112
214
 
113
215
  ```sh
114
216
  cofluxd ports
115
217
  ```
116
218
 
117
- 列出本工作区所有监听端口和对应的公网预览 URL。起了 dev server 之后用它拿 URL 直接
118
- 告诉用户,他点开就能看,不用自己去翻。
219
+ Lists every listening port in this workspace with its public preview URL. After starting a dev
220
+ server, use it to get the URL and tell the user directly; they click it and nobody has to dig.
221
+
222
+ ### Errors from local commands
223
+
224
+ Errors are one readable sentence; do what they say: "not inside a coflux terminal" = you are not
225
+ in a coflux session; "terminal is not in this workspace or does not exist" = check the id with
226
+ `list`; "predates the daemon upgrade" = that terminal was opened before the daemon upgrade, open a
227
+ new one; a `new` without `--cmd` refused for a missing command = this machine's daemon is older
228
+ than session terminals, tell the user to run `cofluxd update && cofluxd restart` (or pass a command
229
+ and use a job terminal); "daemon is not connected to the center" only appears on
230
+ `new`/`list`/`ports`, retry once it reconnects.
231
+
232
+ ## Center MCP: leaving this workspace
233
+
234
+ Local commands only see the workspace you are in. Use the MCP server named `coflux` in the host
235
+ **only** for these:
119
236
 
120
- ## 边界
237
+ - **Open an isolated child workspace to work in parallel**: `create_workspace` (project id from
238
+ `$COFLUX_PROJECT_ID`) really runs `git worktree add` on the device; then `create_terminal` runs
239
+ commands there. The same two kinds apply: `create_terminal` with a `command` opens a
240
+ job terminal, without one it opens a session terminal.
241
+ - **Look at or operate terminals in other workspaces or on other devices**: `list_*` →
242
+ `read_terminal` / `send_terminal_input`.
243
+ - **Join everything under the account when you are not inside a coflux terminal** (for example
244
+ Claude Code the user started on their own machine).
245
+ - **Never run `git worktree add` yourself**: inside a coflux project the plugin blocks
246
+ `git worktree add|remove|move` and points you to `create_workspace` / `remove_workspace`; a
247
+ worktree you create is invisible to the user and cannot host a terminal.
248
+
249
+ Do not detour through MCP for work inside this workspace: that is an extra round trip to the
250
+ center, while a local command does it in one step.
251
+
252
+ ### When MCP is not configured
253
+
254
+ Run `claude mcp list` (Codex: `codex mcp list`) to see whether `coflux` is there. If not, give
255
+ the user the one-line setup, with the URL from `$COFLUX_MCP_URL` (it is the center's public URL
256
+ + `/mcp`):
257
+
258
+ ```sh
259
+ claude mcp add --transport http coflux "$COFLUX_MCP_URL" # Claude Code
260
+ codex mcp add coflux --url "$COFLUX_MCP_URL" # Codex
261
+ ```
121
262
 
122
- - 你能开、读、等、输入,但**输入是人类优先的受限写权**:用户正在接管的终端你写不进去
123
- (会被明确拒绝),用户随时接管也会把你顶掉。别和人抢终端。
124
- - 能看到的只有**你自己所在的工作区**,别的工作区和别的机器都看不见也碰不到。
125
- - 一个工作区同时活着的终端有上限(默认 8,含用户自己开的)。撞上限先 `list` 看看,
126
- 多半是有跑完没收的;真是用户占满了,就 `notify` 告诉他,别硬试。
127
- - 这些命令都要 daemon 连着中心才能用——毕竟「让用户看见」就是它们的全部意义。
128
- 连不上时会明确报错,不会默默降级。
263
+ The host then guides the user through a one-time OAuth authorization in the browser (`/mcp` in
264
+ Claude Code). Authorization is the user's job; you only hand over the URL and the command. Until
265
+ it is set up, keep doing the work inside this workspace with local commands.
266
+
267
+ ### Using the tools
268
+
269
+ The tool list and each tool's contract (parameters, limits, what an error means) come from the
270
+ MCP server itself: read the tool descriptions in the host, they are the source of truth and this
271
+ file does not repeat them. Take ids from the `COFLUX_*` variables first; for anything outside this
272
+ workspace, find ids with the `list_*` tools.
273
+
274
+ The local-command disciplines apply to MCP just the same: `read_terminal` before
275
+ `send_terminal_input`, stop when refused because the user is taking over (communicate with
276
+ `cofluxd notify` instead of retrying), `wait_terminal` instead of a polling loop around
277
+ `read_terminal`, and stop on "needs upgrade" (tell the user to run
278
+ `cofluxd update && cofluxd restart` on that device; do not retry or work around it).
279
+
280
+ ## Boundaries
281
+
282
+ - You can open, read, wait and type, but **typing is a restricted write with humans first**: you
283
+ cannot write into a terminal the user is taking over (you are refused explicitly), and the user
284
+ taking over at any time displaces you. Do not fight a human for a terminal.
285
+ - Local commands only see **the workspace you are in**; other workspaces and other machines go
286
+ through MCP and are limited to the same account.
287
+ - A workspace has a cap on concurrently live terminals (default 8, including the user's own).
288
+ On hitting the cap, `list` first: usually some finished terminals were never collected. If the
289
+ user really filled it up, `notify` them instead of forcing it.
290
+ - `new`/`list`/`ports` and every MCP tool need the daemon connected to the center; "letting the
291
+ user see" is their whole point. `send`/`read`/`wait`/`notify`/`progress` do not depend on the
292
+ center. When disconnected they fail loudly rather than degrade silently.
293
+ - `COFLUX_*` variables exist only in PTYs opened by coflux; exporting or changing them yourself
294
+ has no effect, the center only trusts the ids it issued.