@codemem/opencode-plugin 0.43.1 → 0.44.0-alpha.1
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/.opencode/lib/compat.js +43 -14
- package/.opencode/plugins/codemem.js +125 -29
- package/package.json +6 -3
package/.opencode/lib/compat.js
CHANGED
|
@@ -27,10 +27,20 @@ export const resolveUpgradeGuidance = ({ runner, runnerFrom }) => {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
if (normalizedRunner === "codemem") {
|
|
31
|
+
return {
|
|
32
|
+
mode: "global",
|
|
33
|
+
action:
|
|
34
|
+
"Run `npm install -g codemem @codemem/embeddings` to update both the CLI and the optional semantic runtime, then restart OpenCode. On Linux, prefix with `ONNXRUNTIME_NODE_INSTALL=skip` to avoid the unused GPU provider download.",
|
|
35
|
+
note: "detected global codemem runner mode",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
if (normalizedRunner === "npx") {
|
|
31
40
|
return {
|
|
32
41
|
mode: "npx",
|
|
33
|
-
action:
|
|
42
|
+
action:
|
|
43
|
+
"Run `npm install -g codemem @codemem/embeddings` to update the CLI and optional semantic runtime, then restart OpenCode. On Linux, prefix with `ONNXRUNTIME_NODE_INSTALL=skip` to avoid the unused GPU provider download.",
|
|
34
44
|
note: "detected npx runner mode",
|
|
35
45
|
};
|
|
36
46
|
}
|
|
@@ -106,9 +116,38 @@ const isPinnedGitSource = (runnerFrom) => {
|
|
|
106
116
|
}
|
|
107
117
|
};
|
|
108
118
|
|
|
109
|
-
|
|
119
|
+
const PUBLIC_NPM_REGISTRY = "https://registry.npmjs.org/";
|
|
120
|
+
|
|
121
|
+
const createNpmUpdatePlan = (targetVersion) => {
|
|
122
|
+
const command = [
|
|
123
|
+
"npm",
|
|
124
|
+
"install",
|
|
125
|
+
"-g",
|
|
126
|
+
"--registry",
|
|
127
|
+
PUBLIC_NPM_REGISTRY,
|
|
128
|
+
`--@codemem:registry=${PUBLIC_NPM_REGISTRY}`,
|
|
129
|
+
`codemem@${targetVersion}`,
|
|
130
|
+
`@codemem/embeddings@${targetVersion}`,
|
|
131
|
+
];
|
|
132
|
+
return { allowed: true, reason: null, command, commandText: command.join(" ") };
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const resolveAutoUpdatePlan = ({
|
|
136
|
+
runner,
|
|
137
|
+
runnerFrom,
|
|
138
|
+
runnerFromExplicit = false,
|
|
139
|
+
targetVersion = "latest",
|
|
140
|
+
platform = process.platform,
|
|
141
|
+
}) => {
|
|
110
142
|
const normalizedRunner = String(runner || "").trim();
|
|
111
143
|
const source = String(runnerFrom || "").trim();
|
|
144
|
+
const normalizedTargetVersion = String(targetVersion || "").trim();
|
|
145
|
+
if (platform === "win32") {
|
|
146
|
+
return { allowed: false, reason: "unsupported-platform", command: null, commandText: null };
|
|
147
|
+
}
|
|
148
|
+
if (normalizedTargetVersion !== "latest" && !/^\d+\.\d+\.\d+$/.test(normalizedTargetVersion)) {
|
|
149
|
+
return { allowed: false, reason: "invalid-version", command: null, commandText: null };
|
|
150
|
+
}
|
|
112
151
|
if (isPinnedGitSource(source) || /^codemem@(?!latest$|next$|\*$)[^\s]+$/i.test(source)) {
|
|
113
152
|
return {
|
|
114
153
|
allowed: false,
|
|
@@ -136,12 +175,7 @@ export const resolveAutoUpdatePlan = ({ runner, runnerFrom, runnerFromExplicit =
|
|
|
136
175
|
commandText: null,
|
|
137
176
|
};
|
|
138
177
|
}
|
|
139
|
-
return
|
|
140
|
-
allowed: true,
|
|
141
|
-
reason: null,
|
|
142
|
-
command: ["npm", "install", "-g", "codemem@latest"],
|
|
143
|
-
commandText: "npm install -g codemem@latest",
|
|
144
|
-
};
|
|
178
|
+
return createNpmUpdatePlan(normalizedTargetVersion);
|
|
145
179
|
}
|
|
146
180
|
|
|
147
181
|
if (normalizedRunner === "uv") {
|
|
@@ -163,12 +197,7 @@ export const resolveAutoUpdatePlan = ({ runner, runnerFrom, runnerFromExplicit =
|
|
|
163
197
|
}
|
|
164
198
|
|
|
165
199
|
if (normalizedRunner === "codemem") {
|
|
166
|
-
return
|
|
167
|
-
allowed: true,
|
|
168
|
-
reason: null,
|
|
169
|
-
command: ["npm", "install", "-g", "codemem@latest"],
|
|
170
|
-
commandText: "npm install -g codemem@latest",
|
|
171
|
-
};
|
|
200
|
+
return createNpmUpdatePlan(normalizedTargetVersion);
|
|
172
201
|
}
|
|
173
202
|
|
|
174
203
|
return { allowed: false, reason: "unknown-runner", command: null, commandText: null };
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
|
|
17
17
|
const TRUTHY_VALUES = ["1", "true", "yes"];
|
|
18
18
|
const DISABLED_VALUES = ["0", "false", "off"];
|
|
19
|
-
const PINNED_BACKEND_VERSION = "0.
|
|
19
|
+
const PINNED_BACKEND_VERSION = "0.44.0-alpha.1";
|
|
20
20
|
const COMPAT_CHECK_DELAY_MS = 1500;
|
|
21
21
|
const COMPAT_CHECK_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
22
22
|
const MAX_UPDATE_STATUS_BYTES = 16 * 1024;
|
|
@@ -34,6 +34,8 @@ const VIEWER_HEALTH_TIMEOUT_MS = 5_000;
|
|
|
34
34
|
const VIEWER_HEALTH_RESTART_THRESHOLD = 3;
|
|
35
35
|
const VIEWER_HEALTH_RESTART_COOLDOWN_MS = 5 * 60_000;
|
|
36
36
|
const RAW_EVENTS_STATUS_TIMEOUT_MS = 5_000;
|
|
37
|
+
const DEFAULT_EMBEDDING_MODEL = "Xenova/bge-small-en-v1.5";
|
|
38
|
+
const DEFAULT_EMBEDDING_REVISION = "ea104dacec62c0de699686887e3f920caeb4f3e3";
|
|
37
39
|
|
|
38
40
|
let compatCheckCache = null;
|
|
39
41
|
const notifiedReleaseVersions = new Set();
|
|
@@ -386,6 +388,14 @@ const parseReleaseNotification = (result) => {
|
|
|
386
388
|
}
|
|
387
389
|
};
|
|
388
390
|
|
|
391
|
+
const resolveAutoUpdateEnvironment = ({
|
|
392
|
+
platform = process.platform,
|
|
393
|
+
env = process.env,
|
|
394
|
+
} = {}) => {
|
|
395
|
+
if (platform !== "linux") return env;
|
|
396
|
+
return { ...env, ONNXRUNTIME_NODE_INSTALL: "skip" };
|
|
397
|
+
};
|
|
398
|
+
|
|
389
399
|
const createLogLine = (logPath) => async (line) => {
|
|
390
400
|
if (!logPath) {
|
|
391
401
|
return;
|
|
@@ -632,6 +642,17 @@ const buildViewerIdentityTarget = (env = process.env, cwd = process.cwd()) => {
|
|
|
632
642
|
: trimmed;
|
|
633
643
|
return resolve(cwd, expanded);
|
|
634
644
|
};
|
|
645
|
+
const embeddingModel = env.CODEMEM_EMBEDDING_MODEL || DEFAULT_EMBEDDING_MODEL;
|
|
646
|
+
const configuredEmbeddingRevision = env.CODEMEM_EMBEDDING_REVISION?.trim();
|
|
647
|
+
// Match core's configured request identity. The runtime resolves mutable refs
|
|
648
|
+
// to canonical commits before vectors are persisted.
|
|
649
|
+
let embeddingRevision;
|
|
650
|
+
if (configuredEmbeddingRevision) {
|
|
651
|
+
embeddingRevision = configuredEmbeddingRevision;
|
|
652
|
+
} else {
|
|
653
|
+
embeddingRevision =
|
|
654
|
+
embeddingModel === DEFAULT_EMBEDDING_MODEL ? DEFAULT_EMBEDDING_REVISION : null;
|
|
655
|
+
}
|
|
635
656
|
return {
|
|
636
657
|
device_id: env.CODEMEM_DEVICE_ID?.trim() || null,
|
|
637
658
|
actor_id_present: Object.hasOwn(env, "CODEMEM_ACTOR_ID"),
|
|
@@ -644,7 +665,11 @@ const buildViewerIdentityTarget = (env = process.env, cwd = process.cwd()) => {
|
|
|
644
665
|
embedding_disabled: ["1", "true", "yes"].includes(
|
|
645
666
|
String(env.CODEMEM_EMBEDDING_DISABLED || "").toLowerCase(),
|
|
646
667
|
),
|
|
647
|
-
|
|
668
|
+
embedding_offline: ["1", "true", "yes"].includes(
|
|
669
|
+
String(env.CODEMEM_EMBEDDING_OFFLINE || "").toLowerCase(),
|
|
670
|
+
),
|
|
671
|
+
embedding_model: embeddingModel,
|
|
672
|
+
embedding_revision: embeddingRevision,
|
|
648
673
|
};
|
|
649
674
|
};
|
|
650
675
|
|
|
@@ -1704,13 +1729,47 @@ const tsCliAvailable = (cliPath) => {
|
|
|
1704
1729
|
}
|
|
1705
1730
|
};
|
|
1706
1731
|
|
|
1732
|
+
// When an explicit npx override targets the codemem package, pair it with the
|
|
1733
|
+
// matching @codemem/embeddings spec so the detached Viewer can resolve the
|
|
1734
|
+
// optional runtime instead of silently falling back to lexical search. Returns
|
|
1735
|
+
// null for any spec that is not a bare `codemem`/`codemem@<version>`, so custom
|
|
1736
|
+
// package overrides pass through unchanged.
|
|
1737
|
+
const pairedEmbeddingsForCodememSpec = (spec) => {
|
|
1738
|
+
if (typeof spec !== "string") return null;
|
|
1739
|
+
const trimmed = spec.trim();
|
|
1740
|
+
if (trimmed === "codemem") return "@codemem/embeddings";
|
|
1741
|
+
const match = /^codemem@([^\s]+)$/.exec(trimmed);
|
|
1742
|
+
if (!match || !/^[A-Za-z0-9~^<>=*][A-Za-z0-9._~^<>=*|-]*$/.test(match[1])) return null;
|
|
1743
|
+
return `@codemem/embeddings@${match[1]}`;
|
|
1744
|
+
};
|
|
1745
|
+
|
|
1707
1746
|
const buildRunnerArgs = ({ runner, runnerFrom, runnerFromExplicit }) => {
|
|
1708
1747
|
if (runner === "codemem") {
|
|
1709
1748
|
return [];
|
|
1710
1749
|
}
|
|
1711
1750
|
if (runner === "npx") {
|
|
1712
|
-
|
|
1713
|
-
|
|
1751
|
+
if (runnerFromExplicit) {
|
|
1752
|
+
const pairedEmbeddings = pairedEmbeddingsForCodememSpec(runnerFrom);
|
|
1753
|
+
if (pairedEmbeddings) {
|
|
1754
|
+
return [
|
|
1755
|
+
"-y",
|
|
1756
|
+
"--package",
|
|
1757
|
+
runnerFrom,
|
|
1758
|
+
"--package",
|
|
1759
|
+
pairedEmbeddings,
|
|
1760
|
+
"codemem",
|
|
1761
|
+
];
|
|
1762
|
+
}
|
|
1763
|
+
return ["-y", runnerFrom];
|
|
1764
|
+
}
|
|
1765
|
+
return [
|
|
1766
|
+
"-y",
|
|
1767
|
+
"--package",
|
|
1768
|
+
`codemem@${PINNED_BACKEND_VERSION}`,
|
|
1769
|
+
"--package",
|
|
1770
|
+
`@codemem/embeddings@${PINNED_BACKEND_VERSION}`,
|
|
1771
|
+
"codemem",
|
|
1772
|
+
];
|
|
1714
1773
|
}
|
|
1715
1774
|
if (runner === "node") {
|
|
1716
1775
|
const cliPath = runnerFromExplicit
|
|
@@ -2421,12 +2480,16 @@ export const CodememPlugin = async ({
|
|
|
2421
2480
|
};
|
|
2422
2481
|
|
|
2423
2482
|
const runCommand = async (cmd, options = {}) => {
|
|
2424
|
-
const {
|
|
2483
|
+
const {
|
|
2484
|
+
env = process.env,
|
|
2485
|
+
stdinText = null,
|
|
2486
|
+
timeoutMs = commandTimeout,
|
|
2487
|
+
} = options;
|
|
2425
2488
|
const [command, ...args] = cmd;
|
|
2426
2489
|
return new Promise((resolve) => {
|
|
2427
2490
|
const proc = nodeSpawn(command, args, {
|
|
2428
2491
|
cwd,
|
|
2429
|
-
env
|
|
2492
|
+
env,
|
|
2430
2493
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2431
2494
|
});
|
|
2432
2495
|
let stdout = "";
|
|
@@ -2847,11 +2910,36 @@ export const CodememPlugin = async ({
|
|
|
2847
2910
|
|
|
2848
2911
|
if (backendUpdatePolicy === "auto") {
|
|
2849
2912
|
compatibilityAutoUpdateAttempted = true;
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
["update", "install", "--json"],
|
|
2853
|
-
{ timeoutMs: 420_000 }
|
|
2913
|
+
const notification = parseReleaseNotification(
|
|
2914
|
+
await runCli(["update", "check", "--json"])
|
|
2854
2915
|
);
|
|
2916
|
+
if (!notification?.autoUpdateEligible) {
|
|
2917
|
+
await logLine("compat.auto_update_skipped reason=release_not_eligible");
|
|
2918
|
+
await showToast(
|
|
2919
|
+
`${message}. Auto-update skipped (not eligible). Suggested action: ${guidance.action}`,
|
|
2920
|
+
"warning"
|
|
2921
|
+
);
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
const autoPlan = resolveAutoUpdatePlan({
|
|
2925
|
+
runner,
|
|
2926
|
+
runnerFrom,
|
|
2927
|
+
runnerFromExplicit,
|
|
2928
|
+
targetVersion: notification.latestVersion,
|
|
2929
|
+
});
|
|
2930
|
+
if (!autoPlan.allowed) {
|
|
2931
|
+
await logLine(`compat.auto_update_skipped reason=${autoPlan.reason || "not_eligible"}`);
|
|
2932
|
+
await showToast(
|
|
2933
|
+
`${message}. Auto-update skipped (not eligible). Suggested action: ${guidance.action}`,
|
|
2934
|
+
"warning"
|
|
2935
|
+
);
|
|
2936
|
+
return;
|
|
2937
|
+
}
|
|
2938
|
+
await logLine(`compat.auto_update_start cmd=${autoPlan.commandText}`);
|
|
2939
|
+
const updateResult = await runCommand(autoPlan.command, {
|
|
2940
|
+
env: resolveAutoUpdateEnvironment(),
|
|
2941
|
+
timeoutMs: 480_000,
|
|
2942
|
+
});
|
|
2855
2943
|
if (updateResult?.exitCode === 0) {
|
|
2856
2944
|
await logLine(
|
|
2857
2945
|
`compat.auto_update_result exit=${updateResult?.exitCode ?? "unknown"} stderr=${redactLog(
|
|
@@ -2863,6 +2951,7 @@ export const CodememPlugin = async ({
|
|
|
2863
2951
|
const refreshedVersion = (refreshedResult?.stdout || "").trim();
|
|
2864
2952
|
if (
|
|
2865
2953
|
refreshedResult?.exitCode === 0
|
|
2954
|
+
&& refreshedVersion === notification.latestVersion
|
|
2866
2955
|
&& isVersionAtLeast(refreshedVersion, minVersion)
|
|
2867
2956
|
) {
|
|
2868
2957
|
writeCompatCheckCache(cacheKey, refreshedVersion);
|
|
@@ -2891,22 +2980,11 @@ export const CodememPlugin = async ({
|
|
|
2891
2980
|
);
|
|
2892
2981
|
return;
|
|
2893
2982
|
}
|
|
2894
|
-
let installError = null;
|
|
2895
|
-
try {
|
|
2896
|
-
installError = JSON.parse((updateResult?.stdout || "").trim())?.error || null;
|
|
2897
|
-
} catch {
|
|
2898
|
-
// Non-JSON output is treated as an installation failure.
|
|
2899
|
-
}
|
|
2900
|
-
const failureReason = installError === "update_install_locked"
|
|
2901
|
-
? "another update is already running"
|
|
2902
|
-
: installError === "update_install_refused"
|
|
2903
|
-
? "not eligible"
|
|
2904
|
-
: "installation failed";
|
|
2905
2983
|
await logLine(
|
|
2906
|
-
`compat.auto_update_skipped reason
|
|
2984
|
+
`compat.auto_update_skipped reason=update_install_failed exit=${updateResult?.exitCode ?? "unknown"} stderr=${redactLog((updateResult?.stderr || "").trim())}`
|
|
2907
2985
|
);
|
|
2908
2986
|
await showToast(
|
|
2909
|
-
`${message}. Auto-update skipped (
|
|
2987
|
+
`${message}. Auto-update skipped (installation failed). Suggested action: ${guidance.action}`,
|
|
2910
2988
|
"warning"
|
|
2911
2989
|
);
|
|
2912
2990
|
return;
|
|
@@ -2916,7 +2994,7 @@ export const CodememPlugin = async ({
|
|
|
2916
2994
|
};
|
|
2917
2995
|
|
|
2918
2996
|
const checkForReleaseUpdate = async () => {
|
|
2919
|
-
if (backendUpdatePolicy === "off") return;
|
|
2997
|
+
if (backendUpdatePolicy === "off" || compatibilityAutoUpdateAttempted) return;
|
|
2920
2998
|
const notification = parseReleaseNotification(
|
|
2921
2999
|
await runCli(["update", "check", "--json"])
|
|
2922
3000
|
);
|
|
@@ -2932,13 +3010,31 @@ export const CodememPlugin = async ({
|
|
|
2932
3010
|
&& notification.autoUpdateEligible
|
|
2933
3011
|
&& !compatibilityAutoUpdateAttempted
|
|
2934
3012
|
) {
|
|
2935
|
-
const autoPlan = resolveAutoUpdatePlan({
|
|
3013
|
+
const autoPlan = resolveAutoUpdatePlan({
|
|
3014
|
+
runner,
|
|
3015
|
+
runnerFrom,
|
|
3016
|
+
runnerFromExplicit,
|
|
3017
|
+
targetVersion: notification.latestVersion,
|
|
3018
|
+
});
|
|
2936
3019
|
if (autoPlan.allowed) {
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
3020
|
+
await logLine(`release.auto_update_start cmd=${autoPlan.commandText}`);
|
|
3021
|
+
const installation = await runCommand(autoPlan.command, {
|
|
3022
|
+
env: resolveAutoUpdateEnvironment(),
|
|
3023
|
+
timeoutMs: 480_000,
|
|
3024
|
+
});
|
|
2941
3025
|
if (installation?.exitCode === 0) {
|
|
3026
|
+
const verification = await runCli(["version"]);
|
|
3027
|
+
const installedVersion = (verification?.stdout || "").trim();
|
|
3028
|
+
if (verification?.exitCode !== 0 || installedVersion !== notification.latestVersion) {
|
|
3029
|
+
await logLine(
|
|
3030
|
+
`release.auto_update_verification_failed current=${redactLog(installedVersion || "unknown")} expected=${notification.latestVersion}`
|
|
3031
|
+
);
|
|
3032
|
+
await showToast(
|
|
3033
|
+
`codemem ${notification.latestVersion} was installed, but the active CLI failed verification.`,
|
|
3034
|
+
"warning"
|
|
3035
|
+
);
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
2942
3038
|
const viewerRestart = await restartViewerAfterAutoUpdate();
|
|
2943
3039
|
await showToast(`Updated codemem to ${notification.latestVersion}.`, "success");
|
|
2944
3040
|
if (viewerRestart.attempted && !viewerRestart.ok) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemem/opencode-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0-alpha.1",
|
|
4
4
|
"description": "CodeMem plugin for OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
".opencode/lib/"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@opencode-ai/plugin": "1.18.
|
|
21
|
+
"@opencode-ai/plugin": "1.18.25"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^24.12.4"
|
|
22
25
|
},
|
|
23
26
|
"repository": {
|
|
24
27
|
"type": "git",
|
|
@@ -30,7 +33,7 @@
|
|
|
30
33
|
"url": "https://github.com/kunickiaj/codemem/issues"
|
|
31
34
|
},
|
|
32
35
|
"engines": {
|
|
33
|
-
"node": ">=24"
|
|
36
|
+
"node": ">=24.15.0"
|
|
34
37
|
},
|
|
35
38
|
"license": "MIT",
|
|
36
39
|
"publishConfig": {
|