mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
Stop migrating Cursor sandbox settings (#39325)
## What changed - Ignore `.cursor/sandbox.json` during external agent config migration. - Continue importing supported settings from `.cursor/cli-config.json`. ## Testing - Update app-server migration coverage to verify that environment variables from `cli-config.json` are imported. GitOrigin-RevId: 6094f1cb6f698392d58056f826da21c1f1389a1b
This commit is contained in:
committed by
copyberry
parent
b537d5a097
commit
67ed4e717a
@@ -338,7 +338,10 @@ async fn external_agent_config_migration_source_drives_detect_and_import() -> Re
|
||||
let codex_home = TempDir::new()?;
|
||||
let source_home = secondary_external_agent_home(codex_home.path());
|
||||
std::fs::create_dir_all(&source_home)?;
|
||||
std::fs::write(source_home.join("sandbox.json"), r#"{"type":"read_only"}"#)?;
|
||||
std::fs::write(
|
||||
source_home.join("cli-config.json"),
|
||||
r#"{"env":{"SOURCE":"secondary"}}"#,
|
||||
)?;
|
||||
let home_dir = codex_home.path().display().to_string();
|
||||
let mut mcp = TestAppServer::builder()
|
||||
.with_codex_home(codex_home.path())
|
||||
@@ -387,7 +390,7 @@ async fn external_agent_config_migration_source_drives_detect_and_import() -> Re
|
||||
assert_eq!(completed.item_type_results[0].failures, Vec::new());
|
||||
assert!(
|
||||
std::fs::read_to_string(codex_home.path().join("config.toml"))?
|
||||
.contains("sandbox_mode = \"read-only\"")
|
||||
.contains("SOURCE = \"secondary\"")
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -98,12 +98,11 @@ impl ExternalAgentSource {
|
||||
|
||||
pub(super) fn effective_settings(
|
||||
self,
|
||||
source_config_dir: &Path,
|
||||
source_settings: &Path,
|
||||
) -> io::Result<Option<JsonValue>> {
|
||||
match self {
|
||||
Self::Cla => ClaSource::effective_settings(source_settings),
|
||||
Self::Cur => CurSource::effective_settings(source_config_dir, source_settings),
|
||||
Self::Cur => CurSource::effective_settings(source_settings),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -485,8 +485,7 @@ impl ExternalAgentConfigService {
|
||||
scope: &MigrationScope,
|
||||
) -> io::Result<Option<JsonValue>> {
|
||||
let source_settings = self.source_settings(scope);
|
||||
self.source
|
||||
.effective_settings(self.source_config_dir(scope).as_path(), &source_settings)
|
||||
self.source.effective_settings(&source_settings)
|
||||
}
|
||||
|
||||
pub(crate) fn build_mcp_config(
|
||||
|
||||
@@ -7,7 +7,6 @@ use crate::build_mcp_config_from_json_file;
|
||||
use crate::hook_migration_event_names_cur;
|
||||
use crate::import_hooks_cur;
|
||||
use crate::import_subagents_with_rewrite_profile;
|
||||
use crate::invalid_data_error;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
@@ -22,97 +21,16 @@ impl CurSource {
|
||||
pub const LEGACY_RULES_FILE: &'static str = ".cursorrules";
|
||||
pub const HOME_CONFIG_FILE: &'static str = "cli-config.json";
|
||||
pub const PROJECT_CONFIG_FILE: &'static str = "cli.json";
|
||||
pub const SANDBOX_CONFIG_FILE: &'static str = "sandbox.json";
|
||||
pub const HOOKS_CONFIG_FILE: &'static str = "hooks.json";
|
||||
pub const SANDBOX_SETTINGS_KEY: &'static str = "__cursorSandbox";
|
||||
pub const REWRITE_PROFILE: RewriteProfile = RewriteProfile::new(Self::LEGACY_RULES_FILE, &[])
|
||||
.with_case_sensitive_term_variants(&["Cursor"]);
|
||||
|
||||
pub fn effective_settings(
|
||||
source_dir: &Path,
|
||||
source_settings: &Path,
|
||||
) -> io::Result<Option<JsonValue>> {
|
||||
let mut effective = read_json_file(source_settings)?;
|
||||
let sandbox_settings = read_json_file(&source_dir.join(Self::SANDBOX_CONFIG_FILE))?;
|
||||
if let Some(sandbox_settings) = sandbox_settings {
|
||||
let effective =
|
||||
effective.get_or_insert_with(|| JsonValue::Object(serde_json::Map::new()));
|
||||
let Some(effective) = effective.as_object_mut() else {
|
||||
return Err(invalid_data_error(
|
||||
"external agent settings root must be an object",
|
||||
));
|
||||
};
|
||||
effective.insert(Self::SANDBOX_SETTINGS_KEY.to_string(), sandbox_settings);
|
||||
}
|
||||
Ok(effective)
|
||||
pub fn effective_settings(source_settings: &Path) -> io::Result<Option<JsonValue>> {
|
||||
read_json_file(source_settings)
|
||||
}
|
||||
|
||||
pub fn build_config(settings: &JsonValue) -> io::Result<TomlValue> {
|
||||
build_config(settings, Self::append_config)
|
||||
}
|
||||
|
||||
pub fn append_config(
|
||||
root: &mut toml::map::Map<String, TomlValue>,
|
||||
settings: &serde_json::Map<String, JsonValue>,
|
||||
) {
|
||||
let Some(sandbox) = settings
|
||||
.get(Self::SANDBOX_SETTINGS_KEY)
|
||||
.and_then(JsonValue::as_object)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let sandbox_mode = match sandbox.get("type").and_then(JsonValue::as_str) {
|
||||
Some("workspace_readwrite") => Some("workspace-write"),
|
||||
Some("read_only") => Some("read-only"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(sandbox_mode) = sandbox_mode {
|
||||
root.insert(
|
||||
"sandbox_mode".to_string(),
|
||||
TomlValue::String(sandbox_mode.to_string()),
|
||||
);
|
||||
}
|
||||
if sandbox_mode != Some("workspace-write") {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut workspace_write = toml::map::Map::new();
|
||||
if let Some(paths) = sandbox
|
||||
.get("additionalReadwritePaths")
|
||||
.and_then(JsonValue::as_array)
|
||||
{
|
||||
let paths = paths
|
||||
.iter()
|
||||
.filter_map(JsonValue::as_str)
|
||||
.filter(|path| Path::new(path).is_absolute())
|
||||
.map(|path| TomlValue::String(path.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
if !paths.is_empty() {
|
||||
workspace_write.insert("writable_roots".to_string(), TomlValue::Array(paths));
|
||||
}
|
||||
}
|
||||
if sandbox.get("disableTmpWrite").and_then(JsonValue::as_bool) == Some(true) {
|
||||
workspace_write.insert("exclude_slash_tmp".to_string(), TomlValue::Boolean(true));
|
||||
workspace_write.insert(
|
||||
"exclude_tmpdir_env_var".to_string(),
|
||||
TomlValue::Boolean(true),
|
||||
);
|
||||
}
|
||||
if sandbox
|
||||
.get("networkPolicy")
|
||||
.and_then(JsonValue::as_object)
|
||||
.and_then(|network| network.get("default"))
|
||||
.and_then(JsonValue::as_str)
|
||||
== Some("allow")
|
||||
{
|
||||
workspace_write.insert("network_access".to_string(), TomlValue::Boolean(true));
|
||||
}
|
||||
if !workspace_write.is_empty() {
|
||||
root.insert(
|
||||
"sandbox_workspace_write".to_string(),
|
||||
TomlValue::Table(workspace_write),
|
||||
);
|
||||
}
|
||||
build_config(settings, |_, _| {})
|
||||
}
|
||||
|
||||
pub fn build_mcp_config(source_dir: &Path) -> io::Result<TomlValue> {
|
||||
|
||||
@@ -7,71 +7,6 @@ use crate::model::PluginsMigration;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::HashSet;
|
||||
use tempfile::TempDir;
|
||||
use toml::Value as TomlValue;
|
||||
|
||||
#[test]
|
||||
fn effective_settings_merge_sandbox_configuration() {
|
||||
let root = TempDir::new().expect("tempdir");
|
||||
let source_dir = root.path().join(CurSource::CONFIG_DIR);
|
||||
let source_settings = source_dir.join(CurSource::HOME_CONFIG_FILE);
|
||||
fs::create_dir_all(&source_dir).expect("source directory");
|
||||
fs::write(&source_settings, r#"{"env":{"FOO":"bar"}}"#).expect("source settings");
|
||||
fs::write(
|
||||
source_dir.join(CurSource::SANDBOX_CONFIG_FILE),
|
||||
r#"{"type":"read_only"}"#,
|
||||
)
|
||||
.expect("sandbox settings");
|
||||
|
||||
assert_eq!(
|
||||
CurSource::effective_settings(&source_dir, &source_settings).expect("effective settings"),
|
||||
Some(serde_json::json!({
|
||||
"env": {"FOO": "bar"},
|
||||
(CurSource::SANDBOX_SETTINGS_KEY): {"type": "read_only"}
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_config_maps_workspace_permissions() {
|
||||
let root = TempDir::new().expect("tempdir");
|
||||
let writable_root = root.path().join("generated");
|
||||
let settings = serde_json::json!({
|
||||
(CurSource::SANDBOX_SETTINGS_KEY): {
|
||||
"type": "workspace_readwrite",
|
||||
"additionalReadwritePaths": [writable_root.display().to_string(), "relative/path"],
|
||||
"disableTmpWrite": true,
|
||||
"networkPolicy": {"default": "allow"}
|
||||
}
|
||||
});
|
||||
let mut config = toml::map::Map::new();
|
||||
|
||||
CurSource::append_config(&mut config, settings.as_object().expect("settings object"));
|
||||
|
||||
let mut workspace_write = toml::map::Map::new();
|
||||
workspace_write.insert(
|
||||
"writable_roots".to_string(),
|
||||
TomlValue::Array(vec![TomlValue::String(
|
||||
writable_root.to_string_lossy().into_owned(),
|
||||
)]),
|
||||
);
|
||||
workspace_write.insert("exclude_slash_tmp".to_string(), TomlValue::Boolean(true));
|
||||
workspace_write.insert(
|
||||
"exclude_tmpdir_env_var".to_string(),
|
||||
TomlValue::Boolean(true),
|
||||
);
|
||||
workspace_write.insert("network_access".to_string(), TomlValue::Boolean(true));
|
||||
let mut expected = toml::map::Map::new();
|
||||
expected.insert(
|
||||
"sandbox_mode".to_string(),
|
||||
TomlValue::String("workspace-write".to_string()),
|
||||
);
|
||||
expected.insert(
|
||||
"sandbox_workspace_write".to_string(),
|
||||
TomlValue::Table(workspace_write),
|
||||
);
|
||||
|
||||
assert_eq!(TomlValue::Table(config), TomlValue::Table(expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cached_marketplace_plugins_require_manifest_and_cache_entries() {
|
||||
|
||||
Reference in New Issue
Block a user