use anyhow::Result;
use clap::{Parser, Subcommand};
use piw::{server, ui};
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "piw", version, about)]
struct Cli {
path: Option<PathBuf>,
#[arg(long, value_name = "URL", conflicts_with = "path")]
connect: Option<String>,
#[arg(long, value_name = "NAME")]
theme: Option<String>,
#[arg(long)]
list_themes: bool,
#[command(subcommand)]
command: Option<Command>,
}
#[derive(Subcommand)]
enum Command {
Serve {
#[arg(long, value_name = "DIR")]
runs_dir: Option<PathBuf>,
#[arg(long, default_value = "127.0.0.1:9377")]
bind: String,
},
}
fn default_runs_dir() -> PathBuf {
if let Ok(dir) = std::env::var("PI_WORKFLOWS_RUNS_DIR") {
return PathBuf::from(dir);
}
std::env::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".pi")
.join("agent")
.join("workflows")
.join("runs")
}
fn main() -> Result<()> {
let cli = Cli::parse();
if cli.list_themes {
for name in piw::theme::THEME_NAMES {
println!("{name}");
}
return Ok(());
}
let cli_theme = cli.theme.clone();
match cli.command {
Some(Command::Serve { runs_dir, bind }) => {
let runs_dir = runs_dir.unwrap_or_else(default_runs_dir);
anyhow::ensure!(
runs_dir.is_dir(),
"runs directory {} does not exist",
runs_dir.display()
);
let runtime = tokio::runtime::Runtime::new()?;
runtime.block_on(server::serve(server::ServeOptions { runs_dir, bind }))
}
None => {
if let Some(url) = cli.connect {
return ui::run_remote(&url, cli_theme.as_deref());
}
let path = match cli.path {
Some(path) if path.is_dir() => path,
Some(path) => {
let candidate = default_runs_dir().join(&path);
anyhow::ensure!(
candidate.is_dir(),
"{} is neither a directory nor a run id under {}",
path.display(),
default_runs_dir().display()
);
candidate
}
None => {
let dir = default_runs_dir();
anyhow::ensure!(dir.is_dir(), "{} does not exist", dir.display());
dir
}
};
if path.join("manifest.json").is_file() {
ui::run_single(&path, cli_theme.as_deref())
} else {
ui::run_local(&path, cli_theme.as_deref())
}
}
}
}