Skip to content
Open
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
33 changes: 3 additions & 30 deletions crates/codex-plus-core/src/computer_use_guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
use std::path::{Path, PathBuf};

use anyhow::Context;
use toml_edit::{Array, DocumentMut, Item, Table};
use toml_edit::{Array, DocumentMut, Item};

use crate::toml_helpers::{ensure_trailing_newline, parse_toml_document, table_mut_or_insert};

const BUNDLED_MARKETPLACE: &str = "openai-bundled";
const BUNDLED_MARKETPLACE_PLUGINS: &[&str] = &["browser", "chrome", "computer-use", "latex"];
Expand Down Expand Up @@ -736,28 +738,6 @@ fn windows_extended_path(path: &Path) -> String {
}
}

fn parse_toml_document(contents: &str) -> anyhow::Result<DocumentMut> {
if contents.trim().is_empty() {
Ok(DocumentMut::new())
} else {
contents
.parse::<DocumentMut>()
.with_context(|| "config.toml TOML parse failed")
}
}

fn table_mut_or_insert<'a>(doc: &'a mut DocumentMut, key: &str) -> anyhow::Result<&'a mut Table> {
if !doc.as_table().contains_key(key) {
doc[key] = toml_edit::table();
}
if doc.get(key).and_then(Item::as_table).is_none() {
doc[key] = toml_edit::table();
}
doc.get_mut(key)
.and_then(Item::as_table_mut)
.ok_or_else(|| anyhow::anyhow!("{key} must be a TOML table"))
}

fn ensure_plugin_enabled(doc: &mut DocumentMut, plugin_id: &str) -> anyhow::Result<()> {
let plugins = table_mut_or_insert(doc, "plugins")?;
if !plugins.contains_key(plugin_id) {
Expand All @@ -770,13 +750,6 @@ fn ensure_plugin_enabled(doc: &mut DocumentMut, plugin_id: &str) -> anyhow::Resu
Ok(())
}

fn ensure_trailing_newline(mut contents: String) -> String {
if !contents.ends_with('\n') {
contents.push('\n');
}
contents
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
9 changes: 2 additions & 7 deletions crates/codex-plus-core/src/env_conflicts.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use serde::Serialize;
use std::path::PathBuf;

use crate::timestamp::timestamp_millis;

const WINDOWS_USER_ENV_KEY: &str = "Environment";

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
Expand Down Expand Up @@ -152,13 +154,6 @@ fn source_order(source: EnvConflictSource) -> u8 {
}
}

fn timestamp_millis() -> u128 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis()
}

#[cfg(windows)]
fn detect_user_env_conflicts() -> Vec<EnvConflict> {
crate::windows_integration::read_current_user_string_values(WINDOWS_USER_ENV_KEY)
Expand Down
25 changes: 25 additions & 0 deletions crates/codex-plus-core/src/json_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use serde_json::Value;

pub fn json_string_value(value: Option<&Value>) -> String {
match value {
Some(Value::String(value)) => value.trim().to_string(),
Some(Value::Number(value)) => value.to_string(),
_ => String::new(),
}
}

pub fn required_json_string(raw: &Value, key: &str) -> Option<String> {
raw.get(key)
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned)
}

pub fn optional_json_string(raw: &Value, key: &str) -> String {
raw.get(key)
.and_then(Value::as_str)
.map(str::trim)
.unwrap_or_default()
.to_string()
}
3 changes: 3 additions & 0 deletions crates/codex-plus-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod diagnostic_log;
pub mod env_conflicts;
pub mod http_client;
pub mod install;
pub mod json_value;
pub mod launcher;
pub mod model_catalog;
pub mod models;
Expand All @@ -25,6 +26,8 @@ pub mod routes;
pub mod script_market;
pub mod settings;
pub mod status;
pub mod timestamp;
pub mod toml_helpers;
pub mod update;
pub mod upstream_worktree;
pub mod user_scripts;
Expand Down
20 changes: 1 addition & 19 deletions crates/codex-plus-core/src/model_catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};

use crate::settings::{RelayProfile, SettingsStore};
use crate::toml_helpers::unquote_toml_string;
use serde_json::{Value, json};

