stow-cli 2.0.4 → 2.2.0

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/cli.js CHANGED
@@ -1,359 +1,62 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ CLI_DOCS,
4
+ renderCommandHelp
5
+ } from "./chunk-XJDK2CBE.js";
6
+ import {
7
+ InputValidationError
8
+ } from "./chunk-PLZFHPLC.js";
9
+ import {
10
+ outputError,
11
+ setForceHuman,
12
+ setGlobalFields,
13
+ setGlobalNdjson
14
+ } from "./chunk-P2BKGBQE.js";
2
15
 
3
16
  // src/cli.ts
4
17
  import { StowError } from "@howells/stow-server";
5
18
  import { Command } from "commander";
6
-
7
- // src/lib/cli-docs.ts
8
- var CLI_DOCS = {
9
- root: {
10
- description: "CLI for Stow file storage",
11
- usage: "stow [command] [options]",
12
- examples: [
13
- "stow whoami",
14
- "stow upload ./photo.jpg --bucket photos",
15
- "stow drop ./screenshot.png",
16
- "stow buckets",
17
- "stow files photos --limit 50",
18
- "stow search text 'sunset beach'",
19
- "stow admin health"
20
- ],
21
- notes: [
22
- "Set STOW_API_KEY before running commands that call the API.",
23
- "Set STOW_API_URL to target a non-default environment.",
24
- "Set STOW_ADMIN_SECRET for admin commands."
25
- ]
26
- },
27
- drop: {
28
- description: "Upload a file and get a short URL (quick share)",
29
- usage: "stow drop <file> [options]",
30
- examples: ["stow drop ./video.mp4", "stow drop ./notes.txt --quiet"]
31
- },
32
- upload: {
33
- description: "Upload a file to a bucket",
34
- usage: "stow upload <file> [options]",
35
- examples: [
36
- "stow upload ./logo.png --bucket brand-assets",
37
- "stow upload ./clip.mov --quiet"
38
- ]
39
- },
40
- buckets: {
41
- description: "List your buckets",
42
- usage: "stow buckets",
43
- examples: ["stow buckets"]
44
- },
45
- bucketsCreate: {
46
- description: "Create a new bucket",
47
- usage: "stow buckets create <name> [options]",
48
- examples: [
49
- "stow buckets create photos",
50
- 'stow buckets create docs --description "Product docs"',
51
- "stow buckets create public-media --public"
52
- ]
53
- },
54
- bucketsRename: {
55
- description: "Rename a bucket",
56
- usage: "stow buckets rename <name> <new-name> [options]",
57
- examples: ["stow buckets rename old-name new-name --yes"],
58
- notes: ["Renaming a bucket can break existing public URLs."]
59
- },
60
- bucketsDelete: {
61
- description: "Delete a bucket by ID",
62
- usage: "stow buckets delete <id>",
63
- examples: ["stow buckets delete 8f3d1ab4-..."]
64
- },
65
- files: {
66
- description: "List files in a bucket",
67
- usage: "stow files <bucket> [options]",
68
- examples: [
69
- "stow files photos",
70
- "stow files photos --search avatars/ --limit 100",
71
- "stow files photos --json"
72
- ]
73
- },
74
- filesGet: {
75
- description: "Get details for a single file",
76
- usage: "stow files get <bucket> <key>",
77
- examples: [
78
- "stow files get photos hero.png",
79
- "stow files get photos hero.png --json"
80
- ]
81
- },
82
- filesUpdate: {
83
- description: "Update file metadata",
84
- usage: "stow files update <bucket> <key> -m key=value",
85
- examples: [
86
- "stow files update photos hero.png -m alt='Hero image'",
87
- "stow files update photos hero.png -m category=banner -m priority=high"
88
- ]
89
- },
90
- filesMissing: {
91
- description: "List files missing processing data",
92
- usage: "stow files missing <bucket> <type>",
93
- examples: [
94
- "stow files missing brera dimensions",
95
- "stow files missing brera embeddings --limit 200",
96
- "stow files missing brera colors --json"
97
- ],
98
- notes: ["Valid types: dimensions, embeddings, colors"]
99
- },
100
- filesEnrich: {
101
- description: "Generate title, description, and alt text for an image",
102
- usage: "stow files enrich <bucket> <key>",
103
- examples: [
104
- "stow files enrich photos hero.jpg",
105
- "stow files enrich next l5igro4iutep3"
106
- ],
107
- notes: [
108
- "Triggers title, description, and alt text generation in parallel.",
109
- "Requires a searchable bucket with image files."
110
- ]
111
- },
112
- drops: {
113
- description: "List your drops with usage info",
114
- usage: "stow drops",
115
- examples: ["stow drops"]
116
- },
117
- dropsDelete: {
118
- description: "Delete a drop by ID",
119
- usage: "stow drops delete <id>",
120
- examples: ["stow drops delete drop_abc123"]
121
- },
122
- delete: {
123
- description: "Delete a file from a bucket",
124
- usage: "stow delete <bucket> <key>",
125
- examples: ["stow delete photos hero/banner.png"]
126
- },
127
- whoami: {
128
- description: "Show account info, usage stats, and API key details",
129
- usage: "stow whoami",
130
- examples: ["stow whoami"]
131
- },
132
- open: {
133
- description: "Open a bucket in the browser",
134
- usage: "stow open <bucket>",
135
- examples: ["stow open photos"]
136
- },
137
- interactive: {
138
- description: "Launch interactive TUI mode",
139
- usage: "stow --interactive",
140
- examples: ["stow", "stow --interactive"]
141
- },
142
- search: {
143
- description: "Search files across buckets",
144
- usage: "stow search <subcommand>",
145
- examples: [
146
- "stow search text 'sunset beach' -b photos",
147
- "stow search similar --file hero.png -b photos",
148
- 'stow search color --hex "#ff0000" -b photos',
149
- "stow search diverse -b photos"
150
- ]
151
- },
152
- searchText: {
153
- description: "Semantic text search",
154
- usage: "stow search text <query> [options]",
155
- examples: ["stow search text 'sunset beach' -b photos --limit 10 --json"]
156
- },
157
- searchSimilar: {
158
- description: "Find files similar to a given file",
159
- usage: "stow search similar --file <key> [options]",
160
- examples: ["stow search similar --file hero.png -b photos"]
161
- },
162
- searchColor: {
163
- description: "Search by color",
164
- usage: 'stow search color --hex "#ff0000" [options]',
165
- examples: ['stow search color --hex "#ff0000" -b photos --limit 20']
166
- },
167
- searchDiverse: {
168
- description: "Diversity-aware search",
169
- usage: "stow search diverse [options]",
170
- examples: ["stow search diverse -b photos --limit 20"]
171
- },
172
- tags: {
173
- description: "Manage tags",
174
- usage: "stow tags",
175
- examples: [
176
- "stow tags",
177
- "stow tags create 'Hero Images'",
178
- "stow tags delete <id>"
179
- ]
180
- },
181
- tagsCreate: {
182
- description: "Create a new tag",
183
- usage: "stow tags create <name> [options]",
184
- examples: [
185
- 'stow tags create "Hero Images"',
186
- 'stow tags create "Featured" --color "#ff6600"'
187
- ]
188
- },
189
- tagsDelete: {
190
- description: "Delete a tag by ID",
191
- usage: "stow tags delete <id>",
192
- examples: ["stow tags delete tag_abc123"]
193
- },
194
- profiles: {
195
- description: "Manage taste profiles",
196
- usage: "stow profiles <subcommand>",
197
- examples: [
198
- 'stow profiles create --name "My Profile"',
199
- "stow profiles get <id>",
200
- "stow profiles delete <id>"
201
- ]
202
- },
203
- profilesCreate: {
204
- description: "Create a taste profile",
205
- usage: 'stow profiles create --name "My Profile" [options]',
206
- examples: ['stow profiles create --name "My Profile" -b photos']
207
- },
208
- profilesGet: {
209
- description: "Get a taste profile with clusters",
210
- usage: "stow profiles get <id>",
211
- examples: ["stow profiles get profile_abc123 --json"]
212
- },
213
- profilesDelete: {
214
- description: "Delete a taste profile",
215
- usage: "stow profiles delete <id>",
216
- examples: ["stow profiles delete profile_abc123"]
217
- },
218
- jobs: {
219
- description: "List processing jobs for a bucket",
220
- usage: "stow jobs --bucket <id> [options]",
221
- examples: [
222
- "stow jobs --bucket <id>",
223
- "stow jobs --bucket <id> --status failed",
224
- "stow jobs --bucket <id> --queue extract-colors --json"
225
- ]
226
- },
227
- jobsRetry: {
228
- description: "Retry a failed job",
229
- usage: "stow jobs retry <id> --queue <name> --bucket <id>",
230
- examples: ["stow jobs retry job123 --queue generate-title --bucket <id>"]
231
- },
232
- jobsDelete: {
233
- description: "Remove a job",
234
- usage: "stow jobs delete <id> --queue <name> --bucket <id>",
235
- examples: ["stow jobs delete job123 --queue extract-colors --bucket <id>"]
236
- },
237
- admin: {
238
- description: "Admin commands (requires STOW_ADMIN_SECRET)",
239
- usage: "stow admin <subcommand>",
240
- examples: [
241
- "stow admin health",
242
- "stow admin backfill dimensions --bucket <id> --dry-run",
243
- "stow admin cleanup-drops --dry-run"
244
- ],
245
- notes: ["Requires STOW_ADMIN_SECRET environment variable."]
246
- },
247
- adminHealth: {
248
- description: "Check system health and queue depths",
249
- usage: "stow admin health",
250
- examples: ["stow admin health", "stow admin health --json"]
251
- },
252
- adminBackfill: {
253
- description: "Backfill processing data for files",
254
- usage: "stow admin backfill <type> [options]",
255
- examples: [
256
- "stow admin backfill dimensions --bucket <id> --dry-run",
257
- "stow admin backfill colors --bucket <id> --limit 200",
258
- "stow admin backfill embeddings --bucket <id> --limit 100 --json"
259
- ],
260
- notes: ["Valid types: dimensions, colors, embeddings"]
261
- },
262
- adminCleanupDrops: {
263
- description: "Remove expired drops",
264
- usage: "stow admin cleanup-drops [options]",
265
- examples: ["stow admin cleanup-drops --max-age-hours 24 --dry-run"]
266
- },
267
- adminPurgeEvents: {
268
- description: "Purge old webhook events",
269
- usage: "stow admin purge-events [options]",
270
- examples: ["stow admin purge-events --dry-run"]
271
- },
272
- adminReconcileFiles: {
273
- description: "Reconcile files between R2 and database",
274
- usage: "stow admin reconcile-files --bucket <id>",
275
- examples: ["stow admin reconcile-files --bucket <id> --dry-run"]
276
- },
277
- adminRetrySyncFailures: {
278
- description: "Retry failed S3 sync operations",
279
- usage: "stow admin retry-sync-failures",
280
- examples: ["stow admin retry-sync-failures"]
281
- },
282
- adminJobs: {
283
- description: "List and manage processing jobs",
284
- usage: "stow admin jobs [options]",
285
- examples: [
286
- "stow admin jobs",
287
- "stow admin jobs --status failed",
288
- "stow admin jobs --org <id> --queue generate-title",
289
- "stow admin jobs --json"
290
- ]
291
- },
292
- adminJobsRetry: {
293
- description: "Retry a failed job",
294
- usage: "stow admin jobs retry <id> --queue <name>",
295
- examples: ["stow admin jobs retry job123 --queue generate-title"]
296
- },
297
- adminJobsDelete: {
298
- description: "Remove a job",
299
- usage: "stow admin jobs delete <id> --queue <name>",
300
- examples: ["stow admin jobs delete job123 --queue extract-colors"]
301
- },
302
- adminQueues: {
303
- description: "Show queue depths and counts",
304
- usage: "stow admin queues",
305
- examples: ["stow admin queues", "stow admin queues --json"]
306
- },
307
- adminQueuesClean: {
308
- description: "Clean jobs from a queue",
309
- usage: "stow admin queues clean <name> --failed|--completed",
310
- examples: [
311
- "stow admin queues clean generate-title --failed",
312
- "stow admin queues clean extract-colors --completed --grace 3600"
313
- ]
314
- }
315
- };
316
- function renderCommandHelp(key) {
317
- const doc = CLI_DOCS[key];
318
- const lines = [
319
- "",
320
- `Usage: ${doc.usage}`,
321
- "",
322
- "Examples:",
323
- ...doc.examples.map((example) => ` ${example}`)
324
- ];
325
- if (doc.notes?.length) {
326
- lines.push("", "Notes:", ...doc.notes.map((note) => ` ${note}`));
327
- }
328
- return lines.join("\n");
329
- }
330
-
331
- // src/cli.ts
332
19
  var VERSION = "1.0.0";
