メインコンテンツまでスキップ

「Codex」タグの記事が1件件あります

全てのタグを見る

Codex 移动端使用第三方中转站

· 約8分
MuskAI
MuskAI Team

核心原理​

利用 Codex 客户端 Auth层(负责账户验证、权益)与 Model层(负责实际对话请求)解耦的设计。

  • Auth 层:继续使用 ChatGPT 账号登录,以获得其账户身份和所有功能权益。

  • Model 层:通过配置,将其请求指向支持 OpenAI responses API 的第三方中转服务。

准备工作​

  1. 账户:一个有效的 ChatGPT 账号(免费版即可)。
  2. 中转服务:一个支持 OpenAI responses API 模式的中转站账号
  3. 配置文件:找到 Codex 本地的两个配置文件准备修改:

​ * ~/.codex/auth.json

​ * ~/.codex/config.toml

详细配置步骤​

第一步:正常登录​

首先,在 Codex 客户端中,像平常一样登录你的 ChatGPT 账号,并确保登录成功、状态正常。

第二步:修改认证配置 (auth.json)​

找到并编辑文件 ~/.codex/auth.json。

关键修改项:

  • 将 "auth_mode" 的值改为 "chatgpt"。

  • 将 "OPENAI_API_KEY" 的值设为 null。

目的:确保客户端使用 ChatGPT 方式进行身份验证,并且不使用官方的 OpenAI API Key,从而为切换模型请求层铺平道路。

第三步:修改模型提供商配置 (config.toml)​

找到并编辑文件 ~/.codex/config.toml,在文件的末尾追加以下配置段落:

[[providers]]


id = "MuskAI" # 提供商ID,可自定义


name = "MuskAI" # 提供商显示名称,可自定义


base_url = "在官网用户中心 -【API密钥】页面复制的 Base URL"


models = [] # 保持为空数组,模型列表会从中转站动态拉取


experimental_bearer_token = "sk-xxxxxxxxxxxx" # 替换为你的 API Key


wire_api = "responses" # 必须设置为 "responses"


requires_openai_auth = true # 必须为 true,用于绕过客户端的某些检测

配置项解释:

  • base_url:登录官网后,在用户中心 -【API密钥】页面复制的 Base URL。
  • experimental_bearer_token:你在该中转服务的 API Key。
  • wire_api:这是最关键的一项,必须设置为 "responses"。这指示 Codex 使用 OpenAI 的 /v1/responses 端点格式发送请求,而非常见的 /v1/chat/completions。大部分中转服务需要特别配置才能支持此端点。
  • requires_openai_auth:必须设置为 true。这会使客户端认为当前仍在使用 OpenAI 的认证,从而顺利通过内部检查,维持 ChatGPT 账户的登录状态和功能。

第四步:验证配置​

  1. 重启 Codex 客户端,使配置生效。
  2. 在客户端中发送一条测试消息。
  3. 登录MuskAI服务控制台,查看“用量”或“请求日志”。如果能看到刚刚测试对话产生的 API 调用记录,则说明配置成功,请求已被转发到第三方服务。

重要注意事项​

  1. 历史记录丢失:切换模型提供商后,之前的所有对话历史记录会消失。因为对话记录是与特定的模型提供商绑定的,更换后无法读取之前的记录。
  2. 模型名称格式:在中转站(如 MuskAi)中配置可用模型时,模型名称需要包含提供商前缀。例如,如果你想使用 GPT-5.4,模型名称应配置为 openai/gpt-5.4,而不仅仅是 gpt-5.4。
  3. API 端点要求:该方法成功的关键在于,你选择的第三方中转服务必须支持 OpenAI 的 /v1/responses 端点。如果中转站只支持常见的 /v1/chat/completions 端点,则 Codex 的请求会失败。
  4. 多端同步:此配置修改后,Codex 的桌面端和移动端的功能与状态将保持同步。

最终效果​

通过以上配置,你实现了:

  • 身份与功能:客户端界面、账户权益(如 Plus 标志、插件、移动版解锁)仍由你的原始 ChatGPT 账户提供。
  • 算力与模型:所有你发起的对话请求,其实际处理的计算资源和模型,都来自于你配置的第三方中转 API 服务。

这使得你可以在享受 ChatGPT 官方客户端体验的同时,灵活地选用更经济、更快或更定制化的模型服务。

补充

看到很多人写的修改配置文件,但是不能连接windows端的codex,是因为windows端的codex它默认会删除配置字段。把配置文件改成只读,会让codex修改不了配置文件。这样就可以用手机访问codex了。放图片: 改成只读配置:

image

连接完成图片:

a6feb854e2e87ca443b89020d02cc460

脚本文件,改成ps1后缀

$ErrorActionPreference = "Stop"


$workspace = "C:\Users\11854\Documents\Codex\2026-05-16\chatgpt-codex"
$package = Get-AppxPackage OpenAI.Codex
if (-not $package) {
throw "OpenAI.Codex package not found."
}


$asar = Join-Path $package.InstallLocation "app\resources\app.asar"
$patched = Join-Path $workspace ("app.asar.patched-" + $package.Version)
$backup = Join-Path $workspace ("app.asar.backup-" + $package.Version + "-" + (Get-Date -Format "yyyyMMdd-HHmmss"))
$log = Join-Path $workspace "patch-current-codex-asar.log"
$config = "C:\Users\11854\.codex\config.toml"


function Log($message) {
"$(Get-Date -Format o) $message" | Add-Content -LiteralPath $log -Encoding UTF8
}


function IndexOfBytes([byte[]]$haystack, [byte[]]$needle, [int]$start) {
for ($i = $start; $i -le $haystack.Length - $needle.Length; $i++) {
$ok = $true
for ($j = 0; $j -lt $needle.Length; $j++) {
if ($haystack[$i + $j] -ne $needle[$j]) {
$ok = $false
break
}
}
if ($ok) { return $i }
}
return -1
}


