feat: add web tools config to configure

This commit is contained in:
Peter Steinberger
2026-01-15 05:08:51 +00:00
parent f6a72ef3c2
commit 042b65dfcc
15 changed files with 122 additions and 15 deletions

View File

@@ -25,6 +25,7 @@ vi.mock("../commands/configure.js", () => ({
CONFIGURE_WIZARD_SECTIONS: [
"workspace",
"model",
"web",
"gateway",
"daemon",
"channels",

View File

@@ -26,6 +26,7 @@ vi.mock("../commands/configure.js", () => ({
CONFIGURE_WIZARD_SECTIONS: [
"workspace",
"model",
"web",
"gateway",
"daemon",
"channels",

View File

@@ -25,6 +25,7 @@ vi.mock("../commands/configure.js", () => ({
CONFIGURE_WIZARD_SECTIONS: [
"workspace",
"model",
"web",
"gateway",
"daemon",
"channels",

View File

@@ -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",

View File

@@ -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<ChannelsWizardMod
) as ChannelsWizardMode;
}
async function promptWebToolsConfig(
nextConfig: ClawdbotConfig,
runtime: RuntimeEnv,
): Promise<ClawdbotConfig> {
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;

View File

@@ -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",
);
}
}

View File

@@ -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",
);
}
}

View File

@@ -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)",