opencode-websearch 0.2.0 → 0.2.3
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 +52 -23
- package/dist/config.d.ts +3 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +74 -61
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,41 +16,49 @@ OpenCode will install it automatically at startup.
|
|
|
16
16
|
|
|
17
17
|
## Configuration
|
|
18
18
|
|
|
19
|
-
The plugin
|
|
19
|
+
The plugin looks for an Anthropic provider (`@ai-sdk/anthropic`) with `"websearch": true` set on at least one model. It picks up credentials however you've configured them in OpenCode -- via `/connect`, environment variables, or `options.apiKey` in your config.
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
2. **Environment variable** -- falls back to `ANTHROPIC_API_KEY`
|
|
21
|
+
Add `"websearch": true` to the model you want the plugin to use for searches:
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"provider": {
|
|
26
|
+
"anthropic": {
|
|
27
|
+
"models": {
|
|
28
|
+
"claude-sonnet-4-5": {
|
|
29
|
+
"options": {
|
|
30
|
+
"websearch": true
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
25
38
|
|
|
26
|
-
|
|
39
|
+
This also works with custom providers that use `@ai-sdk/anthropic`, such as a LiteLLM proxy:
|
|
27
40
|
|
|
28
41
|
```json
|
|
29
42
|
{
|
|
30
43
|
"provider": {
|
|
31
|
-
"anthropic": {
|
|
44
|
+
"my-anthropic": {
|
|
32
45
|
"npm": "@ai-sdk/anthropic",
|
|
33
46
|
"options": {
|
|
34
|
-
"
|
|
47
|
+
"baseURL": "http://localhost:4000/v1/",
|
|
48
|
+
"apiKey": "{env:MY_API_KEY}"
|
|
35
49
|
},
|
|
36
50
|
"models": {
|
|
37
|
-
"claude-sonnet-4-5": {
|
|
51
|
+
"claude-sonnet-4-5": {
|
|
52
|
+
"options": {
|
|
53
|
+
"websearch": true
|
|
54
|
+
}
|
|
55
|
+
}
|
|
38
56
|
}
|
|
39
57
|
}
|
|
40
58
|
}
|
|
41
59
|
}
|
|
42
60
|
```
|
|
43
61
|
|
|
44
|
-
The model used for search is the first model listed in the provider config.
|
|
45
|
-
|
|
46
|
-
### Option 2: environment variable
|
|
47
|
-
|
|
48
|
-
```sh
|
|
49
|
-
export ANTHROPIC_API_KEY=sk-ant-...
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
When using the env var fallback, `claude-sonnet-4-5` is used as the default model.
|
|
53
|
-
|
|
54
62
|
## Usage
|
|
55
63
|
|
|
56
64
|
Once configured, the `web-search` tool is available to the AI agent. It accepts:
|
|
@@ -68,15 +76,36 @@ Results are returned as formatted markdown with source links.
|
|
|
68
76
|
|
|
69
77
|
## Development
|
|
70
78
|
|
|
79
|
+
### Local Development
|
|
80
|
+
|
|
81
|
+
To develop or customize the plugin locally, clone the repo and symlink the
|
|
82
|
+
source entry point into your OpenCode plugin directory:
|
|
83
|
+
|
|
71
84
|
```sh
|
|
72
|
-
|
|
85
|
+
git clone https://github.com/emilsvennesson/opencode-websearch ~/.config/opencode/opencode-websearch
|
|
86
|
+
cd ~/.config/opencode/opencode-websearch
|
|
73
87
|
bun install
|
|
88
|
+
mkdir -p ~/.config/opencode/plugin
|
|
89
|
+
ln -sf ~/.config/opencode/opencode-websearch/src/index.ts ~/.config/opencode/plugin/websearch.ts
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
OpenCode will load the plugin directly from source on startup. Any edits to the
|
|
93
|
+
files in `src/` take effect next time you start OpenCode.
|
|
74
94
|
|
|
75
|
-
|
|
76
|
-
|
|
95
|
+
> **Note:** When using the symlink approach, remove `"opencode-websearch"` from
|
|
96
|
+
> the `plugin` array in your `opencode.json` to avoid loading the plugin twice.
|
|
77
97
|
|
|
78
|
-
|
|
79
|
-
|
|
98
|
+
### Commands
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
bun install # install dependencies
|
|
102
|
+
bun run format # auto-format source files
|
|
103
|
+
bun run format:check # verify formatting (no changes)
|
|
104
|
+
bun run lint # run oxlint
|
|
105
|
+
bun run lint:fix # auto-fix lint issues
|
|
106
|
+
bun run typecheck # type check with tsc
|
|
107
|
+
bun run check # format:check + lint + typecheck (full quality gate)
|
|
108
|
+
bun run build # ESM bundle + declaration files → dist/
|
|
80
109
|
```
|
|
81
110
|
|
|
82
111
|
## License
|
package/dist/config.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AnthropicConfig } from "./types.js";
|
|
2
2
|
interface ProviderData {
|
|
3
|
+
id: string;
|
|
4
|
+
key?: string;
|
|
3
5
|
models: Record<string, {
|
|
4
6
|
api: {
|
|
5
7
|
npm: string;
|
|
@@ -7,7 +9,6 @@ interface ProviderData {
|
|
|
7
9
|
id: string;
|
|
8
10
|
options: Record<string, unknown>;
|
|
9
11
|
}>;
|
|
10
|
-
key?: string;
|
|
11
12
|
options: Record<string, unknown>;
|
|
12
13
|
}
|
|
13
14
|
/**
|
|
@@ -16,5 +17,5 @@ interface ProviderData {
|
|
|
16
17
|
*/
|
|
17
18
|
declare const resolveFromProviders: (providers: ProviderData[]) => AnthropicConfig | null;
|
|
18
19
|
declare const formatConfigError: () => string;
|
|
19
|
-
export { formatConfigError, resolveFromProviders };
|
|
20
|
+
export { formatConfigError, ProviderData, resolveFromProviders };
|
|
20
21
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,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,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AA4BD;;;GAGG;AACH,QAAA,MAAM,oBAAoB,GAAI,WAAW,YAAY,EAAE,KAAG,eAAe,GAAG,IAiB3E,CAAC;AAIF,QAAA,MAAM,iBAAiB,QAAO,MA6B0B,CAAC;AAEzD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,CAAC"}
|
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":";;;;;;;;;;;;;;;;;;;AA2BA,wBAsEoB"}
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,70 @@ var MIN_SEARCH_USES = 1;
|
|
|
10
10
|
// src/index.ts
|
|
11
11
|
import { tool } from "@opencode-ai/plugin";
|
|
12
12
|
|
|
13
|
+
// src/config.ts
|
|
14
|
+
var isAnthropicModel = (model) => model.api.npm === ANTHROPIC_NPM_PACKAGE;
|
|
15
|
+
var hasWebSearch = (model) => model.options.websearch === true;
|
|
16
|
+
var normalizeBaseURL = (url) => url.replace(/\/v1\/?$/, "");
|
|
17
|
+
var extractApiKey = (options) => {
|
|
18
|
+
if (typeof options.apiKey !== "string") {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
return options.apiKey;
|
|
22
|
+
};
|
|
23
|
+
var extractBaseURL = (options) => {
|
|
24
|
+
if (typeof options.baseURL !== "string") {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
return normalizeBaseURL(options.baseURL);
|
|
28
|
+
};
|
|
29
|
+
var resolveFromProviders = (providers) => {
|
|
30
|
+
for (const provider of providers) {
|
|
31
|
+
for (const model of Object.values(provider.models)) {
|
|
32
|
+
if (isAnthropicModel(model) && hasWebSearch(model)) {
|
|
33
|
+
const apiKey = provider.key ?? extractApiKey(provider.options);
|
|
34
|
+
if (!apiKey) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
apiKey,
|
|
39
|
+
baseURL: extractBaseURL(provider.options),
|
|
40
|
+
model: model.id
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
var formatConfigError = () => `Error: web-search requires an Anthropic provider with \`websearch: true\` set on at least one model.
|
|
48
|
+
|
|
49
|
+
No model with \`"websearch": true\` was found in your opencode.json configuration.
|
|
50
|
+
|
|
51
|
+
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:
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
"provider": {
|
|
55
|
+
"anthropic": {
|
|
56
|
+
"npm": "@ai-sdk/anthropic",
|
|
57
|
+
"options": {
|
|
58
|
+
"apiKey": "{env:ANTHROPIC_API_KEY}"
|
|
59
|
+
},
|
|
60
|
+
"models": {
|
|
61
|
+
"claude-sonnet-4-5": {
|
|
62
|
+
"options": {
|
|
63
|
+
"websearch": true
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Steps:
|
|
72
|
+
1. Open your opencode.json (project root, .opencode/opencode.json, or ~/.config/opencode/opencode.json)
|
|
73
|
+
2. Ensure you have an Anthropic provider configured with a valid API key
|
|
74
|
+
3. Add \`"websearch": true\` to the \`options\` of the Claude model you want to use for web search
|
|
75
|
+
4. Restart OpenCode to pick up the configuration change`;
|
|
76
|
+
|
|
13
77
|
// src/providers/anthropic.ts
|
|
14
78
|
import Anthropic, { APIError } from "@anthropic-ai/sdk";
|
|
15
79
|
var formatSearchResult = (result) => {
|
|
@@ -112,73 +176,19 @@ var executeSearch = async (config, args) => {
|
|
|
112
176
|
`) || "No results returned from web search.";
|
|
113
177
|
};
|
|
114
178
|
|
|
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.
|
|
143
|
-
|
|
144
|
-
No model with \`"websearch": true\` was found in your opencode.json configuration.
|
|
145
|
-
|
|
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
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
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
179
|
// src/helpers.ts
|
|
173
180
|
var getCurrentMonthYear = () => new Date().toLocaleDateString("en-US", { month: "long", year: "numeric" });
|
|
174
181
|
|
|
175
182
|
// src/index.ts
|
|
176
|
-
var
|
|
177
|
-
const { data } = await
|
|
178
|
-
let config = null;
|
|
183
|
+
var resolveConfig = async (client) => {
|
|
184
|
+
const { data } = await client.config.providers();
|
|
179
185
|
if (data) {
|
|
180
|
-
|
|
186
|
+
return resolveFromProviders(data.providers);
|
|
181
187
|
}
|
|
188
|
+
return null;
|
|
189
|
+
};
|
|
190
|
+
var src_default = async (input) => {
|
|
191
|
+
let config = null;
|
|
182
192
|
return {
|
|
183
193
|
tool: {
|
|
184
194
|
"web-search": tool({
|
|
@@ -213,6 +223,9 @@ IMPORTANT - Use the correct year in search queries:
|
|
|
213
223
|
- It is currently ${getCurrentMonthYear()}. You MUST use this when searching for recent information, documentation, or current events.
|
|
214
224
|
- Example: If the user asks for "latest React docs", search for "React documentation" with the current year, NOT last year`,
|
|
215
225
|
async execute(args) {
|
|
226
|
+
if (!config) {
|
|
227
|
+
config = await resolveConfig(input.client);
|
|
228
|
+
}
|
|
216
229
|
if (!config) {
|
|
217
230
|
return formatConfigError();
|
|
218
231
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-websearch",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Claude Code's WebSearch tool
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "Web search plugin for OpenCode, inspired by Claude Code's WebSearch tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|