@ragable/sdk 0.7.8 → 0.7.10

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/index.mjs CHANGED
@@ -2335,6 +2335,95 @@ function stripTrailingCommas(text) {
2335
2335
  return text.replace(/,(\s*[}\]])/g, "$1").replace(/,\s*$/, "");
2336
2336
  }
2337
2337
 
2338
+ // src/content.ts
2339
+ var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
2340
+ function toWireUserContent(content) {
2341
+ if (typeof content === "string") return content;
2342
+ const out = [];
2343
+ for (const part of content) {
2344
+ if (!part) continue;
2345
+ if (part.type === "text") {
2346
+ out.push(toWireTextPart(part));
2347
+ } else if (part.type === "image") {
2348
+ out.push(toWireImagePart(part));
2349
+ }
2350
+ }
2351
+ return out;
2352
+ }
2353
+ function toWireTextPart(part) {
2354
+ return { type: "text", text: part.text };
2355
+ }
2356
+ function toWireImagePart(part) {
2357
+ const url = imagePartToUrl(part);
2358
+ const detail = part.detail;
2359
+ return {
2360
+ type: "image_url",
2361
+ image_url: detail ? { url, detail } : { url }
2362
+ };
2363
+ }
2364
+ function imagePartToUrl(part) {
2365
+ const img = part.image;
2366
+ if (img instanceof URL) return img.href;
2367
+ if (typeof img === "string") {
2368
+ if (img.startsWith("http://") || img.startsWith("https://")) return img;
2369
+ if (img.startsWith("data:")) return img;
2370
+ const mediaType = requireMediaType(part, "raw base64 string");
2371
+ assertBase64SizeOk(img);
2372
+ return `data:${mediaType};base64,${img}`;
2373
+ }
2374
+ if (img instanceof Uint8Array || img instanceof ArrayBuffer) {
2375
+ const bytes = img instanceof Uint8Array ? img : new Uint8Array(img);
2376
+ assertBinarySizeOk(bytes);
2377
+ const mediaType = requireMediaType(part, "binary image data");
2378
+ const b64 = bytesToBase64(bytes);
2379
+ return `data:${mediaType};base64,${b64}`;
2380
+ }
2381
+ throw new RagableError(
2382
+ "ImagePart.image must be a string, URL, Uint8Array, or ArrayBuffer",
2383
+ 400,
2384
+ { code: "SDK_INVALID_IMAGE_PART" }
2385
+ );
2386
+ }
2387
+ function requireMediaType(part, what) {
2388
+ const m = part.mediaType?.trim();
2389
+ if (!m) {
2390
+ throw new RagableError(
2391
+ `ImagePart.mediaType is required for ${what} (e.g. "image/png")`,
2392
+ 400,
2393
+ { code: "SDK_IMAGE_MEDIA_TYPE_REQUIRED" }
2394
+ );
2395
+ }
2396
+ return m;
2397
+ }
2398
+ function assertBinarySizeOk(bytes) {
2399
+ if (bytes.byteLength > MAX_IMAGE_BYTES) {
2400
+ throw new RagableError(
2401
+ `Image exceeds 5MB limit (${bytes.byteLength} bytes)`,
2402
+ 400,
2403
+ { code: "SDK_IMAGE_TOO_LARGE" }
2404
+ );
2405
+ }
2406
+ }
2407
+ function assertBase64SizeOk(b64) {
2408
+ const approxBytes = Math.floor(b64.length * 3 / 4);
2409
+ if (approxBytes > MAX_IMAGE_BYTES) {
2410
+ throw new RagableError(
2411
+ `Image exceeds 5MB limit (~${approxBytes} bytes decoded)`,
2412
+ 400,
2413
+ { code: "SDK_IMAGE_TOO_LARGE" }
2414
+ );
2415
+ }
2416
+ }
2417
+ function bytesToBase64(bytes) {
2418
+ const CHUNK = 32768;
2419
+ let binary = "";
2420
+ for (let i = 0; i < bytes.length; i += CHUNK) {
2421
+ const slice = bytes.subarray(i, i + CHUNK);
2422
+ binary += String.fromCharCode(...slice);
2423
+ }
2424
+ return btoa(binary);
2425
+ }
2426
+
2338
2427
  // src/stream-parts.ts
