@dropthis/cli 0.33.2 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,12 +39,30 @@ dropthis login verify --email you@example.com --otp 123456
39
39
 
40
40
  > A failed verify prints `Your code has expired` (`otp_expired`) or `That code is incorrect` (`otp_invalid`) and exits non-zero.
41
41
 
42
- To request a capability-scoped key (e.g. to manage teams), add `--scope` on `login verify`:
42
+ ### Scopes
43
+
44
+ Keys are minted with a **capability bundle**. The default for `dropthis login` is **publish** — enough to publish drops and read your own context, nothing else. To manage a team you need a wider bundle, requested with `--scope` on `login verify`:
45
+
46
+ | Bundle | Grants | Use it for |
47
+ |--------|--------|------------|
48
+ | `publish` *(default)* | publish + read your own account/workspaces + mint downscoped keys | publishing, CI |
49
+ | `team` | publish + create/rename workspaces, invite/manage members | running a team |
50
+ | `team-admin` | team + delete workspaces, remove members, change roles | irreversible team admin |
51
+ | `service` | publish without key-minting (workspace-pinned) | automation / CI that can't mint keys |
43
52
 
44
53
  ```bash
54
+ # Mint a team-scoped key
45
55
  dropthis login verify --email you@example.com --otp 123456 --scope team
46
56
  ```
47
57
 
58
+ Minting is **downscope-only** — a minted key can never have more scopes than the key that minted it. Check what your current key carries:
59
+
60
+ ```bash
61
+ dropthis whoami --json # echoes the key's scopes
62
+ ```
63
+
64
+ If a command returns `insufficient_scope` (exit 1, HTTP 403), your key lacks a scope that route needs. Fix it by re-logging in with the right bundle (`dropthis login verify … --scope team`) or, via the SDK, `apiKeys.create({ scopes: ["team"] })`.
65
+
48
66
  Credentials resolve in this order:
49
67
 
50
68
  1. `--api-key sk_...` flag
@@ -232,6 +250,42 @@ dropthis pull drop_abc123 -o ./site
232
250
 
233
251
  Pull fetches the current deployment's file manifest and writes every file into the output directory. It is owner-side read-back via the API — it works regardless of any viewer password. Custom-domain URLs are not resolvable yet; use the `drop_…` id instead.
234
252
 
