webfox 4.10.0 → 4.11.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 (44) hide show
  1. package/README.md +2 -12
  2. package/dist/{adapter-UIFBJKKF.js → adapter-44ZBIK47.js} +4 -6
  3. package/dist/{adapter-HTMXAJTU.js → adapter-57MELLYI.js} +2 -8
  4. package/dist/{adapter-VMMS4YQG.js → adapter-BZO5UEI6.js} +3 -15
  5. package/dist/{adapter-OK2PTP6R.js → adapter-D4EXZEDL.js} +16 -48
  6. package/dist/{adapter-G3I7JEO5.js → adapter-DJCTEEOA.js} +3 -5
  7. package/dist/{adapter-764OQFKY.js → adapter-DLNMW34W.js} +8 -35
  8. package/dist/{adapter-HRI5ZMGK.js → adapter-GFXYSHKP.js} +9 -43
  9. package/dist/{adapter-2UVAARTV.js → adapter-GJOUFE2U.js} +1 -1
  10. package/dist/{adapter-C2VZHFUB.js → adapter-MZ7USB6Q.js} +3 -4
  11. package/dist/adapter-ODDFBGJG.js +167 -0
  12. package/dist/{adapter-K6AMSUCR.js → adapter-QQZFHUOP.js} +14 -37
  13. package/dist/{adapter-AUYLTDPM.js → adapter-RR55EZAW.js} +1 -1
  14. package/dist/{adapter-JJSMYZXG.js → adapter-SZMVKAMC.js} +1 -1
  15. package/dist/{adapter-6EZAIE65.js → adapter-VXT4C6RZ.js} +16 -66
  16. package/dist/{adapter-L6BFKWGX.js → adapter-WXEVTCM3.js} +7 -14
  17. package/dist/{adapter-ZYDUANQL.js → adapter-XSQX3GAI.js} +13 -5
  18. package/dist/{adapter-EIDSGLOP.js → adapter-XU2APKPC.js} +3 -3
  19. package/dist/{adapter-A5WYGGZI.js → adapter-YVZ4YBK5.js} +8 -16
  20. package/dist/chunk-6RQGO727.js +71 -0
  21. package/dist/{chunk-TGULKHB5.js → chunk-KMUCUJVN.js} +3 -3
  22. package/dist/{chunk-EF4VMDZL.js → chunk-LDVDVA4Z.js} +1 -4
  23. package/dist/{chunk-57OOEOXI.js → chunk-NENL6L6N.js} +1 -0
  24. package/dist/{chunk-5FP7KNMC.js → chunk-PVNWACMT.js} +132 -18
  25. package/dist/{chunk-YZXM4U2C.js → chunk-TYJ6RHKB.js} +10 -0
  26. package/dist/{chunk-3WBE6UEW.js → chunk-YILOS4E7.js} +23 -9
  27. package/dist/cli.js +5 -5
  28. package/dist/config.schema.json +187 -0
  29. package/dist/contents.d.ts +5 -0
  30. package/dist/document-schema.d.ts +17 -5
  31. package/dist/domain.d.ts +14 -2
  32. package/dist/durable.js +5 -5
  33. package/dist/index.js +4 -4
  34. package/dist/pi.js +33 -14
  35. package/dist/providers/contract.d.ts +11 -1
  36. package/dist/providers/registry.d.ts +1 -0
  37. package/dist/providers/sofya/adapter.d.ts +8 -0
  38. package/dist/providers/sofya/definition.d.ts +1 -0
  39. package/dist/providers/sofya/types.d.ts +4 -0
  40. package/dist/render.d.ts +8 -2
  41. package/docs/provider.md +27 -0
  42. package/examples/custom/README.md +9 -3
  43. package/package.json +1 -1
  44. package/dist/chunk-BTWECET5.js +0 -31
