webfox 4.1.0 → 4.2.1
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/README.md +4 -1
- package/dist/{adapter-AB7LKV7N.js → adapter-6EZAIE65.js} +86 -48
- package/dist/{chunk-2DWEBTPX.js → chunk-SGHBA2PZ.js} +50 -17
- package/dist/{chunk-7BXAXZPA.js → chunk-TJWQTB6W.js} +5 -6
- package/dist/cli.js +2 -2
- package/dist/config.schema.json +2 -2
- package/dist/errors.d.ts +8 -0
- package/dist/index.js +2 -2
- package/dist/pi-diagnostics.d.ts +2 -0
- package/dist/pi.js +45 -10
- package/dist/providers/gemini/adapter.d.ts +0 -2
- package/example-config.yaml +1 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -174,7 +174,7 @@ or, on Windows, `APPDATA`. Override it with `WEBFOX_CONFIG` or `--config <path>`
|
|
|
174
174
|
For example:
|
|
175
175
|
|
|
176
176
|
```yaml
|
|
177
|
-
$schema: https://unpkg.com/webfox@
|
|
177
|
+
$schema: https://unpkg.com/webfox@latest/dist/config.schema.json
|
|
178
178
|
defaults:
|
|
179
179
|
search:
|
|
180
180
|
provider: brave
|
|
@@ -252,6 +252,9 @@ configuration, progress, and errors.
|
|
|
252
252
|
`web config default <capability> <provider>`.
|
|
253
253
|
- **Missing credentials:** Run `web providers <id>` to check the required key
|
|
254
254
|
names, or see the [provider guide](./docs/provider.md).
|
|
255
|
+
- **Invalid configuration in pi:** Pi continues to start, but Webfox registers
|
|
256
|
+
no tools and reports the error (on stderr in print and JSON modes). Fix the
|
|
257
|
+
configuration, then restart Pi or run `/reload` to load the extension again.
|
|
255
258
|
- **No pi tools:** Select default providers in the shared configuration and
|
|
256
259
|
restart pi. Installing the extension or setting keys alone isn't enough.
|
|
257
260
|
|
|
@@ -3,30 +3,35 @@ import {
|
|
|
3
3
|
} from "./chunk-EF4VMDZL.js";
|
|
4
4
|
import "./chunk-I2UIACY3.js";
|
|
5
5
|
import "./chunk-WCTOYZVB.js";
|
|
6
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
httpError
|
|
8
|
+
} from "./chunk-U4BLULLV.js";
|
|
7
9
|
|
|
8
10
|
// src/providers/gemini/adapter.ts
|
|
9
|
-
import { GoogleGenAI } from "@google/genai";
|
|
10
11
|
var DEFAULT_ANSWER_MODEL = "gemini-3.8-flash";
|
|
11
12
|
var DEFAULT_RESEARCH_AGENT = "deep-research-preview-04-2026";
|
|
12
13
|
var geminiImplementation = {
|
|
13
14
|
async answer(query, config, context, options) {
|
|
14
|
-
const ai = this.createClient(config);
|
|
15
15
|
const request = buildGeminiGenerateContentRequest({
|
|
16
16
|
defaultModel: DEFAULT_ANSWER_MODEL,
|
|
17
17
|
prompt: query,
|
|
18
18
|
options,
|
|
19
19
|
toolConfig: { googleSearch: {} }
|
|
20
20
|
});
|
|
21
|
-
const response = await
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
const response = await requestGemini(
|
|
22
|
+
`models/${encodeURIComponent(request.model.replace(/^models\//, ""))}:generateContent`,
|
|
23
|
+
config,
|
|
24
|
+
context,
|
|
25
|
+
request.body
|
|
26
|
+
);
|
|
27
|
+
const candidate = Array.isArray(response.candidates) ? asRecord(response.candidates[0]) : {};
|
|
28
|
+
const parts = asRecord(candidate.content).parts;
|
|
29
|
+
const text = Array.isArray(parts) ? parts.filter(
|
|
30
|
+
(part) => isPlainObject(part) && !part.thought && typeof part.text === "string"
|
|
31
|
+
).map((part) => part.text).join("").trim() : "";
|
|
32
|
+
const lines = [text || "No answer returned."];
|
|
28
33
|
const sources = extractGroundingSources(
|
|
29
|
-
|
|
34
|
+
asRecord(candidate.groundingMetadata).groundingChunks
|
|
30
35
|
);
|
|
31
36
|
if (sources.length > 0) {
|
|
32
37
|
lines.push("");
|
|
@@ -54,25 +59,30 @@ var geminiImplementation = {
|
|
|
54
59
|
});
|
|
55
60
|
},
|
|
56
61
|
async startResearch(input, config, context, options) {
|
|
57
|
-
const ai = this.createClient(config);
|
|
58
62
|
const requestOptions = getGeminiResearchRequestOptions(options);
|
|
59
|
-
const interaction = await
|
|
63
|
+
const interaction = await requestGemini(
|
|
64
|
+
"interactions",
|
|
65
|
+
config,
|
|
66
|
+
context,
|
|
60
67
|
{
|
|
61
68
|
...requestOptions,
|
|
62
69
|
input,
|
|
63
70
|
agent: DEFAULT_RESEARCH_AGENT,
|
|
64
71
|
background: true
|
|
65
72
|
},
|
|
66
|
-
|
|
73
|
+
context.idempotencyKey
|
|
67
74
|
);
|
|
68
|
-
|
|
75
|
+
const id = readNonEmptyString(interaction.id);
|
|
76
|
+
if (!id) {
|
|
77
|
+
throw new Error("Gemini research response is missing an interaction ID.");
|
|
78
|
+
}
|
|
79
|
+
return { id };
|
|
69
80
|
},
|
|
70
81
|
async pollResearch(id, config, context, _options) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
buildGeminiRequestOptions(context.signal)
|
|
82
|
+
const interaction = await requestGemini(
|
|
83
|
+
`interactions/${encodeURIComponent(id)}`,
|
|
84
|
+
config,
|
|
85
|
+
context
|
|
76
86
|
);
|
|
77
87
|
const status = readNonEmptyString(interaction.status) ?? "unknown";
|
|
78
88
|
if (status === "completed") {
|
|
@@ -110,33 +120,46 @@ var geminiImplementation = {
|
|
|
110
120
|
};
|
|
111
121
|
}
|
|
112
122
|
return status === "in_progress" ? { status: "in_progress" } : { status: "in_progress", statusText: status };
|
|
113
|
-
},
|
|
114
|
-
createClient(config) {
|
|
115
|
-
const apiKey = config.credentials?.api;
|
|
116
|
-
if (!apiKey) {
|
|
117
|
-
throw new Error("is missing an API key");
|
|
118
|
-
}
|
|
119
|
-
return new GoogleGenAI({
|
|
120
|
-
httpOptions: { retryOptions: { attempts: 1 } },
|
|
121
|
-
apiKey
|
|
122
|
-
});
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
125
|
+
async function requestGemini(path, config, context, body, idempotencyKey) {
|
|
126
|
+
const apiKey = config.credentials?.api;
|
|
127
|
+
if (!apiKey) throw new Error("is missing an API key");
|
|
128
|
+
const response = await fetch(
|
|
129
|
+
`https://generativelanguage.googleapis.com/v1beta/${path}`,
|
|
130
|
+
{
|
|
131
|
+
method: body === void 0 ? "GET" : "POST",
|
|
132
|
+
headers: {
|
|
133
|
+
"content-type": "application/json",
|
|
134
|
+
"x-goog-api-key": apiKey,
|
|
135
|
+
...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
|
|
136
|
+
},
|
|
137
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) },
|
|
138
|
+
signal: context.signal
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
const text = (await response.text()).trim();
|
|
143
|
+
let detail = text;
|
|
144
|
+
try {
|
|
145
|
+
const error = asRecord(asRecord(JSON.parse(text)).error);
|
|
146
|
+
detail = readNonEmptyString(error.message) ?? text;
|
|
147
|
+
} catch {
|
|
148
|
+
}
|
|
149
|
+
detail = detail.replaceAll(apiKey, "[redacted]").slice(0, 1e3);
|
|
150
|
+
throw httpError(
|
|
151
|
+
response,
|
|
152
|
+
`Gemini API request failed (${response.status})${detail ? `: ${detail}` : "."}`
|
|
153
|
+
);
|
|
135
154
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
155
|
+
const payload = await response.json();
|
|
156
|
+
if (!isPlainObject(payload)) {
|
|
157
|
+
throw new Error("Gemini API returned an invalid response.");
|
|
158
|
+
}
|
|
159
|
+
return payload;
|
|
160
|
+
}
|
|
161
|
+
function asRecord(value) {
|
|
162
|
+
return isPlainObject(value) ? value : {};
|
|
140
163
|
}
|
|
141
164
|
function readInteractionSteps(interaction) {
|
|
142
165
|
return typeof interaction === "object" && interaction !== null ? interaction.steps : void 0;
|
|
@@ -229,12 +252,27 @@ function buildGeminiGenerateContentRequest({
|
|
|
229
252
|
}) {
|
|
230
253
|
const requestOptions = isPlainObject(options) ? options : {};
|
|
231
254
|
const explicitConfig = isPlainObject(requestOptions.config) ? requestOptions.config : {};
|
|
255
|
+
if (explicitConfig.labels !== void 0) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
"Gemini answer config.labels is not supported by the Gemini Developer API."
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
const generationConfig = Object.fromEntries(
|
|
261
|
+
[
|
|
262
|
+
"thinkingConfig",
|
|
263
|
+
"temperature",
|
|
264
|
+
"topP",
|
|
265
|
+
"topK",
|
|
266
|
+
"candidateCount",
|
|
267
|
+
"maxOutputTokens"
|
|
268
|
+
].filter((key) => explicitConfig[key] !== void 0).map((key) => [key, explicitConfig[key]])
|
|
269
|
+
);
|
|
232
270
|
return {
|
|
233
271
|
model: readNonEmptyString(requestOptions.model) ?? defaultModel,
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
272
|
+
body: {
|
|
273
|
+
contents: [{ role: "user", parts: [{ text: prompt }] }],
|
|
274
|
+
tools: [toolConfig],
|
|
275
|
+
...Object.keys(generationConfig).length ? { generationConfig } : {}
|
|
238
276
|
}
|
|
239
277
|
};
|
|
240
278
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
providers,
|
|
12
12
|
selectProvider,
|
|
13
13
|
validateConfiguredOptions
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-TJWQTB6W.js";
|
|
15
15
|
import {
|
|
16
16
|
cancelProcessGroup,
|
|
17
17
|
killProcessGroup
|
|
@@ -69,7 +69,13 @@ function readFailure(error, options, path) {
|
|
|
69
69
|
if (error instanceof WebfoxError) throw error;
|
|
70
70
|
throw new WebfoxError(
|
|
71
71
|
"INVALID_CONFIG",
|
|
72
|
-
`Could not read configuration: ${path}. Check the path and file permissions
|
|
72
|
+
`Could not read configuration: ${path}. Check the path and file permissions.`,
|
|
73
|
+
{
|
|
74
|
+
configuration: {
|
|
75
|
+
source: path,
|
|
76
|
+
issues: ["Could not read file. Check the path and permissions."]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
73
79
|
);
|
|
74
80
|
}
|
|
75
81
|
function parseConfigDocument(text, source = "config.yaml") {
|
|
@@ -91,7 +97,15 @@ function parseConfigDocument(text, source = "config.yaml") {
|
|
|
91
97
|
} catch {
|
|
92
98
|
throw new WebfoxError(
|
|
93
99
|
"INVALID_CONFIG",
|
|
94
|
-
`Invalid YAML in ${source}. Use one YAML 1.2 document with unique keys, with string keys and finite numbers, without aliases or explicit tags
|
|
100
|
+
`Invalid YAML in ${source}. Use one YAML 1.2 document with unique keys, with string keys and finite numbers, without aliases or explicit tags.`,
|
|
101
|
+
{
|
|
102
|
+
configuration: {
|
|
103
|
+
source,
|
|
104
|
+
issues: [
|
|
105
|
+
"Invalid YAML. Use YAML 1.2 without duplicate keys, aliases, or explicit tags."
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
95
109
|
);
|
|
96
110
|
}
|
|
97
111
|
}
|
|
@@ -105,14 +119,30 @@ function parseConfig(text, source = "config.yaml") {
|
|
|
105
119
|
function validateConfig(value, source = "configuration") {
|
|
106
120
|
const schema = configurationSchema;
|
|
107
121
|
if (!Check(schema, value)) {
|
|
108
|
-
const
|
|
122
|
+
const errors = Errors(schema, value);
|
|
123
|
+
const detail = errors.slice(0, 3).map((error) => `${error.instancePath || "/"}: ${error.message}`).join("; ");
|
|
109
124
|
throw new WebfoxError(
|
|
110
125
|
"INVALID_CONFIG",
|
|
111
|
-
`Invalid ${source}: ${detail}. Provider options belong under providers.<id>.options.<capability
|
|
126
|
+
`Invalid ${source}: ${detail}. Provider options belong under providers.<id>.options.<capability>.`,
|
|
127
|
+
{ configuration: { source, issues: configurationIssues(errors) } }
|
|
112
128
|
);
|
|
113
129
|
}
|
|
114
130
|
return structuredClone(value);
|
|
115
131
|
}
|
|
132
|
+
function configurationIssues(errors) {
|
|
133
|
+
const issues = /* @__PURE__ */ new Map();
|
|
134
|
+
for (const error of errors) {
|
|
135
|
+
if (error.keyword === "additionalProperties") continue;
|
|
136
|
+
const path = error.instancePath || "/";
|
|
137
|
+
if (issues.has(path)) continue;
|
|
138
|
+
const unknownKey = error.keyword === "boolean" && error.schemaPath.endsWith("/additionalProperties");
|
|
139
|
+
const key = path.slice(1).split("/").map((part) => part.replaceAll("~1", "/").replaceAll("~0", "~")).join(".");
|
|
140
|
+
const message = unknownKey ? `Unknown key: ${key}` : error.keyword === "enum" || error.keyword === "const" ? `${path}: unsupported value` : `${path}: ${error.message}`;
|
|
141
|
+
issues.set(path, message);
|
|
142
|
+
if (issues.size === 3) break;
|
|
143
|
+
}
|
|
144
|
+
return issues.size ? [...issues.values()] : ["Invalid configuration structure."];
|
|
145
|
+
}
|
|
116
146
|
function redactConfig(config) {
|
|
117
147
|
const visit2 = (entry) => {
|
|
118
148
|
if (Array.isArray(entry)) return entry.map(visit2);
|
|
@@ -645,26 +675,29 @@ function createWebfox(options = {}) {
|
|
|
645
675
|
}
|
|
646
676
|
};
|
|
647
677
|
const definition = selectProvider(config, capability, selected);
|
|
648
|
-
|
|
678
|
+
const defaults = outward.value({
|
|
679
|
+
options: effectiveOptions(
|
|
680
|
+
config,
|
|
681
|
+
definition,
|
|
682
|
+
capability,
|
|
683
|
+
{},
|
|
684
|
+
"defaults"
|
|
685
|
+
),
|
|
686
|
+
...capability === "search" ? { maxResults: config.defaults?.search?.maxResults ?? 5 } : {}
|
|
687
|
+
});
|
|
688
|
+
return {
|
|
649
689
|
capability,
|
|
650
690
|
provider: selected,
|
|
651
691
|
configured: configured(definition, capability),
|
|
692
|
+
// Static provider schemas contain no credentials. Redacting property
|
|
693
|
+
// names such as maximum_number_of_tokens would invalidate the schema.
|
|
652
694
|
optionSchema: optionSchema(
|
|
653
695
|
definition,
|
|
654
696
|
capability,
|
|
655
697
|
"defaults"
|
|
656
698
|
),
|
|
657
|
-
defaults
|
|
658
|
-
|
|
659
|
-
config,
|
|
660
|
-
definition,
|
|
661
|
-
capability,
|
|
662
|
-
{},
|
|
663
|
-
"defaults"
|
|
664
|
-
),
|
|
665
|
-
...capability === "search" ? { maxResults: config.defaults?.search?.maxResults ?? 5 } : {}
|
|
666
|
-
}
|
|
667
|
-
});
|
|
699
|
+
defaults
|
|
700
|
+
};
|
|
668
701
|
}
|
|
669
702
|
};
|
|
670
703
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "webfox",
|
|
11
|
-
version: "4.1
|
|
11
|
+
version: "4.2.1",
|
|
12
12
|
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.",
|
|
13
13
|
type: "module",
|
|
14
14
|
files: [
|
|
@@ -96,7 +96,6 @@ var package_default = {
|
|
|
96
96
|
"test:watch": "vitest"
|
|
97
97
|
},
|
|
98
98
|
dependencies: {
|
|
99
|
-
"@google/genai": "^2.21.0",
|
|
100
99
|
"@mendable/firecrawl-js": "^4.38.0",
|
|
101
100
|
"@perplexity-ai/perplexity_ai": "^0.38.5",
|
|
102
101
|
"@tavily/core": "^0.7.9",
|
|
@@ -138,7 +137,7 @@ var package_default = {
|
|
|
138
137
|
// src/package-metadata.ts
|
|
139
138
|
var PACKAGE_NAME = package_default.name;
|
|
140
139
|
var PACKAGE_VERSION = package_default.version;
|
|
141
|
-
var CONFIG_SCHEMA_URL = `https://unpkg.com/${PACKAGE_NAME}
|
|
140
|
+
var CONFIG_SCHEMA_URL = `https://unpkg.com/${PACKAGE_NAME}@latest/dist/config.schema.json`;
|
|
142
141
|
|
|
143
142
|
// src/configuration/planning.ts
|
|
144
143
|
import { Check, Errors } from "typebox/value";
|
|
@@ -1249,7 +1248,7 @@ var firecrawlProvider = defineProvider({
|
|
|
1249
1248
|
var geminiProvider = defineProvider({
|
|
1250
1249
|
id: "gemini",
|
|
1251
1250
|
label: "Gemini",
|
|
1252
|
-
docsUrl: "https://
|
|
1251
|
+
docsUrl: "https://ai.google.dev/api",
|
|
1253
1252
|
local: false,
|
|
1254
1253
|
credentials: [
|
|
1255
1254
|
{
|
|
@@ -1303,7 +1302,7 @@ var geminiProvider = defineProvider({
|
|
|
1303
1302
|
type: "string"
|
|
1304
1303
|
}
|
|
1305
1304
|
},
|
|
1306
|
-
description: "
|
|
1305
|
+
description: "Unsupported by the Gemini Developer API; requests with labels are rejected."
|
|
1307
1306
|
},
|
|
1308
1307
|
temperature: {
|
|
1309
1308
|
type: "number",
|
|
@@ -1367,7 +1366,7 @@ var geminiProvider = defineProvider({
|
|
|
1367
1366
|
retrySafe: false
|
|
1368
1367
|
}
|
|
1369
1368
|
},
|
|
1370
|
-
load: async () => (await import("./adapter-
|
|
1369
|
+
load: async () => (await import("./adapter-6EZAIE65.js")).adapter
|
|
1371
1370
|
});
|
|
1372
1371
|
|
|
1373
1372
|
// src/providers/linkup/definition.ts
|
package/dist/cli.js
CHANGED
|
@@ -11,12 +11,12 @@ import {
|
|
|
11
11
|
redactConfig,
|
|
12
12
|
resolveConfigPath,
|
|
13
13
|
setCapabilityDefault
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-SGHBA2PZ.js";
|
|
15
15
|
import "./chunk-WCTOYZVB.js";
|
|
16
16
|
import {
|
|
17
17
|
PACKAGE_VERSION,
|
|
18
18
|
validateConfiguredOptions
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-TJWQTB6W.js";
|
|
20
20
|
import "./chunk-ZVTVERW5.js";
|
|
21
21
|
import {
|
|
22
22
|
CAPABILITIES,
|
package/dist/config.schema.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://unpkg.com/webfox@
|
|
3
|
+
"$id": "https://unpkg.com/webfox@latest/dist/config.schema.json",
|
|
4
4
|
"title": "webfox configuration",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": false,
|
|
@@ -2115,7 +2115,7 @@
|
|
|
2115
2115
|
"type": "string"
|
|
2116
2116
|
}
|
|
2117
2117
|
},
|
|
2118
|
-
"description": "
|
|
2118
|
+
"description": "Unsupported by the Gemini Developer API; requests with labels are rejected."
|
|
2119
2119
|
},
|
|
2120
2120
|
"temperature": {
|
|
2121
2121
|
"type": "number",
|
package/dist/errors.d.ts
CHANGED
|
@@ -4,11 +4,19 @@ export declare class WebfoxError extends Error {
|
|
|
4
4
|
readonly options: {
|
|
5
5
|
cause?: unknown;
|
|
6
6
|
retryable?: boolean;
|
|
7
|
+
configuration?: {
|
|
8
|
+
source: string;
|
|
9
|
+
issues: string[];
|
|
10
|
+
};
|
|
7
11
|
};
|
|
8
12
|
readonly name = "WebfoxError";
|
|
9
13
|
constructor(code: WebfoxErrorCode, message: string, options?: {
|
|
10
14
|
cause?: unknown;
|
|
11
15
|
retryable?: boolean;
|
|
16
|
+
configuration?: {
|
|
17
|
+
source: string;
|
|
18
|
+
issues: string[];
|
|
19
|
+
};
|
|
12
20
|
});
|
|
13
21
|
toJSON(): SerializedError;
|
|
14
22
|
}
|
package/dist/index.js
CHANGED
|
@@ -6,12 +6,12 @@ import {
|
|
|
6
6
|
resolveConfigPath,
|
|
7
7
|
setCapabilityDefault,
|
|
8
8
|
validateConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-SGHBA2PZ.js";
|
|
10
10
|
import "./chunk-WCTOYZVB.js";
|
|
11
11
|
import {
|
|
12
12
|
CONFIG_SCHEMA_URL,
|
|
13
13
|
validateConfiguredOptions
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-TJWQTB6W.js";
|
|
15
15
|
import "./chunk-ZVTVERW5.js";
|
|
16
16
|
import {
|
|
17
17
|
CAPABILITIES,
|
package/dist/pi.js
CHANGED
|
@@ -2,15 +2,18 @@ import {
|
|
|
2
2
|
renderTextDocument
|
|
3
3
|
} from "./chunk-QRHDXM2L.js";
|
|
4
4
|
import {
|
|
5
|
-
createWebfox
|
|
6
|
-
|
|
5
|
+
createWebfox,
|
|
6
|
+
resolveConfigPath
|
|
7
|
+
} from "./chunk-SGHBA2PZ.js";
|
|
7
8
|
import "./chunk-WCTOYZVB.js";
|
|
8
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-TJWQTB6W.js";
|
|
9
10
|
import "./chunk-ZVTVERW5.js";
|
|
10
11
|
import {
|
|
11
12
|
CAPABILITIES
|
|
12
13
|
} from "./chunk-WVHMWLKU.js";
|
|
13
|
-
import
|
|
14
|
+
import {
|
|
15
|
+
WebfoxError
|
|
16
|
+
} from "./chunk-U4BLULLV.js";
|
|
14
17
|
|
|
15
18
|
// src/pi.ts
|
|
16
19
|
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
@@ -417,6 +420,24 @@ function prepareToolArguments(schema, args) {
|
|
|
417
420
|
return args;
|
|
418
421
|
}
|
|
419
422
|
|
|
423
|
+
// src/pi-diagnostics.ts
|
|
424
|
+
import { homedir } from "node:os";
|
|
425
|
+
import { sep } from "node:path";
|
|
426
|
+
function configurationDiagnostic(error) {
|
|
427
|
+
const diagnostic = error.options.configuration;
|
|
428
|
+
const source = diagnostic?.source ?? resolveConfigPath();
|
|
429
|
+
const home = homedir() + sep;
|
|
430
|
+
const path = source.startsWith(home) ? `~/${source.slice(home.length)}` : source;
|
|
431
|
+
const issues = diagnostic?.issues ?? [error.message];
|
|
432
|
+
return [
|
|
433
|
+
"Webfox disabled \u2014 invalid configuration",
|
|
434
|
+
` ${path}`,
|
|
435
|
+
...issues.map((issue) => ` ${issue}`),
|
|
436
|
+
"",
|
|
437
|
+
"Fix the configuration, then /reload."
|
|
438
|
+
].join("\n");
|
|
439
|
+
}
|
|
440
|
+
|
|
420
441
|
// src/pi.ts
|
|
421
442
|
function webExtension(pi) {
|
|
422
443
|
const clients = /* @__PURE__ */ new Map();
|
|
@@ -428,10 +449,24 @@ function webExtension(pi) {
|
|
|
428
449
|
}
|
|
429
450
|
return client;
|
|
430
451
|
};
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
452
|
+
let selected;
|
|
453
|
+
try {
|
|
454
|
+
const initial = clientFor(process.cwd());
|
|
455
|
+
selected = CAPABILITIES.map(
|
|
456
|
+
(capability) => initial.inspectCapability(capability)
|
|
457
|
+
).filter((inspection) => inspection.provider);
|
|
458
|
+
} catch (error) {
|
|
459
|
+
if (!(error instanceof WebfoxError) || !["INVALID_CONFIG", "INVALID_INPUT", "PROVIDER_UNAVAILABLE"].includes(
|
|
460
|
+
error.code
|
|
461
|
+
))
|
|
462
|
+
throw error;
|
|
463
|
+
const message = configurationDiagnostic(error);
|
|
464
|
+
pi.on("session_start", (_event, context) => {
|
|
465
|
+
if (context.hasUI) context.ui.notify(message, "error");
|
|
466
|
+
else console.error(message);
|
|
467
|
+
});
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
435
470
|
if (!selected.length)
|
|
436
471
|
pi.on("session_start", (_event, context) => {
|
|
437
472
|
if (context.hasUI)
|
|
@@ -440,8 +475,8 @@ function webExtension(pi) {
|
|
|
440
475
|
"warning"
|
|
441
476
|
);
|
|
442
477
|
});
|
|
443
|
-
for (const
|
|
444
|
-
const
|
|
478
|
+
for (const inspection of selected) {
|
|
479
|
+
const capability = inspection.capability;
|
|
445
480
|
const provider = inspection.provider;
|
|
446
481
|
const fields = capability === "contents" ? { urls: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }) } : capability === "research" ? { input: Type.String({ minLength: 1 }) } : {
|
|
447
482
|
queries: Type.Array(Type.String({ minLength: 1 }), {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { GoogleGenAI } from "@google/genai";
|
|
2
1
|
import type { ProviderContext, ResearchJob, ResearchPollResult, ToolOutput } from "../contract.js";
|
|
3
2
|
import type { Gemini } from "./types.js";
|
|
4
3
|
export declare const geminiImplementation: {
|
|
@@ -6,7 +5,6 @@ export declare const geminiImplementation: {
|
|
|
6
5
|
research(input: string, config: Gemini, context: ProviderContext, options?: Record<string, unknown>): Promise<ToolOutput>;
|
|
7
6
|
startResearch(input: string, config: Gemini, context: ProviderContext, options?: Record<string, unknown>): Promise<ResearchJob>;
|
|
8
7
|
pollResearch(id: string, config: Gemini, context: ProviderContext, _options?: Record<string, unknown>): Promise<ResearchPollResult>;
|
|
9
|
-
createClient(config: Gemini): GoogleGenAI;
|
|
10
8
|
};
|
|
11
9
|
export declare const adapter: {
|
|
12
10
|
answer(input: import("../contract.js").ProviderRequest<"answer">, config: Gemini, context: ProviderContext): Promise<ToolOutput>;
|
package/example-config.yaml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webfox",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
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": [
|
|
@@ -88,7 +88,6 @@
|
|
|
88
88
|
"test:watch": "vitest"
|
|
89
89
|
},
|
|
90
90
|
"dependencies": {
|
|
91
|
-
"@google/genai": "^2.21.0",
|
|
92
91
|
"@mendable/firecrawl-js": "^4.38.0",
|
|
93
92
|
"@perplexity-ai/perplexity_ai": "^0.38.5",
|
|
94
93
|
"@tavily/core": "^0.7.9",
|