refactor: migrate from better-sqlite3 to bun:sqlite
- Updated build-hooks.js to remove better-sqlite3 dependency and use bun:sqlite. - Modified smart-install.js to eliminate checks and installations related to better-sqlite3. - Refactored Database.ts, SessionSearch.ts, SessionStore.ts, and migrations.ts to import and utilize bun:sqlite. - Replaced exec and pragma calls with appropriate run methods for bun:sqlite compatibility. - Removed unnecessary native module verification and installation logic for better-sqlite3.
This commit is contained in:
@@ -13,7 +13,7 @@ module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'claude-mem-worker',
|
||||
script: './plugin/scripts/worker-service.cjs',
|
||||
script: 'bun ./plugin/scripts/worker-service.cjs',
|
||||
// Windows: prevent visible console windows
|
||||
windowsHide: true,
|
||||
// INTENTIONAL: Watch mode enables auto-restart on plugin updates
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.62",
|
||||
"@modelcontextprotocol/sdk": "^1.20.1",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"better-sqlite3": "^12.5.0",
|
||||
"express": "^4.18.2",
|
||||
"glob": "^11.0.3",
|
||||
"handlebars": "^4.7.8",
|
||||
@@ -60,7 +59,6 @@
|
||||
"zod-to-json-schema": "^3.24.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.6.8",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^20.0.0",
|
||||
|
||||
+4
-5
@@ -1,13 +1,12 @@
|
||||
{
|
||||
"name": "claude-mem-plugin",
|
||||
"version": "7.0.9",
|
||||
"version": "7.0.10",
|
||||
"private": true,
|
||||
"description": "Runtime dependencies for claude-mem bundled hooks",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^12.5.0"
|
||||
},
|
||||
"dependencies": {},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
"node": ">=18.0.0",
|
||||
"bun": ">=1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -287,17 +287,6 @@ async function runNpmInstall() {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
|
||||
// Verify better-sqlite3 was installed
|
||||
if (!existsSync(BETTER_SQLITE3_PATH)) {
|
||||
throw new Error('better-sqlite3 installation verification failed');
|
||||
}
|
||||
|
||||
// Verify native modules actually work
|
||||
const nativeModulesWork = await verifyNativeModules();
|
||||
if (!nativeModulesWork) {
|
||||
throw new Error('Native modules failed to load after install');
|
||||
}
|
||||
|
||||
const packageVersion = getPackageVersion();
|
||||
const nodeVersion = getNodeVersion();
|
||||
setInstalledVersion(packageVersion, nodeVersion);
|
||||
|
||||
File diff suppressed because one or more lines are too long
+8
-10
@@ -59,8 +59,7 @@ async function buildHooks() {
|
||||
console.log('✓ Output directories ready');
|
||||
|
||||
// Generate plugin/package.json for cache directory dependency installation
|
||||
// The bundled hooks use `external: ['better-sqlite3']` so dependencies must be
|
||||
// installed at runtime. This package.json enables npm install in the cache directory.
|
||||
// Note: bun:sqlite is a Bun built-in, no external dependencies needed for SQLite
|
||||
console.log('\n📦 Generating plugin package.json...');
|
||||
const pluginPackageJson = {
|
||||
name: 'claude-mem-plugin',
|
||||
@@ -68,11 +67,10 @@ async function buildHooks() {
|
||||
private: true,
|
||||
description: 'Runtime dependencies for claude-mem bundled hooks',
|
||||
type: 'module',
|
||||
dependencies: {
|
||||
'better-sqlite3': packageJson.dependencies['better-sqlite3']
|
||||
},
|
||||
dependencies: {},
|
||||
engines: {
|
||||
node: '>=18.0.0'
|
||||
node: '>=18.0.0',
|
||||
bun: '>=1.0.0'
|
||||
}
|
||||
};
|
||||
fs.writeFileSync('plugin/package.json', JSON.stringify(pluginPackageJson, null, 2) + '\n');
|
||||
@@ -103,7 +101,7 @@ async function buildHooks() {
|
||||
outfile: `${hooksDir}/${WORKER_SERVICE.name}.cjs`,
|
||||
minify: true,
|
||||
logLevel: 'error', // Suppress warnings (import.meta warning is benign)
|
||||
external: ['better-sqlite3'],
|
||||
external: ['bun:sqlite'],
|
||||
define: {
|
||||
'__DEFAULT_PACKAGE_VERSION__': `"${version}"`
|
||||
},
|
||||
@@ -128,7 +126,7 @@ async function buildHooks() {
|
||||
outfile: `${hooksDir}/${MCP_SERVER.name}.cjs`,
|
||||
minify: true,
|
||||
logLevel: 'error',
|
||||
external: ['better-sqlite3'],
|
||||
external: ['bun:sqlite'],
|
||||
define: {
|
||||
'__DEFAULT_PACKAGE_VERSION__': `"${version}"`
|
||||
},
|
||||
@@ -153,7 +151,7 @@ async function buildHooks() {
|
||||
outfile: `${hooksDir}/${CONTEXT_GENERATOR.name}.cjs`,
|
||||
minify: true,
|
||||
logLevel: 'error',
|
||||
external: ['better-sqlite3'],
|
||||
external: ['bun:sqlite'],
|
||||
define: {
|
||||
'__DEFAULT_PACKAGE_VERSION__': `"${version}"`
|
||||
}
|
||||
@@ -176,7 +174,7 @@ async function buildHooks() {
|
||||
format: 'esm',
|
||||
outfile,
|
||||
minify: true,
|
||||
external: ['better-sqlite3'],
|
||||
external: ['bun:sqlite'],
|
||||
define: {
|
||||
'__DEFAULT_PACKAGE_VERSION__': `"${version}"`
|
||||
},
|
||||
|
||||
@@ -25,7 +25,6 @@ const MARKETPLACE_ROOT = join(homedir(), '.claude', 'plugins', 'marketplaces', '
|
||||
const PACKAGE_JSON_PATH = join(MARKETPLACE_ROOT, 'package.json');
|
||||
const VERSION_MARKER_PATH = join(MARKETPLACE_ROOT, '.install-version');
|
||||
const NODE_MODULES_PATH = join(MARKETPLACE_ROOT, 'node_modules');
|
||||
const BETTER_SQLITE3_PATH = join(NODE_MODULES_PATH, 'better-sqlite3');
|
||||
|
||||
// Colors for output
|
||||
const colors = {
|
||||
@@ -104,11 +103,6 @@ function needsInstall() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if better-sqlite3 is installed
|
||||
if (!existsSync(BETTER_SQLITE3_PATH)) {
|
||||
log('📦 better-sqlite3 missing - reinstalling', colors.cyan);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check version marker
|
||||
const currentPackageVersion = getPackageVersion();
|
||||
@@ -143,46 +137,6 @@ function needsInstall() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that better-sqlite3 native module loads correctly
|
||||
* This catches ABI mismatches and corrupted builds
|
||||
*/
|
||||
async function verifyNativeModules() {
|
||||
try {
|
||||
log('🔍 Verifying native modules...', colors.dim);
|
||||
|
||||
// CRITICAL: Use createRequire() to resolve from MARKETPLACE_ROOT
|
||||
// This script may run from cache but must load modules from marketplace's node_modules
|
||||
const require = createRequire(join(MARKETPLACE_ROOT, 'package.json'));
|
||||
const Database = require('better-sqlite3');
|
||||
|
||||
// Try to create a test in-memory database
|
||||
const db = new Database(':memory:');
|
||||
|
||||
// Run a simple query to ensure it works
|
||||
const result = db.prepare('SELECT 1 + 1 as result').get();
|
||||
|
||||
// Clean up
|
||||
db.close();
|
||||
|
||||
if (result.result !== 2) {
|
||||
throw new Error('SQLite math check failed');
|
||||
}
|
||||
|
||||
log('✓ Native modules verified', colors.dim);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
if (error.code === 'ERR_DLOPEN_FAILED') {
|
||||
log('⚠️ Native module ABI mismatch detected', colors.yellow);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Other errors are unexpected - log and fail
|
||||
log(`❌ Native module verification failed: ${error.message}`, colors.red);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getWindowsErrorHelp(errorOutput) {
|
||||
// Detect Python version at runtime
|
||||
@@ -203,7 +157,7 @@ function getWindowsErrorHelp(errorOutput) {
|
||||
'║ Windows Installation Help ║',
|
||||
'╚══════════════════════════════════════════════════════════════════════╝',
|
||||
'',
|
||||
'📋 better-sqlite3 requires build tools to compile native modules.',
|
||||
'',
|
||||
'',
|
||||
'🔧 Option 1: Install Visual Studio Build Tools (Recommended)',
|
||||
' 1. Download: https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022',
|
||||
@@ -232,9 +186,6 @@ function getWindowsErrorHelp(errorOutput) {
|
||||
help.push('❌ Permission denied - try running as Administrator');
|
||||
}
|
||||
|
||||
help.push('');
|
||||
help.push('📖 Full documentation: https://github.com/WiseLibs/better-sqlite3/blob/master/docs/troubleshooting.md');
|
||||
help.push('');
|
||||
|
||||
return help.join('\n');
|
||||
}
|
||||
@@ -266,10 +217,7 @@ async function runNpmInstall() {
|
||||
windowsHide: true,
|
||||
});
|
||||
|
||||
// Verify better-sqlite3 was installed
|
||||
if (!existsSync(BETTER_SQLITE3_PATH)) {
|
||||
throw new Error('better-sqlite3 installation verification failed');
|
||||
}
|
||||
|
||||
|
||||
// NEW: Verify native modules actually work
|
||||
const nativeModulesWork = await verifyNativeModules();
|
||||
@@ -300,11 +248,6 @@ async function runNpmInstall() {
|
||||
log('❌ Installation failed after retrying!', colors.bright);
|
||||
log('', colors.reset);
|
||||
|
||||
// Provide Windows-specific help
|
||||
if (isWindows && lastError && lastError.message && lastError.message.includes('better-sqlite3')) {
|
||||
log(getWindowsErrorHelp(lastError.message), colors.yellow);
|
||||
}
|
||||
|
||||
// Show generic error info with troubleshooting steps
|
||||
if (lastError) {
|
||||
if (lastError.stderr) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Database } from 'better-sqlite3';
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { DATA_DIR, DB_PATH, ensureDir } from '../../shared/paths.js';
|
||||
|
||||
export interface Migration {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { TableNameRow } from '../../types/database.js';
|
||||
import { DATA_DIR, DB_PATH, ensureDir } from '../../shared/paths.js';
|
||||
import {
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
* Vector search is handled by ChromaDB - this class only supports filtering without query text
|
||||
*/
|
||||
export class SessionSearch {
|
||||
private db: Database.Database;
|
||||
private db: Database;
|
||||
|
||||
constructor(dbPath?: string) {
|
||||
if (!dbPath) {
|
||||
@@ -26,7 +26,7 @@ export class SessionSearch {
|
||||
dbPath = DB_PATH;
|
||||
}
|
||||
this.db = new Database(dbPath);
|
||||
this.db.pragma('journal_mode = WAL');
|
||||
this.db.run('PRAGMA journal_mode = WAL');
|
||||
|
||||
// Ensure FTS tables exist
|
||||
this.ensureFTSTables();
|
||||
@@ -60,7 +60,7 @@ export class SessionSearch {
|
||||
console.error('[SessionSearch] Creating FTS5 tables...');
|
||||
|
||||
// Create observations_fts virtual table
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
|
||||
title,
|
||||
subtitle,
|
||||
@@ -74,14 +74,14 @@ export class SessionSearch {
|
||||
`);
|
||||
|
||||
// Populate with existing data
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
INSERT INTO observations_fts(rowid, title, subtitle, narrative, text, facts, concepts)
|
||||
SELECT id, title, subtitle, narrative, text, facts, concepts
|
||||
FROM observations;
|
||||
`);
|
||||
|
||||
// Create triggers for observations
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE TRIGGER IF NOT EXISTS observations_ai AFTER INSERT ON observations BEGIN
|
||||
INSERT INTO observations_fts(rowid, title, subtitle, narrative, text, facts, concepts)
|
||||
VALUES (new.id, new.title, new.subtitle, new.narrative, new.text, new.facts, new.concepts);
|
||||
@@ -101,7 +101,7 @@ export class SessionSearch {
|
||||
`);
|
||||
|
||||
// Create session_summaries_fts virtual table
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS session_summaries_fts USING fts5(
|
||||
request,
|
||||
investigated,
|
||||
@@ -115,14 +115,14 @@ export class SessionSearch {
|
||||
`);
|
||||
|
||||
// Populate with existing data
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
INSERT INTO session_summaries_fts(rowid, request, investigated, learned, completed, next_steps, notes)
|
||||
SELECT id, request, investigated, learned, completed, next_steps, notes
|
||||
FROM session_summaries;
|
||||
`);
|
||||
|
||||
// Create triggers for session_summaries
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE TRIGGER IF NOT EXISTS session_summaries_ai AFTER INSERT ON session_summaries BEGIN
|
||||
INSERT INTO session_summaries_fts(rowid, request, investigated, learned, completed, next_steps, notes)
|
||||
VALUES (new.id, new.request, new.investigated, new.learned, new.completed, new.next_steps, new.notes);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { DATA_DIR, DB_PATH, ensureDir } from '../../shared/paths.js';
|
||||
import { logger } from '../../utils/logger.js';
|
||||
import {
|
||||
@@ -18,16 +18,16 @@ import {
|
||||
* Provides simple, synchronous CRUD operations for session-based memory
|
||||
*/
|
||||
export class SessionStore {
|
||||
public db: Database.Database;
|
||||
public db: Database;
|
||||
|
||||
constructor() {
|
||||
ensureDir(DATA_DIR);
|
||||
this.db = new Database(DB_PATH);
|
||||
|
||||
// Ensure optimized settings
|
||||
this.db.pragma('journal_mode = WAL');
|
||||
this.db.pragma('synchronous = NORMAL');
|
||||
this.db.pragma('foreign_keys = ON');
|
||||
this.db.run('PRAGMA journal_mode = WAL');
|
||||
this.db.run('PRAGMA synchronous = NORMAL');
|
||||
this.db.run('PRAGMA foreign_keys = ON');
|
||||
|
||||
// Initialize schema if needed (fresh database)
|
||||
this.initializeSchema();
|
||||
@@ -49,7 +49,7 @@ export class SessionStore {
|
||||
private initializeSchema(): void {
|
||||
try {
|
||||
// Create schema_versions table if it doesn't exist
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE TABLE IF NOT EXISTS schema_versions (
|
||||
id INTEGER PRIMARY KEY,
|
||||
version INTEGER UNIQUE NOT NULL,
|
||||
@@ -67,7 +67,7 @@ export class SessionStore {
|
||||
console.error('[SessionStore] Initializing fresh database with migration004...');
|
||||
|
||||
// Migration004: SDK agent architecture tables
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE TABLE IF NOT EXISTS sdk_sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
claude_session_id TEXT UNIQUE NOT NULL,
|
||||
@@ -146,11 +146,11 @@ export class SessionStore {
|
||||
if (applied) return;
|
||||
|
||||
// Check if column exists
|
||||
const tableInfo = this.db.pragma('table_info(sdk_sessions)') as TableColumnInfo[];
|
||||
const tableInfo = this.db.query('PRAGMA table_info(sdk_sessions)').all() as TableColumnInfo[];
|
||||
const hasWorkerPort = tableInfo.some(col => col.name === 'worker_port');
|
||||
|
||||
if (!hasWorkerPort) {
|
||||
this.db.exec('ALTER TABLE sdk_sessions ADD COLUMN worker_port INTEGER');
|
||||
this.db.run('ALTER TABLE sdk_sessions ADD COLUMN worker_port INTEGER');
|
||||
console.error('[SessionStore] Added worker_port column to sdk_sessions table');
|
||||
}
|
||||
|
||||
@@ -171,29 +171,29 @@ export class SessionStore {
|
||||
if (applied) return;
|
||||
|
||||
// Check sdk_sessions for prompt_counter
|
||||
const sessionsInfo = this.db.pragma('table_info(sdk_sessions)') as TableColumnInfo[];
|
||||
const sessionsInfo = this.db.query('PRAGMA table_info(sdk_sessions)').all() as TableColumnInfo[];
|
||||
const hasPromptCounter = sessionsInfo.some(col => col.name === 'prompt_counter');
|
||||
|
||||
if (!hasPromptCounter) {
|
||||
this.db.exec('ALTER TABLE sdk_sessions ADD COLUMN prompt_counter INTEGER DEFAULT 0');
|
||||
this.db.run('ALTER TABLE sdk_sessions ADD COLUMN prompt_counter INTEGER DEFAULT 0');
|
||||
console.error('[SessionStore] Added prompt_counter column to sdk_sessions table');
|
||||
}
|
||||
|
||||
// Check observations for prompt_number
|
||||
const observationsInfo = this.db.pragma('table_info(observations)') as TableColumnInfo[];
|
||||
const observationsInfo = this.db.query('PRAGMA table_info(observations)').all() as TableColumnInfo[];
|
||||
const obsHasPromptNumber = observationsInfo.some(col => col.name === 'prompt_number');
|
||||
|
||||
if (!obsHasPromptNumber) {
|
||||
this.db.exec('ALTER TABLE observations ADD COLUMN prompt_number INTEGER');
|
||||
this.db.run('ALTER TABLE observations ADD COLUMN prompt_number INTEGER');
|
||||
console.error('[SessionStore] Added prompt_number column to observations table');
|
||||
}
|
||||
|
||||
// Check session_summaries for prompt_number
|
||||
const summariesInfo = this.db.pragma('table_info(session_summaries)') as TableColumnInfo[];
|
||||
const summariesInfo = this.db.query('PRAGMA table_info(session_summaries)').all() as TableColumnInfo[];
|
||||
const sumHasPromptNumber = summariesInfo.some(col => col.name === 'prompt_number');
|
||||
|
||||
if (!sumHasPromptNumber) {
|
||||
this.db.exec('ALTER TABLE session_summaries ADD COLUMN prompt_number INTEGER');
|
||||
this.db.run('ALTER TABLE session_summaries ADD COLUMN prompt_number INTEGER');
|
||||
console.error('[SessionStore] Added prompt_number column to session_summaries table');
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ export class SessionStore {
|
||||
if (applied) return;
|
||||
|
||||
// Check if UNIQUE constraint exists
|
||||
const summariesIndexes = this.db.pragma('index_list(session_summaries)') as IndexInfo[];
|
||||
const summariesIndexes = this.db.query('PRAGMA index_list(session_summaries)').all() as IndexInfo[];
|
||||
const hasUniqueConstraint = summariesIndexes.some(idx => idx.unique === 1);
|
||||
|
||||
if (!hasUniqueConstraint) {
|
||||
@@ -226,11 +226,11 @@ export class SessionStore {
|
||||
console.error('[SessionStore] Removing UNIQUE constraint from session_summaries.sdk_session_id...');
|
||||
|
||||
// Begin transaction
|
||||
this.db.exec('BEGIN TRANSACTION');
|
||||
this.db.run('BEGIN TRANSACTION');
|
||||
|
||||
try {
|
||||
// Create new table without UNIQUE constraint
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE TABLE session_summaries_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT NOT NULL,
|
||||
@@ -251,7 +251,7 @@ export class SessionStore {
|
||||
`);
|
||||
|
||||
// Copy data from old table
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
INSERT INTO session_summaries_new
|
||||
SELECT id, sdk_session_id, project, request, investigated, learned,
|
||||
completed, next_steps, files_read, files_edited, notes,
|
||||
@@ -260,20 +260,20 @@ export class SessionStore {
|
||||
`);
|
||||
|
||||
// Drop old table
|
||||
this.db.exec('DROP TABLE session_summaries');
|
||||
this.db.run('DROP TABLE session_summaries');
|
||||
|
||||
// Rename new table
|
||||
this.db.exec('ALTER TABLE session_summaries_new RENAME TO session_summaries');
|
||||
this.db.run('ALTER TABLE session_summaries_new RENAME TO session_summaries');
|
||||
|
||||
// Recreate indexes
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE INDEX idx_session_summaries_sdk_session ON session_summaries(sdk_session_id);
|
||||
CREATE INDEX idx_session_summaries_project ON session_summaries(project);
|
||||
CREATE INDEX idx_session_summaries_created ON session_summaries(created_at_epoch DESC);
|
||||
`);
|
||||
|
||||
// Commit transaction
|
||||
this.db.exec('COMMIT');
|
||||
this.db.run('COMMIT');
|
||||
|
||||
// Record migration
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(7, new Date().toISOString());
|
||||
@@ -281,7 +281,7 @@ export class SessionStore {
|
||||
console.error('[SessionStore] Successfully removed UNIQUE constraint from session_summaries.sdk_session_id');
|
||||
} catch (error: any) {
|
||||
// Rollback on error
|
||||
this.db.exec('ROLLBACK');
|
||||
this.db.run('ROLLBACK');
|
||||
throw error;
|
||||
}
|
||||
} catch (error: any) {
|
||||
@@ -299,7 +299,7 @@ export class SessionStore {
|
||||
if (applied) return;
|
||||
|
||||
// Check if new fields already exist
|
||||
const tableInfo = this.db.pragma('table_info(observations)') as TableColumnInfo[];
|
||||
const tableInfo = this.db.query('PRAGMA table_info(observations)').all() as TableColumnInfo[];
|
||||
const hasTitle = tableInfo.some(col => col.name === 'title');
|
||||
|
||||
if (hasTitle) {
|
||||
@@ -311,7 +311,7 @@ export class SessionStore {
|
||||
console.error('[SessionStore] Adding hierarchical fields to observations table...');
|
||||
|
||||
// Add new columns
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
ALTER TABLE observations ADD COLUMN title TEXT;
|
||||
ALTER TABLE observations ADD COLUMN subtitle TEXT;
|
||||
ALTER TABLE observations ADD COLUMN facts TEXT;
|
||||
@@ -341,7 +341,7 @@ export class SessionStore {
|
||||
if (applied) return;
|
||||
|
||||
// Check if text column is already nullable
|
||||
const tableInfo = this.db.pragma('table_info(observations)') as TableColumnInfo[];
|
||||
const tableInfo = this.db.query('PRAGMA table_info(observations)').all() as TableColumnInfo[];
|
||||
const textColumn = tableInfo.find(col => col.name === 'text');
|
||||
|
||||
if (!textColumn || textColumn.notnull === 0) {
|
||||
@@ -353,11 +353,11 @@ export class SessionStore {
|
||||
console.error('[SessionStore] Making observations.text nullable...');
|
||||
|
||||
// Begin transaction
|
||||
this.db.exec('BEGIN TRANSACTION');
|
||||
this.db.run('BEGIN TRANSACTION');
|
||||
|
||||
try {
|
||||
// Create new table with text as nullable
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE TABLE observations_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT NOT NULL,
|
||||
@@ -379,7 +379,7 @@ export class SessionStore {
|
||||
`);
|
||||
|
||||
// Copy data from old table (all existing columns)
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
INSERT INTO observations_new
|
||||
SELECT id, sdk_session_id, project, text, type, title, subtitle, facts,
|
||||
narrative, concepts, files_read, files_modified, prompt_number,
|
||||
@@ -388,13 +388,13 @@ export class SessionStore {
|
||||
`);
|
||||
|
||||
// Drop old table
|
||||
this.db.exec('DROP TABLE observations');
|
||||
this.db.run('DROP TABLE observations');
|
||||
|
||||
// Rename new table
|
||||
this.db.exec('ALTER TABLE observations_new RENAME TO observations');
|
||||
this.db.run('ALTER TABLE observations_new RENAME TO observations');
|
||||
|
||||
// Recreate indexes
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE INDEX idx_observations_sdk_session ON observations(sdk_session_id);
|
||||
CREATE INDEX idx_observations_project ON observations(project);
|
||||
CREATE INDEX idx_observations_type ON observations(type);
|
||||
@@ -402,7 +402,7 @@ export class SessionStore {
|
||||
`);
|
||||
|
||||
// Commit transaction
|
||||
this.db.exec('COMMIT');
|
||||
this.db.run('COMMIT');
|
||||
|
||||
// Record migration
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(9, new Date().toISOString());
|
||||
@@ -410,7 +410,7 @@ export class SessionStore {
|
||||
console.error('[SessionStore] Successfully made observations.text nullable');
|
||||
} catch (error: any) {
|
||||
// Rollback on error
|
||||
this.db.exec('ROLLBACK');
|
||||
this.db.run('ROLLBACK');
|
||||
throw error;
|
||||
}
|
||||
} catch (error: any) {
|
||||
@@ -428,7 +428,7 @@ export class SessionStore {
|
||||
if (applied) return;
|
||||
|
||||
// Check if table already exists
|
||||
const tableInfo = this.db.pragma('table_info(user_prompts)') as TableColumnInfo[];
|
||||
const tableInfo = this.db.query('PRAGMA table_info(user_prompts)').all() as TableColumnInfo[];
|
||||
if (tableInfo.length > 0) {
|
||||
// Already migrated
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(10, new Date().toISOString());
|
||||
@@ -438,11 +438,11 @@ export class SessionStore {
|
||||
console.error('[SessionStore] Creating user_prompts table with FTS5 support...');
|
||||
|
||||
// Begin transaction
|
||||
this.db.exec('BEGIN TRANSACTION');
|
||||
this.db.run('BEGIN TRANSACTION');
|
||||
|
||||
try {
|
||||
// Create main table (using claude_session_id since sdk_session_id is set asynchronously by worker)
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE TABLE user_prompts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
claude_session_id TEXT NOT NULL,
|
||||
@@ -460,7 +460,7 @@ export class SessionStore {
|
||||
`);
|
||||
|
||||
// Create FTS5 virtual table
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE VIRTUAL TABLE user_prompts_fts USING fts5(
|
||||
prompt_text,
|
||||
content='user_prompts',
|
||||
@@ -469,7 +469,7 @@ export class SessionStore {
|
||||
`);
|
||||
|
||||
// Create triggers to sync FTS5
|
||||
this.db.exec(`
|
||||
this.db.run(`
|
||||
CREATE TRIGGER user_prompts_ai AFTER INSERT ON user_prompts BEGIN
|
||||
INSERT INTO user_prompts_fts(rowid, prompt_text)
|
||||
VALUES (new.id, new.prompt_text);
|
||||
@@ -489,7 +489,7 @@ export class SessionStore {
|
||||
`);
|
||||
|
||||
// Commit transaction
|
||||
this.db.exec('COMMIT');
|
||||
this.db.run('COMMIT');
|
||||
|
||||
// Record migration
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(10, new Date().toISOString());
|
||||
@@ -497,7 +497,7 @@ export class SessionStore {
|
||||
console.error('[SessionStore] Successfully created user_prompts table with FTS5 support');
|
||||
} catch (error: any) {
|
||||
// Rollback on error
|
||||
this.db.exec('ROLLBACK');
|
||||
this.db.run('ROLLBACK');
|
||||
throw error;
|
||||
}
|
||||
} catch (error: any) {
|
||||
@@ -517,20 +517,20 @@ export class SessionStore {
|
||||
if (applied) return;
|
||||
|
||||
// Check if discovery_tokens column exists in observations table
|
||||
const observationsInfo = this.db.pragma('table_info(observations)') as TableColumnInfo[];
|
||||
const observationsInfo = this.db.query('PRAGMA table_info(observations)').all() as TableColumnInfo[];
|
||||
const obsHasDiscoveryTokens = observationsInfo.some(col => col.name === 'discovery_tokens');
|
||||
|
||||
if (!obsHasDiscoveryTokens) {
|
||||
this.db.exec('ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0');
|
||||
this.db.run('ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0');
|
||||
console.error('[SessionStore] Added discovery_tokens column to observations table');
|
||||
}
|
||||
|
||||
// Check if discovery_tokens column exists in session_summaries table
|
||||
const summariesInfo = this.db.pragma('table_info(session_summaries)') as TableColumnInfo[];
|
||||
const summariesInfo = this.db.query('PRAGMA table_info(session_summaries)').all() as TableColumnInfo[];
|
||||
const sumHasDiscoveryTokens = summariesInfo.some(col => col.name === 'discovery_tokens');
|
||||
|
||||
if (!sumHasDiscoveryTokens) {
|
||||
this.db.exec('ALTER TABLE session_summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0');
|
||||
this.db.run('ALTER TABLE session_summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0');
|
||||
console.error('[SessionStore] Added discovery_tokens column to session_summaries table');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Database } from 'better-sqlite3';
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { Migration } from './Database.js';
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user