refactor: remove worker capability summary flags
This commit is contained in:
@@ -5,7 +5,6 @@ pub use server_api::{
|
|||||||
CreateWorkspaceWorkerRequest as BackendCreateWorkerRequest, Diagnostic as BackendDiagnostic,
|
CreateWorkspaceWorkerRequest as BackendCreateWorkerRequest, Diagnostic as BackendDiagnostic,
|
||||||
DiagnosticSeverity as BackendDiagnosticSeverity, ListResponse as BackendRuntimeListResponse,
|
DiagnosticSeverity as BackendDiagnosticSeverity, ListResponse as BackendRuntimeListResponse,
|
||||||
RuntimeSummary as BackendRuntimeSummary,
|
RuntimeSummary as BackendRuntimeSummary,
|
||||||
WorkerCapabilitySummary as BackendWorkerCapabilitySummary,
|
|
||||||
WorkerImplementationSummary as BackendWorkerImplementationSummary,
|
WorkerImplementationSummary as BackendWorkerImplementationSummary,
|
||||||
WorkerLaunchOptionsResponse as BackendWorkerLaunchOptions,
|
WorkerLaunchOptionsResponse as BackendWorkerLaunchOptions,
|
||||||
WorkerLaunchProfileCandidate as BackendWorkerLaunchProfileCandidate,
|
WorkerLaunchProfileCandidate as BackendWorkerLaunchProfileCandidate,
|
||||||
@@ -869,7 +868,6 @@ mod tests {
|
|||||||
"pinned": false,
|
"pinned": false,
|
||||||
"retention_state": "resident",
|
"retention_state": "resident",
|
||||||
"implementation": {"kind": "embedded", "display_hint": "Embedded"},
|
"implementation": {"kind": "embedded", "display_hint": "Embedded"},
|
||||||
"capabilities": {"can_stop": true, "can_spawn_followup": false},
|
|
||||||
"diagnostics": []
|
"diagnostics": []
|
||||||
},
|
},
|
||||||
"diagnostics": []
|
"diagnostics": []
|
||||||
@@ -952,10 +950,11 @@ mod tests {
|
|||||||
"label": "Coder",
|
"label": "Coder",
|
||||||
"workspace": {"visibility": "workspace", "identity": "workspace"},
|
"workspace": {"visibility": "workspace", "identity": "workspace"},
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
|
"availability": "observed",
|
||||||
"implementation": {"kind": "worker", "display_hint": "Coder"},
|
"implementation": {"kind": "worker", "display_hint": "Coder"},
|
||||||
"capabilities": {"can_stop": true, "can_spawn_followup": false},
|
|
||||||
"workdir_attachments": [{
|
"workdir_attachments": [{
|
||||||
"alias": "checkout",
|
"alias": "checkout",
|
||||||
|
"effective_permissions": {"read": true, "write": true, "command": true},
|
||||||
"working_directory": {
|
"working_directory": {
|
||||||
"working_directory_id": "wd-1",
|
"working_directory_id": "wd-1",
|
||||||
"source": {"kind": "repository", "repository_key": "main"},
|
"source": {"kind": "repository", "repository_key": "main"},
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pub use backend_runtime::{
|
|||||||
BackendCreateWorkerRequest, BackendCreateWorkerResponse, BackendDiagnostic,
|
BackendCreateWorkerRequest, BackendCreateWorkerResponse, BackendDiagnostic,
|
||||||
BackendDiagnosticSeverity, BackendRuntimeClientError, BackendRuntimeListResponse,
|
BackendDiagnosticSeverity, BackendRuntimeClientError, BackendRuntimeListResponse,
|
||||||
BackendRuntimeListTarget, BackendRuntimeSummary, BackendRuntimeTarget,
|
BackendRuntimeListTarget, BackendRuntimeSummary, BackendRuntimeTarget,
|
||||||
BackendWorkerCapabilitySummary, BackendWorkerImplementationSummary, BackendWorkerLaunchOptions,
|
BackendWorkerImplementationSummary, BackendWorkerLaunchOptions,
|
||||||
BackendWorkerLaunchProfileCandidate, BackendWorkerLaunchRuntimeOption,
|
BackendWorkerLaunchProfileCandidate, BackendWorkerLaunchRuntimeOption,
|
||||||
BackendWorkerLaunchTarget, BackendWorkerOperationState, BackendWorkerRestoreResponse,
|
BackendWorkerLaunchTarget, BackendWorkerOperationState, BackendWorkerRestoreResponse,
|
||||||
BackendWorkerRestoreResult, BackendWorkerRestoreState, BackendWorkerSummary,
|
BackendWorkerRestoreResult, BackendWorkerRestoreState, BackendWorkerSummary,
|
||||||
|
|||||||
@@ -1029,7 +1029,7 @@ mod tests {
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
"200 OK",
|
"200 OK",
|
||||||
r#"{"workspace_id":"workspace-a","runtime_id":"embedded","worker_id":"worker-1","console_href":"/w/workspace-a/workers/worker-1","worker":{"runtime_id":"embedded","worker_id":"worker-1","host_id":"embedded","display_name":"Intake","label":"worker-1","profile":"builtin:intake","singleton_key":null,"tags":[],"workspace":{"visibility":"workspace","identity":"workspace-a","workspace_id":"workspace-a"},"state":"idle","last_seen_at":null,"pinned":false,"retention_state":"active","implementation":{"kind":"runtime","display_hint":"Runtime Worker"},"capabilities":{"can_stop":true,"can_spawn_followup":false},"diagnostics":[]},"diagnostics":[]}"#,
|
r#"{"workspace_id":"workspace-a","runtime_id":"embedded","worker_id":"worker-1","console_href":"/w/workspace-a/workers/worker-1","worker":{"runtime_id":"embedded","worker_id":"worker-1","host_id":"embedded","display_name":"Intake","label":"worker-1","profile":"builtin:intake","singleton_key":null,"tags":[],"workspace":{"visibility":"workspace","identity":"workspace-a","workspace_id":"workspace-a"},"state":"idle","last_seen_at":null,"pinned":false,"retention_state":"active","implementation":{"kind":"runtime","display_hint":"Runtime Worker"},"diagnostics":[]},"diagnostics":[]}"#,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
let client = BackendWorkspaceProductClient::new_with_access_token(
|
let client = BackendWorkspaceProductClient::new_with_access_token(
|
||||||
@@ -1057,7 +1057,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn workspace_orchestrator_launch_uses_scoped_backend_route() {
|
fn workspace_orchestrator_launch_uses_scoped_backend_route() {
|
||||||
let body = r#"{"workspace_id":"workspace-a","online":true,"disposition":"created","worker":{"runtime_id":"embedded","worker_id":"worker-2","host_id":"embedded","display_name":"Orchestrator","label":"worker-2","profile":"builtin:orchestrator","singleton_key":"workspace-orchestrator","tags":[],"workspace":{"visibility":"workspace","identity":"workspace-a","workspace_id":"workspace-a"},"state":"idle","last_seen_at":null,"pinned":true,"retention_state":"active","implementation":{"kind":"runtime","display_hint":"Runtime Worker"},"capabilities":{"can_stop":true,"can_spawn_followup":false},"diagnostics":[]},"diagnostics":[]}"#;
|
let body = r#"{"workspace_id":"workspace-a","online":true,"disposition":"created","worker":{"runtime_id":"embedded","worker_id":"worker-2","host_id":"embedded","display_name":"Orchestrator","label":"worker-2","profile":"builtin:orchestrator","singleton_key":"workspace-orchestrator","tags":[],"workspace":{"visibility":"workspace","identity":"workspace-a","workspace_id":"workspace-a"},"state":"idle","last_seen_at":null,"pinned":true,"retention_state":"active","implementation":{"kind":"runtime","display_hint":"Runtime Worker"},"diagnostics":[]},"diagnostics":[]}"#;
|
||||||
let (base_url, request, handle) = one_response_server("200 OK", body);
|
let (base_url, request, handle) = one_response_server("200 OK", body);
|
||||||
let client = BackendWorkspaceProductClient::new_with_access_token(
|
let client = BackendWorkspaceProductClient::new_with_access_token(
|
||||||
base_url,
|
base_url,
|
||||||
|
|||||||
@@ -6892,7 +6892,6 @@ pub struct RuntimeWorkerSummary {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub retention_state: String,
|
pub retention_state: String,
|
||||||
pub implementation: WorkerImplementationSummary,
|
pub implementation: WorkerImplementationSummary,
|
||||||
pub capabilities: WorkerCapabilitySummary,
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub workdir_attachments: Vec<serde_json::Value>,
|
pub workdir_attachments: Vec<serde_json::Value>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -7888,13 +7887,6 @@ pub struct WorkerImplementationSummary {
|
|||||||
pub display_hint: String,
|
pub display_hint: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
|
||||||
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
|
||||||
pub struct WorkerCapabilitySummary {
|
|
||||||
pub can_stop: bool,
|
|
||||||
pub can_spawn_followup: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
||||||
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
|
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
|
||||||
@@ -8120,7 +8112,6 @@ pub struct WorkerSummary {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub retention_state: String,
|
pub retention_state: String,
|
||||||
pub implementation: WorkerImplementationSummary,
|
pub implementation: WorkerImplementationSummary,
|
||||||
pub capabilities: WorkerCapabilitySummary,
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub workdir_attachments: Vec<WorkerWorkdirAttachmentSummary>,
|
pub workdir_attachments: Vec<WorkerWorkdirAttachmentSummary>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -8163,7 +8154,6 @@ pub struct WorkerLaunchWorkerSummary {
|
|||||||
pub pinned: bool,
|
pub pinned: bool,
|
||||||
pub retention_state: String,
|
pub retention_state: String,
|
||||||
pub implementation: WorkerImplementationSummary,
|
pub implementation: WorkerImplementationSummary,
|
||||||
pub capabilities: WorkerCapabilitySummary,
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub workdir_attachments: Vec<RuntimeWorkerWorkdirAttachmentSummary>,
|
pub workdir_attachments: Vec<RuntimeWorkerWorkdirAttachmentSummary>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -9339,7 +9329,6 @@ pub fn worker_launch_api_typescript() -> String {
|
|||||||
WorkingDirectorySummary::decl(&config),
|
WorkingDirectorySummary::decl(&config),
|
||||||
WorkerWorkspaceSummary::decl(&config),
|
WorkerWorkspaceSummary::decl(&config),
|
||||||
WorkerImplementationSummary::decl(&config),
|
WorkerImplementationSummary::decl(&config),
|
||||||
WorkerCapabilitySummary::decl(&config),
|
|
||||||
RuntimeWorkerWorkdirAttachmentSummary::decl(&config),
|
RuntimeWorkerWorkdirAttachmentSummary::decl(&config),
|
||||||
WorkerLaunchWorkerSummary::decl(&config),
|
WorkerLaunchWorkerSummary::decl(&config),
|
||||||
WorkerLaunchRuntimeOption::decl(&config),
|
WorkerLaunchRuntimeOption::decl(&config),
|
||||||
@@ -10308,10 +10297,6 @@ mod tests {
|
|||||||
kind: "runtime".to_string(),
|
kind: "runtime".to_string(),
|
||||||
display_hint: "Runtime Worker".to_string(),
|
display_hint: "Runtime Worker".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: WorkerCapabilitySummary {
|
|
||||||
can_stop: true,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: Vec::new(),
|
workdir_attachments: Vec::new(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
@@ -10791,7 +10776,6 @@ mod tests {
|
|||||||
},
|
},
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
"implementation": {"kind": "worker", "display_hint": "Coder"},
|
"implementation": {"kind": "worker", "display_hint": "Coder"},
|
||||||
"capabilities": {"can_stop": true, "can_spawn_followup": false}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
assert!(serde_json::from_value::<WorkerSummary>(payload).is_err());
|
assert!(serde_json::from_value::<WorkerSummary>(payload).is_err());
|
||||||
|
|||||||
@@ -552,9 +552,8 @@ fn working_directory_text(worker: &BackendWorkerSummary) -> String {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use client::{
|
use client::{
|
||||||
BackendDiagnostic, BackendDiagnosticSeverity, BackendWorkerCapabilitySummary,
|
BackendDiagnostic, BackendDiagnosticSeverity, BackendWorkerImplementationSummary,
|
||||||
BackendWorkerImplementationSummary, BackendWorkerRestoreResult,
|
BackendWorkerRestoreResult, BackendWorkerWorkspaceSummary,
|
||||||
BackendWorkerWorkspaceSummary,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn worker(runtime_id: &str, worker_id: &str, profile: Option<&str>) -> BackendWorkerSummary {
|
fn worker(runtime_id: &str, worker_id: &str, profile: Option<&str>) -> BackendWorkerSummary {
|
||||||
@@ -588,10 +587,6 @@ mod tests {
|
|||||||
kind: "embedded".to_string(),
|
kind: "embedded".to_string(),
|
||||||
display_hint: "embedded".to_string(),
|
display_hint: "embedded".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: BackendWorkerCapabilitySummary {
|
|
||||||
can_stop: true,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: Vec::new(),
|
workdir_attachments: Vec::new(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1298,7 +1298,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn runtime_owned_workspace_client_has_no_fixed_request_timeout() {
|
fn runtime_owned_workspace_client_has_no_fixed_request_timeout() {
|
||||||
let (base_url, server) = delayed_workspace_response(Duration::from_millis(75));
|
// This must exceed reqwest::blocking::Client's default 30-second timeout.
|
||||||
|
// A short delay only checks our Option field, not the effective HTTP deadline.
|
||||||
|
let (base_url, server) = delayed_workspace_response(Duration::from_secs(31));
|
||||||
let client =
|
let client =
|
||||||
RuntimeOwnedWorkspaceClient::new("workspace-a", base_url, "runtime-a", "worker-a");
|
RuntimeOwnedWorkspaceClient::new("workspace-a", base_url, "runtime-a", "worker-a");
|
||||||
assert_eq!(client.request_timeout, None);
|
assert_eq!(client.request_timeout, None);
|
||||||
|
|||||||
@@ -240,12 +240,6 @@ pub struct InternalWorkerImplementationSummary {
|
|||||||
pub display_hint: String,
|
pub display_hint: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
||||||
pub struct InternalWorkerCapabilitySummary {
|
|
||||||
pub can_stop: bool,
|
|
||||||
pub can_spawn_followup: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct InternalWorkerSummary {
|
pub struct InternalWorkerSummary {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
@@ -273,7 +267,6 @@ pub struct InternalWorkerSummary {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub retention_state: String,
|
pub retention_state: String,
|
||||||
pub implementation: InternalWorkerImplementationSummary,
|
pub implementation: InternalWorkerImplementationSummary,
|
||||||
pub capabilities: InternalWorkerCapabilitySummary,
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub workdir_attachments: Vec<WorkingDirectoryAttachmentStatus>,
|
pub workdir_attachments: Vec<WorkingDirectoryAttachmentStatus>,
|
||||||
pub diagnostics: Vec<RuntimeDiagnostic>,
|
pub diagnostics: Vec<RuntimeDiagnostic>,
|
||||||
@@ -384,10 +377,6 @@ pub(crate) fn workspace_worker_summary(
|
|||||||
kind: summary.implementation.kind,
|
kind: summary.implementation.kind,
|
||||||
display_hint: summary.implementation.display_hint,
|
display_hint: summary.implementation.display_hint,
|
||||||
},
|
},
|
||||||
capabilities: server_api::WorkerCapabilitySummary {
|
|
||||||
can_stop: summary.capabilities.can_stop,
|
|
||||||
can_spawn_followup: summary.capabilities.can_spawn_followup,
|
|
||||||
},
|
|
||||||
workdir_attachments,
|
workdir_attachments,
|
||||||
diagnostics: summary.diagnostics.into_iter().map(Into::into).collect(),
|
diagnostics: summary.diagnostics.into_iter().map(Into::into).collect(),
|
||||||
}
|
}
|
||||||
@@ -2231,10 +2220,6 @@ impl EmbeddedWorkerRuntime {
|
|||||||
Some(EmbeddedWorkerRef::new(EmbeddedWorkerId::parse(worker_id)?))
|
Some(EmbeddedWorkerRef::new(EmbeddedWorkerId::parse(worker_id)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_stop_embedded_worker(&self, status: EmbeddedWorkerStatus) -> bool {
|
|
||||||
runtime_worker_can_stop(self.execution_enabled, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_worker_summary(
|
fn map_worker_summary(
|
||||||
&self,
|
&self,
|
||||||
summary: worker_runtime::catalog::WorkerSummary,
|
summary: worker_runtime::catalog::WorkerSummary,
|
||||||
@@ -2274,11 +2259,6 @@ impl EmbeddedWorkerRuntime {
|
|||||||
kind: "embedded_worker_runtime".to_string(),
|
kind: "embedded_worker_runtime".to_string(),
|
||||||
display_hint: "backend-internal worker-runtime Worker".to_string(),
|
display_hint: "backend-internal worker-runtime Worker".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: InternalWorkerCapabilitySummary {
|
|
||||||
can_stop: summary.execution_metadata_available
|
|
||||||
&& self.can_stop_embedded_worker(summary.status),
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: summary.workdir_attachments,
|
workdir_attachments: summary.workdir_attachments,
|
||||||
diagnostics: embedded_worker_projection_diagnostics(
|
diagnostics: embedded_worker_projection_diagnostics(
|
||||||
summary.execution_metadata_available,
|
summary.execution_metadata_available,
|
||||||
@@ -2319,11 +2299,6 @@ impl EmbeddedWorkerRuntime {
|
|||||||
kind: "embedded_worker_runtime".to_string(),
|
kind: "embedded_worker_runtime".to_string(),
|
||||||
display_hint: "backend-internal worker-runtime Worker".to_string(),
|
display_hint: "backend-internal worker-runtime Worker".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: InternalWorkerCapabilitySummary {
|
|
||||||
can_stop: detail.execution_metadata_available
|
|
||||||
&& self.can_stop_embedded_worker(detail.status),
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: detail.workdir_attachments,
|
workdir_attachments: detail.workdir_attachments,
|
||||||
diagnostics: embedded_worker_projection_diagnostics(
|
diagnostics: embedded_worker_projection_diagnostics(
|
||||||
detail.execution_metadata_available,
|
detail.execution_metadata_available,
|
||||||
@@ -4127,11 +4102,6 @@ impl RemoteWorkerRuntime {
|
|||||||
kind: "remote_worker_runtime".to_string(),
|
kind: "remote_worker_runtime".to_string(),
|
||||||
display_hint: "Backend-proxied remote worker-runtime Worker".to_string(),
|
display_hint: "Backend-proxied remote worker-runtime Worker".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: InternalWorkerCapabilitySummary {
|
|
||||||
can_stop: summary.execution_metadata_available
|
|
||||||
&& runtime_worker_can_stop(true, summary.status),
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: summary.workdir_attachments,
|
workdir_attachments: summary.workdir_attachments,
|
||||||
diagnostics: remote_worker_projection_diagnostics(summary.execution_metadata_available),
|
diagnostics: remote_worker_projection_diagnostics(summary.execution_metadata_available),
|
||||||
}
|
}
|
||||||
@@ -4170,11 +4140,6 @@ impl RemoteWorkerRuntime {
|
|||||||
kind: "remote_worker_runtime".to_string(),
|
kind: "remote_worker_runtime".to_string(),
|
||||||
display_hint: "Backend-proxied remote worker-runtime Worker".to_string(),
|
display_hint: "Backend-proxied remote worker-runtime Worker".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: InternalWorkerCapabilitySummary {
|
|
||||||
can_stop: detail.execution_metadata_available
|
|
||||||
&& runtime_worker_can_stop(true, detail.status),
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: detail.workdir_attachments,
|
workdir_attachments: detail.workdir_attachments,
|
||||||
diagnostics: remote_worker_projection_diagnostics(detail.execution_metadata_available),
|
diagnostics: remote_worker_projection_diagnostics(detail.execution_metadata_available),
|
||||||
}
|
}
|
||||||
@@ -5152,10 +5117,6 @@ fn embedded_runtime_status_label(status: RuntimeStatus) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtime_worker_can_stop(execution_enabled: bool, status: EmbeddedWorkerStatus) -> bool {
|
|
||||||
execution_enabled && status.is_active()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn embedded_worker_status_label(status: EmbeddedWorkerStatus) -> &'static str {
|
fn embedded_worker_status_label(status: EmbeddedWorkerStatus) -> &'static str {
|
||||||
match status {
|
match status {
|
||||||
EmbeddedWorkerStatus::Idle => "idle",
|
EmbeddedWorkerStatus::Idle => "idle",
|
||||||
@@ -5963,10 +5924,6 @@ pub fn placeholder_worker(host_id: impl Into<String>) -> InternalWorkerSummary {
|
|||||||
kind: "placeholder".to_string(),
|
kind: "placeholder".to_string(),
|
||||||
display_hint: "unsupported".to_string(),
|
display_hint: "unsupported".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: InternalWorkerCapabilitySummary {
|
|
||||||
can_stop: false,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: Vec::new(),
|
workdir_attachments: Vec::new(),
|
||||||
diagnostics: vec![diagnostic(
|
diagnostics: vec![diagnostic(
|
||||||
"runtime_capability_unsupported",
|
"runtime_capability_unsupported",
|
||||||
@@ -6621,10 +6578,6 @@ mod tests {
|
|||||||
kind: "fixture".to_string(),
|
kind: "fixture".to_string(),
|
||||||
display_hint: "test fixture".to_string(),
|
display_hint: "test fixture".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: InternalWorkerCapabilitySummary {
|
|
||||||
can_stop: false,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: Vec::new(),
|
workdir_attachments: Vec::new(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}],
|
}],
|
||||||
@@ -7123,7 +7076,12 @@ mod tests {
|
|||||||
let spawned = runtime.spawn_worker(test_create_binding(), embedded_spawn_request());
|
let spawned = runtime.spawn_worker(test_create_binding(), embedded_spawn_request());
|
||||||
assert_eq!(spawned.state, InternalWorkerOperationState::Accepted);
|
assert_eq!(spawned.state, InternalWorkerOperationState::Accepted);
|
||||||
let worker = spawned.worker.expect("created embedded worker");
|
let worker = spawned.worker.expect("created embedded worker");
|
||||||
assert!(worker.capabilities.can_stop);
|
assert!(
|
||||||
|
serde_json::to_value(&worker)
|
||||||
|
.unwrap()
|
||||||
|
.get("capabilities")
|
||||||
|
.is_none()
|
||||||
|
);
|
||||||
|
|
||||||
let input = runtime.send_input(
|
let input = runtime.send_input(
|
||||||
&worker.worker.worker_id,
|
&worker.worker.worker_id,
|
||||||
@@ -7496,7 +7454,12 @@ mod tests {
|
|||||||
workers.items[0].workspace.identity,
|
workers.items[0].workspace.identity,
|
||||||
"runtime_registry_worker"
|
"runtime_registry_worker"
|
||||||
);
|
);
|
||||||
assert!(workers.items[0].capabilities.can_stop);
|
assert!(
|
||||||
|
serde_json::to_value(&workers.items[0])
|
||||||
|
.unwrap()
|
||||||
|
.get("capabilities")
|
||||||
|
.is_none()
|
||||||
|
);
|
||||||
|
|
||||||
let input = registry
|
let input = registry
|
||||||
.send_input(
|
.send_input(
|
||||||
@@ -7581,11 +7544,14 @@ mod tests {
|
|||||||
|
|
||||||
let workers = registry.list_workers(10);
|
let workers = registry.list_workers(10);
|
||||||
assert_eq!(workers.items.len(), 5);
|
assert_eq!(workers.items.len(), 5);
|
||||||
assert!(!workers.items[0].capabilities.can_stop);
|
for worker in &workers.items {
|
||||||
assert!(workers.items[1].capabilities.can_stop);
|
assert!(
|
||||||
assert!(workers.items[2].capabilities.can_stop);
|
serde_json::to_value(worker)
|
||||||
assert!(workers.items[3].capabilities.can_stop);
|
.unwrap()
|
||||||
assert!(!workers.items[4].capabilities.can_stop);
|
.get("capabilities")
|
||||||
|
.is_none()
|
||||||
|
);
|
||||||
|
}
|
||||||
assert_eq!(workers.items[0].state, "stopped");
|
assert_eq!(workers.items[0].state, "stopped");
|
||||||
assert_eq!(workers.items[1].state, "running");
|
assert_eq!(workers.items[1].state, "running");
|
||||||
assert_eq!(workers.items[2].state, "paused");
|
assert_eq!(workers.items[2].state, "paused");
|
||||||
@@ -7601,7 +7567,12 @@ mod tests {
|
|||||||
let stopped_detail = registry
|
let stopped_detail = registry
|
||||||
.worker(&RuntimeWorkerRef::new("remote:primary", &worker_id))
|
.worker(&RuntimeWorkerRef::new("remote:primary", &worker_id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(!stopped_detail.capabilities.can_stop);
|
assert!(
|
||||||
|
serde_json::to_value(&stopped_detail)
|
||||||
|
.unwrap()
|
||||||
|
.get("capabilities")
|
||||||
|
.is_none()
|
||||||
|
);
|
||||||
assert_eq!(stopped_detail.state, "stopped");
|
assert_eq!(stopped_detail.state, "stopped");
|
||||||
|
|
||||||
server.join().expect("mock remote server finished");
|
server.join().expect("mock remote server finished");
|
||||||
|
|||||||
@@ -152,17 +152,16 @@ use crate::companion::{
|
|||||||
use crate::config_source::ConfigCommitRequest;
|
use crate::config_source::ConfigCommitRequest;
|
||||||
use crate::hosts::{
|
use crate::hosts::{
|
||||||
EMBEDDED_RUNTIME_ID, EmbeddedWorkerRuntime, HostDiagnosticSeverity, InternalHostSummary,
|
EMBEDDED_RUNTIME_ID, EmbeddedWorkerRuntime, HostDiagnosticSeverity, InternalHostSummary,
|
||||||
InternalWorkerCapabilitySummary, InternalWorkerImplementationSummary,
|
InternalWorkerImplementationSummary, InternalWorkerOperationState, InternalWorkerRestoreResult,
|
||||||
InternalWorkerOperationState, InternalWorkerRestoreResult, InternalWorkerSummary,
|
InternalWorkerSummary, InternalWorkerWorkspaceSummary, RemoteRuntimeConfig,
|
||||||
InternalWorkerWorkspaceSummary, RemoteRuntimeConfig, RemoteWorkerRuntime, RuntimeDiagnostic,
|
RemoteWorkerRuntime, RuntimeDiagnostic, RuntimePingFailureKind, RuntimeRegistry,
|
||||||
RuntimePingFailureKind, RuntimeRegistry, RuntimeRegistryError, RuntimeRegistryUnregisterResult,
|
RuntimeRegistryError, RuntimeRegistryUnregisterResult, TicketWorkerRole,
|
||||||
TicketWorkerRole, WorkerCompletionsRequest, WorkerCompletionsResult, WorkerControlOperation,
|
WorkerCompletionsRequest, WorkerCompletionsResult, WorkerControlOperation, WorkerCreateBinding,
|
||||||
WorkerCreateBinding, WorkerInputKind, WorkerInputRequest, WorkerInputResult,
|
WorkerInputKind, WorkerInputRequest, WorkerInputResult, WorkerLifecycleRequest,
|
||||||
WorkerLifecycleRequest, WorkerLifecycleResult, WorkerSpawnAcceptanceRequirement,
|
WorkerLifecycleResult, WorkerSpawnAcceptanceRequirement, WorkerSpawnIntent, WorkerSpawnRequest,
|
||||||
WorkerSpawnIntent, WorkerSpawnRequest, WorkerSpawnResult, WorkerSpawnWorkingDirectoryRequest,
|
WorkerSpawnResult, WorkerSpawnWorkingDirectoryRequest, WorkerTicketAssignmentRequest,
|
||||||
WorkerTicketAssignmentRequest, WorkspaceRuntimeAuthorization,
|
WorkspaceRuntimeAuthorization, is_disallowed_remote_runtime_address,
|
||||||
is_disallowed_remote_runtime_address, is_loopback_runtime_origin,
|
is_loopback_runtime_origin, worker_spawn_create_fingerprint, workspace_worker_summary,
|
||||||
worker_spawn_create_fingerprint, workspace_worker_summary,
|
|
||||||
};
|
};
|
||||||
use crate::memory_backend::execute_memory_backend_operation_with_authority;
|
use crate::memory_backend::execute_memory_backend_operation_with_authority;
|
||||||
use crate::memory_staging::{
|
use crate::memory_staging::{
|
||||||
@@ -5175,10 +5174,6 @@ fn runtime_worker_summary_to_api(
|
|||||||
kind: worker.implementation.kind,
|
kind: worker.implementation.kind,
|
||||||
display_hint: worker.implementation.display_hint,
|
display_hint: worker.implementation.display_hint,
|
||||||
},
|
},
|
||||||
capabilities: server_api::WorkerCapabilitySummary {
|
|
||||||
can_stop: worker.capabilities.can_stop,
|
|
||||||
can_spawn_followup: worker.capabilities.can_spawn_followup,
|
|
||||||
},
|
|
||||||
workdir_attachments: worker
|
workdir_attachments: worker
|
||||||
.workdir_attachments
|
.workdir_attachments
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -16956,10 +16951,6 @@ fn worker_launch_worker_summary(worker: InternalWorkerSummary) -> WorkerLaunchWo
|
|||||||
kind: worker.implementation.kind,
|
kind: worker.implementation.kind,
|
||||||
display_hint: worker.implementation.display_hint,
|
display_hint: worker.implementation.display_hint,
|
||||||
},
|
},
|
||||||
capabilities: server_api::WorkerCapabilitySummary {
|
|
||||||
can_stop: worker.capabilities.can_stop,
|
|
||||||
can_spawn_followup: worker.capabilities.can_spawn_followup,
|
|
||||||
},
|
|
||||||
workdir_attachments: worker
|
workdir_attachments: worker
|
||||||
.workdir_attachments
|
.workdir_attachments
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -24806,10 +24797,6 @@ fn worker_summary_from_registry(record: &WorkerRegistryRecord) -> InternalWorker
|
|||||||
last_seen_at: Some(record.updated_at.clone()),
|
last_seen_at: Some(record.updated_at.clone()),
|
||||||
pinned: record.retention_state == "pinned",
|
pinned: record.retention_state == "pinned",
|
||||||
retention_state: record.retention_state.clone(),
|
retention_state: record.retention_state.clone(),
|
||||||
capabilities: InternalWorkerCapabilitySummary {
|
|
||||||
can_stop: false,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workspace: InternalWorkerWorkspaceSummary {
|
workspace: InternalWorkerWorkspaceSummary {
|
||||||
visibility: "backend_registry".to_string(),
|
visibility: "backend_registry".to_string(),
|
||||||
identity: record.workspace_id.clone(),
|
identity: record.workspace_id.clone(),
|
||||||
@@ -24855,8 +24842,6 @@ fn worker_summary_from_projection(
|
|||||||
.then(|| observation.worker.worker_state.clone())
|
.then(|| observation.worker.worker_state.clone())
|
||||||
.flatten();
|
.flatten();
|
||||||
summary.last_seen_at = Some(observation.observed_at.clone());
|
summary.last_seen_at = Some(observation.observed_at.clone());
|
||||||
summary.capabilities.can_stop = observed
|
|
||||||
&& observation.worker.state != protocol::subscription::SubscriptionWorkerState::Stopped;
|
|
||||||
summary.implementation.display_hint = if observed {
|
summary.implementation.display_hint = if observed {
|
||||||
"Runtime-backed Worker".to_string()
|
"Runtime-backed Worker".to_string()
|
||||||
} else {
|
} else {
|
||||||
@@ -26709,7 +26694,12 @@ mod tests {
|
|||||||
assert_eq!(unavailable.availability, availability);
|
assert_eq!(unavailable.availability, availability);
|
||||||
assert_eq!(unavailable.state, "unavailable");
|
assert_eq!(unavailable.state, "unavailable");
|
||||||
assert_eq!(unavailable.worker_state, None);
|
assert_eq!(unavailable.worker_state, None);
|
||||||
assert!(!unavailable.capabilities.can_stop);
|
assert!(
|
||||||
|
serde_json::to_value(&unavailable)
|
||||||
|
.unwrap()
|
||||||
|
.get("capabilities")
|
||||||
|
.is_none()
|
||||||
|
);
|
||||||
|
|
||||||
let mut observed_stopped = projection;
|
let mut observed_stopped = projection;
|
||||||
let observation = observed_stopped.observation.as_mut().unwrap();
|
let observation = observed_stopped.observation.as_mut().unwrap();
|
||||||
@@ -28645,10 +28635,6 @@ mod tests {
|
|||||||
kind: "fixture".to_string(),
|
kind: "fixture".to_string(),
|
||||||
display_hint: "Workdirless fixture".to_string(),
|
display_hint: "Workdirless fixture".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: InternalWorkerCapabilitySummary {
|
|
||||||
can_stop: true,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: Vec::new(),
|
workdir_attachments: Vec::new(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
@@ -40983,10 +40969,6 @@ mod tests {
|
|||||||
kind: "remote".to_string(),
|
kind: "remote".to_string(),
|
||||||
display_hint: "remote".to_string(),
|
display_hint: "remote".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: server_api::WorkerCapabilitySummary {
|
|
||||||
can_stop: true,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: Vec::new(),
|
workdir_attachments: Vec::new(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
@@ -41081,10 +41063,6 @@ mod tests {
|
|||||||
kind: "remote".to_string(),
|
kind: "remote".to_string(),
|
||||||
display_hint: "remote".to_string(),
|
display_hint: "remote".to_string(),
|
||||||
},
|
},
|
||||||
capabilities: server_api::WorkerCapabilitySummary {
|
|
||||||
can_stop: true,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: Vec::new(),
|
workdir_attachments: Vec::new(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}],
|
}],
|
||||||
@@ -43445,7 +43423,12 @@ mod tests {
|
|||||||
// catalog remains controllable while its failed execution is represented in
|
// catalog remains controllable while its failed execution is represented in
|
||||||
// `worker_state` rather than by removing the catalog Worker.
|
// `worker_state` rather than by removing the catalog Worker.
|
||||||
assert_eq!(restored_worker.state, "idle");
|
assert_eq!(restored_worker.state, "idle");
|
||||||
assert!(restored_worker.capabilities.can_stop);
|
assert!(
|
||||||
|
serde_json::to_value(&restored_worker)
|
||||||
|
.unwrap()
|
||||||
|
.get("capabilities")
|
||||||
|
.is_none()
|
||||||
|
);
|
||||||
|
|
||||||
let bundles = restored
|
let bundles = restored
|
||||||
.runtime
|
.runtime
|
||||||
|
|||||||
+4
-35
@@ -7448,9 +7448,6 @@
|
|||||||
"$ref": "#/components/schemas/SubscriptionWorkerAvailability",
|
"$ref": "#/components/schemas/SubscriptionWorkerAvailability",
|
||||||
"description": "Freshness of the Runtime observation used to construct `worker_state`.\n`Unavailable` means no current controller snapshot is being asserted."
|
"description": "Freshness of the Runtime observation used to construct `worker_state`.\n`Unavailable` means no current controller snapshot is being asserted."
|
||||||
},
|
},
|
||||||
"capabilities": {
|
|
||||||
"$ref": "#/components/schemas/WorkerCapabilitySummary"
|
|
||||||
},
|
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"default": [],
|
"default": [],
|
||||||
"items": {
|
"items": {
|
||||||
@@ -7550,7 +7547,6 @@
|
|||||||
"availability",
|
"availability",
|
||||||
"state",
|
"state",
|
||||||
"implementation",
|
"implementation",
|
||||||
"capabilities",
|
|
||||||
"updated_at"
|
"updated_at"
|
||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
@@ -7791,9 +7787,6 @@
|
|||||||
"RuntimeWorkerSummary": {
|
"RuntimeWorkerSummary": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"capabilities": {
|
|
||||||
"$ref": "#/components/schemas/WorkerCapabilitySummary"
|
|
||||||
},
|
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"default": [],
|
"default": [],
|
||||||
"items": {
|
"items": {
|
||||||
@@ -7872,8 +7865,7 @@
|
|||||||
"label",
|
"label",
|
||||||
"workspace",
|
"workspace",
|
||||||
"state",
|
"state",
|
||||||
"implementation",
|
"implementation"
|
||||||
"capabilities"
|
|
||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
@@ -11254,21 +11246,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"WorkerCapabilitySummary": {
|
|
||||||
"properties": {
|
|
||||||
"can_spawn_followup": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"can_stop": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"can_stop",
|
|
||||||
"can_spawn_followup"
|
|
||||||
],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"WorkerFileDeleteResponse": {
|
"WorkerFileDeleteResponse": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -11447,9 +11424,6 @@
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "Runtime-owned Worker summary embedded in Worker launch responses.\n\nThis preserves the existing launch wire shape. Workspace-owned Worker list\nand detail responses use [`WorkerSummary`] instead.",
|
"description": "Runtime-owned Worker summary embedded in Worker launch responses.\n\nThis preserves the existing launch wire shape. Workspace-owned Worker list\nand detail responses use [`WorkerSummary`] instead.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"capabilities": {
|
|
||||||
"$ref": "#/components/schemas/WorkerCapabilitySummary"
|
|
||||||
},
|
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"default": [],
|
"default": [],
|
||||||
"items": {
|
"items": {
|
||||||
@@ -11529,8 +11503,7 @@
|
|||||||
"state",
|
"state",
|
||||||
"pinned",
|
"pinned",
|
||||||
"retention_state",
|
"retention_state",
|
||||||
"implementation",
|
"implementation"
|
||||||
"capabilities"
|
|
||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
@@ -11830,9 +11803,6 @@
|
|||||||
"$ref": "#/components/schemas/SubscriptionWorkerAvailability",
|
"$ref": "#/components/schemas/SubscriptionWorkerAvailability",
|
||||||
"description": "Freshness of the Runtime observation used to construct `worker_state`.\n`Unavailable` means no current controller snapshot is being asserted."
|
"description": "Freshness of the Runtime observation used to construct `worker_state`.\n`Unavailable` means no current controller snapshot is being asserted."
|
||||||
},
|
},
|
||||||
"capabilities": {
|
|
||||||
"$ref": "#/components/schemas/WorkerCapabilitySummary"
|
|
||||||
},
|
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"default": [],
|
"default": [],
|
||||||
"items": {
|
"items": {
|
||||||
@@ -11928,8 +11898,7 @@
|
|||||||
"workspace",
|
"workspace",
|
||||||
"availability",
|
"availability",
|
||||||
"state",
|
"state",
|
||||||
"implementation",
|
"implementation"
|
||||||
"capabilities"
|
|
||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
@@ -14020,7 +13989,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Yoi Server API",
|
"title": "Yoi Server API",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"x-yoi-source-digest": "sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0"
|
"x-yoi-source-digest": "sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043"
|
||||||
},
|
},
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ const worker = {
|
|||||||
profile: "default",
|
profile: "default",
|
||||||
tags: [],
|
tags: [],
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
capabilities: { can_stop: false, can_spawn_followup: false },
|
|
||||||
workspace: {
|
workspace: {
|
||||||
workspace_id: workspaceId,
|
workspace_id: workspaceId,
|
||||||
visibility: "workspace",
|
visibility: "workspace",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @generated by yoi-companion-openapi-typescript@1. Do not edit.
|
// @generated by yoi-companion-openapi-typescript@1. Do not edit.
|
||||||
// Input: openapi/server-api.json
|
// Input: openapi/server-api.json
|
||||||
// Input contract: 0.1.0 (sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0)
|
// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043)
|
||||||
// Options: companion-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
// Options: companion-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||||
// Output: web/workspace/src/lib/generated/companion-api.ts
|
// Output: web/workspace/src/lib/generated/companion-api.ts
|
||||||
// Regenerate/check: cargo run -q -p server-api --example generate_companion_api_types -- --check
|
// Regenerate/check: cargo run -q -p server-api --example generate_companion_api_types -- --check
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @generated by yoi-repository-openapi-typescript@1. Do not edit.
|
// @generated by yoi-repository-openapi-typescript@1. Do not edit.
|
||||||
// Input: openapi/server-api.json
|
// Input: openapi/server-api.json
|
||||||
// Input contract: 0.1.0 (sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0)
|
// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043)
|
||||||
// Options: repository-operation-schema-closure,openapi-3.1,format:uint64=safe-number,strict-safe-integers,sorted-output
|
// 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
|
// Output: web/workspace/src/lib/generated/repository-api.ts
|
||||||
// Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check
|
// Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check
|
||||||
@@ -61,8 +61,6 @@ export type SubscriptionWorkerAvailability = "observed" | "unavailable";
|
|||||||
|
|
||||||
export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; };
|
export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; };
|
||||||
|
|
||||||
export type WorkerCapabilitySummary = { can_spawn_followup: boolean; can_stop: boolean; };
|
|
||||||
|
|
||||||
export type WorkerImplementationSummary = { display_hint: string; kind: string; };
|
export type WorkerImplementationSummary = { display_hint: string; kind: string; };
|
||||||
|
|
||||||
export type WorkerMaintenanceState = "compacting";
|
export type WorkerMaintenanceState = "compacting";
|
||||||
@@ -73,7 +71,7 @@ export type WorkerState = { kind: "idle"; } | { kind: "busy"; state: WorkerBusyS
|
|||||||
|
|
||||||
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
||||||
|
|
||||||
export type WorkerSummary = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
export type WorkerSummary = { availability: SubscriptionWorkerAvailability; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
||||||
|
|
||||||
export type WorkerWorkdirAttachmentSummary = { alias: string; effective_permissions: ExternalWorkdirPermissions; working_directory: WorkingDirectorySummary; };
|
export type WorkerWorkdirAttachmentSummary = { alias: string; effective_permissions: ExternalWorkdirPermissions; working_directory: WorkingDirectorySummary; };
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @generated by yoi-runtime-openapi-typescript@1. Do not edit.
|
// @generated by yoi-runtime-openapi-typescript@1. Do not edit.
|
||||||
// Input: openapi/server-api.json
|
// Input: openapi/server-api.json
|
||||||
// Input contract: 0.1.0 (sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0)
|
// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043)
|
||||||
// Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
// Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||||
// Output: web/workspace/src/lib/generated/runtime-api.ts
|
// Output: web/workspace/src/lib/generated/runtime-api.ts
|
||||||
// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check
|
// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check
|
||||||
@@ -107,7 +107,7 @@ export type RuntimeWorkerLifecycleResult = { diagnostics?: Array<Diagnostic>; ru
|
|||||||
|
|
||||||
export type RuntimeWorkerListResponse = { diagnostics?: Array<Diagnostic>; items: Array<WorkerSummary>; limit: number; source: string; workspace_id: string; };
|
export type RuntimeWorkerListResponse = { diagnostics?: Array<Diagnostic>; items: Array<WorkerSummary>; limit: number; source: string; workspace_id: string; };
|
||||||
|
|
||||||
export type RuntimeWorkerShowResponse = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; updated_at: string; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
export type RuntimeWorkerShowResponse = { availability: SubscriptionWorkerAvailability; diagnostics?: Array<Diagnostic>; 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<string>; updated_at: string; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
||||||
|
|
||||||
export type RuntimeWorkerSpawnAcceptanceEvidence = { detail: string; kind: string; };
|
export type RuntimeWorkerSpawnAcceptanceEvidence = { detail: string; kind: string; };
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ export type RuntimeWorkerSpawnWorkingDirectoryAttachmentRequest = { alias: strin
|
|||||||
|
|
||||||
export type RuntimeWorkerSpawnWorkingDirectoryRequest = { repository_key: string; selector?: string | null; };
|
export type RuntimeWorkerSpawnWorkingDirectoryRequest = { repository_key: string; selector?: string | null; };
|
||||||
|
|
||||||
export type RuntimeWorkerSummary = { capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<unknown>; worker_id: string; worker_state?: unknown; workspace: WorkerWorkspaceSummary; };
|
export type RuntimeWorkerSummary = { diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<unknown>; worker_id: string; worker_state?: unknown; workspace: WorkerWorkspaceSummary; };
|
||||||
|
|
||||||
export type RuntimeWorkerTicketAssignmentRequest = { operation_id: string; ticket_id: string; };
|
export type RuntimeWorkerTicketAssignmentRequest = { operation_id: string; ticket_id: string; };
|
||||||
|
|
||||||
@@ -137,8 +137,6 @@ export type UploadedFileRef = { artifact_id: string; availability: UploadedFileA
|
|||||||
|
|
||||||
export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; };
|
export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; };
|
||||||
|
|
||||||
export type WorkerCapabilitySummary = { can_spawn_followup: boolean; can_stop: boolean; };
|
|
||||||
|
|
||||||
export type WorkerFileDeleteResponse = { deleted: boolean; };
|
export type WorkerFileDeleteResponse = { deleted: boolean; };
|
||||||
|
|
||||||
export type WorkerFileUploadResponse = { file: UploadedFileRef; };
|
export type WorkerFileUploadResponse = { file: UploadedFileRef; };
|
||||||
@@ -163,7 +161,7 @@ export type WorkerState = { kind: "idle"; } | { kind: "busy"; state: WorkerBusyS
|
|||||||
|
|
||||||
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
||||||
|
|
||||||
export type WorkerSummary = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
export type WorkerSummary = { availability: SubscriptionWorkerAvailability; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
||||||
|
|
||||||
export type WorkerWorkdirAttachmentSummary = { alias: string; effective_permissions: ExternalWorkdirPermissions; working_directory: WorkingDirectorySummary; };
|
export type WorkerWorkdirAttachmentSummary = { alias: string; effective_permissions: ExternalWorkdirPermissions; working_directory: WorkingDirectorySummary; };
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @generated by yoi-ticket-browser-openapi-typescript@1. Do not edit.
|
// @generated by yoi-ticket-browser-openapi-typescript@1. Do not edit.
|
||||||
// Input: openapi/server-api.json
|
// Input: openapi/server-api.json
|
||||||
// Input contract: 0.1.0 (sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0)
|
// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043)
|
||||||
// Options: ticket-objective-merge-request-browser-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
// Options: ticket-objective-merge-request-browser-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||||
// Output: web/workspace/src/lib/generated/ticket-api.ts
|
// Output: web/workspace/src/lib/generated/ticket-api.ts
|
||||||
// Regenerate/check: cargo run -q -p server-api --example generate_ticket_api_types -- --check
|
// Regenerate/check: cargo run -q -p server-api --example generate_ticket_api_types -- --check
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
|
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
|
||||||
// Input: openapi/server-api.json
|
// Input: openapi/server-api.json
|
||||||
// Input contract: 0.1.0 (sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0)
|
// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043)
|
||||||
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||||
// Output: web/workspace/src/lib/generated/worker-launch-api.ts
|
// Output: web/workspace/src/lib/generated/worker-launch-api.ts
|
||||||
// Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check
|
// Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check
|
||||||
@@ -45,8 +45,6 @@ export type UploadedFileRef = { artifact_id: string; availability: UploadedFileA
|
|||||||
|
|
||||||
export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; };
|
export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; };
|
||||||
|
|
||||||
export type WorkerCapabilitySummary = { can_spawn_followup: boolean; can_stop: boolean; };
|
|
||||||
|
|
||||||
export type WorkerImplementationSummary = { display_hint: string; kind: string; };
|
export type WorkerImplementationSummary = { display_hint: string; kind: string; };
|
||||||
|
|
||||||
export type WorkerLaunchOptionsResponse = { default_profile?: string | null; diagnostics: Array<Diagnostic>; profiles: Array<WorkerLaunchProfileCandidate>; repositories: Array<WorkingDirectoryRepositoryOption>; runtimes: Array<WorkerLaunchRuntimeOption>; working_directories: Array<WorkingDirectorySummary>; workspace_id: string; };
|
export type WorkerLaunchOptionsResponse = { default_profile?: string | null; diagnostics: Array<Diagnostic>; profiles: Array<WorkerLaunchProfileCandidate>; repositories: Array<WorkingDirectoryRepositoryOption>; runtimes: Array<WorkerLaunchRuntimeOption>; working_directories: Array<WorkingDirectorySummary>; workspace_id: string; };
|
||||||
@@ -55,7 +53,7 @@ export type WorkerLaunchProfileCandidate = { description: string; id: string; la
|
|||||||
|
|
||||||
export type WorkerLaunchRuntimeOption = { built_in: boolean; diagnostics: Array<Diagnostic>; display_name: string; runtime_id: string; status: string; worker_creation_available: boolean; working_directory_required: boolean; };
|
export type WorkerLaunchRuntimeOption = { built_in: boolean; diagnostics: Array<Diagnostic>; display_name: string; runtime_id: string; status: string; worker_creation_available: boolean; working_directory_required: boolean; };
|
||||||
|
|
||||||
export type WorkerLaunchWorkerSummary = { capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<RuntimeWorkerWorkdirAttachmentSummary>; worker_id: string; workspace: WorkerWorkspaceSummary; };
|
export type WorkerLaunchWorkerSummary = { diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<RuntimeWorkerWorkdirAttachmentSummary>; worker_id: string; workspace: WorkerWorkspaceSummary; };
|
||||||
|
|
||||||
export type WorkerListResponse = { diagnostics?: Array<Diagnostic>; items: Array<WorkerSummary>; limit: number; source: string; workspace_id: string; };
|
export type WorkerListResponse = { diagnostics?: Array<Diagnostic>; items: Array<WorkerSummary>; limit: number; source: string; workspace_id: string; };
|
||||||
|
|
||||||
@@ -67,7 +65,7 @@ export type WorkerState = { kind: "idle"; } | { kind: "busy"; state: WorkerBusyS
|
|||||||
|
|
||||||
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
||||||
|
|
||||||
export type WorkerSummary = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
export type WorkerSummary = { availability: SubscriptionWorkerAvailability; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
||||||
|
|
||||||
export type WorkerWorkdirAttachmentSummary = { alias: string; effective_permissions: ExternalWorkdirPermissions; working_directory: WorkingDirectorySummary; };
|
export type WorkerWorkdirAttachmentSummary = { alias: string; effective_permissions: ExternalWorkdirPermissions; working_directory: WorkingDirectorySummary; };
|
||||||
|
|
||||||
|
|||||||
@@ -56,13 +56,28 @@ const worker = {
|
|||||||
kind: "runtime",
|
kind: "runtime",
|
||||||
display_hint: "Runtime Worker",
|
display_hint: "Runtime Worker",
|
||||||
},
|
},
|
||||||
capabilities: {
|
|
||||||
can_stop: true,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Deno.test("Worker summaries omit the removed management capabilities field", () => {
|
||||||
|
const summary = { ...worker, resource_key: "W-1", availability: "observed" };
|
||||||
|
assertEquals("capabilities" in parseWorkerSummary(summary), false);
|
||||||
|
assertThrows(
|
||||||
|
() => parseWorkerSummary({ ...summary, capabilities: {} }),
|
||||||
|
Error,
|
||||||
|
"unknown field capabilities",
|
||||||
|
);
|
||||||
|
const created = parseBrowserCreateWorkerResponse({
|
||||||
|
workspace_id: "workspace-a",
|
||||||
|
runtime_id: "runtime-a",
|
||||||
|
worker_id: "worker-a",
|
||||||
|
console_href: "/w/workspace-a/workers/W-1/console",
|
||||||
|
worker,
|
||||||
|
diagnostics: [],
|
||||||
|
});
|
||||||
|
assertEquals("capabilities" in created.worker, false);
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test("Worker summary parser enforces observation freshness", () => {
|
Deno.test("Worker summary parser enforces observation freshness", () => {
|
||||||
const unavailable = {
|
const unavailable = {
|
||||||
...worker,
|
...worker,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import type {
|
|||||||
RuntimeWorkerWorkdirAttachmentSummary,
|
RuntimeWorkerWorkdirAttachmentSummary,
|
||||||
RuntimeWorkingDirectoryCleanupTarget,
|
RuntimeWorkingDirectoryCleanupTarget,
|
||||||
RuntimeWorkingDirectorySummary,
|
RuntimeWorkingDirectorySummary,
|
||||||
WorkerCapabilitySummary,
|
|
||||||
WorkerImplementationSummary,
|
WorkerImplementationSummary,
|
||||||
WorkerLaunchOptionsResponse,
|
WorkerLaunchOptionsResponse,
|
||||||
WorkerLaunchProfileCandidate,
|
WorkerLaunchProfileCandidate,
|
||||||
@@ -251,21 +250,6 @@ function implementationSummary(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function capabilitySummary(
|
|
||||||
value: unknown,
|
|
||||||
label: string,
|
|
||||||
): WorkerCapabilitySummary {
|
|
||||||
const item = record(value, label);
|
|
||||||
exact(item, ["can_stop", "can_spawn_followup"], label);
|
|
||||||
return {
|
|
||||||
can_stop: boolean(item.can_stop, `${label}.can_stop`),
|
|
||||||
can_spawn_followup: boolean(
|
|
||||||
item.can_spawn_followup,
|
|
||||||
`${label}.can_spawn_followup`,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function workerStateSnapshot(
|
function workerStateSnapshot(
|
||||||
value: unknown,
|
value: unknown,
|
||||||
label: string,
|
label: string,
|
||||||
@@ -374,7 +358,6 @@ export function parseWorkerSummary(
|
|||||||
"pinned",
|
"pinned",
|
||||||
"retention_state",
|
"retention_state",
|
||||||
"implementation",
|
"implementation",
|
||||||
"capabilities",
|
|
||||||
"workdir_attachments",
|
"workdir_attachments",
|
||||||
"diagnostics",
|
"diagnostics",
|
||||||
],
|
],
|
||||||
@@ -423,7 +406,6 @@ export function parseWorkerSummary(
|
|||||||
item.implementation,
|
item.implementation,
|
||||||
`${label}.implementation`,
|
`${label}.implementation`,
|
||||||
),
|
),
|
||||||
capabilities: capabilitySummary(item.capabilities, `${label}.capabilities`),
|
|
||||||
workdir_attachments: item.workdir_attachments === undefined
|
workdir_attachments: item.workdir_attachments === undefined
|
||||||
? undefined
|
? undefined
|
||||||
: array(
|
: array(
|
||||||
@@ -617,7 +599,6 @@ function workerSummary(
|
|||||||
"pinned",
|
"pinned",
|
||||||
"retention_state",
|
"retention_state",
|
||||||
"implementation",
|
"implementation",
|
||||||
"capabilities",
|
|
||||||
"workdir_attachments",
|
"workdir_attachments",
|
||||||
"diagnostics",
|
"diagnostics",
|
||||||
],
|
],
|
||||||
@@ -641,7 +622,6 @@ function workerSummary(
|
|||||||
item.implementation,
|
item.implementation,
|
||||||
`${label}.implementation`,
|
`${label}.implementation`,
|
||||||
),
|
),
|
||||||
capabilities: capabilitySummary(item.capabilities, `${label}.capabilities`),
|
|
||||||
workdir_attachments: item.workdir_attachments === undefined
|
workdir_attachments: item.workdir_attachments === undefined
|
||||||
? undefined
|
? undefined
|
||||||
: array(
|
: array(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { workerConsoleHref } from '$lib/workspace/resource-links';
|
import { workerConsoleHref } from '$lib/workspace/resource-links';
|
||||||
import { pushWorkspaceAlert } from '$lib/workspace/alerts/store';
|
import { pushWorkspaceAlert } from '$lib/workspace/alerts/store';
|
||||||
import {
|
import {
|
||||||
|
canStopSidebarWorker,
|
||||||
canDeleteSidebarWorker,
|
canDeleteSidebarWorker,
|
||||||
deleteSidebarWorker,
|
deleteSidebarWorker,
|
||||||
stopSidebarWorker,
|
stopSidebarWorker,
|
||||||
@@ -91,14 +92,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function stopWorker(worker: SidebarWorker) {
|
async function stopWorker(worker: SidebarWorker) {
|
||||||
if (busyAction || !worker.capabilities.can_stop) return;
|
if (busyAction || !canStopSidebarWorker(worker)) return;
|
||||||
closeWorkerMenu();
|
closeWorkerMenu();
|
||||||
busyAction = { workerKey: workerKey(worker), kind: 'stop' };
|
busyAction = { workerKey: workerKey(worker), kind: 'stop' };
|
||||||
try {
|
try {
|
||||||
await stopSidebarWorker(workspaceId, worker);
|
await stopSidebarWorker(workspaceId, worker);
|
||||||
workers = workers.map((item) =>
|
workers = workers.map((item) =>
|
||||||
workerKey(item) === workerKey(worker)
|
workerKey(item) === workerKey(worker)
|
||||||
? { ...item, state: 'stopped', capabilities: { ...item.capabilities, can_stop: false } }
|
? { ...item, state: 'stopped', lifecycleState: 'stopped' }
|
||||||
: item
|
: item
|
||||||
);
|
);
|
||||||
pushWorkspaceAlert('info', `${worker.display_name || worker.label} stopped`, {
|
pushWorkspaceAlert('info', `${worker.display_name || worker.label} stopped`, {
|
||||||
@@ -226,7 +227,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
disabled={busyAction !== null || !worker.capabilities.can_stop}
|
disabled={busyAction !== null || !canStopSidebarWorker(worker)}
|
||||||
onclick={() => stopWorker(worker)}
|
onclick={() => stopWorker(worker)}
|
||||||
>
|
>
|
||||||
{isBusy(worker, 'stop') ? 'Stopping…' : 'Stop'}
|
{isBusy(worker, 'stop') ? 'Stopping…' : 'Stop'}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import type {
|
|||||||
BrowserWorkerWorkingDirectorySelection
|
BrowserWorkerWorkingDirectorySelection
|
||||||
as SharedBrowserWorkerWorkingDirectorySelection,
|
as SharedBrowserWorkerWorkingDirectorySelection,
|
||||||
Diagnostic as SharedDiagnostic,
|
Diagnostic as SharedDiagnostic,
|
||||||
WorkerCapabilitySummary as SharedWorkerCapabilitySummary,
|
|
||||||
WorkerLaunchOptionsResponse as SharedWorkerLaunchOptionsResponse,
|
WorkerLaunchOptionsResponse as SharedWorkerLaunchOptionsResponse,
|
||||||
WorkerLaunchProfileCandidate as SharedWorkerLaunchProfileCandidate,
|
WorkerLaunchProfileCandidate as SharedWorkerLaunchProfileCandidate,
|
||||||
WorkerLaunchRuntimeOption as SharedWorkerLaunchRuntimeOption,
|
WorkerLaunchRuntimeOption as SharedWorkerLaunchRuntimeOption,
|
||||||
@@ -78,8 +77,6 @@ export type Runtime = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkerCapabilities = SharedWorkerCapabilitySummary;
|
|
||||||
|
|
||||||
export type WorkerWorkdirAttachment = SharedWorkerWorkdirAttachmentSummary;
|
export type WorkerWorkdirAttachment = SharedWorkerWorkdirAttachmentSummary;
|
||||||
|
|
||||||
export type Worker =
|
export type Worker =
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ import {
|
|||||||
parseRuntimeCleanupPlan,
|
parseRuntimeCleanupPlan,
|
||||||
parseRuntimeWorkerLifecycleResult,
|
parseRuntimeWorkerLifecycleResult,
|
||||||
} from "$lib/workspace/api/runtime-workers";
|
} from "$lib/workspace/api/runtime-workers";
|
||||||
import type {
|
import type { Diagnostic, Worker } from "./types";
|
||||||
Diagnostic,
|
|
||||||
Worker,
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
type FetchFn = typeof fetch;
|
type FetchFn = typeof fetch;
|
||||||
type WorkerActionTarget = Pick<Worker, "runtime_id" | "worker_id" | "state">;
|
type WorkerActionTarget = Pick<Worker, "runtime_id" | "worker_id" | "state">;
|
||||||
@@ -25,13 +22,17 @@ async function responseError(response: Response): Promise<string> {
|
|||||||
const fallback = `${response.status} ${response.statusText}`.trim();
|
const fallback = `${response.status} ${response.statusText}`.trim();
|
||||||
try {
|
try {
|
||||||
const payload: unknown = await response.json();
|
const payload: unknown = await response.json();
|
||||||
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
if (
|
||||||
|
typeof payload !== "object" || payload === null || Array.isArray(payload)
|
||||||
|
) {
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
const record = payload as Record<string, unknown>;
|
const record = payload as Record<string, unknown>;
|
||||||
if (typeof record.message === "string") return record.message.slice(0, 512);
|
if (typeof record.message === "string") return record.message.slice(0, 512);
|
||||||
const nested = record.error;
|
const nested = record.error;
|
||||||
if (typeof nested === "object" && nested !== null && !Array.isArray(nested)) {
|
if (
|
||||||
|
typeof nested === "object" && nested !== null && !Array.isArray(nested)
|
||||||
|
) {
|
||||||
const message = (nested as Record<string, unknown>).message;
|
const message = (nested as Record<string, unknown>).message;
|
||||||
if (typeof message === "string") return message.slice(0, 512);
|
if (typeof message === "string") return message.slice(0, 512);
|
||||||
}
|
}
|
||||||
@@ -51,6 +52,14 @@ function diagnosticMessage(
|
|||||||
fallback;
|
fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function canStopSidebarWorker(worker: {
|
||||||
|
availability: Worker["availability"];
|
||||||
|
lifecycleState: string;
|
||||||
|
}): boolean {
|
||||||
|
return worker.availability === "observed" &&
|
||||||
|
["idle", "running", "paused"].includes(worker.lifecycleState);
|
||||||
|
}
|
||||||
|
|
||||||
export function canDeleteSidebarWorker(worker: WorkerActionTarget): boolean {
|
export function canDeleteSidebarWorker(worker: WorkerActionTarget): boolean {
|
||||||
return worker.state === "stopped";
|
return worker.state === "stopped";
|
||||||
}
|
}
|
||||||
@@ -112,7 +121,9 @@ export async function deleteSidebarWorker(
|
|||||||
throw new Error(await responseError(executionResponse));
|
throw new Error(await responseError(executionResponse));
|
||||||
}
|
}
|
||||||
|
|
||||||
const execution = parseRuntimeCleanupExecution(await executionResponse.json());
|
const execution = parseRuntimeCleanupExecution(
|
||||||
|
await executionResponse.json(),
|
||||||
|
);
|
||||||
const outcome = execution.results.find((result) =>
|
const outcome = execution.results.find((result) =>
|
||||||
result.target_id === candidate.target_id
|
result.target_id === candidate.target_id
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
// @vitest-environment happy-dom
|
||||||
|
|
||||||
|
import {
|
||||||
|
cleanup,
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
waitFor,
|
||||||
|
} from "@testing-library/svelte";
|
||||||
|
import { afterEach, expect, test, vi } from "vitest";
|
||||||
|
import type {
|
||||||
|
SubscriptionFrame,
|
||||||
|
SubscriptionWorker,
|
||||||
|
} from "$lib/generated/protocol";
|
||||||
|
import WorkersNavSection from "./WorkersNavSection.svelte";
|
||||||
|
import { disposeWorkspaceWorkersStore } from "./worker-subscription";
|
||||||
|
|
||||||
|
const transport = vi.hoisted(() => ({
|
||||||
|
listener: null as { onFrame(frame: SubscriptionFrame): void } | null,
|
||||||
|
close: vi.fn(),
|
||||||
|
}));
|
||||||
|
vi.mock("$lib/workspace/multiplexer", () => ({
|
||||||
|
workspaceMultiplexer: () => ({
|
||||||
|
subscribe: (
|
||||||
|
_selector: unknown,
|
||||||
|
listener: NonNullable<typeof transport.listener>,
|
||||||
|
) => {
|
||||||
|
transport.listener = listener;
|
||||||
|
return { close: transport.close };
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
disposeWorkspaceWorkersStore("stop-test");
|
||||||
|
transport.listener = null;
|
||||||
|
vi.unstubAllGlobals();
|
||||||
|
});
|
||||||
|
|
||||||
|
function snapshot(
|
||||||
|
state: SubscriptionWorker["state"],
|
||||||
|
availability: SubscriptionWorker["availability"],
|
||||||
|
): SubscriptionFrame {
|
||||||
|
return {
|
||||||
|
protocol_version: 1,
|
||||||
|
frame: "response",
|
||||||
|
message: {
|
||||||
|
result: "subscribed",
|
||||||
|
payload: {
|
||||||
|
request_id: "request-1",
|
||||||
|
subscription_id: "subscription-1",
|
||||||
|
selector: { topic: "workspace_workers" },
|
||||||
|
snapshot_revision: 1,
|
||||||
|
snapshot: {
|
||||||
|
topic: "workers",
|
||||||
|
data: {
|
||||||
|
workers: [{
|
||||||
|
worker_id: "worker-a",
|
||||||
|
runtime_id: "runtime-a",
|
||||||
|
resource_key: "W-1",
|
||||||
|
availability,
|
||||||
|
subject_revision: 1,
|
||||||
|
state,
|
||||||
|
// An absent foreground snapshot must not prevent stopping an active catalog Worker.
|
||||||
|
worker_state: null,
|
||||||
|
has_running_internal_workers: false,
|
||||||
|
display_name: "Test worker",
|
||||||
|
workspace_id: "stop-test",
|
||||||
|
workdir_attachments: [],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test.each(["idle", "running", "paused", "stopped"] as const)(
|
||||||
|
"Stop uses observed catalog state without capabilities (%s)",
|
||||||
|
async (state) => {
|
||||||
|
const request = vi.fn(async (input: RequestInfo | URL) =>
|
||||||
|
String(input).endsWith("/stop")
|
||||||
|
? Response.json({
|
||||||
|
state: "accepted",
|
||||||
|
runtime_id: "runtime-a",
|
||||||
|
worker_id: "worker-a",
|
||||||
|
diagnostics: [],
|
||||||
|
})
|
||||||
|
: new Response(null, { status: 404 })
|
||||||
|
);
|
||||||
|
vi.stubGlobal("fetch", request);
|
||||||
|
render(WorkersNavSection, { workspaceId: "stop-test" });
|
||||||
|
await waitFor(() => expect(transport.listener).not.toBeNull());
|
||||||
|
transport.listener!.onFrame(snapshot(state, "observed"));
|
||||||
|
await fireEvent.click(
|
||||||
|
await screen.findByRole("button", { name: "Actions for Test worker" }),
|
||||||
|
);
|
||||||
|
const stop = screen.getByRole("menuitem", {
|
||||||
|
name: "Stop",
|
||||||
|
}) as HTMLButtonElement;
|
||||||
|
expect(stop.disabled).toBe(state === "stopped");
|
||||||
|
if (state === "stopped") return;
|
||||||
|
await fireEvent.click(stop);
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(request.mock.calls.some(([url]) => String(url).endsWith("/stop")))
|
||||||
|
.toBe(true)
|
||||||
|
);
|
||||||
|
await fireEvent.click(
|
||||||
|
screen.getByRole("button", { name: "Actions for Test worker" }),
|
||||||
|
);
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(
|
||||||
|
(screen.getByRole("menuitem", { name: "Stop" }) as HTMLButtonElement)
|
||||||
|
.disabled,
|
||||||
|
).toBe(true)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test("Unavailable observation disables Stop even when catalog state is running", async () => {
|
||||||
|
vi.stubGlobal(
|
||||||
|
"fetch",
|
||||||
|
vi.fn(async () => new Response(null, { status: 404 })),
|
||||||
|
);
|
||||||
|
render(WorkersNavSection, { workspaceId: "stop-test" });
|
||||||
|
await waitFor(() => expect(transport.listener).not.toBeNull());
|
||||||
|
transport.listener!.onFrame(snapshot("running", "unavailable"));
|
||||||
|
await fireEvent.click(
|
||||||
|
await screen.findByRole("button", { name: "Actions for Test worker" }),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(screen.getByRole("menuitem", { name: "Stop" }) as HTMLButtonElement)
|
||||||
|
.disabled,
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
@@ -16,6 +16,8 @@ import type { Worker } from "./types";
|
|||||||
export type SidebarWorker = Omit<Worker, "workdir_attachments"> & {
|
export type SidebarWorker = Omit<Worker, "workdir_attachments"> & {
|
||||||
workdir_attachments: SidebarWorkdirAttachment[];
|
workdir_attachments: SidebarWorkdirAttachment[];
|
||||||
has_running_internal_workers: boolean;
|
has_running_internal_workers: boolean;
|
||||||
|
/** Catalog lifecycle, independent of the possibly unknown foreground display state. */
|
||||||
|
lifecycleState: SubscriptionWorker["state"];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkspaceWorkersState = {
|
export type WorkspaceWorkersState = {
|
||||||
@@ -148,6 +150,7 @@ function projectWorker(
|
|||||||
},
|
},
|
||||||
availability: worker.availability,
|
availability: worker.availability,
|
||||||
state: liveWorkerState(worker),
|
state: liveWorkerState(worker),
|
||||||
|
lifecycleState: worker.state,
|
||||||
worker_state: worker.worker_state,
|
worker_state: worker.worker_state,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
retention_state: "transient",
|
retention_state: "transient",
|
||||||
@@ -155,11 +158,6 @@ function projectWorker(
|
|||||||
kind: "runtime_subscription_worker",
|
kind: "runtime_subscription_worker",
|
||||||
display_hint: "Workspace-authorized Runtime Worker",
|
display_hint: "Workspace-authorized Runtime Worker",
|
||||||
},
|
},
|
||||||
capabilities: {
|
|
||||||
can_stop: worker.availability !== "unavailable" &&
|
|
||||||
worker.state !== "stopped",
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: (worker.workdir_attachments ?? []).map((
|
workdir_attachments: (worker.workdir_attachments ?? []).map((
|
||||||
attachment,
|
attachment,
|
||||||
) => ({
|
) => ({
|
||||||
|
|||||||
@@ -38,10 +38,6 @@ function worker(overrides: Partial<Worker>): Worker {
|
|||||||
kind: "runtime_worker",
|
kind: "runtime_worker",
|
||||||
display_hint: "Runtime Worker",
|
display_hint: "Runtime Worker",
|
||||||
},
|
},
|
||||||
capabilities: {
|
|
||||||
can_stop: true,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
workdir_attachments: [],
|
workdir_attachments: [],
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
...overrides,
|
...overrides,
|
||||||
@@ -56,10 +52,6 @@ Deno.test("registry-only workers are not sidebar targets or console targets", ()
|
|||||||
kind: "backend_worker_registry",
|
kind: "backend_worker_registry",
|
||||||
display_hint: "Missing Worker",
|
display_hint: "Missing Worker",
|
||||||
},
|
},
|
||||||
capabilities: {
|
|
||||||
can_stop: false,
|
|
||||||
can_spawn_followup: false,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
assertEquals(canShowWorkerInSidebar(registryOnly), false);
|
assertEquals(canShowWorkerInSidebar(registryOnly), false);
|
||||||
assertEquals(canOpenWorkerConsole(registryOnly), false);
|
assertEquals(canOpenWorkerConsole(registryOnly), false);
|
||||||
|
|||||||
@@ -1935,22 +1935,6 @@
|
|||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
<details class="metadata-details">
|
|
||||||
<summary>Capabilities</summary>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
stop: {worker.capabilities.can_stop
|
|
||||||
? "available"
|
|
||||||
: "unsupported"}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
follow-up spawn: {worker.capabilities
|
|
||||||
.can_spawn_followup
|
|
||||||
? "available"
|
|
||||||
: "unsupported"}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</details>
|
|
||||||
{:else if !workerError}
|
{:else if !workerError}
|
||||||
<p>Loading Worker detail…</p>
|
<p>Loading Worker detail…</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
-4
@@ -62,10 +62,6 @@ function worker(workerId = "worker-a", runtimeId = "runtime-a"): Worker {
|
|||||||
profile: "default",
|
profile: "default",
|
||||||
tags: [],
|
tags: [],
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
capabilities: {
|
|
||||||
can_stop: true,
|
|
||||||
can_spawn_followup: true,
|
|
||||||
},
|
|
||||||
workspace: { visibility: "workspace", identity: "workspace-a" },
|
workspace: { visibility: "workspace", identity: "workspace-a" },
|
||||||
implementation: { kind: "builtin", display_hint: "test" },
|
implementation: { kind: "builtin", display_hint: "test" },
|
||||||
workdir_attachments: [],
|
workdir_attachments: [],
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
canDeleteSidebarWorker,
|
canDeleteSidebarWorker,
|
||||||
|
canStopSidebarWorker,
|
||||||
deleteSidebarWorker,
|
deleteSidebarWorker,
|
||||||
stopSidebarWorker,
|
stopSidebarWorker,
|
||||||
} from "../../src/lib/workspace/sidebar/worker-actions.ts";
|
} from "../../src/lib/workspace/sidebar/worker-actions.ts";
|
||||||
@@ -41,7 +42,6 @@ const worker = {
|
|||||||
runtime_id: "runtime /",
|
runtime_id: "runtime /",
|
||||||
worker_id: "worker /",
|
worker_id: "worker /",
|
||||||
state: "running",
|
state: "running",
|
||||||
capabilities: { can_stop: true },
|
|
||||||
} as Worker;
|
} as Worker;
|
||||||
|
|
||||||
function jsonResponse(payload: unknown, status = 200): Response {
|
function jsonResponse(payload: unknown, status = 200): Response {
|
||||||
@@ -51,6 +51,33 @@ function jsonResponse(payload: unknown, status = 200): Response {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Deno.test("sidebar Stop follows observed catalog lifecycle, not foreground display state", () => {
|
||||||
|
for (const lifecycleState of ["idle", "running", "paused"]) {
|
||||||
|
const target = {
|
||||||
|
availability: "observed" as const,
|
||||||
|
lifecycleState,
|
||||||
|
state: "unknown",
|
||||||
|
};
|
||||||
|
assert(canStopSidebarWorker(target));
|
||||||
|
assert(!canStopSidebarWorker({ ...target, availability: "unavailable" }));
|
||||||
|
}
|
||||||
|
for (
|
||||||
|
const lifecycleState of [
|
||||||
|
"stopped",
|
||||||
|
"missing",
|
||||||
|
"execution_unavailable",
|
||||||
|
"unknown",
|
||||||
|
]
|
||||||
|
) {
|
||||||
|
const target = {
|
||||||
|
availability: "observed" as const,
|
||||||
|
lifecycleState,
|
||||||
|
state: "running",
|
||||||
|
};
|
||||||
|
assert(!canStopSidebarWorker(target));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test("sidebar Stop uses the workspace-scoped Worker lifecycle endpoint", async () => {
|
Deno.test("sidebar Stop uses the workspace-scoped Worker lifecycle endpoint", async () => {
|
||||||
const requests: Array<{ url: string; init?: RequestInit }> = [];
|
const requests: Array<{ url: string; init?: RequestInit }> = [];
|
||||||
const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user