@dropthis/cli 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -113
- package/dist/cli.cjs +384 -229
- package/dist/cli.cjs.map +1 -1
- package/package.json +1 -1
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,56 +112,187 @@ 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({ env: deps.env, store: deps.store }) === null) {
|
|
230
|
+
return writeAuthError(deps);
|
|
231
|
+
}
|
|
232
|
+
const result = await deps.client.account.get();
|
|
233
|
+
if (result.error) {
|
|
234
|
+
return writeApiErrorSimple(deps, result.error.message);
|
|
235
|
+
}
|
|
236
|
+
const data = result.data;
|
|
237
|
+
const pairs = [];
|
|
238
|
+
if (data?.id) pairs.push(["ID", String(data.id)]);
|
|
239
|
+
if (data?.email) pairs.push(["Email", String(data.email)]);
|
|
240
|
+
if (data?.plan) pairs.push(["Plan", String(data.plan)]);
|
|
241
|
+
writeKv(deps, pairs, { ok: true, account: result.data });
|
|
134
242
|
return { exitCode: 0 };
|
|
135
243
|
}
|
|
136
244
|
|
|
137
245
|
// src/commands/api-keys.ts
|
|
138
246
|
async function runApiKeysCreate(input, deps) {
|
|
139
|
-
if (!await hasCredential(deps)) return
|
|
247
|
+
if (!await hasCredential(deps)) return writeAuthError(deps);
|
|
140
248
|
const result = await deps.client.apiKeys.create({ label: input.label });
|
|
141
|
-
if (result.error) return
|
|
142
|
-
|
|
143
|
-
|
|
249
|
+
if (result.error) return writeApiErrorSimple(deps, result.error.message);
|
|
250
|
+
const data = result.data;
|
|
251
|
+
const pairs = [];
|
|
252
|
+
if (data.id) pairs.push(["ID", String(data.id)]);
|
|
253
|
+
if (data.key) pairs.push(["Key", String(data.key)]);
|
|
254
|
+
if (data.last4) pairs.push(["Last 4", String(data.last4)]);
|
|
255
|
+
writeKv(deps, pairs, { ok: true, api_key: result.data });
|
|
144
256
|
return { exitCode: 0 };
|
|
145
257
|
}
|
|
146
258
|
async function runApiKeysList(_input, deps) {
|
|
147
|
-
if (!await hasCredential(deps)) return
|
|
259
|
+
if (!await hasCredential(deps)) return writeAuthError(deps);
|
|
148
260
|
const result = await deps.client.apiKeys.list();
|
|
149
|
-
if (result.error) return
|
|
150
|
-
|
|
151
|
-
|
|
261
|
+
if (result.error) return writeApiErrorSimple(deps, result.error.message);
|
|
262
|
+
const raw = listItems(result.data);
|
|
263
|
+
const items = Array.isArray(raw) ? raw : void 0;
|
|
264
|
+
if (deps.outputMode === "human") {
|
|
265
|
+
if (!items || items.length === 0) {
|
|
266
|
+
writeEmpty(deps, "No API keys found.", { ok: true, api_keys: [] });
|
|
267
|
+
} else {
|
|
268
|
+
for (const item of items) {
|
|
269
|
+
deps.stdout(
|
|
270
|
+
`${item.id ?? ""} ${item.label ?? ""} ...${item.last4 ?? ""}
|
|
152
271
|
`
|
|
153
|
-
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
writeJson(deps, { ok: true, api_keys: items ?? raw });
|
|
277
|
+
}
|
|
154
278
|
return { exitCode: 0 };
|
|
155
279
|
}
|
|
156
280
|
async function runApiKeysDelete(keyId, input, deps) {
|
|
157
281
|
if (!input.yes && input.interactive === false) {
|
|
158
|
-
return
|
|
282
|
+
return writeError(
|
|
283
|
+
deps,
|
|
284
|
+
"invalid_usage",
|
|
285
|
+
"Pass --yes to delete in non-interactive mode."
|
|
286
|
+
);
|
|
159
287
|
}
|
|
160
|
-
if (!await hasCredential(deps)) return
|
|
288
|
+
if (!await hasCredential(deps)) return writeAuthError(deps);
|
|
161
289
|
const result = await deps.client.apiKeys.delete(keyId);
|
|
162
|
-
if (result?.error) return
|
|
163
|
-
deps
|
|
164
|
-
|
|
290
|
+
if (result?.error) return writeApiErrorSimple(deps, result.error.message);
|
|
291
|
+
writeHumanOrJson(deps, success(`Deleted ${keyId}`), {
|
|
292
|
+
ok: true,
|
|
293
|
+
deleted: true,
|
|
294
|
+
id: keyId
|
|
295
|
+
});
|
|
165
296
|
return { exitCode: 0 };
|
|
166
297
|
}
|
|
167
298
|
async function hasCredential(deps) {
|
|
@@ -176,23 +307,6 @@ function listItems(data) {
|
|
|
176
307
|
}
|
|
177
308
|
return data;
|
|
178
309
|
}
|
|
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
310
|
|
|
197
311
|
// src/commands/commands.ts
|
|
198
312
|
var COMMANDS = [
|
|
@@ -290,31 +404,51 @@ async function runCommands(_input, deps) {
|
|
|
290
404
|
|
|
291
405
|
// src/commands/deployments.ts
|
|
292
406
|
async function runDeploymentsList(dropId, input, deps) {
|
|
293
|
-
if (!await hasCredential2(deps)) return
|
|
407
|
+
if (!await hasCredential2(deps)) return writeAuthError(deps);
|
|
294
408
|
const params = {
|
|
295
409
|
...input.limit !== void 0 ? { limit: input.limit } : {},
|
|
296
410
|
...input.cursor ? { cursor: input.cursor } : {}
|
|
297
411
|
};
|
|
298
412
|
const result = await deps.client.deployments.list(dropId, params);
|
|
299
413
|
if (result.error) {
|
|
300
|
-
deps
|
|
301
|
-
|
|
302
|
-
|
|
414
|
+
return writeApiError(deps, result.error);
|
|
415
|
+
}
|
|
416
|
+
const spread = spreadData(result.data);
|
|
417
|
+
const items = spread.deployments ?? spread.data ?? result.data;
|
|
418
|
+
if (deps.outputMode === "human") {
|
|
419
|
+
if (!items || Array.isArray(items) && items.length === 0) {
|
|
420
|
+
writeEmpty(deps, "No deployments found.", {
|
|
421
|
+
ok: true,
|
|
422
|
+
...spread
|
|
423
|
+
});
|
|
424
|
+
} else if (Array.isArray(items)) {
|
|
425
|
+
for (const item of items) {
|
|
426
|
+
deps.stdout(
|
|
427
|
+
`${item.id ?? ""} rev ${item.revision ?? "?"} ${item.created_at ?? ""}
|
|
428
|
+
`
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
} else {
|
|
432
|
+
writeJson(deps, { ok: true, ...spread });
|
|
433
|
+
}
|
|
434
|
+
} else {
|
|
435
|
+
writeJson(deps, { ok: true, ...spread });
|
|
303
436
|
}
|
|
304
|
-
deps.stdout(`${JSON.stringify({ ok: true, ...spreadData(result.data) })}
|
|
305
|
-
`);
|
|
306
437
|
return { exitCode: 0 };
|
|
307
438
|
}
|
|
308
439
|
async function runDeploymentsGet(dropId, deploymentId, _input, deps) {
|
|
309
|
-
if (!await hasCredential2(deps)) return
|
|
440
|
+
if (!await hasCredential2(deps)) return writeAuthError(deps);
|
|
310
441
|
const result = await deps.client.deployments.get(dropId, deploymentId);
|
|
311
442
|
if (result.error) {
|
|
312
|
-
deps
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
443
|
+
return writeApiError(deps, result.error);
|
|
444
|
+
}
|
|
445
|
+
const data = result.data;
|
|
446
|
+
const pairs = [];
|
|
447
|
+
if (data.id) pairs.push(["ID", String(data.id)]);
|
|
448
|
+
if (data.revision !== void 0)
|
|
449
|
+
pairs.push(["Revision", String(data.revision)]);
|
|
450
|
+
if (data.created_at) pairs.push(["Created", String(data.created_at)]);
|
|
451
|
+
writeKv(deps, pairs, { ok: true, deployment: result.data });
|
|
318
452
|
return { exitCode: 0 };
|
|
319
453
|
}
|
|
320
454
|
async function hasCredential2(deps) {
|
|
@@ -324,13 +458,6 @@ async function hasCredential2(deps) {
|
|
|
324
458
|
store: deps.store
|
|
325
459
|
}) !== null;
|
|
326
460
|
}
|
|
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
461
|
function spreadData(data) {
|
|
335
462
|
return data && typeof data === "object" ? data : { data };
|
|
336
463
|
}
|
|
@@ -342,15 +469,19 @@ async function runDoctor(_input, deps) {
|
|
|
342
469
|
env: deps.env,
|
|
343
470
|
store: deps.store
|
|
344
471
|
});
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
472
|
+
const authSource = credential?.source ?? "missing";
|
|
473
|
+
const storageBackend = stored?.storage ?? "none";
|
|
474
|
+
const pairs = [
|
|
475
|
+
["Version", "0.1.0"],
|
|
476
|
+
["Auth", authSource],
|
|
477
|
+
["Storage", storageBackend]
|
|
478
|
+
];
|
|
479
|
+
writeKv(deps, pairs, {
|
|
480
|
+
ok: true,
|
|
481
|
+
version: "0.1.0",
|
|
482
|
+
auth: { source: authSource },
|
|
483
|
+
storage: { backend: storageBackend }
|
|
484
|
+
});
|
|
354
485
|
return { exitCode: 0 };
|
|
355
486
|
}
|
|
356
487
|
|
|
@@ -396,52 +527,82 @@ function parseJsonObject(value, label) {
|
|
|
396
527
|
|
|
397
528
|
// src/commands/drops.ts
|
|
398
529
|
async function runDropsList(input, deps) {
|
|
399
|
-
if (!await hasCredential3(deps)) return
|
|
530
|
+
if (!await hasCredential3(deps)) return writeAuthError(deps);
|
|
400
531
|
const params = {
|
|
401
532
|
...input.limit !== void 0 ? { limit: input.limit } : {},
|
|
402
533
|
...input.cursor ? { cursor: input.cursor } : {}
|
|
403
534
|
};
|
|
404
535
|
const result = await deps.client.drops.list(params);
|
|
405
|
-
if (result.error) return
|
|
406
|
-
|
|
407
|
-
|
|
536
|
+
if (result.error) return writeApiErrorSimple(deps, result.error.message);
|
|
537
|
+
const raw = listItems2(result.data);
|
|
538
|
+
const items = Array.isArray(raw) ? raw : void 0;
|
|
539
|
+
if (deps.outputMode === "human") {
|
|
540
|
+
if (!items || items.length === 0) {
|
|
541
|
+
writeEmpty(deps, "No drops found.", { ok: true, drops: [] });
|
|
542
|
+
} else {
|
|
543
|
+
for (const item of items) {
|
|
544
|
+
deps.stdout(
|
|
545
|
+
`${item.id ?? ""} ${item.title ?? ""} ${item.url ? url(String(item.url)) : ""}
|
|
408
546
|
`
|
|
409
|
-
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
} else {
|
|
551
|
+
writeJson(deps, { ok: true, drops: items ?? raw });
|
|
552
|
+
}
|
|
410
553
|
return { exitCode: 0 };
|
|
411
554
|
}
|
|
412
555
|
async function runDropsGet(dropId, _input, deps) {
|
|
413
|
-
if (!await hasCredential3(deps)) return
|
|
556
|
+
if (!await hasCredential3(deps)) return writeAuthError(deps);
|
|
414
557
|
const result = await deps.client.drops.get(dropId);
|
|
415
|
-
if (result.error) return
|
|
416
|
-
|
|
417
|
-
|
|
558
|
+
if (result.error) return writeApiErrorSimple(deps, result.error.message);
|
|
559
|
+
const data = result.data;
|
|
560
|
+
const pairs = [];
|
|
561
|
+
if (data.id) pairs.push(["ID", String(data.id)]);
|
|
562
|
+
if (data.url) pairs.push(["URL", url(String(data.url))]);
|
|
563
|
+
if (data.title) pairs.push(["Title", String(data.title)]);
|
|
564
|
+
if (data.visibility) pairs.push(["Visibility", String(data.visibility)]);
|
|
565
|
+
if (data.revision !== void 0)
|
|
566
|
+
pairs.push(["Revision", String(data.revision)]);
|
|
567
|
+
if (data.created_at) pairs.push(["Created", String(data.created_at)]);
|
|
568
|
+
writeKv(deps, pairs, { ok: true, drop: result.data });
|
|
418
569
|
return { exitCode: 0 };
|
|
419
570
|
}
|
|
420
571
|
async function runDropsUpdate(dropId, input, deps) {
|
|
421
|
-
if (!await hasCredential3(deps)) return
|
|
572
|
+
if (!await hasCredential3(deps)) return writeAuthError(deps);
|
|
422
573
|
try {
|
|
423
574
|
const options = await parseDropOptions(input);
|
|
424
575
|
const result = await deps.client.drops.update(dropId, options);
|
|
425
|
-
if (result.error) return
|
|
426
|
-
deps
|
|
427
|
-
|
|
576
|
+
if (result.error) return writeApiErrorSimple(deps, result.error.message);
|
|
577
|
+
writeHumanOrJson(deps, success(`Updated ${dropId}`), {
|
|
578
|
+
ok: true,
|
|
579
|
+
drop: result.data
|
|
580
|
+
});
|
|
428
581
|
return { exitCode: 0 };
|
|
429
|
-
} catch (
|
|
430
|
-
return
|
|
582
|
+
} catch (error2) {
|
|
583
|
+
return writeError(
|
|
431
584
|
deps,
|
|
432
|
-
|
|
585
|
+
"invalid_usage",
|
|
586
|
+
error2 instanceof Error ? error2.message : "Invalid drop options."
|
|
433
587
|
);
|
|
434
588
|
}
|
|
435
589
|
}
|
|
436
590
|
async function runDropsDelete(dropId, input, deps) {
|
|
437
591
|
if (!input.yes && input.interactive === false) {
|
|
438
|
-
return
|
|
592
|
+
return writeError(
|
|
593
|
+
deps,
|
|
594
|
+
"invalid_usage",
|
|
595
|
+
"Pass --yes to delete in non-interactive mode."
|
|
596
|
+
);
|
|
439
597
|
}
|
|
440
|
-
if (!await hasCredential3(deps)) return
|
|
598
|
+
if (!await hasCredential3(deps)) return writeAuthError(deps);
|
|
441
599
|
const result = await deps.client.drops.delete(dropId);
|
|
442
|
-
if (result?.error) return
|
|
443
|
-
deps
|
|
444
|
-
|
|
600
|
+
if (result?.error) return writeApiErrorSimple(deps, result.error.message);
|
|
601
|
+
writeHumanOrJson(deps, success(`Deleted ${dropId}`), {
|
|
602
|
+
ok: true,
|
|
603
|
+
deleted: true,
|
|
604
|
+
id: dropId
|
|
605
|
+
});
|
|
445
606
|
return { exitCode: 0 };
|
|
446
607
|
}
|
|
447
608
|
async function hasCredential3(deps) {
|
|
@@ -456,23 +617,6 @@ function listItems2(data) {
|
|
|
456
617
|
}
|
|
457
618
|
return data;
|
|
458
619
|
}
|
|
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
620
|
|
|
477
621
|
// src/commands/login.ts
|
|
478
622
|
var prompts = __toESM(require("@clack/prompts"), 1);
|
|
@@ -592,12 +736,9 @@ async function runLogout(input, deps) {
|
|
|
592
736
|
}
|
|
593
737
|
await deps.store.clear();
|
|
594
738
|
if (revokeError) {
|
|
595
|
-
deps
|
|
596
|
-
`);
|
|
597
|
-
return { exitCode: exitCodeFor("api_error") };
|
|
739
|
+
return writeApiErrorSimple(deps, revokeError);
|
|
598
740
|
}
|
|
599
|
-
deps
|
|
600
|
-
`);
|
|
741
|
+
writeHumanOrJson(deps, success("Logged out."), { ok: true });
|
|
601
742
|
return { exitCode: 0 };
|
|
602
743
|
}
|
|
603
744
|
|
|
@@ -606,14 +747,40 @@ var import_node_crypto = require("crypto");
|
|
|
606
747
|
var import_promises3 = require("fs/promises");
|
|
607
748
|
var import_node_path = require("path");
|
|
608
749
|
|
|
750
|
+
// src/spinner.ts
|
|
751
|
+
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
752
|
+
var frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
753
|
+
function createSpinner(message, stderr) {
|
|
754
|
+
let i = 0;
|
|
755
|
+
const timer = setInterval(() => {
|
|
756
|
+
stderr(`\r\x1B[2K${import_picocolors2.default.cyan(frames[i % frames.length])} ${message}`);
|
|
757
|
+
i++;
|
|
758
|
+
}, 80);
|
|
759
|
+
return {
|
|
760
|
+
stop(finalMessage) {
|
|
761
|
+
clearInterval(timer);
|
|
762
|
+
const text2 = finalMessage ?? message;
|
|
763
|
+
stderr(`\r\x1B[2K${import_picocolors2.default.green(symbols.success)} ${text2}
|
|
764
|
+
`);
|
|
765
|
+
},
|
|
766
|
+
fail() {
|
|
767
|
+
clearInterval(timer);
|
|
768
|
+
stderr("\r\x1B[2K");
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
function shouldSpin(outputMode) {
|
|
773
|
+
return outputMode === "human" && process.stderr.isTTY === true && !process.env.CI;
|
|
774
|
+
}
|
|
775
|
+
|
|
609
776
|
// src/commands/json_body.ts
|
|
610
777
|
var import_promises2 = require("fs/promises");
|
|
611
778
|
async function readJsonObjectFile(path) {
|
|
612
779
|
let parsed;
|
|
613
780
|
try {
|
|
614
781
|
parsed = JSON.parse(await (0, import_promises2.readFile)(path, "utf8"));
|
|
615
|
-
} catch (
|
|
616
|
-
const message =
|
|
782
|
+
} catch (error2) {
|
|
783
|
+
const message = error2 instanceof Error ? error2.message : "Unknown parse error";
|
|
617
784
|
throw new Error(`Invalid JSON body in ${path}: ${message}`);
|
|
618
785
|
}
|
|
619
786
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -631,16 +798,13 @@ async function runPublish(input, raw, deps) {
|
|
|
631
798
|
store: deps.store
|
|
632
799
|
});
|
|
633
800
|
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") };
|
|
801
|
+
return writeAuthError(deps);
|
|
639
802
|
}
|
|
640
803
|
if (raw.dryRun) {
|
|
641
804
|
return handlePublishDryRun(input, raw, deps);
|
|
642
805
|
}
|
|
643
806
|
const singleInput = Array.isArray(input) ? input[0] : input;
|
|
807
|
+
const spin = shouldSpin(deps.outputMode) ? createSpinner("Publishing\u2026", deps.stderr) : void 0;
|
|
644
808
|
try {
|
|
645
809
|
if (raw.fromJson && singleInput) {
|
|
646
810
|
throw new Error("Use either <input> or --from-json, not both.");
|
|
@@ -653,32 +817,28 @@ async function runPublish(input, raw, deps) {
|
|
|
653
817
|
throw new Error("Publish requires <input> or --from-json.");
|
|
654
818
|
})();
|
|
655
819
|
if (result.error) {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
return { exitCode: exitCodeFor("api_error") };
|
|
820
|
+
spin?.fail();
|
|
821
|
+
return writeApiError(deps, result.error);
|
|
659
822
|
}
|
|
823
|
+
spin?.stop("Published");
|
|
660
824
|
if (raw.url) deps.stdout(`${result.data.url}
|
|
661
825
|
`);
|
|
662
|
-
else
|
|
663
|
-
deps.stdout(`Published: ${result.data.url}
|
|
664
|
-
`);
|
|
665
|
-
else deps.stdout(`${JSON.stringify({ ok: true, drop: result.data })}
|
|
666
|
-
`);
|
|
826
|
+
else writeResult(deps, result.data.url, "Published", result.data);
|
|
667
827
|
return { exitCode: 0 };
|
|
668
|
-
} catch (
|
|
669
|
-
const message =
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
return { exitCode: exitCodeFor("invalid_usage") };
|
|
828
|
+
} catch (error2) {
|
|
829
|
+
const message = error2 instanceof Error ? error2.message : "Publish failed.";
|
|
830
|
+
spin?.fail();
|
|
831
|
+
return writeError(deps, "invalid_usage", message);
|
|
673
832
|
}
|
|
674
833
|
}
|
|
675
834
|
async function handlePublishDryRun(input, raw, deps) {
|
|
676
835
|
if (raw.url) {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
836
|
+
return writeError(
|
|
837
|
+
deps,
|
|
838
|
+
"invalid_usage",
|
|
839
|
+
"--url and --dry-run cannot be combined.",
|
|
840
|
+
"Remove --url; dry-run outputs JSON describing what would be published."
|
|
680
841
|
);
|
|
681
|
-
return { exitCode: exitCodeFor("invalid_usage") };
|
|
682
842
|
}
|
|
683
843
|
try {
|
|
684
844
|
if (raw.fromJson) {
|
|
@@ -709,12 +869,10 @@ async function handlePublishDryRun(input, raw, deps) {
|
|
|
709
869
|
deps.stdout(`${JSON.stringify(output, null, 2)}
|
|
710
870
|
`);
|
|
711
871
|
return { exitCode: 0 };
|
|
712
|
-
} catch (
|
|
713
|
-
const code = isFileSystemError(
|
|
714
|
-
const message =
|
|
715
|
-
deps
|
|
716
|
-
`);
|
|
717
|
-
return { exitCode: exitCodeFor(code) };
|
|
872
|
+
} catch (error2) {
|
|
873
|
+
const code = isFileSystemError(error2) ? "local_input_error" : "invalid_usage";
|
|
874
|
+
const message = error2 instanceof Error ? error2.message : "Dry-run failed.";
|
|
875
|
+
return writeError(deps, code, message);
|
|
718
876
|
}
|
|
719
877
|
}
|
|
720
878
|
async function dryRunMultiFile(inputs, options, deps) {
|
|
@@ -724,18 +882,18 @@ async function dryRunMultiFile(inputs, options, deps) {
|
|
|
724
882
|
try {
|
|
725
883
|
info = await (0, import_promises3.stat)(filePath);
|
|
726
884
|
} catch {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
885
|
+
return writeError(
|
|
886
|
+
deps,
|
|
887
|
+
"local_input_error",
|
|
888
|
+
`File not found: ${filePath}`
|
|
730
889
|
);
|
|
731
|
-
return { exitCode: exitCodeFor("local_input_error") };
|
|
732
890
|
}
|
|
733
891
|
if (info.isDirectory()) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
892
|
+
return writeError(
|
|
893
|
+
deps,
|
|
894
|
+
"local_input_error",
|
|
895
|
+
`Cannot mix directories with multiple file arguments. Use a single folder instead: dropthis publish ${filePath}`
|
|
737
896
|
);
|
|
738
|
-
return { exitCode: exitCodeFor("local_input_error") };
|
|
739
897
|
}
|
|
740
898
|
files.push({
|
|
741
899
|
path: (0, import_node_path.basename)(filePath),
|
|
@@ -744,11 +902,11 @@ async function dryRunMultiFile(inputs, options, deps) {
|
|
|
744
902
|
});
|
|
745
903
|
}
|
|
746
904
|
if (files.length > MAX_BUNDLE_FILE_COUNT) {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
905
|
+
return writeError(
|
|
906
|
+
deps,
|
|
907
|
+
"local_input_error",
|
|
908
|
+
`Bundle contains ${files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT}.`
|
|
750
909
|
);
|
|
751
|
-
return { exitCode: exitCodeFor("local_input_error") };
|
|
752
910
|
}
|
|
753
911
|
const entry = options.entry ?? files.find((f) => f.path === "index.html")?.path ?? files[0]?.path ?? null;
|
|
754
912
|
const totalBytes = files.reduce((sum, f) => sum + f.sizeBytes, 0);
|
|
@@ -831,9 +989,9 @@ function mimeForPath(filePath) {
|
|
|
831
989
|
};
|
|
832
990
|
return types[ext] ?? "application/octet-stream";
|
|
833
991
|
}
|
|
834
|
-
function isFileSystemError(
|
|
835
|
-
if (!(
|
|
836
|
-
const code =
|
|
992
|
+
function isFileSystemError(error2) {
|
|
993
|
+
if (!(error2 instanceof Error)) return false;
|
|
994
|
+
const code = error2.code;
|
|
837
995
|
return code === "ENOENT" || code === "ENOTDIR" || code === "ELIMIT";
|
|
838
996
|
}
|
|
839
997
|
function withPublishIdempotency(options) {
|
|
@@ -849,15 +1007,12 @@ async function runUpdate(target, input, raw, deps) {
|
|
|
849
1007
|
store: deps.store
|
|
850
1008
|
});
|
|
851
1009
|
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") };
|
|
1010
|
+
return writeAuthError(deps);
|
|
857
1011
|
}
|
|
858
1012
|
if (raw.dryRun) {
|
|
859
1013
|
return handleUpdateDryRun(target, input, raw, deps);
|
|
860
1014
|
}
|
|
1015
|
+
const spin = shouldSpin(deps.outputMode) ? createSpinner("Updating\u2026", deps.stderr) : void 0;
|
|
861
1016
|
try {
|
|
862
1017
|
assertDropId(target);
|
|
863
1018
|
if (raw.fromJson && input) {
|
|
@@ -881,32 +1036,28 @@ async function runUpdate(target, input, raw, deps) {
|
|
|
881
1036
|
})
|
|
882
1037
|
) : await deps.client.update(target, parsed, revisionOptions);
|
|
883
1038
|
if (result.error) {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
return { exitCode: exitCodeFor("api_error") };
|
|
1039
|
+
spin?.fail();
|
|
1040
|
+
return writeApiError(deps, result.error);
|
|
887
1041
|
}
|
|
1042
|
+
spin?.stop("Updated");
|
|
888
1043
|
if (raw.url) deps.stdout(`${result.data.url}
|
|
889
1044
|
`);
|
|
890
|
-
else
|
|
891
|
-
deps.stdout(`Updated: ${result.data.url}
|
|
892
|
-
`);
|
|
893
|
-
else deps.stdout(`${JSON.stringify({ ok: true, drop: result.data })}
|
|
894
|
-
`);
|
|
1045
|
+
else writeResult(deps, result.data.url, "Updated", result.data);
|
|
895
1046
|
return { exitCode: 0 };
|
|
896
|
-
} catch (
|
|
897
|
-
const message =
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
return { exitCode: exitCodeFor("invalid_usage") };
|
|
1047
|
+
} catch (error2) {
|
|
1048
|
+
const message = error2 instanceof Error ? error2.message : "Update failed.";
|
|
1049
|
+
spin?.fail();
|
|
1050
|
+
return writeError(deps, "invalid_usage", message);
|
|
901
1051
|
}
|
|
902
1052
|
}
|
|
903
1053
|
async function handleUpdateDryRun(target, input, raw, deps) {
|
|
904
1054
|
if (raw.url) {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1055
|
+
return writeError(
|
|
1056
|
+
deps,
|
|
1057
|
+
"invalid_usage",
|
|
1058
|
+
"--url and --dry-run cannot be combined.",
|
|
1059
|
+
"Remove --url; dry-run outputs JSON describing what would be published."
|
|
908
1060
|
);
|
|
909
|
-
return { exitCode: exitCodeFor("invalid_usage") };
|
|
910
1061
|
}
|
|
911
1062
|
try {
|
|
912
1063
|
assertDropId(target);
|
|
@@ -947,12 +1098,10 @@ async function handleUpdateDryRun(target, input, raw, deps) {
|
|
|
947
1098
|
deps.stdout(`${JSON.stringify(output, null, 2)}
|
|
948
1099
|
`);
|
|
949
1100
|
return { exitCode: 0 };
|
|
950
|
-
} catch (
|
|
951
|
-
const code = isFileSystemError2(
|
|
952
|
-
const message =
|
|
953
|
-
deps
|
|
954
|
-
`);
|
|
955
|
-
return { exitCode: exitCodeFor(code) };
|
|
1101
|
+
} catch (error2) {
|
|
1102
|
+
const code = isFileSystemError2(error2) ? "local_input_error" : "invalid_usage";
|
|
1103
|
+
const message = error2 instanceof Error ? error2.message : "Dry-run failed.";
|
|
1104
|
+
return writeError(deps, code, message);
|
|
956
1105
|
}
|
|
957
1106
|
}
|
|
958
1107
|
var MAX_BUNDLE_FILE_COUNT2 = 200;
|
|
@@ -986,9 +1135,9 @@ function validateDryRunFileCount(output) {
|
|
|
986
1135
|
);
|
|
987
1136
|
}
|
|
988
1137
|
}
|
|
989
|
-
function isFileSystemError2(
|
|
990
|
-
if (!(
|
|
991
|
-
const code =
|
|
1138
|
+
function isFileSystemError2(error2) {
|
|
1139
|
+
if (!(error2 instanceof Error)) return false;
|
|
1140
|
+
const code = error2.code;
|
|
992
1141
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
993
1142
|
}
|
|
994
1143
|
function withUpdateIdempotency(options) {
|
|
@@ -1009,23 +1158,29 @@ async function runWhoami(_input, deps) {
|
|
|
1009
1158
|
store: deps.store
|
|
1010
1159
|
});
|
|
1011
1160
|
if (!credential) {
|
|
1012
|
-
deps.
|
|
1013
|
-
|
|
1161
|
+
writeHumanOrJson(deps, "Not authenticated. Run dropthis login.", {
|
|
1162
|
+
ok: true,
|
|
1163
|
+
authenticated: false
|
|
1164
|
+
});
|
|
1014
1165
|
return { exitCode: 0 };
|
|
1015
1166
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1167
|
+
const masked = maskKey(credential.apiKey);
|
|
1168
|
+
const pairs = [];
|
|
1169
|
+
if (credential.email) pairs.push(["Email", credential.email]);
|
|
1170
|
+
pairs.push(["Key", masked]);
|
|
1171
|
+
pairs.push(["Source", credential.source]);
|
|
1172
|
+
if (credential.accountId) pairs.push(["Account", credential.accountId]);
|
|
1173
|
+
const jsonEnvelope = {
|
|
1174
|
+
ok: true,
|
|
1175
|
+
authenticated: true,
|
|
1176
|
+
source: credential.source,
|
|
1177
|
+
masked_key: masked,
|
|
1178
|
+
...credential.keyId ? { key_id: credential.keyId } : {},
|
|
1179
|
+
...credential.keyLast4 ? { key_last4: credential.keyLast4 } : {},
|
|
1180
|
+
...credential.accountId ? { account_id: credential.accountId } : {},
|
|
1181
|
+
...credential.email ? { email: credential.email } : {}
|
|
1182
|
+
};
|
|
1183
|
+
writeKv(deps, pairs, jsonEnvelope);
|
|
1029
1184
|
return { exitCode: 0 };
|
|
1030
1185
|
}
|
|
1031
1186
|
|
|
@@ -1141,11 +1296,11 @@ function credentialPath(configDir) {
|
|
|
1141
1296
|
async function readJsonFile(path) {
|
|
1142
1297
|
try {
|
|
1143
1298
|
return JSON.parse(await (0, import_promises4.readFile)(path, "utf8"));
|
|
1144
|
-
} catch (
|
|
1145
|
-
if (
|
|
1299
|
+
} catch (error2) {
|
|
1300
|
+
if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
|
|
1146
1301
|
return null;
|
|
1147
1302
|
}
|
|
1148
|
-
throw
|
|
1303
|
+
throw error2;
|
|
1149
1304
|
}
|
|
1150
1305
|
}
|
|
1151
1306
|
async function writeCredentialFile(path, credential) {
|
|
@@ -1161,7 +1316,7 @@ async function writeCredentialFile(path, credential) {
|
|
|
1161
1316
|
function buildProgram(options = {}) {
|
|
1162
1317
|
const program = new import_commander.Command();
|
|
1163
1318
|
const store = options.store ?? new MemoryCredentialStore();
|
|
1164
|
-
program.name("dropthis").description("Publish anything online and get a URL back.").version("0.
|
|
1319
|
+
program.name("dropthis").description("Publish anything online and get a URL back.").version("0.2.0");
|
|
1165
1320
|
program.option("--api-key <key>", "Override API key for this invocation");
|
|
1166
1321
|
program.option("--api-url <url>", "Override API base URL");
|
|
1167
1322
|
program.option("--json", "Force JSON output");
|
|
@@ -1482,8 +1637,8 @@ async function readStdinBytes(stdin) {
|
|
|
1482
1637
|
}
|
|
1483
1638
|
|
|
1484
1639
|
// src/cli.ts
|
|
1485
|
-
buildProgram({ store: createCredentialStore() }).parseAsync(process.argv).catch((
|
|
1486
|
-
console.error(
|
|
1640
|
+
buildProgram({ store: createCredentialStore() }).parseAsync(process.argv).catch((error2) => {
|
|
1641
|
+
console.error(error2);
|
|
1487
1642
|
process.exitCode = 1;
|
|
1488
1643
|
});
|
|
1489
1644
|
//# sourceMappingURL=cli.cjs.map
|