@dropthis/cli 0.23.0 → 0.24.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.
@@ -1,3 +1,5 @@
1
+ <p align="center"><img src="https://dropthis.app/icon-512.png" width="76" height="76" alt="dropthis" /></p>
2
+
1
3
  # @dropthis/node
2
4
 
3
5
  Official Node.js SDK for [dropthis](https://dropthis.app) -- the publish layer between AI and the internet. One API call in, one URL out.
@@ -275,6 +277,15 @@ if (result.error) {
275
277
 
276
278
  Local input validation errors (e.g. `file_not_found`) are also returned as `{ error: { code: "file_not_found", ... } }` rather than thrown -- except for `prepare()`, which throws `PublishInputError`.
277
279
 
280
+ `error.retryable` tells an agent whether retrying is worthwhile. The SDK marks its own
281
+ transient failures `retryable: true` only when retrying is safe: a `GET` `timeout` /
282
+ `network_error`, any request you sent with an `idempotencyKey`, or a signed-upload `5xx`.
283
+ A bare (non-idempotent) `POST`/`PATCH`/`DELETE` timeout is left unmarked because the
284
+ server may have applied it before the connection dropped. Server-side errors carry
285
+ whatever `retryable` the API returns (the SDK never overrides it). `publish()` and
286
+ `updateContent()` are always safe to retry — each derives a stable idempotency key, so a
287
+ retried publish never creates a duplicate drop.
288
+
278
289
  ## Configuration
279
290
 
280
291
  ```typescript
@@ -321,6 +332,12 @@ for await (const drop of page.data) {
321
332
  }
322
333
  ```
323
334
 
335
+ Auto-pagination fails loud: if fetching a later page errors, the iterator (and
336
+ `autoPagingToArray()`) throws `CursorPaginationError` instead of silently returning a
337
+ truncated list. The carried error is on `.error` (with `code`, `statusCode`, `retryable`,
338
+ `requestId`); items already yielded stay yielded. Wrap the loop in `try/catch` if you want
339
+ to handle a mid-pagination failure.
340
+
324
341
  > To change a drop's content, use `client.drops.updateContent(dropId, newInput)`. `drops.updateSettings()` is for settings only (title, visibility, password, noindex, expiresAt, metadata).
325
342
 
326
343
  ### deployments
@@ -352,6 +369,8 @@ await dropthis.auth.verifyEmailOtp({ email: "you@example.com", code: "123456" })
352
369
  await dropthis.auth.logout(); // 204 No Content — data is null
353
370
  ```
354
371
 
372
+ > On a failed verify the error `code` is `otp_expired` (no active code — request a new one) or `otp_invalid` (wrong digits — re-check, don't auto-resend). 401 on either.
373
+
355
374
  ### apiKeys
356
375
 
357
376
  ```typescript
@@ -464,11 +483,20 @@ import type {
464
483
  PublishInput,
465
484
  PublishFileInput,
466
485
  ListPage,
486
+ KeyType,
487
+ RevokeImpact,
467
488
  CreateUploadSessionRequest,
468
489
  CreateUploadSessionResponse,
469
490
  } from "@dropthis/node";
470
491
  ```
471
492
 
493
+ The `CursorPaginationError` value (thrown by auto-pagination) and the existing
494
+ `PublishInputError` are runtime exports:
495
+
496
+ ```typescript
497
+ import { CursorPaginationError, PublishInputError } from "@dropthis/node";
498
+ ```
499
+
472
500
  ## Agent skills
473
501
 
474
502
  For AI coding agents (Cursor, Claude Code, Windsurf, etc.), install the [dropthis-skills](https://github.com/dropthis-dev/dropthis-skills) package:
@@ -77,6 +77,10 @@ type DropResponse = {
77
77
  noindex: boolean;
78
78
  passwordProtected: boolean;
79
79
  metadata: Record<string, unknown>;
80
+ /** When the drop was last updated (content or settings), ISO 8601. */
81
+ updatedAt: string;
82
+ /** Origin that created the drop (e.g. "api", "cli", "mcp"); null/omitted when unattributed. */
83
+ source?: string | null;
80
84
  object: string;
81
85
  accessible: boolean;
82
86
  persistent: boolean;
@@ -142,7 +146,8 @@ type Action = {
142
146
  message: string;
143
147
  };
144
148
  type EmailOtpResponse = {
145
- ok: true;
149
+ /** Always `true` on success; optional because the server defaults it. */
150
+ ok?: true;
146
151
  expiresIn: number;
147
152
  nextAction?: Action | null;
148
153
  };
@@ -152,12 +157,35 @@ type SessionResponse = {
152
157
  accountId: string;
153
158
  isNewAccount: boolean;
154
159
  expiresIn: number;
160
+ /**
161
+ * Rotating refresh token for a console browser session. Present when a session is
162
+ * started (email/verify, refresh); `null`/omitted for non-session token issuance.
163
+ */
164
+ refreshToken?: string | null;
155
165
  };
166
+ /**
167
+ * Why an API key exists. `standard` keys are created by the user (CLI/SDK/manual);
168
+ * `mcp_oauth` keys are minted per connected client by the MCP OAuth flow and carry a
169
+ * more generous quota.
170
+ */
171
+ type KeyType = "standard" | "mcp_oauth";
172
+ /** What breaks when a key is revoked. */
173
+ type RevokeImpact = "disconnects_app" | "breaks_automation";
156
174
  type ApiKeyResponse = {
157
175
  object: "api_key";
158
176
  id: string;
159
177
  keyLast4: string;
160
178
  label: string;
179
+ /** Human-facing credential name: the user's label for `standard` keys, the connected client name for `mcp_oauth` keys. */
180
+ appName: string;
181
+ /** Why the key exists (drives quota accounting). */
182
+ keyType: KeyType;
183
+ /** Scopes granted to this key. */
184
+ scopes: string[];
185
+ /** When the key was last used to authenticate, ISO 8601; null/omitted if never used. */
186
+ lastUsedAt?: string | null;
187
+ /** What breaks if this key is revoked. */
188
+ revokeImpact: RevokeImpact;
161
189
  createdAt: string;
162
190
  };
163
191
  type ApiKeyCreatedResponse = ApiKeyResponse & {
@@ -252,9 +280,17 @@ type CreateUploadSessionFileResponse = {
252
280
  type UploadSessionFileResponse = {
253
281
  fileId: string;
254
282
  path: string;
255
- contentType: string;
256
- sizeBytes: number;
283
+ /** Declared MIME type; `null`/omitted for remote-fetch files until ingested. */
284
+ contentType?: string | null;
285
+ /** Declared byte size; `null`/omitted for remote-fetch files until ingested. */
286
+ sizeBytes?: number | null;
257
287
  objectKey: string;
288
+ /** How the file enters staging: "client_put" (client PUTs bytes) or "remote_fetch" (server fetches sourceUrl). */
289
+ origin: "client_put" | "remote_fetch";
290
+ /** Ingest lifecycle state for remote-fetch files (e.g. "pending" | "fetching" | "fetched" | "failed"). */
291
+ state: string;
292
+ /** Public http(s) URL the server fetches into staging; `null`/omitted for client-put files. */
293
+ sourceUrl?: string | null;
258
294
  verified: boolean;
259
295
  };
260
296
  type UploadSessionResponse = {
@@ -691,6 +727,22 @@ declare class DomainsResource {
691
727
  delete(idOrHostname: string): Promise<DropthisResult<DomainDeletedResponse>>;
692
728
  }
693
729
 
730
+ /**
731
+ * Thrown by the auto-pagination iterator (`for await` / `autoPagingToArray`) when
732
+ * fetching a later page fails. Carries the underlying {@link DropthisErrorResponse}
733
+ * on `.error` so an agent can inspect `code`/`statusCode`/`retryable`/`requestId`
734
+ * instead of silently receiving a truncated list. The already-yielded items are not
735
+ * lost — the items consumed before the failure remain consumed; only the continuation
736
+ * rejects.
737
+ */
738
+ declare class CursorPaginationError extends Error {
739
+ readonly error: DropthisErrorResponse;
740
+ /** Response headers from the failed page fetch (rate-limit, trace, request id). */
741
+ readonly headers: Record<string, string>;
742
+ constructor(error: DropthisErrorResponse,
743
+ /** Response headers from the failed page fetch (rate-limit, trace, request id). */
744
+ headers?: Record<string, string>);
745
+ }
694
746
  declare class CursorPage<T> implements ListPage<T> {
695
747
  readonly object: "list";
696
748
  readonly data: T[];
@@ -794,4 +846,4 @@ declare class DropsResource<TInput = PublishInput> {
794
846
  delete(dropId: string): Promise<DropthisResult<null>>;
795
847
  }
796
848
 
797
- export { type AccountLimits as A, type PreparedUploadFile as B, type CreateUploadSessionRequest as C, type DeploymentContentFile as D, type EmailOtpResponse as E, type PublishFileInput as F, type GetContentOptions as G, type PublishInput as H, type InMemoryPublishInput as I, type PublishOptions as J, type RequestOptions as K, type Limitations as L, type SessionResponse as M, type NextHint as N, Transport as O, type PrepareOptions as P, type UploadManifestFile as Q, type RequestControls as R, SHARED_POOL as S, type TierInfo as T, type UpdateContentOptions as U, type UploadSessionFileResponse as V, type UploadSessionResponse as W, type UploadTarget as X, AccountResource as a, type AccountResponse as b, type ActionResolve as c, ApiKeysResource as d, type CreateUploadSessionResponse as e, CursorPage as f, type DeploymentContentManifest as g, DeploymentsResource as h, type DnsRecord as i, type DomainDeletedResponse as j, type DomainListResponse as k, type DomainResponse as l, DomainsResource as m, type DropAction as n, type DropContentFile as o, type DropDeploymentResponse as p, type DropOptions as q, type DropResponse as r, DropsResource as s, type DropthisClientOptions as t, type DropthisErrorResponse as u, type DropthisResult as v, type ListDeploymentsParams as w, type ListDeploymentsResponse as x, type ListPage as y, type PreparedPublishRequest as z };
849
+ export { type AccountLimits as A, type PreparedPublishRequest as B, type CreateUploadSessionRequest as C, type DeploymentContentFile as D, type EmailOtpResponse as E, type PreparedUploadFile as F, type GetContentOptions as G, type PublishFileInput as H, type InMemoryPublishInput as I, type PublishInput as J, type KeyType as K, type Limitations as L, type PublishOptions as M, type NextHint as N, type RequestOptions as O, type PrepareOptions as P, type RevokeImpact as Q, type RequestControls as R, SHARED_POOL as S, type SessionResponse as T, type TierInfo as U, Transport as V, type UpdateContentOptions as W, type UploadManifestFile as X, type UploadSessionFileResponse as Y, type UploadSessionResponse as Z, type UploadTarget as _, AccountResource as a, type AccountResponse as b, type ActionResolve as c, ApiKeysResource as d, type CreateUploadSessionResponse as e, CursorPage as f, CursorPaginationError as g, type DeploymentContentManifest as h, DeploymentsResource as i, type DnsRecord as j, type DomainDeletedResponse as k, type DomainListResponse as l, type DomainResponse as m, DomainsResource as n, type DropAction as o, type DropContentFile as p, type DropDeploymentResponse as q, type DropOptions as r, type DropResponse as s, DropsResource as t, type DropthisClientOptions as u, type DropthisErrorResponse as v, type DropthisResult as w, type ListDeploymentsParams as x, type ListDeploymentsResponse as y, type ListPage as z };
@@ -77,6 +77,10 @@ type DropResponse = {
77
77
  noindex: boolean;
78
78
  passwordProtected: boolean;
79
79
  metadata: Record<string, unknown>;
80
+ /** When the drop was last updated (content or settings), ISO 8601. */
81
+ updatedAt: string;
82
+ /** Origin that created the drop (e.g. "api", "cli", "mcp"); null/omitted when unattributed. */
83
+ source?: string | null;
80
84
  object: string;
81
85
  accessible: boolean;
82
86
  persistent: boolean;
@@ -142,7 +146,8 @@ type Action = {
142
146
  message: string;
143
147
  };
144
148
  type EmailOtpResponse = {
145
- ok: true;
149
+ /** Always `true` on success; optional because the server defaults it. */
150
+ ok?: true;
146
151
  expiresIn: number;
147
152
  nextAction?: Action | null;
148
153
  };
@@ -152,12 +157,35 @@ type SessionResponse = {
152
157
  accountId: string;
153
158
  isNewAccount: boolean;
154
159
  expiresIn: number;
160
+ /**
161
+ * Rotating refresh token for a console browser session. Present when a session is
162
+ * started (email/verify, refresh); `null`/omitted for non-session token issuance.
163
+ */
164
+ refreshToken?: string | null;
155
165
  };
166
+ /**
167
+ * Why an API key exists. `standard` keys are created by the user (CLI/SDK/manual);
168
+ * `mcp_oauth` keys are minted per connected client by the MCP OAuth flow and carry a
169
+ * more generous quota.
170
+ */
171
+ type KeyType = "standard" | "mcp_oauth";
172
+ /** What breaks when a key is revoked. */
173
+ type RevokeImpact = "disconnects_app" | "breaks_automation";
156
174
  type ApiKeyResponse = {
157
175
  object: "api_key";
158
176
  id: string;
159
177
  keyLast4: string;
160
178
  label: string;
179
+ /** Human-facing credential name: the user's label for `standard` keys, the connected client name for `mcp_oauth` keys. */
180
+ appName: string;
181
+ /** Why the key exists (drives quota accounting). */
182
+ keyType: KeyType;
183
+ /** Scopes granted to this key. */
184
+ scopes: string[];
185
+ /** When the key was last used to authenticate, ISO 8601; null/omitted if never used. */
186
+ lastUsedAt?: string | null;
187
+ /** What breaks if this key is revoked. */
188
+ revokeImpact: RevokeImpact;
161
189
  createdAt: string;
162
190
  };
163
191
  type ApiKeyCreatedResponse = ApiKeyResponse & {
@@ -252,9 +280,17 @@ type CreateUploadSessionFileResponse = {
252
280
  type UploadSessionFileResponse = {
253
281
  fileId: string;
254
282
  path: string;
255
- contentType: string;
256
- sizeBytes: number;
283
+ /** Declared MIME type; `null`/omitted for remote-fetch files until ingested. */
284
+ contentType?: string | null;
285
+ /** Declared byte size; `null`/omitted for remote-fetch files until ingested. */
286
+ sizeBytes?: number | null;
257
287
  objectKey: string;
288
+ /** How the file enters staging: "client_put" (client PUTs bytes) or "remote_fetch" (server fetches sourceUrl). */
289
+ origin: "client_put" | "remote_fetch";
290
+ /** Ingest lifecycle state for remote-fetch files (e.g. "pending" | "fetching" | "fetched" | "failed"). */
291
+ state: string;
292
+ /** Public http(s) URL the server fetches into staging; `null`/omitted for client-put files. */
293
+ sourceUrl?: string | null;
258
294
  verified: boolean;
259
295
  };
260
296
  type UploadSessionResponse = {
@@ -691,6 +727,22 @@ declare class DomainsResource {
691
727
  delete(idOrHostname: string): Promise<DropthisResult<DomainDeletedResponse>>;
692
728
  }
693
729
 
730
+ /**
731
+ * Thrown by the auto-pagination iterator (`for await` / `autoPagingToArray`) when
732
+ * fetching a later page fails. Carries the underlying {@link DropthisErrorResponse}
733
+ * on `.error` so an agent can inspect `code`/`statusCode`/`retryable`/`requestId`
734
+ * instead of silently receiving a truncated list. The already-yielded items are not
735
+ * lost — the items consumed before the failure remain consumed; only the continuation
736
+ * rejects.
737
+ */
738
+ declare class CursorPaginationError extends Error {
739
+ readonly error: DropthisErrorResponse;
740
+ /** Response headers from the failed page fetch (rate-limit, trace, request id). */
741
+ readonly headers: Record<string, string>;
742
+ constructor(error: DropthisErrorResponse,
743
+ /** Response headers from the failed page fetch (rate-limit, trace, request id). */
744
+ headers?: Record<string, string>);
745
+ }
694
746
  declare class CursorPage<T> implements ListPage<T> {
695
747
  readonly object: "list";
696
748
  readonly data: T[];
@@ -794,4 +846,4 @@ declare class DropsResource<TInput = PublishInput> {
794
846
  delete(dropId: string): Promise<DropthisResult<null>>;
795
847
  }
796
848
 
797
- export { type AccountLimits as A, type PreparedUploadFile as B, type CreateUploadSessionRequest as C, type DeploymentContentFile as D, type EmailOtpResponse as E, type PublishFileInput as F, type GetContentOptions as G, type PublishInput as H, type InMemoryPublishInput as I, type PublishOptions as J, type RequestOptions as K, type Limitations as L, type SessionResponse as M, type NextHint as N, Transport as O, type PrepareOptions as P, type UploadManifestFile as Q, type RequestControls as R, SHARED_POOL as S, type TierInfo as T, type UpdateContentOptions as U, type UploadSessionFileResponse as V, type UploadSessionResponse as W, type UploadTarget as X, AccountResource as a, type AccountResponse as b, type ActionResolve as c, ApiKeysResource as d, type CreateUploadSessionResponse as e, CursorPage as f, type DeploymentContentManifest as g, DeploymentsResource as h, type DnsRecord as i, type DomainDeletedResponse as j, type DomainListResponse as k, type DomainResponse as l, DomainsResource as m, type DropAction as n, type DropContentFile as o, type DropDeploymentResponse as p, type DropOptions as q, type DropResponse as r, DropsResource as s, type DropthisClientOptions as t, type DropthisErrorResponse as u, type DropthisResult as v, type ListDeploymentsParams as w, type ListDeploymentsResponse as x, type ListPage as y, type PreparedPublishRequest as z };
849
+ export { type AccountLimits as A, type PreparedPublishRequest as B, type CreateUploadSessionRequest as C, type DeploymentContentFile as D, type EmailOtpResponse as E, type PreparedUploadFile as F, type GetContentOptions as G, type PublishFileInput as H, type InMemoryPublishInput as I, type PublishInput as J, type KeyType as K, type Limitations as L, type PublishOptions as M, type NextHint as N, type RequestOptions as O, type PrepareOptions as P, type RevokeImpact as Q, type RequestControls as R, SHARED_POOL as S, type SessionResponse as T, type TierInfo as U, Transport as V, type UpdateContentOptions as W, type UploadManifestFile as X, type UploadSessionFileResponse as Y, type UploadSessionResponse as Z, type UploadTarget as _, AccountResource as a, type AccountResponse as b, type ActionResolve as c, ApiKeysResource as d, type CreateUploadSessionResponse as e, CursorPage as f, CursorPaginationError as g, type DeploymentContentManifest as h, DeploymentsResource as i, type DnsRecord as j, type DomainDeletedResponse as k, type DomainListResponse as l, type DomainResponse as m, DomainsResource as n, type DropAction as o, type DropContentFile as p, type DropDeploymentResponse as q, type DropOptions as r, type DropResponse as s, DropsResource as t, type DropthisClientOptions as u, type DropthisErrorResponse as v, type DropthisResult as w, type ListDeploymentsParams as x, type ListDeploymentsResponse as y, type ListPage as z };
@@ -631,6 +631,16 @@ var DomainsResource = class {
631
631
  };
632
632
 
633
633
  // src/pagination.ts
634
+ var CursorPaginationError = class extends Error {
635
+ constructor(error, headers = {}) {
636
+ super(`Failed to fetch next page: ${error.code} \u2014 ${error.message}`);
637
+ this.error = error;
638
+ this.headers = headers;
639
+ this.name = "CursorPaginationError";
640
+ }
641
+ error;
642
+ headers;
643
+ };
634
644
  var CursorPage = class {
635
645
  object = "list";
636
646
  data;
@@ -656,7 +666,7 @@ var CursorPage = class {
656
666
  let current = this;
657
667
  while (current.hasMore && current.fetchNextPage) {
658
668
  const next = await current.fetchNextPage();
659
- if (next.error) break;
669
+ if (next.error) throw new CursorPaginationError(next.error, next.headers);
660
670
  current = next.data;
661
671
  for (const item of current.data) yield item;
662
672
  }
@@ -871,7 +881,7 @@ function toSnakeCase(value) {
871
881
 
872
882
  // src/transport.ts
873
883
  var DEFAULT_BASE_URL = "https://api.dropthis.app";
874
- var SDK_VERSION = "0.20.0";
884
+ var SDK_VERSION = "0.21.0";
875
885
  var Transport = class {
876
886
  apiKey;
877
887
  baseUrl;
@@ -910,7 +920,10 @@ var Transport = class {
910
920
  `signed_upload_${response.status}`,
911
921
  text || response.statusText,
912
922
  response.status,
913
- { headers: responseHeaders }
923
+ // A 5xx from object storage is a transient upstream failure — safe to
924
+ // re-PUT. A 4xx (e.g. 403 expired/denied signed URL) won't fix itself on
925
+ // a blind retry, so leave it unmarked.
926
+ { headers: responseHeaders, retryable: response.status >= 500 }
914
927
  );
915
928
  }
916
929
  return {
@@ -925,7 +938,8 @@ var Transport = class {
925
938
  return createErrorResult(
926
939
  error instanceof Error && error.name === "AbortError" ? "timeout" : "network_error",
927
940
  message,
928
- null
941
+ null,
942
+ { retryable: true }
929
943
  );
930
944
  } finally {
931
945
  clearTimeout(timeout);
@@ -982,7 +996,8 @@ var Transport = class {
982
996
  return createErrorResult(
983
997
  error instanceof Error && error.name === "AbortError" ? "timeout" : "network_error",
984
998
  message,
985
- null
999
+ null,
1000
+ { retryable: true }
986
1001
  );
987
1002
  } finally {
988
1003
  clearTimeout(timeout);
@@ -1043,10 +1058,12 @@ var Transport = class {
1043
1058
  };
1044
1059
  } catch (error) {
1045
1060
  const message = error instanceof Error && error.name === "AbortError" ? "Request timed out" : error instanceof Error ? error.message : "Network error";
1061
+ const retrySafe = method.toUpperCase() === "GET" || options.idempotencyKey !== void 0;
1046
1062
  return createErrorResult(
1047
1063
  error instanceof Error && error.name === "AbortError" ? "timeout" : "network_error",
1048
1064
  message,
1049
- null
1065
+ null,
1066
+ retrySafe ? { retryable: true } : {}
1050
1067
  );
1051
1068
  } finally {
1052
1069
  clearTimeout(timeout);