cofluxd 0.10.0 → 0.11.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/cofluxd.mjs CHANGED
@@ -761,6 +761,11 @@ async function cmdHook() {
761
761
 
762
762
  const AGENT_TIMEOUT_MS = 30_000;
763
763
  const DEFAULT_READ_LINES = 200;
764
+ // wait 的循环必须在 CLI 侧:单次 agentPost 撑不起长等待(daemon 控制 WS 有自己的往返超时)。
765
+ // 默认 30 分钟——编码任务常跑很久;轮询走 terminal.list(status 来自 sessionExit 事件链,
766
+ // 不受快照 ~2s 延迟影响),3 秒一次对本机 loopback 是零负担。
767
+ const DEFAULT_WAIT_TIMEOUT_S = 1800;
768
+ const WAIT_POLL_MS = 3000;
764
769
 
765
770
  async function agentPost(body) {
766
771
  const portResult = localGatewayPort();
@@ -824,8 +829,34 @@ async function cmdTerminal(values) {
824
829
  console.log(`# ${result.status}${exit}`);
825
830
  const text = tailLines(stripAnsi(result.ansi), lines);
826
831
  console.log(text || "(暂无输出)");
832
+ } else if (sub === "send") {
833
+ const taskId = positionals[2];
834
+ if (!taskId) die("terminal send 需要 <taskId>(用 cofluxd terminal list 查)");
835
+ const text = values.text ?? "";
836
+ if (!text && !values.enter) die(`terminal send 需要 --text "<文本>"(或至少 --enter 发一个回车)`);
837
+ await agentPost({ action: "terminal.send", taskId, text, enter: Boolean(values.enter) });
838
+ console.log(`已写入终端 ${taskId}(用 cofluxd terminal read ${taskId} 核对效果)`);
839
+ } else if (sub === "wait") {
840
+ const taskId = positionals[2];
841
+ if (!taskId) die("terminal wait 需要 <taskId>(用 cofluxd terminal list 查)");
842
+ const requested = Number(values.timeout);
843
+ const timeoutSec = Number.isFinite(requested) && requested > 0 ? requested : DEFAULT_WAIT_TIMEOUT_S;
844
+ const deadline = Date.now() + timeoutSec * 1000;
845
+ for (;;) {
846
+ const { terminals } = await agentPost({ action: "terminal.list" });
847
+ const t = terminals.find((x) => x.taskId === taskId);
848
+ if (!t) die(`本工作区没有终端 ${taskId}(用 cofluxd terminal list 查)`);
849
+ if (t.status === "exited") {
850
+ const exit = t.exitCode === undefined || t.exitCode === null ? "" : ` exit=${t.exitCode}`;
851
+ return void console.log(`# exited${exit}`);
852
+ }
853
+ if (Date.now() >= deadline) {
854
+ die(`等待超时(${timeoutSec}s):终端 ${taskId} 仍是 ${t.status}。可加大 --timeout,或 cofluxd terminal read ${taskId} 看现场`);
855
+ }
856
+ await sleep(WAIT_POLL_MS);
857
+ }
827
858
  } else {
828
- die(`terminal 需要子命令:new | list | read`);
859
+ die(`terminal 需要子命令:new | list | read | wait | send`);
829
860
  }
830
861
  }
831
862
 
@@ -836,6 +867,13 @@ async function cmdNotify() {
836
867
  console.log("已通知用户(工作区在侧栏转为「等待交互」)");
837
868
  }
838
869
 
870
+ async function cmdProgress() {
871
+ const message = positionals.slice(1).join(" ").trim();
872
+ if (!message) die(`progress 需要一句话,例如:cofluxd progress "复现了,正在定位 relay 重连"`);
873
+ await agentPost({ action: "progress", message });
874
+ console.log("已更新进度(显示在工作区卡片上,被下一条覆盖)");
875
+ }
876
+
839
877
  async function cmdPorts() {
840
878
  const { ports } = await agentPost({ action: "ports" });
841
879
  if (!ports.length) return void console.log("本工作区暂无监听端口");
@@ -864,7 +902,12 @@ const HELP = `cofluxd —— coflux daemon 管理
864
902
  cofluxd terminal list 列出本工作区的终端(含 status / 退出码)
865
903
  cofluxd terminal read <taskId> [--lines N]
866
904
  读某个终端的内容(纯文本,默认最后 200 行;终端已退出也能读)
905
+ cofluxd terminal wait <taskId> [--timeout <秒>]
906
+ 阻塞等到该终端退出,打印退出码(默认超时 30 分钟)
907
+ cofluxd terminal send <taskId> --text "<文本>" [--enter]
908
+ 往终端里输入文本(--enter 追加回车)。用户正在接管时会被拒
867
909
  cofluxd notify "<一句话>" 叫人:工作区在侧栏转为「等待交互」并显示这句话
910
+ cofluxd progress "<一句话>" 播报进度:显示在工作区卡片上,被下一条覆盖(不打扰用户)
868
911
  cofluxd ports 列出本工作区的监听端口及可直接打开的预览 URL
869
912
 
870
913
  up flags: --server <ws://.../daemon> --name <名> --shell <路径>
@@ -887,6 +930,9 @@ const { values, positionals } = parseArgs({
887
930
  title: { type: "string" },
888
931
  cmd: { type: "string" },
889
932
  lines: { type: "string" },
933
+ timeout: { type: "string" },
934
+ text: { type: "string" },
935
+ enter: { type: "boolean", default: false },
890
936
  version: { type: "string" },
891
937
  "bin-dir": { type: "string" },
892
938
  "no-start": { type: "boolean", default: false },
@@ -900,7 +946,7 @@ let cmd = positionals[0];
900
946
  if (values.help || cmd === "help") { console.log(HELP); process.exit(0); }
901
947
  if (!cmd) cmd = fs.existsSync(SETTINGS) ? "status" : "up"; // 首次裸跑 → 引导
902
948
 
903
- const handlers = { up: cmdUp, update: cmdUpdate, restart: cmdRestart, down: cmdDown, status: cmdStatus, doctor: cmdDoctor, fda: cmdFda, logs: cmdLogs, uninstall: cmdUninstall, hook: cmdHook, terminal: cmdTerminal, notify: cmdNotify, ports: cmdPorts };
949
+ const handlers = { up: cmdUp, update: cmdUpdate, restart: cmdRestart, down: cmdDown, status: cmdStatus, doctor: cmdDoctor, fda: cmdFda, logs: cmdLogs, uninstall: cmdUninstall, hook: cmdHook, terminal: cmdTerminal, notify: cmdNotify, progress: cmdProgress, ports: cmdPorts };
904
950
  const h = handlers[cmd];
905
951
  if (!h) die(`未知命令: ${cmd}${MIGRATED[cmd] ? `\n${MIGRATED[cmd]}` : ""}\n\n${HELP}`);
906
952
  await h(values);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cofluxd",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "coflux daemon 管理 CLI:装/起/停/升级 Rust daemon(supervisor + worker)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -56,7 +56,44 @@ cofluxd terminal read <taskId> --lines 50
56
56
  **终端已经退出也能 read**——「命令跑完了看输出」正是最常用的场景。
57
57
 
58
58
  内容有最多约 2 秒的延迟(来自中心的定期快照),所以刚 `new` 完立刻 `read` 可能是空的。
59
- 要等一条命令跑完,隔几秒 `list` 一次看它转没转 `exited`,别忙等。
59
+
60
+ ### 等命令跑完
61
+
62
+ ```sh
63
+ cofluxd terminal wait <taskId> # 阻塞到该终端退出,打印退出码(默认最长等 30 分钟)
64
+ cofluxd terminal wait <taskId> --timeout 300 # 自定超时(秒);超时会明确报错并非零退出
65
+ ```
66
+
67
+ 要等一条命令跑完就用 `wait`,**别自己写轮询循环**——它一条命令阻塞到位,退出码直接给你。
68
+ 超时不代表命令失败,只是还没跑完:`read` 看看现场再决定继续等还是处理。
69
+
70
+ ### 往终端里输入
71
+
72
+ ```sh
73
+ cofluxd terminal send <taskId> --text "y" --enter # 输入一行并回车
74
+ cofluxd terminal send <taskId> --enter # 只按一个回车
75
+ ```
76
+
77
+ 用在命令要交互确认(y/N、选项)、或想在跑完的同一 shell 里补一条命令的时候。纪律:
78
+
79
+ - **先 `read` 再 `send`**:看清终端现在在等什么再输入,别盲打。
80
+ - **用户正在接管时会被拒**——这不是错误,是设计:人永远优先。被拒就停手,
81
+ 要沟通用 `notify`,别重试。
82
+ - send 超时后**不要直接重发**:先 `read` 确认刚才那次到底进没进去,重复输入比丢输入更糟。
83
+
84
+ ### 播报进度
85
+
86
+ ```sh
87
+ cofluxd progress "复现了,正在定位 relay 重连的时序"
88
+ ```
89
+
90
+ 一句话告诉用户你干到哪了,显示在工作区卡片上,被下一条覆盖。在关键节点更新:复现了、
91
+ 定位到了、修完在验、卡在哪。它**不打扰用户**,和 `notify` 是两条信道:
92
+
93
+ - `progress` = 播报(用户扫一眼就知道进展,不需要回应)
94
+ - `notify` = 叫人(工作区转「等待交互」,用户该来看看了)
95
+
96
+ 拿不准用哪个:不需要用户做任何事就用 `progress`。
60
97
 
61
98
  ### 叫人
62
99
 
@@ -82,7 +119,8 @@ cofluxd ports
82
119
 
83
120
  ## 边界
84
121
 
85
- - 你**只能开和读**,不能往别人的终端里打字。要交互就 `notify` 让用户接管。
122
+ - 你能开、读、等、输入,但**输入是人类优先的受限写权**:用户正在接管的终端你写不进去
123
+ (会被明确拒绝),用户随时接管也会把你顶掉。别和人抢终端。
86
124
  - 能看到的只有**你自己所在的工作区**,别的工作区和别的机器都看不见也碰不到。
87
125
  - 一个工作区同时活着的终端有上限(默认 8,含用户自己开的)。撞上限先 `list` 看看,
88
126
  多半是有跑完没收的;真是用户占满了,就 `notify` 告诉他,别硬试。