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
@@ -23,6 +23,7 @@
23
23
  "custom",
24
24
  "exa",
25
25
  "firecrawl",
26
+ "keenable",
26
27
  "linkup",
27
28
  "ollama",
28
29
  "openai",
@@ -2197,6 +2198,117 @@
2197
2198
  }
2198
2199
  }
2199
2200
  },
2201
+ "keenable": {
2202
+ "type": "object",
2203
+ "additionalProperties": false,
2204
+ "properties": {
2205
+ "credentials": {
2206
+ "type": "object",
2207
+ "additionalProperties": false,
2208
+ "properties": {
2209
+ "api": {
2210
+ "oneOf": [
2211
+ {
2212
+ "type": "object",
2213
+ "additionalProperties": false,
2214
+ "properties": {
2215
+ "env": {
2216
+ "type": "string",
2217
+ "minLength": 1
2218
+ }
2219
+ },
2220
+ "required": ["env"]
2221
+ },
2222
+ {
2223
+ "type": "object",
2224
+ "additionalProperties": false,
2225
+ "properties": {
2226
+ "value": {
2227
+ "type": "string",
2228
+ "minLength": 1
2229
+ }
2230
+ },
2231
+ "required": ["value"]
2232
+ },
2233
+ {
2234
+ "type": "object",
2235
+ "additionalProperties": false,
2236
+ "properties": {
2237
+ "command": {
2238
+ "type": "array",
2239
+ "minItems": 1,
2240
+ "items": {
2241
+ "type": "string",
2242
+ "minLength": 1
2243
+ }
2244
+ }
2245
+ },
2246
+ "required": ["command"]
2247
+ }
2248
+ ]
2249
+ }
2250
+ }
2251
+ },
2252
+ "baseUrl": {
2253
+ "type": "string",
2254
+ "minLength": 1
2255
+ },
2256
+ "options": {
2257
+ "type": "object",
2258
+ "additionalProperties": false,
2259
+ "properties": {
2260
+ "search": {
2261
+ "type": "object",
2262
+ "properties": {
2263
+ "mode": {
2264
+ "type": "string",
2265
+ "enum": ["pro", "realtime"],
2266
+ "description": "Search mode: 'pro' (default) for deeper retrieval, 'realtime' for the fastest results. Each mode is billed separately."
2267
+ },
2268
+ "site": {
2269
+ "type": "string",
2270
+ "minLength": 1,
2271
+ "description": "Restrict results to one site, e.g. 'techcrunch.com'."
2272
+ },
2273
+ "published_after": {
2274
+ "type": "string",
2275
+ "minLength": 1,
2276
+ "description": "Only pages published at or after this time. Accepts YYYY-MM-DD, an ISO 8601 timestamp, or a relative delta such as 7d, 12h, or 30min."
2277
+ },
2278
+ "published_before": {
2279
+ "type": "string",
2280
+ "minLength": 1,
2281
+ "description": "Only pages published at or before this time. Accepts YYYY-MM-DD, an ISO 8601 timestamp, or a relative delta such as 7d, 12h, or 30min."
2282
+ },
2283
+ "acquired_after": {
2284
+ "type": "string",
2285
+ "minLength": 1,
2286
+ "description": "Only pages indexed at or after this time. Accepts YYYY-MM-DD, an ISO 8601 timestamp, or a relative delta such as 7d, 12h, or 30min."
2287
+ },
2288
+ "acquired_before": {
2289
+ "type": "string",
2290
+ "minLength": 1,
2291
+ "description": "Only pages indexed at or before this time. Accepts YYYY-MM-DD, an ISO 8601 timestamp, or a relative delta such as 7d, 12h, or 30min."
2292
+ },
2293
+ "query_time": {
2294
+ "type": "string",
2295
+ "minLength": 1,
2296
+ "description": "Search the index as it stood at this date or ISO 8601 timestamp; relative time filters resolve against it."
2297
+ },
2298
+ "snippet_max_length": {
2299
+ "type": "integer",
2300
+ "minimum": 180,
2301
+ "maximum": 10000,
2302
+ "description": "Maximum characters of the excerpt per result."
2303
+ }
2304
+ },
2305
+ "description": "Keenable search options.",
2306
+ "additionalProperties": false
2307
+ }
2308
+ }
2309
+ }
2310
+ }
2311
+ },
2200
2312
  "linkup": {
2201
2313
  "type": "object",
2202
2314
  "additionalProperties": false,
@@ -8,7 +8,7 @@ export declare const DOCUMENT_SCHEMAS: {
8
8
  readonly search: Type.TObject<{
9
9
  schemaVersion: Type.TLiteral<1>;
10
10
  capability: Type.TLiteral<"search">;
11
- provider: Type.TEnum<["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"]>;
11
+ provider: Type.TEnum<["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "keenable", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"]>;
12
12
  status: Type.TEnum<["ok", "partial"]>;
13
13
  results: Type.TArray<Type.TUnion<[Type.TObject<{
14
14
  input: Type.TString;
@@ -35,7 +35,7 @@ export declare const DOCUMENT_SCHEMAS: {
35
35
  readonly contents: Type.TObject<{
36
36
  schemaVersion: Type.TLiteral<1>;
37
37
  capability: Type.TLiteral<"contents">;
38
- provider: Type.TEnum<["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"]>;
38
+ provider: Type.TEnum<["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "keenable", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"]>;
39
39
  status: Type.TEnum<["ok", "partial"]>;
40
40
  results: Type.TArray<Type.TUnion<[Type.TObject<{
41
41
  input: Type.TString;
@@ -59,7 +59,7 @@ export declare const DOCUMENT_SCHEMAS: {
59
59
  readonly answer: Type.TObject<{
60
60
  schemaVersion: Type.TLiteral<1>;
61
61
  capability: Type.TLiteral<"answer">;
62
- provider: Type.TEnum<["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"]>;
62
+ provider: Type.TEnum<["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "keenable", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"]>;
63
63
  status: Type.TEnum<["ok", "partial"]>;
64
64
  results: Type.TArray<Type.TUnion<[Type.TObject<{
65
65
  input: Type.TString;
@@ -82,7 +82,7 @@ export declare const DOCUMENT_SCHEMAS: {
82
82
  readonly research: Type.TObject<{
83
83
  schemaVersion: Type.TLiteral<1>;
84
84
  capability: Type.TLiteral<"research">;
85
- provider: Type.TEnum<["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"]>;
85
+ provider: Type.TEnum<["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "keenable", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"]>;
86
86
  status: Type.TEnum<["ok", "partial"]>;
87
87
  results: Type.TArray<Type.TUnion<[Type.TObject<{
88
88
  input: Type.TString;
package/dist/domain.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export declare const CAPABILITIES: readonly ["search", "contents", "answer", "research"];
2
2
  export type Capability = (typeof CAPABILITIES)[number];
3
- export declare const PROVIDER_IDS: readonly ["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"];
3
+ export declare const PROVIDER_IDS: readonly ["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "keenable", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"];
4
4
  export type ProviderId = (typeof PROVIDER_IDS)[number];
5
5
  export declare const WEBFOX_ERROR_CODES: readonly ["INVALID_CONFIG", "INVALID_INPUT", "PROVIDER_UNAVAILABLE", "PROVIDER_FAILURE", "PARTIAL_BATCH", "TIMEOUT", "CANCELLED"];
6
6
  export type WebfoxErrorCode = (typeof WEBFOX_ERROR_CODES)[number];
package/dist/durable.js CHANGED
@@ -8,14 +8,14 @@ import {
8
8
  } from "./chunk-BTWECET5.js";
9
9
  import {
10
10
  createWebfox
11
- } from "./chunk-IRKSK34I.js";
11
+ } from "./chunk-3WBE6UEW.js";
12
12
  import "./chunk-WCTOYZVB.js";
13
- import "./chunk-G7LMBPW4.js";
14
- import "./chunk-QERSNL3J.js";
13
+ import "./chunk-TGULKHB5.js";
14
+ import "./chunk-5FP7KNMC.js";
15
15
  import "./chunk-ZVTVERW5.js";
16
16
  import {
17
17
  CAPABILITIES
18
- } from "./chunk-GPACLYNT.js";
18
+ } from "./chunk-57OOEOXI.js";
19
19
  import "./chunk-U4BLULLV.js";
20
20
 
21
21
  // src/durable.ts
package/dist/index.js CHANGED
@@ -6,20 +6,20 @@ import {
6
6
  resolveConfigPath,
7
7
  setCapabilityDefault,
8
8
  validateConfig
9
- } from "./chunk-IRKSK34I.js";
9
+ } from "./chunk-3WBE6UEW.js";
10
10
  import "./chunk-WCTOYZVB.js";
11
11
  import {
12
12
  CONFIG_SCHEMA_URL
13
- } from "./chunk-G7LMBPW4.js";
13
+ } from "./chunk-TGULKHB5.js";
14
14
  import {
15
15
  validateConfiguredOptions
16
- } from "./chunk-QERSNL3J.js";
16
+ } from "./chunk-5FP7KNMC.js";
17
17
  import "./chunk-ZVTVERW5.js";
18
18
  import {
19
19
  CAPABILITIES,
20
20
  PROVIDER_IDS,
21
21
  WEBFOX_ERROR_CODES
22
- } from "./chunk-GPACLYNT.js";
22
+ } from "./chunk-57OOEOXI.js";
23
23
  import {
24
24
  WebfoxError
25
25
  } from "./chunk-U4BLULLV.js";
package/dist/pi.js CHANGED
@@ -9,16 +9,16 @@ import {
9
9
  import {
10
10
  createWebfox,
11
11
  resolveConfigPath
12
- } from "./chunk-IRKSK34I.js";
12
+ } from "./chunk-3WBE6UEW.js";
13
13
  import "./chunk-WCTOYZVB.js";
14
- import "./chunk-G7LMBPW4.js";
15
- import "./chunk-QERSNL3J.js";
14
+ import "./chunk-TGULKHB5.js";
15
+ import "./chunk-5FP7KNMC.js";
16
16
  import "./chunk-ZVTVERW5.js";
17
17
  import {
18
18
  CAPABILITIES,
19
19
  PROVIDER_IDS,
20
20
  WEBFOX_ERROR_CODES
21
- } from "./chunk-GPACLYNT.js";
21
+ } from "./chunk-57OOEOXI.js";
22
22
  import {
23
23
  WebfoxError
24
24
  } from "./chunk-U4BLULLV.js";
@@ -76,6 +76,7 @@ export interface ProviderConfigMap {
76
76
  exa: import("./exa/types.js").Exa;
77
77
  firecrawl: import("./firecrawl/types.js").Firecrawl;
78
78
  gemini: import("./gemini/types.js").Gemini;
79
+ keenable: import("./keenable/types.js").Keenable;
79
80
  linkup: import("./linkup/types.js").Linkup;
80
81
  ollama: import("./ollama/types.js").Ollama;
81
82
  openai: import("./openai/types.js").OpenAI;
@@ -0,0 +1,5 @@
1
+ import type { ProviderContext, ProviderRequest, SearchResponse } from "../contract.js";
2
+ import type { Keenable } from "./types.js";
3
+ export declare const adapter: {
4
+ search(request: ProviderRequest<"search">, config: Keenable, context: ProviderContext): Promise<SearchResponse>;
5
+ };
@@ -0,0 +1 @@
1
+ export declare const keenableProvider: import("../definition.js").ProviderDefinitionFor<"keenable">;
@@ -0,0 +1,4 @@
1
+ import type { Provider } from "../contract.js";
2
+ export interface Keenable extends Provider {
3
+ baseUrl?: string;
4
+ }
@@ -5,6 +5,7 @@ export declare const providers: {
5
5
  readonly exa: import("./definition.js").ProviderDefinitionFor<"exa">;
6
6
  readonly firecrawl: import("./definition.js").ProviderDefinitionFor<"firecrawl">;
7
7
  readonly gemini: import("./definition.js").ProviderDefinitionFor<"gemini">;
8
+ readonly keenable: import("./definition.js").ProviderDefinitionFor<"keenable">;
8
9
  readonly linkup: import("./definition.js").ProviderDefinitionFor<"linkup">;
9
10
  readonly ollama: import("./definition.js").ProviderDefinitionFor<"ollama">;
10
11
  readonly openai: import("./definition.js").ProviderDefinitionFor<"openai">;
@@ -2,3 +2,19 @@ export declare function trimSnippet(input: string | undefined, maxLength?: numbe
2
2
  export declare function normalizeContentText(input: string | undefined): string;
3
3
  export declare function asJsonObject(value: Record<string, unknown> | undefined): Record<string, unknown>;
4
4
  export declare function formatJson(value: unknown): string;
5
+ export declare function joinUrl(baseUrl: string, path: string): string;
6
+ /** Read a response body as JSON, falling back to its text. */
7
+ export declare function readResponseBody(response: Response): Promise<unknown>;
8
+ /** Reject successful responses whose body is empty or not JSON. */
9
+ export declare function expectJsonBody(payload: unknown, response: Response): unknown;
10
+ /**
11
+ * Send one JSON request without retries; the runtime owns retry policy.
12
+ * Non-2xx responses become classified provider errors with the message that
13
+ * `describeError` extracts from the response body.
14
+ */
15
+ export declare function requestJson(url: string, init: {
16
+ method?: "GET" | "POST";
17
+ headers?: Record<string, string>;
18
+ body?: unknown;
19
+ signal?: AbortSignal;
20
+ }, describeError: (payload: unknown, response: Response) => string): Promise<unknown>;
package/docs/provider.md CHANGED
@@ -123,6 +123,23 @@ The research agent is fixed; answer model settings don't affect it.
123
123
  If you previously selected Gemini for search, choose a search-capable provider
124
124
  and remove `providers.gemini.options.search` from your configuration.
125
125
 
126
+ ## Keenable
127
+
128
+ Supports search. Set `KEENABLE_API_KEY`, or configure
129
+ `providers.keenable.credentials.api` with a credential source. No defaults change
130
+ unless you select Keenable.
131
+
132
+ ```sh
133
+ web search "Node.js release notes" --provider keenable \
134
+ --site nodejs.org --published-after 30d
135
+ web config default search keenable
136
+ ```
137
+
138
+ Keenable caps `maxResults` at 50. Native options include `mode` (`pro` or
139
+ `realtime`), `site`, `published_after`/`published_before`,
140
+ `acquired_after`/`acquired_before`, `query_time`, and `snippet_max_length`.
141
+ Time filters accept dates, ISO 8601 timestamps, or relative deltas such as `7d`.
142
+
126
143
  ## Linkup
127
144
 
128
145
  Supports search, page extraction, and research. Set `LINKUP_API_KEY`.
@@ -160,8 +177,49 @@ web search --provider openai --help
160
177
 
161
178
  Supports search and page extraction. Set `PARALLEL_API_KEY`.
162
179
 
180
+ Search defaults to `advanced`. Choose `fast` for interactive lookups, or see
181
+ [Parallel's search modes](https://docs.parallel.ai/search/modes) for the available
182
+ tradeoffs:
183
+
184
+ ```sh
185
+ web search "Node.js release notes" --provider parallel --mode fast
186
+ ```
187
+
188
+ Limit a query to particular sources with `advanced_settings.source_policy`.
189
+ Use Webfox's `--max-results` to control the number of results:
190
+
191
+ ```sh
192
+ web search "Node.js release notes" --provider parallel --mode fast --max-results 3 \
193
+ --options-json '{"advanced_settings":{"source_policy":{"include_domains":["nodejs.org"]}}}'
194
+ ```
195
+
196
+ To reuse Fast mode, merge this into your [configuration](./reference.md#configuration):
197
+
198
+ ```yaml
199
+ providers:
200
+ parallel:
201
+ options:
202
+ search:
203
+ mode: fast
204
+ ```
205
+
206
+ Provider options don't select a provider. Continue to pass `--provider parallel`
207
+ or use your saved Parallel search default. A request's `--mode` overrides the
208
+ saved mode.
209
+
210
+ For known URLs, use page extraction instead of another search. Parallel returns
211
+ full page content by default:
212
+
213
+ ```sh
214
+ web contents https://nodejs.org/en/about/previous-releases --provider parallel
215
+ ```
216
+
217
+ To request only excerpts relevant to a question, disable full content and enable
218
+ excerpts with an objective:
219
+
163
220
  ```sh
164
- web search "Node.js release notes" --provider parallel
221
+ web contents https://nodejs.org/en/about/previous-releases --provider parallel \
222
+ --no-full-content --excerpts --objective "Which Node.js releases are supported?"
165
223
  ```
166
224
 
167
225
  ## Perplexity
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webfox",
3
- "version": "4.8.0",
3
+ "version": "4.10.0",
4
4
  "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.",
5
5
  "type": "module",
6
6
  "files": [
@@ -92,17 +92,14 @@
92
92
  "test:watch": "vitest"
93
93
  },
94
94
  "dependencies": {
95
- "@mendable/firecrawl-js": "^4.38.0",
96
- "@perplexity-ai/perplexity_ai": "^0.38.5",
97
- "@tavily/core": "^0.7.9",
98
- "cloudflare": "^7.1.0",
95
+ "@perplexity-ai/perplexity_ai": "^0.41.0",
96
+ "@tavily/core": "^0.7.14",
97
+ "cloudflare": "^7.3.0",
99
98
  "commander": "^14.0.3",
100
- "exa-js": "^2.19.0",
101
- "linkup-sdk": "^3.6.0",
102
- "openai": "^7.10.0",
103
- "parallel-web": "^1.3.3",
99
+ "openai": "^7.47.0",
100
+ "parallel-web": "^1.3.7",
104
101
  "picocolors": "^1.1.1",
105
- "valyu-js": "^2.10.1",
102
+ "valyu-js": "^2.11.5",
106
103
  "yaml": "^2.9.0"
107
104
  },
108
105
  "devDependencies": {
@@ -1,21 +0,0 @@
1
- // src/providers/shared.ts
2
- function trimSnippet(input, maxLength = 300) {
3
- const text = (input ?? "").replace(/\s+/g, " ").trim();
4
- return text.length <= maxLength ? text : `${text.slice(0, maxLength - 1)}\u2026`;
5
- }
6
- function normalizeContentText(input) {
7
- return (input ?? "").replace(/\r/g, "").trim().split("\n").map((line) => line.replace(/[ \t]+$/g, "")).join("\n").replace(/\n{3,}/g, "\n\n");
8
- }
9
- function asJsonObject(value) {
10
- return value ? { ...value } : {};
11
- }
12
- function formatJson(value) {
13
- return JSON.stringify(value, null, 2);
14
- }
15
-
16
- export {
17
- trimSnippet,
18
- normalizeContentText,
19
- asJsonObject,
20
- formatJson
21
- };