opencode-websearch 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/constants.d.ts +11 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/helpers.d.ts +4 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +123 -204
- package/dist/providers/anthropic.d.ts +10 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +7 -3
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AnthropicConfig } from "./types.js";
|
|
2
|
+
interface ProviderData {
|
|
3
|
+
models: Record<string, {
|
|
4
|
+
api: {
|
|
5
|
+
npm: string;
|
|
6
|
+
};
|
|
7
|
+
id: string;
|
|
8
|
+
options: Record<string, unknown>;
|
|
9
|
+
}>;
|
|
10
|
+
key?: string;
|
|
11
|
+
options: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Scan providers returned by the SDK for the first Anthropic model
|
|
15
|
+
* with `websearch: true` set in its options.
|
|
16
|
+
*/
|
|
17
|
+
declare const resolveFromProviders: (providers: ProviderData[]) => AnthropicConfig | null;
|
|
18
|
+
declare const formatConfigError: () => string;
|
|
19
|
+
export { formatConfigError, resolveFromProviders };
|
|
20
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAI7C,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC/F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAqBD;;;GAGG;AACH,QAAA,MAAM,oBAAoB,GAAI,WAAW,YAAY,EAAE,KAAG,eAAe,GAAG,IAgB3E,CAAC;AAIF,QAAA,MAAM,iBAAiB,QAAO,MA6B0B,CAAC;AAEzD,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const ANTHROPIC_NPM_PACKAGE = "@ai-sdk/anthropic";
|
|
2
|
+
declare const DEFAULT_SEARCH_USES = 5;
|
|
3
|
+
declare const EMPTY_LENGTH = 0;
|
|
4
|
+
declare const MAX_RESPONSE_TOKENS = 16000;
|
|
5
|
+
declare const MAX_SEARCH_USES = 10;
|
|
6
|
+
declare const MIN_QUERY_LENGTH = 2;
|
|
7
|
+
declare const MIN_SEARCH_USES = 1;
|
|
8
|
+
declare const MONTH_OFFSET = 1;
|
|
9
|
+
declare const PAD_LENGTH = 2;
|
|
10
|
+
export { ANTHROPIC_NPM_PACKAGE, DEFAULT_SEARCH_USES, EMPTY_LENGTH, MAX_RESPONSE_TOKENS, MAX_SEARCH_USES, MIN_QUERY_LENGTH, MIN_SEARCH_USES, MONTH_OFFSET, PAD_LENGTH, };
|
|
11
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,qBAAqB,sBAAsB,CAAC;AAClD,QAAA,MAAM,mBAAmB,IAAI,CAAC;AAC9B,QAAA,MAAM,YAAY,IAAI,CAAC;AACvB,QAAA,MAAM,mBAAmB,QAAS,CAAC;AACnC,QAAA,MAAM,eAAe,KAAK,CAAC;AAC3B,QAAA,MAAM,gBAAgB,IAAI,CAAC;AAC3B,QAAA,MAAM,eAAe,IAAI,CAAC;AAC1B,QAAA,MAAM,YAAY,IAAI,CAAC;AACvB,QAAA,MAAM,UAAU,IAAI,CAAC;AAErB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,UAAU,GACX,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,YAAY,QAAO,MAMxB,CAAC;AAEF,QAAA,MAAM,mBAAmB,QAAO,MAC4C,CAAC;AAE7E,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,CAAC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AASA,wBAsEoB"}
|
package/dist/index.js
CHANGED
|
@@ -1,156 +1,17 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { tool } from "@opencode-ai/plugin";
|
|
6
|
-
import Anthropic, { APIError } from "@anthropic-ai/sdk";
|
|
7
|
-
var DEFAULT_MODEL = "claude-sonnet-4-5";
|
|
8
|
-
var MONTH_OFFSET = 1;
|
|
9
|
-
var PAD_LENGTH = 2;
|
|
10
|
-
var ENV_VAR_CAPTURE_GROUP = 1;
|
|
11
|
-
var FIRST_MODEL_INDEX = 0;
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
var ANTHROPIC_NPM_PACKAGE = "@ai-sdk/anthropic";
|
|
3
|
+
var DEFAULT_SEARCH_USES = 5;
|
|
12
4
|
var EMPTY_LENGTH = 0;
|
|
5
|
+
var MAX_RESPONSE_TOKENS = 16000;
|
|
6
|
+
var MAX_SEARCH_USES = 10;
|
|
13
7
|
var MIN_QUERY_LENGTH = 2;
|
|
14
8
|
var MIN_SEARCH_USES = 1;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const month = String(now.getMonth() + MONTH_OFFSET).padStart(PAD_LENGTH, "0");
|
|
22
|
-
const day = String(now.getDate()).padStart(PAD_LENGTH, "0");
|
|
23
|
-
return `${year}-${month}-${day}`;
|
|
24
|
-
};
|
|
25
|
-
var resolveEnvVar = (value) => {
|
|
26
|
-
const match = value.match(/^\{env:(\w+)\}$/);
|
|
27
|
-
if (match?.[ENV_VAR_CAPTURE_GROUP]) {
|
|
28
|
-
return process.env[match[ENV_VAR_CAPTURE_GROUP]] ?? "";
|
|
29
|
-
}
|
|
30
|
-
return value;
|
|
31
|
-
};
|
|
32
|
-
var normalizeBaseURL = (url) => url.replace(/\/v1\/?$/, "");
|
|
33
|
-
var CONFIG_PATHS = [
|
|
34
|
-
join(process.cwd(), "opencode.json"),
|
|
35
|
-
join(process.cwd(), ".opencode", "opencode.json"),
|
|
36
|
-
join(homedir(), ".config", "opencode", "opencode.json")
|
|
37
|
-
];
|
|
38
|
-
var parseConfigFile = (configPath) => {
|
|
39
|
-
try {
|
|
40
|
-
return JSON.parse(readFileSync(configPath, "utf8"));
|
|
41
|
-
} catch (error) {
|
|
42
|
-
if (error instanceof SyntaxError) {
|
|
43
|
-
return `Failed to parse ${configPath}: ${error.message}`;
|
|
44
|
-
}
|
|
45
|
-
return `Failed to parse ${configPath}: ${String(error)}`;
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
var resolveProviderApiKey = (ctx, rawApiKey) => {
|
|
49
|
-
const apiKey = resolveEnvVar(rawApiKey);
|
|
50
|
-
if (apiKey) {
|
|
51
|
-
return apiKey;
|
|
52
|
-
}
|
|
53
|
-
const envMatch = rawApiKey.match(/^\{env:(\w+)\}$/);
|
|
54
|
-
if (envMatch) {
|
|
55
|
-
ctx.errors.push(`${ctx.configPath}: Environment variable ${envMatch[ENV_VAR_CAPTURE_GROUP]} is not set`);
|
|
56
|
-
} else {
|
|
57
|
-
ctx.errors.push(`${ctx.configPath}: Provider "${ctx.providerName}" has empty apiKey`);
|
|
58
|
-
}
|
|
59
|
-
return;
|
|
60
|
-
};
|
|
61
|
-
var resolveModelName = (provider) => {
|
|
62
|
-
if (!provider.models) {
|
|
63
|
-
return DEFAULT_MODEL;
|
|
64
|
-
}
|
|
65
|
-
const models = Object.keys(provider.models);
|
|
66
|
-
return models[FIRST_MODEL_INDEX] ?? DEFAULT_MODEL;
|
|
67
|
-
};
|
|
68
|
-
var resolveBaseURL = (provider) => {
|
|
69
|
-
if (!provider.options?.baseURL) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
return normalizeBaseURL(resolveEnvVar(provider.options.baseURL));
|
|
73
|
-
};
|
|
74
|
-
var resolveProviderConfig = (ctx, provider) => {
|
|
75
|
-
if (provider.npm !== "@ai-sdk/anthropic") {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
if (!provider.options?.apiKey) {
|
|
79
|
-
ctx.errors.push(`${ctx.configPath}: Provider "${ctx.providerName}" has no apiKey configured`);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const apiKey = resolveProviderApiKey(ctx, provider.options.apiKey);
|
|
83
|
-
if (!apiKey) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
apiKey,
|
|
88
|
-
baseURL: resolveBaseURL(provider),
|
|
89
|
-
model: resolveModelName(provider)
|
|
90
|
-
};
|
|
91
|
-
};
|
|
92
|
-
var scanProviders = (configPath, providers, errors) => {
|
|
93
|
-
for (const [providerName, provider] of Object.entries(providers)) {
|
|
94
|
-
const ctx = { configPath, errors, providerName };
|
|
95
|
-
const resolved = resolveProviderConfig(ctx, provider);
|
|
96
|
-
if (resolved) {
|
|
97
|
-
return resolved;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return;
|
|
101
|
-
};
|
|
102
|
-
var scanConfigFile = (configPath, errors) => {
|
|
103
|
-
if (!existsSync(configPath)) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const parsed = parseConfigFile(configPath);
|
|
107
|
-
if (typeof parsed === "string") {
|
|
108
|
-
errors.push(parsed);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
if (!parsed.provider) {
|
|
112
|
-
errors.push(`${configPath}: No "provider" field found`);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
return scanProviders(configPath, parsed.provider, errors);
|
|
116
|
-
};
|
|
117
|
-
var scanAllConfigFiles = (errors) => {
|
|
118
|
-
for (const configPath of CONFIG_PATHS) {
|
|
119
|
-
const resolved = scanConfigFile(configPath, errors);
|
|
120
|
-
if (resolved) {
|
|
121
|
-
return resolved;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return;
|
|
125
|
-
};
|
|
126
|
-
var getEnvFallback = () => {
|
|
127
|
-
const envApiKey = process.env.ANTHROPIC_API_KEY;
|
|
128
|
-
if (envApiKey) {
|
|
129
|
-
return {
|
|
130
|
-
config: {
|
|
131
|
-
apiKey: envApiKey,
|
|
132
|
-
model: DEFAULT_MODEL
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
return;
|
|
137
|
-
};
|
|
138
|
-
var getAnthropicConfig = () => {
|
|
139
|
-
const errors = [];
|
|
140
|
-
const fromConfig = scanAllConfigFiles(errors);
|
|
141
|
-
if (fromConfig) {
|
|
142
|
-
return { config: fromConfig };
|
|
143
|
-
}
|
|
144
|
-
const fromEnv = getEnvFallback();
|
|
145
|
-
if (fromEnv) {
|
|
146
|
-
return fromEnv;
|
|
147
|
-
}
|
|
148
|
-
if (errors.length > EMPTY_LENGTH) {
|
|
149
|
-
return { config: null, error: errors.join(`
|
|
150
|
-
`) };
|
|
151
|
-
}
|
|
152
|
-
return { config: null };
|
|
153
|
-
};
|
|
9
|
+
|
|
10
|
+
// src/index.ts
|
|
11
|
+
import { tool } from "@opencode-ai/plugin";
|
|
12
|
+
|
|
13
|
+
// src/providers/anthropic.ts
|
|
14
|
+
import Anthropic, { APIError } from "@anthropic-ai/sdk";
|
|
154
15
|
var formatSearchResult = (result) => {
|
|
155
16
|
if (result.page_age) {
|
|
156
17
|
return `- [${result.title}](${result.url}) (Updated: ${result.page_age})`;
|
|
@@ -202,31 +63,6 @@ var buildWebSearchTool = (args) => {
|
|
|
202
63
|
}
|
|
203
64
|
return searchTool;
|
|
204
65
|
};
|
|
205
|
-
var formatConfigError = (error) => {
|
|
206
|
-
let hint = "";
|
|
207
|
-
if (error) {
|
|
208
|
-
hint = `
|
|
209
|
-
|
|
210
|
-
${error}`;
|
|
211
|
-
}
|
|
212
|
-
return `Error: web-search requires an Anthropic API key.
|
|
213
|
-
|
|
214
|
-
Set the ANTHROPIC_API_KEY environment variable, or add an Anthropic provider to your opencode.json:
|
|
215
|
-
|
|
216
|
-
{
|
|
217
|
-
"provider": {
|
|
218
|
-
"anthropic": {
|
|
219
|
-
"npm": "@ai-sdk/anthropic",
|
|
220
|
-
"options": {
|
|
221
|
-
"apiKey": "{env:ANTHROPIC_API_KEY}"
|
|
222
|
-
},
|
|
223
|
-
"models": {
|
|
224
|
-
"claude-sonnet-4-5": { "name": "Claude Sonnet 4.5" }
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}${hint}`;
|
|
229
|
-
};
|
|
230
66
|
var formatErrorMessage = (error) => {
|
|
231
67
|
if (error instanceof APIError) {
|
|
232
68
|
return `Anthropic API error: ${error.message} (status: ${error.status})`;
|
|
@@ -275,41 +111,124 @@ var executeSearch = async (config, args) => {
|
|
|
275
111
|
return results.join(`
|
|
276
112
|
`) || "No results returned from web search.";
|
|
277
113
|
};
|
|
278
|
-
var src_default = async () => ({
|
|
279
|
-
tool: {
|
|
280
|
-
"web-search": tool({
|
|
281
|
-
args: {
|
|
282
|
-
allowed_domains: tool.schema.array(tool.schema.string()).optional().describe("Only include results from these domains"),
|
|
283
|
-
blocked_domains: tool.schema.array(tool.schema.string()).optional().describe("Exclude results from these domains"),
|
|
284
|
-
max_uses: tool.schema.number().min(MIN_SEARCH_USES).max(MAX_SEARCH_USES).optional().describe("Maximum number of searches to perform (default: 5)"),
|
|
285
|
-
query: tool.schema.string().min(MIN_QUERY_LENGTH).describe("The search query to execute")
|
|
286
|
-
},
|
|
287
|
-
description: `Search the web using Anthropic's server-side web_search API.
|
|
288
114
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
115
|
+
// src/config.ts
|
|
116
|
+
var isAnthropicModel = (model) => model.api.npm === ANTHROPIC_NPM_PACKAGE;
|
|
117
|
+
var hasWebSearch = (model) => model.options.websearch === true;
|
|
118
|
+
var normalizeBaseURL = (url) => url.replace(/\/v1\/?$/, "");
|
|
119
|
+
var extractBaseURL = (options) => {
|
|
120
|
+
if (typeof options.baseURL !== "string") {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
return normalizeBaseURL(options.baseURL);
|
|
124
|
+
};
|
|
125
|
+
var resolveFromProviders = (providers) => {
|
|
126
|
+
for (const provider of providers) {
|
|
127
|
+
for (const model of Object.values(provider.models)) {
|
|
128
|
+
if (isAnthropicModel(model) && hasWebSearch(model)) {
|
|
129
|
+
if (!provider.key) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
apiKey: provider.key,
|
|
134
|
+
baseURL: extractBaseURL(provider.options),
|
|
135
|
+
model: model.id
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
};
|
|
142
|
+
var formatConfigError = () => `Error: web-search requires an Anthropic provider with \`websearch: true\` set on at least one model.
|
|
292
143
|
|
|
293
|
-
|
|
144
|
+
No model with \`"websearch": true\` was found in your opencode.json configuration.
|
|
294
145
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
146
|
+
To fix this, add an Anthropic provider to your opencode.json and set \`"websearch": true\` in the options of the model you want to use for web searches:
|
|
147
|
+
|
|
148
|
+
{
|
|
149
|
+
"provider": {
|
|
150
|
+
"anthropic": {
|
|
151
|
+
"npm": "@ai-sdk/anthropic",
|
|
152
|
+
"options": {
|
|
153
|
+
"apiKey": "{env:ANTHROPIC_API_KEY}"
|
|
154
|
+
},
|
|
155
|
+
"models": {
|
|
156
|
+
"claude-sonnet-4-5": {
|
|
157
|
+
"options": {
|
|
158
|
+
"websearch": true
|
|
159
|
+
}
|
|
308
160
|
}
|
|
309
161
|
}
|
|
310
|
-
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
Steps:
|
|
167
|
+
1. Open your opencode.json (project root, .opencode/opencode.json, or ~/.config/opencode/opencode.json)
|
|
168
|
+
2. Ensure you have an Anthropic provider configured with a valid API key
|
|
169
|
+
3. Add \`"websearch": true\` to the \`options\` of the Claude model you want to use for web search
|
|
170
|
+
4. Restart OpenCode to pick up the configuration change`;
|
|
171
|
+
|
|
172
|
+
// src/helpers.ts
|
|
173
|
+
var getCurrentMonthYear = () => new Date().toLocaleDateString("en-US", { month: "long", year: "numeric" });
|
|
174
|
+
|
|
175
|
+
// src/index.ts
|
|
176
|
+
var src_default = async (input) => {
|
|
177
|
+
const { data } = await input.client.config.providers();
|
|
178
|
+
let config = null;
|
|
179
|
+
if (data) {
|
|
180
|
+
config = resolveFromProviders(data.providers);
|
|
311
181
|
}
|
|
312
|
-
|
|
182
|
+
return {
|
|
183
|
+
tool: {
|
|
184
|
+
"web-search": tool({
|
|
185
|
+
args: {
|
|
186
|
+
allowed_domains: tool.schema.array(tool.schema.string()).optional().describe("Only include results from these domains"),
|
|
187
|
+
blocked_domains: tool.schema.array(tool.schema.string()).optional().describe("Exclude results from these domains"),
|
|
188
|
+
max_uses: tool.schema.number().min(MIN_SEARCH_USES).max(MAX_SEARCH_USES).optional().describe("Maximum number of searches to perform (default: 5)"),
|
|
189
|
+
query: tool.schema.string().min(MIN_QUERY_LENGTH).describe("The search query to execute")
|
|
190
|
+
},
|
|
191
|
+
description: `- Allows OpenCode to search the web and use the results to inform responses
|
|
192
|
+
- Provides up-to-date information for current events and recent data
|
|
193
|
+
- Returns search result information formatted as search result blocks, including links as markdown hyperlinks
|
|
194
|
+
- Use this tool for accessing information beyond the model's knowledge cutoff
|
|
195
|
+
- Searches are performed automatically within a single API call
|
|
196
|
+
|
|
197
|
+
CRITICAL REQUIREMENT - You MUST follow this:
|
|
198
|
+
- After answering the user's question, you MUST include a "Sources:" section at the end of your response
|
|
199
|
+
- In the Sources section, list all relevant URLs from the search results as markdown hyperlinks: [Title](URL)
|
|
200
|
+
- This is MANDATORY - never skip including sources in your response
|
|
201
|
+
- Example format:
|
|
202
|
+
|
|
203
|
+
[Your answer here]
|
|
204
|
+
|
|
205
|
+
Sources:
|
|
206
|
+
- [Source Title 1](https://example.com/1)
|
|
207
|
+
- [Source Title 2](https://example.com/2)
|
|
208
|
+
|
|
209
|
+
Usage notes:
|
|
210
|
+
- Domain filtering is supported to include or block specific websites
|
|
211
|
+
|
|
212
|
+
IMPORTANT - Use the correct year in search queries:
|
|
213
|
+
- It is currently ${getCurrentMonthYear()}. You MUST use this when searching for recent information, documentation, or current events.
|
|
214
|
+
- Example: If the user asks for "latest React docs", search for "React documentation" with the current year, NOT last year`,
|
|
215
|
+
async execute(args) {
|
|
216
|
+
if (!config) {
|
|
217
|
+
return formatConfigError();
|
|
218
|
+
}
|
|
219
|
+
if (args.allowed_domains && args.blocked_domains) {
|
|
220
|
+
return "Error: Cannot specify both allowed_domains and blocked_domains.";
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
return await executeSearch(config, args);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
return formatErrorMessage(error);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
};
|
|
313
232
|
export {
|
|
314
233
|
src_default as default
|
|
315
234
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AnthropicConfig } from "../types.js";
|
|
2
|
+
declare const formatErrorMessage: (error: unknown) => string;
|
|
3
|
+
declare const executeSearch: (config: AnthropicConfig, args: {
|
|
4
|
+
allowed_domains?: string[];
|
|
5
|
+
blocked_domains?: string[];
|
|
6
|
+
max_uses?: number;
|
|
7
|
+
query: string;
|
|
8
|
+
}) => Promise<string>;
|
|
9
|
+
export { executeSearch, formatErrorMessage };
|
|
10
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EAKhB,MAAM,aAAa,CAAC;AAqErB,QAAA,MAAM,kBAAkB,GAAI,OAAO,OAAO,KAAG,MAQ5C,CAAC;AAoBF,QAAA,MAAM,aAAa,GACjB,QAAQ,eAAe,EACvB,MAAM;IACJ,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,KACA,OAAO,CAAC,MAAM,CA0BhB,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
interface AnthropicConfig {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
baseURL?: string;
|
|
4
|
+
model: string;
|
|
5
|
+
}
|
|
6
|
+
interface WebSearchResult {
|
|
7
|
+
page_age?: string;
|
|
8
|
+
title: string;
|
|
9
|
+
type: "web_search_result";
|
|
10
|
+
url: string;
|
|
11
|
+
}
|
|
12
|
+
interface WebSearchToolResult {
|
|
13
|
+
content: WebSearchResult[] | {
|
|
14
|
+
error_code: string;
|
|
15
|
+
type: "web_search_tool_result_error";
|
|
16
|
+
};
|
|
17
|
+
tool_use_id: string;
|
|
18
|
+
type: "web_search_tool_result";
|
|
19
|
+
}
|
|
20
|
+
interface ServerToolUse {
|
|
21
|
+
id: string;
|
|
22
|
+
input: {
|
|
23
|
+
query: string;
|
|
24
|
+
};
|
|
25
|
+
name: string;
|
|
26
|
+
type: "server_tool_use";
|
|
27
|
+
}
|
|
28
|
+
interface SearchUsage {
|
|
29
|
+
input_tokens: number;
|
|
30
|
+
output_tokens: number;
|
|
31
|
+
server_tool_use?: {
|
|
32
|
+
web_search_requests?: number;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
type ContentBlock = {
|
|
36
|
+
text: string;
|
|
37
|
+
type: "text";
|
|
38
|
+
} | ServerToolUse | WebSearchToolResult;
|
|
39
|
+
export { AnthropicConfig, ContentBlock, SearchUsage, ServerToolUse, WebSearchResult, WebSearchToolResult, };
|
|
40
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAID,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,mBAAmB,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,mBAAmB;IAC3B,OAAO,EAAE,eAAe,EAAE,GAAG;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,8BAA8B,CAAA;KAAE,CAAC;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,wBAAwB,CAAC;CAChC;AAED,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,UAAU,WAAW;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE;QAAE,mBAAmB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpD;AAED,KAAK,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,GAAG,mBAAmB,CAAC;AAEzF,OAAO,EACL,eAAe,EACf,YAAY,EACZ,WAAW,EACX,aAAa,EACb,eAAe,EACf,mBAAmB,GACpB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-websearch",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Claude Code's WebSearch tool ported to OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,9 +18,12 @@
|
|
|
18
18
|
"build": "bun build src/index.ts --outdir dist --target node --format esm --packages external && bun run build:types",
|
|
19
19
|
"build:types": "tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
20
20
|
"dev": "bun run --watch src/index.ts",
|
|
21
|
+
"format": "oxfmt src/",
|
|
22
|
+
"format:check": "oxfmt --check src/",
|
|
21
23
|
"lint": "oxlint --tsconfig tsconfig.json src/",
|
|
24
|
+
"lint:fix": "oxlint --tsconfig tsconfig.json --fix src/",
|
|
22
25
|
"typecheck": "tsc --noEmit",
|
|
23
|
-
"check": "bun run lint && bun run typecheck",
|
|
26
|
+
"check": "bun run format:check && bun run lint && bun run typecheck",
|
|
24
27
|
"prepublishOnly": "bun run check && bun run build"
|
|
25
28
|
},
|
|
26
29
|
"keywords": [
|
|
@@ -35,7 +38,7 @@
|
|
|
35
38
|
"url": "https://github.com/emilsvennesson/opencode-websearch.git"
|
|
36
39
|
},
|
|
37
40
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
41
|
+
"node": ">=24"
|
|
39
42
|
},
|
|
40
43
|
"license": "MIT",
|
|
41
44
|
"dependencies": {
|
|
@@ -44,6 +47,7 @@
|
|
|
44
47
|
"devDependencies": {
|
|
45
48
|
"@opencode-ai/plugin": "^1.1.65",
|
|
46
49
|
"@types/bun": "latest",
|
|
50
|
+
"oxfmt": "^0.32.0",
|
|
47
51
|
"oxlint": "^1.47.0",
|
|
48
52
|
"typescript": "^5.8.0"
|
|
49
53
|
},
|