Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
edd31d8
fix: enable native fast service tier for API-key auth
Jun 18, 2026
37c96a8
fix: align launcher launch_codex signature
Jun 18, 2026
a55c4fc
fix: align CI release build prerequisites
Jun 18, 2026
1f41409
fix: restore relay profile snapshot guard
Jun 18, 2026
4aa7bf8
fix: keep launcher activation flag mutable on Windows
Jun 18, 2026
e24a57c
fix: harden native fast service tier patch
Jun 22, 2026
c3fd117
fix: avoid persisting aggregate relay runtime fields
Jun 22, 2026
0bad674
style: format updater arch token branch
Jun 22, 2026
b0782b6
fix: remove duplicate relay switch command helper
Jun 22, 2026
6b3d560
Merge main into fix/api-key-native-fast-service-tier-v2
Jun 29, 2026
b51baa5
Merge main into fix/api-key-native-fast-service-tier-v2
Jun 29, 2026
61c0ac5
fix: restore native fast preload on macOS launch
Jun 29, 2026
642e484
fix: stabilize fast mode CI after main merge
Jun 29, 2026
b7fe7f0
fix: align CI checks after main merge
Jun 29, 2026
71d4139
fix: support Codex 26.623.70822 service tier bundles
Jun 30, 2026
0754c07
fix: support Codex 26.623.70822 read service tier
Jun 30, 2026
f5314d5
fix: merge main and keep Codex 26.623.70822 read service tier support
Jun 30, 2026
ac42cc3
test: align context route assertions with manager i18n labels
Jun 30, 2026
4b03f00
test: align manager context title assertion with i18n
Jun 30, 2026
324971b
fix: merge main and retain fast mode injection support
Jul 1, 2026
4928659
fix: support Codex 26.623.81905 service tier bundles
Jul 1, 2026
ba9f518
Merge remote-tracking branch 'origin/main' into fix/api-key-native-fa…
Jul 1, 2026
9125987
Merge remote-tracking branch 'origin/main' into HEAD
Jul 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ jobs:

- name: Build frontend
working-directory: apps/codex-plus-manager
run: npm run vite:build
shell: bash
run: |
./node_modules/.bin/vite build
test -d dist

- name: Rust tests
run: cargo test --workspace
Expand Down Expand Up @@ -116,7 +119,10 @@ jobs:

- name: Build frontend
working-directory: apps/codex-plus-manager
run: npm run vite:build
shell: bash
run: |
./node_modules/.bin/vite build
test -d dist

- name: Build release binaries
run: cargo build --release --target ${{ matrix.target }}
Expand Down
25 changes: 2 additions & 23 deletions .github/workflows/release-assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,6 @@ jobs:
Copy-Item target/release/codex-plus-plus.exe dist/windows/app/
Copy-Item target/release/codex-plus-plus-manager.exe dist/windows/app/

