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 +10 -2
- package/cofluxd.mjs +25 -11
- package/package.json +1 -1
- package/skills/coflux/SKILL.md +239 -73
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
|
-
|
|
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
|
|
952
|
-
//
|
|
953
|
-
//
|
|
953
|
+
// wait 的循环必须在 CLI 侧:单次 agentPost 有 25 秒的 loopback 应答上限。默认 30 分钟——编码任务
|
|
954
|
+
// 常跑很久;轮询走 terminal.status(daemon 本地账本直接答,不经中心),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
|
-
|
|
998
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1034
|
-
const t =
|
|
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
|
-
|
|
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
package/skills/coflux/SKILL.md
CHANGED
|
@@ -1,128 +1,294 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: coflux
|
|
3
|
-
description:
|
|
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
|
-
#
|
|
6
|
+
# Working inside coflux
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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
|
-
|
|
41
|
-
所以别指望在同一个终端里接着跑第二条命令,要么写成 `a && b`,要么再开一个。
|
|
72
|
+
## Local commands
|
|
42
73
|
|
|
43
|
-
|
|
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
|
|
51
|
-
cofluxd terminal
|
|
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
|
-
`
|
|
56
|
-
|
|
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
|
-
|
|
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> #
|
|
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
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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 "
|
|
187
|
+
cofluxd progress "Reproduced; narrowing down the relay reconnect timing"
|
|
88
188
|
```
|
|
89
189
|
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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.
|