diff --git a/packages/memory/src/entrypoint.ts b/packages/memory/src/entrypoint.ts new file mode 100644 index 00000000..6b0294db --- /dev/null +++ b/packages/memory/src/entrypoint.ts @@ -0,0 +1,15 @@ +import { realpathSync } from "fs"; +import { fileURLToPath } from "url"; + +// npx and global installs launch the bin symlink, so argv[1] is the link while +// import.meta.url is the resolved file. Compare real paths. +export function isMainModule(argv1: string | undefined, moduleUrl: string): boolean { + if (!argv1) { + return false; + } + try { + return realpathSync(argv1) === realpathSync(fileURLToPath(moduleUrl)); + } catch { + return false; + } +} diff --git a/packages/memory/src/index.ts b/packages/memory/src/index.ts index 2ab33334..a8fc866b 100644 --- a/packages/memory/src/index.ts +++ b/packages/memory/src/index.ts @@ -1,13 +1,13 @@ #!/usr/bin/env node -import { fileURLToPath } from "url"; +import { isMainModule } from "./entrypoint.js"; import { runServer } from "./server.js"; export * from "./api.js"; // Only start MCP server when this file is run directly as a binary // Not when imported as a library (e.g., by CLI commands) -if (process.argv[1] === fileURLToPath(import.meta.url)) { +if (isMainModule(process.argv[1], import.meta.url)) { runServer().catch((error: Error) => { console.error("Failed to start server:", error); process.exit(1); diff --git a/packages/memory/tests/unit/entrypoint.test.ts b/packages/memory/tests/unit/entrypoint.test.ts new file mode 100644 index 00000000..a85b9ca0 --- /dev/null +++ b/packages/memory/tests/unit/entrypoint.test.ts @@ -0,0 +1,48 @@ +import { mkdtempSync, rmSync, symlinkSync, writeFileSync } from "fs"; +import { tmpdir } from "os"; +import { join } from "path"; +import { pathToFileURL } from "url"; +import { isMainModule } from "../../src/entrypoint"; + +describe("isMainModule", () => { + let dir: string; + let modulePath: string; + let moduleUrl: string; + + beforeEach(() => { + dir = mkdtempSync(join(tmpdir(), "memory-entrypoint-")); + modulePath = join(dir, "index.js"); + writeFileSync(modulePath, ""); + moduleUrl = pathToFileURL(modulePath).href; + }); + + afterEach(() => { + rmSync(dir, { recursive: true, force: true }); + }); + + it("returns true when argv[1] is the module file", () => { + expect(isMainModule(modulePath, moduleUrl)).toBe(true); + }); + + it("returns true when argv[1] is a bin symlink to the module file", () => { + const binPath = join(dir, "ai-devkit-memory"); + symlinkSync(modulePath, binPath); + + expect(isMainModule(binPath, moduleUrl)).toBe(true); + }); + + it("returns false when argv[1] is a different file", () => { + const otherPath = join(dir, "cli.js"); + writeFileSync(otherPath, ""); + + expect(isMainModule(otherPath, moduleUrl)).toBe(false); + }); + + it("returns false when argv[1] is undefined", () => { + expect(isMainModule(undefined, moduleUrl)).toBe(false); + }); + + it("returns false when argv[1] does not exist", () => { + expect(isMainModule(join(dir, "missing.js"), moduleUrl)).toBe(false); + }); +});