cofluxd 0.13.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 +9 -3
- package/cofluxd.mjs +25 -11
- package/package.json +1 -1
- package/skills/coflux/SKILL.md +218 -146
package/README.md
CHANGED
|
@@ -34,16 +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` 等提示符。
|
|
45
49
|
|
|
46
|
-
|
|
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 里写了变量为空时的降级分支。
|
|
47
53
|
|
|
48
54
|
配套的 skill 在 `skills/coflux/SKILL.md`(随包分发),装给 Claude Code:
|
|
49
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,222 +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
|
-
|
|
10
|
-
|
|
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.
|
|
11
12
|
|
|
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 |
|
|
15
17
|
|---|---|---|---|
|
|
16
|
-
|
|
|
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 |
|
|
18
20
|
|
|
19
|
-
|
|
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.
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
## Figure out where you are first
|
|
27
|
+
|
|
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:
|
|
22
32
|
|
|
23
33
|
```sh
|
|
24
34
|
env | grep '^COFLUX_'
|
|
25
35
|
```
|
|
26
36
|
|
|
27
|
-
- **`COFLUX_WORKSPACE_ID`
|
|
28
|
-
|
|
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_*`:
|
|
29
40
|
|
|
30
|
-
|
|
|
41
|
+
| Variable | Meaning |
|
|
31
42
|
|---|---|
|
|
32
|
-
| `COFLUX_DEVICE_ID` |
|
|
33
|
-
| `COFLUX_PROJECT_ID` |
|
|
34
|
-
| `COFLUX_WORKSPACE_ID` |
|
|
35
|
-
| `COFLUX_TASK_ID` |
|
|
36
|
-
| `COFLUX_SESSION_ID` |
|
|
37
|
-
| `COFLUX_MCP_URL` |
|
|
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 |
|
|
38
49
|
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`pwd`,并顺手告诉用户 `cofluxd update && cofluxd restart` 之后就有环境变量了。
|
|
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.
|
|
45
55
|
|
|
46
|
-
##
|
|
56
|
+
## When to open a terminal
|
|
47
57
|
|
|
48
|
-
|
|
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:
|
|
49
62
|
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
-
|
|
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)
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
能接管、能自己敲命令。
|
|
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.
|
|
57
71
|
|
|
58
|
-
|
|
59
|
-
给用户开一堆一秒就结束的终端只是噪音。
|
|
72
|
+
## Local commands
|
|
60
73
|
|
|
61
|
-
|
|
74
|
+
### Open a terminal
|
|
62
75
|
|
|
63
|
-
|
|
76
|
+
There are two kinds, told apart by one single thing: **whether you pass a command**.
|
|
64
77
|
|
|
65
78
|
```sh
|
|
66
|
-
cofluxd terminal new --title "
|
|
79
|
+
cofluxd terminal new --title="Run unit tests" --cmd="pnpm -C tests test" # job terminal
|
|
80
|
+
cofluxd terminal new --title="Debug shell" # session terminal
|
|
67
81
|
```
|
|
68
82
|
|
|
69
|
-
`--title`
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
|
74
122
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
+
```
|
|
78
128
|
|
|
79
|
-
|
|
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.
|
|
80
134
|
|
|
81
|
-
###
|
|
135
|
+
### Wait for a command to finish
|
|
82
136
|
|
|
83
137
|
```sh
|
|
84
|
-
cofluxd terminal
|
|
85
|
-
cofluxd terminal
|
|
86
|
-
cofluxd terminal read <taskId> --lines 50
|
|
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
|
|
87
140
|
```
|
|
88
141
|
|
|
89
|
-
|
|
90
|
-
|
|
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.
|
|
91
145
|
|
|
92
|
-
|
|
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.
|
|
93
149
|
|
|
94
|
-
|
|
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.
|
|
95
154
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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.
|
|
100
162
|
|
|
101
|
-
|
|
102
|
-
|
|
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.
|
|
103
165
|
|
|
104
|
-
###
|
|
166
|
+
### Type into a terminal
|
|
105
167
|
|
|
106
168
|
```sh
|
|
107
|
-
cofluxd terminal send <taskId> --text "y" --enter #
|
|
108
|
-
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
|
|
109
171
|
```
|
|
110
172
|
|
|
111
|
-
|
|
173
|
+
For interactive confirmations (y/N, menus), or to add a command in the same shell after the
|
|
174
|
+
previous one finished. Discipline:
|
|
112
175
|
|
|
113
|
-
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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.
|
|
117
183
|
|
|
118
|
-
###
|
|
184
|
+
### Report progress
|
|
119
185
|
|
|
120
186
|
```sh
|
|
121
|
-
cofluxd progress "
|
|
187
|
+
cofluxd progress "Reproduced; narrowing down the relay reconnect timing"
|
|
122
188
|
```
|
|
123
189
|
|
|
124
|
-
|
|
125
|
-
|
|
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`:
|
|
126
193
|
|
|
127
|
-
- `progress` =
|
|
128
|
-
- `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)
|
|
129
196
|
|
|
130
|
-
|
|
197
|
+
If unsure: when the user does not have to do anything, use `progress`.
|
|
131
198
|
|
|
132
|
-
###
|
|
199
|
+
### Call the user
|
|
133
200
|
|
|
134
201
|
```sh
|
|
135
|
-
cofluxd notify "
|
|
202
|
+
cofluxd notify "Both approaches work; I need you to pick one"
|
|
136
203
|
```
|
|
137
204
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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.
|
|
141
209
|
|
|
142
|
-
|
|
143
|
-
|
|
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".)
|
|
144
212
|
|
|
145
|
-
###
|
|
213
|
+
### Hand the user a clickable preview
|
|
146
214
|
|
|
147
215
|
```sh
|
|
148
216
|
cofluxd ports
|
|
149
217
|
```
|
|
150
218
|
|
|
151
|
-
|
|
152
|
-
|
|
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
|
|
153
233
|
|
|
154
|
-
|
|
234
|
+
Local commands only see the workspace you are in. Use the MCP server named `coflux` in the host
|
|
235
|
+
**only** for these:
|
|
155
236
|
|
|
156
|
-
|
|
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.
|
|
157
248
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
- **看/操作别的工作区、别的设备上的终端**:`list_*` → `read_terminal` / `send_terminal_input`。
|
|
161
|
-
- **不在 coflux 终端里**(比如用户在自己电脑上开的 Claude Code)时接入账号下的一切。
|
|
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.
|
|
162
251
|
|
|
163
|
-
###
|
|
252
|
+
### When MCP is not configured
|
|
164
253
|
|
|
165
|
-
|
|
166
|
-
|
|
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`):
|
|
167
257
|
|
|
168
258
|
```sh
|
|
169
259
|
claude mcp add --transport http coflux "$COFLUX_MCP_URL" # Claude Code
|
|
170
260
|
codex mcp add coflux --url "$COFLUX_MCP_URL" # Codex
|
|
171
261
|
```
|
|
172
262
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
3. **「需要升级」就停**:写 tools 在目标设备的 daemon 太旧时立即返回「该设备的 daemon 需要
|
|
206
|
-
升级」——这是能力门禁,不是暂时故障。转告用户在那台机器上 `cofluxd update && cofluxd restart`,
|
|
207
|
-
别重试也别换 tool 绕。
|
|
208
|
-
|
|
209
|
-
本地命令的纪律(先 read 再 send、被拒即停、`progress` 与 `notify` 分界、用 `wait` 别轮询)
|
|
210
|
-
在 MCP 里同样成立。
|
|
211
|
-
|
|
212
|
-
## 边界
|
|
213
|
-
|
|
214
|
-
- 你能开、读、等、输入,但**输入是人类优先的受限写权**:用户正在接管的终端你写不进去
|
|
215
|
-
(会被明确拒绝),用户随时接管也会把你顶掉。别和人抢终端。
|
|
216
|
-
- 本地命令只看得见**你自己所在的工作区**;别的工作区和别的机器要经 MCP,且限于同一账号。
|
|
217
|
-
- 一个工作区同时活着的终端有上限(默认 8,含用户自己开的)。撞上限先 `list` 看看,
|
|
218
|
-
多半是有跑完没收的;真是用户占满了,就 `notify` 告诉他,别硬试。
|
|
219
|
-
- 这些命令与 tools 都要 daemon 连着中心才能用——毕竟「让用户看见」就是它们的全部意义。
|
|
220
|
-
连不上时会明确报错,不会默默降级。
|
|
221
|
-
- `COFLUX_*` 变量只在 coflux 开出来的 PTY 里有;你自己 `export` 或改它们没有任何效果,
|
|
222
|
-
中心只认它自己下发的 id。
|
|
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.
|