autor3search-typescript 0.1.0
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/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/benchproto/types.d.ts +32 -0
- package/dist/benchproto/types.js +28 -0
- package/dist/benchproto/types.js.map +1 -0
- package/dist/benchrun/child.d.ts +1 -0
- package/dist/benchrun/child.js +222 -0
- package/dist/benchrun/child.js.map +1 -0
- package/dist/benchrun/invoke.d.ts +61 -0
- package/dist/benchrun/invoke.js +152 -0
- package/dist/benchrun/invoke.js.map +1 -0
- package/dist/cli/cmd-baseline.d.ts +15 -0
- package/dist/cli/cmd-baseline.js +292 -0
- package/dist/cli/cmd-baseline.js.map +1 -0
- package/dist/cli/cmd-doctor.d.ts +11 -0
- package/dist/cli/cmd-doctor.js +26 -0
- package/dist/cli/cmd-doctor.js.map +1 -0
- package/dist/cli/cmd-eval.d.ts +30 -0
- package/dist/cli/cmd-eval.js +173 -0
- package/dist/cli/cmd-eval.js.map +1 -0
- package/dist/cli/cmd-init.d.ts +16 -0
- package/dist/cli/cmd-init.js +343 -0
- package/dist/cli/cmd-init.js.map +1 -0
- package/dist/cli/cmd-profile.d.ts +11 -0
- package/dist/cli/cmd-profile.js +140 -0
- package/dist/cli/cmd-profile.js.map +1 -0
- package/dist/cli/cmd-report.d.ts +15 -0
- package/dist/cli/cmd-report.js +70 -0
- package/dist/cli/cmd-report.js.map +1 -0
- package/dist/cli/cmd-status.d.ts +11 -0
- package/dist/cli/cmd-status.js +143 -0
- package/dist/cli/cmd-status.js.map +1 -0
- package/dist/cli/cmd-stop.d.ts +15 -0
- package/dist/cli/cmd-stop.js +143 -0
- package/dist/cli/cmd-stop.js.map +1 -0
- package/dist/cli/main.d.ts +31 -0
- package/dist/cli/main.js +133 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/runctx.d.ts +29 -0
- package/dist/cli/runctx.js +49 -0
- package/dist/cli/runctx.js.map +1 -0
- package/dist/cli/speedup.d.ts +14 -0
- package/dist/cli/speedup.js +25 -0
- package/dist/cli/speedup.js.map +1 -0
- package/dist/config/load.d.ts +6 -0
- package/dist/config/load.js +172 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/schema.d.ts +42 -0
- package/dist/config/schema.js +25 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/discover/benchmarks.d.ts +26 -0
- package/dist/discover/benchmarks.js +88 -0
- package/dist/discover/benchmarks.js.map +1 -0
- package/dist/discover/files.d.ts +23 -0
- package/dist/discover/files.js +82 -0
- package/dist/discover/files.js.map +1 -0
- package/dist/doctor/doctor.d.ts +56 -0
- package/dist/doctor/doctor.js +255 -0
- package/dist/doctor/doctor.js.map +1 -0
- package/dist/freeze/freeze.d.ts +23 -0
- package/dist/freeze/freeze.js +107 -0
- package/dist/freeze/freeze.js.map +1 -0
- package/dist/freeze/manifest.d.ts +6 -0
- package/dist/freeze/manifest.js +9 -0
- package/dist/freeze/manifest.js.map +1 -0
- package/dist/freeze/safepath.d.ts +8 -0
- package/dist/freeze/safepath.js +22 -0
- package/dist/freeze/safepath.js.map +1 -0
- package/dist/gitx/git.d.ts +87 -0
- package/dist/gitx/git.js +235 -0
- package/dist/gitx/git.js.map +1 -0
- package/dist/measure/interleave.d.ts +32 -0
- package/dist/measure/interleave.js +42 -0
- package/dist/measure/interleave.js.map +1 -0
- package/dist/pipeline/eval.d.ts +67 -0
- package/dist/pipeline/eval.js +495 -0
- package/dist/pipeline/eval.js.map +1 -0
- package/dist/pm/detect.d.ts +17 -0
- package/dist/pm/detect.js +131 -0
- package/dist/pm/detect.js.map +1 -0
- package/dist/profile/profile.d.ts +53 -0
- package/dist/profile/profile.js +105 -0
- package/dist/profile/profile.js.map +1 -0
- package/dist/results/results.d.ts +65 -0
- package/dist/results/results.js +214 -0
- package/dist/results/results.js.map +1 -0
- package/dist/runner/exec.d.ts +69 -0
- package/dist/runner/exec.js +185 -0
- package/dist/runner/exec.js.map +1 -0
- package/dist/scope/scope.d.ts +20 -0
- package/dist/scope/scope.js +70 -0
- package/dist/scope/scope.js.map +1 -0
- package/dist/state/baseline.d.ts +35 -0
- package/dist/state/baseline.js +38 -0
- package/dist/state/baseline.js.map +1 -0
- package/dist/state/home.d.ts +9 -0
- package/dist/state/home.js +72 -0
- package/dist/state/home.js.map +1 -0
- package/dist/state/lock.d.ts +11 -0
- package/dist/state/lock.js +89 -0
- package/dist/state/lock.js.map +1 -0
- package/dist/state/runnaming.d.ts +27 -0
- package/dist/state/runnaming.js +47 -0
- package/dist/state/runnaming.js.map +1 -0
- package/dist/state/stop.d.ts +28 -0
- package/dist/state/stop.js +59 -0
- package/dist/state/stop.js.map +1 -0
- package/dist/stats/delta.d.ts +29 -0
- package/dist/stats/delta.js +41 -0
- package/dist/stats/delta.js.map +1 -0
- package/dist/stats/geomean.d.ts +4 -0
- package/dist/stats/geomean.js +21 -0
- package/dist/stats/geomean.js.map +1 -0
- package/dist/stats/mannwhitney.d.ts +27 -0
- package/dist/stats/mannwhitney.js +158 -0
- package/dist/stats/mannwhitney.js.map +1 -0
- package/dist/verdict/verdict.d.ts +46 -0
- package/dist/verdict/verdict.js +102 -0
- package/dist/verdict/verdict.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +35 -0
- package/templates/program.md +131 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { loadConfig, parseDuration } from '../config/load.js';
|
|
4
|
+
import { CONFIG_PATH } from '../config/schema.js';
|
|
5
|
+
import { discoverBenchmarks } from '../discover/benchmarks.js';
|
|
6
|
+
import { profileBenchmark } from '../profile/profile.js';
|
|
7
|
+
/** How many hot functions to print per benchmark. */
|
|
8
|
+
const TOP_N = 15;
|
|
9
|
+
/**
|
|
10
|
+
* Repo-relative directory `.cpuprofile` files are written under. Alongside
|
|
11
|
+
* `config.yaml`, not in the (baseline-only) state home: `profile` must work
|
|
12
|
+
* without a baseline ever existing, and the output is meant for a human to
|
|
13
|
+
* open locally, not for the harness's own cross-run state.
|
|
14
|
+
*/
|
|
15
|
+
const PROFILE_DIRNAME = path.join('.autor3search', 'profiles');
|
|
16
|
+
function fail(message) {
|
|
17
|
+
process.stderr.write(`error: ${message}\n`);
|
|
18
|
+
return 2;
|
|
19
|
+
}
|
|
20
|
+
function messageOf(e) {
|
|
21
|
+
return e instanceof Error ? e.message : String(e);
|
|
22
|
+
}
|
|
23
|
+
/** Declared benchmark ids that are not among the discovered benchmarks. Mirrors cmd-baseline.ts. */
|
|
24
|
+
function missingDeclared(declared, discovered) {
|
|
25
|
+
const found = new Set(discovered.map((b) => b.id));
|
|
26
|
+
return declared.filter((id) => !found.has(id));
|
|
27
|
+
}
|
|
28
|
+
function printHotFrames(frames, totalSelfUs) {
|
|
29
|
+
if (frames.length === 0) {
|
|
30
|
+
process.stdout.write(' no user-code samples were captured (every sampled frame was Node internals) -- this ' +
|
|
31
|
+
'benchmark may be too fast to profile meaningfully; try a longer -benchtime.\n');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
for (const f of frames.slice(0, TOP_N)) {
|
|
35
|
+
const pct = totalSelfUs > 0 ? (f.selfTimeUs / totalSelfUs) * 100 : 0;
|
|
36
|
+
const name = f.functionName.length > 0 ? f.functionName : '(anonymous)';
|
|
37
|
+
process.stdout.write(` ${pct.toFixed(1).padStart(5)}% ${name} ${f.url}:${f.lineNumber}\n`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* `profile`: runs the declared benchmarks under Node's built-in `--cpu-prof`
|
|
42
|
+
* sampling profiler and prints where time actually goes.
|
|
43
|
+
*
|
|
44
|
+
* Deliberately does not touch `baseline` or the state home at all: this is
|
|
45
|
+
* reconnaissance a human runs BEFORE starting the loop, to decide whether
|
|
46
|
+
* the benchmarks even point at the right code, so it must work with only
|
|
47
|
+
* `.autor3search/config.yaml` in place.
|
|
48
|
+
*/
|
|
49
|
+
export async function cmdProfile(ctx, argv) {
|
|
50
|
+
let benchtimeOverride;
|
|
51
|
+
try {
|
|
52
|
+
const normalized = argv.map((a) => (/^-[A-Za-z][A-Za-z-]+$/.test(a) ? `-${a}` : a));
|
|
53
|
+
const { values } = parseArgs({
|
|
54
|
+
args: normalized,
|
|
55
|
+
options: { benchtime: { type: 'string' } },
|
|
56
|
+
strict: true,
|
|
57
|
+
allowPositionals: false,
|
|
58
|
+
});
|
|
59
|
+
benchtimeOverride = values.benchtime;
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
return fail(messageOf(e));
|
|
63
|
+
}
|
|
64
|
+
let config;
|
|
65
|
+
try {
|
|
66
|
+
config = await loadConfig(ctx.configPath);
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
return fail(messageOf(e));
|
|
70
|
+
}
|
|
71
|
+
let discovered;
|
|
72
|
+
try {
|
|
73
|
+
discovered = await discoverBenchmarks(ctx.repoRoot);
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
return fail(`could not discover benchmarks: ${messageOf(e)}`);
|
|
77
|
+
}
|
|
78
|
+
let benchmarks;
|
|
79
|
+
const declared = config.benchmarks;
|
|
80
|
+
if (declared.length > 0) {
|
|
81
|
+
const missing = missingDeclared(declared, discovered);
|
|
82
|
+
if (missing.length > 0) {
|
|
83
|
+
return fail(`config declares benchmark(s) that no longer exist: ${missing.join(', ')}. Update ` +
|
|
84
|
+
`${CONFIG_PATH} or restore the missing benchmark(s).`);
|
|
85
|
+
}
|
|
86
|
+
const byId = new Map(discovered.map((b) => [b.id, b]));
|
|
87
|
+
benchmarks = declared.map((id) => byId.get(id));
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
benchmarks = discovered;
|
|
91
|
+
}
|
|
92
|
+
if (benchmarks.length === 0) {
|
|
93
|
+
return fail('no benchmarks found: there is nothing to profile');
|
|
94
|
+
}
|
|
95
|
+
let benchtimeMs;
|
|
96
|
+
let warmupMs;
|
|
97
|
+
let timeoutMs;
|
|
98
|
+
try {
|
|
99
|
+
benchtimeMs = parseDuration(benchtimeOverride ?? config.benchtime);
|
|
100
|
+
warmupMs = parseDuration(config.warmup);
|
|
101
|
+
timeoutMs = parseDuration(config.timeout);
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
return fail(messageOf(e));
|
|
105
|
+
}
|
|
106
|
+
const profileDir = path.join(ctx.repoRoot, PROFILE_DIRNAME);
|
|
107
|
+
process.stdout.write('autor3search-typescript profile -- where does the time actually go?\n\n');
|
|
108
|
+
let failures = 0;
|
|
109
|
+
for (const b of benchmarks) {
|
|
110
|
+
process.stdout.write(`${b.id}\n`);
|
|
111
|
+
try {
|
|
112
|
+
const result = await profileBenchmark({
|
|
113
|
+
cwd: ctx.repoRoot,
|
|
114
|
+
benchFileAbs: path.join(ctx.repoRoot, b.file),
|
|
115
|
+
fn: b.fn,
|
|
116
|
+
id: b.id,
|
|
117
|
+
benchtimeMs,
|
|
118
|
+
warmupMs,
|
|
119
|
+
timeoutMs,
|
|
120
|
+
nodeArgs: config.nodeArgs,
|
|
121
|
+
profileDir,
|
|
122
|
+
});
|
|
123
|
+
printHotFrames(result.hotFrames, result.totalSelfUs);
|
|
124
|
+
process.stdout.write(` profile written to ${result.profilePath}\n`);
|
|
125
|
+
process.stdout.write(' open it in Chrome DevTools (chrome://inspect -> Open dedicated DevTools for Node -> ' +
|
|
126
|
+
'Profiler tab -> Load) or drag it into https://www.speedscope.app/\n');
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
failures++;
|
|
130
|
+
process.stderr.write(` error: ${messageOf(e)}\n`);
|
|
131
|
+
}
|
|
132
|
+
process.stdout.write('\n');
|
|
133
|
+
}
|
|
134
|
+
if (failures > 0) {
|
|
135
|
+
process.stderr.write(`${failures} of ${benchmarks.length} benchmark(s) could not be profiled\n`);
|
|
136
|
+
return 2;
|
|
137
|
+
}
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=cmd-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cmd-profile.js","sourceRoot":"","sources":["../../src/cli/cmd-profile.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,kBAAkB,EAAkB,MAAM,2BAA2B,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAiB,MAAM,uBAAuB,CAAA;AAGvE,qDAAqD;AACrD,MAAM,KAAK,GAAG,EAAE,CAAA;AAEhB;;;;;GAKG;AACH,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,UAAU,CAAC,CAAA;AAE9D,SAAS,IAAI,CAAC,OAAe;IAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,IAAI,CAAC,CAAA;IAC3C,OAAO,CAAC,CAAA;AACV,CAAC;AAED,SAAS,SAAS,CAAC,CAAU;IAC3B,OAAO,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;AACnD,CAAC;AAED,oGAAoG;AACpG,SAAS,eAAe,CAAC,QAA2B,EAAE,UAAgC;IACpF,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAClD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;AAChD,CAAC;AAED,SAAS,cAAc,CAAC,MAA2B,EAAE,WAAmB;IACtE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0FAA0F;YACxF,+EAA+E,CAClF,CAAA;QACD,OAAM;IACR,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QACpE,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAA;QACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,CAAA;IACjG,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,IAAuB;IACnE,IAAI,iBAAqC,CAAA;IACzC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACnF,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;YAC3B,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;YAC1C,MAAM,EAAE,IAAI;YACZ,gBAAgB,EAAE,KAAK;SACxB,CAAC,CAAA;QACF,iBAAiB,GAAG,MAAM,CAAC,SAAS,CAAA;IACtC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3B,CAAC;IAED,IAAI,MAA8C,CAAA;IAClD,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IAC3C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3B,CAAC;IAED,IAAI,UAAuB,CAAA;IAC3B,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACrD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,IAAI,CAAC,kCAAkC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC/D,CAAC;IAED,IAAI,UAAuB,CAAA;IAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAA;IAClC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;QACrD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,IAAI,CACT,sDAAsD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW;gBACjF,GAAG,WAAW,uCAAuC,CACxD,CAAA;QACH,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QACtD,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC,CAAA;IAClD,CAAC;SAAM,CAAC;QACN,UAAU,GAAG,UAAU,CAAA;IACzB,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC,kDAAkD,CAAC,CAAA;IACjE,CAAC;IAED,IAAI,WAAmB,CAAA;IACvB,IAAI,QAAgB,CAAA;IACpB,IAAI,SAAiB,CAAA;IACrB,IAAI,CAAC;QACH,WAAW,GAAG,aAAa,CAAC,iBAAiB,IAAI,MAAM,CAAC,SAAS,CAAC,CAAA;QAClE,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACvC,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAC3C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3B,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAA;IAE3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yEAAyE,CAAC,CAAA;IAE/F,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;QACjC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;gBACpC,GAAG,EAAE,GAAG,CAAC,QAAQ;gBACjB,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC;gBAC7C,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,WAAW;gBACX,QAAQ;gBACR,SAAS;gBACT,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,UAAU;aACX,CAAC,CAAA;YACF,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,CAAA;YACpD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,MAAM,CAAC,WAAW,IAAI,CAAC,CAAA;YACtE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0FAA0F;gBACxF,qEAAqE,CACxE,CAAA;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,QAAQ,EAAE,CAAA;YACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QACpD,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,OAAO,UAAU,CAAC,MAAM,uCAAuC,CAAC,CAAA;QAChG,OAAO,CAAC,CAAA;IACV,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RunCtx } from './runctx.js';
|
|
2
|
+
/**
|
|
3
|
+
* `report`: what a human reads in the morning after an unattended run --
|
|
4
|
+
* a summary of `results.tsv`, and nothing else. Read-only: it never writes
|
|
5
|
+
* anything, and it never spawns anything (no git, no subprocess), so it is
|
|
6
|
+
* always safe to run alongside a live loop.
|
|
7
|
+
*
|
|
8
|
+
* A missing (or header-only) `results.tsv` is a real, valid state -- a
|
|
9
|
+
* repository that has never run an experiment -- so it is reported as "no
|
|
10
|
+
* experiments recorded yet," not as an error. `loadRows` already treats a
|
|
11
|
+
* missing file as an empty log; this only needs to word the empty case
|
|
12
|
+
* helpfully instead of printing zeroed-out counts a human could misread as
|
|
13
|
+
* "something ran and did nothing."
|
|
14
|
+
*/
|
|
15
|
+
export declare function cmdReport(ctx: RunCtx, _argv: readonly string[]): Promise<number>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { shortSha } from '../gitx/git.js';
|
|
2
|
+
import { RESULTS_PATH, loadRows, summarize } from '../results/results.js';
|
|
3
|
+
import { formatCumulativeSpeedup } from './speedup.js';
|
|
4
|
+
const STATUSES = ['keep', 'discard', 'fail', 'crash'];
|
|
5
|
+
function fail(message) {
|
|
6
|
+
process.stderr.write(`error: ${message}\n`);
|
|
7
|
+
return 2;
|
|
8
|
+
}
|
|
9
|
+
function messageOf(e) {
|
|
10
|
+
return e instanceof Error ? e.message : String(e);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* `report`: what a human reads in the morning after an unattended run --
|
|
14
|
+
* a summary of `results.tsv`, and nothing else. Read-only: it never writes
|
|
15
|
+
* anything, and it never spawns anything (no git, no subprocess), so it is
|
|
16
|
+
* always safe to run alongside a live loop.
|
|
17
|
+
*
|
|
18
|
+
* A missing (or header-only) `results.tsv` is a real, valid state -- a
|
|
19
|
+
* repository that has never run an experiment -- so it is reported as "no
|
|
20
|
+
* experiments recorded yet," not as an error. `loadRows` already treats a
|
|
21
|
+
* missing file as an empty log; this only needs to word the empty case
|
|
22
|
+
* helpfully instead of printing zeroed-out counts a human could misread as
|
|
23
|
+
* "something ran and did nothing."
|
|
24
|
+
*/
|
|
25
|
+
export async function cmdReport(ctx, _argv) {
|
|
26
|
+
let rows;
|
|
27
|
+
try {
|
|
28
|
+
rows = await loadRows(ctx.resultsPath);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
return fail(messageOf(e));
|
|
32
|
+
}
|
|
33
|
+
const lines = ['autor3search-typescript report', ''];
|
|
34
|
+
if (rows.length === 0) {
|
|
35
|
+
lines.push(`no experiments recorded yet (${RESULTS_PATH} does not exist, or has no rows). Run "eval" ` +
|
|
36
|
+
'at least once to populate it.');
|
|
37
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
const summary = summarize(rows);
|
|
41
|
+
lines.push(`experiments: ${rows.length} total`);
|
|
42
|
+
lines.push(` ${STATUSES.map((s) => `${s}: ${summary.counts[s] ?? 0}`).join(' ')}`);
|
|
43
|
+
lines.push('');
|
|
44
|
+
// Deliberately spelled out rather than left implicit: each KEEP advances
|
|
45
|
+
// the measurement baseline to the just-kept commit, so every kept row's
|
|
46
|
+
// own `score` is only that experiment's incremental contribution on top
|
|
47
|
+
// of the last win -- never a re-measurement against the run's original
|
|
48
|
+
// starting point. Only the PRODUCT of every kept score is the true
|
|
49
|
+
// end-to-end speedup since the run began; printing (or reading) the
|
|
50
|
+
// latest row's score alone would silently understate or overstate that,
|
|
51
|
+
// depending on how many wins came before it.
|
|
52
|
+
lines.push(`cumulative speedup: ${formatCumulativeSpeedup(summary.cumulativeSpeedup)} ` +
|
|
53
|
+
"(the product of every KEPT experiment's own score, compounded across the whole run -- " +
|
|
54
|
+
"not the most recent experiment's score by itself)");
|
|
55
|
+
if (summary.topWins.length > 0) {
|
|
56
|
+
lines.push('');
|
|
57
|
+
lines.push('largest individual wins (own score, biggest improvement first):');
|
|
58
|
+
for (const w of summary.topWins) {
|
|
59
|
+
const desc = w.description.length > 0 ? ` -- ${w.description}` : '';
|
|
60
|
+
lines.push(` ${shortSha(w.commit)} score=${w.score.toFixed(4)} best_bench_delta=${w.bestBenchDelta.toFixed(2)}%${desc}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
lines.push('');
|
|
65
|
+
lines.push('no kept experiments yet.');
|
|
66
|
+
}
|
|
67
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=cmd-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cmd-report.js","sourceRoot":"","sources":["../../src/cli/cmd-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RunCtx } from './runctx.js';
|
|
2
|
+
/**
|
|
3
|
+
* `status`: reports where a run is, without changing it. It reads only the
|
|
4
|
+
* baseline record, `results.tsv`, the eval lock and the stop file (plus two
|
|
5
|
+
* read-only `git` queries for the human's own current branch/HEAD) --
|
|
6
|
+
* nothing here is ever written. `-tag` resolves the run directory from the
|
|
7
|
+
* repo root and the tag alone (see `state/home.ts`'s `runDir`), independent
|
|
8
|
+
* of whatever branch the repository happens to be on right now, so a human
|
|
9
|
+
* who has wandered off the run branch can still check on it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function cmdStatus(ctx: RunCtx, argv: readonly string[]): Promise<number>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { access } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
|
+
import { currentBranch, headCommit, shortSha } from '../gitx/git.js';
|
|
5
|
+
import { loadRows, summarize } from '../results/results.js';
|
|
6
|
+
import { readBaseline } from '../state/baseline.js';
|
|
7
|
+
import { runDir } from '../state/home.js';
|
|
8
|
+
import { readEvalLock } from '../state/lock.js';
|
|
9
|
+
import { BRANCH_PREFIX, WORKTREE_DIRNAME, inferTagFromBranch } from '../state/runnaming.js';
|
|
10
|
+
import { readStop } from '../state/stop.js';
|
|
11
|
+
import { formatCumulativeSpeedup } from './speedup.js';
|
|
12
|
+
const STATUSES = ['keep', 'discard', 'fail', 'crash'];
|
|
13
|
+
function fail(message) {
|
|
14
|
+
process.stderr.write(`error: ${message}\n`);
|
|
15
|
+
return 2;
|
|
16
|
+
}
|
|
17
|
+
function messageOf(e) {
|
|
18
|
+
return e instanceof Error ? e.message : String(e);
|
|
19
|
+
}
|
|
20
|
+
async function exists(p) {
|
|
21
|
+
try {
|
|
22
|
+
await access(p);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Mirrors lock.ts's private `isAlive` (not exported): EPERM means the
|
|
31
|
+
* process exists but we lack permission to signal it, which is still
|
|
32
|
+
* "alive." `status` only ever uses this to word its report ("running" vs.
|
|
33
|
+
* "a crashed eval's stale lock") -- it never reclaims anything, which stays
|
|
34
|
+
* `acquireEvalLock`'s job alone, so a merely-informational check here can
|
|
35
|
+
* never race a real acquire.
|
|
36
|
+
*/
|
|
37
|
+
function isPidAlive(pid) {
|
|
38
|
+
try {
|
|
39
|
+
process.kill(pid, 0);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
return err.code === 'EPERM';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function tryGit(f) {
|
|
47
|
+
try {
|
|
48
|
+
return await f();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* `status`: reports where a run is, without changing it. It reads only the
|
|
56
|
+
* baseline record, `results.tsv`, the eval lock and the stop file (plus two
|
|
57
|
+
* read-only `git` queries for the human's own current branch/HEAD) --
|
|
58
|
+
* nothing here is ever written. `-tag` resolves the run directory from the
|
|
59
|
+
* repo root and the tag alone (see `state/home.ts`'s `runDir`), independent
|
|
60
|
+
* of whatever branch the repository happens to be on right now, so a human
|
|
61
|
+
* who has wandered off the run branch can still check on it.
|
|
62
|
+
*/
|
|
63
|
+
export async function cmdStatus(ctx, argv) {
|
|
64
|
+
let tag;
|
|
65
|
+
try {
|
|
66
|
+
const normalized = argv.map((a) => (/^-[A-Za-z][A-Za-z-]+$/.test(a) ? `-${a}` : a));
|
|
67
|
+
const { values } = parseArgs({
|
|
68
|
+
args: normalized,
|
|
69
|
+
options: { tag: { type: 'string' } },
|
|
70
|
+
strict: true,
|
|
71
|
+
allowPositionals: false,
|
|
72
|
+
});
|
|
73
|
+
tag = values.tag;
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
return fail(messageOf(e));
|
|
77
|
+
}
|
|
78
|
+
let resolvedTag;
|
|
79
|
+
try {
|
|
80
|
+
resolvedTag = tag ?? (await inferTagFromBranch(ctx.repoRoot));
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
return fail(messageOf(e));
|
|
84
|
+
}
|
|
85
|
+
const dir = runDir(ctx.repoRoot, resolvedTag);
|
|
86
|
+
let baseline;
|
|
87
|
+
try {
|
|
88
|
+
baseline = await readBaseline(dir);
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
return fail(messageOf(e));
|
|
92
|
+
}
|
|
93
|
+
const [branch, head, lock, stop, rows] = await Promise.all([
|
|
94
|
+
tryGit(() => currentBranch(ctx.repoRoot)),
|
|
95
|
+
tryGit(() => headCommit(ctx.repoRoot)),
|
|
96
|
+
readEvalLock(dir),
|
|
97
|
+
readStop(dir),
|
|
98
|
+
loadRows(ctx.resultsPath),
|
|
99
|
+
]);
|
|
100
|
+
const runBranch = `${BRANCH_PREFIX}${resolvedTag}`;
|
|
101
|
+
const worktreeDir = path.join(dir, WORKTREE_DIRNAME);
|
|
102
|
+
const worktreePresent = await exists(worktreeDir);
|
|
103
|
+
const summary = summarize(rows);
|
|
104
|
+
const lines = [];
|
|
105
|
+
lines.push(`autor3search-typescript status -- tag ${JSON.stringify(resolvedTag)}`);
|
|
106
|
+
lines.push(` run branch: ${runBranch}`);
|
|
107
|
+
lines.push(` current branch: ${branch ?? 'unknown'}` + (branch === runBranch ? '' : ' (not on the run branch)'));
|
|
108
|
+
lines.push(` frozen commit: ${shortSha(baseline.frozenCommit)} (${baseline.frozenCommit})`);
|
|
109
|
+
lines.push(` measure commit: ${shortSha(baseline.measureCommit)} (${baseline.measureCommit})`);
|
|
110
|
+
lines.push(` HEAD: ${head === null ? 'unknown' : `${shortSha(head)} (${head})`}`);
|
|
111
|
+
lines.push(` worktree: ${worktreeDir} (${worktreePresent ? 'present' : 'MISSING'})`);
|
|
112
|
+
if (lock === null) {
|
|
113
|
+
lines.push(' eval: idle');
|
|
114
|
+
}
|
|
115
|
+
else if (isPidAlive(lock.pid)) {
|
|
116
|
+
lines.push(` eval: running (pid ${lock.pid})`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
lines.push(` eval: not running (stale lock left by pid ${lock.pid} -- a previous eval likely crashed)`);
|
|
120
|
+
}
|
|
121
|
+
if (stop === null) {
|
|
122
|
+
lines.push(' stop: none pending');
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const detail = stop.force
|
|
126
|
+
? 'the running eval was asked to abandon its current experiment immediately'
|
|
127
|
+
: 'the agent will stop after its current experiment reports a verdict';
|
|
128
|
+
lines.push(` stop: requested at ${stop.requestedAt} (force: ${stop.force ? 'yes' : 'no'}) -- ${detail}`);
|
|
129
|
+
}
|
|
130
|
+
lines.push(` experiments: ${rows.length} total`);
|
|
131
|
+
lines.push(` ${STATUSES.map((s) => `${s}: ${summary.counts[s] ?? 0}`).join(' ')}`);
|
|
132
|
+
lines.push(` cumulative speedup: ${formatCumulativeSpeedup(summary.cumulativeSpeedup)}`);
|
|
133
|
+
if (summary.topWins.length > 0) {
|
|
134
|
+
lines.push(' top wins:');
|
|
135
|
+
for (const w of summary.topWins) {
|
|
136
|
+
const desc = w.description.length > 0 ? ` -- ${w.description}` : '';
|
|
137
|
+
lines.push(` ${shortSha(w.commit)} score=${w.score.toFixed(4)}${desc}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=cmd-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cmd-status.js","sourceRoot":"","sources":["../../src/cli/cmd-status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RunCtx } from './runctx.js';
|
|
2
|
+
/**
|
|
3
|
+
* `stop`: asks a running (or about-to-run) `eval` loop to end.
|
|
4
|
+
*
|
|
5
|
+
* Plain `stop` only ever writes `stop.json` -- the agent reads it at its
|
|
6
|
+
* next verdict, so the in-flight experiment always finishes and is scored.
|
|
7
|
+
* `-clear` cancels a pending request. `-force` additionally signals the pid
|
|
8
|
+
* recorded in `eval.lock` (if any) to abandon the current experiment right
|
|
9
|
+
* now -- using `runner/exec.ts`'s own exported `killGroup`, so the pid-1
|
|
10
|
+
* refusal lives in exactly one place -- and then reports the repository
|
|
11
|
+
* state that leaves -- including the exact command that would drop the
|
|
12
|
+
* abandoned commit -- WITHOUT running it. Dropping an agent's work is the
|
|
13
|
+
* human's decision, never this tool's.
|
|
14
|
+
*/
|
|
15
|
+
export declare function cmdStop(ctx: RunCtx, argv: readonly string[]): Promise<number>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { parseArgs } from 'node:util';
|
|
2
|
+
import { currentBranch, headCommit, shortSha } from '../gitx/git.js';
|
|
3
|
+
import { killGroup } from '../runner/exec.js';
|
|
4
|
+
import { readBaseline } from '../state/baseline.js';
|
|
5
|
+
import { runDir } from '../state/home.js';
|
|
6
|
+
import { readEvalLock } from '../state/lock.js';
|
|
7
|
+
import { inferTagFromBranch } from '../state/runnaming.js';
|
|
8
|
+
import { clearStop, requestStop } from '../state/stop.js';
|
|
9
|
+
function fail(message) {
|
|
10
|
+
process.stderr.write(`error: ${message}\n`);
|
|
11
|
+
return 2;
|
|
12
|
+
}
|
|
13
|
+
function messageOf(e) {
|
|
14
|
+
return e instanceof Error ? e.message : String(e);
|
|
15
|
+
}
|
|
16
|
+
async function tryGit(f) {
|
|
17
|
+
try {
|
|
18
|
+
return await f();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Mirrors lock.ts's private `isAlive` (not exported): EPERM means the
|
|
26
|
+
* process exists but we lack permission to signal it, which is still
|
|
27
|
+
* "alive." Used here only to word the report honestly -- a lock recording a
|
|
28
|
+
* pid that already died (the eval crashed, or a previous `-force` already
|
|
29
|
+
* killed it) must not be reported as "signalled" just because the file is
|
|
30
|
+
* still there; `readEvalLock` itself never checks this (see its own doc
|
|
31
|
+
* comment), so a caller that needs to know still has to.
|
|
32
|
+
*/
|
|
33
|
+
function isPidAlive(pid) {
|
|
34
|
+
try {
|
|
35
|
+
process.kill(pid, 0);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
return err.code === 'EPERM';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* `stop`: asks a running (or about-to-run) `eval` loop to end.
|
|
44
|
+
*
|
|
45
|
+
* Plain `stop` only ever writes `stop.json` -- the agent reads it at its
|
|
46
|
+
* next verdict, so the in-flight experiment always finishes and is scored.
|
|
47
|
+
* `-clear` cancels a pending request. `-force` additionally signals the pid
|
|
48
|
+
* recorded in `eval.lock` (if any) to abandon the current experiment right
|
|
49
|
+
* now -- using `runner/exec.ts`'s own exported `killGroup`, so the pid-1
|
|
50
|
+
* refusal lives in exactly one place -- and then reports the repository
|
|
51
|
+
* state that leaves -- including the exact command that would drop the
|
|
52
|
+
* abandoned commit -- WITHOUT running it. Dropping an agent's work is the
|
|
53
|
+
* human's decision, never this tool's.
|
|
54
|
+
*/
|
|
55
|
+
export async function cmdStop(ctx, argv) {
|
|
56
|
+
let tag;
|
|
57
|
+
let clear;
|
|
58
|
+
let force;
|
|
59
|
+
try {
|
|
60
|
+
const normalized = argv.map((a) => (/^-[A-Za-z][A-Za-z-]+$/.test(a) ? `-${a}` : a));
|
|
61
|
+
const { values } = parseArgs({
|
|
62
|
+
args: normalized,
|
|
63
|
+
options: {
|
|
64
|
+
tag: { type: 'string' },
|
|
65
|
+
clear: { type: 'boolean', default: false },
|
|
66
|
+
force: { type: 'boolean', default: false },
|
|
67
|
+
},
|
|
68
|
+
strict: true,
|
|
69
|
+
allowPositionals: false,
|
|
70
|
+
});
|
|
71
|
+
tag = values.tag;
|
|
72
|
+
clear = values.clear === true;
|
|
73
|
+
force = values.force === true;
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
return fail(messageOf(e));
|
|
77
|
+
}
|
|
78
|
+
if (clear && force) {
|
|
79
|
+
return fail('-clear and -force are mutually exclusive');
|
|
80
|
+
}
|
|
81
|
+
let resolvedTag;
|
|
82
|
+
try {
|
|
83
|
+
resolvedTag = tag ?? (await inferTagFromBranch(ctx.repoRoot));
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
return fail(messageOf(e));
|
|
87
|
+
}
|
|
88
|
+
const dir = runDir(ctx.repoRoot, resolvedTag);
|
|
89
|
+
try {
|
|
90
|
+
// Confirms a run actually exists before writing (or clearing) any state
|
|
91
|
+
// for it. A human who mistypes a tag must be told the tag is unknown,
|
|
92
|
+
// not silently handed a stop request that nothing will ever read.
|
|
93
|
+
// readBaseline never creates anything -- only reads -- so a nonexistent
|
|
94
|
+
// run is refused here before any state is touched.
|
|
95
|
+
await readBaseline(dir);
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
return fail(messageOf(e));
|
|
99
|
+
}
|
|
100
|
+
if (clear) {
|
|
101
|
+
await clearStop(dir);
|
|
102
|
+
process.stdout.write(`stop request for tag ${JSON.stringify(resolvedTag)} cleared\n`);
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
await requestStop(dir, force);
|
|
106
|
+
if (!force) {
|
|
107
|
+
process.stdout.write(`stop requested for tag ${JSON.stringify(resolvedTag)}: the agent will stop after its current ` +
|
|
108
|
+
'experiment reports a verdict.\n');
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
process.stdout.write(`stop -force requested for tag ${JSON.stringify(resolvedTag)}.\n`);
|
|
112
|
+
const lock = await readEvalLock(dir);
|
|
113
|
+
const signalled = lock !== null && isPidAlive(lock.pid);
|
|
114
|
+
if (lock === null) {
|
|
115
|
+
process.stdout.write(' no eval lock is currently held: nothing to signal.\n');
|
|
116
|
+
}
|
|
117
|
+
else if (!signalled) {
|
|
118
|
+
process.stdout.write(` eval.lock names pid ${lock.pid}, but it is no longer running (a previous eval likely ` +
|
|
119
|
+
'crashed, or was already stopped): nothing to signal.\n');
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
killGroup(lock.pid, 'SIGTERM');
|
|
123
|
+
process.stdout.write(` signalled pid ${lock.pid} (SIGTERM) to abandon its current experiment.\n`);
|
|
124
|
+
}
|
|
125
|
+
const branch = await tryGit(() => currentBranch(ctx.repoRoot));
|
|
126
|
+
const head = await tryGit(() => headCommit(ctx.repoRoot));
|
|
127
|
+
process.stdout.write(` current branch: ${branch ?? 'unknown'}\n`);
|
|
128
|
+
process.stdout.write(` HEAD commit: ${head === null ? 'unknown' : `${shortSha(head)} (${head})`}\n`);
|
|
129
|
+
// Only claim a commit was actually abandoned when something was really
|
|
130
|
+
// signalled -- with no eval lock held, HEAD is just whatever the last
|
|
131
|
+
// completed experiment left behind, not something this invocation
|
|
132
|
+
// interrupted. Either way, nothing has been changed here; dropping a
|
|
133
|
+
// commit is the human's call, never this tool's, so the reset command is
|
|
134
|
+
// only ever printed, never run.
|
|
135
|
+
process.stdout.write(signalled
|
|
136
|
+
? ' this is very likely the commit for the experiment just abandoned. Nothing here has been\n' +
|
|
137
|
+
' changed -- dropping it is your call, not this tool\'s. To drop it:\n'
|
|
138
|
+
: ' no eval was running, so nothing was abandoned. If HEAD above is still an unwanted\n' +
|
|
139
|
+
' experiment, nothing here has been changed -- dropping it is your call. To drop it:\n');
|
|
140
|
+
process.stdout.write('\n git reset --hard HEAD~1\n');
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=cmd-stop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cmd-stop.js","sourceRoot":"","sources":["../../src/cli/cmd-stop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type RunCtx } from './runctx.js';
|
|
3
|
+
type Command = (ctx: RunCtx, argv: string[]) => Promise<number>;
|
|
4
|
+
/**
|
|
5
|
+
* The command table.
|
|
6
|
+
*
|
|
7
|
+
* Ruling 34: a command implemented and tested but never added here is
|
|
8
|
+
* unreachable, yet looks completely healthy from inside its own task's
|
|
9
|
+
* suite -- the missing piece lives in a file that task never touches, and
|
|
10
|
+
* `--help` would still print successfully, just without it. Every command
|
|
11
|
+
* task must register itself here, and prove it with a test that dispatches
|
|
12
|
+
* through `main`, not one that only calls the command function directly.
|
|
13
|
+
* Exported (only) so the COMMANDS/HELP invariant test below can compare
|
|
14
|
+
* this table's keys against what `--help` actually lists.
|
|
15
|
+
*/
|
|
16
|
+
export declare const COMMANDS: Record<string, Command>;
|
|
17
|
+
/**
|
|
18
|
+
* Dispatch only: parse the leading `-C <dir>`, take the subcommand, and
|
|
19
|
+
* delegate. Every command is `(ctx, argv) => Promise<number>`, and its
|
|
20
|
+
* return value becomes the process exit code directly -- exit codes are
|
|
21
|
+
* reserved project-wide (0 KEEP, 1 DISCARD, 2 FAIL, 3 CRASH), and a usage
|
|
22
|
+
* error or an `init` failure is a FAIL (2).
|
|
23
|
+
*
|
|
24
|
+
* This function never rejects: everything below -- including `resolveCtx`
|
|
25
|
+
* failing on a `-C` outside any git repository -- is caught here and
|
|
26
|
+
* turned into `error: <message>` on stderr plus a `2` return, never a raw
|
|
27
|
+
* stack trace. That makes it directly testable (call it, read the
|
|
28
|
+
* returned number) without a test having to unwrap a promise rejection.
|
|
29
|
+
*/
|
|
30
|
+
export declare function main(argv: string[]): Promise<number>;
|
|
31
|
+
export {};
|