@lambdacurry/arbor 0.24.30 → 0.24.32

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.
Files changed (2) hide show
  1. package/dist/arbor.js +84 -10
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -66,6 +66,9 @@ function parseErrorDetails(value) {
66
66
  details.limit = raw.limit;
67
67
  if (typeof raw.retryable === "boolean")
68
68
  details.retryable = raw.retryable;
69
+ if (typeof raw.retryAfterMs === "number" && Number.isFinite(raw.retryAfterMs) && raw.retryAfterMs >= 0) {
70
+ details.retryAfterMs = raw.retryAfterMs;
71
+ }
69
72
  if (typeof raw.phase === "string")
70
73
  details.phase = raw.phase;
71
74
  if (raw.provider && typeof raw.provider === "object" && !Array.isArray(raw.provider)) {
@@ -81,6 +84,19 @@ function parseErrorDetails(value) {
81
84
  };
82
85
  }
83
86
  }
87
+ if (typeof raw.providerCode === "string")
88
+ details.providerCode = raw.providerCode;
89
+ if (raw.providerDetails && typeof raw.providerDetails === "object" && !Array.isArray(raw.providerDetails)) {
90
+ const scalars = Object.entries(raw.providerDetails).filter((entry) => typeof entry[1] === "string" || typeof entry[1] === "number" || typeof entry[1] === "boolean");
91
+ if (scalars.length > 0)
92
+ details.providerDetails = Object.fromEntries(scalars);
93
+ }
94
+ if (typeof raw.runStartId === "string")
95
+ details.runStartId = raw.runStartId;
96
+ if (typeof raw.runCreated === "boolean")
97
+ details.runCreated = raw.runCreated;
98
+ if (typeof raw.computerSessionId === "string")
99
+ details.computerSessionId = raw.computerSessionId;
84
100
  if (typeof raw.runId === "string")
85
101
  details.runId = raw.runId;
86
102
  if (typeof raw.operationId === "string")
@@ -12241,7 +12257,9 @@ function buildOutputSchemas() {
12241
12257
  runId: id,
12242
12258
  runCreated: literal(false),
12243
12259
  status: literal("provisioning"),
12244
- phase: literal("run.admission")
12260
+ phase: literal("run.admission"),
12261
+ idempotencyKey: string2(),
12262
+ nextAction: literal("computer_run_start").describe("No Run exists yet (still provisioning, or stopped before one existed): retry computer_run_start with the same inputs and this idempotencyKey; it replays or resumes this start, never a second Run.")
12245
12263
  })
12246
12264
  ])
12247
12265
  }),
@@ -16749,7 +16767,7 @@ var ACTIONS = ACTION_DEFINITIONS.map((action) => {
16749
16767
  });
16750
16768
 
16751
16769
  // ../actions/src/retention-entry.ts
16752
- var ARBOR_VERSION = "0.24.30";
16770
+ var ARBOR_VERSION = "0.24.32";
16753
16771
  var historyPolicySchema = _enum(["full", "checkpoint"]).optional().describe("text Artifacts: future history retention policy; full retains every source snapshot, checkpoint keeps metadata for every revision and source only when explicitly retained");
16754
16772
  var retainSourceSchema = boolean2().optional().describe("edit: in checkpoint mode, retain this newly minted content revision's source");
16755
16773
  var checkpointSchema = boolean2().optional().describe("edit: retain the exact current version source without incrementing content version");
