@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,325 @@
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 TINYFISH_SEARCH_URL = "https://api.search.tinyfish.ai";
6
+ const TINYFISH_FETCH_URL = "https://api.fetch.tinyfish.ai";
7
+ const CONFIG_PATH = getWebSearchConfigPath();
8
+ const SEARCH_TIMEOUT_MS = 60000;
9
+ const FETCH_TIMEOUT_MS = 150000;
10
+ const MAX_FETCH_URLS = 10;
11
+ const MAX_FETCH_PER_URL_TIMEOUT_MS = 110000;
12
+ let cachedConfig = null;
13
+ function loadConfig() {
14
+ if (cachedConfig)
15
+ return cachedConfig;
16
+ if (!existsSync(CONFIG_PATH)) {
17
+ cachedConfig = {};
18
+ return cachedConfig;
19
+ }
20
+ const raw = readFileSync(CONFIG_PATH, "utf-8");
21
+ try {
22
+ cachedConfig = JSON.parse(raw);
23
+ return cachedConfig;
24
+ } catch (err) {
25
+ const message = err instanceof Error ? err.message : String(err);
26
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
27
+ }
28
+ }
29
+ export function clearTinyFishConfigCache() {
30
+ cachedConfig = null;
31
+ }
32
+ async function getApiKey(signal) {
33
+ const key = await resolveCredential({
34
+ provider: "TinyFish",
35
+ configuredValue: loadConfig().tinyfishApiKey,
36
+ environmentValue: process.env.TINYFISH_API_KEY,
37
+ signal
38
+ });
39
+ if (!key) {
40
+ throw new Error(`TinyFish API key not found. Either:
41
+ ` + ` 1. Create ${CONFIG_PATH} with { "tinyfishApiKey": "your-key" }
42
+ ` + ` 2. Set TINYFISH_API_KEY environment variable
43
+ ` + "Get a key at https://agent.tinyfish.ai/api-keys");
44
+ }
45
+ return key;
46
+ }
47
+ export function isTinyFishAvailable() {
48
+ return hasCredentialSource({
49
+ provider: "TinyFish",
50
+ configuredValue: loadConfig().tinyfishApiKey,
51
+ environmentValue: process.env.TINYFISH_API_KEY
52
+ });
53
+ }
54
+ function errorMessage(err) {
55
+ return err instanceof Error ? err.message : String(err);
56
+ }
57
+ function requestSignal(signal, timeoutMs) {
58
+ const timeout = AbortSignal.timeout(timeoutMs);
59
+ return signal ? AbortSignal.any([signal, timeout]) : timeout;
60
+ }
61
+ function normalizeDomain(value) {
62
+ let input = value.trim().toLowerCase();
63
+ if (!input)
64
+ return null;
65
+ if (input.startsWith("-"))
66
+ input = input.slice(1).trim();
67
+ if (!input)
68
+ return null;
69
+ try {
70
+ const parsed = input.includes("://") ? new URL(input) : new URL(`https://${input}`);
71
+ input = parsed.hostname;
72
+ } catch {
73
+ input = input.split("/")[0]?.split(":")[0] ?? "";
74
+ }
75
+ input = input.replace(/^\.+|\.+$/g, "");
76
+ return /^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(input) ? input : null;
77
+ }
78
+ function mapDomainFilter(domainFilter) {
79
+ const includeDomains = [];
80
+ const excludeDomains = [];
81
+ for (const raw of domainFilter ?? []) {
82
+ const domain = normalizeDomain(raw);
83
+ if (!domain)
84
+ continue;
85
+ const target = raw.trim().startsWith("-") ? excludeDomains : includeDomains;
86
+ if (!target.includes(domain))
87
+ target.push(domain);
88
+ }
89
+ return { includeDomains, excludeDomains };
90
+ }
91
+ function recencyMinutes(filter) {
92
+ if (!filter)
93
+ return;
94
+ const minutes = {
95
+ day: 1440,
96
+ week: 10080,
97
+ month: 43200,
98
+ year: 525600
99
+ };
100
+ return minutes[filter];
101
+ }
102
+ function normalizeNumResults(value) {
103
+ if (typeof value !== "number" || !Number.isFinite(value))
104
+ return 5;
105
+ return Math.max(1, Math.min(Math.floor(value), 20));
106
+ }
107
+ function buildSearchUrl(query, options, page) {
108
+ const params = new URLSearchParams({ query });
109
+ const { includeDomains, excludeDomains } = mapDomainFilter(options.domainFilter);
110
+ if (includeDomains.length > 0)
111
+ params.set("include_domains", includeDomains.join(","));
112
+ if (excludeDomains.length > 0)
113
+ params.set("exclude_domains", excludeDomains.join(","));
114
+ const recency = recencyMinutes(options.recencyFilter);
115
+ if (recency !== undefined)
116
+ params.set("recency_minutes", String(recency));
117
+ if (page > 0)
118
+ params.set("page", String(page));
119
+ return `${TINYFISH_SEARCH_URL}?${params.toString()}`;
120
+ }
121
+ async function tinyFishJsonRequest(label, url, apiKey, init, timeoutMs, signal) {
122
+ let response;
123
+ try {
124
+ response = await fetch(url, {
125
+ ...init,
126
+ headers: {
127
+ "X-API-Key": apiKey,
128
+ ...init.body ? { "Content-Type": "application/json" } : {},
129
+ ...init.headers
130
+ },
131
+ signal: requestSignal(signal, timeoutMs)
132
+ });
133
+ } catch (err) {
134
+ const message = errorMessage(err);
135
+ const redactedMessage = redactCredential(message, apiKey);
136
+ if (redactedMessage === message)
137
+ throw err;
138
+ const redactedError = new Error(redactedMessage);
139
+ if (err instanceof Error)
140
+ redactedError.name = err.name;
141
+ throw redactedError;
142
+ }
143
+ const raw = await response.text();
144
+ if (!response.ok) {
145
+ throw new Error(`TinyFish ${label} API error ${response.status}: ${redactCredential(raw, apiKey).slice(0, 300)}`);
146
+ }
147
+ try {
148
+ return JSON.parse(raw);
149
+ } catch (err) {
150
+ throw new Error(`TinyFish ${label} API returned invalid JSON: ${errorMessage(err)}`);
151
+ }
152
+ }
153
+ function mapSearchResults(results) {
154
+ if (!Array.isArray(results))
155
+ return [];
156
+ return results.flatMap((item) => {
157
+ if (!item || typeof item.url !== "string" || item.url.trim().length === 0)
158
+ return [];
159
+ const url = item.url.trim();
160
+ return [{
161
+ title: typeof item.title === "string" && item.title.trim() ? item.title.trim() : url,
162
+ url,
163
+ snippet: typeof item.snippet === "string" ? item.snippet.replace(/\s+/g, " ").trim() : ""
164
+ }];
165
+ });
166
+ }
167
+ function deduplicateResults(results, limit) {
168
+ const seen = new Set;
169
+ const unique = [];
170
+ for (const result of results) {
171
+ if (seen.has(result.url))
172
+ continue;
173
+ seen.add(result.url);
174
+ unique.push(result);
175
+ if (unique.length >= limit)
176
+ break;
177
+ }
178
+ return unique;
179
+ }
180
+ function buildAnswer(results) {
181
+ return results.map((result) => {
182
+ if (result.snippet)
183
+ return `${result.snippet}
184
+ Source: ${result.title} (${result.url})`;
185
+ return `Source: ${result.title} (${result.url})`;
186
+ }).join(`
187
+
188
+ `);
189
+ }
190
+ function fetchPerUrlTimeout(value) {
191
+ if (typeof value !== "number" || !Number.isFinite(value))
192
+ return MAX_FETCH_PER_URL_TIMEOUT_MS;
193
+ return Math.max(1, Math.min(Math.floor(value), MAX_FETCH_PER_URL_TIMEOUT_MS));
194
+ }
195
+ function fetchBody(urls, options = {}) {
196
+ const body = {
197
+ urls,
198
+ format: "markdown",
199
+ per_url_timeout_ms: fetchPerUrlTimeout(options.timeoutMs)
200
+ };
201
+ const purpose = options.prompt?.trim();
202
+ if (purpose)
203
+ body.purpose = purpose.slice(0, 2000);
204
+ return body;
205
+ }
206
+ function findFetchError(errors, url) {
207
+ if (!Array.isArray(errors))
208
+ return;
209
+ return errors.find((item) => item?.url === url) ?? errors[0];
210
+ }
211
+ function fetchResultContent(result) {
212
+ if (typeof result.text === "string")
213
+ return result.text.trim();
214
+ if (result.text && typeof result.text === "object")
215
+ return JSON.stringify(result.text, null, 2);
216
+ return "";
217
+ }
218
+ function mapFetchResult(result, requestedUrl) {
219
+ if (!result)
220
+ return null;
221
+ const content = fetchResultContent(result);
222
+ if (!content)
223
+ return null;
224
+ return {
225
+ url: requestedUrl,
226
+ title: typeof result.title === "string" ? result.title.trim() : "",
227
+ content,
228
+ error: null
229
+ };
230
+ }
231
+ async function fetchBatch(urls, apiKey, signal, options = {}) {
232
+ return tinyFishJsonRequest("Fetch", TINYFISH_FETCH_URL, apiKey, { method: "POST", body: JSON.stringify(fetchBody(urls, options)) }, FETCH_TIMEOUT_MS, signal);
233
+ }
234
+ async function fetchInlineContent(urls, apiKey, signal) {
235
+ const content = [];
236
+ for (let offset = 0;offset < urls.length; offset += MAX_FETCH_URLS) {
237
+ const batch = urls.slice(offset, offset + MAX_FETCH_URLS);
238
+ const data = await fetchBatch(batch, apiKey, signal);
239
+ if (!Array.isArray(data.results) || !Array.isArray(data.errors)) {
240
+ throw new Error("TinyFish Fetch API returned an unexpected response shape");
241
+ }
242
+ for (const url of batch) {
243
+ const result = Array.isArray(data.results) ? data.results.find((item) => item?.url === url || item?.final_url === url) : undefined;
244
+ const mapped = mapFetchResult(result, url);
245
+ if (mapped)
246
+ content.push(mapped);
247
+ }
248
+ }
249
+ return content;
250
+ }
251
+ export async function searchWithTinyFish(query, options = {}) {
252
+ const apiKey = await getApiKey(options.signal);
253
+ const numResults = normalizeNumResults(options.numResults);
254
+ const activityId = activityMonitor.logStart({ type: "api", query });
255
+ try {
256
+ const combined = [];
257
+ const pages = numResults > 10 ? 2 : 1;
258
+ for (let page = 0;page < pages; page++) {
259
+ const data = await tinyFishJsonRequest("Search", buildSearchUrl(query, options, page), apiKey, { method: "GET" }, SEARCH_TIMEOUT_MS, options.signal);
260
+ if (!Array.isArray(data.results))
261
+ throw new Error("TinyFish Search API returned an unexpected response shape");
262
+ combined.push(...mapSearchResults(data.results));
263
+ if (data.results.length < 10)
264
+ break;
265
+ }
266
+ const results = deduplicateResults(combined, numResults);
267
+ const response = { answer: buildAnswer(results), results };
268
+ if (options.includeContent && results.length > 0) {
269
+ const inlineContent = await fetchInlineContent(results.map((result) => result.url), apiKey, options.signal);
270
+ if (inlineContent.length > 0)
271
+ response.inlineContent = inlineContent;
272
+ }
273
+ activityMonitor.logComplete(activityId, 200);
274
+ return response;
275
+ } catch (err) {
276
+ const message = errorMessage(err);
277
+ const redactedMessage = redactCredential(message, apiKey);
278
+ if (redactedMessage.toLowerCase().includes("abort"))
279
+ activityMonitor.logComplete(activityId, 0);
280
+ else
281
+ activityMonitor.logError(activityId, redactedMessage);
282
+ if (redactedMessage === message)
283
+ throw err;
284
+ const redactedError = new Error(redactedMessage);
285
+ if (err instanceof Error)
286
+ redactedError.name = err.name;
287
+ throw redactedError;
288
+ }
289
+ }
290
+ export async function extractWithTinyFish(url, signal, options = {}) {
291
+ const apiKey = await getApiKey(signal);
292
+ const activityId = activityMonitor.logStart({ type: "fetch", url });
293
+ try {
294
+ const data = await fetchBatch([url], apiKey, signal, options);
295
+ if (!Array.isArray(data.results) || !Array.isArray(data.errors)) {
296
+ throw new Error("TinyFish Fetch API returned an unexpected response shape");
297
+ }
298
+ const result = data.results.find((item) => item?.url === url || item?.final_url === url) ?? data.results[0];
299
+ const mapped = mapFetchResult(result, url);
300
+ if (mapped) {
301
+ activityMonitor.logComplete(activityId, 200);
302
+ return mapped;
303
+ }
304
+ const fetchError = findFetchError(data.errors, url);
305
+ if (fetchError) {
306
+ const status = typeof fetchError.status === "number" ? ` (HTTP ${fetchError.status})` : "";
307
+ throw new Error(`TinyFish Fetch failed for ${url}: ${fetchError.error || "unknown error"}${status}`);
308
+ }
309
+ activityMonitor.logComplete(activityId, 200);
310
+ return null;
311
+ } catch (err) {
312
+ const message = errorMessage(err);
313
+ const redactedMessage = redactCredential(message, apiKey);
314
+ if (redactedMessage.toLowerCase().includes("abort"))
315
+ activityMonitor.logComplete(activityId, 0);
316
+ else
317
+ activityMonitor.logError(activityId, redactedMessage);
318
+ if (redactedMessage === message)
319
+ throw err;
320
+ const redactedError = new Error(redactedMessage);
321
+ if (err instanceof Error)
322
+ redactedError.name = err.name;
323
+ throw redactedError;
324
+ }
325
+ }