function New-PatchedAsar {
$bytes = [System.IO.File]::ReadAllBytes($asar)
$encoding = [System.Text.Encoding]::UTF8
$text = $encoding.GetString($bytes)
$pattern = "function xV(e){return Object.hasOwn(e,``remote_control``)||SV(e.features)&&Object.hasOwn(e.features,``remote_control``)}"
$patchedPattern = "function xV(e){return Object.hasOwn(e,``remote_xontrol``)||SV(e.features)&&Object.hasOwn(e.features,``remote_xontrol``)}"
$needle = "remote_control"
$replacement = [System.Text.Encoding]::UTF8.GetBytes("remote_xontrol")


$idx = $text.IndexOf($pattern, [System.StringComparison]::Ordinal)
if ($idx -lt 0) {
if ($text.IndexOf($patchedPattern, [System.StringComparison]::Ordinal) -ge 0) {
Log "Installed app.asar already appears patched."
return $false
}
throw "Target xV function pattern not found."
}


$positions = @()
$pos = $idx
$end = $idx + $pattern.Length
while (($pos = $text.IndexOf($needle, $pos, [System.StringComparison]::Ordinal)) -ge 0 -and $pos -lt $end) {
$positions += $encoding.GetByteCount($text.Substring(0, $pos))
$pos += $needle.Length
}
if ($positions.Count -ne 2) {
throw "Expected 2 remote_control occurrences in target xV function, found $($positions.Count)."
}


foreach ($p in $positions) {
[Array]::Copy($replacement, 0, $bytes, $p, $replacement.Length)
}


[System.IO.File]::WriteAllBytes($patched, $bytes)
Log "Created patched asar: $patched offsets=$($positions -join ',')"
return $true
}


function Ensure-ConfigFlag {
$bytes = [System.IO.File]::ReadAllBytes($config)
$text = [System.Text.Encoding]::UTF8.GetString($bytes)
if ($text.Length -gt 0 -and [int][char]$text[0] -eq 0xFEFF) {
$text = $text.Substring(1)
}
$changed = $false
if ($text -match '(?m)^\s*remote_control\s*=') {
if (($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF)) {
$encoding = [System.Text.UTF8Encoding]::new($false)
[System.IO.File]::WriteAllText($config, $text, $encoding)
Log "Removed UTF-8 BOM from config.toml."
}
return
}
if ($text -match '(?m)^\s*remote_connections\s*=\s*true\s*$') {
$text = $text -replace '(?m)^(\s*remote_connections\s*=\s*true\s*)$', "`$1`r`nremote_control = true"
$changed = $true
} elseif ($text -match '(?m)^\s*\[features\]\s*$') {
$text = $text -replace '(?m)^(\s*\[features\]\s*)$', "`$1`r`nremote_control = true"
$changed = $true
} else {
$text += "`r`n[features]`r`nremote_control = true`r`n"
$changed = $true
}
if ($changed) {
$encoding = [System.Text.UTF8Encoding]::new($false)
[System.IO.File]::WriteAllText($config, $text, $encoding)
Log "Added remote_control to config.toml."
}
}


function Get-CodexPackageProcesses {
Get-CimInstance Win32_Process |
Where-Object {
$_.ExecutablePath -and
$_.ExecutablePath.StartsWith($package.InstallLocation, [System.StringComparison]::OrdinalIgnoreCase) -and
($_.Name -eq "Codex.exe" -or $_.Name -eq "codex.exe")
}
}


Log "Package version: $($package.Version)"
Log "Target asar: $asar"
New-PatchedAsar | Out-Null
Ensure-ConfigFlag


Log "Waiting for Codex package processes to exit."
while ($true) {
$running = @(Get-CodexPackageProcesses)
if ($running.Count -eq 0) {
break
}
Log "Still waiting for Codex processes: $($running.ProcessId -join ',')"
Start-Sleep -Seconds 2
}


try {
& takeown /F $asar | Out-Null
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
& icacls $asar /grant "$($currentUser):(F)" | Out-Null
Log "Granted temporary app.asar permission."
$cipherOutput = & cipher /d $asar 2>&1
Log "Requested app.asar decryption/unprotect exit=$LASTEXITCODE output=$($cipherOutput -join ' | ')"
} catch {
Log "Failed to grant temporary app.asar permission: $($_.Exception.Message)"
throw
}


if (-not (Test-Path -LiteralPath $backup)) {
[System.IO.File]::WriteAllBytes($backup, [System.IO.File]::ReadAllBytes($asar))
Log "Created backup: $backup"
}


try {
$stream = [System.IO.File]::Open($asar, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
$stream.Close()
} catch {
Log "app.asar is still not writable: $($_.Exception.Message)"
throw
}


Copy-Item -LiteralPath $patched -Destination $asar -Force
Log "Installed patched app.asar."


try {
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
& icacls $asar /remove:g $currentUser | Out-Null
& icacls $asar /setowner "NT SERVICE\TrustedInstaller" | Out-Null
Log "Restored app.asar owner/temporary grant."
} catch {
Log "Failed to restore app.asar owner/temporary grant: $($_.Exception.Message)"
}


Ensure-ConfigFlag
Log "Done."

fc8096305d35df25d48fcc912568c632

官方还未推进windows的app_control链路 写了个脚本,原理就是通过UAC加权的cmd启动codex.exe->把配置文件加上语句后设置为只读(在这之前要手动在配置文件加入 [features] js_repl = true goals = true remote_connections = true remote_control = true ) ->90秒后恢复可读,程序可写入参数

5c8b9ac9ebbc9514cc3bdcfb62008f90971683ea.zip