@lambdacurry/arbor 0.24.31 → 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 +81 -9
  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")
@@ -16751,7 +16767,7 @@ var ACTIONS = ACTION_DEFINITIONS.map((action) => {
16751
16767
  });
16752
16768
 
16753
16769
  // ../actions/src/retention-entry.ts
16754
- var ARBOR_VERSION = "0.24.31";
16770
+ var ARBOR_VERSION = "0.24.32";
16755
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");
16756
16772
  var retainSourceSchema = boolean2().optional().describe("edit: in checkpoint mode, retain this newly minted content revision's source");
16757
16773
  var checkpointSchema = boolean2().optional().describe("edit: retain the exact current version source without incrementing content version");
@@ -17159,7 +17175,7 @@ function dispositionFilename(value) {
17159
17175
  // package.json
17160
17176
  var package_default = {
17161
17177
  name: "@lambdacurry/arbor",
17162
- version: "0.24.31",
17178
+ version: "0.24.32",
17163
17179
  description: "The Arbor CLI — a shared workspace for people and agents. The human + headless-agent write path over Arbor's guarded operation surface.",
17164
17180
  keywords: [
17165
17181
  "agents",
@@ -17595,6 +17611,35 @@ async function waitForComputerExec(executor, input, value, ctx) {
17595
17611
  return withContinuation(latest);
17596
17612
  }
17597
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
+
17598
17643
  // src/commands.ts
17599
17644
  init_errors();
17600
17645
 
@@ -18165,7 +18210,7 @@ Aliases: ${aliases.map((alias) => `\`arbor ${alias}\``).join(", ")} (tool-name f
18165
18210
  const heading = `${commandWords(action)}${aliases.length === 1 ? ` (alias: ${aliases[0]})` : aliases.length > 1 ? ` (aliases: ${aliases.join(", ")})` : ""}`;
18166
18211
  const waitLines = action.name === "computer_exec" ? [
18167
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"
18168
- ] : [];
18213
+ ] : action.name === "computer_run_start" ? [` --wait-ms <number> [optional] — ${WAIT_MS_DESCRIPTION}`] : [];
18169
18214
  const notes = action.cliHelpNotes ? `
18170
18215
 
18171
18216
  ${action.cliHelpNotes}` : "";
@@ -18178,9 +18223,10 @@ ${lines.join(`
18178
18223
  `)}${primaryNote}${explicitNote}${aliasNote}${notes}
18179
18224
  `;
18180
18225
  }
18226
+ const waitUsage = action.name === "computer_exec" ? " [--no-wait]" : action.name === "computer_run_start" ? " [--wait-ms <number>]" : "";
18181
18227
  return `${heading} — ${action.description}
18182
18228
 
18183
- ${usage}${waitLines.length > 0 ? " [--no-wait]" : ""}
18229
+ ${usage}${waitUsage}
18184
18230
 
18185
18231
  ${[...lines, ...waitLines].join(`
18186
18232
  `)}${primaryNote}${explicitNote}${aliasNote}${notes}
@@ -18207,6 +18253,15 @@ function commandCatalog(actions = CLI_ACTIONS) {
18207
18253
  acceptsFile: false,
18208
18254
  description: "return a running processId immediately instead of polling computer_process_read; mutually exclusive with --wait-for-terminal-ms"
18209
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
+ }
18210
18265
  ] : []
18211
18266
  ]
18212
18267
  }));
