@dropthis/cli 0.4.0 → 0.5.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 +39 -21
- package/dist/cli.cjs +449 -24
- package/dist/cli.cjs.map +1 -1
- package/node_modules/@dropthis/node/README.md +5 -1
- package/node_modules/@dropthis/node/dist/drops-fK63OCk6.d.cts +385 -0
- package/node_modules/@dropthis/node/dist/drops-fK63OCk6.d.ts +385 -0
- package/node_modules/@dropthis/node/dist/edge.cjs +584 -0
- package/node_modules/@dropthis/node/dist/edge.cjs.map +1 -0
- package/node_modules/@dropthis/node/dist/edge.d.cts +49 -0
- package/node_modules/@dropthis/node/dist/edge.d.ts +49 -0
- package/node_modules/@dropthis/node/dist/edge.mjs +557 -0
- package/node_modules/@dropthis/node/dist/edge.mjs.map +1 -0
- package/node_modules/@dropthis/node/dist/index.cjs +64 -37
- package/node_modules/@dropthis/node/dist/index.cjs.map +1 -1
- package/node_modules/@dropthis/node/dist/index.d.cts +9 -378
- package/node_modules/@dropthis/node/dist/index.d.ts +9 -378
- package/node_modules/@dropthis/node/dist/index.mjs +64 -37
- package/node_modules/@dropthis/node/dist/index.mjs.map +1 -1
- package/node_modules/@dropthis/node/package.json +11 -1
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
9
16
|
var __copyProps = (to, from, except, desc) => {
|
|
10
17
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
18
|
for (let key of __getOwnPropNames(from))
|
|
@@ -23,6 +30,126 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
30
|
mod
|
|
24
31
|
));
|
|
25
32
|
|
|
33
|
+
// src/inline-auth.ts
|
|
34
|
+
var inline_auth_exports = {};
|
|
35
|
+
__export(inline_auth_exports, {
|
|
36
|
+
runInlineAuth: () => runInlineAuth
|
|
37
|
+
});
|
|
38
|
+
async function runInlineAuth(deps) {
|
|
39
|
+
try {
|
|
40
|
+
const prompt = deps.prompt ?? defaultPrompts();
|
|
41
|
+
deps.stderr("\nNot logged in. Let's fix that:\n");
|
|
42
|
+
const emailResult = await prompt.email();
|
|
43
|
+
if (typeof emailResult === "symbol") {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const email = emailResult;
|
|
47
|
+
const otpRequest = await deps.client.auth.requestEmailOtp({ email });
|
|
48
|
+
if (otpRequest.error) {
|
|
49
|
+
deps.stderr(`${otpRequest.error.message}
|
|
50
|
+
`);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
deps.stderr("Code sent! Check your inbox.\n");
|
|
54
|
+
const maxAttempts = 3;
|
|
55
|
+
let session = null;
|
|
56
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
57
|
+
const codeResult = await prompt.code(attempt, maxAttempts);
|
|
58
|
+
if (typeof codeResult === "symbol") {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const code = codeResult;
|
|
62
|
+
const verifyResult = await deps.client.auth.verifyEmailOtp({
|
|
63
|
+
email,
|
|
64
|
+
code
|
|
65
|
+
});
|
|
66
|
+
if (!verifyResult.error) {
|
|
67
|
+
session = verifyResult.data;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
if (attempt < maxAttempts) {
|
|
71
|
+
deps.stderr(`${verifyResult.error.message}
|
|
72
|
+
`);
|
|
73
|
+
} else {
|
|
74
|
+
deps.stderr(
|
|
75
|
+
"Too many attempts. Run `dropthis login` to request a new code.\n"
|
|
76
|
+
);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!session) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const authedClient = deps.createClient(session.token);
|
|
84
|
+
const apiKeyResult = await authedClient.apiKeys.create({
|
|
85
|
+
label: `CLI on ${deps.hostname ?? (0, import_node_os2.hostname)()}`
|
|
86
|
+
});
|
|
87
|
+
if (apiKeyResult.error) {
|
|
88
|
+
deps.stderr(`${apiKeyResult.error.message}
|
|
89
|
+
`);
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const { id: keyId, key: apiKey, keyLast4, accountId } = apiKeyResult.data;
|
|
93
|
+
try {
|
|
94
|
+
await deps.store.save({
|
|
95
|
+
apiKey,
|
|
96
|
+
keyId,
|
|
97
|
+
keyLast4,
|
|
98
|
+
accountId: accountId ?? session.accountId,
|
|
99
|
+
email,
|
|
100
|
+
storage: "secure"
|
|
101
|
+
});
|
|
102
|
+
} catch (err) {
|
|
103
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
104
|
+
deps.stderr(`Failed to save credentials: ${message}
|
|
105
|
+
`);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
deps.stderr("\u2713 Logged in\n\n");
|
|
109
|
+
return {
|
|
110
|
+
apiKey,
|
|
111
|
+
source: "storage",
|
|
112
|
+
keyId,
|
|
113
|
+
keyLast4,
|
|
114
|
+
accountId: accountId ?? session.accountId,
|
|
115
|
+
email
|
|
116
|
+
};
|
|
117
|
+
} catch (err) {
|
|
118
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
119
|
+
deps.stderr(`Login failed: ${message}
|
|
120
|
+
`);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function defaultPrompts() {
|
|
125
|
+
return {
|
|
126
|
+
email: () => readlineQuestion("Email: "),
|
|
127
|
+
code: (attempt) => readlineQuestion(
|
|
128
|
+
attempt === 1 ? "Code: " : "Try again \u2014 enter the code: "
|
|
129
|
+
)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function readlineQuestion(question) {
|
|
133
|
+
const rl = (0, import_node_readline.createInterface)({
|
|
134
|
+
input: process.stdin,
|
|
135
|
+
output: process.stderr
|
|
136
|
+
});
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
rl.question(question, (answer) => {
|
|
139
|
+
rl.close();
|
|
140
|
+
resolve(answer);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
var import_node_os2, import_node_readline;
|
|
145
|
+
var init_inline_auth = __esm({
|
|
146
|
+
"src/inline-auth.ts"() {
|
|
147
|
+
"use strict";
|
|
148
|
+
import_node_os2 = require("os");
|
|
149
|
+
import_node_readline = require("readline");
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
26
153
|
// src/program.ts
|
|
27
154
|
var import_commander = require("commander");
|
|
28
155
|
var import_picocolors4 = __toESM(require("picocolors"), 1);
|
|
@@ -400,6 +527,7 @@ var COMMAND_CATALOG = [
|
|
|
400
527
|
arguments: ["input"],
|
|
401
528
|
options: ["--json", "--url", "--dry-run", "--title", "--metadata"],
|
|
402
529
|
examples: [
|
|
530
|
+
"dropthis ./file.html",
|
|
403
531
|
"dropthis publish ./site --json",
|
|
404
532
|
"dropthis publish ./site --url",
|
|
405
533
|
"dropthis publish ./dist --dry-run"
|
|
@@ -561,13 +689,13 @@ async function runDoctor(_input, deps) {
|
|
|
561
689
|
const authSource = credential?.source ?? "missing";
|
|
562
690
|
const storageBackend = stored?.storage ?? "none";
|
|
563
691
|
const pairs = [
|
|
564
|
-
["Version", "0.
|
|
692
|
+
["Version", "0.5.0"],
|
|
565
693
|
["Auth", authSource],
|
|
566
694
|
["Storage", storageBackend]
|
|
567
695
|
];
|
|
568
696
|
writeKv(deps, pairs, {
|
|
569
697
|
ok: true,
|
|
570
|
-
version: "0.
|
|
698
|
+
version: "0.5.0",
|
|
571
699
|
auth: { source: authSource },
|
|
572
700
|
storage: { backend: storageBackend }
|
|
573
701
|
});
|
|
@@ -997,8 +1125,9 @@ async function readJsonObjectFile(path) {
|
|
|
997
1125
|
|
|
998
1126
|
// src/commands/publish.ts
|
|
999
1127
|
async function runPublish(input, raw, deps) {
|
|
1128
|
+
let credential;
|
|
1000
1129
|
try {
|
|
1001
|
-
await
|
|
1130
|
+
credential = await resolveCredential({
|
|
1002
1131
|
...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
|
|
1003
1132
|
env: deps.env,
|
|
1004
1133
|
store: deps.store
|
|
@@ -1006,8 +1135,26 @@ async function runPublish(input, raw, deps) {
|
|
|
1006
1135
|
} catch {
|
|
1007
1136
|
return writeAuthError(deps);
|
|
1008
1137
|
}
|
|
1138
|
+
let didInlineAuth = false;
|
|
1139
|
+
if (!credential) {
|
|
1140
|
+
if (deps.interactive && deps.inlineAuth) {
|
|
1141
|
+
try {
|
|
1142
|
+
credential = await deps.inlineAuth();
|
|
1143
|
+
} catch {
|
|
1144
|
+
return writeAuthError(deps);
|
|
1145
|
+
}
|
|
1146
|
+
if (!credential) return writeAuthError(deps);
|
|
1147
|
+
didInlineAuth = true;
|
|
1148
|
+
} else {
|
|
1149
|
+
return writeAuthError(deps);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
let client = deps.client;
|
|
1153
|
+
if (didInlineAuth && deps.createClient) {
|
|
1154
|
+
client = deps.createClient(credential.apiKey);
|
|
1155
|
+
}
|
|
1009
1156
|
if (raw.dryRun) {
|
|
1010
|
-
return handlePublishDryRun(input, raw, deps);
|
|
1157
|
+
return handlePublishDryRun(input, raw, deps, client);
|
|
1011
1158
|
}
|
|
1012
1159
|
const singleInput = Array.isArray(input) ? input[0] : input;
|
|
1013
1160
|
const spin = shouldSpin(deps.outputMode) ? createSpinner("Publishing\u2026", deps.stderr) : void 0;
|
|
@@ -1016,13 +1163,44 @@ async function runPublish(input, raw, deps) {
|
|
|
1016
1163
|
throw new Error("Use either <input> or --from-json, not both.");
|
|
1017
1164
|
}
|
|
1018
1165
|
const options = withIdempotencyKey(await parseDropOptions(raw), "pub");
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
) : singleInput ? await deps.client.publish(singleInput, options) : (() => {
|
|
1166
|
+
const jsonBody = raw.fromJson ? await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson) : void 0;
|
|
1167
|
+
const idempotencyOpt = options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {};
|
|
1168
|
+
const doPublish = (c) => jsonBody ? c.drops.createRaw(jsonBody, idempotencyOpt) : singleInput ? c.publish(singleInput, options) : (() => {
|
|
1023
1169
|
throw new Error("Publish requires <input> or --from-json.");
|
|
1024
1170
|
})();
|
|
1171
|
+
const result = await doPublish(client);
|
|
1025
1172
|
if (result.error) {
|
|
1173
|
+
if (result.error.statusCode === 401 && deps.interactive && deps.inlineAuth && !didInlineAuth) {
|
|
1174
|
+
spin?.stop();
|
|
1175
|
+
const newCredential = await deps.inlineAuth();
|
|
1176
|
+
if (newCredential && deps.createClient) {
|
|
1177
|
+
client = deps.createClient(newCredential.apiKey);
|
|
1178
|
+
const retrySpin = shouldSpin(deps.outputMode) ? createSpinner("Publishing\u2026", deps.stderr) : void 0;
|
|
1179
|
+
try {
|
|
1180
|
+
const retryResult = await doPublish(client);
|
|
1181
|
+
if (retryResult.error) {
|
|
1182
|
+
retrySpin?.fail();
|
|
1183
|
+
return writeApiError(deps, retryResult.error);
|
|
1184
|
+
}
|
|
1185
|
+
retrySpin?.stop();
|
|
1186
|
+
if (raw.url) deps.stdout(`${retryResult.data.url}
|
|
1187
|
+
`);
|
|
1188
|
+
else
|
|
1189
|
+
writeResult(
|
|
1190
|
+
deps,
|
|
1191
|
+
retryResult.data.url,
|
|
1192
|
+
"Published",
|
|
1193
|
+
retryResult.data
|
|
1194
|
+
);
|
|
1195
|
+
return { exitCode: 0 };
|
|
1196
|
+
} catch (retryError) {
|
|
1197
|
+
retrySpin?.fail();
|
|
1198
|
+
const message = retryError instanceof Error ? retryError.message : "Publish failed.";
|
|
1199
|
+
const code = isFileSystemError(retryError) ? "local_input_error" : "invalid_usage";
|
|
1200
|
+
return writeError(deps, code, message);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1026
1204
|
spin?.fail();
|
|
1027
1205
|
return writeApiError(deps, result.error);
|
|
1028
1206
|
}
|
|
@@ -1038,7 +1216,7 @@ async function runPublish(input, raw, deps) {
|
|
|
1038
1216
|
return writeError(deps, code, message);
|
|
1039
1217
|
}
|
|
1040
1218
|
}
|
|
1041
|
-
async function handlePublishDryRun(input, raw, deps) {
|
|
1219
|
+
async function handlePublishDryRun(input, raw, deps, client) {
|
|
1042
1220
|
if (raw.url) {
|
|
1043
1221
|
return writeError(
|
|
1044
1222
|
deps,
|
|
@@ -1067,10 +1245,10 @@ async function handlePublishDryRun(input, raw, deps) {
|
|
|
1067
1245
|
if (Array.isArray(input)) {
|
|
1068
1246
|
return dryRunMultiFile(input, options, deps);
|
|
1069
1247
|
}
|
|
1070
|
-
if (!
|
|
1248
|
+
if (!client.prepare) {
|
|
1071
1249
|
throw new Error("Client does not support dry-run.");
|
|
1072
1250
|
}
|
|
1073
|
-
const prepared = await
|
|
1251
|
+
const prepared = await client.prepare(input, options);
|
|
1074
1252
|
const output = formatDryRunManifest(prepared);
|
|
1075
1253
|
validateManifestFileCount(
|
|
1076
1254
|
output.manifest.files.length
|
|
@@ -1126,6 +1304,9 @@ async function dryRunMultiFile(inputs, options, deps) {
|
|
|
1126
1304
|
}
|
|
1127
1305
|
|
|
1128
1306
|
// src/commands/update.ts
|
|
1307
|
+
var prompts4 = __toESM(require("@clack/prompts"), 1);
|
|
1308
|
+
var NO_UPDATE_MESSAGE = "Nothing to update. Provide an input path or at least one update option.";
|
|
1309
|
+
var NO_UPDATE_NEXT_ACTION = "Run dropthis drops update --help, or retry with an input path (for example, dropthis drops update <dropId> ./dist) or one of: --title, --slug, --visibility, --password, --no-password, --noindex, --index, --expires-at, or --metadata.";
|
|
1129
1310
|
async function runUpdate(target, input, raw, deps) {
|
|
1130
1311
|
try {
|
|
1131
1312
|
await requireCredential({
|
|
@@ -1139,14 +1320,31 @@ async function runUpdate(target, input, raw, deps) {
|
|
|
1139
1320
|
if (raw.dryRun) {
|
|
1140
1321
|
return handleUpdateDryRun(target, input, raw, deps);
|
|
1141
1322
|
}
|
|
1142
|
-
|
|
1323
|
+
let spin;
|
|
1143
1324
|
try {
|
|
1144
1325
|
assertDropId(target);
|
|
1145
1326
|
if (raw.fromJson && input) {
|
|
1146
1327
|
throw new Error("Use either [input] or --from-json, not both.");
|
|
1147
1328
|
}
|
|
1148
|
-
|
|
1329
|
+
let updateInput = input;
|
|
1330
|
+
let parsed = await parseDropOptions(raw);
|
|
1331
|
+
if (!updateInput && !raw.fromJson && !hasUpdateFields(parsed)) {
|
|
1332
|
+
if (shouldPromptForUpdate(deps)) {
|
|
1333
|
+
const prompted = await promptForUpdate(target, deps.prompts ?? prompts4);
|
|
1334
|
+
if (!prompted) return { exitCode: 0 };
|
|
1335
|
+
updateInput = prompted.input;
|
|
1336
|
+
parsed = { ...parsed, ...prompted.options };
|
|
1337
|
+
} else {
|
|
1338
|
+
return writeError(
|
|
1339
|
+
deps,
|
|
1340
|
+
"invalid_usage",
|
|
1341
|
+
NO_UPDATE_MESSAGE,
|
|
1342
|
+
NO_UPDATE_NEXT_ACTION
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1149
1346
|
const revisionOptions = raw.ifRevision !== void 0 ? { ifRevision: Number(raw.ifRevision) } : {};
|
|
1347
|
+
spin = shouldSpin(deps.outputMode) ? createSpinner("Updating\u2026", deps.stderr) : void 0;
|
|
1150
1348
|
const result = raw.fromJson ? await deps.client.deployments.createRaw(
|
|
1151
1349
|
target,
|
|
1152
1350
|
await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson),
|
|
@@ -1157,9 +1355,9 @@ async function runUpdate(target, input, raw, deps) {
|
|
|
1157
1355
|
},
|
|
1158
1356
|
"upd"
|
|
1159
1357
|
)
|
|
1160
|
-
) :
|
|
1358
|
+
) : updateInput ? await deps.client.update(
|
|
1161
1359
|
target,
|
|
1162
|
-
|
|
1360
|
+
updateInput,
|
|
1163
1361
|
withIdempotencyKey(
|
|
1164
1362
|
{
|
|
1165
1363
|
...parsed,
|
|
@@ -1184,6 +1382,162 @@ async function runUpdate(target, input, raw, deps) {
|
|
|
1184
1382
|
return writeError(deps, code, message);
|
|
1185
1383
|
}
|
|
1186
1384
|
}
|
|
1385
|
+
function hasUpdateFields(options) {
|
|
1386
|
+
const { idempotencyKey: _, ...fields } = options;
|
|
1387
|
+
return Object.keys(fields).length > 0;
|
|
1388
|
+
}
|
|
1389
|
+
function shouldPromptForUpdate(deps) {
|
|
1390
|
+
return deps.outputMode === "human" && deps.interactive === true;
|
|
1391
|
+
}
|
|
1392
|
+
async function promptForUpdate(target, prompt) {
|
|
1393
|
+
prompt.intro(`Update ${target}`);
|
|
1394
|
+
const fields = await prompt.multiselect({
|
|
1395
|
+
message: "What do you want to update?",
|
|
1396
|
+
required: true,
|
|
1397
|
+
options: [
|
|
1398
|
+
{
|
|
1399
|
+
value: "content",
|
|
1400
|
+
label: "Content",
|
|
1401
|
+
hint: "replace files, folder, URL, or text"
|
|
1402
|
+
},
|
|
1403
|
+
{ value: "title", label: "Title", hint: "--title" },
|
|
1404
|
+
{ value: "slug", label: "Slug", hint: "--slug" },
|
|
1405
|
+
{ value: "visibility", label: "Visibility", hint: "--visibility" },
|
|
1406
|
+
{
|
|
1407
|
+
value: "password",
|
|
1408
|
+
label: "Password",
|
|
1409
|
+
hint: "--password or --no-password"
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
value: "indexing",
|
|
1413
|
+
label: "Search indexing",
|
|
1414
|
+
hint: "--noindex or --index"
|
|
1415
|
+
},
|
|
1416
|
+
{ value: "expiresAt", label: "Expiration", hint: "--expires-at" },
|
|
1417
|
+
{ value: "metadata", label: "Metadata", hint: "--metadata" }
|
|
1418
|
+
]
|
|
1419
|
+
});
|
|
1420
|
+
if (prompt.isCancel(fields)) {
|
|
1421
|
+
prompt.cancel("Update cancelled.");
|
|
1422
|
+
return void 0;
|
|
1423
|
+
}
|
|
1424
|
+
const selected = new Set(fields);
|
|
1425
|
+
const options = {};
|
|
1426
|
+
let input;
|
|
1427
|
+
if (selected.has("content")) {
|
|
1428
|
+
const value = await prompt.text({
|
|
1429
|
+
message: "Content path, URL, or text",
|
|
1430
|
+
validate: requireNonEmpty("Enter content or a path.")
|
|
1431
|
+
});
|
|
1432
|
+
if (prompt.isCancel(value)) return cancelUpdate(prompt);
|
|
1433
|
+
input = value.trim();
|
|
1434
|
+
}
|
|
1435
|
+
if (selected.has("title")) {
|
|
1436
|
+
const value = await prompt.text({
|
|
1437
|
+
message: "Title",
|
|
1438
|
+
validate: requireNonEmpty("Enter a title.")
|
|
1439
|
+
});
|
|
1440
|
+
if (prompt.isCancel(value)) return cancelUpdate(prompt);
|
|
1441
|
+
options.title = value.trim();
|
|
1442
|
+
}
|
|
1443
|
+
if (selected.has("slug")) {
|
|
1444
|
+
const value = await prompt.text({
|
|
1445
|
+
message: "Slug",
|
|
1446
|
+
validate: requireNonEmpty("Enter a slug.")
|
|
1447
|
+
});
|
|
1448
|
+
if (prompt.isCancel(value)) return cancelUpdate(prompt);
|
|
1449
|
+
options.slug = value.trim();
|
|
1450
|
+
}
|
|
1451
|
+
if (selected.has("visibility")) {
|
|
1452
|
+
const value = await prompt.select({
|
|
1453
|
+
message: "Visibility",
|
|
1454
|
+
options: [
|
|
1455
|
+
{ value: "public", label: "Public" },
|
|
1456
|
+
{ value: "unlisted", label: "Unlisted" }
|
|
1457
|
+
]
|
|
1458
|
+
});
|
|
1459
|
+
if (prompt.isCancel(value)) return cancelUpdate(prompt);
|
|
1460
|
+
options.visibility = value;
|
|
1461
|
+
}
|
|
1462
|
+
if (selected.has("password")) {
|
|
1463
|
+
const action = await prompt.select({
|
|
1464
|
+
message: "Password",
|
|
1465
|
+
options: [
|
|
1466
|
+
{ value: "set", label: "Set password" },
|
|
1467
|
+
{ value: "remove", label: "Remove password" }
|
|
1468
|
+
]
|
|
1469
|
+
});
|
|
1470
|
+
if (prompt.isCancel(action)) return cancelUpdate(prompt);
|
|
1471
|
+
if (action === "set") {
|
|
1472
|
+
const value = await prompt.password({
|
|
1473
|
+
message: "Password",
|
|
1474
|
+
validate: requireNonEmpty("Enter a password.")
|
|
1475
|
+
});
|
|
1476
|
+
if (prompt.isCancel(value)) return cancelUpdate(prompt);
|
|
1477
|
+
options.password = value;
|
|
1478
|
+
} else {
|
|
1479
|
+
options.password = null;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
if (selected.has("indexing")) {
|
|
1483
|
+
const value = await prompt.select({
|
|
1484
|
+
message: "Search indexing",
|
|
1485
|
+
options: [
|
|
1486
|
+
{ value: "noindex", label: "Prevent indexing" },
|
|
1487
|
+
{ value: "index", label: "Allow indexing" }
|
|
1488
|
+
]
|
|
1489
|
+
});
|
|
1490
|
+
if (prompt.isCancel(value)) return cancelUpdate(prompt);
|
|
1491
|
+
options.noindex = value === "noindex";
|
|
1492
|
+
}
|
|
1493
|
+
if (selected.has("expiresAt")) {
|
|
1494
|
+
const value = await prompt.text({
|
|
1495
|
+
message: "Expiration date",
|
|
1496
|
+
placeholder: "2025-12-31T23:59:59Z",
|
|
1497
|
+
validate: validateIsoDate
|
|
1498
|
+
});
|
|
1499
|
+
if (prompt.isCancel(value)) return cancelUpdate(prompt);
|
|
1500
|
+
options.expiresAt = value.trim();
|
|
1501
|
+
}
|
|
1502
|
+
if (selected.has("metadata")) {
|
|
1503
|
+
const value = await prompt.text({
|
|
1504
|
+
message: "Metadata JSON object",
|
|
1505
|
+
placeholder: '{"source":"cli"}',
|
|
1506
|
+
validate: validateJsonObject
|
|
1507
|
+
});
|
|
1508
|
+
if (prompt.isCancel(value)) return cancelUpdate(prompt);
|
|
1509
|
+
options.metadata = parseJsonObject2(value);
|
|
1510
|
+
}
|
|
1511
|
+
return { ...input ? { input } : {}, options };
|
|
1512
|
+
}
|
|
1513
|
+
function cancelUpdate(prompt) {
|
|
1514
|
+
prompt.cancel("Update cancelled.");
|
|
1515
|
+
return void 0;
|
|
1516
|
+
}
|
|
1517
|
+
function requireNonEmpty(message) {
|
|
1518
|
+
return (value) => value?.trim() ? void 0 : message;
|
|
1519
|
+
}
|
|
1520
|
+
function validateIsoDate(value) {
|
|
1521
|
+
if (!value?.trim()) return "Enter an ISO 8601 date.";
|
|
1522
|
+
const d = new Date(value);
|
|
1523
|
+
return Number.isNaN(d.getTime()) ? "Enter a valid ISO 8601 date." : void 0;
|
|
1524
|
+
}
|
|
1525
|
+
function validateJsonObject(value) {
|
|
1526
|
+
if (!value?.trim()) return "Enter a JSON object.";
|
|
1527
|
+
try {
|
|
1528
|
+
parseJsonObject2(value);
|
|
1529
|
+
return void 0;
|
|
1530
|
+
} catch {
|
|
1531
|
+
return "Enter a valid JSON object.";
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
function parseJsonObject2(value) {
|
|
1535
|
+
const parsed = JSON.parse(value);
|
|
1536
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1537
|
+
throw new Error("Metadata must be a JSON object.");
|
|
1538
|
+
}
|
|
1539
|
+
return parsed;
|
|
1540
|
+
}
|
|
1187
1541
|
async function handleUpdateDryRun(target, input, raw, deps) {
|
|
1188
1542
|
if (raw.url) {
|
|
1189
1543
|
return writeError(
|
|
@@ -1402,11 +1756,13 @@ async function readJsonFile(path) {
|
|
|
1402
1756
|
}
|
|
1403
1757
|
async function writeCredentialFile(path, credential) {
|
|
1404
1758
|
await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(path), { recursive: true });
|
|
1405
|
-
|
|
1759
|
+
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
1760
|
+
await (0, import_promises4.writeFile)(tmpPath, `${JSON.stringify(credential, null, 2)}
|
|
1406
1761
|
`, {
|
|
1407
1762
|
mode: 384
|
|
1408
1763
|
});
|
|
1409
|
-
await (0, import_promises4.chmod)(
|
|
1764
|
+
await (0, import_promises4.chmod)(tmpPath, 384);
|
|
1765
|
+
await (0, import_promises4.rename)(tmpPath, path);
|
|
1410
1766
|
}
|
|
1411
1767
|
|
|
1412
1768
|
// src/program.ts
|
|
@@ -1421,7 +1777,7 @@ function buildProgram(options = {}) {
|
|
|
1421
1777
|
` ${import_picocolors4.default.cyan("dropthis login")}`,
|
|
1422
1778
|
` ${import_picocolors4.default.cyan("dropthis publish ./dist")}`
|
|
1423
1779
|
].join("\n")
|
|
1424
|
-
).version("0.
|
|
1780
|
+
).version("0.5.0").configureHelp({
|
|
1425
1781
|
subcommandTerm(cmd) {
|
|
1426
1782
|
const args = cmd.registeredArguments.map((a) => {
|
|
1427
1783
|
const name = a.name();
|
|
@@ -1439,7 +1795,8 @@ function buildProgram(options = {}) {
|
|
|
1439
1795
|
program.option("--api-url <url>", "Override API base URL");
|
|
1440
1796
|
program.option("--json", "Force JSON output");
|
|
1441
1797
|
program.option("-q, --quiet", "Suppress status output and imply JSON");
|
|
1442
|
-
program.
|
|
1798
|
+
program.option("--no-interactive", "Disable interactive prompts");
|
|
1799
|
+
program.command("publish [input...]", { isDefault: true }).description(
|
|
1443
1800
|
"Publish files, folders, URLs, strings, or stdin.\nMultiple files are bundled into one drop.\nUse - to read stdin explicitly, or pipe without args."
|
|
1444
1801
|
).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(
|
|
1445
1802
|
"--content-type <mime>",
|
|
@@ -1454,8 +1811,18 @@ function buildProgram(options = {}) {
|
|
|
1454
1811
|
"--idempotency-key <key>",
|
|
1455
1812
|
"Prevent duplicate publishes on retry (auto-generated)"
|
|
1456
1813
|
).action(async (inputs, commandOptions) => {
|
|
1457
|
-
const deps = await commandDeps(program, options, store, commandOptions);
|
|
1458
1814
|
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
1815
|
+
if (inputs.length === 0 && stdinIsTTY && !commandOptions.fromJson) {
|
|
1816
|
+
program.outputHelp();
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
const { deps } = await buildPublishDeps(
|
|
1820
|
+
program,
|
|
1821
|
+
options,
|
|
1822
|
+
store,
|
|
1823
|
+
commandOptions,
|
|
1824
|
+
stdinIsTTY
|
|
1825
|
+
);
|
|
1459
1826
|
let publishInput;
|
|
1460
1827
|
try {
|
|
1461
1828
|
if (commandOptions.dryRun && inputs.length > 1) {
|
|
@@ -1517,6 +1884,8 @@ function buildProgram(options = {}) {
|
|
|
1517
1884
|
"Prevent duplicate updates on retry (auto-generated)"
|
|
1518
1885
|
).action(
|
|
1519
1886
|
async (dropId, input, commandOptions) => {
|
|
1887
|
+
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
1888
|
+
const global = globalOptions(program, commandOptions);
|
|
1520
1889
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
1521
1890
|
let updateInput;
|
|
1522
1891
|
try {
|
|
@@ -1538,16 +1907,24 @@ function buildProgram(options = {}) {
|
|
|
1538
1907
|
process.exitCode = writeError(deps, "invalid_usage", msg).exitCode;
|
|
1539
1908
|
return;
|
|
1540
1909
|
}
|
|
1541
|
-
const result = await runUpdate(dropId, updateInput, dropOpts,
|
|
1910
|
+
const result = await runUpdate(dropId, updateInput, dropOpts, {
|
|
1911
|
+
...deps,
|
|
1912
|
+
interactive: isInteractive(stdinIsTTY, deps.env, global)
|
|
1913
|
+
});
|
|
1542
1914
|
process.exitCode = result.exitCode;
|
|
1543
1915
|
}
|
|
1544
1916
|
);
|
|
1545
1917
|
drops.command("delete <dropId>").description("Delete a drop").option("--yes", "Confirm deletion").option("--json", "Force JSON output").action(
|
|
1546
1918
|
async (dropId, commandOptions) => {
|
|
1919
|
+
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
1920
|
+
const global = globalOptions(program, commandOptions);
|
|
1547
1921
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
1548
1922
|
const result = await runDropsDelete(
|
|
1549
1923
|
dropId,
|
|
1550
|
-
{
|
|
1924
|
+
{
|
|
1925
|
+
...commandOptions,
|
|
1926
|
+
interactive: isInteractive(stdinIsTTY, deps.env, global)
|
|
1927
|
+
},
|
|
1551
1928
|
deps
|
|
1552
1929
|
);
|
|
1553
1930
|
process.exitCode = result.exitCode;
|
|
@@ -1566,10 +1943,15 @@ function buildProgram(options = {}) {
|
|
|
1566
1943
|
});
|
|
1567
1944
|
apiKeys.command("delete <keyId>").description("Delete an API key").option("--yes", "Confirm deletion").option("--json", "Force JSON output").action(
|
|
1568
1945
|
async (keyId, commandOptions) => {
|
|
1946
|
+
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
1947
|
+
const global = globalOptions(program, commandOptions);
|
|
1569
1948
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
1570
1949
|
const result = await runApiKeysDelete(
|
|
1571
1950
|
keyId,
|
|
1572
|
-
{
|
|
1951
|
+
{
|
|
1952
|
+
...commandOptions,
|
|
1953
|
+
interactive: isInteractive(stdinIsTTY, deps.env, global)
|
|
1954
|
+
},
|
|
1573
1955
|
deps
|
|
1574
1956
|
);
|
|
1575
1957
|
process.exitCode = result.exitCode;
|
|
@@ -1714,13 +2096,56 @@ async function commandDeps(program, options, store, commandOptions) {
|
|
|
1714
2096
|
outputMode: context.output.mode
|
|
1715
2097
|
};
|
|
1716
2098
|
}
|
|
2099
|
+
async function buildPublishDeps(program, options, store, commandOptions, stdinIsTTY) {
|
|
2100
|
+
const global = globalOptions(program, commandOptions);
|
|
2101
|
+
const context = createContext({
|
|
2102
|
+
global,
|
|
2103
|
+
...options.env !== void 0 ? { env: options.env } : {},
|
|
2104
|
+
...options.stdout !== void 0 ? { stdout: options.stdout } : {},
|
|
2105
|
+
...options.stderr !== void 0 ? { stderr: options.stderr } : {},
|
|
2106
|
+
...options.stdoutIsTTY !== void 0 ? { stdoutIsTTY: options.stdoutIsTTY } : {}
|
|
2107
|
+
});
|
|
2108
|
+
const credential = await resolveCredential({
|
|
2109
|
+
...global.apiKey ? { apiKey: global.apiKey } : {},
|
|
2110
|
+
env: context.env,
|
|
2111
|
+
store
|
|
2112
|
+
});
|
|
2113
|
+
const interactive = isInteractive(stdinIsTTY, context.env, global);
|
|
2114
|
+
const inlineAuth = async () => {
|
|
2115
|
+
const { runInlineAuth: runInlineAuth2 } = await Promise.resolve().then(() => (init_inline_auth(), inline_auth_exports));
|
|
2116
|
+
return runInlineAuth2({
|
|
2117
|
+
client: context.createClient(),
|
|
2118
|
+
createClient: (apiKey) => context.createClient(apiKey),
|
|
2119
|
+
store,
|
|
2120
|
+
stderr: context.stderr
|
|
2121
|
+
});
|
|
2122
|
+
};
|
|
2123
|
+
return {
|
|
2124
|
+
deps: {
|
|
2125
|
+
store,
|
|
2126
|
+
client: options.client ?? context.createClient(credential?.apiKey),
|
|
2127
|
+
...global.apiKey ? { apiKey: global.apiKey } : {},
|
|
2128
|
+
env: context.env,
|
|
2129
|
+
stdout: context.stdout,
|
|
2130
|
+
stderr: context.stderr,
|
|
2131
|
+
outputMode: context.output.mode,
|
|
2132
|
+
interactive,
|
|
2133
|
+
inlineAuth,
|
|
2134
|
+
createClient: (apiKey) => context.createClient(apiKey)
|
|
2135
|
+
}
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
function isInteractive(stdinIsTTY, env, global) {
|
|
2139
|
+
return stdinIsTTY && !env.CI && !env.DROPTHIS_NON_INTERACTIVE && global.interactive !== false;
|
|
2140
|
+
}
|
|
1717
2141
|
function globalOptions(program, commandOptions) {
|
|
1718
2142
|
const opts = program.opts();
|
|
1719
2143
|
return {
|
|
1720
2144
|
...opts.apiKey ? { apiKey: opts.apiKey } : {},
|
|
1721
2145
|
...opts.apiUrl ? { apiUrl: opts.apiUrl } : {},
|
|
1722
2146
|
...commandOptions.json !== void 0 ? { json: commandOptions.json } : opts.json !== void 0 ? { json: opts.json } : {},
|
|
1723
|
-
...commandOptions.quiet !== void 0 ? { quiet: commandOptions.quiet } : opts.quiet !== void 0 ? { quiet: opts.quiet } : {}
|
|
2147
|
+
...commandOptions.quiet !== void 0 ? { quiet: commandOptions.quiet } : opts.quiet !== void 0 ? { quiet: opts.quiet } : {},
|
|
2148
|
+
...opts.interactive !== void 0 ? { interactive: opts.interactive } : {}
|
|
1724
2149
|
};
|
|
1725
2150
|
}
|
|
1726
2151
|
function toDropOptions(options) {
|