@duckmind/dm-windows-x64 0.63.0 → 0.63.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +138 -2
  3. package/extensions/dm-web-access/SECURITY.md +5 -0
  4. package/extensions/dm-web-access/activity.js +65 -0
  5. package/extensions/dm-web-access/anysearch.js +158 -0
  6. package/extensions/dm-web-access/auth-fetch.js +131 -0
  7. package/extensions/dm-web-access/bocha.js +214 -0
  8. package/extensions/dm-web-access/brave.js +196 -0
  9. package/extensions/dm-web-access/brightdata-unlocker.js +202 -0
  10. package/extensions/dm-web-access/brightdata.js +334 -0
  11. package/extensions/dm-web-access/chrome-cookies.js +627 -0
  12. package/extensions/dm-web-access/content-find.js +114 -0
  13. package/extensions/dm-web-access/credential-source.js +150 -0
  14. package/extensions/dm-web-access/curator-page.js +3559 -0
  15. package/extensions/dm-web-access/curator-server.js +691 -0
  16. package/extensions/dm-web-access/data-uri-sanitize.js +312 -0
  17. package/extensions/dm-web-access/datalab-pdf-extract.js +346 -0
  18. package/extensions/dm-web-access/declared-web-links.js +167 -0
  19. package/extensions/dm-web-access/dm-web-fetch-demo.mp4 +0 -0
  20. package/extensions/dm-web-access/duckduckgo.js +118 -0
  21. package/extensions/dm-web-access/exa.js +401 -0
  22. package/extensions/dm-web-access/extract.js +1220 -0
  23. package/extensions/dm-web-access/feature-config.js +24 -0
  24. package/extensions/dm-web-access/fetch-params.js +81 -0
  25. package/extensions/dm-web-access/firecrawl.js +378 -0
  26. package/extensions/dm-web-access/gemini-adc.js +241 -0
  27. package/extensions/dm-web-access/gemini-api.js +258 -0
  28. package/extensions/dm-web-access/gemini-pdf-extract.js +74 -0
  29. package/extensions/dm-web-access/gemini-search.js +889 -0
  30. package/extensions/dm-web-access/gemini-url-context.js +97 -0
  31. package/extensions/dm-web-access/gemini-web-config.js +84 -0
  32. package/extensions/dm-web-access/gemini-web.js +351 -0
  33. package/extensions/dm-web-access/github-api.js +166 -0
  34. package/extensions/dm-web-access/github-extract.js +991 -0
  35. package/extensions/dm-web-access/github-issue-pr.js +750 -0
  36. package/extensions/dm-web-access/index.js +3117 -0
  37. package/extensions/dm-web-access/jina-search.js +242 -0
  38. package/extensions/dm-web-access/kagi.js +255 -0
  39. package/extensions/dm-web-access/kimi-search.js +214 -0
  40. package/extensions/dm-web-access/ollama.js +209 -0
  41. package/extensions/dm-web-access/openai-search.js +510 -0
  42. package/extensions/dm-web-access/package.json +34 -0
  43. package/extensions/dm-web-access/page-query.js +124 -0
  44. package/extensions/dm-web-access/parallel-mcp.js +223 -0
  45. package/extensions/dm-web-access/parallel.js +345 -0
  46. package/extensions/dm-web-access/pdf-extract.js +257 -0
  47. package/extensions/dm-web-access/perplexity.js +151 -0
  48. package/extensions/dm-web-access/querit.js +327 -0
  49. package/extensions/dm-web-access/query-rewrite.js +40 -0
  50. package/extensions/dm-web-access/render-search-error.js +80 -0
  51. package/extensions/dm-web-access/rsc-extract.js +347 -0
  52. package/extensions/dm-web-access/search1api.js +245 -0
  53. package/extensions/dm-web-access/searchinfinity.js +221 -0
  54. package/extensions/dm-web-access/searxng.js +223 -0
  55. package/extensions/dm-web-access/serpbase.js +205 -0
  56. package/extensions/dm-web-access/serpdive.js +238 -0
  57. package/extensions/dm-web-access/serper.js +200 -0
  58. package/extensions/dm-web-access/source-check.js +198 -0
  59. package/extensions/dm-web-access/ssrf-protection.js +436 -0
  60. package/extensions/dm-web-access/storage.js +451 -0
  61. package/extensions/dm-web-access/summary-model-scope.js +83 -0
  62. package/extensions/dm-web-access/summary-review.js +364 -0
  63. package/extensions/dm-web-access/tavily.js +199 -0
  64. package/extensions/dm-web-access/tinyfish.js +325 -0
  65. package/extensions/dm-web-access/utils.js +476 -0
  66. package/extensions/dm-web-access/valyu.js +189 -0
  67. package/extensions/dm-web-access/video-extract.js +336 -0
  68. package/extensions/dm-web-access/xai-search.js +285 -0
  69. package/extensions/dm-web-access/xcrawl.js +221 -0
  70. package/extensions/dm-web-access/youtube-extract.js +279 -0
  71. package/package.json +9 -1
