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 codex_home = TempDir::new()?;
|
||||||
let source_home = secondary_external_agent_home(codex_home.path());
|
let source_home = secondary_external_agent_home(codex_home.path());
|
||||||
std::fs::create_dir_all(&source_home)?;
|
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 home_dir = codex_home.path().display().to_string();
|
||||||
let mut mcp = TestAppServer::builder()
|
let mut mcp = TestAppServer::builder()
|
||||||
.with_codex_home(codex_home.path())
|
.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_eq!(completed.item_type_results[0].failures, Vec::new());
|
||||||
assert!(
|
assert!(
|
||||||
std::fs::read_to_string(codex_home.path().join("config.toml"))?
|
std::fs::read_to_string(codex_home.path().join("config.toml"))?
|
||||||
.contains("sandbox_mode = \"read-only\"")
|
.contains("SOURCE = \"secondary\"")
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -98,12 +98,11 @@ impl ExternalAgentSource {
|
|||||||
|
|
||||||
pub(super) fn effective_settings(
|
pub(super) fn effective_settings(
|
||||||
self,
|
self,
|
||||||
source_config_dir: &Path,
|
|
||||||
source_settings: &Path,
|
source_settings: &Path,
|
||||||
) -> io::Result<Option<JsonValue>> {
|
) -> io::Result<Option<JsonValue>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Cla => ClaSource::effective_settings(source_settings),
|
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,
|
scope: &MigrationScope,
|
||||||
) -> io::Result<Option<JsonValue>> {
|
) -> io::Result<Option<JsonValue>> {
|
||||||
let source_settings = self.source_settings(scope);
|
let source_settings = self.source_settings(scope);
|
||||||
self.source
|
self.source.effective_settings(&source_settings)
|
||||||
.effective_settings(self.source_config_dir(scope).as_path(), &source_settings)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_mcp_config(
|
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::hook_migration_event_names_cur;
|
||||||
use crate::import_hooks_cur;
|
use crate::import_hooks_cur;
|
||||||
use crate::import_subagents_with_rewrite_profile;
|
use crate::import_subagents_with_rewrite_profile;
|
||||||
use crate::invalid_data_error;
|
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
@@ -22,97 +21,16 @@ impl CurSource {
|
|||||||
pub const LEGACY_RULES_FILE: &'static str = ".cursorrules";
|
pub const LEGACY_RULES_FILE: &'static str = ".cursorrules";
|
||||||
pub const HOME_CONFIG_FILE: &'static str = "cli-config.json";
|
pub const HOME_CONFIG_FILE: &'static str = "cli-config.json";
|
||||||
pub const PROJECT_CONFIG_FILE: &'static str = "cli.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 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, &[])
|
pub const REWRITE_PROFILE: RewriteProfile = RewriteProfile::new(Self::LEGACY_RULES_FILE, &[])
|
||||||
.with_case_sensitive_term_variants(&["Cursor"]);
|
.with_case_sensitive_term_variants(&["Cursor"]);
|
||||||
|
|
||||||
pub fn effective_settings(
|
pub fn effective_settings(source_settings: &Path) -> io::Result<Option<JsonValue>> {
|
||||||
source_dir: &Path,
|
read_json_file(source_settings)
|
||||||
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 build_config(settings: &JsonValue) -> io::Result<TomlValue> {
|
pub fn build_config(settings: &JsonValue) -> io::Result<TomlValue> {
|
||||||
build_config(settings, Self::append_config)
|
build_config(settings, |_, _| {})
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_mcp_config(source_dir: &Path) -> io::Result<TomlValue> {
|
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 pretty_assertions::assert_eq;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use tempfile::TempDir;
|
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]
|
#[test]
|
||||||
fn cached_marketplace_plugins_require_manifest_and_cache_entries() {
|
fn cached_marketplace_plugins_require_manifest_and_cache_entries() {
|
||||||
|
|||||||
Reference in New Issue
Block a user