fix(codex): #546 landing fixes

App Settings now refuses a Default Codex model that the server would reject,
before anything is written to localStorage. SettingsUpdateSchema is .strict()
and checks codexModel with ^[a-zA-Z0-9._\-/]*$, so a value like gpt-oss:20b
400'd the whole settings PUT while the toast still said "Settings saved", and
because the bad value was already in the local blob every later save from that
device failed the same way. The client check uses the same pattern, shows an
error toast, focuses the field and keeps the modal open. The toast has a zh-CN
translation in i18n.js.

src/web/codex-launch-defaults.ts gets an @fileoverview (fill only unset fields,
re-validate persisted values, callers decide scope, never writes Codex config
files), as every module in src carries one.

Both new Codex rows in index.html carry has-field, like every other App
Settings field row, so on phones the input and the select stack under their
label instead of squeezing it into a narrow column.

The Agent CLIs wiki paragraph said the defaults apply to every local launch.
Scheduled (cron) codex jobs are built without a codexConfig and never get
them, while Resume goes through POST /api/sessions and does, so the sentence
now names the Run menu, Resume, POST /api/sessions and /api/quick-start, and
says cron jobs do not use them.

The Settings Reference lists the two new rows in the Agents & CLIs table. The
neighbouring "Bypass approvals and sandbox" row described Pi's project trust;
it is the Codex --dangerously-bypass-approvals-and-sandbox toggle, so its note
says that now.

The PR's own changeset is removed: the release writes one consolidated
changeset at COM, and the PR's text overstated the scope (it included cron).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-10-09 04:56:29 +02:00
parent 5c08e29a08
commit 28df21f4bb
7 changed files with 31 additions and 9 deletions
+13
View File
@@ -1,3 +1,16 @@
/**
* @fileoverview Launch-time defaults for Codex sessions.
*
* Resolves the synced App Settings `codexModel` / `codexReasoningEffort` into the
* `codexConfig` a launch uses, filling ONLY the fields the caller left unset.
* Persisted values are re-validated with `SettingsUpdateSchema`, so a hand-edited
* settings.json can never smuggle an unchecked value onto the codex command line.
*
* Scope is the caller's decision: the create and quick-start routes apply it to
* local launches only, never to remote, Docker or custom-endpoint launches.
* Nothing here writes Codex's own config files.
*/
import type { CodexConfig } from '../types.js';
import { SettingsUpdateSchema } from './schemas.js';
import { readJsonConfig, SETTINGS_PATH } from './route-helpers.js';
+2
View File
@@ -52,6 +52,8 @@
'新本地 Codex 会话(包括 WSL)使用的模型 ID。留空时使用 Codex 配置。',
'Applies to new local sessions; supported levels depend on the model and Codex version. Custom endpoints, Docker and remote sessions keep their own settings.':
'应用于新本地会话;可用强度取决于模型和 Codex 版本。自定义端点、Docker 和远程会话保留自己的设置。',
'Default Codex model may only contain letters, digits, ".", "_", "-" and "/"':
'Codex 默认模型只能包含字母、数字、"."、"_"、"-" 和 "/"',
'Skip to terminal': '跳转到终端',
'Go to main page': '返回主页',
'Session tabs': '会话标签页',
+2 -2
View File
@@ -2586,14 +2586,14 @@
<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 has-field" 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 local Codex sessions, including WSL. 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 has-field" 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 local sessions; supported levels depend on the model and Codex version. Custom endpoints, Docker and remote sessions keep their own settings.</span>
+9
View File
@@ -2607,6 +2607,15 @@ Object.assign(CodemanApp.prototype, {
},
};
// SettingsUpdateSchema is .strict() and checks codexModel with this same
// pattern, so one bad character 400s the WHOLE settings PUT while the toast
// still says "Settings saved". Refuse it here, before anything is persisted.
if (!/^[A-Za-z0-9._\/-]*$/.test(settings.codexModel)) {
this.showToast('Default Codex model may only contain letters, digits, ".", "_", "-" and "/"', 'error');
document.getElementById('appSettingsCodexModel')?.focus();
return;
}
// The "Token Count" / "Show Cost ($)" header toggles were removed from the
// UI, but their features still read settings.showTokenCount / settings.showCost
// (applyHeaderVisibilitySettings, the header cost render). saveAppSettings