@detergent-software/conductor 0.0.0-stage → 1.0.0-dev.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +131 -2
  3. package/build/claude.js +102 -0
  4. package/build/claude.js.map +1 -0
  5. package/build/cli.js +450 -0
  6. package/build/cli.js.map +1 -0
  7. package/build/client.js +64 -0
  8. package/build/client.js.map +1 -0
  9. package/build/conditions.js +198 -0
  10. package/build/conditions.js.map +1 -0
  11. package/build/config.js +74 -0
  12. package/build/config.js.map +1 -0
  13. package/build/daemon.js +127 -0
  14. package/build/daemon.js.map +1 -0
  15. package/build/errors.js +17 -0
  16. package/build/errors.js.map +1 -0
  17. package/build/jsonc.js +24 -0
  18. package/build/jsonc.js.map +1 -0
  19. package/build/lock.js +64 -0
  20. package/build/lock.js.map +1 -0
  21. package/build/log.js +43 -0
  22. package/build/log.js.map +1 -0
  23. package/build/manager.js +614 -0
  24. package/build/manager.js.map +1 -0
  25. package/build/paths.js +16 -0
  26. package/build/paths.js.map +1 -0
  27. package/build/profile.js +74 -0
  28. package/build/profile.js.map +1 -0
  29. package/build/registry.js +33 -0
  30. package/build/registry.js.map +1 -0
  31. package/build/resolve.js +28 -0
  32. package/build/resolve.js.map +1 -0
  33. package/build/run.js +52 -0
  34. package/build/run.js.map +1 -0
  35. package/build/server.js +227 -0
  36. package/build/server.js.map +1 -0
  37. package/build/spend.js +134 -0
  38. package/build/spend.js.map +1 -0
  39. package/build/startup.js +78 -0
  40. package/build/startup.js.map +1 -0
  41. package/build/store.js +528 -0
  42. package/build/store.js.map +1 -0
  43. package/build/tab.js +26 -0
  44. package/build/tab.js.map +1 -0
  45. package/build/token.js +58 -0
  46. package/build/token.js.map +1 -0
  47. package/build/upgrade.js +93 -0
  48. package/build/upgrade.js.map +1 -0
  49. package/build/wait.js +149 -0
  50. package/build/wait.js.map +1 -0
  51. package/build/watchers/ado.js +167 -0
  52. package/build/watchers/ado.js.map +1 -0
  53. package/build/watchers/github.js +82 -0
  54. package/build/watchers/github.js.map +1 -0
  55. package/build/watchers/health.js +42 -0
  56. package/build/watchers/health.js.map +1 -0
  57. package/build/watchers/index.js +226 -0
  58. package/build/watchers/index.js.map +1 -0
  59. package/build/watchers/linear-key.js +56 -0
  60. package/build/watchers/linear-key.js.map +1 -0
  61. package/build/watchers/linear.js +218 -0
  62. package/build/watchers/linear.js.map +1 -0
  63. package/build/watchers/npm.js +35 -0
  64. package/build/watchers/npm.js.map +1 -0
  65. package/build/watchers/source.js +94 -0
  66. package/build/watchers/source.js.map +1 -0
  67. package/build/worktrees.js +143 -0
  68. package/build/worktrees.js.map +1 -0
  69. package/package.json +56 -5
