webfox 4.8.0 → 4.10.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 (37) hide show
  1. package/README.md +2 -0
  2. package/dist/{adapter-YOHLAGYM.js → adapter-2UVAARTV.js} +1 -1
  3. package/dist/{adapter-UAZNLDSS.js → adapter-764OQFKY.js} +13 -9
  4. package/dist/{adapter-67TIISDW.js → adapter-A5WYGGZI.js} +1 -1
  5. package/dist/{adapter-I6OUKBZQ.js → adapter-C2VZHFUB.js} +1 -1
  6. package/dist/{adapter-QU2G7O25.js → adapter-EIDSGLOP.js} +3 -3
  7. package/dist/{adapter-2YBFCBCZ.js → adapter-G3I7JEO5.js} +1 -1
  8. package/dist/{adapter-PADXKW43.js → adapter-HRI5ZMGK.js} +82 -26
  9. package/dist/{adapter-ISZQTZDP.js → adapter-HTMXAJTU.js} +1 -1
  10. package/dist/adapter-JJSMYZXG.js +91 -0
  11. package/dist/{adapter-HPTMLH75.js → adapter-K6AMSUCR.js} +70 -30
  12. package/dist/{adapter-XBAQSK7I.js → adapter-L6BFKWGX.js} +65 -67
  13. package/dist/{adapter-SLTIAAML.js → adapter-OK2PTP6R.js} +2 -1
  14. package/dist/{adapter-SDQX2533.js → adapter-UIFBJKKF.js} +1 -1
  15. package/dist/{adapter-HITGYUXN.js → adapter-VMMS4YQG.js} +1 -1
  16. package/dist/{adapter-VW4FMMJC.js → adapter-ZYDUANQL.js} +1 -1
  17. package/dist/{chunk-IRKSK34I.js → chunk-3WBE6UEW.js} +3 -3
  18. package/dist/{chunk-GPACLYNT.js → chunk-57OOEOXI.js} +1 -0
  19. package/dist/{chunk-QERSNL3J.js → chunk-5FP7KNMC.js} +83 -15
  20. package/dist/chunk-FSTCLBSF.js +76 -0
  21. package/dist/{chunk-G7LMBPW4.js → chunk-TGULKHB5.js} +9 -12
  22. package/dist/cli.js +4 -4
  23. package/dist/config.schema.json +112 -0
  24. package/dist/document-schema.d.ts +4 -4
  25. package/dist/domain.d.ts +1 -1
  26. package/dist/durable.js +4 -4
  27. package/dist/index.js +4 -4
  28. package/dist/pi.js +4 -4
  29. package/dist/providers/contract.d.ts +1 -0
  30. package/dist/providers/keenable/adapter.d.ts +5 -0
  31. package/dist/providers/keenable/definition.d.ts +1 -0
  32. package/dist/providers/keenable/types.d.ts +4 -0
  33. package/dist/providers/registry.d.ts +1 -0
  34. package/dist/providers/shared.d.ts +16 -0
  35. package/docs/provider.md +59 -1
  36. package/package.json +7 -10
  37. package/dist/chunk-X6ZBIHZC.js +0 -21
@@ -1,52 +1,65 @@
1
1
  import {
2
2
  formatJson,
3
+ joinUrl,
4
+ requestJson,
3
5
  trimSnippet
4
- } from "./chunk-X6ZBIHZC.js";
6
+ } from "./chunk-FSTCLBSF.js";
5
7
  import {
6
8
  orderedContents
7
9
  } from "./chunk-YZXM4U2C.js";
8
10
  import {
9
11
  WebfoxError,
10
- asWebfoxError,
11
- httpError
12
+ asWebfoxError
12
13
  } from "./chunk-U4BLULLV.js";
13
14
 
14
15
  // src/providers/firecrawl/adapter.ts
15
- import FirecrawlClient from "@mendable/firecrawl-js";
16
16
  var FIRECRAWL_CLOUD_HOST = "api.firecrawl.dev";
17
17
  var FIRECRAWL_DEFAULT_API_URL = "https://api.firecrawl.dev";
18
18
  var FIRECRAWL_QUESTION_LIMIT = 1e4;
