@haystackeditor/cli 0.15.21 → 0.15.23

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/index.js CHANGED
@@ -187,7 +187,9 @@ The local control plane must be running. From the Haystack repository:
187
187
  pnpm dev
188
188
 
189
189
  Examples:
190
+ haystack verify hosted start owner/repo --base <sha> --head <sha>
190
191
  haystack verify posthog-js-478 # start a run, then:
192
+ haystack verify reproducibility posthog-js-478 --runs 3
191
193
  haystack verify watch # block until it finishes (exit 1 = tests failed)
192
194
  haystack verify status # one-screen verdicts + live environment URLs
193
195
  haystack verify findings # every problem found, with the readings that moved
@@ -203,6 +205,95 @@ Examples:
203
205
  `)
204
206
  .action((specimen, options) => runPublicCommand(() => verifyCommand(specimen, options), options.json));
205
207
  const verifyRunArg = 'Run id, chapter id (newest run of that chapter), or "latest" (default)';
208
+ const hostedVerify = verify
209
+ .command('hosted')
210
+ .description('Run the production Cloud Verifier against exact GitHub commits');
211
+ hostedVerify
212
+ .command('start')
213
+ .description('Start an authenticated production verification and wait for its terminal result')
214
+ .argument('<repository>', 'Exact GitHub owner/repository name')
215
+ // `verify` already owns --base/--head for the local specimen launcher.
216
+ // Re-declaring these as Commander requiredOption values makes the parent
217
+ // consume them and the child then reject a valid invocation. Runtime
218
+ // validation below accepts the inherited values and still fails closed.
219
+ .option('--base <sha>', 'Required: exact 40-character lowercase base commit SHA')
220
+ .option('--head <sha>', 'Required: exact 40-character lowercase head commit SHA')
221
+ .option('--idempotency-key <key>', 'Stable retry key (default: deterministic from repository and commits)')
222
+ .option('--account <login>', 'Use a specific saved Haystack account')
223
+ .option('--no-wait', 'Return after the production workflow is queued')
224
+ .option('--interval <seconds>', 'Polling interval while waiting (default 5)')
225
+ .option('--timeout <minutes>', 'Maximum wait (default 35)')
226
+ .option('--json', 'Versioned machine-readable run status and terminal output')
227
+ .addHelpText('after', `
228
+ The default idempotency key makes an exact retry return the original run. Pass
229
+ --idempotency-key with a new stable key only when you intentionally want a
230
+ fresh production execution of the same commit pair.
231
+
232
+ Examples:
233
+ haystack verify hosted start owner/repo --base <sha> --head <sha>
234
+ haystack verify hosted start owner/repo --base <sha> --head <sha> --no-wait --json
235
+ `)
236
+ .action(async (repository, _options, cmd) => {
237
+ const options = { ...cmd.optsWithGlobals(), ...cmd.opts() };
238
+ const { verifyHostedStartCommand } = await import('./commands/verify-hosted.js');
239
+ return runPublicCommand(() => verifyHostedStartCommand(repository, options), options.json);
240
+ });
241
+ hostedVerify
242
+ .command('status')
243
+ .description('Read an owned production verification, optionally waiting until terminal')
244
+ .argument('<run-id>', 'Hosted run id (cv_ followed by 48 lowercase hex characters)')
245
+ .option('--account <login>', 'Use a specific saved Haystack account')
246
+ .option('--wait', 'Keep polling until the run reaches a terminal status')
247
+ .option('--interval <seconds>', 'Polling interval while waiting (default 5)')
248
+ .option('--timeout <minutes>', 'Maximum wait (default 35)')
249
+ .option('--json', 'Versioned machine-readable run status and terminal output')
250
+ .addHelpText('after', `
251
+ Example:
252
+ haystack verify hosted status cv_<48 lowercase hex characters> --wait --json
253
+ `)
254
+ .action(async (runId, _options, cmd) => {
255
+ const options = { ...cmd.optsWithGlobals(), ...cmd.opts() };
256
+ const { verifyHostedStatusCommand } = await import('./commands/verify-hosted.js');
257
+ return runPublicCommand(() => verifyHostedStatusCommand(runId, options), options.json);
258
+ });
259
+ hostedVerify
260
+ .command('mcp')
261
+ .description('Run a stdio MCP server for the production verifier lifecycle')
262
+ .option('--account <login>', 'Default saved Haystack account for status and wait tools')
263
+ .addHelpText('after', `
264
+ Tools: verify_start, verify_status, verify_wait.
265
+ No credential, repository source, provider metadata, or sandbox mutation tool
266
+ is exposed through this production server.
267
+ `)
268
+ .action(async (_options, cmd) => {
269
+ const options = { ...cmd.optsWithGlobals(), ...cmd.opts() };
270
+ try {
271
+ const { runHostedVerifyMcpServer } = await import('./commands/verify-hosted-mcp.js');
272
+ await runHostedVerifyMcpServer(options);
273
+ }
274
+ catch (err) {
275
+ console.error(chalk.red('hosted verify mcp server failed:'), err instanceof Error ? err.message : err);
276
+ process.exit(1);
277
+ }
278
+ });
279
+ verify
280
+ .command('reproducibility')
281
+ .description('Separate frozen-plan drift from executor/judgment nondeterminism')
282
+ .argument('<specimen>', 'Specimen id, e.g. posthog-js-478')
283
+ .option('--runs <n>', 'Number of sequential runs to compare (default 3; min 2, max 20)')
284
+ .option('--scope <scope>', 'executor replays one frozen plan (default); planner/full independently replan each run', 'executor')
285
+ .option('--plan-source <runId>', 'executor scope: replay this existing structured frozen plan for every run')
286
+ .option('--interval <seconds>', 'Polling interval for each run (default 3)')
287
+ .option('--timeout <minutes>', 'Per-run timeout (default: wait forever)')
288
+ .option('--server <url>', 'Verifier control-plane URL (default: http://127.0.0.1:3000)')
289
+ .option('--token <token>', 'Bearer token for a control plane that requires auth (or HAYSTACK_VERIFY_TOKEN)')
290
+ .option('--repo-root <path>', 'Repository holding .haystack/verify (default: walk up from cwd)')
291
+ .option('--json', 'Machine-readable stable/unstable cell report')
292
+ .action(async (specimen, commandOptions, cmd) => {
293
+ const options = { ...cmd.optsWithGlobals(), ...commandOptions };
294
+ const { verifyReproducibilityCommand } = await import('./commands/verify-reproducibility.js');
295
+ return runPublicCommand(() => verifyReproducibilityCommand(specimen, options), options.json);
296
+ });
206
297
  verify
207
298
  .command('status')
208
299
  .description('One screen for a run: per-test verdicts, counts, live environments and expiry')
package/dist/schema.d.ts CHANGED
@@ -19,6 +19,7 @@ export declare const SCHEMA_VERSIONS: {
19
19
  readonly traces: "1.0.0";
20
20
  readonly submit: "1.0.0";
21
21
  readonly action: "1.0.0";
22
+ readonly 'cloud-verifier': "1.0.0";
22
23
  readonly error: "1.0.0";
23
24
  };
24
25
  export type SchemaName = keyof typeof SCHEMA_VERSIONS;
package/dist/schema.js CHANGED
@@ -19,6 +19,7 @@ export const SCHEMA_VERSIONS = {
19
19
  traces: '1.0.0',
20
20
  submit: '1.0.0',
21
21
  action: '1.0.0',
22
+ 'cloud-verifier': '1.0.0',
22
23
  error: '1.0.0',
23
24
  };
24
25
  /** Wrap a payload with the `schema_version` envelope. */
@@ -27,7 +27,7 @@ export declare class HaystackApiError extends Error {
27
27
  * class it failed with, and the next command to run.
28
28
  */
29
29
  export declare function classifyHttpError(response: Response, context: string): Promise<HaystackApiError>;
30
- export declare function haystackJson<T>(path: string, token: string, init?: RequestInit): Promise<T>;
30
+ export declare function haystackJson<T>(path: string, token: string, init?: RequestInit, acceptedStatuses?: readonly number[]): Promise<T>;
31
31
  export declare function fetchCliIdentity(token: string): Promise<CliIdentity>;
32
32
  export declare function fetchHaystackInstallations(token: string): Promise<UserInstallation[]>;
33
33
  export declare function fetchGitHubJson<T>(path: string, token: string, init?: RequestInit): Promise<T>;
@@ -69,12 +69,12 @@ export async function classifyHttpError(response, context) {
69
69
  }
70
70
  return new HaystackApiError(response.status, `${context}: unexpected response (HTTP ${response.status})${suffix}.`);
71
71
  }
72
- export async function haystackJson(path, token, init = {}) {
72
+ export async function haystackJson(path, token, init = {}, acceptedStatuses = []) {
73
73
  const response = await fetch(`${HAYSTACK_API_BASE}${path}`, {
74
74
  ...init,
75
75
  headers: headers(token, init.headers),
76
76
  });
77
- if (!response.ok) {
77
+ if (!response.ok && !acceptedStatuses.includes(response.status)) {
78
78
  throw await classifyHttpError(response, `Haystack API ${path}`);
79
79
  }
80
80
  return response.json();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haystackeditor/cli",
3
- "version": "0.15.21",
3
+ "version": "0.15.23",
4
4
  "description": "Set up Haystack for your project — automated PR review, triage, and merge queue",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,96 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://docs.haystackeditor.com/cli/schemas/cloud-verifier.v1.json",
4
+ "title": "Haystack hosted Cloud Verifier result",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schema_version",
9
+ "operation",
10
+ "run_id",
11
+ "status"
12
+ ],
13
+ "properties": {
14
+ "schema_version": {
15
+ "const": "1.0.0"
16
+ },
17
+ "operation": {
18
+ "enum": [
19
+ "start",
20
+ "status"
21
+ ]
22
+ },
23
+ "run_id": {
24
+ "type": "string",
25
+ "pattern": "^cv_[0-9a-f]{48}$"
26
+ },
27
+ "status": {
28
+ "type": "string",
29
+ "pattern": "^[a-z][a-z0-9_-]{1,31}$"
30
+ },
31
+ "repository": {
32
+ "type": "string",
33
+ "pattern": "^[A-Za-z0-9_.-]{1,100}/[A-Za-z0-9_.-]{1,100}$"
34
+ },
35
+ "base_commit": {
36
+ "type": "string",
37
+ "pattern": "^[0-9a-f]{40}$"
38
+ },
39
+ "head_commit": {
40
+ "type": "string",
41
+ "pattern": "^[0-9a-f]{40}$"
42
+ },
43
+ "created_at": {
44
+ "type": "string",
45
+ "format": "date-time"
46
+ },
47
+ "completed_at": {
48
+ "type": "string",
49
+ "format": "date-time"
50
+ },
51
+ "account": {
52
+ "type": "string",
53
+ "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$"
54
+ },
55
+ "idempotency_key": {
56
+ "type": "string",
57
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$"
58
+ },
59
+ "next_command": {
60
+ "type": "string"
61
+ },
62
+ "timed_out": {
63
+ "type": "boolean"
64
+ },
65
+ "output": {
66
+ "type": "object"
67
+ },
68
+ "error": {
69
+ "type": "string"
70
+ }
71
+ },
72
+ "allOf": [
73
+ {
74
+ "if": {
75
+ "properties": {
76
+ "status": {
77
+ "const": "complete"
78
+ }
79
+ },
80
+ "required": [
81
+ "status"
82
+ ]
83
+ },
84
+ "then": {
85
+ "required": [
86
+ "repository",
87
+ "base_commit",
88
+ "head_commit",
89
+ "created_at",
90
+ "completed_at",
91
+ "output"
92
+ ]
93
+ }
94
+ }
95
+ ]
96
+ }