@dropthis/cli 0.31.0 → 0.33.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
@@ -305,6 +305,16 @@ function exitCodeFor(code) {
305
305
  if (code === "verify_timeout") return 7;
306
306
  return 1;
307
307
  }
308
+ var EXIT_CODES = {
309
+ "0": "success",
310
+ "1": "api_error \u2014 the server returned an error (see the error envelope)",
311
+ "2": "invalid_usage \u2014 bad arguments, bad options, or an unknown command",
312
+ "3": "auth_error \u2014 missing or invalid credential; re-authenticate (dropthis login)",
313
+ "4": "local_input_error \u2014 a local file or path could not be read",
314
+ "5": "network_error \u2014 could not reach the API; safe to retry",
315
+ "6": "verify_pending \u2014 a domain is not verified yet; retry later (domains verify)",
316
+ "7": "verify_timeout \u2014 domain verification timed out while waiting (--wait)"
317
+ };
308
318
 
309
319
  // src/program.ts
310
320
  var import_commander = require("commander");
@@ -324,7 +334,8 @@ async function resolveCredential(input) {
324
334
  ...stored.keyId ? { keyId: stored.keyId } : {},
325
335
  ...stored.keyLast4 ? { keyLast4: stored.keyLast4 } : {},
326
336
  ...stored.accountId ? { accountId: stored.accountId } : {},
327
- ...stored.email ? { email: stored.email } : {}
337
+ ...stored.email ? { email: stored.email } : {},
338
+ ...stored.scopes?.length ? { scopes: stored.scopes } : {}
328
339
  };
329
340
  }
330
341
  async function requireCredential(input) {
@@ -673,14 +684,55 @@ async function runApiKeysCreate(input, deps) {
673
684
  } catch {
674
685
  return writeAuthError(deps);
675
686
  }
676
- const result = await deps.client.apiKeys.create({ label: input.label });
687
+ const keyType = input.type ?? "delegated";
688
+ if (input.workspace && keyType !== "service") {
689
+ return writeError(
690
+ deps,
691
+ "invalid_usage",
692
+ "--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."
693
+ );
694
+ }
695
+ if (input.allowedWorkspaces?.length && keyType === "service") {
696
+ return writeError(
697
+ deps,
698
+ "invalid_usage",
699
+ "--allowed-workspace is only valid for delegated keys; do not combine it with --service."
700
+ );
701
+ }
702
+ if (keyType === "service" && !input.workspace) {
703
+ return writeError(
704
+ deps,
705
+ "invalid_usage",
706
+ "Service keys must be pinned to a workspace.",
707
+ "Run: dropthis workspace list to see slugs, then: dropthis api-keys create --service --workspace <slug>"
708
+ );
709
+ }
710
+ const label = input.label ?? (keyType === "service" ? `Service key${input.workspace ? ` (${input.workspace})` : ""}` : "CLI key");
711
+ const createInput = {
712
+ label,
713
+ type: keyType
714
+ };
715
+ if (keyType === "service" && input.workspace) {
716
+ createInput.workspace = input.workspace;
717
+ }
718
+ if (input.allowedWorkspaces?.length) {
719
+ createInput.allowedWorkspaces = input.allowedWorkspaces;
720
+ }
721
+ const result = await deps.client.apiKeys.create(createInput);
677
722
  if (result.error) return writeApiError(deps, result.error);
678
723
  const data = result.data;
679
724
  const pairs = [];
680
725
  if (data.id) pairs.push(["ID", String(data.id)]);
681
726
  if (data.key) pairs.push(["Key", String(data.key)]);
682
727
  if (data.keyLast4) pairs.push(["Last 4", String(data.keyLast4)]);
728
+ if (data.label) pairs.push(["Label", String(data.label)]);
683
729
  writeKv(deps, pairs, { ok: true, api_key: result.data });
730
+ if (deps.outputMode === "human") {
731
+ deps.stderr(
732
+ `${hint("Store this key now \u2014 it will not be shown again.")}
733
+ `
734
+ );
735
+ }
684
736
  return { exitCode: 0 };
685
737
  }
