prodex 0.27.0

OpenAI profile pooling and safe auto-rotate for Codex CLI and Claude Code
Documentation
use super::*;

pub(super) fn migrate_shared_codex_entry(
    local_path: &Path,
    shared_path: &Path,
    kind: SharedCodexEntryKind,
) -> Result<()> {
    let Some(metadata) = load_shared_codex_entry_metadata(local_path)? else {
        return Ok(());
    };

    if metadata.file_type().is_symlink() {
        migrate_shared_codex_symlink_target(local_path, shared_path, kind)?;
        remove_path(local_path)?;
        return Ok(());
    }

    match kind {
        SharedCodexEntryKind::Directory => {
            migrate_shared_codex_directory_entry(local_path, shared_path, &metadata)
        }
        SharedCodexEntryKind::File => {
            migrate_shared_codex_file_entry(local_path, shared_path, &metadata)
        }
    }
}

fn migrate_shared_codex_directory_entry(
    local_path: &Path,
    shared_path: &Path,
    metadata: &fs::Metadata,
) -> Result<()> {
    ensure_shared_codex_directory_public(local_path, metadata)?;
    if !shared_path.exists() {
        move_directory(local_path, shared_path)?;
        return Ok(());
    }

    ensure_shared_codex_path_is_directory_public(shared_path)?;
    copy_directory_contents(local_path, shared_path)?;
    fs::remove_dir_all(local_path)
        .with_context(|| format!("failed to remove {}", local_path.display()))
}

fn migrate_shared_codex_file_entry(
    local_path: &Path,
    shared_path: &Path,
    metadata: &fs::Metadata,
) -> Result<()> {
    ensure_shared_codex_file_public(local_path, metadata)?;
    if !shared_path.exists() {
        move_file(local_path, shared_path)?;
        return Ok(());
    }

    ensure_shared_codex_path_is_file_public(shared_path)?;
    if is_history_jsonl(local_path) {
        merge_history_files(local_path, shared_path)?;
    }
    fs::remove_file(local_path)
        .with_context(|| format!("failed to remove {}", local_path.display()))
}

fn migrate_shared_codex_symlink_target(
    local_path: &Path,
    shared_path: &Path,
    kind: SharedCodexEntryKind,
) -> Result<()> {
    let target_path = shared_codex_symlink_target_path(local_path)?;

    if same_path(&target_path, shared_path) || !target_path.exists() {
        return Ok(());
    }

    ensure_shared_codex_parent_dir(shared_path)?;

    match kind {
        SharedCodexEntryKind::Directory => {
            migrate_shared_codex_symlink_directory_entry(&target_path, shared_path)
        }
        SharedCodexEntryKind::File => {
            migrate_shared_codex_symlink_file_entry(local_path, &target_path, shared_path)
        }
    }
}

fn shared_codex_symlink_target_path(local_path: &Path) -> Result<PathBuf> {
    let target = fs::read_link(local_path)
        .with_context(|| format!("failed to read symlink {}", local_path.display()))?;
    Ok(if target.is_absolute() {
        target
    } else {
        local_path
            .parent()
            .unwrap_or_else(|| Path::new("."))
            .join(target)
    })
}

fn migrate_shared_codex_symlink_directory_entry(
    target_path: &Path,
    shared_path: &Path,
) -> Result<()> {
    ensure_shared_codex_target_is_directory_public(target_path)?;
    if !shared_path.exists() {
        create_codex_home_if_missing(shared_path)?;
    } else {
        ensure_shared_codex_path_is_directory_public(shared_path)?;
    }
    copy_directory_contents(target_path, shared_path)
}

fn migrate_shared_codex_symlink_file_entry(
    local_path: &Path,
    target_path: &Path,
    shared_path: &Path,
) -> Result<()> {
    ensure_shared_codex_target_is_file_public(target_path)?;
    if !shared_path.exists() {
        copy_shared_codex_file(target_path, shared_path)?;
        return Ok(());
    }

    ensure_shared_codex_path_is_file_public(shared_path)?;
    if is_history_jsonl(local_path) {
        merge_history_files(target_path, shared_path)?;
    }
    Ok(())
}

pub(super) fn seed_shared_codex_entry(
    legacy_path: &Path,
    shared_path: &Path,
    kind: SharedCodexEntryKind,
) -> Result<()> {
    let Some(metadata) = load_shared_codex_entry_metadata(legacy_path)? else {
        return Ok(());
    };

    if metadata.file_type().is_symlink() {
        return Ok(());
    }

    ensure_shared_codex_parent_dir(shared_path)?;

    match kind {
        SharedCodexEntryKind::Directory => {
            seed_shared_codex_directory_entry(legacy_path, shared_path, &metadata)
        }
        SharedCodexEntryKind::File => {
            seed_shared_codex_file_entry(legacy_path, shared_path, &metadata)
        }
    }
}

fn seed_shared_codex_directory_entry(
    legacy_path: &Path,
    shared_path: &Path,
    metadata: &fs::Metadata,
) -> Result<()> {
    ensure_shared_codex_directory_public(legacy_path, metadata)?;
    if shared_path.exists() {
        ensure_shared_codex_path_is_directory_public(shared_path)?;

        // Legacy seeding is a one-time bootstrap. Recopying populated
        // session trees from ~/.codex on every `prodex run` adds large
        // disk I/O directly to the startup hot path.
        if !dir_is_empty(shared_path)? {
            return Ok(());
        }
    } else {
        create_codex_home_if_missing(shared_path)?;
    }

    copy_directory_contents(legacy_path, shared_path)
}

fn seed_shared_codex_file_entry(
    legacy_path: &Path,
    shared_path: &Path,
    metadata: &fs::Metadata,
) -> Result<()> {
    ensure_shared_codex_file_public(legacy_path, metadata)?;
    if !shared_path.exists() {
        return copy_legacy_shared_codex_file(legacy_path, shared_path);
    }

    ensure_shared_codex_path_is_file_public(shared_path)?;
    if is_history_jsonl(legacy_path) {
        // Legacy default CODEX_HOME seeding should stay one-shot so
        // startup does not reread and rewrite large history files on
        // every `prodex run`.
        return Ok(());
    }
    Ok(())
}