@quickchatai/quickreview 1.2.0 → 1.3.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.
- package/README.md +116 -53
- package/bin/qcr.js +1 -1
- package/config.example.toml +25 -7
- package/deploy/ansible/README.md +20 -12
- package/deploy/ansible/inventory.example.ini +1 -1
- package/deploy/collect-secrets.sh +1 -1
- package/deploy/qcr-update.sh +1 -1
- package/dist/extensions/cli/home.js +14 -0
- package/dist/extensions/cli/home.js.map +1 -0
- package/dist/extensions/cli/selfimprove.js +18 -0
- package/dist/extensions/cli/selfimprove.js.map +1 -0
- package/dist/extensions/home.js +350 -0
- package/dist/extensions/home.js.map +1 -0
- package/dist/extensions/login-route.js +1 -1
- package/dist/extensions/login-route.js.map +1 -1
- package/dist/extensions/onboarding.js +8 -7
- package/dist/extensions/onboarding.js.map +1 -1
- package/dist/extensions/selfimprove.js +49 -0
- package/dist/extensions/selfimprove.js.map +1 -0
- package/dist/extensions/setup-scan.js +23 -592
- package/dist/extensions/setup-scan.js.map +1 -1
- package/dist/extensions/setup.js +2 -47
- package/dist/extensions/setup.js.map +1 -1
- package/dist/extensions/vendor/openviking-pi/index.js +4 -0
- package/dist/extensions/vendor/openviking-pi/index.js.map +1 -1
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +3 -1
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -1
- package/dist/extensions/vendor/openviking-pi/sync.js +81 -0
- package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -1
- package/dist/src/archive/restore.js +18 -15
- package/dist/src/archive/restore.js.map +1 -1
- package/dist/src/botworker.js +13 -2
- package/dist/src/botworker.js.map +1 -1
- package/dist/src/channelfs.js +3 -26
- package/dist/src/channelfs.js.map +1 -1
- package/dist/src/cli.js +14 -50
- package/dist/src/cli.js.map +1 -1
- package/dist/src/config.js +16 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/daemon.js +33 -4
- package/dist/src/daemon.js.map +1 -1
- package/dist/src/debuground.js +1 -0
- package/dist/src/debuground.js.map +1 -1
- package/dist/src/debugroundpi.js +1 -0
- package/dist/src/debugroundpi.js.map +1 -1
- package/dist/src/engine/caserunner-sdk.js +15 -251
- package/dist/src/engine/caserunner-sdk.js.map +1 -1
- package/dist/src/home/adapters.js +349 -0
- package/dist/src/home/adapters.js.map +1 -0
- package/dist/src/home/advanced.js +318 -0
- package/dist/src/home/advanced.js.map +1 -0
- package/dist/src/home/advancedtools.js +57 -0
- package/dist/src/home/advancedtools.js.map +1 -0
- package/dist/src/home/autoreview.js +325 -0
- package/dist/src/home/autoreview.js.map +1 -0
- package/dist/src/home/daemonstatus.js +32 -0
- package/dist/src/home/daemonstatus.js.map +1 -0
- package/dist/src/home/devicelogin.js +61 -0
- package/dist/src/home/devicelogin.js.map +1 -0
- package/dist/src/home/launch.js +202 -0
- package/dist/src/home/launch.js.map +1 -0
- package/dist/src/home/loginowner.js +205 -0
- package/dist/src/home/loginowner.js.map +1 -0
- package/dist/src/home/memory.js +122 -0
- package/dist/src/home/memory.js.map +1 -0
- package/dist/src/home/model.js +811 -0
- package/dist/src/home/model.js.map +1 -0
- package/dist/src/home/panel.js +520 -0
- package/dist/src/home/panel.js.map +1 -0
- package/dist/src/home/ports.js +19 -0
- package/dist/src/home/ports.js.map +1 -0
- package/dist/src/home/review.js +163 -0
- package/dist/src/home/review.js.map +1 -0
- package/dist/src/home/reviews.js +478 -0
- package/dist/src/home/reviews.js.map +1 -0
- package/dist/src/home/setup.js +507 -0
- package/dist/src/home/setup.js.map +1 -0
- package/dist/src/home/setupadapters.js +214 -0
- package/dist/src/home/setupadapters.js.map +1 -0
- package/dist/src/home/setupcard.js +199 -0
- package/dist/src/home/setupcard.js.map +1 -0
- package/dist/src/home/setuptools.js +45 -0
- package/dist/src/home/setuptools.js.map +1 -0
- package/dist/src/home/spend.js +160 -0
- package/dist/src/home/spend.js.map +1 -0
- package/dist/src/home/switches.js +209 -0
- package/dist/src/home/switches.js.map +1 -0
- package/dist/src/home/switchon.js +439 -0
- package/dist/src/home/switchon.js.map +1 -0
- package/dist/src/home/toolnames.js +41 -0
- package/dist/src/home/toolnames.js.map +1 -0
- package/dist/src/home/tune.js +341 -0
- package/dist/src/home/tune.js.map +1 -0
- package/dist/src/home/words.js +7 -0
- package/dist/src/home/words.js.map +1 -0
- package/dist/src/homes.js +14 -0
- package/dist/src/homes.js.map +1 -1
- package/dist/src/install.js +1 -1
- package/dist/src/install.js.map +1 -1
- package/dist/src/ipc/workerspec.js +8 -0
- package/dist/src/ipc/workerspec.js.map +1 -1
- package/dist/src/ledgercheck.js +39 -10
- package/dist/src/ledgercheck.js.map +1 -1
- package/dist/src/live/publish.js +1 -1
- package/dist/src/live/publish.js.map +1 -1
- package/dist/src/livecli.js +1 -1
- package/dist/src/livecli.js.map +1 -1
- package/dist/src/loginrefresh/handover.js +121 -0
- package/dist/src/loginrefresh/handover.js.map +1 -0
- package/dist/src/loginrefresh/lock.js +54 -3
- package/dist/src/loginrefresh/lock.js.map +1 -1
- package/dist/src/loginrefresh/refresher.js +33 -5
- package/dist/src/loginrefresh/refresher.js.map +1 -1
- package/dist/src/loginrefresh/supervisor.js +18 -1
- package/dist/src/loginrefresh/supervisor.js.map +1 -1
- package/dist/src/npmverbs.js +42 -12
- package/dist/src/npmverbs.js.map +1 -1
- package/dist/src/onboard/choices.js +6 -3
- package/dist/src/onboard/choices.js.map +1 -1
- package/dist/src/onboard/engine.js +33 -292
- package/dist/src/onboard/engine.js.map +1 -1
- package/dist/src/onboard/steps/bringup.js +3 -2
- package/dist/src/onboard/steps/bringup.js.map +1 -1
- package/dist/src/onboard/steps/configfill.js +18 -7
- package/dist/src/onboard/steps/configfill.js.map +1 -1
- package/dist/src/onboard/steps/loginshare.js +213 -0
- package/dist/src/onboard/steps/loginshare.js.map +1 -0
- package/dist/src/onboard/steps/memory.js +14 -2
- package/dist/src/onboard/steps/memory.js.map +1 -1
- package/dist/src/onboard/steps/repoconfig.js +72 -0
- package/dist/src/onboard/steps/repoconfig.js.map +1 -0
- package/dist/src/onboard/toolnames.js +2 -2
- package/dist/src/onboard/toolnames.js.map +1 -1
- package/dist/src/piagentdir.js +13 -0
- package/dist/src/piagentdir.js.map +1 -0
- package/dist/src/pilogin.js +31 -16
- package/dist/src/pilogin.js.map +1 -1
- package/dist/src/qcrbin.js +16 -11
- package/dist/src/qcrbin.js.map +1 -1
- package/dist/src/reviewargs.js +122 -0
- package/dist/src/reviewargs.js.map +1 -0
- package/dist/src/reviewcli.js +7 -161
- package/dist/src/reviewcli.js.map +1 -1
- package/dist/src/reviewdetach.js +76 -11
- package/dist/src/reviewdetach.js.map +1 -1
- package/dist/src/scancli.js +39 -691
- package/dist/src/scancli.js.map +1 -1
- package/dist/src/selfimprove.js +66 -0
- package/dist/src/selfimprove.js.map +1 -0
- package/dist/src/session-command-supervisor.js +38 -9
- package/dist/src/session-command-supervisor.js.map +1 -1
- package/dist/src/setup/adapters.js +5 -223
- package/dist/src/setup/adapters.js.map +1 -1
- package/dist/src/setup/caserunner.js +15 -249
- package/dist/src/setup/caserunner.js.map +1 -1
- package/dist/src/setup/engine.js +32 -341
- package/dist/src/setup/engine.js.map +1 -1
- package/dist/src/setup/facts.js +3 -592
- package/dist/src/setup/facts.js.map +1 -1
- package/dist/src/setup/findings.js +3 -947
- package/dist/src/setup/findings.js.map +1 -1
- package/dist/src/setup/presentation.js +3 -8
- package/dist/src/setup/presentation.js.map +1 -1
- package/dist/src/setup/scan.js +3 -724
- package/dist/src/setup/scan.js.map +1 -1
- package/dist/src/setup/scanbuild.js +9 -92
- package/dist/src/setup/scanbuild.js.map +1 -1
- package/dist/src/setup/scanlease.js +3 -250
- package/dist/src/setup/scanlease.js.map +1 -1
- package/dist/src/setup/scanreport.js +3 -288
- package/dist/src/setup/scanreport.js.map +1 -1
- package/dist/src/setup/scanrun.js +3 -1732
- package/dist/src/setup/scanrun.js.map +1 -1
- package/dist/src/setup/scantree.js +3 -256
- package/dist/src/setup/scantree.js.map +1 -1
- package/dist/src/setup/stage2request.js +3 -258
- package/dist/src/setup/stage2request.js.map +1 -1
- package/dist/src/setup/unitcut.js +3 -436
- package/dist/src/setup/unitcut.js.map +1 -1
- package/dist/src/state.js +75 -9
- package/dist/src/state.js.map +1 -1
- package/dist/src/takeover.js +1 -0
- package/dist/src/takeover.js.map +1 -1
- package/dist/src/takeoverpi.js +2 -1
- package/dist/src/takeoverpi.js.map +1 -1
- package/dist/src/tui/alerts.js +4 -1
- package/dist/src/tui/alerts.js.map +1 -1
- package/dist/src/tui/board.js +2 -2
- package/dist/src/tui/board.js.map +1 -1
- package/dist/src/tui/companion.js +2 -1
- package/dist/src/tui/companion.js.map +1 -1
- package/dist/src/tui/deploytab.js +2 -2
- package/dist/src/tui/deploytab.js.map +1 -1
- package/dist/src/tui/index.js +9 -6
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/memory.js +3 -16
- package/dist/src/tui/memory.js.map +1 -1
- package/dist/src/tui/memoryurl.js +22 -0
- package/dist/src/tui/memoryurl.js.map +1 -0
- package/dist/src/tui/remote.js +1 -1
- package/dist/src/tui/remote.js.map +1 -1
- package/dist/src/tui/route.js +2 -2
- package/dist/src/tui/route.js.map +1 -1
- package/dist/src/tui/setupstate.js +8 -5
- package/dist/src/tui/setupstate.js.map +1 -1
- package/dist/src/tui/sshtriage.js +1 -1
- package/dist/src/tui/sshtriage.js.map +1 -1
- package/dist/src/verdicts.js +12 -0
- package/dist/src/verdicts.js.map +1 -1
- package/dist/src/wire.js +9 -4
- package/dist/src/wire.js.map +1 -1
- package/dist/src/workerbreaker.js +32 -5
- package/dist/src/workerbreaker.js.map +1 -1
- package/dist/src/workerworld.js +4 -0
- package/dist/src/workerworld.js.map +1 -1
- package/docker/README.md +1 -1
- package/docker/entrypoint.sh +40 -6
- package/docker-compose.yml +16 -1
- package/extensions/cli/home.ts +14 -0
- package/extensions/cli/selfimprove.ts +18 -0
- package/extensions/home.ts +484 -0
- package/extensions/login-route.ts +1 -1
- package/extensions/onboarding.ts +8 -7
- package/extensions/selfimprove.ts +65 -0
- package/extensions/setup-scan.ts +79 -766
- package/extensions/setup.ts +14 -70
- package/extensions/vendor/openviking-pi/NOTICE +5 -0
- package/extensions/vendor/openviking-pi/README.md +39 -5
- package/extensions/vendor/openviking-pi/index.ts +4 -0
- package/extensions/vendor/openviking-pi/patches/0014-one-turn-sync-in-flight.patch +239 -0
- package/extensions/vendor/openviking-pi/qcr-hooks.ts +3 -1
- package/extensions/vendor/openviking-pi/sync.ts +80 -0
- package/extensions/vendor/openviking-pi/upstream.json +2 -1
- package/package.json +6 -4
- package/prompts/onboarding.md +8 -7
- package/schemas/review-result.v1.json +60 -0
- package/scripts/deploy.sh +8 -6
- package/scripts/qcr-host.sh +28 -5
- package/scripts/vm-ghcr-login.sh +7 -4
- package/src/archive/restore.ts +18 -21
- package/src/botworker.ts +13 -2
- package/src/channelfs.ts +3 -26
- package/src/cli.ts +13 -54
- package/src/config.ts +38 -0
- package/src/daemon.ts +34 -4
- package/src/debuground.ts +3 -0
- package/src/debugroundpi.ts +1 -0
- package/src/engine/caserunner-sdk.ts +29 -314
- package/src/home/adapters.ts +418 -0
- package/src/home/advanced.ts +421 -0
- package/src/home/advancedtools.ts +96 -0
- package/src/home/autoreview.ts +385 -0
- package/src/home/daemonstatus.ts +55 -0
- package/src/home/devicelogin.ts +107 -0
- package/src/home/launch.ts +257 -0
- package/src/home/loginowner.ts +248 -0
- package/src/home/memory.ts +176 -0
- package/src/home/model.ts +965 -0
- package/src/home/panel.ts +666 -0
- package/src/home/ports.ts +338 -0
- package/src/home/review.ts +206 -0
- package/src/home/reviews.ts +565 -0
- package/src/home/setup.ts +689 -0
- package/src/home/setupadapters.ts +217 -0
- package/src/home/setupcard.ts +288 -0
- package/src/home/setuptools.ts +57 -0
- package/src/home/spend.ts +194 -0
- package/src/home/switches.ts +293 -0
- package/src/home/switchon.ts +554 -0
- package/src/home/toolnames.ts +46 -0
- package/src/home/tune.ts +405 -0
- package/src/home/words.ts +8 -0
- package/src/homes.ts +17 -0
- package/src/install.ts +1 -1
- package/src/ipc/workerspec.ts +15 -0
- package/src/ledgercheck.ts +45 -9
- package/src/live/publish.ts +4 -1
- package/src/livecli.ts +6 -1
- package/src/loginrefresh/handover.ts +138 -0
- package/src/loginrefresh/lock.ts +72 -3
- package/src/loginrefresh/refresher.ts +34 -5
- package/src/loginrefresh/supervisor.ts +25 -2
- package/src/npmverbs.ts +51 -11
- package/src/onboard/choices.ts +6 -3
- package/src/onboard/engine.ts +64 -434
- package/src/onboard/steps/bringup.ts +3 -2
- package/src/onboard/steps/configfill.ts +23 -7
- package/src/onboard/steps/loginshare.ts +258 -0
- package/src/onboard/steps/memory.ts +18 -2
- package/src/onboard/steps/repoconfig.ts +85 -0
- package/src/onboard/toolnames.ts +2 -2
- package/src/piagentdir.ts +12 -0
- package/src/pilogin.ts +32 -16
- package/src/qcrbin.ts +16 -11
- package/src/reviewargs.ts +119 -0
- package/src/reviewcli.ts +6 -167
- package/src/reviewdetach.ts +107 -13
- package/src/scancli.ts +75 -826
- package/src/selfimprove.ts +90 -0
- package/src/session-command-supervisor.ts +41 -8
- package/src/setup/adapters.ts +13 -221
- package/src/setup/caserunner.ts +34 -273
- package/src/setup/engine.ts +62 -496
- package/src/setup/facts.ts +3 -627
- package/src/setup/findings.ts +3 -1264
- package/src/setup/presentation.ts +3 -18
- package/src/setup/scan.ts +3 -1033
- package/src/setup/scanbuild.ts +20 -114
- package/src/setup/scanlease.ts +3 -297
- package/src/setup/scanreport.ts +3 -373
- package/src/setup/scanrun.ts +3 -2268
- package/src/setup/scantree.ts +3 -259
- package/src/setup/stage2request.ts +3 -347
- package/src/setup/unitcut.ts +3 -523
- package/src/state.ts +126 -11
- package/src/takeover.ts +5 -0
- package/src/takeoverpi.ts +3 -2
- package/src/tui/alerts.ts +9 -2
- package/src/tui/board.ts +2 -2
- package/src/tui/companion.ts +2 -1
- package/src/tui/deploytab.ts +2 -2
- package/src/tui/index.ts +9 -6
- package/src/tui/memory.ts +3 -15
- package/src/tui/memoryurl.ts +22 -0
- package/src/tui/remote.ts +1 -1
- package/src/tui/route.ts +2 -2
- package/src/tui/setupstate.ts +20 -16
- package/src/tui/sshtriage.ts +1 -1
- package/src/verdicts.ts +13 -0
- package/src/wire.ts +9 -4
- package/src/workerbreaker.ts +41 -5
- package/src/workerworld.ts +9 -1
- package/dist/src/live/projection.js +0 -450
- package/dist/src/live/projection.js.map +0 -1
- package/dist/src/live/read.js +0 -60
- package/dist/src/live/read.js.map +0 -1
- package/dist/src/onboard/launch.js +0 -75
- package/dist/src/onboard/launch.js.map +0 -1
- package/dist/src/setup/ledger.js +0 -207
- package/dist/src/setup/ledger.js.map +0 -1
- package/dist/src/setup/stage2.js +0 -617
- package/dist/src/setup/stage2.js.map +0 -1
- package/dist/src/setup/textsniff.js +0 -89
- package/dist/src/setup/textsniff.js.map +0 -1
- package/src/live/projection.ts +0 -491
- package/src/live/read.ts +0 -86
- package/src/onboard/launch.ts +0 -103
- package/src/setup/ledger.ts +0 -324
- package/src/setup/stage2.ts +0 -926
- package/src/setup/textsniff.ts +0 -95
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// `qcr review`'s arguments: the usage line and the parse of argv into a
|
|
2
|
+
// command, in a module of their own. The terminal verb (src/reviewcli.ts) and
|
|
3
|
+
// the detached registry (src/reviewdetach.ts) both read them, and the
|
|
4
|
+
// registry's callers (the app's home) never import the review's runtime.
|
|
5
|
+
import { parseDestinations, type Destination } from "@quickchatai/pi-quickreview/src/reviewdest.js";
|
|
6
|
+
|
|
7
|
+
export const REVIEW_USAGE =
|
|
8
|
+
"usage: qcr review start|status|stop (detached runs, see `qcr review start --help`) | qcr review [branch | --uncommitted | --diff FILE|- | <PR number|URL>] [--repo OWNER/NAME] [--base REF] [--preview] [--key K] [--limit USD] [--full] [--to DEST] [--out FILE] [--debug-pi] [--json] | --help";
|
|
9
|
+
|
|
10
|
+
export type ReviewCommand =
|
|
11
|
+
| { help: true }
|
|
12
|
+
| {
|
|
13
|
+
help: false;
|
|
14
|
+
uncommitted: boolean;
|
|
15
|
+
/** `--diff`: a file to read the diff from, or "-" for stdin. */
|
|
16
|
+
diff?: string;
|
|
17
|
+
pr?: string;
|
|
18
|
+
repo?: string;
|
|
19
|
+
base?: string;
|
|
20
|
+
preview: boolean;
|
|
21
|
+
key?: string;
|
|
22
|
+
limit?: number;
|
|
23
|
+
full: boolean;
|
|
24
|
+
to?: Destination[];
|
|
25
|
+
out?: string;
|
|
26
|
+
/** `--debug-pi`: the round runs inside interactive pi. */
|
|
27
|
+
debugPi: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const usage = (why?: string): Error => new Error(why ? `${REVIEW_USAGE}\n(${why})` : REVIEW_USAGE);
|
|
31
|
+
|
|
32
|
+
/** `qcr review`'s arguments (`--json` taken off before). An unknown token is
|
|
33
|
+
* refused, so a mistyped flag never runs something else. */
|
|
34
|
+
export function parseReviewArgs(rest: readonly string[]): ReviewCommand {
|
|
35
|
+
let help = false;
|
|
36
|
+
let uncommitted = false;
|
|
37
|
+
let diff: string | undefined;
|
|
38
|
+
let preview = false;
|
|
39
|
+
let full = false;
|
|
40
|
+
let base: string | undefined;
|
|
41
|
+
let pr: string | undefined;
|
|
42
|
+
let repo: string | undefined;
|
|
43
|
+
let key: string | undefined;
|
|
44
|
+
let limit: number | undefined;
|
|
45
|
+
let to: Destination[] | undefined;
|
|
46
|
+
let out: string | undefined;
|
|
47
|
+
let debugPi = false;
|
|
48
|
+
const value = (i: number, flag: string): string => {
|
|
49
|
+
const v = rest[i + 1];
|
|
50
|
+
if (v === undefined || v.startsWith("--")) throw usage(`${flag} needs a value`);
|
|
51
|
+
return v;
|
|
52
|
+
};
|
|
53
|
+
for (let i = 0; i < rest.length; i++) {
|
|
54
|
+
const a = rest[i]!;
|
|
55
|
+
if (a === "--help" || a === "-h") help = true;
|
|
56
|
+
else if (a === "--preview") preview = true;
|
|
57
|
+
else if (a === "--full") full = true;
|
|
58
|
+
else if (a === "--debug-pi") debugPi = true;
|
|
59
|
+
else if (a === "--base") base = value(i++, a);
|
|
60
|
+
else if (a === "--diff") {
|
|
61
|
+
// "-" is stdin; a file whose name begins with "-" is written ./-name
|
|
62
|
+
const v = rest[i + 1];
|
|
63
|
+
if (v === undefined || (v !== "-" && v.startsWith("-")))
|
|
64
|
+
throw usage("--diff needs a file, or - for stdin");
|
|
65
|
+
diff = v;
|
|
66
|
+
i++;
|
|
67
|
+
} else if (a === "--repo") repo = value(i++, a);
|
|
68
|
+
else if (a === "--key") key = value(i++, a);
|
|
69
|
+
else if (a === "--to") {
|
|
70
|
+
try {
|
|
71
|
+
to = parseDestinations(value(i++, a));
|
|
72
|
+
} catch (err) {
|
|
73
|
+
throw usage(err instanceof Error ? err.message : String(err));
|
|
74
|
+
}
|
|
75
|
+
} else if (a === "--out") out = value(i++, a);
|
|
76
|
+
else if (a === "--limit") {
|
|
77
|
+
const n = Number(value(i++, a));
|
|
78
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
79
|
+
throw usage("--limit must be a positive number of dollars");
|
|
80
|
+
limit = n;
|
|
81
|
+
} else if (a === "branch") continue;
|
|
82
|
+
else if (a === "--uncommitted") uncommitted = true;
|
|
83
|
+
else if (/^#?\d+$/.test(a) || /^https?:\/\//.test(a)) {
|
|
84
|
+
if (pr !== undefined) throw usage("one PR at a time");
|
|
85
|
+
pr = a;
|
|
86
|
+
} else throw usage(`unknown argument ${a}`);
|
|
87
|
+
}
|
|
88
|
+
if (help) return { help: true };
|
|
89
|
+
if (uncommitted && base !== undefined)
|
|
90
|
+
throw usage("--uncommitted reviews against HEAD; --base does not apply");
|
|
91
|
+
if (uncommitted && rest.includes("branch"))
|
|
92
|
+
throw usage("choose one target: branch or --uncommitted");
|
|
93
|
+
if (uncommitted && pr !== undefined) throw usage("choose one target: --uncommitted or a PR");
|
|
94
|
+
if (diff !== undefined) {
|
|
95
|
+
if (uncommitted || pr !== undefined || rest.includes("branch"))
|
|
96
|
+
throw usage("choose one target: branch, --uncommitted, --diff or a PR");
|
|
97
|
+
if (base !== undefined) throw usage("--diff carries its own changes; --base does not apply");
|
|
98
|
+
if (repo !== undefined) throw usage("--repo goes with a PR number or URL");
|
|
99
|
+
}
|
|
100
|
+
if (debugPi && preview) throw usage("--debug-pi runs a round; a preview starts nothing");
|
|
101
|
+
if (debugPi && to?.includes("pr")) throw usage("a debug round posts nothing: drop --to pr");
|
|
102
|
+
if (pr !== undefined && base !== undefined) throw usage("--base applies to a branch, not a PR");
|
|
103
|
+
if (pr === undefined && repo !== undefined) throw usage("--repo goes with a PR number or URL");
|
|
104
|
+
return {
|
|
105
|
+
help: false,
|
|
106
|
+
uncommitted,
|
|
107
|
+
...(diff !== undefined ? { diff } : {}),
|
|
108
|
+
...(pr !== undefined ? { pr } : {}),
|
|
109
|
+
...(repo !== undefined ? { repo } : {}),
|
|
110
|
+
preview,
|
|
111
|
+
full,
|
|
112
|
+
debugPi,
|
|
113
|
+
...(base !== undefined ? { base } : {}),
|
|
114
|
+
...(key !== undefined ? { key } : {}),
|
|
115
|
+
...(limit !== undefined ? { limit } : {}),
|
|
116
|
+
...(to !== undefined ? { to } : {}),
|
|
117
|
+
...(out !== undefined ? { out } : {}),
|
|
118
|
+
};
|
|
119
|
+
}
|
package/src/reviewcli.ts
CHANGED
|
@@ -24,8 +24,7 @@
|
|
|
24
24
|
// stopped short (partial); 2 usage.
|
|
25
25
|
import { existsSync, openSync, readFileSync, writeSync } from "node:fs";
|
|
26
26
|
import { homedir } from "node:os";
|
|
27
|
-
import {
|
|
28
|
-
import { fileURLToPath } from "node:url";
|
|
27
|
+
import { join, resolve } from "node:path";
|
|
29
28
|
import type { CoreConfig } from "@quickchatai/pi-quickreview/src/reviewconfig.js";
|
|
30
29
|
import type { Ecosystem } from "@quickchatai/pi-quickreview/src/ecosystem.js";
|
|
31
30
|
import type { ReviewMemoryPort } from "@quickchatai/pi-quickreview/src/memoryport.js";
|
|
@@ -34,7 +33,6 @@ import type { PrCredentialDeps, PrFetchRequest } from "@quickchatai/pi-quickrevi
|
|
|
34
33
|
import {
|
|
35
34
|
FALLBACK_DESTINATIONS,
|
|
36
35
|
loadDefaultDestinationsIn,
|
|
37
|
-
parseDestinations,
|
|
38
36
|
planDestinations,
|
|
39
37
|
saveDefaultDestinationsIn,
|
|
40
38
|
type Destination,
|
|
@@ -64,9 +62,9 @@ import {
|
|
|
64
62
|
type ReviewTarget,
|
|
65
63
|
} from "@quickchatai/pi-quickreview/src/reviewapi.js";
|
|
66
64
|
import { reviewHome } from "./reviewhome.js";
|
|
65
|
+
import { parseReviewArgs, REVIEW_USAGE, type ReviewCommand } from "./reviewargs.js";
|
|
67
66
|
|
|
68
|
-
export
|
|
69
|
-
"usage: qcr review start|status|stop (detached runs, see `qcr review start --help`) | qcr review [branch | --uncommitted | --diff FILE|- | <PR number|URL>] [--repo OWNER/NAME] [--base REF] [--preview] [--key K] [--limit USD] [--full] [--to DEST] [--out FILE] [--debug-pi] [--json] | --help";
|
|
67
|
+
export { parseReviewArgs, REVIEW_USAGE, type ReviewCommand } from "./reviewargs.js";
|
|
70
68
|
|
|
71
69
|
export function reviewHelp(): string {
|
|
72
70
|
return [
|
|
@@ -130,117 +128,6 @@ export function reviewHelp(): string {
|
|
|
130
128
|
].join("\n");
|
|
131
129
|
}
|
|
132
130
|
|
|
133
|
-
export type ReviewCommand =
|
|
134
|
-
| { help: true }
|
|
135
|
-
| {
|
|
136
|
-
help: false;
|
|
137
|
-
uncommitted: boolean;
|
|
138
|
-
/** `--diff`: a file to read the diff from, or "-" for stdin. */
|
|
139
|
-
diff?: string;
|
|
140
|
-
pr?: string;
|
|
141
|
-
repo?: string;
|
|
142
|
-
base?: string;
|
|
143
|
-
preview: boolean;
|
|
144
|
-
key?: string;
|
|
145
|
-
limit?: number;
|
|
146
|
-
full: boolean;
|
|
147
|
-
to?: Destination[];
|
|
148
|
-
out?: string;
|
|
149
|
-
/** `--debug-pi`: the round runs inside interactive pi. */
|
|
150
|
-
debugPi: boolean;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const usage = (why?: string): Error => new Error(why ? `${REVIEW_USAGE}\n(${why})` : REVIEW_USAGE);
|
|
154
|
-
|
|
155
|
-
/** `qcr review`'s arguments (`--json` taken off before). An unknown token is
|
|
156
|
-
* refused, so a mistyped flag never runs something else. */
|
|
157
|
-
export function parseReviewArgs(rest: readonly string[]): ReviewCommand {
|
|
158
|
-
let help = false;
|
|
159
|
-
let uncommitted = false;
|
|
160
|
-
let diff: string | undefined;
|
|
161
|
-
let preview = false;
|
|
162
|
-
let full = false;
|
|
163
|
-
let base: string | undefined;
|
|
164
|
-
let pr: string | undefined;
|
|
165
|
-
let repo: string | undefined;
|
|
166
|
-
let key: string | undefined;
|
|
167
|
-
let limit: number | undefined;
|
|
168
|
-
let to: Destination[] | undefined;
|
|
169
|
-
let out: string | undefined;
|
|
170
|
-
let debugPi = false;
|
|
171
|
-
const value = (i: number, flag: string): string => {
|
|
172
|
-
const v = rest[i + 1];
|
|
173
|
-
if (v === undefined || v.startsWith("--")) throw usage(`${flag} needs a value`);
|
|
174
|
-
return v;
|
|
175
|
-
};
|
|
176
|
-
for (let i = 0; i < rest.length; i++) {
|
|
177
|
-
const a = rest[i]!;
|
|
178
|
-
if (a === "--help" || a === "-h") help = true;
|
|
179
|
-
else if (a === "--preview") preview = true;
|
|
180
|
-
else if (a === "--full") full = true;
|
|
181
|
-
else if (a === "--debug-pi") debugPi = true;
|
|
182
|
-
else if (a === "--base") base = value(i++, a);
|
|
183
|
-
else if (a === "--diff") {
|
|
184
|
-
// "-" is stdin; a file whose name begins with "-" is written ./-name
|
|
185
|
-
const v = rest[i + 1];
|
|
186
|
-
if (v === undefined || (v !== "-" && v.startsWith("-")))
|
|
187
|
-
throw usage("--diff needs a file, or - for stdin");
|
|
188
|
-
diff = v;
|
|
189
|
-
i++;
|
|
190
|
-
} else if (a === "--repo") repo = value(i++, a);
|
|
191
|
-
else if (a === "--key") key = value(i++, a);
|
|
192
|
-
else if (a === "--to") {
|
|
193
|
-
try {
|
|
194
|
-
to = parseDestinations(value(i++, a));
|
|
195
|
-
} catch (err) {
|
|
196
|
-
throw usage(err instanceof Error ? err.message : String(err));
|
|
197
|
-
}
|
|
198
|
-
} else if (a === "--out") out = value(i++, a);
|
|
199
|
-
else if (a === "--limit") {
|
|
200
|
-
const n = Number(value(i++, a));
|
|
201
|
-
if (!Number.isFinite(n) || n <= 0)
|
|
202
|
-
throw usage("--limit must be a positive number of dollars");
|
|
203
|
-
limit = n;
|
|
204
|
-
} else if (a === "branch") continue;
|
|
205
|
-
else if (a === "--uncommitted") uncommitted = true;
|
|
206
|
-
else if (/^#?\d+$/.test(a) || /^https?:\/\//.test(a)) {
|
|
207
|
-
if (pr !== undefined) throw usage("one PR at a time");
|
|
208
|
-
pr = a;
|
|
209
|
-
} else throw usage(`unknown argument ${a}`);
|
|
210
|
-
}
|
|
211
|
-
if (help) return { help: true };
|
|
212
|
-
if (uncommitted && base !== undefined)
|
|
213
|
-
throw usage("--uncommitted reviews against HEAD; --base does not apply");
|
|
214
|
-
if (uncommitted && rest.includes("branch"))
|
|
215
|
-
throw usage("choose one target: branch or --uncommitted");
|
|
216
|
-
if (uncommitted && pr !== undefined) throw usage("choose one target: --uncommitted or a PR");
|
|
217
|
-
if (diff !== undefined) {
|
|
218
|
-
if (uncommitted || pr !== undefined || rest.includes("branch"))
|
|
219
|
-
throw usage("choose one target: branch, --uncommitted, --diff or a PR");
|
|
220
|
-
if (base !== undefined) throw usage("--diff carries its own changes; --base does not apply");
|
|
221
|
-
if (repo !== undefined) throw usage("--repo goes with a PR number or URL");
|
|
222
|
-
}
|
|
223
|
-
if (debugPi && preview) throw usage("--debug-pi runs a round; a preview starts nothing");
|
|
224
|
-
if (debugPi && to?.includes("pr")) throw usage("a debug round posts nothing: drop --to pr");
|
|
225
|
-
if (pr !== undefined && base !== undefined) throw usage("--base applies to a branch, not a PR");
|
|
226
|
-
if (pr === undefined && repo !== undefined) throw usage("--repo goes with a PR number or URL");
|
|
227
|
-
return {
|
|
228
|
-
help: false,
|
|
229
|
-
uncommitted,
|
|
230
|
-
...(diff !== undefined ? { diff } : {}),
|
|
231
|
-
...(pr !== undefined ? { pr } : {}),
|
|
232
|
-
...(repo !== undefined ? { repo } : {}),
|
|
233
|
-
preview,
|
|
234
|
-
full,
|
|
235
|
-
debugPi,
|
|
236
|
-
...(base !== undefined ? { base } : {}),
|
|
237
|
-
...(key !== undefined ? { key } : {}),
|
|
238
|
-
...(limit !== undefined ? { limit } : {}),
|
|
239
|
-
...(to !== undefined ? { to } : {}),
|
|
240
|
-
...(out !== undefined ? { out } : {}),
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
131
|
export type { ReviewRoute };
|
|
245
132
|
|
|
246
133
|
/** The route a process runs on: the Action sets QCR_ROUTE=action (or runs under
|
|
@@ -465,54 +352,6 @@ export function containerRunEnv(
|
|
|
465
352
|
return { id, name };
|
|
466
353
|
}
|
|
467
354
|
|
|
468
|
-
/** The CLI entry the detached driver re-runs: `cli` beside this module. Read
|
|
469
|
-
* from where this module is, not from `process.argv[1]`, which is whatever the
|
|
470
|
-
* person launched (a bin symlink, a wrapper, a test runner). */
|
|
471
|
-
export function detachedEntry(metaUrl: string = import.meta.url): string {
|
|
472
|
-
return fileURLToPath(new URL(`./cli${extname(fileURLToPath(metaUrl))}`, metaUrl));
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/** The process `qcr review start` detaches: this node, this entry, the run's
|
|
476
|
-
* directory and key, the review's own arguments. */
|
|
477
|
-
export function detachedCommand(
|
|
478
|
-
l: { dir: string; key: string; argv: readonly string[] },
|
|
479
|
-
o: { entry?: string; execPath?: string; execArgv?: readonly string[] } = {},
|
|
480
|
-
): { command: string; args: string[] } {
|
|
481
|
-
return {
|
|
482
|
-
command: o.execPath ?? process.execPath,
|
|
483
|
-
args: [
|
|
484
|
-
...(o.execArgv ?? process.execArgv),
|
|
485
|
-
o.entry ?? detachedEntry(),
|
|
486
|
-
"review",
|
|
487
|
-
"--detached-run",
|
|
488
|
-
l.dir,
|
|
489
|
-
l.key,
|
|
490
|
-
...l.argv,
|
|
491
|
-
"--json",
|
|
492
|
-
],
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/** The watchdog `qcr review start` detaches beside the run: this node, this
|
|
497
|
-
* entry, the run's directory, key and driver pid. */
|
|
498
|
-
export function watchdogCommand(
|
|
499
|
-
w: { dir: string; key: string; pid: number },
|
|
500
|
-
o: { entry?: string; execPath?: string; execArgv?: readonly string[] } = {},
|
|
501
|
-
): { command: string; args: string[] } {
|
|
502
|
-
return {
|
|
503
|
-
command: o.execPath ?? process.execPath,
|
|
504
|
-
args: [
|
|
505
|
-
...(o.execArgv ?? process.execArgv),
|
|
506
|
-
o.entry ?? detachedEntry(),
|
|
507
|
-
"review",
|
|
508
|
-
"--watchdog",
|
|
509
|
-
w.dir,
|
|
510
|
-
w.key,
|
|
511
|
-
String(w.pid),
|
|
512
|
-
],
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
|
|
516
355
|
/** The runtime `[engine] runtime` selects for `qcr review`: the SDK runtime.
|
|
517
356
|
* `[pi] lens_runtime = engine` is
|
|
518
357
|
* the SDK runtime with our lens tools; with `[pi] engine_wave` the runtime
|
|
@@ -578,13 +417,13 @@ export async function cmdReview(
|
|
|
578
417
|
container !== undefined
|
|
579
418
|
? { ok: true, pid: null, container: container.name }
|
|
580
419
|
: det.launchDetached({
|
|
581
|
-
...detachedCommand(l),
|
|
420
|
+
...det.detachedCommand(l),
|
|
582
421
|
dir: l.dir,
|
|
583
422
|
env: process.env,
|
|
584
423
|
}),
|
|
585
424
|
watch: (w) => {
|
|
586
425
|
det.launchDetached({
|
|
587
|
-
...watchdogCommand(w),
|
|
426
|
+
...det.watchdogCommand(w),
|
|
588
427
|
dir: w.dir,
|
|
589
428
|
env: process.env,
|
|
590
429
|
log: "watchdog.log",
|
|
@@ -700,7 +539,7 @@ export async function cmdReview(
|
|
|
700
539
|
// A person's run needs a credential for every provider the review's
|
|
701
540
|
// models name; a key for another provider never stands in for one.
|
|
702
541
|
const { reviewLoginRefusal } = await import("./loginhint.js");
|
|
703
|
-
const { reviewProviders } = await import("@quickchatai/pi-quickreview/src/
|
|
542
|
+
const { reviewProviders } = await import("@quickchatai/pi-quickreview/src/modelroute.js");
|
|
704
543
|
const problem = reviewLoginRefusal({
|
|
705
544
|
providers: reviewProviders(cfg),
|
|
706
545
|
agentDir,
|
package/src/reviewdetach.ts
CHANGED
|
@@ -43,7 +43,8 @@ import {
|
|
|
43
43
|
rmSync,
|
|
44
44
|
writeFileSync,
|
|
45
45
|
} from "node:fs";
|
|
46
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
46
|
+
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
47
|
+
import { fileURLToPath } from "node:url";
|
|
47
48
|
import { stopLiveSessions } from "@quickchatai/pi-quickreview/src/engine/livesessions.js";
|
|
48
49
|
import {
|
|
49
50
|
HEARTBEAT_FILE,
|
|
@@ -55,9 +56,10 @@ import {
|
|
|
55
56
|
resolveBranch,
|
|
56
57
|
resolveUncommitted,
|
|
57
58
|
} from "@quickchatai/pi-quickreview/src/localreview.js";
|
|
58
|
-
import { latestLiveRound, viewRound } from "
|
|
59
|
+
import { latestLiveRound, viewRound } from "@quickchatai/pi-quickreview/src/live/read.js";
|
|
60
|
+
import { lensActivityLine, lensElapsedMs } from "@quickchatai/pi-quickreview/src/live/lensline.js";
|
|
59
61
|
import { parsePrTarget } from "@quickchatai/pi-quickreview/src/prtarget.js";
|
|
60
|
-
import { parseReviewArgs } from "./
|
|
62
|
+
import { parseReviewArgs } from "./reviewargs.js";
|
|
61
63
|
import {
|
|
62
64
|
REVIEW_SCHEMA_VERSION,
|
|
63
65
|
withSchemaVersion,
|
|
@@ -234,6 +236,9 @@ interface Observed {
|
|
|
234
236
|
dir: string;
|
|
235
237
|
state: RunState;
|
|
236
238
|
exitCode: number | null;
|
|
239
|
+
/** When it ended, ms since the epoch: its exit's or its stop's time (a run
|
|
240
|
+
* recorded before the exit carried one has none). */
|
|
241
|
+
endedAt: number | null;
|
|
237
242
|
result: unknown;
|
|
238
243
|
}
|
|
239
244
|
|
|
@@ -243,8 +248,9 @@ function observe(home: string, runId: string, deps: Liveness): Observed | null {
|
|
|
243
248
|
const dir = join(runsDir(home), runId);
|
|
244
249
|
const run = readJson<RunRecord>(join(dir, "run.json"));
|
|
245
250
|
if (run === null) return null;
|
|
246
|
-
const exit = readJson<{ code: number }>(join(dir, "exit.json"));
|
|
247
|
-
const
|
|
251
|
+
const exit = readJson<{ code: number; at?: number }>(join(dir, "exit.json"));
|
|
252
|
+
const stoppedAt = readJson<{ at?: number }>(join(dir, "stopped.json"));
|
|
253
|
+
const stopped = stoppedAt !== null || existsSync(join(dir, "stopped.json"));
|
|
248
254
|
let state: RunState;
|
|
249
255
|
// A run that was stopped and unwinds records a failing exit of its own on the
|
|
250
256
|
// way out (an aborted turn has no result); `stop` is what ended it, so that
|
|
@@ -268,7 +274,15 @@ function observe(home: string, runId: string, deps: Liveness): Observed | null {
|
|
|
268
274
|
} catch {
|
|
269
275
|
// no output yet
|
|
270
276
|
}
|
|
271
|
-
|
|
277
|
+
const at = (v: unknown): number | null =>
|
|
278
|
+
typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
279
|
+
const endedAt =
|
|
280
|
+
state === "running"
|
|
281
|
+
? null
|
|
282
|
+
: state === "stopped"
|
|
283
|
+
? (at(stoppedAt?.at) ?? at(exit?.at))
|
|
284
|
+
: at(exit?.at);
|
|
285
|
+
return { run, dir, state, exitCode: exit?.code ?? null, endedAt, result };
|
|
272
286
|
}
|
|
273
287
|
|
|
274
288
|
function allRuns(home: string, deps: Liveness): Observed[] {
|
|
@@ -285,7 +299,11 @@ function allRuns(home: string, deps: Liveness): Observed[] {
|
|
|
285
299
|
}
|
|
286
300
|
|
|
287
301
|
/** What a running review is doing, from its LiveEvent log projected to
|
|
288
|
-
* round-progress v2 (src/live/): the same
|
|
302
|
+
* round-progress v2 (packages/pi-plugin/src/live/projection.ts): the same
|
|
303
|
+
* snapshot the bot's supervisor reads. Each lens carries what it is doing as
|
|
304
|
+
* it happens: its tool count, its current tool and the path that tool is on,
|
|
305
|
+
* and its time so far (a running lens's counts on from its start between
|
|
306
|
+
* updates). `line` is the same entry as one line a person reads. */
|
|
289
307
|
interface LiveProgress {
|
|
290
308
|
round: number;
|
|
291
309
|
parent_status: "active" | "settled";
|
|
@@ -298,6 +316,8 @@ interface LiveProgress {
|
|
|
298
316
|
duration_ms: number;
|
|
299
317
|
tool_count: number;
|
|
300
318
|
current_tool?: string;
|
|
319
|
+
current_target?: string;
|
|
320
|
+
line: string;
|
|
301
321
|
}>;
|
|
302
322
|
}
|
|
303
323
|
|
|
@@ -308,7 +328,7 @@ interface Progress {
|
|
|
308
328
|
|
|
309
329
|
/** How far a run got: the lenses its stage record names (written when the round
|
|
310
330
|
* ends) and, while it runs, its live log. */
|
|
311
|
-
function progress(dir: string): Progress | null {
|
|
331
|
+
function progress(dir: string, now: number): Progress | null {
|
|
312
332
|
const work = join(dir, "work");
|
|
313
333
|
let dirs: string[] = [];
|
|
314
334
|
try {
|
|
@@ -342,9 +362,11 @@ function progress(dir: string): Progress | null {
|
|
|
342
362
|
agents: snapshot.agents.map((a) => ({
|
|
343
363
|
agent: a.agent,
|
|
344
364
|
status: a.status,
|
|
345
|
-
duration_ms: a.duration_ms,
|
|
365
|
+
duration_ms: lensElapsedMs(a, now) ?? a.duration_ms,
|
|
346
366
|
tool_count: a.tool_count,
|
|
347
367
|
...(a.current_tool ? { current_tool: a.current_tool } : {}),
|
|
368
|
+
...(a.current_target ? { current_target: a.current_target } : {}),
|
|
369
|
+
line: lensActivityLine(a, { nowMs: now }),
|
|
348
370
|
})),
|
|
349
371
|
};
|
|
350
372
|
}
|
|
@@ -358,9 +380,11 @@ function view(o: Observed, now: number): Record<string, unknown> {
|
|
|
358
380
|
run_id: o.run.run_id,
|
|
359
381
|
state: o.state,
|
|
360
382
|
target: o.run.label,
|
|
361
|
-
|
|
383
|
+
started_at: o.run.started_at,
|
|
384
|
+
...(o.endedAt !== null ? { ended_at: o.endedAt } : {}),
|
|
385
|
+
elapsed_s: Math.max(0, Math.round(((o.endedAt ?? now) - o.run.started_at) / 1000)),
|
|
362
386
|
exit_code: o.exitCode,
|
|
363
|
-
...(o.state === "running" ? { progress: progress(o.dir) } : { result: o.result }),
|
|
387
|
+
...(o.state === "running" ? { progress: progress(o.dir, now) } : { result: o.result }),
|
|
364
388
|
...(o.state === "failed" && o.exitCode === null
|
|
365
389
|
? { errors: [`the review process ended without a result; see ${join(o.dir, "driver.log")}`] }
|
|
366
390
|
: {}),
|
|
@@ -378,6 +402,7 @@ function text(v: Record<string, unknown>): string {
|
|
|
378
402
|
const names = by(s);
|
|
379
403
|
if (names.length > 0) bits.push(`${s}: ${names.join(", ")}`);
|
|
380
404
|
}
|
|
405
|
+
for (const a of p.live.agents) if (a.status === "running") bits.push(` ${a.line}`);
|
|
381
406
|
bits.push(`now: ${p.live.last_activity}`);
|
|
382
407
|
}
|
|
383
408
|
const errs = v["errors"] as string[] | undefined;
|
|
@@ -385,6 +410,24 @@ function text(v: Record<string, unknown>): string {
|
|
|
385
410
|
return bits.join("\n");
|
|
386
411
|
}
|
|
387
412
|
|
|
413
|
+
/** One run as `status --json` reports it, with the review's own arguments. */
|
|
414
|
+
export interface RunView {
|
|
415
|
+
view: Record<string, unknown>;
|
|
416
|
+
argv: readonly string[];
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** Every run in the registry at `home`, oldest first, each as `status`
|
|
420
|
+
* reports it: what a host re-attaches to after a restart. */
|
|
421
|
+
export function listRunViews(home: string, deps: Liveness): RunView[] {
|
|
422
|
+
return allRuns(home, deps).map((o) => ({ view: view(o, deps.now()), argv: o.run.argv }));
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/** One run as `status` reports it; null when there is no such run. */
|
|
426
|
+
export function runView(home: string, runId: string, deps: Liveness): RunView | null {
|
|
427
|
+
const o = observe(home, runId, deps);
|
|
428
|
+
return o === null ? null : { view: view(o, deps.now()), argv: o.run.argv };
|
|
429
|
+
}
|
|
430
|
+
|
|
388
431
|
/** Run one `start | status | stop`; resolves its exit code. */
|
|
389
432
|
export async function runDetachedVerb(
|
|
390
433
|
verb: "start" | "status" | "stop",
|
|
@@ -652,7 +695,7 @@ export async function runDetachedChild(o: {
|
|
|
652
695
|
proc.off("SIGINT", onSignal);
|
|
653
696
|
proc.off("SIGTERM", onSignal);
|
|
654
697
|
}
|
|
655
|
-
writeFileSync(join(o.dir, "exit.json"), JSON.stringify({ code }));
|
|
698
|
+
writeFileSync(join(o.dir, "exit.json"), JSON.stringify({ code, at: Date.now() }));
|
|
656
699
|
// only this run's own claim: a stop frees the lock for a newer run
|
|
657
700
|
const lock = lockPath(dirname(dirname(o.dir)), o.key);
|
|
658
701
|
if (readJson<{ run_id?: string }>(lock)?.run_id === basename(o.dir))
|
|
@@ -725,10 +768,61 @@ export async function runWatchdog(
|
|
|
725
768
|
}),
|
|
726
769
|
)}\n`,
|
|
727
770
|
);
|
|
728
|
-
writeFileSync(
|
|
771
|
+
writeFileSync(
|
|
772
|
+
join(w.dir, "exit.json"),
|
|
773
|
+
JSON.stringify({ code: WATCHDOG_EXIT_CODE, at: deps.now() }),
|
|
774
|
+
);
|
|
729
775
|
rmSync(lockPath(dirname(dirname(w.dir)), w.key), { force: true });
|
|
730
776
|
return "killed";
|
|
731
777
|
}
|
|
732
778
|
await deps.sleep(deps.pollMs ?? 2_000);
|
|
733
779
|
}
|
|
734
780
|
}
|
|
781
|
+
|
|
782
|
+
/** The CLI entry the detached driver re-runs: `cli` beside this module. Read
|
|
783
|
+
* from where this module is, not from `process.argv[1]`, which is whatever the
|
|
784
|
+
* person launched (a bin symlink, a wrapper, a test runner). */
|
|
785
|
+
export function detachedEntry(metaUrl: string = import.meta.url): string {
|
|
786
|
+
return fileURLToPath(new URL(`./cli${extname(fileURLToPath(metaUrl))}`, metaUrl));
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/** The process `qcr review start` detaches: this node, this entry, the run's
|
|
790
|
+
* directory and key, the review's own arguments. */
|
|
791
|
+
export function detachedCommand(
|
|
792
|
+
l: { dir: string; key: string; argv: readonly string[] },
|
|
793
|
+
o: { entry?: string; execPath?: string; execArgv?: readonly string[] } = {},
|
|
794
|
+
): { command: string; args: string[] } {
|
|
795
|
+
return {
|
|
796
|
+
command: o.execPath ?? process.execPath,
|
|
797
|
+
args: [
|
|
798
|
+
...(o.execArgv ?? process.execArgv),
|
|
799
|
+
o.entry ?? detachedEntry(),
|
|
800
|
+
"review",
|
|
801
|
+
"--detached-run",
|
|
802
|
+
l.dir,
|
|
803
|
+
l.key,
|
|
804
|
+
...l.argv,
|
|
805
|
+
"--json",
|
|
806
|
+
],
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/** The watchdog `qcr review start` detaches beside the run: this node, this
|
|
811
|
+
* entry, the run's directory, key and driver pid. */
|
|
812
|
+
export function watchdogCommand(
|
|
813
|
+
w: { dir: string; key: string; pid: number },
|
|
814
|
+
o: { entry?: string; execPath?: string; execArgv?: readonly string[] } = {},
|
|
815
|
+
): { command: string; args: string[] } {
|
|
816
|
+
return {
|
|
817
|
+
command: o.execPath ?? process.execPath,
|
|
818
|
+
args: [
|
|
819
|
+
...(o.execArgv ?? process.execArgv),
|
|
820
|
+
o.entry ?? detachedEntry(),
|
|
821
|
+
"review",
|
|
822
|
+
"--watchdog",
|
|
823
|
+
w.dir,
|
|
824
|
+
w.key,
|
|
825
|
+
String(w.pid),
|
|
826
|
+
],
|
|
827
|
+
};
|
|
828
|
+
}
|