333
20
  function handleError(err) {
334
21
  if (err instanceof StowError) {
335
- console.error(`Error: ${err.message}`);
22
+ outputError(err.message, err.code, { status: err.status });
23
+ } else if (err instanceof InputValidationError) {
24
+ outputError(err.message, "INPUT_VALIDATION");
336
25
  } else if (err instanceof Error) {
337
- console.error(`Error: ${err.message}`);
26
+ outputError(err.message);
338
27
  } else {
339
- console.error(`Error: ${err}`);
28
+ outputError(String(err));
340
29
  }
341
- process.exit(1);
342
30
  }
343
31
  var program = new Command();
344
- program.name("stow").description(CLI_DOCS.root.description).version(VERSION).addHelpText("after", renderCommandHelp("root"));
32
+ program.name("stow").description(CLI_DOCS.root.description).version(VERSION).option("--human", "Force human-readable output (default when TTY)").option(
33
+ "--fields <fields>",
34
+ "Comma-separated fields to include in output (e.g. key,similarity)"
35
+ ).option("--ndjson", "Output as newline-delimited JSON (one object per line)").addHelpText("after", renderCommandHelp("root"));
36
+ program.hook("preAction", () => {
37
+ const opts2 = program.opts();
38
+ if (opts2.human) {
39
+ setForceHuman(true);
40
+ }
41
+ if (opts2.fields) {
42
+ setGlobalFields(opts2.fields);
43
+ }
44
+ if (opts2.ndjson) {
45
+ setGlobalNdjson(true);
46
+ }
47
+ });
345
48
  program.command("drop").description(CLI_DOCS.drop.description).argument("<file>", "File to upload").option("-q, --quiet", "Only output the URL").addHelpText("after", renderCommandHelp("drop")).action(async (file, options) => {
346
49
  try {
347
- const { uploadDrop } = await import("./upload-5RIDB2C5.js");
50
+ const { uploadDrop } = await import("./upload-OS6Q6LW5.js");
348
51
  await uploadDrop(file, options);
349
52
  } catch (err) {
350
53
  handleError(err);
351
54
  }
352
55
  });