19
19
  var firecrawlImplementation = {
20
- async search(query, maxResults, config, _context, options) {
21
- const client = createClient(config);
22
- const response = await client.search(query, {
23
- ...options ?? {},
24
- limit: maxResults
25
- });
20
+ async search(query, maxResults, config, context, options) {
21
+ if (!query.trim()) {
22
+ throw new Error("Query cannot be empty");
23
+ }
24
+ const response = await firecrawlRequest(
25
+ config,
26
+ "/v2/search",
27
+ { ...options ?? {}, query, limit: maxResults },
28
+ context.signal,
29
+ "Firecrawl search failed."
30
+ );
26
31
  return {
27
32
  provider: "firecrawl",
28
- results: flattenSearchResults(response).slice(0, maxResults)
33
+ results: flattenSearchResults(
34
+ asRecord(response.data) ?? {}
35
+ ).slice(0, maxResults)
29
36
  };
30
37
  },
31
- async contents(urls, config, _context, options) {
32
- const client = createClient(config);
38
+ async contents(urls, config, context, options) {
39
+ const { autoResume: _autoResume, ...callOptions } = options ?? {};
33
40
  const scrapeOptions = {
34
41
  formats: ["markdown"],
35
42
  onlyMainContent: true,
36
- ...options ?? {},
37
- autoResume: false
43
+ ...callOptions
38
44
  };
39
45
  return {
40
46
  provider: "firecrawl",
41
47
  answers: await Promise.all(
42
48
  urls.map(async (url) => {
43
49
  try {
44
- const document = await client.scrape(url, scrapeOptions);
50
+ const response = await firecrawlRequest(
51
+ config,
52
+ "/v2/scrape",
53
+ { ...scrapeOptions, url: url.trim() },
54
+ context.signal,
55
+ "Firecrawl scrape failed."
56
+ );
57
+ const document = asRecord(response.data) ?? {};
45
58
  const content = getDocumentContent(document);
46
59
  return content ? {
47
60
  url,
48
61
  content,
49
- ...document.metadata ? {
62
+ ...asRecord(document.metadata) ? {
50
63
  metadata: document.metadata
51
64
  } : {}
52
65
  } : {
@@ -94,22 +107,6 @@ var firecrawlImplementation = {
94
107
  };
95
108
  }
96
109
  };
97
- function createClient(config) {
98
- const apiUrl = config.baseUrl;
99
- const apiKey = config.credentials?.api;
100
- if (isFirecrawlCloudApiUrl(apiUrl) && !apiKey) {
101
- throw new WebfoxError(
102
- "PROVIDER_UNAVAILABLE",
103
- "Firecrawl cloud requires FIRECRAWL_API_KEY; self-hosted instances can use baseUrl without a key."
104
- );
105
- }
106
- return new FirecrawlClient({
107
- // Firecrawl counts attempts, unlike SDKs whose maxRetries excludes the first call.
108
- maxRetries: 1,
109
- apiKey,
110
- apiUrl
111
- });
112
- }
113
110
  function isFirecrawlCloudApiUrl(apiUrl) {
114
111
  return !apiUrl || apiUrl.includes(FIRECRAWL_CLOUD_HOST);
115
112
  }
@@ -136,6 +133,19 @@ function stripAnswerOnlyOptions(options) {
136
133
  return scrapeOptions;
137
134
  }
138
135
  async function scrapeQuestion(config, url, question, options, signal) {
136
+ return await firecrawlRequest(
137
+ config,
138
+ "/v2/scrape",
139
+ {
140
+ ...options,
141
+ url,
142
+ formats: [{ type: "question", question }]
143
+ },
144
+ signal,
145
+ "Firecrawl scrape failed."
146
+ );
147
+ }
148
+ async function firecrawlRequest(config, path, body, signal, failure) {
139
149
  const apiUrl = config.baseUrl ?? FIRECRAWL_DEFAULT_API_URL;
140
150
  const apiKey = config.credentials?.api;
141
151
  if (isFirecrawlCloudApiUrl(apiUrl) && !apiKey) {
@@ -144,45 +154,28 @@ async function scrapeQuestion(config, url, question, options, signal) {
144
154
  "Firecrawl cloud requires FIRECRAWL_API_KEY; self-hosted instances can use baseUrl without a key."
145
155
  );
146
156
  }
147
- const response = await fetch(joinUrl(apiUrl, "/v2/scrape"), {
148
- signal,
149
- method: "POST",
150
- headers: {
151
- "Content-Type": "application/json",
152
- ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
157
+ const payload = await requestJson(
158
+ joinUrl(apiUrl, path),
159
+ {
160
+ // Omit Authorization without a key: Firecrawl grants keyless access
161
+ // only when the header is absent.
162
+ headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
163
+ body,
164
+ signal
153
165
  },
154
- body: JSON.stringify({
155
- ...options,
156
- url,
157
- formats: [{ type: "question", question }]
158
- })
159
- });
160
- const payload = await readJsonResponse(response);
161
- if (!response.ok) {
162
- throw httpError(response, readFirecrawlError(payload, response.statusText));
163
- }
166
+ (value, response) => readFirecrawlError(value, response.statusText || failure)
167
+ );
164
168
  if (isFirecrawlFailure(payload)) {
165
- throw new Error(readFirecrawlError(payload, "Firecrawl scrape failed."));
169
+ throw new Error(readFirecrawlError(payload, failure));
166
170
  }
167
- return payload;
168
- }
169
- function joinUrl(baseUrl, path) {
170
- return `${baseUrl.replace(/\/+$/g, "")}/${path.replace(/^\/+/g, "")}`;
171
- }
172
- async function readJsonResponse(response) {
173
- const text = await response.text();
174
- if (!text) {
175
- return {};
176
- }
177
- try {
178
- return JSON.parse(text);
179
- } catch {
180
- return text;
171
+ const record = asRecord(payload);
172
+ if (!record) {
173
+ throw new Error(`Unexpected Firecrawl response: ${formatJson(payload)}`);
181
174
  }
175
+ return record;
182
176
  }
183
177
  function isFirecrawlFailure(value) {
184
- const record = asRecord(value);
185
- return record?.success === false || record?.error !== void 0;
178
+ return asRecord(value)?.success !== true;
186
179
  }
187
180
  function readFirecrawlError(value, fallback) {
188
181
  const record = asRecord(value);
@@ -201,7 +194,12 @@ function getFirecrawlDocument(value) {
201
194
  }
202
195
  function flattenSearchResults(response) {
203
196
  return ["web", "news", "images"].flatMap(
204
- (source) => (response[source] ?? []).map((entry) => toSearchResult(source, entry)).filter((entry) => entry !== null)
197
+ (source) => (response[source] ?? []).map(
198
+ (entry) => toSearchResult(
199
+ source,
200
+ typeof entry === "string" ? { url: entry } : entry
201
+ )
202
+ ).filter((entry) => entry !== null)
205
203
  );
206
204
  }
207
205
  function toSearchResult(source, value) {
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  trimSnippet
3
- } from "./chunk-X6ZBIHZC.js";
3
+ } from "./chunk-FSTCLBSF.js";
4
+ import "./chunk-U4BLULLV.js";
4
5
 
5
6
  // src/providers/perplexity/adapter.ts
6
7
  import PerplexityClient from "@perplexity-ai/perplexity_ai";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  trimSnippet
3
- } from "./chunk-X6ZBIHZC.js";
3
+ } from "./chunk-FSTCLBSF.js";
4
4
  import {
5
5
  orderedContents
6
6
  } from "./chunk-YZXM4U2C.js";
@@ -5,7 +5,7 @@ import "./chunk-I2UIACY3.js";
5
5
  import "./chunk-WCTOYZVB.js";
6
6
  import {
7
7
  trimSnippet
8
- } from "./chunk-X6ZBIHZC.js";
8
+ } from "./chunk-FSTCLBSF.js";
9
9
  import "./chunk-U4BLULLV.js";
10
10
 
11
11
  // src/providers/openai/adapter.ts
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-ZVTVERW5.js";
5
5
  import {
6
6
  WEBFOX_ERROR_CODES
7
- } from "./chunk-GPACLYNT.js";
7
+ } from "./chunk-57OOEOXI.js";
8
8
  import {
9
9
  WebfoxError
10
10
  } from "./chunk-U4BLULLV.js";
@@ -6,21 +6,21 @@ import {
6
6
  } from "./chunk-WCTOYZVB.js";
7
7
  import {
8
8
  configurationSchema
9
- } from "./chunk-G7LMBPW4.js";
9
+ } from "./chunk-TGULKHB5.js";
10
10
  import {
11
11
  effectiveOptions,
12
12
  optionSchema,
13
13
  providers,
14
14
  selectProvider,
15
15
  validateConfiguredOptions
16
- } from "./chunk-QERSNL3J.js";
16
+ } from "./chunk-5FP7KNMC.js";
17
17
  import {
18
18
  cancelProcessGroup,
19
19
  killProcessGroup
20
20
  } from "./chunk-ZVTVERW5.js";
21
21
  import {
22
22
  CAPABILITIES
23
- } from "./chunk-GPACLYNT.js";
23
+ } from "./chunk-57OOEOXI.js";
24
24
  import {
25
25
  WebfoxError,
26
26
  asWebfoxError
@@ -12,6 +12,7 @@ var PROVIDER_IDS = [
12
12
  "exa",
13
13
  "firecrawl",
14
14
  "gemini",
15
+ "keenable",
15
16
  "linkup",
16
17
  "ollama",
17
18
  "openai",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CAPABILITIES
3
- } from "./chunk-GPACLYNT.js";
3
+ } from "./chunk-57OOEOXI.js";
4
4
  import {
5
5
  WebfoxError
6
6
  } from "./chunk-U4BLULLV.js";
@@ -504,7 +504,7 @@ var braveProvider = defineProvider({
504
504
  retrySafe: false
505
505
  }
506
506
  },
507
- load: async () => (await import("./adapter-67TIISDW.js")).adapter
507
+ load: async () => (await import("./adapter-A5WYGGZI.js")).adapter
508
508
  });
509
509
 
510
510
  // src/providers/cloudflare/definition.ts
@@ -633,7 +633,7 @@ var customProvider = defineProvider({
633
633
  retrySafe: false
634
634
  }
635
635
  },
636
- load: async () => (await import("./adapter-VW4FMMJC.js")).adapter
636
+ load: async () => (await import("./adapter-ZYDUANQL.js")).adapter
637
637
  });
638
638
 
639
639
  // src/providers/exa/definition.ts
@@ -944,7 +944,7 @@ var exaProvider = defineProvider({
944
944
  retrySafe: false
945
945
  }
946
946
  },
947
- load: async () => (await import("./adapter-PADXKW43.js")).adapter
947
+ load: async () => (await import("./adapter-HRI5ZMGK.js")).adapter
948
948
  });
949
949
 
950
950
  // src/providers/firecrawl/definition.ts
@@ -1150,7 +1150,7 @@ var firecrawlProvider = defineProvider({
1150
1150
  retrySafe: false
1151
1151
  }
1152
1152
  },
1153
- load: async () => (await import("./adapter-XBAQSK7I.js")).adapter
1153
+ load: async () => (await import("./adapter-L6BFKWGX.js")).adapter
1154
1154
  });
1155
1155
 
1156
1156
  // src/providers/gemini/definition.ts
@@ -1278,6 +1278,73 @@ var geminiProvider = defineProvider({
1278
1278
  load: async () => (await import("./adapter-6EZAIE65.js")).adapter
1279
1279
  });
1280
1280
 
1281
+ // src/providers/keenable/definition.ts
1282
+ var timeFilter = (description) => ({
1283
+ type: "string",
1284
+ minLength: 1,
1285
+ description: `${description} Accepts YYYY-MM-DD, an ISO 8601 timestamp, or a relative delta such as 7d, 12h, or 30min.`
1286
+ });
1287
+ var keenableProvider = defineProvider({
1288
+ id: "keenable",
1289
+ label: "Keenable",
1290
+ docsUrl: "https://docs.keenable.ai/api-reference/search",
1291
+ local: false,
1292
+ credentials: [{ name: "api", environmentVariable: "KEENABLE_API_KEY" }],
1293
+ fields: ["credentials", "baseUrl", "options"],
1294
+ defaults: {},
1295
+ credentialDefaults: {},
1296
+ capabilities: {
1297
+ search: {
1298
+ options: {
1299
+ type: "object",
1300
+ properties: {
1301
+ mode: {
1302
+ type: "string",
1303
+ enum: ["pro", "realtime"],
1304
+ description: "Search mode: 'pro' (default) for deeper retrieval, 'realtime' for the fastest results. Each mode is billed separately."
1305
+ },
1306
+ site: {
1307
+ type: "string",
1308
+ minLength: 1,
1309
+ description: "Restrict results to one site, e.g. 'techcrunch.com'."
1310
+ },
1311
+ published_after: timeFilter(
1312
+ "Only pages published at or after this time."
1313
+ ),
1314
+ published_before: timeFilter(
1315
+ "Only pages published at or before this time."
1316
+ ),
1317
+ acquired_after: timeFilter(
1318
+ "Only pages indexed at or after this time."
1319
+ ),
1320
+ acquired_before: timeFilter(
1321
+ "Only pages indexed at or before this time."
1322
+ ),
1323
+ query_time: {
1324
+ type: "string",
1325
+ minLength: 1,
1326
+ description: "Search the index as it stood at this date or ISO 8601 timestamp; relative time filters resolve against it."
1327
+ },
1328
+ snippet_max_length: {
1329
+ type: "integer",
1330
+ minimum: 180,
1331
+ maximum: 1e4,
1332
+ description: "Maximum characters of the excerpt per result."
1333
+ }
1334
+ },
1335
+ description: "Keenable search options."
1336
+ },
1337
+ promptGuidelines: [
1338
+ "Keenable returns ranked web results with page excerpts; use web_contents to read selected sources in full.",
1339
+ "Use Keenable site to restrict results to one domain, and published_after/published_before (e.g. '7d') for recent or date-bounded results.",
1340
+ "Use Keenable mode='realtime' only when latency matters more than retrieval depth."
1341
+ ],
1342
+ retrySafe: true
1343
+ }
1344
+ },
1345
+ load: async () => (await import("./adapter-JJSMYZXG.js")).adapter
1346
+ });
1347
+
1281
1348
  // src/providers/linkup/definition.ts
1282
1349
  var linkupProvider = defineProvider({
1283
1350
  id: "linkup",
@@ -1473,7 +1540,7 @@ var linkupProvider = defineProvider({
1473
1540
  retrySafe: false
1474
1541
  }
1475
1542
  },
1476
- load: async () => (await import("./adapter-HPTMLH75.js")).adapter
1543
+ load: async () => (await import("./adapter-K6AMSUCR.js")).adapter
1477
1544
  });
1478
1545
 
1479
1546
  // src/providers/ollama/definition.ts
@@ -1502,7 +1569,7 @@ var ollamaProvider = defineProvider({
1502
1569
  retrySafe: true
1503
1570
  }
1504
1571
  },
1505
- load: async () => (await import("./adapter-ISZQTZDP.js")).adapter
1572
+ load: async () => (await import("./adapter-HTMXAJTU.js")).adapter
1506
1573
  });
1507
1574
 
1508
1575
  // src/providers/openai/definition.ts
@@ -1610,7 +1677,7 @@ var openaiProvider = defineProvider({
1610
1677
  retrySafe: false
1611
1678
  }
1612
1679
  },
1613
- load: async () => (await import("./adapter-HITGYUXN.js")).adapter
1680
+ load: async () => (await import("./adapter-VMMS4YQG.js")).adapter
1614
1681
  });
