llm-cli-gateway 1.17.9 → 2.0.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,107 @@ All notable changes to the llm-cli-gateway project.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## [2.0.0] - 2026-06-04: node:sqlite migration — native module out of the prod graph
8
+
9
+ Major release. Persistence moves from the native `better-sqlite3` binding to
10
+ Node's built-in `node:sqlite` module behind a thin adapter. The entire
11
+ 1.17.6-1.17.8 supply-chain incident class — every one of which traced to
12
+ `better-sqlite3`'s install path (`prebuild-install → tar-fs → tar-stream`),
13
+ not its runtime — is now **structurally** gone: the production dependency
14
+ graph contains zero native modules, zero install scripts, and no
15
+ `prebuild-install`/`tar-fs`/`tar-stream` chain. Verified end to end against a
16
+ verdaccio registry reproduction (`scripts/verify-registry-install.sh`):
17
+ consumer tree reified at 94 packages (down from ~124 in 1.17.9), `npm ls`
18
+ exits 0, and no `better-sqlite3`/`tar-stream`/`prebuild-install` appears
19
+ anywhere in the consumer tree.
20
+
21
+ ### BREAKING
22
+
23
+ - **`engines.node` is now `>=24.4.0`** (was `>=20.0.0`). Node 20 is EOL
24
+ (April 2026). The 24.4 floor is required because `node:sqlite`'s
25
+ `allowBareNamedParameters` defaults to `true` only from Node 24.4 — the
26
+ persistence layer binds bare `{ id: ... }` objects to `@id` placeholders
27
+ throughout, and on 24.0-24.3 that would need a per-statement
28
+ `setAllowBareNamedParameters(true)` call. The adapter unit tests assert
29
+ bare-name binding works, so a regression in either direction is caught.
30
+
31
+ ### Added
32
+
33
+ - `src/sqlite-driver.ts`: thin adapter over `node:sqlite`'s `DatabaseSync`.
34
+ Exports `openDatabase`, `openReadOnly`, and a `GatewayDatabase` /
35
+ `GatewayStatement` surface (`exec`/`prepare`/`run`/`get`/`all`/
36
+ `withTransaction`/`close`). It is the ONLY production module that touches
37
+ `node:sqlite`; the release security audit hard-fails if any other
38
+ production module references it. Preserves the flight recorder's
39
+ graceful-degradation path (constructor failure → recorder disabled, gateway
40
+ still runs).
41
+ - Read-only `queryRequests` connection: `openReadOnly` opens the DB with
42
+ `{ readOnly: true }`, so write-disguised-as-read SQL fails at the SQLite
43
+ engine level (`SQLITE_READONLY`). This is **stronger** than the old
44
+ better-sqlite3 `stmt.readonly` JS-property check it replaces — enforcement
45
+ is at the engine, not in JavaScript — with one belt-and-braces guard: the
46
+ read-only connection also rejects `VACUUM`/`VACUUM INTO`, the one statement
47
+ that writes a new file to disk despite `{ readOnly: true }` (and that
48
+ `stmt.readonly` previously blocked). ATTACH-then-write and
49
+ `writable_schema` schema edits are already engine-rejected.
50
+ - Cross-engine WAL crash-recovery fixtures in both directions
51
+ (`src/__tests__/cross-engine-wal.test.ts`): a `better-sqlite3`-written DB
52
+ (SQLite 3.53.1) with live `-wal`/`-shm` from a simulated unclean stop is
53
+ opened and exercised under `node:sqlite` (3.51.3), and the reverse for the
54
+ rollback direction. These gate the "zero data migration" claim across the
55
+ engine-version skew.
56
+
57
+ ### Changed
58
+
59
+ - `better-sqlite3` **moved from `dependencies` to `devDependencies`** (same
60
+ `^12.10.0` range; `@types/better-sqlite3` stays in devDependencies). It is
61
+ retained at dev time deliberately: two suites seed legacy-schema DB files
62
+ with it (`src/__tests__/flight-recorder.test.ts`,
63
+ `src/__tests__/test-veracity-regressions-slice-kappa.test.ts`) to simulate
64
+ databases written by pre-2.0.0 gateways — that realism is the point, and it
65
+ makes them standing old-engine-writer → node:sqlite-reader coverage on every
66
+ CI run — and the cross-engine WAL fixtures need a better-sqlite3 writer.
67
+ Consumers never see it: devDependencies do not install transitively, and the
68
+ prod-only shrinkwrap excludes the whole subtree.
69
+ - `flight-recorder.ts` / `job-store.ts` now open SQLite through the adapter
70
+ (`openDatabase`/`openReadOnly`/`withTransaction`) instead of
71
+ `require("better-sqlite3")`. SQL, schema, migrations, and pragmas are
72
+ unchanged.
73
+ - `package.json#overrides`: the `tar-stream` pin is **removed** (the chain
74
+ that needed it is gone from the prod graph). The `type-is` and `content-type`
75
+ pins stay — unrelated to this chain.
76
+ - `scripts/release-security-audit.sh`: the `consumerAdvisory` carve-out is
77
+ **deleted** — blocked `tar-stream` versions are now hard-fail tripwires
78
+ everywhere (the chain no longer exists in any prod tree). The packed-consumer
79
+ policy now hard-fails on ANY `tar-stream` in the consumer tree (was an
80
+ advisory warning). The repo-lockfile tripwire skips dev-only entries so the
81
+ deliberate devDependency `tar-stream@2.2.0` does not false-fail, while still
82
+ hard-failing any blocked version that re-enters the prod graph. The
83
+ better-sqlite3 PRAGMA scan is repointed at the adapter: it now also asserts
84
+ `node:sqlite` is referenced only by `src/sqlite-driver.ts`.
85
+ - `scripts/pre-release.sh`: the better-sqlite3 native-binding sanity guard is
86
+ removed (the test suite exercises the binding as a devDep and fails loudly if
87
+ broken); the `npm ls tar-stream` step is replaced by an absence assertion
88
+ against the generated prod-only shrinkwrap
89
+ (`better-sqlite3`/`prebuild-install`/`tar-fs`/`tar-stream` must be absent).
90
+ - `scripts/verify-registry-install.sh`: assertions updated for 2.0.0 —
91
+ `tar-stream`/`better-sqlite3`/`prebuild-install` must be ABSENT from the
92
+ consumer tree; consumer `npm ls` must exit 0 (the out-of-range pin that
93
+ caused ELSPROBLEMS is gone); a `node:sqlite` runtime smoke
94
+ (`new DatabaseSync(':memory:')`) confirms the engine; and the reified package
95
+ count is asserted at 94 ±2.
96
+ - README, `socket.yml`, and `docs/personal-mcp/RELEASE_READINESS.md` updated to
97
+ reflect the node:sqlite reality (no native binding, no install scripts,
98
+ Node >=24.4.0, adapter-isolation audit replacing the PRAGMA-helper note).
99
+
100
+ ### Rollback
101
+
102
+ Reverting the 2.0.0 commit re-adds `better-sqlite3` to `dependencies`, the
103
+ `tar-stream` override, and the audit advisory carve-out. DB files are
104
+ compatible in both directions — exactly what the cross-engine WAL fixtures
105
+ prove (the rollback claim inherits that gate; it is not asserted
106
+ independently).
107
+
7
108
  ## [1.17.9] - 2026-06-04: prod-only shrinkwrap + registry-fidelity verification
