@detergent-software/conductor 3.0.0-dev.2 → 3.0.0-dev.20
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 +161 -0
- package/README.md +154 -42
- package/build/brief-cli.js +1 -1
- package/build/brief-cli.js.map +1 -1
- package/build/broker-cli.js +24 -25
- package/build/broker-cli.js.map +1 -1
- package/build/broker.js +66 -92
- package/build/broker.js.map +1 -1
- package/build/claude.js +50 -7
- package/build/claude.js.map +1 -1
- package/build/cli.js +119 -54
- package/build/cli.js.map +1 -1
- package/build/clones.js +92 -0
- package/build/clones.js.map +1 -0
- package/build/code-host-cli.js +95 -22
- package/build/code-host-cli.js.map +1 -1
- package/build/code-host.js +13 -8
- package/build/code-host.js.map +1 -1
- package/build/config.js +31 -12
- package/build/config.js.map +1 -1
- package/build/console.js +40 -24
- package/build/console.js.map +1 -1
- package/build/credentials.js +26 -24
- package/build/credentials.js.map +1 -1
- package/build/daemon.js +35 -21
- package/build/daemon.js.map +1 -1
- package/build/help.js +138 -28
- package/build/help.js.map +1 -1
- package/build/host/credential-manager.js +109 -0
- package/build/host/credential-manager.js.map +1 -0
- package/build/host/keychain.js +44 -0
- package/build/host/keychain.js.map +1 -0
- package/build/host/libsecret.js +40 -0
- package/build/host/libsecret.js.map +1 -0
- package/build/host/macos-terminal.js +63 -0
- package/build/host/macos-terminal.js.map +1 -0
- package/build/host/platform.js +4 -0
- package/build/host/platform.js.map +1 -0
- package/build/host/secrets.js +46 -0
- package/build/host/secrets.js.map +1 -0
- package/build/host/shell.js +26 -0
- package/build/host/shell.js.map +1 -0
- package/build/host/terminal.js +46 -0
- package/build/host/terminal.js.map +1 -0
- package/build/host/tmux.js +39 -0
- package/build/host/tmux.js.map +1 -0
- package/build/host/windows-terminal.js +46 -0
- package/build/host/windows-terminal.js.map +1 -0
- package/build/in-process.js +6 -5
- package/build/in-process.js.map +1 -1
- package/build/inbox.js +50 -5
- package/build/inbox.js.map +1 -1
- package/build/init.js +508 -0
- package/build/init.js.map +1 -0
- package/build/lib/providers/ado/pipelines.js +79 -13
- package/build/lib/providers/ado/pipelines.js.map +1 -1
- package/build/lib/providers/github/actions.js +85 -2
- package/build/lib/providers/github/actions.js.map +1 -1
- package/build/lib/providers/index.js +2 -1
- package/build/lib/providers/index.js.map +1 -1
- package/build/lib/providers/npm/stage.js +14 -4
- package/build/lib/providers/npm/stage.js.map +1 -1
- package/build/lib/providers/timing.js +10 -0
- package/build/lib/providers/timing.js.map +1 -0
- package/build/local-files.js +95 -0
- package/build/local-files.js.map +1 -0
- package/build/manager.js +171 -47
- package/build/manager.js.map +1 -1
- package/build/note.js +1 -1
- package/build/note.js.map +1 -1
- package/build/npm-reads.js +323 -0
- package/build/npm-reads.js.map +1 -0
- package/build/paths.js +44 -1
- package/build/paths.js.map +1 -1
- package/build/plugin.js +10 -0
- package/build/plugin.js.map +1 -1
- package/build/profile-fields.js +149 -0
- package/build/profile-fields.js.map +1 -0
- package/build/profile-layers.js +264 -0
- package/build/profile-layers.js.map +1 -0
- package/build/profile.js +60 -38
- package/build/profile.js.map +1 -1
- package/build/project-check-cli.js +18 -10
- package/build/project-check-cli.js.map +1 -1
- package/build/project-check.js +179 -97
- package/build/project-check.js.map +1 -1
- package/build/pushover.js +5 -5
- package/build/pushover.js.map +1 -1
- package/build/registry.js +195 -36
- package/build/registry.js.map +1 -1
- package/build/rule-actions.js +3 -1
- package/build/rule-actions.js.map +1 -1
- package/build/rules-service.js +101 -44
- package/build/rules-service.js.map +1 -1
- package/build/rules.js +11 -2
- package/build/rules.js.map +1 -1
- package/build/runs.js +49 -0
- package/build/runs.js.map +1 -1
- package/build/scheduler.js +11 -1
- package/build/scheduler.js.map +1 -1
- package/build/server.js +3 -24
- package/build/server.js.map +1 -1
- package/build/spend.js +4 -0
- package/build/spend.js.map +1 -1
- package/build/store.js +82 -25
- package/build/store.js.map +1 -1
- package/build/timeline.js +104 -0
- package/build/timeline.js.map +1 -0
- package/build/tracker-reads-cli.js +4 -5
- package/build/tracker-reads-cli.js.map +1 -1
- package/build/train-cli.js +6 -5
- package/build/train-cli.js.map +1 -1
- package/build/upgrade.js +16 -1
- package/build/upgrade.js.map +1 -1
- package/build/wait.js +6 -6
- package/build/wait.js.map +1 -1
- package/build/watchers/ado.js +3 -2
- package/build/watchers/ado.js.map +1 -1
- package/build/watchers/github.js +3 -2
- package/build/watchers/github.js.map +1 -1
- package/build/watchers/npm.js +6 -4
- package/build/watchers/npm.js.map +1 -1
- package/build/worktrees.js +199 -92
- package/build/worktrees.js.map +1 -1
- package/package.json +61 -61
- package/plugin/skills/conductor-console/SKILL.md +3 -3
- package/plugin/skills/work-item/SKILL.md +5 -3
- package/build/checkout-sync.js +0 -249
- package/build/checkout-sync.js.map +0 -1
- package/build/credential.js +0 -66
- package/build/credential.js.map +0 -1
- package/build/tab.js +0 -32
- package/build/tab.js.map +0 -1
package/build/cli.js
CHANGED
|
@@ -1,36 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
-
import { join,
|
|
4
|
+
import { join, sep } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { parseArgs } from 'node:util';
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { briefCommand } from "./brief-cli.js";
|
|
9
9
|
import { heldLocks, lockCommand, lockLine, portsCommand } from "./broker-cli.js";
|
|
10
|
+
import { trustStep } from "./claude.js";
|
|
10
11
|
import { callApi, NotRunningError, ping, readEndpoint } from "./client.js";
|
|
11
|
-
import { branchCommand, checkoutCommand, fileCommand, landCommand, modeCommand, prCreateCommand, prThreadsCommand, replyCommand, runsCommand, stateCommand, } from "./code-host-cli.js";
|
|
12
|
+
import { branchCommand, checkoutCommand, fileCommand, landCommand, modeCommand, prCreateCommand, prThreadsCommand, replyCommand, runsCommand, stateCommand, timelineCommand, } from "./code-host-cli.js";
|
|
12
13
|
import { configSchema, loadConfig } from "./config.js";
|
|
13
|
-
import { claimConsole, CONSOLE_WINDOW, consoleArgs,
|
|
14
|
+
import { claimConsole, CONSOLE_WINDOW, consoleArgs, consoleEnv, consoleTab, escalationLines, escalationSchema, handledSchema, } from "./console.js";
|
|
14
15
|
import { AlreadyRunningError, logCrashes, startDaemon } from "./daemon.js";
|
|
15
16
|
import { ConductorError } from "./errors.js";
|
|
16
17
|
import { eventsCommand } from "./events.js";
|
|
17
18
|
import { commandHelp, isCommand, listing, terminalStyle, terminalWidth, unknownCommand, usageError } from "./help.js";
|
|
18
19
|
import { flagWindow, historyLines, historyReport, historySchema, parseRange, retroMarkdown, } from "./history.js";
|
|
20
|
+
import { terminal } from "./host/terminal.js";
|
|
19
21
|
import { inboxLines } from "./inbox.js";
|
|
22
|
+
import { initProject, terminalAsk } from "./init.js";
|
|
20
23
|
import { parseJsonc } from "./jsonc.js";
|
|
24
|
+
import { npmStage } from "./lib/providers/index.js";
|
|
21
25
|
import { processAlive, readPid } from "./lock.js";
|
|
22
26
|
import { rotatingLog } from "./log.js";
|
|
23
27
|
import { fleetWorkerSchema, storedFleet, workerByRef, workerLine } from "./manager.js";
|
|
24
28
|
import { noteCommand } from "./note.js";
|
|
25
29
|
import { isQuiet, notifyLines, notifyStatusSchema, parseUntil, testResultSchema } from "./notify.js";
|
|
30
|
+
import { sharedNpmReads } from "./npm-reads.js";
|
|
26
31
|
import { overrideViewSchema, PAUSE } from "./overrides.js";
|
|
27
32
|
import { packageInfo } from "./package-info.js";
|
|
28
33
|
import { conductorPaths } from "./paths.js";
|
|
29
34
|
import { refreshConductorPlugin } from "./plugin.js";
|
|
30
|
-
import {
|
|
35
|
+
import { profileLines, projectProfile } from "./profile-layers.js";
|
|
36
|
+
import { projectCheckCommand, projectCheckReport } from "./project-check-cli.js";
|
|
31
37
|
import { noProject } from "./project-check.js";
|
|
32
38
|
import { pushoverCredential, pushoverSender } from "./pushover.js";
|
|
33
|
-
import { registerProject } from "./registry.js";
|
|
39
|
+
import { cloneProject, registerProject } from "./registry.js";
|
|
34
40
|
import { rulesLines, rulesService, rulesTestResultSchema, rulesViewSchema, testLines, } from "./rules-service.js";
|
|
35
41
|
import { resolveUntil } from "./rules.js";
|
|
36
42
|
import { outputOf, run } from "./run.js";
|
|
@@ -41,10 +47,9 @@ import { installStartup, removeStartup, runStartup, startupRunsCli, startupSuper
|
|
|
41
47
|
import { answerStopRequest, dropStaleStopRequest, pendingStopRequest, STOP_POLL_MS, stopSupervisor, } from "./stop-request.js";
|
|
42
48
|
import { inboxItemSchema, openStore, projectSchema, waitSchema, workerSchema, } from "./store.js";
|
|
43
49
|
import { RESTART_ENV, restartInfoSchema, SERVE_LOCKED_EXIT, serveChild, supervise, SUPERVISE_POLICY, } from "./supervise.js";
|
|
44
|
-
import { openWindowsTerminal } from "./tab.js";
|
|
45
50
|
import { commentsCommand, notesCommand, searchCommand } from "./tracker-reads-cli.js";
|
|
46
51
|
import { trainCommand } from "./train-cli.js";
|
|
47
|
-
import { parseTag, restartDaemon, upgrade } from "./upgrade.js";
|
|
52
|
+
import { parseTag, restartDaemon, tagVersion, upgrade } from "./upgrade.js";
|
|
48
53
|
import { waitCommand } from "./wait.js";
|
|
49
54
|
import { whyLines, whySchema } from "./why.js";
|
|
50
55
|
const STOP_WAIT_MS = 10_000;
|
|
@@ -84,7 +89,13 @@ const retireBody = z.object({
|
|
|
84
89
|
work: workSchema.extend({ checkouts: z.record(z.string(), workSchema).optional() }).optional(),
|
|
85
90
|
worker: workerSchema,
|
|
86
91
|
});
|
|
87
|
-
|
|
92
|
+
// hint: from ATK-529 on; an older daemon sends none.
|
|
93
|
+
const attachBody = z.object({
|
|
94
|
+
hint: z.string().nullable().default(null),
|
|
95
|
+
resumed: z.boolean(),
|
|
96
|
+
tab: z.string().nullable(),
|
|
97
|
+
worker: workerSchema,
|
|
98
|
+
});
|
|
88
99
|
const inboxBody = z.object({ items: z.array(inboxItemSchema) });
|
|
89
100
|
const answerBody = z.object({
|
|
90
101
|
comment: z.object({ id: z.string(), url: z.string().nullable() }).nullable(),
|
|
@@ -96,11 +107,14 @@ const answerRefusal = z.object({
|
|
|
96
107
|
error: z.string(),
|
|
97
108
|
posted: z.enum(['no', 'unknown']).optional(),
|
|
98
109
|
});
|
|
99
|
-
|
|
100
|
-
|
|
110
|
+
// `conduct project add <url | path> [--name <name>]`: the clone is made here, where its progress
|
|
111
|
+
// shows and no API timeout applies, then the daemon registers it (or the store, with none running).
|
|
112
|
+
// `next`: false for `conduct init`, which says what comes next itself.
|
|
113
|
+
async function addProject(paths, source, name, next = true) {
|
|
114
|
+
const made = await cloneProject(paths, source, { ...(name === undefined ? {} : { name }), say: out });
|
|
101
115
|
let result;
|
|
102
116
|
try {
|
|
103
|
-
result = addBody.parse(await expectOk(callApi(paths, { body: {
|
|
117
|
+
result = addBody.parse(await expectOk(callApi(paths, { body: { name: made.name }, method: 'POST', path: '/projects' })));
|
|
104
118
|
}
|
|
105
119
|
catch (error) {
|
|
106
120
|
if (!(error instanceof NotRunningError))
|
|
@@ -108,9 +122,15 @@ async function addProject(paths, path) {
|
|
|
108
122
|
// No daemon: write the store directly. The daemon reads it when it starts.
|
|
109
123
|
// Its rules are checked against config.json here too, as the daemon would.
|
|
110
124
|
const check = rulesService({ config: loadConfig(paths.config), listProjects: () => [], paths }).checkProfile;
|
|
111
|
-
result = withStore(paths, (store) => registerProject(store,
|
|
125
|
+
result = withStore(paths, (store) => registerProject(store, paths, made.name, check));
|
|
112
126
|
}
|
|
113
127
|
out(`${result.created ? 'registered' : 'updated'} ${projectLine(result.project).trim()}`);
|
|
128
|
+
out(made.cloned ? `cloned ${made.url} into ${made.clone}` : `fetched ${made.clone}, the clone it has`);
|
|
129
|
+
out(`its machine profile, which wins over the repo's: ${result.project.profilePath}`);
|
|
130
|
+
// Claude Code trusts a worktree by its repository, and nobody has run Claude in a new clone yet.
|
|
131
|
+
if (made.cloned && next)
|
|
132
|
+
out(`next: ${trustStep(made.clone)}, then \`conduct project check ${made.name}\``);
|
|
133
|
+
return made;
|
|
114
134
|
}
|
|
115
135
|
// `conduct approve|revise|done`: Jason's answer to an inbox item, by id, or for approve and revise
|
|
116
136
|
// by the item whose plan waits.
|
|
@@ -149,6 +169,8 @@ async function attach(paths, ref) {
|
|
|
149
169
|
out(`${result.resumed ? 'woke' : 'attached to'} ${worker.name} (session ${result.worker.handle ?? '?'})`);
|
|
150
170
|
if (result.tab)
|
|
151
171
|
out(`no tab (${result.tab}); open it yourself: claude attach ${result.worker.handle ?? worker.name}`);
|
|
172
|
+
else if (result.hint)
|
|
173
|
+
out(`to see its tab: ${result.hint}`);
|
|
152
174
|
}
|
|
153
175
|
// The pid of the daemon once it answers /health, for upgrade's restart. failure: the detached
|
|
154
176
|
// serve's spawn error, which ends the wait early.
|
|
@@ -191,7 +213,7 @@ async function consoleCommand(paths, options) {
|
|
|
191
213
|
await claimConsole(paths, options.force);
|
|
192
214
|
const child = spawn('claude', consoleArgs(paths.plugin), {
|
|
193
215
|
cwd: paths.console,
|
|
194
|
-
env:
|
|
216
|
+
env: consoleEnv(process.env),
|
|
195
217
|
stdio: 'inherit',
|
|
196
218
|
});
|
|
197
219
|
// Ctrl+C belongs to the session; this process only waits for it to end.
|
|
@@ -206,26 +228,17 @@ async function consoleCommand(paths, options) {
|
|
|
206
228
|
});
|
|
207
229
|
return;
|
|
208
230
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
'run `conduct console --here` in a terminal of your own',
|
|
212
|
-
]);
|
|
213
|
-
}
|
|
214
|
-
// A tab starts with Windows Terminal's environment, not this one's.
|
|
215
|
-
if (process.env.CONDUCTOR_HOME) {
|
|
216
|
-
throw new ConductorError('a console tab would not see CONDUCTOR_HOME', [
|
|
217
|
-
'run `conduct console --here` in a terminal where it is set',
|
|
218
|
-
]);
|
|
219
|
-
}
|
|
220
|
-
const args = consoleTabArgs(paths.console, process.execPath, realpathSync(fileURLToPath(import.meta.url)));
|
|
231
|
+
const where = terminal({ choice: loadConfig(paths.config).workers.terminal });
|
|
232
|
+
const tab = consoleTab(paths.console, process.execPath, realpathSync(fileURLToPath(import.meta.url)), process.env);
|
|
221
233
|
await claimConsole(paths, options.force);
|
|
222
|
-
const
|
|
223
|
-
if (
|
|
224
|
-
throw new ConductorError(
|
|
225
|
-
|
|
234
|
+
const failed = await where.open(tab);
|
|
235
|
+
if (failed !== null)
|
|
236
|
+
throw new ConductorError(`could not open the console tab in ${where.name}`, [
|
|
237
|
+
failed,
|
|
226
238
|
'run `conduct console --here --force` in a terminal instead',
|
|
227
239
|
]);
|
|
228
|
-
|
|
240
|
+
const hint = where.attachHint(CONSOLE_WINDOW);
|
|
241
|
+
out(`opened the console in ${paths.console} (${hint === null ? `${where.name} window "${CONSOLE_WINDOW}"` : `${where.name}: ${hint}`})`);
|
|
229
242
|
}
|
|
230
243
|
// How `conduct start` and upgrade's restart start the daemon detached from this process.
|
|
231
244
|
function daemonStarter(paths, cliPath) {
|
|
@@ -403,7 +416,7 @@ async function listProjects(paths, json) {
|
|
|
403
416
|
if (json)
|
|
404
417
|
return out(JSON.stringify(projects, null, 2));
|
|
405
418
|
if (projects.length === 0)
|
|
406
|
-
return out('no projects; add one with `conduct project add <path>`');
|
|
419
|
+
return out('no projects; add one with `conduct project add <url | path>`');
|
|
407
420
|
projects.forEach((p) => out(projectLine(p)));
|
|
408
421
|
}
|
|
409
422
|
async function main(argv) {
|
|
@@ -443,12 +456,15 @@ async function main(argv) {
|
|
|
443
456
|
options: {
|
|
444
457
|
'allow-downgrade': { type: 'boolean' },
|
|
445
458
|
area: { type: 'string' },
|
|
459
|
+
base: { type: 'string' },
|
|
446
460
|
'blocked-by': { multiple: true, type: 'string' },
|
|
447
461
|
'body-file': { type: 'string' },
|
|
462
|
+
branch: { type: 'string' },
|
|
448
463
|
channel: { type: 'string' },
|
|
449
464
|
comments: { type: 'boolean' },
|
|
450
465
|
conductor: { type: 'string' },
|
|
451
466
|
console: { type: 'boolean' },
|
|
467
|
+
count: { type: 'string' },
|
|
452
468
|
'dry-run': { type: 'boolean' },
|
|
453
469
|
'fail-fast': { type: 'boolean' },
|
|
454
470
|
files: { type: 'string' },
|
|
@@ -456,21 +472,24 @@ async function main(argv) {
|
|
|
456
472
|
for: { type: 'string' },
|
|
457
473
|
force: { type: 'boolean' },
|
|
458
474
|
fresh: { type: 'boolean' },
|
|
475
|
+
grep: { type: 'string' },
|
|
459
476
|
help: { short: 'h', type: 'boolean' },
|
|
460
477
|
here: { type: 'boolean' },
|
|
461
478
|
human: { type: 'boolean' },
|
|
462
479
|
item: { type: 'string' },
|
|
463
480
|
json: { type: 'boolean' },
|
|
481
|
+
key: { type: 'string' },
|
|
464
482
|
kind: { type: 'string' },
|
|
465
483
|
last: { type: 'boolean' },
|
|
466
484
|
mode: { type: 'string' },
|
|
485
|
+
name: { type: 'string' },
|
|
467
486
|
'no-worktree': { type: 'boolean' },
|
|
468
487
|
owner: { type: 'string' },
|
|
469
488
|
phase: { type: 'string' },
|
|
470
|
-
prefer: { type: 'string' },
|
|
471
489
|
probe: { type: 'boolean' },
|
|
472
490
|
project: { type: 'string' },
|
|
473
491
|
prompt: { type: 'string' },
|
|
492
|
+
query: { type: 'string' },
|
|
474
493
|
raise: { type: 'boolean' },
|
|
475
494
|
rearm: { type: 'boolean' },
|
|
476
495
|
reason: { type: 'string' },
|
|
@@ -478,18 +497,23 @@ async function main(argv) {
|
|
|
478
497
|
remove: { type: 'boolean' },
|
|
479
498
|
repo: { type: 'string' },
|
|
480
499
|
requeue: { type: 'string' },
|
|
500
|
+
shared: { type: 'boolean' },
|
|
481
501
|
since: { type: 'string' },
|
|
482
502
|
slots: { type: 'string' },
|
|
483
503
|
tag: { type: 'string' },
|
|
504
|
+
task: { type: 'string' },
|
|
505
|
+
team: { type: 'string' },
|
|
484
506
|
text: { type: 'string' },
|
|
485
507
|
tier: { type: 'string' },
|
|
486
508
|
title: { type: 'string' },
|
|
509
|
+
tracker: { type: 'string' },
|
|
487
510
|
until: { type: 'string' },
|
|
488
511
|
version: { short: 'v', type: 'boolean' },
|
|
489
512
|
wait: { type: 'string' },
|
|
490
513
|
'waits-release': { type: 'boolean' },
|
|
491
514
|
weight: { type: 'string' },
|
|
492
515
|
worker: { type: 'string' },
|
|
516
|
+
yes: { type: 'boolean' },
|
|
493
517
|
},
|
|
494
518
|
}));
|
|
495
519
|
const paths = conductorPaths();
|
|
@@ -516,7 +540,7 @@ async function main(argv) {
|
|
|
516
540
|
case 'branch':
|
|
517
541
|
if (sub === undefined || arg === undefined)
|
|
518
542
|
throw usageError('branch');
|
|
519
|
-
process.exitCode = await branchCommand(sub, arg, item, verbIo());
|
|
543
|
+
process.exitCode = await branchCommand(paths, sub, arg, item, verbIo());
|
|
520
544
|
return;
|
|
521
545
|
case 'brief':
|
|
522
546
|
if (sub === undefined)
|
|
@@ -530,7 +554,7 @@ async function main(argv) {
|
|
|
530
554
|
case 'checkout':
|
|
531
555
|
if (sub === undefined || arg === undefined || fourth === undefined)
|
|
532
556
|
throw usageError('checkout');
|
|
533
|
-
process.exitCode = await checkoutCommand(sub, arg, fourth, item, verbIo());
|
|
557
|
+
process.exitCode = await checkoutCommand(paths, sub, arg, fourth, item, verbIo());
|
|
534
558
|
return;
|
|
535
559
|
case 'comments': {
|
|
536
560
|
if (sub === undefined || arg !== undefined)
|
|
@@ -592,6 +616,26 @@ async function main(argv) {
|
|
|
592
616
|
});
|
|
593
617
|
case 'inbox':
|
|
594
618
|
return inbox(paths, values.json === true);
|
|
619
|
+
case 'init':
|
|
620
|
+
if (arg !== undefined)
|
|
621
|
+
throw usageError('init');
|
|
622
|
+
process.exitCode = await initProject({
|
|
623
|
+
add: (checkout, name) => addProject(paths, checkout, name, false),
|
|
624
|
+
ask: terminalAsk(),
|
|
625
|
+
check: (name) => projectCheckReport(paths, name, { worktree: true }, { err: (line) => process.stderr.write(`${line}\n`) }),
|
|
626
|
+
out,
|
|
627
|
+
paths,
|
|
628
|
+
}, sub ?? '.', {
|
|
629
|
+
shared: values.shared === true,
|
|
630
|
+
yes: values.yes === true,
|
|
631
|
+
...(values.base === undefined ? {} : { base: values.base }),
|
|
632
|
+
...(values.key === undefined ? {} : { key: values.key }),
|
|
633
|
+
...(values.name === undefined ? {} : { name: values.name }),
|
|
634
|
+
...(values.query === undefined ? {} : { query: values.query }),
|
|
635
|
+
...(values.team === undefined ? {} : { team: values.team }),
|
|
636
|
+
...(values.tracker === undefined ? {} : { tracker: values.tracker }),
|
|
637
|
+
});
|
|
638
|
+
return;
|
|
595
639
|
case 'install-startup':
|
|
596
640
|
return values.remove ? uninstall() : install();
|
|
597
641
|
case 'land': {
|
|
@@ -638,7 +682,7 @@ async function main(argv) {
|
|
|
638
682
|
throw usageError('mode');
|
|
639
683
|
if (tier !== 'S' && tier !== 'M' && tier !== 'L')
|
|
640
684
|
throw new ConductorError('--tier takes S, M or L');
|
|
641
|
-
process.exitCode = await modeCommand({ files: values.files, tier }, verbIo());
|
|
685
|
+
process.exitCode = await modeCommand(paths, { files: values.files, tier }, verbIo());
|
|
642
686
|
return;
|
|
643
687
|
}
|
|
644
688
|
case 'note':
|
|
@@ -685,14 +729,23 @@ async function main(argv) {
|
|
|
685
729
|
...(values.until === undefined ? {} : { until: untilOf(values.until) }),
|
|
686
730
|
});
|
|
687
731
|
case 'ports':
|
|
688
|
-
process.exitCode = await portsCommand(paths, sub, {
|
|
689
|
-
json: values.json === true,
|
|
690
|
-
...(values.prefer === undefined ? {} : { prefer: wholeNumber('--prefer', values.prefer) }),
|
|
691
|
-
}, brokerIo());
|
|
732
|
+
process.exitCode = await portsCommand(paths, sub, { json: values.json === true }, brokerIo());
|
|
692
733
|
return;
|
|
734
|
+
case 'profile': {
|
|
735
|
+
if (sub === undefined || arg !== undefined)
|
|
736
|
+
throw usageError('profile');
|
|
737
|
+
// Resolved here from the machine file and the clone, as the daemon resolves it.
|
|
738
|
+
const layered = projectProfile(paths, sub);
|
|
739
|
+
if (values.json === true)
|
|
740
|
+
return out(JSON.stringify({ profile: layered.profile, sources: layered.sources }, null, 2));
|
|
741
|
+
profileLines(sub, layered).forEach((line) => out(line));
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
693
744
|
case 'project':
|
|
694
|
-
if (sub === 'add' && arg !== undefined)
|
|
695
|
-
|
|
745
|
+
if (sub === 'add' && arg !== undefined) {
|
|
746
|
+
await addProject(paths, arg, values.name);
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
696
749
|
if (sub === 'remove' && arg !== undefined)
|
|
697
750
|
return removeProject(paths, arg);
|
|
698
751
|
if (sub === 'list')
|
|
@@ -733,6 +786,7 @@ async function main(argv) {
|
|
|
733
786
|
...item,
|
|
734
787
|
...repo,
|
|
735
788
|
...(values.for === undefined ? {} : { for: values.for }),
|
|
789
|
+
...(values.grep === undefined ? {} : { grep: values.grep }),
|
|
736
790
|
...(values.requeue === undefined ? {} : { requeue: values.requeue }),
|
|
737
791
|
}, verbIo());
|
|
738
792
|
return;
|
|
@@ -758,6 +812,19 @@ async function main(argv) {
|
|
|
758
812
|
case 'supervise':
|
|
759
813
|
process.exitCode = await superviseCommand(paths, values['fail-fast'] === true);
|
|
760
814
|
return;
|
|
815
|
+
case 'timeline':
|
|
816
|
+
if (arg !== undefined)
|
|
817
|
+
throw usageError('timeline');
|
|
818
|
+
process.exitCode = await timelineCommand(paths, {
|
|
819
|
+
...repo,
|
|
820
|
+
...(sub === undefined ? {} : { run: sub }),
|
|
821
|
+
...(values.branch === undefined ? {} : { branch: values.branch }),
|
|
822
|
+
...(values.count === undefined ? {} : { count: values.count }),
|
|
823
|
+
...(values.for === undefined ? {} : { for: values.for }),
|
|
824
|
+
...(values.grep === undefined ? {} : { grep: values.grep }),
|
|
825
|
+
...(values.task === undefined ? {} : { task: values.task }),
|
|
826
|
+
}, verbIo());
|
|
827
|
+
return;
|
|
761
828
|
case 'train':
|
|
762
829
|
return trainCommand(paths, sub, { dryRun: values['dry-run'] === true }, { out });
|
|
763
830
|
case 'unpause':
|
|
@@ -813,7 +880,7 @@ async function notifyTest(paths, options) {
|
|
|
813
880
|
...(options.kind ? { kind: options.kind } : {}),
|
|
814
881
|
raise: options.raise,
|
|
815
882
|
};
|
|
816
|
-
// Pushover can take its 15 s, after a
|
|
883
|
+
// Pushover can take its 15 s, after a secret store read.
|
|
817
884
|
const answer = await notifyAnswer(paths, { body, method: 'POST', path: '/notify/test', timeoutMs: 60_000 });
|
|
818
885
|
if (options.raise) {
|
|
819
886
|
const { item } = z.object({ item: inboxItemSchema }).parse(answer);
|
|
@@ -913,11 +980,11 @@ async function planItem(paths, ref) {
|
|
|
913
980
|
}
|
|
914
981
|
function projectLine(p) {
|
|
915
982
|
const state = p.problem
|
|
916
|
-
? `${p.problem}; the daemon reads
|
|
983
|
+
? `${p.problem}; the daemon reads the profile again when it starts, when ~/.conductor/projects/${p.name}.json is saved, and after each fetch of the clone`
|
|
917
984
|
: p.enabled
|
|
918
985
|
? 'enabled'
|
|
919
986
|
: 'disabled';
|
|
920
|
-
return ` ${p.name} ${p.
|
|
987
|
+
return ` ${p.name} ${p.clone} (protocol ${p.protocol}, ${state})`;
|
|
921
988
|
}
|
|
922
989
|
// The registered projects: from the daemon, or from the store when it isn't running.
|
|
923
990
|
async function projectsNow(paths) {
|
|
@@ -962,7 +1029,7 @@ async function removeProject(paths, name) {
|
|
|
962
1029
|
});
|
|
963
1030
|
}
|
|
964
1031
|
const { project, resolved } = result;
|
|
965
|
-
out(`removed ${project.name} (${project.
|
|
1032
|
+
out(`removed ${project.name} (${project.clone})`);
|
|
966
1033
|
for (const item of resolved)
|
|
967
1034
|
out(` resolved #${item.id} (${item.kind}${item.item ? ` ${item.item}` : ''})`);
|
|
968
1035
|
}
|
|
@@ -1156,7 +1223,7 @@ async function status(paths, json) {
|
|
|
1156
1223
|
if (json)
|
|
1157
1224
|
return out(JSON.stringify({ home: paths.home, inbox: items, locks, pid, port, projects, running: true, workers, workersFrom: 'daemon' }, null, 2));
|
|
1158
1225
|
out(`conductor running (pid ${pid ?? '?'}, port ${port ?? '?'}, home ${paths.home})`);
|
|
1159
|
-
out(projects.length === 0 ? 'no projects; add one with `conduct project add <path>`' : 'projects:');
|
|
1226
|
+
out(projects.length === 0 ? 'no projects; add one with `conduct project add <url | path>`' : 'projects:');
|
|
1160
1227
|
projects.forEach((p) => out(projectLine(p)));
|
|
1161
1228
|
out(workers.length === 0 ? 'no workers' : 'workers:');
|
|
1162
1229
|
workers.forEach((w) => out(workerLine(w)));
|
|
@@ -1310,28 +1377,26 @@ function untilOf(text) {
|
|
|
1310
1377
|
}
|
|
1311
1378
|
async function upgradeCommand(paths, tag, allowDowngrade) {
|
|
1312
1379
|
const cliPath = realpathSync(fileURLToPath(import.meta.url));
|
|
1380
|
+
// npm's own settings, so the tag is read where `npm install --global` installs from (ATK-561).
|
|
1381
|
+
const release = npmStage(sharedNpmReads());
|
|
1313
1382
|
await upgrade({ allowDowngrade, tag: parseTag(tag) }, {
|
|
1314
1383
|
...daemonStarter(paths, cliPath),
|
|
1315
1384
|
cliPath,
|
|
1316
1385
|
installedVersion: version,
|
|
1317
|
-
|
|
1386
|
+
// --prefer-online: npm resolves the exact version against a fresh packument, not a cached one
|
|
1387
|
+
// that may not list it yet (ATK-558).
|
|
1388
|
+
npmInstall: (spec) => npm(['install', '--global', '--prefer-online', spec], NPM_INSTALL_TIMEOUT_MS),
|
|
1318
1389
|
npmRoot: async () => {
|
|
1319
1390
|
const result = await npm(['root', '--global']);
|
|
1320
1391
|
if (result.code !== 0)
|
|
1321
1392
|
throw new ConductorError('could not ask npm for its global folder', [outputOf(result)]);
|
|
1322
1393
|
return realpathSync(result.stdout.trim());
|
|
1323
1394
|
},
|
|
1324
|
-
npmView: async (spec) => {
|
|
1325
|
-
const result = await npm(['view', spec, 'version']);
|
|
1326
|
-
const found = result.stdout.trim();
|
|
1327
|
-
if (result.code !== 0 || found === '')
|
|
1328
|
-
throw new ConductorError(`npm has no ${spec}`, [outputOf(result)]);
|
|
1329
|
-
return found;
|
|
1330
|
-
},
|
|
1331
1395
|
out,
|
|
1332
1396
|
// The installed package's plugin: npm put it where this process's own plugin folder was.
|
|
1333
1397
|
refreshPlugin: () => void refreshConductorPlugin(paths),
|
|
1334
1398
|
stopDaemon: () => stopDaemon(paths),
|
|
1399
|
+
tagVersion: (tag) => tagVersion(release, tag),
|
|
1335
1400
|
});
|
|
1336
1401
|
}
|
|
1337
1402
|
function verbIo() {
|