worker: separate display metadata
This commit is contained in:
parent
ff905d4a22
commit
006762f900
|
|
@ -141,11 +141,17 @@ pub struct BackendWorkerSummary {
|
||||||
pub runtime_id: String,
|
pub runtime_id: String,
|
||||||
pub worker_id: String,
|
pub worker_id: String,
|
||||||
pub host_id: String,
|
pub host_id: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub display_name: String,
|
||||||
pub label: String,
|
pub label: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub role: Option<String>,
|
pub role: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub profile: Option<String>,
|
pub profile: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub singleton_key: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tags: Vec<String>,
|
||||||
pub workspace: BackendWorkerWorkspaceSummary,
|
pub workspace: BackendWorkerWorkspaceSummary,
|
||||||
pub state: String,
|
pub state: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,8 @@ pub struct WorkspaceApiRef {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct CreateWorkerRequest {
|
pub struct CreateWorkerRequest {
|
||||||
pub profile: ProfileSelector,
|
pub profile: ProfileSelector,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub display_name: Option<String>,
|
||||||
pub profile_source: ProfileSourceArchiveSource,
|
pub profile_source: ProfileSourceArchiveSource,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub config_bundle: Option<ConfigBundleRef>,
|
pub config_bundle: Option<ConfigBundleRef>,
|
||||||
|
|
@ -228,6 +230,8 @@ pub struct WorkerSummary {
|
||||||
pub status: WorkerStatus,
|
pub status: WorkerStatus,
|
||||||
pub execution: WorkerExecutionStatus,
|
pub execution: WorkerExecutionStatus,
|
||||||
pub profile: ProfileSelector,
|
pub profile: ProfileSelector,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub display_name: Option<String>,
|
||||||
pub profile_source: ProfileSourceArchiveRef,
|
pub profile_source: ProfileSourceArchiveRef,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub config_bundle: Option<ConfigBundleRef>,
|
pub config_bundle: Option<ConfigBundleRef>,
|
||||||
|
|
@ -242,6 +246,8 @@ pub struct WorkerDetail {
|
||||||
pub status: WorkerStatus,
|
pub status: WorkerStatus,
|
||||||
pub execution: WorkerExecutionStatus,
|
pub execution: WorkerExecutionStatus,
|
||||||
pub profile: ProfileSelector,
|
pub profile: ProfileSelector,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub display_name: Option<String>,
|
||||||
pub profile_source: ProfileSourceArchiveRef,
|
pub profile_source: ProfileSourceArchiveRef,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub config_bundle: Option<ConfigBundleRef>,
|
pub config_bundle: Option<ConfigBundleRef>,
|
||||||
|
|
|
||||||
|
|
@ -887,6 +887,7 @@ mod tests {
|
||||||
let bundle = test_bundle(profile.clone());
|
let bundle = test_bundle(profile.clone());
|
||||||
CreateWorkerRequest {
|
CreateWorkerRequest {
|
||||||
profile,
|
profile,
|
||||||
|
display_name: None,
|
||||||
profile_source: crate::catalog::ProfileSourceArchiveSource::Http {
|
profile_source: crate::catalog::ProfileSourceArchiveSource::Http {
|
||||||
location: crate::catalog::ProfileSourceArchiveHttpRef {
|
location: crate::catalog::ProfileSourceArchiveHttpRef {
|
||||||
url: "http://127.0.0.1/profile-source.tar".to_string(),
|
url: "http://127.0.0.1/profile-source.tar".to_string(),
|
||||||
|
|
@ -1210,6 +1211,7 @@ mod ws_tests {
|
||||||
let bundle = ws_test_bundle(ProfileSelector::RuntimeDefault);
|
let bundle = ws_test_bundle(ProfileSelector::RuntimeDefault);
|
||||||
CreateWorkerRequest {
|
CreateWorkerRequest {
|
||||||
profile: ProfileSelector::RuntimeDefault,
|
profile: ProfileSelector::RuntimeDefault,
|
||||||
|
display_name: None,
|
||||||
profile_source: crate::catalog::ProfileSourceArchiveSource::Http {
|
profile_source: crate::catalog::ProfileSourceArchiveSource::Http {
|
||||||
location: crate::catalog::ProfileSourceArchiveHttpRef {
|
location: crate::catalog::ProfileSourceArchiveHttpRef {
|
||||||
url: "http://127.0.0.1/profile-source.tar".to_string(),
|
url: "http://127.0.0.1/profile-source.tar".to_string(),
|
||||||
|
|
|
||||||
|
|
@ -1860,6 +1860,7 @@ impl WorkerRecord {
|
||||||
.clone()
|
.clone()
|
||||||
.with_restore_dry_check(restore_dry_check),
|
.with_restore_dry_check(restore_dry_check),
|
||||||
profile: self.request.profile.clone(),
|
profile: self.request.profile.clone(),
|
||||||
|
display_name: self.request.display_name.clone(),
|
||||||
profile_source: self.request.profile_source.reference(),
|
profile_source: self.request.profile_source.reference(),
|
||||||
config_bundle: self.request.config_bundle.clone(),
|
config_bundle: self.request.config_bundle.clone(),
|
||||||
last_event_id: self.last_event_id,
|
last_event_id: self.last_event_id,
|
||||||
|
|
@ -1873,6 +1874,7 @@ impl WorkerRecord {
|
||||||
status: self.status,
|
status: self.status,
|
||||||
execution: self.execution.clone(),
|
execution: self.execution.clone(),
|
||||||
profile: self.request.profile.clone(),
|
profile: self.request.profile.clone(),
|
||||||
|
display_name: self.request.display_name.clone(),
|
||||||
profile_source: self.request.profile_source.reference(),
|
profile_source: self.request.profile_source.reference(),
|
||||||
config_bundle: self.request.config_bundle.clone(),
|
config_bundle: self.request.config_bundle.clone(),
|
||||||
last_event_id: self.last_event_id,
|
last_event_id: self.last_event_id,
|
||||||
|
|
@ -2008,6 +2010,7 @@ mod tests {
|
||||||
let bundle = test_bundle_for_profile(profile.clone());
|
let bundle = test_bundle_for_profile(profile.clone());
|
||||||
CreateWorkerRequest {
|
CreateWorkerRequest {
|
||||||
profile,
|
profile,
|
||||||
|
display_name: None,
|
||||||
profile_source: crate::catalog::ProfileSourceArchiveSource::Http {
|
profile_source: crate::catalog::ProfileSourceArchiveSource::Http {
|
||||||
location: crate::catalog::ProfileSourceArchiveHttpRef {
|
location: crate::catalog::ProfileSourceArchiveHttpRef {
|
||||||
url: "http://127.0.0.1/profile-source.tar".to_string(),
|
url: "http://127.0.0.1/profile-source.tar".to_string(),
|
||||||
|
|
|
||||||
|
|
@ -1559,6 +1559,7 @@ mod tests {
|
||||||
let bundle = test_bundle();
|
let bundle = test_bundle();
|
||||||
CreateWorkerRequest {
|
CreateWorkerRequest {
|
||||||
profile: ProfileSelector::RuntimeDefault,
|
profile: ProfileSelector::RuntimeDefault,
|
||||||
|
display_name: None,
|
||||||
profile_source: crate::catalog::ProfileSourceArchiveSource::Embedded {
|
profile_source: crate::catalog::ProfileSourceArchiveSource::Embedded {
|
||||||
archive: bundle.profile_source_archive.clone().unwrap(),
|
archive: bundle.profile_source_archive.clone().unwrap(),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -230,9 +230,15 @@ pub struct WorkerSummary {
|
||||||
pub runtime_id: String,
|
pub runtime_id: String,
|
||||||
pub worker_id: String,
|
pub worker_id: String,
|
||||||
pub host_id: String,
|
pub host_id: String,
|
||||||
|
/// Human-readable display name. This is not identity and may be duplicated.
|
||||||
|
pub display_name: String,
|
||||||
|
/// Backward-compatible display label. New UI should prefer `display_name`.
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub role: Option<String>,
|
pub role: Option<String>,
|
||||||
pub profile: Option<String>,
|
pub profile: Option<String>,
|
||||||
|
pub singleton_key: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tags: Vec<String>,
|
||||||
pub workspace: WorkerWorkspaceSummary,
|
pub workspace: WorkerWorkspaceSummary,
|
||||||
pub state: String,
|
pub state: String,
|
||||||
pub last_seen_at: Option<String>,
|
pub last_seen_at: Option<String>,
|
||||||
|
|
@ -1334,13 +1340,24 @@ impl EmbeddedWorkerRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_worker_summary(&self, summary: worker_runtime::catalog::WorkerSummary) -> WorkerSummary {
|
fn map_worker_summary(&self, summary: worker_runtime::catalog::WorkerSummary) -> WorkerSummary {
|
||||||
|
let worker_id = summary.worker_ref.worker_id.to_string();
|
||||||
|
let profile = embedded_profile_label(&summary.profile);
|
||||||
|
let display = worker_display_metadata(
|
||||||
|
&worker_id,
|
||||||
|
profile.as_deref(),
|
||||||
|
summary.display_name.as_deref(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
WorkerSummary {
|
WorkerSummary {
|
||||||
runtime_id: self.runtime_id.clone(),
|
runtime_id: self.runtime_id.clone(),
|
||||||
worker_id: summary.worker_ref.worker_id.to_string(),
|
worker_id,
|
||||||
host_id: self.host_id.clone(),
|
host_id: self.host_id.clone(),
|
||||||
label: safe_display_hint(&summary.worker_ref.worker_id.to_string()),
|
display_name: display.display_name.clone(),
|
||||||
role: embedded_profile_label(&summary.profile),
|
label: display.display_name,
|
||||||
profile: embedded_profile_label(&summary.profile),
|
role: profile.clone(),
|
||||||
|
profile,
|
||||||
|
singleton_key: display.singleton_key,
|
||||||
|
tags: display.tags,
|
||||||
workspace: WorkerWorkspaceSummary {
|
workspace: WorkerWorkspaceSummary {
|
||||||
visibility: "backend_internal".to_string(),
|
visibility: "backend_internal".to_string(),
|
||||||
identity: "runtime_registry_worker".to_string(),
|
identity: "runtime_registry_worker".to_string(),
|
||||||
|
|
@ -1368,13 +1385,24 @@ impl EmbeddedWorkerRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_worker_detail(&self, detail: EmbeddedWorkerDetail) -> WorkerSummary {
|
fn map_worker_detail(&self, detail: EmbeddedWorkerDetail) -> WorkerSummary {
|
||||||
|
let worker_id = detail.worker_id.to_string();
|
||||||
|
let profile = embedded_profile_label(&detail.profile);
|
||||||
|
let display = worker_display_metadata(
|
||||||
|
&worker_id,
|
||||||
|
profile.as_deref(),
|
||||||
|
detail.display_name.as_deref(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
WorkerSummary {
|
WorkerSummary {
|
||||||
runtime_id: self.runtime_id.clone(),
|
runtime_id: self.runtime_id.clone(),
|
||||||
worker_id: detail.worker_id.to_string(),
|
worker_id,
|
||||||
host_id: self.host_id.clone(),
|
host_id: self.host_id.clone(),
|
||||||
label: safe_display_hint(&detail.worker_id.to_string()),
|
display_name: display.display_name.clone(),
|
||||||
role: embedded_profile_label(&detail.profile),
|
label: display.display_name,
|
||||||
profile: embedded_profile_label(&detail.profile),
|
role: profile.clone(),
|
||||||
|
profile,
|
||||||
|
singleton_key: display.singleton_key,
|
||||||
|
tags: display.tags,
|
||||||
workspace: WorkerWorkspaceSummary {
|
workspace: WorkerWorkspaceSummary {
|
||||||
visibility: "backend_internal".to_string(),
|
visibility: "backend_internal".to_string(),
|
||||||
identity: "runtime_registry_worker".to_string(),
|
identity: "runtime_registry_worker".to_string(),
|
||||||
|
|
@ -1579,9 +1607,9 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime {
|
||||||
}
|
}
|
||||||
if request.requested_worker_name.is_some() {
|
if request.requested_worker_name.is_some() {
|
||||||
diagnostics.push(diagnostic(
|
diagnostics.push(diagnostic(
|
||||||
"embedded_worker_name_ignored",
|
"embedded_worker_name_display_only",
|
||||||
DiagnosticSeverity::Info,
|
DiagnosticSeverity::Info,
|
||||||
"Embedded Runtime v0 allocates opaque runtime-local worker ids; requested display names are not authority".to_string(),
|
"requested_worker_name is used only as display_name; embedded Runtime allocates opaque runtime-local worker ids".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if matches!(request.acceptance, WorkerSpawnAcceptanceRequirement::RunAccepted { expected_segments } if expected_segments > 0)
|
if matches!(request.acceptance, WorkerSpawnAcceptanceRequirement::RunAccepted { expected_segments } if expected_segments > 0)
|
||||||
|
|
@ -1615,6 +1643,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime {
|
||||||
};
|
};
|
||||||
let create_request = CreateWorkerRequest {
|
let create_request = CreateWorkerRequest {
|
||||||
profile,
|
profile,
|
||||||
|
display_name: request.requested_worker_name.clone(),
|
||||||
config_bundle: None,
|
config_bundle: None,
|
||||||
profile_source,
|
profile_source,
|
||||||
initial_input: request.initial_input.clone(),
|
initial_input: request.initial_input.clone(),
|
||||||
|
|
@ -2174,13 +2203,24 @@ impl RemoteWorkerRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_worker_summary(&self, summary: worker_runtime::catalog::WorkerSummary) -> WorkerSummary {
|
fn map_worker_summary(&self, summary: worker_runtime::catalog::WorkerSummary) -> WorkerSummary {
|
||||||
|
let worker_id = summary.worker_ref.worker_id.to_string();
|
||||||
|
let profile = embedded_profile_label(&summary.profile);
|
||||||
|
let display = worker_display_metadata(
|
||||||
|
&worker_id,
|
||||||
|
profile.as_deref(),
|
||||||
|
summary.display_name.as_deref(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
WorkerSummary {
|
WorkerSummary {
|
||||||
runtime_id: self.runtime_id.clone(),
|
runtime_id: self.runtime_id.clone(),
|
||||||
worker_id: summary.worker_ref.worker_id.to_string(),
|
worker_id,
|
||||||
host_id: self.host_id.clone(),
|
host_id: self.host_id.clone(),
|
||||||
label: safe_display_hint(&summary.worker_ref.worker_id.to_string()),
|
display_name: display.display_name.clone(),
|
||||||
role: None,
|
label: display.display_name,
|
||||||
profile: embedded_profile_label(&summary.profile),
|
role: profile.clone(),
|
||||||
|
profile,
|
||||||
|
singleton_key: display.singleton_key,
|
||||||
|
tags: display.tags,
|
||||||
workspace: WorkerWorkspaceSummary {
|
workspace: WorkerWorkspaceSummary {
|
||||||
visibility: "remote_runtime".to_string(),
|
visibility: "remote_runtime".to_string(),
|
||||||
identity: "runtime_registry_worker".to_string(),
|
identity: "runtime_registry_worker".to_string(),
|
||||||
|
|
@ -2208,13 +2248,24 @@ impl RemoteWorkerRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_worker_detail(&self, detail: EmbeddedWorkerDetail) -> WorkerSummary {
|
fn map_worker_detail(&self, detail: EmbeddedWorkerDetail) -> WorkerSummary {
|
||||||
|
let worker_id = detail.worker_id.to_string();
|
||||||
|
let profile = embedded_profile_label(&detail.profile);
|
||||||
|
let display = worker_display_metadata(
|
||||||
|
&worker_id,
|
||||||
|
profile.as_deref(),
|
||||||
|
detail.display_name.as_deref(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
WorkerSummary {
|
WorkerSummary {
|
||||||
runtime_id: self.runtime_id.clone(),
|
runtime_id: self.runtime_id.clone(),
|
||||||
worker_id: detail.worker_id.to_string(),
|
worker_id,
|
||||||
host_id: self.host_id.clone(),
|
host_id: self.host_id.clone(),
|
||||||
label: safe_display_hint(&detail.worker_id.to_string()),
|
display_name: display.display_name.clone(),
|
||||||
role: None,
|
label: display.display_name,
|
||||||
profile: embedded_profile_label(&detail.profile),
|
role: profile.clone(),
|
||||||
|
profile,
|
||||||
|
singleton_key: display.singleton_key,
|
||||||
|
tags: display.tags,
|
||||||
workspace: WorkerWorkspaceSummary {
|
workspace: WorkerWorkspaceSummary {
|
||||||
visibility: "remote_runtime".to_string(),
|
visibility: "remote_runtime".to_string(),
|
||||||
identity: "runtime_registry_worker".to_string(),
|
identity: "runtime_registry_worker".to_string(),
|
||||||
|
|
@ -2473,6 +2524,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime {
|
||||||
};
|
};
|
||||||
let create = CreateWorkerRequest {
|
let create = CreateWorkerRequest {
|
||||||
profile,
|
profile,
|
||||||
|
display_name: request.requested_worker_name.clone(),
|
||||||
config_bundle: None,
|
config_bundle: None,
|
||||||
profile_source,
|
profile_source,
|
||||||
initial_input: request.initial_input.clone(),
|
initial_input: request.initial_input.clone(),
|
||||||
|
|
@ -3000,10 +3052,83 @@ fn ticket_role_profile_slug(role: &TicketWorkerRole) -> &'static str {
|
||||||
fn embedded_profile_label(profile: &ProfileSelector) -> Option<String> {
|
fn embedded_profile_label(profile: &ProfileSelector) -> Option<String> {
|
||||||
Some(match profile {
|
Some(match profile {
|
||||||
ProfileSelector::RuntimeDefault => "runtime_default".to_string(),
|
ProfileSelector::RuntimeDefault => "runtime_default".to_string(),
|
||||||
ProfileSelector::Builtin(name) | ProfileSelector::Named(name) => safe_display_hint(name),
|
ProfileSelector::Builtin(name) | ProfileSelector::Named(name) => {
|
||||||
|
if name.strip_prefix("builtin:").unwrap_or(name) == MEMORY_CONSOLIDATION_PROFILE {
|
||||||
|
MEMORY_CONSOLIDATION_PROFILE.to_string()
|
||||||
|
} else {
|
||||||
|
safe_display_hint(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MEMORY_CONSOLIDATION_PROFILE: &str = "memory-consolidation";
|
||||||
|
const MEMORY_CONSOLIDATION_SINGLETON_KEY: &str = "workspace-memory-consolidation";
|
||||||
|
|
||||||
|
struct WorkerDisplayMetadata {
|
||||||
|
display_name: String,
|
||||||
|
singleton_key: Option<String>,
|
||||||
|
tags: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn worker_display_metadata(
|
||||||
|
worker_id: &str,
|
||||||
|
profile_label: Option<&str>,
|
||||||
|
requested_display_name: Option<&str>,
|
||||||
|
internal: bool,
|
||||||
|
) -> WorkerDisplayMetadata {
|
||||||
|
if profile_label == Some(MEMORY_CONSOLIDATION_PROFILE) {
|
||||||
|
let mut tags = vec![
|
||||||
|
"memory".to_string(),
|
||||||
|
"consolidation".to_string(),
|
||||||
|
"singleton".to_string(),
|
||||||
|
];
|
||||||
|
if internal {
|
||||||
|
tags.insert(0, "internal".to_string());
|
||||||
|
}
|
||||||
|
return WorkerDisplayMetadata {
|
||||||
|
display_name: "Memory Consolidation".to_string(),
|
||||||
|
singleton_key: Some(MEMORY_CONSOLIDATION_SINGLETON_KEY.to_string()),
|
||||||
|
tags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let display_name = requested_display_name
|
||||||
|
.filter(|value| !value.trim().is_empty())
|
||||||
|
.map(safe_display_hint)
|
||||||
|
.or_else(|| profile_label.map(profile_display_name))
|
||||||
|
.unwrap_or_else(|| format!("Worker {worker_id}"));
|
||||||
|
let mut tags = Vec::new();
|
||||||
|
if internal {
|
||||||
|
tags.push("internal".to_string());
|
||||||
|
}
|
||||||
|
if let Some(profile_label) = profile_label {
|
||||||
|
tags.push(format!("profile:{profile_label}"));
|
||||||
|
}
|
||||||
|
WorkerDisplayMetadata {
|
||||||
|
display_name,
|
||||||
|
singleton_key: None,
|
||||||
|
tags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn profile_display_name(profile_label: &str) -> String {
|
||||||
|
match profile_label {
|
||||||
|
"runtime_default" => "Default Worker".to_string(),
|
||||||
|
value => value
|
||||||
|
.split(['-', '_'])
|
||||||
|
.filter(|part| !part.is_empty())
|
||||||
|
.map(|part| {
|
||||||
|
let mut chars = part.chars();
|
||||||
|
match chars.next() {
|
||||||
|
Some(first) => first.to_uppercase().chain(chars).collect::<String>(),
|
||||||
|
None => String::new(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" "),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn embedded_input_rejected(
|
fn embedded_input_rejected(
|
||||||
runtime_id: &str,
|
runtime_id: &str,
|
||||||
worker_id: &str,
|
worker_id: &str,
|
||||||
|
|
@ -3389,9 +3514,12 @@ pub fn placeholder_worker(host_id: impl Into<String>) -> WorkerSummary {
|
||||||
runtime_id: "placeholder".to_string(),
|
runtime_id: "placeholder".to_string(),
|
||||||
worker_id: "worker-placeholder".to_string(),
|
worker_id: "worker-placeholder".to_string(),
|
||||||
host_id,
|
host_id,
|
||||||
|
display_name: "Worker runtime actions are not implemented".to_string(),
|
||||||
label: "Worker runtime actions are not implemented".to_string(),
|
label: "Worker runtime actions are not implemented".to_string(),
|
||||||
role: None,
|
role: None,
|
||||||
profile: None,
|
profile: None,
|
||||||
|
singleton_key: None,
|
||||||
|
tags: Vec::new(),
|
||||||
workspace: WorkerWorkspaceSummary {
|
workspace: WorkerWorkspaceSummary {
|
||||||
visibility: "none".to_string(),
|
visibility: "none".to_string(),
|
||||||
identity: "unsupported".to_string(),
|
identity: "unsupported".to_string(),
|
||||||
|
|
@ -3720,9 +3848,12 @@ mod tests {
|
||||||
runtime_id: runtime_id.to_string(),
|
runtime_id: runtime_id.to_string(),
|
||||||
worker_id: worker_id.to_string(),
|
worker_id: worker_id.to_string(),
|
||||||
host_id: host_id.to_string(),
|
host_id: host_id.to_string(),
|
||||||
|
display_name: label.to_string(),
|
||||||
label: label.to_string(),
|
label: label.to_string(),
|
||||||
role: None,
|
role: None,
|
||||||
profile: None,
|
profile: None,
|
||||||
|
singleton_key: None,
|
||||||
|
tags: Vec::new(),
|
||||||
workspace: WorkerWorkspaceSummary {
|
workspace: WorkerWorkspaceSummary {
|
||||||
visibility: "opaque".to_string(),
|
visibility: "opaque".to_string(),
|
||||||
identity: host_id.to_string(),
|
identity: host_id.to_string(),
|
||||||
|
|
|
||||||
|
|
@ -1514,6 +1514,7 @@ async fn scoped_memory_backend_operation(
|
||||||
}
|
}
|
||||||
|
|
||||||
const MEMORY_CONSOLIDATION_PROFILE: &str = "memory-consolidation";
|
const MEMORY_CONSOLIDATION_PROFILE: &str = "memory-consolidation";
|
||||||
|
const MEMORY_CONSOLIDATION_SINGLETON_KEY: &str = "workspace-memory-consolidation";
|
||||||
const MEMORY_CONSOLIDATION_WORKER_SCAN_LIMIT: usize = 100;
|
const MEMORY_CONSOLIDATION_WORKER_SCAN_LIMIT: usize = 100;
|
||||||
|
|
||||||
async fn scoped_memory_consolidation(
|
async fn scoped_memory_consolidation(
|
||||||
|
|
@ -1699,7 +1700,8 @@ fn try_reuse_memory_consolidation_worker(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_memory_consolidation_worker(worker: &WorkerSummary) -> bool {
|
fn is_memory_consolidation_worker(worker: &WorkerSummary) -> bool {
|
||||||
[worker.profile.as_deref(), worker.role.as_deref()]
|
worker.singleton_key.as_deref() == Some(MEMORY_CONSOLIDATION_SINGLETON_KEY)
|
||||||
|
|| [worker.profile.as_deref(), worker.role.as_deref()]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.any(|value| value == MEMORY_CONSOLIDATION_PROFILE)
|
.any(|value| value == MEMORY_CONSOLIDATION_PROFILE)
|
||||||
|
|
@ -5723,7 +5725,10 @@ fn worker_summary_from_registry(record: &WorkerRegistryRecord) -> WorkerSummary
|
||||||
runtime_id: record.runtime_id.clone(),
|
runtime_id: record.runtime_id.clone(),
|
||||||
host_id: "backend-registry".to_string(),
|
host_id: "backend-registry".to_string(),
|
||||||
role: None,
|
role: None,
|
||||||
|
display_name: record.display_name.clone(),
|
||||||
label: record.display_name.clone(),
|
label: record.display_name.clone(),
|
||||||
|
singleton_key: None,
|
||||||
|
tags: Vec::new(),
|
||||||
state: "missing".to_string(),
|
state: "missing".to_string(),
|
||||||
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",
|
||||||
|
|
@ -7395,6 +7400,13 @@ mod tests {
|
||||||
.worker(EMBEDDED_WORKER_RUNTIME_ID, &worker_id)
|
.worker(EMBEDDED_WORKER_RUNTIME_ID, &worker_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(worker.state, "idle");
|
assert_eq!(worker.state, "idle");
|
||||||
|
assert_eq!(worker.display_name, "Memory Consolidation");
|
||||||
|
assert_eq!(
|
||||||
|
worker.singleton_key.as_deref(),
|
||||||
|
Some(MEMORY_CONSOLIDATION_SINGLETON_KEY)
|
||||||
|
);
|
||||||
|
assert!(worker.tags.iter().any(|tag| tag == "memory"));
|
||||||
|
assert!(worker.tags.iter().any(|tag| tag == "consolidation"));
|
||||||
|
|
||||||
let second = match start_memory_staging_consolidation(
|
let second = match start_memory_staging_consolidation(
|
||||||
api.clone(),
|
api.clone(),
|
||||||
|
|
@ -7942,6 +7954,7 @@ mod tests {
|
||||||
let bundle = runtime_test_bundle();
|
let bundle = runtime_test_bundle();
|
||||||
worker_runtime::catalog::CreateWorkerRequest {
|
worker_runtime::catalog::CreateWorkerRequest {
|
||||||
profile: worker_runtime::catalog::ProfileSelector::RuntimeDefault,
|
profile: worker_runtime::catalog::ProfileSelector::RuntimeDefault,
|
||||||
|
display_name: None,
|
||||||
profile_source: worker_runtime::catalog::ProfileSourceArchiveSource::Http {
|
profile_source: worker_runtime::catalog::ProfileSourceArchiveSource::Http {
|
||||||
location: worker_runtime::catalog::ProfileSourceArchiveHttpRef {
|
location: worker_runtime::catalog::ProfileSourceArchiveHttpRef {
|
||||||
url: "http://127.0.0.1/profile-source.tar".to_string(),
|
url: "http://127.0.0.1/profile-source.tar".to_string(),
|
||||||
|
|
|
||||||
|
|
@ -114,13 +114,16 @@ Deno.test("workspace Worker list lives on the dedicated Workers page", async ()
|
||||||
assert(
|
assert(
|
||||||
workersPage.includes("workerConsoleHref(worker, data.workspaceId)") &&
|
workersPage.includes("workerConsoleHref(worker, data.workspaceId)") &&
|
||||||
workersPage.includes('<table class="workers-table">') &&
|
workersPage.includes('<table class="workers-table">') &&
|
||||||
workersPage.includes('class="icon-action"') &&
|
workersPage.includes("workerDisplayName = worker.display_name || worker.label") &&
|
||||||
workersPage.includes("Delete ${worker.label}"),
|
workersPage.includes("worker <code>{worker.worker_id}</code>") &&
|
||||||
|
workersPage.includes("Delete ${workerDisplayName}"),
|
||||||
"dedicated Workers page should expose a table, console link target, and icon actions per Worker",
|
"dedicated Workers page should expose a table, console link target, and icon actions per Worker",
|
||||||
);
|
);
|
||||||
assert(
|
assert(
|
||||||
workersNav.includes("href={`/w/${workspaceId}/workers`}") &&
|
workersNav.includes("href={`/w/${workspaceId}/workers`}") &&
|
||||||
workersNav.includes("filter(canShowWorkerInSidebar)") &&
|
workersNav.includes("filter(canShowWorkerInSidebar)") &&
|
||||||
|
workersNav.includes("worker.display_name || worker.label") &&
|
||||||
|
workersNav.includes("worker {worker.worker_id}") &&
|
||||||
!workersNav.includes('aria-disabled="true"'),
|
!workersNav.includes('aria-disabled="true"'),
|
||||||
"Workers sidebar should link to the Worker list page and omit registry-only Workers",
|
"Workers sidebar should link to the Worker list page and omit registry-only Workers",
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -105,11 +105,11 @@
|
||||||
<li>
|
<li>
|
||||||
<a href={href} class="nav-item worker-nav-item" class:active={currentPath === href} aria-current={currentPath === href ? 'page' : undefined}>
|
<a href={href} class="nav-item worker-nav-item" class:active={currentPath === href} aria-current={currentPath === href ? 'page' : undefined}>
|
||||||
<span class="worker-title-row">
|
<span class="worker-title-row">
|
||||||
<span class="item-title">{worker.label}</span>
|
<span class="item-title">{worker.display_name || worker.label}</span>
|
||||||
<span class="worker-task-title">-</span>
|
<span class="worker-task-title">-</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="item-meta">
|
<span class="item-meta">
|
||||||
{worker.role ? `${worker.role} · ` : ''}{worker.state} · 🖥 {worker.host_id}
|
worker {worker.worker_id} · {worker.role ? `${worker.role} · ` : ''}{worker.state} · 🖥 {worker.host_id}
|
||||||
{worker.working_directory ? ` · wd:${worker.working_directory.repository_id}@${worker.working_directory.resolved_commit.slice(0, 8)}` : ''}
|
{worker.working_directory ? ` · wd:${worker.working_directory.repository_id}@${worker.working_directory.resolved_commit.slice(0, 8)}` : ''}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -77,9 +77,12 @@ export type Worker = {
|
||||||
runtime_id: string;
|
runtime_id: string;
|
||||||
worker_id: string;
|
worker_id: string;
|
||||||
host_id: string;
|
host_id: string;
|
||||||
|
display_name: string;
|
||||||
label: string;
|
label: string;
|
||||||
role?: string | null;
|
role?: string | null;
|
||||||
profile?: string | null;
|
profile?: string | null;
|
||||||
|
singleton_key?: string | null;
|
||||||
|
tags: string[];
|
||||||
workspace: { visibility: string; identity: string };
|
workspace: { visibility: string; identity: string };
|
||||||
state: string;
|
state: string;
|
||||||
pinned?: boolean;
|
pinned?: boolean;
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,12 @@ function worker(overrides: Partial<Worker>): Worker {
|
||||||
runtime_id: "arc",
|
runtime_id: "arc",
|
||||||
worker_id: "1",
|
worker_id: "1",
|
||||||
host_id: "host",
|
host_id: "host",
|
||||||
label: "worker-1",
|
display_name: "Worker 1",
|
||||||
|
label: "Worker 1",
|
||||||
role: null,
|
role: null,
|
||||||
profile: null,
|
profile: null,
|
||||||
|
singleton_key: null,
|
||||||
|
tags: [],
|
||||||
workspace: { visibility: "workspace", identity: "workspace" },
|
workspace: { visibility: "workspace", identity: "workspace" },
|
||||||
state: "running",
|
state: "running",
|
||||||
pinned: false,
|
pinned: false,
|
||||||
|
|
|
||||||
|
|
@ -190,14 +190,15 @@
|
||||||
{@const cleanup = cleanupCandidate(worker)}
|
{@const cleanup = cleanupCandidate(worker)}
|
||||||
{@const canDelete = cleanup && !cleanup.blocking_reason}
|
{@const canDelete = cleanup && !cleanup.blocking_reason}
|
||||||
{@const anyActionDisabled = actionsDisabled()}
|
{@const anyActionDisabled = actionsDisabled()}
|
||||||
|
{@const workerDisplayName = worker.display_name || worker.label}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{#if canOpenWorkerConsole(worker)}
|
{#if canOpenWorkerConsole(worker)}
|
||||||
<a class="worker-title-link" href={workerConsoleHref(worker, data.workspaceId)}><strong>{worker.label}</strong></a>
|
<a class="worker-title-link" href={workerConsoleHref(worker, data.workspaceId)}><strong>{workerDisplayName}</strong></a>
|
||||||
{:else}
|
{:else}
|
||||||
<strong>{worker.label}</strong>
|
<strong>{workerDisplayName}</strong>
|
||||||
{/if}
|
{/if}
|
||||||
<small><code>{worker.worker_id}</code></small>
|
<small>worker <code>{worker.worker_id}</code></small>
|
||||||
</td>
|
</td>
|
||||||
<td><code>{worker.runtime_id}</code></td>
|
<td><code>{worker.runtime_id}</code></td>
|
||||||
<td>{workerProfile(worker)}</td>
|
<td>{workerProfile(worker)}</td>
|
||||||
|
|
@ -205,12 +206,12 @@
|
||||||
<td><span class="pill {worker.pinned ? 'success' : 'muted'}">{worker.retention_state ?? 'normal'}</span></td>
|
<td><span class="pill {worker.pinned ? 'success' : 'muted'}">{worker.retention_state ?? 'normal'}</span></td>
|
||||||
<td>{workerDirectory(worker)}</td>
|
<td>{workerDirectory(worker)}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="worker-actions" aria-label={`Actions for ${worker.label}`}>
|
<div class="worker-actions" aria-label={`Actions for ${workerDisplayName}`}>
|
||||||
<button
|
<button
|
||||||
class="icon-action"
|
class="icon-action"
|
||||||
type="button"
|
type="button"
|
||||||
disabled={anyActionDisabled}
|
disabled={anyActionDisabled}
|
||||||
aria-label={worker.pinned ? `Unpin ${worker.label}` : `Pin ${worker.label}`}
|
aria-label={worker.pinned ? `Unpin ${workerDisplayName}` : `Pin ${workerDisplayName}`}
|
||||||
title={worker.pinned ? 'Unpin' : 'Pin'}
|
title={worker.pinned ? 'Unpin' : 'Pin'}
|
||||||
onclick={() => setPinned(worker, !worker.pinned)}
|
onclick={() => setPinned(worker, !worker.pinned)}
|
||||||
>
|
>
|
||||||
|
|
@ -227,7 +228,7 @@
|
||||||
class="icon-action danger"
|
class="icon-action danger"
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!canDelete || anyActionDisabled}
|
disabled={!canDelete || anyActionDisabled}
|
||||||
aria-label={`Delete ${worker.label}`}
|
aria-label={`Delete ${workerDisplayName}`}
|
||||||
title={cleanup.blocking_reason ?? cleanup.reason}
|
title={cleanup.blocking_reason ?? cleanup.reason}
|
||||||
onclick={() => deleteWorker(worker, cleanup)}
|
onclick={() => deleteWorker(worker, cleanup)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user