@@ -8,14 +8,14 @@ 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", "keenable", "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", "sofya", "tavily", "valyu", "youcom"]>;
12
12
  status: Type.TEnum<["ok", "partial"]>;
13
13
  results: Type.TArray<Type.TUnion<[Type.TObject<{
14
14
  input: Type.TString;
15
15
  ok: Type.TLiteral<true>;
16
16
  value: Type.TObject<{
17
17
  results: Type.TArray<Type.TObject<{
18
- title: Type.TString;
18
+ title: Type.TOptional<Type.TString>;
19
19
  url: Type.TString;
20
20
  snippet: Type.TString;
21
21
  score: Type.TOptional<Type.TNumber>;
@@ -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", "keenable", "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", "sofya", "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,13 +59,19 @@ 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", "keenable", "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", "sofya", "tavily", "valyu", "youcom"]>;
63
63
  status: Type.TEnum<["ok", "partial"]>;
64
64
  results: Type.TArray<Type.TUnion<[Type.TObject<{
65
65
  input: Type.TString;
66
66
  ok: Type.TLiteral<true>;
67
67
  value: Type.TObject<{
68
68
  text: Type.TString;
69
+ sources: Type.TOptional<Type.TArray<Type.TUnion<[Type.TObject<{
70
+ url: Type.TString;
71
+ title: Type.TOptional<Type.TString>;
72
+ }>, Type.TObject<{
73
+ title: Type.TString;
74
+ }>]>>>;
69
75
  itemCount: Type.TOptional<Type.TInteger>;
70
76
  metadata: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
71
77
  }>;
@@ -82,13 +88,19 @@ export declare const DOCUMENT_SCHEMAS: {
82
88
  readonly research: Type.TObject<{
83
89
  schemaVersion: Type.TLiteral<1>;
84
90
  capability: Type.TLiteral<"research">;
85
- provider: Type.TEnum<["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "keenable", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "tavily", "valyu", "youcom"]>;
91
+ provider: Type.TEnum<["brave", "cloudflare", "custom", "exa", "firecrawl", "gemini", "keenable", "linkup", "ollama", "openai", "parallel", "perplexity", "serpbase", "serper", "sofya", "tavily", "valyu", "youcom"]>;
86
92
  status: Type.TEnum<["ok", "partial"]>;
87
93
  results: Type.TArray<Type.TUnion<[Type.TObject<{
88
94
  input: Type.TString;
89
95
  ok: Type.TLiteral<true>;
90
96
  value: Type.TObject<{
91
97
  text: Type.TString;
98
+ sources: Type.TOptional<Type.TArray<Type.TUnion<[Type.TObject<{
99
+ url: Type.TString;
100
+ title: Type.TOptional<Type.TString>;
101
+ }>, Type.TObject<{
102
+ title: Type.TString;
103
+ }>]>>>;
92
104
  itemCount: Type.TOptional<Type.TInteger>;
93
105
  metadata: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
94
106
  }>;
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", "keenable", "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", "sofya", "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];
@@ -19,7 +19,8 @@ export type InputResult<T> = {
19
19
  error: SerializedError;
20
20
  };
21
21
  export interface SearchResult {
22
- title: string;
22
+ /** Omitted when the provider has no title; renderers fall back to the URL. */
23
+ title?: string;
23
24
  url: string;
24
25
  snippet: string;
25
26
  score?: number;
@@ -28,12 +29,23 @@ export interface SearchResult {
28
29
  export interface ContentsAnswer {
29
30
  /** Final fetched URL; association with the requested URL is in InputResult.input. */
30
31
  url: string;
32
+ /** Omitted when the page has no content; an empty page is still a result. */
31
33
  content?: string;
32
34
  summary?: unknown;
33
35
  metadata?: Record<string, unknown>;
34
36
  }
37
+ /** A cited source with a nonblank URL, title, or both. */
38
+ export type Source = {
39
+ url: string;
40
+ title?: string;
41
+ } | {
42
+ title: string;
43
+ };
35
44
  export interface TextAnswer {
45
+ /** Empty when the provider returned no text; an empty answer is still a result. */
36
46
  text: string;
47
+ sources?: Source[];
48
+ /** Number of sources, derived from `sources`. */
37
49
  itemCount?: number;
38
50
  metadata?: Record<string, unknown>;
39
51
  }
package/dist/durable.js CHANGED
@@ -5,17 +5,17 @@ import {
5
5
  } from "./chunk-VVOQKYOP.js";
6
6
  import {
7
7
  renderTextDocument
8
- } from "./chunk-BTWECET5.js";
8
+ } from "./chunk-6RQGO727.js";
9
9
  import {
10
10
  createWebfox
11
- } from "./chunk-3WBE6UEW.js";
11
+ } from "./chunk-YILOS4E7.js";
12
12
  import "./chunk-WCTOYZVB.js";
13
- import "./chunk-TGULKHB5.js";
14
- import "./chunk-5FP7KNMC.js";
13
+ import "./chunk-KMUCUJVN.js";
14
+ import "./chunk-PVNWACMT.js";
15
15
  import "./chunk-ZVTVERW5.js";
16
16
  import {
17
17
  CAPABILITIES
18
- } from "./chunk-57OOEOXI.js";
18
+ } from "./chunk-NENL6L6N.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-3WBE6UEW.js";
9
+ } from "./chunk-YILOS4E7.js";
10
10
  import "./chunk-WCTOYZVB.js";
11
11
  import {
12
12
  CONFIG_SCHEMA_URL
13
- } from "./chunk-TGULKHB5.js";
13
+ } from "./chunk-KMUCUJVN.js";
14
14
  import {
15
15
  validateConfiguredOptions
16
- } from "./chunk-5FP7KNMC.js";
16
+ } from "./chunk-PVNWACMT.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-57OOEOXI.js";
22
+ } from "./chunk-NENL6L6N.js";
23
23
  import {
24
24
  WebfoxError
25
25
  } from "./chunk-U4BLULLV.js";
package/dist/pi.js CHANGED
@@ -4,21 +4,22 @@ import {
4
4
  webToolParameters
5
5
  } from "./chunk-VVOQKYOP.js";
6
6
  import {
7
+ emptyResultText,
7
8
  renderTextDocument
8
- } from "./chunk-BTWECET5.js";
9
+ } from "./chunk-6RQGO727.js";
9
10
  import {
10
11
  createWebfox,
11
12
  resolveConfigPath
12
- } from "./chunk-3WBE6UEW.js";
13
+ } from "./chunk-YILOS4E7.js";
13
14
  import "./chunk-WCTOYZVB.js";
14
- import "./chunk-TGULKHB5.js";
15
- import "./chunk-5FP7KNMC.js";
15
+ import "./chunk-KMUCUJVN.js";
16
+ import "./chunk-PVNWACMT.js";
16
17
  import "./chunk-ZVTVERW5.js";
17
18
  import {
18
19
  CAPABILITIES,
19
20
  PROVIDER_IDS,
20
21
  WEBFOX_ERROR_CODES
21
- } from "./chunk-57OOEOXI.js";
22
+ } from "./chunk-NENL6L6N.js";
22
23
  import {
23
24
  WebfoxError
24
25
  } from "./chunk-U4BLULLV.js";
@@ -175,7 +176,7 @@ function searchDocument(value) {
175
176
  if (entry.ok === false)
176
177
  return isRecord2(entry.error) && typeof entry.error.message === "string";
177
178
  return entry.ok === true && isRecord2(entry.value) && Array.isArray(entry.value.results) && entry.value.results.every(
178
- (result) => isRecord2(result) && typeof result.title === "string" && typeof result.url === "string" && typeof result.snippet === "string"
179
+ (result) => isRecord2(result) && (result.title === void 0 || typeof result.title === "string") && typeof result.url === "string" && typeof result.snippet === "string"
179
180
  );
180
181
  });
181
182
  }
