opencode-rag-plugin 1.19.4 → 1.19.8
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/chunker/base.js +19 -5
- package/dist/chunker/factory.js +27 -9
- package/dist/chunker/grammar.d.ts +18 -1
- package/dist/chunker/grammar.js +48 -10
- package/dist/chunker/image.js +5 -0
- package/dist/chunker/pdf.js +30 -14
- package/dist/cli/commands/backend-detect.d.ts +61 -0
- package/dist/cli/commands/backend-detect.js +119 -0
- package/dist/cli/commands/index-command.js +11 -0
- package/dist/cli/commands/init-helpers.d.ts +4 -1
- package/dist/cli/commands/init-helpers.js +21 -4
- package/dist/cli/commands/init.js +61 -24
- package/dist/cli/commands/quirk.js +9 -3
- package/dist/cli/commands/setup.js +12 -3
- package/dist/cli/commands/status.js +14 -5
- package/dist/cli/commands/ui.js +23 -9
- package/dist/cli/commands/update.js +4 -5
- package/dist/cli/format.d.ts +5 -2
- package/dist/cli/format.js +14 -5
- package/dist/content/image.js +33 -11
- package/dist/content/reader.js +74 -13
- package/dist/core/bootstrap.js +10 -3
- package/dist/core/config.d.ts +27 -1
- package/dist/core/config.js +41 -2
- package/dist/core/desc-cache.d.ts +8 -2
- package/dist/core/desc-cache.js +10 -3
- package/dist/core/doc-progress.js +5 -2
- package/dist/core/interfaces.d.ts +32 -4
- package/dist/core/manifest.js +1 -1
- package/dist/core/provider-defaults.d.ts +2 -0
- package/dist/core/provider-defaults.js +19 -4
- package/dist/core/runtime-overrides.d.ts +0 -6
- package/dist/core/version-check.d.ts +5 -0
- package/dist/core/version-check.js +8 -2
- package/dist/describer/anthropic.d.ts +2 -2
- package/dist/describer/anthropic.js +19 -5
- package/dist/describer/describer.d.ts +20 -0
- package/dist/describer/describer.js +132 -16
- package/dist/describer/gemini.js +25 -10
- package/dist/describer/shared.d.ts +28 -0
- package/dist/describer/shared.js +60 -0
- package/dist/embedder/factory.d.ts +5 -3
- package/dist/embedder/factory.js +42 -9
- package/dist/embedder/health.js +19 -19
- package/dist/embedder/http.d.ts +14 -1
- package/dist/embedder/http.js +60 -6
- package/dist/embedder/ollama.d.ts +3 -1
- package/dist/embedder/ollama.js +12 -2
- package/dist/eval/session-logger.js +7 -0
- package/dist/eval/storage.js +8 -0
- package/dist/indexer/git-diff.d.ts +1 -1
- package/dist/indexer/git-diff.js +5 -1
- package/dist/indexer/pipeline.js +511 -346
- package/dist/indexer/stats.d.ts +2 -0
- package/dist/indexer/stats.js +1 -0
- package/dist/indexer/watch.js +8 -1
- package/dist/indexer/worker.js +21 -0
- package/dist/mcp/cli.js +4 -0
- package/dist/mcp/handlers.js +16 -5
- package/dist/mcp/server.js +3 -0
- package/dist/opencode/create-read-tool.js +14 -3
- package/dist/opencode/tool-args.js +23 -1
- package/dist/plugin.js +66 -152
- package/dist/quirks/auto-capture.js +5 -0
- package/dist/quirks/quirk-store.d.ts +1 -1
- package/dist/quirks/quirk-store.js +56 -17
- package/dist/retriever/context-optimizer.js +18 -4
- package/dist/retriever/keyword-index.d.ts +2 -0
- package/dist/retriever/keyword-index.js +38 -4
- package/dist/retriever/retriever.js +6 -1
- package/dist/tui.js +41 -4
- package/dist/vectorstore/lancedb.d.ts +104 -8
- package/dist/vectorstore/lancedb.js +345 -71
- package/dist/vectorstore/memory.d.ts +8 -3
- package/dist/vectorstore/memory.js +27 -4
- package/dist/watcher.d.ts +8 -0
- package/dist/watcher.js +237 -85
- package/dist/web/api.d.ts +5 -1
- package/dist/web/api.js +195 -69
- package/dist/web/server.d.ts +2 -0
- package/dist/web/server.js +66 -28
- package/dist/web/static.d.ts +5 -2
- package/dist/web/static.js +9 -5
- package/dist/web/ui/assets/index-BDPYdtA1.js +3 -0
- package/dist/web/ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/ui/assets/index-CJBvt6e0.js +0 -3
package/dist/embedder/http.js
CHANGED
|
@@ -67,6 +67,48 @@ function destroyAllPooledConnections() {
|
|
|
67
67
|
connectionPool.clear();
|
|
68
68
|
}
|
|
69
69
|
export { destroyAllPooledConnections };
|
|
70
|
+
/**
|
|
71
|
+
* Fetch a URL through the configured proxy, applying the proxy to
|
|
72
|
+
* HTTP(S)_PROXY env vars for the duration of the request (serialized).
|
|
73
|
+
*
|
|
74
|
+
* Used by health checks and other one-off requests that do not go
|
|
75
|
+
* through {@link postJson}.
|
|
76
|
+
*/
|
|
77
|
+
export async function fetchWithProxy(urlString, init, proxy) {
|
|
78
|
+
const authHeader = buildProxyAuthHeader(proxy);
|
|
79
|
+
const envOverride = applyProxyEnv(proxy);
|
|
80
|
+
const [savedHttpProxy, savedHttpsProxy] = [process.env.HTTP_PROXY, process.env.HTTPS_PROXY];
|
|
81
|
+
return proxyRequestQueue.then(async () => {
|
|
82
|
+
try {
|
|
83
|
+
if (envOverride) {
|
|
84
|
+
process.env.HTTP_PROXY = envOverride.httpProxy;
|
|
85
|
+
process.env.HTTPS_PROXY = envOverride.httpsProxy;
|
|
86
|
+
}
|
|
87
|
+
const headers = { ...init.headers };
|
|
88
|
+
if (authHeader) {
|
|
89
|
+
headers["Proxy-Authorization"] = authHeader;
|
|
90
|
+
}
|
|
91
|
+
return await fetch(urlString, {
|
|
92
|
+
method: init.method ?? "GET",
|
|
93
|
+
headers,
|
|
94
|
+
body: init.body,
|
|
95
|
+
signal: init.signal,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
if (envOverride) {
|
|
100
|
+
if (savedHttpProxy === undefined)
|
|
101
|
+
delete process.env.HTTP_PROXY;
|
|
102
|
+
else
|
|
103
|
+
process.env.HTTP_PROXY = savedHttpProxy;
|
|
104
|
+
if (savedHttpsProxy === undefined)
|
|
105
|
+
delete process.env.HTTPS_PROXY;
|
|
106
|
+
else
|
|
107
|
+
process.env.HTTPS_PROXY = savedHttpsProxy;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
70
112
|
/**
|
|
71
113
|
* Check whether a hostname refers to the local machine.
|
|
72
114
|
*
|
|
@@ -150,8 +192,8 @@ function applyProxyEnv(proxy) {
|
|
|
150
192
|
* @param redirectCount - Internal redirect counter (starts at 0)
|
|
151
193
|
* @returns A promise resolving to an HttpResponseLike
|
|
152
194
|
*/
|
|
153
|
-
export function directRequest(url, body, headers, timeoutMs, redirectCount = 0) {
|
|
154
|
-
return sendRawHttpRequest(url, body, headers, timeoutMs, redirectCount);
|
|
195
|
+
export function directRequest(url, body, headers, timeoutMs, redirectCount = 0, signal) {
|
|
196
|
+
return sendRawHttpRequest(url, body, headers, timeoutMs, redirectCount, signal);
|
|
155
197
|
}
|
|
156
198
|
const CRLF = Buffer.from("\r\n");
|
|
157
199
|
/** Build the full raw HTTP/1.1 request buffer (headers + JSON body). */
|
|
@@ -241,7 +283,7 @@ function parseResponse(buffer) {
|
|
|
241
283
|
return { status, headers, body };
|
|
242
284
|
}
|
|
243
285
|
/** Raw HTTP/1.1 POST over TCP/TLS with connection pooling, redirect following, and timeout. */
|
|
244
|
-
async function sendRawHttpRequest(url, body, headers, timeoutMs, redirectCount) {
|
|
286
|
+
async function sendRawHttpRequest(url, body, headers, timeoutMs, redirectCount, signal) {
|
|
245
287
|
const requestBuffer = buildRequestPayload(body, headers, url);
|
|
246
288
|
const port = url.port ? Number(url.port) : url.protocol === "https:" ? 443 : 80;
|
|
247
289
|
const isHttps = url.protocol === "https:";
|
|
@@ -259,6 +301,7 @@ async function sendRawHttpRequest(url, body, headers, timeoutMs, redirectCount)
|
|
|
259
301
|
return;
|
|
260
302
|
settled = true;
|
|
261
303
|
clearTimeout(timeout);
|
|
304
|
+
signal?.removeEventListener("abort", onAbort);
|
|
262
305
|
fn();
|
|
263
306
|
};
|
|
264
307
|
const timeout = setTimeout(() => {
|
|
@@ -267,6 +310,16 @@ async function sendRawHttpRequest(url, body, headers, timeoutMs, redirectCount)
|
|
|
267
310
|
reject(new Error(`Request timed out after ${timeoutMs}ms`));
|
|
268
311
|
});
|
|
269
312
|
}, timeoutMs);
|
|
313
|
+
// Wire the caller's AbortSignal through to the raw socket (F2.1)
|
|
314
|
+
const onAbort = () => {
|
|
315
|
+
if (settled)
|
|
316
|
+
return;
|
|
317
|
+
settle(() => {
|
|
318
|
+
socket.destroy();
|
|
319
|
+
reject(new DOMException("The operation was aborted.", "AbortError"));
|
|
320
|
+
});
|
|
321
|
+
};
|
|
322
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
270
323
|
const releaseOrDestroy = () => {
|
|
271
324
|
if (responseConnectionClose) {
|
|
272
325
|
socket.destroy();
|
|
@@ -369,7 +422,8 @@ async function sendRawHttpRequest(url, body, headers, timeoutMs, redirectCount)
|
|
|
369
422
|
typeof location === "string" &&
|
|
370
423
|
location.length > 0) {
|
|
371
424
|
if (redirectCount >= 5) {
|
|
372
|
-
|
|
425
|
+
// Strip the query string — it may contain API keys (e.g. `?key=...`)
|
|
426
|
+
const text = `Redirect limit exceeded for ${url.origin}${url.pathname}`;
|
|
373
427
|
return {
|
|
374
428
|
ok: false,
|
|
375
429
|
status: response.status,
|
|
@@ -389,7 +443,7 @@ async function sendRawHttpRequest(url, body, headers, timeoutMs, redirectCount)
|
|
|
389
443
|
const safeHeaders = sameOrigin
|
|
390
444
|
? headers
|
|
391
445
|
: Object.fromEntries(Object.entries(headers).filter(([key]) => key.toLowerCase() !== "authorization"));
|
|
392
|
-
return sendRawHttpRequest(redirectUrl, body, safeHeaders, timeoutMs, redirectCount + 1);
|
|
446
|
+
return sendRawHttpRequest(redirectUrl, body, safeHeaders, timeoutMs, redirectCount + 1, signal);
|
|
393
447
|
}
|
|
394
448
|
const text = response.body.toString("utf8");
|
|
395
449
|
return {
|
|
@@ -421,7 +475,7 @@ export async function postJson(urlString, body, headers, timeoutMs, proxy, signa
|
|
|
421
475
|
const url = new URL(urlString);
|
|
422
476
|
const bypassProxy = isLocalhost(url.hostname) || matchesNoProxy(url.hostname, proxy?.noProxy);
|
|
423
477
|
if (bypassProxy || !proxy?.url) {
|
|
424
|
-
return directRequest(url, body, headers, timeoutMs);
|
|
478
|
+
return directRequest(url, body, headers, timeoutMs, 0, signal);
|
|
425
479
|
}
|
|
426
480
|
return postJsonViaFetch(urlString, body, headers, timeoutMs, proxy, signal);
|
|
427
481
|
}
|
|
@@ -12,6 +12,7 @@ import type { ProxyConfig } from "../core/config.js";
|
|
|
12
12
|
* @param timeoutMs - Request timeout in milliseconds (default 120000)
|
|
13
13
|
* @param proxy - Optional proxy configuration
|
|
14
14
|
* @param logLevel - Optional logging level for debug output
|
|
15
|
+
* @param keepAlive - Optional Ollama keep_alive value sent with /api/embed requests (e.g. "-1")
|
|
15
16
|
*/
|
|
16
17
|
export declare class OllamaProvider implements EmbeddingProvider {
|
|
17
18
|
readonly name = "ollama";
|
|
@@ -21,7 +22,8 @@ export declare class OllamaProvider implements EmbeddingProvider {
|
|
|
21
22
|
private readonly timeoutMs;
|
|
22
23
|
private proxy?;
|
|
23
24
|
private readonly logLevel?;
|
|
24
|
-
|
|
25
|
+
private readonly keepAlive?;
|
|
26
|
+
constructor(baseUrl: string, model: string, apiKey?: string, timeoutMs?: number, proxy?: ProxyConfig, logLevel?: string, keepAlive?: string);
|
|
25
27
|
private getLogFilePath;
|
|
26
28
|
private debug;
|
|
27
29
|
embed(texts: string[], _purpose?: "query" | "document"): Promise<number[][]>;
|
package/dist/embedder/ollama.js
CHANGED
|
@@ -10,6 +10,7 @@ import { appendDebugLog } from "../core/fileLogger.js";
|
|
|
10
10
|
* @param timeoutMs - Request timeout in milliseconds (default 120000)
|
|
11
11
|
* @param proxy - Optional proxy configuration
|
|
12
12
|
* @param logLevel - Optional logging level for debug output
|
|
13
|
+
* @param keepAlive - Optional Ollama keep_alive value sent with /api/embed requests (e.g. "-1")
|
|
13
14
|
*/
|
|
14
15
|
export class OllamaProvider {
|
|
15
16
|
name = "ollama";
|
|
@@ -19,13 +20,15 @@ export class OllamaProvider {
|
|
|
19
20
|
timeoutMs;
|
|
20
21
|
proxy;
|
|
21
22
|
logLevel;
|
|
22
|
-
|
|
23
|
+
keepAlive;
|
|
24
|
+
constructor(baseUrl, model, apiKey, timeoutMs = 120000, proxy, logLevel, keepAlive) {
|
|
23
25
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
24
26
|
this.model = model;
|
|
25
27
|
this.apiKey = apiKey;
|
|
26
28
|
this.timeoutMs = timeoutMs;
|
|
27
29
|
this.proxy = proxy;
|
|
28
30
|
this.logLevel = logLevel;
|
|
31
|
+
this.keepAlive = keepAlive;
|
|
29
32
|
}
|
|
30
33
|
getLogFilePath() {
|
|
31
34
|
return path.resolve(process.cwd(), ".opencode", "opencode-rag.log");
|
|
@@ -44,7 +47,14 @@ export class OllamaProvider {
|
|
|
44
47
|
}
|
|
45
48
|
this.debug(`OllamaProvider requesting ${texts.length} embedding vector${texts.length === 1 ? "" : "s"}`);
|
|
46
49
|
try {
|
|
47
|
-
const
|
|
50
|
+
const body = {
|
|
51
|
+
model: this.model,
|
|
52
|
+
input: texts.length === 1 ? texts[0] : texts,
|
|
53
|
+
};
|
|
54
|
+
if (this.keepAlive) {
|
|
55
|
+
body.keep_alive = this.keepAlive;
|
|
56
|
+
}
|
|
57
|
+
const response = await postJson(`${this.baseUrl}/embed`, body, headers, this.timeoutMs, this.proxy);
|
|
48
58
|
if (!response.ok) {
|
|
49
59
|
const body = await response.text();
|
|
50
60
|
throw new Error(`Ollama embedding failed (${response.status}): ${body}`);
|
|
@@ -15,6 +15,13 @@ export function createSessionLogger(storePath) {
|
|
|
15
15
|
const info = props.info;
|
|
16
16
|
if (!info || info.role !== "assistant")
|
|
17
17
|
return;
|
|
18
|
+
// message.updated fires repeatedly DURING streaming — only record
|
|
19
|
+
// completed messages (time.completed/finish/error set), otherwise
|
|
20
|
+
// every token chunk triggers a synchronous disk write on the
|
|
21
|
+
// event hot path and the log fills with redundant entries.
|
|
22
|
+
const isFinal = !!info.time?.completed || !!info.finish || !!info.error;
|
|
23
|
+
if (!isFinal)
|
|
24
|
+
return;
|
|
18
25
|
const ev = {
|
|
19
26
|
ts: Date.now(),
|
|
20
27
|
event: "message",
|
package/dist/eval/storage.js
CHANGED
|
@@ -19,6 +19,10 @@ export function validateSessionID(sessionID) {
|
|
|
19
19
|
/** Append a single event to a session's JSONL log file. Creates the directory and file if needed. */
|
|
20
20
|
export function appendSessionEvent(storePath, event) {
|
|
21
21
|
try {
|
|
22
|
+
// Guard against path traversal / malformed IDs — never write outside the
|
|
23
|
+
// eval directory.
|
|
24
|
+
if (!validateSessionID(event.sessionID))
|
|
25
|
+
return;
|
|
22
26
|
const dir = getEvalDir(storePath);
|
|
23
27
|
mkdirSync(dir, { recursive: true });
|
|
24
28
|
const filePath = getSessionPath(storePath, event.sessionID);
|
|
@@ -31,6 +35,8 @@ export function appendSessionEvent(storePath, event) {
|
|
|
31
35
|
/** Read all events for a session from its JSONL log file. Returns an empty array if the file does not exist or cannot be read. */
|
|
32
36
|
export function readSessionEvents(storePath, sessionID) {
|
|
33
37
|
try {
|
|
38
|
+
if (!validateSessionID(sessionID))
|
|
39
|
+
return [];
|
|
34
40
|
const filePath = getSessionPath(storePath, sessionID);
|
|
35
41
|
const content = readFileSync(filePath, "utf8");
|
|
36
42
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
@@ -58,6 +64,8 @@ export function listSessionIDs(storePath) {
|
|
|
58
64
|
/** Delete a session's JSONL log file from disk. Silently succeeds if the file does not exist. */
|
|
59
65
|
export function deleteSession(storePath, sessionID) {
|
|
60
66
|
try {
|
|
67
|
+
if (!validateSessionID(sessionID))
|
|
68
|
+
return;
|
|
61
69
|
const filePath = getSessionPath(storePath, sessionID);
|
|
62
70
|
if (existsSync(filePath)) {
|
|
63
71
|
unlinkSync(filePath);
|
|
@@ -23,7 +23,7 @@ export declare function getCurrentCommit(cwd: string): string | null;
|
|
|
23
23
|
*
|
|
24
24
|
* @param cwd - A path inside the repository.
|
|
25
25
|
* @param fromCommit - The commit SHA to diff against.
|
|
26
|
-
* @returns A diff result, or `null` if the git command fails.
|
|
26
|
+
* @returns A diff result, or `null` if the git command fails or the commit is invalid.
|
|
27
27
|
*/
|
|
28
28
|
export declare function getChangedFilesSince(cwd: string, fromCommit: string): GitDiffResult | null;
|
|
29
29
|
/**
|
package/dist/indexer/git-diff.js
CHANGED
|
@@ -45,9 +45,13 @@ export function getCurrentCommit(cwd) {
|
|
|
45
45
|
*
|
|
46
46
|
* @param cwd - A path inside the repository.
|
|
47
47
|
* @param fromCommit - The commit SHA to diff against.
|
|
48
|
-
* @returns A diff result, or `null` if the git command fails.
|
|
48
|
+
* @returns A diff result, or `null` if the git command fails or the commit is invalid.
|
|
49
49
|
*/
|
|
50
50
|
export function getChangedFilesSince(cwd, fromCommit) {
|
|
51
|
+
// The commit value originates from a manifest file — validate it strictly
|
|
52
|
+
// so a crafted manifest can never inject git options via argv.
|
|
53
|
+
if (!/^[0-9a-f]{7,40}$/i.test(fromCommit))
|
|
54
|
+
return null;
|
|
51
55
|
try {
|
|
52
56
|
const changedRaw = execFileSync("git", ["diff", "--name-only", "--diff-filter=ACMRT", fromCommit, "HEAD"], {
|
|
53
57
|
cwd, encoding: "utf-8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"],
|