orcareplay 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/dist/api.d.ts +127 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +159 -0
- package/dist/api.js.map +1 -0
- package/dist/args.d.ts +24 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +124 -0
- package/dist/args.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/compare.d.ts +27 -0
- package/dist/commands/compare.d.ts.map +1 -0
- package/dist/commands/compare.js +182 -0
- package/dist/commands/compare.js.map +1 -0
- package/dist/commands/doctor.d.ts +24 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +358 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/gc.d.ts +24 -0
- package/dist/commands/gc.d.ts.map +1 -0
- package/dist/commands/gc.js +326 -0
- package/dist/commands/gc.js.map +1 -0
- package/dist/commands/inspect.d.ts +24 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +262 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/record.d.ts +17 -0
- package/dist/commands/record.d.ts.map +1 -0
- package/dist/commands/record.js +497 -0
- package/dist/commands/record.js.map +1 -0
- package/dist/commands/replay.d.ts +43 -0
- package/dist/commands/replay.d.ts.map +1 -0
- package/dist/commands/replay.js +800 -0
- package/dist/commands/replay.js.map +1 -0
- package/dist/commands/scrub.d.ts +34 -0
- package/dist/commands/scrub.d.ts.map +1 -0
- package/dist/commands/scrub.js +452 -0
- package/dist/commands/scrub.js.map +1 -0
- package/dist/commands/setup.d.ts +23 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +177 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/config.d.ts +80 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +117 -0
- package/dist/config.js.map +1 -0
- package/dist/exchange-events.d.ts +72 -0
- package/dist/exchange-events.d.ts.map +1 -0
- package/dist/exchange-events.js +170 -0
- package/dist/exchange-events.js.map +1 -0
- package/dist/flags.d.ts +10 -0
- package/dist/flags.d.ts.map +1 -0
- package/dist/flags.js +83 -0
- package/dist/flags.js.map +1 -0
- package/dist/fs-events.d.ts +19 -0
- package/dist/fs-events.d.ts.map +1 -0
- package/dist/fs-events.js +53 -0
- package/dist/fs-events.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +282 -0
- package/dist/main.js.map +1 -0
- package/dist/mcp-server.d.ts +44 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +245 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp.d.ts +111 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +221 -0
- package/dist/mcp.js.map +1 -0
- package/dist/out.d.ts +69 -0
- package/dist/out.d.ts.map +1 -0
- package/dist/out.js +138 -0
- package/dist/out.js.map +1 -0
- package/dist/rasterize.d.ts +62 -0
- package/dist/rasterize.d.ts.map +1 -0
- package/dist/rasterize.js +154 -0
- package/dist/rasterize.js.map +1 -0
- package/dist/serial.d.ts +19 -0
- package/dist/serial.d.ts.map +1 -0
- package/dist/serial.js +34 -0
- package/dist/serial.js.map +1 -0
- package/dist/share-card.d.ts +73 -0
- package/dist/share-card.d.ts.map +1 -0
- package/dist/share-card.js +413 -0
- package/dist/share-card.js.map +1 -0
- package/dist/snapshot.d.ts +30 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +43 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/tls-capture.d.ts +57 -0
- package/dist/tls-capture.d.ts.map +1 -0
- package/dist/tls-capture.js +198 -0
- package/dist/tls-capture.js.map +1 -0
- package/dist/upstream.d.ts +20 -0
- package/dist/upstream.d.ts.map +1 -0
- package/dist/upstream.js +31 -0
- package/dist/upstream.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { TraceReader, deriveCheckpoints, resolveRunSelector } from '@orcareplay/core';
|
|
2
|
+
import { priceFor } from '@orcareplay/providers';
|
|
3
|
+
import { parseArgs } from '../args.js';
|
|
4
|
+
import { writeFile } from 'node:fs/promises';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
import { renderCompareCard } from '../share-card.js';
|
|
7
|
+
import { cardTarget, svgToPng } from '../rasterize.js';
|
|
8
|
+
import { replayCommand } from './replay.js';
|
|
9
|
+
import { readConfig } from '../config.js';
|
|
10
|
+
/** Run the verdict command inside the fork's worktree. Shell so `npm test -- auth` just works. */
|
|
11
|
+
async function runVerify(command, cwd) {
|
|
12
|
+
const { spawn } = await import('node:child_process');
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const child = spawn(command, { cwd, shell: true, stdio: 'ignore' });
|
|
15
|
+
// A verify command that cannot even start is a failed verdict, not a crashed comparison.
|
|
16
|
+
child.on('error', () => resolve(127));
|
|
17
|
+
child.on('close', (code) => resolve(code ?? 1));
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/** Each fork is a fresh argv, so upstream overrides have to be forwarded explicitly. */
|
|
21
|
+
function upstreamFlags(args) {
|
|
22
|
+
const out = [];
|
|
23
|
+
const a = args.str('upstream-anthropic');
|
|
24
|
+
const o = args.str('upstream-openai');
|
|
25
|
+
if (a)
|
|
26
|
+
out.push('--upstream-anthropic', a);
|
|
27
|
+
if (o)
|
|
28
|
+
out.push('--upstream-openai', o);
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* `orca compare <run> --models a,b,c`
|
|
33
|
+
*
|
|
34
|
+
* N forks from one checkpoint. The comparison only means anything because every branch starts
|
|
35
|
+
* from an identical filesystem tree and conversation prefix — the model is the only variable, so
|
|
36
|
+
* the difference in outcome is attributable to it rather than to drift.
|
|
37
|
+
*/
|
|
38
|
+
export async function compareCommand(args, out, cwd = process.cwd()) {
|
|
39
|
+
// Fall back to what `orca setup` recorded, so the common case needs no flags at all. An explicit
|
|
40
|
+
// --models still wins: the config is a default, not a policy.
|
|
41
|
+
const models = args.list('models').length > 0 ? args.list('models') : ((await readConfig()).models ?? []);
|
|
42
|
+
if (models.length === 0) {
|
|
43
|
+
throw new Error('compare needs models to compare\n' +
|
|
44
|
+
// Not two example ids: they are gateway-specific — OrcaRouter namespaces by provider,
|
|
45
|
+
// a direct provider does not — so a copyable pair here fails against half of them.
|
|
46
|
+
' orca models to see what your gateway serves, then: orca compare last --models a,b\n' +
|
|
47
|
+
' or run orca setup once — it stores a gateway (OrcaRouter by default) and a model list');
|
|
48
|
+
}
|
|
49
|
+
const selector = args.positionals[0] ?? 'last';
|
|
50
|
+
const runDir = (await resolveRunSelector(cwd, selector)).dir;
|
|
51
|
+
const reader = await TraceReader.open(runDir);
|
|
52
|
+
const events = await reader.events();
|
|
53
|
+
const checkpoints = deriveCheckpoints(events);
|
|
54
|
+
if (checkpoints.length === 0) {
|
|
55
|
+
throw new Error('this run has no checkpoints, so there is nothing to fork from');
|
|
56
|
+
}
|
|
57
|
+
const from = args.num('from') ?? checkpoints[checkpoints.length - 1].seq;
|
|
58
|
+
const runId = reader.manifest().run_id;
|
|
59
|
+
out.phase('compare', { run: runId, from, models: models.join(',') });
|
|
60
|
+
const rows = [];
|
|
61
|
+
// Sequential rather than parallel: each fork spawns a real agent against a real worktree, and
|
|
62
|
+
// running four of those concurrently on one machine measures the machine, not the models.
|
|
63
|
+
for (const model of models) {
|
|
64
|
+
const startedAt = Date.now();
|
|
65
|
+
try {
|
|
66
|
+
const forkArgs = parseArgs([
|
|
67
|
+
'replay',
|
|
68
|
+
// The concrete run id, never the caller's selector. "last" is re-resolved on every fork,
|
|
69
|
+
// so after the first branch it would point at the child we just created — which has no
|
|
70
|
+
// checkpoints — and every later model would fail for a reason that is not about the model.
|
|
71
|
+
runId,
|
|
72
|
+
'--from',
|
|
73
|
+
String(from),
|
|
74
|
+
'--model',
|
|
75
|
+
model,
|
|
76
|
+
...(args.bool('loose') ? ['--loose'] : []),
|
|
77
|
+
// Every fork this makes runs a live agent, so interception has to reach them: accepting
|
|
78
|
+
// the flag here and dropping it before the fork is exactly the silent-no-op the flag was
|
|
79
|
+
// fixed for one layer up.
|
|
80
|
+
...(args.bool('tls-intercept') ? ['--tls-intercept'] : []),
|
|
81
|
+
// Same reasoning, and the same silent-no-op if dropped: every fork launches a live agent,
|
|
82
|
+
// and under --json orca's stdout is the result document. A fork that inherits it prints
|
|
83
|
+
// the agent's output into the middle of the JSON.
|
|
84
|
+
...(args.bool('json') ? ['--json'] : []),
|
|
85
|
+
...args.list('tls-hosts').flatMap((h) => ['--tls-hosts', h]),
|
|
86
|
+
...upstreamFlags(args),
|
|
87
|
+
]);
|
|
88
|
+
const result = await replayCommand(forkArgs, out, cwd);
|
|
89
|
+
// The agent exiting 0 only means it did not crash. "Did the task actually get done" needs
|
|
90
|
+
// a command that answers it — otherwise the verdict column is a number nobody should act on.
|
|
91
|
+
const verify = args.str('verify');
|
|
92
|
+
const verifyExitCode = verify && result.worktree ? await runVerify(verify, result.worktree) : undefined;
|
|
93
|
+
const failed = verifyExitCode === undefined ? result.exitCode !== 0 : verifyExitCode !== 0;
|
|
94
|
+
const usage = result.forkRunId
|
|
95
|
+
? await usageOf(cwd, result.forkRunId)
|
|
96
|
+
: { input: 0, output: 0 };
|
|
97
|
+
const money = priceFor({ input_tokens: usage.input, output_tokens: usage.output }, model);
|
|
98
|
+
rows.push({
|
|
99
|
+
model,
|
|
100
|
+
verdict: failed ? 'fail' : 'pass',
|
|
101
|
+
verifyExitCode,
|
|
102
|
+
exitCode: result.exitCode,
|
|
103
|
+
divergences: result.divergences,
|
|
104
|
+
inputTokens: usage.input,
|
|
105
|
+
outputTokens: usage.output,
|
|
106
|
+
cost: money ? money.amount : null,
|
|
107
|
+
wallMs: Date.now() - startedAt,
|
|
108
|
+
forkRunId: result.forkRunId,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
rows.push({
|
|
113
|
+
model,
|
|
114
|
+
verdict: 'fail',
|
|
115
|
+
exitCode: -1,
|
|
116
|
+
divergences: 0,
|
|
117
|
+
inputTokens: 0,
|
|
118
|
+
outputTokens: 0,
|
|
119
|
+
cost: null,
|
|
120
|
+
wallMs: Date.now() - startedAt,
|
|
121
|
+
error: err instanceof Error ? err.message.split('\n')[0] : String(err),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
out.plain('');
|
|
126
|
+
out.table(['MODEL', 'VERDICT', 'TOKENS', 'COST', 'WALL', 'RUN'], rows.map((r) => [
|
|
127
|
+
r.model,
|
|
128
|
+
r.verdict,
|
|
129
|
+
`${r.inputTokens}/${r.outputTokens}`,
|
|
130
|
+
// An unknown model prices as a dash, never as $0.00 — a confidently wrong cost is worse
|
|
131
|
+
// than an absent one when it lands in a table someone decides from. And a real cost too
|
|
132
|
+
// small for four decimals still gets shown: "cheaper" is the whole reason for this column.
|
|
133
|
+
formatCost(r.cost),
|
|
134
|
+
`${(r.wallMs / 1000).toFixed(1)}s`,
|
|
135
|
+
r.forkRunId ?? r.error ?? '',
|
|
136
|
+
]));
|
|
137
|
+
if (args.has('share')) {
|
|
138
|
+
const picked = cardTarget(args.str('share') ?? 'compare.svg', '--share');
|
|
139
|
+
if (picked.format === 'gif') {
|
|
140
|
+
throw new Error('--share draws one table, so it has no animation. Use .svg or .png.');
|
|
141
|
+
}
|
|
142
|
+
const target = resolve(cwd, picked.path);
|
|
143
|
+
const svg = renderCompareCard(rows, {
|
|
144
|
+
runId,
|
|
145
|
+
forkPoint: from,
|
|
146
|
+
verify: args.str('verify'),
|
|
147
|
+
});
|
|
148
|
+
const body = picked.format === 'png' ? await svgToPng(svg) : svg;
|
|
149
|
+
await writeFile(target, body, { mode: 0o644 });
|
|
150
|
+
out.phase('shared', {
|
|
151
|
+
path: target,
|
|
152
|
+
bytes: typeof body === 'string' ? Buffer.byteLength(body) : body.length,
|
|
153
|
+
format: picked.format,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return rows;
|
|
157
|
+
}
|
|
158
|
+
/** Dash for unknown. Otherwise enough precision that a genuinely cheap model does not read $0. */
|
|
159
|
+
export function formatCost(amount) {
|
|
160
|
+
if (amount === null)
|
|
161
|
+
return '—';
|
|
162
|
+
if (amount === 0)
|
|
163
|
+
return '$0';
|
|
164
|
+
if (amount < 0.0001)
|
|
165
|
+
return `$${amount.toExponential(2)}`;
|
|
166
|
+
if (amount < 0.01)
|
|
167
|
+
return `$${amount.toFixed(6)}`;
|
|
168
|
+
return `$${amount.toFixed(4)}`;
|
|
169
|
+
}
|
|
170
|
+
async function usageOf(cwd, runId) {
|
|
171
|
+
const reader = await TraceReader.open((await resolveRunSelector(cwd, runId)).dir);
|
|
172
|
+
let input = 0;
|
|
173
|
+
let output = 0;
|
|
174
|
+
for (const e of await reader.events()) {
|
|
175
|
+
if (e.type !== 'model.response')
|
|
176
|
+
continue;
|
|
177
|
+
input += Number(e.attrs?.input_tokens ?? 0);
|
|
178
|
+
output += Number(e.attrs?.output_tokens ?? 0);
|
|
179
|
+
}
|
|
180
|
+
return { input, output };
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/commands/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ParsedArgs } from '../args.js';
|
|
2
|
+
import type { Output } from '../out.js';
|
|
3
|
+
export interface DoctorCheck {
|
|
4
|
+
name: string;
|
|
5
|
+
status: 'ok' | 'warn' | 'fail';
|
|
6
|
+
detail: string;
|
|
7
|
+
/** What to do about it. Mandatory in spirit for anything that is not ok. */
|
|
8
|
+
fix?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DoctorResult {
|
|
11
|
+
checks: DoctorCheck[];
|
|
12
|
+
ok: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `orca doctor` — will recording actually work here?
|
|
16
|
+
*
|
|
17
|
+
* Orca instruments an agent by putting environment variables in front of someone else's process
|
|
18
|
+
* and shelling out to git. When that goes wrong the symptom appears inside the agent, minutes
|
|
19
|
+
* later, as something that looks like the agent's fault. Every check here is one of those failure
|
|
20
|
+
* modes, answered before the user spends an hour on it — so each is exercised for real (a port is
|
|
21
|
+
* actually bound, a file is actually written) rather than inferred.
|
|
22
|
+
*/
|
|
23
|
+
export declare function doctorCommand(args: ParsedArgs, out: Output, cwd?: string): Promise<DoctorResult>;
|
|
24
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAMxC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,EAAE,EAAE,OAAO,CAAC;CACb;AAMD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,SAAgB,GAClB,OAAO,CAAC,YAAY,CAAC,CAuCvB"}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { mkdtemp, rm, stat, unlink, writeFile } from 'node:fs/promises';
|
|
4
|
+
// Namespaced as well as named, because `statfs` is not present on every supported node build and
|
|
5
|
+
// a missing named import from a builtin is a load-time error for the whole CLI.
|
|
6
|
+
import * as fsPromises from 'node:fs/promises';
|
|
7
|
+
import { createServer } from 'node:net';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { delimiter, dirname, join } from 'node:path';
|
|
10
|
+
import { promisify } from 'node:util';
|
|
11
|
+
import { defaultAdapters } from '@orcareplay/adapters';
|
|
12
|
+
import { listRuns, orcaDir } from '@orcareplay/core';
|
|
13
|
+
import { runGit } from '@orcareplay/fs-capture';
|
|
14
|
+
import { installShellShim, readShellFrames } from '@orcareplay/shell-shim';
|
|
15
|
+
import { RENDER_DEPS, missingRenderDeps } from '../rasterize.js';
|
|
16
|
+
import { formatBytes, runDirBytes } from './gc.js';
|
|
17
|
+
import { shimIsRunnable } from '../mcp.js';
|
|
18
|
+
const execFileAsync = promisify(execFile);
|
|
19
|
+
const MIN_NODE_MAJOR = 20;
|
|
20
|
+
const LOW_DISK_BYTES = 512 * 1024 * 1024;
|
|
21
|
+
const BIG_STORE_BYTES = 1024 * 1024 * 1024;
|
|
22
|
+
/**
|
|
23
|
+
* `orca doctor` — will recording actually work here?
|
|
24
|
+
*
|
|
25
|
+
* Orca instruments an agent by putting environment variables in front of someone else's process
|
|
26
|
+
* and shelling out to git. When that goes wrong the symptom appears inside the agent, minutes
|
|
27
|
+
* later, as something that looks like the agent's fault. Every check here is one of those failure
|
|
28
|
+
* modes, answered before the user spends an hour on it — so each is exercised for real (a port is
|
|
29
|
+
* actually bound, a file is actually written) rather than inferred.
|
|
30
|
+
*/
|
|
31
|
+
export async function doctorCommand(args, out, cwd = process.cwd()) {
|
|
32
|
+
const git = await checkGit();
|
|
33
|
+
const writable = await checkWritable(cwd);
|
|
34
|
+
const checks = [
|
|
35
|
+
checkNode(),
|
|
36
|
+
git.check,
|
|
37
|
+
await checkWorkspace(cwd, git.available),
|
|
38
|
+
writable.check,
|
|
39
|
+
await checkAgents(cwd),
|
|
40
|
+
await checkPort(),
|
|
41
|
+
await checkShellShim(),
|
|
42
|
+
await checkMcpShim(),
|
|
43
|
+
await checkRasteriser(),
|
|
44
|
+
await checkDisk(writable.probeDir),
|
|
45
|
+
await checkRuns(cwd),
|
|
46
|
+
];
|
|
47
|
+
const failed = checks.filter((c) => c.status === 'fail');
|
|
48
|
+
const warned = checks.filter((c) => c.status === 'warn');
|
|
49
|
+
const ok = failed.length === 0;
|
|
50
|
+
out.table(['CHECK', 'STATUS', 'DETAIL'], checks.map((c) => [c.name, c.status, c.detail]));
|
|
51
|
+
const trouble = [...failed, ...warned];
|
|
52
|
+
out.plain('');
|
|
53
|
+
if (trouble.length === 0) {
|
|
54
|
+
out.plain(' all clear — orca record <agent> should work here');
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
for (const check of trouble) {
|
|
58
|
+
out.plain(` ${check.status} ${check.name}: ${check.fix ?? check.detail}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
out.phase('doctor', { ok, checks: checks.length, failed: failed.length, warned: warned.length });
|
|
62
|
+
return { checks, ok };
|
|
63
|
+
}
|
|
64
|
+
function checkNode() {
|
|
65
|
+
const name = 'node version';
|
|
66
|
+
// Read at call time, never cached: the running version is the thing under test.
|
|
67
|
+
const version = process.version;
|
|
68
|
+
const major = Number(/^v?(\d+)/.exec(version)?.[1] ?? Number.NaN);
|
|
69
|
+
if (Number.isNaN(major)) {
|
|
70
|
+
return {
|
|
71
|
+
name,
|
|
72
|
+
status: 'warn',
|
|
73
|
+
detail: `cannot read a major version from ${version}`,
|
|
74
|
+
fix: `run orca on node ${MIN_NODE_MAJOR} or newer`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (major < MIN_NODE_MAJOR) {
|
|
78
|
+
return {
|
|
79
|
+
name,
|
|
80
|
+
status: 'fail',
|
|
81
|
+
detail: `${version} — orca needs node ${MIN_NODE_MAJOR} or newer`,
|
|
82
|
+
fix: `install node ${MIN_NODE_MAJOR}+ (nvm install ${MIN_NODE_MAJOR}) and run orca doctor again`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return { name, status: 'ok', detail: version };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Two capture layers that fail quietly.
|
|
89
|
+
*
|
|
90
|
+
* Both are shims — a PATH entry in front of `sh`/`bash`, and a rewritten MCP config launching a
|
|
91
|
+
* JSON-RPC tee. When either cannot start, nothing goes wrong that anybody sees: the agent runs
|
|
92
|
+
* perfectly, the run succeeds, and the trace is missing a layer, which you discover only when you
|
|
93
|
+
* go looking for an exit code or a tool call that was never recorded. That is exactly the class of
|
|
94
|
+
* failure `doctor` exists to move forward in time, so both are exercised for real rather than
|
|
95
|
+
* inferred from a file existing.
|
|
96
|
+
*/
|
|
97
|
+
async function checkShellShim() {
|
|
98
|
+
const name = 'shell shim';
|
|
99
|
+
const dir = await mkdtemp(join(tmpdir(), 'orca-doctor-shim-'));
|
|
100
|
+
try {
|
|
101
|
+
const shim = await installShellShim({ runDir: dir });
|
|
102
|
+
// Run something through it, rather than trusting that writing the scripts was enough: the
|
|
103
|
+
// failure this catches is the compiled runner being absent from an installed package.
|
|
104
|
+
const { stdout } = await execFileAsync('sh', ['-c', 'printf ok'], {
|
|
105
|
+
env: { ...process.env, PATH: `${shim.dir}${delimiter}${process.env.PATH ?? ''}` },
|
|
106
|
+
});
|
|
107
|
+
if (stdout.trim() !== 'ok') {
|
|
108
|
+
return {
|
|
109
|
+
name,
|
|
110
|
+
status: 'warn',
|
|
111
|
+
detail: `the shim ran but returned ${JSON.stringify(stdout)}`,
|
|
112
|
+
fix: 'record with --no-shell; exit codes and timing will be missing from the trace',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const frames = await readShellFrames(shim.framesPath);
|
|
116
|
+
if (frames.length === 0) {
|
|
117
|
+
return {
|
|
118
|
+
name,
|
|
119
|
+
status: 'warn',
|
|
120
|
+
detail: 'commands pass through but nothing is captured',
|
|
121
|
+
fix: 'record with --no-shell rather than trusting a layer that records nothing',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return { name, status: 'ok', detail: `${shim.shimmed.join(', ')} — captured a test command` };
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
return {
|
|
128
|
+
name,
|
|
129
|
+
status: 'warn',
|
|
130
|
+
detail: String(err instanceof Error ? err.message : err).split('\n')[0] ?? 'unavailable',
|
|
131
|
+
fix: 'run `npm run build` if working from source, or record with --no-shell',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
await rm(dir, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function checkMcpShim() {
|
|
139
|
+
const name = 'mcp shim';
|
|
140
|
+
const runnable = await shimIsRunnable();
|
|
141
|
+
return runnable
|
|
142
|
+
? { name, status: 'ok', detail: 'runnable — pass --mcp-config <path> to use it' }
|
|
143
|
+
: {
|
|
144
|
+
name,
|
|
145
|
+
status: 'warn',
|
|
146
|
+
detail: 'the shim entry point cannot be launched',
|
|
147
|
+
fix: 'run `npm run build` if working from source; MCP calls will not be captured without it',
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** Through fs-capture's own runner, so this tests the git invocation recording will really make. */
|
|
151
|
+
/**
|
|
152
|
+
* Whether a card can be written as a PNG or a GIF.
|
|
153
|
+
*
|
|
154
|
+
* Never a failure. The raster toolchain is optional by design — `docs/media/README.md` keeps it
|
|
155
|
+
* out of `package.json` so `npm ci` stays lean — and recording, replaying and SVG cards all work
|
|
156
|
+
* without it. Reporting it is how someone finds out before they need it rather than at the moment
|
|
157
|
+
* they ask for a `.png`.
|
|
158
|
+
*/
|
|
159
|
+
async function checkRasteriser() {
|
|
160
|
+
const name = 'card rasteriser';
|
|
161
|
+
const missing = await missingRenderDeps();
|
|
162
|
+
return missing.length === 0
|
|
163
|
+
? { name, status: 'ok', detail: 'installed — cards can be written as .svg, .png or .gif' }
|
|
164
|
+
: {
|
|
165
|
+
name,
|
|
166
|
+
status: 'ok',
|
|
167
|
+
detail: 'not installed — cards can be written as .svg',
|
|
168
|
+
fix: `npm i --no-save ${RENDER_DEPS.join(' ')} to also write .png and .gif`,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async function checkGit() {
|
|
172
|
+
const name = 'git';
|
|
173
|
+
const res = await runGit(['--version']);
|
|
174
|
+
if (res.code === 0 && res.stdout.startsWith('git version')) {
|
|
175
|
+
return { check: { name, status: 'ok', detail: res.stdout.trim() }, available: true };
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
check: {
|
|
179
|
+
name,
|
|
180
|
+
status: 'warn',
|
|
181
|
+
detail: 'not runnable here — filesystem capture and forked worktrees will be off',
|
|
182
|
+
fix: 'install git to get fs snapshots and checkpoints; otherwise record with --no-fs',
|
|
183
|
+
},
|
|
184
|
+
available: false,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
async function checkWorkspace(cwd, gitAvailable) {
|
|
188
|
+
const name = 'git workspace';
|
|
189
|
+
const fix = 'git init here, or record from your project root — fs diffs are far more useful in a repo';
|
|
190
|
+
if (gitAvailable) {
|
|
191
|
+
const res = await runGit(['rev-parse', '--show-toplevel'], { cwd });
|
|
192
|
+
if (res.code === 0 && res.stdout.trim() !== '') {
|
|
193
|
+
return { name, status: 'ok', detail: res.stdout.trim() };
|
|
194
|
+
}
|
|
195
|
+
return { name, status: 'warn', detail: `${cwd} is not inside a git repo`, fix };
|
|
196
|
+
}
|
|
197
|
+
// Without git, answer the question from the filesystem rather than declining to answer it.
|
|
198
|
+
const root = await findRepoRoot(cwd);
|
|
199
|
+
return root === undefined
|
|
200
|
+
? { name, status: 'warn', detail: `${cwd} is not inside a git repo`, fix }
|
|
201
|
+
: { name, status: 'ok', detail: `${root} (found by .git; git itself is not runnable)` };
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Writes a real file and removes it. `.orca` is created lazily by `record`, so when it does not
|
|
205
|
+
* exist yet the directory that will have to hold it is what gets probed — and nothing is created.
|
|
206
|
+
*/
|
|
207
|
+
async function checkWritable(cwd) {
|
|
208
|
+
const name = '.orca writable';
|
|
209
|
+
const target = orcaDir(cwd);
|
|
210
|
+
const info = await stat(target).catch(() => null);
|
|
211
|
+
if (info !== null && !info.isDirectory()) {
|
|
212
|
+
return {
|
|
213
|
+
check: {
|
|
214
|
+
name,
|
|
215
|
+
status: 'fail',
|
|
216
|
+
detail: `${target} exists but is not a directory`,
|
|
217
|
+
fix: `move or remove ${target}; orca keeps every recorded run in it`,
|
|
218
|
+
},
|
|
219
|
+
probeDir: cwd,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const probeDir = info === null ? cwd : target;
|
|
223
|
+
const probe = join(probeDir, `.orca-doctor-${randomBytes(4).toString('hex')}.tmp`);
|
|
224
|
+
try {
|
|
225
|
+
await writeFile(probe, 'orca doctor', { mode: 0o600 });
|
|
226
|
+
await unlink(probe);
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
return {
|
|
230
|
+
check: {
|
|
231
|
+
name,
|
|
232
|
+
status: 'fail',
|
|
233
|
+
detail: `cannot write and clean up a file in ${probeDir}: ${messageOf(err)}`,
|
|
234
|
+
fix: `make ${probeDir} writable, or run orca from a directory you own`,
|
|
235
|
+
},
|
|
236
|
+
probeDir,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
check: {
|
|
241
|
+
name,
|
|
242
|
+
status: 'ok',
|
|
243
|
+
detail: info === null ? `${probeDir} is writable (.orca not created yet)` : `${target} is writable`,
|
|
244
|
+
},
|
|
245
|
+
probeDir,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
async function checkAgents(cwd) {
|
|
249
|
+
const name = 'agents detected';
|
|
250
|
+
const registry = defaultAdapters();
|
|
251
|
+
const found = [];
|
|
252
|
+
for (const id of registry.ids()) {
|
|
253
|
+
try {
|
|
254
|
+
if (await registry.get(id).detect(cwd))
|
|
255
|
+
found.push(id);
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// A detector that throws is a "no"; it must never take the diagnosis down with it.
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (found.length === 0) {
|
|
262
|
+
return {
|
|
263
|
+
name,
|
|
264
|
+
status: 'warn',
|
|
265
|
+
detail: 'none of the known agents were found on PATH or in this workspace',
|
|
266
|
+
fix: 'name the agent explicitly, or record any OpenAI-compatible one: orca record generic-openai -- <command>',
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return { name, status: 'ok', detail: found.join(', ') };
|
|
270
|
+
}
|
|
271
|
+
async function checkPort() {
|
|
272
|
+
const name = 'proxy port';
|
|
273
|
+
try {
|
|
274
|
+
const port = await bindEphemeral();
|
|
275
|
+
return { name, status: 'ok', detail: `bound 127.0.0.1:${port}, then released it` };
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
return {
|
|
279
|
+
name,
|
|
280
|
+
status: 'fail',
|
|
281
|
+
detail: `cannot bind a loopback port: ${messageOf(err)}`,
|
|
282
|
+
fix: 'orca records by proxying model traffic through 127.0.0.1 — allow loopback listeners (sandbox or firewall policy) before recording',
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function bindEphemeral() {
|
|
287
|
+
return new Promise((resolve, reject) => {
|
|
288
|
+
const server = createServer();
|
|
289
|
+
server.once('error', reject);
|
|
290
|
+
server.listen(0, '127.0.0.1', () => {
|
|
291
|
+
const address = server.address();
|
|
292
|
+
const port = typeof address === 'object' && address !== null ? address.port : 0;
|
|
293
|
+
server.close(() => {
|
|
294
|
+
if (port > 0)
|
|
295
|
+
resolve(port);
|
|
296
|
+
else
|
|
297
|
+
reject(new Error('listening socket reported no port'));
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
async function checkDisk(probeDir) {
|
|
303
|
+
const name = 'disk space';
|
|
304
|
+
if (typeof fsPromises.statfs !== 'function') {
|
|
305
|
+
return { name, status: 'ok', detail: 'skipped — statfs is unavailable on this node build' };
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
const fs = await fsPromises.statfs(probeDir);
|
|
309
|
+
const free = Number(fs.bavail) * Number(fs.bsize);
|
|
310
|
+
if (free < LOW_DISK_BYTES) {
|
|
311
|
+
return {
|
|
312
|
+
name,
|
|
313
|
+
status: 'warn',
|
|
314
|
+
detail: `${formatBytes(free)} free where .orca lives`,
|
|
315
|
+
fix: 'free some space — one recorded run can reach tens of megabytes: orca gc --older-than 7d',
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
return { name, status: 'ok', detail: `${formatBytes(free)} free` };
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
return { name, status: 'ok', detail: `skipped — statfs failed: ${messageOf(err)}` };
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async function checkRuns(cwd) {
|
|
325
|
+
const name = 'existing runs';
|
|
326
|
+
const runs = await listRuns(cwd);
|
|
327
|
+
if (runs.length === 0)
|
|
328
|
+
return { name, status: 'ok', detail: 'none recorded here yet' };
|
|
329
|
+
let bytes = 0;
|
|
330
|
+
for (const run of runs)
|
|
331
|
+
bytes += await runDirBytes(run.dir);
|
|
332
|
+
const detail = `${runs.length} run${runs.length === 1 ? '' : 's'}, ${formatBytes(bytes)}`;
|
|
333
|
+
if (bytes > BIG_STORE_BYTES) {
|
|
334
|
+
return {
|
|
335
|
+
name,
|
|
336
|
+
status: 'warn',
|
|
337
|
+
detail,
|
|
338
|
+
fix: 'orca gc --keep 20 --dry-run shows what could be reclaimed',
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
return { name, status: 'ok', detail };
|
|
342
|
+
}
|
|
343
|
+
async function findRepoRoot(from) {
|
|
344
|
+
let dir = from;
|
|
345
|
+
for (;;) {
|
|
346
|
+
const found = await stat(join(dir, '.git')).then(() => true, () => false);
|
|
347
|
+
if (found)
|
|
348
|
+
return dir;
|
|
349
|
+
const parent = dirname(dir);
|
|
350
|
+
if (parent === dir)
|
|
351
|
+
return undefined;
|
|
352
|
+
dir = parent;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function messageOf(err) {
|
|
356
|
+
return err instanceof Error ? err.message : String(err);
|
|
357
|
+
}
|
|
358
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ParsedArgs } from '../args.js';
|
|
2
|
+
import type { Output } from '../out.js';
|
|
3
|
+
export interface GcResult {
|
|
4
|
+
runsRemoved: number;
|
|
5
|
+
/** Scratch fork worktrees reclaimed alongside their runs. */
|
|
6
|
+
worktreesRemoved: number;
|
|
7
|
+
blobsRemoved: number;
|
|
8
|
+
bytesReclaimed: number;
|
|
9
|
+
}
|
|
10
|
+
/** Milliseconds for `7d`, `24h`, `30m`; null for anything it will not guess at. */
|
|
11
|
+
export declare function parseDuration(text: string): number | null;
|
|
12
|
+
/**
|
|
13
|
+
* `orca gc` — reclaim the space a trace store grows into.
|
|
14
|
+
*
|
|
15
|
+
* A run is append-only and content-addressed, so nothing inside one grows unboundedly. What grows
|
|
16
|
+
* is the number of runs, and the blobs left behind when a run died between writing a payload and
|
|
17
|
+
* writing the event that referenced it. Removing runs is therefore opt-in: with no retention flag
|
|
18
|
+
* this only sweeps that debris and reports what it would take if asked.
|
|
19
|
+
*/
|
|
20
|
+
export declare function gcCommand(args: ParsedArgs, out: Output, cwd?: string): Promise<GcResult>;
|
|
21
|
+
/** Every byte of file content under a run directory. Shared with `orca doctor`. */
|
|
22
|
+
export declare function runDirBytes(dir: string): Promise<number>;
|
|
23
|
+
export declare function formatBytes(bytes: number): string;
|
|
24
|
+
//# sourceMappingURL=gc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../src/commands/gc.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAcD,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMzD;AAuBD;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,SAAgB,GAClB,OAAO,CAAC,QAAQ,CAAC,CAyHnB;AA0JD,mFAAmF;AACnF,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAY9D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUjD"}
|