@dropthis/cli 0.1.5

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.
Files changed (236) hide show
  1. package/README.md +171 -0
  2. package/dist/cli.cjs +1489 -0
  3. package/dist/cli.cjs.map +1 -0
  4. package/node_modules/@dropthis/node/README.md +90 -0
  5. package/node_modules/@dropthis/node/dist/index.cjs +1010 -0
  6. package/node_modules/@dropthis/node/dist/index.cjs.map +1 -0
  7. package/node_modules/@dropthis/node/dist/index.d.cts +408 -0
  8. package/node_modules/@dropthis/node/dist/index.d.ts +408 -0
  9. package/node_modules/@dropthis/node/dist/index.mjs +969 -0
  10. package/node_modules/@dropthis/node/dist/index.mjs.map +1 -0
  11. package/node_modules/@dropthis/node/package.json +55 -0
  12. package/node_modules/@nodelib/fs.scandir/LICENSE +21 -0
  13. package/node_modules/@nodelib/fs.scandir/README.md +171 -0
  14. package/node_modules/@nodelib/fs.scandir/out/adapters/fs.d.ts +20 -0
  15. package/node_modules/@nodelib/fs.scandir/out/adapters/fs.js +19 -0
  16. package/node_modules/@nodelib/fs.scandir/out/constants.d.ts +4 -0
  17. package/node_modules/@nodelib/fs.scandir/out/constants.js +17 -0
  18. package/node_modules/@nodelib/fs.scandir/out/index.d.ts +12 -0
  19. package/node_modules/@nodelib/fs.scandir/out/index.js +26 -0
  20. package/node_modules/@nodelib/fs.scandir/out/providers/async.d.ts +7 -0
  21. package/node_modules/@nodelib/fs.scandir/out/providers/async.js +104 -0
  22. package/node_modules/@nodelib/fs.scandir/out/providers/common.d.ts +1 -0
  23. package/node_modules/@nodelib/fs.scandir/out/providers/common.js +13 -0
  24. package/node_modules/@nodelib/fs.scandir/out/providers/sync.d.ts +5 -0
  25. package/node_modules/@nodelib/fs.scandir/out/providers/sync.js +54 -0
  26. package/node_modules/@nodelib/fs.scandir/out/settings.d.ts +20 -0
  27. package/node_modules/@nodelib/fs.scandir/out/settings.js +24 -0
  28. package/node_modules/@nodelib/fs.scandir/out/types/index.d.ts +20 -0
  29. package/node_modules/@nodelib/fs.scandir/out/types/index.js +2 -0
  30. package/node_modules/@nodelib/fs.scandir/out/utils/fs.d.ts +2 -0
  31. package/node_modules/@nodelib/fs.scandir/out/utils/fs.js +19 -0
  32. package/node_modules/@nodelib/fs.scandir/out/utils/index.d.ts +2 -0
  33. package/node_modules/@nodelib/fs.scandir/out/utils/index.js +5 -0
  34. package/node_modules/@nodelib/fs.scandir/package.json +44 -0
  35. package/node_modules/@nodelib/fs.stat/LICENSE +21 -0
  36. package/node_modules/@nodelib/fs.stat/README.md +126 -0
  37. package/node_modules/@nodelib/fs.stat/out/adapters/fs.d.ts +13 -0
  38. package/node_modules/@nodelib/fs.stat/out/adapters/fs.js +17 -0
  39. package/node_modules/@nodelib/fs.stat/out/index.d.ts +12 -0
  40. package/node_modules/@nodelib/fs.stat/out/index.js +26 -0
  41. package/node_modules/@nodelib/fs.stat/out/providers/async.d.ts +4 -0
  42. package/node_modules/@nodelib/fs.stat/out/providers/async.js +36 -0
  43. package/node_modules/@nodelib/fs.stat/out/providers/sync.d.ts +3 -0
  44. package/node_modules/@nodelib/fs.stat/out/providers/sync.js +23 -0
  45. package/node_modules/@nodelib/fs.stat/out/settings.d.ts +16 -0
  46. package/node_modules/@nodelib/fs.stat/out/settings.js +16 -0
  47. package/node_modules/@nodelib/fs.stat/out/types/index.d.ts +4 -0
  48. package/node_modules/@nodelib/fs.stat/out/types/index.js +2 -0
  49. package/node_modules/@nodelib/fs.stat/package.json +37 -0
  50. package/node_modules/@nodelib/fs.walk/LICENSE +21 -0
  51. package/node_modules/@nodelib/fs.walk/README.md +215 -0
  52. package/node_modules/@nodelib/fs.walk/out/index.d.ts +14 -0
  53. package/node_modules/@nodelib/fs.walk/out/index.js +34 -0
  54. package/node_modules/@nodelib/fs.walk/out/providers/async.d.ts +12 -0
  55. package/node_modules/@nodelib/fs.walk/out/providers/async.js +30 -0
  56. package/node_modules/@nodelib/fs.walk/out/providers/index.d.ts +4 -0
  57. package/node_modules/@nodelib/fs.walk/out/providers/index.js +9 -0
  58. package/node_modules/@nodelib/fs.walk/out/providers/stream.d.ts +12 -0
  59. package/node_modules/@nodelib/fs.walk/out/providers/stream.js +34 -0
  60. package/node_modules/@nodelib/fs.walk/out/providers/sync.d.ts +10 -0
  61. package/node_modules/@nodelib/fs.walk/out/providers/sync.js +14 -0
  62. package/node_modules/@nodelib/fs.walk/out/readers/async.d.ts +30 -0
  63. package/node_modules/@nodelib/fs.walk/out/readers/async.js +97 -0
  64. package/node_modules/@nodelib/fs.walk/out/readers/common.d.ts +7 -0
  65. package/node_modules/@nodelib/fs.walk/out/readers/common.js +31 -0
  66. package/node_modules/@nodelib/fs.walk/out/readers/reader.d.ts +6 -0
  67. package/node_modules/@nodelib/fs.walk/out/readers/reader.js +11 -0
  68. package/node_modules/@nodelib/fs.walk/out/readers/sync.d.ts +15 -0
  69. package/node_modules/@nodelib/fs.walk/out/readers/sync.js +59 -0
  70. package/node_modules/@nodelib/fs.walk/out/settings.d.ts +30 -0
  71. package/node_modules/@nodelib/fs.walk/out/settings.js +26 -0
  72. package/node_modules/@nodelib/fs.walk/out/types/index.d.ts +8 -0
  73. package/node_modules/@nodelib/fs.walk/out/types/index.js +2 -0
  74. package/node_modules/@nodelib/fs.walk/package.json +44 -0
  75. package/node_modules/braces/LICENSE +21 -0
  76. package/node_modules/braces/README.md +586 -0
  77. package/node_modules/braces/index.js +170 -0
  78. package/node_modules/braces/lib/compile.js +60 -0
  79. package/node_modules/braces/lib/constants.js +57 -0
  80. package/node_modules/braces/lib/expand.js +113 -0
  81. package/node_modules/braces/lib/parse.js +331 -0
  82. package/node_modules/braces/lib/stringify.js +32 -0
  83. package/node_modules/braces/lib/utils.js +122 -0
  84. package/node_modules/braces/package.json +77 -0
  85. package/node_modules/fast-glob/LICENSE +21 -0
  86. package/node_modules/fast-glob/README.md +830 -0
  87. package/node_modules/fast-glob/out/index.d.ts +40 -0
  88. package/node_modules/fast-glob/out/index.js +102 -0
  89. package/node_modules/fast-glob/out/managers/tasks.d.ts +22 -0
  90. package/node_modules/fast-glob/out/managers/tasks.js +110 -0
  91. package/node_modules/fast-glob/out/providers/async.d.ts +9 -0
  92. package/node_modules/fast-glob/out/providers/async.js +23 -0
  93. package/node_modules/fast-glob/out/providers/filters/deep.d.ts +16 -0
  94. package/node_modules/fast-glob/out/providers/filters/deep.js +62 -0
  95. package/node_modules/fast-glob/out/providers/filters/entry.d.ts +17 -0
  96. package/node_modules/fast-glob/out/providers/filters/entry.js +85 -0
  97. package/node_modules/fast-glob/out/providers/filters/error.d.ts +8 -0
  98. package/node_modules/fast-glob/out/providers/filters/error.js +15 -0
  99. package/node_modules/fast-glob/out/providers/matchers/matcher.d.ts +33 -0
  100. package/node_modules/fast-glob/out/providers/matchers/matcher.js +45 -0
  101. package/node_modules/fast-glob/out/providers/matchers/partial.d.ts +4 -0
  102. package/node_modules/fast-glob/out/providers/matchers/partial.js +38 -0
  103. package/node_modules/fast-glob/out/providers/provider.d.ts +19 -0
  104. package/node_modules/fast-glob/out/providers/provider.js +48 -0
  105. package/node_modules/fast-glob/out/providers/stream.d.ts +11 -0
  106. package/node_modules/fast-glob/out/providers/stream.js +31 -0
  107. package/node_modules/fast-glob/out/providers/sync.d.ts +9 -0
  108. package/node_modules/fast-glob/out/providers/sync.js +23 -0
  109. package/node_modules/fast-glob/out/providers/transformers/entry.d.ts +8 -0
  110. package/node_modules/fast-glob/out/providers/transformers/entry.js +26 -0
  111. package/node_modules/fast-glob/out/readers/async.d.ts +10 -0
  112. package/node_modules/fast-glob/out/readers/async.js +35 -0
  113. package/node_modules/fast-glob/out/readers/reader.d.ts +15 -0
  114. package/node_modules/fast-glob/out/readers/reader.js +33 -0
  115. package/node_modules/fast-glob/out/readers/stream.d.ts +14 -0
  116. package/node_modules/fast-glob/out/readers/stream.js +55 -0
  117. package/node_modules/fast-glob/out/readers/sync.d.ts +12 -0
  118. package/node_modules/fast-glob/out/readers/sync.js +43 -0
  119. package/node_modules/fast-glob/out/settings.d.ts +164 -0
  120. package/node_modules/fast-glob/out/settings.js +59 -0
  121. package/node_modules/fast-glob/out/types/index.d.ts +31 -0
  122. package/node_modules/fast-glob/out/types/index.js +2 -0
  123. package/node_modules/fast-glob/out/utils/array.d.ts +2 -0
  124. package/node_modules/fast-glob/out/utils/array.js +22 -0
  125. package/node_modules/fast-glob/out/utils/errno.d.ts +2 -0
  126. package/node_modules/fast-glob/out/utils/errno.js +7 -0
  127. package/node_modules/fast-glob/out/utils/fs.d.ts +4 -0
  128. package/node_modules/fast-glob/out/utils/fs.js +19 -0
  129. package/node_modules/fast-glob/out/utils/index.d.ts +8 -0
  130. package/node_modules/fast-glob/out/utils/index.js +17 -0
  131. package/node_modules/fast-glob/out/utils/path.d.ts +13 -0
  132. package/node_modules/fast-glob/out/utils/path.js +68 -0
  133. package/node_modules/fast-glob/out/utils/pattern.d.ts +49 -0
  134. package/node_modules/fast-glob/out/utils/pattern.js +206 -0
  135. package/node_modules/fast-glob/out/utils/stream.d.ts +4 -0
  136. package/node_modules/fast-glob/out/utils/stream.js +17 -0
  137. package/node_modules/fast-glob/out/utils/string.d.ts +2 -0
  138. package/node_modules/fast-glob/out/utils/string.js +11 -0
  139. package/node_modules/fast-glob/package.json +81 -0
  140. package/node_modules/fastq/LICENSE +13 -0
  141. package/node_modules/fastq/README.md +310 -0
  142. package/node_modules/fastq/SECURITY.md +15 -0
  143. package/node_modules/fastq/bench.js +66 -0
  144. package/node_modules/fastq/eslint.config.js +11 -0
  145. package/node_modules/fastq/example.js +14 -0
  146. package/node_modules/fastq/example.mjs +9 -0
  147. package/node_modules/fastq/index.d.ts +59 -0
  148. package/node_modules/fastq/package.json +49 -0
  149. package/node_modules/fastq/queue.js +346 -0
  150. package/node_modules/fastq/test/example.ts +83 -0
  151. package/node_modules/fastq/test/promise.js +325 -0
  152. package/node_modules/fastq/test/test.js +733 -0
  153. package/node_modules/fastq/test/tsconfig.json +11 -0
  154. package/node_modules/fill-range/LICENSE +21 -0
  155. package/node_modules/fill-range/README.md +237 -0
  156. package/node_modules/fill-range/index.js +248 -0
  157. package/node_modules/fill-range/package.json +74 -0
  158. package/node_modules/glob-parent/LICENSE +15 -0
  159. package/node_modules/glob-parent/README.md +137 -0
  160. package/node_modules/glob-parent/index.js +42 -0
  161. package/node_modules/glob-parent/package.json +48 -0
  162. package/node_modules/ignore/LICENSE-MIT +21 -0
  163. package/node_modules/ignore/README.md +452 -0
  164. package/node_modules/ignore/index.d.ts +81 -0
  165. package/node_modules/ignore/index.js +784 -0
  166. package/node_modules/ignore/legacy.js +681 -0
  167. package/node_modules/ignore/package.json +87 -0
  168. package/node_modules/is-extglob/LICENSE +21 -0
  169. package/node_modules/is-extglob/README.md +107 -0
  170. package/node_modules/is-extglob/index.js +20 -0
  171. package/node_modules/is-extglob/package.json +69 -0
  172. package/node_modules/is-glob/LICENSE +21 -0
  173. package/node_modules/is-glob/README.md +206 -0
  174. package/node_modules/is-glob/index.js +150 -0
  175. package/node_modules/is-glob/package.json +81 -0
  176. package/node_modules/is-number/LICENSE +21 -0
  177. package/node_modules/is-number/README.md +187 -0
  178. package/node_modules/is-number/index.js +18 -0
  179. package/node_modules/is-number/package.json +82 -0
  180. package/node_modules/merge2/LICENSE +21 -0
  181. package/node_modules/merge2/README.md +144 -0
  182. package/node_modules/merge2/index.js +144 -0
  183. package/node_modules/merge2/package.json +43 -0
  184. package/node_modules/micromatch/LICENSE +21 -0
  185. package/node_modules/micromatch/README.md +1024 -0
  186. package/node_modules/micromatch/index.js +474 -0
  187. package/node_modules/micromatch/package.json +119 -0
  188. package/node_modules/mime-db/HISTORY.md +541 -0
  189. package/node_modules/mime-db/LICENSE +23 -0
  190. package/node_modules/mime-db/README.md +109 -0
  191. package/node_modules/mime-db/db.json +9342 -0
  192. package/node_modules/mime-db/index.js +12 -0
  193. package/node_modules/mime-db/package.json +56 -0
  194. package/node_modules/mime-types/HISTORY.md +421 -0
  195. package/node_modules/mime-types/LICENSE +23 -0
  196. package/node_modules/mime-types/README.md +126 -0
  197. package/node_modules/mime-types/index.js +211 -0
  198. package/node_modules/mime-types/mimeScore.js +52 -0
  199. package/node_modules/mime-types/package.json +45 -0
  200. package/node_modules/picomatch/LICENSE +21 -0
  201. package/node_modules/picomatch/README.md +716 -0
  202. package/node_modules/picomatch/index.js +3 -0
  203. package/node_modules/picomatch/lib/constants.js +184 -0
  204. package/node_modules/picomatch/lib/parse.js +1392 -0
  205. package/node_modules/picomatch/lib/picomatch.js +342 -0
  206. package/node_modules/picomatch/lib/scan.js +391 -0
  207. package/node_modules/picomatch/lib/utils.js +64 -0
  208. package/node_modules/picomatch/package.json +81 -0
  209. package/node_modules/queue-microtask/LICENSE +20 -0
  210. package/node_modules/queue-microtask/README.md +90 -0
  211. package/node_modules/queue-microtask/index.d.ts +2 -0
  212. package/node_modules/queue-microtask/index.js +9 -0
  213. package/node_modules/queue-microtask/package.json +55 -0
  214. package/node_modules/reusify/.github/dependabot.yml +7 -0
  215. package/node_modules/reusify/.github/workflows/ci.yml +96 -0
  216. package/node_modules/reusify/LICENSE +22 -0
  217. package/node_modules/reusify/README.md +139 -0
  218. package/node_modules/reusify/SECURITY.md +15 -0
  219. package/node_modules/reusify/benchmarks/createNoCodeFunction.js +30 -0
  220. package/node_modules/reusify/benchmarks/fib.js +13 -0
  221. package/node_modules/reusify/benchmarks/reuseNoCodeFunction.js +38 -0
  222. package/node_modules/reusify/eslint.config.js +14 -0
  223. package/node_modules/reusify/package.json +50 -0
  224. package/node_modules/reusify/reusify.d.ts +14 -0
  225. package/node_modules/reusify/reusify.js +33 -0
  226. package/node_modules/reusify/test.js +66 -0
  227. package/node_modules/reusify/tsconfig.json +11 -0
  228. package/node_modules/run-parallel/LICENSE +20 -0
  229. package/node_modules/run-parallel/README.md +85 -0
  230. package/node_modules/run-parallel/index.js +51 -0
  231. package/node_modules/run-parallel/package.json +58 -0
  232. package/node_modules/to-regex-range/LICENSE +21 -0
  233. package/node_modules/to-regex-range/README.md +305 -0
  234. package/node_modules/to-regex-range/index.js +288 -0
  235. package/node_modules/to-regex-range/package.json +88 -0
  236. package/package.json +53 -0
