@genex-ai/embed-sdk 0.17.0 → 0.21.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,15 @@
1
+ import {
2
+ NATIVE_CHANNEL,
3
+ NATIVE_PROTOCOL_VERSION,
4
+ NATIVE_RECEIVER,
5
+ encodeBridgeMessage,
6
+ hasNativeMarker,
7
+ isGenerationId,
8
+ isGenerationTerminal,
9
+ readBridgeNativeCommand,
10
+ readNativeEntry
11
+ } from "./chunk-Q476GJHI.js";
12
+
1
13
  // src/commerce.ts
2
14
  var cfg = null;
3
15
  function _initCommerce(c) {
@@ -105,234 +117,179 @@ async function buy(opts) {
105
117
  return awaitOutcome(view.id, opts.timeoutMs ?? 18e4);
106
118
  }
107
119
 
108
- // ../embed-protocol/src/constants.ts
109
- var NATIVE_CHANNEL = "genex-native";
110
- var NATIVE_PROTOCOL_VERSION = 1;
111
- var NATIVE_MARKER_PARAM = "genex_native";
112
- var NATIVE_MARKER_VALUE = "1";
113
- var NATIVE_PROTOCOL_PARAM = "genex_protocol";
114
- var NATIVE_ATTEMPT_PARAM = "genex_attempt";
115
- var NATIVE_NONCE_PARAM = "genex_nonce";
116
- var NATIVE_RECEIVER = "__genexNative";
117
- var MAX_BRIDGE_MESSAGE_BYTES = 4096;
118
- var BRIDGE_ID_RE = /^[0-9a-f]{32}$/;
119
- var SLUG_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
120
- function isBridgeId(value) {
121
- return typeof value === "string" && BRIDGE_ID_RE.test(value);
122
- }
123
- function isBridgeSlug(value) {
124
- return typeof value === "string" && SLUG_RE.test(value);
120
+ // src/generation.ts
121
+ var config = null;
122
+ function _initGeneration(value) {
123
+ config = value;
124
+ }
125
+ function must2() {
126
+ if (!config) throw new Error("genex generation: call initEmbed() first");
127
+ return config;
128
+ }
129
+ async function request(path, body) {
130
+ const c = must2();
131
+ const token = c.getToken();
132
+ if (!token) throw new Error("no_player_session");
133
+ const response = await fetch(`${c.apiUrl}/api/runtime${path}`, {
134
+ method: body === void 0 ? "GET" : "POST",
135
+ credentials: "omit",
136
+ cache: "no-store",
137
+ headers: { Authorization: `Bearer ${token}`, ...body === void 0 ? {} : { "Content-Type": "application/json" } },
138
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
139
+ });
140
+ const result = await response.json().catch(() => ({}));
141
+ if (!response.ok) throw new Error(typeof result.error === "string" ? result.error : "generation_unavailable");
142
+ return result;
125
143
  }
126
-
127
- // ../embed-protocol/src/base64url.ts
128
- var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
129
- var LOOKUP = (() => {
130
- const table = new Array(128).fill(-1);
131
- for (let i = 0; i < ALPHABET.length; i++) table[ALPHABET.charCodeAt(i)] = i;
132
- return table;
133
- })();
134
- function utf8Bytes(text) {
135
- const out = [];
136
- for (let i = 0; i < text.length; i++) {
137
- let code = text.charCodeAt(i);
138
- if (code >= 55296 && code <= 56319) {
139
- const next = i + 1 < text.length ? text.charCodeAt(i + 1) : 0;
140
- if (next >= 56320 && next <= 57343) {
141
- code = 65536 + (code - 55296 << 10) + (next - 56320);
142
- i++;
143
- } else {
144
- code = 65533;
145
- }
146
- } else if (code >= 56320 && code <= 57343) {
147
- code = 65533;
148
- }
149
- if (code < 128) out.push(code);
150
- else if (code < 2048) out.push(192 | code >> 6, 128 | code & 63);
151
- else if (code < 65536) {
152
- out.push(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
153
- } else {
154
- out.push(
155
- 240 | code >> 18,
156
- 128 | code >> 12 & 63,
157
- 128 | code >> 6 & 63,
158
- 128 | code & 63
159
- );
160
- }
161
- }
162
- return out;
144
+ function validEstimateCoins(value) {
145
+ return Number.isSafeInteger(value) && value >= 0 && value <= 1e6;
163
146
  }
164
- function utf8Text(bytes) {
165
- let out = "";
166
- for (let i = 0; i < bytes.length; ) {
167
- const b0 = bytes[i];
168
- let code;
169
- let size;
170
- if (b0 < 128) {
171
- code = b0;
172
- size = 1;
173
- } else if ((b0 & 224) === 192) {
174
- code = b0 & 31;
175
- size = 2;
176
- } else if ((b0 & 240) === 224) {
177
- code = b0 & 15;
178
- size = 3;
179
- } else if ((b0 & 248) === 240) {
180
- code = b0 & 7;
181
- size = 4;
182
- } else return null;
183
- if (i + size > bytes.length) return null;
184
- for (let k = 1; k < size; k++) {
185
- const b = bytes[i + k];
186
- if ((b & 192) !== 128) return null;
187
- code = code << 6 | b & 63;
188
- }
189
- if (size === 2 && code < 128) return null;
190
- if (size === 3 && (code < 2048 || code >= 55296 && code <= 57343)) return null;
191
- if (size === 4 && (code < 65536 || code > 1114111)) return null;
192
- if (code > 65535) {
193
- const v = code - 65536;
194
- out += String.fromCharCode(55296 + (v >> 10), 56320 + (v & 1023));
195
- } else {
196
- out += String.fromCharCode(code);
197
- }
198
- i += size;
199
- }
200
- return out;
147
+ function getGenerationModels() {
148
+ return request("/models");
201
149
  }
202
- function utf8ByteLength(text) {
203
- return utf8Bytes(text).length;
150
+ function isWorkflowIdentifier(value) {
151
+ return typeof value === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(value) && value !== "." && value !== "..";
204
152
  }
205
- function encodeBase64UrlJson(value) {
206
- const bytes = utf8Bytes(JSON.stringify(value));
207
- let out = "";
208
- for (let i = 0; i < bytes.length; i += 3) {
209
- const b0 = bytes[i];
210
- const b1 = bytes[i + 1];
211
- const b2 = bytes[i + 2];
212
- out += ALPHABET[b0 >> 2];
213
- out += ALPHABET[(b0 & 3) << 4 | (b1 ?? 0) >> 4];
214
- if (b1 === void 0) break;
215
- out += ALPHABET[(b1 & 15) << 2 | (b2 ?? 0) >> 6];
216
- if (b2 === void 0) break;
217
- out += ALPHABET[b2 & 63];
218
- }
219
- return out;
153
+ function getWorkflowOfferings(workflowId) {
154
+ if (!isWorkflowIdentifier(workflowId)) return Promise.reject(new Error("invalid_workflow_id"));
155
+ return request(`/workflows/${encodeURIComponent(workflowId)}/offerings`);
220
156
  }
221
- function decodeBase64UrlJson(encoded) {
222
- if (encoded.length % 4 === 1) return void 0;
223
- const bytes = [];
224
- let buffer = 0;
225
- let bits = 0;
226
- for (let i = 0; i < encoded.length; i++) {
227
- const code = encoded.charCodeAt(i);
228
- const value = code < 128 ? LOOKUP[code] : -1;
229
- if (value < 0) return void 0;
230
- buffer = buffer << 6 | value;
231
- bits += 6;
232
- if (bits >= 8) {
233
- bits -= 8;
234
- bytes.push(buffer >> bits & 255);
235
- }
236
- }
237
- if (bits > 0 && (buffer & (1 << bits) - 1) !== 0) return void 0;
238
- const text = utf8Text(bytes);
239
- if (text === null) return void 0;
240
- try {
241
- return JSON.parse(text);
242
- } catch {
243
- return void 0;
244
- }
157
+ function getGeneration(generationId) {
158
+ if (!isGenerationId(generationId)) return Promise.reject(new Error("invalid_generation_id"));
159
+ return request(`/generations/${encodeURIComponent(generationId)}`);
245
160
  }
246
-
247
- // ../embed-protocol/src/messages.ts
248
- var MAX_TICKET_LENGTH = 2048;
249
- var MAX_EXPIRES_AT_LENGTH = 32;
250
- var ISO_INSTANT_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z$/;
251
- function isRecord(value) {
252
- return typeof value === "object" && value !== null && !Array.isArray(value);
253
- }
254
- function hasExactKeys(value, keys) {
255
- const own = Object.keys(value);
256
- if (own.length !== keys.length) return false;
257
- for (const key of keys) {
258
- if (!Object.prototype.hasOwnProperty.call(value, key)) return false;
259
- }
260
- return true;
261
- }
262
- var BASE_KEYS = ["channel", "v", "type", "slug", "attemptId", "nonce"];
263
- function envelopeBaseIsValid(value) {
264
- return value.channel === NATIVE_CHANNEL && value.v === NATIVE_PROTOCOL_VERSION && isBridgeSlug(value.slug) && isBridgeId(value.attemptId) && isBridgeId(value.nonce);
265
- }
266
- function parseBridgeNativeCommand(value) {
267
- if (!isRecord(value) || !envelopeBaseIsValid(value)) return void 0;
268
- switch (value.type) {
269
- case "ticket":
270
- if (!hasExactKeys(value, [...BASE_KEYS, "ticket", "expiresAt"])) return void 0;
271
- if (typeof value.ticket !== "string" || value.ticket.length === 0 || value.ticket.length > MAX_TICKET_LENGTH) {
272
- return void 0;
273
- }
274
- if (typeof value.expiresAt !== "string" || value.expiresAt.length > MAX_EXPIRES_AT_LENGTH || !ISO_INSTANT_RE.test(value.expiresAt)) {
275
- return void 0;
276
- }
277
- return value;
278
- case "guest":
279
- case "cancel":
280
- if (!hasExactKeys(value, BASE_KEYS)) return void 0;
281
- return value;
282
- case "foreground":
283
- if (!hasExactKeys(value, [...BASE_KEYS, "active"])) return void 0;
284
- if (typeof value.active !== "boolean") return void 0;
285
- return value;
286
- case "network":
287
- if (!hasExactKeys(value, [...BASE_KEYS, "online"])) return void 0;
288
- if (typeof value.online !== "boolean") return void 0;
289
- return value;
290
- default:
291
- return void 0;
292
- }
161
+ function outcome(view) {
162
+ if (!isGenerationTerminal(view.status)) return void 0;
163
+ return {
164
+ status: view.status,
165
+ generationId: view.id,
166
+ ...billingReceipt(view),
167
+ ...view.status === "succeeded" ? {
168
+ output: view.output,
169
+ source: view.source,
170
+ ...view.modelProvenance ? { modelProvenance: view.modelProvenance } : {}
171
+ } : {},
172
+ ...view.error ? { error: view.error } : {}
173
+ };
293
174
  }
294
-
295
- // ../embed-protocol/src/transport.ts
296
- function read(raw, parse) {
297
- if (typeof raw !== "string" || raw.length === 0) return { ok: false, reason: "undecodable" };
298
- if (utf8ByteLength(raw) > MAX_BRIDGE_MESSAGE_BYTES) return { ok: false, reason: "too-large" };
299
- const decoded = decodeBase64UrlJson(raw);
300
- if (decoded === void 0) return { ok: false, reason: "undecodable" };
301
- const message = parse(decoded);
302
- if (message === void 0) return { ok: false, reason: "invalid" };
303
- return { ok: true, message };
304
- }
305
- function readBridgeNativeCommand(raw) {
306
- return read(raw, parseBridgeNativeCommand);
307
- }
308
- function encodeBridgeMessage(message) {
309
- const encoded = encodeBase64UrlJson(message);
310
- if (encoded.length > MAX_BRIDGE_MESSAGE_BYTES) return void 0;
311
- return encoded;
175
+ function billingReceipt(view) {
176
+ return {
177
+ ...view.billingStatus !== void 0 ? { billingStatus: view.billingStatus } : {},
178
+ ...view.reservedCoins !== void 0 ? { reservedCoins: view.reservedCoins } : {},
179
+ ...view.reservedDisplayUsdCents !== void 0 ? { reservedDisplayUsdCents: view.reservedDisplayUsdCents } : {},
180
+ ...view.chargedCoins !== void 0 ? { chargedCoins: view.chargedCoins } : {},
181
+ ...view.chargedDisplayUsdCents !== void 0 ? { chargedDisplayUsdCents: view.chargedDisplayUsdCents } : {}
182
+ };
312
183
  }
313
-
314
- // ../embed-protocol/src/entry-url.ts
315
- function readParam(search, key) {
316
- const query = search.charAt(0) === "?" ? search.slice(1) : search;
317
- if (query.length === 0) return null;
318
- for (const pair of query.split("&")) {
319
- const eq = pair.indexOf("=");
320
- if (eq === -1) continue;
321
- if (pair.slice(0, eq) === key) return pair.slice(eq + 1);
184
+ function authorizationOutcome(view) {
185
+ if (["queued", "dispatching", "awaiting_external", "succeeded"].includes(view.status)) {
186
+ return { status: "authorized", generationId: view.id, ...billingReceipt(view), ...view.funding ? { funding: view.funding } : {} };
322
187
  }
323
- return null;
188
+ if (!isGenerationTerminal(view.status)) return void 0;
189
+ return {
190
+ status: view.status === "canceled" || view.status === "expired" ? view.status : "failed",
191
+ generationId: view.id,
192
+ ...billingReceipt(view),
193
+ ...view.error ? { error: view.error } : view.status === "unknown" ? { error: "unknown_execution" } : {}
194
+ };
324
195
  }
325
- function hasNativeMarker(search) {
326
- return readParam(search, NATIVE_MARKER_PARAM) === NATIVE_MARKER_VALUE;
196
+ async function poll(generationId, timeoutMs, readOutcome, popup) {
197
+ const deadline = Date.now() + timeoutMs;
198
+ while (Date.now() < deadline) {
199
+ try {
200
+ const view = await getGeneration(generationId);
201
+ const result = readOutcome(view);
202
+ if (result) return result;
203
+ if (popup?.closed && view.status === "requires_confirmation") {
204
+ const canceled = await request(`/generations/${generationId}/cancel`, {});
205
+ const cancelResult = readOutcome(canceled);
206
+ if (cancelResult) return cancelResult;
207
+ }
208
+ } catch {
209
+ }
210
+ await new Promise((resolve) => setTimeout(resolve, 750));
211
+ }
212
+ return { status: "pending", generationId, error: "wait_timeout" };
213
+ }
214
+ function waitForGeneration(generationId, timeoutMs = 6e5) {
215
+ if (!isGenerationId(generationId)) return Promise.resolve({ status: "failed", error: "invalid_generation_id" });
216
+ return poll(generationId, timeoutMs, outcome);
217
+ }
218
+ async function generate(options) {
219
+ if (!validEstimateCoins(options.estimateCoins)) return { status: "failed", error: "invalid_estimate_coins" };
220
+ const { timeoutMs, idempotencyKey, ...input } = options;
221
+ return startGeneration(
222
+ "/generations",
223
+ { ...input, idempotencyKey },
224
+ timeoutMs ?? 6e5,
225
+ outcome
226
+ );
327
227
  }
328
- function readNativeEntry(search) {
329
- if (!hasNativeMarker(search)) return null;
330
- const attemptId = readParam(search, NATIVE_ATTEMPT_PARAM);
331
- const nonce = readParam(search, NATIVE_NONCE_PARAM);
332
- const protocol = Number(readParam(search, NATIVE_PROTOCOL_PARAM));
333
- if (!isBridgeId(attemptId) || !isBridgeId(nonce)) return null;
334
- if (protocol !== NATIVE_PROTOCOL_VERSION) return null;
335
- return { attemptId, nonce, protocol };
228
+ async function requestWorkflow(options) {
229
+ if (!validEstimateCoins(options.estimateCoins)) return { status: "failed", error: "invalid_estimate_coins" };
230
+ if (!isWorkflowIdentifier(options.workflowId)) return { status: "failed", error: "invalid_workflow_id" };
231
+ if (!isWorkflowIdentifier(options.offeringId)) return { status: "failed", error: "invalid_offering_id" };
232
+ return startGeneration(`/workflows/${encodeURIComponent(options.workflowId)}/requests`, {
233
+ offeringId: options.offeringId,
234
+ input: options.input,
235
+ estimateCoins: options.estimateCoins,
236
+ ...options.allowExternal !== void 0 ? { allowExternal: options.allowExternal } : {},
237
+ idempotencyKey: options.idempotencyKey
238
+ }, options.timeoutMs ?? 6e5, authorizationOutcome, options);
239
+ }
240
+ async function resumeWorkflow(generationId, options = {}) {
241
+ if (!isGenerationId(generationId)) return { status: "failed", error: "invalid_generation_id" };
242
+ return startGeneration("", {}, options.timeoutMs ?? 6e5, authorizationOutcome, void 0, generationId);
243
+ }
244
+ async function startGeneration(path, body, timeoutMs, readOutcome, workflow, resumeId) {
245
+ const c = must2();
246
+ if (c.isLocalTest()) return { status: "failed", error: "local_test_unsupported" };
247
+ if (c.isNative()) return { status: "failed", error: "native_unsupported" };
248
+ if (typeof window === "undefined") return { status: "failed", error: "no_window" };
249
+ if (!c.getToken()) return { status: "failed", error: "no_player_session" };
250
+ const parentOrigin2 = c.getParentOrigin();
251
+ if (c.isEmbedded() && (!parentOrigin2 || !c.dashboardOrigins.includes(parentOrigin2))) {
252
+ return { status: "failed", error: "untrusted_parent" };
253
+ }
254
+ let popup = null;
255
+ let dashboard;
256
+ if (!c.isEmbedded()) {
257
+ try {
258
+ dashboard = new URL(c.dashboardOrigins[0]);
259
+ if (dashboard.protocol !== "https:" && !(dashboard.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(dashboard.hostname))) throw new Error();
260
+ } catch {
261
+ return { status: "failed", error: "invalid_dashboard_origin" };
262
+ }
263
+ popup = window.open(`${dashboard.origin}/play/generate`, "_blank", "popup,width=480,height=760");
264
+ if (!popup) return { status: "failed", error: "popup_blocked" };
265
+ try {
266
+ popup.opener = null;
267
+ } catch {
268
+ }
269
+ }
270
+ try {
271
+ const view = resumeId ? await getGeneration(resumeId) : await request(path, { ...body, idempotencyKey: body.idempotencyKey ?? crypto.randomUUID() });
272
+ if (resumeId && (view.id !== resumeId || view.kind !== "workflow")) throw new Error("invalid_generation_response");
273
+ if (!isGenerationId(view.id) || workflow && (view.kind !== "workflow" || view.workflow?.id !== workflow.workflowId || view.workflow.offeringId !== workflow.offeringId)) {
274
+ throw new Error("invalid_generation_response");
275
+ }
276
+ const result = readOutcome(view);
277
+ const needsReceipt = view.billingStatus === "pending" || view.quote?.billingPolicy !== void 0 && (view.chargedCoins ?? 0) > 0;
278
+ if (result && result.status !== "authorized" && !needsReceipt) {
279
+ popup?.close();
280
+ return result;
281
+ }
282
+ if (popup && dashboard) {
283
+ if (!popup.closed) popup.location.href = `${dashboard.origin}/play/generate/${encodeURIComponent(view.id)}`;
284
+ } else if (parentOrigin2) {
285
+ window.parent.postMessage({ type: "genex:generation:confirm", v: 1, generationId: view.id }, parentOrigin2);
286
+ }
287
+ if (result) return result;
288
+ return poll(view.id, timeoutMs, readOutcome, popup);
289
+ } catch (error) {
290
+ popup?.close();
291
+ return { status: "failed", error: error instanceof Error ? error.message : "generation_unavailable" };
292
+ }
336
293
  }
337
294
 
338
295
  // src/index.ts
@@ -341,8 +298,8 @@ var RETRY_FLAG = "genex:embed:retry";
341
298
  var POPOVER_DISMISSED_FLAG = "genex:guest:popover-dismissed";
342
299
  var LOCAL_AUTH_FLAG = "genex:embed:local-auth";
343
300
  var PLAYER_ID_KEY = "genex:player";
344
- var SDK_VERSION = "0.17.0";
345
- var config = null;
301
+ var SDK_VERSION = "0.21.0";
302
+ var config2 = null;
346
303
  var state = "pending";
347
304
  var user = null;
348
305
  var localTestMode = false;
@@ -463,16 +420,25 @@ function initEmbed(cfg2) {
463
420
  clearTimeout(unattendedWaiterTimer);
464
421
  unattendedWaiterTimer = void 0;
465
422
  }
466
- config = {
423
+ config2 = {
467
424
  slug: cfg2.slug,
468
425
  apiUrl: cfg2.apiUrl.replace(/\/$/, ""),
469
426
  dashboardOrigins: [...cfg2.dashboardOrigins]
470
427
  };
471
428
  state = "pending";
472
429
  _initCommerce({
473
- apiUrl: config.apiUrl,
474
- dashboardOrigins: config.dashboardOrigins,
430
+ apiUrl: config2.apiUrl,
431
+ dashboardOrigins: config2.dashboardOrigins,
432
+ getToken: () => embedToken,
433
+ isEmbedded,
434
+ isNative: inNativeMode,
435
+ isLocalTest: () => localTestMode
436
+ });
437
+ _initGeneration({
438
+ apiUrl: config2.apiUrl,
439
+ dashboardOrigins: config2.dashboardOrigins,
475
440
  getToken: () => embedToken,
441
+ getParentOrigin: () => parentOrigin,
476
442
  isEmbedded,
477
443
  isNative: inNativeMode,
478
444
  isLocalTest: () => localTestMode
@@ -566,7 +532,7 @@ function on(event, cb) {
566
532
  }
567
533
  var KEEPALIVE_MAX_BYTES = 32 * 1024;
568
534
  async function ensurePlayer() {
569
- if (!config) throw new Error("genex embed: call initEmbed() first");
535
+ if (!config2) throw new Error("genex embed: call initEmbed() first");
570
536
  if (state === "pending") await waitForPlayer();
571
537
  if (state === "blocked") throw new Error("genex embed auth: blocked");
572
538
  }
@@ -575,7 +541,7 @@ function stateFetch(path, init = {}) {
575
541
  const headers = { Authorization: `Bearer ${embedToken}` };
576
542
  if (init.body !== void 0) headers["Content-Type"] = "application/json";
577
543
  if (init.ifVersion !== void 0) headers["If-Match"] = String(init.ifVersion);
578
- return doFetch(`${config.apiUrl}/api/projects/${encodeURIComponent(config.slug)}${path}`, {
544
+ return doFetch(`${config2.apiUrl}/api/projects/${encodeURIComponent(config2.slug)}${path}`, {
579
545
  method: init.method ?? "GET",
580
546
  headers,
581
547
  body: init.body,
@@ -665,7 +631,7 @@ async function submitScore(score, opts) {
665
631
  return { submitted: true, best: body.best, improved: body.improved };
666
632
  }
667
633
  async function getLeaderboard(opts) {
668
- if (!config) throw new Error("genex embed: call initEmbed() first");
634
+ if (!config2) throw new Error("genex embed: call initEmbed() first");
669
635
  if (localTestMode) return { items: [], me: null };
670
636
  if (state === "pending") {
671
637
  try {
@@ -681,7 +647,7 @@ async function getLeaderboard(opts) {
681
647
  const headers = {};
682
648
  if (embedToken) headers.Authorization = `Bearer ${embedToken}`;
683
649
  const res = await doFetch(
684
- `${config.apiUrl}/api/projects/${encodeURIComponent(config.slug)}/leaderboard${qs ? `?${qs}` : ""}`,
650
+ `${config2.apiUrl}/api/projects/${encodeURIComponent(config2.slug)}/leaderboard${qs ? `?${qs}` : ""}`,
685
651
  { headers }
686
652
  );
687
653
  if (!res.ok) throw new Error(`genex leaderboard failed (${res.status})`);
@@ -728,7 +694,7 @@ function acceptsTicket() {
728
694
  return (state === "pending" || state === "guest") && !redeeming;
729
695
  }
730
696
  async function handleParentMessage(event) {
731
- if (!config) return;
697
+ if (!config2) return;
732
698
  const data = event.data;
733
699
  if (!data || data.v !== PROTOCOL_VERSION) return;
734
700
  const isTicket = data.type === "genex:embed:ticket";
@@ -736,7 +702,7 @@ async function handleParentMessage(event) {
736
702
  if (!isTicket && !isGuest) return;
737
703
  if (isTicket && (!acceptsTicket() || typeof data.ticket !== "string" || !data.ticket)) return;
738
704
  if (isGuest && (state !== "pending" || redeeming || requestingGuest)) return;
739
- if (!config.dashboardOrigins.includes(event.origin)) {
705
+ if (!config2.dashboardOrigins.includes(event.origin)) {
740
706
  const fresh = await fetchDashboardOrigins();
741
707
  if (isTicket && !acceptsTicket()) return;
742
708
  if (isGuest && (state !== "pending" || redeeming || requestingGuest)) return;
@@ -773,13 +739,13 @@ function inNativeMode() {
773
739
  }
774
740
  function postToNative(message) {
775
741
  const w = win();
776
- if (!w || !config || !nativeEntry) return;
742
+ if (!w || !config2 || !nativeEntry) return;
777
743
  const bridge = nativeBridge(w);
778
744
  if (!bridge) return;
779
745
  const encoded = encodeBridgeMessage({
780
746
  channel: NATIVE_CHANNEL,
781
747
  v: NATIVE_PROTOCOL_VERSION,
782
- slug: config.slug,
748
+ slug: config2.slug,
783
749
  attemptId: nativeEntry.attemptId,
784
750
  nonce: nativeEntry.nonce,
785
751
  ...message
@@ -806,11 +772,11 @@ function startNativeHandshake(w, entry) {
806
772
  }, handshakeTimeoutMs);
807
773
  }
808
774
  async function handleNativeCommand(payload) {
809
- if (!config || !nativeEntry) return;
810
- const read2 = readBridgeNativeCommand(payload);
811
- if (!read2.ok) return;
812
- const command = read2.message;
813
- if (command.slug !== config.slug || command.attemptId !== nativeEntry.attemptId || command.nonce !== nativeEntry.nonce) {
775
+ if (!config2 || !nativeEntry) return;
776
+ const read = readBridgeNativeCommand(payload);
777
+ if (!read.ok) return;
778
+ const command = read.message;
779
+ if (command.slug !== config2.slug || command.attemptId !== nativeEntry.attemptId || command.nonce !== nativeEntry.nonce) {
814
780
  return;
815
781
  }
816
782
  switch (command.type) {
@@ -939,33 +905,33 @@ function readFragment(w) {
939
905
  return { ticket, guest };
940
906
  }
941
907
  async function redirectToAuthorize(w, opts) {
942
- if (!config || localTestMode) return;
908
+ if (!config2 || localTestMode) return;
943
909
  if (inNativeMode()) return;
944
- const origin = opts?.guestOk ? config.dashboardOrigins[0] ?? (await fetchDashboardOrigins())[0] : (await fetchDashboardOrigins())[0] ?? config.dashboardOrigins[0];
910
+ const origin = opts?.guestOk ? config2.dashboardOrigins[0] ?? (await fetchDashboardOrigins())[0] : (await fetchDashboardOrigins())[0] ?? config2.dashboardOrigins[0];
945
911
  if (!origin) {
946
912
  enterBlocked();
947
913
  return;
948
914
  }
949
915
  const returnTo = w.location.origin + w.location.pathname;
950
916
  const guestParam = opts?.guestOk ? "&guest=ok" : "";
951
- const url = `${origin}/play/authorize?returnTo=${encodeURIComponent(returnTo)}&slug=${encodeURIComponent(config.slug)}${guestParam}`;
917
+ const url = `${origin}/play/authorize?returnTo=${encodeURIComponent(returnTo)}&slug=${encodeURIComponent(config2.slug)}${guestParam}`;
952
918
  w.location.replace(url);
953
919
  }
954
920
  async function fetchDashboardOrigins() {
955
- if (!config) return [];
921
+ if (!config2) return [];
956
922
  try {
957
- const res = await doFetch(`${config.apiUrl}/api/embed/dashboard-origins`);
958
- if (!res.ok) return config.dashboardOrigins;
923
+ const res = await doFetch(`${config2.apiUrl}/api/embed/dashboard-origins`);
924
+ if (!res.ok) return config2.dashboardOrigins;
959
925
  const body = await res.json();
960
926
  if (Array.isArray(body.origins) && body.origins.every((o) => typeof o === "string")) {
961
927
  return body.origins;
962
928
  }
963
929
  } catch {
964
930
  }
965
- return config.dashboardOrigins;
931
+ return config2.dashboardOrigins;
966
932
  }
967
933
  async function redeemTicket(ticket) {
968
- if (!config || localTestMode || redeeming) return;
934
+ if (!config2 || localTestMode || redeeming) return;
969
935
  const reauthing = nativeReauthPending && state === "authenticated";
970
936
  if (state !== "pending" && state !== "guest" && !reauthing) return;
971
937
  const upgradingFromGuest = state === "guest";
@@ -973,7 +939,7 @@ async function redeemTicket(ticket) {
973
939
  redeeming = true;
974
940
  try {
975
941
  const res = await doFetch(
976
- `${config.apiUrl}/api/embed/session`,
942
+ `${config2.apiUrl}/api/embed/session`,
977
943
  {
978
944
  method: "POST",
979
945
  headers: { "Content-Type": "application/json" },
@@ -1021,18 +987,18 @@ async function redeemTicket(ticket) {
1021
987
  }
1022
988
  }
1023
989
  async function requestGuestSession() {
1024
- if (!config || localTestMode || requestingGuest || redeeming) return;
990
+ if (!config2 || localTestMode || requestingGuest || redeeming) return;
1025
991
  if (state !== "pending" && state !== "guest") return;
1026
992
  const epoch = ++identityEpoch;
1027
993
  requestingGuest = true;
1028
994
  try {
1029
- const res = await doFetch(`${config.apiUrl}/api/embed/guest-session`, {
995
+ const res = await doFetch(`${config2.apiUrl}/api/embed/guest-session`, {
1030
996
  method: "POST",
1031
997
  headers: { "Content-Type": "application/json" },
1032
998
  // playerId is analytics-only: the SERVER still mints the session's own
1033
999
  // guest id and the token is unchanged — this just lets the play event
1034
1000
  // key on something that survives the tab.
1035
- body: JSON.stringify({ slug: config.slug, playerId: playerId() })
1001
+ body: JSON.stringify({ slug: config2.slug, playerId: playerId() })
1036
1002
  }, handshakeTimeoutMs);
1037
1003
  if (epoch !== identityEpoch) return;
1038
1004
  if (!res.ok) {
@@ -1148,12 +1114,12 @@ function scheduleRefresh() {
1148
1114
  }, refreshDelayMs);
1149
1115
  }
1150
1116
  async function refreshToken() {
1151
- if (!config || state !== "authenticated" && state !== "guest" || !embedToken) return;
1117
+ if (!config2 || state !== "authenticated" && state !== "guest" || !embedToken) return;
1152
1118
  const epoch = identityEpoch;
1153
1119
  const tokenAtStart = embedToken;
1154
1120
  let status;
1155
1121
  try {
1156
- const res = await doFetch(`${config.apiUrl}/api/embed/session/refresh`, {
1122
+ const res = await doFetch(`${config2.apiUrl}/api/embed/session/refresh`, {
1157
1123
  method: "POST",
1158
1124
  headers: { Authorization: `Bearer ${tokenAtStart}` },
1159
1125
  // Best-effort completion if the tab starts unloading mid-refresh; zero
@@ -1533,7 +1499,7 @@ function __resetForTests(overrides) {
1533
1499
  removeOverlay(true);
1534
1500
  removeGuestPopover();
1535
1501
  overlayFontRequested = false;
1536
- config = null;
1502
+ config2 = null;
1537
1503
  state = "pending";
1538
1504
  user = null;
1539
1505
  localTestMode = false;
@@ -1564,6 +1530,13 @@ export {
1564
1530
  getEntitlements,
1565
1531
  consumeEntitlement,
1566
1532
  buy,
1533
+ getGenerationModels,
1534
+ getWorkflowOfferings,
1535
+ getGeneration,
1536
+ waitForGeneration,
1537
+ generate,
1538
+ requestWorkflow,
1539
+ resumeWorkflow,
1567
1540
  initEmbed,
1568
1541
  isEmbedded,
1569
1542
  getAuthState,