253
+ ## Teams & workspaces
254
+
255
+ Publishes land in a **workspace**. Everyone has a personal workspace; teams add shared ones. List the workspaces you belong to and switch the active one (delegated keys):
256
+
257
+ ```bash
258
+ dropthis workspace list
259
+ dropthis workspace use acme # by slug or ws_… id
260
+ ```
261
+
262
+ Creating and managing team workspaces needs a `team`-scoped key (see [Scopes](#scopes)):
263
+
264
+ ```bash
265
+ dropthis workspace create "Acme" --slug acme # create a team workspace
266
+ dropthis workspace rename ws_team123 --name "Acme Inc"
267
+ dropthis workspace delete ws_team123 --yes # team-admin scope
268
+ ```
269
+
270
+ Manage who's in a workspace:
271
+
272
+ ```bash
273
+ dropthis members list ws_team123
274
+ dropthis members invite ws_team123 --email teammate@acme.com --role member # --role admin|member
275
+ dropthis members role ws_team123 acc_123 --role admin # --role owner|admin|member
276
+ dropthis members remove ws_team123 acc_123 --yes
277
+ ```
278
+
279
+ Accept an invitation you received:
280
+
281
+ ```bash
282
+ dropthis invitations list # pending invitations for your email
283
+ dropthis invitations accept --token inv_tok_abc # accept by invite token
284
+ dropthis invitations accept-by-id inv_123 # or by invitation id
285
+ ```
286
+
287
+ To publish into a specific workspace for one call, pass `--workspace <slugOrId>` on `publish` (delegated keys only).
288
+
235
289
  ## Commands
236
290
 
237
291
  ```bash
@@ -259,6 +313,18 @@ dropthis domains remove <host> --yes # Disconnect a domain
259
313
 
260
314
  dropthis workspace list # List workspaces you belong to
261
315
  dropthis workspace use <slug|id> # Switch active workspace (delegated keys)
316
+ dropthis workspace create <name> --slug <s> # Create a team workspace (team scope)
317
+ dropthis workspace rename <ws> --name <name> # Rename a team workspace (team scope)
318
+ dropthis workspace delete <ws> --yes # Delete a team workspace (team-admin scope)
319
+
320
+ dropthis members list <ws> # List a workspace's members
321
+ dropthis members invite <ws> --email <e> --role <admin|member> # Invite someone (team scope)
322
+ dropthis members role <ws> <account-id> --role <owner|admin|member> # Change a member's role
323
+ dropthis members remove <ws> <account-id> --yes # Remove a member (team-admin scope)
324
+
325
+ dropthis invitations list # List pending invitations for your email
326
+ dropthis invitations accept --token <token> # Accept an invitation by token
327
+ dropthis invitations accept-by-id <inv-id> # Accept an invitation by id
262
328
 
263
329
  dropthis login # Authenticate with email OTP
264
330
  dropthis login request --email <email> # Request OTP (non-interactive)
@@ -290,6 +356,7 @@ export DROPTHIS_API_KEY=sk_live_... # API key (same as --api-key)
290
356
  export DROPTHIS_API_URL=https://... # Override API base URL (same as --api-url)
291
357
  export DROPTHIS_NON_INTERACTIVE=1 # Disable interactive prompts (same as --no-interactive)
292
358
  export DROPTHIS_NO_UPDATE_NOTIFIER=1 # Disable the startup update notice (also auto-disabled in CI, non-TTY, --json/--quiet)
359
+ export NO_COLOR=1 # Disable ANSI color in human output (also auto-disabled when not a TTY)
293
360
  ```
294
361
 
295
362
  ```bash
@@ -319,6 +386,13 @@ dropthis publish ./dist --url
319
386
 
320
387
  This table — plus the global flags (`--api-key`, `--api-url`, `--json`, `--quiet`, `--no-interactive`) — is machine-readable from `dropthis commands --json` (the `exit_codes` and `global_options` fields), so an agent can discover the whole surface without scraping docs.
321
388
 
389
+ Some failures share exit 1 but carry a distinct error `code` in the JSON envelope:
390
+
391
+ | Error code | HTTP | Meaning & fix |
392
+ |------------|------|----------------|
393
+ | `insufficient_scope` | 403 | The key lacks a scope this route needs. Re-login with the bundle (`dropthis login verify … --scope team`), or mint a key with it via the SDK. See [Scopes](#scopes). |
394
+ | `quota_exceeded` | 403/413 | A plan limit was hit. Check `dropthis account --json` (`entitlements.limits` vs `usage`) or upgrade. |
395
+
322
396
  **JSON output shape:**
323
397
 
324
398
  ```json
@@ -353,6 +427,8 @@ dropthis doctor --online --json
353
427
 
354
428
  It hits `GET /v1/account` (token valid? which workspace resolves? which plan?) and lists custom-domain verification status as a `checks` array. It exits **3** on an auth failure and **5** on a network failure, so a script can branch on the exit code.
355
429
 
430
+ For a pre-publish **quota** check, `dropthis account --json` returns `entitlements.limits` (e.g. `maxStorageBytes`, `maxCustomHostnames`, `seatLimit`) alongside your current `usage` — read it before a large publish to avoid a `quota_exceeded` failure.
431
+
356
432
  ## Pricing tiers
357
433
 
358
434
  dropthis has four tiers — **Free**, **Keep**, **Pro**, and **Business**. Free drops carry the dropthis badge and expire after 30 days; paid tiers add permanence, larger limits, custom domains, passwords, and analytics. Run `dropthis account` to see your active tier and its exact caps, or visit https://dropthis.app/pricing for the current plans.
package/dist/cli.cjs CHANGED
@@ -1032,7 +1032,7 @@ function buildCatalog(program) {
1032
1032
  return program.commands.map((cmd) => toEntry(cmd, []));
1033
1033
  }
1034
1034
  function buildGlobalOptions(program) {
1035
- return program.options.map((o) => ({
1035
+ return program.options.filter((o) => o.long !== "--version" && o.long !== "--help").map((o) => ({
1036
1036
  flags: o.flags,
1037
1037
  description: o.description
1038
1038
  }));
@@ -1136,7 +1136,7 @@ function spreadData(data) {
1136
1136
  }
1137
1137
 
1138
1138
  // src/version.ts
1139
- var CLI_VERSION = true ? "0.33.2" : "0.0.0-dev";
1139
+ var CLI_VERSION = true ? "0.35.0" : "0.0.0-dev";
1140
1140
 
1141
1141
  // src/commands/doctor.ts
1142
1142
  async function onlineChecks(deps, hasCredential) {
@@ -2204,7 +2204,8 @@ async function loadManifestFile(manifestPath) {
2204
2204
  source_url,
2205
2205
  content_type,
2206
2206
  size_bytes,
2207
- checksum_sha256
2207
+ checksum_sha256,
2208
+ transform
2208
2209
  } = entry;
2209
2210
  if (!path || typeof path !== "string") {
2210
2211
  throw new Error(`Manifest files[${i}] must have a 'path' string.`);
@@ -2236,6 +2237,7 @@ async function loadManifestFile(manifestPath) {
2236
2237
  file.sizeBytes = size_bytes;
2237
2238
  if (checksum_sha256 !== void 0)
2238
2239
  file.checksumSha256 = checksum_sha256;
2240
+ if (transform !== void 0) file.transform = transform;
2239
2241
  return file;
2240
2242
  }
2241
2243
  );
@@ -4072,7 +4074,12 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
4072
4074
  const result = await runMembersList(workspaceId, commandOptions, deps);
4073
4075
  process.exitCode = result.exitCode;
4074
4076
  });
4075
- 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(
4077
+ members.command("invite <workspaceId>").description("Invite someone to a workspace by email").requiredOption("--email <email>", "Email address to invite").option(
4078
+ "--role <role>",
4079
+ "Role to grant (admin or member)",
4080
+ parseInvitableRole,
4081
+ "member"
4082
+ ).option("--json", "Force JSON output").action(
4076
4083
  async (workspaceId, commandOptions) => {
4077
4084
  const deps = await commandDeps(program, options, store, commandOptions);
4078
4085
  const result = await runMembersInvite(
@@ -4087,7 +4094,11 @@ ${import_picocolors6.default.bold("Canonical vs raw URLs:")}`,
4087
4094
  process.exitCode = result.exitCode;
4088
4095
  }
4089
4096
  );
4090
- 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(
4097
+ members.command("role <workspaceId> <accountId>").description("Change a member's role").requiredOption(
4098
+ "--role <role>",
4099
+ "New role (owner, admin, or member)",
4100
+ parseWorkspaceRole
4101
+ ).option("--json", "Force JSON output").action(
4091
4102
  async (workspaceId, accountId, commandOptions) => {
4092
4103
  const deps = await commandDeps(program, options, store, commandOptions);
4093
4104
  const result = await runMembersRole(
@@ -4415,6 +4426,18 @@ function parseMode(value) {
4415
4426
  }
4416
4427
  return value;
4417
4428
  }
4429
+ function parseInvitableRole(value) {
4430
+ if (value !== "admin" && value !== "member") {
4431
+ throw new import_commander.InvalidArgumentError('Use "admin" or "member".');
4432
+ }
4433
+ return value;
4434
+ }
4435
+ function parseWorkspaceRole(value) {
4436
+ if (value !== "owner" && value !== "admin" && value !== "member") {
4437
+ throw new import_commander.InvalidArgumentError('Use "owner", "admin", or "member".');
4438
+ }
4439
+ return value;
4440
+ }
4418
4441
  function collect(value, previous) {
4419
4442
  return [...previous, value];
4420
4443
  }