claude-flow 3.21.0 → 3.22.0
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/package.json +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/daemon.js +23 -2
- package/v3/@claude-flow/cli/dist/src/commands/doctor.js +56 -0
- package/v3/@claude-flow/cli/dist/src/commands/memory-distill.d.ts +27 -0
- package/v3/@claude-flow/cli/dist/src/commands/memory-distill.js +374 -0
- package/v3/@claude-flow/cli/dist/src/commands/memory.js +4 -2
- package/v3/@claude-flow/cli/dist/src/index.d.ts +1 -1
- package/v3/@claude-flow/cli/dist/src/index.js +22 -1
- package/v3/@claude-flow/cli/dist/src/init/executor.js +37 -0
- package/v3/@claude-flow/cli/dist/src/init/helper-refresh.d.ts +19 -0
- package/v3/@claude-flow/cli/dist/src/init/helper-refresh.js +175 -0
- package/v3/@claude-flow/cli/dist/src/init/helper-signing.d.ts +30 -0
- package/v3/@claude-flow/cli/dist/src/init/helper-signing.js +60 -0
- package/v3/@claude-flow/cli/dist/src/init/helpers-generator.d.ts +16 -0
- package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +89 -8
- package/v3/@claude-flow/cli/dist/src/init/memory-package-resolver.d.ts +53 -0
- package/v3/@claude-flow/cli/dist/src/init/memory-package-resolver.js +118 -0
- package/v3/@claude-flow/cli/dist/src/init/statusline-generator.js +18 -7
- package/v3/@claude-flow/cli/dist/src/mcp-client.js +13 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-speculate-tools.js +9 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-intent-tools.d.ts +162 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-intent-tools.js +548 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-tools.d.ts +9 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-tools.js +4 -1
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +115 -54
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +71 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +330 -2
- package/v3/@claude-flow/cli/dist/src/services/distill-tuning.d.ts +111 -0
- package/v3/@claude-flow/cli/dist/src/services/distill-tuning.js +510 -0
- package/v3/@claude-flow/cli/dist/src/services/memory-distillation.d.ts +41 -0
- package/v3/@claude-flow/cli/dist/src/services/memory-distillation.js +277 -0
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +22 -1
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +117 -6
- package/v3/@claude-flow/cli/package.json +5 -2
|
@@ -365,11 +365,9 @@ async function getRegistry(dbPath) {
|
|
|
365
365
|
* Replaces naive String.includes() with proper information retrieval scoring.
|
|
366
366
|
* Parameters tuned for short memory entries (k1=1.2, b=0.75).
|
|
367
367
|
*/
|
|
368
|
-
function bm25Score(queryTerms,
|
|
368
|
+
function bm25Score(queryTerms, docWords, docLength, avgDocLength, docCount, termDocFreqs) {
|
|
369
369
|
const k1 = 1.2;
|
|
370
370
|
const b = 0.75;
|
|
371
|
-
const docWords = docContent.toLowerCase().split(/\s+/);
|
|
372
|
-
const docLength = docWords.length;
|
|
373
371
|
let score = 0;
|
|
374
372
|
for (const term of queryTerms) {
|
|
375
373
|
const tf = docWords.filter(w => w === term || w.includes(term)).length;
|
|
@@ -382,23 +380,27 @@ function bm25Score(queryTerms, docContent, avgDocLength, docCount, termDocFreqs)
|
|
|
382
380
|
}
|
|
383
381
|
return score;
|
|
384
382
|
}
|
|
383
|
+
function tokenizeCorpus(rows) {
|
|
384
|
+
return rows.map(row => {
|
|
385
|
+
const contentLower = (row.content || '').toLowerCase();
|
|
386
|
+
return { contentLower, words: contentLower.split(/\s+/) };
|
|
387
|
+
});
|
|
388
|
+
}
|
|
385
389
|
/**
|
|
386
|
-
* Compute BM25 term document frequencies
|
|
390
|
+
* Compute BM25 term document frequencies over an already-tokenized corpus.
|
|
387
391
|
*/
|
|
388
|
-
function computeTermDocFreqs(queryTerms,
|
|
392
|
+
function computeTermDocFreqs(queryTerms, docs) {
|
|
389
393
|
const termDocFreqs = new Map();
|
|
390
394
|
let totalLength = 0;
|
|
391
|
-
for (const
|
|
392
|
-
|
|
393
|
-
const words = content.split(/\s+/);
|
|
394
|
-
totalLength += words.length;
|
|
395
|
+
for (const doc of docs) {
|
|
396
|
+
totalLength += doc.words.length;
|
|
395
397
|
for (const term of queryTerms) {
|
|
396
|
-
if (
|
|
398
|
+
if (doc.contentLower.includes(term)) {
|
|
397
399
|
termDocFreqs.set(term, (termDocFreqs.get(term) || 0) + 1);
|
|
398
400
|
}
|
|
399
401
|
}
|
|
400
402
|
}
|
|
401
|
-
return { termDocFreqs, avgDocLength:
|
|
403
|
+
return { termDocFreqs, avgDocLength: docs.length > 0 ? totalLength / docs.length : 1 };
|
|
402
404
|
}
|
|
403
405
|
// ===== Phase 2: TieredCache helpers =====
|
|
404
406
|
/**
|
|
@@ -484,6 +486,10 @@ async function logAttestation(registry, operation, entryId, metadata) {
|
|
|
484
486
|
// Non-fatal — attestation is observability, not correctness
|
|
485
487
|
}
|
|
486
488
|
}
|
|
489
|
+
// Tracks db handles whose schema DDL has already been ensured, so getDb()
|
|
490
|
+
// runs the CREATE…IF NOT EXISTS block at most once per handle instead of on
|
|
491
|
+
// every bridge call. WeakSet so handles GC without leaking.
|
|
492
|
+
const _schemaEnsuredDbs = new WeakSet();
|
|
487
493
|
/**
|
|
488
494
|
* Get the AgentDB database handle and ensure memory_entries table exists.
|
|
489
495
|
* Returns null if not available.
|
|
@@ -493,35 +499,42 @@ function getDb(registry) {
|
|
|
493
499
|
if (!agentdb?.database)
|
|
494
500
|
return null;
|
|
495
501
|
const db = agentdb.database;
|
|
496
|
-
// Ensure memory_entries table exists (idempotent)
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
502
|
+
// Ensure memory_entries table exists (idempotent). The DDL is run at most
|
|
503
|
+
// once per db handle — re-parsing 4× CREATE…IF NOT EXISTS on every bridge
|
|
504
|
+
// call (store/search/get) was pure per-op overhead. Keyed by handle via a
|
|
505
|
+
// WeakSet so a new db instance re-ensures without a stale global flag.
|
|
506
|
+
if (!_schemaEnsuredDbs.has(db)) {
|
|
507
|
+
try {
|
|
508
|
+
db.exec(`CREATE TABLE IF NOT EXISTS memory_entries (
|
|
509
|
+
id TEXT PRIMARY KEY,
|
|
510
|
+
key TEXT NOT NULL,
|
|
511
|
+
namespace TEXT DEFAULT 'default',
|
|
512
|
+
content TEXT NOT NULL,
|
|
513
|
+
type TEXT DEFAULT 'semantic',
|
|
514
|
+
embedding TEXT,
|
|
515
|
+
embedding_model TEXT DEFAULT 'local',
|
|
516
|
+
embedding_dimensions INTEGER,
|
|
517
|
+
tags TEXT,
|
|
518
|
+
metadata TEXT,
|
|
519
|
+
owner_id TEXT,
|
|
520
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
|
|
521
|
+
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
|
|
522
|
+
expires_at INTEGER,
|
|
523
|
+
last_accessed_at INTEGER,
|
|
524
|
+
access_count INTEGER DEFAULT 0,
|
|
525
|
+
status TEXT DEFAULT 'active',
|
|
526
|
+
UNIQUE(namespace, key)
|
|
527
|
+
)`);
|
|
528
|
+
// Ensure indexes
|
|
529
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_bridge_ns ON memory_entries(namespace)`);
|
|
530
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_bridge_key ON memory_entries(key)`);
|
|
531
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_bridge_status ON memory_entries(status)`);
|
|
532
|
+
_schemaEnsuredDbs.add(db);
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
// Table already exists or db is read-only — that's fine. Don't mark
|
|
536
|
+
// ensured on failure so a later writable call can retry.
|
|
537
|
+
}
|
|
525
538
|
}
|
|
526
539
|
// ─── #2256-followup: rescue agentdb.embedder when its transformers.js
|
|
527
540
|
// path fell through to mock embeddings.
|
|
@@ -637,6 +650,7 @@ export async function bridgeStoreEntry(options) {
|
|
|
637
650
|
}
|
|
638
651
|
// Generate embedding via AgentDB's embedder
|
|
639
652
|
let embeddingJson = null;
|
|
653
|
+
let embeddingArr = null;
|
|
640
654
|
let dimensions = 0;
|
|
641
655
|
let model = 'local';
|
|
642
656
|
if (options.generateEmbeddingFlag !== false && value.length > 0) {
|
|
@@ -645,7 +659,8 @@ export async function bridgeStoreEntry(options) {
|
|
|
645
659
|
if (embedder) {
|
|
646
660
|
const emb = await embedder.embed(value);
|
|
647
661
|
if (emb) {
|
|
648
|
-
|
|
662
|
+
embeddingArr = Array.from(emb);
|
|
663
|
+
embeddingJson = JSON.stringify(embeddingArr);
|
|
649
664
|
dimensions = emb.length;
|
|
650
665
|
model = 'Xenova/all-MiniLM-L6-v2';
|
|
651
666
|
}
|
|
@@ -680,6 +695,31 @@ export async function bridgeStoreEntry(options) {
|
|
|
680
695
|
catch { /* vector_indexes may not exist on legacy DBs — fall through */ }
|
|
681
696
|
const stmt = ctx.db.prepare(insertSql);
|
|
682
697
|
stmt.run(id, key, namespace, value, embeddingJson, dimensions || null, model, tags.length > 0 ? JSON.stringify(tags) : null, '{}', now, now, ttl ? now + (ttl * 1000) : null);
|
|
698
|
+
// #2558: keep `vector_indexes.total_vectors` accurate so status/tooling
|
|
699
|
+
// stop reporting "HNSW index: 0 vectors" while embedded entries exist.
|
|
700
|
+
try {
|
|
701
|
+
ctx.db
|
|
702
|
+
.prepare(`UPDATE vector_indexes SET
|
|
703
|
+
total_vectors = (SELECT COUNT(*) FROM memory_entries
|
|
704
|
+
WHERE namespace = ? AND embedding IS NOT NULL),
|
|
705
|
+
updated_at = ?
|
|
706
|
+
WHERE name = ?`)
|
|
707
|
+
.run(namespace, now, namespace);
|
|
708
|
+
}
|
|
709
|
+
catch { /* vector_indexes may not exist on legacy DBs — non-fatal */ }
|
|
710
|
+
// #2558: better-sqlite3 opens the DB in WAL mode and, for the small write
|
|
711
|
+
// volumes typical of CLI usage, may never reach the auto-checkpoint
|
|
712
|
+
// threshold — leaving committed rows only in the -wal file. WAL-blind
|
|
713
|
+
// readers (the sql.js fallback search path; the statusline's read-only
|
|
714
|
+
// `sqlite3` vector count) then see a stale/empty main DB file and report
|
|
715
|
+
// "0 vectors" / empty search. A PASSIVE checkpoint flushes committed pages
|
|
716
|
+
// into the main file without blocking writers. Best-effort, never fatal.
|
|
717
|
+
try {
|
|
718
|
+
if (typeof ctx.db.pragma === 'function') {
|
|
719
|
+
ctx.db.pragma('wal_checkpoint(PASSIVE)');
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
catch { /* non-WAL, busy, or unsupported — non-fatal */ }
|
|
683
723
|
// Phase 2: Write-through to TieredCache
|
|
684
724
|
const safeNs = String(namespace).replace(/:/g, '_');
|
|
685
725
|
const safeKey = String(key).replace(/:/g, '_');
|
|
@@ -691,7 +731,7 @@ export async function bridgeStoreEntry(options) {
|
|
|
691
731
|
success: true,
|
|
692
732
|
id,
|
|
693
733
|
embedding: embeddingJson ? { dimensions, model } : undefined,
|
|
694
|
-
rawEmbedding:
|
|
734
|
+
rawEmbedding: embeddingArr ?? undefined,
|
|
695
735
|
guarded: true,
|
|
696
736
|
cached: true,
|
|
697
737
|
attested: true,
|
|
@@ -746,12 +786,17 @@ export async function bridgeSearchEntries(options) {
|
|
|
746
786
|
catch {
|
|
747
787
|
return null;
|
|
748
788
|
}
|
|
749
|
-
// Phase 2: Compute BM25 term stats for the corpus
|
|
789
|
+
// Phase 2: Compute BM25 term stats for the corpus. Tokenize the corpus a
|
|
790
|
+
// single time and reuse the per-doc `{contentLower, words}` for term-freq,
|
|
791
|
+
// BM25 scoring, and the coverage floor below.
|
|
750
792
|
const queryTerms = queryStr.toLowerCase().split(/\s+/).filter(t => t.length > 1);
|
|
751
|
-
const
|
|
793
|
+
const docs = tokenizeCorpus(rows);
|
|
794
|
+
const { termDocFreqs, avgDocLength } = computeTermDocFreqs(queryTerms, docs);
|
|
752
795
|
const docCount = rows.length;
|
|
753
796
|
const results = [];
|
|
754
|
-
for (
|
|
797
|
+
for (let i = 0; i < rows.length; i++) {
|
|
798
|
+
const row = rows[i];
|
|
799
|
+
const doc = docs[i];
|
|
755
800
|
let semanticScore = 0;
|
|
756
801
|
let bm25ScoreVal = 0;
|
|
757
802
|
// Semantic scoring via cosine similarity
|
|
@@ -766,21 +811,37 @@ export async function bridgeSearchEntries(options) {
|
|
|
766
811
|
}
|
|
767
812
|
// Phase 2: BM25 keyword scoring (replaces String.includes fallback)
|
|
768
813
|
if (queryTerms.length > 0 && row.content) {
|
|
769
|
-
bm25ScoreVal = bm25Score(queryTerms,
|
|
814
|
+
bm25ScoreVal = bm25Score(queryTerms, doc.words, doc.words.length, avgDocLength, docCount, termDocFreqs);
|
|
770
815
|
// Normalize BM25 to 0-1 range (cap at 10 for normalization)
|
|
771
816
|
bm25ScoreVal = Math.min(bm25ScoreVal / 10, 1.0);
|
|
772
817
|
}
|
|
773
|
-
//
|
|
774
|
-
//
|
|
775
|
-
//
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
818
|
+
// #2558: keyword-coverage floor for the lexical signal.
|
|
819
|
+
// BM25's IDF collapses toward zero when a term appears in most/all
|
|
820
|
+
// documents (routine on small memory corpora), and the /10 normalization
|
|
821
|
+
// crushed exact-keyword hits well below the default 0.3 threshold — so
|
|
822
|
+
// `memory search` recalled NOTHING even when the content literally
|
|
823
|
+
// contained the query term (issue #2558: "keyword recall random"). The
|
|
824
|
+
// pre-BM25 fallback guaranteed keyword recall via matchCount/words*0.5;
|
|
825
|
+
// this restores that guarantee. `coverage` is the fraction of query
|
|
826
|
+
// terms present in the document — a full-coverage hit must always be
|
|
827
|
+
// recallable regardless of IDF.
|
|
828
|
+
const contentLower = doc.contentLower;
|
|
829
|
+
const matchedTerms = queryTerms.filter(t => contentLower.includes(t)).length;
|
|
830
|
+
const coverage = queryTerms.length > 0 ? matchedTerms / queryTerms.length : 0;
|
|
831
|
+
const lexicalScore = Math.max(bm25ScoreVal, coverage);
|
|
832
|
+
// Recall-friendly fusion: a strong semantic OR lexical signal alone must
|
|
833
|
+
// clear the threshold. `blended` (0.6 semantic + 0.4 lexical) drives
|
|
834
|
+
// ranking; taking max() with the raw semantic score means (a) a genuinely
|
|
835
|
+
// similar entry is never dropped just because it lacks the query's exact
|
|
836
|
+
// words, and (b) a full-coverage keyword hit (lexical=1 → blended≥0.4) is
|
|
837
|
+
// never dropped just because its embedding cosine is low or negative.
|
|
838
|
+
const blended = 0.6 * Math.max(0, semanticScore) + 0.4 * lexicalScore;
|
|
839
|
+
const score = Math.max(blended, semanticScore);
|
|
779
840
|
if (score >= threshold) {
|
|
780
841
|
// Phase 4: ExplainableRecall provenance
|
|
781
842
|
const provenance = queryEmbedding
|
|
782
|
-
? `semantic:${semanticScore.toFixed(3)}+
|
|
783
|
-
: `
|
|
843
|
+
? `semantic:${semanticScore.toFixed(3)}+lexical:${lexicalScore.toFixed(3)}`
|
|
844
|
+
: `lexical:${lexicalScore.toFixed(3)}`;
|
|
784
845
|
results.push({
|
|
785
846
|
id: String(row.id).substring(0, 12),
|
|
786
847
|
key: row.key || String(row.id).substring(0, 15),
|
|
@@ -203,6 +203,77 @@ export declare function checkAndMigrateLegacy(options: {
|
|
|
203
203
|
migrated?: boolean;
|
|
204
204
|
migratedCount?: number;
|
|
205
205
|
}>;
|
|
206
|
+
/**
|
|
207
|
+
* Self-heal an EXISTING memory database that is missing the `vector_indexes`
|
|
208
|
+
* table or per-namespace rows.
|
|
209
|
+
*
|
|
210
|
+
* Why this exists: fresh installs create `vector_indexes` + seed rows, but a
|
|
211
|
+
* DB written by an older CLI or by agentdb directly may have thousands of
|
|
212
|
+
* embedded rows in `memory_entries` and NO `vector_indexes` table at all.
|
|
213
|
+
* Two things break as a result:
|
|
214
|
+
* 1. The statusline's vector count read collapsed to `0` (the count query
|
|
215
|
+
* referenced the missing table and failed whole — now split, but the
|
|
216
|
+
* HNSW flag still needs the table).
|
|
217
|
+
* 2. #1941 — `memory_search` routes per namespace via `vector_indexes`; a
|
|
218
|
+
* namespace with no row returns 0 results even when entries exist.
|
|
219
|
+
*
|
|
220
|
+
* This is idempotent and conservative:
|
|
221
|
+
* - Does NOTHING (no writes) when the table already exists and every embedded
|
|
222
|
+
* namespace already has a row — the common already-healed path, hit on
|
|
223
|
+
* every MCP start, must not write to the live DB unnecessarily.
|
|
224
|
+
* - Before ANY write, runs `PRAGMA quick_check`; if the DB reports structural
|
|
225
|
+
* corruption it SKIPS the repair entirely (returns `corrupt:true`) rather
|
|
226
|
+
* than writing into a malformed btree and risking making it worse. The
|
|
227
|
+
* caller/user should recover via `sqlite3 old.db .recover | sqlite3 new.db`.
|
|
228
|
+
* - Does NOT checkpoint. mode=ro readers already see committed WAL frames, and
|
|
229
|
+
* forcing a checkpoint on a DB with a torn WAL could persist latent damage.
|
|
230
|
+
*
|
|
231
|
+
* When a repair IS needed and the DB is healthy: creates the table if absent,
|
|
232
|
+
* seeds the fresh-install default rows, and backfills an accurate
|
|
233
|
+
* `total_vectors` per namespace. Runs on the existing-DB path of
|
|
234
|
+
* `initializeMemoryDatabase` (MCP start / `memory init`) and from `ruflo init`.
|
|
235
|
+
*
|
|
236
|
+
* Uses better-sqlite3 (WAL-safe, native). If the native module is unavailable
|
|
237
|
+
* it is a silent no-op — the split statusline query already prevents the count
|
|
238
|
+
* from zeroing; only the HNSW flag and namespace routing stay degraded.
|
|
239
|
+
*/
|
|
240
|
+
/**
|
|
241
|
+
* Auto-recover a structurally-corrupt memory DB into a clean one, universally
|
|
242
|
+
* (better-sqlite3 only — no dependency on the external `sqlite3` CLI, which is
|
|
243
|
+
* absent on many npx hosts). Safe by construction:
|
|
244
|
+
* 1. Confirms corruption (quick_check) — no-op on a healthy DB.
|
|
245
|
+
* 2. Acquires an EXCLUSIVE lock (BEGIN IMMEDIATE). If another process is
|
|
246
|
+
* writing, it SKIPS (returns reason:'writer-active') rather than racing a
|
|
247
|
+
* writer and losing its in-flight writes — the mistake that must not recur.
|
|
248
|
+
* 3. Rebuilds a fresh DB table-by-table (schema + rows), skipping any single
|
|
249
|
+
* table whose pages won't scan so one bad table can't abort the whole
|
|
250
|
+
* rebuild.
|
|
251
|
+
* 4. VERIFIES the rebuild (integrity_check == ok AND recovered
|
|
252
|
+
* memory_entries count >= the readable source count) BEFORE touching the
|
|
253
|
+
* original.
|
|
254
|
+
* 5. Backs up the corrupt DB to `<db>.corrupt-<ts>.bak`, then atomically
|
|
255
|
+
* renames the verified rebuild into place and drops stale -wal/-shm.
|
|
256
|
+
* On any failure the original + backup are left intact — never destructive.
|
|
257
|
+
*/
|
|
258
|
+
export declare function recoverMemoryDatabase(dbPath: string, opts?: {
|
|
259
|
+
verbose?: boolean;
|
|
260
|
+
}): Promise<{
|
|
261
|
+
recovered: boolean;
|
|
262
|
+
backupPath?: string;
|
|
263
|
+
rows?: number;
|
|
264
|
+
reason?: string;
|
|
265
|
+
}>;
|
|
266
|
+
export declare function repairVectorIndexes(dbPath: string, opts?: {
|
|
267
|
+
verbose?: boolean;
|
|
268
|
+
autoRecover?: boolean;
|
|
269
|
+
}): Promise<{
|
|
270
|
+
repaired: boolean;
|
|
271
|
+
tableCreated: boolean;
|
|
272
|
+
namespaces: string[];
|
|
273
|
+
corrupt?: boolean;
|
|
274
|
+
recovered?: boolean;
|
|
275
|
+
backupPath?: string;
|
|
276
|
+
}>;
|
|
206
277
|
/**
|
|
207
278
|
* Initialize the memory database properly using sql.js
|
|
208
279
|
*/
|