@love-moon/conductor-cli 0.11.0 → 0.11.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,79 @@
1
1
  # @love-moon/conductor-cli
2
2
 
3
+ ## 0.11.1
4
+
5
+ ### Patch Changes
6
+
7
+ - cd5e428: Add a built-in "Update daemon" action to the web settings Danger zone.
8
+
9
+ This replaces the hand-rolled `update` custom command people were keeping in
10
+ `custom_commands` (upgrade the CLI, then restart the daemon in tmux) with a
11
+ first-class button on any daemon advertising the new `update_daemon` capability.
12
+
13
+ The update is designed so that a failure never leaves the machine without a
14
+ daemon:
15
+
16
+ - It runs in a **detached** updater process, not inside the daemon, so the
17
+ daemon it replaces can exit without taking the update with it — and a
18
+ crashing update cannot take the daemon down either.
19
+ - Install and verification happen **first**. The running daemon is only stopped
20
+ once the new version is installed, reports the expected version, and passes
21
+ the node-pty native check. Anything failing before that aborts with the old
22
+ daemon still running and still serving tasks.
23
+ - A half-removed global install (the `ENOTEMPTY` case) is uninstalled, its
24
+ package directory removed, and the install retried once.
25
+ - Progress is journaled to `~/.conductor/state/daemon-update.json` (full output
26
+ in `~/.conductor/logs/daemon-update.log`), so the outcome survives the restart
27
+ and the web UI can poll for it across the daemon's reconnect.
28
+
29
+ Homebrew installs, non-global installs, and shared guest daemons refuse the
30
+ action up front with the reason shown in the UI.
31
+
32
+ - 8ad51f3: Adopt the tmux-detached Fire processes a previous daemon left running, instead
33
+ of judging them stale and marking their tasks `killed`.
34
+
35
+ With `fire_tmux_mode`, daemon shutdown deliberately leaves Fires alive. But a
36
+ fresh daemon started with an empty `activeTaskProcesses` map, and both stale
37
+ sweeps — `recoverStaleTasks()` on startup and `reconcileAssignedTasks()` on
38
+ websocket reconnect — decided "this task is dead" from that map alone. Every
39
+ survivor of the hand-off was therefore PATCHed to `killed` seconds after the
40
+ new daemon connected, destroying the work the hand-off exists to preserve.
41
+ This regressed three times (2026-07-23, 2026-07-31, 2026-08-31).
42
+
43
+ The daemon now hands its Fires over to its successor:
44
+
45
+ - Each tmux spawn persists a hand-off record under
46
+ `$CONDUCTOR_HOME/daemon/fire-sessions/`. It carries the fields the liveness
47
+ reaper cannot recompute — most importantly `exitMarkerToken`, a per-spawn
48
+ nonce without which a successor could never read a Fire's exit code.
49
+ - Startup enumerates surviving `conductor-fire-*` sessions and re-registers
50
+ them, so they regain the same watcher a freshly spawned Fire has. Both stale
51
+ sweeps wait for that pass before judging anything, and also adopt in place
52
+ any live session they find without a record.
53
+ - A session that outlived the Fire inside it is detected via its exit marker,
54
+ killed, and reported — adopting it would strand the task at `running`.
55
+
56
+ Two failure modes that used to read as "nothing is running" are now handled
57
+ explicitly: `tmux list-sessions` failing (a wedged server, a timeout) is no
58
+ longer indistinguishable from "no sessions", and a `tmux -V` probe that fails
59
+ once at startup no longer makes the daemon blind to tmux for its whole life.
60
+ Neither authorizes a kill any more.
61
+
62
+ Also fixed along the way:
63
+
64
+ - `stop_task`, `restart_task`, `refresh_session_inplace` and duplicate
65
+ `create_task` gated on a record's child process, which an adopted record does
66
+ not have. Stopping an adopted task reported it killed while leaving the Fire
67
+ running; restarting one could spawn a second Fire into the same worktree.
68
+ - `Recovered N stale task(s) to killed` counted candidates rather than
69
+ successes, so it claimed recovery even after the PATCH returned 409 or 500.
70
+ - Tasks whose spawn is still in flight are no longer eligible for stale kills.
71
+
72
+ Behaviour outside `fire_tmux_mode` is unchanged.
73
+
74
+ - @love-moon/conductor-sdk@0.11.1
75
+ - @love-moon/ai-sdk@0.11.1
76
+
3
77
  ## 0.11.0
