webfox 4.2.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.
@@ -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 "./chunk-U4BLULLV.js";
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 ai.models.generateContent({
22
- model: request.model,
23
- contents: request.contents,
24
- config: addAbortSignalToGeminiConfig(request.config, context.signal)
25
- });
26
- const lines = [];
27
- lines.push(response.text?.trim() || "No answer returned.");
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
- response.candidates?.[0]?.groundingMetadata?.groundingChunks
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 ai.interactions.create(
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
- buildGeminiRequestOptions(context.signal, context.idempotencyKey)
73
+ context.idempotencyKey
67
74
  );
68
- return { id: interaction.id };
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 ai = this.createClient(config);
72
- const interaction = await ai.interactions.get(
73
- id,
74
- void 0,
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 buildGeminiRequestOptions(signal, idempotencyKey) {
126
- return {
127
- maxRetries: 0,
128
- ...signal ? { signal } : {},
129
- ...idempotencyKey ? { idempotencyKey } : {}
130
- };
131
- }
132
- function addAbortSignalToGeminiConfig(config, signal) {
133
- if (!signal) {
134
- return config;
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
- return {
137
- ...config ?? {},
138
- abortSignal: signal
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
- contents: prompt,
235
- config: {
236
- ...explicitConfig,
237
- tools: [toolConfig]
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-CIZXLW6C.js";
14
+ } from "./chunk-TJWQTB6W.js";
15
15
  import {
16
16
  cancelProcessGroup,
17
17
  killProcessGroup
@@ -8,7 +8,7 @@ import {
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "webfox",
11
- version: "4.2.0",
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",
@@ -1249,7 +1248,7 @@ var firecrawlProvider = defineProvider({
1249
1248
  var geminiProvider = defineProvider({
1250
1249
  id: "gemini",
1251
1250
  label: "Gemini",
1252
- docsUrl: "https://github.com/googleapis/js-genai",
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: "Request labels to attach to the Gemini call."
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-AB7LKV7N.js")).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-KC6K6LCQ.js";
14
+ } from "./chunk-SGHBA2PZ.js";
15
15
  import "./chunk-WCTOYZVB.js";
16
16
  import {
17
17
  PACKAGE_VERSION,
18
18
  validateConfiguredOptions
19
- } from "./chunk-CIZXLW6C.js";
19
+ } from "./chunk-TJWQTB6W.js";
20
20
  import "./chunk-ZVTVERW5.js";
21
21
  import {
22
22
  CAPABILITIES,
@@ -2115,7 +2115,7 @@
2115
2115
  "type": "string"
2116
2116
  }
2117
2117
  },
2118
- "description": "Request labels to attach to the Gemini call."
2118
+ "description": "Unsupported by the Gemini Developer API; requests with labels are rejected."
2119
2119
  },
2120
2120
  "temperature": {
2121
2121
  "type": "number",
package/dist/index.js CHANGED
@@ -6,12 +6,12 @@ import {
6
6
  resolveConfigPath,
7
7
  setCapabilityDefault,
8
8
  validateConfig
9
- } from "./chunk-KC6K6LCQ.js";
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-CIZXLW6C.js";
14
+ } from "./chunk-TJWQTB6W.js";
15
15
  import "./chunk-ZVTVERW5.js";
16
16
  import {
17
17
  CAPABILITIES,
package/dist/pi.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  import {
5
5
  createWebfox,
6
6
  resolveConfigPath
7
- } from "./chunk-KC6K6LCQ.js";
7
+ } from "./chunk-SGHBA2PZ.js";
8
8
  import "./chunk-WCTOYZVB.js";
9
- import "./chunk-CIZXLW6C.js";
9
+ import "./chunk-TJWQTB6W.js";
10
10
  import "./chunk-ZVTVERW5.js";
11
11
  import {
12
12
  CAPABILITIES
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webfox",
3
- "version": "4.2.0",
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",