@aria-cli/tools 1.0.9 → 1.0.11

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 (241) hide show
  1. package/package.json +9 -5
  2. package/src/__tests__/web-fetch-download.test.ts +0 -433
  3. package/src/__tests__/web-tools.test.ts +0 -619
  4. package/src/ask-user-interaction.ts +0 -33
  5. package/src/cache/web-cache.ts +0 -110
  6. package/src/definitions/arion.ts +0 -118
  7. package/src/definitions/browser/browser.ts +0 -502
  8. package/src/definitions/browser/index.ts +0 -5
  9. package/src/definitions/browser/pw-downloads.ts +0 -142
  10. package/src/definitions/browser/pw-interactions.ts +0 -282
  11. package/src/definitions/browser/pw-responses.ts +0 -98
  12. package/src/definitions/browser/pw-session.ts +0 -405
  13. package/src/definitions/browser/pw-shared.ts +0 -85
  14. package/src/definitions/browser/pw-snapshot.ts +0 -383
  15. package/src/definitions/browser/pw-state.ts +0 -101
  16. package/src/definitions/browser/types.ts +0 -203
  17. package/src/definitions/code-intelligence.ts +0 -526
  18. package/src/definitions/core.ts +0 -118
  19. package/src/definitions/delegation.ts +0 -567
  20. package/src/definitions/deploy.ts +0 -73
  21. package/src/definitions/filesystem.ts +0 -217
  22. package/src/definitions/frg.ts +0 -67
  23. package/src/definitions/index.ts +0 -28
  24. package/src/definitions/memory.ts +0 -150
  25. package/src/definitions/messaging.ts +0 -734
  26. package/src/definitions/meta.ts +0 -392
  27. package/src/definitions/network.ts +0 -179
  28. package/src/definitions/outlook.ts +0 -318
  29. package/src/definitions/patch/apply-patch.ts +0 -235
  30. package/src/definitions/patch/fuzzy-match.ts +0 -217
  31. package/src/definitions/patch/index.ts +0 -1
  32. package/src/definitions/patch/patch-parser.ts +0 -297
  33. package/src/definitions/patch/sandbox-paths.ts +0 -129
  34. package/src/definitions/process/index.ts +0 -5
  35. package/src/definitions/process/process-registry.ts +0 -303
  36. package/src/definitions/process/process.ts +0 -456
  37. package/src/definitions/process/pty-keys.ts +0 -298
  38. package/src/definitions/process/session-slug.ts +0 -147
  39. package/src/definitions/quip.ts +0 -225
  40. package/src/definitions/search.ts +0 -67
  41. package/src/definitions/session-history.ts +0 -79
  42. package/src/definitions/shell.ts +0 -202
  43. package/src/definitions/slack.ts +0 -211
  44. package/src/definitions/web.ts +0 -119
  45. package/src/executors/apply-patch.ts +0 -1035
  46. package/src/executors/arion.ts +0 -199
  47. package/src/executors/code-intelligence.ts +0 -1179
  48. package/src/executors/deploy.ts +0 -1066
  49. package/src/executors/filesystem.ts +0 -1428
  50. package/src/executors/frg-freshness.ts +0 -743
  51. package/src/executors/frg.ts +0 -394
  52. package/src/executors/index.ts +0 -280
  53. package/src/executors/learning-meta.ts +0 -1367
  54. package/src/executors/lsp-client.ts +0 -355
  55. package/src/executors/memory.ts +0 -978
  56. package/src/executors/meta.ts +0 -293
  57. package/src/executors/process-registry.ts +0 -570
  58. package/src/executors/pty-session-store.ts +0 -43
  59. package/src/executors/pty.ts +0 -342
  60. package/src/executors/restart.ts +0 -133
  61. package/src/executors/search-freshness.ts +0 -249
  62. package/src/executors/search-types.ts +0 -98
  63. package/src/executors/search.ts +0 -89
  64. package/src/executors/self-diagnose.ts +0 -552
  65. package/src/executors/session-history.ts +0 -435
  66. package/src/executors/shell-safety.ts +0 -519
  67. package/src/executors/shell.ts +0 -1243
  68. package/src/executors/utils.ts +0 -40
  69. package/src/executors/web.ts +0 -786
  70. package/src/extraction/content-extraction.ts +0 -281
  71. package/src/extraction/index.ts +0 -5
  72. package/src/headless-control-contract.ts +0 -1149
  73. package/src/index.ts +0 -788
  74. package/src/local-control-http-auth.ts +0 -2
  75. package/src/mcp/client.ts +0 -218
  76. package/src/mcp/connection.ts +0 -568
  77. package/src/mcp/index.ts +0 -11
  78. package/src/mcp/jsonrpc.ts +0 -195
  79. package/src/mcp/types.ts +0 -199
  80. package/src/network-control-adapter.ts +0 -88
  81. package/src/network-runtime/address-types.ts +0 -218
  82. package/src/network-runtime/db-owner-fencing.ts +0 -91
  83. package/src/network-runtime/delivery-receipts.ts +0 -372
  84. package/src/network-runtime/direct-endpoint-authority.ts +0 -35
  85. package/src/network-runtime/index.ts +0 -316
  86. package/src/network-runtime/local-control-contract.ts +0 -784
  87. package/src/network-runtime/node-store-contract.ts +0 -46
  88. package/src/network-runtime/pair-route-contract.ts +0 -97
  89. package/src/network-runtime/peer-capabilities.ts +0 -48
  90. package/src/network-runtime/peer-principal-ref.ts +0 -20
  91. package/src/network-runtime/peer-state-machine.ts +0 -160
  92. package/src/network-runtime/protocol-schemas.ts +0 -265
  93. package/src/network-runtime/runtime-bootstrap-contract.ts +0 -83
  94. package/src/outlook/desktop-session.ts +0 -409
  95. package/src/policy.ts +0 -171
  96. package/src/providers/brave.ts +0 -80
  97. package/src/providers/duckduckgo.ts +0 -199
  98. package/src/providers/exa.ts +0 -85
  99. package/src/providers/firecrawl.ts +0 -77
  100. package/src/providers/index.ts +0 -8
  101. package/src/providers/jina.ts +0 -70
  102. package/src/providers/router.ts +0 -121
  103. package/src/providers/search-provider.ts +0 -74
  104. package/src/providers/tavily.ts +0 -74
  105. package/src/quip/desktop-session.ts +0 -435
  106. package/src/registry/index.ts +0 -1
  107. package/src/registry/registry.ts +0 -905
  108. package/src/runtime-socket-local-control-client.ts +0 -632
  109. package/src/security/dns-normalization.ts +0 -34
  110. package/src/security/dns-pinning.ts +0 -138
  111. package/src/security/external-content.ts +0 -129
  112. package/src/security/ssrf.ts +0 -207
  113. package/src/slack/desktop-session.ts +0 -493
  114. package/src/tool-factory.ts +0 -91
  115. package/src/types.ts +0 -1341
  116. package/src/utils/retry.ts +0 -163
  117. package/src/utils/safe-parse-json.ts +0 -176
  118. package/src/utils/url.ts +0 -20
  119. package/tests/benchmarks/registry.bench.ts +0 -57
  120. package/tests/cache/web-cache.test.ts +0 -147
  121. package/tests/critical-integration.test.ts +0 -1465
  122. package/tests/definitions/apply-patch.test.ts +0 -586
  123. package/tests/definitions/browser.test.ts +0 -495
  124. package/tests/definitions/delegation-pause-resume.test.ts +0 -758
  125. package/tests/definitions/execution.test.ts +0 -671
  126. package/tests/definitions/messaging-inbox-scope.test.ts +0 -229
  127. package/tests/definitions/messaging.test.ts +0 -1468
  128. package/tests/definitions/outlook.test.ts +0 -30
  129. package/tests/definitions/process.test.ts +0 -469
  130. package/tests/definitions/slack.test.ts +0 -28
  131. package/tests/definitions/tool-inventory.test.ts +0 -218
  132. package/tests/e2e/delegation-quest-orchestration.e2e.test.ts +0 -433
  133. package/tests/e2e/memory-tool-discovery-contract.e2e.test.ts +0 -81
  134. package/tests/executors/apply-patch.test.ts +0 -538
  135. package/tests/executors/arion.test.ts +0 -309
  136. package/tests/executors/conversation-primitives.test.ts +0 -250
  137. package/tests/executors/deploy.test.ts +0 -746
  138. package/tests/executors/filesystem-tools.test.ts +0 -357
  139. package/tests/executors/filesystem.test.ts +0 -959
  140. package/tests/executors/frg-freshness.test.ts +0 -136
  141. package/tests/executors/frg-merge.test.ts +0 -70
  142. package/tests/executors/frg-session-content.test.ts +0 -40
  143. package/tests/executors/frg.test.ts +0 -56
  144. package/tests/executors/memory-bugfixes.test.ts +0 -257
  145. package/tests/executors/memory-real-memoria.integration.test.ts +0 -316
  146. package/tests/executors/memory.test.ts +0 -853
  147. package/tests/executors/meta-tools.test.ts +0 -411
  148. package/tests/executors/meta.test.ts +0 -683
  149. package/tests/executors/path-containment.test.ts +0 -51
  150. package/tests/executors/process-registry.test.ts +0 -505
  151. package/tests/executors/pty.test.ts +0 -664
  152. package/tests/executors/quest-security.test.ts +0 -249
  153. package/tests/executors/read-file-media.test.ts +0 -230
  154. package/tests/executors/recall-knowledge-schema.test.ts +0 -209
  155. package/tests/executors/recall-tags.test.ts +0 -278
  156. package/tests/executors/remember-null-safety.contract.test.ts +0 -41
  157. package/tests/executors/restart.test.ts +0 -67
  158. package/tests/executors/search-unified.test.ts +0 -381
  159. package/tests/executors/session-history.test.ts +0 -340
  160. package/tests/executors/session-transcript.test.ts +0 -561
  161. package/tests/executors/shell-abort.test.ts +0 -416
  162. package/tests/executors/shell-env-blocklist.test.ts +0 -648
  163. package/tests/executors/shell-env-process.test.ts +0 -245
  164. package/tests/executors/shell-process-registry.test.ts +0 -334
  165. package/tests/executors/shell-tools.test.ts +0 -393
  166. package/tests/executors/shell.test.ts +0 -690
  167. package/tests/executors/web-abort-vs-timeout.test.ts +0 -213
  168. package/tests/executors/web-integration.test.ts +0 -633
  169. package/tests/executors/web-symlink.test.ts +0 -18
  170. package/tests/executors/web.test.ts +0 -1400
  171. package/tests/executors/write-stdin.test.ts +0 -145
  172. package/tests/extraction/content-extraction.test.ts +0 -153
  173. package/tests/guards/tools-default-test-lane.integration.test.ts +0 -21
  174. package/tests/guards/tools-package-test-commands.e2e.test.ts +0 -43
  175. package/tests/guards/tools-test-lane-manifest.contract.test.ts +0 -76
  176. package/tests/guards/tools-vitest-workspace-alias.contract.test.ts +0 -63
  177. package/tests/helpers/async-waits.ts +0 -53
  178. package/tests/integration/headless-control-contract.integration.test.ts +0 -153
  179. package/tests/integration/memory-tool-schema-parity.integration.test.ts +0 -67
  180. package/tests/integration/meta-tools-round-trip.integration.test.ts +0 -506
  181. package/tests/integration/quest-round-trip.test.ts +0 -303
  182. package/tests/integration/registry-executor-flow.test.ts +0 -85
  183. package/tests/integration.test.ts +0 -177
  184. package/tests/loading-tier.test.ts +0 -126
  185. package/tests/mcp/client-reconnect.test.ts +0 -267
  186. package/tests/mcp/connection.test.ts +0 -846
  187. package/tests/mcp/injectable-logger.test.ts +0 -83
  188. package/tests/mcp/jsonrpc.test.ts +0 -109
  189. package/tests/mcp/lifecycle.test.ts +0 -879
  190. package/tests/network-runtime/address-types.contract.test.ts +0 -143
  191. package/tests/network-runtime/continuity-bind-schema.contract.test.ts +0 -203
  192. package/tests/network-runtime/local-control-contract.test.ts +0 -869
  193. package/tests/network-runtime/local-control-invite-token.contract.test.ts +0 -146
  194. package/tests/network-runtime/node-store-contract.test.ts +0 -11
  195. package/tests/network-runtime/pair-protocol-nodeid.contract.test.ts +0 -15
  196. package/tests/network-runtime/peer-state-machine.contract.test.ts +0 -148
  197. package/tests/network-runtime/protocol-schemas.contract.test.ts +0 -512
  198. package/tests/network-runtime/relay-pending-nodeid.contract.test.ts +0 -62
  199. package/tests/network-runtime/runtime-bootstrap-contract.test.ts +0 -227
  200. package/tests/network-runtime/runtime-socket-local-control-client.test.ts +0 -621
  201. package/tests/network-runtime/wait-for-message-script.test.ts +0 -288
  202. package/tests/parallel.test.ts +0 -71
  203. package/tests/policy.test.ts +0 -184
  204. package/tests/print-default-test-lane.ts +0 -14
  205. package/tests/print-test-lane-manifest.ts +0 -22
  206. package/tests/providers/brave.test.ts +0 -159
  207. package/tests/providers/duckduckgo.test.ts +0 -207
  208. package/tests/providers/exa.test.ts +0 -175
  209. package/tests/providers/firecrawl.test.ts +0 -168
  210. package/tests/providers/jina.test.ts +0 -144
  211. package/tests/providers/router.test.ts +0 -328
  212. package/tests/providers/tavily.test.ts +0 -165
  213. package/tests/registry/discovery.test.ts +0 -154
  214. package/tests/registry/injectable-logger.test.ts +0 -230
  215. package/tests/registry/input-validation.test.ts +0 -361
  216. package/tests/registry/interface-completeness.test.ts +0 -85
  217. package/tests/registry/mcp-integration.test.ts +0 -103
  218. package/tests/registry/mcp-read-only-hint.test.ts +0 -60
  219. package/tests/registry/memoria-discovery.test.ts +0 -390
  220. package/tests/registry/nested-validation.test.ts +0 -283
  221. package/tests/registry/pseudo-tool-filtering.test.ts +0 -258
  222. package/tests/registry/registration-lifecycle.test.ts +0 -133
  223. package/tests/registry-validation.test.ts +0 -424
  224. package/tests/registry.test.ts +0 -460
  225. package/tests/security/dns-pinning.test.ts +0 -162
  226. package/tests/security/external-content.test.ts +0 -144
  227. package/tests/security/ssrf.test.ts +0 -118
  228. package/tests/shell-safety-integration.test.ts +0 -32
  229. package/tests/shell-safety.test.ts +0 -365
  230. package/tests/slack/desktop-session.test.ts +0 -50
  231. package/tests/test-lane-manifest.ts +0 -440
  232. package/tests/test-utils.ts +0 -27
  233. package/tests/tool-factory.test.ts +0 -188
  234. package/tests/utils/retry.test.ts +0 -231
  235. package/tests/utils/url.test.ts +0 -63
  236. package/tsconfig.cjs.json +0 -24
  237. package/tsconfig.json +0 -12
  238. package/vitest.config.ts +0 -55
  239. package/vitest.e2e.config.ts +0 -24
  240. package/vitest.integration.config.ts +0 -24
  241. package/vitest.native.config.ts +0 -24
