@lambdacurry/arbor 0.13.8 → 0.13.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 +123 -26
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -1810,6 +1810,9 @@ var appBundles = sqliteTable("app_bundles", {
1810
1810
  modules: text("modules", { mode: "json" }).$type().notNull(),
1811
1811
  staticAssets: text("static_assets", { mode: "json" }).$type().notNull(),
1812
1812
  buildProvenance: text("build_provenance", { mode: "json" }).$type(),
1813
+ sourceProvenance: text("source_provenance", {
1814
+ mode: "json"
1815
+ }).$type(),
1813
1816
  runtimeEntrypoint: text("runtime_entrypoint").$type().notNull(),
1814
1817
  capabilityRequirements: text("capability_requirements", { mode: "json" }).$type().notNull().default([]),
1815
1818
  stateSchemaVersion: integer("state_schema_version").notNull().default(0),
@@ -16534,6 +16537,29 @@ var checkpoint = exports_external.looseObject({
16534
16537
  backup: exports_external.string(),
16535
16538
  published: jsonObject
16536
16539
  });
16540
+ var computerProfile = exports_external.looseObject({
16541
+ id: exports_external.string(),
16542
+ version: exports_external.number(),
16543
+ shell: exports_external.string(),
16544
+ runtimes: exports_external.looseObject({ bun: exports_external.string() }),
16545
+ tools: exports_external.array(exports_external.string()),
16546
+ recommendedEditingPrimitive: exports_external.string()
16547
+ });
16548
+ var appBundleSourceProvenance = exports_external.looseObject({
16549
+ entryPath: exports_external.string(),
16550
+ artifactVersionSha256: exports_external.string(),
16551
+ entryPathSha256: exports_external.string(),
16552
+ git: exports_external.looseObject({
16553
+ repositoryRoot: exports_external.string(),
16554
+ head: exports_external.string().nullable(),
16555
+ branch: exports_external.string().nullable(),
16556
+ dirty: exports_external.boolean(),
16557
+ trackedChanges: exports_external.number().int().min(0),
16558
+ untrackedFiles: exports_external.number().int().min(0),
16559
+ entryPathTracked: exports_external.boolean()
16560
+ }).nullable(),
16561
+ warnings: exports_external.array(exports_external.string())
16562
+ });
16537
16563
  var profile2 = exports_external.looseObject({
16538
16564
  id,
16539
16565
  kind: exports_external.string(),
@@ -16720,6 +16746,10 @@ var paged = (key, item) => exports_external.looseObject({
16720
16746
  nextCursor: exports_external.string().optional()
16721
16747
  });
16722
16748
  var MCP_OUTPUT_SCHEMAS = {
16749
+ server_info: exports_external.looseObject({
16750
+ serverVersion: exports_external.string(),
16751
+ deployment: exports_external.looseObject({ id: exports_external.string(), sourceRevision: exports_external.string().nullable(), deployedAt: timestamp }).nullable()
16752
+ }),
16723
16753
  recall: exports_external.object({ result: exports_external.array(recallHit) }),
16724
16754
  contribute: exports_external.looseObject({
16725
16755
  contributionId: id,
@@ -17011,6 +17041,7 @@ var MCP_OUTPUT_SCHEMAS = {
17011
17041
  computerId: id,
17012
17042
  computerSessionId: id,
17013
17043
  backend: exports_external.enum(["worker-shell", "container"]),
17044
+ profile: computerProfile.nullable(),
17014
17045
  startingSnapshot: snapshot.nullable(),
17015
17046
  materialized: checkpoint.nullable(),
17016
17047
  runtime: jsonObject
@@ -17020,13 +17051,15 @@ var MCP_OUTPUT_SCHEMAS = {
17020
17051
  stderr: exports_external.string().optional(),
17021
17052
  exitCode: exports_external.number().optional(),
17022
17053
  jobId: exports_external.string().optional(),
17023
- status: exports_external.string().optional()
17054
+ status: exports_external.string().optional(),
17055
+ strict: exports_external.boolean()
17024
17056
  }),
17025
17057
  computer_status: exports_external.looseObject({
17026
17058
  ok: exports_external.boolean().optional(),
17027
17059
  status: exports_external.string().optional(),
17028
17060
  backend: exports_external.string().optional(),
17029
- processes: exports_external.array(jsonObject).optional()
17061
+ processes: exports_external.array(jsonObject).optional(),
17062
+ profile: computerProfile.nullable()
17030
17063
  }),
17031
17064
  computer_verify: exports_external.looseObject({
17032
17065
  passed: exports_external.literal(true),
@@ -17125,6 +17158,8 @@ var MCP_OUTPUT_SCHEMAS = {
17125
17158
  assetCount: exports_external.number().optional(),
17126
17159
  byteCount: exports_external.number().optional(),
17127
17160
  buildProvenance: jsonObject,
17161
+ sourceProvenance: appBundleSourceProvenance.nullable(),
17162
+ warnings: exports_external.array(exports_external.string()),
17128
17163
  replayed: exports_external.boolean()
17129
17164
  }),
17130
17165
  computer_stop: exports_external.looseObject({ checkpoint, stopped: jsonObject })
@@ -17183,6 +17218,7 @@ var idempotentOpenWorld = {
17183
17218
  openWorldHint: true
17184
17219
  };
17185
17220
  var MCP_TOOL_ANNOTATIONS = {
17221
+ server_info: readOnly,
17186
17222
  recall: readOnly,
17187
17223
  contribute: additive,
17188
17224
  edit: additive,
@@ -17319,6 +17355,15 @@ var PAGINATION_INPUT = {
17319
17355
  cursor: exports_external.string().optional().describe("opaque cursor from a prior response's nextCursor (use with --limit)")
17320
17356
  };
17321
17357
  var ACTION_DEFINITIONS = [
17358
+ {
17359
+ name: "server_info",
17360
+ title: "Inspect Arbor server version",
17361
+ description: "READ ONLY: Return the Arbor release version and current Cloudflare deployment identity so a client can distinguish a fresh server from a cached tool catalog. This cannot modify Arbor, Computer, App, access, or durable state.",
17362
+ inputSchema: {},
17363
+ surfaces: ["mcp", "cli", "computer-mcp", "computer-cli"],
17364
+ toolset: "structure",
17365
+ run: forward("server.info")
17366
+ },
17322
17367
  {
17323
17368
  name: "github_repositories",
17324
17369
  title: "List installed GitHub repositories",
@@ -17588,7 +17633,7 @@ var ACTION_DEFINITIONS = [
17588
17633
  {
17589
17634
  name: "computer_open",
17590
17635
  title: "Open a Thread computer",
17591
- description: "Open or resume this Thread's project environment for filesystem or command work. Returns computerSessionId; pass it unchanged to later Computer tools and to Contributions or responses produced from the work.",
17636
+ description: "Open or resume this Thread's project environment for filesystem or command work. Returns computerSessionId and, when a declared container profile is selected, its guaranteed shell/runtimes/tools/editing primitive; pass the session id unchanged to later Computer tools and to Contributions or responses produced from the work.",
17592
17637
  inputSchema: {
17593
17638
  threadId: exports_external.string().describe("the Arbor Thread whose stable computer to open, thr_…"),
17594
17639
  vcpus: exports_external.number().int().min(1).max(4).optional().describe("optional container vCPU request"),
@@ -17601,13 +17646,14 @@ var ACTION_DEFINITIONS = [
17601
17646
  {
17602
17647
  name: "computer_exec",
17603
17648
  title: "Run a command",
17604
- description: "Run a bounded command in an opened Thread computer, normally from /workspace. Use background only for the container backend; worker-shell deliberately serializes mutations and rejects long-lived processes.",
17649
+ description: "Run one bounded logical operation in an opened Thread computer, normally from /workspace. Strict Bash semantics are ON by default: an early command failure or failed pipeline makes the execution nonzero, while explicit handling such as `cmd || fallback` still works. Set strict=false only for an intentional best-effort script. Use background only for the container backend; worker-shell deliberately serializes mutations and rejects long-lived processes.",
17605
17650
  inputSchema: {
17606
17651
  computerSessionId: exports_external.string().describe("the computerSessionId returned by computer_open, cms_…"),
17607
17652
  command: exports_external.string().min(1).describe("the shell command to run"),
17608
17653
  cwd: exports_external.string().optional().describe("working directory under /workspace"),
17609
17654
  timeout: exports_external.number().int().min(1000).max(1800000).optional().describe("timeout in milliseconds, from 1,000 through 1,800,000"),
17610
- background: exports_external.boolean().optional().describe("start a container process and return its pid")
17655
+ background: exports_external.boolean().optional().describe("start a container process and return its pid"),
17656
+ 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")
17611
17657
  },
17612
17658
  surfaces: ["computer-mcp", "computer-cli"],
17613
17659
  toolset: "loop",
@@ -17616,7 +17662,7 @@ var ACTION_DEFINITIONS = [
17616
17662
  {
17617
17663
  name: "computer_status",
17618
17664
  title: "Inspect a computer",
17619
- description: "Read live Currybox process and runtime status for an opened computer. This is ephemeral provider state; use get_computer on the Arbor surface for durable config and snapshot lineage.",
17665
+ description: "READ ONLY: Read live Currybox process/runtime status and the declared container profile contract for an opened computer. This cannot modify files, processes, checkpoints, or Thread state; use get_computer on the Arbor surface for durable config and snapshot lineage.",
17620
17666
  inputSchema: {
17621
17667
  computerSessionId: exports_external.string().describe("the computerSessionId returned by computer_open, cms_…")
17622
17668
  },
@@ -17775,7 +17821,7 @@ var ACTION_DEFINITIONS = [
17775
17821
  {
17776
17822
  name: "computer_publish_app_bundle",
17777
17823
  title: "Publish an immutable AppBundle",
17778
- description: "After a Bun container build is green, freeze one Worker entry module plus optional static assets from this Thread computer into a content-addressed AppBundle sourced from an ArtifactVersion. Use the returned bundleId with app_deploy; uploads complete and verify before Arbor publishes the bundle manifest, and retries require the same idempotencyKey.",
17824
+ description: "After a Bun container build is green, verify that entryPath bytes exactly match the referenced ArtifactVersion, capture source hashes plus Git/workspace provenance, then freeze the Worker module and optional static assets into a content-addressed AppBundle. A mismatch is rejected; dirty or untracked source is allowed but returned as an explicit warning. Use the returned bundleId with app_deploy; retries require the same idempotencyKey.",
17779
17825
  inputSchema: {
17780
17826
  computerSessionId: exports_external.string().describe("the computerSessionId returned by computer_open, cms_…"),
17781
17827
  artifactVersionId: exports_external.string().describe("the source ArtifactVersion, artv_…"),
@@ -18889,7 +18935,7 @@ var ACTION_DEFINITIONS = [
18889
18935
  path: exports_external.string().min(1).max(2048).describe("relative App path beginning with /; may include a query string"),
18890
18936
  method: exports_external.enum(["GET", "HEAD"]).optional().describe("safe read method; default GET")
18891
18937
  },
18892
- surfaces: ["mcp"],
18938
+ surfaces: ["mcp", "cli"],
18893
18939
  toolset: "apps",
18894
18940
  run: forward("app.fetch")
18895
18941
  },
@@ -18901,11 +18947,11 @@ var ACTION_DEFINITIONS = [
18901
18947
  appId: exports_external.string().describe("target App, app_…"),
18902
18948
  path: exports_external.string().min(1).max(2048).describe("relative App path beginning with /; may include a query string"),
18903
18949
  method: exports_external.enum(["POST", "PUT", "PATCH", "DELETE"]).describe("mutating HTTP method"),
18904
- json: exports_external.json().optional().describe("JSON request value; mutually exclusive with body and sets application/json"),
18950
+ json: exports_external.json().optional().describe("JSON request value; mutually exclusive with body and sets application/json. On the CLI, pass serialized JSON after the command as --json '<value>'"),
18905
18951
  body: exports_external.string().optional().describe("UTF-8 request body; mutually exclusive with json"),
18906
18952
  contentType: exports_external.string().max(160).optional().describe("body media type; allowed only with body, default text/plain; charset=utf-8")
18907
18953
  },
18908
- surfaces: ["mcp"],
18954
+ surfaces: ["mcp", "cli"],
18909
18955
  toolset: "apps",
18910
18956
  run: forward("app.request")
18911
18957
  },
@@ -19501,6 +19547,17 @@ function commandWords(action) {
19501
19547
  var CLI_ACTIONS = ACTIONS.filter((a) => a.surfaces.includes("cli") || a.surfaces.includes("computer-cli"));
19502
19548
  var BY_COMMAND = new Map(CLI_ACTIONS.map((a) => [commandWords(a), a]));
19503
19549
  var MAX_WORDS = Math.max(1, ...CLI_ACTIONS.map((a) => commandWords(a).split(" ").length));
19550
+ var POSITIONAL_FIELDS = {
19551
+ app_fetch: ["appId", "path"],
19552
+ app_request: ["appId", "path"]
19553
+ };
19554
+ function positionalSpecs(action) {
19555
+ const fields = POSITIONAL_FIELDS[action.name];
19556
+ if (!fields)
19557
+ return [];
19558
+ const specs = flagsForSchema(action.inputSchema);
19559
+ return fields.map((field) => specs.find((spec) => spec.field === field));
19560
+ }
19504
19561
  var KNOWN_MISREACHES = [
19505
19562
  { tried: "thread list", use: "tree --depth threads", why: "threads are listed by the tree" },
19506
19563
  { tried: "threads", use: "tree --depth threads", why: "threads are listed by the tree" },
@@ -19559,12 +19616,15 @@ function toolNameAlias(action) {
19559
19616
  return action.name.includes("_") ? action.name : "";
19560
19617
  }
19561
19618
  function helpLine(action) {
19562
- const flags = flagsForSchema(action.inputSchema).map((f) => {
19619
+ const positionals = positionalSpecs(action);
19620
+ const positionalFlags = new Set(positionals.map((spec) => spec.flag));
19621
+ const args = positionals.map((spec) => ` <${spec.field}>`).join("");
19622
+ const flags = flagsForSchema(action.inputSchema).filter((spec) => !positionalFlags.has(spec.flag)).map((f) => {
19563
19623
  const token = f.kind === "boolean" ? `--${f.flag}` : `--${f.flag} <>`;
19564
19624
  return f.required ? token : `[${token}]`;
19565
19625
  }).join(" ");
19566
19626
  const alias2 = toolNameAlias(action);
19567
- const head = ` ${commandWords(action)}${alias2 ? ` (${alias2})` : ""}${flags ? ` ${flags}` : ""}`;
19627
+ const head = ` ${commandWords(action)}${alias2 ? ` (${alias2})` : ""}${args}${flags ? ` ${flags}` : ""}`;
19568
19628
  return `${head}
19569
19629
  ${action.description}`;
19570
19630
  }
@@ -19576,12 +19636,15 @@ function commandHelp(positionals) {
19576
19636
  return;
19577
19637
  const { action } = match;
19578
19638
  const specs = flagsForSchema(action.inputSchema);
19579
- const primary = primaryPositionalField(action.inputSchema);
19580
- const usageFlags = specs.map((f) => {
19639
+ const explicitPositionals = positionalSpecs(action);
19640
+ const primary = explicitPositionals.length === 0 ? primaryPositionalField(action.inputSchema) : undefined;
19641
+ const positionalFlags = new Set(explicitPositionals.map((spec) => spec.flag));
19642
+ const usageFlags = specs.filter((spec) => !positionalFlags.has(spec.flag)).map((f) => {
19581
19643
  const token = f.kind === "boolean" ? `--${f.flag}` : `--${f.flag} <${f.kind}>`;
19582
19644
  return f.required ? token : `[${token}]`;
19583
19645
  }).join(" ");
19584
- const usage = `Usage: arbor ${commandWords(action)}${primary ? ` [<${primary.field}>]` : ""}${usageFlags ? ` ${usageFlags}` : ""}`;
19646
+ const explicitArgs = explicitPositionals.map((spec) => ` <${spec.field}>`).join("");
19647
+ const usage = `Usage: arbor ${commandWords(action)}${explicitArgs}${primary ? ` [<${primary.field}>]` : ""}${usageFlags ? ` ${usageFlags}` : ""}`;
19585
19648
  const lines = specs.map((f) => {
19586
19649
  const req = f.required ? "required" : "optional";
19587
19650
  const fileNote = f.kind === "string" ? ` (or --${f.flag}-file <path|->)` : "";
@@ -19594,6 +19657,9 @@ function commandHelp(positionals) {
19594
19657
  const primaryNote = primary ? `
19595
19658
 
19596
19659
  A single bare argument fills --${primary.flag} (e.g. \`arbor ${commandWords(action)} <${primary.field}>\`).` : "";
19660
+ const explicitNote = explicitPositionals.length > 0 ? `
19661
+
19662
+ Bare arguments fill ${explicitPositionals.map((spec) => `--${spec.flag}`).join(" then ")}; the equivalent flags remain accepted.` : "";
19597
19663
  const alias2 = toolNameAlias(action);
19598
19664
  const aliasNote = alias2 ? `
19599
19665
 
@@ -19604,7 +19670,7 @@ Alias: \`arbor ${alias2}\` (the tool-name form) resolves to this same command.`
19604
19670
  ${usage}
19605
19671
 
19606
19672
  ${lines.join(`
19607
- `)}${primaryNote}${aliasNote}
19673
+ `)}${primaryNote}${explicitNote}${aliasNote}
19608
19674
  `;
19609
19675
  }
19610
19676
  function commandCatalog() {
@@ -19630,17 +19696,21 @@ async function runObjectVerb(positionals, flags, ctx) {
19630
19696
  const { action, words } = match;
19631
19697
  const extras = positionals.slice(words);
19632
19698
  if (extras.length > 0) {
19633
- const primary = primaryPositionalField(action.inputSchema);
19634
- if (!primary) {
19699
+ const explicitPositionals = positionalSpecs(action);
19700
+ const positionalTargets = explicitPositionals.length > 0 ? explicitPositionals : [primaryPositionalField(action.inputSchema)].filter((spec) => spec !== undefined);
19701
+ if (positionalTargets.length === 0) {
19635
19702
  throw new UsageError(`unexpected argument: ${extras[0]} (\`${commandWords(action)}\` takes no positional — use flags)`);
19636
19703
  }
19637
- if (extras.length > 1) {
19638
- throw new UsageError(`too many arguments: \`${commandWords(action)}\` takes one positional (--${primary.flag}); pass the rest as flags`);
19704
+ if (extras.length > positionalTargets.length) {
19705
+ throw new UsageError(`too many arguments: \`${commandWords(action)}\` takes ${positionalTargets.length === 1 ? "one positional" : `${positionalTargets.length} positionals`}; pass the rest as flags`);
19639
19706
  }
19640
- if (flags[primary.flag] !== undefined || flags[`${primary.flag}-file`] !== undefined) {
19641
- throw new UsageError(`pass --${primary.flag} either as a flag or as the positional argument, not both`);
19707
+ for (const [index2, value] of extras.entries()) {
19708
+ const target = positionalTargets[index2];
19709
+ if (flags[target.flag] !== undefined || flags[`${target.flag}-file`] !== undefined) {
19710
+ throw new UsageError(`pass --${target.flag} either as a flag or as the positional argument, not both`);
19711
+ }
19712
+ flags[target.flag] = value;
19642
19713
  }
19643
- flags[primary.flag] = extras[0];
19644
19714
  }
19645
19715
  const accepted = acceptedFlags(action.inputSchema);
19646
19716
  const unknown2 = Object.keys(flags).filter((f) => !accepted.has(f) && !RESERVED_FLAGS.has(f));
@@ -19648,7 +19718,27 @@ async function runObjectVerb(positionals, flags, ctx) {
19648
19718
  const hints = unknown2.map((f) => listFileHint(action, f)).filter(Boolean);
19649
19719
  throw new UsageError(`unknown flag${unknown2.length > 1 ? "s" : ""}: ${unknown2.map((f) => `--${f}`).join(", ")}${hints.length > 0 ? ` — ${hints.join("; ")}` : ""}`);
19650
19720
  }
19651
- const input = buildInput(action.inputSchema, flags, commandWords(action));
19721
+ const inputFlags = { ...flags };
19722
+ if (action.name === "app_request" && lastValue(inputFlags.json) === true) {
19723
+ delete inputFlags.json;
19724
+ }
19725
+ const input = buildInput(action.inputSchema, inputFlags, commandWords(action));
19726
+ if (action.name === "app_request" && input.json !== undefined) {
19727
+ if (input.body !== undefined) {
19728
+ throw new UsageError("--json and --body are mutually exclusive", {
19729
+ reason: "validation.invalid_value",
19730
+ field: "body"
19731
+ });
19732
+ }
19733
+ try {
19734
+ input.json = JSON.parse(String(input.json));
19735
+ } catch {
19736
+ throw new UsageError("--json expects a valid serialized JSON value", {
19737
+ reason: "validation.invalid_value",
19738
+ field: "json"
19739
+ });
19740
+ }
19741
+ }
19652
19742
  if (action.name === "agent_register") {
19653
19743
  advise(`Agent registered. Save its apiKey now — it is shown only once:
19654
19744
  `, ctx);
@@ -19761,6 +19851,11 @@ async function login(opts) {
19761
19851
  }
19762
19852
 
19763
19853
  // src/index.ts
19854
+ function resolveCommandOutput(positionals, flags, opts) {
19855
+ const actionName = matchCommand(positionals)?.action.name;
19856
+ const resolved = resolveOutput(flags, opts);
19857
+ return actionName === "app_fetch" || actionName === "app_request" ? { ...resolved, json: true, quiet: true } : resolved;
19858
+ }
19764
19859
  function setFlag(flags, key, value) {
19765
19860
  const prev = flags[key];
19766
19861
  if (prev === undefined || typeof value === "boolean" || typeof prev === "boolean") {
@@ -19777,9 +19872,11 @@ function parseArgs(argv) {
19777
19872
  if (arg.startsWith("--")) {
19778
19873
  const body = arg.slice(2);
19779
19874
  const eq = body.indexOf("=");
19875
+ const appRequestWords = positionals[0] === "app" && positionals[1] === "request" ? 2 : positionals[0] === "app_request" ? 1 : 0;
19876
+ const hasAppRequestTarget = appRequestWords > 0 && (positionals.length >= appRequestWords + 2 || flags["app-id"] !== undefined && flags.path !== undefined);
19780
19877
  if (eq !== -1) {
19781
19878
  setFlag(flags, body.slice(0, eq), body.slice(eq + 1));
19782
- } else if (!GLOBAL_BOOLEAN_FLAGS.has(body) && i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
19879
+ } else if ((!GLOBAL_BOOLEAN_FLAGS.has(body) || body === "json" && hasAppRequestTarget) && i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
19783
19880
  setFlag(flags, body, argv[++i]);
19784
19881
  } else {
19785
19882
  setFlag(flags, body, true);
@@ -19909,7 +20006,7 @@ async function renderHealth(ctx) {
19909
20006
  }
19910
20007
  async function main() {
19911
20008
  const { positionals, flags } = parseArgs(process.argv.slice(2));
19912
- const ctx = resolveOutput(flags);
20009
+ const ctx = resolveCommandOutput(positionals, flags);
19913
20010
  const [cmd] = positionals;
19914
20011
  try {
19915
20012
  if (flags.version !== undefined && positionals.length === 0 || cmd === "version") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.13.8",
3
+ "version": "0.13.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",