- name: Build Windows zip asset
shell: pwsh
run: |
$version = "${{ github.event.release.tag_name }}".TrimStart("v", "V")
New-Item -ItemType Directory -Force dist/windows | Out-Null
Compress-Archive -Path dist/windows/app/* -DestinationPath "dist/windows/CodexPlusPlus-$version-windows-x64.zip" -Force

- name: Build Windows installer
shell: pwsh
run: |
Expand All @@ -68,9 +61,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.release.tag_name }}
files: |
dist/windows/*.exe
dist/windows/*.zip
files: dist/windows/*.exe

macos-dmg:
name: macOS DMG (${{ matrix.arch }})
Expand Down Expand Up @@ -118,16 +109,6 @@ jobs:
VERSION="${VERSION#V}"
BINARY_DIR="$PWD/target/${{ matrix.target }}/release" bash scripts/installer/macos/package-dmg.sh "$VERSION" "${{ matrix.arch }}"

- name: Build macOS zip asset
shell: bash
run: |
VERSION="${GITHUB_REF_NAME#v}"
VERSION="${VERSION#V}"
mkdir -p dist/macos/app-${{ matrix.arch }}
cp "target/${{ matrix.target }}/release/codex-plus-plus" "dist/macos/app-${{ matrix.arch }}/"
cp "target/${{ matrix.target }}/release/codex-plus-plus-manager" "dist/macos/app-${{ matrix.arch }}/"
(cd dist/macos && zip -r "CodexPlusPlus-${VERSION}-macos-${{ matrix.arch }}.zip" "app-${{ matrix.arch }}")

- name: Verify macOS bundle structure
run: |
set -euo pipefail
Expand All @@ -146,9 +127,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.release.tag_name }}
files: |
dist/macos/*.dmg
dist/macos/*.zip
files: dist/macos/*.dmg

latest-json:
name: Upload static latest.json
Expand Down
9 changes: 9 additions & 0 deletions apps/codex-plus-manager/src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ use serde_json::{Value, json};

use crate::install::{self, InstallActionResult, InstallOptions};

fn test_env_lock() -> std::sync::MutexGuard<'static, ()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(())).lock().expect("test env lock poisoned")
}

#[derive(Debug, Clone, Serialize)]
pub struct CommandResult<T>
where
Expand Down Expand Up @@ -3489,6 +3494,7 @@ mod tests {

#[test]
fn env_conflict_commands_ignore_codex_home_and_remove_openai_vars() {
let _lock = test_env_lock();
let test_openai_name = "OPENAI_CODEX_PLUS_ENV_CONFLICT_TEST";
let previous_openai = std::env::var_os(test_openai_name);
let previous_codex_home = std::env::var_os("CODEX_HOME");
Expand Down Expand Up @@ -3540,6 +3546,7 @@ mod tests {

#[test]
fn delete_local_session_falls_back_when_requested_db_no_longer_contains_thread() {
let _lock = test_env_lock();
let temp = tempfile::tempdir().unwrap();
let previous_codex_home = std::env::var_os("CODEX_HOME");
let codex_home = temp.path().join("codex-home");
Expand Down Expand Up @@ -3606,6 +3613,7 @@ mod tests {

#[test]
fn list_local_sessions_deduplicates_threads_across_current_and_legacy_dbs() {
let _lock = test_env_lock();
let temp = tempfile::tempdir().unwrap();
let previous_codex_home = std::env::var_os("CODEX_HOME");
let codex_home = temp.path().join("codex-home");
Expand Down Expand Up @@ -3634,6 +3642,7 @@ mod tests {

#[test]
fn delete_local_session_removes_duplicate_threads_from_all_candidate_dbs() {
let _lock = test_env_lock();
let temp = tempfile::tempdir().unwrap();
let previous_codex_home = std::env::var_os("CODEX_HOME");
let codex_home = temp.path().join("codex-home");
Expand Down
4 changes: 2 additions & 2 deletions apps/codex-plus-manager/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ const defaultSettings: BackendSettings = {
codexAppUpstreamWorktreeCreate: true,
codexAppNativeMenuPlacement: true,
codexAppNativeMenuLocalization: true,
codexAppServiceTierControls: false,
codexAppServiceTierControls: true,
codexAppStepwiseEnabled: false,
codexAppStepwiseDirectSend: false,
codexAppStepwiseBaseUrl: "",
Expand Down Expand Up @@ -2655,7 +2655,7 @@ function EnhanceScreen({
<FeatureToggle title={t("插件市场解锁")} detail={t("API Key 模式下扩展插件市场请求,尽量显示完整插件列表;官方/混合模式通常不需要。")} checked={form.codexAppPluginMarketplaceUnlock} disabled={!masterEnabled || !patchMode} onChange={(value) => setEnhanceFlag("codexAppPluginMarketplaceUnlock", value)} />
<FeatureToggle title={t("插件列表全量展示")} detail={t("进入插件页后自动连续展开“更多”,尽量一次显示完整插件列表。")} checked={form.codexAppPluginAutoExpand} disabled={!masterEnabled || !patchMode} onChange={(value) => setEnhanceFlag("codexAppPluginAutoExpand", value)} />
<FeatureToggle title={t("模型白名单解锁")} detail={t("从环境变量和 config.toml 的 /v1/models 拉取模型并补进模型列表。")} checked={form.codexAppModelWhitelistUnlock} disabled={!masterEnabled} onChange={(value) => setEnhanceFlag("codexAppModelWhitelistUnlock", value)} />
<FeatureToggle title={t("Fast 按钮")} detail={t("显示服务模式切换按钮;Fast 仅支持 gpt-5.4 / gpt-5.5,其他模型按 Standard 发送。")} checked={form.codexAppServiceTierControls} disabled={!masterEnabled} onChange={(value) => setEnhanceFlag("codexAppServiceTierControls", value)} />
<FeatureToggle title={t("系统 Fast 开关")} detail={t("是否开启系统 Fast 开关:已默认开启,API Key 登录复用 Codex 原生速度选项与标识;具体 Fast / Standard 在 Codex 界面选择。")} checked={true} disabled onChange={() => {}} />
<div className="feature-action-row">
<div>
<strong>{t("官方远端插件缓存")}</strong>
Expand Down
26 changes: 24 additions & 2 deletions apps/codex-plus-manager/src/model-windows.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import assert from "node:assert";
import { describe, it } from "node:test";
import type { RelayProfile } from "./App.tsx";
import {
buildModelWindows,
modelWindowRowsFromProfile,
Expand All @@ -10,8 +9,31 @@ import {
mergeModelWindowRows,
} from "./model-windows.ts";

type RelayProfileShape = {
id: string;
name: string;
model: string;
baseUrl: string;
upstreamBaseUrl: string;
apiKey: string;
protocol: "responses" | "chatCompletions";
relayMode: "official" | "mixedApi" | "pureApi" | "aggregate";
officialMixApiKey: boolean;
testModel: string;
configContents: string;
authContents: string;
useCommonConfig: boolean;
contextSelection: { mcpServers: string[]; skills: string[]; plugins: string[] };
contextSelectionInitialized: boolean;
contextWindow: string;
autoCompactLimit: string;
modelList: string;
modelWindows: string;
userAgent: string;
};

// 类型检查:确保 RelayProfile 包含 modelWindows 字段
const _profileTypeCheck: RelayProfile = {
const _profileTypeCheck: RelayProfileShape = {
id: "test",
name: "",
model: "",
Expand Down
11 changes: 4 additions & 7 deletions assets/inject/renderer-inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -1143,7 +1143,7 @@
}

function defaultCodexPlusSettings() {
return { pluginMarketplaceUnlock: true, pluginAutoExpand: true, modelWhitelistUnlock: true, sessionDelete: true, markdownExport: true, pasteFix: false, projectMove: true, threadIdBadge: false, conversationView: false, conversationViewMaxWidth: conversationViewDefaultWidth, threadScrollRestore: true, zedRemoteOpen: true, upstreamWorktreeCreate: true, nativeMenuPlacement: true, serviceTierControls: false, stepwise: false };
return { pluginMarketplaceUnlock: true, pluginAutoExpand: true, modelWhitelistUnlock: true, sessionDelete: true, markdownExport: true, pasteFix: false, projectMove: true, threadIdBadge: false, conversationView: false, conversationViewMaxWidth: conversationViewDefaultWidth, threadScrollRestore: true, zedRemoteOpen: true, upstreamWorktreeCreate: true, nativeMenuPlacement: true, serviceTierControls: true, stepwise: false };
}

const codexPlusBackendSettingMap = {
Expand Down Expand Up @@ -2003,10 +2003,7 @@
function applyCodexServiceTierRequestOverride(method, params, threadIdHint = "") {
const override = codexServiceTierOverrideForRequest(method, params, threadIdHint);
if (!override) return params;
const nextParams = { ...(params || {}), serviceTier: override.serviceTier };
if (Object.prototype.hasOwnProperty.call(nextParams, "service_tier") || override.fastBlocked) {
nextParams.service_tier = override.serviceTier;
}
const nextParams = { ...(params || {}), serviceTier: override.serviceTier, service_tier: override.serviceTier };
sendCodexPlusDiagnostic("service_tier_request_override_applied", {
method,
threadId: override.threadId || "",
Expand Down Expand Up @@ -2435,8 +2432,8 @@
<button type="button" class="codex-plus-toggle" data-codex-plus-setting="modelWhitelistUnlock"><span></span></button>
</div>
<div class="codex-plus-row">
<div><div class="codex-plus-row-title">Fast 按钮</div><div class="codex-plus-row-description">显示服务模式切换按钮;Fast 仅支持 ${codexServiceTierFastModelListLabel()},其他模型按 Standard 发送。</div></div>
<button type="button" class="codex-plus-toggle" data-codex-plus-setting="serviceTierControls"><span></span></button>
<div><div class="codex-plus-row-title">系统 Fast 开关</div><div class="codex-plus-row-description">是否开启系统 Fast 开关:已默认开启,API Key 登录复用 Codex 原生速度选项与标识;具体 Fast / Standard 在 Codex 界面选择,Fast 仅支持 ${codexServiceTierFastModelListLabel()}。</div></div>
<button type="button" class="codex-plus-toggle" data-codex-plus-setting="serviceTierControls" disabled><span></span></button>
</div>
<div class="codex-plus-row">
<div><div class="codex-plus-row-title">Stepwise</div><div class="codex-plus-row-description">在当前 Codex 页面显示可拖动的下一步建议浮层,可在设置页配置模型和直接发送。</div></div>
Expand Down
31 changes: 29 additions & 2 deletions crates/codex-plus-core/src/launcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf};
use std::process::Stdio;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use std::env;

use anyhow::Context;
use async_trait::async_trait;
Expand Down Expand Up @@ -693,10 +694,36 @@ impl LaunchHooks for DefaultLaunchHooks {
let executable = command
.first()
.ok_or_else(|| anyhow::anyhow!("macOS open command is empty"))?;
let child = Command::new(executable)
let mut child_command = Command::new(executable);
child_command
.args(&command[1..])
.stdout(Stdio::null())
.stderr(Stdio::null())
.stderr(Stdio::null());
if settings.enhancements_enabled {
let preload_path = crate::service_tier_preload::ensure_service_tier_preload()
.context("failed to prepare service tier preload")?;
let node_options =
crate::service_tier_preload::node_options_with_service_tier_preload(
env::var("NODE_OPTIONS").ok().as_deref(),
&preload_path.to_string_lossy(),
);
child_command.env("NODE_OPTIONS", node_options.clone());
let _ = crate::diagnostic_log::append_diagnostic_log(
"launcher.service_tier_preload_enabled",
serde_json::json!({
"preload_path": preload_path.to_string_lossy(),
"node_options": node_options,
}),
);
} else {
let _ = crate::diagnostic_log::append_diagnostic_log(
"launcher.service_tier_preload_disabled",
serde_json::json!({
"enhancements_enabled": settings.enhancements_enabled,
}),
);
}
let child = child_command
.spawn()
.context("failed to launch macOS Codex app")?;
*self.child.lock().await = Some(child);
Expand Down
1 change: 1 addition & 0 deletions crates/codex-plus-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub mod relay_rotation;
pub mod relay_switch;
pub mod routes;
pub mod script_market;
pub mod service_tier_preload;
pub mod settings;
pub mod status;
pub mod stepwise;
Expand Down
14 changes: 10 additions & 4 deletions crates/codex-plus-core/src/ports.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::fs::File;
use std::net::{TcpListener, ToSocketAddrs};
use std::path::{Path, PathBuf};
use std::sync::{Mutex, OnceLock};

use fs2::FileExt;

Expand Down Expand Up @@ -344,7 +345,7 @@ mod tests {

#[test]
fn launcher_guard_port_honors_env_override() {
let _guard = guard_port_env_lock();
let _lock = _guard_port_env_test_lock();
_clear_guard_port_env_vars();
unsafe { std::env::set_var("CODEX_PLUS_GUARD_PORT", "9999") };
let port = launcher_guard_port();
Expand All @@ -354,7 +355,7 @@ mod tests {

#[test]
fn launcher_guard_port_honors_specific_env_override() {
let _guard = guard_port_env_lock();
let _lock = _guard_port_env_test_lock();
_clear_guard_port_env_vars();
unsafe { std::env::set_var("CODEX_PLUS_LAUNCHER_GUARD_PORT", "8888") };
let port = launcher_guard_port();
Expand All @@ -364,7 +365,7 @@ mod tests {

#[test]
fn manager_guard_port_honors_specific_env_override() {
let _guard = guard_port_env_lock();
let _lock = _guard_port_env_test_lock();
_clear_guard_port_env_vars();
unsafe { std::env::set_var("CODEX_PLUS_MANAGER_GUARD_PORT", "7777") };
let port = manager_guard_port();
Expand All @@ -374,7 +375,7 @@ mod tests {

#[test]
fn launcher_guard_port_honors_offset_env() {
let _guard = guard_port_env_lock();
let _lock = _guard_port_env_test_lock();
_clear_guard_port_env_vars();
unsafe { std::env::set_var("CODEX_PLUS_GUARD_PORT_OFFSET", "50") };
let port = launcher_guard_port();
Expand All @@ -399,3 +400,8 @@ fn _clear_guard_port_env_vars() {
let _ = std::env::remove_var("CODEX_PLUS_GUARD_PORT_OFFSET");
}
}

fn _guard_port_env_test_lock() -> std::sync::MutexGuard<'static, ()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(())).lock().expect("guard port env test lock poisoned")
}
8 changes: 8 additions & 0 deletions crates/codex-plus-core/src/relay_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1956,6 +1956,14 @@ fn complete_relay_profile_config(profile: &RelayProfile) -> anyhow::Result<Strin
}

pub fn normalize_relay_profile_for_storage(profile: &mut RelayProfile) -> anyhow::Result<()> {
if profile.relay_mode == crate::settings::RelayMode::Aggregate {
profile.model.clear();
profile.base_url.clear();
profile.upstream_base_url.clear();
profile.api_key.clear();
profile.config_contents.clear();
profile.auth_contents.clear();
}
if profile.model_windows.trim().is_empty() && profile.model_list.contains('[') {
let (clean_list, windows) =
crate::model_suffix::migrate_model_list_with_suffixes(&profile.model_list);
Expand Down
Loading
Loading