686
738
  async function runApiKeysList(_input, deps) {
@@ -845,7 +897,10 @@ var COMMAND_ANNOTATIONS = {
845
897
  },
846
898
  "api-keys create": {
847
899
  auth: "required",
848
- examples: ["dropthis api-keys create --label CI --json"]
900
+ examples: [
901
+ "dropthis api-keys create --label CI --json",
902
+ "dropthis api-keys create --service --workspace acme --json"
903
+ ]
849
904
  },
850
905
  "api-keys list": {
851
906
  auth: "required",
@@ -870,17 +925,6 @@ var COMMAND_ANNOTATIONS = {
870
925
  "dropthis workspace use ws_team123"
871
926
  ]
872
927
  },
873
- keys: {
874
- auth: "required",
875
- examples: ["dropthis keys create --service --workspace acme --json"]
876
- },
877
- "keys create": {
878
- auth: "required",
879
- examples: [
880
- "dropthis keys create --json",
881
- "dropthis keys create --service --workspace acme --json"
882
- ]
883
- },
884
928
  login: {
885
929
  examples: [
886
930
  "dropthis login",
@@ -908,7 +952,9 @@ var COMMAND_ANNOTATIONS = {
908
952
  auth: "required",
909
953
  examples: ["dropthis account delete --yes --json"]
910
954
  },
911
- doctor: { examples: ["dropthis doctor --json"] },
955
+ doctor: {
956
+ examples: ["dropthis doctor --json", "dropthis doctor --online --json"]
957
+ },
912
958
  upgrade: { examples: ["dropthis upgrade", "dropthis upgrade --json"] },
913
959
  commands: { examples: ["dropthis commands --json"] }
914
960
  };
@@ -935,6 +981,12 @@ function toEntry(cmd, path) {
935
981
  function buildCatalog(program) {
936
982
  return program.commands.map((cmd) => toEntry(cmd, []));
937
983
  }
984
+ function buildGlobalOptions(program) {
985
+ return program.options.map((o) => ({
986
+ flags: o.flags,
987
+ description: o.description
988
+ }));
989
+ }
938
990
 
939
991
  // src/commands/commands.ts
940
992
  async function runCommands(_input, deps) {
@@ -942,7 +994,9 @@ async function runCommands(_input, deps) {
942
994
  `${JSON.stringify({
943
995
  ok: true,
944
996
  output: "JSON by default in CI, pipes, non-TTY, --json, or --quiet.",
945
- commands: buildCatalog(deps.program)
997
+ commands: buildCatalog(deps.program),
998
+ global_options: buildGlobalOptions(deps.program),
999
+ exit_codes: EXIT_CODES
946
1000
  })}
947
1001
  `
948
1002
  );
@@ -1032,10 +1086,76 @@ function spreadData(data) {
1032
1086
  }
1033
1087
 
1034
1088
  // src/version.ts
1035
- var CLI_VERSION = true ? "0.31.0" : "0.0.0-dev";
1089
+ var CLI_VERSION = true ? "0.33.0" : "0.0.0-dev";
1036
1090
 
1037
1091
  // src/commands/doctor.ts
1038
- async function runDoctor(_input, deps) {
1092
+ async function onlineChecks(deps, hasCredential) {
1093
+ if (!hasCredential) {
1094
+ return {
1095
+ checks: [
1096
+ {
1097
+ name: "auth",
1098
+ status: "fail",
1099
+ detail: "No credential found \u2014 run `dropthis login` or set DROPTHIS_API_KEY."
1100
+ }
1101
+ ],
1102
+ exitCode: exitCodeFor("auth_error")
1103
+ };
1104
+ }
1105
+ const account = await deps.client.account.get();
1106
+ if (account.error) {
1107
+ const code = cliErrorCodeFor(account.error);
1108
+ const isAuth = code === "auth_error";
1109
+ return {
1110
+ checks: [
1111
+ {
1112
+ name: "api_reachable",
1113
+ status: isAuth ? "ok" : "fail",
1114
+ detail: isAuth ? "reached the API" : `could not reach the API: ${account.error.message}`
1115
+ },
1116
+ {
1117
+ name: "auth",
1118
+ status: isAuth ? "fail" : "warn",
1119
+ detail: isAuth ? "credential rejected \u2014 re-authenticate with `dropthis login`" : account.error.message
1120
+ }
1121
+ ],
1122
+ exitCode: exitCodeFor(code)
1123
+ };
1124
+ }
1125
+ const data = account.data;
1126
+ const checks = [
1127
+ { name: "api_reachable", status: "ok", detail: "reached the API" },
1128
+ { name: "auth", status: "ok", detail: "credential accepted" }
1129
+ ];
1130
+ const ws = data?.workspace;
1131
+ checks.push(
1132
+ ws?.name ? {
1133
+ name: "workspace",
1134
+ status: "ok",
1135
+ detail: `active: ${String(ws.name)} (${String(ws.kind)})`
1136
+ } : {
1137
+ name: "workspace",
1138
+ status: "warn",
1139
+ detail: "no active workspace resolved \u2014 pass --workspace or run `dropthis workspace use`"
1140
+ }
1141
+ );
1142
+ if (data?.plan) {
1143
+ checks.push({ name: "plan", status: "ok", detail: String(data.plan) });
1144
+ }
1145
+ const domains = await deps.client.domains.list();
1146
+ if (!domains.error) {
1147
+ const list = domains.data?.domains ?? [];
1148
+ const live = list.filter((d) => d.status === "live").length;
1149
+ const pending = list.length - live;
1150
+ checks.push({
1151
+ name: "domains",
1152
+ status: pending > 0 ? "warn" : "ok",
1153
+ detail: `${list.length} connected, ${live} live${pending > 0 ? `, ${pending} pending verification` : ""}`
1154
+ });
1155
+ }
1156
+ return { checks, exitCode: 0 };
1157
+ }
1158
+ async function runDoctor(input, deps) {
1039
1159
  const stored = await deps.store.read();
1040
1160
  const credential = await resolveCredential({
1041
1161
  env: deps.env,
@@ -1048,13 +1168,30 @@ async function runDoctor(_input, deps) {
1048
1168
  ["Auth", authSource],
1049
1169
  ["Storage", storageBackend]
1050
1170
  ];
1171
+ if (!input.online) {
1172
+ writeKv(deps, pairs, {
1173
+ ok: true,
1174
+ version: CLI_VERSION,
1175
+ auth: { source: authSource },
1176
+ storage: { backend: storageBackend }
1177
+ });
1178
+ return { exitCode: 0 };
1179
+ }
1180
+ const { checks, exitCode } = await onlineChecks(
1181
+ deps,
1182
+ credential !== void 0
1183
+ );
1184
+ for (const c of checks) {
1185
+ pairs.push([c.name, `${c.status} \u2014 ${c.detail}`]);
1186
+ }
1051
1187
  writeKv(deps, pairs, {
1052
- ok: true,
1188
+ ok: exitCode === 0,
1053
1189
  version: CLI_VERSION,
1054
1190
  auth: { source: authSource },
1055
- storage: { backend: storageBackend }
1191
+ storage: { backend: storageBackend },
1192
+ checks
1056
1193
  });
1057
- return { exitCode: 0 };
1194
+ return { exitCode };
1058
1195
  }
1059
1196
 
1060
1197
  // src/commands/domains.ts
@@ -1635,8 +1772,8 @@ async function runDropsDelete(target, input, deps) {
1635
1772
  return { exitCode: 0 };
1636
1773
  }
1637
1774
 
1638
- // src/commands/keys.ts
1639
- async function runKeysCreate(input, deps) {
1775
+ // src/commands/invitations.ts
1776
+ async function runInvitationsList(_input, deps) {
1640
1777
  try {
1641
1778
  await requireCredential({
1642
1779
  ...deps.apiKey ? { apiKey: deps.apiKey } : {},
@@ -1646,45 +1783,67 @@ async function runKeysCreate(input, deps) {
1646
1783
  } catch {
1647
1784
  return writeAuthError(deps);
1648
1785
  }
1649
- const keyType = input.type ?? "delegated";
1650
- if (input.workspace && keyType !== "service") {
1651
- return writeError(
1652
- deps,
1653
- "invalid_usage",
1654
- "--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."
1655
- );
1656
- }
1657
- if (keyType === "service" && !input.workspace) {
1658
- return writeError(
1659
- deps,
1660
- "invalid_usage",
1661
- "Service keys must be pinned to a workspace.",
1662
- "Run: dropthis workspace list to see slugs, then: dropthis keys create --service --workspace <slug>"
1663
- );
1786
+ const result = await deps.client.invitations.list();
1787
+ if (result.error) return writeApiError(deps, result.error);
1788
+ const raw = unwrapListData(result.data, "invitations");
1789
+ const items = Array.isArray(raw) ? raw : [];
1790
+ if (deps.outputMode === "human") {
1791
+ if (items.length === 0) {
1792
+ writeEmpty(deps, "No pending invitations.", {
1793
+ ok: true,
1794
+ invitations: []
1795
+ });
1796
+ } else {
1797
+ for (const inv of items) {
1798
+ deps.stdout(`${inv.email} ${inv.role} ${inv.status} ${inv.id}
1799
+ `);
1800
+ }
1801
+ }
1802
+ } else {
1803
+ writeJson(deps, { ok: true, invitations: items });
1664
1804
  }
1665
- const label = input.label ?? (keyType === "service" ? `Service key${input.workspace ? ` (${input.workspace})` : ""}` : "CLI key");
1666
- const createInput = {
1667
- label,
1668
- type: keyType
1669
- };
1670
- if (keyType === "service" && input.workspace) {
1671
- createInput.workspace = input.workspace;
1805
+ return { exitCode: 0 };
1806
+ }
1807
+ async function runInvitationsAccept(input, deps) {
1808
+ try {
1809
+ await requireCredential({
1810
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
1811
+ env: deps.env,
1812
+ store: deps.store
1813
+ });
1814
+ } catch {
1815
+ return writeAuthError(deps);
1672
1816
  }
1673
- const result = await deps.client.apiKeys.create(createInput);
1817
+ const result = await deps.client.invitations.accept({ token: input.token });
1674
1818
  if (result.error) return writeApiError(deps, result.error);
1675
- const data = result.data;
1676
- const pairs = [];
1677
- if (data.id) pairs.push(["ID", String(data.id)]);
1678
- if (data.key) pairs.push(["Key", String(data.key)]);
1679
- if (data.keyLast4) pairs.push(["Last 4", String(data.keyLast4)]);
1680
- if (data.label) pairs.push(["Label", String(data.label)]);
1681
- writeKv(deps, pairs, { ok: true, api_key: result.data });
1682
- if (deps.outputMode === "human") {
1683
- deps.stderr(
1684
- `${hint("Store this key now \u2014 it will not be shown again.")}
1685
- `
1686
- );
1819
+ const ws = result.data;
1820
+ writeHumanOrJson(
1821
+ deps,
1822
+ success(`Joined ${ws.name} (${ws.slug}) \u2014 now your active workspace.`),
1823
+ { ok: true, workspace: ws }
1824
+ );
1825
+ return { exitCode: 0 };
1826
+ }
1827
+ async function runInvitationsAcceptById(input, deps) {
1828
+ try {
1829
+ await requireCredential({
1830
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
1831
+ env: deps.env,
1832
+ store: deps.store
1833
+ });
1834
+ } catch {
1835
+ return writeAuthError(deps);
1687
1836
  }
1837
+ const result = await deps.client.invitations.acceptById({
1838
+ invitationId: input.invitationId
1839
+ });
1840
+ if (result.error) return writeApiError(deps, result.error);
1841
+ const ws = result.data;
1842
+ writeHumanOrJson(
1843
+ deps,
1844
+ success(`Joined ${ws.name} (${ws.slug}) \u2014 now your active workspace.`),
1845
+ { ok: true, workspace: ws }
1846
+ );
1688
1847
  return { exitCode: 0 };
1689
1848
  }
1690
1849
 
@@ -1747,7 +1906,8 @@ async function runLoginInteractive(deps) {
1747
1906
  const authedClient = deps.createClient(session.data.token);
1748
1907
  const apiKey = await authedClient.apiKeys.create({
1749
1908
  label: "CLI",
1750
- type: "delegated"
1909
+ type: "delegated",
1910
+ ...deps.scope ? { scopes: [deps.scope] } : {}
1751
1911
  });
1752
1912
  if (apiKey.error) {
1753
1913
  prompts4.cancel(apiKey.error.message);
@@ -1759,6 +1919,7 @@ async function runLoginInteractive(deps) {
1759
1919
  keyLast4: apiKey.data.keyLast4,
1760
1920
  accountId: apiKey.data.accountId ?? session.data.accountId,
1761
1921
  email,
1922
+ ...apiKey.data.scopes ? { scopes: apiKey.data.scopes } : {},
1762
1923
  storage: "secure"
1763
1924
  });
1764
1925
  prompts4.outro("Logged in successfully.");
@@ -1793,7 +1954,10 @@ async function runLoginVerify(input, deps) {
1793
1954
  const authedClient = deps.createClient(session.data.token);
1794
1955
  const apiKey = await authedClient.apiKeys.create({
1795
1956
  label: "CLI",
1796
- type: "delegated"
1957
+ type: "delegated",
1958
+ // A single bundle name (e.g. "team") requests a capability-scoped key; the
1959
+ // server returns the resolved scope list on the mint response.
1960
+ ...input.scope ? { scopes: [input.scope] } : {}
1797
1961
  });
1798
1962
  if (apiKey.error) {
1799
1963
  return writeApiError(deps, apiKey.error);
@@ -1804,6 +1968,7 @@ async function runLoginVerify(input, deps) {
1804
1968
  keyLast4: apiKey.data.keyLast4,
1805
1969
  accountId: apiKey.data.accountId ?? session.data.accountId,
1806
1970
  email: input.email,
1971
+ ...apiKey.data.scopes ? { scopes: apiKey.data.scopes } : {},
1807
1972
  storage: "secure"
1808
1973
  });
1809
1974
  writeHumanOrJson(deps, success(`Logged in as ${input.email}.`), {
@@ -1832,6 +1997,115 @@ async function runLogout(input, deps) {
1832
1997
  return { exitCode: 0 };
1833
1998
  }
1834
1999
 
2000
+ // src/commands/members.ts
2001
+ var prompts5 = __toESM(require("@clack/prompts"), 1);
2002
+ async function runMembersList(workspaceId, _input, deps) {
2003
+ try {
2004
+ await requireCredential({
2005
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
2006
+ env: deps.env,
2007
+ store: deps.store
2008
+ });
2009
+ } catch {
2010
+ return writeAuthError(deps);
2011
+ }
2012
+ const result = await deps.client.members.list(workspaceId);
2013
+ if (result.error) return writeApiError(deps, result.error);
2014
+ const raw = unwrapListData(result.data, "members");
2015
+ const items = Array.isArray(raw) ? raw : [];
2016
+ if (deps.outputMode === "human") {
2017
+ if (items.length === 0) {
2018
+ writeEmpty(deps, "No members.", { ok: true, members: [] });
2019
+ } else {
2020
+ for (const m of items) {
2021
+ const who = m.email || m.accountId;
2022
+ const you = m.isYou ? "(you)" : "";
2023
+ deps.stdout(`${who} ${m.role} ${you}
2024
+ `);
2025
+ }
2026
+ }
2027
+ } else {
2028
+ writeJson(deps, { ok: true, members: items });
2029
+ }
2030
+ return { exitCode: 0 };
2031
+ }
2032
+ async function runMembersInvite(workspaceId, input, deps) {
2033
+ try {
2034
+ await requireCredential({
2035
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
2036
+ env: deps.env,
2037
+ store: deps.store
2038
+ });
2039
+ } catch {
2040
+ return writeAuthError(deps);
2041
+ }
2042
+ const role = input.role ?? "member";
2043
+ const result = await deps.client.members.invite(workspaceId, {
2044
+ email: input.email,
2045
+ role
2046
+ });
2047
+ if (result.error) return writeApiError(deps, result.error);
2048
+ writeHumanOrJson(deps, success(`Invited ${input.email} as ${role}`), {
2049
+ ok: true,
2050
+ invitation: result.data
2051
+ });
2052
+ return { exitCode: 0 };
2053
+ }
2054
+ async function runMembersRole(workspaceId, accountId, input, deps) {
2055
+ try {
2056
+ await requireCredential({
2057
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
2058
+ env: deps.env,
2059
+ store: deps.store
2060
+ });
2061
+ } catch {
2062
+ return writeAuthError(deps);
2063
+ }
2064
+ const result = await deps.client.members.updateRole(workspaceId, accountId, {
2065
+ role: input.role
2066
+ });
2067
+ if (result.error) return writeApiError(deps, result.error);
2068
+ writeHumanOrJson(deps, success(`Set ${accountId} to ${input.role}`), {
2069
+ ok: true,
2070
+ member: result.data
2071
+ });
2072
+ return { exitCode: 0 };
2073
+ }
2074
+ async function runMembersRemove(workspaceId, accountId, input, deps) {
2075
+ if (!input.yes && input.interactive === false) {
2076
+ return writeError(
2077
+ deps,
2078
+ "invalid_usage",
2079
+ "Pass --yes to remove in non-interactive mode."
2080
+ );
2081
+ }
2082
+ if (!input.yes && input.interactive !== false) {
2083
+ const confirmed = await prompts5.confirm({
2084
+ message: `Remove ${accountId} from workspace ${workspaceId}?`
2085
+ });
2086
+ if (prompts5.isCancel(confirmed) || !confirmed) {
2087
+ return { exitCode: 0 };
2088
+ }
2089
+ }
2090
+ try {
2091
+ await requireCredential({
2092
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
2093
+ env: deps.env,
2094
+ store: deps.store
2095
+ });
2096
+ } catch {
2097
+ return writeAuthError(deps);
2098
+ }
2099
+ const result = await deps.client.members.remove(workspaceId, accountId);
2100
+ if (result?.error) return writeApiError(deps, result.error);
2101
+ writeHumanOrJson(deps, success(`Removed ${accountId}`), {
2102
+ ok: true,
2103
+ removed: true,
2104
+ account_id: accountId
2105
+ });
2106
+ return { exitCode: 0 };
2107
+ }
2108
+
1835
2109
  // src/drop-operations.ts
1836
2110
  var import_node_crypto = require("crypto");
1837
2111
  var import_promises = require("fs/promises");
@@ -1968,6 +2242,9 @@ async function parseDropOptions(raw) {
1968
2242
  if (raw.noindex && raw.index) {
1969
2243
  throw new Error("Use either --noindex or --index, not both.");
1970
2244
  }
2245
+ if (raw.expiresAt && raw.noExpires) {
2246
+ throw new Error("Use either --expires-at or --no-expires, not both.");
2247
+ }
1971
2248
  if (raw.shared && raw.domain) {
1972
2249
  throw new Error("Use either --shared or --domain <hostname>, not both.");
1973
2250
  }
@@ -1996,6 +2273,7 @@ async function parseDropOptions(raw) {
1996
2273
  ...raw.noindex ? { noindex: true } : {},
1997
2274
  ...raw.index ? { noindex: false } : {},
1998
2275
  ...raw.expiresAt ? { expiresAt: raw.expiresAt } : {},
2276
+ ...raw.noExpires ? { expiresAt: null } : {},
1999
2277
  ...metadata ? { metadata } : {},
2000
2278
  ...raw.entry ? { entry: raw.entry } : {},
2001
2279
  ...raw.contentType ? { contentType: raw.contentType } : {},
@@ -2197,7 +2475,9 @@ async function runPull(target, input, deps) {
2197
2475
  const dropId = resolved.dropId;
2198
2476
  const defaultDirName = resolved.slug || resolved.dropId;
2199
2477
  const spin = shouldSpin(deps.outputMode) ? createSpinner("Pulling content\u2026", deps.stderr) : void 0;
2200
- const manifestResult = await deps.client.drops.getContent(dropId);
2478
+ const deploymentId = input.deployment;
2479
+ const manifestOptions = deploymentId ? { deploymentId } : void 0;
2480
+ const manifestResult = manifestOptions ? await deps.client.drops.getContent(dropId, manifestOptions) : await deps.client.drops.getContent(dropId);
2201
2481
  if (manifestResult.error) {
2202
2482
  spin?.fail();
2203
2483
  return writeApiError(deps, manifestResult.error);
@@ -2221,7 +2501,10 @@ async function runPull(target, input, deps) {
2221
2501
  try {
2222
2502
  await (0, import_promises3.mkdir)(outDir, { recursive: true });
2223
2503
  for (const { path, dest } of destinations) {
2224
- const fileResult = await deps.client.drops.getContent(dropId, { path });
2504
+ const fileResult = await deps.client.drops.getContent(dropId, {
2505
+ path,
2506
+ ...deploymentId ? { deploymentId } : {}
2507
+ });
2225
2508
  if (fileResult.error) {
2226
2509
  spin?.fail();
2227
2510
  return writeApiError(deps, {
@@ -2408,9 +2691,9 @@ async function handleDryRun(dropId, input, raw, deps) {
2408
2691
  }
2409
2692
 
2410
2693
  // src/commands/update-settings.ts
2411
- var prompts5 = __toESM(require("@clack/prompts"), 1);
2694
+ var prompts6 = __toESM(require("@clack/prompts"), 1);
2412
2695
  var NO_SETTINGS_MESSAGE = "Nothing to update. Provide at least one settings option.";
2413
- 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.";
2696
+ var NO_SETTINGS_NEXT_ACTION = "Run dropthis update-settings --help, or retry with one of: --title, --visibility, --password, --no-password, --noindex, --index, --expires-at, --no-expires, --domain, --shared, --slug, --metadata, or --metadata-file. To replace the content at the URL instead, use dropthis update-content <dropId> ./dist.";
2414
2697
  async function runUpdateSettings(target, raw, deps) {
2415
2698
  const apiKey = raw.apiKey ?? deps.apiKey;
2416
2699
  try {
@@ -2440,7 +2723,7 @@ async function runUpdateSettings(target, raw, deps) {
2440
2723
  if (shouldPrompt(deps)) {
2441
2724
  const prompted = await promptForSettings(
2442
2725
  dropId,
2443
- deps.prompts ?? prompts5
2726
+ deps.prompts ?? prompts6
2444
2727
  );
2445
2728
  if (!prompted) return { exitCode: 0 };
2446
2729
  parsed = { ...parsed, ...prompted };
@@ -2629,8 +2912,10 @@ async function handleDryRun2(dropId, raw, deps) {
2629
2912
  if (options.visibility) fields.visibility = options.visibility;
2630
2913
  if (options.password !== void 0) fields.password = options.password;
2631
2914
  if (options.noindex !== void 0) fields.noindex = options.noindex;
2632
- if (options.expiresAt) fields.expires_at = options.expiresAt;
2915
+ if (options.expiresAt !== void 0) fields.expires_at = options.expiresAt;
2633
2916
  if (options.metadata) fields.metadata = options.metadata;
2917
+ if (options.domain) fields.domain = options.domain;
2918
+ if (options.slug) fields.slug = options.slug;
2634
2919
  deps.stdout(
2635
2920
  `${JSON.stringify({ ok: true, dryRun: true, kind: "settings_update", target: dropId, fields }, null, 2)}
2636
2921
  `
@@ -2778,6 +3063,8 @@ async function runWhoami(_input, deps) {
2778
3063
  pairs.push(["Source", credential.source]);
2779
3064
  if (credential.accountId ?? account.id)
2780
3065
  pairs.push(["Account", String(credential.accountId ?? account.id)]);
3066
+ if (credential.scopes?.length)
3067
+ pairs.push(["Scopes", credential.scopes.join(", ")]);
2781
3068
  const jsonEnvelope = {
2782
3069
  ok: true,
2783
3070
  authenticated: true,
@@ -2786,13 +3073,15 @@ async function runWhoami(_input, deps) {
2786
3073
  ...credential.keyId ? { key_id: credential.keyId } : {},
2787
3074
  ...last4 ? { key_last4: last4 } : {},
2788
3075
  ...credential.accountId ?? account.id ? { account_id: String(credential.accountId ?? account.id) } : {},
2789
- ...credential.email ?? account.email ? { email: String(credential.email ?? account.email) } : {}
3076
+ ...credential.email ?? account.email ? { email: String(credential.email ?? account.email) } : {},
3077
+ ...credential.scopes?.length ? { scopes: credential.scopes } : {}
2790
3078
  };
2791
3079
  writeKv(deps, pairs, jsonEnvelope);
2792
3080
  return { exitCode: 0 };
2793
3081
  }
2794
3082
 
2795
3083
  // src/commands/workspace.ts
3084
+ var prompts7 = __toESM(require("@clack/prompts"), 1);
2796
3085
  async function runWorkspaceList(_input, deps) {
2797
3086
  try {
2798
3087
  await requireCredential({
@@ -2845,6 +3134,104 @@ async function runWorkspaceUse(slugOrId, _input, deps) {
2845
3134
  );
2846
3135
  return { exitCode: 0 };
2847
3136
  }
3137
+ async function runWorkspaceCreate(input, deps) {
3138
+ try {
3139
+ await requireCredential({
3140
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
3141
+ env: deps.env,
3142
+ store: deps.store
3143
+ });
3144
+ } catch {
3145
+ return writeAuthError(deps);
3146
+ }
3147
+ const createInput = { name: input.name };
3148
+ if (input.slug !== void 0) createInput.slug = input.slug;
3149
+ const result = await deps.client.workspaces.create(createInput);
3150
+ if (result.error) return writeApiError(deps, result.error);
3151
+ const ws = result.data;
3152
+ writeHumanOrJson(
3153
+ deps,
3154
+ success(`Created workspace: ${ws.name} (${ws.slug})`),
3155
+ {
3156
+ ok: true,
3157
+ workspace: ws
3158
+ }
3159
+ );
3160
+ if (ws.creatorCanReach === false && deps.outputMode === "human") {
3161
+ deps.stderr(
3162
+ `${hint("This key can't reach the new workspace \u2014 re-authenticate (dropthis login) to pick it up.")}
3163
+ `
3164
+ );
3165
+ }
3166
+ return { exitCode: 0 };
3167
+ }
3168
+ async function runWorkspaceRename(workspaceId, input, deps) {
3169
+ if (input.name === void 0 && input.slug === void 0) {
3170
+ return writeError(
3171
+ deps,
3172
+ "invalid_usage",
3173
+ "Provide --name and/or --slug to rename."
3174
+ );
3175
+ }
3176
+ try {
3177
+ await requireCredential({
3178
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
3179
+ env: deps.env,
3180
+ store: deps.store
3181
+ });
3182
+ } catch {
3183
+ return writeAuthError(deps);
3184
+ }
3185
+ const renameInput = {};
3186
+ if (input.name !== void 0) renameInput.name = input.name;
3187
+ if (input.slug !== void 0) renameInput.slug = input.slug;
3188
+ const result = await deps.client.workspaces.rename(workspaceId, renameInput);
3189
+ if (result.error) return writeApiError(deps, result.error);
3190
+ const ws = result.data;
3191
+ writeHumanOrJson(
3192
+ deps,
3193
+ success(`Renamed workspace: ${ws.name} (${ws.slug})`),
3194
+ {
3195
+ ok: true,
3196
+ workspace: ws
3197
+ }
3198
+ );
3199
+ return { exitCode: 0 };
3200
+ }
3201
+ async function runWorkspaceDelete(workspaceId, input, deps) {
3202
+ if (!input.yes && input.interactive === false) {
3203
+ return writeError(
3204
+ deps,
3205
+ "invalid_usage",
3206
+ "Pass --yes to delete in non-interactive mode."
3207
+ );
3208
+ }
3209
+ if (!input.yes && input.interactive !== false) {
3210
+ const confirmed = await prompts7.confirm({
3211
+ message: `Delete workspace ${workspaceId}?`
3212
+ });
3213
+ if (prompts7.isCancel(confirmed) || !confirmed) {
3214
+ return { exitCode: 0 };
3215
+ }
3216
+ }
3217
+ try {
3218
+ await requireCredential({
3219
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
3220
+ env: deps.env,
3221
+ store: deps.store
3222
+ });
3223
+ } catch {
3224
+ return writeAuthError(deps);
3225
+ }
3226
+ const result = await deps.client.workspaces.delete(workspaceId);
3227
+ if (result?.error) return writeApiError(deps, result.error);
3228
+ writeHumanOrJson(deps, success(`Deleted workspace ${workspaceId}`), {
3229
+ ok: true,
3230
+ deleted: true,
3231
+ id: workspaceId
3232
+ });
3233
+ return { exitCode: 0 };
3234
+ }
2848
3235
 
2849
3236
  // src/context.ts
2850
3237
  var import_node2 = require("@dropthis/node");
@@ -3170,7 +3557,8 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3170
3557
  return;
3171
3558
  }
3172
3559
  }
3173
- if (deps.outputMode === "human" && deps.interactive && inputs.length === 1 && typeof publishInput === "string" && isBareWordToken(publishInput) && !await pathExists(publishInput)) {
3560
+ const publishWasExplicit = (program.rawArgs ?? []).includes("publish");
3561
+ if (!publishWasExplicit && inputs.length === 1 && typeof publishInput === "string" && isBareWordToken(publishInput) && !await pathExists(publishInput)) {
3174
3562
  const suggestion = suggestCommand(
3175
3563
  publishInput,
3176
3564
  program.commands.map((c) => c.name())
@@ -3184,13 +3572,15 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3184
3572
  ).exitCode;
3185
3573
  return;
3186
3574
  }
3187
- const prompts6 = await import("@clack/prompts");
3188
- const confirmed = await prompts6.confirm({
3189
- message: `Publish '${publishInput}' as inline text?`,
3190
- initialValue: false
3191
- });
3192
- if (prompts6.isCancel(confirmed) || !confirmed) {
3193
- return;
3575
+ if (deps.outputMode === "human" && deps.interactive) {
3576
+ const prompts8 = await import("@clack/prompts");
3577
+ const confirmed = await prompts8.confirm({
3578
+ message: `Publish '${publishInput}' as inline text?`,
3579
+ initialValue: false
3580
+ });
3581
+ if (prompts8.isCancel(confirmed) || !confirmed) {
3582
+ return;
3583
+ }
3194
3584
  }
3195
3585
  }
3196
3586
  let dropOpts;
@@ -3281,10 +3671,19 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3281
3671
  );
3282
3672
  program.command("update-settings <dropId>").description(
3283
3673
  "Change a drop's title, visibility, password, expiry, or metadata (pass the full drop_\u2026 id, not the slug).\nContent stays unchanged \u2014 use update-content to replace the files at the URL."
3284
- ).option("--title <title>", "Change title").option("--visibility <v>", "Set to public or unlisted").option("--password <password>", "Require password to view").option("--no-password", "Remove password protection").option("--noindex", "Prevent search-engine indexing").option("--index", "Allow search-engine indexing").option("--expires-at <datetime>", "Auto-delete after this ISO 8601 date").option(
3674
+ ).option("--title <title>", "Change title").option("--visibility <v>", "Set to public or unlisted").option("--password <password>", "Require password to view").option("--no-password", "Remove password protection").option("--noindex", "Prevent search-engine indexing").option("--index", "Allow search-engine indexing").option("--expires-at <datetime>", "Auto-delete after this ISO 8601 date").option("--no-expires", "Clear the auto-delete expiry").option(
3285
3675
  "--metadata <json>",
3286
3676
  `Attach JSON key-value pairs, e.g. '{"source":"ci"}'`
3287
3677
  ).option("--metadata-file <path>", "Read metadata JSON from a file").option(
3678
+ "--domain <hostname>",
3679
+ "Move this drop to a connected custom domain (must be live \u2014 see: dropthis domains list)."
3680
+ ).option(
3681
+ "--shared",
3682
+ "Unmount this drop back to the shared dropthis pool. Cannot combine with --domain."
3683
+ ).option(
3684
+ "--slug <vanity-slug>",
3685
+ "Rename the vanity slug on a path-mode custom domain (1\u201363 lowercase letters/digits/hyphens). 409 on conflict \u2014 does not auto-suffix."
3686
+ ).option(
3288
3687
  "--if-revision <n>",
3289
3688
  "Fail if current revision doesn't match (optimistic lock)",
3290
3689
  parseInteger
@@ -3316,7 +3715,7 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3316
3715
  );
3317
3716
  program.command("pull <target>").description(
3318
3717
  "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."
3319
- ).option("-o, --output <dir>", "Output directory (default: ./<slug-or-id>)").option("--json", "Force JSON output").addHelpText(
3718
+ ).option("-o, --output <dir>", "Output directory (default: ./<slug-or-id>)").option("--deployment <deploymentId>", "Download a specific deployment").option("--json", "Force JSON output").addHelpText(
3320
3719
  "after",
3321
3720
  examplesBlock([
3322
3721
  "dropthis pull drop_abc123 -o ./site # download the drop's files",
@@ -3452,7 +3851,7 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3452
3851
  process.exitCode = result.exitCode;
3453
3852
  });
3454
3853
  domains.command("verify <hostname>").description(
3455
- "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."
3854
+ "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 (one-shot): 0=live, 1=failed, 6=still pending (retry). With --wait: 0=live, 1=failed, 7=timed out."
3456
3855
  ).option("--wait", "Poll until live (or failed/timeout)").option(
3457
3856
  "--timeout <secs>",
3458
3857
  "Max seconds to wait (default: 300)",
@@ -3508,11 +3907,35 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3508
3907
  }
3509
3908
  );
3510
3909
  const apiKeys = program.command("api-keys").description("Manage API keys");
3511
- apiKeys.command("create").description("Create an API key").option("--label <label>", "API key label", "CLI").option("--json", "Force JSON output").action(async (commandOptions) => {
3512
- const deps = await commandDeps(program, options, store, commandOptions);
3513
- const result = await runApiKeysCreate(commandOptions, deps);
3514
- process.exitCode = result.exitCode;
3515
- });
3910
+ apiKeys.command("create").description(
3911
+ "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."
3912
+ ).option("--label <label>", "API key label").option(
3913
+ "--service",
3914
+ "Mint a pinned service key for CI/automation (requires --workspace to pin it)"
3915
+ ).option(
3916
+ "--workspace <slugOrId>",
3917
+ "Pin this service key to the given workspace slug or id (only valid with --service)"
3918
+ ).option(
3919
+ "--allowed-workspace <slugOrId>",
3920
+ "Allow a delegated key to access the given workspace slug or id",
3921
+ collect,
3922
+ []
3923
+ ).option("--json", "Force JSON output").action(
3924
+ async (commandOptions) => {
3925
+ const deps = await commandDeps(program, options, store, commandOptions);
3926
+ const result = await runApiKeysCreate(
3927
+ {
3928
+ ...commandOptions.label !== void 0 ? { label: commandOptions.label } : {},
3929
+ type: commandOptions.service ? "service" : "delegated",
3930
+ ...commandOptions.workspace !== void 0 ? { workspace: commandOptions.workspace } : {},
3931
+ ...commandOptions.allowedWorkspace?.length ? { allowedWorkspaces: commandOptions.allowedWorkspace } : {},
3932
+ ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
3933
+ },
3934
+ deps
3935
+ );
3936
+ process.exitCode = result.exitCode;
3937
+ }
3938
+ );
3516
3939
  apiKeys.command("list").description("List API keys").option("--json", "Force JSON output").action(async (commandOptions) => {
3517
3940
  const deps = await commandDeps(program, options, store, commandOptions);
3518
3941
  const result = await runApiKeysList(commandOptions, deps);
@@ -3549,25 +3972,28 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3549
3972
  const result = await runWorkspaceUse(slugOrId, commandOptions, deps);
3550
3973
  process.exitCode = result.exitCode;
3551
3974
  });
3552
- const keys = program.command("keys").description(
3553
- "Mint API keys (delegated login keys or pinned CI service keys)"
3975
+ workspace.command("create <name>").description("Create a new team workspace").option("--slug <slug>", "Vanity slug for the workspace").option("--json", "Force JSON output").action(
3976
+ async (name, commandOptions) => {
3977
+ const deps = await commandDeps(program, options, store, commandOptions);
3978
+ const result = await runWorkspaceCreate(
3979
+ {
3980
+ name,
3981
+ ...commandOptions.slug !== void 0 ? { slug: commandOptions.slug } : {},
3982
+ ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
3983
+ },
3984
+ deps
3985
+ );
3986
+ process.exitCode = result.exitCode;
3987
+ }
3554
3988
  );
3555
- keys.command("create").description(
3556
- "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."
3557
- ).option("--label <label>", "Key label").option(
3558
- "--service",
3559
- "Mint a pinned service key for CI/automation (requires --workspace to pin it)"
3560
- ).option(
3561
- "--workspace <slugOrId>",
3562
- "Pin this service key to the given workspace slug or id (only valid with --service)"
3563
- ).option("--json", "Force JSON output").action(
3564
- async (commandOptions) => {
3989
+ workspace.command("rename <workspaceId>").description("Rename a workspace or change its vanity slug").option("--name <name>", "New workspace name").option("--slug <slug>", "New vanity slug").option("--json", "Force JSON output").action(
3990
+ async (workspaceId, commandOptions) => {
3565
3991
  const deps = await commandDeps(program, options, store, commandOptions);
3566
- const result = await runKeysCreate(
3992
+ const result = await runWorkspaceRename(
3993
+ workspaceId,
3567
3994
  {
3568
- ...commandOptions.label !== void 0 ? { label: commandOptions.label } : {},
3569
- type: commandOptions.service ? "service" : "delegated",
3570
- ...commandOptions.workspace !== void 0 ? { workspace: commandOptions.workspace } : {},
3995
+ ...commandOptions.name !== void 0 ? { name: commandOptions.name } : {},
3996
+ ...commandOptions.slug !== void 0 ? { slug: commandOptions.slug } : {},
3571
3997
  ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
3572
3998
  },
3573
3999
  deps
@@ -3575,7 +4001,103 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3575
4001
  process.exitCode = result.exitCode;
3576
4002
  }
3577
4003
  );
3578
- 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(
4004
+ workspace.command("delete <workspaceId>").description("Delete a workspace").option("--yes", "Confirm deletion").option("--json", "Force JSON output").action(
4005
+ async (workspaceId, commandOptions) => {
4006
+ const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
4007
+ const global = globalOptions(program, commandOptions);
4008
+ const deps = await commandDeps(program, options, store, commandOptions);
4009
+ const result = await runWorkspaceDelete(
4010
+ workspaceId,
4011
+ {
4012
+ ...commandOptions,
4013
+ interactive: isInteractive(stdinIsTTY, deps.env, global)
4014
+ },
4015
+ deps
4016
+ );
4017
+ process.exitCode = result.exitCode;
4018
+ }
4019
+ );
4020
+ const members = program.command("members").description("Manage team members");
4021
+ members.command("list <workspaceId>").description("List members of a workspace").option("--json", "Force JSON output").action(async (workspaceId, commandOptions) => {
4022
+ const deps = await commandDeps(program, options, store, commandOptions);
4023
+ const result = await runMembersList(workspaceId, commandOptions, deps);
4024
+ process.exitCode = result.exitCode;
4025
+ });
4026
+ members.command("invite <workspaceId>").description("Invite someone to a workspace by email").requiredOption("--email <email>", "Email address to invite").option("--role <role>", "Role to grant (admin or member)", "member").option("--json", "Force JSON output").action(
4027
+ async (workspaceId, commandOptions) => {
4028
+ const deps = await commandDeps(program, options, store, commandOptions);
4029
+ const result = await runMembersInvite(
4030
+ workspaceId,
4031
+ {
4032
+ email: commandOptions.email,
4033
+ ...commandOptions.role !== void 0 ? { role: commandOptions.role } : {},
4034
+ ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
4035
+ },
4036
+ deps
4037
+ );
4038
+ process.exitCode = result.exitCode;
4039
+ }
4040
+ );
4041
+ members.command("role <workspaceId> <accountId>").description("Change a member's role").requiredOption("--role <role>", "New role (owner, admin, or member)").option("--json", "Force JSON output").action(
4042
+ async (workspaceId, accountId, commandOptions) => {
4043
+ const deps = await commandDeps(program, options, store, commandOptions);
4044
+ const result = await runMembersRole(
4045
+ workspaceId,
4046
+ accountId,
4047
+ {
4048
+ role: commandOptions.role,
4049
+ ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
4050
+ },
4051
+ deps
4052
+ );
4053
+ process.exitCode = result.exitCode;
4054
+ }
4055
+ );
4056
+ members.command("remove <workspaceId> <accountId>").description("Remove a member from a workspace").option("--yes", "Confirm removal").option("--json", "Force JSON output").action(
4057
+ async (workspaceId, accountId, commandOptions) => {
4058
+ const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
4059
+ const global = globalOptions(program, commandOptions);
4060
+ const deps = await commandDeps(program, options, store, commandOptions);
4061
+ const result = await runMembersRemove(
4062
+ workspaceId,
4063
+ accountId,
4064
+ {
4065
+ ...commandOptions,
4066
+ interactive: isInteractive(stdinIsTTY, deps.env, global)
4067
+ },
4068
+ deps
4069
+ );
4070
+ process.exitCode = result.exitCode;
4071
+ }
4072
+ );
4073
+ const invitations = program.command("invitations").description("Manage your workspace invitations");
4074
+ invitations.command("list", { isDefault: true }).description("List your pending workspace invitations").option("--json", "Force JSON output").action(async (commandOptions) => {
4075
+ const deps = await commandDeps(program, options, store, commandOptions);
4076
+ const result = await runInvitationsList(commandOptions, deps);
4077
+ process.exitCode = result.exitCode;
4078
+ });
4079
+ invitations.command("accept").description("Accept a workspace invitation by token").requiredOption("--token <token>", "Invitation token").option("--json", "Force JSON output").action(async (commandOptions) => {
4080
+ const deps = await commandDeps(program, options, store, commandOptions);
4081
+ const result = await runInvitationsAccept(commandOptions, deps);
4082
+ process.exitCode = result.exitCode;
4083
+ });
4084
+ invitations.command("accept-by-id <invitationId>").description("Accept a workspace invitation by its id").option("--json", "Force JSON output").action(
4085
+ async (invitationId, commandOptions) => {
4086
+ const deps = await commandDeps(program, options, store, commandOptions);
4087
+ const result = await runInvitationsAcceptById(
4088
+ {
4089
+ invitationId,
4090
+ ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
4091
+ },
4092
+ deps
4093
+ );
4094
+ process.exitCode = result.exitCode;
4095
+ }
4096
+ );
4097
+ program.command("login").description("Authenticate with email OTP").option("--email <email>", "Email address").option("--otp <otp>", "One-time passcode").option(
4098
+ "--scope <scope>",
4099
+ "Capability scope to request for the minted key (e.g. team)"
4100
+ ).option("--json", "Force JSON output").action(
3579
4101
  async (commandOptions) => {
3580
4102
  if (!commandOptions.email || !commandOptions.otp) {
3581
4103
  const deps2 = await commandDeps(program, options, store, commandOptions);
@@ -3593,7 +4115,8 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3593
4115
  const context = createContext({ global: global2 });
3594
4116
  const result2 = await runLoginInteractive({
3595
4117
  ...deps2,
3596
- createClient: (apiKey) => context.createClient(apiKey)
4118
+ createClient: (apiKey) => context.createClient(apiKey),
4119
+ ...commandOptions.scope ? { scope: commandOptions.scope } : {}
3597
4120
  });
3598
4121
  process.exitCode = result2.exitCode;
3599
4122
  return;
@@ -3605,6 +4128,7 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3605
4128
  {
3606
4129
  email: commandOptions.email,
3607
4130
  otp: commandOptions.otp,
4131
+ ...commandOptions.scope ? { scope: commandOptions.scope } : {},
3608
4132
  ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
3609
4133
  },
3610
4134
  {
@@ -3621,7 +4145,10 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3621
4145
  const result = await runLoginRequest(commandOptions, deps);
3622
4146
  process.exitCode = result.exitCode;
3623
4147
  });
3624
- login?.command("verify").description("Verify email OTP and store an API key").requiredOption("--email <email>", "Email address").requiredOption("--otp <otp>", "One-time passcode").option("--json", "Force JSON output").action(
4148
+ login?.command("verify").description("Verify email OTP and store an API key").requiredOption("--email <email>", "Email address").requiredOption("--otp <otp>", "One-time passcode").option(
4149
+ "--scope <scope>",
4150
+ "Capability scope to request for the minted key (e.g. team)"
4151
+ ).option("--json", "Force JSON output").action(
3625
4152
  async (commandOptions) => {
3626
4153
  const deps = await commandDeps(program, options, store, commandOptions);
3627
4154
  const global = globalOptions(program, commandOptions);
@@ -3659,17 +4186,30 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3659
4186
  const result = await runAccountGet(commandOptions, deps);
3660
4187
  process.exitCode = result.exitCode;
3661
4188
  });
3662
- account.command("update").description("Update account display name").requiredOption("--display-name <name>", "Set the display name").option("--json", "Force JSON output").action(async (commandOptions) => {
3663
- const deps = await commandDeps(program, options, store, commandOptions);
3664
- const result = await runAccountUpdate(
3665
- {
3666
- displayName: commandOptions.displayName,
3667
- ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
3668
- },
3669
- deps
3670
- );
3671
- process.exitCode = result.exitCode;
3672
- });
4189
+ account.command("update").description("Update account display name").option("--display-name <name>", "Set the display name").option("--clear-display-name", "Clear the display name").option("--json", "Force JSON output").action(
4190
+ async (commandOptions) => {
4191
+ if (commandOptions.displayName === void 0 && !commandOptions.clearDisplayName || commandOptions.displayName !== void 0 && commandOptions.clearDisplayName) {
4192
+ const writer = outputWriter(program, options, commandOptions);
4193
+ process.exitCode = writeError(
4194
+ writer,
4195
+ "invalid_usage",
4196
+ "Use either --display-name <name> or --clear-display-name."
4197
+ ).exitCode;
4198
+ return;
4199
+ }
4200
+ const displayName = commandOptions.clearDisplayName ? null : commandOptions.displayName;
4201
+ if (displayName === void 0) return;
4202
+ const deps = await commandDeps(program, options, store, commandOptions);
4203
+ const result = await runAccountUpdate(
4204
+ {
4205
+ displayName,
4206
+ ...commandOptions.json !== void 0 ? { json: commandOptions.json } : {}
4207
+ },
4208
+ deps
4209
+ );
4210
+ process.exitCode = result.exitCode;
4211
+ }
4212
+ );
3673
4213
  account.command("delete").description("Delete your account").option("--yes", "Confirm deletion").option("--json", "Force JSON output").action(async (commandOptions) => {
3674
4214
  const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
3675
4215
  const global = globalOptions(program, commandOptions);
@@ -3683,7 +4223,10 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
3683
4223
  );
3684
4224
  process.exitCode = result.exitCode;
3685
4225
  });
3686
- program.command("doctor").description("Report CLI diagnostics").option("--json", "Force JSON output").action(async (commandOptions) => {
4226
+ program.command("doctor").description("Report CLI diagnostics").option("--json", "Force JSON output").option(
4227
+ "--online",
4228
+ "Run a network preflight (auth + active workspace + custom-domain status) \u2014 exits 3 on auth failure, 5 on a network failure"
4229
+ ).action(async (commandOptions) => {
3687
4230
  const deps = await commandDeps(
3688
4231
  program,
3689
4232
  options,
@@ -3818,6 +4361,7 @@ function toDropOptions(options) {
3818
4361
  ...options.noindex ? { noindex: true } : {},
3819
4362
  ...options.index ? { index: true } : {},
3820
4363
  ...options.expiresAt ? { expiresAt: options.expiresAt } : {},
4364
+ ...options.expires === false || options.noExpires ? { noExpires: true } : {},
3821
4365
  ...options.metadata ? { metadata: options.metadata } : {},
3822
4366
  ...options.metadataFile ? { metadataFile: options.metadataFile } : {},
3823
4367
  ...options.entry ? { entry: options.entry } : {},