@jungjaehoon/mama-core 1.2.0 → 1.3.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 +1 -1
- package/dist/config-loader.d.ts +6 -0
- package/dist/config-loader.js +9 -0
- package/dist/db-adapter/index.d.ts +2 -1
- package/dist/db-adapter/index.js +11 -2
- package/dist/db-adapter/node-sqlite-adapter.d.ts +56 -0
- package/dist/db-adapter/node-sqlite-adapter.js +334 -0
- package/dist/db-adapter/node-sqlite-statement.d.ts +23 -0
- package/dist/db-adapter/node-sqlite-statement.js +27 -0
- package/dist/db-adapter/sqlite-adapter.d.ts +5 -70
- package/dist/db-adapter/sqlite-adapter.js +6 -311
- package/dist/db-adapter/statement.d.ts +1 -1
- package/dist/db-adapter/statement.js +3 -1
- package/dist/embedding-server/mobile/auth.js +27 -2
- package/dist/embedding-server/mobile/session-manager.d.ts +3 -0
- package/dist/embedding-server/mobile/session-manager.js +41 -11
- package/dist/embeddings.d.ts +2 -2
- package/dist/embeddings.js +7 -3
- package/dist/tier-validator.d.ts +2 -2
- package/dist/tier-validator.js +9 -10
- package/package.json +2 -4
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ const mamaApi = require('@jungjaehoon/mama-core/mama-api');
|
|
|
107
107
|
## Dependencies
|
|
108
108
|
|
|
109
109
|
- **@huggingface/transformers** - Local embedding generation
|
|
110
|
-
- **
|
|
110
|
+
- **node:sqlite** - Built-in SQLite runtime (Node.js 22+)
|
|
111
111
|
- **Pure-TS cosine similarity** - Vector search (no native extensions)
|
|
112
112
|
|
|
113
113
|
## Development
|
package/dist/config-loader.d.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
export interface MAMAConfig {
|
|
16
16
|
modelName: string;
|
|
17
17
|
embeddingDim: number;
|
|
18
|
+
quantized: boolean;
|
|
18
19
|
cacheDir: string;
|
|
19
20
|
[key: string]: unknown;
|
|
20
21
|
}
|
|
@@ -36,6 +37,10 @@ export declare function getModelName(): string;
|
|
|
36
37
|
* Get current embedding dimension
|
|
37
38
|
*/
|
|
38
39
|
export declare function getEmbeddingDim(): number;
|
|
40
|
+
/**
|
|
41
|
+
* Get whether to use quantized model
|
|
42
|
+
*/
|
|
43
|
+
export declare function getQuantized(): boolean;
|
|
39
44
|
/**
|
|
40
45
|
* Get current cache directory
|
|
41
46
|
*/
|
|
@@ -43,6 +48,7 @@ export declare function getCacheDir(): string;
|
|
|
43
48
|
export interface ConfigUpdates {
|
|
44
49
|
modelName?: string;
|
|
45
50
|
embeddingDim?: number;
|
|
51
|
+
quantized?: boolean;
|
|
46
52
|
cacheDir?: string;
|
|
47
53
|
}
|
|
48
54
|
/**
|
package/dist/config-loader.js
CHANGED
|
@@ -21,6 +21,7 @@ exports.DEFAULT_CONFIG = void 0;
|
|
|
21
21
|
exports.loadConfig = loadConfig;
|
|
22
22
|
exports.getModelName = getModelName;
|
|
23
23
|
exports.getEmbeddingDim = getEmbeddingDim;
|
|
24
|
+
exports.getQuantized = getQuantized;
|
|
24
25
|
exports.getCacheDir = getCacheDir;
|
|
25
26
|
exports.updateConfig = updateConfig;
|
|
26
27
|
exports.getConfigPath = getConfigPath;
|
|
@@ -32,6 +33,7 @@ const debug_logger_js_1 = require("./debug-logger.js");
|
|
|
32
33
|
exports.DEFAULT_CONFIG = {
|
|
33
34
|
modelName: 'Xenova/multilingual-e5-small',
|
|
34
35
|
embeddingDim: 384,
|
|
36
|
+
quantized: true,
|
|
35
37
|
cacheDir: path_1.default.join(os_1.default.homedir(), '.cache', 'huggingface', 'transformers'),
|
|
36
38
|
};
|
|
37
39
|
// Config file path
|
|
@@ -129,6 +131,13 @@ function getEmbeddingDim() {
|
|
|
129
131
|
const config = loadConfig();
|
|
130
132
|
return config.embeddingDim;
|
|
131
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Get whether to use quantized model
|
|
136
|
+
*/
|
|
137
|
+
function getQuantized() {
|
|
138
|
+
const config = loadConfig();
|
|
139
|
+
return config.quantized;
|
|
140
|
+
}
|
|
132
141
|
/**
|
|
133
142
|
* Get current cache directory
|
|
134
143
|
*/
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* @module db-adapter
|
|
8
8
|
*/
|
|
9
9
|
import { SQLiteAdapter } from './sqlite-adapter.js';
|
|
10
|
+
import { NodeSQLiteAdapter } from './node-sqlite-adapter.js';
|
|
10
11
|
import { DatabaseAdapter, type VectorSearchResult, type RunResult } from './base-adapter.js';
|
|
11
12
|
import type { Statement } from './statement.js';
|
|
12
|
-
export { DatabaseAdapter, SQLiteAdapter };
|
|
13
|
+
export { DatabaseAdapter, SQLiteAdapter, NodeSQLiteAdapter };
|
|
13
14
|
export type { Statement, VectorSearchResult, RunResult };
|
|
14
15
|
export interface AdapterConfig {
|
|
15
16
|
dbPath?: string;
|
package/dist/db-adapter/index.js
CHANGED
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
* @module db-adapter
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.SQLiteAdapter = exports.DatabaseAdapter = void 0;
|
|
11
|
+
exports.NodeSQLiteAdapter = exports.SQLiteAdapter = exports.DatabaseAdapter = void 0;
|
|
12
12
|
exports.createAdapter = createAdapter;
|
|
13
13
|
const debug_logger_js_1 = require("../debug-logger.js");
|
|
14
14
|
const sqlite_adapter_js_1 = require("./sqlite-adapter.js");
|
|
15
15
|
Object.defineProperty(exports, "SQLiteAdapter", { enumerable: true, get: function () { return sqlite_adapter_js_1.SQLiteAdapter; } });
|
|
16
|
+
const node_sqlite_adapter_js_1 = require("./node-sqlite-adapter.js");
|
|
17
|
+
Object.defineProperty(exports, "NodeSQLiteAdapter", { enumerable: true, get: function () { return node_sqlite_adapter_js_1.NodeSQLiteAdapter; } });
|
|
16
18
|
const base_adapter_js_1 = require("./base-adapter.js");
|
|
17
19
|
Object.defineProperty(exports, "DatabaseAdapter", { enumerable: true, get: function () { return base_adapter_js_1.DatabaseAdapter; } });
|
|
18
20
|
/**
|
|
@@ -22,7 +24,14 @@ Object.defineProperty(exports, "DatabaseAdapter", { enumerable: true, get: funct
|
|
|
22
24
|
* @returns Configured SQLite adapter instance
|
|
23
25
|
*/
|
|
24
26
|
function createAdapter(config = {}) {
|
|
25
|
-
|
|
27
|
+
const configuredDriver = process.env.MAMA_SQLITE_DRIVER;
|
|
28
|
+
if (configuredDriver &&
|
|
29
|
+
configuredDriver !== 'node-sqlite' &&
|
|
30
|
+
configuredDriver !== 'node:sqlite' &&
|
|
31
|
+
configuredDriver !== 'auto') {
|
|
32
|
+
throw new Error(`Unsupported SQLite driver "${configuredDriver}". MAMA now requires node:sqlite.`);
|
|
33
|
+
}
|
|
34
|
+
(0, debug_logger_js_1.info)('[db-adapter] Using node:sqlite adapter');
|
|
26
35
|
const dbPath = config.dbPath || process.env.MAMA_DB_PATH;
|
|
27
36
|
return new sqlite_adapter_js_1.SQLiteAdapter({ dbPath });
|
|
28
37
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Database Adapter using node:sqlite
|
|
3
|
+
*
|
|
4
|
+
* Keeps the existing DatabaseAdapter surface while avoiding external native addon installation.
|
|
5
|
+
*/
|
|
6
|
+
import { DatabaseAdapter, type VectorSearchResult, type RunResult } from './base-adapter.js';
|
|
7
|
+
import { type Statement } from './statement.js';
|
|
8
|
+
interface SQLiteAdapterConfig {
|
|
9
|
+
dbPath?: string;
|
|
10
|
+
}
|
|
11
|
+
interface NodeSQLiteDatabaseLike {
|
|
12
|
+
exec(sql: string): void;
|
|
13
|
+
close(): void;
|
|
14
|
+
prepare(sql: string): NodeSQLiteStatementLike;
|
|
15
|
+
}
|
|
16
|
+
interface NodeSQLiteStatementLike {
|
|
17
|
+
all: (...params: unknown[]) => unknown[];
|
|
18
|
+
get: (...params: unknown[]) => unknown;
|
|
19
|
+
run: (...params: unknown[]) => {
|
|
20
|
+
changes: number;
|
|
21
|
+
lastInsertRowid: number | bigint;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
declare class NodeSQLiteConnection {
|
|
25
|
+
private db;
|
|
26
|
+
private connected;
|
|
27
|
+
constructor(db: NodeSQLiteDatabaseLike);
|
|
28
|
+
prepare(sql: string): NodeSQLiteStatementLike;
|
|
29
|
+
exec(sql: string): void;
|
|
30
|
+
pragma(sql: string, options?: {
|
|
31
|
+
simple?: boolean;
|
|
32
|
+
}): unknown;
|
|
33
|
+
close(): void;
|
|
34
|
+
get open(): boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare class NodeSQLiteAdapter extends DatabaseAdapter {
|
|
37
|
+
private config;
|
|
38
|
+
private db;
|
|
39
|
+
private _vectorSearchEnabled;
|
|
40
|
+
constructor(config?: SQLiteAdapterConfig);
|
|
41
|
+
get vectorSearchEnabled(): boolean;
|
|
42
|
+
getDbPath(): string;
|
|
43
|
+
connect(): NodeSQLiteConnection;
|
|
44
|
+
disconnect(): void;
|
|
45
|
+
isConnected(): boolean;
|
|
46
|
+
prepare(sql: string): Statement;
|
|
47
|
+
exec(sql: string): void;
|
|
48
|
+
transaction<T>(fn: () => T): T;
|
|
49
|
+
vectorSearch(embedding: Float32Array | number[], limit?: number): VectorSearchResult[] | null;
|
|
50
|
+
insertEmbedding(rowid: number, embedding: Float32Array | number[]): RunResult | null;
|
|
51
|
+
getLastInsertRowid(): number;
|
|
52
|
+
runMigrations(migrationsDir: string): void;
|
|
53
|
+
private migrateFromVssMemories;
|
|
54
|
+
}
|
|
55
|
+
export default NodeSQLiteAdapter;
|
|
56
|
+
//# sourceMappingURL=node-sqlite-adapter.d.ts.map
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SQLite Database Adapter using node:sqlite
|
|
4
|
+
*
|
|
5
|
+
* Keeps the existing DatabaseAdapter surface while avoiding external native addon installation.
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.NodeSQLiteAdapter = void 0;
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const os_1 = __importDefault(require("os"));
|
|
14
|
+
const fs_1 = __importDefault(require("fs"));
|
|
15
|
+
const base_adapter_js_1 = require("./base-adapter.js");
|
|
16
|
+
const node_sqlite_statement_js_1 = require("./node-sqlite-statement.js");
|
|
17
|
+
const debug_logger_js_1 = require("../debug-logger.js");
|
|
18
|
+
const embeddings_js_1 = require("../embeddings.js");
|
|
19
|
+
const LEGACY_DB_PATH = path_1.default.join(os_1.default.homedir(), '.spinelift', 'memories.db');
|
|
20
|
+
const DEFAULT_DB_PATH = path_1.default.join(os_1.default.homedir(), '.claude', 'mama-memory.db');
|
|
21
|
+
let DatabaseSync = null;
|
|
22
|
+
try {
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
24
|
+
({ DatabaseSync } = require('node:sqlite'));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
DatabaseSync = null;
|
|
28
|
+
}
|
|
29
|
+
class NodeSQLiteConnection {
|
|
30
|
+
db;
|
|
31
|
+
connected = true;
|
|
32
|
+
constructor(db) {
|
|
33
|
+
this.db = db;
|
|
34
|
+
}
|
|
35
|
+
prepare(sql) {
|
|
36
|
+
return this.db.prepare(sql);
|
|
37
|
+
}
|
|
38
|
+
exec(sql) {
|
|
39
|
+
this.db.exec(sql);
|
|
40
|
+
}
|
|
41
|
+
pragma(sql, options) {
|
|
42
|
+
const query = sql.trim().replace(/^PRAGMA\s+/i, '');
|
|
43
|
+
const stmt = this.db.prepare(`PRAGMA ${query}`);
|
|
44
|
+
if (options?.simple) {
|
|
45
|
+
const row = stmt.get();
|
|
46
|
+
if (!row) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return Object.values(row)[0];
|
|
50
|
+
}
|
|
51
|
+
return stmt.all();
|
|
52
|
+
}
|
|
53
|
+
close() {
|
|
54
|
+
if (!this.connected) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.db.close();
|
|
58
|
+
this.connected = false;
|
|
59
|
+
}
|
|
60
|
+
get open() {
|
|
61
|
+
return this.connected;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
|
|
65
|
+
config;
|
|
66
|
+
db = null;
|
|
67
|
+
_vectorSearchEnabled = true;
|
|
68
|
+
constructor(config = {}) {
|
|
69
|
+
super();
|
|
70
|
+
this.config = config;
|
|
71
|
+
}
|
|
72
|
+
get vectorSearchEnabled() {
|
|
73
|
+
return this._vectorSearchEnabled;
|
|
74
|
+
}
|
|
75
|
+
getDbPath() {
|
|
76
|
+
const envPath = process.env.MAMA_DB_PATH || process.env.MAMA_DATABASE_PATH;
|
|
77
|
+
const configPath = this.config.dbPath;
|
|
78
|
+
let expandedEnvPath = envPath;
|
|
79
|
+
if (envPath) {
|
|
80
|
+
expandedEnvPath = envPath.replace(/\$\{HOME\}/g, os_1.default.homedir()).replace(/^~/, os_1.default.homedir());
|
|
81
|
+
}
|
|
82
|
+
const targetPath = configPath || expandedEnvPath || DEFAULT_DB_PATH;
|
|
83
|
+
if (!configPath && !envPath && fs_1.default.existsSync(LEGACY_DB_PATH)) {
|
|
84
|
+
(0, debug_logger_js_1.info)('[node-sqlite-adapter] Found legacy database at ~/.spinelift/memories.db, using it for backward compatibility');
|
|
85
|
+
return LEGACY_DB_PATH;
|
|
86
|
+
}
|
|
87
|
+
return targetPath;
|
|
88
|
+
}
|
|
89
|
+
connect() {
|
|
90
|
+
if (this.db) {
|
|
91
|
+
return this.db;
|
|
92
|
+
}
|
|
93
|
+
if (!DatabaseSync) {
|
|
94
|
+
throw new Error('node:sqlite is not available in this Node.js runtime. Use Node 22.13+.');
|
|
95
|
+
}
|
|
96
|
+
const dbPath = this.getDbPath();
|
|
97
|
+
const dbDir = path_1.default.dirname(dbPath);
|
|
98
|
+
if (!fs_1.default.existsSync(dbDir)) {
|
|
99
|
+
fs_1.default.mkdirSync(dbDir, { recursive: true });
|
|
100
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Created database directory: ${dbDir}`);
|
|
101
|
+
}
|
|
102
|
+
const database = new DatabaseSync(dbPath);
|
|
103
|
+
this.db = new NodeSQLiteConnection(database);
|
|
104
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Opened database at: ${dbPath}`);
|
|
105
|
+
this.db.pragma('journal_mode = WAL');
|
|
106
|
+
this.db.pragma('busy_timeout = 5000');
|
|
107
|
+
this.db.pragma('synchronous = NORMAL');
|
|
108
|
+
this.db.pragma('cache_size = -64000');
|
|
109
|
+
this.db.pragma('temp_store = MEMORY');
|
|
110
|
+
this.db.pragma('foreign_keys = ON');
|
|
111
|
+
(0, debug_logger_js_1.info)('[node-sqlite-adapter] Vector search: pure TS cosine similarity (no native extensions)');
|
|
112
|
+
return this.db;
|
|
113
|
+
}
|
|
114
|
+
disconnect() {
|
|
115
|
+
if (this.db) {
|
|
116
|
+
this.db.close();
|
|
117
|
+
this.db = null;
|
|
118
|
+
(0, debug_logger_js_1.info)('[node-sqlite-adapter] Disconnected from database');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
isConnected() {
|
|
122
|
+
return this.db !== null && this.db.open;
|
|
123
|
+
}
|
|
124
|
+
prepare(sql) {
|
|
125
|
+
if (!this.isConnected() || !this.db) {
|
|
126
|
+
throw new Error('Database not connected');
|
|
127
|
+
}
|
|
128
|
+
return new node_sqlite_statement_js_1.NodeSQLiteStatement(this.db.prepare(sql));
|
|
129
|
+
}
|
|
130
|
+
exec(sql) {
|
|
131
|
+
if (!this.isConnected() || !this.db) {
|
|
132
|
+
throw new Error('Database not connected');
|
|
133
|
+
}
|
|
134
|
+
this.db.exec(sql);
|
|
135
|
+
}
|
|
136
|
+
transaction(fn) {
|
|
137
|
+
if (!this.isConnected()) {
|
|
138
|
+
throw new Error('Database not connected');
|
|
139
|
+
}
|
|
140
|
+
this.exec('BEGIN TRANSACTION');
|
|
141
|
+
try {
|
|
142
|
+
const result = fn();
|
|
143
|
+
if (((typeof result === 'object' && result !== null) || typeof result === 'function') &&
|
|
144
|
+
typeof result.then === 'function') {
|
|
145
|
+
throw new Error('DatabaseAdapter.transaction() callbacks must be synchronous');
|
|
146
|
+
}
|
|
147
|
+
this.exec('COMMIT');
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
try {
|
|
152
|
+
this.exec('ROLLBACK');
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// Preserve the original transaction failure when rollback also fails.
|
|
156
|
+
}
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
vectorSearch(embedding, limit = 5) {
|
|
161
|
+
if (!this.isConnected()) {
|
|
162
|
+
throw new Error('Database not connected');
|
|
163
|
+
}
|
|
164
|
+
const queryVector = embedding instanceof Float32Array ? embedding : Float32Array.from(embedding);
|
|
165
|
+
const tableCheck = this.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings'`).all();
|
|
166
|
+
if (tableCheck.length === 0) {
|
|
167
|
+
throw new Error('Embeddings table missing');
|
|
168
|
+
}
|
|
169
|
+
const effectiveLimit = Math.max(limit, 1);
|
|
170
|
+
const batchSize = 500;
|
|
171
|
+
const bestMatches = [];
|
|
172
|
+
let offset = 0;
|
|
173
|
+
let hasMoreRows = true;
|
|
174
|
+
while (hasMoreRows) {
|
|
175
|
+
const rows = this.prepare('SELECT rowid, embedding FROM embeddings LIMIT ? OFFSET ?').all(batchSize, offset);
|
|
176
|
+
if (rows.length === 0) {
|
|
177
|
+
hasMoreRows = false;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
for (const row of rows) {
|
|
181
|
+
const candidate = bytesToVector(row.embedding);
|
|
182
|
+
if (!candidate)
|
|
183
|
+
continue;
|
|
184
|
+
if (candidate.length !== queryVector.length) {
|
|
185
|
+
(0, debug_logger_js_1.warn)(`Skipping rowid ${row.rowid}: dimension mismatch (${candidate.length} vs ${queryVector.length})`);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const similarity = (0, embeddings_js_1.cosineSimilarity)(candidate, queryVector);
|
|
189
|
+
const scoredRow = {
|
|
190
|
+
rowid: row.rowid,
|
|
191
|
+
similarity,
|
|
192
|
+
distance: 1 - similarity,
|
|
193
|
+
};
|
|
194
|
+
if (bestMatches.length < effectiveLimit) {
|
|
195
|
+
bestMatches.push(scoredRow);
|
|
196
|
+
bestMatches.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const weakestMatch = bestMatches[bestMatches.length - 1];
|
|
200
|
+
if ((scoredRow.similarity ?? 0) > (weakestMatch.similarity ?? 0)) {
|
|
201
|
+
bestMatches.pop();
|
|
202
|
+
bestMatches.push(scoredRow);
|
|
203
|
+
bestMatches.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
offset += rows.length;
|
|
207
|
+
}
|
|
208
|
+
return bestMatches;
|
|
209
|
+
}
|
|
210
|
+
insertEmbedding(rowid, embedding) {
|
|
211
|
+
if (!this.isConnected()) {
|
|
212
|
+
throw new Error('Database not connected');
|
|
213
|
+
}
|
|
214
|
+
const vec = embedding instanceof Float32Array ? embedding : Float32Array.from(embedding);
|
|
215
|
+
const buffer = Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
|
|
216
|
+
const stmt = this.prepare(`
|
|
217
|
+
INSERT OR REPLACE INTO embeddings(rowid, embedding)
|
|
218
|
+
VALUES (?, ?)
|
|
219
|
+
`);
|
|
220
|
+
return stmt.run(rowid, buffer);
|
|
221
|
+
}
|
|
222
|
+
getLastInsertRowid() {
|
|
223
|
+
if (!this.isConnected()) {
|
|
224
|
+
throw new Error('Database not connected');
|
|
225
|
+
}
|
|
226
|
+
const result = this.prepare('SELECT last_insert_rowid() as rowid').get();
|
|
227
|
+
return result.rowid;
|
|
228
|
+
}
|
|
229
|
+
runMigrations(migrationsDir) {
|
|
230
|
+
if (!this.isConnected()) {
|
|
231
|
+
throw new Error('Database not connected');
|
|
232
|
+
}
|
|
233
|
+
const tables = this.prepare(`
|
|
234
|
+
SELECT name FROM sqlite_master
|
|
235
|
+
WHERE type='table' AND name='schema_version'
|
|
236
|
+
`).all();
|
|
237
|
+
let currentVersion = 0;
|
|
238
|
+
if (tables.length > 0) {
|
|
239
|
+
const version = this.prepare('SELECT MAX(version) as version FROM schema_version').get();
|
|
240
|
+
currentVersion = version?.version || 0;
|
|
241
|
+
}
|
|
242
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Current schema version: ${currentVersion}`);
|
|
243
|
+
const migrationFiles = fs_1.default
|
|
244
|
+
.readdirSync(migrationsDir)
|
|
245
|
+
.filter((file) => file.endsWith('.sql'))
|
|
246
|
+
.sort();
|
|
247
|
+
for (const file of migrationFiles) {
|
|
248
|
+
const versionMatch = file.match(/^(\d+)-/);
|
|
249
|
+
if (!versionMatch) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const version = parseInt(versionMatch[1], 10);
|
|
253
|
+
if (version <= currentVersion) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
const migrationPath = path_1.default.join(migrationsDir, file);
|
|
257
|
+
const migrationSQL = fs_1.default.readFileSync(migrationPath, 'utf8');
|
|
258
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Applying migration: ${file}`);
|
|
259
|
+
try {
|
|
260
|
+
this.exec('BEGIN TRANSACTION');
|
|
261
|
+
this.exec(migrationSQL);
|
|
262
|
+
this.exec('COMMIT');
|
|
263
|
+
this.prepare('INSERT OR IGNORE INTO schema_version (version) VALUES (?)').run(version);
|
|
264
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migration ${file} applied successfully`);
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
this.exec('ROLLBACK');
|
|
268
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
269
|
+
if (message.includes('duplicate column')) {
|
|
270
|
+
(0, debug_logger_js_1.warn)(`[node-sqlite-adapter] Migration ${file} skipped (duplicate column - already applied)`);
|
|
271
|
+
this.prepare('INSERT OR IGNORE INTO schema_version (version) VALUES (?)').run(version);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (message.includes('no such table')) {
|
|
275
|
+
const hasAlterTable = migrationSQL.toUpperCase().includes('ALTER TABLE');
|
|
276
|
+
if (!hasAlterTable) {
|
|
277
|
+
(0, debug_logger_js_1.error)(`[node-sqlite-adapter] Migration ${file} failed (missing required table):`, err);
|
|
278
|
+
throw new Error(`Migration ${file} failed: ${message}`);
|
|
279
|
+
}
|
|
280
|
+
(0, debug_logger_js_1.warn)(`[node-sqlite-adapter] Migration ${file} skipped: ALTER TABLE on non-existent table (${message})`);
|
|
281
|
+
this.prepare('INSERT OR IGNORE INTO schema_version (version) VALUES (?)').run(version);
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
(0, debug_logger_js_1.error)(`[node-sqlite-adapter] Migration ${file} failed:`, err);
|
|
285
|
+
throw new Error(`Migration ${file} failed: ${message}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const embeddingsTables = this.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings'`).all();
|
|
289
|
+
if (embeddingsTables.length === 0) {
|
|
290
|
+
throw new Error('Embeddings table is missing after migrations. Add the required embeddings migration.');
|
|
291
|
+
}
|
|
292
|
+
this.migrateFromVssMemories();
|
|
293
|
+
}
|
|
294
|
+
migrateFromVssMemories() {
|
|
295
|
+
try {
|
|
296
|
+
const vssTables = this.prepare(`SELECT name FROM sqlite_master WHERE name='vss_memories'`).all();
|
|
297
|
+
if (vssTables.length === 0) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const rows = this.prepare('SELECT rowid, embedding FROM vss_memories').all();
|
|
301
|
+
if (rows.length > 0) {
|
|
302
|
+
const insertStmt = this.prepare('INSERT OR IGNORE INTO embeddings (rowid, embedding) VALUES (?, ?)');
|
|
303
|
+
let migrated = 0;
|
|
304
|
+
for (const row of rows) {
|
|
305
|
+
const res = insertStmt.run(row.rowid, Buffer.from(row.embedding));
|
|
306
|
+
if (res.changes > 0) {
|
|
307
|
+
migrated++;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migrated ${migrated} embeddings from vss_memories to embeddings`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
315
|
+
(0, debug_logger_js_1.warn)(`[node-sqlite-adapter] Could not migrate from vss_memories (expected if sqlite-vec not installed): ${message}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
exports.NodeSQLiteAdapter = NodeSQLiteAdapter;
|
|
320
|
+
exports.default = NodeSQLiteAdapter;
|
|
321
|
+
function bytesToVector(buffer) {
|
|
322
|
+
if (!buffer || buffer.byteLength % 4 !== 0) {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
try {
|
|
326
|
+
const typedBuffer = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
|
|
327
|
+
const arrayBuffer = typedBuffer.buffer.slice(typedBuffer.byteOffset, typedBuffer.byteOffset + typedBuffer.byteLength);
|
|
328
|
+
return new Float32Array(arrayBuffer);
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
//# sourceMappingURL=node-sqlite-adapter.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node SQLite statement wrapper
|
|
3
|
+
*
|
|
4
|
+
* Wraps node:sqlite StatementSync to provide the existing Statement API.
|
|
5
|
+
*/
|
|
6
|
+
import { Statement, type RunResult } from './statement.js';
|
|
7
|
+
interface NodeSQLiteStatementLike {
|
|
8
|
+
all: (...params: unknown[]) => unknown[];
|
|
9
|
+
get: (...params: unknown[]) => unknown;
|
|
10
|
+
run: (...params: unknown[]) => {
|
|
11
|
+
changes: number;
|
|
12
|
+
lastInsertRowid: number | bigint;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare class NodeSQLiteStatement extends Statement {
|
|
16
|
+
private stmt;
|
|
17
|
+
constructor(stmt: NodeSQLiteStatementLike);
|
|
18
|
+
all(...params: unknown[]): object[];
|
|
19
|
+
get(...params: unknown[]): object | undefined;
|
|
20
|
+
run(...params: unknown[]): RunResult;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=node-sqlite-statement.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Node SQLite statement wrapper
|
|
4
|
+
*
|
|
5
|
+
* Wraps node:sqlite StatementSync to provide the existing Statement API.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.NodeSQLiteStatement = void 0;
|
|
9
|
+
const statement_js_1 = require("./statement.js");
|
|
10
|
+
class NodeSQLiteStatement extends statement_js_1.Statement {
|
|
11
|
+
stmt;
|
|
12
|
+
constructor(stmt) {
|
|
13
|
+
super();
|
|
14
|
+
this.stmt = stmt;
|
|
15
|
+
}
|
|
16
|
+
all(...params) {
|
|
17
|
+
return this.stmt.all(...params);
|
|
18
|
+
}
|
|
19
|
+
get(...params) {
|
|
20
|
+
return this.stmt.get(...params);
|
|
21
|
+
}
|
|
22
|
+
run(...params) {
|
|
23
|
+
return this.stmt.run(...params);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.NodeSQLiteStatement = NodeSQLiteStatement;
|
|
27
|
+
//# sourceMappingURL=node-sqlite-statement.js.map
|
|
@@ -1,80 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SQLite
|
|
2
|
+
* SQLite database adapter compatibility export.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* @module sqlite-adapter
|
|
8
|
-
*/
|
|
9
|
-
import Database from 'better-sqlite3';
|
|
10
|
-
import { DatabaseAdapter, type VectorSearchResult, type RunResult } from './base-adapter.js';
|
|
11
|
-
import { type Statement } from './statement.js';
|
|
12
|
-
/**
|
|
13
|
-
* Adapter configuration
|
|
4
|
+
* MAMA now standardizes on Node's built-in node:sqlite runtime. Keep the
|
|
5
|
+
* legacy SQLiteAdapter symbol so existing imports continue to work.
|
|
14
6
|
*/
|
|
7
|
+
import { NodeSQLiteAdapter } from './node-sqlite-adapter.js';
|
|
15
8
|
interface SQLiteAdapterConfig {
|
|
16
9
|
dbPath?: string;
|
|
17
10
|
}
|
|
18
|
-
export declare class SQLiteAdapter extends
|
|
19
|
-
private config;
|
|
20
|
-
private db;
|
|
21
|
-
private _vectorSearchEnabled;
|
|
11
|
+
export declare class SQLiteAdapter extends NodeSQLiteAdapter {
|
|
22
12
|
constructor(config?: SQLiteAdapterConfig);
|
|
23
|
-
/**
|
|
24
|
-
* Check if vector search is enabled
|
|
25
|
-
*/
|
|
26
|
-
get vectorSearchEnabled(): boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Get database path with backward compatibility
|
|
29
|
-
*/
|
|
30
|
-
getDbPath(): string;
|
|
31
|
-
/**
|
|
32
|
-
* Connect to SQLite database
|
|
33
|
-
*/
|
|
34
|
-
connect(): Database.Database;
|
|
35
|
-
/**
|
|
36
|
-
* Disconnect from database
|
|
37
|
-
*/
|
|
38
|
-
disconnect(): void;
|
|
39
|
-
/**
|
|
40
|
-
* Check if connected
|
|
41
|
-
*/
|
|
42
|
-
isConnected(): boolean;
|
|
43
|
-
/**
|
|
44
|
-
* Prepare a SQL statement
|
|
45
|
-
*/
|
|
46
|
-
prepare(sql: string): Statement;
|
|
47
|
-
/**
|
|
48
|
-
* Execute raw SQL
|
|
49
|
-
*/
|
|
50
|
-
exec(sql: string): void;
|
|
51
|
-
/**
|
|
52
|
-
* Execute function in transaction
|
|
53
|
-
*/
|
|
54
|
-
transaction<T>(fn: () => T): T;
|
|
55
|
-
/**
|
|
56
|
-
* Vector similarity search using brute-force cosine similarity
|
|
57
|
-
* Loads all embeddings from the embeddings table, computes similarity in JS,
|
|
58
|
-
* and returns top-N results sorted by similarity descending.
|
|
59
|
-
*/
|
|
60
|
-
vectorSearch(embedding: Float32Array | number[], limit?: number): VectorSearchResult[] | null;
|
|
61
|
-
/**
|
|
62
|
-
* Insert vector embedding into the embeddings table (plain BLOB storage)
|
|
63
|
-
*/
|
|
64
|
-
insertEmbedding(rowid: number, embedding: Float32Array | number[]): RunResult | null;
|
|
65
|
-
/**
|
|
66
|
-
* Get last inserted row ID
|
|
67
|
-
*/
|
|
68
|
-
getLastInsertRowid(): number;
|
|
69
|
-
/**
|
|
70
|
-
* Run migrations
|
|
71
|
-
*/
|
|
72
|
-
runMigrations(migrationsDir: string): void;
|
|
73
|
-
/**
|
|
74
|
-
* Attempt to migrate data from legacy vss_memories (sqlite-vec) to embeddings table.
|
|
75
|
-
* This is best-effort: if sqlite-vec is not loaded, the vss_memories table won't be readable.
|
|
76
|
-
*/
|
|
77
|
-
private _migrateFromVssMemories;
|
|
78
13
|
}
|
|
79
14
|
export default SQLiteAdapter;
|
|
80
15
|
//# sourceMappingURL=sqlite-adapter.d.ts.map
|
|
@@ -1,323 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* SQLite
|
|
3
|
+
* SQLite database adapter compatibility export.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* @module sqlite-adapter
|
|
5
|
+
* MAMA now standardizes on Node's built-in node:sqlite runtime. Keep the
|
|
6
|
+
* legacy SQLiteAdapter symbol so existing imports continue to work.
|
|
9
7
|
*/
|
|
10
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
-
};
|
|
13
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
9
|
exports.SQLiteAdapter = void 0;
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const fs_1 = __importDefault(require("fs"));
|
|
18
|
-
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
19
|
-
const base_adapter_js_1 = require("./base-adapter.js");
|
|
20
|
-
const statement_js_1 = require("./statement.js");
|
|
21
|
-
const debug_logger_js_1 = require("../debug-logger.js");
|
|
22
|
-
const embeddings_js_1 = require("../embeddings.js");
|
|
23
|
-
// Database paths
|
|
24
|
-
const LEGACY_DB_PATH = path_1.default.join(os_1.default.homedir(), '.spinelift', 'memories.db');
|
|
25
|
-
// Default to ~/.claude/mama-memory.db for Claude Code/Desktop compatibility
|
|
26
|
-
const DEFAULT_DB_PATH = path_1.default.join(os_1.default.homedir(), '.claude', 'mama-memory.db');
|
|
27
|
-
class SQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
|
|
28
|
-
config;
|
|
29
|
-
db = null;
|
|
30
|
-
_vectorSearchEnabled = true; // Always true: pure TS cosine similarity
|
|
10
|
+
const node_sqlite_adapter_js_1 = require("./node-sqlite-adapter.js");
|
|
11
|
+
class SQLiteAdapter extends node_sqlite_adapter_js_1.NodeSQLiteAdapter {
|
|
31
12
|
constructor(config = {}) {
|
|
32
|
-
super();
|
|
33
|
-
this.config = config;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Check if vector search is enabled
|
|
37
|
-
*/
|
|
38
|
-
get vectorSearchEnabled() {
|
|
39
|
-
return this._vectorSearchEnabled;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Get database path with backward compatibility
|
|
43
|
-
*/
|
|
44
|
-
getDbPath() {
|
|
45
|
-
// Support both MAMA_DB_PATH and MAMA_DATABASE_PATH for backward compatibility
|
|
46
|
-
const envPath = process.env.MAMA_DB_PATH || process.env.MAMA_DATABASE_PATH;
|
|
47
|
-
const configPath = this.config.dbPath;
|
|
48
|
-
// Expand ${HOME} or ~ in environment variable
|
|
49
|
-
let expandedEnvPath = envPath;
|
|
50
|
-
if (envPath) {
|
|
51
|
-
expandedEnvPath = envPath.replace(/\$\{HOME\}/g, os_1.default.homedir()).replace(/^~/, os_1.default.homedir());
|
|
52
|
-
}
|
|
53
|
-
// Priority: config > env > default
|
|
54
|
-
const targetPath = configPath || expandedEnvPath || DEFAULT_DB_PATH;
|
|
55
|
-
// Backward compatibility: Check legacy path if not explicitly set
|
|
56
|
-
if (!configPath && !envPath && fs_1.default.existsSync(LEGACY_DB_PATH)) {
|
|
57
|
-
(0, debug_logger_js_1.info)('[sqlite-adapter] Found legacy database at ~/.spinelift/memories.db, using it for backward compatibility');
|
|
58
|
-
return LEGACY_DB_PATH;
|
|
59
|
-
}
|
|
60
|
-
return targetPath;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Connect to SQLite database
|
|
64
|
-
*/
|
|
65
|
-
connect() {
|
|
66
|
-
if (this.db) {
|
|
67
|
-
return this.db;
|
|
68
|
-
}
|
|
69
|
-
const dbPath = this.getDbPath();
|
|
70
|
-
const dbDir = path_1.default.dirname(dbPath);
|
|
71
|
-
// Ensure directory exists
|
|
72
|
-
if (!fs_1.default.existsSync(dbDir)) {
|
|
73
|
-
fs_1.default.mkdirSync(dbDir, { recursive: true });
|
|
74
|
-
(0, debug_logger_js_1.info)(`[sqlite-adapter] Created database directory: ${dbDir}`);
|
|
75
|
-
}
|
|
76
|
-
// Open database
|
|
77
|
-
this.db = new better_sqlite3_1.default(dbPath, { verbose: undefined });
|
|
78
|
-
(0, debug_logger_js_1.info)(`[sqlite-adapter] Opened database at: ${dbPath}`);
|
|
79
|
-
// Production configuration
|
|
80
|
-
this.db.pragma('journal_mode = WAL');
|
|
81
|
-
this.db.pragma('busy_timeout = 5000');
|
|
82
|
-
this.db.pragma('synchronous = NORMAL');
|
|
83
|
-
this.db.pragma('cache_size = -64000'); // 64MB cache
|
|
84
|
-
this.db.pragma('temp_store = MEMORY');
|
|
85
|
-
this.db.pragma('foreign_keys = ON');
|
|
86
|
-
(0, debug_logger_js_1.info)('[sqlite-adapter] Vector search: pure TS cosine similarity (no native extensions)');
|
|
87
|
-
return this.db;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Disconnect from database
|
|
91
|
-
*/
|
|
92
|
-
disconnect() {
|
|
93
|
-
if (this.db) {
|
|
94
|
-
this.db.close();
|
|
95
|
-
this.db = null;
|
|
96
|
-
(0, debug_logger_js_1.info)('[sqlite-adapter] Disconnected from database');
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Check if connected
|
|
101
|
-
*/
|
|
102
|
-
isConnected() {
|
|
103
|
-
return this.db !== null && this.db.open;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Prepare a SQL statement
|
|
107
|
-
*/
|
|
108
|
-
prepare(sql) {
|
|
109
|
-
if (!this.isConnected() || !this.db) {
|
|
110
|
-
throw new Error('Database not connected');
|
|
111
|
-
}
|
|
112
|
-
const stmt = this.db.prepare(sql);
|
|
113
|
-
return new statement_js_1.SQLiteStatement(stmt);
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Execute raw SQL
|
|
117
|
-
*/
|
|
118
|
-
exec(sql) {
|
|
119
|
-
if (!this.isConnected() || !this.db) {
|
|
120
|
-
throw new Error('Database not connected');
|
|
121
|
-
}
|
|
122
|
-
this.db.exec(sql);
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Execute function in transaction
|
|
126
|
-
*/
|
|
127
|
-
transaction(fn) {
|
|
128
|
-
if (!this.isConnected() || !this.db) {
|
|
129
|
-
throw new Error('Database not connected');
|
|
130
|
-
}
|
|
131
|
-
const txn = this.db.transaction(fn);
|
|
132
|
-
return txn();
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Vector similarity search using brute-force cosine similarity
|
|
136
|
-
* Loads all embeddings from the embeddings table, computes similarity in JS,
|
|
137
|
-
* and returns top-N results sorted by similarity descending.
|
|
138
|
-
*/
|
|
139
|
-
vectorSearch(embedding, limit = 5) {
|
|
140
|
-
if (!this.isConnected()) {
|
|
141
|
-
throw new Error('Database not connected');
|
|
142
|
-
}
|
|
143
|
-
const queryVector = embedding instanceof Float32Array ? embedding : Float32Array.from(embedding);
|
|
144
|
-
// Check if embeddings table exists
|
|
145
|
-
const tableCheck = this.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings'`).all();
|
|
146
|
-
if (tableCheck.length === 0) {
|
|
147
|
-
throw new Error('Embeddings table missing');
|
|
148
|
-
}
|
|
149
|
-
const rows = this.prepare('SELECT rowid, embedding FROM embeddings').all();
|
|
150
|
-
const scored = [];
|
|
151
|
-
for (const row of rows) {
|
|
152
|
-
const candidate = bufferToVector(row.embedding);
|
|
153
|
-
if (!candidate)
|
|
154
|
-
continue;
|
|
155
|
-
if (candidate.length !== queryVector.length) {
|
|
156
|
-
(0, debug_logger_js_1.warn)(`Skipping rowid ${row.rowid}: dimension mismatch (${candidate.length} vs ${queryVector.length})`);
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
const similarity = (0, embeddings_js_1.cosineSimilarity)(candidate, queryVector);
|
|
160
|
-
scored.push({
|
|
161
|
-
rowid: row.rowid,
|
|
162
|
-
similarity,
|
|
163
|
-
distance: 1 - similarity,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
// Sort by similarity descending, return top-N
|
|
167
|
-
scored.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
|
|
168
|
-
return scored.slice(0, Math.max(limit, 1));
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Insert vector embedding into the embeddings table (plain BLOB storage)
|
|
172
|
-
*/
|
|
173
|
-
insertEmbedding(rowid, embedding) {
|
|
174
|
-
if (!this.isConnected()) {
|
|
175
|
-
throw new Error('Database not connected');
|
|
176
|
-
}
|
|
177
|
-
const vec = embedding instanceof Float32Array ? embedding : Float32Array.from(embedding);
|
|
178
|
-
const buffer = Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
|
|
179
|
-
const stmt = this.prepare(`
|
|
180
|
-
INSERT OR REPLACE INTO embeddings(rowid, embedding)
|
|
181
|
-
VALUES (?, ?)
|
|
182
|
-
`);
|
|
183
|
-
return stmt.run(rowid, buffer);
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Get last inserted row ID
|
|
187
|
-
*/
|
|
188
|
-
getLastInsertRowid() {
|
|
189
|
-
if (!this.isConnected() || !this.db) {
|
|
190
|
-
throw new Error('Database not connected');
|
|
191
|
-
}
|
|
192
|
-
// better-sqlite3 provides this via Database instance
|
|
193
|
-
const result = this.db.prepare('SELECT last_insert_rowid() as rowid').get();
|
|
194
|
-
return result.rowid;
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Run migrations
|
|
198
|
-
*/
|
|
199
|
-
runMigrations(migrationsDir) {
|
|
200
|
-
if (!this.isConnected()) {
|
|
201
|
-
throw new Error('Database not connected');
|
|
202
|
-
}
|
|
203
|
-
// Check if schema_version table exists
|
|
204
|
-
const tables = this.prepare(`
|
|
205
|
-
SELECT name FROM sqlite_master
|
|
206
|
-
WHERE type='table' AND name='schema_version'
|
|
207
|
-
`).all();
|
|
208
|
-
let currentVersion = 0;
|
|
209
|
-
if (tables.length > 0) {
|
|
210
|
-
const version = this.prepare('SELECT MAX(version) as version FROM schema_version').get();
|
|
211
|
-
currentVersion = version?.version || 0;
|
|
212
|
-
}
|
|
213
|
-
(0, debug_logger_js_1.info)(`[sqlite-adapter] Current schema version: ${currentVersion}`);
|
|
214
|
-
// Get all migration files
|
|
215
|
-
const migrationFiles = fs_1.default
|
|
216
|
-
.readdirSync(migrationsDir)
|
|
217
|
-
.filter((file) => file.endsWith('.sql'))
|
|
218
|
-
.sort();
|
|
219
|
-
// Apply migrations
|
|
220
|
-
for (const file of migrationFiles) {
|
|
221
|
-
const versionMatch = file.match(/^(\d+)-/);
|
|
222
|
-
if (!versionMatch) {
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
const version = parseInt(versionMatch[1], 10);
|
|
226
|
-
if (version <= currentVersion) {
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
const migrationPath = path_1.default.join(migrationsDir, file);
|
|
230
|
-
const migrationSQL = fs_1.default.readFileSync(migrationPath, 'utf8');
|
|
231
|
-
(0, debug_logger_js_1.info)(`[sqlite-adapter] Applying migration: ${file}`);
|
|
232
|
-
try {
|
|
233
|
-
this.exec('BEGIN TRANSACTION');
|
|
234
|
-
this.exec(migrationSQL);
|
|
235
|
-
this.exec('COMMIT');
|
|
236
|
-
// Record migration in schema_version table (outside transaction for idempotency)
|
|
237
|
-
this.prepare('INSERT OR IGNORE INTO schema_version (version) VALUES (?)').run(version);
|
|
238
|
-
(0, debug_logger_js_1.info)(`[sqlite-adapter] Migration ${file} applied successfully`);
|
|
239
|
-
}
|
|
240
|
-
catch (err) {
|
|
241
|
-
this.exec('ROLLBACK');
|
|
242
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
243
|
-
// Handle duplicate column errors as idempotent (migration 003)
|
|
244
|
-
if (message.includes('duplicate column')) {
|
|
245
|
-
(0, debug_logger_js_1.warn)(`[sqlite-adapter] Migration ${file} skipped (duplicate column - already applied)`);
|
|
246
|
-
// Record migration as applied
|
|
247
|
-
this.prepare('INSERT OR IGNORE INTO schema_version (version) VALUES (?)').run(version);
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
if (message.includes('no such table')) {
|
|
251
|
-
// Only skip if this migration contains an ALTER TABLE (adding column to optional table)
|
|
252
|
-
// Note: migrationSQL may contain comments, so check if ALTER TABLE exists anywhere
|
|
253
|
-
const hasAlterTable = migrationSQL.toUpperCase().includes('ALTER TABLE');
|
|
254
|
-
if (!hasAlterTable) {
|
|
255
|
-
(0, debug_logger_js_1.error)(`[sqlite-adapter] Migration ${file} failed (missing required table):`, err);
|
|
256
|
-
throw new Error(`Migration ${file} failed: ${message}`);
|
|
257
|
-
}
|
|
258
|
-
(0, debug_logger_js_1.warn)(`[sqlite-adapter] Migration ${file} skipped: ALTER TABLE on non-existent table (${message})`);
|
|
259
|
-
this.prepare('INSERT OR IGNORE INTO schema_version (version) VALUES (?)').run(version);
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
(0, debug_logger_js_1.error)(`[sqlite-adapter] Migration ${file} failed:`, err);
|
|
263
|
-
throw new Error(`Migration ${file} failed: ${message}`);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
// Ensure embeddings table exists (plain table, no native extensions)
|
|
267
|
-
const embeddingsTables = this.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings'`).all();
|
|
268
|
-
if (embeddingsTables.length === 0) {
|
|
269
|
-
(0, debug_logger_js_1.info)('[sqlite-adapter] Creating embeddings table');
|
|
270
|
-
this.exec(`
|
|
271
|
-
CREATE TABLE embeddings (
|
|
272
|
-
rowid INTEGER PRIMARY KEY,
|
|
273
|
-
embedding BLOB NOT NULL
|
|
274
|
-
)
|
|
275
|
-
`);
|
|
276
|
-
}
|
|
277
|
-
// Always attempt migration — uses INSERT OR IGNORE so safe to run multiple times
|
|
278
|
-
this._migrateFromVssMemories();
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Attempt to migrate data from legacy vss_memories (sqlite-vec) to embeddings table.
|
|
282
|
-
* This is best-effort: if sqlite-vec is not loaded, the vss_memories table won't be readable.
|
|
283
|
-
*/
|
|
284
|
-
_migrateFromVssMemories() {
|
|
285
|
-
try {
|
|
286
|
-
const vssTables = this.prepare(`SELECT name FROM sqlite_master WHERE name='vss_memories'`).all();
|
|
287
|
-
if (vssTables.length === 0) {
|
|
288
|
-
return; // No legacy table
|
|
289
|
-
}
|
|
290
|
-
// Try to read from vss_memories — will fail if sqlite-vec extension isn't loaded
|
|
291
|
-
const rows = this.prepare('SELECT rowid, embedding FROM vss_memories').all();
|
|
292
|
-
if (rows.length > 0) {
|
|
293
|
-
const insertStmt = this.prepare('INSERT OR IGNORE INTO embeddings (rowid, embedding) VALUES (?, ?)');
|
|
294
|
-
let migrated = 0;
|
|
295
|
-
for (const row of rows) {
|
|
296
|
-
const res = insertStmt.run(row.rowid, row.embedding);
|
|
297
|
-
if (res.changes > 0)
|
|
298
|
-
migrated++;
|
|
299
|
-
}
|
|
300
|
-
(0, debug_logger_js_1.info)(`[sqlite-adapter] Migrated ${migrated} embeddings from vss_memories to embeddings`);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
catch (err) {
|
|
304
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
305
|
-
(0, debug_logger_js_1.warn)(`[sqlite-adapter] Could not migrate from vss_memories (expected if sqlite-vec not installed): ${message}`);
|
|
306
|
-
}
|
|
13
|
+
super(config);
|
|
307
14
|
}
|
|
308
15
|
}
|
|
309
16
|
exports.SQLiteAdapter = SQLiteAdapter;
|
|
310
17
|
exports.default = SQLiteAdapter;
|
|
311
|
-
function bufferToVector(buffer) {
|
|
312
|
-
if (!buffer || buffer.byteLength % 4 !== 0) {
|
|
313
|
-
return null;
|
|
314
|
-
}
|
|
315
|
-
try {
|
|
316
|
-
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
317
|
-
return new Float32Array(arrayBuffer);
|
|
318
|
-
}
|
|
319
|
-
catch {
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
18
|
//# sourceMappingURL=sqlite-adapter.js.map
|
|
@@ -81,6 +81,7 @@ export declare class SQLiteStatement extends Statement {
|
|
|
81
81
|
run(...params: unknown[]): RunResult;
|
|
82
82
|
finalize(): void;
|
|
83
83
|
}
|
|
84
|
+
export { NodeSQLiteStatement } from './node-sqlite-statement.js';
|
|
84
85
|
/**
|
|
85
86
|
* PostgreSQL statement wrapper (pg)
|
|
86
87
|
*
|
|
@@ -115,5 +116,4 @@ export declare class PostgreSQLStatement extends Statement {
|
|
|
115
116
|
runAsync(...params: unknown[]): Promise<RunResult>;
|
|
116
117
|
finalize(): void;
|
|
117
118
|
}
|
|
118
|
-
export {};
|
|
119
119
|
//# sourceMappingURL=statement.d.ts.map
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @module statement
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.PostgreSQLStatement = exports.SQLiteStatement = exports.Statement = void 0;
|
|
11
|
+
exports.PostgreSQLStatement = exports.NodeSQLiteStatement = exports.SQLiteStatement = exports.Statement = void 0;
|
|
12
12
|
/**
|
|
13
13
|
* Base statement interface
|
|
14
14
|
* All statement wrappers must implement these methods
|
|
@@ -45,6 +45,8 @@ class SQLiteStatement extends Statement {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
exports.SQLiteStatement = SQLiteStatement;
|
|
48
|
+
var node_sqlite_statement_js_1 = require("./node-sqlite-statement.js");
|
|
49
|
+
Object.defineProperty(exports, "NodeSQLiteStatement", { enumerable: true, get: function () { return node_sqlite_statement_js_1.NodeSQLiteStatement; } });
|
|
48
50
|
/**
|
|
49
51
|
* PostgreSQL statement wrapper (pg)
|
|
50
52
|
*
|
|
@@ -20,6 +20,7 @@ exports.isLocalhost = isLocalhost;
|
|
|
20
20
|
exports.authenticate = authenticate;
|
|
21
21
|
exports.authenticateWebSocket = authenticateWebSocket;
|
|
22
22
|
exports.createAuthMiddleware = createAuthMiddleware;
|
|
23
|
+
const node_crypto_1 = require("node:crypto");
|
|
23
24
|
/**
|
|
24
25
|
* Environment variable for auth token
|
|
25
26
|
*/
|
|
@@ -28,6 +29,24 @@ exports.AUTH_TOKEN = process.env.MAMA_AUTH_TOKEN;
|
|
|
28
29
|
* Track if we've warned about missing auth token
|
|
29
30
|
*/
|
|
30
31
|
let hasWarnedAboutToken = false;
|
|
32
|
+
/**
|
|
33
|
+
* Track if we've warned about query parameter token usage
|
|
34
|
+
*/
|
|
35
|
+
let hasWarnedAboutQueryToken = false;
|
|
36
|
+
/**
|
|
37
|
+
* HMAC key for timing-safe token comparison
|
|
38
|
+
*/
|
|
39
|
+
const hmacKey = (0, node_crypto_1.randomBytes)(32);
|
|
40
|
+
/**
|
|
41
|
+
* Timing-safe string comparison using HMAC to prevent timing attacks.
|
|
42
|
+
* Both strings are hashed with HMAC-SHA256 before comparison,
|
|
43
|
+
* ensuring constant-time evaluation regardless of input.
|
|
44
|
+
*/
|
|
45
|
+
function safeTokenEqual(a, b) {
|
|
46
|
+
const hmacA = (0, node_crypto_1.createHmac)('sha256', hmacKey).update(a).digest();
|
|
47
|
+
const hmacB = (0, node_crypto_1.createHmac)('sha256', hmacKey).update(b).digest();
|
|
48
|
+
return (0, node_crypto_1.timingSafeEqual)(hmacA, hmacB);
|
|
49
|
+
}
|
|
31
50
|
/**
|
|
32
51
|
* Check if request is from localhost
|
|
33
52
|
* @param req - HTTP request
|
|
@@ -95,7 +114,7 @@ function authenticate(req) {
|
|
|
95
114
|
const authHeader = req.headers['authorization'];
|
|
96
115
|
if (authHeader?.startsWith('Bearer ')) {
|
|
97
116
|
const token = authHeader.slice(7);
|
|
98
|
-
if (token
|
|
117
|
+
if (safeTokenEqual(token, exports.AUTH_TOKEN)) {
|
|
99
118
|
console.error(`[Auth] External access granted via Bearer token from ${remoteAddress}`);
|
|
100
119
|
return true;
|
|
101
120
|
}
|
|
@@ -103,7 +122,13 @@ function authenticate(req) {
|
|
|
103
122
|
// Check URL query parameter
|
|
104
123
|
const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
105
124
|
const queryToken = url.searchParams.get('token');
|
|
106
|
-
if (queryToken
|
|
125
|
+
if (queryToken && safeTokenEqual(queryToken, exports.AUTH_TOKEN)) {
|
|
126
|
+
if (!hasWarnedAboutQueryToken) {
|
|
127
|
+
console.warn('[Auth] DEPRECATION WARNING: Authentication via query parameter (?token=...) is deprecated. ' +
|
|
128
|
+
'Token in URL may leak through server logs, browser history, and Referer headers. ' +
|
|
129
|
+
'Please use the Authorization header instead: "Authorization: Bearer <token>"');
|
|
130
|
+
hasWarnedAboutQueryToken = true;
|
|
131
|
+
}
|
|
107
132
|
console.error(`[Auth] External access granted via query token from ${remoteAddress}`);
|
|
108
133
|
return true;
|
|
109
134
|
}
|
|
@@ -58,6 +58,9 @@ export declare class SessionManager {
|
|
|
58
58
|
private sessions;
|
|
59
59
|
private db;
|
|
60
60
|
private initialized;
|
|
61
|
+
private cleanupPromise;
|
|
62
|
+
private cleanupHandlersRegistered;
|
|
63
|
+
private handleProcessExit;
|
|
61
64
|
/**
|
|
62
65
|
* Create a new SessionManager instance
|
|
63
66
|
* @param dbPath - Path to SQLite database
|
|
@@ -53,6 +53,9 @@ class SessionManager {
|
|
|
53
53
|
sessions;
|
|
54
54
|
db;
|
|
55
55
|
initialized;
|
|
56
|
+
cleanupPromise;
|
|
57
|
+
cleanupHandlersRegistered;
|
|
58
|
+
handleProcessExit;
|
|
56
59
|
/**
|
|
57
60
|
* Create a new SessionManager instance
|
|
58
61
|
* @param dbPath - Path to SQLite database
|
|
@@ -62,6 +65,9 @@ class SessionManager {
|
|
|
62
65
|
this.sessions = new Map();
|
|
63
66
|
this.db = null;
|
|
64
67
|
this.initialized = false;
|
|
68
|
+
this.cleanupPromise = null;
|
|
69
|
+
this.cleanupHandlersRegistered = false;
|
|
70
|
+
this.handleProcessExit = null;
|
|
65
71
|
}
|
|
66
72
|
/**
|
|
67
73
|
* Initialize the SQLite database and create sessions table
|
|
@@ -71,10 +77,9 @@ class SessionManager {
|
|
|
71
77
|
return;
|
|
72
78
|
}
|
|
73
79
|
try {
|
|
74
|
-
// Dynamically import better-sqlite3 to handle cases where it's not installed
|
|
75
80
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
76
|
-
const
|
|
77
|
-
this.db = new
|
|
81
|
+
const { DatabaseSync } = require('node:sqlite');
|
|
82
|
+
this.db = new DatabaseSync(this.dbPath);
|
|
78
83
|
// Create sessions table if not exists
|
|
79
84
|
this.db.exec(exports.CREATE_SESSIONS_TABLE);
|
|
80
85
|
this.db.exec(CREATE_STATUS_INDEX);
|
|
@@ -307,25 +312,50 @@ class SessionManager {
|
|
|
307
312
|
* @private
|
|
308
313
|
*/
|
|
309
314
|
_setupProcessCleanup() {
|
|
310
|
-
|
|
315
|
+
if (this.cleanupHandlersRegistered) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const cleanup = () => {
|
|
319
|
+
if (this.cleanupPromise) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (this.handleProcessExit) {
|
|
323
|
+
process.off('exit', this.handleProcessExit);
|
|
324
|
+
}
|
|
325
|
+
this.cleanupPromise = Promise.resolve();
|
|
311
326
|
console.error('[SessionManager] Process exiting, cleaning up sessions...');
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
327
|
+
if (this.db && this.initialized) {
|
|
328
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
329
|
+
try {
|
|
330
|
+
session.daemon.kill();
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
// Best effort - process is already exiting.
|
|
334
|
+
}
|
|
335
|
+
this.sessions.delete(sessionId);
|
|
336
|
+
const stmt = this.db.prepare(`
|
|
337
|
+
UPDATE sessions
|
|
338
|
+
SET status = 'terminated', last_active = datetime('now')
|
|
339
|
+
WHERE id = ?
|
|
340
|
+
`);
|
|
341
|
+
stmt.run(sessionId);
|
|
342
|
+
}
|
|
315
343
|
}
|
|
344
|
+
this.close();
|
|
316
345
|
};
|
|
317
|
-
|
|
318
|
-
process.on('
|
|
319
|
-
|
|
346
|
+
this.handleProcessExit = cleanup;
|
|
347
|
+
process.on('exit', this.handleProcessExit);
|
|
348
|
+
this.cleanupHandlersRegistered = true;
|
|
320
349
|
}
|
|
321
350
|
/**
|
|
322
351
|
* Close the database connection
|
|
323
352
|
*/
|
|
324
353
|
close() {
|
|
325
354
|
if (this.db) {
|
|
326
|
-
this.db
|
|
355
|
+
const db = this.db;
|
|
327
356
|
this.db = null;
|
|
328
357
|
this.initialized = false;
|
|
358
|
+
db.close();
|
|
329
359
|
}
|
|
330
360
|
}
|
|
331
361
|
}
|
package/dist/embeddings.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* @date 2025-11-20
|
|
11
11
|
*/
|
|
12
12
|
import { embeddingCache } from './embedding-cache.js';
|
|
13
|
-
import { loadConfig, getModelName, getEmbeddingDim } from './config-loader.js';
|
|
13
|
+
import { loadConfig, getModelName, getEmbeddingDim, getQuantized } from './config-loader.js';
|
|
14
14
|
/**
|
|
15
15
|
* Decision object for enhanced embedding generation
|
|
16
16
|
*/
|
|
@@ -71,5 +71,5 @@ export declare function cosineSimilarity(embA: Float32Array, embB: Float32Array)
|
|
|
71
71
|
export { embeddingCache };
|
|
72
72
|
export declare const EMBEDDING_DIM: number;
|
|
73
73
|
export declare const MODEL_NAME: string;
|
|
74
|
-
export { loadConfig, getModelName, getEmbeddingDim };
|
|
74
|
+
export { loadConfig, getModelName, getEmbeddingDim, getQuantized };
|
|
75
75
|
//# sourceMappingURL=embeddings.d.ts.map
|
package/dist/embeddings.js
CHANGED
|
@@ -14,7 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.getEmbeddingDim = exports.getModelName = exports.loadConfig = exports.MODEL_NAME = exports.EMBEDDING_DIM = exports.embeddingCache = void 0;
|
|
17
|
+
exports.getQuantized = exports.getEmbeddingDim = exports.getModelName = exports.loadConfig = exports.MODEL_NAME = exports.EMBEDDING_DIM = exports.embeddingCache = void 0;
|
|
18
18
|
exports.generateEmbedding = generateEmbedding;
|
|
19
19
|
exports.generateEnhancedEmbedding = generateEnhancedEmbedding;
|
|
20
20
|
exports.generateBatchEmbeddings = generateBatchEmbeddings;
|
|
@@ -29,6 +29,7 @@ const config_loader_js_1 = require("./config-loader.js");
|
|
|
29
29
|
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_loader_js_1.loadConfig; } });
|
|
30
30
|
Object.defineProperty(exports, "getModelName", { enumerable: true, get: function () { return config_loader_js_1.getModelName; } });
|
|
31
31
|
Object.defineProperty(exports, "getEmbeddingDim", { enumerable: true, get: function () { return config_loader_js_1.getEmbeddingDim; } });
|
|
32
|
+
Object.defineProperty(exports, "getQuantized", { enumerable: true, get: function () { return config_loader_js_1.getQuantized; } });
|
|
32
33
|
// Shared cache directory (not in node_modules)
|
|
33
34
|
const DEFAULT_CACHE_DIR = path_1.default.join(os_1.default.homedir(), '.cache', 'huggingface', 'transformers');
|
|
34
35
|
// Singleton pattern for model loading
|
|
@@ -73,11 +74,14 @@ async function loadModel() {
|
|
|
73
74
|
const cacheDir = process.env.HF_HOME || process.env.TRANSFORMERS_CACHE || DEFAULT_CACHE_DIR;
|
|
74
75
|
env.cacheDir = cacheDir;
|
|
75
76
|
(0, debug_logger_js_1.info)(`[MAMA] Model cache directory: ${cacheDir}`);
|
|
76
|
-
|
|
77
|
+
const quantized = (0, config_loader_js_1.getQuantized)();
|
|
78
|
+
embeddingPipeline = (await pipeline('feature-extraction', modelName, {
|
|
79
|
+
dtype: quantized ? 'q8' : 'fp32',
|
|
80
|
+
}));
|
|
77
81
|
currentModelName = modelName;
|
|
78
82
|
const loadTime = Date.now() - startTime;
|
|
79
83
|
const config = (0, config_loader_js_1.loadConfig)();
|
|
80
|
-
(0, progress_indicator_js_1.logComplete)(`Embedding model ready (${loadTime}ms, ${config.embeddingDim}-dim)`);
|
|
84
|
+
(0, progress_indicator_js_1.logComplete)(`Embedding model ready (${loadTime}ms, ${config.embeddingDim}-dim, ${quantized ? 'q8' : 'fp32'})`);
|
|
81
85
|
}
|
|
82
86
|
catch (loadErr) {
|
|
83
87
|
modelLoadFailed = true;
|
package/dist/tier-validator.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Centralized tier validation module for MAMA.
|
|
5
5
|
* Validates system requirements and determines tier status (1 or 2).
|
|
6
6
|
*
|
|
7
|
-
* Tier 1: Full features (Node.js
|
|
7
|
+
* Tier 1: Full features (Node.js 22.13+, SQLite, Embeddings, Database)
|
|
8
8
|
* Tier 2: Degraded mode (missing one or more requirements)
|
|
9
9
|
*
|
|
10
10
|
* @module tier-validator
|
|
@@ -25,7 +25,7 @@ export interface TierValidation {
|
|
|
25
25
|
*/
|
|
26
26
|
export declare function checkNodeVersion(): CheckResult;
|
|
27
27
|
/**
|
|
28
|
-
* Validates SQLite (
|
|
28
|
+
* Validates SQLite availability (node:sqlite)
|
|
29
29
|
*/
|
|
30
30
|
export declare function checkSQLite(): CheckResult;
|
|
31
31
|
/**
|
package/dist/tier-validator.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Centralized tier validation module for MAMA.
|
|
6
6
|
* Validates system requirements and determines tier status (1 or 2).
|
|
7
7
|
*
|
|
8
|
-
* Tier 1: Full features (Node.js
|
|
8
|
+
* Tier 1: Full features (Node.js 22.13+, SQLite, Embeddings, Database)
|
|
9
9
|
* Tier 2: Degraded mode (missing one or more requirements)
|
|
10
10
|
*
|
|
11
11
|
* @module tier-validator
|
|
@@ -30,8 +30,8 @@ const os_1 = __importDefault(require("os"));
|
|
|
30
30
|
function checkNodeVersion() {
|
|
31
31
|
try {
|
|
32
32
|
const nodeVersion = process.versions.node;
|
|
33
|
-
const majorVersion =
|
|
34
|
-
if (majorVersion >=
|
|
33
|
+
const [majorVersion, minorVersion] = nodeVersion.split('.').map((part) => parseInt(part, 10));
|
|
34
|
+
if (majorVersion > 22 || (majorVersion === 22 && minorVersion >= 13)) {
|
|
35
35
|
return {
|
|
36
36
|
status: 'pass',
|
|
37
37
|
details: `v${nodeVersion}`,
|
|
@@ -39,7 +39,7 @@ function checkNodeVersion() {
|
|
|
39
39
|
}
|
|
40
40
|
return {
|
|
41
41
|
status: 'fail',
|
|
42
|
-
details: `v${nodeVersion} (requires
|
|
42
|
+
details: `v${nodeVersion} (requires 22.13+)`,
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
catch (error) {
|
|
@@ -51,25 +51,24 @@ function checkNodeVersion() {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
* Validates SQLite (
|
|
54
|
+
* Validates SQLite availability (node:sqlite)
|
|
55
55
|
*/
|
|
56
56
|
function checkSQLite() {
|
|
57
57
|
try {
|
|
58
58
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
const testDb = new Database(':memory:');
|
|
59
|
+
const { DatabaseSync } = require('node:sqlite');
|
|
60
|
+
const testDb = new DatabaseSync(':memory:');
|
|
62
61
|
testDb.close();
|
|
63
62
|
return {
|
|
64
63
|
status: 'pass',
|
|
65
|
-
details: '
|
|
64
|
+
details: 'node:sqlite built-in driver ready',
|
|
66
65
|
};
|
|
67
66
|
}
|
|
68
67
|
catch (error) {
|
|
69
68
|
const message = error instanceof Error ? error.message : String(error);
|
|
70
69
|
return {
|
|
71
70
|
status: 'fail',
|
|
72
|
-
details: `
|
|
71
|
+
details: `node:sqlite not available: ${message}`,
|
|
73
72
|
};
|
|
74
73
|
}
|
|
75
74
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jungjaehoon/mama-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "MAMA Core - Shared modules for Memory-Augmented MCP Assistant",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -59,15 +59,13 @@
|
|
|
59
59
|
"access": "public"
|
|
60
60
|
},
|
|
61
61
|
"engines": {
|
|
62
|
-
"node": ">=
|
|
62
|
+
"node": ">=22.13.0"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@huggingface/transformers": "^3.8.1",
|
|
66
|
-
"better-sqlite3": "^11.0.0",
|
|
67
66
|
"ws": "^8.19.0"
|
|
68
67
|
},
|
|
69
68
|
"devDependencies": {
|
|
70
|
-
"@types/better-sqlite3": "^7.6.0",
|
|
71
69
|
"@types/node": "^22.0.0",
|
|
72
70
|
"@types/ws": "^8.18.1",
|
|
73
71
|
"typescript": "^5.4.0",
|