@creeperhost/modlens-mcp 1.6.17 → 1.6.18
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/dist/db.d.ts.map +1 -1
- package/dist/db.js +3 -2
- package/dist/db.js.map +1 -1
- package/dist/generated/sqlite/internal/class.js +2 -2
- package/dist/generated/sqlite/internal/class.js.map +1 -1
- package/package.json +4 -3
- package/scripts/build-template-db.mjs +2 -1
- package/scripts/test-package.mjs +209 -0
package/dist/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAK9C;;;;GAIG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAK9C;;;;GAIG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,CAqCnD;AAED,+EAA+E;AAC/E,wBAAgB,EAAE,IAAI,YAAY,CAOjC;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAGhD"}
|
package/dist/db.js
CHANGED
|
@@ -12,8 +12,9 @@ export async function getDb() {
|
|
|
12
12
|
const backend = detectBackend();
|
|
13
13
|
if (backend === "sqlite") {
|
|
14
14
|
const url = process.env.DATABASE_URL ?? "";
|
|
15
|
-
|
|
16
|
-
const
|
|
15
|
+
// Adapter v7 declares SQLite v12 support; test:package covers this pairing with client v6.
|
|
16
|
+
const { PrismaBetterSqlite3 } = await import("@prisma/adapter-better-sqlite3");
|
|
17
|
+
const adapter = new PrismaBetterSqlite3({ url });
|
|
17
18
|
const { PrismaClient: SQLiteClient } = await import("./generated/sqlite/client.js");
|
|
18
19
|
_client = new SQLiteClient({ adapter });
|
|
19
20
|
return _client;
|
package/dist/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,IAAI,OAAO,GAAwB,IAAI,CAAC;AAExC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACvB,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAEhC,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;QAC3C,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;QAC/E,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACjD,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;QACpF,OAAO,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,CAA4B,CAAC;QACnE,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACxD,6DAA6D;QAC7D,iFAAiF;QACjF,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;QAChE,2CAA2C;QAC3C,0CAA0C;QAC1C,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACX,wFAAwF,CAC3F,CAAC;QACN,CAAC;QACD,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,EAAE,CAAC,CAAC;QACrC,OAAO,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACxC,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,OAAO,GAAG,IAAI,YAAY,CAAC;QACvB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;KAC1D,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,EAAE;IACd,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,OAAO,GAAG,IAAI,YAAY,CAAC;YACvB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;SAC1D,CAAC,CAAC;IACP,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC5B,MAAM,OAAO,EAAE,WAAW,EAAE,CAAC;IAC7B,OAAO,GAAG,IAAI,CAAC;AACnB,CAAC"}
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,IAAI,OAAO,GAAwB,IAAI,CAAC;AAExC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACvB,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAEhC,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;QAC3C,2FAA2F;QAC3F,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;QAC/E,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACjD,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;QACpF,OAAO,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,CAA4B,CAAC;QACnE,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACxD,6DAA6D;QAC7D,iFAAiF;QACjF,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;QAChE,2CAA2C;QAC3C,0CAA0C;QAC1C,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACX,wFAAwF,CAC3F,CAAC;QACN,CAAC;QACD,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,EAAE,CAAC,CAAC;QACrC,OAAO,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACxC,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,OAAO,GAAG,IAAI,YAAY,CAAC;QACvB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;KAC1D,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,EAAE;IACd,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,OAAO,GAAG,IAAI,YAAY,CAAC;YACvB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;SAC1D,CAAC,CAAC;IACP,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC5B,MAAM,OAAO,EAAE,WAAW,EAAE,CAAC;IAC7B,OAAO,GAAG,IAAI,CAAC;AACnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"class.js","sourceRoot":"","sources":["../../../../src/generated/sqlite/internal/class.ts"],"names":[],"mappings":"AACA,qEAAqE;AACrE,oBAAoB;AACpB,wCAAwC;AACxC,eAAe;AACf;;;;;;GAMG;AAEH,OAAO,KAAK,OAAO,MAAM,+BAA+B,CAAA;AAIxD,MAAM,MAAM,GAAkC;IAC5C,WAAW,EAAE;QACX,MAAM,EAAE,QAAQ;QAChB,UAAU,EAAE;YACV,YAAY,EAAE,IAAI;YAClB,OAAO,EAAE,eAAe;SACzB;QACD,QAAQ,EAAE;YACR,OAAO,EAAE,gEAAgE;YACzE,YAAY,EAAE,IAAI;SACnB;QACD,QAAQ,EAAE;YACR,
|
|
1
|
+
{"version":3,"file":"class.js","sourceRoot":"","sources":["../../../../src/generated/sqlite/internal/class.ts"],"names":[],"mappings":"AACA,qEAAqE;AACrE,oBAAoB;AACpB,wCAAwC;AACxC,eAAe;AACf;;;;;;GAMG;AAEH,OAAO,KAAK,OAAO,MAAM,+BAA+B,CAAA;AAIxD,MAAM,MAAM,GAAkC;IAC5C,WAAW,EAAE;QACX,MAAM,EAAE,QAAQ;QAChB,UAAU,EAAE;YACV,YAAY,EAAE,IAAI;YAClB,OAAO,EAAE,eAAe;SACzB;QACD,QAAQ,EAAE;YACR,OAAO,EAAE,gEAAgE;YACzE,YAAY,EAAE,IAAI;SACnB;QACD,QAAQ,EAAE;YACR,YAAY,EAAE,QAAQ;YACtB,cAAc,EAAE,KAAK;YACrB,SAAS,EAAE,QAAQ;SACpB;QACD,eAAe,EAAE;YACf;gBACE,YAAY,EAAE,IAAI;gBAClB,OAAO,EAAE,sBAAsB;gBAC/B,QAAQ,EAAE,IAAI;aACf;SACF;QACD,iBAAiB,EAAE,EAAE;QACrB,gBAAgB,EAAE,gFAAgF;QAClG,gBAAgB,EAAE,IAAI;KACvB;IACD,cAAc,EAAE,0BAA0B;IAC1C,eAAe,EAAE,QAAQ;IACzB,eAAe,EAAE,0CAA0C;IAC3D,iBAAiB,EAAE;QACjB,IAAI;KACL;IACD,gBAAgB,EAAE,QAAQ;IAC1B,aAAa,EAAE,KAAK;IACpB,QAAQ,EAAE,gBAAgB;IAC1B,mBAAmB,EAAE;QACnB,IAAI,EAAE;YACJ,KAAK,EAAE;gBACL,YAAY,EAAE,cAAc;gBAC5B,OAAO,EAAE,IAAI;aACd;SACF;KACF;IACD,cAAc,EAAE,wuRAAwuR;IACxvR,kBAAkB,EAAE,kEAAkE;IACtF,YAAY,EAAE,IAAI;IAClB,kBAAkB,EAAE;QAClB,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,EAAE;QACX,OAAO,EAAE,EAAE;KACZ;IACD,SAAS,EAAE,EAAE;CACd,CAAA;AAED,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,i9WAAi9W,CAAC,CAAA;AACv/W,MAAM,CAAC,UAAU,GAAG,SAAS,CAAA;AAE7B,KAAK,UAAU,kBAAkB,CAAC,UAAkB;IAClD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAA;IAC9C,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IACnD,OAAO,IAAI,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,CAAC,YAAY,GAAG;IACpB,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,MAAM,CAAC,qDAAqD,CAAC;IAE3F,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACrC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,iEAAiE,CAAC,CAAA;QAChG,OAAO,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAA;IACvC,CAAC;CACF,CAAA;AA8PD,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAClD,MAAM,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,OAAO,OAAO,CAAC,eAAe,CAAC,MAAM,CAAuC,CAAA;AAC9E,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@creeperhost/modlens-mcp",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.18",
|
|
4
4
|
"description": "MCP server for browsing, decompiling, and analyzing Minecraft mod JARs",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"start": "node dist/server.js",
|
|
33
33
|
"setup": "npm run build && node dist/setup.js",
|
|
34
34
|
"test": "vitest run",
|
|
35
|
+
"test:package": "node scripts/test-package.mjs",
|
|
35
36
|
"test:watch": "vitest",
|
|
36
37
|
"db:generate": "prisma generate",
|
|
37
38
|
"db:push": "prisma db push",
|
|
@@ -50,10 +51,10 @@
|
|
|
50
51
|
"dependencies": {
|
|
51
52
|
"@clack/prompts": "^1.4.0",
|
|
52
53
|
"@modelcontextprotocol/sdk": "^1.10.0",
|
|
53
|
-
"@prisma/adapter-better-sqlite3": "
|
|
54
|
+
"@prisma/adapter-better-sqlite3": "7.9.1",
|
|
54
55
|
"@prisma/client": "^6.0.0",
|
|
55
56
|
"adm-zip": "^0.5.16",
|
|
56
|
-
"better-sqlite3": "^11.
|
|
57
|
+
"better-sqlite3": "^12.11.1",
|
|
57
58
|
"prisma": "^6.0.0",
|
|
58
59
|
"smol-toml": "^1.6.1",
|
|
59
60
|
"zod": "^3.23.8"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* so its schema is always in sync with `prisma/backends/schema.sqlite.prisma`.
|
|
11
11
|
*/
|
|
12
12
|
import { execSync } from "child_process";
|
|
13
|
-
import { existsSync, rmSync } from "fs";
|
|
13
|
+
import { existsSync, rmSync, writeFileSync } from "fs";
|
|
14
14
|
import { join, dirname } from "path";
|
|
15
15
|
import { fileURLToPath } from "url";
|
|
16
16
|
|
|
@@ -19,6 +19,7 @@ const dbFile = join(ROOT, "prisma", "backends", "template.db");
|
|
|
19
19
|
|
|
20
20
|
// Start from a clean slate so the template never carries stale tables/rows.
|
|
21
21
|
if (existsSync(dbFile)) rmSync(dbFile);
|
|
22
|
+
writeFileSync(dbFile, "");
|
|
22
23
|
|
|
23
24
|
// Prisma resolves a relative `file:` URL relative to the schema file's directory
|
|
24
25
|
// (prisma/backends/), so `file:./template.db` lands the db next to the schema.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// Exercise the packed CLI as an npx consumer, outside the source checkout.
|
|
2
|
+
// Run after `npm run build`, or pass a tarball / release-artifact directory.
|
|
3
|
+
import assert from "node:assert/strict";
|
|
4
|
+
import { spawn, execFileSync } from "node:child_process";
|
|
5
|
+
import { existsSync, mkdirSync, mkdtempSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import { dirname, join, resolve, delimiter } from "node:path";
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
10
|
+
|
|
11
|
+
const root = mkdtempSync(join(tmpdir(), "modlens-package-test-"));
|
|
12
|
+
const consumer = join(root, "consumer");
|
|
13
|
+
mkdirSync(consumer);
|
|
14
|
+
writeFileSync(join(consumer, "package.json"), JSON.stringify({ name: "package-smoke-consumer", private: true }));
|
|
15
|
+
writeFileSync(join(root, "npmrc"), "");
|
|
16
|
+
const npm = process.env.npm_execpath ?? (process.platform === "win32"
|
|
17
|
+
? join(dirname(process.execPath), "node_modules/npm/bin/npm-cli.js")
|
|
18
|
+
: realpathSync(join(dirname(process.execPath), "npm")));
|
|
19
|
+
assert.ok(existsSync(npm), "Run this test via npm run test:package");
|
|
20
|
+
const env = Object.fromEntries(Object.entries(process.env).filter(([key]) =>
|
|
21
|
+
!/^(DATABASE_URL|MODLENS_.*|MCP_PORT|DEBUG|NODE_OPTIONS|NODE_PATH|PATH)$/i.test(key)));
|
|
22
|
+
Object.assign(env, {
|
|
23
|
+
PATH: `${dirname(process.execPath)}${delimiter}${process.env.PATH ?? process.env.Path ?? ""}`,
|
|
24
|
+
npm_config_cache: join(root, "cache"),
|
|
25
|
+
npm_config_userconfig: join(root, "npmrc"),
|
|
26
|
+
npm_config_update_notifier: "false",
|
|
27
|
+
npm_config_audit: "false",
|
|
28
|
+
npm_config_fund: "false",
|
|
29
|
+
MODLENS_AUTO_EMBED: "0",
|
|
30
|
+
MODLENS_CACHE_ROOT: join(root, "modlens-cache"),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function start(args, home) {
|
|
34
|
+
const child = spawn(process.execPath, [npm, ...args], {
|
|
35
|
+
cwd: consumer, env: { ...env, MODLENS_HOME: home }, stdio: ["pipe", "pipe", "pipe"],
|
|
36
|
+
detached: process.platform !== "win32",
|
|
37
|
+
});
|
|
38
|
+
let output = "";
|
|
39
|
+
child.stdout.on("data", data => { output += data; });
|
|
40
|
+
child.stderr.on("data", data => { output += data; });
|
|
41
|
+
const done = new Promise((resolveDone, reject) => {
|
|
42
|
+
child.on("error", reject);
|
|
43
|
+
child.on("close", (code, signal) => resolveDone({ code, signal }));
|
|
44
|
+
});
|
|
45
|
+
return { child, done, output: () => output };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function stop(run) {
|
|
49
|
+
if (run.child.exitCode !== null || run.child.signalCode !== null) return;
|
|
50
|
+
if (process.platform === "win32") {
|
|
51
|
+
// Only terminate the process tree created by this test.
|
|
52
|
+
try { execFileSync("taskkill.exe", ["/pid", String(run.child.pid), "/T", "/F"], { stdio: "ignore" }); } catch {}
|
|
53
|
+
} else {
|
|
54
|
+
try { process.kill(-run.child.pid, "SIGTERM"); } catch (error) {
|
|
55
|
+
if (error.code !== "ESRCH") throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
await deadline(run.done, 10_000, "test process shutdown");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function deadline(promise, ms, label) {
|
|
62
|
+
let timer;
|
|
63
|
+
try {
|
|
64
|
+
return await Promise.race([promise, new Promise((_, reject) => {
|
|
65
|
+
timer = setTimeout(() => reject(new Error(`Timed out: ${label}`)), ms);
|
|
66
|
+
})]);
|
|
67
|
+
} finally { clearTimeout(timer); }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let passed = false;
|
|
71
|
+
try {
|
|
72
|
+
let tarball;
|
|
73
|
+
if (process.argv[2]) {
|
|
74
|
+
tarball = resolve(process.argv[2]);
|
|
75
|
+
if (statSync(tarball).isDirectory()) {
|
|
76
|
+
const files = readdirSync(tarball).filter(file => file.endsWith(".tgz"));
|
|
77
|
+
assert.equal(files.length, 1, "Expected one package tarball");
|
|
78
|
+
tarball = join(tarball, files[0]);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
const pack = JSON.parse(execFileSync(process.execPath, [npm, "pack", "--json", "--pack-destination", root], {
|
|
82
|
+
env, encoding: "utf8", timeout: 60_000,
|
|
83
|
+
}));
|
|
84
|
+
tarball = join(root, pack[0].filename);
|
|
85
|
+
}
|
|
86
|
+
console.log(`Testing ${tarball} on ${process.version} (${process.platform}/${process.arch})`);
|
|
87
|
+
const args = ["exec", "--yes", "--foreground-scripts", "--package", tarball, "--", "modlens-mcp"];
|
|
88
|
+
|
|
89
|
+
const setup = start([...args, "--setup"], join(root, "setup-home"));
|
|
90
|
+
try {
|
|
91
|
+
await deadline(new Promise((resolvePrompt, reject) => {
|
|
92
|
+
const inspect = () => {
|
|
93
|
+
if (setup.output().includes("Choose a setup profile")) resolvePrompt();
|
|
94
|
+
};
|
|
95
|
+
setup.child.stdout.on("data", inspect);
|
|
96
|
+
setup.child.stderr.on("data", inspect);
|
|
97
|
+
setup.done.then(result => reject(new Error(`Setup exited before prompting: ${JSON.stringify(result)}\n${setup.output()}`)), reject);
|
|
98
|
+
}), 180_000, "fresh npx install and setup prompt");
|
|
99
|
+
setup.child.stdin.write("\x03"); // Cancel the wizard before saving configuration.
|
|
100
|
+
assert.equal((await deadline(setup.done, 15_000, "setup cancellation")).code, 0, setup.output());
|
|
101
|
+
console.log("PASS: fresh npx install and interactive setup prompt");
|
|
102
|
+
} catch (error) {
|
|
103
|
+
writeFileSync(join(root, "setup.log"), setup.output());
|
|
104
|
+
throw error;
|
|
105
|
+
} finally { await stop(setup); }
|
|
106
|
+
|
|
107
|
+
const installs = readdirSync(join(root, "cache/_npx"))
|
|
108
|
+
.map(dir => join(root, "cache/_npx", dir, "node_modules/@creeperhost/modlens-mcp"))
|
|
109
|
+
.filter(dir => existsSync(join(dir, "package.json")));
|
|
110
|
+
assert.equal(installs.length, 1);
|
|
111
|
+
const installed = installs[0];
|
|
112
|
+
const require = createRequire(join(installed, "package.json"));
|
|
113
|
+
const adapterRequire = createRequire(require.resolve("@prisma/adapter-better-sqlite3"));
|
|
114
|
+
assert.equal(require.resolve("better-sqlite3"), adapterRequire.resolve("better-sqlite3"), "Prisma must share the v12 driver");
|
|
115
|
+
assert.match(require("better-sqlite3/package.json").version, /^12\./);
|
|
116
|
+
|
|
117
|
+
const dataHome = join(root, "server-home");
|
|
118
|
+
async function checkServer() {
|
|
119
|
+
const run = start(args, dataHome);
|
|
120
|
+
const pending = new Map();
|
|
121
|
+
let buffer = "";
|
|
122
|
+
let id = 0;
|
|
123
|
+
run.child.stdout.on("data", data => {
|
|
124
|
+
buffer += data;
|
|
125
|
+
while (buffer.includes("\n")) {
|
|
126
|
+
const newline = buffer.indexOf("\n");
|
|
127
|
+
const line = buffer.slice(0, newline).trim();
|
|
128
|
+
buffer = buffer.slice(newline + 1);
|
|
129
|
+
if (!line) continue;
|
|
130
|
+
try {
|
|
131
|
+
const message = JSON.parse(line);
|
|
132
|
+
const waiter = pending.get(message.id);
|
|
133
|
+
if (waiter) {
|
|
134
|
+
pending.delete(message.id);
|
|
135
|
+
if (message.error) waiter.reject(new Error(JSON.stringify(message.error)));
|
|
136
|
+
else waiter.resolve(message.result);
|
|
137
|
+
}
|
|
138
|
+
} catch (error) { for (const waiter of pending.values()) waiter.reject(error); }
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
run.done.then(result => {
|
|
142
|
+
for (const waiter of pending.values()) waiter.reject(new Error(`MCP exited: ${JSON.stringify(result)}\n${run.output()}`));
|
|
143
|
+
});
|
|
144
|
+
function send(message) { run.child.stdin.write(JSON.stringify({ jsonrpc: "2.0", ...message }) + "\n"); }
|
|
145
|
+
function request(method, params) {
|
|
146
|
+
return deadline(new Promise((resolveRequest, reject) => {
|
|
147
|
+
const requestId = ++id;
|
|
148
|
+
pending.set(requestId, { resolve: resolveRequest, reject });
|
|
149
|
+
send({ id: requestId, method, params });
|
|
150
|
+
}), 30_000, method);
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const initialized = await request("initialize", { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "package-smoke", version: "1.0.0" } });
|
|
154
|
+
assert.ok(initialized.serverInfo);
|
|
155
|
+
send({ method: "notifications/initialized" });
|
|
156
|
+
const list = await request("tools/list", {});
|
|
157
|
+
assert.ok(list.tools.some(tool => tool.name === "mod"));
|
|
158
|
+
const stats = await request("tools/call", { name: "mod", arguments: { action: "stats" } });
|
|
159
|
+
assert.ok(!stats.isError, JSON.stringify(stats));
|
|
160
|
+
assert.equal(JSON.parse(stats.content.find(item => item.type === "text").text).total, 0);
|
|
161
|
+
return run.output();
|
|
162
|
+
} finally { await stop(run); }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const first = await checkServer();
|
|
166
|
+
assert.match(first, /First run/);
|
|
167
|
+
const database = join(dataHome, "data/modlens.db");
|
|
168
|
+
assert.ok(statSync(database).size > 0);
|
|
169
|
+
console.log("PASS: first-run SQLite bootstrap, MCP handshake, tools/list, database stats");
|
|
170
|
+
|
|
171
|
+
// Check the actual installed client/adapter with dates, BLOBs, and transactions.
|
|
172
|
+
// Use a child so Windows releases the native .node file before cleanup.
|
|
173
|
+
function checkDatabase(mode) {
|
|
174
|
+
execFileSync(process.execPath, ["--input-type=module", "-e", `
|
|
175
|
+
import assert from 'node:assert/strict';
|
|
176
|
+
const { getDb, disconnect } = await import(process.argv[1]);
|
|
177
|
+
const db = await getDb();
|
|
178
|
+
try {
|
|
179
|
+
const bytes = new Uint8Array([0, 1, 127, 128, 255]);
|
|
180
|
+
if (process.argv[2] === 'create') {
|
|
181
|
+
const doc = await db.docEntry.create({ data: { title: 'Package smoke', url: 'https://example.invalid', embedding: bytes } });
|
|
182
|
+
assert.deepEqual(doc.embedding, bytes);
|
|
183
|
+
assert.ok(doc.createdAt instanceof Date);
|
|
184
|
+
await db.$transaction(async tx => { await tx.docEntry.update({ where: { id: doc.id }, data: { summary: 'committed' } }); });
|
|
185
|
+
await assert.rejects(db.$transaction(async tx => {
|
|
186
|
+
await tx.docEntry.update({ where: { id: doc.id }, data: { summary: 'rolled back' } });
|
|
187
|
+
throw new Error('rollback sentinel');
|
|
188
|
+
}), /rollback sentinel/);
|
|
189
|
+
}
|
|
190
|
+
const saved = await db.docEntry.findFirstOrThrow({ where: { title: 'Package smoke' } });
|
|
191
|
+
assert.equal(saved.summary, 'committed');
|
|
192
|
+
assert.deepEqual(saved.embedding, bytes);
|
|
193
|
+
} finally { await disconnect(); }
|
|
194
|
+
`, pathToFileURL(join(installed, "dist/db.js")).href, mode], {
|
|
195
|
+
cwd: consumer, env: { ...env, DATABASE_URL: `file:${database}` }, timeout: 30_000, stdio: "pipe",
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
checkDatabase("create");
|
|
199
|
+
console.log("PASS: installed Prisma client/adapter CRUD, binary data, dates, commit and rollback");
|
|
200
|
+
|
|
201
|
+
const second = await checkServer();
|
|
202
|
+
assert.doesNotMatch(second, /First run/);
|
|
203
|
+
checkDatabase("read");
|
|
204
|
+
console.log("PASS: second npx run preserves existing database contents");
|
|
205
|
+
passed = true;
|
|
206
|
+
} finally {
|
|
207
|
+
if (passed) rmSync(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 300 });
|
|
208
|
+
else console.error(`Package test artifacts retained at ${root}`);
|
|
209
|
+
}
|