@dropthis/cli 0.5.0 → 0.6.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
@@ -29,6 +29,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
29
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
30
  mod
31
31
  ));
32
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
33
 
33
34
  // src/inline-auth.ts
34
35
  var inline_auth_exports = {};
@@ -150,42 +151,12 @@ var init_inline_auth = __esm({
150
151
  }
151
152
  });
152
153
 
153
- // src/program.ts
154
- var import_commander = require("commander");
155
- var import_picocolors4 = __toESM(require("picocolors"), 1);
156
-
157
- // src/auth.ts
158
- async function resolveCredential(input) {
159
- if (input.apiKey) return { apiKey: input.apiKey, source: "flag" };
160
- if (input.env.DROPTHIS_API_KEY) {
161
- return { apiKey: input.env.DROPTHIS_API_KEY, source: "env" };
162
- }
163
- const stored = await input.store.read();
164
- if (!stored) return null;
165
- return {
166
- apiKey: stored.apiKey,
167
- source: "storage",
168
- ...stored.keyId ? { keyId: stored.keyId } : {},
169
- ...stored.keyLast4 ? { keyLast4: stored.keyLast4 } : {},
170
- ...stored.accountId ? { accountId: stored.accountId } : {},
171
- ...stored.email ? { email: stored.email } : {}
172
- };
173
- }
174
- async function requireCredential(input) {
175
- const credential = await resolveCredential(input);
176
- if (!credential) {
177
- throw Object.assign(new Error("No API key found."), {
178
- code: "auth_error"
179
- });
180
- }
181
- return credential;
182
- }
183
- function maskKey(apiKey) {
184
- return apiKey.length <= 8 ? "sk_..." : `${apiKey.slice(0, 3)}...${apiKey.slice(-4)}`;
185
- }
186
-
187
- // src/fmt.ts
188
- var import_picocolors2 = __toESM(require("picocolors"), 1);
154
+ // src/cli.ts
155
+ var cli_exports = {};
156
+ __export(cli_exports, {
157
+ writeUsageErrorEnvelope: () => writeUsageErrorEnvelope
158
+ });
159
+ module.exports = __toCommonJS(cli_exports);
189
160
 
190
161
  // src/output.ts
