stow-cli 2.1.0 → 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.
@@ -0,0 +1,52 @@
1
+ // src/lib/validate-input.ts
2
+ import path from "path";
3
+ var InputValidationError = class extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "InputValidationError";
7
+ }
8
+ };
9
+ function validateInput(value, context) {
10
+ if (value.includes("../") || value.includes("..\\")) {
11
+ throw new InputValidationError(`${context}: path traversal not allowed`);
12
+ }
13
+ if (context !== "url" && value.includes("?")) {
14
+ throw new InputValidationError(
15
+ `${context}: embedded query parameters not allowed`
16
+ );
17
+ }
18
+ if (/[\x00-\x08\x0b\x0c\x0e-\x1f]/.test(value)) {
19
+ throw new InputValidationError(
20
+ `${context}: control characters not allowed`
21
+ );
22
+ }
23
+ if (/%25/.test(value)) {
24
+ throw new InputValidationError(
25
+ `${context}: double-encoded values not allowed`
26
+ );
27
+ }
28
+ if (/%2[fF]/.test(value) || /%2[eE]/.test(value)) {
29
+ throw new InputValidationError(
30
+ `${context}: percent-encoded path characters not allowed`
31
+ );
32
+ }
33
+ if (context !== "url" && value.includes("#")) {
34
+ throw new InputValidationError(
35
+ `${context}: embedded hash fragments not allowed`
36
+ );
37
+ }
38
+ return value;
39
+ }
40
+ function validateBucketName(name) {
41
+ return validateInput(name, "bucket name");
42
+ }
43
+ function validateFileKey(key) {
44
+ return validateInput(key, "file key");
45
+ }
46
+
47
+ export {
48
+ InputValidationError,
49
+ validateInput,
50
+ validateBucketName,
51
+ validateFileKey
52
+ };
package/dist/cli.js CHANGED
@@ -3,6 +3,15 @@ import {
3
3
  CLI_DOCS,
4
4
  renderCommandHelp
5
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";
6
15
 
7
16
  // src/cli.ts
8
17
  import { StowError } from "@howells/stow-server";
@@ -10,19 +19,35 @@ import { Command } from "commander";
10
19
  var VERSION = "1.0.0";
11
20
  function handleError(err) {
12
21
  if (err instanceof StowError) {
13
- 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");
14
25
  } else if (err instanceof Error) {
15
- console.error(`Error: ${err.message}`);
26
+ outputError(err.message);
16
27
  } else {
17
- console.error(`Error: ${err}`);
28
+ outputError(String(err));
18
29
  }
19
- process.exit(1);
20
30
  }
21
31
  var program = new Command();
22
- 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
+ });
23
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) => {
24
49
  try {
25
- const { uploadDrop } = await import("./upload-5TAWJU5N.js");
50
+ const { uploadDrop } = await import("./upload-OS6Q6LW5.js");
26
51
  await uploadDrop(file, options);
27
52
  } catch (err) {
28
53
  handleError(err);
@@ -31,7 +56,7 @@ program.command("drop").description(CLI_DOCS.drop.description).argument("<file>"
31
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(
32
57
  async (file, options) => {
33
58
  try {
34
- const { uploadFile } = await import("./upload-5TAWJU5N.js");
59
+ const { uploadFile } = await import("./upload-OS6Q6LW5.js");
35
60
  await uploadFile(file, options);
36
61
  } catch (err) {
37
62
  handleError(err);
@@ -40,26 +65,26 @@ program.command("upload").description(CLI_DOCS.upload.description).argument("<fi
40
65
  );
41
66
  var bucketsCmd = program.command("buckets").description(CLI_DOCS.buckets.description).addHelpText("after", renderCommandHelp("buckets")).action(async () => {
42
67
  try {
43
- const { listBuckets } = await import("./buckets-ESAOL6CH.js");
68
+ const { listBuckets } = await import("./buckets-ZHP3LBLC.js");
44
69
  await listBuckets();
45
70
  } catch (err) {
46
71
  handleError(err);
47
72
  }
48
73
  });
49
- 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").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(
50
75
  async (name, options) => {
51
76
  try {
52
- const { createBucket } = await import("./buckets-ESAOL6CH.js");
77
+ const { createBucket } = await import("./buckets-ZHP3LBLC.js");
53
78
  await createBucket(name, options);
54
79
  } catch (err) {
55
80
  handleError(err);
56
81
  }
57
82
  }
58
83
  );
59
- 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").addHelpText("after", renderCommandHelp("bucketsRename")).action(
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(
60
85
  async (name, newName, options) => {
61
86
  try {
62
- const { renameBucket } = await import("./buckets-ESAOL6CH.js");
87
+ const { renameBucket } = await import("./buckets-ZHP3LBLC.js");
63
88
  await renameBucket(name, newName, options);
64
89
  } catch (err) {
65
90
  handleError(err);
@@ -68,7 +93,7 @@ bucketsCmd.command("rename").description(CLI_DOCS.bucketsRename.description).arg
68
93
  );
69
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) => {
70
95
  try {
71
- const { deleteBucket } = await import("./buckets-ESAOL6CH.js");
96
+ const { deleteBucket } = await import("./buckets-ZHP3LBLC.js");
72
97
  await deleteBucket(id, options);
73
98
  } catch (err) {
74
99
  handleError(err);
@@ -81,7 +106,7 @@ var filesCmd = program.command("files").description(CLI_DOCS.files.description).
81
106
  return;
82
107
  }
83
108
  try {
84
- const { listFiles } = await import("./files-TDIGJDN3.js");
109
+ const { listFiles } = await import("./files-UQODXWNT.js");
85
110
  await listFiles(bucket, options);
86
111
  } catch (err) {
87
112
  handleError(err);
@@ -90,16 +115,16 @@ var filesCmd = program.command("files").description(CLI_DOCS.files.description).
90
115
  );
91
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) => {
92
117
  try {
93
- const { getFile } = await import("./files-TDIGJDN3.js");
118
+ const { getFile } = await import("./files-UQODXWNT.js");
94
119
  await getFile(bucket, key, options);
95
120
  } catch (err) {
96
121
  handleError(err);
97
122
  }
98
123
  });
99
- 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").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(
100
125
  async (bucket, key, options) => {
101
126
  try {
102
- const { updateFile } = await import("./files-TDIGJDN3.js");
127
+ const { updateFile } = await import("./files-UQODXWNT.js");
103
128
  await updateFile(bucket, key, options);
104
129
  } catch (err) {
105
130
  handleError(err);
@@ -108,7 +133,7 @@ filesCmd.command("update").description(CLI_DOCS.filesUpdate.description).argumen
108
133
  );
109
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) => {
110
135
  try {
111
- const { enrichFile } = await import("./files-TDIGJDN3.js");
136
+ const { enrichFile } = await import("./files-UQODXWNT.js");
112
137
  await enrichFile(bucket, key);
113
138
  } catch (err) {
114
139
  handleError(err);
@@ -117,7 +142,7 @@ filesCmd.command("enrich").description(CLI_DOCS.filesEnrich.description).argumen
117
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(
118
143
  async (bucket, type, options) => {
119
144
  try {
120
- const { listMissing } = await import("./files-TDIGJDN3.js");
145
+ const { listMissing } = await import("./files-UQODXWNT.js");
121
146
  await listMissing(bucket, type, options);
122
147
  } catch (err) {
123
148
  handleError(err);
@@ -126,7 +151,7 @@ filesCmd.command("missing").description(CLI_DOCS.filesMissing.description).argum
126
151
  );
127
152
  var dropsCmd = program.command("drops").description(CLI_DOCS.drops.description).addHelpText("after", renderCommandHelp("drops")).action(async () => {
128
153
  try {
129
- const { listDrops } = await import("./drops-RD55IDZ4.js");
154
+ const { listDrops } = await import("./drops-5VIEW3XZ.js");
130
155
  await listDrops();
131
156
  } catch (err) {
132
157
  handleError(err);
@@ -134,7 +159,7 @@ var dropsCmd = program.command("drops").description(CLI_DOCS.drops.description).
134
159
  });
135
160
  dropsCmd.command("delete").description(CLI_DOCS.dropsDelete.description).argument("<id>", "Drop ID").addHelpText("after", renderCommandHelp("dropsDelete")).action(async (id) => {
136
161
  try {
137
- const { deleteDrop } = await import("./drops-RD55IDZ4.js");
162
+ const { deleteDrop } = await import("./drops-5VIEW3XZ.js");
138
163
  await deleteDrop(id);
139
164
  } catch (err) {
140
165
  handleError(err);
@@ -144,7 +169,7 @@ var searchCmd = program.command("search").description(CLI_DOCS.search.descriptio
144
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(
145
170
  async (query, options) => {
146
171
  try {
147
- const { textSearch } = await import("./search-DV4VV62L.js");
172
+ const { textSearch } = await import("./search-TRTPX2SQ.js");
148
173
  await textSearch(query, options);
149
174
  } catch (err) {
150
175
  handleError(err);
@@ -154,7 +179,7 @@ searchCmd.command("text").description(CLI_DOCS.searchText.description).argument(
154
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(
155
180
  async (options) => {
156
181
  try {
157
- const { similarSearch } = await import("./search-DV4VV62L.js");
182
+ const { similarSearch } = await import("./search-TRTPX2SQ.js");
158
183
  await similarSearch(options);
159
184
  } catch (err) {
160
185
  handleError(err);
@@ -164,7 +189,7 @@ searchCmd.command("similar").description(CLI_DOCS.searchSimilar.description).req
164
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(
165
190
  async (options) => {
166
191
  try {
167
- const { colorSearch } = await import("./search-DV4VV62L.js");
192
+ const { colorSearch } = await import("./search-TRTPX2SQ.js");
168
193
  await colorSearch(options);
169
194
  } catch (err) {
170
195
  handleError(err);
@@ -174,7 +199,7 @@ searchCmd.command("color").description(CLI_DOCS.searchColor.description).require
174
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(
175
200
  async (options) => {
176
201
  try {
177
- const { diverseSearch } = await import("./search-DV4VV62L.js");
202
+ const { diverseSearch } = await import("./search-TRTPX2SQ.js");
178
203
  await diverseSearch(options);
179
204
  } catch (err) {
180
205
  handleError(err);
@@ -183,16 +208,16 @@ searchCmd.command("diverse").description(CLI_DOCS.searchDiverse.description).opt
183
208
  );
184
209
  var tagsCmd = program.command("tags").description(CLI_DOCS.tags.description).option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("tags")).action(async (options) => {
185
210
  try {
186
- const { listTags } = await import("./tags-MCFL5M2J.js");
211
+ const { listTags } = await import("./tags-VH44BGQL.js");
187
212
  await listTags(options);
188
213
  } catch (err) {
189
214
  handleError(err);
190
215
  }
191
216
  });
192
- 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").addHelpText("after", renderCommandHelp("tagsCreate")).action(
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(
193
218
  async (name, options) => {
194
219
  try {
195
- const { createTag } = await import("./tags-MCFL5M2J.js");
220
+ const { createTag } = await import("./tags-VH44BGQL.js");
196
221
  await createTag(name, options);
197
222
  } catch (err) {
198
223
  handleError(err);
@@ -201,7 +226,7 @@ tagsCmd.command("create").description(CLI_DOCS.tagsCreate.description).argument(
201
226
  );
202
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) => {
203
228
  try {
204
- const { deleteTag } = await import("./tags-MCFL5M2J.js");
229
+ const { deleteTag } = await import("./tags-VH44BGQL.js");
205
230
  await deleteTag(id, options);
206
231
  } catch (err) {
207
232
  handleError(err);
@@ -211,7 +236,7 @@ var profilesCmd = program.command("profiles").description(CLI_DOCS.profiles.desc
211
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(
212
237
  async (options) => {
213
238
  try {
214
- const { createProfile } = await import("./profiles-C6SCFXS6.js");
239
+ const { createProfile } = await import("./profiles-CHBGKQOE.js");
215
240
  await createProfile(options);
216
241
  } catch (err) {
217
242
  handleError(err);
@@ -220,7 +245,7 @@ profilesCmd.command("create").description(CLI_DOCS.profilesCreate.description).r
220
245
  );
221
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) => {
222
247
  try {
223
- const { getProfile } = await import("./profiles-C6SCFXS6.js");
248
+ const { getProfile } = await import("./profiles-CHBGKQOE.js");
224
249
  await getProfile(id, options);
225
250
  } catch (err) {
226
251
  handleError(err);
@@ -228,7 +253,7 @@ profilesCmd.command("get").description(CLI_DOCS.profilesGet.description).argumen
228
253
  });
229
254
  profilesCmd.command("delete").description(CLI_DOCS.profilesDelete.description).argument("<id>", "Profile ID").addHelpText("after", renderCommandHelp("profilesDelete")).action(async (id) => {
230
255
  try {
231
- const { deleteProfile } = await import("./profiles-C6SCFXS6.js");
256
+ const { deleteProfile } = await import("./profiles-CHBGKQOE.js");
232
257
  await deleteProfile(id);
233
258
  } catch (err) {
234
259
  handleError(err);
@@ -237,7 +262,7 @@ profilesCmd.command("delete").description(CLI_DOCS.profilesDelete.description).a
237
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(
238
263
  async (options) => {
239
264
  try {
240
- const { listJobs } = await import("./jobs-CLXYKZ3B.js");
265
+ const { listJobs } = await import("./jobs-PTV2W5PJ.js");
241
266
  await listJobs(options.bucket, options);
242
267
  } catch (err) {
243
268
  handleError(err);
@@ -246,7 +271,7 @@ var jobsCmd = program.command("jobs").description(CLI_DOCS.jobs.description).req
246
271
  );
247
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) => {
248
273
  try {
249
- const { retryJob } = await import("./jobs-CLXYKZ3B.js");
274
+ const { retryJob } = await import("./jobs-PTV2W5PJ.js");
250
275
  await retryJob(id, options);
251
276
  } catch (err) {
252
277
  handleError(err);
@@ -254,7 +279,7 @@ jobsCmd.command("retry").description(CLI_DOCS.jobsRetry.description).argument("<
254
279
  });
255
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) => {
256
281
  try {
257
- const { deleteJob } = await import("./jobs-CLXYKZ3B.js");
282
+ const { deleteJob } = await import("./jobs-PTV2W5PJ.js");
258
283
  await deleteJob(id, options);
259
284
  } catch (err) {
260
285
  handleError(err);
@@ -263,7 +288,7 @@ jobsCmd.command("delete").description(CLI_DOCS.jobsDelete.description).argument(
263
288
  var adminCmd = program.command("admin").description(CLI_DOCS.admin.description).addHelpText("after", renderCommandHelp("admin"));
264
289
  adminCmd.command("health").description(CLI_DOCS.adminHealth.description).option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("adminHealth")).action(async (options) => {
265
290
  try {
266
- const { health } = await import("./health-6LWM7KAT.js");
291
+ const { health } = await import("./health-RICGWQGN.js");
267
292
  await health(options);
268
293
  } catch (err) {
269
294
  handleError(err);
@@ -273,7 +298,7 @@ var backfillCmd = adminCmd.command("backfill").description(CLI_DOCS.adminBackfil
273
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(
274
299
  async (options) => {
275
300
  try {
276
- const { backfillDimensions } = await import("./backfill-G57M2BRA.js");
301
+ const { backfillDimensions } = await import("./backfill-EVZT7RH6.js");
277
302
  await backfillDimensions(options);
278
303
  } catch (err) {
279
304
  handleError(err);
@@ -283,7 +308,7 @@ backfillCmd.command("dimensions").description("Backfill image dimensions").optio
283
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(
284
309
  async (options) => {
285
310
  try {
286
- const { backfillColors } = await import("./backfill-G57M2BRA.js");
311
+ const { backfillColors } = await import("./backfill-EVZT7RH6.js");
287
312
  await backfillColors(options);
288
313
  } catch (err) {
289
314
  handleError(err);
@@ -293,7 +318,7 @@ backfillCmd.command("colors").description("Backfill color extraction").option("-
293
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(
294
319
  async (options) => {
295
320
  try {
296
- const { backfillEmbeddings } = await import("./backfill-G57M2BRA.js");
321
+ const { backfillEmbeddings } = await import("./backfill-EVZT7RH6.js");
297
322
  await backfillEmbeddings(options);
298
323
  } catch (err) {
299
324
  handleError(err);
@@ -303,7 +328,7 @@ backfillCmd.command("embeddings").description("Backfill embedding generation").o
303
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(
304
329
  async (options) => {
305
330
  try {
306
- const { cleanupDrops } = await import("./maintenance-7LOAGG6J.js");
331
+ const { cleanupDrops } = await import("./maintenance-ZJW2ES4L.js");
307
332
  await cleanupDrops(options);
308
333
  } catch (err) {
309
334
  handleError(err);
@@ -312,7 +337,7 @@ adminCmd.command("cleanup-drops").description(CLI_DOCS.adminCleanupDrops.descrip
312
337
  );
313
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) => {
314
339
  try {
315
- const { purgeEvents } = await import("./maintenance-7LOAGG6J.js");
340
+ const { purgeEvents } = await import("./maintenance-ZJW2ES4L.js");
316
341
  await purgeEvents(options);
317
342
  } catch (err) {
318
343
  handleError(err);
@@ -321,7 +346,7 @@ adminCmd.command("purge-events").description(CLI_DOCS.adminPurgeEvents.descripti
321
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(
322
347
  async (options) => {
323
348
  try {
324
- const { reconcileFiles } = await import("./maintenance-7LOAGG6J.js");
349
+ const { reconcileFiles } = await import("./maintenance-ZJW2ES4L.js");
325
350
  await reconcileFiles(options);
326
351
  } catch (err) {
327
352
  handleError(err);
@@ -330,7 +355,7 @@ adminCmd.command("reconcile-files").description(CLI_DOCS.adminReconcileFiles.des
330
355
  );
331
356
  adminCmd.command("retry-sync-failures").description(CLI_DOCS.adminRetrySyncFailures.description).option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("adminRetrySyncFailures")).action(async (options) => {
332
357
  try {
333
- const { retrySyncFailures } = await import("./maintenance-7LOAGG6J.js");
358
+ const { retrySyncFailures } = await import("./maintenance-ZJW2ES4L.js");
334
359
  await retrySyncFailures(options);
335
360
  } catch (err) {
336
361
  handleError(err);
@@ -339,7 +364,7 @@ adminCmd.command("retry-sync-failures").description(CLI_DOCS.adminRetrySyncFailu
339
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(
340
365
  async (options) => {
341
366
  try {
342
- const { listAdminJobs } = await import("./jobs-K3LTNETU.js");
367
+ const { listAdminJobs } = await import("./jobs-ZWSEXNFA.js");
343
368
  await listAdminJobs(options);
344
369
  } catch (err) {
345
370
  handleError(err);
@@ -348,7 +373,7 @@ var adminJobsCmd = adminCmd.command("jobs").description(CLI_DOCS.adminJobs.descr
348
373
  );
349
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) => {
350
375
  try {
351
- const { retryAdminJob } = await import("./jobs-K3LTNETU.js");
376
+ const { retryAdminJob } = await import("./jobs-ZWSEXNFA.js");
352
377
  await retryAdminJob(id, options);
353
378
  } catch (err) {
354
379
  handleError(err);
@@ -356,7 +381,7 @@ adminJobsCmd.command("retry").description(CLI_DOCS.adminJobsRetry.description).a
356
381
  });
357
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) => {
358
383
  try {
359
- const { deleteAdminJob } = await import("./jobs-K3LTNETU.js");
384
+ const { deleteAdminJob } = await import("./jobs-ZWSEXNFA.js");
360
385
  await deleteAdminJob(id, options);
361
386
  } catch (err) {
362
387
  handleError(err);
@@ -364,7 +389,7 @@ adminJobsCmd.command("delete").description(CLI_DOCS.adminJobsDelete.description)
364
389
  });
365
390
  var adminQueuesCmd = adminCmd.command("queues").description(CLI_DOCS.adminQueues.description).option("--json", "Output as JSON").addHelpText("after", renderCommandHelp("adminQueues")).action(async (options) => {
366
391
  try {
367
- const { listQueues } = await import("./queues-2GJAMMNF.js");
392
+ const { listQueues } = await import("./queues-EZ2VZGXQ.js");
368
393
  await listQueues(options);
369
394
  } catch (err) {
370
395
  handleError(err);
@@ -373,7 +398,7 @@ var adminQueuesCmd = adminCmd.command("queues").description(CLI_DOCS.adminQueues
373
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(
374
399
  async (name, options) => {
375
400
  try {
376
- const { cleanQueue } = await import("./queues-2GJAMMNF.js");
401
+ const { cleanQueue } = await import("./queues-EZ2VZGXQ.js");
377
402
  await cleanQueue(name, options);
378
403
  } catch (err) {
379
404
  handleError(err);
@@ -383,7 +408,7 @@ adminQueuesCmd.command("clean").description(CLI_DOCS.adminQueuesClean.descriptio
383
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(
384
409
  async (bucket, key, options) => {
385
410
  try {
386
- const { deleteFile } = await import("./delete-4JSVNETO.js");
411
+ const { deleteFile } = await import("./delete-YEXSMG4I.js");
387
412
  await deleteFile(bucket, key, options);
388
413
  } catch (err) {
389
414
  handleError(err);
@@ -392,7 +417,7 @@ program.command("delete").description(CLI_DOCS.delete.description).argument("<bu
392
417
  );
393
418
  program.command("whoami").description(CLI_DOCS.whoami.description).addHelpText("after", renderCommandHelp("whoami")).action(async () => {
394
419
  try {
395
- const { whoami } = await import("./whoami-5IYRZKWS.js");
420
+ const { whoami } = await import("./whoami-TVRKBM74.js");
396
421
  await whoami();
397
422
  } catch (err) {
398
423
  handleError(err);
@@ -408,7 +433,7 @@ program.command("open").description(CLI_DOCS.open.description).argument("<bucket
408
433
  });
409
434
  program.command("describe").argument("[command]", "Command path (dot notation, e.g. search.text)").description("Show command schema for AI agents").action(async (command) => {
410
435
  try {
411
- const { describeCommand } = await import("./describe-UFMXNNUB.js");
436
+ const { describeCommand } = await import("./describe-J4ZMUXK7.js");
412
437
  describeCommand(command ?? "");
413
438
  } catch (err) {
414
439
  handleError(err);
@@ -427,5 +452,5 @@ program.parse();
427
452
  var opts = program.opts();
428
453
  var args = process.argv.slice(2);
429
454
  if (args.length === 0 || args.length === 1 && opts.interactive) {
430
- import("./app-DTW2LNXW.js").then(({ startInteractive }) => startInteractive()).catch(handleError);
455
+ import("./app-Q6EW7VSM.js").then(({ startInteractive }) => startInteractive()).catch(handleError);
431
456
  }
@@ -0,0 +1,34 @@
1
+ import {
2
+ validateBucketName,
3
+ validateFileKey
4
+ } from "./chunk-PLZFHPLC.js";
5
+ import {
6
+ createStow
7
+ } from "./chunk-5LU25QZK.js";
8
+ import "./chunk-TOADDO2F.js";
9
+
10
+ // src/commands/delete.ts
11
+ async function deleteFile(bucket, key, options = {}) {
12
+ validateBucketName(bucket);
13
+ validateFileKey(key);
14
+ if (options.dryRun) {
15
+ console.log(
16
+ JSON.stringify(
17
+ {
18
+ dryRun: true,
19
+ action: "deleteFile",
20
+ details: { bucket, key }
21
+ },
22
+ null,
23
+ 2
24
+ )
25
+ );
26
+ return;
27
+ }
28
+ const stow = createStow();
29
+ await stow.deleteFile(key, { bucket });
30
+ console.log(`Deleted: ${key} from ${bucket}`);
31
+ }
32
+ export {
33
+ deleteFile
34
+ };
@@ -0,0 +1,79 @@
1
+ import {
2
+ CLI_DOCS
3
+ } from "./chunk-XJDK2CBE.js";
4
+ import {
5
+ output
6
+ } from "./chunk-P2BKGBQE.js";
7
+
8
+ // src/commands/describe.ts
9
+ var COMMAND_MAP = {
10
+ upload: "upload",
11
+ drop: "drop",
12
+ delete: "delete",
13
+ whoami: "whoami",
14
+ open: "open",
15
+ buckets: "buckets",
16
+ "buckets.create": "bucketsCreate",
17
+ "buckets.rename": "bucketsRename",
18
+ "buckets.delete": "bucketsDelete",
19
+ files: "files",
20
+ "files.get": "filesGet",
21
+ "files.update": "filesUpdate",
22
+ "files.enrich": "filesEnrich",
23
+ "files.missing": "filesMissing",
24
+ search: "search",
25
+ "search.text": "searchText",
26
+ "search.similar": "searchSimilar",
27
+ "search.color": "searchColor",
28
+ "search.diverse": "searchDiverse",
29
+ tags: "tags",
30
+ "tags.create": "tagsCreate",
31
+ "tags.delete": "tagsDelete",
32
+ drops: "drops",
33
+ "drops.delete": "dropsDelete",
34
+ profiles: "profiles",
35
+ "profiles.create": "profilesCreate",
36
+ "profiles.get": "profilesGet",
37
+ "profiles.delete": "profilesDelete",
38
+ jobs: "jobs",
39
+ "jobs.retry": "jobsRetry",
40
+ "jobs.delete": "jobsDelete",
41
+ admin: "admin",
42
+ "admin.health": "adminHealth",
43
+ "admin.backfill": "adminBackfill",
44
+ "admin.cleanup-drops": "adminCleanupDrops",
45
+ "admin.purge-events": "adminPurgeEvents",
46
+ "admin.reconcile-files": "adminReconcileFiles",
47
+ "admin.retry-sync-failures": "adminRetrySyncFailures",
48
+ "admin.jobs": "adminJobs",
49
+ "admin.jobs.retry": "adminJobsRetry",
50
+ "admin.jobs.delete": "adminJobsDelete",
51
+ "admin.queues": "adminQueues",
52
+ "admin.queues.clean": "adminQueuesClean"
53
+ };
54
+ function describeCommand(commandPath) {
55
+ if (!commandPath) {
56
+ output({
57
+ commands: Object.keys(COMMAND_MAP).sort()
58
+ });
59
+ return;
60
+ }
61
+ const docKey = COMMAND_MAP[commandPath];
62
+ if (!(docKey && CLI_DOCS[docKey])) {
63
+ console.error(`Unknown command: ${commandPath}`);
64
+ console.error(`Available: ${Object.keys(COMMAND_MAP).sort().join(", ")}`);
65
+ process.exit(1);
66
+ return;
67
+ }
68
+ const doc = CLI_DOCS[docKey];
69
+ output({
70
+ command: commandPath,
71
+ description: doc.description,
72
+ usage: doc.usage,
73
+ examples: doc.examples,
74
+ notes: doc.notes ?? []
75
+ });
76
+ }
77
+ export {
78
+ describeCommand
79
+ };
@@ -0,0 +1,39 @@
1
+ import {
2
+ formatBytes,
3
+ formatTable,
4
+ usageBar
5
+ } from "./chunk-FZGOTXTE.js";
6
+ import {
7
+ createStow
8
+ } from "./chunk-5LU25QZK.js";
9
+ import "./chunk-TOADDO2F.js";
10
+
11
+ // src/commands/drops.ts
12
+ async function listDrops() {
13
+ const stow = createStow();
14
+ const data = await stow.listDrops();
15
+ if (data.drops.length === 0) {
16
+ console.log("No drops yet. Create one with: stow drop <file>");
17
+ return;
18
+ }
19
+ const rows = data.drops.map((d) => [
20
+ d.filename,
21
+ formatBytes(Number(d.size)),
22
+ d.contentType,
23
+ d.url
24
+ ]);
25
+ console.log(formatTable(["Filename", "Size", "Type", "URL"], rows));
26
+ console.log(
27
+ `
28
+ Storage: ${usageBar(data.usage.bytes, data.usage.limit)} ${formatBytes(data.usage.bytes)} / ${formatBytes(data.usage.limit)}`
29
+ );
30
+ }
31
+ async function deleteDrop(id) {
32
+ const stow = createStow();
33
+ await stow.deleteDrop(id);
34
+ console.log(`Deleted drop: ${id}`);
35
+ }
36
+ export {
37
+ deleteDrop,
38
+ listDrops
39
+ };