mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[1 of 4] tui: route primary settings writes through app server (#22913)
## Why The TUI can run against a remote app server, but several high-traffic settings still persisted by editing the local config file. That sends remote sessions' preference writes to the wrong machine and lets local disk state drift from the app-server-owned config. This is **[1 of 4]** in a stacked series that moves TUI-owned config mutations onto app-server APIs. ## What changed - Added a small TUI helper for typed app-server config writes. - Routed primary interactive preference writes through `config/batchWrite`. - Preserved existing profile scoping for settings that already support `profiles.<profile>.*` overrides. ## Config keys affected - `model` - `model_reasoning_effort` - `personality` - `service_tier` - `plan_mode_reasoning_effort` - `approvals_reviewer` - `notice.fast_default_opt_out` - Profile-scoped equivalents under `profiles.<profile>.*` ## Suggested manual validation - Connect the TUI to a remote app server, change `model` and `model_reasoning_effort`, reconnect, and confirm the remote config retained both values while the local `config.toml` did not change. - Change `personality`, `plan_mode_reasoning_effort`, and the explicit auto-review selection, then reconnect and confirm those choices persist through the app server. - Clear the service tier back to default and confirm `service_tier` is cleared while `notice.fast_default_opt_out = true` is persisted remotely. - Repeat one setting change with an active profile and confirm the write lands under `profiles.<profile>.*`. ## Stack 1. [#22913](https://github.com/openai/codex/pull/22913) `[1 of 4]` primary settings writes 2. [#22914](https://github.com/openai/codex/pull/22914) `[2 of 4]` app and skill enablement 3. [#22915](https://github.com/openai/codex/pull/22915) `[3 of 4]` feature and memory toggles 4. [#22916](https://github.com/openai/codex/pull/22916) `[4 of 4]` startup and onboarding bookkeeping
This commit is contained in:
@@ -403,10 +403,47 @@ fn parse_key_path(path: &str) -> Result<Vec<String>, String> {
|
||||
if path.trim().is_empty() {
|
||||
return Err("keyPath must not be empty".to_string());
|
||||
}
|
||||
Ok(path
|
||||
.split('.')
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect())
|
||||
|
||||
let mut segments = Vec::new();
|
||||
let mut segment = String::new();
|
||||
let mut chars = path.chars();
|
||||
let mut quoted = false;
|
||||
|
||||
// Split on dots unless they appear inside a quoted segment. Bare segments
|
||||
// intentionally stay permissive so existing paths like `sample@catalog`
|
||||
// remain valid.
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'"' if segment.is_empty() && !quoted => quoted = true,
|
||||
'"' if quoted => quoted = false,
|
||||
'\\' if quoted => {
|
||||
// Quoted segments may escape punctuation that would otherwise
|
||||
// participate in parsing, such as `.` or `"`.
|
||||
let Some(escaped) = chars.next() else {
|
||||
return Err("unterminated escape in keyPath".to_string());
|
||||
};
|
||||
segment.push(escaped);
|
||||
}
|
||||
'.' if !quoted => {
|
||||
if segment.is_empty() {
|
||||
return Err("keyPath segments must not be empty".to_string());
|
||||
}
|
||||
segments.push(std::mem::take(&mut segment));
|
||||
}
|
||||
'"' => return Err("invalid quoted keyPath segment".to_string()),
|
||||
_ => segment.push(ch),
|
||||
}
|
||||
}
|
||||
|
||||
if quoted {
|
||||
return Err("unterminated quoted keyPath segment".to_string());
|
||||
}
|
||||
if segment.is_empty() {
|
||||
return Err("keyPath segments must not be empty".to_string());
|
||||
}
|
||||
|
||||
segments.push(segment);
|
||||
Ok(segments)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
Reference in New Issue
Block a user