busa-sdk 0.9.3

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.
package/dist/index.js ADDED
@@ -0,0 +1,2086 @@
1
+ import { createORPCClient } from '@orpc/client';
2
+ import { OpenAPILink } from '@orpc/openapi-client/fetch';
3
+ import { oc, eventIterator } from '@orpc/contract';
4
+ import { z } from 'zod';
5
+
6
+ // src/client.ts
7
+ var AttachmentMetadataSchema = z.record(z.string(), z.unknown());
8
+ z.object({
9
+ id: z.string(),
10
+ url: z.string(),
11
+ fileName: z.string(),
12
+ mimeType: z.string(),
13
+ size: z.number().int().nonnegative()
14
+ });
15
+ var RequestUploadUrlInputSchema = z.object({
16
+ fileName: z.string().min(1).max(255),
17
+ mimeType: z.string().min(1),
18
+ sizeBytes: z.number().int().positive(),
19
+ spaceId: z.string().optional(),
20
+ context: z.string().regex(/^[a-zA-Z0-9_/-]+$/).max(100).optional(),
21
+ /** Content fingerprint (e.g. "sha256:<hex>") for dedup; computed client-side. */
22
+ contentHash: z.string().max(80).optional()
23
+ });
24
+ var ConfirmUploadInputSchema = z.object({
25
+ storageKey: z.string().min(1),
26
+ fileName: z.string().min(1).max(255),
27
+ mimeType: z.string().min(1),
28
+ sizeBytes: z.number().int().positive(),
29
+ spaceId: z.string().optional(),
30
+ context: z.string().regex(/^[a-zA-Z0-9_/-]+$/).max(100).optional(),
31
+ metadata: AttachmentMetadataSchema.optional(),
32
+ /** Content fingerprint (e.g. "sha256:<hex>") persisted for dedup. */
33
+ contentHash: z.string().max(80).optional()
34
+ });
35
+ var RequestUploadUrlVOSchema = z.object({
36
+ uploadUrl: z.string(),
37
+ storageKey: z.string(),
38
+ publicUrl: z.string(),
39
+ expiresIn: z.number(),
40
+ /**
41
+ * True when an identical file (same contentHash, same scope) already exists.
42
+ * The client should SKIP the byte upload and the confirm step, and use
43
+ * `attachmentId`/`publicUrl` directly. `uploadUrl` is empty in this case.
44
+ */
45
+ duplicate: z.boolean().optional(),
46
+ /** Existing attachment id — present only when `duplicate` is true. */
47
+ attachmentId: z.string().optional(),
48
+ /** Host-specific logical asset id, when the app maps uploads into an Asset library. */
49
+ assetId: z.string().optional()
50
+ });
51
+ var ConfirmUploadVOSchema = z.object({
52
+ success: z.boolean(),
53
+ attachmentId: z.string(),
54
+ /** Host-specific logical asset id, when the app maps uploads into an Asset library. */
55
+ assetId: z.string().optional(),
56
+ storageKey: z.string(),
57
+ publicUrl: z.string()
58
+ });
59
+ var AssetVOSchema = z.object({
60
+ id: z.string(),
61
+ attachmentId: z.string(),
62
+ name: z.string(),
63
+ contentKind: z.enum(["text", "binary"]),
64
+ metadata: z.record(z.string(), z.unknown()).default({}),
65
+ fileName: z.string(),
66
+ mimeType: z.string(),
67
+ size: z.number().int().nonnegative(),
68
+ url: z.string(),
69
+ contentHash: z.string().nullable(),
70
+ /** How many places reference this asset (Base records + Doc bodies). */
71
+ usageCount: z.number().int().nonnegative(),
72
+ createdAt: z.string()
73
+ });
74
+ var AssetUsageVOSchema = z.object({
75
+ ownerType: z.enum(["drive", "skill", "base", "doc", "file_node"]),
76
+ nodeId: z.string(),
77
+ nodeName: z.string(),
78
+ nodeType: z.string(),
79
+ /** Node slug, so the UI can link to the owning Base/Doc (`/{type}/{slug}`). */
80
+ nodeSlug: z.string(),
81
+ /** Drive/Skill mounted path, or null when the usage is not path-based. */
82
+ path: z.string().nullable(),
83
+ /** Base record id, or null for whole-node usages (e.g. a Doc body). */
84
+ recordId: z.string().nullable(),
85
+ /** Attachment field slug, or null for whole-node usages. */
86
+ fieldSlug: z.string().nullable(),
87
+ /** Doc block id, or null when the usage is not block-based. */
88
+ blockId: z.string().nullable(),
89
+ createdAt: z.string()
90
+ });
91
+ var AssetDetailVOSchema = z.object({
92
+ asset: AssetVOSchema,
93
+ usages: z.array(AssetUsageVOSchema)
94
+ });
95
+
96
+ // ../../packages/busabase-contract/src/domains/assets/contract.ts
97
+ var UpdateAssetMetadataInputSchema = z.object({
98
+ assetId: z.string(),
99
+ metadata: z.record(z.string(), z.unknown()),
100
+ mode: z.enum(["merge", "replace"]).optional().default("merge")
101
+ });
102
+ var assetsContract = {
103
+ createUploadUrl: oc.route({
104
+ method: "POST",
105
+ path: "/assets/upload-urls",
106
+ tags: ["Assets"],
107
+ summary: "Request asset upload URL",
108
+ successDescription: "Presigned (or dev) upload URL plus the public URL and asset id when identical bytes are already in the library."
109
+ }).input(RequestUploadUrlInputSchema).output(RequestUploadUrlVOSchema),
110
+ confirm: oc.route({
111
+ method: "POST",
112
+ path: "/assets/confirmations",
113
+ tags: ["Assets"],
114
+ summary: "Confirm asset upload",
115
+ successDescription: "Recorded the file and ensured its Busabase Asset library entry."
116
+ }).input(ConfirmUploadInputSchema).output(ConfirmUploadVOSchema),
117
+ list: oc.route({
118
+ method: "GET",
119
+ path: "/assets",
120
+ tags: ["Assets"],
121
+ summary: "List assets",
122
+ successDescription: "Every asset in the space, with file metadata and usage counts."
123
+ }).output(z.array(AssetVOSchema)),
124
+ get: oc.route({
125
+ method: "GET",
126
+ path: "/assets/{assetId}",
127
+ tags: ["Assets"],
128
+ summary: "Get asset detail",
129
+ successDescription: "Asset metadata plus every place it is referenced (where-used)."
130
+ }).input(z.object({ assetId: z.string() })).output(AssetDetailVOSchema),
131
+ updateMetadata: oc.route({
132
+ method: "PATCH",
133
+ path: "/assets/{assetId}/metadata",
134
+ tags: ["Assets"],
135
+ summary: "Update asset metadata",
136
+ successDescription: "Updated AI-readable metadata for a file, such as summary, extracted text, tags, source URL, or schema-specific hints."
137
+ }).input(UpdateAssetMetadataInputSchema).output(AssetDetailVOSchema),
138
+ delete: oc.route({
139
+ method: "DELETE",
140
+ path: "/assets/{assetId}",
141
+ tags: ["Assets"],
142
+ summary: "Delete asset",
143
+ successDescription: "Removed the asset and, if no other row references its bytes, the stored object. Refused while the asset is still referenced (where-used)."
144
+ }).input(z.object({ assetId: z.string() })).output(z.object({ deleted: z.boolean() }))
145
+ };
146
+ var i18n = {
147
+ locales: ["en", "zh-CN", "zh-TW", "ja", "ko", "de", "fr", "es", "pt"]};
148
+ var LocaleSchema = z.enum(i18n.locales);
149
+
150
+ // ../../packages/openlib/i18n/i-string.ts
151
+ var iStringRecordSchema = z.partialRecord(LocaleSchema, z.string());
152
+ z.union([z.string(), iStringRecordSchema]).describe("i18n string");
153
+ var fieldNameSchema = z.union([
154
+ z.string().min(1),
155
+ iStringRecordSchema.refine(
156
+ (record) => Object.values(record).some((value) => value && value.trim().length > 0),
157
+ "field name must have at least one non-empty locale value"
158
+ )
159
+ ]);
160
+ var fieldTypeSchema = z.enum([
161
+ "text",
162
+ "longtext",
163
+ "markdown",
164
+ "html",
165
+ "attachment",
166
+ "relation",
167
+ "number",
168
+ "date",
169
+ "checkbox",
170
+ "select",
171
+ "multiselect",
172
+ "url",
173
+ "embed",
174
+ "email",
175
+ "phone",
176
+ "created_time",
177
+ "updated_time",
178
+ "created_by",
179
+ "updated_by",
180
+ "auto_number",
181
+ "ai_summary",
182
+ "ai_tags",
183
+ "code",
184
+ "json",
185
+ "yaml"
186
+ ]);
187
+ var fieldOptionsSchema = z.object({
188
+ ai: z.object({
189
+ model: z.string().optional(),
190
+ prompt: z.string().optional(),
191
+ reviewRequired: z.boolean().optional(),
192
+ sourceFieldIds: z.array(z.string()).optional()
193
+ }).optional(),
194
+ // Per-field config for `attachment` columns (all optional; logic enforces a
195
+ // 25MB ceiling regardless).
196
+ attachment: z.object({
197
+ maxFiles: z.number().int().positive().optional(),
198
+ allowedMimeTypes: z.array(z.string()).optional(),
199
+ maxFileSize: z.number().int().positive().optional()
200
+ }).optional(),
201
+ choices: z.array(
202
+ z.object({
203
+ color: z.string().optional(),
204
+ id: z.string(),
205
+ name: z.string()
206
+ })
207
+ ).optional(),
208
+ code: z.object({
209
+ language: z.string().optional()
210
+ }).optional(),
211
+ embed: z.object({
212
+ aspectRatio: z.enum(["16:9", "4:3", "1:1"]).optional(),
213
+ height: z.number().int().positive().max(1200).optional(),
214
+ providers: z.array(z.string()).optional()
215
+ }).optional(),
216
+ inverseFieldId: z.string().optional(),
217
+ multiple: z.boolean().optional(),
218
+ // Display formatting for `number` columns (Notion-style: one number type,
219
+ // a format option). `currency` renders via Intl.NumberFormat.
220
+ number: z.object({
221
+ format: z.enum(["plain", "currency"]).optional(),
222
+ currency: z.string().optional(),
223
+ locale: z.string().optional()
224
+ }).optional(),
225
+ targetBaseId: z.string().optional().describe("Relation target Base id (bse_\u2026). Or pass targetBaseSlug to name it by slug."),
226
+ targetBaseSlug: z.string().optional().describe(
227
+ "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."
228
+ )
229
+ }).default({});
230
+ var baseFieldSchema = z.object({
231
+ id: z.string(),
232
+ baseId: z.string(),
233
+ slug: z.string(),
234
+ name: fieldNameSchema,
235
+ type: fieldTypeSchema,
236
+ required: z.boolean(),
237
+ position: z.number(),
238
+ options: fieldOptionsSchema
239
+ });
240
+ var baseSchema = z.object({
241
+ id: z.string(),
242
+ nodeId: z.string(),
243
+ slug: z.string(),
244
+ name: z.string(),
245
+ description: z.string(),
246
+ reviewPolicy: z.object({
247
+ kind: z.literal("single"),
248
+ requiredApprovals: z.number()
249
+ }),
250
+ createdAt: z.string(),
251
+ fields: z.array(baseFieldSchema)
252
+ });
253
+ var createBaseInputSchema = z.object({
254
+ parentNodeId: z.string().optional(),
255
+ slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
256
+ name: z.string().min(1),
257
+ description: z.string().optional().default(""),
258
+ fields: z.array(
259
+ z.object({
260
+ slug: z.string().min(1),
261
+ name: fieldNameSchema,
262
+ type: fieldTypeSchema.default("text"),
263
+ required: z.boolean().default(false),
264
+ options: fieldOptionsSchema.optional().default({})
265
+ })
266
+ ).default([])
267
+ });
268
+ var createBaseFieldInputSchema = z.object({
269
+ name: fieldNameSchema,
270
+ // Field slugs are snake_case identifiers (e.g. `cover_image`, `publish_date`) — the
271
+ // seed and the inline-fields path on `POST /bases` already allow underscores, so the
272
+ // add-field endpoint must too. (Base/folder/view slugs stay kebab-case: they go in URLs.)
273
+ slug: z.string().min(1).regex(/^[a-z0-9_-]+$/),
274
+ type: fieldTypeSchema.default("text"),
275
+ required: z.boolean().optional().default(false),
276
+ options: fieldOptionsSchema.optional().default({})
277
+ });
278
+ var createFieldChangeRequestInputSchema = createBaseFieldInputSchema.extend({
279
+ message: z.string().optional().default("Add field"),
280
+ submittedBy: z.string().optional().default("local-editor")
281
+ });
282
+ var deleteFieldChangeRequestInputSchema = z.object({
283
+ fieldId: z.string().min(1),
284
+ message: z.string().optional(),
285
+ submittedBy: z.string().optional().default("local-editor")
286
+ });
287
+ var updateFieldChangeRequestInputSchema = z.object({
288
+ fieldId: z.string().min(1),
289
+ patch: z.object({
290
+ name: fieldNameSchema.optional(),
291
+ required: z.boolean().optional(),
292
+ options: fieldOptionsSchema.optional()
293
+ }),
294
+ message: z.string().optional(),
295
+ submittedBy: z.string().optional().default("local-editor")
296
+ });
297
+ var previewFieldConversionInputSchema = z.object({
298
+ fieldId: z.string().min(1),
299
+ newType: fieldTypeSchema
300
+ });
301
+ var previewFieldConversionOutputSchema = z.object({
302
+ totalCount: z.number(),
303
+ convertibleCount: z.number(),
304
+ nullCount: z.number(),
305
+ conflicts: z.array(z.object({ recordId: z.string(), currentValue: z.unknown() }))
306
+ });
307
+ var convertFieldChangeRequestInputSchema = z.object({
308
+ fieldId: z.string().min(1),
309
+ newType: fieldTypeSchema,
310
+ selectChoiceMode: z.enum(["auto_create", "null_on_missing"]).default("null_on_missing"),
311
+ message: z.string().optional(),
312
+ submittedBy: z.string().optional().default("local-editor")
313
+ });
314
+ var reorderFieldsChangeRequestInputSchema = z.object({
315
+ fieldIds: z.array(z.string()).min(1),
316
+ message: z.string().optional(),
317
+ submittedBy: z.string().optional().default("local-editor")
318
+ });
319
+ var archiveBaseInputSchema = z.object({
320
+ message: z.string().optional(),
321
+ submittedBy: z.string().optional().default("local-editor")
322
+ });
323
+ var restoreBaseInputSchema = z.object({
324
+ message: z.string().optional(),
325
+ submittedBy: z.string().optional().default("local-editor")
326
+ });
327
+ var restoreFieldChangeRequestInputSchema = z.object({
328
+ fieldId: z.string().min(1),
329
+ message: z.string().optional(),
330
+ submittedBy: z.string().optional().default("local-editor")
331
+ });
332
+
333
+ // ../../packages/busabase-contract/src/domains/base/definition.ts
334
+ var baseNodeType = {
335
+ type: "base",
336
+ label: "Base",
337
+ icon: "table",
338
+ capabilities: { hasDetail: true, creatable: true },
339
+ operations: [
340
+ {
341
+ kind: "record_create",
342
+ label: "Create",
343
+ tone: "border-emerald-200 bg-emerald-50 text-emerald-800"
344
+ },
345
+ { kind: "record_update", label: "Update", tone: "border-sky-200 bg-sky-50 text-sky-800" },
346
+ { kind: "record_delete", label: "Delete", tone: "border-red-200 bg-red-50 text-red-800" },
347
+ {
348
+ kind: "record_variant",
349
+ label: "Variant",
350
+ tone: "border-violet-200 bg-violet-50 text-violet-800"
351
+ },
352
+ {
353
+ kind: "view_create",
354
+ label: "Create view",
355
+ tone: "border-indigo-200 bg-indigo-50 text-indigo-800"
356
+ },
357
+ { kind: "view_update", label: "Update view", tone: "border-blue-200 bg-blue-50 text-blue-800" },
358
+ { kind: "view_delete", label: "Delete view", tone: "border-rose-200 bg-rose-50 text-rose-800" },
359
+ {
360
+ kind: "view_restore",
361
+ label: "Restore view",
362
+ tone: "border-emerald-200 bg-emerald-50 text-emerald-800"
363
+ },
364
+ {
365
+ kind: "base_add_field",
366
+ label: "Add field",
367
+ tone: "border-emerald-200 bg-emerald-50 text-emerald-800"
368
+ },
369
+ {
370
+ kind: "base_delete_field",
371
+ label: "Delete field",
372
+ tone: "border-red-200 bg-red-50 text-red-800"
373
+ },
374
+ {
375
+ kind: "base_update_field",
376
+ label: "Update field",
377
+ tone: "border-sky-200 bg-sky-50 text-sky-800"
378
+ },
379
+ {
380
+ kind: "base_convert_field",
381
+ label: "Convert field",
382
+ tone: "border-violet-200 bg-violet-50 text-violet-800"
383
+ },
384
+ {
385
+ kind: "base_reorder_fields",
386
+ label: "Reorder fields",
387
+ tone: "border-indigo-200 bg-indigo-50 text-indigo-800"
388
+ },
389
+ {
390
+ kind: "base_restore_field",
391
+ label: "Restore field",
392
+ tone: "border-teal-200 bg-teal-50 text-teal-800"
393
+ },
394
+ {
395
+ kind: "base_archive",
396
+ label: "Archive base",
397
+ tone: "border-orange-200 bg-orange-50 text-orange-800"
398
+ },
399
+ {
400
+ kind: "base_restore",
401
+ label: "Restore base",
402
+ tone: "border-teal-200 bg-teal-50 text-teal-800"
403
+ },
404
+ {
405
+ kind: "record_restore",
406
+ label: "Restore record",
407
+ tone: "border-teal-200 bg-teal-50 text-teal-800"
408
+ }
409
+ ]
410
+ };
411
+
412
+ // ../../packages/busabase-contract/src/domains/doc/definition.ts
413
+ var docNodeType = {
414
+ type: "doc",
415
+ label: "Doc",
416
+ icon: "file-text",
417
+ capabilities: { hasDetail: true, creatable: true },
418
+ operations: [
419
+ {
420
+ kind: "doc_update",
421
+ label: "Update doc",
422
+ tone: "border-blue-200 bg-blue-50 text-blue-800"
423
+ }
424
+ ]
425
+ };
426
+
427
+ // ../../packages/busabase-contract/src/domains/filetree/definition.ts
428
+ var fileTreeOperations = (type) => [
429
+ {
430
+ kind: `${type}_file_create`,
431
+ label: "Create file",
432
+ tone: "border-emerald-200 bg-emerald-50 text-emerald-800"
433
+ },
434
+ {
435
+ kind: `${type}_file_update`,
436
+ label: "Update file",
437
+ tone: "border-blue-200 bg-blue-50 text-blue-800"
438
+ },
439
+ {
440
+ kind: `${type}_file_delete`,
441
+ label: "Delete file",
442
+ tone: "border-rose-200 bg-rose-50 text-rose-800"
443
+ },
444
+ {
445
+ kind: `${type}_metadata_update`,
446
+ label: `Update ${type}`,
447
+ tone: "border-violet-200 bg-violet-50 text-violet-800"
448
+ }
449
+ ];
450
+ var makeFileTreeNodeType = (config) => ({
451
+ type: config.type,
452
+ label: config.label,
453
+ icon: config.icon,
454
+ capabilities: { hasDetail: true, creatable: true },
455
+ operations: fileTreeOperations(config.type)
456
+ });
457
+
458
+ // ../../packages/busabase-contract/src/domains/drive/definition.ts
459
+ var driveNodeType = makeFileTreeNodeType({
460
+ type: "drive",
461
+ label: "Drive",
462
+ icon: "hard-drive"});
463
+
464
+ // ../../packages/busabase-contract/src/domains/file-node/definition.ts
465
+ var fileNodeType = {
466
+ type: "file",
467
+ label: "File",
468
+ icon: "file",
469
+ capabilities: { hasDetail: true, creatable: true },
470
+ operations: []
471
+ };
472
+
473
+ // ../../packages/busabase-contract/src/domains/folder/definition.ts
474
+ var folderNodeType = {
475
+ type: "folder",
476
+ label: "Folder",
477
+ icon: "folder",
478
+ capabilities: { container: true, creatable: true, hasDetail: true },
479
+ operations: []
480
+ };
481
+
482
+ // ../../packages/busabase-contract/src/domains/skill/definition.ts
483
+ var skillNodeType = makeFileTreeNodeType({
484
+ type: "skill",
485
+ label: "Skill",
486
+ icon: "sparkles"});
487
+
488
+ // ../../packages/busabase-contract/src/domains/registry.ts
489
+ var GENERIC_NODE_OPERATIONS = [
490
+ {
491
+ kind: "node_create",
492
+ label: "Create node",
493
+ tone: "border-emerald-200 bg-emerald-50 text-emerald-800"
494
+ },
495
+ { kind: "node_rename", label: "Rename node", tone: "border-sky-200 bg-sky-50 text-sky-800" },
496
+ { kind: "node_delete", label: "Delete node", tone: "border-red-200 bg-red-50 text-red-800" },
497
+ {
498
+ kind: "node_restore",
499
+ label: "Restore node",
500
+ tone: "border-emerald-200 bg-emerald-50 text-emerald-800"
501
+ },
502
+ { kind: "node_move", label: "Move node", tone: "border-indigo-200 bg-indigo-50 text-indigo-800" }
503
+ ];
504
+ var registry = /* @__PURE__ */ new Map();
505
+ function registerNodeType(definition) {
506
+ registry.set(definition.type, definition);
507
+ }
508
+ var BUILTIN_NODE_TYPES = [
509
+ folderNodeType,
510
+ baseNodeType,
511
+ skillNodeType,
512
+ driveNodeType,
513
+ fileNodeType,
514
+ docNodeType
515
+ ];
516
+ for (const definition of BUILTIN_NODE_TYPES) {
517
+ registerNodeType(definition);
518
+ }
519
+ var NODE_TYPES = BUILTIN_NODE_TYPES.map((definition) => definition.type);
520
+ var CREATABLE_NODE_TYPES = BUILTIN_NODE_TYPES.filter(
521
+ (definition) => Boolean(definition.capabilities.creatable)
522
+ ).map((definition) => definition.type);
523
+ var ALL_OPERATIONS = [
524
+ ...GENERIC_NODE_OPERATIONS,
525
+ ...BUILTIN_NODE_TYPES.flatMap(
526
+ (definition) => definition.operations
527
+ )
528
+ ];
529
+ var OPERATION_KINDS = ALL_OPERATIONS.map((operation) => operation.kind);
530
+ GENERIC_NODE_OPERATIONS.map(
531
+ (operation) => operation.kind
532
+ );
533
+ Object.fromEntries(
534
+ ALL_OPERATIONS.map((operation) => [
535
+ operation.kind,
536
+ { label: operation.label, tone: operation.tone }
537
+ ])
538
+ );
539
+
540
+ // ../../packages/busabase-contract/src/contract/schemas.ts
541
+ var nodeSchema = z.lazy(
542
+ () => z.object({
543
+ id: z.string(),
544
+ parentId: z.string().nullable(),
545
+ type: z.enum(NODE_TYPES),
546
+ slug: z.string(),
547
+ name: z.string(),
548
+ description: z.string(),
549
+ metadata: z.object({
550
+ entryFile: z.string().optional(),
551
+ visibility: z.enum(["private", "workspace", "public"]).optional(),
552
+ version: z.string().optional(),
553
+ assetId: z.string().optional()
554
+ }).default({}),
555
+ position: z.number(),
556
+ createdAt: z.string(),
557
+ updatedAt: z.string(),
558
+ baseId: z.string().nullable(),
559
+ children: z.array(nodeSchema)
560
+ })
561
+ );
562
+ var userRefSchema = z.object({
563
+ id: z.string(),
564
+ name: z.string().nullable(),
565
+ email: z.string().nullable(),
566
+ image: z.string().nullable(),
567
+ role: z.string().nullable().optional()
568
+ });
569
+ var commitSchema = z.object({
570
+ id: z.string(),
571
+ baseId: z.string().nullable(),
572
+ targetType: z.enum(["base", "node"]),
573
+ nodeId: z.string().nullable(),
574
+ operationId: z.string().nullable(),
575
+ parentCommitId: z.string().nullable(),
576
+ fields: z.record(z.string(), z.unknown()),
577
+ operation: z.enum(OPERATION_KINDS),
578
+ message: z.string(),
579
+ author: z.string(),
580
+ authorUser: userRefSchema.nullable().optional().default(null),
581
+ createdAt: z.string()
582
+ });
583
+ var operationSchema = z.object({
584
+ id: z.string(),
585
+ changeRequestId: z.string(),
586
+ baseId: z.string().nullable(),
587
+ targetType: z.enum(["base", "node"]),
588
+ nodeId: z.string().nullable(),
589
+ operation: z.enum(OPERATION_KINDS),
590
+ status: z.enum(["pending", "merged", "archived", "failed"]),
591
+ targetRecordId: z.string().nullable(),
592
+ targetViewId: z.string().nullable(),
593
+ filePath: z.string().nullable(),
594
+ sourceRecordId: z.string().nullable(),
595
+ sourceCommitId: z.string().nullable(),
596
+ baseCommitId: z.string().nullable(),
597
+ headCommitId: z.string(),
598
+ deleteMode: z.enum(["archive"]),
599
+ mergedRecordId: z.string().nullable(),
600
+ mergedViewId: z.string().nullable(),
601
+ position: z.number(),
602
+ createdAt: z.string(),
603
+ updatedAt: z.string(),
604
+ headCommit: commitSchema,
605
+ // Resolved canonical "before" values for the operation's target, so the UI can
606
+ // render a true before → after field diff. Null for creations (no prior state)
607
+ // and for kinds whose prior state isn't a field map (e.g. skill files, whose
608
+ // previous content lives in storage). Records resolve from the base commit;
609
+ // views resolve from the current view row ({ name, description, config }).
610
+ baseFields: z.record(z.string(), z.unknown()).nullable()
611
+ });
612
+ var reviewSchema = z.object({
613
+ id: z.string(),
614
+ changeRequestId: z.string(),
615
+ reviewerId: z.string(),
616
+ reviewer: userRefSchema.nullable().optional().default(null),
617
+ verdict: z.enum(["approved", "rejected"]),
618
+ reason: z.string().nullable(),
619
+ visibleOperationHeads: z.record(z.string(), z.string()),
620
+ createdAt: z.string()
621
+ });
622
+ var commentSubjectTypeSchema = z.enum(["record", "change_request", "operation", "commit"]);
623
+ var commentSchema = z.object({
624
+ id: z.string(),
625
+ subjectType: commentSubjectTypeSchema,
626
+ subjectId: z.string(),
627
+ recordId: z.string().nullable(),
628
+ changeRequestId: z.string().nullable(),
629
+ operationId: z.string().nullable(),
630
+ commitId: z.string().nullable(),
631
+ authorId: z.string(),
632
+ author: userRefSchema.nullable().optional().default(null),
633
+ body: z.string(),
634
+ mentionsAi: z.boolean(),
635
+ createdAt: z.string(),
636
+ updatedAt: z.string()
637
+ });
638
+ var changeRequestStatusSchema = z.enum([
639
+ "in_review",
640
+ "changes_requested",
641
+ "approved",
642
+ "rejected",
643
+ "merged",
644
+ "abandoned",
645
+ "conflict"
646
+ ]);
647
+ var changeRequestSchema = z.object({
648
+ id: z.string(),
649
+ baseId: z.string().nullable(),
650
+ targetType: z.enum(["base", "node"]),
651
+ nodeId: z.string().nullable(),
652
+ status: changeRequestStatusSchema,
653
+ submittedBy: z.string(),
654
+ submittedByUser: userRefSchema.nullable().optional().default(null),
655
+ sourceMeta: z.record(z.string(), z.unknown()),
656
+ reviewPolicySnapshot: z.record(z.string(), z.unknown()),
657
+ mergeSummary: z.record(z.string(), z.unknown()),
658
+ rejectedReason: z.string().nullable(),
659
+ reviewedAt: z.string().nullable(),
660
+ mergedAt: z.string().nullable(),
661
+ createdAt: z.string(),
662
+ updatedAt: z.string(),
663
+ base: baseSchema.nullable(),
664
+ node: nodeSchema.nullable(),
665
+ operations: z.array(operationSchema),
666
+ primaryOperation: operationSchema.nullable(),
667
+ operationCount: z.number(),
668
+ reviews: z.array(reviewSchema)
669
+ });
670
+ var agentTaskSchema = z.object({
671
+ changeRequest: changeRequestSchema,
672
+ trigger: z.enum(["changes_requested", "ai_mention"]),
673
+ reviewReason: z.string().nullable(),
674
+ aiComments: z.array(commentSchema)
675
+ });
676
+ var searchResultSchema = z.object({
677
+ id: z.string(),
678
+ kind: z.enum(["record", "change_request", "base", "file"]),
679
+ title: z.string(),
680
+ body: z.string(),
681
+ eyebrow: z.string(),
682
+ href: z.string(),
683
+ updatedAt: z.string().nullable()
684
+ });
685
+ var searchResponseSchema = z.object({
686
+ query: z.string(),
687
+ limit: z.number(),
688
+ offset: z.number(),
689
+ hasMore: z.boolean(),
690
+ results: z.array(searchResultSchema)
691
+ });
692
+ var liveEventSchema = z.object({
693
+ kind: z.enum([
694
+ "change_request.created",
695
+ "change_request.updated",
696
+ "change_request.deleted",
697
+ "change_request.reviewed",
698
+ "change_request.merged"
699
+ ]),
700
+ spaceId: z.string(),
701
+ actorId: z.string(),
702
+ changeRequestId: z.string(),
703
+ baseId: z.string().nullable(),
704
+ nodeIds: z.array(z.string()),
705
+ recordIds: z.array(z.string()),
706
+ viewIds: z.array(z.string()),
707
+ operationCount: z.number()
708
+ });
709
+ var auditActionSchema = z.enum([
710
+ "record.viewed",
711
+ "change_request.created",
712
+ "change_request.updated",
713
+ "change_request.deleted",
714
+ "change_request.reviewed",
715
+ "change_request.merged",
716
+ // Direct (non-change-request) mutations — recorded so the audit trail stays
717
+ // complete even for operations that bypass the propose → review → merge flow
718
+ // (container bootstrap, direct edits, library/asset deletes, trash purge).
719
+ "base.created",
720
+ "field.created",
721
+ "doc.created",
722
+ "doc.updated",
723
+ "file.created",
724
+ "skill.created",
725
+ "drive.created",
726
+ "asset.deleted",
727
+ "asset.metadata_updated",
728
+ "node.purged"
729
+ ]);
730
+ var auditEventSchema = z.object({
731
+ id: z.string(),
732
+ action: auditActionSchema,
733
+ actorId: z.string(),
734
+ actor: userRefSchema.nullable().optional().default(null),
735
+ baseId: z.string().nullable(),
736
+ recordId: z.string().nullable(),
737
+ changeRequestId: z.string().nullable(),
738
+ operationId: z.string().nullable(),
739
+ commitId: z.string().nullable(),
740
+ metadata: z.record(z.string(), z.unknown()),
741
+ createdAt: z.string()
742
+ });
743
+ var createAuditEventInputSchema = z.object({
744
+ action: auditActionSchema,
745
+ actorId: z.string().optional().default("local-viewer"),
746
+ baseId: z.string().optional().nullable(),
747
+ recordId: z.string().optional().nullable(),
748
+ changeRequestId: z.string().optional().nullable(),
749
+ operationId: z.string().optional().nullable(),
750
+ commitId: z.string().optional().nullable(),
751
+ metadata: z.record(z.string(), z.unknown()).optional().default({})
752
+ });
753
+ var nodeOperationInputSchema = z.discriminatedUnion("kind", [
754
+ z.object({
755
+ kind: z.literal("create"),
756
+ ref: z.string().min(1).optional().describe(
757
+ '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.'
758
+ ),
759
+ parentNodeId: z.string().optional(),
760
+ parentNodeRef: z.string().min(1).optional().describe(
761
+ "Parent this node under a node an EARLIER operation in the same change request created (matched by its ref). Mutually exclusive with parentNodeId."
762
+ ),
763
+ nodeType: z.enum(CREATABLE_NODE_TYPES),
764
+ slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
765
+ name: z.string().min(1),
766
+ description: z.string().optional().default(""),
767
+ metadata: z.record(z.string(), z.unknown()).optional().default({}),
768
+ // Base fields (nodeType "base" only); a base needs at least one field.
769
+ fields: z.array(
770
+ z.object({
771
+ slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
772
+ name: fieldNameSchema,
773
+ type: fieldTypeSchema.default("text"),
774
+ required: z.boolean().optional().default(false),
775
+ options: fieldOptionsSchema.optional().default({})
776
+ })
777
+ ).optional()
778
+ }),
779
+ z.object({
780
+ kind: z.literal("rename"),
781
+ nodeId: z.string(),
782
+ slug: z.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
783
+ name: z.string().min(1).optional(),
784
+ description: z.string().optional()
785
+ }),
786
+ z.object({
787
+ kind: z.literal("delete"),
788
+ nodeId: z.string()
789
+ }),
790
+ z.object({
791
+ kind: z.literal("restore"),
792
+ nodeId: z.string()
793
+ }),
794
+ z.object({
795
+ kind: z.literal("move"),
796
+ nodeId: z.string(),
797
+ // Exactly one of parentNodeId / parentNodeRef (a ref created earlier in this CR).
798
+ parentNodeId: z.string().optional(),
799
+ parentNodeRef: z.string().min(1).optional(),
800
+ position: z.number().int().optional()
801
+ })
802
+ ]);
803
+ var createNodeChangeRequestInputSchema = z.object({
804
+ message: z.string().optional().default("Update node tree").describe(
805
+ '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".'
806
+ ),
807
+ submittedBy: z.string().optional().default("local-producer"),
808
+ autoMerge: z.boolean().optional().default(false).describe(
809
+ "Whether to approve and merge this structural node change immediately. Defaults to false so node Change Request creation is review-first unless explicitly requested."
810
+ ),
811
+ operations: z.array(nodeOperationInputSchema).min(1)
812
+ });
813
+ var createDeleteChangeRequestInputSchema = z.object({
814
+ message: z.string().optional().default("Delete record").describe(
815
+ 'Explanation shown to the human reviewer. Say what is being removed and why, e.g. "Archive duplicate contact \u2014 merged into Acme Corp".'
816
+ ),
817
+ submittedBy: z.string().optional().default("local-producer"),
818
+ // Only "archive" is supported — hard delete after retention was never
819
+ // implemented, so the API no longer accepts it (breaking change).
820
+ deleteMode: z.enum(["archive"]).optional().default("archive")
821
+ });
822
+ var reviseOperationInputSchema = z.object({
823
+ fields: z.record(z.string(), z.unknown()).describe(
824
+ "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."
825
+ ),
826
+ message: z.string().optional().default("Revise operation").describe(
827
+ '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".'
828
+ ),
829
+ author: z.string().optional().default("local-producer"),
830
+ baseCommitId: z.string().optional()
831
+ });
832
+ var reviewChangeRequestInputSchema = z.object({
833
+ verdict: z.enum(["approved", "rejected"]),
834
+ reason: z.string().optional()
835
+ });
836
+ var commentSubjectInputSchema = z.object({
837
+ subjectType: commentSubjectTypeSchema,
838
+ subjectId: z.string().min(1)
839
+ });
840
+ var createCommentInputSchema = commentSubjectInputSchema.extend({
841
+ authorId: z.string().optional().default("local-admin"),
842
+ body: z.string().trim().min(1),
843
+ mentionsAi: z.boolean().optional().default(false)
844
+ });
845
+ var listInputSchema = z.object({
846
+ limit: z.coerce.number().int().min(1).max(100).optional().default(50)
847
+ }).optional().default({ limit: 50 });
848
+ var listChangeRequestsPagedInputSchema = z.object({
849
+ limit: z.coerce.number().int().min(1).max(100).optional().default(50),
850
+ /** Opaque base64 cursor (`createdAt|id`) for keyset pagination. */
851
+ cursor: z.string().optional(),
852
+ status: z.array(changeRequestStatusSchema).optional(),
853
+ mine: z.boolean().optional()
854
+ }).optional().default({ limit: 50 });
855
+ var listChangeRequestsResponseSchema = z.object({
856
+ changeRequests: z.array(changeRequestSchema),
857
+ nextCursor: z.string().nullable()
858
+ });
859
+ var changeRequestCountsSchema = z.object({
860
+ review: z.number().int().nonnegative(),
861
+ changes: z.number().int().nonnegative(),
862
+ created: z.number().int().nonnegative(),
863
+ approved: z.number().int().nonnegative(),
864
+ merged: z.number().int().nonnegative(),
865
+ rejected: z.number().int().nonnegative()
866
+ });
867
+ var searchInputSchema = z.object({
868
+ query: z.string().default(""),
869
+ limit: z.coerce.number().int().min(1).max(100).optional().default(20),
870
+ offset: z.coerce.number().int().min(0).optional().default(0)
871
+ });
872
+ var authSpaceSchema = z.object({
873
+ id: z.string(),
874
+ name: z.string(),
875
+ slug: z.string().nullable(),
876
+ plan: z.string().nullable()
877
+ });
878
+ var authUserSchema = z.object({
879
+ id: z.string(),
880
+ name: z.string(),
881
+ email: z.string().nullable(),
882
+ image: z.string().nullable()
883
+ });
884
+ var authMemberSchema = z.object({
885
+ userId: z.string(),
886
+ spaceId: z.string(),
887
+ role: z.string()
888
+ });
889
+ var authInfoSchema = z.object({
890
+ /** The space this request is scoped to (explicit `x-busabase-space` header, else the default). */
891
+ space: authSpaceSchema,
892
+ user: authUserSchema,
893
+ member: authMemberSchema,
894
+ /**
895
+ * Every space the authenticated user belongs to. An API key is user-scoped, not
896
+ * space-scoped — when this has more than one entry, callers should confirm the
897
+ * intended space and target it explicitly via the `x-busabase-space` header.
898
+ */
899
+ spaces: z.array(authSpaceSchema)
900
+ });
901
+ var viewFilterOperatorSchema = z.enum([
902
+ "contains",
903
+ "equals",
904
+ "not_empty",
905
+ "is_empty",
906
+ "is_true",
907
+ "is_false"
908
+ ]);
909
+ var viewFilterSchema = z.object({
910
+ fieldSlug: z.string(),
911
+ // Stable field identity — survives slug reuse; populated on merge.
912
+ fieldId: z.string().optional(),
913
+ operator: viewFilterOperatorSchema,
914
+ value: z.unknown().optional()
915
+ });
916
+ var viewSortSchema = z.object({
917
+ direction: z.enum(["asc", "desc"]),
918
+ fieldSlug: z.string(),
919
+ fieldId: z.string().optional()
920
+ });
921
+ var viewConfigSchema = z.object({
922
+ filters: z.array(viewFilterSchema).default([]),
923
+ sorts: z.array(viewSortSchema).default([]),
924
+ visibleFieldSlugs: z.array(z.string()).nullable().optional()
925
+ });
926
+ var viewSchema = z.object({
927
+ id: z.string(),
928
+ baseId: z.string(),
929
+ slug: z.string(),
930
+ name: z.string(),
931
+ description: z.string(),
932
+ type: z.literal("table"),
933
+ config: viewConfigSchema,
934
+ status: z.enum(["active", "archived"]),
935
+ createdBy: z.string(),
936
+ createdByUser: userRefSchema.nullable().optional().default(null),
937
+ archivedAt: z.string().nullable(),
938
+ createdAt: z.string(),
939
+ updatedAt: z.string()
940
+ });
941
+ var createViewInputSchema = z.object({
942
+ config: viewConfigSchema.optional().default({ filters: [], sorts: [] }),
943
+ description: z.string().optional().default(""),
944
+ message: z.string().optional().default("Create view"),
945
+ name: z.string().min(1),
946
+ slug: z.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
947
+ submittedBy: z.string().optional().default("local-producer")
948
+ });
949
+ var updateViewInputSchema = z.object({
950
+ config: viewConfigSchema.optional(),
951
+ description: z.string().optional(),
952
+ message: z.string().optional().default("Update view"),
953
+ name: z.string().min(1).optional(),
954
+ submittedBy: z.string().optional().default("local-producer")
955
+ });
956
+ var deleteViewInputSchema = z.object({
957
+ message: z.string().optional().default("Delete view"),
958
+ submittedBy: z.string().optional().default("local-producer")
959
+ });
960
+ var restoreViewInputSchema = z.object({
961
+ message: z.string().optional().default("Restore view"),
962
+ submittedBy: z.string().optional().default("local-producer")
963
+ });
964
+
965
+ // ../../packages/busabase-contract/src/domains/base/contract/record-schemas.ts
966
+ var recordSchema = z.object({
967
+ id: z.string(),
968
+ baseId: z.string(),
969
+ headCommitId: z.string(),
970
+ parentRecordId: z.string().nullable(),
971
+ parentCommitId: z.string().nullable(),
972
+ status: z.enum(["active", "archived"]),
973
+ createdBy: z.string(),
974
+ createdByUser: userRefSchema.nullable().optional().default(null),
975
+ archivedAt: z.string().nullable(),
976
+ createdAt: z.string(),
977
+ updatedAt: z.string(),
978
+ base: baseSchema,
979
+ headCommit: commitSchema
980
+ });
981
+ var listRecordsFilterSchema = z.object({
982
+ fieldSlug: z.string(),
983
+ fieldType: z.string().optional(),
984
+ operator: viewFilterOperatorSchema,
985
+ value: z.unknown().optional()
986
+ });
987
+ var listRecordsSortSchema = z.object({
988
+ fieldSlug: z.string(),
989
+ fieldType: z.string().optional(),
990
+ direction: z.enum(["asc", "desc"]).optional().default("asc")
991
+ });
992
+ var listRecordsInputSchema = z.object({
993
+ limit: z.coerce.number().int().min(1).max(100).optional().default(50),
994
+ baseId: z.string().optional(),
995
+ /** Opaque base64 cursor for keyset pagination (createdAt-keyed, or sort-keyed when `sort` is set). */
996
+ cursor: z.string().optional(),
997
+ /** View filters for server-side push-down (superset; client still narrows). */
998
+ filters: z.array(listRecordsFilterSchema).optional(),
999
+ /** View sort for server-side push-down (number/date fields only). */
1000
+ sort: listRecordsSortSchema.optional()
1001
+ }).optional().default({ limit: 50 });
1002
+ var listRecordsResponseSchema = z.object({
1003
+ records: z.array(recordSchema),
1004
+ nextCursor: z.string().nullable()
1005
+ });
1006
+ var countRecordsInputSchema = z.object({
1007
+ baseId: z.string().optional()
1008
+ }).optional().default({});
1009
+ var countRecordsResponseSchema = z.object({
1010
+ /** Total active records in the space (optionally scoped to a base). */
1011
+ total: z.number().int().nonnegative()
1012
+ });
1013
+ var createChangeRequestInputSchema = z.object({
1014
+ fields: z.record(z.string(), z.unknown()).describe(
1015
+ "Record field values keyed by field slug. The base's PRIMARY field (its first field) becomes the record's display name and the change request title everywhere \u2014 always give it a short, human-readable value, never an id or placeholder."
1016
+ ),
1017
+ message: z.string().optional().default("Initial change request").describe(
1018
+ 'Explanation shown to the human reviewer. Write a conventional-commit style subject \u2014 imperative verb + what + why, e.g. "Add Acme Corp \u2014 qualified lead from the June webinar".'
1019
+ ),
1020
+ submittedBy: z.string().optional().default("local-producer")
1021
+ });
1022
+ var createBulkChangeRequestInputSchema = z.object({
1023
+ records: z.array(z.record(z.string(), z.unknown())).min(1).max(1e3).describe(
1024
+ "Field-value maps, one per record to create, each keyed by field slug. All records are proposed as a SINGLE change request (one review, one merge) \u2014 use this to import/seed many rows at once instead of one change request per record. Capped at 1000; for very large loads prefer a dedicated import job. Always give each record's PRIMARY field a short human-readable value."
1025
+ ),
1026
+ message: z.string().optional().default("Bulk create records").describe(
1027
+ 'Explanation shown to the human reviewer for the whole batch \u2014 e.g. "Import 240 June webinar leads".'
1028
+ ),
1029
+ submittedBy: z.string().optional().default("local-producer")
1030
+ });
1031
+ var recordFieldFilterInputSchema = z.object({
1032
+ baseId: z.string().optional(),
1033
+ fieldSlug: z.string().min(1),
1034
+ valueText: z.string().min(1),
1035
+ limit: z.coerce.number().int().min(1).max(100).optional().default(50)
1036
+ });
1037
+ var restoreRecordInputSchema = z.object({
1038
+ message: z.string().optional(),
1039
+ submittedBy: z.string().optional().default("local-editor")
1040
+ });
1041
+ var recordLinkSchema = z.object({
1042
+ id: z.string(),
1043
+ baseId: z.string(),
1044
+ fieldId: z.string(),
1045
+ fieldSlug: z.string(),
1046
+ sourceRecordId: z.string(),
1047
+ targetBaseId: z.string(),
1048
+ targetRecordId: z.string(),
1049
+ commitId: z.string(),
1050
+ position: z.number(),
1051
+ createdAt: z.string(),
1052
+ updatedAt: z.string()
1053
+ });
1054
+ var baseContract = {
1055
+ list: oc.route({
1056
+ method: "GET",
1057
+ path: "/bases",
1058
+ tags: ["Bases"],
1059
+ summary: "List Bases",
1060
+ successDescription: "Flat list of developer-facing Bases."
1061
+ }).output(z.array(baseSchema)),
1062
+ listArchived: oc.route({
1063
+ method: "GET",
1064
+ path: "/bases/archived",
1065
+ tags: ["Bases"],
1066
+ summary: "List archived bases",
1067
+ successDescription: "Bases that have been archived."
1068
+ }).output(z.array(baseSchema)),
1069
+ get: oc.route({
1070
+ method: "GET",
1071
+ path: "/bases/{baseId}",
1072
+ tags: ["Bases"],
1073
+ summary: "Get Base",
1074
+ successDescription: "Single Base by id or slug."
1075
+ }).input(z.object({ baseId: z.string() })).output(baseSchema.nullable()),
1076
+ listDeletedFields: oc.route({
1077
+ method: "GET",
1078
+ path: "/bases/{baseId}/fields/deleted",
1079
+ tags: ["Bases"],
1080
+ summary: "List deleted fields",
1081
+ successDescription: "Fields that have been soft-deleted from a Base."
1082
+ }).input(z.object({ baseId: z.string() })).output(z.array(baseFieldSchema)),
1083
+ listViews: oc.route({
1084
+ method: "GET",
1085
+ path: "/bases/{baseId}/views",
1086
+ tags: ["Views"],
1087
+ summary: "List active views for a Base",
1088
+ successDescription: "Saved table views for a Base."
1089
+ }).input(z.object({ baseId: z.string() })).output(z.array(viewSchema)),
1090
+ listArchivedViews: oc.route({
1091
+ method: "GET",
1092
+ path: "/bases/{baseId}/views/archived",
1093
+ tags: ["Views"],
1094
+ summary: "List archived views for a Base",
1095
+ successDescription: "Views that have been archived (soft-deleted) from a Base."
1096
+ }).input(z.object({ baseId: z.string() })).output(z.array(viewSchema)),
1097
+ listArchivedRecords: oc.route({
1098
+ method: "GET",
1099
+ path: "/bases/{baseId}/records/archived",
1100
+ tags: ["Records"],
1101
+ summary: "List archived records for a Base",
1102
+ successDescription: "Records that have been archived (soft-deleted) from a Base."
1103
+ }).input(z.object({ baseId: z.string() })).output(z.array(recordSchema)),
1104
+ create: oc.route({
1105
+ method: "POST",
1106
+ path: "/bases",
1107
+ tags: ["Bases"],
1108
+ summary: "Create Base",
1109
+ successDescription: "Created Base."
1110
+ }).input(createBaseInputSchema).output(baseSchema),
1111
+ createChangeRequest: oc.route({
1112
+ method: "POST",
1113
+ path: "/bases/{baseId}/change-requests",
1114
+ tags: ["Bases", "Change Requests"],
1115
+ summary: "Create Change Request in Base",
1116
+ successDescription: "Created change request for review."
1117
+ }).input(createChangeRequestInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema),
1118
+ createBulkChangeRequest: oc.route({
1119
+ method: "POST",
1120
+ path: "/bases/{baseId}/records/bulk-change-request",
1121
+ tags: ["Bases", "Change Requests"],
1122
+ summary: "Create bulk record Change Request in Base",
1123
+ successDescription: "Created one change request proposing many record creates."
1124
+ }).input(createBulkChangeRequestInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema),
1125
+ createField: oc.route({
1126
+ method: "POST",
1127
+ path: "/bases/{baseId}/fields",
1128
+ tags: ["Bases"],
1129
+ summary: "Create Base field",
1130
+ successDescription: "Created Base field."
1131
+ }).input(createBaseFieldInputSchema.extend({ baseId: z.string() })).output(baseSchema),
1132
+ createFieldChangeRequest: oc.route({
1133
+ method: "POST",
1134
+ path: "/bases/{baseId}/fields/change-requests",
1135
+ tags: ["Bases", "Change Requests"],
1136
+ summary: "Create Add Field change request",
1137
+ successDescription: "Created change request that proposes a new field."
1138
+ }).input(createFieldChangeRequestInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema),
1139
+ createViewChangeRequest: oc.route({
1140
+ method: "POST",
1141
+ path: "/bases/{baseId}/views/change-requests",
1142
+ tags: ["Views", "Change Requests"],
1143
+ summary: "Create View change request",
1144
+ successDescription: "Created change request that proposes a new View."
1145
+ }).input(createViewInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema),
1146
+ deleteFieldChangeRequest: oc.route({
1147
+ method: "DELETE",
1148
+ path: "/bases/{baseId}/fields/change-requests",
1149
+ tags: ["Bases", "Change Requests"],
1150
+ summary: "Create Delete Field change request",
1151
+ successDescription: "Created change request that soft-deletes a field."
1152
+ }).input(deleteFieldChangeRequestInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema),
1153
+ updateFieldChangeRequest: oc.route({
1154
+ method: "PATCH",
1155
+ path: "/bases/{baseId}/fields/change-requests",
1156
+ tags: ["Bases", "Change Requests"],
1157
+ summary: "Create Update Field change request",
1158
+ successDescription: "Created change request that updates field metadata (name, required, options)."
1159
+ }).input(updateFieldChangeRequestInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema),
1160
+ previewFieldConversion: oc.route({
1161
+ method: "POST",
1162
+ path: "/bases/{baseId}/fields/convert/preview",
1163
+ tags: ["Bases"],
1164
+ summary: "Preview field type conversion",
1165
+ successDescription: "Dry-run statistics for converting a field to a different type."
1166
+ }).input(previewFieldConversionInputSchema.extend({ baseId: z.string() })).output(previewFieldConversionOutputSchema),
1167
+ convertFieldChangeRequest: oc.route({
1168
+ method: "POST",
1169
+ path: "/bases/{baseId}/fields/convert/change-requests",
1170
+ tags: ["Bases", "Change Requests"],
1171
+ summary: "Create Convert Field change request",
1172
+ successDescription: "Created change request that converts a field to a different type."
1173
+ }).input(convertFieldChangeRequestInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema),
1174
+ reorderFieldsChangeRequest: oc.route({
1175
+ method: "POST",
1176
+ path: "/bases/{baseId}/fields/reorder/change-requests",
1177
+ tags: ["Bases", "Change Requests"],
1178
+ summary: "Reorder fields",
1179
+ successDescription: "Created change request that reorders fields."
1180
+ }).input(reorderFieldsChangeRequestInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema),
1181
+ archiveChangeRequest: oc.route({
1182
+ method: "POST",
1183
+ path: "/bases/{baseId}/archive/change-requests",
1184
+ tags: ["Bases", "Change Requests"],
1185
+ summary: "Archive base",
1186
+ successDescription: "Created change request that archives a base."
1187
+ }).input(archiveBaseInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema),
1188
+ restoreChangeRequest: oc.route({
1189
+ method: "POST",
1190
+ path: "/bases/{baseId}/restore/change-requests",
1191
+ tags: ["Bases", "Change Requests"],
1192
+ summary: "Restore base",
1193
+ successDescription: "Created change request that restores an archived base."
1194
+ }).input(restoreBaseInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema),
1195
+ restoreFieldChangeRequest: oc.route({
1196
+ method: "POST",
1197
+ path: "/bases/{baseId}/fields/restore/change-requests",
1198
+ tags: ["Bases", "Change Requests"],
1199
+ summary: "Restore deleted field",
1200
+ successDescription: "Created change request that restores a soft-deleted field."
1201
+ }).input(restoreFieldChangeRequestInputSchema.extend({ baseId: z.string() })).output(changeRequestSchema)
1202
+ };
1203
+ var recordContract = {
1204
+ list: oc.route({
1205
+ method: "GET",
1206
+ path: "/records",
1207
+ tags: ["Records"],
1208
+ summary: "List records",
1209
+ successDescription: "Canonical records created from merged change requests."
1210
+ }).input(listInputSchema).output(z.array(recordSchema)),
1211
+ listPaged: oc.route({
1212
+ method: "GET",
1213
+ path: "/records/paged",
1214
+ tags: ["Records"],
1215
+ summary: "List records with keyset pagination",
1216
+ successDescription: "A page of canonical records plus an opaque nextCursor (null at the end)."
1217
+ }).input(listRecordsInputSchema).output(listRecordsResponseSchema),
1218
+ count: oc.route({
1219
+ method: "GET",
1220
+ path: "/records/count",
1221
+ tags: ["Records"],
1222
+ summary: "Count records",
1223
+ successDescription: "Total active records in the space, optionally scoped to a base."
1224
+ }).input(countRecordsInputSchema).output(countRecordsResponseSchema),
1225
+ get: oc.route({
1226
+ method: "GET",
1227
+ path: "/records/{recordId}",
1228
+ tags: ["Records"],
1229
+ summary: "Get record",
1230
+ successDescription: "Canonical record detail."
1231
+ }).input(z.object({ recordId: z.string() })).output(recordSchema),
1232
+ search: oc.route({
1233
+ method: "GET",
1234
+ path: "/records/search",
1235
+ tags: ["Records"],
1236
+ summary: "Filter records by field text",
1237
+ successDescription: "Canonical records matching a field text filter."
1238
+ }).input(recordFieldFilterInputSchema).output(z.array(recordSchema)),
1239
+ updateChangeRequest: oc.route({
1240
+ method: "PUT",
1241
+ path: "/records/{recordId}/change-requests",
1242
+ tags: ["Records", "Change Requests"],
1243
+ summary: "Create record update change request",
1244
+ successDescription: "Created change request that proposes updating a canonical record."
1245
+ }).input(reviseOperationInputSchema.extend({ recordId: z.string() })).output(changeRequestSchema),
1246
+ deleteChangeRequest: oc.route({
1247
+ method: "DELETE",
1248
+ path: "/records/{recordId}/change-requests",
1249
+ tags: ["Records", "Change Requests"],
1250
+ summary: "Create record delete change request",
1251
+ successDescription: "Created change request that proposes archiving or deleting a record."
1252
+ }).input(createDeleteChangeRequestInputSchema.extend({ recordId: z.string() })).output(changeRequestSchema),
1253
+ listChangeRequests: oc.route({
1254
+ method: "GET",
1255
+ path: "/records/{recordId}/change-requests",
1256
+ tags: ["Records", "Change Requests"],
1257
+ summary: "List record change request history",
1258
+ successDescription: "Change requests and operations connected to the canonical record."
1259
+ }).input(z.object({ recordId: z.string() })).output(z.array(changeRequestSchema)),
1260
+ restoreChangeRequest: oc.route({
1261
+ method: "POST",
1262
+ path: "/records/{recordId}/restore/change-requests",
1263
+ tags: ["Records", "Change Requests"],
1264
+ summary: "Create record restore change request",
1265
+ successDescription: "Created change request that restores an archived record."
1266
+ }).input(restoreRecordInputSchema.extend({ recordId: z.string() })).output(changeRequestSchema),
1267
+ listLinks: oc.route({
1268
+ method: "GET",
1269
+ path: "/records/{recordId}/links",
1270
+ tags: ["Records"],
1271
+ summary: "List record links",
1272
+ successDescription: "Active outbound links from a canonical record."
1273
+ }).input(z.object({ recordId: z.string() })).output(z.array(recordLinkSchema))
1274
+ };
1275
+ var viewContract = {
1276
+ updateChangeRequest: oc.route({
1277
+ method: "PUT",
1278
+ path: "/views/{viewId}/change-requests",
1279
+ tags: ["Views", "Change Requests"],
1280
+ summary: "Create View update change request",
1281
+ successDescription: "Created change request that proposes updating a View."
1282
+ }).input(updateViewInputSchema.extend({ viewId: z.string() })).output(changeRequestSchema),
1283
+ deleteChangeRequest: oc.route({
1284
+ method: "DELETE",
1285
+ path: "/views/{viewId}/change-requests",
1286
+ tags: ["Views", "Change Requests"],
1287
+ summary: "Create View delete change request",
1288
+ successDescription: "Created change request that proposes archiving a View."
1289
+ }).input(deleteViewInputSchema.extend({ viewId: z.string() })).output(changeRequestSchema),
1290
+ restoreChangeRequest: oc.route({
1291
+ method: "POST",
1292
+ path: "/views/{viewId}/restore/change-requests",
1293
+ tags: ["Views", "Change Requests"],
1294
+ summary: "Create View restore change request",
1295
+ successDescription: "Created change request that restores an archived View."
1296
+ }).input(restoreViewInputSchema.extend({ viewId: z.string() })).output(changeRequestSchema)
1297
+ };
1298
+ var docSchema = z.object({
1299
+ node: nodeSchema,
1300
+ storagePrefix: z.string(),
1301
+ body: z.string()
1302
+ });
1303
+ var createDocInputSchema = z.object({
1304
+ parentNodeId: z.string().optional(),
1305
+ slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
1306
+ name: z.string().min(1),
1307
+ description: z.string().optional().default(""),
1308
+ body: z.string().optional().default("")
1309
+ });
1310
+ var updateDocInputSchema = z.object({
1311
+ body: z.string()
1312
+ });
1313
+ var createDocChangeRequestInputSchema = z.object({
1314
+ body: z.string(),
1315
+ message: z.string().optional().default("Update doc").describe(
1316
+ 'Explanation shown to the human reviewer. Write a conventional-commit style subject \u2014 imperative verb + what + why, e.g. "Add rollback steps to the deploy runbook".'
1317
+ ),
1318
+ submittedBy: z.string().optional().default("local-producer")
1319
+ });
1320
+ var docContract = {
1321
+ list: oc.route({
1322
+ method: "GET",
1323
+ path: "/docs",
1324
+ tags: ["Docs"],
1325
+ summary: "List Doc nodes",
1326
+ successDescription: "Doc nodes with their storage-backed bodies."
1327
+ }).output(z.array(docSchema)),
1328
+ create: oc.route({
1329
+ method: "POST",
1330
+ path: "/docs",
1331
+ tags: ["Docs"],
1332
+ summary: "Create Doc node",
1333
+ successDescription: "Created Doc node and initialized its body."
1334
+ }).input(createDocInputSchema).output(docSchema),
1335
+ get: oc.route({
1336
+ method: "GET",
1337
+ path: "/docs/{nodeId}",
1338
+ tags: ["Docs"],
1339
+ summary: "Get Doc node",
1340
+ successDescription: "Doc node detail and body."
1341
+ }).input(z.object({ nodeId: z.string() })).output(docSchema),
1342
+ updateBody: oc.route({
1343
+ method: "PUT",
1344
+ path: "/docs/{nodeId}/body",
1345
+ tags: ["Docs"],
1346
+ summary: "Update Doc body",
1347
+ successDescription: "Updated the Doc body."
1348
+ }).input(updateDocInputSchema.extend({ nodeId: z.string() })).output(docSchema),
1349
+ createChangeRequest: oc.route({
1350
+ method: "POST",
1351
+ path: "/docs/{nodeId}/change-requests",
1352
+ tags: ["Docs", "Change Requests"],
1353
+ summary: "Create Doc change request",
1354
+ successDescription: "Created a change request that proposes a new Doc body."
1355
+ }).input(createDocChangeRequestInputSchema.extend({ nodeId: z.string() })).output(changeRequestSchema)
1356
+ };
1357
+ var fileTreeFileSchema = z.object({
1358
+ path: z.string(),
1359
+ name: z.string(),
1360
+ size: z.number(),
1361
+ updatedAt: z.string().nullable(),
1362
+ mimeType: z.string().nullable(),
1363
+ assetId: z.string(),
1364
+ displayName: z.string().nullable()
1365
+ });
1366
+ var assetFileInputSchema = z.object({
1367
+ path: z.string().min(1),
1368
+ assetId: z.string().min(1),
1369
+ displayName: z.string().optional(),
1370
+ mimeType: z.string().optional()
1371
+ }).strict();
1372
+ var textFileInputSchema = z.object({
1373
+ path: z.string().min(1),
1374
+ content: z.string().default(""),
1375
+ mimeType: z.string().optional()
1376
+ }).strict();
1377
+ var assetFileOperationInputSchema = z.object({
1378
+ kind: z.enum(["create", "update"]),
1379
+ path: z.string().min(1),
1380
+ assetId: z.string().min(1),
1381
+ displayName: z.string().optional(),
1382
+ mimeType: z.string().optional(),
1383
+ baseContentHash: z.string().optional()
1384
+ }).strict();
1385
+ var textFileOperationInputSchema = z.object({
1386
+ kind: z.enum(["create", "update"]),
1387
+ path: z.string().min(1),
1388
+ content: z.string(),
1389
+ mimeType: z.string().optional(),
1390
+ baseContentHash: z.string().optional()
1391
+ }).strict();
1392
+ var fileTreeNodeSchema = z.object({
1393
+ node: nodeSchema,
1394
+ entryFile: z.string(),
1395
+ visibility: z.enum(["private", "workspace", "public"]),
1396
+ version: z.string(),
1397
+ files: z.array(fileTreeFileSchema)
1398
+ });
1399
+ var createFileTreeInputSchema = z.object({
1400
+ parentNodeId: z.string().optional(),
1401
+ slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
1402
+ name: z.string().min(1),
1403
+ description: z.string().optional().default(""),
1404
+ visibility: z.enum(["private", "workspace", "public"]).optional().default("private"),
1405
+ version: z.string().optional().default("0.1.0"),
1406
+ files: z.array(z.union([assetFileInputSchema, textFileInputSchema])).optional().default([])
1407
+ });
1408
+ var fileTreeFileOperationInputSchema = z.union([
1409
+ assetFileOperationInputSchema,
1410
+ textFileOperationInputSchema,
1411
+ z.object({
1412
+ kind: z.literal("delete"),
1413
+ path: z.string().min(1),
1414
+ baseContentHash: z.string().optional()
1415
+ }).strict(),
1416
+ z.object({
1417
+ kind: z.literal("metadata_update"),
1418
+ metadata: z.object({
1419
+ entryFile: z.string().optional(),
1420
+ visibility: z.enum(["private", "workspace", "public"]).optional(),
1421
+ version: z.string().optional()
1422
+ }).default({})
1423
+ }).strict()
1424
+ ]);
1425
+ var createFileTreeChangeRequestInputSchema = z.object({
1426
+ message: z.string().optional().default("Update file tree").describe(
1427
+ 'Explanation shown to the human reviewer. Write a conventional-commit style subject \u2014 imperative verb + what + why, e.g. "Rewrite README.md quickstart for the new auth flow".'
1428
+ ),
1429
+ submittedBy: z.string().optional().default("local-producer"),
1430
+ operations: z.array(fileTreeFileOperationInputSchema).min(1)
1431
+ });
1432
+ var makeFileTreeContract = (routeBase, tag) => {
1433
+ const label = tag.endsWith("s") ? tag.slice(0, -1) : tag;
1434
+ const basePath = `/${routeBase}`;
1435
+ return {
1436
+ list: oc.route({
1437
+ method: "GET",
1438
+ path: basePath,
1439
+ tags: [tag],
1440
+ summary: `List ${label} nodes`,
1441
+ successDescription: `${label} nodes with their Asset-backed file trees.`
1442
+ }).output(z.array(fileTreeNodeSchema)),
1443
+ create: oc.route({
1444
+ method: "POST",
1445
+ path: basePath,
1446
+ tags: [tag],
1447
+ summary: `Create ${label} node`,
1448
+ successDescription: `Created ${label} node and initialized file tree.`
1449
+ }).input(createFileTreeInputSchema).output(fileTreeNodeSchema),
1450
+ get: oc.route({
1451
+ method: "GET",
1452
+ path: `${basePath}/{nodeId}`,
1453
+ tags: [tag],
1454
+ summary: `Get ${label} node`,
1455
+ successDescription: `${label} node detail and file tree.`
1456
+ }).input(z.object({ nodeId: z.string() })).output(fileTreeNodeSchema),
1457
+ listFiles: oc.route({
1458
+ method: "GET",
1459
+ path: `${basePath}/{nodeId}/files`,
1460
+ tags: [tag],
1461
+ summary: `List ${label} files`,
1462
+ successDescription: `Asset-backed files mounted under the ${label} node.`
1463
+ }).input(z.object({ nodeId: z.string() })).output(z.array(fileTreeFileSchema)),
1464
+ readFile: oc.route({
1465
+ method: "GET",
1466
+ path: `${basePath}/{nodeId}/files/{+filePath}`,
1467
+ tags: [tag],
1468
+ summary: `Read ${label} file`,
1469
+ successDescription: `${label} file content and content hash.`
1470
+ }).input(z.object({ nodeId: z.string(), filePath: z.string() })).output(
1471
+ z.object({
1472
+ nodeId: z.string(),
1473
+ path: z.string(),
1474
+ encoding: z.enum(["utf8", "url"]),
1475
+ content: z.string(),
1476
+ mimeType: z.string(),
1477
+ assetId: z.string(),
1478
+ displayName: z.string().nullable(),
1479
+ assetUrl: z.string().nullable(),
1480
+ contentHash: z.string()
1481
+ })
1482
+ ),
1483
+ createChangeRequest: oc.route({
1484
+ method: "POST",
1485
+ path: `${basePath}/{nodeId}/change-requests`,
1486
+ tags: [tag, "Change Requests"],
1487
+ summary: `Create ${label} change request`,
1488
+ successDescription: `Created file-tree change request for a ${label} node.`
1489
+ }).input(createFileTreeChangeRequestInputSchema.extend({ nodeId: z.string() })).output(changeRequestSchema)
1490
+ };
1491
+ };
1492
+
1493
+ // ../../packages/busabase-contract/src/domains/drive/contract.ts
1494
+ var driveContract = makeFileTreeContract("drives", "Drives");
1495
+ z.object({
1496
+ assetId: z.string()
1497
+ });
1498
+ var FileNodeVOSchema = z.object({
1499
+ node: nodeSchema,
1500
+ asset: AssetVOSchema
1501
+ });
1502
+
1503
+ // ../../packages/busabase-contract/src/domains/file-node/contract.ts
1504
+ var createFileNodeInputSchema = z.object({
1505
+ parentNodeId: z.string().optional(),
1506
+ slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
1507
+ name: z.string().min(1),
1508
+ description: z.string().optional().default(""),
1509
+ assetId: z.string().min(1)
1510
+ });
1511
+ var fileContract = {
1512
+ list: oc.route({
1513
+ method: "GET",
1514
+ path: "/files",
1515
+ tags: ["Files"],
1516
+ summary: "List File nodes",
1517
+ successDescription: "Workspace File nodes with their backing Asset metadata."
1518
+ }).output(z.array(FileNodeVOSchema)),
1519
+ create: oc.route({
1520
+ method: "POST",
1521
+ path: "/files",
1522
+ tags: ["Files"],
1523
+ summary: "Create File node",
1524
+ successDescription: "Created a first-class File node that references an Asset."
1525
+ }).input(createFileNodeInputSchema).output(FileNodeVOSchema),
1526
+ get: oc.route({
1527
+ method: "GET",
1528
+ path: "/files/{nodeId}",
1529
+ tags: ["Files"],
1530
+ summary: "Get File node",
1531
+ successDescription: "File node detail and backing Asset metadata."
1532
+ }).input(z.object({ nodeId: z.string() })).output(FileNodeVOSchema)
1533
+ };
1534
+ var folderSchema = z.object({
1535
+ node: nodeSchema,
1536
+ children: z.array(nodeSchema)
1537
+ });
1538
+ var folderContract = {
1539
+ list: oc.route({
1540
+ method: "GET",
1541
+ path: "/folders",
1542
+ tags: ["Folders"],
1543
+ summary: "List Folder nodes",
1544
+ successDescription: "Folder nodes with their direct children."
1545
+ }).output(z.array(folderSchema)),
1546
+ get: oc.route({
1547
+ method: "GET",
1548
+ path: "/folders/{nodeId}",
1549
+ tags: ["Folders"],
1550
+ summary: "Get Folder node",
1551
+ successDescription: "Folder node and its direct children."
1552
+ }).input(z.object({ nodeId: z.string() })).output(folderSchema)
1553
+ };
1554
+
1555
+ // ../../packages/busabase-contract/src/domains/skill/contract.ts
1556
+ var skillContract = makeFileTreeContract("skills", "Skills");
1557
+ var VaultItemKeySchema = z.string().trim().min(1).max(128).regex(/^[A-Z_][A-Z0-9_]*$/, "Use uppercase letters, numbers, and underscores");
1558
+ var VaultItemValueSchema = z.string().max(8192);
1559
+ var VaultItemKindSchema = z.enum(["secret", "variable"]);
1560
+ var VaultScopeTypeSchema = z.enum([
1561
+ "personal",
1562
+ "workspace",
1563
+ "base",
1564
+ "agent",
1565
+ "tool",
1566
+ "api_key"
1567
+ ]);
1568
+ var VaultEnvironmentSchema = z.enum(["local", "development", "staging", "production"]);
1569
+ var VaultAccessPolicySchema = z.object({
1570
+ runtime: z.boolean().default(true),
1571
+ reveal: z.boolean().default(true),
1572
+ edit: z.boolean().default(true),
1573
+ share: z.boolean().default(false)
1574
+ });
1575
+ var VaultItemInputSchema = z.object({
1576
+ id: z.string().optional(),
1577
+ kind: VaultItemKindSchema,
1578
+ key: VaultItemKeySchema,
1579
+ value: VaultItemValueSchema,
1580
+ scopeType: VaultScopeTypeSchema.default("personal"),
1581
+ scopeId: z.string().trim().nullable().optional(),
1582
+ environment: VaultEnvironmentSchema.default("local"),
1583
+ description: z.string().trim().max(512).default(""),
1584
+ access: VaultAccessPolicySchema.default({
1585
+ runtime: true,
1586
+ reveal: true,
1587
+ edit: true,
1588
+ share: false
1589
+ })
1590
+ });
1591
+ var UpdateVaultSettingsInputSchema = z.object({
1592
+ items: z.array(VaultItemInputSchema).max(200)
1593
+ });
1594
+ var VaultItemVOSchema = VaultItemInputSchema.extend({
1595
+ id: z.string(),
1596
+ scopeId: z.string().nullable(),
1597
+ updatedAt: z.string(),
1598
+ createdAt: z.string(),
1599
+ lastUsedAt: z.string().nullable()
1600
+ });
1601
+ var VaultSettingsVOSchema = z.object({
1602
+ ownerId: z.string().nullable(),
1603
+ items: z.array(VaultItemVOSchema),
1604
+ updatedAt: z.string().nullable()
1605
+ });
1606
+ z.record(VaultItemKeySchema, VaultItemValueSchema).default({});
1607
+ var VaultSuccessSchema = z.object({ success: z.boolean() });
1608
+
1609
+ // ../../packages/busabase-contract/src/domains/vault/contract.ts
1610
+ var vaultContract = {
1611
+ get: oc.route({
1612
+ method: "GET",
1613
+ path: "/vault",
1614
+ tags: ["Vault"],
1615
+ summary: "Get local Vault settings",
1616
+ successDescription: "Local Vault secrets and variables for this Busabase instance."
1617
+ }).output(VaultSettingsVOSchema),
1618
+ update: oc.route({
1619
+ method: "PUT",
1620
+ path: "/vault",
1621
+ tags: ["Vault"],
1622
+ summary: "Replace local Vault settings",
1623
+ successDescription: "Updated local Vault secrets and variables."
1624
+ }).input(UpdateVaultSettingsInputSchema).output(VaultSettingsVOSchema),
1625
+ clear: oc.route({
1626
+ method: "DELETE",
1627
+ path: "/vault",
1628
+ tags: ["Vault"],
1629
+ summary: "Clear local Vault settings",
1630
+ successDescription: "Removed local Vault secrets and variables."
1631
+ }).output(VaultSuccessSchema)
1632
+ };
1633
+
1634
+ // ../../packages/busabase-contract/src/contract/busabase.ts
1635
+ var changeRequestBatchResultSchema = z.object({
1636
+ results: z.array(
1637
+ z.object({
1638
+ changeRequestId: z.string(),
1639
+ ok: z.boolean(),
1640
+ status: z.string().optional(),
1641
+ error: z.string().optional()
1642
+ })
1643
+ )
1644
+ });
1645
+ var busabaseContractRoutes = {
1646
+ auth: {
1647
+ verify: oc.route({
1648
+ method: "GET",
1649
+ path: "/auth",
1650
+ tags: ["Auth"],
1651
+ summary: "Verify auth and get the targeted space, user, membership, and all spaces",
1652
+ successDescription: "The space this request targets, the acting user, their membership, and every space the user belongs to (`spaces`). Open source returns the local space/user; the cloud resolves the real ones from the user API key \u2014 when `spaces` has more than one entry, target a specific space with the `x-busabase-space` header instead of relying on the default."
1653
+ }).output(authInfoSchema)
1654
+ },
1655
+ search: oc.route({
1656
+ method: "GET",
1657
+ path: "/search",
1658
+ tags: ["Search"],
1659
+ summary: "Search Busabase",
1660
+ successDescription: "Paginated search results across records, change requests, Bases, File nodes, and Assets."
1661
+ }).input(searchInputSchema).output(searchResponseSchema),
1662
+ nodes: {
1663
+ list: oc.route({
1664
+ method: "GET",
1665
+ path: "/nodes",
1666
+ tags: ["Nodes"],
1667
+ summary: "List node tree",
1668
+ successDescription: "Workspace node tree including folders, Bases, files, and agents."
1669
+ }).output(z.array(nodeSchema)),
1670
+ listArchived: oc.route({
1671
+ method: "GET",
1672
+ path: "/nodes/archived",
1673
+ tags: ["Nodes"],
1674
+ summary: "List archived nodes",
1675
+ successDescription: "Soft-archived folders, docs, and skills (for the Trash view)."
1676
+ }).output(z.array(nodeSchema)),
1677
+ createChangeRequest: oc.route({
1678
+ method: "POST",
1679
+ path: "/nodes/change-requests",
1680
+ tags: ["Nodes", "Change Requests"],
1681
+ summary: "Create Node tree change request",
1682
+ successDescription: "Created change request for folder or node tree changes."
1683
+ }).input(createNodeChangeRequestInputSchema).output(changeRequestSchema),
1684
+ purge: oc.route({
1685
+ method: "DELETE",
1686
+ path: "/nodes/{nodeId}",
1687
+ tags: ["Nodes"],
1688
+ summary: "Permanently delete an archived node",
1689
+ successDescription: "Irreversibly removed an archived folder/doc/skill (and its subtree). Refused unless archived and refused if the subtree contains a Base."
1690
+ }).input(z.object({ nodeId: z.string() })).output(z.object({ purged: z.boolean() }))
1691
+ },
1692
+ auditEvents: {
1693
+ list: oc.route({
1694
+ method: "GET",
1695
+ path: "/audit-events",
1696
+ tags: ["Audit"],
1697
+ summary: "List audit events",
1698
+ successDescription: "Recent non-mutating and workflow audit events."
1699
+ }).input(listInputSchema).output(z.array(auditEventSchema)),
1700
+ create: oc.route({
1701
+ method: "POST",
1702
+ path: "/audit-events",
1703
+ tags: ["Audit"],
1704
+ summary: "Create audit event",
1705
+ successDescription: "Recorded audit event."
1706
+ }).input(createAuditEventInputSchema).output(auditEventSchema)
1707
+ },
1708
+ comments: {
1709
+ list: oc.route({
1710
+ method: "GET",
1711
+ path: "/comments",
1712
+ tags: ["Comments"],
1713
+ summary: "List comments",
1714
+ successDescription: "Comments attached to a Busabase subject."
1715
+ }).input(commentSubjectInputSchema).output(z.array(commentSchema)),
1716
+ create: oc.route({
1717
+ method: "POST",
1718
+ path: "/comments",
1719
+ tags: ["Comments"],
1720
+ summary: "Create comment",
1721
+ successDescription: "Created comment attached to a Busabase subject."
1722
+ }).input(createCommentInputSchema).output(commentSchema)
1723
+ },
1724
+ agent: {
1725
+ listTasks: oc.route({
1726
+ method: "GET",
1727
+ path: "/agent/tasks",
1728
+ tags: ["Agent"],
1729
+ summary: "List agent revision tasks",
1730
+ successDescription: "Change requests awaiting an external agent (request-changes or @ai mentions)."
1731
+ }).output(z.array(agentTaskSchema))
1732
+ },
1733
+ live: {
1734
+ // RPC-only by design: no `.route(...)`, so OpenAPI generation and MCP tool
1735
+ // discovery skip this long-lived Event Iterator while `/api/rpc` stays typed.
1736
+ subscribe: oc.output(eventIterator(liveEventSchema))
1737
+ },
1738
+ bases: baseContract,
1739
+ skills: skillContract,
1740
+ drives: driveContract,
1741
+ files: fileContract,
1742
+ docs: docContract,
1743
+ folders: folderContract,
1744
+ assets: assetsContract,
1745
+ vault: vaultContract,
1746
+ changeRequests: {
1747
+ list: oc.route({
1748
+ method: "GET",
1749
+ path: "/change-requests",
1750
+ tags: ["Change Requests"],
1751
+ summary: "List change requests",
1752
+ successDescription: "Change requests waiting for review or ready to merge."
1753
+ }).input(listInputSchema).output(z.array(changeRequestSchema)),
1754
+ listPaged: oc.route({
1755
+ method: "GET",
1756
+ path: "/change-requests/paged",
1757
+ tags: ["Change Requests"],
1758
+ summary: "List change requests with keyset pagination",
1759
+ successDescription: "A page of change requests plus an opaque nextCursor (null at the end). Filter with `status` and/or `mine`."
1760
+ }).input(listChangeRequestsPagedInputSchema).output(listChangeRequestsResponseSchema),
1761
+ counts: oc.route({
1762
+ method: "GET",
1763
+ path: "/change-requests/counts",
1764
+ tags: ["Change Requests"],
1765
+ summary: "Count change requests by inbox tab",
1766
+ successDescription: "Whole-space change request counts per inbox tab (review / changes / created / approved / merged / rejected)."
1767
+ }).output(changeRequestCountsSchema),
1768
+ get: oc.route({
1769
+ method: "GET",
1770
+ path: "/change-requests/{changeRequestId}",
1771
+ tags: ["Change Requests"],
1772
+ summary: "Get change request",
1773
+ successDescription: "Change Request detail."
1774
+ }).input(z.object({ changeRequestId: z.string() })).output(changeRequestSchema),
1775
+ review: oc.route({
1776
+ method: "POST",
1777
+ path: "/change-requests/{changeRequestId}/reviews",
1778
+ tags: ["Change Requests"],
1779
+ summary: "Review change request",
1780
+ successDescription: "Reviewed change request."
1781
+ }).input(reviewChangeRequestInputSchema.extend({ changeRequestId: z.string() })).output(changeRequestSchema),
1782
+ reviewMany: oc.route({
1783
+ method: "POST",
1784
+ path: "/change-requests/reviews",
1785
+ tags: ["Change Requests"],
1786
+ summary: "Review many change requests",
1787
+ successDescription: "Per-change-request review results (failures isolated \u2014 one bad id does not abort the rest)."
1788
+ }).input(
1789
+ reviewChangeRequestInputSchema.extend({
1790
+ changeRequestIds: z.array(z.string()).min(1).max(100)
1791
+ })
1792
+ ).output(changeRequestBatchResultSchema),
1793
+ close: oc.route({
1794
+ method: "POST",
1795
+ path: "/change-requests/{changeRequestId}/close",
1796
+ tags: ["Change Requests"],
1797
+ summary: "Close change request",
1798
+ successDescription: "Closed change request (terminal \u2014 distinct from request changes)."
1799
+ }).input(z.object({ changeRequestId: z.string(), reason: z.string().optional() })).output(changeRequestSchema),
1800
+ merge: oc.route({
1801
+ method: "POST",
1802
+ path: "/change-requests/{changeRequestId}/merge",
1803
+ tags: ["Change Requests"],
1804
+ summary: "Merge change request into Base",
1805
+ successDescription: "Merged change request and canonical record."
1806
+ }).input(z.object({ changeRequestId: z.string() })).output(
1807
+ z.object({
1808
+ changeRequest: changeRequestSchema,
1809
+ record: recordSchema.nullable(),
1810
+ view: viewSchema.nullable()
1811
+ })
1812
+ ),
1813
+ mergeMany: oc.route({
1814
+ method: "POST",
1815
+ path: "/change-requests/merge",
1816
+ tags: ["Change Requests"],
1817
+ summary: "Merge many change requests",
1818
+ successDescription: "Per-change-request merge results (each merged in its own transaction; failures isolated)."
1819
+ }).input(z.object({ changeRequestIds: z.array(z.string()).min(1).max(100) })).output(changeRequestBatchResultSchema)
1820
+ },
1821
+ operations: {
1822
+ revise: oc.route({
1823
+ method: "POST",
1824
+ path: "/operations/{operationId}/revisions",
1825
+ tags: ["Operations", "Change Requests"],
1826
+ summary: "Revise operation",
1827
+ successDescription: "Appended a new commit to the operation and moved the operation head."
1828
+ }).input(reviseOperationInputSchema.extend({ operationId: z.string() })).output(changeRequestSchema)
1829
+ },
1830
+ records: recordContract,
1831
+ views: viewContract
1832
+ };
1833
+ oc.prefix("/api/v1").router(busabaseContractRoutes);
1834
+
1835
+ // ../../packages/busabase-contract/src/contract/cloud.ts
1836
+ var ErrorResponseSchema = z.object({
1837
+ error: z.string()
1838
+ });
1839
+ var HealthResponseSchema = z.object({
1840
+ status: z.string(),
1841
+ timestamp: z.string()
1842
+ });
1843
+ var MetaResponseSchema = z.object({
1844
+ service: z.string(),
1845
+ version: z.string(),
1846
+ timestamp: z.number()
1847
+ });
1848
+ var UserMeResponseSchema = z.object({
1849
+ id: z.string(),
1850
+ name: z.string(),
1851
+ email: z.string(),
1852
+ emailVerified: z.boolean(),
1853
+ image: z.string().nullable(),
1854
+ createdAt: z.string()
1855
+ });
1856
+ var AgentTaskStatusSchema = z.enum([
1857
+ "pending",
1858
+ "in_progress",
1859
+ "completed",
1860
+ "failed",
1861
+ "cancelled"
1862
+ ]);
1863
+ var AgentTaskListItemSchema = z.object({
1864
+ id: z.union([z.string(), z.number()]),
1865
+ title: z.string(),
1866
+ description: z.string().nullable(),
1867
+ status: z.string(),
1868
+ priority: z.string(),
1869
+ createdAt: z.string(),
1870
+ completedAt: z.string().nullable()
1871
+ });
1872
+ var AgentTaskDetailSchema = AgentTaskListItemSchema.extend({
1873
+ result: z.string().nullable()
1874
+ });
1875
+ var authenticatedErrors = {
1876
+ UNAUTHORIZED: {
1877
+ status: 401,
1878
+ message: "Unauthorized",
1879
+ data: ErrorResponseSchema
1880
+ }
1881
+ };
1882
+ var notFoundErrors = {
1883
+ NOT_FOUND: {
1884
+ status: 404,
1885
+ message: "Not Found",
1886
+ data: ErrorResponseSchema
1887
+ }
1888
+ };
1889
+ var { vault: _localVault, ...cloudWorkbenchRoutes } = busabaseContractRoutes;
1890
+ var cloudExtraRoutes = {
1891
+ system: {
1892
+ health: oc.route({
1893
+ method: "GET",
1894
+ path: "/health",
1895
+ tags: ["System"],
1896
+ summary: "Service health status",
1897
+ successDescription: "Service health status"
1898
+ }).output(HealthResponseSchema),
1899
+ meta: oc.route({
1900
+ method: "GET",
1901
+ path: "/meta",
1902
+ tags: ["System"],
1903
+ summary: "Service metadata",
1904
+ successDescription: "Service metadata"
1905
+ }).output(MetaResponseSchema)
1906
+ },
1907
+ users: {
1908
+ me: oc.route({
1909
+ method: "GET",
1910
+ path: "/users/me",
1911
+ tags: ["Users"],
1912
+ summary: "Get authenticated user",
1913
+ successDescription: "Authenticated user information",
1914
+ spec: (operation) => ({
1915
+ ...operation,
1916
+ security: [{ bearerAuth: [] }]
1917
+ })
1918
+ }).errors(authenticatedErrors).output(UserMeResponseSchema)
1919
+ },
1920
+ agentTasks: {
1921
+ list: oc.route({
1922
+ method: "GET",
1923
+ path: "/agent-tasks",
1924
+ tags: ["Agent Tasks"],
1925
+ summary: "List agent tasks",
1926
+ successDescription: "List of agent tasks",
1927
+ inputStructure: "detailed",
1928
+ spec: (operation) => ({
1929
+ ...operation,
1930
+ security: [{ bearerAuth: [] }]
1931
+ })
1932
+ }).errors(authenticatedErrors).input(
1933
+ z.object({
1934
+ query: z.object({
1935
+ limit: z.string().optional(),
1936
+ offset: z.string().optional(),
1937
+ status: AgentTaskStatusSchema.optional()
1938
+ })
1939
+ })
1940
+ ).output(
1941
+ z.object({
1942
+ tasks: z.array(AgentTaskListItemSchema),
1943
+ total: z.number()
1944
+ })
1945
+ ),
1946
+ get: oc.route({
1947
+ method: "GET",
1948
+ path: "/agent-tasks/{id}",
1949
+ tags: ["Agent Tasks"],
1950
+ summary: "Get agent task",
1951
+ successDescription: "Agent task details",
1952
+ inputStructure: "detailed",
1953
+ spec: (operation) => ({
1954
+ ...operation,
1955
+ security: [{ bearerAuth: [] }]
1956
+ })
1957
+ }).errors({
1958
+ ...authenticatedErrors,
1959
+ ...notFoundErrors
1960
+ }).input(
1961
+ z.object({
1962
+ params: z.object({
1963
+ id: z.string()
1964
+ })
1965
+ })
1966
+ ).output(AgentTaskDetailSchema)
1967
+ }
1968
+ };
1969
+ var cloudContract = oc.prefix("/api/v1").router({
1970
+ ...cloudWorkbenchRoutes,
1971
+ ...cloudExtraRoutes
1972
+ });
1973
+
1974
+ // src/client.ts
1975
+ var DEFAULT_BASE_URL = "https://busabase.com";
1976
+ var env = (key) => {
1977
+ if (typeof process === "undefined" || !process.env) {
1978
+ return void 0;
1979
+ }
1980
+ const value = process.env[key];
1981
+ return value && value.length > 0 ? value : void 0;
1982
+ };
1983
+ function normalizeBaseUrl(raw) {
1984
+ return raw.replace(/\/+$/, "").replace(/\/api\/v1$/, "");
1985
+ }
1986
+ function resolveConfig(config = {}) {
1987
+ return {
1988
+ baseUrl: normalizeBaseUrl(config.baseUrl ?? env("BUSABASE_BASE_URL") ?? DEFAULT_BASE_URL),
1989
+ apiKey: config.apiKey ?? env("BUSABASE_API_KEY"),
1990
+ spaceId: config.spaceId ?? env("BUSABASE_SPACE_ID"),
1991
+ headers: config.headers,
1992
+ fetch: config.fetch
1993
+ };
1994
+ }
1995
+ function createBusabaseClient(config = {}) {
1996
+ const resolved = resolveConfig(config);
1997
+ const link = new OpenAPILink(cloudContract, {
1998
+ url: resolved.baseUrl,
1999
+ fetch: resolved.fetch,
2000
+ headers: async () => {
2001
+ const extra = typeof resolved.headers === "function" ? await resolved.headers() : resolved.headers;
2002
+ return {
2003
+ ...resolved.apiKey ? { authorization: `Bearer ${resolved.apiKey}` } : {},
2004
+ ...resolved.spaceId ? { "x-busabase-space": resolved.spaceId } : {},
2005
+ ...extra
2006
+ };
2007
+ }
2008
+ });
2009
+ return createORPCClient(link);
2010
+ }
2011
+
2012
+ // src/index.ts
2013
+ var Busabase = class {
2014
+ /** The underlying fully-typed oRPC client. Use it for anything not surfaced here. */
2015
+ client;
2016
+ /** The config after env / default resolution (base URL, presence of a key, space). */
2017
+ config;
2018
+ constructor(config = {}) {
2019
+ this.config = resolveConfig(config);
2020
+ this.client = createBusabaseClient(this.config);
2021
+ }
2022
+ // Namespaced domain surfaces — delegate to the raw client so callers get the
2023
+ // exact same typing as `client.<ns>` but through a single `Busabase` instance.
2024
+ get bases() {
2025
+ return this.client.bases;
2026
+ }
2027
+ get records() {
2028
+ return this.client.records;
2029
+ }
2030
+ get views() {
2031
+ return this.client.views;
2032
+ }
2033
+ get changeRequests() {
2034
+ return this.client.changeRequests;
2035
+ }
2036
+ get operations() {
2037
+ return this.client.operations;
2038
+ }
2039
+ get nodes() {
2040
+ return this.client.nodes;
2041
+ }
2042
+ get comments() {
2043
+ return this.client.comments;
2044
+ }
2045
+ get auditEvents() {
2046
+ return this.client.auditEvents;
2047
+ }
2048
+ get agent() {
2049
+ return this.client.agent;
2050
+ }
2051
+ get assets() {
2052
+ return this.client.assets;
2053
+ }
2054
+ get skills() {
2055
+ return this.client.skills;
2056
+ }
2057
+ get drives() {
2058
+ return this.client.drives;
2059
+ }
2060
+ get files() {
2061
+ return this.client.files;
2062
+ }
2063
+ get docs() {
2064
+ return this.client.docs;
2065
+ }
2066
+ get folders() {
2067
+ return this.client.folders;
2068
+ }
2069
+ get agentTasks() {
2070
+ return this.client.agentTasks;
2071
+ }
2072
+ /** Full-text search across records, change requests, and Bases. */
2073
+ search(input) {
2074
+ return this.client.search(input);
2075
+ }
2076
+ /** Service health — reaches the server without requiring auth. */
2077
+ health() {
2078
+ return this.client.system.health();
2079
+ }
2080
+ /** The authenticated user behind the configured API key (cloud only). */
2081
+ me() {
2082
+ return this.client.users.me();
2083
+ }
2084
+ };
2085
+
2086
+ export { Busabase, DEFAULT_BASE_URL, cloudContract, createBusabaseClient, normalizeBaseUrl, resolveConfig };