@detergent-software/conductor 1.0.0-dev.3 → 1.0.0-dev.31
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 +240 -0
- package/README.md +391 -10
- package/build/brief-cli.js +68 -0
- package/build/brief-cli.js.map +1 -0
- package/build/brief.js +211 -0
- package/build/brief.js.map +1 -0
- package/build/broker-cli.js +126 -0
- package/build/broker-cli.js.map +1 -0
- package/build/broker.js +154 -0
- package/build/broker.js.map +1 -0
- package/build/cli.js +621 -99
- package/build/cli.js.map +1 -1
- package/build/clock.js +17 -0
- package/build/clock.js.map +1 -0
- package/build/conditions.js +20 -14
- package/build/conditions.js.map +1 -1
- package/build/config.js +95 -1
- package/build/config.js.map +1 -1
- package/build/console.js +176 -0
- package/build/console.js.map +1 -0
- package/build/credential.js +52 -0
- package/build/credential.js.map +1 -0
- package/build/daemon.js +168 -3
- package/build/daemon.js.map +1 -1
- package/build/events.js +107 -0
- package/build/events.js.map +1 -0
- package/build/help.js +447 -0
- package/build/help.js.map +1 -0
- package/build/history.js +634 -0
- package/build/history.js.map +1 -0
- package/build/inbox.js +408 -0
- package/build/inbox.js.map +1 -0
- package/build/manager.js +132 -34
- package/build/manager.js.map +1 -1
- package/build/mod.js +51 -0
- package/build/mod.js.map +1 -0
- package/build/note.js +71 -0
- package/build/note.js.map +1 -0
- package/build/notes.js +172 -0
- package/build/notes.js.map +1 -0
- package/build/notify.js +422 -0
- package/build/notify.js.map +1 -0
- package/build/overrides.js +83 -0
- package/build/overrides.js.map +1 -0
- package/build/paths.js +10 -1
- package/build/paths.js.map +1 -1
- package/build/profile.js +68 -2
- package/build/profile.js.map +1 -1
- package/build/pushover.js +67 -0
- package/build/pushover.js.map +1 -0
- package/build/queue.js +183 -0
- package/build/queue.js.map +1 -0
- package/build/registry.js +17 -10
- package/build/registry.js.map +1 -1
- package/build/rule-actions.js +444 -0
- package/build/rule-actions.js.map +1 -0
- package/build/rule-input.js +17 -0
- package/build/rule-input.js.map +1 -0
- package/build/rules-service.js +402 -0
- package/build/rules-service.js.map +1 -0
- package/build/rules.js +850 -0
- package/build/rules.js.map +1 -0
- package/build/scheduler.js +293 -0
- package/build/scheduler.js.map +1 -0
- package/build/serve.js +51 -0
- package/build/serve.js.map +1 -0
- package/build/server.js +329 -0
- package/build/server.js.map +1 -1
- package/build/spend.js +50 -0
- package/build/spend.js.map +1 -1
- package/build/store.js +452 -4
- package/build/store.js.map +1 -1
- package/build/trackers.js +15 -0
- package/build/trackers.js.map +1 -0
- package/build/upgrade.js +18 -14
- package/build/upgrade.js.map +1 -1
- package/build/wait.js +35 -41
- package/build/wait.js.map +1 -1
- package/build/watchers/ado-api.js +55 -0
- package/build/watchers/ado-api.js.map +1 -0
- package/build/watchers/ado.js +37 -49
- package/build/watchers/ado.js.map +1 -1
- package/build/watchers/boards-brief.js +63 -0
- package/build/watchers/boards-brief.js.map +1 -0
- package/build/watchers/boards-queue.js +120 -0
- package/build/watchers/boards-queue.js.map +1 -0
- package/build/watchers/boards-writes.js +83 -0
- package/build/watchers/boards-writes.js.map +1 -0
- package/build/watchers/boards.js +271 -0
- package/build/watchers/boards.js.map +1 -0
- package/build/watchers/github.js +5 -2
- package/build/watchers/github.js.map +1 -1
- package/build/watchers/index.js +1 -0
- package/build/watchers/index.js.map +1 -1
- package/build/watchers/linear-api.js +55 -0
- package/build/watchers/linear-api.js.map +1 -0
- package/build/watchers/linear-key.js +6 -45
- package/build/watchers/linear-key.js.map +1 -1
- package/build/watchers/linear-labels.js +43 -0
- package/build/watchers/linear-labels.js.map +1 -0
- package/build/watchers/linear-queue.js +96 -0
- package/build/watchers/linear-queue.js.map +1 -0
- package/build/watchers/linear.js +29 -18
- package/build/watchers/linear.js.map +1 -1
- package/build/why.js +78 -0
- package/build/why.js.map +1 -0
- package/mod/.claude-plugin/marketplace.json +6 -0
- package/mod/.claude-plugin/plugin.json +7 -0
- package/mod/README.md +17 -0
- package/mod/hooks/hooks.json +1 -0
- package/mod/hooks/model.ts +457 -0
- package/mod/hooks/register.tsx +542 -0
- package/mod/types/index.d.ts +57 -0
- package/package.json +4 -1
package/build/console.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ConductorError } from "./errors.js";
|
|
3
|
+
import { age } from "./inbox.js";
|
|
4
|
+
import { inboxItemSchema } from "./store.js";
|
|
5
|
+
// The console's escalations (design note, section 11). An `escalate console "<ask>"` rule action
|
|
6
|
+
// appends `console.escalated`, and the conductor mod wakes the console's model with it. The console
|
|
7
|
+
// reports back with `console.handled`, whose cause is the escalation, so `conduct why` follows a
|
|
8
|
+
// finding to the rule behind it. An escalation raised while no console was open stays open until a
|
|
9
|
+
// console handles it, and the inbox tells Jason to start one (unattendedEscalations).
|
|
10
|
+
// Older ones are left out: an ask that waited two weeks is stale.
|
|
11
|
+
export const ESCALATION_DAYS = 14;
|
|
12
|
+
// `conduct console` starts `claude` with these in the project's main checkout: the session's name,
|
|
13
|
+
// auto mode as conduct-atk's session ran, and the console skill as its first prompt.
|
|
14
|
+
export const CONSOLE_ARGS = ['-n', 'conductor', '--permission-mode', 'auto', '--', '/conductor-console'];
|
|
15
|
+
export const CONSOLE_WINDOW = 'conductor';
|
|
16
|
+
// How long escalations may wait with no console following the stream before Jason is told. It
|
|
17
|
+
// outlasts a console's own reconnects: the mod restarts `conduct events` within seconds, and the
|
|
18
|
+
// stream comes back as soon as a restarted daemon listens.
|
|
19
|
+
export const UNATTENDED_GRACE_MS = 30_000;
|
|
20
|
+
// The first command of the item that says no console is open, which also marks it as one.
|
|
21
|
+
const CONSOLE_COMMAND = 'conduct console';
|
|
22
|
+
export const escalationSchema = z.object({
|
|
23
|
+
at: z.string(),
|
|
24
|
+
id: z.number(),
|
|
25
|
+
// The tracker item it is about, when the rule had one.
|
|
26
|
+
item: z.string().nullable(),
|
|
27
|
+
project: z.string().nullable(),
|
|
28
|
+
// The rule that escalated, such as default:conductor-handoff.
|
|
29
|
+
rule: z.string().nullable(),
|
|
30
|
+
text: z.string(),
|
|
31
|
+
});
|
|
32
|
+
export const handledSchema = z.object({
|
|
33
|
+
escalation: escalationSchema,
|
|
34
|
+
event: z.number(),
|
|
35
|
+
// The inbox item `--ask` raised.
|
|
36
|
+
inbox: inboxItemSchema.nullable(),
|
|
37
|
+
});
|
|
38
|
+
export class NoEscalationError extends ConductorError {
|
|
39
|
+
}
|
|
40
|
+
export function consoleEscalations({ inbox, now = () => new Date(), store }) {
|
|
41
|
+
const since = () => new Date(now().getTime() - ESCALATION_DAYS * 24 * 60 * 60_000).toISOString();
|
|
42
|
+
const open = () => store.openEscalations(since()).map(toEscalation);
|
|
43
|
+
return {
|
|
44
|
+
// Synchronous, so no other handle runs between the check and the event. `inbox.raise` takes its
|
|
45
|
+
// own transaction (they don't nest).
|
|
46
|
+
handle: (id, { ask, text }) => {
|
|
47
|
+
const event = store.getEvent(id);
|
|
48
|
+
if (event?.kind !== 'console.escalated')
|
|
49
|
+
throw new NoEscalationError(`no escalation #${id}`, ['`conduct escalations` lists the open ones']);
|
|
50
|
+
const escalation = toEscalation(event);
|
|
51
|
+
if (event.at < since() || store.escalationHandled(id))
|
|
52
|
+
throw new ConductorError(`escalation #${id} is handled already, or older than ${ESCALATION_DAYS} days`);
|
|
53
|
+
const raised = ask
|
|
54
|
+
? inbox.raise({ commands: [], condition: null, item: escalation.item, kind: 'console', links: [], project: escalation.project, text, worker: null })
|
|
55
|
+
: null;
|
|
56
|
+
const handled = store.appendEvent({
|
|
57
|
+
data: { cause: id, outcome: text, ...(raised ? { inbox: raised.id } : {}) },
|
|
58
|
+
kind: 'console.handled',
|
|
59
|
+
project: escalation.project,
|
|
60
|
+
subject: escalation.item,
|
|
61
|
+
});
|
|
62
|
+
return { escalation, event: handled.id, inbox: raised };
|
|
63
|
+
},
|
|
64
|
+
open,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// The project whose checkout the console runs in: the one named, or the only one registered.
|
|
68
|
+
export function consoleProject(projects, name) {
|
|
69
|
+
const names = projects.map((p) => p.name).join(', ') || 'none';
|
|
70
|
+
if (name !== undefined) {
|
|
71
|
+
const named = projects.find((p) => p.name === name);
|
|
72
|
+
if (!named)
|
|
73
|
+
throw new ConductorError(`no project "${name}"`, [`registered: ${names}`]);
|
|
74
|
+
return named;
|
|
75
|
+
}
|
|
76
|
+
const [only, ...others] = projects;
|
|
77
|
+
if (!only)
|
|
78
|
+
throw new ConductorError('no projects: the console runs in a project checkout', ['add one with `conduct project add <path>`']);
|
|
79
|
+
if (others.length > 0)
|
|
80
|
+
throw new ConductorError('several projects: say which checkout the console runs in', [`--project <name>, one of ${names}`]);
|
|
81
|
+
return only;
|
|
82
|
+
}
|
|
83
|
+
// wt.exe's arguments for the console's tab: in the project's checkout, running this CLI's
|
|
84
|
+
// `console --here`, which starts `claude` with CONDUCTOR_CONSOLE=1 (wt.exe splits a command on `;`,
|
|
85
|
+
// so the tab can't set the variable itself).
|
|
86
|
+
export function consoleTabArgs(project, nodePath, cliPath) {
|
|
87
|
+
const split = [project.root, nodePath, cliPath, project.name].find((part) => part.includes(';'));
|
|
88
|
+
if (split !== undefined)
|
|
89
|
+
throw new ConductorError(`a console tab can't take a path with ";": ${split}`, ['run `conduct console --here` in a terminal instead']);
|
|
90
|
+
return ['-w', CONSOLE_WINDOW, 'new-tab', '--title', 'conductor console', '-d', project.root, nodePath, cliPath, 'console', '--here', '--project', project.name];
|
|
91
|
+
}
|
|
92
|
+
// `conduct escalations`: each open one with its age, item and rule, then the ask.
|
|
93
|
+
export function escalationLines(escalations, now) {
|
|
94
|
+
if (escalations.length === 0)
|
|
95
|
+
return ['no open escalations'];
|
|
96
|
+
return escalations.flatMap((e) => [
|
|
97
|
+
`#${e.id} ${[e.item, e.project === null ? null : `(${e.project})`].filter((x) => x !== null).join(' ') || 'the machine'} ${age(e.at, now)}${e.rule ? ` [${e.rule}]` : ''}`,
|
|
98
|
+
` ${e.text}`,
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
101
|
+
export function unattendedEscalations({ escalations, graceMs = UNATTENDED_GRACE_MS, inbox, later = laterTimer, }) {
|
|
102
|
+
let streams = 0;
|
|
103
|
+
let stopped = false;
|
|
104
|
+
let cancel = null;
|
|
105
|
+
const openItems = () => inbox.open().filter((i) => i.kind === 'console' && i.commands[0] === CONSOLE_COMMAND);
|
|
106
|
+
const resolveItems = (items, by) => {
|
|
107
|
+
for (const item of items)
|
|
108
|
+
inbox.resolve(item, by);
|
|
109
|
+
};
|
|
110
|
+
// Also clears an item whose escalations aged out of the 14 days unhandled. The text carries no
|
|
111
|
+
// count, which would go stale as escalations come and go.
|
|
112
|
+
const check = () => {
|
|
113
|
+
cancel = null;
|
|
114
|
+
if (streams > 0)
|
|
115
|
+
return;
|
|
116
|
+
const open = openItems();
|
|
117
|
+
if (escalations.open().length === 0)
|
|
118
|
+
resolveItems(open, 'no escalation waits');
|
|
119
|
+
else if (open.length === 0) {
|
|
120
|
+
inbox.raise({
|
|
121
|
+
commands: [CONSOLE_COMMAND, 'conduct escalations'],
|
|
122
|
+
condition: null,
|
|
123
|
+
item: null,
|
|
124
|
+
kind: 'console',
|
|
125
|
+
links: [],
|
|
126
|
+
project: null,
|
|
127
|
+
text: 'escalations wait for the console and none is open: start one with conduct console',
|
|
128
|
+
worker: null,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
// A check already due keeps its time, so a burst of escalations doesn't put it off. A stopping
|
|
133
|
+
// daemon ends the console's stream too, which checks nothing.
|
|
134
|
+
const checkSoon = () => {
|
|
135
|
+
if (!stopped && streams === 0 && cancel === null)
|
|
136
|
+
cancel = later(graceMs, check);
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
observe: (event) => {
|
|
140
|
+
if (event.kind === 'console.escalated')
|
|
141
|
+
checkSoon();
|
|
142
|
+
else if (event.kind === 'console.handled') {
|
|
143
|
+
// The inbox read first: the escalations' is two scans of the log, and usually no item is open.
|
|
144
|
+
const open = openItems();
|
|
145
|
+
if (open.length > 0 && escalations.open().length === 0)
|
|
146
|
+
resolveItems(open, 'no escalation waits');
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
start: checkSoon,
|
|
150
|
+
stop: () => {
|
|
151
|
+
stopped = true;
|
|
152
|
+
cancel?.();
|
|
153
|
+
cancel = null;
|
|
154
|
+
},
|
|
155
|
+
streamClosed: () => {
|
|
156
|
+
streams = Math.max(0, streams - 1);
|
|
157
|
+
checkSoon();
|
|
158
|
+
},
|
|
159
|
+
streamOpened: () => {
|
|
160
|
+
streams++;
|
|
161
|
+
cancel?.();
|
|
162
|
+
cancel = null;
|
|
163
|
+
resolveItems(openItems(), 'a console opened');
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function laterTimer(ms, action) {
|
|
168
|
+
const timer = setTimeout(action, ms);
|
|
169
|
+
timer.unref();
|
|
170
|
+
return () => clearTimeout(timer);
|
|
171
|
+
}
|
|
172
|
+
function toEscalation(event) {
|
|
173
|
+
const text = event.data.text;
|
|
174
|
+
return { at: event.at, id: event.id, item: event.subject, project: event.project, rule: event.rule, text: typeof text === 'string' ? text : '' };
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=console.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.js","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { run } from "./run.js";
|
|
2
|
+
const NOT_FOUND = 3;
|
|
3
|
+
// Win32 ERROR_NOT_FOUND: CredRead's "no such credential". Any other failure is a broken read.
|
|
4
|
+
const ERROR_NOT_FOUND = 1168;
|
|
5
|
+
// Writes the user name, a newline, then the password.
|
|
6
|
+
const CRED_READ = `
|
|
7
|
+
$ErrorActionPreference = 'Stop'
|
|
8
|
+
try {
|
|
9
|
+
Add-Type -Namespace AtkConduct -Name Cred -MemberDefinition @'
|
|
10
|
+
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
11
|
+
public static extern bool CredRead(string target, int type, int flags, out System.IntPtr cred);
|
|
12
|
+
[DllImport("advapi32.dll")]
|
|
13
|
+
public static extern void CredFree(System.IntPtr cred);
|
|
14
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
15
|
+
public struct CREDENTIAL { public int Flags; public int Type; public string TargetName; public string Comment;
|
|
16
|
+
public long LastWritten; public int CredentialBlobSize; public System.IntPtr CredentialBlob; public int Persist;
|
|
17
|
+
public int AttributeCount; public System.IntPtr Attributes; public string TargetAlias; public string UserName; }
|
|
18
|
+
'@
|
|
19
|
+
$p = [IntPtr]::Zero
|
|
20
|
+
if (-not [AtkConduct.Cred]::CredRead($env:ATK_CONDUCT_CRED_TARGET, 1, 0, [ref]$p)) {
|
|
21
|
+
$code = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
|
22
|
+
if ($code -eq ${ERROR_NOT_FOUND}) { exit ${NOT_FOUND} }
|
|
23
|
+
[Console]::Error.WriteLine("CredRead failed with Win32 error $code"); exit 4
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
$c = [Runtime.InteropServices.Marshal]::PtrToStructure($p, [type][AtkConduct.Cred+CREDENTIAL])
|
|
27
|
+
[Console]::Out.Write([string]$c.UserName + [char]10)
|
|
28
|
+
[Console]::Out.Write([Runtime.InteropServices.Marshal]::PtrToStringUni($c.CredentialBlob, $c.CredentialBlobSize / 2))
|
|
29
|
+
} finally { [AtkConduct.Cred]::CredFree($p) }
|
|
30
|
+
} catch { [Console]::Error.WriteLine($_.Exception.Message); exit 4 }
|
|
31
|
+
`;
|
|
32
|
+
// Reads the generic credential `target`. Windows only.
|
|
33
|
+
export async function readCredential(target, env = process.env) {
|
|
34
|
+
if (process.platform !== 'win32')
|
|
35
|
+
return { found: false, reason: `the ${target} credential is Windows-only` };
|
|
36
|
+
const result = await run('powershell.exe', ['-NoProfile', '-NonInteractive', '-EncodedCommand', Buffer.from(CRED_READ, 'utf16le').toString('base64')], { env: { ...env, ATK_CONDUCT_CRED_TARGET: target }, timeoutMs: 30_000 });
|
|
37
|
+
if (result.code === 0) {
|
|
38
|
+
const newline = result.stdout.indexOf('\n');
|
|
39
|
+
const secret = result.stdout.slice(newline + 1).trim();
|
|
40
|
+
if (newline >= 0 && secret)
|
|
41
|
+
return { credential: { secret, user: result.stdout.slice(0, newline).trim() }, found: true };
|
|
42
|
+
}
|
|
43
|
+
if (result.code === NOT_FOUND)
|
|
44
|
+
return { found: false, reason: `no credential ${target}` };
|
|
45
|
+
// Never echo stdout here: on a partial failure it could hold the secret.
|
|
46
|
+
const line = result.stderr
|
|
47
|
+
.split(/\r?\n/)
|
|
48
|
+
.map((l) => l.trim())
|
|
49
|
+
.find((l) => l && !l.startsWith('#< CLIXML') && !l.startsWith('<Objs'));
|
|
50
|
+
return { found: false, reason: `the credential ${target} couldn't be read (${result.error ?? line ?? `exit ${result.code}`})` };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=credential.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential.js","sourceRoot":"","sources":["../src/credential.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAY/B,MAAM,SAAS,GAAG,CAAC,CAAC;AACpB,8FAA8F;AAC9F,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B,sDAAsD;AACtD,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;kBAgBA,eAAe,YAAY,SAAS;;;;;;;;;CASrD,CAAC;AAEF,uDAAuD;AACvD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc,EAAE,MAAyB,OAAO,CAAC,GAAG;IACvF,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,MAAM,6BAA6B,EAAE,CAAC;IAC9G,MAAM,MAAM,GAAG,MAAM,GAAG,CACtB,gBAAgB,EAChB,CAAC,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAC1G,EAAE,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,uBAAuB,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CACxE,CAAC;IACF,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACvD,IAAI,OAAO,IAAI,CAAC,IAAI,MAAM;YAAE,OAAO,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC3H,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,MAAM,EAAE,EAAE,CAAC;IAC1F,yEAAyE;IACzE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM;SACvB,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1E,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,MAAM,sBAAsB,MAAM,CAAC,KAAK,IAAI,IAAI,IAAI,QAAQ,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;AAClI,CAAC"}
|
package/build/daemon.js
CHANGED
|
@@ -1,29 +1,63 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { briefService, linearBriefs } from "./brief.js";
|
|
3
|
+
import { broker } from "./broker.js";
|
|
2
4
|
import { claudeCli } from "./claude.js";
|
|
3
5
|
import { ping } from "./client.js";
|
|
4
6
|
import { loadConfig } from "./config.js";
|
|
7
|
+
import { consoleEscalations, unattendedEscalations } from "./console.js";
|
|
5
8
|
import { ConductorError } from "./errors.js";
|
|
9
|
+
import { inboxService } from "./inbox.js";
|
|
6
10
|
import { acquirePidLock, processAlive } from "./lock.js";
|
|
7
11
|
import { rotatingLog } from "./log.js";
|
|
8
12
|
import { sessionManager } from "./manager.js";
|
|
13
|
+
import { linearNotes, noteService } from "./notes.js";
|
|
14
|
+
import { notifierService } from "./notify.js";
|
|
15
|
+
import { overridesService } from "./overrides.js";
|
|
16
|
+
import { pushoverSender } from "./pushover.js";
|
|
9
17
|
import { registerProject } from "./registry.js";
|
|
18
|
+
import { ruleActions } from "./rule-actions.js";
|
|
19
|
+
import { rulesService } from "./rules-service.js";
|
|
20
|
+
import { clockFields, selectRules } from "./rules.js";
|
|
21
|
+
import { schedulerService } from "./scheduler.js";
|
|
10
22
|
import { createApi } from "./server.js";
|
|
11
23
|
import { claudeProjectsDir } from "./spend.js";
|
|
12
24
|
import { openStore } from "./store.js";
|
|
13
25
|
import { windowsTerminalTab } from "./tab.js";
|
|
14
26
|
import { ensureToken } from "./token.js";
|
|
27
|
+
import { routedBriefs, routedLabels, routedNotes, routedQueue } from "./trackers.js";
|
|
28
|
+
import { azToken } from "./watchers/ado-api.js";
|
|
29
|
+
import { boardsBriefs } from "./watchers/boards-brief.js";
|
|
30
|
+
import { boardsQueue } from "./watchers/boards-queue.js";
|
|
31
|
+
import { boardsLabels, boardsNotes } from "./watchers/boards-writes.js";
|
|
15
32
|
import { startWatchers } from "./watchers/index.js";
|
|
16
|
-
import {} from "./watchers/
|
|
33
|
+
import { linearLabels } from "./watchers/linear-labels.js";
|
|
34
|
+
import { linearQueue } from "./watchers/linear-queue.js";
|
|
35
|
+
import { rateBudget } from "./watchers/source.js";
|
|
17
36
|
import { gitWorktrees } from "./worktrees.js";
|
|
18
37
|
// A pid file younger than this belongs to a daemon that may still be starting (it writes the pid
|
|
19
38
|
// file before the port file), so a second start refuses even though nothing answers yet.
|
|
20
39
|
const STARTUP_GRACE_MS = 30_000;
|
|
40
|
+
// Events after which the queue may have changed: a phase note, a worker gone, an inbox item cleared
|
|
41
|
+
// (a failed launch the scheduler holds), a project added.
|
|
42
|
+
const QUEUE_CHANGES = new Set(['inbox.resolved', 'note.posted', 'project.added', 'project.updated', 'worker.launch-failed', 'worker.retired']);
|
|
21
43
|
// The pid file's process runs and either is still starting or answers the API. Checking the API too
|
|
22
44
|
// matters after a reboot, when a stale pid file can name a pid some other process now has.
|
|
23
45
|
export function daemonRunning(paths) {
|
|
24
46
|
return async ({ ageMs, pid }) => processAlive(pid) && (ageMs < STARTUP_GRACE_MS || (await ping(paths, pid)));
|
|
25
47
|
}
|
|
26
|
-
|
|
48
|
+
// Under the logon task nobody sees stderr, so a crash goes to the log before the process exits. The
|
|
49
|
+
// next start records the exit (daemon.exited-uncleanly).
|
|
50
|
+
export function logCrashes(log, source = process) {
|
|
51
|
+
for (const event of ['uncaughtException', 'unhandledRejection']) {
|
|
52
|
+
source.on(event, (error) => {
|
|
53
|
+
log.log('error', event === 'uncaughtException' ? 'uncaught exception' : 'unhandled rejection', {
|
|
54
|
+
error: error instanceof Error ? (error.stack ?? String(error)) : String(error),
|
|
55
|
+
});
|
|
56
|
+
source.exit(1);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export async function startDaemon(paths, { consoleGraceMs, keepaliveMs, ports = {}, version, }) {
|
|
27
61
|
mkdirSync(paths.logs, { recursive: true });
|
|
28
62
|
const config = loadConfig(paths.config);
|
|
29
63
|
const lock = await acquirePidLock(paths.pid, daemonRunning(paths));
|
|
@@ -39,16 +73,113 @@ export async function startDaemon(paths, { keepaliveMs, ports = {}, version }) {
|
|
|
39
73
|
let stopping;
|
|
40
74
|
let resolveClosed = () => { };
|
|
41
75
|
const closed = new Promise((resolve) => (resolveClosed = resolve));
|
|
76
|
+
// Loaded at start(), once the inbox listens: a file refused then raises its item.
|
|
77
|
+
const rules = rulesService({
|
|
78
|
+
config,
|
|
79
|
+
emit: (event) => void opened.appendEvent(event),
|
|
80
|
+
listProjects: () => opened.listProjects(),
|
|
81
|
+
log,
|
|
82
|
+
paths,
|
|
83
|
+
updateProject: (project) => void opened.upsertProject(project),
|
|
84
|
+
});
|
|
42
85
|
const manager = sessionManager({
|
|
43
86
|
claude: ports.claude ?? claudeCli(),
|
|
44
87
|
config: config.sessions,
|
|
45
88
|
log,
|
|
46
89
|
openTab: ports.openTab ?? windowsTerminalTab(),
|
|
90
|
+
// Park and resume as the rules say (the defaults park-on-wait and resume-on-wait).
|
|
91
|
+
// A resume for a review comment is also a `pr.comment`, so a rule on that event can allow it.
|
|
92
|
+
policy: (verb, worker, waits) => {
|
|
93
|
+
const comment = waits.some((w) => w.resolvedAt !== null && /^pr:.+:comment$/.test(w.condition));
|
|
94
|
+
const { fired } = selectRules(rules.rules(), {
|
|
95
|
+
events: verb === 'park' ? ['worker.waiting'] : ['wait.resolved', ...(comment ? ['pr.comment'] : [])],
|
|
96
|
+
fields: { ...clockFields(new Date()), 'item.id': worker.item, project: worker.project, ...(worker.phase === null ? {} : { 'worker.phase': worker.phase }) },
|
|
97
|
+
project: worker.project,
|
|
98
|
+
scope: null,
|
|
99
|
+
});
|
|
100
|
+
const rule = fired.find((f) => f.action.verb === verb)?.rule.ref ?? null;
|
|
101
|
+
return { allowed: rule !== null, rule };
|
|
102
|
+
},
|
|
47
103
|
processAlive: ports.processAlive,
|
|
48
104
|
projectsDir: ports.projectsDir ?? claudeProjectsDir(),
|
|
49
105
|
store: opened,
|
|
50
106
|
worktrees: ports.worktrees ?? gitWorktrees(),
|
|
51
107
|
});
|
|
108
|
+
// One az token for the Boards reads and writes outside the watchers.
|
|
109
|
+
const boards = { token: azToken() };
|
|
110
|
+
const tracker = ports.noteTracker ?? routedNotes({ 'ado-boards': boardsNotes(boards), linear: linearNotes() });
|
|
111
|
+
const inbox = inboxService({ onWaitResolved: () => void manager.sweep(), store: opened, tracker });
|
|
112
|
+
const escalations = consoleEscalations({ inbox, store: opened });
|
|
113
|
+
const unattended = unattendedEscalations({ escalations, graceMs: consoleGraceMs, inbox });
|
|
114
|
+
const overrides = overridesService({ config, store: opened });
|
|
115
|
+
// Reads each tracker within its watcher's budget (Boards within ADO's), so the two never pass
|
|
116
|
+
// its limit together.
|
|
117
|
+
const ownBudgets = { ado: rateBudget(config.watchers.ado.perMinute), linear: rateBudget(config.watchers.linear.perMinute) };
|
|
118
|
+
const scheduler = schedulerService({
|
|
119
|
+
budget: (profile) => {
|
|
120
|
+
const api = profile.tracker.kind === 'linear' ? 'linear' : 'ado';
|
|
121
|
+
return watchers.budget(api) ?? ownBudgets[api];
|
|
122
|
+
},
|
|
123
|
+
config,
|
|
124
|
+
inbox,
|
|
125
|
+
log,
|
|
126
|
+
manager,
|
|
127
|
+
overrides,
|
|
128
|
+
reader: ports.queueReader ?? routedQueue({ 'ado-boards': boardsQueue(boards), linear: linearQueue() }),
|
|
129
|
+
rules: () => rules.rules(),
|
|
130
|
+
store: opened,
|
|
131
|
+
});
|
|
132
|
+
const notifier = notifierService({
|
|
133
|
+
config,
|
|
134
|
+
log,
|
|
135
|
+
pushover: ports.pushover ?? pushoverSender({ credential: config.notify.pushover.credential }),
|
|
136
|
+
store: opened,
|
|
137
|
+
});
|
|
138
|
+
const actions = ruleActions({
|
|
139
|
+
inbox,
|
|
140
|
+
labels: ports.labels ?? routedLabels({ 'ado-boards': boardsLabels(boards), linear: linearLabels() }),
|
|
141
|
+
log,
|
|
142
|
+
manager,
|
|
143
|
+
mode: config.scheduler.mode,
|
|
144
|
+
notifier,
|
|
145
|
+
overrides,
|
|
146
|
+
rules: () => rules.rules(),
|
|
147
|
+
scheduler,
|
|
148
|
+
store: opened,
|
|
149
|
+
});
|
|
150
|
+
// After the event that caused it, so an item's own events follow it on /events.
|
|
151
|
+
const stopObserving = opened.onEvent((event) => queueMicrotask(() => {
|
|
152
|
+
if (stopping)
|
|
153
|
+
return;
|
|
154
|
+
if (event.kind === 'project.added' || event.kind === 'project.updated') {
|
|
155
|
+
try {
|
|
156
|
+
rules.reload();
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
log.log('error', 'the rules failed to reload', { error: String(error), event: event.id });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// What changes the queue soon brings the next tick forward.
|
|
163
|
+
if (QUEUE_CHANGES.has(event.kind))
|
|
164
|
+
scheduler.nudge();
|
|
165
|
+
try {
|
|
166
|
+
inbox.observe(event);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
log.log('error', 'inbox failed on an event', { error: String(error), event: event.id, kind: event.kind });
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
unattended.observe(event);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
log.log('error', 'the console watch failed on an event', { error: String(error), event: event.id, kind: event.kind });
|
|
176
|
+
}
|
|
177
|
+
notifier.observe(event).catch((error) => {
|
|
178
|
+
log.log('error', 'the notifier failed on an event', { error: String(error), event: event.id, kind: event.kind });
|
|
179
|
+
});
|
|
180
|
+
// Last: an inbox item a rule acts on is raised by now.
|
|
181
|
+
actions.observe(event);
|
|
182
|
+
}));
|
|
52
183
|
const watchers = startWatchers({
|
|
53
184
|
config: config.watchers,
|
|
54
185
|
log,
|
|
@@ -58,11 +189,21 @@ export async function startDaemon(paths, { keepaliveMs, ports = {}, version }) {
|
|
|
58
189
|
store: opened,
|
|
59
190
|
});
|
|
60
191
|
const api = createApi({
|
|
192
|
+
briefs: briefService({ reader: ports.briefReader ?? routedBriefs({ 'ado-boards': boardsBriefs(boards), linear: linearBriefs() }), store: opened }),
|
|
193
|
+
broker: broker({ listening: ports.listening, store: opened }),
|
|
194
|
+
consoles: unattended,
|
|
195
|
+
escalations,
|
|
196
|
+
inbox,
|
|
61
197
|
keepaliveMs,
|
|
62
198
|
log,
|
|
63
199
|
manager,
|
|
64
|
-
|
|
200
|
+
notes: noteService({ store: opened, tracker }),
|
|
201
|
+
notifier,
|
|
202
|
+
overrides,
|
|
203
|
+
registerProject: (path) => registerProject(opened, path, rules.checkProfile),
|
|
65
204
|
requestShutdown: () => void stop('api'),
|
|
205
|
+
rules,
|
|
206
|
+
scheduler,
|
|
66
207
|
store: opened,
|
|
67
208
|
token,
|
|
68
209
|
});
|
|
@@ -71,8 +212,14 @@ export async function startDaemon(paths, { keepaliveMs, ports = {}, version }) {
|
|
|
71
212
|
opened.appendEvent({ data: { pid: process.pid, reason }, kind: 'daemon.stopped' });
|
|
72
213
|
log.log('info', 'stopping', { reason });
|
|
73
214
|
// Launches in flight carry on as processes of their own; a restart marks them failed.
|
|
215
|
+
rules.stop();
|
|
216
|
+
await actions.stop();
|
|
217
|
+
await scheduler.stop();
|
|
74
218
|
await watchers.stop();
|
|
75
219
|
await manager.stop();
|
|
220
|
+
stopObserving();
|
|
221
|
+
unattended.stop();
|
|
222
|
+
await notifier.stop();
|
|
76
223
|
api.endStreams();
|
|
77
224
|
await new Promise((resolve) => {
|
|
78
225
|
api.server.close(() => resolve());
|
|
@@ -92,9 +239,16 @@ export async function startDaemon(paths, { keepaliveMs, ports = {}, version }) {
|
|
|
92
239
|
});
|
|
93
240
|
port = addressOf(api.server.address()).port;
|
|
94
241
|
writeFileSync(paths.port, `${port}\n`);
|
|
242
|
+
recordUncleanExit(opened, log);
|
|
95
243
|
opened.appendEvent({ data: { pid: process.pid, port, version }, kind: 'daemon.started' });
|
|
244
|
+
inbox.start();
|
|
245
|
+
unattended.start();
|
|
246
|
+
rules.start();
|
|
247
|
+
notifier.start();
|
|
96
248
|
manager.start();
|
|
97
249
|
watchers.start();
|
|
250
|
+
scheduler.start();
|
|
251
|
+
actions.start();
|
|
98
252
|
log.log('info', 'started', { home: paths.home, pid: process.pid, port, version });
|
|
99
253
|
return { closed, log, port, stop };
|
|
100
254
|
}
|
|
@@ -112,6 +266,17 @@ function addressOf(address) {
|
|
|
112
266
|
throw new Error('the API is not listening on TCP');
|
|
113
267
|
return address;
|
|
114
268
|
}
|
|
269
|
+
// This start holds the lock, so no other daemon runs. A last daemon that started and recorded no
|
|
270
|
+
// stop ended without one (a crash, a closed window, a reboot), whatever its pid file says: say so
|
|
271
|
+
// before this start, once.
|
|
272
|
+
function recordUncleanExit(store, log) {
|
|
273
|
+
const last = store.latestEvent(['daemon.started', 'daemon.stopped', 'daemon.exited-uncleanly']);
|
|
274
|
+
if (last?.kind !== 'daemon.started')
|
|
275
|
+
return;
|
|
276
|
+
const data = { lastEventAt: store.latestEvent()?.at ?? last.at, pid: last.data.pid ?? null };
|
|
277
|
+
store.appendEvent({ data, kind: 'daemon.exited-uncleanly' });
|
|
278
|
+
log.log('warn', 'the last daemon exited uncleanly', data);
|
|
279
|
+
}
|
|
115
280
|
// Only the daemon that wrote the port file removes it, so a stopping daemon never deletes the file
|
|
116
281
|
// of one that replaced it.
|
|
117
282
|
function removePortFile(paths, port) {
|
package/build/daemon.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
package/build/events.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { NotRunningError, readEndpoint } from "./client.js";
|
|
2
|
+
import { ConductorError } from "./errors.js";
|
|
3
|
+
// The daemon writes this comment once a stream's replay is out and live events follow.
|
|
4
|
+
const CONNECTED = ': connected';
|
|
5
|
+
const RETRY_MS = [1000, 2000, 5000];
|
|
6
|
+
// `conduct events [--since <id>] [--follow]`: the event log, one JSON event per line. Without
|
|
7
|
+
// --follow it prints the events after --since (all of them without it) and exits. With --follow it
|
|
8
|
+
// starts after --since (live without it), keeps the stream open, and reconnects with Last-Event-ID
|
|
9
|
+
// when the daemon goes away, so a restart loses nothing. --follow also prints the stream's state
|
|
10
|
+
// as lines without an id, `{"kind":"conduct.connected"}` and `{"kind":"conduct.disconnected",
|
|
11
|
+
// "error":…}`, which the conductor mod reads to refetch or clear what it shows.
|
|
12
|
+
export async function eventsCommand(paths, options, io) {
|
|
13
|
+
// Ends early on the signal, so Ctrl+C doesn't wait out a retry while the daemon is down.
|
|
14
|
+
const sleep = io.sleep ??
|
|
15
|
+
((ms) => new Promise((done) => {
|
|
16
|
+
const timer = setTimeout(done, ms);
|
|
17
|
+
io.signal?.addEventListener('abort', () => (clearTimeout(timer), done()), { once: true });
|
|
18
|
+
}));
|
|
19
|
+
let last = options.since ?? null;
|
|
20
|
+
if (!options.follow) {
|
|
21
|
+
await readStream(paths, last ?? 0, { onEvent: (line) => io.out(line), signal: io.signal, untilConnected: true });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
let failures = 0;
|
|
25
|
+
let connected = false;
|
|
26
|
+
while (!io.signal?.aborted) {
|
|
27
|
+
let error;
|
|
28
|
+
try {
|
|
29
|
+
await readStream(paths, last, {
|
|
30
|
+
console: options.console,
|
|
31
|
+
onConnected: () => {
|
|
32
|
+
connected = true;
|
|
33
|
+
failures = 0;
|
|
34
|
+
io.out(JSON.stringify({ kind: 'conduct.connected' }));
|
|
35
|
+
},
|
|
36
|
+
onEvent: (line, id) => {
|
|
37
|
+
last = id;
|
|
38
|
+
io.out(line);
|
|
39
|
+
},
|
|
40
|
+
signal: io.signal,
|
|
41
|
+
untilConnected: false,
|
|
42
|
+
});
|
|
43
|
+
error = 'the conductor closed the stream';
|
|
44
|
+
}
|
|
45
|
+
catch (caught) {
|
|
46
|
+
if (io.signal?.aborted)
|
|
47
|
+
break;
|
|
48
|
+
error = caught instanceof Error ? caught.message : String(caught);
|
|
49
|
+
}
|
|
50
|
+
if (io.signal?.aborted)
|
|
51
|
+
break;
|
|
52
|
+
// Said once per outage, not on every retry while the daemon stays down.
|
|
53
|
+
if (connected || failures === 0)
|
|
54
|
+
io.out(JSON.stringify({ error, kind: 'conduct.disconnected' }));
|
|
55
|
+
connected = false;
|
|
56
|
+
await sleep(RETRY_MS[Math.min(failures, RETRY_MS.length - 1)] ?? 5000);
|
|
57
|
+
failures++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Reads one connection of GET /events: from `since` (Last-Event-ID), or live when it is null.
|
|
61
|
+
async function readStream(paths, since, handlers) {
|
|
62
|
+
const endpoint = readEndpoint(paths);
|
|
63
|
+
if (!endpoint)
|
|
64
|
+
throw new NotRunningError();
|
|
65
|
+
let response;
|
|
66
|
+
try {
|
|
67
|
+
response = await fetch(`http://127.0.0.1:${endpoint.port}/events${handlers.console === true ? '?console=1' : ''}`, {
|
|
68
|
+
headers: { authorization: `Bearer ${endpoint.token}`, ...(since === null ? {} : { 'last-event-id': String(since) }) },
|
|
69
|
+
signal: handlers.signal,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new ConductorError(`the conductor on port ${endpoint.port} did not answer GET /events`, [String(error)]);
|
|
74
|
+
}
|
|
75
|
+
if (response.status === 404)
|
|
76
|
+
throw new ConductorError('the running conductor has no event stream: it is older than this conduct');
|
|
77
|
+
if (response.status !== 200 || !response.body)
|
|
78
|
+
throw new ConductorError(`GET /events answered ${response.status}`);
|
|
79
|
+
const reader = response.body.getReader();
|
|
80
|
+
const decoder = new TextDecoder();
|
|
81
|
+
let buffer = '';
|
|
82
|
+
try {
|
|
83
|
+
for (;;) {
|
|
84
|
+
const { done, value } = await reader.read();
|
|
85
|
+
if (done)
|
|
86
|
+
return;
|
|
87
|
+
buffer += decoder.decode(value, { stream: true }).replaceAll('\r\n', '\n');
|
|
88
|
+
for (let end = buffer.indexOf('\n\n'); end >= 0; end = buffer.indexOf('\n\n')) {
|
|
89
|
+
const frame = buffer.slice(0, end).split('\n');
|
|
90
|
+
buffer = buffer.slice(end + 2);
|
|
91
|
+
if (frame.includes(CONNECTED)) {
|
|
92
|
+
handlers.onConnected?.();
|
|
93
|
+
if (handlers.untilConnected)
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const id = frame.find((l) => l.startsWith('id: '))?.slice('id: '.length);
|
|
97
|
+
const data = frame.find((l) => l.startsWith('data: '))?.slice('data: '.length);
|
|
98
|
+
if (id !== undefined && data !== undefined)
|
|
99
|
+
handlers.onEvent(data, Number(id));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
await reader.cancel().catch(() => { });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=events.js.map
|