diff --git a/crates/client/src/backend_runtime.rs b/crates/client/src/backend_runtime.rs index 515b27c73..0ab75ab80 100644 --- a/crates/client/src/backend_runtime.rs +++ b/crates/client/src/backend_runtime.rs @@ -88,28 +88,25 @@ impl BackendRuntimeTarget { content: Vec, ) -> Result { let api = BackendApiClient::from_stored_token(&self.base_url)?; + let client = runtime_server_api_client(&self.base_url, &api)?; + let grant = client + .runtime_worker_attachment_upload_grant( + self.workspace_id.clone(), + self.runtime_id.clone(), + self.worker_id.clone(), + server_api::WorkerFileUploadQuery { + file_name: file_name.to_string(), + media_type: media_type.to_string(), + upload_id: Some(upload_id.to_string()), + }, + ) + .await?; let worker_path = format!( "/api/w/{}/runtimes/{}/workers/{}", path_segment_encode(&self.workspace_id), path_segment_encode(&self.runtime_id), path_segment_encode(&self.worker_id), ); - let grant_path = format!( - "{worker_path}/attachment-upload-grants?file_name={}&media_type={}&upload_id={}", - path_segment_encode(file_name), - path_segment_encode(media_type), - path_segment_encode(&upload_id), - ); - let grant_response = api - .request(HttpMethod::POST, &grant_path)? - .send() - .await - .map_err(BackendRuntimeClientError::Http)?; - api.check_status(grant_response.status())?; - let grant = grant_response - .json::() - .await - .map_err(BackendRuntimeClientError::Http)?; let upload_path = format!( "{worker_path}/attachment-uploads/{}", path_segment_encode(&grant.upload_id), @@ -133,19 +130,14 @@ impl BackendRuntimeTarget { upload_id: &str, ) -> Result<(), BackendRuntimeClientError> { let api = BackendApiClient::from_stored_token(&self.base_url)?; - let path = format!( - "/api/w/{}/runtimes/{}/workers/{}/attachment-uploads/{}", - path_segment_encode(&self.workspace_id), - path_segment_encode(&self.runtime_id), - path_segment_encode(&self.worker_id), - path_segment_encode(upload_id), - ); - let response = api - .request(HttpMethod::DELETE, &path)? - .send() - .await - .map_err(BackendRuntimeClientError::Http)?; - api.check_status(response.status())?; + runtime_server_api_client(&self.base_url, &api)? + .runtime_worker_attachment_upload_cancel( + self.workspace_id.clone(), + self.runtime_id.clone(), + self.worker_id.clone(), + upload_id.to_string(), + ) + .await?; Ok(()) } @@ -154,30 +146,18 @@ impl BackendRuntimeTarget { artifact_id: &str, ) -> Result<(), BackendRuntimeClientError> { let api = BackendApiClient::from_stored_token(&self.base_url)?; - let path = format!( - "/api/w/{}/runtimes/{}/workers/{}/attachments/{}", - path_segment_encode(&self.workspace_id), - path_segment_encode(&self.runtime_id), - path_segment_encode(&self.worker_id), - path_segment_encode(artifact_id), - ); - let response = api - .request(HttpMethod::DELETE, &path)? - .send() - .await - .map_err(BackendRuntimeClientError::Http)?; - api.check_status(response.status())?; + runtime_server_api_client(&self.base_url, &api)? + .runtime_worker_attachment_delete( + self.workspace_id.clone(), + self.runtime_id.clone(), + self.worker_id.clone(), + artifact_id.to_string(), + ) + .await?; Ok(()) } } -#[derive(Deserialize)] -struct AttachmentUploadGrantResponse { - upload_id: String, - #[allow(dead_code)] - expires_at_ms: u64, -} - #[derive(Deserialize)] struct UploadedFileResponse { file: protocol::UploadedFileRef, @@ -280,6 +260,7 @@ pub enum BackendRuntimeClientError { InvalidTarget(String), Api(BackendApiClientError), SessionApi(server_api::ServerApiClientError), + ContractApi(server_api::client_support::ClientError), Http(reqwest::Error), Protocol(String), } @@ -290,6 +271,7 @@ impl fmt::Display for BackendRuntimeClientError { Self::InvalidTarget(message) => f.write_str(message), Self::Api(error) => write!(f, "{error}"), Self::SessionApi(error) => write!(f, "{error}"), + Self::ContractApi(error) => write!(f, "{error}"), Self::Http(error) => write!(f, "{error}"), Self::Protocol(message) => f.write_str(message), } @@ -304,6 +286,16 @@ impl From for BackendRuntimeClientError { } } +impl From> + for BackendRuntimeClientError +{ + fn from( + value: server_api::client_support::ClientError, + ) -> Self { + Self::ContractApi(value) + } +} + impl From for BackendRuntimeClientError { fn from(error: BackendApiClientError) -> Self { Self::Api(error) @@ -318,6 +310,34 @@ impl From for BackendRuntimeClientError { const WORKER_SESSION_RESPONSE_LIMIT: usize = 16 * 1024 * 1024; +fn runtime_server_api_client( + base_url: &str, + backend: &BackendApiClient, +) -> Result, BackendRuntimeClientError> { + server_api::ServerApiClient::builder(base_url) + .map_err(|error| BackendRuntimeClientError::Protocol(error.to_string()))? + .client(backend.asynchronous_client()) + .authorizer(StoredBearerAuthorizer { + authorization: backend.authorization_header_value(), + }) + .response_body_limit(WORKER_SESSION_RESPONSE_LIMIT) + .build() + .map_err(|error| BackendRuntimeClientError::Protocol(error.to_string())) +} + +fn server_api_error_is_auth( + error: &server_api::client_support::ClientError, +) -> bool { + matches!( + error, + server_api::client_support::ClientError::Public { status, .. } + if matches!( + *status, + reqwest::StatusCode::UNAUTHORIZED | reqwest::StatusCode::FORBIDDEN + ) + ) +} + #[derive(Clone, Debug)] struct StoredBearerAuthorizer { authorization: String, @@ -351,15 +371,7 @@ async fn observe_backend_worker_session_with_client( target: &BackendRuntimeTarget, backend: &BackendApiClient, ) -> Result { - let client = server_api::ServerApiClient::builder(&target.base_url) - .map_err(|error| BackendRuntimeClientError::Protocol(error.to_string()))? - .authorizer(StoredBearerAuthorizer { - authorization: backend.authorization_header_value(), - }) - .response_body_limit(WORKER_SESSION_RESPONSE_LIMIT) - .build() - .map_err(|error| BackendRuntimeClientError::Protocol(error.to_string()))?; - client + runtime_server_api_client(&target.base_url, backend)? .worker_session( target.workspace_id.clone(), target.runtime_id.clone(), @@ -426,80 +438,50 @@ pub async fn list_backend_workers( ) -> Result, BackendRuntimeClientError> { validate_list_target(target)?; let api = BackendApiClient::from_stored_token(&target.base_url)?; + let client = runtime_server_api_client(&target.base_url, &api)?; + let workspace_id = target + .workspace_id + .as_deref() + .expect("validated Backend Workspace scope"); if let Some(runtime_id) = target.runtime_id.as_deref() { - let path = backend_runtime_workers_path( - target - .workspace_id - .as_deref() - .expect("validated Backend Workspace scope"), - runtime_id, - ); - let response = api.request(HttpMethod::GET, &path)?.send().await?; - api.check_status(response.status())?; - return Ok(response - .json::>() - .await?); + return client + .runtime_worker_list( + workspace_id.to_string(), + runtime_id.to_string(), + server_api::RuntimeWorkersQuery::default(), + ) + .await + .map(runtime_worker_list_response) + .map_err(Into::into); } - let runtime_path = backend_runtimes_path( - target - .workspace_id - .as_deref() - .expect("validated Backend Workspace scope"), - ); - let response = api.request(HttpMethod::GET, &runtime_path)?.send().await?; - api.check_status(response.status())?; - let runtimes = response - .json::>() - .await?; - + let runtimes = client.runtime_list(workspace_id.to_string()).await?; let mut items = Vec::new(); let mut diagnostics = runtimes.diagnostics; - for runtime in runtimes.items { - let path = backend_runtime_workers_path( - target - .workspace_id - .as_deref() - .expect("validated Backend Workspace scope"), - &runtime.runtime_id, - ); - let response = match api.request(HttpMethod::GET, &path)?.send().await { - Ok(response) => response, - Err(error) => { - diagnostics.push(BackendDiagnostic { - code: "runtime_worker_list_failed".to_string(), - severity: BackendDiagnosticSeverity::Error, - message: format!( - "failed to list workers for runtime {}: {error}", - runtime.runtime_id - ), - }); - continue; + for resource in runtimes.items { + let runtime = resource.runtime; + match client + .runtime_worker_list( + workspace_id.to_string(), + runtime.runtime_id.clone(), + server_api::RuntimeWorkersQuery::default(), + ) + .await + { + Ok(response) => { + diagnostics.extend(response.diagnostics); + items.extend(response.items); } - }; - if matches!( - response.status(), - reqwest::StatusCode::UNAUTHORIZED | reqwest::StatusCode::FORBIDDEN - ) { - api.check_status(response.status())?; - } - if !response.status().is_success() { - diagnostics.push(BackendDiagnostic { + Err(error) if server_api_error_is_auth(&error) => return Err(error.into()), + Err(error) => diagnostics.push(BackendDiagnostic { code: "runtime_worker_list_failed".to_string(), severity: BackendDiagnosticSeverity::Error, message: format!( - "failed to list workers for runtime {}: Backend returned HTTP {}", - runtime.runtime_id, - response.status().as_u16() + "failed to list workers for runtime {}: {error}", + runtime.runtime_id ), - }); - continue; + }), } - let response = response - .json::>() - .await?; - diagnostics.extend(response.diagnostics); - items.extend(response.items); } Ok(BackendRuntimeListResponse { @@ -521,21 +503,21 @@ pub async fn list_backend_stopped_workers( )); }; let api = BackendApiClient::from_stored_token(&target.base_url)?; - let path = backend_runtime_workers_path( - target - .workspace_id - .as_deref() - .expect("validated Backend Workspace scope"), - runtime_id, - ); - let response = api - .request(HttpMethod::GET, &format!("{path}?status=stopped"))? - .send() - .await?; - api.check_status(response.status())?; - Ok(response - .json::>() - .await?) + runtime_server_api_client(&target.base_url, &api)? + .runtime_worker_list( + target + .workspace_id + .as_deref() + .expect("validated Backend Workspace scope") + .to_string(), + runtime_id.to_string(), + server_api::RuntimeWorkersQuery { + status: Some(server_api::RuntimeWorkersStatusFilter::Stopped), + }, + ) + .await + .map(runtime_worker_list_response) + .map_err(Into::into) } pub async fn restore_backend_worker( @@ -543,18 +525,18 @@ pub async fn restore_backend_worker( ) -> Result { validate_target(target)?; let api = BackendApiClient::from_stored_token(&target.base_url)?; - let path = backend_runtime_worker_restore_path( - &target.workspace_id, - &target.runtime_id, - &target.worker_id, - ); - let response = api - .request(HttpMethod::POST, &path)? - .json(&serde_json::json!({})) - .send() - .await?; - let response = api.require_success(response).await?; - Ok(response.json::().await?) + runtime_server_api_client(&target.base_url, &api)? + .runtime_worker_restore( + target.workspace_id.clone(), + target.runtime_id.clone(), + target.worker_id.clone(), + server_api::RestoreTicketAssignmentQuery { + ticket_id: None, + assignment_operation_id: None, + }, + ) + .await + .map_err(Into::into) } pub async fn connect_backend_runtime( @@ -599,6 +581,18 @@ fn protocol_connect_error_message( ) } +fn runtime_worker_list_response( + response: server_api::RuntimeWorkerListResponse, +) -> BackendRuntimeListResponse { + BackendRuntimeListResponse { + workspace_id: response.workspace_id, + limit: response.limit, + items: response.items, + source: response.source, + diagnostics: response.diagnostics, + } +} + fn validate_target(target: &BackendRuntimeTarget) -> Result<(), BackendRuntimeClientError> { if target.base_url.trim().is_empty() { return Err(BackendRuntimeClientError::InvalidTarget( @@ -697,31 +691,6 @@ fn backend_workspace_workers_launch_options_path(workspace_id: &str) -> String { ) } -fn backend_runtimes_path(workspace_id: &str) -> String { - format!("/api/w/{}/runtimes", path_segment_encode(workspace_id)) -} - -fn backend_runtime_workers_path(workspace_id: &str, runtime_id: &str) -> String { - format!( - "/api/w/{}/runtimes/{}/workers", - path_segment_encode(workspace_id), - path_segment_encode(runtime_id) - ) -} - -fn backend_runtime_worker_restore_path( - workspace_id: &str, - runtime_id: &str, - worker_id: &str, -) -> String { - format!( - "/api/w/{}/runtimes/{}/workers/{}/restore", - path_segment_encode(workspace_id), - path_segment_encode(runtime_id), - path_segment_encode(worker_id) - ) -} - fn protocol_ws_request( target: &BackendRuntimeTarget, api: &BackendApiClient, @@ -1051,21 +1020,4 @@ mod tests { serde_json::json!(64); assert!(serde_json::from_value::(stale).is_err()); } - - #[test] - fn workers_path_requires_workspace_scope_for_status_queries() { - let path = backend_runtime_workers_path("team main", "runtime/one"); - assert_eq!( - format!("{path}?status=stopped"), - "/api/w/team%20main/runtimes/runtime%2Fone/workers?status=stopped" - ); - } - - #[test] - fn restore_worker_path_requires_workspace_scope() { - assert_eq!( - backend_runtime_worker_restore_path("team main", "runtime/one", "worker one"), - "/api/w/team%20main/runtimes/runtime%2Fone/workers/worker%20one/restore" - ); - } } diff --git a/crates/client/src/backend_workspace.rs b/crates/client/src/backend_workspace.rs index 35536fa23..639e7cc75 100644 --- a/crates/client/src/backend_workspace.rs +++ b/crates/client/src/backend_workspace.rs @@ -31,6 +31,9 @@ pub enum BackendWorkspaceClientError { Api(BackendApiClientError), Http(reqwest::Error), ServerApi(server_api::client_support::ClientError), + RuntimeManagementApi( + server_api::client_support::ClientError, + ), } impl fmt::Display for BackendWorkspaceClientError { @@ -40,6 +43,7 @@ impl fmt::Display for BackendWorkspaceClientError { Self::Api(error) => write!(f, "{error}"), Self::Http(error) => write!(f, "{error}"), Self::ServerApi(error) => write!(f, "{error}"), + Self::RuntimeManagementApi(error) => write!(f, "{error}"), } } } @@ -91,6 +95,29 @@ pub(crate) fn server_client_error( } } +pub(crate) fn runtime_management_client_error( + backend: &BackendApiClient, + error: server_api::client_support::ClientError, +) -> BackendWorkspaceClientError { + let status = match &error { + server_api::client_support::ClientError::Public { status, .. } => Some(*status), + server_api::client_support::ClientError::Failure(_) => None, + }; + match status { + Some(reqwest::StatusCode::UNAUTHORIZED) => { + BackendWorkspaceClientError::Api(BackendApiClientError::Unauthorized { + origin: backend.origin().clone(), + }) + } + Some(reqwest::StatusCode::FORBIDDEN) => { + BackendWorkspaceClientError::Api(BackendApiClientError::Forbidden { + origin: backend.origin().clone(), + }) + } + _ => BackendWorkspaceClientError::RuntimeManagementApi(error), + } +} + #[derive(Clone, Debug)] pub(crate) struct ServerBearerAuthorizer { authorization: String, diff --git a/crates/client/src/workspace_product.rs b/crates/client/src/workspace_product.rs index e561b9a44..3676e0337 100644 --- a/crates/client/src/workspace_product.rs +++ b/crates/client/src/workspace_product.rs @@ -345,24 +345,40 @@ impl BackendWorkspaceProductClient { pub fn list_runtimes( &self, ) -> Result, BackendWorkspaceClientError> { - self.get_json("/runtimes") + let workspace_id = self.workspace_id.clone(); + let response = + self.generated(move |client| async move { client.runtime_list(workspace_id).await })?; + Ok(ListResponse { + workspace_id: response.workspace_id, + limit: response.limit, + items: response.items, + source: response.source, + diagnostics: response.diagnostics, + }) } pub fn runtime_detail( &self, runtime_id: &str, ) -> Result { - self.get_json(&format!("/runtimes/{}", encode_path_segment(runtime_id))) + let workspace_id = self.workspace_id.clone(); + let runtime_id = runtime_id.to_string(); + self.generated(move |client| async move { + client.runtime_detail(workspace_id, runtime_id).await + }) } pub fn reveal_runtime_trust_key( &self, runtime_id: &str, ) -> Result { - self.get_json(&format!( - "/runtimes/{}/trust-key", - encode_path_segment(runtime_id) - )) + let workspace_id = self.workspace_id.clone(); + let runtime_id = runtime_id.to_string(); + self.generated(move |client| async move { + client + .runtime_trust_key_reveal(workspace_id, runtime_id) + .await + }) } pub fn revoke_runtime_trust_key( @@ -370,11 +386,20 @@ impl BackendWorkspaceProductClient { runtime_id: &str, request: &RevokeRuntimeTrustKeyRequest, ) -> Result { - self.send_json( - Method::DELETE, - &format!("/runtimes/{}/trust-key", encode_path_segment(runtime_id)), - Some(request), - ) + let client = crate::backend_workspace::server_api_client(&self.api)?; + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .map_err(|error| BackendWorkspaceClientError::InvalidTarget(error.to_string()))?; + runtime + .block_on(client.runtime_trust_key_revoke( + self.workspace_id.clone(), + runtime_id.to_string(), + request.clone(), + )) + .map_err(|error| { + crate::backend_workspace::runtime_management_client_error(&self.api, error) + }) } pub fn memory_document(&self) -> Result { diff --git a/crates/server-api/examples/generate_runtime_api_types.rs b/crates/server-api/examples/generate_runtime_api_types.rs new file mode 100644 index 000000000..69305365a --- /dev/null +++ b/crates/server-api/examples/generate_runtime_api_types.rs @@ -0,0 +1,59 @@ +use std::fs; +use std::path::Path; + +use server_api::repository_openapi_typescript::{ + INPUT_PATH, RUNTIME_OUTPUT_PATH, generate_runtime_typescript, +}; + +fn main() { + if let Err(error) = run() { + eprintln!("{error}"); + std::process::exit(1); + } +} + +fn run() -> Result<(), String> { + let mut check = false; + for argument in std::env::args().skip(1) { + match argument.as_str() { + "--check" => check = true, + "--help" | "-h" => { + println!( + "Generate the bounded Runtime API TypeScript artifact.\n\n\ + Usage: cargo run -q -p server-api --example generate_runtime_api_types -- [--check]" + ); + return Ok(()); + } + other => return Err(format!("unknown argument `{other}`")), + } + } + + let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."); + let input_path = root.join(INPUT_PATH); + let output_path = root.join(RUNTIME_OUTPUT_PATH); + let input = fs::read_to_string(&input_path) + .map_err(|error| format!("failed to read {}: {error}", input_path.display()))?; + let generated = generate_runtime_typescript(&input) + .map_err(|error| format!("failed to generate Runtime API types: {error}"))?; + + if check { + let checked_in = fs::read_to_string(&output_path) + .map_err(|error| format!("failed to read {}: {error}", output_path.display()))?; + if checked_in != generated { + return Err(format!( + "{} is stale; regenerate it without --check", + output_path.display() + )); + } + println!("{} is current", output_path.display()); + return Ok(()); + } + + let temporary_path = output_path.with_extension("ts.tmp"); + fs::write(&temporary_path, generated) + .map_err(|error| format!("failed to write {}: {error}", temporary_path.display()))?; + fs::rename(&temporary_path, &output_path) + .map_err(|error| format!("failed to replace {}: {error}", output_path.display()))?; + println!("generated {}", output_path.display()); + Ok(()) +} diff --git a/crates/server-api/src/lib.rs b/crates/server-api/src/lib.rs index 6943e3a87..e93c931a3 100644 --- a/crates/server-api/src/lib.rs +++ b/crates/server-api/src/lib.rs @@ -300,6 +300,42 @@ impl_openapi_schema!( TicketStateChangeRequest, TicketSummarySearchQuery, TicketThreadEventRequest, + RuntimeManagementApiError, + RuntimeWorkersQuery, + RuntimeWorkerSpawnRequest, + RuntimeWorkerSpawnResponse, + RuntimeConfigBundleSyncRequest, + RuntimeConfigBundleAvailabilityQuery, + RuntimeConfigBundleResult, + RuntimeWorkerShowResponse, + RuntimeWorkerInputRequest, + RuntimeWorkerInputResult, + RuntimeWorkerLifecycleRequest, + RuntimeWorkerLifecycleResult, + RuntimeWorkerCompletionsRequest, + RuntimeWorkerCompletionsResult, + RestoreTicketAssignmentQuery, + RuntimeCleanupPlanResponse, + ExecuteRuntimeCleanupRequest, + RuntimeCleanupExecutionResponse, + WorkerRetentionResponse, + WorkerFileUploadQuery, + AttachmentUploadGrantResponse, + AttachmentUploadCancelResponse, + WorkerFileDeleteResponse, + CreateRemoteRuntimeRequest, + WorkspaceRuntimeResource, + WorkspaceRuntimeDetail, + UpdateRemoteRuntimeRequest, + RemoveRuntimeRequest, + RuntimeRemovalOperationResponse, + RuntimeTrustKeyRevealResponse, + RevokeRuntimeTrustKeyRequest, + RuntimeConnectionTestResponse, + RuntimeListResponse, + WorkspaceRuntimeListResponse, + RuntimeWorkerListResponse, + WorkerRestoreResponse, ); #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -992,6 +1028,547 @@ pub trait ServerApi { #[path] worker_id: String, ) -> Result; + #[get( + "/api/runtimes", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_list_alias(&self) -> Result; + + #[get( + "/api/w/{workspace_id}/runtimes", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_list( + &self, + #[path] workspace_id: String, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes", + status = 201, + alternate_status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_create( + &self, + #[extension] actor: RequestActor, + #[path] workspace_id: String, + #[body] request: CreateRemoteRuntimeRequest, + ) -> Result; + + #[get( + "/api/w/{workspace_id}/runtimes/{runtime_id}", + status = 200, + error_status = 404, + additional_error_statuses = [400, 401, 403, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_detail( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_update( + &self, + #[extension] actor: RequestActor, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[body] request: UpdateRemoteRuntimeRequest, + ) -> Result; + + #[delete( + "/api/w/{workspace_id}/runtimes/{runtime_id}", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_remove( + &self, + #[extension] actor: RequestActor, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[body] request: RemoveRuntimeRequest, + ) -> Result; + + #[get( + "/api/w/{workspace_id}/runtimes/{runtime_id}/trust-key", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 500], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_trust_key_reveal( + &self, + #[extension] actor: RequestActor, + #[path] workspace_id: String, + #[path] runtime_id: String, + ) -> Result; + + #[delete( + "/api/w/{workspace_id}/runtimes/{runtime_id}/trust-key", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_trust_key_revoke( + &self, + #[extension] actor: RequestActor, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[body] request: RevokeRuntimeTrustKeyRequest, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}/connection-tests", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_connection_test( + &self, + #[extension] actor: RequestActor, + #[path] workspace_id: String, + #[path] runtime_id: String, + ) -> Result; + + #[get( + "/api/runtimes/{runtime_id}/workers", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_list_alias( + &self, + #[path] runtime_id: String, + #[query] query: RuntimeWorkersQuery, + ) -> Result; + + #[post( + "/api/runtimes/{runtime_id}/workers", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_create_alias( + &self, + #[path] runtime_id: String, + #[body] request: RuntimeWorkerSpawnRequest, + ) -> Result; + + #[get( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_list( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[query] query: RuntimeWorkersQuery, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_create( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[body] request: RuntimeWorkerSpawnRequest, + ) -> Result; + + #[post( + "/api/runtimes/{runtime_id}/config-bundles", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_config_bundle_sync_alias( + &self, + #[path] runtime_id: String, + #[body] request: RuntimeConfigBundleSyncRequest, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}/config-bundles", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_config_bundle_sync( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[body] request: RuntimeConfigBundleSyncRequest, + ) -> Result; + + #[get( + "/api/runtimes/{runtime_id}/config-bundles/{bundle_id}/availability", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_config_bundle_availability_alias( + &self, + #[path] runtime_id: String, + #[path] bundle_id: String, + #[query] query: RuntimeConfigBundleAvailabilityQuery, + ) -> Result; + + #[get( + "/api/w/{workspace_id}/runtimes/{runtime_id}/config-bundles/{bundle_id}/availability", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_config_bundle_availability( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] bundle_id: String, + #[query] query: RuntimeConfigBundleAvailabilityQuery, + ) -> Result; + + #[get( + "/api/runtimes/{runtime_id}/workers/{worker_id}", + status = 200, + error_status = 404, + additional_error_statuses = [400, 401, 403, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_detail_alias( + &self, + #[path] runtime_id: String, + #[path] worker_id: String, + ) -> Result; + + #[get( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}", + status = 200, + error_status = 404, + additional_error_statuses = [400, 401, 403, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_detail( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + ) -> Result; + + #[post( + "/api/runtimes/{runtime_id}/workers/{worker_id}/restore", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_restore_alias( + &self, + #[path] runtime_id: String, + #[path] worker_id: String, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/restore", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_restore( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + #[query] query: RestoreTicketAssignmentQuery, + ) -> Result; + + #[put( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/pin", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_pin( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + ) -> Result; + + #[delete( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/pin", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_unpin( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + ) -> Result; + + #[get( + "/api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-plan", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_cleanup_plan( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-executions", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_cleanup_execute( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[body] request: ExecuteRuntimeCleanupRequest, + ) -> Result; + + #[post( + "/api/runtimes/{runtime_id}/workers/{worker_id}/input", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_input_alias( + &self, + #[path] runtime_id: String, + #[path] worker_id: String, + #[body] request: RuntimeWorkerInputRequest, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/input", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_input( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + #[body] request: RuntimeWorkerInputRequest, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachment-upload-grants", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_attachment_upload_grant( + &self, + #[extension] actor: RequestActor, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + #[query] query: WorkerFileUploadQuery, + ) -> Result; + + #[delete( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachment-uploads/{upload_id}", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_attachment_upload_cancel( + &self, + #[extension] actor: RequestActor, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + #[path] upload_id: String, + ) -> Result; + + #[delete( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachments/{artifact_id}", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_attachment_delete( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + #[path] artifact_id: String, + ) -> Result; + + #[post( + "/api/runtimes/{runtime_id}/workers/{worker_id}/completions", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_completions_alias( + &self, + #[path] runtime_id: String, + #[path] worker_id: String, + #[body] request: RuntimeWorkerCompletionsRequest, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/completions", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_completions( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + #[body] request: RuntimeWorkerCompletionsRequest, + ) -> Result; + + #[post( + "/api/runtimes/{runtime_id}/workers/{worker_id}/stop", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_stop_alias( + &self, + #[path] runtime_id: String, + #[path] worker_id: String, + #[body] request: RuntimeWorkerLifecycleRequest, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/stop", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_stop( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + #[body] request: RuntimeWorkerLifecycleRequest, + ) -> Result; + + #[post( + "/api/runtimes/{runtime_id}/workers/{worker_id}/cancel", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_cancel_alias( + &self, + #[path] runtime_id: String, + #[path] worker_id: String, + #[body] request: RuntimeWorkerLifecycleRequest, + ) -> Result; + + #[post( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/cancel", + status = 200, + error_status = 400, + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true + )] + async fn runtime_worker_cancel( + &self, + #[path] workspace_id: String, + #[path] runtime_id: String, + #[path] worker_id: String, + #[body] request: RuntimeWorkerLifecycleRequest, + ) -> Result; + #[get( "/api/w/{workspace_id}/repositories", status = 200, @@ -5113,6 +5690,496 @@ pub struct CompleteMergeRequestRequest { pub resolution: ConflictResolution, } +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeManagementApiError { + pub error: String, + pub message: String, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub diagnostics: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] + pub current_revision: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub current_fingerprint: Option, + #[serde(skip, default = "default_repository_error_status")] + status: u16, +} + +impl RuntimeManagementApiError { + pub fn trust_conflict(conflict: RuntimeTrustConflictResponse) -> Self { + Self { + error: match conflict.error { + RuntimeTrustConflictKind::StaleRevision => "stale_revision".to_string(), + RuntimeTrustConflictKind::FingerprintInUse => "fingerprint_in_use".to_string(), + }, + message: conflict.message, + diagnostics: Vec::new(), + current_revision: conflict.current_revision, + current_fingerprint: conflict.current_fingerprint, + status: 409, + } + } +} + +impl api_macros::HttpError for RuntimeManagementApiError { + fn status_code(&self) -> u16 { + self.status + } +} + +impl api_macros::HttpRequestError for RuntimeManagementApiError { + fn from_request_rejection(status: u16, message: String) -> Self { + Self::from(RepositoryApiError::from_request_rejection(status, message)) + } +} + +impl From for RuntimeManagementApiError { + fn from(error: RepositoryApiError) -> Self { + Self { + error: error.error, + message: error.message, + diagnostics: error.diagnostics, + current_revision: None, + current_fingerprint: None, + status: error.status, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeListResponse { + pub workspace_id: String, + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] + pub limit: usize, + pub items: Vec, + pub source: String, + #[serde(default)] + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct WorkspaceRuntimeListResponse { + pub workspace_id: String, + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] + pub limit: usize, + pub items: Vec, + pub source: String, + #[serde(default)] + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerListResponse { + pub workspace_id: String, + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] + pub limit: usize, + pub items: Vec, + pub source: String, + #[serde(default)] + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkersQuery { + pub status: Option, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum RuntimeWorkersStatusFilter { + Stopped, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde( + tag = "kind", + content = "value", + rename_all = "snake_case", + deny_unknown_fields +)] +pub enum RuntimeProfileSelector { + Builtin(String), + Named(String), +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] +pub enum RuntimeWorkerSpawnIntent { + WorkspaceCompanion, + WorkspaceOrchestrator, + WorkspaceCoding, + TicketRole { + ticket_id: String, + role: RuntimeTicketWorkerRole, + }, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum RuntimeTicketWorkerRole { + Intake, + Orchestrator, + Coder, + Reviewer, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum RuntimeWorkerSpawnAcceptanceRequirement { + SocketReady, + RunAccepted { + #[schemars(range(min = 0, max = 9_007_199_254_740_991_usize))] + expected_segments: usize, + }, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerTicketAssignmentRequest { + pub ticket_id: String, + pub operation_id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerSpawnWorkingDirectoryRequest { + pub repository_key: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub selector: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerSpawnWorkingDirectoryAttachmentRequest { + pub alias: String, + pub working_directory: RuntimeWorkerSpawnWorkingDirectoryRequest, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerSpawnRequest { + pub intent: RuntimeWorkerSpawnIntent, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub requested_worker_name: Option, + pub acceptance: RuntimeWorkerSpawnAcceptanceRequirement, + pub profile: RuntimeProfileSelector, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ticket_assignment: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub initial_submit: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub workdir_attachment_requests: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerSummary { + pub runtime_id: String, + pub worker_id: String, + pub host_id: String, + #[serde(default)] + pub display_name: String, + pub label: String, + pub profile: Option, + pub singleton_key: Option, + #[serde(default)] + pub tags: Vec, + pub workspace: WorkerWorkspaceSummary, + pub state: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub worker_state: Option, + pub last_seen_at: Option, + #[serde(default)] + pub pinned: bool, + #[serde(default)] + pub retention_state: String, + pub implementation: WorkerImplementationSummary, + pub capabilities: WorkerCapabilitySummary, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub workdir_attachments: Vec, + #[serde(default)] + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerShowResponse { + #[serde(flatten)] + pub worker: WorkerSummary, + pub updated_at: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerSpawnResponse { + pub state: WorkerOperationState, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub worker: Option, + #[serde(default)] + pub acceptance_evidence: Vec, + #[serde(default)] + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerSpawnAcceptanceEvidence { + pub kind: String, + pub detail: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeConfigBundleSyncRequest { + pub bundle: serde_json::Value, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeConfigBundleAvailabilityQuery { + pub digest: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeConfigBundleResult { + pub state: WorkerOperationState, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub availability: Option, + #[serde(default)] + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerInputRequest { + #[serde(default)] + pub kind: Option, + pub content: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub segments: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerInputResult { + pub state: WorkerOperationState, + pub runtime_id: String, + pub worker_id: String, + #[serde(default)] + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerLifecycleRequest { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reason: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ticket_assignment: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerLifecycleResult { + pub state: WorkerOperationState, + pub runtime_id: String, + pub worker_id: String, + #[serde(default)] + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerCompletionsRequest { + pub kind: serde_json::Value, + #[serde(default)] + pub prefix: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeWorkerCompletionsResult { + pub runtime_id: String, + pub worker_id: String, + pub kind: serde_json::Value, + pub prefix: String, + pub entries: Vec, + #[serde(default)] + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RestoreTicketAssignmentQuery { + pub ticket_id: Option, + pub assignment_operation_id: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum CleanupTargetKind { + WorkerDelete, + WorkdirCleanCleanup, + WorkdirDirtyDiscard, + WorkdirRecordDelete, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum CleanupWorkdirFileStatus { + Pending, + Present, + Active, + CleanupPending, + NotFound, + Corrupted, + Failed, + Unknown, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum CleanupWorkdirCleanliness { + Clean, + Dirty, + Unknown, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct CleanupWorkerCandidate { + pub target_id: String, + pub action: CleanupTargetKind, + pub worker_id: String, + pub runtime_worker_id: String, + pub runtime_id: String, + pub reason: String, + pub blocking_reason: Option, + pub pinned: bool, + pub retention_state: String, + pub linked_workdir_ids: Vec, + pub running_linked: bool, + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] + pub estimated_reclaim_bytes: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct CleanupWorkdirCandidate { + pub target_id: String, + pub action: CleanupTargetKind, + pub workdir_id: String, + pub runtime_id: String, + pub repository_key: String, + pub reason: String, + pub blocking_reason: Option, + pub linked_worker_ids: Vec, + pub linked_running_worker_ids: Vec, + pub running_linked: bool, + pub pinned_linked: bool, + pub file_status: CleanupWorkdirFileStatus, + pub cleanliness: CleanupWorkdirCleanliness, + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] + pub estimated_reclaim_bytes: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeCleanupPlanResponse { + pub workspace_id: String, + pub runtime_id: String, + pub generated_at: String, + pub revision: String, + pub digest: String, + pub workers: Vec, + pub workdirs: Vec, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct ExecuteRuntimeCleanupRequest { + pub expected_plan_revision: String, + pub expected_plan_digest: String, + #[serde(default)] + pub worker_target_ids: Vec, + #[serde(default)] + pub workdir_target_ids: Vec, + #[serde(default)] + pub confirm_dirty_discard_target_ids: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeCleanupExecutionResult { + pub target_id: String, + pub action: CleanupTargetKind, + pub status: String, + pub message: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct RuntimeCleanupExecutionResponse { + pub workspace_id: String, + pub runtime_id: String, + pub executed_at: String, + pub results: Vec, + pub plan_after: RuntimeCleanupPlanResponse, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct WorkerRetentionResponse { + pub workspace_id: String, + pub runtime_id: String, + pub worker_id: String, + pub pinned: bool, + pub retention_state: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct WorkerFileUploadQuery { + pub file_name: String, + pub media_type: String, + #[serde(default)] + pub upload_id: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(deny_unknown_fields)] +pub struct WorkerFileUploadResponse { + pub file: protocol::UploadedFileRef, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct WorkerFileDeleteResponse { + pub deleted: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct AttachmentUploadGrantResponse { + pub upload_id: String, + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] + pub expires_at_ms: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct AttachmentUploadCancelResponse { + pub cancelled: bool, +} + #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] @@ -5121,7 +6188,7 @@ pub enum RuntimeSourceKind { RemoteHttp, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum RuntimeSourceStatus { @@ -5129,7 +6196,7 @@ pub enum RuntimeSourceStatus { Reserved, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum RuntimeIdentityAuthority { @@ -5137,7 +6204,7 @@ pub enum RuntimeIdentityAuthority { ServerRuntimeConfiguration, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RuntimeSourceSummary { @@ -5147,7 +6214,7 @@ pub struct RuntimeSourceSummary { pub note: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] pub struct RuntimeSummary { pub runtime_id: String, @@ -5164,7 +6231,7 @@ pub struct RuntimeSummary { pub diagnostics: Vec, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum WorkspaceRuntimeBindingState { @@ -5173,7 +6240,7 @@ pub enum WorkspaceRuntimeBindingState { Revoked, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum RuntimeConnectionDisplayState { @@ -5183,7 +6250,7 @@ pub enum RuntimeConnectionDisplayState { Revoked, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum RuntimeVerificationOutcome { @@ -5193,7 +6260,7 @@ pub enum RuntimeVerificationOutcome { ConnectivityFailed, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RuntimeVerificationEvidenceSummary { @@ -5201,35 +6268,41 @@ pub struct RuntimeVerificationEvidenceSummary { pub last_checked_at: String, pub last_outcome: RuntimeVerificationOutcome, #[cfg_attr(feature = "typescript", ts(type = "number"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub binding_revision: u64, pub workspace_key_id: String, #[cfg_attr(feature = "typescript", ts(type = "number"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub workspace_identity_revision: u64, #[cfg_attr(feature = "typescript", ts(type = "number"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub workspace_trust_generation: u64, pub runtime_public_key_fingerprint: String, #[cfg_attr(feature = "typescript", ts(type = "number"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub runtime_identity_revision: u64, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct WorkspaceRuntimeBindingSummary { pub state: WorkspaceRuntimeBindingState, pub connection_state: RuntimeConnectionDisplayState, #[cfg_attr(feature = "typescript", ts(type = "number"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub revision: u64, #[serde(default, skip_serializing_if = "Option::is_none")] pub workspace_key_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "typescript", ts(type = "number | null"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub workspace_key_generation: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub verification: Option, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RuntimeManagementSummary { @@ -5242,15 +6315,29 @@ pub struct RuntimeManagementSummary { pub binding: Option, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] pub struct WorkspaceRuntimeResource { #[serde(flatten)] pub runtime: RuntimeSummary, pub management: RuntimeManagementSummary, + #[serde(skip, default = "default_runtime_mutation_status")] + #[cfg_attr(feature = "typescript", ts(skip))] + #[schemars(skip)] + pub http_status: u16, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +const fn default_runtime_mutation_status() -> u16 { + 200 +} + +impl api_macros::HttpSuccess for WorkspaceRuntimeResource { + fn status_code(&self) -> u16 { + self.http_status + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum RuntimeTrustKeyStatus { @@ -5259,7 +6346,7 @@ pub enum RuntimeTrustKeyStatus { Revoked, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RuntimeTrustKeyState { @@ -5268,6 +6355,7 @@ pub struct RuntimeTrustKeyState { pub fingerprint: Option, #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "typescript", ts(type = "number | null"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub revision: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub created_at: Option, @@ -5277,7 +6365,7 @@ pub struct RuntimeTrustKeyState { pub revoked_at: Option, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum RuntimeTrustAuditAction { @@ -5287,7 +6375,7 @@ pub enum RuntimeTrustAuditAction { Revoked, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RuntimeTrustAuditEntry { @@ -5298,11 +6386,12 @@ pub struct RuntimeTrustAuditEntry { #[serde(default, skip_serializing_if = "Option::is_none")] pub new_fingerprint: Option, #[cfg_attr(feature = "typescript", ts(type = "number"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub revision: u64, pub at: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct WorkspaceRuntimeDetail { @@ -5315,31 +6404,33 @@ pub struct WorkspaceRuntimeDetail { pub recent_audit: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RuntimeTrustKeyRevealResponse { pub public_key: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RevokeRuntimeTrustKeyRequest { #[cfg_attr(feature = "typescript", ts(type = "number"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub expected_revision: u64, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RemoveRuntimeRequest { pub operation_id: String, #[cfg_attr(feature = "typescript", ts(type = "number"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub expected_binding_revision: u64, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum RuntimeRemovalOperationState { @@ -5349,7 +6440,7 @@ pub enum RuntimeRemovalOperationState { Failed, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RuntimeRemovalOperationResponse { @@ -5367,7 +6458,7 @@ pub struct RuntimeRemovalOperationResponse { pub completed_at: Option, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum RuntimeTrustConflictKind { @@ -5375,7 +6466,7 @@ pub enum RuntimeTrustConflictKind { FingerprintInUse, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RuntimeTrustConflictResponse { @@ -5383,12 +6474,13 @@ pub struct RuntimeTrustConflictResponse { pub message: String, #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "typescript", ts(type = "number"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub current_revision: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub current_fingerprint: Option, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RuntimePublicIdentityBundle { @@ -5396,7 +6488,7 @@ pub struct RuntimePublicIdentityBundle { pub public_key: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct CreateRemoteRuntimeRequest { @@ -5406,10 +6498,11 @@ pub struct CreateRemoteRuntimeRequest { pub endpoint: String, #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "typescript", ts(type = "number | null"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub expected_revision: Option, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct UpdateRemoteRuntimeRequest { @@ -5418,7 +6511,7 @@ pub struct UpdateRemoteRuntimeRequest { pub endpoint: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum RuntimeConnectionTestStatus { @@ -5426,7 +6519,7 @@ pub enum RuntimeConnectionTestStatus { Failed, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum RuntimeConnectionTestFailureKind { @@ -5441,20 +6534,23 @@ pub enum RuntimeConnectionTestFailureKind { Configuration, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct RuntimeConnectionTestResponse { pub workspace_id: String, pub runtime_id: String, #[cfg_attr(feature = "typescript", ts(type = "number"))] + #[schemars(range(min = 0, max = 9_007_199_254_740_991_u64))] pub binding_revision: u64, pub connection_state: RuntimeConnectionDisplayState, pub verification: Option, pub checked_at: String, pub status: RuntimeConnectionTestStatus, pub failure_kind: Option, + #[schemars(range(min = 0, max = 4_294_967_295_u32))] pub expected_protocol_version: u32, + #[schemars(range(min = 0, max = 4_294_967_295_u32))] pub actual_protocol_version: Option, #[serde(default)] pub diagnostics: Vec, @@ -5840,7 +6936,7 @@ pub struct BrowserWorkspaceOrchestratorResponse { pub diagnostics: Vec, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum WorkerOperationState { Accepted, @@ -5853,7 +6949,7 @@ pub enum WorkerOperationState { /// `Rejected` is reserved for read-only preflight failures. Once live restore /// work starts, failure is either a confirmed `RolledBack` operation or a /// `ReconciliationRequired` result whose commit state must be reread/retried. -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(rename_all = "snake_case")] pub enum WorkerRestoreState { @@ -5863,7 +6959,7 @@ pub enum WorkerRestoreState { ReconciliationRequired, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct WorkerRestoreResult { @@ -5875,7 +6971,7 @@ pub struct WorkerRestoreResult { pub diagnostics: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(deny_unknown_fields)] pub struct WorkerRestoreResponse { @@ -6772,36 +7868,6 @@ pub fn legacy_catalog_typescript() -> String { RepositorySshConnectionProbeResponse::decl(&config), ConfirmRepositorySshHostTrustRequest::decl(&config), RepositoryLogResponse::decl(&config), - RuntimeSourceKind::decl(&config), - RuntimeSourceStatus::decl(&config), - RuntimeIdentityAuthority::decl(&config), - RuntimeSourceSummary::decl(&config), - RuntimeSummary::decl(&config), - WorkspaceRuntimeBindingState::decl(&config), - RuntimeConnectionDisplayState::decl(&config), - RuntimeVerificationOutcome::decl(&config), - RuntimeVerificationEvidenceSummary::decl(&config), - WorkspaceRuntimeBindingSummary::decl(&config), - RuntimeManagementSummary::decl(&config), - WorkspaceRuntimeResource::decl(&config), - RuntimeTrustKeyStatus::decl(&config), - RuntimeTrustKeyState::decl(&config), - RuntimeTrustAuditAction::decl(&config), - RuntimeTrustAuditEntry::decl(&config), - WorkspaceRuntimeDetail::decl(&config), - RuntimeTrustKeyRevealResponse::decl(&config), - RevokeRuntimeTrustKeyRequest::decl(&config), - RemoveRuntimeRequest::decl(&config), - RuntimeRemovalOperationState::decl(&config), - RuntimeRemovalOperationResponse::decl(&config), - RuntimeTrustConflictKind::decl(&config), - RuntimeTrustConflictResponse::decl(&config), - RuntimePublicIdentityBundle::decl(&config), - CreateRemoteRuntimeRequest::decl(&config), - UpdateRemoteRuntimeRequest::decl(&config), - RuntimeConnectionTestStatus::decl(&config), - RuntimeConnectionTestFailureKind::decl(&config), - RuntimeConnectionTestResponse::decl(&config), ] .map(|declaration| format!("export {declaration}")); @@ -6913,35 +7979,6 @@ pub fn auth_api_typescript() -> String { ) } -#[cfg(feature = "typescript")] -pub fn workdir_api_typescript() -> String { - use ts_rs::TS; - - let config = ts_rs::Config::default(); - let declarations = [ - DiagnosticSeverity::decl(&config), - Diagnostic::decl(&config), - WorkingDirectoryMaterializerKind::decl(&config), - WorkingDirectoryStatusKind::decl(&config), - WorkingDirectoryCleanupTarget::decl(&config), - WorkingDirectoryOccupancy::decl(&config), - WorkingDirectorySource::decl(&config), - WorkingDirectorySummary::decl(&config), - WorkingDirectoryCreateRequest::decl(&config), - WorkingDirectoryListResponse::decl(&config), - WorkingDirectoryDetailResponse::decl(&config), - WorkingDirectoryCreateResponse::decl(&config), - ]; - format!( - "// Generated from server-api. Do not edit by hand.\n// Regenerate: cargo run -q -p server-api --features typescript --example generate_workdir_api_types > web/workspace/src/lib/generated/workdir-api.ts\n\n{}\n", - declarations - .into_iter() - .map(|declaration| format!("export {declaration}")) - .collect::>() - .join("\n\n") - ) -} - #[cfg(feature = "typescript")] pub fn worker_launch_api_typescript() -> String { use ts_rs::TS; @@ -7072,35 +8109,6 @@ mod skill_typescript_tests { } } -#[cfg(all(test, feature = "typescript"))] -mod workdir_typescript_tests { - #[test] - fn generated_workdir_api_contract_is_current() { - let expected = super::workdir_api_typescript(); - let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) - .join("../../web/workspace/src/lib/generated/workdir-api.ts"); - let actual = std::fs::read_to_string(&path) - .unwrap_or_else(|error| panic!("failed to read {}: {error}", path.display())); - assert_eq!( - normalize(&actual), - normalize(&expected), - "regenerate Workdir API TypeScript types with `cargo run -q -p server-api --features typescript --example generate_workdir_api_types > web/workspace/src/lib/generated/workdir-api.ts` and format the generated file", - ); - } - - fn normalize(value: &str) -> String { - value - .chars() - .filter_map(|character| match character { - character if character.is_whitespace() => None, - ',' => Some(';'), - character => Some(character), - }) - .collect::() - .replace("=|", "=") - } -} - #[cfg(test)] mod tests { use super::*; @@ -8139,9 +9147,9 @@ mod tests { assert!(output.contains( "export type WorkspaceProfileSourceProvenance = \"project_profile_source_tree\"" )); - assert!(output.contains("export type RuntimeConnectionTestResponse =")); - assert!(output.contains("status: RuntimeConnectionTestStatus")); - assert!(output.contains("failure_kind: RuntimeConnectionTestFailureKind | null")); + assert!(!output.contains("export type RuntimeConnectionTestResponse =")); + assert!(!output.contains("export type RuntimePublicIdentityBundle =")); + assert!(!output.contains("export type WorkspaceRuntimeResource =")); assert!(!output.contains("repository_key: string, display_name")); } diff --git a/crates/server-api/src/repository_openapi_typescript.rs b/crates/server-api/src/repository_openapi_typescript.rs index fa374443f..58d62758a 100644 --- a/crates/server-api/src/repository_openapi_typescript.rs +++ b/crates/server-api/src/repository_openapi_typescript.rs @@ -12,6 +12,11 @@ pub const WORKDIR_GENERATOR_VERSION: &str = "1"; pub const WORKDIR_GENERATOR_OPTIONS: &str = "workdir-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output"; pub const WORKDIR_OUTPUT_PATH: &str = "web/workspace/src/lib/generated/workdir-api.ts"; +pub const RUNTIME_GENERATOR_NAME: &str = "yoi-runtime-openapi-typescript"; +pub const RUNTIME_GENERATOR_VERSION: &str = "1"; +pub const RUNTIME_GENERATOR_OPTIONS: &str = + "runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output"; +pub const RUNTIME_OUTPUT_PATH: &str = "web/workspace/src/lib/generated/runtime-api.ts"; const MAX_SAFE_INTEGER: f64 = 9_007_199_254_740_991.0; const MIN_SAFE_INTEGER: f64 = -9_007_199_254_740_991.0; @@ -119,6 +124,48 @@ pub fn generate_workdir_typescript(input: &str) -> Result Result { + let document: Value = serde_json::from_str(input) + .map_err(|error| GenerationError::invalid(format!("invalid OpenAPI JSON: {error}")))?; + let root = object(&document, "OpenAPI document")?; + if root.get("openapi").and_then(Value::as_str) != Some("3.1.0") { + return Err(GenerationError::invalid( + "Runtime TypeScript generation requires OpenAPI 3.1.0", + )); + } + + let info = object_field(root, "info", "OpenAPI document")?; + let contract_version = required_string(info, "version", "OpenAPI info")?; + let contract_digest = required_string(info, "x-yoi-source-digest", "OpenAPI info")?; + validate_contract_digest(contract_digest)?; + let schemas = object_field( + object_field(root, "components", "OpenAPI document")?, + "schemas", + "OpenAPI components", + )?; + let paths = object_field(root, "paths", "OpenAPI document")?; + let roots = validate_runtime_operations(paths)?; + let closure = schema_closure(schemas, &roots)?; + + let mut output = format!( + "// @generated by {RUNTIME_GENERATOR_NAME}@{RUNTIME_GENERATOR_VERSION}. Do not edit.\n\ +// Input: {INPUT_PATH}\n\ +// Input contract: {contract_version} ({contract_digest})\n\ +// Options: {RUNTIME_GENERATOR_OPTIONS}\n\ +// Output: {RUNTIME_OUTPUT_PATH}\n\ +// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check\n\n" + ); + for name in closure { + let schema = schemas + .get(&name) + .ok_or_else(|| GenerationError::invalid(format!("missing schema `{name}`")))?; + let declaration = render_schema(schema, &format!("schema `{name}`"))?; + output.push_str(&format!("export type {name} = {declaration};\n\n")); + } + output.pop(); + Ok(output) +} + fn validate_contract_digest(digest: &str) -> Result<(), GenerationError> { let Some(hex) = digest.strip_prefix("sha256:") else { return Err(GenerationError::invalid( @@ -347,6 +394,364 @@ fn validate_workdir_operations( validate_operation_contracts(paths, &operations) } +fn validate_runtime_operations( + paths: &Map, +) -> Result, GenerationError> { + let operations = [ + runtime_operation( + "/api/runtimes", + "get", + "runtime_list_alias", + None, + &[("200", "RuntimeListResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes", + "get", + "runtime_list", + None, + &[("200", "WorkspaceRuntimeListResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes", + "post", + "runtime_create", + Some("CreateRemoteRuntimeRequest"), + &[ + ("200", "WorkspaceRuntimeResource"), + ("201", "WorkspaceRuntimeResource"), + ], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}", + "get", + "runtime_detail", + None, + &[("200", "WorkspaceRuntimeDetail")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}", + "post", + "runtime_update", + Some("UpdateRemoteRuntimeRequest"), + &[("200", "WorkspaceRuntimeDetail")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}", + "delete", + "runtime_remove", + Some("RemoveRuntimeRequest"), + &[("200", "RuntimeRemovalOperationResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/trust-key", + "get", + "runtime_trust_key_reveal", + None, + &[("200", "RuntimeTrustKeyRevealResponse")], + ), + RuntimeOperationContract { + error: "RuntimeManagementApiError", + ..runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/trust-key", + "delete", + "runtime_trust_key_revoke", + Some("RevokeRuntimeTrustKeyRequest"), + &[("200", "WorkspaceRuntimeDetail")], + ) + }, + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/connection-tests", + "post", + "runtime_connection_test", + None, + &[("200", "RuntimeConnectionTestResponse")], + ), + runtime_operation( + "/api/runtimes/{runtime_id}/workers", + "get", + "runtime_worker_list_alias", + None, + &[("200", "RuntimeWorkerListResponse")], + ), + runtime_operation( + "/api/runtimes/{runtime_id}/workers", + "post", + "runtime_worker_create_alias", + Some("RuntimeWorkerSpawnRequest"), + &[("200", "RuntimeWorkerSpawnResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers", + "get", + "runtime_worker_list", + None, + &[("200", "RuntimeWorkerListResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers", + "post", + "runtime_worker_create", + Some("RuntimeWorkerSpawnRequest"), + &[("200", "RuntimeWorkerSpawnResponse")], + ), + runtime_operation( + "/api/runtimes/{runtime_id}/config-bundles", + "post", + "runtime_config_bundle_sync_alias", + Some("RuntimeConfigBundleSyncRequest"), + &[("200", "RuntimeConfigBundleResult")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/config-bundles", + "post", + "runtime_config_bundle_sync", + Some("RuntimeConfigBundleSyncRequest"), + &[("200", "RuntimeConfigBundleResult")], + ), + runtime_operation( + "/api/runtimes/{runtime_id}/config-bundles/{bundle_id}/availability", + "get", + "runtime_config_bundle_availability_alias", + None, + &[("200", "RuntimeConfigBundleResult")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/config-bundles/{bundle_id}/availability", + "get", + "runtime_config_bundle_availability", + None, + &[("200", "RuntimeConfigBundleResult")], + ), + runtime_operation( + "/api/runtimes/{runtime_id}/workers/{worker_id}", + "get", + "runtime_worker_detail_alias", + None, + &[("200", "RuntimeWorkerShowResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}", + "get", + "runtime_worker_detail", + None, + &[("200", "RuntimeWorkerShowResponse")], + ), + runtime_operation( + "/api/runtimes/{runtime_id}/workers/{worker_id}/restore", + "post", + "runtime_worker_restore_alias", + None, + &[("200", "WorkerRestoreResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/restore", + "post", + "runtime_worker_restore", + None, + &[("200", "WorkerRestoreResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/pin", + "put", + "runtime_worker_pin", + None, + &[("200", "WorkerRetentionResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/pin", + "delete", + "runtime_worker_unpin", + None, + &[("200", "WorkerRetentionResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-plan", + "get", + "runtime_cleanup_plan", + None, + &[("200", "RuntimeCleanupPlanResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-executions", + "post", + "runtime_cleanup_execute", + Some("ExecuteRuntimeCleanupRequest"), + &[("200", "RuntimeCleanupExecutionResponse")], + ), + runtime_operation( + "/api/runtimes/{runtime_id}/workers/{worker_id}/input", + "post", + "runtime_worker_input_alias", + Some("RuntimeWorkerInputRequest"), + &[("200", "RuntimeWorkerInputResult")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/input", + "post", + "runtime_worker_input", + Some("RuntimeWorkerInputRequest"), + &[("200", "RuntimeWorkerInputResult")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachment-upload-grants", + "post", + "runtime_worker_attachment_upload_grant", + None, + &[("200", "AttachmentUploadGrantResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachment-uploads/{upload_id}", + "delete", + "runtime_worker_attachment_upload_cancel", + None, + &[("200", "AttachmentUploadCancelResponse")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachments/{artifact_id}", + "delete", + "runtime_worker_attachment_delete", + None, + &[("200", "WorkerFileDeleteResponse")], + ), + runtime_operation( + "/api/runtimes/{runtime_id}/workers/{worker_id}/completions", + "post", + "runtime_worker_completions_alias", + Some("RuntimeWorkerCompletionsRequest"), + &[("200", "RuntimeWorkerCompletionsResult")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/completions", + "post", + "runtime_worker_completions", + Some("RuntimeWorkerCompletionsRequest"), + &[("200", "RuntimeWorkerCompletionsResult")], + ), + runtime_operation( + "/api/runtimes/{runtime_id}/workers/{worker_id}/stop", + "post", + "runtime_worker_stop_alias", + Some("RuntimeWorkerLifecycleRequest"), + &[("200", "RuntimeWorkerLifecycleResult")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/stop", + "post", + "runtime_worker_stop", + Some("RuntimeWorkerLifecycleRequest"), + &[("200", "RuntimeWorkerLifecycleResult")], + ), + runtime_operation( + "/api/runtimes/{runtime_id}/workers/{worker_id}/cancel", + "post", + "runtime_worker_cancel_alias", + Some("RuntimeWorkerLifecycleRequest"), + &[("200", "RuntimeWorkerLifecycleResult")], + ), + runtime_operation( + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/cancel", + "post", + "runtime_worker_cancel", + Some("RuntimeWorkerLifecycleRequest"), + &[("200", "RuntimeWorkerLifecycleResult")], + ), + ]; + validate_runtime_operation_contracts(paths, &operations) +} + +const fn runtime_operation( + path: &'static str, + method: &'static str, + operation_id: &'static str, + request: Option<&'static str>, + successes: &'static [(&'static str, &'static str)], +) -> RuntimeOperationContract { + RuntimeOperationContract { + path, + method, + operation_id, + request, + successes, + error: "RepositoryApiError", + } +} + +fn validate_runtime_operation_contracts( + paths: &Map, + operations: &[RuntimeOperationContract], +) -> Result, GenerationError> { + let mut roots = BTreeSet::new(); + for operation in operations { + let path = object_field(paths, operation.path, "OpenAPI paths")?; + let value = object_field(path, operation.method, operation.path)?; + if value.get("operationId").and_then(Value::as_str) != Some(operation.operation_id) { + return Err(GenerationError::invalid(format!( + "{} {} must retain operationId `{}`", + operation.method, operation.path, operation.operation_id + ))); + } + match operation.request { + Some(expected) => { + let request = object_field(value, "requestBody", operation.operation_id)?; + require_schema_ref( + json_content_schema(request, operation.operation_id)?, + expected, + operation.operation_id, + )?; + roots.insert(expected.to_owned()); + } + None if value.contains_key("requestBody") => { + return Err(GenerationError::invalid(format!( + "{} unexpectedly gained a request body", + operation.operation_id + ))); + } + None => {} + } + let responses = object_field(value, "responses", operation.operation_id)?; + let successes = operation + .successes + .iter() + .copied() + .collect::>(); + for (status, response) in responses { + let response = object( + response, + &format!("{} response {status}", operation.operation_id), + )?; + let expected = successes + .get(status.as_str()) + .copied() + .unwrap_or(operation.error); + require_schema_ref( + json_content_schema(response, operation.operation_id)?, + expected, + operation.operation_id, + )?; + roots.insert(expected.to_owned()); + } + for (status, expected) in successes { + if !responses.contains_key(status) { + return Err(GenerationError::invalid(format!( + "{} is missing success response {status} ({expected})", + operation.operation_id + ))); + } + } + } + Ok(roots) +} + +struct RuntimeOperationContract { + path: &'static str, + method: &'static str, + operation_id: &'static str, + request: Option<&'static str>, + successes: &'static [(&'static str, &'static str)], + error: &'static str, +} + fn validate_operation_contracts( paths: &Map, operations: &[OperationContract], @@ -686,6 +1091,21 @@ fn render_schema(value: &Value, context: &str) -> Result>>(); + if let Some(types) = disjoint_types { + let unique = types.iter().copied().collect::>(); + if outer_type.is_none() && unique.len() == branches.len() { + return branches + .iter() + .map(|branch| render_schema(branch, context)) + .collect::, _>>() + .map(|branches| branches.join(" | ")); + } + } + let mut rendered = Vec::with_capacity(branches.len()); let mut seen_literals = BTreeSet::new(); for branch in branches { @@ -1007,6 +1427,44 @@ mod tests { const OPENAPI: &str = include_str!("../../../openapi/server-api.json"); const TYPESCRIPT: &str = include_str!("../../../web/workspace/src/lib/generated/repository-api.ts"); + const RUNTIME_TYPESCRIPT: &str = + include_str!("../../../web/workspace/src/lib/generated/runtime-api.ts"); + + #[test] + fn checked_in_runtime_typescript_is_current() { + assert_eq!( + generate_runtime_typescript(OPENAPI).unwrap(), + RUNTIME_TYPESCRIPT, + "regenerate with `cargo run -q -p server-api --example generate_runtime_api_types`", + ); + } + + #[test] + fn runtime_generator_validates_operation_ownership_and_schema_closure() { + let output = generate_runtime_typescript(OPENAPI).unwrap(); + assert!(output.contains(&format!( + "@generated by {RUNTIME_GENERATOR_NAME}@{RUNTIME_GENERATOR_VERSION}" + ))); + assert!(output.contains("export type RuntimeCleanupPlanResponse =")); + assert!(output.contains("export type RuntimeWorkerLifecycleResult =")); + assert!(output.contains("export type RuntimeWorkerSpawnAcceptanceRequirement = \"socket_ready\" | { run_accepted:")); + assert!(!output.contains("export type UnmigratedDomain =")); + + let mut document: Value = serde_json::from_str(OPENAPI).unwrap(); + document["paths"]["/api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-plan"]["get"]["operationId"] = + serde_json::json!("manual_cleanup_plan"); + let error = generate_runtime_typescript(&document.to_string()).unwrap_err(); + assert!(error.to_string().contains("runtime_cleanup_plan")); + + let mut document: Value = serde_json::from_str(OPENAPI).unwrap(); + document["components"]["schemas"]["RuntimeWorkerSpawnAcceptanceRequirement"] + ["oneOf"] + .as_array_mut() + .unwrap() + .push(serde_json::json!({"type": "object", "properties": {}, "additionalProperties": false})); + let error = generate_runtime_typescript(&document.to_string()).unwrap_err(); + assert!(error.to_string().contains("ambiguous oneOf")); + } #[test] fn checked_in_repository_typescript_is_current() { diff --git a/crates/workspace-server/src/server.rs b/crates/workspace-server/src/server.rs index 91e79e95a..510541fe8 100644 --- a/crates/workspace-server/src/server.rs +++ b/crates/workspace-server/src/server.rs @@ -14,7 +14,7 @@ use axum::http::header::{ use axum::http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, Uri}; use axum::middleware::{self, Next}; use axum::response::{IntoResponse, Response}; -use axum::routing::{delete, get, post, put}; +use axum::routing::{get, post, put}; use axum::{Json, Router}; use chrono::{Duration, SecondsFormat, Utc}; use config_source::ConfigTreeSnapshot; @@ -48,16 +48,15 @@ use server_api::{ RepositoryDetailResponse, RepositoryListResponse, RepositoryLogResponse, RepositorySshConnectionProbeRequest, RepositorySshConnectionProbeResponse, RepositorySshConnectionTrustState, RepositorySshCredential, RepositorySshHostKeyCandidate, - RepositorySshHostTrust, RepositorySshPublicKey, RequestActor, RevokeRuntimeTrustKeyRequest, + RepositorySshHostTrust, RepositorySshPublicKey, RequestActor, RotateRepositorySshCredentialRequest, RuntimeConnectionDisplayState, RuntimeConnectionTestFailureKind, RuntimeConnectionTestResponse, RuntimeConnectionTestStatus, RuntimeManagementSummary, RuntimeRemovalOperationResponse, RuntimeRemovalOperationState as ApiRuntimeRemovalOperationState, RuntimeTrustAuditAction, - RuntimeTrustAuditEntry, RuntimeTrustConflictKind, RuntimeTrustConflictResponse, - RuntimeTrustKeyRevealResponse, RuntimeTrustKeyState, RuntimeTrustKeyStatus, - UpdateRemoteRuntimeRequest, UpdateWorkspaceMetadataRequest, WhoamiResponse, - WorkerLaunchOptionsResponse, WorkerLaunchProfileCandidate, WorkerLaunchRuntimeOption, - WorkerLaunchWorkerSummary, + RuntimeTrustAuditEntry, RuntimeTrustKeyRevealResponse, RuntimeTrustKeyState, + RuntimeTrustKeyStatus, UpdateRemoteRuntimeRequest, UpdateWorkspaceMetadataRequest, + WhoamiResponse, WorkerLaunchOptionsResponse, WorkerLaunchProfileCandidate, + WorkerLaunchRuntimeOption, WorkerLaunchWorkerSummary, WorkingDirectoryCreateRequest as BrowserWorkingDirectoryCreateRequest, WorkingDirectoryCreateResponse as BrowserWorkingDirectoryCreateResponse, WorkingDirectoryDetailResponse as BrowserWorkingDirectoryDetailResponse, @@ -5049,6 +5048,791 @@ impl server_api::ServerApi for ServerApiContractService { }) } + async fn runtime_list_alias( + &self, + ) -> std::result::Result { + let Json(response) = list_runtimes(State(self.workspace_api()?.clone())) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_list( + &self, + workspace_id: String, + ) -> std::result::Result + { + let Json(response) = scoped_list_runtimes( + State(self.workspace_api()?.clone()), + AxumPath(ScopedWorkspacePath { workspace_id }), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_create( + &self, + actor: RequestActor, + workspace_id: String, + request: server_api::CreateRemoteRuntimeRequest, + ) -> std::result::Result + { + let (status, Json(mut response)) = scoped_create_remote_runtime( + State(self.workspace_api()?.clone()), + AxumPath(ScopedWorkspacePath { workspace_id }), + Extension(actor), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + response.http_status = status.as_u16(); + Ok(response) + } + + async fn runtime_detail( + &self, + workspace_id: String, + runtime_id: String, + ) -> std::result::Result + { + scoped_get_runtime_detail( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimePath { + workspace_id, + runtime_id, + }), + ) + .await + .map(|Json(response)| response) + .map_err(ApiError::into_repository_api_error) + } + + async fn runtime_update( + &self, + actor: RequestActor, + workspace_id: String, + runtime_id: String, + request: server_api::UpdateRemoteRuntimeRequest, + ) -> std::result::Result + { + scoped_update_remote_runtime( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimePath { + workspace_id, + runtime_id, + }), + Extension(actor), + Json(request), + ) + .await + .map(|Json(response)| response) + .map_err(ApiError::into_repository_api_error) + } + + async fn runtime_remove( + &self, + actor: RequestActor, + workspace_id: String, + runtime_id: String, + request: server_api::RemoveRuntimeRequest, + ) -> std::result::Result< + server_api::RuntimeRemovalOperationResponse, + server_api::RepositoryApiError, + > { + scoped_remove_remote_runtime( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimePath { + workspace_id, + runtime_id, + }), + Extension(actor), + Json(request), + ) + .await + .map(|Json(response)| response) + .map_err(ApiError::into_repository_api_error) + } + + async fn runtime_trust_key_reveal( + &self, + actor: RequestActor, + workspace_id: String, + runtime_id: String, + ) -> std::result::Result< + server_api::RuntimeTrustKeyRevealResponse, + server_api::RepositoryApiError, + > { + scoped_reveal_runtime_trust_key( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimePath { + workspace_id, + runtime_id, + }), + Extension(actor), + ) + .await + .map(|Json(response)| response) + .map_err(ApiError::into_repository_api_error) + } + + async fn runtime_trust_key_revoke( + &self, + actor: RequestActor, + workspace_id: String, + runtime_id: String, + request: server_api::RevokeRuntimeTrustKeyRequest, + ) -> std::result::Result< + server_api::WorkspaceRuntimeDetail, + server_api::RuntimeManagementApiError, + > { + let api = self + .workspace_api() + .map_err(server_api::RuntimeManagementApiError::from)? + .clone(); + let map_error = |error: ApiError| { + server_api::RuntimeManagementApiError::from(error.into_repository_api_error()) + }; + validate_workspace_scope(&api, &workspace_id).map_err(map_error)?; + require_workspace_owner(&api, &workspace_id, &actor, "Runtime trust changes") + .await + .map_err(map_error)?; + if runtime_id == EMBEDDED_WORKER_RUNTIME_ID { + return Err(map_error(settings_bad_request( + "embedded_runtime_trust_managed_internally", + "the embedded Runtime trust key is managed by the embedded Runtime authority", + ))); + } + if request.expected_revision == 0 { + return Err(map_error(settings_bad_request( + "invalid_runtime_binding_revision", + "expected_revision must be greater than zero", + ))); + } + let now = Utc::now().to_rfc3339(); + if let Err(error) = api + .store + .revoke_workspace_runtime_binding_key( + &workspace_id, + &runtime_id, + request.expected_revision, + &actor.account_id, + &now, + ) + .await + { + let kind = match error { + Error::RuntimeBindingRevisionConflict { .. } => { + Some(server_api::RuntimeTrustConflictKind::StaleRevision) + } + Error::RuntimeBindingFingerprintConflict { .. } => { + Some(server_api::RuntimeTrustConflictKind::FingerprintInUse) + } + _ => None, + }; + if let Some(kind) = kind { + let current = api + .store + .get_workspace_runtime_binding(&workspace_id, &runtime_id) + .await + .map_err(|error| map_error(ApiError::from(error)))?; + return Err(server_api::RuntimeManagementApiError::trust_conflict( + server_api::RuntimeTrustConflictResponse { + error: kind, + message: match kind { + server_api::RuntimeTrustConflictKind::StaleRevision => { + "the Runtime trust binding changed; reload before retrying".to_string() + } + server_api::RuntimeTrustConflictKind::FingerprintInUse => { + "the public key is already bound to another Runtime in this Workspace".to_string() + } + }, + current_revision: current.as_ref().map(|binding| binding.binding_revision), + current_fingerprint: current + .as_ref() + .map(|binding| binding.public_key_fingerprint.clone()), + }, + )); + } + return Err(map_error(ApiError::from(error))); + } + api.runtime_binding_expectations + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .remove(&(workspace_id.clone(), runtime_id.clone())); + api.runtime_subscription_broker + .unregister_runtime(&runtime_id); + workspace_runtime_detail(&api, &workspace_id, &runtime_id) + .await + .map_err(map_error) + } + + async fn runtime_connection_test( + &self, + actor: RequestActor, + workspace_id: String, + runtime_id: String, + ) -> std::result::Result< + server_api::RuntimeConnectionTestResponse, + server_api::RepositoryApiError, + > { + scoped_test_runtime_connection( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimePath { + workspace_id, + runtime_id, + }), + Extension(actor), + ) + .await + .map(|Json(response)| response) + .map_err(ApiError::into_repository_api_error) + } + + async fn runtime_worker_list_alias( + &self, + runtime_id: String, + query: server_api::RuntimeWorkersQuery, + ) -> std::result::Result + { + let query = project_contract_dto(&query)?; + let Json(response) = list_runtime_workers( + State(self.workspace_api()?.clone()), + AxumPath(runtime_id), + Query(query), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_create_alias( + &self, + runtime_id: String, + request: server_api::RuntimeWorkerSpawnRequest, + ) -> std::result::Result + { + let request = project_contract_dto(&request)?; + let Json(response) = create_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath(runtime_id), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_list( + &self, + workspace_id: String, + runtime_id: String, + query: server_api::RuntimeWorkersQuery, + ) -> std::result::Result + { + let query = project_contract_dto(&query)?; + let Json(response) = scoped_list_runtime_workers( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimePath { + workspace_id, + runtime_id, + }), + Query(query), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_create( + &self, + workspace_id: String, + runtime_id: String, + request: server_api::RuntimeWorkerSpawnRequest, + ) -> std::result::Result + { + let request = project_contract_dto(&request)?; + let Json(response) = scoped_create_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimePath { + workspace_id, + runtime_id, + }), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_config_bundle_sync_alias( + &self, + runtime_id: String, + request: server_api::RuntimeConfigBundleSyncRequest, + ) -> std::result::Result + { + let request = project_contract_dto(&request)?; + let Json(response) = sync_runtime_config_bundle( + State(self.workspace_api()?.clone()), + AxumPath(runtime_id), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_config_bundle_sync( + &self, + workspace_id: String, + runtime_id: String, + request: server_api::RuntimeConfigBundleSyncRequest, + ) -> std::result::Result + { + let request = project_contract_dto(&request)?; + let Json(response) = scoped_sync_runtime_config_bundle( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimePath { + workspace_id, + runtime_id, + }), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_config_bundle_availability_alias( + &self, + runtime_id: String, + bundle_id: String, + query: server_api::RuntimeConfigBundleAvailabilityQuery, + ) -> std::result::Result + { + let query = project_contract_dto(&query)?; + let Json(response) = check_runtime_config_bundle( + State(self.workspace_api()?.clone()), + AxumPath((runtime_id, bundle_id)), + Query(query), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_config_bundle_availability( + &self, + workspace_id: String, + runtime_id: String, + bundle_id: String, + query: server_api::RuntimeConfigBundleAvailabilityQuery, + ) -> std::result::Result + { + let query = project_contract_dto(&query)?; + let Json(response) = scoped_check_runtime_config_bundle( + State(self.workspace_api()?.clone()), + AxumPath(ScopedConfigBundlePath { + workspace_id, + runtime_id, + bundle_id, + }), + Query(query), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_detail_alias( + &self, + runtime_id: String, + worker_id: String, + ) -> std::result::Result + { + let Json(response) = get_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath((runtime_id, worker_id)), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_detail( + &self, + workspace_id: String, + runtime_id: String, + worker_id: String, + ) -> std::result::Result + { + let Json(response) = scoped_get_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimeWorkerPath { + workspace_id, + worker: RuntimeWorkerRef::new(runtime_id, worker_id), + }), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_restore_alias( + &self, + runtime_id: String, + worker_id: String, + ) -> std::result::Result + { + restore_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath((runtime_id, worker_id)), + ) + .await + .map(|Json(response)| response) + .map_err(ApiError::into_repository_api_error) + } + + async fn runtime_worker_restore( + &self, + workspace_id: String, + runtime_id: String, + worker_id: String, + query: server_api::RestoreTicketAssignmentQuery, + ) -> std::result::Result + { + let query = project_contract_dto(&query)?; + scoped_restore_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimeWorkerPath { + workspace_id, + worker: RuntimeWorkerRef::new(runtime_id, worker_id), + }), + Query(query), + ) + .await + .map(|Json(response)| response) + .map_err(ApiError::into_repository_api_error) + } + + async fn runtime_worker_pin( + &self, + workspace_id: String, + runtime_id: String, + worker_id: String, + ) -> std::result::Result + { + let Json(response) = scoped_pin_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimeWorkerPath { + workspace_id, + worker: RuntimeWorkerRef::new(runtime_id, worker_id), + }), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_unpin( + &self, + workspace_id: String, + runtime_id: String, + worker_id: String, + ) -> std::result::Result + { + let Json(response) = scoped_unpin_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimeWorkerPath { + workspace_id, + worker: RuntimeWorkerRef::new(runtime_id, worker_id), + }), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_cleanup_plan( + &self, + workspace_id: String, + runtime_id: String, + ) -> std::result::Result + { + let Json(response) = scoped_runtime_cleanup_plan( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimePath { + workspace_id, + runtime_id, + }), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_cleanup_execute( + &self, + workspace_id: String, + runtime_id: String, + request: server_api::ExecuteRuntimeCleanupRequest, + ) -> std::result::Result< + server_api::RuntimeCleanupExecutionResponse, + server_api::RepositoryApiError, + > { + let request = project_contract_dto(&request)?; + let Json(response) = scoped_execute_runtime_cleanup( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimePath { + workspace_id, + runtime_id, + }), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_input_alias( + &self, + runtime_id: String, + worker_id: String, + request: server_api::RuntimeWorkerInputRequest, + ) -> std::result::Result + { + let request = project_contract_dto(&request)?; + let Json(response) = send_runtime_worker_input( + State(self.workspace_api()?.clone()), + AxumPath((runtime_id, worker_id)), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_input( + &self, + workspace_id: String, + runtime_id: String, + worker_id: String, + request: server_api::RuntimeWorkerInputRequest, + ) -> std::result::Result + { + let request = project_contract_dto(&request)?; + let Json(response) = scoped_send_runtime_worker_input( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimeWorkerPath { + workspace_id, + worker: RuntimeWorkerRef::new(runtime_id, worker_id), + }), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_attachment_upload_grant( + &self, + actor: RequestActor, + workspace_id: String, + runtime_id: String, + worker_id: String, + query: server_api::WorkerFileUploadQuery, + ) -> std::result::Result< + server_api::AttachmentUploadGrantResponse, + server_api::RepositoryApiError, + > { + let query = project_contract_dto(&query)?; + let Json(response) = scoped_create_attachment_upload_grant( + State(self.workspace_api()?.clone()), + Extension(actor), + AxumPath(ScopedRuntimeWorkerPath { + workspace_id, + worker: RuntimeWorkerRef::new(runtime_id, worker_id), + }), + Query(query), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_attachment_upload_cancel( + &self, + actor: RequestActor, + workspace_id: String, + runtime_id: String, + worker_id: String, + upload_id: String, + ) -> std::result::Result< + server_api::AttachmentUploadCancelResponse, + server_api::RepositoryApiError, + > { + let Json(response) = scoped_cancel_attachment_upload( + State(self.workspace_api()?.clone()), + Extension(actor), + AxumPath(ScopedAttachmentUploadPath { + workspace_id, + runtime_id, + worker_id, + upload_id, + }), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_attachment_delete( + &self, + workspace_id: String, + runtime_id: String, + worker_id: String, + artifact_id: String, + ) -> std::result::Result + { + let Json(response) = scoped_delete_runtime_worker_uploaded_file( + State(self.workspace_api()?.clone()), + AxumPath(( + ScopedRuntimeWorkerPath { + workspace_id, + worker: RuntimeWorkerRef::new(runtime_id, worker_id), + }, + artifact_id, + )), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_completions_alias( + &self, + runtime_id: String, + worker_id: String, + request: server_api::RuntimeWorkerCompletionsRequest, + ) -> std::result::Result< + server_api::RuntimeWorkerCompletionsResult, + server_api::RepositoryApiError, + > { + let request = project_contract_dto(&request)?; + let Json(response) = runtime_worker_completions( + State(self.workspace_api()?.clone()), + AxumPath((runtime_id, worker_id)), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_completions( + &self, + workspace_id: String, + runtime_id: String, + worker_id: String, + request: server_api::RuntimeWorkerCompletionsRequest, + ) -> std::result::Result< + server_api::RuntimeWorkerCompletionsResult, + server_api::RepositoryApiError, + > { + let request = project_contract_dto(&request)?; + let Json(response) = scoped_runtime_worker_completions( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimeWorkerPath { + workspace_id, + worker: RuntimeWorkerRef::new(runtime_id, worker_id), + }), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_stop_alias( + &self, + runtime_id: String, + worker_id: String, + request: server_api::RuntimeWorkerLifecycleRequest, + ) -> std::result::Result + { + let request = project_contract_dto(&request)?; + let Json(response) = stop_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath((runtime_id, worker_id)), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_stop( + &self, + workspace_id: String, + runtime_id: String, + worker_id: String, + request: server_api::RuntimeWorkerLifecycleRequest, + ) -> std::result::Result + { + let request = project_contract_dto(&request)?; + let Json(response) = scoped_stop_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimeWorkerPath { + workspace_id, + worker: RuntimeWorkerRef::new(runtime_id, worker_id), + }), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_cancel_alias( + &self, + runtime_id: String, + worker_id: String, + request: server_api::RuntimeWorkerLifecycleRequest, + ) -> std::result::Result + { + let request = project_contract_dto(&request)?; + let Json(response) = cancel_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath((runtime_id, worker_id)), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + + async fn runtime_worker_cancel( + &self, + workspace_id: String, + runtime_id: String, + worker_id: String, + request: server_api::RuntimeWorkerLifecycleRequest, + ) -> std::result::Result + { + let request = project_contract_dto(&request)?; + let Json(response) = scoped_cancel_runtime_worker( + State(self.workspace_api()?.clone()), + AxumPath(ScopedRuntimeWorkerPath { + workspace_id, + worker: RuntimeWorkerRef::new(runtime_id, worker_id), + }), + Json(request), + ) + .await + .map_err(ApiError::into_repository_api_error)?; + project_contract_dto(&response) + } + async fn repository_list( &self, workspace_id: String, @@ -6630,25 +7414,6 @@ fn build_inner_router(api: WorkspaceApi) -> Router { "/api/w/{workspace_id}/runtime-config", get(get_latest_workspace_runtime_config), ) - .route("/api/runtimes", get(list_runtimes)) - .route( - "/api/w/{workspace_id}/runtimes", - get(scoped_list_runtimes).post(scoped_create_remote_runtime), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}", - get(scoped_get_runtime_detail) - .post(scoped_update_remote_runtime) - .delete(scoped_remove_remote_runtime), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/trust-key", - get(scoped_reveal_runtime_trust_key).delete(scoped_revoke_runtime_trust_key), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/connection-tests", - post(scoped_test_runtime_connection), - ) .route( "/api/workers", get(list_workers).post(create_workspace_worker), @@ -6738,104 +7503,11 @@ fn build_inner_router(api: WorkspaceApi) -> Router { "/api/w/{workspace_id}/workers/remove", post(scoped_worker_remove_source_boundary), ) - .route( - "/api/runtimes/{runtime_id}/workers", - get(list_runtime_workers).post(create_runtime_worker), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/workers", - get(scoped_list_runtime_workers).post(scoped_create_runtime_worker), - ) - .route( - "/api/runtimes/{runtime_id}/config-bundles", - post(sync_runtime_config_bundle), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/config-bundles", - post(scoped_sync_runtime_config_bundle), - ) - .route( - "/api/runtimes/{runtime_id}/config-bundles/{bundle_id}/availability", - get(check_runtime_config_bundle), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/config-bundles/{bundle_id}/availability", - get(scoped_check_runtime_config_bundle), - ) - .route( - "/api/runtimes/{runtime_id}/workers/{worker_id}", - get(get_runtime_worker), - ) - .route( - "/api/runtimes/{runtime_id}/workers/{worker_id}/restore", - post(restore_runtime_worker), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}", - get(scoped_get_runtime_worker), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/restore", - post(scoped_restore_runtime_worker), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/pin", - put(scoped_pin_runtime_worker).delete(scoped_unpin_runtime_worker), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-plan", - get(scoped_runtime_cleanup_plan), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-executions", - post(scoped_execute_runtime_cleanup), - ) - .route( - "/api/runtimes/{runtime_id}/workers/{worker_id}/input", - post(send_runtime_worker_input), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/input", - post(scoped_send_runtime_worker_input), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachment-upload-grants", - post(scoped_create_attachment_upload_grant), - ) .route( "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachment-uploads/{upload_id}", put(scoped_upload_runtime_worker_file) - .delete(scoped_cancel_attachment_upload) .layer(DefaultBodyLimit::max(MAX_WORKER_FILE_UPLOAD_BYTES)), ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachments/{artifact_id}", - delete(scoped_delete_runtime_worker_uploaded_file), - ) - .route( - "/api/runtimes/{runtime_id}/workers/{worker_id}/completions", - post(runtime_worker_completions), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/completions", - post(scoped_runtime_worker_completions), - ) - .route( - "/api/runtimes/{runtime_id}/workers/{worker_id}/stop", - post(stop_runtime_worker), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/stop", - post(scoped_stop_runtime_worker), - ) - .route( - "/api/runtimes/{runtime_id}/workers/{worker_id}/cancel", - post(cancel_runtime_worker), - ) - .route( - "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/cancel", - post(scoped_cancel_runtime_worker), - ) .route( "/api/runtimes/{runtime_id}/workers/{worker_id}/protocol/ws", get(worker_protocol_ws), @@ -16713,101 +17385,6 @@ async fn scoped_reveal_runtime_trust_key( })) } -async fn scoped_revoke_runtime_trust_key( - State(api): State, - AxumPath(path): AxumPath, - Extension(actor): Extension, - Json(request): Json, -) -> std::result::Result { - validate_workspace_scope(&api, &path.workspace_id)?; - require_workspace_owner(&api, &path.workspace_id, &actor, "Runtime trust changes").await?; - let actor_account_id = actor.account_id.clone(); - if path.runtime_id == EMBEDDED_WORKER_RUNTIME_ID { - return Err(settings_bad_request( - "embedded_runtime_trust_managed_internally", - "the embedded Runtime trust key is managed by the embedded Runtime authority", - )); - } - if request.expected_revision == 0 { - return Err(settings_bad_request( - "invalid_runtime_binding_revision", - "expected_revision must be greater than zero", - )); - } - let now = Utc::now().to_rfc3339(); - let mutation = api - .store - .revoke_workspace_runtime_binding_key( - &path.workspace_id, - &path.runtime_id, - request.expected_revision, - &actor_account_id, - &now, - ) - .await; - let _ = match mutation { - Ok(result) => result, - Err(error) => { - if let Some(response) = runtime_trust_conflict_response(&api, &path, &error).await { - return Ok(response); - } - return Err(error.into()); - } - }; - api.runtime_binding_expectations - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .remove(&(path.workspace_id.clone(), path.runtime_id.clone())); - api.runtime_subscription_broker - .unregister_runtime(&path.runtime_id); - Ok( - Json(workspace_runtime_detail(&api, &path.workspace_id, &path.runtime_id).await?) - .into_response(), - ) -} - -async fn runtime_trust_conflict_response( - api: &WorkspaceApi, - path: &ScopedRuntimePath, - error: &Error, -) -> Option { - let kind = match error { - Error::RuntimeBindingRevisionConflict { .. } => RuntimeTrustConflictKind::StaleRevision, - Error::RuntimeBindingFingerprintConflict { .. } => { - RuntimeTrustConflictKind::FingerprintInUse - } - _ => return None, - }; - let current = api - .store - .get_workspace_runtime_binding(&path.workspace_id, &path.runtime_id) - .await - .ok() - .flatten(); - Some( - ( - StatusCode::CONFLICT, - Json(RuntimeTrustConflictResponse { - error: kind, - message: match kind { - RuntimeTrustConflictKind::StaleRevision => { - "the Runtime trust binding changed; reload before retrying".to_string() - } - RuntimeTrustConflictKind::FingerprintInUse => { - "the public key is already bound to another Runtime in this Workspace" - .to_string() - } - }, - current_revision: current.as_ref().map(|binding| binding.binding_revision), - current_fingerprint: current - .as_ref() - .map(|binding| binding.public_key_fingerprint.clone()), - }), - ) - .into_response(), - ) -} - async fn scoped_remove_remote_runtime( State(api): State, AxumPath(path): AxumPath, @@ -21059,6 +21636,7 @@ async fn workspace_runtime_resources_response( runtime_binding_summary(binding, verifications.get(&binding.runtime_id)) }), }, + http_status: StatusCode::OK.as_u16(), } }) .collect::>(); @@ -21108,6 +21686,7 @@ async fn workspace_runtime_resources_response( verifications.get(&binding.runtime_id), )), }, + http_status: StatusCode::OK.as_u16(), }); } @@ -21229,6 +21808,7 @@ async fn workspace_runtime_detail( token_ref_configured: false, binding: Some(runtime_binding_summary(&binding, None)), }, + http_status: StatusCode::OK.as_u16(), }); } let mut resource = resource.ok_or_else(|| Error::UnknownRuntime(runtime_id.to_string()))?; @@ -32802,34 +33382,30 @@ mod tests { .unwrap_err(); assert_eq!(denied.into_response().status(), StatusCode::FORBIDDEN); - let denied = scoped_revoke_runtime_trust_key( - State(api.clone()), - AxumPath(ScopedRuntimePath { - workspace_id: TEST_WORKSPACE_ID.to_owned(), - runtime_id: "runtime-a".to_owned(), - }), - Extension(non_owner), - Json(RevokeRuntimeTrustKeyRequest { + let contract_service = ServerApiContractService::Workspace(api.clone()); + let denied = ::runtime_trust_key_revoke( + &contract_service, + non_owner, + TEST_WORKSPACE_ID.to_owned(), + "runtime-a".to_owned(), + server_api::RevokeRuntimeTrustKeyRequest { expected_revision: 1, - }), + }, ) .await .unwrap_err(); - assert_eq!(denied.into_response().status(), StatusCode::FORBIDDEN); - let response = scoped_revoke_runtime_trust_key( - State(api.clone()), - AxumPath(ScopedRuntimePath { - workspace_id: TEST_WORKSPACE_ID.to_owned(), - runtime_id: "runtime-a".to_owned(), - }), - Extension(owner), - Json(RevokeRuntimeTrustKeyRequest { + assert_eq!(denied.error, "Forbidden"); + ::runtime_trust_key_revoke( + &contract_service, + owner, + TEST_WORKSPACE_ID.to_owned(), + "runtime-a".to_owned(), + server_api::RevokeRuntimeTrustKeyRequest { expected_revision: 1, - }), + }, ) .await .unwrap(); - assert_eq!(response.status(), StatusCode::OK); let binding = api .store .get_workspace_runtime_binding(TEST_WORKSPACE_ID, "runtime-a") diff --git a/openapi/server-api.json b/openapi/server-api.json index 19327a623..3a47d8674 100644 --- a/openapi/server-api.json +++ b/openapi/server-api.json @@ -81,6 +81,37 @@ }, "type": "array" }, + "AttachmentUploadCancelResponse": { + "additionalProperties": false, + "properties": { + "cancelled": { + "type": "boolean" + } + }, + "required": [ + "cancelled" + ], + "type": "object" + }, + "AttachmentUploadGrantResponse": { + "additionalProperties": false, + "properties": { + "expires_at_ms": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "upload_id": { + "type": "string" + } + }, + "required": [ + "upload_id", + "expires_at_ms" + ], + "type": "object" + }, "AuditEvent": { "oneOf": [ { @@ -455,6 +486,179 @@ ], "type": "string" }, + "CleanupTargetKind": { + "enum": [ + "worker_delete", + "workdir_clean_cleanup", + "workdir_dirty_discard", + "workdir_record_delete" + ], + "type": "string" + }, + "CleanupWorkdirCandidate": { + "additionalProperties": false, + "properties": { + "action": { + "$ref": "#/components/schemas/CleanupTargetKind" + }, + "blocking_reason": { + "type": [ + "string", + "null" + ] + }, + "cleanliness": { + "$ref": "#/components/schemas/CleanupWorkdirCleanliness" + }, + "estimated_reclaim_bytes": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "file_status": { + "$ref": "#/components/schemas/CleanupWorkdirFileStatus" + }, + "linked_running_worker_ids": { + "items": { + "type": "string" + }, + "type": "array" + }, + "linked_worker_ids": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pinned_linked": { + "type": "boolean" + }, + "reason": { + "type": "string" + }, + "repository_key": { + "type": "string" + }, + "running_linked": { + "type": "boolean" + }, + "runtime_id": { + "type": "string" + }, + "target_id": { + "type": "string" + }, + "workdir_id": { + "type": "string" + } + }, + "required": [ + "target_id", + "action", + "workdir_id", + "runtime_id", + "repository_key", + "reason", + "linked_worker_ids", + "linked_running_worker_ids", + "running_linked", + "pinned_linked", + "file_status", + "cleanliness" + ], + "type": "object" + }, + "CleanupWorkdirCleanliness": { + "enum": [ + "clean", + "dirty", + "unknown" + ], + "type": "string" + }, + "CleanupWorkdirFileStatus": { + "enum": [ + "pending", + "present", + "active", + "cleanup_pending", + "not_found", + "corrupted", + "failed", + "unknown" + ], + "type": "string" + }, + "CleanupWorkerCandidate": { + "additionalProperties": false, + "properties": { + "action": { + "$ref": "#/components/schemas/CleanupTargetKind" + }, + "blocking_reason": { + "type": [ + "string", + "null" + ] + }, + "estimated_reclaim_bytes": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "linked_workdir_ids": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pinned": { + "type": "boolean" + }, + "reason": { + "type": "string" + }, + "retention_state": { + "type": "string" + }, + "running_linked": { + "type": "boolean" + }, + "runtime_id": { + "type": "string" + }, + "runtime_worker_id": { + "type": "string" + }, + "target_id": { + "type": "string" + }, + "worker_id": { + "type": "string" + } + }, + "required": [ + "target_id", + "action", + "worker_id", + "runtime_worker_id", + "runtime_id", + "reason", + "pinned", + "retention_state", + "linked_workdir_ids", + "running_linked" + ], + "type": "object" + }, "ClearTicketRoleAssignmentQuery": { "additionalProperties": false, "properties": { @@ -1060,6 +1264,37 @@ }, "type": "object" }, + "CreateRemoteRuntimeRequest": { + "additionalProperties": false, + "properties": { + "display_name": { + "type": [ + "string", + "null" + ] + }, + "endpoint": { + "type": "string" + }, + "expected_revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "public_bundle": { + "$ref": "#/components/schemas/RuntimePublicIdentityBundle" + } + }, + "required": [ + "public_bundle", + "endpoint" + ], + "type": "object" + }, "CreateRepositorySshCredentialRequest": { "additionalProperties": false, "properties": { @@ -1636,6 +1871,43 @@ ], "type": "string" }, + "ExecuteRuntimeCleanupRequest": { + "additionalProperties": false, + "properties": { + "confirm_dirty_discard_target_ids": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "expected_plan_digest": { + "type": "string" + }, + "expected_plan_revision": { + "type": "string" + }, + "workdir_target_ids": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "worker_target_ids": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "expected_plan_revision", + "expected_plan_digest" + ], + "type": "object" + }, "ExtensibleTicketStatus": { "oneOf": [ { @@ -5451,6 +5723,25 @@ ], "type": "object" }, + "RemoveRuntimeRequest": { + "additionalProperties": false, + "properties": { + "expected_binding_revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "operation_id": { + "type": "string" + } + }, + "required": [ + "operation_id", + "expected_binding_revision" + ], + "type": "object" + }, "RepairMergeRequestSelectorRequest": { "additionalProperties": false, "properties": { @@ -6175,6 +6466,24 @@ ], "type": "object" }, + "RestoreTicketAssignmentQuery": { + "additionalProperties": false, + "properties": { + "assignment_operation_id": { + "type": [ + "string", + "null" + ] + }, + "ticket_id": { + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, "ReviewDecision": { "enum": [ "approve", @@ -6327,6 +6636,21 @@ ], "type": "object" }, + "RevokeRuntimeTrustKeyRequest": { + "additionalProperties": false, + "properties": { + "expected_revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "expected_revision" + ], + "type": "object" + }, "RotateRepositorySshCredentialRequest": { "additionalProperties": false, "properties": { @@ -6357,6 +6681,1427 @@ ], "type": "object" }, + "RuntimeCleanupExecutionResponse": { + "additionalProperties": false, + "properties": { + "diagnostics": { + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "executed_at": { + "type": "string" + }, + "plan_after": { + "$ref": "#/components/schemas/RuntimeCleanupPlanResponse" + }, + "results": { + "items": { + "$ref": "#/components/schemas/RuntimeCleanupExecutionResult" + }, + "type": "array" + }, + "runtime_id": { + "type": "string" + }, + "workspace_id": { + "type": "string" + } + }, + "required": [ + "workspace_id", + "runtime_id", + "executed_at", + "results", + "plan_after", + "diagnostics" + ], + "type": "object" + }, + "RuntimeCleanupExecutionResult": { + "additionalProperties": false, + "properties": { + "action": { + "$ref": "#/components/schemas/CleanupTargetKind" + }, + "message": { + "type": "string" + }, + "status": { + "type": "string" + }, + "target_id": { + "type": "string" + } + }, + "required": [ + "target_id", + "action", + "status", + "message" + ], + "type": "object" + }, + "RuntimeCleanupPlanResponse": { + "additionalProperties": false, + "properties": { + "diagnostics": { + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "digest": { + "type": "string" + }, + "generated_at": { + "type": "string" + }, + "revision": { + "type": "string" + }, + "runtime_id": { + "type": "string" + }, + "workdirs": { + "items": { + "$ref": "#/components/schemas/CleanupWorkdirCandidate" + }, + "type": "array" + }, + "workers": { + "items": { + "$ref": "#/components/schemas/CleanupWorkerCandidate" + }, + "type": "array" + }, + "workspace_id": { + "type": "string" + } + }, + "required": [ + "workspace_id", + "runtime_id", + "generated_at", + "revision", + "digest", + "workers", + "workdirs", + "diagnostics" + ], + "type": "object" + }, + "RuntimeConfigBundleAvailabilityQuery": { + "additionalProperties": false, + "properties": { + "digest": { + "type": "string" + } + }, + "required": [ + "digest" + ], + "type": "object" + }, + "RuntimeConfigBundleResult": { + "additionalProperties": false, + "properties": { + "availability": true, + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "state": { + "$ref": "#/components/schemas/WorkerOperationState" + } + }, + "required": [ + "state" + ], + "type": "object" + }, + "RuntimeConfigBundleSyncRequest": { + "additionalProperties": false, + "properties": { + "bundle": true + }, + "required": [ + "bundle" + ], + "type": "object" + }, + "RuntimeConnectionDisplayState": { + "enum": [ + "configured", + "verified", + "unavailable", + "revoked" + ], + "type": "string" + }, + "RuntimeConnectionTestFailureKind": { + "enum": [ + "authentication", + "authorization", + "network_unreachable", + "timeout", + "tls_or_transport", + "malformed_response", + "protocol_version_mismatch", + "runtime_identity_mismatch", + "configuration" + ], + "type": "string" + }, + "RuntimeConnectionTestResponse": { + "additionalProperties": false, + "properties": { + "actual_protocol_version": { + "format": "uint32", + "maximum": 4294967295, + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "binding_revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "checked_at": { + "type": "string" + }, + "connection_state": { + "$ref": "#/components/schemas/RuntimeConnectionDisplayState" + }, + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "expected_protocol_version": { + "format": "uint32", + "maximum": 4294967295, + "minimum": 0, + "type": "integer" + }, + "failure_kind": { + "anyOf": [ + { + "$ref": "#/components/schemas/RuntimeConnectionTestFailureKind" + }, + { + "type": "null" + } + ] + }, + "runtime_id": { + "type": "string" + }, + "status": { + "$ref": "#/components/schemas/RuntimeConnectionTestStatus" + }, + "verification": { + "anyOf": [ + { + "$ref": "#/components/schemas/RuntimeVerificationEvidenceSummary" + }, + { + "type": "null" + } + ] + }, + "workspace_id": { + "type": "string" + } + }, + "required": [ + "workspace_id", + "runtime_id", + "binding_revision", + "connection_state", + "checked_at", + "status", + "expected_protocol_version" + ], + "type": "object" + }, + "RuntimeConnectionTestStatus": { + "enum": [ + "compatible", + "failed" + ], + "type": "string" + }, + "RuntimeIdentityAuthority": { + "enum": [ + "runtime_registry_projection", + "server_runtime_configuration" + ], + "type": "string" + }, + "RuntimeListResponse": { + "additionalProperties": false, + "properties": { + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "items": { + "items": { + "$ref": "#/components/schemas/RuntimeSummary" + }, + "type": "array" + }, + "limit": { + "format": "uint", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "source": { + "type": "string" + }, + "workspace_id": { + "type": "string" + } + }, + "required": [ + "workspace_id", + "limit", + "items", + "source" + ], + "type": "object" + }, + "RuntimeManagementApiError": { + "additionalProperties": false, + "properties": { + "current_fingerprint": { + "type": [ + "string", + "null" + ] + }, + "current_revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "diagnostics": { + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "error", + "message" + ], + "type": "object" + }, + "RuntimeManagementSummary": { + "additionalProperties": false, + "properties": { + "binding": { + "anyOf": [ + { + "$ref": "#/components/schemas/WorkspaceRuntimeBindingSummary" + }, + { + "type": "null" + } + ] + }, + "built_in": { + "type": "boolean" + }, + "config_managed": { + "type": "boolean" + }, + "endpoint_configured": { + "type": "boolean" + }, + "removable": { + "type": "boolean" + }, + "token_ref_configured": { + "type": "boolean" + } + }, + "required": [ + "built_in", + "config_managed", + "removable", + "endpoint_configured", + "token_ref_configured" + ], + "type": "object" + }, + "RuntimeProfileSelector": { + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "kind": { + "const": "builtin", + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "kind", + "value" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "kind": { + "const": "named", + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "kind", + "value" + ], + "type": "object" + } + ] + }, + "RuntimePublicIdentityBundle": { + "additionalProperties": false, + "properties": { + "identity_id": { + "type": "string" + }, + "public_key": { + "type": "string" + } + }, + "required": [ + "identity_id", + "public_key" + ], + "type": "object" + }, + "RuntimeRemovalOperationResponse": { + "additionalProperties": false, + "properties": { + "binding_removed": { + "type": "boolean" + }, + "completed_at": { + "type": [ + "string", + "null" + ] + }, + "created_at": { + "type": "string" + }, + "failure_category": { + "type": [ + "string", + "null" + ] + }, + "operation_id": { + "type": "string" + }, + "runtime_id": { + "type": "string" + }, + "runtime_registration_removed": { + "type": [ + "boolean", + "null" + ] + }, + "state": { + "$ref": "#/components/schemas/RuntimeRemovalOperationState" + }, + "updated_at": { + "type": "string" + }, + "workspace_id": { + "type": "string" + } + }, + "required": [ + "operation_id", + "workspace_id", + "runtime_id", + "state", + "binding_removed", + "created_at", + "updated_at" + ], + "type": "object" + }, + "RuntimeRemovalOperationState": { + "enum": [ + "pending", + "cleanup_pending", + "succeeded", + "failed" + ], + "type": "string" + }, + "RuntimeSourceKind": { + "enum": [ + "embedded_worker_runtime", + "remote_http" + ], + "type": "string" + }, + "RuntimeSourceStatus": { + "enum": [ + "active", + "reserved" + ], + "type": "string" + }, + "RuntimeSourceSummary": { + "additionalProperties": false, + "properties": { + "identity_authority": { + "$ref": "#/components/schemas/RuntimeIdentityAuthority" + }, + "kind": { + "$ref": "#/components/schemas/RuntimeSourceKind" + }, + "note": { + "type": "string" + }, + "status": { + "$ref": "#/components/schemas/RuntimeSourceStatus" + } + }, + "required": [ + "kind", + "status", + "identity_authority", + "note" + ], + "type": "object" + }, + "RuntimeSummary": { + "properties": { + "arch": { + "type": "string" + }, + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "host_ids": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "kind": { + "type": "string" + }, + "label": { + "type": "string" + }, + "os": { + "type": "string" + }, + "runtime_id": { + "type": "string" + }, + "source": { + "$ref": "#/components/schemas/RuntimeSourceSummary" + }, + "status": { + "type": "string" + }, + "worker_creation_available": { + "type": "boolean" + } + }, + "required": [ + "runtime_id", + "label", + "kind", + "status", + "source", + "worker_creation_available", + "os", + "arch" + ], + "type": "object" + }, + "RuntimeTicketWorkerRole": { + "enum": [ + "intake", + "orchestrator", + "coder", + "reviewer" + ], + "type": "string" + }, + "RuntimeTrustAuditAction": { + "enum": [ + "created", + "replaced", + "reactivated", + "revoked" + ], + "type": "string" + }, + "RuntimeTrustAuditEntry": { + "additionalProperties": false, + "properties": { + "action": { + "$ref": "#/components/schemas/RuntimeTrustAuditAction" + }, + "actor_account_id": { + "type": "string" + }, + "at": { + "type": "string" + }, + "new_fingerprint": { + "type": [ + "string", + "null" + ] + }, + "old_fingerprint": { + "type": [ + "string", + "null" + ] + }, + "revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "action", + "actor_account_id", + "revision", + "at" + ], + "type": "object" + }, + "RuntimeTrustKeyRevealResponse": { + "additionalProperties": false, + "properties": { + "public_key": { + "type": "string" + } + }, + "required": [ + "public_key" + ], + "type": "object" + }, + "RuntimeTrustKeyState": { + "additionalProperties": false, + "properties": { + "created_at": { + "type": [ + "string", + "null" + ] + }, + "fingerprint": { + "type": [ + "string", + "null" + ] + }, + "revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "revoked_at": { + "type": [ + "string", + "null" + ] + }, + "status": { + "$ref": "#/components/schemas/RuntimeTrustKeyStatus" + }, + "updated_at": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "status" + ], + "type": "object" + }, + "RuntimeTrustKeyStatus": { + "enum": [ + "unconfigured", + "active", + "revoked" + ], + "type": "string" + }, + "RuntimeVerificationEvidenceSummary": { + "additionalProperties": false, + "properties": { + "binding_revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "last_checked_at": { + "type": "string" + }, + "last_outcome": { + "$ref": "#/components/schemas/RuntimeVerificationOutcome" + }, + "runtime_identity_revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "runtime_public_key_fingerprint": { + "type": "string" + }, + "verified_at": { + "type": [ + "string", + "null" + ] + }, + "workspace_identity_revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "workspace_key_id": { + "type": "string" + }, + "workspace_trust_generation": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "last_checked_at", + "last_outcome", + "binding_revision", + "workspace_key_id", + "workspace_identity_revision", + "workspace_trust_generation", + "runtime_public_key_fingerprint", + "runtime_identity_revision" + ], + "type": "object" + }, + "RuntimeVerificationOutcome": { + "enum": [ + "verified", + "challenge_issued", + "verification_failed", + "connectivity_failed" + ], + "type": "string" + }, + "RuntimeWorkerCompletionsRequest": { + "additionalProperties": false, + "properties": { + "kind": true, + "prefix": { + "default": "", + "type": "string" + } + }, + "required": [ + "kind" + ], + "type": "object" + }, + "RuntimeWorkerCompletionsResult": { + "additionalProperties": false, + "properties": { + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "entries": { + "items": true, + "type": "array" + }, + "kind": true, + "prefix": { + "type": "string" + }, + "runtime_id": { + "type": "string" + }, + "worker_id": { + "type": "string" + } + }, + "required": [ + "runtime_id", + "worker_id", + "kind", + "prefix", + "entries" + ], + "type": "object" + }, + "RuntimeWorkerInputRequest": { + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "kind": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "segments": { + "items": true, + "type": [ + "array", + "null" + ] + } + }, + "required": [ + "content" + ], + "type": "object" + }, + "RuntimeWorkerInputResult": { + "additionalProperties": false, + "properties": { + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "runtime_id": { + "type": "string" + }, + "state": { + "$ref": "#/components/schemas/WorkerOperationState" + }, + "worker_id": { + "type": "string" + } + }, + "required": [ + "state", + "runtime_id", + "worker_id" + ], + "type": "object" + }, + "RuntimeWorkerLifecycleRequest": { + "additionalProperties": false, + "properties": { + "reason": { + "type": [ + "string", + "null" + ] + }, + "ticket_assignment": { + "anyOf": [ + { + "$ref": "#/components/schemas/RuntimeWorkerTicketAssignmentRequest" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "RuntimeWorkerLifecycleResult": { + "additionalProperties": false, + "properties": { + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "runtime_id": { + "type": "string" + }, + "state": { + "$ref": "#/components/schemas/WorkerOperationState" + }, + "worker_id": { + "type": "string" + } + }, + "required": [ + "state", + "runtime_id", + "worker_id" + ], + "type": "object" + }, + "RuntimeWorkerListResponse": { + "additionalProperties": false, + "properties": { + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "items": { + "items": { + "$ref": "#/components/schemas/WorkerSummary" + }, + "type": "array" + }, + "limit": { + "format": "uint", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "source": { + "type": "string" + }, + "workspace_id": { + "type": "string" + } + }, + "required": [ + "workspace_id", + "limit", + "items", + "source" + ], + "type": "object" + }, + "RuntimeWorkerShowResponse": { + "additionalProperties": false, + "description": "Workspace-authoritative Worker projection.\n\n`resource_key` is required here even though Runtime-internal Worker summaries\ndo not carry one. The Workspace Server must resolve it from Workspace\nauthority before constructing this response.", + "properties": { + "capabilities": { + "$ref": "#/components/schemas/WorkerCapabilitySummary" + }, + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "display_name": { + "default": "", + "type": "string" + }, + "host_id": { + "type": "string" + }, + "implementation": { + "$ref": "#/components/schemas/WorkerImplementationSummary" + }, + "label": { + "type": "string" + }, + "last_seen_at": { + "type": [ + "string", + "null" + ] + }, + "pinned": { + "default": false, + "type": "boolean" + }, + "profile": { + "type": [ + "string", + "null" + ] + }, + "resource_key": { + "type": "string" + }, + "retention_state": { + "default": "", + "type": "string" + }, + "runtime_id": { + "type": "string" + }, + "singleton_key": { + "type": [ + "string", + "null" + ] + }, + "state": { + "description": "Runtime catalog lifecycle compatibility state. Live foreground state, when\navailable, is carried separately in `worker_state`.", + "type": "string" + }, + "tags": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "updated_at": { + "type": "string" + }, + "workdir_attachments": { + "items": { + "$ref": "#/components/schemas/WorkerWorkdirAttachmentSummary" + }, + "type": "array" + }, + "worker_id": { + "type": "string" + }, + "worker_state": true, + "workspace": { + "$ref": "#/components/schemas/WorkerWorkspaceSummary" + } + }, + "required": [ + "runtime_id", + "worker_id", + "resource_key", + "host_id", + "label", + "workspace", + "state", + "implementation", + "capabilities", + "updated_at" + ], + "type": "object" + }, + "RuntimeWorkerSpawnAcceptanceEvidence": { + "additionalProperties": false, + "properties": { + "detail": { + "type": "string" + }, + "kind": { + "type": "string" + } + }, + "required": [ + "kind", + "detail" + ], + "type": "object" + }, + "RuntimeWorkerSpawnAcceptanceRequirement": { + "oneOf": [ + { + "enum": [ + "socket_ready" + ], + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "run_accepted": { + "properties": { + "expected_segments": { + "format": "uint", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "expected_segments" + ], + "type": "object" + } + }, + "required": [ + "run_accepted" + ], + "type": "object" + } + ] + }, + "RuntimeWorkerSpawnIntent": { + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "kind": { + "const": "workspace_companion", + "type": "string" + } + }, + "required": [ + "kind" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "kind": { + "const": "workspace_orchestrator", + "type": "string" + } + }, + "required": [ + "kind" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "kind": { + "const": "workspace_coding", + "type": "string" + } + }, + "required": [ + "kind" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "kind": { + "const": "ticket_role", + "type": "string" + }, + "role": { + "$ref": "#/components/schemas/RuntimeTicketWorkerRole" + }, + "ticket_id": { + "type": "string" + } + }, + "required": [ + "kind", + "ticket_id", + "role" + ], + "type": "object" + } + ] + }, + "RuntimeWorkerSpawnRequest": { + "additionalProperties": false, + "properties": { + "acceptance": { + "$ref": "#/components/schemas/RuntimeWorkerSpawnAcceptanceRequirement" + }, + "initial_submit": { + "items": true, + "type": "array" + }, + "intent": { + "$ref": "#/components/schemas/RuntimeWorkerSpawnIntent" + }, + "profile": { + "$ref": "#/components/schemas/RuntimeProfileSelector" + }, + "requested_worker_name": { + "type": [ + "string", + "null" + ] + }, + "ticket_assignment": { + "anyOf": [ + { + "$ref": "#/components/schemas/RuntimeWorkerTicketAssignmentRequest" + }, + { + "type": "null" + } + ] + }, + "workdir_attachment_requests": { + "items": { + "$ref": "#/components/schemas/RuntimeWorkerSpawnWorkingDirectoryAttachmentRequest" + }, + "type": "array" + } + }, + "required": [ + "intent", + "acceptance", + "profile" + ], + "type": "object" + }, + "RuntimeWorkerSpawnResponse": { + "additionalProperties": false, + "properties": { + "acceptance_evidence": { + "default": [], + "items": { + "$ref": "#/components/schemas/RuntimeWorkerSpawnAcceptanceEvidence" + }, + "type": "array" + }, + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "state": { + "$ref": "#/components/schemas/WorkerOperationState" + }, + "worker": { + "anyOf": [ + { + "$ref": "#/components/schemas/RuntimeWorkerSummary" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "state" + ], + "type": "object" + }, + "RuntimeWorkerSpawnWorkingDirectoryAttachmentRequest": { + "additionalProperties": false, + "properties": { + "alias": { + "type": "string" + }, + "working_directory": { + "$ref": "#/components/schemas/RuntimeWorkerSpawnWorkingDirectoryRequest" + } + }, + "required": [ + "alias", + "working_directory" + ], + "type": "object" + }, + "RuntimeWorkerSpawnWorkingDirectoryRequest": { + "additionalProperties": false, + "properties": { + "repository_key": { + "type": "string" + }, + "selector": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "repository_key" + ], + "type": "object" + }, + "RuntimeWorkerSummary": { + "additionalProperties": false, + "properties": { + "capabilities": { + "$ref": "#/components/schemas/WorkerCapabilitySummary" + }, + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "display_name": { + "default": "", + "type": "string" + }, + "host_id": { + "type": "string" + }, + "implementation": { + "$ref": "#/components/schemas/WorkerImplementationSummary" + }, + "label": { + "type": "string" + }, + "last_seen_at": { + "type": [ + "string", + "null" + ] + }, + "pinned": { + "default": false, + "type": "boolean" + }, + "profile": { + "type": [ + "string", + "null" + ] + }, + "retention_state": { + "default": "", + "type": "string" + }, + "runtime_id": { + "type": "string" + }, + "singleton_key": { + "type": [ + "string", + "null" + ] + }, + "state": { + "type": "string" + }, + "tags": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "workdir_attachments": { + "items": true, + "type": "array" + }, + "worker_id": { + "type": "string" + }, + "worker_state": true, + "workspace": { + "$ref": "#/components/schemas/WorkerWorkspaceSummary" + } + }, + "required": [ + "runtime_id", + "worker_id", + "host_id", + "label", + "workspace", + "state", + "implementation", + "capabilities" + ], + "type": "object" + }, + "RuntimeWorkerTicketAssignmentRequest": { + "additionalProperties": false, + "properties": { + "operation_id": { + "type": "string" + }, + "ticket_id": { + "type": "string" + } + }, + "required": [ + "ticket_id", + "operation_id" + ], + "type": "object" + }, + "RuntimeWorkersQuery": { + "additionalProperties": false, + "properties": { + "status": { + "anyOf": [ + { + "$ref": "#/components/schemas/RuntimeWorkersStatusFilter" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "RuntimeWorkersStatusFilter": { + "enum": [ + "stopped" + ], + "type": "string" + }, "ServerJsonSafeU64": { "format": "uint64", "maximum": 9007199254740991, @@ -9222,6 +10967,24 @@ ], "type": "object" }, + "UpdateRemoteRuntimeRequest": { + "additionalProperties": false, + "properties": { + "display_name": { + "type": [ + "string", + "null" + ] + }, + "endpoint": { + "type": "string" + } + }, + "required": [ + "endpoint" + ], + "type": "object" + }, "UpdateWorkspaceMemorySettingsRequest": { "additionalProperties": false, "description": "Compare-and-swap update for Workspace-owned Memory settings.", @@ -9940,6 +11703,41 @@ ], "type": "object" }, + "WorkerFileDeleteResponse": { + "additionalProperties": false, + "properties": { + "deleted": { + "type": "boolean" + } + }, + "required": [ + "deleted" + ], + "type": "object" + }, + "WorkerFileUploadQuery": { + "additionalProperties": false, + "properties": { + "file_name": { + "type": "string" + }, + "media_type": { + "type": "string" + }, + "upload_id": { + "default": null, + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "file_name", + "media_type" + ], + "type": "object" + }, "WorkerImplementationSummary": { "properties": { "display_hint": { @@ -10042,6 +11840,105 @@ ], "type": "string" }, + "WorkerOperationState": { + "enum": [ + "accepted", + "unsupported", + "rejected" + ], + "type": "string" + }, + "WorkerRestoreResponse": { + "additionalProperties": false, + "properties": { + "result": { + "$ref": "#/components/schemas/WorkerRestoreResult" + }, + "runtime_id": { + "type": "string" + }, + "worker_id": { + "type": "string" + }, + "workspace_id": { + "type": "string" + } + }, + "required": [ + "workspace_id", + "runtime_id", + "worker_id", + "result" + ], + "type": "object" + }, + "WorkerRestoreResult": { + "additionalProperties": false, + "properties": { + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "state": { + "$ref": "#/components/schemas/WorkerRestoreState" + }, + "worker": { + "anyOf": [ + { + "$ref": "#/components/schemas/WorkerSummary" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "state" + ], + "type": "object" + }, + "WorkerRestoreState": { + "description": "Cross-layer outcome of an explicit Worker restore operation.\n\n`Rejected` is reserved for read-only preflight failures. Once live restore\nwork starts, failure is either a confirmed `RolledBack` operation or a\n`ReconciliationRequired` result whose commit state must be reread/retried.", + "enum": [ + "accepted", + "rejected", + "rolled_back", + "reconciliation_required" + ], + "type": "string" + }, + "WorkerRetentionResponse": { + "additionalProperties": false, + "properties": { + "pinned": { + "type": "boolean" + }, + "retention_state": { + "type": "string" + }, + "runtime_id": { + "type": "string" + }, + "worker_id": { + "type": "string" + }, + "workspace_id": { + "type": "string" + } + }, + "required": [ + "workspace_id", + "runtime_id", + "worker_id", + "pinned", + "retention_state" + ], + "type": "object" + }, "WorkerSummary": { "description": "Workspace-authoritative Worker projection.\n\n`resource_key` is required here even though Runtime-internal Worker summaries\ndo not carry one. The Workspace Server must resolve it from Workspace\nauthority before constructing this response.", "properties": { @@ -11375,6 +13272,189 @@ ], "type": "object" }, + "WorkspaceRuntimeBindingState": { + "enum": [ + "configured", + "verified", + "revoked" + ], + "type": "string" + }, + "WorkspaceRuntimeBindingSummary": { + "additionalProperties": false, + "properties": { + "connection_state": { + "$ref": "#/components/schemas/RuntimeConnectionDisplayState" + }, + "revision": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "state": { + "$ref": "#/components/schemas/WorkspaceRuntimeBindingState" + }, + "verification": { + "anyOf": [ + { + "$ref": "#/components/schemas/RuntimeVerificationEvidenceSummary" + }, + { + "type": "null" + } + ] + }, + "workspace_key_generation": { + "format": "uint64", + "maximum": 9007199254740991, + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "workspace_key_id": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "state", + "connection_state", + "revision" + ], + "type": "object" + }, + "WorkspaceRuntimeDetail": { + "additionalProperties": false, + "properties": { + "endpoint": { + "type": [ + "string", + "null" + ] + }, + "recent_audit": { + "default": [], + "items": { + "$ref": "#/components/schemas/RuntimeTrustAuditEntry" + }, + "type": "array" + }, + "runtime": { + "$ref": "#/components/schemas/WorkspaceRuntimeResource" + }, + "trust_key": { + "$ref": "#/components/schemas/RuntimeTrustKeyState" + }, + "workspace_id": { + "type": "string" + } + }, + "required": [ + "workspace_id", + "runtime", + "trust_key" + ], + "type": "object" + }, + "WorkspaceRuntimeListResponse": { + "additionalProperties": false, + "properties": { + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "items": { + "items": { + "$ref": "#/components/schemas/WorkspaceRuntimeResource" + }, + "type": "array" + }, + "limit": { + "format": "uint", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "source": { + "type": "string" + }, + "workspace_id": { + "type": "string" + } + }, + "required": [ + "workspace_id", + "limit", + "items", + "source" + ], + "type": "object" + }, + "WorkspaceRuntimeResource": { + "properties": { + "arch": { + "type": "string" + }, + "diagnostics": { + "default": [], + "items": { + "$ref": "#/components/schemas/Diagnostic" + }, + "type": "array" + }, + "host_ids": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "kind": { + "type": "string" + }, + "label": { + "type": "string" + }, + "management": { + "$ref": "#/components/schemas/RuntimeManagementSummary" + }, + "os": { + "type": "string" + }, + "runtime_id": { + "type": "string" + }, + "source": { + "$ref": "#/components/schemas/RuntimeSourceSummary" + }, + "status": { + "type": "string" + }, + "worker_creation_available": { + "type": "boolean" + } + }, + "required": [ + "runtime_id", + "label", + "kind", + "status", + "source", + "worker_creation_available", + "os", + "arch", + "management" + ], + "type": "object" + }, "WorkspaceSigningIdentityPublic": { "additionalProperties": false, "description": "Public metadata for a Workspace signing identity. Private material and its\nstorage reference are deliberately not part of this wire authority.", @@ -11565,7 +13645,7 @@ "info": { "title": "Yoi Server API", "version": "0.1.0", - "x-yoi-source-digest": "sha256:e3f776a17079ce202725bb97de607426f4bfad9afe390ba7bbf76186e871be59" + "x-yoi-source-digest": "sha256:67e56d1ffd3b27bc8653ef791bd68b5150ac7d507ccbe4e65b2e00320834d718" }, "openapi": "3.1.0", "paths": { @@ -12532,6 +14612,1399 @@ ] } }, + "/api/runtimes": { + "get": { + "operationId": "runtime_list_alias", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeListResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/runtimes/{runtime_id}/config-bundles": { + "post": { + "operationId": "runtime_config_bundle_sync_alias", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeConfigBundleSyncRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeConfigBundleResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/runtimes/{runtime_id}/config-bundles/{bundle_id}/availability": { + "get": { + "operationId": "runtime_config_bundle_availability_alias", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "bundle_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": true, + "in": "query", + "name": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/RuntimeConfigBundleAvailabilityQuery" + }, + "style": "form" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeConfigBundleResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/runtimes/{runtime_id}/workers": { + "get": { + "operationId": "runtime_worker_list_alias", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": true, + "in": "query", + "name": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/RuntimeWorkersQuery" + }, + "style": "form" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerListResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + }, + "post": { + "operationId": "runtime_worker_create_alias", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerSpawnRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerSpawnResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/runtimes/{runtime_id}/workers/{worker_id}": { + "get": { + "operationId": "runtime_worker_detail_alias", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerShowResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/runtimes/{runtime_id}/workers/{worker_id}/cancel": { + "post": { + "operationId": "runtime_worker_cancel_alias", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerLifecycleRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerLifecycleResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/runtimes/{runtime_id}/workers/{worker_id}/completions": { + "post": { + "operationId": "runtime_worker_completions_alias", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerCompletionsRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerCompletionsResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/runtimes/{runtime_id}/workers/{worker_id}/input": { + "post": { + "operationId": "runtime_worker_input_alias", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerInputRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerInputResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/runtimes/{runtime_id}/workers/{worker_id}/restore": { + "post": { + "operationId": "runtime_worker_restore_alias", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkerRestoreResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "504": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/runtimes/{runtime_id}/workers/{worker_id}/stop": { + "post": { + "operationId": "runtime_worker_stop_alias", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerLifecycleRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerLifecycleResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "504": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, "/api/tickets": { "get": { "operationId": "ticket_list_alias", @@ -16323,6 +19796,3339 @@ ] } }, + "/api/w/{workspace_id}/runtimes": { + "get": { + "operationId": "runtime_list", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkspaceRuntimeListResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + }, + "post": { + "operationId": "runtime_create", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRemoteRuntimeRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkspaceRuntimeResource" + } + } + }, + "description": "Alternate successful response" + }, + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkspaceRuntimeResource" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}": { + "delete": { + "operationId": "runtime_remove", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RemoveRuntimeRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeRemovalOperationResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + }, + "get": { + "operationId": "runtime_detail", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkspaceRuntimeDetail" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + }, + "post": { + "operationId": "runtime_update", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateRemoteRuntimeRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkspaceRuntimeDetail" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-executions": { + "post": { + "operationId": "runtime_cleanup_execute", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExecuteRuntimeCleanupRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeCleanupExecutionResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-plan": { + "get": { + "operationId": "runtime_cleanup_plan", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeCleanupPlanResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/config-bundles": { + "post": { + "operationId": "runtime_config_bundle_sync", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeConfigBundleSyncRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeConfigBundleResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/config-bundles/{bundle_id}/availability": { + "get": { + "operationId": "runtime_config_bundle_availability", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "bundle_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": true, + "in": "query", + "name": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/RuntimeConfigBundleAvailabilityQuery" + }, + "style": "form" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeConfigBundleResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/connection-tests": { + "post": { + "operationId": "runtime_connection_test", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeConnectionTestResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "504": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/trust-key": { + "delete": { + "operationId": "runtime_trust_key_revoke", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevokeRuntimeTrustKeyRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkspaceRuntimeDetail" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeManagementApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeManagementApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeManagementApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeManagementApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeManagementApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeManagementApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + }, + "get": { + "operationId": "runtime_trust_key_reveal", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeTrustKeyRevealResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers": { + "get": { + "operationId": "runtime_worker_list", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": true, + "in": "query", + "name": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/RuntimeWorkersQuery" + }, + "style": "form" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerListResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + }, + "post": { + "operationId": "runtime_worker_create", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerSpawnRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerSpawnResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}": { + "get": { + "operationId": "runtime_worker_detail", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerShowResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachment-upload-grants": { + "post": { + "operationId": "runtime_worker_attachment_upload_grant", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": true, + "in": "query", + "name": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/WorkerFileUploadQuery" + }, + "style": "form" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AttachmentUploadGrantResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachment-uploads/{upload_id}": { + "delete": { + "operationId": "runtime_worker_attachment_upload_cancel", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "upload_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AttachmentUploadCancelResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/attachments/{artifact_id}": { + "delete": { + "operationId": "runtime_worker_attachment_delete", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "artifact_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkerFileDeleteResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/cancel": { + "post": { + "operationId": "runtime_worker_cancel", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerLifecycleRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerLifecycleResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/completions": { + "post": { + "operationId": "runtime_worker_completions", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerCompletionsRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerCompletionsResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/input": { + "post": { + "operationId": "runtime_worker_input", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerInputRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerInputResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/pin": { + "delete": { + "operationId": "runtime_worker_unpin", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkerRetentionResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + }, + "put": { + "operationId": "runtime_worker_pin", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkerRetentionResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/restore": { + "post": { + "operationId": "runtime_worker_restore", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": true, + "in": "query", + "name": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/RestoreTicketAssignmentQuery" + }, + "style": "form" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkerRestoreResponse" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "504": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, + "/api/w/{workspace_id}/runtimes/{runtime_id}/workers/{worker_id}/stop": { + "post": { + "operationId": "runtime_worker_stop", + "parameters": [ + { + "explode": false, + "in": "path", + "name": "workspace_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "runtime_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + }, + { + "explode": false, + "in": "path", + "name": "worker_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/string" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerLifecycleRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeWorkerLifecycleResult" + } + } + }, + "description": "Successful response" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, + "504": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] + } + }, "/api/w/{workspace_id}/runtimes/{runtime_id}/working-directories": { "get": { "operationId": "runtime_workdir_list", diff --git a/web/workspace/src/lib/generated/legacy-server-api.ts b/web/workspace/src/lib/generated/legacy-server-api.ts index dd34705e3..2dd94fea6 100644 --- a/web/workspace/src/lib/generated/legacy-server-api.ts +++ b/web/workspace/src/lib/generated/legacy-server-api.ts @@ -359,207 +359,3 @@ export type RepositoryLogResponse = { items: Array; diagnostics: Array; }; - -export type RuntimeSourceKind = "embedded_worker_runtime" | "remote_http"; - -export type RuntimeSourceStatus = "active" | "reserved"; - -export type RuntimeIdentityAuthority = - | "runtime_registry_projection" - | "server_runtime_configuration"; - -export type RuntimeSourceSummary = { - kind: RuntimeSourceKind; - status: RuntimeSourceStatus; - identity_authority: RuntimeIdentityAuthority; - note: string; -}; - -export type RuntimeSummary = { - runtime_id: string; - label: string; - kind: string; - status: string; - source: RuntimeSourceSummary; - host_ids: Array; - worker_creation_available: boolean; - os: string; - arch: string; - diagnostics: Array; -}; - -export type WorkspaceRuntimeBindingState = - | "configured" - | "verified" - | "revoked"; - -export type RuntimeConnectionDisplayState = - | "configured" - | "verified" - | "unavailable" - | "revoked"; - -export type RuntimeVerificationOutcome = - | "verified" - | "challenge_issued" - | "verification_failed" - | "connectivity_failed"; - -export type RuntimeVerificationEvidenceSummary = { - verified_at: string | null; - last_checked_at: string; - last_outcome: RuntimeVerificationOutcome; - binding_revision: number; - workspace_key_id: string; - workspace_identity_revision: number; - workspace_trust_generation: number; - runtime_public_key_fingerprint: string; - runtime_identity_revision: number; -}; - -export type WorkspaceRuntimeBindingSummary = { - state: WorkspaceRuntimeBindingState; - connection_state: RuntimeConnectionDisplayState; - revision: number; - workspace_key_id?: string | null; - workspace_key_generation?: number | null; - verification?: RuntimeVerificationEvidenceSummary | null; -}; - -export type RuntimeManagementSummary = { - built_in: boolean; - config_managed: boolean; - removable: boolean; - endpoint_configured: boolean; - token_ref_configured: boolean; - binding?: WorkspaceRuntimeBindingSummary | null; -}; - -export type WorkspaceRuntimeResource = { - management: RuntimeManagementSummary; - runtime_id: string; - label: string; - kind: string; - status: string; - source: RuntimeSourceSummary; - host_ids: Array; - worker_creation_available: boolean; - os: string; - arch: string; - diagnostics: Array; -}; - -export type RuntimeTrustKeyStatus = "unconfigured" | "active" | "revoked"; - -export type RuntimeTrustKeyState = { - status: RuntimeTrustKeyStatus; - fingerprint?: string | null; - revision?: number | null; - created_at?: string | null; - updated_at?: string | null; - revoked_at?: string | null; -}; - -export type RuntimeTrustAuditAction = - | "created" - | "replaced" - | "reactivated" - | "revoked"; - -export type RuntimeTrustAuditEntry = { - action: RuntimeTrustAuditAction; - actor_account_id: string; - old_fingerprint?: string | null; - new_fingerprint?: string | null; - revision: number; - at: string; -}; - -export type WorkspaceRuntimeDetail = { - workspace_id: string; - runtime: WorkspaceRuntimeResource; - endpoint?: string | null; - trust_key: RuntimeTrustKeyState; - recent_audit: Array; -}; - -export type RuntimeTrustKeyRevealResponse = { public_key: string }; - -export type RevokeRuntimeTrustKeyRequest = { expected_revision: number }; - -export type RemoveRuntimeRequest = { - operation_id: string; - expected_binding_revision: number; -}; - -export type RuntimeRemovalOperationState = - | "pending" - | "cleanup_pending" - | "succeeded" - | "failed"; - -export type RuntimeRemovalOperationResponse = { - operation_id: string; - workspace_id: string; - runtime_id: string; - state: RuntimeRemovalOperationState; - binding_removed: boolean; - runtime_registration_removed: boolean | null; - failure_category?: string | null; - created_at: string; - updated_at: string; - completed_at?: string | null; -}; - -export type RuntimeTrustConflictKind = "stale_revision" | "fingerprint_in_use"; - -export type RuntimeTrustConflictResponse = { - error: RuntimeTrustConflictKind; - message: string; - current_revision?: number; - current_fingerprint?: string | null; -}; - -export type RuntimePublicIdentityBundle = { - identity_id: string; - public_key: string; -}; - -export type CreateRemoteRuntimeRequest = { - public_bundle: RuntimePublicIdentityBundle; - display_name?: string | null; - endpoint: string; - expected_revision?: number | null; -}; - -export type UpdateRemoteRuntimeRequest = { - display_name?: string | null; - endpoint: string; -}; - -export type RuntimeConnectionTestStatus = "compatible" | "failed"; - -export type RuntimeConnectionTestFailureKind = - | "authentication" - | "authorization" - | "network_unreachable" - | "timeout" - | "tls_or_transport" - | "malformed_response" - | "protocol_version_mismatch" - | "runtime_identity_mismatch" - | "configuration"; - -export type RuntimeConnectionTestResponse = { - workspace_id: string; - runtime_id: string; - binding_revision: number; - connection_state: RuntimeConnectionDisplayState; - verification: RuntimeVerificationEvidenceSummary | null; - checked_at: string; - status: RuntimeConnectionTestStatus; - failure_kind: RuntimeConnectionTestFailureKind | null; - expected_protocol_version: number; - actual_protocol_version: number | null; - diagnostics: Array; -}; diff --git a/web/workspace/src/lib/generated/repository-api.ts b/web/workspace/src/lib/generated/repository-api.ts index 2da845e3a..b9d58d0e4 100644 --- a/web/workspace/src/lib/generated/repository-api.ts +++ b/web/workspace/src/lib/generated/repository-api.ts @@ -1,6 +1,6 @@ // @generated by yoi-repository-openapi-typescript@1. Do not edit. // Input: openapi/server-api.json -// Input contract: 0.1.0 (sha256:e3f776a17079ce202725bb97de607426f4bfad9afe390ba7bbf76186e871be59) +// Input contract: 0.1.0 (sha256:67e56d1ffd3b27bc8653ef791bd68b5150ac7d507ccbe4e65b2e00320834d718) // Options: repository-operation-schema-closure,openapi-3.1,format:uint64=safe-number,strict-safe-integers,sorted-output // Output: web/workspace/src/lib/generated/repository-api.ts // Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check diff --git a/web/workspace/src/lib/generated/runtime-api.ts b/web/workspace/src/lib/generated/runtime-api.ts new file mode 100644 index 000000000..bdc01ad91 --- /dev/null +++ b/web/workspace/src/lib/generated/runtime-api.ts @@ -0,0 +1,172 @@ +// @generated by yoi-runtime-openapi-typescript@1. Do not edit. +// Input: openapi/server-api.json +// Input contract: 0.1.0 (sha256:67e56d1ffd3b27bc8653ef791bd68b5150ac7d507ccbe4e65b2e00320834d718) +// Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output +// Output: web/workspace/src/lib/generated/runtime-api.ts +// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check + +export type AttachmentUploadCancelResponse = { cancelled: boolean; }; + +export type AttachmentUploadGrantResponse = { expires_at_ms: number; upload_id: string; }; + +export type CleanupTargetKind = "worker_delete" | "workdir_clean_cleanup" | "workdir_dirty_discard" | "workdir_record_delete"; + +export type CleanupWorkdirCandidate = { action: CleanupTargetKind; blocking_reason?: string | null; cleanliness: CleanupWorkdirCleanliness; estimated_reclaim_bytes?: number | null; file_status: CleanupWorkdirFileStatus; linked_running_worker_ids: Array; linked_worker_ids: Array; pinned_linked: boolean; reason: string; repository_key: string; running_linked: boolean; runtime_id: string; target_id: string; workdir_id: string; }; + +export type CleanupWorkdirCleanliness = "clean" | "dirty" | "unknown"; + +export type CleanupWorkdirFileStatus = "pending" | "present" | "active" | "cleanup_pending" | "not_found" | "corrupted" | "failed" | "unknown"; + +export type CleanupWorkerCandidate = { action: CleanupTargetKind; blocking_reason?: string | null; estimated_reclaim_bytes?: number | null; linked_workdir_ids: Array; pinned: boolean; reason: string; retention_state: string; running_linked: boolean; runtime_id: string; runtime_worker_id: string; target_id: string; worker_id: string; }; + +export type CreateRemoteRuntimeRequest = { display_name?: string | null; endpoint: string; expected_revision?: number | null; public_bundle: RuntimePublicIdentityBundle; }; + +export type Diagnostic = { code: string; message: string; severity: DiagnosticSeverity; }; + +export type DiagnosticSeverity = "info" | "warning" | "error"; + +export type ExecuteRuntimeCleanupRequest = { confirm_dirty_discard_target_ids?: Array; expected_plan_digest: string; expected_plan_revision: string; workdir_target_ids?: Array; worker_target_ids?: Array; }; + +export type RemoveRuntimeRequest = { expected_binding_revision: number; operation_id: string; }; + +export type RepositoryApiError = { diagnostics?: Array; error: string; message: string; }; + +export type RevokeRuntimeTrustKeyRequest = { expected_revision: number; }; + +export type RuntimeCleanupExecutionResponse = { diagnostics: Array; executed_at: string; plan_after: RuntimeCleanupPlanResponse; results: Array; runtime_id: string; workspace_id: string; }; + +export type RuntimeCleanupExecutionResult = { action: CleanupTargetKind; message: string; status: string; target_id: string; }; + +export type RuntimeCleanupPlanResponse = { diagnostics: Array; digest: string; generated_at: string; revision: string; runtime_id: string; workdirs: Array; workers: Array; workspace_id: string; }; + +export type RuntimeConfigBundleResult = { availability?: unknown; diagnostics?: Array; state: WorkerOperationState; }; + +export type RuntimeConfigBundleSyncRequest = { bundle: unknown; }; + +export type RuntimeConnectionDisplayState = "configured" | "verified" | "unavailable" | "revoked"; + +export type RuntimeConnectionTestFailureKind = "authentication" | "authorization" | "network_unreachable" | "timeout" | "tls_or_transport" | "malformed_response" | "protocol_version_mismatch" | "runtime_identity_mismatch" | "configuration"; + +export type RuntimeConnectionTestResponse = { actual_protocol_version?: number | null; binding_revision: number; checked_at: string; connection_state: RuntimeConnectionDisplayState; diagnostics?: Array; expected_protocol_version: number; failure_kind?: RuntimeConnectionTestFailureKind | null; runtime_id: string; status: RuntimeConnectionTestStatus; verification?: RuntimeVerificationEvidenceSummary | null; workspace_id: string; }; + +export type RuntimeConnectionTestStatus = "compatible" | "failed"; + +export type RuntimeIdentityAuthority = "runtime_registry_projection" | "server_runtime_configuration"; + +export type RuntimeListResponse = { diagnostics?: Array; items: Array; limit: number; source: string; workspace_id: string; }; + +export type RuntimeManagementApiError = { current_fingerprint?: string | null; current_revision?: number | null; diagnostics?: Array; error: string; message: string; }; + +export type RuntimeManagementSummary = { binding?: WorkspaceRuntimeBindingSummary | null; built_in: boolean; config_managed: boolean; endpoint_configured: boolean; removable: boolean; token_ref_configured: boolean; }; + +export type RuntimeProfileSelector = { kind: "builtin"; value: string; } | { kind: "named"; value: string; }; + +export type RuntimePublicIdentityBundle = { identity_id: string; public_key: string; }; + +export type RuntimeRemovalOperationResponse = { binding_removed: boolean; completed_at?: string | null; created_at: string; failure_category?: string | null; operation_id: string; runtime_id: string; runtime_registration_removed?: boolean | null; state: RuntimeRemovalOperationState; updated_at: string; workspace_id: string; }; + +export type RuntimeRemovalOperationState = "pending" | "cleanup_pending" | "succeeded" | "failed"; + +export type RuntimeSourceKind = "embedded_worker_runtime" | "remote_http"; + +export type RuntimeSourceStatus = "active" | "reserved"; + +export type RuntimeSourceSummary = { identity_authority: RuntimeIdentityAuthority; kind: RuntimeSourceKind; note: string; status: RuntimeSourceStatus; }; + +export type RuntimeSummary = { arch: string; diagnostics?: Array; host_ids?: Array; kind: string; label: string; os: string; runtime_id: string; source: RuntimeSourceSummary; status: string; worker_creation_available: boolean; }; + +export type RuntimeTicketWorkerRole = "intake" | "orchestrator" | "coder" | "reviewer"; + +export type RuntimeTrustAuditAction = "created" | "replaced" | "reactivated" | "revoked"; + +export type RuntimeTrustAuditEntry = { action: RuntimeTrustAuditAction; actor_account_id: string; at: string; new_fingerprint?: string | null; old_fingerprint?: string | null; revision: number; }; + +export type RuntimeTrustKeyRevealResponse = { public_key: string; }; + +export type RuntimeTrustKeyState = { created_at?: string | null; fingerprint?: string | null; revision?: number | null; revoked_at?: string | null; status: RuntimeTrustKeyStatus; updated_at?: string | null; }; + +export type RuntimeTrustKeyStatus = "unconfigured" | "active" | "revoked"; + +export type RuntimeVerificationEvidenceSummary = { binding_revision: number; last_checked_at: string; last_outcome: RuntimeVerificationOutcome; runtime_identity_revision: number; runtime_public_key_fingerprint: string; verified_at?: string | null; workspace_identity_revision: number; workspace_key_id: string; workspace_trust_generation: number; }; + +export type RuntimeVerificationOutcome = "verified" | "challenge_issued" | "verification_failed" | "connectivity_failed"; + +export type RuntimeWorkerCompletionsRequest = { kind: unknown; prefix?: string; }; + +export type RuntimeWorkerCompletionsResult = { diagnostics?: Array; entries: Array; kind: unknown; prefix: string; runtime_id: string; worker_id: string; }; + +export type RuntimeWorkerInputRequest = { content: string; kind?: string | null; segments?: Array | null; }; + +export type RuntimeWorkerInputResult = { diagnostics?: Array; runtime_id: string; state: WorkerOperationState; worker_id: string; }; + +export type RuntimeWorkerLifecycleRequest = { reason?: string | null; ticket_assignment?: RuntimeWorkerTicketAssignmentRequest | null; }; + +export type RuntimeWorkerLifecycleResult = { diagnostics?: Array; runtime_id: string; state: WorkerOperationState; worker_id: string; }; + +export type RuntimeWorkerListResponse = { diagnostics?: Array; items: Array; limit: number; source: string; workspace_id: string; }; + +export type RuntimeWorkerShowResponse = { capabilities: WorkerCapabilitySummary; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; resource_key: string; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; updated_at: string; workdir_attachments?: Array; worker_id: string; worker_state?: unknown; workspace: WorkerWorkspaceSummary; }; + +export type RuntimeWorkerSpawnAcceptanceEvidence = { detail: string; kind: string; }; + +export type RuntimeWorkerSpawnAcceptanceRequirement = "socket_ready" | { run_accepted: { expected_segments: number; }; }; + +export type RuntimeWorkerSpawnIntent = { kind: "workspace_companion"; } | { kind: "workspace_orchestrator"; } | { kind: "workspace_coding"; } | { kind: "ticket_role"; role: RuntimeTicketWorkerRole; ticket_id: string; }; + +export type RuntimeWorkerSpawnRequest = { acceptance: RuntimeWorkerSpawnAcceptanceRequirement; initial_submit?: Array; intent: RuntimeWorkerSpawnIntent; profile: RuntimeProfileSelector; requested_worker_name?: string | null; ticket_assignment?: RuntimeWorkerTicketAssignmentRequest | null; workdir_attachment_requests?: Array; }; + +export type RuntimeWorkerSpawnResponse = { acceptance_evidence?: Array; diagnostics?: Array; state: WorkerOperationState; worker?: RuntimeWorkerSummary | null; }; + +export type RuntimeWorkerSpawnWorkingDirectoryAttachmentRequest = { alias: string; working_directory: RuntimeWorkerSpawnWorkingDirectoryRequest; }; + +export type RuntimeWorkerSpawnWorkingDirectoryRequest = { repository_key: string; selector?: string | null; }; + +export type RuntimeWorkerSummary = { capabilities: WorkerCapabilitySummary; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; workdir_attachments?: Array; worker_id: string; worker_state?: unknown; workspace: WorkerWorkspaceSummary; }; + +export type RuntimeWorkerTicketAssignmentRequest = { operation_id: string; ticket_id: string; }; + +export type UpdateRemoteRuntimeRequest = { display_name?: string | null; endpoint: string; }; + +export type WorkerCapabilitySummary = { can_spawn_followup: boolean; can_stop: boolean; }; + +export type WorkerFileDeleteResponse = { deleted: boolean; }; + +export type WorkerImplementationSummary = { display_hint: string; kind: string; }; + +export type WorkerOperationState = "accepted" | "unsupported" | "rejected"; + +export type WorkerRestoreResponse = { result: WorkerRestoreResult; runtime_id: string; worker_id: string; workspace_id: string; }; + +export type WorkerRestoreResult = { diagnostics?: Array; state: WorkerRestoreState; worker?: WorkerSummary | null; }; + +export type WorkerRestoreState = "accepted" | "rejected" | "rolled_back" | "reconciliation_required"; + +export type WorkerRetentionResponse = { pinned: boolean; retention_state: string; runtime_id: string; worker_id: string; workspace_id: string; }; + +export type WorkerSummary = { capabilities: WorkerCapabilitySummary; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; resource_key: string; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; workdir_attachments?: Array; worker_id: string; worker_state?: unknown; workspace: WorkerWorkspaceSummary; }; + +export type WorkerWorkdirAttachmentSummary = { alias: string; working_directory: WorkingDirectorySummary; }; + +export type WorkerWorkspaceSummary = { identity: string; visibility: string; workspace_id?: string | null; }; + +export type WorkingDirectoryCleanupTarget = { kind: string; repository_key: string; working_directory_id: string; }; + +export type WorkingDirectoryMaterializerKind = "runtime_git_clone" | "client_hosted_external"; + +export type WorkingDirectoryOccupancy = { display_name: string; linked_at: string; runtime_id: string; worker_id: string; }; + +export type WorkingDirectorySource = { kind: "repository"; repository_key: string; } | { grant_id: string; kind: "external_grant"; }; + +export type WorkingDirectoryStatusKind = "active" | "cleanup_pending" | "corrupted" | "not_found" | "unknown"; + +export type WorkingDirectorySummary = { cleanliness?: string | null; cleanup_target?: WorkingDirectoryCleanupTarget | null; creation_ref?: string | null; creation_selector?: string | null; creation_tree?: string | null; current_ref?: string | null; current_selector?: string | null; current_tree?: string | null; display_name?: string | null; materializer_kind: WorkingDirectoryMaterializerKind; observed_at_epoch_seconds?: number | null; occupied_by?: WorkingDirectoryOccupancy | null; source: WorkingDirectorySource; status: WorkingDirectoryStatusKind; working_directory_id: string; }; + +export type WorkspaceRuntimeBindingState = "configured" | "verified" | "revoked"; + +export type WorkspaceRuntimeBindingSummary = { connection_state: RuntimeConnectionDisplayState; revision: number; state: WorkspaceRuntimeBindingState; verification?: RuntimeVerificationEvidenceSummary | null; workspace_key_generation?: number | null; workspace_key_id?: string | null; }; + +export type WorkspaceRuntimeDetail = { endpoint?: string | null; recent_audit?: Array; runtime: WorkspaceRuntimeResource; trust_key: RuntimeTrustKeyState; workspace_id: string; }; + +export type WorkspaceRuntimeListResponse = { diagnostics?: Array; items: Array; limit: number; source: string; workspace_id: string; }; + +export type WorkspaceRuntimeResource = { arch: string; diagnostics?: Array; host_ids?: Array; kind: string; label: string; management: RuntimeManagementSummary; os: string; runtime_id: string; source: RuntimeSourceSummary; status: string; worker_creation_available: boolean; }; diff --git a/web/workspace/src/lib/generated/workdir-api.ts b/web/workspace/src/lib/generated/workdir-api.ts index c9b61fc1c..135e935f6 100644 --- a/web/workspace/src/lib/generated/workdir-api.ts +++ b/web/workspace/src/lib/generated/workdir-api.ts @@ -1,6 +1,6 @@ // @generated by yoi-workdir-openapi-typescript@1. Do not edit. // Input: openapi/server-api.json -// Input contract: 0.1.0 (sha256:e3f776a17079ce202725bb97de607426f4bfad9afe390ba7bbf76186e871be59) +// Input contract: 0.1.0 (sha256:67e56d1ffd3b27bc8653ef791bd68b5150ac7d507ccbe4e65b2e00320834d718) // Options: workdir-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output // Output: web/workspace/src/lib/generated/workdir-api.ts // Regenerate/check: cargo run -q -p server-api --example generate_workdir_api_types -- --check diff --git a/web/workspace/src/lib/workspace/api/runtime-connection.ts b/web/workspace/src/lib/workspace/api/runtime-connection.ts index ada1ff6aa..7f308e685 100644 --- a/web/workspace/src/lib/workspace/api/runtime-connection.ts +++ b/web/workspace/src/lib/workspace/api/runtime-connection.ts @@ -3,7 +3,7 @@ import type { RuntimeConnectionTestFailureKind, RuntimeConnectionTestResponse, RuntimeVerificationEvidenceSummary, -} from "$lib/generated/legacy-server-api"; +} from "$lib/generated/runtime-api"; const RESPONSE_KEYS = [ "workspace_id", diff --git a/web/workspace/src/lib/workspace/api/runtime-management.ts b/web/workspace/src/lib/workspace/api/runtime-management.ts index 92cd1ba0a..b16355ed0 100644 --- a/web/workspace/src/lib/workspace/api/runtime-management.ts +++ b/web/workspace/src/lib/workspace/api/runtime-management.ts @@ -5,6 +5,7 @@ import type { RevokeRuntimeTrustKeyRequest, RuntimeConnectionDisplayState, RuntimeIdentityAuthority, + RuntimeManagementApiError, RuntimeManagementSummary, RuntimeRemovalOperationResponse, RuntimeRemovalOperationState, @@ -13,8 +14,6 @@ import type { RuntimeSourceSummary, RuntimeTrustAuditAction, RuntimeTrustAuditEntry, - RuntimeTrustConflictKind, - RuntimeTrustConflictResponse, RuntimeTrustKeyRevealResponse, RuntimeTrustKeyState, RuntimeTrustKeyStatus, @@ -23,12 +22,24 @@ import type { WorkspaceRuntimeBindingState, WorkspaceRuntimeBindingSummary, WorkspaceRuntimeDetail, + WorkspaceRuntimeListResponse, WorkspaceRuntimeResource, -} from "$lib/generated/legacy-server-api.ts"; -import type { ListResponse } from "$lib/workspace/sidebar/types"; +} from "$lib/generated/runtime-api.ts"; import { workspaceApiPath } from "./http.ts"; -export type WorkspaceRuntimeList = ListResponse; +export type WorkspaceRuntimeList = Omit & { + diagnostics: Diagnostic[]; +}; +export type ValidatedWorkspaceRuntimeDetail = Omit< + WorkspaceRuntimeDetail, + "recent_audit" +> & { + recent_audit: RuntimeTrustAuditEntry[]; +}; +export type RuntimeTrustConflictKind = "stale_revision" | "fingerprint_in_use"; +export type RuntimeTrustConflictResponse = RuntimeManagementApiError & { + error: RuntimeTrustConflictKind; +}; const LIMITS = { runtimeItems: 200, @@ -690,7 +701,7 @@ export function parseWorkspaceRuntimeList( export function parseWorkspaceRuntimeDetail( value: unknown, -): WorkspaceRuntimeDetail { +): ValidatedWorkspaceRuntimeDetail { const response = object(value, "Runtime detail response"); exactKeys( response, diff --git a/web/workspace/src/lib/workspace/api/runtime-workers.ts b/web/workspace/src/lib/workspace/api/runtime-workers.ts new file mode 100644 index 000000000..18878ce66 --- /dev/null +++ b/web/workspace/src/lib/workspace/api/runtime-workers.ts @@ -0,0 +1,356 @@ +import type { + CleanupTargetKind, + CleanupWorkdirCandidate, + CleanupWorkdirCleanliness, + CleanupWorkdirFileStatus, + CleanupWorkerCandidate, + Diagnostic, + RuntimeCleanupExecutionResponse, + RuntimeCleanupExecutionResult, + RuntimeCleanupPlanResponse, + RuntimeWorkerLifecycleResult, + WorkerOperationState, + WorkerRetentionResponse, +} from "$lib/generated/runtime-api"; + +const MAX_STRING_BYTES = 4_096; +const MAX_CANDIDATES = 1_000; +const MAX_RESULTS = 2_000; +const MAX_DIAGNOSTICS = 64; +const encoder = new TextEncoder(); +type JsonObject = Record; + +const TARGET_KINDS = new Set([ + "worker_delete", + "workdir_clean_cleanup", + "workdir_dirty_discard", + "workdir_record_delete", +]); +const FILE_STATUSES = new Set([ + "pending", + "present", + "active", + "cleanup_pending", + "not_found", + "corrupted", + "failed", + "unknown", +]); +const CLEANLINESS = new Set([ + "clean", + "dirty", + "unknown", +]); + +export class RuntimeWorkerValidationError extends Error { + constructor(message: string) { + super(message.slice(0, 256)); + this.name = "RuntimeWorkerValidationError"; + } +} + +function fail(path: string, message: string): never { + throw new RuntimeWorkerValidationError(`${path} ${message}`); +} + +function object(value: unknown, path: string): JsonObject { + if (typeof value !== "object" || value === null || Array.isArray(value)) { + return fail(path, "must be an object"); + } + return value as JsonObject; +} + +function exactKeys( + value: JsonObject, + required: readonly string[], + optional: readonly string[], + path: string, +): void { + const allowed = new Set([...required, ...optional]); + for (const key of Object.keys(value)) { + if (!allowed.has(key)) fail(`${path}.${key}`, "is not part of the wire contract"); + } + for (const key of required) { + if (!Object.hasOwn(value, key)) fail(`${path}.${key}`, "is required"); + } +} + +function boundedString(value: unknown, path: string, allowEmpty = false): string { + if (typeof value !== "string") return fail(path, "must be a string"); + if (!allowEmpty && value.length === 0) return fail(path, "must not be empty"); + if (encoder.encode(value).byteLength > MAX_STRING_BYTES) { + return fail(path, `must be at most ${MAX_STRING_BYTES} UTF-8 bytes`); + } + return value; +} + +function boolean(value: unknown, path: string): boolean { + if (typeof value !== "boolean") return fail(path, "must be a boolean"); + return value; +} + +function array(value: unknown, path: string, max: number): unknown[] { + if (!Array.isArray(value)) return fail(path, "must be an array"); + if (value.length > max) return fail(path, `must contain at most ${max} items`); + return value; +} + +function strings(value: unknown, path: string): string[] { + return array(value, path, MAX_CANDIDATES).map((entry, index) => + boundedString(entry, `${path}[${index}]`) + ); +} + +function optionalNullableString(value: unknown, path: string): string | null | undefined { + if (value === undefined || value === null) return value; + return boundedString(value, path); +} + +function optionalNullableSafeInteger(value: unknown, path: string): number | null | undefined { + if (value === undefined || value === null) return value; + if (!Number.isSafeInteger(value) || (value as number) < 0) { + return fail(path, "must be a non-negative safe integer"); + } + return value as number; +} + +function targetKind(value: unknown, path: string): CleanupTargetKind { + if (typeof value !== "string" || !TARGET_KINDS.has(value as CleanupTargetKind)) { + return fail(path, "has an unknown cleanup action"); + } + return value as CleanupTargetKind; +} + +function diagnostic(value: unknown, path: string): Diagnostic { + const item = object(value, path); + exactKeys(item, ["code", "severity", "message"], [], path); + const severity = boundedString(item.severity, `${path}.severity`); + if (severity !== "info" && severity !== "warning" && severity !== "error") { + fail(`${path}.severity`, "has an unknown value"); + } + return { + code: boundedString(item.code, `${path}.code`), + severity, + message: boundedString(item.message, `${path}.message`, true), + }; +} + +function diagnostics(value: unknown, path: string): Diagnostic[] { + return array(value, path, MAX_DIAGNOSTICS).map((entry, index) => + diagnostic(entry, `${path}[${index}]`) + ); +} + +function workerCandidate(value: unknown, path: string): CleanupWorkerCandidate { + const item = object(value, path); + exactKeys( + item, + [ + "target_id", + "action", + "worker_id", + "runtime_worker_id", + "runtime_id", + "reason", + "pinned", + "retention_state", + "linked_workdir_ids", + "running_linked", + ], + ["blocking_reason", "estimated_reclaim_bytes"], + path, + ); + return { + target_id: boundedString(item.target_id, `${path}.target_id`), + action: targetKind(item.action, `${path}.action`), + worker_id: boundedString(item.worker_id, `${path}.worker_id`), + runtime_worker_id: boundedString(item.runtime_worker_id, `${path}.runtime_worker_id`), + runtime_id: boundedString(item.runtime_id, `${path}.runtime_id`), + reason: boundedString(item.reason, `${path}.reason`, true), + blocking_reason: optionalNullableString(item.blocking_reason, `${path}.blocking_reason`), + pinned: boolean(item.pinned, `${path}.pinned`), + retention_state: boundedString(item.retention_state, `${path}.retention_state`), + linked_workdir_ids: strings(item.linked_workdir_ids, `${path}.linked_workdir_ids`), + running_linked: boolean(item.running_linked, `${path}.running_linked`), + estimated_reclaim_bytes: optionalNullableSafeInteger( + item.estimated_reclaim_bytes, + `${path}.estimated_reclaim_bytes`, + ), + }; +} + +function workdirCandidate(value: unknown, path: string): CleanupWorkdirCandidate { + const item = object(value, path); + exactKeys( + item, + [ + "target_id", + "action", + "workdir_id", + "runtime_id", + "repository_key", + "reason", + "linked_worker_ids", + "linked_running_worker_ids", + "running_linked", + "pinned_linked", + "file_status", + "cleanliness", + ], + ["blocking_reason", "estimated_reclaim_bytes"], + path, + ); + const fileStatus = boundedString(item.file_status, `${path}.file_status`); + const cleanliness = boundedString(item.cleanliness, `${path}.cleanliness`); + if (!FILE_STATUSES.has(fileStatus as CleanupWorkdirFileStatus)) { + fail(`${path}.file_status`, "has an unknown value"); + } + if (!CLEANLINESS.has(cleanliness as CleanupWorkdirCleanliness)) { + fail(`${path}.cleanliness`, "has an unknown value"); + } + return { + target_id: boundedString(item.target_id, `${path}.target_id`), + action: targetKind(item.action, `${path}.action`), + workdir_id: boundedString(item.workdir_id, `${path}.workdir_id`), + runtime_id: boundedString(item.runtime_id, `${path}.runtime_id`), + repository_key: boundedString(item.repository_key, `${path}.repository_key`), + reason: boundedString(item.reason, `${path}.reason`, true), + blocking_reason: optionalNullableString(item.blocking_reason, `${path}.blocking_reason`), + linked_worker_ids: strings(item.linked_worker_ids, `${path}.linked_worker_ids`), + linked_running_worker_ids: strings( + item.linked_running_worker_ids, + `${path}.linked_running_worker_ids`, + ), + running_linked: boolean(item.running_linked, `${path}.running_linked`), + pinned_linked: boolean(item.pinned_linked, `${path}.pinned_linked`), + file_status: fileStatus as CleanupWorkdirFileStatus, + cleanliness: cleanliness as CleanupWorkdirCleanliness, + estimated_reclaim_bytes: optionalNullableSafeInteger( + item.estimated_reclaim_bytes, + `${path}.estimated_reclaim_bytes`, + ), + }; +} + +export function parseWorkerRetentionResponse(value: unknown): WorkerRetentionResponse { + const item = object(value, "Worker retention response"); + exactKeys( + item, + ["workspace_id", "runtime_id", "worker_id", "pinned", "retention_state"], + [], + "Worker retention response", + ); + return { + workspace_id: boundedString(item.workspace_id, "Worker retention response.workspace_id"), + runtime_id: boundedString(item.runtime_id, "Worker retention response.runtime_id"), + worker_id: boundedString(item.worker_id, "Worker retention response.worker_id"), + pinned: boolean(item.pinned, "Worker retention response.pinned"), + retention_state: boundedString( + item.retention_state, + "Worker retention response.retention_state", + ), + }; +} + +const WORKER_OPERATION_STATES = new Set([ + "accepted", + "unsupported", + "rejected", +]); + +export function parseRuntimeWorkerLifecycleResult( + value: unknown, +): RuntimeWorkerLifecycleResult { + const item = object(value, "Runtime Worker lifecycle result"); + exactKeys( + item, + ["state", "runtime_id", "worker_id", "diagnostics"], + [], + "Runtime Worker lifecycle result", + ); + const state = boundedString(item.state, "Runtime Worker lifecycle result.state"); + if (!WORKER_OPERATION_STATES.has(state as WorkerOperationState)) { + fail("Runtime Worker lifecycle result.state", "has an unknown value"); + } + return { + state: state as WorkerOperationState, + runtime_id: boundedString( + item.runtime_id, + "Runtime Worker lifecycle result.runtime_id", + ), + worker_id: boundedString( + item.worker_id, + "Runtime Worker lifecycle result.worker_id", + ), + diagnostics: diagnostics( + item.diagnostics, + "Runtime Worker lifecycle result.diagnostics", + ), + }; +} + +export function parseRuntimeCleanupPlan(value: unknown): RuntimeCleanupPlanResponse { + const item = object(value, "Runtime cleanup plan"); + exactKeys( + item, + [ + "workspace_id", + "runtime_id", + "generated_at", + "revision", + "digest", + "workers", + "workdirs", + "diagnostics", + ], + [], + "Runtime cleanup plan", + ); + return { + workspace_id: boundedString(item.workspace_id, "Runtime cleanup plan.workspace_id"), + runtime_id: boundedString(item.runtime_id, "Runtime cleanup plan.runtime_id"), + generated_at: boundedString(item.generated_at, "Runtime cleanup plan.generated_at"), + revision: boundedString(item.revision, "Runtime cleanup plan.revision"), + digest: boundedString(item.digest, "Runtime cleanup plan.digest"), + workers: array(item.workers, "Runtime cleanup plan.workers", MAX_CANDIDATES).map( + (entry, index) => workerCandidate(entry, `Runtime cleanup plan.workers[${index}]`), + ), + workdirs: array(item.workdirs, "Runtime cleanup plan.workdirs", MAX_CANDIDATES).map( + (entry, index) => workdirCandidate(entry, `Runtime cleanup plan.workdirs[${index}]`), + ), + diagnostics: diagnostics(item.diagnostics, "Runtime cleanup plan.diagnostics"), + }; +} + +function executionResult(value: unknown, path: string): RuntimeCleanupExecutionResult { + const item = object(value, path); + exactKeys(item, ["target_id", "action", "status", "message"], [], path); + return { + target_id: boundedString(item.target_id, `${path}.target_id`), + action: targetKind(item.action, `${path}.action`), + status: boundedString(item.status, `${path}.status`), + message: boundedString(item.message, `${path}.message`, true), + }; +} + +export function parseRuntimeCleanupExecution( + value: unknown, +): RuntimeCleanupExecutionResponse { + const item = object(value, "Runtime cleanup execution"); + exactKeys( + item, + ["workspace_id", "runtime_id", "executed_at", "results", "plan_after", "diagnostics"], + [], + "Runtime cleanup execution", + ); + return { + workspace_id: boundedString(item.workspace_id, "Runtime cleanup execution.workspace_id"), + runtime_id: boundedString(item.runtime_id, "Runtime cleanup execution.runtime_id"), + executed_at: boundedString(item.executed_at, "Runtime cleanup execution.executed_at"), + results: array(item.results, "Runtime cleanup execution.results", MAX_RESULTS).map( + (entry, index) => executionResult(entry, `Runtime cleanup execution.results[${index}]`), + ), + plan_after: parseRuntimeCleanupPlan(item.plan_after), + diagnostics: diagnostics(item.diagnostics, "Runtime cleanup execution.diagnostics"), + }; +} diff --git a/web/workspace/src/lib/workspace/console/composer-attachments.ts b/web/workspace/src/lib/workspace/console/composer-attachments.ts index 45d179a9a..15737998c 100644 --- a/web/workspace/src/lib/workspace/console/composer-attachments.ts +++ b/web/workspace/src/lib/workspace/console/composer-attachments.ts @@ -1,3 +1,4 @@ +import type { AttachmentUploadGrantResponse } from "$lib/generated/runtime-api.ts"; import type { UploadedFileRef } from "$lib/generated/protocol.ts"; export const MAX_UPLOADED_FILE_BYTES = 10 * 1024 * 1024; @@ -133,10 +134,19 @@ export function uploadAttachment( function isUploadGrantResponse( value: unknown, -): value is { upload_id: string; expires_at_ms: number } { - return !!value && typeof value === "object" && - "upload_id" in value && typeof value.upload_id === "string" && - "expires_at_ms" in value && typeof value.expires_at_ms === "number"; +): value is AttachmentUploadGrantResponse { + if (!value || typeof value !== "object" || Array.isArray(value)) return false; + const keys = Object.keys(value); + if ( + keys.length !== 2 || !keys.includes("upload_id") || + !keys.includes("expires_at_ms") + ) { + return false; + } + const grant = value as Record; + return typeof grant.upload_id === "string" && grant.upload_id.length > 0 && + grant.upload_id.length <= 256 && Number.isSafeInteger(grant.expires_at_ms) && + (grant.expires_at_ms as number) >= 0; } function isUploadedFileResponse( diff --git a/web/workspace/src/lib/workspace/repositories/ssh-connection.ts b/web/workspace/src/lib/workspace/repositories/ssh-connection.ts index 7b01e33f0..67e29f0e8 100644 --- a/web/workspace/src/lib/workspace/repositories/ssh-connection.ts +++ b/web/workspace/src/lib/workspace/repositories/ssh-connection.ts @@ -1,4 +1,4 @@ -import type { WorkspaceRuntimeResource } from "$lib/generated/legacy-server-api"; +import type { WorkspaceRuntimeResource } from "$lib/generated/runtime-api"; export type RepositorySshProbeSelection = Readonly<{ changed: boolean; diff --git a/web/workspace/src/lib/workspace/sidebar/types.ts b/web/workspace/src/lib/workspace/sidebar/types.ts index bf33a3742..db44802f9 100644 --- a/web/workspace/src/lib/workspace/sidebar/types.ts +++ b/web/workspace/src/lib/workspace/sidebar/types.ts @@ -1,3 +1,10 @@ +import type { + CleanupTargetKind, + CleanupWorkdirCandidate, + CleanupWorkerCandidate, + RuntimeCleanupExecutionResponse, + RuntimeCleanupPlanResponse, +} from "$lib/generated/runtime-api"; import type { BrowserCreateWorkerResponse as SharedBrowserCreateWorkerResponse, BrowserWorkerWorkingDirectorySelection @@ -123,67 +130,12 @@ export type WorkerLaunchProfileCandidate = SharedWorkerLaunchProfileCandidate; export type WorkingDirectoryRepositoryOption = SharedWorkingDirectoryRepositoryOption; -export type CleanupTargetKind = - | "worker_delete" - | "workdir_clean_cleanup" - | "workdir_dirty_discard" - | "workdir_record_delete"; - -export type CleanupWorkerCandidate = { - target_id: string; - action: CleanupTargetKind; - worker_id: string; - runtime_worker_id: string; - runtime_id: string; - reason: string; - blocking_reason?: string | null; - pinned: boolean; - retention_state: string; - linked_workdir_ids: string[]; - running_linked: boolean; - estimated_reclaim_bytes?: number | null; -}; - -export type CleanupWorkdirCandidate = { - target_id: string; - action: CleanupTargetKind; - workdir_id: string; - runtime_id: string; - repository_key: string; - reason: string; - blocking_reason?: string | null; - linked_worker_ids: string[]; - linked_running_worker_ids: string[]; - running_linked: boolean; - pinned_linked: boolean; - file_status: string; - cleanliness: string; - estimated_reclaim_bytes?: number | null; -}; - -export type RuntimeCleanupPlanResponse = { - workspace_id: string; - runtime_id: string; - generated_at: string; - revision: string; - digest: string; - workers: CleanupWorkerCandidate[]; - workdirs: CleanupWorkdirCandidate[]; - diagnostics: Diagnostic[]; -}; - -export type RuntimeCleanupExecutionResponse = { - workspace_id: string; - runtime_id: string; - executed_at: string; - results: { - target_id: string; - action: CleanupTargetKind; - status: string; - message: string; - }[]; - plan_after: RuntimeCleanupPlanResponse; - diagnostics: Diagnostic[]; +export type { + CleanupTargetKind, + CleanupWorkdirCandidate, + CleanupWorkerCandidate, + RuntimeCleanupExecutionResponse, + RuntimeCleanupPlanResponse, }; export type BrowserWorkerWorkingDirectorySelection = diff --git a/web/workspace/src/lib/workspace/sidebar/worker-actions.ts b/web/workspace/src/lib/workspace/sidebar/worker-actions.ts index c76d41f6a..2ac7adc89 100644 --- a/web/workspace/src/lib/workspace/sidebar/worker-actions.ts +++ b/web/workspace/src/lib/workspace/sidebar/worker-actions.ts @@ -1,19 +1,17 @@ import { workspaceApiPath } from "$lib/workspace/api/http"; +import { + parseRuntimeCleanupExecution, + parseRuntimeCleanupPlan, + parseRuntimeWorkerLifecycleResult, +} from "$lib/workspace/api/runtime-workers"; import type { Diagnostic, - RuntimeCleanupExecutionResponse, - RuntimeCleanupPlanResponse, Worker, } from "./types"; type FetchFn = typeof fetch; type WorkerActionTarget = Pick; -type WorkerLifecycleResponse = { - state: string; - diagnostics?: Diagnostic[]; -}; - function workerPath(workspaceId: string, worker: WorkerActionTarget): string { return workspaceApiPath( workspaceId, @@ -62,7 +60,7 @@ export async function stopSidebarWorker( }); if (!response.ok) throw new Error(await responseError(response)); - const result = await response.json() as WorkerLifecycleResponse; + const result = parseRuntimeWorkerLifecycleResult(await response.json()); if (result.state !== "accepted") { throw new Error( diagnosticMessage(result.diagnostics, `Worker stop was ${result.state}`), @@ -81,7 +79,7 @@ export async function deleteSidebarWorker( ); if (!planResponse.ok) throw new Error(await responseError(planResponse)); - const plan = await planResponse.json() as RuntimeCleanupPlanResponse; + const plan = parseRuntimeCleanupPlan(await planResponse.json()); const candidate = plan.workers.find((item) => item.runtime_id === worker.runtime_id && item.runtime_worker_id === worker.worker_id @@ -107,8 +105,7 @@ export async function deleteSidebarWorker( throw new Error(await responseError(executionResponse)); } - const execution = await executionResponse - .json() as RuntimeCleanupExecutionResponse; + const execution = parseRuntimeCleanupExecution(await executionResponse.json()); const outcome = execution.results.find((result) => result.target_id === candidate.target_id ); diff --git a/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/+page.svelte b/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/+page.svelte index cfcd96a00..66114c0c1 100644 --- a/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/+page.svelte +++ b/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/+page.svelte @@ -4,7 +4,7 @@ RuntimeConnectionTestResponse, RuntimePublicIdentityBundle, WorkspaceRuntimeResource, - } from '$lib/generated/legacy-server-api'; + } from '$lib/generated/runtime-api'; import { createRemoteRuntime, previewRuntimePublicKeyFingerprint, @@ -397,12 +397,12 @@ {@const currentResult = currentTestResult(runtime)} - {#if runtime.diagnostics.length > 0 || currentResult} + {#if (runtime.diagnostics ?? []).length > 0 || currentResult} - {#if runtime.diagnostics.length > 0} + {#if (runtime.diagnostics ?? []).length > 0}
    - {#each runtime.diagnostics as diagnostic} + {#each runtime.diagnostics ?? [] as diagnostic}
  • {diagnostic.code} {diagnostic.message} @@ -413,8 +413,8 @@ {#if currentResult}
    Connection test: {connectionTestSummary(currentResult)} - {#if currentResult.diagnostics[0]} - {currentResult.diagnostics[0].message} + {#if (currentResult.diagnostics ?? [])[0]} + {(currentResult.diagnostics ?? [])[0].message} {/if} Checked {new Date(currentResult.checked_at).toLocaleString()}
    diff --git a/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/+page.svelte b/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/+page.svelte index f3472859a..6e900f9c5 100644 --- a/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/+page.svelte +++ b/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/+page.svelte @@ -3,7 +3,7 @@ import type { RevokeRuntimeTrustKeyRequest, RuntimeTrustKeyStatus, - } from '$lib/generated/legacy-server-api'; + } from '$lib/generated/runtime-api'; import { createRemoteRuntime, removeRemoteRuntime, @@ -414,9 +414,9 @@
    Updated
    {formatTimestamp(trust.updated_at)}
    Revoked
    {formatTimestamp(trust.revoked_at)}
    - {#if runtime.diagnostics.length > 0} + {#if (runtime.diagnostics ?? []).length > 0}
      - {#each runtime.diagnostics as diagnostic} + {#each runtime.diagnostics ?? [] as diagnostic}
    • {diagnostic.code} {diagnostic.message} diff --git a/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/workdirs/+page.svelte b/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/workdirs/+page.svelte index 74b732fda..d75721336 100644 --- a/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/workdirs/+page.svelte +++ b/web/workspace/src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/workdirs/+page.svelte @@ -1,10 +1,10 @@