import type { SecretInputMode } from "../commands/onboard-types.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import {
DEFAULT_SECRET_PROVIDER_ALIAS,
type SecretInput,
type SecretRef,
hasConfiguredSecretInput,
normalizeSecretInputString,
} from "../config/types.secrets.js"; import { enablePluginInConfig } from "../plugins/enable.js"; import type { PluginWebSearchProviderEntry } from "../plugins/types.js"; import { resolvePluginWebSearchProviders } from "../plugins/web-search-providers.runtime.js"; import { sortWebSearchProviders } from "../plugins/web-search-providers.shared.js"; import type { RuntimeEnv } from "../runtime.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; import type { WizardPrompter } from "../wizard/prompts.js"; import type { FlowContribution, FlowOption } from "./types.js"; import { sortFlowContributionsByLabel } from "./types.js";
export type SearchProvider = NonNullable<
NonNullable<NonNullable<NonNullable<OpenClawConfig["tools"]>["web"]>["search"]>["provider"]
>;
type SearchConfig = NonNullable<NonNullable<NonNullable<OpenClawConfig["tools"]>["web"]>["search"]>;
type MutableSearchConfig = SearchConfig & Record<string, unknown>;
export type SearchProviderSetupOption = FlowOption & {
value: SearchProvider;
};
return {
...next,
plugins: nextPlugins as OpenClawConfig["plugins"],
};
}
export type SetupSearchOptions = {
quickstartDefaults?: boolean;
secretInputMode?: SecretInputMode;
};
async function finalizeSearchProviderSetup(params: {
originalConfig: OpenClawConfig;
nextConfig: OpenClawConfig;
entry: PluginWebSearchProviderEntry;
runtime: RuntimeEnv;
prompter: WizardPrompter;
opts?: SetupSearchOptions;
}): Promise<OpenClawConfig> {
let next = preserveDisabledState(params.originalConfig, params.nextConfig); if (!params.entry.runSetup) { return next;
}
next = await params.entry.runSetup({
config: next,
runtime: params.runtime,
prompter: params.prompter,
quickstartDefaults: params.opts?.quickstartDefaults,
secretInputMode: params.opts?.secretInputMode,
}); return preserveDisabledState(params.originalConfig, next);
}
export async function runSearchSetupFlow(
config: OpenClawConfig,
runtime: RuntimeEnv,
prompter: WizardPrompter,
opts?: SetupSearchOptions,
): Promise<OpenClawConfig> { const providerOptions = resolveSearchProviderOptions(config); if (providerOptions.length === 0) {
await prompter.note(
[ "No web search providers are currently available under this plugin policy.", "Enable plugins or remove deny rules, then run setup again.", "Docs: https://docs.openclaw.ai/tools/web",
].join("\n"), "Web search",
); return config;
}
await prompter.note(
[ "Web search lets your agent look things up online.", "Choose a provider. Some providers need an API key, and some work key-free.", "Docs: https://docs.openclaw.ai/tools/web",
].join("\n"), "Web search",
);
if (!needsCredential) {
await prompter.note(
[
`${entry.label} works without an API key.`, "OpenClaw will enable the plugin and use it as your web_search provider.",
`Docs: ${entry.docsUrl ?? "https://docs.openclaw.ai/tools/web"}`,
].join("\n"), "Web search",
); return await finalizeSearchProviderSetup({
originalConfig: config,
nextConfig: applySearchProviderSelection(config, choice),
entry,
runtime,
prompter,
opts,
});
}
const useSecretRefMode = opts?.secretInputMode === "ref"; // pragma: allowlist secret if (useSecretRefMode) { if (keyConfigured) { return await finalizeSearchProviderSetup({
originalConfig: config,
nextConfig: applySearchProviderSelection(config, choice),
entry,
runtime,
prompter,
opts,
});
} const ref = buildSearchEnvRef(config, choice);
await prompter.note(
[ "Secret references enabled — OpenClaw will store a reference instead of the API key.",
`Env var: ${ref.id}${envAvailable ? " (detected)" : ""}.`,
...(envAvailable ? [] : [`Set ${ref.id} in the Gateway environment.`]), "Docs: https://docs.openclaw.ai/tools/web",
].join("\n"), "Web search",
); return await finalizeSearchProviderSetup({
originalConfig: config,
nextConfig: applySearchKey(config, choice, ref),
entry,
runtime,
prompter,
opts,
});
}
const keyInput = await prompter.text({
message: keyConfigured
? `${credentialLabel} (leave blank to keep current)`
: envAvailable
? `${credentialLabel} (leave blank to use env var)`
: credentialLabel,
placeholder: keyConfigured ? "Leave blank to keep current" : entry.placeholder,
});
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.