@genex-ai/embed-sdk 0.16.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.
@@ -0,0 +1,44 @@
1
+ import { RuntimeWorkflowInput, RuntimeGenerationView } from '@genex/embed-protocol';
2
+
3
+ /** Server-only benchmarks spend the authenticated creator's own coin wallet. */
4
+
5
+ interface DevelopmentGenerationConfig {
6
+ apiUrl: string;
7
+ /** Full creator bearer credential, kept only in your server or local script. */
8
+ creatorToken: string;
9
+ projectId: string;
10
+ }
11
+ interface DevelopmentGenerationOptions extends DevelopmentGenerationConfig {
12
+ /** Explicit maximum reservation in your own coins. Actual usage is charged. */
13
+ maxCoins: number;
14
+ request: {
15
+ modelId: string;
16
+ prompt: string;
17
+ outputFormat: 'text' | 'json';
18
+ schema?: Record<string, unknown>;
19
+ idempotencyKey?: string;
20
+ };
21
+ /** Waiting time only; timeout returns the last server view without canceling. */
22
+ timeoutMs?: number;
23
+ }
24
+ interface DevelopmentWorkflowOptions extends DevelopmentGenerationConfig {
25
+ workflowId: string;
26
+ maxCoins: number;
27
+ request: {
28
+ offeringId: string;
29
+ input: RuntimeWorkflowInput;
30
+ idempotencyKey?: string;
31
+ };
32
+ }
33
+ /** Create a generic benchmark and wait for execution and its actual-usage receipt. */
34
+ declare function developmentGenerate(options: DevelopmentGenerationOptions): Promise<RuntimeGenerationView>;
35
+ /** Returns the accepted workflow so its trusted executor can enqueue before waiting. */
36
+ declare function developmentRequestWorkflow(options: DevelopmentWorkflowOptions): Promise<RuntimeGenerationView>;
37
+ /** Recover a benchmark with the same creator and project; no play token is used. */
38
+ declare function getDevelopmentGeneration(config: DevelopmentGenerationConfig, generationId: string): Promise<RuntimeGenerationView>;
39
+ /** Acknowledges cancellation; the returned receipt determines the charge or remaining hold. */
40
+ declare function cancelDevelopmentGeneration(config: DevelopmentGenerationConfig, generationId: string): Promise<RuntimeGenerationView>;
41
+ /** Wait for execution and final billing; timeout preserves the latest pending receipt. */
42
+ declare function waitForDevelopmentGeneration(config: DevelopmentGenerationConfig, generationId: string, timeoutMs?: number): Promise<RuntimeGenerationView>;
43
+
44
+ export { type DevelopmentGenerationConfig, type DevelopmentGenerationOptions, type DevelopmentWorkflowOptions, cancelDevelopmentGeneration, developmentGenerate, developmentRequestWorkflow, getDevelopmentGeneration, waitForDevelopmentGeneration };
@@ -0,0 +1,99 @@
1
+ import {
2
+ isGenerationId,
3
+ isGenerationTerminal
4
+ } from "./chunk-Q476GJHI.js";
5
+
6
+ // src/development.ts
7
+ function assertIdentifier(value) {
8
+ if (!/^[A-Za-z0-9_.:-]{1,120}$/.test(value) || value === "." || value === "..") throw new Error("invalid_development_identifier");
9
+ }
10
+ function baseUrl(config) {
11
+ if (typeof window !== "undefined") throw new Error("development_server_only");
12
+ assertIdentifier(config.projectId);
13
+ if (!config.creatorToken || /\s/.test(config.creatorToken)) throw new Error("creator_token_required");
14
+ const url = new URL(config.apiUrl);
15
+ if (url.username || url.password || url.search || url.hash || url.pathname !== "/" || url.protocol !== "https:" && !(url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) {
16
+ throw new Error("invalid_api_url");
17
+ }
18
+ return url.origin + "/api/runtime/development/projects/" + encodeURIComponent(config.projectId);
19
+ }
20
+ function validateMaximum(maxCoins) {
21
+ if (!Number.isSafeInteger(maxCoins) || maxCoins <= 0 || maxCoins > 1e6) throw new Error("invalid_max_coins");
22
+ }
23
+ async function request(config, path, body) {
24
+ const response = await fetch(baseUrl(config) + path, {
25
+ method: body === void 0 ? "GET" : "POST",
26
+ credentials: "omit",
27
+ cache: "no-store",
28
+ redirect: "error",
29
+ headers: { Authorization: "Bearer " + config.creatorToken, ...body === void 0 ? {} : { "Content-Type": "application/json" } },
30
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
31
+ });
32
+ const result = await response.json().catch(() => ({}));
33
+ if (!response.ok) throw new Error(typeof result.error === "string" ? result.error : "development_generation_unavailable");
34
+ const view = result;
35
+ if (!isGenerationId(view.id) || view.projectId !== config.projectId || view.development !== true || view.quote?.billingPolicy !== "consumed-v1" || view.funding !== "coins") throw new Error("invalid_development_response");
36
+ return view;
37
+ }
38
+ async function developmentGenerate(options) {
39
+ validateMaximum(options.maxCoins);
40
+ if (options.timeoutMs !== void 0 && (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 0)) throw new Error("invalid_timeout");
41
+ const input = options.request;
42
+ const view = await request(options, "/generations", { maxCoins: options.maxCoins, request: {
43
+ modelId: input.modelId,
44
+ prompt: input.prompt,
45
+ outputFormat: input.outputFormat,
46
+ ...input.schema === void 0 ? {} : { schema: input.schema },
47
+ idempotencyKey: input.idempotencyKey ?? crypto.randomUUID()
48
+ } });
49
+ return wait(options, view, options.timeoutMs ?? 6e5);
50
+ }
51
+ async function developmentRequestWorkflow(options) {
52
+ validateMaximum(options.maxCoins);
53
+ assertIdentifier(options.workflowId);
54
+ assertIdentifier(options.request.offeringId);
55
+ const view = await request(options, "/workflows/" + encodeURIComponent(options.workflowId) + "/requests", {
56
+ maxCoins: options.maxCoins,
57
+ request: {
58
+ offeringId: options.request.offeringId,
59
+ input: options.request.input,
60
+ idempotencyKey: options.request.idempotencyKey ?? crypto.randomUUID()
61
+ }
62
+ });
63
+ if (view.kind !== "workflow" || view.workflow?.id !== options.workflowId || view.workflow.offeringId !== options.request.offeringId) {
64
+ throw new Error("invalid_development_response");
65
+ }
66
+ return view;
67
+ }
68
+ async function getDevelopmentGeneration(config, generationId) {
69
+ if (!isGenerationId(generationId)) throw new Error("invalid_generation_id");
70
+ const view = await request(config, "/generations/" + encodeURIComponent(generationId));
71
+ if (view.id !== generationId) throw new Error("invalid_development_response");
72
+ return view;
73
+ }
74
+ async function cancelDevelopmentGeneration(config, generationId) {
75
+ if (!isGenerationId(generationId)) throw new Error("invalid_generation_id");
76
+ const view = await request(config, "/generations/" + encodeURIComponent(generationId) + "/cancel", {});
77
+ if (view.id !== generationId) throw new Error("invalid_development_response");
78
+ return view;
79
+ }
80
+ async function wait(config, initial, timeoutMs) {
81
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) throw new Error("invalid_timeout");
82
+ const deadline = Date.now() + timeoutMs;
83
+ let view = initial;
84
+ while (!(isGenerationTerminal(view.status) && view.billingStatus === "final") && Date.now() < deadline) {
85
+ await new Promise((resolve) => setTimeout(resolve, Math.min(750, deadline - Date.now())));
86
+ view = await getDevelopmentGeneration(config, view.id);
87
+ }
88
+ return view;
89
+ }
90
+ async function waitForDevelopmentGeneration(config, generationId, timeoutMs = 6e5) {
91
+ return wait(config, await getDevelopmentGeneration(config, generationId), timeoutMs);
92
+ }
93
+ export {
94
+ cancelDevelopmentGeneration,
95
+ developmentGenerate,
96
+ developmentRequestWorkflow,
97
+ getDevelopmentGeneration,
98
+ waitForDevelopmentGeneration
99
+ };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import { RuntimeWorkflowInput, RuntimeGenerationBilling, RuntimeGenerationFunding, RuntimeGenerationView, RuntimeGenerationModels, RuntimeWorkflowOfferings } from '@genex/embed-protocol';
2
+ export { RuntimeBillingPolicy, RuntimeExternalProvider, RuntimeGenerationBilling, RuntimeGenerationFunding, RuntimeGenerationModels, RuntimeGenerationStatus, RuntimeGenerationView, RuntimeWorkflowInput, RuntimeWorkflowOfferings } from '@genex/embed-protocol';
3
+
1
4
  /** A thing the game sells. Resolved server-side; the game never sets a price. */
