@dropthis/cli 0.1.5 → 0.2.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/cli.cjs CHANGED
@@ -69,37 +69,37 @@ function errorEnvelope(code, message, nextAction) {
69
69
  }
70
70
  };
71
71
  }
72
- function apiErrorEnvelope(error) {
72
+ function apiErrorEnvelope(error2) {
73
73
  return {
74
74
  ok: false,
75
75
  error: {
76
- code: error.code ?? "api_error",
77
- message: error.message,
78
- ...error.statusCode !== void 0 ? { status: error.statusCode } : {},
79
- ...typeof error.detail === "string" ? { detail: error.detail } : {},
80
- ...error.param ? { param: error.param } : {},
81
- ...error.currentRevision !== void 0 ? { current_revision: error.currentRevision } : {},
82
- ...error.requestId ? { request_id: error.requestId } : {},
83
- next_action: nextActionForApiError(error)
76
+ code: error2.code ?? "api_error",
77
+ message: error2.message,
78
+ ...error2.statusCode !== void 0 ? { status: error2.statusCode } : {},
79
+ ...typeof error2.detail === "string" ? { detail: error2.detail } : {},
80
+ ...error2.param ? { param: error2.param } : {},
81
+ ...error2.currentRevision !== void 0 ? { current_revision: error2.currentRevision } : {},
82
+ ...error2.requestId ? { request_id: error2.requestId } : {},
83
+ next_action: nextActionForApiError(error2)
84
84
  }
85
85
  };
86
86
  }
87
- function nextActionForApiError(error) {
88
- const uploadNextAction = error.code ? UPLOAD_NEXT_ACTIONS[error.code] : void 0;
87
+ function nextActionForApiError(error2) {
88
+ const uploadNextAction = error2.code ? UPLOAD_NEXT_ACTIONS[error2.code] : void 0;
89
89
  if (uploadNextAction) return uploadNextAction;
90
- if (error.code === "revision_conflict") {
90
+ if (error2.code === "revision_conflict") {
91
91
  return "Fetch the drop, merge your changes, and retry with the current revision.";
92
92
  }
93
- if (error.statusCode === 401 || error.code === "missing_api_key") {
93
+ if (error2.statusCode === 401 || error2.code === "missing_api_key") {
94
94
  return "Authenticate with dropthis login or set DROPTHIS_API_KEY.";
95
95
  }
96
- if (error.statusCode === 413 || error.code === "quota_exceeded") {
96
+ if (error2.statusCode === 413 || error2.code === "quota_exceeded") {
97
97
  return "Reduce the upload size or upgrade the account limit.";
98
98
  }
99
- if (error.statusCode === 422) {
99
+ if (error2.statusCode === 422) {
100
100
  return "Fix the input shown in the error detail and retry.";
101
101
  }
102
- if (error.statusCode !== void 0 && error.statusCode !== null && error.statusCode >= 500) {
102
+ if (error2.statusCode !== void 0 && error2.statusCode !== null && error2.statusCode >= 500) {
103
103
  return "Retry the request with the same idempotency key, or contact support with the request id.";
104
104
  }
105
105
  return "Fix the request or retry after checking the drop state.";
@@ -112,60 +112,208 @@ function exitCodeFor(code) {
112
112
  return 1;
113
113
  }
114
114
 
115
- // src/commands/account.ts
116
- async function runAccountGet(_input, deps) {
117
- if (await resolveCredential({ env: deps.env, store: deps.store }) === null) {
118
- deps.stderr(
119
- `${JSON.stringify(errorEnvelope("auth_error", "No API key found.", "Set DROPTHIS_API_KEY or run dropthis login."))}
120
- `
121
- );
122
- return { exitCode: exitCodeFor("auth_error") };
115
+ // src/ui.ts
116
+ var import_picocolors = __toESM(require("picocolors"), 1);
117
+ var symbols = {
118
+ success: "\u2713",
119
+ error: "\u2717",
120
+ warning: "!"
121
+ };
122
+ function success(msg) {
123
+ return `${import_picocolors.default.green(symbols.success)} ${msg}`;
124
+ }
125
+ function error(msg) {
126
+ return `${import_picocolors.default.red(symbols.error)} ${msg}`;
127
+ }
128
+ function hint(msg) {
129
+ return ` ${import_picocolors.default.dim(msg)}`;
130
+ }
131
+ function url(u) {
132
+ return import_picocolors.default.cyan(u);
133
+ }
134
+ function kvLines(pairs) {
135
+ if (pairs.length === 0) return "";
136
+ const maxKey = Math.max(...pairs.map(([k]) => k.length));
137
+ return pairs.map(([k, v]) => ` ${import_picocolors.default.dim(`${k.padEnd(maxKey)}:`)} ${v}`).join("\n");
138
+ }
139
+
140
+ // src/fmt.ts
141
+ function writeResult(deps, link, verb, data) {
142
+ if (deps.outputMode === "human") {
143
+ deps.stdout(`${success(`${verb} ${url(link)}`)}
144
+ `);
145
+ } else {
146
+ deps.stdout(`${JSON.stringify({ ok: true, drop: data })}
147
+ `);
123
148
  }
124
- const result = await deps.client.account.get();
125
- if (result.error) {
149
+ }
150
+ function writeJson(deps, envelope) {
151
+ deps.stdout(`${JSON.stringify(envelope)}
152
+ `);
153
+ }
154
+ function writeKv(deps, pairs, json) {
155
+ if (deps.outputMode === "human") {
156
+ deps.stdout(`${kvLines(pairs)}
157
+ `);
158
+ } else {
159
+ deps.stdout(`${JSON.stringify(json)}
160
+ `);
161
+ }
162
+ }
163
+ function writeHumanOrJson(deps, humanText, json) {
164
+ if (deps.outputMode === "human") {
165
+ deps.stdout(`${humanText}
166
+ `);
167
+ } else {
168
+ deps.stdout(`${JSON.stringify(json)}
169
+ `);
170
+ }
171
+ }
172
+ function writeError(deps, code, message, nextAction) {
173
+ if (deps.outputMode === "human") {
174
+ deps.stderr(`${error(message)}
175
+ `);
176
+ if (nextAction) deps.stderr(`${hint(nextAction)}
177
+ `);
178
+ } else {
126
179
  deps.stderr(
127
- `${JSON.stringify(errorEnvelope("api_error", result.error.message))}
180
+ `${JSON.stringify(errorEnvelope(code, message, nextAction))}
128
181
  `
129
182
  );
130
- return { exitCode: exitCodeFor("api_error") };
131
183
  }
132
- deps.stdout(`${JSON.stringify({ ok: true, account: result.data })}
184
+ return { exitCode: exitCodeFor(code) };
185
+ }
186
+ function writeApiError(deps, details) {
187
+ if (deps.outputMode === "human") {
188
+ deps.stderr(`${error(details.message)}
189
+ `);
190
+ const nextAction = details.requestId ? `Request ID: ${details.requestId}` : void 0;
191
+ if (nextAction) deps.stderr(`${hint(nextAction)}
192
+ `);
193
+ } else {
194
+ deps.stderr(`${JSON.stringify(apiErrorEnvelope(details))}
195
+ `);
196
+ }
197
+ return { exitCode: exitCodeFor("api_error") };
198
+ }
199
+ function writeApiErrorSimple(deps, message) {
200
+ if (deps.outputMode === "human") {
201
+ deps.stderr(`${error(message)}
202
+ `);
203
+ } else {
204
+ deps.stderr(`${JSON.stringify(errorEnvelope("api_error", message))}
205
+ `);
206
+ }
207
+ return { exitCode: exitCodeFor("api_error") };
208
+ }
209
+ function writeAuthError(deps) {
210
+ return writeError(
211
+ deps,
212
+ "auth_error",
213
+ "No API key found.",
214
+ "Set DROPTHIS_API_KEY or run dropthis login."
215
+ );
216
+ }
217
+ function writeEmpty(deps, message, json) {
218
+ if (deps.outputMode === "human") {
219
+ deps.stdout(`${message}
220
+ `);
221
+ } else {
222
+ deps.stdout(`${JSON.stringify(json)}
133
223
  `);
224
+ }
225
+ }
226
+
227
+ // src/commands/account.ts
228
+ async function runAccountGet(_input, deps) {
229
+ if (await resolveCredential({
230
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
231
+ env: deps.env,
232
+ store: deps.store
233
+ }) === null) {
234
+ return writeAuthError(deps);
235
+ }
236
+ const result = await deps.client.account.get();
237
+ if (result.error) {
238
+ return writeApiErrorSimple(deps, result.error.message);
239
+ }
240
+ const data = result.data;
241
+ const pairs = [];
242
+ if (data?.id) pairs.push(["ID", String(data.id)]);
243
+ if (data?.email) pairs.push(["Email", String(data.email)]);
244
+ if (data?.plan) pairs.push(["Plan", String(data.plan)]);
245
+ writeKv(deps, pairs, { ok: true, account: result.data });
134
246
  return { exitCode: 0 };
135
247
  }
136
248
 
137
249
  // src/commands/api-keys.ts
250
+ var prompts = __toESM(require("@clack/prompts"), 1);
138
251
  async function runApiKeysCreate(input, deps) {
139
- if (!await hasCredential(deps)) return authError(deps);
252
+ if (!await hasCredential(deps)) return writeAuthError(deps);
140
253
  const result = await deps.client.apiKeys.create({ label: input.label });
141
- if (result.error) return apiError(deps, result.error.message);
142
- deps.stdout(`${JSON.stringify({ ok: true, api_key: result.data })}
143
- `);
254
+ if (result.error) return writeApiErrorSimple(deps, result.error.message);
255
+ const data = result.data;
256
+ const pairs = [];
257
+ if (data.id) pairs.push(["ID", String(data.id)]);
258
+ if (data.key) pairs.push(["Key", String(data.key)]);
259
+ if (data.last4) pairs.push(["Last 4", String(data.last4)]);
260
+ writeKv(deps, pairs, { ok: true, api_key: result.data });
144
261
  return { exitCode: 0 };
145
262
  }
146
263
  async function runApiKeysList(_input, deps) {
147
- if (!await hasCredential(deps)) return authError(deps);
264
+ if (!await hasCredential(deps)) return writeAuthError(deps);
148
265
  const result = await deps.client.apiKeys.list();
149
- if (result.error) return apiError(deps, result.error.message);
150
- deps.stdout(
151
- `${JSON.stringify({ ok: true, api_keys: listItems(result.data) })}
266
+ if (result.error) return writeApiErrorSimple(deps, result.error.message);
267
+ const raw = listItems(result.data);
268
+ const items = Array.isArray(raw) ? raw : void 0;
269
+ if (deps.outputMode === "human") {
270
+ if (!items || items.length === 0) {
271
+ writeEmpty(deps, "No API keys found.", { ok: true, api_keys: [] });
272
+ } else {
273
+ for (const item of items) {
274
+ deps.stdout(
275
+ `${item.id ?? ""} ${item.label ?? ""} ...${item.last4 ?? ""}
152
276
  `
153
- );
277
+ );
278
+ }
279
+ }
280
+ } else {
281
+ writeJson(deps, { ok: true, api_keys: items ?? raw });
282
+ }
154
283
  return { exitCode: 0 };
155
284
  }
156
285
  async function runApiKeysDelete(keyId, input, deps) {
157
286
  if (!input.yes && input.interactive === false) {
158
- return invalidUsage(deps, "Pass --yes to delete in non-interactive mode.");
287
+ return writeError(
288
+ deps,
289
+ "invalid_usage",
290
+ "Pass --yes to delete in non-interactive mode."
291
+ );
159
292
  }
160
- if (!await hasCredential(deps)) return authError(deps);
293
+ if (!input.yes && input.interactive !== false) {
294
+ const confirmed = await prompts.confirm({
295
+ message: `Delete API key ${keyId}?`
296
+ });
297
+ if (prompts.isCancel(confirmed) || !confirmed) {
298
+ return { exitCode: 0 };
299
+ }
300
+ }
301
+ if (!await hasCredential(deps)) return writeAuthError(deps);
161
302
  const result = await deps.client.apiKeys.delete(keyId);
162
- if (result?.error) return apiError(deps, result.error.message);
163
- deps.stdout(`${JSON.stringify({ ok: true, deleted: true, id: keyId })}
164
- `);
303
+ if (result?.error) return writeApiErrorSimple(deps, result.error.message);
304
+ writeHumanOrJson(deps, success(`Deleted ${keyId}`), {
305
+ ok: true,
306
+ deleted: true,
307
+ id: keyId
308
+ });
165
309
  return { exitCode: 0 };
166
310
  }
167
311
  async function hasCredential(deps) {
168
- return await resolveCredential({ env: deps.env, store: deps.store }) !== null;
312
+ return await resolveCredential({
313
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
314
+ env: deps.env,
315
+ store: deps.store
316
+ }) !== null;
169
317
  }
170
318
  function listItems(data) {
171
319
  if (data && typeof data === "object" && "data" in data) {
@@ -176,23 +324,6 @@ function listItems(data) {
176
324
  }
177
325
  return data;
178
326
  }
179
- function authError(deps) {
180
- deps.stderr(
181
- `${JSON.stringify(errorEnvelope("auth_error", "No API key found.", "Set DROPTHIS_API_KEY or run dropthis login."))}
182
- `
183
- );
184
- return { exitCode: exitCodeFor("auth_error") };
185
- }
186
- function apiError(deps, message) {
187
- deps.stderr(`${JSON.stringify(errorEnvelope("api_error", message))}
188
- `);
189
- return { exitCode: exitCodeFor("api_error") };
190
- }
191
- function invalidUsage(deps, message) {
192
- deps.stderr(`${JSON.stringify(errorEnvelope("invalid_usage", message))}
193
- `);
194
- return { exitCode: exitCodeFor("invalid_usage") };
195
- }
196
327
 
197
328
  // src/commands/commands.ts
198
329
  var COMMANDS = [
@@ -290,31 +421,51 @@ async function runCommands(_input, deps) {
290
421
 
291
422
  // src/commands/deployments.ts
292
423
  async function runDeploymentsList(dropId, input, deps) {
293
- if (!await hasCredential2(deps)) return authError2(deps);
424
+ if (!await hasCredential2(deps)) return writeAuthError(deps);
294
425
  const params = {
295
426
  ...input.limit !== void 0 ? { limit: input.limit } : {},
296
427
  ...input.cursor ? { cursor: input.cursor } : {}
297
428
  };
298
429
  const result = await deps.client.deployments.list(dropId, params);
299
430
  if (result.error) {
300
- deps.stderr(`${JSON.stringify(apiErrorEnvelope(result.error))}
301
- `);
302
- return { exitCode: exitCodeFor("api_error") };
431
+ return writeApiError(deps, result.error);
432
+ }
433
+ const spread = spreadData(result.data);
434
+ const items = spread.deployments ?? spread.data ?? result.data;
435
+ if (deps.outputMode === "human") {
436
+ if (!items || Array.isArray(items) && items.length === 0) {
437
+ writeEmpty(deps, "No deployments found.", {
438
+ ok: true,
439
+ ...spread
440
+ });
441
+ } else if (Array.isArray(items)) {
442
+ for (const item of items) {
443
+ deps.stdout(
444
+ `${item.id ?? ""} rev ${item.revision ?? "?"} ${item.created_at ?? ""}
445
+ `
446
+ );
447
+ }
448
+ } else {
449
+ writeJson(deps, { ok: true, ...spread });
450
+ }
451
+ } else {
452
+ writeJson(deps, { ok: true, ...spread });
303
453
  }
304
- deps.stdout(`${JSON.stringify({ ok: true, ...spreadData(result.data) })}
305
- `);
306
454
  return { exitCode: 0 };
307
455
  }
308
456
  async function runDeploymentsGet(dropId, deploymentId, _input, deps) {
309
- if (!await hasCredential2(deps)) return authError2(deps);
457
+ if (!await hasCredential2(deps)) return writeAuthError(deps);
310
458
  const result = await deps.client.deployments.get(dropId, deploymentId);
311
459
  if (result.error) {
312
- deps.stderr(`${JSON.stringify(apiErrorEnvelope(result.error))}
313
- `);
314
- return { exitCode: exitCodeFor("api_error") };
315
- }
316
- deps.stdout(`${JSON.stringify({ ok: true, deployment: result.data })}
317
- `);
460
+ return writeApiError(deps, result.error);
461
+ }
462
+ const data = result.data;
463
+ const pairs = [];
464
+ if (data.id) pairs.push(["ID", String(data.id)]);
465
+ if (data.revision !== void 0)
466
+ pairs.push(["Revision", String(data.revision)]);
467
+ if (data.created_at) pairs.push(["Created", String(data.created_at)]);
468
+ writeKv(deps, pairs, { ok: true, deployment: result.data });
318
469
  return { exitCode: 0 };
319
470
  }
320
471
  async function hasCredential2(deps) {
@@ -324,13 +475,6 @@ async function hasCredential2(deps) {
324
475
  store: deps.store
325
476
  }) !== null;
326
477
  }
327
- function authError2(deps) {
328
- deps.stderr(
329
- `${JSON.stringify(errorEnvelope("auth_error", "No API key found.", "Set DROPTHIS_API_KEY or run dropthis login."))}
330
- `
331
- );
332
- return { exitCode: exitCodeFor("auth_error") };
333
- }
334
478
  function spreadData(data) {
335
479
  return data && typeof data === "object" ? data : { data };
336
480
  }
@@ -342,18 +486,25 @@ async function runDoctor(_input, deps) {
342
486
  env: deps.env,
343
487
  store: deps.store
344
488
  });
345
- deps.stdout(
346
- `${JSON.stringify({
347
- ok: true,
348
- version: "0.1.0",
349
- auth: { source: credential?.source ?? "missing" },
350
- storage: { backend: stored?.storage ?? "none" }
351
- })}
352
- `
353
- );
489
+ const authSource = credential?.source ?? "missing";
490
+ const storageBackend = stored?.storage ?? "none";
491
+ const pairs = [
492
+ ["Version", "0.2.1"],
493
+ ["Auth", authSource],
494
+ ["Storage", storageBackend]
495
+ ];
496
+ writeKv(deps, pairs, {
497
+ ok: true,
498
+ version: "0.2.1",
499
+ auth: { source: authSource },
500
+ storage: { backend: storageBackend }
501
+ });
354
502
  return { exitCode: 0 };
355
503
  }
356
504
 
505
+ // src/commands/drops.ts
506
+ var prompts2 = __toESM(require("@clack/prompts"), 1);
507
+
357
508
  // src/options.ts
358
509
  var import_promises = require("fs/promises");
359
510
  async function parseDropOptions(raw) {
@@ -396,56 +547,98 @@ function parseJsonObject(value, label) {
396
547
 
397
548
  // src/commands/drops.ts
398
549
  async function runDropsList(input, deps) {
399
- if (!await hasCredential3(deps)) return authError3(deps);
550
+ if (!await hasCredential3(deps)) return writeAuthError(deps);
400
551
  const params = {
401
552
  ...input.limit !== void 0 ? { limit: input.limit } : {},
402
553
  ...input.cursor ? { cursor: input.cursor } : {}
403
554
  };
404
555
  const result = await deps.client.drops.list(params);
405
- if (result.error) return apiError2(deps, result.error.message);
406
- deps.stdout(
407
- `${JSON.stringify({ ok: true, drops: listItems2(result.data) })}
556
+ if (result.error) return writeApiErrorSimple(deps, result.error.message);
557
+ const raw = listItems2(result.data);
558
+ const items = Array.isArray(raw) ? raw : void 0;
559
+ if (deps.outputMode === "human") {
560
+ if (!items || items.length === 0) {
561
+ writeEmpty(deps, "No drops found.", { ok: true, drops: [] });
562
+ } else {
563
+ for (const item of items) {
564
+ deps.stdout(
565
+ `${item.id ?? ""} ${item.title ?? ""} ${item.url ? url(String(item.url)) : ""}
408
566
  `
409
- );
567
+ );
568
+ }
569
+ }
570
+ } else {
571
+ writeJson(deps, { ok: true, drops: items ?? raw });
572
+ }
410
573
  return { exitCode: 0 };
411
574
  }
412
575
  async function runDropsGet(dropId, _input, deps) {
413
- if (!await hasCredential3(deps)) return authError3(deps);
576
+ if (!await hasCredential3(deps)) return writeAuthError(deps);
414
577
  const result = await deps.client.drops.get(dropId);
415
- if (result.error) return apiError2(deps, result.error.message);
416
- deps.stdout(`${JSON.stringify({ ok: true, drop: result.data })}
417
- `);
578
+ if (result.error) return writeApiErrorSimple(deps, result.error.message);
579
+ const data = result.data;
580
+ const pairs = [];
581
+ if (data.id) pairs.push(["ID", String(data.id)]);
582
+ if (data.url) pairs.push(["URL", url(String(data.url))]);
583
+ if (data.title) pairs.push(["Title", String(data.title)]);
584
+ if (data.visibility) pairs.push(["Visibility", String(data.visibility)]);
585
+ if (data.revision !== void 0)
586
+ pairs.push(["Revision", String(data.revision)]);
587
+ if (data.created_at) pairs.push(["Created", String(data.created_at)]);
588
+ writeKv(deps, pairs, { ok: true, drop: result.data });
418
589
  return { exitCode: 0 };
419
590
  }
420
591
  async function runDropsUpdate(dropId, input, deps) {
421
- if (!await hasCredential3(deps)) return authError3(deps);
592
+ if (!await hasCredential3(deps)) return writeAuthError(deps);
422
593
  try {
423
594
  const options = await parseDropOptions(input);
424
595
  const result = await deps.client.drops.update(dropId, options);
425
- if (result.error) return apiError2(deps, result.error.message);
426
- deps.stdout(`${JSON.stringify({ ok: true, drop: result.data })}
427
- `);
596
+ if (result.error) return writeApiErrorSimple(deps, result.error.message);
597
+ writeHumanOrJson(deps, success(`Updated ${dropId}`), {
598
+ ok: true,
599
+ drop: result.data
600
+ });
428
601
  return { exitCode: 0 };
429
- } catch (error) {
430
- return invalidUsage2(
602
+ } catch (error2) {
603
+ return writeError(
431
604
  deps,
432
- error instanceof Error ? error.message : "Invalid drop options."
605
+ "invalid_usage",
606
+ error2 instanceof Error ? error2.message : "Invalid drop options."
433
607
  );
434
608
  }
435
609
  }
436
610
  async function runDropsDelete(dropId, input, deps) {
437
611
  if (!input.yes && input.interactive === false) {
438
- return invalidUsage2(deps, "Pass --yes to delete in non-interactive mode.");
612
+ return writeError(
613
+ deps,
614
+ "invalid_usage",
615
+ "Pass --yes to delete in non-interactive mode."
616
+ );
439
617
  }
440
- if (!await hasCredential3(deps)) return authError3(deps);
618
+ if (!input.yes && input.interactive !== false) {
619
+ const confirmed = await prompts2.confirm({
620
+ message: `Delete ${dropId}?`
621
+ });
622
+ if (prompts2.isCancel(confirmed) || !confirmed) {
623
+ return { exitCode: 0 };
624
+ }
625
+ }
626
+ if (!await hasCredential3(deps)) return writeAuthError(deps);
441
627
  const result = await deps.client.drops.delete(dropId);
442
- if (result?.error) return apiError2(deps, result.error.message);
443
- deps.stdout(`${JSON.stringify({ ok: true, deleted: true, id: dropId })}
444
- `);
628
+ if (result?.error) return writeApiErrorSimple(deps, result.error.message);
629
+ writeHumanOrJson(deps, success(`Deleted ${dropId}`), {
630
+ ok: true,
631
+ deleted: true,
632
+ id: dropId
633
+ });
445
634
  return { exitCode: 0 };
446
635
  }
447
636
  async function hasCredential3(deps) {
448
- return await resolveCredential({ env: deps.env, store: deps.store }) !== null;
637
+ return await resolveCredential({
638
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
639
+ env: deps.env,
640
+ store: deps.store
641
+ }) !== null;
449
642
  }
450
643
  function listItems2(data) {
451
644
  if (data && typeof data === "object" && "data" in data) {
@@ -456,57 +649,40 @@ function listItems2(data) {
456
649
  }
457
650
  return data;
458
651
  }
459
- function authError3(deps) {
460
- deps.stderr(
461
- `${JSON.stringify(errorEnvelope("auth_error", "No API key found.", "Set DROPTHIS_API_KEY or run dropthis login."))}
462
- `
463
- );
464
- return { exitCode: exitCodeFor("auth_error") };
465
- }
466
- function apiError2(deps, message) {
467
- deps.stderr(`${JSON.stringify(errorEnvelope("api_error", message))}
468
- `);
469
- return { exitCode: exitCodeFor("api_error") };
470
- }
471
- function invalidUsage2(deps, message) {
472
- deps.stderr(`${JSON.stringify(errorEnvelope("invalid_usage", message))}
473
- `);
474
- return { exitCode: exitCodeFor("invalid_usage") };
475
- }
476
652
 
477
653
  // src/commands/login.ts
478
- var prompts = __toESM(require("@clack/prompts"), 1);
654
+ var prompts3 = __toESM(require("@clack/prompts"), 1);
479
655
  async function runLoginInteractive(deps) {
480
- prompts.intro("dropthis login");
481
- const email = await prompts.text({
656
+ prompts3.intro("dropthis login");
657
+ const email = await prompts3.text({
482
658
  message: "Email address",
483
659
  validate: (v) => v?.includes("@") ? void 0 : "Enter a valid email"
484
660
  });
485
- if (prompts.isCancel(email)) {
486
- prompts.cancel("Login cancelled.");
661
+ if (prompts3.isCancel(email)) {
662
+ prompts3.cancel("Login cancelled.");
487
663
  return { exitCode: 0 };
488
664
  }
489
665
  const requestResult = await deps.client.auth.requestEmailOtp({ email });
490
666
  if (requestResult.error) {
491
- prompts.cancel(requestResult.error.message);
667
+ prompts3.cancel(requestResult.error.message);
492
668
  return { exitCode: exitCodeFor("api_error") };
493
669
  }
494
- const otp = await prompts.text({
670
+ const otp = await prompts3.text({
495
671
  message: "Paste the code from your email"
496
672
  });
497
- if (prompts.isCancel(otp)) {
498
- prompts.cancel("Login cancelled.");
673
+ if (prompts3.isCancel(otp)) {
674
+ prompts3.cancel("Login cancelled.");
499
675
  return { exitCode: 0 };
500
676
  }
501
677
  const session = await deps.client.auth.verifyEmailOtp({ email, code: otp });
502
678
  if (session.error) {
503
- prompts.cancel(session.error.message);
679
+ prompts3.cancel(session.error.message);
504
680
  return { exitCode: exitCodeFor("api_error") };
505
681
  }
506
682
  const authedClient = deps.createClient(session.data.token);
507
683
  const apiKey = await authedClient.apiKeys.create({ label: "CLI" });
508
684
  if (apiKey.error) {
509
- prompts.cancel(apiKey.error.message);
685
+ prompts3.cancel(apiKey.error.message);
510
686
  return { exitCode: exitCodeFor("api_error") };
511
687
  }
512
688
  await deps.store.save({
@@ -517,7 +693,7 @@ async function runLoginInteractive(deps) {
517
693
  email,
518
694
  storage: "secure"
519
695
  });
520
- prompts.outro("Logged in successfully.");
696
+ prompts3.outro("Logged in successfully.");
521
697
  return { exitCode: 0 };
522
698
  }
523
699
  async function runLoginRequest(input, deps) {
@@ -553,7 +729,8 @@ async function runLoginVerify(input, deps) {
553
729
  );
554
730
  return { exitCode: exitCodeFor("api_error") };
555
731
  }
556
- const apiKey = await deps.client.apiKeys.create({ label: "CLI" });
732
+ const authedClient = deps.createClient(session.data.token);
733
+ const apiKey = await authedClient.apiKeys.create({ label: "CLI" });
557
734
  if (apiKey.error) {
558
735
  deps.stderr(
559
736
  `${JSON.stringify(errorEnvelope("api_error", apiKey.error.message))}
@@ -590,14 +767,11 @@ async function runLogout(input, deps) {
590
767
  const result = await deps.client.apiKeys.delete(stored.keyId);
591
768
  if (result?.error) revokeError = result.error.message;
592
769
  }
593
- await deps.store.clear();
594
770
  if (revokeError) {
595
- deps.stderr(`${JSON.stringify(errorEnvelope("api_error", revokeError))}
596
- `);
597
- return { exitCode: exitCodeFor("api_error") };
771
+ return writeApiErrorSimple(deps, revokeError);
598
772
  }
599
- deps.stdout(`${JSON.stringify({ ok: true })}
600
- `);
773
+ await deps.store.clear();
774
+ writeHumanOrJson(deps, success("Logged out."), { ok: true });
601
775
  return { exitCode: 0 };
602
776
  }
603
777
 
@@ -606,14 +780,40 @@ var import_node_crypto = require("crypto");
606
780
  var import_promises3 = require("fs/promises");
607
781
  var import_node_path = require("path");
608
782
 
783
+ // src/spinner.ts
784
+ var import_picocolors2 = __toESM(require("picocolors"), 1);
785
+ var frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
786
+ function createSpinner(message, stderr) {
787
+ let i = 0;
788
+ const timer = setInterval(() => {
789
+ stderr(`\r\x1B[2K${import_picocolors2.default.cyan(frames[i % frames.length])} ${message}`);
790
+ i++;
791
+ }, 80);
792
+ return {
793
+ stop(finalMessage) {
794
+ clearInterval(timer);
795
+ const text2 = finalMessage ?? message;
796
+ stderr(`\r\x1B[2K${import_picocolors2.default.green(symbols.success)} ${text2}
797
+ `);
798
+ },
799
+ fail() {
800
+ clearInterval(timer);
801
+ stderr("\r\x1B[2K");
802
+ }
803
+ };
804
+ }
805
+ function shouldSpin(outputMode) {
806
+ return outputMode === "human" && process.stderr.isTTY === true && !process.env.CI;
807
+ }
808
+
609
809
  // src/commands/json_body.ts
610
810
  var import_promises2 = require("fs/promises");
611
811
  async function readJsonObjectFile(path) {
612
812
  let parsed;
613
813
  try {
614
814
  parsed = JSON.parse(await (0, import_promises2.readFile)(path, "utf8"));
615
- } catch (error) {
616
- const message = error instanceof Error ? error.message : "Unknown parse error";
815
+ } catch (error2) {
816
+ const message = error2 instanceof Error ? error2.message : "Unknown parse error";
617
817
  throw new Error(`Invalid JSON body in ${path}: ${message}`);
618
818
  }
619
819
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -626,21 +826,18 @@ async function readJsonObjectFile(path) {
626
826
  var MAX_BUNDLE_FILE_COUNT = 200;
627
827
  async function runPublish(input, raw, deps) {
628
828
  const credential = await resolveCredential({
629
- ...raw.apiKey ? { apiKey: raw.apiKey } : {},
829
+ ...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
630
830
  env: deps.env,
631
831
  store: deps.store
632
832
  });
633
833
  if (!credential) {
634
- deps.stderr(
635
- `${JSON.stringify(errorEnvelope("auth_error", "No API key found.", "Set DROPTHIS_API_KEY or run dropthis login."))}
636
- `
637
- );
638
- return { exitCode: exitCodeFor("auth_error") };
834
+ return writeAuthError(deps);
639
835
  }
640
836
  if (raw.dryRun) {
641
837
  return handlePublishDryRun(input, raw, deps);
642
838
  }
643
839
  const singleInput = Array.isArray(input) ? input[0] : input;
840
+ const spin = shouldSpin(deps.outputMode) ? createSpinner("Publishing\u2026", deps.stderr) : void 0;
644
841
  try {
645
842
  if (raw.fromJson && singleInput) {
646
843
  throw new Error("Use either <input> or --from-json, not both.");
@@ -653,32 +850,28 @@ async function runPublish(input, raw, deps) {
653
850
  throw new Error("Publish requires <input> or --from-json.");
654
851
  })();
655
852
  if (result.error) {
656
- deps.stderr(`${JSON.stringify(apiErrorEnvelope(result.error))}
657
- `);
658
- return { exitCode: exitCodeFor("api_error") };
853
+ spin?.fail();
854
+ return writeApiError(deps, result.error);
659
855
  }
856
+ spin?.stop("Published");
660
857
  if (raw.url) deps.stdout(`${result.data.url}
661
858
  `);
662
- else if (deps.outputMode === "human")
663
- deps.stdout(`Published: ${result.data.url}
664
- `);
665
- else deps.stdout(`${JSON.stringify({ ok: true, drop: result.data })}
666
- `);
859
+ else writeResult(deps, result.data.url, "Published", result.data);
667
860
  return { exitCode: 0 };
668
- } catch (error) {
669
- const message = error instanceof Error ? error.message : "Publish failed.";
670
- deps.stderr(`${JSON.stringify(errorEnvelope("invalid_usage", message))}
671
- `);
672
- return { exitCode: exitCodeFor("invalid_usage") };
861
+ } catch (error2) {
862
+ const message = error2 instanceof Error ? error2.message : "Publish failed.";
863
+ spin?.fail();
864
+ return writeError(deps, "invalid_usage", message);
673
865
  }
674
866
  }
675
867
  async function handlePublishDryRun(input, raw, deps) {
676
868
  if (raw.url) {
677
- deps.stderr(
678
- `${JSON.stringify(errorEnvelope("invalid_usage", "--url and --dry-run cannot be combined.", "Remove --url; dry-run outputs JSON describing what would be published."))}
679
- `
869
+ return writeError(
870
+ deps,
871
+ "invalid_usage",
872
+ "--url and --dry-run cannot be combined.",
873
+ "Remove --url; dry-run outputs JSON describing what would be published."
680
874
  );
681
- return { exitCode: exitCodeFor("invalid_usage") };
682
875
  }
683
876
  try {
684
877
  if (raw.fromJson) {
@@ -709,12 +902,10 @@ async function handlePublishDryRun(input, raw, deps) {
709
902
  deps.stdout(`${JSON.stringify(output, null, 2)}
710
903
  `);
711
904
  return { exitCode: 0 };
712
- } catch (error) {
713
- const code = isFileSystemError(error) ? "local_input_error" : "invalid_usage";
714
- const message = error instanceof Error ? error.message : "Dry-run failed.";
715
- deps.stderr(`${JSON.stringify(errorEnvelope(code, message))}
716
- `);
717
- return { exitCode: exitCodeFor(code) };
905
+ } catch (error2) {
906
+ const code = isFileSystemError(error2) ? "local_input_error" : "invalid_usage";
907
+ const message = error2 instanceof Error ? error2.message : "Dry-run failed.";
908
+ return writeError(deps, code, message);
718
909
  }
719
910
  }
720
911
  async function dryRunMultiFile(inputs, options, deps) {
@@ -724,18 +915,18 @@ async function dryRunMultiFile(inputs, options, deps) {
724
915
  try {
725
916
  info = await (0, import_promises3.stat)(filePath);
726
917
  } catch {
727
- deps.stderr(
728
- `${JSON.stringify(errorEnvelope("local_input_error", `File not found: ${filePath}`))}
729
- `
918
+ return writeError(
919
+ deps,
920
+ "local_input_error",
921
+ `File not found: ${filePath}`
730
922
  );
731
- return { exitCode: exitCodeFor("local_input_error") };
732
923
  }
733
924
  if (info.isDirectory()) {
734
- deps.stderr(
735
- `${JSON.stringify(errorEnvelope("local_input_error", `Cannot mix directories with multiple file arguments. Use a single folder instead: dropthis publish ${filePath}`))}
736
- `
925
+ return writeError(
926
+ deps,
927
+ "local_input_error",
928
+ `Cannot mix directories with multiple file arguments. Use a single folder instead: dropthis publish ${filePath}`
737
929
  );
738
- return { exitCode: exitCodeFor("local_input_error") };
739
930
  }
740
931
  files.push({
741
932
  path: (0, import_node_path.basename)(filePath),
@@ -744,11 +935,11 @@ async function dryRunMultiFile(inputs, options, deps) {
744
935
  });
745
936
  }
746
937
  if (files.length > MAX_BUNDLE_FILE_COUNT) {
747
- deps.stderr(
748
- `${JSON.stringify(errorEnvelope("local_input_error", `Bundle contains ${files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT}.`))}
749
- `
938
+ return writeError(
939
+ deps,
940
+ "local_input_error",
941
+ `Bundle contains ${files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT}.`
750
942
  );
751
- return { exitCode: exitCodeFor("local_input_error") };
752
943
  }
753
944
  const entry = options.entry ?? files.find((f) => f.path === "index.html")?.path ?? files[0]?.path ?? null;
754
945
  const totalBytes = files.reduce((sum, f) => sum + f.sizeBytes, 0);
@@ -831,9 +1022,9 @@ function mimeForPath(filePath) {
831
1022
  };
832
1023
  return types[ext] ?? "application/octet-stream";
833
1024
  }
834
- function isFileSystemError(error) {
835
- if (!(error instanceof Error)) return false;
836
- const code = error.code;
1025
+ function isFileSystemError(error2) {
1026
+ if (!(error2 instanceof Error)) return false;
1027
+ const code = error2.code;
837
1028
  return code === "ENOENT" || code === "ENOTDIR" || code === "ELIMIT";
838
1029
  }
839
1030
  function withPublishIdempotency(options) {
@@ -844,20 +1035,17 @@ function withPublishIdempotency(options) {
844
1035
  var import_node_crypto2 = require("crypto");
845
1036
  async function runUpdate(target, input, raw, deps) {
846
1037
  const credential = await resolveCredential({
847
- ...raw.apiKey ? { apiKey: raw.apiKey } : {},
1038
+ ...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
848
1039
  env: deps.env,
849
1040
  store: deps.store
850
1041
  });
851
1042
  if (!credential) {
852
- deps.stderr(
853
- `${JSON.stringify(errorEnvelope("auth_error", "No API key found.", "Set DROPTHIS_API_KEY or run dropthis login."))}
854
- `
855
- );
856
- return { exitCode: exitCodeFor("auth_error") };
1043
+ return writeAuthError(deps);
857
1044
  }
858
1045
  if (raw.dryRun) {
859
1046
  return handleUpdateDryRun(target, input, raw, deps);
860
1047
  }
1048
+ const spin = shouldSpin(deps.outputMode) ? createSpinner("Updating\u2026", deps.stderr) : void 0;
861
1049
  try {
862
1050
  assertDropId(target);
863
1051
  if (raw.fromJson && input) {
@@ -881,32 +1069,28 @@ async function runUpdate(target, input, raw, deps) {
881
1069
  })
882
1070
  ) : await deps.client.update(target, parsed, revisionOptions);
883
1071
  if (result.error) {
884
- deps.stderr(`${JSON.stringify(apiErrorEnvelope(result.error))}
885
- `);
886
- return { exitCode: exitCodeFor("api_error") };
1072
+ spin?.fail();
1073
+ return writeApiError(deps, result.error);
887
1074
  }
1075
+ spin?.stop("Updated");
888
1076
  if (raw.url) deps.stdout(`${result.data.url}
889
1077
  `);
890
- else if (deps.outputMode === "human")
891
- deps.stdout(`Updated: ${result.data.url}
892
- `);
893
- else deps.stdout(`${JSON.stringify({ ok: true, drop: result.data })}
894
- `);
1078
+ else writeResult(deps, result.data.url, "Updated", result.data);
895
1079
  return { exitCode: 0 };
896
- } catch (error) {
897
- const message = error instanceof Error ? error.message : "Update failed.";
898
- deps.stderr(`${JSON.stringify(errorEnvelope("invalid_usage", message))}
899
- `);
900
- return { exitCode: exitCodeFor("invalid_usage") };
1080
+ } catch (error2) {
1081
+ const message = error2 instanceof Error ? error2.message : "Update failed.";
1082
+ spin?.fail();
1083
+ return writeError(deps, "invalid_usage", message);
901
1084
  }
902
1085
  }
903
1086
  async function handleUpdateDryRun(target, input, raw, deps) {
904
1087
  if (raw.url) {
905
- deps.stderr(
906
- `${JSON.stringify(errorEnvelope("invalid_usage", "--url and --dry-run cannot be combined.", "Remove --url; dry-run outputs JSON describing what would be published."))}
907
- `
1088
+ return writeError(
1089
+ deps,
1090
+ "invalid_usage",
1091
+ "--url and --dry-run cannot be combined.",
1092
+ "Remove --url; dry-run outputs JSON describing what would be published."
908
1093
  );
909
- return { exitCode: exitCodeFor("invalid_usage") };
910
1094
  }
911
1095
  try {
912
1096
  assertDropId(target);
@@ -947,12 +1131,10 @@ async function handleUpdateDryRun(target, input, raw, deps) {
947
1131
  deps.stdout(`${JSON.stringify(output, null, 2)}
948
1132
  `);
949
1133
  return { exitCode: 0 };
950
- } catch (error) {
951
- const code = isFileSystemError2(error) ? "local_input_error" : "invalid_usage";
952
- const message = error instanceof Error ? error.message : "Dry-run failed.";
953
- deps.stderr(`${JSON.stringify(errorEnvelope(code, message))}
954
- `);
955
- return { exitCode: exitCodeFor(code) };
1134
+ } catch (error2) {
1135
+ const code = isFileSystemError2(error2) ? "local_input_error" : "invalid_usage";
1136
+ const message = error2 instanceof Error ? error2.message : "Dry-run failed.";
1137
+ return writeError(deps, code, message);
956
1138
  }
957
1139
  }
958
1140
  var MAX_BUNDLE_FILE_COUNT2 = 200;
@@ -986,9 +1168,9 @@ function validateDryRunFileCount(output) {
986
1168
  );
987
1169
  }
988
1170
  }
989
- function isFileSystemError2(error) {
990
- if (!(error instanceof Error)) return false;
991
- const code = error.code;
1171
+ function isFileSystemError2(error2) {
1172
+ if (!(error2 instanceof Error)) return false;
1173
+ const code = error2.code;
992
1174
  return code === "ENOENT" || code === "ENOTDIR";
993
1175
  }
994
1176
  function withUpdateIdempotency(options) {
@@ -1009,23 +1191,29 @@ async function runWhoami(_input, deps) {
1009
1191
  store: deps.store
1010
1192
  });
1011
1193
  if (!credential) {
1012
- deps.stdout(`${JSON.stringify({ ok: true, authenticated: false })}
1013
- `);
1194
+ writeHumanOrJson(deps, "Not authenticated. Run dropthis login.", {
1195
+ ok: true,
1196
+ authenticated: false
1197
+ });
1014
1198
  return { exitCode: 0 };
1015
1199
  }
1016
- deps.stdout(
1017
- `${JSON.stringify({
1018
- ok: true,
1019
- authenticated: true,
1020
- source: credential.source,
1021
- masked_key: maskKey(credential.apiKey),
1022
- ...credential.keyId ? { key_id: credential.keyId } : {},
1023
- ...credential.keyLast4 ? { key_last4: credential.keyLast4 } : {},
1024
- ...credential.accountId ? { account_id: credential.accountId } : {},
1025
- ...credential.email ? { email: credential.email } : {}
1026
- })}
1027
- `
1028
- );
1200
+ const masked = maskKey(credential.apiKey);
1201
+ const pairs = [];
1202
+ if (credential.email) pairs.push(["Email", credential.email]);
1203
+ pairs.push(["Key", masked]);
1204
+ pairs.push(["Source", credential.source]);
1205
+ if (credential.accountId) pairs.push(["Account", credential.accountId]);
1206
+ const jsonEnvelope = {
1207
+ ok: true,
1208
+ authenticated: true,
1209
+ source: credential.source,
1210
+ masked_key: masked,
1211
+ ...credential.keyId ? { key_id: credential.keyId } : {},
1212
+ ...credential.keyLast4 ? { key_last4: credential.keyLast4 } : {},
1213
+ ...credential.accountId ? { account_id: credential.accountId } : {},
1214
+ ...credential.email ? { email: credential.email } : {}
1215
+ };
1216
+ writeKv(deps, pairs, jsonEnvelope);
1029
1217
  return { exitCode: 0 };
1030
1218
  }
1031
1219
 
@@ -1079,7 +1267,9 @@ var InsecureFileCredentialStore = class {
1079
1267
  }
1080
1268
  async read() {
1081
1269
  const value = await readJsonFile(this.filePath);
1082
- return value ? { ...value, storage: "insecure" } : null;
1270
+ if (!value) return null;
1271
+ if (typeof value.apiKey !== "string" || !value.apiKey) return null;
1272
+ return { ...value, storage: "insecure" };
1083
1273
  }
1084
1274
  async save(credential) {
1085
1275
  await writeCredentialFile(this.filePath, {
@@ -1102,7 +1292,7 @@ var KeyringCredentialStore = class {
1102
1292
  const metadata = await readJsonFile(this.filePath);
1103
1293
  if (!metadata) return null;
1104
1294
  const apiKey = await this.keyring.getPassword();
1105
- if (!apiKey) return null;
1295
+ if (typeof apiKey !== "string" || !apiKey) return null;
1106
1296
  return { ...metadata, apiKey, storage: "secure" };
1107
1297
  }
1108
1298
  async save(credential) {
@@ -1141,11 +1331,11 @@ function credentialPath(configDir) {
1141
1331
  async function readJsonFile(path) {
1142
1332
  try {
1143
1333
  return JSON.parse(await (0, import_promises4.readFile)(path, "utf8"));
1144
- } catch (error) {
1145
- if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
1334
+ } catch (error2) {
1335
+ if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
1146
1336
  return null;
1147
1337
  }
1148
- throw error;
1338
+ throw error2;
1149
1339
  }
1150
1340
  }
1151
1341
  async function writeCredentialFile(path, credential) {
@@ -1161,7 +1351,7 @@ async function writeCredentialFile(path, credential) {
1161
1351
  function buildProgram(options = {}) {
1162
1352
  const program = new import_commander.Command();
1163
1353
  const store = options.store ?? new MemoryCredentialStore();
1164
- program.name("dropthis").description("Publish anything online and get a URL back.").version("0.1.5");
1354
+ program.name("dropthis").description("Publish anything online and get a URL back.").version("0.2.1");
1165
1355
  program.option("--api-key <key>", "Override API key for this invocation");
1166
1356
  program.option("--api-url <url>", "Override API base URL");
1167
1357
  program.option("--json", "Force JSON output");
@@ -1170,8 +1360,8 @@ function buildProgram(options = {}) {
1170
1360
  async (commandOptions) => {
1171
1361
  if (!commandOptions.email || !commandOptions.otp) {
1172
1362
  const deps2 = await commandDeps(program, options, store, commandOptions);
1173
- const global = globalOptions(program, commandOptions);
1174
- const context = createContext({ global });
1363
+ const global2 = globalOptions(program, commandOptions);
1364
+ const context = createContext({ global: global2 });
1175
1365
  const result2 = await runLoginInteractive({
1176
1366
  ...deps2,
1177
1367
  createClient: (apiKey) => context.createClient(apiKey)
@@ -1180,13 +1370,18 @@ function buildProgram(options = {}) {
1180
1370
  return;
1181
1371
  }
1182
1372
  const deps = await commandDeps(program, options, store, commandOptions);
1373
+ const global = globalOptions(program, commandOptions);
1374
+ const verifyContext = createContext({ global });
1183
1375
  const result = await runLoginVerify(
1184
1376
  {
1185
1377
  email: commandOptions.email,
1186
1378
  otp: commandOptions.otp,
1187
1379
  ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
1188
1380
  },
1189
- deps
1381
+ {
1382
+ ...deps,
1383
+ createClient: (apiKey) => verifyContext.createClient(apiKey)
1384
+ }
1190
1385
  );
1191
1386
  process.exitCode = result.exitCode;
1192
1387
  }
@@ -1200,7 +1395,12 @@ function buildProgram(options = {}) {
1200
1395
  login?.command("verify").description("Verify email OTP and store an API key").requiredOption("--email <email>", "Email address").requiredOption("--otp <otp>", "One-time passcode").option("--json", "Force JSON output").action(
1201
1396
  async (commandOptions) => {
1202
1397
  const deps = await commandDeps(program, options, store, commandOptions);
1203
- const result = await runLoginVerify(commandOptions, deps);
1398
+ const global = globalOptions(program, commandOptions);
1399
+ const verifySubContext = createContext({ global });
1400
+ const result = await runLoginVerify(commandOptions, {
1401
+ ...deps,
1402
+ createClient: (apiKey) => verifySubContext.createClient(apiKey)
1403
+ });
1204
1404
  process.exitCode = result.exitCode;
1205
1405
  }
1206
1406
  );
@@ -1422,7 +1622,9 @@ function toDropOptions(options) {
1422
1622
  };
1423
1623
  }
1424
1624
  function parseInteger(value) {
1425
- return Number.parseInt(value, 10);
1625
+ const n = Number.parseInt(value, 10);
1626
+ if (Number.isNaN(n)) throw new import_commander.InvalidArgumentError("Expected an integer.");
1627
+ return n;
1426
1628
  }
1427
1629
  async function resolveCommandInput(input, stdin) {
1428
1630
  if (input !== "-") return input;
@@ -1443,22 +1645,33 @@ async function resolvePublishInputs(inputs, stdin, stdinIsTTY) {
1443
1645
  }
1444
1646
  return inputs[0];
1445
1647
  }
1648
+ const {
1649
+ mkdtemp,
1650
+ copyFile,
1651
+ writeFile: writeFile2,
1652
+ stat: fsStat
1653
+ } = await import("fs/promises");
1654
+ const { join: join2, basename: pathBasename } = await import("path");
1655
+ const { tmpdir } = await import("os");
1656
+ const tmpDir = await mkdtemp(join2(tmpdir(), "dropthis-multi-"));
1446
1657
  const resolved = [];
1447
1658
  for (const input of inputs) {
1448
1659
  if (input === "-") {
1449
1660
  const buf = await readStdinBytes(stdin ?? process.stdin);
1450
- const tmpPath = `/tmp/.dropthis-stdin-${Date.now()}`;
1451
- const { writeFile: writeFile2 } = await import("fs/promises");
1661
+ const tmpPath = join2(tmpDir, `stdin-${Date.now()}`);
1452
1662
  await writeFile2(tmpPath, buf);
1453
1663
  resolved.push(tmpPath);
1454
1664
  } else {
1455
1665
  resolved.push(input);
1456
1666
  }
1457
1667
  }
1458
- const { mkdtemp, copyFile, stat: fsStat } = await import("fs/promises");
1459
- const { join: join2, basename: pathBasename } = await import("path");
1460
- const { tmpdir } = await import("os");
1461
- const tmpDir = await mkdtemp(join2(tmpdir(), "dropthis-multi-"));
1668
+ const names = resolved.map((f) => pathBasename(f));
1669
+ const dupes = names.filter((n, i) => names.indexOf(n) !== i);
1670
+ if (dupes.length > 0) {
1671
+ throw new Error(
1672
+ `Duplicate file names: ${[...new Set(dupes)].join(", ")}. Rename files to avoid collisions.`
1673
+ );
1674
+ }
1462
1675
  for (const filePath of resolved) {
1463
1676
  const info = await fsStat(filePath);
1464
1677
  if (info.isDirectory()) {
@@ -1482,8 +1695,8 @@ async function readStdinBytes(stdin) {
1482
1695
  }
1483
1696
 
1484
1697
  // src/cli.ts
1485
- buildProgram({ store: createCredentialStore() }).parseAsync(process.argv).catch((error) => {
1486
- console.error(error);
1698
+ buildProgram({ store: createCredentialStore() }).parseAsync(process.argv).catch((error2) => {
1699
+ console.error(error2);
1487
1700
  process.exitCode = 1;
1488
1701
  });
1489
1702
  //# sourceMappingURL=cli.cjs.map