feat(codex): xAI (Grok) OAuth managed provider with native Responses compat

Add a managed "xAI (Grok) OAuth" Codex provider that routes through the
local proxy to api.x.ai via the shared Grok CLI OAuth identity, plus the
native-Responses compatibility layer that makes Codex 0.142+ traffic work
against xAI's strict upstream serde parser.

Provider:
- codex.rs: recognize the xai_oauth placeholder in extract_auth, hard-pin
  the base URL to api.x.ai and the tool profile to native Responses
- forwarder.rs: treat xAI OAuth auth failures as non-retryable
- presets + ProviderForm/CodexFormFields: managed OAuth preset that hides
  the api key/endpoint fields and derives the provider type across apps

Native Responses compatibility (gated on is_xai_oauth, so no other
provider is affected):
- transform_codex_responses_namespace: flatten Codex's private
  namespace/plugin tool declarations into top-level function tools on the
  request; restore the flat function_call names back to {name, namespace}
  on the response (streaming and non-streaming) so the client matches its
  own namespaced tool registry
- transform_codex_responses_xai_sanitize: strip the OpenAI-backend-private
  fields xAI rejects (external_web_access, prompt_cache_retention,
  safety_identifier, the additional_tools carrier, tool_search, ...) with
  deterministic removals that keep the prompt-cache prefix stable
- wire both into the native passthrough after the request transform;
  response restore runs in a dedicated handler so the generic passthrough
  hot path is untouched

Ports the proven approach of sub2api's Grok Responses gateway. Verified
with a 4-round codex -> xAI OAuth workload: all tasks green, zero upstream
errors.
This commit is contained in:
Jason
2026-07-20 23:50:24 +08:00
parent 8dcedbc062
commit dbb5bd1537
12 changed files with 1734 additions and 54 deletions
+38 -16
View File
@@ -717,6 +717,17 @@ function ProviderFormFull({
}));
}, [appId]);
// 预设声明的托管身份类型(github_copilot / codex_oauth / xai_oauth)。
// 跨应用通用:claude 的 templatePreset 与此查同一张 presetEntries 表,
// codex 等其它应用没有 templatePreset,只能走这里。
const presetProviderType = useMemo(() => {
if (!selectedPresetId) return undefined;
const preset = presetEntries.find(
(entry) => entry.id === selectedPresetId,
)?.preset;
return preset && "providerType" in preset ? preset.providerType : undefined;
}, [presetEntries, selectedPresetId]);
const {
templateValues,
templateValueEntries,
@@ -1153,14 +1164,14 @@ function ProviderFormFull({
// OAuth 未登录:B 类(token 根本不存在,保存了也没法建立)
const isCopilotProvider =
templatePreset?.providerType === "github_copilot" ||
presetProviderType === "github_copilot" ||
initialData?.meta?.providerType === "github_copilot" ||
baseUrl.includes("githubcopilot.com");
const isCodexOauthProvider =
templatePreset?.providerType === "codex_oauth" ||
presetProviderType === "codex_oauth" ||
initialData?.meta?.providerType === "codex_oauth";
const isXaiOauthProvider =
templatePreset?.providerType === "xai_oauth" ||
presetProviderType === "xai_oauth" ||
initialData?.meta?.providerType === "xai_oauth";
if (isCopilotProvider && !isCopilotAuthenticated) {
toast.error(
@@ -1284,14 +1295,16 @@ function ProviderFormFull({
);
}
} else if (appId === "codex") {
if (!codexBaseUrl.trim()) {
// 托管 OAuth 预设(xAI):端点由 adapter 硬定向、token 由代理注入,
// 两项都不需要用户填写
if (!isXaiOauthProvider && !codexBaseUrl.trim()) {
issues.push(
t("providerForm.endpointRequired", {
defaultValue: "非官方供应商请填写 API 端点",
}),
);
}
if (!codexApiKey.trim()) {
if (!isXaiOauthProvider && !codexApiKey.trim()) {
issues.push(
t("providerForm.apiKeyRequired", {
defaultValue: "非官方供应商请填写 API Key",
@@ -1343,14 +1356,14 @@ function ProviderFormFull({
// OAuth / 其它身份识别(与 handleSubmit 保持一致)
const isCopilotProvider =
templatePreset?.providerType === "github_copilot" ||
presetProviderType === "github_copilot" ||
initialData?.meta?.providerType === "github_copilot" ||
baseUrl.includes("githubcopilot.com");
const isCodexOauthProvider =
templatePreset?.providerType === "codex_oauth" ||
presetProviderType === "codex_oauth" ||
initialData?.meta?.providerType === "codex_oauth";
const isXaiOauthProvider =
templatePreset?.providerType === "xai_oauth" ||
presetProviderType === "xai_oauth" ||
initialData?.meta?.providerType === "xai_oauth";
let settingsConfig: string;
@@ -1529,7 +1542,7 @@ function ProviderFormFull({
// 确定 providerType(新建时从预设获取,编辑时从现有数据获取)
const providerType =
templatePreset?.providerType || initialData?.meta?.providerType;
presetProviderType || initialData?.meta?.providerType;
const nextMeta: ProviderMeta = {
...(baseMeta ?? {}),
@@ -1603,7 +1616,9 @@ function ProviderFormFull({
? "openai_responses"
: localApiFormat
: appId === "codex" && category !== "official"
? localCodexApiFormat
? isXaiOauthProvider
? "openai_responses"
: localCodexApiFormat
: undefined,
apiKeyField:
appId === "claude" &&
@@ -2186,26 +2201,26 @@ function ProviderFormFull({
isPartner={isClaudePartner}
partnerPromotionKey={claudePartnerPromotionKey}
isCopilotPreset={
templatePreset?.providerType === "github_copilot" ||
presetProviderType === "github_copilot" ||
initialData?.meta?.providerType === "github_copilot" ||
baseUrl.includes("githubcopilot.com")
}
isCodexOauthPreset={
templatePreset?.providerType === "codex_oauth" ||
presetProviderType === "codex_oauth" ||
initialData?.meta?.providerType === "codex_oauth"
}
isXaiOauthPreset={
templatePreset?.providerType === "xai_oauth" ||
presetProviderType === "xai_oauth" ||
initialData?.meta?.providerType === "xai_oauth"
}
usesOAuth={
templatePreset?.requiresOAuth === true ||
templatePreset?.providerType === "github_copilot" ||
presetProviderType === "github_copilot" ||
initialData?.meta?.providerType === "github_copilot" ||
baseUrl.includes("githubcopilot.com") ||
templatePreset?.providerType === "codex_oauth" ||
presetProviderType === "codex_oauth" ||
initialData?.meta?.providerType === "codex_oauth" ||
templatePreset?.providerType === "xai_oauth" ||
presetProviderType === "xai_oauth" ||
initialData?.meta?.providerType === "xai_oauth"
}
isCopilotAuthenticated={isCopilotAuthenticated}
@@ -2265,6 +2280,13 @@ function ProviderFormFull({
{appId === "codex" && (
<CodexFormFields
providerId={providerId}
isXaiOauthPreset={
presetProviderType === "xai_oauth" ||
initialData?.meta?.providerType === "xai_oauth"
}
isXaiOauthAuthenticated={isXaiOauthAuthenticated}
selectedXaiAccountId={selectedXaiAccountId}
onXaiAccountSelect={setSelectedXaiAccountId}
codexApiKey={codexApiKey}
onApiKeyChange={handleCodexApiKeyChange}
category={category}