@@ -17157,7 +17175,7 @@ function dispositionFilename(value) {
17157
17175
  // package.json
17158
17176
  var package_default = {
17159
17177
  name: "@lambdacurry/arbor",
17160
- version: "0.24.30",
17178
+ version: "0.24.32",
17161
17179
  description: "The Arbor CLI — a shared workspace for people and agents. The human + headless-agent write path over Arbor's guarded operation surface.",
17162
17180
  keywords: [
17163
17181
  "agents",
@@ -17593,6 +17611,35 @@ async function waitForComputerExec(executor, input, value, ctx) {
17593
17611
  return withContinuation(latest);
17594
17612
  }
17595
17613
 
17614
+ // src/computer-run-start-wait.ts
17615
+ init_errors();
17616
+ var DEFAULT_SETUP_RETRY_AFTER_MS = 30000;
17617
+ function isSetupPendingRefusal(details) {
17618
+ return details?.retryable === true && details.suggestedAction === "computer_run_start" && details.phase === "computer.open";
17619
+ }
17620
+ function sleep2(ms) {
17621
+ return new Promise((resolve) => setTimeout(resolve, ms));
17622
+ }
17623
+ async function startComputerRunWithWait(action, executor, input, waitMs, ctx) {
17624
+ const deadline = Date.now() + waitMs;
17625
+ for (;; ) {
17626
+ try {
17627
+ return await action.run(executor, input);
17628
+ } catch (err) {
17629
+ const details = detailsForError(err);
17630
+ if (!isSetupPendingRefusal(details))
17631
+ throw err;
17632
+ const delay = typeof details?.retryAfterMs === "number" && details.retryAfterMs > 0 ? details.retryAfterMs : DEFAULT_SETUP_RETRY_AFTER_MS;
17633
+ const remaining = deadline - Date.now();
17634
+ if (delay > remaining)
17635
+ throw err;
17636
+ advise(`Computer setup is still running; retrying computer_run_start with the same idempotencyKey in ${Math.round(delay / 1000)}s (${Math.round(remaining / 1000)}s of --wait-ms left).
17637
+ `, ctx);
17638
+ await sleep2(delay);
17639
+ }
17640
+ }
17641
+ }
17642
+
17596
17643
  // src/commands.ts
17597
17644
  init_errors();
17598
17645
 
@@ -18163,7 +18210,7 @@ Aliases: ${aliases.map((alias) => `\`arbor ${alias}\``).join(", ")} (tool-name f
18163
18210
  const heading = `${commandWords(action)}${aliases.length === 1 ? ` (alias: ${aliases[0]})` : aliases.length > 1 ? ` (aliases: ${aliases.join(", ")})` : ""}`;
18164
18211
  const waitLines = action.name === "computer_exec" ? [
18165
18212
  " --no-wait [<boolean>] [optional] — return a running processId immediately instead of polling computer_process_read (implied by --background); mutually exclusive with --wait-for-terminal-ms"
18166
- ] : [];
18213
+ ] : action.name === "computer_run_start" ? [` --wait-ms <number> [optional] — ${WAIT_MS_DESCRIPTION}`] : [];
18167
18214
  const notes = action.cliHelpNotes ? `
18168
18215
 
18169
18216
  ${action.cliHelpNotes}` : "";
@@ -18176,9 +18223,10 @@ ${lines.join(`
18176
18223
  `)}${primaryNote}${explicitNote}${aliasNote}${notes}
18177
18224
  `;
18178
18225
  }
18226
+ const waitUsage = action.name === "computer_exec" ? " [--no-wait]" : action.name === "computer_run_start" ? " [--wait-ms <number>]" : "";
18179
18227
  return `${heading} — ${action.description}
18180
18228
 
18181
- ${usage}${waitLines.length > 0 ? " [--no-wait]" : ""}
18229
+ ${usage}${waitUsage}
18182
18230
 
18183
18231
  ${[...lines, ...waitLines].join(`
18184
18232
  `)}${primaryNote}${explicitNote}${aliasNote}${notes}
@@ -18205,6 +18253,15 @@ function commandCatalog(actions = CLI_ACTIONS) {
18205
18253
  acceptsFile: false,
18206
18254
  description: "return a running processId immediately instead of polling computer_process_read; mutually exclusive with --wait-for-terminal-ms"
18207
18255
  }
18256
+ ] : [],
18257
+ ...action.name === "computer_run_start" ? [
18258
+ {
18259
+ flag: "wait-ms",
18260
+ kind: "number",
18261
+ required: false,
18262
+ acceptsFile: false,
18263
+ description: WAIT_MS_DESCRIPTION
18264
+ }
18208
18265
  ] : []
18209
18266
  ]
18210
18267
  }));
@@ -18248,6 +18305,20 @@ function resolveCommand(positionals, flags) {
18248
18305
  const bareFeedbackTree = normalized.length === 1 && normalized[0] === "feedback" && !explicitFeedbackInput;
18249
18306
  return bareFeedbackTree ? { action: CLI_ACTIONS.find((action) => action.name === "feedback_tree"), words: 1 } : matchCommand(positionals);
18250
18307
  }
