@ambasada/sdk 0.8.9 → 0.8.11

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/define.d.ts CHANGED
@@ -31,6 +31,19 @@ export interface MiniAppConfig {
31
31
  scopes?: ("identity" | "notifications")[];
32
32
  visibility?: "public" | "builders" | "unlisted" | `event:${string}`;
33
33
  }
34
+ /**
35
+ * How the app is listed, whether or not it has a mini app (dry run 7 nit d: with the mini app off there was nowhere
36
+ * for these). The mini app's own `age_rating`, `kind` and `tags` win; these are its defaults, and `miniapp disable`
37
+ * keeps them here.
38
+ */
39
+ export interface ListingConfig {
40
+ /** Who it is for: "4+", "9+", "12+" or "17+" (the mini app's default "4+"). */
41
+ age_rating?: MiniAppConfig["age_rating"];
42
+ /** What it is: "product", "event", "tool" or "assistant" (the mini app's default "tool"). */
43
+ kind?: MiniAppConfig["kind"];
44
+ /** Up to 5 search words. */
45
+ tags?: string[];
46
+ }
34
47
  export interface AppConfig {
35
48
  /** The app's slug (lowercase): it lives at https://<id>.ambasada.dev. */
36
49
  id: string;
@@ -46,6 +59,8 @@ export interface AppConfig {
46
59
  };
47
60
  /** The mini app inside the Ambasada app, at a path of this one. Off without it. */
48
61
  miniapp?: MiniAppConfig;
62
+ /** age_rating, kind and tags, for an app with or without a mini app (the mini app's own win). */
63
+ listing?: ListingConfig;
49
64
  /** Add-ons to load besides the ones found in node_modules (package names, e.g. "@ambasada/village"). */
50
65
  addons?: string[];
51
66
  }
