mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-10 17:29:41 +02:00
feat(codex): add synced model and reasoning defaults
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import type { CodexConfig } from '../types.js';
|
||||
import { SettingsUpdateSchema } from './schemas.js';
|
||||
import { readJsonConfig, SETTINGS_PATH } from './route-helpers.js';
|
||||
|
||||
/** Resolve launch-only defaults without changing Codex's own configuration files. */
|
||||
export async function resolveCodexLaunchDefaults(
|
||||
config: CodexConfig | undefined,
|
||||
customEndpoint = false
|
||||
): Promise<CodexConfig | undefined> {
|
||||
if (customEndpoint) return config;
|
||||
const settings = await readJsonConfig<Record<string, unknown>>(SETTINGS_PATH, 'Codex launch defaults', {});
|
||||
const model = SettingsUpdateSchema.shape.codexModel.safeParse(settings.codexModel);
|
||||
const effort = SettingsUpdateSchema.shape.codexReasoningEffort.safeParse(settings.codexReasoningEffort);
|
||||
const defaultModel = model.success ? model.data || undefined : undefined;
|
||||
const defaultEffort = effort.success ? effort.data || undefined : undefined;
|
||||
if (!defaultModel && !defaultEffort) return config;
|
||||
return {
|
||||
...config,
|
||||
model: config?.model ?? defaultModel,
|
||||
reasoningEffort: config?.reasoningEffort ?? defaultEffort,
|
||||
};
|
||||
}
|
||||
@@ -45,6 +45,11 @@
|
||||
// Exact English-source translations. Technical names, command examples, model
|
||||
// names, keyboard chords, and user-authored content intentionally stay unchanged.
|
||||
const ZH_CN = Object.freeze({
|
||||
'Default Codex model': 'Codex 默认模型',
|
||||
'Default Codex reasoning effort': 'Codex 默认思考强度',
|
||||
'Use Codex configuration': '使用 Codex 配置',
|
||||
'Model ID for new Codex sessions. Leave empty to use Codex configuration.': '新 Codex 会话使用的模型 ID。留空时使用 Codex 配置。',
|
||||
'Applies to new sessions; supported levels depend on the model and Codex version. Custom endpoints keep their own settings.': '应用于新会话;可用强度取决于模型和 Codex 版本。自定义端点保留自己的设置。',
|
||||
'Skip to terminal': '跳转到终端',
|
||||
'Go to main page': '返回主页',
|
||||
'Session tabs': '会话标签页',
|
||||
|
||||
@@ -2527,6 +2527,30 @@
|
||||
<div class="set-group" id="appSettingsCodexGroup">
|
||||
<div class="set-group-head"><h4>Codex</h4><span class="set-scope">synced</span></div>
|
||||
<div class="set-group-body">
|
||||
<div class="set-row" data-search="codex default model">
|
||||
<div class="set-row-text">
|
||||
<span class="set-row-label">Default Codex model</span>
|
||||
<span class="set-row-desc">Model ID for new Codex sessions. Leave empty to use Codex configuration.</span>
|
||||
</div>
|
||||
<input id="appSettingsCodexModel" class="set-input" type="text" maxlength="100" aria-label="Default Codex model" placeholder="Use Codex configuration" autocomplete="off" spellcheck="false">
|
||||
</div>
|
||||
<div class="set-row" data-search="codex default reasoning effort thinking">
|
||||
<div class="set-row-text">
|
||||
<span class="set-row-label">Default Codex reasoning effort</span>
|
||||
<span class="set-row-desc">Applies to new sessions; supported levels depend on the model and Codex version. Custom endpoints keep their own settings.</span>
|
||||
</div>
|
||||
<select id="appSettingsCodexReasoningEffort" class="set-select" aria-label="Default Codex reasoning effort">
|
||||
<option value="">Use Codex configuration</option>
|
||||
<option value="none">none</option>
|
||||
<option value="minimal">minimal</option>
|
||||
<option value="low">low</option>
|
||||
<option value="medium">medium</option>
|
||||
<option value="high">high</option>
|
||||
<option value="xhigh">xhigh</option>
|
||||
<option value="max">max</option>
|
||||
<option value="ultra">ultra</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="set-row" data-search="codex bypass approvals sandbox">
|
||||
<div class="set-row-text">
|
||||
<span class="set-row-label">Bypass approvals and sandbox</span>
|
||||
|
||||
@@ -526,6 +526,8 @@ Object.assign(CodemanApp.prototype, {
|
||||
settings.codexDangerouslyBypassApprovals ?? false;
|
||||
document.getElementById('appSettingsCodexAnimations').checked =
|
||||
settings.codexAnimationsEnabled ?? false;
|
||||
document.getElementById('appSettingsCodexModel').value = settings.codexModel ?? '';
|
||||
document.getElementById('appSettingsCodexReasoningEffort').value = settings.codexReasoningEffort ?? '';
|
||||
this._applyCodexSettingsVisibility();
|
||||
// Claude Permissions settings
|
||||
document.getElementById('appSettingsAgentTeams').checked = settings.agentTeamsEnabled ?? false;
|
||||
@@ -2534,6 +2536,8 @@ Object.assign(CodemanApp.prototype, {
|
||||
claudeMode: document.getElementById('appSettingsClaudeMode').value,
|
||||
allowedTools: document.getElementById('appSettingsAllowedTools').value.trim(),
|
||||
// Codex CLI settings
|
||||
codexModel: document.getElementById('appSettingsCodexModel').value.trim(),
|
||||
codexReasoningEffort: document.getElementById('appSettingsCodexReasoningEffort').value,
|
||||
codexDangerouslyBypassApprovals: document.getElementById('appSettingsCodexDangerouslyBypassApprovals').checked,
|
||||
codexAnimationsEnabled: document.getElementById('appSettingsCodexAnimations').checked,
|
||||
// Claude Permissions settings
|
||||
|
||||
@@ -115,6 +115,7 @@ import { clampEnvOverridesForOwner } from '../../session-env-clamp.js';
|
||||
import { enabledClis, getCli } from '../../config/cli-registry/registry.js';
|
||||
import type { NewlineSequence } from '../../config/cli-registry/types.js';
|
||||
import { resolveCliLaunchError } from '../../utils/cli-launcher.js';
|
||||
import { resolveCodexLaunchDefaults } from '../codex-launch-defaults.js';
|
||||
import { legacyConfigForMode } from '../../session-cli-registry-bridge.js';
|
||||
import { isMultiUserMode } from '../../config/multiuser.js';
|
||||
import { AUTH_COOKIE_NAME } from '../middleware/auth.js';
|
||||
@@ -1103,6 +1104,7 @@ export function registerSessionRoutes(
|
||||
const globalNice = await ctx.getGlobalNiceConfig();
|
||||
const modelConfig = await ctx.getModelConfig();
|
||||
const mode = body.mode || 'claude';
|
||||
const launchCodexConfig = mode === 'codex' ? await resolveCodexLaunchDefaults(body.codexConfig) : body.codexConfig;
|
||||
// Where a model override comes from is a capability, and the three answers are
|
||||
// genuinely different mechanisms:
|
||||
// 'flag' — the CLI takes --model, so read the value the caller sent
|
||||
@@ -1117,9 +1119,10 @@ export function registerSessionRoutes(
|
||||
const modelSource = getCli(mode)?.capabilities.model;
|
||||
const model =
|
||||
modelSource?.source === 'flag'
|
||||
? (legacyConfigForMode(mode, body as unknown as Record<string, unknown>)?.[modelSource.param ?? 'model'] as
|
||||
| string
|
||||
| undefined)
|
||||
? (legacyConfigForMode(mode, { ...body, codexConfig: launchCodexConfig } as unknown as Record<
|
||||
string,
|
||||
unknown
|
||||
>)?.[modelSource.param ?? 'model'] as string | undefined)
|
||||
: modelSource?.source === 'claude-settings-file'
|
||||
? body.model || modelConfig?.defaultModel || undefined
|
||||
: undefined;
|
||||
@@ -1136,7 +1139,7 @@ export function registerSessionRoutes(
|
||||
deepSeekConfig: gatedDeepSeekConfig,
|
||||
} = await _clampExternalCliBypassForOwner(
|
||||
owner,
|
||||
body.codexConfig,
|
||||
launchCodexConfig,
|
||||
body.geminiConfig,
|
||||
body.antigravityConfig,
|
||||
body.piConfig,
|
||||
@@ -3831,13 +3834,15 @@ export function registerSessionRoutes(
|
||||
// Apply global Nice priority config and model config from settings
|
||||
const niceConfig = await ctx.getGlobalNiceConfig();
|
||||
const qsModelConfig = await ctx.getModelConfig();
|
||||
const qsLaunchCodexConfig =
|
||||
mode === 'codex' && !remote ? await resolveCodexLaunchDefaults(codexConfig, !!customModel) : codexConfig;
|
||||
// See the create path for why this is a capability rather than a mode ladder.
|
||||
const qsModelSource = getCli(mode)?.capabilities.model;
|
||||
const qsModel =
|
||||
qsModelSource?.source === 'flag'
|
||||
? (legacyConfigForMode(mode, {
|
||||
openCodeConfig,
|
||||
codexConfig,
|
||||
codexConfig: qsLaunchCodexConfig,
|
||||
geminiConfig,
|
||||
antigravityConfig,
|
||||
piConfig,
|
||||
@@ -3859,7 +3864,7 @@ export function registerSessionRoutes(
|
||||
deepSeekConfig: qsGatedDeepSeekConfig,
|
||||
} = await _clampExternalCliBypassForOwner(
|
||||
owner,
|
||||
codexConfig,
|
||||
qsLaunchCodexConfig,
|
||||
geminiConfig,
|
||||
antigravityConfig,
|
||||
piConfig,
|
||||
|
||||
@@ -1457,6 +1457,12 @@ export const SettingsUpdateSchema = z
|
||||
claudeMode: z.string().max(50).optional(),
|
||||
allowedTools: z.string().max(2000).optional(),
|
||||
// Codex CLI settings
|
||||
codexModel: z
|
||||
.string()
|
||||
.max(100)
|
||||
.regex(/^[a-zA-Z0-9._\-/]*$/)
|
||||
.optional(),
|
||||
codexReasoningEffort: z.enum(['', ...CODEX_REASONING_EFFORTS]).optional(),
|
||||
codexDangerouslyBypassApprovals: z.boolean().optional(),
|
||||
codexAnimationsEnabled: z.boolean().optional(),
|
||||
// Terminal history and retention
|
||||
|
||||
Reference in New Issue
Block a user