65 lines
2.7 KiB
JavaScript
65 lines
2.7 KiB
JavaScript
const net = require("net");
|
|
const { WebSearchTool } = require("./backend/search_tool");
|
|
const { normalizeOrigin } = require("./backend/result_formatter");
|
|
const { readSettings } = require("./backend/settings");
|
|
const { isLocalHostname, isPrivateAddress } = require("./backend/url_policy");
|
|
|
|
module.exports.checkAvailability = ({ paths }) => {
|
|
const settings = readSettings(paths.data);
|
|
if (!settings.enabled) {
|
|
return { available: false, message: "Web search is disabled in tool settings." };
|
|
}
|
|
if (!settings.provider_endpoint) {
|
|
return { available: false, message: "Configure a search provider endpoint in Tool Settings." };
|
|
}
|
|
try {
|
|
const endpoint = new URL(settings.provider_endpoint);
|
|
const hostname = endpoint.hostname.replace(/^\[|\]$/g, "");
|
|
if (!["http:", "https:"].includes(endpoint.protocol) || endpoint.username || endpoint.password ||
|
|
isLocalHostname(hostname) || (net.isIP(hostname) && isPrivateAddress(hostname))) {
|
|
return { available: false, message: "Search provider endpoint is blocked by network safety rules." };
|
|
}
|
|
} catch {
|
|
return { available: false, message: "Search provider endpoint is invalid." };
|
|
}
|
|
return { available: true };
|
|
};
|
|
|
|
module.exports.register = ({ registerTool, paths }) => {
|
|
const search = new WebSearchTool({ dataDir: paths.data });
|
|
registerTool({
|
|
tool_id: "lumi_ai_web_search.search",
|
|
display_name: "Search the web",
|
|
description: "Search current public web information only when verified local Lumi context is insufficient or current external information is explicitly needed. Returns normalized, policy-filtered results for final answer formatting.",
|
|
required_role: "user",
|
|
required_permission: "lumi_ai_web_search.search",
|
|
audit_category: "web_search",
|
|
confirmation_required: false,
|
|
risk_level: "low",
|
|
schema: {
|
|
query: { type: "string", required: true },
|
|
reason: {
|
|
type: "string",
|
|
required: true,
|
|
enum: [
|
|
"fact_lookup",
|
|
"resource_lookup",
|
|
"troubleshooting",
|
|
"documentation_lookup",
|
|
"news_or_recent",
|
|
"general_lookup"
|
|
]
|
|
},
|
|
requested_depth: { type: "string", required: false, enum: ["search", "full_page"] },
|
|
freshness: { type: "string", required: false }
|
|
},
|
|
permission_check: ({ user, context }) => {
|
|
const settings = readSettings(paths.data);
|
|
const origin = normalizeOrigin(context?.origin || context?.platform || "other");
|
|
return Boolean(user?.id) && settings.enabled && settings.allowed_origins.includes(origin);
|
|
},
|
|
workflow_handler: ({ arguments: args, user, ctx }) =>
|
|
search.run({ ...args, user, ctx })
|
|
});
|
|
};
|