@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/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.4.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.4.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 requireCredential({
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 result = raw.fromJson ? await deps.client.drops.createRaw(
1020
- await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson),
1021
- options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
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 (!deps.client.prepare) {
1248
+ if (!client.prepare) {
1071
1249
  throw new Error("Client does not support dry-run.");
1072
1250
  }
1073
- const prepared = await deps.client.prepare(input, options);
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
- const spin = shouldSpin(deps.outputMode) ? createSpinner("Updating\u2026", deps.stderr) : void 0;
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
- const parsed = await parseDropOptions(raw);
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
- ) : input ? await deps.client.update(
1358
+ ) : updateInput ? await deps.client.update(
1161
1359
  target,
1162
- input,
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
- await (0, import_promises4.writeFile)(path, `${JSON.stringify(credential, null, 2)}
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)(path, 384);
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.4.0").configureHelp({
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.command("publish [input...]").description(
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, deps);
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
- { ...commandOptions, interactive: deps.outputMode === "human" },
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
- { ...commandOptions, interactive: deps.outputMode === "human" },
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) {