@dbx-app/mcp-server 0.1.3 → 0.2.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.
package/README.md CHANGED
@@ -7,7 +7,7 @@ MCP server for [DBX](https://github.com/t8y2/dbx) — lets AI agents (Claude Cod
7
7
  ## Features
8
8
 
9
9
  - **Zero config** — Automatically reads your DBX connections (including passwords from system keyring)
10
- - **5 tools** — List connections, list tables, describe table, execute SQL, open table in DBX UI
10
+ - **7 tools** — List/add/remove connections, list tables, describe table, execute SQL, open table in DBX UI
11
11
  - **Connection pooling** — Reuses database connections across queries
12
12
  - **PostgreSQL & MySQL** — Supports PostgreSQL, MySQL, and compatible databases (Doris, StarRocks, etc.)
13
13
  - **DBX UI integration** — Open tables directly in the DBX desktop app from your AI agent
@@ -69,6 +69,8 @@ In Claude Code, just ask:
69
69
  | Tool | Description |
70
70
  |---|---|
71
71
  | `dbx_list_connections` | List all database connections configured in DBX |
72
+ | `dbx_add_connection` | Add a new database connection |
73
+ | `dbx_remove_connection` | Remove a database connection |
72
74
  | `dbx_list_tables` | List tables and views for a connection |
73
75
  | `dbx_describe_table` | Get column definitions for a table |
74
76
  | `dbx_execute_query` | Execute a SQL query (max 100 rows) |
@@ -79,17 +81,14 @@ In Claude Code, just ask:
79
81
  ```
80
82
  AI Agent → MCP Server → Database
81
83
  ↓
82
- DBX connections.json
83
- + system keyring (passwords)
84
+ DBX SQLite database (dbx.db)
84
85
  ```
85
86
 
86
- The MCP server reads your database connections from DBX's config directory:
87
+ The MCP server reads your database connections from DBX's SQLite database:
87
88
 
88
- - **macOS**: `~/Library/Application Support/com.dbx.app/connections.json`
89
- - **Linux**: `~/.config/com.dbx.app/connections.json`
90
- - **Windows**: `%APPDATA%\com.dbx.app\connections.json`
91
-
92
- Passwords are retrieved from the system keyring (macOS Keychain / Linux Secret Service / Windows Credential Manager).
89
+ - **macOS**: `~/Library/Application Support/com.dbx.app/dbx.db`
90
+ - **Linux**: `~/.config/com.dbx.app/dbx.db`
91
+ - **Windows**: `%APPDATA%\com.dbx.app\dbx.db`
93
92
 
94
93
  ## DBX UI Integration
95
94
 
@@ -112,8 +111,8 @@ MIT
112
111
 
113
112
  ### 特性
114
113
 
115
- - **零配置** — 自动读取 DBX 的连接配置(包括系统钥匙串中的密码)
116
- - **5 个工具** — 列出连接、列出表、查看表结构、执行 SQL、在 DBX 中打开表
114
+ - **零配置** — 自动读取 DBX 的连接配置
115
+ - **7 个工具** — 列出/添加/删除连接、列出表、查看表结构、执行 SQL、在 DBX 中打开表
117
116
  - **连接池** — 跨查询复用数据库连接
118
117
  - **PostgreSQL 和 MySQL** — 支持 PostgreSQL、MySQL 及兼容数据库(Doris、StarRocks 等)
119
118
  - **DBX UI 联动** — 从 AI 助手直接在 DBX 桌面端打开表
@@ -161,6 +160,8 @@ npx @dbx-app/mcp-server
161
160
  | 工具 | 说明 |
162
161
  |---|---|
163
162
  | `dbx_list_connections` | 列出 DBX 中所有已配置的数据库连接 |
163
+ | `dbx_add_connection` | 添加新的数据库连接 |
164
+ | `dbx_remove_connection` | 删除数据库连接 |
164
165
  | `dbx_list_tables` | 列出指定连接的表和视图 |
165
166
  | `dbx_describe_table` | 获取表的列定义 |
166
167
  | `dbx_execute_query` | 执行 SQL 查询(最多返回 100 行) |
@@ -168,13 +169,11 @@ npx @dbx-app/mcp-server
168
169
 
169
170
  ### 工作原理
170
171
 
171
- MCP Server 从 DBX 的配置目录读取连接信息:
172
-
173
- - **macOS**: `~/Library/Application Support/com.dbx.app/connections.json`
174
- - **Linux**: `~/.config/com.dbx.app/connections.json`
175
- - **Windows**: `%APPDATA%\com.dbx.app\connections.json`
172
+ MCP Server 从 DBX 的 SQLite 数据库读取连接信息:
176
173
 
177
- 密码从系统钥匙串中获取(macOS Keychain / Linux Secret Service / Windows 凭据管理器)。
174
+ - **macOS**: `~/Library/Application Support/com.dbx.app/dbx.db`
175
+ - **Linux**: `~/.config/com.dbx.app/dbx.db`
176
+ - **Windows**: `%APPDATA%\com.dbx.app\dbx.db`
178
177
 
179
178
  ### DBX UI 联动
180
179
 
@@ -14,3 +14,5 @@ export interface ConnectionConfig {
14
14
  }
15
15
  export declare function loadConnections(): Promise<ConnectionConfig[]>;
16
16
  export declare function findConnection(name: string): Promise<ConnectionConfig | undefined>;
17
+ export declare function addConnection(config: Omit<ConnectionConfig, "id">): Promise<ConnectionConfig>;
18
+ export declare function removeConnection(name: string): Promise<boolean>;
@@ -1,7 +1,7 @@
1
- import { readFile } from "node:fs/promises";
2
1
  import { join } from "node:path";
3
2
  import { homedir, platform } from "node:os";
4
- const KEYRING_SERVICE = "dev.dbx.connections";
3
+ import { randomUUID } from "node:crypto";
4
+ import Database from "better-sqlite3";
5
5
  function appDataDir() {
6
6
  const home = homedir();
7
7
  switch (platform()) {
@@ -13,41 +13,27 @@ function appDataDir() {
13
13
  return join(home, ".config", "com.dbx.app");
14
14
  }
15
15
  }
16
- async function getKeytarSecret(connectionId, key) {
17
- try {
18
- const keytar = await import("keytar");
19
- return await keytar.default.getPassword(KEYRING_SERVICE, `connection:${connectionId}:${key}`);
20
- }
21
- catch {
22
- return null;
23
- }
24
- }
25
- async function getFileSecret(connectionId, key) {
26
- try {
27
- const secretsPath = join(appDataDir(), "secrets.json");
28
- const data = JSON.parse(await readFile(secretsPath, "utf-8"));
29
- return data[`connection:${connectionId}:${key}`] ?? null;
30
- }
31
- catch {
32
- return null;
33
- }
16
+ function openDb(readonly = false) {
17
+ const dbPath = join(appDataDir(), "dbx.db");
18
+ return new Database(dbPath, { readonly });
34
19
  }
35
- async function getSecret(connectionId, key) {
36
- const fromKeyring = await getKeytarSecret(connectionId, key);
37
- if (fromKeyring)
38
- return fromKeyring;
39
- const fromFile = await getFileSecret(connectionId, key);
40
- return fromFile ?? "";
20
+ function getSecret(db, connectionId, key) {
21
+ const row = db.prepare("SELECT secret FROM connection_secrets WHERE connection_id = ? AND key = ?").get(connectionId, key);
22
+ return row?.secret ?? "";
41
23
  }
42
24
  export async function loadConnections() {
43
- const configPath = join(appDataDir(), "connections.json");
44
25
  try {
45
- const raw = await readFile(configPath, "utf-8");
46
- const configs = JSON.parse(raw);
47
- for (const config of configs) {
26
+ const db = openDb(true);
27
+ const rows = db.prepare("SELECT id, config_json FROM connections").all();
28
+ const configs = [];
29
+ for (const row of rows) {
30
+ const config = JSON.parse(row.config_json);
31
+ config.id = row.id;
48
32
  if (!config.password)
49
- config.password = await getSecret(config.id, "password");
33
+ config.password = getSecret(db, row.id, "password");
34
+ configs.push(config);
50
35
  }
36
+ db.close();
51
37
  return configs;
52
38
  }
53
39
  catch {
@@ -58,3 +44,31 @@ export async function findConnection(name) {
58
44
  const connections = await loadConnections();
59
45
  return connections.find((c) => c.name.toLowerCase() === name.toLowerCase());
60
46
  }
47
+ export async function addConnection(config) {
48
+ const id = randomUUID();
49
+ const db = openDb();
50
+ const sanitized = { ...config, id, password: "" };
51
+ const configJson = JSON.stringify(sanitized);
52
+ const insert = db.transaction(() => {
53
+ db.prepare("INSERT INTO connections (id, config_json) VALUES (?, ?)").run(id, configJson);
54
+ if (config.password) {
55
+ db.prepare("INSERT INTO connection_secrets (connection_id, key, secret) VALUES (?, ?, ?)").run(id, "password", config.password);
56
+ }
57
+ });
58
+ insert();
59
+ db.close();
60
+ return { ...config, id };
61
+ }
62
+ export async function removeConnection(name) {
63
+ const connection = await findConnection(name);
64
+ if (!connection)
65
+ return false;
66
+ const db = openDb();
67
+ const remove = db.transaction(() => {
68
+ db.prepare("DELETE FROM connections WHERE id = ?").run(connection.id);
69
+ db.prepare("DELETE FROM connection_secrets WHERE connection_id = ?").run(connection.id);
70
+ });
71
+ remove();
72
+ db.close();
73
+ return true;
74
+ }
package/dist/index.js CHANGED
@@ -5,8 +5,25 @@ import { z } from "zod";
5
5
  import { readFile } from "node:fs/promises";
6
6
  import { join } from "node:path";
7
7
  import { homedir, platform } from "node:os";
8
- import { loadConnections, findConnection } from "./connections.js";
9
- import { listTables, describeTable, executeQuery } from "./database.js";
8
+ import { loadConnections as desktopLoadConnections, findConnection as desktopFindConnection, addConnection as desktopAddConnection, removeConnection as desktopRemoveConnection, } from "./connections.js";
9
+ import { listTables as desktopListTables, describeTable as desktopDescribeTable, executeQuery as desktopExecuteQuery, } from "./database.js";
10
+ const isWebMode = !!process.env.DBX_WEB_URL;
11
+ let backend;
12
+ if (isWebMode) {
13
+ const web = await import("./web-backend.js");
14
+ backend = web;
15
+ }
16
+ else {
17
+ backend = {
18
+ loadConnections: desktopLoadConnections,
19
+ findConnection: desktopFindConnection,
20
+ addConnection: desktopAddConnection,
21
+ removeConnection: desktopRemoveConnection,
22
+ listTables: desktopListTables,
23
+ describeTable: desktopDescribeTable,
24
+ executeQuery: desktopExecuteQuery,
25
+ };
26
+ }
10
27
  function text(s) {
11
28
  return { content: [{ type: "text", text: s }] };
12
29
  }
@@ -19,10 +36,10 @@ function mdTable(headers, rows) {
19
36
  }
20
37
  const server = new McpServer({
21
38
  name: "dbx",
22
- version: "0.1.1",
39
+ version: "0.2.1",
23
40
  });
24
41
  server.tool("dbx_list_connections", "List all database connections configured in DBX", {}, async () => {
25
- const connections = await loadConnections();
42
+ const connections = await backend.loadConnections();
26
43
  if (connections.length === 0)
27
44
  return text("No connections configured in DBX.");
28
45
  const rows = connections.map((c) => [c.name, c.db_type, c.host, String(c.port), c.database || ""]);
@@ -32,10 +49,10 @@ server.tool("dbx_list_tables", "List tables and views for a database connection"
32
49
  connection_name: z.string().describe("Name of the DBX connection"),
33
50
  schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
34
51
  }, async ({ connection_name, schema }) => {
35
- const config = await findConnection(connection_name);
52
+ const config = await backend.findConnection(connection_name);
36
53
  if (!config)
37
54
  return text(`Connection "${connection_name}" not found`);
38
- const tables = await listTables(config, schema);
55
+ const tables = await backend.listTables(config, schema);
39
56
  if (tables.length === 0)
40
57
  return text("No tables found.");
41
58
  const rows = tables.map((t) => [t.name, t.type]);
@@ -46,10 +63,10 @@ server.tool("dbx_describe_table", "Get column definitions for a table", {
46
63
  table: z.string().describe("Table name"),
47
64
  schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
48
65
  }, async ({ connection_name, table, schema }) => {
49
- const config = await findConnection(connection_name);
66
+ const config = await backend.findConnection(connection_name);
50
67
  if (!config)
51
68
  return text(`Connection "${connection_name}" not found`);
52
- const columns = await describeTable(config, table, schema);
69
+ const columns = await backend.describeTable(config, table, schema);
53
70
  if (columns.length === 0)
54
71
  return text("No columns found.");
55
72
  const rows = columns.map((c) => [
@@ -65,11 +82,11 @@ server.tool("dbx_execute_query", "Execute a SQL query on a database connection (
65
82
  connection_name: z.string().describe("Name of the DBX connection"),
66
83
  sql: z.string().describe("SQL query to execute"),
67
84
  }, async ({ connection_name, sql }) => {
68
- const config = await findConnection(connection_name);
85
+ const config = await backend.findConnection(connection_name);
69
86
  if (!config)
70
87
  return text(`Connection "${connection_name}" not found`);
71
88
  try {
72
- const result = await executeQuery(config, sql);
89
+ const result = await backend.executeQuery(config, sql);
73
90
  if (result.columns.length === 0)
74
91
  return text(`Query executed. ${result.row_count} row(s) affected.`);
75
92
  const rows = result.rows.map((r) => result.columns.map((c) => formatCell(r[c])));
@@ -80,6 +97,35 @@ server.tool("dbx_execute_query", "Execute a SQL query on a database connection (
80
97
  return text(`Query error: ${msg}`);
81
98
  }
82
99
  });
100
+ server.tool("dbx_add_connection", "Add a new database connection to DBX", {
101
+ name: z.string().describe("Connection name"),
102
+ db_type: z.string().describe("Database type: postgres, mysql, sqlite, redis, duckdb, clickhouse, sqlserver, mongodb, oracle, elasticsearch"),
103
+ host: z.string().describe("Database host"),
104
+ port: z.number().describe("Database port"),
105
+ username: z.string().default("").describe("Username"),
106
+ password: z.string().default("").describe("Password"),
107
+ database: z.string().optional().describe("Default database name"),
108
+ ssl: z.boolean().default(false).describe("Enable SSL"),
109
+ }, async ({ name, db_type, host, port, username, password, database, ssl }) => {
110
+ const existing = await backend.findConnection(name);
111
+ if (existing)
112
+ return text(`Connection "${name}" already exists.`);
113
+ const config = await backend.addConnection({
114
+ name, db_type, host, port, username, password,
115
+ database, ssl, ssh_enabled: false,
116
+ });
117
+ await notifyReload();
118
+ return text(`Connection "${config.name}" added (id: ${config.id}).`);
119
+ });
120
+ server.tool("dbx_remove_connection", "Remove a database connection from DBX", {
121
+ connection_name: z.string().describe("Name of the connection to remove"),
122
+ }, async ({ connection_name }) => {
123
+ const removed = await backend.removeConnection(connection_name);
124
+ if (!removed)
125
+ return text(`Connection "${connection_name}" not found.`);
126
+ await notifyReload();
127
+ return text(`Connection "${connection_name}" removed.`);
128
+ });
83
129
  function formatCell(value) {
84
130
  if (value === null || value === undefined)
85
131
  return "NULL";
@@ -103,21 +149,31 @@ async function getBridgeUrl() {
103
149
  const port = (await readFile(portFile, "utf-8")).trim();
104
150
  return `http://127.0.0.1:${port}`;
105
151
  }
106
- server.tool("dbx_open_table", "Open a table in DBX desktop app UI. Requires DBX to be running.", {
107
- connection_name: z.string().describe("Name of the DBX connection"),
108
- table: z.string().describe("Table name to open"),
109
- database: z.string().optional().describe("Database name"),
110
- schema: z.string().optional().describe("Schema name"),
111
- }, async ({ connection_name, table, database, schema }) => {
112
- return bridgeRequest("/open-table", { connection_name, table, database, schema }, `Opened ${table} in DBX`);
113
- });
114
- server.tool("dbx_execute_and_show", "Execute a SQL query in DBX desktop app UI and show results there. Requires DBX to be running.", {
115
- connection_name: z.string().describe("Name of the DBX connection"),
116
- sql: z.string().describe("SQL query to execute"),
117
- database: z.string().optional().describe("Database name"),
118
- }, async ({ connection_name, sql, database }) => {
119
- return bridgeRequest("/execute-query", { connection_name, sql, database }, "Query sent to DBX");
120
- });
152
+ async function notifyReload() {
153
+ try {
154
+ const bridgeUrl = await getBridgeUrl();
155
+ await fetch(`${bridgeUrl}/reload-connections`, { method: "POST" });
156
+ }
157
+ catch { }
158
+ }
159
+ // Desktop-only tools: open table and execute-and-show require the Tauri bridge
160
+ if (!isWebMode) {
161
+ server.tool("dbx_open_table", "Open a table in DBX desktop app UI. Requires DBX to be running.", {
162
+ connection_name: z.string().describe("Name of the DBX connection"),
163
+ table: z.string().describe("Table name to open"),
164
+ database: z.string().optional().describe("Database name"),
165
+ schema: z.string().optional().describe("Schema name"),
166
+ }, async ({ connection_name, table, database, schema }) => {
167
+ return bridgeRequest("/open-table", { connection_name, table, database, schema }, `Opened ${table} in DBX`);
168
+ });
169
+ server.tool("dbx_execute_and_show", "Execute a SQL query in DBX desktop app UI and show results there. Requires DBX to be running.", {
170
+ connection_name: z.string().describe("Name of the DBX connection"),
171
+ sql: z.string().describe("SQL query to execute"),
172
+ database: z.string().optional().describe("Database name"),
173
+ }, async ({ connection_name, sql, database }) => {
174
+ return bridgeRequest("/execute-query", { connection_name, sql, database }, "Query sent to DBX");
175
+ });
176
+ }
121
177
  async function bridgeRequest(path, body, successMsg) {
122
178
  try {
123
179
  const bridgeUrl = await getBridgeUrl();
@@ -0,0 +1,9 @@
1
+ import type { ConnectionConfig } from "./connections.js";
2
+ import type { TableInfo, ColumnInfo, QueryResult } from "./database.js";
3
+ export declare function loadConnections(): Promise<ConnectionConfig[]>;
4
+ export declare function findConnection(name: string): Promise<ConnectionConfig | undefined>;
5
+ export declare function addConnection(config: Omit<ConnectionConfig, "id">): Promise<ConnectionConfig>;
6
+ export declare function removeConnection(name: string): Promise<boolean>;
7
+ export declare function listTables(config: ConnectionConfig, schema?: string): Promise<TableInfo[]>;
8
+ export declare function describeTable(config: ConnectionConfig, table: string, schema?: string): Promise<ColumnInfo[]>;
9
+ export declare function executeQuery(config: ConnectionConfig, sql: string): Promise<QueryResult>;
@@ -0,0 +1,114 @@
1
+ const baseUrl = process.env.DBX_WEB_URL.replace(/\/+$/, "");
2
+ const password = process.env.DBX_WEB_PASSWORD || "";
3
+ let sessionCookie = null;
4
+ async function ensureAuth() {
5
+ if (sessionCookie)
6
+ return;
7
+ if (!password)
8
+ return; // no password set, assume no auth required
9
+ const res = await fetch(`${baseUrl}/api/auth/login`, {
10
+ method: "POST",
11
+ headers: { "Content-Type": "application/json" },
12
+ body: JSON.stringify({ password }),
13
+ redirect: "manual",
14
+ });
15
+ if (!res.ok) {
16
+ throw new Error(`Authentication failed: ${res.status} ${res.statusText}`);
17
+ }
18
+ const setCookie = res.headers.get("set-cookie");
19
+ if (setCookie) {
20
+ const match = setCookie.match(/dbx_session=([^;]+)/);
21
+ if (match) {
22
+ sessionCookie = match[1];
23
+ }
24
+ }
25
+ }
26
+ function headers(extra) {
27
+ const h = { "Content-Type": "application/json", ...extra };
28
+ if (sessionCookie) {
29
+ h["Cookie"] = `dbx_session=${sessionCookie}`;
30
+ }
31
+ return h;
32
+ }
33
+ async function apiFetch(path, init) {
34
+ await ensureAuth();
35
+ const res = await fetch(`${baseUrl}${path}`, {
36
+ ...init,
37
+ headers: headers(init?.headers),
38
+ });
39
+ if (!res.ok) {
40
+ const body = await res.text().catch(() => "");
41
+ throw new Error(`API request ${path} failed: ${res.status} ${res.statusText} ${body}`);
42
+ }
43
+ return res;
44
+ }
45
+ export async function loadConnections() {
46
+ const res = await apiFetch("/api/connection/list");
47
+ return res.json();
48
+ }
49
+ export async function findConnection(name) {
50
+ const connections = await loadConnections();
51
+ return connections.find((c) => c.name.toLowerCase() === name.toLowerCase());
52
+ }
53
+ export async function addConnection(config) {
54
+ const res = await apiFetch("/api/connection/save", {
55
+ method: "POST",
56
+ body: JSON.stringify({ configs: [config] }),
57
+ });
58
+ const saved = (await res.json());
59
+ return saved;
60
+ }
61
+ export async function removeConnection(name) {
62
+ const connection = await findConnection(name);
63
+ if (!connection)
64
+ return false;
65
+ await apiFetch(`/api/connection/delete?id=${encodeURIComponent(connection.id)}`, { method: "DELETE" });
66
+ return true;
67
+ }
68
+ async function ensureConnected(config) {
69
+ await apiFetch("/api/connection/connect", {
70
+ method: "POST",
71
+ body: JSON.stringify({ config }),
72
+ });
73
+ }
74
+ export async function listTables(config, schema) {
75
+ await ensureConnected(config);
76
+ const params = new URLSearchParams({
77
+ connection_id: config.id,
78
+ database: config.database || "",
79
+ schema: schema || "",
80
+ });
81
+ const res = await apiFetch(`/api/schema/tables?${params}`);
82
+ return res.json();
83
+ }
84
+ export async function describeTable(config, table, schema) {
85
+ await ensureConnected(config);
86
+ const params = new URLSearchParams({
87
+ connection_id: config.id,
88
+ database: config.database || "",
89
+ schema: schema || "",
90
+ table,
91
+ });
92
+ const res = await apiFetch(`/api/schema/columns?${params}`);
93
+ return res.json();
94
+ }
95
+ export async function executeQuery(config, sql) {
96
+ await ensureConnected(config);
97
+ const res = await apiFetch("/api/query/execute", {
98
+ method: "POST",
99
+ body: JSON.stringify({
100
+ connectionId: config.id,
101
+ database: config.database || "",
102
+ sql,
103
+ }),
104
+ });
105
+ const data = (await res.json());
106
+ const rows = data.rows.map((row) => {
107
+ const obj = {};
108
+ data.columns.forEach((col, i) => {
109
+ obj[col] = row[i];
110
+ });
111
+ return obj;
112
+ });
113
+ return { columns: data.columns, rows, row_count: rows.length };
114
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@dbx-app/mcp-server",
3
- "version": "0.1.3",
3
+ "version": "0.2.1",
4
4
  "mcpName": "io.github.t8y2/dbx",
5
- "description": "MCP server for DBX — query databases from Claude Code, Cursor, and other AI agents",
5
+ "description": "MCP server for DBX \u2014 query databases from Claude Code, Cursor, and other AI agents",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "dbx-mcp-server": "dist/index.js"
@@ -33,15 +33,23 @@
33
33
  "homepage": "https://github.com/t8y2/dbx/tree/main/mcp",
34
34
  "dependencies": {
35
35
  "@modelcontextprotocol/sdk": "^1.12.1",
36
+ "better-sqlite3": "^12.9.0",
36
37
  "keytar": "^7.9.0",
37
38
  "mysql2": "^3.14.1",
38
39
  "pg": "^8.16.0",
39
40
  "zod": "^3.25.20"
40
41
  },
41
42
  "devDependencies": {
43
+ "@types/better-sqlite3": "^7.6.13",
42
44
  "@types/node": "^22.15.21",
43
45
  "@types/pg": "^8.15.4",
44
46
  "tsx": "^4.19.4",
45
47
  "typescript": "^5.8.3"
48
+ },
49
+ "pnpm": {
50
+ "onlyBuiltDependencies": [
51
+ "better-sqlite3",
52
+ "esbuild"
53
+ ]
46
54
  }
47
- }
55
+ }