@framers/agentos-ext-web-search 1.1.0 → 1.2.0

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 (39) hide show
  1. package/README.md +31 -188
  2. package/dist/index.d.ts +18 -11
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +57 -50
  5. package/dist/index.js.map +1 -1
  6. package/dist/services/searchProvider.d.ts +89 -3
  7. package/dist/services/searchProvider.d.ts.map +1 -1
  8. package/dist/services/searchProvider.js +342 -26
  9. package/dist/services/searchProvider.js.map +1 -1
  10. package/dist/tools/factCheck.d.ts +46 -88
  11. package/dist/tools/factCheck.d.ts.map +1 -1
  12. package/dist/tools/factCheck.js +141 -170
  13. package/dist/tools/factCheck.js.map +1 -1
  14. package/dist/tools/researchAggregator.d.ts +37 -91
  15. package/dist/tools/researchAggregator.d.ts.map +1 -1
  16. package/dist/tools/researchAggregator.js +141 -175
  17. package/dist/tools/researchAggregator.js.map +1 -1
  18. package/dist/tools/webSearch.d.ts +31 -59
  19. package/dist/tools/webSearch.d.ts.map +1 -1
  20. package/dist/tools/webSearch.js +76 -87
  21. package/dist/tools/webSearch.js.map +1 -1
  22. package/dist/types.d.ts.map +1 -1
  23. package/dist/types.js +2 -2
  24. package/dist/types.js.map +1 -1
  25. package/manifest.json +14 -9
  26. package/package.json +26 -16
  27. package/.eslintrc.json +0 -21
  28. package/CHANGELOG.md +0 -16
  29. package/src/index.ts +0 -123
  30. package/src/services/searchProvider.ts +0 -416
  31. package/src/tools/factCheck.ts +0 -318
  32. package/src/tools/researchAggregator.ts +0 -297
  33. package/src/tools/webSearch.ts +0 -128
  34. package/src/types.ts +0 -41
  35. package/test/integration.spec.ts +0 -173
  36. package/test/searchProvider.spec.ts +0 -153
  37. package/test/webSearch.spec.ts +0 -128
  38. package/tsconfig.json +0 -21
  39. package/vitest.config.ts +0 -32