package/dist/errors.d.ts CHANGED
@@ -40,9 +40,11 @@ export declare const ERROR_STATUS: {
40
40
  readonly APPROVALS_PENDING: 429;
41
41
  readonly PROVIDER_BUDGET: 503;
42
42
  readonly PROVIDER_DENIED: 502;
43
+ readonly TOOL_UNAVAILABLE: 503;
43
44
  readonly FROZEN: 403;
44
45
  readonly CAP_INBOX_RATE: 429;
45
46
  readonly CAP_CHAT_RATE: 429;
47
+ readonly CAP_TOOLS_RATE: 429;
46
48
  readonly NETWORK: 0;
47
49
  readonly CONFIG: 0;
48
50
  readonly RECEIPT_INVALID: 402;
@@ -61,6 +63,8 @@ export interface ErrorDetails {
61
63
  upstream_status?: number;
62
64
  /** APPROVAL_REQUIRED: retry with `{ approval: approvalId }` once the team approved it. */
63
65
  approval_id?: string;
66
+ /** CAP_TOOLS_RATE, TOOL_UNAVAILABLE, a busy seller: seconds until it is worth trying again. */
67
+ retry_after_s?: number;
64
68
  [key: string]: unknown;
65
69
  }
66
70
  export declare class AmbasadaError extends Error {
@@ -74,6 +78,8 @@ export declare class AmbasadaError extends Error {
74
78
  get callId(): string | undefined;
75
79
  get refunded(): boolean;
76
80
  get approvalId(): string | undefined;
81
+ /** Seconds until it is worth trying again (CAP_TOOLS_RATE, TOOL_UNAVAILABLE, a busy seller), when the error says. */
82
+ get retryAfterS(): number | undefined;
77
83
  /** The wire shape, e.g. to pass an error on from your own endpoint. */
78
84
  toJSON(): {
79
85
  error: {
@@ -81,7 +87,7 @@ export declare class AmbasadaError extends Error {
81
87
  message: string;
82
88
  } & ErrorDetails;
83
89
  };
84
- /** A `Response` with the wire shape and status (402 for receipt problems). */
90
+ /** A `Response` with the wire shape and status (402 for receipt problems), and `Retry-After` when it has one. */
85
91
  toResponse(): Response;
86
92
  }
87
93
  export type WebhookFailure = "missing_headers" | "bad_timestamp" | "too_old" | "too_new" | "bad_signature" | "bad_secret" | "bad_body";
@@ -11,7 +11,7 @@ export { getHeader } from "./headers.js";
11
11
  export type { HeaderSource } from "./headers.js";
12
12
  export { defineApp } from "./define.js";
13
13
  export { MINIAPP_AGE_RATINGS, MINIAPP_ENTRY, MINIAPP_KINDS, supportUrl } from "./define.js";
14
- export type { AppConfig, MiniAppConfig } from "./define.js";
14
+ export type { AppConfig, ListingConfig, MiniAppConfig } from "./define.js";
15
15
  export { AmbasadaAuth, AUTH_PATHS, JWKS_TTL_MS, SESSION_TTL_S } from "./auth.js";
16
16
  export type { AmbasadaUser, AuthConfig, LoginOptions, ShellUser, WithHeaders } from "./auth.js";
17
17
  export { AmbasadaApps, APP_SIGNATURE_HEADER, APP_TIMESTAMP_HEADER, appWebhookHeaders, signAppWebhook } from "./apps.js";
package/dist/platform.js CHANGED
@@ -169,12 +169,14 @@ var ERROR_STATUS = {
169
169
  // LLMOK mapping (utilities stream, docs/LLMOK.md)
170
170
  PROVIDER_BUDGET: 503,
171
171
  PROVIDER_DENIED: 502,
172
+ TOOL_UNAVAILABLE: 503,
172
173
  // Clock windows (agent stream): deploys / steering refused while frozen
173
174
  FROZEN: 403,
174
175
  // Exchange stream: an agent sent too many inbox messages
175
176
  CAP_INBOX_RATE: 429,
176
177
  // The CEO chat: too many turns for one company
177
178
  CAP_CHAT_RATE: 429,
179
+ CAP_TOOLS_RATE: 429,
178
180
  // SDK-side
179
181
  NETWORK: 0,
180
182
  CONFIG: 0,
@@ -209,13 +211,19 @@ var AmbasadaError = class _AmbasadaError extends Error {
209
211
  get approvalId() {
210
212
  return this.details.approval_id;
211
213
  }
214
+ /** Seconds until it is worth trying again (CAP_TOOLS_RATE, TOOL_UNAVAILABLE, a busy seller), when the error says. */
215
+ get retryAfterS() {
216
+ const s = this.details.retry_after_s;
217
+ return typeof s === "number" && Number.isFinite(s) ? s : void 0;
218
+ }
212
219
  /** The wire shape, e.g. to pass an error on from your own endpoint. */
213
220
  toJSON() {
214
221
  return { error: { code: this.code, message: this.message, ...this.details } };
215
222
  }
216
- /** A `Response` with the wire shape and status (402 for receipt problems). */
223
+ /** A `Response` with the wire shape and status (402 for receipt problems), and `Retry-After` when it has one. */
217
224
  toResponse() {
218
- return Response.json(this.toJSON(), { status: this.status || 500 });
225
+ const retry = this.details.retry_after_s;
226
+ return Response.json(this.toJSON(), { status: this.status || 500, ...typeof retry === "number" ? { headers: { "retry-after": String(Math.ceil(retry)) } } : {} });
219
227
  }
220
228
  };
221
229
  var WebhookInvalidError = class extends AmbasadaError {
@@ -815,6 +823,11 @@ function supportUrl(value) {
815
823
  return void 0;
816
824
  }
817
825
  var MINIAPP_ENTRY = /^\/(?:[A-Za-z0-9._~-]+(?:\/[A-Za-z0-9._~-]+)*)?$/;
826
+ function checkListing(id, key, l) {
827
+ if (l.age_rating !== void 0 && !MINIAPP_AGE_RATINGS.includes(l.age_rating)) throw new Error(`${id}: ${key}.age_rating must be one of ${MINIAPP_AGE_RATINGS.join(", ")} (got ${JSON.stringify(l.age_rating)})`);
828
+ if (l.kind !== void 0 && !MINIAPP_KINDS.includes(l.kind)) throw new Error(`${id}: ${key}.kind must be one of ${MINIAPP_KINDS.join(", ")} (got ${JSON.stringify(l.kind)})`);
829
+ if (l.tags !== void 0 && (!Array.isArray(l.tags) || l.tags.length > 5 || l.tags.some((t) => typeof t !== "string"))) throw new Error(`${id}: ${key}.tags must be up to 5 strings`);
830
+ }
818
831
  function defineApp(config) {
819
832
  if (typeof config.id !== "string" || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(config.id)) throw new Error(`app id "${String(config.id)}" must be a lowercase slug (a-z, 0-9, -)`);
820
833
  const m = config.miniapp;
@@ -825,10 +838,12 @@ function defineApp(config) {
825
838
  }
826
839
  if (m.icon !== void 0 && (typeof m.icon !== "string" || !m.icon.startsWith("/"))) throw new Error(`${config.id}: miniapp.icon must be a path like "/icon.png"`);
827
840
  if (m.accent !== void 0 && !/^#[0-9a-fA-F]{6}$/.test(m.accent)) throw new Error(`${config.id}: miniapp.accent must be a #RRGGBB colour`);
828
- if (m.age_rating !== void 0 && !MINIAPP_AGE_RATINGS.includes(m.age_rating)) throw new Error(`${config.id}: miniapp.age_rating must be one of ${MINIAPP_AGE_RATINGS.join(", ")} (got ${JSON.stringify(m.age_rating)})`);
829
- if (m.kind !== void 0 && !MINIAPP_KINDS.includes(m.kind)) throw new Error(`${config.id}: miniapp.kind must be one of ${MINIAPP_KINDS.join(", ")} (got ${JSON.stringify(m.kind)})`);
841
+ checkListing(config.id, "miniapp", m);
830
842
  if (m.support !== void 0 && !supportUrl(m.support)) throw new Error(`${config.id}: miniapp.support must be an email or an https URL (got ${JSON.stringify(m.support)})`);
831
- if (m.tags !== void 0 && (!Array.isArray(m.tags) || m.tags.length > 5 || m.tags.some((t) => typeof t !== "string"))) throw new Error(`${config.id}: miniapp.tags must be up to 5 strings`);
843
+ }
844
+ if (config.listing !== void 0) {
845
+ if (!config.listing || typeof config.listing !== "object") throw new Error(`${config.id}: listing must be { age_rating, kind, tags }`);
846
+ checkListing(config.id, "listing", config.listing);
832
847
  }
833
848
  if (config.support !== void 0 && !supportUrl(config.support)) throw new Error(`${config.id}: support must be an email or an https URL (got ${JSON.stringify(config.support)})`);
834
849
  return config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ambasada/sdk",
3
- "version": "0.8.9",
3
+ "version": "0.8.11",
4
4
  "description": "The Ambasada platform SDK: Sign in with Ambasada, mini-app notifications, the project store, files.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -27,7 +27,7 @@
27
27
  "node": ">=22"
28
28
  },
29
29
  "dependencies": {
30
- "@ambasada/village": "^0.8.9"
30
+ "@ambasada/village": "^0.8.11"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^22.20.5",