@@ -231,17 +232,21 @@ function renderSearchResult(details, fallback, theme) {
231
232
  return;
232
233
  }
233
234
  if (!entry.value.results.length)
234
- append(theme.fg("toolOutput", "No results found."));
235
+ append(theme.fg("toolOutput", emptyResultText("search")));
235
236
  entry.value.results.forEach((result, resultIndex) => {
236
237
  if (resultIndex) lines.push("");
237
238
  const number = `${resultIndex + 1}. `;
238
239
  const indent = " ".repeat(number.length);
239
- append(
240
- theme.fg("toolOutput", plainText(result.title)),
241
- theme.fg("accent", number)
242
- );
243
- append(link(result.url, theme), indent);
244
- if (result.snippet)
240
+ if (result.title === void 0)
241
+ append(link(result.url, theme), theme.fg("accent", number));
242
+ else {
243
+ append(
244
+ theme.fg("toolOutput", plainText(result.title)),
245
+ theme.fg("accent", number)
246
+ );
247
+ append(link(result.url, theme), indent);
248
+ }
249
+ if (result.snippet.trim())
245
250
  append(theme.fg("toolOutput", plainText(result.snippet)), indent);
246
251
  });
247
252
  });
@@ -372,7 +377,7 @@ var metadata = Type.Record(Type.String(), Type.Unknown(), {
372
377
  });
