@lambdacurry/arbor 0.13.7 → 0.13.9

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 +101 -21
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -1847,7 +1847,7 @@ var apps = sqliteTable("apps", {
1847
1847
  stateMode: text("state_mode").$type().notNull(),
1848
1848
  stateIdentity: text("state_identity").notNull(),
1849
1849
  stateGeneration: integer("state_generation").notNull().default(0),
1850
- pendingStateResetId: text("pending_state_reset_id"),
1850
+ pendingStateOperationId: text("pending_state_reset_id"),
1851
1851
  currentStateSchemaVersion: integer("current_state_schema_version").notNull().default(0),
1852
1852
  deploymentRevision: integer("deployment_revision").notNull().default(0),
1853
1853
  metadataRevision: integer("metadata_revision").notNull().default(0),
@@ -1897,6 +1897,19 @@ var appStateResets = sqliteTable("app_state_resets", {
1897
1897
  activeDeploymentId: text("active_deployment_id"),
1898
1898
  deploymentRevision: integer("deployment_revision"),
1899
1899
  targetStateSchemaVersion: integer("target_state_schema_version"),
1900
+ recoveryBookmark: text("recovery_bookmark"),
1901
+ recoveryExpiresAt: ts("recovery_expires_at"),
1902
+ previousStateSchemaVersion: integer("previous_state_schema_version"),
1903
+ restoreIdempotencyKey: text("restore_idempotency_key"),
1904
+ restoredByProfileId: text("restored_by_profile_id").references(() => profiles.id),
1905
+ restoreExecutionContextId: text("restore_execution_context_id").references(() => executionContexts.id),
1906
+ restoreProviderReceipt: text("restore_provider_receipt", {
1907
+ mode: "json"
1908
+ }).$type(),
1909
+ restoreFromGeneration: integer("restore_from_generation"),
1910
+ restoreToGeneration: integer("restore_to_generation"),
1911
+ restoreRequestedAt: ts("restore_requested_at"),
1912
+ restoredAt: ts("restored_at"),
1900
1913
  createdAt: ts("created_at").notNull(),
1901
1914
  completedAt: ts("completed_at")
1902
1915
  }, (t) => ({
@@ -16983,6 +16996,7 @@ var MCP_OUTPUT_SCHEMAS = {
16983
16996
  }),
16984
16997
  app_set_access: app2,
16985
16998
  app_reset_state: exports_external.looseObject({ reset: jsonObject, replayed: exports_external.boolean() }),
16999
+ app_restore_state: exports_external.looseObject({ reset: jsonObject, replayed: exports_external.boolean() }),
16986
17000
  app_archive: app2,
16987
17001
  charter: exports_external.looseObject({
16988
17002
  ok: exports_external.boolean().optional(),
@@ -17231,6 +17245,7 @@ var MCP_TOOL_ANNOTATIONS = {
17231
17245
  app_rollback: additive,
17232
17246
  app_set_access: additive,
17233
17247
  app_reset_state: destructiveIdempotent,
17248
+ app_restore_state: destructiveIdempotent,
17234
17249
  app_archive: additive,
17235
17250
  charter: destructive,
17236
17251
  computer_open: additive,
@@ -18874,7 +18889,7 @@ var ACTION_DEFINITIONS = [
18874
18889
  path: exports_external.string().min(1).max(2048).describe("relative App path beginning with /; may include a query string"),
18875
18890
  method: exports_external.enum(["GET", "HEAD"]).optional().describe("safe read method; default GET")
18876
18891
  },
18877
- surfaces: ["mcp"],
18892
+ surfaces: ["mcp", "cli"],
18878
18893
  toolset: "apps",
18879
18894
  run: forward("app.fetch")
18880
18895
  },
@@ -18886,11 +18901,11 @@ var ACTION_DEFINITIONS = [
18886
18901
  appId: exports_external.string().describe("target App, app_…"),
18887
18902
  path: exports_external.string().min(1).max(2048).describe("relative App path beginning with /; may include a query string"),
18888
18903
  method: exports_external.enum(["POST", "PUT", "PATCH", "DELETE"]).describe("mutating HTTP method"),
18889
- json: exports_external.json().optional().describe("JSON request value; mutually exclusive with body and sets application/json"),
18904
+ 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>'"),
18890
18905
  body: exports_external.string().optional().describe("UTF-8 request body; mutually exclusive with json"),
18891
18906
  contentType: exports_external.string().max(160).optional().describe("body media type; allowed only with body, default text/plain; charset=utf-8")
18892
18907
  },
18893
- surfaces: ["mcp"],
18908
+ surfaces: ["mcp", "cli"],
18894
18909
  toolset: "apps",
18895
18910
  run: forward("app.request")
18896
18911
  },
@@ -18936,7 +18951,7 @@ var ACTION_DEFINITIONS = [
18936
18951
  {
18937
18952
  name: "app_reset_state",
18938
18953
  title: "Reset durable App state",
18939
- description: "Permanently delete one durable App facet's isolated SQLite database as an explicit attributed transition; deploying or rolling back never does this. Read app_get first, pass its expectedGeneration, and keep the same idempotencyKey across retries. A provider interruption leaves a visible pending intent that the same call resumes; do not change release or archive while pending.",
18954
+ description: "Delete one durable App facet's isolated SQLite database as an explicit attributed transition after capturing a Cloudflare recovery point valid for 30 days. Only the App creator or an Org owner/admin may do this; deploying or rolling back never does. Read app_get first, pass its expectedGeneration, and keep the same idempotencyKey across retries. A provider interruption leaves a visible pending intent that the same call resumes; do not change release or archive while pending.",
18940
18955
  inputSchema: {
18941
18956
  appId: exports_external.string().describe("durable App, app_…"),
18942
18957
  expectedGeneration: exports_external.number().int().min(0).describe("state generation from app_get"),
@@ -18946,6 +18961,20 @@ var ACTION_DEFINITIONS = [
18946
18961
  toolset: "apps",
18947
18962
  run: forward("app.reset_state")
18948
18963
  },
18964
+ {
18965
+ name: "app_restore_state",
18966
+ title: "Restore durable App state",
18967
+ description: "Restore the latest completed durable-state reset from its automatic Cloudflare recovery point, discarding state written after that reset. Recovery is available for 30 days, only while that reset remains latest and its active release has not changed. Only the App creator or an Org owner/admin may do this. Read app_get first, pass the reset id and current generation, and keep the same idempotencyKey across retries.",
18968
+ inputSchema: {
18969
+ appId: exports_external.string().describe("durable App, app_…"),
18970
+ resetId: exports_external.string().describe("latest recoverable reset from app_get, rst_…"),
18971
+ expectedGeneration: exports_external.number().int().min(0).describe("state generation from app_get"),
18972
+ idempotencyKey: exports_external.string().min(1).max(200).describe("stable key for this one deliberate recovery")
18973
+ },
18974
+ surfaces: ["mcp", "cli"],
18975
+ toolset: "apps",
18976
+ run: forward("app.restore_state")
18977
+ },
18949
18978
  {
18950
18979
  name: "app_archive",
18951
18980
  title: "Archive an App",
@@ -19472,6 +19501,17 @@ function commandWords(action) {
19472
19501
  var CLI_ACTIONS = ACTIONS.filter((a) => a.surfaces.includes("cli") || a.surfaces.includes("computer-cli"));
19473
19502
  var BY_COMMAND = new Map(CLI_ACTIONS.map((a) => [commandWords(a), a]));
19474
19503
  var MAX_WORDS = Math.max(1, ...CLI_ACTIONS.map((a) => commandWords(a).split(" ").length));
19504
+ var POSITIONAL_FIELDS = {
19505
+ app_fetch: ["appId", "path"],
19506
+ app_request: ["appId", "path"]
19507
+ };
19508
+ function positionalSpecs(action) {
19509
+ const fields = POSITIONAL_FIELDS[action.name];
19510
+ if (!fields)
19511
+ return [];
19512
+ const specs = flagsForSchema(action.inputSchema);
19513
+ return fields.map((field) => specs.find((spec) => spec.field === field));
19514
+ }
19475
19515
  var KNOWN_MISREACHES = [
19476
19516
  { tried: "thread list", use: "tree --depth threads", why: "threads are listed by the tree" },
19477
19517
  { tried: "threads", use: "tree --depth threads", why: "threads are listed by the tree" },
@@ -19530,12 +19570,15 @@ function toolNameAlias(action) {
19530
19570
  return action.name.includes("_") ? action.name : "";
19531
19571
  }
19532
19572
  function helpLine(action) {
19533
- const flags = flagsForSchema(action.inputSchema).map((f) => {
19573
+ const positionals = positionalSpecs(action);
19574
+ const positionalFlags = new Set(positionals.map((spec) => spec.flag));
19575
+ const args = positionals.map((spec) => ` <${spec.field}>`).join("");
19576
+ const flags = flagsForSchema(action.inputSchema).filter((spec) => !positionalFlags.has(spec.flag)).map((f) => {
19534
19577
  const token = f.kind === "boolean" ? `--${f.flag}` : `--${f.flag} <>`;
19535
19578
  return f.required ? token : `[${token}]`;
19536
19579
  }).join(" ");
19537
19580
  const alias2 = toolNameAlias(action);
19538
- const head = ` ${commandWords(action)}${alias2 ? ` (${alias2})` : ""}${flags ? ` ${flags}` : ""}`;
19581
+ const head = ` ${commandWords(action)}${alias2 ? ` (${alias2})` : ""}${args}${flags ? ` ${flags}` : ""}`;
19539
19582
  return `${head}
19540
19583
  ${action.description}`;
19541
19584
  }
@@ -19547,12 +19590,15 @@ function commandHelp(positionals) {
19547
19590
  return;
19548
19591
  const { action } = match;
19549
19592
  const specs = flagsForSchema(action.inputSchema);
19550
- const primary = primaryPositionalField(action.inputSchema);
19551
- const usageFlags = specs.map((f) => {
19593
+ const explicitPositionals = positionalSpecs(action);
19594
+ const primary = explicitPositionals.length === 0 ? primaryPositionalField(action.inputSchema) : undefined;
19595
+ const positionalFlags = new Set(explicitPositionals.map((spec) => spec.flag));
19596
+ const usageFlags = specs.filter((spec) => !positionalFlags.has(spec.flag)).map((f) => {
19552
19597
  const token = f.kind === "boolean" ? `--${f.flag}` : `--${f.flag} <${f.kind}>`;
19553
19598
  return f.required ? token : `[${token}]`;
19554
19599
  }).join(" ");
19555
- const usage = `Usage: arbor ${commandWords(action)}${primary ? ` [<${primary.field}>]` : ""}${usageFlags ? ` ${usageFlags}` : ""}`;
19600
+ const explicitArgs = explicitPositionals.map((spec) => ` <${spec.field}>`).join("");
19601
+ const usage = `Usage: arbor ${commandWords(action)}${explicitArgs}${primary ? ` [<${primary.field}>]` : ""}${usageFlags ? ` ${usageFlags}` : ""}`;
19556
19602
  const lines = specs.map((f) => {
19557
19603
  const req = f.required ? "required" : "optional";
19558
19604
  const fileNote = f.kind === "string" ? ` (or --${f.flag}-file <path|->)` : "";
@@ -19565,6 +19611,9 @@ function commandHelp(positionals) {
19565
19611
  const primaryNote = primary ? `
19566
19612
 
19567
19613
  A single bare argument fills --${primary.flag} (e.g. \`arbor ${commandWords(action)} <${primary.field}>\`).` : "";
19614
+ const explicitNote = explicitPositionals.length > 0 ? `
19615
+
19616
+ Bare arguments fill ${explicitPositionals.map((spec) => `--${spec.flag}`).join(" then ")}; the equivalent flags remain accepted.` : "";
19568
19617
  const alias2 = toolNameAlias(action);
19569
19618
  const aliasNote = alias2 ? `
19570
19619
 
@@ -19575,7 +19624,7 @@ Alias: \`arbor ${alias2}\` (the tool-name form) resolves to this same command.`
19575
19624
  ${usage}
19576
19625
 
19577
19626
  ${lines.join(`
19578
- `)}${primaryNote}${aliasNote}
19627
+ `)}${primaryNote}${explicitNote}${aliasNote}
19579
19628
  `;
19580
19629
  }
19581
19630
  function commandCatalog() {
@@ -19601,17 +19650,21 @@ async function runObjectVerb(positionals, flags, ctx) {
19601
19650
  const { action, words } = match;
19602
19651
  const extras = positionals.slice(words);
19603
19652
  if (extras.length > 0) {
19604
- const primary = primaryPositionalField(action.inputSchema);
19605
- if (!primary) {
19653
+ const explicitPositionals = positionalSpecs(action);
19654
+ const positionalTargets = explicitPositionals.length > 0 ? explicitPositionals : [primaryPositionalField(action.inputSchema)].filter((spec) => spec !== undefined);
19655
+ if (positionalTargets.length === 0) {
19606
19656
  throw new UsageError(`unexpected argument: ${extras[0]} (\`${commandWords(action)}\` takes no positional — use flags)`);
19607
19657
  }
19608
- if (extras.length > 1) {
19609
- throw new UsageError(`too many arguments: \`${commandWords(action)}\` takes one positional (--${primary.flag}); pass the rest as flags`);
19658
+ if (extras.length > positionalTargets.length) {
19659
+ throw new UsageError(`too many arguments: \`${commandWords(action)}\` takes ${positionalTargets.length === 1 ? "one positional" : `${positionalTargets.length} positionals`}; pass the rest as flags`);
19610
19660
  }
19611
- if (flags[primary.flag] !== undefined || flags[`${primary.flag}-file`] !== undefined) {
19612
- throw new UsageError(`pass --${primary.flag} either as a flag or as the positional argument, not both`);
19661
+ for (const [index2, value] of extras.entries()) {
19662
+ const target = positionalTargets[index2];
19663
+ if (flags[target.flag] !== undefined || flags[`${target.flag}-file`] !== undefined) {
19664
+ throw new UsageError(`pass --${target.flag} either as a flag or as the positional argument, not both`);
19665
+ }
19666
+ flags[target.flag] = value;
19613
19667
  }
19614
- flags[primary.flag] = extras[0];
19615
19668
  }
19616
19669
  const accepted = acceptedFlags(action.inputSchema);
19617
19670
  const unknown2 = Object.keys(flags).filter((f) => !accepted.has(f) && !RESERVED_FLAGS.has(f));
@@ -19619,7 +19672,27 @@ async function runObjectVerb(positionals, flags, ctx) {
19619
19672
  const hints = unknown2.map((f) => listFileHint(action, f)).filter(Boolean);
19620
19673
  throw new UsageError(`unknown flag${unknown2.length > 1 ? "s" : ""}: ${unknown2.map((f) => `--${f}`).join(", ")}${hints.length > 0 ? ` — ${hints.join("; ")}` : ""}`);
19621
19674
  }
19622
- const input = buildInput(action.inputSchema, flags, commandWords(action));
19675
+ const inputFlags = { ...flags };
19676
+ if (action.name === "app_request" && lastValue(inputFlags.json) === true) {
19677
+ delete inputFlags.json;
19678
+ }
19679
+ const input = buildInput(action.inputSchema, inputFlags, commandWords(action));
19680
+ if (action.name === "app_request" && input.json !== undefined) {
19681
+ if (input.body !== undefined) {
19682
+ throw new UsageError("--json and --body are mutually exclusive", {
19683
+ reason: "validation.invalid_value",
19684
+ field: "body"
19685
+ });
19686
+ }
19687
+ try {
19688
+ input.json = JSON.parse(String(input.json));
19689
+ } catch {
19690
+ throw new UsageError("--json expects a valid serialized JSON value", {
19691
+ reason: "validation.invalid_value",
19692
+ field: "json"
19693
+ });
19694
+ }
19695
+ }
19623
19696
  if (action.name === "agent_register") {
19624
19697
  advise(`Agent registered. Save its apiKey now — it is shown only once:
19625
19698
  `, ctx);
@@ -19732,6 +19805,11 @@ async function login(opts) {
19732
19805
  }
19733
19806
 
19734
19807
  // src/index.ts
19808
+ function resolveCommandOutput(positionals, flags, opts) {
19809
+ const actionName = matchCommand(positionals)?.action.name;
19810
+ const resolved = resolveOutput(flags, opts);
19811
+ return actionName === "app_fetch" || actionName === "app_request" ? { ...resolved, json: true, quiet: true } : resolved;
19812
+ }
19735
19813
  function setFlag(flags, key, value) {
19736
19814
  const prev = flags[key];
19737
19815
  if (prev === undefined || typeof value === "boolean" || typeof prev === "boolean") {
@@ -19748,9 +19826,11 @@ function parseArgs(argv) {
19748
19826
  if (arg.startsWith("--")) {
19749
19827
  const body = arg.slice(2);
19750
19828
  const eq = body.indexOf("=");
19829
+ const appRequestWords = positionals[0] === "app" && positionals[1] === "request" ? 2 : positionals[0] === "app_request" ? 1 : 0;
19830
+ const hasAppRequestTarget = appRequestWords > 0 && (positionals.length >= appRequestWords + 2 || flags["app-id"] !== undefined && flags.path !== undefined);
19751
19831
  if (eq !== -1) {
19752
19832
  setFlag(flags, body.slice(0, eq), body.slice(eq + 1));
19753
- } else if (!GLOBAL_BOOLEAN_FLAGS.has(body) && i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
19833
+ } else if ((!GLOBAL_BOOLEAN_FLAGS.has(body) || body === "json" && hasAppRequestTarget) && i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
19754
19834
  setFlag(flags, body, argv[++i]);
19755
19835
  } else {
19756
19836
  setFlag(flags, body, true);
@@ -19880,7 +19960,7 @@ async function renderHealth(ctx) {
19880
19960
  }
19881
19961
  async function main() {
19882
19962
  const { positionals, flags } = parseArgs(process.argv.slice(2));
19883
- const ctx = resolveOutput(flags);
19963
+ const ctx = resolveCommandOutput(positionals, flags);
19884
19964
  const [cmd] = positionals;
19885
19965
  try {
19886
19966
  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.7",
3
+ "version": "0.13.9",
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",