busa-cli 0.9.8 → 0.9.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.
@@ -120,7 +120,7 @@ function nodeIcon(type) {
120
120
  }
121
121
 
122
122
  // src/run.ts
123
- import { createHash as createHash3 } from "crypto";
123
+ import { createHash as createHash6 } from "crypto";
124
124
  import { readFileSync as readFileSync2 } from "fs";
125
125
  import { basename, extname as extname2 } from "path";
126
126
  import {
@@ -137,6 +137,566 @@ import {
137
137
  Option
138
138
  } from "commander";
139
139
 
140
+ // src/backup/export/full-exporter.ts
141
+ import { createHash as createHash2 } from "crypto";
142
+
143
+ // ../../packages/busabase-contract/src/domains/dump/types.ts
144
+ import { z } from "zod";
145
+ var DumpTableSchema = z.enum([
146
+ "nodes",
147
+ /** Node-level access grants (permissions). Not secret — restore must keep them
148
+ * so a backed-up space's sharing/permission config survives a full restore. */
149
+ "nodePrincipals",
150
+ "bases",
151
+ "baseFields",
152
+ "views",
153
+ "records",
154
+ "fieldValues",
155
+ "recordLinks",
156
+ /** The physical bytes registry (open-domains/attachments) an Asset's `attachmentId` FKs into. */
157
+ "attachments",
158
+ "assets",
159
+ "assetUsages",
160
+ "assetTexts",
161
+ "commits",
162
+ "changeRequests",
163
+ "operations",
164
+ "comments",
165
+ "reviews",
166
+ "auditEvents"
167
+ ]);
168
+ var ExportTablesInputSchema = z.object({
169
+ table: DumpTableSchema,
170
+ /** Opaque pagination cursor from a previous page's `nextCursor`; omit for the first page. */
171
+ cursor: z.string().optional(),
172
+ /** Page size, default 500, capped at 2000. */
173
+ limit: z.number().int().positive().max(2e3).optional().default(500)
174
+ });
175
+ var ExportTablesVOSchema = z.object({
176
+ rows: z.array(z.record(z.string(), z.unknown())),
177
+ nextCursor: z.string().nullable()
178
+ });
179
+ var ExportAssetTextInputSchema = z.object({ assetId: z.string() });
180
+ var ExportAssetTextVOSchema = z.object({
181
+ assetId: z.string(),
182
+ /** The row's `text_storage_key` — the exact key a restore must write back to. */
183
+ textStorageKey: z.string(),
184
+ /**
185
+ * `null` when this row owns no separate text object and therefore needs no
186
+ * blob in the archive:
187
+ * - auto-registered text-kind rows (`writtenBy: "auto"`), whose
188
+ * `textStorageKey` IS the owning attachment's own key — those bytes are
189
+ * already archived by the attachment-blob pass, and re-archiving them
190
+ * would duplicate bytes and collide on restore;
191
+ * - `status: "none"` rows (no extractable text), whose key is `""`.
192
+ */
193
+ downloadUrl: z.string().nullable(),
194
+ /** `sha256:<hex>` of the text bytes, so a backup can verify what it downloaded. */
195
+ textContentHash: z.string().nullable(),
196
+ byteCount: z.number().int().nonnegative()
197
+ });
198
+ var ImportBeginVOSchema = z.object({
199
+ sessionId: z.string()
200
+ });
201
+ var ImportTablesInputSchema = z.object({
202
+ sessionId: z.string(),
203
+ table: z.union([
204
+ DumpTableSchema,
205
+ z.literal("docBodies"),
206
+ z.literal("attachmentBlobs"),
207
+ z.literal("assetTextBlobs")
208
+ ]),
209
+ rows: z.array(z.record(z.string(), z.unknown()))
210
+ });
211
+ var ImportTablesVOSchema = z.object({
212
+ inserted: z.number().int().nonnegative()
213
+ });
214
+ var ImportSessionInputSchema = z.object({
215
+ sessionId: z.string()
216
+ });
217
+ var ImportCommitVOSchema = z.object({
218
+ ok: z.boolean(),
219
+ warnings: z.array(z.string())
220
+ });
221
+ var ImportAbortVOSchema = z.object({
222
+ ok: z.boolean()
223
+ });
224
+
225
+ // src/backup/format/archive-writer.ts
226
+ import { createHash } from "crypto";
227
+ import { createWriteStream } from "fs";
228
+ import { PassThrough } from "stream";
229
+ import { pipeline } from "stream/promises";
230
+ import { ZstdCompress } from "zlib";
231
+ import * as tar from "tar-stream";
232
+
233
+ // src/backup/format/manifest.ts
234
+ import { z as z2 } from "zod";
235
+ var FORMAT_VERSION = 1;
236
+ var ManifestSchema = z2.object({
237
+ formatVersion: z2.literal(FORMAT_VERSION),
238
+ toolVersion: z2.string(),
239
+ exportedAt: z2.string(),
240
+ spaceId: z2.string(),
241
+ sourceHost: z2.string(),
242
+ fidelity: z2.enum(["full", "state"]),
243
+ /** Vault items and webhook signing secrets are never written to the archive. */
244
+ excludesSecrets: z2.literal(true),
245
+ tables: z2.record(z2.string(), z2.number().int().nonnegative()),
246
+ blobCount: z2.number().int().nonnegative(),
247
+ blobBytes: z2.number().int().nonnegative(),
248
+ /**
249
+ * Extracted-text ("Drive Grep Retrieval") blobs, counted separately from the
250
+ * attachment blobs above because they are a different object class — derived
251
+ * text, content-addressed under `asset-texts/blobs/sha256/…`, one per
252
+ * `busabase_asset_texts` row that owns its own object.
253
+ *
254
+ * `.default(0)` rather than required: archives written before asset-text
255
+ * blobs were captured at all carry no such field, and those are exactly the
256
+ * archives an operator most needs to be able to still open — the import's
257
+ * integrity pass is what tells them the text is missing, which it can only
258
+ * do if the manifest parses in the first place.
259
+ */
260
+ textBlobCount: z2.number().int().nonnegative().optional().default(0),
261
+ textBlobBytes: z2.number().int().nonnegative().optional().default(0),
262
+ /** sha256 hex digest — see the doc comment above for exactly what it covers. */
263
+ checksum: z2.string()
264
+ });
265
+ function canonicalizeEntryChecksums(entries) {
266
+ return entries.slice().sort((a, b) => a.path.localeCompare(b.path)).map((e) => `${e.path}:${e.sha256}`).join("\n");
267
+ }
268
+
269
+ // src/backup/format/archive-writer.ts
270
+ var ArchiveWriter = class _ArchiveWriter {
271
+ pack = tar.pack();
272
+ entryChecksums = [];
273
+ donePromise;
274
+ constructor(outPath) {
275
+ const zstd = new ZstdCompress();
276
+ this.donePromise = pipeline(this.pack, zstd, createWriteStream(outPath));
277
+ }
278
+ static create(outPath) {
279
+ return new _ArchiveWriter(outPath);
280
+ }
281
+ /** Add one archive entry from an in-memory buffer/string (small entries: manifest, base.json, etc). */
282
+ async addBuffer(path, data) {
283
+ const buf = typeof data === "string" ? Buffer.from(data, "utf8") : data;
284
+ const sha256 = createHash("sha256").update(buf).digest("hex");
285
+ this.entryChecksums.push({ path, sha256 });
286
+ await new Promise((resolvePromise, reject) => {
287
+ this.pack.entry(
288
+ { name: path, size: buf.length },
289
+ buf,
290
+ (err) => err ? reject(err) : resolvePromise()
291
+ );
292
+ });
293
+ }
294
+ /** Add one archive entry streamed from a Readable (large entries: records.ndjson, blobs). */
295
+ async addStream(path, size, source) {
296
+ const hash = createHash("sha256");
297
+ const entry = this.pack.entry({ name: path, size });
298
+ const tee = new PassThrough();
299
+ tee.on("data", (chunk) => hash.update(chunk));
300
+ await pipeline(source, tee, entry);
301
+ this.entryChecksums.push({ path, sha256: hash.digest("hex") });
302
+ }
303
+ /**
304
+ * Write `manifest.json` (with the top-level checksum over every other
305
+ * entry) and close the archive. Must be called exactly once, after every
306
+ * other entry has been added.
307
+ */
308
+ async finalize(manifestWithoutChecksum) {
309
+ const checksum = createHash("sha256").update(canonicalizeEntryChecksums(this.entryChecksums)).digest("hex");
310
+ const manifest = { ...manifestWithoutChecksum, checksum };
311
+ const buf = Buffer.from(JSON.stringify(manifest, null, 2), "utf8");
312
+ await new Promise((resolvePromise, reject) => {
313
+ this.pack.entry(
314
+ { name: "manifest.json", size: buf.length },
315
+ buf,
316
+ (err) => err ? reject(err) : resolvePromise()
317
+ );
318
+ });
319
+ this.pack.finalize();
320
+ await this.donePromise;
321
+ return manifest;
322
+ }
323
+ };
324
+
325
+ // src/backup/export/full-exporter.ts
326
+ var EXPORT_TABLES = [...DumpTableSchema.options];
327
+ var HISTORY_TABLES = [
328
+ "commits",
329
+ "changeRequests",
330
+ "operations",
331
+ "comments",
332
+ "reviews",
333
+ "auditEvents"
334
+ ];
335
+ async function exportFull(options) {
336
+ const { client, outPath, spaceId, sourceHost, includeHistory, toolVersion, onProgress } = options;
337
+ const pageLimit = options.pageLimit ?? 500;
338
+ const log = onProgress ?? (() => {
339
+ });
340
+ const writer = ArchiveWriter.create(outPath);
341
+ const tableCounts = {};
342
+ const tableRows = {};
343
+ const tables = includeHistory ? EXPORT_TABLES : EXPORT_TABLES.filter((t) => !HISTORY_TABLES.includes(t));
344
+ for (const table of tables) {
345
+ const rows = [];
346
+ let cursor;
347
+ do {
348
+ const page = await client.dump.exportTables({ table, cursor, limit: pageLimit });
349
+ rows.push(...page.rows);
350
+ cursor = page.nextCursor ?? void 0;
351
+ } while (cursor);
352
+ tableRows[table] = rows;
353
+ tableCounts[table] = rows.length;
354
+ const ndjson = `${rows.map((row) => JSON.stringify(row)).join("\n")}${rows.length ? "\n" : ""}`;
355
+ await writer.addBuffer(`tree/${table}.ndjson`, ndjson);
356
+ log(`exported ${table}: ${rows.length} rows`);
357
+ }
358
+ const docNodes = (tableRows.nodes ?? []).filter((n) => n.type === "doc");
359
+ let docBodyCount = 0;
360
+ for (const node of docNodes) {
361
+ const nodeId = node.id;
362
+ const doc = await client.docs.get({ nodeId });
363
+ await writer.addBuffer(`docs/${nodeId}.md`, doc.body ?? "");
364
+ docBodyCount += 1;
365
+ }
366
+ log(`exported ${docBodyCount} doc bodies`);
367
+ const attachmentsById = new Map((tableRows.attachments ?? []).map((a) => [a.id, a]));
368
+ const attachmentBlobRows = [];
369
+ let blobCount = 0;
370
+ let blobBytes = 0;
371
+ const skippedAssets = [];
372
+ for (const asset of tableRows.assets ?? []) {
373
+ const assetId = asset.id;
374
+ const attachment = attachmentsById.get(asset.attachmentId);
375
+ try {
376
+ if (!attachment) throw new Error("no matching attachments row (orphaned asset)");
377
+ const download = await client.assets.download({ assetId });
378
+ const resolvedUrl = new URL(download.downloadUrl, sourceHost).toString();
379
+ const res = await fetch(resolvedUrl);
380
+ if (!res.ok || !res.body) {
381
+ throw new Error(`HTTP ${res.status}`);
382
+ }
383
+ const buf = Buffer.from(await res.arrayBuffer());
384
+ attachmentBlobRows.push({
385
+ storageKey: attachment.storageKey,
386
+ mimeType: download.mimeType,
387
+ base64: buf.toString("base64")
388
+ });
389
+ blobCount += 1;
390
+ blobBytes += buf.length;
391
+ } catch (error) {
392
+ const message = error instanceof Error ? error.message : String(error);
393
+ skippedAssets.push(assetId);
394
+ log(` WARNING: could not download asset ${assetId}, skipping its blob (${message})`);
395
+ }
396
+ }
397
+ const blobNdjson = `${attachmentBlobRows.map((row) => JSON.stringify(row)).join("\n")}${attachmentBlobRows.length ? "\n" : ""}`;
398
+ await writer.addBuffer("tree/attachmentBlobs.ndjson", blobNdjson);
399
+ log(
400
+ `exported ${blobCount} attachment blobs (${blobBytes} bytes), skipped ${skippedAssets.length}`
401
+ );
402
+ const textBlobRows = [];
403
+ const seenTextKeys = /* @__PURE__ */ new Set();
404
+ let textBlobCount = 0;
405
+ let textBlobBytes = 0;
406
+ const skippedAssetTexts = [];
407
+ for (const assetText of tableRows.assetTexts ?? []) {
408
+ const assetId = assetText.assetId;
409
+ try {
410
+ const info = await client.dump.exportAssetText({ assetId });
411
+ if (!info.downloadUrl) continue;
412
+ if (seenTextKeys.has(info.textStorageKey)) continue;
413
+ const resolvedUrl = new URL(info.downloadUrl, sourceHost).toString();
414
+ const res = await fetch(resolvedUrl);
415
+ if (!res.ok || !res.body) {
416
+ throw new Error(`HTTP ${res.status}`);
417
+ }
418
+ const buf = Buffer.from(await res.arrayBuffer());
419
+ if (info.textContentHash) {
420
+ const actual = `sha256:${createHash2("sha256").update(buf).digest("hex")}`;
421
+ if (actual !== info.textContentHash) {
422
+ throw new Error(
423
+ `content hash mismatch (expected ${info.textContentHash}, downloaded ${actual})`
424
+ );
425
+ }
426
+ }
427
+ seenTextKeys.add(info.textStorageKey);
428
+ textBlobRows.push({ textStorageKey: info.textStorageKey, base64: buf.toString("base64") });
429
+ textBlobCount += 1;
430
+ textBlobBytes += buf.length;
431
+ } catch (error) {
432
+ const message = error instanceof Error ? error.message : String(error);
433
+ skippedAssetTexts.push(assetId);
434
+ log(
435
+ ` WARNING: could not download text for asset ${assetId}, skipping its blob (${message})`
436
+ );
437
+ }
438
+ }
439
+ const textBlobNdjson = `${textBlobRows.map((row) => JSON.stringify(row)).join("\n")}${textBlobRows.length ? "\n" : ""}`;
440
+ await writer.addBuffer("tree/assetTextBlobs.ndjson", textBlobNdjson);
441
+ log(
442
+ `exported ${textBlobCount} asset-text blobs (${textBlobBytes} bytes), skipped ${skippedAssetTexts.length}`
443
+ );
444
+ const manifestWithoutChecksum = {
445
+ formatVersion: FORMAT_VERSION,
446
+ toolVersion,
447
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
448
+ spaceId,
449
+ sourceHost,
450
+ fidelity: "full",
451
+ excludesSecrets: true,
452
+ tables: tableCounts,
453
+ blobCount,
454
+ blobBytes,
455
+ textBlobCount,
456
+ textBlobBytes
457
+ };
458
+ return writer.finalize(manifestWithoutChecksum);
459
+ }
460
+
461
+ // src/backup/format/archive-reader.ts
462
+ import { createHash as createHash3 } from "crypto";
463
+ import { createReadStream } from "fs";
464
+ import { ZstdDecompress } from "zlib";
465
+ import * as tar2 from "tar-stream";
466
+ var ZSTD_MAGIC = Buffer.from([40, 181, 47, 253]);
467
+ var GZIP_MAGIC = Buffer.from([31, 139]);
468
+ async function detectCompression(path) {
469
+ return new Promise((resolvePromise, reject) => {
470
+ const stream = createReadStream(path, { start: 0, end: 3 });
471
+ const chunks = [];
472
+ stream.on("data", (c2) => chunks.push(c2));
473
+ stream.on("error", reject);
474
+ stream.on("end", () => {
475
+ const head = Buffer.concat(chunks);
476
+ if (head.subarray(0, 4).equals(ZSTD_MAGIC)) resolvePromise("zstd");
477
+ else if (head.subarray(0, 2).equals(GZIP_MAGIC)) resolvePromise("gzip");
478
+ else reject(new Error("Unrecognized .bbdump compression (not zstd or gzip magic bytes)."));
479
+ });
480
+ });
481
+ }
482
+ async function readArchive(path) {
483
+ const compression = await detectCompression(path);
484
+ if (compression === "gzip") {
485
+ throw new Error(
486
+ "This .bbdump archive is gzip-compressed; `busabase-cli restore` only reads zstd archives in v1."
487
+ );
488
+ }
489
+ const extract2 = tar2.extract();
490
+ const entries = /* @__PURE__ */ new Map();
491
+ const checksums = [];
492
+ let manifestBuf;
493
+ await new Promise((resolvePromise, reject) => {
494
+ extract2.on("entry", (header, stream, next) => {
495
+ const chunks = [];
496
+ stream.on("data", (c2) => chunks.push(c2));
497
+ stream.on("end", () => {
498
+ const buf = Buffer.concat(chunks);
499
+ if (header.name === "manifest.json") {
500
+ manifestBuf = buf;
501
+ } else {
502
+ const sha256 = createHash3("sha256").update(buf).digest("hex");
503
+ checksums.push({ path: header.name, sha256 });
504
+ entries.set(header.name, buf);
505
+ }
506
+ next();
507
+ });
508
+ stream.on("error", reject);
509
+ stream.resume();
510
+ });
511
+ extract2.on("finish", resolvePromise);
512
+ extract2.on("error", reject);
513
+ createReadStream(path).pipe(new ZstdDecompress()).on("error", reject).pipe(extract2);
514
+ });
515
+ if (!manifestBuf) {
516
+ throw new Error("Archive is missing manifest.json.");
517
+ }
518
+ const manifest = ManifestSchema.parse(JSON.parse(manifestBuf.toString("utf8")));
519
+ const recomputed = createHash3("sha256").update(canonicalizeEntryChecksums(checksums)).digest("hex");
520
+ if (recomputed !== manifest.checksum) {
521
+ throw new Error(
522
+ `Archive checksum mismatch \u2014 file may be truncated or corrupted (expected ${manifest.checksum}, got ${recomputed}).`
523
+ );
524
+ }
525
+ return { manifest, entries };
526
+ }
527
+
528
+ // src/backup/import/full-importer.ts
529
+ var IMPORT_ORDER = [
530
+ "nodes",
531
+ // `nodeId` / `sourceNodeId` FK into busabase_nodes — must follow "nodes".
532
+ "nodePrincipals",
533
+ "bases",
534
+ "baseFields",
535
+ "views",
536
+ "commits",
537
+ "changeRequests",
538
+ "operations",
539
+ "records",
540
+ "fieldValues",
541
+ "recordLinks",
542
+ "attachments",
543
+ "assets",
544
+ "assetUsages",
545
+ "assetTexts",
546
+ "comments",
547
+ "reviews",
548
+ "auditEvents"
549
+ ];
550
+ var BATCH_SIZE = 200;
551
+ async function importFull(options) {
552
+ const { client, archive, onProgress } = options;
553
+ const log = onProgress ?? (() => {
554
+ });
555
+ const { sessionId } = await client.dump.importBegin();
556
+ try {
557
+ const blobBuf = archive.entries.get("tree/attachmentBlobs.ndjson");
558
+ if (blobBuf) {
559
+ const blobRows = blobBuf.toString("utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
560
+ for (let i = 0; i < blobRows.length; i += BATCH_SIZE) {
561
+ const batch = blobRows.slice(i, i + BATCH_SIZE);
562
+ await client.dump.importTables({ sessionId, table: "attachmentBlobs", rows: batch });
563
+ }
564
+ log(`imported ${blobRows.length} attachment blobs`);
565
+ }
566
+ const textBlobBuf = archive.entries.get("tree/assetTextBlobs.ndjson");
567
+ if (textBlobBuf) {
568
+ const textBlobRows = textBlobBuf.toString("utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
569
+ for (let i = 0; i < textBlobRows.length; i += BATCH_SIZE) {
570
+ const batch = textBlobRows.slice(i, i + BATCH_SIZE);
571
+ await client.dump.importTables({ sessionId, table: "assetTextBlobs", rows: batch });
572
+ }
573
+ log(`imported ${textBlobRows.length} asset-text blobs`);
574
+ }
575
+ for (const table of IMPORT_ORDER) {
576
+ const buf = archive.entries.get(`tree/${table}.ndjson`);
577
+ if (!buf) continue;
578
+ const rows = buf.toString("utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
579
+ for (let i = 0; i < rows.length; i += BATCH_SIZE) {
580
+ const batch = rows.slice(i, i + BATCH_SIZE);
581
+ await client.dump.importTables({ sessionId, table, rows: batch });
582
+ }
583
+ log(`imported ${table}: ${rows.length} rows`);
584
+ }
585
+ const docBodyRows = [...archive.entries.entries()].filter(([path]) => path.startsWith("docs/") && path.endsWith(".md")).map(([path, buf]) => ({
586
+ nodeId: path.slice("docs/".length, -".md".length),
587
+ markdown: buf.toString("utf8")
588
+ }));
589
+ if (docBodyRows.length > 0) {
590
+ await client.dump.importTables({ sessionId, table: "docBodies", rows: docBodyRows });
591
+ log(`imported ${docBodyRows.length} doc bodies`);
592
+ }
593
+ const result = await client.dump.importCommit({ sessionId });
594
+ return result;
595
+ } catch (error) {
596
+ await client.dump.importAbort({ sessionId }).catch(() => void 0);
597
+ throw error;
598
+ }
599
+ }
600
+
601
+ // src/backup/commands.ts
602
+ var reportProgress = (message) => {
603
+ console.error(message);
604
+ };
605
+ var STATE_ONLY_BACKUP_MESSAGE = [
606
+ "--state-only backup is not implemented, and will not be \u2014 it is superseded by",
607
+ "`busabase-cli publish`, which writes the same state-level content as a readable,",
608
+ "diffable directory you can push to GitHub:",
609
+ "",
610
+ " busabase-cli publish <node-slug> -o ./my-package",
611
+ " busabase-cli install https://github.com/<you>/<repo>",
612
+ "",
613
+ "`busabase-cli backup` stays the full-fidelity backup path (raw rows, original ids,",
614
+ "history) \u2014 run it without --state-only. The two formats are deliberately separate:",
615
+ "a backup carries the audit events, comments, reviews and ACLs that a published",
616
+ "package must never leak."
617
+ ].join("\n");
618
+ var STATE_ONLY_RESTORE_MESSAGE = [
619
+ "state-only archive restore is not implemented, and will not be \u2014 it is superseded",
620
+ "by `busabase-cli install`, which installs state-level content into an EXISTING",
621
+ "space as change requests you review before they go live:",
622
+ "",
623
+ " busabase-cli install https://github.com/<you>/<repo> --into-folder <name>",
624
+ "",
625
+ "`busabase-cli restore` stays the full-fidelity restore path (original ids and",
626
+ "history, into an empty space)."
627
+ ].join("\n");
628
+ var runBackup = async (client, options) => {
629
+ if (options.stateOnly) throw new Error(STATE_ONLY_BACKUP_MESSAGE);
630
+ const { spaceId } = options;
631
+ if (!spaceId) {
632
+ throw new Error(
633
+ "No space configured. Pass --space-id <id>, export BUSABASE_SPACE_ID, or run `busabase-cli login`."
634
+ );
635
+ }
636
+ reportProgress(
637
+ `Backing up space ${spaceId} \u2192 ${options.outFile} (full fidelity, history=${options.includeHistory})`
638
+ );
639
+ const manifest = await exportFull({
640
+ client,
641
+ outPath: options.outFile,
642
+ spaceId,
643
+ sourceHost: options.baseUrl,
644
+ includeHistory: options.includeHistory,
645
+ toolVersion: options.toolVersion,
646
+ onProgress: (message) => reportProgress(` ${message}`)
647
+ });
648
+ const rows = Object.values(manifest.tables).reduce((total, count) => total + count, 0);
649
+ if (options.json) {
650
+ return {
651
+ file: options.outFile,
652
+ rows,
653
+ blobCount: manifest.blobCount,
654
+ blobBytes: manifest.blobBytes,
655
+ manifest
656
+ };
657
+ }
658
+ return [
659
+ `Done. ${rows} rows, ${manifest.blobCount} blobs (${manifest.blobBytes} bytes) \u2192 ${options.outFile}`,
660
+ "",
661
+ "Restore it into an empty space with:",
662
+ ` busabase-cli restore ${options.outFile}`
663
+ ].join("\n");
664
+ };
665
+ var runRestore = async (client, options) => {
666
+ if (options.resume) {
667
+ throw new Error("--resume is not implemented yet in this version.");
668
+ }
669
+ reportProgress(`Reading ${options.file} \u2026`);
670
+ const archive = await readArchive(options.file);
671
+ reportProgress(
672
+ `Archive: fidelity=${archive.manifest.fidelity} space=${archive.manifest.spaceId} exportedAt=${archive.manifest.exportedAt}`
673
+ );
674
+ if (archive.manifest.fidelity !== "full") throw new Error(STATE_ONLY_RESTORE_MESSAGE);
675
+ if (options.intoFolder) {
676
+ throw new Error("--into-folder only applies to state-only archives.");
677
+ }
678
+ const result = await importFull({
679
+ client,
680
+ archive,
681
+ onProgress: (message) => reportProgress(` ${message}`)
682
+ });
683
+ if (options.json) {
684
+ return {
685
+ restored: result.ok,
686
+ file: options.file,
687
+ spaceId: archive.manifest.spaceId,
688
+ warnings: result.warnings
689
+ };
690
+ }
691
+ if (!result.ok || result.warnings.length > 0) {
692
+ return [
693
+ "Restore completed with warnings:",
694
+ ...result.warnings.map((warning) => ` \u2022 ${warning}`)
695
+ ].join("\n");
696
+ }
697
+ return "Restore completed successfully.";
698
+ };
699
+
140
700
  // src/banner.ts
141
701
  var useColor = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
142
702
  var paint = (code, s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
@@ -206,7 +766,7 @@ function writeDotEnvFile(updates) {
206
766
 
207
767
  // src/login.ts
208
768
  import { spawn } from "child_process";
209
- import { createHash, randomBytes } from "crypto";
769
+ import { createHash as createHash4, randomBytes } from "crypto";
210
770
  import { createServer } from "http";
211
771
  import { createInterface } from "readline/promises";
212
772
  import { DEFAULT_BASE_URL, normalizeBaseUrl } from "busabase-sdk";
@@ -274,7 +834,7 @@ async function verifyAuth(baseUrl, token) {
274
834
  }
275
835
  async function oauthLogin(baseUrl, useBrowser) {
276
836
  const codeVerifier = base64url(randomBytes(32));
277
- const codeChallenge = base64url(createHash("sha256").update(codeVerifier).digest());
837
+ const codeChallenge = base64url(createHash4("sha256").update(codeVerifier).digest());
278
838
  const state = base64url(randomBytes(16));
279
839
  const { code, redirectUri } = await new Promise(
280
840
  (resolve, reject) => {
@@ -585,34 +1145,34 @@ import { readFile } from "fs/promises";
585
1145
  import { join as join3 } from "path";
586
1146
 
587
1147
  // ../../packages/busabase-contract/src/domains/package/types.ts
588
- import { z as z6 } from "zod";
1148
+ import { z as z8 } from "zod";
589
1149
 
590
1150
  // ../../packages/openlib/i18n/i-string.ts
591
- import { z as z2 } from "zod";
1151
+ import { z as z4 } from "zod";
592
1152
 
593
1153
  // ../../packages/openlib/i18n/i18n.ts
594
- import { z } from "zod";
1154
+ import { z as z3 } from "zod";
595
1155
  var i18n = {
596
1156
  defaultLocale: "en",
597
1157
  locales: ["en", "zh-CN", "zh-TW", "ja", "ko", "de", "fr", "es", "pt"],
598
1158
  extendLocales: ["en", "zh-CN", "zh-TW", "ja", "ko", "fr", "de", "es", "ru", "it", "vi", "pt"]
599
1159
  };
600
- var LocaleSchema = z.enum(i18n.locales);
1160
+ var LocaleSchema = z3.enum(i18n.locales);
601
1161
 
602
1162
  // ../../packages/openlib/i18n/i-string.ts
603
- var iStringRecordSchema = z2.partialRecord(LocaleSchema, z2.string());
604
- var iStringSchema = z2.union([z2.string(), iStringRecordSchema]).describe("i18n string");
1163
+ var iStringRecordSchema = z4.partialRecord(LocaleSchema, z4.string());
1164
+ var iStringSchema = z4.union([z4.string(), iStringRecordSchema]).describe("i18n string");
605
1165
 
606
1166
  // ../../packages/busabase-contract/src/domains/base/contract/base-schemas.ts
607
- import { z as z3 } from "zod";
608
- var fieldNameSchema = z3.union([
609
- z3.string().min(1),
1167
+ import { z as z5 } from "zod";
1168
+ var fieldNameSchema = z5.union([
1169
+ z5.string().min(1),
610
1170
  iStringRecordSchema.refine(
611
1171
  (record) => Object.values(record).some((value) => value && value.trim().length > 0),
612
1172
  "field name must have at least one non-empty locale value"
613
1173
  )
614
1174
  ]);
615
- var fieldTypeSchema = z3.enum([
1175
+ var fieldTypeSchema = z5.enum([
616
1176
  "text",
617
1177
  "longtext",
618
1178
  "markdown",
@@ -639,85 +1199,85 @@ var fieldTypeSchema = z3.enum([
639
1199
  "json",
640
1200
  "yaml"
641
1201
  ]);
642
- var fieldOptionsSchema = z3.object({
643
- ai: z3.object({
644
- model: z3.string().optional(),
645
- prompt: z3.string().optional(),
646
- reviewRequired: z3.boolean().optional(),
647
- sourceFieldIds: z3.array(z3.string()).optional()
1202
+ var fieldOptionsSchema = z5.object({
1203
+ ai: z5.object({
1204
+ model: z5.string().optional(),
1205
+ prompt: z5.string().optional(),
1206
+ reviewRequired: z5.boolean().optional(),
1207
+ sourceFieldIds: z5.array(z5.string()).optional()
648
1208
  }).optional(),
649
1209
  // Per-field config for `attachment` columns (all optional; logic enforces a
650
1210
  // 25MB ceiling regardless).
651
- attachment: z3.object({
652
- maxFiles: z3.number().int().positive().optional(),
653
- allowedMimeTypes: z3.array(z3.string()).optional(),
654
- maxFileSize: z3.number().int().positive().optional()
1211
+ attachment: z5.object({
1212
+ maxFiles: z5.number().int().positive().optional(),
1213
+ allowedMimeTypes: z5.array(z5.string()).optional(),
1214
+ maxFileSize: z5.number().int().positive().optional()
655
1215
  }).optional(),
656
- choices: z3.array(
657
- z3.object({
658
- color: z3.string().optional(),
659
- id: z3.string(),
660
- name: z3.string()
1216
+ choices: z5.array(
1217
+ z5.object({
1218
+ color: z5.string().optional(),
1219
+ id: z5.string(),
1220
+ name: z5.string()
661
1221
  })
662
1222
  ).optional(),
663
- code: z3.object({
664
- language: z3.string().optional()
1223
+ code: z5.object({
1224
+ language: z5.string().optional()
665
1225
  }).optional(),
666
- embed: z3.object({
667
- aspectRatio: z3.enum(["16:9", "4:3", "1:1"]).optional(),
668
- height: z3.number().int().positive().max(1200).optional(),
669
- providers: z3.array(z3.string()).optional()
1226
+ embed: z5.object({
1227
+ aspectRatio: z5.enum(["16:9", "4:3", "1:1"]).optional(),
1228
+ height: z5.number().int().positive().max(1200).optional(),
1229
+ providers: z5.array(z5.string()).optional()
670
1230
  }).optional(),
671
- inverseFieldId: z3.string().optional(),
672
- multiple: z3.boolean().optional(),
1231
+ inverseFieldId: z5.string().optional(),
1232
+ multiple: z5.boolean().optional(),
673
1233
  // Display formatting for `number` columns (Notion-style: one number type,
674
1234
  // a format option). `currency` renders via Intl.NumberFormat.
675
- number: z3.object({
676
- format: z3.enum(["plain", "currency"]).optional(),
677
- currency: z3.string().optional(),
678
- locale: z3.string().optional()
1235
+ number: z5.object({
1236
+ format: z5.enum(["plain", "currency"]).optional(),
1237
+ currency: z5.string().optional(),
1238
+ locale: z5.string().optional()
679
1239
  }).optional(),
680
- targetBaseId: z3.string().optional().describe("Relation target Base id (bse_\u2026). Or pass targetBaseSlug to name it by slug."),
681
- targetBaseSlug: z3.string().optional().describe(
1240
+ targetBaseId: z5.string().optional().describe("Relation target Base id (bse_\u2026). Or pass targetBaseSlug to name it by slug."),
1241
+ targetBaseSlug: z5.string().optional().describe(
682
1242
  "Relation target Base by slug \u2014 a convenience alias for targetBaseId, resolved server-side (active bases in the current space). If both are given, targetBaseId wins."
683
1243
  )
684
1244
  }).default({});
685
- var baseFieldSchema = z3.object({
686
- id: z3.string(),
687
- baseId: z3.string(),
688
- slug: z3.string(),
1245
+ var baseFieldSchema = z5.object({
1246
+ id: z5.string(),
1247
+ baseId: z5.string(),
1248
+ slug: z5.string(),
689
1249
  name: fieldNameSchema,
690
1250
  type: fieldTypeSchema,
691
- required: z3.boolean(),
692
- position: z3.number(),
1251
+ required: z5.boolean(),
1252
+ position: z5.number(),
693
1253
  options: fieldOptionsSchema
694
1254
  });
695
- var baseSchema = z3.object({
696
- id: z3.string(),
697
- nodeId: z3.string(),
698
- slug: z3.string(),
699
- name: z3.string(),
700
- description: z3.string(),
701
- reviewPolicy: z3.object({
702
- kind: z3.literal("single"),
703
- requiredApprovals: z3.number()
1255
+ var baseSchema = z5.object({
1256
+ id: z5.string(),
1257
+ nodeId: z5.string(),
1258
+ slug: z5.string(),
1259
+ name: z5.string(),
1260
+ description: z5.string(),
1261
+ reviewPolicy: z5.object({
1262
+ kind: z5.literal("single"),
1263
+ requiredApprovals: z5.number()
704
1264
  }),
705
- createdAt: z3.string(),
706
- fields: z3.array(baseFieldSchema)
1265
+ createdAt: z5.string(),
1266
+ fields: z5.array(baseFieldSchema)
707
1267
  });
708
- var createBaseInputSchema = z3.object({
709
- parentNodeId: z3.string().optional().describe(
1268
+ var createBaseInputSchema = z5.object({
1269
+ parentNodeId: z5.string().optional().describe(
710
1270
  "Parent node id. Must be a folder or the space root; container-incapable node types (Base, Doc, AirApp, etc.) cannot hold children."
711
1271
  ),
712
- slug: z3.string().min(1).regex(/^[a-z0-9-]+$/),
713
- name: z3.string().min(1),
714
- description: z3.string().optional().default(""),
715
- fields: z3.array(
716
- z3.object({
717
- slug: z3.string().min(1),
1272
+ slug: z5.string().min(1).regex(/^[a-z0-9-]+$/),
1273
+ name: z5.string().min(1),
1274
+ description: z5.string().optional().default(""),
1275
+ fields: z5.array(
1276
+ z5.object({
1277
+ slug: z5.string().min(1),
718
1278
  name: fieldNameSchema,
719
1279
  type: fieldTypeSchema.default("text"),
720
- required: z3.boolean().default(false),
1280
+ required: z5.boolean().default(false),
721
1281
  options: fieldOptionsSchema.optional().default({})
722
1282
  })
723
1283
  ).default([]),
@@ -725,78 +1285,78 @@ var createBaseInputSchema = z3.object({
725
1285
  // as a pending ChangeRequest (status "in_review") instead of creating it
726
1286
  // immediately. Pass `autoMerge: true` only for callers that don't need human
727
1287
  // review (seed/migration scripts, an explicit no-review agent task).
728
- autoMerge: z3.boolean().optional().default(false)
1288
+ autoMerge: z5.boolean().optional().default(false)
729
1289
  });
730
- var createBaseFieldInputSchema = z3.object({
1290
+ var createBaseFieldInputSchema = z5.object({
731
1291
  name: fieldNameSchema,
732
1292
  // Field slugs are snake_case identifiers (e.g. `cover_image`, `publish_date`) — the
733
1293
  // seed and the inline-fields path on `POST /bases` already allow underscores, so the
734
1294
  // add-field endpoint must too. (Base/folder/view slugs stay kebab-case: they go in URLs.)
735
- slug: z3.string().min(1).regex(/^[a-z0-9_-]+$/),
1295
+ slug: z5.string().min(1).regex(/^[a-z0-9_-]+$/),
736
1296
  type: fieldTypeSchema.default("text"),
737
- required: z3.boolean().optional().default(false),
1297
+ required: z5.boolean().optional().default(false),
738
1298
  options: fieldOptionsSchema.optional().default({})
739
1299
  });
740
1300
  var createFieldChangeRequestInputSchema = createBaseFieldInputSchema.extend({
741
- message: z3.string().optional().default("Add field"),
742
- submittedBy: z3.string().optional().default("local-editor")
1301
+ message: z5.string().optional().default("Add field"),
1302
+ submittedBy: z5.string().optional().default("local-editor")
743
1303
  });
744
- var deleteFieldChangeRequestInputSchema = z3.object({
745
- fieldId: z3.string().min(1),
746
- message: z3.string().optional(),
747
- submittedBy: z3.string().optional().default("local-editor")
1304
+ var deleteFieldChangeRequestInputSchema = z5.object({
1305
+ fieldId: z5.string().min(1),
1306
+ message: z5.string().optional(),
1307
+ submittedBy: z5.string().optional().default("local-editor")
748
1308
  });
749
- var updateFieldChangeRequestInputSchema = z3.object({
750
- fieldId: z3.string().min(1),
751
- patch: z3.object({
1309
+ var updateFieldChangeRequestInputSchema = z5.object({
1310
+ fieldId: z5.string().min(1),
1311
+ patch: z5.object({
752
1312
  name: fieldNameSchema.optional(),
753
- required: z3.boolean().optional(),
1313
+ required: z5.boolean().optional(),
754
1314
  options: fieldOptionsSchema.optional()
755
1315
  }),
756
- message: z3.string().optional(),
757
- submittedBy: z3.string().optional().default("local-editor")
1316
+ message: z5.string().optional(),
1317
+ submittedBy: z5.string().optional().default("local-editor")
758
1318
  });
759
- var previewFieldConversionInputSchema = z3.object({
760
- fieldId: z3.string().min(1),
1319
+ var previewFieldConversionInputSchema = z5.object({
1320
+ fieldId: z5.string().min(1),
761
1321
  newType: fieldTypeSchema
762
1322
  });
763
- var previewFieldConversionOutputSchema = z3.object({
764
- totalCount: z3.number(),
765
- convertibleCount: z3.number(),
766
- nullCount: z3.number(),
767
- conflicts: z3.array(z3.object({ recordId: z3.string(), currentValue: z3.unknown() }))
1323
+ var previewFieldConversionOutputSchema = z5.object({
1324
+ totalCount: z5.number(),
1325
+ convertibleCount: z5.number(),
1326
+ nullCount: z5.number(),
1327
+ conflicts: z5.array(z5.object({ recordId: z5.string(), currentValue: z5.unknown() }))
768
1328
  });
769
- var convertFieldChangeRequestInputSchema = z3.object({
770
- fieldId: z3.string().min(1),
1329
+ var convertFieldChangeRequestInputSchema = z5.object({
1330
+ fieldId: z5.string().min(1),
771
1331
  newType: fieldTypeSchema,
772
- selectChoiceMode: z3.enum(["auto_create", "null_on_missing"]).default("null_on_missing"),
773
- message: z3.string().optional(),
774
- submittedBy: z3.string().optional().default("local-editor")
1332
+ selectChoiceMode: z5.enum(["auto_create", "null_on_missing"]).default("null_on_missing"),
1333
+ message: z5.string().optional(),
1334
+ submittedBy: z5.string().optional().default("local-editor")
775
1335
  });
776
- var reorderFieldsChangeRequestInputSchema = z3.object({
777
- fieldIds: z3.array(z3.string()).min(1),
778
- message: z3.string().optional(),
779
- submittedBy: z3.string().optional().default("local-editor")
1336
+ var reorderFieldsChangeRequestInputSchema = z5.object({
1337
+ fieldIds: z5.array(z5.string()).min(1),
1338
+ message: z5.string().optional(),
1339
+ submittedBy: z5.string().optional().default("local-editor")
780
1340
  });
781
- var archiveBaseInputSchema = z3.object({
782
- message: z3.string().optional(),
783
- submittedBy: z3.string().optional().default("local-editor")
1341
+ var archiveBaseInputSchema = z5.object({
1342
+ message: z5.string().optional(),
1343
+ submittedBy: z5.string().optional().default("local-editor")
784
1344
  });
785
- var restoreBaseInputSchema = z3.object({
786
- message: z3.string().optional(),
787
- submittedBy: z3.string().optional().default("local-editor")
1345
+ var restoreBaseInputSchema = z5.object({
1346
+ message: z5.string().optional(),
1347
+ submittedBy: z5.string().optional().default("local-editor")
788
1348
  });
789
- var restoreFieldChangeRequestInputSchema = z3.object({
790
- fieldId: z3.string().min(1),
791
- message: z3.string().optional(),
792
- submittedBy: z3.string().optional().default("local-editor")
1349
+ var restoreFieldChangeRequestInputSchema = z5.object({
1350
+ fieldId: z5.string().min(1),
1351
+ message: z5.string().optional(),
1352
+ submittedBy: z5.string().optional().default("local-editor")
793
1353
  });
794
1354
 
795
1355
  // ../../packages/busabase-contract/src/domains/base/contract/view-schemas.ts
796
- import { z as z5 } from "zod";
1356
+ import { z as z7 } from "zod";
797
1357
 
798
1358
  // ../../packages/busabase-contract/src/contract/schemas.ts
799
- import { z as z4 } from "zod";
1359
+ import { z as z6 } from "zod";
800
1360
 
801
1361
  // ../../packages/busabase-contract/src/domains/filetree/definition.ts
802
1362
  var fileTreeOperations = (type) => [
@@ -1025,128 +1585,140 @@ var OPERATION_META = Object.fromEntries(
1025
1585
  );
1026
1586
 
1027
1587
  // ../../packages/busabase-contract/src/contract/schemas.ts
1028
- var nodeSchema = z4.lazy(
1029
- () => z4.object({
1030
- id: z4.string(),
1031
- parentId: z4.string().nullable(),
1032
- type: z4.enum(NODE_TYPES),
1033
- slug: z4.string(),
1034
- name: z4.string(),
1035
- description: z4.string(),
1036
- metadata: z4.object({
1037
- entryFile: z4.string().optional(),
1038
- visibility: z4.enum(["private", "workspace", "public"]).optional(),
1039
- version: z4.string().optional(),
1040
- assetId: z4.string().optional()
1588
+ var nodeSchema = z6.lazy(
1589
+ () => z6.object({
1590
+ id: z6.string(),
1591
+ parentId: z6.string().nullable(),
1592
+ type: z6.enum(NODE_TYPES),
1593
+ slug: z6.string(),
1594
+ name: z6.string(),
1595
+ description: z6.string(),
1596
+ metadata: z6.object({
1597
+ entryFile: z6.string().optional(),
1598
+ visibility: z6.enum(["private", "workspace", "public"]).optional(),
1599
+ version: z6.string().optional(),
1600
+ assetId: z6.string().optional()
1041
1601
  }).default({}),
1042
- position: z4.number(),
1043
- createdAt: z4.string(),
1044
- updatedAt: z4.string(),
1045
- baseId: z4.string().nullable(),
1046
- children: z4.array(nodeSchema),
1047
- hasChildren: z4.boolean().optional()
1602
+ position: z6.number(),
1603
+ createdAt: z6.string(),
1604
+ updatedAt: z6.string(),
1605
+ baseId: z6.string().nullable(),
1606
+ children: z6.array(nodeSchema),
1607
+ hasChildren: z6.boolean().optional()
1048
1608
  })
1049
1609
  );
1050
- var nodePrincipalSchema = z4.object({
1051
- id: z4.string(),
1052
- nodeId: z4.string(),
1053
- principalType: z4.enum(["user", "team", "space"]),
1054
- principalId: z4.string(),
1055
- role: z4.enum(["read", "changeRequest", "write", "manage"]),
1056
- grantedBy: z4.string(),
1057
- createdAt: z4.string(),
1058
- updatedAt: z4.string()
1610
+ var nodePrincipalSchema = z6.object({
1611
+ id: z6.string(),
1612
+ nodeId: z6.string(),
1613
+ principalType: z6.enum(["user", "team", "space"]),
1614
+ principalId: z6.string(),
1615
+ role: z6.enum(["read", "changeRequest", "write", "manage"]),
1616
+ grantedBy: z6.string(),
1617
+ createdAt: z6.string(),
1618
+ updatedAt: z6.string()
1059
1619
  });
1060
- var listNodesInputSchema = z4.object({
1061
- parentId: z4.string().nullable().optional().describe("Node to start from. Omit or null to start from the space root."),
1062
- depth: z4.coerce.number().int().min(1).max(5).optional().describe(
1620
+ var listNodesInputSchema = z6.object({
1621
+ parentId: z6.string().nullable().optional().describe("Node to start from. Omit or null to start from the space root."),
1622
+ depth: z6.coerce.number().int().min(1).max(5).optional().describe(
1063
1623
  "How many levels beneath the start point to eagerly include (default 2 once either field is set). Capped at 5."
1064
1624
  )
1065
1625
  }).optional();
1066
- var isDescendantInputSchema = z4.object({
1067
- nodeId: z4.string(),
1068
- potentialAncestorId: z4.string()
1626
+ var isDescendantInputSchema = z6.object({
1627
+ nodeId: z6.string(),
1628
+ potentialAncestorId: z6.string()
1629
+ });
1630
+ var isDescendantOutputSchema = z6.object({
1631
+ isDescendant: z6.boolean()
1632
+ });
1633
+ var searchNodesByNameInputSchema = z6.object({
1634
+ query: z6.string().min(1),
1635
+ limit: z6.number().int().min(1).max(50).optional().default(20)
1069
1636
  });
1070
- var isDescendantOutputSchema = z4.object({
1071
- isDescendant: z4.boolean()
1637
+ var nodeSearchResultSchema = z6.object({
1638
+ id: z6.string(),
1639
+ type: z6.enum(NODE_TYPES),
1640
+ name: z6.string(),
1641
+ slug: z6.string(),
1642
+ path: z6.string(),
1643
+ updatedAt: z6.string()
1072
1644
  });
1073
- var userRefSchema = z4.object({
1074
- id: z4.string(),
1075
- name: z4.string().nullable(),
1076
- email: z4.string().nullable(),
1077
- image: z4.string().nullable(),
1078
- role: z4.string().nullable().optional()
1645
+ var userRefSchema = z6.object({
1646
+ id: z6.string(),
1647
+ name: z6.string().nullable(),
1648
+ email: z6.string().nullable(),
1649
+ image: z6.string().nullable(),
1650
+ role: z6.string().nullable().optional()
1079
1651
  });
1080
- var commitSchema = z4.object({
1081
- id: z4.string(),
1082
- baseId: z4.string().nullable(),
1083
- targetType: z4.enum(["base", "node"]),
1084
- nodeId: z4.string().nullable(),
1085
- operationId: z4.string().nullable(),
1086
- parentCommitId: z4.string().nullable(),
1087
- fields: z4.record(z4.string(), z4.unknown()),
1088
- operation: z4.enum(OPERATION_KINDS),
1089
- message: z4.string(),
1090
- author: z4.string(),
1652
+ var commitSchema = z6.object({
1653
+ id: z6.string(),
1654
+ baseId: z6.string().nullable(),
1655
+ targetType: z6.enum(["base", "node"]),
1656
+ nodeId: z6.string().nullable(),
1657
+ operationId: z6.string().nullable(),
1658
+ parentCommitId: z6.string().nullable(),
1659
+ fields: z6.record(z6.string(), z6.unknown()),
1660
+ operation: z6.enum(OPERATION_KINDS),
1661
+ message: z6.string(),
1662
+ author: z6.string(),
1091
1663
  authorUser: userRefSchema.nullable().optional().default(null),
1092
- createdAt: z4.string()
1664
+ createdAt: z6.string()
1093
1665
  });
1094
- var operationSchema = z4.object({
1095
- id: z4.string(),
1096
- changeRequestId: z4.string(),
1097
- baseId: z4.string().nullable(),
1098
- targetType: z4.enum(["base", "node"]),
1099
- nodeId: z4.string().nullable(),
1100
- operation: z4.enum(OPERATION_KINDS),
1101
- status: z4.enum(["pending", "merged", "archived", "failed"]),
1102
- targetRecordId: z4.string().nullable(),
1103
- targetViewId: z4.string().nullable(),
1104
- filePath: z4.string().nullable(),
1105
- sourceRecordId: z4.string().nullable(),
1106
- sourceCommitId: z4.string().nullable(),
1107
- baseCommitId: z4.string().nullable(),
1108
- headCommitId: z4.string(),
1109
- deleteMode: z4.enum(["archive"]),
1110
- mergedRecordId: z4.string().nullable(),
1111
- mergedViewId: z4.string().nullable(),
1112
- position: z4.number(),
1113
- createdAt: z4.string(),
1114
- updatedAt: z4.string(),
1666
+ var operationSchema = z6.object({
1667
+ id: z6.string(),
1668
+ changeRequestId: z6.string(),
1669
+ baseId: z6.string().nullable(),
1670
+ targetType: z6.enum(["base", "node"]),
1671
+ nodeId: z6.string().nullable(),
1672
+ operation: z6.enum(OPERATION_KINDS),
1673
+ status: z6.enum(["pending", "merged", "archived", "failed"]),
1674
+ targetRecordId: z6.string().nullable(),
1675
+ targetViewId: z6.string().nullable(),
1676
+ filePath: z6.string().nullable(),
1677
+ sourceRecordId: z6.string().nullable(),
1678
+ sourceCommitId: z6.string().nullable(),
1679
+ baseCommitId: z6.string().nullable(),
1680
+ headCommitId: z6.string(),
1681
+ deleteMode: z6.enum(["archive"]),
1682
+ mergedRecordId: z6.string().nullable(),
1683
+ mergedViewId: z6.string().nullable(),
1684
+ position: z6.number(),
1685
+ createdAt: z6.string(),
1686
+ updatedAt: z6.string(),
1115
1687
  headCommit: commitSchema,
1116
1688
  // Resolved canonical "before" values for the operation's target, so the UI can
1117
1689
  // render a true before → after field diff. Null for creations (no prior state)
1118
1690
  // and for kinds whose prior state isn't a field map (e.g. skill files, whose
1119
1691
  // previous content lives in storage). Records resolve from the base commit;
1120
1692
  // views resolve from the current view row ({ name, description, config }).
1121
- baseFields: z4.record(z4.string(), z4.unknown()).nullable()
1693
+ baseFields: z6.record(z6.string(), z6.unknown()).nullable()
1122
1694
  });
1123
- var reviewSchema = z4.object({
1124
- id: z4.string(),
1125
- changeRequestId: z4.string(),
1126
- reviewerId: z4.string(),
1695
+ var reviewSchema = z6.object({
1696
+ id: z6.string(),
1697
+ changeRequestId: z6.string(),
1698
+ reviewerId: z6.string(),
1127
1699
  reviewer: userRefSchema.nullable().optional().default(null),
1128
- verdict: z4.enum(["approved", "rejected"]),
1129
- reason: z4.string().nullable(),
1130
- visibleOperationHeads: z4.record(z4.string(), z4.string()),
1131
- createdAt: z4.string()
1700
+ verdict: z6.enum(["approved", "rejected"]),
1701
+ reason: z6.string().nullable(),
1702
+ visibleOperationHeads: z6.record(z6.string(), z6.string()),
1703
+ createdAt: z6.string()
1132
1704
  });
1133
- var commentSubjectTypeSchema = z4.enum(["record", "change_request", "operation", "commit"]);
1134
- var commentSchema = z4.object({
1135
- id: z4.string(),
1705
+ var commentSubjectTypeSchema = z6.enum(["record", "change_request", "operation", "commit"]);
1706
+ var commentSchema = z6.object({
1707
+ id: z6.string(),
1136
1708
  subjectType: commentSubjectTypeSchema,
1137
- subjectId: z4.string(),
1138
- recordId: z4.string().nullable(),
1139
- changeRequestId: z4.string().nullable(),
1140
- operationId: z4.string().nullable(),
1141
- commitId: z4.string().nullable(),
1142
- authorId: z4.string(),
1709
+ subjectId: z6.string(),
1710
+ recordId: z6.string().nullable(),
1711
+ changeRequestId: z6.string().nullable(),
1712
+ operationId: z6.string().nullable(),
1713
+ commitId: z6.string().nullable(),
1714
+ authorId: z6.string(),
1143
1715
  author: userRefSchema.nullable().optional().default(null),
1144
- body: z4.string(),
1145
- mentionsAi: z4.boolean(),
1146
- createdAt: z4.string(),
1147
- updatedAt: z4.string()
1716
+ body: z6.string(),
1717
+ mentionsAi: z6.boolean(),
1718
+ createdAt: z6.string(),
1719
+ updatedAt: z6.string()
1148
1720
  });
1149
- var changeRequestStatusSchema = z4.enum([
1721
+ var changeRequestStatusSchema = z6.enum([
1150
1722
  "in_review",
1151
1723
  "changes_requested",
1152
1724
  "approved",
@@ -1155,53 +1727,53 @@ var changeRequestStatusSchema = z4.enum([
1155
1727
  "abandoned",
1156
1728
  "conflict"
1157
1729
  ]);
1158
- var changeRequestSchema = z4.object({
1159
- id: z4.string(),
1160
- baseId: z4.string().nullable(),
1161
- targetType: z4.enum(["base", "node"]),
1162
- nodeId: z4.string().nullable(),
1730
+ var changeRequestSchema = z6.object({
1731
+ id: z6.string(),
1732
+ baseId: z6.string().nullable(),
1733
+ targetType: z6.enum(["base", "node"]),
1734
+ nodeId: z6.string().nullable(),
1163
1735
  status: changeRequestStatusSchema,
1164
- submittedBy: z4.string(),
1736
+ submittedBy: z6.string(),
1165
1737
  submittedByUser: userRefSchema.nullable().optional().default(null),
1166
- sourceMeta: z4.record(z4.string(), z4.unknown()),
1167
- reviewPolicySnapshot: z4.record(z4.string(), z4.unknown()),
1168
- mergeSummary: z4.record(z4.string(), z4.unknown()),
1169
- rejectedReason: z4.string().nullable(),
1170
- reviewedAt: z4.string().nullable(),
1171
- mergedAt: z4.string().nullable(),
1172
- createdAt: z4.string(),
1173
- updatedAt: z4.string(),
1738
+ sourceMeta: z6.record(z6.string(), z6.unknown()),
1739
+ reviewPolicySnapshot: z6.record(z6.string(), z6.unknown()),
1740
+ mergeSummary: z6.record(z6.string(), z6.unknown()),
1741
+ rejectedReason: z6.string().nullable(),
1742
+ reviewedAt: z6.string().nullable(),
1743
+ mergedAt: z6.string().nullable(),
1744
+ createdAt: z6.string(),
1745
+ updatedAt: z6.string(),
1174
1746
  base: baseSchema.nullable(),
1175
1747
  node: nodeSchema.nullable(),
1176
- operations: z4.array(operationSchema),
1748
+ operations: z6.array(operationSchema),
1177
1749
  primaryOperation: operationSchema.nullable(),
1178
- operationCount: z4.number(),
1179
- reviews: z4.array(reviewSchema)
1750
+ operationCount: z6.number(),
1751
+ reviews: z6.array(reviewSchema)
1180
1752
  });
1181
- var agentTaskSchema = z4.object({
1753
+ var agentTaskSchema = z6.object({
1182
1754
  changeRequest: changeRequestSchema,
1183
- trigger: z4.enum(["changes_requested", "ai_mention"]),
1184
- reviewReason: z4.string().nullable(),
1185
- aiComments: z4.array(commentSchema)
1755
+ trigger: z6.enum(["changes_requested", "ai_mention"]),
1756
+ reviewReason: z6.string().nullable(),
1757
+ aiComments: z6.array(commentSchema)
1186
1758
  });
1187
- var searchResultSchema = z4.object({
1188
- id: z4.string(),
1189
- kind: z4.enum(["record", "change_request", "base", "file"]),
1190
- title: z4.string(),
1191
- body: z4.string(),
1192
- eyebrow: z4.string(),
1193
- href: z4.string(),
1194
- updatedAt: z4.string().nullable()
1759
+ var searchResultSchema = z6.object({
1760
+ id: z6.string(),
1761
+ kind: z6.enum(["record", "change_request", "base", "file"]),
1762
+ title: z6.string(),
1763
+ body: z6.string(),
1764
+ eyebrow: z6.string(),
1765
+ href: z6.string(),
1766
+ updatedAt: z6.string().nullable()
1195
1767
  });
1196
- var searchResponseSchema = z4.object({
1197
- query: z4.string(),
1198
- limit: z4.number(),
1199
- offset: z4.number(),
1200
- hasMore: z4.boolean(),
1201
- results: z4.array(searchResultSchema)
1768
+ var searchResponseSchema = z6.object({
1769
+ query: z6.string(),
1770
+ limit: z6.number(),
1771
+ offset: z6.number(),
1772
+ hasMore: z6.boolean(),
1773
+ results: z6.array(searchResultSchema)
1202
1774
  });
1203
- var liveEventSchema = z4.object({
1204
- kind: z4.enum([
1775
+ var liveEventSchema = z6.object({
1776
+ kind: z6.enum([
1205
1777
  "change_request.created",
1206
1778
  "change_request.updated",
1207
1779
  "change_request.deleted",
@@ -1215,16 +1787,16 @@ var liveEventSchema = z4.object({
1215
1787
  // busabase-cloud's host hook to persist an inbox notification row.
1216
1788
  "change_request.pending_review"
1217
1789
  ]),
1218
- spaceId: z4.string(),
1219
- actorId: z4.string(),
1220
- changeRequestId: z4.string(),
1221
- baseId: z4.string().nullable(),
1222
- nodeIds: z4.array(z4.string()),
1223
- recordIds: z4.array(z4.string()),
1224
- viewIds: z4.array(z4.string()),
1225
- operationCount: z4.number()
1790
+ spaceId: z6.string(),
1791
+ actorId: z6.string(),
1792
+ changeRequestId: z6.string(),
1793
+ baseId: z6.string().nullable(),
1794
+ nodeIds: z6.array(z6.string()),
1795
+ recordIds: z6.array(z6.string()),
1796
+ viewIds: z6.array(z6.string()),
1797
+ operationCount: z6.number()
1226
1798
  });
1227
- var auditActionSchema = z4.enum([
1799
+ var auditActionSchema = z6.enum([
1228
1800
  "record.viewed",
1229
1801
  "change_request.created",
1230
1802
  "change_request.updated",
@@ -1248,155 +1820,155 @@ var auditActionSchema = z4.enum([
1248
1820
  "asset.text_marked_none",
1249
1821
  "node.purged"
1250
1822
  ]);
1251
- var auditEventSchema = z4.object({
1252
- id: z4.string(),
1823
+ var auditEventSchema = z6.object({
1824
+ id: z6.string(),
1253
1825
  action: auditActionSchema,
1254
- actorId: z4.string(),
1826
+ actorId: z6.string(),
1255
1827
  actor: userRefSchema.nullable().optional().default(null),
1256
- baseId: z4.string().nullable(),
1257
- recordId: z4.string().nullable(),
1258
- changeRequestId: z4.string().nullable(),
1259
- operationId: z4.string().nullable(),
1260
- commitId: z4.string().nullable(),
1261
- metadata: z4.record(z4.string(), z4.unknown()),
1262
- createdAt: z4.string()
1828
+ baseId: z6.string().nullable(),
1829
+ recordId: z6.string().nullable(),
1830
+ changeRequestId: z6.string().nullable(),
1831
+ operationId: z6.string().nullable(),
1832
+ commitId: z6.string().nullable(),
1833
+ metadata: z6.record(z6.string(), z6.unknown()),
1834
+ createdAt: z6.string()
1263
1835
  });
1264
- var createAuditEventInputSchema = z4.object({
1836
+ var createAuditEventInputSchema = z6.object({
1265
1837
  action: auditActionSchema,
1266
- actorId: z4.string().optional().default("local-viewer"),
1267
- baseId: z4.string().optional().nullable(),
1268
- recordId: z4.string().optional().nullable(),
1269
- changeRequestId: z4.string().optional().nullable(),
1270
- operationId: z4.string().optional().nullable(),
1271
- commitId: z4.string().optional().nullable(),
1272
- metadata: z4.record(z4.string(), z4.unknown()).optional().default({})
1838
+ actorId: z6.string().optional().default("local-viewer"),
1839
+ baseId: z6.string().optional().nullable(),
1840
+ recordId: z6.string().optional().nullable(),
1841
+ changeRequestId: z6.string().optional().nullable(),
1842
+ operationId: z6.string().optional().nullable(),
1843
+ commitId: z6.string().optional().nullable(),
1844
+ metadata: z6.record(z6.string(), z6.unknown()).optional().default({})
1273
1845
  });
1274
- var nodeOperationInputSchema = z4.discriminatedUnion("kind", [
1275
- z4.object({
1276
- kind: z4.literal("create"),
1277
- ref: z4.string().min(1).optional().describe(
1846
+ var nodeOperationInputSchema = z6.discriminatedUnion("kind", [
1847
+ z6.object({
1848
+ kind: z6.literal("create"),
1849
+ ref: z6.string().min(1).optional().describe(
1278
1850
  'Optional in-change-request temp id for this node. A later operation can set parentNodeRef to this value to nest under it \u2014 e.g. create a folder with ref "growth", then create Bases with parentNodeRef "growth", all in one change request.'
1279
1851
  ),
1280
- parentNodeId: z4.string().optional(),
1281
- parentNodeRef: z4.string().min(1).optional().describe(
1852
+ parentNodeId: z6.string().optional(),
1853
+ parentNodeRef: z6.string().min(1).optional().describe(
1282
1854
  "Parent this node under a node an EARLIER operation in the same change request created (matched by its ref). Mutually exclusive with parentNodeId."
1283
1855
  ),
1284
- nodeType: z4.enum(CREATABLE_NODE_TYPES),
1285
- slug: z4.string().min(1).regex(/^[a-z0-9-]+$/),
1286
- name: z4.string().min(1),
1287
- description: z4.string().optional().default(""),
1288
- metadata: z4.record(z4.string(), z4.unknown()).optional().default({}),
1856
+ nodeType: z6.enum(CREATABLE_NODE_TYPES),
1857
+ slug: z6.string().min(1).regex(/^[a-z0-9-]+$/),
1858
+ name: z6.string().min(1),
1859
+ description: z6.string().optional().default(""),
1860
+ metadata: z6.record(z6.string(), z6.unknown()).optional().default({}),
1289
1861
  // Base fields (nodeType "base" only); a base needs at least one field.
1290
- fields: z4.array(
1291
- z4.object({
1292
- slug: z4.string().min(1).regex(/^[a-z0-9-]+$/),
1862
+ fields: z6.array(
1863
+ z6.object({
1864
+ slug: z6.string().min(1).regex(/^[a-z0-9-]+$/),
1293
1865
  name: fieldNameSchema,
1294
1866
  type: fieldTypeSchema.default("text"),
1295
- required: z4.boolean().optional().default(false),
1867
+ required: z6.boolean().optional().default(false),
1296
1868
  options: fieldOptionsSchema.optional().default({})
1297
1869
  })
1298
1870
  ).optional()
1299
1871
  }),
1300
- z4.object({
1301
- kind: z4.literal("rename"),
1302
- nodeId: z4.string(),
1303
- slug: z4.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
1304
- name: z4.string().min(1).optional(),
1305
- description: z4.string().optional()
1872
+ z6.object({
1873
+ kind: z6.literal("rename"),
1874
+ nodeId: z6.string(),
1875
+ slug: z6.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
1876
+ name: z6.string().min(1).optional(),
1877
+ description: z6.string().optional()
1306
1878
  }),
1307
- z4.object({
1308
- kind: z4.literal("delete"),
1309
- nodeId: z4.string()
1879
+ z6.object({
1880
+ kind: z6.literal("delete"),
1881
+ nodeId: z6.string()
1310
1882
  }),
1311
- z4.object({
1312
- kind: z4.literal("restore"),
1313
- nodeId: z4.string()
1883
+ z6.object({
1884
+ kind: z6.literal("restore"),
1885
+ nodeId: z6.string()
1314
1886
  }),
1315
- z4.object({
1316
- kind: z4.literal("move"),
1317
- nodeId: z4.string(),
1887
+ z6.object({
1888
+ kind: z6.literal("move"),
1889
+ nodeId: z6.string(),
1318
1890
  // Exactly one of parentNodeId / parentNodeRef (a ref created earlier in this CR).
1319
- parentNodeId: z4.string().optional(),
1320
- parentNodeRef: z4.string().min(1).optional(),
1321
- position: z4.number().int().optional()
1891
+ parentNodeId: z6.string().optional(),
1892
+ parentNodeRef: z6.string().min(1).optional(),
1893
+ position: z6.number().int().optional()
1322
1894
  })
1323
1895
  ]);
1324
- var createNodeChangeRequestInputSchema = z4.object({
1325
- message: z4.string().optional().default("Update node tree").describe(
1896
+ var createNodeChangeRequestInputSchema = z6.object({
1897
+ message: z6.string().optional().default("Update node tree").describe(
1326
1898
  '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".'
1327
1899
  ),
1328
- submittedBy: z4.string().optional().default("local-producer"),
1329
- autoMerge: z4.boolean().optional().default(false).describe(
1900
+ submittedBy: z6.string().optional().default("local-producer"),
1901
+ autoMerge: z6.boolean().optional().default(false).describe(
1330
1902
  "Whether to approve and merge this structural node change immediately. Defaults to false so node Change Request creation is review-first unless explicitly requested."
1331
1903
  ),
1332
- operations: z4.array(nodeOperationInputSchema).min(1)
1904
+ operations: z6.array(nodeOperationInputSchema).min(1)
1333
1905
  });
1334
- var moveNodeInputSchema = z4.object({
1335
- nodeId: z4.string(),
1336
- parentNodeId: z4.string().optional().describe("New parent folder node id. Omit to keep the current parent and only reorder."),
1337
- position: z4.number().int().optional().describe("New position among the target parent's children."),
1338
- message: z4.string().optional().describe("Reviewer-facing Change Request message."),
1339
- submittedBy: z4.string().optional()
1906
+ var moveNodeInputSchema = z6.object({
1907
+ nodeId: z6.string(),
1908
+ parentNodeId: z6.string().optional().describe("New parent folder node id. Omit to keep the current parent and only reorder."),
1909
+ position: z6.number().int().optional().describe("New position among the target parent's children."),
1910
+ message: z6.string().optional().describe("Reviewer-facing Change Request message."),
1911
+ submittedBy: z6.string().optional()
1340
1912
  });
1341
- var createDeleteChangeRequestInputSchema = z4.object({
1342
- message: z4.string().optional().default("Delete record").describe(
1913
+ var createDeleteChangeRequestInputSchema = z6.object({
1914
+ message: z6.string().optional().default("Delete record").describe(
1343
1915
  'Explanation shown to the human reviewer. Say what is being removed and why, e.g. "Archive duplicate contact \u2014 merged into Acme Corp".'
1344
1916
  ),
1345
- submittedBy: z4.string().optional().default("local-producer"),
1917
+ submittedBy: z6.string().optional().default("local-producer"),
1346
1918
  // Only "archive" is supported — hard delete after retention was never
1347
1919
  // implemented, so the API no longer accepts it (breaking change).
1348
- deleteMode: z4.enum(["archive"]).optional().default("archive")
1920
+ deleteMode: z6.enum(["archive"]).optional().default("archive")
1349
1921
  });
1350
- var reviseOperationInputSchema = z4.object({
1351
- fields: z4.record(z4.string(), z4.unknown()).describe(
1922
+ var reviseOperationInputSchema = z6.object({
1923
+ fields: z6.record(z6.string(), z6.unknown()).describe(
1352
1924
  "Updated field values keyed by field slug. If you set the base's PRIMARY field (its first field), keep it a short human-readable name \u2014 it is the record's display title everywhere."
1353
1925
  ),
1354
- message: z4.string().optional().default("Revise operation").describe(
1926
+ message: z6.string().optional().default("Revise operation").describe(
1355
1927
  'Explanation shown to the human reviewer. Write a conventional-commit style subject \u2014 imperative verb + what + why, e.g. "Update deal stage to qualified \u2014 demo booked for July 8".'
1356
1928
  ),
1357
- author: z4.string().optional().default("local-producer"),
1358
- baseCommitId: z4.string().optional()
1929
+ author: z6.string().optional().default("local-producer"),
1930
+ baseCommitId: z6.string().optional()
1359
1931
  });
1360
- var reviewChangeRequestInputSchema = z4.object({
1361
- verdict: z4.enum(["approved", "rejected"]),
1362
- reason: z4.string().optional()
1932
+ var reviewChangeRequestInputSchema = z6.object({
1933
+ verdict: z6.enum(["approved", "rejected"]),
1934
+ reason: z6.string().optional()
1363
1935
  });
1364
- var commentSubjectInputSchema = z4.object({
1936
+ var commentSubjectInputSchema = z6.object({
1365
1937
  subjectType: commentSubjectTypeSchema,
1366
- subjectId: z4.string().min(1)
1938
+ subjectId: z6.string().min(1)
1367
1939
  });
1368
1940
  var createCommentInputSchema = commentSubjectInputSchema.extend({
1369
- authorId: z4.string().optional().default("local-admin"),
1370
- body: z4.string().trim().min(1),
1371
- mentionsAi: z4.boolean().optional().default(false)
1941
+ authorId: z6.string().optional().default("local-admin"),
1942
+ body: z6.string().trim().min(1),
1943
+ mentionsAi: z6.boolean().optional().default(false)
1372
1944
  });
1373
- var listInputSchema = z4.object({
1374
- limit: z4.coerce.number().int().min(1).max(100).optional().default(50)
1945
+ var listInputSchema = z6.object({
1946
+ limit: z6.coerce.number().int().min(1).max(100).optional().default(50)
1375
1947
  }).optional().default({ limit: 50 });
1376
- var listChangeRequestsPagedInputSchema = z4.object({
1377
- limit: z4.coerce.number().int().min(1).max(100).optional().default(50),
1948
+ var listChangeRequestsPagedInputSchema = z6.object({
1949
+ limit: z6.coerce.number().int().min(1).max(100).optional().default(50),
1378
1950
  /** Opaque base64 cursor (`createdAt|id`) for keyset pagination. */
1379
- cursor: z4.string().optional(),
1380
- status: z4.array(changeRequestStatusSchema).optional(),
1381
- mine: z4.boolean().optional()
1951
+ cursor: z6.string().optional(),
1952
+ status: z6.array(changeRequestStatusSchema).optional(),
1953
+ mine: z6.boolean().optional()
1382
1954
  }).optional().default({ limit: 50 });
1383
- var listChangeRequestsResponseSchema = z4.object({
1384
- changeRequests: z4.array(changeRequestSchema),
1385
- nextCursor: z4.string().nullable()
1955
+ var listChangeRequestsResponseSchema = z6.object({
1956
+ changeRequests: z6.array(changeRequestSchema),
1957
+ nextCursor: z6.string().nullable()
1386
1958
  });
1387
- var changeRequestCountsSchema = z4.object({
1388
- review: z4.number().int().nonnegative(),
1389
- changes: z4.number().int().nonnegative(),
1390
- created: z4.number().int().nonnegative(),
1391
- approved: z4.number().int().nonnegative(),
1392
- merged: z4.number().int().nonnegative(),
1393
- rejected: z4.number().int().nonnegative()
1959
+ var changeRequestCountsSchema = z6.object({
1960
+ review: z6.number().int().nonnegative(),
1961
+ changes: z6.number().int().nonnegative(),
1962
+ created: z6.number().int().nonnegative(),
1963
+ approved: z6.number().int().nonnegative(),
1964
+ merged: z6.number().int().nonnegative(),
1965
+ rejected: z6.number().int().nonnegative()
1394
1966
  });
1395
1967
  var SEARCH_SOURCES = ["records", "files", "names"];
1396
- var searchInputSchema = z4.object({
1397
- query: z4.string().default(""),
1398
- limit: z4.coerce.number().int().min(1).max(100).optional().default(20),
1399
- offset: z4.coerce.number().int().min(0).optional().default(0),
1968
+ var searchInputSchema = z6.object({
1969
+ query: z6.string().default(""),
1970
+ limit: z6.coerce.number().int().min(1).max(100).optional().default(20),
1971
+ offset: z6.coerce.number().int().min(0).optional().default(0),
1400
1972
  /**
1401
1973
  * Restrict which content this call searches. Omitted means all three
1402
1974
  * (unchanged behavior for every caller before this parameter existed).
@@ -1406,26 +1978,26 @@ var searchInputSchema = z4.object({
1406
1978
  * (`?sources=records&sources=files`) becomes an array. Accept both shapes
1407
1979
  * and normalize to an array.
1408
1980
  */
1409
- sources: z4.union([z4.array(z4.enum(SEARCH_SOURCES)), z4.enum(SEARCH_SOURCES)]).transform((value) => Array.isArray(value) ? value : [value]).optional()
1981
+ sources: z6.union([z6.array(z6.enum(SEARCH_SOURCES)), z6.enum(SEARCH_SOURCES)]).transform((value) => Array.isArray(value) ? value : [value]).optional()
1410
1982
  });
1411
- var authSpaceSchema = z4.object({
1412
- id: z4.string(),
1413
- name: z4.string(),
1414
- slug: z4.string().nullable(),
1415
- plan: z4.string().nullable()
1983
+ var authSpaceSchema = z6.object({
1984
+ id: z6.string(),
1985
+ name: z6.string(),
1986
+ slug: z6.string().nullable(),
1987
+ plan: z6.string().nullable()
1416
1988
  });
1417
- var authUserSchema = z4.object({
1418
- id: z4.string(),
1419
- name: z4.string(),
1420
- email: z4.string().nullable(),
1421
- image: z4.string().nullable()
1989
+ var authUserSchema = z6.object({
1990
+ id: z6.string(),
1991
+ name: z6.string(),
1992
+ email: z6.string().nullable(),
1993
+ image: z6.string().nullable()
1422
1994
  });
1423
- var authMemberSchema = z4.object({
1424
- userId: z4.string(),
1425
- spaceId: z4.string(),
1426
- role: z4.string()
1995
+ var authMemberSchema = z6.object({
1996
+ userId: z6.string(),
1997
+ spaceId: z6.string(),
1998
+ role: z6.string()
1427
1999
  });
1428
- var authInfoSchema = z4.object({
2000
+ var authInfoSchema = z6.object({
1429
2001
  /** The space this request is scoped to (explicit `x-busabase-space` header, else the default). */
1430
2002
  space: authSpaceSchema,
1431
2003
  user: authUserSchema,
@@ -1435,11 +2007,11 @@ var authInfoSchema = z4.object({
1435
2007
  * space-scoped — when this has more than one entry, callers should confirm the
1436
2008
  * intended space and target it explicitly via the `x-busabase-space` header.
1437
2009
  */
1438
- spaces: z4.array(authSpaceSchema)
2010
+ spaces: z6.array(authSpaceSchema)
1439
2011
  });
1440
2012
 
1441
2013
  // ../../packages/busabase-contract/src/domains/base/contract/view-schemas.ts
1442
- var viewFilterOperatorSchema = z5.enum([
2014
+ var viewFilterOperatorSchema = z7.enum([
1443
2015
  "contains",
1444
2016
  "equals",
1445
2017
  "not_empty",
@@ -1447,82 +2019,82 @@ var viewFilterOperatorSchema = z5.enum([
1447
2019
  "is_true",
1448
2020
  "is_false"
1449
2021
  ]);
1450
- var viewFilterSchema = z5.object({
1451
- fieldSlug: z5.string(),
2022
+ var viewFilterSchema = z7.object({
2023
+ fieldSlug: z7.string(),
1452
2024
  // Stable field identity — survives slug reuse; populated on merge.
1453
- fieldId: z5.string().optional(),
2025
+ fieldId: z7.string().optional(),
1454
2026
  operator: viewFilterOperatorSchema,
1455
- value: z5.unknown().optional()
2027
+ value: z7.unknown().optional()
1456
2028
  });
1457
- var viewSortSchema = z5.object({
1458
- direction: z5.enum(["asc", "desc"]),
1459
- fieldSlug: z5.string(),
1460
- fieldId: z5.string().optional()
2029
+ var viewSortSchema = z7.object({
2030
+ direction: z7.enum(["asc", "desc"]),
2031
+ fieldSlug: z7.string(),
2032
+ fieldId: z7.string().optional()
1461
2033
  });
1462
- var viewTypeSchema = z5.enum(["table", "gallery", "kanban", "calendar", "gantt"]);
1463
- var ganttScaleSchema = z5.enum(["week", "month"]);
1464
- var galleryCoverFitSchema = z5.enum(["cover", "fit"]);
1465
- var galleryCardSizeSchema = z5.enum(["small", "medium", "large"]);
1466
- var viewConfigSchema = z5.object({
1467
- filters: z5.array(viewFilterSchema).default([]),
1468
- sorts: z5.array(viewSortSchema).default([]),
1469
- visibleFieldSlugs: z5.array(z5.string()).nullable().optional(),
2034
+ var viewTypeSchema = z7.enum(["table", "gallery", "kanban", "calendar", "gantt"]);
2035
+ var ganttScaleSchema = z7.enum(["week", "month"]);
2036
+ var galleryCoverFitSchema = z7.enum(["cover", "fit"]);
2037
+ var galleryCardSizeSchema = z7.enum(["small", "medium", "large"]);
2038
+ var viewConfigSchema = z7.object({
2039
+ filters: z7.array(viewFilterSchema).default([]),
2040
+ sorts: z7.array(viewSortSchema).default([]),
2041
+ visibleFieldSlugs: z7.array(z7.string()).nullable().optional(),
1470
2042
  // ── Gallery-only presentation config (ignored by table views) ──
1471
2043
  // Which attachment field supplies the cover image (null = no cover).
1472
- coverFieldSlug: z5.string().nullable().optional(),
2044
+ coverFieldSlug: z7.string().nullable().optional(),
1473
2045
  coverFit: galleryCoverFitSchema.optional(),
1474
2046
  cardSize: galleryCardSizeSchema.optional(),
1475
2047
  // Whether to render the field label above each value on a card.
1476
- showFieldLabels: z5.boolean().optional(),
2048
+ showFieldLabels: z7.boolean().optional(),
1477
2049
  // ── Kanban-only: which single-select field stacks records into columns.
1478
- stackByFieldSlug: z5.string().nullable().optional(),
2050
+ stackByFieldSlug: z7.string().nullable().optional(),
1479
2051
  // ── Calendar-only: which date field positions records on the month grid.
1480
- dateFieldSlug: z5.string().nullable().optional(),
2052
+ dateFieldSlug: z7.string().nullable().optional(),
1481
2053
  // ── Gantt-only: the start/end date fields bounding each record's bar, plus
1482
2054
  // the time-axis granularity.
1483
- startFieldSlug: z5.string().nullable().optional(),
1484
- endFieldSlug: z5.string().nullable().optional(),
2055
+ startFieldSlug: z7.string().nullable().optional(),
2056
+ endFieldSlug: z7.string().nullable().optional(),
1485
2057
  ganttScale: ganttScaleSchema.optional()
1486
2058
  });
1487
- var viewSchema = z5.object({
1488
- id: z5.string(),
1489
- baseId: z5.string(),
1490
- slug: z5.string(),
1491
- name: z5.string(),
1492
- description: z5.string(),
2059
+ var viewSchema = z7.object({
2060
+ id: z7.string(),
2061
+ baseId: z7.string(),
2062
+ slug: z7.string(),
2063
+ name: z7.string(),
2064
+ description: z7.string(),
1493
2065
  type: viewTypeSchema,
1494
2066
  config: viewConfigSchema,
1495
- status: z5.enum(["active", "archived"]),
1496
- createdBy: z5.string(),
2067
+ status: z7.enum(["active", "archived"]),
2068
+ createdBy: z7.string(),
1497
2069
  createdByUser: userRefSchema.nullable().optional().default(null),
1498
- archivedAt: z5.string().nullable(),
1499
- createdAt: z5.string(),
1500
- updatedAt: z5.string()
2070
+ archivedAt: z7.string().nullable(),
2071
+ createdAt: z7.string(),
2072
+ updatedAt: z7.string()
1501
2073
  });
1502
- var createViewInputSchema = z5.object({
2074
+ var createViewInputSchema = z7.object({
1503
2075
  config: viewConfigSchema.optional().default({ filters: [], sorts: [] }),
1504
- description: z5.string().optional().default(""),
1505
- message: z5.string().optional().default("Create view"),
1506
- name: z5.string().min(1),
2076
+ description: z7.string().optional().default(""),
2077
+ message: z7.string().optional().default("Create view"),
2078
+ name: z7.string().min(1),
1507
2079
  type: viewTypeSchema.optional().default("table"),
1508
- slug: z5.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
1509
- submittedBy: z5.string().optional().default("local-producer")
2080
+ slug: z7.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
2081
+ submittedBy: z7.string().optional().default("local-producer")
1510
2082
  });
1511
- var updateViewInputSchema = z5.object({
2083
+ var updateViewInputSchema = z7.object({
1512
2084
  config: viewConfigSchema.optional(),
1513
- description: z5.string().optional(),
1514
- message: z5.string().optional().default("Update view"),
1515
- name: z5.string().min(1).optional(),
2085
+ description: z7.string().optional(),
2086
+ message: z7.string().optional().default("Update view"),
2087
+ name: z7.string().min(1).optional(),
1516
2088
  type: viewTypeSchema.optional(),
1517
- submittedBy: z5.string().optional().default("local-producer")
2089
+ submittedBy: z7.string().optional().default("local-producer")
1518
2090
  });
1519
- var deleteViewInputSchema = z5.object({
1520
- message: z5.string().optional().default("Delete view"),
1521
- submittedBy: z5.string().optional().default("local-producer")
2091
+ var deleteViewInputSchema = z7.object({
2092
+ message: z7.string().optional().default("Delete view"),
2093
+ submittedBy: z7.string().optional().default("local-producer")
1522
2094
  });
1523
- var restoreViewInputSchema = z5.object({
1524
- message: z5.string().optional().default("Restore view"),
1525
- submittedBy: z5.string().optional().default("local-producer")
2095
+ var restoreViewInputSchema = z7.object({
2096
+ message: z7.string().optional().default("Restore view"),
2097
+ submittedBy: z7.string().optional().default("local-producer")
1526
2098
  });
1527
2099
 
1528
2100
  // ../../packages/busabase-contract/src/domains/package/types.ts
@@ -1552,129 +2124,129 @@ var PACKAGE_MAX_FILE_BYTES = 25 * 1024 * 1024;
1552
2124
  var PACKAGE_MAX_TOTAL_BYTES = 250 * 1024 * 1024;
1553
2125
  var PACKAGE_MAX_FILE_COUNT = 5e3;
1554
2126
  var PACKAGE_MAX_RECORDS_PER_BASE = 5e4;
1555
- var PackageManifestSchema = z6.object({
1556
- format: z6.literal(PACKAGE_FORMAT),
2127
+ var PackageManifestSchema = z8.object({
2128
+ format: z8.literal(PACKAGE_FORMAT),
1557
2129
  /** Default `--into-folder` name. Not the repo name — a repo can host many packages. */
1558
- name: z6.string().min(1),
1559
- description: z6.string().default(""),
2130
+ name: z8.string().min(1),
2131
+ description: z8.string().default(""),
1560
2132
  /**
1561
2133
  * Informational / reserved for a future marketplace index. The real version pin
1562
2134
  * is always the git ref in the install URL.
1563
2135
  */
1564
- version: z6.string().optional(),
1565
- author: z6.string().optional(),
1566
- license: z6.string().optional(),
1567
- homepage: z6.string().url().optional(),
1568
- tags: z6.array(z6.string()).default([])
2136
+ version: z8.string().optional(),
2137
+ author: z8.string().optional(),
2138
+ license: z8.string().optional(),
2139
+ homepage: z8.string().url().optional(),
2140
+ tags: z8.array(z8.string()).default([])
1569
2141
  });
1570
- var PackageFileTreeNodeMetaSchema = z6.object({
1571
- type: z6.enum(PACKAGE_FILE_TREE_NODE_TYPES),
1572
- name: z6.string().min(1),
1573
- description: z6.string().default(""),
1574
- position: z6.number().int().optional()
2142
+ var PackageFileTreeNodeMetaSchema = z8.object({
2143
+ type: z8.enum(PACKAGE_FILE_TREE_NODE_TYPES),
2144
+ name: z8.string().min(1),
2145
+ description: z8.string().default(""),
2146
+ position: z8.number().int().optional()
1575
2147
  });
1576
- var PackageFolderMetaSchema = z6.object({
2148
+ var PackageFolderMetaSchema = z8.object({
1577
2149
  /** Defaults to the humanized slug when absent. */
1578
- name: z6.string().optional(),
1579
- description: z6.string().default(""),
1580
- position: z6.number().int().optional()
2150
+ name: z8.string().optional(),
2151
+ description: z8.string().default(""),
2152
+ position: z8.number().int().optional()
1581
2153
  });
1582
2154
  var PackageFileNodeMetaSchema = PackageFolderMetaSchema;
1583
- var PackageDocFrontmatterSchema = z6.object({
1584
- name: z6.string().min(1),
1585
- description: z6.string().default(""),
1586
- position: z6.number().int().optional()
2155
+ var PackageDocFrontmatterSchema = z8.object({
2156
+ name: z8.string().min(1),
2157
+ description: z8.string().default(""),
2158
+ position: z8.number().int().optional()
1587
2159
  });
1588
- var PackageFieldOptionsSchema = z6.object({
1589
- ai: z6.object({
1590
- model: z6.string().optional(),
1591
- prompt: z6.string().optional(),
1592
- reviewRequired: z6.boolean().optional(),
2160
+ var PackageFieldOptionsSchema = z8.object({
2161
+ ai: z8.object({
2162
+ model: z8.string().optional(),
2163
+ prompt: z8.string().optional(),
2164
+ reviewRequired: z8.boolean().optional(),
1593
2165
  /** Package-only — replaces `ai.sourceFieldIds`. */
1594
- sourceFieldSlugs: z6.array(z6.string()).optional()
2166
+ sourceFieldSlugs: z8.array(z8.string()).optional()
1595
2167
  }).optional(),
1596
- attachment: z6.object({
1597
- maxFiles: z6.number().int().positive().optional(),
1598
- allowedMimeTypes: z6.array(z6.string()).optional(),
1599
- maxFileSize: z6.number().int().positive().optional()
2168
+ attachment: z8.object({
2169
+ maxFiles: z8.number().int().positive().optional(),
2170
+ allowedMimeTypes: z8.array(z8.string()).optional(),
2171
+ maxFileSize: z8.number().int().positive().optional()
1600
2172
  }).optional(),
1601
- choices: z6.array(
1602
- z6.object({
1603
- color: z6.string().optional(),
1604
- id: z6.string(),
1605
- name: z6.string()
2173
+ choices: z8.array(
2174
+ z8.object({
2175
+ color: z8.string().optional(),
2176
+ id: z8.string(),
2177
+ name: z8.string()
1606
2178
  })
1607
2179
  ).optional(),
1608
- code: z6.object({
1609
- language: z6.string().optional()
2180
+ code: z8.object({
2181
+ language: z8.string().optional()
1610
2182
  }).optional(),
1611
- embed: z6.object({
1612
- aspectRatio: z6.enum(["16:9", "4:3", "1:1"]).optional(),
1613
- height: z6.number().int().positive().max(1200).optional(),
1614
- providers: z6.array(z6.string()).optional()
2183
+ embed: z8.object({
2184
+ aspectRatio: z8.enum(["16:9", "4:3", "1:1"]).optional(),
2185
+ height: z8.number().int().positive().max(1200).optional(),
2186
+ providers: z8.array(z8.string()).optional()
1615
2187
  }).optional(),
1616
2188
  /** Package-only — replaces `inverseFieldId`. */
1617
- inverseFieldSlug: z6.string().optional(),
1618
- multiple: z6.boolean().optional(),
1619
- number: z6.object({
1620
- format: z6.enum(["plain", "currency"]).optional(),
1621
- currency: z6.string().optional(),
1622
- locale: z6.string().optional()
2189
+ inverseFieldSlug: z8.string().optional(),
2190
+ multiple: z8.boolean().optional(),
2191
+ number: z8.object({
2192
+ format: z8.enum(["plain", "currency"]).optional(),
2193
+ currency: z8.string().optional(),
2194
+ locale: z8.string().optional()
1623
2195
  }).optional(),
1624
2196
  /** Native API alias — the server resolves it to `targetBaseId`. */
1625
- targetBaseSlug: z6.string().optional()
2197
+ targetBaseSlug: z8.string().optional()
1626
2198
  }).default({});
1627
- var PackageBaseFieldSchema = z6.object({
1628
- slug: z6.string().min(1),
2199
+ var PackageBaseFieldSchema = z8.object({
2200
+ slug: z8.string().min(1),
1629
2201
  name: fieldNameSchema,
1630
2202
  type: fieldTypeSchema,
1631
- required: z6.boolean().default(false),
1632
- position: z6.number().int(),
2203
+ required: z8.boolean().default(false),
2204
+ position: z8.number().int(),
1633
2205
  options: PackageFieldOptionsSchema
1634
2206
  });
1635
- var PackageViewFilterSchema = z6.object({
1636
- fieldSlug: z6.string().min(1),
2207
+ var PackageViewFilterSchema = z8.object({
2208
+ fieldSlug: z8.string().min(1),
1637
2209
  operator: viewFilterOperatorSchema,
1638
- value: z6.unknown().optional()
2210
+ value: z8.unknown().optional()
1639
2211
  });
1640
- var PackageViewSortSchema = z6.object({
1641
- direction: z6.enum(["asc", "desc"]),
1642
- fieldSlug: z6.string().min(1)
2212
+ var PackageViewSortSchema = z8.object({
2213
+ direction: z8.enum(["asc", "desc"]),
2214
+ fieldSlug: z8.string().min(1)
1643
2215
  });
1644
- var PackageViewConfigSchema = z6.object({
1645
- filters: z6.array(PackageViewFilterSchema).default([]),
1646
- sorts: z6.array(PackageViewSortSchema).default([]),
1647
- visibleFieldSlugs: z6.array(z6.string()).nullable().optional()
2216
+ var PackageViewConfigSchema = z8.object({
2217
+ filters: z8.array(PackageViewFilterSchema).default([]),
2218
+ sorts: z8.array(PackageViewSortSchema).default([]),
2219
+ visibleFieldSlugs: z8.array(z8.string()).nullable().optional()
1648
2220
  });
1649
- var PackageViewSchema = z6.object({
1650
- slug: z6.string().min(1),
1651
- name: z6.string().min(1),
1652
- description: z6.string().default(""),
1653
- type: z6.literal("table").default("table"),
2221
+ var PackageViewSchema = z8.object({
2222
+ slug: z8.string().min(1),
2223
+ name: z8.string().min(1),
2224
+ description: z8.string().default(""),
2225
+ type: z8.literal("table").default("table"),
1654
2226
  config: PackageViewConfigSchema.default({ filters: [], sorts: [] })
1655
2227
  });
1656
- var PackageBaseSchema = z6.object({
1657
- name: z6.string().min(1),
1658
- description: z6.string().default(""),
1659
- position: z6.number().int().optional(),
2228
+ var PackageBaseSchema = z8.object({
2229
+ name: z8.string().min(1),
2230
+ description: z8.string().default(""),
2231
+ position: z8.number().int().optional(),
1660
2232
  /**
1661
2233
  * Informational only — `reviewPolicy` is NOT settable via `bases.create`, so
1662
2234
  * install cannot apply it and warns when it differs from the target's default.
1663
2235
  */
1664
- reviewPolicy: z6.object({
1665
- kind: z6.literal("single"),
1666
- requiredApprovals: z6.number()
2236
+ reviewPolicy: z8.object({
2237
+ kind: z8.literal("single"),
2238
+ requiredApprovals: z8.number()
1667
2239
  }).optional(),
1668
- fields: z6.array(PackageBaseFieldSchema).default([]),
1669
- views: z6.array(PackageViewSchema).default([])
2240
+ fields: z8.array(PackageBaseFieldSchema).default([]),
2241
+ views: z8.array(PackageViewSchema).default([])
1670
2242
  });
1671
- var PackageRecordLineSchema = z6.object({
1672
- key: z6.string().min(1),
1673
- fields: z6.record(z6.string(), z6.unknown()).default({})
2243
+ var PackageRecordLineSchema = z8.object({
2244
+ key: z8.string().min(1),
2245
+ fields: z8.record(z8.string(), z8.unknown()).default({})
1674
2246
  });
1675
2247
 
1676
2248
  // src/package/apply.ts
1677
- import { createHash as createHash2 } from "crypto";
2249
+ import { createHash as createHash5 } from "crypto";
1678
2250
 
1679
2251
  // src/package/tree.ts
1680
2252
  import { extname } from "path";
@@ -2047,7 +2619,7 @@ var createFileTreeNode = async (client, node, parentNodeId, options, state) => {
2047
2619
  var UploadTargetUnsupportedError = class extends Error {
2048
2620
  };
2049
2621
  var uploadAsset = async (client, fileName, mimeType, bytes) => {
2050
- const contentHash = `sha256:${createHash2("sha256").update(bytes).digest("hex")}`;
2622
+ const contentHash = `sha256:${createHash5("sha256").update(bytes).digest("hex")}`;
2051
2623
  const requested = await client.assets.createUploadUrl({
2052
2624
  fileName,
2053
2625
  mimeType,
@@ -3338,15 +3910,15 @@ Nothing was created. Re-run with --rename to install the colliding items under s
3338
3910
  };
3339
3911
 
3340
3912
  // src/package/commands.ts
3341
- var reportProgress = (message) => {
3913
+ var reportProgress2 = (message) => {
3342
3914
  console.error(message);
3343
3915
  };
3344
3916
  var runInstall = async (client, repoUrl, options) => {
3345
- reportProgress(`Fetching ${repoUrl} \u2026`);
3917
+ reportProgress2(`Fetching ${repoUrl} \u2026`);
3346
3918
  const { source, files } = await fetchGithubPackageFiles(repoUrl, {
3347
3919
  githubToken: options.githubToken
3348
3920
  });
3349
- reportProgress(`Downloaded ${files.size} file(s) from ${source.owner}/${source.repo}.`);
3921
+ reportProgress2(`Downloaded ${files.size} file(s) from ${source.owner}/${source.repo}.`);
3350
3922
  const tree = readPackageTree(files);
3351
3923
  const [nodes, bases] = await Promise.all([client.nodes.list(), client.bases.list()]);
3352
3924
  const targetFolderSlug = options.intoFolder ?? tree.manifest.name;
@@ -3368,7 +3940,7 @@ Dry run \u2014 nothing was created.`;
3368
3940
  const result = await applyInstall(client, plan, {
3369
3941
  autoMerge: Boolean(options.autoMerge),
3370
3942
  submittedBy: `busabase-cli install (${tree.manifest.name})`,
3371
- onProgress: reportProgress
3943
+ onProgress: reportProgress2
3372
3944
  });
3373
3945
  if (options.json) return { installed: true, ...result };
3374
3946
  return renderInstallReport(plan.targetFolderSlug, result);
@@ -3412,7 +3984,7 @@ var runPublish = async (client, nodeSlugOrId, options) => {
3412
3984
  const source = findSourceNode(nodes, nodeSlugOrId);
3413
3985
  const warnings = [];
3414
3986
  const manifest = await resolveManifest(options.outDir, options.name, source.name, source.slug);
3415
- reportProgress(`Reading "${source.slug}" \u2026`);
3987
+ reportProgress2(`Reading "${source.slug}" \u2026`);
3416
3988
  const tree = await collectPackageTree(client, source, {
3417
3989
  manifest,
3418
3990
  warn: (message) => warnings.push(message),
@@ -3666,7 +4238,7 @@ async function uploadAsset2(config, opts) {
3666
4238
  const fileName = opts.fileName ?? basename(filePath);
3667
4239
  const mimeType = opts.mimeType ?? guessMimeType2(filePath);
3668
4240
  const context = opts.context ?? "record-field";
3669
- const contentHash = `sha256:${createHash3("sha256").update(file).digest("hex")}`;
4241
+ const contentHash = `sha256:${createHash6("sha256").update(file).digest("hex")}`;
3670
4242
  const requested = await rawRequest(config, "POST", "/api/v1/assets/upload-urls", {
3671
4243
  fileName,
3672
4244
  mimeType,
@@ -4496,6 +5068,60 @@ Examples:
4496
5068
  })
4497
5069
  )
4498
5070
  );
5071
+ addGlobalFlags(program.command("backup")).description("Back up the active space to a full-fidelity .bbdump archive").option("-o, --out-file <file>", "output archive path", "space.bbdump").option(
5072
+ "--no-history",
5073
+ "full-fidelity rows, but omit the history tables (commits, change requests, operations, comments, reviews, audit events)"
5074
+ ).option("--state-only", "superseded by `busabase-cli publish` \u2014 see the error text").addHelpText(
5075
+ "after",
5076
+ `
5077
+ A backup is full fidelity: raw rows with their original ids, every doc body, and
5078
+ every attachment's bytes, in one integrity-checked archive. Vault items and
5079
+ webhook signing secrets are never written to it.
5080
+
5081
+ Examples:
5082
+ busabase-cli backup # \u2192 ./space.bbdump
5083
+ busabase-cli backup -o ./backups/acme-2026-07-19.bbdump
5084
+ busabase-cli backup --no-history # smaller: current state only
5085
+
5086
+ To hand content to someone else rather than back it up, use \`busabase-cli publish\`,
5087
+ which writes a readable, diffable package directory you can push to GitHub.`
5088
+ ).action(
5089
+ runAction(
5090
+ state,
5091
+ (client, opts, config) => runBackup(client, {
5092
+ outFile: opts.outFile,
5093
+ includeHistory: opts.history !== false,
5094
+ stateOnly: Boolean(opts.stateOnly),
5095
+ json: config.output === "json",
5096
+ baseUrl: config.baseUrl,
5097
+ spaceId: config.spaceId,
5098
+ toolVersion: pkgVersion()
5099
+ })
5100
+ )
5101
+ );
5102
+ addGlobalFlags(program.command("restore")).description("Restore a .bbdump archive into an EMPTY space, preserving original ids").argument("<file>", "path to the .bbdump archive").option("--resume", "resume a previously interrupted restore (not yet implemented)").option("--into-folder <name>", "state-only archives only \u2014 see the error text").addHelpText(
5103
+ "after",
5104
+ `
5105
+ The archive's checksum is verified before a single row is written, and the target
5106
+ space must be EMPTY \u2014 a restore replays original ids, so it cannot merge into a
5107
+ space that already holds content.
5108
+
5109
+ Examples:
5110
+ busabase-cli restore ./space.bbdump
5111
+ busabase-cli restore ./backups/acme-2026-07-19.bbdump --space-id spc_123
5112
+
5113
+ To add content to a space that is already in use, use \`busabase-cli install\` instead.`
5114
+ ).action(
5115
+ runArgAction(
5116
+ state,
5117
+ (file, client, opts, config) => runRestore(client, {
5118
+ file,
5119
+ resume: Boolean(opts.resume),
5120
+ intoFolder: opts.intoFolder,
5121
+ json: config.output === "json"
5122
+ })
5123
+ )
5124
+ );
4499
5125
  addGlobalFlags(program.command("api")).description("Raw request to any /api/v1 endpoint").addOption(
4500
5126
  new Option("--method <get|post|put|delete>", "HTTP method").choices(["get", "post", "put", "delete"]).makeOptionMandatory()
4501
5127
  ).requiredOption("--path <p>", "path under /api/v1, e.g. /bases").option("--query <k=v...>", "query-string param, repeatable").option("--body-json <json>", "JSON request body").action(