@lambdacurry/arbor 0.21.9 → 0.21.10

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 +104 -20
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -18365,7 +18365,9 @@ var MCP_OUTPUT_SCHEMAS = {
18365
18365
  exitCode: exports_external.number().optional(),
18366
18366
  processId: exports_external.string().optional(),
18367
18367
  status: exports_external.string().optional(),
18368
- cwd: exports_external.string().optional()
18368
+ cwd: exports_external.string().optional(),
18369
+ execution: exports_external.enum(["completed", "running"]).optional(),
18370
+ nextAction: exports_external.literal("computer_process_read").optional()
18369
18371
  }),
18370
18372
  computer_process_read: exports_external.looseObject({
18371
18373
  status: exports_external.string(),
@@ -18682,6 +18684,8 @@ var OUTPUT_SHAPERS = {
18682
18684
  ["exitCode", result.exitCode],
18683
18685
  ["processId", result.processId],
18684
18686
  ["status", result.status],
18687
+ ["execution", result.execution],
18688
+ ["nextAction", result.nextAction],
18685
18689
  ["cwd", input.cwd === undefined ? result.cwd : undefined]
18686
18690
  ]),
18687
18691
  computer_process_read: (result) => defined([
@@ -18906,7 +18910,9 @@ Arbor's MCP is the collaboration and durable-knowledge surface. Configure, autho
18906
18910
  8. DURABLE OUTPUT IS AN ARTIFACT, NOT A LONG CONTRIBUTION (AD-201). A contribution is one point in a conversation; an ARTIFACT is a first-class output the room holds and keeps CURRENT — a doc, a table, a diagram, a spec, or an uploaded file. When your point is really a deliverable the team will read or UPDATE later — a table of options, a reference doc, a plan, a rubric — make it an artifact (\`artifact\` verb=create with kind doc/table/diagram/html + \`source\`, or promote a file) instead of pasting it into prose. Text-kind artifacts are LIVING: \`artifact_get\` it, then \`artifact\` verb=edit naming the \`baseVersion\` you read — patches apply exact-match and atomically, one version per call, every version attributed. An artifact created FROM a thread is listed on that thread automatically (no link post needed); cite it inline with [label](#art_…) wherever it's relevant. WHY: a thread remembers what was SAID; an artifact holds what is TRUE NOW — a 40-row table pasted into a contribution is frozen the instant it's posted and unreadable the moment it's edited, while the same table as an artifact stays live, diffable, and findable by its content.
18907
18911
 
18908
18912
  Run \`arbor help\` (or read the MCP tool list) for the exact command/flags — those stay generated from the live action surface, so they're always current.`;
18909
- var COMPUTER_ORIENTATION = `Arbor Computer is the complete software execution platform attached to Arbor's durable collaboration graph. It configures project environments, executes and verifies work, packages and releases Apps, and preserves reviewable evidence; it does not schedule agents or replace Arbor collaboration.
18913
+ var COMPUTER_ORIENTATION = `Long Computer work returns a handle do not hold one tool call for a full test or build. \`computer_exec\` runs a command; if the result is still running, take \`processId\` and poll \`computer_process_read\`. Do not chain \`pnpm test && pnpm build\` in one exec; split steps or pass \`background:true\`. The CLI waits on the same handle unless \`--no-wait\`.
18914
+
18915
+ Arbor Computer is the complete software execution platform attached to Arbor's durable collaboration graph. It configures project environments, executes and verifies work, packages and releases Apps, and preserves reviewable evidence; it does not schedule agents or replace Arbor collaboration.
18910
18916
 
18911
18917
  ${ARBOR_FEEDBACK_GUIDANCE}
18912
18918
 
@@ -19612,14 +19618,14 @@ var ACTION_DEFINITIONS = [
19612
19618
  {
19613
19619
  name: "computer_exec",
19614
19620
  title: "Run a command",
19615
- description: "Run one bounded shell operation in a parent Computer or Run with ambient GitHub authorization for connected humans. For long work prefer background:true + computer_process_read; explicit timeout terminates execution, while after a missed isolated response use computer_run_receipt.operations[].operationId with computer_process_read instead of repeating the mutation.",
19621
+ description: "Run one bounded shell operation in a parent Computer or Run with ambient GitHub authorization for connected humans. A long command returns processId if still running — poll computer_process_read (background:true skips the wait; timeout kills the process); after a missed isolated response use computer_run_receipt.operations[].operationId with computer_process_read instead of repeating the mutation.",
19616
19622
  inputSchema: {
19617
19623
  computerSessionId: exports_external.string().optional().describe("the computerSessionId returned by computer_open, cms_…; required unless runId is supplied"),
19618
19624
  command: exports_external.string().min(1).describe("the shell command to run"),
19619
19625
  runId: exports_external.string().optional().describe("route this command according to this Run execution placement, run_… (from computer_run_start)"),
19620
19626
  cwd: exports_external.string().optional().describe("working directory under /workspace, or under the run; omit to use defaultCwd"),
19621
- timeout: exports_external.number().int().min(1000).max(1800000).optional().describe("command deadline in milliseconds, from 1,000 through 1,800,000"),
19622
- background: exports_external.boolean().optional().describe("start a container process and return its pid"),
19627
+ timeout: exports_external.number().int().min(1000).max(1800000).optional().describe("process kill deadline in milliseconds, from 1,000 through 1,800,000 — not the RPC hold"),
19628
+ background: exports_external.boolean().optional().describe("start the process and return immediately without the short RPC wait"),
19623
19629
  activityLabel: exports_external.string().min(1).max(COMPUTER_RUN_ACTIVITY_LABEL_MAX).optional().describe("short human-readable label for run-scoped background work; Arbor records this label instead of the raw command or output"),
19624
19630
  activityKind: exports_external.literal("verification").optional().describe("mark a labeled run-scoped background command as a verification check; its provider-observed terminal result is recorded as verification.finished"),
19625
19631
  strict: exports_external.boolean().optional().describe("fail on the first unhandled command or pipeline failure; defaults to true, set false only for intentional best-effort execution")
@@ -21633,7 +21639,8 @@ var GLOBAL_BOOLEAN_FLAGS = new Set([
21633
21639
  "json",
21634
21640
  "quiet",
21635
21641
  "no-quiet",
21636
- "help"
21642
+ "help",
21643
+ "no-wait"
21637
21644
  ]);
21638
21645
  var RESERVED_FLAGS = new Set([
21639
21646
  ...GLOBAL_BOOLEAN_FLAGS,
@@ -21725,6 +21732,66 @@ function emitDual(data, human, action, ctx) {
21725
21732
  process.stdout.write(human);
21726
21733
  }
21727
21734
 
21735
+ // src/computer-exec-wait.ts
21736
+ var DEFAULT_PROCESS_DEADLINE_MS = 1800000;
21737
+ var POLL_MS = 400;
21738
+ var processRead = ACTIONS.find((action) => action.name === "computer_process_read");
21739
+ function record3(value) {
21740
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
21741
+ }
21742
+ function isRunning(value) {
21743
+ const status = typeof value.status === "string" ? value.status.toLowerCase() : "";
21744
+ if (value.done === false)
21745
+ return true;
21746
+ if (status === "running" || status === "started" || status === "starting")
21747
+ return true;
21748
+ if (value.done === true)
21749
+ return false;
21750
+ if (status === "completed" || status === "exited" || status === "failed" || status === "cancelled" || status === "terminated" || status === "killed" || status === "unknown") {
21751
+ return false;
21752
+ }
21753
+ if (typeof value.exitCode === "number")
21754
+ return false;
21755
+ return typeof value.processId === "string";
21756
+ }
21757
+ function sleep(ms) {
21758
+ return new Promise((resolve) => setTimeout(resolve, ms));
21759
+ }
21760
+ function isComputerExecHandle(value) {
21761
+ const rec = record3(value);
21762
+ return Boolean(rec && typeof rec.processId === "string" && isRunning(rec));
21763
+ }
21764
+ async function waitForComputerExec(executor, input, value, ctx) {
21765
+ if (!processRead || !isComputerExecHandle(value))
21766
+ return value;
21767
+ let latest = value;
21768
+ let cursor = typeof latest.nextCursor === "string" && latest.nextCursor ? latest.nextCursor : undefined;
21769
+ const deadline = Date.now() + (typeof input.timeout === "number" && Number.isFinite(input.timeout) ? input.timeout : DEFAULT_PROCESS_DEADLINE_MS);
21770
+ advise(`Process ${String(latest.processId)} is still running; polling computer_process_read.
21771
+ `, ctx);
21772
+ while (isRunning(latest) && Date.now() < deadline) {
21773
+ await sleep(POLL_MS);
21774
+ const read = record3(await processRead.run(executor, {
21775
+ processId: latest.processId,
21776
+ ...cursor ? { cursor } : {},
21777
+ ...typeof input.runId === "string" ? { runId: input.runId } : {},
21778
+ ...typeof input.computerSessionId === "string" ? { computerSessionId: input.computerSessionId } : {}
21779
+ }));
21780
+ if (!read) {
21781
+ throw new Error("computer_process_read returned an empty result");
21782
+ }
21783
+ latest = read;
21784
+ if (typeof read.nextCursor === "string" && read.nextCursor)
21785
+ cursor = read.nextCursor;
21786
+ const stdout = typeof latest.stdout === "string" ? latest.stdout : "";
21787
+ if (stdout)
21788
+ advise(stdout, ctx);
21789
+ if (latest.recovery)
21790
+ break;
21791
+ }
21792
+ return latest;
21793
+ }
21794
+
21728
21795
  // src/schema.ts
21729
21796
  import { readFileSync as readFileSync2 } from "node:fs";
21730
21797
  function kebab(field) {
@@ -22067,11 +22134,14 @@ Bare arguments fill ${explicitPositionals.map((spec) => `--${spec.flag}`).join("
22067
22134
 
22068
22135
  Alias: \`arbor ${alias2}\` (the tool-name form) resolves to this same command.` : "";
22069
22136
  const heading = `${commandWords(action)}${alias2 ? ` (alias: ${alias2})` : ""}`;
22137
+ const waitLines = action.name === "computer_exec" ? [
22138
+ " --no-wait [<boolean>] [optional] — return a running processId immediately instead of polling computer_process_read"
22139
+ ] : [];
22070
22140
  return `${heading} — ${action.description}
22071
22141
 
22072
- ${usage}
22142
+ ${usage}${waitLines.length > 0 ? " [--no-wait]" : ""}
22073
22143
 
22074
- ${lines.join(`
22144
+ ${[...lines, ...waitLines].join(`
22075
22145
  `)}${primaryNote}${explicitNote}${aliasNote}
22076
22146
  `;
22077
22147
  }
@@ -22080,13 +22150,24 @@ function commandCatalog() {
22080
22150
  command: commandWords(action),
22081
22151
  name: action.name,
22082
22152
  description: action.description,
22083
- flags: flagsForSchema(action.inputSchema).map((f) => ({
22084
- flag: f.flag,
22085
- kind: f.kind,
22086
- required: f.required,
22087
- acceptsFile: f.kind === "string",
22088
- description: f.description
22089
- }))
22153
+ flags: [
22154
+ ...flagsForSchema(action.inputSchema).map((f) => ({
22155
+ flag: f.flag,
22156
+ kind: f.kind,
22157
+ required: f.required,
22158
+ acceptsFile: f.kind === "string",
22159
+ description: f.description
22160
+ })),
22161
+ ...action.name === "computer_exec" ? [
22162
+ {
22163
+ flag: "no-wait",
22164
+ kind: "boolean",
22165
+ required: false,
22166
+ acceptsFile: false,
22167
+ description: "return a running processId immediately instead of polling computer_process_read"
22168
+ }
22169
+ ] : []
22170
+ ]
22090
22171
  }));
22091
22172
  }
22092
22173
  function applyPositionals(action, extras, flags) {
@@ -22165,13 +22246,16 @@ async function runObjectVerb(positionals, flags, ctx) {
22165
22246
  advise(`Agent registered. Save its apiKey now — it is shown only once:
22166
22247
  `, ctx);
22167
22248
  }
22168
- const value = await action.run(httpExecutor, input);
22249
+ let value = await action.run(httpExecutor, input);
22250
+ if (action.name === "computer_exec" && !truthyFlag(flags["no-wait"])) {
22251
+ value = await waitForComputerExec(httpExecutor, input, value, ctx);
22252
+ }
22169
22253
  emitData(shapeActionOutput(action.name, value, input), action.name, ctx);
22170
22254
  }
22171
22255
 
22172
22256
  // src/connect.ts
22173
22257
  var DEFAULT_POLL_INTERVAL_MS = 2000;
22174
- var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
22258
+ var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
22175
22259
  function bareConnectUrl(apiUrl, returnedUrl) {
22176
22260
  const fallback = `${apiUrl.replace(/\/$/, "")}/connect`;
22177
22261
  if (!returnedUrl)
@@ -22215,7 +22299,7 @@ async function connect(opts) {
22215
22299
  if (Date.now() > deadline) {
22216
22300
  throw new Error("pairing timed out — ask for a new pairing key and run `arbor connect` again");
22217
22301
  }
22218
- await sleep(pollIntervalMs);
22302
+ await sleep2(pollIntervalMs);
22219
22303
  const tokRes = await fetch(`${apiUrl}/api/agent/connect/token`, {
22220
22304
  method: "POST",
22221
22305
  headers,
@@ -22241,7 +22325,7 @@ async function connect(opts) {
22241
22325
  // src/login.ts
22242
22326
  var CLIENT_ID = "arbor-cli";
22243
22327
  var GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
22244
- var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
22328
+ var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
22245
22329
  async function login(opts) {
22246
22330
  const apiUrl = opts.url ?? loadConfig().apiUrl;
22247
22331
  const codeRes = await fetch(`${apiUrl}/api/auth/device/code`, {
@@ -22266,7 +22350,7 @@ async function login(opts) {
22266
22350
  for (;; ) {
22267
22351
  if (Date.now() > deadline)
22268
22352
  throw new Error("login timed out — run `arbor login` again");
22269
- await sleep2(interval);
22353
+ await sleep3(interval);
22270
22354
  const tokRes = await fetch(`${apiUrl}/api/auth/device/token`, {
22271
22355
  method: "POST",
22272
22356
  headers: { "content-type": "application/json" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.21.9",
3
+ "version": "0.21.10",
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",