busa-cli 0.9.13 → 0.9.15

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.
@@ -912,7 +912,6 @@ async function loopbackOauthLogin(baseUrl, useBrowser) {
912
912
  authorizeUrl.searchParams.set("code_challenge_method", "S256");
913
913
  authorizeUrl.searchParams.set("redirect_uri", redirect);
914
914
  authorizeUrl.searchParams.set("state", state);
915
- authorizeUrl.searchParams.set("prompt", "login");
916
915
  const href = authorizeUrl.toString();
917
916
  say("");
918
917
  say("Open this URL in your browser to sign in:");
@@ -1380,8 +1379,12 @@ var fieldTypeSchema = z5.enum([
1380
1379
  "ai_tags",
1381
1380
  "code",
1382
1381
  "json",
1383
- "yaml"
1382
+ "yaml",
1383
+ "formula",
1384
+ "lookup",
1385
+ "whiteboard"
1384
1386
  ]);
1387
+ var lookupRollupSchema = z5.enum(["values", "count", "sum", "average", "min", "max", "concatenate"]).default("values");
1385
1388
  var fieldOptionsSchema = z5.object({
1386
1389
  ai: z5.object({
1387
1390
  model: z5.string().optional(),
@@ -1411,7 +1414,26 @@ var fieldOptionsSchema = z5.object({
1411
1414
  height: z5.number().int().positive().max(1200).optional(),
1412
1415
  providers: z5.array(z5.string()).optional()
1413
1416
  }).optional(),
1417
+ // `formula` fields are server-computed (like the other computed types) from
1418
+ // `expression` at record write time — see packages/busabase-core's
1419
+ // domains/base/formula/ engine. `{slug}` references a sibling field, including
1420
+ // another `formula` field (chained formulas) — a dependency graph orders
1421
+ // computation and rejects cycles at field create/edit time.
1422
+ formula: z5.object({
1423
+ expression: z5.string().min(1)
1424
+ }).optional(),
1414
1425
  inverseFieldId: z5.string().optional(),
1426
+ // `lookup` fields pull a field's values across a `relation` field on the same
1427
+ // Base, optionally rolling them up into one scalar. Unlike every other computed
1428
+ // type they are NOT stored on the commit: a lookup's value depends on OTHER
1429
+ // records, which can change without this record ever being written, so it is
1430
+ // resolved at read time (see busabase-core's domains/base/logic/lookup-values.ts).
1431
+ lookup: z5.object({
1432
+ relationFieldSlug: z5.string().min(1).describe("Slug of a `relation` field on THIS Base \u2014 the hop to follow."),
1433
+ targetFieldSlug: z5.string().min(1).describe("Slug of the field on the related Base whose values are pulled over."),
1434
+ rollup: lookupRollupSchema.optional(),
1435
+ limit: z5.enum(["all", "first"]).optional().describe("`first` looks at only the first linked record; default `all`.")
1436
+ }).optional(),
1415
1437
  multiple: z5.boolean().optional(),
1416
1438
  // Display formatting for `number` columns (Notion-style: one number type,
1417
1439
  // a format option). `currency` renders via Intl.NumberFormat.
@@ -1464,11 +1486,11 @@ var createBaseInputSchema = z5.object({
1464
1486
  options: fieldOptionsSchema.optional().default({})
1465
1487
  })
1466
1488
  ).default([]),
1467
- // Review-first by default: without `autoMerge: true`, this proposes the Base
1468
- // as a pending ChangeRequest (status "in_review") instead of creating it
1469
- // immediately. Pass `autoMerge: true` only for callers that don't need human
1470
- // review (seed/migration scripts, an explicit no-review agent task).
1471
- autoMerge: z5.boolean().optional().default(false)
1489
+ // Permission-aware default: omitted merges immediately if the actor has
1490
+ // write access on the parent node, otherwise falls back to a pending
1491
+ // ChangeRequest (status "in_review"). Pass explicit `autoMerge: false` to
1492
+ // force review even with write access.
1493
+ autoMerge: z5.boolean().optional()
1472
1494
  });
1473
1495
  var createBaseFieldInputSchema = z5.object({
1474
1496
  name: fieldNameSchema,
@@ -1704,6 +1726,15 @@ var folderNodeType = {
1704
1726
  operations: []
1705
1727
  };
1706
1728
 
1729
+ // ../../packages/busabase-contract/src/domains/form/definition.ts
1730
+ var formNodeType = {
1731
+ type: "form",
1732
+ label: "Form",
1733
+ icon: "clipboard-list",
1734
+ capabilities: { hasDetail: true, creatable: true },
1735
+ operations: []
1736
+ };
1737
+
1707
1738
  // ../../packages/busabase-contract/src/domains/html/definition.ts
1708
1739
  var htmlNodeType = {
1709
1740
  type: "html",
@@ -1769,6 +1800,7 @@ var BUILTIN_NODE_TYPES = [
1769
1800
  airappNodeType,
1770
1801
  fileNodeType,
1771
1802
  docNodeType,
1803
+ formNodeType,
1772
1804
  whiteboardNodeType,
1773
1805
  workflowNodeType,
1774
1806
  htmlNodeType
@@ -1830,6 +1862,15 @@ var nodePrincipalSchema = z6.object({
1830
1862
  createdAt: z6.string(),
1831
1863
  updatedAt: z6.string()
1832
1864
  });
1865
+ var nodeShareSchema = z6.object({
1866
+ nodeId: z6.string(),
1867
+ scope: z6.enum(["none", "public"]),
1868
+ capability: z6.enum(["read", "submit"]),
1869
+ // Derived from `passwordHash != null` — the hash itself is never serialized.
1870
+ hasPassword: z6.boolean(),
1871
+ expiresAt: z6.string().nullable(),
1872
+ updatedAt: z6.string()
1873
+ });
1833
1874
  var listNodesInputSchema = z6.object({
1834
1875
  parentId: z6.string().nullable().optional().describe("Node to start from. Omit or null to start from the space root."),
1835
1876
  depth: z6.coerce.number().int().min(1).max(5).optional().describe(
@@ -2116,8 +2157,8 @@ var createNodeChangeRequestInputSchema = z6.object({
2116
2157
  'Explanation shown to the human reviewer. Write a conventional-commit style subject \u2014 imperative verb + what + why, e.g. "Reorganize marketing docs under a Campaigns folder".'
2117
2158
  ),
2118
2159
  submittedBy: z6.string().optional().default("local-producer"),
2119
- autoMerge: z6.boolean().optional().default(false).describe(
2120
- "Whether to approve and merge this structural node change immediately. Defaults to false so node Change Request creation is review-first unless explicitly requested."
2160
+ autoMerge: z6.boolean().optional().describe(
2161
+ "Whether to approve and merge this structural node change immediately. Omitted defaults to merging immediately if the actor has write access on every target node, otherwise falling back to a pending Change Request; pass explicit false to force review even with write access."
2121
2162
  ),
2122
2163
  operations: z6.array(nodeOperationInputSchema).min(1)
2123
2164
  });
@@ -2341,10 +2382,21 @@ var PACKAGE_COMPUTED_FIELD_TYPES = [
2341
2382
  "created_by",
2342
2383
  "updated_by",
2343
2384
  "auto_number",
2385
+ "formula",
2386
+ // `lookup` is derived from OTHER records and resolved at read time, so it
2387
+ // shows up in `headCommit.fields` like any other value — but publishing it
2388
+ // would ship derived data as if it were authored, and install would strip it
2389
+ // server-side anyway.
2390
+ "lookup",
2344
2391
  ...PACKAGE_AI_FIELD_TYPES
2345
2392
  ];
2346
2393
  var PACKAGE_DEFERRED_FIELD_TYPES = [
2347
2394
  "relation",
2395
+ // `lookup` hops through a `relation` field on the same Base, and the server
2396
+ // rejects a lookup whose relation sibling doesn't exist yet. Since relations
2397
+ // are themselves deferred to pass 2, a lookup created in pass 1 would fail
2398
+ // validation outright — it has to land in the same pass as the hop it needs.
2399
+ "lookup",
2348
2400
  ...PACKAGE_AI_FIELD_TYPES
2349
2401
  ];
2350
2402
  var PACKAGE_MAX_FILE_BYTES = 25 * 1024 * 1024;
@@ -2412,8 +2464,23 @@ var PackageFieldOptionsSchema = z8.object({
2412
2464
  height: z8.number().int().positive().max(1200).optional(),
2413
2465
  providers: z8.array(z8.string()).optional()
2414
2466
  }).optional(),
2467
+ /** Mirrors the API's `formula.expression` verbatim — `{slug}` references carry
2468
+ * over unchanged since the package format keys everything by slug already. */
2469
+ formula: z8.object({
2470
+ expression: z8.string().min(1)
2471
+ }).optional(),
2415
2472
  /** Package-only — replaces `inverseFieldId`. */
2416
2473
  inverseFieldSlug: z8.string().optional(),
2474
+ /** Carried verbatim: the API's own `lookup` config is already keyed entirely
2475
+ * by slug (`relationFieldSlug` / `targetFieldSlug`), so it survives a
2476
+ * re-id'ing install with no package-only alias. The companion `number`
2477
+ * snapshot rides along in the `number` key above. */
2478
+ lookup: z8.object({
2479
+ relationFieldSlug: z8.string().min(1),
2480
+ targetFieldSlug: z8.string().min(1),
2481
+ rollup: lookupRollupSchema.optional(),
2482
+ limit: z8.enum(["all", "first"]).optional()
2483
+ }).optional(),
2417
2484
  multiple: z8.boolean().optional(),
2418
2485
  number: z8.object({
2419
2486
  format: z8.enum(["plain", "currency"]).optional(),
@@ -4802,14 +4869,21 @@ The flags below skip the menu (handy for scripts / CI):
4802
4869
  addGlobalFlags(nodes.command("list")).description("Workspace node tree").action(runAction(state, (client) => client.nodes.list()));
4803
4870
  addGlobalFlags(nodes.command("create-change-request")).description("Propose a new node via a Change Request").addOption(
4804
4871
  new Option(`--type <${CREATABLE_NODE_TYPES2.join("|")}>`, "node type").choices(CREATABLE_NODE_TYPES2).makeOptionMandatory()
4805
- ).requiredOption("--slug <slug>", "node slug").requiredOption("--name <name>", "node name").option("--description <text>", "optional node description").option("--parent-node-id <id>", "parent folder node id; omit for root").option("--message <text>", "reviewer-facing Change Request message").option("--submitted-by <name>", "producer label").option("--field <slug:name:type...>", "base field, repeatable (for --type base)").option("--fields-json <json|@file>", "base fields as JSON array (for --type base)").option("--asset-id <id>", "backing Asset id (required for --type file)").addHelpText(
4872
+ ).requiredOption("--slug <slug>", "node slug").requiredOption("--name <name>", "node name").option("--description <text>", "optional node description").option("--parent-node-id <id>", "parent folder node id; omit for root").option("--message <text>", "reviewer-facing Change Request message").option("--submitted-by <name>", "producer label").option("--field <slug:name:type...>", "base field, repeatable (for --type base)").option("--fields-json <json|@file>", "base fields as JSON array (for --type base)").option("--asset-id <id>", "backing Asset id (required for --type file)").option(
4873
+ "--auto-merge",
4874
+ "skip review and create the node immediately if you have write access (default: merge immediately if you have write access, otherwise propose a pending Change Request)"
4875
+ ).option(
4876
+ "--require-review",
4877
+ "always propose a pending Change Request, even if you have write access"
4878
+ ).addHelpText(
4806
4879
  "after",
4807
4880
  `
4808
4881
  Examples:
4809
4882
  busabase-cli nodes create-change-request --type folder --slug cms --name "\u5185\u5BB9\u7BA1\u7406 CMS"
4810
4883
  busabase-cli nodes create-change-request --type base --slug blog --name "\u535A\u5BA2\u6587\u7AE0 Blog Posts" --field title:Title:text --field body:Body:markdown
4811
4884
  busabase-cli nodes create-change-request --type base --slug products --name "\u4EA7\u54C1\u76EE\u5F55 Products" --fields-json @fields.json
4812
- busabase-cli nodes create-change-request --type file --slug board-plan --name "Board Plan" --asset-id ast_123`
4885
+ busabase-cli nodes create-change-request --type file --slug board-plan --name "Board Plan" --asset-id ast_123
4886
+ busabase-cli nodes create-change-request --type folder --slug cms --name "\u5185\u5BB9\u7BA1\u7406 CMS" --require-review # always propose a Change Request`
4813
4887
  ).action(
4814
4888
  runAction(state, (client, opts) => {
4815
4889
  const nodeType = opts.type;
@@ -4823,6 +4897,7 @@ Examples:
4823
4897
  return client.nodes.createChangeRequest({
4824
4898
  message: opts.message ?? `Create ${nodeType} ${name}`,
4825
4899
  submittedBy: opts.submittedBy,
4900
+ ...opts.requireReview ? { autoMerge: false } : opts.autoMerge ? { autoMerge: true } : {},
4826
4901
  operations: [
4827
4902
  {
4828
4903
  kind: "create",
@@ -4902,14 +4977,18 @@ Examples:
4902
4977
  );
4903
4978
  addGlobalFlags(bases.command("create")).description("Create a Base \u2014 review-first by default: returns a pending Change Request").requiredOption("--slug <slug>", "Base slug").requiredOption("--name <name>", "Base name").option("--field <slug:name:type...>", "field definition, repeatable").option("--fields-json <json|@file>", "field definitions as JSON array").option("--description <text>", "optional description").option("--parent-node-id <id>", "parent folder node id; omit for root").option(
4904
4979
  "--auto-merge",
4905
- "skip review and create the Base immediately (default: propose a pending Change Request)"
4980
+ "skip review and create the Base immediately if you have write access (default: merge immediately if you have write access, otherwise propose a pending Change Request)"
4981
+ ).option(
4982
+ "--require-review",
4983
+ "always propose a pending Change Request, even if you have write access"
4906
4984
  ).addHelpText(
4907
4985
  "after",
4908
4986
  `
4909
4987
  Examples:
4910
4988
  busabase-cli bases create --slug products --name "\u4EA7\u54C1\u76EE\u5F55 Products" --field product_name:"Product Name":text
4911
4989
  busabase-cli bases create --slug products --name "\u4EA7\u54C1\u76EE\u5F55 Products" --fields-json @fields.json
4912
- busabase-cli bases create --slug products --name "Products" --auto-merge # skip review, create immediately`
4990
+ busabase-cli bases create --slug products --name "Products" --auto-merge # skip review, create immediately
4991
+ busabase-cli bases create --slug products --name "Products" --require-review # always propose a Change Request`
4913
4992
  ).action(
4914
4993
  runAction(
4915
4994
  state,
@@ -4919,7 +4998,7 @@ Examples:
4919
4998
  description: opts.description,
4920
4999
  parentNodeId: opts.parentNodeId,
4921
5000
  fields: parseFieldDefinitions(opts),
4922
- ...opts.autoMerge ? { autoMerge: true } : {}
5001
+ ...opts.requireReview ? { autoMerge: false } : opts.autoMerge ? { autoMerge: true } : {}
4923
5002
  })
4924
5003
  )
4925
5004
  );
@@ -4976,14 +5055,18 @@ Examples:
4976
5055
  );
4977
5056
  addGlobalFlags(bases.command("create-change-request")).description("Propose a new record via a Change Request \u2014 review-first by default").requiredOption("--base-id <id>", "target Base id").requiredOption("--fields-json <json|@file>", "record fields as JSON, or @file.json").option("--message <text>", "reviewer-facing Change Request message").option("--submitted-by <name>", "producer label").option(
4978
5057
  "--auto-merge",
4979
- "skip review and create the record immediately (default: propose a pending Change Request)"
5058
+ "skip review and create the record immediately if you have write access (default: merge immediately if you have write access, otherwise propose a pending Change Request)"
5059
+ ).option(
5060
+ "--require-review",
5061
+ "always propose a pending Change Request, even if you have write access"
4980
5062
  ).addHelpText(
4981
5063
  "after",
4982
5064
  `
4983
5065
  Examples:
4984
5066
  busabase-cli bases create-change-request --base-id bse_123 --fields-json '{"title":"Hello","status":"draft"}'
4985
5067
  busabase-cli bases create-change-request --base-id bse_123 --fields-json @record.json
4986
- busabase-cli bases create-change-request --base-id bse_123 --fields-json '{"title":"Hello"}' --auto-merge # skip review, create immediately`
5068
+ busabase-cli bases create-change-request --base-id bse_123 --fields-json '{"title":"Hello"}' --auto-merge # skip review, create immediately
5069
+ busabase-cli bases create-change-request --base-id bse_123 --fields-json '{"title":"Hello"}' --require-review # always propose a Change Request`
4987
5070
  ).action(
4988
5071
  runAction(
4989
5072
  state,
@@ -4992,7 +5075,7 @@ Examples:
4992
5075
  fields: parseJsonValue(opts.fieldsJson, "fields-json"),
4993
5076
  message: opts.message,
4994
5077
  submittedBy: opts.submittedBy,
4995
- ...opts.autoMerge ? { autoMerge: true } : {}
5078
+ ...opts.requireReview ? { autoMerge: false } : opts.autoMerge ? { autoMerge: true } : {}
4996
5079
  })
4997
5080
  )
4998
5081
  );
@@ -5016,7 +5099,10 @@ Examples:
5016
5099
  ).choices(["merge", "replace"])
5017
5100
  ).option(
5018
5101
  "--auto-merge",
5019
- "skip review and create the AirApp immediately (default: propose a pending Change Request)"
5102
+ "skip review and create the AirApp immediately if you have write access (default: merge immediately if you have write access, otherwise propose a pending Change Request)"
5103
+ ).option(
5104
+ "--require-review",
5105
+ "always propose a pending Change Request, even if you have write access"
5020
5106
  ).addHelpText(
5021
5107
  "after",
5022
5108
  `
@@ -5033,7 +5119,8 @@ mergeMode explains how --files-json combines with the AirApp's default scaffold:
5033
5119
  Examples:
5034
5120
  busabase-cli airapps create --slug hello-app --name "Hello App" --files-json @files.json
5035
5121
  busabase-cli airapps create --slug vite-app --name "Vite App" --files-json @files.json --merge-mode replace
5036
- busabase-cli airapps create --slug hello-app --name "Hello App" --files-json @files.json --auto-merge # skip review, create immediately`
5122
+ busabase-cli airapps create --slug hello-app --name "Hello App" --files-json @files.json --auto-merge # skip review, create immediately
5123
+ busabase-cli airapps create --slug hello-app --name "Hello App" --files-json @files.json --require-review # always propose a Change Request`
5037
5124
  ).action(
5038
5125
  runAction(
5039
5126
  state,
@@ -5046,7 +5133,7 @@ Examples:
5046
5133
  version: opts.version,
5047
5134
  files: opts.filesJson ? parseJsonValue(opts.filesJson, "files-json") : void 0,
5048
5135
  mergeMode: opts.mergeMode,
5049
- ...opts.autoMerge ? { autoMerge: true } : {}
5136
+ ...opts.requireReview ? { autoMerge: false } : opts.autoMerge ? { autoMerge: true } : {}
5050
5137
  })
5051
5138
  )
5052
5139
  );
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-LRUXSAKS.js";
4
+ } from "./chunk-RGZM6OXX.js";
5
5
 
6
6
  // src/cli.ts
7
7
  process.exit(await runCli(process.argv.slice(2)));
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  HELP,
3
3
  render,
4
4
  runCli
5
- } from "./chunk-LRUXSAKS.js";
5
+ } from "./chunk-RGZM6OXX.js";
6
6
 
7
7
  // src/index.ts
8
8
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "busa-cli",
3
- "version": "0.9.13",
3
+ "version": "0.9.15",
4
4
  "description": "Command-line client for the Busabase OpenAPI REST API. Talks to a local or remote `busabase server`. Short-name alias for busabase-cli.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/busabase/busabase/tree/main/apps/busabase-cli",
@@ -33,7 +33,7 @@
33
33
  "dependencies": {
34
34
  "@zip.js/zip.js": "^2.8.20",
35
35
  "busabase-package": "workspace:*",
36
- "busabase-sdk": "0.9.13",
36
+ "busabase-sdk": "0.9.15",
37
37
  "commander": "^14.0.0",
38
38
  "tar-stream": "^3.1.7",
39
39
  "yaml": "^2.9.0",