Cloud Tasks
codex cloud es el subcomando que envía el prompt a la nube de OpenAI para correr: localmente no llama al modelo ni corre el bucle del Agent; solo se encarga de enviar la tarea, recoger el estado, sacar el diff y aplicar el parche. cloud-tasks es la entrada de TUI + CLI; cloud-tasks-client ofrece el trait CloudBackend + la implementación HTTP; cloud-tasks-mock-client inyecta un backend falso para debug; backend-client es el HTTP subyacente (reutilizado por otras backend APIs). A diferencia de la inferencia local de Model Provider, aquí «la inferencia del modelo» ocurre dentro de un contenedor en la nube.
Responsabilidades
- Enviar la tarea:
run_exec_commandempaqueta prompt + git ref + environment id y hacePOST /wham/tasks(o/api/codex/tasks), devolviendo el task id (codex-rs/cloud-tasks/src/lib.rs:161-184). - Listado / detalle / diff:
run_list_command,run_status_command,run_diff_commandson tres subcomandos CLI que se mapean alist_tasks/get_task_summary/get_task_diff(codex-rs/cloud-tasks/src/cli.rs:16-27). - Aplicar el parche:
apply_taskhace primero un preflight dry-run y luego un apply real;diff_overridepermite al usuario elegir un intento concreto dentro de un best-of-N (codex-rs/cloud-tasks-client/src/http.rs:99-121). - Abstraer el trait:
CloudBackendreúne todos los métodos de interacción con el backend como un trait; el build de debug usaMockClienty el de release usaHttpClient(codex-rs/cloud-tasks-client/src/api.rs:136-176). - Inicializar el backend:
init_backendensambla base URL, UA, auth provider y ChatGPT-Account-Id, y cambia entre el estilo de ruta WHAM / Codex API segúnCODEX_CLOUD_TASKS_BASE_URL(codex-rs/cloud-tasks/src/lib.rs:43-107).
Motivación de diseño
Es un crate aparte y no está dentro de core, porque esta ruta no corre el bucle principal del Agent. El client de core envía peticiones de stream al modelo, parsea SSE y mantiene turn state; cloud task es «envía una tarea larga a la nube y espera el resultado asíncronamente». Los dos modelos de concurrencia son completamente distintos: el primero es una conexión larga de stream; el segundo, poll + pull. El trait CloudBackend existe para que el TUI pueda correr en builds de debug: cuando init_backend detecta CODEX_CLOUD_TASKS_MODE=mock, cambia directamente a MockClient, permitiendo depurar la máquina de estados de la UI sin conexión real al backend de ChatGPT. El HttpClient reutiliza por dentro backend-client::Client en vez de levantar otro HTTP aparte, porque las cabeceras de auth, la cookie store de Cloudflare y el estilo de ruta (wham vs codex-api) son consistentes con otras llamadas a la backend API.
Best-of-N es la particularidad de esta ruta: create_task acepta attempts: usize (1-4); la nube corre varios intentos; list_sibling_attempts los recupera a todos; ApplyCommand deja al usuario especificar --attempt N para elegir un resultado. El campo diff_override atraviesa apply_task_preflight y apply_task, de modo que en la fase de apply no se vuelve a tirar del diff, sino que se usa el elegido por el usuario.
Archivos clave
codex-rs/cloud-tasks/src/lib.rs:735-744 — run_main, dispatch de subcomandos + entrada al modo TUI.codex-rs/cloud-tasks/src/lib.rs:43-107 — BackendContext e init_backend, elige mock / http por variable de entorno y ensambla auth.codex-rs/cloud-tasks/src/cli.rs:29-50 — ExecCommand, define los tres parámetros centrales: --env, --attempts, --branch.codex-rs/cloud-tasks-client/src/api.rs:136-176 — trait CloudBackend, 11 métodos que cubren list / get / apply / create.codex-rs/cloud-tasks-client/src/http.rs:25-63 — HttpClient, envuelve backend-client::Client e implementa CloudBackend.codex-rs/backend-client/src/client.rs:451-482 — create_task en el base client, saca el task id del JSON.codex-rs/cloud-tasks-mock-client/src/mock.rs:163-189 — MockClient implementa el trait y delega a las funciones internas de generación de datos mock.La factory init_backend comprueba variables de entorno en build de debug y decide entre mock o HTTP real:
// cloud-tasks/src/lib.rs:43-60 — selección de backend
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,
});
}El trait CloudBackend usa CloudBackendFuture para unificar el tipo de retorno como boxed future, de modo que mock y http compartan la misma firma:
// cloud-tasks-client/src/api.rs:136-176 — abstracción de backend
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 en el base client hace el POST y luego saca el task id del JSON (prioriza task.id, y si no, el id del nivel superior):
// backend-client/src/client.rs:451-482 — POST /wham/tasks y extracción del 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),
};
// ...envía la petición, parsea el 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}; ..."),
}
}Flujo de datos
Bordes y fallos
- Sin login, salida directa:
init_backend, al detectar queauthes None ouses_codex_backend()es false, haceprocess::exit(1)(codex-rs/cloud-tasks/src/lib.rs:76-95). - El estilo de ruta lo decide el base URL:
https://chatgpt.com/backend-apiva por WHAM (/wham/tasks); otros van por Codex API (/api/codex/tasks), decidido porPathStyle::from_base_url(codex-rs/backend-client/src/client.rs:151-177). - Rango de attempts limitado:
parse_attemptsfuerza 1-4, alineado con el límite superior de best-of-N del backend (codex-rs/cloud-tasks/src/cli.rs:52-61). - Exit code no 0 si apply falla:
run_apply_commandcompruebaApplyStatus; si no esSuccess, haceprocess::exit(1)para encadenar bien con scripts (codex-rs/cloud-tasks/src/lib.rs:589-608). create_taskacepta dos formas de JSON: tantotask.idcomoiden el nivel superior, porque los backends devuelven estructuras distintas (codex-rs/backend-client/src/client.rs:464-481).
Resumen
Cloud Tasks comprime los tres pasos «enviar, consultar, aplicar» en el trait CloudBackend; HTTP y mock implementan el mismo trait, permitiendo al TUI depurar sin backend local. El HTTP subyacente reutiliza backend-client::Client, compartiendo autenticación y estilo de ruta con Model Provider. La configuración propia de las tareas en la nube (environments permitidos, políticas enterprise) la provee el cloud config bundle de Sistema de configuración, que se descarga al arranque y se aplica.