cli: route worker lists through targets
This commit is contained in:
@@ -3,7 +3,8 @@ use std::io;
|
||||
use std::time::Duration;
|
||||
|
||||
use client::{
|
||||
BackendRuntimeListTarget, BackendRuntimeTarget, BackendWorkerSummary, list_backend_workers,
|
||||
BackendRuntimeListTarget, BackendRuntimeTarget, BackendWorkerSummary,
|
||||
list_backend_stopped_workers, list_backend_workers, restore_backend_worker,
|
||||
};
|
||||
use crossterm::event::{self, Event as TermEvent, KeyCode, KeyEventKind, KeyModifiers};
|
||||
use ratatui::backend::CrosstermBackend;
|
||||
@@ -18,13 +19,30 @@ use crate::console;
|
||||
const MAX_ROWS: usize = 10;
|
||||
const VIEWPORT_LINES: u16 = MAX_ROWS as u16 + 4;
|
||||
|
||||
pub(crate) async fn run(target: BackendRuntimeListTarget) -> Result<(), Box<dyn Error>> {
|
||||
let response = list_backend_workers(&target).await.map_err(|error| {
|
||||
pub(crate) async fn run(
|
||||
target: BackendRuntimeListTarget,
|
||||
include_stopped: bool,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut response = list_backend_workers(&target).await.map_err(|error| {
|
||||
io::Error::other(format!(
|
||||
"failed to list Backend runtime workers from {}: {error}",
|
||||
target.base_url
|
||||
))
|
||||
})?;
|
||||
if include_stopped {
|
||||
match list_backend_stopped_workers(&target).await {
|
||||
Ok(stopped) => {
|
||||
response.items.extend(stopped.items);
|
||||
response.diagnostics.extend(stopped.diagnostics);
|
||||
}
|
||||
Err(error) => response.diagnostics.push(client::BackendDiagnostic {
|
||||
code: "backend_stopped_workers_list_failed".to_string(),
|
||||
severity: Some("error".to_string()),
|
||||
message: error.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
dedup_workers(&mut response.items);
|
||||
if response.items.is_empty() {
|
||||
let diagnostics = response
|
||||
.diagnostics
|
||||
@@ -44,11 +62,36 @@ pub(crate) async fn run(target: BackendRuntimeListTarget) -> Result<(), Box<dyn
|
||||
}
|
||||
|
||||
let selected = pick_worker(target.clone(), response.items)?;
|
||||
let worker = if selected.state == "stopped" {
|
||||
let restore_target = BackendRuntimeTarget::new(
|
||||
target.base_url.clone(),
|
||||
selected.runtime_id.clone(),
|
||||
selected.worker_id.clone(),
|
||||
);
|
||||
restore_backend_worker(&restore_target)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
io::Error::other(format!(
|
||||
"failed to restore Backend worker {}/{}: {error}",
|
||||
selected.runtime_id, selected.worker_id
|
||||
))
|
||||
})?
|
||||
.result
|
||||
.worker
|
||||
.unwrap_or(selected)
|
||||
} else {
|
||||
selected
|
||||
};
|
||||
let attach_target =
|
||||
BackendRuntimeTarget::new(target.base_url, selected.runtime_id, selected.worker_id);
|
||||
BackendRuntimeTarget::new(target.base_url, worker.runtime_id, worker.worker_id);
|
||||
console::run_backend_runtime(attach_target).await
|
||||
}
|
||||
|
||||
fn dedup_workers(workers: &mut Vec<BackendWorkerSummary>) {
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
workers.retain(|worker| seen.insert((worker.runtime_id.clone(), worker.worker_id.clone())));
|
||||
}
|
||||
|
||||
fn pick_worker(
|
||||
target: BackendRuntimeListTarget,
|
||||
mut workers: Vec<BackendWorkerSummary>,
|
||||
|
||||
@@ -331,6 +331,15 @@ pub(crate) async fn run_resume(
|
||||
runtime_command: WorkerRuntimeCommand,
|
||||
workspace_root: PathBuf,
|
||||
all: bool,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
run_worker_picker(runtime_command, workspace_root, all, true).await
|
||||
}
|
||||
|
||||
pub(crate) async fn run_worker_picker(
|
||||
runtime_command: WorkerRuntimeCommand,
|
||||
workspace_root: PathBuf,
|
||||
all: bool,
|
||||
include_stopped: bool,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Pick a Worker in its own inline viewport, dropping the viewport before
|
||||
// attaching/restoring so each phase gets fresh vertical room.
|
||||
@@ -338,7 +347,8 @@ pub(crate) async fn run_resume(
|
||||
picker::PickerOptions::all()
|
||||
} else {
|
||||
picker::PickerOptions::workspace(workspace_root)
|
||||
};
|
||||
}
|
||||
.with_stopped(include_stopped);
|
||||
let (worker_name, socket_override) = match picker::run(picker_options).await? {
|
||||
PickerOutcome::Picked {
|
||||
worker_name,
|
||||
|
||||
+34
-8
@@ -58,7 +58,11 @@ pub enum LaunchMode {
|
||||
},
|
||||
/// `yoi workers` / `yoi --backend <url>`: list workers through the selected
|
||||
/// connection target, then attach to the selected Worker.
|
||||
Workers { runtime_id: Option<String> },
|
||||
Workers {
|
||||
runtime_id: Option<String>,
|
||||
include_stopped: bool,
|
||||
all: bool,
|
||||
},
|
||||
/// `yoi --backend <url> --runtime-id <id> --worker-id <id>`: open one Worker
|
||||
/// through the selected connection target.
|
||||
OpenWorker {
|
||||
@@ -76,7 +80,7 @@ pub enum LaunchMode {
|
||||
worker_name: Option<String>,
|
||||
},
|
||||
/// `yoi panel`: open the workspace Dashboard from the current workspace.
|
||||
Panel,
|
||||
Panel { include_stopped: bool },
|
||||
}
|
||||
|
||||
pub async fn launch(options: LaunchOptions) -> ExitCode {
|
||||
@@ -128,12 +132,34 @@ pub async fn launch(options: LaunchOptions) -> ExitCode {
|
||||
}
|
||||
Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
|
||||
},
|
||||
LaunchMode::Workers { runtime_id } => {
|
||||
match target.list_workers(WorkerListRequest::new(runtime_id)) {
|
||||
Ok(worker_list) => backend_worker_picker::run(worker_list.target).await,
|
||||
Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
|
||||
LaunchMode::Workers {
|
||||
runtime_id,
|
||||
include_stopped,
|
||||
all,
|
||||
} => match target.list_workers(if include_stopped {
|
||||
WorkerListRequest::with_stopped(runtime_id)
|
||||
} else {
|
||||
WorkerListRequest::new(runtime_id)
|
||||
}) {
|
||||
Ok(worker_list) => {
|
||||
if let Some(target) = worker_list.backend_target {
|
||||
backend_worker_picker::run(target, worker_list.include_stopped).await
|
||||
} else if let Some(runtime_command) = worker_list.local_runtime_command {
|
||||
console::run_worker_picker(
|
||||
runtime_command,
|
||||
workspace_root.clone(),
|
||||
all,
|
||||
worker_list.include_stopped,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
Err(Box::new(io::Error::other(
|
||||
"worker list target did not include a local or backend source",
|
||||
)) as Box<dyn std::error::Error>)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
|
||||
},
|
||||
LaunchMode::OpenWorker {
|
||||
runtime_id,
|
||||
worker_id,
|
||||
@@ -153,7 +179,7 @@ pub async fn launch(options: LaunchOptions) -> ExitCode {
|
||||
}
|
||||
Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
|
||||
},
|
||||
LaunchMode::Panel => match target.dashboard() {
|
||||
LaunchMode::Panel { .. } => match target.dashboard() {
|
||||
Ok(dashboard) => dashboard::launch(dashboard.runtime_command).await,
|
||||
Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
|
||||
},
|
||||
|
||||
@@ -80,20 +80,28 @@ pub enum PickerOutcome {
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct PickerOptions {
|
||||
scope: PickerScope,
|
||||
include_stopped: bool,
|
||||
}
|
||||
|
||||
impl PickerOptions {
|
||||
pub(crate) fn workspace(workspace_root: PathBuf) -> Self {
|
||||
Self {
|
||||
scope: PickerScope::Workspace(workspace_root),
|
||||
include_stopped: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn all() -> Self {
|
||||
Self {
|
||||
scope: PickerScope::All,
|
||||
include_stopped: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_stopped(mut self, include_stopped: bool) -> Self {
|
||||
self.include_stopped = include_stopped;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -134,6 +142,11 @@ fn list_for_options(
|
||||
stored_workers: Vec<StoredWorkerInfo>,
|
||||
live_workers: Vec<LiveWorkerInfo>,
|
||||
) -> WorkerList {
|
||||
let stored_workers = if options.include_stopped {
|
||||
stored_workers
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
match &options.scope {
|
||||
PickerScope::Workspace(workspace_root) => WorkerList::from_workspace_sources(
|
||||
WorkerVisibilitySource::ResumePicker,
|
||||
|
||||
Reference in New Issue
Block a user