4
78
 
5
79
  ### Minor Changes
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Internal entrypoint for the built-in "Update Daemon" action.
5
+ *
6
+ * The daemon spawns this file *detached* (see `createDaemonUpdateHandlers`) and
7
+ * then forgets about it: the updater has to outlive the very daemon it is about
8
+ * to replace. It is deliberately not registered as a `conductor` subcommand —
9
+ * users update interactively with `conductor update`.
10
+ *
11
+ * Usage: node bin/conductor-daemon-update.js '<params-json>'
12
+ */
13
+
14
+ import process from "node:process";
15
+
16
+ import { runDaemonUpdate, writeDaemonUpdateStatus } from "../src/daemon-update.js";
17
+
18
+ const raw = process.argv[2];
19
+ if (!raw) {
20
+ process.stderr.write("conductor-daemon-update: missing params JSON argument\n");
21
+ process.exit(2);
22
+ }
23
+
24
+ let params;
25
+ try {
26
+ params = JSON.parse(raw);
27
+ } catch (error) {
28
+ process.stderr.write(`conductor-daemon-update: invalid params JSON: ${error?.message || error}\n`);
29
+ process.exit(2);
30
+ }
31
+
32
+ let result;
33
+ try {
34
+ result = await runDaemonUpdate(params, {
35
+ writeLine: (line) => process.stdout.write(`[${new Date().toISOString()}] ${line}\n`),
36
+ });
37
+ } catch (error) {
38
+ // The status file is how the UI learns the outcome; an unexpected crash here
39
+ // must not leave it stuck on "running" forever.
40
+ const message = error?.stack || error?.message || String(error);
41
+ process.stderr.write(`conductor-daemon-update: ${message}\n`);
42
+ writeDaemonUpdateStatus(params.statusPath, {
43
+ runId: params.runId ?? null,
44
+ status: "failed",
45
+ phase: "done",
46
+ message: "Update failed",
47
+ error: message,
48
+ logPath: params.logPath ?? null,
49
+ daemonRestarted: false,
50
+ finishedAt: new Date().toISOString(),
51
+ });
52
+ process.exit(1);
53
+ }
54
+
55
+ process.exit(result.status === "completed" ? 0 : 1);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@love-moon/conductor-cli",
3
- "version": "0.11.0",
4
- "gitCommitId": "7d57852",
3
+ "version": "0.11.1",
4
+ "gitCommitId": "c174c7e",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/lovemoon-ai/conductor.git"
@@ -24,8 +24,8 @@
24
24
  "test": "node --test test/*.test.js"
25
25
  },
26
26
  "dependencies": {
27
- "@love-moon/ai-sdk": "0.11.0",
28
- "@love-moon/conductor-sdk": "0.11.0",
27
+ "@love-moon/ai-sdk": "0.11.1",
28
+ "@love-moon/conductor-sdk": "0.11.1",
29
29
  "@github/copilot-sdk": "^0.3.0",
30
30
  "chrome-launcher": "^1.2.1",
31
31
  "chrome-remote-interface": "^0.33.0",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "optionalDependencies": {
40
40
  "@roamhq/wrtc": "^0.10.0",
41
- "@love-moon/chat-web": "0.11.0"
41
+ "@love-moon/chat-web": "0.11.1"
42
42
  },
43
43
  "pnpm": {
44
44
  "onlyBuiltDependencies": [