From 7d8cb5db054508392f2b525e6a3e24d3a476c37e Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Tue, 18 Aug 2026 14:58:10 -0700 Subject: [PATCH 1/2] feat(sandbox): add main restart policy Signed-off-by: Drew Newberry --- .agents/skills/openshell-cli/SKILL.md | 7 + architecture/compute-runtimes.md | 7 + architecture/gateway.md | 7 +- architecture/sandbox.md | 12 +- crates/openshell-cli/src/commands/common.rs | 1 + crates/openshell-cli/src/main.rs | 55 + crates/openshell-cli/src/run.rs | 107 +- crates/openshell-driver-docker/src/tests.rs | 4 +- crates/openshell-driver-podman/README.md | 1 + .../openshell-driver-podman/src/container.rs | 9 + crates/openshell-sdk/src/client.rs | 7 + crates/openshell-sdk/src/lib.rs | 4 +- crates/openshell-sdk/src/types.rs | 42 +- crates/openshell-server/src/compute/mod.rs | 665 +++++++++- crates/openshell-server/src/grpc/sandbox.rs | 35 +- .../openshell-server/src/grpc/validation.rs | 18 + crates/openshell-tui/src/lib.rs | 2 + crates/openshell-tui/src/ui/sandbox_detail.rs | 4 +- crates/openshell-tui/src/ui/sandboxes.rs | 2 +- docs/sandboxes/manage-sandboxes.mdx | 36 +- e2e/rust/tests/sandbox_lifecycle.rs | 86 ++ proto/openshell.proto | 26 +- python/openshell/sandbox.py | 10 + python/openshell/sandbox_test.py | 6 + .../v1/internal/converter/coverage_test.go | 20 +- .../v1/internal/converter/sandbox.go | 41 +- .../v1/internal/converter/sandbox_test.go | 28 +- sdk/go/openshell/v1/types.go | 11 + sdk/go/openshell/v1/types/sandbox.go | 26 +- sdk/go/openshell/v1/types/types.go | 11 + sdk/go/proto/openshellv1/openshell.pb.go | 1182 +++++++++-------- sdk/typescript/src/client.test.ts | 52 +- sdk/typescript/src/client.ts | 42 +- sdk/typescript/src/index.ts | 1 + 34 files changed, 1943 insertions(+), 624 deletions(-) diff --git a/.agents/skills/openshell-cli/SKILL.md b/.agents/skills/openshell-cli/SKILL.md index 96a66e2cc6..dae567c1e4 100644 --- a/.agents/skills/openshell-cli/SKILL.md +++ b/.agents/skills/openshell-cli/SKILL.md @@ -233,6 +233,7 @@ Key flags: - `--label KEY=VALUE`: Add labels for later selection (repeatable) - `--env KEY=VALUE`: Set non-secret sandbox environment variables (repeatable); use `--provider` for credentials - `--tty`: Allocate a retained PTY for the canonical main process +- `--restart-policy never|on-failure|always`: Select gateway-owned main-process restart behavior; `never` is the default - `--approval-mode manual|auto`: Control handling of agent-authored policy proposals; `manual` is the default - `--upload [:]`: Upload local files into the container working directory or an explicit destination - `--no-git-ignore`: Disable `.gitignore` filtering for uploads @@ -273,6 +274,12 @@ VS Code Remote-SSH with: openshell sandbox ssh-config my-sandbox >> ~/.ssh/config ``` +When the main process exits under `on-failure` or `always`, the sandbox enters +`Restarting` while the gateway applies exponential backoff and recreates the +compute resource. Connect and exec commands are unavailable until the new +supervisor session makes it `Ready`. An explicit `sandbox stop` cancels a +pending restart. + ### Upload and download files ```bash diff --git a/architecture/compute-runtimes.md b/architecture/compute-runtimes.md index d487aba31a..b72da305c6 100644 --- a/architecture/compute-runtimes.md +++ b/architecture/compute-runtimes.md @@ -37,6 +37,13 @@ Canonical main-process support is part of the `ComputeDriver` contract. Every in-tree and extension driver must forward the exact specification; it is not an optional capability that drivers can omit or negotiate. +The gateway also owns canonical main-process restart policy. Drivers disable +native container, pod, or VM restart behavior and implement the existing stop +and start operations. When policy selects a restart, the gateway persists the +`Restarting` phase and backoff deadline, stops compute, starts it again after +the deadline, and waits for a new supervisor session before returning to +`Ready`. Driver snapshots cannot override that gateway-owned transition. + Drivers own runtime-specific platform event interpretation. When an event should drive client provisioning UI, the driver attaches the shared `openshell.progress.*` metadata defined in `openshell-core` instead of requiring diff --git a/architecture/gateway.md b/architecture/gateway.md index 32bca6a1f6..8da58662eb 100644 --- a/architecture/gateway.md +++ b/architecture/gateway.md @@ -15,9 +15,10 @@ workloads. - Resolve provider credentials and inference bundles for sandbox supervisors. - Coordinate supervisor relay sessions for connect, exec, file sync, and service forwarding. -- Persist the canonical main-process instance ID and normalized exit code on - sandbox status. Any main process exit transitions the sandbox to `Error`, - including exit code zero. +- Persist the canonical main-process instance ID, normalized exit code, restart + count, and restart deadline on sandbox status. Evaluate the sandbox restart + policy when that process exits and either transition to `Error` or restart + the sandbox compute resource with bounded exponential backoff. The gateway does not enforce agent network policy at request time. That happens inside each sandbox, where the supervisor and proxy can observe local process diff --git a/architecture/sandbox.md b/architecture/sandbox.md index 5d6397e2cc..96b184bd8f 100644 --- a/architecture/sandbox.md +++ b/architecture/sandbox.md @@ -412,7 +412,11 @@ engine with a gateway policy revision. re-evaluate. - If the supervisor relay drops, the sandbox can keep running, but connect and exec operations fail until the supervisor registers again. -- If the canonical main process exits, including with code 0, the supervisor - reports its normalized exit code before shutdown. The gateway persists the - code on sandbox status, records `MainProcessExited`, and makes the sandbox - terminal `Error`; runtime restart policies must not replace the process. +- If the canonical main process exits, the supervisor reports its normalized + exit code before shutdown. The gateway persists the result and evaluates the + sandbox's `Never`, `OnFailure`, or `Always` restart policy. A selected restart + moves the sandbox to `Restarting` and recreates its compute resource after + bounded exponential backoff; otherwise the sandbox becomes terminal `Error`. +- Compute runtimes keep their native restart mechanisms disabled. This gives + the gateway one durable restart counter, deadline, and policy decision across + Docker, Podman, Kubernetes, and VM drivers. diff --git a/crates/openshell-cli/src/commands/common.rs b/crates/openshell-cli/src/commands/common.rs index 75dc260ba7..fd92aa54af 100644 --- a/crates/openshell-cli/src/commands/common.rs +++ b/crates/openshell-cli/src/commands/common.rs @@ -65,6 +65,7 @@ pub fn phase_name(phase: i32) -> &'static str { Ok(SandboxPhase::Stopping) => "Stopping", Ok(SandboxPhase::Stopped) => "Stopped", Ok(SandboxPhase::Starting) => "Starting", + Ok(SandboxPhase::Restarting) => "Restarting", Ok(SandboxPhase::Unknown) | Err(_) => "Unknown", } } diff --git a/crates/openshell-cli/src/main.rs b/crates/openshell-cli/src/main.rs index de057b5f82..615d458c62 100644 --- a/crates/openshell-cli/src/main.rs +++ b/crates/openshell-cli/src/main.rs @@ -1430,6 +1430,14 @@ enum SandboxCommands { #[arg(long, conflicts_with_all = ["editor", "no_keep"])] detach: bool, + /// Restart behavior after the canonical main process exits. + #[arg( + long, + value_parser = ["never", "on-failure", "always"], + default_value = "never" + )] + restart_policy: String, + /// Auto-create missing providers from local credentials. /// /// Without this flag, an interactive prompt asks per-provider; @@ -2991,6 +2999,7 @@ async fn run_async() -> Result<()> { tty, no_tty, detach, + restart_policy, auto_providers, no_auto_providers, labels, @@ -3087,6 +3096,7 @@ async fn run_async() -> Result<()> { approval_mode: &approval_mode, output: output.as_str(), detach, + restart_policy: &restart_policy, }, &cli.workspace, &tls, @@ -5140,6 +5150,51 @@ mod tests { } } + #[test] + fn sandbox_create_restart_policy_defaults_to_never() { + let cli = Cli::try_parse_from(["openshell", "sandbox", "create"]).unwrap(); + match cli.command { + Some(Commands::Sandbox { + command: Some(SandboxCommands::Create { restart_policy, .. }), + .. + }) => assert_eq!(restart_policy, "never"), + other => panic!("expected SandboxCommands::Create, got: {other:?}"), + } + } + + #[test] + fn sandbox_create_restart_policy_accepts_on_failure() { + let cli = Cli::try_parse_from([ + "openshell", + "sandbox", + "create", + "--restart-policy", + "on-failure", + ]) + .unwrap(); + match cli.command { + Some(Commands::Sandbox { + command: Some(SandboxCommands::Create { restart_policy, .. }), + .. + }) => assert_eq!(restart_policy, "on-failure"), + other => panic!("expected SandboxCommands::Create, got: {other:?}"), + } + } + + #[test] + fn sandbox_create_restart_policy_rejects_unknown_value() { + assert!( + Cli::try_parse_from([ + "openshell", + "sandbox", + "create", + "--restart-policy", + "unless-stopped", + ]) + .is_err() + ); + } + #[test] fn sandbox_create_detach_parses_with_main_command() { let cli = Cli::try_parse_from([ diff --git a/crates/openshell-cli/src/run.rs b/crates/openshell-cli/src/run.rs index fd0e585068..0ebd627311 100644 --- a/crates/openshell-cli/src/run.rs +++ b/crates/openshell-cli/src/run.rs @@ -50,10 +50,11 @@ use openshell_core::proto::{ ProviderCredentialRefreshStrategy, ProviderProfile, ProviderProfileDiagnostic, ProviderProfileImportItem, RejectDraftChunkRequest, ResourceRequirements, RevokeSshSessionRequest, RotateProviderCredentialRequest, Sandbox, SandboxPhase, SandboxPolicy, - SandboxSpec, SandboxTemplate, ServiceEndpointResponse, SetInferenceRouteRequest, SettingScope, - StartSandboxRequest, StopSandboxRequest, TcpForwardFrame, TcpForwardInit, TcpRelayTarget, - UpdateConfigRequest, UpdateProviderProfilesRequest, UpdateProviderRequest, WatchSandboxRequest, - exec_sandbox_event, setting_value, tcp_forward_init, + SandboxRestartPolicy, SandboxSpec, SandboxTemplate, ServiceEndpointResponse, + SetInferenceRouteRequest, SettingScope, StartSandboxRequest, StopSandboxRequest, + TcpForwardFrame, TcpForwardInit, TcpRelayTarget, UpdateConfigRequest, + UpdateProviderProfilesRequest, UpdateProviderRequest, WatchSandboxRequest, exec_sandbox_event, + setting_value, tcp_forward_init, }; use openshell_core::settings; use openshell_core::{ObjectId, ObjectName, ObjectWorkspace}; @@ -384,6 +385,7 @@ pub struct SandboxCreateConfig<'a> { pub approval_mode: &'a str, pub output: &'a str, pub detach: bool, + pub restart_policy: &'a str, } impl Default for SandboxCreateConfig<'_> { @@ -409,6 +411,7 @@ impl Default for SandboxCreateConfig<'_> { approval_mode: "manual", output: "table", detach: false, + restart_policy: "never", } } } @@ -442,6 +445,7 @@ pub async fn sandbox_create( approval_mode, output, detach, + restart_policy, } = config; if editor.is_some() && !command.is_empty() { @@ -545,6 +549,12 @@ pub async fn sandbox_create( template, command: main_command, tty: main_terminal, + restart_policy: match restart_policy { + "never" => SandboxRestartPolicy::Never as i32, + "on-failure" => SandboxRestartPolicy::OnFailure as i32, + "always" => SandboxRestartPolicy::Always as i32, + value => return Err(miette::miette!("invalid restart policy '{value}'")), + }, ..SandboxSpec::default() }), name: name.unwrap_or_default().to_string(), @@ -1329,6 +1339,38 @@ pub async fn sandbox_get( "Resource version:".dimmed(), sandbox.metadata.as_ref().map_or(0, |m| m.resource_version) ); + println!( + " {} {}", + "Restart policy:".dimmed(), + sandbox + .spec + .as_ref() + .map_or("never", |spec| { restart_policy_name(spec.restart_policy) }) + ); + if let Some(status) = sandbox.status.as_ref() { + println!( + " {} {}", + "Main process instance:".dimmed(), + if status.main_process_instance_id.is_empty() { + "-" + } else { + &status.main_process_instance_id + } + ); + println!( + " {} {}", + "Last exit code:".dimmed(), + status + .exit_code + .map_or_else(|| "-".to_string(), |code| code.to_string()) + ); + println!(" {} {}", "Restart count:".dimmed(), status.restart_count); + println!( + " {} {}", + "Next restart:".dimmed(), + format_optional_epoch_ms(status.next_restart_at_ms) + ); + } // Display labels if present if let Some(metadata) = &sandbox.metadata @@ -2105,6 +2147,14 @@ fn sandbox_to_json(sandbox: &Sandbox) -> serde_json::Value { }) } +fn restart_policy_name(policy: i32) -> &'static str { + match SandboxRestartPolicy::try_from(policy) { + Ok(SandboxRestartPolicy::OnFailure) => "on-failure", + Ok(SandboxRestartPolicy::Always) => "always", + Ok(SandboxRestartPolicy::Unspecified | SandboxRestartPolicy::Never) | Err(_) => "never", + } +} + fn sandbox_detail_to_json( sandbox: &Sandbox, config: &GetSandboxConfigResponse, @@ -2114,6 +2164,31 @@ fn sandbox_detail_to_json( .as_object_mut() .expect("sandbox_to_json returns object"); + let restart_policy = sandbox + .spec + .as_ref() + .map_or("never", |spec| restart_policy_name(spec.restart_policy)); + obj.insert("restart_policy".into(), serde_json::json!(restart_policy)); + if let Some(status) = sandbox.status.as_ref() { + obj.insert( + "main_process_instance_id".into(), + serde_json::json!(status.main_process_instance_id), + ); + obj.insert("exit_code".into(), serde_json::json!(status.exit_code)); + obj.insert( + "restart_count".into(), + serde_json::json!(status.restart_count), + ); + obj.insert( + "next_restart_at_ms".into(), + serde_json::json!(status.next_restart_at_ms), + ); + obj.insert( + "main_process_started_at_ms".into(), + serde_json::json!(status.main_process_started_at_ms), + ); + } + let policy_source = if config.policy_source == PolicySource::Global as i32 { "global" } else { @@ -7150,7 +7225,8 @@ mod tests { ProviderCredentialRefresh, ProviderCredentialRefreshStatus, ProviderCredentialRefreshStrategy, ProviderCredentialTokenGrant, ProviderProfile, ProviderProfileCredential, ResourceRequirements, Sandbox, SandboxCondition, SandboxPhase, - SandboxPolicyRevision, SandboxStatus, datamodel::v1::ObjectMeta, + SandboxPolicyRevision, SandboxRestartPolicy, SandboxSpec, SandboxStatus, + datamodel::v1::ObjectMeta, }; #[test] @@ -8495,6 +8571,19 @@ mod tests { }; sandbox.set_phase(SandboxPhase::Ready as i32); sandbox.set_current_policy_version(2); + sandbox.spec = Some(SandboxSpec { + restart_policy: SandboxRestartPolicy::OnFailure as i32, + ..Default::default() + }); + sandbox.status = Some(SandboxStatus { + phase: SandboxPhase::Restarting as i32, + main_process_instance_id: "main-2".to_string(), + exit_code: Some(9), + restart_count: 2, + next_restart_at_ms: 1_700_000_000_000, + main_process_started_at_ms: 1_699_999_000_000, + ..Default::default() + }); let config = GetSandboxConfigResponse { policy_source: PolicySource::Global as i32, @@ -8506,7 +8595,13 @@ mod tests { assert_eq!(json["id"], "sb-123"); assert_eq!(json["name"], "test-sb"); - assert_eq!(json["phase"], "Ready"); + assert_eq!(json["phase"], "Restarting"); + assert_eq!(json["restart_policy"], "on-failure"); + assert_eq!(json["main_process_instance_id"], "main-2"); + assert_eq!(json["exit_code"], 9); + assert_eq!(json["restart_count"], 2); + assert_eq!(json["next_restart_at_ms"], 1_700_000_000_000_i64); + assert_eq!(json["main_process_started_at_ms"], 1_699_999_000_000_i64); assert_eq!(json["policy_source"], "global"); assert_eq!(json["revision"], 3); assert!(json["policy"].is_null()); diff --git a/crates/openshell-driver-docker/src/tests.rs b/crates/openshell-driver-docker/src/tests.rs index 00b96baa76..30690be210 100644 --- a/crates/openshell-driver-docker/src/tests.rs +++ b/crates/openshell-driver-docker/src/tests.rs @@ -45,8 +45,8 @@ fn test_sandbox() -> DriverSandbox { }), resource_requirements: None, sandbox_token: String::new(), - command: Vec::new(), - tty: false, + command: vec!["/bin/bash".to_string(), "-l".to_string()], + tty: true, }), status: None, workspace: String::new(), diff --git a/crates/openshell-driver-podman/README.md b/crates/openshell-driver-podman/README.md index cbb782db67..005239d27f 100644 --- a/crates/openshell-driver-podman/README.md +++ b/crates/openshell-driver-podman/README.md @@ -73,6 +73,7 @@ The container spec in `container.rs` sets these security-critical fields: | `cap_drop` | Selected unneeded defaults | Podman's default capability set is already restricted. The driver drops capabilities the supervisor does not need. | | `cap_add` | `SYS_ADMIN`, `NET_ADMIN`, `SYS_PTRACE`, `SYSLOG`, `DAC_READ_SEARCH`, `SETPCAP` | Grants supervisor-only capabilities required for namespace setup, process identity, bypass diagnostics, and child bounding-set cleanup. | | `no_new_privileges` | `true` | Prevents privilege escalation after exec. | +| `restart_policy` | `no` | Keeps the gateway authoritative for canonical-main restart decisions. | | `seccomp_profile_path` | `unconfined` | The supervisor installs its own policy-aware BPF filter. A container-level profile can block Landlock/seccomp syscalls during setup. | | `mounts` | Private tmpfs at `/run/netns` | Lets the supervisor create named network namespaces in rootless Podman. | | CDI GPU devices | Opaque `driver_config.cdi_devices` values when set, otherwise the requested count of NVIDIA CDI GPUs selected in round-robin order. Local `/dev/dxg` permits `nvidia.com/gpu=all` as a WSL2 all-only compatibility fallback, where it counts as one selectable device. | Exposes requested GPUs to GPU-enabled sandbox containers. Exact CDI device lists must not contain duplicates and must match the effective GPU count. | diff --git a/crates/openshell-driver-podman/src/container.rs b/crates/openshell-driver-podman/src/container.rs index ea22a757b1..c8f671007b 100644 --- a/crates/openshell-driver-podman/src/container.rs +++ b/crates/openshell-driver-podman/src/container.rs @@ -224,6 +224,8 @@ struct ContainerSpec { /// File-mounted Podman secrets. secrets: Vec, stop_timeout: u32, + /// Native restart stays disabled; the gateway owns sandbox restart policy. + restart_policy: String, /// Extra /etc/hosts entries. Used to inject `host.containers.internal` /// via Podman's `host-gateway` magic so sandbox containers can reach /// the gateway server running on the host in rootless mode. @@ -1215,6 +1217,7 @@ pub fn build_container_spec_for_image( secrets }, stop_timeout: config.stop_timeout_secs, + restart_policy: "no".to_string(), // Inject stable host aliases into /etc/hosts so sandbox containers can // reach services on the host. `host.openshell.internal` is the driver- // neutral alias used by policies and e2e tests. @@ -2700,6 +2703,12 @@ mod tests { config.host_gateway_ip = "192.168.127.254".to_string(); let spec = build_container_spec(&sandbox, &config); + assert_eq!( + spec["restart_policy"].as_str(), + Some("no"), + "the gateway owns sandbox restart policy" + ); + let hostadd: Vec<&str> = spec["hostadd"] .as_array() .expect("hostadd should be an array") diff --git a/crates/openshell-sdk/src/client.rs b/crates/openshell-sdk/src/client.rs index f95b7ee111..7ee73b84e2 100644 --- a/crates/openshell-sdk/src/client.rs +++ b/crates/openshell-sdk/src/client.rs @@ -801,6 +801,7 @@ fn create_sandbox_request(spec: SandboxSpec) -> proto::CreateSandboxRequest { gpu, command, tty, + restart_policy, } = spec; let template = image.map(|image| proto::SandboxTemplate { image, @@ -817,6 +818,7 @@ fn create_sandbox_request(spec: SandboxSpec) -> proto::CreateSandboxRequest { resource_requirements, command, tty, + restart_policy: proto::SandboxRestartPolicy::from(restart_policy) as i32, ..proto::SandboxSpec::default() }), name: name.unwrap_or_default(), @@ -1002,11 +1004,16 @@ mod tests { let request = create_sandbox_request(SandboxSpec { command: vec!["/opt/agent binary".into(), "--serve exactly".into()], tty: false, + restart_policy: crate::types::SandboxRestartPolicy::OnFailure, ..SandboxSpec::default() }); let spec = request.spec.expect("sandbox spec should be present"); assert_eq!(spec.command, ["/opt/agent binary", "--serve exactly"]); assert!(!spec.tty); + assert_eq!( + spec.restart_policy(), + proto::SandboxRestartPolicy::OnFailure + ); } } diff --git a/crates/openshell-sdk/src/lib.rs b/crates/openshell-sdk/src/lib.rs index dbf2524a2a..db93bd45fe 100644 --- a/crates/openshell-sdk/src/lib.rs +++ b/crates/openshell-sdk/src/lib.rs @@ -46,6 +46,6 @@ pub use config::{AuthConfig, ClientConfig}; pub use error::SdkError; pub use refresh::{Refresh, RefreshError, RefreshedToken, TokenSource}; pub use types::{ - ExecOptions, ExecResult, Health, ListOptions, SandboxPhase, SandboxRef, SandboxSpec, - ServiceStatus, WorkspaceRef, + ExecOptions, ExecResult, Health, ListOptions, SandboxPhase, SandboxRef, SandboxRestartPolicy, + SandboxSpec, ServiceStatus, WorkspaceRef, }; diff --git a/crates/openshell-sdk/src/types.rs b/crates/openshell-sdk/src/types.rs index 3715c7fdd6..04eca31cfc 100644 --- a/crates/openshell-sdk/src/types.rs +++ b/crates/openshell-sdk/src/types.rs @@ -65,6 +65,7 @@ pub enum SandboxPhase { Stopping, Stopped, Starting, + Restarting, } impl From for SandboxPhase { @@ -79,6 +80,26 @@ impl From for SandboxPhase { proto::SandboxPhase::Stopping => Self::Stopping, proto::SandboxPhase::Stopped => Self::Stopped, proto::SandboxPhase::Starting => Self::Starting, + proto::SandboxPhase::Restarting => Self::Restarting, + } + } +} + +/// Policy applied when the canonical main process exits unexpectedly. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub enum SandboxRestartPolicy { + #[default] + Never, + OnFailure, + Always, +} + +impl From for proto::SandboxRestartPolicy { + fn from(value: SandboxRestartPolicy) -> Self { + match value { + SandboxRestartPolicy::Never => Self::Never, + SandboxRestartPolicy::OnFailure => Self::OnFailure, + SandboxRestartPolicy::Always => Self::Always, } } } @@ -113,6 +134,8 @@ pub struct SandboxSpec { pub command: Vec, /// Allocate a retained pseudo-terminal for the canonical command. pub tty: bool, + /// Restart behavior after the canonical process exits. + pub restart_policy: SandboxRestartPolicy, } /// Reference to a sandbox owned by the gateway. @@ -126,12 +149,26 @@ pub struct SandboxRef { pub labels: HashMap, pub resource_version: u64, pub exit_code: Option, + pub restart_count: u32, + pub next_restart_at_ms: Option, + pub main_process_started_at_ms: Option, } impl SandboxRef { pub(crate) fn from_proto(sandbox: proto::Sandbox) -> Self { let phase = sandbox.phase().into(); - let exit_code = sandbox.status.as_ref().and_then(|status| status.exit_code); + let (exit_code, restart_count, next_restart_at_ms, main_process_started_at_ms) = sandbox + .status + .as_ref() + .map_or((None, 0, None, None), |status| { + ( + status.exit_code, + status.restart_count, + (status.next_restart_at_ms > 0).then_some(status.next_restart_at_ms), + (status.main_process_started_at_ms > 0) + .then_some(status.main_process_started_at_ms), + ) + }); let meta = sandbox.metadata.unwrap_or_default(); Self { id: meta.id, @@ -141,6 +178,9 @@ impl SandboxRef { labels: meta.labels, resource_version: meta.resource_version, exit_code, + restart_count, + next_restart_at_ms, + main_process_started_at_ms, } } } diff --git a/crates/openshell-server/src/compute/mod.rs b/crates/openshell-server/src/compute/mod.rs index b19600a588..354a69fcea 100644 --- a/crates/openshell-server/src/compute/mod.rs +++ b/crates/openshell-server/src/compute/mod.rs @@ -45,8 +45,8 @@ use openshell_core::proto::compute::v1::{ gateway_listener_requirement::Selector, watch_sandboxes_event, }; use openshell_core::proto::{ - PlatformEvent, Sandbox, SandboxCondition, SandboxPhase, SandboxSpec, SandboxStatus, - SandboxTemplate, ServiceEndpoint, SshSession, + PlatformEvent, Sandbox, SandboxCondition, SandboxPhase, SandboxRestartPolicy, SandboxSpec, + SandboxStatus, SandboxTemplate, ServiceEndpoint, SshSession, }; use openshell_core::{ObjectLabels, ObjectWorkspace}; #[cfg(not(target_os = "windows"))] @@ -284,6 +284,15 @@ pub struct ComputeDriverInfoSnapshot { /// Interval between store-vs-backend reconciliation sweeps. const RECONCILE_INTERVAL: Duration = Duration::from_secs(60); +/// Restart intents live in sandbox rows. A lightweight holder-only scan makes +/// cross-replica writes and lost wakeups recover without changing the general +/// backend reconciliation cadence. +const RESTART_SCAN_INTERVAL: Duration = Duration::from_secs(1); +const RESTART_INITIAL_DELAY_MS: i64 = 10_000; +const RESTART_MAX_DELAY_MS: i64 = 300_000; +const RESTART_STABILITY_WINDOW_MS: i64 = 600_000; +const RESTART_READINESS_TIMEOUT_MS: i64 = 120_000; + /// How long a sandbox can remain provisioning in the store without a /// corresponding backend resource before it is considered orphaned. const ORPHAN_GRACE_PERIOD: Duration = Duration::from_secs(300); @@ -1028,9 +1037,12 @@ impl ComputeRuntime { .map_err(Status::internal)?; return Ok(current); } - if !matches!(phase, SandboxPhase::Ready | SandboxPhase::Stopping) { + if !matches!( + phase, + SandboxPhase::Ready | SandboxPhase::Stopping | SandboxPhase::Restarting + ) { return Err(Status::failed_precondition(format!( - "sandbox must be Ready to stop (current phase: {phase:?})" + "sandbox must be Ready or Restarting to stop (current phase: {phase:?})" ))); } @@ -1394,6 +1406,9 @@ impl ComputeRuntime { let status = sandbox.status.get_or_insert_with(Default::default); status.main_process_instance_id.clear(); status.exit_code = None; + status.restart_count = 0; + status.next_restart_at_ms = 0; + status.main_process_started_at_ms = 0; } upsert_ready_condition( &mut sandbox.status, @@ -2013,8 +2028,13 @@ impl ComputeRuntime { tokio::spawn(async move { watch_runtime.watch_loop(watch_shutdown).await; }); + let reconcile_runtime = runtime.clone(); + let reconcile_shutdown = shutdown_rx.clone(); tokio::spawn(async move { - runtime.reconcile_loop(shutdown_rx).await; + reconcile_runtime.reconcile_loop(reconcile_shutdown).await; + }); + tokio::spawn(async move { + runtime.restart_loop(shutdown_rx).await; }); } else { tokio::spawn(async move { @@ -2143,7 +2163,10 @@ impl ComputeRuntime { /// `StartSandbox` is idempotent, so call it for every persisted phase that /// requires running compute for drivers that request gateway-managed /// lifecycle. Stable stopped, deleting, and error states are deliberately - /// left alone. + /// left alone. The restart controller exclusively owns `Restarting` + /// sandboxes so startup cannot bypass their persisted backoff deadline. A + /// missing resource or start failure moves the sandbox to `Error` so the + /// failure is visible. /// /// Should be called once at gateway startup, before watchers spawn, /// so the watch loop sees the post-start state on its first poll. @@ -2472,8 +2495,14 @@ impl ComputeRuntime { }); let runtime = self.clone(); + let reconcile_cancel = cancel_rx.clone(); let reconcile_handle = tokio::spawn(async move { - runtime.reconcile_loop(cancel_rx).await; + runtime.reconcile_loop(reconcile_cancel).await; + }); + + let runtime = self.clone(); + let restart_handle = tokio::spawn(async move { + runtime.restart_loop(cancel_rx).await; }); loop { @@ -2502,6 +2531,7 @@ impl ComputeRuntime { let _ = cancel_tx.send(true); let _ = watch_handle.await; let _ = reconcile_handle.await; + let _ = restart_handle.await; return; } } @@ -2511,6 +2541,7 @@ impl ComputeRuntime { let _ = cancel_tx.send(true); let _ = watch_handle.await; let _ = reconcile_handle.await; + let _ = restart_handle.await; info!(replica = %lease.replica_id(), "reconciler lease lost — returning to standby"); } @@ -2582,6 +2613,226 @@ impl ComputeRuntime { } } + async fn restart_loop(self: Arc, mut cancel: watch::Receiver) { + loop { + if let Err(err) = self.restart_due_sandboxes().await { + warn!(error = %err, "Sandbox restart sweep failed"); + } + tokio::select! { + () = tokio::time::sleep(RESTART_SCAN_INTERVAL) => {} + _ = cancel.changed() => return, + } + } + } + + async fn restart_due_sandboxes(&self) -> Result<(), String> { + let now_ms = openshell_core::time::now_ms(); + let mut offset = 0; + loop { + let records = self + .store + .list_by_type(Sandbox::object_type(), 1000, offset) + .await + .map_err(|err| err.to_string())?; + let page_len = records.len(); + for record in records { + let sandbox = match Sandbox::decode(record.payload.as_slice()) { + Ok(sandbox) => sandbox, + Err(err) => { + warn!(error = %err, "Failed to decode sandbox during restart sweep"); + continue; + } + }; + let phase = + SandboxPhase::try_from(sandbox.phase()).unwrap_or(SandboxPhase::Unknown); + let due = sandbox.status.as_ref().is_some_and(|status| { + status.next_restart_at_ms > 0 && status.next_restart_at_ms <= now_ms + }); + if phase == SandboxPhase::Restarting + && due + && let Err(err) = self.restart_sandbox_runtime(sandbox.object_id()).await + { + warn!( + sandbox_id = %sandbox.object_id(), + sandbox_name = %sandbox.object_name(), + error = %err, + "Automatic sandbox restart attempt failed" + ); + } + } + if page_len < 1000 { + break; + } + offset += u32::try_from(page_len).expect("restart scan page length is capped at 1000"); + } + Ok(()) + } + + async fn restart_sandbox_runtime(&self, sandbox_id: &str) -> Result<(), String> { + let lifecycle_guard = self.lifecycle_gates.lock_for(sandbox_id).await; + let global_guard = self.lock_global_for_lifecycle(&lifecycle_guard).await; + let Some(current) = self + .store + .get_message::(sandbox_id) + .await + .map_err(|err| err.to_string())? + else { + return Ok(()); + }; + let phase = SandboxPhase::try_from(current.phase()).unwrap_or(SandboxPhase::Unknown); + let now_ms = openshell_core::time::now_ms(); + let due = current.status.as_ref().is_some_and(|status| { + status.next_restart_at_ms > 0 && status.next_restart_at_ms <= now_ms + }); + if phase != SandboxPhase::Restarting || !due { + return Ok(()); + } + + let sandbox_name = current.object_name().to_string(); + let claimed = self + .store + .update_message_cas::( + sandbox_id, + sandbox_resource_version(¤t), + |sandbox| { + let name = sandbox.object_name().to_string(); + let status = sandbox.status.get_or_insert_with(Default::default); + // This deadline is a durable readiness watchdog. A fresh + // supervisor clears it when the replacement becomes Ready. + status.next_restart_at_ms = now_ms.saturating_add(RESTART_READINESS_TIMEOUT_MS); + upsert_ready_condition( + &mut sandbox.status, + &name, + SandboxCondition { + r#type: "Ready".to_string(), + status: "False".to_string(), + reason: "SandboxRestarting".to_string(), + message: "Replacing sandbox runtime after canonical main process exit" + .to_string(), + last_transition_time: String::new(), + }, + ); + }, + ) + .await + .map_err(|err| err.to_string())?; + self.sandbox_index.update_from_sandbox(&claimed); + self.sandbox_watch_bus.notify(sandbox_id); + drop(global_guard); + + let stop_result = self + .driver + .call("driver.stop_sandbox", Some(sandbox_id), |driver| { + let sandbox_id = sandbox_id.to_string(); + let sandbox_name = sandbox_name.clone(); + async move { + driver + .stop_sandbox(Request::new(StopSandboxRequest { + sandbox_id, + sandbox_name, + })) + .await + } + }) + .await; + if let Err(status) = stop_result { + return self + .record_restart_driver_failure(&lifecycle_guard, sandbox_id, "stop", status) + .await; + } + + self.cleanup_stopped_sandbox_sessions(&claimed).await?; + + let start_result = self + .driver + .call("driver.start_sandbox", Some(sandbox_id), |driver| { + let sandbox_id = sandbox_id.to_string(); + let sandbox_name = sandbox_name.clone(); + async move { + driver + .start_sandbox(Request::new(StartSandboxRequest { + sandbox_id, + sandbox_name, + })) + .await + } + }) + .await; + if let Err(status) = start_result { + return self + .record_restart_driver_failure(&lifecycle_guard, sandbox_id, "start", status) + .await; + } + + info!( + sandbox_id, + sandbox_name, "Sandbox runtime restarted; waiting for replacement supervisor" + ); + Ok(()) + } + + async fn record_restart_driver_failure( + &self, + lifecycle_guard: &SandboxLifecycleGuard, + sandbox_id: &str, + operation: &str, + driver_status: Status, + ) -> Result<(), String> { + let _global_guard = self.lock_global_for_lifecycle(lifecycle_guard).await; + let Some(current) = self + .store + .get_message::(sandbox_id) + .await + .map_err(|err| err.to_string())? + else { + return Ok(()); + }; + if SandboxPhase::try_from(current.phase()) != Ok(SandboxPhase::Restarting) { + return Ok(()); + } + let terminal = driver_status.code() == Code::NotFound; + let operation = operation.to_string(); + let driver_message = driver_status.message().to_string(); + let now_ms = openshell_core::time::now_ms(); + let updated = self + .store + .update_message_cas::( + sandbox_id, + sandbox_resource_version(¤t), + |sandbox| { + let name = sandbox.object_name().to_string(); + let status = sandbox.status.get_or_insert_with(Default::default); + if terminal { + status.next_restart_at_ms = 0; + sandbox.set_phase(SandboxPhase::Error as i32); + } else { + status.next_restart_at_ms = + now_ms.saturating_add(restart_delay_ms(status.restart_count.max(1))); + } + upsert_ready_condition( + &mut sandbox.status, + &name, + SandboxCondition { + r#type: "Ready".to_string(), + status: "False".to_string(), + reason: "SandboxRestartFailed".to_string(), + message: format!( + "Sandbox restart {operation} failed: {driver_message}" + ), + last_transition_time: String::new(), + }, + ); + }, + ) + .await + .map_err(|err| err.to_string())?; + self.sandbox_index.update_from_sandbox(&updated); + self.sandbox_watch_bus.notify(sandbox_id); + Err(format!( + "driver {operation} failed during sandbox restart: {driver_status}" + )) + } + #[tracing::instrument( name = "reconcile", skip_all, @@ -2838,6 +3089,18 @@ impl ComputeRuntime { if !connected && current_phase != SandboxPhase::Ready { return Ok(()); } + if connected + && current_phase == SandboxPhase::Restarting + && existing.status.as_ref().is_some_and(|status| { + !status.main_process_instance_id.is_empty() + && Some(status.main_process_instance_id.as_str()) == instance_id + }) + { + return Err(format!( + "restarting sandbox requires a fresh supervisor instance (rejected '{instance_id}')", + instance_id = instance_id.unwrap_or_default() + )); + } let expected_resource_version = sandbox_resource_version(&existing); // Use CAS to update sandbox phase based on supervisor session state @@ -2850,6 +3113,8 @@ impl ComputeRuntime { let status = sandbox.status.get_or_insert_with(Default::default); status.main_process_instance_id = instance_id.unwrap_or_default().to_string(); status.exit_code = None; + status.next_restart_at_ms = 0; + status.main_process_started_at_ms = openshell_core::time::now_ms(); sandbox.set_phase(SandboxPhase::Ready as i32); } else { ensure_supervisor_not_ready_status(&mut sandbox.status, &sandbox_name); @@ -2932,11 +3197,20 @@ impl ComputeRuntime { return Ok(()); } } + let restart = existing + .spec + .as_ref() + .is_some_and(|spec| should_restart_main_process(spec.restart_policy, exit_code)); + let now_ms = openshell_core::time::now_ms(); let expected_resource_version = sandbox_resource_version(&existing); let sandbox = self .store .update_message_cas::(sandbox_id, expected_resource_version, |sandbox| { - apply_main_process_exit(sandbox, instance_id, exit_code); + if restart { + apply_main_process_restart(sandbox, instance_id, exit_code, now_ms); + } else { + apply_main_process_exit(sandbox, instance_id, exit_code); + } }) .await .map_err(|error| error.to_string())?; @@ -3214,7 +3488,10 @@ impl ComputeRuntime { let phase = SandboxPhase::try_from(sandbox.phase()).unwrap_or(SandboxPhase::Unknown); if matches!( phase, - SandboxPhase::Stopping | SandboxPhase::Stopped | SandboxPhase::Starting + SandboxPhase::Stopping + | SandboxPhase::Stopped + | SandboxPhase::Starting + | SandboxPhase::Restarting ) { let updated = self .store @@ -3307,6 +3584,8 @@ fn apply_main_process_exit(sandbox: &mut Sandbox, instance_id: &str, exit_code: }); status.main_process_instance_id = instance_id.to_string(); status.exit_code = Some(exit_code); + status.next_restart_at_ms = 0; + status.main_process_started_at_ms = 0; upsert_ready_condition( &mut sandbox.status, &sandbox_name, @@ -3321,6 +3600,63 @@ fn apply_main_process_exit(sandbox: &mut Sandbox, instance_id: &str, exit_code: sandbox.set_phase(SandboxPhase::Error as i32); } +fn should_restart_main_process(policy: i32, exit_code: i32) -> bool { + match SandboxRestartPolicy::try_from(policy).unwrap_or(SandboxRestartPolicy::Never) { + SandboxRestartPolicy::Always => true, + SandboxRestartPolicy::OnFailure => exit_code != 0, + SandboxRestartPolicy::Unspecified | SandboxRestartPolicy::Never => false, + } +} + +fn restart_delay_ms(restart_count: u32) -> i64 { + let shift = restart_count.saturating_sub(1).min(31); + RESTART_INITIAL_DELAY_MS + .saturating_mul(1_i64.checked_shl(shift).unwrap_or(i64::MAX)) + .min(RESTART_MAX_DELAY_MS) +} + +fn apply_main_process_restart( + sandbox: &mut Sandbox, + instance_id: &str, + exit_code: i32, + now_ms: i64, +) { + let sandbox_name = sandbox.object_name().to_string(); + let status = sandbox.status.get_or_insert_with(|| SandboxStatus { + sandbox_name: sandbox_name.clone(), + ..Default::default() + }); + let stable_run = status.main_process_started_at_ms > 0 + && now_ms.saturating_sub(status.main_process_started_at_ms) >= RESTART_STABILITY_WINDOW_MS; + status.restart_count = if stable_run || status.restart_count == 0 { + 1 + } else { + status.restart_count.saturating_add(1) + }; + let delay_ms = restart_delay_ms(status.restart_count); + status.main_process_instance_id = instance_id.to_string(); + status.exit_code = Some(exit_code); + status.main_process_started_at_ms = 0; + status.next_restart_at_ms = now_ms.saturating_add(delay_ms); + let restart_count = status.restart_count; + upsert_ready_condition( + &mut sandbox.status, + &sandbox_name, + SandboxCondition { + r#type: "Ready".to_string(), + status: "False".to_string(), + reason: "MainProcessRestartBackoff".to_string(), + message: format!( + "Canonical main process exited; restart {} scheduled in {} seconds", + restart_count, + delay_ms / 1000 + ), + last_transition_time: String::new(), + }, + ); + sandbox.set_phase(SandboxPhase::Restarting as i32); +} + /// Connect to an unmanaged remote compute driver that is already listening on /// `socket_path` and return the acquired endpoint. /// @@ -3674,6 +4010,9 @@ fn public_status_from_driver( current_policy_version, main_process_instance_id: String::new(), exit_code: None, + restart_count: 0, + next_restart_at_ms: 0, + main_process_started_at_ms: 0, } } @@ -3720,6 +4059,18 @@ fn apply_driver_snapshot(sandbox: &mut Sandbox, incoming: &DriverSandbox, sessio }, ); + // Driver status has no canonical-main fields. Preserve the gateway-owned + // process generation and restart controller state across every snapshot. + if let (Some(old_status), Some(new_status)) = (sandbox.status.as_ref(), status.as_mut()) { + new_status + .main_process_instance_id + .clone_from(&old_status.main_process_instance_id); + new_status.exit_code = old_status.exit_code; + new_status.restart_count = old_status.restart_count; + new_status.next_restart_at_ms = old_status.next_restart_at_ms; + new_status.main_process_started_at_ms = old_status.main_process_started_at_ms; + } + phase = match old_phase { SandboxPhase::Stopping if phase == SandboxPhase::Stopped || driver_snapshot_confirms_stopped(incoming) => @@ -3734,6 +4085,7 @@ fn apply_driver_snapshot(sandbox: &mut Sandbox, incoming: &DriverSandbox, sessio SandboxPhase::Starting if !matches!(phase, SandboxPhase::Ready | SandboxPhase::Error) => { SandboxPhase::Starting } + SandboxPhase::Restarting => SandboxPhase::Restarting, _ => phase, }; @@ -4000,9 +4352,9 @@ fn rewrite_user_facing_conditions(status: &mut Option, spec: Opti } /// Phases for which a sandbox should have a running compute resource. -/// `Deleting` and `Error` are intentionally excluded: deletion is in -/// progress, or the sandbox has already failed and should not be -/// silently revived. `Unspecified` is included because it is the proto +/// Terminal and lifecycle-controller-owned phases are intentionally excluded: +/// they must not be silently revived or bypass a persisted transition/backoff. +/// `Unspecified` is included because it is the proto /// default value; persisted rows with that value should be reconciled /// from the live driver state rather than skipped forever. fn sandbox_phase_should_be_running(phase: SandboxPhase) -> bool { @@ -4959,6 +5311,295 @@ mod tests { })); } + #[test] + fn restart_policy_matches_kubernetes_exit_semantics() { + assert!(!should_restart_main_process( + SandboxRestartPolicy::Never as i32, + 1 + )); + assert!(!should_restart_main_process( + SandboxRestartPolicy::OnFailure as i32, + 0 + )); + assert!(should_restart_main_process( + SandboxRestartPolicy::OnFailure as i32, + 1 + )); + assert!(should_restart_main_process( + SandboxRestartPolicy::Always as i32, + 0 + )); + } + + #[test] + fn restart_backoff_matches_kubernetes_defaults_and_cap() { + assert_eq!(restart_delay_ms(1), 10_000); + assert_eq!(restart_delay_ms(2), 20_000); + assert_eq!(restart_delay_ms(3), 40_000); + assert_eq!(restart_delay_ms(4), 80_000); + assert_eq!(restart_delay_ms(5), 160_000); + assert_eq!(restart_delay_ms(6), 300_000); + assert_eq!(restart_delay_ms(20), 300_000); + } + + #[tokio::test] + async fn always_policy_schedules_zero_exit_once_and_new_supervisor_becomes_ready() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let mut sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Ready); + sandbox.spec = Some(SandboxSpec { + restart_policy: SandboxRestartPolicy::Always as i32, + ..Default::default() + }); + sandbox.status = Some(SandboxStatus { + phase: SandboxPhase::Ready as i32, + main_process_instance_id: "instance-1".into(), + main_process_started_at_ms: openshell_core::time::now_ms(), + ..Default::default() + }); + runtime.store.put_message(&sandbox).await.unwrap(); + + runtime + .main_process_exited("sb-1", "instance-1", 0) + .await + .unwrap(); + runtime + .main_process_exited("sb-1", "instance-1", 0) + .await + .unwrap(); + + let restarting = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + assert_eq!(restarting.phase(), SandboxPhase::Restarting as i32); + let status = restarting.status.unwrap(); + assert_eq!(status.restart_count, 1); + assert_eq!(status.exit_code, Some(0)); + assert!(status.next_restart_at_ms > openshell_core::time::now_ms()); + + runtime + .supervisor_session_connected("sb-1", "instance-2") + .await + .unwrap(); + let ready = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + assert_eq!(ready.phase(), SandboxPhase::Ready as i32); + let status = ready.status.unwrap(); + assert_eq!(status.main_process_instance_id, "instance-2"); + assert_eq!(status.exit_code, None); + assert_eq!(status.next_restart_at_ms, 0); + assert!(status.main_process_started_at_ms > 0); + } + + #[tokio::test] + async fn restarting_sandbox_rejects_previous_supervisor_instance() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let mut sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Restarting); + sandbox.status = Some(SandboxStatus { + phase: SandboxPhase::Restarting as i32, + main_process_instance_id: "instance-1".into(), + exit_code: Some(9), + restart_count: 1, + next_restart_at_ms: openshell_core::time::now_ms() + 10_000, + ..Default::default() + }); + runtime.store.put_message(&sandbox).await.unwrap(); + + let error = runtime + .supervisor_session_connected("sb-1", "instance-1") + .await + .unwrap_err(); + + assert!(error.contains("requires a fresh supervisor instance")); + let stored = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + assert_eq!(stored.phase(), SandboxPhase::Restarting as i32); + assert_eq!( + stored.status.unwrap().main_process_instance_id, + "instance-1" + ); + } + + #[tokio::test] + async fn on_failure_policy_keeps_zero_exit_terminal() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let mut sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Ready); + sandbox.spec = Some(SandboxSpec { + restart_policy: SandboxRestartPolicy::OnFailure as i32, + ..Default::default() + }); + sandbox.status = Some(SandboxStatus { + phase: SandboxPhase::Ready as i32, + main_process_instance_id: "instance-1".into(), + ..Default::default() + }); + runtime.store.put_message(&sandbox).await.unwrap(); + + runtime + .main_process_exited("sb-1", "instance-1", 0) + .await + .unwrap(); + + let stored = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + assert_eq!(stored.phase(), SandboxPhase::Error as i32); + assert_eq!(stored.status.unwrap().restart_count, 0); + } + + #[test] + fn stable_main_run_resets_restart_backoff_count() { + let now_ms = openshell_core::time::now_ms(); + let mut sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Ready); + sandbox.status = Some(SandboxStatus { + restart_count: 5, + main_process_started_at_ms: now_ms - RESTART_STABILITY_WINDOW_MS, + ..Default::default() + }); + + apply_main_process_restart(&mut sandbox, "instance-6", 9, now_ms); + + let status = sandbox.status.unwrap(); + assert_eq!(status.restart_count, 1); + assert_eq!(status.next_restart_at_ms, now_ms + RESTART_INITIAL_DELAY_MS); + } + + #[tokio::test] + async fn due_restart_stops_and_starts_driver_without_exposing_stopped_phase() { + let driver = ControlledDriver::new(); + let runtime = test_runtime(driver.clone()).await; + let mut sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Restarting); + sandbox.spec = Some(SandboxSpec { + restart_policy: SandboxRestartPolicy::OnFailure as i32, + ..Default::default() + }); + sandbox.status = Some(SandboxStatus { + phase: SandboxPhase::Restarting as i32, + main_process_instance_id: "instance-1".into(), + exit_code: Some(9), + restart_count: 1, + next_restart_at_ms: openshell_core::time::now_ms() - 1, + ..Default::default() + }); + runtime.store.put_message(&sandbox).await.unwrap(); + + runtime.restart_sandbox_runtime("sb-1").await.unwrap(); + + assert_eq!(driver.stop_calls(), 1); + assert_eq!(driver.start_calls(), 1); + let stored = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + assert_eq!(stored.phase(), SandboxPhase::Restarting as i32); + let status = stored.status.unwrap(); + assert_eq!(status.exit_code, Some(9)); + assert!(status.next_restart_at_ms > openshell_core::time::now_ms()); + assert!(status.conditions.iter().any(|condition| { + condition.r#type == "Ready" && condition.reason == "SandboxRestarting" + })); + } + + #[tokio::test] + async fn retryable_restart_driver_failure_reschedules_attempt() { + let driver = ControlledDriver::new(); + driver.set_stop_outcome(ControlledLifecycleOutcome::Error("transport unavailable")); + let runtime = test_runtime(driver).await; + let mut sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Restarting); + sandbox.status = Some(SandboxStatus { + phase: SandboxPhase::Restarting as i32, + exit_code: Some(9), + restart_count: 2, + next_restart_at_ms: openshell_core::time::now_ms() - 1, + ..Default::default() + }); + runtime.store.put_message(&sandbox).await.unwrap(); + + let error = runtime.restart_sandbox_runtime("sb-1").await.unwrap_err(); + + assert!(error.contains("transport unavailable")); + let stored = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + assert_eq!(stored.phase(), SandboxPhase::Restarting as i32); + let status = stored.status.unwrap(); + assert!(status.next_restart_at_ms > openshell_core::time::now_ms()); + assert!(status.conditions.iter().any(|condition| { + condition.r#type == "Ready" && condition.reason == "SandboxRestartFailed" + })); + } + + #[tokio::test] + async fn missing_runtime_during_restart_is_terminal() { + let driver = ControlledDriver::new(); + driver.set_stop_outcome(ControlledLifecycleOutcome::NotFound); + let runtime = test_runtime(driver).await; + let mut sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Restarting); + sandbox.status = Some(SandboxStatus { + phase: SandboxPhase::Restarting as i32, + exit_code: Some(9), + restart_count: 1, + next_restart_at_ms: openshell_core::time::now_ms() - 1, + ..Default::default() + }); + runtime.store.put_message(&sandbox).await.unwrap(); + + runtime.restart_sandbox_runtime("sb-1").await.unwrap_err(); + + let stored = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + assert_eq!(stored.phase(), SandboxPhase::Error as i32); + let status = stored.status.unwrap(); + assert_eq!(status.next_restart_at_ms, 0); + assert_eq!(status.exit_code, Some(9)); + } + + #[tokio::test] + async fn manual_stop_cancels_pending_restart() { + let driver = ControlledDriver::new(); + let runtime = test_runtime(driver.clone()).await; + let mut sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Restarting); + sandbox.status = Some(SandboxStatus { + phase: SandboxPhase::Restarting as i32, + exit_code: Some(9), + restart_count: 2, + next_restart_at_ms: openshell_core::time::now_ms() + 60_000, + ..Default::default() + }); + runtime.store.put_message(&sandbox).await.unwrap(); + + let stopped = runtime.stop_sandbox("default", "sandbox-a").await.unwrap(); + + assert_eq!(stopped.phase(), SandboxPhase::Stopped as i32); + let status = stopped.status.unwrap(); + assert_eq!(status.exit_code, None); + assert_eq!(status.restart_count, 0); + assert_eq!(status.next_restart_at_ms, 0); + assert_eq!(driver.stop_calls(), 1); + } + #[tokio::test] async fn stale_main_process_exit_is_acknowledged_without_replacing_active_instance() { let runtime = test_runtime(Arc::new(TestDriver::default())).await; diff --git a/crates/openshell-server/src/grpc/sandbox.rs b/crates/openshell-server/src/grpc/sandbox.rs index 40476d43dc..5f053f8a11 100644 --- a/crates/openshell-server/src/grpc/sandbox.rs +++ b/crates/openshell-server/src/grpc/sandbox.rs @@ -27,7 +27,9 @@ use openshell_core::proto::{ TcpForwardFrame, TcpForwardInit, TcpRelayTarget, WatchSandboxRequest, relay_open, tcp_forward_init, }; -use openshell_core::proto::{Sandbox, SandboxPhase, SandboxTemplate, SshSession}; +use openshell_core::proto::{ + Sandbox, SandboxPhase, SandboxRestartPolicy, SandboxTemplate, SshSession, +}; use openshell_core::telemetry::{ LifecycleOperation, LifecycleResource, SandboxTemplateSource, TelemetryComputeDriver, TelemetryOutcome, @@ -141,6 +143,14 @@ pub(super) async fn fetch_and_authorize_sandbox( Ok(sandbox) } +fn require_ready_sandbox(sandbox: &Sandbox) -> Result<(), Status> { + match SandboxPhase::try_from(sandbox.phase()).ok() { + Some(SandboxPhase::Ready) => Ok(()), + Some(SandboxPhase::Restarting) => Err(Status::failed_precondition("sandbox is restarting")), + _ => Err(Status::failed_precondition("sandbox is not ready")), + } +} + fn generate_routable_name() -> String { let name = petname::petname(2, "-").unwrap_or_else(generate_name); let mut truncated = &name[..name.len().min(MAX_ROUTABLE_NAME_LEN)]; @@ -227,6 +237,9 @@ async fn handle_create_sandbox_inner( spec.command = vec!["/bin/bash".to_string(), "-l".to_string()]; spec.tty = true; } + if spec.restart_policy == SandboxRestartPolicy::Unspecified as i32 { + spec.restart_policy = SandboxRestartPolicy::Never as i32; + } // Validate field sizes before any I/O (fail fast on oversized payloads). validate_sandbox_spec(&request.name, &spec)?; @@ -1192,9 +1205,7 @@ pub(super) async fn handle_exec_sandbox( let sandbox = fetch_and_authorize_sandbox(state, &principal, &req.sandbox_id).await?; - if SandboxPhase::try_from(sandbox.phase()).ok() != Some(SandboxPhase::Ready) { - return Err(Status::failed_precondition("sandbox is not ready")); - } + require_ready_sandbox(&sandbox)?; // Open a relay channel through the supervisor session. Use a 15s // session-wait timeout, enough to cover a transient supervisor reconnect @@ -1302,9 +1313,7 @@ pub(super) async fn handle_forward_tcp( let sandbox = fetch_and_authorize_sandbox(state, &principal, &init.sandbox_id).await?; - if SandboxPhase::try_from(sandbox.phase()).ok() != Some(SandboxPhase::Ready) { - return Err(Status::failed_precondition("sandbox is not ready")); - } + require_ready_sandbox(&sandbox)?; let connection_guard = acquire_forward_connection_guard(state, &init, &sandbox).await?; let (channel_id, relay_rx) = state @@ -1624,9 +1633,7 @@ pub(super) async fn handle_exec_sandbox_interactive( let sandbox = fetch_and_authorize_sandbox(state, &principal, &req.sandbox_id).await?; - if SandboxPhase::try_from(sandbox.phase()).ok() != Some(SandboxPhase::Ready) { - return Err(Status::failed_precondition("sandbox is not ready")); - } + require_ready_sandbox(&sandbox)?; let (channel_id, relay_rx) = state .supervisor_sessions @@ -1695,9 +1702,7 @@ pub(super) async fn handle_create_ssh_session( let sandbox = fetch_and_authorize_sandbox(state, &principal, &req.sandbox_id).await?; - if SandboxPhase::try_from(sandbox.phase()).ok() != Some(SandboxPhase::Ready) { - return Err(Status::failed_precondition("sandbox is not ready")); - } + require_ready_sandbox(&sandbox)?; let token = uuid::Uuid::new_v4().to_string(); let now_ms = current_time_ms(); @@ -3352,6 +3357,10 @@ mod tests { .into_inner(); let created = response.sandbox.expect("created sandbox"); + assert_eq!( + created.spec.as_ref().unwrap().restart_policy(), + SandboxRestartPolicy::Never + ); assert_eq!( created .metadata diff --git a/crates/openshell-server/src/grpc/validation.rs b/crates/openshell-server/src/grpc/validation.rs index eb53c80f29..43387479d6 100644 --- a/crates/openshell-server/src/grpc/validation.rs +++ b/crates/openshell-server/src/grpc/validation.rs @@ -154,6 +154,13 @@ pub(super) fn validate_sandbox_spec( name: &str, spec: &openshell_core::proto::SandboxSpec, ) -> Result<(), Status> { + openshell_core::proto::SandboxRestartPolicy::try_from(spec.restart_policy).map_err(|_| { + Status::invalid_argument(format!( + "spec.restart_policy has unknown value {}", + spec.restart_policy + )) + })?; + // --- request.name --- if !name.is_empty() && name.len() > MAX_ROUTABLE_NAME_LEN { return Err(Status::invalid_argument(format!( @@ -1039,6 +1046,17 @@ mod tests { assert!(validate_sandbox_spec("", &default_spec()).is_ok()); } + #[test] + fn validate_sandbox_spec_rejects_unknown_restart_policy() { + let spec = SandboxSpec { + restart_policy: 99, + ..Default::default() + }; + let err = validate_sandbox_spec("", &spec).unwrap_err(); + assert_eq!(err.code(), Code::InvalidArgument); + assert!(err.message().contains("restart_policy")); + } + #[test] fn validate_sandbox_spec_accepts_exact_main_process_argv() { let spec = SandboxSpec { diff --git a/crates/openshell-tui/src/lib.rs b/crates/openshell-tui/src/lib.rs index 1f610015b4..2d72157676 100644 --- a/crates/openshell-tui/src/lib.rs +++ b/crates/openshell-tui/src/lib.rs @@ -2690,6 +2690,7 @@ fn phase_label(phase: i32) -> String { x if x == SandboxPhase::Stopping as i32 => "Stopping", x if x == SandboxPhase::Stopped as i32 => "Stopped", x if x == SandboxPhase::Starting as i32 => "Starting", + x if x == SandboxPhase::Restarting as i32 => "Restarting", _ => "Unknown", } .to_string() @@ -2728,6 +2729,7 @@ mod phase_label_tests { assert_eq!(phase_label(SandboxPhase::Stopping as i32), "Stopping"); assert_eq!(phase_label(SandboxPhase::Stopped as i32), "Stopped"); assert_eq!(phase_label(SandboxPhase::Starting as i32), "Starting"); + assert_eq!(phase_label(SandboxPhase::Restarting as i32), "Restarting"); } } diff --git a/crates/openshell-tui/src/ui/sandbox_detail.rs b/crates/openshell-tui/src/ui/sandbox_detail.rs index 434f369d39..c15979c7f3 100644 --- a/crates/openshell-tui/src/ui/sandbox_detail.rs +++ b/crates/openshell-tui/src/ui/sandbox_detail.rs @@ -23,14 +23,14 @@ pub fn draw(frame: &mut Frame<'_>, app: &App, area: Rect) { let phase_style = match phase { "Ready" => t.status_ok, - "Provisioning" | "Stopping" | "Starting" => t.status_warn, + "Provisioning" | "Stopping" | "Starting" | "Restarting" => t.status_warn, "Error" => t.status_err, _ => t.muted, }; let status_indicator = match phase { "Ready" => "●", - "Provisioning" | "Stopping" | "Starting" => "◐", + "Provisioning" | "Stopping" | "Starting" | "Restarting" => "◐", "Error" | "Stopped" => "○", _ => "…", }; diff --git a/crates/openshell-tui/src/ui/sandboxes.rs b/crates/openshell-tui/src/ui/sandboxes.rs index d927537189..072810d40d 100644 --- a/crates/openshell-tui/src/ui/sandboxes.rs +++ b/crates/openshell-tui/src/ui/sandboxes.rs @@ -41,7 +41,7 @@ pub fn draw(frame: &mut Frame<'_>, app: &App, area: Rect, focused: bool) { let phase_style = match phase { "Ready" => t.status_ok, - "Provisioning" | "Stopping" | "Starting" => t.status_warn, + "Provisioning" | "Stopping" | "Starting" | "Restarting" => t.status_warn, "Error" => t.status_err, _ => t.muted, }; diff --git a/docs/sandboxes/manage-sandboxes.mdx b/docs/sandboxes/manage-sandboxes.mdx index ead6533294..05fa6ccad5 100644 --- a/docs/sandboxes/manage-sandboxes.mdx +++ b/docs/sandboxes/manage-sandboxes.mdx @@ -29,6 +29,24 @@ the sandbox without attaching: openshell sandbox create --name worker --detach -- ./worker ``` +By default, the sandbox enters `Error` when its main process exits. Set a +Kubernetes-style restart policy when the main process should be restarted with +the sandbox compute resource: + +```shell +openshell sandbox create --name worker --detach --restart-policy on-failure -- ./worker +``` + +`never` is the default. `on-failure` restarts only after a nonzero normalized +exit code, and `always` restarts after every exit. OpenShell applies exponential +backoff from 10 seconds to 5 minutes and resets the backoff after the main +process runs successfully for 10 minutes. + +A restart replaces the sandbox compute runtime, supervisor, main process, and +retained PTY history. The sandbox identity and configuration remain stable; +filesystem persistence follows the selected compute driver's existing +stop/start behavior. This does not checkpoint or restore process state. + `--upload` cannot yet be combined with a trailing main command because uploads finish after the canonical process starts. Create a scratch sandbox, upload the files, then launch the workload with `sandbox exec`, or build the files into the @@ -500,7 +518,8 @@ Every sandbox moves through a defined set of phases: | Stopping | The gateway accepted a stop request and is stopping compute while retaining persistent state. | | Stopped | Compute is stopped and access is unavailable. The sandbox record and driver-owned persistent workspace remain. | | Starting | Compute is starting. The sandbox becomes usable only after a fresh supervisor session connects. | -| Error | Provisioning failed or the canonical main process exited unexpectedly. Main-process exit is terminal even with exit code 0. Check logs with `openshell logs`. | +| Restarting | The main process exited and its restart policy selected a restart. OpenShell is waiting for backoff or for the new supervisor session. | +| Error | Provisioning failed, a restart failed terminally, or the main process exited without a selected restart. Check logs with `openshell logs`. | | Deleting | The sandbox is being torn down. The system releases resources and purges credentials. | The compute backend can become ready before the sandbox supervisor connects to @@ -510,10 +529,17 @@ After a gateway restart, an existing sandbox can return to `Provisioning` temporarily while its supervisor reconnects. Wait for the phase to return to `Ready` before you connect to the sandbox or execute commands. -The gateway records a canonical main-process exit as `Ready=False` with reason -`MainProcessExited`. It also sets `status.exit_code`; signal exits use the -standard `128 + signal` convention. Compute runtimes do not automatically -restart that process. +The gateway records the normalized main-process result in `status.exit_code`; +signal exits use the standard `128 + signal` convention. If policy selects a +restart, status also exposes the restart count and next restart deadline while +the sandbox is `Restarting`. Connect and exec operations remain unavailable +until the replacement supervisor session returns the sandbox to `Ready`. +Use `openshell sandbox get ` to inspect the policy, most recent exit code, +restart count, and next attempt time. + +`sandbox stop` is an explicit user action and cancels pending restart intent. +Starting the sandbox again resets its restart count and launches a fresh main +process generation. ## Sandbox Compute Drivers diff --git a/e2e/rust/tests/sandbox_lifecycle.rs b/e2e/rust/tests/sandbox_lifecycle.rs index 0b0f4e0e66..708aec6624 100644 --- a/e2e/rust/tests/sandbox_lifecycle.rs +++ b/e2e/rust/tests/sandbox_lifecycle.rs @@ -14,6 +14,7 @@ use tokio::time::{Instant, sleep}; const SANDBOX_PRESENCE_TIMEOUT: Duration = Duration::from_secs(30); const SANDBOX_LIST_POLL_INTERVAL: Duration = Duration::from_millis(500); +const SANDBOX_RESTART_TIMEOUT: Duration = Duration::from_secs(60); fn normalize_output(output: &str) -> String { let stripped = strip_ansi(output).replace('\r', ""); @@ -130,6 +131,26 @@ async fn run_sandbox_lifecycle_command(operation: &str, name: &str) -> String { combined } +async fn sandbox_details(name: &str) -> String { + let mut cmd = openshell_cmd(); + cmd.args(["sandbox", "get", name]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let output = cmd.output().await.expect("spawn openshell sandbox get"); + let combined = normalize_output(&format!( + "{}{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + )); + assert!( + output.status.success(), + "sandbox get should succeed (exit {:?}):\n{combined}", + output.status.code(), + ); + combined +} + #[tokio::test] async fn sandbox_stop_start_preserves_workspace() { const SENTINEL: &str = "openshell-stop-start-sentinel"; @@ -421,6 +442,71 @@ async fn canonical_main_disconnect_reconnect_replays_history_for_same_process() sandbox.cleanup().await; } +#[tokio::test] +async fn on_failure_policy_replaces_runtime_and_preserves_workspace() { + const FIRST_MARKER: &str = "initial-main-ready"; + const SCRIPT: &str = r#" +marker=/sandbox/.openshell-restart-e2e +if [ -e "$marker" ]; then + printf 'replacement-%s\n' "$(cat /proc/sys/kernel/random/uuid)" > /sandbox/replacement-run + printf 'replacement-main-ready\n' + sleep 300 +else + touch "$marker" + printf 'initial-%s\n' "$(cat /proc/sys/kernel/random/uuid)" > /sandbox/initial-run + printf 'initial-main-ready\n' + sleep 2 + exit 17 +fi +"#; + + let mut sandbox = SandboxGuard::create_keep_with_args( + &["--restart-policy", "on-failure"], + &["sh", "-lc", SCRIPT], + FIRST_MARKER, + ) + .await + .expect("create sandbox with OnFailure restart policy"); + + let deadline = Instant::now() + SANDBOX_RESTART_TIMEOUT; + let mut observed_restarting = false; + let final_details = loop { + let details = sandbox_details(&sandbox.name).await; + observed_restarting |= details.contains("Phase: Restarting"); + if observed_restarting + && details.contains("Phase: Ready") + && details.contains("Restart count: 1") + { + break details; + } + assert!( + Instant::now() < deadline, + "sandbox did not complete its policy-driven restart within \ + {SANDBOX_RESTART_TIMEOUT:?}; last details:\n{details}" + ); + sleep(Duration::from_millis(250)).await; + }; + + assert!( + final_details.contains("Restart policy: on-failure"), + "restart policy should remain visible after replacement:\n{final_details}" + ); + let runs = sandbox + .exec(&["cat", "/sandbox/initial-run", "/sandbox/replacement-run"]) + .await + .expect("replacement sandbox should retain the first run's workspace"); + assert!( + runs.contains("initial-"), + "missing initial run marker:\n{runs}" + ); + assert!( + runs.contains("replacement-"), + "missing replacement run marker:\n{runs}" + ); + + sandbox.cleanup().await; +} + #[tokio::test] async fn sandbox_create_with_no_keep_cleans_up_after_tty_command() { let mut cmd = openshell_tty_cmd(&["sandbox", "create", "--no-keep", "--", "echo", "OK"]); diff --git a/proto/openshell.proto b/proto/openshell.proto index 2dc70eec01..eb28371e8e 100644 --- a/proto/openshell.proto +++ b/proto/openshell.proto @@ -831,6 +831,9 @@ message SandboxSpec { repeated string command = 12; // Allocate a retained pseudo-terminal for the main process. bool tty = 13; + // Gateway-owned policy for replacing the sandbox runtime after the canonical + // main process exits. Unspecified is normalized to Never before persistence. + SandboxRestartPolicy restart_policy = 14; } message ResourceRequirements { @@ -897,9 +900,17 @@ message SandboxStatus { // Supervisor instance currently associated with the canonical main process. // The gateway uses this to reject stale exit reports after a restart. string main_process_instance_id = 8; - // Normalized main process result. Signal exits use 128 + signal number. - // Presence indicates that the main process exited and the sandbox is in Error. + // Most recent normalized main process result. Signal exits use 128 + signal + // number. Cleared when a replacement main process becomes ready. optional int32 exit_code = 9; + // Consecutive policy-driven restart number in the current crash loop. + uint32 restart_count = 10; + // Unix epoch deadline in milliseconds for the next restart-controller + // attempt or recovery check. Zero when no automatic restart is pending. + int64 next_restart_at_ms = 11; + // Unix epoch time in milliseconds when the current main process became + // ready. Used to reset restart backoff after a stable run. + int64 main_process_started_at_ms = 12; } // User-facing sandbox condition derived from driver-native conditions. @@ -930,6 +941,7 @@ enum SandboxPhase { SANDBOX_PHASE_STOPPING = 6; SANDBOX_PHASE_STOPPED = 7; SANDBOX_PHASE_STARTING = 8; + SANDBOX_PHASE_RESTARTING = 9; } // Public platform event exposed on the sandbox watch stream. @@ -2689,6 +2701,16 @@ enum WorkspaceRole { WORKSPACE_ROLE_ADMIN = 2; } +// Policy applied when the canonical main process exits outside an intentional +// stop or delete operation. Kept after existing enums so generated enum +// descriptors remain stable. +enum SandboxRestartPolicy { + SANDBOX_RESTART_POLICY_UNSPECIFIED = 0; + SANDBOX_RESTART_POLICY_NEVER = 1; + SANDBOX_RESTART_POLICY_ON_FAILURE = 2; + SANDBOX_RESTART_POLICY_ALWAYS = 3; +} + // Workspace membership record. message WorkspaceMember { openshell.datamodel.v1.ObjectMeta metadata = 1; diff --git a/python/openshell/sandbox.py b/python/openshell/sandbox.py index b66ed936b6..d0b073ebe1 100644 --- a/python/openshell/sandbox.py +++ b/python/openshell/sandbox.py @@ -131,6 +131,9 @@ class SandboxStatusRef: phase: int current_policy_version: int exit_code: int | None = None + restart_count: int = 0 + next_restart_at_ms: int | None = None + main_process_started_at_ms: int | None = None class _ImmutableLabels(dict[str, str]): @@ -1096,6 +1099,13 @@ def _sandbox_ref(sandbox: openshell_pb2.Sandbox) -> SandboxRef: exit_code=status.exit_code if status is not None and status.HasField("exit_code") else None, + restart_count=status.restart_count if status is not None else 0, + next_restart_at_ms=status.next_restart_at_ms + if status is not None and status.next_restart_at_ms > 0 + else None, + main_process_started_at_ms=status.main_process_started_at_ms + if status is not None and status.main_process_started_at_ms > 0 + else None, ), labels=sandbox.metadata.labels if sandbox.metadata else {}, ) diff --git a/python/openshell/sandbox_test.py b/python/openshell/sandbox_test.py index a059192460..9364114bba 100644 --- a/python/openshell/sandbox_test.py +++ b/python/openshell/sandbox_test.py @@ -1775,10 +1775,16 @@ def test_sandbox_ref_retains_gateway_labels() -> None: def test_sandbox_ref_includes_main_process_result() -> None: proto = _make_sandbox_proto("sandbox-1", "job-1") proto.status.exit_code = 0 + proto.status.restart_count = 2 + proto.status.next_restart_at_ms = 1_700_000_000_000 + proto.status.main_process_started_at_ms = 1_699_999_000_000 status = _sandbox_ref(proto).status assert status.exit_code == 0 + assert status.restart_count == 2 + assert status.next_restart_at_ms == 1_700_000_000_000 + assert status.main_process_started_at_ms == 1_699_999_000_000 def test_returned_labels_are_immutable() -> None: diff --git a/sdk/go/openshell/v1/internal/converter/coverage_test.go b/sdk/go/openshell/v1/internal/converter/coverage_test.go index 49532f59be..02426b0659 100644 --- a/sdk/go/openshell/v1/internal/converter/coverage_test.go +++ b/sdk/go/openshell/v1/internal/converter/coverage_test.go @@ -31,6 +31,7 @@ func TestConverterCoversAllProtoFields_SandboxSpec(t *testing.T) { "resource_requirements": true, "command": true, "tty": true, + "restart_policy": true, } assertAllFieldsCovered(t, (&pb.SandboxSpec{}).ProtoReflect().Descriptor(), handled, nil) @@ -54,14 +55,17 @@ func TestConverterCoversAllProtoFields_SandboxTemplate(t *testing.T) { func TestConverterCoversAllProtoFields_SandboxStatus(t *testing.T) { handled := fieldSet{ - "sandbox_name": true, - "agent_pod": true, - "agent_fd": true, - "sandbox_fd": true, - "phase": true, - "conditions": true, - "current_policy_version": true, - "exit_code": true, + "sandbox_name": true, + "agent_pod": true, + "agent_fd": true, + "sandbox_fd": true, + "phase": true, + "conditions": true, + "current_policy_version": true, + "exit_code": true, + "restart_count": true, + "next_restart_at_ms": true, + "main_process_started_at_ms": true, } // The instance ID is an internal gateway/supervisor fencing token exposed // only through the raw protobuf API. diff --git a/sdk/go/openshell/v1/internal/converter/sandbox.go b/sdk/go/openshell/v1/internal/converter/sandbox.go index 5d26a1a7e2..9b9cec2eb5 100644 --- a/sdk/go/openshell/v1/internal/converter/sandbox.go +++ b/sdk/go/openshell/v1/internal/converter/sandbox.go @@ -46,10 +46,11 @@ func SandboxFromProto(s *pb.Sandbox) *types.Sandbox { func sandboxSpecFromProto(spec *pb.SandboxSpec) types.SandboxSpec { result := types.SandboxSpec{ - LogLevel: spec.GetLogLevel(), - Environment: CopyStringMap(spec.GetEnvironment()), - Providers: CopyStringSlice(spec.GetProviders()), - Policy: SandboxPolicyFromProto(spec.GetPolicy()), + LogLevel: spec.GetLogLevel(), + Environment: CopyStringMap(spec.GetEnvironment()), + Providers: CopyStringSlice(spec.GetProviders()), + Policy: SandboxPolicyFromProto(spec.GetPolicy()), + RestartPolicy: SandboxRestartPolicyFromProto(spec.GetRestartPolicy()), } if tmpl := spec.GetTemplate(); tmpl != nil { @@ -102,6 +103,9 @@ func sandboxStatusFromProto(status *pb.SandboxStatus) types.SandboxStatus { }) } result.ExitCode = CopyInt32Ptr(status.ExitCode) + result.RestartCount = status.GetRestartCount() + result.NextRestartAtMs = status.GetNextRestartAtMs() + result.MainProcessStartedAtMs = status.GetMainProcessStartedAtMs() return result } @@ -125,6 +129,8 @@ func SandboxPhaseFromProto(phase pb.SandboxPhase) types.SandboxPhase { return types.SandboxStopped case pb.SandboxPhase_SANDBOX_PHASE_STARTING: return types.SandboxStarting + case pb.SandboxPhase_SANDBOX_PHASE_RESTARTING: + return types.SandboxRestarting default: return types.SandboxUnknown } @@ -149,6 +155,8 @@ func SandboxPhaseToProto(phase types.SandboxPhase) pb.SandboxPhase { return pb.SandboxPhase_SANDBOX_PHASE_STOPPED case types.SandboxStarting: return pb.SandboxPhase_SANDBOX_PHASE_STARTING + case types.SandboxRestarting: + return pb.SandboxPhase_SANDBOX_PHASE_RESTARTING default: return pb.SandboxPhase_SANDBOX_PHASE_UNKNOWN } @@ -225,10 +233,35 @@ func SandboxSpecToProto(spec *types.SandboxSpec) *pb.SandboxSpec { result.Command = CopyStringSlice(spec.Command) result.Tty = spec.TTY + result.RestartPolicy = SandboxRestartPolicyToProto(spec.RestartPolicy) return result } +// SandboxRestartPolicyFromProto converts the restart policy to the curated SDK type. +func SandboxRestartPolicyFromProto(policy pb.SandboxRestartPolicy) types.SandboxRestartPolicy { + switch policy { + case pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ON_FAILURE: + return types.SandboxRestartOnFailure + case pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ALWAYS: + return types.SandboxRestartAlways + default: + return types.SandboxRestartNever + } +} + +// SandboxRestartPolicyToProto converts the curated SDK restart policy to protobuf. +func SandboxRestartPolicyToProto(policy types.SandboxRestartPolicy) pb.SandboxRestartPolicy { + switch policy { + case types.SandboxRestartOnFailure: + return pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ON_FAILURE + case types.SandboxRestartAlways: + return pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ALWAYS + default: + return pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_NEVER + } +} + // SandboxSpecToProtoChecked converts an SDK SandboxSpec and reports values // that protobuf Struct cannot represent instead of silently dropping them. func SandboxSpecToProtoChecked(spec *types.SandboxSpec) (*pb.SandboxSpec, error) { diff --git a/sdk/go/openshell/v1/internal/converter/sandbox_test.go b/sdk/go/openshell/v1/internal/converter/sandbox_test.go index 8293a784c0..89bed79d47 100644 --- a/sdk/go/openshell/v1/internal/converter/sandbox_test.go +++ b/sdk/go/openshell/v1/internal/converter/sandbox_test.go @@ -57,18 +57,22 @@ func TestSandboxFromProto(t *testing.T) { Count: &gpuCount, }, }, - Command: []string{"/opt/agent", "--serve"}, - Tty: false, + Command: []string{"/opt/agent", "--serve"}, + Tty: false, + RestartPolicy: pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ON_FAILURE, }, Status: &pb.SandboxStatus{ - SandboxName: "sb-compute-1", - AgentPod: "agent-pod-xyz", - AgentFd: "fd-agent", - SandboxFd: "fd-sandbox", - Phase: pb.SandboxPhase_SANDBOX_PHASE_READY, - CurrentPolicyVersion: 7, - MainProcessInstanceId: "instance-1", - ExitCode: &exitCode, + SandboxName: "sb-compute-1", + AgentPod: "agent-pod-xyz", + AgentFd: "fd-agent", + SandboxFd: "fd-sandbox", + Phase: pb.SandboxPhase_SANDBOX_PHASE_READY, + CurrentPolicyVersion: 7, + MainProcessInstanceId: "instance-1", + ExitCode: &exitCode, + RestartCount: 3, + NextRestartAtMs: 1700000070000, + MainProcessStartedAtMs: 1700000010000, Conditions: []*pb.SandboxCondition{ { Type: "Ready", @@ -102,6 +106,7 @@ func TestSandboxFromProto(t *testing.T) { assert.Equal(t, uint32(2), *s.Spec.GPUCount) assert.Equal(t, []string{"/opt/agent", "--serve"}, s.Spec.Command) assert.False(t, s.Spec.TTY) + assert.Equal(t, v1.SandboxRestartOnFailure, s.Spec.RestartPolicy) // Template require.NotNil(t, s.Spec.Template) @@ -134,6 +139,9 @@ func TestSandboxFromProto(t *testing.T) { assert.Equal(t, "2024-01-01T00:00:00Z", s.Status.Conditions[0].LastTransitionTime) require.NotNil(t, s.Status.ExitCode) assert.Equal(t, int32(0), *s.Status.ExitCode) + assert.Equal(t, uint32(3), s.Status.RestartCount) + assert.Equal(t, int64(1700000070000), s.Status.NextRestartAtMs) + assert.Equal(t, int64(1700000010000), s.Status.MainProcessStartedAtMs) } func TestSandboxFromProto_TemplateResourcesDeepCopy(t *testing.T) { diff --git a/sdk/go/openshell/v1/types.go b/sdk/go/openshell/v1/types.go index dea7872a04..28640f9c65 100644 --- a/sdk/go/openshell/v1/types.go +++ b/sdk/go/openshell/v1/types.go @@ -20,6 +20,17 @@ const ( SandboxStopping = types.SandboxStopping SandboxStopped = types.SandboxStopped SandboxStarting = types.SandboxStarting + SandboxRestarting = types.SandboxRestarting +) + +// SandboxRestartPolicy controls replacement after the canonical main process exits. +type SandboxRestartPolicy = types.SandboxRestartPolicy + +// Sandbox restart policy values. +const ( + SandboxRestartNever = types.SandboxRestartNever + SandboxRestartOnFailure = types.SandboxRestartOnFailure + SandboxRestartAlways = types.SandboxRestartAlways ) // EventType classifies watch events. diff --git a/sdk/go/openshell/v1/types/sandbox.go b/sdk/go/openshell/v1/types/sandbox.go index 6ce51d84a1..cec242171c 100644 --- a/sdk/go/openshell/v1/types/sandbox.go +++ b/sdk/go/openshell/v1/types/sandbox.go @@ -27,9 +27,10 @@ type SandboxSpec struct { Providers []string GPUCount *uint32 // Policy is the security policy for the sandbox. Nil means no policy specified. - Policy *SandboxPolicy - Command []string - TTY bool + Policy *SandboxPolicy + Command []string + TTY bool + RestartPolicy SandboxRestartPolicy } // SandboxTemplate defines the container template for a sandbox. @@ -47,14 +48,17 @@ type SandboxTemplate struct { // SandboxStatus holds the observed state of a sandbox. type SandboxStatus struct { - SandboxName string - AgentPod string - AgentFd string - SandboxFd string - Phase SandboxPhase - Conditions []SandboxCondition - CurrentPolicyVersion uint32 - ExitCode *int32 + SandboxName string + AgentPod string + AgentFd string + SandboxFd string + Phase SandboxPhase + Conditions []SandboxCondition + CurrentPolicyVersion uint32 + ExitCode *int32 + RestartCount uint32 + NextRestartAtMs int64 + MainProcessStartedAtMs int64 } // SandboxCondition describes an observed condition of a sandbox. diff --git a/sdk/go/openshell/v1/types/types.go b/sdk/go/openshell/v1/types/types.go index 4e3b830805..2948f0e6e2 100644 --- a/sdk/go/openshell/v1/types/types.go +++ b/sdk/go/openshell/v1/types/types.go @@ -18,6 +18,17 @@ const ( SandboxStopping SandboxPhase = "Stopping" SandboxStopped SandboxPhase = "Stopped" SandboxStarting SandboxPhase = "Starting" + SandboxRestarting SandboxPhase = "Restarting" +) + +// SandboxRestartPolicy controls replacement after the canonical main process exits. +type SandboxRestartPolicy string + +// Sandbox restart policy values. +const ( + SandboxRestartNever SandboxRestartPolicy = "Never" + SandboxRestartOnFailure SandboxRestartPolicy = "OnFailure" + SandboxRestartAlways SandboxRestartPolicy = "Always" ) // EventType classifies watch events. diff --git a/sdk/go/proto/openshellv1/openshell.pb.go b/sdk/go/proto/openshellv1/openshell.pb.go index 38e54f419e..f2ff2b2dac 100644 --- a/sdk/go/proto/openshellv1/openshell.pb.go +++ b/sdk/go/proto/openshellv1/openshell.pb.go @@ -44,6 +44,7 @@ const ( SandboxPhase_SANDBOX_PHASE_STOPPING SandboxPhase = 6 SandboxPhase_SANDBOX_PHASE_STOPPED SandboxPhase = 7 SandboxPhase_SANDBOX_PHASE_STARTING SandboxPhase = 8 + SandboxPhase_SANDBOX_PHASE_RESTARTING SandboxPhase = 9 ) // Enum value maps for SandboxPhase. @@ -58,6 +59,7 @@ var ( 6: "SANDBOX_PHASE_STOPPING", 7: "SANDBOX_PHASE_STOPPED", 8: "SANDBOX_PHASE_STARTING", + 9: "SANDBOX_PHASE_RESTARTING", } SandboxPhase_value = map[string]int32{ "SANDBOX_PHASE_UNSPECIFIED": 0, @@ -69,6 +71,7 @@ var ( "SANDBOX_PHASE_STOPPING": 6, "SANDBOX_PHASE_STOPPED": 7, "SANDBOX_PHASE_STARTING": 8, + "SANDBOX_PHASE_RESTARTING": 9, } ) @@ -388,6 +391,61 @@ func (WorkspaceRole) EnumDescriptor() ([]byte, []int) { return file_openshell_proto_rawDescGZIP(), []int{5} } +// Policy applied when the canonical main process exits outside an intentional +// stop or delete operation. Kept after existing enums so generated enum +// descriptors remain stable. +type SandboxRestartPolicy int32 + +const ( + SandboxRestartPolicy_SANDBOX_RESTART_POLICY_UNSPECIFIED SandboxRestartPolicy = 0 + SandboxRestartPolicy_SANDBOX_RESTART_POLICY_NEVER SandboxRestartPolicy = 1 + SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ON_FAILURE SandboxRestartPolicy = 2 + SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ALWAYS SandboxRestartPolicy = 3 +) + +// Enum value maps for SandboxRestartPolicy. +var ( + SandboxRestartPolicy_name = map[int32]string{ + 0: "SANDBOX_RESTART_POLICY_UNSPECIFIED", + 1: "SANDBOX_RESTART_POLICY_NEVER", + 2: "SANDBOX_RESTART_POLICY_ON_FAILURE", + 3: "SANDBOX_RESTART_POLICY_ALWAYS", + } + SandboxRestartPolicy_value = map[string]int32{ + "SANDBOX_RESTART_POLICY_UNSPECIFIED": 0, + "SANDBOX_RESTART_POLICY_NEVER": 1, + "SANDBOX_RESTART_POLICY_ON_FAILURE": 2, + "SANDBOX_RESTART_POLICY_ALWAYS": 3, + } +) + +func (x SandboxRestartPolicy) Enum() *SandboxRestartPolicy { + p := new(SandboxRestartPolicy) + *p = x + return p +} + +func (x SandboxRestartPolicy) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SandboxRestartPolicy) Descriptor() protoreflect.EnumDescriptor { + return file_openshell_proto_enumTypes[6].Descriptor() +} + +func (SandboxRestartPolicy) Type() protoreflect.EnumType { + return &file_openshell_proto_enumTypes[6] +} + +func (x SandboxRestartPolicy) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SandboxRestartPolicy.Descriptor instead. +func (SandboxRestartPolicy) EnumDescriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{6} +} + // IssueSandboxToken request. Empty body; identity is established by the // authentication credentials carried in the request headers (a projected // Kubernetes ServiceAccount JWT in the K8s driver path). @@ -1118,7 +1176,10 @@ type SandboxSpec struct { // portable scratch login shell before persistence. Command []string `protobuf:"bytes,12,rep,name=command,proto3" json:"command,omitempty"` // Allocate a retained pseudo-terminal for the main process. - Tty bool `protobuf:"varint,13,opt,name=tty,proto3" json:"tty,omitempty"` + Tty bool `protobuf:"varint,13,opt,name=tty,proto3" json:"tty,omitempty"` + // Gateway-owned policy for replacing the sandbox runtime after the canonical + // main process exits. Unspecified is normalized to Never before persistence. + RestartPolicy SandboxRestartPolicy `protobuf:"varint,14,opt,name=restart_policy,json=restartPolicy,proto3,enum=openshell.v1.SandboxRestartPolicy" json:"restart_policy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1209,6 +1270,13 @@ func (x *SandboxSpec) GetTty() bool { return false } +func (x *SandboxSpec) GetRestartPolicy() SandboxRestartPolicy { + if x != nil { + return x.RestartPolicy + } + return SandboxRestartPolicy_SANDBOX_RESTART_POLICY_UNSPECIFIED +} + type ResourceRequirements struct { state protoimpl.MessageState `protogen:"open.v1"` // GPU requirements for the sandbox. Presence indicates a GPU request. @@ -1448,11 +1516,19 @@ type SandboxStatus struct { // Supervisor instance currently associated with the canonical main process. // The gateway uses this to reject stale exit reports after a restart. MainProcessInstanceId string `protobuf:"bytes,8,opt,name=main_process_instance_id,json=mainProcessInstanceId,proto3" json:"main_process_instance_id,omitempty"` - // Normalized main process result. Signal exits use 128 + signal number. - // Presence indicates that the main process exited and the sandbox is in Error. - ExitCode *int32 `protobuf:"varint,9,opt,name=exit_code,json=exitCode,proto3,oneof" json:"exit_code,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Most recent normalized main process result. Signal exits use 128 + signal + // number. Cleared when a replacement main process becomes ready. + ExitCode *int32 `protobuf:"varint,9,opt,name=exit_code,json=exitCode,proto3,oneof" json:"exit_code,omitempty"` + // Consecutive policy-driven restart number in the current crash loop. + RestartCount uint32 `protobuf:"varint,10,opt,name=restart_count,json=restartCount,proto3" json:"restart_count,omitempty"` + // Unix epoch deadline in milliseconds for the next restart-controller + // attempt or recovery check. Zero when no automatic restart is pending. + NextRestartAtMs int64 `protobuf:"varint,11,opt,name=next_restart_at_ms,json=nextRestartAtMs,proto3" json:"next_restart_at_ms,omitempty"` + // Unix epoch time in milliseconds when the current main process became + // ready. Used to reset restart backoff after a stable run. + MainProcessStartedAtMs int64 `protobuf:"varint,12,opt,name=main_process_started_at_ms,json=mainProcessStartedAtMs,proto3" json:"main_process_started_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SandboxStatus) Reset() { @@ -1548,6 +1624,27 @@ func (x *SandboxStatus) GetExitCode() int32 { return 0 } +func (x *SandboxStatus) GetRestartCount() uint32 { + if x != nil { + return x.RestartCount + } + return 0 +} + +func (x *SandboxStatus) GetNextRestartAtMs() int64 { + if x != nil { + return x.NextRestartAtMs + } + return 0 +} + +func (x *SandboxStatus) GetMainProcessStartedAtMs() int64 { + if x != nil { + return x.MainProcessStartedAtMs + } + return 0 +} + // User-facing sandbox condition derived from driver-native conditions. type SandboxCondition struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -13358,7 +13455,7 @@ const file_openshell_proto_rawDesc = "" + "\aSandbox\x12>\n" + "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12-\n" + "\x04spec\x18\x02 \x01(\v2\x19.openshell.v1.SandboxSpecR\x04spec\x123\n" + - "\x06status\x18\x03 \x01(\v2\x1b.openshell.v1.SandboxStatusR\x06statusJ\x04\b\x04\x10\x05J\x04\b\x05\x10\x06R\x05phaseR\x16current_policy_version\"\x83\x04\n" + + "\x06status\x18\x03 \x01(\v2\x1b.openshell.v1.SandboxStatusR\x06statusJ\x04\b\x04\x10\x05J\x04\b\x05\x10\x06R\x05phaseR\x16current_policy_version\"\xce\x04\n" + "\vSandboxSpec\x12\x1b\n" + "\tlog_level\x18\x01 \x01(\tR\blogLevel\x12L\n" + "\venvironment\x18\x05 \x03(\v2*.openshell.v1.SandboxSpec.EnvironmentEntryR\venvironment\x129\n" + @@ -13367,7 +13464,8 @@ const file_openshell_proto_rawDesc = "" + "\tproviders\x18\b \x03(\tR\tproviders\x12W\n" + "\x15resource_requirements\x18\t \x01(\v2\".openshell.v1.ResourceRequirementsR\x14resourceRequirements\x12\x18\n" + "\acommand\x18\f \x03(\tR\acommand\x12\x10\n" + - "\x03tty\x18\r \x01(\bR\x03tty\x1a>\n" + + "\x03tty\x18\r \x01(\bR\x03tty\x12I\n" + + "\x0erestart_policy\x18\x0e \x01(\x0e2\".openshell.v1.SandboxRestartPolicyR\rrestartPolicy\x1a>\n" + "\x10EnvironmentEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\n" + @@ -13399,7 +13497,7 @@ const file_openshell_proto_rawDesc = "" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x12\n" + "\x10_user_namespacesJ\x04\b\t\x10\n" + - "R\x16volume_claim_templates\"\x9a\x03\n" + + "R\x16volume_claim_templates\"\xa8\x04\n" + "\rSandboxStatus\x12!\n" + "\fsandbox_name\x18\x01 \x01(\tR\vsandboxName\x12\x1b\n" + "\tagent_pod\x18\x02 \x01(\tR\bagentPod\x12\x19\n" + @@ -13412,7 +13510,11 @@ const file_openshell_proto_rawDesc = "" + "\x05phase\x18\x06 \x01(\x0e2\x1a.openshell.v1.SandboxPhaseR\x05phase\x124\n" + "\x16current_policy_version\x18\a \x01(\rR\x14currentPolicyVersion\x127\n" + "\x18main_process_instance_id\x18\b \x01(\tR\x15mainProcessInstanceId\x12 \n" + - "\texit_code\x18\t \x01(\x05H\x00R\bexitCode\x88\x01\x01B\f\n" + + "\texit_code\x18\t \x01(\x05H\x00R\bexitCode\x88\x01\x01\x12#\n" + + "\rrestart_count\x18\n" + + " \x01(\rR\frestartCount\x12+\n" + + "\x12next_restart_at_ms\x18\v \x01(\x03R\x0fnextRestartAtMs\x12:\n" + + "\x1amain_process_started_at_ms\x18\f \x01(\x03R\x16mainProcessStartedAtMsB\f\n" + "\n" + "_exit_code\"\xa2\x01\n" + "\x10SandboxCondition\x12\x12\n" + @@ -14337,7 +14439,7 @@ const file_openshell_proto_rawDesc = "" + "\x1aExtensionServiceCredential\x12!\n" + "\fservice_name\x18\x01 \x01(\tR\vserviceName\x12\x1a\n" + "\x05token\x18\x02 \x01(\tB\x04\x88\xb5\x18\x01R\x05token\x12\"\n" + - "\rexpires_at_ms\x18\x03 \x01(\x03R\vexpiresAtMs*\x89\x02\n" + + "\rexpires_at_ms\x18\x03 \x01(\x03R\vexpiresAtMs*\xa7\x02\n" + "\fSandboxPhase\x12\x1d\n" + "\x19SANDBOX_PHASE_UNSPECIFIED\x10\x00\x12\x1e\n" + "\x1aSANDBOX_PHASE_PROVISIONING\x10\x01\x12\x17\n" + @@ -14347,7 +14449,8 @@ const file_openshell_proto_rawDesc = "" + "\x15SANDBOX_PHASE_UNKNOWN\x10\x05\x12\x1a\n" + "\x16SANDBOX_PHASE_STOPPING\x10\x06\x12\x19\n" + "\x15SANDBOX_PHASE_STOPPED\x10\a\x12\x1a\n" + - "\x16SANDBOX_PHASE_STARTING\x10\b*\xc3\x03\n" + + "\x16SANDBOX_PHASE_STARTING\x10\b\x12\x1c\n" + + "\x18SANDBOX_PHASE_RESTARTING\x10\t*\xc3\x03\n" + "!ProviderCredentialRefreshStrategy\x124\n" + "0PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED\x10\x00\x12/\n" + "+PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC\x10\x01\x121\n" + @@ -14379,7 +14482,12 @@ const file_openshell_proto_rawDesc = "" + "\rWorkspaceRole\x12\x1e\n" + "\x1aWORKSPACE_ROLE_UNSPECIFIED\x10\x00\x12\x17\n" + "\x13WORKSPACE_ROLE_USER\x10\x01\x12\x18\n" + - "\x14WORKSPACE_ROLE_ADMIN\x10\x022\x95E\n" + + "\x14WORKSPACE_ROLE_ADMIN\x10\x02*\xaa\x01\n" + + "\x14SandboxRestartPolicy\x12&\n" + + "\"SANDBOX_RESTART_POLICY_UNSPECIFIED\x10\x00\x12 \n" + + "\x1cSANDBOX_RESTART_POLICY_NEVER\x10\x01\x12%\n" + + "!SANDBOX_RESTART_POLICY_ON_FAILURE\x10\x02\x12!\n" + + "\x1dSANDBOX_RESTART_POLICY_ALWAYS\x10\x032\x95E\n" + "\tOpenShell\x12Z\n" + "\x06Health\x12\x1b.openshell.v1.HealthRequest\x1a\x1c.openshell.v1.HealthResponse\"\x15\x82\xb5\x18\x11\n" + "\x0funauthenticated\x12i\n" + @@ -14531,7 +14639,7 @@ func file_openshell_proto_rawDescGZIP() []byte { return file_openshell_proto_rawDescData } -var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 6) +var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 7) var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 213) var file_openshell_proto_goTypes = []any{ (SandboxPhase)(0), // 0: openshell.v1.SandboxPhase @@ -14540,531 +14648,533 @@ var file_openshell_proto_goTypes = []any{ (PolicyStatus)(0), // 3: openshell.v1.PolicyStatus (ServiceStatus)(0), // 4: openshell.v1.ServiceStatus (WorkspaceRole)(0), // 5: openshell.v1.WorkspaceRole - (*IssueSandboxTokenRequest)(nil), // 6: openshell.v1.IssueSandboxTokenRequest - (*IssueSandboxTokenResponse)(nil), // 7: openshell.v1.IssueSandboxTokenResponse - (*RefreshSandboxTokenRequest)(nil), // 8: openshell.v1.RefreshSandboxTokenRequest - (*RefreshSandboxTokenResponse)(nil), // 9: openshell.v1.RefreshSandboxTokenResponse - (*HealthRequest)(nil), // 10: openshell.v1.HealthRequest - (*HealthResponse)(nil), // 11: openshell.v1.HealthResponse - (*GetCurrentUserRequest)(nil), // 12: openshell.v1.GetCurrentUserRequest - (*GetCurrentUserResponse)(nil), // 13: openshell.v1.GetCurrentUserResponse - (*GetGatewayInfoRequest)(nil), // 14: openshell.v1.GetGatewayInfoRequest - (*GetGatewayInfoResponse)(nil), // 15: openshell.v1.GetGatewayInfoResponse - (*ComputeDriverInfo)(nil), // 16: openshell.v1.ComputeDriverInfo - (*ComputeDriverCapabilities)(nil), // 17: openshell.v1.ComputeDriverCapabilities - (*Sandbox)(nil), // 18: openshell.v1.Sandbox - (*SandboxSpec)(nil), // 19: openshell.v1.SandboxSpec - (*ResourceRequirements)(nil), // 20: openshell.v1.ResourceRequirements - (*GpuResourceRequirements)(nil), // 21: openshell.v1.GpuResourceRequirements - (*SandboxTemplate)(nil), // 22: openshell.v1.SandboxTemplate - (*SandboxStatus)(nil), // 23: openshell.v1.SandboxStatus - (*SandboxCondition)(nil), // 24: openshell.v1.SandboxCondition - (*PlatformEvent)(nil), // 25: openshell.v1.PlatformEvent - (*CreateSandboxRequest)(nil), // 26: openshell.v1.CreateSandboxRequest - (*GetSandboxRequest)(nil), // 27: openshell.v1.GetSandboxRequest - (*ListSandboxesRequest)(nil), // 28: openshell.v1.ListSandboxesRequest - (*ListSandboxProvidersRequest)(nil), // 29: openshell.v1.ListSandboxProvidersRequest - (*AttachSandboxProviderRequest)(nil), // 30: openshell.v1.AttachSandboxProviderRequest - (*DetachSandboxProviderRequest)(nil), // 31: openshell.v1.DetachSandboxProviderRequest - (*DeleteSandboxRequest)(nil), // 32: openshell.v1.DeleteSandboxRequest - (*StopSandboxRequest)(nil), // 33: openshell.v1.StopSandboxRequest - (*StartSandboxRequest)(nil), // 34: openshell.v1.StartSandboxRequest - (*SandboxResponse)(nil), // 35: openshell.v1.SandboxResponse - (*ListSandboxesResponse)(nil), // 36: openshell.v1.ListSandboxesResponse - (*ListSandboxProvidersResponse)(nil), // 37: openshell.v1.ListSandboxProvidersResponse - (*AttachSandboxProviderResponse)(nil), // 38: openshell.v1.AttachSandboxProviderResponse - (*DetachSandboxProviderResponse)(nil), // 39: openshell.v1.DetachSandboxProviderResponse - (*DeleteSandboxResponse)(nil), // 40: openshell.v1.DeleteSandboxResponse - (*CreateSshSessionRequest)(nil), // 41: openshell.v1.CreateSshSessionRequest - (*CreateSshSessionResponse)(nil), // 42: openshell.v1.CreateSshSessionResponse - (*ExposeServiceRequest)(nil), // 43: openshell.v1.ExposeServiceRequest - (*GetServiceRequest)(nil), // 44: openshell.v1.GetServiceRequest - (*ListServicesRequest)(nil), // 45: openshell.v1.ListServicesRequest - (*ListServicesResponse)(nil), // 46: openshell.v1.ListServicesResponse - (*DeleteServiceRequest)(nil), // 47: openshell.v1.DeleteServiceRequest - (*DeleteServiceResponse)(nil), // 48: openshell.v1.DeleteServiceResponse - (*ServiceEndpoint)(nil), // 49: openshell.v1.ServiceEndpoint - (*ServiceEndpointResponse)(nil), // 50: openshell.v1.ServiceEndpointResponse - (*RevokeSshSessionRequest)(nil), // 51: openshell.v1.RevokeSshSessionRequest - (*RevokeSshSessionResponse)(nil), // 52: openshell.v1.RevokeSshSessionResponse - (*ExecSandboxRequest)(nil), // 53: openshell.v1.ExecSandboxRequest - (*ExecSandboxStdout)(nil), // 54: openshell.v1.ExecSandboxStdout - (*ExecSandboxStderr)(nil), // 55: openshell.v1.ExecSandboxStderr - (*ExecSandboxExit)(nil), // 56: openshell.v1.ExecSandboxExit - (*ExecSandboxEvent)(nil), // 57: openshell.v1.ExecSandboxEvent - (*TcpForwardInit)(nil), // 58: openshell.v1.TcpForwardInit - (*TcpForwardFrame)(nil), // 59: openshell.v1.TcpForwardFrame - (*ExecSandboxInput)(nil), // 60: openshell.v1.ExecSandboxInput - (*ExecSandboxWindowResize)(nil), // 61: openshell.v1.ExecSandboxWindowResize - (*SshSession)(nil), // 62: openshell.v1.SshSession - (*WatchSandboxRequest)(nil), // 63: openshell.v1.WatchSandboxRequest - (*SandboxStreamEvent)(nil), // 64: openshell.v1.SandboxStreamEvent - (*SandboxLogLine)(nil), // 65: openshell.v1.SandboxLogLine - (*SandboxStreamWarning)(nil), // 66: openshell.v1.SandboxStreamWarning - (*CreateProviderRequest)(nil), // 67: openshell.v1.CreateProviderRequest - (*GetProviderRequest)(nil), // 68: openshell.v1.GetProviderRequest - (*ListProvidersRequest)(nil), // 69: openshell.v1.ListProvidersRequest - (*UpdateProviderRequest)(nil), // 70: openshell.v1.UpdateProviderRequest - (*DeleteProviderRequest)(nil), // 71: openshell.v1.DeleteProviderRequest - (*ProviderResponse)(nil), // 72: openshell.v1.ProviderResponse - (*ListProvidersResponse)(nil), // 73: openshell.v1.ListProvidersResponse - (*ListProviderProfilesRequest)(nil), // 74: openshell.v1.ListProviderProfilesRequest - (*GetProviderProfileRequest)(nil), // 75: openshell.v1.GetProviderProfileRequest - (*ProviderProfileImportItem)(nil), // 76: openshell.v1.ProviderProfileImportItem - (*ProviderProfileDiagnostic)(nil), // 77: openshell.v1.ProviderProfileDiagnostic - (*ProviderCredentialTokenGrantAudienceOverride)(nil), // 78: openshell.v1.ProviderCredentialTokenGrantAudienceOverride - (*ProviderCredentialTokenGrant)(nil), // 79: openshell.v1.ProviderCredentialTokenGrant - (*ProviderProfileCredential)(nil), // 80: openshell.v1.ProviderProfileCredential - (*ProviderCredentialRefreshMaterial)(nil), // 81: openshell.v1.ProviderCredentialRefreshMaterial - (*ProviderCredentialRefreshOutput)(nil), // 82: openshell.v1.ProviderCredentialRefreshOutput - (*ProviderCredentialRefresh)(nil), // 83: openshell.v1.ProviderCredentialRefresh - (*ProviderCredentialRefreshStatus)(nil), // 84: openshell.v1.ProviderCredentialRefreshStatus - (*ProviderProfileDiscovery)(nil), // 85: openshell.v1.ProviderProfileDiscovery - (*StoredProviderCredentialRefreshState)(nil), // 86: openshell.v1.StoredProviderCredentialRefreshState - (*StoredRefreshMaterialDeletion)(nil), // 87: openshell.v1.StoredRefreshMaterialDeletion - (*GetProviderRefreshStatusRequest)(nil), // 88: openshell.v1.GetProviderRefreshStatusRequest - (*GetProviderRefreshStatusResponse)(nil), // 89: openshell.v1.GetProviderRefreshStatusResponse - (*ConfigureProviderRefreshRequest)(nil), // 90: openshell.v1.ConfigureProviderRefreshRequest - (*ConfigureProviderRefreshResponse)(nil), // 91: openshell.v1.ConfigureProviderRefreshResponse - (*RotateProviderCredentialRequest)(nil), // 92: openshell.v1.RotateProviderCredentialRequest - (*RotateProviderCredentialResponse)(nil), // 93: openshell.v1.RotateProviderCredentialResponse - (*DeleteProviderRefreshRequest)(nil), // 94: openshell.v1.DeleteProviderRefreshRequest - (*DeleteProviderRefreshResponse)(nil), // 95: openshell.v1.DeleteProviderRefreshResponse - (*ProviderProfile)(nil), // 96: openshell.v1.ProviderProfile - (*StoredProviderProfile)(nil), // 97: openshell.v1.StoredProviderProfile - (*ProviderProfileResponse)(nil), // 98: openshell.v1.ProviderProfileResponse - (*ListProviderProfilesResponse)(nil), // 99: openshell.v1.ListProviderProfilesResponse - (*ImportProviderProfilesRequest)(nil), // 100: openshell.v1.ImportProviderProfilesRequest - (*ImportProviderProfilesResponse)(nil), // 101: openshell.v1.ImportProviderProfilesResponse - (*UpdateProviderProfilesRequest)(nil), // 102: openshell.v1.UpdateProviderProfilesRequest - (*UpdateProviderProfilesResponse)(nil), // 103: openshell.v1.UpdateProviderProfilesResponse - (*LintProviderProfilesRequest)(nil), // 104: openshell.v1.LintProviderProfilesRequest - (*LintProviderProfilesResponse)(nil), // 105: openshell.v1.LintProviderProfilesResponse - (*DeleteProviderResponse)(nil), // 106: openshell.v1.DeleteProviderResponse - (*DeleteProviderProfileRequest)(nil), // 107: openshell.v1.DeleteProviderProfileRequest - (*DeleteProviderProfileResponse)(nil), // 108: openshell.v1.DeleteProviderProfileResponse - (*GetSandboxProviderEnvironmentRequest)(nil), // 109: openshell.v1.GetSandboxProviderEnvironmentRequest - (*StaticCredentialEndpointBinding)(nil), // 110: openshell.v1.StaticCredentialEndpointBinding - (*StaticCredentialBinding)(nil), // 111: openshell.v1.StaticCredentialBinding - (*GetSandboxProviderEnvironmentResponse)(nil), // 112: openshell.v1.GetSandboxProviderEnvironmentResponse - (*UpdateConfigRequest)(nil), // 113: openshell.v1.UpdateConfigRequest - (*PolicyMergeOperation)(nil), // 114: openshell.v1.PolicyMergeOperation - (*AddNetworkRule)(nil), // 115: openshell.v1.AddNetworkRule - (*RemoveNetworkEndpoint)(nil), // 116: openshell.v1.RemoveNetworkEndpoint - (*RemoveNetworkRule)(nil), // 117: openshell.v1.RemoveNetworkRule - (*AddDenyRules)(nil), // 118: openshell.v1.AddDenyRules - (*AddAllowRules)(nil), // 119: openshell.v1.AddAllowRules - (*RemoveNetworkBinary)(nil), // 120: openshell.v1.RemoveNetworkBinary - (*UpdateConfigResponse)(nil), // 121: openshell.v1.UpdateConfigResponse - (*GetSandboxPolicyStatusRequest)(nil), // 122: openshell.v1.GetSandboxPolicyStatusRequest - (*GetSandboxPolicyStatusResponse)(nil), // 123: openshell.v1.GetSandboxPolicyStatusResponse - (*ListSandboxPoliciesRequest)(nil), // 124: openshell.v1.ListSandboxPoliciesRequest - (*ListSandboxPoliciesResponse)(nil), // 125: openshell.v1.ListSandboxPoliciesResponse - (*ReportPolicyStatusRequest)(nil), // 126: openshell.v1.ReportPolicyStatusRequest - (*ReportPolicyStatusResponse)(nil), // 127: openshell.v1.ReportPolicyStatusResponse - (*SandboxPolicyRevision)(nil), // 128: openshell.v1.SandboxPolicyRevision - (*GetSandboxLogsRequest)(nil), // 129: openshell.v1.GetSandboxLogsRequest - (*PushSandboxLogsRequest)(nil), // 130: openshell.v1.PushSandboxLogsRequest - (*PushSandboxLogsResponse)(nil), // 131: openshell.v1.PushSandboxLogsResponse - (*GetSandboxLogsResponse)(nil), // 132: openshell.v1.GetSandboxLogsResponse - (*SupervisorMessage)(nil), // 133: openshell.v1.SupervisorMessage - (*GatewayMessage)(nil), // 134: openshell.v1.GatewayMessage - (*SupervisorHello)(nil), // 135: openshell.v1.SupervisorHello - (*SessionAccepted)(nil), // 136: openshell.v1.SessionAccepted - (*SessionRejected)(nil), // 137: openshell.v1.SessionRejected - (*SupervisorHeartbeat)(nil), // 138: openshell.v1.SupervisorHeartbeat - (*GatewayHeartbeat)(nil), // 139: openshell.v1.GatewayHeartbeat - (*ReportMainProcessExitRequest)(nil), // 140: openshell.v1.ReportMainProcessExitRequest - (*ReportMainProcessExitResponse)(nil), // 141: openshell.v1.ReportMainProcessExitResponse - (*RelayOpen)(nil), // 142: openshell.v1.RelayOpen - (*SshRelayTarget)(nil), // 143: openshell.v1.SshRelayTarget - (*TcpRelayTarget)(nil), // 144: openshell.v1.TcpRelayTarget - (*RelayInit)(nil), // 145: openshell.v1.RelayInit - (*RelayFrame)(nil), // 146: openshell.v1.RelayFrame - (*RelayOpenResult)(nil), // 147: openshell.v1.RelayOpenResult - (*RelayClose)(nil), // 148: openshell.v1.RelayClose - (*L7RequestSample)(nil), // 149: openshell.v1.L7RequestSample - (*DenialSummary)(nil), // 150: openshell.v1.DenialSummary - (*DenialGroupCount)(nil), // 151: openshell.v1.DenialGroupCount - (*NetworkActivitySummary)(nil), // 152: openshell.v1.NetworkActivitySummary - (*PolicyChunk)(nil), // 153: openshell.v1.PolicyChunk - (*DraftPolicyUpdate)(nil), // 154: openshell.v1.DraftPolicyUpdate - (*SubmitPolicyAnalysisRequest)(nil), // 155: openshell.v1.SubmitPolicyAnalysisRequest - (*SubmitPolicyAnalysisResponse)(nil), // 156: openshell.v1.SubmitPolicyAnalysisResponse - (*GetDraftPolicyRequest)(nil), // 157: openshell.v1.GetDraftPolicyRequest - (*GetDraftPolicyResponse)(nil), // 158: openshell.v1.GetDraftPolicyResponse - (*ApproveDraftChunkRequest)(nil), // 159: openshell.v1.ApproveDraftChunkRequest - (*ApproveDraftChunkResponse)(nil), // 160: openshell.v1.ApproveDraftChunkResponse - (*RejectDraftChunkRequest)(nil), // 161: openshell.v1.RejectDraftChunkRequest - (*RejectDraftChunkResponse)(nil), // 162: openshell.v1.RejectDraftChunkResponse - (*ApproveAllDraftChunksRequest)(nil), // 163: openshell.v1.ApproveAllDraftChunksRequest - (*ApproveAllDraftChunksResponse)(nil), // 164: openshell.v1.ApproveAllDraftChunksResponse - (*EditDraftChunkRequest)(nil), // 165: openshell.v1.EditDraftChunkRequest - (*EditDraftChunkResponse)(nil), // 166: openshell.v1.EditDraftChunkResponse - (*UndoDraftChunkRequest)(nil), // 167: openshell.v1.UndoDraftChunkRequest - (*UndoDraftChunkResponse)(nil), // 168: openshell.v1.UndoDraftChunkResponse - (*ClearDraftChunksRequest)(nil), // 169: openshell.v1.ClearDraftChunksRequest - (*ClearDraftChunksResponse)(nil), // 170: openshell.v1.ClearDraftChunksResponse - (*GetDraftHistoryRequest)(nil), // 171: openshell.v1.GetDraftHistoryRequest - (*DraftHistoryEntry)(nil), // 172: openshell.v1.DraftHistoryEntry - (*GetDraftHistoryResponse)(nil), // 173: openshell.v1.GetDraftHistoryResponse - (*PolicyRevisionPayload)(nil), // 174: openshell.v1.PolicyRevisionPayload - (*DraftChunkPayload)(nil), // 175: openshell.v1.DraftChunkPayload - (*StoredPolicyRevision)(nil), // 176: openshell.v1.StoredPolicyRevision - (*StoredDraftChunk)(nil), // 177: openshell.v1.StoredDraftChunk - (*CreateWorkspaceRequest)(nil), // 178: openshell.v1.CreateWorkspaceRequest - (*CreateWorkspaceResponse)(nil), // 179: openshell.v1.CreateWorkspaceResponse - (*GetWorkspaceRequest)(nil), // 180: openshell.v1.GetWorkspaceRequest - (*GetWorkspaceResponse)(nil), // 181: openshell.v1.GetWorkspaceResponse - (*ListWorkspacesRequest)(nil), // 182: openshell.v1.ListWorkspacesRequest - (*ListWorkspacesResponse)(nil), // 183: openshell.v1.ListWorkspacesResponse - (*DeleteWorkspaceRequest)(nil), // 184: openshell.v1.DeleteWorkspaceRequest - (*DeleteWorkspaceResponse)(nil), // 185: openshell.v1.DeleteWorkspaceResponse - (*WorkspaceMember)(nil), // 186: openshell.v1.WorkspaceMember - (*AddWorkspaceMemberRequest)(nil), // 187: openshell.v1.AddWorkspaceMemberRequest - (*AddWorkspaceMemberResponse)(nil), // 188: openshell.v1.AddWorkspaceMemberResponse - (*RemoveWorkspaceMemberRequest)(nil), // 189: openshell.v1.RemoveWorkspaceMemberRequest - (*RemoveWorkspaceMemberResponse)(nil), // 190: openshell.v1.RemoveWorkspaceMemberResponse - (*ListWorkspaceMembersRequest)(nil), // 191: openshell.v1.ListWorkspaceMembersRequest - (*ListWorkspaceMembersResponse)(nil), // 192: openshell.v1.ListWorkspaceMembersResponse - (*ExtensionServiceCredential)(nil), // 193: openshell.v1.ExtensionServiceCredential - nil, // 194: openshell.v1.SandboxSpec.EnvironmentEntry - nil, // 195: openshell.v1.SandboxTemplate.LabelsEntry - nil, // 196: openshell.v1.SandboxTemplate.AnnotationsEntry - nil, // 197: openshell.v1.SandboxTemplate.EnvironmentEntry - nil, // 198: openshell.v1.PlatformEvent.MetadataEntry - nil, // 199: openshell.v1.CreateSandboxRequest.LabelsEntry - nil, // 200: openshell.v1.CreateSandboxRequest.AnnotationsEntry - nil, // 201: openshell.v1.ExecSandboxRequest.EnvironmentEntry - nil, // 202: openshell.v1.SandboxLogLine.FieldsEntry - nil, // 203: openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry - nil, // 204: openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry - nil, // 205: openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry - nil, // 206: openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry - nil, // 207: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - nil, // 208: openshell.v1.ProviderProfile.AnnotationsEntry - nil, // 209: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - nil, // 210: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry - nil, // 211: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - nil, // 212: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - nil, // 213: openshell.v1.UpdateConfigRequest.AnnotationsEntry - nil, // 214: openshell.v1.UpdateConfigResponse.AnnotationsEntry - nil, // 215: openshell.v1.SandboxPolicyRevision.ProvenanceEntry - nil, // 216: openshell.v1.PolicyRevisionPayload.ProvenanceEntry - nil, // 217: openshell.v1.StoredPolicyRevision.ProvenanceEntry - nil, // 218: openshell.v1.CreateWorkspaceRequest.LabelsEntry - (*datamodelv1.ObjectMeta)(nil), // 219: openshell.datamodel.v1.ObjectMeta - (*sandboxv1.SandboxPolicy)(nil), // 220: openshell.sandbox.v1.SandboxPolicy - (*structpb.Struct)(nil), // 221: google.protobuf.Struct - (*datamodelv1.Provider)(nil), // 222: openshell.datamodel.v1.Provider - (*datamodelv1.CredentialHandle)(nil), // 223: openshell.datamodel.v1.CredentialHandle - (*sandboxv1.NetworkEndpoint)(nil), // 224: openshell.sandbox.v1.NetworkEndpoint - (*sandboxv1.NetworkBinary)(nil), // 225: openshell.sandbox.v1.NetworkBinary - (*sandboxv1.SettingValue)(nil), // 226: openshell.sandbox.v1.SettingValue - (*sandboxv1.NetworkPolicyRule)(nil), // 227: openshell.sandbox.v1.NetworkPolicyRule - (*sandboxv1.L7DenyRule)(nil), // 228: openshell.sandbox.v1.L7DenyRule - (*sandboxv1.L7Rule)(nil), // 229: openshell.sandbox.v1.L7Rule - (*datamodelv1.Workspace)(nil), // 230: openshell.datamodel.v1.Workspace - (*sandboxv1.GetSandboxConfigRequest)(nil), // 231: openshell.sandbox.v1.GetSandboxConfigRequest - (*sandboxv1.GetGatewayConfigRequest)(nil), // 232: openshell.sandbox.v1.GetGatewayConfigRequest - (*sandboxv1.GetSandboxConfigResponse)(nil), // 233: openshell.sandbox.v1.GetSandboxConfigResponse - (*sandboxv1.GetGatewayConfigResponse)(nil), // 234: openshell.sandbox.v1.GetGatewayConfigResponse + (SandboxRestartPolicy)(0), // 6: openshell.v1.SandboxRestartPolicy + (*IssueSandboxTokenRequest)(nil), // 7: openshell.v1.IssueSandboxTokenRequest + (*IssueSandboxTokenResponse)(nil), // 8: openshell.v1.IssueSandboxTokenResponse + (*RefreshSandboxTokenRequest)(nil), // 9: openshell.v1.RefreshSandboxTokenRequest + (*RefreshSandboxTokenResponse)(nil), // 10: openshell.v1.RefreshSandboxTokenResponse + (*HealthRequest)(nil), // 11: openshell.v1.HealthRequest + (*HealthResponse)(nil), // 12: openshell.v1.HealthResponse + (*GetCurrentUserRequest)(nil), // 13: openshell.v1.GetCurrentUserRequest + (*GetCurrentUserResponse)(nil), // 14: openshell.v1.GetCurrentUserResponse + (*GetGatewayInfoRequest)(nil), // 15: openshell.v1.GetGatewayInfoRequest + (*GetGatewayInfoResponse)(nil), // 16: openshell.v1.GetGatewayInfoResponse + (*ComputeDriverInfo)(nil), // 17: openshell.v1.ComputeDriverInfo + (*ComputeDriverCapabilities)(nil), // 18: openshell.v1.ComputeDriverCapabilities + (*Sandbox)(nil), // 19: openshell.v1.Sandbox + (*SandboxSpec)(nil), // 20: openshell.v1.SandboxSpec + (*ResourceRequirements)(nil), // 21: openshell.v1.ResourceRequirements + (*GpuResourceRequirements)(nil), // 22: openshell.v1.GpuResourceRequirements + (*SandboxTemplate)(nil), // 23: openshell.v1.SandboxTemplate + (*SandboxStatus)(nil), // 24: openshell.v1.SandboxStatus + (*SandboxCondition)(nil), // 25: openshell.v1.SandboxCondition + (*PlatformEvent)(nil), // 26: openshell.v1.PlatformEvent + (*CreateSandboxRequest)(nil), // 27: openshell.v1.CreateSandboxRequest + (*GetSandboxRequest)(nil), // 28: openshell.v1.GetSandboxRequest + (*ListSandboxesRequest)(nil), // 29: openshell.v1.ListSandboxesRequest + (*ListSandboxProvidersRequest)(nil), // 30: openshell.v1.ListSandboxProvidersRequest + (*AttachSandboxProviderRequest)(nil), // 31: openshell.v1.AttachSandboxProviderRequest + (*DetachSandboxProviderRequest)(nil), // 32: openshell.v1.DetachSandboxProviderRequest + (*DeleteSandboxRequest)(nil), // 33: openshell.v1.DeleteSandboxRequest + (*StopSandboxRequest)(nil), // 34: openshell.v1.StopSandboxRequest + (*StartSandboxRequest)(nil), // 35: openshell.v1.StartSandboxRequest + (*SandboxResponse)(nil), // 36: openshell.v1.SandboxResponse + (*ListSandboxesResponse)(nil), // 37: openshell.v1.ListSandboxesResponse + (*ListSandboxProvidersResponse)(nil), // 38: openshell.v1.ListSandboxProvidersResponse + (*AttachSandboxProviderResponse)(nil), // 39: openshell.v1.AttachSandboxProviderResponse + (*DetachSandboxProviderResponse)(nil), // 40: openshell.v1.DetachSandboxProviderResponse + (*DeleteSandboxResponse)(nil), // 41: openshell.v1.DeleteSandboxResponse + (*CreateSshSessionRequest)(nil), // 42: openshell.v1.CreateSshSessionRequest + (*CreateSshSessionResponse)(nil), // 43: openshell.v1.CreateSshSessionResponse + (*ExposeServiceRequest)(nil), // 44: openshell.v1.ExposeServiceRequest + (*GetServiceRequest)(nil), // 45: openshell.v1.GetServiceRequest + (*ListServicesRequest)(nil), // 46: openshell.v1.ListServicesRequest + (*ListServicesResponse)(nil), // 47: openshell.v1.ListServicesResponse + (*DeleteServiceRequest)(nil), // 48: openshell.v1.DeleteServiceRequest + (*DeleteServiceResponse)(nil), // 49: openshell.v1.DeleteServiceResponse + (*ServiceEndpoint)(nil), // 50: openshell.v1.ServiceEndpoint + (*ServiceEndpointResponse)(nil), // 51: openshell.v1.ServiceEndpointResponse + (*RevokeSshSessionRequest)(nil), // 52: openshell.v1.RevokeSshSessionRequest + (*RevokeSshSessionResponse)(nil), // 53: openshell.v1.RevokeSshSessionResponse + (*ExecSandboxRequest)(nil), // 54: openshell.v1.ExecSandboxRequest + (*ExecSandboxStdout)(nil), // 55: openshell.v1.ExecSandboxStdout + (*ExecSandboxStderr)(nil), // 56: openshell.v1.ExecSandboxStderr + (*ExecSandboxExit)(nil), // 57: openshell.v1.ExecSandboxExit + (*ExecSandboxEvent)(nil), // 58: openshell.v1.ExecSandboxEvent + (*TcpForwardInit)(nil), // 59: openshell.v1.TcpForwardInit + (*TcpForwardFrame)(nil), // 60: openshell.v1.TcpForwardFrame + (*ExecSandboxInput)(nil), // 61: openshell.v1.ExecSandboxInput + (*ExecSandboxWindowResize)(nil), // 62: openshell.v1.ExecSandboxWindowResize + (*SshSession)(nil), // 63: openshell.v1.SshSession + (*WatchSandboxRequest)(nil), // 64: openshell.v1.WatchSandboxRequest + (*SandboxStreamEvent)(nil), // 65: openshell.v1.SandboxStreamEvent + (*SandboxLogLine)(nil), // 66: openshell.v1.SandboxLogLine + (*SandboxStreamWarning)(nil), // 67: openshell.v1.SandboxStreamWarning + (*CreateProviderRequest)(nil), // 68: openshell.v1.CreateProviderRequest + (*GetProviderRequest)(nil), // 69: openshell.v1.GetProviderRequest + (*ListProvidersRequest)(nil), // 70: openshell.v1.ListProvidersRequest + (*UpdateProviderRequest)(nil), // 71: openshell.v1.UpdateProviderRequest + (*DeleteProviderRequest)(nil), // 72: openshell.v1.DeleteProviderRequest + (*ProviderResponse)(nil), // 73: openshell.v1.ProviderResponse + (*ListProvidersResponse)(nil), // 74: openshell.v1.ListProvidersResponse + (*ListProviderProfilesRequest)(nil), // 75: openshell.v1.ListProviderProfilesRequest + (*GetProviderProfileRequest)(nil), // 76: openshell.v1.GetProviderProfileRequest + (*ProviderProfileImportItem)(nil), // 77: openshell.v1.ProviderProfileImportItem + (*ProviderProfileDiagnostic)(nil), // 78: openshell.v1.ProviderProfileDiagnostic + (*ProviderCredentialTokenGrantAudienceOverride)(nil), // 79: openshell.v1.ProviderCredentialTokenGrantAudienceOverride + (*ProviderCredentialTokenGrant)(nil), // 80: openshell.v1.ProviderCredentialTokenGrant + (*ProviderProfileCredential)(nil), // 81: openshell.v1.ProviderProfileCredential + (*ProviderCredentialRefreshMaterial)(nil), // 82: openshell.v1.ProviderCredentialRefreshMaterial + (*ProviderCredentialRefreshOutput)(nil), // 83: openshell.v1.ProviderCredentialRefreshOutput + (*ProviderCredentialRefresh)(nil), // 84: openshell.v1.ProviderCredentialRefresh + (*ProviderCredentialRefreshStatus)(nil), // 85: openshell.v1.ProviderCredentialRefreshStatus + (*ProviderProfileDiscovery)(nil), // 86: openshell.v1.ProviderProfileDiscovery + (*StoredProviderCredentialRefreshState)(nil), // 87: openshell.v1.StoredProviderCredentialRefreshState + (*StoredRefreshMaterialDeletion)(nil), // 88: openshell.v1.StoredRefreshMaterialDeletion + (*GetProviderRefreshStatusRequest)(nil), // 89: openshell.v1.GetProviderRefreshStatusRequest + (*GetProviderRefreshStatusResponse)(nil), // 90: openshell.v1.GetProviderRefreshStatusResponse + (*ConfigureProviderRefreshRequest)(nil), // 91: openshell.v1.ConfigureProviderRefreshRequest + (*ConfigureProviderRefreshResponse)(nil), // 92: openshell.v1.ConfigureProviderRefreshResponse + (*RotateProviderCredentialRequest)(nil), // 93: openshell.v1.RotateProviderCredentialRequest + (*RotateProviderCredentialResponse)(nil), // 94: openshell.v1.RotateProviderCredentialResponse + (*DeleteProviderRefreshRequest)(nil), // 95: openshell.v1.DeleteProviderRefreshRequest + (*DeleteProviderRefreshResponse)(nil), // 96: openshell.v1.DeleteProviderRefreshResponse + (*ProviderProfile)(nil), // 97: openshell.v1.ProviderProfile + (*StoredProviderProfile)(nil), // 98: openshell.v1.StoredProviderProfile + (*ProviderProfileResponse)(nil), // 99: openshell.v1.ProviderProfileResponse + (*ListProviderProfilesResponse)(nil), // 100: openshell.v1.ListProviderProfilesResponse + (*ImportProviderProfilesRequest)(nil), // 101: openshell.v1.ImportProviderProfilesRequest + (*ImportProviderProfilesResponse)(nil), // 102: openshell.v1.ImportProviderProfilesResponse + (*UpdateProviderProfilesRequest)(nil), // 103: openshell.v1.UpdateProviderProfilesRequest + (*UpdateProviderProfilesResponse)(nil), // 104: openshell.v1.UpdateProviderProfilesResponse + (*LintProviderProfilesRequest)(nil), // 105: openshell.v1.LintProviderProfilesRequest + (*LintProviderProfilesResponse)(nil), // 106: openshell.v1.LintProviderProfilesResponse + (*DeleteProviderResponse)(nil), // 107: openshell.v1.DeleteProviderResponse + (*DeleteProviderProfileRequest)(nil), // 108: openshell.v1.DeleteProviderProfileRequest + (*DeleteProviderProfileResponse)(nil), // 109: openshell.v1.DeleteProviderProfileResponse + (*GetSandboxProviderEnvironmentRequest)(nil), // 110: openshell.v1.GetSandboxProviderEnvironmentRequest + (*StaticCredentialEndpointBinding)(nil), // 111: openshell.v1.StaticCredentialEndpointBinding + (*StaticCredentialBinding)(nil), // 112: openshell.v1.StaticCredentialBinding + (*GetSandboxProviderEnvironmentResponse)(nil), // 113: openshell.v1.GetSandboxProviderEnvironmentResponse + (*UpdateConfigRequest)(nil), // 114: openshell.v1.UpdateConfigRequest + (*PolicyMergeOperation)(nil), // 115: openshell.v1.PolicyMergeOperation + (*AddNetworkRule)(nil), // 116: openshell.v1.AddNetworkRule + (*RemoveNetworkEndpoint)(nil), // 117: openshell.v1.RemoveNetworkEndpoint + (*RemoveNetworkRule)(nil), // 118: openshell.v1.RemoveNetworkRule + (*AddDenyRules)(nil), // 119: openshell.v1.AddDenyRules + (*AddAllowRules)(nil), // 120: openshell.v1.AddAllowRules + (*RemoveNetworkBinary)(nil), // 121: openshell.v1.RemoveNetworkBinary + (*UpdateConfigResponse)(nil), // 122: openshell.v1.UpdateConfigResponse + (*GetSandboxPolicyStatusRequest)(nil), // 123: openshell.v1.GetSandboxPolicyStatusRequest + (*GetSandboxPolicyStatusResponse)(nil), // 124: openshell.v1.GetSandboxPolicyStatusResponse + (*ListSandboxPoliciesRequest)(nil), // 125: openshell.v1.ListSandboxPoliciesRequest + (*ListSandboxPoliciesResponse)(nil), // 126: openshell.v1.ListSandboxPoliciesResponse + (*ReportPolicyStatusRequest)(nil), // 127: openshell.v1.ReportPolicyStatusRequest + (*ReportPolicyStatusResponse)(nil), // 128: openshell.v1.ReportPolicyStatusResponse + (*SandboxPolicyRevision)(nil), // 129: openshell.v1.SandboxPolicyRevision + (*GetSandboxLogsRequest)(nil), // 130: openshell.v1.GetSandboxLogsRequest + (*PushSandboxLogsRequest)(nil), // 131: openshell.v1.PushSandboxLogsRequest + (*PushSandboxLogsResponse)(nil), // 132: openshell.v1.PushSandboxLogsResponse + (*GetSandboxLogsResponse)(nil), // 133: openshell.v1.GetSandboxLogsResponse + (*SupervisorMessage)(nil), // 134: openshell.v1.SupervisorMessage + (*GatewayMessage)(nil), // 135: openshell.v1.GatewayMessage + (*SupervisorHello)(nil), // 136: openshell.v1.SupervisorHello + (*SessionAccepted)(nil), // 137: openshell.v1.SessionAccepted + (*SessionRejected)(nil), // 138: openshell.v1.SessionRejected + (*SupervisorHeartbeat)(nil), // 139: openshell.v1.SupervisorHeartbeat + (*GatewayHeartbeat)(nil), // 140: openshell.v1.GatewayHeartbeat + (*ReportMainProcessExitRequest)(nil), // 141: openshell.v1.ReportMainProcessExitRequest + (*ReportMainProcessExitResponse)(nil), // 142: openshell.v1.ReportMainProcessExitResponse + (*RelayOpen)(nil), // 143: openshell.v1.RelayOpen + (*SshRelayTarget)(nil), // 144: openshell.v1.SshRelayTarget + (*TcpRelayTarget)(nil), // 145: openshell.v1.TcpRelayTarget + (*RelayInit)(nil), // 146: openshell.v1.RelayInit + (*RelayFrame)(nil), // 147: openshell.v1.RelayFrame + (*RelayOpenResult)(nil), // 148: openshell.v1.RelayOpenResult + (*RelayClose)(nil), // 149: openshell.v1.RelayClose + (*L7RequestSample)(nil), // 150: openshell.v1.L7RequestSample + (*DenialSummary)(nil), // 151: openshell.v1.DenialSummary + (*DenialGroupCount)(nil), // 152: openshell.v1.DenialGroupCount + (*NetworkActivitySummary)(nil), // 153: openshell.v1.NetworkActivitySummary + (*PolicyChunk)(nil), // 154: openshell.v1.PolicyChunk + (*DraftPolicyUpdate)(nil), // 155: openshell.v1.DraftPolicyUpdate + (*SubmitPolicyAnalysisRequest)(nil), // 156: openshell.v1.SubmitPolicyAnalysisRequest + (*SubmitPolicyAnalysisResponse)(nil), // 157: openshell.v1.SubmitPolicyAnalysisResponse + (*GetDraftPolicyRequest)(nil), // 158: openshell.v1.GetDraftPolicyRequest + (*GetDraftPolicyResponse)(nil), // 159: openshell.v1.GetDraftPolicyResponse + (*ApproveDraftChunkRequest)(nil), // 160: openshell.v1.ApproveDraftChunkRequest + (*ApproveDraftChunkResponse)(nil), // 161: openshell.v1.ApproveDraftChunkResponse + (*RejectDraftChunkRequest)(nil), // 162: openshell.v1.RejectDraftChunkRequest + (*RejectDraftChunkResponse)(nil), // 163: openshell.v1.RejectDraftChunkResponse + (*ApproveAllDraftChunksRequest)(nil), // 164: openshell.v1.ApproveAllDraftChunksRequest + (*ApproveAllDraftChunksResponse)(nil), // 165: openshell.v1.ApproveAllDraftChunksResponse + (*EditDraftChunkRequest)(nil), // 166: openshell.v1.EditDraftChunkRequest + (*EditDraftChunkResponse)(nil), // 167: openshell.v1.EditDraftChunkResponse + (*UndoDraftChunkRequest)(nil), // 168: openshell.v1.UndoDraftChunkRequest + (*UndoDraftChunkResponse)(nil), // 169: openshell.v1.UndoDraftChunkResponse + (*ClearDraftChunksRequest)(nil), // 170: openshell.v1.ClearDraftChunksRequest + (*ClearDraftChunksResponse)(nil), // 171: openshell.v1.ClearDraftChunksResponse + (*GetDraftHistoryRequest)(nil), // 172: openshell.v1.GetDraftHistoryRequest + (*DraftHistoryEntry)(nil), // 173: openshell.v1.DraftHistoryEntry + (*GetDraftHistoryResponse)(nil), // 174: openshell.v1.GetDraftHistoryResponse + (*PolicyRevisionPayload)(nil), // 175: openshell.v1.PolicyRevisionPayload + (*DraftChunkPayload)(nil), // 176: openshell.v1.DraftChunkPayload + (*StoredPolicyRevision)(nil), // 177: openshell.v1.StoredPolicyRevision + (*StoredDraftChunk)(nil), // 178: openshell.v1.StoredDraftChunk + (*CreateWorkspaceRequest)(nil), // 179: openshell.v1.CreateWorkspaceRequest + (*CreateWorkspaceResponse)(nil), // 180: openshell.v1.CreateWorkspaceResponse + (*GetWorkspaceRequest)(nil), // 181: openshell.v1.GetWorkspaceRequest + (*GetWorkspaceResponse)(nil), // 182: openshell.v1.GetWorkspaceResponse + (*ListWorkspacesRequest)(nil), // 183: openshell.v1.ListWorkspacesRequest + (*ListWorkspacesResponse)(nil), // 184: openshell.v1.ListWorkspacesResponse + (*DeleteWorkspaceRequest)(nil), // 185: openshell.v1.DeleteWorkspaceRequest + (*DeleteWorkspaceResponse)(nil), // 186: openshell.v1.DeleteWorkspaceResponse + (*WorkspaceMember)(nil), // 187: openshell.v1.WorkspaceMember + (*AddWorkspaceMemberRequest)(nil), // 188: openshell.v1.AddWorkspaceMemberRequest + (*AddWorkspaceMemberResponse)(nil), // 189: openshell.v1.AddWorkspaceMemberResponse + (*RemoveWorkspaceMemberRequest)(nil), // 190: openshell.v1.RemoveWorkspaceMemberRequest + (*RemoveWorkspaceMemberResponse)(nil), // 191: openshell.v1.RemoveWorkspaceMemberResponse + (*ListWorkspaceMembersRequest)(nil), // 192: openshell.v1.ListWorkspaceMembersRequest + (*ListWorkspaceMembersResponse)(nil), // 193: openshell.v1.ListWorkspaceMembersResponse + (*ExtensionServiceCredential)(nil), // 194: openshell.v1.ExtensionServiceCredential + nil, // 195: openshell.v1.SandboxSpec.EnvironmentEntry + nil, // 196: openshell.v1.SandboxTemplate.LabelsEntry + nil, // 197: openshell.v1.SandboxTemplate.AnnotationsEntry + nil, // 198: openshell.v1.SandboxTemplate.EnvironmentEntry + nil, // 199: openshell.v1.PlatformEvent.MetadataEntry + nil, // 200: openshell.v1.CreateSandboxRequest.LabelsEntry + nil, // 201: openshell.v1.CreateSandboxRequest.AnnotationsEntry + nil, // 202: openshell.v1.ExecSandboxRequest.EnvironmentEntry + nil, // 203: openshell.v1.SandboxLogLine.FieldsEntry + nil, // 204: openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry + nil, // 205: openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry + nil, // 206: openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry + nil, // 207: openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry + nil, // 208: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry + nil, // 209: openshell.v1.ProviderProfile.AnnotationsEntry + nil, // 210: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry + nil, // 211: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry + nil, // 212: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry + nil, // 213: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry + nil, // 214: openshell.v1.UpdateConfigRequest.AnnotationsEntry + nil, // 215: openshell.v1.UpdateConfigResponse.AnnotationsEntry + nil, // 216: openshell.v1.SandboxPolicyRevision.ProvenanceEntry + nil, // 217: openshell.v1.PolicyRevisionPayload.ProvenanceEntry + nil, // 218: openshell.v1.StoredPolicyRevision.ProvenanceEntry + nil, // 219: openshell.v1.CreateWorkspaceRequest.LabelsEntry + (*datamodelv1.ObjectMeta)(nil), // 220: openshell.datamodel.v1.ObjectMeta + (*sandboxv1.SandboxPolicy)(nil), // 221: openshell.sandbox.v1.SandboxPolicy + (*structpb.Struct)(nil), // 222: google.protobuf.Struct + (*datamodelv1.Provider)(nil), // 223: openshell.datamodel.v1.Provider + (*datamodelv1.CredentialHandle)(nil), // 224: openshell.datamodel.v1.CredentialHandle + (*sandboxv1.NetworkEndpoint)(nil), // 225: openshell.sandbox.v1.NetworkEndpoint + (*sandboxv1.NetworkBinary)(nil), // 226: openshell.sandbox.v1.NetworkBinary + (*sandboxv1.SettingValue)(nil), // 227: openshell.sandbox.v1.SettingValue + (*sandboxv1.NetworkPolicyRule)(nil), // 228: openshell.sandbox.v1.NetworkPolicyRule + (*sandboxv1.L7DenyRule)(nil), // 229: openshell.sandbox.v1.L7DenyRule + (*sandboxv1.L7Rule)(nil), // 230: openshell.sandbox.v1.L7Rule + (*datamodelv1.Workspace)(nil), // 231: openshell.datamodel.v1.Workspace + (*sandboxv1.GetSandboxConfigRequest)(nil), // 232: openshell.sandbox.v1.GetSandboxConfigRequest + (*sandboxv1.GetGatewayConfigRequest)(nil), // 233: openshell.sandbox.v1.GetGatewayConfigRequest + (*sandboxv1.GetSandboxConfigResponse)(nil), // 234: openshell.sandbox.v1.GetSandboxConfigResponse + (*sandboxv1.GetGatewayConfigResponse)(nil), // 235: openshell.sandbox.v1.GetGatewayConfigResponse } var file_openshell_proto_depIdxs = []int32{ - 193, // 0: openshell.v1.RefreshSandboxTokenResponse.extension_credentials:type_name -> openshell.v1.ExtensionServiceCredential + 194, // 0: openshell.v1.RefreshSandboxTokenResponse.extension_credentials:type_name -> openshell.v1.ExtensionServiceCredential 4, // 1: openshell.v1.HealthResponse.status:type_name -> openshell.v1.ServiceStatus 4, // 2: openshell.v1.GetGatewayInfoResponse.status:type_name -> openshell.v1.ServiceStatus - 16, // 3: openshell.v1.GetGatewayInfoResponse.compute_drivers:type_name -> openshell.v1.ComputeDriverInfo - 17, // 4: openshell.v1.ComputeDriverInfo.capabilities:type_name -> openshell.v1.ComputeDriverCapabilities - 219, // 5: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 19, // 6: openshell.v1.Sandbox.spec:type_name -> openshell.v1.SandboxSpec - 23, // 7: openshell.v1.Sandbox.status:type_name -> openshell.v1.SandboxStatus - 194, // 8: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry - 22, // 9: openshell.v1.SandboxSpec.template:type_name -> openshell.v1.SandboxTemplate - 220, // 10: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 20, // 11: openshell.v1.SandboxSpec.resource_requirements:type_name -> openshell.v1.ResourceRequirements - 21, // 12: openshell.v1.ResourceRequirements.gpu:type_name -> openshell.v1.GpuResourceRequirements - 195, // 13: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry - 196, // 14: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry - 197, // 15: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry - 221, // 16: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct - 221, // 17: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct - 24, // 18: openshell.v1.SandboxStatus.conditions:type_name -> openshell.v1.SandboxCondition - 0, // 19: openshell.v1.SandboxStatus.phase:type_name -> openshell.v1.SandboxPhase - 198, // 20: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry - 19, // 21: openshell.v1.CreateSandboxRequest.spec:type_name -> openshell.v1.SandboxSpec - 199, // 22: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry - 200, // 23: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry - 18, // 24: openshell.v1.SandboxResponse.sandbox:type_name -> openshell.v1.Sandbox - 18, // 25: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox - 222, // 26: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 18, // 27: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 18, // 28: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 50, // 29: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse - 219, // 30: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 49, // 31: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint - 201, // 32: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry - 54, // 33: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout - 55, // 34: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr - 56, // 35: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit - 143, // 36: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget - 144, // 37: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget - 58, // 38: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit - 53, // 39: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest - 61, // 40: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize - 219, // 41: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 18, // 42: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox - 65, // 43: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine - 25, // 44: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent - 66, // 45: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning - 154, // 46: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate - 202, // 47: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry - 222, // 48: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 222, // 49: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 203, // 50: openshell.v1.UpdateProviderRequest.credential_expires_at_ms:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry - 222, // 51: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider - 222, // 52: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 96, // 53: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile - 78, // 54: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride - 83, // 55: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh - 79, // 56: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant - 1, // 57: openshell.v1.ProviderCredentialRefresh.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 81, // 58: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial - 82, // 59: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput - 1, // 60: openshell.v1.ProviderCredentialRefreshStatus.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 219, // 61: openshell.v1.StoredProviderCredentialRefreshState.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 1, // 62: openshell.v1.StoredProviderCredentialRefreshState.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 204, // 63: openshell.v1.StoredProviderCredentialRefreshState.material:type_name -> openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry - 205, // 64: openshell.v1.StoredProviderCredentialRefreshState.additional_output_keys:type_name -> openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry - 206, // 65: openshell.v1.StoredProviderCredentialRefreshState.secret_material_handles:type_name -> openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry - 87, // 66: openshell.v1.StoredProviderCredentialRefreshState.pending_secret_deletions:type_name -> openshell.v1.StoredRefreshMaterialDeletion - 223, // 67: openshell.v1.StoredRefreshMaterialDeletion.handle:type_name -> openshell.datamodel.v1.CredentialHandle - 84, // 68: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 1, // 69: openshell.v1.ConfigureProviderRefreshRequest.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 207, // 70: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - 84, // 71: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 84, // 72: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 2, // 73: openshell.v1.ProviderProfile.category:type_name -> openshell.v1.ProviderProfileCategory - 80, // 74: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential - 224, // 75: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint - 225, // 76: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 85, // 77: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery - 208, // 78: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry - 219, // 79: openshell.v1.StoredProviderProfile.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 96, // 80: openshell.v1.StoredProviderProfile.profile:type_name -> openshell.v1.ProviderProfile - 96, // 81: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile - 96, // 82: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 76, // 83: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 77, // 84: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 96, // 85: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 76, // 86: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem - 77, // 87: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 96, // 88: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile - 76, // 89: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 77, // 90: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 110, // 91: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding - 209, // 92: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - 210, // 93: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expires_at_ms:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry - 211, // 94: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - 212, // 95: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - 220, // 96: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 226, // 97: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue - 114, // 98: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation - 213, // 99: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry - 115, // 100: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule - 116, // 101: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint - 117, // 102: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule - 118, // 103: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules - 119, // 104: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules - 120, // 105: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary - 227, // 106: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 228, // 107: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule - 229, // 108: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule - 214, // 109: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry - 128, // 110: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision - 128, // 111: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision - 3, // 112: openshell.v1.ReportPolicyStatusRequest.status:type_name -> openshell.v1.PolicyStatus - 3, // 113: openshell.v1.SandboxPolicyRevision.status:type_name -> openshell.v1.PolicyStatus - 220, // 114: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 215, // 115: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry - 65, // 116: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine - 65, // 117: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine - 135, // 118: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello - 138, // 119: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat - 147, // 120: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult - 148, // 121: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose - 136, // 122: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted - 137, // 123: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected - 139, // 124: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat - 142, // 125: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen - 148, // 126: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose - 143, // 127: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget - 144, // 128: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget - 145, // 129: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit - 149, // 130: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample - 151, // 131: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount - 227, // 132: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 150, // 133: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary - 153, // 134: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk - 152, // 135: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary - 153, // 136: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk - 227, // 137: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 172, // 138: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry - 220, // 139: openshell.v1.PolicyRevisionPayload.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 216, // 140: openshell.v1.PolicyRevisionPayload.provenance:type_name -> openshell.v1.PolicyRevisionPayload.ProvenanceEntry - 227, // 141: openshell.v1.DraftChunkPayload.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 217, // 142: openshell.v1.StoredPolicyRevision.provenance:type_name -> openshell.v1.StoredPolicyRevision.ProvenanceEntry - 218, // 143: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry - 230, // 144: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 230, // 145: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 230, // 146: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace - 219, // 147: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 5, // 148: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole - 5, // 149: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole - 186, // 150: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember - 186, // 151: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember - 223, // 152: openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry.value:type_name -> openshell.datamodel.v1.CredentialHandle - 80, // 153: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential - 111, // 154: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding - 10, // 155: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest - 12, // 156: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest - 14, // 157: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest - 26, // 158: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest - 27, // 159: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest - 28, // 160: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest - 29, // 161: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest - 30, // 162: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest - 31, // 163: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest - 32, // 164: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest - 33, // 165: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest - 34, // 166: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest - 41, // 167: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest - 43, // 168: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest - 44, // 169: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest - 45, // 170: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest - 47, // 171: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest - 51, // 172: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest - 53, // 173: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest - 59, // 174: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame - 60, // 175: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput - 67, // 176: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest - 68, // 177: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest - 69, // 178: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest - 74, // 179: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest - 75, // 180: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest - 100, // 181: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest - 102, // 182: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest - 104, // 183: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest - 70, // 184: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest - 88, // 185: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest - 90, // 186: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest - 92, // 187: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest - 94, // 188: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest - 71, // 189: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest - 107, // 190: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest - 231, // 191: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest - 232, // 192: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest - 113, // 193: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest - 122, // 194: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest - 124, // 195: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest - 126, // 196: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest - 109, // 197: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest - 129, // 198: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest - 130, // 199: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest - 133, // 200: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage - 140, // 201: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest - 146, // 202: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame - 63, // 203: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest - 155, // 204: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest - 157, // 205: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest - 159, // 206: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest - 161, // 207: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest - 163, // 208: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest - 165, // 209: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest - 167, // 210: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest - 169, // 211: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest - 171, // 212: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest - 6, // 213: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest - 8, // 214: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest - 178, // 215: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest - 180, // 216: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest - 182, // 217: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest - 184, // 218: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest - 187, // 219: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest - 189, // 220: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest - 191, // 221: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest - 11, // 222: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse - 13, // 223: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse - 15, // 224: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse - 35, // 225: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse - 35, // 226: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse - 36, // 227: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse - 37, // 228: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse - 38, // 229: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse - 39, // 230: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse - 40, // 231: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse - 35, // 232: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse - 35, // 233: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse - 42, // 234: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse - 50, // 235: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse - 50, // 236: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse - 46, // 237: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse - 48, // 238: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse - 52, // 239: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse - 57, // 240: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent - 59, // 241: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame - 57, // 242: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent - 72, // 243: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse - 72, // 244: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse - 73, // 245: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse - 99, // 246: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse - 98, // 247: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse - 101, // 248: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse - 103, // 249: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse - 105, // 250: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse - 72, // 251: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse - 89, // 252: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse - 91, // 253: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse - 93, // 254: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse - 95, // 255: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse - 106, // 256: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse - 108, // 257: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse - 233, // 258: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse - 234, // 259: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse - 121, // 260: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse - 123, // 261: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse - 125, // 262: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse - 127, // 263: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse - 112, // 264: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse - 132, // 265: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse - 131, // 266: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse - 134, // 267: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage - 141, // 268: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse - 146, // 269: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame - 64, // 270: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent - 156, // 271: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse - 158, // 272: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse - 160, // 273: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse - 162, // 274: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse - 164, // 275: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse - 166, // 276: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse - 168, // 277: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse - 170, // 278: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse - 173, // 279: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse - 7, // 280: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse - 9, // 281: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse - 179, // 282: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse - 181, // 283: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse - 183, // 284: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse - 185, // 285: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse - 188, // 286: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse - 190, // 287: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse - 192, // 288: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse - 222, // [222:289] is the sub-list for method output_type - 155, // [155:222] is the sub-list for method input_type - 155, // [155:155] is the sub-list for extension type_name - 155, // [155:155] is the sub-list for extension extendee - 0, // [0:155] is the sub-list for field type_name + 17, // 3: openshell.v1.GetGatewayInfoResponse.compute_drivers:type_name -> openshell.v1.ComputeDriverInfo + 18, // 4: openshell.v1.ComputeDriverInfo.capabilities:type_name -> openshell.v1.ComputeDriverCapabilities + 220, // 5: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 20, // 6: openshell.v1.Sandbox.spec:type_name -> openshell.v1.SandboxSpec + 24, // 7: openshell.v1.Sandbox.status:type_name -> openshell.v1.SandboxStatus + 195, // 8: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry + 23, // 9: openshell.v1.SandboxSpec.template:type_name -> openshell.v1.SandboxTemplate + 221, // 10: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 21, // 11: openshell.v1.SandboxSpec.resource_requirements:type_name -> openshell.v1.ResourceRequirements + 6, // 12: openshell.v1.SandboxSpec.restart_policy:type_name -> openshell.v1.SandboxRestartPolicy + 22, // 13: openshell.v1.ResourceRequirements.gpu:type_name -> openshell.v1.GpuResourceRequirements + 196, // 14: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry + 197, // 15: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry + 198, // 16: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry + 222, // 17: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct + 222, // 18: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct + 25, // 19: openshell.v1.SandboxStatus.conditions:type_name -> openshell.v1.SandboxCondition + 0, // 20: openshell.v1.SandboxStatus.phase:type_name -> openshell.v1.SandboxPhase + 199, // 21: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry + 20, // 22: openshell.v1.CreateSandboxRequest.spec:type_name -> openshell.v1.SandboxSpec + 200, // 23: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry + 201, // 24: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry + 19, // 25: openshell.v1.SandboxResponse.sandbox:type_name -> openshell.v1.Sandbox + 19, // 26: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox + 223, // 27: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider + 19, // 28: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox + 19, // 29: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox + 51, // 30: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse + 220, // 31: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 50, // 32: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint + 202, // 33: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry + 55, // 34: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout + 56, // 35: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr + 57, // 36: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit + 144, // 37: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget + 145, // 38: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget + 59, // 39: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit + 54, // 40: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest + 62, // 41: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize + 220, // 42: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 19, // 43: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox + 66, // 44: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine + 26, // 45: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent + 67, // 46: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning + 155, // 47: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate + 203, // 48: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry + 223, // 49: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider + 223, // 50: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider + 204, // 51: openshell.v1.UpdateProviderRequest.credential_expires_at_ms:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry + 223, // 52: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider + 223, // 53: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider + 97, // 54: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile + 79, // 55: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride + 84, // 56: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh + 80, // 57: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant + 1, // 58: openshell.v1.ProviderCredentialRefresh.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy + 82, // 59: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial + 83, // 60: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput + 1, // 61: openshell.v1.ProviderCredentialRefreshStatus.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy + 220, // 62: openshell.v1.StoredProviderCredentialRefreshState.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 1, // 63: openshell.v1.StoredProviderCredentialRefreshState.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy + 205, // 64: openshell.v1.StoredProviderCredentialRefreshState.material:type_name -> openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry + 206, // 65: openshell.v1.StoredProviderCredentialRefreshState.additional_output_keys:type_name -> openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry + 207, // 66: openshell.v1.StoredProviderCredentialRefreshState.secret_material_handles:type_name -> openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry + 88, // 67: openshell.v1.StoredProviderCredentialRefreshState.pending_secret_deletions:type_name -> openshell.v1.StoredRefreshMaterialDeletion + 224, // 68: openshell.v1.StoredRefreshMaterialDeletion.handle:type_name -> openshell.datamodel.v1.CredentialHandle + 85, // 69: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 1, // 70: openshell.v1.ConfigureProviderRefreshRequest.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy + 208, // 71: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry + 85, // 72: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 85, // 73: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 2, // 74: openshell.v1.ProviderProfile.category:type_name -> openshell.v1.ProviderProfileCategory + 81, // 75: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential + 225, // 76: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint + 226, // 77: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary + 86, // 78: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery + 209, // 79: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry + 220, // 80: openshell.v1.StoredProviderProfile.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 97, // 81: openshell.v1.StoredProviderProfile.profile:type_name -> openshell.v1.ProviderProfile + 97, // 82: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile + 97, // 83: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile + 77, // 84: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem + 78, // 85: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 97, // 86: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile + 77, // 87: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem + 78, // 88: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 97, // 89: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile + 77, // 90: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem + 78, // 91: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 111, // 92: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding + 210, // 93: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry + 211, // 94: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expires_at_ms:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry + 212, // 95: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry + 213, // 96: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry + 221, // 97: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 227, // 98: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue + 115, // 99: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation + 214, // 100: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry + 116, // 101: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule + 117, // 102: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint + 118, // 103: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule + 119, // 104: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules + 120, // 105: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules + 121, // 106: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary + 228, // 107: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 229, // 108: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule + 230, // 109: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule + 215, // 110: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry + 129, // 111: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision + 129, // 112: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision + 3, // 113: openshell.v1.ReportPolicyStatusRequest.status:type_name -> openshell.v1.PolicyStatus + 3, // 114: openshell.v1.SandboxPolicyRevision.status:type_name -> openshell.v1.PolicyStatus + 221, // 115: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 216, // 116: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry + 66, // 117: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine + 66, // 118: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine + 136, // 119: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello + 139, // 120: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat + 148, // 121: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult + 149, // 122: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose + 137, // 123: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted + 138, // 124: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected + 140, // 125: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat + 143, // 126: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen + 149, // 127: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose + 144, // 128: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget + 145, // 129: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget + 146, // 130: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit + 150, // 131: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample + 152, // 132: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount + 228, // 133: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 151, // 134: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary + 154, // 135: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk + 153, // 136: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary + 154, // 137: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk + 228, // 138: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 173, // 139: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry + 221, // 140: openshell.v1.PolicyRevisionPayload.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 217, // 141: openshell.v1.PolicyRevisionPayload.provenance:type_name -> openshell.v1.PolicyRevisionPayload.ProvenanceEntry + 228, // 142: openshell.v1.DraftChunkPayload.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 218, // 143: openshell.v1.StoredPolicyRevision.provenance:type_name -> openshell.v1.StoredPolicyRevision.ProvenanceEntry + 219, // 144: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry + 231, // 145: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace + 231, // 146: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace + 231, // 147: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace + 220, // 148: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 5, // 149: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole + 5, // 150: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole + 187, // 151: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember + 187, // 152: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember + 224, // 153: openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry.value:type_name -> openshell.datamodel.v1.CredentialHandle + 81, // 154: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential + 112, // 155: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding + 11, // 156: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest + 13, // 157: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest + 15, // 158: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest + 27, // 159: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest + 28, // 160: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest + 29, // 161: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest + 30, // 162: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest + 31, // 163: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest + 32, // 164: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest + 33, // 165: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest + 34, // 166: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest + 35, // 167: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest + 42, // 168: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest + 44, // 169: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest + 45, // 170: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest + 46, // 171: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest + 48, // 172: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest + 52, // 173: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest + 54, // 174: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest + 60, // 175: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame + 61, // 176: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput + 68, // 177: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest + 69, // 178: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest + 70, // 179: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest + 75, // 180: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest + 76, // 181: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest + 101, // 182: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest + 103, // 183: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest + 105, // 184: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest + 71, // 185: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest + 89, // 186: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest + 91, // 187: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest + 93, // 188: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest + 95, // 189: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest + 72, // 190: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest + 108, // 191: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest + 232, // 192: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest + 233, // 193: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest + 114, // 194: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest + 123, // 195: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest + 125, // 196: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest + 127, // 197: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest + 110, // 198: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest + 130, // 199: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest + 131, // 200: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest + 134, // 201: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage + 141, // 202: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest + 147, // 203: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame + 64, // 204: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest + 156, // 205: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest + 158, // 206: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest + 160, // 207: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest + 162, // 208: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest + 164, // 209: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest + 166, // 210: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest + 168, // 211: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest + 170, // 212: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest + 172, // 213: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest + 7, // 214: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest + 9, // 215: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest + 179, // 216: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest + 181, // 217: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest + 183, // 218: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest + 185, // 219: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest + 188, // 220: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest + 190, // 221: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest + 192, // 222: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest + 12, // 223: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse + 14, // 224: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse + 16, // 225: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse + 36, // 226: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse + 36, // 227: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse + 37, // 228: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse + 38, // 229: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse + 39, // 230: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse + 40, // 231: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse + 41, // 232: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse + 36, // 233: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse + 36, // 234: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse + 43, // 235: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse + 51, // 236: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse + 51, // 237: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse + 47, // 238: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse + 49, // 239: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse + 53, // 240: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse + 58, // 241: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent + 60, // 242: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame + 58, // 243: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent + 73, // 244: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse + 73, // 245: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse + 74, // 246: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse + 100, // 247: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse + 99, // 248: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse + 102, // 249: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse + 104, // 250: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse + 106, // 251: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse + 73, // 252: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse + 90, // 253: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse + 92, // 254: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse + 94, // 255: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse + 96, // 256: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse + 107, // 257: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse + 109, // 258: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse + 234, // 259: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse + 235, // 260: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse + 122, // 261: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse + 124, // 262: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse + 126, // 263: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse + 128, // 264: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse + 113, // 265: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse + 133, // 266: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse + 132, // 267: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse + 135, // 268: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage + 142, // 269: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse + 147, // 270: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame + 65, // 271: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent + 157, // 272: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse + 159, // 273: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse + 161, // 274: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse + 163, // 275: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse + 165, // 276: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse + 167, // 277: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse + 169, // 278: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse + 171, // 279: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse + 174, // 280: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse + 8, // 281: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse + 10, // 282: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse + 180, // 283: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse + 182, // 284: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse + 184, // 285: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse + 186, // 286: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse + 189, // 287: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse + 191, // 288: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse + 193, // 289: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse + 223, // [223:290] is the sub-list for method output_type + 156, // [156:223] is the sub-list for method input_type + 156, // [156:156] is the sub-list for extension type_name + 156, // [156:156] is the sub-list for extension extendee + 0, // [0:156] is the sub-list for field type_name } func init() { file_openshell_proto_init() } @@ -15137,7 +15247,7 @@ func file_openshell_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_openshell_proto_rawDesc), len(file_openshell_proto_rawDesc)), - NumEnums: 6, + NumEnums: 7, NumMessages: 213, NumExtensions: 0, NumServices: 1, diff --git a/sdk/typescript/src/client.test.ts b/sdk/typescript/src/client.test.ts index 5e72c27ba0..89c3abe5f0 100644 --- a/sdk/typescript/src/client.test.ts +++ b/sdk/typescript/src/client.test.ts @@ -20,7 +20,7 @@ import { SCOPE_NAMES, STATUS_NAMES, } from './client.js'; -import { OpenShell, SandboxPhase, ServiceStatus } from './gen/openshell_pb.js'; +import { OpenShell, SandboxPhase, SandboxRestartPolicy, ServiceStatus } from './gen/openshell_pb.js'; import { PolicySource, SettingScope } from './gen/sandbox_pb.js'; function client(impl: Partial>): SandboxClient { @@ -215,6 +215,29 @@ describe('create', () => { expect(created.spec?.policy?.version).toBe(1); }); + it('sends canonical main process and restart policy fields', async () => { + let created: { + spec?: { command?: string[]; tty?: boolean; restartPolicy?: SandboxRestartPolicy }; + } = {}; + const sandbox = client({ + createSandbox: (req) => { + created = req; + return readySandbox('sb', 'sb-id'); + }, + }); + + await sandbox.create({ + image: 'img', + command: ['/opt/worker', '--serve'], + tty: true, + restartPolicy: 'on-failure', + }); + + expect(created.spec?.command).toEqual(['/opt/worker', '--serve']); + expect(created.spec?.tty).toBe(true); + expect(created.spec?.restartPolicy).toBe(SandboxRestartPolicy.ON_FAILURE); + }); + it('rawSpec reaches an ungated field and overrides a curated one', async () => { let created: { spec?: { @@ -248,6 +271,33 @@ describe('create', () => { }); await expect(sandbox.get('sb')).rejects.toMatchObject({ code: 'invalid_config' }); }); + + it('maps restart controller status', async () => { + const sandbox = client({ + getSandbox: () => ({ + sandbox: { + metadata: { id: 'sb-id', name: 'sb', resourceVersion: 8n }, + status: { + phase: SandboxPhase.RESTARTING, + mainProcessInstanceId: 'main-1', + exitCode: 9, + restartCount: 3, + nextRestartAtMs: 1_700_000_000_000n, + mainProcessStartedAtMs: 1_699_999_000_000n, + }, + }, + }), + }); + + await expect(sandbox.get('sb')).resolves.toMatchObject({ + phase: 'restarting', + mainProcessInstanceId: 'main-1', + exitCode: 9, + restartCount: 3, + nextRestartAtMs: 1_700_000_000_000, + mainProcessStartedAtMs: 1_699_999_000_000, + }); + }); }); describe('waits', () => { diff --git a/sdk/typescript/src/client.ts b/sdk/typescript/src/client.ts index 4650e3fdde..c104085a85 100644 --- a/sdk/typescript/src/client.ts +++ b/sdk/typescript/src/client.ts @@ -22,6 +22,7 @@ import { type ExecSandboxInputSchema, OpenShell, SandboxPhase, + SandboxRestartPolicy, type SandboxSpecSchema, ServiceStatus, type TcpForwardFrameSchema, @@ -59,7 +60,11 @@ export type SandboxPhaseName = | 'unknown' | 'stopping' | 'stopped' - | 'starting'; + | 'starting' + | 'restarting'; + +/** Restart behavior after the canonical main process exits. */ +export type SandboxRestartPolicyName = 'never' | 'on-failure' | 'always'; /** Lowercase mirror of the generated `ServiceStatus` enum. Hand-maintained. */ export type HealthStatus = 'unspecified' | 'healthy' | 'degraded' | 'unhealthy'; @@ -82,6 +87,12 @@ export interface SandboxSpec { environment?: Record; providers?: string[]; gpu?: boolean; + /** Exact canonical command. Empty selects the gateway scratch shell. */ + command?: string[]; + /** Allocate a retained pseudo-terminal for the canonical command. */ + tty?: boolean; + /** Restart behavior after the canonical main process exits. */ + restartPolicy?: SandboxRestartPolicyName; /** * Create-time sandbox policy (the safety boundary). Sandbox-scoped * `setPolicy` cannot introduce static fields later, so express filesystem, @@ -105,6 +116,11 @@ export interface SandboxRef { labels: Record; /** u64 rendered as a string — JS numbers can't hold it safely. */ resourceVersion: string; + mainProcessInstanceId?: string; + exitCode?: number; + restartCount: number; + nextRestartAtMs?: number; + mainProcessStartedAtMs?: number; } export interface ListOptions { @@ -287,6 +303,7 @@ export const PHASE_NAMES: Record = { [SandboxPhase.STOPPING]: 'stopping', [SandboxPhase.STOPPED]: 'stopped', [SandboxPhase.STARTING]: 'starting', + [SandboxPhase.RESTARTING]: 'restarting', }; export const STATUS_NAMES: Record = { [ServiceStatus.UNSPECIFIED]: 'unspecified', @@ -308,6 +325,17 @@ export const POLICY_SOURCE_NAMES: Record = { function phaseName(p: SandboxPhase): SandboxPhaseName { return PHASE_NAMES[p] ?? 'unspecified'; } + +function restartPolicyValue(policy: SandboxRestartPolicyName | undefined): SandboxRestartPolicy { + switch (policy) { + case 'on-failure': + return SandboxRestartPolicy.ON_FAILURE; + case 'always': + return SandboxRestartPolicy.ALWAYS; + default: + return SandboxRestartPolicy.NEVER; + } +} function statusName(s: ServiceStatus): HealthStatus { return STATUS_NAMES[s] ?? 'unspecified'; } @@ -330,6 +358,15 @@ function sandboxRef(sandbox: Sandbox | undefined): SandboxRef { phase: phaseName(sandbox.status?.phase ?? SandboxPhase.UNSPECIFIED), labels: meta?.labels ?? {}, resourceVersion: (meta?.resourceVersion ?? 0n).toString(), + mainProcessInstanceId: sandbox.status?.mainProcessInstanceId || undefined, + exitCode: sandbox.status?.exitCode, + restartCount: sandbox.status?.restartCount ?? 0, + nextRestartAtMs: + sandbox.status && sandbox.status.nextRestartAtMs > 0n ? Number(sandbox.status.nextRestartAtMs) : undefined, + mainProcessStartedAtMs: + sandbox.status && sandbox.status.mainProcessStartedAtMs > 0n + ? Number(sandbox.status.mainProcessStartedAtMs) + : undefined, }; } @@ -561,6 +598,9 @@ export class SandboxClient { template: spec.image ? { image: spec.image } : undefined, resourceRequirements: spec.gpu ? { gpu: {} } : undefined, policy: spec.policy, + command: spec.command ?? [], + tty: spec.tty ?? false, + restartPolicy: restartPolicyValue(spec.restartPolicy), }; if (spec.rawSpec) Object.assign(specInit, spec.rawSpec); diff --git a/sdk/typescript/src/index.ts b/sdk/typescript/src/index.ts index 6dae221887..a46b905396 100644 --- a/sdk/typescript/src/index.ts +++ b/sdk/typescript/src/index.ts @@ -31,6 +31,7 @@ export type { SandboxPhaseName, SandboxPolicy, SandboxRef, + SandboxRestartPolicyName, SandboxSpec, SetPolicyOptions, SettingScopeName, From 31bc2c14055a5538ef46cdaff66e888d765eabbf Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Tue, 18 Aug 2026 17:26:12 -0700 Subject: [PATCH 2/2] fix(sandbox): harden policy-driven restarts Signed-off-by: Drew Newberry --- .agents/skills/openshell-cli/cli-reference.md | 6 ++ .agents/skills/tui-development/SKILL.md | 4 +- crates/openshell-driver-vm/src/driver.rs | 72 +++++++++++++++---- crates/openshell-server/src/compute/mod.rs | 37 +++++++++- 4 files changed, 100 insertions(+), 19 deletions(-) diff --git a/.agents/skills/openshell-cli/cli-reference.md b/.agents/skills/openshell-cli/cli-reference.md index 61beab95f1..f3d097e5c7 100644 --- a/.agents/skills/openshell-cli/cli-reference.md +++ b/.agents/skills/openshell-cli/cli-reference.md @@ -215,6 +215,7 @@ without one, the default is `/bin/bash -l` with a PTY. | `--from ` | Community name, Dockerfile path, directory, or image reference (BYOC) | | `--no-keep` | Delete the sandbox after the initial command or shell exits | | `--detach` | Start the canonical main process without attaching | +| `--restart-policy never|on-failure|always` | Restart behavior for canonical main exit; default: `never` | | `--editor vscode|cursor` | Launch a remote editor and keep the sandbox alive | | `--gpu [COUNT]` | Request the driver's default GPU selection or a specific count | | `--cpu ` | CPU limit (for example: `500m`, `1`, `2.5`) | @@ -294,6 +295,11 @@ new shell. The name defaults to the last-used sandbox. `--editor vscode|cursor` launches a supported remote editor instead of attaching to the canonical main process. +With `on-failure` or `always`, a main-process exit moves the sandbox through +`Restarting` while the gateway applies exponential backoff and replaces the +runtime. A successful replacement returns to `Ready` with a new main-process +instance and empty PTY history. + ### `openshell sandbox upload [dest]` Upload files using tar-over-SSH. The CLI discovers the canonical remote working directory when the destination is omitted. A named directory merges into an existing directory of the same name, overwriting matching entries without deleting unrelated entries. `.gitignore` filtering is enabled unless `--no-git-ignore` is passed. diff --git a/.agents/skills/tui-development/SKILL.md b/.agents/skills/tui-development/SKILL.md index 7f11db26ff..dbc1fb067e 100644 --- a/.agents/skills/tui-development/SKILL.md +++ b/.agents/skills/tui-development/SKILL.md @@ -258,7 +258,7 @@ The `Theme` struct has 16 `Style` fields, accessed at runtime via `app.theme`: | `border` | EVERGLADE fg | Light sage fg | Unfocused panel borders | | `border_focused` | NVIDIA_GREEN fg | NVIDIA_GREEN_DARK fg | Focused panel borders | | `status_ok` | NVIDIA_GREEN fg | NVIDIA_GREEN_DARK fg | Healthy, INFO, Ready | -| `status_warn` | Yellow fg | Dark yellow fg | Degraded, WARN, Provisioning | +| `status_warn` | Yellow fg | Dark yellow fg | Degraded, WARN, Provisioning, Restarting | | `status_err` | Red fg | Dark red fg | Unhealthy, ERROR | | `key_hint` | NVIDIA_GREEN fg | NVIDIA_GREEN_DARK fg | Keyboard shortcut labels | | `log_cursor` | EVERGLADE bg | Light green bg | Selected log line highlight | @@ -293,7 +293,7 @@ fn draw_detail_popup(frame: &mut Frame<'_>, data: &MyData, area: Rect, theme: &T - **Selected row**: Green `▌` left-border marker on the selected row. Active gateway also gets a green `●` dot. - **Focused panel**: Border changes from `border` to `border_focused` style. -- **Status indicators**: Green for healthy/ready/info, yellow for degraded/provisioning/warn, red for unhealthy/error. +- **Status indicators**: Green for healthy/ready/info, yellow for degraded/provisioning/restarting/warn, red for unhealthy/error. - **Separators**: Muted `│` characters between title bar segments and nav bar sections. - **Log source labels**: `"sandbox"` source renders in `accent` (green), `"gateway"` in `muted`. diff --git a/crates/openshell-driver-vm/src/driver.rs b/crates/openshell-driver-vm/src/driver.rs index faa477e123..180222cf04 100644 --- a/crates/openshell-driver-vm/src/driver.rs +++ b/crates/openshell-driver-vm/src/driver.rs @@ -1529,21 +1529,15 @@ impl VmDriver { ); } - if clear_stop_marker { - match tokio::fs::remove_file(state_dir.join(SANDBOX_STOPPED_FILE)).await { - Ok(()) => {} - Err(err) if err.kind() == std::io::ErrorKind::NotFound => {} - Err(err) => { - self.registry.lock().await.remove(&sandbox.id); - warn!( - sandbox_id = %sandbox.id, - state_dir = %state_dir.display(), - error = %err, - "vm driver: cannot clear stop marker for persisted sandbox restore" - ); - return false; - } - } + if clear_stop_marker && let Err(err) = clear_explicit_start_markers(&state_dir).await { + self.registry.lock().await.remove(&sandbox.id); + warn!( + sandbox_id = %sandbox.id, + state_dir = %state_dir.display(), + error = %err, + "vm driver: cannot clear lifecycle markers for persisted sandbox restore" + ); + return false; } self.publish_platform_event( @@ -4830,6 +4824,19 @@ async fn write_private_file(path: &Path, bytes: Vec) -> Result<(), std::io:: restrict_owner_read_write(path).await } +async fn clear_explicit_start_markers(state_dir: &Path) -> Result<(), std::io::Error> { + // Remove the terminal marker first. If clearing the durable stop marker + // fails, the sandbox remains stopped and a later start can safely retry. + for marker in [MAIN_PROCESS_EXITED_FILE, SANDBOX_STOPPED_FILE] { + match tokio::fs::remove_file(state_dir.join(marker)).await { + Ok(()) => {} + Err(err) if err.kind() == std::io::ErrorKind::NotFound => {} + Err(err) => return Err(err), + } + } + Ok(()) +} + #[cfg(unix)] async fn restrict_owner_read_write(path: &Path) -> Result<(), std::io::Error> { tokio::fs::set_permissions(path, fs::Permissions::from_mode(0o600)).await @@ -6717,6 +6724,9 @@ mod tests { tokio::fs::write(state_dir.join(SANDBOX_STOPPED_FILE), b"stopped\n") .await .unwrap(); + tokio::fs::write(state_dir.join(MAIN_PROCESS_EXITED_FILE), b"terminal\n") + .await + .unwrap(); let snapshot = sandbox_snapshot(&sandbox, stopped_condition(), false); driver.registry.lock().await.insert( sandbox.id.clone(), @@ -6743,6 +6753,12 @@ mod tests { .is_ok(), "failed start must retain its durable stop marker" ); + assert!( + tokio::fs::metadata(state_dir.join(MAIN_PROCESS_EXITED_FILE)) + .await + .is_ok(), + "failed start must retain its terminal marker" + ); let restored = driver .get_sandbox(&sandbox.id, &sandbox.name) .await @@ -6757,6 +6773,32 @@ mod tests { assert_eq!(condition.status, "True"); } + #[tokio::test] + async fn explicit_start_clears_stop_and_terminal_markers() { + let temp = tempfile::tempdir().unwrap(); + let state_dir = temp.path().join("sandbox"); + tokio::fs::create_dir_all(&state_dir).await.unwrap(); + tokio::fs::write(state_dir.join(SANDBOX_STOPPED_FILE), b"stopped\n") + .await + .unwrap(); + tokio::fs::write(state_dir.join(MAIN_PROCESS_EXITED_FILE), b"terminal\n") + .await + .unwrap(); + + clear_explicit_start_markers(&state_dir).await.unwrap(); + + assert!( + !tokio::fs::try_exists(state_dir.join(SANDBOX_STOPPED_FILE)) + .await + .unwrap() + ); + assert!( + !tokio::fs::try_exists(state_dir.join(MAIN_PROCESS_EXITED_FILE)) + .await + .unwrap() + ); + } + #[test] fn prepare_sandbox_overlay_preserves_existing_overlay_on_start() { let base = unique_temp_dir(); diff --git a/crates/openshell-server/src/compute/mod.rs b/crates/openshell-server/src/compute/mod.rs index 354a69fcea..4065d8de05 100644 --- a/crates/openshell-server/src/compute/mod.rs +++ b/crates/openshell-server/src/compute/mod.rs @@ -3111,10 +3111,14 @@ impl ComputeRuntime { if connected { ensure_supervisor_ready_status(&mut sandbox.status, &sandbox_name); let status = sandbox.status.get_or_insert_with(Default::default); - status.main_process_instance_id = instance_id.unwrap_or_default().to_string(); + let next_instance_id = instance_id.unwrap_or_default(); + let started_new_main = status.main_process_instance_id != next_instance_id; + status.main_process_instance_id = next_instance_id.to_string(); status.exit_code = None; status.next_restart_at_ms = 0; - status.main_process_started_at_ms = openshell_core::time::now_ms(); + if started_new_main { + status.main_process_started_at_ms = openshell_core::time::now_ms(); + } sandbox.set_phase(SandboxPhase::Ready as i32); } else { ensure_supervisor_not_ready_status(&mut sandbox.status, &sandbox_name); @@ -5430,6 +5434,35 @@ mod tests { ); } + #[tokio::test] + async fn same_main_reconnect_preserves_process_start_time() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let mut sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Ready); + sandbox.status = Some(SandboxStatus { + phase: SandboxPhase::Ready as i32, + main_process_instance_id: "instance-1".into(), + main_process_started_at_ms: 12_345, + ..Default::default() + }); + runtime.store.put_message(&sandbox).await.unwrap(); + + runtime + .supervisor_session_connected("sb-1", "instance-1") + .await + .unwrap(); + + let reconnected = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + assert_eq!( + reconnected.status.unwrap().main_process_started_at_ms, + 12_345 + ); + } + #[tokio::test] async fn on_failure_policy_keeps_zero_exit_terminal() { let runtime = test_runtime(Arc::new(TestDriver::default())).await;