webseek 0.3.0 → 0.4.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/README.md +51 -0
- package/dist/cli/index.cjs +29 -4
- package/dist/cli/index.mjs +29 -4
- package/dist/index.cjs +2 -1
- package/dist/index.d.cts +54 -9
- package/dist/index.d.mts +54 -9
- package/dist/index.mjs +2 -2
- package/dist/{tools-Ci1EDrCT.cjs → tools-40nYFbvw.cjs} +271 -4
- package/dist/{tools-DBF9nCIs.mjs → tools-B4VbXLvm.mjs} +266 -5
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -19,6 +19,38 @@ The Gemini provider supports two backends that share the same request/response
|
|
|
19
19
|
shape: the **Gemini Developer API** (`gemini-api`, default) and **Vertex AI
|
|
20
20
|
express mode** (`vertex-express`).
|
|
21
21
|
|
|
22
|
+
### Usage and cost
|
|
23
|
+
|
|
24
|
+
Every search reports what it consumed and an estimated cost in USD. The text
|
|
25
|
+
output ends with a line such as:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
Usage: 1,100 tokens (1,000 in, 100 out), 1 search, ~$0.0180
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
JSON output (and the MCP tool result) carries the same data as `usage` and
|
|
32
|
+
`cost`, plus the `model` that served the search:
|
|
33
|
+
|
|
34
|
+
```jsonc
|
|
35
|
+
{
|
|
36
|
+
"model": "gpt-5.5",
|
|
37
|
+
"usage": { "inputTokens": 1000, "outputTokens": 100, "totalTokens": 1100, "searchCalls": 1 },
|
|
38
|
+
"cost": { "totalUsd": 0.018, "tokensUsd": 0.008, "searchUsd": 0.01 },
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- `usage.inputTokens` includes cached tokens (`usage.cachedInputTokens`) and the
|
|
43
|
+
search content the model read; `usage.outputTokens` includes reasoning/thinking
|
|
44
|
+
tokens. SERP providers (`google`) report no tokens.
|
|
45
|
+
- `usage.searchCalls` counts OpenAI `web_search` calls, Gemini grounding queries,
|
|
46
|
+
or Google Custom Search requests (one per page of 10 results).
|
|
47
|
+
- `cost` is computed from built-in list prices: token rates for the model plus
|
|
48
|
+
the provider's search fee (OpenAI $10 per 1k calls, Gemini 3.x $14 per 1k
|
|
49
|
+
queries, Gemini 2.5 $35 per 1k grounded prompts, Google Custom Search $5 per 1k
|
|
50
|
+
queries). It is a list-price estimate, not your bill: free allowances,
|
|
51
|
+
discounts and long-context rates are **not** applied. `cost` is omitted when the model has no known price
|
|
52
|
+
or the provider reported no token counts.
|
|
53
|
+
|
|
22
54
|
## Install
|
|
23
55
|
|
|
24
56
|
```bash
|
|
@@ -105,6 +137,24 @@ CommonJS consumers can `require` it the same way:
|
|
|
105
137
|
const { runSearch } = require("webseek");
|
|
106
138
|
```
|
|
107
139
|
|
|
140
|
+
Each result carries `usage` and, when it can be priced, an estimated `cost` (see
|
|
141
|
+
[Usage and cost](#usage-and-cost)). To price usage yourself — for example to sum
|
|
142
|
+
several searches — call `estimateCost` with the provider, the model and a
|
|
143
|
+
`usage` object; it returns `undefined` when the model has no known price:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { estimateCost } from "webseek";
|
|
147
|
+
|
|
148
|
+
const cost = estimateCost({
|
|
149
|
+
provider: "openai",
|
|
150
|
+
model: "gpt-5.5",
|
|
151
|
+
usage: { inputTokens: 1000, outputTokens: 100, searchCalls: 1 },
|
|
152
|
+
});
|
|
153
|
+
console.log(cost?.totalUsd.toFixed(3)); // "0.018"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Prices are list prices built into the package and change with its releases.
|
|
157
|
+
|
|
108
158
|
To embed the `web_search` tool into your own MCP server, use the
|
|
109
159
|
`createWebSearchTool` factory exported from the same entry point.
|
|
110
160
|
|
|
@@ -139,6 +189,7 @@ src/
|
|
|
139
189
|
mcp/ MCP server + the web_search tool
|
|
140
190
|
lib/ runSearch — the shared core called by both CLI and MCP
|
|
141
191
|
providers/ per-provider implementations (openai, google-cse, gemini)
|
|
192
|
+
pricing/ list-price table and cost estimation
|
|
142
193
|
config/ credential + base-URL resolution from env
|
|
143
194
|
output/ text / JSON formatting
|
|
144
195
|
utils/ logger, error formatter
|
package/dist/cli/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_tools = require("../tools-
|
|
2
|
+
const require_tools = require("../tools-40nYFbvw.cjs");
|
|
3
3
|
let node_fs = require("node:fs");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
let commander = require("commander");
|
|
@@ -102,7 +102,10 @@ function formatJson(result) {
|
|
|
102
102
|
results: result.results,
|
|
103
103
|
answer: result.answer,
|
|
104
104
|
citations: result.citations,
|
|
105
|
-
searchQueries: result.searchQueries
|
|
105
|
+
searchQueries: result.searchQueries,
|
|
106
|
+
model: result.model,
|
|
107
|
+
usage: result.usage,
|
|
108
|
+
cost: result.cost
|
|
106
109
|
};
|
|
107
110
|
if (result.raw !== void 0) payload.raw = result.raw;
|
|
108
111
|
return JSON.stringify(payload, null, 2);
|
|
@@ -128,8 +131,29 @@ function formatText(result) {
|
|
|
128
131
|
lines.push("");
|
|
129
132
|
}
|
|
130
133
|
if (result.searchQueries.length > 0) lines.push(`Searches: ${result.searchQueries.join(" | ")}`);
|
|
134
|
+
if (result.usage) lines.push(formatUsage({
|
|
135
|
+
usage: result.usage,
|
|
136
|
+
cost: result.cost
|
|
137
|
+
}));
|
|
131
138
|
return lines.join("\n").trimEnd();
|
|
132
139
|
}
|
|
140
|
+
function formatUsage(params) {
|
|
141
|
+
const { usage, cost } = params;
|
|
142
|
+
const parts = [];
|
|
143
|
+
if (usage.inputTokens !== void 0 || usage.outputTokens !== void 0) {
|
|
144
|
+
const input = usage.inputTokens ?? 0;
|
|
145
|
+
const output = usage.outputTokens ?? 0;
|
|
146
|
+
const total = usage.totalTokens ?? input + output;
|
|
147
|
+
parts.push(`${total.toLocaleString("en-US")} tokens (${input.toLocaleString("en-US")} in, ${output.toLocaleString("en-US")} out)`);
|
|
148
|
+
}
|
|
149
|
+
parts.push(`${usage.searchCalls} search${usage.searchCalls === 1 ? "" : "es"}`);
|
|
150
|
+
parts.push(cost === void 0 ? "cost unknown" : `~${formatUsd(cost.totalUsd)}`);
|
|
151
|
+
return `Usage: ${parts.join(", ")}`;
|
|
152
|
+
}
|
|
153
|
+
function formatUsd(value) {
|
|
154
|
+
if (value > 0 && value < 5e-5) return "<$0.0001";
|
|
155
|
+
return `$${value.toFixed(4)}`;
|
|
156
|
+
}
|
|
133
157
|
//#endregion
|
|
134
158
|
//#region src/cli/commands/search.ts
|
|
135
159
|
/**
|
|
@@ -152,10 +176,11 @@ function toSearchRequest(params) {
|
|
|
152
176
|
};
|
|
153
177
|
}
|
|
154
178
|
async function runSearchCommand(params) {
|
|
155
|
-
const
|
|
179
|
+
const request = toSearchRequest({
|
|
156
180
|
queryParts: params.queryParts,
|
|
157
181
|
options: params.options
|
|
158
|
-
})
|
|
182
|
+
});
|
|
183
|
+
const result = await require_tools.runSearch(request);
|
|
159
184
|
params.logger.result(formatResult({
|
|
160
185
|
result,
|
|
161
186
|
json: Boolean(params.options.json)
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as coerceGeminiBackend, c as runSearch, d as
|
|
2
|
+
import { a as coerceGeminiBackend, c as runSearch, d as errorExitCode, f as formatError, o as coerceMaxResults, s as coerceProvider, t as createWebSearchTool, u as WebseekError } from "../tools-B4VbXLvm.mjs";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { Command } from "commander";
|
|
@@ -102,7 +102,10 @@ function formatJson(result) {
|
|
|
102
102
|
results: result.results,
|
|
103
103
|
answer: result.answer,
|
|
104
104
|
citations: result.citations,
|
|
105
|
-
searchQueries: result.searchQueries
|
|
105
|
+
searchQueries: result.searchQueries,
|
|
106
|
+
model: result.model,
|
|
107
|
+
usage: result.usage,
|
|
108
|
+
cost: result.cost
|
|
106
109
|
};
|
|
107
110
|
if (result.raw !== void 0) payload.raw = result.raw;
|
|
108
111
|
return JSON.stringify(payload, null, 2);
|
|
@@ -128,8 +131,29 @@ function formatText(result) {
|
|
|
128
131
|
lines.push("");
|
|
129
132
|
}
|
|
130
133
|
if (result.searchQueries.length > 0) lines.push(`Searches: ${result.searchQueries.join(" | ")}`);
|
|
134
|
+
if (result.usage) lines.push(formatUsage({
|
|
135
|
+
usage: result.usage,
|
|
136
|
+
cost: result.cost
|
|
137
|
+
}));
|
|
131
138
|
return lines.join("\n").trimEnd();
|
|
132
139
|
}
|
|
140
|
+
function formatUsage(params) {
|
|
141
|
+
const { usage, cost } = params;
|
|
142
|
+
const parts = [];
|
|
143
|
+
if (usage.inputTokens !== void 0 || usage.outputTokens !== void 0) {
|
|
144
|
+
const input = usage.inputTokens ?? 0;
|
|
145
|
+
const output = usage.outputTokens ?? 0;
|
|
146
|
+
const total = usage.totalTokens ?? input + output;
|
|
147
|
+
parts.push(`${total.toLocaleString("en-US")} tokens (${input.toLocaleString("en-US")} in, ${output.toLocaleString("en-US")} out)`);
|
|
148
|
+
}
|
|
149
|
+
parts.push(`${usage.searchCalls} search${usage.searchCalls === 1 ? "" : "es"}`);
|
|
150
|
+
parts.push(cost === void 0 ? "cost unknown" : `~${formatUsd(cost.totalUsd)}`);
|
|
151
|
+
return `Usage: ${parts.join(", ")}`;
|
|
152
|
+
}
|
|
153
|
+
function formatUsd(value) {
|
|
154
|
+
if (value > 0 && value < 5e-5) return "<$0.0001";
|
|
155
|
+
return `$${value.toFixed(4)}`;
|
|
156
|
+
}
|
|
133
157
|
//#endregion
|
|
134
158
|
//#region src/cli/commands/search.ts
|
|
135
159
|
/**
|
|
@@ -152,10 +176,11 @@ function toSearchRequest(params) {
|
|
|
152
176
|
};
|
|
153
177
|
}
|
|
154
178
|
async function runSearchCommand(params) {
|
|
155
|
-
const
|
|
179
|
+
const request = toSearchRequest({
|
|
156
180
|
queryParts: params.queryParts,
|
|
157
181
|
options: params.options
|
|
158
|
-
})
|
|
182
|
+
});
|
|
183
|
+
const result = await runSearch(request);
|
|
159
184
|
params.logger.result(formatResult({
|
|
160
185
|
result,
|
|
161
186
|
json: Boolean(params.options.json)
|
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_tools = require("./tools-
|
|
2
|
+
const require_tools = require("./tools-40nYFbvw.cjs");
|
|
3
3
|
exports.GEMINI_BACKENDS = require_tools.GEMINI_BACKENDS;
|
|
4
4
|
exports.PROVIDER_NAMES = require_tools.PROVIDER_NAMES;
|
|
5
5
|
exports.WebseekError = require_tools.WebseekError;
|
|
6
6
|
exports.createWebSearchTool = require_tools.createWebSearchTool;
|
|
7
7
|
exports.errorExitCode = require_tools.errorExitCode;
|
|
8
|
+
exports.estimateCost = require_tools.estimateCost;
|
|
8
9
|
exports.formatError = require_tools.formatError;
|
|
9
10
|
exports.runSearch = require_tools.runSearch;
|
|
10
11
|
exports.webSearchInputShape = require_tools.webSearchInputShape;
|
package/dist/index.d.cts
CHANGED
|
@@ -41,6 +41,24 @@ interface Citation {
|
|
|
41
41
|
startIndex?: number;
|
|
42
42
|
endIndex?: number;
|
|
43
43
|
}
|
|
44
|
+
/** What a search consumed. Token counts are absent for SERP providers. */
|
|
45
|
+
interface SearchUsage {
|
|
46
|
+
/** Prompt tokens, including cached ones and fetched search content. */
|
|
47
|
+
inputTokens?: number;
|
|
48
|
+
/** The part of `inputTokens` served from the prompt cache. */
|
|
49
|
+
cachedInputTokens?: number;
|
|
50
|
+
/** Generated tokens, including reasoning/thinking tokens. */
|
|
51
|
+
outputTokens?: number;
|
|
52
|
+
totalTokens?: number;
|
|
53
|
+
/** Searches the provider ran (web search tool calls, grounding queries, or SERP requests). */
|
|
54
|
+
searchCalls: number;
|
|
55
|
+
}
|
|
56
|
+
/** Estimated list-price cost of a search in USD (free tiers not applied). */
|
|
57
|
+
interface SearchCost {
|
|
58
|
+
totalUsd: number;
|
|
59
|
+
tokensUsd: number;
|
|
60
|
+
searchUsd: number;
|
|
61
|
+
}
|
|
44
62
|
/** The normalized result shape returned by every provider. */
|
|
45
63
|
interface NormalizedSearchResult {
|
|
46
64
|
provider: ProviderName;
|
|
@@ -53,6 +71,12 @@ interface NormalizedSearchResult {
|
|
|
53
71
|
citations: Citation[];
|
|
54
72
|
/** Queries the provider actually ran (grounded providers). */
|
|
55
73
|
searchQueries: string[];
|
|
74
|
+
/** The model that served the search (grounded providers). */
|
|
75
|
+
model?: string;
|
|
76
|
+
/** Tokens and searches consumed (always set by the built-in providers). */
|
|
77
|
+
usage?: SearchUsage;
|
|
78
|
+
/** Estimated cost; absent when the model's price is unknown. */
|
|
79
|
+
cost?: SearchCost;
|
|
56
80
|
/** The provider's raw response, included only when requested. */
|
|
57
81
|
raw?: unknown;
|
|
58
82
|
}
|
|
@@ -75,8 +99,8 @@ interface SearchProvider {
|
|
|
75
99
|
}
|
|
76
100
|
//#endregion
|
|
77
101
|
//#region src/lib/search.d.ts
|
|
78
|
-
declare const PROVIDER_NAMES: readonly ["openai", "google", "gemini"];
|
|
79
|
-
declare const GEMINI_BACKENDS: readonly ["gemini-api", "vertex-express"];
|
|
102
|
+
export declare const PROVIDER_NAMES: readonly ["openai", "google", "gemini"];
|
|
103
|
+
export declare const GEMINI_BACKENDS: readonly ["gemini-api", "vertex-express"];
|
|
80
104
|
interface RunSearchParams {
|
|
81
105
|
provider: ProviderName;
|
|
82
106
|
query: string;
|
|
@@ -87,7 +111,28 @@ interface RunSearchParams {
|
|
|
87
111
|
env?: Env;
|
|
88
112
|
fetchImpl?: typeof fetch;
|
|
89
113
|
}
|
|
90
|
-
declare function runSearch(params: RunSearchParams): Promise<NormalizedSearchResult>;
|
|
114
|
+
export declare function runSearch(params: RunSearchParams): Promise<NormalizedSearchResult>;
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/pricing/pricing.d.ts
|
|
117
|
+
interface EstimateCostParams {
|
|
118
|
+
provider: ProviderName;
|
|
119
|
+
/** The model that served the search (ignored for SERP providers). */
|
|
120
|
+
model?: string;
|
|
121
|
+
/**
|
|
122
|
+
* Priced instead when `model` is not in the table — typically the requested
|
|
123
|
+
* model, since the served id may be a snapshot or variant the table lacks.
|
|
124
|
+
*/
|
|
125
|
+
fallbackModel?: string;
|
|
126
|
+
usage: SearchUsage;
|
|
127
|
+
/** Price as of this moment (rates can change on a set date); defaults to now. */
|
|
128
|
+
now?: Date;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Estimate what a search cost at list price, or `undefined` when it cannot be
|
|
132
|
+
* priced: the model is not in this table (e.g. newer than it), or an LLM-backed
|
|
133
|
+
* provider reported no token counts (a search-fee-only figure would understate).
|
|
134
|
+
*/
|
|
135
|
+
export declare function estimateCost(params: EstimateCostParams): SearchCost | undefined;
|
|
91
136
|
//#endregion
|
|
92
137
|
//#region src/utils/error.d.ts
|
|
93
138
|
/**
|
|
@@ -104,7 +149,7 @@ interface WebseekErrorOptions {
|
|
|
104
149
|
message: string;
|
|
105
150
|
cause?: unknown;
|
|
106
151
|
}
|
|
107
|
-
declare class WebseekError extends Error {
|
|
152
|
+
export declare class WebseekError extends Error {
|
|
108
153
|
readonly code: WebseekErrorCode;
|
|
109
154
|
constructor(options: WebseekErrorOptions);
|
|
110
155
|
}
|
|
@@ -112,12 +157,12 @@ declare class WebseekError extends Error {
|
|
|
112
157
|
* Map a thrown value to a process exit code: `2` for usage mistakes, `1` for
|
|
113
158
|
* any other failure.
|
|
114
159
|
*/
|
|
115
|
-
declare function errorExitCode(error: unknown): number;
|
|
160
|
+
export declare function errorExitCode(error: unknown): number;
|
|
116
161
|
/** Render any thrown value into a single-line, user-facing message. */
|
|
117
|
-
declare function formatError(error: unknown): string;
|
|
162
|
+
export declare function formatError(error: unknown): string;
|
|
118
163
|
//#endregion
|
|
119
164
|
//#region src/mcp/tools.d.ts
|
|
120
|
-
declare const webSearchInputShape: {
|
|
165
|
+
export declare const webSearchInputShape: {
|
|
121
166
|
query: z.ZodString;
|
|
122
167
|
provider: z.ZodEnum<{
|
|
123
168
|
gemini: "gemini";
|
|
@@ -171,6 +216,6 @@ interface WebSearchTool {
|
|
|
171
216
|
};
|
|
172
217
|
handler: (args: WebSearchArgs) => Promise<ToolResult>;
|
|
173
218
|
}
|
|
174
|
-
declare function createWebSearchTool(params?: CreateWebSearchToolParams): WebSearchTool;
|
|
219
|
+
export declare function createWebSearchTool(params?: CreateWebSearchToolParams): WebSearchTool;
|
|
175
220
|
//#endregion
|
|
176
|
-
export {
|
|
221
|
+
export type { Citation, CreateWebSearchToolParams, Env, EstimateCostParams, GeminiBackend, NormalizedSearchResult, ProviderName, RunSearchParams, SearchCost, SearchParams, SearchProvider, SearchResultItem, SearchUsage, ToolResult, WebSearchArgs, WebSearchTool, WebseekErrorCode, WebseekErrorOptions };
|
package/dist/index.d.mts
CHANGED
|
@@ -41,6 +41,24 @@ interface Citation {
|
|
|
41
41
|
startIndex?: number;
|
|
42
42
|
endIndex?: number;
|
|
43
43
|
}
|
|
44
|
+
/** What a search consumed. Token counts are absent for SERP providers. */
|
|
45
|
+
interface SearchUsage {
|
|
46
|
+
/** Prompt tokens, including cached ones and fetched search content. */
|
|
47
|
+
inputTokens?: number;
|
|
48
|
+
/** The part of `inputTokens` served from the prompt cache. */
|
|
49
|
+
cachedInputTokens?: number;
|
|
50
|
+
/** Generated tokens, including reasoning/thinking tokens. */
|
|
51
|
+
outputTokens?: number;
|
|
52
|
+
totalTokens?: number;
|
|
53
|
+
/** Searches the provider ran (web search tool calls, grounding queries, or SERP requests). */
|
|
54
|
+
searchCalls: number;
|
|
55
|
+
}
|
|
56
|
+
/** Estimated list-price cost of a search in USD (free tiers not applied). */
|
|
57
|
+
interface SearchCost {
|
|
58
|
+
totalUsd: number;
|
|
59
|
+
tokensUsd: number;
|
|
60
|
+
searchUsd: number;
|
|
61
|
+
}
|
|
44
62
|
/** The normalized result shape returned by every provider. */
|
|
45
63
|
interface NormalizedSearchResult {
|
|
46
64
|
provider: ProviderName;
|
|
@@ -53,6 +71,12 @@ interface NormalizedSearchResult {
|
|
|
53
71
|
citations: Citation[];
|
|
54
72
|
/** Queries the provider actually ran (grounded providers). */
|
|
55
73
|
searchQueries: string[];
|
|
74
|
+
/** The model that served the search (grounded providers). */
|
|
75
|
+
model?: string;
|
|
76
|
+
/** Tokens and searches consumed (always set by the built-in providers). */
|
|
77
|
+
usage?: SearchUsage;
|
|
78
|
+
/** Estimated cost; absent when the model's price is unknown. */
|
|
79
|
+
cost?: SearchCost;
|
|
56
80
|
/** The provider's raw response, included only when requested. */
|
|
57
81
|
raw?: unknown;
|
|
58
82
|
}
|
|
@@ -75,8 +99,8 @@ interface SearchProvider {
|
|
|
75
99
|
}
|
|
76
100
|
//#endregion
|
|
77
101
|
//#region src/lib/search.d.ts
|
|
78
|
-
declare const PROVIDER_NAMES: readonly ["openai", "google", "gemini"];
|
|
79
|
-
declare const GEMINI_BACKENDS: readonly ["gemini-api", "vertex-express"];
|
|
102
|
+
export declare const PROVIDER_NAMES: readonly ["openai", "google", "gemini"];
|
|
103
|
+
export declare const GEMINI_BACKENDS: readonly ["gemini-api", "vertex-express"];
|
|
80
104
|
interface RunSearchParams {
|
|
81
105
|
provider: ProviderName;
|
|
82
106
|
query: string;
|
|
@@ -87,7 +111,28 @@ interface RunSearchParams {
|
|
|
87
111
|
env?: Env;
|
|
88
112
|
fetchImpl?: typeof fetch;
|
|
89
113
|
}
|
|
90
|
-
declare function runSearch(params: RunSearchParams): Promise<NormalizedSearchResult>;
|
|
114
|
+
export declare function runSearch(params: RunSearchParams): Promise<NormalizedSearchResult>;
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/pricing/pricing.d.ts
|
|
117
|
+
interface EstimateCostParams {
|
|
118
|
+
provider: ProviderName;
|
|
119
|
+
/** The model that served the search (ignored for SERP providers). */
|
|
120
|
+
model?: string;
|
|
121
|
+
/**
|
|
122
|
+
* Priced instead when `model` is not in the table — typically the requested
|
|
123
|
+
* model, since the served id may be a snapshot or variant the table lacks.
|
|
124
|
+
*/
|
|
125
|
+
fallbackModel?: string;
|
|
126
|
+
usage: SearchUsage;
|
|
127
|
+
/** Price as of this moment (rates can change on a set date); defaults to now. */
|
|
128
|
+
now?: Date;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Estimate what a search cost at list price, or `undefined` when it cannot be
|
|
132
|
+
* priced: the model is not in this table (e.g. newer than it), or an LLM-backed
|
|
133
|
+
* provider reported no token counts (a search-fee-only figure would understate).
|
|
134
|
+
*/
|
|
135
|
+
export declare function estimateCost(params: EstimateCostParams): SearchCost | undefined;
|
|
91
136
|
//#endregion
|
|
92
137
|
//#region src/utils/error.d.ts
|
|
93
138
|
/**
|
|
@@ -104,7 +149,7 @@ interface WebseekErrorOptions {
|
|
|
104
149
|
message: string;
|
|
105
150
|
cause?: unknown;
|
|
106
151
|
}
|
|
107
|
-
declare class WebseekError extends Error {
|
|
152
|
+
export declare class WebseekError extends Error {
|
|
108
153
|
readonly code: WebseekErrorCode;
|
|
109
154
|
constructor(options: WebseekErrorOptions);
|
|
110
155
|
}
|
|
@@ -112,12 +157,12 @@ declare class WebseekError extends Error {
|
|
|
112
157
|
* Map a thrown value to a process exit code: `2` for usage mistakes, `1` for
|
|
113
158
|
* any other failure.
|
|
114
159
|
*/
|
|
115
|
-
declare function errorExitCode(error: unknown): number;
|
|
160
|
+
export declare function errorExitCode(error: unknown): number;
|
|
116
161
|
/** Render any thrown value into a single-line, user-facing message. */
|
|
117
|
-
declare function formatError(error: unknown): string;
|
|
162
|
+
export declare function formatError(error: unknown): string;
|
|
118
163
|
//#endregion
|
|
119
164
|
//#region src/mcp/tools.d.ts
|
|
120
|
-
declare const webSearchInputShape: {
|
|
165
|
+
export declare const webSearchInputShape: {
|
|
121
166
|
query: z.ZodString;
|
|
122
167
|
provider: z.ZodEnum<{
|
|
123
168
|
gemini: "gemini";
|
|
@@ -171,6 +216,6 @@ interface WebSearchTool {
|
|
|
171
216
|
};
|
|
172
217
|
handler: (args: WebSearchArgs) => Promise<ToolResult>;
|
|
173
218
|
}
|
|
174
|
-
declare function createWebSearchTool(params?: CreateWebSearchToolParams): WebSearchTool;
|
|
219
|
+
export declare function createWebSearchTool(params?: CreateWebSearchToolParams): WebSearchTool;
|
|
175
220
|
//#endregion
|
|
176
|
-
export {
|
|
221
|
+
export type { Citation, CreateWebSearchToolParams, Env, EstimateCostParams, GeminiBackend, NormalizedSearchResult, ProviderName, RunSearchParams, SearchCost, SearchParams, SearchProvider, SearchResultItem, SearchUsage, ToolResult, WebSearchArgs, WebSearchTool, WebseekErrorCode, WebseekErrorOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { c as runSearch, d as formatError, i as PROVIDER_NAMES, l as
|
|
2
|
-
export { GEMINI_BACKENDS, PROVIDER_NAMES, WebseekError, createWebSearchTool, errorExitCode, formatError, runSearch, webSearchInputShape };
|
|
1
|
+
import { c as runSearch, d as errorExitCode, f as formatError, i as PROVIDER_NAMES, l as estimateCost, n as webSearchInputShape, r as GEMINI_BACKENDS, t as createWebSearchTool, u as WebseekError } from "./tools-B4VbXLvm.mjs";
|
|
2
|
+
export { GEMINI_BACKENDS, PROVIDER_NAMES, WebseekError, createWebSearchTool, errorExitCode, estimateCost, formatError, runSearch, webSearchInputShape };
|
|
@@ -99,6 +99,183 @@ function resolveGeminiConfig(params) {
|
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
101
|
//#endregion
|
|
102
|
+
//#region src/pricing/pricing.ts
|
|
103
|
+
/** OpenAI's `web_search` tool: $10 per 1k calls; search content tokens bill at model rates. */
|
|
104
|
+
const OPENAI_SEARCH_PER_1K = 10;
|
|
105
|
+
const OPENAI_PRICES = {
|
|
106
|
+
"gpt-5.5": openai({
|
|
107
|
+
input: 5,
|
|
108
|
+
cachedInput: .5,
|
|
109
|
+
output: 30
|
|
110
|
+
}),
|
|
111
|
+
"gpt-5.4": openai({
|
|
112
|
+
input: 2.5,
|
|
113
|
+
cachedInput: .25,
|
|
114
|
+
output: 15
|
|
115
|
+
}),
|
|
116
|
+
"gpt-5": openai({
|
|
117
|
+
input: 1.25,
|
|
118
|
+
cachedInput: .125,
|
|
119
|
+
output: 10
|
|
120
|
+
}),
|
|
121
|
+
"gpt-5-mini": openai({
|
|
122
|
+
input: .25,
|
|
123
|
+
cachedInput: .025,
|
|
124
|
+
output: 2
|
|
125
|
+
}),
|
|
126
|
+
"gpt-5-nano": openai({
|
|
127
|
+
input: .05,
|
|
128
|
+
cachedInput: .005,
|
|
129
|
+
output: .4
|
|
130
|
+
}),
|
|
131
|
+
"gpt-4.1": openai({
|
|
132
|
+
input: 2,
|
|
133
|
+
cachedInput: .5,
|
|
134
|
+
output: 8
|
|
135
|
+
}),
|
|
136
|
+
"gpt-4o": openai({
|
|
137
|
+
input: 2.5,
|
|
138
|
+
cachedInput: 1.25,
|
|
139
|
+
output: 10
|
|
140
|
+
}),
|
|
141
|
+
"gpt-4o-mini": openai({
|
|
142
|
+
input: .15,
|
|
143
|
+
cachedInput: .075,
|
|
144
|
+
output: .6
|
|
145
|
+
})
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Gemini 3.x: "$14 per 1,000 requests", read as one charge per search query the
|
|
149
|
+
* model ran (each `webSearchQueries` entry), not per prompt.
|
|
150
|
+
*/
|
|
151
|
+
const GEMINI_3_SEARCH_PER_1K = 14;
|
|
152
|
+
/** Gemini 2.5 bills each grounded prompt, however many queries it ran ($35 per 1k). */
|
|
153
|
+
const GEMINI_2_5_SEARCH_PER_1K = 35;
|
|
154
|
+
const GEMINI_3_FLASH = {
|
|
155
|
+
rates: {
|
|
156
|
+
input: .75,
|
|
157
|
+
cachedInput: .075,
|
|
158
|
+
output: 3.75
|
|
159
|
+
},
|
|
160
|
+
next: {
|
|
161
|
+
from: "2027-01-01",
|
|
162
|
+
rates: {
|
|
163
|
+
input: 1.5,
|
|
164
|
+
cachedInput: .15,
|
|
165
|
+
output: 7.5
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
searchPer1k: GEMINI_3_SEARCH_PER_1K
|
|
169
|
+
};
|
|
170
|
+
const GEMINI_PRICES = {
|
|
171
|
+
"gemini-3.8-flash": GEMINI_3_FLASH,
|
|
172
|
+
"gemini-3.7-flash": GEMINI_3_FLASH,
|
|
173
|
+
"gemini-3.6-flash": GEMINI_3_FLASH,
|
|
174
|
+
"gemini-3-flash-preview": GEMINI_3_FLASH,
|
|
175
|
+
"gemini-3.5-flash": gemini3({
|
|
176
|
+
input: 1.5,
|
|
177
|
+
cachedInput: .15,
|
|
178
|
+
output: 9
|
|
179
|
+
}),
|
|
180
|
+
"gemini-3.5-flash-lite": gemini3({
|
|
181
|
+
input: .3,
|
|
182
|
+
cachedInput: .03,
|
|
183
|
+
output: 2.5
|
|
184
|
+
}),
|
|
185
|
+
"gemini-3.1-flash-lite": gemini3({
|
|
186
|
+
input: .25,
|
|
187
|
+
cachedInput: .025,
|
|
188
|
+
output: 1.5
|
|
189
|
+
}),
|
|
190
|
+
"gemini-3.1-pro-preview": gemini3({
|
|
191
|
+
input: 2,
|
|
192
|
+
cachedInput: .2,
|
|
193
|
+
output: 12
|
|
194
|
+
}),
|
|
195
|
+
"gemini-2.5-pro": gemini25({
|
|
196
|
+
input: 1.25,
|
|
197
|
+
cachedInput: .125,
|
|
198
|
+
output: 10
|
|
199
|
+
}),
|
|
200
|
+
"gemini-2.5-flash": gemini25({
|
|
201
|
+
input: .3,
|
|
202
|
+
cachedInput: .03,
|
|
203
|
+
output: 2.5
|
|
204
|
+
}),
|
|
205
|
+
"gemini-2.5-flash-lite": gemini25({
|
|
206
|
+
input: .1,
|
|
207
|
+
cachedInput: .01,
|
|
208
|
+
output: .4
|
|
209
|
+
})
|
|
210
|
+
};
|
|
211
|
+
/** Google Custom Search: $5 per 1k queries (each paginated request is a query). */
|
|
212
|
+
const GOOGLE_CSE_PER_1K = 5;
|
|
213
|
+
function openai(rates) {
|
|
214
|
+
return {
|
|
215
|
+
rates,
|
|
216
|
+
searchPer1k: OPENAI_SEARCH_PER_1K
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function gemini3(rates) {
|
|
220
|
+
return {
|
|
221
|
+
rates,
|
|
222
|
+
searchPer1k: GEMINI_3_SEARCH_PER_1K
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function gemini25(rates) {
|
|
226
|
+
return {
|
|
227
|
+
rates,
|
|
228
|
+
searchPer1k: GEMINI_2_5_SEARCH_PER_1K,
|
|
229
|
+
searchPerPrompt: true
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Estimate what a search cost at list price, or `undefined` when it cannot be
|
|
234
|
+
* priced: the model is not in this table (e.g. newer than it), or an LLM-backed
|
|
235
|
+
* provider reported no token counts (a search-fee-only figure would understate).
|
|
236
|
+
*/
|
|
237
|
+
function estimateCost(params) {
|
|
238
|
+
const { usage } = params;
|
|
239
|
+
if (params.provider === "google") {
|
|
240
|
+
const searchUsd = usage.searchCalls * GOOGLE_CSE_PER_1K / 1e3;
|
|
241
|
+
return {
|
|
242
|
+
totalUsd: searchUsd,
|
|
243
|
+
tokensUsd: 0,
|
|
244
|
+
searchUsd
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const table = params.provider === "openai" ? OPENAI_PRICES : GEMINI_PRICES;
|
|
248
|
+
const price = [params.model, params.fallbackModel].filter((model) => model !== void 0).map((model) => lookupPrice({
|
|
249
|
+
table,
|
|
250
|
+
model
|
|
251
|
+
})).find((found) => found !== void 0);
|
|
252
|
+
if (price === void 0) return;
|
|
253
|
+
if (usage.inputTokens === void 0 && usage.outputTokens === void 0) return;
|
|
254
|
+
const rates = ratesAt({
|
|
255
|
+
price,
|
|
256
|
+
now: params.now ?? /* @__PURE__ */ new Date()
|
|
257
|
+
});
|
|
258
|
+
const inputTokens = usage.inputTokens ?? 0;
|
|
259
|
+
const cachedInputTokens = Math.min(usage.cachedInputTokens ?? 0, inputTokens);
|
|
260
|
+
const tokensUsd = ((inputTokens - cachedInputTokens) * rates.input + cachedInputTokens * rates.cachedInput + (usage.outputTokens ?? 0) * rates.output) / 1e6;
|
|
261
|
+
const searchUsd = (price.searchPerPrompt ? Math.min(usage.searchCalls, 1) : usage.searchCalls) * price.searchPer1k / 1e3;
|
|
262
|
+
return {
|
|
263
|
+
totalUsd: tokensUsd + searchUsd,
|
|
264
|
+
tokensUsd,
|
|
265
|
+
searchUsd
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function lookupPrice(params) {
|
|
269
|
+
const { table } = params;
|
|
270
|
+
const id = params.model.replace(/^(?:.*\/)?models\//, "").replace(/-\d{4}-\d{2}-\d{2}$/, "").toLowerCase();
|
|
271
|
+
return Object.hasOwn(table, id) ? table[id] : void 0;
|
|
272
|
+
}
|
|
273
|
+
function ratesAt(params) {
|
|
274
|
+
const { price } = params;
|
|
275
|
+
if (price.next && params.now.toISOString().slice(0, 10) >= price.next.from) return price.next.rates;
|
|
276
|
+
return price.rates;
|
|
277
|
+
}
|
|
278
|
+
//#endregion
|
|
102
279
|
//#region src/providers/gemini.ts
|
|
103
280
|
/**
|
|
104
281
|
* Gemini web search provider via "Grounding with Google Search".
|
|
@@ -151,8 +328,18 @@ const candidateSchema = zod.z.looseObject({
|
|
|
151
328
|
content: zod.z.looseObject({ parts: zod.z.array(zod.z.looseObject({ text: zod.z.string().optional() })).optional() }).optional(),
|
|
152
329
|
groundingMetadata: groundingMetadataSchema.optional()
|
|
153
330
|
});
|
|
331
|
+
const usageMetadataSchema = zod.z.looseObject({
|
|
332
|
+
promptTokenCount: zod.z.number().optional(),
|
|
333
|
+
cachedContentTokenCount: zod.z.number().optional(),
|
|
334
|
+
candidatesTokenCount: zod.z.number().optional(),
|
|
335
|
+
thoughtsTokenCount: zod.z.number().optional(),
|
|
336
|
+
toolUsePromptTokenCount: zod.z.number().optional(),
|
|
337
|
+
totalTokenCount: zod.z.number().optional()
|
|
338
|
+
});
|
|
154
339
|
const responseSchema$2 = zod.z.looseObject({
|
|
155
340
|
candidates: zod.z.array(candidateSchema).optional(),
|
|
341
|
+
usageMetadata: usageMetadataSchema.optional(),
|
|
342
|
+
modelVersion: zod.z.string().optional(),
|
|
156
343
|
error: zod.z.looseObject({ message: zod.z.string().optional() }).optional()
|
|
157
344
|
});
|
|
158
345
|
function createGeminiProvider(params) {
|
|
@@ -183,6 +370,11 @@ function createGeminiProvider(params) {
|
|
|
183
370
|
body
|
|
184
371
|
});
|
|
185
372
|
const { answer, citations, searchQueries } = extract$1(parsed.data);
|
|
373
|
+
const servedModel = parsed.data.modelVersion ?? model;
|
|
374
|
+
const usage = toUsage$1({
|
|
375
|
+
usage: parsed.data.usageMetadata,
|
|
376
|
+
searchCalls: searchQueries.length
|
|
377
|
+
});
|
|
186
378
|
return {
|
|
187
379
|
provider: "gemini",
|
|
188
380
|
query: searchParams.query,
|
|
@@ -190,6 +382,14 @@ function createGeminiProvider(params) {
|
|
|
190
382
|
answer,
|
|
191
383
|
citations,
|
|
192
384
|
searchQueries,
|
|
385
|
+
model: servedModel,
|
|
386
|
+
usage,
|
|
387
|
+
cost: estimateCost({
|
|
388
|
+
provider: "gemini",
|
|
389
|
+
model: servedModel,
|
|
390
|
+
fallbackModel: model,
|
|
391
|
+
usage
|
|
392
|
+
}),
|
|
193
393
|
raw: searchParams.includeRaw ? body : void 0
|
|
194
394
|
};
|
|
195
395
|
}
|
|
@@ -208,6 +408,23 @@ function extract$1(data) {
|
|
|
208
408
|
searchQueries: metadata?.webSearchQueries ?? []
|
|
209
409
|
};
|
|
210
410
|
}
|
|
411
|
+
function toUsage$1(params) {
|
|
412
|
+
const { usage } = params;
|
|
413
|
+
if (usage === void 0) return { searchCalls: params.searchCalls };
|
|
414
|
+
const inputTokens = sumDefined([usage.promptTokenCount, usage.toolUsePromptTokenCount]);
|
|
415
|
+
const outputTokens = sumDefined([usage.candidatesTokenCount, usage.thoughtsTokenCount]);
|
|
416
|
+
return {
|
|
417
|
+
inputTokens,
|
|
418
|
+
cachedInputTokens: usage.cachedContentTokenCount,
|
|
419
|
+
outputTokens,
|
|
420
|
+
totalTokens: usage.totalTokenCount,
|
|
421
|
+
searchCalls: params.searchCalls
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function sumDefined(values) {
|
|
425
|
+
const defined = values.filter((value) => value !== void 0);
|
|
426
|
+
return defined.length > 0 ? defined.reduce((total, value) => total + value, 0) : void 0;
|
|
427
|
+
}
|
|
211
428
|
function toError$2(params) {
|
|
212
429
|
const parsed = responseSchema$2.safeParse(params.body);
|
|
213
430
|
const message = (parsed.success ? parsed.data.error?.message : void 0) ?? `Gemini grounding request failed (HTTP ${params.status}).`;
|
|
@@ -262,6 +479,7 @@ function createGoogleCseProvider(params) {
|
|
|
262
479
|
const desired = clampDesired(searchParams.maxResults ?? MAX_PER_REQUEST);
|
|
263
480
|
const items = [];
|
|
264
481
|
let lastRaw;
|
|
482
|
+
let requests = 0;
|
|
265
483
|
for (let start = 1; start <= MAX_TOTAL_RESULTS && items.length < desired; start += MAX_PER_REQUEST) {
|
|
266
484
|
const num = Math.min(MAX_PER_REQUEST, desired - items.length);
|
|
267
485
|
const response = await fetchImpl(buildUrl({
|
|
@@ -270,6 +488,7 @@ function createGoogleCseProvider(params) {
|
|
|
270
488
|
start,
|
|
271
489
|
num
|
|
272
490
|
}));
|
|
491
|
+
requests += 1;
|
|
273
492
|
const body = await response.json().catch(() => void 0);
|
|
274
493
|
lastRaw = body;
|
|
275
494
|
const parsed = responseSchema$1.safeParse(body);
|
|
@@ -281,6 +500,7 @@ function createGoogleCseProvider(params) {
|
|
|
281
500
|
items.push(...page);
|
|
282
501
|
if (page.length < num) break;
|
|
283
502
|
}
|
|
503
|
+
const usage = { searchCalls: requests };
|
|
284
504
|
return {
|
|
285
505
|
provider: "google",
|
|
286
506
|
query: searchParams.query,
|
|
@@ -292,6 +512,11 @@ function createGoogleCseProvider(params) {
|
|
|
292
512
|
})),
|
|
293
513
|
citations: [],
|
|
294
514
|
searchQueries: [searchParams.query],
|
|
515
|
+
usage,
|
|
516
|
+
cost: estimateCost({
|
|
517
|
+
provider: "google",
|
|
518
|
+
usage
|
|
519
|
+
}),
|
|
295
520
|
raw: searchParams.includeRaw ? lastRaw : void 0
|
|
296
521
|
};
|
|
297
522
|
}
|
|
@@ -358,9 +583,17 @@ const outputItemSchema = zod.z.looseObject({
|
|
|
358
583
|
content: zod.z.array(contentSchema).optional(),
|
|
359
584
|
action: zod.z.looseObject({ query: zod.z.string().optional() }).optional()
|
|
360
585
|
});
|
|
586
|
+
const usageSchema = zod.z.looseObject({
|
|
587
|
+
input_tokens: zod.z.number().optional(),
|
|
588
|
+
input_tokens_details: zod.z.looseObject({ cached_tokens: zod.z.number().optional() }).nullish(),
|
|
589
|
+
output_tokens: zod.z.number().optional(),
|
|
590
|
+
total_tokens: zod.z.number().optional()
|
|
591
|
+
});
|
|
361
592
|
const responseSchema = zod.z.looseObject({
|
|
362
593
|
output: zod.z.array(outputItemSchema).optional(),
|
|
363
594
|
output_text: zod.z.string().optional(),
|
|
595
|
+
model: zod.z.string().optional(),
|
|
596
|
+
usage: usageSchema.nullish(),
|
|
364
597
|
error: zod.z.looseObject({ message: zod.z.string().optional() }).nullish()
|
|
365
598
|
});
|
|
366
599
|
function createOpenAIProvider(params) {
|
|
@@ -388,7 +621,12 @@ function createOpenAIProvider(params) {
|
|
|
388
621
|
status: response.status,
|
|
389
622
|
body
|
|
390
623
|
});
|
|
391
|
-
const { answer, citations, searchQueries } = extract(parsed.data);
|
|
624
|
+
const { answer, citations, searchQueries, searchCalls } = extract(parsed.data);
|
|
625
|
+
const servedModel = parsed.data.model ?? model;
|
|
626
|
+
const usage = toUsage({
|
|
627
|
+
usage: parsed.data.usage,
|
|
628
|
+
searchCalls
|
|
629
|
+
});
|
|
392
630
|
return {
|
|
393
631
|
provider: "openai",
|
|
394
632
|
query: searchParams.query,
|
|
@@ -396,6 +634,14 @@ function createOpenAIProvider(params) {
|
|
|
396
634
|
answer,
|
|
397
635
|
citations,
|
|
398
636
|
searchQueries,
|
|
637
|
+
model: servedModel,
|
|
638
|
+
usage,
|
|
639
|
+
cost: estimateCost({
|
|
640
|
+
provider: "openai",
|
|
641
|
+
model: servedModel,
|
|
642
|
+
fallbackModel: model,
|
|
643
|
+
usage
|
|
644
|
+
}),
|
|
399
645
|
raw: searchParams.includeRaw ? body : void 0
|
|
400
646
|
};
|
|
401
647
|
}
|
|
@@ -405,8 +651,12 @@ function extract(data) {
|
|
|
405
651
|
const textParts = [];
|
|
406
652
|
const citations = [];
|
|
407
653
|
const searchQueries = [];
|
|
654
|
+
let searchCalls = 0;
|
|
408
655
|
for (const item of data.output ?? []) {
|
|
409
|
-
if (item.type === "web_search_call"
|
|
656
|
+
if (item.type === "web_search_call") {
|
|
657
|
+
searchCalls += 1;
|
|
658
|
+
if (item.action?.query) searchQueries.push(item.action.query);
|
|
659
|
+
}
|
|
410
660
|
for (const content of item.content ?? []) {
|
|
411
661
|
if (content.type === "output_text" && content.text) textParts.push(content.text);
|
|
412
662
|
for (const annotation of content.annotations ?? []) if (annotation.type === "url_citation" && annotation.url) citations.push({
|
|
@@ -420,7 +670,18 @@ function extract(data) {
|
|
|
420
670
|
return {
|
|
421
671
|
answer: textParts.length > 0 ? textParts.join("") : data.output_text ?? "",
|
|
422
672
|
citations,
|
|
423
|
-
searchQueries
|
|
673
|
+
searchQueries,
|
|
674
|
+
searchCalls
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
function toUsage(params) {
|
|
678
|
+
const { usage } = params;
|
|
679
|
+
return {
|
|
680
|
+
inputTokens: usage?.input_tokens,
|
|
681
|
+
cachedInputTokens: usage?.input_tokens_details?.cached_tokens,
|
|
682
|
+
outputTokens: usage?.output_tokens,
|
|
683
|
+
totalTokens: usage?.total_tokens,
|
|
684
|
+
searchCalls: params.searchCalls
|
|
424
685
|
};
|
|
425
686
|
}
|
|
426
687
|
function toError(params) {
|
|
@@ -515,7 +776,7 @@ function createWebSearchTool(params = {}) {
|
|
|
515
776
|
name: "web_search",
|
|
516
777
|
config: {
|
|
517
778
|
title: "Web Search",
|
|
518
|
-
description: "Search the web using a provider's API key (OpenAI, Google Custom Search, or Gemini). Returns a normalized JSON result with SERP results and/or a grounded answer with citations.",
|
|
779
|
+
description: "Search the web using a provider's API key (OpenAI, Google Custom Search, or Gemini). Returns a normalized JSON result with SERP results and/or a grounded answer with citations, plus the tokens and searches used and an estimated USD cost.",
|
|
519
780
|
inputSchema: webSearchInputShape
|
|
520
781
|
},
|
|
521
782
|
handler: async (args) => {
|
|
@@ -595,6 +856,12 @@ Object.defineProperty(exports, "errorExitCode", {
|
|
|
595
856
|
return errorExitCode;
|
|
596
857
|
}
|
|
597
858
|
});
|
|
859
|
+
Object.defineProperty(exports, "estimateCost", {
|
|
860
|
+
enumerable: true,
|
|
861
|
+
get: function() {
|
|
862
|
+
return estimateCost;
|
|
863
|
+
}
|
|
864
|
+
});
|
|
598
865
|
Object.defineProperty(exports, "formatError", {
|
|
599
866
|
enumerable: true,
|
|
600
867
|
get: function() {
|
|
@@ -99,6 +99,183 @@ function resolveGeminiConfig(params) {
|
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
101
|
//#endregion
|
|
102
|
+
//#region src/pricing/pricing.ts
|
|
103
|
+
/** OpenAI's `web_search` tool: $10 per 1k calls; search content tokens bill at model rates. */
|
|
104
|
+
const OPENAI_SEARCH_PER_1K = 10;
|
|
105
|
+
const OPENAI_PRICES = {
|
|
106
|
+
"gpt-5.5": openai({
|
|
107
|
+
input: 5,
|
|
108
|
+
cachedInput: .5,
|
|
109
|
+
output: 30
|
|
110
|
+
}),
|
|
111
|
+
"gpt-5.4": openai({
|
|
112
|
+
input: 2.5,
|
|
113
|
+
cachedInput: .25,
|
|
114
|
+
output: 15
|
|
115
|
+
}),
|
|
116
|
+
"gpt-5": openai({
|
|
117
|
+
input: 1.25,
|
|
118
|
+
cachedInput: .125,
|
|
119
|
+
output: 10
|
|
120
|
+
}),
|
|
121
|
+
"gpt-5-mini": openai({
|
|
122
|
+
input: .25,
|
|
123
|
+
cachedInput: .025,
|
|
124
|
+
output: 2
|
|
125
|
+
}),
|
|
126
|
+
"gpt-5-nano": openai({
|
|
127
|
+
input: .05,
|
|
128
|
+
cachedInput: .005,
|
|
129
|
+
output: .4
|
|
130
|
+
}),
|
|
131
|
+
"gpt-4.1": openai({
|
|
132
|
+
input: 2,
|
|
133
|
+
cachedInput: .5,
|
|
134
|
+
output: 8
|
|
135
|
+
}),
|
|
136
|
+
"gpt-4o": openai({
|
|
137
|
+
input: 2.5,
|
|
138
|
+
cachedInput: 1.25,
|
|
139
|
+
output: 10
|
|
140
|
+
}),
|
|
141
|
+
"gpt-4o-mini": openai({
|
|
142
|
+
input: .15,
|
|
143
|
+
cachedInput: .075,
|
|
144
|
+
output: .6
|
|
145
|
+
})
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Gemini 3.x: "$14 per 1,000 requests", read as one charge per search query the
|
|
149
|
+
* model ran (each `webSearchQueries` entry), not per prompt.
|
|
150
|
+
*/
|
|
151
|
+
const GEMINI_3_SEARCH_PER_1K = 14;
|
|
152
|
+
/** Gemini 2.5 bills each grounded prompt, however many queries it ran ($35 per 1k). */
|
|
153
|
+
const GEMINI_2_5_SEARCH_PER_1K = 35;
|
|
154
|
+
const GEMINI_3_FLASH = {
|
|
155
|
+
rates: {
|
|
156
|
+
input: .75,
|
|
157
|
+
cachedInput: .075,
|
|
158
|
+
output: 3.75
|
|
159
|
+
},
|
|
160
|
+
next: {
|
|
161
|
+
from: "2027-01-01",
|
|
162
|
+
rates: {
|
|
163
|
+
input: 1.5,
|
|
164
|
+
cachedInput: .15,
|
|
165
|
+
output: 7.5
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
searchPer1k: GEMINI_3_SEARCH_PER_1K
|
|
169
|
+
};
|
|
170
|
+
const GEMINI_PRICES = {
|
|
171
|
+
"gemini-3.8-flash": GEMINI_3_FLASH,
|
|
172
|
+
"gemini-3.7-flash": GEMINI_3_FLASH,
|
|
173
|
+
"gemini-3.6-flash": GEMINI_3_FLASH,
|
|
174
|
+
"gemini-3-flash-preview": GEMINI_3_FLASH,
|
|
175
|
+
"gemini-3.5-flash": gemini3({
|
|
176
|
+
input: 1.5,
|
|
177
|
+
cachedInput: .15,
|
|
178
|
+
output: 9
|
|
179
|
+
}),
|
|
180
|
+
"gemini-3.5-flash-lite": gemini3({
|
|
181
|
+
input: .3,
|
|
182
|
+
cachedInput: .03,
|
|
183
|
+
output: 2.5
|
|
184
|
+
}),
|
|
185
|
+
"gemini-3.1-flash-lite": gemini3({
|
|
186
|
+
input: .25,
|
|
187
|
+
cachedInput: .025,
|
|
188
|
+
output: 1.5
|
|
189
|
+
}),
|
|
190
|
+
"gemini-3.1-pro-preview": gemini3({
|
|
191
|
+
input: 2,
|
|
192
|
+
cachedInput: .2,
|
|
193
|
+
output: 12
|
|
194
|
+
}),
|
|
195
|
+
"gemini-2.5-pro": gemini25({
|
|
196
|
+
input: 1.25,
|
|
197
|
+
cachedInput: .125,
|
|
198
|
+
output: 10
|
|
199
|
+
}),
|
|
200
|
+
"gemini-2.5-flash": gemini25({
|
|
201
|
+
input: .3,
|
|
202
|
+
cachedInput: .03,
|
|
203
|
+
output: 2.5
|
|
204
|
+
}),
|
|
205
|
+
"gemini-2.5-flash-lite": gemini25({
|
|
206
|
+
input: .1,
|
|
207
|
+
cachedInput: .01,
|
|
208
|
+
output: .4
|
|
209
|
+
})
|
|
210
|
+
};
|
|
211
|
+
/** Google Custom Search: $5 per 1k queries (each paginated request is a query). */
|
|
212
|
+
const GOOGLE_CSE_PER_1K = 5;
|
|
213
|
+
function openai(rates) {
|
|
214
|
+
return {
|
|
215
|
+
rates,
|
|
216
|
+
searchPer1k: OPENAI_SEARCH_PER_1K
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function gemini3(rates) {
|
|
220
|
+
return {
|
|
221
|
+
rates,
|
|
222
|
+
searchPer1k: GEMINI_3_SEARCH_PER_1K
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function gemini25(rates) {
|
|
226
|
+
return {
|
|
227
|
+
rates,
|
|
228
|
+
searchPer1k: GEMINI_2_5_SEARCH_PER_1K,
|
|
229
|
+
searchPerPrompt: true
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Estimate what a search cost at list price, or `undefined` when it cannot be
|
|
234
|
+
* priced: the model is not in this table (e.g. newer than it), or an LLM-backed
|
|
235
|
+
* provider reported no token counts (a search-fee-only figure would understate).
|
|
236
|
+
*/
|
|
237
|
+
function estimateCost(params) {
|
|
238
|
+
const { usage } = params;
|
|
239
|
+
if (params.provider === "google") {
|
|
240
|
+
const searchUsd = usage.searchCalls * GOOGLE_CSE_PER_1K / 1e3;
|
|
241
|
+
return {
|
|
242
|
+
totalUsd: searchUsd,
|
|
243
|
+
tokensUsd: 0,
|
|
244
|
+
searchUsd
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const table = params.provider === "openai" ? OPENAI_PRICES : GEMINI_PRICES;
|
|
248
|
+
const price = [params.model, params.fallbackModel].filter((model) => model !== void 0).map((model) => lookupPrice({
|
|
249
|
+
table,
|
|
250
|
+
model
|
|
251
|
+
})).find((found) => found !== void 0);
|
|
252
|
+
if (price === void 0) return;
|
|
253
|
+
if (usage.inputTokens === void 0 && usage.outputTokens === void 0) return;
|
|
254
|
+
const rates = ratesAt({
|
|
255
|
+
price,
|
|
256
|
+
now: params.now ?? /* @__PURE__ */ new Date()
|
|
257
|
+
});
|
|
258
|
+
const inputTokens = usage.inputTokens ?? 0;
|
|
259
|
+
const cachedInputTokens = Math.min(usage.cachedInputTokens ?? 0, inputTokens);
|
|
260
|
+
const tokensUsd = ((inputTokens - cachedInputTokens) * rates.input + cachedInputTokens * rates.cachedInput + (usage.outputTokens ?? 0) * rates.output) / 1e6;
|
|
261
|
+
const searchUsd = (price.searchPerPrompt ? Math.min(usage.searchCalls, 1) : usage.searchCalls) * price.searchPer1k / 1e3;
|
|
262
|
+
return {
|
|
263
|
+
totalUsd: tokensUsd + searchUsd,
|
|
264
|
+
tokensUsd,
|
|
265
|
+
searchUsd
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function lookupPrice(params) {
|
|
269
|
+
const { table } = params;
|
|
270
|
+
const id = params.model.replace(/^(?:.*\/)?models\//, "").replace(/-\d{4}-\d{2}-\d{2}$/, "").toLowerCase();
|
|
271
|
+
return Object.hasOwn(table, id) ? table[id] : void 0;
|
|
272
|
+
}
|
|
273
|
+
function ratesAt(params) {
|
|
274
|
+
const { price } = params;
|
|
275
|
+
if (price.next && params.now.toISOString().slice(0, 10) >= price.next.from) return price.next.rates;
|
|
276
|
+
return price.rates;
|
|
277
|
+
}
|
|
278
|
+
//#endregion
|
|
102
279
|
//#region src/providers/gemini.ts
|
|
103
280
|
/**
|
|
104
281
|
* Gemini web search provider via "Grounding with Google Search".
|
|
@@ -151,8 +328,18 @@ const candidateSchema = z.looseObject({
|
|
|
151
328
|
content: z.looseObject({ parts: z.array(z.looseObject({ text: z.string().optional() })).optional() }).optional(),
|
|
152
329
|
groundingMetadata: groundingMetadataSchema.optional()
|
|
153
330
|
});
|
|
331
|
+
const usageMetadataSchema = z.looseObject({
|
|
332
|
+
promptTokenCount: z.number().optional(),
|
|
333
|
+
cachedContentTokenCount: z.number().optional(),
|
|
334
|
+
candidatesTokenCount: z.number().optional(),
|
|
335
|
+
thoughtsTokenCount: z.number().optional(),
|
|
336
|
+
toolUsePromptTokenCount: z.number().optional(),
|
|
337
|
+
totalTokenCount: z.number().optional()
|
|
338
|
+
});
|
|
154
339
|
const responseSchema$2 = z.looseObject({
|
|
155
340
|
candidates: z.array(candidateSchema).optional(),
|
|
341
|
+
usageMetadata: usageMetadataSchema.optional(),
|
|
342
|
+
modelVersion: z.string().optional(),
|
|
156
343
|
error: z.looseObject({ message: z.string().optional() }).optional()
|
|
157
344
|
});
|
|
158
345
|
function createGeminiProvider(params) {
|
|
@@ -183,6 +370,11 @@ function createGeminiProvider(params) {
|
|
|
183
370
|
body
|
|
184
371
|
});
|
|
185
372
|
const { answer, citations, searchQueries } = extract$1(parsed.data);
|
|
373
|
+
const servedModel = parsed.data.modelVersion ?? model;
|
|
374
|
+
const usage = toUsage$1({
|
|
375
|
+
usage: parsed.data.usageMetadata,
|
|
376
|
+
searchCalls: searchQueries.length
|
|
377
|
+
});
|
|
186
378
|
return {
|
|
187
379
|
provider: "gemini",
|
|
188
380
|
query: searchParams.query,
|
|
@@ -190,6 +382,14 @@ function createGeminiProvider(params) {
|
|
|
190
382
|
answer,
|
|
191
383
|
citations,
|
|
192
384
|
searchQueries,
|
|
385
|
+
model: servedModel,
|
|
386
|
+
usage,
|
|
387
|
+
cost: estimateCost({
|
|
388
|
+
provider: "gemini",
|
|
389
|
+
model: servedModel,
|
|
390
|
+
fallbackModel: model,
|
|
391
|
+
usage
|
|
392
|
+
}),
|
|
193
393
|
raw: searchParams.includeRaw ? body : void 0
|
|
194
394
|
};
|
|
195
395
|
}
|
|
@@ -208,6 +408,23 @@ function extract$1(data) {
|
|
|
208
408
|
searchQueries: metadata?.webSearchQueries ?? []
|
|
209
409
|
};
|
|
210
410
|
}
|
|
411
|
+
function toUsage$1(params) {
|
|
412
|
+
const { usage } = params;
|
|
413
|
+
if (usage === void 0) return { searchCalls: params.searchCalls };
|
|
414
|
+
const inputTokens = sumDefined([usage.promptTokenCount, usage.toolUsePromptTokenCount]);
|
|
415
|
+
const outputTokens = sumDefined([usage.candidatesTokenCount, usage.thoughtsTokenCount]);
|
|
416
|
+
return {
|
|
417
|
+
inputTokens,
|
|
418
|
+
cachedInputTokens: usage.cachedContentTokenCount,
|
|
419
|
+
outputTokens,
|
|
420
|
+
totalTokens: usage.totalTokenCount,
|
|
421
|
+
searchCalls: params.searchCalls
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function sumDefined(values) {
|
|
425
|
+
const defined = values.filter((value) => value !== void 0);
|
|
426
|
+
return defined.length > 0 ? defined.reduce((total, value) => total + value, 0) : void 0;
|
|
427
|
+
}
|
|
211
428
|
function toError$2(params) {
|
|
212
429
|
const parsed = responseSchema$2.safeParse(params.body);
|
|
213
430
|
const message = (parsed.success ? parsed.data.error?.message : void 0) ?? `Gemini grounding request failed (HTTP ${params.status}).`;
|
|
@@ -262,6 +479,7 @@ function createGoogleCseProvider(params) {
|
|
|
262
479
|
const desired = clampDesired(searchParams.maxResults ?? MAX_PER_REQUEST);
|
|
263
480
|
const items = [];
|
|
264
481
|
let lastRaw;
|
|
482
|
+
let requests = 0;
|
|
265
483
|
for (let start = 1; start <= MAX_TOTAL_RESULTS && items.length < desired; start += MAX_PER_REQUEST) {
|
|
266
484
|
const num = Math.min(MAX_PER_REQUEST, desired - items.length);
|
|
267
485
|
const response = await fetchImpl(buildUrl({
|
|
@@ -270,6 +488,7 @@ function createGoogleCseProvider(params) {
|
|
|
270
488
|
start,
|
|
271
489
|
num
|
|
272
490
|
}));
|
|
491
|
+
requests += 1;
|
|
273
492
|
const body = await response.json().catch(() => void 0);
|
|
274
493
|
lastRaw = body;
|
|
275
494
|
const parsed = responseSchema$1.safeParse(body);
|
|
@@ -281,6 +500,7 @@ function createGoogleCseProvider(params) {
|
|
|
281
500
|
items.push(...page);
|
|
282
501
|
if (page.length < num) break;
|
|
283
502
|
}
|
|
503
|
+
const usage = { searchCalls: requests };
|
|
284
504
|
return {
|
|
285
505
|
provider: "google",
|
|
286
506
|
query: searchParams.query,
|
|
@@ -292,6 +512,11 @@ function createGoogleCseProvider(params) {
|
|
|
292
512
|
})),
|
|
293
513
|
citations: [],
|
|
294
514
|
searchQueries: [searchParams.query],
|
|
515
|
+
usage,
|
|
516
|
+
cost: estimateCost({
|
|
517
|
+
provider: "google",
|
|
518
|
+
usage
|
|
519
|
+
}),
|
|
295
520
|
raw: searchParams.includeRaw ? lastRaw : void 0
|
|
296
521
|
};
|
|
297
522
|
}
|
|
@@ -358,9 +583,17 @@ const outputItemSchema = z.looseObject({
|
|
|
358
583
|
content: z.array(contentSchema).optional(),
|
|
359
584
|
action: z.looseObject({ query: z.string().optional() }).optional()
|
|
360
585
|
});
|
|
586
|
+
const usageSchema = z.looseObject({
|
|
587
|
+
input_tokens: z.number().optional(),
|
|
588
|
+
input_tokens_details: z.looseObject({ cached_tokens: z.number().optional() }).nullish(),
|
|
589
|
+
output_tokens: z.number().optional(),
|
|
590
|
+
total_tokens: z.number().optional()
|
|
591
|
+
});
|
|
361
592
|
const responseSchema = z.looseObject({
|
|
362
593
|
output: z.array(outputItemSchema).optional(),
|
|
363
594
|
output_text: z.string().optional(),
|
|
595
|
+
model: z.string().optional(),
|
|
596
|
+
usage: usageSchema.nullish(),
|
|
364
597
|
error: z.looseObject({ message: z.string().optional() }).nullish()
|
|
365
598
|
});
|
|
366
599
|
function createOpenAIProvider(params) {
|
|
@@ -388,7 +621,12 @@ function createOpenAIProvider(params) {
|
|
|
388
621
|
status: response.status,
|
|
389
622
|
body
|
|
390
623
|
});
|
|
391
|
-
const { answer, citations, searchQueries } = extract(parsed.data);
|
|
624
|
+
const { answer, citations, searchQueries, searchCalls } = extract(parsed.data);
|
|
625
|
+
const servedModel = parsed.data.model ?? model;
|
|
626
|
+
const usage = toUsage({
|
|
627
|
+
usage: parsed.data.usage,
|
|
628
|
+
searchCalls
|
|
629
|
+
});
|
|
392
630
|
return {
|
|
393
631
|
provider: "openai",
|
|
394
632
|
query: searchParams.query,
|
|
@@ -396,6 +634,14 @@ function createOpenAIProvider(params) {
|
|
|
396
634
|
answer,
|
|
397
635
|
citations,
|
|
398
636
|
searchQueries,
|
|
637
|
+
model: servedModel,
|
|
638
|
+
usage,
|
|
639
|
+
cost: estimateCost({
|
|
640
|
+
provider: "openai",
|
|
641
|
+
model: servedModel,
|
|
642
|
+
fallbackModel: model,
|
|
643
|
+
usage
|
|
644
|
+
}),
|
|
399
645
|
raw: searchParams.includeRaw ? body : void 0
|
|
400
646
|
};
|
|
401
647
|
}
|
|
@@ -405,8 +651,12 @@ function extract(data) {
|
|
|
405
651
|
const textParts = [];
|
|
406
652
|
const citations = [];
|
|
407
653
|
const searchQueries = [];
|
|
654
|
+
let searchCalls = 0;
|
|
408
655
|
for (const item of data.output ?? []) {
|
|
409
|
-
if (item.type === "web_search_call"
|
|
656
|
+
if (item.type === "web_search_call") {
|
|
657
|
+
searchCalls += 1;
|
|
658
|
+
if (item.action?.query) searchQueries.push(item.action.query);
|
|
659
|
+
}
|
|
410
660
|
for (const content of item.content ?? []) {
|
|
411
661
|
if (content.type === "output_text" && content.text) textParts.push(content.text);
|
|
412
662
|
for (const annotation of content.annotations ?? []) if (annotation.type === "url_citation" && annotation.url) citations.push({
|
|
@@ -420,7 +670,18 @@ function extract(data) {
|
|
|
420
670
|
return {
|
|
421
671
|
answer: textParts.length > 0 ? textParts.join("") : data.output_text ?? "",
|
|
422
672
|
citations,
|
|
423
|
-
searchQueries
|
|
673
|
+
searchQueries,
|
|
674
|
+
searchCalls
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
function toUsage(params) {
|
|
678
|
+
const { usage } = params;
|
|
679
|
+
return {
|
|
680
|
+
inputTokens: usage?.input_tokens,
|
|
681
|
+
cachedInputTokens: usage?.input_tokens_details?.cached_tokens,
|
|
682
|
+
outputTokens: usage?.output_tokens,
|
|
683
|
+
totalTokens: usage?.total_tokens,
|
|
684
|
+
searchCalls: params.searchCalls
|
|
424
685
|
};
|
|
425
686
|
}
|
|
426
687
|
function toError(params) {
|
|
@@ -515,7 +776,7 @@ function createWebSearchTool(params = {}) {
|
|
|
515
776
|
name: "web_search",
|
|
516
777
|
config: {
|
|
517
778
|
title: "Web Search",
|
|
518
|
-
description: "Search the web using a provider's API key (OpenAI, Google Custom Search, or Gemini). Returns a normalized JSON result with SERP results and/or a grounded answer with citations.",
|
|
779
|
+
description: "Search the web using a provider's API key (OpenAI, Google Custom Search, or Gemini). Returns a normalized JSON result with SERP results and/or a grounded answer with citations, plus the tokens and searches used and an estimated USD cost.",
|
|
519
780
|
inputSchema: webSearchInputShape
|
|
520
781
|
},
|
|
521
782
|
handler: async (args) => {
|
|
@@ -547,4 +808,4 @@ function createWebSearchTool(params = {}) {
|
|
|
547
808
|
};
|
|
548
809
|
}
|
|
549
810
|
//#endregion
|
|
550
|
-
export { coerceGeminiBackend as a, runSearch as c,
|
|
811
|
+
export { coerceGeminiBackend as a, runSearch as c, errorExitCode as d, formatError as f, PROVIDER_NAMES as i, estimateCost as l, webSearchInputShape as n, coerceMaxResults as o, GEMINI_BACKENDS as r, coerceProvider as s, createWebSearchTool as t, WebseekError as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webseek",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Unified multi-provider web search as a CLI, MCP server, and library (OpenAI, Google Custom Search, Gemini)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web-search",
|
|
@@ -54,24 +54,24 @@
|
|
|
54
54
|
"access": "public"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@openrouter/sdk": "1.
|
|
58
|
-
"@secretlint/secretlint-rule-preset-recommend": "13.0.
|
|
57
|
+
"@openrouter/sdk": "1.3.21",
|
|
58
|
+
"@secretlint/secretlint-rule-preset-recommend": "13.0.5",
|
|
59
59
|
"@tsconfig/node24": "^24.0.4",
|
|
60
60
|
"@types/node": "^26.0.0",
|
|
61
|
-
"cspell": "10.
|
|
62
|
-
"lint-staged": "17.1
|
|
63
|
-
"oxfmt": "0.
|
|
64
|
-
"oxlint": "1.
|
|
65
|
-
"repomix": "1.
|
|
66
|
-
"resend": "6.
|
|
67
|
-
"rulesync": "^
|
|
68
|
-
"secretlint": "13.0.
|
|
69
|
-
"simple-git-hooks": "2.
|
|
70
|
-
"smol-toml": "1.
|
|
71
|
-
"tsdown": "^0.
|
|
61
|
+
"cspell": "10.3.3",
|
|
62
|
+
"lint-staged": "17.5.1",
|
|
63
|
+
"oxfmt": "0.70.0",
|
|
64
|
+
"oxlint": "1.85.0",
|
|
65
|
+
"repomix": "1.18.1",
|
|
66
|
+
"resend": "6.28.1",
|
|
67
|
+
"rulesync": "^17.0.0",
|
|
68
|
+
"secretlint": "13.0.5",
|
|
69
|
+
"simple-git-hooks": "2.14.0",
|
|
70
|
+
"smol-toml": "1.9.0",
|
|
71
|
+
"tsdown": "^0.23.0",
|
|
72
72
|
"tsx": "^4.22.4",
|
|
73
73
|
"typescript": "^7.0.2",
|
|
74
|
-
"vitest": "^
|
|
74
|
+
"vitest": "^5.0.0"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
77
|
"@modelcontextprotocol/sdk": "^1.29.0",
|