@sdsrs/code-graph 0.25.0 → 0.25.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.
|
@@ -87,13 +87,33 @@ function isNativeBinary(candidate) {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Decide whether a cached binary path is fresh enough to skip the full
|
|
92
|
+
* discovery walk. Matches the auto-update cache logic at ~:185-188 —
|
|
93
|
+
* cache wins only when its binary's version >= pkg version. Without this
|
|
94
|
+
* check, a stale cache entry (e.g. dev checkout's `bin/code-graph-mcp`
|
|
95
|
+
* recorded once, then never refreshed) shadows newer auto-update or
|
|
96
|
+
* platform-pkg binaries forever (see mem #8454).
|
|
97
|
+
*
|
|
98
|
+
* Permissive on unknown values: missing pkg version or unreadable binary
|
|
99
|
+
* version → trust cache (don't refuse the only path we know about).
|
|
100
|
+
*/
|
|
101
|
+
function isCachedBinaryFresh(cachedPath, pkgVersion) {
|
|
102
|
+
if (!isNativeBinary(cachedPath)) return false;
|
|
103
|
+
if (!pkgVersion) return true;
|
|
104
|
+
const cacheVer = readBinaryVersion(cachedPath);
|
|
105
|
+
if (!cacheVer) return true;
|
|
106
|
+
return compareVersions(cacheVer, pkgVersion) >= 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
90
109
|
/**
|
|
91
110
|
* Locate the code-graph-mcp binary using multiple strategies.
|
|
92
111
|
* Results are cached to disk so repeated calls (e.g. per-hook) are fast.
|
|
93
112
|
*
|
|
94
113
|
* Priority:
|
|
95
|
-
* cache (if valid) → dev-mode (target/release) →
|
|
96
|
-
* → platform npm pkg → bundled (bin/) →
|
|
114
|
+
* cache (if valid + version >= pkg) → dev-mode (target/release) →
|
|
115
|
+
* auto-update cache → platform npm pkg → bundled (bin/) →
|
|
116
|
+
* cargo install → PATH → npx cache
|
|
97
117
|
*
|
|
98
118
|
* Returns the absolute path or null if not found.
|
|
99
119
|
*/
|
|
@@ -101,7 +121,7 @@ function findBinary() {
|
|
|
101
121
|
// Try disk cache first (avoids spawning `which` on hot paths)
|
|
102
122
|
try {
|
|
103
123
|
const cached = fs.readFileSync(CACHE_FILE, 'utf8').trim();
|
|
104
|
-
if (
|
|
124
|
+
if (isCachedBinaryFresh(cached, getPackageVersion())) return cached;
|
|
105
125
|
if (cached) clearCache();
|
|
106
126
|
} catch { /* no cache or stale */ }
|
|
107
127
|
|
|
@@ -242,7 +262,7 @@ function clearCache() {
|
|
|
242
262
|
module.exports = {
|
|
243
263
|
findBinary, findBinaryUncached, clearCache,
|
|
244
264
|
globalNodeModulesCandidates, findPlatformBinary,
|
|
245
|
-
getPackageVersion, compareVersions,
|
|
265
|
+
getPackageVersion, compareVersions, isCachedBinaryFresh,
|
|
246
266
|
CACHE_FILE, BINARY_NAME, PLATFORM_PKG,
|
|
247
267
|
};
|
|
248
268
|
|
|
@@ -6,7 +6,7 @@ const os = require('os');
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
|
|
8
8
|
const { globalNodeModulesCandidates, findPlatformBinary, BINARY_NAME,
|
|
9
|
-
compareVersions, getPackageVersion } = require('./find-binary');
|
|
9
|
+
compareVersions, getPackageVersion, isCachedBinaryFresh } = require('./find-binary');
|
|
10
10
|
|
|
11
11
|
function mkDir(t, prefix) {
|
|
12
12
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
@@ -145,3 +145,77 @@ test('getPackageVersion reads root package.json', () => {
|
|
|
145
145
|
const v = getPackageVersion();
|
|
146
146
|
assert.match(v, /^\d+\.\d+\.\d+$/, `expected semver-ish, got: ${v}`);
|
|
147
147
|
});
|
|
148
|
+
|
|
149
|
+
// ─── isCachedBinaryFresh: disk cache version-check (mem #8454) ────────────
|
|
150
|
+
//
|
|
151
|
+
// Builds a fake binary that responds to `--version` with a controllable
|
|
152
|
+
// string. process.execPath (node itself) won't do — we need a binary
|
|
153
|
+
// whose --version line we control. Smallest approach: shell wrapper.
|
|
154
|
+
|
|
155
|
+
function buildFakeBinary(t, versionLine) {
|
|
156
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cgmcp-fake-bin-'));
|
|
157
|
+
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
|
|
158
|
+
const binPath = path.join(dir, BINARY_NAME);
|
|
159
|
+
// readBinaryVersion parses "code-graph-mcp X.Y.Z" via the binary's first
|
|
160
|
+
// stdout line on `--version`. Shell wrapper is simpler than compiling.
|
|
161
|
+
const script = process.platform === 'win32'
|
|
162
|
+
? `@echo off\r\necho ${versionLine}\r\n`
|
|
163
|
+
: `#!/bin/sh\necho '${versionLine}'\n`;
|
|
164
|
+
fs.writeFileSync(binPath, script);
|
|
165
|
+
if (process.platform !== 'win32') fs.chmodSync(binPath, 0o755);
|
|
166
|
+
return binPath;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
test('isCachedBinaryFresh: cache binary version >= pkg → fresh', (t) => {
|
|
170
|
+
const bin = buildFakeBinary(t, 'code-graph-mcp 9.9.9');
|
|
171
|
+
assert.equal(isCachedBinaryFresh(bin, '0.25.0'), true);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('isCachedBinaryFresh: cache binary version equals pkg → fresh', (t) => {
|
|
175
|
+
const bin = buildFakeBinary(t, 'code-graph-mcp 0.25.0');
|
|
176
|
+
assert.equal(isCachedBinaryFresh(bin, '0.25.0'), true);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('isCachedBinaryFresh: cache binary version < pkg → stale (THE BUG)', (t) => {
|
|
180
|
+
// Reproduces mem #8454: cache pointed at bin/code-graph-mcp v0.5.28
|
|
181
|
+
// while pkg was v0.25.0 → cache was returned silently with no
|
|
182
|
+
// version-check, shadowing the installed 0.25.0 platform binary.
|
|
183
|
+
// After this fix, returns false → caller clears cache + falls through.
|
|
184
|
+
const bin = buildFakeBinary(t, 'code-graph-mcp 0.5.28');
|
|
185
|
+
assert.equal(isCachedBinaryFresh(bin, '0.25.0'), false);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('isCachedBinaryFresh: missing pkg version → permissive (trust cache)', (t) => {
|
|
189
|
+
// Caller couldn't read package.json; refusing the cache would leave us
|
|
190
|
+
// with nothing. Better to trust the one path we have.
|
|
191
|
+
const bin = buildFakeBinary(t, 'code-graph-mcp 0.5.28');
|
|
192
|
+
assert.equal(isCachedBinaryFresh(bin, null), true);
|
|
193
|
+
assert.equal(isCachedBinaryFresh(bin, ''), true);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test('isCachedBinaryFresh: unreadable cache binary version → permissive', (t) => {
|
|
197
|
+
// Old binary that doesn't support `--version`, or output we can't
|
|
198
|
+
// parse. Same permissive path as missing pkg version.
|
|
199
|
+
const bin = buildFakeBinary(t, 'whatever garbage no semver here');
|
|
200
|
+
assert.equal(isCachedBinaryFresh(bin, '0.25.0'), true);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test('isCachedBinaryFresh: cache path does not exist → not fresh', () => {
|
|
204
|
+
assert.equal(isCachedBinaryFresh('/nonexistent/path/code-graph-mcp', '0.25.0'), false);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('isCachedBinaryFresh: empty/null cache path → not fresh', () => {
|
|
208
|
+
assert.equal(isCachedBinaryFresh('', '0.25.0'), false);
|
|
209
|
+
assert.equal(isCachedBinaryFresh(null, '0.25.0'), false);
|
|
210
|
+
assert.equal(isCachedBinaryFresh(undefined, '0.25.0'), false);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('isCachedBinaryFresh: file basename mismatch → not fresh', (t) => {
|
|
214
|
+
// realpathSync.basename check inside isNativeBinary — wrong name = not ours.
|
|
215
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cgmcp-wrongname-'));
|
|
216
|
+
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
|
|
217
|
+
const wrongName = path.join(dir, 'other-tool');
|
|
218
|
+
fs.writeFileSync(wrongName, '#!/bin/sh\necho wrong\n');
|
|
219
|
+
if (process.platform !== 'win32') fs.chmodSync(wrongName, 0o755);
|
|
220
|
+
assert.equal(isCachedBinaryFresh(wrongName, '0.25.0'), false);
|
|
221
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdsrs/code-graph",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.1",
|
|
4
4
|
"description": "MCP server that indexes codebases into an AST knowledge graph with semantic search, call graph traversal, and HTTP route tracing",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"node": ">=16"
|
|
36
36
|
},
|
|
37
37
|
"optionalDependencies": {
|
|
38
|
-
"@sdsrs/code-graph-linux-x64": "0.25.
|
|
39
|
-
"@sdsrs/code-graph-linux-arm64": "0.25.
|
|
40
|
-
"@sdsrs/code-graph-darwin-x64": "0.25.
|
|
41
|
-
"@sdsrs/code-graph-darwin-arm64": "0.25.
|
|
42
|
-
"@sdsrs/code-graph-win32-x64": "0.25.
|
|
38
|
+
"@sdsrs/code-graph-linux-x64": "0.25.1",
|
|
39
|
+
"@sdsrs/code-graph-linux-arm64": "0.25.1",
|
|
40
|
+
"@sdsrs/code-graph-darwin-x64": "0.25.1",
|
|
41
|
+
"@sdsrs/code-graph-darwin-arm64": "0.25.1",
|
|
42
|
+
"@sdsrs/code-graph-win32-x64": "0.25.1"
|
|
43
43
|
}
|
|
44
44
|
}
|