use anyhow::{bail, Context, Result};
use clap::Args as ClapArgs;
use wavekat_platform_client::{loopback_handshake, HandshakeOptions};
use crate::client::Client;
use crate::config;
use crate::style;
pub const DEFAULT_BASE_URL: &str = "https://platform.wavekat.com";
#[derive(ClapArgs)]
pub struct Args {
#[arg(long, env = "WK_BASE_URL")]
base_url: Option<String>,
#[arg(long)]
no_browser: bool,
#[arg(long, env = "WK_TOKEN")]
token: Option<String>,
}
pub async fn run(args: Args) -> Result<()> {
let existing = config::load().ok();
let base_url = args
.base_url
.or_else(|| existing.as_ref().map(|c| c.base_url.clone()))
.unwrap_or_else(|| DEFAULT_BASE_URL.to_string())
.trim_end_matches('/')
.to_string();
let token = match args.token {
Some(t) => t.trim().to_string(),
None => browser_handshake(&base_url, args.no_browser).await?,
};
if token.is_empty() {
bail!("got an empty token from the platform");
}
let mut cfg = config::load_or_default();
cfg.base_url = base_url;
cfg.token = Some(token);
cfg.session_cookie = None;
let client = Client::new(&cfg)?;
let me: serde_json::Value = client
.get_json("/api/me")
.await
.context("verifying token against /api/me")?;
let login = me.get("login").and_then(|v| v.as_str()).unwrap_or("?");
let role = me.get("role").and_then(|v| v.as_str()).unwrap_or("?");
config::save(&cfg)?;
let path = config::auth_path()?;
println!(
"{} Signed in as {} ({} {}).",
style::green("✓"),
style::bold(login),
style::dim("role:"),
style::role(role),
);
println!(
"{} {}",
style::dim("Credentials saved to"),
style::dim(&path.display().to_string()),
);
Ok(())
}
async fn browser_handshake(base_url: &str, no_browser: bool) -> Result<String> {
let options = HandshakeOptions {
client: Some("wavekat-cli".to_string()),
..HandshakeOptions::default()
};
let pending =
loopback_handshake(base_url, options).context("starting loopback OAuth handshake")?;
if no_browser {
println!(
"Open this URL in any browser to finish signing in:\n {}\n",
pending.url(),
);
} else {
println!("Opening {base_url} in your browser to sign in…");
if let Err(e) = webbrowser::open(pending.url()) {
eprintln!("(couldn't open the browser automatically: {e})");
println!("Open this URL manually:\n {}\n", pending.url());
}
}
println!("Waiting for the browser to redirect back (Ctrl-C to cancel)…");
let outcome = pending
.wait()
.await
.context("waiting for the browser callback")?;
Ok(outcome.token.as_str().to_string())
}