8
109
 
9
110
  Patch release shipping a prod-only `npm-shrinkwrap.json` and correcting the
package/README.md CHANGED
@@ -214,6 +214,8 @@ Opt-in flags (all default off) live under `[cache_awareness]` in `~/.llm-cli-gat
214
214
 
215
215
  ## Prerequisites
216
216
 
217
+ **Node.js >= 24.4.0** is required (`engines.node` in `package.json`). The gateway uses Node's built-in `node:sqlite` module for persistence — there is no native binding to compile and no install scripts run. The 24.4 floor is where `allowBareNamedParameters` defaults to `true`, which the persistence layer relies on.
218
+
217
219
  Before using this gateway, you need to install the CLI tools you want to use:
218
220
 
219
221
  ### Claude Code CLI
@@ -1180,8 +1182,8 @@ If you're vetting `llm-cli-gateway` through [Socket](https://socket.dev/npm/pack
1180
1182
  | **Network access** | `src/http-transport.ts` opens an HTTP MCP transport when started via `npm run start:http`. `src/endpoint-exposure.ts` issues a HEAD probe to verify configured public/tunnel URLs. Socket also flagged `dist/upstream-contracts.js` in v1.17.2 from descriptive text, not a network call. | The transport binds to `127.0.0.1` by default and requires `LLM_GATEWAY_AUTH_TOKEN` to be set. The default stdio MCP entry point (`npm start`) opens no sockets. `src/upstream-contracts.ts` stores provider CLI metadata and imports no HTTP client APIs. |
1181
1183
  | **Shell access** | `src/executor.ts` uses `child_process.spawn(cmd, args, …)` to invoke the underlying LLM CLIs. | `spawn` is called with an argument array and **never** `shell: true`, so there is no shell interpolation path for caller input. The command name is restricted to an allow-list of known CLI binaries (`claude`, `codex`, `gemini`, `grok`, `vibe`). |
1182
1184
  | **Uses eval** | None in our source. Transitive: `@modelcontextprotocol/sdk` → `ajv@8` uses `new Function(...)` in `ajv/dist/compile/index.js` to compile JSON Schema validators. | This is ajv's standard codegen path. Only known schemas (defined in our source and the MCP SDK) flow into it; no caller-supplied data ever reaches the compiled function body. |
1183
- | **better-sqlite3 PRAGMA helper** | Transitive: `better-sqlite3/lib/methods/pragma.js` interpolates its caller-provided `source` into a `PRAGMA ${source}` statement. | We do not call `db.pragma()` from production source. Internal SQLite setup uses fixed literal `db.exec("PRAGMA ...")` statements, and `npm run security:audit` fails the release if production code reintroduces `.pragma()` calls. |
1184
- | **Dependency ownership** | A handful of small transitive packages (e.g. `bindings` via `better-sqlite3`, `media-typer` via `@modelcontextprotocol/sdk`) trip Socket's "unstable ownership" or "obfuscated code" heuristics. | These are pinned, well-known micro-deps in the Node ecosystem with no known issues. We pin direct override versions of `content-type` and `type-is` in `package.json#overrides`. Our previous direct dependency on `toml@3.0.0` (also single-maintainer, last released 2020) was replaced with the actively-maintained `smol-toml` to reduce inherited risk. |
1185
+ | **SQLite adapter isolation** | Persistence uses Node's built-in `node:sqlite` module (no native binding, no install scripts) through a single adapter, `src/sqlite-driver.ts`. | `node:sqlite` is touched by exactly one production module (the adapter); every other module talks to SQLite through its typed surface. We never call any `db.pragma()` helper (it does not exist on `node:sqlite`); SQLite setup uses fixed literal `db.exec("PRAGMA ...")` statements. `npm run security:audit` fails the release if production code references `node:sqlite` outside the adapter or reintroduces a `.pragma()` call. |
1186
+ | **Dependency ownership** | A handful of small transitive packages (e.g. `media-typer` via `@modelcontextprotocol/sdk`) trip Socket's "unstable ownership" or "obfuscated code" heuristics. | These are pinned, well-known micro-deps in the Node ecosystem with no known issues. We pin direct override versions of `content-type` and `type-is` in `package.json#overrides`. As of 2.0.0 the prod graph carries no native module (`better-sqlite3` moved to devDependencies; `node:sqlite` is built into Node), eliminating the entire `prebuild-install`/`tar-fs`/`tar-stream` install-time chain. Our earlier direct dependency on `toml@3.0.0` was replaced with `smol-toml`. |
1185
1187
 
