@detergent-software/conductor 1.2.0-dev.5 → 1.2.0-dev.7
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 +19 -0
- package/README.md +18 -6
- package/build/cli.js +108 -20
- package/build/cli.js.map +1 -1
- package/build/conditions.js +20 -0
- package/build/conditions.js.map +1 -1
- package/build/daemon.js +14 -2
- package/build/daemon.js.map +1 -1
- package/build/help.js +12 -2
- package/build/help.js.map +1 -1
- package/build/history.js +96 -0
- package/build/history.js.map +1 -1
- package/build/manager.js +21 -4
- package/build/manager.js.map +1 -1
- package/build/notes.js +9 -1
- package/build/notes.js.map +1 -1
- package/build/queue.js +24 -10
- package/build/queue.js.map +1 -1
- package/build/scheduler.js +21 -1
- package/build/scheduler.js.map +1 -1
- package/build/startup.js +30 -16
- package/build/startup.js.map +1 -1
- package/build/store.js +8 -0
- package/build/store.js.map +1 -1
- package/build/supervise.js +153 -0
- package/build/supervise.js.map +1 -0
- package/build/upgrade.js +9 -5
- package/build/upgrade.js.map +1 -1
- package/mod/hooks/model.ts +6 -0
- package/mod/hooks/register.tsx +4 -1
- package/mod/types/index.d.ts +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
// serve's exit code when another daemon holds the lock: there is nothing for a supervisor to restart.
|
|
4
|
+
export const SERVE_LOCKED_EXIT = 3;
|
|
5
|
+
// The env var a supervisor passes a restarted serve, which records it as daemon.restarted.
|
|
6
|
+
export const RESTART_ENV = 'CONDUCTOR_RESTART';
|
|
7
|
+
// What a supervisor tells the serve it restarts: how many restarts this outage, how the last serve
|
|
8
|
+
// ended, when the outage began, and whether Jason was pushed about it.
|
|
9
|
+
export const restartInfoSchema = z.strictObject({
|
|
10
|
+
attempt: z.int().min(1),
|
|
11
|
+
code: z.int().nullable(),
|
|
12
|
+
downSince: z.iso.datetime(),
|
|
13
|
+
pushed: z.boolean(),
|
|
14
|
+
signal: z.string().nullable(),
|
|
15
|
+
});
|
|
16
|
+
// How long after a serve exits its pipes may still deliver what it wrote.
|
|
17
|
+
const EXIT_DRAIN_MS = 1_000;
|
|
18
|
+
// serve prints this once the daemon listens (cli.ts).
|
|
19
|
+
const LISTENING = /^conductor listening on /m;
|
|
20
|
+
export const SUPERVISE_POLICY = {
|
|
21
|
+
afterPushMs: 5 * 60_000,
|
|
22
|
+
backoffMs: [1_000, 5_000, 15_000, 30_000, 60_000],
|
|
23
|
+
failFast: false,
|
|
24
|
+
healthyMs: 5 * 60_000,
|
|
25
|
+
pushAfterMs: 2 * 60_000,
|
|
26
|
+
};
|
|
27
|
+
// Runs `node cli.js serve` in home and resolves when it exits, with the last 2 KB of its stderr and
|
|
28
|
+
// whether it came up.
|
|
29
|
+
export function serveChild(home, cliPath) {
|
|
30
|
+
return (env) => new Promise((resolve) => {
|
|
31
|
+
let stderr = '';
|
|
32
|
+
let up = false;
|
|
33
|
+
let settled = false;
|
|
34
|
+
const done = (exit, extra = '') => {
|
|
35
|
+
if (settled)
|
|
36
|
+
return;
|
|
37
|
+
settled = true;
|
|
38
|
+
resolve({ ...exit, stderr: (stderr + extra).trim().slice(-2048), up });
|
|
39
|
+
};
|
|
40
|
+
// Only this restart's info: never one this supervisor inherited.
|
|
41
|
+
const base = { ...process.env };
|
|
42
|
+
delete base[RESTART_ENV];
|
|
43
|
+
const child = spawn(process.execPath, [cliPath, 'serve'], {
|
|
44
|
+
cwd: home,
|
|
45
|
+
env: { ...base, ...env },
|
|
46
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
47
|
+
windowsHide: true,
|
|
48
|
+
});
|
|
49
|
+
// Read to the end, so a full pipe never blocks the serve.
|
|
50
|
+
let stdout = '';
|
|
51
|
+
child.stdout.setEncoding('utf8').on('data', (chunk) => {
|
|
52
|
+
if (up)
|
|
53
|
+
return;
|
|
54
|
+
stdout = (stdout + chunk).slice(-4096);
|
|
55
|
+
up = LISTENING.test(stdout);
|
|
56
|
+
});
|
|
57
|
+
child.stderr.setEncoding('utf8').on('data', (chunk) => (stderr = (stderr + chunk).slice(-4096)));
|
|
58
|
+
child.on('error', (error) => done({ code: null, signal: null }, error.message));
|
|
59
|
+
child.on('close', (code, signal) => done({ code, signal }));
|
|
60
|
+
// 'close' waits for every holder of the pipes, and a process the daemon started can inherit
|
|
61
|
+
// them (it does on Linux): so a second after the exit, stop reading and go on without it.
|
|
62
|
+
child.on('exit', (code, signal) => setTimeout(() => {
|
|
63
|
+
child.stdout.destroy();
|
|
64
|
+
child.stderr.destroy();
|
|
65
|
+
done({ code, signal });
|
|
66
|
+
}, EXIT_DRAIN_MS));
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// Keeps `conduct serve` running (ATK-417). A serve that exits 0 was stopped (conduct stop, upgrade,
|
|
70
|
+
// Ctrl+C) and one that finds the lock held has another daemon beside it: either ends the supervisor.
|
|
71
|
+
// Anything else (a crash, a kill, a start that fails) restarts it after a backoff. An outage that
|
|
72
|
+
// lasts pushAfterMs sends one push, held through quiet hours. Resolves with the exit code the
|
|
73
|
+
// supervisor should end with.
|
|
74
|
+
export async function supervise(deps, policy = SUPERVISE_POLICY) {
|
|
75
|
+
let outage = null;
|
|
76
|
+
for (let first = true;; first = false) {
|
|
77
|
+
const started = deps.now();
|
|
78
|
+
const exit = await deps.serve(restartEnv(outage));
|
|
79
|
+
const ended = deps.now();
|
|
80
|
+
const how = { code: exit.code, signal: exit.signal };
|
|
81
|
+
if (exit.code === 0 || deps.stopping()) {
|
|
82
|
+
deps.log.log('info', 'the daemon stopped; the supervisor ends', how);
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
if (exit.code === SERVE_LOCKED_EXIT) {
|
|
86
|
+
deps.log.log('info', 'another daemon holds the lock; the supervisor ends', how);
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
if (first && policy.failFast && !exit.up) {
|
|
90
|
+
deps.log.log('error', 'the daemon failed to start; the supervisor ends', { ...how, stderr: exit.stderr });
|
|
91
|
+
return exit.code ?? 1;
|
|
92
|
+
}
|
|
93
|
+
outage = nextOutage(ended - started < policy.healthyMs ? outage : null, exit, ended);
|
|
94
|
+
if (!outage.pushed && ended - outage.since >= policy.pushAfterMs && !deps.quiet(new Date(ended))) {
|
|
95
|
+
// Once per outage, sent or not: a missing credential would fail every time.
|
|
96
|
+
outage.pushed = true;
|
|
97
|
+
const result = await deps
|
|
98
|
+
.push(downMessage(outage, deps.logPath))
|
|
99
|
+
.catch((error) => ({ error: String(error), ok: false }));
|
|
100
|
+
if (result.ok)
|
|
101
|
+
deps.log.log('info', 'pushed that the daemon is down', { attempt: outage.attempt });
|
|
102
|
+
else
|
|
103
|
+
deps.log.log('warn', 'could not push that the daemon is down', { error: result.error });
|
|
104
|
+
}
|
|
105
|
+
const delay = outage.pushed
|
|
106
|
+
? policy.afterPushMs
|
|
107
|
+
: (policy.backoffMs[Math.min(outage.attempt, policy.backoffMs.length) - 1] ?? policy.afterPushMs);
|
|
108
|
+
deps.log.log('warn', 'the daemon exited; the supervisor restarts it', {
|
|
109
|
+
...how,
|
|
110
|
+
attempt: outage.attempt,
|
|
111
|
+
inMs: delay,
|
|
112
|
+
...(exit.stderr ? { stderr: exit.stderr } : {}),
|
|
113
|
+
});
|
|
114
|
+
await deps.sleep(delay);
|
|
115
|
+
// Asked to stop while it waited (Ctrl+C cuts the wait short): no next serve.
|
|
116
|
+
if (deps.stopping()) {
|
|
117
|
+
deps.log.log('info', 'the supervisor was stopped between restarts', { attempt: outage.attempt });
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function downMessage(outage, logPath) {
|
|
123
|
+
const since = new Date(outage.since).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
124
|
+
const ended = outage.last.signal ?? `exit code ${outage.last.code ?? '?'}`;
|
|
125
|
+
return {
|
|
126
|
+
message: [
|
|
127
|
+
`Down since ${since}: ${outage.attempt} runs failed, the last with ${ended}.`,
|
|
128
|
+
...(outage.last.stderr ? [outage.last.stderr.slice(-400)] : []),
|
|
129
|
+
`See ${logPath}`,
|
|
130
|
+
].join('\n'),
|
|
131
|
+
title: 'conductor · the daemon is down',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// The outage with this exit added. A serve that was up long enough (no prior) starts a new one.
|
|
135
|
+
function nextOutage(prior, exit, at) {
|
|
136
|
+
return prior
|
|
137
|
+
? { ...prior, attempt: prior.attempt + 1, last: exit }
|
|
138
|
+
: { attempt: 1, last: exit, pushed: false, since: at };
|
|
139
|
+
}
|
|
140
|
+
// What the next serve is told about the outage it ends, if any.
|
|
141
|
+
function restartEnv(outage) {
|
|
142
|
+
if (!outage)
|
|
143
|
+
return {};
|
|
144
|
+
const info = {
|
|
145
|
+
attempt: outage.attempt,
|
|
146
|
+
code: outage.last.code,
|
|
147
|
+
downSince: new Date(outage.since).toISOString(),
|
|
148
|
+
pushed: outage.pushed,
|
|
149
|
+
signal: outage.last.signal,
|
|
150
|
+
};
|
|
151
|
+
return { [RESTART_ENV]: JSON.stringify(info) };
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=supervise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervise.js","sourceRoot":"","sources":["../src/supervise.ts"],"names":[],"mappings":"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"}
|
package/build/upgrade.js
CHANGED
|
@@ -24,7 +24,7 @@ export function parseTag(tag) {
|
|
|
24
24
|
throw new ConductorError(`--tag takes ${TAGS.join(' or ')}, not "${tag}"`);
|
|
25
25
|
}
|
|
26
26
|
// Starts the daemon detached and says how: through the logon task when it runs this cli.js, else
|
|
27
|
-
// as a detached `conduct
|
|
27
|
+
// as a detached `conduct supervise`. For upgrade's restart and `conduct start`.
|
|
28
28
|
export async function restartDaemon(deps) {
|
|
29
29
|
if (deps.taskRunsCli()) {
|
|
30
30
|
try {
|
|
@@ -37,12 +37,13 @@ export async function restartDaemon(deps) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
deps.serveDetached();
|
|
40
|
-
return `with conduct
|
|
40
|
+
return `with conduct supervise (pid ${await deps.awaitDaemon()})`;
|
|
41
41
|
}
|
|
42
42
|
// Stop, install, restart. Refuses a linked build, and a tag older than the installed version unless
|
|
43
43
|
// allowDowngrade, before it touches anything; does nothing when the tag's version is already
|
|
44
44
|
// installed, and starts the daemon again only if it stopped one: through the logon task when that
|
|
45
|
-
// runs this cli.js (a global install keeps the path), else detached.
|
|
45
|
+
// runs this cli.js (a global install keeps the path), else detached. A logon task that still runs
|
|
46
|
+
// the unsupervised `conduct serve` gets a line saying to register it again.
|
|
46
47
|
export async function upgrade({ allowDowngrade = false, tag }, deps) {
|
|
47
48
|
const root = await deps.npmRoot();
|
|
48
49
|
if (!isGlobalInstall(deps.cliPath, root)) {
|
|
@@ -93,11 +94,14 @@ export async function upgrade({ allowDowngrade = false, tag }, deps) {
|
|
|
93
94
|
if (restartError) {
|
|
94
95
|
throw new ConductorError(`installed ${deps.installedVersion()}, but the conductor did not start again`, [
|
|
95
96
|
messageOf(restartError),
|
|
96
|
-
'start it with `conduct
|
|
97
|
+
'start it with `conduct start`',
|
|
97
98
|
]);
|
|
98
99
|
}
|
|
99
100
|
deps.out(`${downgrade ? 'downgraded' : 'upgraded'} ${from} → ${deps.installedVersion()} (from ${chosen})`);
|
|
100
|
-
deps.out(restart ? `started the conductor ${restart}` : 'the conductor was not running; start it with `conduct
|
|
101
|
+
deps.out(restart ? `started the conductor ${restart}` : 'the conductor was not running; start it with `conduct start`');
|
|
102
|
+
if (deps.taskRunsCli() && !deps.taskSupervises()) {
|
|
103
|
+
deps.out(`the "${TASK_NAME}" logon task runs conduct serve, which nothing restarts if it dies: run \`conduct install-startup\` once to supervise it`);
|
|
104
|
+
}
|
|
101
105
|
// A folder marketplace is read in place, so the mod was replaced with the package.
|
|
102
106
|
deps.out('if the conductor mod is installed (conduct install-mod), it is the new version too: run /reload-plugins in open Claude sessions');
|
|
103
107
|
}
|
package/build/upgrade.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAI5B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,CAAC,MAAM,OAAO,GAAG,+BAA+B,CAAC;AACvD,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"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"}
|
package/mod/hooks/model.ts
CHANGED
|
@@ -167,6 +167,11 @@ export function glyph(status: string): string {
|
|
|
167
167
|
return '·';
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
// A worker's PR as its row shows it: `#1850`; the expanded row shows the whole ref.
|
|
171
|
+
export function prLabel(pr: null | string): string {
|
|
172
|
+
return pr === null ? '' : `#${pr.slice(pr.lastIndexOf('#') + 1)}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
170
175
|
// What a worker waits on or does: its open wait, else the session's activity.
|
|
171
176
|
export function workerDoing(w: ConductorWorker): string {
|
|
172
177
|
return w.waits[0] ?? w.activity;
|
|
@@ -329,6 +334,7 @@ export function parseFleet(body: unknown): ConductorWorker[] | null {
|
|
|
329
334
|
item: typeof w.item === 'string' ? w.item : w.name,
|
|
330
335
|
name: w.name,
|
|
331
336
|
phase: typeof w.phase === 'string' ? w.phase : null,
|
|
337
|
+
pr: typeof w.pr === 'string' ? w.pr : null,
|
|
332
338
|
project: typeof w.project === 'string' ? w.project : '?',
|
|
333
339
|
spendToday: spend,
|
|
334
340
|
status: w.status,
|
package/mod/hooks/register.tsx
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
parseLine,
|
|
28
28
|
type PingOutcome,
|
|
29
29
|
pingOutcome,
|
|
30
|
+
prLabel,
|
|
30
31
|
profileText,
|
|
31
32
|
projectGroups,
|
|
32
33
|
type PushOutcome,
|
|
@@ -783,7 +784,8 @@ export const register: Register = (on) => {
|
|
|
783
784
|
onPress={() => update($, expanded, (name) => (name === w.name ? null : w.name))}
|
|
784
785
|
plain
|
|
785
786
|
>
|
|
786
|
-
{glyph(w.status)} {w.name} {w.phase ?? ''}
|
|
787
|
+
{glyph(w.status)} {w.name} {w.phase ?? ''}
|
|
788
|
+
{w.pr === null ? '' : ` ${prLabel(w.pr)}`}{' '}
|
|
787
789
|
<Text dimColor>
|
|
788
790
|
{workerDoing(w)} · {age(w.updatedAt, now)} · {tokens(w.spendToday)} tok
|
|
789
791
|
</Text>
|
|
@@ -798,6 +800,7 @@ export const register: Register = (on) => {
|
|
|
798
800
|
<Button key={`why-${w.id}`} label='why' onPress={async () => $.ui.toast(await why($, w.item))} />
|
|
799
801
|
<Text dimColor>
|
|
800
802
|
{w.status}
|
|
803
|
+
{w.pr === null ? '' : ` · PR ${w.pr}`}
|
|
801
804
|
{w.waits.length > 0 ? ` · waits on ${w.waits.join(', ')}` : ''}
|
|
802
805
|
</Text>
|
|
803
806
|
</Box>
|
package/mod/types/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export type ConductorWorker = {
|
|
|
6
6
|
item: string;
|
|
7
7
|
name: string;
|
|
8
8
|
phase: string | null;
|
|
9
|
+
// Its PR as the daemon spells it, such as `ado:Emergent.AgenticToolkit#1850`; null before it has one.
|
|
10
|
+
pr: string | null;
|
|
9
11
|
project: string;
|
|
10
12
|
spendToday: number;
|
|
11
13
|
status: string;
|
package/package.json
CHANGED