Add Codex JSON sessions and usage limits

This commit is contained in:
iris committed 2026-09-07 23:29:15 -04:00
1 parent 0862b47f76
commit 6a0202b1b5
14 files changed
+1173 -52

No files matched your search

+77
View File
@@ -101,6 +101,83 @@ pub enum Transport {
}
impl Transport {
/// Exchanges newline-delimited JSON requests with a short-lived stdio
/// server. `initial` is written first; after its response arrives,
/// `requests` is written and the response bearing `wanted_id` is returned.
///
/// This is the shape Codex's app-server requires for a usage read: an
/// initialize round trip must finish before the initialized notification
/// and account request are accepted.
pub fn request_json_blocking(
&self,
launch: &Launch,
initial: &serde_json::Value,
requests: &[serde_json::Value],
wanted_id: u64,
) -> Result<serde_json::Value> {
use std::io::{BufRead, BufReader, Write};
let host = match self {
Self::Here => None,
Self::Ssh { ssh, .. } => Some(ssh),
};
let mut command = crate::ssh::command(
host,
&launch.program,
&launch.args,
launch.cwd.as_deref(),
launch.forward,
);
command
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let mut child = command
.spawn()
.with_context(|| format!("couldn't run \"{}\" {}", launch.program, self.describe()))?;
let mut stdin = child.stdin.take().context("the JSON server has no stdin")?;
let stdout = child
.stdout
.take()
.context("the JSON server has no stdout")?;
writeln!(stdin, "{initial}")?;
stdin.flush()?;
let mut reader = BufReader::new(stdout);
let mut line = String::new();
loop {
line.clear();
if reader.read_line(&mut line)? == 0 {
anyhow::bail!("the JSON server exited before initialization completed");
}
let Ok(value) = serde_json::from_str::<serde_json::Value>(&line) else {
continue;
};
if value.get("id").and_then(serde_json::Value::as_u64)
== initial.get("id").and_then(serde_json::Value::as_u64)
{
break;
}
}
for request in requests {
writeln!(stdin, "{request}")?;
}
stdin.flush()?;
loop {
line.clear();
if reader.read_line(&mut line)? == 0 {
anyhow::bail!("the JSON server exited before answering request {wanted_id}");
}
let Ok(value) = serde_json::from_str::<serde_json::Value>(&line) else {
continue;
};
if value.get("id").and_then(serde_json::Value::as_u64) == Some(wanted_id) {
let _ = child.kill();
let _ = child.wait();
return Ok(value);
}
}
}
/// The transport a setup describes; a setup with no `ssh` is here.
pub fn for_setup(setup: &crate::config::SetupConfig) -> Self {
match &setup.ssh {