@howells/stow-server 2.2.1 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -25,7 +25,10 @@ __export(index_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(index_exports);
27
27
  var import_node_crypto = require("crypto");
28
+ var import_promises = require("timers/promises");
28
29
  var import_zod = require("zod");
30
+
31
+ // src/stow-error.ts
29
32
  var StowError = class extends Error {
30
33
  status;
31
34
  code;
@@ -36,6 +39,8 @@ var StowError = class extends Error {
36
39
  this.code = code;
37
40
  }
38
41
  };
42
+
43
+ // src/index.ts
39
44
  var fileColorSchema = import_zod.z.object({
40
45
  position: import_zod.z.number().int(),
41
46
  proportion: import_zod.z.number(),
@@ -194,10 +199,7 @@ var presignDedupeResultSchema = import_zod.z.object({
194
199
  size: import_zod.z.number(),
195
200
  contentType: import_zod.z.string()
196
201
  });
197
- var presignResultSchema = import_zod.z.union([
198
- presignDedupeResultSchema,
199
- presignNewResultSchema
200
- ]);
202
+ var presignResultSchema = import_zod.z.union([presignDedupeResultSchema, presignNewResultSchema]);
201
203
  var confirmResultSchema = import_zod.z.object({
202
204
  key: import_zod.z.string(),
203
205
  url: import_zod.z.string().nullable(),
@@ -249,6 +251,42 @@ var profileResultSchema = import_zod.z.object({
249
251
  createdAt: import_zod.z.string(),
250
252
  updatedAt: import_zod.z.string()
251
253
  });
254
+ var clusterGroupResultSchema = import_zod.z.object({
255
+ id: import_zod.z.string(),
256
+ index: import_zod.z.number().int(),
257
+ name: import_zod.z.string().nullable(),
258
+ description: import_zod.z.string().nullable(),
259
+ fileCount: import_zod.z.number().int(),
260
+ nameGeneratedAt: import_zod.z.string().nullable()
261
+ });
262
+ var clusterResourceResultSchema = import_zod.z.object({
263
+ id: import_zod.z.string(),
264
+ name: import_zod.z.string().nullable(),
265
+ clusterCount: import_zod.z.number().int(),
266
+ fileCount: import_zod.z.number().int(),
267
+ clusteredAt: import_zod.z.string().nullable(),
268
+ createdAt: import_zod.z.string(),
269
+ updatedAt: import_zod.z.string(),
270
+ clusters: import_zod.z.array(clusterGroupResultSchema)
271
+ });
272
+ var clusterFileResultSchema = import_zod.z.object({
273
+ id: import_zod.z.string(),
274
+ key: import_zod.z.string(),
275
+ bucketId: import_zod.z.string(),
276
+ originalFilename: import_zod.z.string().nullable(),
277
+ size: import_zod.z.number(),
278
+ contentType: import_zod.z.string(),
279
+ metadata: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).nullable(),
280
+ createdAt: import_zod.z.string(),
281
+ distance: import_zod.z.number().nullable()
282
+ });
283
+ var clusterFilesResultSchema = import_zod.z.object({
284
+ clusterId: import_zod.z.string(),
285
+ files: import_zod.z.array(clusterFileResultSchema),
286
+ limit: import_zod.z.number().int(),
287
+ offset: import_zod.z.number().int(),
288
+ total: import_zod.z.number().int()
289
+ });
252
290
  var profileFilesResultSchema = import_zod.z.object({
253
291
  id: import_zod.z.string(),
254
292
  fileCount: import_zod.z.number()
@@ -296,12 +334,48 @@ var anchorResponseSchema = import_zod.z.object({
296
334
  var anchorListResponseSchema = import_zod.z.object({
297
335
  anchors: import_zod.z.array(anchorResponseSchema)
298
336
  });
337
+ function sleep(ms) {
338
+ return (0, import_promises.setTimeout)(ms);
339
+ }
340
+ function buildTransformUrl(url, options) {
341
+ if (!(options && (options.width || options.height || options.quality || options.format))) {
342
+ return url;
343
+ }
344
+ const parsed = new URL(url);
345
+ if (options.width) {
346
+ parsed.searchParams.set("w", String(options.width));
347
+ }
348
+ if (options.height) {
349
+ parsed.searchParams.set("h", String(options.height));
350
+ }
351
+ if (options.quality) {
352
+ parsed.searchParams.set("q", String(options.quality));
353
+ }
354
+ if (options.format) {
355
+ parsed.searchParams.set("f", options.format);
356
+ }
357
+ return parsed.toString();
358
+ }
299
359
  var StowServer = class {
300
360
  apiKey;
301
361
  baseUrl;
302
362
  bucket;
303
363
  timeout;
304
364
  retries;
365
+ /**
366
+ * Pure helper for building signed transform URLs from an existing public file URL.
367
+ *
368
+ * This does not perform any network I/O and is safe to pass around as a plain
369
+ * function, for example to view-layer code that needs responsive image URLs.
370
+ */
371
+ getTransformUrl;
372
+ /**
373
+ * Create a server SDK instance.
374
+ *
375
+ * Pass a bare string when you only need the API key and want default values
376
+ * for `baseUrl`, `timeout`, and retries. Pass an object when you want a
377
+ * default bucket, a non-production API origin, or custom transport settings.
378
+ */
305
379
  constructor(config) {
306
380
  if (typeof config === "string") {
307
381
  this.apiKey = config;
@@ -315,10 +389,9 @@ var StowServer = class {
315
389
  this.timeout = config.timeout ?? 3e4;
316
390
  this.retries = config.retries ?? 3;
317
391
  }
392
+ this.getTransformUrl = buildTransformUrl;
318
393
  }
319
- /**
320
- * Get the base URL for this instance (used by client SDK)
321
- */
394
+ /** Return the configured API origin, mainly for adapter packages such as `stow-next`. */
322
395
  getBaseUrl() {
323
396
  return this.baseUrl;
324
397
  }
@@ -432,7 +505,7 @@ var StowServer = class {
432
505
  // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: retry + timeout + error normalization intentionally handled in one request pipeline
433
506
  async request(path, options, schema) {
434
507
  const maxAttempts = this.retries + 1;
435
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
508
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
436
509
  const controller = new AbortController();
437
510
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
438
511
  if (options.signal) {
@@ -454,32 +527,28 @@ var StowServer = class {
454
527
  const code = error.success ? error.data.code : void 0;
455
528
  const isRetryable = response.status === 429 || response.status >= 500;
456
529
  if (isRetryable && attempt < maxAttempts - 1) {
457
- await this.sleep(1e3 * 2 ** attempt);
530
+ await sleep(1e3 * 2 ** attempt);
458
531
  continue;
459
532
  }
460
533
  throw new StowError(message, response.status, code);
461
534
  }
462
535
  return schema ? schema.parse(data) : data;
463
- } catch (err) {
464
- if (err instanceof StowError) {
465
- throw err;
536
+ } catch (error) {
537
+ if (error instanceof StowError) {
538
+ throw error;
466
539
  }
467
- if (err instanceof import_zod.z.ZodError) {
468
- throw new StowError(
469
- "Invalid response format",
470
- 500,
471
- "INVALID_RESPONSE"
472
- );
540
+ if (error instanceof import_zod.z.ZodError) {
541
+ throw new StowError("Invalid response format", 500, "INVALID_RESPONSE");
473
542
  }
474
- if (err instanceof DOMException || err instanceof Error && err.name === "AbortError") {
543
+ if (error instanceof DOMException || error instanceof Error && error.name === "AbortError") {
475
544
  throw new StowError("Request timed out", 408, "TIMEOUT");
476
545
  }
477
546
  if (attempt < maxAttempts - 1) {
478
- await this.sleep(1e3 * 2 ** attempt);
547
+ await sleep(1e3 * 2 ** attempt);
479
548
  continue;
480
549
  }
481
550
  throw new StowError(
482
- err instanceof Error ? err.message : "Network error",
551
+ error instanceof Error ? error.message : "Network error",
483
552
  0,
484
553
  "NETWORK_ERROR"
485
554
  );
@@ -489,11 +558,30 @@ var StowServer = class {
489
558
  }
490
559
  throw new StowError("Max retries exceeded", 0, "MAX_RETRIES");
491
560
  }
492
- sleep(ms) {
493
- return new Promise((resolve) => setTimeout(resolve, ms));
494
- }
495
561
  /**
496
- * Upload a file directly from the server
562
+ * Upload bytes from a trusted server environment.
563
+ *
564
+ * This is the highest-level server upload helper:
565
+ * 1. compute a SHA-256 hash for dedupe
566
+ * 2. request a presigned upload URL
567
+ * 3. PUT bytes to storage
568
+ * 4. confirm the upload with optional AI metadata generation
569
+ *
570
+ * Prefer this method when your code already has the file bytes in memory.
571
+ * Use `getPresignedUrl()` + `confirmUpload()` for direct browser uploads
572
+ * instead, and `uploadFromUrl()` when the source is an external URL.
573
+ *
574
+ * @example
575
+ * ```typescript
576
+ * await stow.uploadFile(buffer, {
577
+ * filename: "product.jpg",
578
+ * contentType: "image/jpeg",
579
+ * bucket: "catalog",
580
+ * metadata: { sku: "SKU-123" },
581
+ * title: true,
582
+ * altText: true,
583
+ * });
584
+ * ```
497
585
  */
498
586
  async uploadFile(file, options) {
499
587
  const filename = options?.filename || "file";
@@ -527,10 +615,7 @@ var StowServer = class {
527
615
  throw new StowError("Failed to upload to storage", uploadRes.status);
528
616
  }
529
617
  return this.request(
530
- this.withBucket(
531
- presign.confirmUrl || "/presign/confirm",
532
- options?.bucket
533
- ),
618
+ this.withBucket(presign.confirmUrl || "/presign/confirm", options?.bucket),
534
619
  {
535
620
  method: "POST",
536
621
  headers: { "Content-Type": "application/json" },
@@ -550,7 +635,12 @@ var StowServer = class {
550
635
  );
551
636
  }
552
637
  /**
553
- * Upload a file from a URL (server-side fetch + upload)
638
+ * Import a remote asset by URL.
639
+ *
640
+ * Stow fetches the remote URL server-side, stores the resulting bytes, and
641
+ * persists the file as if it had been uploaded normally. This is useful for
642
+ * migrations, ingestion pipelines, or bringing third-party assets into Stow
643
+ * without downloading them into your own process first.
554
644
  */
555
645
  async uploadFromUrl(url, filename, options) {
556
646
  const result = await this.request(
@@ -585,7 +675,7 @@ var StowServer = class {
585
675
  * The actual fetch, validation, and upload happen in a background worker.
586
676
  * Use this for bulk imports where you don't need immediate confirmation.
587
677
  */
588
- async queueUploadFromUrl(url, filename, options) {
678
+ queueUploadFromUrl(url, filename, options) {
589
679
  return this.request(
590
680
  this.withBucket("/upload", options?.bucket),
591
681
  {
@@ -606,24 +696,20 @@ var StowServer = class {
606
696
  );
607
697
  }
608
698
  /**
609
- * Get a presigned URL for direct client-side upload.
699
+ * Get a presigned URL for a direct client upload.
700
+ *
701
+ * This is the server-side half of the browser upload flow used by
702
+ * `@howells/stow-client` and `@howells/stow-next`:
703
+ * 1. browser calls your app
704
+ * 2. your app calls `getPresignedUrl()`
705
+ * 3. browser PUTs bytes to `uploadUrl`
706
+ * 4. browser or your app calls `confirmUpload()`
610
707
  *
611
- * This enables uploads that bypass your server entirely:
612
- * 1. Client calls your endpoint
613
- * 2. Your endpoint calls this method
614
- * 3. Client PUTs directly to the returned uploadUrl
615
- * 4. Client calls confirmUpload to finalize
708
+ * If `contentHash` matches an existing file in the target bucket, the API
709
+ * short-circuits with `{ dedupe: true, ... }` and no upload is required.
616
710
  */
617
711
  getPresignedUrl(request) {
618
- const {
619
- filename,
620
- contentType,
621
- size,
622
- route,
623
- bucket,
624
- metadata,
625
- contentHash
626
- } = request;
712
+ const { filename, contentType, size, route, bucket, metadata, contentHash } = request;
627
713
  return this.request(
628
714
  this.withBucket("/presign", bucket),
629
715
  {
@@ -642,8 +728,12 @@ var StowServer = class {
642
728
  );
643
729
  }
644
730
  /**
645
- * Confirm a presigned upload after the client has uploaded to R2.
646
- * This creates the file record in the database.
731
+ * Confirm a direct upload after the client has finished the storage PUT.
732
+ *
733
+ * This finalizes the file record in the database and optionally triggers
734
+ * post-processing such as AI-generated title/description/alt text.
735
+ *
736
+ * Call this exactly once per successful presigned upload.
647
737
  */
648
738
  confirmUpload(request) {
649
739
  const {
@@ -679,9 +769,13 @@ var StowServer = class {
679
769
  );
680
770
  }
681
771
  /**
682
- * List files in the bucket
772
+ * List files in a bucket with optional prefix filtering and enrichment blocks.
773
+ *
774
+ * Use `cursor` to continue pagination from a previous page. When requesting
775
+ * `include`, Stow expands those relationships inline so you can avoid follow-up
776
+ * per-file lookups.
683
777
  *
684
- * @param options.include - Optional enrichment fields: `"tags"`, `"taxonomies"`
778
+ * @param options.include Optional enrichment fields: `"tags"`, `"taxonomies"`
685
779
  */
686
780
  listFiles(options) {
687
781
  const params = new URLSearchParams();
@@ -701,11 +795,7 @@ var StowServer = class {
701
795
  params.set("include", options.include.join(","));
702
796
  }
703
797
  const path = `/files?${params}`;
704
- return this.request(
705
- this.withBucket(path, options?.bucket),
706
- { method: "GET" },
707
- listFilesSchema
708
- );
798
+ return this.request(this.withBucket(path, options?.bucket), { method: "GET" }, listFilesSchema);
709
799
  }
710
800
  /**
711
801
  * Delete a file by key
@@ -728,9 +818,12 @@ var StowServer = class {
728
818
  });
729
819
  }
730
820
  /**
731
- * Get a single file by key
821
+ * Get one file by key.
822
+ *
823
+ * This is the detailed file view and includes dimensions, embeddings status,
824
+ * extracted colors, and AI metadata fields when available.
732
825
  *
733
- * @param options.include - Optional enrichment fields: `"tags"`, `"taxonomies"`
826
+ * @param options.include Optional enrichment fields: `"tags"`, `"taxonomies"`
734
827
  */
735
828
  getFile(key, options) {
736
829
  const params = new URLSearchParams();
@@ -838,35 +931,6 @@ var StowServer = class {
838
931
  replaceResultSchema
839
932
  );
840
933
  }
841
- /**
842
- * Get a transform URL for an image.
843
- *
844
- * Appends transform query params (?w=, ?h=, ?q=, ?f=) to a file URL.
845
- * Transforms are applied at the edge by the Cloudflare Worker — no
846
- * server round-trip needed.
847
- *
848
- * @param url - Full file URL (e.g. from upload result's fileUrl)
849
- * @param options - Transform options (width, height, quality, format)
850
- */
851
- getTransformUrl(url, options) {
852
- if (!(options && (options.width || options.height || options.quality || options.format))) {
853
- return url;
854
- }
855
- const parsed = new URL(url);
856
- if (options.width) {
857
- parsed.searchParams.set("w", String(options.width));
858
- }
859
- if (options.height) {
860
- parsed.searchParams.set("h", String(options.height));
861
- }
862
- if (options.quality) {
863
- parsed.searchParams.set("q", String(options.quality));
864
- }
865
- if (options.format) {
866
- parsed.searchParams.set("f", options.format);
867
- }
868
- return parsed.toString();
869
- }
870
934
  // ============================================================
871
935
  // TAGS - Org-scoped labels for file organization
872
936
  // ============================================================
@@ -944,7 +1008,34 @@ var StowServer = class {
944
1008
  // SEARCH - Vector similarity search
945
1009
  // ============================================================
946
1010
  /**
947
- * Search namespace for vector similarity search
1011
+ * Semantic search namespace.
1012
+ *
1013
+ * Methods:
1014
+ * - `text(...)` embeds text and finds matching files
1015
+ * - `similar(...)` finds nearest neighbors for a file, anchor, profile, or cluster
1016
+ * - `diverse(...)` balances similarity against result spread
1017
+ * - `color(...)` performs palette similarity search
1018
+ * - `image(...)` searches using an existing file or an external image URL
1019
+ *
1020
+ * Cluster-aware search examples:
1021
+ *
1022
+ * @example
1023
+ * ```typescript
1024
+ * const cluster = await stow.clusters.create({
1025
+ * bucket: "inspiration",
1026
+ * fileKeys,
1027
+ * clusterCount: 12,
1028
+ * });
1029
+ *
1030
+ * const firstGroup = cluster.clusters[0];
1031
+ * if (firstGroup) {
1032
+ * const related = await stow.search.similar({
1033
+ * bucket: "inspiration",
1034
+ * clusterId: firstGroup.id,
1035
+ * limit: 20,
1036
+ * });
1037
+ * }
1038
+ * ```
948
1039
  */
949
1040
  get search() {
950
1041
  return {
@@ -1116,7 +1207,11 @@ var StowServer = class {
1116
1207
  // PROFILES - Taste/preference profiles from file collections
1117
1208
  // ============================================================
1118
1209
  /**
1119
- * Profiles namespace for managing taste profiles
1210
+ * Taste-profile namespace.
1211
+ *
1212
+ * Profiles are long-lived preference objects. They can be seeded from files,
1213
+ * updated through weighted signals, clustered into interpretable segments, and
1214
+ * then reused as semantic search seeds.
1120
1215
  */
1121
1216
  get profiles() {
1122
1217
  return {
@@ -1132,6 +1227,49 @@ var StowServer = class {
1132
1227
  renameCluster: (profileId, clusterId, params, bucket) => this.renameProfileCluster(profileId, clusterId, params, bucket)
1133
1228
  };
1134
1229
  }
1230
+ /**
1231
+ * Curated cluster namespace.
1232
+ *
1233
+ * Use this when you have an explicit file set that should be grouped by visual
1234
+ * similarity, but should not be modeled as a behavioral profile.
1235
+ *
1236
+ * Typical workflow:
1237
+ * 1. `create({ fileKeys, clusterCount })`
1238
+ * 2. poll `get(id)` until `clusteredAt` is non-null
1239
+ * 3. inspect `clusters`
1240
+ * 4. fetch representative files with `files(id, clusterId)`
1241
+ * 5. optionally `renameCluster(...)`
1242
+ * 6. use `clusterId` with `search.similar(...)` or `search.diverse(...)`
1243
+ *
1244
+ * @example
1245
+ * ```typescript
1246
+ * const resource = await stow.clusters.create({
1247
+ * bucket: "featured-products",
1248
+ * fileKeys: featuredKeys,
1249
+ * clusterCount: 12,
1250
+ * name: "Navigator groups",
1251
+ * });
1252
+ *
1253
+ * const latest = await stow.clusters.get(resource.id, "featured-products");
1254
+ * const group = latest.clusters[0];
1255
+ * if (group) {
1256
+ * const representatives = await stow.clusters.files(resource.id, group.id, {
1257
+ * limit: 12,
1258
+ * offset: 0,
1259
+ * });
1260
+ * }
1261
+ * ```
1262
+ */
1263
+ get clusters() {
1264
+ return {
1265
+ create: (params) => this.createClustersResource(params),
1266
+ get: (id, bucket) => this.getClustersResource(id, bucket),
1267
+ recluster: (id, params, bucket) => this.reclusterClustersResource(id, params, bucket),
1268
+ files: (id, clusterId, params, bucket) => this.getClusterFiles(id, clusterId, params, bucket),
1269
+ renameCluster: (id, clusterId, params, bucket) => this.renameClusterGroup(id, clusterId, params, bucket),
1270
+ delete: (id, bucket) => this.deleteClustersResource(id, bucket)
1271
+ };
1272
+ }
1135
1273
  createProfile(params) {
1136
1274
  return this.request(
1137
1275
  this.withBucket("/profiles", params?.bucket),
@@ -1154,10 +1292,9 @@ var StowServer = class {
1154
1292
  );
1155
1293
  }
1156
1294
  async deleteProfile(id, bucket) {
1157
- await this.request(
1158
- this.withBucket(`/profiles/${encodeURIComponent(id)}`, bucket),
1159
- { method: "DELETE" }
1160
- );
1295
+ await this.request(this.withBucket(`/profiles/${encodeURIComponent(id)}`, bucket), {
1296
+ method: "DELETE"
1297
+ });
1161
1298
  }
1162
1299
  addProfileFiles(id, fileKeys, bucket) {
1163
1300
  return this.request(
@@ -1204,36 +1341,98 @@ var StowServer = class {
1204
1341
  );
1205
1342
  }
1206
1343
  getProfileClusters(id, bucket) {
1344
+ return this.request(this.withBucket(`/profiles/${encodeURIComponent(id)}/clusters`, bucket), {
1345
+ method: "GET"
1346
+ });
1347
+ }
1348
+ reclusterProfile(id, params, bucket) {
1349
+ return this.request(this.withBucket(`/profiles/${encodeURIComponent(id)}/clusters`, bucket), {
1350
+ method: "POST",
1351
+ headers: { "Content-Type": "application/json" },
1352
+ body: JSON.stringify(
1353
+ params?.clusterCount === void 0 ? {} : { clusterCount: params.clusterCount }
1354
+ )
1355
+ });
1356
+ }
1357
+ renameProfileCluster(profileId, clusterId, params, bucket) {
1207
1358
  return this.request(
1208
- this.withBucket(`/profiles/${encodeURIComponent(id)}/clusters`, bucket),
1209
- { method: "GET" }
1359
+ this.withBucket(
1360
+ `/profiles/${encodeURIComponent(profileId)}/clusters/${encodeURIComponent(clusterId)}`,
1361
+ bucket
1362
+ ),
1363
+ {
1364
+ method: "PUT",
1365
+ headers: { "Content-Type": "application/json" },
1366
+ body: JSON.stringify(params)
1367
+ }
1210
1368
  );
1211
1369
  }
1212
- reclusterProfile(id, params, bucket) {
1370
+ createClustersResource(params) {
1213
1371
  return this.request(
1214
- this.withBucket(`/profiles/${encodeURIComponent(id)}/clusters`, bucket),
1372
+ this.withBucket("/clusters", params.bucket),
1215
1373
  {
1216
1374
  method: "POST",
1217
1375
  headers: { "Content-Type": "application/json" },
1218
1376
  body: JSON.stringify({
1219
- ...params?.clusterCount === void 0 ? {} : { clusterCount: params.clusterCount }
1377
+ fileKeys: params.fileKeys,
1378
+ ...params.clusterCount === void 0 ? {} : { clusterCount: params.clusterCount },
1379
+ ...params.name ? { name: params.name } : {}
1220
1380
  })
1221
- }
1381
+ },
1382
+ clusterResourceResultSchema
1222
1383
  );
1223
1384
  }
1224
- renameProfileCluster(profileId, clusterId, params, bucket) {
1385
+ getClustersResource(id, bucket) {
1386
+ return this.request(
1387
+ this.withBucket(`/clusters/${encodeURIComponent(id)}`, bucket),
1388
+ { method: "GET" },
1389
+ clusterResourceResultSchema
1390
+ );
1391
+ }
1392
+ reclusterClustersResource(id, params, bucket) {
1393
+ return this.request(
1394
+ this.withBucket(`/clusters/${encodeURIComponent(id)}/recluster`, bucket),
1395
+ {
1396
+ method: "POST",
1397
+ headers: { "Content-Type": "application/json" },
1398
+ body: JSON.stringify(
1399
+ params?.clusterCount === void 0 ? {} : { clusterCount: params.clusterCount }
1400
+ )
1401
+ },
1402
+ clusterResourceResultSchema
1403
+ );
1404
+ }
1405
+ getClusterFiles(id, clusterId, params, bucket) {
1406
+ const searchParams = new URLSearchParams();
1407
+ if (params?.limit !== void 0) {
1408
+ searchParams.set("limit", String(params.limit));
1409
+ }
1410
+ if (params?.offset !== void 0) {
1411
+ searchParams.set("offset", String(params.offset));
1412
+ }
1413
+ const qs = searchParams.toString();
1414
+ const path = `/clusters/${encodeURIComponent(id)}/clusters/${encodeURIComponent(clusterId)}/files${qs ? `?${qs}` : ""}`;
1415
+ return this.request(this.withBucket(path, bucket), { method: "GET" }, clusterFilesResultSchema);
1416
+ }
1417
+ renameClusterGroup(id, clusterId, params, bucket) {
1225
1418
  return this.request(
1226
1419
  this.withBucket(
1227
- `/profiles/${encodeURIComponent(profileId)}/clusters/${encodeURIComponent(clusterId)}`,
1420
+ `/clusters/${encodeURIComponent(id)}/clusters/${encodeURIComponent(clusterId)}`,
1228
1421
  bucket
1229
1422
  ),
1230
1423
  {
1231
1424
  method: "PUT",
1232
1425
  headers: { "Content-Type": "application/json" },
1233
1426
  body: JSON.stringify(params)
1234
- }
1427
+ },
1428
+ clusterGroupResultSchema
1235
1429
  );
1236
1430
  }
1431
+ async deleteClustersResource(id, bucket) {
1432
+ await this.request(this.withBucket(`/clusters/${encodeURIComponent(id)}`, bucket), {
1433
+ method: "DELETE"
1434
+ });
1435
+ }
1237
1436
  // ============================================================
1238
1437
  // ANCHORS - Named semantic reference points in vector space
1239
1438
  // ============================================================
@@ -1295,10 +1494,9 @@ var StowServer = class {
1295
1494
  );
1296
1495
  }
1297
1496
  async deleteAnchor(id, options) {
1298
- await this.request(
1299
- this.withBucket(`/anchors/${encodeURIComponent(id)}`, options?.bucket),
1300
- { method: "DELETE" }
1301
- );
1497
+ await this.request(this.withBucket(`/anchors/${encodeURIComponent(id)}`, options?.bucket), {
1498
+ method: "DELETE"
1499
+ });
1302
1500
  }
1303
1501
  };
1304
1502
  // Annotate the CommonJS export names for ESM import in node: