Cloud Tasks
codex cloud ist das Subkommando, das einen Prompt in die OpenAI-Cloud verlagert: Lokal wird kein Modell aufgerufen und keine Agent-Schleife gedreht; es geht nur um das Einreichen der Aufgabe, das Abholen des Status, das Holen des Diffs und das Anwenden des Patches. cloud-tasks ist der TUI- + CLI-Einstieg, cloud-tasks-client liefert das CloudBackend-Trait + HTTP-Implementierung, cloud-tasks-mock-client steckt im Debug-Modus einen Schein-Backend hinein, und backend-client ist das zugrundeliegende HTTP (auch für andere Backend-APIs wiederverwendet). Anders als bei Model Provider mit lokaler Inferenz — hier läuft die „Modellinferenz" in einem Container in der Cloud.
Verantwortlichkeiten
- Aufgabe einreichen:
run_exec_commandverpackt prompt + git ref + environment id inPOST /wham/tasks(oder/api/codex/tasks) und liefert die task id (codex-rs/cloud-tasks/src/lib.rs:161-184). - Liste / Detail / Diff: Die drei CLI-Subkommandos
run_list_command,run_status_command,run_diff_commandentsprechenlist_tasks/get_task_summary/get_task_diff(codex-rs/cloud-tasks/src/cli.rs:16-27). - Patch anwenden:
apply_taskmacht erst dry-run preflight und dann echtes apply;diff_overridelässt den Nutzer aus best-of-N einen bestimmten Versuch wählen (codex-rs/cloud-tasks-client/src/http.rs:99-121). - Abstrakter Trait:
CloudBackendfasst alle Backend-Interaktionen als Trait zusammen; Debug-Build geht überMockClient, Release überHttpClient(codex-rs/cloud-tasks-client/src/api.rs:136-176). - Backend initialisieren:
init_backendbestückt base URL, UA, auth provider, ChatGPT-Account-Id und schaltet je nachCODEX_CLOUD_TASKS_BASE_URLzwischen WHAM- und Codex-API-Pfadstil (codex-rs/cloud-tasks/src/lib.rs:43-107).
Entwurfsbeweggründe
Eigener Crate statt in core, weil dieser Pfad die Agent-Hauptschleife nicht dreht. Der client in core schickt Stream-Requests an das Modell, parst SSE und pflegt den turn state; cloud task ist „lange Aufgabe in die Cloud schicken und asynchron auf Ergebnis warten". Die Nebenläufigkeitsmodelle sind grundverschieden: jener lange Streaming-Verbund, dieser poll + pull. Der CloudBackend-Trait existiert, damit die TUI im Debug-Build läuft — erkennt init_backend CODEX_CLOUD_TASKS_MODE=mock, wird direkt auf MockClient umgeschaltet und die UI-Zustandsmaschine lässt sich lokal ohne echtes ChatGPT-Backend testen. HttpClient intern wiederverwendet backend-client::Client statt ein eigenes HTTP aufzusetzen, weil Auth-Header, Cloudflare-Cookie-Store und Pfadstil (wham vs codex-api) mit anderen backend-API-Aufrufen übereinstimmen.
best-of-N ist das Sondermerkmal dieses Pfads: create_task akzeptiert attempts: usize (1-4); die Cloud dreht mehrere Versuche, list_sibling_attempts holt sie alle zurück, und ApplyCommand erlaubt dem Nutzer, --attempt N zu wählen. diff_override durchzieht apply_task_preflight und apply_task, sodass die apply-Phase nicht erneut das Diff holt, sondern die vom Nutzer ausgewählte Variante nutzt.
Wichtige Dateien
codex-rs/cloud-tasks/src/lib.rs:735-744 — run_main; Subkommando-Dispatch + Einstieg in den TUI-Modus.codex-rs/cloud-tasks/src/lib.rs:43-107 — BackendContext und init_backend; entscheiden je nach Umgebungsvariable zwischen mock und http und bestücken auth.codex-rs/cloud-tasks/src/cli.rs:29-50 — ExecCommand; definiert die drei Kernparameter --env, --attempts, --branch.codex-rs/cloud-tasks-client/src/api.rs:136-176 — CloudBackend-Trait; 11 Methoden decken list / get / apply / create ab.codex-rs/cloud-tasks-client/src/http.rs:25-63 — HttpClient; umwickelt backend-client::Client und implementiert CloudBackend.codex-rs/backend-client/src/client.rs:451-482 — create_task-Implementierung im base client; extrahiert task id aus JSON.codex-rs/cloud-tasks-mock-client/src/mock.rs:163-189 — MockClient implementiert den Trait und delegiert an interne Mock-Datenfunktionen.Die Factory init_backend prüft im Debug-Build die Umgebungsvariable und entscheidet zwischen mock und echtem HTTP:
// cloud-tasks/src/lib.rs:43-60 — 后端选择
async fn init_backend(user_agent_suffix: &str) -> anyhow::Result<BackendContext> {
#[cfg(debug_assertions)]
let use_mock = matches!(
std::env::var("CODEX_CLOUD_TASKS_MODE").ok().as_deref(),
Some("mock") | Some("MOCK")
);
let base_url = std::env::var("CODEX_CLOUD_TASKS_BASE_URL")
.unwrap_or_else(|_| "https://chatgpt.com/backend-api".to_string());
#[cfg(debug_assertions)]
if use_mock {
return Ok(BackendContext {
backend: Arc::new(codex_cloud_tasks_mock_client::MockClient),
base_url,
});
}Der CloudBackend-Trait nutzt CloudBackendFuture, um Rückgabetypen als boxed future zu vereinheitlichen; mock und http teilen sich dieselbe Signatur:
// cloud-tasks-client/src/api.rs:136-176 — 后端抽象
pub trait CloudBackend: Send + Sync {
fn list_tasks<'a>(
&'a self,
env: Option<&'a str>,
limit: Option<i64>,
cursor: Option<&'a str>,
) -> CloudBackendFuture<'a, TaskListPage>;
fn get_task_summary(&self, id: TaskId) -> CloudBackendFuture<'_, TaskSummary>;
fn get_task_diff(&self, id: TaskId) -> CloudBackendFuture<'_, Option<String>>;
// ...messages / sibling attempts / apply preflight / apply / create
fn create_task<'a>(
&'a self,
env_id: &'a str,
prompt: &'a str,
git_ref: &'a str,
qa_mode: bool,
best_of_n: usize,
) -> CloudBackendFuture<'a, CreatedTask>;
}create_task POST im base client und extrahiert dann task id aus JSON (bevorzugt task.id, Fallback auf top-level id):
// backend-client/src/client.rs:451-482 — POST /wham/tasks 并提取 task id
pub async fn create_task(&self, request_body: serde_json::Value) -> Result<String> {
let url = match self.path_style {
PathStyle::CodexApi => format!("{}/api/codex/tasks", self.base_url),
PathStyle::ChatGptApi => format!("{}/wham/tasks", self.base_url),
};
// ...发请求,解析 body
match serde_json::from_str::<serde_json::Value>(&body) {
Ok(v) => {
if let Some(id) = v.get("task").and_then(|t| t.get("id")).and_then(|s| s.as_str()) {
Ok(id.to_string())
} else if let Some(id) = v.get("id").and_then(|s| s.as_str()) {
Ok(id.to_string())
} else {
anyhow::bail!("POST {url} succeeded but no task id found; ...");
}
}
Err(e) => anyhow::bail!("Decode error for {url}: {e}; ..."),
}
}Datenfluss
Grenzen und Fehler
- Ohne Login direkter Ausstieg:
init_backendbricht mitprocess::exit(1)ab, wennauthNone ist oderuses_codex_backend()false liefert (codex-rs/cloud-tasks/src/lib.rs:76-95). - Pfadstil durch base URL bestimmt:
https://chatgpt.com/backend-apigeht über WHAM (/wham/tasks), sonst Codex API (/api/codex/tasks); entscheidetPathStyle::from_base_url(codex-rs/backend-client/src/client.rs:151-177). - attempts-Bereich beschränkt:
parse_attemptserzwingt 1-4, passend zur best-of-N-Obergrenze des Backends (codex-rs/cloud-tasks/src/cli.rs:52-61). - apply-Fehlschlag liefert Exit-Code ungleich 0:
run_apply_commandprüftApplyStatus; ist es nichtSuccess, erfolgtprocess::exit(1), damit Skripte darauf reagieren können (codex-rs/cloud-tasks/src/lib.rs:589-608). - create_task akzeptiert zwei JSON-Formen: Sowohl
task.idals auch top-levelidwerden akzeptiert, weil unterschiedliche Backends unterschiedliche Strukturen zurückgeben (codex-rs/backend-client/src/client.rs:464-481).
Zusammenfassung
Cloud Tasks presst die drei Schritte „einreichen, abfragen, anwenden" in den CloudBackend-Trait; HTTP und mock implementieren denselben Trait, sodass die TUI lokal ohne Backend debuggt werden kann. Das zugrundeliegende HTTP wiederverwendet backend-client::Client und teilt sich mit Model Provider die Auth- und Pfadstil-Logik. Die Konfiguration der Cloud-Aufgaben selbst (erlaubte environments, enterprise-Policy) wird vom cloud-config-bundle im Konfigurationssystem geliefert und beim Start abgerufen und angewandt.