diff --git a/CHANGELOG.md b/CHANGELOG.md index b1b56f4..f2662ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,14 @@ All notable changes to webclaw are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/). -## [Unreleased] +## [0.6.16] - 2026-07-22 ### Added - **`@webclaw/mcp` — zero-install MCP launcher.** `npx -y @webclaw/mcp` downloads the prebuilt `webclaw-mcp` binary once, verifies it against the release `SHA256SUMS`, caches it, and runs it as an MCP stdio server. This is now the canonical way to add webclaw to an MCP client (`{"command": "npx", "args": ["-y", "@webclaw/mcp"]}`) — no Rust build — and it makes the server introspectable in MCP registries (Glama, Smithery, the MCP registry). `create-webclaw` stays as the auto-detect installer. +- **Atlas Cloud provider in the LLM chain (opt-in).** Set `ATLASCLOUD_API_KEY` to add [Atlas Cloud](https://atlascloud.ai) as a provider for the LLM features (extraction, summarization). It's added at the end of the chain — Ollama → OpenAI → Gemini → Anthropic → Atlas Cloud — so it only runs when you configure it and never preempts an already-configured provider. Override the model with `ATLASCLOUD_MODEL` (default `qwen/qwen3.5-flash`) and the endpoint with `ATLASCLOUD_BASE_URL`. + +### Fixed +- **`--urls-file` with a single URL now works.** A file containing exactly one URL (with no positional URL on the command line) was rejected with *"no input provided"*: it fell between the batch path, which skips single-entry files, and the single-URL path, which only reads positional arguments. A lone file URL is now treated exactly like `webclaw `, so it works with stdout, `--output-dir`, `--brand`, `--diff-with`, `--crawl`, `--watch`, and the LLM paths. ## [0.6.15] - 2026-07-18 diff --git a/CLAUDE.md b/CLAUDE.md index aab2b15..c34155d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,7 +63,7 @@ Three binaries: `webclaw` (CLI), `webclaw-mcp` (MCP server), `webclaw-server` (R - `url_security.rs` — SSRF guards + SSRF-safe redirect policy ### LLM Modules (`webclaw-llm`) -- Provider chain (`chain.rs`): Ollama (local-first, always added; availability checked at call time) -> OpenAI -> Gemini -> Anthropic. Gemini sits ahead of Anthropic so Google Cloud credits are preferred; Anthropic is the last-resort fallback. Each provider lives in `providers/` (`ollama.rs`, `openai.rs`, `gemini.rs`, `anthropic.rs`). +- Provider chain (`chain.rs`): Ollama (local-first, always added; availability checked at call time) -> OpenAI -> Gemini -> Anthropic -> Atlas Cloud. Gemini sits ahead of Anthropic so Google Cloud credits are preferred; Anthropic is the last-resort fallback. Atlas Cloud is opt-in and added last — only when `ATLASCLOUD_API_KEY` is set — so it never preempts an already-configured provider (`ATLASCLOUD_MODEL` / `ATLASCLOUD_BASE_URL` override its model/endpoint). Each provider lives in `providers/` (`ollama.rs`, `openai.rs`, `gemini.rs`, `anthropic.rs`, `atlascloud.rs`). - JSON schema extraction, prompt-based extraction, summarization ### PDF Modules (`webclaw-pdf`) diff --git a/Cargo.lock b/Cargo.lock index 40c72a4..e09d11c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3221,7 +3221,7 @@ dependencies = [ [[package]] name = "webclaw-cli" -version = "0.6.15" +version = "0.6.16" dependencies = [ "clap", "dotenvy", @@ -3242,7 +3242,7 @@ dependencies = [ [[package]] name = "webclaw-core" -version = "0.6.15" +version = "0.6.16" dependencies = [ "ego-tree", "once_cell", @@ -3260,7 +3260,7 @@ dependencies = [ [[package]] name = "webclaw-fetch" -version = "0.6.15" +version = "0.6.16" dependencies = [ "async-trait", "bytes", @@ -3288,7 +3288,7 @@ dependencies = [ [[package]] name = "webclaw-llm" -version = "0.6.15" +version = "0.6.16" dependencies = [ "async-trait", "reqwest", @@ -3301,7 +3301,7 @@ dependencies = [ [[package]] name = "webclaw-mcp" -version = "0.6.15" +version = "0.6.16" dependencies = [ "dirs", "dotenvy", @@ -3321,7 +3321,7 @@ dependencies = [ [[package]] name = "webclaw-pdf" -version = "0.6.15" +version = "0.6.16" dependencies = [ "pdf-extract", "thiserror", @@ -3330,7 +3330,7 @@ dependencies = [ [[package]] name = "webclaw-server" -version = "0.6.15" +version = "0.6.16" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index e92e797..295f22f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["crates/*"] [workspace.package] -version = "0.6.15" +version = "0.6.16" edition = "2024" license = "AGPL-3.0" repository = "https://github.com/0xMassi/webclaw" diff --git a/crates/webclaw-llm/src/chain.rs b/crates/webclaw-llm/src/chain.rs index e2c6b8b..f564172 100644 --- a/crates/webclaw-llm/src/chain.rs +++ b/crates/webclaw-llm/src/chain.rs @@ -1,5 +1,5 @@ /// Provider chain — tries providers in order until one succeeds. -/// Default order: Ollama (local, free) -> OpenAI -> Gemini -> Anthropic. +/// Default order: Ollama (local, free) -> OpenAI -> Gemini -> Anthropic -> Atlas Cloud (opt-in). /// Only includes providers that are actually configured/available. use async_trait::async_trait; use tracing::{debug, warn}; @@ -7,8 +7,8 @@ use tracing::{debug, warn}; use crate::error::LlmError; use crate::provider::{CompletionRequest, LlmProvider}; use crate::providers::{ - anthropic::AnthropicProvider, gemini::GeminiProvider, ollama::OllamaProvider, - openai::OpenAiProvider, + anthropic::AnthropicProvider, atlascloud::AtlasCloudProvider, gemini::GeminiProvider, + ollama::OllamaProvider, openai::OpenAiProvider, }; pub struct ProviderChain { @@ -16,11 +16,13 @@ pub struct ProviderChain { } impl ProviderChain { - /// Build the default chain: Ollama -> OpenAI -> Gemini -> Anthropic. + /// Build the default chain: Ollama -> OpenAI -> Gemini -> Anthropic -> Atlas Cloud. /// Ollama is always added (availability checked at call time). /// Cloud providers are only added if their API keys are configured. /// Gemini sits ahead of Anthropic so Google Cloud credits are preferred, - /// with Anthropic as the last-resort fallback. + /// with Anthropic as the last-resort fallback. Atlas Cloud is opt-in and + /// added last (only when `ATLASCLOUD_API_KEY` is set), so it never preempts + /// an already-configured provider. pub async fn default() -> Self { let mut providers: Vec> = Vec::new(); @@ -47,6 +49,11 @@ impl ProviderChain { providers.push(Box::new(anthropic)); } + if let Some(atlas) = AtlasCloudProvider::new(None, None, None) { + debug!("atlascloud configured, adding to chain"); + providers.push(Box::new(atlas)); + } + Self { providers } } diff --git a/crates/webclaw-llm/src/providers/atlascloud.rs b/crates/webclaw-llm/src/providers/atlascloud.rs index a234ec9..8855d87 100644 --- a/crates/webclaw-llm/src/providers/atlascloud.rs +++ b/crates/webclaw-llm/src/providers/atlascloud.rs @@ -21,7 +21,9 @@ impl AtlasCloudProvider { let base_url = base_url .or_else(|| std::env::var("ATLASCLOUD_BASE_URL").ok()) .unwrap_or_else(|| "https://api.atlascloud.ai/v1".into()); - let model = model.unwrap_or_else(|| "qwen/qwen3.5-flash".into()); + let model = model + .or_else(|| std::env::var("ATLASCLOUD_MODEL").ok()) + .unwrap_or_else(|| "qwen/qwen3.5-flash".into()); let inner = OpenAiProvider::new(Some(key), Some(base_url), Some(model))?; Some(Self { inner }) }