@love-moon/conductor-cli 0.11.0 → 0.11.2

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,102 @@
1
1
  # @love-moon/conductor-cli
2
2
 
3
+ ## 0.11.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 938efe7: Fix "New task from this" when the successor runs on a different daemon.
8
+
9
+ Picking another daemon in the dialog created the successor row and then killed
10
+ it one second later with `new task failed: Could not resolve resume cwd`. The
11
+ server intentionally drops the source machine's paths for a cross-daemon fork,
12
+ and the target daemon had nothing left to resolve: no launch-config `cwd`, no
13
+ worktree, no local project path (the default project has none, and a project
14
+ bound to another daemon is refused by design), and no source session in its own
15
+ session store.
16
+
17
+ Fork modes now fall back to a fresh `<workspace>/<date>/<run>` directory — the
18
+ same fallback `create_task` already uses — so the successor starts like a
19
+ brand-new task on that machine and picks up the conversation through the
20
+ resume-context URL. In-place restarts still fail loudly, because they must reuse
21
+ the original working directory.
22
+
23
+ - @love-moon/conductor-sdk@0.11.2
24
+ - @love-moon/ai-sdk@0.11.2
25
+
26
+ ## 0.11.1
27
+
28
+ ### Patch Changes
29
+
30
+ - cd5e428: Add a built-in "Update daemon" action to the web settings Danger zone.
31
+
32
+ This replaces the hand-rolled `update` custom command people were keeping in
33
+ `custom_commands` (upgrade the CLI, then restart the daemon in tmux) with a
34
+ first-class button on any daemon advertising the new `update_daemon` capability.
35
+
36
+ The update is designed so that a failure never leaves the machine without a
37
+ daemon:
38
+
39
+ - It runs in a **detached** updater process, not inside the daemon, so the
40
+ daemon it replaces can exit without taking the update with it — and a
41
+ crashing update cannot take the daemon down either.
42
+ - Install and verification happen **first**. The running daemon is only stopped
43
+ once the new version is installed, reports the expected version, and passes
44
+ the node-pty native check. Anything failing before that aborts with the old
45
+ daemon still running and still serving tasks.
46
+ - A half-removed global install (the `ENOTEMPTY` case) is uninstalled, its
47
+ package directory removed, and the install retried once.
48
+ - Progress is journaled to `~/.conductor/state/daemon-update.json` (full output
49
+ in `~/.conductor/logs/daemon-update.log`), so the outcome survives the restart
50
+ and the web UI can poll for it across the daemon's reconnect.
51
+
52
+ Homebrew installs, non-global installs, and shared guest daemons refuse the
53
+ action up front with the reason shown in the UI.
54
+
55
+ - 8ad51f3: Adopt the tmux-detached Fire processes a previous daemon left running, instead
56
+ of judging them stale and marking their tasks `killed`.
57
+
58
+ With `fire_tmux_mode`, daemon shutdown deliberately leaves Fires alive. But a
59
+ fresh daemon started with an empty `activeTaskProcesses` map, and both stale
60
+ sweeps — `recoverStaleTasks()` on startup and `reconcileAssignedTasks()` on
61
+ websocket reconnect — decided "this task is dead" from that map alone. Every
62
+ survivor of the hand-off was therefore PATCHed to `killed` seconds after the
63
+ new daemon connected, destroying the work the hand-off exists to preserve.
64
+ This regressed three times (2026-07-23, 2026-07-31, 2026-08-31).
65
+
66
+ The daemon now hands its Fires over to its successor:
67
+
68
+ - Each tmux spawn persists a hand-off record under
69
+ `$CONDUCTOR_HOME/daemon/fire-sessions/`. It carries the fields the liveness
70
+ reaper cannot recompute — most importantly `exitMarkerToken`, a per-spawn
71
+ nonce without which a successor could never read a Fire's exit code.
72
+ - Startup enumerates surviving `conductor-fire-*` sessions and re-registers
73
+ them, so they regain the same watcher a freshly spawned Fire has. Both stale
74
+ sweeps wait for that pass before judging anything, and also adopt in place
75
+ any live session they find without a record.
76
+ - A session that outlived the Fire inside it is detected via its exit marker,
77
+ killed, and reported — adopting it would strand the task at `running`.
78
+
79
+ Two failure modes that used to read as "nothing is running" are now handled
80
+ explicitly: `tmux list-sessions` failing (a wedged server, a timeout) is no
81
+ longer indistinguishable from "no sessions", and a `tmux -V` probe that fails
82
+ once at startup no longer makes the daemon blind to tmux for its whole life.
83
+ Neither authorizes a kill any more.
84
+
85
+ Also fixed along the way:
86
+
87
+ - `stop_task`, `restart_task`, `refresh_session_inplace` and duplicate
88
+ `create_task` gated on a record's child process, which an adopted record does
89
+ not have. Stopping an adopted task reported it killed while leaving the Fire
90
+ running; restarting one could spawn a second Fire into the same worktree.
91
+ - `Recovered N stale task(s) to killed` counted candidates rather than
92
+ successes, so it claimed recovery even after the PATCH returned 409 or 500.
93
+ - Tasks whose spawn is still in flight are no longer eligible for stale kills.
94
+
95
+ Behaviour outside `fire_tmux_mode` is unchanged.
96
+
97
+ - @love-moon/conductor-sdk@0.11.1
98
+ - @love-moon/ai-sdk@0.11.1
99
+
3
100
  ## 0.11.0
4
101
 
5
102
  ### Minor Changes
@@ -415,8 +415,10 @@ async function main() {
415
415
  "# Optional features",
416
416
  "# ---------------------------------------------------------------------------",
417
417
  "",
418
- "# Extra Codex auth.json files to switch between from the app's account",
419
- "# picker. The file name (without .json) becomes the account name.",
418
+ "# Optional: extra Codex auth.json files to switch between from the app's",
419
+ "# account picker (shown only when more than one is listed). The file name",
420
+ "# (without .json) becomes the account name. Without this, quota comes from",
421
+ "# $CODEX_HOME/auth.json (default ~/.codex/auth.json).",
420
422
  "# ai_manager:",
421
423
  "# codex:",
422
424
  "# auth_json:",
@@ -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.2",
4
+ "gitCommitId": "067735d",
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.2",
28
+ "@love-moon/conductor-sdk": "0.11.2",
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.2"
42
42
  },
43
43
  "pnpm": {
44
44
  "onlyBuiltDependencies": [