2
5
  interface ShopItem {
3
6
  id: string;
@@ -118,6 +121,75 @@ declare function buy(opts: {
118
121
  timeoutMs?: number;
119
122
  }): Promise<PurchaseResult>;
120
123
 
124
+ interface GenerateOptions {
125
+ /** Fixed coin price of a started attempt. Zero is accepted only for server-authorized free options. */
126
+ estimateCoins: number;
127
+ modelId: string;
128
+ prompt: string;
129
+ outputFormat: 'text' | 'json';
130
+ schema?: Record<string, unknown>;
131
+ /** Allow player-supplied results through their own configured Claude or ChatGPT account. Defaults off. */
132
+ allowExternal?: boolean;
133
+ /** Reuse for retries of the same operation, including after a game reload. */
134
+ idempotencyKey?: string;
135
+ /** Waiting time only. Reaching it does not cancel an accepted generation. */
136
+ timeoutMs?: number;
137
+ }
138
+ interface RuntimeGenerationResult extends RuntimeGenerationBilling {
139
+ status: 'succeeded' | 'failed' | 'unknown' | 'canceled' | 'expired' | 'pending';
140
+ generationId?: string;
141
+ output?: unknown;
142
+ /** External output is user-supplied; never use it as authority for rewards or money. */
143
+ source?: 'openrouter' | 'external';
144
+ modelProvenance?: 'unverified';
145
+ error?: string;
146
+ }
147
+ interface RequestWorkflowOptions {
148
+ /** Fixed coin price of a started attempt, including failed or canceled work. */
149
+ estimateCoins: number;
150
+ /** Offer configured personal Claude/ChatGPT funding on the same quote. Defaults off. */
151
+ allowExternal?: boolean;
152
+ workflowId: string;
153
+ offeringId: string;
154
+ input: RuntimeWorkflowInput;
155
+ /** Reuse for the exact same operation after sign-in, cancellation or reload. */
156
+ idempotencyKey?: string;
157
+ /** Approval waiting time only. A timeout does not cancel an accepted request. */
158
+ timeoutMs?: number;
159
+ }
160
+ interface RuntimeWorkflowAuthorization extends RuntimeGenerationBilling {
161
+ status: 'authorized' | 'canceled' | 'expired' | 'failed' | 'pending';
162
+ generationId?: string;
163
+ funding?: RuntimeGenerationFunding;
164
+ error?: string;
165
+ }
166
+ /** Available models and pricing; the confirmation always uses a fresh server quote. */
167
+ declare function getGenerationModels(): Promise<RuntimeGenerationModels>;
168
+ /** Server-owned estimates, reservation maxima and availability. Confirmation seals the quote. */
169
+ declare function getWorkflowOfferings(workflowId: string): Promise<RuntimeWorkflowOfferings>;
170
+ /** Recover an operation by ID after a reload. Scoped to this player AND this game. */
171
+ declare function getGeneration(generationId: string): Promise<RuntimeGenerationView>;
172
+ /** Resume waiting for execution. A terminal result can still carry pending billing; read getGeneration() for settlement. */
173
+ declare function waitForGeneration(generationId: string, timeoutMs?: number): Promise<RuntimeGenerationResult>;
174
+ /**
175
+ * Call directly from a click handler. The trusted popup is reserved BEFORE any
176
+ * asynchronous work, so standalone games retain the browser's user gesture.
177
+ * The player approves on Genex. Cancellation resolves normally; only API state
178
+ * can return an output. Store generationId to recover a pending result on boot.
179
+ */
180
+ declare function generate(options: GenerateOptions): Promise<RuntimeGenerationResult>;
181
+ /**
182
+ * Call directly from the player's click, before awaiting anything. Returns when
183
+ * Genex authorizes the entire workflow, so the game can enqueue its executor.
184
+ * Personal-provider instructions stay open after authorization. Store the ID;
185
+ * getGeneration()/waitForGeneration() recover the eventual result without a new charge.
186
+ */
187
+ declare function requestWorkflow(options: RequestWorkflowOptions): Promise<RuntimeWorkflowAuthorization>;
188
+ /** Reopen a saved approval without repricing or submitting a new request. Call directly from a click. */
189
+ declare function resumeWorkflow(generationId: string, options?: {
190
+ timeoutMs?: number;
191
+ }): Promise<RuntimeWorkflowAuthorization>;
192
+
121
193
  interface EmbedConfig {
122
194
  /** This game's own slug (GENEX.slug) — identifies the project to /play/authorize. */
123
195
  slug: string;
@@ -258,7 +330,9 @@ declare function getColyseusUrls(): string[];
258
330
  * session ends up blocked. THE gate for /state saves and any account-bound
259
331
  * feature. In a guest session this stays PENDING (it is not rejected —
260
332
  * a live upgrade resolves it), so never gate scene boot or connect() on it:
261
- * use waitForPlayer() for those.
333
+ * use waitForPlayer() for those. Call initEmbed() FIRST: parked before it,
334
+ * this logs one console line and, unattended, ends `blocked` after the
335
+ * handshake budget instead of hanging.
262
336
  */
263
337
  declare function waitForAuth(): Promise<{
264
338
  user: EmbedUser;
@@ -266,7 +340,10 @@ declare function waitForAuth(): Promise<{
266
340
  /**
267
341
  * Resolves with the current player — guest OR signed-in — as soon as either
268
342
  * identity exists; rejects only if the session ends up blocked. THE gate for
269
- * multiplayer connect() and player-name UI.
343
+ * multiplayer connect() and player-name UI. Call initEmbed() FIRST — and draw
344
+ * a frame before awaiting this: parked before init, it logs one console line
345
+ * and, unattended, ends `blocked` after the handshake budget instead of
346
+ * leaving the page black.
270
347
  */
271
348
  declare function waitForPlayer(): Promise<{
272
349
  user: EmbedUser;
@@ -352,4 +429,4 @@ declare function __resetForTests(overrides?: {
352
429
  heartbeatIntervalMs?: number;
353
430
  }): void;
354
431
 
355
- export { type AuthState, type EmbedConfig, type EmbedEvent, type EmbedUser, type Entitlement, type Leaderboard, type LeaderboardEntry, type PlayerStateResult, type PurchaseResult, type PurchaseStatus, type SaveStateResult, type ShopItem, type SubmitScoreResult, type WorldStateResult, __resetForTests, _stashTicketFromUrl, buy, consumeEntitlement, getAuthState, getColyseusAuth, getColyseusUrls, getEmbedToken, getEntitlements, getLeaderboard, getShop, getUser, initEmbed, isEmbedded, loadPlayerState, loadWorldState, on, savePlayerState, saveWorldState, submitScore, waitForAuth, waitForPlayer };
432
+ export { type AuthState, type EmbedConfig, type EmbedEvent, type EmbedUser, type Entitlement, type GenerateOptions, type Leaderboard, type LeaderboardEntry, type PlayerStateResult, type PurchaseResult, type PurchaseStatus, type RequestWorkflowOptions, type RuntimeGenerationResult, type RuntimeWorkflowAuthorization, type SaveStateResult, type ShopItem, type SubmitScoreResult, type WorldStateResult, __resetForTests, _stashTicketFromUrl, buy, consumeEntitlement, generate, getAuthState, getColyseusAuth, getColyseusUrls, getEmbedToken, getEntitlements, getGeneration, getGenerationModels, getLeaderboard, getShop, getUser, getWorkflowOfferings, initEmbed, isEmbedded, loadPlayerState, loadWorldState, on, requestWorkflow, resumeWorkflow, savePlayerState, saveWorldState, submitScore, waitForAuth, waitForGeneration, waitForPlayer };
package/dist/index.js CHANGED
@@ -3,46 +3,61 @@ import {
3
3
  _stashTicketFromUrl,
4
4
  buy,
5
5
  consumeEntitlement,
6
+ generate,
6
7
  getAuthState,
7
8
  getColyseusAuth,
8
9
  getColyseusUrls,
9
10
  getEmbedToken,
10
11
  getEntitlements,
12
+ getGeneration,
13
+ getGenerationModels,
11
14
  getLeaderboard,
12
15
  getShop,
13
16
  getUser,
17
+ getWorkflowOfferings,
14
18
  initEmbed,
15
19
  isEmbedded,
16
20
  loadPlayerState,
17
21
  loadWorldState,
18
22
  on,
23
+ requestWorkflow,
24
+ resumeWorkflow,
19
25
  savePlayerState,
20
26
  saveWorldState,
21
27
  submitScore,
22
28
  waitForAuth,
29
+ waitForGeneration,
23
30
  waitForPlayer
24
- } from "./chunk-ALBFRX3L.js";
31
+ } from "./chunk-WJ4KFQLC.js";
32
+ import "./chunk-Q476GJHI.js";
25
33
  export {
26
34
  __resetForTests,
27
35
  _stashTicketFromUrl,
28
36
  buy,
29
37
  consumeEntitlement,
38
+ generate,
30
39
  getAuthState,
31
40
  getColyseusAuth,
32
41
  getColyseusUrls,
33
42
  getEmbedToken,
34
43
  getEntitlements,
44
+ getGeneration,
45
+ getGenerationModels,
35
46
  getLeaderboard,
36
47
  getShop,
37
48
  getUser,
49
+ getWorkflowOfferings,
38
50
  initEmbed,
39
51
  isEmbedded,
40
52
  loadPlayerState,
41
53
  loadWorldState,
42
54
  on,
55
+ requestWorkflow,
56
+ resumeWorkflow,
43
57
  savePlayerState,
44
58
  saveWorldState,
45
59
  submitScore,
46
60
  waitForAuth,
61
+ waitForGeneration,
47
62
  waitForPlayer
48
63
  };
package/dist/sentry.js CHANGED
@@ -2,7 +2,8 @@ import {
2
2
  _stashTicketFromUrl,
3
3
  getUser,
4
4
  on
5
- } from "./chunk-ALBFRX3L.js";
5
+ } from "./chunk-WJ4KFQLC.js";
6
+ import "./chunk-Q476GJHI.js";
6
7
 
7
8
  // src/sentry.ts
8
9
  import * as Sentry from "@sentry/browser";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genex-ai/embed-sdk",
3
- "version": "0.16.0",
4
- "description": "Player identity + durable game state for genex games \u2014 signed-in or guest play, per-player save slots, shared world state, and soft-trust leaderboards.",
3
+ "version": "0.21.0",
4
+ "description": "Player identity + durable game state for genex games — signed-in or guest play, per-player save slots, shared world state, and soft-trust leaderboards.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -15,6 +15,12 @@
15
15
  "@genex-ai/source": "./src/sentry.ts",
16
16
  "types": "./dist/sentry.d.ts",
17
17
  "import": "./dist/sentry.js"
18
+ },
19
+ "./development": {
20
+ "types": "./dist/development.d.ts",
21
+ "browser": null,
22
+ "@genex-ai/source": "./src/development.ts",
23
+ "import": "./dist/development.js"
18
24
  }
19
25
  },
20
26
  "files": [