@opengeni/xai-subscription 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,1245 @@
1
+ import {
2
+ XAI_CLIENT_MODE,
3
+ XAI_CLIENT_VERSION,
4
+ XAI_DEVICE_AUTHORIZATION_URL,
5
+ XAI_DEVICE_CODE_GRANT_TYPE,
6
+ XAI_IMAGE_MODEL,
7
+ XAI_IMAGE_REQUEST_TIMEOUT_MS,
8
+ XAI_OAUTH_CLIENT_ID,
9
+ XAI_OAUTH_ISSUER,
10
+ XAI_OAUTH_OPERATION_TIMEOUT_MS,
11
+ XAI_OAUTH_SCOPES,
12
+ XAI_PUBLIC_API_BASE_URL,
13
+ XAI_REFRESH_FALLBACK_MS,
14
+ XAI_REFRESH_WINDOW_MS,
15
+ XAI_RESPONSE_SDK_OUTER_TIMEOUT_MS,
16
+ XAI_SUBSCRIPTION_AUTO_COMPACTION_PERCENT,
17
+ XAI_SUBSCRIPTION_EFFECTIVE_CONTEXT_PERCENT,
18
+ XAI_SUBSCRIPTION_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
19
+ XAI_SUBSCRIPTION_MODEL_CONTEXT_WINDOW_TOKENS,
20
+ XAI_SUBSCRIPTION_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
21
+ XAI_SUBSCRIPTION_MODEL_ID_PREFIX,
22
+ XAI_SUBSCRIPTION_MODEL_SLUGS,
23
+ XAI_SUBSCRIPTION_PROVIDER_ID,
24
+ XAI_SUBSCRIPTION_PROXY_BASE_URL,
25
+ XAI_TOKEN_AUTH_HEADER_VALUE,
26
+ XAI_TOKEN_URL,
27
+ XAI_USERINFO_URL,
28
+ XAI_VIDEO_DOWNLOAD_TIMEOUT_MS,
29
+ XAI_VIDEO_GENERATION_TIMEOUT_MS,
30
+ XAI_VIDEO_MODEL,
31
+ XAI_VIDEO_POLL_INTERVAL_MS,
32
+ XAI_VIDEO_POLL_REQUEST_TIMEOUT_MS,
33
+ XAI_VIDEO_START_TIMEOUT_MS
34
+ } from "./chunk-JEFGIUGN.js";
35
+
36
+ // src/errors.ts
37
+ var XaiSubscriptionError = class extends Error {
38
+ constructor(kind, message, status) {
39
+ super(message);
40
+ this.kind = kind;
41
+ this.status = status;
42
+ this.name = "XaiSubscriptionError";
43
+ }
44
+ };
45
+ var XaiSubscriptionReloginRequired = class extends XaiSubscriptionError {
46
+ constructor(message = "The SuperGrok connection is no longer valid. Reconnect the account.") {
47
+ super("relogin_required", message);
48
+ this.name = "XaiSubscriptionReloginRequired";
49
+ }
50
+ };
51
+ var XaiSubscriptionTransientError = class extends XaiSubscriptionError {
52
+ constructor(message, status) {
53
+ super("transient", message, status);
54
+ this.name = "XaiSubscriptionTransientError";
55
+ }
56
+ };
57
+
58
+ // src/bounded-operation.ts
59
+ async function runBoundedXaiOperation(operation, timeoutMs) {
60
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
61
+ throw new Error("xAI operation timeout must be positive");
62
+ }
63
+ const controller = new AbortController();
64
+ let timedOut = false;
65
+ let timeout;
66
+ const work = operation(controller.signal).then(
67
+ (value) => ({ ok: true, value }),
68
+ () => ({
69
+ ok: false,
70
+ reason: timedOut || controller.signal.aborted ? "timeout" : "network_error"
71
+ })
72
+ );
73
+ const deadline = new Promise((resolve) => {
74
+ timeout = setTimeout(() => {
75
+ timedOut = true;
76
+ controller.abort();
77
+ resolve({ ok: false, reason: "timeout" });
78
+ }, timeoutMs);
79
+ });
80
+ try {
81
+ return await Promise.race([work, deadline]);
82
+ } finally {
83
+ if (timeout) clearTimeout(timeout);
84
+ }
85
+ }
86
+
87
+ // src/oauth.ts
88
+ import {
89
+ OAUTH_MAX_RESPONSE_BYTES,
90
+ readResponseJsonBounded,
91
+ validateHttpUrl
92
+ } from "@opengeni/network";
93
+ function oauthHeaders() {
94
+ return new Headers({
95
+ accept: "application/json",
96
+ "content-type": "application/x-www-form-urlencoded",
97
+ "user-agent": `opengeni/${XAI_CLIENT_VERSION}`,
98
+ "x-grok-client-version": XAI_CLIENT_VERSION,
99
+ "x-grok-client-surface": "headless"
100
+ });
101
+ }
102
+ function positiveSeconds(value, fallback) {
103
+ const number = Number(value);
104
+ return Number.isFinite(number) && number > 0 ? Math.ceil(number) : fallback;
105
+ }
106
+ function requireNonEmptyString(value, field) {
107
+ if (typeof value !== "string" || value.length === 0) {
108
+ throw new XaiSubscriptionError("invalid_response", `xAI OAuth response is missing ${field}`);
109
+ }
110
+ return value;
111
+ }
112
+ function nullableString(value) {
113
+ return typeof value === "string" && value.length > 0 ? value : null;
114
+ }
115
+ function validateUserCode(userCode) {
116
+ if (![...userCode].every((char) => /[A-Za-z0-9-]/.test(char))) {
117
+ throw new XaiSubscriptionError("invalid_response", "xAI returned an invalid user code");
118
+ }
119
+ }
120
+ function validateVerificationUri(uri) {
121
+ try {
122
+ return validateHttpUrl(uri, {
123
+ allowLoopbackHttp: true,
124
+ label: "xAI device verification"
125
+ });
126
+ } catch {
127
+ throw new XaiSubscriptionError("invalid_response", "xAI returned an invalid verification URL");
128
+ }
129
+ }
130
+ async function boundedFetchJson(label, input, init, options) {
131
+ const fetchImpl = options.fetch ?? fetch;
132
+ const timeoutMs = options.timeoutMs ?? XAI_OAUTH_OPERATION_TIMEOUT_MS;
133
+ const fetched = await runBoundedXaiOperation(async (signal) => {
134
+ const response = await fetchImpl(input, { ...init, signal });
135
+ const body = await readResponseJsonBounded(
136
+ response,
137
+ OAUTH_MAX_RESPONSE_BYTES,
138
+ label,
139
+ { signal }
140
+ );
141
+ return { response, body };
142
+ }, timeoutMs);
143
+ if (!fetched.ok) {
144
+ throw new XaiSubscriptionTransientError(`xAI ${label} ${fetched.reason}`);
145
+ }
146
+ return fetched.value;
147
+ }
148
+ async function requestXaiDeviceCode(options = {}) {
149
+ const { response, body } = await boundedFetchJson(
150
+ "device code request",
151
+ options.deviceAuthorizationUrl ?? XAI_DEVICE_AUTHORIZATION_URL,
152
+ {
153
+ method: "POST",
154
+ headers: oauthHeaders(),
155
+ body: new URLSearchParams({
156
+ client_id: options.clientId ?? XAI_OAUTH_CLIENT_ID,
157
+ scope: XAI_OAUTH_SCOPES.join(" "),
158
+ referrer: "opengeni"
159
+ }).toString()
160
+ },
161
+ options
162
+ );
163
+ if (response.status === 404) {
164
+ throw new XaiSubscriptionError(
165
+ "not_enabled",
166
+ "SuperGrok device-code login is not enabled by xAI",
167
+ 404
168
+ );
169
+ }
170
+ if (!response.ok) {
171
+ throw new XaiSubscriptionTransientError(
172
+ `xAI device code request failed (${response.status})`,
173
+ response.status
174
+ );
175
+ }
176
+ const deviceCode = requireNonEmptyString(body.device_code, "device_code");
177
+ const userCode = requireNonEmptyString(body.user_code, "user_code");
178
+ const verificationUri = validateVerificationUri(
179
+ requireNonEmptyString(body.verification_uri, "verification_uri")
180
+ );
181
+ const verificationUriComplete = nullableString(body.verification_uri_complete);
182
+ validateUserCode(userCode);
183
+ return {
184
+ deviceCode,
185
+ userCode,
186
+ verificationUri,
187
+ verificationUriComplete: verificationUriComplete ? validateVerificationUri(verificationUriComplete) : null,
188
+ expiresInSeconds: positiveSeconds(body.expires_in, 5 * 60),
189
+ intervalSeconds: Math.max(1, positiveSeconds(body.interval, 5))
190
+ };
191
+ }
192
+ async function pollXaiDeviceCode(input, options = {}) {
193
+ const { response, body } = await boundedFetchJson(
194
+ "device token exchange",
195
+ options.tokenUrl ?? XAI_TOKEN_URL,
196
+ {
197
+ method: "POST",
198
+ headers: oauthHeaders(),
199
+ body: new URLSearchParams({
200
+ grant_type: XAI_DEVICE_CODE_GRANT_TYPE,
201
+ client_id: options.clientId ?? XAI_OAUTH_CLIENT_ID,
202
+ device_code: input.deviceCode
203
+ }).toString()
204
+ },
205
+ options
206
+ );
207
+ if (response.ok) {
208
+ return { status: "authorized", tokens: parseTokenResponse(body, true) };
209
+ }
210
+ const code = nullableString(body.error);
211
+ if (code === "authorization_pending") {
212
+ return { status: "pending", intervalSeconds: Math.max(1, input.intervalSeconds) };
213
+ }
214
+ if (code === "slow_down") {
215
+ return { status: "slow_down", intervalSeconds: Math.max(1, input.intervalSeconds) + 5 };
216
+ }
217
+ if (code === "access_denied" || code === "authorization_denied") {
218
+ return { status: "denied" };
219
+ }
220
+ if (code === "expired_token") {
221
+ return { status: "expired" };
222
+ }
223
+ throw new XaiSubscriptionTransientError(
224
+ `xAI device token exchange failed (${response.status})`,
225
+ response.status
226
+ );
227
+ }
228
+ async function refreshXaiToken(refreshToken, options = {}) {
229
+ const { response, body } = await boundedFetchJson(
230
+ "token refresh",
231
+ options.tokenUrl ?? XAI_TOKEN_URL,
232
+ {
233
+ method: "POST",
234
+ headers: oauthHeaders(),
235
+ body: new URLSearchParams({
236
+ grant_type: "refresh_token",
237
+ refresh_token: refreshToken,
238
+ client_id: options.clientId ?? XAI_OAUTH_CLIENT_ID
239
+ }).toString()
240
+ },
241
+ options
242
+ );
243
+ if (!response.ok) {
244
+ const code = nullableString(body.error);
245
+ if (response.status === 401 || code === "invalid_grant" || code === "invalid_token" || code === "access_denied") {
246
+ throw new XaiSubscriptionReloginRequired();
247
+ }
248
+ throw new XaiSubscriptionTransientError(
249
+ `xAI token refresh failed (${response.status})`,
250
+ response.status
251
+ );
252
+ }
253
+ const parsed = parseTokenResponse(body, false);
254
+ return {
255
+ ...parsed,
256
+ refreshToken: parsed.refreshToken || refreshToken
257
+ };
258
+ }
259
+ function parseTokenResponse(body, requireRefresh) {
260
+ const accessToken = requireNonEmptyString(body.access_token, "access_token");
261
+ const refreshToken = nullableString(body.refresh_token);
262
+ if (requireRefresh && !refreshToken) {
263
+ throw new XaiSubscriptionError(
264
+ "invalid_response",
265
+ "xAI OAuth response is missing refresh_token"
266
+ );
267
+ }
268
+ return {
269
+ accessToken,
270
+ refreshToken: refreshToken ?? "",
271
+ idToken: nullableString(body.id_token),
272
+ tokenType: nullableString(body.token_type),
273
+ scope: nullableString(body.scope),
274
+ expiresInSeconds: positiveSeconds(body.expires_in, 60 * 60)
275
+ };
276
+ }
277
+ async function fetchXaiVerifiedIdentity(accessToken, options = {}) {
278
+ const headers = new Headers({
279
+ accept: "application/json",
280
+ authorization: `Bearer ${accessToken}`,
281
+ "user-agent": `opengeni/${XAI_CLIENT_VERSION}`,
282
+ "x-grok-client-version": XAI_CLIENT_VERSION
283
+ });
284
+ const { response, body } = await boundedFetchJson(
285
+ "userinfo request",
286
+ options.userinfoUrl ?? XAI_USERINFO_URL,
287
+ { method: "GET", headers },
288
+ options
289
+ );
290
+ if (response.status === 401 || response.status === 403) {
291
+ throw new XaiSubscriptionReloginRequired();
292
+ }
293
+ if (!response.ok) {
294
+ throw new XaiSubscriptionTransientError(
295
+ `xAI userinfo request failed (${response.status})`,
296
+ response.status
297
+ );
298
+ }
299
+ return {
300
+ subject: requireNonEmptyString(body.sub, "userinfo sub"),
301
+ email: nullableString(body.email),
302
+ emailVerified: typeof body.email_verified === "boolean" ? body.email_verified : null,
303
+ name: nullableString(body.name)
304
+ };
305
+ }
306
+ function decodeXaiJwtPayload(jwt) {
307
+ const payload = jwt.split(".")[1];
308
+ if (!payload) return null;
309
+ try {
310
+ return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
311
+ } catch {
312
+ return null;
313
+ }
314
+ }
315
+ function xaiAccessTokenExpiry(accessToken) {
316
+ const payload = decodeXaiJwtPayload(accessToken);
317
+ return typeof payload?.exp === "number" ? new Date(payload.exp * 1e3) : null;
318
+ }
319
+
320
+ // src/request-context.ts
321
+ import { AsyncLocalStorage } from "async_hooks";
322
+ var xaiSubscriptionRequestStorage = new AsyncLocalStorage();
323
+
324
+ // src/normalize.ts
325
+ var XAI_INTERNAL_MODEL_HANDOFF_PREFIX = "supergrok/";
326
+ function normalizeXaiSubscriptionRequestBody(value, resolveModel, hostedSearch) {
327
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
328
+ throw new Error("SuperGrok subscription request body must be a JSON object");
329
+ }
330
+ const body = { ...value };
331
+ if (typeof body.model !== "string") {
332
+ throw new Error("SuperGrok subscription request is missing model");
333
+ }
334
+ const candidate = body.model.startsWith(XAI_INTERNAL_MODEL_HANDOFF_PREFIX) ? body.model.slice(XAI_INTERNAL_MODEL_HANDOFF_PREFIX.length) : body.model;
335
+ body.model = resolveModel(candidate);
336
+ body.store = false;
337
+ const include = Array.isArray(body.include) ? body.include.filter((entry) => typeof entry === "string") : [];
338
+ if (!include.includes("reasoning.encrypted_content")) {
339
+ include.push("reasoning.encrypted_content");
340
+ }
341
+ body.include = include;
342
+ const tools = Array.isArray(body.tools) ? body.tools.map(normalizeXaiSubscriptionTool) : [];
343
+ appendHostedTool(tools, "web_search", hostedSearch?.webSearch);
344
+ appendHostedTool(tools, "x_search", hostedSearch?.xSearch);
345
+ if (tools.length > 0) body.tools = tools;
346
+ return body;
347
+ }
348
+ function normalizeXaiSubscriptionTool(tool) {
349
+ if (!tool || typeof tool !== "object" || Array.isArray(tool)) return tool;
350
+ const record2 = tool;
351
+ if (record2.type !== "web_search") return tool;
352
+ const { search_context_size: _unsupported, ...supported } = record2;
353
+ return supported;
354
+ }
355
+ function appendHostedTool(tools, type, options) {
356
+ if (!options) return;
357
+ if (tools.some(
358
+ (tool) => tool && typeof tool === "object" && !Array.isArray(tool) && tool.type === type
359
+ )) {
360
+ return;
361
+ }
362
+ tools.push(normalizeXaiSubscriptionTool(options === true ? { type } : { type, ...options }));
363
+ }
364
+ function normalizeXaiResponseEventJson(value) {
365
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
366
+ return { value, finalContextTokens: null };
367
+ }
368
+ const event = value;
369
+ const response = event.response && typeof event.response === "object" && !Array.isArray(event.response) ? { ...event.response } : null;
370
+ if (!response) return { value, finalContextTokens: null };
371
+ let changed = false;
372
+ if (Array.isArray(response.tools)) {
373
+ const filtered = response.tools.filter((tool) => isOpenAiSdkResponseTool(tool));
374
+ if (filtered.length !== response.tools.length) {
375
+ response.tools = filtered;
376
+ changed = true;
377
+ }
378
+ }
379
+ let finalContextTokens = null;
380
+ if (event.type === "response.completed" || event.type === "response.incomplete") {
381
+ const usage = response.usage && typeof response.usage === "object" && !Array.isArray(response.usage) ? { ...response.usage } : null;
382
+ const context = usage?.context_details && typeof usage.context_details === "object" && !Array.isArray(usage.context_details) ? usage.context_details : null;
383
+ const inputTokens = finiteNonNegativeInteger(context?.input_tokens);
384
+ const outputTokens = finiteNonNegativeInteger(context?.output_tokens);
385
+ if (usage && inputTokens !== null && outputTokens !== null) {
386
+ finalContextTokens = inputTokens + outputTokens;
387
+ usage.total_tokens = finalContextTokens;
388
+ response.usage = usage;
389
+ changed = true;
390
+ }
391
+ }
392
+ return {
393
+ value: changed ? { ...event, response } : value,
394
+ finalContextTokens
395
+ };
396
+ }
397
+ function finiteNonNegativeInteger(value) {
398
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
399
+ }
400
+ function isOpenAiSdkResponseTool(tool) {
401
+ if (!tool || typeof tool !== "object" || Array.isArray(tool)) return false;
402
+ const type = tool.type;
403
+ return typeof type === "string" && [
404
+ "function",
405
+ "file_search",
406
+ "computer_use_preview",
407
+ "computer",
408
+ "web_search",
409
+ "web_search_preview",
410
+ "code_interpreter",
411
+ "image_generation",
412
+ "local_shell",
413
+ "shell",
414
+ "apply_patch",
415
+ "mcp",
416
+ "custom"
417
+ ].includes(type);
418
+ }
419
+
420
+ // src/fetch.ts
421
+ import { randomUUID } from "crypto";
422
+ var REPLAYABLE_REQUEST_BODY_FACTORY = Symbol.for("opengeni.replayable-request-body-factory");
423
+ var XAI_SUBSCRIPTION_TRANSPORT_ERROR_HEADER = "x-opengeni-xai-subscription-transport-error";
424
+ var XAI_SUBSCRIPTION_REQUEST_BODY_NORMALIZED_HEADER = "x-opengeni-xai-subscription-body-normalized";
425
+ var XAI_SUBSCRIPTION_REQUEST_MODEL_HEADER = "x-opengeni-xai-subscription-model";
426
+ var XAI_SUBSCRIPTION_REQUEST_ID_HEADER = "x-opengeni-xai-subscription-request-id";
427
+ var MAX_ERROR_BODY_BYTES = 64 * 1024;
428
+ function xaiSubscriptionFetch(base) {
429
+ return async (input, init) => {
430
+ const context = xaiSubscriptionRequestStorage.getStore();
431
+ if (!context) throw new Error("SuperGrok subscription request context is unavailable");
432
+ const originalUrl = input instanceof Request ? input.url : String(input);
433
+ const url = new URL(originalUrl);
434
+ if (!url.pathname.endsWith("/responses")) {
435
+ throw new Error("SuperGrok subscription models require the Responses API");
436
+ }
437
+ const originalHeaders = new Headers(input instanceof Request ? input.headers : void 0);
438
+ if (init?.headers) {
439
+ new Headers(init.headers).forEach((value, key) => originalHeaders.set(key, value));
440
+ }
441
+ const normalized = originalHeaders.get(XAI_SUBSCRIPTION_REQUEST_BODY_NORMALIZED_HEADER) === "1";
442
+ const requestId = originalHeaders.get(XAI_SUBSCRIPTION_REQUEST_ID_HEADER) ?? randomUUID();
443
+ const handedOffModel = originalHeaders.get(XAI_SUBSCRIPTION_REQUEST_MODEL_HEADER);
444
+ originalHeaders.delete(XAI_SUBSCRIPTION_REQUEST_BODY_NORMALIZED_HEADER);
445
+ originalHeaders.delete(XAI_SUBSCRIPTION_REQUEST_ID_HEADER);
446
+ originalHeaders.delete(XAI_SUBSCRIPTION_REQUEST_MODEL_HEADER);
447
+ const replayableBodyFactory = init?.[REPLAYABLE_REQUEST_BODY_FACTORY];
448
+ const body = await requestBodyText(input, init, replayableBodyFactory);
449
+ const parsed = body ? JSON.parse(body) : {};
450
+ const normalizedBody = normalized ? parsed : normalizeXaiSubscriptionRequestBody(parsed, context.resolveModel, context.hostedSearch);
451
+ const model = handedOffModel ?? (typeof normalizedBody.model === "string" ? normalizedBody.model : "grok-4.6");
452
+ const send = async (refresh) => {
453
+ const token = refresh ? await context.refresh() : await context.getToken();
454
+ const headers = new Headers(originalHeaders);
455
+ headers.set("authorization", `Bearer ${token.accessToken}`);
456
+ headers.set("content-type", "application/json");
457
+ headers.set("accept", "text/event-stream");
458
+ headers.set("user-agent", `opengeni/${context.clientVersion || XAI_CLIENT_VERSION}`);
459
+ headers.set("x-grok-client-version", context.clientVersion || XAI_CLIENT_VERSION);
460
+ headers.set("x-grok-client-identifier", "opengeni");
461
+ headers.set("x-grok-client-mode", XAI_CLIENT_MODE);
462
+ headers.set("x-authenticateresponse", "authenticate-response");
463
+ headers.set("x-xai-token-auth", XAI_TOKEN_AUTH_HEADER_VALUE);
464
+ headers.set("x-userid", token.userId);
465
+ headers.set("x-grok-user-id", token.userId);
466
+ headers.set("x-grok-conv-id", context.sessionId);
467
+ headers.set("x-grok-session-id", context.sessionId);
468
+ headers.set("x-grok-req-id", requestId);
469
+ headers.set("x-grok-agent-id", context.turnId);
470
+ headers.set("x-grok-model-override", model);
471
+ return await base(url, {
472
+ ...init,
473
+ method: init?.method ?? (input instanceof Request ? input.method : "POST"),
474
+ headers,
475
+ body: JSON.stringify(normalizedBody)
476
+ });
477
+ };
478
+ let response = await send(false);
479
+ if (response.status === 401) {
480
+ await response.body?.cancel().catch(() => void 0);
481
+ response = await send(true);
482
+ }
483
+ return await normalizeResponse(response, context.onFinalContextUsage);
484
+ };
485
+ }
486
+ async function requestBodyText(input, init, replayableBodyFactory) {
487
+ if (typeof init?.body === "string") return init.body;
488
+ if (input instanceof Request) return await input.clone().text();
489
+ if (replayableBodyFactory) return await new Response(replayableBodyFactory()).text();
490
+ if (init?.body === void 0 || init.body === null) return "";
491
+ throw new Error("SuperGrok subscription request body must be replayable JSON text");
492
+ }
493
+ async function normalizeResponse(response, onFinalContextUsage) {
494
+ if (!response.ok) {
495
+ const bytes = new Uint8Array(await response.arrayBuffer());
496
+ const bounded = bytes.byteLength > MAX_ERROR_BODY_BYTES ? bytes.slice(0, MAX_ERROR_BODY_BYTES) : bytes;
497
+ const headers = new Headers(response.headers);
498
+ headers.set(XAI_SUBSCRIPTION_TRANSPORT_ERROR_HEADER, "1");
499
+ return new Response(bounded, {
500
+ status: response.status,
501
+ statusText: response.statusText,
502
+ headers
503
+ });
504
+ }
505
+ if (!response.body) return response;
506
+ const contentType = response.headers.get("content-type") ?? "";
507
+ if (!contentType.includes("text/event-stream")) {
508
+ const value = await response.json();
509
+ const normalized = normalizeXaiResponseEventJson({
510
+ type: "response.completed",
511
+ response: value
512
+ });
513
+ emitContextUsage(normalized.value, normalized.finalContextTokens, onFinalContextUsage);
514
+ const responseValue = normalized.value && typeof normalized.value === "object" && !Array.isArray(normalized.value) ? normalized.value.response : value;
515
+ return Response.json(responseValue, {
516
+ status: response.status,
517
+ statusText: response.statusText,
518
+ headers: response.headers
519
+ });
520
+ }
521
+ const reader = response.body.getReader();
522
+ const decoder = new TextDecoder();
523
+ const encoder = new TextEncoder();
524
+ let pending = "";
525
+ const body = new ReadableStream({
526
+ async pull(controller) {
527
+ const chunk = await reader.read();
528
+ pending += decoder.decode(chunk.value, { stream: !chunk.done });
529
+ const parts = pending.split("\n\n");
530
+ pending = parts.pop() ?? "";
531
+ for (const part of parts)
532
+ controller.enqueue(encoder.encode(`${normalizeSseEvent(part, onFinalContextUsage)}
533
+
534
+ `));
535
+ if (chunk.done) {
536
+ if (pending)
537
+ controller.enqueue(encoder.encode(normalizeSseEvent(pending, onFinalContextUsage)));
538
+ controller.close();
539
+ }
540
+ },
541
+ cancel(reason) {
542
+ return reader.cancel(reason);
543
+ }
544
+ });
545
+ return new Response(body, {
546
+ status: response.status,
547
+ statusText: response.statusText,
548
+ headers: response.headers
549
+ });
550
+ }
551
+ function normalizeSseEvent(block, onFinalContextUsage) {
552
+ const lines = block.split("\n");
553
+ return lines.map((line) => {
554
+ if (!line.startsWith("data:")) return line;
555
+ const data = line.slice(5).trimStart();
556
+ if (!data || data === "[DONE]") return line;
557
+ try {
558
+ const normalized = normalizeXaiResponseEventJson(JSON.parse(data));
559
+ emitContextUsage(normalized.value, normalized.finalContextTokens, onFinalContextUsage);
560
+ return `data: ${JSON.stringify(normalized.value)}`;
561
+ } catch {
562
+ return line;
563
+ }
564
+ }).join("\n");
565
+ }
566
+ function emitContextUsage(value, finalContextTokens, sink) {
567
+ if (finalContextTokens === null || !sink) return;
568
+ const response = value && typeof value === "object" && !Array.isArray(value) ? value.response : null;
569
+ const usage = response && typeof response === "object" && !Array.isArray(response) ? response.usage : null;
570
+ const context = usage && typeof usage === "object" && !Array.isArray(usage) ? usage.context_details : null;
571
+ if (!context || typeof context !== "object" || Array.isArray(context)) return;
572
+ const inputTokens = Number(context.input_tokens);
573
+ const outputTokens = Number(context.output_tokens);
574
+ if (!Number.isSafeInteger(inputTokens) || !Number.isSafeInteger(outputTokens)) return;
575
+ try {
576
+ sink({ inputTokens, outputTokens, totalTokens: finalContextTokens });
577
+ } catch {
578
+ }
579
+ }
580
+ function isXaiSubscriptionTransportError(error) {
581
+ let current = error;
582
+ for (let depth = 0; depth < 6 && current && typeof current === "object"; depth += 1) {
583
+ const value = current;
584
+ const headers = value.headers;
585
+ if (headers && typeof headers === "object" && typeof headers.get === "function" && headers.get(XAI_SUBSCRIPTION_TRANSPORT_ERROR_HEADER) === "1") {
586
+ return true;
587
+ }
588
+ current = value.cause;
589
+ }
590
+ return false;
591
+ }
592
+
593
+ // src/proxy.ts
594
+ import { OAUTH_MAX_RESPONSE_BYTES as OAUTH_MAX_RESPONSE_BYTES2, pinnedFetch, readResponseJsonBounded as readResponseJsonBounded2 } from "@opengeni/network";
595
+ var defaultProxyFetch = async (input, init) => await pinnedFetch(
596
+ input,
597
+ init,
598
+ { environment: "production", integrationsAllowPrivateNetworkTargets: false },
599
+ { label: "xAI subscription proxy", requireHttpsOutsideLocalTest: true }
600
+ );
601
+ async function fetchXaiProxyJson(input) {
602
+ const fetchImpl = input.fetch ?? defaultProxyFetch;
603
+ const timeoutMs = input.timeoutMs ?? 15e3;
604
+ const maxBytes = input.maxBytes ?? OAUTH_MAX_RESPONSE_BYTES2;
605
+ const url = `${(input.baseUrl ?? XAI_SUBSCRIPTION_PROXY_BASE_URL).replace(/\/+$/, "")}/${input.path.replace(/^\/+/, "")}`;
606
+ const request = async (token, signal) => {
607
+ const headers = xaiSubscriptionProxyHeaders(token, input.context.clientVersion);
608
+ return await fetchImpl(url, { method: "GET", redirect: "error", headers, signal });
609
+ };
610
+ const fetched = await runBoundedXaiOperation(async (signal) => {
611
+ let response = await request(await input.context.getToken(), signal);
612
+ if (response.status === 401) {
613
+ await response.body?.cancel().catch(() => void 0);
614
+ response = await request(await input.context.refresh(), signal);
615
+ }
616
+ if (response.status === 401 || response.status === 403) {
617
+ await response.body?.cancel().catch(() => void 0);
618
+ throw new XaiSubscriptionReloginRequired();
619
+ }
620
+ if (!response.ok) {
621
+ await response.body?.cancel().catch(() => void 0);
622
+ throw new XaiSubscriptionTransientError(
623
+ `xAI ${input.label} failed (${response.status})`,
624
+ response.status
625
+ );
626
+ }
627
+ return await readResponseJsonBounded2(response, maxBytes, `xAI ${input.label}`, { signal });
628
+ }, timeoutMs);
629
+ if (!fetched.ok) {
630
+ throw new XaiSubscriptionTransientError(`xAI ${input.label} ${fetched.reason}`);
631
+ }
632
+ return fetched.value;
633
+ }
634
+ function xaiSubscriptionProxyHeaders(token, clientVersion = XAI_CLIENT_VERSION) {
635
+ return new Headers({
636
+ accept: "application/json",
637
+ authorization: `Bearer ${token.accessToken}`,
638
+ "user-agent": `opengeni/${clientVersion}`,
639
+ "x-grok-client-version": clientVersion,
640
+ "x-grok-client-identifier": "opengeni",
641
+ "x-grok-client-mode": XAI_CLIENT_MODE,
642
+ "x-authenticateresponse": "authenticate-response",
643
+ "x-xai-token-auth": XAI_TOKEN_AUTH_HEADER_VALUE,
644
+ "x-userid": token.userId,
645
+ "x-grok-user-id": token.userId
646
+ });
647
+ }
648
+
649
+ // src/quota.ts
650
+ async function fetchXaiSubscriptionQuota(input) {
651
+ const body = await fetchXaiProxyJson({
652
+ path: "billing?format=credits",
653
+ context: input.context,
654
+ ...input.fetch ? { fetch: input.fetch } : {},
655
+ ...input.timeoutMs ? { timeoutMs: input.timeoutMs } : {},
656
+ ...input.baseUrl ? { baseUrl: input.baseUrl } : {},
657
+ label: "billing request"
658
+ });
659
+ const config = record(body.config);
660
+ const currentPeriod = record(config?.currentPeriod ?? config?.current_period);
661
+ const monthlyLimit = cents(config?.monthlyLimit ?? config?.monthly_limit);
662
+ const used = cents(config?.used);
663
+ const directPercent = finitePercent(config?.creditUsagePercent ?? config?.credit_usage_percent);
664
+ const derivedPercent = monthlyLimit !== null && monthlyLimit > 0 && used !== null ? Math.max(0, Math.min(100, used / monthlyLimit * 100)) : null;
665
+ const period = currentPeriod ? {
666
+ type: stringOrNull(currentPeriod.type),
667
+ start: dateOrNull(currentPeriod.start),
668
+ end: dateOrNull(currentPeriod.end)
669
+ } : legacyPeriod(config);
670
+ return {
671
+ usedPercent: directPercent ?? derivedPercent,
672
+ period,
673
+ prepaidBalanceCents: cents(config?.prepaidBalance ?? config?.prepaid_balance),
674
+ onDemandCapCents: cents(config?.onDemandCap ?? config?.on_demand_cap),
675
+ onDemandUsedCents: cents(config?.onDemandUsed ?? config?.on_demand_used),
676
+ onDemandEnabled: booleanOrNull(body.onDemandEnabled ?? body.on_demand_enabled),
677
+ unifiedBilling: booleanOrNull(config?.isUnifiedBillingUser ?? config?.is_unified_billing_user),
678
+ subscriptionTier: stringOrNull(body.subscriptionTier ?? body.subscription_tier),
679
+ checkedAt: /* @__PURE__ */ new Date()
680
+ };
681
+ }
682
+ function record(value) {
683
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
684
+ }
685
+ function cents(value) {
686
+ if (typeof value === "number" && Number.isSafeInteger(value)) return value;
687
+ const object = record(value);
688
+ const candidate = object?.val;
689
+ return typeof candidate === "number" && Number.isSafeInteger(candidate) ? candidate : null;
690
+ }
691
+ function finitePercent(value) {
692
+ const candidate = Number(value);
693
+ return Number.isFinite(candidate) ? Math.max(0, Math.min(100, candidate)) : null;
694
+ }
695
+ function stringOrNull(value) {
696
+ return typeof value === "string" && value.length > 0 ? value : null;
697
+ }
698
+ function booleanOrNull(value) {
699
+ return typeof value === "boolean" ? value : null;
700
+ }
701
+ function dateOrNull(value) {
702
+ if (typeof value !== "string") return null;
703
+ const date = new Date(value);
704
+ return Number.isNaN(date.getTime()) ? null : date;
705
+ }
706
+ function legacyPeriod(config) {
707
+ const start = dateOrNull(config?.billingPeriodStart ?? config?.billing_period_start);
708
+ const end = dateOrNull(config?.billingPeriodEnd ?? config?.billing_period_end);
709
+ return start || end ? { type: null, start, end } : null;
710
+ }
711
+
712
+ // src/models.ts
713
+ async function fetchXaiSubscriptionModels(input) {
714
+ const body = await fetchXaiProxyJson({
715
+ path: "models",
716
+ context: input.context,
717
+ ...input.fetch ? { fetch: input.fetch } : {},
718
+ ...input.timeoutMs ? { timeoutMs: input.timeoutMs } : {},
719
+ ...input.baseUrl ? { baseUrl: input.baseUrl } : {},
720
+ maxBytes: 4 * 1024 * 1024,
721
+ label: "model metadata request"
722
+ });
723
+ const values = Array.isArray(body) ? body : body && typeof body === "object" && Array.isArray(body.data) ? body.data : [];
724
+ return values.flatMap((value) => {
725
+ const parsed = parseXaiSubscriptionModelMetadata(value);
726
+ return parsed ? [parsed] : [];
727
+ });
728
+ }
729
+ function parseXaiSubscriptionModelMetadata(value) {
730
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
731
+ const object = value;
732
+ const meta = object._meta && typeof object._meta === "object" && !Array.isArray(object._meta) ? object._meta : {};
733
+ const slug = firstString(object.model, object.modelId, object.id, meta.model, meta.modelId);
734
+ const contextWindowTokens = firstPositiveInteger(
735
+ object.contextWindow,
736
+ object.context_window,
737
+ meta.contextWindow,
738
+ meta.totalContextTokens
739
+ );
740
+ if (!slug || contextWindowTokens === null) return null;
741
+ const effectivePercent = firstPositiveInteger(
742
+ object.effectiveContextWindowPercent,
743
+ object.effective_context_window_percent
744
+ ) ?? XAI_SUBSCRIPTION_EFFECTIVE_CONTEXT_PERCENT;
745
+ const autoCompactPercent = firstPositiveInteger(
746
+ object.autoCompactThresholdPercent,
747
+ object.auto_compact_threshold_percent
748
+ ) ?? XAI_SUBSCRIPTION_AUTO_COMPACTION_PERCENT;
749
+ const backend = firstString(object.apiBackend, object.api_backend);
750
+ return {
751
+ slug,
752
+ name: firstString(object.name) ?? slug,
753
+ contextWindowTokens,
754
+ effectiveContextWindowTokens: Math.floor(contextWindowTokens * effectivePercent / 100),
755
+ autoCompactTokenLimit: Math.floor(contextWindowTokens * autoCompactPercent / 100),
756
+ maxCompletionTokens: firstPositiveInteger(
757
+ object.maxCompletionTokens,
758
+ object.max_completion_tokens
759
+ ),
760
+ apiBackend: backend === "chat_completions" || backend === "messages" ? backend : "responses"
761
+ };
762
+ }
763
+ function firstString(...values) {
764
+ for (const value of values) {
765
+ if (typeof value === "string" && value.length > 0) return value;
766
+ }
767
+ return null;
768
+ }
769
+ function firstPositiveInteger(...values) {
770
+ for (const value of values) {
771
+ const number = Number(value);
772
+ if (Number.isSafeInteger(number) && number > 0) return number;
773
+ }
774
+ return null;
775
+ }
776
+
777
+ // src/images.ts
778
+ import { pinnedFetch as pinnedFetch2, readJsonBase64Field, readResponseTextBounded } from "@opengeni/network";
779
+ var XAI_IMAGE_RESPONSE_MAX_BYTES = 90 * 1024 * 1024;
780
+ var XAI_IMAGE_MAX_BYTES = 64 * 1024 * 1024;
781
+ var XAI_IMAGE_ERROR_MAX_BYTES = 64 * 1024;
782
+ var defaultImageFetch = async (input, init) => await pinnedFetch2(
783
+ input,
784
+ init,
785
+ { environment: "production", integrationsAllowPrivateNetworkTargets: false },
786
+ { label: "xAI image generation", requireHttpsOutsideLocalTest: true }
787
+ );
788
+ async function generateXaiSubscriptionImage(input) {
789
+ const timeoutMs = input.requestTimeoutMs ?? XAI_IMAGE_REQUEST_TIMEOUT_MS;
790
+ const deadline = new AbortController();
791
+ const timer = setTimeout(
792
+ () => deadline.abort(new XaiSubscriptionError("timeout", "xAI image generation timed out")),
793
+ timeoutMs
794
+ );
795
+ const signal = input.abortSignal ? AbortSignal.any([input.abortSignal, deadline.signal]) : deadline.signal;
796
+ const fetchImpl = input.fetch ?? defaultImageFetch;
797
+ const references = input.references ?? [];
798
+ if (references.length > 3) {
799
+ throw new XaiSubscriptionError(
800
+ "provider_rejected",
801
+ "xAI image editing supports at most three references"
802
+ );
803
+ }
804
+ const url = `${(input.baseUrl ?? XAI_PUBLIC_API_BASE_URL).replace(/\/+$/, "")}/images/${references.length > 0 ? "edits" : "generations"}`;
805
+ const request = async (token) => await fetchImpl(url, {
806
+ method: "POST",
807
+ redirect: "error",
808
+ headers: {
809
+ accept: "application/json",
810
+ authorization: `Bearer ${token.accessToken}`,
811
+ "content-type": "application/json",
812
+ "user-agent": `opengeni/${XAI_CLIENT_VERSION}`,
813
+ "x-grok-client-version": XAI_CLIENT_VERSION,
814
+ "x-grok-client-identifier": "opengeni",
815
+ ...input.sessionId ? { "x-grok-session-id": input.sessionId } : {}
816
+ },
817
+ body: JSON.stringify(
818
+ references.length > 0 ? {
819
+ model: XAI_IMAGE_MODEL,
820
+ prompt: input.prompt,
821
+ n: 1,
822
+ resolution: "1k",
823
+ response_format: "b64_json",
824
+ ...references.length === 1 ? { image: referencePayload(references[0]) } : {
825
+ images: references.map(referencePayload),
826
+ aspect_ratio: input.aspectRatio ?? "auto"
827
+ }
828
+ } : {
829
+ model: XAI_IMAGE_MODEL,
830
+ prompt: input.prompt,
831
+ n: 1,
832
+ aspect_ratio: input.aspectRatio ?? "auto",
833
+ resolution: "1k",
834
+ response_format: "b64_json"
835
+ }
836
+ ),
837
+ signal
838
+ });
839
+ try {
840
+ let response = await request(await input.getToken());
841
+ if (response.status === 401) {
842
+ await response.body?.cancel().catch(() => void 0);
843
+ response = await request(await input.refresh());
844
+ }
845
+ if (!response.ok) {
846
+ const detail = await readResponseTextBounded(
847
+ response,
848
+ XAI_IMAGE_ERROR_MAX_BYTES,
849
+ "xAI image generation error",
850
+ { signal }
851
+ ).catch(() => "");
852
+ throw new XaiSubscriptionError(
853
+ "provider_rejected",
854
+ `xAI image generation failed (${response.status})${detail ? `: ${boundedMessage(detail)}` : ""}`,
855
+ response.status
856
+ );
857
+ }
858
+ const bytes = await readJsonBase64Field(response, {
859
+ fieldName: "b64_json",
860
+ shape: "string",
861
+ maxResponseBytes: XAI_IMAGE_RESPONSE_MAX_BYTES,
862
+ maxDecodedBytes: XAI_IMAGE_MAX_BYTES,
863
+ label: "xAI image generation",
864
+ signal
865
+ });
866
+ return { bytes, declaredMediaType: detectImageMediaType(bytes) };
867
+ } finally {
868
+ clearTimeout(timer);
869
+ }
870
+ }
871
+ function referencePayload(reference) {
872
+ return {
873
+ url: `data:${reference.mediaType};base64,${Buffer.from(reference.bytes).toString("base64")}`
874
+ };
875
+ }
876
+ function detectImageMediaType(bytes) {
877
+ if (bytes.byteLength >= 8 && [137, 80, 78, 71, 13, 10, 26, 10].every((byte, index) => bytes[index] === byte)) {
878
+ return "image/png";
879
+ }
880
+ if (bytes.byteLength >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
881
+ return "image/jpeg";
882
+ }
883
+ if (bytes.byteLength >= 12 && String.fromCharCode(...bytes.subarray(0, 4)) === "RIFF" && String.fromCharCode(...bytes.subarray(8, 12)) === "WEBP") {
884
+ return "image/webp";
885
+ }
886
+ throw new XaiSubscriptionError("invalid_response", "xAI returned an unsupported image format");
887
+ }
888
+ function boundedMessage(body) {
889
+ try {
890
+ const value = JSON.parse(body);
891
+ const error = value.error && typeof value.error === "object" && !Array.isArray(value.error) ? value.error : null;
892
+ const message = error?.message ?? value.message;
893
+ if (typeof message === "string") return message.replace(/\s+/g, " ").trim().slice(0, 1e3);
894
+ } catch {
895
+ }
896
+ return body.replace(/\s+/g, " ").trim().slice(0, 1e3);
897
+ }
898
+
899
+ // src/video.ts
900
+ import {
901
+ pinnedFetch as pinnedFetch3,
902
+ readResponseBodyBounded,
903
+ readResponseJsonBounded as readResponseJsonBounded3,
904
+ readResponseTextBounded as readResponseTextBounded2,
905
+ validateHttpUrl as validateHttpUrl2
906
+ } from "@opengeni/network";
907
+ var XAI_VIDEO_MAX_BYTES = 256 * 1024 * 1024;
908
+ var XAI_VIDEO_ERROR_MAX_BYTES = 64 * 1024;
909
+ var defaultVideoFetch = async (input, init) => await pinnedFetch3(
910
+ input,
911
+ init,
912
+ {
913
+ environment: "production",
914
+ integrationsAllowPrivateNetworkTargets: false
915
+ },
916
+ { label: "xAI video generation", requireHttpsOutsideLocalTest: true }
917
+ );
918
+ async function startXaiSubscriptionVideoWithBody(input) {
919
+ const fetchImpl = input.fetch ?? defaultVideoFetch;
920
+ const baseUrl = (input.baseUrl ?? XAI_PUBLIC_API_BASE_URL).replace(/\/+$/, "");
921
+ const request = async (token) => await fetchWithDeadline(
922
+ fetchImpl,
923
+ `${baseUrl}/videos/generations`,
924
+ {
925
+ method: "POST",
926
+ redirect: "error",
927
+ headers: publicVideoHeaders(token, input.sessionId, true),
928
+ body: JSON.stringify(input.body),
929
+ ...input.signal ? { signal: input.signal } : {}
930
+ },
931
+ XAI_VIDEO_START_TIMEOUT_MS,
932
+ input.signal ?? AbortSignal.timeout(XAI_VIDEO_START_TIMEOUT_MS)
933
+ );
934
+ let response = await request(await input.getToken());
935
+ if (response.status === 401) {
936
+ await response.body?.cancel().catch(() => void 0);
937
+ response = await request(await input.refresh());
938
+ }
939
+ if (!response.ok)
940
+ throw await providerError("start", response, input.signal ?? new AbortController().signal);
941
+ const body = await readResponseJsonBounded3(
942
+ response,
943
+ 1024 * 1024,
944
+ "xAI video start",
945
+ input.signal ? { signal: input.signal } : {}
946
+ );
947
+ if (typeof body.request_id !== "string" || !body.request_id.trim()) {
948
+ throw new XaiSubscriptionError("invalid_response", "xAI video start response is malformed");
949
+ }
950
+ return { providerJobId: body.request_id };
951
+ }
952
+ async function getXaiSubscriptionVideoStatus(input) {
953
+ if (!input.providerJobId.trim() || input.providerJobId.length > 1024) {
954
+ throw new Error("xAI video job identity is invalid");
955
+ }
956
+ const fetchImpl = input.fetch ?? defaultVideoFetch;
957
+ const baseUrl = (input.baseUrl ?? XAI_PUBLIC_API_BASE_URL).replace(/\/+$/, "");
958
+ const request = async (token) => await fetchWithDeadline(
959
+ fetchImpl,
960
+ `${baseUrl}/videos/${encodeURIComponent(input.providerJobId)}`,
961
+ {
962
+ method: "GET",
963
+ redirect: "error",
964
+ headers: publicVideoHeaders(token, input.sessionId, false),
965
+ ...input.signal ? { signal: input.signal } : {}
966
+ },
967
+ XAI_VIDEO_POLL_REQUEST_TIMEOUT_MS,
968
+ input.signal ?? AbortSignal.timeout(XAI_VIDEO_POLL_REQUEST_TIMEOUT_MS)
969
+ );
970
+ let response = await request(await input.getToken());
971
+ if (response.status === 401) {
972
+ await response.body?.cancel().catch(() => void 0);
973
+ response = await request(await input.refresh());
974
+ }
975
+ if (!response.ok && response.status !== 202) {
976
+ throw await providerError("poll", response, input.signal ?? new AbortController().signal);
977
+ }
978
+ const body = await readResponseJsonBounded3(
979
+ response,
980
+ 1024 * 1024,
981
+ "xAI video poll",
982
+ input.signal ? { signal: input.signal } : {}
983
+ );
984
+ if (body.status === "pending" || body.status === "queued" || body.status === "running") {
985
+ return { status: "pending" };
986
+ }
987
+ if (body.status === "failed" || body.status === "expired") {
988
+ return {
989
+ status: "error",
990
+ publicReason: `xAI video generation ${body.status}`
991
+ };
992
+ }
993
+ const video = body.video && typeof body.video === "object" && !Array.isArray(body.video) ? body.video : null;
994
+ if (body.status !== "done" || typeof video?.url !== "string") {
995
+ throw new XaiSubscriptionError("invalid_response", "xAI video poll response is malformed");
996
+ }
997
+ return {
998
+ status: "completed",
999
+ outputUrl: validateMediaUrl(video.url),
1000
+ mediaType: "video/mp4"
1001
+ };
1002
+ }
1003
+ function publicVideoHeaders(token, sessionId, json) {
1004
+ return new Headers({
1005
+ accept: "application/json",
1006
+ authorization: `Bearer ${token.accessToken}`,
1007
+ "user-agent": `opengeni/${XAI_CLIENT_VERSION}`,
1008
+ "x-grok-client-version": XAI_CLIENT_VERSION,
1009
+ "x-grok-client-identifier": "opengeni",
1010
+ "x-grok-client-mode": XAI_CLIENT_MODE,
1011
+ ...sessionId ? { "x-grok-session-id": sessionId } : {},
1012
+ ...json ? { "content-type": "application/json" } : {}
1013
+ });
1014
+ }
1015
+ async function generateXaiSubscriptionVideo(input) {
1016
+ const baseUrl = (input.baseUrl ?? XAI_PUBLIC_API_BASE_URL).replace(/\/+$/, "");
1017
+ const fetchImpl = input.fetch ?? defaultVideoFetch;
1018
+ const deadline = new AbortController();
1019
+ const timeoutMs = input.generationTimeoutMs ?? XAI_VIDEO_GENERATION_TIMEOUT_MS;
1020
+ const timer = setTimeout(
1021
+ () => deadline.abort(new XaiSubscriptionError("timeout", "xAI video generation timed out")),
1022
+ timeoutMs
1023
+ );
1024
+ const signal = input.abortSignal ? AbortSignal.any([input.abortSignal, deadline.signal]) : deadline.signal;
1025
+ const references = (input.referenceImageUrls ?? []).map(validateMediaUrl);
1026
+ const image = input.imageUrl ? validateMediaUrl(input.imageUrl) : void 0;
1027
+ const request = async (token) => await fetchWithDeadline(
1028
+ fetchImpl,
1029
+ `${baseUrl}/videos/generations`,
1030
+ {
1031
+ method: "POST",
1032
+ redirect: "error",
1033
+ headers: {
1034
+ accept: "application/json",
1035
+ authorization: `Bearer ${token.accessToken}`,
1036
+ "content-type": "application/json"
1037
+ },
1038
+ body: JSON.stringify({
1039
+ model: XAI_VIDEO_MODEL,
1040
+ prompt: input.prompt,
1041
+ ...image ? { image: { url: image } } : {},
1042
+ ...references.length > 0 ? { reference_images: references.map((url) => ({ url })) } : {},
1043
+ ...input.durationSeconds ? { duration: input.durationSeconds } : {},
1044
+ aspect_ratio: input.aspectRatio ?? "16:9",
1045
+ resolution: input.resolution ?? "480p"
1046
+ }),
1047
+ signal
1048
+ },
1049
+ XAI_VIDEO_START_TIMEOUT_MS,
1050
+ signal
1051
+ );
1052
+ try {
1053
+ let start = await request(await input.getToken());
1054
+ if (start.status === 401) {
1055
+ await start.body?.cancel().catch(() => void 0);
1056
+ start = await request(await input.refresh());
1057
+ }
1058
+ if (!start.ok) throw await providerError("start", start, signal);
1059
+ const started = await readResponseJsonBounded3(
1060
+ start,
1061
+ 1024 * 1024,
1062
+ "xAI video start",
1063
+ { signal }
1064
+ );
1065
+ const requestId = typeof started.request_id === "string" && started.request_id.length > 0 ? started.request_id : null;
1066
+ if (!requestId) {
1067
+ throw new XaiSubscriptionError(
1068
+ "invalid_response",
1069
+ "xAI video generation did not return request_id"
1070
+ );
1071
+ }
1072
+ const sleep = input.sleep ?? abortableSleep;
1073
+ for (; ; ) {
1074
+ await sleep(XAI_VIDEO_POLL_INTERVAL_MS, signal);
1075
+ let poll = await pollVideo(fetchImpl, baseUrl, requestId, await input.getToken(), signal);
1076
+ if (poll.status === 401) {
1077
+ await poll.body?.cancel().catch(() => void 0);
1078
+ poll = await pollVideo(fetchImpl, baseUrl, requestId, await input.refresh(), signal);
1079
+ }
1080
+ if (!poll.ok && poll.status !== 202) throw await providerError("poll", poll, signal);
1081
+ const status = await readResponseJsonBounded3(
1082
+ poll,
1083
+ 1024 * 1024,
1084
+ "xAI video poll",
1085
+ { signal }
1086
+ );
1087
+ if (status.status === "failed" || status.status === "expired") {
1088
+ throw new XaiSubscriptionError(
1089
+ "provider_rejected",
1090
+ `xAI video generation ${status.status}`
1091
+ );
1092
+ }
1093
+ if (status.status !== "done") continue;
1094
+ const video = status.video && typeof status.video === "object" && !Array.isArray(status.video) ? status.video : null;
1095
+ const downloadUrl = typeof video?.url === "string" ? validateMediaUrl(video.url) : null;
1096
+ if (!downloadUrl) {
1097
+ throw new XaiSubscriptionError(
1098
+ "invalid_response",
1099
+ "xAI video generation completed without a download URL"
1100
+ );
1101
+ }
1102
+ const download = await fetchWithDeadline(
1103
+ fetchImpl,
1104
+ downloadUrl,
1105
+ { method: "GET", redirect: "error", signal },
1106
+ XAI_VIDEO_DOWNLOAD_TIMEOUT_MS,
1107
+ signal
1108
+ );
1109
+ if (!download.ok) throw await providerError("download", download, signal);
1110
+ const bytes = await readResponseBodyBounded(
1111
+ download,
1112
+ XAI_VIDEO_MAX_BYTES,
1113
+ "xAI video download",
1114
+ { signal }
1115
+ );
1116
+ return { bytes, declaredMediaType: "video/mp4", requestId };
1117
+ }
1118
+ } finally {
1119
+ clearTimeout(timer);
1120
+ }
1121
+ }
1122
+ async function pollVideo(fetchImpl, baseUrl, requestId, token, signal) {
1123
+ return await fetchWithDeadline(
1124
+ fetchImpl,
1125
+ `${baseUrl}/videos/${encodeURIComponent(requestId)}`,
1126
+ {
1127
+ method: "GET",
1128
+ redirect: "error",
1129
+ headers: {
1130
+ accept: "application/json",
1131
+ authorization: `Bearer ${token.accessToken}`
1132
+ },
1133
+ signal
1134
+ },
1135
+ XAI_VIDEO_POLL_REQUEST_TIMEOUT_MS,
1136
+ signal
1137
+ );
1138
+ }
1139
+ async function fetchWithDeadline(fetchImpl, url, init, timeoutMs, outerSignal) {
1140
+ const timeout = AbortSignal.timeout(timeoutMs);
1141
+ return await fetchImpl(url, {
1142
+ ...init,
1143
+ signal: AbortSignal.any([outerSignal, timeout])
1144
+ });
1145
+ }
1146
+ async function providerError(phase, response, signal) {
1147
+ const detail = await readResponseTextBounded2(
1148
+ response,
1149
+ XAI_VIDEO_ERROR_MAX_BYTES,
1150
+ `xAI video ${phase} error`,
1151
+ { signal }
1152
+ ).catch(() => "");
1153
+ return new XaiSubscriptionError(
1154
+ "provider_rejected",
1155
+ `xAI video ${phase} failed (${response.status})${detail ? `: ${detail.replace(/\s+/g, " ").trim().slice(0, 1e3)}` : ""}`,
1156
+ response.status
1157
+ );
1158
+ }
1159
+ function validateMediaUrl(value) {
1160
+ try {
1161
+ return validateHttpUrl2(value, { label: "xAI media" });
1162
+ } catch {
1163
+ throw new XaiSubscriptionError("invalid_response", "xAI returned an invalid media URL");
1164
+ }
1165
+ }
1166
+ async function abortableSleep(ms, signal) {
1167
+ if (signal.aborted) throw signal.reason;
1168
+ await new Promise((resolve, reject) => {
1169
+ const timer = setTimeout(done, ms);
1170
+ const onAbort = () => done(signal.reason);
1171
+ function done(error) {
1172
+ clearTimeout(timer);
1173
+ signal.removeEventListener("abort", onAbort);
1174
+ if (error === void 0) {
1175
+ resolve();
1176
+ } else {
1177
+ reject(error);
1178
+ }
1179
+ }
1180
+ signal.addEventListener("abort", onAbort, { once: true });
1181
+ });
1182
+ }
1183
+ export {
1184
+ XAI_CLIENT_MODE,
1185
+ XAI_CLIENT_VERSION,
1186
+ XAI_DEVICE_AUTHORIZATION_URL,
1187
+ XAI_DEVICE_CODE_GRANT_TYPE,
1188
+ XAI_IMAGE_MODEL,
1189
+ XAI_IMAGE_REQUEST_TIMEOUT_MS,
1190
+ XAI_OAUTH_CLIENT_ID,
1191
+ XAI_OAUTH_ISSUER,
1192
+ XAI_OAUTH_OPERATION_TIMEOUT_MS,
1193
+ XAI_OAUTH_SCOPES,
1194
+ XAI_PUBLIC_API_BASE_URL,
1195
+ XAI_REFRESH_FALLBACK_MS,
1196
+ XAI_REFRESH_WINDOW_MS,
1197
+ XAI_RESPONSE_SDK_OUTER_TIMEOUT_MS,
1198
+ XAI_SUBSCRIPTION_AUTO_COMPACTION_PERCENT,
1199
+ XAI_SUBSCRIPTION_EFFECTIVE_CONTEXT_PERCENT,
1200
+ XAI_SUBSCRIPTION_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
1201
+ XAI_SUBSCRIPTION_MODEL_CONTEXT_WINDOW_TOKENS,
1202
+ XAI_SUBSCRIPTION_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
1203
+ XAI_SUBSCRIPTION_MODEL_ID_PREFIX,
1204
+ XAI_SUBSCRIPTION_MODEL_SLUGS,
1205
+ XAI_SUBSCRIPTION_PROVIDER_ID,
1206
+ XAI_SUBSCRIPTION_PROXY_BASE_URL,
1207
+ XAI_SUBSCRIPTION_REQUEST_BODY_NORMALIZED_HEADER,
1208
+ XAI_SUBSCRIPTION_REQUEST_ID_HEADER,
1209
+ XAI_SUBSCRIPTION_REQUEST_MODEL_HEADER,
1210
+ XAI_SUBSCRIPTION_TRANSPORT_ERROR_HEADER,
1211
+ XAI_TOKEN_AUTH_HEADER_VALUE,
1212
+ XAI_TOKEN_URL,
1213
+ XAI_USERINFO_URL,
1214
+ XAI_VIDEO_DOWNLOAD_TIMEOUT_MS,
1215
+ XAI_VIDEO_GENERATION_TIMEOUT_MS,
1216
+ XAI_VIDEO_MODEL,
1217
+ XAI_VIDEO_POLL_INTERVAL_MS,
1218
+ XAI_VIDEO_POLL_REQUEST_TIMEOUT_MS,
1219
+ XAI_VIDEO_START_TIMEOUT_MS,
1220
+ XaiSubscriptionError,
1221
+ XaiSubscriptionReloginRequired,
1222
+ XaiSubscriptionTransientError,
1223
+ decodeXaiJwtPayload,
1224
+ fetchXaiProxyJson,
1225
+ fetchXaiSubscriptionModels,
1226
+ fetchXaiSubscriptionQuota,
1227
+ fetchXaiVerifiedIdentity,
1228
+ generateXaiSubscriptionImage,
1229
+ generateXaiSubscriptionVideo,
1230
+ getXaiSubscriptionVideoStatus,
1231
+ isXaiSubscriptionTransportError,
1232
+ normalizeXaiResponseEventJson,
1233
+ normalizeXaiSubscriptionRequestBody,
1234
+ parseXaiSubscriptionModelMetadata,
1235
+ pollXaiDeviceCode,
1236
+ refreshXaiToken,
1237
+ requestXaiDeviceCode,
1238
+ runBoundedXaiOperation,
1239
+ startXaiSubscriptionVideoWithBody,
1240
+ xaiAccessTokenExpiry,
1241
+ xaiSubscriptionFetch,
1242
+ xaiSubscriptionProxyHeaders,
1243
+ xaiSubscriptionRequestStorage
1244
+ };
1245
+ //# sourceMappingURL=index.js.map