| import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce"; |
| import type { Command } from "commander"; |
| import { resolveAgentDir } from "../../agents/agent-scope.js"; |
| import { getRuntimeConfig } from "../../config/config.js"; |
| import { defaultRuntime } from "../../runtime.js"; |
| import { |
| isWebFetchProviderConfigured, |
| listWebFetchProviders, |
| resolveWebFetchDefinition, |
| } from "../../web-fetch/runtime.js"; |
| import { |
| isWebSearchProviderConfigured, |
| listWebSearchProviders, |
| runWebSearch, |
| } from "../../web-search/runtime.js"; |
| import { runCommandWithRuntime } from "../cli-utils.js"; |
| import { |
| getCapabilityWebFetchCommandSecretTargets, |
| getCapabilityWebSearchCommandSecretTargets, |
| } from "../command-secret-targets.js"; |
| import { exitCliAfterOutput } from "../one-shot-exit.js"; |
| import type { CapabilityEnvelope } from "./metadata.js"; |
| import { emitJsonOrText, formatEnvelopeForText } from "./output.js"; |
| import { |
| parseOptionalPositiveInteger, |
| registerLocalProvidersCommand, |
| resolveLocalCapabilityRuntimeConfig, |
| } from "./shared.js"; |
|
|
| function describeWebResultFailure(result: Record<string, unknown>): string | undefined { |
| const statusCode = |
| typeof result.statusCode === "number" && Number.isFinite(result.statusCode) |
| ? result.statusCode |
| : undefined; |
| const error = result.error; |
| const errorMessage = |
| typeof error === "string" |
| ? error |
| : error && |
| typeof error === "object" && |
| typeof (error as { message?: unknown }).message === "string" |
| ? (error as { message: string }).message |
| : undefined; |
| if (result.ok !== false && (statusCode === undefined || statusCode < 400) && !errorMessage) { |
| return undefined; |
| } |
| return ( |
| errorMessage ?? |
| (statusCode ? `provider returned status ${statusCode}` : "provider reported failure") |
| ); |
| } |
|
|
| async function runWebSearchCommand(params: { query: string; provider?: string; limit?: number }) { |
| const rawConfig = getRuntimeConfig(); |
| const scopedTargets = getCapabilityWebSearchCommandSecretTargets(rawConfig, { |
| providerId: params.provider, |
| }); |
| const cfg = await resolveLocalCapabilityRuntimeConfig({ |
| commandName: "infer web search", |
| ...scopedTargets, |
| config: rawConfig, |
| }); |
| const result = await runWebSearch({ |
| config: cfg, |
| providerId: params.provider, |
| args: { |
| query: params.query, |
| count: params.limit, |
| limit: params.limit, |
| }, |
| }); |
| const error = describeWebResultFailure(result.result); |
| return { |
| ok: error === undefined, |
| capability: "web.search", |
| transport: "local" as const, |
| provider: result.provider, |
| attempts: [], |
| outputs: [{ result: result.result }], |
| ...(error ? { error } : {}), |
| } satisfies CapabilityEnvelope; |
| } |
|
|
| async function runWebFetchCommand(params: { url: string; provider?: string; format?: string }) { |
| const rawConfig = getRuntimeConfig(); |
| const scopedTargets = getCapabilityWebFetchCommandSecretTargets(rawConfig, { |
| providerId: params.provider, |
| }); |
| const cfg = await resolveLocalCapabilityRuntimeConfig({ |
| commandName: "infer web fetch", |
| ...scopedTargets, |
| config: rawConfig, |
| }); |
| const resolved = resolveWebFetchDefinition({ |
| config: cfg, |
| providerId: params.provider, |
| }); |
| if (!resolved) { |
| throw new Error("web.fetch is disabled or no provider is available."); |
| } |
| const result = await resolved.definition.execute({ |
| url: params.url, |
| format: params.format, |
| }); |
| const error = describeWebResultFailure(result); |
| return { |
| ok: error === undefined, |
| capability: "web.fetch", |
| transport: "local" as const, |
| provider: resolved.provider.id, |
| attempts: [], |
| outputs: [{ result }], |
| ...(error ? { error } : {}), |
| } satisfies CapabilityEnvelope; |
| } |
|
|
| export function registerWebCapabilityCommands(capability: Command): void { |
| const web = capability.command("web").description("Web capabilities"); |
|
|
| web |
| .command("search") |
| .description("Run web search") |
| .requiredOption("--query <text>", "Search query") |
| .option("--provider <id>", "Provider id") |
| .option("--limit <n>", "Result limit") |
| .option("--json", "Output JSON", false) |
| .action(async (opts) => { |
| await runCommandWithRuntime(defaultRuntime, async () => { |
| const result = await runWebSearchCommand({ |
| query: String(opts.query), |
| provider: opts.provider as string | undefined, |
| limit: parseOptionalPositiveInteger(opts.limit, "--limit"), |
| }); |
| emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText); |
| if (!result.ok) { |
| exitCliAfterOutput(defaultRuntime, 1); |
| } |
| }); |
| }); |
|
|
| web |
| .command("fetch") |
| .description("Fetch one URL") |
| .requiredOption("--url <url>", "URL") |
| .option("--provider <id>", "Provider id") |
| .option("--format <format>", "Format hint") |
| .option("--json", "Output JSON", false) |
| .action(async (opts) => { |
| await runCommandWithRuntime(defaultRuntime, async () => { |
| const result = await runWebFetchCommand({ |
| url: String(opts.url), |
| provider: opts.provider as string | undefined, |
| format: opts.format as string | undefined, |
| }); |
| emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText); |
| if (!result.ok) { |
| exitCliAfterOutput(defaultRuntime, 1); |
| } |
| }); |
| }); |
|
|
| registerLocalProvidersCommand( |
| web, |
| "List web providers", |
| (cfg, agentId) => { |
| const agentDir = resolveAgentDir(cfg, agentId); |
| const selectedSearchProvider = |
| typeof cfg.tools?.web?.search?.provider === "string" |
| ? normalizeLowercaseStringOrEmpty(cfg.tools.web.search.provider) |
| : ""; |
| const selectedFetchProvider = |
| typeof cfg.tools?.web?.fetch?.provider === "string" |
| ? normalizeLowercaseStringOrEmpty(cfg.tools.web.fetch.provider) |
| : ""; |
| return { |
| search: listWebSearchProviders({ config: cfg }).map((provider) => ({ |
| available: true, |
| configured: isWebSearchProviderConfigured({ provider, config: cfg, agentDir }), |
| selected: provider.id === selectedSearchProvider, |
| id: provider.id, |
| envVars: provider.envVars, |
| })), |
| fetch: listWebFetchProviders({ config: cfg }).map((provider) => ({ |
| available: true, |
| configured: isWebFetchProviderConfigured({ provider, config: cfg }), |
| selected: provider.id === selectedFetchProvider, |
| id: provider.id, |
| envVars: provider.envVars, |
| })), |
| }; |
| }, |
| (value) => JSON.stringify(value, null, 2), |
| ); |
| } |
|
|