use anyhow::Result;
use clap::{Parser, Subcommand};
mod audio;
mod client;
mod commands;
mod config;
mod progress;
mod style;
mod telemetry;
const VERSION: &str = concat!(
env!("CARGO_PKG_VERSION"),
"\n",
env!("CARGO_PKG_NAME"),
" — ",
env!("CARGO_PKG_DESCRIPTION"),
"\n",
env!("CARGO_PKG_REPOSITORY"),
);
const HELP_TEMPLATE: &str = "\
{about-with-newline}
{usage-heading} {usage}
Account:
login Authenticate against a WaveKat platform instance
logout Forget stored credentials
me Show the currently signed-in user (`GET /api/me`)
Resources:
projects Manage projects
annotations Manage annotations
exports Manage dataset exports
models Manage trained models (push, list, download)
files Manage project files (list, reserve / unreserve test set)
Admin & raw API:
admin Root-only platform analytics (users, installs, usage, …) as JSON
api GET any platform endpoint and print its JSON (`wk api /api/admin/geo`)
CLI:
config Read or change persisted CLI preferences (telemetry, …)
version Print the local CLI version and probe the platform's `/api/health`
update Replace this binary with the latest release (or `--check` to peek)
agents Print the bundled AGENTS.md guide for AI agents using `wk`
help Print this message or the help of the given subcommand(s)
Options:
{options}{after-help}";
#[derive(Parser)]
#[command(
name = "wk",
version = VERSION,
about = "Command-line client for the WaveKat platform",
long_about = "Command-line client for the WaveKat platform.\n\n\
Run `wk login` to authenticate. Credentials are stored under your platform \
config dir (e.g. ~/.config/wavekat/auth.json on Linux/macOS).\n\n\
Run `wk update` to upgrade in place, or `wk agents` for the AI-agent \
integration guide (also at https://github.com/wavekat/wavekat-cli/blob/main/AGENTS.md).",
help_template = HELP_TEMPLATE,
)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Login(commands::login::Args),
Logout,
Me,
Projects {
#[command(subcommand)]
command: commands::projects::Cmd,
},
Annotations {
#[command(subcommand)]
command: commands::annotations::Cmd,
},
Exports {
#[command(subcommand)]
command: commands::exports::Cmd,
},
Models {
#[command(subcommand)]
command: commands::models::Cmd,
},
Files {
#[command(subcommand)]
command: commands::files::Cmd,
},
Admin {
#[command(subcommand)]
command: commands::admin::Cmd,
},
Api(commands::api::Args),
Config {
#[command(subcommand)]
command: commands::config::Cmd,
},
Version(commands::version::Args),
Update(commands::update::Args),
Agents,
}
#[tokio::main]
async fn main() -> Result<()> {
let _telemetry = telemetry::init();
let cli = Cli::parse();
let command_name = command_name(&cli.command);
telemetry::maybe_print_first_run_notice();
let started = std::time::Instant::now();
let result = dispatch(cli.command).await;
if let Err(err) = &result {
if reports_errors(command_name) {
telemetry::report_error(command_name, started.elapsed(), err);
}
}
result
}
async fn dispatch(cmd: Command) -> Result<()> {
match cmd {
Command::Login(args) => commands::login::run(args).await,
Command::Logout => commands::logout::run().await,
Command::Me => commands::me::run().await,
Command::Projects { command } => commands::projects::run(command).await,
Command::Annotations { command } => commands::annotations::run(command).await,
Command::Exports { command } => commands::exports::run(command).await,
Command::Models { command } => commands::models::run(command).await,
Command::Files { command } => commands::files::run(command).await,
Command::Admin { command } => commands::admin::run(command).await,
Command::Api(args) => commands::api::run(args).await,
Command::Config { command } => commands::config::run(command).await,
Command::Version(args) => commands::version::run(args).await,
Command::Update(args) => commands::update::run(args).await,
Command::Agents => commands::agents::run().await,
}
}
fn reports_errors(command_name: &str) -> bool {
!matches!(command_name, "admin" | "api")
}
fn command_name(cmd: &Command) -> &'static str {
match cmd {
Command::Login(_) => "login",
Command::Logout => "logout",
Command::Me => "me",
Command::Projects { .. } => "projects",
Command::Annotations { .. } => "annotations",
Command::Exports { .. } => "exports",
Command::Models { .. } => "models",
Command::Files { .. } => "files",
Command::Admin { .. } => "admin",
Command::Api(_) => "api",
Command::Config { .. } => "config",
Command::Version(_) => "version",
Command::Update(_) => "update",
Command::Agents => "agents",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn customer_data_commands_skip_error_reporting() {
assert!(!reports_errors("admin"));
assert!(!reports_errors("api"));
assert!(reports_errors("exports"));
}
}