opencode-rag-plugin 1.15.1 → 1.17.2
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 -9
- package/dist/api.js +44 -24
- package/dist/chunker/base.d.ts +34 -3
- package/dist/chunker/base.js +70 -21
- package/dist/chunker/factory.d.ts +4 -1
- package/dist/chunker/factory.js +12 -1
- package/dist/chunker/grammar.js +3 -0
- package/dist/chunker/image.js +8 -8
- package/dist/chunker/pdf.js +11 -13
- package/dist/chunker/xml.d.ts +2 -0
- package/dist/chunker/xml.js +2 -0
- package/dist/cli/commands/index-command.js +0 -4
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/init-helpers.d.ts +12 -0
- package/dist/cli/commands/init-helpers.js +87 -2
- package/dist/cli/commands/init.js +20 -2
- package/dist/cli/commands/query.js +1 -0
- package/dist/cli/commands/setup.d.ts +2 -0
- package/dist/cli/commands/setup.js +113 -0
- package/dist/cli/commands/status.js +64 -2
- package/dist/cli/commands/update.d.ts +4 -6
- package/dist/cli/commands/update.js +44 -58
- package/dist/cli/index.js +2 -1
- package/dist/content/image.js +24 -3
- package/dist/content/reader.d.ts +6 -1
- package/dist/content/reader.js +49 -4
- package/dist/core/config.d.ts +29 -1
- package/dist/core/config.js +105 -8
- package/dist/core/desc-cache.d.ts +31 -0
- package/dist/core/desc-cache.js +124 -0
- package/dist/core/interfaces.d.ts +58 -3
- package/dist/core/manifest.d.ts +24 -1
- package/dist/core/manifest.js +34 -3
- package/dist/core/resolve-api-key.js +4 -2
- package/dist/core/runtime-overrides.js +12 -8
- package/dist/core/setup-runtime.d.ts +23 -0
- package/dist/core/setup-runtime.js +231 -0
- package/dist/core/version-check.d.ts +85 -0
- package/dist/core/version-check.js +160 -0
- package/dist/describer/anthropic.d.ts +2 -2
- package/dist/describer/anthropic.js +5 -7
- package/dist/describer/describer.d.ts +2 -2
- package/dist/describer/describer.js +6 -8
- package/dist/describer/gemini.d.ts +2 -2
- package/dist/describer/gemini.js +5 -7
- package/dist/embedder/cohere.d.ts +1 -1
- package/dist/embedder/cohere.js +2 -2
- package/dist/embedder/factory.d.ts +3 -1
- package/dist/embedder/factory.js +8 -2
- package/dist/embedder/health.js +5 -1
- package/dist/embedder/http.d.ts +1 -1
- package/dist/embedder/http.js +74 -43
- package/dist/embedder/ollama.d.ts +1 -1
- package/dist/embedder/ollama.js +2 -2
- package/dist/embedder/openai.d.ts +1 -1
- package/dist/embedder/openai.js +2 -2
- package/dist/eval/compare-merge.d.ts +10 -0
- package/dist/eval/compare-merge.js +537 -0
- package/dist/eval/compare-rankings.d.ts +10 -0
- package/dist/eval/compare-rankings.js +245 -0
- package/dist/eval/dump-descriptions.d.ts +7 -0
- package/dist/eval/dump-descriptions.js +58 -0
- package/dist/eval/fast-index.d.ts +8 -0
- package/dist/eval/fast-index.js +283 -0
- package/dist/eval/run-branch-compare.d.ts +7 -0
- package/dist/eval/run-branch-compare.js +220 -0
- package/dist/eval/run-token-test.js +1 -0
- package/dist/eval/test-kw.d.ts +1 -0
- package/dist/eval/test-kw.js +22 -0
- package/dist/eval/update-descriptions.d.ts +7 -0
- package/dist/eval/update-descriptions.js +84 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/indexer/embed-stage.js +2 -1
- package/dist/indexer/git-diff.js +21 -9
- package/dist/indexer/pipeline.d.ts +6 -0
- package/dist/indexer/pipeline.js +304 -40
- package/dist/indexer/watch.js +1 -3
- package/dist/indexer/worker.d.ts +15 -2
- package/dist/indexer/worker.js +25 -12
- package/dist/mcp/handlers.d.ts +9 -0
- package/dist/mcp/handlers.js +23 -6
- package/dist/mcp/server.js +2 -0
- package/dist/opencode/create-read-tool.d.ts +2 -0
- package/dist/opencode/create-read-tool.js +8 -2
- package/dist/opencode/read-fallback.d.ts +1 -5
- package/dist/opencode/read-fallback.js +1 -18
- package/dist/opencode/read-format.js +5 -3
- package/dist/opencode/tools.js +5 -7
- package/dist/plugin.js +223 -71
- package/dist/retriever/keyword-index.d.ts +3 -2
- package/dist/retriever/keyword-index.js +25 -1
- package/dist/retriever/retriever.d.ts +4 -1
- package/dist/retriever/retriever.js +34 -56
- package/dist/tui.js +18 -1
- package/dist/vectorstore/lancedb.d.ts +35 -4
- package/dist/vectorstore/lancedb.js +146 -23
- package/dist/vectorstore/memory.d.ts +6 -1
- package/dist/vectorstore/memory.js +58 -0
- package/dist/watcher.d.ts +2 -0
- package/dist/watcher.js +5 -1
- package/dist/web/api.js +10 -2
- package/dist/web/server.js +18 -3
- package/package.json +8 -9
- package/scripts/postinstall-setup.js +82 -0
- package/dist/api.js.map +0 -1
- package/dist/chunker/base.js.map +0 -1
- package/dist/chunker/bash.js.map +0 -1
- package/dist/chunker/c.js.map +0 -1
- package/dist/chunker/cpp.js.map +0 -1
- package/dist/chunker/csharp.js.map +0 -1
- package/dist/chunker/css.js.map +0 -1
- package/dist/chunker/doc.js.map +0 -1
- package/dist/chunker/dockerfile.js.map +0 -1
- package/dist/chunker/docx.js.map +0 -1
- package/dist/chunker/excel.js.map +0 -1
- package/dist/chunker/factory.js.map +0 -1
- package/dist/chunker/fallback.js.map +0 -1
- package/dist/chunker/go.js.map +0 -1
- package/dist/chunker/grammar.js.map +0 -1
- package/dist/chunker/html.js.map +0 -1
- package/dist/chunker/image.js.map +0 -1
- package/dist/chunker/ini.js.map +0 -1
- package/dist/chunker/java.js.map +0 -1
- package/dist/chunker/javascript.js.map +0 -1
- package/dist/chunker/json.js.map +0 -1
- package/dist/chunker/kotlin.js.map +0 -1
- package/dist/chunker/loader.js.map +0 -1
- package/dist/chunker/markdown.js.map +0 -1
- package/dist/chunker/pdf.js.map +0 -1
- package/dist/chunker/php.js.map +0 -1
- package/dist/chunker/powershell.js.map +0 -1
- package/dist/chunker/python.js.map +0 -1
- package/dist/chunker/razor.js.map +0 -1
- package/dist/chunker/ruby.js.map +0 -1
- package/dist/chunker/rust.js.map +0 -1
- package/dist/chunker/sln.js.map +0 -1
- package/dist/chunker/sql.js.map +0 -1
- package/dist/chunker/ssl.js.map +0 -1
- package/dist/chunker/swift.js.map +0 -1
- package/dist/chunker/tex.js.map +0 -1
- package/dist/chunker/toml.js.map +0 -1
- package/dist/chunker/typescript.js.map +0 -1
- package/dist/chunker/uuid.js.map +0 -1
- package/dist/chunker/xml.js.map +0 -1
- package/dist/chunker/yaml.js.map +0 -1
- package/dist/cli/commands/clear.js.map +0 -1
- package/dist/cli/commands/describe-image.js.map +0 -1
- package/dist/cli/commands/dump.js.map +0 -1
- package/dist/cli/commands/eval.js.map +0 -1
- package/dist/cli/commands/index-command.js.map +0 -1
- package/dist/cli/commands/index.js.map +0 -1
- package/dist/cli/commands/init-helpers.js.map +0 -1
- package/dist/cli/commands/init.js.map +0 -1
- package/dist/cli/commands/list.js.map +0 -1
- package/dist/cli/commands/mcp.js.map +0 -1
- package/dist/cli/commands/query.js.map +0 -1
- package/dist/cli/commands/show.js.map +0 -1
- package/dist/cli/commands/status.js.map +0 -1
- package/dist/cli/commands/ui.js.map +0 -1
- package/dist/cli/commands/update.js.map +0 -1
- package/dist/cli/format.js.map +0 -1
- package/dist/cli/helpers.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/progress.d.ts +0 -42
- package/dist/cli/progress.js +0 -137
- package/dist/cli/progress.js.map +0 -1
- package/dist/cli/types.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/content/doc.js.map +0 -1
- package/dist/content/docx.js.map +0 -1
- package/dist/content/excel.js.map +0 -1
- package/dist/content/image.js.map +0 -1
- package/dist/content/pdf.js.map +0 -1
- package/dist/content/reader.js.map +0 -1
- package/dist/content/types.js.map +0 -1
- package/dist/core/bootstrap.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/doc-progress.js.map +0 -1
- package/dist/core/fileLogger.js.map +0 -1
- package/dist/core/interfaces.js.map +0 -1
- package/dist/core/manifest.js.map +0 -1
- package/dist/core/provider-defaults.js.map +0 -1
- package/dist/core/rag-injection-flag.js.map +0 -1
- package/dist/core/resolve-api-key.js.map +0 -1
- package/dist/core/runtime-overrides.js.map +0 -1
- package/dist/describer/anthropic.js.map +0 -1
- package/dist/describer/describer.js.map +0 -1
- package/dist/describer/factory.js.map +0 -1
- package/dist/describer/gemini.js.map +0 -1
- package/dist/describer/shared.js.map +0 -1
- package/dist/embedder/cohere.js.map +0 -1
- package/dist/embedder/factory.js.map +0 -1
- package/dist/embedder/health.js.map +0 -1
- package/dist/embedder/http.js.map +0 -1
- package/dist/embedder/ollama.js.map +0 -1
- package/dist/embedder/openai.js.map +0 -1
- package/dist/eval/index.js.map +0 -1
- package/dist/eval/run-token-test.js.map +0 -1
- package/dist/eval/session-logger.js.map +0 -1
- package/dist/eval/storage.js.map +0 -1
- package/dist/eval/token-analysis.js.map +0 -1
- package/dist/eval/token-counter.js.map +0 -1
- package/dist/eval/types.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/indexer/description-stage.js.map +0 -1
- package/dist/indexer/embed-stage.js.map +0 -1
- package/dist/indexer/git-diff.js.map +0 -1
- package/dist/indexer/metadata.js.map +0 -1
- package/dist/indexer/pipeline.js.map +0 -1
- package/dist/indexer/stats.js.map +0 -1
- package/dist/indexer/watch.js.map +0 -1
- package/dist/indexer/worker.js.map +0 -1
- package/dist/indexer.js.map +0 -1
- package/dist/mcp/cli.js.map +0 -1
- package/dist/mcp/handlers.js.map +0 -1
- package/dist/mcp/server.js.map +0 -1
- package/dist/opencode/create-read-tool.js.map +0 -1
- package/dist/opencode/read-fallback.js.map +0 -1
- package/dist/opencode/read-format.js.map +0 -1
- package/dist/opencode/read-query.js.map +0 -1
- package/dist/opencode/tool-args.js.map +0 -1
- package/dist/opencode/tools.js.map +0 -1
- package/dist/plugin-entry.js.map +0 -1
- package/dist/plugin.js.map +0 -1
- package/dist/retriever/context-optimizer.js.map +0 -1
- package/dist/retriever/keyword-index.js.map +0 -1
- package/dist/retriever/retriever.js.map +0 -1
- package/dist/tui.js.map +0 -1
- package/dist/updater.d.ts +0 -45
- package/dist/updater.js +0 -175
- package/dist/updater.js.map +0 -1
- package/dist/vectorstore/factory.js.map +0 -1
- package/dist/vectorstore/lancedb.js.map +0 -1
- package/dist/vectorstore/memory.js.map +0 -1
- package/dist/watcher.js.map +0 -1
- package/dist/web/api.js.map +0 -1
- package/dist/web/server.js.map +0 -1
- package/dist/web/static.js.map +0 -1
package/dist/core/manifest.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @fileoverview File hash manifest for tracking indexing state across sessions.
|
|
3
3
|
* Provides load/save with atomic writes, schema versioning, and corruption detection.
|
|
4
4
|
*/
|
|
5
|
+
import type { RagConfig } from "./config.js";
|
|
5
6
|
/** Metadata entry for a single indexed file. */
|
|
6
7
|
export interface ManifestEntry {
|
|
7
8
|
/** SHA-256 hash of the file content. */
|
|
@@ -16,9 +17,22 @@ export interface ManifestEntry {
|
|
|
16
17
|
size?: number;
|
|
17
18
|
/** Whether the description generation step failed for this file. */
|
|
18
19
|
descriptionFailed?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Hash of the description config (provider, model, baseUrl, prompts) that
|
|
22
|
+
* was active when descriptions were generated for this file. Skipped when
|
|
23
|
+
* no description provider is configured. Used to avoid re-describing
|
|
24
|
+
* files whose source content and description config are both unchanged.
|
|
25
|
+
*/
|
|
26
|
+
descHash?: string;
|
|
19
27
|
}
|
|
20
28
|
/** Current schema version for manifest files. */
|
|
21
|
-
export declare const SCHEMA_VERSION =
|
|
29
|
+
export declare const SCHEMA_VERSION = 3;
|
|
30
|
+
/**
|
|
31
|
+
* Oldest schema version still accepted as valid.
|
|
32
|
+
* Bumping this intentionally marks older manifests corrupt and forces a
|
|
33
|
+
* rebuild, so only raise it when a migration is not practical.
|
|
34
|
+
*/
|
|
35
|
+
export declare const MIN_SUPPORTED_SCHEMA_VERSION = 3;
|
|
22
36
|
/** Persistent manifest tracking indexing state across sessions. */
|
|
23
37
|
export interface FileManifest {
|
|
24
38
|
/** Unix timestamp of the last full index pass. */
|
|
@@ -49,6 +63,15 @@ export declare function manifestPathFor(dbPath: string): string;
|
|
|
49
63
|
export declare function normalizeFilePath(filePath: string): string;
|
|
50
64
|
/** Compute the SHA-256 hex hash of a string. */
|
|
51
65
|
export declare function computeFileHash(content: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Compute a hash of the description configuration (provider, model, baseUrl,
|
|
68
|
+
* systemPrompt, image prompt, etc.) so that files are only re-described when
|
|
69
|
+
* the config actually changes.
|
|
70
|
+
*
|
|
71
|
+
* Returns `undefined` when neither `description` nor `imageDescription` is
|
|
72
|
+
* configured — callers treat this as "no descriptions needed".
|
|
73
|
+
*/
|
|
74
|
+
export declare function computeDescriptionConfigHash(config: RagConfig): string | undefined;
|
|
52
75
|
/** Load a manifest from disk, handling missing or corrupt files gracefully. */
|
|
53
76
|
export declare function loadManifest(dbPath: string): Promise<LoadedManifest>;
|
|
54
77
|
/** Persist a manifest to disk with atomic write (temp file + rename, with Windows fallback). */
|
package/dist/core/manifest.js
CHANGED
|
@@ -6,7 +6,13 @@ import { createHash } from "node:crypto";
|
|
|
6
6
|
import fs from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
/** Current schema version for manifest files. */
|
|
9
|
-
export const SCHEMA_VERSION =
|
|
9
|
+
export const SCHEMA_VERSION = 3;
|
|
10
|
+
/**
|
|
11
|
+
* Oldest schema version still accepted as valid.
|
|
12
|
+
* Bumping this intentionally marks older manifests corrupt and forces a
|
|
13
|
+
* rebuild, so only raise it when a migration is not practical.
|
|
14
|
+
*/
|
|
15
|
+
export const MIN_SUPPORTED_SCHEMA_VERSION = 3;
|
|
10
16
|
/** Create a new empty manifest with the current schema version. */
|
|
11
17
|
export function createEmptyManifest() {
|
|
12
18
|
return { files: {}, schemaVersion: SCHEMA_VERSION };
|
|
@@ -23,6 +29,30 @@ export function normalizeFilePath(filePath) {
|
|
|
23
29
|
export function computeFileHash(content) {
|
|
24
30
|
return createHash("sha256").update(content).digest("hex");
|
|
25
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Compute a hash of the description configuration (provider, model, baseUrl,
|
|
34
|
+
* systemPrompt, image prompt, etc.) so that files are only re-described when
|
|
35
|
+
* the config actually changes.
|
|
36
|
+
*
|
|
37
|
+
* Returns `undefined` when neither `description` nor `imageDescription` is
|
|
38
|
+
* configured — callers treat this as "no descriptions needed".
|
|
39
|
+
*/
|
|
40
|
+
export function computeDescriptionConfigHash(config) {
|
|
41
|
+
const desc = config.description;
|
|
42
|
+
const img = config.imageDescription;
|
|
43
|
+
if (!desc && !img)
|
|
44
|
+
return undefined;
|
|
45
|
+
const parts = [];
|
|
46
|
+
if (desc) {
|
|
47
|
+
parts.push(`desc:${desc.provider}|${desc.model}|${desc.baseUrl}|${desc.systemPrompt}`);
|
|
48
|
+
}
|
|
49
|
+
if (img) {
|
|
50
|
+
parts.push(`img:${img.provider}|${img.model}|${img.baseUrl}|${img.prompt}`);
|
|
51
|
+
}
|
|
52
|
+
if (parts.length === 0)
|
|
53
|
+
return undefined;
|
|
54
|
+
return createHash("sha256").update(parts.join("||")).digest("hex").slice(0, 16);
|
|
55
|
+
}
|
|
26
56
|
/** Load a manifest from disk, handling missing or corrupt files gracefully. */
|
|
27
57
|
export async function loadManifest(dbPath) {
|
|
28
58
|
const filePath = manifestPathFor(dbPath);
|
|
@@ -32,14 +62,15 @@ export async function loadManifest(dbPath) {
|
|
|
32
62
|
if (!parsed || typeof parsed !== "object" || !parsed.files || typeof parsed.files !== "object") {
|
|
33
63
|
return { manifest: createEmptyManifest(), path: filePath, status: "corrupt" };
|
|
34
64
|
}
|
|
65
|
+
const version = parsed.schemaVersion ?? 0;
|
|
35
66
|
return {
|
|
36
67
|
manifest: {
|
|
37
68
|
lastIndexedAt: typeof parsed.lastIndexedAt === "number" ? parsed.lastIndexedAt : undefined,
|
|
38
|
-
schemaVersion:
|
|
69
|
+
schemaVersion: version,
|
|
39
70
|
files: parsed.files,
|
|
40
71
|
},
|
|
41
72
|
path: filePath,
|
|
42
|
-
status:
|
|
73
|
+
status: version >= MIN_SUPPORTED_SCHEMA_VERSION ? "ok" : "corrupt",
|
|
43
74
|
};
|
|
44
75
|
}
|
|
45
76
|
catch (error) {
|
|
@@ -41,7 +41,7 @@ function resolveForSection(provider, section, worktree) {
|
|
|
41
41
|
// so createEmbedder can throw a clear error about the missing key
|
|
42
42
|
}
|
|
43
43
|
function stripJsoncComments(text) {
|
|
44
|
-
return text.replace(
|
|
44
|
+
return text.replace(/("[^"\\]*(?:\\.[^"\\]*)*")|(\/\/[^\n]*|\/\*[\s\S]*?\*\/)/g, (_, string) => string ?? "");
|
|
45
45
|
}
|
|
46
46
|
function readOpenCodeProviderKey(worktree, providerId) {
|
|
47
47
|
const locations = [
|
|
@@ -57,7 +57,9 @@ function readOpenCodeProviderKey(worktree, providerId) {
|
|
|
57
57
|
if (!existsSync(loc))
|
|
58
58
|
continue;
|
|
59
59
|
const raw = readFileSync(loc, "utf-8");
|
|
60
|
-
|
|
60
|
+
// Only strip JSONC comments from .jsonc files; plain JSON may contain
|
|
61
|
+
// "https://" URLs that look like line-comment markers after "//".
|
|
62
|
+
const cleaned = loc.endsWith(".jsonc") ? stripJsoncComments(raw) : raw;
|
|
61
63
|
const config = JSON.parse(cleaned);
|
|
62
64
|
const providerSection = config.provider;
|
|
63
65
|
if (!providerSection)
|
|
@@ -11,7 +11,9 @@ export function loadRuntimeOverrides(storePath) {
|
|
|
11
11
|
if (!existsSync(overridePath))
|
|
12
12
|
return {};
|
|
13
13
|
try {
|
|
14
|
-
|
|
14
|
+
const raw = readFileSync(overridePath, "utf-8");
|
|
15
|
+
const stripped = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
|
|
16
|
+
return JSON.parse(stripped);
|
|
15
17
|
}
|
|
16
18
|
catch {
|
|
17
19
|
return {};
|
|
@@ -80,41 +82,43 @@ export function applyRuntimeOverrides(cfg, overrides) {
|
|
|
80
82
|
merged.embedding.baseUrl = overrides.embedding.baseUrl;
|
|
81
83
|
}
|
|
82
84
|
if (overrides.description) {
|
|
85
|
+
const defaultDesc = DEFAULT_CONFIG.description;
|
|
83
86
|
if (overrides.description.enabled !== undefined) {
|
|
84
87
|
if (!merged.description)
|
|
85
|
-
merged.description = {
|
|
88
|
+
merged.description = { ...defaultDesc };
|
|
86
89
|
merged.description.enabled = overrides.description.enabled;
|
|
87
90
|
}
|
|
88
91
|
if (overrides.description.provider !== undefined) {
|
|
89
92
|
if (!merged.description)
|
|
90
|
-
merged.description = {
|
|
93
|
+
merged.description = { ...defaultDesc };
|
|
91
94
|
merged.description.provider = overrides.description.provider;
|
|
92
95
|
}
|
|
93
96
|
if (overrides.description.model !== undefined) {
|
|
94
97
|
if (!merged.description)
|
|
95
|
-
merged.description = {
|
|
98
|
+
merged.description = { ...defaultDesc };
|
|
96
99
|
merged.description.model = overrides.description.model;
|
|
97
100
|
}
|
|
98
101
|
if (overrides.description.baseUrl !== undefined) {
|
|
99
102
|
if (!merged.description)
|
|
100
|
-
merged.description = {
|
|
103
|
+
merged.description = { ...defaultDesc };
|
|
101
104
|
merged.description.baseUrl = overrides.description.baseUrl;
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
if (overrides.imageDescription) {
|
|
108
|
+
const defaultImg = DEFAULT_CONFIG.imageDescription;
|
|
105
109
|
if (overrides.imageDescription.enabled !== undefined) {
|
|
106
110
|
if (!merged.imageDescription)
|
|
107
|
-
merged.imageDescription = {
|
|
111
|
+
merged.imageDescription = { ...defaultImg };
|
|
108
112
|
merged.imageDescription.enabled = overrides.imageDescription.enabled;
|
|
109
113
|
}
|
|
110
114
|
if (overrides.imageDescription.provider !== undefined) {
|
|
111
115
|
if (!merged.imageDescription)
|
|
112
|
-
merged.imageDescription = {
|
|
116
|
+
merged.imageDescription = { ...defaultImg };
|
|
113
117
|
merged.imageDescription.provider = overrides.imageDescription.provider;
|
|
114
118
|
}
|
|
115
119
|
if (overrides.imageDescription.model !== undefined) {
|
|
116
120
|
if (!merged.imageDescription)
|
|
117
|
-
merged.imageDescription = {
|
|
121
|
+
merged.imageDescription = { ...defaultImg };
|
|
118
122
|
merged.imageDescription.model = overrides.imageDescription.model;
|
|
119
123
|
}
|
|
120
124
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface SetupResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
errors: string[];
|
|
4
|
+
}
|
|
5
|
+
export declare function getRuntimeDir(): string;
|
|
6
|
+
export declare function getNpmGlobalRoot(): string;
|
|
7
|
+
export declare function getVersionFile(runtimeDir: string): string;
|
|
8
|
+
export declare function readVersionFile(versionFile: string): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* npm-generated .cmd / .ps1 wrappers on Windows invoke `.js` files directly
|
|
11
|
+
* via file association. If the `.js` association points to a text editor
|
|
12
|
+
* (e.g. Notepad++) instead of Node.js, the CLI opens the editor instead of
|
|
13
|
+
* running. This function patches the wrappers to call `node` explicitly.
|
|
14
|
+
*
|
|
15
|
+
* Runs only on Windows. Safe to call on every setup — detects already-patched
|
|
16
|
+
* wrappers by checking for the `node` prefix.
|
|
17
|
+
*/
|
|
18
|
+
export declare function patchWindowsWrappers(npmGlobalRoot: string): void;
|
|
19
|
+
export declare function setupRuntime(options?: {
|
|
20
|
+
force?: boolean;
|
|
21
|
+
silent?: boolean;
|
|
22
|
+
version?: string;
|
|
23
|
+
}): Promise<SetupResult>;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync, symlinkSync, lstatSync, } from "node:fs";
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
5
|
+
const PLUGIN_NAME = "opencode-rag-plugin";
|
|
6
|
+
export function getRuntimeDir() {
|
|
7
|
+
return path.join(os.homedir(), ".opencode");
|
|
8
|
+
}
|
|
9
|
+
export function getNpmGlobalRoot() {
|
|
10
|
+
return execSync("npm root -g", {
|
|
11
|
+
encoding: "utf-8",
|
|
12
|
+
timeout: 10_000,
|
|
13
|
+
}).trim();
|
|
14
|
+
}
|
|
15
|
+
function createJunction(targetPath, linkPath) {
|
|
16
|
+
const type = process.platform === "win32" ? "junction" : "dir";
|
|
17
|
+
symlinkSync(targetPath, linkPath, type);
|
|
18
|
+
}
|
|
19
|
+
function removeIfExists(targetPath) {
|
|
20
|
+
if (existsSync(targetPath)) {
|
|
21
|
+
rmSync(targetPath, { recursive: true, force: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function getVersionFile(runtimeDir) {
|
|
25
|
+
return path.join(runtimeDir, ".bundle-version");
|
|
26
|
+
}
|
|
27
|
+
export function readVersionFile(versionFile) {
|
|
28
|
+
try {
|
|
29
|
+
return readFileSync(versionFile, "utf-8").trim();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Bin name npm generates for this package (without .cmd/.ps1). */
|
|
36
|
+
function getBinName() {
|
|
37
|
+
const pkgJson = path.join(getNpmGlobalRoot(), PLUGIN_NAME, "package.json");
|
|
38
|
+
try {
|
|
39
|
+
const pkg = JSON.parse(readFileSync(pkgJson, "utf-8"));
|
|
40
|
+
if (pkg.bin && typeof pkg.bin === "object") {
|
|
41
|
+
return Object.keys(pkg.bin)[0] ?? PLUGIN_NAME;
|
|
42
|
+
}
|
|
43
|
+
if (typeof pkg.bin === "string") {
|
|
44
|
+
return PLUGIN_NAME;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// fall through
|
|
49
|
+
}
|
|
50
|
+
return PLUGIN_NAME;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* npm-generated .cmd / .ps1 wrappers on Windows invoke `.js` files directly
|
|
54
|
+
* via file association. If the `.js` association points to a text editor
|
|
55
|
+
* (e.g. Notepad++) instead of Node.js, the CLI opens the editor instead of
|
|
56
|
+
* running. This function patches the wrappers to call `node` explicitly.
|
|
57
|
+
*
|
|
58
|
+
* Runs only on Windows. Safe to call on every setup — detects already-patched
|
|
59
|
+
* wrappers by checking for the `node` prefix.
|
|
60
|
+
*/
|
|
61
|
+
export function patchWindowsWrappers(npmGlobalRoot) {
|
|
62
|
+
if (process.platform !== "win32")
|
|
63
|
+
return;
|
|
64
|
+
const binDir = path.resolve(npmGlobalRoot, "..");
|
|
65
|
+
const binName = getBinName();
|
|
66
|
+
// ── .cmd wrapper ──────────────────────────────────────────────
|
|
67
|
+
const cmdFile = path.join(binDir, `${binName}.cmd`);
|
|
68
|
+
if (existsSync(cmdFile)) {
|
|
69
|
+
const content = readFileSync(cmdFile, "utf-8");
|
|
70
|
+
// npm generates: "%dp0%\node_modules\opencode-rag-plugin\dist\cli\index.js" %*
|
|
71
|
+
// We want: node "%dp0%\node_modules\opencode-rag-plugin\dist\cli\index.js" %*
|
|
72
|
+
const cmdJsLine = /^(?!node\s)("[^"]+\.js"\s+%\*)$/m;
|
|
73
|
+
if (cmdJsLine.test(content)) {
|
|
74
|
+
const patched = content.replace(cmdJsLine, "node $1");
|
|
75
|
+
writeFileSync(cmdFile, patched, "utf-8");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ── PowerShell wrapper ────────────────────────────────────────
|
|
79
|
+
const ps1File = path.join(binDir, `${binName}.ps1`);
|
|
80
|
+
if (existsSync(ps1File)) {
|
|
81
|
+
let content = readFileSync(ps1File, "utf-8");
|
|
82
|
+
// Skip if already patched
|
|
83
|
+
if (content.includes('node "$basedir'))
|
|
84
|
+
return;
|
|
85
|
+
// npm generates:
|
|
86
|
+
// $input | & "$basedir/node_modules/.../index.js" $args
|
|
87
|
+
// & "$basedir/node_modules/.../index.js" $args
|
|
88
|
+
// Replace to: & node "$basedir/.../index.js" $args
|
|
89
|
+
const ps1DirectLine = /(&\s+)("\$basedir\/[^"]+\.js"\s+\$args)/g;
|
|
90
|
+
if (ps1DirectLine.test(content)) {
|
|
91
|
+
content = content.replace(ps1DirectLine, "$1node $2");
|
|
92
|
+
writeFileSync(ps1File, content, "utf-8");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export async function setupRuntime(options) {
|
|
97
|
+
const errors = [];
|
|
98
|
+
const pluginVersion = options?.version || process.env.OPCODE_RAG_VERSION || "0.0.0";
|
|
99
|
+
const runtimeDir = getRuntimeDir();
|
|
100
|
+
const versionFile = getVersionFile(runtimeDir);
|
|
101
|
+
const runtimePluginDir = path.join(runtimeDir, "node_modules", PLUGIN_NAME);
|
|
102
|
+
const runtimeSdkDir = path.join(runtimeDir, "node_modules", "@opencode-ai");
|
|
103
|
+
const runtimeSdkPluginDir = path.join(runtimeSdkDir, "plugin");
|
|
104
|
+
const installedVersion = readVersionFile(versionFile);
|
|
105
|
+
const runtimeDist = path.join(runtimePluginDir, "dist");
|
|
106
|
+
const alreadyInstalled = existsSync(runtimeDist);
|
|
107
|
+
if (alreadyInstalled && installedVersion === pluginVersion && !options?.force) {
|
|
108
|
+
return { success: true, errors: [] };
|
|
109
|
+
}
|
|
110
|
+
let npmGlobalRoot;
|
|
111
|
+
try {
|
|
112
|
+
npmGlobalRoot = getNpmGlobalRoot();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
errors.push("npm is not available on PATH. Cannot determine global package location.");
|
|
116
|
+
return { success: false, errors };
|
|
117
|
+
}
|
|
118
|
+
const globalPluginDir = path.join(npmGlobalRoot, PLUGIN_NAME);
|
|
119
|
+
const globalSdkPluginDir = path.join(npmGlobalRoot, "@opencode-ai", "plugin");
|
|
120
|
+
if (!existsSync(globalPluginDir)) {
|
|
121
|
+
errors.push(`Plugin not found at: ${globalPluginDir}`);
|
|
122
|
+
return { success: false, errors };
|
|
123
|
+
}
|
|
124
|
+
if (!existsSync(path.join(globalPluginDir, "dist", "cli.js"))) {
|
|
125
|
+
errors.push(`Global install seems incomplete: dist/ not found in ${globalPluginDir}`);
|
|
126
|
+
return { success: false, errors };
|
|
127
|
+
}
|
|
128
|
+
mkdirSync(runtimeDir, { recursive: true });
|
|
129
|
+
const runtimePkg = path.join(runtimeDir, "package.json");
|
|
130
|
+
if (!existsSync(runtimePkg)) {
|
|
131
|
+
writeFileSync(runtimePkg, JSON.stringify({ private: true, type: "module" }, null, 2) + "\n", "utf-8");
|
|
132
|
+
}
|
|
133
|
+
removeIfExists(runtimePluginDir);
|
|
134
|
+
// Ensure stale directory is fully gone before creating junction
|
|
135
|
+
let retries = 3;
|
|
136
|
+
while (retries > 0 && existsSync(runtimePluginDir)) {
|
|
137
|
+
try {
|
|
138
|
+
rmSync(runtimePluginDir, { recursive: true, force: true });
|
|
139
|
+
}
|
|
140
|
+
catch { /* retry */ }
|
|
141
|
+
retries--;
|
|
142
|
+
}
|
|
143
|
+
mkdirSync(path.dirname(runtimePluginDir), { recursive: true });
|
|
144
|
+
let junctionOk = false;
|
|
145
|
+
try {
|
|
146
|
+
createJunction(globalPluginDir, runtimePluginDir);
|
|
147
|
+
junctionOk = process.platform !== "win32";
|
|
148
|
+
if (process.platform === "win32") {
|
|
149
|
+
const stat = lstatSync(runtimePluginDir);
|
|
150
|
+
junctionOk = stat.isSymbolicLink();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// fall through to cpSync
|
|
155
|
+
}
|
|
156
|
+
if (!junctionOk) {
|
|
157
|
+
if (!options?.silent) {
|
|
158
|
+
console.error(" [warn] Junction not supported, falling back to copy...");
|
|
159
|
+
}
|
|
160
|
+
const { cpSync } = await import("node:fs");
|
|
161
|
+
cpSync(globalPluginDir, runtimePluginDir, { recursive: true });
|
|
162
|
+
}
|
|
163
|
+
// Ensure the @opencode-ai/plugin SDK is available globally.
|
|
164
|
+
// We must NOT run `npm install` inside the runtime dir because npm
|
|
165
|
+
// re-resolves all of node_modules/ and replaces the plugin junction
|
|
166
|
+
// with the published npm version (corrupting the local link).
|
|
167
|
+
if (!existsSync(globalSdkPluginDir)) {
|
|
168
|
+
try {
|
|
169
|
+
execSync(`npm install -g @opencode-ai/plugin`, {
|
|
170
|
+
stdio: "pipe",
|
|
171
|
+
timeout: 60_000,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (cause) {
|
|
175
|
+
errors.push(`Failed to install @opencode-ai/plugin SDK globally: ${cause.message}`);
|
|
176
|
+
return { success: false, errors };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Create junction from global SDK to runtime (same pattern as plugin above)
|
|
180
|
+
if (existsSync(globalSdkPluginDir)) {
|
|
181
|
+
removeIfExists(runtimeSdkPluginDir);
|
|
182
|
+
retries = 3;
|
|
183
|
+
while (retries > 0 && existsSync(runtimeSdkPluginDir)) {
|
|
184
|
+
try {
|
|
185
|
+
rmSync(runtimeSdkPluginDir, { recursive: true, force: true });
|
|
186
|
+
}
|
|
187
|
+
catch { /* retry */ }
|
|
188
|
+
retries--;
|
|
189
|
+
}
|
|
190
|
+
mkdirSync(runtimeSdkDir, { recursive: true });
|
|
191
|
+
let sdkJunctionOk = false;
|
|
192
|
+
try {
|
|
193
|
+
createJunction(globalSdkPluginDir, runtimeSdkPluginDir);
|
|
194
|
+
sdkJunctionOk = process.platform !== "win32";
|
|
195
|
+
if (process.platform === "win32") {
|
|
196
|
+
const stat = lstatSync(runtimeSdkPluginDir);
|
|
197
|
+
sdkJunctionOk = stat.isSymbolicLink();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// fall through to cpSync
|
|
202
|
+
}
|
|
203
|
+
if (!sdkJunctionOk) {
|
|
204
|
+
if (!options?.silent) {
|
|
205
|
+
console.error(" [warn] SDK junction not supported, falling back to copy...");
|
|
206
|
+
}
|
|
207
|
+
const { cpSync } = await import("node:fs");
|
|
208
|
+
cpSync(globalSdkPluginDir, runtimeSdkPluginDir, { recursive: true });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
errors.push(`@opencode-ai/plugin SDK not available after global install`);
|
|
213
|
+
return { success: false, errors };
|
|
214
|
+
}
|
|
215
|
+
writeFileSync(versionFile, pluginVersion, "utf-8");
|
|
216
|
+
patchWindowsWrappers(npmGlobalRoot);
|
|
217
|
+
const cliEntry = path.join(runtimePluginDir, "dist", "cli.js");
|
|
218
|
+
const pluginEntry = path.join(runtimePluginDir, "dist", "plugin-entry.js");
|
|
219
|
+
const sdkPkg = path.join(runtimeSdkPluginDir, "package.json");
|
|
220
|
+
const success = existsSync(cliEntry) && existsSync(pluginEntry) && existsSync(sdkPkg);
|
|
221
|
+
if (!success) {
|
|
222
|
+
if (!existsSync(cliEntry))
|
|
223
|
+
errors.push(`CLI entry missing: ${cliEntry}`);
|
|
224
|
+
if (!existsSync(pluginEntry))
|
|
225
|
+
errors.push(`Plugin entry missing: ${pluginEntry}`);
|
|
226
|
+
if (!existsSync(sdkPkg))
|
|
227
|
+
errors.push(`Plugin SDK missing: ${sdkPkg}`);
|
|
228
|
+
}
|
|
229
|
+
return { success, errors };
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=setup-runtime.js.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Version check and self-update functionality. Checks GitHub
|
|
3
|
+
* releases for new versions and installs the newest version via npm, then
|
|
4
|
+
* re-syncs the OpenCode runtime junctions.
|
|
5
|
+
*/
|
|
6
|
+
import { setupRuntime } from "./setup-runtime.js";
|
|
7
|
+
/** Information about an available update. */
|
|
8
|
+
export interface UpdateInfo {
|
|
9
|
+
/** The currently installed version string. */
|
|
10
|
+
currentVersion: string;
|
|
11
|
+
/** The latest available version on GitHub. */
|
|
12
|
+
latestVersion: string;
|
|
13
|
+
/** Whether a newer version than the current one exists. */
|
|
14
|
+
updateAvailable: boolean;
|
|
15
|
+
/** URL to the GitHub release page. */
|
|
16
|
+
releaseUrl: string;
|
|
17
|
+
/** ISO date string of when the release was published. */
|
|
18
|
+
publishedAt: string;
|
|
19
|
+
}
|
|
20
|
+
/** Result of an install-update attempt. */
|
|
21
|
+
export interface InstallUpdateResult {
|
|
22
|
+
/** Whether the install completed successfully. */
|
|
23
|
+
success: boolean;
|
|
24
|
+
/** Human-readable status message. */
|
|
25
|
+
message: string;
|
|
26
|
+
/** Version before the update. */
|
|
27
|
+
fromVersion: string;
|
|
28
|
+
/** Version after the update (undefined if the install failed). */
|
|
29
|
+
toVersion?: string;
|
|
30
|
+
}
|
|
31
|
+
/** Callable shape for the npm runner (a subset of execSync's signature). */
|
|
32
|
+
type NpmRunner = (command: string, options: {
|
|
33
|
+
stdio: "inherit" | "pipe";
|
|
34
|
+
timeout: number;
|
|
35
|
+
}) => unknown;
|
|
36
|
+
/**
|
|
37
|
+
* Read the current version from the package.json sitting next to this module.
|
|
38
|
+
*
|
|
39
|
+
* Resolves the package root via `import.meta.url` so it works both from the
|
|
40
|
+
* source tree and from the compiled `dist/` output. Returns `"0.0.0"` if the
|
|
41
|
+
* file cannot be read or parsed (best-effort — never throws).
|
|
42
|
+
*
|
|
43
|
+
* @returns The current package version string.
|
|
44
|
+
*/
|
|
45
|
+
export declare function getCurrentVersion(): string;
|
|
46
|
+
/**
|
|
47
|
+
* Compare two semver-ish strings.
|
|
48
|
+
* @returns 1 if a > b, -1 if a < b, 0 if equal.
|
|
49
|
+
*/
|
|
50
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
51
|
+
/**
|
|
52
|
+
* Check the GitHub releases API for a newer version of OpenCodeRAG.
|
|
53
|
+
*
|
|
54
|
+
* Uses a 5-second timeout; failures (network errors, non-OK responses, missing
|
|
55
|
+
* `tag_name`) are silently caught and reported as "no update available" so the
|
|
56
|
+
* caller never has to handle a rejection.
|
|
57
|
+
*
|
|
58
|
+
* @param currentVersion - The version string to compare against.
|
|
59
|
+
* @returns UpdateInfo indicating whether an update is available.
|
|
60
|
+
*/
|
|
61
|
+
export declare function checkForUpdate(currentVersion: string): Promise<UpdateInfo>;
|
|
62
|
+
/**
|
|
63
|
+
* Install the newest published version of OpenCodeRAG.
|
|
64
|
+
*
|
|
65
|
+
* Runs `npm install -g <package>@latest` to refresh the global install, then
|
|
66
|
+
* calls {@link setupRuntime} with `force: true` to re-create the
|
|
67
|
+
* `~/.opencode/node_modules/` junctions so OpenCode picks up the new build on
|
|
68
|
+
* the next restart.
|
|
69
|
+
*
|
|
70
|
+
* @param options - Optional verbosity flag. When `verbose` is true, npm output
|
|
71
|
+
* is streamed to the console; otherwise it is captured silently. The
|
|
72
|
+
* `_execSync` and `_setupRuntime` seams are for testing only.
|
|
73
|
+
* @returns An {@link InstallUpdateResult} with success status, message, and the
|
|
74
|
+
* from/to versions.
|
|
75
|
+
*/
|
|
76
|
+
export declare function installLatestUpdate(options?: {
|
|
77
|
+
verbose?: boolean;
|
|
78
|
+
/** Test seam: override the npm runner. */
|
|
79
|
+
_execSync?: NpmRunner;
|
|
80
|
+
/** Test seam: override the runtime sync. */
|
|
81
|
+
_setupRuntime?: typeof setupRuntime;
|
|
82
|
+
/** Test seam: override the version reader for the "to" version. */
|
|
83
|
+
_getCurrentVersion?: typeof getCurrentVersion;
|
|
84
|
+
}): Promise<InstallUpdateResult>;
|
|
85
|
+
export {};
|