webfox 4.8.0 → 4.9.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.
- package/dist/{adapter-YOHLAGYM.js → adapter-2UVAARTV.js} +1 -1
- package/dist/{adapter-UAZNLDSS.js → adapter-764OQFKY.js} +13 -9
- package/dist/{adapter-67TIISDW.js → adapter-A5WYGGZI.js} +1 -1
- package/dist/{adapter-I6OUKBZQ.js → adapter-C2VZHFUB.js} +1 -1
- package/dist/{adapter-2YBFCBCZ.js → adapter-G3I7JEO5.js} +1 -1
- package/dist/{adapter-PADXKW43.js → adapter-HRI5ZMGK.js} +82 -26
- package/dist/{adapter-ISZQTZDP.js → adapter-HTMXAJTU.js} +1 -1
- package/dist/{adapter-HPTMLH75.js → adapter-K6AMSUCR.js} +70 -30
- package/dist/{adapter-XBAQSK7I.js → adapter-L6BFKWGX.js} +65 -67
- package/dist/{adapter-SLTIAAML.js → adapter-OK2PTP6R.js} +2 -1
- package/dist/{adapter-QU2G7O25.js → adapter-QIP32KA4.js} +2 -2
- package/dist/{adapter-SDQX2533.js → adapter-UIFBJKKF.js} +1 -1
- package/dist/{adapter-HITGYUXN.js → adapter-VMMS4YQG.js} +1 -1
- package/dist/chunk-FSTCLBSF.js +76 -0
- package/dist/{chunk-G7LMBPW4.js → chunk-HCYQF24V.js} +8 -11
- package/dist/{chunk-QERSNL3J.js → chunk-LTJOWNI2.js} +13 -13
- package/dist/{chunk-IRKSK34I.js → chunk-QZFN2747.js} +2 -2
- package/dist/cli.js +3 -3
- package/dist/durable.js +3 -3
- package/dist/index.js +3 -3
- package/dist/pi.js +3 -3
- package/dist/providers/shared.d.ts +16 -0
- package/docs/provider.md +42 -1
- package/package.json +7 -10
- package/dist/chunk-X6ZBIHZC.js +0 -21
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
formatJson,
|
|
11
11
|
trimSnippet
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FSTCLBSF.js";
|
|
13
13
|
import {
|
|
14
14
|
orderedContents
|
|
15
15
|
} from "./chunk-YZXM4U2C.js";
|
|
@@ -78,14 +78,18 @@ var valyuImplementation = {
|
|
|
78
78
|
error: "No content returned for this URL."
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
if (result.status === "failed" || result.status === "not_found") {
|
|
82
|
+
return {
|
|
83
|
+
url,
|
|
84
|
+
error: result.error ?? formatJson(result)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const page = result;
|
|
88
|
+
return {
|
|
89
|
+
url: page.url,
|
|
90
|
+
...typeof page.content === "string" || typeof page.content === "number" ? { content: String(page.content) } : {},
|
|
91
|
+
...page.summary !== void 0 ? { summary: page.summary } : {},
|
|
92
|
+
metadata: page
|
|
89
93
|
};
|
|
90
94
|
})
|
|
91
95
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatJson,
|
|
3
|
+
joinUrl,
|
|
4
|
+
requestJson,
|
|
3
5
|
trimSnippet
|
|
4
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FSTCLBSF.js";
|
|
5
7
|
import {
|
|
6
8
|
orderedContents
|
|
7
9
|
} from "./chunk-YZXM4U2C.js";
|
|
@@ -10,15 +12,19 @@ import {
|
|
|
10
12
|
} from "./chunk-U4BLULLV.js";
|
|
11
13
|
|
|
12
14
|
// src/providers/exa/adapter.ts
|
|
13
|
-
|
|
15
|
+
var DEFAULT_BASE_URL = "https://api.exa.ai";
|
|
16
|
+
var DEFAULT_MAX_CHARACTERS = 1e4;
|
|
14
17
|
var exaImplementation = {
|
|
15
|
-
async search(query, maxResults, config,
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
async search(query, maxResults, config, context, searchOptions) {
|
|
19
|
+
const response = await exaRequest(
|
|
20
|
+
config,
|
|
21
|
+
"/search",
|
|
22
|
+
buildSearchBody(query, {
|
|
23
|
+
...searchOptions ?? {},
|
|
24
|
+
numResults: maxResults
|
|
25
|
+
}),
|
|
26
|
+
context
|
|
27
|
+
);
|
|
22
28
|
return {
|
|
23
29
|
provider: "exa",
|
|
24
30
|
results: (response.results ?? []).slice(0, maxResults).map((result) => ({
|
|
@@ -31,9 +37,13 @@ var exaImplementation = {
|
|
|
31
37
|
}))
|
|
32
38
|
};
|
|
33
39
|
},
|
|
34
|
-
async contents(urls, config,
|
|
35
|
-
const
|
|
36
|
-
|
|
40
|
+
async contents(urls, config, context, options) {
|
|
41
|
+
const response = await exaRequest(
|
|
42
|
+
config,
|
|
43
|
+
"/contents",
|
|
44
|
+
{ urls, ...options ?? {} },
|
|
45
|
+
context
|
|
46
|
+
);
|
|
37
47
|
const results = response.results ?? [];
|
|
38
48
|
return {
|
|
39
49
|
provider: "exa",
|
|
@@ -54,9 +64,21 @@ var exaImplementation = {
|
|
|
54
64
|
})
|
|
55
65
|
};
|
|
56
66
|
},
|
|
57
|
-
async answer(query, config,
|
|
58
|
-
const
|
|
59
|
-
|
|
67
|
+
async answer(query, config, context, options) {
|
|
68
|
+
const response = await exaRequest(
|
|
69
|
+
config,
|
|
70
|
+
"/answer",
|
|
71
|
+
{
|
|
72
|
+
query,
|
|
73
|
+
stream: false,
|
|
74
|
+
text: options?.text ?? false,
|
|
75
|
+
model: options?.model ?? "exa",
|
|
76
|
+
systemPrompt: options?.systemPrompt,
|
|
77
|
+
outputSchema: options?.outputSchema,
|
|
78
|
+
userLocation: options?.userLocation
|
|
79
|
+
},
|
|
80
|
+
context
|
|
81
|
+
);
|
|
60
82
|
const lines = [];
|
|
61
83
|
lines.push(
|
|
62
84
|
typeof response.answer === "string" ? response.answer : formatJson(response.answer)
|
|
@@ -81,15 +103,19 @@ var exaImplementation = {
|
|
|
81
103
|
async research(input, config, context, options) {
|
|
82
104
|
context.signal?.throwIfAborted();
|
|
83
105
|
context.onProgress?.("Exa is researching and synthesizing the report.");
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
type: "
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
const result = await exaRequest(
|
|
107
|
+
config,
|
|
108
|
+
"/search",
|
|
109
|
+
buildSearchBody(input, {
|
|
110
|
+
...options ?? {},
|
|
111
|
+
type: "deep-reasoning",
|
|
112
|
+
outputSchema: {
|
|
113
|
+
type: "text",
|
|
114
|
+
description: "A detailed Markdown research report answering the query. Cite sources with links, distinguish evidence from uncertainty, and include a conclusion."
|
|
115
|
+
}
|
|
116
|
+
}),
|
|
117
|
+
context
|
|
118
|
+
);
|
|
93
119
|
const content = result.output?.content;
|
|
94
120
|
if (typeof content !== "string" || !content.trim())
|
|
95
121
|
throw new WebfoxError(
|
|
@@ -113,12 +139,42 @@ var exaImplementation = {
|
|
|
113
139
|
};
|
|
114
140
|
}
|
|
115
141
|
};
|
|
116
|
-
function
|
|
142
|
+
function buildSearchBody(query, options) {
|
|
143
|
+
const { stream: _stream, ...rest } = options;
|
|
144
|
+
if (!("contents" in rest)) {
|
|
145
|
+
return {
|
|
146
|
+
query,
|
|
147
|
+
...rest,
|
|
148
|
+
contents: { text: { maxCharacters: DEFAULT_MAX_CHARACTERS } }
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (rest.contents === false || rest.contents == null) {
|
|
152
|
+
const { contents: _contents, ...withoutContents } = rest;
|
|
153
|
+
return { query, ...withoutContents };
|
|
154
|
+
}
|
|
155
|
+
return { query, ...rest };
|
|
156
|
+
}
|
|
157
|
+
async function exaRequest(config, path, body, context) {
|
|
117
158
|
const apiKey = config.credentials?.api;
|
|
118
159
|
if (!apiKey) {
|
|
119
160
|
throw new Error("is missing an API key");
|
|
120
161
|
}
|
|
121
|
-
return
|
|
162
|
+
return await requestJson(
|
|
163
|
+
joinUrl(config.baseUrl ?? DEFAULT_BASE_URL, path),
|
|
164
|
+
{ headers: { "x-api-key": apiKey }, body, signal: context.signal },
|
|
165
|
+
readExaError
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
function readExaError(payload, response) {
|
|
169
|
+
if (typeof payload !== "object" || payload === null) {
|
|
170
|
+
const text = typeof payload === "string" ? payload.trim().slice(0, 500) : "";
|
|
171
|
+
return text ? `Request failed with status ${response.status}: ${text}` : `Request failed with status ${response.status}.`;
|
|
172
|
+
}
|
|
173
|
+
const record = payload;
|
|
174
|
+
const envelope = typeof record.error === "object" && record.error !== null ? record.error : void 0;
|
|
175
|
+
const parts = envelope ? [envelope.message ?? record.message] : [record.error, record.message];
|
|
176
|
+
const message = parts.filter((part) => typeof part === "string" && part !== "").join(". ");
|
|
177
|
+
return message || `Request failed with status ${response.status}.`;
|
|
122
178
|
}
|
|
123
179
|
var adapter = {
|
|
124
180
|
async search(input, config, context) {
|
|
@@ -4,45 +4,45 @@ import {
|
|
|
4
4
|
import "./chunk-I2UIACY3.js";
|
|
5
5
|
import "./chunk-WCTOYZVB.js";
|
|
6
6
|
import {
|
|
7
|
+
expectJsonBody,
|
|
7
8
|
formatJson,
|
|
9
|
+
joinUrl,
|
|
10
|
+
readResponseBody,
|
|
8
11
|
trimSnippet
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FSTCLBSF.js";
|
|
10
13
|
import {
|
|
11
14
|
orderedContents
|
|
12
15
|
} from "./chunk-YZXM4U2C.js";
|
|
13
16
|
import {
|
|
17
|
+
WebfoxError,
|
|
14
18
|
asWebfoxError
|
|
15
19
|
} from "./chunk-U4BLULLV.js";
|
|
16
20
|
|
|
17
21
|
// src/providers/linkup/adapter.ts
|
|
18
|
-
|
|
19
|
-
LinkupClient
|
|
20
|
-
} from "linkup-sdk";
|
|
22
|
+
var DEFAULT_BASE_URL = "https://api.linkup.so/v1";
|
|
21
23
|
var linkupImplementation = {
|
|
22
|
-
async search(query, maxResults, config,
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
buildSearchParams(query, maxResults, {
|
|
24
|
+
async search(query, maxResults, config, context, options) {
|
|
25
|
+
const response = await linkupRequest(config, context, "/search", {
|
|
26
|
+
body: buildSearchParams(query, maxResults, {
|
|
26
27
|
...options ?? {}
|
|
27
28
|
})
|
|
28
|
-
);
|
|
29
|
+
});
|
|
29
30
|
return {
|
|
30
31
|
provider: "linkup",
|
|
31
32
|
results: (response.results ?? []).map(toSearchResult).filter((result) => result !== null).slice(0, maxResults)
|
|
32
33
|
};
|
|
33
34
|
},
|
|
34
|
-
async contents(urls, config,
|
|
35
|
-
const client = createClient(config);
|
|
35
|
+
async contents(urls, config, context, options) {
|
|
36
36
|
return {
|
|
37
37
|
provider: "linkup",
|
|
38
38
|
answers: await Promise.all(
|
|
39
39
|
urls.map(async (url) => {
|
|
40
40
|
try {
|
|
41
|
-
const response = await
|
|
42
|
-
buildFetchParams(url, {
|
|
41
|
+
const response = await linkupRequest(config, context, "/fetch", {
|
|
42
|
+
body: buildFetchParams(url, {
|
|
43
43
|
...options ?? {}
|
|
44
44
|
})
|
|
45
|
-
);
|
|
45
|
+
});
|
|
46
46
|
const metadata = Object.fromEntries(
|
|
47
47
|
Object.entries(response).filter(
|
|
48
48
|
([key, value]) => [
|
|
@@ -86,18 +86,20 @@ var linkupImplementation = {
|
|
|
86
86
|
poll: (id, researchContext) => linkupImplementation.pollResearch(id, config, researchContext)
|
|
87
87
|
});
|
|
88
88
|
},
|
|
89
|
-
async startResearch(input, config,
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
buildResearchParams(input, {
|
|
89
|
+
async startResearch(input, config, context, options) {
|
|
90
|
+
const task = await linkupRequest(config, context, "/research", {
|
|
91
|
+
body: buildResearchParams(input, {
|
|
93
92
|
...options ?? {}
|
|
94
93
|
})
|
|
95
|
-
);
|
|
94
|
+
});
|
|
96
95
|
return { id: task.id };
|
|
97
96
|
},
|
|
98
|
-
async pollResearch(id, config,
|
|
99
|
-
const
|
|
100
|
-
|
|
97
|
+
async pollResearch(id, config, context) {
|
|
98
|
+
const task = await linkupRequest(
|
|
99
|
+
config,
|
|
100
|
+
context,
|
|
101
|
+
`/research/${encodeURIComponent(id)}`
|
|
102
|
+
);
|
|
101
103
|
if (task.status === "completed") {
|
|
102
104
|
return {
|
|
103
105
|
status: "completed",
|
|
@@ -135,7 +137,7 @@ function buildSearchParams(query, maxResults, options) {
|
|
|
135
137
|
);
|
|
136
138
|
}
|
|
137
139
|
return {
|
|
138
|
-
query,
|
|
140
|
+
q: query,
|
|
139
141
|
depth: searchOptions.depth ?? "standard",
|
|
140
142
|
outputType: "searchResults",
|
|
141
143
|
maxResults,
|
|
@@ -179,7 +181,7 @@ function buildResearchParams(input, options) {
|
|
|
179
181
|
);
|
|
180
182
|
}
|
|
181
183
|
const commonParams = {
|
|
182
|
-
|
|
184
|
+
q: input,
|
|
183
185
|
...researchOptions.includeDomains !== void 0 ? { includeDomains: researchOptions.includeDomains } : {},
|
|
184
186
|
...researchOptions.excludeDomains !== void 0 ? { excludeDomains: researchOptions.excludeDomains } : {},
|
|
185
187
|
...researchOptions.fromDate !== void 0 ? { fromDate: toDate(researchOptions.fromDate, "fromDate") } : {},
|
|
@@ -191,7 +193,9 @@ function buildResearchParams(input, options) {
|
|
|
191
193
|
return {
|
|
192
194
|
...commonParams,
|
|
193
195
|
outputType,
|
|
194
|
-
structuredOutputSchema:
|
|
196
|
+
structuredOutputSchema: serializeSchema(
|
|
197
|
+
researchOptions.structuredOutputSchema
|
|
198
|
+
)
|
|
195
199
|
};
|
|
196
200
|
}
|
|
197
201
|
return {
|
|
@@ -199,14 +203,50 @@ function buildResearchParams(input, options) {
|
|
|
199
203
|
outputType
|
|
200
204
|
};
|
|
201
205
|
}
|
|
202
|
-
function
|
|
206
|
+
function serializeSchema(schema) {
|
|
207
|
+
return typeof schema === "string" ? schema : JSON.stringify(schema);
|
|
208
|
+
}
|
|
209
|
+
async function linkupRequest(config, context, path, init = {}) {
|
|
203
210
|
const apiKey = config.credentials?.api;
|
|
204
211
|
if (!apiKey) {
|
|
205
212
|
throw new Error("is missing an API key");
|
|
206
213
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
214
|
+
const response = await fetch(
|
|
215
|
+
joinUrl(config.baseUrl ?? DEFAULT_BASE_URL, path),
|
|
216
|
+
{
|
|
217
|
+
method: init.body === void 0 ? "GET" : "POST",
|
|
218
|
+
headers: {
|
|
219
|
+
Accept: "application/json",
|
|
220
|
+
Authorization: `Bearer ${apiKey}`,
|
|
221
|
+
...init.body === void 0 ? {} : { "Content-Type": "application/json" }
|
|
222
|
+
},
|
|
223
|
+
...init.body === void 0 ? {} : { body: JSON.stringify(init.body) },
|
|
224
|
+
signal: context.signal
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
const payload = await readResponseBody(response);
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
throw linkupError(response, payload);
|
|
230
|
+
}
|
|
231
|
+
return expectJsonBody(payload, response);
|
|
232
|
+
}
|
|
233
|
+
var NON_RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
234
|
+
"INSUFFICIENT_FUNDS_CREDITS",
|
|
235
|
+
"EXCEED_BUDGET_LIMIT"
|
|
236
|
+
]);
|
|
237
|
+
function linkupError(response, payload) {
|
|
238
|
+
const error = asRecord(asRecord(payload)?.error);
|
|
239
|
+
const code = readString(error?.code);
|
|
240
|
+
const details = Array.isArray(error?.details) ? error.details : [];
|
|
241
|
+
const message = [
|
|
242
|
+
readString(error?.message) ?? (typeof payload === "string" && payload.trim() ? payload.trim().slice(0, 500) : `Linkup request failed with status ${response.status}.`),
|
|
243
|
+
...details.flatMap((detail) => {
|
|
244
|
+
const text = readString(asRecord(detail)?.message);
|
|
245
|
+
return text ? [text] : [];
|
|
246
|
+
})
|
|
247
|
+
].join(" ");
|
|
248
|
+
return new WebfoxError("PROVIDER_FAILURE", message, {
|
|
249
|
+
retryable: [408, 429, 500, 502, 503, 504].includes(response.status) && !(code && NON_RETRYABLE_CODES.has(code))
|
|
210
250
|
});
|
|
211
251
|
}
|
|
212
252
|
function formatResearchTaskOutput(task) {
|
|
@@ -298,7 +338,7 @@ function toDate(value, name) {
|
|
|
298
338
|
`Linkup option '${name}' must be a valid date string, timestamp, or Date.`
|
|
299
339
|
);
|
|
300
340
|
}
|
|
301
|
-
return date;
|
|
341
|
+
return date.toISOString().slice(0, 10);
|
|
302
342
|
}
|
|
303
343
|
var adapter = {
|
|
304
344
|
async search(input, config, context) {
|
|
@@ -1,52 +1,65 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatJson,
|
|
3
|
+
joinUrl,
|
|
4
|
+
requestJson,
|
|
3
5
|
trimSnippet
|
|
4
|
-
} from "./chunk-
|
|
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,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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(
|
|
33
|
+
results: flattenSearchResults(
|
|
34
|
+
asRecord(response.data) ?? {}
|
|
35
|
+
).slice(0, maxResults)
|
|
29
36
|
};
|
|
30
37
|
},
|
|
31
|
-
async contents(urls, config,
|
|
32
|
-
const
|
|
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
|
-
...
|
|
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
|
|
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
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
155
|
-
|
|
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,
|
|
169
|
+
throw new Error(readFirecrawlError(payload, failure));
|
|
166
170
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
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(
|
|
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) {
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
SERPBASE_MODES,
|
|
3
3
|
serpbaseProvider,
|
|
4
4
|
validateOptions
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LTJOWNI2.js";
|
|
6
6
|
import {
|
|
7
7
|
trimSnippet
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-FSTCLBSF.js";
|
|
9
9
|
import "./chunk-GPACLYNT.js";
|
|
10
10
|
import {
|
|
11
11
|
WebfoxError,
|
|
@@ -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,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
optionSchema,
|
|
3
3
|
providers
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LTJOWNI2.js";
|
|
5
5
|
import {
|
|
6
6
|
CAPABILITIES
|
|
7
7
|
} from "./chunk-GPACLYNT.js";
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
// package.json
|
|
10
10
|
var package_default = {
|
|
11
11
|
name: "webfox",
|
|
12
|
-
version: "4.
|
|
12
|
+
version: "4.9.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
|
-
"@
|
|
105
|
-
"@
|
|
106
|
-
|
|
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
|
-
|
|
110
|
-
"
|
|
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.
|
|
111
|
+
"valyu-js": "^2.11.5",
|
|
115
112
|
yaml: "^2.9.0"
|
|
116
113
|
},
|
|
117
114
|
devDependencies: {
|
|
@@ -504,7 +504,7 @@ var braveProvider = defineProvider({
|
|
|
504
504
|
retrySafe: false
|
|
505
505
|
}
|
|
506
506
|
},
|
|
507
|
-
load: async () => (await import("./adapter-
|
|
507
|
+
load: async () => (await import("./adapter-A5WYGGZI.js")).adapter
|
|
508
508
|
});
|
|
509
509
|
|
|
510
510
|
// src/providers/cloudflare/definition.ts
|
|
@@ -944,7 +944,7 @@ var exaProvider = defineProvider({
|
|
|
944
944
|
retrySafe: false
|
|
945
945
|
}
|
|
946
946
|
},
|
|
947
|
-
load: async () => (await import("./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-
|
|
1153
|
+
load: async () => (await import("./adapter-L6BFKWGX.js")).adapter
|
|
1154
1154
|
});
|
|
1155
1155
|
|
|
1156
1156
|
// src/providers/gemini/definition.ts
|
|
@@ -1473,7 +1473,7 @@ var linkupProvider = defineProvider({
|
|
|
1473
1473
|
retrySafe: false
|
|
1474
1474
|
}
|
|
1475
1475
|
},
|
|
1476
|
-
load: async () => (await import("./adapter-
|
|
1476
|
+
load: async () => (await import("./adapter-K6AMSUCR.js")).adapter
|
|
1477
1477
|
});
|
|
1478
1478
|
|
|
1479
1479
|
// src/providers/ollama/definition.ts
|
|
@@ -1502,7 +1502,7 @@ var ollamaProvider = defineProvider({
|
|
|
1502
1502
|
retrySafe: true
|
|
1503
1503
|
}
|
|
1504
1504
|
},
|
|
1505
|
-
load: async () => (await import("./adapter-
|
|
1505
|
+
load: async () => (await import("./adapter-HTMXAJTU.js")).adapter
|
|
1506
1506
|
});
|
|
1507
1507
|
|
|
1508
1508
|
// src/providers/openai/definition.ts
|
|
@@ -1610,7 +1610,7 @@ var openaiProvider = defineProvider({
|
|
|
1610
1610
|
retrySafe: false
|
|
1611
1611
|
}
|
|
1612
1612
|
},
|
|
1613
|
-
load: async () => (await import("./adapter-
|
|
1613
|
+
load: async () => (await import("./adapter-VMMS4YQG.js")).adapter
|
|
1614
1614
|
});
|
|
1615
1615
|
|
|
1616
1616
|
// src/providers/parallel/definition.ts
|
|
@@ -1769,7 +1769,7 @@ var parallelProvider = defineProvider({
|
|
|
1769
1769
|
retrySafe: true
|
|
1770
1770
|
}
|
|
1771
1771
|
},
|
|
1772
|
-
load: async () => (await import("./adapter-
|
|
1772
|
+
load: async () => (await import("./adapter-G3I7JEO5.js")).adapter
|
|
1773
1773
|
});
|
|
1774
1774
|
|
|
1775
1775
|
// src/providers/perplexity/definition.ts
|
|
@@ -1890,7 +1890,7 @@ var perplexityProvider = defineProvider({
|
|
|
1890
1890
|
answer: { options: chatOptions, retrySafe: false },
|
|
1891
1891
|
research: { options: chatOptions, retrySafe: false }
|
|
1892
1892
|
},
|
|
1893
|
-
load: async () => (await import("./adapter-
|
|
1893
|
+
load: async () => (await import("./adapter-OK2PTP6R.js")).adapter
|
|
1894
1894
|
});
|
|
1895
1895
|
|
|
1896
1896
|
// src/providers/serpbase/definition.ts
|
|
@@ -1991,7 +1991,7 @@ var serpbaseProvider = defineProvider({
|
|
|
1991
1991
|
retrySafe: true
|
|
1992
1992
|
}
|
|
1993
1993
|
},
|
|
1994
|
-
load: async () => (await import("./adapter-
|
|
1994
|
+
load: async () => (await import("./adapter-QIP32KA4.js")).adapter
|
|
1995
1995
|
});
|
|
1996
1996
|
|
|
1997
1997
|
// src/providers/serper/definition.ts
|
|
@@ -2131,7 +2131,7 @@ var serperProvider = defineProvider({
|
|
|
2131
2131
|
retrySafe: true
|
|
2132
2132
|
}
|
|
2133
2133
|
},
|
|
2134
|
-
load: async () => (await import("./adapter-
|
|
2134
|
+
load: async () => (await import("./adapter-C2VZHFUB.js")).adapter
|
|
2135
2135
|
});
|
|
2136
2136
|
|
|
2137
2137
|
// src/providers/tavily/definition.ts
|
|
@@ -2326,7 +2326,7 @@ var tavilyProvider = defineProvider({
|
|
|
2326
2326
|
retrySafe: true
|
|
2327
2327
|
}
|
|
2328
2328
|
},
|
|
2329
|
-
load: async () => (await import("./adapter-
|
|
2329
|
+
load: async () => (await import("./adapter-UIFBJKKF.js")).adapter
|
|
2330
2330
|
});
|
|
2331
2331
|
|
|
2332
2332
|
// src/providers/valyu/definition.ts
|
|
@@ -2794,7 +2794,7 @@ var valyuProvider = defineProvider({
|
|
|
2794
2794
|
retrySafe: false
|
|
2795
2795
|
}
|
|
2796
2796
|
},
|
|
2797
|
-
load: async () => (await import("./adapter-
|
|
2797
|
+
load: async () => (await import("./adapter-764OQFKY.js")).adapter
|
|
2798
2798
|
});
|
|
2799
2799
|
|
|
2800
2800
|
// src/providers/youcom/definition.ts
|
|
@@ -2985,7 +2985,7 @@ var youcomProvider = defineProvider({
|
|
|
2985
2985
|
retrySafe: true
|
|
2986
2986
|
}
|
|
2987
2987
|
},
|
|
2988
|
-
load: async () => (await import("./adapter-
|
|
2988
|
+
load: async () => (await import("./adapter-2UVAARTV.js")).adapter
|
|
2989
2989
|
});
|
|
2990
2990
|
|
|
2991
2991
|
// src/providers/registry.ts
|
|
@@ -6,14 +6,14 @@ import {
|
|
|
6
6
|
} from "./chunk-WCTOYZVB.js";
|
|
7
7
|
import {
|
|
8
8
|
configurationSchema
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-HCYQF24V.js";
|
|
10
10
|
import {
|
|
11
11
|
effectiveOptions,
|
|
12
12
|
optionSchema,
|
|
13
13
|
providers,
|
|
14
14
|
selectProvider,
|
|
15
15
|
validateConfiguredOptions
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-LTJOWNI2.js";
|
|
17
17
|
import {
|
|
18
18
|
cancelProcessGroup,
|
|
19
19
|
killProcessGroup
|
package/dist/cli.js
CHANGED
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
redactConfig,
|
|
12
12
|
resolveConfigPath,
|
|
13
13
|
setCapabilityDefault
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-QZFN2747.js";
|
|
15
15
|
import "./chunk-WCTOYZVB.js";
|
|
16
16
|
import {
|
|
17
17
|
PACKAGE_VERSION
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-HCYQF24V.js";
|
|
19
19
|
import {
|
|
20
20
|
validateConfiguredOptions
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-LTJOWNI2.js";
|
|
22
22
|
import "./chunk-ZVTVERW5.js";
|
|
23
23
|
import {
|
|
24
24
|
CAPABILITIES,
|
package/dist/durable.js
CHANGED
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
} from "./chunk-BTWECET5.js";
|
|
9
9
|
import {
|
|
10
10
|
createWebfox
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-QZFN2747.js";
|
|
12
12
|
import "./chunk-WCTOYZVB.js";
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-HCYQF24V.js";
|
|
14
|
+
import "./chunk-LTJOWNI2.js";
|
|
15
15
|
import "./chunk-ZVTVERW5.js";
|
|
16
16
|
import {
|
|
17
17
|
CAPABILITIES
|
package/dist/index.js
CHANGED
|
@@ -6,14 +6,14 @@ import {
|
|
|
6
6
|
resolveConfigPath,
|
|
7
7
|
setCapabilityDefault,
|
|
8
8
|
validateConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-QZFN2747.js";
|
|
10
10
|
import "./chunk-WCTOYZVB.js";
|
|
11
11
|
import {
|
|
12
12
|
CONFIG_SCHEMA_URL
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-HCYQF24V.js";
|
|
14
14
|
import {
|
|
15
15
|
validateConfiguredOptions
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-LTJOWNI2.js";
|
|
17
17
|
import "./chunk-ZVTVERW5.js";
|
|
18
18
|
import {
|
|
19
19
|
CAPABILITIES,
|
package/dist/pi.js
CHANGED
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
createWebfox,
|
|
11
11
|
resolveConfigPath
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-QZFN2747.js";
|
|
13
13
|
import "./chunk-WCTOYZVB.js";
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-HCYQF24V.js";
|
|
15
|
+
import "./chunk-LTJOWNI2.js";
|
|
16
16
|
import "./chunk-ZVTVERW5.js";
|
|
17
17
|
import {
|
|
18
18
|
CAPABILITIES,
|
|
@@ -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
|
@@ -160,8 +160,49 @@ web search --provider openai --help
|
|
|
160
160
|
|
|
161
161
|
Supports search and page extraction. Set `PARALLEL_API_KEY`.
|
|
162
162
|
|
|
163
|
+
Search defaults to `advanced`. Choose `fast` for interactive lookups, or see
|
|
164
|
+
[Parallel's search modes](https://docs.parallel.ai/search/modes) for the available
|
|
165
|
+
tradeoffs:
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
web search "Node.js release notes" --provider parallel --mode fast
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Limit a query to particular sources with `advanced_settings.source_policy`.
|
|
172
|
+
Use Webfox's `--max-results` to control the number of results:
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
web search "Node.js release notes" --provider parallel --mode fast --max-results 3 \
|
|
176
|
+
--options-json '{"advanced_settings":{"source_policy":{"include_domains":["nodejs.org"]}}}'
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
To reuse Fast mode, merge this into your [configuration](./reference.md#configuration):
|
|
180
|
+
|
|
181
|
+
```yaml
|
|
182
|
+
providers:
|
|
183
|
+
parallel:
|
|
184
|
+
options:
|
|
185
|
+
search:
|
|
186
|
+
mode: fast
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Provider options don't select a provider. Continue to pass `--provider parallel`
|
|
190
|
+
or use your saved Parallel search default. A request's `--mode` overrides the
|
|
191
|
+
saved mode.
|
|
192
|
+
|
|
193
|
+
For known URLs, use page extraction instead of another search. Parallel returns
|
|
194
|
+
full page content by default:
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
web contents https://nodejs.org/en/about/previous-releases --provider parallel
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
To request only excerpts relevant to a question, disable full content and enable
|
|
201
|
+
excerpts with an objective:
|
|
202
|
+
|
|
163
203
|
```sh
|
|
164
|
-
web
|
|
204
|
+
web contents https://nodejs.org/en/about/previous-releases --provider parallel \
|
|
205
|
+
--no-full-content --excerpts --objective "Which Node.js releases are supported?"
|
|
165
206
|
```
|
|
166
207
|
|
|
167
208
|
## Perplexity
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webfox",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.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
|
-
"@
|
|
96
|
-
"@
|
|
97
|
-
"
|
|
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
|
-
"
|
|
101
|
-
"
|
|
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.
|
|
102
|
+
"valyu-js": "^2.11.5",
|
|
106
103
|
"yaml": "^2.9.0"
|
|
107
104
|
},
|
|
108
105
|
"devDependencies": {
|
package/dist/chunk-X6ZBIHZC.js
DELETED
|
@@ -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
|
-
};
|