@dropthis/cli 0.27.0 → 0.29.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
@@ -218,6 +218,7 @@ function normalizeApiErrorCode(error2) {
218
218
  }
219
219
  function nextActionForApiError(error2, mode = "json") {
220
220
  const cliHint = cliNextAction(error2);
221
+ if (error2.code === "workspace_choice_required" && cliHint) return cliHint;
221
222
  if (mode === "human" && cliHint) return cliHint;
222
223
  if (error2.suggestion) return error2.suggestion;
223
224
  if (cliHint) return cliHint;
@@ -229,12 +230,32 @@ function nextActionForApiError(error2, mode = "json") {
229
230
  }
230
231
  return "Fix the request or retry after checking the drop state.";
231
232
  }
233
+ function workspaceChoiceSlugs(error2) {
234
+ const body = error2.body;
235
+ const raw = body && typeof body === "object" ? body.choices : void 0;
236
+ if (!Array.isArray(raw)) return [];
237
+ return raw.map(
238
+ (c) => c && typeof c === "object" ? c.slug : void 0
239
+ ).filter((s) => typeof s === "string" && s.length > 0);
240
+ }
232
241
  function cliNextAction(error2) {
233
242
  const uploadNextAction = error2.code ? UPLOAD_NEXT_ACTIONS[error2.code] : void 0;
234
243
  if (uploadNextAction) return uploadNextAction;
244
+ if (error2.code === "workspace_choice_required") {
245
+ const slugs = workspaceChoiceSlugs(error2);
246
+ const example = slugs[0] ?? "<slug>";
247
+ const list = slugs.length ? ` Workspaces: ${slugs.join(", ")}.` : "";
248
+ return `This account belongs to more than one workspace \u2014 pick one. Switch it for every later command with: dropthis workspace use ${example} \u2014 or target just this one with: --workspace ${example}.${list}`;
249
+ }
235
250
  if (error2.code === "network_error") {
236
251
  return "Could not reach the dropthis API \u2014 check your network or DROPTHIS_API_URL.";
237
252
  }
253
+ if (error2.code === "workspace_pinned") {
254
+ return "Service keys are pinned to one workspace. To switch workspaces, use a delegated login (run: dropthis login) instead of a service key.";
255
+ }
256
+ if (error2.code === "workspace_selector_not_allowed") {
257
+ return "Service keys (sk_) are pinned to one workspace and cannot target another with --workspace. Use a delegated login key (run: dropthis login), or run without --workspace.";
258
+ }
238
259
  if (error2.code === "revision_conflict") {
239
260
  return "Re-read the drop with dropthis get <drop-id>, merge your changes, and retry with --if-revision set to the current revision.";
240
261
  }
@@ -250,9 +271,15 @@ function cliNextAction(error2) {
250
271
  if (error2.code === "otp_invalid") {
251
272
  return "Re-enter the code from your email; if it keeps failing, request a fresh one with dropthis login request --email <email>.";
252
273
  }
274
+ if (error2.code === "workspace_not_found") {
275
+ return "Workspace not found. List your workspaces with: dropthis workspace list";
276
+ }
253
277
  if (error2.statusCode === 404 || error2.code === "not_found") {
254
278
  return "Check the drop id or slug and retry; list your drops with dropthis list.";
255
279
  }
280
+ if (error2.code === "invalid_api_key") {
281
+ return "API key is invalid or revoked. Run dropthis login to re-authenticate, or set a valid DROPTHIS_API_KEY.";
282
+ }
256
283
  if (error2.statusCode === 401 || error2.code === "missing_api_key") {
257
284
  return "Authenticate with dropthis login or set DROPTHIS_API_KEY.";
258
285
  }
@@ -706,7 +733,8 @@ var COMMAND_ANNOTATIONS = {
706
733
  "dropthis ./report.html",
707
734
  "dropthis publish ./site --json",
708
735
  "dropthis publish ./site --url",
709
- "dropthis publish ./dist --dry-run"
736
+ "dropthis publish ./dist --dry-run",
737
+ "dropthis publish ./report.html --workspace acme --json # target a specific workspace (delegated keys only)"
710
738
  ]
711
739
  },
712
740
  "update-content": {
@@ -792,6 +820,18 @@ var COMMAND_ANNOTATIONS = {
792
820
  auth: "required",
793
821
  examples: ["dropthis api-keys create --label CI --json"]
794
822
  },
823
+ "api-keys create": {
824
+ auth: "required",
825
+ examples: ["dropthis api-keys create --label CI --json"]
826
+ },
827
+ "api-keys list": {
828
+ auth: "required",
829
+ examples: ["dropthis api-keys list --json"]
830
+ },
831
+ "api-keys delete": {
832
+ auth: "required",
833
+ examples: ["dropthis api-keys delete key_abc --yes --json"]
834
+ },
795
835
  workspace: {
796
836
  auth: "required",
797
837
  examples: ["dropthis workspace list --json", "dropthis workspace use acme"]
@@ -819,6 +859,16 @@ var COMMAND_ANNOTATIONS = {
819
859
  ]
820
860
  },
821
861
  login: {
862
+ examples: [
863
+ "dropthis login",
864
+ "dropthis login request --email user@example.com --json",
865
+ "dropthis login verify --email user@example.com --otp 123456 --json"
866
+ ]
867
+ },
868
+ "login request": {
869
+ examples: ["dropthis login request --email user@example.com --json"]
870
+ },
871
+ "login verify": {
822
872
  examples: [
823
873
  "dropthis login verify --email user@example.com --otp 123456 --json"
824
874
  ]
@@ -959,7 +1009,7 @@ function spreadData(data) {
959
1009
  }
960
1010
 
961
1011
  // src/version.ts
962
- var CLI_VERSION = true ? "0.27.0" : "0.0.0-dev";
1012
+ var CLI_VERSION = true ? "0.29.0" : "0.0.0-dev";
963
1013
 
964
1014
  // src/commands/doctor.ts
965
1015
  async function runDoctor(_input, deps) {
@@ -1071,6 +1121,13 @@ function computeSleepMs(records, elapsedMs, timeoutMs) {
1071
1121
  return Math.min(desiredMs, Math.max(0, remainingMs));
1072
1122
  }
1073
1123
  async function runDomainsConnect(input, deps) {
1124
+ if (input.mode !== "path" && input.mode !== "dedicated") {
1125
+ return writeError(
1126
+ deps,
1127
+ "invalid_usage",
1128
+ '--mode must be "path" or "dedicated"'
1129
+ );
1130
+ }
1074
1131
  try {
1075
1132
  await requireCredential({
1076
1133
  ...deps.apiKey ? { apiKey: deps.apiKey } : {},
@@ -1080,7 +1137,9 @@ async function runDomainsConnect(input, deps) {
1080
1137
  } catch {
1081
1138
  return writeAuthError(deps);
1082
1139
  }
1083
- const result = await deps.client.domains.connect(input);
1140
+ const result = await deps.client.domains.connect(
1141
+ input
1142
+ );
1084
1143
  if (result.error) return writeApiError(deps, result.error);
1085
1144
  const domain = result.data;
1086
1145
  if (deps.outputMode === "human") {
@@ -1198,6 +1257,27 @@ async function runDomainsVerify(idOrHostname, input, deps, sleep = defaultSleep)
1198
1257
  if (result.error) return writeApiError(deps, result.error);
1199
1258
  const domain = result.data;
1200
1259
  if (!input.wait) {
1260
+ if (domain.status !== "live" && domain.status !== "failed") {
1261
+ if (deps.outputMode === "human") {
1262
+ deps.stdout(`${warn(`${idOrHostname}: ${domain.status}`)}
1263
+ `);
1264
+ if (domain.dns.length > 0) {
1265
+ deps.stdout("\n");
1266
+ deps.stdout(`${formatDnsTable(domain.dns)}
1267
+ `);
1268
+ }
1269
+ printNextHints(deps, domain.next, domain.hostname, domain.dns);
1270
+ printConsoleHandoff(deps, domain);
1271
+ } else {
1272
+ writeError(
1273
+ deps,
1274
+ "verify_pending",
1275
+ `${idOrHostname}: ${domain.status}`,
1276
+ `Re-run: dropthis domains verify ${idOrHostname}`
1277
+ );
1278
+ }
1279
+ return { exitCode: 6 };
1280
+ }
1201
1281
  if (deps.outputMode === "human") {
1202
1282
  deps.stdout(
1203
1283
  `${domain.status === "live" ? success(`${idOrHostname} is live`) : warn(`${idOrHostname}: ${domain.status}`)}
@@ -1214,7 +1294,7 @@ async function runDomainsVerify(idOrHostname, input, deps, sleep = defaultSleep)
1214
1294
  writeJson(deps, { ok: true, domain });
1215
1295
  }
1216
1296
  return {
1217
- exitCode: domain.status === "live" ? 0 : domain.status === "failed" ? 1 : 6
1297
+ exitCode: domain.status === "live" ? 0 : 1
1218
1298
  };
1219
1299
  }
1220
1300
  if (domain.status === "live" || domain.status === "failed") {
@@ -1245,12 +1325,19 @@ async function runDomainsVerify(idOrHostname, input, deps, sleep = defaultSleep)
1245
1325
  );
1246
1326
  } else {
1247
1327
  deps.stderr(
1248
- `${JSON.stringify(errorEnvelope("verify_timeout", message))}
1328
+ `${JSON.stringify(errorEnvelope("verify_timeout", message, `DNS is still propagating. Re-run: dropthis domains verify ${idOrHostname}`))}
1249
1329
  `
1250
1330
  );
1251
1331
  }
1252
1332
  return { exitCode: 7 };
1253
1333
  }
1334
+ if (deps.outputMode === "json" && domain.dns.length > 0) {
1335
+ const elapsed = Math.round(elapsedMs / 1e3);
1336
+ deps.stderr(
1337
+ `${JSON.stringify({ ok: false, pending: true, status: domain.status, elapsed })}
1338
+ `
1339
+ );
1340
+ }
1254
1341
  if (deps.outputMode === "human" && domain.dns.length > 0) {
1255
1342
  deps.stdout(`${formatDnsTable(domain.dns)}
1256
1343
  `);
@@ -1548,7 +1635,8 @@ async function runKeysCreate(input, deps) {
1548
1635
  return writeError(
1549
1636
  deps,
1550
1637
  "invalid_usage",
1551
- "Service keys must be pinned to a workspace: pass --workspace <slug-or-id>. (A service key is for CI/automation and never switches workspace.)"
1638
+ "Service keys must be pinned to a workspace.",
1639
+ "Run: dropthis workspace list to see slugs, then: dropthis keys create --service --workspace <slug>"
1552
1640
  );
1553
1641
  }
1554
1642
  const label = input.label ?? (keyType === "service" ? `Service key${input.workspace ? ` (${input.workspace})` : ""}` : "CLI key");
@@ -2709,7 +2797,8 @@ async function runWorkspaceList(_input, deps) {
2709
2797
  }
2710
2798
  }
2711
2799
  } else {
2712
- writeJson(deps, { ok: true, workspaces: items });
2800
+ const activeWorkspace = items.find((w) => w.isActive) ?? null;
2801
+ writeJson(deps, { ok: true, workspaces: items, active: activeWorkspace });
2713
2802
  }
2714
2803
  return { exitCode: 0 };
2715
2804
  }
@@ -3314,9 +3403,11 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3314
3403
  ).action(
3315
3404
  async (hostname, commandOptions) => {
3316
3405
  if (commandOptions.mode !== "path" && commandOptions.mode !== "dedicated") {
3317
- process.stderr.write(`error: --mode must be "path" or "dedicated"
3318
- `);
3319
- process.exitCode = 2;
3406
+ process.exitCode = writeError(
3407
+ outputWriter(program, options, commandOptions),
3408
+ "invalid_usage",
3409
+ '--mode must be "path" or "dedicated"'
3410
+ ).exitCode;
3320
3411
  return;
3321
3412
  }
3322
3413
  const deps = await commandDeps(program, options, store, commandOptions);
@@ -3338,7 +3429,7 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3338
3429
  process.exitCode = result.exitCode;
3339
3430
  });
3340
3431
  domains.command("verify <hostname>").description(
3341
- "Trigger a DNS verification check. With --wait, polls until live or failed.\nUse after adding the DNS records from: dropthis domains connect"
3432
+ "Trigger a DNS verification check. With --wait, polls until live or failed.\nUse after adding the DNS records from: dropthis domains connect\n\nExit codes: 0=live, 1=failed, 6=still pending (retry). Use --wait to poll automatically."
3342
3433
  ).option("--wait", "Poll until live (or failed/timeout)").option(
3343
3434
  "--timeout <secs>",
3344
3435
  "Max seconds to wait (default: 300)",
@@ -3604,6 +3695,21 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3604
3695
  });
3605
3696
  return program;
3606
3697
  }
3698
+ function outputWriter(program, options, commandOptions) {
3699
+ const global = globalOptions(program, commandOptions);
3700
+ const context = createContext({
3701
+ global,
3702
+ ...options.env !== void 0 ? { env: options.env } : {},
3703
+ ...options.stdout !== void 0 ? { stdout: options.stdout } : {},
3704
+ ...options.stderr !== void 0 ? { stderr: options.stderr } : {},
3705
+ ...options.stdoutIsTTY !== void 0 ? { stdoutIsTTY: options.stdoutIsTTY } : {}
3706
+ });
3707
+ return {
3708
+ stdout: context.stdout,
3709
+ stderr: context.stderr,
3710
+ outputMode: context.output.mode
3711
+ };
3712
+ }
3607
3713
  async function commandDeps(program, options, store, commandOptions) {
3608
3714
  const global = globalOptions(program, commandOptions);
3609
3715
  const context = createContext({