mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-11-14 21:13:11 +08:00
* fix(mcp,extraction): a daemon whose install changed exits; grammar-load failures are never stored (#2335, #2336) A daemon that outlived an upgrade loaded grammars lazily from an install that no longer existed, and stored every file its watcher re-indexed as a zero-node row under the file's new content hash: no hash-based sync ever revisited those rows, and `codegraph status` called the index up to date. - The daemon checks its own package.json every 30 s (CODEGRAPH_DAEMON_INSTALL_CHECK_MS, 0 turns it off) and exits once the file is gone or carries another version, so the next session starts a daemon from the current install. - A result whose grammar failed to load (`parser_error`) is never stored: the file keeps its previous data and the next sync or index retries it, and a row an older engine stored that way is re-indexed even though its hash matches. The tag-based CFML path reports a missing grammar with the same code. - A launcher whose own install was deleted no longer crashes on the spawn error of the daemon it tried to start. - `codegraph status` and `status --json` (filesNeedingReindex, filesWithParseErrors) report indexed files whose symbols are missing although their content is current (#2336), and `files --json` carries each file's recorded errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(mcp): a session whose own install changed never claims the writer lock (#2335) A daemon now exits once its install is upgraded or removed, and nobody holds the writer slot for a successor at that point. Its sessions run the same replaced code and serve themselves in-process the moment it goes; one that found the slot free claimed it as its writer even though its engine could not load (a real npm upgrade: "Cannot find module './sqlite-adapter'"), and so kept a daemon from the current install from starting. Such a session now falls back read-only and says to restart it. Also adds the CHANGELOG entries for the install check and grammar-load failures (#2335) and for the status report (#2336). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
2.4 KiB
TypeScript
62 lines
2.4 KiB
TypeScript
/**
|
|
* Resolved package version, computed once at module load.
|
|
*
|
|
* The version string is the rendezvous datum between cooperating daemon and
|
|
* proxy processes: the daemon advertises its version in the hello line, and
|
|
* the proxy refuses to share IPC across a mismatch (falls back to direct
|
|
* mode), or replaces a daemon of an older release ({@link isOlderRelease}).
|
|
* Keeping the resolution in one place avoids drift between the CLI
|
|
* `--version` output (which reads `package.json` directly) and the daemon
|
|
* handshake.
|
|
*
|
|
* Resolution strategy: read the bundled `package.json` two levels up from
|
|
* this file — same relative position whether we're loaded from `src/mcp/` or
|
|
* the `dist/mcp/` output, since `tsc` preserves the layout. If reading fails
|
|
* (e.g. the package was unpacked oddly), fall back to "0.0.0-unknown" — a
|
|
* sentinel that will never match a real version, so the proxy harmlessly
|
|
* falls back to direct mode.
|
|
*/
|
|
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
/** The `package.json` of the install this process runs from. */
|
|
export const CodeGraphPackageJsonPath = path.join(__dirname, '..', '..', 'package.json');
|
|
|
|
function readPackageVersion(): string {
|
|
try {
|
|
const raw = fs.readFileSync(CodeGraphPackageJsonPath, 'utf8');
|
|
const parsed = JSON.parse(raw);
|
|
if (typeof parsed?.version === 'string' && parsed.version.length > 0) {
|
|
return parsed.version;
|
|
}
|
|
} catch {
|
|
// Fall through to sentinel.
|
|
}
|
|
return '0.0.0-unknown';
|
|
}
|
|
|
|
export const CodeGraphPackageVersion = readPackageVersion();
|
|
|
|
/**
|
|
* Whether `version` is a CodeGraph release older than `than` — the test a
|
|
* launcher applies before it replaces a running daemon (#2335). Only plain
|
|
* `MAJOR.MINOR.PATCH` releases compare: a prerelease, a build suffix or the
|
|
* "0.0.0-unknown" sentinel is never older. A daemon is replaced only by a
|
|
* strictly newer release, so two installed versions can never take turns
|
|
* stopping each other's daemon.
|
|
*/
|
|
export function isOlderRelease(version: string, than: string): boolean {
|
|
const a = parseRelease(version);
|
|
const b = parseRelease(than);
|
|
if (!a || !b) return false;
|
|
if (a[0] !== b[0]) return a[0] < b[0];
|
|
if (a[1] !== b[1]) return a[1] < b[1];
|
|
return a[2] < b[2];
|
|
}
|
|
|
|
function parseRelease(version: string): [number, number, number] | null {
|
|
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
|
|
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
}
|