feat: zero-search
Browse files- bun.lock +0 -11
- package.json +0 -1
- src/tools/WebSearchTool/WebSearchTool.ts +65 -10
bun.lock
CHANGED
|
@@ -54,7 +54,6 @@
|
|
| 54 |
"cli-highlight": "^2.1.11",
|
| 55 |
"code-excerpt": "^4.0.0",
|
| 56 |
"diff": "^8.0.4",
|
| 57 |
-
"duck-duck-scrape": "^2.2.7",
|
| 58 |
"emoji-regex": "^10.6.0",
|
| 59 |
"env-paths": "^4.0.0",
|
| 60 |
"execa": "^9.6.1",
|
|
@@ -619,8 +618,6 @@
|
|
| 619 |
|
| 620 |
"dom-mutator": ["dom-mutator@0.6.0", "", {}, "sha512-iCt9o0aYfXMUkz/43ZOAUFQYotjGB+GNbYJiJdz4TgXkyToXbbRy5S6FbTp72lRBtfpUMwEc1KmpFEU4CZeoNg=="],
|
| 621 |
|
| 622 |
-
"duck-duck-scrape": ["duck-duck-scrape@2.2.7", "", { "dependencies": { "html-entities": "^2.3.3", "needle": "^3.2.0" } }, "sha512-BEcglwnfx5puJl90KQfX+Q2q5vCguqyMpZcSRPBWk8OY55qWwV93+E+7DbIkrGDW4qkqPfUvtOUdi0lXz6lEMQ=="],
|
| 623 |
-
|
| 624 |
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
| 625 |
|
| 626 |
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
|
@@ -745,8 +742,6 @@
|
|
| 745 |
|
| 746 |
"hono": ["hono@4.12.9", "", {}, "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA=="],
|
| 747 |
|
| 748 |
-
"html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="],
|
| 749 |
-
|
| 750 |
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
| 751 |
|
| 752 |
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
|
@@ -867,8 +862,6 @@
|
|
| 867 |
|
| 868 |
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
|
| 869 |
|
| 870 |
-
"needle": ["needle@3.5.0", "", { "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" }, "bin": { "needle": "bin/needle" } }, "sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w=="],
|
| 871 |
-
|
| 872 |
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
| 873 |
|
| 874 |
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
|
@@ -973,8 +966,6 @@
|
|
| 973 |
|
| 974 |
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
| 975 |
|
| 976 |
-
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
| 977 |
-
|
| 978 |
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
| 979 |
|
| 980 |
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
|
@@ -1435,8 +1426,6 @@
|
|
| 1435 |
|
| 1436 |
"minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
| 1437 |
|
| 1438 |
-
"needle/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
| 1439 |
-
|
| 1440 |
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
| 1441 |
|
| 1442 |
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
|
|
|
|
| 54 |
"cli-highlight": "^2.1.11",
|
| 55 |
"code-excerpt": "^4.0.0",
|
| 56 |
"diff": "^8.0.4",
|
|
|
|
| 57 |
"emoji-regex": "^10.6.0",
|
| 58 |
"env-paths": "^4.0.0",
|
| 59 |
"execa": "^9.6.1",
|
|
|
|
| 618 |
|
| 619 |
"dom-mutator": ["dom-mutator@0.6.0", "", {}, "sha512-iCt9o0aYfXMUkz/43ZOAUFQYotjGB+GNbYJiJdz4TgXkyToXbbRy5S6FbTp72lRBtfpUMwEc1KmpFEU4CZeoNg=="],
|
| 620 |
|
|
|
|
|
|
|
| 621 |
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
| 622 |
|
| 623 |
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
|
|
|
| 742 |
|
| 743 |
"hono": ["hono@4.12.9", "", {}, "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA=="],
|
| 744 |
|
|
|
|
|
|
|
| 745 |
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
| 746 |
|
| 747 |
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
|
|
|
| 862 |
|
| 863 |
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
|
| 864 |
|
|
|
|
|
|
|
| 865 |
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
| 866 |
|
| 867 |
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
|
|
|
| 966 |
|
| 967 |
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
| 968 |
|
|
|
|
|
|
|
| 969 |
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
| 970 |
|
| 971 |
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
|
|
|
| 1426 |
|
| 1427 |
"minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
| 1428 |
|
|
|
|
|
|
|
| 1429 |
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
| 1430 |
|
| 1431 |
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
|
package.json
CHANGED
|
@@ -69,7 +69,6 @@
|
|
| 69 |
"cli-highlight": "^2.1.11",
|
| 70 |
"code-excerpt": "^4.0.0",
|
| 71 |
"diff": "^8.0.4",
|
| 72 |
-
"duck-duck-scrape": "^2.2.7",
|
| 73 |
"emoji-regex": "^10.6.0",
|
| 74 |
"env-paths": "^4.0.0",
|
| 75 |
"execa": "^9.6.1",
|
|
|
|
| 69 |
"cli-highlight": "^2.1.11",
|
| 70 |
"code-excerpt": "^4.0.0",
|
| 71 |
"diff": "^8.0.4",
|
|
|
|
| 72 |
"emoji-regex": "^10.6.0",
|
| 73 |
"env-paths": "^4.0.0",
|
| 74 |
"execa": "^9.6.1",
|
src/tools/WebSearchTool/WebSearchTool.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
import { search } from 'duck-duck-scrape'
|
| 2 |
import type { PermissionResult } from 'src/utils/permissions/PermissionResult.js'
|
| 3 |
import { z } from 'zod/v4'
|
| 4 |
import { buildTool, type ToolDef } from '../../Tool.js'
|
|
@@ -64,6 +63,64 @@ export type { WebSearchProgress } from '../../types/tools.js'
|
|
| 64 |
|
| 65 |
import type { WebSearchProgress } from '../../types/tools.js'
|
| 66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
/**
|
| 68 |
* Filter search results by domain
|
| 69 |
*/
|
|
@@ -183,15 +240,13 @@ export const WebSearchTool = buildTool<InputSchema, Output, WebSearchTool>({
|
|
| 183 |
|
| 184 |
try {
|
| 185 |
// Call DuckDuckGo search
|
| 186 |
-
const
|
| 187 |
-
safeSearch: 'moderate',
|
| 188 |
-
})
|
| 189 |
|
| 190 |
// Filter results by domain if specified
|
| 191 |
-
let filteredResults =
|
| 192 |
if (allowed_domains || blocked_domains) {
|
| 193 |
filteredResults = filterDomains(
|
| 194 |
-
|
| 195 |
allowed_domains,
|
| 196 |
blocked_domains
|
| 197 |
)
|
|
@@ -210,12 +265,12 @@ export const WebSearchTool = buildTool<InputSchema, Output, WebSearchTool>({
|
|
| 210 |
}
|
| 211 |
|
| 212 |
// Convert to output format
|
| 213 |
-
const
|
| 214 |
|
| 215 |
if (filteredResults.length === 0) {
|
| 216 |
-
|
| 217 |
} else {
|
| 218 |
-
|
| 219 |
tool_use_id: 'search-1',
|
| 220 |
content: filteredResults.map(r => ({
|
| 221 |
title: r.title,
|
|
@@ -229,7 +284,7 @@ export const WebSearchTool = buildTool<InputSchema, Output, WebSearchTool>({
|
|
| 229 |
|
| 230 |
return {
|
| 231 |
query,
|
| 232 |
-
results,
|
| 233 |
durationSeconds,
|
| 234 |
}
|
| 235 |
} catch (error) {
|
|
|
|
|
|
|
| 1 |
import type { PermissionResult } from 'src/utils/permissions/PermissionResult.js'
|
| 2 |
import { z } from 'zod/v4'
|
| 3 |
import { buildTool, type ToolDef } from '../../Tool.js'
|
|
|
|
| 63 |
|
| 64 |
import type { WebSearchProgress } from '../../types/tools.js'
|
| 65 |
|
| 66 |
+
/**
|
| 67 |
+
* Search DuckDuckGo and return results
|
| 68 |
+
*/
|
| 69 |
+
async function searchDuckDuckGo(query: string): Promise<Array<{ title: string; url: string }>> {
|
| 70 |
+
const url = new URL('https://duckduckgo.com/html/')
|
| 71 |
+
url.searchParams.set('q', query)
|
| 72 |
+
|
| 73 |
+
const response = await fetch(url.toString(), {
|
| 74 |
+
headers: {
|
| 75 |
+
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
| 76 |
+
},
|
| 77 |
+
})
|
| 78 |
+
|
| 79 |
+
if (!response.ok) {
|
| 80 |
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
const html = await response.text()
|
| 84 |
+
|
| 85 |
+
// Parse HTML to extract search results
|
| 86 |
+
const results: Array<{ title: string; url: string }> = []
|
| 87 |
+
|
| 88 |
+
// DuckDuckGo search results are in <a class="result__a"> tags
|
| 89 |
+
// with title as text content and href as URL
|
| 90 |
+
const resultRegex = /<a[^>]*class="result__a"[^>]*>(.*?)<\/a>/gi
|
| 91 |
+
let match: RegExpExecArray | null
|
| 92 |
+
|
| 93 |
+
while ((match = resultRegex.exec(html)) !== null && results.length < 10) {
|
| 94 |
+
const title = match[1]
|
| 95 |
+
.replace(/<[^>]*>/g, '') // Remove HTML tags
|
| 96 |
+
.replace(/&/g, '&')
|
| 97 |
+
.replace(/</g, '<')
|
| 98 |
+
.replace(/>/g, '>')
|
| 99 |
+
.replace(/"/g, '"')
|
| 100 |
+
.trim()
|
| 101 |
+
|
| 102 |
+
// Extract URL from href attribute
|
| 103 |
+
const hrefMatch = match[0].match(/href="([^"]*)"/)
|
| 104 |
+
if (hrefMatch) {
|
| 105 |
+
let url = hrefMatch[1]
|
| 106 |
+
|
| 107 |
+
// DuckDuckGo URLs are often redirects
|
| 108 |
+
if (url.startsWith('/l/?uddg=')) {
|
| 109 |
+
const urlMatch = url.match(/uddg=([^&]+)/)
|
| 110 |
+
if (urlMatch) {
|
| 111 |
+
url = decodeURIComponent(urlMatch[1])
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
if (title && url) {
|
| 116 |
+
results.push({ title, url })
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
return results
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
/**
|
| 125 |
* Filter search results by domain
|
| 126 |
*/
|
|
|
|
| 240 |
|
| 241 |
try {
|
| 242 |
// Call DuckDuckGo search
|
| 243 |
+
const results = await searchDuckDuckGo(query)
|
|
|
|
|
|
|
| 244 |
|
| 245 |
// Filter results by domain if specified
|
| 246 |
+
let filteredResults = results
|
| 247 |
if (allowed_domains || blocked_domains) {
|
| 248 |
filteredResults = filterDomains(
|
| 249 |
+
results,
|
| 250 |
allowed_domains,
|
| 251 |
blocked_domains
|
| 252 |
)
|
|
|
|
| 265 |
}
|
| 266 |
|
| 267 |
// Convert to output format
|
| 268 |
+
const searchResults: (SearchResult | string)[] = []
|
| 269 |
|
| 270 |
if (filteredResults.length === 0) {
|
| 271 |
+
searchResults.push(`No results for: ${query}`)
|
| 272 |
} else {
|
| 273 |
+
searchResults.push({
|
| 274 |
tool_use_id: 'search-1',
|
| 275 |
content: filteredResults.map(r => ({
|
| 276 |
title: r.title,
|
|
|
|
| 284 |
|
| 285 |
return {
|
| 286 |
query,
|
| 287 |
+
results: searchResults,
|
| 288 |
durationSeconds,
|
| 289 |
}
|
| 290 |
} catch (error) {
|