Files
OmniRoute/src/shared/constants/clientIdentityProfiles.ts
backryun 67a0b99240 feat(providers): add GPT-5.6 model family (#6862)
* feat(providers): add GPT-5.6 model family

* fix(chatgpt-web): resume temporary chat handoffs

* fix(codex): auto-merge discovery, filter denylist, revalidate on lifecycle

Restore live/GitHub auto-merge for Codex catalogs, drop models via
explicit denylist (GPT-5.4 family), and run scrub+live re-sync once on
first-start, app upgrade, or setup completion. Success log:
kill deprecated models complete.

* fix(codex): preserve live catalog reconciliation

Expose remote-only Codex models without dropping user custom entries, and complete lifecycle revalidation only after a successful internal sync. Keep credentialed self-fetches pinned to the active dashboard listener.

---------

Co-authored-by: backryun <backryun@daonlab.local>
2026-07-12 03:30:47 -03:00

91 lines
3.1 KiB
TypeScript

/**
* Named "client identity" header presets for OpenAI-/Anthropic-compatible
* provider nodes (e.g. mimicking a known CLI's `User-Agent`).
*
* This module is intentionally dumb: it only supplies preset header
* VALUES. It introduces NO new header-merge path — selecting a profile in
* the compatible-provider UI merges its headers into the SAME
* `providerSpecificData.customHeaders` field already wired end-to-end
* (node -> connection -> `DefaultExecutor.buildHeaders()` ->
* `applyCustomHeaders()` in `open-sse/executors/default.ts`), which already
* sanitizes via `isForbiddenCustomHeaderName` (`upstreamHeaders.ts`) and is
* applied AFTER the credential-auth headers are set. Auth/cookie headers
* therefore always win over anything a profile (or a hand-edited custom
* header) tries to set — no new precedence logic is needed here.
*/
export interface ClientIdentityProfile {
readonly id: string;
readonly label: string;
readonly headers: Readonly<Record<string, string>>;
}
const DEFAULT_PROFILE: ClientIdentityProfile = Object.freeze({
id: "default",
label: "Default",
headers: Object.freeze({}),
});
const CLAUDE_CLI_PROFILE: ClientIdentityProfile = Object.freeze({
id: "claude-cli",
label: "Claude CLI",
headers: Object.freeze({
"User-Agent": "claude-cli/2.1.207 (external, cli)",
"X-App": "cli",
}),
});
const CODEX_CLI_PROFILE: ClientIdentityProfile = Object.freeze({
id: "codex-cli",
label: "Codex CLI",
headers: Object.freeze({
"User-Agent": "codex_cli_rs/0.144.1",
originator: "codex_cli_rs",
}),
});
const GEMINI_CLI_PROFILE: ClientIdentityProfile = Object.freeze({
id: "gemini-cli",
label: "Gemini CLI",
headers: Object.freeze({
"User-Agent": "GeminiCLI/0.1.0 (linux; x64)",
}),
});
/** Ordered so `CLIENT_IDENTITY_PROFILE_OPTIONS` renders "Default" first. */
export const CLIENT_IDENTITY_PROFILES: Readonly<Record<string, ClientIdentityProfile>> =
Object.freeze({
default: DEFAULT_PROFILE,
"claude-cli": CLAUDE_CLI_PROFILE,
"codex-cli": CODEX_CLI_PROFILE,
"gemini-cli": GEMINI_CLI_PROFILE,
});
export const CLIENT_IDENTITY_PROFILE_IDS: readonly string[] = Object.keys(CLIENT_IDENTITY_PROFILES);
export const CLIENT_IDENTITY_PROFILE_OPTIONS: ReadonlyArray<{ value: string; label: string }> =
CLIENT_IDENTITY_PROFILE_IDS.map((id) => ({
value: id,
label: CLIENT_IDENTITY_PROFILES[id].label,
}));
export function isClientIdentityProfileId(value: unknown): value is string {
return (
typeof value === "string" &&
Object.prototype.hasOwnProperty.call(CLIENT_IDENTITY_PROFILES, value)
);
}
/**
* Returns a plain (mutable, unfrozen) copy of the preset's headers so callers
* can safely spread/merge it into `providerSpecificData.customHeaders`
* without ever needing to sanitize here — that happens downstream in
* `applyCustomHeaders()` regardless of where the header entries came from.
*/
export function getClientIdentityProfileHeaders(
profileId: string | undefined | null
): Record<string, string> {
if (!isClientIdentityProfileId(profileId)) return {};
return { ...CLIENT_IDENTITY_PROFILES[profileId].headers };
}