const BASE_URL_ENV_KEYS: &[&str] = &[
Expand Down Expand Up @@ -757,22 +758,3 @@ fn string_value(value: Option<&String>) -> String {
.map(|value| value.trim().to_string())
.unwrap_or_default()
}

fn unquote_toml_string(value: &str) -> String {
let value = value.trim();
if let Ok(parsed) = toml::from_str::<toml::Value>(&format!("value = {value}")) {
if let Some(value) = parsed.get("value").and_then(toml::Value::as_str) {
return value.to_string();
}
}
value
.strip_prefix('"')
.and_then(|value| value.strip_suffix('"'))
.or_else(|| {
value
.strip_prefix('\'')
.and_then(|value| value.strip_suffix('\''))
})
.unwrap_or(value)
.to_string()
}
67 changes: 9 additions & 58 deletions crates/codex-plus-core/src/relay_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ use serde::Serialize;
use serde_json::{Value, json};
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use toml_edit::{DocumentMut, Item, Table, TableLike};

use crate::timestamp::timestamp_millis;
use crate::toml_helpers::{
ensure_trailing_newline, normalize_trimmed_text, parse_toml_document, table_mut_or_insert,
unquote_toml_string_simple as unquote_toml_string,
};

use crate::settings::{RelayContextSelection, RelayProfile, RelayProtocol};

const RELAY_PROVIDER: &str = "custom";
Expand Down Expand Up @@ -142,18 +147,6 @@ fn set_codex_goals_feature_text_fallback(existing: &str, enabled: bool) -> Strin
ensure_trailing_newline(updated)
}

fn table_mut_or_insert<'a>(doc: &'a mut DocumentMut, key: &str) -> anyhow::Result<&'a mut Table> {
if !doc.as_table().contains_key(key) {
doc[key] = toml_edit::table();
}
if doc.get(key).and_then(Item::as_table).is_none() {
doc[key] = toml_edit::table();
}
doc.get_mut(key)
.and_then(Item::as_table_mut)
.ok_or_else(|| anyhow::anyhow!("{key} 必须是 TOML table"))
}

fn table_mut_if_exists<'a>(doc: &'a mut DocumentMut, key: &str) -> Option<&'a mut Table> {
doc.get_mut(key).and_then(Item::as_table_mut)
}
Expand Down Expand Up @@ -1128,16 +1121,6 @@ fn provider_table_exists(doc: &DocumentMut, provider_id: &str) -> bool {
.is_some()
}

fn parse_toml_document(contents: &str) -> anyhow::Result<DocumentMut> {
if contents.trim().is_empty() {
Ok(DocumentMut::new())
} else {
contents
.parse::<DocumentMut>()
.with_context(|| "config.toml TOML 解析失败")
}
}

fn remove_provider_specific_common_keys(table: &mut dyn TableLike) {
for key in [
"model",
Expand Down Expand Up @@ -1188,16 +1171,7 @@ fn sanitize_common_config_text_fallback(common_config: &str) -> String {
kept.push(line);
}

normalize_text_toml(kept.join("\n"))
}

fn normalize_text_toml(contents: String) -> String {
let trimmed = contents.trim();
if trimmed.is_empty() {
String::new()
} else {
ensure_trailing_newline(trimmed.to_string())
}
normalize_trimmed_text(kept.join("\n"))
}

pub fn normalize_config_text(contents: &str) -> String {
Expand Down Expand Up @@ -1241,7 +1215,7 @@ fn normalize_duplicate_toml_text(contents: &str) -> String {
kept.push(line);
}

normalize_text_toml(kept.join("\n"))
normalize_trimmed_text(kept.join("\n"))
}

fn strip_common_config_text_fallback(config_text: &str, common_config: &str) -> String {
Expand Down Expand Up @@ -1280,7 +1254,7 @@ fn strip_common_config_text_fallback(config_text: &str, common_config: &str) ->
kept.push(line);
}

normalize_text_toml(kept.join("\n"))
normalize_trimmed_text(kept.join("\n"))
}

struct CommonConfigAnchors {
Expand Down Expand Up @@ -2177,20 +2151,6 @@ fn create_live_backup(
Ok(Some(backup_dir.to_string_lossy().to_string()))
}

fn timestamp_millis() -> u128 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis()
}

fn ensure_trailing_newline(mut contents: String) -> String {
if !contents.ends_with('\n') {
contents.push('\n');
}
contents
}