package/dist/cli.cjs ADDED
@@ -0,0 +1,1489 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/program.ts
27
+ var import_commander = require("commander");
28
+
29
+ // src/auth.ts
30
+ async function resolveCredential(input) {
31
+ if (input.apiKey) return { apiKey: input.apiKey, source: "flag" };
32
+ if (input.env.DROPTHIS_API_KEY) {
33
+ return { apiKey: input.env.DROPTHIS_API_KEY, source: "env" };
34
+ }
35
+ const stored = await input.store.read();
36
+ if (!stored) return null;
37
+ return {
38
+ apiKey: stored.apiKey,
39
+ source: "storage",
40
+ ...stored.keyId ? { keyId: stored.keyId } : {},
41
+ ...stored.keyLast4 ? { keyLast4: stored.keyLast4 } : {},
42
+ ...stored.accountId ? { accountId: stored.accountId } : {},
43
+ ...stored.email ? { email: stored.email } : {}
44
+ };
45
+ }
46
+ function maskKey(apiKey) {
47
+ return apiKey.length <= 8 ? "sk_..." : `${apiKey.slice(0, 3)}...${apiKey.slice(-4)}`;
48
+ }
49
+
50
+ // src/output.ts
51
+ var UPLOAD_NEXT_ACTIONS = {
52
+ upload_expired: "Create a new upload session and retry the publish.",
53
+ upload_already_used: "Create a new upload session; upload sessions are single-use.",
54
+ upload_verification_failed: "Re-upload the file bytes and complete the upload again.",
55
+ too_many_active_uploads: "Cancel unused uploads or wait for expired uploads to be cleaned.",
56
+ upload_not_complete: "Complete the upload session before publishing."
57
+ };
58
+ function createOutput(input) {
59
+ const json = input.json === true || input.quiet === true || input.stdoutIsTTY === false || input.env.CI === "true";
60
+ return { mode: json ? "json" : "human", quiet: input.quiet === true };
61
+ }
62
+ function errorEnvelope(code, message, nextAction) {
63
+ return {
64
+ ok: false,
65
+ error: {
66
+ code,
67
+ message,
68
+ ...nextAction ? { next_action: nextAction } : {}
69
+ }
70
+ };
71
+ }
72
+ function apiErrorEnvelope(error) {
73
+ return {
74
+ ok: false,
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)
84
+ }
85
+ };
86
+ }
87
+ function nextActionForApiError(error) {
88
+ const uploadNextAction = error.code ? UPLOAD_NEXT_ACTIONS[error.code] : void 0;
89
+ if (uploadNextAction) return uploadNextAction;
90
+ if (error.code === "revision_conflict") {
91
+ return "Fetch the drop, merge your changes, and retry with the current revision.";
92
+ }
93
+ if (error.statusCode === 401 || error.code === "missing_api_key") {
94
+ return "Authenticate with dropthis login or set DROPTHIS_API_KEY.";
95
+ }
96
+ if (error.statusCode === 413 || error.code === "quota_exceeded") {
97
+ return "Reduce the upload size or upgrade the account limit.";
98
+ }
99
+ if (error.statusCode === 422) {
100
+ return "Fix the input shown in the error detail and retry.";
101
+ }
102
+ if (error.statusCode !== void 0 && error.statusCode !== null && error.statusCode >= 500) {
103
+ return "Retry the request with the same idempotency key, or contact support with the request id.";
104
+ }
105
+ return "Fix the request or retry after checking the drop state.";
106
+ }
107
+ function exitCodeFor(code) {
108
+ if (code === "invalid_usage") return 2;
109
+ if (code === "auth_error") return 3;
110
+ if (code === "local_input_error") return 4;
111
+ if (code === "network_error") return 5;
112
+ return 1;
113
+ }
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") };
123
+ }
124
+ const result = await deps.client.account.get();
125
+ if (result.error) {
126
+ deps.stderr(
127
+ `${JSON.stringify(errorEnvelope("api_error", result.error.message))}
128
+ `
129
+ );
130
+ return { exitCode: exitCodeFor("api_error") };
131
+ }
132
+ deps.stdout(`${JSON.stringify({ ok: true, account: result.data })}
133
+ `);
134
+ return { exitCode: 0 };
135
+ }
136
+
137
+ // src/commands/api-keys.ts
138
+ async function runApiKeysCreate(input, deps) {
139
+ if (!await hasCredential(deps)) return authError(deps);
140
+ 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
+ `);
144
+ return { exitCode: 0 };
145
+ }
146
+ async function runApiKeysList(_input, deps) {
147
+ if (!await hasCredential(deps)) return authError(deps);
148
+ 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) })}
152
+ `
153
+ );
154
+ return { exitCode: 0 };
155
+ }
156
+ async function runApiKeysDelete(keyId, input, deps) {
157
+ if (!input.yes && input.interactive === false) {
158
+ return invalidUsage(deps, "Pass --yes to delete in non-interactive mode.");
159
+ }
160
+ if (!await hasCredential(deps)) return authError(deps);
161
+ 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
+ `);
165
+ return { exitCode: 0 };
166
+ }
167
+ async function hasCredential(deps) {
168
+ return await resolveCredential({ env: deps.env, store: deps.store }) !== null;
169
+ }
170
+ function listItems(data) {
171
+ if (data && typeof data === "object" && "data" in data) {
172
+ return data.data;
173
+ }
174
+ if (data && typeof data === "object" && "apiKeys" in data) {
175
+ return data.apiKeys;
176
+ }
177
+ return data;
178
+ }
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
+
197
+ // src/commands/commands.ts
198
+ var COMMANDS = [
199
+ {
200
+ name: "publish",
201
+ description: "Publish an input and return a Dropthis URL.",
202
+ arguments: ["input"],
203
+ options: ["--json", "--url", "--dry-run", "--title", "--metadata"],
204
+ examples: [
205
+ "dropthis publish ./site --json",
206
+ "dropthis publish ./site --url",
207
+ "dropthis publish ./dist --dry-run"
208
+ ]
209
+ },
210
+ {
211
+ name: "update",
212
+ description: "Update an existing drop by id.",
213
+ arguments: ["target", "input"],
214
+ options: ["--json", "--url", "--dry-run", "--if-revision"],
215
+ examples: [
216
+ "dropthis update drop_abc ./site --json",
217
+ "dropthis update drop_abc --title 'New Title' --dry-run"
218
+ ]
219
+ },
220
+ {
221
+ name: "deployments list <dropId>",
222
+ description: "List deployments for a drop.",
223
+ arguments: ["dropId"],
224
+ options: ["--json", "--limit", "--cursor"],
225
+ auth: "required",
226
+ examples: ["dropthis deployments list drop_abc --json"]
227
+ },
228
+ {
229
+ name: "deployments get <dropId> <deploymentId>",
230
+ description: "Get deployment details for a drop.",
231
+ arguments: ["dropId", "deploymentId"],
232
+ options: ["--json"],
233
+ auth: "required",
234
+ examples: ["dropthis deployments get drop_abc dep_abc --json"]
235
+ },
236
+ {
237
+ name: "drops",
238
+ description: "List, inspect, update, and delete drops.",
239
+ examples: ["dropthis drops list --json"]
240
+ },
241
+ {
242
+ name: "api-keys",
243
+ description: "Create, list, and delete API keys.",
244
+ examples: ["dropthis api-keys create --label CI --json"]
245
+ },
246
+ {
247
+ name: "login",
248
+ description: "Authenticate with email OTP and store an API key.",
249
+ examples: [
250
+ "dropthis login verify --email user@example.com --otp 123456 --json"
251
+ ]
252
+ },
253
+ {
254
+ name: "logout",
255
+ description: "Remove stored local credentials.",
256
+ examples: ["dropthis logout --json"]
257
+ },
258
+ {
259
+ name: "whoami",
260
+ description: "Show current auth source and credential metadata.",
261
+ examples: ["dropthis whoami --json"]
262
+ },
263
+ {
264
+ name: "account",
265
+ description: "Get account details.",
266
+ examples: ["dropthis account get --json"]
267
+ },
268
+ {
269
+ name: "doctor",
270
+ description: "Report local CLI diagnostics.",
271
+ examples: ["dropthis doctor --json"]
272
+ },
273
+ {
274
+ name: "commands",
275
+ description: "Print this machine-readable command tree.",
276
+ examples: ["dropthis commands --json"]
277
+ }
278
+ ];
279
+ async function runCommands(_input, deps) {
280
+ deps.stdout(
281
+ `${JSON.stringify({
282
+ ok: true,
283
+ output: "JSON by default in CI, pipes, non-TTY, --json, or --quiet.",
284
+ commands: COMMANDS
285
+ })}
286
+ `
287
+ );
288
+ return { exitCode: 0 };
289
+ }
290
+
291
+ // src/commands/deployments.ts
292
+ async function runDeploymentsList(dropId, input, deps) {
293
+ if (!await hasCredential2(deps)) return authError2(deps);
294
+ const params = {
295
+ ...input.limit !== void 0 ? { limit: input.limit } : {},
296
+ ...input.cursor ? { cursor: input.cursor } : {}
297
+ };
298
+ const result = await deps.client.deployments.list(dropId, params);
299
+ if (result.error) {
300
+ deps.stderr(`${JSON.stringify(apiErrorEnvelope(result.error))}
301
+ `);
302
+ return { exitCode: exitCodeFor("api_error") };
303
+ }
304
+ deps.stdout(`${JSON.stringify({ ok: true, ...spreadData(result.data) })}
305
+ `);
306
+ return { exitCode: 0 };
307
+ }
308
+ async function runDeploymentsGet(dropId, deploymentId, _input, deps) {
309
+ if (!await hasCredential2(deps)) return authError2(deps);
310
+ const result = await deps.client.deployments.get(dropId, deploymentId);
311
+ 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
+ `);
318
+ return { exitCode: 0 };
319
+ }
320
+ async function hasCredential2(deps) {
321
+ return await resolveCredential({
322
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
323
+ env: deps.env,
324
+ store: deps.store
325
+ }) !== null;
326
+ }
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
+ function spreadData(data) {
335
+ return data && typeof data === "object" ? data : { data };
336
+ }
337
+
338
+ // src/commands/doctor.ts
339
+ async function runDoctor(_input, deps) {
340
+ const stored = await deps.store.read();
341
+ const credential = await resolveCredential({
342
+ env: deps.env,
343
+ store: deps.store
344
+ });
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
+ );
354
+ return { exitCode: 0 };
355
+ }
356
+
357
+ // src/options.ts
358
+ var import_promises = require("fs/promises");
359
+ async function parseDropOptions(raw) {
360
+ if (raw.metadata && raw.metadataFile) {
361
+ throw new Error("Use either --metadata or --metadata-file, not both.");
362
+ }
363
+ if (raw.password && raw.noPassword) {
364
+ throw new Error("Use either --password or --no-password, not both.");
365
+ }
366
+ if (raw.noindex && raw.index) {
367
+ throw new Error("Use either --noindex or --index, not both.");
368
+ }
369
+ const metadata = raw.metadata ? parseJsonObject(raw.metadata, "--metadata") : raw.metadataFile ? parseJsonObject(
370
+ await (0, import_promises.readFile)(raw.metadataFile, "utf8"),
371
+ "--metadata-file"
372
+ ) : void 0;
373
+ return {
374
+ ...raw.slug ? { slug: raw.slug } : {},
375
+ ...raw.title ? { title: raw.title } : {},
376
+ ...raw.visibility ? { visibility: raw.visibility } : {},
377
+ ...raw.password ? { password: raw.password } : {},
378
+ ...raw.noPassword ? { password: null } : {},
379
+ ...raw.noindex ? { noindex: true } : {},
380
+ ...raw.index ? { noindex: false } : {},
381
+ ...raw.expiresAt ? { expiresAt: raw.expiresAt } : {},
382
+ ...metadata ? { metadata } : {},
383
+ ...raw.entry ? { entry: raw.entry } : {},
384
+ ...raw.contentType ? { contentType: raw.contentType } : {},
385
+ ...raw.path ? { path: raw.path } : {},
386
+ ...raw.idempotencyKey ? { idempotencyKey: raw.idempotencyKey } : {}
387
+ };
388
+ }
389
+ function parseJsonObject(value, label) {
390
+ const parsed = JSON.parse(value);
391
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
392
+ throw new Error(`${label} must be a JSON object.`);
393
+ }
394
+ return parsed;
395
+ }
396
+
397
+ // src/commands/drops.ts
398
+ async function runDropsList(input, deps) {
399
+ if (!await hasCredential3(deps)) return authError3(deps);
400
+ const params = {
401
+ ...input.limit !== void 0 ? { limit: input.limit } : {},
402
+ ...input.cursor ? { cursor: input.cursor } : {}
403
+ };
404
+ 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) })}
408
+ `
409
+ );
410
+ return { exitCode: 0 };
411
+ }
412
+ async function runDropsGet(dropId, _input, deps) {
413
+ if (!await hasCredential3(deps)) return authError3(deps);
414
+ 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
+ `);
418
+ return { exitCode: 0 };
419
+ }
420
+ async function runDropsUpdate(dropId, input, deps) {
421
+ if (!await hasCredential3(deps)) return authError3(deps);
422
+ try {
423
+ const options = await parseDropOptions(input);
424
+ 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
+ `);
428
+ return { exitCode: 0 };
429
+ } catch (error) {
430
+ return invalidUsage2(
431
+ deps,
432
+ error instanceof Error ? error.message : "Invalid drop options."
433
+ );
434
+ }
435
+ }
436
+ async function runDropsDelete(dropId, input, deps) {
437
+ if (!input.yes && input.interactive === false) {
438
+ return invalidUsage2(deps, "Pass --yes to delete in non-interactive mode.");
439
+ }
440
+ if (!await hasCredential3(deps)) return authError3(deps);
441
+ 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
+ `);
445
+ return { exitCode: 0 };
446
+ }
447
+ async function hasCredential3(deps) {
448
+ return await resolveCredential({ env: deps.env, store: deps.store }) !== null;
449
+ }
450
+ function listItems2(data) {
451
+ if (data && typeof data === "object" && "data" in data) {
452
+ return data.data;
453
+ }
454
+ if (data && typeof data === "object" && "drops" in data) {
455
+ return data.drops;
456
+ }
457
+ return data;
458
+ }
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
+
477
+ // src/commands/login.ts
478
+ var prompts = __toESM(require("@clack/prompts"), 1);
479
+ async function runLoginInteractive(deps) {
480
+ prompts.intro("dropthis login");
481
+ const email = await prompts.text({
482
+ message: "Email address",
483
+ validate: (v) => v?.includes("@") ? void 0 : "Enter a valid email"
484
+ });
485
+ if (prompts.isCancel(email)) {
486
+ prompts.cancel("Login cancelled.");
487
+ return { exitCode: 0 };
488
+ }
489
+ const requestResult = await deps.client.auth.requestEmailOtp({ email });
490
+ if (requestResult.error) {
491
+ prompts.cancel(requestResult.error.message);
492
+ return { exitCode: exitCodeFor("api_error") };
493
+ }
494
+ const otp = await prompts.text({
495
+ message: "Paste the code from your email"
496
+ });
497
+ if (prompts.isCancel(otp)) {
498
+ prompts.cancel("Login cancelled.");
499
+ return { exitCode: 0 };
500
+ }
501
+ const session = await deps.client.auth.verifyEmailOtp({ email, code: otp });
502
+ if (session.error) {
503
+ prompts.cancel(session.error.message);
504
+ return { exitCode: exitCodeFor("api_error") };
505
+ }
506
+ const authedClient = deps.createClient(session.data.token);
507
+ const apiKey = await authedClient.apiKeys.create({ label: "CLI" });
508
+ if (apiKey.error) {
509
+ prompts.cancel(apiKey.error.message);
510
+ return { exitCode: exitCodeFor("api_error") };
511
+ }
512
+ await deps.store.save({
513
+ apiKey: apiKey.data.key,
514
+ keyId: apiKey.data.id,
515
+ keyLast4: apiKey.data.keyLast4,
516
+ accountId: apiKey.data.accountId ?? session.data.accountId,
517
+ email,
518
+ storage: "secure"
519
+ });
520
+ prompts.outro("Logged in successfully.");
521
+ return { exitCode: 0 };
522
+ }
523
+ async function runLoginRequest(input, deps) {
524
+ const result = await deps.client.auth.requestEmailOtp({
525
+ email: input.email
526
+ });
527
+ if (result.error) {
528
+ deps.stderr(
529
+ `${JSON.stringify(errorEnvelope("api_error", result.error.message))}
530
+ `
531
+ );
532
+ return { exitCode: exitCodeFor("api_error") };
533
+ }
534
+ deps.stdout(
535
+ `${JSON.stringify({
536
+ ok: true,
537
+ email: input.email,
538
+ expires_in: result.data.expiresIn
539
+ })}
540
+ `
541
+ );
542
+ return { exitCode: 0 };
543
+ }
544
+ async function runLoginVerify(input, deps) {
545
+ const session = await deps.client.auth.verifyEmailOtp({
546
+ email: input.email,
547
+ code: input.otp
548
+ });
549
+ if (session.error) {
550
+ deps.stderr(
551
+ `${JSON.stringify(errorEnvelope("api_error", session.error.message))}
552
+ `
553
+ );
554
+ return { exitCode: exitCodeFor("api_error") };
555
+ }
556
+ const apiKey = await deps.client.apiKeys.create({ label: "CLI" });
557
+ if (apiKey.error) {
558
+ deps.stderr(
559
+ `${JSON.stringify(errorEnvelope("api_error", apiKey.error.message))}
560
+ `
561
+ );
562
+ return { exitCode: exitCodeFor("api_error") };
563
+ }
564
+ await deps.store.save({
565
+ apiKey: apiKey.data.key,
566
+ keyId: apiKey.data.id,
567
+ keyLast4: apiKey.data.keyLast4,
568
+ accountId: apiKey.data.accountId ?? session.data.accountId,
569
+ email: input.email,
570
+ storage: "secure"
571
+ });
572
+ deps.stdout(
573
+ `${JSON.stringify({
574
+ ok: true,
575
+ account_id: apiKey.data.accountId ?? session.data.accountId,
576
+ key_id: apiKey.data.id,
577
+ key_last4: apiKey.data.keyLast4,
578
+ is_new_account: apiKey.data.isNewAccount ?? session.data.isNewAccount
579
+ })}
580
+ `
581
+ );
582
+ return { exitCode: 0 };
583
+ }
584
+
585
+ // src/commands/logout.ts
586
+ async function runLogout(input, deps) {
587
+ const stored = await deps.store.read();
588
+ let revokeError;
589
+ if (input.revoke && stored?.keyId) {
590
+ const result = await deps.client.apiKeys.delete(stored.keyId);
591
+ if (result?.error) revokeError = result.error.message;
592
+ }
593
+ await deps.store.clear();
594
+ if (revokeError) {
595
+ deps.stderr(`${JSON.stringify(errorEnvelope("api_error", revokeError))}
596
+ `);
597
+ return { exitCode: exitCodeFor("api_error") };
598
+ }
599
+ deps.stdout(`${JSON.stringify({ ok: true })}
600
+ `);
601
+ return { exitCode: 0 };
602
+ }
603
+
604
+ // src/commands/publish.ts
605
+ var import_node_crypto = require("crypto");
606
+ var import_promises3 = require("fs/promises");
607
+ var import_node_path = require("path");
608
+
609
+ // src/commands/json_body.ts
610
+ var import_promises2 = require("fs/promises");
611
+ async function readJsonObjectFile(path) {
612
+ let parsed;
613
+ try {
614
+ parsed = JSON.parse(await (0, import_promises2.readFile)(path, "utf8"));
615
+ } catch (error) {
616
+ const message = error instanceof Error ? error.message : "Unknown parse error";
617
+ throw new Error(`Invalid JSON body in ${path}: ${message}`);
618
+ }
619
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
620
+ throw new Error("JSON body must be an object.");
621
+ }
622
+ return parsed;
623
+ }
624
+
625
+ // src/commands/publish.ts
626
+ var MAX_BUNDLE_FILE_COUNT = 200;
627
+ async function runPublish(input, raw, deps) {
628
+ const credential = await resolveCredential({
629
+ ...raw.apiKey ? { apiKey: raw.apiKey } : {},
630
+ env: deps.env,
631
+ store: deps.store
632
+ });
633
+ 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") };
639
+ }
640
+ if (raw.dryRun) {
641
+ return handlePublishDryRun(input, raw, deps);
642
+ }
643
+ const singleInput = Array.isArray(input) ? input[0] : input;
644
+ try {
645
+ if (raw.fromJson && singleInput) {
646
+ throw new Error("Use either <input> or --from-json, not both.");
647
+ }
648
+ const options = withPublishIdempotency(await parseDropOptions(raw));
649
+ const result = raw.fromJson ? await deps.client.drops.createRaw(
650
+ await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson),
651
+ options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
652
+ ) : singleInput ? await deps.client.publish(singleInput, options) : (() => {
653
+ throw new Error("Publish requires <input> or --from-json.");
654
+ })();
655
+ if (result.error) {
656
+ deps.stderr(`${JSON.stringify(apiErrorEnvelope(result.error))}
657
+ `);
658
+ return { exitCode: exitCodeFor("api_error") };
659
+ }
660
+ if (raw.url) deps.stdout(`${result.data.url}
661
+ `);
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
+ `);
667
+ 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") };
673
+ }
674
+ }
675
+ async function handlePublishDryRun(input, raw, deps) {
676
+ 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
+ `
680
+ );
681
+ return { exitCode: exitCodeFor("invalid_usage") };
682
+ }
683
+ try {
684
+ if (raw.fromJson) {
685
+ if (input)
686
+ throw new Error("Use either <input> or --from-json, not both.");
687
+ const body = await (deps.readJsonObject ?? readJsonObjectFile)(
688
+ raw.fromJson
689
+ );
690
+ deps.stdout(
691
+ `${JSON.stringify({ dryRun: true, kind: "raw_json", endpoint: "POST /drops", body }, null, 2)}
692
+ `
693
+ );
694
+ return { exitCode: 0 };
695
+ }
696
+ if (!input || Array.isArray(input) && input.length === 0) {
697
+ throw new Error("Publish requires <input> or --from-json.");
698
+ }
699
+ const options = await parseDropOptions(raw);
700
+ if (Array.isArray(input)) {
701
+ return dryRunMultiFile(input, options, deps);
702
+ }
703
+ if (!deps.client.prepare) {
704
+ throw new Error("Client does not support dry-run.");
705
+ }
706
+ const prepared = await deps.client.prepare(input, options);
707
+ const output = formatDryRunOutput(prepared);
708
+ validateDryRunOutput(output);
709
+ deps.stdout(`${JSON.stringify(output, null, 2)}
710
+ `);
711
+ 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) };
718
+ }
719
+ }
720
+ async function dryRunMultiFile(inputs, options, deps) {
721
+ const files = [];
722
+ for (const filePath of inputs) {
723
+ let info;
724
+ try {
725
+ info = await (0, import_promises3.stat)(filePath);
726
+ } catch {
727
+ deps.stderr(
728
+ `${JSON.stringify(errorEnvelope("local_input_error", `File not found: ${filePath}`))}
729
+ `
730
+ );
731
+ return { exitCode: exitCodeFor("local_input_error") };
732
+ }
733
+ 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
+ `
737
+ );
738
+ return { exitCode: exitCodeFor("local_input_error") };
739
+ }
740
+ files.push({
741
+ path: (0, import_node_path.basename)(filePath),
742
+ contentType: mimeForPath(filePath),
743
+ sizeBytes: info.size
744
+ });
745
+ }
746
+ 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
+ `
750
+ );
751
+ return { exitCode: exitCodeFor("local_input_error") };
752
+ }
753
+ const entry = options.entry ?? files.find((f) => f.path === "index.html")?.path ?? files[0]?.path ?? null;
754
+ const totalBytes = files.reduce((sum, f) => sum + f.sizeBytes, 0);
755
+ const output = {
756
+ dryRun: true,
757
+ kind: "staged",
758
+ manifest: { files, entry },
759
+ options: dryRunOptionsBody(options),
760
+ totalBytes
761
+ };
762
+ deps.stdout(`${JSON.stringify(output, null, 2)}
763
+ `);
764
+ return { exitCode: 0 };
765
+ }
766
+ function formatDryRunOutput(prepared) {
767
+ const totalBytes = prepared.manifest.files.reduce(
768
+ (sum, f) => sum + f.sizeBytes,
769
+ 0
770
+ );
771
+ return {
772
+ dryRun: true,
773
+ kind: "staged",
774
+ manifest: {
775
+ files: prepared.manifest.files,
776
+ entry: prepared.manifest.entry ?? null
777
+ },
778
+ options: prepared.options,
779
+ ...prepared.metadata ? { metadata: prepared.metadata } : {},
780
+ totalBytes
781
+ };
782
+ }
783
+ function validateDryRunOutput(output) {
784
+ if (output.kind !== "staged") return;
785
+ const manifest = output.manifest;
786
+ if (manifest.files.length > MAX_BUNDLE_FILE_COUNT) {
787
+ throw Object.assign(
788
+ new Error(
789
+ `Bundle contains ${manifest.files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT}.`
790
+ ),
791
+ { code: "ELIMIT" }
792
+ );
793
+ }
794
+ }
795
+ function dryRunOptionsBody(options) {
796
+ const result = {};
797
+ if (options.title) result.title = options.title;
798
+ if (options.visibility) result.visibility = options.visibility;
799
+ if (options.password !== void 0) result.password = options.password;
800
+ if (options.noindex !== void 0) result.noindex = options.noindex;
801
+ if (options.expiresAt) result.expires_at = options.expiresAt;
802
+ return result;
803
+ }
804
+ function mimeForPath(filePath) {
805
+ const ext = (0, import_node_path.extname)(filePath).toLowerCase();
806
+ const types = {
807
+ ".html": "text/html",
808
+ ".htm": "text/html",
809
+ ".css": "text/css",
810
+ ".js": "text/javascript",
811
+ ".mjs": "text/javascript",
812
+ ".json": "application/json",
813
+ ".xml": "application/xml",
814
+ ".svg": "image/svg+xml",
815
+ ".txt": "text/plain",
816
+ ".md": "text/markdown",
817
+ ".jpg": "image/jpeg",
818
+ ".jpeg": "image/jpeg",
819
+ ".png": "image/png",
820
+ ".gif": "image/gif",
821
+ ".webp": "image/webp",
822
+ ".ico": "image/x-icon",
823
+ ".avif": "image/avif",
824
+ ".pdf": "application/pdf",
825
+ ".zip": "application/zip",
826
+ ".woff": "font/woff",
827
+ ".woff2": "font/woff2",
828
+ ".ttf": "font/ttf",
829
+ ".mp4": "video/mp4",
830
+ ".webm": "video/webm"
831
+ };
832
+ return types[ext] ?? "application/octet-stream";
833
+ }
834
+ function isFileSystemError(error) {
835
+ if (!(error instanceof Error)) return false;
836
+ const code = error.code;
837
+ return code === "ENOENT" || code === "ENOTDIR" || code === "ELIMIT";
838
+ }
839
+ function withPublishIdempotency(options) {
840
+ return options.idempotencyKey ? options : { ...options, idempotencyKey: `cli_pub_${(0, import_node_crypto.randomUUID)()}` };
841
+ }
842
+
843
+ // src/commands/update.ts
844
+ var import_node_crypto2 = require("crypto");
845
+ async function runUpdate(target, input, raw, deps) {
846
+ const credential = await resolveCredential({
847
+ ...raw.apiKey ? { apiKey: raw.apiKey } : {},
848
+ env: deps.env,
849
+ store: deps.store
850
+ });
851
+ 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") };
857
+ }
858
+ if (raw.dryRun) {
859
+ return handleUpdateDryRun(target, input, raw, deps);
860
+ }
861
+ try {
862
+ assertDropId(target);
863
+ if (raw.fromJson && input) {
864
+ throw new Error("Use either [input] or --from-json, not both.");
865
+ }
866
+ const parsed = await parseDropOptions(raw);
867
+ const revisionOptions = raw.ifRevision !== void 0 ? { ifRevision: Number(raw.ifRevision) } : {};
868
+ const result = raw.fromJson ? await deps.client.deployments.createRaw(
869
+ target,
870
+ await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson),
871
+ withUpdateIdempotency({
872
+ ...revisionOptions,
873
+ ...parsed.idempotencyKey ? { idempotencyKey: parsed.idempotencyKey } : {}
874
+ })
875
+ ) : input ? await deps.client.update(
876
+ target,
877
+ input,
878
+ withUpdateIdempotency({
879
+ ...parsed,
880
+ ...revisionOptions
881
+ })
882
+ ) : await deps.client.update(target, parsed, revisionOptions);
883
+ if (result.error) {
884
+ deps.stderr(`${JSON.stringify(apiErrorEnvelope(result.error))}
885
+ `);
886
+ return { exitCode: exitCodeFor("api_error") };
887
+ }
888
+ if (raw.url) deps.stdout(`${result.data.url}
889
+ `);
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
+ `);
895
+ 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") };
901
+ }
902
+ }
903
+ async function handleUpdateDryRun(target, input, raw, deps) {
904
+ 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
+ `
908
+ );
909
+ return { exitCode: exitCodeFor("invalid_usage") };
910
+ }
911
+ try {
912
+ assertDropId(target);
913
+ if (raw.fromJson) {
914
+ if (input)
915
+ throw new Error("Use either [input] or --from-json, not both.");
916
+ const body = await (deps.readJsonObject ?? readJsonObjectFile)(
917
+ raw.fromJson
918
+ );
919
+ deps.stdout(
920
+ `${JSON.stringify({ dryRun: true, kind: "raw_json", endpoint: `POST /drops/${target}/deployments`, body }, null, 2)}
921
+ `
922
+ );
923
+ return { exitCode: 0 };
924
+ }
925
+ const options = await parseDropOptions(raw);
926
+ if (!input) {
927
+ const fields = {};
928
+ if (options.slug) fields.slug = options.slug;
929
+ if (options.title) fields.title = options.title;
930
+ if (options.visibility) fields.visibility = options.visibility;
931
+ if (options.password !== void 0) fields.password = options.password;
932
+ if (options.noindex !== void 0) fields.noindex = options.noindex;
933
+ if (options.expiresAt) fields.expires_at = options.expiresAt;
934
+ if (options.metadata) fields.metadata = options.metadata;
935
+ deps.stdout(
936
+ `${JSON.stringify({ dryRun: true, kind: "options_update", target, fields }, null, 2)}
937
+ `
938
+ );
939
+ return { exitCode: 0 };
940
+ }
941
+ if (!deps.client.prepare) {
942
+ throw new Error("Client does not support dry-run.");
943
+ }
944
+ const prepared = await deps.client.prepare(input, options);
945
+ const output = formatUpdateDryRun(target, prepared);
946
+ validateDryRunFileCount(output);
947
+ deps.stdout(`${JSON.stringify(output, null, 2)}
948
+ `);
949
+ 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) };
956
+ }
957
+ }
958
+ var MAX_BUNDLE_FILE_COUNT2 = 200;
959
+ function formatUpdateDryRun(target, prepared) {
960
+ const totalBytes = prepared.manifest.files.reduce(
961
+ (sum, f) => sum + f.sizeBytes,
962
+ 0
963
+ );
964
+ return {
965
+ dryRun: true,
966
+ kind: "staged",
967
+ target,
968
+ manifest: {
969
+ files: prepared.manifest.files,
970
+ entry: prepared.manifest.entry ?? null
971
+ },
972
+ options: prepared.options,
973
+ ...prepared.metadata ? { metadata: prepared.metadata } : {},
974
+ totalBytes
975
+ };
976
+ }
977
+ function validateDryRunFileCount(output) {
978
+ if (output.kind !== "staged") return;
979
+ const manifest = output.manifest;
980
+ if (manifest.files.length > MAX_BUNDLE_FILE_COUNT2) {
981
+ throw Object.assign(
982
+ new Error(
983
+ `Bundle contains ${manifest.files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT2}.`
984
+ ),
985
+ { code: "ELIMIT" }
986
+ );
987
+ }
988
+ }
989
+ function isFileSystemError2(error) {
990
+ if (!(error instanceof Error)) return false;
991
+ const code = error.code;
992
+ return code === "ENOENT" || code === "ENOTDIR";
993
+ }
994
+ function withUpdateIdempotency(options) {
995
+ return options.idempotencyKey ? options : { ...options, idempotencyKey: `cli_upd_${(0, import_node_crypto2.randomUUID)()}` };
996
+ }
997
+ function assertDropId(target) {
998
+ if (!target.startsWith("drop_")) {
999
+ throw new Error(
1000
+ "Update target must be a drop id returned by --json output, for example drop_..."
1001
+ );
1002
+ }
1003
+ }
1004
+
1005
+ // src/commands/whoami.ts
1006
+ async function runWhoami(_input, deps) {
1007
+ const credential = await resolveCredential({
1008
+ env: deps.env,
1009
+ store: deps.store
1010
+ });
1011
+ if (!credential) {
1012
+ deps.stdout(`${JSON.stringify({ ok: true, authenticated: false })}
1013
+ `);
1014
+ return { exitCode: 0 };
1015
+ }
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
+ );
1029
+ return { exitCode: 0 };
1030
+ }
1031
+
1032
+ // src/context.ts
1033
+ var import_node = require("@dropthis/node");
1034
+ function createContext(input) {
1035
+ const env = input.env ?? process.env;
1036
+ const global = input.global ?? {};
1037
+ return {
1038
+ env,
1039
+ global,
1040
+ output: createOutput({
1041
+ ...global.json !== void 0 ? { json: global.json } : {},
1042
+ ...global.quiet !== void 0 ? { quiet: global.quiet } : {},
1043
+ stdoutIsTTY: input.stdoutIsTTY ?? process.stdout.isTTY === true,
1044
+ env
1045
+ }),
1046
+ stdout: input.stdout ?? ((value) => process.stdout.write(value)),
1047
+ stderr: input.stderr ?? ((value) => process.stderr.write(value)),
1048
+ createClient(apiKey) {
1049
+ const baseUrl = global.apiUrl ?? env.DROPTHIS_API_URL;
1050
+ return new import_node.Dropthis({
1051
+ ...apiKey ? { apiKey } : {},
1052
+ ...baseUrl ? { baseUrl } : {}
1053
+ });
1054
+ }
1055
+ };
1056
+ }
1057
+
1058
+ // src/storage.ts
1059
+ var import_promises4 = require("fs/promises");
1060
+ var import_node_os = require("os");
1061
+ var import_node_path2 = require("path");
1062
+ var import_keyring = require("@napi-rs/keyring");
1063
+ var MemoryCredentialStore = class {
1064
+ credential = null;
1065
+ async read() {
1066
+ return this.credential;
1067
+ }
1068
+ async save(credential) {
1069
+ this.credential = credential;
1070
+ }
1071
+ async clear() {
1072
+ this.credential = null;
1073
+ }
1074
+ };
1075
+ var InsecureFileCredentialStore = class {
1076
+ filePath;
1077
+ constructor(options = {}) {
1078
+ this.filePath = credentialPath(options.configDir);
1079
+ }
1080
+ async read() {
1081
+ const value = await readJsonFile(this.filePath);
1082
+ return value ? { ...value, storage: "insecure" } : null;
1083
+ }
1084
+ async save(credential) {
1085
+ await writeCredentialFile(this.filePath, {
1086
+ ...credential,
1087
+ storage: "insecure"
1088
+ });
1089
+ }
1090
+ async clear() {
1091
+ await (0, import_promises4.rm)(this.filePath, { force: true });
1092
+ }
1093
+ };
1094
+ var KeyringCredentialStore = class {
1095
+ filePath;
1096
+ keyring;
1097
+ constructor(options = {}) {
1098
+ this.filePath = credentialPath(options.configDir);
1099
+ this.keyring = options.keyring ?? defaultKeyringAdapter();
1100
+ }
1101
+ async read() {
1102
+ const metadata = await readJsonFile(this.filePath);
1103
+ if (!metadata) return null;
1104
+ const apiKey = await this.keyring.getPassword();
1105
+ if (!apiKey) return null;
1106
+ return { ...metadata, apiKey, storage: "secure" };
1107
+ }
1108
+ async save(credential) {
1109
+ await this.keyring.setPassword(credential.apiKey);
1110
+ const { apiKey: _apiKey, ...metadata } = credential;
1111
+ await writeCredentialFile(this.filePath, {
1112
+ ...metadata,
1113
+ storage: "secure"
1114
+ });
1115
+ }
1116
+ async clear() {
1117
+ await this.keyring.deletePassword();
1118
+ await (0, import_promises4.rm)(this.filePath, { force: true });
1119
+ }
1120
+ };
1121
+ function createCredentialStore(options = {}) {
1122
+ if (options.insecure) return new InsecureFileCredentialStore(options);
1123
+ return new KeyringCredentialStore(options);
1124
+ }
1125
+ function defaultKeyringAdapter() {
1126
+ const entry = new import_keyring.Entry("dropthis", "default");
1127
+ return {
1128
+ getPassword: () => entry.getPassword(),
1129
+ setPassword: (value) => entry.setPassword(value),
1130
+ deletePassword: () => {
1131
+ entry.deletePassword();
1132
+ }
1133
+ };
1134
+ }
1135
+ function credentialPath(configDir) {
1136
+ return (0, import_node_path2.join)(
1137
+ configDir ?? (0, import_node_path2.join)((0, import_node_os.homedir)(), ".config", "dropthis"),
1138
+ "credentials.json"
1139
+ );
1140
+ }
1141
+ async function readJsonFile(path) {
1142
+ try {
1143
+ 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") {
1146
+ return null;
1147
+ }
1148
+ throw error;
1149
+ }
1150
+ }
1151
+ async function writeCredentialFile(path, credential) {
1152
+ await (0, import_promises4.mkdir)((0, import_node_path2.dirname)(path), { recursive: true });
1153
+ await (0, import_promises4.writeFile)(path, `${JSON.stringify(credential, null, 2)}
1154
+ `, {
1155
+ mode: 384
1156
+ });
1157
+ await (0, import_promises4.chmod)(path, 384);
1158
+ }
1159
+
1160
+ // src/program.ts
1161
+ function buildProgram(options = {}) {
1162
+ const program = new import_commander.Command();
1163
+ const store = options.store ?? new MemoryCredentialStore();
1164
+ program.name("dropthis").description("Publish anything online and get a URL back.").version("0.1.5");
1165
+ program.option("--api-key <key>", "Override API key for this invocation");
1166
+ program.option("--api-url <url>", "Override API base URL");
1167
+ program.option("--json", "Force JSON output");
1168
+ program.option("-q, --quiet", "Suppress status output and imply JSON");
1169
+ program.command("login").description("Authenticate with email OTP").option("--email <email>", "Email address").option("--otp <otp>", "One-time passcode").option("--json", "Force JSON output").action(
1170
+ async (commandOptions) => {
1171
+ if (!commandOptions.email || !commandOptions.otp) {
1172
+ const deps2 = await commandDeps(program, options, store, commandOptions);
1173
+ const global = globalOptions(program, commandOptions);
1174
+ const context = createContext({ global });
1175
+ const result2 = await runLoginInteractive({
1176
+ ...deps2,
1177
+ createClient: (apiKey) => context.createClient(apiKey)
1178
+ });
1179
+ process.exitCode = result2.exitCode;
1180
+ return;
1181
+ }
1182
+ const deps = await commandDeps(program, options, store, commandOptions);
1183
+ const result = await runLoginVerify(
1184
+ {
1185
+ email: commandOptions.email,
1186
+ otp: commandOptions.otp,
1187
+ ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
1188
+ },
1189
+ deps
1190
+ );
1191
+ process.exitCode = result.exitCode;
1192
+ }
1193
+ );
1194
+ const login = program.commands.find((command) => command.name() === "login");
1195
+ login?.command("request").description("Request an email OTP").requiredOption("--email <email>", "Email address").option("--json", "Force JSON output").action(async (commandOptions) => {
1196
+ const deps = await commandDeps(program, options, store, commandOptions);
1197
+ const result = await runLoginRequest(commandOptions, deps);
1198
+ process.exitCode = result.exitCode;
1199
+ });
1200
+ 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
+ async (commandOptions) => {
1202
+ const deps = await commandDeps(program, options, store, commandOptions);
1203
+ const result = await runLoginVerify(commandOptions, deps);
1204
+ process.exitCode = result.exitCode;
1205
+ }
1206
+ );
1207
+ program.command("logout").description("Remove stored credentials").option("--revoke", "Best-effort revoke the saved API key").option("--json", "Force JSON output").action(async (commandOptions) => {
1208
+ const deps = await commandDeps(
1209
+ program,
1210
+ options,
1211
+ store,
1212
+ commandOptions
1213
+ );
1214
+ const result = await runLogout(commandOptions, deps);
1215
+ process.exitCode = result.exitCode;
1216
+ });
1217
+ program.command("whoami").description("Show current authentication status").option("--json", "Force JSON output").action(async (commandOptions) => {
1218
+ const deps = await commandDeps(
1219
+ program,
1220
+ options,
1221
+ store,
1222
+ commandOptions
1223
+ );
1224
+ const result = await runWhoami(commandOptions, deps);
1225
+ process.exitCode = result.exitCode;
1226
+ });
1227
+ program.command("account").description("Account commands").command("get").description("Get account details").option("--json", "Force JSON output").action(async (commandOptions) => {
1228
+ const deps = await commandDeps(program, options, store, commandOptions);
1229
+ const result = await runAccountGet(commandOptions, deps);
1230
+ process.exitCode = result.exitCode;
1231
+ });
1232
+ program.command("publish [input...]").description(
1233
+ "Publish files, folders, URLs, strings, or stdin.\nMultiple files are bundled into one drop.\nUse - to read stdin explicitly, or pipe without args."
1234
+ ).option("--from-json <path>", "Read exact POST /drops JSON request body").option("--json", "Force JSON output").option("--title <title>", "Drop title").option("--visibility <visibility>", "Drop visibility").option("--password <password>", "Set password").option("--no-password", "Clear password").option("--noindex", "Prevent indexing").option("--index", "Allow indexing").option("--expires-at <datetime>", "Expiration datetime").option("--metadata <json>", "Metadata JSON object").option("--metadata-file <path>", "Metadata JSON file").option("--entry <path>", "Entry file path").option("--content-type <mime>", "Content type").option("--path <path>", "Path for stdin or byte input").option("--idempotency-key <key>", "Idempotency key").option("--url", "Print only the published URL").option("--dry-run", "Validate without publishing").action(async (inputs, commandOptions) => {
1235
+ const deps = await commandDeps(program, options, store, commandOptions);
1236
+ const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
1237
+ let publishInput;
1238
+ if (commandOptions.dryRun && inputs.length > 1) {
1239
+ publishInput = inputs;
1240
+ } else {
1241
+ publishInput = await resolvePublishInputs(
1242
+ inputs,
1243
+ options.stdin,
1244
+ stdinIsTTY
1245
+ );
1246
+ }
1247
+ const result = await runPublish(
1248
+ publishInput,
1249
+ toDropOptions(commandOptions),
1250
+ deps
1251
+ );
1252
+ process.exitCode = result.exitCode;
1253
+ });
1254
+ program.command("update <target> [input]").description("Update an existing drop by id").option("--json", "Force JSON output").option("--slug <slug>", "Replace shared hosted drop slug").option("--title <title>", "Drop title").option("--visibility <visibility>", "Drop visibility").option("--password <password>", "Set password").option("--no-password", "Clear password").option("--noindex", "Prevent indexing").option("--index", "Allow indexing").option("--expires-at <datetime>", "Expiration datetime").option("--metadata <json>", "Metadata JSON object").option("--metadata-file <path>", "Metadata JSON file").option("--entry <path>", "Entry file path").option("--content-type <mime>", "Content type").option("--path <path>", "Path for stdin or byte input").option("--idempotency-key <key>", "Idempotency key").option("--if-revision <n>", "Expected current revision", parseInteger).option(
1255
+ "--from-json <path>",
1256
+ "Read exact POST /drops/{drop_id}/deployments JSON request body"
1257
+ ).option("--url", "Print only the published URL").option("--dry-run", "Validate without publishing").action(
1258
+ async (target, input, commandOptions) => {
1259
+ const deps = await commandDeps(program, options, store, commandOptions);
1260
+ const updateInput = input ? await resolveCommandInput(input, options.stdin) : void 0;
1261
+ const result = await runUpdate(
1262
+ target,
1263
+ updateInput,
1264
+ toDropOptions(commandOptions),
1265
+ deps
1266
+ );
1267
+ process.exitCode = result.exitCode;
1268
+ }
1269
+ );
1270
+ const drops = program.command("drops").description("Drop resource commands");
1271
+ drops.command("list").option("--limit <n>", "Page size", parseInteger).option("--cursor <cursor>", "Pagination cursor").option("--json", "Force JSON output").action(
1272
+ async (commandOptions) => {
1273
+ const deps = await commandDeps(program, options, store, commandOptions);
1274
+ const result = await runDropsList(commandOptions, deps);
1275
+ process.exitCode = result.exitCode;
1276
+ }
1277
+ );
1278
+ drops.command("get <dropId>").option("--json", "Force JSON output").action(async (dropId, commandOptions) => {
1279
+ const deps = await commandDeps(program, options, store, commandOptions);
1280
+ const result = await runDropsGet(dropId, commandOptions, deps);
1281
+ process.exitCode = result.exitCode;
1282
+ });
1283
+ drops.command("update <dropId>").option("--json", "Force JSON output").option("--title <title>", "Drop title").option("--visibility <visibility>", "Drop visibility").option("--password <password>", "Set password").option("--no-password", "Clear password").option("--noindex", "Prevent indexing").option("--index", "Allow indexing").option("--expires-at <datetime>", "Expiration datetime").option("--metadata <json>", "Metadata JSON object").option("--metadata-file <path>", "Metadata JSON file").action(async (dropId, commandOptions) => {
1284
+ const deps = await commandDeps(program, options, store, commandOptions);
1285
+ const result = await runDropsUpdate(
1286
+ dropId,
1287
+ toDropOptions(commandOptions),
1288
+ deps
1289
+ );
1290
+ process.exitCode = result.exitCode;
1291
+ });
1292
+ drops.command("delete <dropId>").option("--yes", "Confirm deletion").option("--json", "Force JSON output").action(
1293
+ async (dropId, commandOptions) => {
1294
+ const deps = await commandDeps(program, options, store, commandOptions);
1295
+ const result = await runDropsDelete(
1296
+ dropId,
1297
+ { ...commandOptions, interactive: deps.outputMode === "human" },
1298
+ deps
1299
+ );
1300
+ process.exitCode = result.exitCode;
1301
+ }
1302
+ );
1303
+ const apiKeys = program.command("api-keys").description("API key commands");
1304
+ apiKeys.command("create").option("--label <label>", "API key label", "CLI").option("--json", "Force JSON output").action(async (commandOptions) => {
1305
+ const deps = await commandDeps(program, options, store, commandOptions);
1306
+ const result = await runApiKeysCreate(commandOptions, deps);
1307
+ process.exitCode = result.exitCode;
1308
+ });
1309
+ apiKeys.command("list").option("--json", "Force JSON output").action(async (commandOptions) => {
1310
+ const deps = await commandDeps(program, options, store, commandOptions);
1311
+ const result = await runApiKeysList(commandOptions, deps);
1312
+ process.exitCode = result.exitCode;
1313
+ });
1314
+ apiKeys.command("delete <keyId>").option("--yes", "Confirm deletion").option("--json", "Force JSON output").action(
1315
+ async (keyId, commandOptions) => {
1316
+ const deps = await commandDeps(program, options, store, commandOptions);
1317
+ const result = await runApiKeysDelete(
1318
+ keyId,
1319
+ { ...commandOptions, interactive: deps.outputMode === "human" },
1320
+ deps
1321
+ );
1322
+ process.exitCode = result.exitCode;
1323
+ }
1324
+ );
1325
+ const deployments = program.command("deployments").description("Deployment commands");
1326
+ deployments.command("list <dropId>").option("--limit <n>", "Page size", parseInteger).option("--cursor <cursor>", "Pagination cursor").option("--json", "Force JSON output").action(
1327
+ async (dropId, commandOptions) => {
1328
+ const deps = await commandDeps(program, options, store, commandOptions);
1329
+ const result = await runDeploymentsList(dropId, commandOptions, deps);
1330
+ process.exitCode = result.exitCode;
1331
+ }
1332
+ );
1333
+ deployments.command("get <dropId> <deploymentId>").option("--json", "Force JSON output").action(
1334
+ async (dropId, deploymentId, commandOptions) => {
1335
+ const deps = await commandDeps(program, options, store, commandOptions);
1336
+ const result = await runDeploymentsGet(
1337
+ dropId,
1338
+ deploymentId,
1339
+ commandOptions,
1340
+ deps
1341
+ );
1342
+ process.exitCode = result.exitCode;
1343
+ }
1344
+ );
1345
+ program.command("doctor").description("Report CLI diagnostics").option("--json", "Force JSON output").action(async (commandOptions) => {
1346
+ const deps = await commandDeps(
1347
+ program,
1348
+ options,
1349
+ store,
1350
+ commandOptions
1351
+ );
1352
+ const result = await runDoctor(commandOptions, deps);
1353
+ process.exitCode = result.exitCode;
1354
+ });
1355
+ program.command("commands").description("Print machine-readable command metadata").option("--json", "Force JSON output").action(async (commandOptions) => {
1356
+ const deps = await commandDeps(
1357
+ program,
1358
+ options,
1359
+ store,
1360
+ commandOptions
1361
+ );
1362
+ const result = await runCommands(commandOptions, deps);
1363
+ process.exitCode = result.exitCode;
1364
+ });
1365
+ return program;
1366
+ }
1367
+ async function commandDeps(program, options, store, commandOptions) {
1368
+ const global = globalOptions(program, commandOptions);
1369
+ const context = createContext({
1370
+ global,
1371
+ ...options.env !== void 0 ? { env: options.env } : {},
1372
+ ...options.stdout !== void 0 ? { stdout: options.stdout } : {},
1373
+ ...options.stderr !== void 0 ? { stderr: options.stderr } : {},
1374
+ ...options.stdoutIsTTY !== void 0 ? { stdoutIsTTY: options.stdoutIsTTY } : {}
1375
+ });
1376
+ const credential = await resolveCredential({
1377
+ ...global.apiKey ? { apiKey: global.apiKey } : {},
1378
+ env: context.env,
1379
+ store
1380
+ });
1381
+ return {
1382
+ store,
1383
+ client: options.client ?? context.createClient(credential?.apiKey),
1384
+ ...global.apiKey ? { apiKey: global.apiKey } : {},
1385
+ env: context.env,
1386
+ stdout: context.stdout,
1387
+ stderr: context.stderr,
1388
+ outputMode: context.output.mode
1389
+ };
1390
+ }
1391
+ function globalOptions(program, commandOptions) {
1392
+ const opts = program.opts();
1393
+ return {
1394
+ ...opts.apiKey ? { apiKey: opts.apiKey } : {},
1395
+ ...opts.apiUrl ? { apiUrl: opts.apiUrl } : {},
1396
+ ...commandOptions.json !== void 0 ? { json: commandOptions.json } : opts.json !== void 0 ? { json: opts.json } : {},
1397
+ ...commandOptions.quiet !== void 0 ? { quiet: commandOptions.quiet } : opts.quiet !== void 0 ? { quiet: opts.quiet } : {}
1398
+ };
1399
+ }
1400
+ function toDropOptions(options) {
1401
+ return {
1402
+ ...options.slug ? { slug: options.slug } : {},
1403
+ ...options.title ? { title: options.title } : {},
1404
+ ...options.visibility === "public" || options.visibility === "unlisted" ? { visibility: options.visibility } : {},
1405
+ ...typeof options.password === "string" ? { password: options.password } : {},
1406
+ ...options.password === false || options.noPassword ? { noPassword: true } : {},
1407
+ ...options.noindex ? { noindex: true } : {},
1408
+ ...options.index ? { index: true } : {},
1409
+ ...options.expiresAt ? { expiresAt: options.expiresAt } : {},
1410
+ ...options.metadata ? { metadata: options.metadata } : {},
1411
+ ...options.metadataFile ? { metadataFile: options.metadataFile } : {},
1412
+ ...options.entry ? { entry: options.entry } : {},
1413
+ ...options.contentType ? { contentType: options.contentType } : {},
1414
+ ...options.path ? { path: options.path } : {},
1415
+ ...options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {},
1416
+ ...options.json !== void 0 ? { json: options.json } : {},
1417
+ ...options.quiet !== void 0 ? { quiet: options.quiet } : {},
1418
+ ...options.url !== void 0 ? { url: options.url } : {},
1419
+ ...options.ifRevision !== void 0 ? { ifRevision: options.ifRevision } : {},
1420
+ ...options.fromJson ? { fromJson: options.fromJson } : {},
1421
+ ...options.dryRun ? { dryRun: options.dryRun } : {}
1422
+ };
1423
+ }
1424
+ function parseInteger(value) {
1425
+ return Number.parseInt(value, 10);
1426
+ }
1427
+ async function resolveCommandInput(input, stdin) {
1428
+ if (input !== "-") return input;
1429
+ const buf = await readStdinBytes(stdin ?? process.stdin);
1430
+ return buf.toString("utf8");
1431
+ }
1432
+ async function resolvePublishInputs(inputs, stdin, stdinIsTTY) {
1433
+ if (inputs.length === 0) {
1434
+ if (stdinIsTTY) return void 0;
1435
+ const buf = await readStdinBytes(stdin ?? process.stdin);
1436
+ if (buf.length === 0) return void 0;
1437
+ return buf.toString("utf8");
1438
+ }
1439
+ if (inputs.length === 1) {
1440
+ if (inputs[0] === "-") {
1441
+ const buf = await readStdinBytes(stdin ?? process.stdin);
1442
+ return buf.toString("utf8");
1443
+ }
1444
+ return inputs[0];
1445
+ }
1446
+ const resolved = [];
1447
+ for (const input of inputs) {
1448
+ if (input === "-") {
1449
+ const buf = await readStdinBytes(stdin ?? process.stdin);
1450
+ const tmpPath = `/tmp/.dropthis-stdin-${Date.now()}`;
1451
+ const { writeFile: writeFile2 } = await import("fs/promises");
1452
+ await writeFile2(tmpPath, buf);
1453
+ resolved.push(tmpPath);
1454
+ } else {
1455
+ resolved.push(input);
1456
+ }
1457
+ }
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-"));
1462
+ for (const filePath of resolved) {
1463
+ const info = await fsStat(filePath);
1464
+ if (info.isDirectory()) {
1465
+ throw new Error(
1466
+ `Cannot mix directories with multiple file arguments. Use a single folder instead: dropthis publish ${filePath}`
1467
+ );
1468
+ }
1469
+ const destName = pathBasename(filePath);
1470
+ await copyFile(filePath, join2(tmpDir, destName));
1471
+ }
1472
+ return tmpDir;
1473
+ }
1474
+ async function readStdinBytes(stdin) {
1475
+ const chunks = [];
1476
+ for await (const chunk of stdin) {
1477
+ chunks.push(
1478
+ typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk)
1479
+ );
1480
+ }
1481
+ return Buffer.concat(chunks);
1482
+ }
1483
+
1484
+ // src/cli.ts
1485
+ buildProgram({ store: createCredentialStore() }).parseAsync(process.argv).catch((error) => {
1486
+ console.error(error);
1487
+ process.exitCode = 1;
1488
+ });
1489
+ //# sourceMappingURL=cli.cjs.map