353
- program.command("upload").description(CLI_DOCS.upload.description).argument("<file>", "File to upload").option("-b, --bucket <name>", "Bucket name or ID").option("-q, --quiet", "Only output the URL").addHelpText("after", renderCommandHelp("upload")).action(
56
+ program.command("upload").description(CLI_DOCS.upload.description).argument("<file>", "File to upload").option("-b, --bucket <name>", "Bucket name or ID").option("-q, --quiet", "Only output the URL").option("--dry-run", "Preview without uploading").addHelpText("after", renderCommandHelp("upload")).action(
354
57
  async (file, options) => {
355
58
  try {
356
- const { uploadFile } = await import("./upload-5RIDB2C5.js");
59
+ const { uploadFile } = await import("./upload-OS6Q6LW5.js");
357
60
  await uploadFile(file, options);
358
61
  } catch (err) {
359
62
  handleError(err);
@@ -362,34 +65,36 @@ program.command("upload").description(CLI_DOCS.upload.description).argument("<fi
362
65
  );
363
66
  var bucketsCmd = program.command("buckets").description(CLI_DOCS.buckets.description).addHelpText("after", renderCommandHelp("buckets")).action(async () => {
364
67
  try {
365
- const { listBuckets } = await import("./buckets-NXIVHPW5.js");
68
+ const { listBuckets } = await import("./buckets-ZHP3LBLC.js");
366
69
  await listBuckets();
367
70
  } catch (err) {
368
71
  handleError(err);
369
72
  }
370
73
  });
371
- bucketsCmd.command("create").description(CLI_DOCS.bucketsCreate.description).argument("<name>", "Bucket name").option("-d, --description <text>", "Bucket description").option("--public", "Make bucket public").addHelpText("after", renderCommandHelp("bucketsCreate")).action(
74
+ bucketsCmd.command("create").description(CLI_DOCS.bucketsCreate.description).argument("<name>", "Bucket name").option("-d, --description <text>", "Bucket description").option("--public", "Make bucket public").option("--dry-run", "Preview without creating").option("--input-json <json>", "Raw JSON payload").addHelpText("after", renderCommandHelp("bucketsCreate")).action(
372
75
  async (name, options) => {
373
76
  try {
374
- const { createBucket } = await import("./buckets-NXIVHPW5.js");
77
+ const { createBucket } = await import("./buckets-ZHP3LBLC.js");
375
78
  await createBucket(name, options);
376
79
  } catch (err) {
377
80
  handleError(err);
378
81
  }
379
82
  }
380
83
  );
381
- bucketsCmd.command("rename").description(CLI_DOCS.bucketsRename.description).argument("<name>", "Current bucket name").argument("<new-name>", "New bucket name").option("-y, --yes", "Skip confirmation warning").addHelpText("after", renderCommandHelp("bucketsRename")).action(async (name, newName, options) => {
382
- try {
383
- const { renameBucket } = await import("./buckets-NXIVHPW5.js");
384
- await renameBucket(name, newName, options);
385
- } catch (err) {
386
- handleError(err);
84
+ bucketsCmd.command("rename").description(CLI_DOCS.bucketsRename.description).argument("<name>", "Current bucket name").argument("<new-name>", "New bucket name").option("-y, --yes", "Skip confirmation warning").option("--dry-run", "Preview without renaming").option("--input-json <json>", "Raw JSON payload").addHelpText("after", renderCommandHelp("bucketsRename")).action(
85
+ async (name, newName, options) => {
86
+ try {
87
+ const { renameBucket } = await import("./buckets-ZHP3LBLC.js");
88
+ await renameBucket(name, newName, options);
89
+ } catch (err) {
90
+ handleError(err);
91
+ }
387
92
  }
388
- });
389
- bucketsCmd.command("delete").description(CLI_DOCS.bucketsDelete.description).argument("<id>", "Bucket ID").addHelpText("after", renderCommandHelp("bucketsDelete")).action(async (id) => {
93
+ );
94
+ bucketsCmd.command("delete").description(CLI_DOCS.bucketsDelete.description).argument("<id>", "Bucket ID").option("--dry-run", "Preview without deleting").addHelpText("after", renderCommandHelp("bucketsDelete")).action(async (id, options) => {
390
95
  try {
391
- const { deleteBucket } = await import("./buckets-NXIVHPW5.js");
392
- await deleteBucket(id);
96
+ const { deleteBucket } = await import("./buckets-ZHP3LBLC.js");
97
+ await deleteBucket(id, options);
393
98
  } catch (err) {
394
99
  handleError(err);
395
100
  }
@@ -401,7 +106,7 @@ var filesCmd = program.command("files").description(CLI_DOCS.files.description).
401
106
  return;
402
107
  }
403
108
  try {
404
- const { listFiles } = await import("./files-E662TXUP.js");
109
+ const { listFiles } = await import("./files-UQODXWNT.js");
405
110
  await listFiles(bucket, options);
406
111
  } catch (err) {
407
112
  handleError(err);
@@ -410,16 +115,16 @@ var filesCmd = program.command("files").description(CLI_DOCS.files.description).
410
115
  );
411
116
  filesCmd.command("get").description(CLI_DOCS.filesGet.description).argument("<bucket>", "Bucket name").argument("<key>", "File key").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("filesGet")).action(async (bucket, key, options) => {
412
117
  try {
413
- const { getFile } = await import("./files-E662TXUP.js");
118
+ const { getFile } = await import("./files-UQODXWNT.js");
414
119
  await getFile(bucket, key, options);
415
120
  } catch (err) {
416
121
  handleError(err);
417
122
  }
418
123
  });
419
- filesCmd.command("update").description(CLI_DOCS.filesUpdate.description).argument("<bucket>", "Bucket name").argument("<key>", "File key").option("-m, --metadata <kv...>", "Metadata key=value pairs").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("filesUpdate")).action(
124
+ filesCmd.command("update").description(CLI_DOCS.filesUpdate.description).argument("<bucket>", "Bucket name").argument("<key>", "File key").option("-m, --metadata <kv...>", "Metadata key=value pairs").option("--json", "Output as JSON").option("--dry-run", "Preview without updating").option("--input-json <json>", "Raw JSON payload").addHelpText("after", renderCommandHelp("filesUpdate")).action(
420
125
  async (bucket, key, options) => {
421
126
  try {
422
- const { updateFile } = await import("./files-E662TXUP.js");
127
+ const { updateFile } = await import("./files-UQODXWNT.js");
423
128
  await updateFile(bucket, key, options);
424
129
  } catch (err) {
425
130
  handleError(err);
@@ -428,7 +133,7 @@ filesCmd.command("update").description(CLI_DOCS.filesUpdate.description).argumen
428
133
  );
429
134
  filesCmd.command("enrich").description(CLI_DOCS.filesEnrich.description).argument("<bucket>", "Bucket name").argument("<key>", "File key").addHelpText("after", renderCommandHelp("filesEnrich")).action(async (bucket, key) => {
430
135
  try {
431
- const { enrichFile } = await import("./files-E662TXUP.js");
136
+ const { enrichFile } = await import("./files-UQODXWNT.js");
432
137
  await enrichFile(bucket, key);
433
138
  } catch (err) {
434
139
  handleError(err);
@@ -437,7 +142,7 @@ filesCmd.command("enrich").description(CLI_DOCS.filesEnrich.description).argumen
437
142
  filesCmd.command("missing").description(CLI_DOCS.filesMissing.description).argument("<bucket>", "Bucket name").argument("<type>", "dimensions | embeddings | colors").option("-l, --limit <count>", "Max files to return").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("filesMissing")).action(
438
143
  async (bucket, type, options) => {
439
144
  try {
440
- const { listMissing } = await import("./files-E662TXUP.js");
145
+ const { listMissing } = await import("./files-UQODXWNT.js");
441
146
  await listMissing(bucket, type, options);
442
147
  } catch (err) {
443
148
  handleError(err);
@@ -446,7 +151,7 @@ filesCmd.command("missing").description(CLI_DOCS.filesMissing.description).argum
446
151
  );
447
152
  var dropsCmd = program.command("drops").description(CLI_DOCS.drops.description).addHelpText("after", renderCommandHelp("drops")).action(async () => {
448
153
  try {
449
- const { listDrops } = await import("./drops-RD55IDZ4.js");
154
+ const { listDrops } = await import("./drops-5VIEW3XZ.js");
450
155
  await listDrops();
451
156
  } catch (err) {
452
157
  handleError(err);
@@ -454,7 +159,7 @@ var dropsCmd = program.command("drops").description(CLI_DOCS.drops.description).
454
159
  });
455
160
  dropsCmd.command("delete").description(CLI_DOCS.dropsDelete.description).argument("<id>", "Drop ID").addHelpText("after", renderCommandHelp("dropsDelete")).action(async (id) => {
456
161
  try {
457
- const { deleteDrop } = await import("./drops-RD55IDZ4.js");
162
+ const { deleteDrop } = await import("./drops-5VIEW3XZ.js");
458
163
  await deleteDrop(id);
459
164
  } catch (err) {
460
165
  handleError(err);
@@ -464,7 +169,7 @@ var searchCmd = program.command("search").description(CLI_DOCS.search.descriptio
464
169
  searchCmd.command("text").description(CLI_DOCS.searchText.description).argument("<query>", "Search query").option("-b, --bucket <name>", "Bucket name").option("-l, --limit <count>", "Max results").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("searchText")).action(
465
170
  async (query, options) => {
466
171
  try {
467
- const { textSearch } = await import("./search-DV4VV62L.js");
172
+ const { textSearch } = await import("./search-TRTPX2SQ.js");
468
173
  await textSearch(query, options);
469
174
  } catch (err) {
470
175
  handleError(err);
@@ -474,7 +179,7 @@ searchCmd.command("text").description(CLI_DOCS.searchText.description).argument(
474
179
  searchCmd.command("similar").description(CLI_DOCS.searchSimilar.description).requiredOption("--file <key>", "File key to search from").option("-b, --bucket <name>", "Bucket name").option("-l, --limit <count>", "Max results").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("searchSimilar")).action(
475
180
  async (options) => {
476
181
  try {
477
- const { similarSearch } = await import("./search-DV4VV62L.js");
182
+ const { similarSearch } = await import("./search-TRTPX2SQ.js");
478
183
  await similarSearch(options);
479
184
  } catch (err) {
480
185
  handleError(err);
@@ -484,7 +189,7 @@ searchCmd.command("similar").description(CLI_DOCS.searchSimilar.description).req
484
189
  searchCmd.command("color").description(CLI_DOCS.searchColor.description).requiredOption("--hex <color>", "Hex color code").option("-b, --bucket <name>", "Bucket name").option("-l, --limit <count>", "Max results").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("searchColor")).action(
485
190
  async (options) => {
486
191
  try {
487
- const { colorSearch } = await import("./search-DV4VV62L.js");
192
+ const { colorSearch } = await import("./search-TRTPX2SQ.js");
488
193
  await colorSearch(options);
489
194
  } catch (err) {
490
195
  handleError(err);
@@ -494,7 +199,7 @@ searchCmd.command("color").description(CLI_DOCS.searchColor.description).require
494
199
  searchCmd.command("diverse").description(CLI_DOCS.searchDiverse.description).option("-b, --bucket <name>", "Bucket name").option("-l, --limit <count>", "Max results").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("searchDiverse")).action(
495
200
  async (options) => {
496
201
  try {
497
- const { diverseSearch } = await import("./search-DV4VV62L.js");
202
+ const { diverseSearch } = await import("./search-TRTPX2SQ.js");
498
203
  await diverseSearch(options);
499
204
  } catch (err) {
500
205
  handleError(err);
@@ -503,24 +208,26 @@ searchCmd.command("diverse").description(CLI_DOCS.searchDiverse.description).opt
503
208
  );
504
209
  var tagsCmd = program.command("tags").description(CLI_DOCS.tags.description).option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("tags")).action(async (options) => {
505
210
  try {
506
- const { listTags } = await import("./tags-URHK2YH5.js");
211
+ const { listTags } = await import("./tags-VH44BGQL.js");
507
212
  await listTags(options);
508
213
  } catch (err) {
509
214
  handleError(err);
510
215
  }
511
216
  });
512
- tagsCmd.command("create").description(CLI_DOCS.tagsCreate.description).argument("<name>", "Tag name").option("--color <hex>", "Tag color (hex)").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("tagsCreate")).action(async (name, options) => {
513
- try {
514
- const { createTag } = await import("./tags-URHK2YH5.js");
515
- await createTag(name, options);
516
- } catch (err) {
517
- handleError(err);
217
+ tagsCmd.command("create").description(CLI_DOCS.tagsCreate.description).argument("<name>", "Tag name").option("--color <hex>", "Tag color (hex)").option("--json", "Output as JSON").option("--dry-run", "Preview without creating").option("--input-json <json>", "Raw JSON payload").addHelpText("after", renderCommandHelp("tagsCreate")).action(
218
+ async (name, options) => {
219
+ try {
220
+ const { createTag } = await import("./tags-VH44BGQL.js");
221
+ await createTag(name, options);
222
+ } catch (err) {
223
+ handleError(err);
224
+ }
518
225
  }
519
- });
520
- tagsCmd.command("delete").description(CLI_DOCS.tagsDelete.description).argument("<id>", "Tag ID").addHelpText("after", renderCommandHelp("tagsDelete")).action(async (id) => {
226
+ );
227
+ tagsCmd.command("delete").description(CLI_DOCS.tagsDelete.description).argument("<id>", "Tag ID").option("--dry-run", "Preview without deleting").addHelpText("after", renderCommandHelp("tagsDelete")).action(async (id, options) => {
521
228
  try {
522
- const { deleteTag } = await import("./tags-URHK2YH5.js");
523
- await deleteTag(id);
229
+ const { deleteTag } = await import("./tags-VH44BGQL.js");
230
+ await deleteTag(id, options);
524
231
  } catch (err) {
525
232
  handleError(err);
526
233
  }
@@ -529,7 +236,7 @@ var profilesCmd = program.command("profiles").description(CLI_DOCS.profiles.desc
529
236
  profilesCmd.command("create").description(CLI_DOCS.profilesCreate.description).requiredOption("--name <name>", "Profile name").option("-b, --bucket <id>", "Bucket ID").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("profilesCreate")).action(
530
237
  async (options) => {
531
238
  try {
532
- const { createProfile } = await import("./profiles-C6SCFXS6.js");
239
+ const { createProfile } = await import("./profiles-CHBGKQOE.js");
533
240
  await createProfile(options);
534
241
  } catch (err) {
535
242
  handleError(err);
@@ -538,7 +245,7 @@ profilesCmd.command("create").description(CLI_DOCS.profilesCreate.description).r
538
245
  );
539
246
  profilesCmd.command("get").description(CLI_DOCS.profilesGet.description).argument("<id>", "Profile ID").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("profilesGet")).action(async (id, options) => {
540
247
  try {
541
- const { getProfile } = await import("./profiles-C6SCFXS6.js");
248
+ const { getProfile } = await import("./profiles-CHBGKQOE.js");
542
249
  await getProfile(id, options);
543
250
  } catch (err) {
544
251
  handleError(err);
@@ -546,7 +253,7 @@ profilesCmd.command("get").description(CLI_DOCS.profilesGet.description).argumen
546
253
  });
547
254
  profilesCmd.command("delete").description(CLI_DOCS.profilesDelete.description).argument("<id>", "Profile ID").addHelpText("after", renderCommandHelp("profilesDelete")).action(async (id) => {
548
255
  try {
549
- const { deleteProfile } = await import("./profiles-C6SCFXS6.js");
256
+ const { deleteProfile } = await import("./profiles-CHBGKQOE.js");
550
257
  await deleteProfile(id);
551
258
  } catch (err) {
552
259
  handleError(err);
@@ -555,7 +262,7 @@ profilesCmd.command("delete").description(CLI_DOCS.profilesDelete.description).a
555
262
  var jobsCmd = program.command("jobs").description(CLI_DOCS.jobs.description).requiredOption("-b, --bucket <id>", "Bucket ID (required)").option("-s, --status <status>", "Filter by status").option("-q, --queue <name>", "Filter by queue name").option("-l, --limit <count>", "Max jobs to return").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("jobs")).action(
556
263
  async (options) => {
557
264
  try {
558
- const { listJobs } = await import("./jobs-CLXYKZ3B.js");
265
+ const { listJobs } = await import("./jobs-PTV2W5PJ.js");
559
266
  await listJobs(options.bucket, options);
560
267
  } catch (err) {
561
268
  handleError(err);
@@ -564,7 +271,7 @@ var jobsCmd = program.command("jobs").description(CLI_DOCS.jobs.description).req
564
271
  );
565
272
  jobsCmd.command("retry").description(CLI_DOCS.jobsRetry.description).argument("<id>", "Job ID").requiredOption("-q, --queue <name>", "Queue name").requiredOption("-b, --bucket <id>", "Bucket ID").addHelpText("after", renderCommandHelp("jobsRetry")).action(async (id, options) => {
566
273
  try {
567
- const { retryJob } = await import("./jobs-CLXYKZ3B.js");
274
+ const { retryJob } = await import("./jobs-PTV2W5PJ.js");
568
275
  await retryJob(id, options);
569
276
  } catch (err) {
570
277
  handleError(err);
@@ -572,7 +279,7 @@ jobsCmd.command("retry").description(CLI_DOCS.jobsRetry.description).argument("<
572
279
  });
573
280
  jobsCmd.command("delete").description(CLI_DOCS.jobsDelete.description).argument("<id>", "Job ID").requiredOption("-q, --queue <name>", "Queue name").requiredOption("-b, --bucket <id>", "Bucket ID").addHelpText("after", renderCommandHelp("jobsDelete")).action(async (id, options) => {
574
281
  try {
575
- const { deleteJob } = await import("./jobs-CLXYKZ3B.js");
282
+ const { deleteJob } = await import("./jobs-PTV2W5PJ.js");
576
283
  await deleteJob(id, options);
577
284
  } catch (err) {
578
285
  handleError(err);
@@ -581,7 +288,7 @@ jobsCmd.command("delete").description(CLI_DOCS.jobsDelete.description).argument(
581
288
  var adminCmd = program.command("admin").description(CLI_DOCS.admin.description).addHelpText("after", renderCommandHelp("admin"));
582
289
  adminCmd.command("health").description(CLI_DOCS.adminHealth.description).option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("adminHealth")).action(async (options) => {
583
290
  try {
584
- const { health } = await import("./health-6LWM7KAT.js");
291
+ const { health } = await import("./health-RICGWQGN.js");
585
292
  await health(options);
586
293
  } catch (err) {
587
294
  handleError(err);
@@ -591,7 +298,7 @@ var backfillCmd = adminCmd.command("backfill").description(CLI_DOCS.adminBackfil
591
298
  backfillCmd.command("dimensions").description("Backfill image dimensions").option("--bucket <id>", "Bucket ID").option("-l, --limit <count>", "Max files to enqueue").option("--dry-run", "Preview without enqueuing").option("--json", "Output as JSON").action(
592
299
  async (options) => {
593
300
  try {
594
- const { backfillDimensions } = await import("./backfill-G57M2BRA.js");
301
+ const { backfillDimensions } = await import("./backfill-EVZT7RH6.js");
595
302
  await backfillDimensions(options);
596
303
  } catch (err) {
597
304
  handleError(err);
@@ -601,7 +308,7 @@ backfillCmd.command("dimensions").description("Backfill image dimensions").optio
601
308
  backfillCmd.command("colors").description("Backfill color extraction").option("--bucket <id>", "Bucket ID").option("-l, --limit <count>", "Max files to enqueue").option("--dry-run", "Preview without enqueuing").option("--json", "Output as JSON").action(
602
309
  async (options) => {
603
310
  try {
604
- const { backfillColors } = await import("./backfill-G57M2BRA.js");
311
+ const { backfillColors } = await import("./backfill-EVZT7RH6.js");
605
312
  await backfillColors(options);
606
313
  } catch (err) {
607
314
  handleError(err);
@@ -611,7 +318,7 @@ backfillCmd.command("colors").description("Backfill color extraction").option("-
611
318
  backfillCmd.command("embeddings").description("Backfill embedding generation").option("--bucket <id>", "Bucket ID").option("-l, --limit <count>", "Max files to enqueue").option("--dry-run", "Preview without enqueuing").option("--json", "Output as JSON").action(
612
319
  async (options) => {
613
320
  try {
614
- const { backfillEmbeddings } = await import("./backfill-G57M2BRA.js");
321
+ const { backfillEmbeddings } = await import("./backfill-EVZT7RH6.js");
615
322
  await backfillEmbeddings(options);
616
323
  } catch (err) {
617
324
  handleError(err);
@@ -621,7 +328,7 @@ backfillCmd.command("embeddings").description("Backfill embedding generation").o
621
328
  adminCmd.command("cleanup-drops").description(CLI_DOCS.adminCleanupDrops.description).option("--max-age-hours <hours>", "Max age in hours").option("--dry-run", "Preview without deleting").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("adminCleanupDrops")).action(
622
329
  async (options) => {
623
330
  try {
624
- const { cleanupDrops } = await import("./maintenance-7LOAGG6J.js");
331
+ const { cleanupDrops } = await import("./maintenance-ZJW2ES4L.js");
625
332
  await cleanupDrops(options);
626
333
  } catch (err) {
627
334
  handleError(err);
@@ -630,7 +337,7 @@ adminCmd.command("cleanup-drops").description(CLI_DOCS.adminCleanupDrops.descrip
630
337
  );
631
338
  adminCmd.command("purge-events").description(CLI_DOCS.adminPurgeEvents.description).option("--dry-run", "Preview without deleting").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("adminPurgeEvents")).action(async (options) => {
632
339
  try {
633
- const { purgeEvents } = await import("./maintenance-7LOAGG6J.js");
340
+ const { purgeEvents } = await import("./maintenance-ZJW2ES4L.js");
634
341
  await purgeEvents(options);
635
342
  } catch (err) {
636
343
  handleError(err);
@@ -639,7 +346,7 @@ adminCmd.command("purge-events").description(CLI_DOCS.adminPurgeEvents.descripti
639
346
  adminCmd.command("reconcile-files").description(CLI_DOCS.adminReconcileFiles.description).requiredOption("--bucket <id>", "Bucket ID").option("--dry-run", "Preview without reconciling").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("adminReconcileFiles")).action(
640
347
  async (options) => {
641
348
  try {
642
- const { reconcileFiles } = await import("./maintenance-7LOAGG6J.js");
349
+ const { reconcileFiles } = await import("./maintenance-ZJW2ES4L.js");
643
350
  await reconcileFiles(options);
644
351
  } catch (err) {
645
352
  handleError(err);
@@ -648,7 +355,7 @@ adminCmd.command("reconcile-files").description(CLI_DOCS.adminReconcileFiles.des
648
355
  );
649
356
  adminCmd.command("retry-sync-failures").description(CLI_DOCS.adminRetrySyncFailures.description).option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("adminRetrySyncFailures")).action(async (options) => {
650
357
  try {
651
- const { retrySyncFailures } = await import("./maintenance-7LOAGG6J.js");
358
+ const { retrySyncFailures } = await import("./maintenance-ZJW2ES4L.js");
652
359
  await retrySyncFailures(options);
653
360
  } catch (err) {
654
361
  handleError(err);
@@ -657,7 +364,7 @@ adminCmd.command("retry-sync-failures").description(CLI_DOCS.adminRetrySyncFailu
657
364
  var adminJobsCmd = adminCmd.command("jobs").description(CLI_DOCS.adminJobs.description).option("--org <id>", "Filter by org ID").option("--bucket <id>", "Filter by bucket ID").option("-s, --status <status>", "Filter by status").option("-q, --queue <name>", "Filter by queue name").option("-l, --limit <count>", "Max jobs to return").option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("adminJobs")).action(
658
365
  async (options) => {
659
366
  try {
660
- const { listAdminJobs } = await import("./jobs-K3LTNETU.js");
367
+ const { listAdminJobs } = await import("./jobs-ZWSEXNFA.js");
661
368
  await listAdminJobs(options);
662
369
  } catch (err) {
663
370
  handleError(err);
@@ -666,7 +373,7 @@ var adminJobsCmd = adminCmd.command("jobs").description(CLI_DOCS.adminJobs.descr
666
373
  );
667
374
  adminJobsCmd.command("retry").description(CLI_DOCS.adminJobsRetry.description).argument("<id>", "Job ID").requiredOption("-q, --queue <name>", "Queue name").addHelpText("after", renderCommandHelp("adminJobsRetry")).action(async (id, options) => {
668
375
  try {
669
- const { retryAdminJob } = await import("./jobs-K3LTNETU.js");
376
+ const { retryAdminJob } = await import("./jobs-ZWSEXNFA.js");
670
377
  await retryAdminJob(id, options);
671
378
  } catch (err) {
672
379
  handleError(err);
@@ -674,7 +381,7 @@ adminJobsCmd.command("retry").description(CLI_DOCS.adminJobsRetry.description).a
674
381
  });
675
382
  adminJobsCmd.command("delete").description(CLI_DOCS.adminJobsDelete.description).argument("<id>", "Job ID").requiredOption("-q, --queue <name>", "Queue name").addHelpText("after", renderCommandHelp("adminJobsDelete")).action(async (id, options) => {
676
383
  try {
677
- const { deleteAdminJob } = await import("./jobs-K3LTNETU.js");
384
+ const { deleteAdminJob } = await import("./jobs-ZWSEXNFA.js");
678
385
  await deleteAdminJob(id, options);
679
386
  } catch (err) {
680
387
  handleError(err);
@@ -682,7 +389,7 @@ adminJobsCmd.command("delete").description(CLI_DOCS.adminJobsDelete.description)
682
389
  });
683
390
  var adminQueuesCmd = adminCmd.command("queues").description(CLI_DOCS.adminQueues.description).option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("adminQueues")).action(async (options) => {
684
391
  try {
685
- const { listQueues } = await import("./queues-2GJAMMNF.js");
392
+ const { listQueues } = await import("./queues-EZ2VZGXQ.js");
686
393
  await listQueues(options);
687
394
  } catch (err) {
688
395
  handleError(err);
@@ -691,24 +398,26 @@ var adminQueuesCmd = adminCmd.command("queues").description(CLI_DOCS.adminQueues
691
398
  adminQueuesCmd.command("clean").description(CLI_DOCS.adminQueuesClean.description).argument("<name>", "Queue name").option("--failed", "Clean failed jobs").option("--completed", "Clean completed jobs").option("--grace <seconds>", "Grace period in seconds").addHelpText("after", renderCommandHelp("adminQueuesClean")).action(
692
399
  async (name, options) => {
693
400
  try {
694
- const { cleanQueue } = await import("./queues-2GJAMMNF.js");
401
+ const { cleanQueue } = await import("./queues-EZ2VZGXQ.js");
695
402
  await cleanQueue(name, options);
696
403
  } catch (err) {
697
404
  handleError(err);
698
405
  }
699
406
  }
700
407
  );
701
- program.command("delete").description(CLI_DOCS.delete.description).argument("<bucket>", "Bucket name").argument("<key>", "File key").addHelpText("after", renderCommandHelp("delete")).action(async (bucket, key) => {
702
- try {
703
- const { deleteFile } = await import("./delete-GEX3O7YS.js");
704
- await deleteFile(bucket, key);
705
- } catch (err) {
706
- handleError(err);
408
+ program.command("delete").description(CLI_DOCS.delete.description).argument("<bucket>", "Bucket name").argument("<key>", "File key").option("--dry-run", "Preview without deleting").addHelpText("after", renderCommandHelp("delete")).action(
409
+ async (bucket, key, options) => {
410
+ try {
411
+ const { deleteFile } = await import("./delete-YEXSMG4I.js");
412
+ await deleteFile(bucket, key, options);
413
+ } catch (err) {
414
+ handleError(err);
415
+ }
707
416
  }
708
- });
417
+ );
709
418
  program.command("whoami").description(CLI_DOCS.whoami.description).addHelpText("after", renderCommandHelp("whoami")).action(async () => {
710
419
  try {
711
- const { whoami } = await import("./whoami-5IYRZKWS.js");
420
+ const { whoami } = await import("./whoami-TVRKBM74.js");
712
421
  await whoami();
713
422
  } catch (err) {
714
423
  handleError(err);
@@ -722,10 +431,26 @@ program.command("open").description(CLI_DOCS.open.description).argument("<bucket
722
431
  handleError(err);
723
432
  }
724
433
  });
434
+ program.command("describe").argument("[command]", "Command path (dot notation, e.g. search.text)").description("Show command schema for AI agents").action(async (command) => {
435
+ try {
436
+ const { describeCommand } = await import("./describe-J4ZMUXK7.js");
437
+ describeCommand(command ?? "");
438
+ } catch (err) {
439
+ handleError(err);
440
+ }
441
+ });
442
+ program.command("mcp").description("Start MCP stdio server for AI agent integration").action(async () => {
443
+ try {
444
+ const { startMcpServer } = await import("./mcp-RZT4TJEX.js");
445
+ await startMcpServer();
446
+ } catch (err) {
447
+ handleError(err);
448
+ }
449
+ });
725
450
  program.option("-i, --interactive", CLI_DOCS.interactive.description).addHelpText("after", renderCommandHelp("interactive"));
726
451
  program.parse();
727
452
  var opts = program.opts();
728
453
  var args = process.argv.slice(2);
729
454
  if (args.length === 0 || args.length === 1 && opts.interactive) {
730
- import("./app-DTW2LNXW.js").then(({ startInteractive }) => startInteractive()).catch(handleError);
455
+ import("./app-Q6EW7VSM.js").then(({ startInteractive }) => startInteractive()).catch(handleError);
731
456
  }