fn move_model_providers_before_profiles(contents: &str) -> String {
let lines = contents.lines().collect::<Vec<_>>();
let Some(provider_start) = lines
Expand Down Expand Up @@ -2462,15 +2422,6 @@ fn table_values(contents: &str, table: &str) -> Option<std::collections::HashMap
in_table.then_some(values)
}

fn unquote_toml_string(value: &str) -> String {
let value = value.trim();
value
.strip_prefix('"')
.and_then(|value| value.strip_suffix('"'))
.unwrap_or(value)
.to_string()
}

fn root_line_key(line: &str) -> Option<&str> {
let trimmed = line.trim();
if trimmed.starts_with('#') || trimmed.starts_with('[') {
Expand Down
31 changes: 9 additions & 22 deletions crates/codex-plus-core/src/script_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anyhow::Context;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::json_value::{optional_json_string, required_json_string};
use crate::user_scripts::UserScriptManager;

pub const DEFAULT_MARKET_INDEX_URL: &str =
Expand Down Expand Up @@ -109,16 +110,16 @@ pub async fn install_market_script(
}

fn parse_market_script(raw: Value) -> Option<MarketScript> {
let id = required_string(&raw, "id")?;
let name = required_string(&raw, "name")?;
let version = required_string(&raw, "version")?;
let script_url = required_string(&raw, "script_url")?;
let id = required_json_string(&raw, "id")?;
let name = required_json_string(&raw, "name")?;
let version = required_json_string(&raw, "version")?;
let script_url = required_json_string(&raw, "script_url")?;
Some(MarketScript {
id,
name,
description: optional_string(&raw, "description"),
description: optional_json_string(&raw, "description"),
version,
author: optional_string(&raw, "author"),
author: optional_json_string(&raw, "author"),
tags: raw
.get("tags")
.and_then(Value::as_array)
Expand All @@ -132,24 +133,10 @@ fn parse_market_script(raw: Value) -> Option<MarketScript> {
.collect()
})
.unwrap_or_default(),
homepage: optional_string(&raw, "homepage"),
homepage: optional_json_string(&raw, "homepage"),
script_url,
sha256: optional_string(&raw, "sha256"),
sha256: optional_json_string(&raw, "sha256"),
})
}

fn required_string(raw: &Value, key: &str) -> Option<String> {
raw.get(key)
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned)
}

fn optional_string(raw: &Value, key: &str) -> String {
raw.get(key)
.and_then(Value::as_str)
.map(str::trim)
.unwrap_or_default()
.to_string()
}
26 changes: 4 additions & 22 deletions crates/codex-plus-core/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use serde::Deserialize;
use serde_json::{Map, Value};
use toml_edit::{DocumentMut, Item};

use crate::toml_helpers::{normalize_trimmed_text, parse_toml_document};
use crate::zed_remote::ZedOpenStrategy;

#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
Expand Down Expand Up @@ -851,16 +852,6 @@ fn active_provider_id(doc: &DocumentMut) -> Option<String> {
.map(ToString::to_string)
}

fn parse_toml_document(contents: &str) -> anyhow::Result<DocumentMut> {
if contents.trim().is_empty() {
Ok(DocumentMut::new())
} else {
contents
.parse::<DocumentMut>()
.with_context(|| "config.toml TOML 解析失败")
}
}

fn settings_to_object(settings: &BackendSettings) -> Map<String, Value> {
match serde_json::to_value(settings).unwrap_or_else(|_| Value::Object(Map::new())) {
Value::Object(map) => map,
Expand Down Expand Up @@ -903,8 +894,8 @@ fn split_context_config_sections(config: &str) -> (String, String) {
}

(
normalize_text_config(common.join("\n")),
normalize_text_config(context.join("\n")),
normalize_trimmed_text(common.join("\n")),
normalize_trimmed_text(context.join("\n")),
)
}

Expand All @@ -921,16 +912,7 @@ fn join_config_sections(sections: &[&str]) -> String {
.filter(|section| !section.is_empty())
.collect::<Vec<_>>()
.join("\n\n");
normalize_text_config(joined)
}

fn normalize_text_config(contents: String) -> String {
let trimmed = contents.trim();
if trimmed.is_empty() {
String::new()
} else {
format!("{trimmed}\n")
}
normalize_trimmed_text(joined)
}

pub(crate) fn atomic_write(path: &Path, bytes: &[u8]) -> anyhow::Result<()> {
Expand Down
Loading