2339
2428
  function normalizeFinishReason(raw) {
2340
2429
  switch (raw) {
@@ -2547,7 +2636,9 @@ var ZERO_USAGE = {
2547
2636
  function buildInferenceRequestBody(params, responseFormat) {
2548
2637
  const body = {
2549
2638
  model: params.model,
2550
- messages: params.messages
2639
+ messages: params.messages.map(
2640
+ (m) => m.role === "user" ? { role: "user", content: toWireUserContent(m.content) } : m
2641
+ )
2551
2642
  };
2552
2643
  if (params.system !== void 0) body.system = params.system;
2553
2644
  if (typeof params.temperature === "number")
@@ -3782,6 +3873,120 @@ var RagableBrowserStorageClient = class {
3782
3873
  return new BrowserStorageBucketClient(this.options, this.fetchImpl, bucketId);
3783
3874
  }
3784
3875
  };
3876
+ var RagableBrowserMailClient = class {
3877
+ constructor(options, auth) {
3878
+ this.options = options;
3879
+ this.auth = auth;
3880
+ __publicField(this, "fetchImpl");
3881
+ this.fetchImpl = bindFetch(options.fetch);
3882
+ }
3883
+ requireWebsiteId() {
3884
+ const websiteId = this.options.websiteId?.trim();
3885
+ if (!websiteId) {
3886
+ throw new RagableError(
3887
+ "websiteId is required for mail operations. Use createWebsiteRagableClient() or pass createBrowserClient({ websiteId, ... }).",
3888
+ 400,
3889
+ { code: "SDK_MISSING_WEBSITE_ID" }
3890
+ );
3891
+ }
3892
+ return websiteId;
3893
+ }
3894
+ pathTo(p) {
3895
+ const websiteId = this.requireWebsiteId();
3896
+ const orgId = this.options.organizationId;
3897
+ return `${normalizeBrowserApiBase()}/public/organizations/${orgId}/websites/${websiteId}/mail${p.startsWith("/") ? p : `/${p}`}`;
3898
+ }
3899
+ /**
3900
+ * Get the Bearer token used to authenticate the call:
3901
+ * 1. End-user access token (preferred when an auth group is configured
3902
+ * and the user has signed in)
3903
+ * 2. `dataStaticKey` from createBrowserClient options
3904
+ * 3. Caller-supplied `getAccessToken()`
3905
+ *
3906
+ * If none is available, throws — server-side use should pass the
3907
+ * data-admin key as `dataStaticKey`.
3908
+ */
3909
+ async getBearerToken() {
3910
+ if (this.auth) {
3911
+ const token = await this.auth.getValidAccessToken().catch(() => null);
3912
+ if (token) return token;
3913
+ }
3914
+ const callerProvided = await this.options.getAccessToken?.();
3915
+ if (typeof callerProvided === "string" && callerProvided.length > 0) {
3916
+ return callerProvided;
3917
+ }
3918
+ if (this.options.dataStaticKey?.trim()) {
3919
+ return this.options.dataStaticKey.trim();
3920
+ }
3921
+ throw new RagableError(
3922
+ "Mail requests need authentication: either sign in via client.auth.signIn(...) or pass dataStaticKey (the auth group data-admin key) when creating the client.",
3923
+ 401,
3924
+ { code: "SDK_MAIL_NOT_AUTHENTICATED" }
3925
+ );
3926
+ }
3927
+ async request(path, init = {}) {
3928
+ const token = await this.getBearerToken();
3929
+ const headers = new Headers(init.headers ?? this.options.headers);
3930
+ headers.set("Authorization", `Bearer ${token}`);
3931
+ if (init.body && !headers.has("Content-Type")) {
3932
+ headers.set("Content-Type", "application/json");
3933
+ }
3934
+ const response = await this.fetchImpl(this.pathTo(path), {
3935
+ ...init,
3936
+ headers
3937
+ });
3938
+ const payload = await parseMaybeJsonBody(response);
3939
+ if (!response.ok) {
3940
+ const message = extractErrorMessage(payload, response.statusText);
3941
+ throw new RagableError(message, response.status, payload);
3942
+ }
3943
+ return payload;
3944
+ }
3945
+ /** Send an email from this website's linked Gmail account. */
3946
+ async send(params) {
3947
+ if (!params.to?.length) {
3948
+ throw new RagableError(
3949
+ "`to` must contain at least one recipient.",
3950
+ 400,
3951
+ { code: "SDK_MAIL_NO_RECIPIENTS" }
3952
+ );
3953
+ }
3954
+ if (!params.bodyText && !params.bodyHtml) {
3955
+ throw new RagableError(
3956
+ "Provide at least one of `bodyText` or `bodyHtml`.",
3957
+ 400,
3958
+ { code: "SDK_MAIL_NO_BODY" }
3959
+ );
3960
+ }
3961
+ return this.request("/send", {
3962
+ method: "POST",
3963
+ body: JSON.stringify(params)
3964
+ });
3965
+ }
3966
+ /** Search messages with Gmail query syntax. */
3967
+ async search(params = {}) {
3968
+ const qs = new URLSearchParams();
3969
+ if (params.query) qs.set("q", params.query);
3970
+ if (params.maxResults) qs.set("maxResults", String(params.maxResults));
3971
+ if (params.pageToken) qs.set("pageToken", params.pageToken);
3972
+ if (params.labelIds?.length) qs.set("labelIds", params.labelIds.join(","));
3973
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
3974
+ return this.request(`/search${suffix}`, { method: "GET" });
3975
+ }
3976
+ /** Fetch a single message in full (headers + decoded text/html body). */
3977
+ async getMessage(messageId) {
3978
+ if (!messageId) {
3979
+ throw new RagableError("`messageId` is required.", 400, {
3980
+ code: "SDK_MAIL_NO_MESSAGE_ID"
3981
+ });
3982
+ }
3983
+ const { message } = await this.request(
3984
+ `/messages/${encodeURIComponent(messageId)}`,
3985
+ { method: "GET" }
3986
+ );
3987
+ return message;
3988
+ }
3989
+ };
3785
3990
  var RagableBrowserAgentsClient = class {
3786
3991
  constructor(options) {
3787
3992
  this.options = options;
@@ -4108,6 +4313,7 @@ var RagableBrowser = class {
4108
4313
  __publicField(this, "database");
4109
4314
  __publicField(this, "db");
4110
4315
  __publicField(this, "storage");
4316
+ __publicField(this, "mail");
4111
4317
  __publicField(this, "transport");
4112
4318
  __publicField(this, "_ragableAuth");
4113
4319
  /** Delegates to `database.from()`. Kept for back-compat — prefer `database.from()`. */
@@ -4153,6 +4359,7 @@ var RagableBrowser = class {
4153
4359
  this.database._setTransport(this.transport);
4154
4360
  this.db = this.database;
4155
4361
  this.storage = new RagableBrowserStorageClient(options, bindFetch(options.fetch));
4362
+ this.mail = new RagableBrowserMailClient(options, this._ragableAuth);
4156
4363
  }
4157
4364
  destroy() {
4158
4365
  this._ragableAuth?.destroy();
@@ -4193,6 +4400,7 @@ export {
4193
4400
  RagableBrowserAiClient,
4194
4401
  RagableBrowserAuthClient,
4195
4402
  RagableBrowserDatabaseClient,
4403
+ RagableBrowserMailClient,
4196
4404
  RagableBrowserStorageClient,
4197
4405
  RagableError,
4198
4406
  RagableNetworkError,
@@ -4205,6 +4413,7 @@ export {
4205
4413
  bindFetch,
4206
4414
  buildInferenceRequestBody,
4207
4415
  buildResponseFormat,
4416
+ bytesToBase64,
4208
4417
  collectAssistantTextFromUiSegments,
4209
4418
  collectionRecordToRowWithMeta,
4210
4419
  collectionRecordsToRowWithMeta,
@@ -4220,6 +4429,7 @@ export {
4220
4429
  formatPostgrestError,
4221
4430
  formatSdkError,
4222
4431
  generateIdempotencyKey,
4432
+ imagePartToUrl,
4223
4433
  isIncompleteAgentStreamError,
4224
4434
  mapAgentEvent,
4225
4435
  mapFireworksChunk,
@@ -4234,6 +4444,7 @@ export {
4234
4444
  runAgentChatStreamLenient,
4235
4445
  streamObjectFromContext,
4236
4446
  toRagableResult,
4447
+ toWireUserContent,
4237
4448
  tryParsePartialJson,
4238
4449
  unwrapPostgrest,
4239
4450
  wrapStreamTextAsObject