@matthugh1/conductor-cli 0.2.2 → 0.2.4
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/dist/agent.js +166 -90
- package/dist/{chunk-7S5HKGS5.js → branch-overview-RRHX3XGY.js} +114 -9
- package/dist/{chunk-FAZ7FCZQ.js → chunk-N2KKNG4C.js} +15 -3
- package/dist/{chunk-B2WDTKD7.js → cli-config-LEERSU5N.js} +6 -7
- package/dist/{daemon-GGOJDZDB.js → daemon-ZJDZIP3R.js} +24 -15
- package/dist/{daemon-client-BE64H437.js → daemon-client-CTYOJMJP.js} +124 -1
- package/dist/{git-hooks-UZJ6AER4.js → git-hooks-RQ6WJQS4.js} +1 -2
- package/dist/{git-wrapper-DVJ46TMA.js → git-wrapper-QRZYTYCZ.js} +1 -2
- package/package.json +2 -2
- package/dist/branch-overview-DSSCUE5F.js +0 -18
- package/dist/chunk-3MJBQK2F.js +0 -75
- package/dist/chunk-4YEHSYVN.js +0 -17
- package/dist/chunk-6VMREHG4.js +0 -22
- package/dist/chunk-KB2DTST2.js +0 -482
- package/dist/chunk-PANC6BTV.js +0 -151
- package/dist/cli-config-2ZDXUUQN.js +0 -21
- package/dist/cli-tasks-NM5D5PIZ.js +0 -180
- package/dist/db-U6Y3QJDD.js +0 -16
- package/dist/git-snapshots-N3FBS7T3.js +0 -90
- package/dist/health-UFK7YCKQ.js +0 -147
- package/dist/health-snapshots-6MUVHE3G.js +0 -39
- package/dist/work-queue-U3JYHLX2.js +0 -758
- package/dist/worktree-manager-2ZUJEL3L.js +0 -31
package/dist/agent.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
ensureDatabaseUrl,
|
|
4
|
-
postToServer
|
|
5
|
-
} from "./chunk-B2WDTKD7.js";
|
|
6
2
|
|
|
7
3
|
// ../../src/cli/agent.ts
|
|
8
4
|
import { resolve } from "path";
|
|
@@ -124,12 +120,10 @@ function writeErr(text) {
|
|
|
124
120
|
process.stderr.write(text + "\n");
|
|
125
121
|
}
|
|
126
122
|
async function cmdCheck(projectRoot, jsonOutput) {
|
|
127
|
-
const {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
);
|
|
132
|
-
if (projects.length === 0) {
|
|
123
|
+
const { createDaemonClient } = await import("./daemon-client-CTYOJMJP.js");
|
|
124
|
+
const client = createDaemonClient();
|
|
125
|
+
const project = await client.resolveProjectByPath(projectRoot);
|
|
126
|
+
if (!project) {
|
|
133
127
|
if (jsonOutput) {
|
|
134
128
|
writeOut(
|
|
135
129
|
JSON.stringify({
|
|
@@ -144,8 +138,8 @@ async function cmdCheck(projectRoot, jsonOutput) {
|
|
|
144
138
|
}
|
|
145
139
|
return 1;
|
|
146
140
|
}
|
|
147
|
-
const
|
|
148
|
-
const report = await
|
|
141
|
+
const projectId = project.id;
|
|
142
|
+
const report = await runLocalHealthChecks(projectRoot);
|
|
149
143
|
if (jsonOutput) {
|
|
150
144
|
writeOut(JSON.stringify(report, null, 2));
|
|
151
145
|
} else {
|
|
@@ -157,18 +151,15 @@ async function cmdCheck(projectRoot, jsonOutput) {
|
|
|
157
151
|
}
|
|
158
152
|
}
|
|
159
153
|
try {
|
|
160
|
-
|
|
161
|
-
await saveHealthSnapshot(projects[0].id, report);
|
|
154
|
+
await client.saveHealthSnapshot(projectId, report);
|
|
162
155
|
} catch {
|
|
163
|
-
|
|
164
|
-
if (!posted && !jsonOutput) {
|
|
156
|
+
if (!jsonOutput) {
|
|
165
157
|
writeErr("\u26A0 Could not sync results to the dashboard.");
|
|
166
158
|
}
|
|
167
159
|
}
|
|
168
160
|
try {
|
|
169
|
-
const { getGitBranchInfo, formatGitBranchLine, getStatusSummary, getRecentCommits } = await import("./git-wrapper-
|
|
170
|
-
const { getHookStatus } = await import("./git-hooks-
|
|
171
|
-
const { saveGitStatusSnapshot } = await import("./git-snapshots-N3FBS7T3.js");
|
|
161
|
+
const { getGitBranchInfo, formatGitBranchLine, getStatusSummary, getRecentCommits, runGit } = await import("./git-wrapper-QRZYTYCZ.js");
|
|
162
|
+
const { getHookStatus } = await import("./git-hooks-RQ6WJQS4.js");
|
|
172
163
|
const branchInfo = await getGitBranchInfo(projectRoot);
|
|
173
164
|
const branchLine = formatGitBranchLine(branchInfo);
|
|
174
165
|
const status = await getStatusSummary(projectRoot);
|
|
@@ -182,21 +173,20 @@ async function cmdCheck(projectRoot, jsonOutput) {
|
|
|
182
173
|
hookStatus = await getHookStatus(projectRoot);
|
|
183
174
|
} catch {
|
|
184
175
|
}
|
|
185
|
-
const worktreeRows = await dbQuery(
|
|
186
|
-
"SELECT branch_name, worktree_path FROM worktrees WHERE project_id = $1 AND status = 'active'",
|
|
187
|
-
[projects[0].id]
|
|
188
|
-
);
|
|
189
176
|
const worktreeCommits = {};
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
177
|
+
try {
|
|
178
|
+
const worktrees = await client.listActiveWorktrees(projectId);
|
|
179
|
+
for (const wt of worktrees) {
|
|
180
|
+
try {
|
|
181
|
+
const out = await runGit(wt.worktreePath, ["log", "-1", "--format=%cI"]);
|
|
182
|
+
const date = out.trim();
|
|
183
|
+
if (date) worktreeCommits[wt.branchName] = date;
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
197
186
|
}
|
|
187
|
+
} catch {
|
|
198
188
|
}
|
|
199
|
-
await saveGitStatusSnapshot(
|
|
189
|
+
await client.saveGitStatusSnapshot(projectId, {
|
|
200
190
|
branch: branchInfo.branchName,
|
|
201
191
|
branchLine,
|
|
202
192
|
status,
|
|
@@ -218,17 +208,16 @@ async function cmdCheck(projectRoot, jsonOutput) {
|
|
|
218
208
|
}
|
|
219
209
|
}
|
|
220
210
|
try {
|
|
221
|
-
const { getGitBranchInfo } = await import("./git-wrapper-
|
|
222
|
-
const { getBranchOverview } = await import("./branch-overview-
|
|
223
|
-
const { saveBranchOverviewSnapshot } = await import("./git-snapshots-N3FBS7T3.js");
|
|
211
|
+
const { getGitBranchInfo } = await import("./git-wrapper-QRZYTYCZ.js");
|
|
212
|
+
const { getBranchOverview } = await import("./branch-overview-RRHX3XGY.js");
|
|
224
213
|
const branchInfo = await getGitBranchInfo(projectRoot);
|
|
225
214
|
let currentBranch = branchInfo.branchName;
|
|
226
215
|
if (currentBranch?.startsWith("detached (")) currentBranch = null;
|
|
227
216
|
const overview = await getBranchOverview(projectRoot, {
|
|
228
|
-
projectId:
|
|
217
|
+
projectId: null,
|
|
229
218
|
currentBranch
|
|
230
219
|
});
|
|
231
|
-
await saveBranchOverviewSnapshot(
|
|
220
|
+
await client.saveBranchOverviewSnapshot(projectId, overview);
|
|
232
221
|
if (!jsonOutput) {
|
|
233
222
|
writeOut(`Branches: ${overview.branches.length} found`);
|
|
234
223
|
}
|
|
@@ -244,14 +233,73 @@ async function cmdCheck(projectRoot, jsonOutput) {
|
|
|
244
233
|
if (hasWarn) return 2;
|
|
245
234
|
return 0;
|
|
246
235
|
}
|
|
236
|
+
async function runLocalHealthChecks(projectRoot) {
|
|
237
|
+
const { existsSync } = await import("fs");
|
|
238
|
+
const { readFile } = await import("fs/promises");
|
|
239
|
+
const { join } = await import("path");
|
|
240
|
+
const { runGit } = await import("./git-wrapper-QRZYTYCZ.js");
|
|
241
|
+
const start = performance.now();
|
|
242
|
+
const checks = [];
|
|
243
|
+
const gitStart = performance.now();
|
|
244
|
+
try {
|
|
245
|
+
const output = await runGit(projectRoot, ["--version"]);
|
|
246
|
+
const version = output.trim().replace("git version ", "");
|
|
247
|
+
checks.push({ name: "Git", status: "pass", message: `Git ${version} found`, durationMs: Math.round(performance.now() - gitStart) });
|
|
248
|
+
} catch {
|
|
249
|
+
checks.push({ name: "Git", status: "fail", message: "Git not found or not working", durationMs: Math.round(performance.now() - gitStart) });
|
|
250
|
+
}
|
|
251
|
+
const hookStart = performance.now();
|
|
252
|
+
const hookPath = join(projectRoot, ".git", "hooks", "post-commit");
|
|
253
|
+
const CONDUCTOR_HOOK_MARKER = "# Conductor narration hook";
|
|
254
|
+
if (!existsSync(hookPath)) {
|
|
255
|
+
checks.push({
|
|
256
|
+
name: "Hooks",
|
|
257
|
+
status: "warn",
|
|
258
|
+
message: "Hooks not installed",
|
|
259
|
+
fixAction: { label: "Install hooks", href: "/git" },
|
|
260
|
+
durationMs: Math.round(performance.now() - hookStart)
|
|
261
|
+
});
|
|
262
|
+
} else {
|
|
263
|
+
try {
|
|
264
|
+
const content = await readFile(hookPath, "utf-8");
|
|
265
|
+
if (content.includes(CONDUCTOR_HOOK_MARKER)) {
|
|
266
|
+
checks.push({ name: "Hooks", status: "pass", message: "Hooks installed", durationMs: Math.round(performance.now() - hookStart) });
|
|
267
|
+
} else {
|
|
268
|
+
checks.push({
|
|
269
|
+
name: "Hooks",
|
|
270
|
+
status: "warn",
|
|
271
|
+
message: "Hook file exists but is not a Conductor hook",
|
|
272
|
+
fixAction: { label: "Install hooks", href: "/git" },
|
|
273
|
+
durationMs: Math.round(performance.now() - hookStart)
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
} catch {
|
|
277
|
+
checks.push({ name: "Hooks", status: "warn", message: "Could not read hook file", durationMs: Math.round(performance.now() - hookStart) });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const apiStart = performance.now();
|
|
281
|
+
try {
|
|
282
|
+
const { createDaemonClient } = await import("./daemon-client-CTYOJMJP.js");
|
|
283
|
+
await createDaemonClient().resolveProjectByPath(projectRoot);
|
|
284
|
+
checks.push({ name: "API", status: "pass", message: "Conductor server reachable", durationMs: Math.round(performance.now() - apiStart) });
|
|
285
|
+
} catch {
|
|
286
|
+
checks.push({ name: "API", status: "warn", message: "Conductor server not reachable", durationMs: Math.round(performance.now() - apiStart) });
|
|
287
|
+
}
|
|
288
|
+
const hasFail = checks.some((c) => c.status === "fail");
|
|
289
|
+
const hasWarn = checks.some((c) => c.status === "warn");
|
|
290
|
+
const overall = hasFail ? "unhealthy" : hasWarn ? "degraded" : "healthy";
|
|
291
|
+
return {
|
|
292
|
+
checks,
|
|
293
|
+
overall,
|
|
294
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
295
|
+
totalDurationMs: Math.round(performance.now() - start)
|
|
296
|
+
};
|
|
297
|
+
}
|
|
247
298
|
async function cmdScan(projectRoot, jsonOutput) {
|
|
248
|
-
const {
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
[projectRoot]
|
|
253
|
-
);
|
|
254
|
-
if (projects.length === 0) {
|
|
299
|
+
const { createDaemonClient } = await import("./daemon-client-CTYOJMJP.js");
|
|
300
|
+
const client = createDaemonClient();
|
|
301
|
+
const project = await client.resolveProjectByPath(projectRoot);
|
|
302
|
+
if (!project) {
|
|
255
303
|
if (jsonOutput) {
|
|
256
304
|
writeOut(
|
|
257
305
|
JSON.stringify({
|
|
@@ -266,8 +314,7 @@ async function cmdScan(projectRoot, jsonOutput) {
|
|
|
266
314
|
}
|
|
267
315
|
return 1;
|
|
268
316
|
}
|
|
269
|
-
const
|
|
270
|
-
const result = await scanWorktrees(projectId, projectRoot);
|
|
317
|
+
const { scan: result } = await client.scanWorktreesViaApi(project.id);
|
|
271
318
|
if (jsonOutput) {
|
|
272
319
|
writeOut(JSON.stringify(result, null, 2));
|
|
273
320
|
} else {
|
|
@@ -293,7 +340,7 @@ async function cmdScan(projectRoot, jsonOutput) {
|
|
|
293
340
|
async function cmdInit(projectRoot, jsonOutput) {
|
|
294
341
|
const { resolve: resolvePath, basename, join } = await import("path");
|
|
295
342
|
const { realpath, mkdir, readFile, writeFile, access } = await import("fs/promises");
|
|
296
|
-
const { getServerBaseUrl } = await import("./cli-config-
|
|
343
|
+
const { getServerBaseUrl } = await import("./cli-config-LEERSU5N.js");
|
|
297
344
|
let root = resolvePath(projectRoot.trim());
|
|
298
345
|
try {
|
|
299
346
|
root = await realpath(root);
|
|
@@ -368,7 +415,7 @@ async function cmdInit(projectRoot, jsonOutput) {
|
|
|
368
415
|
return 0;
|
|
369
416
|
}
|
|
370
417
|
async function cmdHooks(projectRoot, subcommand, jsonOutput) {
|
|
371
|
-
const { getHookStatus, installHooks } = await import("./git-hooks-
|
|
418
|
+
const { getHookStatus, installHooks } = await import("./git-hooks-RQ6WJQS4.js");
|
|
372
419
|
if (subcommand === "install") {
|
|
373
420
|
const result = await installHooks(projectRoot);
|
|
374
421
|
if (jsonOutput) {
|
|
@@ -410,13 +457,11 @@ async function cmdHooks(projectRoot, subcommand, jsonOutput) {
|
|
|
410
457
|
return 2;
|
|
411
458
|
}
|
|
412
459
|
async function cmdWatch(projectRoot, once, jsonOutput) {
|
|
413
|
-
const {
|
|
414
|
-
const {
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
);
|
|
419
|
-
if (projects.length === 0) {
|
|
460
|
+
const { createDaemonClient } = await import("./daemon-client-CTYOJMJP.js");
|
|
461
|
+
const { runGit } = await import("./git-wrapper-QRZYTYCZ.js");
|
|
462
|
+
const client = createDaemonClient();
|
|
463
|
+
const project = await client.resolveProjectByPath(projectRoot);
|
|
464
|
+
if (!project) {
|
|
420
465
|
if (jsonOutput) {
|
|
421
466
|
writeOut(
|
|
422
467
|
JSON.stringify({
|
|
@@ -431,13 +476,56 @@ async function cmdWatch(projectRoot, once, jsonOutput) {
|
|
|
431
476
|
}
|
|
432
477
|
return 1;
|
|
433
478
|
}
|
|
434
|
-
const projectId =
|
|
435
|
-
|
|
479
|
+
const projectId = project.id;
|
|
480
|
+
async function executeTaskViaApi(task) {
|
|
481
|
+
try {
|
|
482
|
+
switch (task.taskType) {
|
|
483
|
+
case "remove_worktree": {
|
|
484
|
+
const worktreeId = task.payload.worktreeId;
|
|
485
|
+
if (!worktreeId) {
|
|
486
|
+
const msg = "Missing worktreeId in task payload.";
|
|
487
|
+
await client.failCliTask(task.id, msg);
|
|
488
|
+
return msg;
|
|
489
|
+
}
|
|
490
|
+
await client.removeWorktreeViaApi(projectId, worktreeId);
|
|
491
|
+
await client.completeCliTask(task.id, { worktreeId });
|
|
492
|
+
return `Removed worktree ${worktreeId}`;
|
|
493
|
+
}
|
|
494
|
+
case "delete_branch": {
|
|
495
|
+
const branchName = task.payload.branchName;
|
|
496
|
+
if (!branchName) {
|
|
497
|
+
const msg = "Missing branchName in task payload.";
|
|
498
|
+
await client.failCliTask(task.id, msg);
|
|
499
|
+
return msg;
|
|
500
|
+
}
|
|
501
|
+
await runGit(projectRoot, ["branch", "-D", branchName]);
|
|
502
|
+
await client.logGitActivityViaApi({
|
|
503
|
+
projectRoot,
|
|
504
|
+
eventType: "branch_delete",
|
|
505
|
+
branch: branchName,
|
|
506
|
+
summary: `Deleted branch "${branchName}"`
|
|
507
|
+
});
|
|
508
|
+
await client.completeCliTask(task.id, { branchName });
|
|
509
|
+
return `Deleted branch "${branchName}"`;
|
|
510
|
+
}
|
|
511
|
+
default: {
|
|
512
|
+
const msg = `Unknown task type: ${task.taskType}`;
|
|
513
|
+
await client.failCliTask(task.id, msg);
|
|
514
|
+
return msg;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
} catch (err) {
|
|
518
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
519
|
+
await client.failCliTask(task.id, msg);
|
|
520
|
+
return `Failed: ${msg}`;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
const staleCount = await client.failStaleCliTasks(projectId);
|
|
436
524
|
if (staleCount > 0 && !jsonOutput) {
|
|
437
525
|
writeOut(`Auto-failed ${staleCount} stale task(s).`);
|
|
438
526
|
}
|
|
439
527
|
if (once) {
|
|
440
|
-
const task = await
|
|
528
|
+
const task = await client.claimNextCliTask(projectId);
|
|
441
529
|
if (!task) {
|
|
442
530
|
if (jsonOutput) {
|
|
443
531
|
writeOut(JSON.stringify({ message: "No pending tasks." }));
|
|
@@ -446,7 +534,7 @@ async function cmdWatch(projectRoot, once, jsonOutput) {
|
|
|
446
534
|
}
|
|
447
535
|
return 0;
|
|
448
536
|
}
|
|
449
|
-
const summary = await
|
|
537
|
+
const summary = await executeTaskViaApi(task);
|
|
450
538
|
if (jsonOutput) {
|
|
451
539
|
writeOut(JSON.stringify({ taskId: task.id, taskType: task.taskType, summary }));
|
|
452
540
|
} else {
|
|
@@ -468,9 +556,9 @@ async function cmdWatch(projectRoot, once, jsonOutput) {
|
|
|
468
556
|
process.on("SIGTERM", shutdown);
|
|
469
557
|
while (running) {
|
|
470
558
|
try {
|
|
471
|
-
const task = await
|
|
559
|
+
const task = await client.claimNextCliTask(projectId);
|
|
472
560
|
if (task) {
|
|
473
|
-
const summary = await
|
|
561
|
+
const summary = await executeTaskViaApi(task);
|
|
474
562
|
if (jsonOutput) {
|
|
475
563
|
writeOut(
|
|
476
564
|
JSON.stringify({ taskId: task.id, taskType: task.taskType, summary })
|
|
@@ -479,7 +567,7 @@ async function cmdWatch(projectRoot, once, jsonOutput) {
|
|
|
479
567
|
writeOut(`[${task.taskType}] ${summary}`);
|
|
480
568
|
}
|
|
481
569
|
}
|
|
482
|
-
await
|
|
570
|
+
await client.failStaleCliTasks(projectId);
|
|
483
571
|
} catch (err) {
|
|
484
572
|
const msg = err instanceof Error ? err.message : String(err);
|
|
485
573
|
if (!jsonOutput) {
|
|
@@ -496,23 +584,21 @@ async function cmdRun(opts) {
|
|
|
496
584
|
const { spawn } = await import("child_process");
|
|
497
585
|
const { readFileSync, existsSync } = await import("fs");
|
|
498
586
|
const { assembleAutonomousPrompt } = await import("./runner-prompt-MOOPKA5P.js");
|
|
499
|
-
const {
|
|
500
|
-
const
|
|
501
|
-
let project
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
project = rows[0].path;
|
|
587
|
+
const { createDaemonClient } = await import("./daemon-client-CTYOJMJP.js");
|
|
588
|
+
const client = createDaemonClient();
|
|
589
|
+
let project;
|
|
590
|
+
let projectId;
|
|
591
|
+
try {
|
|
592
|
+
const resolved = await client.resolveProject({
|
|
593
|
+
name: opts.projectName,
|
|
594
|
+
path: opts.projectName ? void 0 : opts.projectRoot
|
|
595
|
+
});
|
|
596
|
+
project = resolved.path;
|
|
597
|
+
projectId = resolved.id;
|
|
598
|
+
} catch (err) {
|
|
599
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
600
|
+
writeErr(`Project not found: ${msg}`);
|
|
601
|
+
return 1;
|
|
516
602
|
}
|
|
517
603
|
function ts() {
|
|
518
604
|
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
@@ -556,7 +642,7 @@ async function cmdRun(opts) {
|
|
|
556
642
|
});
|
|
557
643
|
}
|
|
558
644
|
async function getNextItem(skipIds) {
|
|
559
|
-
const queue = await
|
|
645
|
+
const queue = await client.pollWorkQueue(projectId, 50, { autonomous: true });
|
|
560
646
|
for (const item of queue.queue) {
|
|
561
647
|
if (item.type === "deliverable" && (item.tier === "ready" || item.tier === "active") && !skipIds.has(item.entityId)) {
|
|
562
648
|
return item;
|
|
@@ -678,16 +764,6 @@ async function main() {
|
|
|
678
764
|
writeOut(HELP_TEXT);
|
|
679
765
|
process.exit(parsed.help ? 0 : 1);
|
|
680
766
|
}
|
|
681
|
-
const needsDb = parsed.command !== "daemon";
|
|
682
|
-
if (needsDb) {
|
|
683
|
-
const hasDb = await ensureDatabaseUrl();
|
|
684
|
-
if (!hasDb) {
|
|
685
|
-
writeErr(
|
|
686
|
-
"Could not find a database connection. Make sure the MCP server is running (npm run mcp) and try again."
|
|
687
|
-
);
|
|
688
|
-
process.exit(1);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
767
|
try {
|
|
692
768
|
let exitCode;
|
|
693
769
|
switch (parsed.command) {
|
|
@@ -727,7 +803,7 @@ async function main() {
|
|
|
727
803
|
});
|
|
728
804
|
break;
|
|
729
805
|
case "daemon": {
|
|
730
|
-
const { cmdDaemon, cmdDaemonCancel } = await import("./daemon-
|
|
806
|
+
const { cmdDaemon, cmdDaemonCancel } = await import("./daemon-ZJDZIP3R.js");
|
|
731
807
|
if (parsed.subcommand === "cancel") {
|
|
732
808
|
exitCode = await cmdDaemonCancel(
|
|
733
809
|
parsed.projectRoot,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
ConductorError,
|
|
3
4
|
runGit
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
query
|
|
7
|
-
} from "./chunk-PANC6BTV.js";
|
|
5
|
+
} from "./chunk-N2KKNG4C.js";
|
|
8
6
|
|
|
9
7
|
// ../../src/core/branch-overview-match.ts
|
|
10
8
|
function slugifyTitle(title) {
|
|
@@ -71,6 +69,114 @@ function resolveDeliverableLink(branchName, hints, sessionByBranch) {
|
|
|
71
69
|
return null;
|
|
72
70
|
}
|
|
73
71
|
|
|
72
|
+
// ../../src/core/db.ts
|
|
73
|
+
import { Pool } from "pg";
|
|
74
|
+
var pool = null;
|
|
75
|
+
function getDatabaseUrl() {
|
|
76
|
+
const url = process.env.DATABASE_URL;
|
|
77
|
+
if (!url) {
|
|
78
|
+
throw new ConductorError(
|
|
79
|
+
"DB_UNREACHABLE" /* DB_UNREACHABLE */,
|
|
80
|
+
"DATABASE_URL is not set. Set it to your Supabase (or other Postgres) connection string."
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return url;
|
|
84
|
+
}
|
|
85
|
+
function getPool() {
|
|
86
|
+
if (pool) {
|
|
87
|
+
return pool;
|
|
88
|
+
}
|
|
89
|
+
const connectionString = getDatabaseUrl();
|
|
90
|
+
const isRemote = connectionString.includes("supabase.co");
|
|
91
|
+
pool = new Pool({
|
|
92
|
+
connectionString,
|
|
93
|
+
max: 10,
|
|
94
|
+
connectionTimeoutMillis: 5e3,
|
|
95
|
+
idleTimeoutMillis: 3e4,
|
|
96
|
+
...isRemote ? { ssl: { rejectUnauthorized: false } } : {}
|
|
97
|
+
});
|
|
98
|
+
pool.on("error", () => {
|
|
99
|
+
console.error(
|
|
100
|
+
"Unexpected database error on an idle connection. Check that Postgres is running."
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
return pool;
|
|
104
|
+
}
|
|
105
|
+
var TRANSIENT_CODES = /* @__PURE__ */ new Set(["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT"]);
|
|
106
|
+
function isTransientConnectionError(err) {
|
|
107
|
+
const code = getErrorCode(err);
|
|
108
|
+
if (code && TRANSIENT_CODES.has(code)) return true;
|
|
109
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
110
|
+
return msg.includes("ECONNRESET") || msg.includes("ECONNREFUSED") || msg.includes("ETIMEDOUT") || msg.includes("connection terminated unexpectedly");
|
|
111
|
+
}
|
|
112
|
+
async function query(sql, params) {
|
|
113
|
+
const p = getPool();
|
|
114
|
+
try {
|
|
115
|
+
const result = await p.query(sql, params);
|
|
116
|
+
return result.rows;
|
|
117
|
+
} catch (err) {
|
|
118
|
+
if (isTransientConnectionError(err)) {
|
|
119
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
120
|
+
try {
|
|
121
|
+
const result = await p.query(sql, params);
|
|
122
|
+
return result.rows;
|
|
123
|
+
} catch (retryErr) {
|
|
124
|
+
throw mapDbError(retryErr);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
throw mapDbError(err);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function getErrorCode(err) {
|
|
131
|
+
if (err !== null && typeof err === "object" && "code" in err) {
|
|
132
|
+
const code = err.code;
|
|
133
|
+
return typeof code === "string" ? code : void 0;
|
|
134
|
+
}
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
function getBestErrorMessage(err) {
|
|
138
|
+
if (err instanceof AggregateError && err.errors.length > 0) {
|
|
139
|
+
const first = err.errors[0];
|
|
140
|
+
if (first instanceof Error && first.message.trim().length > 0) {
|
|
141
|
+
return first.message;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (err instanceof Error && err.message.trim().length > 0) {
|
|
145
|
+
return err.message;
|
|
146
|
+
}
|
|
147
|
+
return String(err);
|
|
148
|
+
}
|
|
149
|
+
function mapDbError(err) {
|
|
150
|
+
const code = getErrorCode(err);
|
|
151
|
+
const message = getBestErrorMessage(err);
|
|
152
|
+
if (code === "ECONNREFUSED" || code === "ECONNRESET" || code === "ETIMEDOUT" || message.includes("ECONNREFUSED") || message.includes("ECONNRESET") || message.includes("ETIMEDOUT") || message.includes("connect ECONNREFUSED") || message.includes("connection terminated unexpectedly")) {
|
|
153
|
+
return new ConductorError(
|
|
154
|
+
"DB_UNREACHABLE" /* DB_UNREACHABLE */,
|
|
155
|
+
"Could not reach the database. Postgres may not be running on this machine.",
|
|
156
|
+
true
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (message.includes("password") || message.includes("authentication") || message.includes("28P01")) {
|
|
160
|
+
return new ConductorError(
|
|
161
|
+
"DB_QUERY_FAILED" /* DB_QUERY_FAILED */,
|
|
162
|
+
"Could not sign in to the database. Check your username and password."
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (message.includes("does not exist") && message.includes("database")) {
|
|
166
|
+
return new ConductorError(
|
|
167
|
+
"DB_QUERY_FAILED" /* DB_QUERY_FAILED */,
|
|
168
|
+
"That database does not exist yet. Create it first, then try again."
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
if (message.trim().length > 0) {
|
|
172
|
+
return new ConductorError("DB_QUERY_FAILED" /* DB_QUERY_FAILED */, message);
|
|
173
|
+
}
|
|
174
|
+
return new ConductorError(
|
|
175
|
+
"DB_QUERY_FAILED" /* DB_QUERY_FAILED */,
|
|
176
|
+
"Something went wrong while talking to the database."
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
74
180
|
// ../../src/core/branch-overview.ts
|
|
75
181
|
var STALE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
76
182
|
var MAX_COMMITS = 20;
|
|
@@ -596,11 +702,10 @@ async function getMergeStats(projectRoot, defaultBranch, featureBranch) {
|
|
|
596
702
|
const files = await loadChangedFiles(projectRoot, defaultBranch, featureBranch);
|
|
597
703
|
return { commitCount, fileCount: files.length };
|
|
598
704
|
}
|
|
599
|
-
|
|
600
705
|
export {
|
|
601
|
-
isBranchFullyOnMain,
|
|
602
|
-
getConflictBranchesForWorkQueue,
|
|
603
|
-
getBranchOverview,
|
|
604
706
|
assertCanQueueMerge,
|
|
605
|
-
|
|
707
|
+
getBranchOverview,
|
|
708
|
+
getConflictBranchesForWorkQueue,
|
|
709
|
+
getMergeStats,
|
|
710
|
+
isBranchFullyOnMain
|
|
606
711
|
};
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
ConductorError
|
|
4
|
-
} from "./chunk-4YEHSYVN.js";
|
|
5
2
|
|
|
6
3
|
// ../../src/core/git-wrapper.ts
|
|
7
4
|
import { execFile } from "child_process";
|
|
8
5
|
import { randomBytes } from "crypto";
|
|
9
6
|
import { promisify } from "util";
|
|
7
|
+
|
|
8
|
+
// ../../src/core/errors.ts
|
|
9
|
+
var ConductorError = class extends Error {
|
|
10
|
+
code;
|
|
11
|
+
retryable;
|
|
12
|
+
constructor(code, message, retryable = false) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "ConductorError";
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.retryable = retryable;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// ../../src/core/git-wrapper.ts
|
|
10
21
|
var execFileAsync = promisify(execFile);
|
|
11
22
|
var MAX_BUFFER = 10 * 1024 * 1024;
|
|
12
23
|
var READ_TIMEOUT_MS = 3e4;
|
|
@@ -520,6 +531,7 @@ async function executeNarrated(projectRoot, gitArgs) {
|
|
|
520
531
|
}
|
|
521
532
|
|
|
522
533
|
export {
|
|
534
|
+
ConductorError,
|
|
523
535
|
runGit,
|
|
524
536
|
getGitBranchInfo,
|
|
525
537
|
formatGitBranchLine,
|
|
@@ -123,14 +123,13 @@ async function ensureDatabaseUrl() {
|
|
|
123
123
|
}
|
|
124
124
|
return false;
|
|
125
125
|
}
|
|
126
|
-
|
|
127
126
|
export {
|
|
128
|
-
|
|
129
|
-
writeConfig,
|
|
130
|
-
readApiKey,
|
|
131
|
-
readProjectId,
|
|
132
|
-
getServerBaseUrl,
|
|
127
|
+
ensureDatabaseUrl,
|
|
133
128
|
fetchConfigFromServer,
|
|
129
|
+
getServerBaseUrl,
|
|
134
130
|
postToServer,
|
|
135
|
-
|
|
131
|
+
readApiKey,
|
|
132
|
+
readConfig,
|
|
133
|
+
readProjectId,
|
|
134
|
+
writeConfig
|
|
136
135
|
};
|
|
@@ -16,7 +16,7 @@ function log(msg) {
|
|
|
16
16
|
async function cmdDaemonCancel(projectRoot, projectName, jsonOutput, apiUrl, apiKey) {
|
|
17
17
|
const { existsSync, readFileSync, unlinkSync } = await import("fs");
|
|
18
18
|
const { join } = await import("path");
|
|
19
|
-
const { createDaemonClient } = await import("./daemon-client-
|
|
19
|
+
const { createDaemonClient } = await import("./daemon-client-CTYOJMJP.js");
|
|
20
20
|
const resolvedUrl = apiUrl ?? process.env.CONDUCTOR_API_URL;
|
|
21
21
|
const client = createDaemonClient(resolvedUrl, apiKey);
|
|
22
22
|
let projectId;
|
|
@@ -66,13 +66,13 @@ async function cmdDaemonCancel(projectRoot, projectName, jsonOutput, apiUrl, api
|
|
|
66
66
|
async function cmdDaemon(opts) {
|
|
67
67
|
const { mkdirSync, writeFileSync, unlinkSync, existsSync, readFileSync } = await import("fs");
|
|
68
68
|
const { join } = await import("path");
|
|
69
|
-
const { createDaemonClient } = await import("./daemon-client-
|
|
69
|
+
const { createDaemonClient } = await import("./daemon-client-CTYOJMJP.js");
|
|
70
70
|
const resolvedUrl = opts.apiUrl ?? process.env.CONDUCTOR_API_URL;
|
|
71
71
|
const client = createDaemonClient(resolvedUrl, opts.apiKey);
|
|
72
72
|
let projectPath;
|
|
73
73
|
let projectId;
|
|
74
74
|
try {
|
|
75
|
-
const { readProjectId } = await import("./cli-config-
|
|
75
|
+
const { readProjectId } = await import("./cli-config-LEERSU5N.js");
|
|
76
76
|
const configProjectId = readProjectId();
|
|
77
77
|
if (configProjectId) {
|
|
78
78
|
projectId = configProjectId;
|
|
@@ -137,7 +137,7 @@ async function cmdDaemon(opts) {
|
|
|
137
137
|
process.on("SIGTERM", shutdown);
|
|
138
138
|
let fileConfig = {};
|
|
139
139
|
try {
|
|
140
|
-
const { readConfig } = await import("./cli-config-
|
|
140
|
+
const { readConfig } = await import("./cli-config-LEERSU5N.js");
|
|
141
141
|
const config = readConfig(projectPath);
|
|
142
142
|
fileConfig = config.daemon ?? {};
|
|
143
143
|
} catch {
|
|
@@ -393,22 +393,31 @@ async function cmdDaemon(opts) {
|
|
|
393
393
|
}
|
|
394
394
|
if (daemonConfig.useWorktree && task.initiativeId) {
|
|
395
395
|
try {
|
|
396
|
-
const
|
|
397
|
-
createWorktree,
|
|
398
|
-
getWorktreeForInitiative
|
|
399
|
-
} = await import("./worktree-manager-2ZUJEL3L.js");
|
|
400
|
-
const existing = await getWorktreeForInitiative(projectId, task.initiativeId);
|
|
396
|
+
const existing = await client.getWorktreeForInitiative(projectId, task.initiativeId);
|
|
401
397
|
if (existing) {
|
|
402
398
|
worktreePath = existing.worktreePath;
|
|
403
399
|
log(`Reusing existing worktree at ${worktreePath}`);
|
|
404
400
|
} else {
|
|
405
|
-
const
|
|
401
|
+
const { runGit } = await import("./git-wrapper-QRZYTYCZ.js");
|
|
402
|
+
const path = await import("path");
|
|
403
|
+
const slug = (initiativeTitle ?? "unknown").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
|
|
404
|
+
const branchName = slug;
|
|
405
|
+
const parentDir = path.dirname(projectPath);
|
|
406
|
+
const projectDir = path.basename(projectPath);
|
|
407
|
+
const worktreeDir = path.join(parentDir, `${projectDir}--${slug}`);
|
|
408
|
+
try {
|
|
409
|
+
await runGit(projectPath, ["rev-parse", "--verify", branchName]);
|
|
410
|
+
} catch {
|
|
411
|
+
await runGit(projectPath, ["branch", branchName]);
|
|
412
|
+
}
|
|
413
|
+
await runGit(projectPath, ["worktree", "add", worktreeDir, branchName]);
|
|
414
|
+
await client.registerWorktree({
|
|
406
415
|
projectId,
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
);
|
|
411
|
-
worktreePath =
|
|
416
|
+
initiativeId: task.initiativeId,
|
|
417
|
+
branchName,
|
|
418
|
+
worktreePath: worktreeDir
|
|
419
|
+
});
|
|
420
|
+
worktreePath = worktreeDir;
|
|
412
421
|
log(`Created worktree for "${initiativeTitle}" at ${worktreePath}`);
|
|
413
422
|
}
|
|
414
423
|
} catch (err) {
|