mor 0.0.2 → 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/README.md +160 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +587 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +56 -0
- package/dist/config.js.map +1 -0
- package/dist/db.d.ts +37 -0
- package/dist/db.js +134 -0
- package/dist/db.js.map +1 -0
- package/dist/embeddings/none.d.ts +6 -0
- package/dist/embeddings/none.js +8 -0
- package/dist/embeddings/none.js.map +1 -0
- package/dist/embeddings/ollama.d.ts +9 -0
- package/dist/embeddings/ollama.js +28 -0
- package/dist/embeddings/ollama.js.map +1 -0
- package/dist/embeddings/openai.d.ts +10 -0
- package/dist/embeddings/openai.js +33 -0
- package/dist/embeddings/openai.js.map +1 -0
- package/dist/embeddings/provider.d.ts +7 -0
- package/dist/embeddings/provider.js +15 -0
- package/dist/embeddings/provider.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +160 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +188 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory.d.ts +20 -0
- package/dist/memory.js +124 -0
- package/dist/memory.js.map +1 -0
- package/dist/operations.d.ts +61 -0
- package/dist/operations.js +122 -0
- package/dist/operations.js.map +1 -0
- package/dist/query.d.ts +3 -0
- package/dist/query.js +44 -0
- package/dist/query.js.map +1 -0
- package/dist/remote.d.ts +35 -0
- package/dist/remote.js +77 -0
- package/dist/remote.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +241 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +35 -17
- package/.dir-locals.el +0 -6
- package/.editorconfig +0 -15
- package/.eslintrc +0 -26
- package/.npmignore +0 -32
- package/LICENSE +0 -21
- package/alg/dfs.js +0 -36
- package/alg/revDfs.js +0 -36
- package/cli.js +0 -106
- package/mor-core.js +0 -60
- package/mor-link.js +0 -23
- package/mor-outdated.js +0 -26
package/dist/config.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
function expandHome(p) {
|
|
4
|
+
if (p.startsWith('~/') || p === '~') {
|
|
5
|
+
return path.join(process.env.HOME ?? '', p.slice(1));
|
|
6
|
+
}
|
|
7
|
+
return p;
|
|
8
|
+
}
|
|
9
|
+
function getConfigDir() {
|
|
10
|
+
if (process.env.MOR_HOME) {
|
|
11
|
+
return expandHome(process.env.MOR_HOME);
|
|
12
|
+
}
|
|
13
|
+
return path.join(process.env.HOME ?? '', '.config', 'mor');
|
|
14
|
+
}
|
|
15
|
+
const DEFAULT_CONFIG = {
|
|
16
|
+
memoryDir: '~/.config/mor/memories',
|
|
17
|
+
dbPath: '~/.config/mor/index.db',
|
|
18
|
+
embedding: {
|
|
19
|
+
provider: 'none',
|
|
20
|
+
model: 'text-embedding-3-small',
|
|
21
|
+
baseUrl: 'https://api.openai.com/v1',
|
|
22
|
+
dimensions: 1536,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
export function loadConfig() {
|
|
26
|
+
const configDir = getConfigDir();
|
|
27
|
+
const configPath = path.join(configDir, 'config.json');
|
|
28
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
29
|
+
let config;
|
|
30
|
+
if (fs.existsSync(configPath)) {
|
|
31
|
+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
32
|
+
config = {
|
|
33
|
+
...DEFAULT_CONFIG,
|
|
34
|
+
...raw,
|
|
35
|
+
embedding: { ...DEFAULT_CONFIG.embedding, ...raw.embedding },
|
|
36
|
+
...(raw.server ? { server: raw.server } : {}),
|
|
37
|
+
...(raw.serve ? { serve: raw.serve } : {}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
config = { ...DEFAULT_CONFIG };
|
|
42
|
+
config.memoryDir = path.join(configDir, 'memories');
|
|
43
|
+
config.dbPath = path.join(configDir, 'index.db');
|
|
44
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
45
|
+
}
|
|
46
|
+
// Resolve paths relative to configDir when they use ~
|
|
47
|
+
config.memoryDir = expandHome(config.memoryDir);
|
|
48
|
+
config.dbPath = expandHome(config.dbPath);
|
|
49
|
+
// Ensure memory directory exists
|
|
50
|
+
fs.mkdirSync(config.memoryDir, { recursive: true });
|
|
51
|
+
return config;
|
|
52
|
+
}
|
|
53
|
+
export function isRemote(config) {
|
|
54
|
+
return !!config.server?.url;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import type { Config } from './types.js';
|
|
3
|
+
export type DB = Database.Database;
|
|
4
|
+
export declare function openDb(config: Config): DB;
|
|
5
|
+
export declare function upsertMemoryChecked(db: DB, mem: {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
tags: string[];
|
|
9
|
+
type: string;
|
|
10
|
+
repository?: string;
|
|
11
|
+
created: string;
|
|
12
|
+
updated: string;
|
|
13
|
+
content: string;
|
|
14
|
+
filePath: string;
|
|
15
|
+
contentHash: string;
|
|
16
|
+
}): void;
|
|
17
|
+
export declare function deleteMemoryFromDb(db: DB, id: string): void;
|
|
18
|
+
export declare function searchFts(db: DB, query: string, limit?: number): Array<{
|
|
19
|
+
id: string;
|
|
20
|
+
score: number;
|
|
21
|
+
}>;
|
|
22
|
+
export declare function getMemoryById(db: DB, id: string): {
|
|
23
|
+
file_path: string;
|
|
24
|
+
} | undefined;
|
|
25
|
+
export declare function getMemoryByPrefix(db: DB, prefix: string): {
|
|
26
|
+
file_path: string;
|
|
27
|
+
id: string;
|
|
28
|
+
} | undefined;
|
|
29
|
+
export declare function getMemoryByFilename(db: DB, filename: string): {
|
|
30
|
+
file_path: string;
|
|
31
|
+
} | undefined;
|
|
32
|
+
export declare function getAllMemoryIds(db: DB): Set<string>;
|
|
33
|
+
export declare function getContentHash(db: DB, id: string): string | undefined;
|
|
34
|
+
export declare function grepMemories(db: DB, pattern: string, limit?: number, ignoreCase?: boolean): Array<{
|
|
35
|
+
id: string;
|
|
36
|
+
}>;
|
|
37
|
+
export declare function clearDb(db: DB): void;
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
const SCHEMA = `
|
|
3
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
4
|
+
id TEXT PRIMARY KEY,
|
|
5
|
+
title TEXT NOT NULL,
|
|
6
|
+
tags TEXT NOT NULL DEFAULT '',
|
|
7
|
+
type TEXT NOT NULL DEFAULT 'knowledge',
|
|
8
|
+
repository TEXT,
|
|
9
|
+
created TEXT NOT NULL,
|
|
10
|
+
updated TEXT NOT NULL,
|
|
11
|
+
content TEXT NOT NULL,
|
|
12
|
+
file_path TEXT NOT NULL UNIQUE,
|
|
13
|
+
content_hash TEXT NOT NULL
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
17
|
+
title, tags, content, content='', content_rowid='rowid',
|
|
18
|
+
tokenize='porter unicode61'
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
CREATE TABLE IF NOT EXISTS embeddings (
|
|
22
|
+
id TEXT PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
|
|
23
|
+
embedding BLOB NOT NULL,
|
|
24
|
+
model TEXT NOT NULL,
|
|
25
|
+
dimensions INTEGER NOT NULL
|
|
26
|
+
);
|
|
27
|
+
`;
|
|
28
|
+
export function openDb(config) {
|
|
29
|
+
const db = new Database(config.dbPath);
|
|
30
|
+
db.pragma('journal_mode = WAL');
|
|
31
|
+
db.pragma('foreign_keys = ON');
|
|
32
|
+
db.exec(SCHEMA);
|
|
33
|
+
return db;
|
|
34
|
+
}
|
|
35
|
+
export function upsertMemoryChecked(db, mem) {
|
|
36
|
+
db.transaction(() => {
|
|
37
|
+
const existing = db
|
|
38
|
+
.prepare('SELECT rowid, title, tags, content FROM memories WHERE id = ?')
|
|
39
|
+
.get(mem.id);
|
|
40
|
+
if (existing) {
|
|
41
|
+
db.prepare("INSERT INTO memories_fts(memories_fts, rowid, title, tags, content) VALUES('delete', ?, ?, ?, ?)").run(existing.rowid, existing.title, existing.tags, existing.content);
|
|
42
|
+
}
|
|
43
|
+
const tagsStr = mem.tags.join(',');
|
|
44
|
+
db.prepare(`INSERT INTO memories (id, title, tags, type, repository, created, updated, content, file_path, content_hash)
|
|
45
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
46
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
47
|
+
title=excluded.title, tags=excluded.tags, type=excluded.type,
|
|
48
|
+
repository=excluded.repository, updated=excluded.updated,
|
|
49
|
+
content=excluded.content, file_path=excluded.file_path,
|
|
50
|
+
content_hash=excluded.content_hash`).run(mem.id, mem.title, tagsStr, mem.type, mem.repository ?? null, mem.created, mem.updated, mem.content, mem.filePath, mem.contentHash);
|
|
51
|
+
const row = db
|
|
52
|
+
.prepare('SELECT rowid FROM memories WHERE id = ?')
|
|
53
|
+
.get(mem.id);
|
|
54
|
+
db.prepare('INSERT INTO memories_fts(rowid, title, tags, content) VALUES(?, ?, ?, ?)').run(row.rowid, mem.title, tagsStr, mem.content);
|
|
55
|
+
})();
|
|
56
|
+
}
|
|
57
|
+
export function deleteMemoryFromDb(db, id) {
|
|
58
|
+
db.transaction(() => {
|
|
59
|
+
const existing = db
|
|
60
|
+
.prepare('SELECT rowid, title, tags, content FROM memories WHERE id = ?')
|
|
61
|
+
.get(id);
|
|
62
|
+
if (existing) {
|
|
63
|
+
db.prepare("INSERT INTO memories_fts(memories_fts, rowid, title, tags, content) VALUES('delete', ?, ?, ?, ?)").run(existing.rowid, existing.title, existing.tags, existing.content);
|
|
64
|
+
db.prepare('DELETE FROM memories WHERE id = ?').run(id);
|
|
65
|
+
}
|
|
66
|
+
})();
|
|
67
|
+
}
|
|
68
|
+
export function searchFts(db, query, limit = 20) {
|
|
69
|
+
const rows = db
|
|
70
|
+
.prepare(`SELECT m.id, rank
|
|
71
|
+
FROM memories_fts f
|
|
72
|
+
JOIN memories m ON m.rowid = f.rowid
|
|
73
|
+
WHERE memories_fts MATCH ?
|
|
74
|
+
ORDER BY rank
|
|
75
|
+
LIMIT ?`)
|
|
76
|
+
.all(escapeFtsQuery(query), limit);
|
|
77
|
+
// rank is negative (more negative = better match), normalize to relative 0-1 score
|
|
78
|
+
if (rows.length === 0)
|
|
79
|
+
return [];
|
|
80
|
+
const best = Math.abs(rows[0].rank) || 1;
|
|
81
|
+
return rows.map((r) => ({
|
|
82
|
+
id: r.id,
|
|
83
|
+
score: Math.abs(r.rank) / best,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
function escapeFtsQuery(query) {
|
|
87
|
+
// Quote each token to prevent FTS5 operator interpretation
|
|
88
|
+
// e.g. "retry-after" → '"retry-after"', "foo bar" → '"foo" "bar"'
|
|
89
|
+
return query
|
|
90
|
+
.split(/\s+/)
|
|
91
|
+
.filter(Boolean)
|
|
92
|
+
.map((token) => `"${token.replace(/"/g, '""')}"`)
|
|
93
|
+
.join(' ');
|
|
94
|
+
}
|
|
95
|
+
function escapeLike(s) {
|
|
96
|
+
return s.replace(/[%_\\]/g, '\\$&');
|
|
97
|
+
}
|
|
98
|
+
export function getMemoryById(db, id) {
|
|
99
|
+
return db.prepare('SELECT file_path FROM memories WHERE id = ?').get(id);
|
|
100
|
+
}
|
|
101
|
+
export function getMemoryByPrefix(db, prefix) {
|
|
102
|
+
const rows = db
|
|
103
|
+
.prepare("SELECT id, file_path FROM memories WHERE id LIKE ? || '%' ESCAPE '\\'")
|
|
104
|
+
.all(escapeLike(prefix));
|
|
105
|
+
return rows.length === 1 ? rows[0] : undefined;
|
|
106
|
+
}
|
|
107
|
+
export function getMemoryByFilename(db, filename) {
|
|
108
|
+
return db
|
|
109
|
+
.prepare("SELECT file_path FROM memories WHERE file_path LIKE '%/' || ? ESCAPE '\\'")
|
|
110
|
+
.get(escapeLike(filename));
|
|
111
|
+
}
|
|
112
|
+
export function getAllMemoryIds(db) {
|
|
113
|
+
const rows = db.prepare('SELECT id FROM memories').all();
|
|
114
|
+
return new Set(rows.map((r) => r.id));
|
|
115
|
+
}
|
|
116
|
+
export function getContentHash(db, id) {
|
|
117
|
+
const row = db
|
|
118
|
+
.prepare('SELECT content_hash FROM memories WHERE id = ?')
|
|
119
|
+
.get(id);
|
|
120
|
+
return row?.content_hash;
|
|
121
|
+
}
|
|
122
|
+
export function grepMemories(db, pattern, limit = 20, ignoreCase = false) {
|
|
123
|
+
const escaped = escapeLike(pattern);
|
|
124
|
+
const sql = ignoreCase
|
|
125
|
+
? `SELECT id FROM memories WHERE (content LIKE '%' || ? || '%' ESCAPE '\\' COLLATE NOCASE) OR (title LIKE '%' || ? || '%' ESCAPE '\\' COLLATE NOCASE) LIMIT ?`
|
|
126
|
+
: `SELECT id FROM memories WHERE (content LIKE '%' || ? || '%' ESCAPE '\\') OR (title LIKE '%' || ? || '%' ESCAPE '\\') LIMIT ?`;
|
|
127
|
+
return db.prepare(sql).all(escaped, escaped, limit);
|
|
128
|
+
}
|
|
129
|
+
export function clearDb(db) {
|
|
130
|
+
db.exec('DELETE FROM embeddings');
|
|
131
|
+
db.exec('DELETE FROM memories');
|
|
132
|
+
db.exec("INSERT INTO memories_fts(memories_fts) VALUES('delete-all')");
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"none.js","sourceRoot":"","sources":["../../src/embeddings/none.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,YAAY;IACvB,IAAI,GAAG,MAAM,CAAC;IACd,KAAK,GAAG,MAAM,CAAC;IAEf,KAAK,CAAC,KAAK,CAAC,KAAa;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EmbeddingConfig } from '../types.js';
|
|
2
|
+
import type { EmbeddingProvider } from './provider.js';
|
|
3
|
+
export declare class OllamaProvider implements EmbeddingProvider {
|
|
4
|
+
name: string;
|
|
5
|
+
model: string;
|
|
6
|
+
private baseUrl;
|
|
7
|
+
constructor(config: EmbeddingConfig);
|
|
8
|
+
embed(text: string): Promise<number[]>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export class OllamaProvider {
|
|
2
|
+
name = 'ollama';
|
|
3
|
+
model;
|
|
4
|
+
baseUrl;
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.model = config.model;
|
|
7
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
8
|
+
}
|
|
9
|
+
async embed(text) {
|
|
10
|
+
const res = await fetch(`${this.baseUrl}/api/embeddings`, {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: { 'Content-Type': 'application/json' },
|
|
13
|
+
body: JSON.stringify({
|
|
14
|
+
model: this.model,
|
|
15
|
+
prompt: text,
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok) {
|
|
19
|
+
throw new Error(`Ollama embedding request failed: ${res.status} ${await res.text()}`);
|
|
20
|
+
}
|
|
21
|
+
const json = (await res.json());
|
|
22
|
+
if (!json.embedding) {
|
|
23
|
+
throw new Error('Ollama returned unexpected embedding response');
|
|
24
|
+
}
|
|
25
|
+
return json.embedding;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=ollama.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/embeddings/ollama.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,cAAc;IACzB,IAAI,GAAG,QAAQ,CAAC;IAChB,KAAK,CAAS;IACN,OAAO,CAAS;IAExB,YAAY,MAAuB;QACjC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,iBAAiB,EAAE;YACxD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,MAAM,EAAE,IAAI;aACb,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,oCAAoC,GAAG,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CACrE,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;QAC3D,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EmbeddingConfig } from '../types.js';
|
|
2
|
+
import type { EmbeddingProvider } from './provider.js';
|
|
3
|
+
export declare class OpenAIProvider implements EmbeddingProvider {
|
|
4
|
+
name: string;
|
|
5
|
+
model: string;
|
|
6
|
+
private baseUrl;
|
|
7
|
+
private apiKey;
|
|
8
|
+
constructor(config: EmbeddingConfig);
|
|
9
|
+
embed(text: string): Promise<number[]>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export class OpenAIProvider {
|
|
2
|
+
name = 'openai';
|
|
3
|
+
model;
|
|
4
|
+
baseUrl;
|
|
5
|
+
apiKey;
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.model = config.model;
|
|
8
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
9
|
+
this.apiKey = config.apiKey ?? process.env.OPENAI_API_KEY ?? '';
|
|
10
|
+
}
|
|
11
|
+
async embed(text) {
|
|
12
|
+
const res = await fetch(`${this.baseUrl}/embeddings`, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: {
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
...(this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}),
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify({
|
|
19
|
+
model: this.model,
|
|
20
|
+
input: text,
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok) {
|
|
24
|
+
throw new Error(`OpenAI embedding request failed: ${res.status} ${await res.text()}`);
|
|
25
|
+
}
|
|
26
|
+
const json = (await res.json());
|
|
27
|
+
if (!json.data?.[0]?.embedding) {
|
|
28
|
+
throw new Error('OpenAI returned unexpected embedding response');
|
|
29
|
+
}
|
|
30
|
+
return json.data[0].embedding;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/embeddings/openai.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,cAAc;IACzB,IAAI,GAAG,QAAQ,CAAC;IAChB,KAAK,CAAS;IACN,OAAO,CAAS;IAChB,MAAM,CAAS;IAEvB,YAAY,MAAuB;QACjC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,aAAa,EAAE;YACpD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACnE;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,KAAK,EAAE,IAAI;aACZ,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,oCAAoC,GAAG,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CACrE,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA6C,CAAC;QAC5E,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChC,CAAC;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NoneProvider } from './none.js';
|
|
2
|
+
import { OllamaProvider } from './ollama.js';
|
|
3
|
+
import { OpenAIProvider } from './openai.js';
|
|
4
|
+
export function createProvider(config) {
|
|
5
|
+
switch (config.provider) {
|
|
6
|
+
case 'openai':
|
|
7
|
+
return new OpenAIProvider(config);
|
|
8
|
+
case 'ollama':
|
|
9
|
+
return new OllamaProvider(config);
|
|
10
|
+
case 'none':
|
|
11
|
+
default:
|
|
12
|
+
return new NoneProvider();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/embeddings/provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAQ7C,MAAM,UAAU,cAAc,CAAC,MAAuB;IACpD,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,QAAQ;YACX,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;QACpC,KAAK,QAAQ;YACX,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;QACpC,KAAK,MAAM,CAAC;QACZ;YACE,OAAO,IAAI,YAAY,EAAE,CAAC;IAC9B,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type DB } from './db.js';
|
|
2
|
+
import { type EmbeddingProvider } from './embeddings/provider.js';
|
|
3
|
+
import type { Config, SearchResult } from './types.js';
|
|
4
|
+
export declare function hashContent(content: string): string;
|
|
5
|
+
/** Debounced syncIndex for read paths — skips if synced within 200ms */
|
|
6
|
+
export declare function syncIndexIfNeeded(config: Config, db: DB): void;
|
|
7
|
+
export declare function syncIndex(config: Config, db: DB): void;
|
|
8
|
+
export declare function reindex(config: Config, db: DB): Promise<void>;
|
|
9
|
+
export declare function searchAsync(config: Config, db: DB, query: string, limit?: number, provider?: EmbeddingProvider): Promise<SearchResult[]>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { clearDb, deleteMemoryFromDb, getAllMemoryIds, getContentHash, searchFts, upsertMemoryChecked, } from './db.js';
|
|
4
|
+
import { createProvider, } from './embeddings/provider.js';
|
|
5
|
+
import { listMemoryFiles, readMemory } from './memory.js';
|
|
6
|
+
export function hashContent(content) {
|
|
7
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
8
|
+
}
|
|
9
|
+
const lastSyncMap = new WeakMap();
|
|
10
|
+
/** Debounced syncIndex for read paths — skips if synced within 200ms */
|
|
11
|
+
export function syncIndexIfNeeded(config, db) {
|
|
12
|
+
const now = Date.now();
|
|
13
|
+
const last = lastSyncMap.get(db) ?? 0;
|
|
14
|
+
if (now - last < 200)
|
|
15
|
+
return;
|
|
16
|
+
lastSyncMap.set(db, now);
|
|
17
|
+
syncIndex(config, db);
|
|
18
|
+
}
|
|
19
|
+
export function syncIndex(config, db) {
|
|
20
|
+
const files = listMemoryFiles(config);
|
|
21
|
+
const dbIds = getAllMemoryIds(db);
|
|
22
|
+
const seenIds = new Set();
|
|
23
|
+
for (const filePath of files) {
|
|
24
|
+
let mem;
|
|
25
|
+
try {
|
|
26
|
+
mem = readMemory(filePath);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
continue; // Skip unparseable files
|
|
30
|
+
}
|
|
31
|
+
seenIds.add(mem.id);
|
|
32
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
33
|
+
const hash = hashContent(raw);
|
|
34
|
+
const existingHash = getContentHash(db, mem.id);
|
|
35
|
+
if (existingHash !== hash) {
|
|
36
|
+
upsertMemoryChecked(db, {
|
|
37
|
+
id: mem.id,
|
|
38
|
+
title: mem.title,
|
|
39
|
+
tags: mem.tags,
|
|
40
|
+
type: mem.type,
|
|
41
|
+
repository: mem.repository,
|
|
42
|
+
created: mem.created,
|
|
43
|
+
updated: mem.updated,
|
|
44
|
+
content: mem.content,
|
|
45
|
+
filePath,
|
|
46
|
+
contentHash: hash,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Delete DB entries for files that no longer exist
|
|
51
|
+
for (const id of dbIds) {
|
|
52
|
+
if (!seenIds.has(id)) {
|
|
53
|
+
deleteMemoryFromDb(db, id);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function reindex(config, db) {
|
|
58
|
+
clearDb(db);
|
|
59
|
+
const files = listMemoryFiles(config);
|
|
60
|
+
const provider = createProvider(config.embedding);
|
|
61
|
+
for (const filePath of files) {
|
|
62
|
+
let mem;
|
|
63
|
+
try {
|
|
64
|
+
mem = readMemory(filePath);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
70
|
+
const hash = hashContent(raw);
|
|
71
|
+
upsertMemoryChecked(db, {
|
|
72
|
+
id: mem.id,
|
|
73
|
+
title: mem.title,
|
|
74
|
+
tags: mem.tags,
|
|
75
|
+
type: mem.type,
|
|
76
|
+
repository: mem.repository,
|
|
77
|
+
created: mem.created,
|
|
78
|
+
updated: mem.updated,
|
|
79
|
+
content: mem.content,
|
|
80
|
+
filePath,
|
|
81
|
+
contentHash: hash,
|
|
82
|
+
});
|
|
83
|
+
await computeAndStoreEmbedding(db, provider, mem);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function computeAndStoreEmbedding(db, provider, mem) {
|
|
87
|
+
if (provider.name === 'none')
|
|
88
|
+
return;
|
|
89
|
+
const text = `${mem.title}\n${mem.tags.join(', ')}\n${mem.content}`;
|
|
90
|
+
const embedding = await provider.embed(text);
|
|
91
|
+
const buffer = Buffer.from(new Float32Array(embedding).buffer);
|
|
92
|
+
db.prepare(`INSERT INTO embeddings (id, embedding, model, dimensions)
|
|
93
|
+
VALUES (?, ?, ?, ?)
|
|
94
|
+
ON CONFLICT(id) DO UPDATE SET embedding=excluded.embedding, model=excluded.model, dimensions=excluded.dimensions`).run(mem.id, buffer, provider.model, embedding.length);
|
|
95
|
+
}
|
|
96
|
+
export async function searchAsync(config, db, query, limit = 20, provider) {
|
|
97
|
+
syncIndexIfNeeded(config, db);
|
|
98
|
+
const ftsResults = searchFts(db, query, limit);
|
|
99
|
+
const ftsMap = new Map(ftsResults.map((r) => [r.id, r.score]));
|
|
100
|
+
// Check for embeddings
|
|
101
|
+
const embeddingRows = db
|
|
102
|
+
.prepare('SELECT COUNT(*) as count FROM embeddings')
|
|
103
|
+
.get();
|
|
104
|
+
const effectiveProvider = provider ?? createProvider(config.embedding);
|
|
105
|
+
if (embeddingRows.count > 0 && effectiveProvider.name !== 'none') {
|
|
106
|
+
const queryEmbedding = await effectiveProvider.embed(query);
|
|
107
|
+
const allEmbeddings = db
|
|
108
|
+
.prepare('SELECT id, embedding FROM embeddings')
|
|
109
|
+
.all();
|
|
110
|
+
const vectorScores = [];
|
|
111
|
+
for (const row of allEmbeddings) {
|
|
112
|
+
const stored = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / 4);
|
|
113
|
+
if (queryEmbedding.length !== stored.length)
|
|
114
|
+
continue;
|
|
115
|
+
const sim = cosineSimilarity(queryEmbedding, Array.from(stored));
|
|
116
|
+
vectorScores.push({ id: row.id, score: (sim + 1) / 2 }); // normalize to 0-1
|
|
117
|
+
}
|
|
118
|
+
vectorScores.sort((a, b) => b.score - a.score);
|
|
119
|
+
const topVector = vectorScores.slice(0, limit);
|
|
120
|
+
const vectorMap = new Map(topVector.map((r) => [r.id, r.score]));
|
|
121
|
+
// Merge: FTS weight 0.6, vector weight 0.4
|
|
122
|
+
const allIds = new Set([...ftsMap.keys(), ...vectorMap.keys()]);
|
|
123
|
+
const merged = [];
|
|
124
|
+
for (const id of allIds) {
|
|
125
|
+
const ftsScore = ftsMap.get(id) ?? 0;
|
|
126
|
+
const vecScore = vectorMap.get(id) ?? 0;
|
|
127
|
+
merged.push({ id, score: ftsScore * 0.6 + vecScore * 0.4 });
|
|
128
|
+
}
|
|
129
|
+
merged.sort((a, b) => b.score - a.score);
|
|
130
|
+
return merged.slice(0, limit).map((r) => {
|
|
131
|
+
const row = db
|
|
132
|
+
.prepare('SELECT file_path FROM memories WHERE id = ?')
|
|
133
|
+
.get(r.id);
|
|
134
|
+
const mem = readMemory(row.file_path);
|
|
135
|
+
return {
|
|
136
|
+
memory: mem,
|
|
137
|
+
score: r.score,
|
|
138
|
+
matchType: (vectorMap.has(r.id) ? 'vector' : 'fts'),
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return ftsResults.map((r) => {
|
|
143
|
+
const row = db
|
|
144
|
+
.prepare('SELECT file_path FROM memories WHERE id = ?')
|
|
145
|
+
.get(r.id);
|
|
146
|
+
const mem = readMemory(row.file_path);
|
|
147
|
+
return { memory: mem, score: r.score, matchType: 'fts' };
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function cosineSimilarity(a, b) {
|
|
151
|
+
let dot = 0, normA = 0, normB = 0;
|
|
152
|
+
for (let i = 0; i < a.length; i++) {
|
|
153
|
+
dot += a[i] * b[i];
|
|
154
|
+
normA += a[i] * a[i];
|
|
155
|
+
normB += b[i] * b[i];
|
|
156
|
+
}
|
|
157
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
158
|
+
return denom === 0 ? 0 : dot / denom;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startMcpServer(): Promise<void>;
|