busabase-sdk 0.9.4 → 0.9.5
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 +4829 -2842
- package/dist/index.js +1008 -693
- package/package.json +3 -3
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
|
];
|
|
@@ -872,6 +603,7 @@ var auditActionSchema = z.enum([
|
|
|
872
603
|
"file.created",
|
|
873
604
|
"skill.created",
|
|
874
605
|
"drive.created",
|
|
606
|
+
"airapp.created",
|
|
875
607
|
"asset.deleted",
|
|
876
608
|
"asset.metadata_updated",
|
|
877
609
|
"asset.text_written",
|
|
@@ -961,6 +693,13 @@ var createNodeChangeRequestInputSchema = z.object({
|
|
|
961
693
|
),
|
|
962
694
|
operations: z.array(nodeOperationInputSchema).min(1)
|
|
963
695
|
});
|
|
696
|
+
var moveNodeInputSchema = z.object({
|
|
697
|
+
nodeId: z.string(),
|
|
698
|
+
parentNodeId: z.string().optional().describe("New parent folder node id. Omit to keep the current parent and only reorder."),
|
|
699
|
+
position: z.number().int().optional().describe("New position among the target parent's children."),
|
|
700
|
+
message: z.string().optional().describe("Reviewer-facing Change Request message."),
|
|
701
|
+
submittedBy: z.string().optional()
|
|
702
|
+
});
|
|
964
703
|
var createDeleteChangeRequestInputSchema = z.object({
|
|
965
704
|
message: z.string().optional().default("Delete record").describe(
|
|
966
705
|
'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 +709,556 @@ var createDeleteChangeRequestInputSchema = z.object({
|
|
|
970
709
|
// implemented, so the API no longer accepts it (breaking change).
|
|
971
710
|
deleteMode: z.enum(["archive"]).optional().default("archive")
|
|
972
711
|
});
|
|
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()
|
|
712
|
+
var reviseOperationInputSchema = z.object({
|
|
713
|
+
fields: z.record(z.string(), z.unknown()).describe(
|
|
714
|
+
"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."
|
|
715
|
+
),
|
|
716
|
+
message: z.string().optional().default("Revise operation").describe(
|
|
717
|
+
'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".'
|
|
718
|
+
),
|
|
719
|
+
author: z.string().optional().default("local-producer"),
|
|
720
|
+
baseCommitId: z.string().optional()
|
|
721
|
+
});
|
|
722
|
+
var reviewChangeRequestInputSchema = z.object({
|
|
723
|
+
verdict: z.enum(["approved", "rejected"]),
|
|
724
|
+
reason: z.string().optional()
|
|
725
|
+
});
|
|
726
|
+
var commentSubjectInputSchema = z.object({
|
|
727
|
+
subjectType: commentSubjectTypeSchema,
|
|
728
|
+
subjectId: z.string().min(1)
|
|
729
|
+
});
|
|
730
|
+
var createCommentInputSchema = commentSubjectInputSchema.extend({
|
|
731
|
+
authorId: z.string().optional().default("local-admin"),
|
|
732
|
+
body: z.string().trim().min(1),
|
|
733
|
+
mentionsAi: z.boolean().optional().default(false)
|
|
734
|
+
});
|
|
735
|
+
var listInputSchema = z.object({
|
|
736
|
+
limit: z.coerce.number().int().min(1).max(100).optional().default(50)
|
|
737
|
+
}).optional().default({ limit: 50 });
|
|
738
|
+
var listChangeRequestsPagedInputSchema = z.object({
|
|
739
|
+
limit: z.coerce.number().int().min(1).max(100).optional().default(50),
|
|
740
|
+
/** Opaque base64 cursor (`createdAt|id`) for keyset pagination. */
|
|
741
|
+
cursor: z.string().optional(),
|
|
742
|
+
status: z.array(changeRequestStatusSchema).optional(),
|
|
743
|
+
mine: z.boolean().optional()
|
|
744
|
+
}).optional().default({ limit: 50 });
|
|
745
|
+
var listChangeRequestsResponseSchema = z.object({
|
|
746
|
+
changeRequests: z.array(changeRequestSchema),
|
|
747
|
+
nextCursor: z.string().nullable()
|
|
748
|
+
});
|
|
749
|
+
var changeRequestCountsSchema = z.object({
|
|
750
|
+
review: z.number().int().nonnegative(),
|
|
751
|
+
changes: z.number().int().nonnegative(),
|
|
752
|
+
created: z.number().int().nonnegative(),
|
|
753
|
+
approved: z.number().int().nonnegative(),
|
|
754
|
+
merged: z.number().int().nonnegative(),
|
|
755
|
+
rejected: z.number().int().nonnegative()
|
|
756
|
+
});
|
|
757
|
+
var searchInputSchema = z.object({
|
|
758
|
+
query: z.string().default(""),
|
|
759
|
+
limit: z.coerce.number().int().min(1).max(100).optional().default(20),
|
|
760
|
+
offset: z.coerce.number().int().min(0).optional().default(0)
|
|
761
|
+
});
|
|
762
|
+
var authSpaceSchema = z.object({
|
|
763
|
+
id: z.string(),
|
|
764
|
+
name: z.string(),
|
|
765
|
+
slug: z.string().nullable(),
|
|
766
|
+
plan: z.string().nullable()
|
|
767
|
+
});
|
|
768
|
+
var authUserSchema = z.object({
|
|
769
|
+
id: z.string(),
|
|
770
|
+
name: z.string(),
|
|
771
|
+
email: z.string().nullable(),
|
|
772
|
+
image: z.string().nullable()
|
|
773
|
+
});
|
|
774
|
+
var authMemberSchema = z.object({
|
|
775
|
+
userId: z.string(),
|
|
776
|
+
spaceId: z.string(),
|
|
777
|
+
role: z.string()
|
|
778
|
+
});
|
|
779
|
+
var authInfoSchema = z.object({
|
|
780
|
+
/** The space this request is scoped to (explicit `x-busabase-space` header, else the default). */
|
|
781
|
+
space: authSpaceSchema,
|
|
782
|
+
user: authUserSchema,
|
|
783
|
+
member: authMemberSchema,
|
|
784
|
+
/**
|
|
785
|
+
* Every space the authenticated user belongs to. An API key is user-scoped, not
|
|
786
|
+
* space-scoped — when this has more than one entry, callers should confirm the
|
|
787
|
+
* intended space and target it explicitly via the `x-busabase-space` header.
|
|
788
|
+
*/
|
|
789
|
+
spaces: z.array(authSpaceSchema)
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
// ../../packages/busabase-contract/src/domains/filetree/contract.ts
|
|
793
|
+
var fileTreeFileSchema = z.object({
|
|
794
|
+
path: z.string(),
|
|
795
|
+
name: z.string(),
|
|
796
|
+
size: z.number(),
|
|
797
|
+
updatedAt: z.string().nullable(),
|
|
798
|
+
mimeType: z.string().nullable(),
|
|
799
|
+
assetId: z.string(),
|
|
800
|
+
displayName: z.string().nullable()
|
|
801
|
+
});
|
|
802
|
+
var assetFileInputSchema = z.object({
|
|
803
|
+
path: z.string().min(1),
|
|
804
|
+
assetId: z.string().min(1),
|
|
805
|
+
displayName: z.string().optional(),
|
|
806
|
+
mimeType: z.string().optional()
|
|
807
|
+
}).strict();
|
|
808
|
+
var textFileInputSchema = z.object({
|
|
809
|
+
path: z.string().min(1),
|
|
810
|
+
content: z.string().default(""),
|
|
811
|
+
mimeType: z.string().optional()
|
|
812
|
+
}).strict();
|
|
813
|
+
var assetFileOperationInputSchema = z.object({
|
|
814
|
+
kind: z.enum(["create", "update"]),
|
|
815
|
+
path: z.string().min(1),
|
|
816
|
+
assetId: z.string().min(1),
|
|
817
|
+
displayName: z.string().optional(),
|
|
818
|
+
mimeType: z.string().optional(),
|
|
819
|
+
baseContentHash: z.string().optional()
|
|
820
|
+
}).strict();
|
|
821
|
+
var textFileOperationInputSchema = z.object({
|
|
822
|
+
kind: z.enum(["create", "update"]),
|
|
823
|
+
path: z.string().min(1),
|
|
824
|
+
content: z.string(),
|
|
825
|
+
mimeType: z.string().optional(),
|
|
826
|
+
baseContentHash: z.string().optional()
|
|
827
|
+
}).strict();
|
|
828
|
+
var fileTreeNodeSchema = z.object({
|
|
829
|
+
node: nodeSchema,
|
|
830
|
+
entryFile: z.string(),
|
|
831
|
+
visibility: z.enum(["private", "workspace", "public"]),
|
|
832
|
+
version: z.string(),
|
|
833
|
+
files: z.array(fileTreeFileSchema)
|
|
834
|
+
});
|
|
835
|
+
var createFileTreeInputSchema = z.object({
|
|
836
|
+
parentNodeId: z.string().optional(),
|
|
837
|
+
slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
|
|
838
|
+
name: z.string().min(1),
|
|
839
|
+
description: z.string().optional().default(""),
|
|
840
|
+
visibility: z.enum(["private", "workspace", "public"]).optional().default("private"),
|
|
841
|
+
version: z.string().optional().default("0.1.0"),
|
|
842
|
+
files: z.array(z.union([assetFileInputSchema, textFileInputSchema])).optional().default([]),
|
|
843
|
+
// Review-first by default: without `autoMerge: true`, this proposes the node
|
|
844
|
+
// as a pending ChangeRequest (status "in_review") instead of creating it
|
|
845
|
+
// immediately. Pass `autoMerge: true` only for callers that don't need human
|
|
846
|
+
// review (seed/migration scripts, an explicit no-review agent task).
|
|
847
|
+
autoMerge: z.boolean().optional().default(false),
|
|
848
|
+
// "merge" (default): `files` is layered on top of the config's default seed
|
|
849
|
+
// files by path — a caller supplying just a couple of extra files (e.g. a
|
|
850
|
+
// Skill's own reference doc) still gets the default scaffold (SKILL.md,
|
|
851
|
+
// skill.json, ...) for any path they didn't provide themselves. "replace":
|
|
852
|
+
// `files` replaces the defaults entirely — for a caller handing over a
|
|
853
|
+
// complete, different-shaped project (e.g. an AirApp seeded with a Vite
|
|
854
|
+
// project instead of the default Hono template) who does NOT want leftover
|
|
855
|
+
// default files with unrelated content mixed in.
|
|
856
|
+
mergeMode: z.enum(["merge", "replace"]).optional().default("merge")
|
|
857
|
+
});
|
|
858
|
+
var fileTreeFileOperationInputSchema = z.union([
|
|
859
|
+
assetFileOperationInputSchema,
|
|
860
|
+
textFileOperationInputSchema,
|
|
861
|
+
z.object({
|
|
862
|
+
kind: z.literal("delete"),
|
|
863
|
+
path: z.string().min(1),
|
|
864
|
+
baseContentHash: z.string().optional()
|
|
865
|
+
}).strict(),
|
|
866
|
+
z.object({
|
|
867
|
+
kind: z.literal("metadata_update"),
|
|
868
|
+
metadata: z.object({
|
|
869
|
+
entryFile: z.string().optional(),
|
|
870
|
+
visibility: z.enum(["private", "workspace", "public"]).optional(),
|
|
871
|
+
version: z.string().optional()
|
|
872
|
+
}).default({})
|
|
873
|
+
}).strict()
|
|
874
|
+
]);
|
|
875
|
+
var createFileTreeChangeRequestInputSchema = z.object({
|
|
876
|
+
message: z.string().optional().default("Update file tree").describe(
|
|
877
|
+
'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".'
|
|
878
|
+
),
|
|
879
|
+
submittedBy: z.string().optional().default("local-producer"),
|
|
880
|
+
operations: z.array(fileTreeFileOperationInputSchema).min(1)
|
|
881
|
+
});
|
|
882
|
+
var makeFileTreeContract = (routeBase, tag) => {
|
|
883
|
+
const label = tag.endsWith("s") ? tag.slice(0, -1) : tag;
|
|
884
|
+
const basePath = `/${routeBase}`;
|
|
885
|
+
return {
|
|
886
|
+
list: oc.route({
|
|
887
|
+
method: "GET",
|
|
888
|
+
path: basePath,
|
|
889
|
+
tags: [tag],
|
|
890
|
+
summary: `List ${label} nodes`,
|
|
891
|
+
successDescription: `${label} nodes with their Asset-backed file trees.`
|
|
892
|
+
}).output(z.array(fileTreeNodeSchema)),
|
|
893
|
+
create: oc.route({
|
|
894
|
+
method: "POST",
|
|
895
|
+
path: basePath,
|
|
896
|
+
tags: [tag],
|
|
897
|
+
summary: `Create ${label} node`,
|
|
898
|
+
successDescription: `Review-first by default: a pending ChangeRequest proposing the ${label} node. Returns the materialized ${label} node instead when \`autoMerge: true\` is passed.`
|
|
899
|
+
}).input(createFileTreeInputSchema).output(z.union([fileTreeNodeSchema, changeRequestSchema])),
|
|
900
|
+
get: oc.route({
|
|
901
|
+
method: "GET",
|
|
902
|
+
path: `${basePath}/{nodeId}`,
|
|
903
|
+
tags: [tag],
|
|
904
|
+
summary: `Get ${label} node`,
|
|
905
|
+
successDescription: `${label} node detail and file tree.`
|
|
906
|
+
}).input(z.object({ nodeId: z.string() })).output(fileTreeNodeSchema),
|
|
907
|
+
listFiles: oc.route({
|
|
908
|
+
method: "GET",
|
|
909
|
+
path: `${basePath}/{nodeId}/files`,
|
|
910
|
+
tags: [tag],
|
|
911
|
+
summary: `List ${label} files`,
|
|
912
|
+
successDescription: `Asset-backed files mounted under the ${label} node.`
|
|
913
|
+
}).input(z.object({ nodeId: z.string() })).output(z.array(fileTreeFileSchema)),
|
|
914
|
+
readFile: oc.route({
|
|
915
|
+
method: "GET",
|
|
916
|
+
path: `${basePath}/{nodeId}/files/{+filePath}`,
|
|
917
|
+
tags: [tag],
|
|
918
|
+
summary: `Read ${label} file`,
|
|
919
|
+
successDescription: `${label} file content and content hash.`
|
|
920
|
+
}).input(z.object({ nodeId: z.string(), filePath: z.string() })).output(
|
|
921
|
+
z.object({
|
|
922
|
+
nodeId: z.string(),
|
|
923
|
+
path: z.string(),
|
|
924
|
+
encoding: z.enum(["utf8", "url"]),
|
|
925
|
+
content: z.string(),
|
|
926
|
+
mimeType: z.string(),
|
|
927
|
+
assetId: z.string(),
|
|
928
|
+
displayName: z.string().nullable(),
|
|
929
|
+
assetUrl: z.string().nullable(),
|
|
930
|
+
contentHash: z.string()
|
|
931
|
+
})
|
|
932
|
+
),
|
|
933
|
+
createChangeRequest: oc.route({
|
|
934
|
+
method: "POST",
|
|
935
|
+
path: `${basePath}/{nodeId}/change-requests`,
|
|
936
|
+
tags: [tag, "Change Requests"],
|
|
937
|
+
summary: `Create ${label} change request`,
|
|
938
|
+
successDescription: `Created file-tree change request for a ${label} node.`
|
|
939
|
+
}).input(createFileTreeChangeRequestInputSchema.extend({ nodeId: z.string() })).output(changeRequestSchema)
|
|
940
|
+
};
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
// ../../packages/busabase-contract/src/domains/airapp/contract.ts
|
|
944
|
+
var airappContract = makeFileTreeContract("airapps", "AirApps");
|
|
945
|
+
var AttachmentMetadataSchema = z.record(z.string(), z.unknown());
|
|
946
|
+
z.object({
|
|
947
|
+
id: z.string(),
|
|
948
|
+
url: z.string(),
|
|
949
|
+
fileName: z.string(),
|
|
950
|
+
mimeType: z.string(),
|
|
951
|
+
size: z.number().int().nonnegative()
|
|
952
|
+
});
|
|
953
|
+
var RequestUploadUrlInputSchema = z.object({
|
|
954
|
+
fileName: z.string().min(1).max(255),
|
|
955
|
+
mimeType: z.string().min(1),
|
|
956
|
+
sizeBytes: z.number().int().positive(),
|
|
957
|
+
spaceId: z.string().optional(),
|
|
958
|
+
context: z.string().regex(/^[a-zA-Z0-9_/-]+$/).max(100).optional(),
|
|
959
|
+
/** Content fingerprint (e.g. "sha256:<hex>") for dedup; computed client-side. */
|
|
960
|
+
contentHash: z.string().max(80).optional()
|
|
961
|
+
});
|
|
962
|
+
var ConfirmUploadInputSchema = z.object({
|
|
963
|
+
storageKey: z.string().min(1),
|
|
964
|
+
fileName: z.string().min(1).max(255),
|
|
965
|
+
mimeType: z.string().min(1),
|
|
966
|
+
sizeBytes: z.number().int().positive(),
|
|
967
|
+
spaceId: z.string().optional(),
|
|
968
|
+
context: z.string().regex(/^[a-zA-Z0-9_/-]+$/).max(100).optional(),
|
|
969
|
+
metadata: AttachmentMetadataSchema.optional(),
|
|
970
|
+
/** Content fingerprint (e.g. "sha256:<hex>") persisted for dedup. */
|
|
971
|
+
contentHash: z.string().max(80).optional()
|
|
972
|
+
});
|
|
973
|
+
var RequestUploadUrlVOSchema = z.object({
|
|
974
|
+
uploadUrl: z.string(),
|
|
975
|
+
storageKey: z.string(),
|
|
976
|
+
publicUrl: z.string(),
|
|
977
|
+
expiresIn: z.number(),
|
|
978
|
+
/**
|
|
979
|
+
* True when an identical file (same contentHash, same scope) already exists.
|
|
980
|
+
* The client should SKIP the byte upload and the confirm step, and use
|
|
981
|
+
* `attachmentId`/`publicUrl` directly. `uploadUrl` is empty in this case.
|
|
982
|
+
*/
|
|
983
|
+
duplicate: z.boolean().optional(),
|
|
984
|
+
/** Existing attachment id — present only when `duplicate` is true. */
|
|
985
|
+
attachmentId: z.string().optional(),
|
|
986
|
+
/** Host-specific logical asset id, when the app maps uploads into an Asset library. */
|
|
987
|
+
assetId: z.string().optional()
|
|
988
|
+
});
|
|
989
|
+
var ConfirmUploadVOSchema = z.object({
|
|
990
|
+
success: z.boolean(),
|
|
991
|
+
attachmentId: z.string(),
|
|
992
|
+
/** Host-specific logical asset id, when the app maps uploads into an Asset library. */
|
|
993
|
+
assetId: z.string().optional(),
|
|
994
|
+
storageKey: z.string(),
|
|
995
|
+
publicUrl: z.string()
|
|
982
996
|
});
|
|
983
|
-
var
|
|
984
|
-
|
|
985
|
-
|
|
997
|
+
var AssetTextStatusSchema = z.enum(["missing", "present", "none", "stale"]);
|
|
998
|
+
var AssetVOSchema = z.object({
|
|
999
|
+
id: z.string(),
|
|
1000
|
+
attachmentId: z.string(),
|
|
1001
|
+
name: z.string(),
|
|
1002
|
+
contentKind: z.enum(["text", "binary"]),
|
|
1003
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
1004
|
+
fileName: z.string(),
|
|
1005
|
+
mimeType: z.string(),
|
|
1006
|
+
size: z.number().int().nonnegative(),
|
|
1007
|
+
url: z.string(),
|
|
1008
|
+
contentHash: z.string().nullable(),
|
|
1009
|
+
/** How many places reference this asset (Base records + Doc bodies). */
|
|
1010
|
+
usageCount: z.number().int().nonnegative(),
|
|
1011
|
+
/** Drive Grep Retrieval text-slot status — see {@link AssetTextStatusSchema}. */
|
|
1012
|
+
textStatus: AssetTextStatusSchema,
|
|
1013
|
+
createdAt: z.string()
|
|
986
1014
|
});
|
|
987
|
-
var
|
|
988
|
-
|
|
989
|
-
|
|
1015
|
+
var AssetUsageVOSchema = z.object({
|
|
1016
|
+
ownerType: z.enum(["drive", "skill", "airapp", "base", "doc", "file_node"]),
|
|
1017
|
+
nodeId: z.string(),
|
|
1018
|
+
nodeName: z.string(),
|
|
1019
|
+
nodeType: z.string(),
|
|
1020
|
+
/** Node slug, so the UI can link to the owning Base/Doc (`/{type}/{slug}`). */
|
|
1021
|
+
nodeSlug: z.string(),
|
|
1022
|
+
/** Drive/Skill mounted path, or null when the usage is not path-based. */
|
|
1023
|
+
path: z.string().nullable(),
|
|
1024
|
+
/** Base record id, or null for whole-node usages (e.g. a Doc body). */
|
|
1025
|
+
recordId: z.string().nullable(),
|
|
1026
|
+
/** Attachment field slug, or null for whole-node usages. */
|
|
1027
|
+
fieldSlug: z.string().nullable(),
|
|
1028
|
+
/** Doc block id, or null when the usage is not block-based. */
|
|
1029
|
+
blockId: z.string().nullable(),
|
|
1030
|
+
createdAt: z.string()
|
|
990
1031
|
});
|
|
991
|
-
var
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
mentionsAi: z.boolean().optional().default(false)
|
|
1032
|
+
var AssetDetailVOSchema = z.object({
|
|
1033
|
+
asset: AssetVOSchema,
|
|
1034
|
+
usages: z.array(AssetUsageVOSchema)
|
|
995
1035
|
});
|
|
996
|
-
var
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1036
|
+
var PutTextInputSchema = z.object({
|
|
1037
|
+
assetId: z.string(),
|
|
1038
|
+
/** Inline text body, ≤ 1 MB. For larger text, use `createTextUploadUrl` + bind by `storageKey`. */
|
|
1039
|
+
text: z.string().optional(),
|
|
1040
|
+
/** Bind a presigned-uploaded text object (a temp `asset-texts/pending/*.txt` key). */
|
|
1041
|
+
storageKey: z.string().optional(),
|
|
1042
|
+
/**
|
|
1043
|
+
* Claimed content hash for the `storageKey` bind path (`sha256:<hex>`, echoing
|
|
1044
|
+
* `createTextUploadUrl`'s input like `open-domains/attachments`' confirm step).
|
|
1045
|
+
* The server always computes the ACTUAL hash from the bytes during the
|
|
1046
|
+
* confirm scan and rejects a mismatch (hash-poisoning defense) — this field
|
|
1047
|
+
* is only an optional early-mismatch check, never trusted for addressing.
|
|
1048
|
+
*/
|
|
1049
|
+
contentHash: z.string().optional(),
|
|
1050
|
+
/** Mark as having no extractable text (e.g. a scanned, image-only PDF). */
|
|
1051
|
+
none: z.boolean().optional()
|
|
1009
1052
|
});
|
|
1010
|
-
var
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
rejected: z.number().int().nonnegative()
|
|
1053
|
+
var AssetTextVOSchema = z.object({
|
|
1054
|
+
assetId: z.string(),
|
|
1055
|
+
textStatus: AssetTextStatusSchema,
|
|
1056
|
+
lineCount: z.number().int().nonnegative(),
|
|
1057
|
+
charCount: z.number().int().nonnegative(),
|
|
1058
|
+
byteCount: z.number().int().nonnegative()
|
|
1017
1059
|
});
|
|
1018
|
-
var
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1060
|
+
var CreateTextUploadUrlInputSchema = z.object({
|
|
1061
|
+
assetId: z.string(),
|
|
1062
|
+
sizeBytes: z.number().int().positive(),
|
|
1063
|
+
/** Optional claim, mirrors `RequestUploadUrlDTO.contentHash` (never trusted for addressing). */
|
|
1064
|
+
contentHash: z.string().optional()
|
|
1022
1065
|
});
|
|
1023
|
-
var
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
plan: z.string().nullable()
|
|
1066
|
+
var CreateTextUploadUrlVOSchema = z.object({
|
|
1067
|
+
uploadUrl: z.string(),
|
|
1068
|
+
storageKey: z.string(),
|
|
1069
|
+
expiresIn: z.number().int().nonnegative()
|
|
1028
1070
|
});
|
|
1029
|
-
var
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1071
|
+
var GrepScopeSchema = z.object({
|
|
1072
|
+
assetIds: z.array(z.string()).optional(),
|
|
1073
|
+
/** Drive/Skill mounted path prefix (matches `busabase_asset_usages.path`). */
|
|
1074
|
+
drivePath: z.string().optional(),
|
|
1075
|
+
mimeTypes: z.array(z.string()).optional()
|
|
1034
1076
|
});
|
|
1035
|
-
var
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1077
|
+
var GREP_DEFAULT_MAX_MATCHES = 100;
|
|
1078
|
+
var GREP_HARD_MAX_MATCHES = 1e3;
|
|
1079
|
+
var GREP_DEFAULT_CONTEXT_LINES = 0;
|
|
1080
|
+
var GREP_MAX_CONTEXT_LINES = 10;
|
|
1081
|
+
var GrepInputSchema = z.object({
|
|
1082
|
+
pattern: z.string().min(1),
|
|
1083
|
+
/** JS RegExp flags, e.g. `"i"` for case-insensitive. `g`/`y` are ignored (grep always scans every match per line). */
|
|
1084
|
+
flags: z.string().optional().default(""),
|
|
1085
|
+
scope: GrepScopeSchema.optional(),
|
|
1086
|
+
maxMatches: z.coerce.number().int().min(1).max(GREP_HARD_MAX_MATCHES).optional().default(GREP_DEFAULT_MAX_MATCHES),
|
|
1087
|
+
contextLines: z.coerce.number().int().min(0).max(GREP_MAX_CONTEXT_LINES).optional().default(GREP_DEFAULT_CONTEXT_LINES)
|
|
1039
1088
|
});
|
|
1040
|
-
var
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1089
|
+
var GrepMatchVOSchema = z.object({
|
|
1090
|
+
assetId: z.string(),
|
|
1091
|
+
fileName: z.string(),
|
|
1092
|
+
/** Drive/Skill mounted path, or "" when the asset isn't path-mounted (e.g. a File node). */
|
|
1093
|
+
drivePath: z.string(),
|
|
1094
|
+
line: z.number().int().positive(),
|
|
1095
|
+
/** 1-based character column (not byte offset) of the match start within the line. */
|
|
1096
|
+
column: z.number().int().positive(),
|
|
1097
|
+
/** The matching line, truncated if it exceeds the long-line guard. */
|
|
1098
|
+
text: z.string(),
|
|
1099
|
+
before: z.array(z.string()),
|
|
1100
|
+
after: z.array(z.string())
|
|
1101
|
+
});
|
|
1102
|
+
var GrepResultVOSchema = z.object({
|
|
1103
|
+
matches: z.array(GrepMatchVOSchema),
|
|
1104
|
+
filesScanned: z.number().int().nonnegative(),
|
|
1105
|
+
/** Asset ids in scope with no text yet (contentKind text-or-writable-binary, no row). */
|
|
1106
|
+
missing: z.array(z.string()),
|
|
1107
|
+
/** Asset ids in scope whose derived text is stale (source replaced since it was written). */
|
|
1108
|
+
stale: z.array(z.string()),
|
|
1109
|
+
/** Count of assets in scope explicitly marked `none` (no extractable text). */
|
|
1110
|
+
unsearchable: z.number().int().nonnegative(),
|
|
1045
1111
|
/**
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1112
|
+
* Asset ids whose scan was attempted but failed (storage error, corrupt
|
|
1113
|
+
* cache file, object deleted mid-flight) — NOT counted in `filesScanned`.
|
|
1114
|
+
* Honest coverage: these were not actually searched, so a caller must not
|
|
1115
|
+
* treat their absence from `matches` as a clean "no match".
|
|
1049
1116
|
*/
|
|
1050
|
-
|
|
1117
|
+
errored: z.array(z.string()),
|
|
1118
|
+
/**
|
|
1119
|
+
* Count of in-scope, present-and-searchable assets the scan never even
|
|
1120
|
+
* reached because the deadline or `maxMatches` budget ran out first. Only
|
|
1121
|
+
* nonzero when `truncated` is true.
|
|
1122
|
+
*/
|
|
1123
|
+
notReached: z.number().int().nonnegative(),
|
|
1124
|
+
truncated: z.boolean()
|
|
1125
|
+
});
|
|
1126
|
+
var ReadTextLinesInputSchema = z.object({
|
|
1127
|
+
assetId: z.string(),
|
|
1128
|
+
startLine: z.coerce.number().int().min(1),
|
|
1129
|
+
endLine: z.coerce.number().int().min(1)
|
|
1130
|
+
});
|
|
1131
|
+
var ReadLinesVOSchema = z.object({
|
|
1132
|
+
lines: z.array(z.string()),
|
|
1133
|
+
startLine: z.number().int().positive(),
|
|
1134
|
+
endLine: z.number().int().positive(),
|
|
1135
|
+
totalLines: z.number().int().nonnegative(),
|
|
1136
|
+
truncated: z.boolean()
|
|
1137
|
+
});
|
|
1138
|
+
var AssetContentEditSchema = z.object({
|
|
1139
|
+
oldString: z.string().min(1),
|
|
1140
|
+
newString: z.string(),
|
|
1141
|
+
/** Replace every occurrence instead of requiring a single unique match. */
|
|
1142
|
+
replaceAll: z.boolean().optional().default(false)
|
|
1143
|
+
}).strict();
|
|
1144
|
+
var EditAssetContentInputSchema = z.object({
|
|
1145
|
+
assetId: z.string(),
|
|
1146
|
+
edits: z.array(AssetContentEditSchema).min(1),
|
|
1147
|
+
message: z.string().optional().default("Edit file content").describe(
|
|
1148
|
+
'Explanation shown to the human reviewer. Write a conventional-commit style subject \u2014 imperative verb + what + why, e.g. "Fix typo in setup instructions".'
|
|
1149
|
+
),
|
|
1150
|
+
submittedBy: z.string().optional().default("agent")
|
|
1151
|
+
});
|
|
1152
|
+
var AssetDownloadInputSchema = z.object({ assetId: z.string() });
|
|
1153
|
+
var AssetDownloadVOSchema = z.object({
|
|
1154
|
+
assetId: z.string(),
|
|
1155
|
+
downloadUrl: z.string(),
|
|
1156
|
+
fileName: z.string(),
|
|
1157
|
+
mimeType: z.string(),
|
|
1158
|
+
size: z.number().int().nonnegative(),
|
|
1159
|
+
contentHash: z.string().nullable()
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
// ../../packages/busabase-contract/src/domains/assets/contract.ts
|
|
1163
|
+
var UpdateAssetMetadataInputSchema = z.object({
|
|
1164
|
+
assetId: z.string(),
|
|
1165
|
+
metadata: z.record(z.string(), z.unknown()),
|
|
1166
|
+
mode: z.enum(["merge", "replace"]).optional().default("merge")
|
|
1051
1167
|
});
|
|
1168
|
+
var assetsContract = {
|
|
1169
|
+
createUploadUrl: oc.route({
|
|
1170
|
+
method: "POST",
|
|
1171
|
+
path: "/assets/upload-urls",
|
|
1172
|
+
tags: ["Assets"],
|
|
1173
|
+
summary: "Request asset upload URL",
|
|
1174
|
+
successDescription: "Presigned (or dev) upload URL plus the public URL and asset id when identical bytes are already in the library."
|
|
1175
|
+
}).input(RequestUploadUrlInputSchema).output(RequestUploadUrlVOSchema),
|
|
1176
|
+
confirm: oc.route({
|
|
1177
|
+
method: "POST",
|
|
1178
|
+
path: "/assets/confirmations",
|
|
1179
|
+
tags: ["Assets"],
|
|
1180
|
+
summary: "Confirm asset upload",
|
|
1181
|
+
successDescription: "Recorded the file and ensured its Busabase Asset library entry."
|
|
1182
|
+
}).input(ConfirmUploadInputSchema).output(ConfirmUploadVOSchema),
|
|
1183
|
+
list: oc.route({
|
|
1184
|
+
method: "GET",
|
|
1185
|
+
path: "/assets",
|
|
1186
|
+
tags: ["Assets"],
|
|
1187
|
+
summary: "List assets",
|
|
1188
|
+
successDescription: "Every asset in the space, with file metadata and usage counts."
|
|
1189
|
+
}).output(z.array(AssetVOSchema)),
|
|
1190
|
+
get: oc.route({
|
|
1191
|
+
method: "GET",
|
|
1192
|
+
path: "/assets/{assetId}",
|
|
1193
|
+
tags: ["Assets"],
|
|
1194
|
+
summary: "Get asset detail",
|
|
1195
|
+
successDescription: "Asset metadata plus every place it is referenced (where-used)."
|
|
1196
|
+
}).input(z.object({ assetId: z.string() })).output(AssetDetailVOSchema),
|
|
1197
|
+
updateMetadata: oc.route({
|
|
1198
|
+
method: "PATCH",
|
|
1199
|
+
path: "/assets/{assetId}/metadata",
|
|
1200
|
+
tags: ["Assets"],
|
|
1201
|
+
summary: "Update asset metadata",
|
|
1202
|
+
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."
|
|
1203
|
+
}).input(UpdateAssetMetadataInputSchema).output(AssetDetailVOSchema),
|
|
1204
|
+
delete: oc.route({
|
|
1205
|
+
method: "DELETE",
|
|
1206
|
+
path: "/assets/{assetId}",
|
|
1207
|
+
tags: ["Assets"],
|
|
1208
|
+
summary: "Delete asset",
|
|
1209
|
+
successDescription: "Removed the asset and, if no other row references its bytes, the stored object. Refused while the asset is still referenced (where-used)."
|
|
1210
|
+
}).input(z.object({ assetId: z.string() })).output(z.object({ deleted: z.boolean() })),
|
|
1211
|
+
download: oc.route({
|
|
1212
|
+
method: "GET",
|
|
1213
|
+
path: "/assets/{assetId}/content",
|
|
1214
|
+
tags: ["Assets"],
|
|
1215
|
+
summary: "Get an asset's binary content download URL",
|
|
1216
|
+
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."
|
|
1217
|
+
}).input(AssetDownloadInputSchema).output(AssetDownloadVOSchema),
|
|
1218
|
+
// ── Drive Grep Retrieval ─────────────────────────────────────────────────
|
|
1219
|
+
// Busabase stores, indexes, and searches text; it never generates it. Text
|
|
1220
|
+
// always arrives via putText — an agent's own extractor, or (future) an
|
|
1221
|
+
// Outgoing-Hook-triggered service — never a bundled parser/OCR library.
|
|
1222
|
+
putText: oc.route({
|
|
1223
|
+
method: "PUT",
|
|
1224
|
+
path: "/assets/{assetId}/text",
|
|
1225
|
+
tags: ["Assets"],
|
|
1226
|
+
summary: "Write (or mark none) an asset's text slot",
|
|
1227
|
+
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."
|
|
1228
|
+
}).input(PutTextInputSchema).output(AssetTextVOSchema),
|
|
1229
|
+
createTextUploadUrl: oc.route({
|
|
1230
|
+
method: "POST",
|
|
1231
|
+
path: "/assets/text/upload-urls",
|
|
1232
|
+
tags: ["Assets"],
|
|
1233
|
+
summary: "Request a presigned upload URL for large text",
|
|
1234
|
+
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."
|
|
1235
|
+
}).input(CreateTextUploadUrlInputSchema).output(CreateTextUploadUrlVOSchema),
|
|
1236
|
+
grep: oc.route({
|
|
1237
|
+
method: "POST",
|
|
1238
|
+
path: "/assets/grep",
|
|
1239
|
+
tags: ["Assets"],
|
|
1240
|
+
summary: "Search every text-bearing asset in scope",
|
|
1241
|
+
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."
|
|
1242
|
+
}).input(GrepInputSchema).output(GrepResultVOSchema),
|
|
1243
|
+
readTextLines: oc.route({
|
|
1244
|
+
method: "GET",
|
|
1245
|
+
path: "/assets/{assetId}/text/lines",
|
|
1246
|
+
tags: ["Assets"],
|
|
1247
|
+
summary: "Read an exact line range from an asset's text",
|
|
1248
|
+
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."
|
|
1249
|
+
}).input(ReadTextLinesInputSchema).output(ReadLinesVOSchema),
|
|
1250
|
+
// ── editContent — edit an asset's REAL file content via ChangeRequest ──────
|
|
1251
|
+
// Unlike putText (derived/extracted text, direct write, disposable), this
|
|
1252
|
+
// edits the asset's actual mounted Drive/Skill file bytes and always goes
|
|
1253
|
+
// through the existing filetree ChangeRequest pipeline — never auto-merged.
|
|
1254
|
+
editContent: oc.route({
|
|
1255
|
+
method: "POST",
|
|
1256
|
+
path: "/assets/{assetId}/edit-content",
|
|
1257
|
+
tags: ["Assets", "Change Requests"],
|
|
1258
|
+
summary: "Edit an asset's file content via string-replace edits, as a ChangeRequest",
|
|
1259
|
+
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.`
|
|
1260
|
+
}).input(EditAssetContentInputSchema).output(changeRequestSchema)
|
|
1261
|
+
};
|
|
1052
1262
|
var viewFilterOperatorSchema = z.enum([
|
|
1053
1263
|
"contains",
|
|
1054
1264
|
"equals",
|
|
@@ -1459,6 +1669,13 @@ var viewContract = {
|
|
|
1459
1669
|
successDescription: "Created change request that restores an archived View."
|
|
1460
1670
|
}).input(restoreViewInputSchema.extend({ viewId: z.string() })).output(changeRequestSchema)
|
|
1461
1671
|
};
|
|
1672
|
+
var ReadDocLinesInputSchema = z.object({
|
|
1673
|
+
nodeId: z.string(),
|
|
1674
|
+
startLine: z.coerce.number().int().min(1),
|
|
1675
|
+
endLine: z.coerce.number().int().min(1)
|
|
1676
|
+
});
|
|
1677
|
+
|
|
1678
|
+
// ../../packages/busabase-contract/src/domains/doc/contract.ts
|
|
1462
1679
|
var docSchema = z.object({
|
|
1463
1680
|
node: nodeSchema,
|
|
1464
1681
|
storagePrefix: z.string(),
|
|
@@ -1508,6 +1725,13 @@ var docContract = {
|
|
|
1508
1725
|
summary: "Get Doc node",
|
|
1509
1726
|
successDescription: "Doc node detail and body."
|
|
1510
1727
|
}).input(z.object({ nodeId: z.string() })).output(docSchema),
|
|
1728
|
+
readLines: oc.route({
|
|
1729
|
+
method: "GET",
|
|
1730
|
+
path: "/docs/{nodeId}/lines",
|
|
1731
|
+
tags: ["Docs"],
|
|
1732
|
+
summary: "Read an exact line range from a Doc body",
|
|
1733
|
+
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."
|
|
1734
|
+
}).input(ReadDocLinesInputSchema).output(ReadLinesVOSchema),
|
|
1511
1735
|
updateBody: oc.route({
|
|
1512
1736
|
method: "PUT",
|
|
1513
1737
|
path: "/docs/{nodeId}/body",
|
|
@@ -1523,149 +1747,100 @@ var docContract = {
|
|
|
1523
1747
|
successDescription: "Created a change request that proposes a new Doc body."
|
|
1524
1748
|
}).input(createDocChangeRequestInputSchema.extend({ nodeId: z.string() })).output(changeRequestSchema)
|
|
1525
1749
|
};
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1750
|
+
|
|
1751
|
+
// ../../packages/busabase-contract/src/domains/drive/contract.ts
|
|
1752
|
+
var driveContract = makeFileTreeContract("drives", "Drives");
|
|
1753
|
+
var DumpTableSchema = z.enum([
|
|
1754
|
+
"nodes",
|
|
1755
|
+
"bases",
|
|
1756
|
+
"baseFields",
|
|
1757
|
+
"views",
|
|
1758
|
+
"records",
|
|
1759
|
+
"fieldValues",
|
|
1760
|
+
"recordLinks",
|
|
1761
|
+
/** The physical bytes registry (open-domains/attachments) an Asset's `attachmentId` FKs into. */
|
|
1762
|
+
"attachments",
|
|
1763
|
+
"assets",
|
|
1764
|
+
"assetUsages",
|
|
1765
|
+
"assetTexts",
|
|
1766
|
+
"commits",
|
|
1767
|
+
"changeRequests",
|
|
1768
|
+
"operations",
|
|
1769
|
+
"comments",
|
|
1770
|
+
"reviews",
|
|
1771
|
+
"auditEvents"
|
|
1772
|
+
]);
|
|
1773
|
+
var ExportTablesInputSchema = z.object({
|
|
1774
|
+
table: DumpTableSchema,
|
|
1775
|
+
/** Opaque pagination cursor from a previous page's `nextCursor`; omit for the first page. */
|
|
1776
|
+
cursor: z.string().optional(),
|
|
1777
|
+
/** Page size, default 500, capped at 2000. */
|
|
1778
|
+
limit: z.number().int().positive().max(2e3).optional().default(500)
|
|
1534
1779
|
});
|
|
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)
|
|
1780
|
+
var ExportTablesVOSchema = z.object({
|
|
1781
|
+
rows: z.array(z.record(z.string(), z.unknown())),
|
|
1782
|
+
nextCursor: z.string().nullable()
|
|
1567
1783
|
});
|
|
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)
|
|
1784
|
+
var ImportBeginVOSchema = z.object({
|
|
1785
|
+
sessionId: z.string()
|
|
1581
1786
|
});
|
|
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)
|
|
1787
|
+
var ImportTablesInputSchema = z.object({
|
|
1788
|
+
sessionId: z.string(),
|
|
1789
|
+
table: z.union([DumpTableSchema, z.literal("docBodies"), z.literal("attachmentBlobs")]),
|
|
1790
|
+
rows: z.array(z.record(z.string(), z.unknown()))
|
|
1791
|
+
});
|
|
1792
|
+
var ImportTablesVOSchema = z.object({
|
|
1793
|
+
inserted: z.number().int().nonnegative()
|
|
1794
|
+
});
|
|
1795
|
+
var ImportSessionInputSchema = z.object({
|
|
1796
|
+
sessionId: z.string()
|
|
1797
|
+
});
|
|
1798
|
+
var ImportCommitVOSchema = z.object({
|
|
1799
|
+
ok: z.boolean(),
|
|
1800
|
+
warnings: z.array(z.string())
|
|
1801
|
+
});
|
|
1802
|
+
var ImportAbortVOSchema = z.object({
|
|
1803
|
+
ok: z.boolean()
|
|
1605
1804
|
});
|
|
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
1805
|
|
|
1667
|
-
// ../../packages/busabase-contract/src/domains/
|
|
1668
|
-
var
|
|
1806
|
+
// ../../packages/busabase-contract/src/domains/dump/contract.ts
|
|
1807
|
+
var dumpContract = {
|
|
1808
|
+
exportTables: oc.route({
|
|
1809
|
+
method: "POST",
|
|
1810
|
+
path: "/dump/export/tables",
|
|
1811
|
+
tags: ["Dump"],
|
|
1812
|
+
summary: "Export raw rows for one table (cursor-paginated)",
|
|
1813
|
+
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."
|
|
1814
|
+
}).input(ExportTablesInputSchema).output(ExportTablesVOSchema),
|
|
1815
|
+
importBegin: oc.route({
|
|
1816
|
+
method: "POST",
|
|
1817
|
+
path: "/dump/import/begin",
|
|
1818
|
+
tags: ["Dump"],
|
|
1819
|
+
summary: "Begin a full-fidelity import session",
|
|
1820
|
+
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)."
|
|
1821
|
+
}).output(ImportBeginVOSchema),
|
|
1822
|
+
importTables: oc.route({
|
|
1823
|
+
method: "POST",
|
|
1824
|
+
path: "/dump/import/tables",
|
|
1825
|
+
tags: ["Dump"],
|
|
1826
|
+
summary: "Import a batch of raw rows into an open session",
|
|
1827
|
+
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)."
|
|
1828
|
+
}).input(ImportTablesInputSchema).output(ImportTablesVOSchema),
|
|
1829
|
+
importCommit: oc.route({
|
|
1830
|
+
method: "POST",
|
|
1831
|
+
path: "/dump/import/commit",
|
|
1832
|
+
tags: ["Dump"],
|
|
1833
|
+
summary: "Finalize an import session",
|
|
1834
|
+
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."
|
|
1835
|
+
}).input(ImportSessionInputSchema).output(ImportCommitVOSchema),
|
|
1836
|
+
importAbort: oc.route({
|
|
1837
|
+
method: "POST",
|
|
1838
|
+
path: "/dump/import/abort",
|
|
1839
|
+
tags: ["Dump"],
|
|
1840
|
+
summary: "Abort an import session",
|
|
1841
|
+
successDescription: "Best-effort cleanup of everything written so far in this session. Only ever touches the target space validated empty at `importBegin` time."
|
|
1842
|
+
}).input(ImportSessionInputSchema).output(ImportAbortVOSchema)
|
|
1843
|
+
};
|
|
1669
1844
|
z.object({
|
|
1670
1845
|
assetId: z.string()
|
|
1671
1846
|
});
|
|
@@ -1809,15 +1984,20 @@ var vaultContract = {
|
|
|
1809
1984
|
successDescription: "Removed local Vault secrets and variables."
|
|
1810
1985
|
}).output(VaultSuccessSchema)
|
|
1811
1986
|
};
|
|
1812
|
-
var WebhookEventTypeSchema = z.enum([
|
|
1813
|
-
|
|
1987
|
+
var WebhookEventTypeSchema = z.enum([
|
|
1988
|
+
"record.created",
|
|
1989
|
+
"ai_mention",
|
|
1990
|
+
"changes_requested",
|
|
1991
|
+
"asset.uploaded"
|
|
1992
|
+
]);
|
|
1993
|
+
z.enum(["webhook", "notify_agent", "run_function"]);
|
|
1814
1994
|
var WebhookDeliveryStatusSchema = z.enum(["success", "failed", "skipped"]);
|
|
1815
1995
|
var WebhookHttpConfigSchema = z.object({
|
|
1816
1996
|
targetUrl: z.string().url(),
|
|
1817
1997
|
secret: z.string().min(1).max(256).optional(),
|
|
1818
1998
|
headers: z.record(z.string(), z.string()).optional()
|
|
1819
1999
|
});
|
|
1820
|
-
var
|
|
2000
|
+
var WebhookFunctionConfigSchema = z.object({
|
|
1821
2001
|
code: z.string().min(1).max(2e4),
|
|
1822
2002
|
timeoutMs: z.number().int().min(100).max(5e3).default(2e3)
|
|
1823
2003
|
});
|
|
@@ -1826,7 +2006,7 @@ var WebhookHttpConfigVOSchema = z.object({
|
|
|
1826
2006
|
hasSecret: z.boolean(),
|
|
1827
2007
|
headers: z.record(z.string(), z.string()).optional()
|
|
1828
2008
|
});
|
|
1829
|
-
var
|
|
2009
|
+
var WebhookFunctionConfigVOSchema = WebhookFunctionConfigSchema;
|
|
1830
2010
|
var webhookRuleCommonInputFields = {
|
|
1831
2011
|
name: z.string().min(1).max(200),
|
|
1832
2012
|
eventType: WebhookEventTypeSchema,
|
|
@@ -1846,8 +2026,8 @@ var WebhookRuleInputSchema = z.discriminatedUnion("actionKind", [
|
|
|
1846
2026
|
}),
|
|
1847
2027
|
z.object({
|
|
1848
2028
|
...webhookRuleCommonInputFields,
|
|
1849
|
-
actionKind: z.literal("
|
|
1850
|
-
config:
|
|
2029
|
+
actionKind: z.literal("run_function"),
|
|
2030
|
+
config: WebhookFunctionConfigSchema
|
|
1851
2031
|
})
|
|
1852
2032
|
]);
|
|
1853
2033
|
var WebhookRuleUpdateInputSchema = z.discriminatedUnion("actionKind", [
|
|
@@ -1866,8 +2046,8 @@ var WebhookRuleUpdateInputSchema = z.discriminatedUnion("actionKind", [
|
|
|
1866
2046
|
z.object({
|
|
1867
2047
|
id: z.string(),
|
|
1868
2048
|
...webhookRuleCommonInputFields,
|
|
1869
|
-
actionKind: z.literal("
|
|
1870
|
-
config:
|
|
2049
|
+
actionKind: z.literal("run_function"),
|
|
2050
|
+
config: WebhookFunctionConfigSchema
|
|
1871
2051
|
})
|
|
1872
2052
|
]);
|
|
1873
2053
|
var webhookRuleVOCommonFields = {
|
|
@@ -1896,8 +2076,8 @@ var WebhookRuleVOSchema = z.discriminatedUnion("actionKind", [
|
|
|
1896
2076
|
}),
|
|
1897
2077
|
z.object({
|
|
1898
2078
|
...webhookRuleVOCommonFields,
|
|
1899
|
-
actionKind: z.literal("
|
|
1900
|
-
config:
|
|
2079
|
+
actionKind: z.literal("run_function"),
|
|
2080
|
+
config: WebhookFunctionConfigVOSchema
|
|
1901
2081
|
})
|
|
1902
2082
|
]);
|
|
1903
2083
|
var WebhookDeliveryVOSchema = z.object({
|
|
@@ -1937,7 +2117,7 @@ var webhookContract = {
|
|
|
1937
2117
|
path: "/webhooks",
|
|
1938
2118
|
tags: ["Webhooks"],
|
|
1939
2119
|
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
|
|
2120
|
+
successDescription: "Created webhook automation rule. Dispatches on the configured event via an HTTP webhook, an agent notification, or a sandboxed function."
|
|
1941
2121
|
}).input(WebhookRuleInputSchema).output(WebhookRuleVOSchema),
|
|
1942
2122
|
update: oc.route({
|
|
1943
2123
|
method: "PUT",
|
|
@@ -2000,6 +2180,107 @@ var listActivityResponseSchema = z.object({
|
|
|
2000
2180
|
items: z.array(activityItemSchema),
|
|
2001
2181
|
nextCursor: z.string().nullable()
|
|
2002
2182
|
});
|
|
2183
|
+
var GrepSourceSchema = z.enum(["files", "docs", "records"]);
|
|
2184
|
+
var UnifiedGrepFilesScopeSchema = z.object({
|
|
2185
|
+
assetIds: z.array(z.string()).optional(),
|
|
2186
|
+
/** Drive/Skill mounted path prefix (matches `busabase_asset_usages.path`). */
|
|
2187
|
+
drivePath: z.string().optional(),
|
|
2188
|
+
mimeTypes: z.array(z.string()).optional()
|
|
2189
|
+
});
|
|
2190
|
+
var UnifiedGrepDocsScopeSchema = z.object({
|
|
2191
|
+
nodeIds: z.array(z.string()).optional()
|
|
2192
|
+
});
|
|
2193
|
+
var UnifiedGrepRecordsScopeSchema = z.object({
|
|
2194
|
+
baseIds: z.array(z.string()).optional(),
|
|
2195
|
+
baseSlugs: z.array(z.string()).optional()
|
|
2196
|
+
});
|
|
2197
|
+
var UnifiedGrepScopeSchema = z.object({
|
|
2198
|
+
files: UnifiedGrepFilesScopeSchema.optional(),
|
|
2199
|
+
docs: UnifiedGrepDocsScopeSchema.optional(),
|
|
2200
|
+
records: UnifiedGrepRecordsScopeSchema.optional()
|
|
2201
|
+
});
|
|
2202
|
+
var UnifiedGrepInputSchema = z.object({
|
|
2203
|
+
pattern: z.string().min(1),
|
|
2204
|
+
/** JS RegExp flags, e.g. `"i"` for case-insensitive — same language as `assets.grep`. */
|
|
2205
|
+
flags: z.string().optional().default(""),
|
|
2206
|
+
/** Which sources to scan. Omitted = all three (`files`, `docs`, `records`). */
|
|
2207
|
+
sources: z.array(GrepSourceSchema).optional(),
|
|
2208
|
+
scope: UnifiedGrepScopeSchema.optional(),
|
|
2209
|
+
/** Shared across every scanned source — files run to completion first, then docs, then whatever remains goes to records. */
|
|
2210
|
+
maxMatches: z.coerce.number().int().min(1).max(GREP_HARD_MAX_MATCHES).optional().default(GREP_DEFAULT_MAX_MATCHES),
|
|
2211
|
+
contextLines: z.coerce.number().int().min(0).max(GREP_MAX_CONTEXT_LINES).optional().default(GREP_DEFAULT_CONTEXT_LINES)
|
|
2212
|
+
});
|
|
2213
|
+
var grepHitFields = {
|
|
2214
|
+
line: z.number().int().positive(),
|
|
2215
|
+
/** 1-based character column (not byte offset) of the match start within the line. */
|
|
2216
|
+
column: z.number().int().positive(),
|
|
2217
|
+
/** The matching line, truncated if it exceeds the long-line guard. */
|
|
2218
|
+
text: z.string(),
|
|
2219
|
+
before: z.array(z.string()),
|
|
2220
|
+
after: z.array(z.string())
|
|
2221
|
+
};
|
|
2222
|
+
var UnifiedGrepFileMatchVOSchema = z.object({
|
|
2223
|
+
source: z.literal("files"),
|
|
2224
|
+
assetId: z.string(),
|
|
2225
|
+
fileName: z.string(),
|
|
2226
|
+
/** Drive/Skill mounted path, or "" when the asset isn't path-mounted (e.g. a File node). */
|
|
2227
|
+
drivePath: z.string(),
|
|
2228
|
+
...grepHitFields
|
|
2229
|
+
});
|
|
2230
|
+
var UnifiedGrepDocMatchVOSchema = z.object({
|
|
2231
|
+
source: z.literal("docs"),
|
|
2232
|
+
nodeId: z.string(),
|
|
2233
|
+
slug: z.string(),
|
|
2234
|
+
name: z.string(),
|
|
2235
|
+
...grepHitFields
|
|
2236
|
+
});
|
|
2237
|
+
var UnifiedGrepRecordMatchVOSchema = z.object({
|
|
2238
|
+
source: z.literal("records"),
|
|
2239
|
+
baseId: z.string(),
|
|
2240
|
+
baseSlug: z.string(),
|
|
2241
|
+
recordId: z.string(),
|
|
2242
|
+
fieldSlug: z.string(),
|
|
2243
|
+
...grepHitFields
|
|
2244
|
+
});
|
|
2245
|
+
var UnifiedGrepMatchVOSchema = z.discriminatedUnion("source", [
|
|
2246
|
+
UnifiedGrepFileMatchVOSchema,
|
|
2247
|
+
UnifiedGrepDocMatchVOSchema,
|
|
2248
|
+
UnifiedGrepRecordMatchVOSchema
|
|
2249
|
+
]);
|
|
2250
|
+
var UnifiedGrepFilesCoverageSchema = z.object({
|
|
2251
|
+
scanned: z.number().int().nonnegative(),
|
|
2252
|
+
missing: z.array(z.string()),
|
|
2253
|
+
stale: z.array(z.string()),
|
|
2254
|
+
unsearchable: z.number().int().nonnegative(),
|
|
2255
|
+
errored: z.array(z.string()),
|
|
2256
|
+
notReached: z.number().int().nonnegative()
|
|
2257
|
+
});
|
|
2258
|
+
var UnifiedGrepDocsCoverageSchema = z.object({
|
|
2259
|
+
scanned: z.number().int().nonnegative(),
|
|
2260
|
+
/** Doc node ids whose body read/scan was attempted but failed — NOT a clean "scanned, no match". */
|
|
2261
|
+
errored: z.array(z.string()),
|
|
2262
|
+
/** Count of in-scope docs the scan never reached because the deadline/maxMatches budget ran out first. */
|
|
2263
|
+
notReached: z.number().int().nonnegative()
|
|
2264
|
+
});
|
|
2265
|
+
var UnifiedGrepRecordsCoverageSchema = z.object({
|
|
2266
|
+
scanned: z.number().int().nonnegative(),
|
|
2267
|
+
/** Record ids whose commit-fields read/flatten/scan was attempted but failed — NOT a clean "scanned, no match". */
|
|
2268
|
+
errored: z.array(z.string()),
|
|
2269
|
+
/** Count of in-scope records the scan never reached because the deadline/maxMatches budget ran out first. */
|
|
2270
|
+
notReached: z.number().int().nonnegative()
|
|
2271
|
+
});
|
|
2272
|
+
var UnifiedGrepCoverageSchema = z.object({
|
|
2273
|
+
files: UnifiedGrepFilesCoverageSchema,
|
|
2274
|
+
docs: UnifiedGrepDocsCoverageSchema,
|
|
2275
|
+
records: UnifiedGrepRecordsCoverageSchema
|
|
2276
|
+
});
|
|
2277
|
+
var UnifiedGrepResultVOSchema = z.object({
|
|
2278
|
+
/** Deterministic order: every `files` match, then every `docs` match, then every `records` match. */
|
|
2279
|
+
matches: z.array(UnifiedGrepMatchVOSchema),
|
|
2280
|
+
coverage: UnifiedGrepCoverageSchema,
|
|
2281
|
+
/** True when any source truncated, or any source has `notReached > 0`. */
|
|
2282
|
+
truncated: z.boolean()
|
|
2283
|
+
});
|
|
2003
2284
|
|
|
2004
2285
|
// ../../packages/busabase-contract/src/contract/busabase.ts
|
|
2005
2286
|
var changeRequestBatchResultSchema = z.object({
|
|
@@ -2029,6 +2310,18 @@ var busabaseContractRoutes = {
|
|
|
2029
2310
|
summary: "Search Busabase",
|
|
2030
2311
|
successDescription: "Paginated search results across records, change requests, Bases, File nodes, and Assets."
|
|
2031
2312
|
}).input(searchInputSchema).output(searchResponseSchema),
|
|
2313
|
+
// Unified Grep (P2a files+docs, P2b records) — top-level, cross-source
|
|
2314
|
+
// superset of `assets.grep`. See apps/busabase/content/spec/unified-grep.md.
|
|
2315
|
+
// Composes `logic/grep.ts`; `assets.grep` (files-only specialist) is
|
|
2316
|
+
// unchanged and stays the dedicated endpoint for its fuller
|
|
2317
|
+
// missing/stale/unsearchable reporting.
|
|
2318
|
+
grep: oc.route({
|
|
2319
|
+
method: "POST",
|
|
2320
|
+
path: "/grep",
|
|
2321
|
+
tags: ["Search"],
|
|
2322
|
+
summary: "Search files, Docs, and Base records with one pattern (unified grep)",
|
|
2323
|
+
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."
|
|
2324
|
+
}).input(UnifiedGrepInputSchema).output(UnifiedGrepResultVOSchema),
|
|
2032
2325
|
nodes: {
|
|
2033
2326
|
list: oc.route({
|
|
2034
2327
|
method: "GET",
|
|
@@ -2051,6 +2344,13 @@ var busabaseContractRoutes = {
|
|
|
2051
2344
|
summary: "Create Node tree change request",
|
|
2052
2345
|
successDescription: "Created change request for folder or node tree changes."
|
|
2053
2346
|
}).input(createNodeChangeRequestInputSchema).output(changeRequestSchema),
|
|
2347
|
+
move: oc.route({
|
|
2348
|
+
method: "POST",
|
|
2349
|
+
path: "/nodes/{nodeId}/move",
|
|
2350
|
+
tags: ["Nodes"],
|
|
2351
|
+
summary: "Move or reorder a node",
|
|
2352
|
+
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."
|
|
2353
|
+
}).input(moveNodeInputSchema).output(changeRequestSchema),
|
|
2054
2354
|
purge: oc.route({
|
|
2055
2355
|
method: "DELETE",
|
|
2056
2356
|
path: "/nodes/{nodeId}",
|
|
@@ -2117,12 +2417,14 @@ var busabaseContractRoutes = {
|
|
|
2117
2417
|
bases: baseContract,
|
|
2118
2418
|
skills: skillContract,
|
|
2119
2419
|
drives: driveContract,
|
|
2420
|
+
airapps: airappContract,
|
|
2120
2421
|
files: fileContract,
|
|
2121
2422
|
docs: docContract,
|
|
2122
2423
|
folders: folderContract,
|
|
2123
2424
|
assets: assetsContract,
|
|
2124
2425
|
vault: vaultContract,
|
|
2125
2426
|
webhooks: webhookContract,
|
|
2427
|
+
dump: dumpContract,
|
|
2126
2428
|
changeRequests: {
|
|
2127
2429
|
list: oc.route({
|
|
2128
2430
|
method: "GET",
|
|
@@ -2456,6 +2758,19 @@ var Busabase = class {
|
|
|
2456
2758
|
search(input) {
|
|
2457
2759
|
return this.client.search(input);
|
|
2458
2760
|
}
|
|
2761
|
+
/**
|
|
2762
|
+
* Unified grep — one regex/literal pattern scanned across every in-scope
|
|
2763
|
+
* source (Drive/Skill files, Doc bodies, and Base records — records read
|
|
2764
|
+
* the canonical `headCommit.fields`, never the truncated search
|
|
2765
|
+
* projection), with a shared `maxMatches`/deadline budget and per-source
|
|
2766
|
+
* honest coverage. Use this when the answer could live anywhere; use
|
|
2767
|
+
* `client.assets.grep` directly instead when you specifically only care
|
|
2768
|
+
* about files and want its fuller `missing`/`stale`/`unsearchable`
|
|
2769
|
+
* file-only reporting.
|
|
2770
|
+
*/
|
|
2771
|
+
grep(input) {
|
|
2772
|
+
return this.client.grep(input);
|
|
2773
|
+
}
|
|
2459
2774
|
/**
|
|
2460
2775
|
* Supply text for an Asset's Drive Grep Retrieval text slot in one call —
|
|
2461
2776
|
* inline for small text, a presigned upload for large text — so callers
|