@haystackeditor/cli 0.15.20 → 0.15.21
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 +14 -0
- package/dist/commands/verify-core.d.ts +424 -0
- package/dist/commands/verify-core.js +753 -0
- package/dist/commands/verify-mcp.d.ts +8 -0
- package/dist/commands/verify-mcp.js +458 -0
- package/dist/commands/verify-ops.d.ts +158 -0
- package/dist/commands/verify-ops.js +1148 -0
- package/dist/commands/verify-reseal.d.ts +9 -0
- package/dist/commands/verify-reseal.js +148 -0
- package/dist/commands/verify-sandboxes.d.ts +95 -0
- package/dist/commands/verify-sandboxes.js +352 -0
- package/dist/commands/verify.d.ts +11 -0
- package/dist/commands/verify.js +97 -0
- package/dist/index.js +255 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.js +15 -0
- package/package.json +2 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { setVerifyToken, verifyAuthHeaders } from './verify-core.js';
|
|
6
|
+
const DEFAULT_CONTROL_PLANE = 'http://127.0.0.1:3000';
|
|
7
|
+
const SPECIMENS = new Set(['all', 'posthog-js-478', 'analytics-js-255']);
|
|
8
|
+
function openResults(url) {
|
|
9
|
+
const platform = process.platform;
|
|
10
|
+
const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'cmd' : 'xdg-open';
|
|
11
|
+
const args = platform === 'win32' ? ['/c', 'start', '', url] : [url];
|
|
12
|
+
const child = spawn(command, args, { detached: true, stdio: 'ignore' });
|
|
13
|
+
child.unref();
|
|
14
|
+
}
|
|
15
|
+
async function readIntentTranscript(path) {
|
|
16
|
+
if (!path)
|
|
17
|
+
return undefined;
|
|
18
|
+
const absolutePath = resolve(path);
|
|
19
|
+
try {
|
|
20
|
+
return await readFile(absolutePath, 'utf8');
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24
|
+
throw new Error(`Could not read intent transcript at ${absolutePath}: ${message}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function verifyCommand(specimenArg, options) {
|
|
28
|
+
const specimen = specimenArg || 'all';
|
|
29
|
+
if (!SPECIMENS.has(specimen)) {
|
|
30
|
+
throw new Error(`Unknown or unreproduced specimen "${specimen}". Choose all, posthog-js-478, or analytics-js-255.`);
|
|
31
|
+
}
|
|
32
|
+
setVerifyToken(options.token);
|
|
33
|
+
const server = (options.server || process.env.HAYSTACK_VERIFY_SERVER || DEFAULT_CONTROL_PLANE)
|
|
34
|
+
.replace(/\/+$/, '');
|
|
35
|
+
const intentTranscript = await readIntentTranscript(options.intentTranscript);
|
|
36
|
+
if (!options.json) {
|
|
37
|
+
console.error(chalk.dim('Preparing the cloud verification run…'));
|
|
38
|
+
}
|
|
39
|
+
let response;
|
|
40
|
+
try {
|
|
41
|
+
response = await fetch(`${server}/api/cloud-verifier/runs`, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: { 'content-type': 'application/json', ...verifyAuthHeaders() },
|
|
44
|
+
body: JSON.stringify({
|
|
45
|
+
specimen,
|
|
46
|
+
repoPath: options.repo ? resolve(options.repo) : undefined,
|
|
47
|
+
baseRef: options.base,
|
|
48
|
+
headRef: options.head,
|
|
49
|
+
intentTranscript,
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
55
|
+
throw new Error(`Could not reach the Haystack verifier at ${server}. Start the demo control plane with \`pnpm dev\` and retry. ${message}`);
|
|
56
|
+
}
|
|
57
|
+
const raw = await response.text();
|
|
58
|
+
if (response.status === 401) {
|
|
59
|
+
throw new Error(`Control plane at ${server} requires a token (HTTP 401). Pass --token or set HAYSTACK_VERIFY_TOKEN.`);
|
|
60
|
+
}
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
let reason = raw;
|
|
63
|
+
try {
|
|
64
|
+
reason = JSON.parse(raw).error || raw;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Preserve the response body when it is not JSON.
|
|
68
|
+
}
|
|
69
|
+
throw new Error(`Verifier rejected the run (${response.status}): ${reason}`);
|
|
70
|
+
}
|
|
71
|
+
let started;
|
|
72
|
+
try {
|
|
73
|
+
started = JSON.parse(raw);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
throw new Error('Verifier returned an invalid response.');
|
|
77
|
+
}
|
|
78
|
+
if (!started.runId || !started.resultsUrl) {
|
|
79
|
+
throw new Error('Verifier response is missing runId or resultsUrl.');
|
|
80
|
+
}
|
|
81
|
+
if (options.json) {
|
|
82
|
+
process.stdout.write(`${JSON.stringify({
|
|
83
|
+
schema_version: '1.0.0',
|
|
84
|
+
run_id: started.runId,
|
|
85
|
+
status: started.status,
|
|
86
|
+
results_url: started.resultsUrl,
|
|
87
|
+
}, null, 2)}\n`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log(`${chalk.green('✓')} Verification started: ${chalk.bold(started.runId)}`);
|
|
92
|
+
console.log(` ${chalk.dim('Results:')} ${chalk.cyan(started.resultsUrl)}`);
|
|
93
|
+
}
|
|
94
|
+
if (options.open !== false) {
|
|
95
|
+
openResults(started.resultsUrl);
|
|
96
|
+
}
|
|
97
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -48,6 +48,7 @@ import { schemaCommand, listSchemas } from './commands/schema-cmd.js';
|
|
|
48
48
|
import { registerWebhook, listWebhooks, rotateWebhookSecret, setWebhookEnabled, listDeliveries, replayDelivery, } from './commands/webhooks.js';
|
|
49
49
|
import { editRulesCommand, validateRulesCommand } from './commands/rules.js';
|
|
50
50
|
import { systemMapValidateCommand } from './commands/system-map.js';
|
|
51
|
+
import { verifyCommand } from './commands/verify.js';
|
|
51
52
|
import { headlessLoginCommand, headlessLogoutCommand, listTokensCommand, revokeTokenCommand, } from './commands/tokens.js';
|
|
52
53
|
// `mcp` is imported lazily inside its command action (below), NOT at the top level.
|
|
53
54
|
// It is the only module that pulls in `@modelcontextprotocol/sdk`; keeping it out of
|
|
@@ -164,6 +165,260 @@ program
|
|
|
164
165
|
.command('status')
|
|
165
166
|
.description('Check if .haystack.json exists and is valid')
|
|
166
167
|
.action(statusCommand);
|
|
168
|
+
const verify = program
|
|
169
|
+
.command('verify')
|
|
170
|
+
.description('Verify a change in matched cloud universes, or bisect a historical regression')
|
|
171
|
+
.argument('[specimen]', 'Launch specimen to run (default: all)')
|
|
172
|
+
.option('--repo <path>', 'Repository to analyze (defaults to the selected launch specimen)')
|
|
173
|
+
.option('--base <ref>', 'Base revision for a repository run')
|
|
174
|
+
.option('--head <ref>', 'Head revision for a repository run')
|
|
175
|
+
.option('--intent-transcript <file>', 'Coding-agent transcript used as intent evidence')
|
|
176
|
+
.option('--server <url>', 'Verifier control-plane URL (default: http://127.0.0.1:3000)')
|
|
177
|
+
.option('--token <token>', 'Bearer token for a control plane that requires auth (or HAYSTACK_VERIFY_TOKEN)')
|
|
178
|
+
.option('--no-open', 'Do not open the live results page')
|
|
179
|
+
.option('--json', 'Machine-readable run id, status, and results URL')
|
|
180
|
+
.addHelpText('after', `
|
|
181
|
+
Launch specimens:
|
|
182
|
+
posthog-js-478 Replay PostHog/posthog-js PR #478 in matched browser environments (latent-issue mode)
|
|
183
|
+
analytics-js-255 Identity routing regression with sandbox bisect
|
|
184
|
+
all Run each reproduced specimen as an isolated chapter (default)
|
|
185
|
+
|
|
186
|
+
The local control plane must be running. From the Haystack repository:
|
|
187
|
+
pnpm dev
|
|
188
|
+
|
|
189
|
+
Examples:
|
|
190
|
+
haystack verify posthog-js-478 # start a run, then:
|
|
191
|
+
haystack verify watch # block until it finishes (exit 1 = tests failed)
|
|
192
|
+
haystack verify status # one-screen verdicts + live environment URLs
|
|
193
|
+
haystack verify findings # every problem found, with the readings that moved
|
|
194
|
+
haystack verify show latest 3 # one test in full: readings, timeline, SDK log
|
|
195
|
+
haystack verify diff latest 3 # base-vs-head screenshot differ (1/2/3 views)
|
|
196
|
+
haystack verify open latest 3 --live # open a failing test's live environment
|
|
197
|
+
haystack verify cleanup latest 3 # retained-state deletion/reference status
|
|
198
|
+
haystack verify refork latest 3 attempt:fix-2 # checkpoint a durable child
|
|
199
|
+
haystack verify shell latest 3 # terminal into that live universe
|
|
200
|
+
haystack verify artifacts latest # pull screenshots / DOM / video locally
|
|
201
|
+
haystack verify sandboxes --kill-stale # reclaim E2B capacity from superseded runs
|
|
202
|
+
haystack verify reseal <specimen> # rebuild a sealed bundle + restart control plane
|
|
203
|
+
`)
|
|
204
|
+
.action((specimen, options) => runPublicCommand(() => verifyCommand(specimen, options), options.json));
|
|
205
|
+
const verifyRunArg = 'Run id, chapter id (newest run of that chapter), or "latest" (default)';
|
|
206
|
+
verify
|
|
207
|
+
.command('status')
|
|
208
|
+
.description('One screen for a run: per-test verdicts, counts, live environments and expiry')
|
|
209
|
+
.argument('[run]', verifyRunArg)
|
|
210
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
211
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
212
|
+
.option('--json', 'Machine-readable summary')
|
|
213
|
+
.action(async (run, _options, cmd) => {
|
|
214
|
+
// optsWithGlobals: the parent `verify` command also declares --json/--server
|
|
215
|
+
// and commander's default parsing lets it consume those flags even when
|
|
216
|
+
// they appear after the subcommand; the merged view (self wins) restores them.
|
|
217
|
+
const options = cmd.optsWithGlobals();
|
|
218
|
+
const { verifyStatusCommand } = await import('./commands/verify-ops.js');
|
|
219
|
+
return runPublicCommand(() => verifyStatusCommand(run, options), options.json);
|
|
220
|
+
});
|
|
221
|
+
verify
|
|
222
|
+
.command('watch')
|
|
223
|
+
.description('Follow a run until it finishes; exit 0 all passed, 1 tests failed, 2 run/infra broke')
|
|
224
|
+
.argument('[run]', verifyRunArg)
|
|
225
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
226
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
227
|
+
.option('--interval <seconds>', 'Poll interval (default 3)')
|
|
228
|
+
.option('--timeout <minutes>', 'Give up after this long (default: wait forever)')
|
|
229
|
+
.option('--json', 'Suppress streaming; print the final summary as JSON')
|
|
230
|
+
.action(async (run, _options, cmd) => {
|
|
231
|
+
const options = cmd.optsWithGlobals();
|
|
232
|
+
const { verifyWatchCommand } = await import('./commands/verify-ops.js');
|
|
233
|
+
return runPublicCommand(() => verifyWatchCommand(run, options), options.json);
|
|
234
|
+
});
|
|
235
|
+
verify
|
|
236
|
+
.command('list')
|
|
237
|
+
.description('Runs newest first: chapter, verdict counts, age (control plane, else disk)')
|
|
238
|
+
.option('--limit <n>', 'Max runs to show (default 20)')
|
|
239
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
240
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
241
|
+
.option('--json', 'Machine-readable list')
|
|
242
|
+
.action(async (_options, cmd) => {
|
|
243
|
+
const options = cmd.optsWithGlobals();
|
|
244
|
+
const { verifyListCommand } = await import('./commands/verify-ops.js');
|
|
245
|
+
return runPublicCommand(() => verifyListCommand(options), options.json);
|
|
246
|
+
});
|
|
247
|
+
verify
|
|
248
|
+
.command('findings')
|
|
249
|
+
.description('Every problem the run found, in one screen: what failed, which readings moved, live links')
|
|
250
|
+
.argument('[run]', verifyRunArg)
|
|
251
|
+
.option('--full', 'Print the judge\'s full explanation for each finding')
|
|
252
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
253
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
254
|
+
.option('--json', 'Machine-readable findings')
|
|
255
|
+
.action(async (run, _options, cmd) => {
|
|
256
|
+
const options = cmd.optsWithGlobals();
|
|
257
|
+
const { verifyFindingsCommand } = await import('./commands/verify-ops.js');
|
|
258
|
+
return runPublicCommand(() => verifyFindingsCommand(run, options), options.json);
|
|
259
|
+
});
|
|
260
|
+
verify
|
|
261
|
+
.command('open')
|
|
262
|
+
.description('Open a run artifact (screenshot/video/DOM) or a failing test\'s live environment')
|
|
263
|
+
.argument('<run>', verifyRunArg)
|
|
264
|
+
.argument('<target>', 'Test selector (ordinal/title), or an artifact file name (….png/.webm/.html)')
|
|
265
|
+
.option('--live', 'Open the test\'s live environment URL instead of an artifact')
|
|
266
|
+
.option('--side <side>', 'base or head (default head)')
|
|
267
|
+
.option('--step <n>', 'Open the screenshot captured after replay step n')
|
|
268
|
+
.option('--kind <kind>', 'screenshot | video | dom (default: video, else last screenshot)')
|
|
269
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
270
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
271
|
+
.action(async (run, target, _options, cmd) => {
|
|
272
|
+
const options = cmd.optsWithGlobals();
|
|
273
|
+
const { verifyOpenCommand } = await import('./commands/verify-ops.js');
|
|
274
|
+
return runPublicCommand(() => verifyOpenCommand(run, target, options));
|
|
275
|
+
});
|
|
276
|
+
verify
|
|
277
|
+
.command('diff')
|
|
278
|
+
.description('Open a base-vs-head screenshot differ for one test (1 what changed / 2 before / 3 after)')
|
|
279
|
+
.argument('<run>', verifyRunArg)
|
|
280
|
+
.argument('<cell>', 'Test selector: ordinal, cell id, or title substring')
|
|
281
|
+
.option('--step <n>', 'Compare only the screenshot captured after replay step n')
|
|
282
|
+
.option('--out <dir>', 'Where to build the diff page (default: a temp directory)')
|
|
283
|
+
.option('--no-open', 'Build the page but do not open it')
|
|
284
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
285
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
286
|
+
.option('--json', 'Machine-readable result (implies --no-open)')
|
|
287
|
+
.action(async (run, cell, _options, cmd) => {
|
|
288
|
+
const options = { ...cmd.optsWithGlobals(), ...cmd.opts() };
|
|
289
|
+
const { verifyDiffCommand } = await import('./commands/verify-ops.js');
|
|
290
|
+
return runPublicCommand(() => verifyDiffCommand(run, cell, options), options.json);
|
|
291
|
+
});
|
|
292
|
+
verify
|
|
293
|
+
.command('cleanup')
|
|
294
|
+
.description('Show exact retained-state references, provider deletion, and cleanup-SLO status')
|
|
295
|
+
.argument('<run>', verifyRunArg)
|
|
296
|
+
.argument('<cell>', 'Stable test selector: ordinal, riskCellId, or exact planner cellKey')
|
|
297
|
+
.option('--side <side>', 'base or head (default head)')
|
|
298
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
299
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
300
|
+
.option('--json', 'Machine-readable cleanup status')
|
|
301
|
+
.action(async (run, cell, _options, cmd) => {
|
|
302
|
+
const options = cmd.optsWithGlobals();
|
|
303
|
+
const { verifyCleanupCommand } = await import('./commands/verify-ops.js');
|
|
304
|
+
return runPublicCommand(() => verifyCleanupCommand(run, cell, options), options.json);
|
|
305
|
+
});
|
|
306
|
+
verify
|
|
307
|
+
.command('reopen')
|
|
308
|
+
.description('Attach fresh E2B compute to retained Archil state from a test or exact fork ID')
|
|
309
|
+
.argument('<source>', 'Run selector, or an exact archil:…:branch:… fork ID')
|
|
310
|
+
.argument('[cell]', 'Stable test selector for a run: ordinal, riskCellId, or exact planner cellKey')
|
|
311
|
+
.option('--side <side>', 'base or head (default head)')
|
|
312
|
+
.option('--open-browser', 'Open the restarted application in a browser')
|
|
313
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
314
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
315
|
+
.option('--json', 'Machine-readable attachment details')
|
|
316
|
+
.action(async (source, cell, _options, cmd) => {
|
|
317
|
+
const options = cmd.optsWithGlobals();
|
|
318
|
+
const { verifyReopenCommand } = await import('./commands/verify-ops.js');
|
|
319
|
+
return runPublicCommand(() => verifyReopenCommand(source, cell, options), options.json);
|
|
320
|
+
});
|
|
321
|
+
verify
|
|
322
|
+
.command('refork')
|
|
323
|
+
.description('Checkpoint a reopened Archil universe into a durable child fork')
|
|
324
|
+
.argument('<source>', 'Run selector, or an exact archil:…:branch:… parent fork ID')
|
|
325
|
+
.argument('<cell-or-child-key>', 'Stable test selector for a run, or child key for an exact fork ID')
|
|
326
|
+
.argument('[child-key]', 'Structured stable child key when source is a run')
|
|
327
|
+
.option('--side <side>', 'base or head (default head; only applies to a run)')
|
|
328
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
329
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
330
|
+
.option('--json', 'Machine-readable parent, child, checkpoint, and expiry')
|
|
331
|
+
.action(async (source, cellOrChildKey, childKey, _options, cmd) => {
|
|
332
|
+
const options = cmd.optsWithGlobals();
|
|
333
|
+
const { verifyReforkCommand } = await import('./commands/verify-ops.js');
|
|
334
|
+
return runPublicCommand(() => verifyReforkCommand(source, cellOrChildKey, childKey, options), options.json);
|
|
335
|
+
});
|
|
336
|
+
verify
|
|
337
|
+
.command('shell')
|
|
338
|
+
.description('Terminal into a failing test\'s live universe (e2b sandbox connect)')
|
|
339
|
+
.argument('<run>', verifyRunArg)
|
|
340
|
+
.argument('<cell>', 'Stable test selector: ordinal, riskCellId, or exact planner cellKey')
|
|
341
|
+
.option('--side <side>', 'base or head (default head; only failing tests keep a universe alive)')
|
|
342
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
343
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
344
|
+
.action(async (run, cell, _options, cmd) => {
|
|
345
|
+
const options = cmd.optsWithGlobals();
|
|
346
|
+
const { verifyShellCommand } = await import('./commands/verify-ops.js');
|
|
347
|
+
return runPublicCommand(() => verifyShellCommand(run, cell, options));
|
|
348
|
+
});
|
|
349
|
+
verify
|
|
350
|
+
.command('show')
|
|
351
|
+
.description('One test in full: finding, changed readings, request timeline, SDK log lines')
|
|
352
|
+
.argument('<run>', verifyRunArg)
|
|
353
|
+
.argument('<cell>', 'Test ordinal, cell id, planner cell key, or title substring')
|
|
354
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
355
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
356
|
+
.option('--no-timeline', 'Skip the request timeline')
|
|
357
|
+
.option('--json', 'Machine-readable detail')
|
|
358
|
+
.action(async (run, cell, _options, cmd) => {
|
|
359
|
+
const options = cmd.optsWithGlobals();
|
|
360
|
+
const { verifyShowCommand } = await import('./commands/verify-ops.js');
|
|
361
|
+
return runPublicCommand(() => verifyShowCommand(run, cell, options), options.json);
|
|
362
|
+
});
|
|
363
|
+
verify
|
|
364
|
+
.command('artifacts')
|
|
365
|
+
.description('Pull a run\'s screenshots, DOM snapshots and videos to a local directory')
|
|
366
|
+
.argument('<run>', verifyRunArg)
|
|
367
|
+
.option('--out <dir>', 'Destination (default: ./verify-artifacts/<runId>)')
|
|
368
|
+
.option('--cell <selector>', 'Only artifacts for one test (ordinal, cell id, or title substring)')
|
|
369
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
370
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
371
|
+
.option('--json', 'Machine-readable result')
|
|
372
|
+
.action(async (run, _options, cmd) => {
|
|
373
|
+
const options = cmd.optsWithGlobals();
|
|
374
|
+
const { verifyArtifactsCommand } = await import('./commands/verify-ops.js');
|
|
375
|
+
return runPublicCommand(() => verifyArtifactsCommand(run, options), options.json);
|
|
376
|
+
});
|
|
377
|
+
verify
|
|
378
|
+
.command('sandboxes')
|
|
379
|
+
.description('E2B sandbox inventory with run attribution; --kill-stale reclaims superseded ones')
|
|
380
|
+
.option('--kill-stale', 'Kill verifier-attributed sandboxes superseded by newer runs (never unattributed ones)')
|
|
381
|
+
.option('--kill-run <runId>', 'Kill every sandbox attributed to one run')
|
|
382
|
+
.option('--server <url>', 'Control-plane URL, preferred over direct E2B access (default: http://127.0.0.1:3000)')
|
|
383
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
384
|
+
.option('--json', 'Machine-readable inventory')
|
|
385
|
+
.action(async (_options, cmd) => {
|
|
386
|
+
const options = cmd.optsWithGlobals();
|
|
387
|
+
const { verifySandboxesCommand } = await import('./commands/verify-sandboxes.js');
|
|
388
|
+
return runPublicCommand(() => verifySandboxesCommand(options), options.json);
|
|
389
|
+
});
|
|
390
|
+
verify
|
|
391
|
+
.command('reseal')
|
|
392
|
+
.description('Rebuild a sealed specimen bundle (guarded) and restart the control plane onto it')
|
|
393
|
+
.argument('<specimen>', 'Specimen id, e.g. posthog-js-478')
|
|
394
|
+
.option('--source <dir>', 'Specimen source directory holding reseal.sh')
|
|
395
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
396
|
+
.option('--repo-root <path>', 'Repository holding demo/cloud-verifier (default: walk up from cwd)')
|
|
397
|
+
.option('--no-restart', 'Only rebuild the bundle; leave the running server on the old checkout')
|
|
398
|
+
.action(async (specimen, _options, cmd) => {
|
|
399
|
+
const options = cmd.optsWithGlobals();
|
|
400
|
+
const { verifyResealCommand } = await import('./commands/verify-reseal.js');
|
|
401
|
+
return runPublicCommand(() => verifyResealCommand(specimen, options));
|
|
402
|
+
});
|
|
403
|
+
verify
|
|
404
|
+
.command('mcp')
|
|
405
|
+
.description('Run a stdio MCP server exposing the verifier to agents (verify_start/verify_wait/…)')
|
|
406
|
+
.option('--server <url>', 'Control-plane URL (default: http://127.0.0.1:3000)')
|
|
407
|
+
.option('--token <token>', 'Bearer token for a control plane that requires auth (or HAYSTACK_VERIFY_TOKEN)')
|
|
408
|
+
.option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
|
|
409
|
+
.action(async (_options, cmd) => {
|
|
410
|
+
const options = cmd.optsWithGlobals();
|
|
411
|
+
try {
|
|
412
|
+
// Lazy import, same reasoning as the top-level `mcp` command: only this
|
|
413
|
+
// path needs @modelcontextprotocol/sdk.
|
|
414
|
+
const { runVerifyMcpServer } = await import('./commands/verify-mcp.js');
|
|
415
|
+
await runVerifyMcpServer(options);
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
console.error(chalk.red('verify mcp server failed:'), err instanceof Error ? err.message : err);
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
167
422
|
program
|
|
168
423
|
.command('login')
|
|
169
424
|
.description('Add or refresh a saved GitHub account (use --headless for CI tokens)')
|
package/dist/types.d.ts
CHANGED
|
@@ -719,11 +719,52 @@ declare const TriageConfigSchema: z.ZodObject<{
|
|
|
719
719
|
maxTurns?: Partial<Record<"code-review" | "rules-validator" | "intent-drift", number>> | undefined;
|
|
720
720
|
timeoutMs?: number | undefined;
|
|
721
721
|
}>;
|
|
722
|
+
declare const PreferencesSchema: z.ZodObject<{
|
|
723
|
+
/** Run analysis automatically for PR webhook events (default: true) */
|
|
724
|
+
automatic_analysis: z.ZodOptional<z.ZodBoolean>;
|
|
725
|
+
/** Enable sandbox-backed verification for this repository */
|
|
726
|
+
sandbox_enabled: z.ZodOptional<z.ZodBoolean>;
|
|
727
|
+
/** Enroll submitted PRs in auto-merge by default */
|
|
728
|
+
auto_merge: z.ZodOptional<z.ZodBoolean>;
|
|
729
|
+
/** Allow Haystack to attempt automatic fixes */
|
|
730
|
+
auto_fix: z.ZodOptional<z.ZodBoolean>;
|
|
731
|
+
}, "strip", z.ZodTypeAny, {
|
|
732
|
+
automatic_analysis?: boolean | undefined;
|
|
733
|
+
sandbox_enabled?: boolean | undefined;
|
|
734
|
+
auto_merge?: boolean | undefined;
|
|
735
|
+
auto_fix?: boolean | undefined;
|
|
736
|
+
}, {
|
|
737
|
+
automatic_analysis?: boolean | undefined;
|
|
738
|
+
sandbox_enabled?: boolean | undefined;
|
|
739
|
+
auto_merge?: boolean | undefined;
|
|
740
|
+
auto_fix?: boolean | undefined;
|
|
741
|
+
}>;
|
|
722
742
|
export declare const HaystackConfigSchema: z.ZodObject<{
|
|
723
743
|
/** Config version (must be "1") */
|
|
724
744
|
version: z.ZodLiteral<"1">;
|
|
725
745
|
/** Project name (for display) */
|
|
726
746
|
name: z.ZodOptional<z.ZodString>;
|
|
747
|
+
/** Repository-level Haystack behavior */
|
|
748
|
+
preferences: z.ZodOptional<z.ZodObject<{
|
|
749
|
+
/** Run analysis automatically for PR webhook events (default: true) */
|
|
750
|
+
automatic_analysis: z.ZodOptional<z.ZodBoolean>;
|
|
751
|
+
/** Enable sandbox-backed verification for this repository */
|
|
752
|
+
sandbox_enabled: z.ZodOptional<z.ZodBoolean>;
|
|
753
|
+
/** Enroll submitted PRs in auto-merge by default */
|
|
754
|
+
auto_merge: z.ZodOptional<z.ZodBoolean>;
|
|
755
|
+
/** Allow Haystack to attempt automatic fixes */
|
|
756
|
+
auto_fix: z.ZodOptional<z.ZodBoolean>;
|
|
757
|
+
}, "strip", z.ZodTypeAny, {
|
|
758
|
+
automatic_analysis?: boolean | undefined;
|
|
759
|
+
sandbox_enabled?: boolean | undefined;
|
|
760
|
+
auto_merge?: boolean | undefined;
|
|
761
|
+
auto_fix?: boolean | undefined;
|
|
762
|
+
}, {
|
|
763
|
+
automatic_analysis?: boolean | undefined;
|
|
764
|
+
sandbox_enabled?: boolean | undefined;
|
|
765
|
+
auto_merge?: boolean | undefined;
|
|
766
|
+
auto_fix?: boolean | undefined;
|
|
767
|
+
}>>;
|
|
727
768
|
/** Package manager override (auto-detected if not specified) */
|
|
728
769
|
package_manager: z.ZodOptional<z.ZodEnum<["npm", "pnpm", "yarn", "bun"]>>;
|
|
729
770
|
/** Development server configuration */
|
|
@@ -1315,6 +1356,12 @@ export declare const HaystackConfigSchema: z.ZodObject<{
|
|
|
1315
1356
|
root?: string | undefined;
|
|
1316
1357
|
depends_on?: string[] | undefined;
|
|
1317
1358
|
}> | undefined;
|
|
1359
|
+
preferences?: {
|
|
1360
|
+
automatic_analysis?: boolean | undefined;
|
|
1361
|
+
sandbox_enabled?: boolean | undefined;
|
|
1362
|
+
auto_merge?: boolean | undefined;
|
|
1363
|
+
auto_fix?: boolean | undefined;
|
|
1364
|
+
} | undefined;
|
|
1318
1365
|
package_manager?: "npm" | "pnpm" | "yarn" | "bun" | undefined;
|
|
1319
1366
|
dev_server?: {
|
|
1320
1367
|
command: string;
|
|
@@ -1442,6 +1489,12 @@ export declare const HaystackConfigSchema: z.ZodObject<{
|
|
|
1442
1489
|
root?: string | undefined;
|
|
1443
1490
|
depends_on?: string[] | undefined;
|
|
1444
1491
|
}> | undefined;
|
|
1492
|
+
preferences?: {
|
|
1493
|
+
automatic_analysis?: boolean | undefined;
|
|
1494
|
+
sandbox_enabled?: boolean | undefined;
|
|
1495
|
+
auto_merge?: boolean | undefined;
|
|
1496
|
+
auto_fix?: boolean | undefined;
|
|
1497
|
+
} | undefined;
|
|
1445
1498
|
package_manager?: "npm" | "pnpm" | "yarn" | "bun" | undefined;
|
|
1446
1499
|
dev_server?: {
|
|
1447
1500
|
command: string;
|
|
@@ -1559,6 +1612,7 @@ export type NetworkConfig = z.infer<typeof NetworkConfigSchema>;
|
|
|
1559
1612
|
export type SecretDeclaration = z.infer<typeof SecretDeclarationSchema>;
|
|
1560
1613
|
export type MergeQueueConfig = z.infer<typeof MergeQueueConfigSchema>;
|
|
1561
1614
|
export type TriageConfig = z.infer<typeof TriageConfigSchema>;
|
|
1615
|
+
export type Preferences = z.infer<typeof PreferencesSchema>;
|
|
1562
1616
|
/**
|
|
1563
1617
|
* Project detection results
|
|
1564
1618
|
*/
|
package/dist/types.js
CHANGED
|
@@ -280,6 +280,19 @@ const TriageConfigSchema = z.object({
|
|
|
280
280
|
timeoutMs: z.number().int().positive().optional(),
|
|
281
281
|
});
|
|
282
282
|
// =============================================================================
|
|
283
|
+
// REPOSITORY PREFERENCES
|
|
284
|
+
// =============================================================================
|
|
285
|
+
const PreferencesSchema = z.object({
|
|
286
|
+
/** Run analysis automatically for PR webhook events (default: true) */
|
|
287
|
+
automatic_analysis: z.boolean().optional(),
|
|
288
|
+
/** Enable sandbox-backed verification for this repository */
|
|
289
|
+
sandbox_enabled: z.boolean().optional(),
|
|
290
|
+
/** Enroll submitted PRs in auto-merge by default */
|
|
291
|
+
auto_merge: z.boolean().optional(),
|
|
292
|
+
/** Allow Haystack to attempt automatic fixes */
|
|
293
|
+
auto_fix: z.boolean().optional(),
|
|
294
|
+
});
|
|
295
|
+
// =============================================================================
|
|
283
296
|
// MAIN CONFIG
|
|
284
297
|
// =============================================================================
|
|
285
298
|
export const HaystackConfigSchema = z.object({
|
|
@@ -287,6 +300,8 @@ export const HaystackConfigSchema = z.object({
|
|
|
287
300
|
version: z.literal('1'),
|
|
288
301
|
/** Project name (for display) */
|
|
289
302
|
name: z.string().optional(),
|
|
303
|
+
/** Repository-level Haystack behavior */
|
|
304
|
+
preferences: PreferencesSchema.optional(),
|
|
290
305
|
/** Package manager override (auto-detected if not specified) */
|
|
291
306
|
package_manager: z.enum(['npm', 'pnpm', 'yarn', 'bun']).optional(),
|
|
292
307
|
/** Development server configuration */
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haystackeditor/cli",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.21",
|
|
4
4
|
"description": "Set up Haystack for your project — automated PR review, triage, and merge queue",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"haystack": "
|
|
7
|
+
"haystack": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "rm -rf dist && tsc && cp -r src/assets dist/assets",
|