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