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 }) }); };