Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ PatewayAI 专注于官方高质量模型 API 中继,完整支持 Claude、Code
- Skills 与插件:`/skills/` 路由以独立 Tab 管理 Skills 安装和 Codex 插件安装;Skills 可从内置/自定义 GitHub 仓库或 skills.sh 搜索后单独安装,也支持 ZIP、目录导入和安全卸载;Codex 原生 Marketplace 继续负责完整插件安装,系统 Skill 只读
- 项目启动:桌面端收藏本机目录;Windows / macOS 直接在 ChatGPT Codex App 中打开对应工作区,“会话”通过本机 CLI 继续项目;Web / Docker 不访问设备目录
- 设置页:统一管理端口、监听地址、代理、请求超时、SSE 保活、主题、自动更新、后台行为
- 基础设置中的“移除 requires_openai_auth”默认开启,保持托管网关的图片/actor 扩展认证行为。关闭后仅在切换到网关、重复应用或启动同步时保留 `cm` provider 已有的值,不会恢复此前删除的字段;保留 `true` 可能禁用 Codex 图片扩展。此设置不影响直连聚合 API,也不改写其他 provider。

### Service 版
- `codexmanager-service`:提供本地 OpenAI 兼容网关
Expand Down
15 changes: 15 additions & 0 deletions apps/src/app/settings/components/general-basics-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { AppSettings } from "@/types";
type GeneralBasicsSnapshot = Pick<
AppSettings,
| "updateAutoCheck"
| "removeRequiresOpenaiAuth"
| "autoStartEnabled"
| "autoStartSupported"
| "showMainWindowOnStartup"
Expand Down Expand Up @@ -157,6 +158,20 @@ export function GeneralBasicsCard({
}
/>
</div>
<div className="flex items-center justify-between gap-4 py-3">
<div className="min-w-0 flex flex-col gap-0.5">
<Label htmlFor="settings-remove-openai-auth">{t("移除 requires_openai_auth")}</Label>
<p id="settings-remove-openai-auth-description" className="text-xs text-muted-foreground">
{t("默认开启,在切换到网关或同步配置时清理旧认证标记;关闭仅保留已有值,不恢复已删除的配置。保留 true 可能禁用图片扩展。")}
</p>
</div>
<Switch
id="settings-remove-openai-auth"
aria-describedby="settings-remove-openai-auth-description"
checked={snapshot.removeRequiresOpenaiAuth}
onCheckedChange={(value) => updateSettings.mutate({ removeRequiresOpenaiAuth: value })}
/>
</div>
<div className="flex items-center justify-between gap-4 py-3">
<div className="min-w-0 space-y-0.5">
<Label>{t("开机自动启动")}</Label>
Expand Down
1 change: 1 addition & 0 deletions apps/src/lib/api/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1900,6 +1900,7 @@ export function normalizeAppSettings(payload: unknown): AppSettings {
);
return {
updateAutoCheck: asBoolean(source.updateAutoCheck, true),
removeRequiresOpenaiAuth: asBoolean(source.removeRequiresOpenaiAuth, true),
autoStartEnabled: asBoolean(source.autoStartEnabled, false),
autoStartSupported: asBoolean(source.autoStartSupported, false),
showMainWindowOnStartup: asBoolean(source.showMainWindowOnStartup, true),
Expand Down
3 changes: 3 additions & 0 deletions apps/src/lib/i18n/messages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ export const EN_MESSAGES: MessageCatalog = {
基础设置: "Basic settings",
控制应用启动和窗口行为: "Control startup and window behavior.",
自动检查更新: "Check updates automatically",
"移除 requires_openai_auth": "Remove requires_openai_auth",
"默认开启,在切换到网关或同步配置时清理旧认证标记;关闭仅保留已有值,不恢复已删除的配置。保留 true 可能禁用图片扩展。":
"Enabled by default: clear the old auth flag when switching to the gateway or syncing the profile. Disabling only preserves existing values; it does not restore removed settings. Keeping true may disable image extensions.",
"启动完成后在后台检查更新,并每 7 小时检查一次":
"Check for updates in the background after startup, then every 7 hours.",
"检测到新版本,是否现在更新?":
Expand Down
3 changes: 3 additions & 0 deletions apps/src/lib/i18n/messages/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ export const KO_MESSAGES: MessageCatalog = {
"CodexManager는 Codex CLI 계정, 로컬 게이트웨이, 플랫폼 키, 요청 로그, 사용량 통계를 통합 관리하여 Codex 연결과 운영을 더 집중적이고 제어 가능하게 합니다.",
基础设置: "기본 설정",
自动检查更新: "자동 업데이트 확인",
"移除 requires_openai_auth": "requires_openai_auth 제거",
"默认开启,在切换到网关或同步配置时清理旧认证标记;关闭仅保留已有值,不恢复已删除的配置。保留 true 可能禁用图片扩展。":
"기본적으로 켜져 있으며 게이트웨이 전환 또는 프로필 동기화 시 이전 인증 플래그를 제거합니다. 끄면 기존 값만 유지하며 삭제된 설정은 복원하지 않습니다. true를 유지하면 이미지 확장 기능이 비활성화될 수 있습니다.",
"启动完成后在后台检查更新,并每 7 小时检查一次":
"시작이 완료되면 백그라운드에서 업데이트를 확인하고 이후 7시간마다 확인합니다.",
"检测到新版本,是否现在更新?":
Expand Down
3 changes: 3 additions & 0 deletions apps/src/lib/i18n/messages/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ export const RU_MESSAGES: MessageCatalog = {
基础设置: "Основные настройки",
控制应用启动和窗口行为: "Управление запуском приложения и поведением окна.",
自动检查更新: "Автопроверка обновлений",
"移除 requires_openai_auth": "Удалять requires_openai_auth",
"默认开启,在切换到网关或同步配置时清理旧认证标记;关闭仅保留已有值,不恢复已删除的配置。保留 true 可能禁用图片扩展。":
"По умолчанию включено: удалять старый флаг авторизации при переключении на шлюз или синхронизации профиля. Отключение сохраняет только существующие значения и не восстанавливает удалённые. Значение true может отключить расширения для изображений.",
"启动完成后在后台检查更新,并每 7 小时检查一次":
"Проверять обновления в фоне после запуска, а затем каждые 7 часов.",
"检测到新版本,是否现在更新?":
Expand Down
1 change: 1 addition & 0 deletions apps/src/lib/store/useAppStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const useAppStore = create<AppState>((set) => ({
},
appSettings: {
updateAutoCheck: true,
removeRequiresOpenaiAuth: true,
autoStartEnabled: false,
autoStartSupported: false,
showMainWindowOnStartup: true,
Expand Down
1 change: 1 addition & 0 deletions apps/src/types/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export type GatewayTransportPatch = Partial<GatewayTransportValues>;

export interface AppSettings extends GatewayTransportValues {
updateAutoCheck: boolean;
removeRequiresOpenaiAuth: boolean;
autoStartEnabled: boolean;
autoStartSupported: boolean;
showMainWindowOnStartup: boolean;
Expand Down
41 changes: 41 additions & 0 deletions apps/tests/gateway-auth-cleanup.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expect, test } from "@playwright/test";

test("gateway auth cleanup defaults on and explicit preservation survives reload", async ({ page }) => {
let settings: Record<string, unknown> = {
locale: "en",
codexCliGuideDismissed: true,
serviceAddr: "localhost:48760",
theme: "tech",
};
const patches: Record<string, unknown>[] = [];
await page.route(/\/api\/runtime\/?(?:\?.*)?$/, (route) => route.fulfill({
json: { mode: "web-gateway", rpcBaseUrl: "/api/rpc", canManageService: false },
}));
await page.route(/\/api\/rpc\/?(?:\?.*)?$/, async (route) => {
const { id, method, params } = route.request().postDataJSON();
let result: unknown = {};
if (method === "appSettings/get") result = settings;
if (method === "appSettings/set") {
patches.push(params);
settings = { ...settings, ...params };
result = settings;
}
if (method === "initialize") result = { userAgent: "codex_cli_rs/test", codexHome: "C:/Test/.codex" };
if (method === "accountManager/session/current") {
result = { mode: "none", role: "system_admin", permissions: [], distributionEnabled: false };
}
await route.fulfill({ json: { jsonrpc: "2.0", id, result } });
});
await page.goto("/settings/");
const control = page.getByRole("switch", { name: "Remove requires_openai_auth", exact: true });
await expect(control).toBeChecked();
await expect(page.locator("#settings-remove-openai-auth-description")).toContainText("may disable image extensions");
await control.click();
await expect.poll(() => patches.some((patch) => patch.removeRequiresOpenaiAuth === false)).toBe(true);
await page.reload();
await expect(control).not.toBeChecked();
await control.click();
await expect.poll(() => patches.some((patch) => patch.removeRequiresOpenaiAuth === true)).toBe(true);
await page.reload();
await expect(control).toBeChecked();
});
14 changes: 12 additions & 2 deletions crates/service/src/app_settings/api/current.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ use super::{
save_persisted_bool_setting, sync_runtime_settings_from_storage,
APP_SETTING_AUTHOR_SERVER_RECOMMENDATIONS_KEY, APP_SETTING_AUTHOR_SPONSORS_KEY,
APP_SETTING_AUTO_START_ENABLED_KEY, APP_SETTING_CLOSE_TO_TRAY_ON_CLOSE_KEY,
APP_SETTING_ENV_OVERRIDES_KEY, APP_SETTING_GATEWAY_ACCOUNT_MAX_INFLIGHT_KEY,
APP_SETTING_GATEWAY_BACKGROUND_TASKS_KEY, APP_SETTING_GATEWAY_COMPACT_MODEL_FORWARD_RULES_KEY,
APP_SETTING_CODEX_PROFILE_REMOVE_REQUIRES_OPENAI_AUTH_KEY, APP_SETTING_ENV_OVERRIDES_KEY,
APP_SETTING_GATEWAY_ACCOUNT_MAX_INFLIGHT_KEY, APP_SETTING_GATEWAY_BACKGROUND_TASKS_KEY,
APP_SETTING_GATEWAY_COMPACT_MODEL_FORWARD_RULES_KEY,
APP_SETTING_GATEWAY_FREE_ACCOUNT_MAX_MODEL_KEY, APP_SETTING_GATEWAY_MODEL_FORWARD_RULES_KEY,
APP_SETTING_GATEWAY_ORIGINATOR_KEY, APP_SETTING_GATEWAY_QUOTA_GUARD_KEY,
APP_SETTING_GATEWAY_RESIDENCY_REQUIREMENT_KEY, APP_SETTING_GATEWAY_ROUTE_STRATEGY_KEY,
Expand Down Expand Up @@ -158,6 +159,11 @@ fn current_app_settings_value_inner(
let runtime_time_zone = current_runtime_time_zone_value();
let update_auto_check = setting_bool(&settings, APP_SETTING_UPDATE_AUTO_CHECK_KEY, true);
let auto_start_enabled = setting_bool(&settings, APP_SETTING_AUTO_START_ENABLED_KEY, false);
let remove_requires_openai_auth = setting_bool(
&settings,
APP_SETTING_CODEX_PROFILE_REMOVE_REQUIRES_OPENAI_AUTH_KEY,
true,
);
let show_main_window_on_startup =
setting_bool(&settings, APP_SETTING_SHOW_MAIN_WINDOW_ON_STARTUP_KEY, true);
let persisted_close_to_tray =
Expand Down Expand Up @@ -386,6 +392,10 @@ fn current_app_settings_value_inner(
"webAccessPasswordConfigured": web_access_password_configured(),
});
if let Some(object) = result.as_object_mut() {
object.insert(
"removeRequiresOpenaiAuth".to_string(),
remove_requires_openai_auth.into(),
);
object.insert("zoomFactor".to_string(), zoom_factor.into());
object.insert("gatewayUserAgent".to_string(), gateway_user_agent.into());
object.insert(
Expand Down
5 changes: 3 additions & 2 deletions crates/service/src/app_settings/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ pub(super) use super::{normalize_optional_text, parse_bool_with_default};
pub(super) use super::{
APP_SETTING_AUTHOR_SERVER_RECOMMENDATIONS_KEY, APP_SETTING_AUTHOR_SPONSORS_KEY,
APP_SETTING_AUTO_START_ENABLED_KEY, APP_SETTING_CLOSE_TO_TRAY_ON_CLOSE_KEY,
APP_SETTING_ENV_OVERRIDES_KEY, APP_SETTING_GATEWAY_ACCOUNT_MAX_INFLIGHT_KEY,
APP_SETTING_GATEWAY_BACKGROUND_TASKS_KEY, APP_SETTING_GATEWAY_COMPACT_MODEL_FORWARD_RULES_KEY,
APP_SETTING_CODEX_PROFILE_REMOVE_REQUIRES_OPENAI_AUTH_KEY, APP_SETTING_ENV_OVERRIDES_KEY,
APP_SETTING_GATEWAY_ACCOUNT_MAX_INFLIGHT_KEY, APP_SETTING_GATEWAY_BACKGROUND_TASKS_KEY,
APP_SETTING_GATEWAY_COMPACT_MODEL_FORWARD_RULES_KEY,
APP_SETTING_GATEWAY_FREE_ACCOUNT_MAX_MODEL_KEY, APP_SETTING_GATEWAY_MODEL_FORWARD_RULES_KEY,
APP_SETTING_GATEWAY_ORIGINATOR_KEY, APP_SETTING_GATEWAY_QUOTA_GUARD_KEY,
APP_SETTING_GATEWAY_RESIDENCY_REQUIREMENT_KEY, APP_SETTING_GATEWAY_ROUTE_STRATEGY_KEY,
Expand Down
21 changes: 15 additions & 6 deletions crates/service/src/app_settings/api/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ use super::author_links::{
normalize_author_link_items, serialize_author_link_items, AuthorLinkItem,
};
use super::{
save_persisted_app_setting, set_auto_start_enabled_setting, set_close_to_tray_on_close_setting,
set_codex_cli_guide_dismissed, set_env_overrides, set_gateway_account_max_inflight,
set_gateway_background_tasks, set_gateway_compact_model_forward_rules,
set_gateway_free_account_max_model, set_gateway_model_forward_rules, set_gateway_originator,
set_gateway_quota_guard, set_gateway_residency_requirement, set_gateway_route_strategy,
save_persisted_app_setting, save_persisted_bool_setting, set_auto_start_enabled_setting,
set_close_to_tray_on_close_setting, set_codex_cli_guide_dismissed, set_env_overrides,
set_gateway_account_max_inflight, set_gateway_background_tasks,
set_gateway_compact_model_forward_rules, set_gateway_free_account_max_model,
set_gateway_model_forward_rules, set_gateway_originator, set_gateway_quota_guard,
set_gateway_residency_requirement, set_gateway_route_strategy,
set_gateway_sse_keepalive_enabled, set_gateway_sse_keepalive_interval_ms,
set_gateway_thread_aware_account_distribution_enabled, set_gateway_upstream_proxy_bypass_hosts,
set_gateway_upstream_proxy_url, set_gateway_upstream_stream_timeout_ms,
Expand All @@ -20,13 +21,15 @@ use super::{
set_ui_appearance_preset, set_ui_locale, set_ui_low_transparency_enabled, set_ui_theme,
set_ui_zoom_factor, set_update_auto_check_enabled, BackgroundTasksInput, QuotaGuardInput,
APP_SETTING_AUTHOR_SERVER_RECOMMENDATIONS_KEY, APP_SETTING_AUTHOR_SPONSORS_KEY,
APP_SETTING_PLUGIN_MARKET_MODE_KEY, APP_SETTING_PLUGIN_MARKET_SOURCE_URL_KEY,
APP_SETTING_CODEX_PROFILE_REMOVE_REQUIRES_OPENAI_AUTH_KEY, APP_SETTING_PLUGIN_MARKET_MODE_KEY,
APP_SETTING_PLUGIN_MARKET_SOURCE_URL_KEY,
};

#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct AppSettingsPatch {
update_auto_check: Option<bool>,
remove_requires_openai_auth: Option<bool>,
auto_start_enabled: Option<bool>,
show_main_window_on_startup: Option<bool>,
close_to_tray_on_close: Option<bool>,
Expand Down Expand Up @@ -104,6 +107,12 @@ pub(super) fn apply_app_settings_patch(patch: AppSettingsPatch) -> Result<(), St
if let Some(enabled) = patch.update_auto_check {
set_update_auto_check_enabled(enabled)?;
}
if let Some(enabled) = patch.remove_requires_openai_auth {
save_persisted_bool_setting(
APP_SETTING_CODEX_PROFILE_REMOVE_REQUIRES_OPENAI_AUTH_KEY,
enabled,
)?;
}
if let Some(enabled) = patch.auto_start_enabled {
set_auto_start_enabled_setting(enabled)?;
}
Expand Down
1 change: 1 addition & 0 deletions crates/service/src/app_settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub(crate) use shared::{normalize_optional_text, parse_bool_with_default};
pub use shared::{
APP_SETTING_AUTHOR_SERVER_RECOMMENDATIONS_KEY, APP_SETTING_AUTHOR_SPONSORS_KEY,
APP_SETTING_AUTO_START_ENABLED_KEY, APP_SETTING_CLOSE_TO_TRAY_ON_CLOSE_KEY,
APP_SETTING_CODEX_PROFILE_REMOVE_REQUIRES_OPENAI_AUTH_KEY,
APP_SETTING_DISTRIBUTION_ENABLED_KEY, APP_SETTING_ENV_OVERRIDES_KEY,
APP_SETTING_GATEWAY_ACCOUNT_MAX_INFLIGHT_KEY, APP_SETTING_GATEWAY_BACKGROUND_TASKS_KEY,
APP_SETTING_GATEWAY_COMPACT_MODEL_FORWARD_RULES_KEY,
Expand Down
2 changes: 2 additions & 0 deletions crates/service/src/app_settings/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub const APP_SETTING_UI_CODEX_CLI_GUIDE_DISMISSED_KEY: &str = "ui.codex_cli_gui
pub const APP_SETTING_UI_THEME_KEY: &str = "ui.theme";
pub const APP_SETTING_UI_APPEARANCE_PRESET_KEY: &str = "ui.appearance_preset";
pub const APP_SETTING_UI_LOCALE_KEY: &str = "ui.locale";
pub const APP_SETTING_CODEX_PROFILE_REMOVE_REQUIRES_OPENAI_AUTH_KEY: &str =
"codex_profile.remove_requires_openai_auth";
pub const APP_SETTING_SERVICE_ADDR_KEY: &str = "app.service_addr";
pub const APP_SETTING_AUTHOR_SPONSORS_KEY: &str = "author.sponsors";
pub const APP_SETTING_AUTHOR_SERVER_RECOMMENDATIONS_KEY: &str = "author.server_recommendations";
Expand Down
18 changes: 16 additions & 2 deletions crates/service/src/codex_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,7 @@ pub(crate) async fn apply_gateway_async(
&paths.gateway_model_catalog_path,
supports_websockets,
&secret,
remove_requires_openai_auth_enabled(),
)?;
write_profile_files(
&profile_dir,
Expand Down Expand Up @@ -2803,6 +2804,14 @@ fn expand_home_prefix(input: &str) -> PathBuf {
PathBuf::from(input)
}

fn remove_requires_openai_auth_enabled() -> bool {
crate::app_settings::get_persisted_app_setting(
crate::app_settings::APP_SETTING_CODEX_PROFILE_REMOVE_REQUIRES_OPENAI_AUTH_KEY,
)
.map(|value| crate::app_settings::parse_bool_with_default(&value, true))
.unwrap_or(true)
}

fn profile_key(profile_dir: &Path) -> String {
profile_dir.to_string_lossy().to_string()
}
Expand Down Expand Up @@ -2982,6 +2991,7 @@ fn patch_config_for_gateway(
managed_catalog_path: &Path,
supports_websockets: bool,
bearer_token: &str,
remove_requires_openai_auth: bool,
) -> Result<String, String> {
let mut doc = parse_config(content.as_deref().unwrap_or(""))?;
doc.as_table_mut()
Expand Down Expand Up @@ -3017,8 +3027,11 @@ fn patch_config_for_gateway(
// Codex only treats actor authorization as an extension capability for custom providers
// that do not use ambient OpenAI auth. A provider-scoped bearer token works for local and
// remote CodexManager gateways without coupling the profile to a movable desktop executable.
// It is mutually exclusive with command/env/ambient auth, so remove stale values first.
provider.remove("requires_openai_auth");
// Keep the existing actor-capable behavior by default. Users who depend on ambient auth
// can explicitly preserve their existing value, at the cost of actor/image extensions.
if remove_requires_openai_auth {
provider.remove("requires_openai_auth");
}
set_provider_bearer_auth(provider, bearer_token)?;
provider.insert("base_url", toml_value(base_url));
provider.insert("wire_api", toml_value("responses"));
Expand Down Expand Up @@ -3364,6 +3377,7 @@ async fn sync_active_gateway_profile_from_storage_with_changes_async(
&paths.gateway_model_catalog_path,
supports_websockets,
secret,
remove_requires_openai_auth_enabled(),
)?)
}
_ => None,
Expand Down
Loading
Loading