373
378
  var searchResult = Type.Object(
374
379
  {
375
- title: Type.String(),
380
+ title: Type.Optional(Type.String({ minLength: 1 })),
376
381
  url: Type.String(),
377
382
  snippet: Type.String(),
378
383
  score: Type.Optional(Type.Number()),
@@ -392,6 +397,20 @@ var contentsAnswer = Type.Object(
392
397
  var textAnswer = Type.Object(
393
398
  {
394
399
  text: Type.String(),
400
+ sources: Type.Optional(
401
+ Type.Array(
402
+ Type.Union([
403
+ Type.Object(
404
+ {
405
+ url: Type.String({ minLength: 1 }),
406
+ title: Type.Optional(Type.String({ minLength: 1 }))
407
+ },
408
+ closed
409
+ ),
410
+ Type.Object({ title: Type.String({ minLength: 1 }) }, closed)
411
+ ])
412
+ )
413
+ ),
395
414
  itemCount: Type.Optional(Type.Integer()),
396
415
  metadata: Type.Optional(metadata)
397
416
  },
@@ -4,10 +4,19 @@ export interface SearchResponse {
4
4
  provider: ProviderId;
5
5
  results: SearchResult[];
6
6
  }
7
+ /** A provider citation; the runtime trims fields and drops blank entries. */
8
+ export interface SourceInput {
9
+ url?: string;
10
+ title?: string;
11
+ }
12
+ /**
13
+ * Answer and research output. Return the provider's text as-is, even when
14
+ * empty, and its citations as `sources`; presentation belongs to the renderer.
15
+ */
7
16
  export interface ToolOutput {
8
17
  provider: ProviderId;
9
18
  text: string;
10
- itemCount?: number;
19
+ sources?: SourceInput[];
11
20
  metadata?: Record<string, unknown>;
12
21
  }
13
22
  export interface ResearchJob {
@@ -84,6 +93,7 @@ export interface ProviderConfigMap {
84
93
  perplexity: import("./perplexity/types.js").Perplexity;
85
94
  serpbase: import("./serpbase/types.js").SerpBase;
86
95
  serper: import("./serper/types.js").Serper;
96
+ sofya: import("./sofya/types.js").Sofya;
87
97
  tavily: import("./tavily/types.js").Tavily;
88
98
  valyu: import("./valyu/types.js").Valyu;
89
99
  youcom: import("./youcom/types.js").Youcom;
@@ -13,6 +13,7 @@ export declare const providers: {
13
13
  readonly perplexity: import("./definition.js").ProviderDefinitionFor<"perplexity">;
14
14
  readonly serpbase: import("./definition.js").ProviderDefinitionFor<"serpbase">;
15
15
  readonly serper: import("./definition.js").ProviderDefinitionFor<"serper">;
16
+ readonly sofya: import("./definition.js").ProviderDefinitionFor<"sofya">;
16
17
  readonly tavily: import("./definition.js").ProviderDefinitionFor<"tavily">;
17
18
  readonly valyu: import("./definition.js").ProviderDefinitionFor<"valyu">;
18
19
  readonly youcom: import("./definition.js").ProviderDefinitionFor<"youcom">;
@@ -0,0 +1,8 @@
1
+ import type { ProviderContext, ProviderRequest, SearchResponse, ToolOutput } from "../contract.js";
2
+ import type { Sofya } from "./types.js";
3
+ export declare const adapter: {
4
+ search(request: ProviderRequest<"search">, config: Sofya, context: ProviderContext): Promise<SearchResponse>;
5
+ contents(request: ProviderRequest<"contents">, config: Sofya, context: ProviderContext): Promise<import("../../contents.js").IndexedContentsResponse>;
6
+ answer(request: ProviderRequest<"answer">, config: Sofya, context: ProviderContext): Promise<ToolOutput>;
7
+ research(request: ProviderRequest<"research">, config: Sofya, context: ProviderContext): Promise<ToolOutput>;
8
+ };
@@ -0,0 +1 @@
1
+ export declare const sofyaProvider: import("../definition.js").ProviderDefinitionFor<"sofya">;
@@ -0,0 +1,4 @@
1
+ import type { Provider } from "../contract.js";
2
+ export interface Sofya extends Provider {
3
+ baseUrl?: string;
4
+ }
package/dist/render.d.ts CHANGED
@@ -1,5 +1,11 @@
1
- import type { WebDocument } from "./domain.js";
2
- /** Presentation is deliberately outside the application/runtime. */
1
+ import type { Capability, SearchResult, Source, WebDocument } from "./domain.js";
2
+ /**
3
+ * Presentation is deliberately outside the application/runtime: providers
4
+ * return empty results as data, and only renderers decide how they look.
5
+ */
6
+ export declare function emptyResultText(capability: Capability): string;
7
+ /** Label a search result or source by its title, falling back to its URL. */
8
+ export declare function resultLabel(result: SearchResult | Source): string;
3
9
  export declare function renderTextDocument(document: WebDocument, { includeErrors }?: {
4
10
  includeErrors?: boolean;
5
11
  }): string;
package/docs/provider.md CHANGED
@@ -340,6 +340,33 @@ Supports search. Set `SERPER_API_KEY`.
340
340
  web search "Node.js release notes" --provider serper
341
341
  ```
342
342
 
343
+ ## Sofya
344
+
345
+ Supports search, page extraction, answers, and research. Set `SOFYA_API_KEY`,
346
+ or configure `providers.sofya.credentials.api` with a credential source. No
347
+ defaults change unless you select Sofya.
348
+
349
+ ```sh
350
+ web search "Node.js release notes" --provider sofya --freshness month
351
+ web contents https://nodejs.org/en/blog --provider sofya
352
+ web answer "What is the latest Node.js LTS?" --provider sofya
353
+ web research "State of WebAssembly outside the browser" --provider sofya
354
+ web config default search sofya
355
+ ```
356
+
357
+ Search caps `maxResults` at 20 and defaults to `search_depth: "snippets"`;
358
+ `basic` also reads each page and returns its text in `metadata.content`. Native
359
+ options include `topic` (`general` or `news`), `freshness` (`day`, `week`,
360
+ `month`, `year`, or a `YYYY-MM-DD:YYYY-MM-DD` range), and
361
+ `include_domains`/`exclude_domains`. Answers come from a search with
362
+ `include_answer` and report Sofya's `altered_query` or `relaxed_query` in
363
+ metadata. Contents accepts `include_raw_html`. Research accepts `topic`,
364
+ `freshness`, and `max_sources` (5-30) and usually takes 20-60 seconds.
365
+
366
+ Sofya calls can take up to 60 seconds, so search, contents, and answers have a
367
+ 90-second overall deadline by default. An explicit CLI `--timeout`, library
368
+ `timeoutMs`, or YAML `execution.timeoutMs` takes precedence.
369
+
343
370
  ## You.com
344
371
 
345
372
  Supports search. Set `YDC_API_KEY`; the library, CLI, and Pi extension use the
@@ -32,10 +32,16 @@ Payloads and responses:
32
32
 
33
33
  | Capability | Input | Response |
34
34
  | --- | --- | --- |
35
- | Search | `{ "query": "...", "maxResults": 5 }` | `{ "results": [{ "title": "...", "url": "https://...", "snippet": "..." }] }` |
35
+ | Search | `{ "query": "...", "maxResults": 5 }` | `{ "results": [{ "title": "...", "url": "https://...", "snippet": "..." }] }` (`title` is optional) |
36
36
  | Contents | `{ "urls": ["https://..."] }` | `{ "answers": [{ "inputIndex": 0, "url": "https://...", "content": "..." }] }` |
37
- | Answer | `{ "query": "..." }` | `{ "text": "..." }` |
38
- | Research | `{ "input": "..." }` | `{ "text": "..." }` |
37
+ | Answer | `{ "query": "..." }` | `{ "text": "...", "sources": [{ "url": "https://...", "title": "..." }] }` |
38
+ | Research | `{ "input": "..." }` | `{ "text": "...", "sources": [{ "url": "https://...", "title": "..." }] }` |
39
+
40
+ Return results as data and leave presentation to webfox. Empty results are
41
+ valid: return no search results, an empty `text`, or a page without `content`
42
+ instead of placeholder text. Omit titles you don't have rather than repeating
43
+ the URL. `sources` is optional; webfox drops sources that have neither a `url`
44
+ nor a `title`.
39
45
 
40
46
  Contents answers must cover every request index exactly once. `inputIndex`
41
47
  associates a response with the requested URL; `url` can hold a redirected URL.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webfox",
3
- "version": "4.10.0",
3
+ "version": "4.11.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": [
@@ -1,31 +0,0 @@
1
- // src/render.ts
2
- function renderTextDocument(document, { includeErrors = true } = {}) {
3
- return document.results.map((entry, index) => {
4
- const heading = document.results.length > 1 ? `## ${index + 1}. ${entry.input}` : "";
5
- if (!entry.ok)
6
- return includeErrors ? [heading, `Error: ${entry.error.message}`].filter(Boolean).join("\n\n") : void 0;
7
- let body;
8
- if ("results" in entry.value)
9
- body = entry.value.results.length ? entry.value.results.map(
10
- (result, i) => `${i + 1}. ${result.title}
11
- ${result.url}
12
- ${result.snippet.replaceAll("\n", "\n ")}`
13
- ).join("\n\n") : "No results found.";
14
- else if ("text" in entry.value) body = entry.value.text;
15
- else body = renderContents(entry.value);
16
- return [heading, body].filter(Boolean).join("\n\n");
17
- }).filter((part) => part !== void 0).join("\n\n");
18
- }
19
- function renderContents(answer) {
20
- return [
21
- `## ${answer.url}`,
22
- answer.content,
23
- answer.summary === void 0 ? void 0 : `### Summary
24
-
25
- ${typeof answer.summary === "string" ? answer.summary : JSON.stringify(answer.summary, null, 2)}`
26
- ].filter(Boolean).join("\n\n");
27
- }
28
-
29
- export {
30
- renderTextDocument
31
- };