1615
1682
 
1616
1683
  // src/providers/parallel/definition.ts
@@ -1769,7 +1836,7 @@ var parallelProvider = defineProvider({
1769
1836
  retrySafe: true
1770
1837
  }
1771
1838
  },
1772
- load: async () => (await import("./adapter-2YBFCBCZ.js")).adapter
1839
+ load: async () => (await import("./adapter-G3I7JEO5.js")).adapter
1773
1840
  });
1774
1841
 
1775
1842
  // src/providers/perplexity/definition.ts
@@ -1890,7 +1957,7 @@ var perplexityProvider = defineProvider({
1890
1957
  answer: { options: chatOptions, retrySafe: false },
1891
1958
  research: { options: chatOptions, retrySafe: false }
1892
1959
  },
1893
- load: async () => (await import("./adapter-SLTIAAML.js")).adapter
1960
+ load: async () => (await import("./adapter-OK2PTP6R.js")).adapter
1894
1961
  });
1895
1962
 
1896
1963
  // src/providers/serpbase/definition.ts
@@ -1991,7 +2058,7 @@ var serpbaseProvider = defineProvider({
1991
2058
  retrySafe: true
1992
2059
  }
1993
2060
  },
1994
- load: async () => (await import("./adapter-QU2G7O25.js")).adapter
2061
+ load: async () => (await import("./adapter-EIDSGLOP.js")).adapter
1995
2062
  });
1996
2063
 
1997
2064
  // src/providers/serper/definition.ts
@@ -2131,7 +2198,7 @@ var serperProvider = defineProvider({
2131
2198
  retrySafe: true
2132
2199
  }
2133
2200
  },
2134
- load: async () => (await import("./adapter-I6OUKBZQ.js")).adapter
2201
+ load: async () => (await import("./adapter-C2VZHFUB.js")).adapter
2135
2202
  });
2136
2203
 
2137
2204
  // src/providers/tavily/definition.ts
@@ -2326,7 +2393,7 @@ var tavilyProvider = defineProvider({
2326
2393
  retrySafe: true
2327
2394
  }
2328
2395
  },
2329
- load: async () => (await import("./adapter-SDQX2533.js")).adapter
2396
+ load: async () => (await import("./adapter-UIFBJKKF.js")).adapter
2330
2397
  });
2331
2398
 
2332
2399
  // src/providers/valyu/definition.ts
@@ -2794,7 +2861,7 @@ var valyuProvider = defineProvider({
2794
2861
  retrySafe: false
2795
2862
  }
2796
2863
  },
2797
- load: async () => (await import("./adapter-UAZNLDSS.js")).adapter
2864
+ load: async () => (await import("./adapter-764OQFKY.js")).adapter
2798
2865
  });
2799
2866
 
2800
2867
  // src/providers/youcom/definition.ts
@@ -2985,7 +3052,7 @@ var youcomProvider = defineProvider({
2985
3052
  retrySafe: true
2986
3053
  }
2987
3054
  },
2988
- load: async () => (await import("./adapter-YOHLAGYM.js")).adapter
3055
+ load: async () => (await import("./adapter-2UVAARTV.js")).adapter
2989
3056
  });
2990
3057
 
2991
3058
  // src/providers/registry.ts
@@ -2996,6 +3063,7 @@ var providers = {
2996
3063
  exa: exaProvider,
2997
3064
  firecrawl: firecrawlProvider,
2998
3065
  gemini: geminiProvider,
3066
+ keenable: keenableProvider,
2999
3067
  linkup: linkupProvider,
3000
3068
  ollama: ollamaProvider,
3001
3069
  openai: openaiProvider,
@@ -0,0 +1,76 @@
1
+ import {
2
+ WebfoxError,
3
+ httpError
4
+ } from "./chunk-U4BLULLV.js";
5
+
6
+ // src/providers/shared.ts
7
+ function trimSnippet(input, maxLength = 300) {
8
+ const text = (input ?? "").replace(/\s+/g, " ").trim();
9
+ return text.length <= maxLength ? text : `${text.slice(0, maxLength - 1)}\u2026`;
10
+ }
11
+ function normalizeContentText(input) {
12
+ return (input ?? "").replace(/\r/g, "").trim().split("\n").map((line) => line.replace(/[ \t]+$/g, "")).join("\n").replace(/\n{3,}/g, "\n\n");
13
+ }
14
+ function asJsonObject(value) {
15
+ return value ? { ...value } : {};
16
+ }
17
+ function formatJson(value) {
18
+ return JSON.stringify(value, null, 2);
19
+ }
20
+ function joinUrl(baseUrl, path) {
21
+ return `${baseUrl.replace(/\/+$/g, "")}/${path.replace(/^\/+/g, "")}`;
22
+ }
23
+ async function readResponseBody(response) {
24
+ const text = await response.text();
25
+ if (!text) return void 0;
26
+ try {
27
+ return JSON.parse(text);
28
+ } catch {
29
+ return text;
30
+ }
31
+ }
32
+ function expectJsonBody(payload, response) {
33
+ if (payload === void 0) {
34
+ throw new WebfoxError(
35
+ "PROVIDER_FAILURE",
36
+ `Expected a JSON response but received an empty body (status ${response.status}).`
37
+ );
38
+ }
39
+ if (typeof payload === "string") {
40
+ const snippet = payload.trim().replace(/\s+/g, " ").slice(0, 200);
41
+ throw new WebfoxError(
42
+ "PROVIDER_FAILURE",
43
+ `Expected a JSON response but received a non-JSON body (status ${response.status}): ${snippet}`
44
+ );
45
+ }
46
+ return payload;
47
+ }
48
+ async function requestJson(url, init, describeError) {
49
+ const method = init.method ?? (init.body === void 0 ? "GET" : "POST");
50
+ const response = await fetch(url, {
51
+ method,
52
+ headers: {
53
+ Accept: "application/json",
54
+ ...init.body === void 0 ? {} : { "Content-Type": "application/json" },
55
+ ...init.headers
56
+ },
57
+ ...init.body === void 0 ? {} : { body: JSON.stringify(init.body) },
58
+ signal: init.signal
59
+ });
60
+ const payload = await readResponseBody(response);
61
+ if (!response.ok) {
62
+ throw httpError(response, describeError(payload, response));
63
+ }
64
+ return expectJsonBody(payload, response);
65
+ }
66
+
67
+ export {
68
+ trimSnippet,
69
+ normalizeContentText,
70
+ asJsonObject,
71
+ formatJson,
72
+ joinUrl,
73
+ readResponseBody,
74
+ expectJsonBody,
75
+ requestJson
76
+ };
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  optionSchema,
3
3
  providers
4
- } from "./chunk-QERSNL3J.js";
4
+ } from "./chunk-5FP7KNMC.js";
5
5
  import {
6
6
  CAPABILITIES
7
- } from "./chunk-GPACLYNT.js";
7
+ } from "./chunk-57OOEOXI.js";
8
8
 
9
9
  // package.json
10
10
  var package_default = {
11
11
  name: "webfox",
12
- version: "4.8.0",
12
+ version: "4.10.0",
13
13
  description: "Search the web, extract pages, get grounded answers, and run deep research with the web CLI, a TypeScript library, and a pi extension. Bring your own providers and API keys.",
14
14
  type: "module",
15
15
  files: [
@@ -101,17 +101,14 @@ var package_default = {
101
101
  "test:watch": "vitest"
102
102
  },
103
103
  dependencies: {
104
- "@mendable/firecrawl-js": "^4.38.0",
105
- "@perplexity-ai/perplexity_ai": "^0.38.5",
106
- "@tavily/core": "^0.7.9",
107
- cloudflare: "^7.1.0",
104
+ "@perplexity-ai/perplexity_ai": "^0.41.0",
105
+ "@tavily/core": "^0.7.14",
106
+ cloudflare: "^7.3.0",
108
107
  commander: "^14.0.3",
109
- "exa-js": "^2.19.0",
110
- "linkup-sdk": "^3.6.0",
111
- openai: "^7.10.0",
112
- "parallel-web": "^1.3.3",
108
+ openai: "^7.47.0",
109
+ "parallel-web": "^1.3.7",
113
110
  picocolors: "^1.1.1",
114
- "valyu-js": "^2.10.1",
111
+ "valyu-js": "^2.11.5",
115
112
  yaml: "^2.9.0"
116
113
  },
117
114
  devDependencies: {
package/dist/cli.js CHANGED
@@ -11,19 +11,19 @@ import {
11
11
  redactConfig,
12
12
  resolveConfigPath,
13
13
  setCapabilityDefault
14
- } from "./chunk-IRKSK34I.js";
14
+ } from "./chunk-3WBE6UEW.js";
15
15
  import "./chunk-WCTOYZVB.js";
16
16
  import {
17
17
  PACKAGE_VERSION
18
- } from "./chunk-G7LMBPW4.js";
18
+ } from "./chunk-TGULKHB5.js";
19
19
  import {
20
20
  validateConfiguredOptions
21
- } from "./chunk-QERSNL3J.js";
21
+ } from "./chunk-5FP7KNMC.js";
22
22
  import "./chunk-ZVTVERW5.js";
23
23
  import {
24
24
  CAPABILITIES,
25
25
  PROVIDER_IDS
26
- } from "./chunk-GPACLYNT.js";
26
+ } from "./chunk-57OOEOXI.js";
27
27
  import {
28
28
  WebfoxError
29
29
  } from "./chunk-U4BLULLV.js";