@dropthis/cli 0.26.0 → 0.28.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,9 +230,23 @@ 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
  }
@@ -792,6 +807,32 @@ var COMMAND_ANNOTATIONS = {
792
807
  auth: "required",
793
808
  examples: ["dropthis api-keys create --label CI --json"]
794
809
  },
810
+ workspace: {
811
+ auth: "required",
812
+ examples: ["dropthis workspace list --json", "dropthis workspace use acme"]
813
+ },
814
+ "workspace list": {
815
+ auth: "required",
816
+ examples: ["dropthis workspace list --json"]
817
+ },
818
+ "workspace use": {
819
+ auth: "required",
820
+ examples: [
821
+ "dropthis workspace use acme",
822
+ "dropthis workspace use ws_team123"
823
+ ]
824
+ },
825
+ keys: {
826
+ auth: "required",
827
+ examples: ["dropthis keys create --service --workspace acme --json"]
828
+ },
829
+ "keys create": {
830
+ auth: "required",
831
+ examples: [
832
+ "dropthis keys create --json",
833
+ "dropthis keys create --service --workspace acme --json"
834
+ ]
835
+ },
795
836
  login: {
796
837
  examples: [
797
838
  "dropthis login verify --email user@example.com --otp 123456 --json"
@@ -932,6 +973,9 @@ function spreadData(data) {
932
973
  return data && typeof data === "object" ? data : { data };
933
974
  }
934
975
 
976
+ // src/version.ts
977
+ var CLI_VERSION = true ? "0.28.0" : "0.0.0-dev";
978
+
935
979
  // src/commands/doctor.ts
936
980
  async function runDoctor(_input, deps) {
937
981
  const stored = await deps.store.read();
@@ -942,13 +986,13 @@ async function runDoctor(_input, deps) {
942
986
  const authSource = credential?.source ?? "missing";
943
987
  const storageBackend = stored?.storage ?? "none";
944
988
  const pairs = [
945
- ["Version", "0.26.0"],
989
+ ["Version", CLI_VERSION],
946
990
  ["Auth", authSource],
947
991
  ["Storage", storageBackend]
948
992
  ];
949
993
  writeKv(deps, pairs, {
950
994
  ok: true,
951
- version: "0.26.0",
995
+ version: CLI_VERSION,
952
996
  auth: { source: authSource },
953
997
  storage: { backend: storageBackend }
954
998
  });
@@ -1496,6 +1540,58 @@ async function runDropsDelete(target, input, deps) {
1496
1540
  return { exitCode: 0 };
1497
1541
  }
1498
1542
 
1543
+ // src/commands/keys.ts
1544
+ async function runKeysCreate(input, deps) {
1545
+ try {
1546
+ await requireCredential({
1547
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
1548
+ env: deps.env,
1549
+ store: deps.store
1550
+ });
1551
+ } catch {
1552
+ return writeAuthError(deps);
1553
+ }
1554
+ const keyType = input.type ?? "delegated";
1555
+ if (input.workspace && keyType !== "service") {
1556
+ return writeError(
1557
+ deps,
1558
+ "invalid_usage",
1559
+ "--workspace is only valid with --service (it pins a CI service key to one workspace). A delegated login key is account-scoped and switchable \u2014 use `dropthis workspace use <slug>` to change its active workspace."
1560
+ );
1561
+ }
1562
+ if (keyType === "service" && !input.workspace) {
1563
+ return writeError(
1564
+ deps,
1565
+ "invalid_usage",
1566
+ "Service keys must be pinned to a workspace: pass --workspace <slug-or-id>. (A service key is for CI/automation and never switches workspace.)"
1567
+ );
1568
+ }
1569
+ const label = input.label ?? (keyType === "service" ? `Service key${input.workspace ? ` (${input.workspace})` : ""}` : "CLI key");
1570
+ const createInput = {
1571
+ label,
1572
+ type: keyType
1573
+ };
1574
+ if (keyType === "service" && input.workspace) {
1575
+ createInput.workspace = input.workspace;
1576
+ }
1577
+ const result = await deps.client.apiKeys.create(createInput);
1578
+ if (result.error) return writeApiError(deps, result.error);
1579
+ const data = result.data;
1580
+ const pairs = [];
1581
+ if (data.id) pairs.push(["ID", String(data.id)]);
1582
+ if (data.key) pairs.push(["Key", String(data.key)]);
1583
+ if (data.keyLast4) pairs.push(["Last 4", String(data.keyLast4)]);
1584
+ if (data.label) pairs.push(["Label", String(data.label)]);
1585
+ writeKv(deps, pairs, { ok: true, api_key: result.data });
1586
+ if (deps.outputMode === "human") {
1587
+ deps.stderr(
1588
+ `${hint("Store this key now \u2014 it will not be shown again.")}
1589
+ `
1590
+ );
1591
+ }
1592
+ return { exitCode: 0 };
1593
+ }
1594
+
1499
1595
  // src/commands/login.ts
1500
1596
  var prompts4 = __toESM(require("@clack/prompts"), 1);
1501
1597
  async function runLoginInteractive(deps) {
@@ -1553,7 +1649,10 @@ async function runLoginInteractive(deps) {
1553
1649
  return { exitCode: exitCodeFor("api_error") };
1554
1650
  }
1555
1651
  const authedClient = deps.createClient(session.data.token);
1556
- const apiKey = await authedClient.apiKeys.create({ label: "CLI" });
1652
+ const apiKey = await authedClient.apiKeys.create({
1653
+ label: "CLI",
1654
+ type: "delegated"
1655
+ });
1557
1656
  if (apiKey.error) {
1558
1657
  prompts4.cancel(apiKey.error.message);
1559
1658
  return { exitCode: exitCodeFor("api_error") };
@@ -1596,7 +1695,10 @@ async function runLoginVerify(input, deps) {
1596
1695
  return writeApiError(deps, session.error);
1597
1696
  }
1598
1697
  const authedClient = deps.createClient(session.data.token);
1599
- const apiKey = await authedClient.apiKeys.create({ label: "CLI" });
1698
+ const apiKey = await authedClient.apiKeys.create({
1699
+ label: "CLI",
1700
+ type: "delegated"
1701
+ });
1600
1702
  if (apiKey.error) {
1601
1703
  return writeApiError(deps, apiKey.error);
1602
1704
  }
@@ -1804,7 +1906,8 @@ async function parseDropOptions(raw) {
1804
1906
  ...raw.path ? { path: raw.path } : {},
1805
1907
  ...raw.idempotencyKey ? { idempotencyKey: raw.idempotencyKey } : {},
1806
1908
  ...raw.shared ? { domain: import_node.SHARED_POOL } : raw.domain ? { domain: raw.domain } : {},
1807
- ...raw.slug ? { slug: raw.slug } : {}
1909
+ ...raw.slug ? { slug: raw.slug } : {},
1910
+ ...raw.workspace ? { workspace: raw.workspace } : {}
1808
1911
  };
1809
1912
  }
1810
1913
  function parseJsonObject(value, label) {
@@ -2593,6 +2696,59 @@ async function runWhoami(_input, deps) {
2593
2696
  return { exitCode: 0 };
2594
2697
  }
2595
2698
 
2699
+ // src/commands/workspace.ts
2700
+ async function runWorkspaceList(_input, deps) {
2701
+ try {
2702
+ await requireCredential({
2703
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
2704
+ env: deps.env,
2705
+ store: deps.store
2706
+ });
2707
+ } catch {
2708
+ return writeAuthError(deps);
2709
+ }
2710
+ const result = await deps.client.workspaces.list();
2711
+ if (result.error) return writeApiError(deps, result.error);
2712
+ const raw = unwrapListData(result.data);
2713
+ const items = Array.isArray(raw) ? raw : [];
2714
+ if (deps.outputMode === "human") {
2715
+ if (items.length === 0) {
2716
+ writeEmpty(deps, "No workspaces found.", { ok: true, workspaces: [] });
2717
+ } else {
2718
+ for (const ws of items) {
2719
+ const active = ws.isActive ? "* " : " ";
2720
+ deps.stdout(
2721
+ `${active}${ws.slug} ${ws.name} (${ws.kind}, ${ws.role}, ${ws.plan})
2722
+ `
2723
+ );
2724
+ }
2725
+ }
2726
+ } else {
2727
+ writeJson(deps, { ok: true, workspaces: items });
2728
+ }
2729
+ return { exitCode: 0 };
2730
+ }
2731
+ async function runWorkspaceUse(slugOrId, _input, deps) {
2732
+ try {
2733
+ await requireCredential({
2734
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
2735
+ env: deps.env,
2736
+ store: deps.store
2737
+ });
2738
+ } catch {
2739
+ return writeAuthError(deps);
2740
+ }
2741
+ const result = await deps.client.workspaces.use(slugOrId);
2742
+ if (result.error) return writeApiError(deps, result.error);
2743
+ const ws = result.data;
2744
+ writeHumanOrJson(
2745
+ deps,
2746
+ success(`Now using workspace: ${ws.name} (${ws.slug})`),
2747
+ { ok: true, workspace: ws }
2748
+ );
2749
+ return { exitCode: 0 };
2750
+ }
2751
+
2596
2752
  // src/context.ts
2597
2753
  var import_node2 = require("@dropthis/node");
2598
2754
  function createContext(input) {
@@ -2801,7 +2957,7 @@ function buildProgram(options = {}) {
2801
2957
  ` ${import_picocolors6.default.cyan("dropthis login")}`,
2802
2958
  ` ${import_picocolors6.default.cyan("dropthis publish ./dist")}`
2803
2959
  ].join("\n")
2804
- ).version("0.26.0").configureHelp({
2960
+ ).version(CLI_VERSION).configureHelp({
2805
2961
  subcommandTerm(cmd) {
2806
2962
  const args = cmd.registeredArguments.map((a) => {
2807
2963
  const name = a.name();
@@ -2855,6 +3011,9 @@ ${lines.map((l) => ` ${l}`).join("\n")}
2855
3011
  ).option(
2856
3012
  "--manifest <file>",
2857
3013
  "Publish a multi-file bundle defined in a JSON file (path, content/source_url/content_base64 per file). Cannot combine with a positional input."
3014
+ ).option(
3015
+ "--workspace <slugOrId>",
3016
+ "Target workspace slug or id for this publish (delegated keys only; overrides server-side active workspace for this call)"
2858
3017
  ).option("--url", "Print only the published URL (no JSON envelope)").option(
2859
3018
  "--dry-run",
2860
3019
  "Show what would be published without publishing (JSON; cannot combine with --url)"
@@ -3276,6 +3435,47 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3276
3435
  process.exitCode = result.exitCode;
3277
3436
  }
3278
3437
  );
3438
+ const workspace = program.command("workspace").description(
3439
+ "Manage workspaces. Switch your active workspace (delegated keys only) or list all workspaces you belong to."
3440
+ );
3441
+ workspace.command("list", { isDefault: true }).description("List workspaces you belong to").option("--json", "Force JSON output").action(async (commandOptions) => {
3442
+ const deps = await commandDeps(program, options, store, commandOptions);
3443
+ const result = await runWorkspaceList(commandOptions, deps);
3444
+ process.exitCode = result.exitCode;
3445
+ });
3446
+ workspace.command("use <slugOrId>").description(
3447
+ "Switch your active workspace (server-side, persists on the credential). Delegated keys only \u2014 pinned service keys cannot switch."
3448
+ ).option("--json", "Force JSON output").action(async (slugOrId, commandOptions) => {
3449
+ const deps = await commandDeps(program, options, store, commandOptions);
3450
+ const result = await runWorkspaceUse(slugOrId, commandOptions, deps);
3451
+ process.exitCode = result.exitCode;
3452
+ });
3453
+ const keys = program.command("keys").description(
3454
+ "Mint API keys (delegated login keys or pinned CI service keys)"
3455
+ );
3456
+ keys.command("create").description(
3457
+ "Create an API key. Without --service, mints a delegated account-scoped key (default for login). With --service --workspace <ws>, mints a CI key pinned to that workspace."
3458
+ ).option("--label <label>", "Key label").option(
3459
+ "--service",
3460
+ "Mint a pinned service key for CI/automation (requires --workspace to pin it)"
3461
+ ).option(
3462
+ "--workspace <slugOrId>",
3463
+ "Pin this service key to the given workspace slug or id (only valid with --service)"
3464
+ ).option("--json", "Force JSON output").action(
3465
+ async (commandOptions) => {
3466
+ const deps = await commandDeps(program, options, store, commandOptions);
3467
+ const result = await runKeysCreate(
3468
+ {
3469
+ ...commandOptions.label !== void 0 ? { label: commandOptions.label } : {},
3470
+ type: commandOptions.service ? "service" : "delegated",
3471
+ ...commandOptions.workspace !== void 0 ? { workspace: commandOptions.workspace } : {},
3472
+ ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
3473
+ },
3474
+ deps
3475
+ );
3476
+ process.exitCode = result.exitCode;
3477
+ }
3478
+ );
3279
3479
  program.command("login").description("Authenticate with email OTP").option("--email <email>", "Email address").option("--otp <otp>", "One-time passcode").option("--json", "Force JSON output").action(
3280
3480
  async (commandOptions) => {
3281
3481
  if (!commandOptions.email || !commandOptions.otp) {
@@ -3403,7 +3603,7 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3403
3603
  );
3404
3604
  const result = await runUpgrade(commandOptions, {
3405
3605
  ...deps,
3406
- currentVersion: "0.26.0"
3606
+ currentVersion: CLI_VERSION
3407
3607
  });
3408
3608
  process.exitCode = result.exitCode;
3409
3609
  });
@@ -3514,6 +3714,7 @@ function toDropOptions(options) {
3514
3714
  ...options.shared ? { shared: true } : {},
3515
3715
  ...options.slug ? { slug: options.slug } : {},
3516
3716
  ...options.manifest ? { manifest: options.manifest } : {},
3717
+ ...options.workspace ? { workspace: options.workspace } : {},
3517
3718
  ...options.json !== void 0 ? { json: options.json } : {},
3518
3719
  ...options.quiet !== void 0 ? { quiet: options.quiet } : {},
3519
3720
  ...options.url !== void 0 ? { url: options.url } : {},
@@ -3657,7 +3858,7 @@ var showNotice = shouldShowNotice({
3657
3858
  if (showNotice) {
3658
3859
  const notice = noticeFromCache({
3659
3860
  cache: updateCache,
3660
- currentVersion: "0.26.0"
3861
+ currentVersion: CLI_VERSION
3661
3862
  });
3662
3863
  if (notice) {
3663
3864
  process.stderr.write(`