Merge pull request #93 from 0xMassi/release/0.6.16

release: 0.6.16 — Atlas Cloud opt-in provider + `--urls-file` single-row fix
This commit is contained in:
Valerio 2026-07-22 18:13:15 +02:00 committed by GitHub
commit 50a17d27e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 29 additions and 16 deletions

View file

@ -3,10 +3,14 @@
All notable changes to webclaw are documented here. All notable changes to webclaw are documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/). Format follows [Keep a Changelog](https://keepachangelog.com/).
## [Unreleased] ## [0.6.16] - 2026-07-22
### Added ### 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. - **`@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 <url>`, so it works with stdout, `--output-dir`, `--brand`, `--diff-with`, `--crawl`, `--watch`, and the LLM paths.
## [0.6.15] - 2026-07-18 ## [0.6.15] - 2026-07-18

View file

@ -63,7 +63,7 @@ Three binaries: `webclaw` (CLI), `webclaw-mcp` (MCP server), `webclaw-server` (R
- `url_security.rs` — SSRF guards + SSRF-safe redirect policy - `url_security.rs` — SSRF guards + SSRF-safe redirect policy
### LLM Modules (`webclaw-llm`) ### 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 - JSON schema extraction, prompt-based extraction, summarization
### PDF Modules (`webclaw-pdf`) ### PDF Modules (`webclaw-pdf`)

14
Cargo.lock generated
View file

@ -3221,7 +3221,7 @@ dependencies = [
[[package]] [[package]]
name = "webclaw-cli" name = "webclaw-cli"
version = "0.6.15" version = "0.6.16"
dependencies = [ dependencies = [
"clap", "clap",
"dotenvy", "dotenvy",
@ -3242,7 +3242,7 @@ dependencies = [
[[package]] [[package]]
name = "webclaw-core" name = "webclaw-core"
version = "0.6.15" version = "0.6.16"
dependencies = [ dependencies = [
"ego-tree", "ego-tree",
"once_cell", "once_cell",
@ -3260,7 +3260,7 @@ dependencies = [
[[package]] [[package]]
name = "webclaw-fetch" name = "webclaw-fetch"
version = "0.6.15" version = "0.6.16"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@ -3288,7 +3288,7 @@ dependencies = [
[[package]] [[package]]
name = "webclaw-llm" name = "webclaw-llm"
version = "0.6.15" version = "0.6.16"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"reqwest", "reqwest",
@ -3301,7 +3301,7 @@ dependencies = [
[[package]] [[package]]
name = "webclaw-mcp" name = "webclaw-mcp"
version = "0.6.15" version = "0.6.16"
dependencies = [ dependencies = [
"dirs", "dirs",
"dotenvy", "dotenvy",
@ -3321,7 +3321,7 @@ dependencies = [
[[package]] [[package]]
name = "webclaw-pdf" name = "webclaw-pdf"
version = "0.6.15" version = "0.6.16"
dependencies = [ dependencies = [
"pdf-extract", "pdf-extract",
"thiserror", "thiserror",
@ -3330,7 +3330,7 @@ dependencies = [
[[package]] [[package]]
name = "webclaw-server" name = "webclaw-server"
version = "0.6.15" version = "0.6.16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"axum", "axum",

View file

@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.6.15" version = "0.6.16"
edition = "2024" edition = "2024"
license = "AGPL-3.0" license = "AGPL-3.0"
repository = "https://github.com/0xMassi/webclaw" repository = "https://github.com/0xMassi/webclaw"

View file

@ -1,5 +1,5 @@
/// Provider chain — tries providers in order until one succeeds. /// 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. /// Only includes providers that are actually configured/available.
use async_trait::async_trait; use async_trait::async_trait;
use tracing::{debug, warn}; use tracing::{debug, warn};
@ -7,8 +7,8 @@ use tracing::{debug, warn};
use crate::error::LlmError; use crate::error::LlmError;
use crate::provider::{CompletionRequest, LlmProvider}; use crate::provider::{CompletionRequest, LlmProvider};
use crate::providers::{ use crate::providers::{
anthropic::AnthropicProvider, gemini::GeminiProvider, ollama::OllamaProvider, anthropic::AnthropicProvider, atlascloud::AtlasCloudProvider, gemini::GeminiProvider,
openai::OpenAiProvider, ollama::OllamaProvider, openai::OpenAiProvider,
}; };
pub struct ProviderChain { pub struct ProviderChain {
@ -16,11 +16,13 @@ pub struct ProviderChain {
} }
impl 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). /// Ollama is always added (availability checked at call time).
/// Cloud providers are only added if their API keys are configured. /// Cloud providers are only added if their API keys are configured.
/// Gemini sits ahead of Anthropic so Google Cloud credits are preferred, /// 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 { pub async fn default() -> Self {
let mut providers: Vec<Box<dyn LlmProvider>> = Vec::new(); let mut providers: Vec<Box<dyn LlmProvider>> = Vec::new();
@ -47,6 +49,11 @@ impl ProviderChain {
providers.push(Box::new(anthropic)); 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 } Self { providers }
} }

View file

@ -21,7 +21,9 @@ impl AtlasCloudProvider {
let base_url = base_url let base_url = base_url
.or_else(|| std::env::var("ATLASCLOUD_BASE_URL").ok()) .or_else(|| std::env::var("ATLASCLOUD_BASE_URL").ok())
.unwrap_or_else(|| "https://api.atlascloud.ai/v1".into()); .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))?; let inner = OpenAiProvider::new(Some(key), Some(base_url), Some(model))?;
Some(Self { inner }) Some(Self { inner })
} }