'use strict'; // ============================================ // CursorPro SQLite Utils - 反混淆版本 // ============================================ const { exec } = require('child_process'); const { promisify } = require('util'); const fs = require('fs'); const execAsync = promisify(exec); /** * 转义 SQL 字符串中的单引号 */ function escapeSqlString(value) { if (value === null || value === undefined) { return ''; } return String(value).replace(/'/g, "''"); } /** * 执行 SQLite 命令 * @param {string} dbPath - 数据库文件路径 * @param {string} sql - SQL 语句 * @returns {Promise} - 执行结果 */ async function execSqlite(dbPath, sql) { const isWindows = process.platform === 'win32'; try { if (isWindows) { // Windows: 直接使用 sqlite3 命令 const escapedSql = sql.replace(/"/g, '\\"'); const command = `sqlite3 "${dbPath}" "${escapedSql}"`; const { stdout, stderr } = await execAsync(command, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 // 10MB }); if (stderr && !stderr.includes('-- Loading')) { console.warn('[SQLite] stderr:', stderr); } return stdout.trim(); } else { // macOS/Linux: 使用临时文件避免转义问题 const os = require('os'); const pathModule = require('path'); const tempFile = pathModule.join( os.tmpdir(), 'cursor_sql_' + Date.now() + '.sql' ); // 写入 SQL 到临时文件 fs.writeFileSync(tempFile, sql, 'utf-8'); try { const command = `sqlite3 "${dbPath}" < "${tempFile}"`; const { stdout, stderr } = await execAsync(command, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, shell: '/bin/bash' }); if (stderr && !stderr.includes('-- Loading')) { console.warn('[SQLite] stderr:', stderr); } return stdout.trim(); } finally { // 清理临时文件 try { fs.unlinkSync(tempFile); } catch (e) {} } } } catch (error) { // 检查是否是 sqlite3 不存在的错误 if ( error.message === 'ENOENT' || error.message?.includes('sqlite3') || error.message?.includes('not found') ) { throw new Error('sqlite3 命令不存在,请先安装 SQLite3'); } throw error; } } /** * 从 SQLite 数据库读取单个值 * @param {string} dbPath - 数据库路径 * @param {string} key - 键名 * @returns {Promise} - 值或 null */ async function sqliteGet(dbPath, key) { if (!fs.existsSync(dbPath)) { console.warn('[SQLite] 数据库文件不存在:', dbPath); return null; } try { const sql = `SELECT value FROM ItemTable WHERE key = '${escapeSqlString(key)}';`; const result = await execSqlite(dbPath, sql); return result || null; } catch (error) { console.error('[SQLite] 读取失败:', error); return null; } } exports.sqliteGet = sqliteGet; /** * 向 SQLite 数据库写入单个值 * @param {string} dbPath - 数据库路径 * @param {string} key - 键名 * @param {string} value - 值 * @returns {Promise} - 是否成功 */ async function sqliteSet(dbPath, key, value) { if (!fs.existsSync(dbPath)) { console.warn('[SQLite] 数据库文件不存在:', dbPath); return false; } try { // 使用 REPLACE INTO 实现 upsert const sql = `REPLACE INTO ItemTable (key, value) VALUES ('${escapeSqlString(key)}', '${escapeSqlString(value)}');`; await execSqlite(dbPath, sql); return true; } catch (error) { console.error('[SQLite] 写入失败:', error); return false; } } exports.sqliteSet = sqliteSet; /** * 批量写入 SQLite 数据库 * @param {string} dbPath - 数据库路径 * @param {Array<[string, string]>} kvPairs - 键值对数组 * @returns {Promise} - 是否成功 */ async function sqliteSetBatch(dbPath, kvPairs) { if (!fs.existsSync(dbPath)) { console.warn('[SQLite] 数据库文件不存在:', dbPath); return false; } if (kvPairs.length === 0) { return true; } try { // 构建批量 SQL 语句 const statements = kvPairs.map(([key, value]) => `REPLACE INTO ItemTable (key, value) VALUES ('${escapeSqlString(key)}', '${escapeSqlString(value)}');` ); const sql = 'BEGIN TRANSACTION; ' + statements.join(' ') + ' COMMIT;'; await execSqlite(dbPath, sql); return true; } catch (error) { console.error('[SQLite] 批量写入失败:', error); return false; } } exports.sqliteSetBatch = sqliteSetBatch; /** * 批量读取 SQLite 数据库 * @param {string} dbPath - 数据库路径 * @param {string[]} keys - 键名数组 * @returns {Promise>} - 键值 Map */ async function sqliteGetBatch(dbPath, keys) { const resultMap = new Map(); if (!fs.existsSync(dbPath)) { console.warn('[SQLite] 数据库文件不存在:', dbPath); keys.forEach(key => resultMap.set(key, null)); return resultMap; } try { // 逐个读取 (SQLite CLI 批量读取输出解析较复杂) for (const key of keys) { const value = await sqliteGet(dbPath, key); resultMap.set(key, value); } return resultMap; } catch (error) { console.error('[SQLite] 批量读取失败:', error); keys.forEach(key => resultMap.set(key, null)); return resultMap; } } exports.sqliteGetBatch = sqliteGetBatch;