mirror of
https://github.com/0xMassi/webclaw.git
synced 2026-07-27 08:01:01 +02:00
Merge pull request #91 from 0xMassi/fix/urls-file-single-row
fix(cli): --urls-file with a single row now works (#86)
This commit is contained in:
commit
026b97cdd1
1 changed files with 58 additions and 17 deletions
|
|
@ -864,6 +864,18 @@ fn collect_urls(cli: &Cli) -> Result<Vec<(String, Option<String>)>, String> {
|
||||||
Ok(entries)
|
Ok(entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When `--urls-file` supplies exactly one URL and no positional URL was given,
|
||||||
|
/// treat it like a single positional URL so a one-line file behaves identically
|
||||||
|
/// to `webclaw <url>` (stdout, `--output-dir`, cloud fallback, `--raw-html`,
|
||||||
|
/// `--diff`, LLM paths). Without this the lone file URL gives `entries.len() == 1`,
|
||||||
|
/// which the batch gates skip, and the single path then finds an empty `cli.urls`
|
||||||
|
/// and errors "no input provided". See issue #86.
|
||||||
|
fn backfill_single_file_url(urls: &mut Vec<String>, entries: &[(String, Option<String>)]) {
|
||||||
|
if urls.is_empty() && entries.len() == 1 {
|
||||||
|
urls.push(entries[0].0.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Result that can be either a local extraction or a cloud API JSON response.
|
/// Result that can be either a local extraction or a cloud API JSON response.
|
||||||
enum FetchOutput {
|
enum FetchOutput {
|
||||||
Local(Box<ExtractionResult>),
|
Local(Box<ExtractionResult>),
|
||||||
|
|
@ -2633,7 +2645,7 @@ async fn run_research(cli: &Cli, query: &str) -> Result<(), String> {
|
||||||
async fn main() {
|
async fn main() {
|
||||||
dotenvy::dotenv().ok();
|
dotenvy::dotenv().ok();
|
||||||
|
|
||||||
let cli = Cli::parse();
|
let mut cli = Cli::parse();
|
||||||
init_logging(cli.verbose);
|
init_logging(cli.verbose);
|
||||||
|
|
||||||
// Subcommand path. Handled before the flag dispatch so a subcommand
|
// Subcommand path. Handled before the flag dispatch so a subcommand
|
||||||
|
|
@ -2776,6 +2788,20 @@ async fn main() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect URLs from args + --urls-file up front, and backfill a lone
|
||||||
|
// --urls-file URL into cli.urls so a one-line file behaves like a positional
|
||||||
|
// URL across ALL modes below (crawl / watch / diff / brand / batch / single).
|
||||||
|
// See issue #86 — the mode gates and single path read cli.urls, which is
|
||||||
|
// empty when the URL came only from --urls-file.
|
||||||
|
let entries = match collect_urls(&cli) {
|
||||||
|
Ok(u) => u,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("error: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
backfill_single_file_url(&mut cli.urls, &entries);
|
||||||
|
|
||||||
// --crawl: recursive crawl mode
|
// --crawl: recursive crawl mode
|
||||||
if cli.crawl {
|
if cli.crawl {
|
||||||
if let Err(e) = run_crawl(&cli).await {
|
if let Err(e) = run_crawl(&cli).await {
|
||||||
|
|
@ -2787,13 +2813,7 @@ async fn main() {
|
||||||
|
|
||||||
// --watch: poll URL(s) for changes
|
// --watch: poll URL(s) for changes
|
||||||
if cli.watch {
|
if cli.watch {
|
||||||
let watch_urls: Vec<String> = match collect_urls(&cli) {
|
let watch_urls: Vec<String> = entries.iter().map(|(url, _)| url.clone()).collect();
|
||||||
Ok(entries) => entries.into_iter().map(|(url, _)| url).collect(),
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("error: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Err(e) = run_watch(&cli, &watch_urls).await {
|
if let Err(e) = run_watch(&cli, &watch_urls).await {
|
||||||
eprintln!("error: {e}");
|
eprintln!("error: {e}");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
|
|
@ -2828,15 +2848,6 @@ async fn main() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect all URLs from args + --urls-file
|
|
||||||
let entries = match collect_urls(&cli) {
|
|
||||||
Ok(u) => u,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("error: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// LLM modes: --extract-json, --extract-prompt, --summarize
|
// LLM modes: --extract-json, --extract-prompt, --summarize
|
||||||
// When multiple URLs are provided, run batch LLM extraction over all of them.
|
// When multiple URLs are provided, run batch LLM extraction over all of them.
|
||||||
if has_llm_flags(&cli) {
|
if has_llm_flags(&cli) {
|
||||||
|
|
@ -2908,6 +2919,36 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use webclaw_core::Content;
|
use webclaw_core::Content;
|
||||||
|
|
||||||
|
// issue #86: a single URL sourced only from --urls-file must be promoted to
|
||||||
|
// a positional URL so it takes the single-scrape path (batch gates need >1).
|
||||||
|
#[test]
|
||||||
|
fn single_file_url_backfilled_into_positional() {
|
||||||
|
let mut urls: Vec<String> = Vec::new();
|
||||||
|
let entries = vec![("https://example.com".to_string(), None)];
|
||||||
|
backfill_single_file_url(&mut urls, &entries);
|
||||||
|
assert_eq!(urls, vec!["https://example.com".to_string()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn positional_urls_left_untouched() {
|
||||||
|
let mut urls = vec!["https://a.com".to_string()];
|
||||||
|
let entries = vec![("https://a.com".to_string(), None)];
|
||||||
|
backfill_single_file_url(&mut urls, &entries);
|
||||||
|
assert_eq!(urls, vec!["https://a.com".to_string()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_file_urls_not_backfilled() {
|
||||||
|
// 2+ URLs already route to run_batch, so cli.urls stays empty here.
|
||||||
|
let mut urls: Vec<String> = Vec::new();
|
||||||
|
let entries = vec![
|
||||||
|
("https://a.com".to_string(), None),
|
||||||
|
("https://b.com".to_string(), None),
|
||||||
|
];
|
||||||
|
backfill_single_file_url(&mut urls, &entries);
|
||||||
|
assert!(urls.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
fn empty_result(title: Option<&str>, url: Option<&str>, markdown: &str) -> ExtractionResult {
|
fn empty_result(title: Option<&str>, url: Option<&str>, markdown: &str) -> ExtractionResult {
|
||||||
ExtractionResult {
|
ExtractionResult {
|
||||||
metadata: Metadata {
|
metadata: Metadata {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue