busabase-sdk 0.9.2 → 0.9.4

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +10932 -4862
  2. package/dist/index.js +655 -92
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -56,6 +56,7 @@ var ConfirmUploadVOSchema = z.object({
56
56
  storageKey: z.string(),
57
57
  publicUrl: z.string()
58
58
  });
59
+ var AssetTextStatusSchema = z.enum(["missing", "present", "none", "stale"]);
59
60
  var AssetVOSchema = z.object({
60
61
  id: z.string(),
61
62
  attachmentId: z.string(),
@@ -69,6 +70,8 @@ var AssetVOSchema = z.object({
69
70
  contentHash: z.string().nullable(),
70
71
  /** How many places reference this asset (Base records + Doc bodies). */
71
72
  usageCount: z.number().int().nonnegative(),
73
+ /** Drive Grep Retrieval text-slot status — see {@link AssetTextStatusSchema}. */
74
+ textStatus: AssetTextStatusSchema,
72
75
  createdAt: z.string()
73
76
  });
74
77
  var AssetUsageVOSchema = z.object({
@@ -92,6 +95,108 @@ var AssetDetailVOSchema = z.object({
92
95
  asset: AssetVOSchema,
93
96
  usages: z.array(AssetUsageVOSchema)
94
97
  });
98
+ var PutTextInputSchema = z.object({
99
+ assetId: z.string(),
100
+ /** Inline text body, ≤ 1 MB. For larger text, use `createTextUploadUrl` + bind by `storageKey`. */
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()
114
+ });
115
+ var AssetTextVOSchema = z.object({
116
+ assetId: z.string(),
117
+ textStatus: AssetTextStatusSchema,
118
+ lineCount: z.number().int().nonnegative(),
119
+ charCount: z.number().int().nonnegative(),
120
+ byteCount: z.number().int().nonnegative()
121
+ });
122
+ var CreateTextUploadUrlInputSchema = z.object({
123
+ assetId: z.string(),
124
+ sizeBytes: z.number().int().positive(),
125
+ /** Optional claim, mirrors `RequestUploadUrlDTO.contentHash` (never trusted for addressing). */
126
+ contentHash: z.string().optional()
127
+ });
128
+ var CreateTextUploadUrlVOSchema = z.object({
129
+ uploadUrl: z.string(),
130
+ storageKey: z.string(),
131
+ expiresIn: z.number().int().nonnegative()
132
+ });
133
+ var GrepScopeSchema = z.object({
134
+ assetIds: z.array(z.string()).optional(),
135
+ /** Drive/Skill mounted path prefix (matches `busabase_asset_usages.path`). */
136
+ drivePath: z.string().optional(),
137
+ mimeTypes: z.array(z.string()).optional()
138
+ });
139
+ var GREP_DEFAULT_MAX_MATCHES = 100;
140
+ var GREP_HARD_MAX_MATCHES = 1e3;
141
+ var GREP_DEFAULT_CONTEXT_LINES = 0;
142
+ var GREP_MAX_CONTEXT_LINES = 10;
143
+ var GrepInputSchema = z.object({
144
+ pattern: z.string().min(1),
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)
150
+ });
151
+ var GrepMatchVOSchema = z.object({
152
+ assetId: z.string(),
153
+ fileName: z.string(),
154
+ /** Drive/Skill mounted path, or "" when the asset isn't path-mounted (e.g. a File node). */
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())
163
+ });
164
+ var GrepResultVOSchema = z.object({
165
+ matches: z.array(GrepMatchVOSchema),
166
+ filesScanned: z.number().int().nonnegative(),
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()
187
+ });
188
+ var ReadTextLinesInputSchema = z.object({
189
+ assetId: z.string(),
190
+ startLine: z.coerce.number().int().min(1),
191
+ endLine: z.coerce.number().int().min(1)
192
+ });
193
+ var ReadLinesVOSchema = z.object({
194
+ lines: z.array(z.string()),
195
+ startLine: z.number().int().positive(),
196
+ endLine: z.number().int().positive(),
197
+ totalLines: z.number().int().nonnegative(),
198
+ truncated: z.boolean()
199
+ });
95
200
 
96
201
  // ../../packages/busabase-contract/src/domains/assets/contract.ts
97
202
  var UpdateAssetMetadataInputSchema = z.object({
@@ -133,7 +238,7 @@ var assetsContract = {
133
238
  path: "/assets/{assetId}/metadata",
134
239
  tags: ["Assets"],
135
240
  summary: "Update asset metadata",
136
- successDescription: "Updated AI-readable metadata for a file, such as summary, extracted text, tags, source URL, or schema-specific hints."
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."
137
242
  }).input(UpdateAssetMetadataInputSchema).output(AssetDetailVOSchema),
138
243
  delete: oc.route({
139
244
  method: "DELETE",
@@ -141,7 +246,39 @@ var assetsContract = {
141
246
  tags: ["Assets"],
142
247
  summary: "Delete asset",
143
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)."
144
- }).input(z.object({ assetId: z.string() })).output(z.object({ deleted: z.boolean() }))
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)
145
282
  };
146
283
  var i18n = {
147
284
  locales: ["en", "zh-CN", "zh-TW", "ja", "ko", "de", "fr", "es", "pt"]};
@@ -170,6 +307,7 @@ var fieldTypeSchema = z.enum([
170
307
  "select",
171
308
  "multiselect",
172
309
  "url",
310
+ "embed",
173
311
  "email",
174
312
  "phone",
175
313
  "created_time",
@@ -207,6 +345,11 @@ var fieldOptionsSchema = z.object({
207
345
  code: z.object({
208
346
  language: z.string().optional()
209
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(),
210
353
  inverseFieldId: z.string().optional(),
211
354
  multiple: z.boolean().optional(),
212
355
  // Display formatting for `number` columns (Notion-style: one number type,
@@ -257,7 +400,12 @@ var createBaseInputSchema = z.object({
257
400
  required: z.boolean().default(false),
258
401
  options: fieldOptionsSchema.optional().default({})
259
402
  })
260
- ).default([])
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)
261
409
  });
262
410
  var createBaseFieldInputSchema = z.object({
263
411
  name: fieldNameSchema,
@@ -629,20 +777,21 @@ var commentSchema = z.object({
629
777
  createdAt: z.string(),
630
778
  updatedAt: z.string()
631
779
  });
780
+ var changeRequestStatusSchema = z.enum([
781
+ "in_review",
782
+ "changes_requested",
783
+ "approved",
784
+ "rejected",
785
+ "merged",
786
+ "abandoned",
787
+ "conflict"
788
+ ]);
632
789
  var changeRequestSchema = z.object({
633
790
  id: z.string(),
634
791
  baseId: z.string().nullable(),
635
792
  targetType: z.enum(["base", "node"]),
636
793
  nodeId: z.string().nullable(),
637
- status: z.enum([
638
- "in_review",
639
- "changes_requested",
640
- "approved",
641
- "rejected",
642
- "merged",
643
- "abandoned",
644
- "conflict"
645
- ]),
794
+ status: changeRequestStatusSchema,
646
795
  submittedBy: z.string(),
647
796
  submittedByUser: userRefSchema.nullable().optional().default(null),
648
797
  sourceMeta: z.record(z.string(), z.unknown()),
@@ -688,7 +837,14 @@ var liveEventSchema = z.object({
688
837
  "change_request.updated",
689
838
  "change_request.deleted",
690
839
  "change_request.reviewed",
691
- "change_request.merged"
840
+ "change_request.merged",
841
+ // Fired only when a CONTENT change request freshly enters human review
842
+ // (record_* ops created via record-ops.ts) — never for structural ops
843
+ // that auto-merge instantly (those still fire "change_request.created"
844
+ // via the audit funnel, but nothing needs reviewing). Consumed by
845
+ // `use-live-sync.ts` to pop a desktop Notification, and by
846
+ // busabase-cloud's host hook to persist an inbox notification row.
847
+ "change_request.pending_review"
692
848
  ]),
693
849
  spaceId: z.string(),
694
850
  actorId: z.string(),
@@ -718,6 +874,8 @@ var auditActionSchema = z.enum([
718
874
  "drive.created",
719
875
  "asset.deleted",
720
876
  "asset.metadata_updated",
877
+ "asset.text_written",
878
+ "asset.text_marked_none",
721
879
  "node.purged"
722
880
  ]);
723
881
  var auditEventSchema = z.object({
@@ -838,6 +996,25 @@ var createCommentInputSchema = commentSubjectInputSchema.extend({
838
996
  var listInputSchema = z.object({
839
997
  limit: z.coerce.number().int().min(1).max(100).optional().default(50)
840
998
  }).optional().default({ limit: 50 });
999
+ var listChangeRequestsPagedInputSchema = z.object({
1000
+ limit: z.coerce.number().int().min(1).max(100).optional().default(50),
1001
+ /** Opaque base64 cursor (`createdAt|id`) for keyset pagination. */
1002
+ cursor: z.string().optional(),
1003
+ status: z.array(changeRequestStatusSchema).optional(),
1004
+ mine: z.boolean().optional()
1005
+ }).optional().default({ limit: 50 });
1006
+ var listChangeRequestsResponseSchema = z.object({
1007
+ changeRequests: z.array(changeRequestSchema),
1008
+ nextCursor: z.string().nullable()
1009
+ });
1010
+ var changeRequestCountsSchema = z.object({
1011
+ review: z.number().int().nonnegative(),
1012
+ changes: z.number().int().nonnegative(),
1013
+ created: z.number().int().nonnegative(),
1014
+ approved: z.number().int().nonnegative(),
1015
+ merged: z.number().int().nonnegative(),
1016
+ rejected: z.number().int().nonnegative()
1017
+ });
841
1018
  var searchInputSchema = z.object({
842
1019
  query: z.string().default(""),
843
1020
  limit: z.coerce.number().int().min(1).max(100).optional().default(20),
@@ -872,6 +1049,69 @@ var authInfoSchema = z.object({
872
1049
  */
873
1050
  spaces: z.array(authSpaceSchema)
874
1051
  });
1052
+ var viewFilterOperatorSchema = z.enum([
1053
+ "contains",
1054
+ "equals",
1055
+ "not_empty",
1056
+ "is_empty",
1057
+ "is_true",
1058
+ "is_false"
1059
+ ]);
1060
+ var viewFilterSchema = z.object({
1061
+ fieldSlug: z.string(),
1062
+ // Stable field identity — survives slug reuse; populated on merge.
1063
+ fieldId: z.string().optional(),
1064
+ operator: viewFilterOperatorSchema,
1065
+ value: z.unknown().optional()
1066
+ });
1067
+ var viewSortSchema = z.object({
1068
+ direction: z.enum(["asc", "desc"]),
1069
+ fieldSlug: z.string(),
1070
+ fieldId: z.string().optional()
1071
+ });
1072
+ var viewConfigSchema = z.object({
1073
+ filters: z.array(viewFilterSchema).default([]),
1074
+ sorts: z.array(viewSortSchema).default([]),
1075
+ visibleFieldSlugs: z.array(z.string()).nullable().optional()
1076
+ });
1077
+ var viewSchema = z.object({
1078
+ id: z.string(),
1079
+ baseId: z.string(),
1080
+ slug: z.string(),
1081
+ name: z.string(),
1082
+ description: z.string(),
1083
+ type: z.literal("table"),
1084
+ config: viewConfigSchema,
1085
+ status: z.enum(["active", "archived"]),
1086
+ createdBy: z.string(),
1087
+ createdByUser: userRefSchema.nullable().optional().default(null),
1088
+ archivedAt: z.string().nullable(),
1089
+ createdAt: z.string(),
1090
+ updatedAt: z.string()
1091
+ });
1092
+ var createViewInputSchema = z.object({
1093
+ config: viewConfigSchema.optional().default({ filters: [], sorts: [] }),
1094
+ description: z.string().optional().default(""),
1095
+ message: z.string().optional().default("Create view"),
1096
+ name: z.string().min(1),
1097
+ slug: z.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
1098
+ submittedBy: z.string().optional().default("local-producer")
1099
+ });
1100
+ var updateViewInputSchema = z.object({
1101
+ config: viewConfigSchema.optional(),
1102
+ description: z.string().optional(),
1103
+ message: z.string().optional().default("Update view"),
1104
+ name: z.string().min(1).optional(),
1105
+ submittedBy: z.string().optional().default("local-producer")
1106
+ });
1107
+ var deleteViewInputSchema = z.object({
1108
+ message: z.string().optional().default("Delete view"),
1109
+ submittedBy: z.string().optional().default("local-producer")
1110
+ });
1111
+ var restoreViewInputSchema = z.object({
1112
+ message: z.string().optional().default("Restore view"),
1113
+ submittedBy: z.string().optional().default("local-producer")
1114
+ });
875
1115
 
876
1116
  // ../../packages/busabase-contract/src/domains/base/contract/record-schemas.ts
877
1117
  var recordSchema = z.object({
@@ -889,16 +1129,44 @@ var recordSchema = z.object({
889
1129
  base: baseSchema,
890
1130
  headCommit: commitSchema
891
1131
  });
1132
+ var listRecordsFilterSchema = z.object({
1133
+ fieldSlug: z.string(),
1134
+ fieldType: z.string().optional(),
1135
+ operator: viewFilterOperatorSchema,
1136
+ value: z.unknown().optional()
1137
+ });
1138
+ var listRecordsSortSchema = z.object({
1139
+ fieldSlug: z.string(),
1140
+ fieldType: z.string().optional(),
1141
+ direction: z.enum(["asc", "desc"]).optional().default("asc")
1142
+ });
892
1143
  var listRecordsInputSchema = z.object({
893
1144
  limit: z.coerce.number().int().min(1).max(100).optional().default(50),
894
1145
  baseId: z.string().optional(),
895
- /** Opaque base64 cursor (`createdAt:id`) for keyset pagination. */
896
- cursor: z.string().optional()
1146
+ /** Opaque base64 cursor for keyset pagination (createdAt-keyed, or sort-keyed when `sort` is set). */
1147
+ cursor: z.string().optional(),
1148
+ /** View filters for server-side push-down (superset; client still narrows). */
1149
+ filters: z.array(listRecordsFilterSchema).optional(),
1150
+ /** View sort for server-side push-down (number/date fields only). */
1151
+ sort: listRecordsSortSchema.optional()
897
1152
  }).optional().default({ limit: 50 });
898
1153
  var listRecordsResponseSchema = z.object({
899
1154
  records: z.array(recordSchema),
900
1155
  nextCursor: z.string().nullable()
901
1156
  });
1157
+ var listArchivedRecordsPagedInputSchema = z.object({
1158
+ baseId: z.string(),
1159
+ limit: z.coerce.number().int().min(1).max(100).optional().default(50),
1160
+ /** Opaque base64 cursor (createdAt-keyed) for keyset pagination. */
1161
+ cursor: z.string().optional()
1162
+ });
1163
+ var countRecordsInputSchema = z.object({
1164
+ baseId: z.string().optional()
1165
+ }).optional().default({});
1166
+ var countRecordsResponseSchema = z.object({
1167
+ /** Total active records in the space (optionally scoped to a base). */
1168
+ total: z.number().int().nonnegative()
1169
+ });
902
1170
  var createChangeRequestInputSchema = z.object({
903
1171
  fields: z.record(z.string(), z.unknown()).describe(
904
1172
  "Record field values keyed by field slug. The base's PRIMARY field (its first field) becomes the record's display name and the change request title everywhere \u2014 always give it a short, human-readable value, never an id or placeholder."
@@ -940,71 +1208,6 @@ var recordLinkSchema = z.object({
940
1208
  createdAt: z.string(),
941
1209
  updatedAt: z.string()
942
1210
  });
943
- var viewFilterOperatorSchema = z.enum([
944
- "contains",
945
- "equals",
946
- "not_empty",
947
- "is_empty",
948
- "is_true",
949
- "is_false"
950
- ]);
951
- var viewFilterSchema = z.object({
952
- fieldSlug: z.string(),
953
- // Stable field identity — survives slug reuse; populated on merge.
954
- fieldId: z.string().optional(),
955
- operator: viewFilterOperatorSchema,
956
- value: z.unknown().optional()
957
- });
958
- var viewSortSchema = z.object({
959
- direction: z.enum(["asc", "desc"]),
960
- fieldSlug: z.string(),
961
- fieldId: z.string().optional()
962
- });
963
- var viewConfigSchema = z.object({
964
- filters: z.array(viewFilterSchema).default([]),
965
- sorts: z.array(viewSortSchema).default([]),
966
- visibleFieldSlugs: z.array(z.string()).nullable().optional()
967
- });
968
- var viewSchema = z.object({
969
- id: z.string(),
970
- baseId: z.string(),
971
- slug: z.string(),
972
- name: z.string(),
973
- description: z.string(),
974
- type: z.literal("table"),
975
- config: viewConfigSchema,
976
- status: z.enum(["active", "archived"]),
977
- createdBy: z.string(),
978
- createdByUser: userRefSchema.nullable().optional().default(null),
979
- archivedAt: z.string().nullable(),
980
- createdAt: z.string(),
981
- updatedAt: z.string()
982
- });
983
- var createViewInputSchema = z.object({
984
- config: viewConfigSchema.optional().default({ filters: [], sorts: [] }),
985
- description: z.string().optional().default(""),
986
- message: z.string().optional().default("Create view"),
987
- name: z.string().min(1),
988
- slug: z.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
989
- submittedBy: z.string().optional().default("local-producer")
990
- });
991
- var updateViewInputSchema = z.object({
992
- config: viewConfigSchema.optional(),
993
- description: z.string().optional(),
994
- message: z.string().optional().default("Update view"),
995
- name: z.string().min(1).optional(),
996
- submittedBy: z.string().optional().default("local-producer")
997
- });
998
- var deleteViewInputSchema = z.object({
999
- message: z.string().optional().default("Delete view"),
1000
- submittedBy: z.string().optional().default("local-producer")
1001
- });
1002
- var restoreViewInputSchema = z.object({
1003
- message: z.string().optional().default("Restore view"),
1004
- submittedBy: z.string().optional().default("local-producer")
1005
- });
1006
-
1007
- // ../../packages/busabase-contract/src/domains/base/contract/routes.ts
1008
1211
  var baseContract = {
1009
1212
  list: oc.route({
1010
1213
  method: "GET",
@@ -1055,13 +1258,20 @@ var baseContract = {
1055
1258
  summary: "List archived records for a Base",
1056
1259
  successDescription: "Records that have been archived (soft-deleted) from a Base."
1057
1260
  }).input(z.object({ baseId: z.string() })).output(z.array(recordSchema)),
1261
+ listArchivedRecordsPaged: oc.route({
1262
+ method: "GET",
1263
+ path: "/bases/{baseId}/records/archived/paged",
1264
+ tags: ["Records"],
1265
+ summary: "List archived records for a Base with keyset pagination",
1266
+ successDescription: "A page of archived (soft-deleted) records plus an opaque nextCursor (null at the end)."
1267
+ }).input(listArchivedRecordsPagedInputSchema).output(listRecordsResponseSchema),
1058
1268
  create: oc.route({
1059
1269
  method: "POST",
1060
1270
  path: "/bases",
1061
1271
  tags: ["Bases"],
1062
1272
  summary: "Create Base",
1063
- successDescription: "Created Base."
1064
- }).input(createBaseInputSchema).output(baseSchema),
1273
+ successDescription: "Review-first by default: a pending ChangeRequest proposing the Base. Returns the materialized Base instead when `autoMerge: true` is passed."
1274
+ }).input(createBaseInputSchema).output(z.union([baseSchema, changeRequestSchema])),
1065
1275
  createChangeRequest: oc.route({
1066
1276
  method: "POST",
1067
1277
  path: "/bases/{baseId}/change-requests",
@@ -1169,6 +1379,13 @@ var recordContract = {
1169
1379
  summary: "List records with keyset pagination",
1170
1380
  successDescription: "A page of canonical records plus an opaque nextCursor (null at the end)."
1171
1381
  }).input(listRecordsInputSchema).output(listRecordsResponseSchema),
1382
+ count: oc.route({
1383
+ method: "GET",
1384
+ path: "/records/count",
1385
+ tags: ["Records"],
1386
+ summary: "Count records",
1387
+ successDescription: "Total active records in the space, optionally scoped to a base."
1388
+ }).input(countRecordsInputSchema).output(countRecordsResponseSchema),
1172
1389
  get: oc.route({
1173
1390
  method: "GET",
1174
1391
  path: "/records/{recordId}",
@@ -1252,7 +1469,12 @@ var createDocInputSchema = z.object({
1252
1469
  slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
1253
1470
  name: z.string().min(1),
1254
1471
  description: z.string().optional().default(""),
1255
- body: z.string().optional().default("")
1472
+ body: z.string().optional().default(""),
1473
+ // Review-first by default: without `autoMerge: true`, this proposes the Doc
1474
+ // as a pending ChangeRequest (status "in_review") instead of creating it
1475
+ // immediately. Pass `autoMerge: true` only for callers that don't need human
1476
+ // review (seed/migration scripts, an explicit no-review agent task).
1477
+ autoMerge: z.boolean().optional().default(false)
1256
1478
  });
1257
1479
  var updateDocInputSchema = z.object({
1258
1480
  body: z.string()
@@ -1277,8 +1499,8 @@ var docContract = {
1277
1499
  path: "/docs",
1278
1500
  tags: ["Docs"],
1279
1501
  summary: "Create Doc node",
1280
- successDescription: "Created Doc node and initialized its body."
1281
- }).input(createDocInputSchema).output(docSchema),
1502
+ successDescription: "Review-first by default: a pending ChangeRequest proposing the Doc. Returns the materialized Doc node instead when `autoMerge: true` is passed."
1503
+ }).input(createDocInputSchema).output(z.union([docSchema, changeRequestSchema])),
1282
1504
  get: oc.route({
1283
1505
  method: "GET",
1284
1506
  path: "/docs/{nodeId}",
@@ -1350,7 +1572,12 @@ var createFileTreeInputSchema = z.object({
1350
1572
  description: z.string().optional().default(""),
1351
1573
  visibility: z.enum(["private", "workspace", "public"]).optional().default("private"),
1352
1574
  version: z.string().optional().default("0.1.0"),
1353
- files: z.array(z.union([assetFileInputSchema, textFileInputSchema])).optional().default([])
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)
1354
1581
  });
1355
1582
  var fileTreeFileOperationInputSchema = z.union([
1356
1583
  assetFileOperationInputSchema,
@@ -1392,8 +1619,8 @@ var makeFileTreeContract = (routeBase, tag) => {
1392
1619
  path: basePath,
1393
1620
  tags: [tag],
1394
1621
  summary: `Create ${label} node`,
1395
- successDescription: `Created ${label} node and initialized file tree.`
1396
- }).input(createFileTreeInputSchema).output(fileTreeNodeSchema),
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])),
1397
1624
  get: oc.route({
1398
1625
  method: "GET",
1399
1626
  path: `${basePath}/{nodeId}`,
@@ -1453,7 +1680,12 @@ var createFileNodeInputSchema = z.object({
1453
1680
  slug: z.string().min(1).regex(/^[a-z0-9-]+$/),
1454
1681
  name: z.string().min(1),
1455
1682
  description: z.string().optional().default(""),
1456
- assetId: z.string().min(1)
1683
+ assetId: z.string().min(1),
1684
+ // Review-first by default: without `autoMerge: true`, this proposes the File
1685
+ // node as a pending ChangeRequest (status "in_review") instead of creating it
1686
+ // immediately. Pass `autoMerge: true` only for callers that don't need human
1687
+ // review (seed/migration scripts, an explicit no-review agent task).
1688
+ autoMerge: z.boolean().optional().default(false)
1457
1689
  });
1458
1690
  var fileContract = {
1459
1691
  list: oc.route({
@@ -1468,8 +1700,8 @@ var fileContract = {
1468
1700
  path: "/files",
1469
1701
  tags: ["Files"],
1470
1702
  summary: "Create File node",
1471
- successDescription: "Created a first-class File node that references an Asset."
1472
- }).input(createFileNodeInputSchema).output(FileNodeVOSchema),
1703
+ successDescription: "Review-first by default: a pending ChangeRequest proposing the File node. Returns the materialized File node instead when `autoMerge: true` is passed."
1704
+ }).input(createFileNodeInputSchema).output(z.union([FileNodeVOSchema, changeRequestSchema])),
1473
1705
  get: oc.route({
1474
1706
  method: "GET",
1475
1707
  path: "/files/{nodeId}",
@@ -1501,6 +1733,273 @@ var folderContract = {
1501
1733
 
1502
1734
  // ../../packages/busabase-contract/src/domains/skill/contract.ts
1503
1735
  var skillContract = makeFileTreeContract("skills", "Skills");
1736
+ var VaultItemKeySchema = z.string().trim().min(1).max(128).regex(/^[A-Z_][A-Z0-9_]*$/, "Use uppercase letters, numbers, and underscores");
1737
+ var VaultItemValueSchema = z.string().max(8192);
1738
+ var VaultItemKindSchema = z.enum(["secret", "variable"]);
1739
+ var VaultScopeTypeSchema = z.enum([
1740
+ "personal",
1741
+ "workspace",
1742
+ "base",
1743
+ "agent",
1744
+ "tool",
1745
+ "api_key"
1746
+ ]);
1747
+ var VaultEnvironmentSchema = z.enum(["local", "development", "staging", "production"]);
1748
+ var VaultAccessPolicySchema = z.object({
1749
+ runtime: z.boolean().default(true),
1750
+ reveal: z.boolean().default(true),
1751
+ edit: z.boolean().default(true),
1752
+ share: z.boolean().default(false)
1753
+ });
1754
+ var VaultItemInputSchema = z.object({
1755
+ id: z.string().optional(),
1756
+ kind: VaultItemKindSchema,
1757
+ key: VaultItemKeySchema,
1758
+ value: VaultItemValueSchema,
1759
+ scopeType: VaultScopeTypeSchema.default("personal"),
1760
+ scopeId: z.string().trim().nullable().optional(),
1761
+ environment: VaultEnvironmentSchema.default("local"),
1762
+ description: z.string().trim().max(512).default(""),
1763
+ access: VaultAccessPolicySchema.default({
1764
+ runtime: true,
1765
+ reveal: true,
1766
+ edit: true,
1767
+ share: false
1768
+ })
1769
+ });
1770
+ var UpdateVaultSettingsInputSchema = z.object({
1771
+ items: z.array(VaultItemInputSchema).max(200)
1772
+ });
1773
+ var VaultItemVOSchema = VaultItemInputSchema.extend({
1774
+ id: z.string(),
1775
+ scopeId: z.string().nullable(),
1776
+ updatedAt: z.string(),
1777
+ createdAt: z.string(),
1778
+ lastUsedAt: z.string().nullable()
1779
+ });
1780
+ var VaultSettingsVOSchema = z.object({
1781
+ ownerId: z.string().nullable(),
1782
+ items: z.array(VaultItemVOSchema),
1783
+ updatedAt: z.string().nullable()
1784
+ });
1785
+ z.record(VaultItemKeySchema, VaultItemValueSchema).default({});
1786
+ var VaultSuccessSchema = z.object({ success: z.boolean() });
1787
+
1788
+ // ../../packages/busabase-contract/src/domains/vault/contract.ts
1789
+ var vaultContract = {
1790
+ get: oc.route({
1791
+ method: "GET",
1792
+ path: "/vault",
1793
+ tags: ["Vault"],
1794
+ summary: "Get local Vault settings",
1795
+ successDescription: "Local Vault secrets and variables for this Busabase instance."
1796
+ }).output(VaultSettingsVOSchema),
1797
+ update: oc.route({
1798
+ method: "PUT",
1799
+ path: "/vault",
1800
+ tags: ["Vault"],
1801
+ summary: "Replace local Vault settings",
1802
+ successDescription: "Updated local Vault secrets and variables."
1803
+ }).input(UpdateVaultSettingsInputSchema).output(VaultSettingsVOSchema),
1804
+ clear: oc.route({
1805
+ method: "DELETE",
1806
+ path: "/vault",
1807
+ tags: ["Vault"],
1808
+ summary: "Clear local Vault settings",
1809
+ successDescription: "Removed local Vault secrets and variables."
1810
+ }).output(VaultSuccessSchema)
1811
+ };
1812
+ var WebhookEventTypeSchema = z.enum(["record.created", "ai_mention", "changes_requested"]);
1813
+ z.enum(["webhook", "notify_agent", "run_snippet"]);
1814
+ var WebhookDeliveryStatusSchema = z.enum(["success", "failed", "skipped"]);
1815
+ var WebhookHttpConfigSchema = z.object({
1816
+ targetUrl: z.string().url(),
1817
+ secret: z.string().min(1).max(256).optional(),
1818
+ headers: z.record(z.string(), z.string()).optional()
1819
+ });
1820
+ var WebhookSnippetConfigSchema = z.object({
1821
+ code: z.string().min(1).max(2e4),
1822
+ timeoutMs: z.number().int().min(100).max(5e3).default(2e3)
1823
+ });
1824
+ var WebhookHttpConfigVOSchema = z.object({
1825
+ targetUrl: z.string().url(),
1826
+ hasSecret: z.boolean(),
1827
+ headers: z.record(z.string(), z.string()).optional()
1828
+ });
1829
+ var WebhookSnippetConfigVOSchema = WebhookSnippetConfigSchema;
1830
+ var webhookRuleCommonInputFields = {
1831
+ name: z.string().min(1).max(200),
1832
+ eventType: WebhookEventTypeSchema,
1833
+ baseId: z.string().nullable().optional(),
1834
+ enabled: z.boolean().default(true)
1835
+ };
1836
+ var WebhookRuleInputSchema = z.discriminatedUnion("actionKind", [
1837
+ z.object({
1838
+ ...webhookRuleCommonInputFields,
1839
+ actionKind: z.literal("webhook"),
1840
+ config: WebhookHttpConfigSchema
1841
+ }),
1842
+ z.object({
1843
+ ...webhookRuleCommonInputFields,
1844
+ actionKind: z.literal("notify_agent"),
1845
+ config: WebhookHttpConfigSchema
1846
+ }),
1847
+ z.object({
1848
+ ...webhookRuleCommonInputFields,
1849
+ actionKind: z.literal("run_snippet"),
1850
+ config: WebhookSnippetConfigSchema
1851
+ })
1852
+ ]);
1853
+ var WebhookRuleUpdateInputSchema = z.discriminatedUnion("actionKind", [
1854
+ z.object({
1855
+ id: z.string(),
1856
+ ...webhookRuleCommonInputFields,
1857
+ actionKind: z.literal("webhook"),
1858
+ config: WebhookHttpConfigSchema
1859
+ }),
1860
+ z.object({
1861
+ id: z.string(),
1862
+ ...webhookRuleCommonInputFields,
1863
+ actionKind: z.literal("notify_agent"),
1864
+ config: WebhookHttpConfigSchema
1865
+ }),
1866
+ z.object({
1867
+ id: z.string(),
1868
+ ...webhookRuleCommonInputFields,
1869
+ actionKind: z.literal("run_snippet"),
1870
+ config: WebhookSnippetConfigSchema
1871
+ })
1872
+ ]);
1873
+ var webhookRuleVOCommonFields = {
1874
+ id: z.string(),
1875
+ spaceId: z.string(),
1876
+ baseId: z.string().nullable(),
1877
+ name: z.string(),
1878
+ eventType: WebhookEventTypeSchema,
1879
+ enabled: z.boolean(),
1880
+ createdBy: z.string(),
1881
+ createdAt: z.string(),
1882
+ updatedAt: z.string(),
1883
+ lastTriggeredAt: z.string().nullable(),
1884
+ lastStatus: WebhookDeliveryStatusSchema.nullable()
1885
+ };
1886
+ var WebhookRuleVOSchema = z.discriminatedUnion("actionKind", [
1887
+ z.object({
1888
+ ...webhookRuleVOCommonFields,
1889
+ actionKind: z.literal("webhook"),
1890
+ config: WebhookHttpConfigVOSchema
1891
+ }),
1892
+ z.object({
1893
+ ...webhookRuleVOCommonFields,
1894
+ actionKind: z.literal("notify_agent"),
1895
+ config: WebhookHttpConfigVOSchema
1896
+ }),
1897
+ z.object({
1898
+ ...webhookRuleVOCommonFields,
1899
+ actionKind: z.literal("run_snippet"),
1900
+ config: WebhookSnippetConfigVOSchema
1901
+ })
1902
+ ]);
1903
+ var WebhookDeliveryVOSchema = z.object({
1904
+ id: z.string(),
1905
+ ruleId: z.string(),
1906
+ eventType: WebhookEventTypeSchema,
1907
+ status: WebhookDeliveryStatusSchema,
1908
+ httpStatus: z.number().nullable(),
1909
+ detail: z.string().nullable(),
1910
+ durationMs: z.number().nullable(),
1911
+ createdAt: z.string()
1912
+ });
1913
+ z.object({}).optional().default({});
1914
+ var ListWebhookDeliveriesInputSchema = z.object({
1915
+ ruleId: z.string(),
1916
+ limit: z.coerce.number().int().min(1).max(100).default(20)
1917
+ });
1918
+
1919
+ // ../../packages/busabase-contract/src/domains/webhook/contract.ts
1920
+ var webhookContract = {
1921
+ list: oc.route({
1922
+ method: "GET",
1923
+ path: "/webhooks",
1924
+ tags: ["Webhooks"],
1925
+ summary: "List webhook automation rules",
1926
+ successDescription: "Configured webhook automation rules for this space."
1927
+ }).output(WebhookRuleVOSchema.array()),
1928
+ get: oc.route({
1929
+ method: "GET",
1930
+ path: "/webhooks/{id}",
1931
+ tags: ["Webhooks"],
1932
+ summary: "Get webhook automation rule",
1933
+ successDescription: "A single webhook automation rule."
1934
+ }).input(z.object({ id: z.string() })).output(WebhookRuleVOSchema),
1935
+ create: oc.route({
1936
+ method: "POST",
1937
+ path: "/webhooks",
1938
+ tags: ["Webhooks"],
1939
+ 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 snippet."
1941
+ }).input(WebhookRuleInputSchema).output(WebhookRuleVOSchema),
1942
+ update: oc.route({
1943
+ method: "PUT",
1944
+ path: "/webhooks/{id}",
1945
+ tags: ["Webhooks"],
1946
+ summary: "Update webhook automation rule",
1947
+ successDescription: "Updated webhook automation rule."
1948
+ }).input(WebhookRuleUpdateInputSchema).output(WebhookRuleVOSchema),
1949
+ delete: oc.route({
1950
+ method: "DELETE",
1951
+ path: "/webhooks/{id}",
1952
+ tags: ["Webhooks"],
1953
+ summary: "Delete webhook automation rule",
1954
+ successDescription: "Removed the webhook automation rule."
1955
+ }).input(z.object({ id: z.string() })).output(z.object({ success: z.boolean() })),
1956
+ deliveries: oc.route({
1957
+ method: "GET",
1958
+ path: "/webhooks/{ruleId}/deliveries",
1959
+ tags: ["Webhooks"],
1960
+ summary: "List webhook rule delivery attempts",
1961
+ successDescription: "Recent delivery attempts for a webhook rule, newest first."
1962
+ }).input(ListWebhookDeliveriesInputSchema).output(WebhookDeliveryVOSchema.array()),
1963
+ testFire: oc.route({
1964
+ method: "POST",
1965
+ path: "/webhooks/{id}/test-fire",
1966
+ tags: ["Webhooks"],
1967
+ summary: "Test-fire a webhook automation rule",
1968
+ successDescription: "The delivery record produced by firing this rule right now with a synthetic payload \u2014 runs regardless of the rule's enabled state or its real trigger."
1969
+ }).input(z.object({ id: z.string() })).output(WebhookDeliveryVOSchema)
1970
+ };
1971
+ var activityItemSchema = z.discriminatedUnion("kind", [
1972
+ z.object({
1973
+ kind: z.literal("change_request"),
1974
+ timestamp: z.string(),
1975
+ changeRequest: changeRequestSchema
1976
+ }),
1977
+ z.object({
1978
+ kind: z.literal("operation"),
1979
+ timestamp: z.string(),
1980
+ operationId: z.string(),
1981
+ changeRequest: changeRequestSchema
1982
+ }),
1983
+ z.object({
1984
+ kind: z.literal("record"),
1985
+ timestamp: z.string(),
1986
+ record: recordSchema
1987
+ }),
1988
+ z.object({
1989
+ kind: z.literal("audit"),
1990
+ timestamp: z.string(),
1991
+ auditEvent: auditEventSchema,
1992
+ record: recordSchema.nullable()
1993
+ })
1994
+ ]);
1995
+ var listActivityPagedInputSchema = z.object({
1996
+ limit: z.coerce.number().int().min(1).max(100).optional().default(50),
1997
+ cursor: z.string().optional()
1998
+ }).optional().default({ limit: 50 });
1999
+ var listActivityResponseSchema = z.object({
2000
+ items: z.array(activityItemSchema),
2001
+ nextCursor: z.string().nullable()
2002
+ });
1504
2003
 
1505
2004
  // ../../packages/busabase-contract/src/contract/busabase.ts
1506
2005
  var changeRequestBatchResultSchema = z.object({
@@ -1576,6 +2075,15 @@ var busabaseContractRoutes = {
1576
2075
  successDescription: "Recorded audit event."
1577
2076
  }).input(createAuditEventInputSchema).output(auditEventSchema)
1578
2077
  },
2078
+ activity: {
2079
+ listPaged: oc.route({
2080
+ method: "GET",
2081
+ path: "/activity/paged",
2082
+ tags: ["Activity"],
2083
+ summary: "List the activity feed with keyset pagination",
2084
+ successDescription: "A page of activity items (change requests, operations, records and audit events merged, newest first) plus an opaque nextCursor (null at the end)."
2085
+ }).input(listActivityPagedInputSchema).output(listActivityResponseSchema)
2086
+ },
1579
2087
  comments: {
1580
2088
  list: oc.route({
1581
2089
  method: "GET",
@@ -1613,6 +2121,8 @@ var busabaseContractRoutes = {
1613
2121
  docs: docContract,
1614
2122
  folders: folderContract,
1615
2123
  assets: assetsContract,
2124
+ vault: vaultContract,
2125
+ webhooks: webhookContract,
1616
2126
  changeRequests: {
1617
2127
  list: oc.route({
1618
2128
  method: "GET",
@@ -1621,6 +2131,20 @@ var busabaseContractRoutes = {
1621
2131
  summary: "List change requests",
1622
2132
  successDescription: "Change requests waiting for review or ready to merge."
1623
2133
  }).input(listInputSchema).output(z.array(changeRequestSchema)),
2134
+ listPaged: oc.route({
2135
+ method: "GET",
2136
+ path: "/change-requests/paged",
2137
+ tags: ["Change Requests"],
2138
+ summary: "List change requests with keyset pagination",
2139
+ successDescription: "A page of change requests plus an opaque nextCursor (null at the end). Filter with `status` and/or `mine`."
2140
+ }).input(listChangeRequestsPagedInputSchema).output(listChangeRequestsResponseSchema),
2141
+ counts: oc.route({
2142
+ method: "GET",
2143
+ path: "/change-requests/counts",
2144
+ tags: ["Change Requests"],
2145
+ summary: "Count change requests by inbox tab",
2146
+ successDescription: "Whole-space change request counts per inbox tab (review / changes / created / approved / merged / rejected)."
2147
+ }).output(changeRequestCountsSchema),
1624
2148
  get: oc.route({
1625
2149
  method: "GET",
1626
2150
  path: "/change-requests/{changeRequestId}",
@@ -1742,6 +2266,7 @@ var notFoundErrors = {
1742
2266
  data: ErrorResponseSchema
1743
2267
  }
1744
2268
  };
2269
+ var { vault: _localVault, ...cloudWorkbenchRoutes } = busabaseContractRoutes;
1745
2270
  var cloudExtraRoutes = {
1746
2271
  system: {
1747
2272
  health: oc.route({
@@ -1822,7 +2347,7 @@ var cloudExtraRoutes = {
1822
2347
  }
1823
2348
  };
1824
2349
  var cloudContract = oc.prefix("/api/v1").router({
1825
- ...busabaseContractRoutes,
2350
+ ...cloudWorkbenchRoutes,
1826
2351
  ...cloudExtraRoutes
1827
2352
  });
1828
2353
 
@@ -1924,10 +2449,48 @@ var Busabase = class {
1924
2449
  get agentTasks() {
1925
2450
  return this.client.agentTasks;
1926
2451
  }
2452
+ get webhooks() {
2453
+ return this.client.webhooks;
2454
+ }
1927
2455
  /** Full-text search across records, change requests, and Bases. */
1928
2456
  search(input) {
1929
2457
  return this.client.search(input);
1930
2458
  }
2459
+ /**
2460
+ * Supply text for an Asset's Drive Grep Retrieval text slot in one call —
2461
+ * inline for small text, a presigned upload for large text — so callers
2462
+ * never see the underlying three-step flow
2463
+ * (`createTextUploadUrl` → PUT bytes → `putText({ storageKey })`).
2464
+ *
2465
+ * @example
2466
+ * ```ts
2467
+ * await bb.putText(assetId, extractedText); // picks inline vs presigned by size
2468
+ * ```
2469
+ */
2470
+ async putText(assetId, text) {
2471
+ const INLINE_TEXT_MAX_BYTES = 1024 * 1024;
2472
+ const byteLength = typeof Buffer !== "undefined" ? Buffer.byteLength(text, "utf8") : new Blob([text]).size;
2473
+ if (byteLength <= INLINE_TEXT_MAX_BYTES) {
2474
+ return this.client.assets.putText({ assetId, text });
2475
+ }
2476
+ const upload = await this.client.assets.createTextUploadUrl({
2477
+ assetId,
2478
+ sizeBytes: byteLength
2479
+ });
2480
+ const doFetch = this.config.fetch ?? fetch;
2481
+ const response = await doFetch(upload.uploadUrl, {
2482
+ method: "PUT",
2483
+ headers: { "content-type": "text/plain; charset=utf-8" },
2484
+ body: text
2485
+ });
2486
+ if (!response.ok) {
2487
+ const detail = await response.text().catch(() => "");
2488
+ throw new Error(
2489
+ `putText: presigned upload failed (${response.status} ${response.statusText})${detail ? `: ${detail}` : ""}`
2490
+ );
2491
+ }
2492
+ return this.client.assets.putText({ assetId, storageKey: upload.storageKey });
2493
+ }
1931
2494
  /** Service health — reaches the server without requiring auth. */
1932
2495
  health() {
1933
2496
  return this.client.system.health();