diff --git a/CHANGELOG.md b/CHANGELOG.md index 82fdbc2a1..9b1512198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Docs: document DM history limits for channel DMs. (#883) — thanks @pkrmf. - Security: add detect-secrets CI scan and baseline guidance. (#227) — thanks @Hyaxia. - Tools: add `web_search`/`web_fetch` (Brave API), auto-enable `web_fetch` for sandboxed sessions, and remove the `brave-search` skill. +- CLI/Docs: add a web tools configure section for storing Brave API keys and update onboarding tips. - Browser: add Chrome extension relay takeover mode (toolbar button), plus `clawdbot browser extension install/path` and remote browser control via `clawdbot browser serve` + `browser.controlToken`. ### Fixes diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 72778e02c..5cae33957 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -1607,7 +1607,7 @@ Legacy: `tools.bash` is still accepted as an alias. `tools.web` configures web search + fetch tools: - `tools.web.search.enabled` (default: true when key is present) -- `tools.web.search.apiKey` (or `BRAVE_API_KEY` env var) +- `tools.web.search.apiKey` (recommended: set via `clawdbot configure --section web`, or use `BRAVE_API_KEY` env var) - `tools.web.search.maxResults` (1–10, default 5) - `tools.web.search.timeoutSeconds` (default 30) - `tools.web.search.cacheTtlMinutes` (default 15) diff --git a/docs/start/faq.md b/docs/start/faq.md index ff763a36d..27d2c68e2 100644 --- a/docs/start/faq.md +++ b/docs/start/faq.md @@ -576,7 +576,9 @@ The Gateway watches the config and supports hot‑reload: ### How do I enable web search (and web fetch)? `web_fetch` works without an API key. `web_search` requires a Brave Search API -key (`BRAVE_API_KEY` or `tools.web.search.apiKey`). +key. **Recommended:** run `clawdbot configure --section web` to store it in +`tools.web.search.apiKey`. Environment alternative: set `BRAVE_API_KEY` for the +Gateway process. ```json5 { @@ -584,7 +586,7 @@ key (`BRAVE_API_KEY` or `tools.web.search.apiKey`). web: { search: { enabled: true, - apiKey: "BRAVE_API_KEY_HERE", // or set BRAVE_API_KEY + apiKey: "BRAVE_API_KEY_HERE", maxResults: 5 }, fetch: { @@ -598,6 +600,7 @@ key (`BRAVE_API_KEY` or `tools.web.search.apiKey`). Notes: - If you use allowlists, add `web_search`/`web_fetch` or `group:web`. - In sandboxed sessions, `web_fetch` auto-enables unless explicitly disabled. +- Daemons read env vars from `~/.clawdbot/.env` (or the service environment). Docs: [Web tools](/tools/web). diff --git a/docs/start/getting-started.md b/docs/start/getting-started.md index f94821237..2007b0604 100644 --- a/docs/start/getting-started.md +++ b/docs/start/getting-started.md @@ -40,8 +40,9 @@ run on host, set an explicit per-agent override: - Node `>=22` - `pnpm` (optional; recommended if you build from source) -- **Recommended:** Brave Search API key for web search. Set `BRAVE_API_KEY` or - `tools.web.search.apiKey`. See [Web tools](/tools/web). +- **Recommended:** Brave Search API key for web search. Easiest path: + `clawdbot configure --section web` (stores `tools.web.search.apiKey`). + See [Web tools](/tools/web). macOS: if you plan to build the apps, install Xcode / CLT. For the CLI + gateway only, Node is enough. Windows: use **WSL2** (Ubuntu recommended). WSL2 is strongly recommended; native Windows is untested and more problematic. Install WSL2 first, then run the Linux steps inside WSL. See [Windows (WSL2)](/platforms/windows). diff --git a/docs/start/wizard.md b/docs/start/wizard.md index d334bb89b..2bd70e853 100644 --- a/docs/start/wizard.md +++ b/docs/start/wizard.md @@ -25,8 +25,8 @@ clawdbot configure ``` Recommended: set up a Brave Search API key so the agent can use `web_search` -(`web_fetch` works without a key). Set `BRAVE_API_KEY` or -`tools.web.search.apiKey`. Docs: [Web tools](/tools/web). +(`web_fetch` works without a key). Easiest path: `clawdbot configure --section web` +which stores `tools.web.search.apiKey`. Docs: [Web tools](/tools/web). ## QuickStart vs Advanced diff --git a/docs/tools/index.md b/docs/tools/index.md index 3362a6e56..3aa017389 100644 --- a/docs/tools/index.md +++ b/docs/tools/index.md @@ -197,7 +197,7 @@ Core parameters: - `count` (1–10; default from `tools.web.search.maxResults`) Notes: -- Requires `BRAVE_API_KEY` or `tools.web.search.apiKey`. +- Requires a Brave API key (recommended: `clawdbot configure --section web`, or set `BRAVE_API_KEY`). - Enable via `tools.web.search.enabled`. - Responses are cached (default 15 min). - See [Web tools](/tools/web) for setup. diff --git a/docs/tools/web.md b/docs/tools/web.md index d837d1069..850ed5e39 100644 --- a/docs/tools/web.md +++ b/docs/tools/web.md @@ -28,11 +28,20 @@ These are **not** browser automation. For JS-heavy sites or logins, use the 1) Create a Brave Search API account at https://brave.com/search/api/ 2) Generate an API key in the dashboard. -3) Set `BRAVE_API_KEY` in your environment or paste it into `tools.web.search.apiKey`. +3) Run `clawdbot configure --section web` to store the key in config (recommended), or set `BRAVE_API_KEY` in your environment. Brave provides a free tier plus paid plans; check the Brave API portal for the current limits and pricing. +### Where to set the key (recommended) + +**Recommended:** run `clawdbot configure --section web`. It stores the key in +`~/.clawdbot/clawdbot.json` under `tools.web.search.apiKey`. + +**Environment alternative:** set `BRAVE_API_KEY` in the Gateway process +environment. For a daemon install, put it in `~/.clawdbot/.env` (or your +service environment). See [Env vars](/start/faq#how-does-clawdbot-load-environment-variables). + ## web_search Search the web with Brave’s API. @@ -40,7 +49,7 @@ Search the web with Brave’s API. ### Requirements - `tools.web.search.enabled: true` -- Brave API key via `BRAVE_API_KEY` **or** `tools.web.search.apiKey` +- Brave API key (recommended: `clawdbot configure --section web`, or set `BRAVE_API_KEY`) ### Config diff --git a/src/cli/program.nodes-basic.test.ts b/src/cli/program.nodes-basic.test.ts index 75a7ac17c..fb6499f16 100644 --- a/src/cli/program.nodes-basic.test.ts +++ b/src/cli/program.nodes-basic.test.ts @@ -25,6 +25,7 @@ vi.mock("../commands/configure.js", () => ({ CONFIGURE_WIZARD_SECTIONS: [ "workspace", "model", + "web", "gateway", "daemon", "channels", diff --git a/src/cli/program.nodes-media.test.ts b/src/cli/program.nodes-media.test.ts index fd12040bf..58cc2ffd6 100644 --- a/src/cli/program.nodes-media.test.ts +++ b/src/cli/program.nodes-media.test.ts @@ -26,6 +26,7 @@ vi.mock("../commands/configure.js", () => ({ CONFIGURE_WIZARD_SECTIONS: [ "workspace", "model", + "web", "gateway", "daemon", "channels", diff --git a/src/cli/program.smoke.test.ts b/src/cli/program.smoke.test.ts index 4aef009fd..172e437a6 100644 --- a/src/cli/program.smoke.test.ts +++ b/src/cli/program.smoke.test.ts @@ -25,6 +25,7 @@ vi.mock("../commands/configure.js", () => ({ CONFIGURE_WIZARD_SECTIONS: [ "workspace", "model", + "web", "gateway", "daemon", "channels", diff --git a/src/commands/configure.shared.ts b/src/commands/configure.shared.ts index 1a1a06b94..bc89529d8 100644 --- a/src/commands/configure.shared.ts +++ b/src/commands/configure.shared.ts @@ -11,6 +11,7 @@ import { stylePromptHint, stylePromptMessage, stylePromptTitle } from "../termin export const CONFIGURE_WIZARD_SECTIONS = [ "workspace", "model", + "web", "gateway", "daemon", "channels", @@ -34,6 +35,7 @@ export const CONFIGURE_SECTION_OPTIONS: Array<{ }> = [ { value: "workspace", label: "Workspace", hint: "Set workspace + sessions" }, { value: "model", label: "Model", hint: "Pick provider + credentials" }, + { value: "web", label: "Web tools", hint: "Configure Brave search + fetch" }, { value: "gateway", label: "Gateway", hint: "Port, bind, auth, tailscale" }, { value: "daemon", diff --git a/src/commands/configure.wizard.ts b/src/commands/configure.wizard.ts index 16fe624c9..2a38ba691 100644 --- a/src/commands/configure.wizard.ts +++ b/src/commands/configure.wizard.ts @@ -21,7 +21,14 @@ import type { ConfigureWizardParams, WizardSection, } from "./configure.shared.js"; -import { CONFIGURE_SECTION_OPTIONS, intro, outro, select, text } from "./configure.shared.js"; +import { + CONFIGURE_SECTION_OPTIONS, + confirm, + intro, + outro, + select, + text, +} from "./configure.shared.js"; import { healthCommand } from "./health.js"; import { formatHealthCheckFailure } from "./health-format.js"; import { setupChannels } from "./onboard-channels.js"; @@ -83,6 +90,77 @@ async function promptChannelMode(runtime: RuntimeEnv): Promise { + const existingSearch = nextConfig.tools?.web?.search; + const existingFetch = nextConfig.tools?.web?.fetch; + const hasSearchKey = Boolean(existingSearch?.apiKey); + + const enableSearch = guardCancel( + await confirm({ + message: "Enable web_search (Brave Search API)?", + initialValue: existingSearch?.enabled ?? hasSearchKey, + }), + runtime, + ); + + let nextSearch = { + ...existingSearch, + enabled: enableSearch, + }; + + if (enableSearch) { + const keyInput = guardCancel( + await text({ + message: hasSearchKey + ? "Brave Search API key (leave blank to keep current or use BRAVE_API_KEY)" + : "Brave Search API key (leave blank to use BRAVE_API_KEY)", + placeholder: hasSearchKey ? "Leave blank to keep current" : "BSA...", + }), + runtime, + ); + const key = String(keyInput ?? "").trim(); + if (key) { + nextSearch = { ...nextSearch, apiKey: key }; + } else if (!hasSearchKey) { + note( + [ + "No key stored. web_search needs BRAVE_API_KEY or tools.web.search.apiKey.", + "Docs: https://docs.clawd.bot/tools/web", + ].join("\n"), + "Web search", + ); + } + } + + const enableFetch = guardCancel( + await confirm({ + message: "Enable web_fetch (keyless HTTP fetch)?", + initialValue: existingFetch?.enabled ?? true, + }), + runtime, + ); + + const nextFetch = { + ...existingFetch, + enabled: enableFetch, + }; + + return { + ...nextConfig, + tools: { + ...nextConfig.tools, + web: { + ...nextConfig.tools?.web, + search: nextSearch, + fetch: nextFetch, + }, + }, + }; +} + export async function runConfigureWizard( opts: ConfigureWizardParams, runtime: RuntimeEnv = defaultRuntime, @@ -219,6 +297,10 @@ export async function runConfigureWizard( nextConfig = await promptAuthConfig(nextConfig, runtime, prompter); } + if (selected.includes("web")) { + nextConfig = await promptWebToolsConfig(nextConfig, runtime); + } + if (selected.includes("gateway")) { const gateway = await promptGatewayConfig(nextConfig, runtime); nextConfig = gateway.config; @@ -314,6 +396,11 @@ export async function runConfigureWizard( await persistConfig(); } + if (choice === "web") { + nextConfig = await promptWebToolsConfig(nextConfig, runtime); + await persistConfig(); + } + if (choice === "gateway") { const gateway = await promptGatewayConfig(nextConfig, runtime); nextConfig = gateway.config; diff --git a/src/commands/onboard-non-interactive/local.ts b/src/commands/onboard-non-interactive/local.ts index 5027ab69f..56b11b3b1 100644 --- a/src/commands/onboard-non-interactive/local.ts +++ b/src/commands/onboard-non-interactive/local.ts @@ -113,7 +113,7 @@ export async function runNonInteractiveOnboardingLocal(params: { if (!opts.json) { runtime.log( - "Tip: set BRAVE_API_KEY (or tools.web.search.apiKey) to enable web_search. Docs: https://docs.clawd.bot/tools/web", + "Tip: run `clawdbot configure --section web` to store your Brave API key for web_search. Docs: https://docs.clawd.bot/tools/web", ); } } diff --git a/src/commands/onboard-non-interactive/remote.ts b/src/commands/onboard-non-interactive/remote.ts index ab2e01e40..b75d78d87 100644 --- a/src/commands/onboard-non-interactive/remote.ts +++ b/src/commands/onboard-non-interactive/remote.ts @@ -45,7 +45,7 @@ export async function runNonInteractiveOnboardingRemote(params: { runtime.log(`Remote gateway: ${remoteUrl}`); runtime.log(`Auth: ${payload.auth}`); runtime.log( - "Tip: set BRAVE_API_KEY (or tools.web.search.apiKey) to enable web_search. Docs: https://docs.clawd.bot/tools/web", + "Tip: run `clawdbot configure --section web` to store your Brave API key for web_search. Docs: https://docs.clawd.bot/tools/web", ); } } diff --git a/src/wizard/onboarding.ts b/src/wizard/onboarding.ts index 710b48b81..b11047df0 100644 --- a/src/wizard/onboarding.ts +++ b/src/wizard/onboarding.ts @@ -77,8 +77,9 @@ export async function runOnboardingWizard( await prompter.intro("Clawdbot onboarding"); await prompter.note( [ - "Recommended: get a Brave Search API key to enable web_search.", - "Set BRAVE_API_KEY or tools.web.search.apiKey.", + "Recommended: set up a Brave Search API key for web_search.", + "Easiest: clawdbot configure --section web (stores tools.web.search.apiKey).", + "Env alternative: BRAVE_API_KEY (gateway environment).", "Docs: https://docs.clawd.bot/tools/web", ].join("\n"), "Web search (optional)",