@dropthis/cli 0.11.0 → 0.12.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 +10 -6
- package/dist/cli.cjs +392 -103
- package/dist/cli.cjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -202,10 +202,25 @@ function normalizeApiErrorCode(error2) {
|
|
|
202
202
|
if (error2.statusCode === 404) return "not_found";
|
|
203
203
|
return "api_error";
|
|
204
204
|
}
|
|
205
|
-
function nextActionForApiError(error2) {
|
|
205
|
+
function nextActionForApiError(error2, mode = "json") {
|
|
206
|
+
const cliHint = cliNextAction(error2);
|
|
207
|
+
if (mode === "human" && cliHint) return cliHint;
|
|
206
208
|
if (error2.suggestion) return error2.suggestion;
|
|
209
|
+
if (cliHint) return cliHint;
|
|
210
|
+
if (error2.statusCode === 422) {
|
|
211
|
+
return "Fix the input shown in the error detail and retry.";
|
|
212
|
+
}
|
|
213
|
+
if (error2.statusCode !== void 0 && error2.statusCode !== null && error2.statusCode >= 500) {
|
|
214
|
+
return "Retry the request with the same idempotency key, or contact support with the request id.";
|
|
215
|
+
}
|
|
216
|
+
return "Fix the request or retry after checking the drop state.";
|
|
217
|
+
}
|
|
218
|
+
function cliNextAction(error2) {
|
|
207
219
|
const uploadNextAction = error2.code ? UPLOAD_NEXT_ACTIONS[error2.code] : void 0;
|
|
208
220
|
if (uploadNextAction) return uploadNextAction;
|
|
221
|
+
if (error2.code === "network_error") {
|
|
222
|
+
return "Could not reach the dropthis API \u2014 check your network or DROPTHIS_API_URL.";
|
|
223
|
+
}
|
|
209
224
|
if (error2.code === "revision_conflict") {
|
|
210
225
|
return "Re-read the drop with dropthis get <drop-id>, merge your changes, and retry with --if-revision set to the current revision.";
|
|
211
226
|
}
|
|
@@ -218,13 +233,7 @@ function nextActionForApiError(error2) {
|
|
|
218
233
|
if (error2.statusCode === 413 || error2.code === "quota_exceeded") {
|
|
219
234
|
return "Reduce the upload size or upgrade the account limit.";
|
|
220
235
|
}
|
|
221
|
-
|
|
222
|
-
return "Fix the input shown in the error detail and retry.";
|
|
223
|
-
}
|
|
224
|
-
if (error2.statusCode !== void 0 && error2.statusCode !== null && error2.statusCode >= 500) {
|
|
225
|
-
return "Retry the request with the same idempotency key, or contact support with the request id.";
|
|
226
|
-
}
|
|
227
|
-
return "Fix the request or retry after checking the drop state.";
|
|
236
|
+
return void 0;
|
|
228
237
|
}
|
|
229
238
|
function exitCodeFor(code) {
|
|
230
239
|
if (code === "usage_error") return 2;
|
|
@@ -239,7 +248,7 @@ function exitCodeFor(code) {
|
|
|
239
248
|
|
|
240
249
|
// src/program.ts
|
|
241
250
|
var import_commander = require("commander");
|
|
242
|
-
var
|
|
251
|
+
var import_picocolors6 = __toESM(require("picocolors"), 1);
|
|
243
252
|
|
|
244
253
|
// src/auth.ts
|
|
245
254
|
async function resolveCredential(input) {
|
|
@@ -318,6 +327,10 @@ function writeResult(deps, link, verb, data) {
|
|
|
318
327
|
`${hint(`Vanity slug was taken \u2014 served at: ${w.detail ?? "see URL"}`)}
|
|
319
328
|
`
|
|
320
329
|
);
|
|
330
|
+
} else {
|
|
331
|
+
const text2 = typeof w.detail === "string" ? w.detail : typeof w.message === "string" ? w.message : typeof w.code === "string" ? w.code : void 0;
|
|
332
|
+
if (text2) deps.stderr(`${hint(text2)}
|
|
333
|
+
`);
|
|
321
334
|
}
|
|
322
335
|
}
|
|
323
336
|
}
|
|
@@ -339,6 +352,13 @@ function formatDropDetails(data) {
|
|
|
339
352
|
if (parts.length === 0) return "";
|
|
340
353
|
return ` ${import_picocolors2.default.dim(parts.join(" \xB7 "))}`;
|
|
341
354
|
}
|
|
355
|
+
function formatDate(iso) {
|
|
356
|
+
return iso.split("T")[0] ?? iso;
|
|
357
|
+
}
|
|
358
|
+
function formatDateTime(iso) {
|
|
359
|
+
const match = iso.match(/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})/);
|
|
360
|
+
return match ? `${match[1]} ${match[2]}` : iso;
|
|
361
|
+
}
|
|
342
362
|
function formatBytes(bytes) {
|
|
343
363
|
if (bytes < 1024) return `${bytes} B`;
|
|
344
364
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -384,7 +404,7 @@ function writeApiError(deps, details) {
|
|
|
384
404
|
if (deps.outputMode === "human") {
|
|
385
405
|
deps.stderr(`${error(details.message)}
|
|
386
406
|
`);
|
|
387
|
-
const nextAction = nextActionForApiError(details);
|
|
407
|
+
const nextAction = nextActionForApiError(details, "human");
|
|
388
408
|
if (nextAction) deps.stderr(`${hint(nextAction)}
|
|
389
409
|
`);
|
|
390
410
|
if (details.currentRevision !== void 0)
|
|
@@ -399,7 +419,12 @@ function writeApiError(deps, details) {
|
|
|
399
419
|
deps.stderr(`${JSON.stringify(apiErrorEnvelope(details))}
|
|
400
420
|
`);
|
|
401
421
|
}
|
|
402
|
-
return { exitCode: exitCodeFor(
|
|
422
|
+
return { exitCode: exitCodeFor(cliErrorCodeFor(details)) };
|
|
423
|
+
}
|
|
424
|
+
function cliErrorCodeFor(details) {
|
|
425
|
+
if (details.code === "network_error") return "network_error";
|
|
426
|
+
if (details.code === "file_not_found") return "local_input_error";
|
|
427
|
+
return "api_error";
|
|
403
428
|
}
|
|
404
429
|
function writeApiErrorSimple(deps, message) {
|
|
405
430
|
if (deps.outputMode === "human") {
|
|
@@ -507,6 +532,14 @@ async function runAccountDelete(input, deps) {
|
|
|
507
532
|
var prompts = __toESM(require("@clack/prompts"), 1);
|
|
508
533
|
|
|
509
534
|
// src/types.ts
|
|
535
|
+
function extractPagination(data) {
|
|
536
|
+
if (!data || typeof data !== "object") return void 0;
|
|
537
|
+
const rec = data;
|
|
538
|
+
if (!("nextCursor" in rec) && !("hasMore" in rec)) return void 0;
|
|
539
|
+
const nextCursor = typeof rec.nextCursor === "string" ? rec.nextCursor : null;
|
|
540
|
+
const hasMore = typeof rec.hasMore === "boolean" ? rec.hasMore : nextCursor !== null;
|
|
541
|
+
return { nextCursor, hasMore };
|
|
542
|
+
}
|
|
510
543
|
function unwrapListData(data, ...fallbackKeys) {
|
|
511
544
|
if (data && typeof data === "object") {
|
|
512
545
|
if ("data" in data) {
|
|
@@ -647,7 +680,7 @@ var COMMAND_ANNOTATIONS = {
|
|
|
647
680
|
},
|
|
648
681
|
deployments: {
|
|
649
682
|
auth: "required",
|
|
650
|
-
examples: ["dropthis deployments drop_abc --json"]
|
|
683
|
+
examples: ["dropthis deployments list drop_abc --json"]
|
|
651
684
|
},
|
|
652
685
|
"deployments list": {
|
|
653
686
|
auth: "required",
|
|
@@ -657,6 +690,36 @@ var COMMAND_ANNOTATIONS = {
|
|
|
657
690
|
auth: "required",
|
|
658
691
|
examples: ["dropthis deployments get drop_abc dep_abc --json"]
|
|
659
692
|
},
|
|
693
|
+
domains: {
|
|
694
|
+
auth: "required",
|
|
695
|
+
examples: ["dropthis domains list --json"]
|
|
696
|
+
},
|
|
697
|
+
"domains connect": {
|
|
698
|
+
auth: "required",
|
|
699
|
+
examples: [
|
|
700
|
+
"dropthis domains connect reports.example.com --mode path --json"
|
|
701
|
+
]
|
|
702
|
+
},
|
|
703
|
+
"domains list": {
|
|
704
|
+
auth: "required",
|
|
705
|
+
examples: ["dropthis domains list --json"]
|
|
706
|
+
},
|
|
707
|
+
"domains status": {
|
|
708
|
+
auth: "required",
|
|
709
|
+
examples: ["dropthis domains status reports.example.com --json"]
|
|
710
|
+
},
|
|
711
|
+
"domains verify": {
|
|
712
|
+
auth: "required",
|
|
713
|
+
examples: ["dropthis domains verify reports.example.com --wait --json"]
|
|
714
|
+
},
|
|
715
|
+
"domains update": {
|
|
716
|
+
auth: "required",
|
|
717
|
+
examples: ["dropthis domains update reports.example.com --default --json"]
|
|
718
|
+
},
|
|
719
|
+
"domains remove": {
|
|
720
|
+
auth: "required",
|
|
721
|
+
examples: ["dropthis domains remove reports.example.com --yes --json"]
|
|
722
|
+
},
|
|
660
723
|
"api-keys": {
|
|
661
724
|
auth: "required",
|
|
662
725
|
examples: ["dropthis api-keys create --label CI --json"]
|
|
@@ -738,6 +801,7 @@ async function runDeploymentsList(dropId, input, deps) {
|
|
|
738
801
|
return writeApiError(deps, result.error);
|
|
739
802
|
}
|
|
740
803
|
const spread = spreadData(result.data);
|
|
804
|
+
const page = extractPagination(result.data);
|
|
741
805
|
const items = spread.deployments ?? spread.data ?? result.data;
|
|
742
806
|
if (deps.outputMode === "human") {
|
|
743
807
|
if (!items || Array.isArray(items) && items.length === 0) {
|
|
@@ -747,8 +811,15 @@ async function runDeploymentsList(dropId, input, deps) {
|
|
|
747
811
|
});
|
|
748
812
|
} else if (Array.isArray(items)) {
|
|
749
813
|
for (const item of items) {
|
|
814
|
+
const created = typeof item.createdAt === "string" ? formatDateTime(item.createdAt) : "";
|
|
750
815
|
deps.stdout(
|
|
751
|
-
`${item.id ?? ""} rev ${item.revision ?? "?"} ${
|
|
816
|
+
`${item.id ?? ""} rev ${item.revision ?? "?"} ${created}
|
|
817
|
+
`
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
if (page?.hasMore && page.nextCursor) {
|
|
821
|
+
deps.stdout(
|
|
822
|
+
`${hint(`More deployments: dropthis deployments list ${dropId} --cursor ${page.nextCursor}`)}
|
|
752
823
|
`
|
|
753
824
|
);
|
|
754
825
|
}
|
|
@@ -756,7 +827,11 @@ async function runDeploymentsList(dropId, input, deps) {
|
|
|
756
827
|
writeJson(deps, { ok: true, ...spread });
|
|
757
828
|
}
|
|
758
829
|
} else {
|
|
759
|
-
writeJson(deps, {
|
|
830
|
+
writeJson(deps, {
|
|
831
|
+
ok: true,
|
|
832
|
+
...spread,
|
|
833
|
+
...page ? { next_cursor: page.nextCursor, has_more: page.hasMore } : {}
|
|
834
|
+
});
|
|
760
835
|
}
|
|
761
836
|
return { exitCode: 0 };
|
|
762
837
|
}
|
|
@@ -779,7 +854,8 @@ async function runDeploymentsGet(dropId, deploymentId, _input, deps) {
|
|
|
779
854
|
if (data.id) pairs.push(["ID", String(data.id)]);
|
|
780
855
|
if (data.revision !== void 0)
|
|
781
856
|
pairs.push(["Revision", String(data.revision)]);
|
|
782
|
-
if (data.createdAt)
|
|
857
|
+
if (data.createdAt)
|
|
858
|
+
pairs.push(["Created", formatDateTime(String(data.createdAt))]);
|
|
783
859
|
writeKv(deps, pairs, { ok: true, deployment: result.data });
|
|
784
860
|
return { exitCode: 0 };
|
|
785
861
|
}
|
|
@@ -797,13 +873,13 @@ async function runDoctor(_input, deps) {
|
|
|
797
873
|
const authSource = credential?.source ?? "missing";
|
|
798
874
|
const storageBackend = stored?.storage ?? "none";
|
|
799
875
|
const pairs = [
|
|
800
|
-
["Version", "0.
|
|
876
|
+
["Version", "0.12.0"],
|
|
801
877
|
["Auth", authSource],
|
|
802
878
|
["Storage", storageBackend]
|
|
803
879
|
];
|
|
804
880
|
writeKv(deps, pairs, {
|
|
805
881
|
ok: true,
|
|
806
|
-
version: "0.
|
|
882
|
+
version: "0.12.0",
|
|
807
883
|
auth: { source: authSource },
|
|
808
884
|
storage: { backend: storageBackend }
|
|
809
885
|
});
|
|
@@ -811,6 +887,7 @@ async function runDoctor(_input, deps) {
|
|
|
811
887
|
}
|
|
812
888
|
|
|
813
889
|
// src/commands/domains.ts
|
|
890
|
+
var prompts2 = __toESM(require("@clack/prompts"), 1);
|
|
814
891
|
var import_picocolors3 = __toESM(require("picocolors"), 1);
|
|
815
892
|
function formatDnsTable(records) {
|
|
816
893
|
if (records.length === 0) return "";
|
|
@@ -848,11 +925,32 @@ function formatDnsTable(records) {
|
|
|
848
925
|
}
|
|
849
926
|
return rows.join("\n");
|
|
850
927
|
}
|
|
851
|
-
function
|
|
852
|
-
if (
|
|
928
|
+
function renderNextHint(h, hostname, maxRetryAfter) {
|
|
929
|
+
if (h.action === "verify") {
|
|
930
|
+
const recheck = maxRetryAfter > 0 ? ` (re-checks every ${maxRetryAfter}s)` : "";
|
|
931
|
+
return `Run: dropthis domains verify ${hostname} --wait${recheck}`;
|
|
932
|
+
}
|
|
933
|
+
if (h.action === "publish") {
|
|
934
|
+
return `Run: dropthis publish <file> --domain ${hostname}`;
|
|
935
|
+
}
|
|
936
|
+
return h.message;
|
|
937
|
+
}
|
|
938
|
+
function duplicatesRecordHint(h, dns) {
|
|
939
|
+
if (h.action !== "dns") return false;
|
|
940
|
+
return dns.some(
|
|
941
|
+
(r) => r.hint && h.message.includes(r.name) && h.message.includes(r.value)
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
function printNextHints(deps, hints, hostname, dns) {
|
|
945
|
+
const lines = hints.filter((h) => !duplicatesRecordHint(h, dns));
|
|
946
|
+
if (lines.length === 0) return;
|
|
947
|
+
const maxRetryAfter = dns.reduce(
|
|
948
|
+
(max, r) => Math.max(max, r.retryAfter ?? 0),
|
|
949
|
+
0
|
|
950
|
+
);
|
|
853
951
|
deps.stdout("\n");
|
|
854
|
-
for (const h of
|
|
855
|
-
deps.stdout(`${hint(h
|
|
952
|
+
for (const h of lines) {
|
|
953
|
+
deps.stdout(`${hint(renderNextHint(h, hostname, maxRetryAfter))}
|
|
856
954
|
`);
|
|
857
955
|
}
|
|
858
956
|
}
|
|
@@ -889,7 +987,7 @@ async function runDomainsConnect(input, deps) {
|
|
|
889
987
|
deps.stdout(`${formatDnsTable(domain.dns)}
|
|
890
988
|
`);
|
|
891
989
|
}
|
|
892
|
-
printNextHints(deps, domain.next);
|
|
990
|
+
printNextHints(deps, domain.next, domain.hostname, domain.dns);
|
|
893
991
|
} else {
|
|
894
992
|
writeJson(deps, { ok: true, domain });
|
|
895
993
|
}
|
|
@@ -957,17 +1055,19 @@ async function runDomainsStatus(idOrHostname, _input, deps) {
|
|
|
957
1055
|
`);
|
|
958
1056
|
}
|
|
959
1057
|
if (domain.verifiedAt) {
|
|
960
|
-
deps.stdout(
|
|
961
|
-
`)
|
|
1058
|
+
deps.stdout(
|
|
1059
|
+
` ${import_picocolors3.default.dim("Verified:")} ${formatDate(domain.verifiedAt)}
|
|
1060
|
+
`
|
|
1061
|
+
);
|
|
962
1062
|
}
|
|
963
|
-
deps.stdout(` ${import_picocolors3.default.dim("Created: ")} ${domain.createdAt}
|
|
1063
|
+
deps.stdout(` ${import_picocolors3.default.dim("Created: ")} ${formatDate(domain.createdAt)}
|
|
964
1064
|
`);
|
|
965
1065
|
if (domain.dns.length > 0) {
|
|
966
1066
|
deps.stdout("\nDNS records:\n\n");
|
|
967
1067
|
deps.stdout(`${formatDnsTable(domain.dns)}
|
|
968
1068
|
`);
|
|
969
1069
|
}
|
|
970
|
-
printNextHints(deps, domain.next);
|
|
1070
|
+
printNextHints(deps, domain.next, domain.hostname, domain.dns);
|
|
971
1071
|
} else {
|
|
972
1072
|
writeJson(deps, { ok: true, domain });
|
|
973
1073
|
}
|
|
@@ -1000,7 +1100,7 @@ async function runDomainsVerify(idOrHostname, input, deps, sleep = defaultSleep)
|
|
|
1000
1100
|
deps.stdout(`${formatDnsTable(domain.dns)}
|
|
1001
1101
|
`);
|
|
1002
1102
|
}
|
|
1003
|
-
printNextHints(deps, domain.next);
|
|
1103
|
+
printNextHints(deps, domain.next, domain.hostname, domain.dns);
|
|
1004
1104
|
} else {
|
|
1005
1105
|
writeJson(deps, { ok: true, domain });
|
|
1006
1106
|
}
|
|
@@ -1081,7 +1181,22 @@ async function runDomainsUpdate(idOrHostname, input, deps) {
|
|
|
1081
1181
|
});
|
|
1082
1182
|
return { exitCode: 0 };
|
|
1083
1183
|
}
|
|
1084
|
-
async function runDomainsRemove(idOrHostname,
|
|
1184
|
+
async function runDomainsRemove(idOrHostname, input, deps) {
|
|
1185
|
+
if (!input.yes && input.interactive === false) {
|
|
1186
|
+
return writeError(
|
|
1187
|
+
deps,
|
|
1188
|
+
"invalid_usage",
|
|
1189
|
+
"Pass --yes to remove the domain in non-interactive mode."
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
if (!input.yes && input.interactive !== false) {
|
|
1193
|
+
const confirmed = await prompts2.confirm({
|
|
1194
|
+
message: `Remove ${idOrHostname}? This removes the domain and unmounts its drops.`
|
|
1195
|
+
});
|
|
1196
|
+
if (prompts2.isCancel(confirmed) || !confirmed) {
|
|
1197
|
+
return { exitCode: 0 };
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1085
1200
|
try {
|
|
1086
1201
|
await requireCredential({
|
|
1087
1202
|
...deps.apiKey ? { apiKey: deps.apiKey } : {},
|
|
@@ -1114,7 +1229,30 @@ async function runDomainsRemove(idOrHostname, _input, deps) {
|
|
|
1114
1229
|
}
|
|
1115
1230
|
|
|
1116
1231
|
// src/commands/drops.ts
|
|
1117
|
-
var
|
|
1232
|
+
var prompts3 = __toESM(require("@clack/prompts"), 1);
|
|
1233
|
+
var import_picocolors4 = __toESM(require("picocolors"), 1);
|
|
1234
|
+
async function resolveDropTarget(target, deps) {
|
|
1235
|
+
if (target.startsWith("drop_")) return { ok: true, dropId: target };
|
|
1236
|
+
const resolved = await deps.client.drops.resolve(target);
|
|
1237
|
+
if (resolved.error) {
|
|
1238
|
+
return { ok: false, ...writeApiError(deps, resolved.error) };
|
|
1239
|
+
}
|
|
1240
|
+
if (resolved.data === null) {
|
|
1241
|
+
return {
|
|
1242
|
+
ok: false,
|
|
1243
|
+
...writeApiError(deps, {
|
|
1244
|
+
code: "not_found",
|
|
1245
|
+
message: `No drop matching "${target}" found in your account.`,
|
|
1246
|
+
suggestion: "It may belong to another account or have been deleted. Run dropthis list to see your drops, or pass the full drop_\u2026 id."
|
|
1247
|
+
})
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
return {
|
|
1251
|
+
ok: true,
|
|
1252
|
+
dropId: resolved.data.id,
|
|
1253
|
+
...resolved.data.slug ? { slug: resolved.data.slug } : {}
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1118
1256
|
async function runDropsList(input, deps) {
|
|
1119
1257
|
try {
|
|
1120
1258
|
await requireCredential({
|
|
@@ -1132,24 +1270,40 @@ async function runDropsList(input, deps) {
|
|
|
1132
1270
|
const result = await deps.client.drops.list(params);
|
|
1133
1271
|
if (result.error) return writeApiError(deps, result.error);
|
|
1134
1272
|
const raw = unwrapListData(result.data, "drops");
|
|
1273
|
+
const page = extractPagination(result.data);
|
|
1135
1274
|
const items = Array.isArray(raw) ? raw : void 0;
|
|
1136
1275
|
if (deps.outputMode === "human") {
|
|
1137
1276
|
if (!items || items.length === 0) {
|
|
1138
1277
|
writeEmpty(deps, "No drops found.", { ok: true, drops: [] });
|
|
1139
1278
|
} else {
|
|
1140
1279
|
for (const item of items) {
|
|
1280
|
+
const created = typeof item.createdAt === "string" ? import_picocolors4.default.dim(formatDate(item.createdAt)) : "";
|
|
1141
1281
|
deps.stdout(
|
|
1142
|
-
`${item.id ?? ""} ${item.title ?? ""} ${item.url ? url(String(item.url)) : ""}
|
|
1282
|
+
`${item.id ?? ""} ${created} ${item.title ?? ""} ${item.url ? url(String(item.url)) : ""}
|
|
1283
|
+
`
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1286
|
+
deps.stdout(
|
|
1287
|
+
`${import_picocolors4.default.dim(`${items.length} ${items.length === 1 ? "drop" : "drops"}`)}
|
|
1288
|
+
`
|
|
1289
|
+
);
|
|
1290
|
+
if (page?.hasMore && page.nextCursor) {
|
|
1291
|
+
deps.stdout(
|
|
1292
|
+
`${hint(`More drops: dropthis list --cursor ${page.nextCursor}`)}
|
|
1143
1293
|
`
|
|
1144
1294
|
);
|
|
1145
1295
|
}
|
|
1146
1296
|
}
|
|
1147
1297
|
} else {
|
|
1148
|
-
writeJson(deps, {
|
|
1298
|
+
writeJson(deps, {
|
|
1299
|
+
ok: true,
|
|
1300
|
+
drops: items ?? raw,
|
|
1301
|
+
...page ? { next_cursor: page.nextCursor, has_more: page.hasMore } : {}
|
|
1302
|
+
});
|
|
1149
1303
|
}
|
|
1150
1304
|
return { exitCode: 0 };
|
|
1151
1305
|
}
|
|
1152
|
-
async function runDropsGet(
|
|
1306
|
+
async function runDropsGet(target, _input, deps) {
|
|
1153
1307
|
try {
|
|
1154
1308
|
await requireCredential({
|
|
1155
1309
|
...deps.apiKey ? { apiKey: deps.apiKey } : {},
|
|
@@ -1159,7 +1313,9 @@ async function runDropsGet(dropId, _input, deps) {
|
|
|
1159
1313
|
} catch {
|
|
1160
1314
|
return writeAuthError(deps);
|
|
1161
1315
|
}
|
|
1162
|
-
const
|
|
1316
|
+
const resolved = await resolveDropTarget(target, deps);
|
|
1317
|
+
if (!resolved.ok) return { exitCode: resolved.exitCode };
|
|
1318
|
+
const result = await deps.client.drops.get(resolved.dropId);
|
|
1163
1319
|
if (result.error) return writeApiError(deps, result.error);
|
|
1164
1320
|
const data = result.data;
|
|
1165
1321
|
const pairs = [];
|
|
@@ -1169,7 +1325,8 @@ async function runDropsGet(dropId, _input, deps) {
|
|
|
1169
1325
|
if (data.visibility) pairs.push(["Visibility", String(data.visibility)]);
|
|
1170
1326
|
if (data.revision !== void 0)
|
|
1171
1327
|
pairs.push(["Revision", String(data.revision)]);
|
|
1172
|
-
if (data.createdAt)
|
|
1328
|
+
if (data.createdAt)
|
|
1329
|
+
pairs.push(["Created", formatDate(String(data.createdAt))]);
|
|
1173
1330
|
if (data.accessible !== void 0)
|
|
1174
1331
|
pairs.push(["Accessible", String(data.accessible)]);
|
|
1175
1332
|
if (data.persistent !== void 0)
|
|
@@ -1179,7 +1336,7 @@ async function runDropsGet(dropId, _input, deps) {
|
|
|
1179
1336
|
writeKv(deps, pairs, { ok: true, drop: result.data });
|
|
1180
1337
|
return { exitCode: 0 };
|
|
1181
1338
|
}
|
|
1182
|
-
async function runDropsDelete(
|
|
1339
|
+
async function runDropsDelete(target, input, deps) {
|
|
1183
1340
|
if (!input.yes && input.interactive === false) {
|
|
1184
1341
|
return writeError(
|
|
1185
1342
|
deps,
|
|
@@ -1187,14 +1344,6 @@ async function runDropsDelete(dropId, input, deps) {
|
|
|
1187
1344
|
"Pass --yes to delete in non-interactive mode."
|
|
1188
1345
|
);
|
|
1189
1346
|
}
|
|
1190
|
-
if (!input.yes && input.interactive !== false) {
|
|
1191
|
-
const confirmed = await prompts2.confirm({
|
|
1192
|
-
message: `Delete ${dropId}?`
|
|
1193
|
-
});
|
|
1194
|
-
if (prompts2.isCancel(confirmed) || !confirmed) {
|
|
1195
|
-
return { exitCode: 0 };
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
1347
|
try {
|
|
1199
1348
|
await requireCredential({
|
|
1200
1349
|
...deps.apiKey ? { apiKey: deps.apiKey } : {},
|
|
@@ -1204,6 +1353,17 @@ async function runDropsDelete(dropId, input, deps) {
|
|
|
1204
1353
|
} catch {
|
|
1205
1354
|
return writeAuthError(deps);
|
|
1206
1355
|
}
|
|
1356
|
+
const resolved = await resolveDropTarget(target, deps);
|
|
1357
|
+
if (!resolved.ok) return { exitCode: resolved.exitCode };
|
|
1358
|
+
const dropId = resolved.dropId;
|
|
1359
|
+
if (!input.yes && input.interactive !== false) {
|
|
1360
|
+
const confirmed = await prompts3.confirm({
|
|
1361
|
+
message: `Delete ${dropId}${dropId === target ? "" : ` (${target})`}?`
|
|
1362
|
+
});
|
|
1363
|
+
if (prompts3.isCancel(confirmed) || !confirmed) {
|
|
1364
|
+
return { exitCode: 0 };
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1207
1367
|
const result = await deps.client.drops.delete(dropId);
|
|
1208
1368
|
if (result?.error) return writeApiError(deps, result.error);
|
|
1209
1369
|
writeHumanOrJson(deps, success(`Deleted ${dropId}`), {
|
|
@@ -1215,31 +1375,31 @@ async function runDropsDelete(dropId, input, deps) {
|
|
|
1215
1375
|
}
|
|
1216
1376
|
|
|
1217
1377
|
// src/commands/login.ts
|
|
1218
|
-
var
|
|
1378
|
+
var prompts4 = __toESM(require("@clack/prompts"), 1);
|
|
1219
1379
|
async function runLoginInteractive(deps) {
|
|
1220
|
-
|
|
1221
|
-
const email = await
|
|
1380
|
+
prompts4.intro("dropthis login");
|
|
1381
|
+
const email = await prompts4.text({
|
|
1222
1382
|
message: "Email address",
|
|
1223
1383
|
validate: (v) => v?.includes("@") ? void 0 : "Enter a valid email"
|
|
1224
1384
|
});
|
|
1225
|
-
if (
|
|
1226
|
-
|
|
1385
|
+
if (prompts4.isCancel(email)) {
|
|
1386
|
+
prompts4.cancel("Login cancelled.");
|
|
1227
1387
|
return { exitCode: 0 };
|
|
1228
1388
|
}
|
|
1229
1389
|
const requestResult = await deps.client.auth.requestEmailOtp({ email });
|
|
1230
1390
|
if (requestResult.error) {
|
|
1231
|
-
|
|
1391
|
+
prompts4.cancel(requestResult.error.message);
|
|
1232
1392
|
return { exitCode: exitCodeFor("api_error") };
|
|
1233
1393
|
}
|
|
1234
1394
|
const maxAttempts = 3;
|
|
1235
1395
|
let session;
|
|
1236
1396
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1237
|
-
const otp = await
|
|
1397
|
+
const otp = await prompts4.text({
|
|
1238
1398
|
message: attempt === 1 ? "Paste the code from your email" : "Try again \u2014 paste the code from your email",
|
|
1239
1399
|
validate: (v) => v && v.length >= 4 && v.length <= 8 ? void 0 : "Code must be 4\u20138 characters."
|
|
1240
1400
|
});
|
|
1241
|
-
if (
|
|
1242
|
-
|
|
1401
|
+
if (prompts4.isCancel(otp)) {
|
|
1402
|
+
prompts4.cancel("Login cancelled.");
|
|
1243
1403
|
return { exitCode: 0 };
|
|
1244
1404
|
}
|
|
1245
1405
|
const result = await deps.client.auth.verifyEmailOtp({
|
|
@@ -1251,20 +1411,20 @@ async function runLoginInteractive(deps) {
|
|
|
1251
1411
|
break;
|
|
1252
1412
|
}
|
|
1253
1413
|
if (attempt < maxAttempts) {
|
|
1254
|
-
|
|
1414
|
+
prompts4.log.warning(result.error.message);
|
|
1255
1415
|
} else {
|
|
1256
|
-
|
|
1416
|
+
prompts4.cancel(result.error.message);
|
|
1257
1417
|
return { exitCode: exitCodeFor("api_error") };
|
|
1258
1418
|
}
|
|
1259
1419
|
}
|
|
1260
1420
|
if (!session) {
|
|
1261
|
-
|
|
1421
|
+
prompts4.cancel("Login failed.");
|
|
1262
1422
|
return { exitCode: exitCodeFor("api_error") };
|
|
1263
1423
|
}
|
|
1264
1424
|
const authedClient = deps.createClient(session.data.token);
|
|
1265
1425
|
const apiKey = await authedClient.apiKeys.create({ label: "CLI" });
|
|
1266
1426
|
if (apiKey.error) {
|
|
1267
|
-
|
|
1427
|
+
prompts4.cancel(apiKey.error.message);
|
|
1268
1428
|
return { exitCode: exitCodeFor("api_error") };
|
|
1269
1429
|
}
|
|
1270
1430
|
await deps.store.save({
|
|
@@ -1275,7 +1435,7 @@ async function runLoginInteractive(deps) {
|
|
|
1275
1435
|
email,
|
|
1276
1436
|
storage: "secure"
|
|
1277
1437
|
});
|
|
1278
|
-
|
|
1438
|
+
prompts4.outro("Logged in successfully.");
|
|
1279
1439
|
return { exitCode: 0 };
|
|
1280
1440
|
}
|
|
1281
1441
|
async function runLoginRequest(input, deps) {
|
|
@@ -1464,12 +1624,12 @@ function parseJsonObject(value, label) {
|
|
|
1464
1624
|
}
|
|
1465
1625
|
|
|
1466
1626
|
// src/spinner.ts
|
|
1467
|
-
var
|
|
1627
|
+
var import_picocolors5 = __toESM(require("picocolors"), 1);
|
|
1468
1628
|
var frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1469
1629
|
function createSpinner(message, stderr) {
|
|
1470
1630
|
let i = 0;
|
|
1471
1631
|
const timer = setInterval(() => {
|
|
1472
|
-
stderr(`\r\x1B[2K${
|
|
1632
|
+
stderr(`\r\x1B[2K${import_picocolors5.default.cyan(frames[i % frames.length])} ${message}`);
|
|
1473
1633
|
i++;
|
|
1474
1634
|
}, 80);
|
|
1475
1635
|
return {
|
|
@@ -1620,24 +1780,10 @@ async function runPull(target, input, deps) {
|
|
|
1620
1780
|
} catch {
|
|
1621
1781
|
return writeAuthError(deps);
|
|
1622
1782
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
defaultDirName = target;
|
|
1628
|
-
} else {
|
|
1629
|
-
const resolved = await deps.client.drops.resolve(target);
|
|
1630
|
-
if (resolved.error) return writeApiError(deps, resolved.error);
|
|
1631
|
-
if (resolved.data === null) {
|
|
1632
|
-
return writeApiError(deps, {
|
|
1633
|
-
code: "not_found",
|
|
1634
|
-
message: `No drop matching "${target}" found in your account.`,
|
|
1635
|
-
suggestion: "It may belong to another account or have been deleted. Run dropthis list to see your drops, or pass the full drop_\u2026 id."
|
|
1636
|
-
});
|
|
1637
|
-
}
|
|
1638
|
-
dropId = resolved.data.id;
|
|
1639
|
-
defaultDirName = resolved.data.slug || resolved.data.id;
|
|
1640
|
-
}
|
|
1783
|
+
const resolved = await resolveDropTarget(target, deps);
|
|
1784
|
+
if (!resolved.ok) return { exitCode: resolved.exitCode };
|
|
1785
|
+
const dropId = resolved.dropId;
|
|
1786
|
+
const defaultDirName = resolved.slug || resolved.dropId;
|
|
1641
1787
|
const spin = shouldSpin(deps.outputMode) ? createSpinner("Pulling content\u2026", deps.stderr) : void 0;
|
|
1642
1788
|
const manifestResult = await deps.client.drops.getContent(dropId);
|
|
1643
1789
|
if (manifestResult.error) {
|
|
@@ -1820,7 +1966,7 @@ function assertDropId(target) {
|
|
|
1820
1966
|
}
|
|
1821
1967
|
|
|
1822
1968
|
// src/commands/update-settings.ts
|
|
1823
|
-
var
|
|
1969
|
+
var prompts5 = __toESM(require("@clack/prompts"), 1);
|
|
1824
1970
|
var NO_SETTINGS_MESSAGE = "Nothing to update. Provide at least one settings option.";
|
|
1825
1971
|
var NO_SETTINGS_NEXT_ACTION = "Run dropthis update-settings --help, or retry with one of: --title, --visibility, --password, --no-password, --noindex, --index, --expires-at, --metadata, or --metadata-file. To replace the content at the URL instead, use dropthis update-content <dropId> ./dist.";
|
|
1826
1972
|
async function runUpdateSettings(dropId, raw, deps) {
|
|
@@ -1844,7 +1990,7 @@ async function runUpdateSettings(dropId, raw, deps) {
|
|
|
1844
1990
|
if (shouldPrompt(deps)) {
|
|
1845
1991
|
const prompted = await promptForSettings(
|
|
1846
1992
|
dropId,
|
|
1847
|
-
deps.prompts ??
|
|
1993
|
+
deps.prompts ?? prompts5
|
|
1848
1994
|
);
|
|
1849
1995
|
if (!prompted) return { exitCode: 0 };
|
|
1850
1996
|
parsed = { ...parsed, ...prompted };
|
|
@@ -2233,6 +2379,55 @@ async function writeCredentialFile(path, credential) {
|
|
|
2233
2379
|
await (0, import_promises3.rename)(tmpPath, path);
|
|
2234
2380
|
}
|
|
2235
2381
|
|
|
2382
|
+
// src/typo-guard.ts
|
|
2383
|
+
var import_promises4 = require("fs/promises");
|
|
2384
|
+
function levenshtein(a, b) {
|
|
2385
|
+
if (a === b) return 0;
|
|
2386
|
+
if (a.length === 0) return b.length;
|
|
2387
|
+
if (b.length === 0) return a.length;
|
|
2388
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
2389
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2390
|
+
const curr = [i];
|
|
2391
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2392
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
2393
|
+
const deletion = (prev[j] ?? 0) + 1;
|
|
2394
|
+
const insertion = (curr[j - 1] ?? 0) + 1;
|
|
2395
|
+
const substitution = (prev[j - 1] ?? 0) + cost;
|
|
2396
|
+
curr[j] = Math.min(deletion, insertion, substitution);
|
|
2397
|
+
}
|
|
2398
|
+
prev = curr;
|
|
2399
|
+
}
|
|
2400
|
+
return prev[b.length] ?? 0;
|
|
2401
|
+
}
|
|
2402
|
+
function suggestCommand(token, names) {
|
|
2403
|
+
const lower = token.toLowerCase();
|
|
2404
|
+
let best = null;
|
|
2405
|
+
for (const name of names) {
|
|
2406
|
+
if (lower.length >= 2 && name.startsWith(lower)) return name;
|
|
2407
|
+
const dist = levenshtein(lower, name);
|
|
2408
|
+
if (best === null || dist < best.dist) best = { name, dist };
|
|
2409
|
+
}
|
|
2410
|
+
if (best !== null && best.dist <= 2 && best.dist < lower.length) {
|
|
2411
|
+
return best.name;
|
|
2412
|
+
}
|
|
2413
|
+
return null;
|
|
2414
|
+
}
|
|
2415
|
+
function isBareWordToken(token) {
|
|
2416
|
+
if (token === "" || token === "-") return false;
|
|
2417
|
+
if (/\s/.test(token)) return false;
|
|
2418
|
+
if (token.includes("/") || token.includes("\\")) return false;
|
|
2419
|
+
if (token.includes(".")) return false;
|
|
2420
|
+
return true;
|
|
2421
|
+
}
|
|
2422
|
+
async function pathExists(path) {
|
|
2423
|
+
try {
|
|
2424
|
+
await (0, import_promises4.stat)(path);
|
|
2425
|
+
return true;
|
|
2426
|
+
} catch {
|
|
2427
|
+
return false;
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2236
2431
|
// src/program.ts
|
|
2237
2432
|
function buildProgram(options = {}) {
|
|
2238
2433
|
const program = new import_commander.Command();
|
|
@@ -2243,6 +2438,7 @@ function buildProgram(options = {}) {
|
|
|
2243
2438
|
return argv.includes("--json") || argv.includes("--quiet") || argv.includes("-q") || options.env?.CI === "true" || options.env === void 0 && process.env.CI === "true" || options.stdoutIsTTY === false || options.stdoutIsTTY === void 0 && process.stdout.isTTY !== true;
|
|
2244
2439
|
};
|
|
2245
2440
|
program.exitOverride();
|
|
2441
|
+
program.showSuggestionAfterError();
|
|
2246
2442
|
program.configureOutput({
|
|
2247
2443
|
writeErr,
|
|
2248
2444
|
// In JSON mode, suppress commander's default plain-text error so the
|
|
@@ -2253,13 +2449,13 @@ function buildProgram(options = {}) {
|
|
|
2253
2449
|
});
|
|
2254
2450
|
program.name("dropthis").description(
|
|
2255
2451
|
[
|
|
2256
|
-
|
|
2452
|
+
import_picocolors6.default.dim("Publish anything online and get a URL back."),
|
|
2257
2453
|
"",
|
|
2258
|
-
`${
|
|
2259
|
-
` ${
|
|
2260
|
-
` ${
|
|
2454
|
+
`${import_picocolors6.default.bold("Quick start:")}`,
|
|
2455
|
+
` ${import_picocolors6.default.cyan("dropthis login")}`,
|
|
2456
|
+
` ${import_picocolors6.default.cyan("dropthis publish ./dist")}`
|
|
2261
2457
|
].join("\n")
|
|
2262
|
-
).version("0.
|
|
2458
|
+
).version("0.12.0").configureHelp({
|
|
2263
2459
|
subcommandTerm(cmd) {
|
|
2264
2460
|
const args = cmd.registeredArguments.map((a) => {
|
|
2265
2461
|
const name = a.name();
|
|
@@ -2268,16 +2464,28 @@ function buildProgram(options = {}) {
|
|
|
2268
2464
|
}).join(" ");
|
|
2269
2465
|
return args ? `${cmd.name()} ${args}` : cmd.name();
|
|
2270
2466
|
},
|
|
2271
|
-
styleTitle: (title) =>
|
|
2272
|
-
styleCommandText: (str) =>
|
|
2273
|
-
styleSubcommandText: (str) =>
|
|
2274
|
-
styleOptionText: (str) =>
|
|
2467
|
+
styleTitle: (title) => import_picocolors6.default.bold(title),
|
|
2468
|
+
styleCommandText: (str) => import_picocolors6.default.cyan(str),
|
|
2469
|
+
styleSubcommandText: (str) => import_picocolors6.default.cyan(str),
|
|
2470
|
+
styleOptionText: (str) => import_picocolors6.default.yellow(str)
|
|
2275
2471
|
});
|
|
2276
2472
|
program.option("--api-key <key>", "Override API key for this invocation");
|
|
2277
2473
|
program.option("--api-url <url>", "Override API base URL");
|
|
2278
2474
|
program.option("--json", "Force JSON output");
|
|
2279
2475
|
program.option("-q, --quiet", "Suppress status output and imply JSON");
|
|
2280
2476
|
program.option("--no-interactive", "Disable interactive prompts");
|
|
2477
|
+
program.addHelpText(
|
|
2478
|
+
"after",
|
|
2479
|
+
[
|
|
2480
|
+
"",
|
|
2481
|
+
"Output is JSON when piped/CI/--json; human tables in a terminal.",
|
|
2482
|
+
"Docs: https://dropthis.app"
|
|
2483
|
+
].join("\n")
|
|
2484
|
+
);
|
|
2485
|
+
const examplesBlock = (lines) => `
|
|
2486
|
+
${import_picocolors6.default.bold("Examples:")}
|
|
2487
|
+
${lines.map((l) => ` ${l}`).join("\n")}
|
|
2488
|
+
`;
|
|
2281
2489
|
program.command("publish [input...]", { isDefault: true }).description(
|
|
2282
2490
|
"Publish content to a permanent public URL (also: share, post, put online, make public, get a link).\nFiles, folders, URLs, strings, or stdin; multiple files bundle into one drop. Use - for stdin, or pipe without args.\nCreates a NEW drop each run \u2014 to change one you already published, use update-content (the files) or update-settings (title/visibility/password/expiry/metadata) with its drop_\u2026 id, not publish again."
|
|
2283
2491
|
).option("--title <title>", "Drop title").option("--visibility <v>", "public or unlisted (default: public)").option("--password <password>", "Require password to view").option("--noindex", "Prevent search-engine indexing").option("--expires-at <datetime>", "Auto-delete after this ISO 8601 date").option(
|
|
@@ -2301,6 +2509,15 @@ function buildProgram(options = {}) {
|
|
|
2301
2509
|
).option("--json", "Force JSON output").option(
|
|
2302
2510
|
"--idempotency-key <key>",
|
|
2303
2511
|
"Prevent duplicate publishes on retry (auto-generated)"
|
|
2512
|
+
).addHelpText(
|
|
2513
|
+
"after",
|
|
2514
|
+
examplesBlock([
|
|
2515
|
+
"dropthis publish ./report.html",
|
|
2516
|
+
"dropthis publish ./dist --title 'Launch page'",
|
|
2517
|
+
"cat report.md | dropthis publish - --content-type text/markdown --path report.md",
|
|
2518
|
+
"dropthis publish ./dist --domain reports.example.com --slug launch",
|
|
2519
|
+
"URL=$(dropthis publish ./dist --url)"
|
|
2520
|
+
])
|
|
2304
2521
|
).action(async (inputs, commandOptions) => {
|
|
2305
2522
|
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
2306
2523
|
if (inputs.length === 0 && stdinIsTTY) {
|
|
@@ -2326,6 +2543,29 @@ function buildProgram(options = {}) {
|
|
|
2326
2543
|
process.exitCode = writeError(deps, "local_input_error", msg).exitCode;
|
|
2327
2544
|
return;
|
|
2328
2545
|
}
|
|
2546
|
+
if (deps.outputMode === "human" && deps.interactive && inputs.length === 1 && typeof publishInput === "string" && isBareWordToken(publishInput) && !await pathExists(publishInput)) {
|
|
2547
|
+
const suggestion = suggestCommand(
|
|
2548
|
+
publishInput,
|
|
2549
|
+
program.commands.map((c) => c.name())
|
|
2550
|
+
);
|
|
2551
|
+
if (suggestion) {
|
|
2552
|
+
process.exitCode = writeError(
|
|
2553
|
+
deps,
|
|
2554
|
+
"invalid_usage",
|
|
2555
|
+
`Unknown command '${publishInput}' \u2014 did you mean '${suggestion}'?`,
|
|
2556
|
+
`To publish the literal text, pipe it: echo '${publishInput}' | dropthis publish`
|
|
2557
|
+
).exitCode;
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
const prompts6 = await import("@clack/prompts");
|
|
2561
|
+
const confirmed = await prompts6.confirm({
|
|
2562
|
+
message: `Publish '${publishInput}' as inline text?`,
|
|
2563
|
+
initialValue: false
|
|
2564
|
+
});
|
|
2565
|
+
if (prompts6.isCancel(confirmed) || !confirmed) {
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2329
2569
|
let dropOpts;
|
|
2330
2570
|
try {
|
|
2331
2571
|
dropOpts = toDropOptions(commandOptions);
|
|
@@ -2429,7 +2669,14 @@ function buildProgram(options = {}) {
|
|
|
2429
2669
|
);
|
|
2430
2670
|
program.command("pull <target>").description(
|
|
2431
2671
|
"Download a drop's files into a local directory (accepts the drop_\u2026 id, the drop URL, or its slug).\nFetches the current deployment's file manifest and writes every file. Pull, edit, then update-content to ship the changes back to the same URL \u2014 also the rollback path."
|
|
2432
|
-
).option("-o, --output <dir>", "Output directory (default: ./<slug-or-id>)").option("--json", "Force JSON output").
|
|
2672
|
+
).option("-o, --output <dir>", "Output directory (default: ./<slug-or-id>)").option("--json", "Force JSON output").addHelpText(
|
|
2673
|
+
"after",
|
|
2674
|
+
examplesBlock([
|
|
2675
|
+
"dropthis pull drop_abc123 -o ./site # download the drop's files",
|
|
2676
|
+
"$EDITOR ./site/index.html # edit locally",
|
|
2677
|
+
"dropthis update-content drop_abc123 ./site # ship back to the same URL"
|
|
2678
|
+
])
|
|
2679
|
+
).action(
|
|
2433
2680
|
async (target, commandOptions) => {
|
|
2434
2681
|
const deps = await commandDeps(
|
|
2435
2682
|
program,
|
|
@@ -2441,19 +2688,29 @@ function buildProgram(options = {}) {
|
|
|
2441
2688
|
process.exitCode = result.exitCode;
|
|
2442
2689
|
}
|
|
2443
2690
|
);
|
|
2444
|
-
program.command("get <dropId>").description(
|
|
2691
|
+
program.command("get <dropId>").description(
|
|
2692
|
+
"Show drop details (accepts the drop_\u2026 id, the drop URL, or its slug)"
|
|
2693
|
+
).option("--json", "Force JSON output").action(async (dropId, commandOptions) => {
|
|
2445
2694
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
2446
2695
|
const result = await runDropsGet(dropId, commandOptions, deps);
|
|
2447
2696
|
process.exitCode = result.exitCode;
|
|
2448
2697
|
});
|
|
2449
|
-
program.command("list").description("List your drops").option("--limit <n>", "Page size", parseInteger).option("--cursor <cursor>", "Pagination cursor").option("--json", "Force JSON output").
|
|
2698
|
+
program.command("list").description("List your drops").option("--limit <n>", "Page size", parseInteger).option("--cursor <cursor>", "Pagination cursor").option("--json", "Force JSON output").addHelpText(
|
|
2699
|
+
"after",
|
|
2700
|
+
examplesBlock([
|
|
2701
|
+
"dropthis list",
|
|
2702
|
+
"dropthis list --json | jq -r '.drops[] | [.id, .url] | @tsv' # recover drop ids"
|
|
2703
|
+
])
|
|
2704
|
+
).action(
|
|
2450
2705
|
async (commandOptions) => {
|
|
2451
2706
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
2452
2707
|
const result = await runDropsList(commandOptions, deps);
|
|
2453
2708
|
process.exitCode = result.exitCode;
|
|
2454
2709
|
}
|
|
2455
2710
|
);
|
|
2456
|
-
program.command("delete <dropId>").description(
|
|
2711
|
+
program.command("delete <dropId>").description(
|
|
2712
|
+
"Delete a drop (accepts the drop_\u2026 id, the drop URL, or its slug)"
|
|
2713
|
+
).option("--yes", "Confirm deletion").option("--json", "Force JSON output").action(
|
|
2457
2714
|
async (dropId, commandOptions) => {
|
|
2458
2715
|
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
2459
2716
|
const global = globalOptions(program, commandOptions);
|
|
@@ -2497,7 +2754,15 @@ function buildProgram(options = {}) {
|
|
|
2497
2754
|
).requiredOption(
|
|
2498
2755
|
"--mode <mode>",
|
|
2499
2756
|
"Mount mode: path (many drops at hostname/{slug}/) or dedicated (one drop at root)"
|
|
2500
|
-
).option("--json", "Force JSON output").
|
|
2757
|
+
).option("--json", "Force JSON output").addHelpText(
|
|
2758
|
+
"after",
|
|
2759
|
+
examplesBlock([
|
|
2760
|
+
"dropthis domains connect reports.example.com --mode path",
|
|
2761
|
+
"# add the CNAME shown above at your DNS provider, then:",
|
|
2762
|
+
"dropthis domains verify reports.example.com --wait",
|
|
2763
|
+
"dropthis publish ./report.html --domain reports.example.com"
|
|
2764
|
+
])
|
|
2765
|
+
).action(
|
|
2501
2766
|
async (hostname, commandOptions) => {
|
|
2502
2767
|
if (commandOptions.mode !== "path" && commandOptions.mode !== "dedicated") {
|
|
2503
2768
|
process.stderr.write(`error: --mode must be "path" or "dedicated"
|
|
@@ -2529,7 +2794,13 @@ function buildProgram(options = {}) {
|
|
|
2529
2794
|
"--timeout <secs>",
|
|
2530
2795
|
"Max seconds to wait (default: 300)",
|
|
2531
2796
|
parseInteger
|
|
2532
|
-
).option("--json", "Force JSON output").
|
|
2797
|
+
).option("--json", "Force JSON output").addHelpText(
|
|
2798
|
+
"after",
|
|
2799
|
+
examplesBlock([
|
|
2800
|
+
"dropthis domains verify reports.example.com --wait",
|
|
2801
|
+
"dropthis domains verify reports.example.com --wait --timeout 600"
|
|
2802
|
+
])
|
|
2803
|
+
).action(
|
|
2533
2804
|
async (hostname, commandOptions) => {
|
|
2534
2805
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
2535
2806
|
const result = await runDomainsVerify(hostname, commandOptions, deps);
|
|
@@ -2553,12 +2824,23 @@ function buildProgram(options = {}) {
|
|
|
2553
2824
|
}
|
|
2554
2825
|
);
|
|
2555
2826
|
domains.command("remove <hostname>").description(
|
|
2556
|
-
"Remove a custom domain. IMPORTANT: also delete the DNS CNAME at your provider to prevent DNS hijacking."
|
|
2557
|
-
).option("--json", "Force JSON output").action(
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2827
|
+
"Remove a custom domain (unmounts its drops). IMPORTANT: also delete the DNS CNAME at your provider to prevent DNS hijacking."
|
|
2828
|
+
).option("--yes", "Confirm removal").option("--json", "Force JSON output").action(
|
|
2829
|
+
async (hostname, commandOptions) => {
|
|
2830
|
+
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
2831
|
+
const global = globalOptions(program, commandOptions);
|
|
2832
|
+
const deps = await commandDeps(program, options, store, commandOptions);
|
|
2833
|
+
const result = await runDomainsRemove(
|
|
2834
|
+
hostname,
|
|
2835
|
+
{
|
|
2836
|
+
...commandOptions,
|
|
2837
|
+
interactive: isInteractive(stdinIsTTY, deps.env, global)
|
|
2838
|
+
},
|
|
2839
|
+
deps
|
|
2840
|
+
);
|
|
2841
|
+
process.exitCode = result.exitCode;
|
|
2842
|
+
}
|
|
2843
|
+
);
|
|
2562
2844
|
const apiKeys = program.command("api-keys").description("Manage API keys");
|
|
2563
2845
|
apiKeys.command("create").description("Create an API key").option("--label <label>", "API key label", "CLI").option("--json", "Force JSON output").action(async (commandOptions) => {
|
|
2564
2846
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
@@ -2591,6 +2873,16 @@ function buildProgram(options = {}) {
|
|
|
2591
2873
|
if (!commandOptions.email || !commandOptions.otp) {
|
|
2592
2874
|
const deps2 = await commandDeps(program, options, store, commandOptions);
|
|
2593
2875
|
const global2 = globalOptions(program, commandOptions);
|
|
2876
|
+
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
2877
|
+
if (!isInteractive(stdinIsTTY, deps2.env, global2)) {
|
|
2878
|
+
process.exitCode = writeError(
|
|
2879
|
+
deps2,
|
|
2880
|
+
"invalid_usage",
|
|
2881
|
+
"dropthis login is interactive and needs a terminal.",
|
|
2882
|
+
"Set DROPTHIS_API_KEY, or run: dropthis login request --email <email>, then dropthis login verify --email <email> --otp <code>."
|
|
2883
|
+
).exitCode;
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2594
2886
|
const context = createContext({ global: global2 });
|
|
2595
2887
|
const result2 = await runLoginInteractive({
|
|
2596
2888
|
...deps2,
|
|
@@ -2849,9 +3141,6 @@ function writeUsageErrorEnvelope(input) {
|
|
|
2849
3141
|
`${JSON.stringify(errorEnvelope("usage_error", input.message))}
|
|
2850
3142
|
`
|
|
2851
3143
|
);
|
|
2852
|
-
} else {
|
|
2853
|
-
input.stderr(`${input.message}
|
|
2854
|
-
`);
|
|
2855
3144
|
}
|
|
2856
3145
|
return 2;
|
|
2857
3146
|
}
|