@@ -1,80 +0,0 @@
1
- import type {
2
- SearchProvider,
3
- SearchOptions,
4
- SearchResult,
5
- SearchProviderEnv,
6
- } from "./search-provider.js";
7
- import { createProviderAbortSignal, resolveSearchProviderEnv } from "./search-provider.js";
8
-
9
- export class BraveSearchProvider implements SearchProvider {
10
- readonly name = "brave";
11
- readonly requiresApiKey = true;
12
- readonly priority = 1;
13
-
14
- constructor(
15
- private readonly env: SearchProviderEnv | Record<string, string | undefined> = process.env,
16
- ) {}
17
-
18
- isAvailable(): boolean {
19
- const apiKey = resolveSearchProviderEnv(this.env).BRAVE_API_KEY;
20
- return Boolean(apiKey && apiKey.trim().length > 0);
21
- }
22
-
23
- async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {
24
- const apiKey = resolveSearchProviderEnv(this.env).BRAVE_API_KEY;
25
- if (!apiKey) {
26
- throw new Error("BRAVE_API_KEY environment variable is not set");
27
- }
28
-
29
- const limit = options?.limit ?? 5;
30
-
31
- const url = new URL("https://api.search.brave.com/res/v1/web/search");
32
- url.searchParams.set("q", query);
33
- url.searchParams.set("count", String(limit));
34
-
35
- // Handle timeRange option (map to Brave's freshness parameter)
36
- if (options?.timeRange) {
37
- const freshnessMap: Record<string, string> = {
38
- day: "pd",
39
- week: "pw",
40
- month: "pm",
41
- year: "py",
42
- };
43
- const freshness = freshnessMap[options.timeRange];
44
- if (freshness) {
45
- url.searchParams.set("freshness", freshness);
46
- }
47
- }
48
-
49
- const { signal, cleanup } = createProviderAbortSignal(30_000, options?.signal);
50
-
51
- try {
52
- const response = await fetch(url.toString(), {
53
- method: "GET",
54
- headers: {
55
- Accept: "application/json",
56
- "X-Subscription-Token": apiKey,
57
- },
58
- signal,
59
- });
60
-
61
- if (!response.ok) {
62
- throw new Error(`Brave Search API error: ${response.status} ${response.statusText}`);
63
- }
64
-
65
- const json = await response.json();
66
- const webResults = json.web?.results || [];
67
-
68
- return webResults.map(
69
- (r: { title: string; url: string; description: string; relevance_score?: number }) => ({
70
- title: r.title,
71
- url: r.url,
72
- content: r.description,
73
- score: r.relevance_score,
74
- }),
75
- );
76
- } finally {
77
- cleanup();
78
- }
79
- }
80
- }
@@ -1,199 +0,0 @@
1
- import type { SearchProvider, SearchOptions, SearchResult } from "./search-provider.js";
2
- import { createProviderAbortSignal } from "./search-provider.js";
3
-
4
- export class DuckDuckGoSearchProvider implements SearchProvider {
5
- readonly name = "duckduckgo";
6
- readonly requiresApiKey = false;
7
- readonly priority = 6;
8
-
9
- isAvailable(): boolean {
10
- // DuckDuckGo is always available (no API key required)
11
- return true;
12
- }
13
-
14
- async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {
15
- const limit = options?.limit ?? 5;
16
-
17
- // DuckDuckGo HTML search endpoint (we'll parse the HTML response)
18
- // The Instant Answer API doesn't provide web search results, only factoids
19
- const url = new URL("https://html.duckduckgo.com/html/");
20
- url.searchParams.set("q", query);
21
-
22
- const { signal, cleanup } = createProviderAbortSignal(30_000, options?.signal);
23
-
24
- try {
25
- const response = await fetch(url.toString(), {
26
- method: "GET",
27
- signal,
28
- headers: {
29
- "User-Agent": "Mozilla/5.0 (compatible; ARIA/1.0)",
30
- },
31
- });
32
-
33
- if (!response.ok) {
34
- throw new Error(`DuckDuckGo Search error: ${response.status} ${response.statusText}`);
35
- }
36
-
37
- const html = await response.text();
38
-
39
- // Parse HTML to extract search results
40
- // DuckDuckGo HTML results have result__a (title/url) and result__snippet (description)
41
- const results: SearchResult[] = [];
42
-
43
- // First pass: extract all result__a links (title + url)
44
- const linkMap = new Map<string, { title: string; url: string }>();
45
- const linkRegex =
46
- /<a(?=[^>]*\bclass=(["'])[^"']*\bresult__a\b[^"']*\1)(?=[^>]*\bhref=(["'])(.*?)\2)[^>]*>([\s\S]*?)<\/a>/gi;
47
- let match;
48
- let resultAnchorCount = 0;
49
- while ((match = linkRegex.exec(html)) !== null) {
50
- const rawHref = match[3];
51
- const rawTitle = match[4];
52
- if (!rawHref || !rawTitle) {
53
- continue;
54
- }
55
- resultAnchorCount++;
56
- const url = this.resolveDuckDuckGoResultUrl(rawHref);
57
- if (!url) {
58
- continue;
59
- }
60
- const title = this.stripHtmlTags(rawTitle);
61
- if (!title) {
62
- continue;
63
- }
64
- linkMap.set(url, { title, url });
65
- }
66
-
67
- // If DDG returned result anchors but we extracted zero usable URLs, parsing is stale.
68
- // Throw to allow router fallback rather than silently returning empty results.
69
- if (resultAnchorCount > 0 && linkMap.size === 0) {
70
- throw new Error("Could not resolve any result URLs from DuckDuckGo response");
71
- }
72
-
73
- // Second pass: extract snippets
74
- const snippetRegex =
75
- /<(?:a|div|span)(?=[^>]*\bclass=(["'])[^"']*\bresult__snippet\b[^"']*\1)[^>]*>([\s\S]*?)<\/(?:a|div|span)>/gi;
76
- const snippets: string[] = [];
77
- while ((match = snippetRegex.exec(html)) !== null) {
78
- const rawSnippet = match[2];
79
- snippets.push(this.stripHtmlTags(rawSnippet ?? ""));
80
- }
81
-
82
- // Combine results (matching by order, which should align)
83
- const linkEntries = Array.from(linkMap.values());
84
- for (let i = 0; i < Math.min(linkEntries.length, limit); i++) {
85
- const link = linkEntries[i];
86
- if (!link) {
87
- continue;
88
- }
89
- const snippet = snippets[i] || link.title;
90
- results.push({
91
- title: link.title,
92
- url: link.url,
93
- content: snippet,
94
- });
95
- }
96
-
97
- return results;
98
- } finally {
99
- cleanup();
100
- }
101
- }
102
-
103
- /**
104
- * Strip HTML tags from a string
105
- */
106
- private stripHtmlTags(html: string): string {
107
- return html
108
- .replace(/<[^>]*>/g, "")
109
- .replace(/&nbsp;/g, " ")
110
- .replace(/&amp;/g, "&")
111
- .replace(/&lt;/g, "<")
112
- .replace(/&gt;/g, ">")
113
- .replace(/&quot;/g, '"')
114
- .replace(/&#39;/g, "'")
115
- .trim();
116
- }
117
-
118
- /**
119
- * Decode HTML entities in URLs
120
- */
121
- private decodeHtmlEntities(text: string): string {
122
- return text
123
- .replace(/&amp;/g, "&")
124
- .replace(/&lt;/g, "<")
125
- .replace(/&gt;/g, ">")
126
- .replace(/&quot;/g, '"')
127
- .replace(/&#39;/g, "'");
128
- }
129
-
130
- private resolveDuckDuckGoResultUrl(rawHref: string): string | null {
131
- const decodedHref = this.decodeHtmlEntities(rawHref).trim();
132
- if (!decodedHref) {
133
- return null;
134
- }
135
-
136
- let absoluteHref = decodedHref;
137
- if (absoluteHref.startsWith("//")) {
138
- absoluteHref = `https:${absoluteHref}`;
139
- } else if (absoluteHref.startsWith("/")) {
140
- absoluteHref = `https://duckduckgo.com${absoluteHref}`;
141
- }
142
-
143
- let parsed: URL;
144
- try {
145
- parsed = new URL(absoluteHref);
146
- } catch {
147
- return null;
148
- }
149
-
150
- const hostname = parsed.hostname.toLowerCase();
151
- if (hostname === "duckduckgo.com" || hostname.endsWith(".duckduckgo.com")) {
152
- const redirected = parsed.searchParams.get("uddg");
153
- if (!redirected) {
154
- return null;
155
- }
156
- return this.normalizeResultUrl(redirected);
157
- }
158
-
159
- return this.normalizeResultUrl(absoluteHref);
160
- }
161
-
162
- private normalizeResultUrl(candidate: string): string | null {
163
- let value = candidate.trim();
164
- if (!value) {
165
- return null;
166
- }
167
-
168
- if (value.startsWith("//")) {
169
- value = `https:${value}`;
170
- }
171
-
172
- // Some uddg values are encoded multiple times; decode at most twice.
173
- for (let i = 0; i < 2; i++) {
174
- try {
175
- const decoded = decodeURIComponent(value);
176
- if (decoded === value) {
177
- break;
178
- }
179
- value = decoded;
180
- } catch {
181
- break;
182
- }
183
- }
184
-
185
- try {
186
- const parsed = new URL(value);
187
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
188
- return null;
189
- }
190
- const hostname = parsed.hostname.toLowerCase();
191
- if (hostname === "duckduckgo.com" || hostname.endsWith(".duckduckgo.com")) {
192
- return null;
193
- }
194
- return parsed.toString();
195
- } catch {
196
- return null;
197
- }
198
- }
199
- }
@@ -1,85 +0,0 @@
1
- import type {
2
- SearchProvider,
3
- SearchOptions,
4
- SearchResult,
5
- SearchProviderEnv,
6
- } from "./search-provider.js";
7
- import { createProviderAbortSignal, resolveSearchProviderEnv } from "./search-provider.js";
8
-
9
- export class ExaSearchProvider implements SearchProvider {
10
- readonly name = "exa";
11
- readonly requiresApiKey = true;
12
- readonly priority = 3;
13
-
14
- constructor(
15
- private readonly env: SearchProviderEnv | Record<string, string | undefined> = process.env,
16
- ) {}
17
-
18
- isAvailable(): boolean {
19
- const apiKey = resolveSearchProviderEnv(this.env).EXA_API_KEY;
20
- return Boolean(apiKey && apiKey.trim().length > 0);
21
- }
22
-
23
- async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {
24
- const apiKey = resolveSearchProviderEnv(this.env).EXA_API_KEY;
25
- if (!apiKey) {
26
- throw new Error("EXA_API_KEY environment variable is not set");
27
- }
28
-
29
- const limit = options?.limit ?? 5;
30
-
31
- const body: Record<string, unknown> = {
32
- query,
33
- numResults: limit,
34
- useAutoprompt: true,
35
- contents: {
36
- text: true,
37
- },
38
- };
39
-
40
- // Domain filtering
41
- if (options?.domains) {
42
- body.includeDomains = options.domains;
43
- }
44
- if (options?.excludeDomains) {
45
- body.excludeDomains = options.excludeDomains;
46
- }
47
-
48
- const { signal, cleanup } = createProviderAbortSignal(30_000, options?.signal);
49
-
50
- try {
51
- const response = await fetch("https://api.exa.ai/search", {
52
- method: "POST",
53
- headers: {
54
- "Content-Type": "application/json",
55
- "x-api-key": apiKey,
56
- },
57
- body: JSON.stringify(body),
58
- signal,
59
- });
60
-
61
- if (!response.ok) {
62
- throw new Error(`Exa Search API error: ${response.status} ${response.statusText}`);
63
- }
64
-
65
- const json = await response.json();
66
- const results = (json.results || []) as Array<{
67
- title: string;
68
- url: string;
69
- text?: string;
70
- score?: number;
71
- publishedDate?: string;
72
- }>;
73
-
74
- return results.map((r) => ({
75
- title: r.title,
76
- url: r.url,
77
- content: r.text || "",
78
- score: r.score,
79
- publishedDate: r.publishedDate,
80
- }));
81
- } finally {
82
- cleanup();
83
- }
84
- }
85
- }
@@ -1,77 +0,0 @@
1
- import type {
2
- SearchProvider,
3
- SearchOptions,
4
- SearchResult,
5
- SearchProviderEnv,
6
- } from "./search-provider.js";
7
- import { createProviderAbortSignal, resolveSearchProviderEnv } from "./search-provider.js";
8
-
9
- export class FirecrawlSearchProvider implements SearchProvider {
10
- readonly name = "firecrawl";
11
- readonly requiresApiKey = true;
12
- readonly priority = 2;
13
-
14
- constructor(
15
- private readonly env: SearchProviderEnv | Record<string, string | undefined> = process.env,
16
- ) {}
17
-
18
- isAvailable(): boolean {
19
- const apiKey = resolveSearchProviderEnv(this.env).FIRECRAWL_API_KEY;
20
- return Boolean(apiKey && apiKey.trim().length > 0);
21
- }
22
-
23
- async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {
24
- const apiKey = resolveSearchProviderEnv(this.env).FIRECRAWL_API_KEY;
25
- if (!apiKey) {
26
- throw new Error("FIRECRAWL_API_KEY environment variable is not set");
27
- }
28
-
29
- const limit = options?.limit ?? 5;
30
-
31
- const body: Record<string, unknown> = {
32
- query,
33
- limit,
34
- };
35
-
36
- const { signal, cleanup } = createProviderAbortSignal(30_000, options?.signal);
37
-
38
- try {
39
- const response = await fetch("https://api.firecrawl.dev/v1/search", {
40
- method: "POST",
41
- headers: {
42
- "Content-Type": "application/json",
43
- Authorization: `Bearer ${apiKey}`,
44
- },
45
- body: JSON.stringify(body),
46
- signal,
47
- });
48
-
49
- if (!response.ok) {
50
- throw new Error(`Firecrawl Search API error: ${response.status} ${response.statusText}`);
51
- }
52
-
53
- const json = await response.json();
54
-
55
- // Firecrawl can return 200 with success: false
56
- if (json.success === false) {
57
- throw new Error(`Firecrawl API error: ${json.error || "Unknown error"}`);
58
- }
59
-
60
- const results = (json.data || []) as Array<{
61
- title: string;
62
- url: string;
63
- markdown?: string;
64
- score?: number;
65
- }>;
66
-
67
- return results.map((r) => ({
68
- title: r.title,
69
- url: r.url,
70
- content: r.markdown || "",
71
- score: r.score,
72
- }));
73
- } finally {
74
- cleanup();
75
- }
76
- }
77
- }
@@ -1,8 +0,0 @@
1
- export type { SearchOptions, SearchResult, SearchProvider } from "./search-provider.js";
2
- export { JinaSearchProvider } from "./jina.js";
3
- export { DuckDuckGoSearchProvider } from "./duckduckgo.js";
4
- export { TavilySearchProvider } from "./tavily.js";
5
- export { BraveSearchProvider } from "./brave.js";
6
- export { ExaSearchProvider } from "./exa.js";
7
- export { FirecrawlSearchProvider } from "./firecrawl.js";
8
- export { SearchProviderRouter } from "./router.js";
@@ -1,70 +0,0 @@
1
- import type {
2
- SearchProvider,
3
- SearchOptions,
4
- SearchResult,
5
- SearchProviderEnv,
6
- } from "./search-provider.js";
7
- import { createProviderAbortSignal, resolveSearchProviderEnv } from "./search-provider.js";
8
-
9
- export class JinaSearchProvider implements SearchProvider {
10
- readonly name = "jina";
11
- readonly requiresApiKey = false;
12
- readonly priority = 5;
13
-
14
- constructor(
15
- private readonly env: SearchProviderEnv | Record<string, string | undefined> = process.env,
16
- ) {}
17
-
18
- isAvailable(): boolean {
19
- // Jina works without an API key (20 req/min), or with a key (200 req/min)
20
- return true;
21
- }
22
-
23
- async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {
24
- const apiKey = resolveSearchProviderEnv(this.env).JINA_API_KEY;
25
- const limit = options?.limit ?? 5;
26
-
27
- const url = new URL("https://s.jina.ai/");
28
- url.searchParams.set("q", query);
29
-
30
- const headers: HeadersInit = {
31
- Accept: "application/json",
32
- };
33
-
34
- if (apiKey) {
35
- headers.Authorization = `Bearer ${apiKey}`;
36
- }
37
-
38
- const { signal, cleanup } = createProviderAbortSignal(30_000, options?.signal);
39
-
40
- try {
41
- const response = await fetch(url.toString(), {
42
- method: "GET",
43
- headers,
44
- signal,
45
- });
46
-
47
- if (!response.ok) {
48
- throw new Error(`Jina Search API error: ${response.status} ${response.statusText}`);
49
- }
50
-
51
- const json = await response.json();
52
- const results = (json.data || []) as Array<{
53
- title: string;
54
- url: string;
55
- content: string;
56
- score?: number;
57
- }>;
58
-
59
- // Apply limit client-side
60
- return results.slice(0, limit).map((r) => ({
61
- title: r.title,
62
- url: r.url,
63
- content: r.content,
64
- score: r.score,
65
- }));
66
- } finally {
67
- cleanup();
68
- }
69
- }
70
- }
@@ -1,121 +0,0 @@
1
- import { log } from "@aria-cli/types";
2
- import type {
3
- SearchProvider,
4
- SearchOptions,
5
- SearchResult,
6
- SearchProviderEnv,
7
- } from "./search-provider.js";
8
- import { resolveSearchProviderEnv } from "./search-provider.js";
9
-
10
- export class SearchProviderRouter {
11
- constructor(
12
- private _providers: SearchProvider[],
13
- private readonly env: SearchProviderEnv | Record<string, string | undefined> = process.env,
14
- ) {}
15
-
16
- /**
17
- * Resolves the best available search provider.
18
- * Priority:
19
- * 1. Explicit override via ARIA_SEARCH_PROVIDER env var
20
- * 2. Highest-priority available provider (lowest priority number)
21
- * @throws {Error} if no providers are available or override is invalid
22
- */
23
- resolve(): SearchProvider {
24
- const env = resolveSearchProviderEnv(this.env);
25
- // 1. Check for explicit override
26
- const override = env.ARIA_SEARCH_PROVIDER;
27
- if (override) {
28
- const provider = this._providers.find((p) => p.name === override);
29
- if (!provider) {
30
- throw new Error(
31
- `ARIA_SEARCH_PROVIDER override '${override}' not found in provider registry`,
32
- );
33
- }
34
- // Verify the overridden provider is actually available (e.g., has API key)
35
- if (!provider.isAvailable()) {
36
- // Fall through to default priority-based routing
37
- log.debug(
38
- `[SearchProviderRouter] ARIA_SEARCH_PROVIDER override '${override}' is not available, falling back to priority routing`,
39
- );
40
- } else {
41
- return provider;
42
- }
43
- }
44
-
45
- // 2. Select first available provider by priority (lower number = higher priority)
46
- const sorted = [...this._providers].sort((a, b) => a.priority - b.priority);
47
- const available = sorted.find((p) => p.isAvailable());
48
-
49
- if (!available) {
50
- throw new Error(
51
- "No search providers available. Set at least one API key (BRAVE_API_KEY, TAVILY_API_KEY, etc.)",
52
- );
53
- }
54
-
55
- return available;
56
- }
57
-
58
- /**
59
- * Performs a search with automatic fallback.
60
- * Tries providers in priority order (resolved provider first, then others).
61
- * Circuit breaker: stops at first successful response.
62
- * @throws {AggregateError} if all providers fail
63
- */
64
- async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {
65
- if (options?.signal?.aborted) {
66
- throw new Error("Search aborted");
67
- }
68
-
69
- const env = resolveSearchProviderEnv(this.env);
70
- // Get all available providers sorted by priority
71
- const availableProviders = [...this._providers]
72
- .filter((p) => p.isAvailable())
73
- .sort((a, b) => a.priority - b.priority);
74
-
75
- if (availableProviders.length === 0) {
76
- throw new Error("No search providers available");
77
- }
78
-
79
- // Honor ARIA_SEARCH_PROVIDER override: try the override provider first
80
- const override = env.ARIA_SEARCH_PROVIDER;
81
- if (override) {
82
- const overrideProvider = availableProviders.find((p) => p.name === override);
83
- if (overrideProvider) {
84
- // Move override provider to front of the list
85
- const reordered = [
86
- overrideProvider,
87
- ...availableProviders.filter((p) => p.name !== override),
88
- ];
89
- availableProviders.length = 0;
90
- availableProviders.push(...reordered);
91
- }
92
- }
93
-
94
- const errors: Error[] = [];
95
-
96
- // Try each provider in order
97
- for (const provider of availableProviders) {
98
- if (options?.signal?.aborted) {
99
- throw new Error("Search aborted");
100
- }
101
- try {
102
- const results = await provider.search(query, options);
103
- return results; // Circuit breaker: return on first success
104
- } catch (error) {
105
- if (options?.signal?.aborted) {
106
- throw error instanceof Error ? error : new Error("Search aborted");
107
- }
108
- errors.push(
109
- error instanceof Error ? error : new Error(`Unknown error from ${provider.name}`),
110
- );
111
- // Continue to next provider
112
- }
113
- }
114
-
115
- // All providers failed
116
- throw new AggregateError(
117
- errors,
118
- `All search providers failed (tried ${availableProviders.length} providers)`,
119
- );
120
- }
121
- }