prodex 0.19.0

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

const PRODEX_CAVEMAN_MARKETPLACE_NAME: &str = "prodex-caveman";
const PRODEX_CAVEMAN_PLUGIN_NAME: &str = "caveman";
const PRODEX_CAVEMAN_PLUGIN_VERSION: &str = "0.1.0";
const PRODEX_CAVEMAN_PLUGIN_ID: &str = "caveman@prodex-caveman";
const PRODEX_CAVEMAN_SOURCE_REPO: &str = "https://github.com/JuliusBrussee/caveman.git";
const PRODEX_CAVEMAN_HOOK_COMMAND: &str = "echo 'CAVEMAN MODE ACTIVE. Rules: Drop articles/filler/pleasantries/hedging. Fragments OK. Short synonyms. Pattern: [thing] [action] [reason]. [next step]. Not: Sure! I would be happy to help you with that. Yes: Bug in auth middleware. Fix: Code/commits/security: write normal. User says stop caveman or normal mode to deactivate.'";

struct EmbeddedCavemanFile {
    relative_path: &'static str,
    contents: &'static str,
}

const CAVEMAN_PLUGIN_FILES: &[EmbeddedCavemanFile] = &[
    EmbeddedCavemanFile {
        relative_path: ".codex-plugin/plugin.json",
        contents: include_str!("caveman_assets/.codex-plugin/plugin.json"),
    },
    EmbeddedCavemanFile {
        relative_path: "assets/caveman-small.svg",
        contents: include_str!("caveman_assets/assets/caveman-small.svg"),
    },
    EmbeddedCavemanFile {
        relative_path: "assets/caveman.svg",
        contents: include_str!("caveman_assets/assets/caveman.svg"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/caveman/SKILL.md",
        contents: include_str!("caveman_assets/skills/caveman/SKILL.md"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/caveman/agents/openai.yaml",
        contents: include_str!("caveman_assets/skills/caveman/agents/openai.yaml"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/caveman/assets/caveman-small.svg",
        contents: include_str!("caveman_assets/skills/caveman/assets/caveman-small.svg"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/caveman/assets/caveman.svg",
        contents: include_str!("caveman_assets/skills/caveman/assets/caveman.svg"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/compress/SKILL.md",
        contents: include_str!("caveman_assets/skills/compress/SKILL.md"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/compress/scripts/__init__.py",
        contents: include_str!("caveman_assets/skills/compress/scripts/__init__.py"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/compress/scripts/__main__.py",
        contents: include_str!("caveman_assets/skills/compress/scripts/__main__.py"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/compress/scripts/benchmark.py",
        contents: include_str!("caveman_assets/skills/compress/scripts/benchmark.py"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/compress/scripts/cli.py",
        contents: include_str!("caveman_assets/skills/compress/scripts/cli.py"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/compress/scripts/compress.py",
        contents: include_str!("caveman_assets/skills/compress/scripts/compress.py"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/compress/scripts/detect.py",
        contents: include_str!("caveman_assets/skills/compress/scripts/detect.py"),
    },
    EmbeddedCavemanFile {
        relative_path: "skills/compress/scripts/validate.py",
        contents: include_str!("caveman_assets/skills/compress/scripts/validate.py"),
    },
];

pub(super) fn handle_caveman(args: CavemanArgs) -> Result<()> {
    let codex_args = normalize_run_codex_args(&args.codex_args);
    let include_code_review = is_review_invocation(&codex_args);
    let prepared = prepare_runtime_launch(RuntimeLaunchRequest {
        profile: args.profile.as_deref(),
        allow_auto_rotate: !args.no_auto_rotate,
        skip_quota_check: args.skip_quota_check,
        base_url: args.base_url.as_deref(),
        include_code_review,
        force_runtime_proxy: false,
    })?;
    let runtime_proxy = prepared.runtime_proxy;
    let runtime_args = runtime_proxy
        .as_ref()
        .map(|proxy| {
            if proxy.openai_mount_path == RUNTIME_PROXY_OPENAI_MOUNT_PATH {
                runtime_proxy_codex_args(proxy.listen_addr, &codex_args)
            } else {
                runtime_proxy_codex_args_with_mount_path(
                    proxy.listen_addr,
                    &proxy.openai_mount_path,
                    &codex_args,
                )
            }
        })
        .unwrap_or(codex_args);

    let caveman_home = prepare_caveman_launch_home(&prepared.paths, &prepared.codex_home)?;
    let status = run_child(
        &codex_bin(),
        &runtime_args,
        &caveman_home,
        &[],
        &[],
        runtime_proxy.as_ref(),
    );
    drop(runtime_proxy);
    let _ = fs::remove_dir_all(&caveman_home);
    exit_with_status(status?)
}

pub(super) fn prepare_caveman_launch_home(
    paths: &AppPaths,
    base_codex_home: &Path,
) -> Result<PathBuf> {
    let caveman_home = create_temporary_caveman_home(paths)?;
    if let Err(err) = copy_codex_home(base_codex_home, &caveman_home)
        .and_then(|_| configure_caveman_launch_home(&caveman_home))
    {
        let _ = fs::remove_dir_all(&caveman_home);
        return Err(err);
    }
    Ok(caveman_home)
}

pub(super) fn configure_caveman_launch_home(codex_home: &Path) -> Result<()> {
    localize_caveman_config_file(codex_home)?;
    configure_caveman_config(codex_home)?;
    install_caveman_marketplace(codex_home)?;
    install_caveman_plugin_cache(codex_home)?;
    write_caveman_hooks_file(codex_home)?;
    Ok(())
}

fn create_temporary_caveman_home(paths: &AppPaths) -> Result<PathBuf> {
    fs::create_dir_all(&paths.managed_profiles_root).with_context(|| {
        format!(
            "failed to create managed profile root {}",
            paths.managed_profiles_root.display()
        )
    })?;

    for attempt in 0..100 {
        let stamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_default()
            .as_nanos();
        let candidate = paths
            .managed_profiles_root
            .join(format!(".caveman-{}-{stamp}-{attempt}", std::process::id()));
        if candidate.exists() {
            continue;
        }
        create_codex_home_if_missing(&candidate)?;
        return Ok(candidate);
    }

    bail!("failed to allocate a temporary CODEX_HOME for Caveman")
}

fn localize_caveman_config_file(codex_home: &Path) -> Result<()> {
    let config_path = codex_home.join("config.toml");
    match fs::symlink_metadata(&config_path) {
        Ok(metadata) if metadata.file_type().is_symlink() => {
            let contents = fs::read_to_string(&config_path)
                .with_context(|| format!("failed to read {}", config_path.display()))?;
            fs::remove_file(&config_path)
                .with_context(|| format!("failed to remove {}", config_path.display()))?;
            fs::write(&config_path, contents)
                .with_context(|| format!("failed to write {}", config_path.display()))?;
        }
        Ok(metadata) if metadata.is_dir() => {
            bail!("{} is a directory, expected a file", config_path.display());
        }
        Ok(_) => {}
        Err(err) if err.kind() == io::ErrorKind::NotFound => {
            fs::write(&config_path, "")
                .with_context(|| format!("failed to write {}", config_path.display()))?;
        }
        Err(err) => {
            return Err(err)
                .with_context(|| format!("failed to inspect {}", config_path.display()));
        }
    }
    Ok(())
}

fn configure_caveman_config(codex_home: &Path) -> Result<()> {
    let config_path = codex_home.join("config.toml");
    let contents = fs::read_to_string(&config_path).unwrap_or_default();
    let mut table = if contents.trim().is_empty() {
        toml::Table::new()
    } else {
        match toml::from_str::<toml::Value>(&contents)
            .with_context(|| format!("failed to parse {}", config_path.display()))?
        {
            toml::Value::Table(table) => table,
            _ => bail!("{} did not parse as a TOML table", config_path.display()),
        }
    };

    let features = ensure_child_table(&mut table, "features");
    features.insert("plugins".to_string(), toml::Value::Boolean(true));
    features.insert("codex_hooks".to_string(), toml::Value::Boolean(true));

    let marketplaces = ensure_child_table(&mut table, "marketplaces");
    let caveman_marketplace = ensure_child_table(marketplaces, PRODEX_CAVEMAN_MARKETPLACE_NAME);
    caveman_marketplace.insert(
        "last_updated".to_string(),
        toml::Value::String(Local::now().to_rfc3339()),
    );
    caveman_marketplace.insert(
        "source_type".to_string(),
        toml::Value::String("git".to_string()),
    );
    caveman_marketplace.insert(
        "source".to_string(),
        toml::Value::String(PRODEX_CAVEMAN_SOURCE_REPO.to_string()),
    );
    caveman_marketplace.insert("ref".to_string(), toml::Value::String("main".to_string()));

    let plugins = ensure_child_table(&mut table, "plugins");
    let caveman_plugin = ensure_child_table(plugins, PRODEX_CAVEMAN_PLUGIN_ID);
    caveman_plugin.insert("enabled".to_string(), toml::Value::Boolean(true));

    let rendered = toml::to_string(&toml::Value::Table(table))
        .context("failed to render Caveman config overlay")?;
    fs::write(&config_path, rendered)
        .with_context(|| format!("failed to write {}", config_path.display()))?;
    Ok(())
}

fn ensure_child_table<'a>(parent: &'a mut toml::Table, key: &str) -> &'a mut toml::Table {
    if !matches!(parent.get(key), Some(toml::Value::Table(_))) {
        parent.insert(key.to_string(), toml::Value::Table(toml::Table::new()));
    }
    match parent.get_mut(key) {
        Some(toml::Value::Table(table)) => table,
        _ => unreachable!("child table should exist after insertion"),
    }
}

fn install_caveman_marketplace(codex_home: &Path) -> Result<()> {
    let marketplace_root = caveman_marketplace_root(codex_home);
    let plugin_root = marketplace_root
        .join("plugins")
        .join(PRODEX_CAVEMAN_PLUGIN_NAME);
    fs::create_dir_all(marketplace_root.join(".agents/plugins")).with_context(|| {
        format!(
            "failed to create Caveman marketplace root {}",
            marketplace_root.display()
        )
    })?;
    write_caveman_plugin_tree(&plugin_root)?;
    let marketplace_manifest = serde_json::to_string_pretty(&serde_json::json!({
        "name": PRODEX_CAVEMAN_MARKETPLACE_NAME,
        "interface": {
            "displayName": "Prodex Caveman",
        },
        "plugins": [
            {
                "name": PRODEX_CAVEMAN_PLUGIN_NAME,
                "source": {
                    "source": "local",
                    "path": format!("./plugins/{PRODEX_CAVEMAN_PLUGIN_NAME}"),
                },
                "policy": {
                    "installation": "AVAILABLE",
                    "authentication": "ON_INSTALL",
                },
                "category": "Productivity",
            }
        ],
    }))
    .context("failed to serialize Caveman marketplace manifest")?;
    fs::write(
        marketplace_root.join(".agents/plugins/marketplace.json"),
        marketplace_manifest,
    )
    .with_context(|| {
        format!(
            "failed to write {}",
            marketplace_root
                .join(".agents/plugins/marketplace.json")
                .display()
        )
    })?;
    Ok(())
}

fn install_caveman_plugin_cache(codex_home: &Path) -> Result<()> {
    let plugin_cache_base = codex_home
        .join("plugins/cache")
        .join(PRODEX_CAVEMAN_MARKETPLACE_NAME)
        .join(PRODEX_CAVEMAN_PLUGIN_NAME);
    if plugin_cache_base.exists() {
        fs::remove_dir_all(&plugin_cache_base)
            .with_context(|| format!("failed to clear {}", plugin_cache_base.display()))?;
    }
    write_caveman_plugin_tree(&plugin_cache_base.join(PRODEX_CAVEMAN_PLUGIN_VERSION))
}

fn write_caveman_plugin_tree(root: &Path) -> Result<()> {
    for file in CAVEMAN_PLUGIN_FILES {
        let path = root.join(file.relative_path);
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent)
                .with_context(|| format!("failed to create {}", parent.display()))?;
        }
        fs::write(&path, file.contents)
            .with_context(|| format!("failed to write {}", path.display()))?;
    }
    Ok(())
}

fn write_caveman_hooks_file(codex_home: &Path) -> Result<()> {
    let hooks_path = codex_home.join("hooks.json");
    let hooks = serde_json::json!({
        "hooks": {
            "SessionStart": [
                {
                    "hooks": [
                        {
                            "type": "command",
                            "command": PRODEX_CAVEMAN_HOOK_COMMAND,
                        }
                    ]
                }
            ]
        }
    });
    let rendered = serde_json::to_string_pretty(&hooks)
        .context("failed to serialize Caveman hooks configuration")?;
    fs::write(&hooks_path, rendered)
        .with_context(|| format!("failed to write {}", hooks_path.display()))?;
    Ok(())
}

fn caveman_marketplace_root(codex_home: &Path) -> PathBuf {
    codex_home
        .join(".tmp/marketplaces")
        .join(PRODEX_CAVEMAN_MARKETPLACE_NAME)
}