use std::{
io::{self, Write},
path::Path,
};
use clap::{ArgGroup, Subcommand};
use serde_json::json;
use super::print_json;
use artifactize_core::{
auth::providers::{self, Custom, CustomView, EndpointUrl, KeyChange},
config::{Backend, Provider},
llm::EndpointApi,
};
#[derive(Debug, Subcommand)]
pub enum ProviderCommand {
List,
#[command(group(ArgGroup::new("target").args(["alias", "api"]).required(true)))]
Add {
name: Backend,
#[arg(long, value_enum, value_name = "PROVIDER", conflicts_with_all = ["url", "key"])]
alias: Option<Provider>,
#[arg(long, value_enum, requires = "url")]
api: Option<EndpointApi>,
#[arg(long, value_parser = parse_url)]
url: Option<EndpointUrl>,
#[arg(long)]
key: bool,
},
Remove { name: Backend },
}
fn parse_url(value: &str) -> Result<EndpointUrl, String> {
value.parse()
}
pub(super) async fn execute(
state: Option<&Path>,
repo: Option<&Path>,
command: ProviderCommand,
json: bool,
) -> Result<u8, String> {
let mut out = io::stdout().lock();
match command {
ProviderCommand::List => {
let list = providers::list(state, repo)?;
if json {
print_json(&list)?;
return Ok(0);
}
for key in &list.keys {
let source = match key.source {
providers::KeySource::Environment if key.saved => {
format!(
"key from {}; a saved key is kept but not used",
key.variable
)
}
providers::KeySource::Environment => format!("key from {}", key.variable),
providers::KeySource::Saved => "saved key".to_owned(),
providers::KeySource::Absent => "no key".to_owned(),
};
writeln!(out, "{}\t{source}", key.provider).map_err(|e| e.to_string())?;
}
for (name, custom) in &list.custom {
let target = match custom {
CustomView::Alias { provider } => format!("alias of {provider}"),
CustomView::Endpoint { api, url, key } => format!(
"{} at {}{}",
json!(api).as_str().unwrap_or_default(),
url.as_str(),
if *key { ", saved key" } else { "" }
),
};
writeln!(out, "{name}\t{target}").map_err(|e| e.to_string())?;
}
}
ProviderCommand::Add {
name,
alias,
api,
url,
key,
} => {
let (custom, change) = match (alias, api, url) {
(Some(provider), _, _) => (Custom::Alias { provider }, KeyChange::None),
(None, Some(api), Some(url)) => (
Custom::Endpoint {
api,
url,
key: None,
},
if key {
KeyChange::Set(super::secret::read("endpoint key")?.parse()?)
} else {
KeyChange::None
},
),
_ => unreachable!("clap requires --alias, or --api with --url"),
};
providers::save_custom(state, repo, name.clone(), custom, change).await?;
if json {
print_json(&json!({ "provider": name, "saved": true }))?;
} else {
writeln!(out, "Saved custom provider {name}.").map_err(|e| e.to_string())?;
}
}
ProviderCommand::Remove { name } => {
providers::remove_custom(state, repo, &name).await?;
if json {
print_json(&json!({ "provider": name, "saved": false }))?;
} else {
writeln!(out, "Removed custom provider {name}.").map_err(|e| e.to_string())?;
}
}
}
Ok(0)
}