1186
1188
  See [`socket.yml`](./socket.yml) for the same context in machine-readable form.
1187
1189
 
@@ -34,6 +34,9 @@ interface LoggerLike {
34
34
  export declare function resolveFlightRecorderDbPath(): string | null;
35
35
  export declare class FlightRecorder {
36
36
  private db;
37
+ private readOnlyDb;
38
+ private closed;
39
+ private readonly dbPath;
37
40
  private insertStartTxn;
38
41
  private updateCompleteTxn;
39
42
  constructor(dbPath: string);
@@ -1,10 +1,10 @@
1
- import { chmodSync, existsSync, mkdirSync } from "fs";
1
+ import { chmodSync } from "fs";
2
2
  import os from "os";
3
3
  import path from "path";
4
- import { createRequire } from "module";
4
+ import { openDatabase, openReadOnly } from "./sqlite-driver.js";
5
5
  const MAX_THINKING_BYTES = 1_000_000;
6
6
  function ensureRequestsCacheColumns(db) {
7
- const rows = db.prepare("PRAGMA table_info(requests)").all?.() ?? [];
7
+ const rows = db.prepare("PRAGMA table_info(requests)").all();
8
8
  const names = new Set(rows.map((row) => (row && typeof row.name === "string" ? row.name : "")));
9
9
  if (!names.has("cache_read_tokens")) {
10
10
  db.exec("ALTER TABLE requests ADD COLUMN cache_read_tokens INTEGER");
@@ -14,7 +14,7 @@ function ensureRequestsCacheColumns(db) {
14
14
  }
15
15
  }
16
16
  function ensureStablePrefixColumns(db) {
17
- const rows = db.prepare("PRAGMA table_info(requests)").all?.() ?? [];
17
+ const rows = db.prepare("PRAGMA table_info(requests)").all();
18
18
  const names = new Set(rows.map((row) => (row && typeof row.name === "string" ? row.name : "")));
19
19
  if (!names.has("stable_prefix_hash")) {
20
20
  db.exec("ALTER TABLE requests ADD COLUMN stable_prefix_hash TEXT");
@@ -25,7 +25,7 @@ function ensureStablePrefixColumns(db) {
25
25
  db.exec("CREATE INDEX IF NOT EXISTS idx_requests_stable_hash ON requests(stable_prefix_hash)");
26
26
  }
27
27
  function ensureCacheControlBlocksColumn(db) {
28
- const rows = db.prepare("PRAGMA table_info(requests)").all?.() ?? [];
28
+ const rows = db.prepare("PRAGMA table_info(requests)").all();
29
29
  const names = new Set(rows.map((row) => (row && typeof row.name === "string" ? row.name : "")));
30
30
  if (!names.has("cache_control_blocks")) {
31
31
  db.exec("ALTER TABLE requests ADD COLUMN cache_control_blocks INTEGER");
@@ -77,16 +77,14 @@ function truncateThinkingBlocks(blocks) {
77
77
  }
78
78
  export class FlightRecorder {
79
79
  db;
80
+ readOnlyDb = null;
81
+ closed = false;
82
+ dbPath;
80
83
  insertStartTxn;
81
84
  updateCompleteTxn;
82
85
  constructor(dbPath) {
83
- const require = createRequire(import.meta.url);
84
- const BetterSqlite3 = require("better-sqlite3");
85
- const directory = path.dirname(dbPath);
86
- if (!existsSync(directory)) {
87
- mkdirSync(directory, { recursive: true });
88
- }
89
- this.db = new BetterSqlite3(dbPath);
86
+ this.dbPath = dbPath;
87
+ this.db = openDatabase(dbPath);
90
88
  this.db.exec("PRAGMA journal_mode = WAL");
91
89
  this.db.exec("PRAGMA foreign_keys = ON");
92
90
  this.db.exec(`
@@ -165,7 +163,7 @@ export class FlightRecorder {
165
163
  INSERT INTO gateway_metadata (request_id, async_job_id, status)
166
164
  VALUES (@request_id, @async_job_id, 'started')
167
165
  `);
168
- this.insertStartTxn = this.db.transaction((entry) => {
166
+ this.insertStartTxn = this.db.withTransaction((entry) => {
169
167
  insertRequest.run({
170
168
  id: entry.correlationId,
171
169
  cli: entry.cli,
@@ -206,7 +204,7 @@ export class FlightRecorder {
206
204
  status = @status
207
205
  WHERE request_id = @id AND status = 'started'
208
206
  `);
209
- this.updateCompleteTxn = this.db.transaction((correlationId, result) => {
207
+ this.updateCompleteTxn = this.db.withTransaction((correlationId, result) => {
210
208
  const thinkingBlocks = result.thinkingBlocks && result.thinkingBlocks.length > 0
211
209
  ? JSON.stringify(truncateThinkingBlocks(result.thinkingBlocks))
212
210
  : null;
@@ -240,18 +238,22 @@ export class FlightRecorder {
240
238
  this.updateCompleteTxn(correlationId, result);
241
239
  }
242
240
  queryRequests(sql, ...params) {
243
- const stmt = this.db.prepare(sql);
244
- if (stmt.readonly === false) {
245
- throw new Error("FlightRecorder.queryRequests refuses non-readonly SQL — use a transaction or a separate write surface for INSERT/UPDATE/DELETE.");
241
+ if (this.closed) {
242
+ throw new Error("flight recorder is closed");
246
243
  }
247
- if (!stmt.all) {
248
- return [];
244
+ if (!this.readOnlyDb) {
245
+ this.readOnlyDb = openReadOnly(this.dbPath);
249
246
  }
250
- return stmt.all(...params);
247
+ return this.readOnlyDb.prepare(sql).all(...params);
251
248
  }
252
249
  flush() {
253
250
  }
254
251
  close() {
252
+ this.closed = true;
253
+ if (this.readOnlyDb) {
254
+ this.readOnlyDb.close();
255
+ this.readOnlyDb = null;
256
+ }
255
257
  this.db.close();
256
258
  }
257
259
  }
package/dist/job-store.js CHANGED
@@ -1,8 +1,8 @@
1
- import { chmodSync, existsSync, mkdirSync } from "fs";
1
+ import { chmodSync } from "fs";
2
2
  import os from "os";
3
3
  import path from "path";
4
4
  import { createHash } from "crypto";
5
- import { createRequire } from "module";
5
+ import { openDatabase } from "./sqlite-driver.js";
6
6
  import { noopLogger } from "./logger.js";
7
7
  export function resolveJobStoreDbPath() {
8
8
  const configured = process.env.LLM_GATEWAY_JOBS_DB ?? process.env.LLM_GATEWAY_LOGS_DB;
@@ -74,13 +74,7 @@ export class SqliteJobStore {
74
74
  deleteExpiredStmt;
75
75
  constructor(dbPath, logger = noopLogger, options = {}) {
76
76
  this.logger = logger;
77
- const require = createRequire(import.meta.url);
78
- const BetterSqlite3 = require("better-sqlite3");
79
- const directory = path.dirname(dbPath);
80
- if (!existsSync(directory)) {
81
- mkdirSync(directory, { recursive: true });
82
- }
83
- this.db = new BetterSqlite3(dbPath);
77
+ this.db = openDatabase(dbPath);
84
78
  this.db.exec("PRAGMA journal_mode = WAL");
85
79
  this.db.exec("PRAGMA synchronous = NORMAL");
86
80
  this.db.exec(`
@@ -211,7 +205,7 @@ export class SqliteJobStore {
211
205
  markOrphanedOnStartup() {
212
206
  const now = new Date().toISOString();
213
207
  const expiresAt = new Date(Date.now() + this.retentionMs).toISOString();
214
- const rows = (this.selectRunningOrphansStmt.all?.() ?? []);
208
+ const rows = this.selectRunningOrphansStmt.all();
215
209
  const orphaned = rows.map(row => ({
216
210
  id: row.id,
217
211
  correlationId: row.correlation_id,
@@ -221,12 +215,12 @@ export class SqliteJobStore {
221
215
  exitCode: row.exit_code,
222
216
  }));
223
217
  const result = this.markOrphanedStmt.run(now, expiresAt);
224
- return { count: result?.changes ?? 0, orphaned };
218
+ return { count: Number(result.changes), orphaned };
225
219
  }
226
220
  evictExpired() {
227
221
  const now = new Date().toISOString();
228
222
  const result = this.deleteExpiredStmt.run(now);
229
- return result?.changes ?? 0;
223
+ return Number(result.changes);
230
224
  }
231
225
  close() {
232
226
  try {
@@ -0,0 +1,16 @@
1
+ export interface GatewayStatement {
2
+ run(...args: unknown[]): {
3
+ changes: number;
4
+ lastInsertRowid: number | bigint;
5
+ };
6
+ get(...args: unknown[]): unknown;
7
+ all(...args: unknown[]): unknown[];
8
+ }
9
+ export interface GatewayDatabase {
10
+ exec(sql: string): void;
11
+ prepare(sql: string): GatewayStatement;
12
+ withTransaction<A extends unknown[]>(fn: (...args: A) => void): (...args: A) => void;
13
+ close(): void;
14
+ }
15
+ export declare function openDatabase(dbPath: string): GatewayDatabase;
16
+ export declare function openReadOnly(dbPath: string): GatewayDatabase;
@@ -0,0 +1,149 @@
1
+ import { existsSync, mkdirSync } from "fs";
2
+ import path from "path";
3
+ import { createRequire } from "module";
4
+ function loadNodeSqlite() {
5
+ const require = createRequire(import.meta.url);
6
+ return require("node:sqlite");
7
+ }
8
+ function wrapStatement(stmt) {
9
+ return {
10
+ run(...args) {
11
+ return stmt.run(...args);
12
+ },
13
+ get(...args) {
14
+ return stmt.get(...args);
15
+ },
16
+ all(...args) {
17
+ return stmt.all(...args);
18
+ },
19
+ };
20
+ }
21
+ function statementLeadingKeywords(sql) {
22
+ const keywords = [];
23
+ let i = 0;
24
+ const skipTrivia = () => {
25
+ for (;;) {
26
+ while (i < sql.length && /\s|;/.test(sql[i] ?? ""))
27
+ i++;
28
+ if (sql.startsWith("--", i)) {
29
+ i += 2;
30
+ while (i < sql.length && sql[i] !== "\n")
31
+ i++;
32
+ continue;
33
+ }
34
+ if (sql.startsWith("/*", i)) {
35
+ const end = sql.indexOf("*/", i + 2);
36
+ i = end === -1 ? sql.length : end + 2;
37
+ continue;
38
+ }
39
+ break;
40
+ }
41
+ };
42
+ const skipQuoted = (quote) => {
43
+ i++;
44
+ while (i < sql.length) {
45
+ if (sql[i] === quote) {
46
+ if (sql[i + 1] === quote) {
47
+ i += 2;
48
+ continue;
49
+ }
50
+ i++;
51
+ return;
52
+ }
53
+ i++;
54
+ }
55
+ };
56
+ while (i < sql.length) {
57
+ skipTrivia();
58
+ const m = /^[a-zA-Z]+/.exec(sql.slice(i));
59
+ if (m) {
60
+ keywords.push(m[0].toUpperCase());
61
+ }
62
+ while (i < sql.length && sql[i] !== ";") {
63
+ if (sql.startsWith("--", i)) {
64
+ i += 2;
65
+ while (i < sql.length && sql[i] !== "\n")
66
+ i++;
67
+ }
68
+ else if (sql.startsWith("/*", i)) {
69
+ const end = sql.indexOf("*/", i + 2);
70
+ i = end === -1 ? sql.length : end + 2;
71
+ }
72
+ else if (sql[i] === "'" || sql[i] === '"' || sql[i] === "`") {
73
+ skipQuoted(sql[i]);
74
+ }
75
+ else if (sql[i] === "[") {
76
+ i++;
77
+ while (i < sql.length && sql[i] !== "]")
78
+ i++;
79
+ if (i < sql.length)
80
+ i++;
81
+ }
82
+ else {
83
+ i++;
84
+ }
85
+ }
86
+ }
87
+ return keywords;
88
+ }
89
+ class GatewayDatabaseImpl {
90
+ db;
91
+ readOnly;
92
+ inTransaction = false;
93
+ constructor(db, readOnly = false) {
94
+ this.db = db;
95
+ this.readOnly = readOnly;
96
+ }
97
+ guardReadOnly(sql) {
98
+ if (this.readOnly && statementLeadingKeywords(sql).includes("VACUUM")) {
99
+ throw new Error("read-only connection rejects VACUUM (writes to disk despite readOnly)");
100
+ }
101
+ }
102
+ exec(sql) {
103
+ this.guardReadOnly(sql);
104
+ this.db.exec(sql);
105
+ }
106
+ prepare(sql) {
107
+ this.guardReadOnly(sql);
108
+ return wrapStatement(this.db.prepare(sql));
109
+ }
110
+ withTransaction(fn) {
111
+ return (...args) => {
112
+ if (this.inTransaction) {
113
+ throw new Error("nested transaction");
114
+ }
115
+ this.db.exec("BEGIN");
116
+ this.inTransaction = true;
117
+ try {
118
+ fn(...args);
119
+ this.db.exec("COMMIT");
120
+ }
121
+ catch (error) {
122
+ try {
123
+ this.db.exec("ROLLBACK");
124
+ }
125
+ catch {
126
+ }
127
+ throw error;
128
+ }
129
+ finally {
130
+ this.inTransaction = false;
131
+ }
132
+ };
133
+ }
134
+ close() {
135
+ this.db.close();
136
+ }
137
+ }
138
+ export function openDatabase(dbPath) {
139
+ const { DatabaseSync } = loadNodeSqlite();
140
+ const directory = path.dirname(dbPath);
141
+ if (!existsSync(directory)) {
142
+ mkdirSync(directory, { recursive: true });
143
+ }
144
+ return new GatewayDatabaseImpl(new DatabaseSync(dbPath));
145
+ }
146
+ export function openReadOnly(dbPath) {
147
+ const { DatabaseSync } = loadNodeSqlite();
148
+ return new GatewayDatabaseImpl(new DatabaseSync(dbPath, { readOnly: true }), true);
149
+ }
@@ -1,16 +1,15 @@
1
1
  {
2
2
  "name": "llm-cli-gateway",
3
- "version": "1.17.9",
3
+ "version": "2.0.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "llm-cli-gateway",
9
- "version": "1.17.9",
9
+ "version": "2.0.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@modelcontextprotocol/sdk": "^1.29.0",
13
- "better-sqlite3": "^12.10.0",
14
13
  "content-type": "1.0.5",
15
14
  "smol-toml": "^1.6.1",
16
15
  "type-is": "2.0.1",
@@ -20,7 +19,7 @@
20
19
  "llm-cli-gateway": "dist/index.js"
21
20
  },
22
21
  "engines": {
23
- "node": ">=20.0.0"
22
+ "node": ">=24.4.0"
24
23
  },
25
24
  "peerDependencies": {
26
25
  "pg": "^8.12.0"
@@ -129,35 +128,6 @@
129
128
  }
130
129
  }
131
130
  },
132
- "node_modules/b4a": {
133
- "version": "1.6.4",
134
- "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
135
- "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==",
136
- "license": "ISC"
137
- },
138
- "node_modules/better-sqlite3": {
139
- "version": "12.10.0",
140
- "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.10.0.tgz",
141
- "integrity": "sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==",
142
- "hasInstallScript": true,
143
- "license": "MIT",
144
- "dependencies": {
145
- "bindings": "^1.5.0",
146
- "prebuild-install": "^7.1.1"
147
- },
148
- "engines": {
149
- "node": "20.x || 22.x || 23.x || 24.x || 25.x || 26.x"
150
- }
151
- },
152
- "node_modules/bindings": {
153
- "version": "1.5.0",
154
- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
155
- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
156
- "license": "MIT",
157
- "dependencies": {
158
- "file-uri-to-path": "1.0.0"
159
- }
160
- },
161
131
  "node_modules/body-parser": {
162
132
  "version": "2.2.2",
163
133
  "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
@@ -220,12 +190,6 @@
220
190
  "url": "https://github.com/sponsors/ljharb"
221
191
  }
222
192
  },
223
- "node_modules/chownr": {
224
- "version": "1.1.4",
225
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
226
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
227
- "license": "ISC"
228
- },
229
193
  "node_modules/content-disposition": {
230
194
  "version": "1.1.0",
231
195
  "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
@@ -314,30 +278,6 @@
314
278
  }
315
279
  }
316
280
  },
317
- "node_modules/decompress-response": {
318
- "version": "6.0.0",
319
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
320
- "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
321
- "license": "MIT",
322
- "dependencies": {
323
- "mimic-response": "^3.1.0"
324
- },
325
- "engines": {
326
- "node": ">=10"
327
- },
328
- "funding": {
329
- "url": "https://github.com/sponsors/sindresorhus"
330
- }
331
- },
332
- "node_modules/deep-extend": {
333
- "version": "0.6.0",
334
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
335
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
336
- "license": "MIT",
337
- "engines": {
338
- "node": ">=4.0.0"
339
- }
340
- },
341
281
  "node_modules/depd": {
342
282
  "version": "2.0.0",
343
283
  "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -347,15 +287,6 @@
347
287
  "node": ">= 0.8"
348
288
  }
349
289
  },
350
- "node_modules/detect-libc": {
351
- "version": "2.1.2",
352
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
353
- "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
354
- "license": "Apache-2.0",
355
- "engines": {
356
- "node": ">=8"
357
- }
358
- },
359
290
  "node_modules/dunder-proto": {
360
291
  "version": "1.0.1",
361
292
  "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -385,15 +316,6 @@
385
316
  "node": ">= 0.8"
386
317
  }
387
318
  },
388
- "node_modules/end-of-stream": {
389
- "version": "1.4.5",
390
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
391
- "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
392
- "license": "MIT",
393
- "dependencies": {
394
- "once": "^1.4.0"
395
- }
396
- },
397
319
  "node_modules/es-define-property": {
398
320
  "version": "1.0.1",
399
321
  "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -460,15 +382,6 @@
460
382
  "node": ">=18.0.0"
461
383
  }
462
384
  },
463
- "node_modules/expand-template": {
464
- "version": "2.0.3",
465
- "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
466
- "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
467
- "license": "(MIT OR WTFPL)",
468
- "engines": {
469
- "node": ">=6"
470
- }
471
- },
472
385
  "node_modules/express": {
473
386
  "version": "5.2.1",
474
387
  "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
@@ -536,12 +449,6 @@
536
449
  "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
537
450
  "license": "MIT"
538
451
  },
539
- "node_modules/fast-fifo": {
540
- "version": "1.3.2",
541
- "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
542
- "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
543
- "license": "MIT"
544
- },
545
452
  "node_modules/fast-uri": {
546
453
  "version": "3.1.2",
547
454
  "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
@@ -558,12 +465,6 @@
558
465
  ],
559
466
  "license": "BSD-3-Clause"
560
467
  },
561
- "node_modules/file-uri-to-path": {
562
- "version": "1.0.0",
563
- "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
564
- "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
565
- "license": "MIT"
566
- },
567
468
  "node_modules/finalhandler": {
568
469
  "version": "2.1.1",
569
470
  "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
@@ -649,12 +550,6 @@
649
550
  "node": ">= 0.4"
650
551
  }
651
552
  },
652
- "node_modules/github-from-package": {
653
- "version": "0.0.0",
654
- "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
655
- "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
656
- "license": "MIT"
657
- },
658
553
  "node_modules/gopd": {
659
554
  "version": "1.2.0",
660
555
  "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -742,12 +637,6 @@
742
637
  "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
743
638
  "license": "ISC"
744
639
  },
745
- "node_modules/ini": {
746
- "version": "1.3.8",
747
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
748
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
749
- "license": "ISC"
750
- },
751
640
  "node_modules/ip-address": {
752
641
  "version": "10.2.0",
753
642
  "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
@@ -854,45 +743,12 @@
854
743
  "url": "https://opencollective.com/express"
855
744
  }
856
745
  },
857
- "node_modules/mimic-response": {
858
- "version": "3.1.0",
859
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
860
- "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
861
- "license": "MIT",
862
- "engines": {
863
- "node": ">=10"
864
- },
865
- "funding": {
866
- "url": "https://github.com/sponsors/sindresorhus"
867
- }
868
- },
869
- "node_modules/minimist": {
870
- "version": "1.2.8",
871
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
872
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
873
- "license": "MIT",
874
- "funding": {
875
- "url": "https://github.com/sponsors/ljharb"
876
- }
877
- },
878
- "node_modules/mkdirp-classic": {
879
- "version": "0.5.3",
880
- "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
881
- "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
882
- "license": "MIT"
883
- },
884
746
  "node_modules/ms": {
885
747
  "version": "2.1.3",
886
748
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
887
749
  "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
888
750
  "license": "MIT"
889
751
  },
890
- "node_modules/napi-build-utils": {
891
- "version": "2.0.0",
892
- "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
893
- "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
894
- "license": "MIT"
895
- },
896
752
  "node_modules/negotiator": {
897
753
  "version": "1.0.0",
898
754
  "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
@@ -902,18 +758,6 @@
902
758
  "node": ">= 0.6"
903
759
  }
904
760
  },
905
- "node_modules/node-abi": {
906
- "version": "3.89.0",
907
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz",
908
- "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==",
909
- "license": "MIT",
910
- "dependencies": {
911
- "semver": "^7.3.5"
912
- },
913
- "engines": {
914
- "node": ">=10"
915
- }
916
- },
917
761
  "node_modules/object-assign": {
918
762
  "version": "4.1.1",
919
763
  "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -993,33 +837,6 @@
993
837
  "node": ">=16.20.0"
994
838
  }
995
839
  },
996
- "node_modules/prebuild-install": {
997
- "version": "7.1.3",
998
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
999
- "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
1000
- "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
1001
- "license": "MIT",
1002
- "dependencies": {
1003
- "detect-libc": "^2.0.0",
1004
- "expand-template": "^2.0.3",
1005
- "github-from-package": "0.0.0",
1006
- "minimist": "^1.2.3",
1007
- "mkdirp-classic": "^0.5.3",
1008
- "napi-build-utils": "^2.0.0",
1009
- "node-abi": "^3.3.0",
1010
- "pump": "^3.0.0",
1011
- "rc": "^1.2.7",
1012
- "simple-get": "^4.0.0",
1013
- "tar-fs": "^2.0.0",
1014
- "tunnel-agent": "^0.6.0"
1015
- },
1016
- "bin": {
1017
- "prebuild-install": "bin.js"
1018
- },
1019
- "engines": {
1020
- "node": ">=10"
1021
- }
1022
- },
1023
840
  "node_modules/proxy-addr": {
1024
841
  "version": "2.0.7",
1025
842
  "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -1033,16 +850,6 @@
1033
850
  "node": ">= 0.10"
1034
851
  }
1035
852
  },
1036
- "node_modules/pump": {
1037
- "version": "3.0.4",
1038
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
1039
- "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
1040
- "license": "MIT",
1041
- "dependencies": {
1042
- "end-of-stream": "^1.1.0",
1043
- "once": "^1.3.1"
1044
- }
1045
- },
1046
853
  "node_modules/qs": {
1047
854
  "version": "6.15.2",
1048
855
  "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
@@ -1058,12 +865,6 @@
1058
865
  "url": "https://github.com/sponsors/ljharb"
1059
866
  }
1060
867
  },
1061
- "node_modules/queue-tick": {
1062
- "version": "1.0.1",
1063
- "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
1064
- "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
1065
- "license": "MIT"
1066
- },
1067
868
  "node_modules/range-parser": {
1068
869
  "version": "1.2.1",
1069
870
  "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -1088,30 +889,6 @@
1088
889
  "node": ">= 0.10"
1089
890
  }
1090
891
  },
1091
- "node_modules/rc": {
1092
- "version": "1.2.8",
1093
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
1094
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
1095
- "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
1096
- "dependencies": {
1097
- "deep-extend": "^0.6.0",
1098
- "ini": "~1.3.0",
1099
- "minimist": "^1.2.0",
1100
- "strip-json-comments": "~2.0.1"
1101
- },
1102
- "bin": {
1103
- "rc": "cli.js"
1104
- }
1105
- },
1106
- "node_modules/rc/node_modules/strip-json-comments": {
1107
- "version": "2.0.1",
1108
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
1109
- "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
1110
- "license": "MIT",
1111
- "engines": {
1112
- "node": ">=0.10.0"
1113
- }
1114
- },
1115
892
  "node_modules/require-from-string": {
1116
893
  "version": "2.0.2",
1117
894
  "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -1137,44 +914,12 @@
1137
914
  "node": ">= 18"
1138
915
  }
1139
916
  },
1140
- "node_modules/safe-buffer": {
1141
- "version": "5.2.1",
1142
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1143
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1144
- "funding": [
1145
- {
1146
- "type": "github",
1147
- "url": "https://github.com/sponsors/feross"
1148
- },
1149
- {
1150
- "type": "patreon",
1151
- "url": "https://www.patreon.com/feross"
1152
- },
1153
- {
1154
- "type": "consulting",
1155
- "url": "https://feross.org/support"
1156
- }
1157
- ],
1158
- "license": "MIT"
1159
- },
1160
917
  "node_modules/safer-buffer": {
1161
918
  "version": "2.1.2",
1162
919
  "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1163
920
  "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1164
921
  "license": "MIT"
1165
922
  },
1166
- "node_modules/semver": {
1167
- "version": "7.7.3",
1168
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
1169
- "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
1170
- "license": "ISC",
1171
- "bin": {
1172
- "semver": "bin/semver.js"
1173
- },
1174
- "engines": {
1175
- "node": ">=10"
1176
- }
1177
- },
1178
923
  "node_modules/send": {
1179
924
  "version": "1.2.1",
1180
925
  "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
@@ -1319,51 +1064,6 @@
1319
1064
  "url": "https://github.com/sponsors/ljharb"
1320
1065
  }
1321
1066
  },
1322
- "node_modules/simple-concat": {
1323
- "version": "1.0.1",
1324
- "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
1325
- "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
1326
- "funding": [
1327
- {
1328
- "type": "github",
1329
- "url": "https://github.com/sponsors/feross"
1330
- },
1331
- {
1332
- "type": "patreon",
1333
- "url": "https://www.patreon.com/feross"
1334
- },
1335
- {
1336
- "type": "consulting",
1337
- "url": "https://feross.org/support"
1338
- }
1339
- ],
1340
- "license": "MIT"
1341
- },
1342
- "node_modules/simple-get": {
1343
- "version": "4.0.1",
1344
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
1345
- "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
1346
- "funding": [
1347
- {
1348
- "type": "github",
1349
- "url": "https://github.com/sponsors/feross"
1350
- },
1351
- {
1352
- "type": "patreon",
1353
- "url": "https://www.patreon.com/feross"
1354
- },
1355
- {
1356
- "type": "consulting",
1357
- "url": "https://feross.org/support"
1358
- }
1359
- ],
1360
- "license": "MIT",
1361
- "dependencies": {
1362
- "decompress-response": "^6.0.0",
1363
- "once": "^1.3.1",
1364
- "simple-concat": "^1.0.0"
1365
- }
1366
- },
1367
1067
  "node_modules/smol-toml": {
1368
1068
  "version": "1.6.1",
1369
1069
  "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
@@ -1385,39 +1085,6 @@
1385
1085
  "node": ">= 0.8"
1386
1086
  }
1387
1087
  },
1388
- "node_modules/streamx": {
1389
- "version": "2.15.0",
1390
- "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.0.tgz",
1391
- "integrity": "sha512-HcxY6ncGjjklGs1xsP1aR71INYcsXFJet5CU1CHqihQ2J5nOsbd4OjgjHO42w/4QNv9gZb3BueV+Vxok5pLEXg==",
1392
- "license": "MIT",
1393
- "dependencies": {
1394
- "fast-fifo": "^1.1.0",
1395
- "queue-tick": "^1.0.1"
1396
- }
1397
- },
1398
- "node_modules/tar-fs": {
1399
- "version": "2.1.4",
1400
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
1401
- "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
1402
- "license": "MIT",
1403
- "dependencies": {
1404
- "chownr": "^1.1.1",
1405
- "mkdirp-classic": "^0.5.2",
1406
- "pump": "^3.0.0",
1407
- "tar-stream": "^2.1.4"
1408
- }
1409
- },
1410
- "node_modules/tar-stream": {
1411
- "version": "3.1.7",
1412
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
1413
- "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
1414
- "license": "MIT",
1415
- "dependencies": {
1416
- "b4a": "^1.6.4",
1417
- "fast-fifo": "^1.2.0",
1418
- "streamx": "^2.15.0"
1419
- }
1420
- },
1421
1088
  "node_modules/toidentifier": {
1422
1089
  "version": "1.0.1",
1423
1090
  "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -1427,18 +1094,6 @@
1427
1094
  "node": ">=0.6"
1428
1095
  }
1429
1096
  },
