mirror of
https://github.com/openai/codex.git
synced 2026-06-01 19:02:59 +00:00
Merge branch 'etraut/next-turn-state-remove-override-context' into etraut/next-turn-state-core
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)]
|
||||
|
||||
@@ -893,6 +893,71 @@ async fn config_batch_write_applies_multiple_edits() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn config_batch_write_preserves_dotted_profile_names() -> Result<()> {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
let codex_home = tmp_dir.path().canonicalize()?;
|
||||
write_config(
|
||||
&tmp_dir,
|
||||
r#"
|
||||
profile = "team.prod"
|
||||
|
||||
[profiles."team.prod"]
|
||||
model = "gpt-5.3-spark"
|
||||
|
||||
[profiles.team.prod]
|
||||
model = "should-stay-put"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let mut mcp = McpProcess::new(&codex_home).await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||
|
||||
let batch_id = mcp
|
||||
.send_config_batch_write_request(ConfigBatchWriteParams {
|
||||
file_path: Some(codex_home.join("config.toml").display().to_string()),
|
||||
edits: vec![
|
||||
ConfigEdit {
|
||||
key_path: "profiles.\"team.prod\".model".to_string(),
|
||||
value: json!("gpt-5.5"),
|
||||
merge_strategy: MergeStrategy::Replace,
|
||||
},
|
||||
ConfigEdit {
|
||||
key_path: "items.sample@catalog.enabled".to_string(),
|
||||
value: json!(true),
|
||||
merge_strategy: MergeStrategy::Replace,
|
||||
},
|
||||
],
|
||||
expected_version: None,
|
||||
reload_user_config: false,
|
||||
})
|
||||
.await?;
|
||||
let batch_resp: JSONRPCResponse = timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
mcp.read_stream_until_response_message(RequestId::Integer(batch_id)),
|
||||
)
|
||||
.await??;
|
||||
let batch_write: ConfigWriteResponse = to_response(batch_resp)?;
|
||||
assert_eq!(batch_write.status, WriteStatus::Ok);
|
||||
|
||||
let config: toml::Value =
|
||||
toml::from_str(&std::fs::read_to_string(codex_home.join("config.toml"))?)?;
|
||||
assert_eq!(
|
||||
config["profiles"]["team.prod"]["model"].as_str(),
|
||||
Some("gpt-5.5")
|
||||
);
|
||||
assert_eq!(
|
||||
config["profiles"]["team"]["prod"]["model"].as_str(),
|
||||
Some("should-stay-put")
|
||||
);
|
||||
assert_eq!(
|
||||
config["items"]["sample@catalog"]["enabled"].as_bool(),
|
||||
Some(true)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn config_batch_write_updates_multiple_desktop_settings() -> Result<()> {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
|
||||
Reference in New Issue
Block a user