package/build/store.js ADDED
@@ -0,0 +1,528 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { DatabaseSync } from 'node:sqlite';
3
+ import { z } from 'zod';
4
+ import { ConductorError } from "./errors.js";
5
+ import { issueLines } from "./jsonc.js";
6
+ import { profileSchema } from "./profile.js";
7
+ import { totalTokens } from "./spend.js";
8
+ // Migrations by schema version (PRAGMA user_version). Append only: a shipped migration never
9
+ // changes. Version 1 is the design note's section 15; the tables later build steps fill start empty.
10
+ // Exported so tests can build a database at an older version.
11
+ export const MIGRATIONS = [
12
+ `
13
+ CREATE TABLE projects (
14
+ name TEXT PRIMARY KEY,
15
+ root TEXT NOT NULL,
16
+ profile_path TEXT NOT NULL,
17
+ protocol INTEGER NOT NULL,
18
+ profile TEXT NOT NULL,
19
+ enabled INTEGER NOT NULL DEFAULT 1,
20
+ added_at TEXT NOT NULL,
21
+ updated_at TEXT NOT NULL
22
+ ) STRICT;
23
+
24
+ CREATE TABLE items (
25
+ project TEXT NOT NULL,
26
+ id TEXT NOT NULL,
27
+ title TEXT,
28
+ state TEXT,
29
+ labels TEXT NOT NULL DEFAULT '[]',
30
+ area TEXT,
31
+ blocked_by TEXT NOT NULL DEFAULT '[]',
32
+ priority INTEGER,
33
+ latest_note TEXT,
34
+ updated_at TEXT NOT NULL,
35
+ PRIMARY KEY (project, id)
36
+ ) STRICT;
37
+
38
+ CREATE TABLE workers (
39
+ id INTEGER PRIMARY KEY,
40
+ project TEXT NOT NULL,
41
+ item TEXT NOT NULL,
42
+ session_id TEXT,
43
+ worktree TEXT,
44
+ branch TEXT,
45
+ pr TEXT,
46
+ phase TEXT,
47
+ status TEXT NOT NULL CHECK (status IN ('running', 'parked', 'stopped-unexpectedly', 'exited')),
48
+ retries INTEGER NOT NULL DEFAULT 0,
49
+ created_at TEXT NOT NULL,
50
+ updated_at TEXT NOT NULL
51
+ ) STRICT;
52
+
53
+ CREATE TABLE waits (
54
+ id INTEGER PRIMARY KEY,
55
+ worker INTEGER NOT NULL,
56
+ condition TEXT NOT NULL,
57
+ created_at TEXT NOT NULL,
58
+ resolved_at TEXT,
59
+ resolution TEXT
60
+ ) STRICT;
61
+
62
+ CREATE TABLE inbox (
63
+ id INTEGER PRIMARY KEY,
64
+ project TEXT,
65
+ item TEXT,
66
+ kind TEXT NOT NULL,
67
+ text TEXT NOT NULL,
68
+ commands TEXT NOT NULL DEFAULT '[]',
69
+ links TEXT NOT NULL DEFAULT '[]',
70
+ raised_at TEXT NOT NULL,
71
+ resolved_at TEXT,
72
+ resolved_by TEXT
73
+ ) STRICT;
74
+
75
+ CREATE TABLE spend (
76
+ worker INTEGER NOT NULL,
77
+ project TEXT NOT NULL,
78
+ item TEXT NOT NULL,
79
+ day TEXT NOT NULL,
80
+ tokens INTEGER NOT NULL DEFAULT 0,
81
+ PRIMARY KEY (worker, day)
82
+ ) STRICT;
83
+
84
+ CREATE TABLE rules (
85
+ id INTEGER PRIMARY KEY,
86
+ source TEXT NOT NULL,
87
+ position INTEGER NOT NULL,
88
+ rule TEXT NOT NULL,
89
+ loaded_at TEXT NOT NULL
90
+ ) STRICT;
91
+
92
+ CREATE TABLE overrides (
93
+ id INTEGER PRIMARY KEY,
94
+ project TEXT,
95
+ kind TEXT NOT NULL,
96
+ value TEXT NOT NULL,
97
+ until TEXT NOT NULL,
98
+ created_at TEXT NOT NULL
99
+ ) STRICT;
100
+
101
+ CREATE TABLE todos (
102
+ id INTEGER PRIMARY KEY,
103
+ project TEXT,
104
+ item TEXT,
105
+ action TEXT NOT NULL,
106
+ after_ticks INTEGER,
107
+ after TEXT,
108
+ created_at TEXT NOT NULL,
109
+ done_at TEXT
110
+ ) STRICT;
111
+
112
+ CREATE TABLE locks (
113
+ name TEXT PRIMARY KEY,
114
+ holder TEXT NOT NULL,
115
+ since TEXT NOT NULL
116
+ ) STRICT;
117
+
118
+ CREATE TABLE events (
119
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
120
+ at TEXT NOT NULL,
121
+ project TEXT,
122
+ kind TEXT NOT NULL,
123
+ subject TEXT,
124
+ data TEXT NOT NULL,
125
+ rule TEXT
126
+ ) STRICT;
127
+ CREATE INDEX events_by_project ON events (project, id);
128
+ CREATE TRIGGER events_no_update BEFORE UPDATE ON events BEGIN SELECT RAISE(ABORT, 'events is append-only'); END;
129
+ CREATE TRIGGER events_no_delete BEFORE DELETE ON events BEGIN SELECT RAISE(ABORT, 'events is append-only'); END;
130
+ `,
131
+ // 2: the session manager (ATK-309). A worker is `launching` while its worktree is made, and
132
+ // `failed` when that or the session start fails. `name` is its session's and worktree's name
133
+ // (probe-<item> for a probe); `handle` is the short id `claude stop`/`rm`/`attach` take. A
134
+ // resolved wait is delivered when a resume carries it. Spend keeps the four kinds of token, and
135
+ // `tokens` is their sum. `transcripts` holds where reading each transcript stopped.
136
+ `
137
+ CREATE TABLE workers_v2 (
138
+ id INTEGER PRIMARY KEY,
139
+ project TEXT NOT NULL,
140
+ item TEXT NOT NULL,
141
+ name TEXT NOT NULL,
142
+ probe INTEGER NOT NULL DEFAULT 0,
143
+ session_id TEXT,
144
+ handle TEXT,
145
+ worktree TEXT,
146
+ branch TEXT,
147
+ pr TEXT,
148
+ phase TEXT,
149
+ status TEXT NOT NULL CHECK (status IN ('launching', 'running', 'parked', 'stopped-unexpectedly', 'failed', 'exited')),
150
+ retries INTEGER NOT NULL DEFAULT 0,
151
+ attached_at TEXT,
152
+ stopped_at TEXT,
153
+ created_at TEXT NOT NULL,
154
+ updated_at TEXT NOT NULL
155
+ ) STRICT;
156
+ INSERT INTO workers_v2 (id, project, item, name, session_id, worktree, branch, pr, phase, status, retries, created_at, updated_at)
157
+ SELECT id, project, item, item, session_id, worktree, branch, pr, phase, status, retries, created_at, updated_at FROM workers;
158
+ DROP TABLE workers;
159
+ ALTER TABLE workers_v2 RENAME TO workers;
160
+ CREATE UNIQUE INDEX workers_in_flight ON workers (project, name) WHERE status NOT IN ('failed', 'exited');
161
+
162
+ ALTER TABLE waits ADD COLUMN delivered_at TEXT;
163
+ CREATE INDEX waits_by_worker ON waits (worker, id);
164
+
165
+ ALTER TABLE spend ADD COLUMN input INTEGER NOT NULL DEFAULT 0;
166
+ ALTER TABLE spend ADD COLUMN output INTEGER NOT NULL DEFAULT 0;
167
+ ALTER TABLE spend ADD COLUMN cache_read INTEGER NOT NULL DEFAULT 0;
168
+ ALTER TABLE spend ADD COLUMN cache_write INTEGER NOT NULL DEFAULT 0;
169
+
170
+ CREATE TABLE transcripts (
171
+ file TEXT PRIMARY KEY,
172
+ worker INTEGER NOT NULL,
173
+ bytes INTEGER NOT NULL DEFAULT 0,
174
+ recent_ids TEXT NOT NULL DEFAULT '[]'
175
+ ) STRICT;
176
+ `,
177
+ ];
178
+ export const SCHEMA_VERSION = MIGRATIONS.length;
179
+ const eventRow = z.object({
180
+ at: z.string(),
181
+ data: z.string().transform((s) => z.record(z.string(), z.unknown()).parse(JSON.parse(s))),
182
+ id: z.number(),
183
+ kind: z.string(),
184
+ project: z.string().nullable(),
185
+ rule: z.string().nullable(),
186
+ subject: z.string().nullable(),
187
+ });
188
+ const projectRow = z.object({
189
+ added_at: z.string(),
190
+ enabled: z.number(),
191
+ name: z.string(),
192
+ profile: z.string(),
193
+ profile_path: z.string(),
194
+ protocol: z.number(),
195
+ root: z.string(),
196
+ updated_at: z.string(),
197
+ });
198
+ // A registered project as the API returns it. A stored profile that this build's schema no longer
199
+ // accepts comes back as `profile: null` with the reason in `problem`, so one stale row doesn't break
200
+ // every listing; `conduct project add` again refreshes it.
201
+ export const projectSchema = z.object({
202
+ addedAt: z.string(),
203
+ enabled: z.boolean(),
204
+ name: z.string(),
205
+ problem: z.string().nullable(),
206
+ profile: profileSchema.nullable(),
207
+ profilePath: z.string(),
208
+ protocol: z.number(),
209
+ root: z.string(),
210
+ updatedAt: z.string(),
211
+ });
212
+ export const WORKER_STATUSES = ['launching', 'running', 'parked', 'stopped-unexpectedly', 'failed', 'exited'];
213
+ // The statuses of a worker the daemon still looks after; `failed` and `exited` are history.
214
+ export const ACTIVE_STATUSES = ['launching', 'running', 'parked', 'stopped-unexpectedly'];
215
+ export const workerSchema = z.object({
216
+ attachedAt: z.string().nullable(),
217
+ branch: z.string().nullable(),
218
+ createdAt: z.string(),
219
+ handle: z.string().nullable(),
220
+ id: z.number(),
221
+ item: z.string(),
222
+ name: z.string(),
223
+ probe: z.boolean(),
224
+ project: z.string(),
225
+ sessionId: z.string().nullable(),
226
+ status: z.enum(WORKER_STATUSES),
227
+ stoppedAt: z.string().nullable(),
228
+ updatedAt: z.string(),
229
+ worktree: z.string().nullable(),
230
+ });
231
+ export const waitSchema = z.object({
232
+ condition: z.string(),
233
+ createdAt: z.string(),
234
+ deliveredAt: z.string().nullable(),
235
+ id: z.number(),
236
+ resolution: z.string().nullable(),
237
+ resolvedAt: z.string().nullable(),
238
+ worker: z.number(),
239
+ });
240
+ const workerRow = z.object({
241
+ attached_at: z.string().nullable(),
242
+ branch: z.string().nullable(),
243
+ created_at: z.string(),
244
+ handle: z.string().nullable(),
245
+ id: z.number(),
246
+ item: z.string(),
247
+ name: z.string(),
248
+ probe: z.number(),
249
+ project: z.string(),
250
+ session_id: z.string().nullable(),
251
+ status: z.enum(WORKER_STATUSES),
252
+ stopped_at: z.string().nullable(),
253
+ updated_at: z.string(),
254
+ worktree: z.string().nullable(),
255
+ });
256
+ const waitRow = z.object({
257
+ condition: z.string(),
258
+ created_at: z.string(),
259
+ delivered_at: z.string().nullable(),
260
+ id: z.number(),
261
+ resolution: z.string().nullable(),
262
+ resolved_at: z.string().nullable(),
263
+ worker: z.number(),
264
+ });
265
+ const PATCH_KEYS = [
266
+ 'attachedAt',
267
+ 'branch',
268
+ 'handle',
269
+ 'sessionId',
270
+ 'status',
271
+ 'stoppedAt',
272
+ 'worktree',
273
+ ];
274
+ const WORKER_COLUMNS = {
275
+ attachedAt: 'attached_at',
276
+ branch: 'branch',
277
+ handle: 'handle',
278
+ sessionId: 'session_id',
279
+ status: 'status',
280
+ stoppedAt: 'stopped_at',
281
+ worktree: 'worktree',
282
+ };
283
+ export function openStore(file, now = () => new Date()) {
284
+ const db = new DatabaseSync(file);
285
+ try {
286
+ db.exec('PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 5000; PRAGMA foreign_keys = ON;');
287
+ migrate(db, file);
288
+ }
289
+ catch (error) {
290
+ db.close();
291
+ throw error;
292
+ }
293
+ const emitter = new EventEmitter();
294
+ emitter.setMaxListeners(0);
295
+ const insertEvent = db.prepare('INSERT INTO events (at, project, kind, subject, data, rule) VALUES (?, ?, ?, ?, ?, ?) RETURNING *');
296
+ const selectAfter = db.prepare('SELECT * FROM events WHERE id > ? ORDER BY id LIMIT ?');
297
+ // The only writer to `events`. Everything the daemon observes or does goes through here.
298
+ const appendEvent = (event) => {
299
+ const row = eventRow.parse(insertEvent.get(now().toISOString(), event.project ?? null, event.kind, event.subject ?? null, JSON.stringify(event.data ?? {}), event.rule ?? null));
300
+ emitter.emit('event', row);
301
+ return row;
302
+ };
303
+ const toProject = (row) => {
304
+ const r = projectRow.parse(row);
305
+ const stored = parseStoredProfile(r.profile);
306
+ return {
307
+ addedAt: r.added_at,
308
+ enabled: r.enabled === 1,
309
+ name: r.name,
310
+ problem: stored.problem,
311
+ profile: stored.profile,
312
+ profilePath: r.profile_path,
313
+ protocol: r.protocol,
314
+ root: r.root,
315
+ updatedAt: r.updated_at,
316
+ };
317
+ };
318
+ const getWorker = (id) => {
319
+ const row = db.prepare('SELECT * FROM workers WHERE id = ?').get(id);
320
+ return row === undefined ? null : toWorker(row);
321
+ };
322
+ return {
323
+ addSpend: ({ day, item, project, usage, worker }) => {
324
+ db.prepare(`INSERT INTO spend (worker, project, item, day, tokens, input, output, cache_read, cache_write)
325
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
326
+ ON CONFLICT (worker, day) DO UPDATE SET
327
+ tokens = tokens + excluded.tokens, input = input + excluded.input, output = output + excluded.output,
328
+ cache_read = cache_read + excluded.cache_read, cache_write = cache_write + excluded.cache_write`).run(worker, project, item, day, totalTokens(usage), usage.input, usage.output, usage.cacheRead, usage.cacheWrite);
329
+ },
330
+ addWait: (worker, condition) => toWait(db
331
+ .prepare('INSERT INTO waits (worker, condition, created_at) VALUES (?, ?, ?) RETURNING *')
332
+ .get(worker, condition, now().toISOString())),
333
+ appendEvent,
334
+ close: () => db.close(),
335
+ closeWaits: (worker, resolution) => {
336
+ const at = now().toISOString();
337
+ db.prepare(`UPDATE waits SET resolved_at = coalesce(resolved_at, ?), resolution = coalesce(resolution, ?), delivered_at = ?
338
+ WHERE worker = ? AND delivered_at IS NULL`).run(at, resolution, at, worker);
339
+ },
340
+ deliverWaits: (ids) => {
341
+ const deliver = db.prepare('UPDATE waits SET delivered_at = ? WHERE id = ? AND resolved_at IS NOT NULL');
342
+ const at = now().toISOString();
343
+ for (const id of ids)
344
+ deliver.run(at, id);
345
+ },
346
+ eventsAfter: (id, limit = 500) => selectAfter.all(id, limit).map((row) => eventRow.parse(row)),
347
+ getWait: (id) => {
348
+ const row = db.prepare('SELECT * FROM waits WHERE id = ?').get(id);
349
+ return row === undefined ? null : toWait(row);
350
+ },
351
+ getWorker,
352
+ insertWorker: ({ item, name, probe, project }) => {
353
+ const at = now().toISOString();
354
+ try {
355
+ return toWorker(db
356
+ .prepare(`INSERT INTO workers (project, item, name, probe, status, created_at, updated_at)
357
+ VALUES (?, ?, ?, ?, 'launching', ?, ?) RETURNING *`)
358
+ .get(project, item, name, probe ? 1 : 0, at, at));
359
+ }
360
+ catch (error) {
361
+ if (error instanceof Error && /UNIQUE constraint failed/.test(error.message)) {
362
+ throw new ConductorError(`${name} is already in flight in ${project}`);
363
+ }
364
+ throw error;
365
+ }
366
+ },
367
+ latestEventId: () => z.object({ id: z.number() }).parse(db.prepare('SELECT coalesce(max(id), 0) AS id FROM events').get()).id,
368
+ listProjects: () => db.prepare('SELECT * FROM projects ORDER BY name').all().map(toProject),
369
+ listWorkers: (statuses = WORKER_STATUSES) => db
370
+ .prepare(`SELECT * FROM workers WHERE status IN (${statuses.map(() => '?').join(', ')}) ORDER BY id`)
371
+ .all(...statuses)
372
+ .map(toWorker),
373
+ onEvent: (listener) => {
374
+ emitter.on('event', listener);
375
+ return () => emitter.off('event', listener);
376
+ },
377
+ openWaits: () => db.prepare('SELECT * FROM waits WHERE resolved_at IS NULL ORDER BY id').all().map(toWait),
378
+ resolveWait: (id, resolution) => {
379
+ const row = db
380
+ .prepare('UPDATE waits SET resolved_at = ?, resolution = ? WHERE id = ? AND resolved_at IS NULL RETURNING *')
381
+ .get(now().toISOString(), resolution, id);
382
+ if (row === undefined)
383
+ throw new ConductorError(`wait ${id} is not open`);
384
+ return toWait(row);
385
+ },
386
+ saveTranscript: (file, worker, cursor) => {
387
+ db.prepare(`INSERT INTO transcripts (file, worker, bytes, recent_ids) VALUES (?, ?, ?, ?)
388
+ ON CONFLICT (file) DO UPDATE SET worker = excluded.worker, bytes = excluded.bytes, recent_ids = excluded.recent_ids`).run(file, worker, cursor.bytes, JSON.stringify(cursor.recentIds));
389
+ },
390
+ spendByWorker: (day) => {
391
+ const rows = db
392
+ .prepare(`SELECT worker, sum(tokens) AS total, sum(CASE WHEN day = ? THEN tokens ELSE 0 END) AS today
393
+ FROM spend GROUP BY worker`)
394
+ .all(day);
395
+ const totals = z.array(z.object({ today: z.number(), total: z.number(), worker: z.number() })).parse(rows);
396
+ return new Map(totals.map((t) => [t.worker, { today: t.today, total: t.total }]));
397
+ },
398
+ tables: () => db
399
+ .prepare("SELECT name FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name")
400
+ .all()
401
+ .map((row) => z.object({ name: z.string() }).parse(row).name),
402
+ transaction: (write) => {
403
+ db.exec('BEGIN IMMEDIATE');
404
+ try {
405
+ const result = write();
406
+ db.exec('COMMIT');
407
+ return result;
408
+ }
409
+ catch (error) {
410
+ db.exec('ROLLBACK');
411
+ throw error;
412
+ }
413
+ },
414
+ transcriptCursor: (file) => {
415
+ const row = db.prepare('SELECT bytes, recent_ids FROM transcripts WHERE file = ?').get(file);
416
+ if (row === undefined)
417
+ return null;
418
+ const r = z.object({ bytes: z.number(), recent_ids: z.string() }).parse(row);
419
+ return { bytes: r.bytes, recentIds: z.array(z.string()).parse(JSON.parse(r.recent_ids)) };
420
+ },
421
+ updateWorker: (id, patch) => {
422
+ const keys = PATCH_KEYS.filter((k) => patch[k] !== undefined);
423
+ const row = db
424
+ .prepare(`UPDATE workers SET ${[...keys.map((k) => `${WORKER_COLUMNS[k]} = ?`), 'updated_at = ?'].join(', ')}
425
+ WHERE id = ? RETURNING *`)
426
+ .get(...keys.map((k) => patch[k] ?? null), now().toISOString(), id);
427
+ if (row === undefined)
428
+ throw new ConductorError(`no worker ${id}`);
429
+ return toWorker(row);
430
+ },
431
+ // One row per project name. Adding a project again (or from another checkout path) updates it.
432
+ upsertProject: ({ profile, profilePath, root }) => {
433
+ const at = now().toISOString();
434
+ const existing = db.prepare('SELECT 1 FROM projects WHERE name = ?').get(profile.name) !== undefined;
435
+ const project = toProject(db
436
+ .prepare(`INSERT INTO projects (name, root, profile_path, protocol, profile, added_at, updated_at)
437
+ VALUES (?, ?, ?, ?, ?, ?, ?)
438
+ ON CONFLICT (name) DO UPDATE SET
439
+ root = excluded.root, profile_path = excluded.profile_path, protocol = excluded.protocol,
440
+ profile = excluded.profile, updated_at = excluded.updated_at
441
+ RETURNING *`)
442
+ .get(profile.name, root, profilePath, profile.protocol, JSON.stringify(profile), at, at));
443
+ appendEvent({
444
+ data: { profilePath, root },
445
+ kind: existing ? 'project.updated' : 'project.added',
446
+ project: project.name,
447
+ });
448
+ return { created: !existing, project };
449
+ },
450
+ waitsOf: (worker) => db.prepare('SELECT * FROM waits WHERE worker = ? AND delivered_at IS NULL ORDER BY id').all(worker).map(toWait),
451
+ workerEvents: (worker, limit = 20) => db
452
+ .prepare("SELECT * FROM events WHERE json_extract(data, '$.worker') = ? ORDER BY id DESC LIMIT ?")
453
+ .all(worker, limit)
454
+ .map((row) => eventRow.parse(row)),
455
+ };
456
+ }
457
+ // The daemon and a CLI running without it can open a new database at the same moment, so the
458
+ // version is read again under the write lock before anything is created.
459
+ function migrate(db, file) {
460
+ if (schemaVersion(db, file) === SCHEMA_VERSION)
461
+ return;
462
+ db.exec('BEGIN IMMEDIATE');
463
+ try {
464
+ for (let next = schemaVersion(db, file); next < SCHEMA_VERSION; next++) {
465
+ db.exec(MIGRATIONS[next] ?? '');
466
+ db.exec(`PRAGMA user_version = ${next + 1}`);
467
+ }
468
+ db.exec('COMMIT');
469
+ }
470
+ catch (error) {
471
+ db.exec('ROLLBACK');
472
+ throw error;
473
+ }
474
+ }
475
+ function parseStoredProfile(text) {
476
+ let value;
477
+ try {
478
+ value = JSON.parse(text);
479
+ }
480
+ catch {
481
+ return { problem: 'the stored profile is not JSON', profile: null };
482
+ }
483
+ const result = profileSchema.safeParse(value);
484
+ return result.success
485
+ ? { problem: null, profile: result.data }
486
+ : { problem: `the stored profile no longer validates: ${issueLines(result.error).join('; ')}`, profile: null };
487
+ }
488
+ function schemaVersion(db, file) {
489
+ const { user_version: version } = z.object({ user_version: z.number() }).parse(db.prepare('PRAGMA user_version').get());
490
+ if (version > SCHEMA_VERSION) {
491
+ throw new ConductorError(`${file} has schema version ${version}; this conductor knows up to ${SCHEMA_VERSION}`, [
492
+ 'upgrade the conductor (npm install --global @detergent-software/conductor@dev, or @latest; a linked checkout: pull and rebuild) before starting it',
493
+ ]);
494
+ }
495
+ return version;
496
+ }
497
+ function toWait(row) {
498
+ const r = waitRow.parse(row);
499
+ return {
500
+ condition: r.condition,
501
+ createdAt: r.created_at,
502
+ deliveredAt: r.delivered_at,
503
+ id: r.id,
504
+ resolution: r.resolution,
505
+ resolvedAt: r.resolved_at,
506
+ worker: r.worker,
507
+ };
508
+ }
509
+ function toWorker(row) {
510
+ const r = workerRow.parse(row);
511
+ return {
512
+ attachedAt: r.attached_at,
513
+ branch: r.branch,
514
+ createdAt: r.created_at,
515
+ handle: r.handle,
516
+ id: r.id,
517
+ item: r.item,
518
+ name: r.name,
519
+ probe: r.probe === 1,
520
+ project: r.project,
521
+ sessionId: r.session_id,
522
+ status: r.status,
523
+ stoppedAt: r.stopped_at,
524
+ updatedAt: r.updated_at,
525
+ worktree: r.worktree,
526
+ };
527
+ }
528
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
package/build/tab.js ADDED
@@ -0,0 +1,26 @@
1
+ import { outputOf, run } from "./run.js";
2
+ // A tab in the project's Windows Terminal window, attached to the session. wt.exe splits its own
3
+ // arguments on `;`, so titles lose it and the command chains with `||`. The tab closes when the
4
+ // session stops (a park), and stays open only to show a failed attach (ATK-260).
5
+ export function tabArgs(tab) {
6
+ const clean = (text) => text.replace(/;/g, ',').replace(/\s+/g, ' ').trim();
7
+ return [
8
+ '-w',
9
+ clean(tab.window),
10
+ 'new-tab',
11
+ '--title',
12
+ clean(tab.title).slice(0, 80),
13
+ 'pwsh',
14
+ '-Command',
15
+ `claude attach ${tab.handle} || Read-Host 'claude attach failed, press Enter to close this tab'`,
16
+ ];
17
+ }
18
+ export function windowsTerminalTab() {
19
+ return async (tab) => {
20
+ if (process.platform !== 'win32')
21
+ return 'tabs need Windows Terminal';
22
+ const result = await run('wt.exe', tabArgs(tab), { cwd: tab.cwd, timeoutMs: 60_000 });
23
+ return result.code === 0 ? null : outputOf(result, 500);
24
+ };
25
+ }
26
+ //# sourceMappingURL=tab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tab.js","sourceRoot":"","sources":["../src/tab.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAazC,iGAAiG;AACjG,gGAAgG;AAChG,iFAAiF;AACjF,MAAM,UAAU,OAAO,CAAC,GAAQ;IAC9B,MAAM,KAAK,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5F,OAAO;QACL,IAAI;QACJ,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;QACjB,SAAS;QACT,SAAS;QACT,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAC7B,MAAM;QACN,UAAU;QACV,iBAAiB,GAAG,CAAC,MAAM,qEAAqE;KACjG,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;YAAE,OAAO,4BAA4B,CAAC;QACtE,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACtF,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1D,CAAC,CAAC;AACJ,CAAC"}
package/build/token.js ADDED
@@ -0,0 +1,58 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { randomBytes, timingSafeEqual } from 'node:crypto';
3
+ import { chmodSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { userInfo } from 'node:os';
5
+ import { ConductorError, isMissing } from "./errors.js";
6
+ const TOKEN = /^[0-9a-f]{64}$/;
7
+ // Constant-time check of an `Authorization: Bearer <token>` header.
8
+ export function bearerMatches(token, header) {
9
+ const match = /^Bearer (\S+)$/.exec(header ?? '');
10
+ if (!match?.[1])
11
+ return false;
12
+ const given = Buffer.from(match[1]);
13
+ const expected = Buffer.from(token);
14
+ return given.length === expected.length && timingSafeEqual(given, expected);
15
+ }
16
+ // The API's shared secret: 32 random bytes in ~/.conductor/token, readable by this user only.
17
+ // Created on the first `serve` and kept, so clients and the conductor mod keep working across
18
+ // restarts. `restrict` limits a new token file to the current user.
19
+ export function ensureToken(file, restrict = restrictToUser) {
20
+ const existing = readToken(file);
21
+ if (existing)
22
+ return existing;
23
+ const token = randomBytes(32).toString('hex');
24
+ writeFileSync(file, `${token}\n`, { flag: 'wx', mode: 0o600 });
25
+ try {
26
+ restrict(file);
27
+ }
28
+ catch (error) {
29
+ // A token readable by others must not survive for the next start to reuse.
30
+ rmSync(file, { force: true });
31
+ throw new ConductorError(`could not restrict ${file} to the current user`, [String(error)]);
32
+ }
33
+ return token;
34
+ }
35
+ export function readToken(file) {
36
+ let text;
37
+ try {
38
+ text = readFileSync(file, 'utf8').trim();
39
+ }
40
+ catch (error) {
41
+ if (isMissing(error))
42
+ return null;
43
+ throw error;
44
+ }
45
+ if (!TOKEN.test(text)) {
46
+ throw new ConductorError(`${file} is not a conductor token`, ['delete it; the next `conduct serve` writes a new one']);
47
+ }
48
+ return text;
49
+ }
50
+ // Windows ignores the mode bits: drop the inherited ACL and grant only the current user.
51
+ function restrictToUser(file) {
52
+ if (process.platform !== 'win32') {
53
+ chmodSync(file, 0o600);
54
+ return;
55
+ }
56
+ execFileSync('icacls', [file, '/inheritance:r', '/grant:r', `${userInfo().username}:F`], { stdio: 'ignore' });
57
+ }
58
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../src/token.ts"],"names":[],"mappings":"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"}