1430
- "node_modules/tunnel-agent": {
1431
- "version": "0.6.0",
1432
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
1433
- "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
1434
- "license": "Apache-2.0",
1435
- "dependencies": {
1436
- "safe-buffer": "^5.0.1"
1437
- },
1438
- "engines": {
1439
- "node": "*"
1440
- }
1441
- },
1442
1097
  "node_modules/type-is": {
1443
1098
  "version": "2.0.1",
1444
1099
  "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-cli-gateway",
3
- "version": "1.17.9",
3
+ "version": "2.0.0",
4
4
  "mcpName": "io.github.verivus-oss/llm-cli-gateway",
5
5
  "description": "MCP server providing unified access to Claude Code, Codex, Gemini, Grok, and Mistral Vibe CLIs with session management, retry logic, async job orchestration, durable job results, and cross-LLM validation.",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  "llm-cli-gateway": "dist/index.js"
41
41
  },
42
42
  "engines": {
43
- "node": ">=20.0.0"
43
+ "node": ">=24.4.0"
44
44
  },
45
45
  "files": [
46
46
  "dist/**/*.js",
@@ -87,7 +87,6 @@
87
87
  },
88
88
  "dependencies": {
89
89
  "@modelcontextprotocol/sdk": "^1.29.0",
90
- "better-sqlite3": "^12.10.0",
91
90
  "content-type": "1.0.5",
92
91
  "smol-toml": "^1.6.1",
93
92
  "type-is": "2.0.1",
@@ -104,6 +103,7 @@
104
103
  "devDependencies": {
105
104
  "@eslint/js": "^10.0.1",
106
105
  "@types/better-sqlite3": "^7.6.0",
106
+ "better-sqlite3": "^12.10.0",
107
107
  "@types/node": "^25.9.1",
108
108
  "@types/pg": "^8.11.10",
109
109
  "@typescript-eslint/eslint-plugin": "^8.59.4",
@@ -120,8 +120,7 @@
120
120
  },
121
121
  "overrides": {
122
122
  "type-is": "2.0.1",
123
- "content-type": "1.0.5",
124
- "tar-stream": "3.1.7"
123
+ "content-type": "1.0.5"
125
124
  },
126
125
  "directories": {
127
126
  "doc": "docs"
package/socket.yml CHANGED
@@ -25,10 +25,15 @@ version: 2
25
25
  # imported or called from upstream-contracts.ts. The wording now uses
26
26
  # "remote retrieval" to avoid that heuristic.
27
27
  #
28
- # Transitive tar-stream@2.2.0 (better-sqlite3 → prebuild-install → tar-fs)
29
- # triggered Socket "Potential vulnerability" (tar path traversal at install
30
- # only). v1.17.7+ overrides tar-stream to 3.1.7 and blocks 2.x in the
31
- # release security audit.
28
+ # Historical (resolved in 2.0.0): transitive tar-stream@2.2.0
29
+ # (better-sqlite3 prebuild-install tar-fs) triggered Socket "Potential
30
+ # vulnerability" (tar path traversal at install only). v1.17.7-1.17.9 worked
31
+ # around it with a tar-stream 3.1.7 override + prod-only shrinkwrap. As of
32
+ # 2.0.0 the gateway uses Node's built-in `node:sqlite` for persistence;
33
+ # better-sqlite3 is a devDependency only, so the prod graph has NO native
34
+ # module and NO tar-stream/prebuild-install/tar-fs install chain. The
35
+ # release security audit now hard-fails if any of those packages re-enter
36
+ # the prod graph (and still blocks the flagged tar-stream 2.x versions).
32
37
  #
33
38
  # shellAccess
34
39
  # This alert fires on every module that imports node:child_process, and
@@ -69,12 +74,16 @@ version: 2
69
74
  # This is ajv's standard codegen path; no caller-supplied data flows
70
75
  # into the compiled function body.
71
76
  #
72
- # better-sqlite3 PRAGMA helper
73
- # Socket may flag better-sqlite3/lib/methods/pragma.js because it
74
- # constructs PRAGMA SQL from its caller-provided `source` string. The
75
- # gateway does not call db.pragma() from production code; SQLite setup
76
- # uses fixed literal db.exec("PRAGMA ...") statements, and the release
77
- # security audit fails future production `.pragma()` calls.
77
+ # SQLite (node:sqlite) adapter isolation
78
+ # As of 2.0.0 persistence uses Node's built-in `node:sqlite` module (no
79
+ # native binding, no install scripts) through a single adapter,
80
+ # src/sqlite-driver.ts. No `db.pragma()` helper exists on node:sqlite and
81
+ # the gateway never calls one; SQLite setup uses fixed literal
82
+ # db.exec("PRAGMA ...") statements. The release security audit hard-fails
83
+ # if production code references node:sqlite outside the adapter or
84
+ # reintroduces a `.pragma()` call. (better-sqlite3 — and its
85
+ # lib/methods/pragma.js that older Socket scans flagged — is now a
86
+ # devDependency only and is absent from the published prod artifact.)
78
87
 
79
88
  issueRules:
80
89
  # Defaults from Socket. Listed explicitly so future contributors see what
@@ -84,7 +93,7 @@ issueRules:
84
93
  didYouMean: true
85
94
  installScripts: true
86
95
  telemetry: true
87
- hasNativeCode: true # better-sqlite3 known and expected
96
+ hasNativeCode: true # devDependency-only as of 2.0.0 (better-sqlite3); prod artifact has no native code
88
97
  shellAccess: false # reviewed gateway capability; see rationale above
89
98
  shellScriptOverride: true
90
99
  gitDependency: true