diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 1b0fe4247..5bb76eb18 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -118,8 +118,8 @@ The Rust workspace (`src/`) implements multiple sandboxing backends behind the ` ### Config flow -1. User provides JSON config (file or base64) → `config_parser.rs` deserializes into intermediate `Raw*` structs → validates and maps to `CodexRequest` (the internal execution model in `models.rs`) -2. `CodexRequest` includes the containment backend selection, process config, filesystem/network policies, and optional experimental features +1. User provides JSON config (file or base64) → `config_parser.rs` deserializes into intermediate `Raw*` structs → validates and maps to `ExecutionRequest` (the internal execution model in `models.rs`) +2. `ExecutionRequest` includes the containment backend selection, process config, filesystem/network policies, and optional experimental features 3. The appropriate `ScriptRunner` implementation executes the process and returns `ScriptResponse` ### TypeScript layers diff --git a/docs/authoring-a-new-feature.md b/docs/authoring-a-new-feature.md index bf63af6a0..444add0de 100644 --- a/docs/authoring-a-new-feature.md +++ b/docs/authoring-a-new-feature.md @@ -249,10 +249,10 @@ Add tests to verify: - Missing optional fields use defaults - Unknown fields under `experimental` are ignored (forward compatibility) -Also ensure that `convert_raw_config()` populates `CodexRequest.experimental`: +Also ensure that `convert_raw_config()` populates `ExecutionRequest.experimental`: ```rust -Ok(CodexRequest { +Ok(ExecutionRequest { // ... existing fields ... experimental, // ← include the parsed experimental config }) @@ -261,7 +261,7 @@ Ok(CodexRequest { ## Step 4: Implement the feature in the runner > The `--experimental` CLI flag and `experimental_enabled` field on -> `CodexRequest` already exist from when `compartments` was added. No changes +> `ExecutionRequest` already exist from when `compartments` was added. No changes > needed in `main.rs`. The full flow is: @@ -278,7 +278,7 @@ In the appropriate runner (`appcontainer.rs`, `lxc_runner.rs`, etc.), guard your feature behind `experimental_enabled`: ```rust -fn run(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { +fn run(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { // ... normal execution (filesystem, network, etc.) ... if request.experimental_enabled { @@ -367,7 +367,7 @@ When your experimental feature is ready to ship: 1. Move the property from `experimental` to a top-level property in the schema (e.g., `experimental.gpuIsolation` → `gpuIsolation`) -2. Move the struct from `ExperimentalConfig` to `CodexRequest` +2. Move the struct from `ExperimentalConfig` to `ExecutionRequest` 3. Move the field from `RawExperimental` to `RawConfig` 4. Remove the `if request.experimental_enabled` guard 5. Bump the minor version diff --git a/docs/base-process-container/guide.md b/docs/base-process-container/guide.md index e24c9002b..df4290bfe 100644 --- a/docs/base-process-container/guide.md +++ b/docs/base-process-container/guide.md @@ -99,7 +99,7 @@ In `src/wxc_common/src/base_container_runner.rs`, update `build_sandbox_spec` to include your new data: ```rust -fn build_sandbox_spec(request: &CodexRequest) -> Vec { +fn build_sandbox_spec(request: &ExecutionRequest) -> Vec { // ... existing code ... let spec = SandboxSpec::create( diff --git a/docs/bwrap-support/bubblewrap-backend-plan.md b/docs/bwrap-support/bubblewrap-backend-plan.md index b709270ef..bd7b34b1f 100644 --- a/docs/bwrap-support/bubblewrap-backend-plan.md +++ b/docs/bwrap-support/bubblewrap-backend-plan.md @@ -21,7 +21,7 @@ Key advantages over LXC for MXC: MXC's `ScriptRunner` trait requires only: ```rust -fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse; +fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse; ``` Plus optional `validate_runner()`. This is a perfect fit for bwrap, which is fundamentally "run a command in a namespace sandbox" — a single `std::process::Command` invocation. @@ -70,7 +70,7 @@ Bubblewrap, ``` No `BubblewrapConfig` struct needed for now — the runner uses only the shared -`ContainerPolicy` fields on `CodexRequest` (filesystem paths, network policy, env, etc.). +`ContainerPolicy` fields on `ExecutionRequest` (filesystem paths, network policy, env, etc.). A backend-specific config can be added later under `ExperimentalConfig` if needed. ### 3. Config Parser Changes @@ -117,20 +117,20 @@ Add `bwrap_common` to workspace `members` in `src/Cargo.toml`. ### 5. BubblewrapScriptRunner Implementation -Core design — translate `CodexRequest` into a `bwrap` command line: +Core design — translate `ExecutionRequest` into a `bwrap` command line: ```rust pub struct BubblewrapScriptRunner; impl ScriptRunner for BubblewrapScriptRunner { - fn validate_runner(&self, request: &CodexRequest) -> Result<(), ScriptResponse> { + fn validate_runner(&self, request: &ExecutionRequest) -> Result<(), ScriptResponse> { // Check bwrap is on PATH // Check user namespaces are enabled (cat /proc/sys/kernel/unprivileged_userns_clone) // Validate filesystem paths exist // Reject allowedHosts/blockedHosts (not supported by bwrap) } - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { let mut cmd = std::process::Command::new("bwrap"); // Namespace isolation (all unshared by default) @@ -336,7 +336,7 @@ policy gap is a design decision, not an implementation challenge. - `src/Cargo.toml` — add `bwrap_common` to workspace members + dependencies - `src/lxc/Cargo.toml` — add `bwrap_common` dependency - `src/lxc/src/main.rs` — add dispatch arm for `ContainmentBackend::Bubblewrap` -- `src/wxc_common/src/models.rs` — add `Bubblewrap` variant, `BubblewrapConfig` struct, wire into `ExperimentalConfig` and `CodexRequest` +- `src/wxc_common/src/models.rs` — add `Bubblewrap` variant, `BubblewrapConfig` struct, wire into `ExperimentalConfig` and `ExecutionRequest` - `src/wxc_common/src/config_parser.rs` — add `RawBubblewrap`, parsing, containment match arm ### Schema (modify) diff --git a/docs/diagnostics.md b/docs/diagnostics.md index c6649df37..80429dce2 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -30,7 +30,7 @@ wxc-exec.exe --experimental my-config.json ## What Gets Logged -- Input JSON config and parsed `CodexRequest` (env values redacted, script truncated) +- Input JSON config and parsed `ExecutionRequest` (env values redacted, script truncated) - Sandbox spec details (size, UI flags, capabilities, filesystem/network policy) - Process lifecycle (command line, identity, child PID, exit code, elapsed time) - Section markers for key execution stages diff --git a/docs/isolation-session/initial-bringup-plan.md b/docs/isolation-session/initial-bringup-plan.md index 4bf866a4c..cc6ae6a77 100644 --- a/docs/isolation-session/initial-bringup-plan.md +++ b/docs/isolation-session/initial-bringup-plan.md @@ -253,7 +253,7 @@ versions and stating that the bindings must be regenerated. |---|---:|---|---| | Config parsing | ~8 | `config_parser.rs` | `"isolation_session"` containment value, `experimental.isolation_session` section, `configurationId` values + defaults | | Policy validation | ~15 | `isolation_session_runner.rs` | Phase-specific behaviour: provision accepts `readwritePaths` / `readonlyPaths` and rejects `deniedPaths`; non-provision phases reject every filesystem field; network and proxy are rejected at every phase | -| Option building | ~6 | `isolation_session_runner.rs` | `CodexRequest` → `ProcessOptions` mapping (timeout, cwd, env vars, redirect flags) | +| Option building | ~6 | `isolation_session_runner.rs` | `ExecutionRequest` → `ProcessOptions` mapping (timeout, cwd, env vars, redirect flags) | | Feature unavailable | 1 | `isolation_session_runner.rs` | Runner returns a clean error on machines without the IsolationSession feature enabled, so the test passes everywhere | These ~22 backend-specific tests run alongside the existing workspace tests diff --git a/docs/nanvix-microvm/nanvix-integration-plan.md b/docs/nanvix-microvm/nanvix-integration-plan.md index a73dfb716..bd9f8fb8b 100644 --- a/docs/nanvix-microvm/nanvix-integration-plan.md +++ b/docs/nanvix-microvm/nanvix-integration-plan.md @@ -193,7 +193,7 @@ All variants are surfaced via stderr output. Preflight and Platform errors preve ### Config Field Mapping -| JSON Field | Rust Field (`CodexRequest`) | NanVix Behavior | +| JSON Field | Rust Field (`ExecutionRequest`) | NanVix Behavior | |------------|---------------------------|----------------| | `process.commandLine` | `script_code: String` | ✅ **Used** — raw Python source code (not a shell command) | | `process.timeout` | `script_timeout: u32` | ✅ **Used** — script execution timeout in ms | @@ -238,7 +238,7 @@ Setup scripts (PowerShell & Bash) will download matching pre-release binaries an **Goal:** Add NanVix as a functional containment backend in `wxc-exec.exe`. **What changed:** -- `models.rs` — Added `MicroVm` variant to `ContainmentBackend`, added `NanVixConfig` struct, added `nanvix_config` field to `CodexRequest` +- `models.rs` — Added `MicroVm` variant to `ContainmentBackend`, added `NanVixConfig` struct, added `nanvix_config` field to `ExecutionRequest` - `config_parser.rs` — Added `RawNanVix` serde struct, `"microvm"` containment parsing, NanVix config section parsing - `error.rs` — Added `WxcError::NanVix(String)` variant - `nanvix_runner.rs` — **NEW** — `NanVixScriptRunner` implementing `ScriptRunner` trait diff --git a/docs/state-aware-lifecycle/mxc-state-aware-sandbox-api-overview.md b/docs/state-aware-lifecycle/mxc-state-aware-sandbox-api-overview.md index de9b6934f..ed766a1f4 100644 --- a/docs/state-aware-lifecycle/mxc-state-aware-sandbox-api-overview.md +++ b/docs/state-aware-lifecycle/mxc-state-aware-sandbox-api-overview.md @@ -222,7 +222,7 @@ pub trait StatefulSandboxBackend { // Default body mints `:`; override for native provision. fn provision( &mut self, - request: &CodexRequest, + request: &ExecutionRequest, config: Option, ) -> Result, MxcError> { /* ... */ } @@ -230,7 +230,7 @@ pub trait StatefulSandboxBackend { fn start( &mut self, sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, config: Option, ) -> Result, MxcError> { /* ... */ } @@ -238,21 +238,21 @@ pub trait StatefulSandboxBackend { fn exec( &mut self, sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, config: Option, ) -> Result; fn stop( &mut self, sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, config: Option, ) -> Result, MxcError> { /* ... */ } fn deprovision( &mut self, sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, config: Option, ) -> Result, MxcError> { /* ... */ } @@ -264,14 +264,14 @@ Backends declare two consts (`ID_PREFIX` for sandbox-id routing, `BACKEND_KEY` f wire-format `containment` value and `experimental..` deserialisation — see reference §5), per-phase config and metadata as associated types (use `()` for phases that don't need either), and override only the methods they care about — `exec` -is the only required method. Trait methods take `&CodexRequest` (the existing one-shot +is the only required method. Trait methods take `&ExecutionRequest` (the existing one-shot domain model from `wxc_common::models`) plus `sandbox_id` for non-provision phases and an optional backend-specific typed config; cross-cutting policy fields flow through `request.policy` (a `ContainerPolicy`) and per-exec process info flows through `request.script_code` / `request.working_directory` / `request.script_timeout` / `request.env`. There is no Rust `ProcessConfig` / `FilesystemConfig` / `NetworkConfig` / `UiConfig` wrapper struct — those names exist as TypeScript SDK interfaces only. See -"Why the trait reuses `CodexRequest`" in main doc §9.2 for the rationale. +"Why the trait reuses `ExecutionRequest`" in main doc §9.2 for the rationale. A backend's participation mode (ephemeral-only, state-aware-only, both) is declared by which traits it implements. State-aware backends additionally register their @@ -312,7 +312,7 @@ const { sandboxId } = await provisionSandbox( ``` ```rust -// Parser deserializes the JSON above into a CodexRequest with +// Parser deserializes the JSON above into an ExecutionRequest with // request.policy.readwrite_paths = ["C:\\workspace"] // request.policy.default_network_policy = NetworkPolicy::Allow // request.policy.allowed_hosts = ["api.anthropic.com"] diff --git a/docs/state-aware-lifecycle/mxc-state-aware-sandbox-api.md b/docs/state-aware-lifecycle/mxc-state-aware-sandbox-api.md index afe523136..6b3575045 100644 --- a/docs/state-aware-lifecycle/mxc-state-aware-sandbox-api.md +++ b/docs/state-aware-lifecycle/mxc-state-aware-sandbox-api.md @@ -752,7 +752,7 @@ const { sandboxId } = await provisionSandbox( ``` ```rust -// Parser deserializes the JSON above into a CodexRequest with +// Parser deserializes the JSON above into an ExecutionRequest with // request.policy.readwrite_paths = ["C:\\workspace"] // request.policy.default_network_policy = NetworkPolicy::Allow // request.policy.allowed_hosts = ["api.anthropic.com"] @@ -985,7 +985,7 @@ implements one trait, the other, or both, depending on its declared participatio MXC's existing parser at `src/wxc_common/src/config_parser.rs` uses private `Raw*` intermediate structs that mirror the wire-format JSON shape (with serde renames to handle camelCase keys), then converts them into typed domain models via `convert_*` helpers -(e.g., `RawConfig` → `CodexRequest`) before dispatch. The state-aware path extends this +(e.g., `RawConfig` → `ExecutionRequest`) before dispatch. The state-aware path extends this same pattern. ```rust @@ -1032,16 +1032,16 @@ falls through to the one-shot branch. Per-phase requirements (`containment` for than at the deserialiser; the `Raw*` struct accepts both fields as optional. Conversion from `Raw*` into the typed domain model is the existing -`convert_raw_config` → `CodexRequest` path, used for both one-shot and state-aware +`convert_raw_config` → `ExecutionRequest` path, used for both one-shot and state-aware requests. The cross-cutting wire fields (`filesystem`, `network`, `ui`) populate -`CodexRequest.policy` (a `ContainerPolicy`) exactly as the one-shot path does today, -and `process` populates `CodexRequest`'s flat `script_code` / `working_directory` / +`ExecutionRequest.policy` (a `ContainerPolicy`) exactly as the one-shot path does today, +and `process` populates `ExecutionRequest`'s flat `script_code` / `working_directory` / `script_timeout` / `env` fields via the existing `RawProcess` intermediate. The state-aware-only fields (`phase`, `sandboxId`, `experimental..`) are -extracted alongside the `CodexRequest` and bundled into a `ParsedStateAwareRequest` -domain model — `{ request: CodexRequest, phase: Phase, containment: +extracted alongside the `ExecutionRequest` and bundled into a `ParsedStateAwareRequest` +domain model — `{ request: ExecutionRequest, phase: Phase, containment: Option, sandbox_id: Option, experimental_raw: ... }` — -that the dispatcher consumes (§9.3). The bundling does not modify `CodexRequest`'s +that the dispatcher consumes (§9.3). The bundling does not modify `ExecutionRequest`'s shape. There is no unified Rust "policy" type — Rust reads cross-cutting fields directly from `request.policy` (a `ContainerPolicy`), exactly as the one-shot path does today. Domain models are exposed to the dispatch layer; the `Raw*` types stay private @@ -1085,7 +1085,7 @@ pub trait StatefulSandboxBackend { /// (e.g., allocating a session, registering with the underlying service). fn provision( &mut self, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option, ) -> Result, MxcError> { Ok(ProvisionResult { @@ -1099,7 +1099,7 @@ pub trait StatefulSandboxBackend { fn start( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option, ) -> Result, MxcError> { Ok(StartResult { metadata: None }) @@ -1109,7 +1109,7 @@ pub trait StatefulSandboxBackend { fn exec( &mut self, sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, config: Option, ) -> Result; @@ -1117,7 +1117,7 @@ pub trait StatefulSandboxBackend { fn stop( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option, ) -> Result, MxcError> { Ok(StopResult { metadata: None }) @@ -1127,7 +1127,7 @@ pub trait StatefulSandboxBackend { fn deprovision( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option, ) -> Result, MxcError> { Ok(DeprovisionResult { metadata: None }) @@ -1140,7 +1140,7 @@ pub trait StatefulSandboxBackend { /// the chosen `MxcError` code. fn validate_provision( &self, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&Self::ProvisionConfig>, ) -> Result<(), MxcError> { Ok(()) @@ -1149,7 +1149,7 @@ pub trait StatefulSandboxBackend { fn validate_start( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&Self::StartConfig>, ) -> Result<(), MxcError> { Ok(()) @@ -1158,7 +1158,7 @@ pub trait StatefulSandboxBackend { fn validate_exec( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&Self::ExecConfig>, ) -> Result<(), MxcError> { Ok(()) @@ -1167,7 +1167,7 @@ pub trait StatefulSandboxBackend { fn validate_stop( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&Self::StopConfig>, ) -> Result<(), MxcError> { Ok(()) @@ -1176,7 +1176,7 @@ pub trait StatefulSandboxBackend { fn validate_deprovision( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&Self::DeprovisionConfig>, ) -> Result<(), MxcError> { Ok(()) @@ -1214,7 +1214,7 @@ pub struct ExecHandle { } ``` -Trait methods take `&CodexRequest` (the existing one-shot domain model from +Trait methods take `&ExecutionRequest` (the existing one-shot domain model from `wxc_common::models`, populated by the same `convert_raw_config` parser path that serves one-shot calls), plus `sandbox_id` for non-provision phases and an optional backend-specific typed config (`Self::Config`). Cross-cutting policy fields @@ -1240,32 +1240,32 @@ do not need to accumulate state between calls within a backend instance — with single call a backend may use mutability to hold open service connections, but no state needs to survive across phase calls. -#### Why the trait reuses `CodexRequest` +#### Why the trait reuses `ExecutionRequest` The trait could plausibly require its own per-phase request types (e.g., an `ExecRequest` containing typed `ProcessConfig`, `FilesystemConfig`, `NetworkConfig`, -and `UiConfig` fields) instead of taking `&CodexRequest` directly. The design rejects -that shape and reuses `CodexRequest` for five concrete reasons: +and `UiConfig` fields) instead of taking `&ExecutionRequest` directly. The design rejects +that shape and reuses `ExecutionRequest` for five concrete reasons: 1. **The field-ignore precedent is established across every existing backend.** Every `ScriptRunner` impl in the workspace today (`AppContainer`, `BaseContainer`, `NanVix`, `WindowsSandbox`, `IsolationSession`, `Lxc`, `Wslc`) takes - `&CodexRequest` and reads only the fields it needs. `NanVix` and + `&ExecutionRequest` and reads only the fields it needs. `NanVix` and `IsolationSession` go further and actively reject fields they cannot honor (e.g., `NanVixScriptRunner::validate_runner` rejects filesystem paths, network rules, network proxy, and a non-empty working directory). State-aware follows the same pattern, so the trait ergonomic stays consistent across one-shot and state-aware surfaces. -2. **Process info is already typed on `CodexRequest`.** The wire-format `process` - block (`commandLine`, `cwd`, `env`, `timeout`) deserialises into `CodexRequest`'s +2. **Process info is already typed on `ExecutionRequest`.** The wire-format `process` + block (`commandLine`, `cwd`, `env`, `timeout`) deserialises into `ExecutionRequest`'s flat fields (`script_code`, `working_directory`, `script_timeout`, `env`) via the existing `RawProcess` intermediate in `config_parser.rs`. Wrapping these four typed fields into a Rust `ProcessConfig` struct adds no type safety the compiler does not already provide on the flat fields. The TypeScript-side `ProcessConfig` in `sdk/src/types.ts` is unchanged regardless. -3. **Cross-cutting policy is already typed on `CodexRequest`.** Existing backends read +3. **Cross-cutting policy is already typed on `ExecutionRequest`.** Existing backends read `request.policy.readwrite_paths`, `request.policy.allowed_hosts`, `request.policy.network_proxy`, `request.policy.ui`, etc. directly today. State-aware `provision` and `validate_` hooks read the same fields. @@ -1274,11 +1274,11 @@ that shape and reuses `CodexRequest` for five concrete reasons: without changing what any of them does. 4. **The existing extraction helpers already work for state-aware exec.** The - `IsolationSessionRunner::build_process_options(&CodexRequest)` function in + `IsolationSessionRunner::build_process_options(&ExecutionRequest)` function in `wxc_common::isolation_session_runner` extracts process info into the runner's internal `ProcessOptions` struct used to populate `IsoSessionProcessOptions` for `RunProcessWithOptionsAsync`. State-aware `exec` calls the same function with the - same `&CodexRequest` argument; no new public Rust type closes a semantic gap that + same `&ExecutionRequest` argument; no new public Rust type closes a semantic gap that does not exist. 5. **No SDK or wire-format change is required.** The TypeScript `ProcessConfig`, @@ -1288,11 +1288,11 @@ that shape and reuses `CodexRequest` for five concrete reasons: `request.policy.allowed_hosts`, etc. is an internal implementation choice invisible above the Rust layer. -What would justify deviating from `CodexRequest` reuse — none of which apply to the v1 +What would justify deviating from `ExecutionRequest` reuse — none of which apply to the v1 surface in this proposal: - A fundamentally new state-aware-only field that does not fit any existing - `CodexRequest` shape (e.g., a snapshot id for a hypothetical `restore` phase). + `ExecutionRequest` shape (e.g., a snapshot id for a hypothetical `restore` phase). - A type-system invariant only expressible via a wrapper struct (e.g., enforcing at compile time that exec requests always carry a non-empty command line — `validate_exec_common` checks this at runtime instead per §10.1). @@ -1300,7 +1300,7 @@ surface in this proposal: across the SDK-Rust boundary. If any of these emerges, the trait gains the necessary type at that point. The v1 -surface introduces none, so the trait stays minimal and reuses `CodexRequest`. +surface introduces none, so the trait stays minimal and reuses `ExecutionRequest`. ### 9.3 Dispatch @@ -1333,7 +1333,7 @@ fn dispatch_state_aware( parsed: ParsedStateAwareRequest, dry_run: bool, ) -> Result { - // `parsed` carries the typed `CodexRequest`, the parsed `Phase`, the optional + // `parsed` carries the typed `ExecutionRequest`, the parsed `Phase`, the optional // `sandbox_id`, and the raw JSON value for `experimental..` (if // present). The dispatcher deserialises that raw JSON into the backend's // `Self::Config` associated type before calling the trait method. diff --git a/docs/versioning.md b/docs/versioning.md index eebeaf27e..ae1cb5219 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -182,7 +182,7 @@ pub struct ExperimentalConfig { pub gpu_isolation: Option, } -pub struct CodexRequest { +pub struct ExecutionRequest { // ... stable fields ... pub experimental_enabled: bool, // set by --experimental flag pub experimental: ExperimentalConfig, @@ -191,7 +191,7 @@ pub struct CodexRequest { **In the runner (e.g., `appcontainer.rs`):** ```rust -fn run(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { +fn run(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { // ... normal execution ... // Experimental features only applied when flag is set @@ -209,7 +209,7 @@ fn run(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse **Promotion process:** When an experimental feature is ready to ship: 1. Move the property from `experimental` to a top-level property in the schema (e.g., `experimental.gpuIsolation` → `gpuIsolation`) -2. Move the struct from `ExperimentalConfig` to `CodexRequest` +2. Move the struct from `ExperimentalConfig` to `ExecutionRequest` 3. Move the field from `RawExperimental` to `RawConfig` 4. Remove the `if request.experimental_enabled` guard 5. Bump the minor version diff --git a/docs/wsl/wsl-container-support-plan.md b/docs/wsl/wsl-container-support-plan.md index 614292e6d..ba13c293a 100644 --- a/docs/wsl/wsl-container-support-plan.md +++ b/docs/wsl/wsl-container-support-plan.md @@ -128,7 +128,7 @@ Shipped in earlier PRs. The SDK supports `SandboxingMethod` types and Shipped in PR #44. The config parser reads `"containment": "wslc"` and the `"wslc"` section with image, cpuCount, memoryMb, gpu, storagePath, and portMappings. `ContainerConfig` struct and `container_config` field on -`CodexRequest` are in place. +`ExecutionRequest` are in place. **Example config (current format):** ```json diff --git a/src/bwrap_common/src/bwrap_command.rs b/src/bwrap_common/src/bwrap_command.rs index 4f52da90e..823f5ac90 100644 --- a/src/bwrap_common/src/bwrap_command.rs +++ b/src/bwrap_common/src/bwrap_command.rs @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! Builds the `bwrap` CLI argument vector from a [`CodexRequest`]. +//! Builds the `bwrap` CLI argument vector from an [`ExecutionRequest`]. //! //! This module is platform-agnostic: it only produces a `Vec` of //! arguments without spawning any processes, so it compiles and can be //! unit-tested on every host (Windows, macOS, Linux). -use wxc_common::models::{CodexRequest, NetworkPolicy, ProxyAddress}; +use wxc_common::models::{ExecutionRequest, NetworkPolicy, ProxyAddress}; /// Env var keys that the proxy block manages. Listed here so we can strip /// any conflicting entries the caller supplied via `request.env` (callers @@ -35,7 +35,7 @@ const PROXY_ENV_KEYS: &[&str] = &[ /// - strips any caller-supplied `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` /// entries from `request.env`, /// - emits `--setenv` for those keys pointing at the proxy URL. -pub fn build_args(request: &CodexRequest, proxy_address: Option<&ProxyAddress>) -> Vec { +pub fn build_args(request: &ExecutionRequest, proxy_address: Option<&ProxyAddress>) -> Vec { // -- Namespace isolation (all unshared by default) --------------------- let mut args = vec![ "--unshare-user", @@ -146,10 +146,10 @@ pub fn build_args(request: &CodexRequest, proxy_address: Option<&ProxyAddress>) #[cfg(test)] mod tests { use super::*; - use wxc_common::models::{CodexRequest, NetworkPolicy, ProxyAddress}; + use wxc_common::models::{ExecutionRequest, NetworkPolicy, ProxyAddress}; - fn base_request() -> CodexRequest { - CodexRequest { + fn base_request() -> ExecutionRequest { + ExecutionRequest { script_code: "echo hello".into(), working_directory: "/home/user".into(), ..Default::default() diff --git a/src/bwrap_common/src/bwrap_runner.rs b/src/bwrap_common/src/bwrap_runner.rs index cd76a54f2..ba888e98f 100644 --- a/src/bwrap_common/src/bwrap_runner.rs +++ b/src/bwrap_common/src/bwrap_runner.rs @@ -5,7 +5,7 @@ //! namespace sandbox via the `bwrap` CLI. //! //! Bubblewrap uses Linux user namespaces to create an unprivileged sandbox. -//! The runner translates `CodexRequest` policy fields into `bwrap` CLI +//! The runner translates `ExecutionRequest` policy fields into `bwrap` CLI //! arguments via [`crate::bwrap_command::build_args`], then spawns `bwrap` //! with stdout/stderr capture and optional timeout enforcement. //! @@ -32,7 +32,7 @@ use std::time::{Duration, Instant}; use lxc_common::network_iptables::NetworkIptablesManager; use wxc_common::linux_proxy_coordinator::LinuxProxyCoordinator; use wxc_common::logger::Logger; -use wxc_common::models::{CodexRequest, NetworkEnforcementMode, ScriptResponse}; +use wxc_common::models::{ExecutionRequest, NetworkEnforcementMode, ScriptResponse}; use wxc_common::script_runner::ScriptRunner; use crate::bwrap_command; @@ -63,7 +63,7 @@ impl BubblewrapScriptRunner { } impl ScriptRunner for BubblewrapScriptRunner { - fn validate_runner(&self, request: &CodexRequest) -> Result<(), ScriptResponse> { + fn validate_runner(&self, request: &ExecutionRequest) -> Result<(), ScriptResponse> { // User-input validation runs before the environmental `bwrap` // probe so config errors are reported deterministically even on // hosts without bwrap installed. @@ -103,7 +103,7 @@ impl ScriptRunner for BubblewrapScriptRunner { Ok(()) } - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { // 1. Start the network proxy if configured. Must happen before // arg-building so the proxy's loopback address can be injected as // HTTP_PROXY / HTTPS_PROXY into the sandbox environment. @@ -254,7 +254,7 @@ impl ScriptRunner for BubblewrapScriptRunner { /// Returns `true` when the request has per-host network rules that require /// iptables. Pure `"block"` with no host lists uses `--unshare-net` instead. -fn needs_iptables_rules(request: &CodexRequest) -> bool { +fn needs_iptables_rules(request: &ExecutionRequest) -> bool { let uses_firewall = matches!( request.policy.network_enforcement_mode, NetworkEnforcementMode::Firewall | NetworkEnforcementMode::Both @@ -323,8 +323,8 @@ mod tests { use super::*; use wxc_common::models::ProxyConfig; - fn base_request() -> CodexRequest { - CodexRequest { + fn base_request() -> ExecutionRequest { + ExecutionRequest { script_code: "echo hi".into(), ..Default::default() } diff --git a/src/bwrap_common/src/lib.rs b/src/bwrap_common/src/lib.rs index 55c6b143e..dab572857 100644 --- a/src/bwrap_common/src/lib.rs +++ b/src/bwrap_common/src/lib.rs @@ -4,7 +4,7 @@ //! `bwrap_common` — shared library for the Bubblewrap sandbox backend. //! //! - [`bwrap_command`] builds the `bwrap` CLI argument vector from a -//! [`CodexRequest`](wxc_common::models::CodexRequest). It is +//! [`ExecutionRequest`](wxc_common::models::ExecutionRequest). It is //! platform-agnostic (pure argument generation) so it compiles and is //! fully unit-tested on every host. //! - [`bwrap_runner`] is gated to `target_os = "linux"` since it actually diff --git a/src/lxc/src/main.rs b/src/lxc/src/main.rs index ed9cc2d72..c5927beab 100644 --- a/src/lxc/src/main.rs +++ b/src/lxc/src/main.rs @@ -8,7 +8,7 @@ use std::time::Instant; use clap::Parser; use wxc_common::config_parser::load_request; use wxc_common::logger::{Logger, Mode}; -use wxc_common::models::{CodexRequest, ContainmentBackend, ScriptResponse}; +use wxc_common::models::{ContainmentBackend, ExecutionRequest, ScriptResponse}; use wxc_common::script_runner::{handle_dry_run_exit, ScriptRunner}; #[cfg(target_os = "linux")] @@ -73,7 +73,7 @@ struct Cli { force: bool, } -fn log_request(request: &CodexRequest, logger: &mut Logger) { +fn log_request(request: &ExecutionRequest, logger: &mut Logger) { let _ = writeln!(logger, "Script code length: {}", request.script_code.len()); let _ = writeln!(logger, "Working directory: {}", request.working_directory); let _ = writeln!(logger, "Script timeout: {}", request.script_timeout); diff --git a/src/lxc_common/src/lxc_runner.rs b/src/lxc_common/src/lxc_runner.rs index 86aba9760..4ee844d27 100644 --- a/src/lxc_common/src/lxc_runner.rs +++ b/src/lxc_common/src/lxc_runner.rs @@ -11,7 +11,7 @@ use std::time::{Duration, Instant}; use wxc_common::logger::Logger; use wxc_common::models::{ - CodexRequest, LifecycleConfig, LxcConfig, NetworkEnforcementMode, ScriptResponse, + ExecutionRequest, LifecycleConfig, LxcConfig, NetworkEnforcementMode, ScriptResponse, }; use wxc_common::script_runner::ScriptRunner; @@ -88,7 +88,7 @@ impl LxcScriptRunner { } /// Core execution logic. - fn run_internal(&self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn run_internal(&self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { // Validate required LXC fields if self.config.distribution.is_empty() || self.config.release.is_empty() { return ScriptResponse::error( @@ -246,7 +246,7 @@ impl LxcScriptRunner { } impl ScriptRunner for LxcScriptRunner { - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { // Run with panic catching for safety match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { self.run_internal(request, logger) diff --git a/src/mxc_darwin/src/main.rs b/src/mxc_darwin/src/main.rs index 5330bd349..b0dea97ad 100644 --- a/src/mxc_darwin/src/main.rs +++ b/src/mxc_darwin/src/main.rs @@ -15,7 +15,7 @@ use std::process; use clap::Parser; use wxc_common::config_parser::load_request; use wxc_common::logger::{Logger, Mode}; -use wxc_common::models::{CodexRequest, ContainmentBackend}; +use wxc_common::models::{ContainmentBackend, ExecutionRequest}; #[cfg(target_os = "macos")] use std::time::Instant; @@ -57,7 +57,7 @@ struct Cli { log_file: Option, } -fn log_request(request: &CodexRequest, logger: &mut Logger) { +fn log_request(request: &ExecutionRequest, logger: &mut Logger) { let _ = writeln!(logger, "Script code length: {}", request.script_code.len()); let _ = writeln!(logger, "Working directory: {}", request.working_directory); let _ = writeln!(logger, "Script timeout: {}", request.script_timeout); @@ -132,7 +132,7 @@ fn main() { } #[cfg(target_os = "macos")] -fn run_seatbelt(request: &CodexRequest, logger: &mut Logger) -> ! { +fn run_seatbelt(request: &ExecutionRequest, logger: &mut Logger) -> ! { use seatbelt_common::seatbelt_runner::SeatbeltScriptRunner; let mut runner = SeatbeltScriptRunner::new(); @@ -153,7 +153,7 @@ fn run_seatbelt(request: &CodexRequest, logger: &mut Logger) -> ! { } #[cfg(not(target_os = "macos"))] -fn run_seatbelt(_request: &CodexRequest, logger: &mut Logger) -> ! { +fn run_seatbelt(_request: &ExecutionRequest, logger: &mut Logger) -> ! { eprintln!( "mxc-exec-mac: the macOS sandbox backend is only available on macOS. \ This binary was built for a non-Darwin target and cannot execute scripts." diff --git a/src/seatbelt_common/Cargo.toml b/src/seatbelt_common/Cargo.toml index e0ed7882c..aa7f1529c 100644 --- a/src/seatbelt_common/Cargo.toml +++ b/src/seatbelt_common/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true [dependencies] -# Provides CodexRequest, ContainerPolicy, SeatbeltConfig, Logger, and +# Provides ExecutionRequest, ContainerPolicy, SeatbeltConfig, Logger, and # the ScriptRunner trait that seatbelt_runner implements. Same dep that # lxc_common uses. wxc_common = { workspace = true } diff --git a/src/seatbelt_common/src/profile_builder.rs b/src/seatbelt_common/src/profile_builder.rs index 6db000346..988f1e20a 100644 --- a/src/seatbelt_common/src/profile_builder.rs +++ b/src/seatbelt_common/src/profile_builder.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! Pure builder that converts a [`CodexRequest`] into a TinyScheme sandbox +//! Pure builder that converts an [`ExecutionRequest`] into a TinyScheme sandbox //! profile string suitable for `/usr/bin/sandbox-exec -p `. //! //! This module is platform-agnostic — it is just string generation — so it @@ -27,7 +27,7 @@ use std::fmt::Write as _; -use wxc_common::models::{ClipboardPolicy, CodexRequest, NetworkPolicy}; +use wxc_common::models::{ClipboardPolicy, ExecutionRequest, NetworkPolicy}; /// Build a complete sandbox profile string from the given request. /// @@ -35,7 +35,7 @@ use wxc_common::models::{ClipboardPolicy, CodexRequest, NetworkPolicy}; /// string is returned verbatim and policy fields are ignored. This is the /// escape hatch for advanced/testing scenarios that need to hand-author a /// profile. -pub fn build_profile(request: &CodexRequest) -> Result { +pub fn build_profile(request: &ExecutionRequest) -> Result { if let Some(override_profile) = request .experimental .seatbelt @@ -144,7 +144,7 @@ const TTY_ALLOW: &str = "\ (allow file-read* (subpath \"/dev/fd\")) "; -fn write_filesystem_allow(out: &mut String, request: &CodexRequest) -> Result<(), String> { +fn write_filesystem_allow(out: &mut String, request: &ExecutionRequest) -> Result<(), String> { let policy = &request.policy; if !policy.readonly_paths.is_empty() { @@ -170,7 +170,7 @@ fn write_filesystem_allow(out: &mut String, request: &CodexRequest) -> Result<() Ok(()) } -fn write_filesystem_deny(out: &mut String, request: &CodexRequest) -> Result<(), String> { +fn write_filesystem_deny(out: &mut String, request: &ExecutionRequest) -> Result<(), String> { let policy = &request.policy; if !policy.denied_paths.is_empty() { @@ -186,7 +186,7 @@ fn write_filesystem_deny(out: &mut String, request: &CodexRequest) -> Result<(), Ok(()) } -fn write_network_rules(out: &mut String, request: &CodexRequest) { +fn write_network_rules(out: &mut String, request: &ExecutionRequest) { let policy = &request.policy; let allow_outbound = matches!(policy.default_network_policy, NetworkPolicy::Allow); let has_allowed_hosts = !policy.allowed_hosts.is_empty(); @@ -236,7 +236,7 @@ fn write_local_network_rules(out: &mut String, allow_local_network: bool) { out.push_str("(allow network-inbound (local ip))\n"); } -fn write_ui_rules(out: &mut String, request: &CodexRequest) { +fn write_ui_rules(out: &mut String, request: &ExecutionRequest) { let ui = &request.policy.ui; let gui_access = request .experimental @@ -320,7 +320,7 @@ fn write_ui_rules(out: &mut String, request: &CodexRequest) { /// Emit rules so the inner process can call `posix_openpt()` and allocate /// its own pty. Skipped when `gui_access` (with UI enabled) already emits /// a strict superset. -fn write_nested_pty_rules(out: &mut String, request: &CodexRequest) { +fn write_nested_pty_rules(out: &mut String, request: &ExecutionRequest) { let sb = request.experimental.seatbelt.as_ref(); let enabled = sb.is_none_or(|c| c.nested_pty); let gui_block_emitted = sb.is_some_and(|c| c.gui_access) && !request.policy.ui.disable; @@ -359,7 +359,7 @@ fn write_nested_pty_rules(out: &mut String, request: &CodexRequest) { /// stores under `/Library/Keychains` and `/System/Library/Keychains` /// are already covered by the baseline `/Library` and `/System` /// read-only allows, so we don't re-add them here. -fn write_keychain_rules(out: &mut String, request: &CodexRequest) -> Result<(), String> { +fn write_keychain_rules(out: &mut String, request: &ExecutionRequest) -> Result<(), String> { let enabled = request .experimental .seatbelt @@ -413,7 +413,7 @@ fn write_keychain_rules(out: &mut String, request: &CodexRequest) -> Result<(), /// Emit caller-provided `extraMachLookups` rules: additional Mach service /// global-names the inner process may resolve. No-op when the list is empty. -fn write_extra_seatbelt_rules(out: &mut String, request: &CodexRequest) { +fn write_extra_seatbelt_rules(out: &mut String, request: &ExecutionRequest) { let Some(sb) = request.experimental.seatbelt.as_ref() else { return; }; @@ -473,8 +473,8 @@ mod tests { use super::*; use wxc_common::models::{SeatbeltConfig, UiPolicy}; - fn req() -> CodexRequest { - CodexRequest { + fn req() -> ExecutionRequest { + ExecutionRequest { script_code: "echo hi".to_string(), ..Default::default() } diff --git a/src/seatbelt_common/src/seatbelt_runner.rs b/src/seatbelt_common/src/seatbelt_runner.rs index 2bb1c325b..978e28ea7 100644 --- a/src/seatbelt_common/src/seatbelt_runner.rs +++ b/src/seatbelt_common/src/seatbelt_runner.rs @@ -27,7 +27,7 @@ use std::time::{Duration, Instant}; use mxc_pty::{run_with_pty, PtyOptions, PtyOutcome}; use wxc_common::logger::Logger; -use wxc_common::models::{CodexRequest, LaunchMethod, ScriptResponse}; +use wxc_common::models::{ExecutionRequest, LaunchMethod, ScriptResponse}; use wxc_common::script_runner::ScriptRunner; use crate::profile_builder::build_profile; @@ -68,7 +68,7 @@ impl SeatbeltScriptRunner { const POLL_INTERVAL_MS: u64 = 500; impl ScriptRunner for SeatbeltScriptRunner { - fn validate_runner(&self, request: &CodexRequest) -> Result<(), ScriptResponse> { + fn validate_runner(&self, request: &ExecutionRequest) -> Result<(), ScriptResponse> { // Seatbelt cannot filter network by hostname — reject blockedHosts // rather than silently allowing traffic the user expects to be denied. if !request.policy.blocked_hosts.is_empty() { @@ -92,7 +92,7 @@ impl ScriptRunner for SeatbeltScriptRunner { Ok(()) } - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { // 1. Build the Seatbelt profile from the policy. let profile = match build_profile(request) { Ok(p) => p, @@ -135,7 +135,7 @@ impl SeatbeltScriptRunner { fn execute_exec( &self, profile: &str, - request: &CodexRequest, + request: &ExecutionRequest, gui_access: bool, logger: &mut Logger, ) -> ScriptResponse { @@ -244,7 +244,7 @@ impl SeatbeltScriptRunner { fn execute_open( &self, profile: &str, - request: &CodexRequest, + request: &ExecutionRequest, logger: &mut Logger, ) -> ScriptResponse { let _ = writeln!( @@ -516,11 +516,11 @@ fn cleanup_files(paths: &[&str]) { #[cfg(test)] mod tests { use super::*; - use wxc_common::models::{CodexRequest, SeatbeltConfig}; + use wxc_common::models::{ExecutionRequest, SeatbeltConfig}; #[allow(clippy::field_reassign_with_default)] - fn base_request() -> CodexRequest { - let mut request = CodexRequest::default(); + fn base_request() -> ExecutionRequest { + let mut request = ExecutionRequest::default(); request.experimental_enabled = true; request.experimental.seatbelt = Some(SeatbeltConfig::default()); request diff --git a/src/wslc_common/src/wsl_container_runner.rs b/src/wslc_common/src/wsl_container_runner.rs index 1009dfb48..d76b79b88 100644 --- a/src/wslc_common/src/wsl_container_runner.rs +++ b/src/wslc_common/src/wsl_container_runner.rs @@ -15,7 +15,7 @@ use std::ptr; use std::sync::{Arc, Mutex}; use wxc_common::logger::Logger; -use wxc_common::models::{CodexRequest, NetworkPolicy, ScriptResponse, WslcConfig}; +use wxc_common::models::{ExecutionRequest, NetworkPolicy, ScriptResponse, WslcConfig}; use wxc_common::script_runner::ScriptRunner; use wxc_common::string_util::{to_wide, CoTaskMemPWSTR}; @@ -318,7 +318,7 @@ fn sdk_error(context: &str, hr: HRESULT, sdk_msg: &str) -> ScriptResponse { } impl ScriptRunner for WSLContainerRunner { - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { unsafe { self.run_internal(request, logger) } } } @@ -377,7 +377,7 @@ impl WSLContainerRunner { unsafe fn create_session( &self, sdk: &WslcSdk, - request: &CodexRequest, + request: &ExecutionRequest, logger: &mut Logger, ) -> Result { let session_name: Vec = to_wide(&request.container_id); @@ -731,7 +731,7 @@ impl WSLContainerRunner { process_guard: &WslcProcessGuard, container_guard: &WslcContainerGuard, io_ctx: &IoContext, - request: &CodexRequest, + request: &ExecutionRequest, logger: &mut Logger, ) -> Result<(i32, bool), ScriptResponse> { let mut exit_event: HANDLE = ptr::null_mut(); @@ -850,7 +850,11 @@ impl WSLContainerRunner { /// (`WslcSessionGuard`, `WslcContainerGuard`, `WslcProcessGuard`, /// `IoCtxRawGuard`) ensure handles and reference counts are released /// on every exit path. - unsafe fn run_internal(&self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + unsafe fn run_internal( + &self, + request: &ExecutionRequest, + logger: &mut Logger, + ) -> ScriptResponse { let _ = writeln!(logger, "[WSLC] Starting WSL Container runner"); // -- Init: COM + SDK + preflight -- diff --git a/src/wxc/src/main.rs b/src/wxc/src/main.rs index f49580415..47f0972b9 100644 --- a/src/wxc/src/main.rs +++ b/src/wxc/src/main.rs @@ -19,7 +19,7 @@ use wxc_common::hyperlight_runner::HyperlightScriptRunner; #[cfg(feature = "isolation_session")] use wxc_common::isolation_session::IsolationSessionRunner; use wxc_common::logger::{Logger, Mode}; -use wxc_common::models::{CodexRequest, ContainmentBackend, ScriptResponse}; +use wxc_common::models::{ContainmentBackend, ExecutionRequest, ScriptResponse}; use wxc_common::mxc_error::{MxcError, ResponseEnvelope}; #[cfg(feature = "microvm")] use wxc_common::nanvix_runner::NanVixScriptRunner; @@ -183,7 +183,7 @@ impl Cli { } } -fn log_request(request: &CodexRequest, logger: &mut Logger) { +fn log_request(request: &ExecutionRequest, logger: &mut Logger) { if !request.container_id.is_empty() { let _ = writeln!(logger, "Container ID: {}", request.container_id); } @@ -257,7 +257,7 @@ fn command_override_context_for_state_aware( } fn apply_command_override( - request: &mut CodexRequest, + request: &mut ExecutionRequest, command_override: Option<&str>, logger: &mut Logger, ) { @@ -842,7 +842,7 @@ fn main() { // Emit the full (redacted) request policy for diagnostics. let _ = writeln!( logger, - "SECTION: Full `CodexRequest` configuration (redacted)" + "SECTION: Full `ExecutionRequest` configuration (redacted)" ); let _ = writeln!( logger, @@ -1395,7 +1395,7 @@ mod tests { #[test] fn state_aware_command_override_only_applies_to_exec_phase() { let parsed = ParsedStateAwareRequest { - request: CodexRequest::default(), + request: ExecutionRequest::default(), phase: Phase::Start, containment: None, sandbox_id: Some("iso:wxc-1234".into()), diff --git a/src/wxc_common/src/appcontainer_runner.rs b/src/wxc_common/src/appcontainer_runner.rs index aa6e88953..b45d0e2c7 100644 --- a/src/wxc_common/src/appcontainer_runner.rs +++ b/src/wxc_common/src/appcontainer_runner.rs @@ -26,7 +26,7 @@ use windows_core::{PCWSTR, PWSTR}; use crate::error::WxcError; use crate::job_object::UiJobObject; use crate::logger::Logger; -use crate::models::{CodexRequest, NetworkEnforcementMode, NetworkPolicy, ScriptResponse}; +use crate::models::{ExecutionRequest, NetworkEnforcementMode, NetworkPolicy, ScriptResponse}; use crate::process_util::{get_capability_sid_from_name, OwnedHandle, SidAndAttributes}; use crate::script_runner::{get_timeout_milliseconds, ScriptRunner}; use crate::{process_mitigation, string_util, ui_policy}; @@ -343,7 +343,7 @@ impl AppContainerScriptRunner { /// Core implementation of `run_internal`, returning `Result` for ergonomic error handling. fn run_internal_impl( &self, - request: &CodexRequest, + request: &ExecutionRequest, logger: &mut Logger, ) -> Result { // --- Validate permissiveLearningMode --- @@ -730,7 +730,7 @@ impl AppContainerScriptRunner { } /// Create the AppContainer SID for the given request. - fn initialize(&mut self, request: &CodexRequest) -> Result<(), WxcError> { + fn initialize(&mut self, request: &ExecutionRequest) -> Result<(), WxcError> { let container_name = if request.container_id.is_empty() { "CLI".to_string() } else { @@ -756,7 +756,7 @@ impl AppContainerScriptRunner { } /// Execute the script inside the AppContainer, converting errors to ScriptResponse. - fn run_internal(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn run_internal(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { match self.run_internal_impl(request, logger) { Ok(response) => response, Err(e) => ScriptResponse::error(&e.to_string()), @@ -771,7 +771,7 @@ impl Default for AppContainerScriptRunner { } impl ScriptRunner for AppContainerScriptRunner { - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { use crate::filesystem_bfs::FileSystemBfsManager; use crate::launch_diagnostics::diagnose_process_exit; use crate::models::FailurePhase; diff --git a/src/wxc_common/src/base_container_runner.rs b/src/wxc_common/src/base_container_runner.rs index 04ac40c23..1daf91143 100644 --- a/src/wxc_common/src/base_container_runner.rs +++ b/src/wxc_common/src/base_container_runner.rs @@ -31,7 +31,8 @@ use crate::log_symbols::{ }; use crate::logger::Logger; use crate::models::{ - CodexRequest, FailurePhase, NetworkEnforcementMode, NetworkPolicy, ProxyAddress, ScriptResponse, + ExecutionRequest, FailurePhase, NetworkEnforcementMode, NetworkPolicy, ProxyAddress, + ScriptResponse, }; use crate::proxy_coordinator::ProxyCoordinator; use crate::sandbox_tracking::{self, TrackingEntry}; @@ -137,7 +138,7 @@ impl BaseContainerRunner { /// - `disallow_win32k_system_calls` from `ui.disable` /// - `ui_restrictions` bitmask from `ui.to_ui_restrictions_bitmask()` /// - `network_policy.proxy.url` from proxy config - fn build_sandbox_spec(request: &CodexRequest) -> Vec { + fn build_sandbox_spec(request: &ExecutionRequest) -> Vec { let mut builder = flatbuffers::FlatBufferBuilder::with_capacity(1024); let version = builder.create_string(SANDBOX_SPEC_VERSION); @@ -400,7 +401,7 @@ impl BaseContainerRunner { } impl ScriptRunner for BaseContainerRunner { - fn validate_runner(&self, request: &CodexRequest) -> Result<(), ScriptResponse> { + fn validate_runner(&self, request: &ExecutionRequest) -> Result<(), ScriptResponse> { Self::is_base_container_api_present().map_err(|e| { let hint = if !request.experimental_enabled { format!( @@ -422,7 +423,7 @@ impl ScriptRunner for BaseContainerRunner { }) } - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { let _ = writeln!( logger, "{EMOJI_SECTION} SECTION: Backend runner 'BaseContainer'" @@ -856,7 +857,7 @@ mod tests { #[test] fn build_sandbox_spec_produces_valid_flatbuffer() { - let mut request = CodexRequest::default(); + let mut request = ExecutionRequest::default(); request.policy.least_privilege_mode = true; request.policy.capabilities = vec!["internetClient".into(), "registryRead".into()]; request.policy.readwrite_paths = vec!["C:\\temp".into()]; @@ -908,7 +909,7 @@ mod tests { #[test] fn build_sandbox_spec_empty_policy() { // Default network policy is Block — no internetClient auto-add. - let request = CodexRequest::default(); + let request = ExecutionRequest::default(); let bytes = BaseContainerRunner::build_sandbox_spec(&request); assert!(base_container_layout::sandbox_spec_buffer_has_identifier( @@ -928,7 +929,7 @@ mod tests { #[test] fn build_sandbox_spec_network_block_no_internet_client() { - let mut request = CodexRequest::default(); + let mut request = ExecutionRequest::default(); request.policy.default_network_policy = NetworkPolicy::Block; let bytes = BaseContainerRunner::build_sandbox_spec(&request); @@ -938,7 +939,7 @@ mod tests { #[test] fn build_sandbox_spec_ui_disabled() { - let mut request = CodexRequest::default(); + let mut request = ExecutionRequest::default(); request.policy.ui = UiPolicy { disable: true, ..Default::default() @@ -968,7 +969,7 @@ mod tests { #[test] fn build_sandbox_spec_ui_clipboard_read_only() { - let mut request = CodexRequest::default(); + let mut request = ExecutionRequest::default(); request.policy.ui = UiPolicy { disable: false, clipboard: ClipboardPolicy::Read, @@ -999,7 +1000,7 @@ mod tests { #[test] fn build_sandbox_spec_ui_clipboard_readwrite_no_injection() { - let mut request = CodexRequest::default(); + let mut request = ExecutionRequest::default(); request.policy.ui = UiPolicy { disable: false, clipboard: ClipboardPolicy::All, @@ -1031,7 +1032,7 @@ mod tests { fn build_sandbox_spec_proxy_url() { use crate::models::ProxyAddress; - let mut request = CodexRequest::default(); + let mut request = ExecutionRequest::default(); request.policy.default_network_policy = NetworkPolicy::Block; request.policy.network_proxy = ProxyConfig { address: Some(ProxyAddress::new("127.0.0.1".to_string(), 8080)), @@ -1048,7 +1049,7 @@ mod tests { #[test] fn build_sandbox_spec_no_proxy() { - let request = CodexRequest::default(); + let request = ExecutionRequest::default(); let bytes = BaseContainerRunner::build_sandbox_spec(&request); let spec = base_container_layout::root_as_sandbox_spec(&bytes).unwrap(); assert!(spec.network_policy().is_none()); diff --git a/src/wxc_common/src/cmdline.rs b/src/wxc_common/src/cmdline.rs index 6aa437b0c..773ffdf60 100644 --- a/src/wxc_common/src/cmdline.rs +++ b/src/wxc_common/src/cmdline.rs @@ -2,9 +2,9 @@ // Licensed under the MIT License. //! Helpers for turning a CLI argv vector back into the single `commandLine` -//! string stored on `CodexRequest`. +//! string stored on `ExecutionRequest`. //! -//! `script_code` on `CodexRequest` is a single `String`, so when the +//! `script_code` on `ExecutionRequest` is a single `String`, so when the //! driver collects trailing CLI args we must serialise them as if the //! user had written the same value in `process.commandLine`. The direct //! Windows path uses `CommandLineToArgvW`-compatible quoting; shell-backed diff --git a/src/wxc_common/src/config_parser.rs b/src/wxc_common/src/config_parser.rs index 46488ad1b..2618f9b44 100644 --- a/src/wxc_common/src/config_parser.rs +++ b/src/wxc_common/src/config_parser.rs @@ -10,7 +10,7 @@ use crate::encoding::base64_decode; use crate::error::WxcError; use crate::logger::Logger; use crate::models::{ - ClipboardPolicy, CodexRequest, ContainerPolicy, ContainmentBackend, ExperimentalConfig, + ClipboardPolicy, ContainerPolicy, ContainmentBackend, ExecutionRequest, ExperimentalConfig, IsolationSessionConfig, IsolationSessionUser, LifecycleConfig, LxcConfig, NetworkEnforcementMode, NetworkPolicy, PortMapping, ProxyAddress, ProxyConfig, SeatbeltConfig, TestFeatureConfig, UiPolicy, WindowsSandboxConfig, WslcConfig, @@ -27,7 +27,7 @@ pub enum ParseError { /// I/O, base64-decode, or top-level JSON parse failure — the input could /// not be discriminated as state-aware vs one-shot. Decode(WxcError), - /// Discriminated as one-shot; conversion to `CodexRequest` failed. + /// Discriminated as one-shot; conversion to `ExecutionRequest` failed. OneShot(WxcError), /// Discriminated as state-aware; conversion to `ParsedStateAwareRequest` /// failed. Carries an `MxcError` so the driver can emit a typed envelope. @@ -234,7 +234,7 @@ struct RawConfig { // the struct, no field-level default) and acts as the discriminator against // `RawConfig`; the other fields mirror `RawConfig`'s wire shape so // cross-cutting fields (filesystem/network/ui/process) populate the inner -// `CodexRequest` via the same conversion path. The `experimental` block stays +// `ExecutionRequest` via the same conversion path. The `experimental` block stays // raw — typed deserialisation happens at dispatch time keyed by backend. #[derive(Deserialize)] struct RawStateAwareRequest { @@ -382,7 +382,7 @@ pub fn load_request( input: &str, logger: &mut Logger, is_base64: bool, -) -> Result { +) -> Result { load_request_with_options( input, logger, @@ -399,7 +399,7 @@ pub fn load_request_with_options( input: &str, logger: &mut Logger, opts: LoadOptions, -) -> Result { +) -> Result { let json_str = decode_request_input(input, logger, opts.is_base64)?; let raw: RawConfig = serde_json::from_str(&json_str).map_err(|e| { @@ -604,7 +604,7 @@ fn convert_raw_config_inner( logger: &mut Logger, require_process: bool, allow_missing_command: bool, -) -> Result { +) -> Result { // New top-level fields let schema_version = raw.version.unwrap_or_default(); let container_id = raw.container_id.unwrap_or_default(); @@ -1085,7 +1085,7 @@ fn convert_raw_config_inner( }; } - Ok(CodexRequest { + Ok(ExecutionRequest { schema_version, container_id, platform, @@ -1119,7 +1119,7 @@ fn convert_raw_state_aware( Some(s) => Some(parse_containment_str(s, logger)?), }; - // Build a RawConfig surrogate so the inner CodexRequest is populated by the + // Build a RawConfig surrogate so the inner ExecutionRequest is populated by the // same conversion path one-shot uses for cross-cutting wire fields. let surrogate = RawConfig { version: raw.version, diff --git a/src/wxc_common/src/diagnostic.rs b/src/wxc_common/src/diagnostic.rs index ee7ca0c15..7c4b870fb 100644 --- a/src/wxc_common/src/diagnostic.rs +++ b/src/wxc_common/src/diagnostic.rs @@ -8,7 +8,7 @@ use std::env; -use crate::models::CodexRequest; +use crate::models::ExecutionRequest; use windows::Win32::Foundation::CloseHandle; use windows::Win32::Security::{GetTokenInformation, TokenUser, TOKEN_QUERY, TOKEN_USER}; @@ -107,12 +107,12 @@ impl DiagnosticConfig { } } -/// Produce a redacted JSON representation of a `CodexRequest` suitable for diagnostic logging. +/// Produce a redacted JSON representation of an `ExecutionRequest` suitable for diagnostic logging. /// /// - Environment variable values are replaced with ``. /// - `script_code` is truncated to [`SCRIPT_CODE_TRUNCATE_LEN`] characters. /// - `network_proxy` (which is `#[serde(skip)]`) is logged separately. -pub fn redacted_request_json(request: &CodexRequest) -> String { +pub fn redacted_request_json(request: &ExecutionRequest) -> String { // Build a redacted copy for serialization. let mut redacted = request.clone(); @@ -267,7 +267,7 @@ mod tests { #[test] fn redacted_request_hides_env_values() { - let request = CodexRequest { + let request = ExecutionRequest { env: vec![ "PATH=C:\\Windows".to_string(), "SECRET_TOKEN=abc123".to_string(), @@ -283,7 +283,7 @@ mod tests { #[test] fn redacted_request_truncates_script_code() { - let request = CodexRequest { + let request = ExecutionRequest { script_code: "x".repeat(500), ..Default::default() }; @@ -294,7 +294,7 @@ mod tests { #[test] fn redacted_request_shows_proxy_info() { - let mut request = CodexRequest::default(); + let mut request = ExecutionRequest::default(); request.policy.network_proxy = ProxyConfig { address: Some(ProxyAddress::new("127.0.0.1".to_string(), 8080)), builtin_test_server: false, @@ -306,7 +306,7 @@ mod tests { #[test] fn redacted_request_shows_proxy_disabled() { - let request = CodexRequest::default(); + let request = ExecutionRequest::default(); let json = redacted_request_json(&request); assert!(json.contains("network_proxy: disabled")); } diff --git a/src/wxc_common/src/dispatcher.rs b/src/wxc_common/src/dispatcher.rs index 3a0e302f4..ae7f5da5d 100644 --- a/src/wxc_common/src/dispatcher.rs +++ b/src/wxc_common/src/dispatcher.rs @@ -4,7 +4,7 @@ //! BaseContainer-fallback tier dispatcher. //! //! Wires Phases 0–3 (telemetry, fallback detector, AppContainer modes, -//! DACL manager) into a single entrypoint. Given a [`CodexRequest`], the +//! DACL manager) into a single entrypoint. Given an [`ExecutionRequest`], the //! dispatcher consults [`crate::fallback_detector::detect`] to choose //! between Tier 1 (BaseContainer), Tier 2 (AppContainer + BFS), or Tier 3 //! (AppContainer + DACL), constructs the appropriate runner, and applies @@ -74,7 +74,7 @@ use crate::base_container_runner::BaseContainerRunner; use crate::error::WxcError; use crate::fallback_detector::{self, FallbackError, IsolationTier}; use crate::filesystem_dacl::{DaclError, DaclManager, RO_MASK, RW_MASK}; -use crate::models::CodexRequest; +use crate::models::ExecutionRequest; use crate::script_runner::ScriptRunner; /// Result of a successful dispatch decision: a phased handle holding a @@ -186,7 +186,7 @@ impl From for DispatchError { /// The container-id → AppContainer-name mapping used by the runners. Empty /// container_id maps to `"CLI"` (matches both AppContainerScriptRunner and /// BaseContainerRunner internals). -fn container_name(request: &CodexRequest) -> String { +fn container_name(request: &ExecutionRequest) -> String { if request.container_id.is_empty() { "CLI".to_string() } else { @@ -275,7 +275,7 @@ fn build_t3_dacl( /// execute and (when applicable) a [`DaclManager`] that has already /// applied its ACEs. Use [`Dispatched::into_runner_and_guard`] to /// extract both; the manager MUST stay alive through the run. -pub fn dispatch_with_fallback(request: &CodexRequest) -> Result { +pub fn dispatch_with_fallback(request: &ExecutionRequest) -> Result { let decision = fallback_detector::detect(&request.policy, /*prefer_bc=*/ true)?; let (runner, dacl_manager): (Box, Option) = match decision.tier { @@ -363,7 +363,7 @@ pub fn dispatch_with_fallback(request: &CodexRequest) -> Result CodexRequest { - CodexRequest { + fn test_request(policy: ContainerPolicy) -> ExecutionRequest { + ExecutionRequest { container_id: "MxcDispatcherTest".to_string(), policy, - ..CodexRequest::default() + ..ExecutionRequest::default() } } diff --git a/src/wxc_common/src/hyperlight_runner.rs b/src/wxc_common/src/hyperlight_runner.rs index b75ae2bbc..de43f88a2 100644 --- a/src/wxc_common/src/hyperlight_runner.rs +++ b/src/wxc_common/src/hyperlight_runner.rs @@ -72,7 +72,7 @@ use std::path::{Path, PathBuf}; use crate::logger::Logger; -use crate::models::{CodexRequest, NetworkPolicy, ScriptResponse}; +use crate::models::{ExecutionRequest, NetworkPolicy, ScriptResponse}; use crate::script_runner::ScriptRunner; use hyperlight_unikraft::pyhl; @@ -276,7 +276,7 @@ impl HyperlightScriptRunner { /// Reject only policies that the hyperlight backend genuinely cannot honor. /// Filesystem mounts and network policies ARE supported. - fn validate_policies(request: &CodexRequest) -> Result<(), PyhlError> { + fn validate_policies(request: &ExecutionRequest) -> Result<(), PyhlError> { if request.policy.network_proxy.is_enabled() { return Err(PyhlError::Preflight(ERR_PROXY_POLICY.to_string())); } @@ -320,7 +320,7 @@ impl HyperlightScriptRunner { /// - `default_network_policy == Block`, no host lists → `None` (networking disabled) /// - `default_network_policy == Allow`, no host lists → `AllowAll` fn network_policy_from_request( - request: &CodexRequest, + request: &ExecutionRequest, ) -> Result, PyhlError> { if !request.policy.allowed_hosts.is_empty() { let allow_list = AllowList::from_hosts(&request.policy.allowed_hosts) @@ -349,7 +349,7 @@ impl HyperlightScriptRunner { /// /// `readonlyPaths` are mounted with `Preopen::read_only()`, blocking /// all write operations at the host-function level. - fn preopens_from_policy(request: &CodexRequest) -> Result, PyhlError> { + fn preopens_from_policy(request: &ExecutionRequest) -> Result, PyhlError> { let mut preopens = Vec::new(); let mut seen_guest_paths = std::collections::HashSet::new(); @@ -490,11 +490,11 @@ impl HyperlightScriptRunner { } impl ScriptRunner for HyperlightScriptRunner { - fn validate_runner(&self, request: &CodexRequest) -> Result<(), ScriptResponse> { + fn validate_runner(&self, request: &ExecutionRequest) -> Result<(), ScriptResponse> { Self::validate_policies(request).map_err(|e| e.to_response()) } - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { let home = match Self::resolve_home() { Ok(h) => h, Err(e) => { @@ -721,7 +721,7 @@ mod tests { // the policy→Preopen mapping. let tmp = std::env::temp_dir().join(format!("hl-mount-{}", std::process::id())); std::fs::create_dir_all(&tmp).unwrap(); - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { readwrite_paths: vec![tmp.to_string_lossy().to_string()], ..Default::default() @@ -743,7 +743,7 @@ mod tests { let b = std::env::temp_dir().join(format!("hl-col-b-{}/same", std::process::id())); std::fs::create_dir_all(&a).unwrap(); std::fs::create_dir_all(&b).unwrap(); - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { readwrite_paths: vec![ a.to_string_lossy().to_string(), @@ -765,7 +765,7 @@ mod tests { #[test] fn policy_rejects_denied_overlapping_allow() { let mut r = runner(); - let request = CodexRequest { + let request = ExecutionRequest { script_code: "print('x')".to_string(), policy: ContainerPolicy { readwrite_paths: vec!["/tmp/x".to_string()], @@ -782,7 +782,7 @@ mod tests { #[test] fn network_policy_allow_all_when_default_allow() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { default_network_policy: NetworkPolicy::Allow, ..Default::default() @@ -798,7 +798,7 @@ mod tests { #[test] fn network_policy_allowlist_from_allowed_hosts() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { allowed_hosts: vec!["127.0.0.1".to_string()], ..Default::default() @@ -814,7 +814,7 @@ mod tests { #[test] fn network_policy_none_when_blocked() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { default_network_policy: NetworkPolicy::Block, ..Default::default() @@ -827,7 +827,7 @@ mod tests { #[test] fn network_policy_blocklist_from_blocked_hosts() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { blocked_hosts: vec!["127.0.0.1".to_string()], ..Default::default() @@ -844,7 +844,7 @@ mod tests { #[test] fn policy_rejects_allowed_and_blocked_hosts() { let mut r = runner(); - let request = CodexRequest { + let request = ExecutionRequest { script_code: "print('x')".to_string(), policy: ContainerPolicy { allowed_hosts: vec!["a.com".to_string()], @@ -862,7 +862,7 @@ mod tests { #[test] fn policy_rejects_working_directory() { let mut r = runner(); - let request = CodexRequest { + let request = ExecutionRequest { script_code: "print('x')".to_string(), working_directory: "C:/tmp".to_string(), ..Default::default() diff --git a/src/wxc_common/src/isolation_session/one_shot.rs b/src/wxc_common/src/isolation_session/one_shot.rs index e2b81b28f..9f25cee88 100644 --- a/src/wxc_common/src/isolation_session/one_shot.rs +++ b/src/wxc_common/src/isolation_session/one_shot.rs @@ -10,7 +10,7 @@ use std::io::IsTerminal; use crate::id::mint_random_token; use crate::logger::Logger; -use crate::models::{CodexRequest, IsolationSessionConfigurationId, ScriptResponse}; +use crate::models::{ExecutionRequest, IsolationSessionConfigurationId, ScriptResponse}; use crate::script_runner::ScriptRunner; use super::manager::IsolationSessionManager; @@ -19,7 +19,7 @@ use super::process_options::build_process_options; use super::IsolationSessionRunner; impl ScriptRunner for IsolationSessionRunner { - fn validate_runner(&self, request: &CodexRequest) -> Result<(), ScriptResponse> { + fn validate_runner(&self, request: &ExecutionRequest) -> Result<(), ScriptResponse> { // One-shot runs the full provision → start → exec → stop → // deprovision lifecycle in a single process, so provision-phase // semantics apply to the whole call. @@ -33,7 +33,7 @@ impl ScriptRunner for IsolationSessionRunner { validate_provision_policy(request).map_err(ScriptResponse::from) } - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { // Detect at runtime whether wxc-exec's stdout is a TTY. This flips // the backend into ConPTY mode (`InteractiveConsole = true`) and // adjusts the redirect flags (no separate stderr in ConPTY mode — @@ -153,7 +153,7 @@ mod tests { #[test] fn validate_runner_one_shot_rejects_user() { let runner = IsolationSessionRunner::new(); - let req = CodexRequest { + let req = ExecutionRequest { experimental: ExperimentalConfig { isolation_session: Some(IsolationSessionConfig { user: Some(well_formed_user()), @@ -175,7 +175,7 @@ mod tests { #[test] fn validate_runner_one_shot_accepts_no_user() { let runner = IsolationSessionRunner::new(); - let req = CodexRequest { + let req = ExecutionRequest { experimental: ExperimentalConfig { isolation_session: Some(IsolationSessionConfig::default()), ..Default::default() diff --git a/src/wxc_common/src/isolation_session/policy.rs b/src/wxc_common/src/isolation_session/policy.rs index f2abb02a5..361cc0e52 100644 --- a/src/wxc_common/src/isolation_session/policy.rs +++ b/src/wxc_common/src/isolation_session/policy.rs @@ -6,7 +6,7 @@ //! other filesystem field is rejected, and network / proxy policy is //! rejected at every phase — the backend has no equivalent primitive. -use crate::models::{CodexRequest, IsolationSessionUser, NetworkPolicy}; +use crate::models::{ExecutionRequest, IsolationSessionUser, NetworkPolicy}; use crate::mxc_error::MxcError; use super::error::IsolationSessionError; @@ -20,7 +20,7 @@ const ERR_PROXY_POLICY: &str = "network proxy is not supported by the isolation /// honored (applied later via `share_folders`); `denied_paths` is rejected /// because the underlying API has no equivalent primitive. pub(super) fn validate_provision_policy( - request: &CodexRequest, + request: &ExecutionRequest, ) -> Result<(), IsolationSessionError> { if !request.policy.denied_paths.is_empty() { return Err(IsolationSessionError::Policy( @@ -34,7 +34,7 @@ pub(super) fn validate_provision_policy( /// are rejected because filesystem policy is bound to provision and /// immutable thereafter. pub(super) fn validate_post_provision_policy( - request: &CodexRequest, + request: &ExecutionRequest, ) -> Result<(), IsolationSessionError> { if !request.policy.readwrite_paths.is_empty() || !request.policy.readonly_paths.is_empty() @@ -67,7 +67,9 @@ pub(super) fn validate_isolation_session_user(user: &IsolationSessionUser) -> Re Ok(()) } -fn validate_network_and_proxy_policy(request: &CodexRequest) -> Result<(), IsolationSessionError> { +fn validate_network_and_proxy_policy( + request: &ExecutionRequest, +) -> Result<(), IsolationSessionError> { if !request.policy.allowed_hosts.is_empty() || !request.policy.blocked_hosts.is_empty() || request.policy.default_network_policy != NetworkPolicy::Block @@ -107,7 +109,7 @@ mod tests { #[test] fn provision_policy_accepts_readwrite_paths() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { readwrite_paths: vec!["C:\\src".to_string()], ..Default::default() @@ -119,7 +121,7 @@ mod tests { #[test] fn provision_policy_accepts_readonly_paths() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { readonly_paths: vec!["C:\\data".to_string()], ..Default::default() @@ -131,7 +133,7 @@ mod tests { #[test] fn provision_policy_accepts_readwrite_and_readonly_together() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { readwrite_paths: vec!["C:\\src".to_string()], readonly_paths: vec!["C:\\data".to_string()], @@ -144,7 +146,7 @@ mod tests { #[test] fn provision_policy_rejects_denied_paths() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { denied_paths: vec!["C:\\secret".to_string()], ..Default::default() @@ -159,7 +161,7 @@ mod tests { #[test] fn provision_policy_rejects_denied_even_with_rw() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { readwrite_paths: vec!["C:\\src".to_string()], denied_paths: vec!["C:\\secret".to_string()], @@ -175,7 +177,7 @@ mod tests { #[test] fn provision_policy_rejects_network_policy() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { allowed_hosts: vec!["example.com".to_string()], ..Default::default() @@ -190,7 +192,7 @@ mod tests { #[test] fn provision_policy_rejects_proxy() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { network_proxy: ProxyConfig { address: Some(ProxyAddress::new("127.0.0.1".to_string(), 8080)), @@ -208,7 +210,7 @@ mod tests { #[test] fn post_provision_policy_rejects_readwrite_paths() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { readwrite_paths: vec!["C:\\src".to_string()], ..Default::default() @@ -223,7 +225,7 @@ mod tests { #[test] fn post_provision_policy_rejects_readonly_paths() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { readonly_paths: vec!["C:\\data".to_string()], ..Default::default() @@ -238,7 +240,7 @@ mod tests { #[test] fn post_provision_policy_rejects_denied_paths() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { denied_paths: vec!["C:\\secret".to_string()], ..Default::default() @@ -253,7 +255,7 @@ mod tests { #[test] fn post_provision_policy_rejects_blocked_hosts() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { blocked_hosts: vec!["evil.com".to_string()], ..Default::default() @@ -268,7 +270,7 @@ mod tests { #[test] fn post_provision_policy_rejects_network_allow_policy() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { default_network_policy: NetworkPolicy::Allow, ..Default::default() @@ -283,7 +285,7 @@ mod tests { #[test] fn post_provision_policy_accepts_default_request() { - let request = CodexRequest::default(); + let request = ExecutionRequest::default(); assert!(validate_post_provision_policy(&request).is_ok()); } diff --git a/src/wxc_common/src/isolation_session/process_options.rs b/src/wxc_common/src/isolation_session/process_options.rs index bcd769245..bee0ef77d 100644 --- a/src/wxc_common/src/isolation_session/process_options.rs +++ b/src/wxc_common/src/isolation_session/process_options.rs @@ -2,10 +2,10 @@ // Licensed under the MIT License. //! Process-creation options for the IsolationSession backend: MXC-internal -//! `ProcessOptions` built from a `CodexRequest`, then translated to the +//! `ProcessOptions` built from an `ExecutionRequest`, then translated to the //! WinRT `IsoSessionProcessOptions` consumed by `RunProcessWithOptionsAsync`. -use crate::models::CodexRequest; +use crate::models::ExecutionRequest; use isolation_session_bindings::bindings::IsoSessionProcessOptions; use windows_core::HSTRING; @@ -30,7 +30,7 @@ fn compute_redirect_flags(interactive: bool) -> u32 { flags } -/// Process creation options decoupled from `CodexRequest` and from the +/// Process creation options decoupled from `ExecutionRequest` and from the /// WinRT types — small struct so the builder is unit-testable without a /// live `IsoSessionOps` activation. #[derive(Debug, Clone, PartialEq, Eq)] @@ -48,14 +48,17 @@ pub(super) struct ProcessOptions { pub interactive: bool, } -/// Builds `ProcessOptions` from a `CodexRequest`. `interactive` flips the +/// Builds `ProcessOptions` from an `ExecutionRequest`. `interactive` flips the /// backend into ConPTY mode (`InteractiveConsole = true`) and adjusts /// `redirect_flags` accordingly (no separate stderr stream in ConPTY mode). /// /// The command line is wrapped with `cmd.exe /c` so shell features (pipes, /// redirections, chained commands) work — same pattern as the LXC backend's /// `/bin/sh -c`. -pub(super) fn build_process_options(request: &CodexRequest, interactive: bool) -> ProcessOptions { +pub(super) fn build_process_options( + request: &ExecutionRequest, + interactive: bool, +) -> ProcessOptions { let env_vars: Vec<(String, String)> = request .env .iter() @@ -138,7 +141,7 @@ mod tests { #[test] fn options_wraps_command_with_cmd_exe() { - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo hello".to_string(), ..Default::default() }; @@ -155,7 +158,7 @@ mod tests { #[test] fn options_maps_timeout() { - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo hi".to_string(), script_timeout: 30000, ..Default::default() @@ -166,7 +169,7 @@ mod tests { #[test] fn options_maps_working_directory() { - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo hi".to_string(), working_directory: r"C:\Windows".to_string(), ..Default::default() @@ -177,7 +180,7 @@ mod tests { #[test] fn options_parses_env_vars() { - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo hi".to_string(), env: vec!["FOO=bar".to_string(), "PATH=C:\\bin;C:\\tools".to_string()], ..Default::default() @@ -193,7 +196,7 @@ mod tests { #[test] fn options_skips_malformed_env_vars() { - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo hi".to_string(), env: vec![ "GOOD=value".to_string(), @@ -210,7 +213,7 @@ mod tests { #[test] fn options_non_interactive_redirects_all_three_streams() { - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo hi".to_string(), ..Default::default() }; @@ -224,7 +227,7 @@ mod tests { #[test] fn options_interactive_redirects_stdin_stdout_only() { - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo hi".to_string(), ..Default::default() }; diff --git a/src/wxc_common/src/isolation_session/state_aware.rs b/src/wxc_common/src/isolation_session/state_aware.rs index a9eb6fcbe..39575469b 100644 --- a/src/wxc_common/src/isolation_session/state_aware.rs +++ b/src/wxc_common/src/isolation_session/state_aware.rs @@ -11,7 +11,7 @@ use std::io::IsTerminal; use serde::Serialize; use crate::id::mint_random_token; -use crate::models::{CodexRequest, IsolationSessionConfig, IsolationSessionProvisionConfig}; +use crate::models::{ExecutionRequest, IsolationSessionConfig, IsolationSessionProvisionConfig}; use crate::mxc_error::MxcError; use crate::state_aware_backend::{ DeprovisionResult, ExecHandle, ProvisionResult, StartResult, StatefulSandboxBackend, StopResult, @@ -72,7 +72,7 @@ impl StatefulSandboxBackend for IsolationSessionRunner { fn provision( &mut self, - request: &CodexRequest, + request: &ExecutionRequest, config: Option, ) -> Result, MxcError> { let user = config.and_then(|c| c.user); @@ -125,7 +125,7 @@ impl StatefulSandboxBackend for IsolationSessionRunner { fn start( &mut self, sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, config: Option, ) -> Result, MxcError> { let provision_id = extract_provision_id(sandbox_id)?; @@ -146,7 +146,7 @@ impl StatefulSandboxBackend for IsolationSessionRunner { fn stop( &mut self, sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<()>, ) -> Result, MxcError> { let provision_id = extract_provision_id(sandbox_id)?; @@ -161,7 +161,7 @@ impl StatefulSandboxBackend for IsolationSessionRunner { fn deprovision( &mut self, sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<()>, ) -> Result, MxcError> { let provision_id = extract_provision_id(sandbox_id)?; @@ -182,7 +182,7 @@ impl StatefulSandboxBackend for IsolationSessionRunner { fn validate_provision( &self, - request: &CodexRequest, + request: &ExecutionRequest, config: Option<&IsolationSessionProvisionConfig>, ) -> Result<(), MxcError> { if let Some(user) = config.and_then(|c| c.user.as_ref()) { @@ -194,7 +194,7 @@ impl StatefulSandboxBackend for IsolationSessionRunner { fn validate_start( &self, sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, config: Option<&IsolationSessionConfig>, ) -> Result<(), MxcError> { let provision_id = extract_provision_id(sandbox_id)?; @@ -227,7 +227,7 @@ impl StatefulSandboxBackend for IsolationSessionRunner { fn validate_exec( &self, _sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, _config: Option<&()>, ) -> Result<(), MxcError> { validate_post_provision_policy(request).map_err(map_lifecycle_error) @@ -236,7 +236,7 @@ impl StatefulSandboxBackend for IsolationSessionRunner { fn validate_stop( &self, _sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, _config: Option<&()>, ) -> Result<(), MxcError> { validate_post_provision_policy(request).map_err(map_lifecycle_error) @@ -245,7 +245,7 @@ impl StatefulSandboxBackend for IsolationSessionRunner { fn validate_deprovision( &self, _sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, _config: Option<&()>, ) -> Result<(), MxcError> { validate_post_provision_policy(request).map_err(map_lifecycle_error) @@ -261,7 +261,7 @@ impl StatefulSandboxBackend for IsolationSessionRunner { fn exec( &mut self, sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, _config: Option<()>, ) -> Result { let provision_id = extract_provision_id(sandbox_id)?; @@ -328,8 +328,8 @@ mod tests { ); } - fn request_with_filesystem_policy() -> CodexRequest { - CodexRequest { + fn request_with_filesystem_policy() -> ExecutionRequest { + ExecutionRequest { policy: ContainerPolicy { readwrite_paths: vec!["C:\\workspace".into()], ..Default::default() @@ -378,7 +378,7 @@ mod tests { #[test] fn validate_provision_hook_rejects_denied_paths() { let runner = IsolationSessionRunner::new(); - let req = CodexRequest { + let req = ExecutionRequest { policy: ContainerPolicy { denied_paths: vec!["C:\\secret".into()], ..Default::default() @@ -412,7 +412,7 @@ mod tests { #[test] fn validate_phase_hooks_accept_clean_request() { let runner = IsolationSessionRunner::new(); - let req = CodexRequest::default(); + let req = ExecutionRequest::default(); runner.validate_provision(&req, None).unwrap(); runner.validate_start("iso:abc", &req, None).unwrap(); @@ -430,7 +430,7 @@ mod tests { user: Some(well_formed_user()), }; runner - .validate_provision(&CodexRequest::default(), Some(&cfg)) + .validate_provision(&ExecutionRequest::default(), Some(&cfg)) .unwrap(); } @@ -444,7 +444,7 @@ mod tests { }), }; let err = runner - .validate_provision(&CodexRequest::default(), Some(&cfg)) + .validate_provision(&ExecutionRequest::default(), Some(&cfg)) .unwrap_err(); assert_eq!(err.code, MxcErrorCode::PolicyValidation); } @@ -459,7 +459,7 @@ mod tests { runner .validate_start( "iso:alice@contoso.com", - &CodexRequest::default(), + &ExecutionRequest::default(), Some(&cfg), ) .unwrap(); @@ -469,7 +469,7 @@ mod tests { fn validate_start_entra_sandbox_without_user_is_malformed() { let runner = IsolationSessionRunner::new(); let err = runner - .validate_start("iso:alice@contoso.com", &CodexRequest::default(), None) + .validate_start("iso:alice@contoso.com", &ExecutionRequest::default(), None) .unwrap_err(); assert_eq!(err.code, MxcErrorCode::MalformedRequest); assert!( @@ -487,7 +487,7 @@ mod tests { ..Default::default() }; let err = runner - .validate_start("iso:wxc-abcd1234", &CodexRequest::default(), Some(&cfg)) + .validate_start("iso:wxc-abcd1234", &ExecutionRequest::default(), Some(&cfg)) .unwrap_err(); assert_eq!(err.code, MxcErrorCode::MalformedRequest); assert!( @@ -501,7 +501,7 @@ mod tests { fn validate_start_local_sandbox_without_user_accepts() { let runner = IsolationSessionRunner::new(); runner - .validate_start("iso:wxc-abcd1234", &CodexRequest::default(), None) + .validate_start("iso:wxc-abcd1234", &ExecutionRequest::default(), None) .unwrap(); } @@ -518,7 +518,7 @@ mod tests { let err = runner .validate_start( "iso:alice@contoso.com", - &CodexRequest::default(), + &ExecutionRequest::default(), Some(&cfg), ) .unwrap_err(); @@ -543,7 +543,7 @@ mod tests { runner .validate_start( "iso:alice@contoso.com", - &CodexRequest::default(), + &ExecutionRequest::default(), Some(&cfg), ) .unwrap(); diff --git a/src/wxc_common/src/models.rs b/src/wxc_common/src/models.rs index ad185244d..4870d04aa 100644 --- a/src/wxc_common/src/models.rs +++ b/src/wxc_common/src/models.rs @@ -518,7 +518,7 @@ pub struct ExperimentalConfig { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default)] -pub struct CodexRequest { +pub struct ExecutionRequest { /// Schema version for the config format. pub schema_version: String, /// Externally assigned container identifier. @@ -547,7 +547,7 @@ pub struct CodexRequest { pub dry_run: bool, } -impl Default for CodexRequest { +impl Default for ExecutionRequest { fn default() -> Self { Self { schema_version: String::new(), diff --git a/src/wxc_common/src/nanvix_runner.rs b/src/wxc_common/src/nanvix_runner.rs index 803bd2b9b..19c086951 100644 --- a/src/wxc_common/src/nanvix_runner.rs +++ b/src/wxc_common/src/nanvix_runner.rs @@ -48,7 +48,7 @@ use std::thread::JoinHandle; use std::time::{Duration, Instant}; use crate::logger::Logger; -use crate::models::{CodexRequest, NetworkPolicy, ScriptResponse}; +use crate::models::{ExecutionRequest, NetworkPolicy, ScriptResponse}; use crate::script_runner::ScriptRunner; /// Multi-binary initrd (daemons + CPython) loaded by NanVix at warm start. @@ -432,7 +432,7 @@ impl NanVixScriptRunner { } } - fn validate_policies(request: &CodexRequest) -> Result<(), NanVixError> { + fn validate_policies(request: &ExecutionRequest) -> Result<(), NanVixError> { // denied_paths is explicitly rejected — microvm has no host visibility. if !request.policy.denied_paths.is_empty() { return Err(NanVixError::Preflight(ERR_DENIED_PATHS.to_string())); @@ -674,11 +674,11 @@ impl NanVixScriptRunner { } impl ScriptRunner for NanVixScriptRunner { - fn validate_runner(&self, request: &CodexRequest) -> Result<(), ScriptResponse> { + fn validate_runner(&self, request: &ExecutionRequest) -> Result<(), ScriptResponse> { Self::validate_policies(request).map_err(|e| e.to_response()) } - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { let paths = match self.resolve_paths() { Ok(p) => p, Err(e) => return e.to_response(), @@ -784,7 +784,7 @@ mod tests { #[test] fn policy_accepts_readwrite_paths() { // Validation passes; the runner fails later on path resolution. - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo test".to_string(), policy: ContainerPolicy { readwrite_paths: vec!["/tmp".to_string()], @@ -798,7 +798,7 @@ mod tests { #[test] fn policy_accepts_readonly_paths() { - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo test".to_string(), policy: ContainerPolicy { readonly_paths: vec!["/data".to_string()], @@ -812,7 +812,7 @@ mod tests { #[test] fn policy_rejects_denied_paths() { - let request = CodexRequest { + let request = ExecutionRequest { policy: ContainerPolicy { denied_paths: vec!["/secret".to_string()], ..Default::default() @@ -832,7 +832,7 @@ mod tests { #[test] fn policy_rejects_network_hosts() { let mut runner = NanVixScriptRunner::new(); - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo test".to_string(), policy: ContainerPolicy { allowed_hosts: vec!["example.com".to_string()], @@ -849,7 +849,7 @@ mod tests { #[test] fn policy_rejects_blocked_network_hosts() { let mut runner = NanVixScriptRunner::new(); - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo test".to_string(), policy: ContainerPolicy { blocked_hosts: vec!["evil.com".to_string()], @@ -866,7 +866,7 @@ mod tests { #[test] fn policy_rejects_network_allow_policy() { let mut runner = NanVixScriptRunner::new(); - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo test".to_string(), policy: ContainerPolicy { default_network_policy: NetworkPolicy::Allow, @@ -883,7 +883,7 @@ mod tests { #[test] fn policy_rejects_working_directory() { let mut runner = NanVixScriptRunner::new(); - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo test".to_string(), working_directory: "/home/user".to_string(), ..Default::default() @@ -900,7 +900,7 @@ mod tests { // no network stack -- Block is naturally enforced. The run then // fails later on missing nanvixd binaries, not on policy. let mut runner = NanVixScriptRunner::new(); - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo test".to_string(), ..Default::default() }; @@ -920,7 +920,7 @@ mod tests { #[test] fn policy_rejects_network_proxy() { let mut runner = NanVixScriptRunner::new(); - let request = CodexRequest { + let request = ExecutionRequest { script_code: "echo test".to_string(), policy: ContainerPolicy { network_proxy: crate::models::ProxyConfig { diff --git a/src/wxc_common/src/script_runner.rs b/src/wxc_common/src/script_runner.rs index d6eab96a1..30dd79167 100644 --- a/src/wxc_common/src/script_runner.rs +++ b/src/wxc_common/src/script_runner.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. use crate::logger::Logger; -use crate::models::{CodexRequest, ScriptResponse}; +use crate::models::{ExecutionRequest, ScriptResponse}; use crate::validator::validate_common; /// Trait for executing scripts within a containment backend. @@ -17,18 +17,18 @@ use crate::validator::validate_common; pub trait ScriptRunner { /// Validate the request before execution. Override to add /// runner-specific checks. Default accepts all requests. - fn validate_runner(&self, _request: &CodexRequest) -> Result<(), ScriptResponse> { + fn validate_runner(&self, _request: &ExecutionRequest) -> Result<(), ScriptResponse> { Ok(()) } /// Execute the script inside this backend's containment and return the response. /// Implement this instead of `run` — validation and dry-run are handled by the trait. - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse; + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse; /// Entry point called by the binary. Runs shared validation, runner-specific /// validation, checks for dry-run mode, then delegates to /// [`execute`](ScriptRunner::execute). - fn run(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn run(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { if let Err(response) = validate_common(request) { return response; } diff --git a/src/wxc_common/src/state_aware_backend.rs b/src/wxc_common/src/state_aware_backend.rs index 278e04602..15848aa1c 100644 --- a/src/wxc_common/src/state_aware_backend.rs +++ b/src/wxc_common/src/state_aware_backend.rs @@ -8,7 +8,7 @@ //! exposes the five lifecycle phases — provision, start, exec, stop, //! deprovision — plus per-phase validation hooks. //! -//! Methods take `&CodexRequest` (the same one-shot domain model used by every +//! Methods take `&ExecutionRequest` (the same one-shot domain model used by every //! existing backend) plus the typed `sandbox_id` for non-provision phases and //! an optional backend-specific config object. Cross-cutting policy fields //! flow through `request.policy`; per-exec process info flows through @@ -22,7 +22,7 @@ use serde::{de::DeserializeOwned, Serialize}; use crate::id::mint_random_token; -use crate::models::CodexRequest; +use crate::models::ExecutionRequest; use crate::mxc_error::MxcError; /// Platform pipe-handle wrapper used by `ExecHandle`. On Windows this is a @@ -116,7 +116,7 @@ pub trait StatefulSandboxBackend { /// metadata. Override when the backend has native provision work. fn provision( &mut self, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option, ) -> Result, MxcError> { Ok(ProvisionResult { @@ -129,7 +129,7 @@ pub trait StatefulSandboxBackend { fn start( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option, ) -> Result, MxcError> { Ok(StartResult { metadata: None }) @@ -139,7 +139,7 @@ pub trait StatefulSandboxBackend { fn exec( &mut self, sandbox_id: &str, - request: &CodexRequest, + request: &ExecutionRequest, config: Option, ) -> Result; @@ -147,7 +147,7 @@ pub trait StatefulSandboxBackend { fn stop( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option, ) -> Result, MxcError> { Ok(StopResult { metadata: None }) @@ -157,7 +157,7 @@ pub trait StatefulSandboxBackend { fn deprovision( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option, ) -> Result, MxcError> { Ok(DeprovisionResult { metadata: None }) @@ -169,7 +169,7 @@ pub trait StatefulSandboxBackend { /// enforcement, id format checks beyond the prefix). fn validate_provision( &self, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&Self::ProvisionConfig>, ) -> Result<(), MxcError> { Ok(()) @@ -178,7 +178,7 @@ pub trait StatefulSandboxBackend { fn validate_start( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&Self::StartConfig>, ) -> Result<(), MxcError> { Ok(()) @@ -187,7 +187,7 @@ pub trait StatefulSandboxBackend { fn validate_exec( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&Self::ExecConfig>, ) -> Result<(), MxcError> { Ok(()) @@ -196,7 +196,7 @@ pub trait StatefulSandboxBackend { fn validate_stop( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&Self::StopConfig>, ) -> Result<(), MxcError> { Ok(()) @@ -205,7 +205,7 @@ pub trait StatefulSandboxBackend { fn validate_deprovision( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&Self::DeprovisionConfig>, ) -> Result<(), MxcError> { Ok(()) @@ -239,7 +239,7 @@ mod tests { fn exec( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<()>, ) -> Result { Err(MxcError::backend_error("StubBackend::exec not implemented")) @@ -249,7 +249,7 @@ mod tests { #[test] fn default_provision_mints_id_with_prefix_and_token() { let mut b = StubBackend; - let r = b.provision(&CodexRequest::default(), None).unwrap(); + let r = b.provision(&ExecutionRequest::default(), None).unwrap(); // Expected shape: "stub:" followed by 8 lowercase hex chars. assert!(r.sandbox_id.starts_with("stub:"), "got {:?}", r.sandbox_id); let token = &r.sandbox_id["stub:".len()..]; @@ -272,8 +272,8 @@ mod tests { #[test] fn default_provision_produces_distinct_ids() { let mut b = StubBackend; - let a = b.provision(&CodexRequest::default(), None).unwrap(); - let c = b.provision(&CodexRequest::default(), None).unwrap(); + let a = b.provision(&ExecutionRequest::default(), None).unwrap(); + let c = b.provision(&ExecutionRequest::default(), None).unwrap(); assert_ne!(a.sandbox_id, c.sandbox_id); } @@ -281,7 +281,7 @@ mod tests { fn default_start_returns_no_metadata() { let mut b = StubBackend; let r = b - .start("stub:abcd1234", &CodexRequest::default(), None) + .start("stub:abcd1234", &ExecutionRequest::default(), None) .unwrap(); assert!(r.metadata.is_none()); } @@ -290,7 +290,7 @@ mod tests { fn default_stop_returns_no_metadata() { let mut b = StubBackend; let r = b - .stop("stub:abcd1234", &CodexRequest::default(), None) + .stop("stub:abcd1234", &ExecutionRequest::default(), None) .unwrap(); assert!(r.metadata.is_none()); } @@ -299,7 +299,7 @@ mod tests { fn default_deprovision_returns_no_metadata() { let mut b = StubBackend; let r = b - .deprovision("stub:abcd1234", &CodexRequest::default(), None) + .deprovision("stub:abcd1234", &ExecutionRequest::default(), None) .unwrap(); assert!(r.metadata.is_none()); } @@ -307,7 +307,7 @@ mod tests { #[test] fn default_validate_hooks_all_pass() { let b = StubBackend; - let req = CodexRequest::default(); + let req = ExecutionRequest::default(); b.validate_provision(&req, None).unwrap(); b.validate_start("stub:abcd1234", &req, None).unwrap(); b.validate_exec("stub:abcd1234", &req, None).unwrap(); @@ -322,7 +322,7 @@ mod tests { // surface this code rather than aborting the test binary. let mut b = StubBackend; let err = b - .exec("stub:abcd1234", &CodexRequest::default(), None) + .exec("stub:abcd1234", &ExecutionRequest::default(), None) .unwrap_err(); assert_eq!(err.code, MxcErrorCode::BackendError); } diff --git a/src/wxc_common/src/state_aware_dispatch.rs b/src/wxc_common/src/state_aware_dispatch.rs index b841c3e71..7afe00550 100644 --- a/src/wxc_common/src/state_aware_dispatch.rs +++ b/src/wxc_common/src/state_aware_dispatch.rs @@ -245,7 +245,7 @@ impl HasMetadata for DeprovisionResult { #[cfg(test)] mod tests { use super::*; - use crate::models::CodexRequest; + use crate::models::ExecutionRequest; use crate::mxc_error::MxcErrorCode; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -305,7 +305,7 @@ mod tests { fn provision( &mut self, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<()>, ) -> Result, MxcError> { self.provision_calls.set(self.provision_calls.get() + 1); @@ -320,7 +320,7 @@ mod tests { fn start( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<()>, ) -> Result, MxcError> { self.start_calls.set(self.start_calls.get() + 1); @@ -329,7 +329,7 @@ mod tests { fn exec( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<()>, ) -> Result { self.exec_calls.set(self.exec_calls.get() + 1); @@ -338,7 +338,7 @@ mod tests { fn stop( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<()>, ) -> Result, MxcError> { self.stop_calls.set(self.stop_calls.get() + 1); @@ -347,7 +347,7 @@ mod tests { fn deprovision( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<()>, ) -> Result, MxcError> { self.deprovision_calls.set(self.deprovision_calls.get() + 1); @@ -356,7 +356,7 @@ mod tests { fn validate_provision( &self, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&()>, ) -> Result<(), MxcError> { self.validate_provision_calls @@ -369,7 +369,7 @@ mod tests { fn validate_start( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&()>, ) -> Result<(), MxcError> { self.validate_start_calls @@ -379,7 +379,7 @@ mod tests { fn validate_exec( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&()>, ) -> Result<(), MxcError> { self.validate_exec_calls @@ -389,7 +389,7 @@ mod tests { fn validate_stop( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&()>, ) -> Result<(), MxcError> { self.validate_stop_calls @@ -399,7 +399,7 @@ mod tests { fn validate_deprovision( &self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<&()>, ) -> Result<(), MxcError> { self.validate_deprovision_calls @@ -445,7 +445,7 @@ mod tests { fn exec( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, _config: Option<()>, ) -> Result { Err(MxcError::backend_error("typed stub exec not wired")) @@ -454,7 +454,7 @@ mod tests { fn start( &mut self, _sandbox_id: &str, - _request: &CodexRequest, + _request: &ExecutionRequest, config: Option, ) -> Result, MxcError> { self.captured_start_config.set(config); @@ -468,7 +468,7 @@ mod tests { exp: Option, ) -> ParsedStateAwareRequest { ParsedStateAwareRequest { - request: CodexRequest::default(), + request: ExecutionRequest::default(), phase, containment: Some(ContainmentBackend::IsolationSession), sandbox_id: sandbox_id.map(String::from), @@ -643,7 +643,7 @@ mod tests { // No state-aware impls registered yet — every recognized backend is // unsupported. Smoke-test scenario #2 from decision 6. let p = ParsedStateAwareRequest { - request: CodexRequest::default(), + request: ExecutionRequest::default(), phase: Phase::Provision, containment: Some(ContainmentBackend::Wslc), sandbox_id: None, @@ -656,7 +656,7 @@ mod tests { #[test] fn run_state_aware_provision_without_containment_is_malformed() { let p = ParsedStateAwareRequest { - request: CodexRequest::default(), + request: ExecutionRequest::default(), phase: Phase::Provision, containment: None, sandbox_id: None, @@ -669,7 +669,7 @@ mod tests { #[test] fn resolve_backend_for_iso_prefix_returns_isolation_session() { let p = ParsedStateAwareRequest { - request: CodexRequest::default(), + request: ExecutionRequest::default(), phase: Phase::Start, containment: None, sandbox_id: Some("iso:wxc-abcd1234".into()), @@ -684,7 +684,7 @@ mod tests { #[test] fn resolve_backend_for_unknown_prefix_returns_unsupported_containment() { let p = ParsedStateAwareRequest { - request: CodexRequest::default(), + request: ExecutionRequest::default(), phase: Phase::Start, containment: None, sandbox_id: Some("unknownxyz:abc".into()), @@ -697,7 +697,7 @@ mod tests { #[test] fn resolve_backend_for_malformed_id_surfaces_malformed_id() { let p = ParsedStateAwareRequest { - request: CodexRequest::default(), + request: ExecutionRequest::default(), phase: Phase::Start, containment: None, sandbox_id: Some("no-colon".into()), diff --git a/src/wxc_common/src/state_aware_request.rs b/src/wxc_common/src/state_aware_request.rs index 388560874..aa99d895b 100644 --- a/src/wxc_common/src/state_aware_request.rs +++ b/src/wxc_common/src/state_aware_request.rs @@ -4,11 +4,11 @@ //! State-aware request types — public domain output of the parser. //! //! `MxcRequest` is the bridge between parser and dispatcher: a one-shot call -//! produces `MxcRequest::OneShot(CodexRequest)`, a state-aware call produces +//! produces `MxcRequest::OneShot(ExecutionRequest)`, a state-aware call produces //! `MxcRequest::StateAware(ParsedStateAwareRequest)`. The parser narrows the //! discriminator by presence of the wire-format `phase` field. //! -//! `ParsedStateAwareRequest` bundles the inner `CodexRequest` (populated by +//! `ParsedStateAwareRequest` bundles the inner `ExecutionRequest` (populated by //! the same parser path one-shot uses for cross-cutting fields) with the //! state-aware-only fields: `phase`, optional `containment`, optional //! `sandbox_id`, and the raw JSON `experimental` block. The dispatcher @@ -19,7 +19,7 @@ use serde::de::DeserializeOwned; use serde_json::Value; -use crate::models::{CodexRequest, ContainmentBackend}; +use crate::models::{ContainmentBackend, ExecutionRequest}; use crate::mxc_error::MxcError; /// Lifecycle phase in a state-aware request. @@ -67,7 +67,7 @@ impl std::fmt::Display for Phase { } } -/// Parsed state-aware request. Pairs the inner `CodexRequest` with the +/// Parsed state-aware request. Pairs the inner `ExecutionRequest` with the /// state-aware-only wire fields the dispatcher consumes. #[derive(Debug, Clone)] pub struct ParsedStateAwareRequest { @@ -76,7 +76,7 @@ pub struct ParsedStateAwareRequest { /// For non-exec phases the process-related fields (`script_code`, /// `working_directory`, `script_timeout`, `env`) are left at their default /// values; only exec carries process info on the wire. - pub request: CodexRequest, + pub request: ExecutionRequest, pub phase: Phase, /// Present iff the request carried a `containment` field. Required for /// `provision`; for non-provision phases the dispatcher resolves the @@ -136,7 +136,7 @@ impl ParsedStateAwareRequest { /// Public bridge between parser and dispatcher. #[derive(Debug, Clone)] pub enum MxcRequest { - OneShot(CodexRequest), + OneShot(ExecutionRequest), StateAware(ParsedStateAwareRequest), } @@ -154,7 +154,7 @@ mod tests { fn parsed_with_experimental(exp: Option, phase: Phase) -> ParsedStateAwareRequest { ParsedStateAwareRequest { - request: CodexRequest::default(), + request: ExecutionRequest::default(), phase, containment: None, sandbox_id: None, diff --git a/src/wxc_common/src/validator.rs b/src/wxc_common/src/validator.rs index ed87f3bca..f6e6d4225 100644 --- a/src/wxc_common/src/validator.rs +++ b/src/wxc_common/src/validator.rs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::models::{CodexRequest, ScriptResponse}; +use crate::models::{ExecutionRequest, ScriptResponse}; use crate::mxc_error::MxcError; /// Validates non-backend-specific parts of the request (e.g. non-empty script). -pub fn validate_common(request: &CodexRequest) -> Result<(), ScriptResponse> { +pub fn validate_common(request: &ExecutionRequest) -> Result<(), ScriptResponse> { if request.script_code.is_empty() { return Err(ScriptResponse::error("Script content must not be empty.")); } @@ -15,7 +15,7 @@ pub fn validate_common(request: &CodexRequest) -> Result<(), ScriptResponse> { /// Cross-backend invariants for state-aware `exec`. The dispatcher calls this /// before the backend's own `validate_exec` hook. Only the exec phase has a /// common-check today (a non-empty `process.commandLine`). -pub fn validate_exec_common(request: &CodexRequest) -> Result<(), MxcError> { +pub fn validate_exec_common(request: &ExecutionRequest) -> Result<(), MxcError> { if request.script_code.is_empty() { return Err(MxcError::malformed_request( "exec phase requires a non-empty process.commandLine", @@ -27,12 +27,12 @@ pub fn validate_exec_common(request: &CodexRequest) -> Result<(), MxcError> { #[cfg(test)] mod tests { use super::*; - use crate::models::CodexRequest; + use crate::models::ExecutionRequest; use crate::mxc_error::MxcErrorCode; #[test] fn rejects_empty_script() { - let req = CodexRequest { + let req = ExecutionRequest { script_code: String::new(), ..Default::default() }; @@ -41,7 +41,7 @@ mod tests { #[test] fn accepts_valid_script() { - let req = CodexRequest { + let req = ExecutionRequest { script_code: "echo hello".to_string(), ..Default::default() }; @@ -50,7 +50,7 @@ mod tests { #[test] fn accepts_full_config() { - let req = CodexRequest { + let req = ExecutionRequest { script_code: "print('test')".to_string(), working_directory: "C:\\temp".to_string(), script_timeout: 5000, @@ -62,7 +62,7 @@ mod tests { #[test] fn error_mentions_empty() { - let req = CodexRequest::default(); + let req = ExecutionRequest::default(); let err = validate_common(&req).unwrap_err(); assert!( err.error_message.contains("empty"), @@ -73,14 +73,14 @@ mod tests { #[test] fn validate_exec_common_rejects_empty_command_line() { - let req = CodexRequest::default(); + let req = ExecutionRequest::default(); let err = validate_exec_common(&req).unwrap_err(); assert_eq!(err.code, MxcErrorCode::MalformedRequest); } #[test] fn validate_exec_common_accepts_non_empty_command_line() { - let req = CodexRequest { + let req = ExecutionRequest { script_code: "echo hello".to_string(), ..Default::default() }; diff --git a/src/wxc_common/src/windows_sandbox_runner.rs b/src/wxc_common/src/windows_sandbox_runner.rs index 8caba7aba..155c26044 100644 --- a/src/wxc_common/src/windows_sandbox_runner.rs +++ b/src/wxc_common/src/windows_sandbox_runner.rs @@ -9,7 +9,7 @@ use std::net::TcpStream; use std::time::Duration; use crate::logger::Logger; -use crate::models::{CodexRequest, ScriptResponse, WindowsSandboxConfig}; +use crate::models::{ExecutionRequest, ScriptResponse, WindowsSandboxConfig}; use crate::process_util::resolve_sibling_binary; use crate::sandbox_protocol::DaemonResult; use crate::script_runner::ScriptRunner; @@ -115,7 +115,11 @@ impl WindowsSandboxScriptRunner { } /// Send an execution request to the daemon and read the result. - fn execute_via_daemon(&self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute_via_daemon( + &self, + request: &ExecutionRequest, + logger: &mut Logger, + ) -> ScriptResponse { // Pre-flight: verify Windows Sandbox is available. if let Err(err) = Self::check_sandbox_available() { return ScriptResponse::error(&err); @@ -187,7 +191,7 @@ impl WindowsSandboxScriptRunner { } impl ScriptRunner for WindowsSandboxScriptRunner { - fn execute(&mut self, request: &CodexRequest, logger: &mut Logger) -> ScriptResponse { + fn execute(&mut self, request: &ExecutionRequest, logger: &mut Logger) -> ScriptResponse { self.execute_via_daemon(request, logger) } }