mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Use AbsolutePathBuf in skill loading and codex_home (#17407)
Helps with FS migration later
This commit is contained in:
+27
-30
@@ -60,6 +60,8 @@ use crate::resume_picker::SessionSelection;
|
||||
use crate::resume_picker::SessionTarget;
|
||||
#[cfg(test)]
|
||||
use crate::test_support::PathBufExt;
|
||||
#[cfg(test)]
|
||||
use crate::test_support::test_path_buf;
|
||||
use crate::tui;
|
||||
use crate::tui::TuiEvent;
|
||||
use crate::update_action::UpdateAction;
|
||||
@@ -1112,7 +1114,7 @@ impl App {
|
||||
overrides.cwd = Some(cwd.clone());
|
||||
let cwd_display = cwd.display().to_string();
|
||||
ConfigBuilder::default()
|
||||
.codex_home(self.config.codex_home.clone())
|
||||
.codex_home(self.config.codex_home.to_path_buf())
|
||||
.cli_overrides(self.cli_kv_overrides.clone())
|
||||
.harness_overrides(overrides)
|
||||
.build()
|
||||
@@ -3867,13 +3869,7 @@ impl App {
|
||||
let tx = app.app_event_tx.clone();
|
||||
let logs_base_dir = app.config.codex_home.clone();
|
||||
let sandbox_policy = app.config.permissions.sandbox_policy.get().clone();
|
||||
Self::spawn_world_writable_scan(
|
||||
cwd.to_path_buf(),
|
||||
env_map,
|
||||
logs_base_dir,
|
||||
sandbox_policy,
|
||||
tx,
|
||||
);
|
||||
Self::spawn_world_writable_scan(cwd, env_map, logs_base_dir, sandbox_policy, tx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5231,7 +5227,7 @@ impl App {
|
||||
let logs_base_dir = self.config.codex_home.clone();
|
||||
let sandbox_policy = self.config.permissions.sandbox_policy.get().clone();
|
||||
Self::spawn_world_writable_scan(
|
||||
cwd.to_path_buf(),
|
||||
cwd,
|
||||
env_map,
|
||||
logs_base_dir,
|
||||
sandbox_policy,
|
||||
@@ -5409,7 +5405,7 @@ impl App {
|
||||
}
|
||||
AppEvent::SetSkillEnabled { path, enabled } => {
|
||||
let edits = [ConfigEdit::SetSkillConfig {
|
||||
path: path.clone(),
|
||||
path: path.to_path_buf(),
|
||||
enabled,
|
||||
}];
|
||||
match ConfigEditsBuilder::new(&self.config.codex_home)
|
||||
@@ -5418,7 +5414,7 @@ impl App {
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
self.chat_widget.update_skill_enabled(path.clone(), enabled);
|
||||
self.chat_widget.update_skill_enabled(path, enabled);
|
||||
if let Err(err) = self.refresh_in_memory_config_from_disk().await {
|
||||
tracing::warn!(
|
||||
error = %err,
|
||||
@@ -6111,19 +6107,20 @@ impl App {
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn spawn_world_writable_scan(
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
env_map: std::collections::HashMap<String, String>,
|
||||
logs_base_dir: PathBuf,
|
||||
logs_base_dir: AbsolutePathBuf,
|
||||
sandbox_policy: codex_protocol::protocol::SandboxPolicy,
|
||||
tx: AppEventSender,
|
||||
) {
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let logs_base_dir_path = logs_base_dir.as_path();
|
||||
let result = codex_windows_sandbox::apply_world_writable_scan_and_denies(
|
||||
&logs_base_dir,
|
||||
&cwd,
|
||||
logs_base_dir_path,
|
||||
cwd.as_path(),
|
||||
&env_map,
|
||||
&sandbox_policy,
|
||||
Some(logs_base_dir.as_path()),
|
||||
Some(logs_base_dir_path),
|
||||
);
|
||||
if result.is_err() {
|
||||
// Scan failed: warn without examples.
|
||||
@@ -7905,7 +7902,7 @@ mod tests {
|
||||
async fn update_feature_flags_enabling_guardian_selects_guardian_approvals() -> Result<()> {
|
||||
let (mut app, mut app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
let guardian_approvals = guardian_approvals_mode();
|
||||
|
||||
app.update_feature_flags(vec![(Feature::GuardianApproval, true)])
|
||||
@@ -7989,7 +7986,7 @@ mod tests {
|
||||
-> Result<()> {
|
||||
let (mut app, mut app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = "approvals_reviewer = \"guardian_subagent\"\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\n\n[features]\nguardian_approval = true\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
@@ -8080,7 +8077,7 @@ mod tests {
|
||||
-> Result<()> {
|
||||
let (mut app, _app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
let guardian_approvals = guardian_approvals_mode();
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = "approvals_reviewer = \"user\"\n";
|
||||
@@ -8148,7 +8145,7 @@ mod tests {
|
||||
-> Result<()> {
|
||||
let (mut app, mut app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = "approvals_reviewer = \"user\"\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\n\n[features]\nguardian_approval = true\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
@@ -8207,7 +8204,7 @@ mod tests {
|
||||
-> Result<()> {
|
||||
let (mut app, _app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
let guardian_approvals = guardian_approvals_mode();
|
||||
app.active_profile = Some("guardian".to_string());
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
@@ -8278,7 +8275,7 @@ mod tests {
|
||||
-> Result<()> {
|
||||
let (mut app, mut app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
app.active_profile = Some("guardian".to_string());
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = r#"
|
||||
@@ -8366,7 +8363,7 @@ guardian_approval = true
|
||||
-> Result<()> {
|
||||
let (mut app, mut app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
app.active_profile = Some("guardian".to_string());
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = "profile = \"guardian\"\napprovals_reviewer = \"guardian_subagent\"\n\n[features]\nguardian_approval = true\n";
|
||||
@@ -9083,7 +9080,7 @@ guardian_approval = true
|
||||
|
||||
async fn render_clear_ui_header_after_long_transcript_for_snapshot() -> String {
|
||||
let mut app = make_test_app().await;
|
||||
app.config.cwd = PathBuf::from("/tmp/project").abs();
|
||||
app.config.cwd = test_path_buf("/tmp/project").abs();
|
||||
app.chat_widget.set_model("gpt-test");
|
||||
app.chat_widget
|
||||
.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
@@ -9136,7 +9133,7 @@ guardian_approval = true
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
cwd: PathBuf::from("/tmp/project").abs().to_path_buf(),
|
||||
cwd: test_path_buf("/tmp/project"),
|
||||
reasoning_effort: Some(ReasoningEffortConfig::High),
|
||||
history_log_id: 0,
|
||||
history_entry_count: 0,
|
||||
@@ -9220,7 +9217,7 @@ guardian_approval = true
|
||||
)]
|
||||
async fn clear_ui_header_shows_fast_status_for_fast_capable_models() {
|
||||
let mut app = make_test_app().await;
|
||||
app.config.cwd = PathBuf::from("/tmp/project").abs();
|
||||
app.config.cwd = test_path_buf("/tmp/project").abs();
|
||||
app.chat_widget.set_model("gpt-5.4");
|
||||
set_fast_mode_test_catalog(&mut app.chat_widget);
|
||||
app.chat_widget
|
||||
@@ -10229,7 +10226,7 @@ guardian_approval = true
|
||||
async fn refresh_in_memory_config_from_disk_loads_latest_apps_state() -> Result<()> {
|
||||
let mut app = make_test_app().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
let app_id = "unit_test_refresh_in_memory_config_connector".to_string();
|
||||
|
||||
assert_eq!(app_enabled_in_effective_config(&app.config, &app_id), None);
|
||||
@@ -10269,7 +10266,7 @@ guardian_approval = true
|
||||
-> Result<()> {
|
||||
let mut app = make_test_app().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
std::fs::write(codex_home.path().join("config.toml"), "[broken")?;
|
||||
let original_config = app.config.clone();
|
||||
|
||||
@@ -10323,7 +10320,7 @@ guardian_approval = true
|
||||
-> Result<()> {
|
||||
let mut app = make_test_app().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
std::fs::write(codex_home.path().join("config.toml"), "[broken")?;
|
||||
let current_config = app.config.clone();
|
||||
let current_cwd = current_config.cwd.clone();
|
||||
@@ -10340,7 +10337,7 @@ guardian_approval = true
|
||||
async fn rebuild_config_for_resume_or_fallback_errors_when_cwd_changes() -> Result<()> {
|
||||
let mut app = make_test_app().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
std::fs::write(codex_home.path().join("config.toml"), "[broken")?;
|
||||
let current_cwd = app.config.cwd.clone();
|
||||
let next_cwd_tmp = tempdir()?;
|
||||
|
||||
@@ -502,7 +502,7 @@ pub(crate) enum AppEvent {
|
||||
|
||||
/// Enable or disable a skill by path.
|
||||
SetSkillEnabled {
|
||||
path: PathBuf,
|
||||
path: AbsolutePathBuf,
|
||||
enabled: bool,
|
||||
},
|
||||
|
||||
|
||||
@@ -4008,6 +4008,8 @@ impl ChatComposer {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_support::PathBufExt;
|
||||
use crate::test_support::test_path_buf;
|
||||
use image::ImageBuffer;
|
||||
use image::Rgba;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -5053,6 +5055,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn mention_items_show_plugin_owned_skill_and_app_duplicates() {
|
||||
let skill_path = test_path_buf("/tmp/repo/google-calendar/SKILL.md").abs();
|
||||
let (tx, _rx) = unbounded_channel::<AppEvent>();
|
||||
let sender = AppEventSender::new(tx);
|
||||
let mut composer = ChatComposer::new(
|
||||
@@ -5078,7 +5081,7 @@ mod tests {
|
||||
}),
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
path_to_skills_md: PathBuf::from("/tmp/repo/google-calendar/SKILL.md"),
|
||||
path_to_skills_md: skill_path.clone(),
|
||||
scope: codex_protocol::protocol::SkillScope::Repo,
|
||||
}]));
|
||||
composer.set_plugin_mentions(Some(vec![PluginCapabilitySummary {
|
||||
@@ -5115,10 +5118,7 @@ mod tests {
|
||||
let mentions = composer.mention_items();
|
||||
assert_eq!(mentions.len(), 3);
|
||||
assert_eq!(mentions[0].category_tag, Some("[Skill]".to_string()));
|
||||
assert_eq!(
|
||||
mentions[0].path,
|
||||
Some("/tmp/repo/google-calendar/SKILL.md".to_string())
|
||||
);
|
||||
assert_eq!(mentions[0].path, Some(skill_path.display().to_string()));
|
||||
assert_eq!(mentions[0].display_name, "Google Calendar".to_string());
|
||||
assert_eq!(mentions[1].category_tag, Some("[Plugin]".to_string()));
|
||||
assert_eq!(
|
||||
@@ -5176,7 +5176,7 @@ mod tests {
|
||||
}),
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
path_to_skills_md: PathBuf::from("/tmp/repo/google-calendar/SKILL.md"),
|
||||
path_to_skills_md: test_path_buf("/tmp/repo/google-calendar/SKILL.md").abs(),
|
||||
scope: codex_protocol::protocol::SkillScope::Repo,
|
||||
}]));
|
||||
composer.set_plugin_mentions(Some(vec![PluginCapabilitySummary {
|
||||
|
||||
@@ -1242,6 +1242,8 @@ mod tests {
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::status_indicator_widget::STATUS_DETAILS_DEFAULT_MAX_LINES;
|
||||
use crate::status_indicator_widget::StatusDetailsCapitalization;
|
||||
use crate::test_support::PathBufExt;
|
||||
use crate::test_support::test_path_buf;
|
||||
use codex_protocol::protocol::Op;
|
||||
use codex_protocol::protocol::SkillScope;
|
||||
use crossterm::event::KeyEventKind;
|
||||
@@ -1250,7 +1252,6 @@ mod tests {
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::Rect;
|
||||
use std::cell::Cell;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use tokio::sync::mpsc::unbounded_channel;
|
||||
|
||||
@@ -1719,7 +1720,7 @@ mod tests {
|
||||
interface: None,
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
path_to_skills_md: PathBuf::from("test-skill"),
|
||||
path_to_skills_md: test_path_buf("/tmp/test-skill/SKILL.md").abs(),
|
||||
scope: SkillScope::User,
|
||||
}]),
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
use crossterm::event::KeyModifiers;
|
||||
@@ -38,7 +37,7 @@ pub(crate) struct SkillsToggleItem {
|
||||
pub skill_name: String,
|
||||
pub description: String,
|
||||
pub enabled: bool,
|
||||
pub path: PathBuf,
|
||||
pub path: AbsolutePathBuf,
|
||||
}
|
||||
|
||||
pub(crate) struct SkillsToggleView {
|
||||
@@ -381,6 +380,8 @@ fn skills_toggle_hint_line() -> Line<'static> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::test_support::PathBufExt;
|
||||
use crate::test_support::test_path_buf;
|
||||
use insta::assert_snapshot;
|
||||
use ratatui::layout::Rect;
|
||||
use tokio::sync::mpsc::unbounded_channel;
|
||||
@@ -418,14 +419,14 @@ mod tests {
|
||||
skill_name: "repo_scout".to_string(),
|
||||
description: "Summarize the repo layout".to_string(),
|
||||
enabled: true,
|
||||
path: PathBuf::from("/tmp/skills/repo_scout.toml"),
|
||||
path: test_path_buf("/tmp/skills/repo_scout.toml").abs(),
|
||||
},
|
||||
SkillsToggleItem {
|
||||
name: "Changelog Writer".to_string(),
|
||||
skill_name: "changelog_writer".to_string(),
|
||||
description: "Draft release notes".to_string(),
|
||||
enabled: false,
|
||||
path: PathBuf::from("/tmp/skills/changelog_writer.toml"),
|
||||
path: test_path_buf("/tmp/skills/changelog_writer.toml").abs(),
|
||||
},
|
||||
];
|
||||
let view = SkillsToggleView::new(items, tx);
|
||||
|
||||
@@ -801,7 +801,7 @@ pub(crate) struct ChatWidget {
|
||||
pending_collab_spawn_requests: HashMap<String, multi_agents::SpawnRequestSummary>,
|
||||
suppressed_exec_calls: HashSet<String>,
|
||||
skills_all: Vec<ProtocolSkillMetadata>,
|
||||
skills_initial_state: Option<HashMap<PathBuf, bool>>,
|
||||
skills_initial_state: Option<HashMap<AbsolutePathBuf, bool>>,
|
||||
last_unified_wait: Option<UnifiedExecWaitState>,
|
||||
unified_exec_wait_streak: Option<UnifiedExecWaitStreak>,
|
||||
turn_sleep_inhibitor: SleepInhibitor,
|
||||
@@ -5353,7 +5353,7 @@ impl ChatWidget {
|
||||
.map(|binding| binding.mention.clone())
|
||||
.collect();
|
||||
let mut skill_names_lower: HashSet<String> = HashSet::new();
|
||||
let mut selected_skill_paths: HashSet<PathBuf> = HashSet::new();
|
||||
let mut selected_skill_paths: HashSet<AbsolutePathBuf> = HashSet::new();
|
||||
let mut selected_plugin_ids: HashSet<String> = HashSet::new();
|
||||
|
||||
if let Some(skills) = self.bottom_pane.skills() {
|
||||
@@ -5375,7 +5375,7 @@ impl ChatWidget {
|
||||
{
|
||||
items.push(UserInput::Skill {
|
||||
name: skill.name.clone(),
|
||||
path: skill.path_to_skills_md.clone(),
|
||||
path: skill.path_to_skills_md.to_path_buf(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5389,7 +5389,7 @@ impl ChatWidget {
|
||||
}
|
||||
items.push(UserInput::Skill {
|
||||
name: skill.name.clone(),
|
||||
path: skill.path_to_skills_md.clone(),
|
||||
path: skill.path_to_skills_md.to_path_buf(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10341,7 +10341,7 @@ impl ChatWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
let plugins = PluginsManager::new(self.config.codex_home.clone())
|
||||
let plugins = PluginsManager::new(self.config.codex_home.to_path_buf())
|
||||
.plugins_for_config(&self.config)
|
||||
.capability_summaries()
|
||||
.to_vec();
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::ChatWidget;
|
||||
use crate::app_event::AppEvent;
|
||||
@@ -23,6 +21,7 @@ use codex_protocol::parse_command::ParsedCommand;
|
||||
use codex_protocol::protocol::ListSkillsResponseEvent;
|
||||
use codex_protocol::protocol::SkillMetadata as ProtocolSkillMetadata;
|
||||
use codex_protocol::protocol::SkillsListEntry;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
impl ChatWidget {
|
||||
pub(crate) fn open_skills_list(&mut self) {
|
||||
@@ -68,7 +67,7 @@ impl ChatWidget {
|
||||
|
||||
let mut initial_state = HashMap::new();
|
||||
for skill in &self.skills_all {
|
||||
initial_state.insert(normalize_skill_config_path(&skill.path), skill.enabled);
|
||||
initial_state.insert(skill.path.clone(), skill.enabled);
|
||||
}
|
||||
self.skills_initial_state = Some(initial_state);
|
||||
|
||||
@@ -95,10 +94,9 @@ impl ChatWidget {
|
||||
self.bottom_pane.show_view(Box::new(view));
|
||||
}
|
||||
|
||||
pub(crate) fn update_skill_enabled(&mut self, path: PathBuf, enabled: bool) {
|
||||
let target = normalize_skill_config_path(&path);
|
||||
pub(crate) fn update_skill_enabled(&mut self, path: AbsolutePathBuf, enabled: bool) {
|
||||
for skill in &mut self.skills_all {
|
||||
if normalize_skill_config_path(&skill.path) == target {
|
||||
if skill.path == path {
|
||||
skill.enabled = enabled;
|
||||
}
|
||||
}
|
||||
@@ -111,7 +109,7 @@ impl ChatWidget {
|
||||
};
|
||||
let mut current_state = HashMap::new();
|
||||
for skill in &self.skills_all {
|
||||
current_state.insert(normalize_skill_config_path(&skill.path), skill.enabled);
|
||||
current_state.insert(skill.path.clone(), skill.enabled);
|
||||
}
|
||||
|
||||
let mut enabled_count = 0;
|
||||
@@ -161,7 +159,11 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
// Best effort only: annotate exact SKILL.md path matches from the loaded skills list.
|
||||
if let Some(skill) = self.skills_all.iter().find(|skill| skill.path == *path) {
|
||||
if let Some(skill) = self
|
||||
.skills_all
|
||||
.iter()
|
||||
.find(|skill| skill.path.as_path() == path)
|
||||
{
|
||||
*name = format!("{name} ({} skill)", skill.name);
|
||||
}
|
||||
}
|
||||
@@ -170,10 +172,13 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
|
||||
fn skills_for_cwd(cwd: &Path, skills_entries: &[SkillsListEntry]) -> Vec<ProtocolSkillMetadata> {
|
||||
fn skills_for_cwd(
|
||||
cwd: &AbsolutePathBuf,
|
||||
skills_entries: &[SkillsListEntry],
|
||||
) -> Vec<ProtocolSkillMetadata> {
|
||||
skills_entries
|
||||
.iter()
|
||||
.find(|entry| entry.cwd.as_path() == cwd)
|
||||
.find(|entry| entry.cwd.as_path() == cwd.as_path())
|
||||
.map(|entry| entry.skills.clone())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
@@ -222,10 +227,6 @@ fn protocol_skill_to_core(skill: &ProtocolSkillMetadata) -> SkillMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_skill_config_path(path: &Path) -> PathBuf {
|
||||
dunce::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_tool_mentions(
|
||||
text: &str,
|
||||
mention_paths: &HashMap<String, String>,
|
||||
|
||||
@@ -29,6 +29,7 @@ pub(super) use crate::legacy_core::skills::model::SkillMetadata;
|
||||
pub(super) use crate::model_catalog::ModelCatalog;
|
||||
pub(super) use crate::test_backend::VT100Backend;
|
||||
pub(super) use crate::test_support::PathBufExt;
|
||||
pub(super) use crate::test_support::test_path_buf;
|
||||
pub(super) use crate::test_support::test_path_display;
|
||||
pub(super) use crate::tui::FrameRequester;
|
||||
pub(super) use assert_matches::assert_matches;
|
||||
|
||||
@@ -398,8 +398,8 @@ async fn submission_prefers_selected_duplicate_skill_path() {
|
||||
});
|
||||
drain_insert_history(&mut rx);
|
||||
|
||||
let repo_skill_path = PathBuf::from("/tmp/repo/figma/SKILL.md");
|
||||
let user_skill_path = PathBuf::from("/tmp/user/figma/SKILL.md");
|
||||
let repo_skill_path = test_path_buf("/tmp/repo/figma/SKILL.md").abs();
|
||||
let user_skill_path = test_path_buf("/tmp/user/figma/SKILL.md").abs();
|
||||
chat.set_skills(Some(vec![
|
||||
SkillMetadata {
|
||||
name: "figma".to_string(),
|
||||
@@ -445,7 +445,7 @@ async fn submission_prefers_selected_duplicate_skill_path() {
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(selected_skill_paths, vec![user_skill_path]);
|
||||
assert_eq!(selected_skill_paths, vec![user_skill_path.to_path_buf()]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -11,7 +11,7 @@ pub(super) async fn test_config() -> Config {
|
||||
let mut config =
|
||||
Config::load_default_with_cli_overrides_for_codex_home(codex_home.clone(), Vec::new())
|
||||
.expect("config");
|
||||
config.codex_home = codex_home.clone();
|
||||
config.codex_home = codex_home.abs();
|
||||
config.sqlite_home = codex_home.clone();
|
||||
config.log_dir = codex_home.join("log");
|
||||
config.cwd = PathBuf::from(test_path_display("/tmp/project")).abs();
|
||||
@@ -951,7 +951,7 @@ pub(super) fn plugins_test_detail(
|
||||
description: format!("{name} description"),
|
||||
short_description: None,
|
||||
interface: None,
|
||||
path: PathBuf::from(format!("/skills/{name}/SKILL.md")),
|
||||
path: plugins_test_absolute_path(&format!("skills/{name}/SKILL.md")),
|
||||
enabled: true,
|
||||
})
|
||||
.collect(),
|
||||
|
||||
@@ -245,10 +245,10 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::new_workspace_write_policy())
|
||||
.expect("set sandbox policy");
|
||||
chat.config.cwd = PathBuf::from("/home/user/main").abs();
|
||||
chat.config.cwd = test_path_buf("/home/user/main").abs();
|
||||
|
||||
let expected_sandbox = SandboxPolicy::new_read_only_policy();
|
||||
let expected_cwd = PathBuf::from("/home/user/sub-agent").abs();
|
||||
let expected_cwd = test_path_buf("/home/user/sub-agent").abs();
|
||||
let configured = codex_protocol::protocol::SessionConfiguredEvent {
|
||||
session_id: ThreadId::new(),
|
||||
forked_from_id: None,
|
||||
@@ -392,7 +392,9 @@ async fn forked_thread_history_line_includes_name_and_id_snapshot() {
|
||||
let (chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
let mut chat = chat;
|
||||
let temp = tempdir().expect("tempdir");
|
||||
chat.config.codex_home = temp.path().to_path_buf();
|
||||
chat.config.codex_home =
|
||||
codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path(temp.path())
|
||||
.expect("temp dir is absolute");
|
||||
|
||||
let forked_from_id =
|
||||
ThreadId::from_string("e9f18a88-8081-4e51-9d4e-8af5cde2d8dd").expect("forked id");
|
||||
@@ -429,7 +431,9 @@ async fn forked_thread_history_line_without_name_shows_id_once_snapshot() {
|
||||
let (chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
let mut chat = chat;
|
||||
let temp = tempdir().expect("tempdir");
|
||||
chat.config.codex_home = temp.path().to_path_buf();
|
||||
chat.config.codex_home =
|
||||
codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path(temp.path())
|
||||
.expect("temp dir is absolute");
|
||||
|
||||
let forked_from_id =
|
||||
ThreadId::from_string("019c2d47-4935-7423-a190-05691f566092").expect("forked id");
|
||||
|
||||
@@ -51,8 +51,9 @@ async fn preset_matching_accepts_workspace_write_with_extra_roots() {
|
||||
.into_iter()
|
||||
.find(|p| p.id == "auto")
|
||||
.expect("auto preset exists");
|
||||
let extra_root = test_path_buf("/tmp/extra").abs();
|
||||
let current_sandbox = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![PathBuf::from("C:\\extra").abs()],
|
||||
writable_roots: vec![extra_root],
|
||||
read_only_access: Default::default(),
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: false,
|
||||
@@ -496,7 +497,7 @@ async fn permissions_selection_marks_guardian_approvals_current_with_custom_work
|
||||
.features
|
||||
.set_enabled(Feature::GuardianApproval, /*enabled*/ true);
|
||||
|
||||
let extra_root = PathBuf::from("/tmp/guardian-approvals-extra").abs();
|
||||
let extra_root = test_path_buf("/tmp/guardian-approvals-extra").abs();
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "session-configured-custom-workspace".to_string(),
|
||||
|
||||
@@ -35,6 +35,8 @@ use crate::style::proposed_plan_style;
|
||||
use crate::style::user_message_style;
|
||||
#[cfg(test)]
|
||||
use crate::test_support::PathBufExt;
|
||||
#[cfg(test)]
|
||||
use crate::test_support::test_path_buf;
|
||||
use crate::text_formatting::format_and_truncate_tool_result;
|
||||
use crate::text_formatting::truncate_text;
|
||||
use crate::tooltips;
|
||||
@@ -1843,7 +1845,9 @@ pub(crate) fn new_mcp_tools_output(
|
||||
lines.push("".into());
|
||||
}
|
||||
|
||||
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(config.codex_home.clone())));
|
||||
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(
|
||||
config.codex_home.to_path_buf(),
|
||||
)));
|
||||
let effective_servers = mcp_manager.effective_servers(config, /*auth*/ None);
|
||||
let mut servers: Vec<_> = effective_servers.iter().collect();
|
||||
servers.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
@@ -2988,7 +2992,7 @@ mod tests {
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: codex_protocol::config_types::ApprovalsReviewer::User,
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
cwd: PathBuf::from("/tmp/project").abs().to_path_buf(),
|
||||
cwd: test_path_buf("/tmp/project"),
|
||||
reasoning_effort: None,
|
||||
history_log_id: 0,
|
||||
history_entry_count: 0,
|
||||
@@ -3108,7 +3112,7 @@ mod tests {
|
||||
)]
|
||||
async fn session_info_availability_nux_tooltip_snapshot() {
|
||||
let mut config = test_config().await;
|
||||
config.cwd = PathBuf::from("/tmp/project").abs();
|
||||
config.cwd = test_path_buf("/tmp/project").abs();
|
||||
let cell = new_session_info(
|
||||
&config,
|
||||
"gpt-5",
|
||||
|
||||
@@ -870,7 +870,7 @@ pub async fn run_main(
|
||||
if matches!(app_server_target, AppServerTarget::Embedded) {
|
||||
#[allow(clippy::print_stderr)]
|
||||
if let Err(err) = enforce_login_restrictions(&AuthConfig {
|
||||
codex_home: config.codex_home.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
auth_credentials_store_mode: config.cli_auth_credentials_store_mode,
|
||||
forced_login_method: config.forced_login_method,
|
||||
forced_chatgpt_workspace_id: config.forced_chatgpt_workspace_id.clone(),
|
||||
@@ -1137,7 +1137,7 @@ async fn run_ratatui_app(
|
||||
// status detection edge cases.
|
||||
if show_login_screen && !remote_mode {
|
||||
cloud_requirements = cloud_requirements_loader_for_storage(
|
||||
initial_config.codex_home.clone(),
|
||||
initial_config.codex_home.to_path_buf(),
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
initial_config.cli_auth_credentials_store_mode,
|
||||
initial_config.chatgpt_base_url.clone(),
|
||||
@@ -1378,7 +1378,7 @@ async fn run_ratatui_app(
|
||||
// this must happen after the last possible reload.
|
||||
if let Some(w) = crate::render::highlight::set_theme_override(
|
||||
config.tui_theme.clone(),
|
||||
find_codex_home().ok(),
|
||||
find_codex_home().ok().map(AbsolutePathBuf::into_path_buf),
|
||||
) {
|
||||
config.startup_warnings.push(w);
|
||||
}
|
||||
@@ -2052,7 +2052,7 @@ mod tests {
|
||||
std::fs::write(&rollout_path, "")?;
|
||||
|
||||
let state_runtime = codex_state::StateRuntime::init(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
config.model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
@@ -2481,7 +2481,7 @@ trust_level = "untrusted"
|
||||
)?;
|
||||
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
config.model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -86,7 +86,7 @@ impl OnboardingScreen {
|
||||
config,
|
||||
} = args;
|
||||
let cwd = config.cwd.to_path_buf();
|
||||
let codex_home = config.codex_home.clone();
|
||||
let codex_home = config.codex_home.to_path_buf();
|
||||
let forced_login_method = config.forced_login_method;
|
||||
let mut steps: Vec<Step> = Vec::new();
|
||||
steps.push(Step::Welcome(WelcomeWidget::new(
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::legacy_core::config::Config;
|
||||
use crate::legacy_core::config::ConfigBuilder;
|
||||
use crate::status::StatusAccountDisplay;
|
||||
use crate::test_support::PathBufExt;
|
||||
use crate::test_support::test_path_buf;
|
||||
use chrono::Duration as ChronoDuration;
|
||||
use chrono::TimeZone;
|
||||
use chrono::Utc;
|
||||
@@ -22,7 +23,6 @@ use codex_protocol::protocol::TokenUsageInfo;
|
||||
use insta::assert_snapshot;
|
||||
use pretty_assertions::assert_eq;
|
||||
use ratatui::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::TempDir;
|
||||
|
||||
async fn test_config(temp_home: &TempDir) -> Config {
|
||||
@@ -108,7 +108,7 @@ async fn status_snapshot_includes_reasoning_details() {
|
||||
})
|
||||
.expect("set sandbox policy");
|
||||
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -192,7 +192,7 @@ async fn status_permissions_non_default_workspace_write_is_custom() {
|
||||
exclude_slash_tmp: false,
|
||||
})
|
||||
.expect("set sandbox policy");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage::default();
|
||||
@@ -241,7 +241,7 @@ async fn status_snapshot_includes_forked_from() {
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.model_provider_id = "openai".to_string();
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -295,7 +295,7 @@ async fn status_snapshot_includes_monthly_limit() {
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.model_provider_id = "openai".to_string();
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -548,7 +548,7 @@ async fn status_card_token_usage_excludes_cached_tokens() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -596,7 +596,7 @@ async fn status_snapshot_truncates_in_narrow_terminal() {
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.model_provider_id = "openai".to_string();
|
||||
config.model_reasoning_summary = Some(ReasoningSummary::Detailed);
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -659,7 +659,7 @@ async fn status_snapshot_shows_missing_limits_message() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -707,7 +707,7 @@ async fn status_snapshot_shows_refreshing_limits_notice() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 500,
|
||||
@@ -771,7 +771,7 @@ async fn status_snapshot_includes_credits_and_limits() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -840,7 +840,7 @@ async fn status_snapshot_shows_unavailable_limits_message() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -897,7 +897,7 @@ async fn status_snapshot_treats_refreshing_empty_limits_as_unavailable() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 500,
|
||||
@@ -954,7 +954,7 @@ async fn status_snapshot_shows_stale_limits_message() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -1020,7 +1020,7 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
pub(crate) use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
pub(crate) use codex_utils_absolute_path::test_support::PathExt;
|
||||
use std::path::Path;
|
||||
pub(crate) use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
|
||||
pub(crate) fn test_path_display(path: &str) -> String {
|
||||
Path::new(path).abs().display().to_string()
|
||||
test_path_buf(path).display().to_string()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user