feat: add Grok Web (Subscription) provider

Adds a new provider that routes through Grok's internal NDJSON API using
an X/Grok subscription SSO cookie, enabling access to Grok 3, 4, 4.1,
4.2/4.20, and 4 Heavy via grok.com without xAI API costs.

- GrokWebExecutor with full NDJSON→OpenAI translation
- 12 models incl. grok-4.2 (4.20 beta), grok-4-heavy (SuperGrok)
- Dynamic x-statsig-id generation (base64 fake TypeError)
- W3C traceparent headers for Cloudflare compatibility
- Thinking/reasoning mode for mini/thinking/expert/heavy variants
- SSO cookie auth with auto-strip of 'sso=' prefix
- Fetch timeout + upstreamExtraHeaders (BaseExecutor parity)
- 16 unit tests, all passing

Derived from: GrokProxy, GrokBridge, grok-web-api, grok2api-merged,
Grok API Research Report
This commit is contained in:
Ravi Tharuma
2026-04-15 23:01:25 +02:00
parent eaec3279ab
commit 97dca71c2c
5 changed files with 971 additions and 0 deletions
+24
View File
@@ -1039,6 +1039,30 @@ export const REGISTRY: Record<string, RegistryEntry> = {
],
},
"grok-web": {
id: "grok-web",
alias: "grok-web",
format: "openai",
executor: "grok-web",
baseUrl: "https://grok.com/rest/app-chat/conversations/new",
authType: "apikey",
authHeader: "cookie",
models: [
{ id: "grok-3", name: "Grok 3" },
{ id: "grok-3-mini", name: "Grok 3 Mini (Thinking)" },
{ id: "grok-3-thinking", name: "Grok 3 Thinking" },
{ id: "grok-4", name: "Grok 4" },
{ id: "grok-4-mini", name: "Grok 4 Mini (Thinking)" },
{ id: "grok-4-thinking", name: "Grok 4 Thinking" },
{ id: "grok-4-heavy", name: "Grok 4 Heavy (SuperGrok)" },
{ id: "grok-4.1-mini", name: "Grok 4.1 Mini (Thinking)" },
{ id: "grok-4.1-fast", name: "Grok 4.1 Fast" },
{ id: "grok-4.1-expert", name: "Grok 4.1 Expert" },
{ id: "grok-4.1-thinking", name: "Grok 4.1 Thinking" },
{ id: "grok-4.2", name: "Grok 4.2 (4.20 Beta)" },
],
},
mistral: {
id: "mistral",
alias: "mistral",
+581
View File
@@ -0,0 +1,581 @@
/**
* GrokWebExecutor — Grok Web Session Provider
*
* Routes requests through Grok's internal NDJSON API using an X/Grok
* subscription SSO cookie, translating between OpenAI chat completions
* format and Grok's internal protocol.
*
* Derived from:
* - grok2api-merged (model mappings, payload structure, statsig, processor)
* - GrokProxy / GrokBridge (cookie auth, streaming token extraction)
* - grok-web-api (response types, chat options)
* - Grok API Research Report (headers, Cloudflare bypass techniques)
*/
import { BaseExecutor, mergeUpstreamExtraHeaders, mergeAbortSignals, type ExecuteInput } from "./base.ts";
import { FETCH_TIMEOUT_MS } from "../config/constants.ts";
// ─── Constants ──────────────────────────────────────────────────────────────
const GROK_CHAT_API = "https://grok.com/rest/app-chat/conversations/new";
const GROK_USER_AGENT =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";
// ─── Model mappings ─────────────────────────────────────────────────────────
// Maps OmniRoute model IDs → [grokModel, modelMode]
interface GrokModelInfo {
grokModel: string;
modelMode: string;
isThinking: boolean;
}
const MODEL_MAP: Record<string, GrokModelInfo> = {
"grok-3": { grokModel: "grok-3", modelMode: "MODEL_MODE_GROK_3", isThinking: false },
"grok-3-mini": { grokModel: "grok-3", modelMode: "MODEL_MODE_GROK_3_MINI_THINKING", isThinking: true },
"grok-3-thinking": { grokModel: "grok-3", modelMode: "MODEL_MODE_GROK_3_THINKING", isThinking: true },
"grok-4": { grokModel: "grok-4", modelMode: "MODEL_MODE_GROK_4", isThinking: false },
"grok-4-mini": { grokModel: "grok-4-mini", modelMode: "MODEL_MODE_GROK_4_MINI_THINKING", isThinking: true },
"grok-4-thinking": { grokModel: "grok-4", modelMode: "MODEL_MODE_GROK_4_THINKING", isThinking: true },
"grok-4-heavy": { grokModel: "grok-4", modelMode: "MODEL_MODE_HEAVY", isThinking: true },
"grok-4.1-mini": { grokModel: "grok-4-1-thinking-1129", modelMode: "MODEL_MODE_GROK_4_1_MINI_THINKING", isThinking: true },
"grok-4.1-fast": { grokModel: "grok-4-1-thinking-1129", modelMode: "MODEL_MODE_FAST", isThinking: false },
"grok-4.1-expert": { grokModel: "grok-4-1-thinking-1129", modelMode: "MODEL_MODE_EXPERT", isThinking: true },
"grok-4.1-thinking": { grokModel: "grok-4-1-thinking-1129", modelMode: "MODEL_MODE_GROK_4_1_THINKING", isThinking: true },
"grok-4.2": { grokModel: "grok-420", modelMode: "MODEL_MODE_GROK_420", isThinking: false },
"grok-4.20": { grokModel: "grok-420", modelMode: "MODEL_MODE_GROK_420", isThinking: false },
"grok-4.20-beta": { grokModel: "grok-420", modelMode: "MODEL_MODE_GROK_420", isThinking: false },
};
// ─── Statsig ID generation ──────────────────────────────────────────────────
function randomString(length: number, alphanumeric = false): string {
const chars = alphanumeric
? "abcdefghijklmnopqrstuvwxyz0123456789"
: "abcdefghijklmnopqrstuvwxyz";
let result = "";
for (let i = 0; i < length; i++) {
result += chars[Math.floor(Math.random() * chars.length)];
}
return result;
}
function generateStatsigId(): string {
const msg = Math.random() < 0.5
? `e:TypeError: Cannot read properties of null (reading 'children["${randomString(5, true)}"]')`
: `e:TypeError: Cannot read properties of undefined (reading '${randomString(10)}')`;
return btoa(msg);
}
// ─── Helpers ────────────────────────────────────────────────────────────────
function randomHex(bytes: number): string {
const arr = new Uint8Array(bytes);
crypto.getRandomValues(arr);
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
}
// ─── OpenAI message → Grok query translation ───────────────────────────────
function parseOpenAIMessages(messages: Array<Record<string, unknown>>): string {
const parts: string[] = [];
let lastUserIdx = -1;
// Extract text from each message
const extracted: Array<{ role: string; text: string }> = [];
for (const msg of messages) {
let role = String(msg.role || "user");
if (role === "developer") role = "system";
let content = "";
if (typeof msg.content === "string") {
content = msg.content;
} else if (Array.isArray(msg.content)) {
content = (msg.content as Array<Record<string, unknown>>)
.filter((c) => c.type === "text")
.map((c) => String(c.text || ""))
.join(" ");
}
if (!content.trim()) continue;
extracted.push({ role, text: content });
}
// Find last user message index
for (let i = extracted.length - 1; i >= 0; i--) {
if (extracted[i].role === "user") {
lastUserIdx = i;
break;
}
}
// Build combined message — last user message is raw, others are prefixed
for (let i = 0; i < extracted.length; i++) {
const { role, text } = extracted[i];
if (i === lastUserIdx) {
parts.push(text);
} else {
parts.push(`${role}: ${text}`);
}
}
return parts.join("\n\n");
}
// ─── NDJSON stream types ────────────────────────────────────────────────────
interface GrokStreamResponse {
token?: string;
responseId?: string;
llmInfo?: { modelHash?: string };
modelResponse?: {
message?: string;
responseId?: string;
generatedImageUrls?: string[];
metadata?: { llm_info?: { modelHash?: string } };
pipelineToken?: string;
};
}
interface GrokStreamEvent {
result?: { response?: GrokStreamResponse };
error?: { message?: string; code?: string };
}
// ─── NDJSON parsing ─────────────────────────────────────────────────────────
async function* readGrokNdjsonEvents(
body: ReadableStream<Uint8Array>,
signal?: AbortSignal | null,
): AsyncGenerator<GrokStreamEvent> {
const reader = body.getReader();
const decoder = new TextDecoder();
let buffer = "";
try {
while (true) {
if (signal?.aborted) return;
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
while (true) {
const idx = buffer.indexOf("\n");
if (idx < 0) break;
const line = buffer.slice(0, idx).trim();
buffer = buffer.slice(idx + 1);
if (!line) continue;
try {
yield JSON.parse(line) as GrokStreamEvent;
} catch {
// Skip non-JSON lines
}
}
}
// Flush remaining buffer
buffer += decoder.decode();
const remaining = buffer.trim();
if (remaining) {
try {
yield JSON.parse(remaining) as GrokStreamEvent;
} catch {
// ignore
}
}
} finally {
reader.releaseLock();
}
}
// ─── Content extraction ─────────────────────────────────────────────────────
interface ContentChunk {
delta?: string;
thinking?: string;
fingerprint?: string;
responseId?: string;
fullMessage?: string;
error?: string;
done?: boolean;
}
async function* extractContent(
eventStream: ReadableStream<Uint8Array>,
isThinkingModel: boolean,
signal?: AbortSignal | null,
): AsyncGenerator<ContentChunk> {
let fingerprint = "";
let responseId = "";
let thinkOpened = false;
for await (const event of readGrokNdjsonEvents(eventStream, signal)) {
// Error handling
if (event.error) {
yield { error: event.error.message || `Grok error: ${event.error.code}`, done: true };
return;
}
const resp = event.result?.response;
if (!resp) continue;
// Extract metadata
if (resp.llmInfo?.modelHash && !fingerprint) {
fingerprint = resp.llmInfo.modelHash;
}
if (resp.responseId) {
responseId = resp.responseId;
}
// modelResponse = final/complete response
if (resp.modelResponse) {
const mr = resp.modelResponse;
// Close thinking block if open
if (thinkOpened && isThinkingModel) {
if (mr.message) {
yield { thinking: mr.message };
}
thinkOpened = false;
}
// Extract final message
if (mr.message) {
yield { fullMessage: mr.message, fingerprint, responseId };
}
// Extract fingerprint from metadata
if (mr.metadata?.llm_info?.modelHash) {
fingerprint = mr.metadata.llm_info.modelHash;
}
continue;
}
// Streaming token
if (resp.token != null) {
yield { delta: resp.token, fingerprint, responseId };
}
}
yield { done: true, fingerprint, responseId };
}
// ─── OpenAI SSE format builders ─────────────────────────────────────────────
function sseChunk(data: unknown): string {
return `data: ${JSON.stringify(data)}\n\n`;
}
function buildStreamingResponse(
eventStream: ReadableStream<Uint8Array>,
model: string,
cid: string,
created: number,
isThinkingModel: boolean,
signal?: AbortSignal | null,
): ReadableStream<Uint8Array> {
const encoder = new TextEncoder();
return new ReadableStream({
async start(controller) {
try {
// Initial role chunk
controller.enqueue(
encoder.encode(
sseChunk({
id: cid, object: "chat.completion.chunk", created, model,
system_fingerprint: null,
choices: [{ index: 0, delta: { role: "assistant" }, finish_reason: null, logprobs: null }],
}),
),
);
let fp = "";
for await (const chunk of extractContent(eventStream, isThinkingModel, signal)) {
if (chunk.fingerprint) fp = chunk.fingerprint;
if (chunk.error) {
controller.enqueue(encoder.encode(sseChunk({
id: cid, object: "chat.completion.chunk", created, model, system_fingerprint: fp || null,
choices: [{ index: 0, delta: { content: `[Error: ${chunk.error}]` }, finish_reason: null, logprobs: null }],
})));
break;
}
if (chunk.thinking) {
controller.enqueue(encoder.encode(sseChunk({
id: cid, object: "chat.completion.chunk", created, model, system_fingerprint: fp || null,
choices: [{ index: 0, delta: { reasoning_content: chunk.thinking }, finish_reason: null, logprobs: null }],
})));
continue;
}
if (chunk.done) break;
if (chunk.delta) {
controller.enqueue(encoder.encode(sseChunk({
id: cid, object: "chat.completion.chunk", created, model, system_fingerprint: fp || null,
choices: [{ index: 0, delta: { content: chunk.delta }, finish_reason: null, logprobs: null }],
})));
}
}
// Stop chunk
controller.enqueue(encoder.encode(sseChunk({
id: cid, object: "chat.completion.chunk", created, model, system_fingerprint: fp || null,
choices: [{ index: 0, delta: {}, finish_reason: "stop", logprobs: null }],
})));
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
} catch (err) {
controller.enqueue(encoder.encode(sseChunk({
id: cid, object: "chat.completion.chunk", created, model, system_fingerprint: null,
choices: [{ index: 0, delta: { content: `[Stream error: ${err instanceof Error ? err.message : String(err)}]` }, finish_reason: "stop", logprobs: null }],
})));
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
} finally {
controller.close();
}
},
});
}
async function buildNonStreamingResponse(
eventStream: ReadableStream<Uint8Array>,
model: string,
cid: string,
created: number,
isThinkingModel: boolean,
signal?: AbortSignal | null,
): Promise<Response> {
let fullContent = "";
let fingerprint = "";
const thinkingParts: string[] = [];
for await (const chunk of extractContent(eventStream, isThinkingModel, signal)) {
if (chunk.fingerprint) fingerprint = chunk.fingerprint;
if (chunk.error) {
return new Response(
JSON.stringify({ error: { message: chunk.error, type: "upstream_error", code: "GROK_ERROR" } }),
{ status: 502, headers: { "Content-Type": "application/json" } },
);
}
if (chunk.thinking) {
thinkingParts.push(chunk.thinking);
continue;
}
if (chunk.done) break;
if (chunk.fullMessage) {
fullContent = chunk.fullMessage;
} else if (chunk.delta) {
fullContent += chunk.delta;
}
}
const msg: Record<string, unknown> = { role: "assistant", content: fullContent };
if (thinkingParts.length > 0) {
msg.reasoning_content = thinkingParts.join("\n");
}
const promptTokens = Math.ceil(fullContent.length / 4);
const completionTokens = Math.ceil(fullContent.length / 4);
return new Response(
JSON.stringify({
id: cid, object: "chat.completion", created, model,
system_fingerprint: fingerprint || null,
choices: [{ index: 0, message: msg, finish_reason: "stop", logprobs: null }],
usage: { prompt_tokens: promptTokens, completion_tokens: completionTokens, total_tokens: promptTokens + completionTokens },
}),
{ status: 200, headers: { "Content-Type": "application/json" } },
);
}
// ─── Executor ───────────────────────────────────────────────────────────────
export class GrokWebExecutor extends BaseExecutor {
constructor() {
super("grok-web", { id: "grok-web", baseUrl: GROK_CHAT_API });
}
async execute({ model, body, stream, credentials, signal, log, upstreamExtraHeaders }: ExecuteInput) {
const messages = (body as Record<string, unknown>).messages as
| Array<Record<string, unknown>>
| undefined;
if (!messages || !Array.isArray(messages) || messages.length === 0) {
const errResp = new Response(
JSON.stringify({ error: { message: "Missing or empty messages array", type: "invalid_request" } }),
{ status: 400, headers: { "Content-Type": "application/json" } },
);
return { response: errResp, url: GROK_CHAT_API, headers: {}, transformedBody: body };
}
// Resolve model → Grok internal model/mode
const modelInfo = MODEL_MAP[model];
if (!modelInfo) {
log?.info?.("GROK-WEB", `Unmapped model ${model}, defaulting to grok-4.1-fast`);
}
const { grokModel, modelMode, isThinking } = modelInfo || MODEL_MAP["grok-4.1-fast"];
// Parse OpenAI messages → single Grok message string
const message = parseOpenAIMessages(messages);
if (!message.trim()) {
const errResp = new Response(
JSON.stringify({ error: { message: "Empty query after processing", type: "invalid_request" } }),
{ status: 400, headers: { "Content-Type": "application/json" } },
);
return { response: errResp, url: GROK_CHAT_API, headers: {}, transformedBody: body };
}
// Build Grok request payload
const grokPayload: Record<string, unknown> = {
temporary: true,
modelName: grokModel,
modelMode: modelMode,
message: message,
fileAttachments: [],
imageAttachments: [],
disableSearch: false,
enableImageGeneration: false,
returnImageBytes: false,
returnRawGrokInXaiRequest: false,
enableImageStreaming: false,
imageGenerationCount: 0,
forceConcise: false,
toolOverrides: {},
enableSideBySide: true,
sendFinalMetadata: true,
isReasoning: false,
disableTextFollowUps: false,
disableMemory: true,
forceSideBySide: false,
isAsyncChat: false,
disableSelfHarmShortCircuit: false,
deviceEnvInfo: {
darkModeEnabled: false,
devicePixelRatio: 2,
screenWidth: 2056,
screenHeight: 1329,
viewportWidth: 2056,
viewportHeight: 1083,
},
};
// Build headers
const traceId = randomHex(16);
const spanId = randomHex(8);
const headers: Record<string, string> = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Accept-Language": "en-US,en;q=0.9",
"Baggage": "sentry-environment=production,sentry-release=d6add6fb0460641fd482d767a335ef72b9b6abb8,sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c",
"Cache-Control": "no-cache",
"Content-Type": "application/json",
"Origin": "https://grok.com",
"Pragma": "no-cache",
"Referer": "https://grok.com/",
"Sec-Ch-Ua": '"Google Chrome";v="136", "Chromium";v="136", "Not(A:Brand";v="24"',
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": '"macOS"',
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": GROK_USER_AGENT,
"x-statsig-id": generateStatsigId(),
"x-xai-request-id": crypto.randomUUID(),
"traceparent": `00-${traceId}-${spanId}-00`,
};
// Cookie auth — strip "sso=" prefix if user included it
if (credentials.apiKey) {
let token = credentials.apiKey;
if (token.startsWith("sso=")) token = token.slice(4);
headers["Cookie"] = `sso=${token}`;
}
// Apply upstream extra headers
mergeUpstreamExtraHeaders(headers, upstreamExtraHeaders);
log?.info?.(
"GROK-WEB",
`Query to ${model} (grok=${grokModel}, mode=${modelMode}), len=${message.length}`,
);
// Apply fetch timeout
const timeoutSignal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
const combinedSignal = signal ? mergeAbortSignals(signal, timeoutSignal) : timeoutSignal;
// Fetch from Grok
const fetchOptions: RequestInit = {
method: "POST",
headers,
body: JSON.stringify(grokPayload),
signal: combinedSignal,
};
let response: Response;
try {
response = await fetch(GROK_CHAT_API, fetchOptions);
} catch (err) {
log?.error?.(
"GROK-WEB",
`Fetch failed: ${err instanceof Error ? err.message : String(err)}`,
);
const errResp = new Response(
JSON.stringify({
error: { message: `Grok connection failed: ${err instanceof Error ? err.message : String(err)}`, type: "upstream_error" },
}),
{ status: 502, headers: { "Content-Type": "application/json" } },
);
return { response: errResp, url: GROK_CHAT_API, headers, transformedBody: grokPayload };
}
if (!response.ok) {
const status = response.status;
let errMsg = `Grok returned HTTP ${status}`;
if (status === 401 || status === 403) {
errMsg = "Grok auth failed — SSO cookie may be expired. Re-paste your sso cookie value from grok.com.";
} else if (status === 429) {
errMsg = "Grok rate limited. Wait a moment and retry, or rotate cookies.";
}
log?.warn?.("GROK-WEB", errMsg);
const errResp = new Response(
JSON.stringify({ error: { message: errMsg, type: "upstream_error", code: `HTTP_${status}` } }),
{ status, headers: { "Content-Type": "application/json" } },
);
return { response: errResp, url: GROK_CHAT_API, headers, transformedBody: grokPayload };
}
if (!response.body) {
const errResp = new Response(
JSON.stringify({ error: { message: "Grok returned empty response body", type: "upstream_error" } }),
{ status: 502, headers: { "Content-Type": "application/json" } },
);
return { response: errResp, url: GROK_CHAT_API, headers, transformedBody: grokPayload };
}
// Build OpenAI-compatible response
const cid = `chatcmpl-grok-${crypto.randomUUID().slice(0, 12)}`;
const created = Math.floor(Date.now() / 1000);
let finalResponse: Response;
if (stream) {
const sseStream = buildStreamingResponse(
response.body, model, cid, created, isThinking, signal,
);
finalResponse = new Response(sseStream, {
status: 200,
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "X-Accel-Buffering": "no" },
});
} else {
finalResponse = await buildNonStreamingResponse(
response.body, model, cid, created, isThinking, signal,
);
}
return { response: finalResponse, url: GROK_CHAT_API, headers, transformedBody: grokPayload };
}
}
+3
View File
@@ -12,6 +12,7 @@ import { OpencodeExecutor } from "./opencode.ts";
import { PuterExecutor } from "./puter.ts";
import { VertexExecutor } from "./vertex.ts";
import { CliproxyapiExecutor } from "./cliproxyapi.ts";
import { GrokWebExecutor } from "./grok-web.ts";
const executors = {
antigravity: new AntigravityExecutor(),
@@ -33,6 +34,7 @@ const executors = {
vertex: new VertexExecutor(),
cliproxyapi: new CliproxyapiExecutor(),
cpa: new CliproxyapiExecutor(), // Alias
"grok-web": new GrokWebExecutor(),
};
const defaultCache = new Map();
@@ -62,3 +64,4 @@ export { OpencodeExecutor } from "./opencode.ts";
export { PuterExecutor } from "./puter.ts";
export { CliproxyapiExecutor } from "./cliproxyapi.ts";
export { VertexExecutor } from "./vertex.ts";
export { GrokWebExecutor } from "./grok-web.ts";
+10
View File
@@ -214,6 +214,16 @@ export const APIKEY_PROVIDERS = {
textIcon: "XA",
website: "https://x.ai",
},
"grok-web": {
id: "grok-web",
alias: "grok-web",
name: "Grok Web (Subscription)",
icon: "auto_awesome",
color: "#1DA1F2",
textIcon: "GW",
website: "https://grok.com",
authHint: "Paste your sso cookie value from grok.com (found in browser DevTools → Cookies → sso)",
},
mistral: {
id: "mistral",
alias: "mistral",
+353
View File
@@ -0,0 +1,353 @@
import test from "node:test";
import assert from "node:assert/strict";
const { GrokWebExecutor } = await import("../../open-sse/executors/grok-web.ts");
const { getExecutor, hasSpecializedExecutor } = await import("../../open-sse/executors/index.ts");
// ─── Helpers ────────────────────────────────────────────────────────────────
function mockGrokStream(events: unknown[]) {
const encoder = new TextEncoder();
const lines = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
return new ReadableStream({
start(controller) {
controller.enqueue(encoder.encode(lines));
controller.close();
},
});
}
function mockFetch(status: number, events: unknown[]) {
const original = globalThis.fetch;
globalThis.fetch = async () =>
new Response(mockGrokStream(events), {
status,
headers: { "Content-Type": "application/json" },
});
return () => { globalThis.fetch = original; };
}
function mockFetchCapture(events: unknown[]) {
const original = globalThis.fetch;
let capturedUrl: string | null = null;
let capturedHeaders: Record<string, string> = {};
let capturedBody: Record<string, unknown> = {};
globalThis.fetch = async (url: any, opts: any) => {
capturedUrl = String(url);
capturedHeaders = opts?.headers || {};
capturedBody = JSON.parse(opts?.body || "{}");
return new Response(mockGrokStream(events), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
return {
restore: () => { globalThis.fetch = original; },
get url() { return capturedUrl; },
get headers() { return capturedHeaders; },
get body() { return capturedBody; },
};
}
const SIMPLE_RESPONSE = [
{ result: { response: { token: "Hello" } } },
{ result: { response: { token: " world!" } } },
{ result: { response: { modelResponse: { message: "Hello world!", responseId: "resp-123" } } } },
];
// ─── Registration ───────────────────────────────────────────────────────────
test("GrokWebExecutor is registered in executor index", () => {
assert.ok(hasSpecializedExecutor("grok-web"));
const executor = getExecutor("grok-web");
assert.ok(executor instanceof GrokWebExecutor);
});
test("GrokWebExecutor sets correct provider name", () => {
const executor = new GrokWebExecutor();
assert.equal(executor.getProvider(), "grok-web");
});
// ─── Non-streaming ──────────────────────────────────────────────────────────
test("Non-streaming: simple response", async () => {
const restore = mockFetch(200, SIMPLE_RESPONSE);
try {
const executor = new GrokWebExecutor();
const result = await executor.execute({
model: "grok-4.1-fast",
body: { messages: [{ role: "user", content: "hi" }], stream: false },
stream: false,
credentials: { apiKey: "test-sso-token" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(result.response.status, 200);
const json = await result.response.json();
assert.equal(json.object, "chat.completion");
assert.equal(json.choices[0].message.role, "assistant");
assert.equal(json.choices[0].message.content, "Hello world!");
assert.equal(json.choices[0].finish_reason, "stop");
assert.ok(json.id.startsWith("chatcmpl-grok-"));
} finally { restore(); }
});
// ─── Streaming ──────────────────────────────────────────────────────────────
test("Streaming: produces valid SSE chunks", async () => {
const restore = mockFetch(200, SIMPLE_RESPONSE);
try {
const executor = new GrokWebExecutor();
const result = await executor.execute({
model: "grok-4.1-fast",
body: { messages: [{ role: "user", content: "hello" }], stream: true },
stream: true,
credentials: { apiKey: "test-sso" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(result.response.status, 200);
assert.equal(result.response.headers.get("Content-Type"), "text/event-stream");
const text = await result.response.text();
const lines = text.split("\n").filter((l: string) => l.startsWith("data: "));
assert.ok(lines.length >= 3, `Expected at least 3 SSE data lines, got ${lines.length}`);
// First chunk has role
const first = JSON.parse(lines[0].slice(6));
assert.equal(first.choices[0].delta.role, "assistant");
// Last line is [DONE]
const lastLine = text.trim().split("\n").filter(Boolean).pop();
assert.equal(lastLine, "data: [DONE]");
} finally { restore(); }
});
// ─── Error handling ─────────────────────────────────────────────────────────
test("Error: 401 returns auth error", async () => {
const restore = mockFetch(401, []);
try {
const executor = new GrokWebExecutor();
const result = await executor.execute({
model: "grok-4",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: { apiKey: "expired" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(result.response.status, 401);
const json = await result.response.json();
assert.ok(json.error.message.includes("auth failed"));
assert.ok(json.error.message.includes("sso"));
} finally { restore(); }
});
test("Error: 429 returns rate limit message", async () => {
const restore = mockFetch(429, []);
try {
const executor = new GrokWebExecutor();
const result = await executor.execute({
model: "grok-4",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: { apiKey: "test" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(result.response.status, 429);
const json = await result.response.json();
assert.ok(json.error.message.includes("rate limited"));
} finally { restore(); }
});
test("Error: empty messages returns 400", async () => {
const executor = new GrokWebExecutor();
const result = await executor.execute({
model: "grok-4",
body: { messages: [] },
stream: false,
credentials: { apiKey: "test" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(result.response.status, 400);
});
test("Error: Grok stream error returns 502", async () => {
const restore = mockFetch(200, [{ error: { message: "Internal error", code: "500" } }]);
try {
const executor = new GrokWebExecutor();
const result = await executor.execute({
model: "grok-4",
body: { messages: [{ role: "user", content: "test" }], stream: false },
stream: false,
credentials: { apiKey: "test" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(result.response.status, 502);
const json = await result.response.json();
assert.ok(json.error.message.includes("Internal error"));
} finally { restore(); }
});
// ─── Auth headers ───────────────────────────────────────────────────────────
test("Auth: cookie sends sso= header", async () => {
const cap = mockFetchCapture(SIMPLE_RESPONSE);
try {
const executor = new GrokWebExecutor();
await executor.execute({
model: "grok-4.1-fast",
body: { messages: [{ role: "user", content: "test" }], stream: false },
stream: false,
credentials: { apiKey: "my-sso-token-value" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(cap.headers["Cookie"], "sso=my-sso-token-value");
} finally { cap.restore(); }
});
test("Auth: strips sso= prefix if user included it", async () => {
const cap = mockFetchCapture(SIMPLE_RESPONSE);
try {
const executor = new GrokWebExecutor();
await executor.execute({
model: "grok-4.1-fast",
body: { messages: [{ role: "user", content: "test" }], stream: false },
stream: false,
credentials: { apiKey: "sso=my-token" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(cap.headers["Cookie"], "sso=my-token");
assert.ok(!cap.headers["Cookie"].includes("sso=sso="));
} finally { cap.restore(); }
});
// ─── Request format ─────────────────────────────────────────────────────────
test("Request: posts to correct Grok endpoint", async () => {
const cap = mockFetchCapture(SIMPLE_RESPONSE);
try {
const executor = new GrokWebExecutor();
await executor.execute({
model: "grok-4.1-fast",
body: { messages: [{ role: "user", content: "test" }], stream: false },
stream: false,
credentials: { apiKey: "test" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(cap.url, "https://grok.com/rest/app-chat/conversations/new");
assert.equal(cap.headers["Origin"], "https://grok.com");
assert.ok(cap.headers["x-statsig-id"], "Should have x-statsig-id header");
assert.ok(cap.headers["x-xai-request-id"], "Should have x-xai-request-id header");
assert.ok(cap.headers["traceparent"]?.startsWith("00-"), "Should have W3C traceparent");
} finally { cap.restore(); }
});
test("Request: payload has correct model mapping", async () => {
const cap = mockFetchCapture(SIMPLE_RESPONSE);
try {
const executor = new GrokWebExecutor();
await executor.execute({
model: "grok-4.1-expert",
body: { messages: [{ role: "user", content: "test" }], stream: false },
stream: false,
credentials: { apiKey: "test" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(cap.body.modelName, "grok-4-1-thinking-1129");
assert.equal(cap.body.modelMode, "MODEL_MODE_EXPERT");
assert.equal(cap.body.temporary, true);
} finally { cap.restore(); }
});
test("Request: grok-4-heavy maps to heavy mode", async () => {
const cap = mockFetchCapture(SIMPLE_RESPONSE);
try {
const executor = new GrokWebExecutor();
await executor.execute({
model: "grok-4-heavy",
body: { messages: [{ role: "user", content: "test" }], stream: false },
stream: false,
credentials: { apiKey: "test" },
signal: AbortSignal.timeout(10000),
log: null,
});
assert.equal(cap.body.modelName, "grok-4");
assert.equal(cap.body.modelMode, "MODEL_MODE_HEAVY");
} finally { cap.restore(); }
});
// ─── Message parsing ────────────────────────────────────────────────────────
test("Message parsing: combines system + history + user", async () => {
const cap = mockFetchCapture(SIMPLE_RESPONSE);
try {
const executor = new GrokWebExecutor();
await executor.execute({
model: "grok-4.1-fast",
body: {
messages: [
{ role: "system", content: "Be helpful" },
{ role: "user", content: "First question" },
{ role: "assistant", content: "First answer" },
{ role: "user", content: "Follow up" },
],
stream: false,
},
stream: false,
credentials: { apiKey: "test" },
signal: AbortSignal.timeout(10000),
log: null,
});
const msg = cap.body.message as string;
assert.ok(msg.includes("Follow up"), "Should contain current user message");
assert.ok(msg.includes("Be helpful"), "Should contain system message");
assert.ok(msg.includes("First answer"), "Should contain assistant history");
} finally { cap.restore(); }
});
// ─── Provider registry ──────────────────────────────────────────────────────
test("Provider registry: grok-web has correct models", async () => {
const { PROVIDER_MODELS } = await import("../../open-sse/config/providerModels.ts");
const models = PROVIDER_MODELS["grok-web"];
assert.ok(models, "grok-web should be in PROVIDER_MODELS");
assert.equal(models.length, 12, `Expected 12 models, got ${models.length}`);
const ids = models.map((m: any) => m.id);
assert.ok(ids.includes("grok-3"));
assert.ok(ids.includes("grok-4"));
assert.ok(ids.includes("grok-4-heavy"));
assert.ok(ids.includes("grok-4.1-fast"));
assert.ok(ids.includes("grok-4.1-expert"));
assert.ok(ids.includes("grok-4.1-thinking"));
assert.ok(ids.includes("grok-4.2"));
});
// ─── Statsig header ─────────────────────────────────────────────────────────
test("Statsig: x-statsig-id is valid base64", async () => {
const cap = mockFetchCapture(SIMPLE_RESPONSE);
try {
const executor = new GrokWebExecutor();
await executor.execute({
model: "grok-4.1-fast",
body: { messages: [{ role: "user", content: "test" }], stream: false },
stream: false,
credentials: { apiKey: "test" },
signal: AbortSignal.timeout(10000),
log: null,
});
const statsig = cap.headers["x-statsig-id"];
assert.ok(statsig, "Should have statsig header");
const decoded = atob(statsig);
assert.ok(decoded.startsWith("e:TypeError:"), `Decoded statsig should start with e:TypeError:, got: ${decoded}`);
} finally { cap.restore(); }
});