@haystackeditor/cli 0.15.28 → 0.15.30

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.
@@ -1,8 +1,9 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
2
3
  import chalk from 'chalk';
3
4
  import { withSchema } from '../schema.js';
4
5
  import { resolveAuthContext } from '../utils/auth.js';
5
- import { HaystackApiError, haystackJson } from '../utils/haystack-api.js';
6
+ import { classifyHttpError, HaystackApiError, haystackApiUrl, haystackJson } from '../utils/haystack-api.js';
6
7
  const REPOSITORY_COMPONENT = /^[A-Za-z0-9_.-]{1,100}$/;
7
8
  const COMMIT_SHA = /^[0-9a-f]{40}$/;
8
9
  const IDEMPOTENCY_KEY = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
@@ -11,6 +12,12 @@ const STATUS = /^[a-z][a-z0-9_-]{1,31}$/;
11
12
  const TERMINAL_STATUSES = new Set(['complete', 'errored', 'expired', 'terminated']);
12
13
  const DEFAULT_INTERVAL_SECONDS = 5;
13
14
  const DEFAULT_TIMEOUT_MINUTES = 35;
15
+ const INTENT_FIELDS = [
16
+ 'problem',
17
+ 'goal',
18
+ 'intended_outcomes',
19
+ ];
20
+ const FLEET_PROJECTION_VERSION = 'dv-cloud-verifier-fleet-runner-projection-v1';
14
21
  function requireRecord(value, context) {
15
22
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
16
23
  throw new Error(`${context} returned an invalid response object.`);
@@ -74,12 +81,67 @@ export function parseHostedRunState(value, expectedRunId) {
74
81
  ? undefined
75
82
  : requireRecord(outputValue, 'Hosted verifier output');
76
83
  const resultValue = response.result;
77
- const result = resultValue === undefined
84
+ const fleet = response.provider === 'haystack';
85
+ if (response.provider !== undefined && !fleet) {
86
+ throw new Error('Hosted verifier returned an unsupported provider.');
87
+ }
88
+ if (fleet && response.version !== FLEET_PROJECTION_VERSION) {
89
+ throw new Error('Hosted verifier returned an unsupported fleet projection version.');
90
+ }
91
+ const result = fleet || resultValue === undefined
78
92
  ? undefined
79
93
  : parseHostedRunnerResult(resultValue);
80
94
  const createdAt = optionalTimestamp(response, 'createdAt');
81
95
  const completedAt = optionalTimestamp(response, 'completedAt');
82
- if (status === 'complete') {
96
+ let progress;
97
+ let fleetResult;
98
+ if (fleet) {
99
+ if (response.publicRunId !== runId || !repository || !baseCommit || !headCommit || !createdAt || output) {
100
+ throw new Error('Hosted fleet projection is missing its immutable run identity.');
101
+ }
102
+ if (response.progress !== undefined && response.progress !== null) {
103
+ progress = requireRecord(response.progress, 'Hosted fleet progress');
104
+ if (progress.version !== 'dv-verify-progress-v1' || progress.publicRunId !== runId) {
105
+ throw new Error('Hosted fleet progress does not match the requested run.');
106
+ }
107
+ requireString(progress, 'stage');
108
+ requireString(progress, 'status');
109
+ }
110
+ if (resultValue !== undefined && resultValue !== null) {
111
+ fleetResult = requireRecord(resultValue, 'Hosted fleet result');
112
+ const mapping = requireRecord(fleetResult.mapping, 'Hosted fleet result mapping');
113
+ const provenance = requireRecord(fleetResult.provenance, 'Hosted fleet provenance');
114
+ if (fleetResult.version !== 'dv-verify-result-v1' || fleetResult.provider !== 'haystack'
115
+ || mapping.publicRunId !== runId || provenance.baseSha !== baseCommit || provenance.headSha !== headCommit) {
116
+ throw new Error('Hosted fleet result does not match the requested run and commits.');
117
+ }
118
+ // CONTRACT-GAP: P0 currently couples its canonical parsers to runner-only
119
+ // modules outside this published CLI. The server validates the complete
120
+ // P0 result; the CLI only checks the fields it presents, retaining the
121
+ // original document instead of defining a second P0 wire contract.
122
+ requireString(fleetResult, 'lifecycle', /^(complete|failed|cancelled|budget-stopped|account-action)$/);
123
+ requireString(fleetResult, 'runHealth', /^(healthy|degraded|infrastructure-error|not-executed)$/);
124
+ const cleanup = requireRecord(fleetResult.cleanup, 'Hosted fleet cleanup');
125
+ requireString(cleanup, 'status', /^(complete|pending)$/);
126
+ if (!Array.isArray(cleanup.pending) || !Array.isArray(fleetResult.findings)) {
127
+ throw new Error('Hosted fleet result has invalid findings or cleanup.');
128
+ }
129
+ for (const value of fleetResult.findings) {
130
+ const finding = requireRecord(value, 'Hosted fleet finding');
131
+ requireString(finding, 'findingId');
132
+ requireString(finding, 'caseId');
133
+ requireString(finding, 'judgment');
134
+ }
135
+ requireString(provenance, 'intent', /^(user|inferred)$/);
136
+ if (status === 'complete' && fleetResult.lifecycle !== 'complete') {
137
+ throw new Error('Hosted fleet lifecycle disagrees with its completed job.');
138
+ }
139
+ }
140
+ if (status === 'complete' && !fleetResult) {
141
+ throw new Error('Hosted fleet run completed without a result receipt.');
142
+ }
143
+ }
144
+ else if (status === 'complete') {
83
145
  // Two terminal shapes exist: the Workflow receipt envelope (`output`)
84
146
  // and the runner-queue result envelope (`result`). Exactly one must
85
147
  // validate.
@@ -113,6 +175,7 @@ export function parseHostedRunState(value, expectedRunId) {
113
175
  completedAt,
114
176
  output,
115
177
  result,
178
+ ...(fleet ? { provider: 'haystack', version: FLEET_PROJECTION_VERSION, progress, fleetResult } : {}),
116
179
  error: optionalString(response, 'error'),
117
180
  };
118
181
  }
@@ -159,11 +222,67 @@ function requireCommit(value, flag) {
159
222
  }
160
223
  return value;
161
224
  }
162
- export function defaultHostedIdempotencyKey(repository, baseCommit, headCommit, replayPlanFrom) {
225
+ function requireRevision(value, flag) {
226
+ if (typeof value !== 'string' || value.length === 0 || value.length > 1_024 || /[\u0000-\u0020\u007f]/.test(value)) {
227
+ throw new Error(`${flag} must be a non-empty Git revision of at most 1024 characters without whitespace or control characters.`);
228
+ }
229
+ return value;
230
+ }
231
+ export function readHostedIntentFile(intentFile) {
232
+ if (typeof intentFile !== 'string' || intentFile.length === 0) {
233
+ throw new Error('--intent-file is required and must name a JSON file containing exactly problem, goal, and intended_outcomes.');
234
+ }
235
+ let source;
236
+ try {
237
+ source = readFileSync(intentFile, 'utf8');
238
+ }
239
+ catch (error) {
240
+ const reason = error instanceof Error ? error.message : String(error);
241
+ throw new Error(`Could not read --intent-file "${intentFile}": ${reason}`);
242
+ }
243
+ let parsed;
244
+ try {
245
+ parsed = JSON.parse(source);
246
+ }
247
+ catch {
248
+ throw new Error(`--intent-file "${intentFile}" must contain valid JSON.`);
249
+ }
250
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
251
+ throw new Error(`--intent-file "${intentFile}" must contain exactly problem, goal, and intended_outcomes.`);
252
+ }
253
+ const record = parsed;
254
+ const unknownField = Object.keys(record)
255
+ .find(field => !INTENT_FIELDS.includes(field));
256
+ if (unknownField) {
257
+ throw new Error(`--intent-file "${intentFile}" contains unknown field "${unknownField}"; expected exactly problem, goal, and intended_outcomes.`);
258
+ }
259
+ for (const field of ['problem', 'goal']) {
260
+ if (typeof record[field] !== 'string' || record[field].trim().length === 0) {
261
+ throw new Error(`--intent-file "${intentFile}" field "${field}" must be a non-empty string.`);
262
+ }
263
+ }
264
+ const outcomes = record.intended_outcomes;
265
+ if (!Array.isArray(outcomes)
266
+ || outcomes.length === 0
267
+ || outcomes.some(item => typeof item !== 'string' || item.trim().length === 0)) {
268
+ throw new Error(`--intent-file "${intentFile}" field "intended_outcomes" must be a non-empty array of non-empty strings.`);
269
+ }
270
+ if (Object.keys(record).length !== INTENT_FIELDS.length) {
271
+ throw new Error(`--intent-file "${intentFile}" must contain exactly problem, goal, and intended_outcomes.`);
272
+ }
273
+ return {
274
+ problem: record.problem,
275
+ goal: record.goal,
276
+ intended_outcomes: record.intended_outcomes,
277
+ };
278
+ }
279
+ export function defaultHostedIdempotencyKey(repository, baseCommit, headCommit, replayPlanFrom, fleetRequest) {
163
280
  const digest = createHash('sha256')
164
- .update(JSON.stringify(replayPlanFrom
165
- ? [repository, baseCommit, headCommit, replayPlanFrom]
166
- : [repository, baseCommit, headCommit]))
281
+ .update(JSON.stringify(fleetRequest
282
+ ? [fleetRequest.provider, repository, baseCommit, headCommit, fleetRequest.intent ?? null]
283
+ : replayPlanFrom
284
+ ? [repository, baseCommit, headCommit, replayPlanFrom]
285
+ : [repository, baseCommit, headCommit]))
167
286
  .digest('hex');
168
287
  return `cli:${digest}`;
169
288
  }
@@ -190,40 +309,78 @@ export function hostedPollingOptions(interval, timeout, onStatus) {
190
309
  };
191
310
  }
192
311
  export async function startHostedVerification(repositoryArg, options, token) {
193
- const repository = parseHostedRepository(repositoryArg);
194
- const baseCommit = requireCommit(options.base, '--base');
195
- const headCommit = requireCommit(options.head, '--head');
196
- const replayPlanFrom = options.replayPlanFrom;
197
- if (replayPlanFrom !== undefined && !RUN_ID.test(replayPlanFrom)) {
198
- throw new Error('--replay-plan-from must match cv_ followed by 48 lowercase hex characters.');
199
- }
200
- const idempotencyKey = requireIdempotencyKey(options.idempotencyKey
201
- ?? defaultHostedIdempotencyKey(repository.fullName, baseCommit, headCommit, replayPlanFrom));
202
- const response = await haystackJson('/api/agent/cloud-verifier/runs', token, {
312
+ const { repository, launchBody, idempotencyKey } = prepareHostedLaunch(repositoryArg, options);
313
+ const path = '/api/agent/cloud-verifier/runs';
314
+ const response = await fetch(haystackApiUrl(path), {
203
315
  method: 'POST',
204
- headers: { 'Content-Type': 'application/json' },
205
- body: JSON.stringify({
206
- owner: repository.owner,
207
- repository: repository.repository,
208
- baseCommit,
209
- headCommit,
210
- idempotencyKey,
211
- ...(replayPlanFrom
212
- ? { replayPlanFromRunId: replayPlanFrom }
213
- : {}),
214
- }),
316
+ headers: {
317
+ Authorization: `Bearer ${token}`,
318
+ Accept: 'application/json',
319
+ 'User-Agent': 'Haystack-CLI',
320
+ 'Content-Type': 'application/json',
321
+ },
322
+ body: JSON.stringify(launchBody),
215
323
  });
216
- const state = parseHostedRunState(response);
324
+ if (!response.ok) {
325
+ // Keep the shared HTTP classification and raw-body fallback while exposing
326
+ // the launch service's additional explanation for a retryable failure.
327
+ const body = response.status === 503
328
+ ? await response.clone().json().catch(() => null)
329
+ : null;
330
+ const error = await classifyHttpError(response, `Haystack API ${path}`);
331
+ if (body && typeof body === 'object' && 'detail' in body
332
+ && typeof body.detail === 'string' && body.detail.trim()) {
333
+ error.message += ` Details: ${body.detail}`;
334
+ }
335
+ throw error;
336
+ }
337
+ const state = parseHostedRunState(await response.json());
338
+ if (options.provider === 'haystack' && state.provider !== 'haystack') {
339
+ throw new Error('Hosted verifier did not acknowledge the fleet-pinned request.');
340
+ }
217
341
  return {
218
342
  state: {
219
343
  ...state,
220
344
  repository: state.repository ?? repository.fullName,
221
- baseCommit: state.baseCommit ?? baseCommit,
222
- headCommit: state.headCommit ?? headCommit,
345
+ baseCommit: state.baseCommit ?? launchBody.baseCommit,
346
+ headCommit: state.headCommit ?? launchBody.headCommit,
223
347
  },
224
348
  idempotencyKey,
225
349
  };
226
350
  }
351
+ function prepareHostedLaunch(repositoryArg, options) {
352
+ const repository = parseHostedRepository(repositoryArg);
353
+ const fleet = options.provider === 'haystack';
354
+ const validateRevision = fleet ? requireRevision : requireCommit;
355
+ const baseCommit = validateRevision(options.base, '--base');
356
+ const headCommit = validateRevision(options.head, '--head');
357
+ const intent = fleet && options.intentFile === undefined ? undefined : readHostedIntentFile(options.intentFile);
358
+ if (fleet && intent && [intent.problem, intent.goal, ...intent.intended_outcomes].some(value => /[\u0000-\u001f\u007f]/.test(value))) {
359
+ throw new Error('Fleet --intent-file fields must be single-line text without control characters.');
360
+ }
361
+ const replayPlanFrom = options.replayPlanFrom;
362
+ if (fleet && replayPlanFrom !== undefined) {
363
+ throw new Error('--replay-plan-from is available through the explicit hosted start alias.');
364
+ }
365
+ if (replayPlanFrom !== undefined && !RUN_ID.test(replayPlanFrom)) {
366
+ throw new Error('--replay-plan-from must match cv_ followed by 48 lowercase hex characters.');
367
+ }
368
+ const idempotencyKey = requireIdempotencyKey(options.idempotencyKey
369
+ ?? defaultHostedIdempotencyKey(repository.fullName, baseCommit, headCommit, replayPlanFrom, fleet ? { provider: 'haystack', intent } : undefined));
370
+ const launchBody = {
371
+ ...(fleet ? { provider: 'haystack' } : {}),
372
+ owner: repository.owner,
373
+ repository: repository.repository,
374
+ baseCommit,
375
+ headCommit,
376
+ idempotencyKey,
377
+ ...(intent ? { intent } : {}),
378
+ ...(replayPlanFrom
379
+ ? { replayPlanFromRunId: replayPlanFrom }
380
+ : {}),
381
+ };
382
+ return { repository, launchBody, idempotencyKey };
383
+ }
227
384
  export async function getHostedVerification(runId, token) {
228
385
  if (!RUN_ID.test(runId)) {
229
386
  throw new Error('Run id must match cv_ followed by 48 lowercase hex characters.');
@@ -246,12 +403,13 @@ function delay(milliseconds) {
246
403
  }
247
404
  export async function waitForHostedVerification(runId, token, options) {
248
405
  const startedAt = Date.now();
249
- let lastStatus;
406
+ let lastProgress;
250
407
  for (;;) {
251
408
  const state = await getHostedVerification(runId, token);
252
- if (state.status !== lastStatus) {
409
+ const progressKey = JSON.stringify([state.status, state.progress?.sequence, state.progress?.stage, state.progress?.status]);
410
+ if (progressKey !== lastProgress) {
253
411
  options.onStatus?.(state);
254
- lastStatus = state.status;
412
+ lastProgress = progressKey;
255
413
  }
256
414
  if (TERMINAL_STATUSES.has(state.status)) {
257
415
  return { state, timedOut: false };
@@ -281,6 +439,9 @@ export function buildHostedJson(operation, state, extras = {}) {
281
439
  ...(extras.timedOut !== undefined ? { timed_out: extras.timedOut } : {}),
282
440
  ...(state.output ? { output: state.output } : {}),
283
441
  ...(state.result ? { result: state.result } : {}),
442
+ ...(state.provider ? { provider: state.provider, version: state.version } : {}),
443
+ ...(state.progress ? { progress: state.progress } : {}),
444
+ ...(state.fleetResult ? { result: state.fleetResult } : {}),
284
445
  ...(state.error ? { error: state.error } : {}),
285
446
  });
286
447
  }
@@ -300,11 +461,13 @@ function printHostedState(operation, state, options) {
300
461
  }), null, 2)}\n`);
301
462
  return;
302
463
  }
303
- const icon = state.status === 'complete'
304
- ? chalk.green('✓')
305
- : TERMINAL_STATUSES.has(state.status)
306
- ? chalk.red('✗')
307
- : chalk.cyan('•');
464
+ const icon = state.provider === 'haystack' && state.status === 'complete'
465
+ ? chalk.cyan('•')
466
+ : state.status === 'complete'
467
+ ? chalk.green('✓')
468
+ : TERMINAL_STATUSES.has(state.status)
469
+ ? chalk.red('✗')
470
+ : chalk.cyan('•');
308
471
  console.log(`${icon} Hosted verification ${chalk.bold(state.runId)}: ${state.status}`);
309
472
  if (state.repository) {
310
473
  console.log(` ${chalk.dim('Repository:')} ${state.repository}`);
@@ -321,6 +484,22 @@ function printHostedState(operation, state, options) {
321
484
  }
322
485
  console.log(` ${chalk.dim('Artifacts:')} ${state.result.outputLocation}`);
323
486
  }
487
+ if (state.progress) {
488
+ console.log(` ${chalk.dim('Progress:')} ${state.progress.stage} (${state.progress.status})`);
489
+ }
490
+ if (state.fleetResult) {
491
+ const result = state.fleetResult;
492
+ const cleanup = requireRecord(result.cleanup, 'Hosted fleet cleanup');
493
+ const provenance = requireRecord(result.provenance, 'Hosted fleet provenance');
494
+ console.log(` ${chalk.dim('Lifecycle:')} ${result.lifecycle}`);
495
+ console.log(` ${chalk.dim('Run health:')} ${result.runHealth}`);
496
+ console.log(` ${chalk.dim('Intent:')} ${provenance.intent}`);
497
+ console.log(` ${chalk.dim('Cleanup:')} ${cleanup.status} (${cleanup.pending.length} pending)`);
498
+ for (const value of result.findings) {
499
+ const finding = requireRecord(value, 'Hosted fleet finding');
500
+ console.log(` ${chalk.dim('Finding:')} ${finding.findingId} / ${finding.caseId}: ${finding.judgment}`);
501
+ }
502
+ }
324
503
  if (state.error)
325
504
  console.log(` ${chalk.dim('Error:')} ${state.error}`);
326
505
  if (options.timedOut) {
@@ -330,20 +509,38 @@ function printHostedState(operation, state, options) {
330
509
  console.log(` ${chalk.dim('Resume:')} ${nextCommand}`);
331
510
  }
332
511
  function markNonSuccess(state, timedOut = false) {
512
+ if (state.fleetResult) {
513
+ const result = state.fleetResult;
514
+ const cleanup = requireRecord(result.cleanup, 'Hosted fleet cleanup');
515
+ if (result.lifecycle !== 'complete' || result.runHealth !== 'healthy' || cleanup.status !== 'complete') {
516
+ process.exitCode = 2;
517
+ return;
518
+ }
519
+ if (result.findings.some(value => requireRecord(value, 'Hosted fleet finding').judgment === 'regression')) {
520
+ process.exitCode = 1;
521
+ }
522
+ }
333
523
  if (timedOut
334
524
  || (state.status !== 'complete' && TERMINAL_STATUSES.has(state.status))) {
335
525
  process.exitCode = 2;
336
526
  }
337
527
  }
338
528
  export async function verifyHostedStartCommand(repositoryArg, options) {
339
- const repository = parseHostedRepository(repositoryArg);
529
+ // Validate all local input before resolving accounts or submitting a job.
530
+ const { repository } = prepareHostedLaunch(repositoryArg, options);
531
+ const polling = hostedPollingOptions(options.interval, options.timeout, current => {
532
+ if (!options.json)
533
+ console.error(chalk.dim(hostedProgressMessage(current)));
534
+ });
340
535
  const auth = await resolveAuthContext({
341
536
  preferredLogin: options.account,
342
537
  owner: repository.owner,
343
538
  repo: repository.repository,
344
539
  });
345
540
  if (!options.json) {
346
- console.error(chalk.dim('Submitting the exact commit pair to the hosted verifier…'));
541
+ console.error(chalk.dim(options.provider === 'haystack'
542
+ ? 'Submitting repository revisions to the hosted fleet verifier…'
543
+ : 'Submitting the exact commit pair to the hosted verifier…'));
347
544
  }
348
545
  const started = await startHostedVerification(repositoryArg, options, auth.token);
349
546
  const selectedAccount = auth.token.startsWith('hsk_live_')
@@ -352,11 +549,7 @@ export async function verifyHostedStartCommand(repositoryArg, options) {
352
549
  let state = started.state;
353
550
  let timedOut;
354
551
  if (options.wait !== false && !TERMINAL_STATUSES.has(state.status)) {
355
- const waited = await waitForHostedVerification(state.runId, auth.token, hostedPollingOptions(options.interval, options.timeout, current => {
356
- if (!options.json) {
357
- console.error(chalk.dim(`Hosted verifier: ${current.status}…`));
358
- }
359
- }));
552
+ const waited = await waitForHostedVerification(state.runId, auth.token, polling);
360
553
  state = waited.state;
361
554
  timedOut = waited.timedOut;
362
555
  }
@@ -371,7 +564,14 @@ export async function verifyHostedStartCommand(repositoryArg, options) {
371
564
  });
372
565
  markNonSuccess(state, timedOut);
373
566
  }
567
+ function hostedProgressMessage(state) {
568
+ return `Hosted verifier: ${state.status}${state.progress ? ` · ${state.progress.stage} (${state.progress.status})` : ''}…`;
569
+ }
374
570
  export async function verifyHostedStatusCommand(runId, options) {
571
+ const polling = hostedPollingOptions(options.interval, options.timeout, current => {
572
+ if (!options.json)
573
+ console.error(chalk.dim(hostedProgressMessage(current)));
574
+ });
375
575
  const auth = await resolveAuthContext({ preferredLogin: options.account });
376
576
  const selectedAccount = auth.token.startsWith('hsk_live_')
377
577
  ? undefined
@@ -379,11 +579,7 @@ export async function verifyHostedStatusCommand(runId, options) {
379
579
  let state = await getHostedVerification(runId, auth.token);
380
580
  let timedOut;
381
581
  if (options.wait && !TERMINAL_STATUSES.has(state.status)) {
382
- const waited = await waitForHostedVerification(runId, auth.token, hostedPollingOptions(options.interval, options.timeout, current => {
383
- if (!options.json) {
384
- console.error(chalk.dim(`Hosted verifier: ${current.status}…`));
385
- }
386
- }));
582
+ const waited = await waitForHostedVerification(runId, auth.token, polling);
387
583
  state = waited.state;
388
584
  timedOut = waited.timedOut;
389
585
  }
@@ -1,4 +1,12 @@
1
- export type PrecomputeOutcome = 'enqueued' | 'cached' | 'existing' | 'skipped_no_auth' | 'error';
1
+ import type { DeliverPrecomputeRequest } from './precompute-delivery-contract.js';
2
+ export type RepoWalkHookStatus = {
3
+ status: 'queued' | 'existing';
4
+ runId: string;
5
+ } | {
6
+ status: 'unavailable';
7
+ reason: 'runner-unavailable' | 'enqueue-failed';
8
+ };
9
+ export type PrecomputeOutcome = 'pending' | 'enqueued' | 'cached' | 'existing' | 'skipped_no_auth' | 'error';
2
10
  export interface VerifyPrecomputeOptions {
3
11
  hook?: boolean;
4
12
  }
@@ -7,6 +15,7 @@ export interface FrontHalfEnqueueRequest {
7
15
  repository: string;
8
16
  baseCommit: string;
9
17
  workCommit: string;
18
+ observedAt: number;
10
19
  cacheKey: string;
11
20
  patchGzBase64: string;
12
21
  patchSha256: string;
@@ -16,4 +25,6 @@ export interface PrecomputeDerivation {
16
25
  request: FrontHalfEnqueueRequest;
17
26
  }
18
27
  export declare function derivePrecomputeRequest(gitRoot: string, owner: string, repository: string, deadline?: number): Promise<PrecomputeDerivation>;
28
+ /** One authenticated submission of the already captured request. No source reads or retries. */
29
+ export declare const deliverPrecomputeRequest: DeliverPrecomputeRequest;
19
30
  export declare function verifyPrecomputeCommand(options: VerifyPrecomputeOptions): Promise<void>;