@@ -1,416 +0,0 @@
1
- /**
2
- * Configuration for search providers
3
- */
4
- export interface SearchProviderConfig {
5
- serperApiKey?: string;
6
- serpApiKey?: string;
7
- braveApiKey?: string;
8
- maxRetries?: number;
9
- rateLimit?: {
10
- maxRequests: number;
11
- windowMs: number;
12
- };
13
- }
14
-
15
- /**
16
- * Search result structure
17
- */
18
- export interface SearchResult {
19
- title: string;
20
- url: string;
21
- snippet: string;
22
- position?: number;
23
- }
24
-
25
- /**
26
- * Provider-specific response
27
- */
28
- export interface ProviderResponse {
29
- provider: string;
30
- results: SearchResult[];
31
- metadata: {
32
- query: string;
33
- timestamp: string;
34
- responseTime?: number;
35
- fallback?: boolean;
36
- };
37
- }
38
-
39
- /**
40
- * Service for managing multiple search providers with fallback support
41
- *
42
- * @class SearchProviderService
43
- *
44
- * @example
45
- * ```typescript
46
- * const service = new SearchProviderService({
47
- * serperApiKey: 'your-key',
48
- * rateLimit: { maxRequests: 10, windowMs: 60000 }
49
- * });
50
- *
51
- * const results = await service.search('query');
52
- * ```
53
- */
54
- export class SearchProviderService {
55
- private config: SearchProviderConfig;
56
- private rateLimitState: Map<string, { count: number; resetTime: number }>;
57
-
58
- /**
59
- * Creates an instance of SearchProviderService
60
- *
61
- * @param {SearchProviderConfig} config - Configuration for the service
62
- */
63
- constructor(config: SearchProviderConfig) {
64
- this.config = {
65
- maxRetries: 3,
66
- rateLimit: {
67
- maxRequests: 10,
68
- windowMs: 60000
69
- },
70
- ...config
71
- };
72
- this.rateLimitState = new Map();
73
- }
74
-
75
- /**
76
- * Performs a search across available providers with automatic fallback
77
- *
78
- * @param {string} query - The search query
79
- * @param {Object} options - Search options
80
- * @param {number} [options.maxResults=10] - Maximum results to return
81
- * @param {string} [options.provider] - Specific provider to use
82
- * @returns {Promise<ProviderResponse>} Search results with metadata
83
- *
84
- * @throws {Error} If all providers fail and no fallback is available
85
- */
86
- async search(
87
- query: string,
88
- options: {
89
- maxResults?: number;
90
- provider?: string
91
- } = {}
92
- ): Promise<ProviderResponse> {
93
- const startTime = Date.now();
94
- const maxResults = options.maxResults || 10;
95
-
96
- // If specific provider requested, try only that one
97
- if (options.provider) {
98
- return this.searchWithProvider(query, options.provider, maxResults, startTime);
99
- }
100
-
101
- // Try providers in order of preference
102
- const providers = this.getAvailableProviders();
103
-
104
- for (const provider of providers) {
105
- try {
106
- if (await this.checkRateLimit(provider)) {
107
- return await this.searchWithProvider(query, provider, maxResults, startTime);
108
- }
109
- } catch (error) {
110
- console.warn(`Provider ${provider} failed:`, error);
111
- continue;
112
- }
113
- }
114
-
115
- // Final fallback to DuckDuckGo (no API key required)
116
- return this.searchDuckDuckGo(query, maxResults, startTime);
117
- }
118
-
119
- /**
120
- * Searches using a specific provider
121
- *
122
- * @private
123
- * @param {string} query - Search query
124
- * @param {string} provider - Provider name
125
- * @param {number} maxResults - Maximum results
126
- * @param {number} startTime - Request start timestamp
127
- * @returns {Promise<ProviderResponse>} Search results
128
- */
129
- private async searchWithProvider(
130
- query: string,
131
- provider: string,
132
- maxResults: number,
133
- startTime: number
134
- ): Promise<ProviderResponse> {
135
- let results: SearchResult[];
136
-
137
- switch (provider) {
138
- case 'serper':
139
- results = await this.searchSerper(query, maxResults);
140
- break;
141
- case 'serpapi':
142
- results = await this.searchSerpApi(query, maxResults);
143
- break;
144
- case 'brave':
145
- results = await this.searchBrave(query, maxResults);
146
- break;
147
- case 'duckduckgo':
148
- default:
149
- results = await this.searchDuckDuckGoAPI(query, maxResults);
150
- break;
151
- }
152
-
153
- return {
154
- provider,
155
- results: results.slice(0, maxResults),
156
- metadata: {
157
- query,
158
- timestamp: new Date().toISOString(),
159
- responseTime: Date.now() - startTime
160
- }
161
- };
162
- }
163
-
164
- /**
165
- * Gets list of available providers based on configured API keys
166
- *
167
- * @private
168
- * @returns {string[]} Array of available provider names
169
- */
170
- private getAvailableProviders(): string[] {
171
- const providers: string[] = [];
172
-
173
- if (this.config.serperApiKey) providers.push('serper');
174
- if (this.config.serpApiKey) providers.push('serpapi');
175
- if (this.config.braveApiKey) providers.push('brave');
176
-
177
- return providers;
178
- }
179
-
180
- /**
181
- * Checks if a provider is within rate limits
182
- *
183
- * @private
184
- * @param {string} provider - Provider name
185
- * @returns {Promise<boolean>} True if within limits
186
- */
187
- private async checkRateLimit(provider: string): Promise<boolean> {
188
- const now = Date.now();
189
- const state = this.rateLimitState.get(provider);
190
-
191
- if (!state || now > state.resetTime) {
192
- this.rateLimitState.set(provider, {
193
- count: 1,
194
- resetTime: now + this.config.rateLimit!.windowMs
195
- });
196
- return true;
197
- }
198
-
199
- if (state.count >= this.config.rateLimit!.maxRequests) {
200
- return false;
201
- }
202
-
203
- state.count++;
204
- return true;
205
- }
206
-
207
- /**
208
- * Search using Serper.dev API
209
- *
210
- * @private
211
- * @param {string} query - Search query
212
- * @param {number} maxResults - Maximum results
213
- * @returns {Promise<SearchResult[]>} Search results
214
- */
215
- private async searchSerper(query: string, maxResults: number): Promise<SearchResult[]> {
216
- const response = await fetch('https://google.serper.dev/search', {
217
- method: 'POST',
218
- headers: {
219
- 'X-API-KEY': this.config.serperApiKey!,
220
- 'Content-Type': 'application/json'
221
- },
222
- body: JSON.stringify({ q: query, num: maxResults })
223
- });
224
-
225
- if (!response.ok) throw new Error(`Serper API error: ${response.statusText}`);
226
-
227
- const data = await response.json();
228
- return (data.organic || []).map((item: any) => ({
229
- title: item.title,
230
- url: item.link,
231
- snippet: item.snippet,
232
- position: item.position
233
- }));
234
- }
235
-
236
- /**
237
- * Search using SerpAPI
238
- *
239
- * @private
240
- * @param {string} query - Search query
241
- * @param {number} maxResults - Maximum results
242
- * @returns {Promise<SearchResult[]>} Search results
243
- */
244
- private async searchSerpApi(query: string, maxResults: number): Promise<SearchResult[]> {
245
- const params = new URLSearchParams({
246
- q: query,
247
- api_key: this.config.serpApiKey!,
248
- num: maxResults.toString()
249
- });
250
-
251
- const response = await fetch(`https://serpapi.com/search?${params}`);
252
- if (!response.ok) throw new Error(`SerpAPI error: ${response.statusText}`);
253
-
254
- const data = await response.json();
255
- return (data.organic_results || []).map((item: any) => ({
256
- title: item.title,
257
- url: item.link,
258
- snippet: item.snippet,
259
- position: item.position
260
- }));
261
- }
262
-
263
- /**
264
- * Search using Brave Search API
265
- *
266
- * @private
267
- * @param {string} query - Search query
268
- * @param {number} maxResults - Maximum results
269
- * @returns {Promise<SearchResult[]>} Search results
270
- */
271
- private async searchBrave(query: string, maxResults: number): Promise<SearchResult[]> {
272
- const params = new URLSearchParams({
273
- q: query,
274
- count: maxResults.toString()
275
- });
276
-
277
- const response = await fetch(`https://api.search.brave.com/res/v1/web/search?${params}`, {
278
- headers: {
279
- 'X-Subscription-Token': this.config.braveApiKey!
280
- }
281
- });
282
-
283
- if (!response.ok) throw new Error(`Brave API error: ${response.statusText}`);
284
-
285
- const data = await response.json();
286
- return (data.web?.results || []).map((item: any, index: number) => ({
287
- title: item.title,
288
- url: item.url,
289
- snippet: item.description,
290
- position: index + 1
291
- }));
292
- }
293
-
294
- /**
295
- * Search using DuckDuckGo (HTML scraping fallback)
296
- *
297
- * @private
298
- * @param {string} query - Search query
299
- * @param {number} maxResults - Maximum results
300
- * @param {number} startTime - Request start timestamp
301
- * @returns {Promise<ProviderResponse>} Search results
302
- */
303
- private async searchDuckDuckGo(
304
- query: string,
305
- maxResults: number,
306
- startTime: number
307
- ): Promise<ProviderResponse> {
308
- try {
309
- const results = await this.searchDuckDuckGoAPI(query, maxResults);
310
- return {
311
- provider: 'duckduckgo',
312
- results,
313
- metadata: {
314
- query,
315
- timestamp: new Date().toISOString(),
316
- responseTime: Date.now() - startTime,
317
- fallback: true
318
- }
319
- };
320
- } catch (error) {
321
- // Return empty results rather than throwing
322
- return {
323
- provider: 'duckduckgo',
324
- results: [],
325
- metadata: {
326
- query,
327
- timestamp: new Date().toISOString(),
328
- responseTime: Date.now() - startTime,
329
- fallback: true
330
- }
331
- };
332
- }
333
- }
334
-
335
- /**
336
- * Search using DuckDuckGo instant answer API
337
- *
338
- * @private
339
- * @param {string} query - Search query
340
- * @param {number} maxResults - Maximum results
341
- * @returns {Promise<SearchResult[]>} Search results
342
- */
343
- private async searchDuckDuckGoAPI(query: string, maxResults: number): Promise<SearchResult[]> {
344
- // DuckDuckGo instant answer API (limited but free)
345
- const params = new URLSearchParams({
346
- q: query,
347
- format: 'json',
348
- no_html: '1',
349
- skip_disambig: '1'
350
- });
351
-
352
- const response = await fetch(`https://api.duckduckgo.com/?${params}`);
353
- const data = await response.json();
354
-
355
- const results: SearchResult[] = [];
356
-
357
- // Add instant answer if available
358
- if (data.AbstractText && data.AbstractURL) {
359
- results.push({
360
- title: data.Heading || query,
361
- url: data.AbstractURL,
362
- snippet: data.AbstractText
363
- });
364
- }
365
-
366
- // Add related topics
367
- if (data.RelatedTopics) {
368
- for (const topic of data.RelatedTopics.slice(0, maxResults - 1)) {
369
- if (topic.FirstURL && topic.Text) {
370
- results.push({
371
- title: topic.Text.split(' - ')[0] || topic.Text,
372
- url: topic.FirstURL,
373
- snippet: topic.Text
374
- });
375
- }
376
- }
377
- }
378
-
379
- return results.slice(0, maxResults);
380
- }
381
-
382
- /**
383
- * Gets recommended search providers with signup information
384
- *
385
- * @static
386
- * @returns {Array} Array of provider recommendations
387
- */
388
- static getRecommendedProviders() {
389
- return [
390
- {
391
- name: 'Serper',
392
- signupUrl: 'https://serper.dev',
393
- freeQuota: '2,500 queries/month',
394
- description: 'Google search results API'
395
- },
396
- {
397
- name: 'SerpAPI',
398
- signupUrl: 'https://serpapi.com',
399
- freeQuota: '100 searches/month',
400
- description: 'Multiple search engines API'
401
- },
402
- {
403
- name: 'Brave Search',
404
- signupUrl: 'https://brave.com/search-api',
405
- freeQuota: '2,000 queries/month',
406
- description: 'Privacy-focused search API'
407
- },
408
- {
409
- name: 'DuckDuckGo',
410
- signupUrl: 'No signup required',
411
- freeQuota: 'Unlimited (rate limited)',
412
- description: 'Privacy-focused, no API key needed'
413
- }
414
- ];
415
- }
416
- }