feat(providers): wire chatgpt-session into the dashboard and validation

This commit is contained in:
diegosouzapw
2026-09-02 03:20:31 -03:00
parent 7583ae0e9e
commit abd720029b
6 changed files with 165 additions and 0 deletions

View File

@@ -80,6 +80,7 @@ import {
validatePoeProvider,
} from "./validation/audioMiscProviders";
import { validateChatGptWebCodexProvider } from "./validation/chatgptWebCodex";
import { validateChatGptSessionProvider } from "./validation/chatgptSession";
import { validateZaiWebProvider } from "./validation/zaiWeb";
import { validateSearchProvider, SEARCH_VALIDATOR_CONFIGS } from "./validation/searchProviders";
import {
@@ -309,6 +310,7 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
"grok-web": validateGrokWebProvider,
"kimi-web": validateKimiWebProvider,
"chatgpt-web-codex": validateChatGptWebCodexProvider,
"chatgpt-session": validateChatGptSessionProvider,
"perplexity-web": validatePerplexityWebProvider,
"blackbox-web": validateBlackboxWebProvider,
"muse-spark-web": validateMuseSparkWebProvider,

View File

@@ -0,0 +1,93 @@
import { randomBytes } from "node:crypto";
import { rmSync } from "node:fs";
import { CHATGPT_WEB_CODEX_CONNECTOR_NAME } from "@/shared/constants/chatgptWebCodex";
import { inspectBrowserLoginCapabilities } from "@omniroute/open-sse/vendor/codex-chatgpt-web/browser-login.ts";
import { decodeChatGptWebCodexSecrets } from "@omniroute/open-sse/executors/chatgpt-web-codex/credentials.ts";
import { detectChromeExecutable } from "@omniroute/open-sse/executors/chatgpt-web-codex.ts";
import {
connectionRuntimePaths,
ensureConnectionStorageState,
ensureConnectionStorageStateFromCredential,
} from "@omniroute/open-sse/executors/chatgpt-web-codex/storageState.ts";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
export async function validateChatGptSessionProvider({
apiKey,
providerSpecificData = {},
}: {
apiKey?: string;
providerSpecificData?: Record<string, unknown>;
}) {
try {
const secrets = decodeChatGptWebCodexSecrets(String(apiKey || ""));
if (!secrets.cookie && !secrets.storageState) {
return {
valid: false,
error: "A ChatGPT cookie header or a stored browser session is required.",
};
}
const cdpEndpoint = process.env.CHATGPT_WEB_CODEX_CDP_URL?.trim();
const chromeExecutablePath = detectChromeExecutable(
typeof providerSpecificData.chromeExecutablePath === "string"
? providerSpecificData.chromeExecutablePath
: undefined
);
if (!chromeExecutablePath && !cdpEndpoint) {
return {
valid: false,
error:
"No supported Chrome or Chromium was found. Install Chromium or configure the browser path.",
};
}
const validationId = `validation-${randomBytes(12).toString("hex")}`;
const paths = connectionRuntimePaths(validationId);
const freshCookie = Boolean(secrets.cookie);
if (secrets.cookie) ensureConnectionStorageState(validationId, secrets.cookie);
else ensureConnectionStorageStateFromCredential(validationId, secrets);
let capabilities;
try {
capabilities = await inspectBrowserLoginCapabilities({
appName: CHATGPT_WEB_CODEX_CONNECTOR_NAME,
...(chromeExecutablePath ? { chromeExecutablePath } : {}),
...(cdpEndpoint ? { cdpEndpoint } : {}),
storageStatePath: paths.storageStatePath,
headed: false,
proAvailable: false,
autoApproveToolCalls: false,
});
} catch (error) {
rmSync(paths.root, { recursive: true, force: true });
throw error;
}
if (!freshCookie) rmSync(paths.root, { recursive: true, force: true });
return {
valid: true,
error: null,
method: "headless-browser",
capabilities: {
browser: "ready",
storageState: "verified",
login: "authenticated",
solAvailable: capabilities.solAvailable,
proAvailable: capabilities.proAvailable,
},
providerSpecificData: {
solAvailable: capabilities.solAvailable,
proAvailable: capabilities.proAvailable,
browserVerified: true,
...(chromeExecutablePath ? { chromeExecutablePath } : {}),
...(freshCookie ? { validationId } : {}),
},
};
} catch (error) {
return {
valid: false,
error: sanitizeErrorMessage(error instanceof Error ? error.message : error),
};
}
}

View File

@@ -329,6 +329,7 @@ const LOBE_PROVIDER_ALIASES = {
"black-forest-labs": "Bfl",
cerebras: "Cerebras",
"chatgpt-web-codex": "OpenAI",
"chatgpt-session": "OpenAI",
claude: "ClaudeCode",
"claude-web": "Claude",
cline: "Cline",

View File

@@ -18,6 +18,21 @@ export const WEB_COOKIE_PROVIDERS = {
riskNoticeVariant: "webCookie",
toolCalling: "native",
},
"chatgpt-session": {
id: "chatgpt-session",
serviceKinds: ["llm"],
alias: "cgpt-session",
name: "ChatGPT Web (Session)",
icon: "chat",
color: "#10A37F",
textIcon: "CS",
website: "https://chatgpt.com",
authHint:
"Paste the full ChatGPT Cookie header. OmniRoute verifies it in an isolated browser profile and then stores only the verified session state.",
subscriptionRisk: true,
riskNoticeVariant: "webCookie",
toolCalling: "emulated",
},
"grok-web": {
id: "grok-web",
serviceKinds: ["llm"],

View File

@@ -35,6 +35,13 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = {
acceptsFullCookieHeader: true,
storageKeys: ["cookie", "sessionToken", "session-token", "__Secure-next-auth.session-token"],
},
"chatgpt-session": {
kind: "cookie",
credentialName: "ChatGPT Cookie header (full)",
placeholder: "__Secure-next-auth.session-token=...; cf_clearance=...",
acceptsFullCookieHeader: true,
storageKeys: ["cookie", "sessionToken", "session-token", "__Secure-next-auth.session-token"],
},
"zenmux-free": {
kind: "cookie",
credentialName: "Cookie header (full)",

View File

@@ -0,0 +1,47 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { WEB_COOKIE_PROVIDERS } from "../../src/shared/constants/providers/web-cookie.ts";
import { WEB_SESSION_CREDENTIAL_REQUIREMENTS } from "../../src/shared/providers/webSessionCredentials.ts";
test("the dashboard card describes the session provider", () => {
const card = (WEB_COOKIE_PROVIDERS as Record<string, Record<string, unknown>>)[
"chatgpt-session"
];
assert.ok(card, "chatgpt-session must have a web-cookie card");
assert.equal(card.alias, "cgpt-session");
assert.equal(card.website, "https://chatgpt.com");
assert.equal(card.subscriptionRisk, true);
assert.equal(card.riskNoticeVariant, "webCookie");
assert.equal(card.toolCalling, "emulated");
});
test("the credential requirement accepts a full cookie header", () => {
const requirement = (
WEB_SESSION_CREDENTIAL_REQUIREMENTS as Record<string, Record<string, unknown>>
)["chatgpt-session"];
assert.ok(requirement);
assert.equal(requirement.kind, "cookie");
assert.equal(requirement.acceptsFullCookieHeader, true);
});
test("validation is registered for the provider id", async () => {
const module = await import("../../src/lib/providers/validation.ts");
const source = await import("node:fs").then((fs) =>
fs.readFileSync(
new URL("../../src/lib/providers/validation.ts", import.meta.url),
"utf8"
)
);
assert.match(source, /"chatgpt-session":\s*validateChatGptSessionProvider/);
assert.ok(module);
});
test("validation rejects an empty credential without launching a browser", async () => {
const { validateChatGptSessionProvider } = await import(
"../../src/lib/providers/validation/chatgptSession.ts"
);
const result = await validateChatGptSessionProvider({ apiKey: "" });
assert.equal(result.valid, false);
assert.match(String(result.error), /cookie|credential/i);
});