prodex 0.56.0

OpenAI profile pooling and safe auto-rotate for Codex CLI and Claude Code
Documentation
use super::{AppPaths, secret_store::SecretBackendKind};

mod cache;
mod load;
mod paths;
mod types;
mod validate;

#[cfg(test)]
pub(super) use self::cache::clear_runtime_policy_cache;
#[cfg(test)]
pub(super) use self::load::load_runtime_policy_from_root;
pub(super) use self::load::{
    ensure_runtime_policy_valid, runtime_policy_proxy, runtime_policy_runtime,
    runtime_policy_secrets, runtime_policy_summary,
};
#[cfg(test)]
pub(super) use self::paths::runtime_policy_path;
pub(super) use self::types::{RuntimeLogFormat, RuntimePolicySummary};

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use std::path::PathBuf;
    use std::time::{SystemTime, UNIX_EPOCH};

    fn temp_root(name: &str) -> PathBuf {
        let nanos = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_default()
            .as_nanos();
        let root = std::env::temp_dir().join(format!(
            "prodex-runtime-policy-{name}-{}-{nanos:x}",
            std::process::id()
        ));
        fs::create_dir_all(&root).unwrap();
        root
    }

    #[test]
    fn load_runtime_policy_from_root_reads_versioned_policy_and_resolves_relative_log_dir() {
        clear_runtime_policy_cache();
        let root = temp_root("loads");
        let path = runtime_policy_path(&root);
        fs::write(
            &path,
            r#"
version = 1

[runtime]
log_format = "json"
log_dir = "runtime-logs"

[secrets]
backend = "file"

[runtime_proxy]
worker_count = 12
active_request_limit = 96
profile_inflight_soft_limit = 5
profile_inflight_hard_limit = 9
"#,
        )
        .unwrap();

        let loaded = load_runtime_policy_from_root(&root).unwrap().unwrap();
        assert_eq!(loaded.version, 1);
        assert_eq!(loaded.runtime.log_format, Some(RuntimeLogFormat::Json));
        assert_eq!(loaded.runtime.log_dir, Some(root.join("runtime-logs")));
        assert_eq!(loaded.secrets.backend, Some(SecretBackendKind::File));
        assert_eq!(loaded.runtime_proxy.worker_count, Some(12));
        assert_eq!(loaded.runtime_proxy.active_request_limit, Some(96));
        assert_eq!(loaded.runtime_proxy.profile_inflight_soft_limit, Some(5));
        assert_eq!(loaded.runtime_proxy.profile_inflight_hard_limit, Some(9));

        let _ = fs::remove_dir_all(root);
    }

    #[test]
    fn load_runtime_policy_from_root_rejects_unsupported_version() {
        clear_runtime_policy_cache();
        let root = temp_root("version");
        let path = runtime_policy_path(&root);
        fs::write(
            &path,
            r#"
version = 2

[runtime]
log_format = "json"
"#,
        )
        .unwrap();

        let err = load_runtime_policy_from_root(&root).unwrap_err();
        assert!(
            err.to_string()
                .contains("unsupported prodex policy version")
        );

        let _ = fs::remove_dir_all(root);
    }

    #[test]
    fn load_runtime_policy_from_root_parses_secret_settings() {
        clear_runtime_policy_cache();
        let root = temp_root("secrets");
        let path = runtime_policy_path(&root);
        fs::write(
            &path,
            r#"
version = 1

[secrets]
backend = "keyring"
keyring_service = "prodex"
"#,
        )
        .unwrap();

        let loaded = load_runtime_policy_from_root(&root).unwrap().unwrap();
        assert_eq!(loaded.secrets.backend, Some(SecretBackendKind::Keyring));
        assert_eq!(loaded.secrets.keyring_service.as_deref(), Some("prodex"));

        let _ = fs::remove_dir_all(root);
    }

    #[test]
    fn load_runtime_policy_from_root_rejects_keyring_backend_without_service() {
        clear_runtime_policy_cache();
        let root = temp_root("secrets-missing-service");
        let path = runtime_policy_path(&root);
        fs::write(
            &path,
            r#"
version = 1

[secrets]
backend = "keyring"
"#,
        )
        .unwrap();

        let err = load_runtime_policy_from_root(&root).unwrap_err();
        assert!(err.to_string().contains("secrets.keyring_service"));

        let _ = fs::remove_dir_all(root);
    }

    #[test]
    fn load_runtime_policy_from_root_rejects_zero_profile_inflight_limits() {
        clear_runtime_policy_cache();
        let root = temp_root("inflight-zero");
        let path = runtime_policy_path(&root);
        fs::write(
            &path,
            r#"
version = 1

[runtime_proxy]
profile_inflight_soft_limit = 0
profile_inflight_hard_limit = 1
"#,
        )
        .unwrap();

        let err = load_runtime_policy_from_root(&root).unwrap_err();
        assert!(
            err.to_string()
                .contains("runtime_proxy.profile_inflight_soft_limit")
        );

        let _ = fs::remove_dir_all(root);
    }
}