@codemcp/workflows-core 3.1.21-fix-build-after-monorepo.0 → 3.1.21-fix-build-after-monorepo.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/.turbo/turbo-build.log +1 -1
- package/dist/database.d.ts +24 -15
- package/dist/database.js +217 -124
- package/dist/database.js.map +1 -1
- package/package.json +1 -1
- package/src/database.ts +254 -200
package/.turbo/turbo-build.log
CHANGED
@@ -1,4 +1,4 @@
|
|
1
1
|
|
2
|
-
> @codemcp/workflows-core@3.1.21-fix-build-after-monorepo.
|
2
|
+
> @codemcp/workflows-core@3.1.21-fix-build-after-monorepo.1 build /home/runner/work/responsible-vibe-mcp/responsible-vibe-mcp/packages/core
|
3
3
|
> tsc -p tsconfig.build.json
|
4
4
|
|
package/dist/database.d.ts
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
* Database Manager
|
3
3
|
*
|
4
4
|
* Handles SQLite database operations for conversation state persistence.
|
5
|
-
* Uses
|
5
|
+
* Uses @sqlite.org/sqlite-wasm for reliable cross-platform WebAssembly bindings.
|
6
6
|
*/
|
7
7
|
import type { ConversationState, InteractionLog } from './types.js';
|
8
8
|
/**
|
@@ -10,12 +10,21 @@ import type { ConversationState, InteractionLog } from './types.js';
|
|
10
10
|
*/
|
11
11
|
export declare class Database {
|
12
12
|
private db;
|
13
|
+
private sqlite3;
|
13
14
|
private dbPath;
|
14
15
|
constructor(dbPath: string);
|
15
16
|
/**
|
16
17
|
* Initialize database connection and create tables
|
17
18
|
*/
|
18
19
|
initialize(): Promise<void>;
|
20
|
+
/**
|
21
|
+
* Load database content from file
|
22
|
+
*/
|
23
|
+
private loadFromFile;
|
24
|
+
/**
|
25
|
+
* Save database content to file
|
26
|
+
*/
|
27
|
+
private saveToFile;
|
19
28
|
/**
|
20
29
|
* Create database tables if they don't exist
|
21
30
|
*/
|
@@ -25,39 +34,39 @@ export declare class Database {
|
|
25
34
|
*/
|
26
35
|
saveConversationState(state: ConversationState): Promise<void>;
|
27
36
|
/**
|
28
|
-
*
|
29
|
-
*/
|
30
|
-
loadConversationState(conversationId: string): Promise<ConversationState | null>;
|
31
|
-
/**
|
32
|
-
* Get conversation state by ID (alias for loadConversationState)
|
37
|
+
* Get conversation state by ID
|
33
38
|
*/
|
34
39
|
getConversationState(conversationId: string): Promise<ConversationState | null>;
|
35
40
|
/**
|
36
|
-
*
|
41
|
+
* Get all conversation states
|
37
42
|
*/
|
38
|
-
|
43
|
+
getAllConversationStates(): Promise<ConversationState[]>;
|
39
44
|
/**
|
40
45
|
* Delete conversation state
|
41
46
|
*/
|
42
47
|
deleteConversationState(conversationId: string): Promise<boolean>;
|
43
48
|
/**
|
44
|
-
* Log
|
49
|
+
* Log interaction
|
45
50
|
*/
|
46
51
|
logInteraction(log: InteractionLog): Promise<void>;
|
47
52
|
/**
|
48
|
-
* Get
|
53
|
+
* Get interaction logs for a conversation
|
54
|
+
*/
|
55
|
+
getInteractionLogs(conversationId: string): Promise<InteractionLog[]>;
|
56
|
+
/**
|
57
|
+
* Get interaction logs for a conversation (alias for compatibility)
|
49
58
|
*/
|
50
59
|
getInteractionsByConversationId(conversationId: string): Promise<InteractionLog[]>;
|
51
60
|
/**
|
52
|
-
* Soft delete interaction logs
|
61
|
+
* Soft delete interaction logs (for compatibility - actually deletes them)
|
53
62
|
*/
|
54
63
|
softDeleteInteractionLogs(conversationId: string): Promise<void>;
|
55
64
|
/**
|
56
|
-
*
|
65
|
+
* Reset conversation state (for testing)
|
57
66
|
*/
|
58
|
-
|
67
|
+
resetConversationState(conversationId: string): Promise<void>;
|
59
68
|
/**
|
60
|
-
*
|
69
|
+
* Close database connection
|
61
70
|
*/
|
62
|
-
|
71
|
+
close(): Promise<void>;
|
63
72
|
}
|
package/dist/database.js
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
* Database Manager
|
3
3
|
*
|
4
4
|
* Handles SQLite database operations for conversation state persistence.
|
5
|
-
* Uses
|
5
|
+
* Uses @sqlite.org/sqlite-wasm for reliable cross-platform WebAssembly bindings.
|
6
6
|
*/
|
7
|
-
import
|
7
|
+
import sqlite3InitModule from '@sqlite.org/sqlite-wasm';
|
8
8
|
import { mkdir } from 'node:fs/promises';
|
9
9
|
import { dirname } from 'node:path';
|
10
10
|
import { createLogger } from './logger.js';
|
@@ -14,6 +14,7 @@ const logger = createLogger('Database');
|
|
14
14
|
*/
|
15
15
|
export class Database {
|
16
16
|
db = null;
|
17
|
+
sqlite3 = null;
|
17
18
|
dbPath;
|
18
19
|
constructor(dbPath) {
|
19
20
|
this.dbPath = dbPath;
|
@@ -23,15 +24,19 @@ export class Database {
|
|
23
24
|
*/
|
24
25
|
async initialize() {
|
25
26
|
try {
|
26
|
-
//
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
// Initialize SQLite WASM
|
28
|
+
this.sqlite3 = await sqlite3InitModule();
|
29
|
+
// Always use in-memory database (sqlite-wasm Node.js limitation)
|
30
|
+
this.db = new this.sqlite3.oo1.DB();
|
31
|
+
logger.debug('Database connection established (in-memory)', {
|
32
|
+
originalPath: this.dbPath,
|
33
|
+
});
|
33
34
|
// Create tables
|
34
35
|
await this.createTables();
|
36
|
+
// Load existing data from file if it exists
|
37
|
+
if (this.dbPath !== ':memory:' && this.dbPath) {
|
38
|
+
await this.loadFromFile();
|
39
|
+
}
|
35
40
|
logger.info('Database initialized successfully', { dbPath: this.dbPath });
|
36
41
|
}
|
37
42
|
catch (error) {
|
@@ -39,6 +44,76 @@ export class Database {
|
|
39
44
|
throw error;
|
40
45
|
}
|
41
46
|
}
|
47
|
+
/**
|
48
|
+
* Load database content from file
|
49
|
+
*/
|
50
|
+
async loadFromFile() {
|
51
|
+
if (!this.db || !this.dbPath || this.dbPath === ':memory:') {
|
52
|
+
return;
|
53
|
+
}
|
54
|
+
try {
|
55
|
+
const { readFile, access } = await import('node:fs/promises');
|
56
|
+
await access(this.dbPath);
|
57
|
+
const data = await readFile(this.dbPath);
|
58
|
+
if (data.length > 0) {
|
59
|
+
// Close current in-memory DB and create new one from file data
|
60
|
+
this.db.close();
|
61
|
+
// Create new DB and deserialize data into it
|
62
|
+
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
63
|
+
this.db = new this.sqlite3.oo1.DB();
|
64
|
+
if (!this.db.pointer) {
|
65
|
+
throw new Error('Failed to create database');
|
66
|
+
}
|
67
|
+
// Convert Buffer to Uint8Array
|
68
|
+
const uint8Data = new Uint8Array(data);
|
69
|
+
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
70
|
+
const wasmPtr = this.sqlite3.wasm.allocFromTypedArray(uint8Data);
|
71
|
+
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
72
|
+
this.sqlite3.capi.sqlite3_deserialize(this.db.pointer, 'main', wasmPtr, data.length, data.length, 0x01 // SQLITE_DESERIALIZE_FREEONCLOSE
|
73
|
+
);
|
74
|
+
logger.debug('Loaded database from file', {
|
75
|
+
dbPath: this.dbPath,
|
76
|
+
size: data.length,
|
77
|
+
});
|
78
|
+
}
|
79
|
+
}
|
80
|
+
catch {
|
81
|
+
// File doesn't exist - that's OK for new databases
|
82
|
+
logger.debug('No existing database file to load', {
|
83
|
+
dbPath: this.dbPath,
|
84
|
+
});
|
85
|
+
}
|
86
|
+
}
|
87
|
+
/**
|
88
|
+
* Save database content to file
|
89
|
+
*/
|
90
|
+
async saveToFile() {
|
91
|
+
if (!this.db || !this.dbPath || this.dbPath === ':memory:') {
|
92
|
+
return;
|
93
|
+
}
|
94
|
+
try {
|
95
|
+
const { writeFile } = await import('node:fs/promises');
|
96
|
+
const dbDir = dirname(this.dbPath);
|
97
|
+
await mkdir(dbDir, { recursive: true });
|
98
|
+
// Export database to Uint8Array and save to file
|
99
|
+
if (!this.db.pointer) {
|
100
|
+
throw new Error('Database pointer is invalid');
|
101
|
+
}
|
102
|
+
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
103
|
+
const data = this.sqlite3.capi.sqlite3_js_db_export(this.db.pointer);
|
104
|
+
await writeFile(this.dbPath, data);
|
105
|
+
logger.debug('Saved database to file', {
|
106
|
+
dbPath: this.dbPath,
|
107
|
+
size: data.length,
|
108
|
+
});
|
109
|
+
}
|
110
|
+
catch (error) {
|
111
|
+
logger.warn('Failed to save database to file', {
|
112
|
+
error: error,
|
113
|
+
dbPath: this.dbPath,
|
114
|
+
});
|
115
|
+
}
|
116
|
+
}
|
42
117
|
/**
|
43
118
|
* Create database tables if they don't exist
|
44
119
|
*/
|
@@ -69,14 +144,12 @@ export class Database {
|
|
69
144
|
responseData TEXT NOT NULL,
|
70
145
|
currentPhase TEXT NOT NULL,
|
71
146
|
timestamp TEXT NOT NULL,
|
72
|
-
isReset INTEGER DEFAULT 0,
|
73
|
-
resetAt TEXT,
|
74
147
|
FOREIGN KEY (conversationId) REFERENCES conversation_state(conversationId)
|
75
148
|
)
|
76
149
|
`;
|
77
150
|
this.db.exec(createConversationStateTable);
|
78
151
|
this.db.exec(createInteractionLogTable);
|
79
|
-
logger.debug('
|
152
|
+
logger.debug('Database tables created');
|
80
153
|
}
|
81
154
|
/**
|
82
155
|
* Save conversation state to database
|
@@ -85,84 +158,86 @@ export class Database {
|
|
85
158
|
if (!this.db) {
|
86
159
|
throw new Error('Database not initialized');
|
87
160
|
}
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
161
|
+
this.db.exec({
|
162
|
+
sql: `INSERT OR REPLACE INTO conversation_state
|
163
|
+
(conversationId, projectPath, gitBranch, currentPhase, planFilePath, workflowName,
|
164
|
+
gitCommitConfig, requireReviewsBeforePhaseTransition, createdAt, updatedAt)
|
165
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
166
|
+
bind: [
|
167
|
+
state.conversationId,
|
168
|
+
state.projectPath,
|
169
|
+
state.gitBranch,
|
170
|
+
state.currentPhase,
|
171
|
+
state.planFilePath,
|
172
|
+
state.workflowName,
|
173
|
+
state.gitCommitConfig ? JSON.stringify(state.gitCommitConfig) : null,
|
174
|
+
state.requireReviewsBeforePhaseTransition ? 1 : 0,
|
175
|
+
state.createdAt,
|
176
|
+
state.updatedAt,
|
177
|
+
],
|
178
|
+
});
|
179
|
+
// Persist to file
|
180
|
+
await this.saveToFile();
|
95
181
|
logger.debug('Conversation state saved', {
|
96
182
|
conversationId: state.conversationId,
|
97
|
-
workflowName: state.workflowName,
|
98
183
|
currentPhase: state.currentPhase,
|
99
184
|
});
|
100
185
|
}
|
101
186
|
/**
|
102
|
-
*
|
187
|
+
* Get conversation state by ID
|
103
188
|
*/
|
104
|
-
async
|
189
|
+
async getConversationState(conversationId) {
|
105
190
|
if (!this.db) {
|
106
191
|
throw new Error('Database not initialized');
|
107
192
|
}
|
108
|
-
const
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
currentPhase: row.currentPhase,
|
116
|
-
planFilePath: row.planFilePath,
|
117
|
-
workflowName: row.workflowName,
|
118
|
-
gitCommitConfig: row.gitCommitConfig
|
119
|
-
? JSON.parse(row.gitCommitConfig)
|
120
|
-
: undefined,
|
121
|
-
requireReviewsBeforePhaseTransition: row.requireReviewsBeforePhaseTransition === 1,
|
122
|
-
createdAt: row.createdAt,
|
123
|
-
updatedAt: row.updatedAt,
|
124
|
-
};
|
125
|
-
logger.debug('Conversation state loaded', {
|
126
|
-
conversationId,
|
127
|
-
workflowName: state.workflowName,
|
128
|
-
currentPhase: state.currentPhase,
|
129
|
-
});
|
130
|
-
return state;
|
193
|
+
const result = this.db.exec({
|
194
|
+
sql: 'SELECT * FROM conversation_state WHERE conversationId = ?',
|
195
|
+
bind: [conversationId],
|
196
|
+
returnValue: 'resultRows',
|
197
|
+
});
|
198
|
+
if (!result || result.length === 0) {
|
199
|
+
return null;
|
131
200
|
}
|
132
|
-
|
133
|
-
return
|
201
|
+
const row = result[0];
|
202
|
+
return {
|
203
|
+
conversationId: row[0],
|
204
|
+
projectPath: row[1],
|
205
|
+
gitBranch: row[2],
|
206
|
+
currentPhase: row[3],
|
207
|
+
planFilePath: row[4],
|
208
|
+
workflowName: row[5],
|
209
|
+
gitCommitConfig: row[6] ? JSON.parse(row[6]) : null,
|
210
|
+
requireReviewsBeforePhaseTransition: Boolean(row[7]),
|
211
|
+
createdAt: row[8],
|
212
|
+
updatedAt: row[9],
|
213
|
+
};
|
134
214
|
}
|
135
215
|
/**
|
136
|
-
* Get conversation
|
216
|
+
* Get all conversation states
|
137
217
|
*/
|
138
|
-
async
|
139
|
-
return this.loadConversationState(conversationId);
|
140
|
-
}
|
141
|
-
/**
|
142
|
-
* List all conversation states
|
143
|
-
*/
|
144
|
-
async listConversationStates() {
|
218
|
+
async getAllConversationStates() {
|
145
219
|
if (!this.db) {
|
146
220
|
throw new Error('Database not initialized');
|
147
221
|
}
|
148
|
-
const
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
222
|
+
const result = this.db.exec({
|
223
|
+
sql: 'SELECT * FROM conversation_state ORDER BY updatedAt DESC',
|
224
|
+
returnValue: 'resultRows',
|
225
|
+
});
|
226
|
+
if (!result) {
|
227
|
+
return [];
|
228
|
+
}
|
229
|
+
return result.map(row => ({
|
230
|
+
conversationId: row[0],
|
231
|
+
projectPath: row[1],
|
232
|
+
gitBranch: row[2],
|
233
|
+
currentPhase: row[3],
|
234
|
+
planFilePath: row[4],
|
235
|
+
workflowName: row[5],
|
236
|
+
gitCommitConfig: row[6] ? JSON.parse(row[6]) : null,
|
237
|
+
requireReviewsBeforePhaseTransition: Boolean(row[7]),
|
238
|
+
createdAt: row[8],
|
239
|
+
updatedAt: row[9],
|
163
240
|
}));
|
164
|
-
logger.debug('Listed conversation states', { count: states.length });
|
165
|
-
return states;
|
166
241
|
}
|
167
242
|
/**
|
168
243
|
* Delete conversation state
|
@@ -171,79 +246,103 @@ export class Database {
|
|
171
246
|
if (!this.db) {
|
172
247
|
throw new Error('Database not initialized');
|
173
248
|
}
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
249
|
+
this.db.exec({
|
250
|
+
sql: 'DELETE FROM conversation_state WHERE conversationId = ?',
|
251
|
+
bind: [conversationId],
|
252
|
+
});
|
253
|
+
// Persist to file
|
254
|
+
await this.saveToFile();
|
255
|
+
logger.debug('Conversation state deleted', { conversationId });
|
256
|
+
return true;
|
179
257
|
}
|
180
258
|
/**
|
181
|
-
* Log
|
259
|
+
* Log interaction
|
182
260
|
*/
|
183
261
|
async logInteraction(log) {
|
184
262
|
if (!this.db) {
|
185
263
|
throw new Error('Database not initialized');
|
186
264
|
}
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
265
|
+
this.db.exec({
|
266
|
+
sql: `INSERT INTO interaction_log
|
267
|
+
(conversationId, toolName, inputParams, responseData, currentPhase, timestamp)
|
268
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
269
|
+
bind: [
|
270
|
+
log.conversationId,
|
271
|
+
log.toolName,
|
272
|
+
JSON.stringify(log.inputParams),
|
273
|
+
JSON.stringify(log.responseData),
|
274
|
+
log.currentPhase,
|
275
|
+
log.timestamp,
|
276
|
+
],
|
277
|
+
});
|
278
|
+
// Persist to file
|
279
|
+
await this.saveToFile();
|
193
280
|
logger.debug('Interaction logged', {
|
194
281
|
conversationId: log.conversationId,
|
195
282
|
toolName: log.toolName,
|
196
|
-
timestamp: log.timestamp,
|
197
283
|
});
|
198
284
|
}
|
199
285
|
/**
|
200
|
-
* Get
|
286
|
+
* Get interaction logs for a conversation
|
201
287
|
*/
|
202
|
-
async
|
288
|
+
async getInteractionLogs(conversationId) {
|
203
289
|
if (!this.db) {
|
204
290
|
throw new Error('Database not initialized');
|
205
291
|
}
|
206
|
-
const
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
`);
|
211
|
-
const rows = stmt.all(conversationId);
|
212
|
-
const logs = rows.map(row => ({
|
213
|
-
id: row.id,
|
214
|
-
conversationId: row.conversationId,
|
215
|
-
toolName: row.toolName,
|
216
|
-
inputParams: row.inputParams,
|
217
|
-
responseData: row.responseData,
|
218
|
-
currentPhase: row.currentPhase,
|
219
|
-
timestamp: row.timestamp,
|
220
|
-
isReset: row.isReset === 1,
|
221
|
-
resetAt: row.resetAt,
|
222
|
-
}));
|
223
|
-
logger.debug('Retrieved interaction logs', {
|
224
|
-
conversationId,
|
225
|
-
count: logs.length,
|
292
|
+
const result = this.db.exec({
|
293
|
+
sql: 'SELECT * FROM interaction_log WHERE conversationId = ? ORDER BY timestamp ASC',
|
294
|
+
bind: [conversationId],
|
295
|
+
returnValue: 'resultRows',
|
226
296
|
});
|
227
|
-
|
297
|
+
if (!result) {
|
298
|
+
return [];
|
299
|
+
}
|
300
|
+
return result.map(row => ({
|
301
|
+
id: row[0],
|
302
|
+
conversationId: row[1],
|
303
|
+
toolName: row[2],
|
304
|
+
inputParams: JSON.parse(row[3]),
|
305
|
+
responseData: JSON.parse(row[4]),
|
306
|
+
currentPhase: row[5],
|
307
|
+
timestamp: row[6],
|
308
|
+
}));
|
309
|
+
}
|
310
|
+
/**
|
311
|
+
* Get interaction logs for a conversation (alias for compatibility)
|
312
|
+
*/
|
313
|
+
async getInteractionsByConversationId(conversationId) {
|
314
|
+
return this.getInteractionLogs(conversationId);
|
228
315
|
}
|
229
316
|
/**
|
230
|
-
* Soft delete interaction logs
|
317
|
+
* Soft delete interaction logs (for compatibility - actually deletes them)
|
231
318
|
*/
|
232
319
|
async softDeleteInteractionLogs(conversationId) {
|
320
|
+
if (!this.db) {
|
321
|
+
throw new Error('Database not initialized');
|
322
|
+
}
|
323
|
+
this.db.exec({
|
324
|
+
sql: 'DELETE FROM interaction_log WHERE conversationId = ?',
|
325
|
+
bind: [conversationId],
|
326
|
+
});
|
327
|
+
// Persist to file
|
328
|
+
await this.saveToFile();
|
329
|
+
logger.debug('Interaction logs deleted', { conversationId });
|
330
|
+
}
|
331
|
+
/**
|
332
|
+
* Reset conversation state (for testing)
|
333
|
+
*/
|
334
|
+
async resetConversationState(conversationId) {
|
233
335
|
if (!this.db) {
|
234
336
|
throw new Error('Database not initialized');
|
235
337
|
}
|
236
338
|
const resetAt = new Date().toISOString();
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
WHERE conversationId = ? AND (isReset = 0 OR isReset IS NULL)
|
241
|
-
`);
|
242
|
-
const result = stmt.run(resetAt, conversationId);
|
243
|
-
logger.debug('Soft deleted interaction logs', {
|
244
|
-
conversationId,
|
245
|
-
affectedRows: result.changes,
|
339
|
+
this.db.exec({
|
340
|
+
sql: 'UPDATE conversation_state SET updatedAt = ? WHERE conversationId = ?',
|
341
|
+
bind: [resetAt, conversationId],
|
246
342
|
});
|
343
|
+
// Persist to file
|
344
|
+
await this.saveToFile();
|
345
|
+
logger.debug('Conversation state reset', { conversationId, resetAt });
|
247
346
|
}
|
248
347
|
/**
|
249
348
|
* Close database connection
|
@@ -255,11 +354,5 @@ export class Database {
|
|
255
354
|
logger.debug('Database connection closed');
|
256
355
|
}
|
257
356
|
}
|
258
|
-
/**
|
259
|
-
* Check if database is initialized
|
260
|
-
*/
|
261
|
-
isInitialized() {
|
262
|
-
return this.db !== null;
|
263
|
-
}
|
264
357
|
}
|
265
358
|
//# sourceMappingURL=database.js.map
|
package/dist/database.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"database.js","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/database.ts
CHANGED
@@ -2,10 +2,13 @@
|
|
2
2
|
* Database Manager
|
3
3
|
*
|
4
4
|
* Handles SQLite database operations for conversation state persistence.
|
5
|
-
* Uses
|
5
|
+
* Uses @sqlite.org/sqlite-wasm for reliable cross-platform WebAssembly bindings.
|
6
6
|
*/
|
7
7
|
|
8
|
-
import
|
8
|
+
import sqlite3InitModule, {
|
9
|
+
type Database as SqliteDatabase,
|
10
|
+
type Sqlite3Static,
|
11
|
+
} from '@sqlite.org/sqlite-wasm';
|
9
12
|
import { mkdir } from 'node:fs/promises';
|
10
13
|
import { dirname } from 'node:path';
|
11
14
|
import { createLogger } from './logger.js';
|
@@ -17,7 +20,8 @@ const logger = createLogger('Database');
|
|
17
20
|
* Database connection and operations manager
|
18
21
|
*/
|
19
22
|
export class Database {
|
20
|
-
private db:
|
23
|
+
private db: SqliteDatabase | null = null;
|
24
|
+
private sqlite3: Sqlite3Static | null = null;
|
21
25
|
private dbPath: string;
|
22
26
|
|
23
27
|
constructor(dbPath: string) {
|
@@ -29,17 +33,23 @@ export class Database {
|
|
29
33
|
*/
|
30
34
|
async initialize(): Promise<void> {
|
31
35
|
try {
|
32
|
-
//
|
33
|
-
|
34
|
-
await mkdir(dbDir, { recursive: true });
|
35
|
-
logger.debug('Database directory ensured', { dbDir });
|
36
|
+
// Initialize SQLite WASM
|
37
|
+
this.sqlite3 = await sqlite3InitModule();
|
36
38
|
|
37
|
-
//
|
38
|
-
this.db = new
|
39
|
-
logger.debug('Database connection established'
|
39
|
+
// Always use in-memory database (sqlite-wasm Node.js limitation)
|
40
|
+
this.db = new this.sqlite3.oo1.DB();
|
41
|
+
logger.debug('Database connection established (in-memory)', {
|
42
|
+
originalPath: this.dbPath,
|
43
|
+
});
|
40
44
|
|
41
45
|
// Create tables
|
42
46
|
await this.createTables();
|
47
|
+
|
48
|
+
// Load existing data from file if it exists
|
49
|
+
if (this.dbPath !== ':memory:' && this.dbPath) {
|
50
|
+
await this.loadFromFile();
|
51
|
+
}
|
52
|
+
|
43
53
|
logger.info('Database initialized successfully', { dbPath: this.dbPath });
|
44
54
|
} catch (error) {
|
45
55
|
logger.error('Failed to initialize database', error as Error);
|
@@ -47,6 +57,90 @@ export class Database {
|
|
47
57
|
}
|
48
58
|
}
|
49
59
|
|
60
|
+
/**
|
61
|
+
* Load database content from file
|
62
|
+
*/
|
63
|
+
private async loadFromFile(): Promise<void> {
|
64
|
+
if (!this.db || !this.dbPath || this.dbPath === ':memory:') {
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
|
68
|
+
try {
|
69
|
+
const { readFile, access } = await import('node:fs/promises');
|
70
|
+
await access(this.dbPath);
|
71
|
+
|
72
|
+
const data = await readFile(this.dbPath);
|
73
|
+
if (data.length > 0) {
|
74
|
+
// Close current in-memory DB and create new one from file data
|
75
|
+
this.db.close();
|
76
|
+
// Create new DB and deserialize data into it
|
77
|
+
|
78
|
+
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
79
|
+
this.db = new this.sqlite3!.oo1.DB();
|
80
|
+
if (!this.db.pointer) {
|
81
|
+
throw new Error('Failed to create database');
|
82
|
+
}
|
83
|
+
|
84
|
+
// Convert Buffer to Uint8Array
|
85
|
+
const uint8Data = new Uint8Array(data);
|
86
|
+
|
87
|
+
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
88
|
+
const wasmPtr = this.sqlite3!.wasm.allocFromTypedArray(uint8Data);
|
89
|
+
|
90
|
+
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
91
|
+
this.sqlite3!.capi.sqlite3_deserialize(
|
92
|
+
this.db.pointer,
|
93
|
+
'main',
|
94
|
+
wasmPtr,
|
95
|
+
data.length,
|
96
|
+
data.length,
|
97
|
+
0x01 // SQLITE_DESERIALIZE_FREEONCLOSE
|
98
|
+
);
|
99
|
+
logger.debug('Loaded database from file', {
|
100
|
+
dbPath: this.dbPath,
|
101
|
+
size: data.length,
|
102
|
+
});
|
103
|
+
}
|
104
|
+
} catch {
|
105
|
+
// File doesn't exist - that's OK for new databases
|
106
|
+
logger.debug('No existing database file to load', {
|
107
|
+
dbPath: this.dbPath,
|
108
|
+
});
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
/**
|
113
|
+
* Save database content to file
|
114
|
+
*/
|
115
|
+
private async saveToFile(): Promise<void> {
|
116
|
+
if (!this.db || !this.dbPath || this.dbPath === ':memory:') {
|
117
|
+
return;
|
118
|
+
}
|
119
|
+
|
120
|
+
try {
|
121
|
+
const { writeFile } = await import('node:fs/promises');
|
122
|
+
const dbDir = dirname(this.dbPath);
|
123
|
+
await mkdir(dbDir, { recursive: true });
|
124
|
+
|
125
|
+
// Export database to Uint8Array and save to file
|
126
|
+
if (!this.db.pointer) {
|
127
|
+
throw new Error('Database pointer is invalid');
|
128
|
+
}
|
129
|
+
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
130
|
+
const data = this.sqlite3!.capi.sqlite3_js_db_export(this.db.pointer);
|
131
|
+
await writeFile(this.dbPath, data);
|
132
|
+
logger.debug('Saved database to file', {
|
133
|
+
dbPath: this.dbPath,
|
134
|
+
size: data.length,
|
135
|
+
});
|
136
|
+
} catch (error) {
|
137
|
+
logger.warn('Failed to save database to file', {
|
138
|
+
error: error as Error,
|
139
|
+
dbPath: this.dbPath,
|
140
|
+
});
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
50
144
|
/**
|
51
145
|
* Create database tables if they don't exist
|
52
146
|
*/
|
@@ -79,15 +173,14 @@ export class Database {
|
|
79
173
|
responseData TEXT NOT NULL,
|
80
174
|
currentPhase TEXT NOT NULL,
|
81
175
|
timestamp TEXT NOT NULL,
|
82
|
-
isReset INTEGER DEFAULT 0,
|
83
|
-
resetAt TEXT,
|
84
176
|
FOREIGN KEY (conversationId) REFERENCES conversation_state(conversationId)
|
85
177
|
)
|
86
178
|
`;
|
87
179
|
|
88
180
|
this.db.exec(createConversationStateTable);
|
89
181
|
this.db.exec(createInteractionLogTable);
|
90
|
-
|
182
|
+
|
183
|
+
logger.debug('Database tables created');
|
91
184
|
}
|
92
185
|
|
93
186
|
/**
|
@@ -98,141 +191,98 @@ export class Database {
|
|
98
191
|
throw new Error('Database not initialized');
|
99
192
|
}
|
100
193
|
|
101
|
-
|
102
|
-
INSERT OR REPLACE INTO conversation_state
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
194
|
+
this.db.exec({
|
195
|
+
sql: `INSERT OR REPLACE INTO conversation_state
|
196
|
+
(conversationId, projectPath, gitBranch, currentPhase, planFilePath, workflowName,
|
197
|
+
gitCommitConfig, requireReviewsBeforePhaseTransition, createdAt, updatedAt)
|
198
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
199
|
+
bind: [
|
200
|
+
state.conversationId,
|
201
|
+
state.projectPath,
|
202
|
+
state.gitBranch,
|
203
|
+
state.currentPhase,
|
204
|
+
state.planFilePath,
|
205
|
+
state.workflowName,
|
206
|
+
state.gitCommitConfig ? JSON.stringify(state.gitCommitConfig) : null,
|
207
|
+
state.requireReviewsBeforePhaseTransition ? 1 : 0,
|
208
|
+
state.createdAt,
|
209
|
+
state.updatedAt,
|
210
|
+
],
|
211
|
+
});
|
212
|
+
|
213
|
+
// Persist to file
|
214
|
+
await this.saveToFile();
|
120
215
|
|
121
216
|
logger.debug('Conversation state saved', {
|
122
217
|
conversationId: state.conversationId,
|
123
|
-
workflowName: state.workflowName,
|
124
218
|
currentPhase: state.currentPhase,
|
125
219
|
});
|
126
220
|
}
|
127
221
|
|
128
222
|
/**
|
129
|
-
*
|
223
|
+
* Get conversation state by ID
|
130
224
|
*/
|
131
|
-
async
|
225
|
+
async getConversationState(
|
132
226
|
conversationId: string
|
133
227
|
): Promise<ConversationState | null> {
|
134
228
|
if (!this.db) {
|
135
229
|
throw new Error('Database not initialized');
|
136
230
|
}
|
137
231
|
|
138
|
-
const
|
139
|
-
'SELECT * FROM conversation_state WHERE conversationId = ?'
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
conversationId: string;
|
144
|
-
projectPath: string;
|
145
|
-
gitBranch: string;
|
146
|
-
currentPhase: string;
|
147
|
-
planFilePath: string;
|
148
|
-
workflowName: string;
|
149
|
-
gitCommitConfig: string;
|
150
|
-
requireReviewsBeforePhaseTransition: number;
|
151
|
-
createdAt: string;
|
152
|
-
updatedAt: string;
|
153
|
-
}
|
154
|
-
| undefined;
|
155
|
-
|
156
|
-
if (row) {
|
157
|
-
const state: ConversationState = {
|
158
|
-
conversationId: row.conversationId,
|
159
|
-
projectPath: row.projectPath,
|
160
|
-
gitBranch: row.gitBranch,
|
161
|
-
currentPhase: row.currentPhase,
|
162
|
-
planFilePath: row.planFilePath,
|
163
|
-
workflowName: row.workflowName,
|
164
|
-
gitCommitConfig: row.gitCommitConfig
|
165
|
-
? JSON.parse(row.gitCommitConfig)
|
166
|
-
: undefined,
|
167
|
-
requireReviewsBeforePhaseTransition:
|
168
|
-
row.requireReviewsBeforePhaseTransition === 1,
|
169
|
-
createdAt: row.createdAt,
|
170
|
-
updatedAt: row.updatedAt,
|
171
|
-
};
|
172
|
-
|
173
|
-
logger.debug('Conversation state loaded', {
|
174
|
-
conversationId,
|
175
|
-
workflowName: state.workflowName,
|
176
|
-
currentPhase: state.currentPhase,
|
177
|
-
});
|
178
|
-
return state;
|
179
|
-
}
|
232
|
+
const result = this.db.exec({
|
233
|
+
sql: 'SELECT * FROM conversation_state WHERE conversationId = ?',
|
234
|
+
bind: [conversationId],
|
235
|
+
returnValue: 'resultRows',
|
236
|
+
});
|
180
237
|
|
181
|
-
|
182
|
-
|
183
|
-
|
238
|
+
if (!result || result.length === 0) {
|
239
|
+
return null;
|
240
|
+
}
|
184
241
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
242
|
+
const row = result[0];
|
243
|
+
return {
|
244
|
+
conversationId: row[0] as string,
|
245
|
+
projectPath: row[1] as string,
|
246
|
+
gitBranch: row[2] as string,
|
247
|
+
currentPhase: row[3] as string,
|
248
|
+
planFilePath: row[4] as string,
|
249
|
+
workflowName: row[5] as string,
|
250
|
+
gitCommitConfig: row[6] ? JSON.parse(row[6] as string) : null,
|
251
|
+
requireReviewsBeforePhaseTransition: Boolean(row[7]),
|
252
|
+
createdAt: row[8] as string,
|
253
|
+
updatedAt: row[9] as string,
|
254
|
+
};
|
192
255
|
}
|
193
256
|
|
194
257
|
/**
|
195
|
-
*
|
258
|
+
* Get all conversation states
|
196
259
|
*/
|
197
|
-
async
|
260
|
+
async getAllConversationStates(): Promise<ConversationState[]> {
|
198
261
|
if (!this.db) {
|
199
262
|
throw new Error('Database not initialized');
|
200
263
|
}
|
201
264
|
|
202
|
-
const
|
203
|
-
'SELECT * FROM conversation_state ORDER BY updatedAt DESC'
|
204
|
-
|
205
|
-
|
206
|
-
conversationId: string;
|
207
|
-
projectPath: string;
|
208
|
-
gitBranch: string;
|
209
|
-
currentPhase: string;
|
210
|
-
planFilePath: string;
|
211
|
-
workflowName: string;
|
212
|
-
gitCommitConfig: string;
|
213
|
-
requireReviewsBeforePhaseTransition: number;
|
214
|
-
createdAt: string;
|
215
|
-
updatedAt: string;
|
216
|
-
}[];
|
217
|
-
|
218
|
-
const states = rows.map(row => ({
|
219
|
-
conversationId: row.conversationId,
|
220
|
-
projectPath: row.projectPath,
|
221
|
-
gitBranch: row.gitBranch,
|
222
|
-
currentPhase: row.currentPhase,
|
223
|
-
planFilePath: row.planFilePath,
|
224
|
-
workflowName: row.workflowName,
|
225
|
-
gitCommitConfig: row.gitCommitConfig
|
226
|
-
? JSON.parse(row.gitCommitConfig)
|
227
|
-
: undefined,
|
228
|
-
requireReviewsBeforePhaseTransition:
|
229
|
-
row.requireReviewsBeforePhaseTransition === 1,
|
230
|
-
createdAt: row.createdAt,
|
231
|
-
updatedAt: row.updatedAt,
|
232
|
-
}));
|
265
|
+
const result = this.db.exec({
|
266
|
+
sql: 'SELECT * FROM conversation_state ORDER BY updatedAt DESC',
|
267
|
+
returnValue: 'resultRows',
|
268
|
+
});
|
233
269
|
|
234
|
-
|
235
|
-
|
270
|
+
if (!result) {
|
271
|
+
return [];
|
272
|
+
}
|
273
|
+
|
274
|
+
return result.map(row => ({
|
275
|
+
conversationId: row[0] as string,
|
276
|
+
projectPath: row[1] as string,
|
277
|
+
gitBranch: row[2] as string,
|
278
|
+
currentPhase: row[3] as string,
|
279
|
+
planFilePath: row[4] as string,
|
280
|
+
workflowName: row[5] as string,
|
281
|
+
gitCommitConfig: row[6] ? JSON.parse(row[6] as string) : null,
|
282
|
+
requireReviewsBeforePhaseTransition: Boolean(row[7]),
|
283
|
+
createdAt: row[8] as string,
|
284
|
+
updatedAt: row[9] as string,
|
285
|
+
}));
|
236
286
|
}
|
237
287
|
|
238
288
|
/**
|
@@ -243,114 +293,125 @@ export class Database {
|
|
243
293
|
throw new Error('Database not initialized');
|
244
294
|
}
|
245
295
|
|
246
|
-
|
247
|
-
'DELETE FROM conversation_state WHERE conversationId = ?'
|
248
|
-
|
249
|
-
|
296
|
+
this.db.exec({
|
297
|
+
sql: 'DELETE FROM conversation_state WHERE conversationId = ?',
|
298
|
+
bind: [conversationId],
|
299
|
+
});
|
250
300
|
|
251
|
-
|
252
|
-
|
253
|
-
|
301
|
+
// Persist to file
|
302
|
+
await this.saveToFile();
|
303
|
+
|
304
|
+
logger.debug('Conversation state deleted', { conversationId });
|
305
|
+
return true;
|
254
306
|
}
|
255
307
|
|
256
308
|
/**
|
257
|
-
* Log
|
309
|
+
* Log interaction
|
258
310
|
*/
|
259
311
|
async logInteraction(log: InteractionLog): Promise<void> {
|
260
312
|
if (!this.db) {
|
261
313
|
throw new Error('Database not initialized');
|
262
314
|
}
|
263
315
|
|
264
|
-
|
265
|
-
INSERT INTO interaction_log
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
);
|
316
|
+
this.db.exec({
|
317
|
+
sql: `INSERT INTO interaction_log
|
318
|
+
(conversationId, toolName, inputParams, responseData, currentPhase, timestamp)
|
319
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
320
|
+
bind: [
|
321
|
+
log.conversationId,
|
322
|
+
log.toolName,
|
323
|
+
JSON.stringify(log.inputParams),
|
324
|
+
JSON.stringify(log.responseData),
|
325
|
+
log.currentPhase,
|
326
|
+
log.timestamp,
|
327
|
+
],
|
328
|
+
});
|
329
|
+
|
330
|
+
// Persist to file
|
331
|
+
await this.saveToFile();
|
280
332
|
|
281
333
|
logger.debug('Interaction logged', {
|
282
334
|
conversationId: log.conversationId,
|
283
335
|
toolName: log.toolName,
|
284
|
-
timestamp: log.timestamp,
|
285
336
|
});
|
286
337
|
}
|
287
338
|
|
288
339
|
/**
|
289
|
-
* Get
|
340
|
+
* Get interaction logs for a conversation
|
341
|
+
*/
|
342
|
+
async getInteractionLogs(conversationId: string): Promise<InteractionLog[]> {
|
343
|
+
if (!this.db) {
|
344
|
+
throw new Error('Database not initialized');
|
345
|
+
}
|
346
|
+
|
347
|
+
const result = this.db.exec({
|
348
|
+
sql: 'SELECT * FROM interaction_log WHERE conversationId = ? ORDER BY timestamp ASC',
|
349
|
+
bind: [conversationId],
|
350
|
+
returnValue: 'resultRows',
|
351
|
+
});
|
352
|
+
|
353
|
+
if (!result) {
|
354
|
+
return [];
|
355
|
+
}
|
356
|
+
|
357
|
+
return result.map(row => ({
|
358
|
+
id: row[0] as number,
|
359
|
+
conversationId: row[1] as string,
|
360
|
+
toolName: row[2] as string,
|
361
|
+
inputParams: JSON.parse(row[3] as string),
|
362
|
+
responseData: JSON.parse(row[4] as string),
|
363
|
+
currentPhase: row[5] as string,
|
364
|
+
timestamp: row[6] as string,
|
365
|
+
}));
|
366
|
+
}
|
367
|
+
|
368
|
+
/**
|
369
|
+
* Get interaction logs for a conversation (alias for compatibility)
|
290
370
|
*/
|
291
371
|
async getInteractionsByConversationId(
|
292
372
|
conversationId: string
|
293
373
|
): Promise<InteractionLog[]> {
|
374
|
+
return this.getInteractionLogs(conversationId);
|
375
|
+
}
|
376
|
+
|
377
|
+
/**
|
378
|
+
* Soft delete interaction logs (for compatibility - actually deletes them)
|
379
|
+
*/
|
380
|
+
async softDeleteInteractionLogs(conversationId: string): Promise<void> {
|
294
381
|
if (!this.db) {
|
295
382
|
throw new Error('Database not initialized');
|
296
383
|
}
|
297
384
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
ORDER BY timestamp ASC
|
302
|
-
`);
|
303
|
-
const rows = stmt.all(conversationId) as {
|
304
|
-
id: number;
|
305
|
-
conversationId: string;
|
306
|
-
toolName: string;
|
307
|
-
inputParams: string;
|
308
|
-
responseData: string;
|
309
|
-
currentPhase: string;
|
310
|
-
timestamp: string;
|
311
|
-
isReset: number;
|
312
|
-
resetAt: string;
|
313
|
-
}[];
|
314
|
-
|
315
|
-
const logs = rows.map(row => ({
|
316
|
-
id: row.id,
|
317
|
-
conversationId: row.conversationId,
|
318
|
-
toolName: row.toolName,
|
319
|
-
inputParams: row.inputParams,
|
320
|
-
responseData: row.responseData,
|
321
|
-
currentPhase: row.currentPhase,
|
322
|
-
timestamp: row.timestamp,
|
323
|
-
isReset: row.isReset === 1,
|
324
|
-
resetAt: row.resetAt,
|
325
|
-
}));
|
326
|
-
|
327
|
-
logger.debug('Retrieved interaction logs', {
|
328
|
-
conversationId,
|
329
|
-
count: logs.length,
|
385
|
+
this.db.exec({
|
386
|
+
sql: 'DELETE FROM interaction_log WHERE conversationId = ?',
|
387
|
+
bind: [conversationId],
|
330
388
|
});
|
331
|
-
|
389
|
+
|
390
|
+
// Persist to file
|
391
|
+
await this.saveToFile();
|
392
|
+
|
393
|
+
logger.debug('Interaction logs deleted', { conversationId });
|
332
394
|
}
|
333
395
|
|
334
396
|
/**
|
335
|
-
*
|
397
|
+
* Reset conversation state (for testing)
|
336
398
|
*/
|
337
|
-
async
|
399
|
+
async resetConversationState(conversationId: string): Promise<void> {
|
338
400
|
if (!this.db) {
|
339
401
|
throw new Error('Database not initialized');
|
340
402
|
}
|
341
403
|
|
342
404
|
const resetAt = new Date().toISOString();
|
343
|
-
|
344
|
-
|
345
|
-
SET
|
346
|
-
|
347
|
-
`);
|
348
|
-
|
349
|
-
const result = stmt.run(resetAt, conversationId);
|
350
|
-
logger.debug('Soft deleted interaction logs', {
|
351
|
-
conversationId,
|
352
|
-
affectedRows: result.changes,
|
405
|
+
|
406
|
+
this.db.exec({
|
407
|
+
sql: 'UPDATE conversation_state SET updatedAt = ? WHERE conversationId = ?',
|
408
|
+
bind: [resetAt, conversationId],
|
353
409
|
});
|
410
|
+
|
411
|
+
// Persist to file
|
412
|
+
await this.saveToFile();
|
413
|
+
|
414
|
+
logger.debug('Conversation state reset', { conversationId, resetAt });
|
354
415
|
}
|
355
416
|
|
356
417
|
/**
|
@@ -363,11 +424,4 @@ export class Database {
|
|
363
424
|
logger.debug('Database connection closed');
|
364
425
|
}
|
365
426
|
}
|
366
|
-
|
367
|
-
/**
|
368
|
-
* Check if database is initialized
|
369
|
-
*/
|
370
|
-
isInitialized(): boolean {
|
371
|
-
return this.db !== null;
|
372
|
-
}
|
373
427
|
}
|