opencode-search-plugin 2.2.0 → 3.0.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 +84 -46
- package/README.zh-CN.md +82 -44
- package/index.ts +5 -1
- package/package.json +5 -2
- package/plugins/anysearch-search.ts +4 -4
- package/plugins/bocha-search.ts +392 -0
- package/plugins/brave-search.ts +4 -4
- package/plugins/deepseek-search.ts +4 -4
- package/plugins/jina-search.ts +4 -4
- package/plugins/keenable-search.ts +5 -5
- package/plugins/linkup-search.ts +4 -4
- package/plugins/metaso-search.ts +4 -4
- package/plugins/ollama-search.ts +253 -0
- package/plugins/querit-search.ts +4 -4
- package/plugins/serpapi-search.ts +4 -4
- package/plugins/serper-search.ts +4 -4
- package/plugins/youcom-search.ts +4 -4
package/plugins/linkup-search.ts
CHANGED
|
@@ -55,7 +55,7 @@ function envStr(name: string): string {
|
|
|
55
55
|
// (single agentic pass) or "deep" (several iterations)
|
|
56
56
|
// LINKUP_MAX_RESULTS results per search, clamped to 1-100
|
|
57
57
|
// LINKUP_TIMEOUT ms to wait before failing (0 removes the deadline)
|
|
58
|
-
//
|
|
58
|
+
// OSP_LINKUP_API_KEY required; from https://app.linkup.so
|
|
59
59
|
const CACHE_TTL = envInt("LINKUP_CACHE_TTL", 600_000)
|
|
60
60
|
// OpenCode's own providers each bounded themselves at 25s, and the host adds
|
|
61
61
|
// no deadline of its own around `execute`, so this is the only one that exists.
|
|
@@ -108,7 +108,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
108
108
|
})
|
|
109
109
|
editor.method.update({
|
|
110
110
|
integrationID: ID,
|
|
111
|
-
method: { type: "env", names: ["
|
|
111
|
+
method: { type: "env", names: ["OSP_LINKUP_API_KEY"] },
|
|
112
112
|
})
|
|
113
113
|
})
|
|
114
114
|
|
|
@@ -128,7 +128,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
128
128
|
const credential = connection
|
|
129
129
|
? yield* soften(ctx.integration.connection.resolve(connection))
|
|
130
130
|
: undefined
|
|
131
|
-
return credential?.type === "key" ? credential.key : process.env.
|
|
131
|
+
return credential?.type === "key" ? credential.key : process.env.OSP_LINKUP_API_KEY
|
|
132
132
|
})
|
|
133
133
|
|
|
134
134
|
// Linkup is bound to websearch only while a key exists. Deleting the key
|
|
@@ -152,7 +152,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
152
152
|
const key = yield* resolveKey()
|
|
153
153
|
if (!key) {
|
|
154
154
|
return yield* Effect.fail(
|
|
155
|
-
new Error("Connect Linkup in /connect, or set
|
|
155
|
+
new Error("Connect Linkup in /connect, or set OSP_LINKUP_API_KEY"),
|
|
156
156
|
)
|
|
157
157
|
}
|
|
158
158
|
|
package/plugins/metaso-search.ts
CHANGED
|
@@ -112,7 +112,7 @@ type SearchResult = ReturnType<typeof toResult>
|
|
|
112
112
|
// search cost, far more context tokens. Only worth it
|
|
113
113
|
// when you would otherwise webfetch those hits anyway.
|
|
114
114
|
// METASO_TIMEOUT ms to wait before failing (0 removes the deadline)
|
|
115
|
-
//
|
|
115
|
+
// OSP_METASO_API_KEY required; from https://metaso.cn
|
|
116
116
|
const CACHE_TTL = envInt("METASO_CACHE_TTL", 600_000)
|
|
117
117
|
// OpenCode's own providers each bounded themselves at 25s, and the host adds
|
|
118
118
|
// no deadline of its own around `execute`, so this is the only one that exists.
|
|
@@ -163,7 +163,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
163
163
|
})
|
|
164
164
|
editor.method.update({
|
|
165
165
|
integrationID: ID,
|
|
166
|
-
method: { type: "env", names: ["
|
|
166
|
+
method: { type: "env", names: ["OSP_METASO_API_KEY"] },
|
|
167
167
|
})
|
|
168
168
|
})
|
|
169
169
|
|
|
@@ -183,7 +183,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
183
183
|
const credential = connection
|
|
184
184
|
? yield* soften(ctx.integration.connection.resolve(connection))
|
|
185
185
|
: undefined
|
|
186
|
-
return credential?.type === "key" ? credential.key : process.env.
|
|
186
|
+
return credential?.type === "key" ? credential.key : process.env.OSP_METASO_API_KEY
|
|
187
187
|
})
|
|
188
188
|
|
|
189
189
|
// Metaso is bound to websearch only while a key exists. Deleting the key
|
|
@@ -207,7 +207,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
207
207
|
const key = yield* resolveKey()
|
|
208
208
|
if (!key) {
|
|
209
209
|
return yield* Effect.fail(
|
|
210
|
-
new Error("Connect Metaso in /connect, or set
|
|
210
|
+
new Error("Connect Metaso in /connect, or set OSP_METASO_API_KEY"),
|
|
211
211
|
)
|
|
212
212
|
}
|
|
213
213
|
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { Plugin } from "@opencode/plugin/effect"
|
|
2
|
+
import { Effect, Schema, Stream } from "effect"
|
|
3
|
+
|
|
4
|
+
// Ollama's hosted search answers with the flattest envelope in this package:
|
|
5
|
+
// `results` carries the hits, each a title, a URL and the page's own markdown
|
|
6
|
+
// -- hundreds to tens of thousands of characters, because `content` is the
|
|
7
|
+
// fetched page, not a snippet -- with no wrapper fields and, unlike every
|
|
8
|
+
// other provider here, no dates. Every field but the URL is optional, and a
|
|
9
|
+
// missing one means "Ollama provided none", not an error.
|
|
10
|
+
const OllamaResponse = Schema.Struct({
|
|
11
|
+
results: Schema.optional(
|
|
12
|
+
Schema.Array(
|
|
13
|
+
Schema.Struct({
|
|
14
|
+
title: Schema.optional(Schema.String),
|
|
15
|
+
url: Schema.String,
|
|
16
|
+
content: Schema.optional(Schema.String),
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
19
|
+
),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// The /connect integration ID, and the websearch provider ID it binds to.
|
|
23
|
+
// The `osp-` prefix is this package's namespace: OpenCode's built-ins and a
|
|
24
|
+
// vendor plugin's own entry are bare vendor names, and OpenCode's own `ollama`
|
|
25
|
+
// model provider already registers one, so the bare name is spoken for -- the
|
|
26
|
+
// prefix is what keeps this websearch entry and its /connect row distinct from
|
|
27
|
+
// that model provider, and from the opencode.db row keyed by integration ID.
|
|
28
|
+
const ID = "osp-ollama"
|
|
29
|
+
|
|
30
|
+
// Ollama returns no publication date, so results carry an empty time record.
|
|
31
|
+
function toResult(item: {
|
|
32
|
+
title?: string | undefined
|
|
33
|
+
url: string
|
|
34
|
+
content?: string | undefined
|
|
35
|
+
}) {
|
|
36
|
+
return {
|
|
37
|
+
url: item.url,
|
|
38
|
+
// The URL is the one field the schema insists on, so it is also the one
|
|
39
|
+
// thing a title can fall back to rather than leave the result untitled.
|
|
40
|
+
title: item.title || item.url,
|
|
41
|
+
content: item.content ?? "",
|
|
42
|
+
time: {},
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type SearchResult = ReturnType<typeof toResult>
|
|
47
|
+
|
|
48
|
+
function envInt(name: string, fallback: number): number {
|
|
49
|
+
const raw = (process.env[name] ?? "").trim()
|
|
50
|
+
if (raw === "") return fallback
|
|
51
|
+
const value = Number(raw)
|
|
52
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function envStr(name: string): string {
|
|
56
|
+
return (process.env[name] ?? "").trim()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Web search rides on an Ollama account: the API is hosted at ollama.com, not
|
|
60
|
+
// served by the local model runner (the official SDKs hardcode that host, and
|
|
61
|
+
// the local server neither proxies this endpoint nor reads this key). A
|
|
62
|
+
// generous free tier covers individual use, and the API caps a search at 10
|
|
63
|
+
// results, which is the clamp below. Knobs are read once at load time, so
|
|
64
|
+
// restart after changing them.
|
|
65
|
+
//
|
|
66
|
+
// OLLAMA_CACHE_TTL ms to reuse a response for the same query (0 disables)
|
|
67
|
+
// OLLAMA_MAX_RESULTS results per search, clamped to 1-10 (the API's range).
|
|
68
|
+
// `content` is a whole page of markdown, not a snippet,
|
|
69
|
+
// so the default of 5 already means thousands of
|
|
70
|
+
// characters of context per search
|
|
71
|
+
// OLLAMA_TIMEOUT ms to wait before failing (0 removes the deadline)
|
|
72
|
+
// OSP_OLLAMA_API_KEY required; from https://ollama.com/settings/keys.
|
|
73
|
+
// Namespaced away from the vendor-canonical
|
|
74
|
+
// OLLAMA_API_KEY, which other tools -- and OpenCode's
|
|
75
|
+
// own config examples -- also read, sometimes holding
|
|
76
|
+
// a bearer token for an Ollama-compatible proxy
|
|
77
|
+
// rather than an account key. A /connect credential
|
|
78
|
+
// still outranks this variable.
|
|
79
|
+
const CACHE_TTL = envInt("OLLAMA_CACHE_TTL", 600_000)
|
|
80
|
+
// OpenCode's own providers each bounded themselves at 25s, and the host adds
|
|
81
|
+
// no deadline of its own around `execute`, so this is the only one that exists.
|
|
82
|
+
const SEARCH_TIMEOUT = envInt("OLLAMA_TIMEOUT", 25_000)
|
|
83
|
+
const MAX_RESULTS = Math.min(Math.max(envInt("OLLAMA_MAX_RESULTS", 5), 1), 10)
|
|
84
|
+
|
|
85
|
+
const SEARCH_URL = "https://ollama.com/api/web_search"
|
|
86
|
+
|
|
87
|
+
const CACHE_LIMIT = 200
|
|
88
|
+
const cache = new Map<string, { at: number; results: SearchResult[] }>()
|
|
89
|
+
|
|
90
|
+
function cacheKey(query: string): string {
|
|
91
|
+
return query.trim().replace(/\s+/g, " ")
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function readCache(queryKey: string): SearchResult[] | undefined {
|
|
95
|
+
if (CACHE_TTL <= 0) return undefined
|
|
96
|
+
const hit = cache.get(queryKey)
|
|
97
|
+
return hit && Date.now() - hit.at < CACHE_TTL ? hit.results : undefined
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function writeCache(queryKey: string, results: SearchResult[]): void {
|
|
101
|
+
if (CACHE_TTL <= 0) return
|
|
102
|
+
cache.set(queryKey, { at: Date.now(), results })
|
|
103
|
+
if (cache.size > CACHE_LIMIT) {
|
|
104
|
+
const oldest = cache.keys().next().value
|
|
105
|
+
if (oldest !== undefined) cache.delete(oldest)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// A hung upstream must not hang the agent: the deadline bounds it, and the
|
|
110
|
+
// thunk's signal carries host cancellation into the socket. `timeout <= 0`
|
|
111
|
+
// removes the deadline but never the cancellation, because
|
|
112
|
+
// `AbortSignal.timeout(0)` aborts instantly rather than "never".
|
|
113
|
+
function timedSignal(signal: AbortSignal, timeout: number): AbortSignal {
|
|
114
|
+
return timeout > 0 ? AbortSignal.any([signal, AbortSignal.timeout(timeout)]) : signal
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const effect = (ctx: Plugin.Context) =>
|
|
118
|
+
Effect.gen(function* () {
|
|
119
|
+
// Make "Ollama" show up in /connect with an API-key method.
|
|
120
|
+
yield* ctx.integration.transform((editor) => {
|
|
121
|
+
editor.update(ID, (integration) => {
|
|
122
|
+
integration.name = "Ollama Search (osp)"
|
|
123
|
+
})
|
|
124
|
+
editor.method.update({
|
|
125
|
+
integrationID: ID,
|
|
126
|
+
method: { type: "key", label: "Ollama API key" },
|
|
127
|
+
})
|
|
128
|
+
editor.method.update({
|
|
129
|
+
integrationID: ID,
|
|
130
|
+
method: { type: "env", names: ["OSP_OLLAMA_API_KEY"] },
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// The host instantiates this API's failure channel as `unknown`, which would
|
|
135
|
+
// make the whole plugin effect unrunnable. A credential read must never take
|
|
136
|
+
// the plugin down: a failed read degrades to "no stored key", and the
|
|
137
|
+
// environment fallback below applies exactly as it does when the credential
|
|
138
|
+
// simply does not exist.
|
|
139
|
+
const soften = <A>(read: Effect.Effect<A, unknown>): Effect.Effect<A | undefined, never> =>
|
|
140
|
+
read.pipe(Effect.catch(() => Effect.succeed(undefined)))
|
|
141
|
+
|
|
142
|
+
// The key comes from the /connect credential, falling back to the
|
|
143
|
+
// environment. Resolved per call so a rotated key needs no restart.
|
|
144
|
+
const resolveKey = () =>
|
|
145
|
+
Effect.gen(function* () {
|
|
146
|
+
const connection = yield* soften(ctx.integration.connection.active(ID))
|
|
147
|
+
const credential = connection
|
|
148
|
+
? yield* soften(ctx.integration.connection.resolve(connection))
|
|
149
|
+
: undefined
|
|
150
|
+
return credential?.type === "key" ? credential.key : process.env.OSP_OLLAMA_API_KEY
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
// Web search requires an Ollama account, so the provider binds only while
|
|
154
|
+
// a key exists. Deleting the key unbinds it, and `random` can never pick a
|
|
155
|
+
// keyless Ollama. The transform replays on reload and reads `bound` as it
|
|
156
|
+
// stands then.
|
|
157
|
+
let bound = Boolean(yield* resolveKey())
|
|
158
|
+
|
|
159
|
+
yield* ctx.websearch.transform((editor) => {
|
|
160
|
+
if (!bound) return
|
|
161
|
+
editor.add({
|
|
162
|
+
id: ID,
|
|
163
|
+
name: "Ollama Search",
|
|
164
|
+
execute: ({ query }) =>
|
|
165
|
+
Effect.gen(function* () {
|
|
166
|
+
const queryKey = cacheKey(query)
|
|
167
|
+
const cached = readCache(queryKey)
|
|
168
|
+
if (cached) return cached
|
|
169
|
+
|
|
170
|
+
// Rechecked per call: binding can lag a key deletion by the
|
|
171
|
+
// event round-trip, so this keeps that window explicit.
|
|
172
|
+
const key = yield* resolveKey()
|
|
173
|
+
if (!key) {
|
|
174
|
+
return yield* Effect.fail(
|
|
175
|
+
new Error("Connect Ollama in /connect, or set OSP_OLLAMA_API_KEY"),
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const response = yield* Effect.tryPromise({
|
|
180
|
+
try: (signal) =>
|
|
181
|
+
fetch(SEARCH_URL, {
|
|
182
|
+
method: "POST",
|
|
183
|
+
headers: {
|
|
184
|
+
Accept: "application/json",
|
|
185
|
+
"Content-Type": "application/json",
|
|
186
|
+
Authorization: "Bearer " + key,
|
|
187
|
+
},
|
|
188
|
+
body: JSON.stringify({
|
|
189
|
+
query: queryKey,
|
|
190
|
+
max_results: MAX_RESULTS,
|
|
191
|
+
}),
|
|
192
|
+
signal: timedSignal(signal, SEARCH_TIMEOUT),
|
|
193
|
+
}),
|
|
194
|
+
// A timeout is its own failure: naming it stops the user hunting
|
|
195
|
+
// for a key problem that was never there.
|
|
196
|
+
catch: (error) =>
|
|
197
|
+
error instanceof Error && error.name === "TimeoutError"
|
|
198
|
+
? new Error("Ollama search timed out after " + SEARCH_TIMEOUT + "ms")
|
|
199
|
+
: new Error("Ollama request failed: " + String(error)),
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
// A bad key answers 401 with `{"error":"Unauthorized"}` (checked
|
|
204
|
+
// against the live API); the body rides along verbatim, so any
|
|
205
|
+
// shape Ollama adds later is reported rather than mistranslated.
|
|
206
|
+
const detail = yield* Effect.tryPromise({
|
|
207
|
+
try: () => response.text(),
|
|
208
|
+
catch: () => new Error("response body unavailable"),
|
|
209
|
+
})
|
|
210
|
+
return yield* Effect.fail(
|
|
211
|
+
new Error("Ollama API error " + response.status + ": " + detail),
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const json = yield* Effect.tryPromise({
|
|
216
|
+
try: () => response.json(),
|
|
217
|
+
catch: (error) => new Error("Invalid Ollama response: " + String(error)),
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
const body = yield* Schema.decodeUnknownEffect(OllamaResponse)(json)
|
|
221
|
+
const results = (body.results ?? []).map(toResult)
|
|
222
|
+
|
|
223
|
+
writeCache(queryKey, results)
|
|
224
|
+
return results
|
|
225
|
+
}),
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
// Rebind on credential changes: connect, delete, or rotate a key and the
|
|
230
|
+
// provider joins or leaves the websearch pool without a restart. Env-var
|
|
231
|
+
// keys cannot change under a running process, so they bind at startup.
|
|
232
|
+
yield* Effect.forkScoped(
|
|
233
|
+
ctx.event.subscribe().pipe(
|
|
234
|
+
Stream.filter(
|
|
235
|
+
(event) =>
|
|
236
|
+
event.type === "credential.updated" ||
|
|
237
|
+
event.type === "credential.switched" ||
|
|
238
|
+
event.type === "integration.updated",
|
|
239
|
+
),
|
|
240
|
+
Stream.mapEffect(() =>
|
|
241
|
+
Effect.gen(function* () {
|
|
242
|
+
const next = Boolean(yield* resolveKey())
|
|
243
|
+
if (next === bound) return
|
|
244
|
+
bound = next
|
|
245
|
+
yield* ctx.websearch.reload()
|
|
246
|
+
}),
|
|
247
|
+
),
|
|
248
|
+
Stream.runDrain,
|
|
249
|
+
),
|
|
250
|
+
)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
export default Plugin.define({ id: "ollama-search", effect })
|
package/plugins/querit-search.ts
CHANGED
|
@@ -123,7 +123,7 @@ type SearchResult = ReturnType<typeof toResult>
|
|
|
123
123
|
// QUERIT_CACHE_TTL ms to reuse a response for the same query (0 disables)
|
|
124
124
|
// QUERIT_COUNT results per search; Querit's own limit applies
|
|
125
125
|
// QUERIT_TIMEOUT ms to wait before failing (0 removes the deadline)
|
|
126
|
-
//
|
|
126
|
+
// OSP_QUERIT_API_KEY required; from https://querit.ai
|
|
127
127
|
const CACHE_TTL = envInt("QUERIT_CACHE_TTL", 600_000)
|
|
128
128
|
// OpenCode's own providers each bounded themselves at 25s, and the host adds
|
|
129
129
|
// no deadline of its own around `execute`, so this is the only one that exists.
|
|
@@ -173,7 +173,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
173
173
|
})
|
|
174
174
|
editor.method.update({
|
|
175
175
|
integrationID: ID,
|
|
176
|
-
method: { type: "env", names: ["
|
|
176
|
+
method: { type: "env", names: ["OSP_QUERIT_API_KEY"] },
|
|
177
177
|
})
|
|
178
178
|
})
|
|
179
179
|
|
|
@@ -193,7 +193,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
193
193
|
const credential = connection
|
|
194
194
|
? yield* soften(ctx.integration.connection.resolve(connection))
|
|
195
195
|
: undefined
|
|
196
|
-
return credential?.type === "key" ? credential.key : process.env.
|
|
196
|
+
return credential?.type === "key" ? credential.key : process.env.OSP_QUERIT_API_KEY
|
|
197
197
|
})
|
|
198
198
|
|
|
199
199
|
// Querit is bound to websearch only while a key exists. Deleting the key
|
|
@@ -217,7 +217,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
217
217
|
const key = yield* resolveKey()
|
|
218
218
|
if (!key) {
|
|
219
219
|
return yield* Effect.fail(
|
|
220
|
-
new Error("Connect Querit in /connect, or set
|
|
220
|
+
new Error("Connect Querit in /connect, or set OSP_QUERIT_API_KEY"),
|
|
221
221
|
)
|
|
222
222
|
}
|
|
223
223
|
|
|
@@ -112,7 +112,7 @@ type SearchResult = ReturnType<typeof toResult>
|
|
|
112
112
|
// SERPAPI_HL language code for results, e.g. "en", "zh-cn"
|
|
113
113
|
// SERPAPI_TBS advanced filters, e.g. "qdr:d" (past day), "qdr:y"
|
|
114
114
|
// SERPAPI_TIMEOUT ms to wait before failing (0 removes the deadline)
|
|
115
|
-
//
|
|
115
|
+
// OSP_SERPAPI_API_KEY required; from https://serpapi.com
|
|
116
116
|
//
|
|
117
117
|
// There is deliberately no result-count knob: Google withdrew the `num`
|
|
118
118
|
// parameter, so SerpApi cannot honour one and a count would be a lie.
|
|
@@ -167,7 +167,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
167
167
|
})
|
|
168
168
|
editor.method.update({
|
|
169
169
|
integrationID: ID,
|
|
170
|
-
method: { type: "env", names: ["
|
|
170
|
+
method: { type: "env", names: ["OSP_SERPAPI_API_KEY"] },
|
|
171
171
|
})
|
|
172
172
|
})
|
|
173
173
|
|
|
@@ -187,7 +187,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
187
187
|
const credential = connection
|
|
188
188
|
? yield* soften(ctx.integration.connection.resolve(connection))
|
|
189
189
|
: undefined
|
|
190
|
-
return credential?.type === "key" ? credential.key : process.env.
|
|
190
|
+
return credential?.type === "key" ? credential.key : process.env.OSP_SERPAPI_API_KEY
|
|
191
191
|
})
|
|
192
192
|
|
|
193
193
|
// SerpApi is bound to websearch only while a key exists. Deleting the key
|
|
@@ -211,7 +211,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
211
211
|
const key = yield* resolveKey()
|
|
212
212
|
if (!key) {
|
|
213
213
|
return yield* Effect.fail(
|
|
214
|
-
new Error("Connect SerpApi in /connect, or set
|
|
214
|
+
new Error("Connect SerpApi in /connect, or set OSP_SERPAPI_API_KEY"),
|
|
215
215
|
)
|
|
216
216
|
}
|
|
217
217
|
|
package/plugins/serper-search.ts
CHANGED
|
@@ -113,7 +113,7 @@ type SearchResult = ReturnType<typeof toResult>
|
|
|
113
113
|
// SERPER_TBS time filter: "qdr:h" (hour) "qdr:d" (day) "qdr:w" (week)
|
|
114
114
|
// "qdr:m" (month) "qdr:y" (year)
|
|
115
115
|
// SERPER_TIMEOUT ms to wait before failing (0 removes the deadline)
|
|
116
|
-
//
|
|
116
|
+
// OSP_SERPER_API_KEY required; from https://serper.dev
|
|
117
117
|
const CACHE_TTL = envInt("SERPER_CACHE_TTL", 600_000)
|
|
118
118
|
// OpenCode's own providers each bounded themselves at 25s, and the host adds
|
|
119
119
|
// no deadline of its own around `execute`, so this is the only one that exists.
|
|
@@ -166,7 +166,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
166
166
|
})
|
|
167
167
|
editor.method.update({
|
|
168
168
|
integrationID: ID,
|
|
169
|
-
method: { type: "env", names: ["
|
|
169
|
+
method: { type: "env", names: ["OSP_SERPER_API_KEY"] },
|
|
170
170
|
})
|
|
171
171
|
})
|
|
172
172
|
|
|
@@ -186,7 +186,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
186
186
|
const credential = connection
|
|
187
187
|
? yield* soften(ctx.integration.connection.resolve(connection))
|
|
188
188
|
: undefined
|
|
189
|
-
return credential?.type === "key" ? credential.key : process.env.
|
|
189
|
+
return credential?.type === "key" ? credential.key : process.env.OSP_SERPER_API_KEY
|
|
190
190
|
})
|
|
191
191
|
|
|
192
192
|
// Serper is bound to websearch only while a key exists. Deleting the key
|
|
@@ -210,7 +210,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
210
210
|
const key = yield* resolveKey()
|
|
211
211
|
if (!key) {
|
|
212
212
|
return yield* Effect.fail(
|
|
213
|
-
new Error("Connect Serper in /connect, or set
|
|
213
|
+
new Error("Connect Serper in /connect, or set OSP_SERPER_API_KEY"),
|
|
214
214
|
)
|
|
215
215
|
}
|
|
216
216
|
|
package/plugins/youcom-search.ts
CHANGED
|
@@ -141,7 +141,7 @@ type SearchResult = ReturnType<typeof toResult>
|
|
|
141
141
|
// YOUCOM_COUNTRY geographic focus, e.g. "US", "CN"
|
|
142
142
|
// YOUCOM_FRESHNESS "day" | "week" | "month" | "year" or "YYYY-MM-DDtoYYYY-MM-DD"
|
|
143
143
|
// YOUCOM_TIMEOUT ms to wait before failing (0 removes the deadline)
|
|
144
|
-
//
|
|
144
|
+
// OSP_YDC_API_KEY required; You.com's canonical env var, shared with its SDKs
|
|
145
145
|
const CACHE_TTL = envInt("YOUCOM_CACHE_TTL", 600_000)
|
|
146
146
|
// OpenCode's own providers each bounded themselves at 25s, and the host adds
|
|
147
147
|
// no deadline of its own around `execute`, so this is the only one that exists.
|
|
@@ -197,7 +197,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
197
197
|
})
|
|
198
198
|
editor.method.update({
|
|
199
199
|
integrationID: ID,
|
|
200
|
-
method: { type: "env", names: ["
|
|
200
|
+
method: { type: "env", names: ["OSP_YDC_API_KEY"] },
|
|
201
201
|
})
|
|
202
202
|
})
|
|
203
203
|
|
|
@@ -217,7 +217,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
217
217
|
const credential = connection
|
|
218
218
|
? yield* soften(ctx.integration.connection.resolve(connection))
|
|
219
219
|
: undefined
|
|
220
|
-
return credential?.type === "key" ? credential.key : process.env.
|
|
220
|
+
return credential?.type === "key" ? credential.key : process.env.OSP_YDC_API_KEY
|
|
221
221
|
})
|
|
222
222
|
|
|
223
223
|
// You.com is bound to websearch only while a key exists. Deleting the key
|
|
@@ -241,7 +241,7 @@ export const effect = (ctx: Plugin.Context) =>
|
|
|
241
241
|
const key = yield* resolveKey()
|
|
242
242
|
if (!key) {
|
|
243
243
|
return yield* Effect.fail(
|
|
244
|
-
new Error("Connect You.com in /connect, or set
|
|
244
|
+
new Error("Connect You.com in /connect, or set OSP_YDC_API_KEY"),
|
|
245
245
|
)
|
|
246
246
|
}
|
|
247
247
|
|