mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
## Why Clients running an app-server on one OS and an exec-server on another OS need to be able to pass sandbox config to app-server that refers to resources on the executor's foreign OS. ## What `AbsolutePathBuf` can't represent these paths and we don't want users to be exposed to `PathUri` yet, so this moves the public app-server API to be expressed in terms of `ApiPathString`. Stacked on #28165. - change app-server v2 filesystem permission paths, including legacy read/write roots, to `ApiPathString` - localize API paths through `PathUri` when converting into the current native core permission types - make path-bearing permission conversions fallible and surface localization failures instead of silently treating malformed grants as ordinary denials - propagate conversion failures through app-server and TUI approval handling - regenerate the app-server JSON and TypeScript schemas - leave migration TODOs on native-path conversions so they can be removed once core permission paths use `PathUri`
177 lines
6.7 KiB
Rust
177 lines
6.7 KiB
Rust
//! Narrow conversion helpers for approval-related app-server payloads.
|
|
//!
|
|
//! The TUI mostly keeps app-server approval types intact. These helpers cover
|
|
//! the remaining cases where the UI consumes a private file-change display
|
|
//! model or needs to translate a granted permission response for outbound
|
|
//! submission.
|
|
|
|
use crate::diff_model::FileChange;
|
|
use codex_app_server_protocol::AdditionalNetworkPermissions;
|
|
use codex_app_server_protocol::FileUpdateChange;
|
|
use codex_app_server_protocol::GrantedPermissionProfile;
|
|
use codex_app_server_protocol::PatchChangeKind;
|
|
use codex_protocol::request_permissions::RequestPermissionProfile as CoreRequestPermissionProfile;
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
|
|
pub(crate) fn granted_permission_profile_from_request(
|
|
value: CoreRequestPermissionProfile,
|
|
) -> GrantedPermissionProfile {
|
|
GrantedPermissionProfile {
|
|
network: value.network.map(|network| AdditionalNetworkPermissions {
|
|
enabled: network.enabled,
|
|
}),
|
|
file_system: value.file_system.map(Into::into),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn file_update_changes_to_display(
|
|
changes: Vec<FileUpdateChange>,
|
|
) -> HashMap<PathBuf, FileChange> {
|
|
changes
|
|
.into_iter()
|
|
.map(|change| {
|
|
let path = PathBuf::from(change.path);
|
|
let file_change = match change.kind {
|
|
PatchChangeKind::Add => FileChange::Add {
|
|
content: change.diff,
|
|
},
|
|
PatchChangeKind::Delete => FileChange::Delete {
|
|
content: change.diff,
|
|
},
|
|
PatchChangeKind::Update { move_path } => FileChange::Update {
|
|
unified_diff: change.diff,
|
|
move_path,
|
|
},
|
|
};
|
|
(path, file_change)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::file_update_changes_to_display;
|
|
use super::granted_permission_profile_from_request;
|
|
use crate::diff_model::FileChange;
|
|
use codex_app_server_protocol::AdditionalFileSystemPermissions;
|
|
use codex_app_server_protocol::AdditionalNetworkPermissions;
|
|
use codex_app_server_protocol::FileSystemAccessMode;
|
|
use codex_app_server_protocol::FileSystemPath;
|
|
use codex_app_server_protocol::FileSystemSandboxEntry;
|
|
use codex_app_server_protocol::FileSystemSpecialPath;
|
|
use codex_app_server_protocol::FileUpdateChange;
|
|
use codex_app_server_protocol::GrantedPermissionProfile;
|
|
use codex_app_server_protocol::PatchChangeKind;
|
|
use codex_app_server_protocol::RequestPermissionProfile;
|
|
use codex_protocol::request_permissions::RequestPermissionProfile as CoreRequestPermissionProfile;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use pretty_assertions::assert_eq;
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
|
|
fn absolute_path(path: &str) -> AbsolutePathBuf {
|
|
AbsolutePathBuf::try_from(PathBuf::from(path)).expect("path must be absolute")
|
|
}
|
|
|
|
#[test]
|
|
fn converts_file_update_changes_to_display() {
|
|
assert_eq!(
|
|
file_update_changes_to_display(vec![FileUpdateChange {
|
|
path: "foo.txt".to_string(),
|
|
kind: PatchChangeKind::Add,
|
|
diff: "hello\n".to_string(),
|
|
}]),
|
|
HashMap::from([(
|
|
PathBuf::from("foo.txt"),
|
|
FileChange::Add {
|
|
content: "hello\n".to_string(),
|
|
},
|
|
)])
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn converts_request_permissions_into_granted_permissions() {
|
|
let request = RequestPermissionProfile {
|
|
network: Some(AdditionalNetworkPermissions {
|
|
enabled: Some(true),
|
|
}),
|
|
file_system: Some(AdditionalFileSystemPermissions {
|
|
read: Some(vec![absolute_path("/tmp/read-only").into()]),
|
|
write: Some(vec![absolute_path("/tmp/write").into()]),
|
|
glob_scan_max_depth: None,
|
|
entries: None,
|
|
}),
|
|
};
|
|
let request = CoreRequestPermissionProfile::try_from(request)
|
|
.expect("API paths should convert to native paths");
|
|
|
|
assert_eq!(
|
|
granted_permission_profile_from_request(request),
|
|
GrantedPermissionProfile {
|
|
network: Some(AdditionalNetworkPermissions {
|
|
enabled: Some(true),
|
|
}),
|
|
file_system: Some(AdditionalFileSystemPermissions {
|
|
read: Some(vec![absolute_path("/tmp/read-only").into()]),
|
|
write: Some(vec![absolute_path("/tmp/write").into()]),
|
|
glob_scan_max_depth: None,
|
|
entries: Some(vec![
|
|
FileSystemSandboxEntry {
|
|
path: FileSystemPath::Path {
|
|
path: absolute_path("/tmp/read-only").into(),
|
|
},
|
|
access: FileSystemAccessMode::Read,
|
|
},
|
|
FileSystemSandboxEntry {
|
|
path: FileSystemPath::Path {
|
|
path: absolute_path("/tmp/write").into(),
|
|
},
|
|
access: FileSystemAccessMode::Write,
|
|
},
|
|
]),
|
|
}),
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn converts_request_permissions_into_canonical_granted_permissions() {
|
|
let request = RequestPermissionProfile {
|
|
network: None,
|
|
file_system: Some(AdditionalFileSystemPermissions {
|
|
read: None,
|
|
write: None,
|
|
glob_scan_max_depth: None,
|
|
entries: Some(vec![FileSystemSandboxEntry {
|
|
path: FileSystemPath::Special {
|
|
value: FileSystemSpecialPath::Root,
|
|
},
|
|
access: FileSystemAccessMode::Write,
|
|
}]),
|
|
}),
|
|
};
|
|
let request = CoreRequestPermissionProfile::try_from(request)
|
|
.expect("API paths should convert to native paths");
|
|
|
|
assert_eq!(
|
|
granted_permission_profile_from_request(request),
|
|
GrantedPermissionProfile {
|
|
network: None,
|
|
file_system: Some(AdditionalFileSystemPermissions {
|
|
read: None,
|
|
write: None,
|
|
glob_scan_max_depth: None,
|
|
entries: Some(vec![FileSystemSandboxEntry {
|
|
path: FileSystemPath::Special {
|
|
value: FileSystemSpecialPath::Root,
|
|
},
|
|
access: FileSystemAccessMode::Write,
|
|
}]),
|
|
}),
|
|
}
|
|
);
|
|
}
|
|
}
|