@yanggao7/memlio 0.1.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/dist/store.js ADDED
@@ -0,0 +1,535 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { mkdirSync, readFileSync, existsSync, realpathSync, statSync, rmSync, copyFileSync, chmodSync } from 'node:fs';
3
+ import { join, resolve, relative, isAbsolute, basename, extname } from 'node:path';
4
+ import { createHash, randomUUID } from 'node:crypto';
5
+ import { loadConfig, atomicWrite } from './config.js';
6
+ import { fetchPage, extractPage } from './capture.js';
7
+ import { LocalEmbedder, cosine } from './embedding.js';
8
+ import { z } from 'zod';
9
+ const STOP = new Set('a an and are as at be been but by can did do for from had has have how i in is it its me my of on or our that the their them there these this those to was were what when where which who with would you your find saved remember about thing something'.split(' '));
10
+ const CHUNK_SIZE = 900;
11
+ const CHUNK_STEP = 750;
12
+ // The title/note/description prefix is repeated in every chunk. The model reads about 512 tokens,
13
+ // so the prefix is capped to leave room for the body text.
14
+ const CONTEXT_LIMIT = 300;
15
+ const FILE_LIMIT = 20 * 1024 * 1024;
16
+ const TEXT_EXTENSIONS = ['.txt', '.md', '.csv', '.json'];
17
+ export function terms(text) {
18
+ const words = text.toLowerCase().match(/[\p{L}\p{N}_]+/gu) ?? [];
19
+ return [...new Set(words.filter((t) => t.length > 1 && !STOP.has(t)))].slice(0, 40);
20
+ }
21
+ export function chunkText(text) {
22
+ const result = [];
23
+ for (let i = 0; i < text.length; i += CHUNK_STEP) {
24
+ result.push(text.slice(i, i + CHUNK_SIZE));
25
+ if (i + CHUNK_SIZE >= text.length)
26
+ break;
27
+ }
28
+ return result.length ? result : [''];
29
+ }
30
+ const hash = (value) => createHash('sha256').update(value).digest('hex');
31
+ const errorText = (error) => (error instanceof Error ? error.message : String(error));
32
+ export function within(path, root) {
33
+ const rel = relative(root, path);
34
+ return rel === '' || (!rel.startsWith('../') && rel !== '..' && !isAbsolute(rel));
35
+ }
36
+ function parseItem(record) {
37
+ const item = JSON.parse(record);
38
+ // Collections created before semantic search became mandatory may hold 'disabled'.
39
+ if (item.indexing === 'disabled')
40
+ item.indexing = 'pending';
41
+ return item;
42
+ }
43
+ export class Memory {
44
+ home;
45
+ db;
46
+ config;
47
+ embedder;
48
+ constructor(home, embedder) {
49
+ this.home = home;
50
+ mkdirSync(home, { recursive: true, mode: 0o700 });
51
+ mkdirSync(join(home, 'assets'), { recursive: true, mode: 0o700 });
52
+ this.config = loadConfig(home);
53
+ this.embedder = embedder ?? new LocalEmbedder(home);
54
+ this.db = new DatabaseSync(join(home, 'memory.sqlite'));
55
+ this.db.exec(`PRAGMA busy_timeout=15000; PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON; PRAGMA synchronous=FULL;
56
+ CREATE TABLE IF NOT EXISTS items (id TEXT PRIMARY KEY, hash TEXT UNIQUE NOT NULL, record TEXT NOT NULL);
57
+ CREATE TABLE IF NOT EXISTS chunks (id TEXT PRIMARY KEY, item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,
58
+ text TEXT NOT NULL, vector TEXT, model TEXT);
59
+ CREATE VIRTUAL TABLE IF NOT EXISTS search USING fts5(id UNINDEXED, item_id UNINDEXED, text, tokenize='porter unicode61');
60
+ CREATE TRIGGER IF NOT EXISTS chunks_deleted AFTER DELETE ON chunks BEGIN DELETE FROM search WHERE id=old.id; END;`);
61
+ chmodSync(join(home, 'memory.sqlite'), 0o600);
62
+ }
63
+ transaction(fn) {
64
+ this.db.exec('BEGIN IMMEDIATE');
65
+ try {
66
+ const value = fn();
67
+ this.db.exec('COMMIT');
68
+ return value;
69
+ }
70
+ catch (e) {
71
+ this.db.exec('ROLLBACK');
72
+ throw e;
73
+ }
74
+ }
75
+ get(id) {
76
+ const row = this.db.prepare('SELECT record FROM items WHERE id=?').get(id);
77
+ if (!row)
78
+ throw new Error(`Memory ${id} was not found.`);
79
+ return parseItem(row.record);
80
+ }
81
+ list() {
82
+ const rows = this.db.prepare('SELECT record FROM items ORDER BY rowid DESC').all();
83
+ return rows.map((r) => parseItem(r.record));
84
+ }
85
+ update(item) {
86
+ item.updated = new Date().toISOString();
87
+ this.db.prepare('UPDATE items SET record=? WHERE id=?').run(JSON.stringify(item), item.id);
88
+ }
89
+ /** Rebuild an item's chunks and keyword entries, keeping vectors whose chunk text did not change. */
90
+ buildChunks(item) {
91
+ const previous = new Map(this.db.prepare('SELECT id,text,vector,model FROM chunks WHERE item_id=?').all(item.id).map((c) => [c.id, c]));
92
+ this.db.prepare('DELETE FROM chunks WHERE item_id=?').run(item.id);
93
+ const context = [item.title, item.note, item.description, item.kind === 'url' ? item.original : '']
94
+ .filter(Boolean)
95
+ .join('\n')
96
+ .slice(0, CONTEXT_LIMIT);
97
+ const parts = chunkText(item.text);
98
+ let pending = 0;
99
+ for (const [n, part] of parts.entries()) {
100
+ const id = `${item.id}:${n}`;
101
+ const text = `${context}\n${part}`;
102
+ const old = previous.get(id);
103
+ const keep = old && old.text === text && old.vector && old.model === this.embedder.name;
104
+ if (!keep)
105
+ pending++;
106
+ this.db
107
+ .prepare('INSERT INTO chunks(id,item_id,text,vector,model) VALUES(?,?,?,?,?)')
108
+ .run(id, item.id, text, keep ? old.vector : null, keep ? old.model : null);
109
+ this.db.prepare('INSERT INTO search(id,item_id,text) VALUES(?,?,?)').run(id, item.id, text);
110
+ }
111
+ item.indexing = pending ? 'pending' : 'ready';
112
+ item.indexError = null;
113
+ }
114
+ async store(input, fileAccess = {}) {
115
+ if (!input.input.trim())
116
+ throw new Error('Provide non-empty text, a URL, or a file path.');
117
+ if (input.input.length > 1_000_000)
118
+ throw new Error('Input exceeds 1 million characters.');
119
+ if ((input.title?.length ?? 0) > 500 || (input.note?.length ?? 0) > 20_000 || (input.description?.length ?? 0) > 20_000) {
120
+ throw new Error('Title/context exceeds its size limit.');
121
+ }
122
+ if ((input.source?.length ?? 0) > 1000)
123
+ throw new Error('Source exceeds 1000 characters.');
124
+ const looksLikeFile = isAbsolute(input.input) || input.input.startsWith('./') || input.input.startsWith('../');
125
+ const kind = input.kind ?? (/^https?:\/\//i.test(input.input) ? 'url' : looksLikeFile ? 'file' : 'note');
126
+ let bytes;
127
+ let asset = null;
128
+ let text = kind === 'note' ? input.input : '';
129
+ let original = input.input;
130
+ if (kind === 'url') {
131
+ if (original.length > 8192)
132
+ throw new Error('URL exceeds 8192 characters.');
133
+ const url = new URL(original);
134
+ if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) {
135
+ throw new Error('Use an HTTP(S) URL without credentials.');
136
+ }
137
+ }
138
+ if (kind === 'file') {
139
+ const path = resolve(input.input);
140
+ if (!existsSync(path))
141
+ throw new Error(`File not found: ${path}`);
142
+ original = realpathSync(path);
143
+ const roots = [...this.config.allowedPaths, ...(fileAccess.roots ?? [])].filter(existsSync).map((p) => realpathSync(p));
144
+ if (!fileAccess.explicit && !roots.some((root) => within(original, root))) {
145
+ throw new Error(`${original} is outside the folders memlio may read. Allow it with: memlio setup <client> --allow-path <folder>`);
146
+ }
147
+ const stat = statSync(original);
148
+ if (!stat.isFile() || stat.size > FILE_LIMIT)
149
+ throw new Error('Capture requires a regular file no larger than 20 MB.');
150
+ bytes = readFileSync(original);
151
+ if (bytes.length > FILE_LIMIT)
152
+ throw new Error('File exceeds 20 MB.');
153
+ const extension = extname(original).toLowerCase();
154
+ if (TEXT_EXTENSIONS.includes(extension))
155
+ text = bytes.toString('utf8').slice(0, 1_000_000);
156
+ asset = `${hash(bytes)}${extension.replace(/[^.a-z0-9]/g, '').slice(0, 12)}`;
157
+ }
158
+ const now = new Date().toISOString();
159
+ const fingerprint = hash(JSON.stringify([kind, bytes ? hash(bytes) : original, input.note ?? '', input.description ?? '', input.title ?? '']));
160
+ const duplicate = this.db.prepare('SELECT id FROM items WHERE hash=?').get(fingerprint);
161
+ if (duplicate)
162
+ return { item: this.get(duplicate.id), duplicate: true };
163
+ const defaultTitle = kind === 'url' ? new URL(original).hostname : kind === 'file' ? basename(original) : original.split('\n')[0].slice(0, 100);
164
+ const item = {
165
+ id: 'm_' + randomUUID().replaceAll('-', ''),
166
+ kind,
167
+ title: input.title || defaultTitle,
168
+ original,
169
+ note: input.note ?? '',
170
+ description: input.description ?? '',
171
+ source: input.source ?? 'cli',
172
+ created: now,
173
+ updated: now,
174
+ text,
175
+ asset,
176
+ capture: kind === 'url' ? 'pending' : 'ready',
177
+ captureError: null,
178
+ indexing: 'pending',
179
+ indexError: null,
180
+ hash: fingerprint,
181
+ finalUrl: null,
182
+ capturedAt: kind === 'url' ? null : now,
183
+ };
184
+ const saved = this.transaction(() => {
185
+ const existing = this.db.prepare('SELECT id FROM items WHERE hash=?').get(fingerprint);
186
+ if (existing)
187
+ return { item: this.get(existing.id), duplicate: true };
188
+ if (asset && bytes && !existsSync(join(this.home, 'assets', asset)))
189
+ atomicWrite(join(this.home, 'assets', asset), bytes);
190
+ this.db.prepare('INSERT INTO items VALUES(?,?,?)').run(item.id, item.hash, JSON.stringify(item));
191
+ this.buildChunks(item);
192
+ this.update(item);
193
+ return { item, duplicate: false };
194
+ });
195
+ if (saved.duplicate || input.defer)
196
+ return saved;
197
+ await this.process(item.id, Boolean(input.title));
198
+ return { item: this.get(item.id), duplicate: false };
199
+ }
200
+ /** Finish one item: capture its page if needed, then embed its chunks. Failures are recorded on the item. */
201
+ async process(id, preserveTitle = false) {
202
+ await this.captureItem(id, preserveTitle);
203
+ await this.embedPending(id).catch(() => {
204
+ /* Recorded on the item as indexing: failed. */
205
+ });
206
+ }
207
+ async captureItem(id, preserveTitle) {
208
+ let item = this.get(id);
209
+ if (item.kind !== 'url' || item.capture === 'ready')
210
+ return;
211
+ try {
212
+ const page = await fetchPage(item.original);
213
+ const extracted = page.contentType.startsWith('text/plain')
214
+ ? { title: item.title, markdown: page.body }
215
+ : extractPage(page.body, page.url);
216
+ this.transaction(() => {
217
+ item = this.get(id);
218
+ item.text = extracted.markdown;
219
+ if (!preserveTitle && item.title === new URL(item.original).hostname)
220
+ item.title = extracted.title.slice(0, 500);
221
+ item.capture = 'ready';
222
+ item.captureError = null;
223
+ item.finalUrl = page.url;
224
+ item.capturedAt = new Date().toISOString();
225
+ this.buildChunks(item);
226
+ this.update(item);
227
+ });
228
+ }
229
+ catch (e) {
230
+ this.transaction(() => {
231
+ item = this.get(id);
232
+ item.capture = 'failed';
233
+ item.captureError = errorText(e);
234
+ this.update(item);
235
+ });
236
+ }
237
+ }
238
+ /** Embed every chunk that lacks a current vector, for one item or the whole collection. Throws if the model is unavailable. */
239
+ async embedPending(itemId) {
240
+ const rows = this.db
241
+ .prepare(`SELECT id,item_id,text FROM chunks WHERE (vector IS NULL OR model!=?)${itemId ? ' AND item_id=?' : ''}`)
242
+ .all(...(itemId ? [this.embedder.name, itemId] : [this.embedder.name]));
243
+ if (!rows.length)
244
+ return;
245
+ const affected = [...new Set(rows.map((r) => r.item_id))];
246
+ try {
247
+ const vectors = await this.embedder.embed(rows.map((r) => r.text));
248
+ if (vectors.length !== rows.length || vectors.some((v) => !v.length || v.some((n) => !Number.isFinite(n)))) {
249
+ throw new Error('Embedding provider returned invalid vectors.');
250
+ }
251
+ this.transaction(() => {
252
+ for (let i = 0; i < rows.length; i++) {
253
+ // Do not attach an old vector if another process changed this chunk meanwhile.
254
+ this.db
255
+ .prepare('UPDATE chunks SET vector=?,model=? WHERE id=? AND text=?')
256
+ .run(JSON.stringify(vectors[i]), this.embedder.name, rows[i].id, rows[i].text);
257
+ }
258
+ for (const id of affected)
259
+ this.refreshIndexing(id, null);
260
+ });
261
+ }
262
+ catch (e) {
263
+ this.transaction(() => {
264
+ for (const id of affected)
265
+ this.refreshIndexing(id, errorText(e));
266
+ });
267
+ throw e;
268
+ }
269
+ }
270
+ refreshIndexing(id, error) {
271
+ if (!this.db.prepare('SELECT id FROM items WHERE id=?').get(id))
272
+ return;
273
+ const item = this.get(id);
274
+ const pending = this.db
275
+ .prepare('SELECT count(*) AS n FROM chunks WHERE item_id=? AND (vector IS NULL OR model!=?)')
276
+ .get(id, this.embedder.name);
277
+ item.indexing = error ? 'failed' : pending.n ? 'pending' : 'ready';
278
+ item.indexError = error;
279
+ this.update(item);
280
+ }
281
+ async search(query, options = {}) {
282
+ if (!query.trim() || query.length > 10_000)
283
+ throw new Error('Search query must contain 1–10000 characters.');
284
+ const limit = options.limit ?? 5;
285
+ if (!Number.isInteger(limit) || limit < 1 || limit > 50)
286
+ throw new Error('Limit must be an integer from 1 to 50.');
287
+ for (const date of [options.after, options.before]) {
288
+ if (date && !/^\d{4}-\d{2}-\d{2}$/.test(date))
289
+ throw new Error('Dates must use YYYY-MM-DD.');
290
+ }
291
+ const eligible = new Map(this.list()
292
+ .filter((i) => (!options.kind || i.kind === options.kind) &&
293
+ (!options.after || i.created.slice(0, 10) >= options.after) &&
294
+ (!options.before || i.created.slice(0, 10) <= options.before))
295
+ .map((i) => [i.id, i]));
296
+ const candidates = new Map();
297
+ const add = (id, score, excerpt, keyword, similarity) => {
298
+ if (!eligible.has(id))
299
+ return;
300
+ const current = candidates.get(id);
301
+ if (current) {
302
+ current.score += score;
303
+ current.keyword ||= keyword;
304
+ if (similarity !== null)
305
+ current.similarity = Math.max(current.similarity ?? -1, similarity);
306
+ }
307
+ else
308
+ candidates.set(id, { id, score, excerpt: excerpt.slice(0, 600), keyword, similarity });
309
+ };
310
+ // Keyword branch: FTS5 with BM25, best chunk per item.
311
+ const tokens = terms(query);
312
+ if (tokens.length) {
313
+ const rows = this.db
314
+ .prepare('SELECT item_id,text,bm25(search) AS rank FROM search WHERE search MATCH ? ORDER BY rank')
315
+ .all(tokens.map((t) => `"${t}"`).join(' OR '));
316
+ const seen = new Set();
317
+ let rank = 0;
318
+ for (const row of rows) {
319
+ if (!eligible.has(row.item_id) || seen.has(row.item_id))
320
+ continue;
321
+ seen.add(row.item_id);
322
+ add(row.item_id, 1 / (60 + ++rank), row.text, true, null);
323
+ }
324
+ }
325
+ // Semantic branch: embed anything still pending, then the query. Falls back to keyword-only if the model is unavailable.
326
+ const warnings = [];
327
+ let mode = 'keyword';
328
+ const hasChunks = this.db.prepare('SELECT count(*) AS n FROM chunks').get().n > 0;
329
+ if (hasChunks) {
330
+ try {
331
+ await this.embedPending();
332
+ const [vector] = await this.embedder.embed([query]);
333
+ mode = 'hybrid';
334
+ const indexed = this.db
335
+ .prepare('SELECT item_id,text,vector FROM chunks WHERE vector IS NOT NULL AND model=?')
336
+ .all(this.embedder.name);
337
+ const rows = indexed
338
+ .filter((r) => eligible.has(r.item_id))
339
+ .map((r) => ({ ...r, similarity: cosine(vector, JSON.parse(r.vector)) }))
340
+ .filter((r) => r.similarity >= (options.minSimilarity ?? 0.25))
341
+ .sort((a, b) => b.similarity - a.similarity);
342
+ const seen = new Set();
343
+ let rank = 0;
344
+ for (const row of rows) {
345
+ if (seen.has(row.item_id))
346
+ continue;
347
+ seen.add(row.item_id);
348
+ add(row.item_id, 1 / (60 + ++rank), row.text, false, row.similarity);
349
+ }
350
+ }
351
+ catch (e) {
352
+ warnings.push(`Semantic search unavailable: ${errorText(e)} Showing keyword matches only.`);
353
+ }
354
+ }
355
+ const results = [...candidates.values()]
356
+ .sort((a, b) => b.score - a.score)
357
+ .slice(0, limit)
358
+ .map((r) => {
359
+ const item = eligible.get(r.id);
360
+ return {
361
+ ...r,
362
+ title: item.title,
363
+ kind: item.kind,
364
+ created: item.created,
365
+ source: item.source,
366
+ url: item.kind === 'url' ? item.original : null,
367
+ assetPath: item.asset ? join(this.home, 'assets', item.asset) : null,
368
+ reason: [
369
+ r.keyword ? 'Keyword match in saved content or context' : '',
370
+ r.similarity !== null ? `Semantic similarity ${r.similarity.toFixed(3)}` : '',
371
+ ]
372
+ .filter(Boolean)
373
+ .join('; '),
374
+ };
375
+ });
376
+ return { query, mode, warnings, results };
377
+ }
378
+ status() {
379
+ const items = this.list();
380
+ const failures = items
381
+ .filter((i) => i.captureError || i.indexError)
382
+ .map((i) => ({ id: i.id, title: i.title, captureError: i.captureError, indexError: i.indexError }));
383
+ const capturePending = items.filter((i) => i.capture === 'pending').length;
384
+ const indexPending = items.filter((i) => i.indexing === 'pending').length;
385
+ return {
386
+ home: this.home,
387
+ count: items.length,
388
+ model: this.embedder.name,
389
+ capturePending,
390
+ captureFailed: items.filter((i) => i.capture === 'failed').length,
391
+ indexPending,
392
+ indexFailed: items.filter((i) => i.indexing === 'failed').length,
393
+ failures,
394
+ next: failures.length || capturePending || indexPending ? 'Run memlio repair to retry unfinished work.' : null,
395
+ };
396
+ }
397
+ delete(id) {
398
+ const item = this.get(id);
399
+ this.transaction(() => {
400
+ this.db.prepare('DELETE FROM items WHERE id=?').run(id);
401
+ });
402
+ // Metadata deletion is committed first; a cleanup failure must not resurrect an item with missing bytes.
403
+ let cleanupError = null;
404
+ try {
405
+ if (item.asset && !this.list().some((i) => i.asset === item.asset))
406
+ rmSync(join(this.home, 'assets', item.asset), { force: true });
407
+ }
408
+ catch (e) {
409
+ cleanupError = errorText(e);
410
+ }
411
+ return { deleted: id, cleanupError };
412
+ }
413
+ /** Rebuild search data from the stored records, retry failed page captures, and embed anything missing. */
414
+ async repair() {
415
+ this.transaction(() => {
416
+ this.db.exec('DELETE FROM search');
417
+ for (const item of this.list()) {
418
+ this.buildChunks(item);
419
+ this.update(item);
420
+ }
421
+ });
422
+ for (const item of this.list())
423
+ if (item.capture !== 'ready')
424
+ await this.captureItem(item.id, false);
425
+ await this.embedPending().catch(() => {
426
+ /* Recorded on the items as indexing: failed. */
427
+ });
428
+ return this.status();
429
+ }
430
+ export(destination) {
431
+ const target = resolve(destination);
432
+ if (within(target, this.home) || within(this.home, target))
433
+ throw new Error('Export destination must be separate from the collection.');
434
+ if (existsSync(target))
435
+ throw new Error('Export destination already exists; choose a new directory.');
436
+ return this.transaction(() => {
437
+ mkdirSync(join(target, 'assets'), { recursive: true, mode: 0o700 });
438
+ const items = this.list();
439
+ atomicWrite(join(target, 'records.json'), JSON.stringify({ version: 1, items }, null, 2));
440
+ for (const asset of new Set(items.map((i) => i.asset).filter((v) => Boolean(v)))) {
441
+ copyFileSync(join(this.home, 'assets', asset), join(target, 'assets', asset));
442
+ }
443
+ return { destination: target, count: items.length };
444
+ });
445
+ }
446
+ async import(directory) {
447
+ const root = realpathSync(directory);
448
+ const recordSchema = z.object({
449
+ id: z.string().regex(/^m_[a-f0-9]{32}$/),
450
+ kind: z.enum(['note', 'url', 'file']),
451
+ title: z.string().max(500),
452
+ original: z.string().max(1_000_000),
453
+ note: z.string().max(20_000),
454
+ description: z.string().max(20_000),
455
+ source: z.string().max(1000),
456
+ created: z.string().datetime(),
457
+ updated: z.string().datetime(),
458
+ text: z.string().max(5_000_000),
459
+ asset: z
460
+ .string()
461
+ .regex(/^[a-f0-9]{64}(\.[a-z0-9]{1,11})?$/)
462
+ .nullable(),
463
+ capture: z.enum(['pending', 'ready', 'failed']),
464
+ captureError: z.string().nullable(),
465
+ indexing: z.enum(['pending', 'ready', 'disabled', 'failed']),
466
+ indexError: z.string().nullable(),
467
+ hash: z.string().regex(/^[a-f0-9]{64}$/),
468
+ finalUrl: z.string().nullable(),
469
+ capturedAt: z.string().datetime().nullable(),
470
+ });
471
+ const manifest = join(root, 'records.json');
472
+ if (!existsSync(manifest))
473
+ throw new Error(`No records.json in ${root}. Point import at a directory created by memlio export.`);
474
+ if (statSync(manifest).size > 100 * 1024 * 1024)
475
+ throw new Error('Import manifest exceeds 100 MB.');
476
+ const { items } = z
477
+ .object({ version: z.literal(1), items: z.array(recordSchema).max(100_000) })
478
+ .parse(JSON.parse(readFileSync(manifest, 'utf8')));
479
+ const assets = new Map();
480
+ for (const item of items) {
481
+ if (!item.asset || assets.has(item.asset))
482
+ continue;
483
+ const path = realpathSync(join(root, 'assets', item.asset));
484
+ if (!within(path, root) || statSync(path).size > FILE_LIMIT)
485
+ throw new Error('Invalid import asset path or size.');
486
+ const bytes = readFileSync(path);
487
+ if (!item.asset.startsWith(hash(bytes)))
488
+ throw new Error('Import asset content hash mismatch.');
489
+ assets.set(item.asset, bytes);
490
+ }
491
+ const result = this.transaction(() => {
492
+ let imported = 0;
493
+ let duplicates = 0;
494
+ for (const record of items) {
495
+ if (this.db.prepare('SELECT id FROM items WHERE hash=?').get(record.hash)) {
496
+ duplicates++;
497
+ continue;
498
+ }
499
+ if (this.db.prepare('SELECT id FROM items WHERE id=?').get(record.id))
500
+ throw new Error(`Import ID collision: ${record.id}`);
501
+ if (record.asset && !existsSync(join(this.home, 'assets', record.asset))) {
502
+ atomicWrite(join(this.home, 'assets', record.asset), assets.get(record.asset));
503
+ }
504
+ const item = { ...record, indexing: 'pending', indexError: null };
505
+ this.db.prepare('INSERT INTO items VALUES(?,?,?)').run(item.id, item.hash, JSON.stringify(item));
506
+ this.buildChunks(item);
507
+ this.update(item);
508
+ imported++;
509
+ }
510
+ return { imported, duplicates };
511
+ });
512
+ await this.embedPending().catch(() => {
513
+ /* Recorded on the items; status() points at repair. */
514
+ });
515
+ return { ...result, ...this.status() };
516
+ }
517
+ async close() {
518
+ this.db.close();
519
+ await this.embedder.dispose();
520
+ }
521
+ }
522
+ /** The short save receipt shared by the CLI and the MCP tool. */
523
+ export function summarize(saved) {
524
+ const { item, duplicate } = saved;
525
+ return {
526
+ id: item.id,
527
+ title: item.title,
528
+ duplicate,
529
+ capture: item.capture,
530
+ captureError: item.captureError,
531
+ indexing: item.indexing,
532
+ indexError: item.indexError,
533
+ };
534
+ }
535
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACvH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAiB,aAAa,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAuDxB,MAAM,IAAI,GAAG,IAAI,GAAG,CAClB,yPAAyP,CAAC,KAAK,CAC7P,GAAG,CACJ,CACF,CAAC;AACF,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,kGAAkG;AAClG,2DAA2D;AAC3D,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACpC,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAEzD,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC;IACjE,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtF,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,GAAG,UAAU,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM;IAC3C,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,IAAI,GAAG,CAAC,KAAsB,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1F,MAAM,SAAS,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAE/F,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,IAAY;IAC/C,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjC,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AACpF,CAAC;AAED,SAAS,SAAS,CAAC,MAAc;IAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAyE,CAAC;IACxG,mFAAmF;IACnF,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU;QAAE,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC5D,OAAO,IAAY,CAAC;AACtB,CAAC;AAED,MAAM,OAAO,MAAM;IAMN;IALF,EAAE,CAAe;IACjB,MAAM,CAAC;IACP,QAAQ,CAAW;IAE5B,YACW,IAAY,EACrB,QAAmB;QADV,SAAI,GAAJ,IAAI,CAAQ;QAGrB,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAClD,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,EAAE,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;wHAKuG,CAAC,CAAC;QACtH,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,KAAK,CAAC,CAAC;IAChD,CAAC;IAED,WAAW,CAAI,EAAW;QACxB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,EAAE,EAAE,CAAC;YACnB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvB,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACzB,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAmC,CAAC;QAC7G,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;QACzD,OAAO,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,8CAA8C,CAAC,CAAC,GAAG,EAA0B,CAAC;QAC3G,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,CAAC;IAEO,MAAM,CAAC,IAAU;QACvB,IAAI,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,qGAAqG;IAC7F,WAAW,CAAC,IAAU;QAC5B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAEpB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,yDAAyD,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAMvF,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CACxB,CAAC;QACF,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;aAChG,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,IAAI,CAAC;aACV,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;QAC3B,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YACxC,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,GAAG,OAAO,KAAK,IAAI,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC7B,MAAM,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YACxF,IAAI,CAAC,IAAI;gBAAE,OAAO,EAAE,CAAC;YACrB,IAAI,CAAC,EAAE;iBACJ,OAAO,CAAC,oEAAoE,CAAC;iBAC7E,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC7E,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC9F,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;QAC9C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAiB,EAAE,aAAuD,EAAE;QACtF,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QAC3F,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QAC3F,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC;YACxH,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAC3F,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC/G,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACzG,IAAI,KAAyB,CAAC;QAC9B,IAAI,KAAK,GAAkB,IAAI,CAAC;QAChC,IAAI,IAAI,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,IAAI,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC;QAC3B,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACnB,IAAI,QAAQ,CAAC,MAAM,GAAG,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;YAC5E,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC9B,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;gBAChF,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;QACD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;YAClE,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;YACxH,IAAI,CAAC,UAAU,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC1E,MAAM,IAAI,KAAK,CACb,GAAG,QAAQ,qGAAqG,CACjH,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,GAAG,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;YACvH,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAC/B,IAAI,KAAK,CAAC,MAAM,GAAG,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACtE,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;YAClD,IAAI,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAAE,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;YAC3F,KAAK,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QAC/E,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,WAAW,GAAG,IAAI,CACtB,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,EAAE,KAAK,CAAC,WAAW,IAAI,EAAE,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CACrH,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC,GAAG,CAAC,WAAW,CAA+B,CAAC;QACtH,IAAI,SAAS;YAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACxE,MAAM,YAAY,GAChB,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC7H,MAAM,IAAI,GAAS;YACjB,EAAE,EAAE,IAAI,GAAG,UAAU,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC;YAC3C,IAAI;YACJ,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,YAAY;YAClC,QAAQ;YACR,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;YACtB,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,EAAE;YACpC,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK;YAC7B,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,GAAG;YACZ,IAAI;YACJ,KAAK;YACL,OAAO,EAAE,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;YAC7C,YAAY,EAAE,IAAI;YAClB,QAAQ,EAAE,SAAS;YACnB,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,WAAW;YACjB,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG;SACxC,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;YAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC,GAAG,CAAC,WAAW,CAA+B,CAAC;YACrH,IAAI,QAAQ;gBAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;YACtE,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;gBAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;YAC1H,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YACjG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAClB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACpC,CAAC,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACjD,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAClD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACvD,CAAC;IAED,6GAA6G;IAC7G,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,aAAa,GAAG,KAAK;QAC7C,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;QAC1C,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACrC,+CAA+C;QACjD,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,aAAsB;QAC1D,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxB,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO;YAAE,OAAO;QAC5D,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,YAAY,CAAC;gBACzD,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE;gBAC5C,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YACrC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;gBACpB,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACpB,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC;gBAC/B,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAQ;oBAAE,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;gBACjH,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;gBACvB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;gBACzB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC;gBACzB,IAAI,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC3C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;gBACpB,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACpB,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC;gBACxB,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;gBACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,+HAA+H;IACvH,KAAK,CAAC,YAAY,CAAC,MAAe;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,OAAO,CAAC,wEAAwE,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;aACjH,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAoD,CAAC;QAC7H,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YACnE,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3G,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAClE,CAAC;YACD,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;gBACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACrC,+EAA+E;oBAC/E,IAAI,CAAC,EAAE;yBACJ,OAAO,CAAC,0DAA0D,CAAC;yBACnE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACnF,CAAC;gBACD,KAAK,MAAM,EAAE,IAAI,QAAQ;oBAAE,IAAI,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAC5D,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;gBACpB,KAAK,MAAM,EAAE,IAAI,QAAQ;oBAAE,IAAI,CAAC,eAAe,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YACpE,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,EAAU,EAAE,KAAoB;QACtD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO;QACxE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE;aACpB,OAAO,CAAC,mFAAmF,CAAC;aAC5F,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAkB,CAAC;QAChD,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;QACnE,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,UAAyB,EAAE;QACrD,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAC7G,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QACnH,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnD,IAAI,IAAI,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAC/F,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,IAAI,CAAC,IAAI,EAAE;aACR,MAAM,CACL,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC;YAC1C,CAAC,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC;YAC3D,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAChE;aACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CACzB,CAAC;QAEF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAqB,CAAC;QAChD,MAAM,GAAG,GAAG,CAAC,EAAU,EAAE,KAAa,EAAE,OAAe,EAAE,OAAgB,EAAE,UAAyB,EAAE,EAAE;YACtG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO;YAC9B,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnC,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC;gBACvB,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC;gBAC5B,IAAI,UAAU,KAAK,IAAI;oBAAE,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;YAC/F,CAAC;;gBAAM,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;QAChG,CAAC,CAAC;QAEF,uDAAuD;QACvD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;iBACjB,OAAO,CAAC,yFAAyF,CAAC;iBAClG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAsD,CAAC;YACtG,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;oBAAE,SAAS;gBAClE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACtB,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAED,yHAAyH;QACzH,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,IAAI,IAAI,GAAyB,SAAS,CAAC;QAC3C,MAAM,SAAS,GAAI,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC,GAAG,EAAoB,CAAC,CAAC,GAAG,CAAC,CAAC;QACrG,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;gBAC1B,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;gBACpD,IAAI,GAAG,QAAQ,CAAC;gBAChB,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE;qBACpB,OAAO,CAAC,6EAA6E,CAAC;qBACtF,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAwD,CAAC;gBAClF,MAAM,IAAI,GAAG,OAAO;qBACjB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;qBACtC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;qBACxE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,CAAC;qBAC9D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;gBAC/C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;gBAC/B,IAAI,IAAI,GAAG,CAAC,CAAC;gBACb,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;oBACvB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;wBAAE,SAAS;oBACpC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBACtB,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBACvE,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,QAAQ,CAAC,IAAI,CAAC,gCAAgC,SAAS,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC;YAC9F,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAmB,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;aACrD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;aACjC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;aACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACT,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAE,CAAC;YACjC,OAAO;gBACL,GAAG,CAAC;gBACJ,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,GAAG,EAAE,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;gBAC/C,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI;gBACpE,MAAM,EAAE;oBACN,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,2CAA2C,CAAC,CAAC,CAAC,EAAE;oBAC5D,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;iBAC9E;qBACE,MAAM,CAAC,OAAO,CAAC;qBACf,IAAI,CAAC,IAAI,CAAC;aACd,CAAC;QACJ,CAAC,CAAC,CAAC;QACL,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAC5C,CAAC;IAED,MAAM;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,KAAK;aACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,UAAU,CAAC;aAC7C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QACtG,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;QAC3E,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;QAC1E,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;YACzB,cAAc;YACd,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,MAAM;YACjE,YAAY;YACZ,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM;YAChE,QAAQ;YACR,IAAI,EAAE,QAAQ,CAAC,MAAM,IAAI,cAAc,IAAI,YAAY,CAAC,CAAC,CAAC,6CAA6C,CAAC,CAAC,CAAC,IAAI;SAC/G,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,EAAU;QACf,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1B,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;YACpB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QACH,yGAAyG;QACzG,IAAI,YAAY,GAAkB,IAAI,CAAC;QACvC,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC;gBAChE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC;IACvC,CAAC;IAED,2GAA2G;IAC3G,KAAK,CAAC,MAAM;QACV,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;YACpB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YACnC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC/B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE;YAAE,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO;gBAAE,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACrG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;YACnC,gDAAgD;QAClD,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,WAAmB;QACxB,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QACpC,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;YACxD,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC9E,IAAI,UAAU,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;QACtG,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;YAC3B,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACpE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC1B,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1F,KAAK,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9F,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;YAChF,CAAC;YACD,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QACtD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAAiB;QAC5B,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;YAC5B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC;YACxC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;YACrC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;YAC1B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC;YACnC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;YAC5B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;YACnC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;YAC5B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC;YAC/B,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,KAAK,CAAC,mCAAmC,CAAC;iBAC1C,QAAQ,EAAE;YACb,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC/C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YACnC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;YAC5D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC;YACxC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC/B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;SAC7C,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAC5C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,yDAAyD,CAAC,CAAC;QACvG,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACpG,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC;aAChB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;aAC5E,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;gBAAE,SAAS;YACpD,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YAC5D,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACnH,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;YAChG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;YACnC,IAAI,QAAQ,GAAG,CAAC,CAAC;YACjB,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;gBAC3B,IAAI,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC1E,UAAU,EAAE,CAAC;oBACb,SAAS;gBACX,CAAC;gBACD,IAAI,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;oBACnE,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvD,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;oBACzE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAE,CAAC,CAAC;gBAClF,CAAC;gBACD,MAAM,IAAI,GAAS,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;gBACxE,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;gBACjG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAClB,QAAQ,EAAE,CAAC;YACb,CAAC;YACD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;QAClC,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;YACnC,uDAAuD;QACzD,CAAC,CAAC,CAAC;QACH,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAChC,CAAC;CACF;AAED,iEAAiE;AACjE,MAAM,UAAU,SAAS,CAAC,KAAyC;IACjE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAClC,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,SAAS;QACT,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,IAAI,CAAC,UAAU;KAC5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,33 @@
1
+ # Architecture decisions
2
+
3
+ For an end-to-end trace of the commands see [WORKFLOWS](WORKFLOWS.md).
4
+
5
+ ## One local core, two front ends
6
+
7
+ The CLI and the stdio MCP server share one `Memory` class. The skills teach the host agent how to call the five MCP tools and how to inspect evidence; they hold no logic of their own. Nothing runs in the background: no daemon, no container, no external service, no API key.
8
+
9
+ ## SQLite is the record store
10
+
11
+ Records are JSON rows in SQLite. WAL mode, a busy timeout, `BEGIN IMMEDIATE` transactions, and full synchronous commits let separate CLI processes and a long-running MCP server write to the same file. Original file bytes are copied into content-addressed files under `assets/`, written atomically and fsynced. The chunk, keyword, and vector tables are derived and can be rebuilt from the records with `repair`. `export` produces the portable form: `records.json` plus the asset copies.
12
+
13
+ ## Small local search instead of a search engine
14
+
15
+ FTS5 plus a 23 MB quantized MiniLM model covers keyword and paraphrase recall for a personal collection without a vector database, a reranker, or a multi-gigabyte model download. The `Embedder` interface isolates the model so a different local or remote provider can be substituted; the ranking code itself is not pluggable.
16
+
17
+ Semantic search is always on. If the model cannot be loaded, search runs on keywords and says so in a warning, and pending embeddings are completed by the next search that has the model. This replaces an earlier explicit keyword-only mode: one fewer setting, one fewer command, and no client restart after changing it.
18
+
19
+ ## Originals before enrichment
20
+
21
+ A save commits the record and keyword index first, then fetches the page and computes embeddings. Both steps write their failure onto the record. `store` therefore never loses the thing you asked to keep, and `status` and `repair` deal with the rest.
22
+
23
+ ## URL capture is deliberately narrow
24
+
25
+ Public HTTP(S) only, DNS resolved and checked per redirect and pinned to the socket, standard ports, size and time limits, no script execution, no browser impersonation. Pages that reject non-browser clients are saved as bookmarks with a clear error. A headless browser would capture more and cost far more in dependencies and surface area.
26
+
27
+ ## Agent integration by configuration, not by plugin
28
+
29
+ `setup` writes the MCP server entry and the skill file into each client's user-level configuration, keeps unrelated settings, keeps one backup, and refuses to overwrite entries it did not create. The file capture boundary for MCP is a list of allowed folders plus the client's reported roots, enforced in the server, not in the prompt.
30
+
31
+ ## Not yet
32
+
33
+ npm publication, a fresh-install CI matrix, Linux validation, OCR and PDF extraction, browser-assisted capture, and an evaluation on a realistic personal corpus with hard distractors.