@dropthis/cli 0.34.0 → 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/dist/cli.cjs +4 -2
- package/dist/cli.cjs.map +1 -1
- package/node_modules/@dropthis/node/README.md +85 -0
- package/node_modules/@dropthis/node/dist/edge.cjs +82 -3
- package/node_modules/@dropthis/node/dist/edge.cjs.map +1 -1
- package/node_modules/@dropthis/node/dist/edge.d.cts +7 -1
- package/node_modules/@dropthis/node/dist/edge.d.ts +7 -1
- package/node_modules/@dropthis/node/dist/edge.mjs +82 -3
- package/node_modules/@dropthis/node/dist/edge.mjs.map +1 -1
- package/node_modules/@dropthis/node/dist/index.cjs +5 -3
- package/node_modules/@dropthis/node/dist/index.cjs.map +1 -1
- package/node_modules/@dropthis/node/dist/index.d.cts +3 -39
- package/node_modules/@dropthis/node/dist/index.d.ts +3 -39
- package/node_modules/@dropthis/node/dist/index.mjs +5 -3
- package/node_modules/@dropthis/node/dist/index.mjs.map +1 -1
- package/node_modules/@dropthis/node/dist/{workspaces-CebiV4DS.d.cts → workspaces-Cq705UM6.d.cts} +64 -1
- package/node_modules/@dropthis/node/dist/{workspaces-CebiV4DS.d.ts → workspaces-Cq705UM6.d.ts} +64 -1
- package/node_modules/@dropthis/node/package.json +15 -2
- package/package.json +2 -2
|
@@ -286,6 +286,28 @@ whatever `retryable` the API returns (the SDK never overrides it). `publish()` a
|
|
|
286
286
|
`updateContent()` are always safe to retry — each derives a stable idempotency key, so a
|
|
287
287
|
retried publish never creates a duplicate drop.
|
|
288
288
|
|
|
289
|
+
### Plan gates & quotas
|
|
290
|
+
|
|
291
|
+
A feature your plan doesn't include (`feature_not_in_plan`) or a limit you've hit (`quota_exceeded`)
|
|
292
|
+
comes back as a typed, **non-retryable** error — don't retry it, surface the upgrade path instead.
|
|
293
|
+
`isPlanGate(error)` covers both; the narrower `isFeatureNotInPlan` / `isQuotaExceeded` are exported too.
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { isPlanGate } from "@dropthis/node";
|
|
297
|
+
|
|
298
|
+
const { error } = await dropthis.drops.publish("./dist", { password: "hunter2" });
|
|
299
|
+
if (isPlanGate(error)) {
|
|
300
|
+
// error.feature ("passwordProtect"), error.requiredPlan ("pro"), error.upgradeUrl
|
|
301
|
+
// — on quota_exceeded also: error.limit, error.usage, error.requested
|
|
302
|
+
console.log(`Needs ${error.requiredPlan}: ${error.upgradeUrl}`);
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Pre-flight instead of failing: `account.get().data.entitlements` carries the capability matrix +
|
|
307
|
+
numeric limits, so you can check a gate or size a publish before attempting it. A `403`
|
|
308
|
+
`insufficient_scope` is different — that's a [scope](#capability-scopes) gap on the credential, not a
|
|
309
|
+
plan gate; fix it by minting/re-logging with the needed scope, not by upgrading.
|
|
310
|
+
|
|
289
311
|
## Configuration
|
|
290
312
|
|
|
291
313
|
```typescript
|
|
@@ -304,6 +326,11 @@ You can also pass just the API key as a string:
|
|
|
304
326
|
const dropthis = new Dropthis("sk_...");
|
|
305
327
|
```
|
|
306
328
|
|
|
329
|
+
**Getting a key.** Grab an `sk_…` key from the [console](https://app.dropthis.app) Credentials page,
|
|
330
|
+
or mint one programmatically after an OTP login (`auth.verifyEmailOtp()` → `apiKeys.create()`). The
|
|
331
|
+
credential is sent as `Authorization: Bearer <key>`; an `at_…` session token works in the same slot.
|
|
332
|
+
With no explicit `apiKey`, the client reads `DROPTHIS_API_KEY` from the environment.
|
|
333
|
+
|
|
307
334
|
## Resources
|
|
308
335
|
|
|
309
336
|
### drops
|
|
@@ -392,12 +419,39 @@ await dropthis.apiKeys.create({
|
|
|
392
419
|
workspace: "prod-team",
|
|
393
420
|
});
|
|
394
421
|
|
|
422
|
+
// Key that can create + manage teams (not just publish) — pass a scope bundle
|
|
423
|
+
await dropthis.apiKeys.create({ label: "Team bot", scopes: ["team"] });
|
|
424
|
+
|
|
395
425
|
await dropthis.apiKeys.list();
|
|
396
426
|
await dropthis.apiKeys.delete("key_abc123"); // 204 No Content — data is null
|
|
397
427
|
```
|
|
398
428
|
|
|
399
429
|
`KeyType` is `"delegated" | "service"`. A `delegated` key acts on behalf of the owning account and can switch workspaces server-side via `workspaces.use()`; a `service` key is pinned to one workspace and is intended for CI/automation where a stable target is needed.
|
|
400
430
|
|
|
431
|
+
#### Capability scopes
|
|
432
|
+
|
|
433
|
+
Every credential carries a set of **capability scopes** that decide what it may do (ADR 0068). You
|
|
434
|
+
request them as **bundles**; `apiKeys.create({ scopes })` mints the requested set **intersected with
|
|
435
|
+
your own** (downscope-only — you can never grant a key more than you hold).
|
|
436
|
+
|
|
437
|
+
| Bundle | Grants | Use it for |
|
|
438
|
+
| --- | --- | --- |
|
|
439
|
+
| `publish` *(default)* | publish drops, read your own context, mint downscope-only keys | a normal API key / `dropthis login` |
|
|
440
|
+
| `team` | `publish` + create/rename workspaces + invite/manage members | building or running a team |
|
|
441
|
+
| `team-admin` | `team` + delete workspaces, remove members, change roles | the irreversible team-admin tier |
|
|
442
|
+
| `service` | `publish` minus key-minting, workspace-pinned | CI/automation |
|
|
443
|
+
|
|
444
|
+
Omit `scopes` for the default `publish` bundle. A plain key **cannot** do team management — call a
|
|
445
|
+
team op (`workspaces.create()`, members, invitations) with a publish-only key and the API returns
|
|
446
|
+
`403 insufficient_scope`; mint or re-login with the `team` bundle to fix it. Fine-grained scopes
|
|
447
|
+
(e.g. `members:admin`) may be passed individually; the created key's `data.scopes` echoes the
|
|
448
|
+
granted set.
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
const { data } = await dropthis.apiKeys.create({ label: "Team bot", scopes: ["team"] });
|
|
452
|
+
console.log(data.scopes); // the granted set (requested ∩ yours)
|
|
453
|
+
```
|
|
454
|
+
|
|
401
455
|
### account
|
|
402
456
|
|
|
403
457
|
```typescript
|
|
@@ -512,6 +566,37 @@ const { data } = await dropthis.drops.publish("./dist");
|
|
|
512
566
|
|
|
513
567
|
`DropthisEdge` honours the same `workspace` constructor option and publish option.
|
|
514
568
|
|
|
569
|
+
#### Team management (workspaces, members, invitations)
|
|
570
|
+
|
|
571
|
+
A **team** workspace is shared by multiple accounts. Creating and managing one needs a `team`-scoped
|
|
572
|
+
credential (see [Capability scopes](#capability-scopes)) — a default publish key gets
|
|
573
|
+
`403 insufficient_scope`. Drops published while a team workspace is active land there and route on its
|
|
574
|
+
shared custom domain automatically.
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
// Create a team (caller becomes its owner) and switch to it
|
|
578
|
+
const { data: team } = await dropthis.workspaces.create({ name: "Acme" });
|
|
579
|
+
await dropthis.workspaces.use(team.slug);
|
|
580
|
+
|
|
581
|
+
// Invite + manage members
|
|
582
|
+
await dropthis.members.invite(team.id, { email: "teammate@acme.com", role: "member" });
|
|
583
|
+
const { data: roster } = await dropthis.members.list(team.id);
|
|
584
|
+
await dropthis.members.updateRole(team.id, "acc_123", { role: "admin" }); // needs members:admin
|
|
585
|
+
await dropthis.members.remove(team.id, "acc_123"); // remove a member, or your own id to leave
|
|
586
|
+
|
|
587
|
+
// Rename / delete the workspace
|
|
588
|
+
await dropthis.workspaces.rename(team.id, { name: "Acme Inc" });
|
|
589
|
+
await dropthis.workspaces.delete(team.id); // owner only, needs workspaces:admin
|
|
590
|
+
|
|
591
|
+
// Invitee side — accept an invite (joins + switches active workspace)
|
|
592
|
+
const { data: pending } = await dropthis.invitations.list();
|
|
593
|
+
await dropthis.invitations.accept({ token: "inv_raw_token_from_email" });
|
|
594
|
+
await dropthis.invitations.acceptById({ invitationId: "inv_abc123" }); // agent path: no token, just be the invited email
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
`role` is `"admin" | "member"` for invites; `updateRole` also accepts `"owner"` to transfer ownership
|
|
598
|
+
(owner-only, enforced server-side).
|
|
599
|
+
|
|
515
600
|
## Pricing tiers
|
|
516
601
|
|
|
517
602
|
- **Free** — drops expire after 30 days, 5 MB per drop, 500 MB active storage, dropthis badge.
|
|
@@ -246,7 +246,8 @@ function buildStagedRequest(files, options, entry) {
|
|
|
246
246
|
sourceUrl: file.sourceUrl,
|
|
247
247
|
...file.contentType ? { contentType: file.contentType } : {},
|
|
248
248
|
...file.sizeBytes !== void 0 ? { sizeBytes: file.sizeBytes } : {},
|
|
249
|
-
...file.checksumSha256 ? { checksumSha256: file.checksumSha256 } : {}
|
|
249
|
+
...file.checksumSha256 ? { checksumSha256: file.checksumSha256 } : {},
|
|
250
|
+
...file.transform ? { transform: file.transform } : {}
|
|
250
251
|
}
|
|
251
252
|
) : {
|
|
252
253
|
path: normalizeManifestPath(file.path),
|
|
@@ -333,7 +334,8 @@ function prepareBundleFile(file) {
|
|
|
333
334
|
sourceUrl: file.sourceUrl,
|
|
334
335
|
...file.contentType ? { contentType: file.contentType } : {},
|
|
335
336
|
...file.sizeBytes !== void 0 ? { sizeBytes: file.sizeBytes } : {},
|
|
336
|
-
...file.checksumSha256 ? { checksumSha256: file.checksumSha256 } : {}
|
|
337
|
+
...file.checksumSha256 ? { checksumSha256: file.checksumSha256 } : {},
|
|
338
|
+
...file.transform ? { transform: file.transform } : {}
|
|
337
339
|
};
|
|
338
340
|
}
|
|
339
341
|
const bytes = fileBytes(file);
|
|
@@ -877,6 +879,69 @@ function updateBody(options) {
|
|
|
877
879
|
};
|
|
878
880
|
}
|
|
879
881
|
|
|
882
|
+
// src/resources/invitations.ts
|
|
883
|
+
var InvitationsResource = class {
|
|
884
|
+
constructor(transport) {
|
|
885
|
+
this.transport = transport;
|
|
886
|
+
}
|
|
887
|
+
transport;
|
|
888
|
+
/** List the calling account's own pending invitations. */
|
|
889
|
+
list() {
|
|
890
|
+
return this.transport.request("GET", "/invitations");
|
|
891
|
+
}
|
|
892
|
+
/** Accept by the raw single-use token from the invite email. Joins + switches active workspace. */
|
|
893
|
+
accept(input) {
|
|
894
|
+
return this.transport.request("POST", "/invitations/accept", {
|
|
895
|
+
body: input
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
/** Accept by invitation id, once authenticated as the invited email — the agent path, no token. */
|
|
899
|
+
acceptById(input) {
|
|
900
|
+
return this.transport.request("POST", "/invitations/accept-by-id", {
|
|
901
|
+
body: input
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
// src/resources/members.ts
|
|
907
|
+
var MembersResource = class {
|
|
908
|
+
constructor(transport) {
|
|
909
|
+
this.transport = transport;
|
|
910
|
+
}
|
|
911
|
+
transport;
|
|
912
|
+
/** List a workspace's members (any member, `members:read`). */
|
|
913
|
+
list(workspaceId) {
|
|
914
|
+
return this.transport.request(
|
|
915
|
+
"GET",
|
|
916
|
+
`/workspaces/${encodeURIComponent(workspaceId)}/members`
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
/** Invite an email to the workspace (owner/admin, `members:write`). */
|
|
920
|
+
invite(workspaceId, input) {
|
|
921
|
+
return this.transport.request(
|
|
922
|
+
"POST",
|
|
923
|
+
`/workspaces/${encodeURIComponent(workspaceId)}/invitations`,
|
|
924
|
+
{ body: input }
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
/** Change a member's role (`members:admin`, owner-only-touches-owner enforced server-side). */
|
|
928
|
+
updateRole(workspaceId, accountId, input) {
|
|
929
|
+
return this.transport.request(
|
|
930
|
+
"PATCH",
|
|
931
|
+
`/workspaces/${encodeURIComponent(workspaceId)}/members/${encodeURIComponent(accountId)}`,
|
|
932
|
+
{ body: input }
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
/** Remove a member, or leave the workspace (your own id). Removing others needs `members:admin`;
|
|
936
|
+
* leaving needs `members:write`. 204 — data is null. */
|
|
937
|
+
remove(workspaceId, accountId) {
|
|
938
|
+
return this.transport.request(
|
|
939
|
+
"DELETE",
|
|
940
|
+
`/workspaces/${encodeURIComponent(workspaceId)}/members/${encodeURIComponent(accountId)}`
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
|
|
880
945
|
// src/resources/workspaces.ts
|
|
881
946
|
var WorkspacesResource = class {
|
|
882
947
|
constructor(transport) {
|
|
@@ -958,7 +1023,7 @@ function toSnakeCase(value) {
|
|
|
958
1023
|
|
|
959
1024
|
// src/transport.ts
|
|
960
1025
|
var DEFAULT_BASE_URL = "https://api.dropthis.app";
|
|
961
|
-
var SDK_VERSION = "0.
|
|
1026
|
+
var SDK_VERSION = true ? "0.32.0" : "0.0.0-dev";
|
|
962
1027
|
var Transport = class {
|
|
963
1028
|
apiKey;
|
|
964
1029
|
baseUrl;
|
|
@@ -1215,6 +1280,8 @@ var DropthisEdge = class {
|
|
|
1215
1280
|
deploymentsResource;
|
|
1216
1281
|
domainsResource;
|
|
1217
1282
|
workspacesResource;
|
|
1283
|
+
membersResource;
|
|
1284
|
+
invitationsResource;
|
|
1218
1285
|
constructor(options = {}) {
|
|
1219
1286
|
this.transport = new Transport(options);
|
|
1220
1287
|
this.defaultWorkspace = typeof options === "string" ? void 0 : options.workspace;
|
|
@@ -1258,6 +1325,18 @@ var DropthisEdge = class {
|
|
|
1258
1325
|
this.workspacesResource = new WorkspacesResource(this.transport);
|
|
1259
1326
|
return this.workspacesResource;
|
|
1260
1327
|
}
|
|
1328
|
+
/** Team membership management (ADR 0068; needs a team / team-admin scoped credential). */
|
|
1329
|
+
get members() {
|
|
1330
|
+
if (!this.membersResource)
|
|
1331
|
+
this.membersResource = new MembersResource(this.transport);
|
|
1332
|
+
return this.membersResource;
|
|
1333
|
+
}
|
|
1334
|
+
/** Invitee-side invitations: list the caller's pending invites, accept by token or id. */
|
|
1335
|
+
get invitations() {
|
|
1336
|
+
if (!this.invitationsResource)
|
|
1337
|
+
this.invitationsResource = new InvitationsResource(this.transport);
|
|
1338
|
+
return this.invitationsResource;
|
|
1339
|
+
}
|
|
1261
1340
|
};
|
|
1262
1341
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1263
1342
|
0 && (module.exports = {
|