busa-sdk 0.9.4 → 0.9.6
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.d.ts +4949 -2925
- package/dist/index.js +1044 -697
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,473 +4,234 @@ import { oc, eventIterator } from '@orpc/contract';
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
6
6
|
// src/client.ts
|
|
7
|
-
var
|
|
8
|
-
|
|
7
|
+
var i18n = {
|
|
8
|
+
locales: ["en", "zh-CN", "zh-TW", "ja", "ko", "de", "fr", "es", "pt"]};
|
|
9
|
+
var LocaleSchema = z.enum(i18n.locales);
|
|
10
|
+
|
|
11
|
+
// ../../packages/openlib/i18n/i-string.ts
|
|
12
|
+
var iStringRecordSchema = z.partialRecord(LocaleSchema, z.string());
|
|
13
|
+
z.union([z.string(), iStringRecordSchema]).describe("i18n string");
|
|
14
|
+
var fieldNameSchema = z.union([
|
|
15
|
+
z.string().min(1),
|
|
16
|
+
iStringRecordSchema.refine(
|
|
17
|
+
(record) => Object.values(record).some((value) => value && value.trim().length > 0),
|
|
18
|
+
"field name must have at least one non-empty locale value"
|
|
19
|
+
)
|
|
20
|
+
]);
|
|
21
|
+
var fieldTypeSchema = z.enum([
|
|
22
|
+
"text",
|
|
23
|
+
"longtext",
|
|
24
|
+
"markdown",
|
|
25
|
+
"html",
|
|
26
|
+
"attachment",
|
|
27
|
+
"relation",
|
|
28
|
+
"number",
|
|
29
|
+
"date",
|
|
30
|
+
"checkbox",
|
|
31
|
+
"select",
|
|
32
|
+
"multiselect",
|
|
33
|
+
"url",
|
|
34
|
+
"embed",
|
|
35
|
+
"email",
|
|
36
|
+
"phone",
|
|
37
|
+
"created_time",
|
|
38
|
+
"updated_time",
|
|
39
|
+
"created_by",
|
|
40
|
+
"updated_by",
|
|
41
|
+
"auto_number",
|
|
42
|
+
"ai_summary",
|
|
43
|
+
"ai_tags",
|
|
44
|
+
"code",
|
|
45
|
+
"json",
|
|
46
|
+
"yaml"
|
|
47
|
+
]);
|
|
48
|
+
var fieldOptionsSchema = z.object({
|
|
49
|
+
ai: z.object({
|
|
50
|
+
model: z.string().optional(),
|
|
51
|
+
prompt: z.string().optional(),
|
|
52
|
+
reviewRequired: z.boolean().optional(),
|
|
53
|
+
sourceFieldIds: z.array(z.string()).optional()
|
|
54
|
+
}).optional(),
|
|
55
|
+
// Per-field config for `attachment` columns (all optional; logic enforces a
|
|
56
|
+
// 25MB ceiling regardless).
|
|
57
|
+
attachment: z.object({
|
|
58
|
+
maxFiles: z.number().int().positive().optional(),
|
|
59
|
+
allowedMimeTypes: z.array(z.string()).optional(),
|
|
60
|
+
maxFileSize: z.number().int().positive().optional()
|
|
61
|
+
}).optional(),
|
|
62
|
+
choices: z.array(
|
|
63
|
+
z.object({
|
|
64
|
+
color: z.string().optional(),
|
|
65
|
+
id: z.string(),
|
|
66
|
+
name: z.string()
|
|
67
|
+
})
|
|
68
|
+
).optional(),
|
|
69
|
+
code: z.object({
|
|
70
|
+
language: z.string().optional()
|
|
71
|
+
}).optional(),
|
|
72
|
+
embed: z.object({
|
|
73
|
+
aspectRatio: z.enum(["16:9", "4:3", "1:1"]).optional(),
|
|
74
|
+
height: z.number().int().positive().max(1200).optional(),
|
|
75
|
+
providers: z.array(z.string()).optional()
|
|
76
|
+
}).optional(),
|
|
77
|
+
inverseFieldId: z.string().optional(),
|
|
78
|
+
multiple: z.boolean().optional(),
|
|
79
|
+
// Display formatting for `number` columns (Notion-style: one number type,
|
|
80
|
+
// a format option). `currency` renders via Intl.NumberFormat.
|
|
81
|
+
number: z.object({
|
|
82
|
+
format: z.enum(["plain", "currency"]).optional(),
|
|
83
|
+
currency: z.string().optional(),
|
|
84
|
+
locale: z.string().optional()
|
|
85
|
+
}).optional(),
|
|
86
|
+
targetBaseId: z.string().optional().describe("Relation target Base id (bse_\u2026). Or pass targetBaseSlug to name it by slug."),
|
|
87
|
+
targetBaseSlug: z.string().optional().describe(
|
|
88
|
+
"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."
|
|
89
|
+
)
|
|
90
|
+
}).default({});
|
|
91
|
+
var baseFieldSchema = z.object({
|
|
9
92
|
id: z.string(),
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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()
|
|
93
|
+
baseId: z.string(),
|
|
94
|
+
slug: z.string(),
|
|
95
|
+
name: fieldNameSchema,
|
|
96
|
+
type: fieldTypeSchema,
|
|
97
|
+
required: z.boolean(),
|
|
98
|
+
position: z.number(),
|
|
99
|
+
options: fieldOptionsSchema
|
|
58
100
|
});
|
|
59
|
-
var
|
|
60
|
-
var AssetVOSchema = z.object({
|
|
101
|
+
var baseSchema = z.object({
|
|
61
102
|
id: z.string(),
|
|
62
|
-
|
|
103
|
+
nodeId: z.string(),
|
|
104
|
+
slug: z.string(),
|
|
63
105
|
name: z.string(),
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
/** How many places reference this asset (Base records + Doc bodies). */
|
|
72
|
-
usageCount: z.number().int().nonnegative(),
|
|
73
|
-
/** Drive Grep Retrieval text-slot status — see {@link AssetTextStatusSchema}. */
|
|
74
|
-
textStatus: AssetTextStatusSchema,
|
|
75
|
-
createdAt: z.string()
|
|
106
|
+
description: z.string(),
|
|
107
|
+
reviewPolicy: z.object({
|
|
108
|
+
kind: z.literal("single"),
|
|
109
|
+
requiredApprovals: z.number()
|
|
110
|
+
}),
|
|
111
|
+
createdAt: z.string(),
|
|
112
|
+
fields: z.array(baseFieldSchema)
|
|
76
113
|
});
|
|
77
|
-
var
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
114
|
+
var createBaseInputSchema = z.object({
|
|
115
|
+
parentNodeId: z.string().optional(),
|
|
116
|
+
slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
|
|
117
|
+
name: z.string().min(1),
|
|
118
|
+
description: z.string().optional().default(""),
|
|
119
|
+
fields: z.array(
|
|
120
|
+
z.object({
|
|
121
|
+
slug: z.string().min(1),
|
|
122
|
+
name: fieldNameSchema,
|
|
123
|
+
type: fieldTypeSchema.default("text"),
|
|
124
|
+
required: z.boolean().default(false),
|
|
125
|
+
options: fieldOptionsSchema.optional().default({})
|
|
126
|
+
})
|
|
127
|
+
).default([]),
|
|
128
|
+
// Review-first by default: without `autoMerge: true`, this proposes the Base
|
|
129
|
+
// as a pending ChangeRequest (status "in_review") instead of creating it
|
|
130
|
+
// immediately. Pass `autoMerge: true` only for callers that don't need human
|
|
131
|
+
// review (seed/migration scripts, an explicit no-review agent task).
|
|
132
|
+
autoMerge: z.boolean().optional().default(false)
|
|
93
133
|
});
|
|
94
|
-
var
|
|
95
|
-
|
|
96
|
-
|
|
134
|
+
var createBaseFieldInputSchema = z.object({
|
|
135
|
+
name: fieldNameSchema,
|
|
136
|
+
// Field slugs are snake_case identifiers (e.g. `cover_image`, `publish_date`) — the
|
|
137
|
+
// seed and the inline-fields path on `POST /bases` already allow underscores, so the
|
|
138
|
+
// add-field endpoint must too. (Base/folder/view slugs stay kebab-case: they go in URLs.)
|
|
139
|
+
slug: z.string().min(1).regex(/^[a-z0-9_-]+$/),
|
|
140
|
+
type: fieldTypeSchema.default("text"),
|
|
141
|
+
required: z.boolean().optional().default(false),
|
|
142
|
+
options: fieldOptionsSchema.optional().default({})
|
|
97
143
|
});
|
|
98
|
-
var
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
text: z.string().optional(),
|
|
102
|
-
/** Bind a presigned-uploaded text object (a temp `asset-texts/pending/*.txt` key). */
|
|
103
|
-
storageKey: z.string().optional(),
|
|
104
|
-
/**
|
|
105
|
-
* Claimed content hash for the `storageKey` bind path (`sha256:<hex>`, echoing
|
|
106
|
-
* `createTextUploadUrl`'s input like `open-domains/attachments`' confirm step).
|
|
107
|
-
* The server always computes the ACTUAL hash from the bytes during the
|
|
108
|
-
* confirm scan and rejects a mismatch (hash-poisoning defense) — this field
|
|
109
|
-
* is only an optional early-mismatch check, never trusted for addressing.
|
|
110
|
-
*/
|
|
111
|
-
contentHash: z.string().optional(),
|
|
112
|
-
/** Mark as having no extractable text (e.g. a scanned, image-only PDF). */
|
|
113
|
-
none: z.boolean().optional()
|
|
144
|
+
var createFieldChangeRequestInputSchema = createBaseFieldInputSchema.extend({
|
|
145
|
+
message: z.string().optional().default("Add field"),
|
|
146
|
+
submittedBy: z.string().optional().default("local-editor")
|
|
114
147
|
});
|
|
115
|
-
var
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
charCount: z.number().int().nonnegative(),
|
|
120
|
-
byteCount: z.number().int().nonnegative()
|
|
148
|
+
var deleteFieldChangeRequestInputSchema = z.object({
|
|
149
|
+
fieldId: z.string().min(1),
|
|
150
|
+
message: z.string().optional(),
|
|
151
|
+
submittedBy: z.string().optional().default("local-editor")
|
|
121
152
|
});
|
|
122
|
-
var
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
153
|
+
var updateFieldChangeRequestInputSchema = z.object({
|
|
154
|
+
fieldId: z.string().min(1),
|
|
155
|
+
patch: z.object({
|
|
156
|
+
name: fieldNameSchema.optional(),
|
|
157
|
+
required: z.boolean().optional(),
|
|
158
|
+
options: fieldOptionsSchema.optional()
|
|
159
|
+
}),
|
|
160
|
+
message: z.string().optional(),
|
|
161
|
+
submittedBy: z.string().optional().default("local-editor")
|
|
127
162
|
});
|
|
128
|
-
var
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
expiresIn: z.number().int().nonnegative()
|
|
163
|
+
var previewFieldConversionInputSchema = z.object({
|
|
164
|
+
fieldId: z.string().min(1),
|
|
165
|
+
newType: fieldTypeSchema
|
|
132
166
|
});
|
|
133
|
-
var
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
167
|
+
var previewFieldConversionOutputSchema = z.object({
|
|
168
|
+
totalCount: z.number(),
|
|
169
|
+
convertibleCount: z.number(),
|
|
170
|
+
nullCount: z.number(),
|
|
171
|
+
conflicts: z.array(z.object({ recordId: z.string(), currentValue: z.unknown() }))
|
|
138
172
|
});
|
|
139
|
-
var
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
/** JS RegExp flags, e.g. `"i"` for case-insensitive. `g`/`y` are ignored (grep always scans every match per line). */
|
|
146
|
-
flags: z.string().optional().default(""),
|
|
147
|
-
scope: GrepScopeSchema.optional(),
|
|
148
|
-
maxMatches: z.coerce.number().int().min(1).max(GREP_HARD_MAX_MATCHES).optional().default(GREP_DEFAULT_MAX_MATCHES),
|
|
149
|
-
contextLines: z.coerce.number().int().min(0).max(GREP_MAX_CONTEXT_LINES).optional().default(GREP_DEFAULT_CONTEXT_LINES)
|
|
173
|
+
var convertFieldChangeRequestInputSchema = z.object({
|
|
174
|
+
fieldId: z.string().min(1),
|
|
175
|
+
newType: fieldTypeSchema,
|
|
176
|
+
selectChoiceMode: z.enum(["auto_create", "null_on_missing"]).default("null_on_missing"),
|
|
177
|
+
message: z.string().optional(),
|
|
178
|
+
submittedBy: z.string().optional().default("local-editor")
|
|
150
179
|
});
|
|
151
|
-
var
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
drivePath: z.string(),
|
|
156
|
-
line: z.number().int().positive(),
|
|
157
|
-
/** 1-based character column (not byte offset) of the match start within the line. */
|
|
158
|
-
column: z.number().int().positive(),
|
|
159
|
-
/** The matching line, truncated if it exceeds the long-line guard. */
|
|
160
|
-
text: z.string(),
|
|
161
|
-
before: z.array(z.string()),
|
|
162
|
-
after: z.array(z.string())
|
|
180
|
+
var reorderFieldsChangeRequestInputSchema = z.object({
|
|
181
|
+
fieldIds: z.array(z.string()).min(1),
|
|
182
|
+
message: z.string().optional(),
|
|
183
|
+
submittedBy: z.string().optional().default("local-editor")
|
|
163
184
|
});
|
|
164
|
-
var
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
/** Asset ids in scope with no text yet (contentKind text-or-writable-binary, no row). */
|
|
168
|
-
missing: z.array(z.string()),
|
|
169
|
-
/** Asset ids in scope whose derived text is stale (source replaced since it was written). */
|
|
170
|
-
stale: z.array(z.string()),
|
|
171
|
-
/** Count of assets in scope explicitly marked `none` (no extractable text). */
|
|
172
|
-
unsearchable: z.number().int().nonnegative(),
|
|
173
|
-
/**
|
|
174
|
-
* Asset ids whose scan was attempted but failed (storage error, corrupt
|
|
175
|
-
* cache file, object deleted mid-flight) — NOT counted in `filesScanned`.
|
|
176
|
-
* Honest coverage: these were not actually searched, so a caller must not
|
|
177
|
-
* treat their absence from `matches` as a clean "no match".
|
|
178
|
-
*/
|
|
179
|
-
errored: z.array(z.string()),
|
|
180
|
-
/**
|
|
181
|
-
* Count of in-scope, present-and-searchable assets the scan never even
|
|
182
|
-
* reached because the deadline or `maxMatches` budget ran out first. Only
|
|
183
|
-
* nonzero when `truncated` is true.
|
|
184
|
-
*/
|
|
185
|
-
notReached: z.number().int().nonnegative(),
|
|
186
|
-
truncated: z.boolean()
|
|
185
|
+
var archiveBaseInputSchema = z.object({
|
|
186
|
+
message: z.string().optional(),
|
|
187
|
+
submittedBy: z.string().optional().default("local-editor")
|
|
187
188
|
});
|
|
188
|
-
var
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
endLine: z.coerce.number().int().min(1)
|
|
189
|
+
var restoreBaseInputSchema = z.object({
|
|
190
|
+
message: z.string().optional(),
|
|
191
|
+
submittedBy: z.string().optional().default("local-editor")
|
|
192
192
|
});
|
|
193
|
-
var
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
totalLines: z.number().int().nonnegative(),
|
|
198
|
-
truncated: z.boolean()
|
|
193
|
+
var restoreFieldChangeRequestInputSchema = z.object({
|
|
194
|
+
fieldId: z.string().min(1),
|
|
195
|
+
message: z.string().optional(),
|
|
196
|
+
submittedBy: z.string().optional().default("local-editor")
|
|
199
197
|
});
|
|
200
198
|
|
|
201
|
-
// ../../packages/busabase-contract/src/domains/
|
|
202
|
-
var
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
199
|
+
// ../../packages/busabase-contract/src/domains/filetree/definition.ts
|
|
200
|
+
var fileTreeOperations = (type) => [
|
|
201
|
+
{
|
|
202
|
+
kind: `${type}_file_create`,
|
|
203
|
+
label: "Create file",
|
|
204
|
+
tone: "border-emerald-200 bg-emerald-50 text-emerald-800"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
kind: `${type}_file_update`,
|
|
208
|
+
label: "Update file",
|
|
209
|
+
tone: "border-blue-200 bg-blue-50 text-blue-800"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
kind: `${type}_file_delete`,
|
|
213
|
+
label: "Delete file",
|
|
214
|
+
tone: "border-rose-200 bg-rose-50 text-rose-800"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
kind: `${type}_metadata_update`,
|
|
218
|
+
label: `Update ${type}`,
|
|
219
|
+
tone: "border-violet-200 bg-violet-50 text-violet-800"
|
|
220
|
+
}
|
|
221
|
+
];
|
|
222
|
+
var makeFileTreeNodeType = (config) => ({
|
|
223
|
+
type: config.type,
|
|
224
|
+
label: config.label,
|
|
225
|
+
icon: config.icon,
|
|
226
|
+
capabilities: { hasDetail: true, creatable: true },
|
|
227
|
+
operations: fileTreeOperations(config.type)
|
|
206
228
|
});
|
|
207
|
-
var assetsContract = {
|
|
208
|
-
createUploadUrl: oc.route({
|
|
209
|
-
method: "POST",
|
|
210
|
-
path: "/assets/upload-urls",
|
|
211
|
-
tags: ["Assets"],
|
|
212
|
-
summary: "Request asset upload URL",
|
|
213
|
-
successDescription: "Presigned (or dev) upload URL plus the public URL and asset id when identical bytes are already in the library."
|
|
214
|
-
}).input(RequestUploadUrlInputSchema).output(RequestUploadUrlVOSchema),
|
|
215
|
-
confirm: oc.route({
|
|
216
|
-
method: "POST",
|
|
217
|
-
path: "/assets/confirmations",
|
|
218
|
-
tags: ["Assets"],
|
|
219
|
-
summary: "Confirm asset upload",
|
|
220
|
-
successDescription: "Recorded the file and ensured its Busabase Asset library entry."
|
|
221
|
-
}).input(ConfirmUploadInputSchema).output(ConfirmUploadVOSchema),
|
|
222
|
-
list: oc.route({
|
|
223
|
-
method: "GET",
|
|
224
|
-
path: "/assets",
|
|
225
|
-
tags: ["Assets"],
|
|
226
|
-
summary: "List assets",
|
|
227
|
-
successDescription: "Every asset in the space, with file metadata and usage counts."
|
|
228
|
-
}).output(z.array(AssetVOSchema)),
|
|
229
|
-
get: oc.route({
|
|
230
|
-
method: "GET",
|
|
231
|
-
path: "/assets/{assetId}",
|
|
232
|
-
tags: ["Assets"],
|
|
233
|
-
summary: "Get asset detail",
|
|
234
|
-
successDescription: "Asset metadata plus every place it is referenced (where-used)."
|
|
235
|
-
}).input(z.object({ assetId: z.string() })).output(AssetDetailVOSchema),
|
|
236
|
-
updateMetadata: oc.route({
|
|
237
|
-
method: "PATCH",
|
|
238
|
-
path: "/assets/{assetId}/metadata",
|
|
239
|
-
tags: ["Assets"],
|
|
240
|
-
summary: "Update asset metadata",
|
|
241
|
-
successDescription: "Updated AI-readable metadata for a file, such as summary, tags, source URL, or schema-specific hints. Large text does not live here \u2014 see putText / grep / readTextLines."
|
|
242
|
-
}).input(UpdateAssetMetadataInputSchema).output(AssetDetailVOSchema),
|
|
243
|
-
delete: oc.route({
|
|
244
|
-
method: "DELETE",
|
|
245
|
-
path: "/assets/{assetId}",
|
|
246
|
-
tags: ["Assets"],
|
|
247
|
-
summary: "Delete asset",
|
|
248
|
-
successDescription: "Removed the asset and, if no other row references its bytes, the stored object. Refused while the asset is still referenced (where-used)."
|
|
249
|
-
}).input(z.object({ assetId: z.string() })).output(z.object({ deleted: z.boolean() })),
|
|
250
|
-
// ── Drive Grep Retrieval ─────────────────────────────────────────────────
|
|
251
|
-
// Busabase stores, indexes, and searches text; it never generates it. Text
|
|
252
|
-
// always arrives via putText — an agent's own extractor, or (future) an
|
|
253
|
-
// Outgoing-Hook-triggered service — never a bundled parser/OCR library.
|
|
254
|
-
putText: oc.route({
|
|
255
|
-
method: "PUT",
|
|
256
|
-
path: "/assets/{assetId}/text",
|
|
257
|
-
tags: ["Assets"],
|
|
258
|
-
summary: "Write (or mark none) an asset's text slot",
|
|
259
|
-
successDescription: "Text slot updated: inline body (\u22641MB), or bound from a presigned upload (server-verified content hash, hash-poisoning-safe), or marked `none` for files with no extractable text. Direct write, audit-logged, not ChangeRequest-gated."
|
|
260
|
-
}).input(PutTextInputSchema).output(AssetTextVOSchema),
|
|
261
|
-
createTextUploadUrl: oc.route({
|
|
262
|
-
method: "POST",
|
|
263
|
-
path: "/assets/text/upload-urls",
|
|
264
|
-
tags: ["Assets"],
|
|
265
|
-
summary: "Request a presigned upload URL for large text",
|
|
266
|
-
successDescription: "Presigned (or dev) upload URL for a temporary text object; PUT the bytes there, then call putText with the returned storageKey to bind, verify, and content-address it."
|
|
267
|
-
}).input(CreateTextUploadUrlInputSchema).output(CreateTextUploadUrlVOSchema),
|
|
268
|
-
grep: oc.route({
|
|
269
|
-
method: "POST",
|
|
270
|
-
path: "/assets/grep",
|
|
271
|
-
tags: ["Assets"],
|
|
272
|
-
summary: "Search every text-bearing asset in scope",
|
|
273
|
-
successDescription: "Streaming regex/literal matches with real file + line + column numbers and context, across every asset with text \u2014 any size, no 256KB cap. Honest coverage: missing/stale/unsearchable/errored name assets that were not (fully or successfully) searched, notReached counts present assets the scan never got to, and truncated flags a capped response."
|
|
274
|
-
}).input(GrepInputSchema).output(GrepResultVOSchema),
|
|
275
|
-
readTextLines: oc.route({
|
|
276
|
-
method: "GET",
|
|
277
|
-
path: "/assets/{assetId}/text/lines",
|
|
278
|
-
tags: ["Assets"],
|
|
279
|
-
summary: "Read an exact line range from an asset's text",
|
|
280
|
-
successDescription: "Lines [startLine, endLine] (range capped at 2000 lines / ~2MB response) read via a storage byte-range request \u2014 the server never loads the whole object, even for a multi-GB file."
|
|
281
|
-
}).input(ReadTextLinesInputSchema).output(ReadLinesVOSchema)
|
|
282
|
-
};
|
|
283
|
-
var i18n = {
|
|
284
|
-
locales: ["en", "zh-CN", "zh-TW", "ja", "ko", "de", "fr", "es", "pt"]};
|
|
285
|
-
var LocaleSchema = z.enum(i18n.locales);
|
|
286
229
|
|
|
287
|
-
// ../../packages/
|
|
288
|
-
var
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
iStringRecordSchema.refine(
|
|
293
|
-
(record) => Object.values(record).some((value) => value && value.trim().length > 0),
|
|
294
|
-
"field name must have at least one non-empty locale value"
|
|
295
|
-
)
|
|
296
|
-
]);
|
|
297
|
-
var fieldTypeSchema = z.enum([
|
|
298
|
-
"text",
|
|
299
|
-
"longtext",
|
|
300
|
-
"markdown",
|
|
301
|
-
"html",
|
|
302
|
-
"attachment",
|
|
303
|
-
"relation",
|
|
304
|
-
"number",
|
|
305
|
-
"date",
|
|
306
|
-
"checkbox",
|
|
307
|
-
"select",
|
|
308
|
-
"multiselect",
|
|
309
|
-
"url",
|
|
310
|
-
"embed",
|
|
311
|
-
"email",
|
|
312
|
-
"phone",
|
|
313
|
-
"created_time",
|
|
314
|
-
"updated_time",
|
|
315
|
-
"created_by",
|
|
316
|
-
"updated_by",
|
|
317
|
-
"auto_number",
|
|
318
|
-
"ai_summary",
|
|
319
|
-
"ai_tags",
|
|
320
|
-
"code",
|
|
321
|
-
"json",
|
|
322
|
-
"yaml"
|
|
323
|
-
]);
|
|
324
|
-
var fieldOptionsSchema = z.object({
|
|
325
|
-
ai: z.object({
|
|
326
|
-
model: z.string().optional(),
|
|
327
|
-
prompt: z.string().optional(),
|
|
328
|
-
reviewRequired: z.boolean().optional(),
|
|
329
|
-
sourceFieldIds: z.array(z.string()).optional()
|
|
330
|
-
}).optional(),
|
|
331
|
-
// Per-field config for `attachment` columns (all optional; logic enforces a
|
|
332
|
-
// 25MB ceiling regardless).
|
|
333
|
-
attachment: z.object({
|
|
334
|
-
maxFiles: z.number().int().positive().optional(),
|
|
335
|
-
allowedMimeTypes: z.array(z.string()).optional(),
|
|
336
|
-
maxFileSize: z.number().int().positive().optional()
|
|
337
|
-
}).optional(),
|
|
338
|
-
choices: z.array(
|
|
339
|
-
z.object({
|
|
340
|
-
color: z.string().optional(),
|
|
341
|
-
id: z.string(),
|
|
342
|
-
name: z.string()
|
|
343
|
-
})
|
|
344
|
-
).optional(),
|
|
345
|
-
code: z.object({
|
|
346
|
-
language: z.string().optional()
|
|
347
|
-
}).optional(),
|
|
348
|
-
embed: z.object({
|
|
349
|
-
aspectRatio: z.enum(["16:9", "4:3", "1:1"]).optional(),
|
|
350
|
-
height: z.number().int().positive().max(1200).optional(),
|
|
351
|
-
providers: z.array(z.string()).optional()
|
|
352
|
-
}).optional(),
|
|
353
|
-
inverseFieldId: z.string().optional(),
|
|
354
|
-
multiple: z.boolean().optional(),
|
|
355
|
-
// Display formatting for `number` columns (Notion-style: one number type,
|
|
356
|
-
// a format option). `currency` renders via Intl.NumberFormat.
|
|
357
|
-
number: z.object({
|
|
358
|
-
format: z.enum(["plain", "currency"]).optional(),
|
|
359
|
-
currency: z.string().optional(),
|
|
360
|
-
locale: z.string().optional()
|
|
361
|
-
}).optional(),
|
|
362
|
-
targetBaseId: z.string().optional().describe("Relation target Base id (bse_\u2026). Or pass targetBaseSlug to name it by slug."),
|
|
363
|
-
targetBaseSlug: z.string().optional().describe(
|
|
364
|
-
"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."
|
|
365
|
-
)
|
|
366
|
-
}).default({});
|
|
367
|
-
var baseFieldSchema = z.object({
|
|
368
|
-
id: z.string(),
|
|
369
|
-
baseId: z.string(),
|
|
370
|
-
slug: z.string(),
|
|
371
|
-
name: fieldNameSchema,
|
|
372
|
-
type: fieldTypeSchema,
|
|
373
|
-
required: z.boolean(),
|
|
374
|
-
position: z.number(),
|
|
375
|
-
options: fieldOptionsSchema
|
|
376
|
-
});
|
|
377
|
-
var baseSchema = z.object({
|
|
378
|
-
id: z.string(),
|
|
379
|
-
nodeId: z.string(),
|
|
380
|
-
slug: z.string(),
|
|
381
|
-
name: z.string(),
|
|
382
|
-
description: z.string(),
|
|
383
|
-
reviewPolicy: z.object({
|
|
384
|
-
kind: z.literal("single"),
|
|
385
|
-
requiredApprovals: z.number()
|
|
386
|
-
}),
|
|
387
|
-
createdAt: z.string(),
|
|
388
|
-
fields: z.array(baseFieldSchema)
|
|
389
|
-
});
|
|
390
|
-
var createBaseInputSchema = z.object({
|
|
391
|
-
parentNodeId: z.string().optional(),
|
|
392
|
-
slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
|
|
393
|
-
name: z.string().min(1),
|
|
394
|
-
description: z.string().optional().default(""),
|
|
395
|
-
fields: z.array(
|
|
396
|
-
z.object({
|
|
397
|
-
slug: z.string().min(1),
|
|
398
|
-
name: fieldNameSchema,
|
|
399
|
-
type: fieldTypeSchema.default("text"),
|
|
400
|
-
required: z.boolean().default(false),
|
|
401
|
-
options: fieldOptionsSchema.optional().default({})
|
|
402
|
-
})
|
|
403
|
-
).default([]),
|
|
404
|
-
// Review-first by default: without `autoMerge: true`, this proposes the Base
|
|
405
|
-
// as a pending ChangeRequest (status "in_review") instead of creating it
|
|
406
|
-
// immediately. Pass `autoMerge: true` only for callers that don't need human
|
|
407
|
-
// review (seed/migration scripts, an explicit no-review agent task).
|
|
408
|
-
autoMerge: z.boolean().optional().default(false)
|
|
409
|
-
});
|
|
410
|
-
var createBaseFieldInputSchema = z.object({
|
|
411
|
-
name: fieldNameSchema,
|
|
412
|
-
// Field slugs are snake_case identifiers (e.g. `cover_image`, `publish_date`) — the
|
|
413
|
-
// seed and the inline-fields path on `POST /bases` already allow underscores, so the
|
|
414
|
-
// add-field endpoint must too. (Base/folder/view slugs stay kebab-case: they go in URLs.)
|
|
415
|
-
slug: z.string().min(1).regex(/^[a-z0-9_-]+$/),
|
|
416
|
-
type: fieldTypeSchema.default("text"),
|
|
417
|
-
required: z.boolean().optional().default(false),
|
|
418
|
-
options: fieldOptionsSchema.optional().default({})
|
|
419
|
-
});
|
|
420
|
-
var createFieldChangeRequestInputSchema = createBaseFieldInputSchema.extend({
|
|
421
|
-
message: z.string().optional().default("Add field"),
|
|
422
|
-
submittedBy: z.string().optional().default("local-editor")
|
|
423
|
-
});
|
|
424
|
-
var deleteFieldChangeRequestInputSchema = z.object({
|
|
425
|
-
fieldId: z.string().min(1),
|
|
426
|
-
message: z.string().optional(),
|
|
427
|
-
submittedBy: z.string().optional().default("local-editor")
|
|
428
|
-
});
|
|
429
|
-
var updateFieldChangeRequestInputSchema = z.object({
|
|
430
|
-
fieldId: z.string().min(1),
|
|
431
|
-
patch: z.object({
|
|
432
|
-
name: fieldNameSchema.optional(),
|
|
433
|
-
required: z.boolean().optional(),
|
|
434
|
-
options: fieldOptionsSchema.optional()
|
|
435
|
-
}),
|
|
436
|
-
message: z.string().optional(),
|
|
437
|
-
submittedBy: z.string().optional().default("local-editor")
|
|
438
|
-
});
|
|
439
|
-
var previewFieldConversionInputSchema = z.object({
|
|
440
|
-
fieldId: z.string().min(1),
|
|
441
|
-
newType: fieldTypeSchema
|
|
442
|
-
});
|
|
443
|
-
var previewFieldConversionOutputSchema = z.object({
|
|
444
|
-
totalCount: z.number(),
|
|
445
|
-
convertibleCount: z.number(),
|
|
446
|
-
nullCount: z.number(),
|
|
447
|
-
conflicts: z.array(z.object({ recordId: z.string(), currentValue: z.unknown() }))
|
|
448
|
-
});
|
|
449
|
-
var convertFieldChangeRequestInputSchema = z.object({
|
|
450
|
-
fieldId: z.string().min(1),
|
|
451
|
-
newType: fieldTypeSchema,
|
|
452
|
-
selectChoiceMode: z.enum(["auto_create", "null_on_missing"]).default("null_on_missing"),
|
|
453
|
-
message: z.string().optional(),
|
|
454
|
-
submittedBy: z.string().optional().default("local-editor")
|
|
455
|
-
});
|
|
456
|
-
var reorderFieldsChangeRequestInputSchema = z.object({
|
|
457
|
-
fieldIds: z.array(z.string()).min(1),
|
|
458
|
-
message: z.string().optional(),
|
|
459
|
-
submittedBy: z.string().optional().default("local-editor")
|
|
460
|
-
});
|
|
461
|
-
var archiveBaseInputSchema = z.object({
|
|
462
|
-
message: z.string().optional(),
|
|
463
|
-
submittedBy: z.string().optional().default("local-editor")
|
|
464
|
-
});
|
|
465
|
-
var restoreBaseInputSchema = z.object({
|
|
466
|
-
message: z.string().optional(),
|
|
467
|
-
submittedBy: z.string().optional().default("local-editor")
|
|
468
|
-
});
|
|
469
|
-
var restoreFieldChangeRequestInputSchema = z.object({
|
|
470
|
-
fieldId: z.string().min(1),
|
|
471
|
-
message: z.string().optional(),
|
|
472
|
-
submittedBy: z.string().optional().default("local-editor")
|
|
473
|
-
});
|
|
230
|
+
// ../../packages/busabase-contract/src/domains/airapp/definition.ts
|
|
231
|
+
var airappNodeType = makeFileTreeNodeType({
|
|
232
|
+
type: "airapp",
|
|
233
|
+
label: "AirApp",
|
|
234
|
+
icon: "app-window"});
|
|
474
235
|
|
|
475
236
|
// ../../packages/busabase-contract/src/domains/base/definition.ts
|
|
476
237
|
var baseNodeType = {
|
|
@@ -566,37 +327,6 @@ var docNodeType = {
|
|
|
566
327
|
]
|
|
567
328
|
};
|
|
568
329
|
|
|
569
|
-
// ../../packages/busabase-contract/src/domains/filetree/definition.ts
|
|
570
|
-
var fileTreeOperations = (type) => [
|
|
571
|
-
{
|
|
572
|
-
kind: `${type}_file_create`,
|
|
573
|
-
label: "Create file",
|
|
574
|
-
tone: "border-emerald-200 bg-emerald-50 text-emerald-800"
|
|
575
|
-
},
|
|
576
|
-
{
|
|
577
|
-
kind: `${type}_file_update`,
|
|
578
|
-
label: "Update file",
|
|
579
|
-
tone: "border-blue-200 bg-blue-50 text-blue-800"
|
|
580
|
-
},
|
|
581
|
-
{
|
|
582
|
-
kind: `${type}_file_delete`,
|
|
583
|
-
label: "Delete file",
|
|
584
|
-
tone: "border-rose-200 bg-rose-50 text-rose-800"
|
|
585
|
-
},
|
|
586
|
-
{
|
|
587
|
-
kind: `${type}_metadata_update`,
|
|
588
|
-
label: `Update ${type}`,
|
|
589
|
-
tone: "border-violet-200 bg-violet-50 text-violet-800"
|
|
590
|
-
}
|
|
591
|
-
];
|
|
592
|
-
var makeFileTreeNodeType = (config) => ({
|
|
593
|
-
type: config.type,
|
|
594
|
-
label: config.label,
|
|
595
|
-
icon: config.icon,
|
|
596
|
-
capabilities: { hasDetail: true, creatable: true },
|
|
597
|
-
operations: fileTreeOperations(config.type)
|
|
598
|
-
});
|
|
599
|
-
|
|
600
330
|
// ../../packages/busabase-contract/src/domains/drive/definition.ts
|
|
601
331
|
var driveNodeType = makeFileTreeNodeType({
|
|
602
332
|
type: "drive",
|
|
@@ -652,6 +382,7 @@ var BUILTIN_NODE_TYPES = [
|
|
|
652
382
|
baseNodeType,
|
|
653
383
|
skillNodeType,
|
|
654
384
|
driveNodeType,
|
|
385
|
+
airappNodeType,
|
|
655
386
|
fileNodeType,
|
|
656
387
|
docNodeType
|
|
657
388
|
];
|
|
@@ -698,9 +429,23 @@ var nodeSchema = z.lazy(
|
|
|
698
429
|
createdAt: z.string(),
|
|
699
430
|
updatedAt: z.string(),
|
|
700
431
|
baseId: z.string().nullable(),
|
|
701
|
-
children: z.array(nodeSchema)
|
|
432
|
+
children: z.array(nodeSchema),
|
|
433
|
+
hasChildren: z.boolean().optional()
|
|
702
434
|
})
|
|
703
435
|
);
|
|
436
|
+
var listNodesInputSchema = z.object({
|
|
437
|
+
parentId: z.string().nullable().optional().describe("Node to start from. Omit or null to start from the space root."),
|
|
438
|
+
depth: z.coerce.number().int().min(1).max(5).optional().describe(
|
|
439
|
+
"How many levels beneath the start point to eagerly include (default 2 once either field is set). Capped at 5."
|
|
440
|
+
)
|
|
441
|
+
}).optional();
|
|
442
|
+
var isDescendantInputSchema = z.object({
|
|
443
|
+
nodeId: z.string(),
|
|
444
|
+
potentialAncestorId: z.string()
|
|
445
|
+
});
|
|
446
|
+
var isDescendantOutputSchema = z.object({
|
|
447
|
+
isDescendant: z.boolean()
|
|
448
|
+
});
|
|
704
449
|
var userRefSchema = z.object({
|
|
705
450
|
id: z.string(),
|
|
706
451
|
name: z.string().nullable(),
|
|
@@ -872,6 +617,7 @@ var auditActionSchema = z.enum([
|
|
|
872
617
|
"file.created",
|
|
873
618
|
"skill.created",
|
|
874
619
|
"drive.created",
|
|
620
|
+
"airapp.created",
|
|
875
621
|
"asset.deleted",
|
|
876
622
|
"asset.metadata_updated",
|
|
877
623
|
"asset.text_written",
|
|
@@ -961,6 +707,13 @@ var createNodeChangeRequestInputSchema = z.object({
|
|
|
961
707
|
),
|
|
962
708
|
operations: z.array(nodeOperationInputSchema).min(1)
|
|
963
709
|
});
|
|
710
|
+
var moveNodeInputSchema = z.object({
|
|
711
|
+
nodeId: z.string(),
|
|
712
|
+
parentNodeId: z.string().optional().describe("New parent folder node id. Omit to keep the current parent and only reorder."),
|
|
713
|
+
position: z.number().int().optional().describe("New position among the target parent's children."),
|
|
714
|
+
message: z.string().optional().describe("Reviewer-facing Change Request message."),
|
|
715
|
+
submittedBy: z.string().optional()
|
|
716
|
+
});
|
|
964
717
|
var createDeleteChangeRequestInputSchema = z.object({
|
|
965
718
|
message: z.string().optional().default("Delete record").describe(
|
|
966
719
|
'Explanation shown to the human reviewer. Say what is being removed and why, e.g. "Archive duplicate contact \u2014 merged into Acme Corp".'
|
|
@@ -970,85 +723,567 @@ var createDeleteChangeRequestInputSchema = z.object({
|
|
|
970
723
|
// implemented, so the API no longer accepts it (breaking change).
|
|
971
724
|
deleteMode: z.enum(["archive"]).optional().default("archive")
|
|
972
725
|
});
|
|
973
|
-
var reviseOperationInputSchema = z.object({
|
|
974
|
-
fields: z.record(z.string(), z.unknown()).describe(
|
|
975
|
-
"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."
|
|
976
|
-
),
|
|
977
|
-
message: z.string().optional().default("Revise operation").describe(
|
|
978
|
-
'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".'
|
|
979
|
-
),
|
|
980
|
-
author: z.string().optional().default("local-producer"),
|
|
981
|
-
baseCommitId: z.string().optional()
|
|
726
|
+
var reviseOperationInputSchema = z.object({
|
|
727
|
+
fields: z.record(z.string(), z.unknown()).describe(
|
|
728
|
+
"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."
|
|
729
|
+
),
|
|
730
|
+
message: z.string().optional().default("Revise operation").describe(
|
|
731
|
+
'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".'
|
|
732
|
+
),
|
|
733
|
+
author: z.string().optional().default("local-producer"),
|
|
734
|
+
baseCommitId: z.string().optional()
|
|
735
|
+
});
|
|
736
|
+
var reviewChangeRequestInputSchema = z.object({
|
|
737
|
+
verdict: z.enum(["approved", "rejected"]),
|
|
738
|
+
reason: z.string().optional()
|
|
739
|
+
});
|
|
740
|
+
var commentSubjectInputSchema = z.object({
|
|
741
|
+
subjectType: commentSubjectTypeSchema,
|
|
742
|
+
subjectId: z.string().min(1)
|
|
743
|
+
});
|
|
744
|
+
var createCommentInputSchema = commentSubjectInputSchema.extend({
|
|
745
|
+
authorId: z.string().optional().default("local-admin"),
|
|
746
|
+
body: z.string().trim().min(1),
|
|
747
|
+
mentionsAi: z.boolean().optional().default(false)
|
|
748
|
+
});
|
|
749
|
+
var listInputSchema = z.object({
|
|
750
|
+
limit: z.coerce.number().int().min(1).max(100).optional().default(50)
|
|
751
|
+
}).optional().default({ limit: 50 });
|
|
752
|
+
var listChangeRequestsPagedInputSchema = z.object({
|
|
753
|
+
limit: z.coerce.number().int().min(1).max(100).optional().default(50),
|
|
754
|
+
/** Opaque base64 cursor (`createdAt|id`) for keyset pagination. */
|
|
755
|
+
cursor: z.string().optional(),
|
|
756
|
+
status: z.array(changeRequestStatusSchema).optional(),
|
|
757
|
+
mine: z.boolean().optional()
|
|
758
|
+
}).optional().default({ limit: 50 });
|
|
759
|
+
var listChangeRequestsResponseSchema = z.object({
|
|
760
|
+
changeRequests: z.array(changeRequestSchema),
|
|
761
|
+
nextCursor: z.string().nullable()
|
|
762
|
+
});
|
|
763
|
+
var changeRequestCountsSchema = z.object({
|
|
764
|
+
review: z.number().int().nonnegative(),
|
|
765
|
+
changes: z.number().int().nonnegative(),
|
|
766
|
+
created: z.number().int().nonnegative(),
|
|
767
|
+
approved: z.number().int().nonnegative(),
|
|
768
|
+
merged: z.number().int().nonnegative(),
|
|
769
|
+
rejected: z.number().int().nonnegative()
|
|
770
|
+
});
|
|
771
|
+
var SEARCH_SOURCES = ["records", "files", "names"];
|
|
772
|
+
var searchInputSchema = z.object({
|
|
773
|
+
query: z.string().default(""),
|
|
774
|
+
limit: z.coerce.number().int().min(1).max(100).optional().default(20),
|
|
775
|
+
offset: z.coerce.number().int().min(0).optional().default(0),
|
|
776
|
+
/**
|
|
777
|
+
* Restrict which content this call searches. Omitted means all three
|
|
778
|
+
* (unchanged behavior for every caller before this parameter existed).
|
|
779
|
+
*
|
|
780
|
+
* A GET query param that occurs exactly once (`?sources=records`) arrives
|
|
781
|
+
* as a bare string, not a 1-element array — only a REPEATED occurrence
|
|
782
|
+
* (`?sources=records&sources=files`) becomes an array. Accept both shapes
|
|
783
|
+
* and normalize to an array.
|
|
784
|
+
*/
|
|
785
|
+
sources: z.union([z.array(z.enum(SEARCH_SOURCES)), z.enum(SEARCH_SOURCES)]).transform((value) => Array.isArray(value) ? value : [value]).optional()
|
|
786
|
+
});
|
|
787
|
+
var authSpaceSchema = z.object({
|
|
788
|
+
id: z.string(),
|
|
789
|
+
name: z.string(),
|
|
790
|
+
slug: z.string().nullable(),
|
|
791
|
+
plan: z.string().nullable()
|
|
792
|
+
});
|
|
793
|
+
var authUserSchema = z.object({
|
|
794
|
+
id: z.string(),
|
|
795
|
+
name: z.string(),
|
|
796
|
+
email: z.string().nullable(),
|
|
797
|
+
image: z.string().nullable()
|
|
798
|
+
});
|
|
799
|
+
var authMemberSchema = z.object({
|
|
800
|
+
userId: z.string(),
|
|
801
|
+
spaceId: z.string(),
|
|
802
|
+
role: z.string()
|
|
803
|
+
});
|
|
804
|
+
var authInfoSchema = z.object({
|
|
805
|
+
/** The space this request is scoped to (explicit `x-busabase-space` header, else the default). */
|
|
806
|
+
space: authSpaceSchema,
|
|
807
|
+
user: authUserSchema,
|
|
808
|
+
member: authMemberSchema,
|
|
809
|
+
/**
|
|
810
|
+
* Every space the authenticated user belongs to. An API key is user-scoped, not
|
|
811
|
+
* space-scoped — when this has more than one entry, callers should confirm the
|
|
812
|
+
* intended space and target it explicitly via the `x-busabase-space` header.
|
|
813
|
+
*/
|
|
814
|
+
spaces: z.array(authSpaceSchema)
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
// ../../packages/busabase-contract/src/domains/filetree/contract.ts
|
|
818
|
+
var fileTreeFileSchema = z.object({
|
|
819
|
+
path: z.string(),
|
|
820
|
+
name: z.string(),
|
|
821
|
+
size: z.number(),
|
|
822
|
+
updatedAt: z.string().nullable(),
|
|
823
|
+
mimeType: z.string().nullable(),
|
|
824
|
+
assetId: z.string(),
|
|
825
|
+
displayName: z.string().nullable()
|
|
826
|
+
});
|
|
827
|
+
var assetFileInputSchema = z.object({
|
|
828
|
+
path: z.string().min(1),
|
|
829
|
+
assetId: z.string().min(1),
|
|
830
|
+
displayName: z.string().optional(),
|
|
831
|
+
mimeType: z.string().optional()
|
|
832
|
+
}).strict();
|
|
833
|
+
var textFileInputSchema = z.object({
|
|
834
|
+
path: z.string().min(1),
|
|
835
|
+
content: z.string().default(""),
|
|
836
|
+
mimeType: z.string().optional()
|
|
837
|
+
}).strict();
|
|
838
|
+
var assetFileOperationInputSchema = z.object({
|
|
839
|
+
kind: z.enum(["create", "update"]),
|
|
840
|
+
path: z.string().min(1),
|
|
841
|
+
assetId: z.string().min(1),
|
|
842
|
+
displayName: z.string().optional(),
|
|
843
|
+
mimeType: z.string().optional(),
|
|
844
|
+
baseContentHash: z.string().optional()
|
|
845
|
+
}).strict();
|
|
846
|
+
var textFileOperationInputSchema = z.object({
|
|
847
|
+
kind: z.enum(["create", "update"]),
|
|
848
|
+
path: z.string().min(1),
|
|
849
|
+
content: z.string(),
|
|
850
|
+
mimeType: z.string().optional(),
|
|
851
|
+
baseContentHash: z.string().optional()
|
|
852
|
+
}).strict();
|
|
853
|
+
var fileTreeNodeSchema = z.object({
|
|
854
|
+
node: nodeSchema,
|
|
855
|
+
entryFile: z.string(),
|
|
856
|
+
visibility: z.enum(["private", "workspace", "public"]),
|
|
857
|
+
version: z.string(),
|
|
858
|
+
files: z.array(fileTreeFileSchema)
|
|
859
|
+
});
|
|
860
|
+
var createFileTreeInputSchema = z.object({
|
|
861
|
+
parentNodeId: z.string().optional(),
|
|
862
|
+
slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
|
|
863
|
+
name: z.string().min(1),
|
|
864
|
+
description: z.string().optional().default(""),
|
|
865
|
+
visibility: z.enum(["private", "workspace", "public"]).optional().default("private"),
|
|
866
|
+
version: z.string().optional().default("0.1.0"),
|
|
867
|
+
files: z.array(z.union([assetFileInputSchema, textFileInputSchema])).optional().default([]),
|
|
868
|
+
// Review-first by default: without `autoMerge: true`, this proposes the node
|
|
869
|
+
// as a pending ChangeRequest (status "in_review") instead of creating it
|
|
870
|
+
// immediately. Pass `autoMerge: true` only for callers that don't need human
|
|
871
|
+
// review (seed/migration scripts, an explicit no-review agent task).
|
|
872
|
+
autoMerge: z.boolean().optional().default(false),
|
|
873
|
+
// "merge" (default): `files` is layered on top of the config's default seed
|
|
874
|
+
// files by path — a caller supplying just a couple of extra files (e.g. a
|
|
875
|
+
// Skill's own reference doc) still gets the default scaffold (SKILL.md,
|
|
876
|
+
// skill.json, ...) for any path they didn't provide themselves. "replace":
|
|
877
|
+
// `files` replaces the defaults entirely — for a caller handing over a
|
|
878
|
+
// complete, different-shaped project (e.g. an AirApp seeded with a Vite
|
|
879
|
+
// project instead of the default Hono template) who does NOT want leftover
|
|
880
|
+
// default files with unrelated content mixed in.
|
|
881
|
+
mergeMode: z.enum(["merge", "replace"]).optional().default("merge")
|
|
882
|
+
});
|
|
883
|
+
var fileTreeFileOperationInputSchema = z.union([
|
|
884
|
+
assetFileOperationInputSchema,
|
|
885
|
+
textFileOperationInputSchema,
|
|
886
|
+
z.object({
|
|
887
|
+
kind: z.literal("delete"),
|
|
888
|
+
path: z.string().min(1),
|
|
889
|
+
baseContentHash: z.string().optional()
|
|
890
|
+
}).strict(),
|
|
891
|
+
z.object({
|
|
892
|
+
kind: z.literal("metadata_update"),
|
|
893
|
+
metadata: z.object({
|
|
894
|
+
entryFile: z.string().optional(),
|
|
895
|
+
visibility: z.enum(["private", "workspace", "public"]).optional(),
|
|
896
|
+
version: z.string().optional()
|
|
897
|
+
}).default({})
|
|
898
|
+
}).strict()
|
|
899
|
+
]);
|
|
900
|
+
var createFileTreeChangeRequestInputSchema = z.object({
|
|
901
|
+
message: z.string().optional().default("Update file tree").describe(
|
|
902
|
+
'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".'
|
|
903
|
+
),
|
|
904
|
+
submittedBy: z.string().optional().default("local-producer"),
|
|
905
|
+
operations: z.array(fileTreeFileOperationInputSchema).min(1)
|
|
906
|
+
});
|
|
907
|
+
var makeFileTreeContract = (routeBase, tag) => {
|
|
908
|
+
const label = tag.endsWith("s") ? tag.slice(0, -1) : tag;
|
|
909
|
+
const basePath = `/${routeBase}`;
|
|
910
|
+
return {
|
|
911
|
+
list: oc.route({
|
|
912
|
+
method: "GET",
|
|
913
|
+
path: basePath,
|
|
914
|
+
tags: [tag],
|
|
915
|
+
summary: `List ${label} nodes`,
|
|
916
|
+
successDescription: `${label} nodes with their Asset-backed file trees.`
|
|
917
|
+
}).output(z.array(fileTreeNodeSchema)),
|
|
918
|
+
create: oc.route({
|
|
919
|
+
method: "POST",
|
|
920
|
+
path: basePath,
|
|
921
|
+
tags: [tag],
|
|
922
|
+
summary: `Create ${label} node`,
|
|
923
|
+
successDescription: `Review-first by default: a pending ChangeRequest proposing the ${label} node. Returns the materialized ${label} node instead when \`autoMerge: true\` is passed.`
|
|
924
|
+
}).input(createFileTreeInputSchema).output(z.union([fileTreeNodeSchema, changeRequestSchema])),
|
|
925
|
+
get: oc.route({
|
|
926
|
+
method: "GET",
|
|
927
|
+
path: `${basePath}/{nodeId}`,
|
|
928
|
+
tags: [tag],
|
|
929
|
+
summary: `Get ${label} node`,
|
|
930
|
+
successDescription: `${label} node detail and file tree.`
|
|
931
|
+
}).input(z.object({ nodeId: z.string() })).output(fileTreeNodeSchema),
|
|
932
|
+
listFiles: oc.route({
|
|
933
|
+
method: "GET",
|
|
934
|
+
path: `${basePath}/{nodeId}/files`,
|
|
935
|
+
tags: [tag],
|
|
936
|
+
summary: `List ${label} files`,
|
|
937
|
+
successDescription: `Asset-backed files mounted under the ${label} node.`
|
|
938
|
+
}).input(z.object({ nodeId: z.string() })).output(z.array(fileTreeFileSchema)),
|
|
939
|
+
readFile: oc.route({
|
|
940
|
+
method: "GET",
|
|
941
|
+
path: `${basePath}/{nodeId}/files/{+filePath}`,
|
|
942
|
+
tags: [tag],
|
|
943
|
+
summary: `Read ${label} file`,
|
|
944
|
+
successDescription: `${label} file content and content hash.`
|
|
945
|
+
}).input(z.object({ nodeId: z.string(), filePath: z.string() })).output(
|
|
946
|
+
z.object({
|
|
947
|
+
nodeId: z.string(),
|
|
948
|
+
path: z.string(),
|
|
949
|
+
encoding: z.enum(["utf8", "url"]),
|
|
950
|
+
content: z.string(),
|
|
951
|
+
mimeType: z.string(),
|
|
952
|
+
assetId: z.string(),
|
|
953
|
+
displayName: z.string().nullable(),
|
|
954
|
+
assetUrl: z.string().nullable(),
|
|
955
|
+
contentHash: z.string()
|
|
956
|
+
})
|
|
957
|
+
),
|
|
958
|
+
createChangeRequest: oc.route({
|
|
959
|
+
method: "POST",
|
|
960
|
+
path: `${basePath}/{nodeId}/change-requests`,
|
|
961
|
+
tags: [tag, "Change Requests"],
|
|
962
|
+
summary: `Create ${label} change request`,
|
|
963
|
+
successDescription: `Created file-tree change request for a ${label} node.`
|
|
964
|
+
}).input(createFileTreeChangeRequestInputSchema.extend({ nodeId: z.string() })).output(changeRequestSchema)
|
|
965
|
+
};
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
// ../../packages/busabase-contract/src/domains/airapp/contract.ts
|
|
969
|
+
var airappContract = makeFileTreeContract("airapps", "AirApps");
|
|
970
|
+
var AttachmentMetadataSchema = z.record(z.string(), z.unknown());
|
|
971
|
+
z.object({
|
|
972
|
+
id: z.string(),
|
|
973
|
+
url: z.string(),
|
|
974
|
+
fileName: z.string(),
|
|
975
|
+
mimeType: z.string(),
|
|
976
|
+
size: z.number().int().nonnegative()
|
|
977
|
+
});
|
|
978
|
+
var RequestUploadUrlInputSchema = z.object({
|
|
979
|
+
fileName: z.string().min(1).max(255),
|
|
980
|
+
mimeType: z.string().min(1),
|
|
981
|
+
sizeBytes: z.number().int().positive(),
|
|
982
|
+
spaceId: z.string().optional(),
|
|
983
|
+
context: z.string().regex(/^[a-zA-Z0-9_/-]+$/).max(100).optional(),
|
|
984
|
+
/** Content fingerprint (e.g. "sha256:<hex>") for dedup; computed client-side. */
|
|
985
|
+
contentHash: z.string().max(80).optional()
|
|
986
|
+
});
|
|
987
|
+
var ConfirmUploadInputSchema = z.object({
|
|
988
|
+
storageKey: z.string().min(1),
|
|
989
|
+
fileName: z.string().min(1).max(255),
|
|
990
|
+
mimeType: z.string().min(1),
|
|
991
|
+
sizeBytes: z.number().int().positive(),
|
|
992
|
+
spaceId: z.string().optional(),
|
|
993
|
+
context: z.string().regex(/^[a-zA-Z0-9_/-]+$/).max(100).optional(),
|
|
994
|
+
metadata: AttachmentMetadataSchema.optional(),
|
|
995
|
+
/** Content fingerprint (e.g. "sha256:<hex>") persisted for dedup. */
|
|
996
|
+
contentHash: z.string().max(80).optional()
|
|
997
|
+
});
|
|
998
|
+
var RequestUploadUrlVOSchema = z.object({
|
|
999
|
+
uploadUrl: z.string(),
|
|
1000
|
+
storageKey: z.string(),
|
|
1001
|
+
publicUrl: z.string(),
|
|
1002
|
+
expiresIn: z.number(),
|
|
1003
|
+
/**
|
|
1004
|
+
* True when an identical file (same contentHash, same scope) already exists.
|
|
1005
|
+
* The client should SKIP the byte upload and the confirm step, and use
|
|
1006
|
+
* `attachmentId`/`publicUrl` directly. `uploadUrl` is empty in this case.
|
|
1007
|
+
*/
|
|
1008
|
+
duplicate: z.boolean().optional(),
|
|
1009
|
+
/** Existing attachment id — present only when `duplicate` is true. */
|
|
1010
|
+
attachmentId: z.string().optional(),
|
|
1011
|
+
/** Host-specific logical asset id, when the app maps uploads into an Asset library. */
|
|
1012
|
+
assetId: z.string().optional()
|
|
1013
|
+
});
|
|
1014
|
+
var ConfirmUploadVOSchema = z.object({
|
|
1015
|
+
success: z.boolean(),
|
|
1016
|
+
attachmentId: z.string(),
|
|
1017
|
+
/** Host-specific logical asset id, when the app maps uploads into an Asset library. */
|
|
1018
|
+
assetId: z.string().optional(),
|
|
1019
|
+
storageKey: z.string(),
|
|
1020
|
+
publicUrl: z.string()
|
|
982
1021
|
});
|
|
983
|
-
var
|
|
984
|
-
|
|
985
|
-
|
|
1022
|
+
var AssetTextStatusSchema = z.enum(["missing", "present", "none", "stale"]);
|
|
1023
|
+
var AssetVOSchema = z.object({
|
|
1024
|
+
id: z.string(),
|
|
1025
|
+
attachmentId: z.string(),
|
|
1026
|
+
name: z.string(),
|
|
1027
|
+
contentKind: z.enum(["text", "binary"]),
|
|
1028
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
1029
|
+
fileName: z.string(),
|
|
1030
|
+
mimeType: z.string(),
|
|
1031
|
+
size: z.number().int().nonnegative(),
|
|
1032
|
+
url: z.string(),
|
|
1033
|
+
contentHash: z.string().nullable(),
|
|
1034
|
+
/** How many places reference this asset (Base records + Doc bodies). */
|
|
1035
|
+
usageCount: z.number().int().nonnegative(),
|
|
1036
|
+
/** Drive Grep Retrieval text-slot status — see {@link AssetTextStatusSchema}. */
|
|
1037
|
+
textStatus: AssetTextStatusSchema,
|
|
1038
|
+
createdAt: z.string()
|
|
986
1039
|
});
|
|
987
|
-
var
|
|
988
|
-
|
|
989
|
-
|
|
1040
|
+
var AssetUsageVOSchema = z.object({
|
|
1041
|
+
ownerType: z.enum(["drive", "skill", "airapp", "base", "doc", "file_node"]),
|
|
1042
|
+
nodeId: z.string(),
|
|
1043
|
+
nodeName: z.string(),
|
|
1044
|
+
nodeType: z.string(),
|
|
1045
|
+
/** Node slug, so the UI can link to the owning Base/Doc (`/{type}/{slug}`). */
|
|
1046
|
+
nodeSlug: z.string(),
|
|
1047
|
+
/** Drive/Skill mounted path, or null when the usage is not path-based. */
|
|
1048
|
+
path: z.string().nullable(),
|
|
1049
|
+
/** Base record id, or null for whole-node usages (e.g. a Doc body). */
|
|
1050
|
+
recordId: z.string().nullable(),
|
|
1051
|
+
/** Attachment field slug, or null for whole-node usages. */
|
|
1052
|
+
fieldSlug: z.string().nullable(),
|
|
1053
|
+
/** Doc block id, or null when the usage is not block-based. */
|
|
1054
|
+
blockId: z.string().nullable(),
|
|
1055
|
+
createdAt: z.string()
|
|
990
1056
|
});
|
|
991
|
-
var
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
mentionsAi: z.boolean().optional().default(false)
|
|
1057
|
+
var AssetDetailVOSchema = z.object({
|
|
1058
|
+
asset: AssetVOSchema,
|
|
1059
|
+
usages: z.array(AssetUsageVOSchema)
|
|
995
1060
|
});
|
|
996
|
-
var
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1061
|
+
var PutTextInputSchema = z.object({
|
|
1062
|
+
assetId: z.string(),
|
|
1063
|
+
/** Inline text body, ≤ 1 MB. For larger text, use `createTextUploadUrl` + bind by `storageKey`. */
|
|
1064
|
+
text: z.string().optional(),
|
|
1065
|
+
/** Bind a presigned-uploaded text object (a temp `asset-texts/pending/*.txt` key). */
|
|
1066
|
+
storageKey: z.string().optional(),
|
|
1067
|
+
/**
|
|
1068
|
+
* Claimed content hash for the `storageKey` bind path (`sha256:<hex>`, echoing
|
|
1069
|
+
* `createTextUploadUrl`'s input like `open-domains/attachments`' confirm step).
|
|
1070
|
+
* The server always computes the ACTUAL hash from the bytes during the
|
|
1071
|
+
* confirm scan and rejects a mismatch (hash-poisoning defense) — this field
|
|
1072
|
+
* is only an optional early-mismatch check, never trusted for addressing.
|
|
1073
|
+
*/
|
|
1074
|
+
contentHash: z.string().optional(),
|
|
1075
|
+
/** Mark as having no extractable text (e.g. a scanned, image-only PDF). */
|
|
1076
|
+
none: z.boolean().optional()
|
|
1009
1077
|
});
|
|
1010
|
-
var
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
rejected: z.number().int().nonnegative()
|
|
1078
|
+
var AssetTextVOSchema = z.object({
|
|
1079
|
+
assetId: z.string(),
|
|
1080
|
+
textStatus: AssetTextStatusSchema,
|
|
1081
|
+
lineCount: z.number().int().nonnegative(),
|
|
1082
|
+
charCount: z.number().int().nonnegative(),
|
|
1083
|
+
byteCount: z.number().int().nonnegative()
|
|
1017
1084
|
});
|
|
1018
|
-
var
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1085
|
+
var CreateTextUploadUrlInputSchema = z.object({
|
|
1086
|
+
assetId: z.string(),
|
|
1087
|
+
sizeBytes: z.number().int().positive(),
|
|
1088
|
+
/** Optional claim, mirrors `RequestUploadUrlDTO.contentHash` (never trusted for addressing). */
|
|
1089
|
+
contentHash: z.string().optional()
|
|
1022
1090
|
});
|
|
1023
|
-
var
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
plan: z.string().nullable()
|
|
1091
|
+
var CreateTextUploadUrlVOSchema = z.object({
|
|
1092
|
+
uploadUrl: z.string(),
|
|
1093
|
+
storageKey: z.string(),
|
|
1094
|
+
expiresIn: z.number().int().nonnegative()
|
|
1028
1095
|
});
|
|
1029
|
-
var
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1096
|
+
var GrepScopeSchema = z.object({
|
|
1097
|
+
assetIds: z.array(z.string()).optional(),
|
|
1098
|
+
/** Drive/Skill mounted path prefix (matches `busabase_asset_usages.path`). */
|
|
1099
|
+
drivePath: z.string().optional(),
|
|
1100
|
+
mimeTypes: z.array(z.string()).optional()
|
|
1034
1101
|
});
|
|
1035
|
-
var
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1102
|
+
var GREP_DEFAULT_MAX_MATCHES = 100;
|
|
1103
|
+
var GREP_HARD_MAX_MATCHES = 1e3;
|
|
1104
|
+
var GREP_DEFAULT_CONTEXT_LINES = 0;
|
|
1105
|
+
var GREP_MAX_CONTEXT_LINES = 10;
|
|
1106
|
+
var GrepInputSchema = z.object({
|
|
1107
|
+
pattern: z.string().min(1),
|
|
1108
|
+
/** JS RegExp flags, e.g. `"i"` for case-insensitive. `g`/`y` are ignored (grep always scans every match per line). */
|
|
1109
|
+
flags: z.string().optional().default(""),
|
|
1110
|
+
scope: GrepScopeSchema.optional(),
|
|
1111
|
+
maxMatches: z.coerce.number().int().min(1).max(GREP_HARD_MAX_MATCHES).optional().default(GREP_DEFAULT_MAX_MATCHES),
|
|
1112
|
+
contextLines: z.coerce.number().int().min(0).max(GREP_MAX_CONTEXT_LINES).optional().default(GREP_DEFAULT_CONTEXT_LINES)
|
|
1039
1113
|
});
|
|
1040
|
-
var
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1114
|
+
var GrepMatchVOSchema = z.object({
|
|
1115
|
+
assetId: z.string(),
|
|
1116
|
+
fileName: z.string(),
|
|
1117
|
+
/** Drive/Skill mounted path, or "" when the asset isn't path-mounted (e.g. a File node). */
|
|
1118
|
+
drivePath: z.string(),
|
|
1119
|
+
line: z.number().int().positive(),
|
|
1120
|
+
/** 1-based character column (not byte offset) of the match start within the line. */
|
|
1121
|
+
column: z.number().int().positive(),
|
|
1122
|
+
/** The matching line, truncated if it exceeds the long-line guard. */
|
|
1123
|
+
text: z.string(),
|
|
1124
|
+
before: z.array(z.string()),
|
|
1125
|
+
after: z.array(z.string())
|
|
1126
|
+
});
|
|
1127
|
+
var GrepResultVOSchema = z.object({
|
|
1128
|
+
matches: z.array(GrepMatchVOSchema),
|
|
1129
|
+
filesScanned: z.number().int().nonnegative(),
|
|
1130
|
+
/** Asset ids in scope with no text yet (contentKind text-or-writable-binary, no row). */
|
|
1131
|
+
missing: z.array(z.string()),
|
|
1132
|
+
/** Asset ids in scope whose derived text is stale (source replaced since it was written). */
|
|
1133
|
+
stale: z.array(z.string()),
|
|
1134
|
+
/** Count of assets in scope explicitly marked `none` (no extractable text). */
|
|
1135
|
+
unsearchable: z.number().int().nonnegative(),
|
|
1045
1136
|
/**
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1137
|
+
* Asset ids whose scan was attempted but failed (storage error, corrupt
|
|
1138
|
+
* cache file, object deleted mid-flight) — NOT counted in `filesScanned`.
|
|
1139
|
+
* Honest coverage: these were not actually searched, so a caller must not
|
|
1140
|
+
* treat their absence from `matches` as a clean "no match".
|
|
1049
1141
|
*/
|
|
1050
|
-
|
|
1142
|
+
errored: z.array(z.string()),
|
|
1143
|
+
/**
|
|
1144
|
+
* Count of in-scope, present-and-searchable assets the scan never even
|
|
1145
|
+
* reached because the deadline or `maxMatches` budget ran out first. Only
|
|
1146
|
+
* nonzero when `truncated` is true.
|
|
1147
|
+
*/
|
|
1148
|
+
notReached: z.number().int().nonnegative(),
|
|
1149
|
+
truncated: z.boolean()
|
|
1150
|
+
});
|
|
1151
|
+
var ReadTextLinesInputSchema = z.object({
|
|
1152
|
+
assetId: z.string(),
|
|
1153
|
+
startLine: z.coerce.number().int().min(1),
|
|
1154
|
+
endLine: z.coerce.number().int().min(1)
|
|
1155
|
+
});
|
|
1156
|
+
var ReadLinesVOSchema = z.object({
|
|
1157
|
+
lines: z.array(z.string()),
|
|
1158
|
+
startLine: z.number().int().positive(),
|
|
1159
|
+
endLine: z.number().int().positive(),
|
|
1160
|
+
totalLines: z.number().int().nonnegative(),
|
|
1161
|
+
truncated: z.boolean()
|
|
1162
|
+
});
|
|
1163
|
+
var AssetContentEditSchema = z.object({
|
|
1164
|
+
oldString: z.string().min(1),
|
|
1165
|
+
newString: z.string(),
|
|
1166
|
+
/** Replace every occurrence instead of requiring a single unique match. */
|
|
1167
|
+
replaceAll: z.boolean().optional().default(false)
|
|
1168
|
+
}).strict();
|
|
1169
|
+
var EditAssetContentInputSchema = z.object({
|
|
1170
|
+
assetId: z.string(),
|
|
1171
|
+
edits: z.array(AssetContentEditSchema).min(1),
|
|
1172
|
+
message: z.string().optional().default("Edit file content").describe(
|
|
1173
|
+
'Explanation shown to the human reviewer. Write a conventional-commit style subject \u2014 imperative verb + what + why, e.g. "Fix typo in setup instructions".'
|
|
1174
|
+
),
|
|
1175
|
+
submittedBy: z.string().optional().default("agent")
|
|
1176
|
+
});
|
|
1177
|
+
var AssetDownloadInputSchema = z.object({ assetId: z.string() });
|
|
1178
|
+
var AssetDownloadVOSchema = z.object({
|
|
1179
|
+
assetId: z.string(),
|
|
1180
|
+
downloadUrl: z.string(),
|
|
1181
|
+
fileName: z.string(),
|
|
1182
|
+
mimeType: z.string(),
|
|
1183
|
+
size: z.number().int().nonnegative(),
|
|
1184
|
+
contentHash: z.string().nullable()
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
// ../../packages/busabase-contract/src/domains/assets/contract.ts
|
|
1188
|
+
var UpdateAssetMetadataInputSchema = z.object({
|
|
1189
|
+
assetId: z.string(),
|
|
1190
|
+
metadata: z.record(z.string(), z.unknown()),
|
|
1191
|
+
mode: z.enum(["merge", "replace"]).optional().default("merge")
|
|
1051
1192
|
});
|
|
1193
|
+
var assetsContract = {
|
|
1194
|
+
createUploadUrl: oc.route({
|
|
1195
|
+
method: "POST",
|
|
1196
|
+
path: "/assets/upload-urls",
|
|
1197
|
+
tags: ["Assets"],
|
|
1198
|
+
summary: "Request asset upload URL",
|
|
1199
|
+
successDescription: "Presigned (or dev) upload URL plus the public URL and asset id when identical bytes are already in the library."
|
|
1200
|
+
}).input(RequestUploadUrlInputSchema).output(RequestUploadUrlVOSchema),
|
|
1201
|
+
confirm: oc.route({
|
|
1202
|
+
method: "POST",
|
|
1203
|
+
path: "/assets/confirmations",
|
|
1204
|
+
tags: ["Assets"],
|
|
1205
|
+
summary: "Confirm asset upload",
|
|
1206
|
+
successDescription: "Recorded the file and ensured its Busabase Asset library entry."
|
|
1207
|
+
}).input(ConfirmUploadInputSchema).output(ConfirmUploadVOSchema),
|
|
1208
|
+
list: oc.route({
|
|
1209
|
+
method: "GET",
|
|
1210
|
+
path: "/assets",
|
|
1211
|
+
tags: ["Assets"],
|
|
1212
|
+
summary: "List assets",
|
|
1213
|
+
successDescription: "Every asset in the space, with file metadata and usage counts."
|
|
1214
|
+
}).output(z.array(AssetVOSchema)),
|
|
1215
|
+
get: oc.route({
|
|
1216
|
+
method: "GET",
|
|
1217
|
+
path: "/assets/{assetId}",
|
|
1218
|
+
tags: ["Assets"],
|
|
1219
|
+
summary: "Get asset detail",
|
|
1220
|
+
successDescription: "Asset metadata plus every place it is referenced (where-used)."
|
|
1221
|
+
}).input(z.object({ assetId: z.string() })).output(AssetDetailVOSchema),
|
|
1222
|
+
updateMetadata: oc.route({
|
|
1223
|
+
method: "PATCH",
|
|
1224
|
+
path: "/assets/{assetId}/metadata",
|
|
1225
|
+
tags: ["Assets"],
|
|
1226
|
+
summary: "Update asset metadata",
|
|
1227
|
+
successDescription: "Updated AI-readable metadata for a file, such as summary, tags, source URL, or schema-specific hints. Large text does not live here \u2014 see putText / grep / readTextLines."
|
|
1228
|
+
}).input(UpdateAssetMetadataInputSchema).output(AssetDetailVOSchema),
|
|
1229
|
+
delete: oc.route({
|
|
1230
|
+
method: "DELETE",
|
|
1231
|
+
path: "/assets/{assetId}",
|
|
1232
|
+
tags: ["Assets"],
|
|
1233
|
+
summary: "Delete asset",
|
|
1234
|
+
successDescription: "Removed the asset and, if no other row references its bytes, the stored object. Refused while the asset is still referenced (where-used)."
|
|
1235
|
+
}).input(z.object({ assetId: z.string() })).output(z.object({ deleted: z.boolean() })),
|
|
1236
|
+
download: oc.route({
|
|
1237
|
+
method: "GET",
|
|
1238
|
+
path: "/assets/{assetId}/content",
|
|
1239
|
+
tags: ["Assets"],
|
|
1240
|
+
summary: "Get an asset's binary content download URL",
|
|
1241
|
+
successDescription: "A resolved, time-bounded download URL for the asset's raw bytes (local dev: the existing static /uploads route; cloud/S3: a presigned URL) plus its file metadata \u2014 see AssetDownloadVOSchema for why this returns a URL, not a raw-binary oRPC response."
|
|
1242
|
+
}).input(AssetDownloadInputSchema).output(AssetDownloadVOSchema),
|
|
1243
|
+
// ── Drive Grep Retrieval ─────────────────────────────────────────────────
|
|
1244
|
+
// Busabase stores, indexes, and searches text; it never generates it. Text
|
|
1245
|
+
// always arrives via putText — an agent's own extractor, or (future) an
|
|
1246
|
+
// Outgoing-Hook-triggered service — never a bundled parser/OCR library.
|
|
1247
|
+
putText: oc.route({
|
|
1248
|
+
method: "PUT",
|
|
1249
|
+
path: "/assets/{assetId}/text",
|
|
1250
|
+
tags: ["Assets"],
|
|
1251
|
+
summary: "Write (or mark none) an asset's text slot",
|
|
1252
|
+
successDescription: "Text slot updated: inline body (\u22641MB), or bound from a presigned upload (server-verified content hash, hash-poisoning-safe), or marked `none` for files with no extractable text. Direct write, audit-logged, not ChangeRequest-gated."
|
|
1253
|
+
}).input(PutTextInputSchema).output(AssetTextVOSchema),
|
|
1254
|
+
createTextUploadUrl: oc.route({
|
|
1255
|
+
method: "POST",
|
|
1256
|
+
path: "/assets/text/upload-urls",
|
|
1257
|
+
tags: ["Assets"],
|
|
1258
|
+
summary: "Request a presigned upload URL for large text",
|
|
1259
|
+
successDescription: "Presigned (or dev) upload URL for a temporary text object; PUT the bytes there, then call putText with the returned storageKey to bind, verify, and content-address it."
|
|
1260
|
+
}).input(CreateTextUploadUrlInputSchema).output(CreateTextUploadUrlVOSchema),
|
|
1261
|
+
grep: oc.route({
|
|
1262
|
+
method: "POST",
|
|
1263
|
+
path: "/assets/grep",
|
|
1264
|
+
tags: ["Assets"],
|
|
1265
|
+
summary: "Search every text-bearing asset in scope",
|
|
1266
|
+
successDescription: "Streaming regex/literal matches with real file + line + column numbers and context, across every asset with text \u2014 any size, no 256KB cap. Honest coverage: missing/stale/unsearchable/errored name assets that were not (fully or successfully) searched, notReached counts present assets the scan never got to, and truncated flags a capped response."
|
|
1267
|
+
}).input(GrepInputSchema).output(GrepResultVOSchema),
|
|
1268
|
+
readTextLines: oc.route({
|
|
1269
|
+
method: "GET",
|
|
1270
|
+
path: "/assets/{assetId}/text/lines",
|
|
1271
|
+
tags: ["Assets"],
|
|
1272
|
+
summary: "Read an exact line range from an asset's text",
|
|
1273
|
+
successDescription: "Lines [startLine, endLine] (range capped at 2000 lines / ~2MB response) read via a storage byte-range request \u2014 the server never loads the whole object, even for a multi-GB file."
|
|
1274
|
+
}).input(ReadTextLinesInputSchema).output(ReadLinesVOSchema),
|
|
1275
|
+
// ── editContent — edit an asset's REAL file content via ChangeRequest ──────
|
|
1276
|
+
// Unlike putText (derived/extracted text, direct write, disposable), this
|
|
1277
|
+
// edits the asset's actual mounted Drive/Skill file bytes and always goes
|
|
1278
|
+
// through the existing filetree ChangeRequest pipeline — never auto-merged.
|
|
1279
|
+
editContent: oc.route({
|
|
1280
|
+
method: "POST",
|
|
1281
|
+
path: "/assets/{assetId}/edit-content",
|
|
1282
|
+
tags: ["Assets", "Change Requests"],
|
|
1283
|
+
summary: "Edit an asset's file content via string-replace edits, as a ChangeRequest",
|
|
1284
|
+
successDescription: `Applied the string-replace edits (coding-agent Edit-tool semantics: unique-match or replaceAll) to the asset's current mounted Drive/Skill file content and proposed the result as a ChangeRequest (status "in_review") for human review. Reuses the existing filetree update-via-CR pipeline end to end, including baseContentHash optimistic-concurrency conflict protection at merge time. Requires the asset to be mounted in exactly one editable Drive/Skill location.`
|
|
1285
|
+
}).input(EditAssetContentInputSchema).output(changeRequestSchema)
|
|
1286
|
+
};
|
|
1052
1287
|
var viewFilterOperatorSchema = z.enum([
|
|
1053
1288
|
"contains",
|
|
1054
1289
|
"equals",
|
|
@@ -1459,6 +1694,13 @@ var viewContract = {
|
|
|
1459
1694
|
successDescription: "Created change request that restores an archived View."
|
|
1460
1695
|
}).input(restoreViewInputSchema.extend({ viewId: z.string() })).output(changeRequestSchema)
|
|
1461
1696
|
};
|
|
1697
|
+
var ReadDocLinesInputSchema = z.object({
|
|
1698
|
+
nodeId: z.string(),
|
|
1699
|
+
startLine: z.coerce.number().int().min(1),
|
|
1700
|
+
endLine: z.coerce.number().int().min(1)
|
|
1701
|
+
});
|
|
1702
|
+
|
|
1703
|
+
// ../../packages/busabase-contract/src/domains/doc/contract.ts
|
|
1462
1704
|
var docSchema = z.object({
|
|
1463
1705
|
node: nodeSchema,
|
|
1464
1706
|
storagePrefix: z.string(),
|
|
@@ -1508,6 +1750,13 @@ var docContract = {
|
|
|
1508
1750
|
summary: "Get Doc node",
|
|
1509
1751
|
successDescription: "Doc node detail and body."
|
|
1510
1752
|
}).input(z.object({ nodeId: z.string() })).output(docSchema),
|
|
1753
|
+
readLines: oc.route({
|
|
1754
|
+
method: "GET",
|
|
1755
|
+
path: "/docs/{nodeId}/lines",
|
|
1756
|
+
tags: ["Docs"],
|
|
1757
|
+
summary: "Read an exact line range from a Doc body",
|
|
1758
|
+
successDescription: "Lines [startLine, endLine] (range capped at 2000 lines / ~2MB response) sliced from the Doc's full body \u2014 Docs are KB-scale, so the whole body is read in memory; no byte-range/checkpoint machinery like assets.readTextLines uses for potentially multi-GB files. The Doc-domain follow-up to a Unified Grep match with `source: \"docs\"`, so an agent can read just the lines around a match instead of `get`'s entire body."
|
|
1759
|
+
}).input(ReadDocLinesInputSchema).output(ReadLinesVOSchema),
|
|
1511
1760
|
updateBody: oc.route({
|
|
1512
1761
|
method: "PUT",
|
|
1513
1762
|
path: "/docs/{nodeId}/body",
|
|
@@ -1523,149 +1772,100 @@ var docContract = {
|
|
|
1523
1772
|
successDescription: "Created a change request that proposes a new Doc body."
|
|
1524
1773
|
}).input(createDocChangeRequestInputSchema.extend({ nodeId: z.string() })).output(changeRequestSchema)
|
|
1525
1774
|
};
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1775
|
+
|
|
1776
|
+
// ../../packages/busabase-contract/src/domains/drive/contract.ts
|
|
1777
|
+
var driveContract = makeFileTreeContract("drives", "Drives");
|
|
1778
|
+
var DumpTableSchema = z.enum([
|
|
1779
|
+
"nodes",
|
|
1780
|
+
"bases",
|
|
1781
|
+
"baseFields",
|
|
1782
|
+
"views",
|
|
1783
|
+
"records",
|
|
1784
|
+
"fieldValues",
|
|
1785
|
+
"recordLinks",
|
|
1786
|
+
/** The physical bytes registry (open-domains/attachments) an Asset's `attachmentId` FKs into. */
|
|
1787
|
+
"attachments",
|
|
1788
|
+
"assets",
|
|
1789
|
+
"assetUsages",
|
|
1790
|
+
"assetTexts",
|
|
1791
|
+
"commits",
|
|
1792
|
+
"changeRequests",
|
|
1793
|
+
"operations",
|
|
1794
|
+
"comments",
|
|
1795
|
+
"reviews",
|
|
1796
|
+
"auditEvents"
|
|
1797
|
+
]);
|
|
1798
|
+
var ExportTablesInputSchema = z.object({
|
|
1799
|
+
table: DumpTableSchema,
|
|
1800
|
+
/** Opaque pagination cursor from a previous page's `nextCursor`; omit for the first page. */
|
|
1801
|
+
cursor: z.string().optional(),
|
|
1802
|
+
/** Page size, default 500, capped at 2000. */
|
|
1803
|
+
limit: z.number().int().positive().max(2e3).optional().default(500)
|
|
1534
1804
|
});
|
|
1535
|
-
var
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
displayName: z.string().optional(),
|
|
1539
|
-
mimeType: z.string().optional()
|
|
1540
|
-
}).strict();
|
|
1541
|
-
var textFileInputSchema = z.object({
|
|
1542
|
-
path: z.string().min(1),
|
|
1543
|
-
content: z.string().default(""),
|
|
1544
|
-
mimeType: z.string().optional()
|
|
1545
|
-
}).strict();
|
|
1546
|
-
var assetFileOperationInputSchema = z.object({
|
|
1547
|
-
kind: z.enum(["create", "update"]),
|
|
1548
|
-
path: z.string().min(1),
|
|
1549
|
-
assetId: z.string().min(1),
|
|
1550
|
-
displayName: z.string().optional(),
|
|
1551
|
-
mimeType: z.string().optional(),
|
|
1552
|
-
baseContentHash: z.string().optional()
|
|
1553
|
-
}).strict();
|
|
1554
|
-
var textFileOperationInputSchema = z.object({
|
|
1555
|
-
kind: z.enum(["create", "update"]),
|
|
1556
|
-
path: z.string().min(1),
|
|
1557
|
-
content: z.string(),
|
|
1558
|
-
mimeType: z.string().optional(),
|
|
1559
|
-
baseContentHash: z.string().optional()
|
|
1560
|
-
}).strict();
|
|
1561
|
-
var fileTreeNodeSchema = z.object({
|
|
1562
|
-
node: nodeSchema,
|
|
1563
|
-
entryFile: z.string(),
|
|
1564
|
-
visibility: z.enum(["private", "workspace", "public"]),
|
|
1565
|
-
version: z.string(),
|
|
1566
|
-
files: z.array(fileTreeFileSchema)
|
|
1805
|
+
var ExportTablesVOSchema = z.object({
|
|
1806
|
+
rows: z.array(z.record(z.string(), z.unknown())),
|
|
1807
|
+
nextCursor: z.string().nullable()
|
|
1567
1808
|
});
|
|
1568
|
-
var
|
|
1569
|
-
|
|
1570
|
-
slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
|
|
1571
|
-
name: z.string().min(1),
|
|
1572
|
-
description: z.string().optional().default(""),
|
|
1573
|
-
visibility: z.enum(["private", "workspace", "public"]).optional().default("private"),
|
|
1574
|
-
version: z.string().optional().default("0.1.0"),
|
|
1575
|
-
files: z.array(z.union([assetFileInputSchema, textFileInputSchema])).optional().default([]),
|
|
1576
|
-
// Review-first by default: without `autoMerge: true`, this proposes the node
|
|
1577
|
-
// as a pending ChangeRequest (status "in_review") instead of creating it
|
|
1578
|
-
// immediately. Pass `autoMerge: true` only for callers that don't need human
|
|
1579
|
-
// review (seed/migration scripts, an explicit no-review agent task).
|
|
1580
|
-
autoMerge: z.boolean().optional().default(false)
|
|
1809
|
+
var ImportBeginVOSchema = z.object({
|
|
1810
|
+
sessionId: z.string()
|
|
1581
1811
|
});
|
|
1582
|
-
var
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
z.
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
var createFileTreeChangeRequestInputSchema = z.object({
|
|
1600
|
-
message: z.string().optional().default("Update file tree").describe(
|
|
1601
|
-
'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".'
|
|
1602
|
-
),
|
|
1603
|
-
submittedBy: z.string().optional().default("local-producer"),
|
|
1604
|
-
operations: z.array(fileTreeFileOperationInputSchema).min(1)
|
|
1812
|
+
var ImportTablesInputSchema = z.object({
|
|
1813
|
+
sessionId: z.string(),
|
|
1814
|
+
table: z.union([DumpTableSchema, z.literal("docBodies"), z.literal("attachmentBlobs")]),
|
|
1815
|
+
rows: z.array(z.record(z.string(), z.unknown()))
|
|
1816
|
+
});
|
|
1817
|
+
var ImportTablesVOSchema = z.object({
|
|
1818
|
+
inserted: z.number().int().nonnegative()
|
|
1819
|
+
});
|
|
1820
|
+
var ImportSessionInputSchema = z.object({
|
|
1821
|
+
sessionId: z.string()
|
|
1822
|
+
});
|
|
1823
|
+
var ImportCommitVOSchema = z.object({
|
|
1824
|
+
ok: z.boolean(),
|
|
1825
|
+
warnings: z.array(z.string())
|
|
1826
|
+
});
|
|
1827
|
+
var ImportAbortVOSchema = z.object({
|
|
1828
|
+
ok: z.boolean()
|
|
1605
1829
|
});
|
|
1606
|
-
var makeFileTreeContract = (routeBase, tag) => {
|
|
1607
|
-
const label = tag.endsWith("s") ? tag.slice(0, -1) : tag;
|
|
1608
|
-
const basePath = `/${routeBase}`;
|
|
1609
|
-
return {
|
|
1610
|
-
list: oc.route({
|
|
1611
|
-
method: "GET",
|
|
1612
|
-
path: basePath,
|
|
1613
|
-
tags: [tag],
|
|
1614
|
-
summary: `List ${label} nodes`,
|
|
1615
|
-
successDescription: `${label} nodes with their Asset-backed file trees.`
|
|
1616
|
-
}).output(z.array(fileTreeNodeSchema)),
|
|
1617
|
-
create: oc.route({
|
|
1618
|
-
method: "POST",
|
|
1619
|
-
path: basePath,
|
|
1620
|
-
tags: [tag],
|
|
1621
|
-
summary: `Create ${label} node`,
|
|
1622
|
-
successDescription: `Review-first by default: a pending ChangeRequest proposing the ${label} node. Returns the materialized ${label} node instead when \`autoMerge: true\` is passed.`
|
|
1623
|
-
}).input(createFileTreeInputSchema).output(z.union([fileTreeNodeSchema, changeRequestSchema])),
|
|
1624
|
-
get: oc.route({
|
|
1625
|
-
method: "GET",
|
|
1626
|
-
path: `${basePath}/{nodeId}`,
|
|
1627
|
-
tags: [tag],
|
|
1628
|
-
summary: `Get ${label} node`,
|
|
1629
|
-
successDescription: `${label} node detail and file tree.`
|
|
1630
|
-
}).input(z.object({ nodeId: z.string() })).output(fileTreeNodeSchema),
|
|
1631
|
-
listFiles: oc.route({
|
|
1632
|
-
method: "GET",
|
|
1633
|
-
path: `${basePath}/{nodeId}/files`,
|
|
1634
|
-
tags: [tag],
|
|
1635
|
-
summary: `List ${label} files`,
|
|
1636
|
-
successDescription: `Asset-backed files mounted under the ${label} node.`
|
|
1637
|
-
}).input(z.object({ nodeId: z.string() })).output(z.array(fileTreeFileSchema)),
|
|
1638
|
-
readFile: oc.route({
|
|
1639
|
-
method: "GET",
|
|
1640
|
-
path: `${basePath}/{nodeId}/files/{+filePath}`,
|
|
1641
|
-
tags: [tag],
|
|
1642
|
-
summary: `Read ${label} file`,
|
|
1643
|
-
successDescription: `${label} file content and content hash.`
|
|
1644
|
-
}).input(z.object({ nodeId: z.string(), filePath: z.string() })).output(
|
|
1645
|
-
z.object({
|
|
1646
|
-
nodeId: z.string(),
|
|
1647
|
-
path: z.string(),
|
|
1648
|
-
encoding: z.enum(["utf8", "url"]),
|
|
1649
|
-
content: z.string(),
|
|
1650
|
-
mimeType: z.string(),
|
|
1651
|
-
assetId: z.string(),
|
|
1652
|
-
displayName: z.string().nullable(),
|
|
1653
|
-
assetUrl: z.string().nullable(),
|
|
1654
|
-
contentHash: z.string()
|
|
1655
|
-
})
|
|
1656
|
-
),
|
|
1657
|
-
createChangeRequest: oc.route({
|
|
1658
|
-
method: "POST",
|
|
1659
|
-
path: `${basePath}/{nodeId}/change-requests`,
|
|
1660
|
-
tags: [tag, "Change Requests"],
|
|
1661
|
-
summary: `Create ${label} change request`,
|
|
1662
|
-
successDescription: `Created file-tree change request for a ${label} node.`
|
|
1663
|
-
}).input(createFileTreeChangeRequestInputSchema.extend({ nodeId: z.string() })).output(changeRequestSchema)
|
|
1664
|
-
};
|
|
1665
|
-
};
|
|
1666
1830
|
|
|
1667
|
-
// ../../packages/busabase-contract/src/domains/
|
|
1668
|
-
var
|
|
1831
|
+
// ../../packages/busabase-contract/src/domains/dump/contract.ts
|
|
1832
|
+
var dumpContract = {
|
|
1833
|
+
exportTables: oc.route({
|
|
1834
|
+
method: "POST",
|
|
1835
|
+
path: "/dump/export/tables",
|
|
1836
|
+
tags: ["Dump"],
|
|
1837
|
+
summary: "Export raw rows for one table (cursor-paginated)",
|
|
1838
|
+
successDescription: "A page of raw rows for the requested table (id-ordered), plus an opaque nextCursor (null at the end). Vault items and webhook secrets are never exportable through this endpoint."
|
|
1839
|
+
}).input(ExportTablesInputSchema).output(ExportTablesVOSchema),
|
|
1840
|
+
importBegin: oc.route({
|
|
1841
|
+
method: "POST",
|
|
1842
|
+
path: "/dump/import/begin",
|
|
1843
|
+
tags: ["Dump"],
|
|
1844
|
+
summary: "Begin a full-fidelity import session",
|
|
1845
|
+
successDescription: "Created an import session for the current space. Refused unless the space's node tree is empty (full-fidelity import preserves original ids and cannot merge into existing data)."
|
|
1846
|
+
}).output(ImportBeginVOSchema),
|
|
1847
|
+
importTables: oc.route({
|
|
1848
|
+
method: "POST",
|
|
1849
|
+
path: "/dump/import/tables",
|
|
1850
|
+
tags: ["Dump"],
|
|
1851
|
+
summary: "Import a batch of raw rows into an open session",
|
|
1852
|
+
successDescription: "Inserted rows preserving their original ids. `docBodies` is a pseudo-table ({nodeId, markdown}[]) written directly to object storage (doc bodies are not a DB row)."
|
|
1853
|
+
}).input(ImportTablesInputSchema).output(ImportTablesVOSchema),
|
|
1854
|
+
importCommit: oc.route({
|
|
1855
|
+
method: "POST",
|
|
1856
|
+
path: "/dump/import/commit",
|
|
1857
|
+
tags: ["Dump"],
|
|
1858
|
+
summary: "Finalize an import session",
|
|
1859
|
+
successDescription: "Ran integrity checks (FK orphans, missing blobs) and closed the session. `ok:false` or a non-empty `warnings` array means the import completed but may be incomplete \u2014 inspect before relying on the space."
|
|
1860
|
+
}).input(ImportSessionInputSchema).output(ImportCommitVOSchema),
|
|
1861
|
+
importAbort: oc.route({
|
|
1862
|
+
method: "POST",
|
|
1863
|
+
path: "/dump/import/abort",
|
|
1864
|
+
tags: ["Dump"],
|
|
1865
|
+
summary: "Abort an import session",
|
|
1866
|
+
successDescription: "Best-effort cleanup of everything written so far in this session. Only ever touches the target space validated empty at `importBegin` time."
|
|
1867
|
+
}).input(ImportSessionInputSchema).output(ImportAbortVOSchema)
|
|
1868
|
+
};
|
|
1669
1869
|
z.object({
|
|
1670
1870
|
assetId: z.string()
|
|
1671
1871
|
});
|
|
@@ -1809,15 +2009,20 @@ var vaultContract = {
|
|
|
1809
2009
|
successDescription: "Removed local Vault secrets and variables."
|
|
1810
2010
|
}).output(VaultSuccessSchema)
|
|
1811
2011
|
};
|
|
1812
|
-
var WebhookEventTypeSchema = z.enum([
|
|
1813
|
-
|
|
2012
|
+
var WebhookEventTypeSchema = z.enum([
|
|
2013
|
+
"record.created",
|
|
2014
|
+
"ai_mention",
|
|
2015
|
+
"changes_requested",
|
|
2016
|
+
"asset.uploaded"
|
|
2017
|
+
]);
|
|
2018
|
+
z.enum(["webhook", "notify_agent", "run_function"]);
|
|
1814
2019
|
var WebhookDeliveryStatusSchema = z.enum(["success", "failed", "skipped"]);
|
|
1815
2020
|
var WebhookHttpConfigSchema = z.object({
|
|
1816
2021
|
targetUrl: z.string().url(),
|
|
1817
2022
|
secret: z.string().min(1).max(256).optional(),
|
|
1818
2023
|
headers: z.record(z.string(), z.string()).optional()
|
|
1819
2024
|
});
|
|
1820
|
-
var
|
|
2025
|
+
var WebhookFunctionConfigSchema = z.object({
|
|
1821
2026
|
code: z.string().min(1).max(2e4),
|
|
1822
2027
|
timeoutMs: z.number().int().min(100).max(5e3).default(2e3)
|
|
1823
2028
|
});
|
|
@@ -1826,7 +2031,7 @@ var WebhookHttpConfigVOSchema = z.object({
|
|
|
1826
2031
|
hasSecret: z.boolean(),
|
|
1827
2032
|
headers: z.record(z.string(), z.string()).optional()
|
|
1828
2033
|
});
|
|
1829
|
-
var
|
|
2034
|
+
var WebhookFunctionConfigVOSchema = WebhookFunctionConfigSchema;
|
|
1830
2035
|
var webhookRuleCommonInputFields = {
|
|
1831
2036
|
name: z.string().min(1).max(200),
|
|
1832
2037
|
eventType: WebhookEventTypeSchema,
|
|
@@ -1846,8 +2051,8 @@ var WebhookRuleInputSchema = z.discriminatedUnion("actionKind", [
|
|
|
1846
2051
|
}),
|
|
1847
2052
|
z.object({
|
|
1848
2053
|
...webhookRuleCommonInputFields,
|
|
1849
|
-
actionKind: z.literal("
|
|
1850
|
-
config:
|
|
2054
|
+
actionKind: z.literal("run_function"),
|
|
2055
|
+
config: WebhookFunctionConfigSchema
|
|
1851
2056
|
})
|
|
1852
2057
|
]);
|
|
1853
2058
|
var WebhookRuleUpdateInputSchema = z.discriminatedUnion("actionKind", [
|
|
@@ -1866,8 +2071,8 @@ var WebhookRuleUpdateInputSchema = z.discriminatedUnion("actionKind", [
|
|
|
1866
2071
|
z.object({
|
|
1867
2072
|
id: z.string(),
|
|
1868
2073
|
...webhookRuleCommonInputFields,
|
|
1869
|
-
actionKind: z.literal("
|
|
1870
|
-
config:
|
|
2074
|
+
actionKind: z.literal("run_function"),
|
|
2075
|
+
config: WebhookFunctionConfigSchema
|
|
1871
2076
|
})
|
|
1872
2077
|
]);
|
|
1873
2078
|
var webhookRuleVOCommonFields = {
|
|
@@ -1896,8 +2101,8 @@ var WebhookRuleVOSchema = z.discriminatedUnion("actionKind", [
|
|
|
1896
2101
|
}),
|
|
1897
2102
|
z.object({
|
|
1898
2103
|
...webhookRuleVOCommonFields,
|
|
1899
|
-
actionKind: z.literal("
|
|
1900
|
-
config:
|
|
2104
|
+
actionKind: z.literal("run_function"),
|
|
2105
|
+
config: WebhookFunctionConfigVOSchema
|
|
1901
2106
|
})
|
|
1902
2107
|
]);
|
|
1903
2108
|
var WebhookDeliveryVOSchema = z.object({
|
|
@@ -1937,7 +2142,7 @@ var webhookContract = {
|
|
|
1937
2142
|
path: "/webhooks",
|
|
1938
2143
|
tags: ["Webhooks"],
|
|
1939
2144
|
summary: "Create webhook automation rule",
|
|
1940
|
-
successDescription: "Created webhook automation rule. Dispatches on the configured event via an HTTP webhook, an agent notification, or a sandboxed
|
|
2145
|
+
successDescription: "Created webhook automation rule. Dispatches on the configured event via an HTTP webhook, an agent notification, or a sandboxed function."
|
|
1941
2146
|
}).input(WebhookRuleInputSchema).output(WebhookRuleVOSchema),
|
|
1942
2147
|
update: oc.route({
|
|
1943
2148
|
method: "PUT",
|
|
@@ -2000,6 +2205,107 @@ var listActivityResponseSchema = z.object({
|
|
|
2000
2205
|
items: z.array(activityItemSchema),
|
|
2001
2206
|
nextCursor: z.string().nullable()
|
|
2002
2207
|
});
|
|
2208
|
+
var GrepSourceSchema = z.enum(["files", "docs", "records"]);
|
|
2209
|
+
var UnifiedGrepFilesScopeSchema = z.object({
|
|
2210
|
+
assetIds: z.array(z.string()).optional(),
|
|
2211
|
+
/** Drive/Skill mounted path prefix (matches `busabase_asset_usages.path`). */
|
|
2212
|
+
drivePath: z.string().optional(),
|
|
2213
|
+
mimeTypes: z.array(z.string()).optional()
|
|
2214
|
+
});
|
|
2215
|
+
var UnifiedGrepDocsScopeSchema = z.object({
|
|
2216
|
+
nodeIds: z.array(z.string()).optional()
|
|
2217
|
+
});
|
|
2218
|
+
var UnifiedGrepRecordsScopeSchema = z.object({
|
|
2219
|
+
baseIds: z.array(z.string()).optional(),
|
|
2220
|
+
baseSlugs: z.array(z.string()).optional()
|
|
2221
|
+
});
|
|
2222
|
+
var UnifiedGrepScopeSchema = z.object({
|
|
2223
|
+
files: UnifiedGrepFilesScopeSchema.optional(),
|
|
2224
|
+
docs: UnifiedGrepDocsScopeSchema.optional(),
|
|
2225
|
+
records: UnifiedGrepRecordsScopeSchema.optional()
|
|
2226
|
+
});
|
|
2227
|
+
var UnifiedGrepInputSchema = z.object({
|
|
2228
|
+
pattern: z.string().min(1),
|
|
2229
|
+
/** JS RegExp flags, e.g. `"i"` for case-insensitive — same language as `assets.grep`. */
|
|
2230
|
+
flags: z.string().optional().default(""),
|
|
2231
|
+
/** Which sources to scan. Omitted = all three (`files`, `docs`, `records`). */
|
|
2232
|
+
sources: z.array(GrepSourceSchema).optional(),
|
|
2233
|
+
scope: UnifiedGrepScopeSchema.optional(),
|
|
2234
|
+
/** Shared across every scanned source — files run to completion first, then docs, then whatever remains goes to records. */
|
|
2235
|
+
maxMatches: z.coerce.number().int().min(1).max(GREP_HARD_MAX_MATCHES).optional().default(GREP_DEFAULT_MAX_MATCHES),
|
|
2236
|
+
contextLines: z.coerce.number().int().min(0).max(GREP_MAX_CONTEXT_LINES).optional().default(GREP_DEFAULT_CONTEXT_LINES)
|
|
2237
|
+
});
|
|
2238
|
+
var grepHitFields = {
|
|
2239
|
+
line: z.number().int().positive(),
|
|
2240
|
+
/** 1-based character column (not byte offset) of the match start within the line. */
|
|
2241
|
+
column: z.number().int().positive(),
|
|
2242
|
+
/** The matching line, truncated if it exceeds the long-line guard. */
|
|
2243
|
+
text: z.string(),
|
|
2244
|
+
before: z.array(z.string()),
|
|
2245
|
+
after: z.array(z.string())
|
|
2246
|
+
};
|
|
2247
|
+
var UnifiedGrepFileMatchVOSchema = z.object({
|
|
2248
|
+
source: z.literal("files"),
|
|
2249
|
+
assetId: z.string(),
|
|
2250
|
+
fileName: z.string(),
|
|
2251
|
+
/** Drive/Skill mounted path, or "" when the asset isn't path-mounted (e.g. a File node). */
|
|
2252
|
+
drivePath: z.string(),
|
|
2253
|
+
...grepHitFields
|
|
2254
|
+
});
|
|
2255
|
+
var UnifiedGrepDocMatchVOSchema = z.object({
|
|
2256
|
+
source: z.literal("docs"),
|
|
2257
|
+
nodeId: z.string(),
|
|
2258
|
+
slug: z.string(),
|
|
2259
|
+
name: z.string(),
|
|
2260
|
+
...grepHitFields
|
|
2261
|
+
});
|
|
2262
|
+
var UnifiedGrepRecordMatchVOSchema = z.object({
|
|
2263
|
+
source: z.literal("records"),
|
|
2264
|
+
baseId: z.string(),
|
|
2265
|
+
baseSlug: z.string(),
|
|
2266
|
+
recordId: z.string(),
|
|
2267
|
+
fieldSlug: z.string(),
|
|
2268
|
+
...grepHitFields
|
|
2269
|
+
});
|
|
2270
|
+
var UnifiedGrepMatchVOSchema = z.discriminatedUnion("source", [
|
|
2271
|
+
UnifiedGrepFileMatchVOSchema,
|
|
2272
|
+
UnifiedGrepDocMatchVOSchema,
|
|
2273
|
+
UnifiedGrepRecordMatchVOSchema
|
|
2274
|
+
]);
|
|
2275
|
+
var UnifiedGrepFilesCoverageSchema = z.object({
|
|
2276
|
+
scanned: z.number().int().nonnegative(),
|
|
2277
|
+
missing: z.array(z.string()),
|
|
2278
|
+
stale: z.array(z.string()),
|
|
2279
|
+
unsearchable: z.number().int().nonnegative(),
|
|
2280
|
+
errored: z.array(z.string()),
|
|
2281
|
+
notReached: z.number().int().nonnegative()
|
|
2282
|
+
});
|
|
2283
|
+
var UnifiedGrepDocsCoverageSchema = z.object({
|
|
2284
|
+
scanned: z.number().int().nonnegative(),
|
|
2285
|
+
/** Doc node ids whose body read/scan was attempted but failed — NOT a clean "scanned, no match". */
|
|
2286
|
+
errored: z.array(z.string()),
|
|
2287
|
+
/** Count of in-scope docs the scan never reached because the deadline/maxMatches budget ran out first. */
|
|
2288
|
+
notReached: z.number().int().nonnegative()
|
|
2289
|
+
});
|
|
2290
|
+
var UnifiedGrepRecordsCoverageSchema = z.object({
|
|
2291
|
+
scanned: z.number().int().nonnegative(),
|
|
2292
|
+
/** Record ids whose commit-fields read/flatten/scan was attempted but failed — NOT a clean "scanned, no match". */
|
|
2293
|
+
errored: z.array(z.string()),
|
|
2294
|
+
/** Count of in-scope records the scan never reached because the deadline/maxMatches budget ran out first. */
|
|
2295
|
+
notReached: z.number().int().nonnegative()
|
|
2296
|
+
});
|
|
2297
|
+
var UnifiedGrepCoverageSchema = z.object({
|
|
2298
|
+
files: UnifiedGrepFilesCoverageSchema,
|
|
2299
|
+
docs: UnifiedGrepDocsCoverageSchema,
|
|
2300
|
+
records: UnifiedGrepRecordsCoverageSchema
|
|
2301
|
+
});
|
|
2302
|
+
var UnifiedGrepResultVOSchema = z.object({
|
|
2303
|
+
/** Deterministic order: every `files` match, then every `docs` match, then every `records` match. */
|
|
2304
|
+
matches: z.array(UnifiedGrepMatchVOSchema),
|
|
2305
|
+
coverage: UnifiedGrepCoverageSchema,
|
|
2306
|
+
/** True when any source truncated, or any source has `notReached > 0`. */
|
|
2307
|
+
truncated: z.boolean()
|
|
2308
|
+
});
|
|
2003
2309
|
|
|
2004
2310
|
// ../../packages/busabase-contract/src/contract/busabase.ts
|
|
2005
2311
|
var changeRequestBatchResultSchema = z.object({
|
|
@@ -2029,14 +2335,33 @@ var busabaseContractRoutes = {
|
|
|
2029
2335
|
summary: "Search Busabase",
|
|
2030
2336
|
successDescription: "Paginated search results across records, change requests, Bases, File nodes, and Assets."
|
|
2031
2337
|
}).input(searchInputSchema).output(searchResponseSchema),
|
|
2338
|
+
// Unified Grep (P2a files+docs, P2b records) — top-level, cross-source
|
|
2339
|
+
// superset of `assets.grep`. See apps/busabase/content/spec/unified-grep.md.
|
|
2340
|
+
// Composes `logic/grep.ts`; `assets.grep` (files-only specialist) is
|
|
2341
|
+
// unchanged and stays the dedicated endpoint for its fuller
|
|
2342
|
+
// missing/stale/unsearchable reporting.
|
|
2343
|
+
grep: oc.route({
|
|
2344
|
+
method: "POST",
|
|
2345
|
+
path: "/grep",
|
|
2346
|
+
tags: ["Search"],
|
|
2347
|
+
summary: "Search files, Docs, and Base records with one pattern (unified grep)",
|
|
2348
|
+
successDescription: "Streaming regex/literal matches across every in-scope source \u2014 Drive/Skill files, Doc bodies, and Base records (canonical headCommit.fields, never the truncated search projection) \u2014 with one shared pattern, one shared maxMatches/deadline budget (files scanned first, then docs, then whatever budget remains goes to records), and a per-source honest coverage report (files keeps its existing missing/stale/unsearchable/errored/notReached; docs and records report scanned/errored/notReached). truncated is set when any source truncated or has notReached > 0."
|
|
2349
|
+
}).input(UnifiedGrepInputSchema).output(UnifiedGrepResultVOSchema),
|
|
2032
2350
|
nodes: {
|
|
2033
2351
|
list: oc.route({
|
|
2034
2352
|
method: "GET",
|
|
2035
2353
|
path: "/nodes",
|
|
2036
2354
|
tags: ["Nodes"],
|
|
2037
2355
|
summary: "List node tree",
|
|
2038
|
-
successDescription: "Workspace node tree including folders, Bases, files, and agents."
|
|
2039
|
-
}).output(z.array(nodeSchema)),
|
|
2356
|
+
successDescription: "Workspace node tree including folders, Bases, files, and agents. With no `parentId`/`depth`, returns the FULL tree (legacy behavior, still what every non-sidebar caller gets). Passing `parentId` and/or `depth` switches to a depth-bounded fetch: `parentId` omitted/null starts from the space root and returns it wrapped exactly like the legacy call (just depth-limited); an explicit `parentId` returns that node's children directly, ready to merge into its `NodeVO.children` for a sidebar's lazy per-folder expand. See `NodeVO.hasChildren` for how a depth boundary is surfaced."
|
|
2357
|
+
}).input(listNodesInputSchema).output(z.array(nodeSchema)),
|
|
2358
|
+
isDescendant: oc.route({
|
|
2359
|
+
method: "GET",
|
|
2360
|
+
path: "/nodes/{nodeId}/is-descendant",
|
|
2361
|
+
tags: ["Nodes"],
|
|
2362
|
+
summary: "Check whether a node is a descendant of another",
|
|
2363
|
+
successDescription: "Server-authoritative parentId-chain walk from nodeId up to potentialAncestorId. Used to gate cross-branch drag-and-drop drops in the sidebar, since the full tree is no longer guaranteed to be loaded client-side (depth-bounded lazy load) \u2014 a purely local walk could wrongly allow dropping a folder into its own unloaded descendant."
|
|
2364
|
+
}).input(isDescendantInputSchema).output(isDescendantOutputSchema),
|
|
2040
2365
|
listArchived: oc.route({
|
|
2041
2366
|
method: "GET",
|
|
2042
2367
|
path: "/nodes/archived",
|
|
@@ -2051,6 +2376,13 @@ var busabaseContractRoutes = {
|
|
|
2051
2376
|
summary: "Create Node tree change request",
|
|
2052
2377
|
successDescription: "Created change request for folder or node tree changes."
|
|
2053
2378
|
}).input(createNodeChangeRequestInputSchema).output(changeRequestSchema),
|
|
2379
|
+
move: oc.route({
|
|
2380
|
+
method: "POST",
|
|
2381
|
+
path: "/nodes/{nodeId}/move",
|
|
2382
|
+
tags: ["Nodes"],
|
|
2383
|
+
summary: "Move or reorder a node",
|
|
2384
|
+
successDescription: "Merged change request that repositioned the node under its (optionally new) parent. Applied immediately (auto-merged) since reordering is a low-risk structural tweak, not a review-worthy content change."
|
|
2385
|
+
}).input(moveNodeInputSchema).output(changeRequestSchema),
|
|
2054
2386
|
purge: oc.route({
|
|
2055
2387
|
method: "DELETE",
|
|
2056
2388
|
path: "/nodes/{nodeId}",
|
|
@@ -2117,12 +2449,14 @@ var busabaseContractRoutes = {
|
|
|
2117
2449
|
bases: baseContract,
|
|
2118
2450
|
skills: skillContract,
|
|
2119
2451
|
drives: driveContract,
|
|
2452
|
+
airapps: airappContract,
|
|
2120
2453
|
files: fileContract,
|
|
2121
2454
|
docs: docContract,
|
|
2122
2455
|
folders: folderContract,
|
|
2123
2456
|
assets: assetsContract,
|
|
2124
2457
|
vault: vaultContract,
|
|
2125
2458
|
webhooks: webhookContract,
|
|
2459
|
+
dump: dumpContract,
|
|
2126
2460
|
changeRequests: {
|
|
2127
2461
|
list: oc.route({
|
|
2128
2462
|
method: "GET",
|
|
@@ -2456,6 +2790,19 @@ var Busabase = class {
|
|
|
2456
2790
|
search(input) {
|
|
2457
2791
|
return this.client.search(input);
|
|
2458
2792
|
}
|
|
2793
|
+
/**
|
|
2794
|
+
* Unified grep — one regex/literal pattern scanned across every in-scope
|
|
2795
|
+
* source (Drive/Skill files, Doc bodies, and Base records — records read
|
|
2796
|
+
* the canonical `headCommit.fields`, never the truncated search
|
|
2797
|
+
* projection), with a shared `maxMatches`/deadline budget and per-source
|
|
2798
|
+
* honest coverage. Use this when the answer could live anywhere; use
|
|
2799
|
+
* `client.assets.grep` directly instead when you specifically only care
|
|
2800
|
+
* about files and want its fuller `missing`/`stale`/`unsearchable`
|
|
2801
|
+
* file-only reporting.
|
|
2802
|
+
*/
|
|
2803
|
+
grep(input) {
|
|
2804
|
+
return this.client.grep(input);
|
|
2805
|
+
}
|
|
2459
2806
|
/**
|
|
2460
2807
|
* Supply text for an Asset's Drive Grep Retrieval text slot in one call —
|
|
2461
2808
|
* inline for small text, a presigned upload for large text — so callers
|
|
@@ -2501,4 +2848,4 @@ var Busabase = class {
|
|
|
2501
2848
|
}
|
|
2502
2849
|
};
|
|
2503
2850
|
|
|
2504
|
-
export { Busabase, DEFAULT_BASE_URL, cloudContract, createBusabaseClient, normalizeBaseUrl, resolveConfig };
|
|
2851
|
+
export { Busabase, CREATABLE_NODE_TYPES, DEFAULT_BASE_URL, cloudContract, createBusabaseClient, normalizeBaseUrl, resolveConfig };
|