@userreason/cli 0.2.0 → 0.2.1
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 +12 -2
- package/dist/first-survey-source.d.ts +1 -0
- package/dist/first-survey-source.js +15 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/profile.js +1 -1
- package/dist/recommendation-source.d.ts +13 -0
- package/dist/recommendation-source.js +211 -0
- package/dist/setup-diagnostics.d.ts +2 -0
- package/dist/setup-diagnostics.js +26 -0
- package/dist/setup-executor.d.ts +8 -1
- package/dist/setup-executor.js +153 -132
- package/dist/setup-heartbeat.d.ts +268 -0
- package/dist/setup-heartbeat.js +120 -0
- package/dist/setup-runner.d.ts +3 -1
- package/dist/setup-runner.js +157 -108
- package/dist/setup-source.d.ts +3 -2
- package/dist/setup-source.js +6 -6
- package/dist/setup-store.d.ts +2 -2
- package/dist/setup-store.js +16 -6
- package/dist/setup-wire.d.ts +130 -0
- package/dist/setup-wire.js +22 -7
- package/dist/setup.d.ts +211 -11
- package/dist/setup.js +10 -4
- package/package.json +1 -1
package/dist/setup-executor.js
CHANGED
|
@@ -4,7 +4,9 @@ import { lstat, open, opendir, rename, rm } from 'node:fs/promises';
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { SetupError, setupPrivateDirectory, setupProjectFingerprint, withSetupProjectLock } from './setup-store.js';
|
|
6
6
|
import { parseSetupCommandResultInput, parseSetupLocalPlan } from './setup-wire.js';
|
|
7
|
+
import { SetupHeartbeat, observeSetupClient, setupAbortable } from './setup-heartbeat.js';
|
|
7
8
|
import { readSetupFile, setupDigest, setupHash, verifySetupFiles } from './setup-files.js';
|
|
9
|
+
import { sanitizeSetupDiagnostic } from './setup-diagnostics.js';
|
|
8
10
|
const terminal = new Set(['complete', 'cancelled', 'expired']);
|
|
9
11
|
const stopped = new Set(['ready_for_recommendations', 'analyzing', 'selecting_drafts', 'creating_drafts', 'agent_test']);
|
|
10
12
|
const limit = 8_000_000;
|
|
@@ -93,7 +95,8 @@ function validateJournal(data, identity, active = true) {
|
|
|
93
95
|
data.plan = validatePrepared(data.plan);
|
|
94
96
|
for (const [operationId, receipt] of Object.entries(data.receipts)) {
|
|
95
97
|
if (!uuid(operationId) || !receipt || typeof receipt !== 'object' || Array.isArray(receipt) ||
|
|
96
|
-
Object.keys(receipt).some(key => !['input_digest', 'stage', 'outcome', 'outputs', 'request_id'].includes(key)) ||
|
|
98
|
+
Object.keys(receipt).some(key => !['input_digest', 'stage', 'outcome', 'diagnostic', 'outputs', 'request_id'].includes(key)) ||
|
|
99
|
+
(receipt.diagnostic !== undefined && (typeof receipt.diagnostic !== 'string' || receipt.diagnostic.length > 8000)) ||
|
|
97
100
|
!/^[a-f0-9]{64}$/.test(receipt.input_digest) || !['claimed', 'running', 'settled'].includes(receipt.stage) || !uuid(receipt.request_id) ||
|
|
98
101
|
(receipt.stage === 'settled') !== Boolean(receipt.outcome))
|
|
99
102
|
throw new Error('invalid operation receipt');
|
|
@@ -179,151 +182,169 @@ async function verified(directory, operation, runtime, receipt) {
|
|
|
179
182
|
return runtime.verify ? runtime.verify(operation) : operation.effect.kind !== 'install_packages';
|
|
180
183
|
}
|
|
181
184
|
/** Runs only after browser approval. Durable intents precede effects and durable outcomes precede reports. */
|
|
182
|
-
export async function runSetupExecutor(client, directory, runtime, options = {}) {
|
|
185
|
+
export async function runSetupExecutor(client, directory, runtime, options = {}, sharedHeartbeat) {
|
|
183
186
|
if (await setupProjectFingerprint(directory) !== client.identity.project_fingerprint)
|
|
184
187
|
throw new SetupError('setup_binding_mismatch', 'This executor belongs to another project.');
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
session = await client.report({ ...scope, operation_id: known.operation_id, executor_generation: known.executor_generation, lease_generation: known.lease_generation, ...journal.receipts[known.operation_id].outcome });
|
|
188
|
+
const heartbeat = sharedHeartbeat ?? new SetupHeartbeat(client, options);
|
|
189
|
+
client = observeSetupClient(client, heartbeat, options.signal);
|
|
190
|
+
try {
|
|
191
|
+
return await withSetupProjectLock(directory, async () => {
|
|
192
|
+
const path = await journalPath(client), journal = await loadJournal(path, client);
|
|
193
|
+
const shownErrors = new Set();
|
|
194
|
+
const showError = (id, receipt) => {
|
|
195
|
+
if (!receipt.diagnostic || shownErrors.has(id))
|
|
196
|
+
return;
|
|
197
|
+
const operation = journal.plan?.operations.find(item => setupDigest(item.effect) === receipt.input_digest);
|
|
198
|
+
if (!operation)
|
|
199
|
+
return;
|
|
200
|
+
shownErrors.add(id);
|
|
201
|
+
try {
|
|
202
|
+
options.onOperationError?.({ kind: operation.effect.kind, message: sanitizeSetupDiagnostic(receipt.diagnostic), receiptPath: path });
|
|
201
203
|
}
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
catch { /* A display callback cannot prevent the durable result report. */ }
|
|
205
|
+
};
|
|
206
|
+
for (const [id, receipt] of Object.entries(journal.receipts))
|
|
207
|
+
showError(id, receipt);
|
|
208
|
+
let session = await client.poll(), polls = 0;
|
|
209
|
+
const scope = { schema_version: 1, setup_id: client.identity.setup_id };
|
|
210
|
+
const pause = async () => { if (options.sleep)
|
|
211
|
+
await options.sleep(options.pollMs ?? 2000);
|
|
212
|
+
else
|
|
213
|
+
await new Promise((resolve, reject) => { const timer = setTimeout(done, options.pollMs ?? 2000); function done() { options.signal?.removeEventListener('abort', abort); resolve(); } function abort() { clearTimeout(timer); reject(options.signal?.reason); } options.signal?.addEventListener('abort', abort, { once: true }); }); };
|
|
214
|
+
while (true) {
|
|
215
|
+
heartbeat.check();
|
|
216
|
+
session = heartbeat.latest(session);
|
|
217
|
+
options.onSession?.(session);
|
|
218
|
+
if (terminal.has(session.phase) || stopped.has(session.phase) || options.maxPolls !== undefined && polls++ >= options.maxPolls) {
|
|
219
|
+
if (session.phase === 'cancelled') {
|
|
220
|
+
const known = session.commands.find(command => command.status === 'running' && journal.receipts[command.operation_id]?.outcome);
|
|
221
|
+
if (known)
|
|
222
|
+
session = await client.report({ ...scope, operation_id: known.operation_id, executor_generation: known.executor_generation, lease_generation: known.lease_generation, ...journal.receipts[known.operation_id].outcome });
|
|
223
|
+
}
|
|
224
|
+
return session;
|
|
225
|
+
}
|
|
226
|
+
if (session.phase === 'configuring') {
|
|
227
|
+
if (!session.site_id || !session.workspace_id) {
|
|
228
|
+
await heartbeat.pulse();
|
|
229
|
+
heartbeat.check();
|
|
230
|
+
session = heartbeat.latest(session);
|
|
231
|
+
await pause();
|
|
232
|
+
session = await client.poll();
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const targetDigest = setupDigest({ workspace_id: session.workspace_id, site_id: session.site_id, api_url: client.identity.api_url });
|
|
236
|
+
if (!journal.plan || journal.plan.wire.choices_digest !== setupDigest(session.choices) || journal.target_digest !== targetDigest) {
|
|
237
|
+
if (Object.keys(journal.receipts).length)
|
|
238
|
+
throw new SetupError('setup_plan_changed', 'A completed local plan cannot be replaced without a new setup.');
|
|
239
|
+
journal.plan = validatePrepared(await setupAbortable(runtime.prepare(session), heartbeat.signal));
|
|
240
|
+
journal.target_digest = targetDigest;
|
|
241
|
+
journal.plan_request_id = randomUUID();
|
|
242
|
+
await saveJournal(path, journal);
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
session = await client.plan({ ...scope, request_id: journal.plan_request_id, expected_revision: session.revision, plan: journal.plan.wire });
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
if (!(error instanceof SetupError) || error.code !== 'revision_conflict')
|
|
249
|
+
throw error;
|
|
250
|
+
session = await client.poll();
|
|
251
|
+
}
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (!journal.plan || !session.local_plan || setupDigest(session.local_plan) !== setupDigest(journal.plan.wire))
|
|
255
|
+
throw new SetupError('setup_plan_missing', 'The browser-approved local plan is unavailable on this computer. No effects were applied.');
|
|
256
|
+
if (session.phase !== 'applying') {
|
|
257
|
+
await heartbeat.pulse();
|
|
258
|
+
heartbeat.check();
|
|
259
|
+
session = heartbeat.latest(session);
|
|
207
260
|
await pause();
|
|
208
261
|
session = await client.poll();
|
|
209
262
|
continue;
|
|
210
263
|
}
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
264
|
+
const unknown = session.commands.find(command => command.status === 'outcome_unknown' || command.status === 'running' || command.status === 'leased' && journal.receipts[command.operation_id]);
|
|
265
|
+
if (unknown) {
|
|
266
|
+
const operation = journal.plan.operations.find(item => setupDigest(item.effect) === unknown.input_digest);
|
|
267
|
+
if (!operation)
|
|
268
|
+
throw new SetupError('setup_command_mismatch', 'The command does not match the local reviewed plan.');
|
|
269
|
+
const receipt = journal.receipts[unknown.operation_id];
|
|
270
|
+
// A running command belongs to an interrupted process. Wait for the server
|
|
271
|
+
// to close its lease; never issue the side effect a second time.
|
|
272
|
+
if (unknown.status !== 'outcome_unknown') {
|
|
273
|
+
await pause();
|
|
274
|
+
session = await client.poll();
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const stillVerified = await verified(directory, operation, runtime, receipt);
|
|
278
|
+
const outcome = receipt?.outcome && receipt.outcome.status !== 'succeeded' ? receipt.outcome : stillVerified
|
|
279
|
+
? { status: 'succeeded', result: { code: 'already_present', verified: true } }
|
|
280
|
+
: { status: 'manual', result: { code: receipt?.outcome?.status === 'succeeded' ? 'review_required' : 'outcome_unknown' } };
|
|
281
|
+
const requestId = receipt?.request_id ?? randomUUID();
|
|
282
|
+
journal.receipts[unknown.operation_id] = { input_digest: unknown.input_digest, stage: 'settled', outcome, diagnostic: receipt?.diagnostic, outputs: receipt?.outputs, request_id: requestId };
|
|
218
283
|
await saveJournal(path, journal);
|
|
284
|
+
session = await client.reconcile({ ...scope, request_id: requestId, project_fingerprint: client.identity.project_fingerprint, executor_generation: session.executor_generation, operation_id: unknown.operation_id, input_digest: unknown.input_digest, ...outcome });
|
|
285
|
+
continue;
|
|
219
286
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
throw new SetupError('setup_plan_missing', 'The browser-approved local plan is unavailable on this computer. No effects were applied.');
|
|
225
|
-
if (session.phase !== 'applying') {
|
|
226
|
-
session = await client.heartbeat({ ...scope, executor_generation: session.executor_generation, operation_id: null, lease_generation: null });
|
|
227
|
-
await pause();
|
|
228
|
-
session = await client.poll();
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
const unknown = session.commands.find(command => command.status === 'outcome_unknown' || command.status === 'running' || command.status === 'leased' && journal.receipts[command.operation_id]);
|
|
232
|
-
if (unknown) {
|
|
233
|
-
const operation = journal.plan.operations.find(item => setupDigest(item.effect) === unknown.input_digest);
|
|
234
|
-
if (!operation)
|
|
235
|
-
throw new SetupError('setup_command_mismatch', 'The command does not match the local reviewed plan.');
|
|
236
|
-
const receipt = journal.receipts[unknown.operation_id];
|
|
237
|
-
// A running command belongs to an interrupted process. Wait for the server
|
|
238
|
-
// to close its lease; never issue the side effect a second time.
|
|
239
|
-
if (unknown.status !== 'outcome_unknown') {
|
|
287
|
+
const claimed = await client.claim({ ...scope, request_id: journal.claim_request_id, executor_generation: session.executor_generation });
|
|
288
|
+
session = claimed.session;
|
|
289
|
+
const command = claimed.command;
|
|
290
|
+
if (!command) {
|
|
240
291
|
await pause();
|
|
241
292
|
session = await client.poll();
|
|
242
293
|
continue;
|
|
243
294
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
295
|
+
if (command.status !== 'leased')
|
|
296
|
+
continue;
|
|
297
|
+
const operation = journal.plan.operations.find(item => item.effect.effect_id === command.input.effect_id);
|
|
298
|
+
if (!operation || setupDigest(operation.effect) !== command.input_digest || setupDigest(operation.effect) !== setupDigest(command.input))
|
|
299
|
+
throw new SetupError('setup_command_mismatch', 'The command differs from the approved local plan.');
|
|
300
|
+
const lease = { ...scope, operation_id: command.operation_id, executor_generation: command.executor_generation, lease_generation: command.lease_generation };
|
|
301
|
+
if (journal.receipts[command.operation_id])
|
|
302
|
+
throw new SetupError('setup_replay_refused', 'A local receipt already exists. Reconcile before attempting this command again.');
|
|
303
|
+
const receipt = { input_digest: command.input_digest, stage: 'claimed', request_id: randomUUID() };
|
|
304
|
+
journal.receipts[command.operation_id] = receipt;
|
|
250
305
|
await saveJournal(path, journal);
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const operation = journal.plan.operations.find(item => item.effect.effect_id === command.input.effect_id);
|
|
265
|
-
if (!operation || setupDigest(operation.effect) !== command.input_digest || setupDigest(operation.effect) !== setupDigest(command.input))
|
|
266
|
-
throw new SetupError('setup_command_mismatch', 'The command differs from the approved local plan.');
|
|
267
|
-
const lease = { ...scope, operation_id: command.operation_id, executor_generation: command.executor_generation, lease_generation: command.lease_generation };
|
|
268
|
-
if (journal.receipts[command.operation_id])
|
|
269
|
-
throw new SetupError('setup_replay_refused', 'A local receipt already exists. Reconcile before attempting this command again.');
|
|
270
|
-
const receipt = { input_digest: command.input_digest, stage: 'claimed', request_id: randomUUID() };
|
|
271
|
-
journal.receipts[command.operation_id] = receipt;
|
|
272
|
-
await saveJournal(path, journal);
|
|
273
|
-
session = await client.startCommand(lease);
|
|
274
|
-
receipt.stage = 'running';
|
|
275
|
-
await saveJournal(path, journal);
|
|
276
|
-
const controller = new AbortController();
|
|
277
|
-
const abort = () => controller.abort(options.signal?.reason);
|
|
278
|
-
options.signal?.addEventListener('abort', abort, { once: true });
|
|
279
|
-
if (options.signal?.aborted)
|
|
280
|
-
abort();
|
|
281
|
-
if (terminal.has(session.phase))
|
|
282
|
-
controller.abort(new Error('Setup ended before local execution'));
|
|
283
|
-
let heartbeatFailure, heartbeatBusy = false;
|
|
284
|
-
let inFlightHeartbeat;
|
|
285
|
-
const timer = setInterval(() => {
|
|
286
|
-
if (heartbeatBusy)
|
|
287
|
-
return;
|
|
288
|
-
heartbeatBusy = true;
|
|
289
|
-
inFlightHeartbeat = client.heartbeat(lease).then(value => { session = value; if (terminal.has(value.phase))
|
|
290
|
-
controller.abort(new Error('Setup ended')); }, error => { heartbeatFailure = error; controller.abort(error); }).finally(() => { heartbeatBusy = false; });
|
|
291
|
-
}, options.heartbeatMs ?? 10_000);
|
|
292
|
-
try {
|
|
293
|
-
controller.signal.throwIfAborted();
|
|
294
|
-
if (!await verifySetupFiles(directory, operation.files, 'before'))
|
|
295
|
-
throw new SetupError('setup_files_changed', 'Files changed after browser approval.');
|
|
296
|
-
for (const guard of operation.guards ?? []) {
|
|
297
|
-
const content = await readSetupFile(directory, guard.path);
|
|
298
|
-
if ((content === null ? null : setupHash(content)) !== guard.before_hash)
|
|
299
|
-
throw new SetupError('setup_files_changed', 'A package configuration changed after review.');
|
|
300
|
-
}
|
|
301
|
-
if (operation.effect.kind === 'install_packages')
|
|
302
|
-
for (const file of operation.effect.managed_outputs ?? []) {
|
|
303
|
-
const content = await readSetupFile(directory, file.path);
|
|
304
|
-
if ((content === null ? null : setupHash(content)) !== file.before_hash)
|
|
305
|
-
throw new SetupError('setup_files_changed', 'Package-manager outputs changed after approval.');
|
|
306
|
+
heartbeat.setLease(lease);
|
|
307
|
+
session = await client.startCommand(lease);
|
|
308
|
+
receipt.stage = 'running';
|
|
309
|
+
await saveJournal(path, journal);
|
|
310
|
+
const operationSignal = heartbeat.signal;
|
|
311
|
+
try {
|
|
312
|
+
operationSignal.throwIfAborted();
|
|
313
|
+
if (!await verifySetupFiles(directory, operation.files, 'before'))
|
|
314
|
+
throw new SetupError('setup_files_changed', 'Files changed after browser approval.');
|
|
315
|
+
for (const guard of operation.guards ?? []) {
|
|
316
|
+
const content = await readSetupFile(directory, guard.path);
|
|
317
|
+
if ((content === null ? null : setupHash(content)) !== guard.before_hash)
|
|
318
|
+
throw new SetupError('setup_files_changed', 'A package configuration changed after review.');
|
|
306
319
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
320
|
+
if (operation.effect.kind === 'install_packages')
|
|
321
|
+
for (const file of operation.effect.managed_outputs ?? []) {
|
|
322
|
+
const content = await readSetupFile(directory, file.path);
|
|
323
|
+
if ((content === null ? null : setupHash(content)) !== file.before_hash)
|
|
324
|
+
throw new SetupError('setup_files_changed', 'Package-manager outputs changed after approval.');
|
|
325
|
+
}
|
|
326
|
+
const code = await runtime.apply(operation, operationSignal);
|
|
327
|
+
operationSignal.throwIfAborted();
|
|
328
|
+
receipt.outputs = await outputs(directory, operation);
|
|
329
|
+
if (!await verified(directory, operation, runtime, receipt))
|
|
330
|
+
throw new SetupError('setup_verification_failed', 'The completed effect did not match the reviewed files.');
|
|
331
|
+
receipt.outcome = { status: 'succeeded', result: { code, verified: true } };
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
receipt.outcome = { status: error instanceof SetupError && error.code === 'setup_files_changed' ? 'manual' : 'failed', result: { code: operationSignal.aborted ? 'outcome_unknown' : error instanceof SetupError && error.code === 'setup_files_changed' ? 'review_required' : 'operation_failed' } };
|
|
335
|
+
receipt.diagnostic = sanitizeSetupDiagnostic(error instanceof Error ? error.message : 'Local operation failed without an error message.');
|
|
336
|
+
}
|
|
337
|
+
receipt.stage = 'settled';
|
|
338
|
+
journal.claim_request_id = randomUUID();
|
|
339
|
+
await saveJournal(path, journal);
|
|
340
|
+
showError(command.operation_id, receipt);
|
|
341
|
+
// A lost report is retried from its saved receipt on resume; the effect is never rerun.
|
|
342
|
+
session = await heartbeat.duringReport(() => client.report({ ...lease, ...receipt.outcome }));
|
|
321
343
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
});
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
if (!sharedHeartbeat)
|
|
348
|
+
await heartbeat.stop();
|
|
349
|
+
}
|
|
329
350
|
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import type { BrowserSetupClient } from './setup.js';
|
|
2
|
+
import type { SetupSessionView } from './setup-wire.js';
|
|
3
|
+
/** Stop waiting when this run ends, including user-supplied preparations. */
|
|
4
|
+
export declare function setupAbortable<T>(work: Promise<T>, signal?: AbortSignal): Promise<T>;
|
|
5
|
+
/** One lease-aware heartbeat for the entire run, independent of its current stage. */
|
|
6
|
+
export declare class SetupHeartbeat {
|
|
7
|
+
private client;
|
|
8
|
+
private options;
|
|
9
|
+
private session?;
|
|
10
|
+
private timer?;
|
|
11
|
+
private pending?;
|
|
12
|
+
private stopped;
|
|
13
|
+
private paused;
|
|
14
|
+
private failure?;
|
|
15
|
+
private lease?;
|
|
16
|
+
private readonly lifetime;
|
|
17
|
+
readonly signal: AbortSignal;
|
|
18
|
+
constructor(client: Pick<BrowserSetupClient, 'identity' | 'heartbeat'>, options?: {
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
heartbeatMs?: number;
|
|
21
|
+
});
|
|
22
|
+
observe(next: SetupSessionView): SetupSessionView;
|
|
23
|
+
get current(): ({
|
|
24
|
+
phase: "expired" | "cancelled" | "analyzing" | "configuring" | "awaiting_local_approval" | "applying" | "ready_for_recommendations" | "selecting_drafts" | "creating_drafts" | "agent_test" | "complete";
|
|
25
|
+
revision: string;
|
|
26
|
+
workspace_id: string | null;
|
|
27
|
+
site_id: string | null;
|
|
28
|
+
project_fingerprint: string;
|
|
29
|
+
choices: {
|
|
30
|
+
install_sdk: boolean;
|
|
31
|
+
install_skill: boolean;
|
|
32
|
+
footer_badge: boolean;
|
|
33
|
+
} & {};
|
|
34
|
+
capabilities: {
|
|
35
|
+
framework: string;
|
|
36
|
+
package_manager: string;
|
|
37
|
+
install_packages: boolean;
|
|
38
|
+
initialize_sdk: boolean;
|
|
39
|
+
install_skill: boolean;
|
|
40
|
+
insert_footer: boolean;
|
|
41
|
+
source_review: boolean;
|
|
42
|
+
sample_read: boolean;
|
|
43
|
+
} & {
|
|
44
|
+
onboarding_v2?: boolean | undefined;
|
|
45
|
+
project_name?: string | undefined;
|
|
46
|
+
launcher?: ({
|
|
47
|
+
wizard_version: string;
|
|
48
|
+
api_origin: string;
|
|
49
|
+
} & {
|
|
50
|
+
registry?: string | undefined;
|
|
51
|
+
}) | undefined;
|
|
52
|
+
terminal?: ({
|
|
53
|
+
platform: string;
|
|
54
|
+
app: string;
|
|
55
|
+
} & {}) | undefined;
|
|
56
|
+
};
|
|
57
|
+
local_plan: ({
|
|
58
|
+
project_fingerprint: string;
|
|
59
|
+
choices_digest: string;
|
|
60
|
+
effects: import("./setup-wire.js").SetupEffect[];
|
|
61
|
+
} & {
|
|
62
|
+
manual_steps?: ({
|
|
63
|
+
kind: "install_packages" | "initialize_sdk" | "install_skill" | "insert_footer";
|
|
64
|
+
code: string;
|
|
65
|
+
} & {})[] | undefined;
|
|
66
|
+
}) | null;
|
|
67
|
+
plan_digest: string | null;
|
|
68
|
+
commands: ({
|
|
69
|
+
operation_id: string;
|
|
70
|
+
sequence: number;
|
|
71
|
+
kind: "install_packages" | "initialize_sdk" | "install_skill" | "insert_footer";
|
|
72
|
+
expected_revision: string;
|
|
73
|
+
input: import("./setup-wire.js").SetupEffect;
|
|
74
|
+
input_digest: string;
|
|
75
|
+
approval_digest: string;
|
|
76
|
+
status: "approved" | "manual" | "cancelled" | "failed" | "outcome_unknown" | "prepared" | "leased" | "running" | "succeeded";
|
|
77
|
+
executor_generation: number | null;
|
|
78
|
+
lease_generation: number;
|
|
79
|
+
lease_expires_at: string | null;
|
|
80
|
+
result: ({
|
|
81
|
+
code: "cancelled" | "skipped" | "already_present" | "unsupported" | "operation_failed" | "verified" | "review_required" | "dependency_failed" | "outcome_unknown";
|
|
82
|
+
} & {
|
|
83
|
+
verified?: boolean | undefined;
|
|
84
|
+
}) | null;
|
|
85
|
+
result_digest: string | null;
|
|
86
|
+
created_at: string;
|
|
87
|
+
started_at: string | null;
|
|
88
|
+
finished_at: string | null;
|
|
89
|
+
updated_at: string;
|
|
90
|
+
} & {})[];
|
|
91
|
+
executor_generation: number;
|
|
92
|
+
connection: string;
|
|
93
|
+
heartbeat_at: string | null;
|
|
94
|
+
sample: {
|
|
95
|
+
status: string;
|
|
96
|
+
first_received_at: string | null;
|
|
97
|
+
latest_received_at: string | null;
|
|
98
|
+
latest_request_id: string | null;
|
|
99
|
+
} & {};
|
|
100
|
+
draft_outcomes: ({
|
|
101
|
+
candidate_id: string;
|
|
102
|
+
status: string;
|
|
103
|
+
prompt_id: string;
|
|
104
|
+
request_id: string;
|
|
105
|
+
revision: string | null;
|
|
106
|
+
code: "cancelled" | "skipped" | "already_present" | "unsupported" | "operation_failed" | "verified" | "review_required" | "dependency_failed" | "outcome_unknown" | null;
|
|
107
|
+
} & {})[];
|
|
108
|
+
bound_at: string;
|
|
109
|
+
last_activity_at: string;
|
|
110
|
+
idle_expires_at: string;
|
|
111
|
+
absolute_expires_at: string;
|
|
112
|
+
terminal_at: string | null;
|
|
113
|
+
schema_version: 1;
|
|
114
|
+
setup_id: string;
|
|
115
|
+
} & {
|
|
116
|
+
source?: ({
|
|
117
|
+
preparation_id: string;
|
|
118
|
+
status: string;
|
|
119
|
+
packet_digest: string;
|
|
120
|
+
provider_fingerprint: string;
|
|
121
|
+
authorized_at: string | null;
|
|
122
|
+
expires_at: string;
|
|
123
|
+
error_code: "operation_failed" | "planning_unavailable" | "planning_paused" | "planning_daily_budget" | "planning_allowance_exhausted" | "planning_provider_unavailable" | "planning_policy_changed" | "planning_consent_expired" | "planning_input_budget" | "planning_invalid_output" | "planning_timeout" | "source_changed" | "source_unavailable" | "recommendations_no_source" | null;
|
|
124
|
+
} & {
|
|
125
|
+
analysis_id?: string | undefined;
|
|
126
|
+
analysis_started_at?: string | undefined;
|
|
127
|
+
analysis_completed_at?: string | undefined;
|
|
128
|
+
deadline_at?: string | undefined;
|
|
129
|
+
timings?: ({} & {
|
|
130
|
+
extraction_ms?: number | undefined;
|
|
131
|
+
consolidation_ms?: number | undefined;
|
|
132
|
+
authoring_ms?: number | undefined;
|
|
133
|
+
total_ms?: number | undefined;
|
|
134
|
+
}) | undefined;
|
|
135
|
+
}) | null | undefined;
|
|
136
|
+
recommendation_digest?: string | null | undefined;
|
|
137
|
+
selected_candidates?: string[] | undefined;
|
|
138
|
+
}) | undefined;
|
|
139
|
+
setLease(lease: {
|
|
140
|
+
operation_id: string;
|
|
141
|
+
executor_generation: number;
|
|
142
|
+
lease_generation: number;
|
|
143
|
+
}): void;
|
|
144
|
+
latest(fallback: SetupSessionView): {
|
|
145
|
+
phase: "expired" | "cancelled" | "analyzing" | "configuring" | "awaiting_local_approval" | "applying" | "ready_for_recommendations" | "selecting_drafts" | "creating_drafts" | "agent_test" | "complete";
|
|
146
|
+
revision: string;
|
|
147
|
+
workspace_id: string | null;
|
|
148
|
+
site_id: string | null;
|
|
149
|
+
project_fingerprint: string;
|
|
150
|
+
choices: {
|
|
151
|
+
install_sdk: boolean;
|
|
152
|
+
install_skill: boolean;
|
|
153
|
+
footer_badge: boolean;
|
|
154
|
+
} & {};
|
|
155
|
+
capabilities: {
|
|
156
|
+
framework: string;
|
|
157
|
+
package_manager: string;
|
|
158
|
+
install_packages: boolean;
|
|
159
|
+
initialize_sdk: boolean;
|
|
160
|
+
install_skill: boolean;
|
|
161
|
+
insert_footer: boolean;
|
|
162
|
+
source_review: boolean;
|
|
163
|
+
sample_read: boolean;
|
|
164
|
+
} & {
|
|
165
|
+
onboarding_v2?: boolean | undefined;
|
|
166
|
+
project_name?: string | undefined;
|
|
167
|
+
launcher?: ({
|
|
168
|
+
wizard_version: string;
|
|
169
|
+
api_origin: string;
|
|
170
|
+
} & {
|
|
171
|
+
registry?: string | undefined;
|
|
172
|
+
}) | undefined;
|
|
173
|
+
terminal?: ({
|
|
174
|
+
platform: string;
|
|
175
|
+
app: string;
|
|
176
|
+
} & {}) | undefined;
|
|
177
|
+
};
|
|
178
|
+
local_plan: ({
|
|
179
|
+
project_fingerprint: string;
|
|
180
|
+
choices_digest: string;
|
|
181
|
+
effects: import("./setup-wire.js").SetupEffect[];
|
|
182
|
+
} & {
|
|
183
|
+
manual_steps?: ({
|
|
184
|
+
kind: "install_packages" | "initialize_sdk" | "install_skill" | "insert_footer";
|
|
185
|
+
code: string;
|
|
186
|
+
} & {})[] | undefined;
|
|
187
|
+
}) | null;
|
|
188
|
+
plan_digest: string | null;
|
|
189
|
+
commands: ({
|
|
190
|
+
operation_id: string;
|
|
191
|
+
sequence: number;
|
|
192
|
+
kind: "install_packages" | "initialize_sdk" | "install_skill" | "insert_footer";
|
|
193
|
+
expected_revision: string;
|
|
194
|
+
input: import("./setup-wire.js").SetupEffect;
|
|
195
|
+
input_digest: string;
|
|
196
|
+
approval_digest: string;
|
|
197
|
+
status: "approved" | "manual" | "cancelled" | "failed" | "outcome_unknown" | "prepared" | "leased" | "running" | "succeeded";
|
|
198
|
+
executor_generation: number | null;
|
|
199
|
+
lease_generation: number;
|
|
200
|
+
lease_expires_at: string | null;
|
|
201
|
+
result: ({
|
|
202
|
+
code: "cancelled" | "skipped" | "already_present" | "unsupported" | "operation_failed" | "verified" | "review_required" | "dependency_failed" | "outcome_unknown";
|
|
203
|
+
} & {
|
|
204
|
+
verified?: boolean | undefined;
|
|
205
|
+
}) | null;
|
|
206
|
+
result_digest: string | null;
|
|
207
|
+
created_at: string;
|
|
208
|
+
started_at: string | null;
|
|
209
|
+
finished_at: string | null;
|
|
210
|
+
updated_at: string;
|
|
211
|
+
} & {})[];
|
|
212
|
+
executor_generation: number;
|
|
213
|
+
connection: string;
|
|
214
|
+
heartbeat_at: string | null;
|
|
215
|
+
sample: {
|
|
216
|
+
status: string;
|
|
217
|
+
first_received_at: string | null;
|
|
218
|
+
latest_received_at: string | null;
|
|
219
|
+
latest_request_id: string | null;
|
|
220
|
+
} & {};
|
|
221
|
+
draft_outcomes: ({
|
|
222
|
+
candidate_id: string;
|
|
223
|
+
status: string;
|
|
224
|
+
prompt_id: string;
|
|
225
|
+
request_id: string;
|
|
226
|
+
revision: string | null;
|
|
227
|
+
code: "cancelled" | "skipped" | "already_present" | "unsupported" | "operation_failed" | "verified" | "review_required" | "dependency_failed" | "outcome_unknown" | null;
|
|
228
|
+
} & {})[];
|
|
229
|
+
bound_at: string;
|
|
230
|
+
last_activity_at: string;
|
|
231
|
+
idle_expires_at: string;
|
|
232
|
+
absolute_expires_at: string;
|
|
233
|
+
terminal_at: string | null;
|
|
234
|
+
schema_version: 1;
|
|
235
|
+
setup_id: string;
|
|
236
|
+
} & {
|
|
237
|
+
source?: ({
|
|
238
|
+
preparation_id: string;
|
|
239
|
+
status: string;
|
|
240
|
+
packet_digest: string;
|
|
241
|
+
provider_fingerprint: string;
|
|
242
|
+
authorized_at: string | null;
|
|
243
|
+
expires_at: string;
|
|
244
|
+
error_code: "operation_failed" | "planning_unavailable" | "planning_paused" | "planning_daily_budget" | "planning_allowance_exhausted" | "planning_provider_unavailable" | "planning_policy_changed" | "planning_consent_expired" | "planning_input_budget" | "planning_invalid_output" | "planning_timeout" | "source_changed" | "source_unavailable" | "recommendations_no_source" | null;
|
|
245
|
+
} & {
|
|
246
|
+
analysis_id?: string | undefined;
|
|
247
|
+
analysis_started_at?: string | undefined;
|
|
248
|
+
analysis_completed_at?: string | undefined;
|
|
249
|
+
deadline_at?: string | undefined;
|
|
250
|
+
timings?: ({} & {
|
|
251
|
+
extraction_ms?: number | undefined;
|
|
252
|
+
consolidation_ms?: number | undefined;
|
|
253
|
+
authoring_ms?: number | undefined;
|
|
254
|
+
total_ms?: number | undefined;
|
|
255
|
+
}) | undefined;
|
|
256
|
+
}) | null | undefined;
|
|
257
|
+
recommendation_digest?: string | null | undefined;
|
|
258
|
+
selected_candidates?: string[] | undefined;
|
|
259
|
+
};
|
|
260
|
+
check(): void;
|
|
261
|
+
private schedule;
|
|
262
|
+
pulse(): Promise<void>;
|
|
263
|
+
/** Finish the old lease's in-flight heartbeat before closing it in a report. */
|
|
264
|
+
duringReport<T>(operation: () => Promise<T>): Promise<T>;
|
|
265
|
+
stop(): Promise<void>;
|
|
266
|
+
}
|
|
267
|
+
/** Observe all responses; cancel outstanding reads/source work when the run ends. */
|
|
268
|
+
export declare function observeSetupClient<T extends object>(client: T, heartbeat: SetupHeartbeat, signal?: AbortSignal): T;
|