File size: 6,695 Bytes
f778c12 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | 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),
);
}
|