191
162
  var UPLOAD_NEXT_ACTIONS = {
@@ -213,7 +184,7 @@ function apiErrorEnvelope(error2) {
213
184
  return {
214
185
  ok: false,
215
186
  error: {
216
- code: error2.code ?? "api_error",
187
+ code: normalizeApiErrorCode(error2),
217
188
  message: error2.message,
218
189
  ...error2.statusCode !== void 0 ? { status: error2.statusCode } : {},
219
190
  ...typeof error2.detail === "string" ? { detail: error2.detail } : {},
@@ -226,6 +197,11 @@ function apiErrorEnvelope(error2) {
226
197
  }
227
198
  };
228
199
  }
200
+ function normalizeApiErrorCode(error2) {
201
+ if (error2.code) return error2.code;
202
+ if (error2.statusCode === 404) return "not_found";
203
+ return "api_error";
204
+ }
229
205
  function nextActionForApiError(error2) {
230
206
  if (error2.suggestion) return error2.suggestion;
231
207
  const uploadNextAction = error2.code ? UPLOAD_NEXT_ACTIONS[error2.code] : void 0;
@@ -233,6 +209,9 @@ function nextActionForApiError(error2) {
233
209
  if (error2.code === "revision_conflict") {
234
210
  return "Fetch the drop, merge your changes, and retry with the current revision.";
235
211
  }
212
+ if (error2.statusCode === 404 || error2.code === "not_found") {
213
+ return "Check the drop id or slug and retry; list your drops with dropthis drops list.";
214
+ }
236
215
  if (error2.statusCode === 401 || error2.code === "missing_api_key") {
237
216
  return "Authenticate with dropthis login or set DROPTHIS_API_KEY.";
238
217
  }
@@ -248,6 +227,7 @@ function nextActionForApiError(error2) {
248
227
  return "Fix the request or retry after checking the drop state.";
249
228
  }
250
229
  function exitCodeFor(code) {
230
+ if (code === "usage_error") return 2;
251
231
  if (code === "invalid_usage") return 2;
252
232
  if (code === "auth_error") return 3;
253
233
  if (code === "local_input_error") return 4;
@@ -255,6 +235,43 @@ function exitCodeFor(code) {
255
235
  return 1;
256
236
  }
257
237
 
238
+ // src/program.ts
239
+ var import_commander = require("commander");
240
+ var import_picocolors4 = __toESM(require("picocolors"), 1);
241
+
242
+ // src/auth.ts
243
+ async function resolveCredential(input) {
244
+ if (input.apiKey) return { apiKey: input.apiKey, source: "flag" };
245
+ if (input.env.DROPTHIS_API_KEY) {
246
+ return { apiKey: input.env.DROPTHIS_API_KEY, source: "env" };
247
+ }
248
+ const stored = await input.store.read();
249
+ if (!stored) return null;
250
+ return {
251
+ apiKey: stored.apiKey,
252
+ source: "storage",
253
+ ...stored.keyId ? { keyId: stored.keyId } : {},
254
+ ...stored.keyLast4 ? { keyLast4: stored.keyLast4 } : {},
255
+ ...stored.accountId ? { accountId: stored.accountId } : {},
256
+ ...stored.email ? { email: stored.email } : {}
257
+ };
258
+ }
259
+ async function requireCredential(input) {
260
+ const credential = await resolveCredential(input);
261
+ if (!credential) {
262
+ throw Object.assign(new Error("No API key found."), {
263
+ code: "auth_error"
264
+ });
265
+ }
266
+ return credential;
267
+ }
268
+ function maskKey(apiKey) {
269
+ return apiKey.length <= 8 ? "sk_..." : `${apiKey.slice(0, 3)}...${apiKey.slice(-4)}`;
270
+ }
271
+
272
+ // src/fmt.ts
273
+ var import_picocolors2 = __toESM(require("picocolors"), 1);
274
+
258
275
  // src/ui.ts
259
276
  var import_picocolors = __toESM(require("picocolors"), 1);
260
277
  var symbols = {
@@ -295,13 +312,13 @@ function writeResult(deps, link, verb, data) {
295
312
  }
296
313
  function formatDropDetails(data) {
297
314
  const parts = [];
298
- const size = data.sizeBytes ?? data.size_bytes;
315
+ const size = data.sizeBytes;
299
316
  if (typeof size === "number") parts.push(formatBytes(size));
300
- const ct = data.contentType ?? data.content_type;
317
+ const ct = data.contentType;
301
318
  if (typeof ct === "string") parts.push(ct.split(";")[0]);
302
319
  const vis = data.visibility;
303
320
  if (vis === "unlisted") parts.push("unlisted");
304
- const exp = data.expiresAt ?? data.expires_at;
321
+ const exp = data.expiresAt;
305
322
  if (typeof exp === "string") parts.push(`expires ${exp.split("T")[0]}`);
306
323
  if (parts.length === 0) return "";
307
324
  return ` ${import_picocolors2.default.dim(parts.join(" \xB7 "))}`;
@@ -404,16 +421,66 @@ async function runAccountGet(_input, deps) {
404
421
  }
405
422
  const result = await deps.client.account.get();
406
423
  if (result.error) {
407
- return writeApiErrorSimple(deps, result.error.message);
424
+ return writeApiError(deps, result.error);
408
425
  }
409
426
  const data = result.data;
410
427
  const pairs = [];
411
428
  if (data?.id) pairs.push(["ID", String(data.id)]);
412
429
  if (data?.email) pairs.push(["Email", String(data.email)]);
413
430
  if (data?.plan) pairs.push(["Plan", String(data.plan)]);
431
+ if (data?.displayName) pairs.push(["Name", String(data.displayName)]);
432
+ if (data?.status) pairs.push(["Status", String(data.status)]);
433
+ if (data?.createdAt)
434
+ pairs.push(["Created", String(data.createdAt).split("T")[0]]);
435
+ writeKv(deps, pairs, { ok: true, account: result.data });
436
+ return { exitCode: 0 };
437
+ }
438
+ async function runAccountUpdate(input, deps) {
439
+ try {
440
+ await requireCredential({
441
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
442
+ env: deps.env,
443
+ store: deps.store
444
+ });
445
+ } catch {
446
+ return writeAuthError(deps);
447
+ }
448
+ const result = await deps.client.account.update({
449
+ displayName: input.displayName
450
+ });
451
+ if (result.error) return writeApiError(deps, result.error);
452
+ const data = result.data;
453
+ const pairs = [];
454
+ if (data?.id) pairs.push(["ID", String(data.id)]);
455
+ if (data?.displayName) pairs.push(["Name", String(data.displayName)]);
414
456
  writeKv(deps, pairs, { ok: true, account: result.data });
415
457
  return { exitCode: 0 };
416
458
  }
459
+ async function runAccountDelete(input, deps) {
460
+ if (!input.yes && input.interactive === false) {
461
+ return writeError(
462
+ deps,
463
+ "invalid_usage",
464
+ "Pass --yes to delete the account in non-interactive mode."
465
+ );
466
+ }
467
+ try {
468
+ await requireCredential({
469
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
470
+ env: deps.env,
471
+ store: deps.store
472
+ });
473
+ } catch {
474
+ return writeAuthError(deps);
475
+ }
476
+ const result = await deps.client.account.delete();
477
+ if (result.error) return writeApiError(deps, result.error);
478
+ writeHumanOrJson(deps, success("Account deleted"), {
479
+ ok: true,
480
+ deleted: true
481
+ });
482
+ return { exitCode: 0 };
483
+ }
417
484
 
418
485
  // src/commands/api-keys.ts
419
486
  var prompts = __toESM(require("@clack/prompts"), 1);
@@ -445,12 +512,12 @@ async function runApiKeysCreate(input, deps) {
445
512
  return writeAuthError(deps);
446
513
  }
447
514
  const result = await deps.client.apiKeys.create({ label: input.label });
448
- if (result.error) return writeApiErrorSimple(deps, result.error.message);
515
+ if (result.error) return writeApiError(deps, result.error);
449
516
  const data = result.data;
450
517
  const pairs = [];
451
518
  if (data.id) pairs.push(["ID", String(data.id)]);
452
519
  if (data.key) pairs.push(["Key", String(data.key)]);
453
- if (data.last4) pairs.push(["Last 4", String(data.last4)]);
520
+ if (data.keyLast4) pairs.push(["Last 4", String(data.keyLast4)]);
454
521
  writeKv(deps, pairs, { ok: true, api_key: result.data });
455
522
  return { exitCode: 0 };
456
523
  }
@@ -465,8 +532,8 @@ async function runApiKeysList(_input, deps) {
465
532
  return writeAuthError(deps);
466
533
  }
467
534
  const result = await deps.client.apiKeys.list();
468
- if (result.error) return writeApiErrorSimple(deps, result.error.message);
469
- const raw = unwrapListData(result.data, "apiKeys", "api_keys");
535
+ if (result.error) return writeApiError(deps, result.error);
536
+ const raw = unwrapListData(result.data);
470
537
  const items = Array.isArray(raw) ? raw : void 0;
471
538
  if (deps.outputMode === "human") {
472
539
  if (!items || items.length === 0) {
@@ -474,7 +541,7 @@ async function runApiKeysList(_input, deps) {
474
541
  } else {
475
542
  for (const item of items) {
476
543
  deps.stdout(
477
- `${item.id ?? ""} ${item.label ?? ""} ...${item.last4 ?? ""}
544
+ `${item.id ?? ""} ${item.label ?? ""} ...${item.keyLast4 ?? ""}
478
545
  `
479
546
  );
480
547
  }
@@ -510,7 +577,7 @@ async function runApiKeysDelete(keyId, input, deps) {
510
577
  return writeAuthError(deps);
511
578
  }
512
579
  const result = await deps.client.apiKeys.delete(keyId);
513
- if (result?.error) return writeApiErrorSimple(deps, result.error.message);
580
+ if (result?.error) return writeApiError(deps, result.error);
514
581
  writeHumanOrJson(deps, success(`Deleted ${keyId}`), {
515
582
  ok: true,
516
583
  deleted: true,
@@ -520,12 +587,9 @@ async function runApiKeysDelete(keyId, input, deps) {
520
587
  }
521
588
 
522
589
  // src/catalog.ts
523
- var COMMAND_CATALOG = [
524
- {
525
- name: "publish",
526
- description: "Publish an input and return a Dropthis URL.",
527
- arguments: ["input"],
528
- options: ["--json", "--url", "--dry-run", "--title", "--metadata"],
590
+ var COMMAND_ANNOTATIONS = {
591
+ publish: {
592
+ auth: "required",
529
593
  examples: [
530
594
  "dropthis ./file.html",
531
595
  "dropthis publish ./site --json",
@@ -533,69 +597,70 @@ var COMMAND_CATALOG = [
533
597
  "dropthis publish ./dist --dry-run"
534
598
  ]
535
599
  },
536
- {
537
- name: "deployments list <dropId>",
538
- description: "List deployments for a drop.",
539
- arguments: ["dropId"],
540
- options: ["--json", "--limit", "--cursor"],
600
+ drops: {
541
601
  auth: "required",
542
- examples: ["dropthis deployments list drop_abc --json"]
543
- },
544
- {
545
- name: "deployments get <dropId> <deploymentId>",
546
- description: "Get deployment details for a drop.",
547
- arguments: ["dropId", "deploymentId"],
548
- options: ["--json"],
549
- auth: "required",
550
- examples: ["dropthis deployments get drop_abc dep_abc --json"]
551
- },
552
- {
553
- name: "drops",
554
- description: "List, inspect, update, and delete drops.",
555
602
  examples: [
556
603
  "dropthis drops list --json",
557
604
  "dropthis drops update drop_abc ./site --json",
558
605
  "dropthis drops update drop_abc --title 'New Title' --dry-run"
559
606
  ]
560
607
  },
561
- {
562
- name: "api-keys",
563
- description: "Create, list, and delete API keys.",
608
+ deployments: { auth: "required" },
609
+ "deployments list": {
610
+ auth: "required",
611
+ examples: ["dropthis deployments list drop_abc --json"]
612
+ },
613
+ "deployments get": {
614
+ auth: "required",
615
+ examples: ["dropthis deployments get drop_abc dep_abc --json"]
616
+ },
617
+ "api-keys": {
618
+ auth: "required",
564
619
  examples: ["dropthis api-keys create --label CI --json"]
565
620
  },
566
- {
567
- name: "login",
568
- description: "Authenticate with email OTP and store an API key.",
621
+ login: {
569
622
  examples: [
570
623
  "dropthis login verify --email user@example.com --otp 123456 --json"
571
624
  ]
572
625
  },
573
- {
574
- name: "logout",
575
- description: "Remove stored local credentials.",
576
- examples: ["dropthis logout --json"]
577
- },
578
- {
579
- name: "whoami",
580
- description: "Show current auth source and credential metadata.",
581
- examples: ["dropthis whoami --json"]
582
- },
583
- {
584
- name: "account",
585
- description: "Get account details.",
586
- examples: ["dropthis account --json"]
626
+ logout: { examples: ["dropthis logout --json"] },
627
+ whoami: { auth: "required", examples: ["dropthis whoami --json"] },
628
+ account: { auth: "required", examples: ["dropthis account --json"] },
629
+ "account get": { auth: "required" },
630
+ "account update": {
631
+ auth: "required",
632
+ examples: ["dropthis account update --display-name 'Ada' --json"]
587
633
  },
588
- {
589
- name: "doctor",
590
- description: "Report local CLI diagnostics.",
591
- examples: ["dropthis doctor --json"]
634
+ "account delete": {
635
+ auth: "required",
636
+ examples: ["dropthis account delete --yes --json"]
592
637
  },
593
- {
594
- name: "commands",
595
- description: "Print this machine-readable command tree.",
596
- examples: ["dropthis commands --json"]
597
- }
598
- ];
638
+ doctor: { examples: ["dropthis doctor --json"] },
639
+ commands: { examples: ["dropthis commands --json"] }
640
+ };
641
+ function describeArgs(cmd) {
642
+ return cmd.registeredArguments.map((a) => a.name());
643
+ }
644
+ function describeOptions(cmd) {
645
+ return cmd.options.map((o) => o.long ?? o.short ?? o.flags);
646
+ }
647
+ function toEntry(cmd, path) {
648
+ const fullPath = [...path, cmd.name()];
649
+ const annotation = COMMAND_ANNOTATIONS[fullPath.join(" ")];
650
+ const subs = cmd.commands.map((sub) => toEntry(sub, fullPath));
651
+ return {
652
+ name: cmd.name(),
653
+ description: cmd.description(),
654
+ ...describeArgs(cmd).length ? { arguments: describeArgs(cmd) } : {},
655
+ ...describeOptions(cmd).length ? { options: describeOptions(cmd) } : {},
656
+ ...annotation?.auth ? { auth: annotation.auth } : {},
657
+ ...annotation?.examples ? { examples: annotation.examples } : {},
658
+ ...subs.length ? { subcommands: subs } : {}
659
+ };
660
+ }
661
+ function buildCatalog(program) {
662
+ return program.commands.map((cmd) => toEntry(cmd, []));
663
+ }
599
664
 
600
665
  // src/commands/commands.ts
601
666
  async function runCommands(_input, deps) {
@@ -603,7 +668,7 @@ async function runCommands(_input, deps) {
603
668
  `${JSON.stringify({
604
669
  ok: true,
605
670
  output: "JSON by default in CI, pipes, non-TTY, --json, or --quiet.",
606
- commands: COMMAND_CATALOG
671
+ commands: buildCatalog(deps.program)
607
672
  })}
608
673
  `
609
674
  );
@@ -640,7 +705,7 @@ async function runDeploymentsList(dropId, input, deps) {
640
705
  } else if (Array.isArray(items)) {
641
706
  for (const item of items) {
642
707
  deps.stdout(
643
- `${item.id ?? ""} rev ${item.revision ?? "?"} ${item.created_at ?? ""}
708
+ `${item.id ?? ""} rev ${item.revision ?? "?"} ${item.createdAt ?? ""}
644
709
  `
645
710
  );
646
711
  }
@@ -671,7 +736,7 @@ async function runDeploymentsGet(dropId, deploymentId, _input, deps) {
671
736
  if (data.id) pairs.push(["ID", String(data.id)]);
672
737
  if (data.revision !== void 0)
673
738
  pairs.push(["Revision", String(data.revision)]);
674
- if (data.created_at) pairs.push(["Created", String(data.created_at)]);
739
+ if (data.createdAt) pairs.push(["Created", String(data.createdAt)]);
675
740
  writeKv(deps, pairs, { ok: true, deployment: result.data });
676
741
  return { exitCode: 0 };
677
742
  }
@@ -689,13 +754,13 @@ async function runDoctor(_input, deps) {
689
754
  const authSource = credential?.source ?? "missing";
690
755
  const storageBackend = stored?.storage ?? "none";
691
756
  const pairs = [
692
- ["Version", "0.5.0"],
757
+ ["Version", "0.6.1"],
693
758
  ["Auth", authSource],
694
759
  ["Storage", storageBackend]
695
760
  ];
696
761
  writeKv(deps, pairs, {
697
762
  ok: true,
698
- version: "0.5.0",
763
+ version: "0.6.1",
699
764
  auth: { source: authSource },
700
765
  storage: { backend: storageBackend }
701
766
  });
@@ -719,7 +784,7 @@ async function runDropsList(input, deps) {
719
784
  ...input.cursor ? { cursor: input.cursor } : {}
720
785
  };
721
786
  const result = await deps.client.drops.list(params);
722
- if (result.error) return writeApiErrorSimple(deps, result.error.message);
787
+ if (result.error) return writeApiError(deps, result.error);
723
788
  const raw = unwrapListData(result.data, "drops");
724
789
  const items = Array.isArray(raw) ? raw : void 0;
725
790
  if (deps.outputMode === "human") {
@@ -749,7 +814,7 @@ async function runDropsGet(dropId, _input, deps) {
749
814
  return writeAuthError(deps);
750
815
  }
751
816
  const result = await deps.client.drops.get(dropId);
752
- if (result.error) return writeApiErrorSimple(deps, result.error.message);
817
+ if (result.error) return writeApiError(deps, result.error);
753
818
  const data = result.data;
754
819
  const pairs = [];
755
820
  if (data.id) pairs.push(["ID", String(data.id)]);
@@ -758,7 +823,7 @@ async function runDropsGet(dropId, _input, deps) {
758
823
  if (data.visibility) pairs.push(["Visibility", String(data.visibility)]);
759
824
  if (data.revision !== void 0)
760
825
  pairs.push(["Revision", String(data.revision)]);
761
- if (data.created_at) pairs.push(["Created", String(data.created_at)]);
826
+ if (data.createdAt) pairs.push(["Created", String(data.createdAt)]);
762
827
  if (data.accessible !== void 0)
763
828
  pairs.push(["Accessible", String(data.accessible)]);
764
829
  if (data.persistent !== void 0)
@@ -794,7 +859,7 @@ async function runDropsDelete(dropId, input, deps) {
794
859
  return writeAuthError(deps);
795
860
  }
796
861
  const result = await deps.client.drops.delete(dropId);
797
- if (result?.error) return writeApiErrorSimple(deps, result.error.message);
862
+ if (result?.error) return writeApiError(deps, result.error);
798
863
  writeHumanOrJson(deps, success(`Deleted ${dropId}`), {
799
864
  ok: true,
800
865
  deleted: true,
@@ -946,51 +1011,16 @@ async function runLogout(input, deps) {
946
1011
  return { exitCode: 0 };
947
1012
  }
948
1013
 
949
- // src/commands/publish.ts
950
- var import_promises3 = require("fs/promises");
951
- var import_node_path2 = require("path");
952
-
953
1014
  // src/drop-operations.ts
954
1015
  var import_node_crypto = require("crypto");
955
- var import_node_path = require("path");
956
1016
  var MAX_BUNDLE_FILE_COUNT = 200;
957
1017
  function isFileSystemError(error2) {
958
1018
  if (!(error2 instanceof Error)) return false;
959
1019
  const code = error2.code;
960
1020
  return code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELIMIT";
961
1021
  }
962
- function mimeForPath(filePath) {
963
- const ext = (0, import_node_path.extname)(filePath).toLowerCase();
964
- const types = {
965
- ".html": "text/html",
966
- ".htm": "text/html",
967
- ".css": "text/css",
968
- ".js": "text/javascript",
969
- ".mjs": "text/javascript",
970
- ".json": "application/json",
971
- ".xml": "application/xml",
972
- ".svg": "image/svg+xml",
973
- ".txt": "text/plain",
974
- ".md": "text/markdown",
975
- ".jpg": "image/jpeg",
976
- ".jpeg": "image/jpeg",
977
- ".png": "image/png",
978
- ".gif": "image/gif",
979
- ".webp": "image/webp",
980
- ".ico": "image/x-icon",
981
- ".avif": "image/avif",
982
- ".pdf": "application/pdf",
983
- ".zip": "application/zip",
984
- ".woff": "font/woff",
985
- ".woff2": "font/woff2",
986
- ".ttf": "font/ttf",
987
- ".mp4": "video/mp4",
988
- ".webm": "video/webm"
989
- };
990
- return types[ext] ?? "application/octet-stream";
991
- }
992
- function withIdempotencyKey(options, prefix) {
993
- return options.idempotencyKey ? options : { ...options, idempotencyKey: `cli_${prefix}_${(0, import_node_crypto.randomUUID)()}` };
1022
+ function withIdempotencyKey(options, _prefix) {
1023
+ return options.idempotencyKey ? options : { ...options, idempotencyKey: (0, import_node_crypto.randomUUID)() };
994
1024
  }
995
1025
  function validateManifestFileCount(count) {
996
1026
  if (count > MAX_BUNDLE_FILE_COUNT) {
@@ -1003,11 +1033,23 @@ function validateManifestFileCount(count) {
1003
1033
  }
1004
1034
  }
1005
1035
  function formatDryRunManifest(prepared, extra) {
1036
+ if (prepared.kind === "source") {
1037
+ return {
1038
+ ok: true,
1039
+ dryRun: true,
1040
+ kind: "source",
1041
+ ...extra,
1042
+ sourceUrl: prepared.sourceUrl,
1043
+ options: prepared.options,
1044
+ ...prepared.metadata ? { metadata: prepared.metadata } : {}
1045
+ };
1046
+ }
1006
1047
  const totalBytes = prepared.manifest.files.reduce(
1007
1048
  (sum, f) => sum + f.sizeBytes,
1008
1049
  0
1009
1050
  );
1010
1051
  return {
1052
+ ok: true,
1011
1053
  dryRun: true,
1012
1054
  kind: "staged",
1013
1055
  ...extra,
@@ -1020,15 +1062,6 @@ function formatDryRunManifest(prepared, extra) {
1020
1062
  totalBytes
1021
1063
  };
1022
1064
  }
1023
- function dryRunOptionsBody(options) {
1024
- const result = {};
1025
- if (options.title) result.title = options.title;
1026
- if (options.visibility) result.visibility = options.visibility;
1027
- if (options.password !== void 0) result.password = options.password;
1028
- if (options.noindex !== void 0) result.noindex = options.noindex;
1029
- if (options.expiresAt) result.expires_at = options.expiresAt;
1030
- return result;
1031
- }
1032
1065
 
1033
1066
  // src/options.ts
1034
1067
  var import_promises = require("fs/promises");
@@ -1107,22 +1140,6 @@ function shouldSpin(outputMode) {
1107
1140
  return outputMode === "human" && process.stderr.isTTY === true && !process.env.CI;
1108
1141
  }
1109
1142
 
1110
- // src/commands/json_body.ts
1111
- var import_promises2 = require("fs/promises");
1112
- async function readJsonObjectFile(path) {
1113
- let parsed;
1114
- try {
1115
- parsed = JSON.parse(await (0, import_promises2.readFile)(path, "utf8"));
1116
- } catch (error2) {
1117
- const message = error2 instanceof Error ? error2.message : "Unknown parse error";
1118
- throw new Error(`Invalid JSON body in ${path}: ${message}`);
1119
- }
1120
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1121
- throw new Error("JSON body must be an object.");
1122
- }
1123
- return parsed;
1124
- }
1125
-
1126
1143
  // src/commands/publish.ts
1127
1144
  async function runPublish(input, raw, deps) {
1128
1145
  let credential;
@@ -1156,17 +1173,12 @@ async function runPublish(input, raw, deps) {
1156
1173
  if (raw.dryRun) {
1157
1174
  return handlePublishDryRun(input, raw, deps, client);
1158
1175
  }
1159
- const singleInput = Array.isArray(input) ? input[0] : input;
1176
+ const hasInput = Array.isArray(input) ? input.length > 0 : input !== void 0;
1160
1177
  const spin = shouldSpin(deps.outputMode) ? createSpinner("Publishing\u2026", deps.stderr) : void 0;
1161
1178
  try {
1162
- if (raw.fromJson && singleInput) {
1163
- throw new Error("Use either <input> or --from-json, not both.");
1164
- }
1165
1179
  const options = withIdempotencyKey(await parseDropOptions(raw), "pub");
1166
- const jsonBody = raw.fromJson ? await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson) : void 0;
1167
- const idempotencyOpt = options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {};
1168
- const doPublish = (c) => jsonBody ? c.drops.createRaw(jsonBody, idempotencyOpt) : singleInput ? c.publish(singleInput, options) : (() => {
1169
- throw new Error("Publish requires <input> or --from-json.");
1180
+ const doPublish = (c) => hasInput ? c.publish(input, options) : (() => {
1181
+ throw new Error("Publish requires <input>.");
1170
1182
  })();
1171
1183
  const result = await doPublish(client);
1172
1184
  if (result.error) {
@@ -1226,33 +1238,18 @@ async function handlePublishDryRun(input, raw, deps, client) {
1226
1238
  );
1227
1239
  }
1228
1240
  try {
1229
- if (raw.fromJson) {
1230
- if (input)
1231
- throw new Error("Use either <input> or --from-json, not both.");
1232
- const body = await (deps.readJsonObject ?? readJsonObjectFile)(
1233
- raw.fromJson
1234
- );
1235
- deps.stdout(
1236
- `${JSON.stringify({ dryRun: true, kind: "raw_json", endpoint: "POST /drops", body }, null, 2)}
1237
- `
1238
- );
1239
- return { exitCode: 0 };
1240
- }
1241
1241
  if (!input || Array.isArray(input) && input.length === 0) {
1242
- throw new Error("Publish requires <input> or --from-json.");
1242
+ throw new Error("Publish requires <input>.");
1243
1243
  }
1244
1244
  const options = await parseDropOptions(raw);
1245
- if (Array.isArray(input)) {
1246
- return dryRunMultiFile(input, options, deps);
1247
- }
1248
1245
  if (!client.prepare) {
1249
1246
  throw new Error("Client does not support dry-run.");
1250
1247
  }
1251
1248
  const prepared = await client.prepare(input, options);
1252
1249
  const output = formatDryRunManifest(prepared);
1253
- validateManifestFileCount(
1254
- output.manifest.files.length
1255
- );
1250
+ if (prepared.kind === "staged") {
1251
+ validateManifestFileCount(prepared.manifest.files.length);
1252
+ }
1256
1253
  deps.stdout(`${JSON.stringify(output, null, 2)}
1257
1254
  `);
1258
1255
  return { exitCode: 0 };
@@ -1262,46 +1259,6 @@ async function handlePublishDryRun(input, raw, deps, client) {
1262
1259
  return writeError(deps, code, message);
1263
1260
  }
1264
1261
  }
1265
- async function dryRunMultiFile(inputs, options, deps) {
1266
- const files = [];
1267
- for (const filePath of inputs) {
1268
- let info;
1269
- try {
1270
- info = await (0, import_promises3.stat)(filePath);
1271
- } catch {
1272
- return writeError(
1273
- deps,
1274
- "local_input_error",
1275
- `File not found: ${filePath}`
1276
- );
1277
- }
1278
- if (info.isDirectory()) {
1279
- return writeError(
1280
- deps,
1281
- "local_input_error",
1282
- `Cannot mix directories with multiple file arguments. Use a single folder instead: dropthis publish ${filePath}`
1283
- );
1284
- }
1285
- files.push({
1286
- path: (0, import_node_path2.basename)(filePath),
1287
- contentType: mimeForPath(filePath),
1288
- sizeBytes: info.size
1289
- });
1290
- }
1291
- validateManifestFileCount(files.length);
1292
- const entry = options.entry ?? files.find((f) => f.path === "index.html")?.path ?? files[0]?.path ?? null;
1293
- const totalBytes = files.reduce((sum, f) => sum + f.sizeBytes, 0);
1294
- const output = {
1295
- dryRun: true,
1296
- kind: "staged",
1297
- manifest: { files, entry },
1298
- options: dryRunOptionsBody(options),
1299
- totalBytes
1300
- };
1301
- deps.stdout(`${JSON.stringify(output, null, 2)}
1302
- `);
1303
- return { exitCode: 0 };
1304
- }
1305
1262
 
1306
1263
  // src/commands/update.ts
1307
1264
  var prompts4 = __toESM(require("@clack/prompts"), 1);
@@ -1323,12 +1280,9 @@ async function runUpdate(target, input, raw, deps) {
1323
1280
  let spin;
1324
1281
  try {
1325
1282
  assertDropId(target);
1326
- if (raw.fromJson && input) {
1327
- throw new Error("Use either [input] or --from-json, not both.");
1328
- }
1329
1283
  let updateInput = input;
1330
1284
  let parsed = await parseDropOptions(raw);
1331
- if (!updateInput && !raw.fromJson && !hasUpdateFields(parsed)) {
1285
+ if (!updateInput && !hasUpdateFields(parsed)) {
1332
1286
  if (shouldPromptForUpdate(deps)) {
1333
1287
  const prompted = await promptForUpdate(target, deps.prompts ?? prompts4);
1334
1288
  if (!prompted) return { exitCode: 0 };
@@ -1345,27 +1299,14 @@ async function runUpdate(target, input, raw, deps) {
1345
1299
  }
1346
1300
  const revisionOptions = raw.ifRevision !== void 0 ? { ifRevision: Number(raw.ifRevision) } : {};
1347
1301
  spin = shouldSpin(deps.outputMode) ? createSpinner("Updating\u2026", deps.stderr) : void 0;
1348
- const result = raw.fromJson ? await deps.client.deployments.createRaw(
1349
- target,
1350
- await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson),
1351
- withIdempotencyKey(
1352
- {
1353
- ...revisionOptions,
1354
- ...parsed.idempotencyKey ? { idempotencyKey: parsed.idempotencyKey } : {}
1355
- },
1356
- "upd"
1357
- )
1358
- ) : updateInput ? await deps.client.update(
1302
+ const result = updateInput ? await deps.client.deploy(
1359
1303
  target,
1360
1304
  updateInput,
1361
- withIdempotencyKey(
1362
- {
1363
- ...parsed,
1364
- ...revisionOptions
1365
- },
1366
- "upd"
1367
- )
1368
- ) : await deps.client.update(target, parsed, revisionOptions);
1305
+ withIdempotencyKey({ ...parsed, ...revisionOptions }, "upd")
1306
+ ) : await deps.client.update(
1307
+ target,
1308
+ withIdempotencyKey({ ...parsed, ...revisionOptions }, "upd")
1309
+ );
1369
1310
  if (result.error) {
1370
1311
  spin?.fail();
1371
1312
  return writeApiError(deps, result.error);
@@ -1549,18 +1490,6 @@ async function handleUpdateDryRun(target, input, raw, deps) {
1549
1490
  }
1550
1491
  try {
1551
1492
  assertDropId(target);
1552
- if (raw.fromJson) {
1553
- if (input)
1554
- throw new Error("Use either [input] or --from-json, not both.");
1555
- const body = await (deps.readJsonObject ?? readJsonObjectFile)(
1556
- raw.fromJson
1557
- );
1558
- deps.stdout(
1559
- `${JSON.stringify({ dryRun: true, kind: "raw_json", endpoint: `POST /drops/${target}/deployments`, body }, null, 2)}
1560
- `
1561
- );
1562
- return { exitCode: 0 };
1563
- }
1564
1493
  const options = await parseDropOptions(raw);
1565
1494
  if (!input) {
1566
1495
  const fields = {};
@@ -1572,7 +1501,7 @@ async function handleUpdateDryRun(target, input, raw, deps) {
1572
1501
  if (options.expiresAt) fields.expires_at = options.expiresAt;
1573
1502
  if (options.metadata) fields.metadata = options.metadata;
1574
1503
  deps.stdout(
1575
- `${JSON.stringify({ dryRun: true, kind: "options_update", target, fields }, null, 2)}
1504
+ `${JSON.stringify({ ok: true, dryRun: true, kind: "options_update", target, fields }, null, 2)}
1576
1505
  `
1577
1506
  );
1578
1507
  return { exitCode: 0 };
@@ -1582,7 +1511,9 @@ async function handleUpdateDryRun(target, input, raw, deps) {
1582
1511
  }
1583
1512
  const prepared = await deps.client.prepare(input, options);
1584
1513
  const output = formatDryRunManifest(prepared, { target });
1585
- validateManifestFileCount(prepared.manifest.files.length);
1514
+ if (prepared.kind === "staged") {
1515
+ validateManifestFileCount(prepared.manifest.files.length);
1516
+ }
1586
1517
  deps.stdout(`${JSON.stringify(output, null, 2)}
1587
1518
  `);
1588
1519
  return { exitCode: 0 };
@@ -1603,6 +1534,7 @@ function assertDropId(target) {
1603
1534
  // src/commands/whoami.ts
1604
1535
  async function runWhoami(_input, deps) {
1605
1536
  const credential = await resolveCredential({
1537
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
1606
1538
  env: deps.env,
1607
1539
  store: deps.store
1608
1540
  });
@@ -1613,21 +1545,34 @@ async function runWhoami(_input, deps) {
1613
1545
  });
1614
1546
  return { exitCode: 0 };
1615
1547
  }
1548
+ const result = await deps.client.account.get();
1549
+ if (result.error) {
1550
+ writeHumanOrJson(
1551
+ deps,
1552
+ `Not authenticated: ${result.error.message}. Run dropthis login.`,
1553
+ { ok: true, authenticated: false, reason: result.error.message }
1554
+ );
1555
+ return { exitCode: 0 };
1556
+ }
1557
+ const account = result.data ?? {};
1616
1558
  const masked = maskKey(credential.apiKey);
1559
+ const last4 = credential.keyLast4 ?? credential.apiKey.slice(-4);
1617
1560
  const pairs = [];
1618
- if (credential.email) pairs.push(["Email", credential.email]);
1561
+ if (credential.email ?? account.email)
1562
+ pairs.push(["Email", String(credential.email ?? account.email)]);
1619
1563
  pairs.push(["Key", masked]);
1620
1564
  pairs.push(["Source", credential.source]);
1621
- if (credential.accountId) pairs.push(["Account", credential.accountId]);
1565
+ if (credential.accountId ?? account.id)
1566
+ pairs.push(["Account", String(credential.accountId ?? account.id)]);
1622
1567
  const jsonEnvelope = {
1623
1568
  ok: true,
1624
1569
  authenticated: true,
1625
1570
  source: credential.source,
1626
1571
  masked_key: masked,
1627
1572
  ...credential.keyId ? { key_id: credential.keyId } : {},
1628
- ...credential.keyLast4 ? { key_last4: credential.keyLast4 } : {},
1629
- ...credential.accountId ? { account_id: credential.accountId } : {},
1630
- ...credential.email ? { email: credential.email } : {}
1573
+ ...last4 ? { key_last4: last4 } : {},
1574
+ ...credential.accountId ?? account.id ? { account_id: String(credential.accountId ?? account.id) } : {},
1575
+ ...credential.email ?? account.email ? { email: String(credential.email ?? account.email) } : {}
1631
1576
  };
1632
1577
  writeKv(deps, pairs, jsonEnvelope);
1633
1578
  return { exitCode: 0 };
@@ -1660,9 +1605,9 @@ function createContext(input) {
1660
1605
  }
1661
1606
 
1662
1607
  // src/storage.ts
1663
- var import_promises4 = require("fs/promises");
1608
+ var import_promises2 = require("fs/promises");
1664
1609
  var import_node_os = require("os");
1665
- var import_node_path3 = require("path");
1610
+ var import_node_path = require("path");
1666
1611
  var import_keyring = require("@napi-rs/keyring");
1667
1612
  var MemoryCredentialStore = class {
1668
1613
  credential = null;
@@ -1694,7 +1639,7 @@ var InsecureFileCredentialStore = class {
1694
1639
  });
1695
1640
  }
1696
1641
  async clear() {
1697
- await (0, import_promises4.rm)(this.filePath, { force: true });
1642
+ await (0, import_promises2.rm)(this.filePath, { force: true });
1698
1643
  }
1699
1644
  };
1700
1645
  var KeyringCredentialStore = class {
@@ -1721,7 +1666,7 @@ var KeyringCredentialStore = class {
1721
1666
  }
1722
1667
  async clear() {
1723
1668
  await this.keyring.deletePassword();
1724
- await (0, import_promises4.rm)(this.filePath, { force: true });
1669
+ await (0, import_promises2.rm)(this.filePath, { force: true });
1725
1670
  }
1726
1671
  };
1727
1672
  function createCredentialStore(options = {}) {
@@ -1739,14 +1684,14 @@ function defaultKeyringAdapter() {
1739
1684
  };
1740
1685
  }
1741
1686
  function credentialPath(configDir) {
1742
- return (0, import_node_path3.join)(
1743
- configDir ?? (0, import_node_path3.join)((0, import_node_os.homedir)(), ".config", "dropthis"),
1687
+ return (0, import_node_path.join)(
1688
+ configDir ?? (0, import_node_path.join)((0, import_node_os.homedir)(), ".config", "dropthis"),
1744
1689
  "credentials.json"
1745
1690
  );
1746
1691
  }
1747
1692
  async function readJsonFile(path) {
1748
1693
  try {
1749
- return JSON.parse(await (0, import_promises4.readFile)(path, "utf8"));
1694
+ return JSON.parse(await (0, import_promises2.readFile)(path, "utf8"));
1750
1695
  } catch (error2) {
1751
1696
  if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
1752
1697
  return null;
@@ -1755,20 +1700,34 @@ async function readJsonFile(path) {
1755
1700
  }
1756
1701
  }
1757
1702
  async function writeCredentialFile(path, credential) {
1758
- await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(path), { recursive: true });
1703
+ await (0, import_promises2.mkdir)((0, import_node_path.dirname)(path), { recursive: true });
1759
1704
  const tmpPath = `${path}.${process.pid}.tmp`;
1760
- await (0, import_promises4.writeFile)(tmpPath, `${JSON.stringify(credential, null, 2)}
1705
+ await (0, import_promises2.writeFile)(tmpPath, `${JSON.stringify(credential, null, 2)}
1761
1706
  `, {
1762
1707
  mode: 384
1763
1708
  });
1764
- await (0, import_promises4.chmod)(tmpPath, 384);
1765
- await (0, import_promises4.rename)(tmpPath, path);
1709
+ await (0, import_promises2.chmod)(tmpPath, 384);
1710
+ await (0, import_promises2.rename)(tmpPath, path);
1766
1711
  }
1767
1712
 
1768
1713
  // src/program.ts
1769
1714
  function buildProgram(options = {}) {
1770
1715
  const program = new import_commander.Command();
1771
1716
  const store = options.store ?? new MemoryCredentialStore();
1717
+ const writeErr = options.stderr ?? ((value) => process.stderr.write(value));
1718
+ const jsonModeForOutput = () => {
1719
+ const argv = program.rawArgs ?? process.argv;
1720
+ return argv.includes("--json") || argv.includes("--quiet") || argv.includes("-q") || options.env?.CI === "true" || options.env === void 0 && process.env.CI === "true" || options.stdoutIsTTY === false || options.stdoutIsTTY === void 0 && process.stdout.isTTY !== true;
1721
+ };
1722
+ program.exitOverride();
1723
+ program.configureOutput({
1724
+ writeErr,
1725
+ // In JSON mode, suppress commander's default plain-text error so the
1726
+ // single JSON envelope written by cli.ts's catch is the only output.
1727
+ outputError: (str, write) => {
1728
+ if (!jsonModeForOutput()) write(str);
1729
+ }
1730
+ });
1772
1731
  program.name("dropthis").description(
1773
1732
  [
1774
1733
  import_picocolors4.default.dim("Publish anything online and get a URL back."),
@@ -1777,7 +1736,7 @@ function buildProgram(options = {}) {
1777
1736
  ` ${import_picocolors4.default.cyan("dropthis login")}`,
1778
1737
  ` ${import_picocolors4.default.cyan("dropthis publish ./dist")}`
1779
1738
  ].join("\n")
1780
- ).version("0.5.0").configureHelp({
1739
+ ).version("0.6.1").configureHelp({
1781
1740
  subcommandTerm(cmd) {
1782
1741
  const args = cmd.registeredArguments.map((a) => {
1783
1742
  const name = a.name();
@@ -1807,12 +1766,15 @@ function buildProgram(options = {}) {
1807
1766
  ).option(
1808
1767
  "--metadata <json>",
1809
1768
  `Attach JSON key-value pairs, e.g. '{"source":"ci"}'`
1810
- ).option("--metadata-file <path>", "Read metadata JSON from a file").option("--path <name>", "Set filename when publishing from stdin").option("--url", "Print only the published URL (no JSON envelope)").option("--dry-run", "Show what would be published without publishing").option("--json", "Force JSON output").option("--from-json <path>", "Send raw API request body from a JSON file").option(
1769
+ ).option("--metadata-file <path>", "Read metadata JSON from a file").option("--path <name>", "Set filename when publishing from stdin").option("--url", "Print only the published URL (no JSON envelope)").option(
1770
+ "--dry-run",
1771
+ "Show what would be published without publishing (JSON; cannot combine with --url)"
1772
+ ).option("--json", "Force JSON output").option(
1811
1773
  "--idempotency-key <key>",
1812
1774
  "Prevent duplicate publishes on retry (auto-generated)"
1813
1775
  ).action(async (inputs, commandOptions) => {
1814
1776
  const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
1815
- if (inputs.length === 0 && stdinIsTTY && !commandOptions.fromJson) {
1777
+ if (inputs.length === 0 && stdinIsTTY) {
1816
1778
  program.outputHelp();
1817
1779
  return;
1818
1780
  }
@@ -1825,15 +1787,11 @@ function buildProgram(options = {}) {
1825
1787
  );
1826
1788
  let publishInput;
1827
1789
  try {
1828
- if (commandOptions.dryRun && inputs.length > 1) {
1829
- publishInput = inputs;
1830
- } else {
1831
- publishInput = await resolvePublishInputs(
1832
- inputs,
1833
- options.stdin,
1834
- stdinIsTTY
1835
- );
1836
- }
1790
+ publishInput = await resolvePublishInputs(
1791
+ inputs,
1792
+ options.stdin,
1793
+ stdinIsTTY
1794
+ );
1837
1795
  } catch (err) {
1838
1796
  const msg = err instanceof Error ? err.message : "Invalid input.";
1839
1797
  process.exitCode = writeError(deps, "local_input_error", msg).exitCode;
@@ -1876,10 +1834,10 @@ function buildProgram(options = {}) {
1876
1834
  "--if-revision <n>",
1877
1835
  "Fail if current revision doesn't match (optimistic lock)",
1878
1836
  parseInteger
1879
- ).option("--url", "Print only the published URL (no JSON envelope)").option("--dry-run", "Show what would be updated without updating").option("--json", "Force JSON output").option(
1880
- "--from-json <path>",
1881
- "Send raw API deployment body from a JSON file"
1882
- ).option(
1837
+ ).option("--url", "Print only the published URL (no JSON envelope)").option(
1838
+ "--dry-run",
1839
+ "Show what would be updated without updating (JSON; cannot combine with --url)"
1840
+ ).option("--json", "Force JSON output").option(
1883
1841
  "--idempotency-key <key>",
1884
1842
  "Prevent duplicate updates on retry (auto-generated)"
1885
1843
  ).action(
@@ -2045,11 +2003,36 @@ function buildProgram(options = {}) {
2045
2003
  const result = await runWhoami(commandOptions, deps);
2046
2004
  process.exitCode = result.exitCode;
2047
2005
  });
2048
- program.command("account").description("Show account details").option("--json", "Force JSON output").action(async (commandOptions) => {
2006
+ const account = program.command("account").description("Show or manage your account");
2007
+ account.command("get", { isDefault: true }).description("Show account details").option("--json", "Force JSON output").action(async (commandOptions) => {
2049
2008
  const deps = await commandDeps(program, options, store, commandOptions);
2050
2009
  const result = await runAccountGet(commandOptions, deps);
2051
2010
  process.exitCode = result.exitCode;
2052
2011
  });
2012
+ account.command("update").description("Update account display name").requiredOption("--display-name <name>", "Set the display name").option("--json", "Force JSON output").action(async (commandOptions) => {
2013
+ const deps = await commandDeps(program, options, store, commandOptions);
2014
+ const result = await runAccountUpdate(
2015
+ {
2016
+ displayName: commandOptions.displayName,
2017
+ ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
2018
+ },
2019
+ deps
2020
+ );
2021
+ process.exitCode = result.exitCode;
2022
+ });
2023
+ account.command("delete").description("Delete your account").option("--yes", "Confirm deletion").option("--json", "Force JSON output").action(async (commandOptions) => {
2024
+ const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
2025
+ const global = globalOptions(program, commandOptions);
2026
+ const deps = await commandDeps(program, options, store, commandOptions);
2027
+ const result = await runAccountDelete(
2028
+ {
2029
+ ...commandOptions,
2030
+ interactive: isInteractive(stdinIsTTY, deps.env, global)
2031
+ },
2032
+ deps
2033
+ );
2034
+ process.exitCode = result.exitCode;
2035
+ });
2053
2036
  program.command("doctor").description("Report CLI diagnostics").option("--json", "Force JSON output").action(async (commandOptions) => {
2054
2037
  const deps = await commandDeps(
2055
2038
  program,
@@ -2067,7 +2050,7 @@ function buildProgram(options = {}) {
2067
2050
  store,
2068
2051
  commandOptions
2069
2052
  );
2070
- const result = await runCommands(commandOptions, deps);
2053
+ const result = await runCommands(commandOptions, { ...deps, program });
2071
2054
  process.exitCode = result.exitCode;
2072
2055
  });
2073
2056
  return program;
@@ -2168,7 +2151,6 @@ function toDropOptions(options) {
2168
2151
  ...options.quiet !== void 0 ? { quiet: options.quiet } : {},
2169
2152
  ...options.url !== void 0 ? { url: options.url } : {},
2170
2153
  ...options.ifRevision !== void 0 ? { ifRevision: options.ifRevision } : {},
2171
- ...options.fromJson ? { fromJson: options.fromJson } : {},
2172
2154
  ...options.dryRun ? { dryRun: options.dryRun } : {}
2173
2155
  };
2174
2156
  }
@@ -2196,44 +2178,12 @@ async function resolvePublishInputs(inputs, stdin, stdinIsTTY) {
2196
2178
  }
2197
2179
  return inputs[0];
2198
2180
  }
2199
- const {
2200
- mkdtemp,
2201
- copyFile,
2202
- writeFile: writeFile2,
2203
- stat: fsStat
2204
- } = await import("fs/promises");
2205
- const { join: join2, basename: pathBasename } = await import("path");
2206
- const { tmpdir } = await import("os");
2207
- const tmpDir = await mkdtemp(join2(tmpdir(), "dropthis-multi-"));
2208
- const resolved = [];
2209
- for (const input of inputs) {
2210
- if (input === "-") {
2211
- const buf = await readStdinBytes(stdin ?? process.stdin);
2212
- const tmpPath = join2(tmpDir, `stdin-${Date.now()}`);
2213
- await writeFile2(tmpPath, buf);
2214
- resolved.push(tmpPath);
2215
- } else {
2216
- resolved.push(input);
2217
- }
2218
- }
2219
- const names = resolved.map((f) => pathBasename(f));
2220
- const dupes = names.filter((n, i) => names.indexOf(n) !== i);
2221
- if (dupes.length > 0) {
2181
+ if (inputs.includes("-")) {
2222
2182
  throw new Error(
2223
- `Duplicate file names: ${[...new Set(dupes)].join(", ")}. Rename files to avoid collisions.`
2183
+ "Cannot read stdin (-) alongside multiple file arguments. Pass file paths only."
2224
2184
  );
2225
2185
  }
2226
- for (const filePath of resolved) {
2227
- const info = await fsStat(filePath);
2228
- if (info.isDirectory()) {
2229
- throw new Error(
2230
- `Cannot mix directories with multiple file arguments. Use a single folder instead: dropthis publish ${filePath}`
2231
- );
2232
- }
2233
- const destName = pathBasename(filePath);
2234
- await copyFile(filePath, join2(tmpDir, destName));
2235
- }
2236
- return tmpDir;
2186
+ return inputs;
2237
2187
  }
2238
2188
  async function readStdinBytes(stdin) {
2239
2189
  const chunks = [];
@@ -2246,8 +2196,42 @@ async function readStdinBytes(stdin) {
2246
2196
  }
2247
2197
 
2248
2198
  // src/cli.ts
2199
+ function writeUsageErrorEnvelope(input) {
2200
+ if (input.json) {
2201
+ input.stderr(
2202
+ `${JSON.stringify(errorEnvelope("usage_error", input.message))}
2203
+ `
2204
+ );
2205
+ } else {
2206
+ input.stderr(`${input.message}
2207
+ `);
2208
+ }
2209
+ return 2;
2210
+ }
2211
+ function isCommanderError(err) {
2212
+ return typeof err === "object" && err !== null && typeof err.code === "string" && err.code.startsWith("commander.");
2213
+ }
2214
+ function wantsJson(argv) {
2215
+ return argv.includes("--json") || argv.includes("--quiet") || argv.includes("-q") || process.env.CI === "true" || process.stdout.isTTY !== true;
2216
+ }
2249
2217
  buildProgram({ store: createCredentialStore() }).parseAsync(process.argv).catch((error2) => {
2218
+ if (isCommanderError(error2)) {
2219
+ if (error2.code === "commander.help" || error2.code === "commander.helpDisplayed" || error2.code === "commander.version") {
2220
+ process.exitCode = 0;
2221
+ return;
2222
+ }
2223
+ process.exitCode = writeUsageErrorEnvelope({
2224
+ message: error2.message,
2225
+ json: wantsJson(process.argv.slice(2)),
2226
+ stderr: (value) => process.stderr.write(value)
2227
+ });
2228
+ return;
2229
+ }
2250
2230
  console.error(error2);
2251
2231
  process.exitCode = 1;
2252
2232
  });
2233
+ // Annotate the CommonJS export names for ESM import in node:
2234
+ 0 && (module.exports = {
2235
+ writeUsageErrorEnvelope
2236
+ });
2253
2237
  //# sourceMappingURL=cli.cjs.map