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 CHANGED
@@ -16,41 +16,49 @@ OpenCode will install it automatically at startup.
16
16
 
17
17
  ## Configuration
18
18
 
19
- The plugin needs an Anthropic API key. It resolves credentials in this order:
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
- 1. **opencode.json** -- looks for an `@ai-sdk/anthropic` provider in your project or global config
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
- ### Option 1: opencode.json provider (recommended)
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
- If you already have an Anthropic provider configured, the plugin will use it automatically:
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
- "apiKey": "{env:ANTHROPIC_API_KEY}"
47
+ "baseURL": "http://localhost:4000/v1/",
48
+ "apiKey": "{env:MY_API_KEY}"
35
49
  },
36
50
  "models": {
37
- "claude-sonnet-4-5": { "name": "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
- # Install dependencies
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
- # Type check
76
- bun run typecheck
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
- # Build
79
- bun run build
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
@@ -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,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"}
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AASA,wBAsEoB"}
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 src_default = async (input) => {
177
- const { data } = await input.client.config.providers();
178
- let config = null;
183
+ var resolveConfig = async (client) => {
184
+ const { data } = await client.config.providers();
179
185
  if (data) {
180
- config = resolveFromProviders(data.providers);
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.0",
4
- "description": "Claude Code's WebSearch tool ported to OpenCode",
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",