Skip to content

Commit 5bc68e8

Browse files
committed
feat: add search command, bump version to 0.2.1
- Add `search` subcommand: search autocli.ai for existing adapters by URL - Auto-prepend https:// when URL has no scheme - Fix adapter save: extract site/name from YAML content instead of server display name
1 parent 01232db commit 5bc68e8

2 files changed

Lines changed: 96 additions & 2 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ members = [
1212
]
1313

1414
[workspace.package]
15-
version = "0.2.0"
15+
version = "0.2.1"
1616
edition = "2021"
1717
license = "Apache-2.0"
1818

crates/opencli-rs-cli/src/main.rs

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ fn build_cli(registry: &Registry, external_clis: &[ExternalCli]) -> Command {
122122
.arg(Arg::new("site").long("site").help("Override site name"))
123123
.arg(Arg::new("ai").long("ai").action(ArgAction::SetTrue).help("Use AI (LLM) to analyze and generate adapter (requires ~/.opencli-rs/config.json)")),
124124
)
125+
.subcommand(
126+
Command::new("search")
127+
.about("Search for existing adapters on autocli.ai")
128+
.arg(Arg::new("url").required(true).help("URL to search adapters for")),
129+
)
125130
.subcommand(
126131
Command::new("auth")
127132
.about("Authenticate with AutoCLI"),
@@ -364,6 +369,84 @@ async fn main() {
364369
completion::run_completion(&mut app, shell);
365370
return;
366371
}
372+
"search" => {
373+
let raw_url = site_matches.get_one::<String>("url").unwrap();
374+
let url = if raw_url.starts_with("http://") || raw_url.starts_with("https://") {
375+
raw_url.clone()
376+
} else {
377+
format!("https://{}", raw_url)
378+
};
379+
let config = opencli_rs_ai::load_config();
380+
let token = match &config.autocli_token {
381+
Some(t) => t.clone(),
382+
None => {
383+
eprintln!("{}", t("❌ 未认证,请先运行: opencli-rs auth", "❌ Not authenticated. Run first: opencli-rs auth"));
384+
std::process::exit(1);
385+
}
386+
};
387+
388+
match search_existing_adapters(&url, &token).await {
389+
Ok(matches) if !matches.is_empty() => {
390+
let options: Vec<String> = matches.iter().map(|m| {
391+
let tag = match m.match_type.as_str() {
392+
"exact" => "[exact] ",
393+
"partial" => "[partial]",
394+
"domain" => "[domain] ",
395+
_ => "[other] ",
396+
};
397+
let desc = if m.description.is_empty() {
398+
String::new()
399+
} else {
400+
format!(" - {}", m.description)
401+
};
402+
let author = if m.author.is_empty() {
403+
String::new()
404+
} else {
405+
format!(" (by {})", m.author)
406+
};
407+
format!("{} {} {}{}{}", tag, m.site_name, m.cmd_name, author, desc)
408+
}).collect();
409+
410+
let selection = inquire::Select::new(
411+
t("找到以下配置,请选择:", "Adapters found, please select:"),
412+
options,
413+
).prompt();
414+
415+
match selection {
416+
Ok(chosen) => {
417+
let idx = matches.iter().position(|m| {
418+
chosen.contains(&m.cmd_name) && chosen.contains(&m.site_name)
419+
});
420+
if let Some(i) = idx {
421+
let m = &matches[i];
422+
let yaml_site = m.config.lines()
423+
.find(|l| l.starts_with("site:"))
424+
.and_then(|l| l.strip_prefix("site:"))
425+
.map(|s| s.trim().trim_matches('"').to_string())
426+
.unwrap_or_else(|| m.site_name.clone());
427+
let yaml_name = m.config.lines()
428+
.find(|l| l.starts_with("name:"))
429+
.and_then(|l| l.strip_prefix("name:"))
430+
.map(|s| s.trim().trim_matches('"').to_string())
431+
.unwrap_or_else(|| m.cmd_name.clone());
432+
save_adapter(&yaml_site, &yaml_name, &m.config);
433+
}
434+
}
435+
Err(_) => {
436+
eprintln!("{}", t("已取消", "Cancelled"));
437+
}
438+
}
439+
}
440+
Ok(_) => {
441+
eprintln!("{}", t("📭 未找到匹配的配置", "📭 No matching adapters found"));
442+
}
443+
Err(e) => {
444+
eprintln!("{}", e);
445+
std::process::exit(1);
446+
}
447+
}
448+
return;
449+
}
367450
"auth" => {
368451
// Open browser to get token
369452
let token_url = "https://autocli.ai/get-token";
@@ -577,7 +660,18 @@ async fn main() {
577660
});
578661
if let Some(i) = idx {
579662
let m = &matches[i];
580-
save_adapter(&m.site_name, &m.cmd_name, &m.config);
663+
// Extract site and name from YAML config content, not server's display name
664+
let yaml_site = m.config.lines()
665+
.find(|l| l.starts_with("site:"))
666+
.and_then(|l| l.strip_prefix("site:"))
667+
.map(|s| s.trim().trim_matches('"').to_string())
668+
.unwrap_or_else(|| m.site_name.clone());
669+
let yaml_name = m.config.lines()
670+
.find(|l| l.starts_with("name:"))
671+
.and_then(|l| l.strip_prefix("name:"))
672+
.map(|s| s.trim().trim_matches('"').to_string())
673+
.unwrap_or_else(|| m.cmd_name.clone());
674+
save_adapter(&yaml_site, &yaml_name, &m.config);
581675
let _ = page.close().await;
582676
return;
583677
} else {

0 commit comments

Comments
 (0)