Extract claude and codex configs into separate profiles
This commit is contained in:
+16
-13
@@ -1,5 +1,6 @@
|
||||
# Globals; [profile.<name>] overrides them when --profile <name> is passed.
|
||||
# CLI flags override both.
|
||||
# Layered settings: CLI > active profile > globals. `--profile` selects the
|
||||
# profile, otherwise `default-profile` below is used. Vec fields append across
|
||||
# layers; scalars replace.
|
||||
|
||||
whitelist = true
|
||||
# blacklist = true
|
||||
@@ -11,8 +12,6 @@ whitelist = true
|
||||
# chdir = "~/projects/my-repo"
|
||||
|
||||
ro = [
|
||||
"~/.local/share/claude-code",
|
||||
"~/.local/share/codex-cli",
|
||||
"~/dev/agent-config",
|
||||
"/etc/alsa",
|
||||
"/run/user/1000/pulse",
|
||||
@@ -20,7 +19,6 @@ ro = [
|
||||
# "/host/path:/sandbox/path", # SRC:DST -> mount host SRC at a different target
|
||||
]
|
||||
rw = [
|
||||
"~/.config/claude",
|
||||
"~/.cargo",
|
||||
"~/.rustup",
|
||||
]
|
||||
@@ -33,12 +31,17 @@ env = [
|
||||
]
|
||||
# unsetenv = ["SOME_LEAKED_VAR"]
|
||||
|
||||
entrypoint = ["claude", "--dangerously-skip-permissions"]
|
||||
# command = ["--model", "opus"] # default trailing args
|
||||
# bwrap-args = ["--tmpfs /opt/scratch"] # raw bwrap escape hatch
|
||||
# entrypoint = ["claude", "--dangerously-skip-permissions"] # binary + baked-in args
|
||||
# command = ["--model", "opus"] # default trailing args
|
||||
# bwrap-args = ["--tmpfs /opt/scratch"] # raw bwrap escape hatch
|
||||
|
||||
# Profiles inherit all globals above and override keys they set. Select one at
|
||||
# runtime with `--profile <name>`. Vec fields (ro/rw/mask/env/unsetenv) append
|
||||
# to the globals; scalar fields replace. Profile-less runs use just the globals.
|
||||
[profile.blacklist]
|
||||
blacklist = true
|
||||
default-profile = "claude"
|
||||
|
||||
[profile.claude]
|
||||
ro = ["~/.local/share/claude-code"]
|
||||
rw = ["~/.config/claude"]
|
||||
entrypoint = ["claude", "--dangerously-skip-permissions"]
|
||||
|
||||
[profile.codex]
|
||||
ro = ["~/.local/share/codex-cli"]
|
||||
entrypoint = ["codex", "--dangerously-bypass-approvals-and-sandbox"]
|
||||
|
||||
+62
-1
@@ -290,6 +290,8 @@ pub struct FileConfig {
|
||||
pub options: Options,
|
||||
#[serde(default)]
|
||||
pub profile: HashMap<String, Options>,
|
||||
#[serde(rename = "default-profile", default)]
|
||||
pub default_profile: Option<String>,
|
||||
// Collects unrecognized keys; deny_unknown_fields is incompatible with flatten.
|
||||
#[serde(flatten)]
|
||||
_unknown: HashMap<String, toml::Value>,
|
||||
@@ -322,7 +324,7 @@ impl FileConfig {
|
||||
}
|
||||
|
||||
fn resolve_profile(&self, name: Option<&str>) -> Result<Options, SandboxError> {
|
||||
match name {
|
||||
match name.or(self.default_profile.as_deref()) {
|
||||
Some(n) => self
|
||||
.profile
|
||||
.get(n)
|
||||
@@ -879,6 +881,65 @@ mod tests {
|
||||
assert!(!config.hardened);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_default_profile_used_when_cli_absent() {
|
||||
let file_config = FileConfig {
|
||||
default_profile: Some("auto".into()),
|
||||
profile: HashMap::from([(
|
||||
"auto".into(),
|
||||
Options {
|
||||
hardened: Some(true),
|
||||
..Options::default()
|
||||
},
|
||||
)]),
|
||||
..FileConfig::default()
|
||||
};
|
||||
let config = build(args_with_command(), Some(file_config)).unwrap();
|
||||
assert!(config.hardened);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_cli_profile_overrides_default_profile() {
|
||||
let file_config = FileConfig {
|
||||
default_profile: Some("auto".into()),
|
||||
profile: HashMap::from([
|
||||
(
|
||||
"auto".into(),
|
||||
Options {
|
||||
hardened: Some(true),
|
||||
..Options::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"other".into(),
|
||||
Options {
|
||||
hardened: Some(false),
|
||||
..Options::default()
|
||||
},
|
||||
),
|
||||
]),
|
||||
..FileConfig::default()
|
||||
};
|
||||
let args = Args {
|
||||
profile: Some("other".into()),
|
||||
..args_with_command()
|
||||
};
|
||||
let config = build(args, Some(file_config)).unwrap();
|
||||
assert!(!config.hardened);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_missing_default_profile_errors() {
|
||||
let file_config = FileConfig {
|
||||
default_profile: Some("nope".into()),
|
||||
..FileConfig::default()
|
||||
};
|
||||
assert!(matches!(
|
||||
build(args_with_command(), Some(file_config)),
|
||||
Err(SandboxError::ProfileNotFound(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_missing_profile_errors() {
|
||||
let args = Args {
|
||||
|
||||
Reference in New Issue
Block a user