fix cmd
This commit is contained in:
@@ -63,6 +63,8 @@ pub struct AgentConfig {
|
|||||||
pub host: Option<String>,
|
pub host: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub protocol: Option<String>,
|
pub protocol: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub depends_on: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_cwd() -> String {
|
fn default_cwd() -> String {
|
||||||
@@ -128,7 +130,7 @@ fn load_file_full(path: &Path) -> LoadedConfig {
|
|||||||
}
|
}
|
||||||
if let (Some(name), Some(task), Some(cwd)) = (multi.name, multi.task, multi.cwd) {
|
if let (Some(name), Some(task), Some(cwd)) = (multi.name, multi.task, multi.cwd) {
|
||||||
return LoadedConfig {
|
return LoadedConfig {
|
||||||
agents: vec![AgentConfig { name, task, cwd, host: None, protocol: None }],
|
agents: vec![AgentConfig { name, task, cwd, host: None, protocol: None, depends_on: vec![] }],
|
||||||
interval: multi.interval,
|
interval: multi.interval,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pub fn run(config_or_task: &str, cwd_override: Option<&str>, name_override: Opti
|
|||||||
.map(|p| p.display().to_string())
|
.map(|p| p.display().to_string())
|
||||||
.unwrap_or_else(|_| ".".to_string()));
|
.unwrap_or_else(|_| ".".to_string()));
|
||||||
let name = name_override.unwrap_or("task").to_string();
|
let name = name_override.unwrap_or("task").to_string();
|
||||||
(vec![config::AgentConfig { name, task: config_or_task.to_string(), cwd, host: None, protocol: None }], None)
|
(vec![config::AgentConfig { name, task: config_or_task.to_string(), cwd, host: None, protocol: None, depends_on: vec![] }], None)
|
||||||
};
|
};
|
||||||
|
|
||||||
if configs.is_empty() {
|
if configs.is_empty() {
|
||||||
@@ -355,28 +355,63 @@ fn strip_dir_listing(text: &str) -> &str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_and_wait(configs: &[config::AgentConfig], running: &Arc<AtomicBool>) -> Vec<Agent> {
|
fn spawn_and_wait(configs: &[config::AgentConfig], running: &Arc<AtomicBool>) -> Vec<Agent> {
|
||||||
let mut agents = Vec::new();
|
let mut agents: Vec<Agent> = Vec::new();
|
||||||
|
let mut pending: Vec<&config::AgentConfig> = Vec::new();
|
||||||
let mut next_id = 1;
|
let mut next_id = 1;
|
||||||
|
|
||||||
|
// Spawn agents without dependencies immediately, defer the rest
|
||||||
for cfg in configs {
|
for cfg in configs {
|
||||||
let cwd = expand_tilde(&cfg.cwd);
|
if cfg.depends_on.is_empty() {
|
||||||
match Agent::spawn_with_config(next_id, &cfg.name, &cfg.task, &cwd, cfg.host.as_deref(), cfg.protocol.as_deref()) {
|
let cwd = expand_tilde(&cfg.cwd);
|
||||||
Ok(agent) => {
|
match Agent::spawn_with_config(next_id, &cfg.name, &cfg.task, &cwd, cfg.host.as_deref(), cfg.protocol.as_deref()) {
|
||||||
eprintln!(" started: {} ({})", cfg.name, cwd);
|
Ok(agent) => {
|
||||||
agents.push(agent);
|
eprintln!(" started: {} ({})", cfg.name, cwd);
|
||||||
next_id += 1;
|
agents.push(agent);
|
||||||
|
next_id += 1;
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!(" failed: {}: {e}", cfg.name),
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!(" failed: {}: {e}", cfg.name),
|
} else {
|
||||||
|
eprintln!(" waiting: {} (depends on: {})", cfg.name, cfg.depends_on.join(", "));
|
||||||
|
pending.push(cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut last_status: Vec<String> = Vec::new();
|
let mut last_status: Vec<String> = Vec::new();
|
||||||
while running.load(Ordering::Relaxed) {
|
while running.load(Ordering::Relaxed) {
|
||||||
|
// Poll running agents
|
||||||
let mut any_running = false;
|
let mut any_running = false;
|
||||||
for agent in &mut agents {
|
for agent in &mut agents {
|
||||||
agent.poll();
|
agent.poll();
|
||||||
if agent.is_running() { any_running = true; }
|
if agent.is_running() { any_running = true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if any pending agents can now start
|
||||||
|
let mut newly_started = Vec::new();
|
||||||
|
pending.retain(|cfg| {
|
||||||
|
let deps_met = cfg.depends_on.iter().all(|dep| {
|
||||||
|
agents.iter().any(|a| &a.name == dep && !a.is_running())
|
||||||
|
});
|
||||||
|
if deps_met {
|
||||||
|
let cwd = expand_tilde(&cfg.cwd);
|
||||||
|
match Agent::spawn_with_config(next_id, &cfg.name, &cfg.task, &cwd, cfg.host.as_deref(), cfg.protocol.as_deref()) {
|
||||||
|
Ok(agent) => {
|
||||||
|
eprintln!(" started: {} ({})", cfg.name, cwd);
|
||||||
|
newly_started.push(agent);
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!(" failed: {}: {e}", cfg.name),
|
||||||
|
}
|
||||||
|
false // remove from pending
|
||||||
|
} else {
|
||||||
|
true // keep waiting
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for a in newly_started {
|
||||||
|
next_id += 1;
|
||||||
|
any_running = true;
|
||||||
|
agents.push(a);
|
||||||
|
}
|
||||||
|
|
||||||
if agents.iter().any(|a| a.dirty) {
|
if agents.iter().any(|a| a.dirty) {
|
||||||
write_state(&agents);
|
write_state(&agents);
|
||||||
for a in &mut agents { a.dirty = false; }
|
for a in &mut agents { a.dirty = false; }
|
||||||
@@ -393,7 +428,7 @@ fn spawn_and_wait(configs: &[config::AgentConfig], running: &Arc<AtomicBool>) ->
|
|||||||
last_status = current;
|
last_status = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !any_running { break; }
|
if !any_running && pending.is_empty() { break; }
|
||||||
std::thread::sleep(Duration::from_millis(200));
|
std::thread::sleep(Duration::from_millis(200));
|
||||||
}
|
}
|
||||||
agents
|
agents
|
||||||
@@ -426,7 +461,7 @@ fn extract_agent_configs(text: &str) -> Vec<config::AgentConfig> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if name.is_empty() || task.is_empty() { return None; }
|
if name.is_empty() || task.is_empty() { return None; }
|
||||||
Some(config::AgentConfig { name, task, cwd, host: None, protocol: None })
|
Some(config::AgentConfig { name, task, cwd, host: None, protocol: None, depends_on: vec![] })
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -749,6 +784,7 @@ pub fn commit() {
|
|||||||
cwd,
|
cwd,
|
||||||
host: None,
|
host: None,
|
||||||
protocol: None,
|
protocol: None,
|
||||||
|
depends_on: vec![],
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let running = Arc::new(AtomicBool::new(true));
|
let running = Arc::new(AtomicBool::new(true));
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ fn main() {
|
|||||||
println!("{}", env!("CARGO_PKG_VERSION"));
|
println!("{}", env!("CARGO_PKG_VERSION"));
|
||||||
}
|
}
|
||||||
Some("help" | "--help" | "-h") => print_help(),
|
Some("help" | "--help" | "-h") => print_help(),
|
||||||
None => print_help(),
|
None | Some("tui") => {
|
||||||
Some("tui") => {
|
|
||||||
// Show logo before entering alternate screen
|
// Show logo before entering alternate screen
|
||||||
eprintln!("\x1b[38;5;226m{}\x1b[0m\n\x1b[1m aishell\x1b[0m v{}\n",
|
eprintln!("\x1b[38;5;226m{}\x1b[0m\n\x1b[1m aishell\x1b[0m v{}\n",
|
||||||
LOGO, env!("CARGO_PKG_VERSION"));
|
LOGO, env!("CARGO_PKG_VERSION"));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ssh": "[protocol: ssh]\n[host: {host}]\nExecute commands via: ssh {host} \"command\"",
|
"ssh": "[protocol: ssh]\n[host: {host}]\nThe host value is an SSH config name or user@host. Execute commands via: ssh {host} \"command\"\nDo not specify port or identity file — they are configured in ~/.ssh/config.\nIf the command fails, report the error. Do not retry with different connection parameters.",
|
||||||
"https": "[protocol: https]\n[endpoint: {host}]\nAccess via HTTP/HTTPS API calls.",
|
"https": "[protocol: https]\n[endpoint: {host}]\nAccess via HTTP/HTTPS API calls. Use curl or equivalent.",
|
||||||
"at": "[protocol: at]\n[did: {host}]\nAccess via AT Protocol. Use ailog or atproto API.",
|
"at": "[protocol: at]\n[did: {host}]\nAccess via AT Protocol. Use ailog or atproto API.",
|
||||||
"git": "[protocol: git]\n[remote: {host}]\nAccess via git commands."
|
"git": "[protocol: git]\n[remote: {host}]\nAccess via git commands (clone, pull, push, log, etc)."
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user