18308
+ var WAIT_MS_DESCRIPTION = "CLI-only: while Arbor answers that Computer setup is still running (retryable, suggestedAction computer_run_start), wait each retryAfterMs and repeat this exact start with the same idempotencyKey for up to this many milliseconds; a terminal setup failure or any other error returns at once. Omit for one attempt";
18309
+ function parseWaitMs(value) {
18310
+ const raw = stringFlag(value, "wait-ms");
18311
+ if (raw === undefined)
18312
+ return;
18313
+ const ms = Number(raw);
18314
+ if (!Number.isInteger(ms) || ms < 0) {
18315
+ throw new UsageError(`--wait-ms expects a non-negative whole number of milliseconds, got "${raw}"`, {
18316
+ reason: "validation.invalid_value",
18317
+ field: "waitMs"
18318
+ });
18319
+ }
18320
+ return ms;
18321
+ }
18251
18322
  async function runObjectVerb(positionals, flags, ctx) {
18252
18323
  const match = resolveCommand(positionals, flags);
18253
18324
  if (!match) {
@@ -18258,6 +18329,8 @@ async function runObjectVerb(positionals, flags, ctx) {
18258
18329
  const action = { ...match.action, inputSchema: canonicalInputShape(match.action) };
18259
18330
  applyPositionals(action, positionals.slice(words), flags);
18260
18331
  const accepted = acceptedFlags(action.inputSchema);
18332
+ if (action.name === "computer_run_start")
18333
+ accepted.add("wait-ms");
18261
18334
  const unknown = Object.keys(flags).filter((f) => !accepted.has(f) && !RESERVED_FLAGS.has(f));
18262
18335
  if (unknown.length > 0) {
18263
18336
  const hints = unknown.map((f) => listFileHint(action, f)).filter(Boolean);
@@ -18287,6 +18360,7 @@ async function runObjectVerb(positionals, flags, ctx) {
18287
18360
  field: "waitForTerminalMs"
18288
18361
  });
18289
18362
  }
18363
+ const waitMs = action.name === "computer_run_start" ? parseWaitMs(flags["wait-ms"]) : undefined;
18290
18364
  if (action.name === "app_request" && input.json !== undefined) {
18291
18365
  if (input.body !== undefined) {
18292
18366
  throw new UsageError("--json and --body are mutually exclusive", {
@@ -18307,7 +18381,7 @@ async function runObjectVerb(positionals, flags, ctx) {
18307
18381
  advise(`Agent registered. Save its apiKey now — it is shown only once:
18308
18382
  `, ctx);
18309
18383
  }
18310
- let value = action.name === "attachment_upload" ? await uploadLocalAttachments(httpExecutor, input) : await action.run(httpExecutor, input);
18384
+ let value = action.name === "attachment_upload" ? await uploadLocalAttachments(httpExecutor, input) : waitMs !== undefined ? await startComputerRunWithWait(action, httpExecutor, input, waitMs, ctx) : await action.run(httpExecutor, input);
18311
18385
  if (action.name === "computer_exec" && !truthyFlag(flags["no-wait"]) && input.waitForTerminalMs === undefined && input.background !== true) {
18312
18386
  value = await waitForComputerExec(httpExecutor, input, value, ctx);
18313
18387
  }
@@ -18320,7 +18394,7 @@ init_config();
18320
18394
  // src/connect.ts
18321
18395
  init_config();
18322
18396
  var DEFAULT_POLL_INTERVAL_MS = 2000;
18323
- var sleep2 = (ms) => new Promise((r) => globalThis.setTimeout(r, ms));
18397
+ var sleep3 = (ms) => new Promise((r) => globalThis.setTimeout(r, ms));
18324
18398
  function bareConnectUrl(apiUrl, returnedUrl) {
18325
18399
  const fallback = `${apiUrl.replace(/\/$/, "")}/connect`;
18326
18400
  if (!returnedUrl)
@@ -18378,7 +18452,7 @@ async function connect2(opts) {
18378
18452
  if (typeof globalThis.setTimeout !== "function") {
18379
18453
  throw new Error("approval pending — approve it, then rerun the same `arbor connect` command to finish saving this agent's credential");
18380
18454
  }
18381
- await sleep2(pollIntervalMs);
18455
+ await sleep3(pollIntervalMs);
18382
18456
  continue;
18383
18457
  }
18384
18458
  if (tok.status === "expired")
@@ -18398,7 +18472,7 @@ init_errors();
18398
18472
  init_config();
18399
18473
  var CLIENT_ID = "arbor-cli";
18400
18474
  var GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
18401
- var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
18475
+ var sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
18402
18476
  async function login(opts) {
18403
18477
  const apiUrl = opts.url ?? loadConfig().apiUrl;
18404
18478
  const codeRes = await fetch(`${apiUrl}/api/auth/device/code`, {
@@ -18423,7 +18497,7 @@ async function login(opts) {
18423
18497
  for (;; ) {
18424
18498
  if (Date.now() > deadline)
18425
18499
  throw new Error("login timed out — run `arbor login` again");
18426
- await sleep3(interval);
18500
+ await sleep4(interval);
18427
18501
  const tokRes = await fetch(`${apiUrl}/api/auth/device/token`, {
18428
18502
  method: "POST",
18429
18503
  headers: { "content-type": "application/json" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.24.30",
3
+ "version": "0.24.32",
4
4
  "description": "The Arbor CLI — a shared workspace for people and agents. The human + headless-agent write path over Arbor's guarded operation surface.",
5
5
  "keywords": [
6
6
  "agents",