harness-codex 0.1.1

Codex harness integration for the SteadyMux local continuity router.
Documentation
// SPDX-License-Identifier: AGPL-3.0-or-later

#![forbid(unsafe_code)]

#[cfg(unix)]
use harness_codex::{
    CodexAppServerClient, OwnedAppServerConfig, S08OwnedChildTurnReceipt, run_s08_owned_child_turn,
};
use std::env;
use std::path::{Component, PathBuf};
use std::process::ExitCode;
use std::time::Duration;

#[derive(Clone, Copy)]
enum Seam {
    Cli,
    Desktop,
}

struct Arguments {
    app_server: PathBuf,
    app_server_sha256: String,
    correlation: String,
    seam: Seam,
}

fn main() -> ExitCode {
    #[cfg(unix)]
    {
        match run() {
            Ok(()) => ExitCode::SUCCESS,
            Err(()) => {
                eprintln!("s08-owned-child-turn: refused");
                ExitCode::from(2)
            }
        }
    }
    #[cfg(not(unix))]
    {
        eprintln!("s08-owned-child-turn: requires macOS or Linux");
        ExitCode::from(2)
    }
}

#[cfg(unix)]
fn run() -> Result<(), ()> {
    let arguments = parse_arguments()?;
    let configuration = match arguments.seam {
        Seam::Cli => OwnedAppServerConfig::codex_pinned(
            arguments.app_server,
            &arguments.app_server_sha256,
            Duration::from_secs(30),
            Duration::from_secs(10),
        ),
        Seam::Desktop => OwnedAppServerConfig::codex_desktop_pinned(
            arguments.app_server,
            &arguments.app_server_sha256,
            Duration::from_secs(30),
            Duration::from_secs(10),
        ),
    }
    .map_err(|_| ())?;
    let mut client = CodexAppServerClient::spawn(&configuration).map_err(|_| ())?;
    let receipt = run_s08_owned_child_turn(&mut client, &arguments.correlation).map_err(|_| ())?;
    print_receipt(&receipt)
}

#[cfg(unix)]
fn print_receipt(receipt: &S08OwnedChildTurnReceipt) -> Result<(), ()> {
    let output = serde_json::to_string(receipt).map_err(|_| ())?;
    if output.len() > 4096 {
        return Err(());
    }
    println!("{output}");
    Ok(())
}

fn parse_arguments() -> Result<Arguments, ()> {
    let mut app_server = None;
    let mut app_server_sha256 = None;
    let mut correlation = None;
    let mut seam = None;
    let mut arguments = env::args().skip(1);
    while let Some(argument) = arguments.next() {
        match argument.as_str() {
            "--app-server" => app_server = Some(PathBuf::from(arguments.next().ok_or(())?)),
            "--app-server-sha256" => app_server_sha256 = Some(arguments.next().ok_or(())?),
            "--correlation" => correlation = Some(arguments.next().ok_or(())?),
            "--seam" => {
                seam = Some(match arguments.next().as_deref() {
                    Some("cli") => Seam::Cli,
                    Some("desktop") => Seam::Desktop,
                    _ => return Err(()),
                });
            }
            _ => return Err(()),
        }
    }
    let app_server = app_server.ok_or(())?;
    if !app_server.is_absolute()
        || app_server
            .components()
            .any(|component| component == Component::ParentDir)
    {
        return Err(());
    }
    let app_server_sha256 = app_server_sha256.ok_or(())?;
    if !valid_sha256(&app_server_sha256) {
        return Err(());
    }
    Ok(Arguments {
        app_server,
        app_server_sha256,
        correlation: correlation.ok_or(())?,
        seam: seam.ok_or(())?,
    })
}

fn valid_sha256(value: &str) -> bool {
    value.len() == 64
        && value
            .bytes()
            .all(|byte| byte.is_ascii_digit() || matches!(byte, b'a'..=b'f'))
}