@@ -0,0 +1,334 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { activityMonitor } from "./activity.js";
3
+ import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.js";
4
+ import { getWebSearchConfigPath } from "./utils.js";
5
+ const BRIGHTDATA_API_URL = "https://api.brightdata.com/request";
6
+ const CONFIG_PATH = getWebSearchConfigPath();
7
+ const SEARCH_TIMEOUT_MS = 60000;
8
+ const ZONE_PATTERN = /^[A-Za-z0-9_-]+$/;
9
+ const RECENCY_TBS = {
10
+ day: "qdr:d",
11
+ week: "qdr:w",
12
+ month: "qdr:m",
13
+ year: "qdr:y"
14
+ };
15
+ let cachedConfig = null;
16
+ function configParseDetail(err) {
17
+ const position = errorMessage(err).match(/at position \d+(?: \(line \d+ column \d+\))?/i);
18
+ return position ? `not valid JSON, ${position[0]}` : "not valid JSON";
19
+ }
20
+ function loadConfig() {
21
+ if (cachedConfig)
22
+ return cachedConfig;
23
+ if (!existsSync(CONFIG_PATH)) {
24
+ cachedConfig = {};
25
+ return cachedConfig;
26
+ }
27
+ const raw = readFileSync(CONFIG_PATH, "utf-8");
28
+ let parsed;
29
+ try {
30
+ parsed = JSON.parse(raw);
31
+ } catch (err) {
32
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${configParseDetail(err)}`);
33
+ }
34
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
35
+ throw new Error(`Invalid config in ${CONFIG_PATH}: expected a JSON object`);
36
+ }
37
+ cachedConfig = parsed;
38
+ return cachedConfig;
39
+ }
40
+ async function getApiKey(signal) {
41
+ return resolveCredential({
42
+ provider: "Bright Data",
43
+ configuredValue: loadConfig().brightdataApiKey,
44
+ environmentValue: process.env.BRIGHTDATA_API_KEY,
45
+ signal
46
+ });
47
+ }
48
+ async function requireApiKey(signal) {
49
+ const apiKey = await getApiKey(signal);
50
+ if (!apiKey) {
51
+ throw new Error(`Bright Data API key not found. Either:
52
+ ` + ` 1. Create ${CONFIG_PATH} with { "brightdataApiKey": "your-key" }
53
+ ` + ` 2. Set BRIGHTDATA_API_KEY environment variable
54
+ ` + "Get a key at https://brightdata.com/cp/setting/users");
55
+ }
56
+ return apiKey;
57
+ }
58
+ function normalizeZone(value) {
59
+ if (typeof value !== "string")
60
+ return null;
61
+ const trimmed = value.trim();
62
+ if (!trimmed)
63
+ return null;
64
+ return ZONE_PATTERN.test(trimmed) ? trimmed : null;
65
+ }
66
+ function serpZoneSetting() {
67
+ const fromEnv = process.env.BRIGHTDATA_SERP_ZONE;
68
+ if (typeof fromEnv === "string" && fromEnv.trim()) {
69
+ return { raw: fromEnv.trim(), label: "BRIGHTDATA_SERP_ZONE" };
70
+ }
71
+ const configured = loadConfig().brightdataSerpZone;
72
+ if (typeof configured === "string" && configured.trim()) {
73
+ return { raw: configured.trim(), label: `brightdataSerpZone in ${CONFIG_PATH}` };
74
+ }
75
+ return null;
76
+ }
77
+ function getSerpZone() {
78
+ const setting = serpZoneSetting();
79
+ return setting ? normalizeZone(setting.raw) : null;
80
+ }
81
+ function requireSerpZone() {
82
+ const setting = serpZoneSetting();
83
+ const zone = setting ? normalizeZone(setting.raw) : null;
84
+ if (zone)
85
+ return zone;
86
+ if (setting) {
87
+ throw new Error(`Bright Data SERP zone is invalid: ${setting.label} must be a zone name of letters, digits, "-", or "_" ` + `(got "${untrustedText(setting.raw, 60)}").
88
+ ` + "The zone must be of Bright Data type `serp`; a Web Unlocker zone (type `unblocker`) is a different product and does not return SERP JSON.\n" + "Create or rename one at https://brightdata.com/cp/zones");
89
+ }
90
+ throw new Error(`Bright Data SERP zone is invalid or missing. Either:
91
+ ` + ` 1. Create ${CONFIG_PATH} with { "brightdataSerpZone": "your_serp_zone" }
92
+ ` + ` 2. Set BRIGHTDATA_SERP_ZONE environment variable
93
+ ` + "The zone must be of Bright Data type `serp`; a Web Unlocker zone is a different product and does not return SERP JSON.\n" + "Create one at https://brightdata.com/cp/zones");
94
+ }
95
+ function normalizeCount(value) {
96
+ if (typeof value !== "number" || !Number.isFinite(value))
97
+ return 5;
98
+ return Math.max(1, Math.min(Math.floor(value), 20));
99
+ }
100
+ function normalizeDomain(value) {
101
+ let input = value.trim().toLowerCase();
102
+ if (!input)
103
+ return null;
104
+ if (input.startsWith("-"))
105
+ input = input.slice(1).trim();
106
+ if (!input)
107
+ return null;
108
+ try {
109
+ const parsed = input.includes("://") ? new URL(input) : new URL(`https://${input}`);
110
+ input = parsed.hostname;
111
+ } catch {
112
+ input = input.split("/")[0]?.split(":")[0] ?? "";
113
+ }
114
+ input = input.replace(/^\.+|\.+$/g, "");
115
+ return /^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(input) ? input : null;
116
+ }
117
+ function parseDomainFilter(domainFilter) {
118
+ const filters = { include: [], exclude: [] };
119
+ if (!domainFilter?.length)
120
+ return filters;
121
+ for (const raw of domainFilter) {
122
+ const domain = normalizeDomain(raw);
123
+ if (!domain)
124
+ continue;
125
+ const target = raw.trim().startsWith("-") ? filters.exclude : filters.include;
126
+ if (!target.includes(domain))
127
+ target.push(domain);
128
+ }
129
+ return filters;
130
+ }
131
+ function buildSearchQuery(query, filters) {
132
+ const parts = [query];
133
+ if (filters.include.length === 1) {
134
+ parts.push(`site:${filters.include[0]}`);
135
+ } else if (filters.include.length > 1) {
136
+ parts.push(filters.include.map((domain) => `site:${domain}`).join(" OR "));
137
+ }
138
+ for (const domain of filters.exclude) {
139
+ parts.push(`-site:${domain}`);
140
+ }
141
+ return parts.join(" ");
142
+ }
143
+ function buildSerpUrl(searchQuery, numResults, recencyFilter) {
144
+ const params = new URLSearchParams({ q: searchQuery });
145
+ params.set("num", String(Math.min(numResults + 5, 20)));
146
+ const tbs = recencyFilter ? RECENCY_TBS[recencyFilter] : undefined;
147
+ if (tbs)
148
+ params.set("tbs", tbs);
149
+ params.set("brd_json", "1");
150
+ return `https://www.google.com/search?${params.toString()}`;
151
+ }
152
+ function domainMatches(hostname, domain) {
153
+ return hostname === domain || hostname.endsWith(`.${domain}`);
154
+ }
155
+ function passesDomainFilters(url, filters) {
156
+ if (filters.include.length === 0 && filters.exclude.length === 0)
157
+ return true;
158
+ let hostname;
159
+ try {
160
+ hostname = new URL(url).hostname.toLowerCase();
161
+ } catch {
162
+ return false;
163
+ }
164
+ if (filters.exclude.some((domain) => domainMatches(hostname, domain)))
165
+ return false;
166
+ if (filters.include.length === 0)
167
+ return true;
168
+ return filters.include.some((domain) => domainMatches(hostname, domain));
169
+ }
170
+ function requestSignal(signal) {
171
+ const timeout = AbortSignal.timeout(SEARCH_TIMEOUT_MS);
172
+ return signal ? AbortSignal.any([signal, timeout]) : timeout;
173
+ }
174
+ function errorMessage(err) {
175
+ return err instanceof Error ? err.message : String(err);
176
+ }
177
+ const STATUS_SHAPED_PATTERN = /\b(?:error|status|http)[\s:=-]{1,4}(\d{3})\b/gi;
178
+ const QUOTA_SHAPED_PATTERN = /rate limit|quota|too many requests/gi;
179
+ function untrustedText(text, limit = 300) {
180
+ return text.replace(/\s+/g, " ").trim().slice(0, limit).replace(STATUS_SHAPED_PATTERN, "upstream $1").replace(QUOTA_SHAPED_PATTERN, "upstream rate-limit notice");
181
+ }
182
+ function invalidResponse(zone, message) {
183
+ return new Error(`Bright Data API returned invalid response for zone ${zone}: ${message}`);
184
+ }
185
+ function envelopeError(envelope, apiKey) {
186
+ const parts = [];
187
+ const { error, errors } = envelope;
188
+ if (typeof error === "string" && error.trim())
189
+ parts.push(error.trim());
190
+ else if (error && typeof error === "object")
191
+ parts.push(JSON.stringify(error));
192
+ if (Array.isArray(errors) && errors.length > 0)
193
+ parts.push(JSON.stringify(errors));
194
+ else if (typeof errors === "string" && errors.trim())
195
+ parts.push(errors.trim());
196
+ if (parts.length === 0)
197
+ return null;
198
+ for (const key of ["code", "error_code"]) {
199
+ const code = envelope[key];
200
+ if (typeof code === "string" && code.trim())
201
+ parts.push(`${key} ${code.trim()}`);
202
+ else if (typeof code === "number")
203
+ parts.push(`${key} ${code}`);
204
+ }
205
+ return untrustedText(redactCredential(parts.join(", "), apiKey), 200);
206
+ }
207
+ function parseSerpResponse(value, zone, apiKey) {
208
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
209
+ throw invalidResponse(zone, "expected an object envelope");
210
+ }
211
+ const envelope = value;
212
+ const upstreamError = envelopeError(envelope, apiKey);
213
+ if (upstreamError) {
214
+ throw invalidResponse(zone, `Bright Data reported an error instead of a SERP: ${upstreamError}`);
215
+ }
216
+ if (envelope.organic === undefined || envelope.organic === null) {
217
+ throw invalidResponse(zone, "expected an organic array and the envelope carried none. A `serp` zone queried with brd_json=1 " + "returns { organic: [...] }; a zone of type `unblocker`, or a missing brd_json=1, is the usual cause");
218
+ }
219
+ if (!Array.isArray(envelope.organic))
220
+ throw invalidResponse(zone, "expected organic array");
221
+ const organic = [];
222
+ for (const [index, entry] of envelope.organic.entries()) {
223
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
224
+ throw invalidResponse(zone, `expected organic[${index}] object`);
225
+ }
226
+ organic.push(entry);
227
+ }
228
+ return { organic };
229
+ }
230
+ function mapResults(organic, numResults, filters) {
231
+ if (!Array.isArray(organic))
232
+ return [];
233
+ const mapped = [];
234
+ for (const item of organic) {
235
+ const url = typeof item.link === "string" ? item.link.trim() : "";
236
+ if (!url || !passesDomainFilters(url, filters))
237
+ continue;
238
+ const title = typeof item.title === "string" ? item.title.trim() : "";
239
+ mapped.push({
240
+ title: title || `Source ${mapped.length + 1}`,
241
+ url,
242
+ snippet: typeof item.description === "string" ? item.description.replace(/\s+/g, " ").trim() : ""
243
+ });
244
+ if (mapped.length >= numResults)
245
+ break;
246
+ }
247
+ return mapped;
248
+ }
249
+ function buildAnswer(results) {
250
+ return results.map((result) => result.snippet ? `${result.snippet}
251
+ Source: ${result.title} (${result.url})` : `Source: ${result.title} (${result.url})`).join(`
252
+
253
+ `);
254
+ }
255
+ export function isBrightDataAvailable() {
256
+ try {
257
+ if (getSerpZone() === null)
258
+ return false;
259
+ return hasCredentialSource({
260
+ provider: "Bright Data",
261
+ configuredValue: loadConfig().brightdataApiKey,
262
+ environmentValue: process.env.BRIGHTDATA_API_KEY
263
+ });
264
+ } catch {
265
+ return false;
266
+ }
267
+ }
268
+ export async function searchWithBrightData(query, options = {}) {
269
+ const zone = requireSerpZone();
270
+ const apiKey = await requireApiKey(options.signal);
271
+ const numResults = normalizeCount(options.numResults);
272
+ const filters = parseDomainFilter(options.domainFilter);
273
+ const searchQuery = buildSearchQuery(query, filters);
274
+ const body = {
275
+ url: buildSerpUrl(searchQuery, numResults, options.recencyFilter),
276
+ zone,
277
+ format: "raw",
278
+ data_format: "parsed_light"
279
+ };
280
+ const activityId = activityMonitor.logStart({ type: "api", query: searchQuery });
281
+ let response;
282
+ try {
283
+ response = await fetch(BRIGHTDATA_API_URL, {
284
+ method: "POST",
285
+ headers: {
286
+ Authorization: `Bearer ${apiKey}`,
287
+ "Content-Type": "application/json"
288
+ },
289
+ body: JSON.stringify(body),
290
+ signal: requestSignal(options.signal)
291
+ });
292
+ } catch (err) {
293
+ const message = errorMessage(err);
294
+ const redactedMessage = redactCredential(message, apiKey);
295
+ if (redactedMessage.toLowerCase().includes("abort"))
296
+ activityMonitor.logComplete(activityId, 0);
297
+ else
298
+ activityMonitor.logError(activityId, redactedMessage);
299
+ if (redactedMessage === message)
300
+ throw err;
301
+ const redactedError = new Error(redactedMessage);
302
+ if (err instanceof Error)
303
+ redactedError.name = err.name;
304
+ throw redactedError;
305
+ }
306
+ if (!response.ok) {
307
+ activityMonitor.logComplete(activityId, response.status);
308
+ const errorText = untrustedText(redactCredential(await response.text(), apiKey));
309
+ throw new Error(`Bright Data API error ${response.status} for zone ${zone}: ${errorText}`);
310
+ }
311
+ const raw = await response.text();
312
+ if (!raw.trim()) {
313
+ activityMonitor.logComplete(activityId, response.status);
314
+ throw new Error(`Bright Data API returned empty response for zone ${zone}`);
315
+ }
316
+ let rawData;
317
+ try {
318
+ rawData = JSON.parse(raw);
319
+ } catch (err) {
320
+ activityMonitor.logComplete(activityId, response.status);
321
+ const body = untrustedText(redactCredential(raw, apiKey));
322
+ throw new Error(`Bright Data API returned invalid JSON for zone ${zone}: ${body}`);
323
+ }
324
+ let data;
325
+ try {
326
+ data = parseSerpResponse(rawData, zone, apiKey);
327
+ } catch (err) {
328
+ activityMonitor.logComplete(activityId, response.status);
329
+ throw err;
330
+ }
331
+ activityMonitor.logComplete(activityId, response.status);
332
+ const results = mapResults(data.organic, numResults, filters);
333
+ return { answer: buildAnswer(results), results };
334
+ }