Skip to content

Cloud Tasks

源码版本rust-v0.145.0

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

  1. Aufgabe einreichen: run_exec_command verpackt prompt + git ref + environment id in POST /wham/tasks (oder /api/codex/tasks) und liefert die task id (codex-rs/cloud-tasks/src/lib.rs:161-184).
  2. Liste / Detail / Diff: Die drei CLI-Subkommandos run_list_command, run_status_command, run_diff_command entsprechen list_tasks / get_task_summary / get_task_diff (codex-rs/cloud-tasks/src/cli.rs:16-27).
  3. Patch anwenden: apply_task macht erst dry-run preflight und dann echtes apply; diff_override lässt den Nutzer aus best-of-N einen bestimmten Versuch wählen (codex-rs/cloud-tasks-client/src/http.rs:99-121).
  4. Abstrakter Trait: CloudBackend fasst alle Backend-Interaktionen als Trait zusammen; Debug-Build geht über MockClient, Release über HttpClient (codex-rs/cloud-tasks-client/src/api.rs:136-176).
  5. Backend initialisieren: init_backend bestückt base URL, UA, auth provider, ChatGPT-Account-Id und schaltet je nach CODEX_CLOUD_TASKS_BASE_URL zwischen 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-744run_main; Subkommando-Dispatch + Einstieg in den TUI-Modus.codex-rs/cloud-tasks/src/lib.rs:43-107BackendContext und init_backend; entscheiden je nach Umgebungsvariable zwischen mock und http und bestücken auth.codex-rs/cloud-tasks/src/cli.rs:29-50ExecCommand; definiert die drei Kernparameter --env, --attempts, --branch.codex-rs/cloud-tasks-client/src/api.rs:136-176CloudBackend-Trait; 11 Methoden decken list / get / apply / create ab.codex-rs/cloud-tasks-client/src/http.rs:25-63HttpClient; umwickelt backend-client::Client und implementiert CloudBackend.codex-rs/backend-client/src/client.rs:451-482create_task-Implementierung im base client; extrahiert task id aus JSON.codex-rs/cloud-tasks-mock-client/src/mock.rs:163-189MockClient 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:

rust
// 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:

rust
// 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):

rust
// 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

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.