@@ -18250,6 +18305,20 @@ function resolveCommand(positionals, flags) {
18250
18305
  const bareFeedbackTree = normalized.length === 1 && normalized[0] === "feedback" && !explicitFeedbackInput;
18251
18306
  return bareFeedbackTree ? { action: CLI_ACTIONS.find((action) => action.name === "feedback_tree"), words: 1 } : matchCommand(positionals);
18252
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
+ }
18253
18322
  async function runObjectVerb(positionals, flags, ctx) {
18254
18323
  const match = resolveCommand(positionals, flags);
18255
18324
  if (!match) {
@@ -18260,6 +18329,8 @@ async function runObjectVerb(positionals, flags, ctx) {
18260
18329
  const action = { ...match.action, inputSchema: canonicalInputShape(match.action) };
18261
18330
  applyPositionals(action, positionals.slice(words), flags);
18262
18331
  const accepted = acceptedFlags(action.inputSchema);
18332
+ if (action.name === "computer_run_start")
18333
+ accepted.add("wait-ms");
18263
18334
  const unknown = Object.keys(flags).filter((f) => !accepted.has(f) && !RESERVED_FLAGS.has(f));
18264
18335
  if (unknown.length > 0) {
18265
18336
  const hints = unknown.map((f) => listFileHint(action, f)).filter(Boolean);
@@ -18289,6 +18360,7 @@ async function runObjectVerb(positionals, flags, ctx) {
18289
18360
  field: "waitForTerminalMs"
18290
18361
  });
18291
18362
  }
18363
+ const waitMs = action.name === "computer_run_start" ? parseWaitMs(flags["wait-ms"]) : undefined;
18292
18364
  if (action.name === "app_request" && input.json !== undefined) {
18293
18365
  if (input.body !== undefined) {
18294
18366
  throw new UsageError("--json and --body are mutually exclusive", {
@@ -18309,7 +18381,7 @@ async function runObjectVerb(positionals, flags, ctx) {
18309
18381
  advise(`Agent registered. Save its apiKey now — it is shown only once:
18310
18382
  `, ctx);
18311
18383
  }
18312
- 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);
18313
18385
  if (action.name === "computer_exec" && !truthyFlag(flags["no-wait"]) && input.waitForTerminalMs === undefined && input.background !== true) {
18314
18386
  value = await waitForComputerExec(httpExecutor, input, value, ctx);
18315
18387
  }
@@ -18322,7 +18394,7 @@ init_config();
18322
18394
  // src/connect.ts
18323
18395
  init_config();
18324
18396
  var DEFAULT_POLL_INTERVAL_MS = 2000;
18325
- var sleep2 = (ms) => new Promise((r) => globalThis.setTimeout(r, ms));
18397
+ var sleep3 = (ms) => new Promise((r) => globalThis.setTimeout(r, ms));
18326
18398
  function bareConnectUrl(apiUrl, returnedUrl) {
18327
18399
  const fallback = `${apiUrl.replace(/\/$/, "")}/connect`;
18328
18400
  if (!returnedUrl)
@@ -18380,7 +18452,7 @@ async function connect2(opts) {
18380
18452
  if (typeof globalThis.setTimeout !== "function") {
18381
18453
  throw new Error("approval pending — approve it, then rerun the same `arbor connect` command to finish saving this agent's credential");
18382
18454
  }
18383
- await sleep2(pollIntervalMs);
18455
+ await sleep3(pollIntervalMs);
18384
18456
  continue;
18385
18457
  }
18386
18458
  if (tok.status === "expired")
@@ -18400,7 +18472,7 @@ init_errors();
18400
18472
  init_config();
18401
18473
  var CLIENT_ID = "arbor-cli";
18402
18474
  var GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
18403
- var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
18475
+ var sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
18404
18476
  async function login(opts) {
18405
18477
  const apiUrl = opts.url ?? loadConfig().apiUrl;
18406
18478
  const codeRes = await fetch(`${apiUrl}/api/auth/device/code`, {
@@ -18425,7 +18497,7 @@ async function login(opts) {
18425
18497
  for (;; ) {
18426
18498
  if (Date.now() > deadline)
18427
18499
  throw new Error("login timed out — run `arbor login` again");
18428
- await sleep3(interval);
18500
+ await sleep4(interval);
18429
18501
  const tokRes = await fetch(`${apiUrl}/api/auth/device/token`, {
18430
18502
  method: "POST",
18431
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.31",
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",