mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 02:42:24 +03:00
feat(providers): wire chatgpt-session into the dashboard and validation
This commit is contained in:
@@ -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,
|
||||
|
||||
93
src/lib/providers/validation/chatgptSession.ts
Normal file
93
src/lib/providers/validation/chatgptSession.ts
Normal 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),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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)",
|
||||
|
||||
47
tests/unit/chatgpt-session-validation.test.ts
Normal file
47
tests/unit/chatgpt-session-validation.test.ts
Normal 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);
|
||||
});
|
||||
Reference in New Issue
Block a user