webseek 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dyoshikawa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # webseek
2
+
3
+ Web search using the API keys of multiple providers behind a single, unified
4
+ interface. Bring whichever provider key you already have and search the web —
5
+ either from your terminal (**CLI mode**) or from an MCP client such as an editor
6
+ or agent (**MCP server mode**). Both modes share the same core search logic.
7
+
8
+ ## Providers
9
+
10
+ `webseek` normalizes two fundamentally different kinds of "web search":
11
+
12
+ | Provider | Kind | Output |
13
+ | -------- | ------------------------------------------- | -------------------------------- |
14
+ | `google` | SERP-style (Google Custom Search) | a ranked list of links |
15
+ | `openai` | LLM-grounded (Responses API `web_search`) | a synthesized answer + citations |
16
+ | `gemini` | LLM-grounded (Grounding with Google Search) | a synthesized answer + citations |
17
+
18
+ The Gemini provider supports two backends that share the same request/response
19
+ shape: the **Gemini Developer API** (`gemini-api`, default) and **Vertex AI
20
+ express mode** (`vertex-express`).
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ npm install -g webseek # install the CLI globally
26
+ npx webseek search "..." -p google # or run without installing
27
+ ```
28
+
29
+ ### Build from source
30
+
31
+ ```bash
32
+ pnpm install
33
+ pnpm build # dual-format (ESM + CJS) build via tsdown; exposes the `webseek` bin
34
+ ```
35
+
36
+ During development you can run without building via `pnpm dev -- <args>` (runs
37
+ the TypeScript source through `tsx`).
38
+
39
+ ## CLI mode
40
+
41
+ ```bash
42
+ webseek search <query...> --provider <openai|google|gemini> [options]
43
+ ```
44
+
45
+ Options for `search`:
46
+
47
+ | Flag | Description |
48
+ | ----------------------- | ------------------------------------------- |
49
+ | `-p, --provider <name>` | `openai` \| `google` \| `gemini` (required) |
50
+ | `-n, --max-results <n>` | Desired number of results (SERP providers) |
51
+ | `-m, --model <name>` | Model override (`openai`, `gemini`) |
52
+ | `--gemini-backend <b>` | `gemini-api` (default) \| `vertex-express` |
53
+ | `--json` | Emit normalized JSON instead of text |
54
+ | `--raw` | Include the provider's raw response |
55
+
56
+ Examples:
57
+
58
+ ```bash
59
+ webseek search "best static site generators 2026" -p google -n 5
60
+ webseek search "summarize the latest TypeScript release" -p openai
61
+ webseek search "who won euro 2024" -p gemini --gemini-backend vertex-express --json
62
+ ```
63
+
64
+ ## MCP server mode
65
+
66
+ Start a [Model Context Protocol](https://modelcontextprotocol.io) server over
67
+ stdio that exposes a single `web_search` tool:
68
+
69
+ ```bash
70
+ webseek mcp
71
+ ```
72
+
73
+ Register it with an MCP client, e.g.:
74
+
75
+ ```jsonc
76
+ {
77
+ "mcpServers": {
78
+ "webseek": {
79
+ "command": "webseek",
80
+ "args": ["mcp"],
81
+ "env": { "GEMINI_API_KEY": "..." },
82
+ },
83
+ },
84
+ }
85
+ ```
86
+
87
+ The `web_search` tool accepts `{ query, provider, maxResults?, model?, geminiBackend?, includeRaw? }`
88
+ and returns the normalized result as JSON.
89
+
90
+ ## Library (programmatic API)
91
+
92
+ `webseek` is published as a dual-format package (ESM + CJS), so it can be
93
+ imported as a library in addition to running as a CLI/MCP server:
94
+
95
+ ```ts
96
+ import { runSearch, WebseekError } from "webseek";
97
+
98
+ const result = await runSearch({ provider: "google", query: "best static site generators 2026" });
99
+ console.log(result.results);
100
+ ```
101
+
102
+ CommonJS consumers can `require` it the same way:
103
+
104
+ ```js
105
+ const { runSearch } = require("webseek");
106
+ ```
107
+
108
+ To embed the `web_search` tool into your own MCP server, use the
109
+ `createWebSearchTool` factory exported from the same entry point.
110
+
111
+ ## Authentication
112
+
113
+ API keys are read from **environment variables only** (never from flags), so
114
+ they don't leak into shell history or process listings.
115
+
116
+ | Provider | Environment variables |
117
+ | ------------------------- | ----------------------------------------------------------------- |
118
+ | `openai` | `OPENAI_API_KEY` |
119
+ | `google` | `GOOGLE_API_KEY`, `GOOGLE_CSE_CX` (Programmable Search Engine ID) |
120
+ | `gemini` (gemini-api) | `GEMINI_API_KEY` (falls back to `GOOGLE_API_KEY`) |
121
+ | `gemini` (vertex-express) | `VERTEX_API_KEY` |
122
+
123
+ ## Caveats
124
+
125
+ - **Google Custom Search is closed to new customers.** Existing customers must
126
+ migrate before 2027-01-01. Treat this provider as the most at-risk.
127
+ - **Google Custom Search returns at most 100 results** (10 per request); large
128
+ `--max-results` values paginate and consume more quota.
129
+ - **Gemini grounding requires displaying Google Search Suggestions** per its
130
+ terms. As a CLI we surface the queries the model ran on the `Searches:` line;
131
+ the source URIs Gemini returns are temporary redirect links.
132
+
133
+ ## Architecture
134
+
135
+ ```
136
+ src/
137
+ index.ts public library entry (runSearch, types, createWebSearchTool)
138
+ cli/ commander program: `search` and `mcp` commands
139
+ mcp/ MCP server + the web_search tool
140
+ lib/ runSearch — the shared core called by both CLI and MCP
141
+ providers/ per-provider implementations (openai, google-cse, gemini)
142
+ config/ credential + base-URL resolution from env
143
+ output/ text / JSON formatting
144
+ utils/ logger, error formatter
145
+ e2e/ end-to-end tests (spawn the CLI / drive the MCP server)
146
+ ```
147
+
148
+ ## Development
149
+
150
+ ```bash
151
+ pnpm cicheck # format check, lint, typecheck, unit tests, spelling, secrets
152
+ pnpm test:e2e # end-to-end tests (CLI subprocess + MCP stdio client)
153
+ ```
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+ const require_tools = require("../tools-Cd93c2_h.cjs");
3
+ let node_fs = require("node:fs");
4
+ let node_path = require("node:path");
5
+ let commander = require("commander");
6
+ let _modelcontextprotocol_sdk_server_mcp_js = require("@modelcontextprotocol/sdk/server/mcp.js");
7
+ let _modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
8
+ //#region src/mcp/server.ts
9
+ /**
10
+ * MCP server mode: exposes the `web_search` tool over the stdio transport so
11
+ * MCP clients (editors, agents) can search the web through this tool.
12
+ *
13
+ * Diagnostics go to stderr only — stdout is reserved for the JSON-RPC stream.
14
+ */
15
+ async function startMcpServer(params) {
16
+ const server = new _modelcontextprotocol_sdk_server_mcp_js.McpServer({
17
+ name: "webseek",
18
+ version: params.version
19
+ });
20
+ const tool = require_tools.createWebSearchTool();
21
+ server.registerTool(tool.name, tool.config, tool.handler);
22
+ const transport = new _modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport();
23
+ await server.connect(transport);
24
+ params.logger.info("webseek MCP server started (stdio)");
25
+ }
26
+ //#endregion
27
+ //#region src/utils/logger.ts
28
+ function writeErr(message) {
29
+ process.stderr.write(`${message}\n`);
30
+ }
31
+ function createConsoleLogger(params = {}) {
32
+ const silent = params.silent ?? process.env.NODE_ENV === "test";
33
+ const diagnosticsOnly = params.diagnosticsOnly ?? false;
34
+ return {
35
+ info: (message) => {
36
+ if (!silent) writeErr(message);
37
+ },
38
+ warn: (message) => {
39
+ if (!silent) writeErr(`warning: ${message}`);
40
+ },
41
+ error: (message) => {
42
+ writeErr(message);
43
+ },
44
+ result: (text) => {
45
+ if (diagnosticsOnly) {
46
+ writeErr(text);
47
+ return;
48
+ }
49
+ process.stdout.write(`${text}\n`);
50
+ }
51
+ };
52
+ }
53
+ //#endregion
54
+ //#region src/cli/wrap-command.ts
55
+ /**
56
+ * Wraps a commander action so every command shares the same error handling:
57
+ * a logger is created, the handler runs, and any thrown value is formatted to
58
+ * stderr and mapped to a process exit code.
59
+ */
60
+ /**
61
+ * Returns a commander-compatible action. Commander invokes actions with the
62
+ * command's positionals/options followed by the Command instance, all of which
63
+ * are forwarded to the handler.
64
+ */
65
+ function wrapCommand(handler) {
66
+ return async (...args) => {
67
+ const logger = createConsoleLogger();
68
+ try {
69
+ await handler({ logger }, ...args);
70
+ } catch (error) {
71
+ logger.error(require_tools.formatError(error));
72
+ process.exitCode = require_tools.errorExitCode(error);
73
+ }
74
+ };
75
+ }
76
+ //#endregion
77
+ //#region src/cli/commands/mcp.ts
78
+ async function runMcpCommand(params) {
79
+ await startMcpServer({
80
+ version: params.version,
81
+ logger: params.logger
82
+ });
83
+ }
84
+ function registerMcpCommand(params) {
85
+ params.program.command("mcp").description("Start the MCP server (stdio) exposing the web_search tool").action(wrapCommand(async ({ logger }) => {
86
+ await runMcpCommand({
87
+ logger,
88
+ version: params.version
89
+ });
90
+ }));
91
+ }
92
+ //#endregion
93
+ //#region src/output/format.ts
94
+ function formatResult(params) {
95
+ if (params.json) return formatJson(params.result);
96
+ return formatText(params.result);
97
+ }
98
+ function formatJson(result) {
99
+ const payload = {
100
+ provider: result.provider,
101
+ query: result.query,
102
+ results: result.results,
103
+ answer: result.answer,
104
+ citations: result.citations,
105
+ searchQueries: result.searchQueries
106
+ };
107
+ if (result.raw !== void 0) payload.raw = result.raw;
108
+ return JSON.stringify(payload, null, 2);
109
+ }
110
+ function formatText(result) {
111
+ const lines = [];
112
+ if (result.answer) lines.push(result.answer.trim(), "");
113
+ if (result.results.length > 0) {
114
+ lines.push("Results:");
115
+ result.results.forEach((item, index) => {
116
+ lines.push(`${index + 1}. ${item.title || item.url}`);
117
+ lines.push(` ${item.url}`);
118
+ if (item.snippet) lines.push(` ${item.snippet}`);
119
+ });
120
+ lines.push("");
121
+ }
122
+ if (result.citations.length > 0) {
123
+ lines.push("Sources:");
124
+ result.citations.forEach((citation, index) => {
125
+ const label = citation.title ? `${citation.title} — ${citation.url}` : citation.url;
126
+ lines.push(` [${index + 1}] ${label}`);
127
+ });
128
+ lines.push("");
129
+ }
130
+ if (result.searchQueries.length > 0) lines.push(`Searches: ${result.searchQueries.join(" | ")}`);
131
+ return lines.join("\n").trimEnd();
132
+ }
133
+ //#endregion
134
+ //#region src/cli/commands/search.ts
135
+ /**
136
+ * Translate raw CLI args/options into validated `runSearch` parameters. Kept
137
+ * pure (no I/O) so it can be unit-tested directly.
138
+ */
139
+ function toSearchRequest(params) {
140
+ const query = params.queryParts.join(" ").trim();
141
+ if (!query) throw new require_tools.WebseekError({
142
+ code: "invalid_usage",
143
+ message: "Missing search query."
144
+ });
145
+ return {
146
+ provider: require_tools.coerceProvider(params.options.provider),
147
+ query,
148
+ maxResults: params.options.maxResults === void 0 ? void 0 : require_tools.coerceMaxResults(params.options.maxResults),
149
+ model: params.options.model,
150
+ includeRaw: Boolean(params.options.raw),
151
+ geminiBackend: params.options.geminiBackend === void 0 ? void 0 : require_tools.coerceGeminiBackend(params.options.geminiBackend)
152
+ };
153
+ }
154
+ async function runSearchCommand(params) {
155
+ const result = await require_tools.runSearch(toSearchRequest({
156
+ queryParts: params.queryParts,
157
+ options: params.options
158
+ }));
159
+ params.logger.result(formatResult({
160
+ result,
161
+ json: Boolean(params.options.json)
162
+ }));
163
+ }
164
+ function registerSearchCommand(program) {
165
+ program.command("search").description("Search the web via a provider").argument("<query...>", "the search query").requiredOption("-p, --provider <name>", "provider: openai | google | gemini").option("-n, --max-results <number>", "desired number of results (SERP providers)").option("-m, --model <name>", "model override (openai, gemini)").option("--gemini-backend <backend>", "gemini backend: gemini-api | vertex-express").option("--json", "emit normalized JSON instead of text").option("--raw", "include the provider's raw response").action(wrapCommand(async ({ logger }, queryParts, options) => {
166
+ await runSearchCommand({
167
+ logger,
168
+ queryParts,
169
+ options
170
+ });
171
+ }));
172
+ }
173
+ //#endregion
174
+ //#region src/cli/index.ts
175
+ /**
176
+ * webseek CLI entry point.
177
+ *
178
+ * Two modes:
179
+ * webseek search <query> --provider <name> run a one-off web search
180
+ * webseek mcp start the MCP server (stdio)
181
+ */
182
+ function readPackageVersion(dir) {
183
+ try {
184
+ const pkg = JSON.parse((0, node_fs.readFileSync)((0, node_path.join)(dir, "package.json"), "utf8"));
185
+ return pkg.name === "webseek" ? pkg.version : void 0;
186
+ } catch {
187
+ return;
188
+ }
189
+ }
190
+ function getVersion() {
191
+ let dir = __dirname;
192
+ for (let depth = 0; depth < 6; depth += 1) {
193
+ const version = readPackageVersion(dir);
194
+ if (version) return version;
195
+ dir = (0, node_path.dirname)(dir);
196
+ }
197
+ return "0.0.0";
198
+ }
199
+ function buildProgram() {
200
+ const program = new commander.Command();
201
+ const version = getVersion();
202
+ program.name("webseek").description("Unified multi-provider web search (CLI + MCP server)").version(version, "-v, --version", "Show version");
203
+ registerSearchCommand(program);
204
+ registerMcpCommand({
205
+ program,
206
+ version
207
+ });
208
+ return program;
209
+ }
210
+ async function main() {
211
+ await buildProgram().parseAsync(process.argv);
212
+ }
213
+ main().catch((error) => {
214
+ process.stderr.write(`${require_tools.formatError(error)}\n`);
215
+ process.exitCode = 1;
216
+ });
217
+ //#endregion
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env node
2
+ import { a as coerceGeminiBackend, c as runSearch, d as formatError, l as WebseekError, o as coerceMaxResults, s as coerceProvider, t as createWebSearchTool, u as errorExitCode } from "../tools-CCqL3Phx.mjs";
3
+ import { readFileSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ import { Command } from "commander";
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ //#region src/mcp/server.ts
9
+ /**
10
+ * MCP server mode: exposes the `web_search` tool over the stdio transport so
11
+ * MCP clients (editors, agents) can search the web through this tool.
12
+ *
13
+ * Diagnostics go to stderr only — stdout is reserved for the JSON-RPC stream.
14
+ */
15
+ async function startMcpServer(params) {
16
+ const server = new McpServer({
17
+ name: "webseek",
18
+ version: params.version
19
+ });
20
+ const tool = createWebSearchTool();
21
+ server.registerTool(tool.name, tool.config, tool.handler);
22
+ const transport = new StdioServerTransport();
23
+ await server.connect(transport);
24
+ params.logger.info("webseek MCP server started (stdio)");
25
+ }
26
+ //#endregion
27
+ //#region src/utils/logger.ts
28
+ function writeErr(message) {
29
+ process.stderr.write(`${message}\n`);
30
+ }
31
+ function createConsoleLogger(params = {}) {
32
+ const silent = params.silent ?? process.env.NODE_ENV === "test";
33
+ const diagnosticsOnly = params.diagnosticsOnly ?? false;
34
+ return {
35
+ info: (message) => {
36
+ if (!silent) writeErr(message);
37
+ },
38
+ warn: (message) => {
39
+ if (!silent) writeErr(`warning: ${message}`);
40
+ },
41
+ error: (message) => {
42
+ writeErr(message);
43
+ },
44
+ result: (text) => {
45
+ if (diagnosticsOnly) {
46
+ writeErr(text);
47
+ return;
48
+ }
49
+ process.stdout.write(`${text}\n`);
50
+ }
51
+ };
52
+ }
53
+ //#endregion
54
+ //#region src/cli/wrap-command.ts
55
+ /**
56
+ * Wraps a commander action so every command shares the same error handling:
57
+ * a logger is created, the handler runs, and any thrown value is formatted to
58
+ * stderr and mapped to a process exit code.
59
+ */
60
+ /**
61
+ * Returns a commander-compatible action. Commander invokes actions with the
62
+ * command's positionals/options followed by the Command instance, all of which
63
+ * are forwarded to the handler.
64
+ */
65
+ function wrapCommand(handler) {
66
+ return async (...args) => {
67
+ const logger = createConsoleLogger();
68
+ try {
69
+ await handler({ logger }, ...args);
70
+ } catch (error) {
71
+ logger.error(formatError(error));
72
+ process.exitCode = errorExitCode(error);
73
+ }
74
+ };
75
+ }
76
+ //#endregion
77
+ //#region src/cli/commands/mcp.ts
78
+ async function runMcpCommand(params) {
79
+ await startMcpServer({
80
+ version: params.version,
81
+ logger: params.logger
82
+ });
83
+ }
84
+ function registerMcpCommand(params) {
85
+ params.program.command("mcp").description("Start the MCP server (stdio) exposing the web_search tool").action(wrapCommand(async ({ logger }) => {
86
+ await runMcpCommand({
87
+ logger,
88
+ version: params.version
89
+ });
90
+ }));
91
+ }
92
+ //#endregion
93
+ //#region src/output/format.ts
94
+ function formatResult(params) {
95
+ if (params.json) return formatJson(params.result);
96
+ return formatText(params.result);
97
+ }
98
+ function formatJson(result) {
99
+ const payload = {
100
+ provider: result.provider,
101
+ query: result.query,
102
+ results: result.results,
103
+ answer: result.answer,
104
+ citations: result.citations,
105
+ searchQueries: result.searchQueries
106
+ };
107
+ if (result.raw !== void 0) payload.raw = result.raw;
108
+ return JSON.stringify(payload, null, 2);
109
+ }
110
+ function formatText(result) {
111
+ const lines = [];
112
+ if (result.answer) lines.push(result.answer.trim(), "");
113
+ if (result.results.length > 0) {
114
+ lines.push("Results:");
115
+ result.results.forEach((item, index) => {
116
+ lines.push(`${index + 1}. ${item.title || item.url}`);
117
+ lines.push(` ${item.url}`);
118
+ if (item.snippet) lines.push(` ${item.snippet}`);
119
+ });
120
+ lines.push("");
121
+ }
122
+ if (result.citations.length > 0) {
123
+ lines.push("Sources:");
124
+ result.citations.forEach((citation, index) => {
125
+ const label = citation.title ? `${citation.title} — ${citation.url}` : citation.url;
126
+ lines.push(` [${index + 1}] ${label}`);
127
+ });
128
+ lines.push("");
129
+ }
130
+ if (result.searchQueries.length > 0) lines.push(`Searches: ${result.searchQueries.join(" | ")}`);
131
+ return lines.join("\n").trimEnd();
132
+ }
133
+ //#endregion
134
+ //#region src/cli/commands/search.ts
135
+ /**
136
+ * Translate raw CLI args/options into validated `runSearch` parameters. Kept
137
+ * pure (no I/O) so it can be unit-tested directly.
138
+ */
139
+ function toSearchRequest(params) {
140
+ const query = params.queryParts.join(" ").trim();
141
+ if (!query) throw new WebseekError({
142
+ code: "invalid_usage",
143
+ message: "Missing search query."
144
+ });
145
+ return {
146
+ provider: coerceProvider(params.options.provider),
147
+ query,
148
+ maxResults: params.options.maxResults === void 0 ? void 0 : coerceMaxResults(params.options.maxResults),
149
+ model: params.options.model,
150
+ includeRaw: Boolean(params.options.raw),
151
+ geminiBackend: params.options.geminiBackend === void 0 ? void 0 : coerceGeminiBackend(params.options.geminiBackend)
152
+ };
153
+ }
154
+ async function runSearchCommand(params) {
155
+ const result = await runSearch(toSearchRequest({
156
+ queryParts: params.queryParts,
157
+ options: params.options
158
+ }));
159
+ params.logger.result(formatResult({
160
+ result,
161
+ json: Boolean(params.options.json)
162
+ }));
163
+ }
164
+ function registerSearchCommand(program) {
165
+ program.command("search").description("Search the web via a provider").argument("<query...>", "the search query").requiredOption("-p, --provider <name>", "provider: openai | google | gemini").option("-n, --max-results <number>", "desired number of results (SERP providers)").option("-m, --model <name>", "model override (openai, gemini)").option("--gemini-backend <backend>", "gemini backend: gemini-api | vertex-express").option("--json", "emit normalized JSON instead of text").option("--raw", "include the provider's raw response").action(wrapCommand(async ({ logger }, queryParts, options) => {
166
+ await runSearchCommand({
167
+ logger,
168
+ queryParts,
169
+ options
170
+ });
171
+ }));
172
+ }
173
+ //#endregion
174
+ //#region src/cli/index.ts
175
+ /**
176
+ * webseek CLI entry point.
177
+ *
178
+ * Two modes:
179
+ * webseek search <query> --provider <name> run a one-off web search
180
+ * webseek mcp start the MCP server (stdio)
181
+ */
182
+ function readPackageVersion(dir) {
183
+ try {
184
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
185
+ return pkg.name === "webseek" ? pkg.version : void 0;
186
+ } catch {
187
+ return;
188
+ }
189
+ }
190
+ function getVersion() {
191
+ let dir = import.meta.dirname;
192
+ for (let depth = 0; depth < 6; depth += 1) {
193
+ const version = readPackageVersion(dir);
194
+ if (version) return version;
195
+ dir = dirname(dir);
196
+ }
197
+ return "0.0.0";
198
+ }
199
+ function buildProgram() {
200
+ const program = new Command();
201
+ const version = getVersion();
202
+ program.name("webseek").description("Unified multi-provider web search (CLI + MCP server)").version(version, "-v, --version", "Show version");
203
+ registerSearchCommand(program);
204
+ registerMcpCommand({
205
+ program,
206
+ version
207
+ });
208
+ return program;
209
+ }
210
+ async function main() {
211
+ await buildProgram().parseAsync(process.argv);
212
+ }
213
+ main().catch((error) => {
214
+ process.stderr.write(`${formatError(error)}\n`);
215
+ process.exitCode = 1;
216
+ });
217
+ //#endregion
218
+ export {};
package/dist/index.cjs ADDED
@@ -0,0 +1,10 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_tools = require("./tools-Cd93c2_h.cjs");
3
+ exports.GEMINI_BACKENDS = require_tools.GEMINI_BACKENDS;
4
+ exports.PROVIDER_NAMES = require_tools.PROVIDER_NAMES;
5
+ exports.WebseekError = require_tools.WebseekError;
6
+ exports.createWebSearchTool = require_tools.createWebSearchTool;
7
+ exports.errorExitCode = require_tools.errorExitCode;
8
+ exports.formatError = require_tools.formatError;
9
+ exports.runSearch = require_tools.runSearch;
10
+ exports.webSearchInputShape = require_tools.webSearchInputShape;