mor 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/cli.js +61 -89
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config.d.ts +1 -1
  4. package/dist/config.js +1 -6
  5. package/dist/config.js.map +1 -1
  6. package/dist/db.d.ts +7 -1
  7. package/dist/db.js +66 -46
  8. package/dist/db.js.map +1 -1
  9. package/dist/embeddings/azure-openai.d.ts +1 -1
  10. package/dist/embeddings/ollama.d.ts +1 -1
  11. package/dist/embeddings/openai.d.ts +1 -1
  12. package/dist/embeddings/provider.d.ts +1 -1
  13. package/dist/filter.d.ts +1 -1
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.js +27 -49
  16. package/dist/index.js.map +1 -1
  17. package/dist/mcp.js +27 -131
  18. package/dist/mcp.js.map +1 -1
  19. package/dist/memory.d.ts +14 -3
  20. package/dist/memory.js +61 -45
  21. package/dist/memory.js.map +1 -1
  22. package/dist/operations-client.d.ts +38 -0
  23. package/dist/operations-client.js +101 -0
  24. package/dist/operations-client.js.map +1 -0
  25. package/dist/operations-local.d.ts +44 -0
  26. package/dist/operations-local.js +297 -0
  27. package/dist/operations-local.js.map +1 -0
  28. package/dist/operations-remote.d.ts +39 -0
  29. package/dist/operations-remote.js +80 -0
  30. package/dist/operations-remote.js.map +1 -0
  31. package/dist/operations-server.d.ts +8 -0
  32. package/dist/operations-server.js +218 -0
  33. package/dist/operations-server.js.map +1 -0
  34. package/dist/operations.d.ts +64 -40
  35. package/dist/operations.js +9 -143
  36. package/dist/operations.js.map +1 -1
  37. package/dist/server.js +1 -1
  38. package/dist/server.js.map +1 -1
  39. package/dist/utils/diff.d.ts +1 -0
  40. package/dist/utils/diff.js +90 -0
  41. package/dist/utils/diff.js.map +1 -0
  42. package/dist/utils/ext.d.ts +2 -0
  43. package/dist/utils/ext.js +57 -0
  44. package/dist/utils/ext.js.map +1 -0
  45. package/dist/utils/git.d.ts +1 -0
  46. package/dist/utils/git.js +8 -0
  47. package/dist/utils/git.js.map +1 -0
  48. package/dist/utils/glob.d.ts +2 -0
  49. package/dist/utils/glob.js +9 -0
  50. package/dist/utils/glob.js.map +1 -0
  51. package/dist/utils/path.d.ts +1 -0
  52. package/dist/utils/path.js +8 -0
  53. package/dist/utils/path.js.map +1 -0
  54. package/dist/utils/similarity.d.ts +1 -0
  55. package/dist/utils/similarity.js +11 -0
  56. package/dist/utils/similarity.js.map +1 -0
  57. package/package.json +4 -1
@@ -0,0 +1,297 @@
1
+ import crypto from 'node:crypto';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { clearDb, deleteMemoryFromDb, getAllEmbeddings, getAllMemoryIds, getContentHash, getEmbeddingCount, getMemoryByFilename, getMemoryById, getMemoryByPrefix, grepMemories, openDb, searchFts, upsertEmbedding, upsertMemoryChecked, } from './db.js';
4
+ import { createProvider, } from './embeddings/provider.js';
5
+ import { createMemory, deleteMemory, listMemoryFiles, readMemory, readMemoryWithRaw, safeReadMemory, updateMemory, } from './memory.js';
6
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7
+ const UUID_PREFIX_RE = /^[0-9a-f]{4,}$/i;
8
+ function hashContent(content) {
9
+ return crypto.createHash('sha256').update(content).digest('hex');
10
+ }
11
+ import { matchGlob } from './utils/glob.js';
12
+ import { cosineSimilarity } from './utils/similarity.js';
13
+ function matchMemory(mem, filter) {
14
+ if (filter.type) {
15
+ const types = filter.type.split(',').map((t) => t.trim());
16
+ if (!types.some((t) => matchGlob(mem.type, t)))
17
+ return false;
18
+ }
19
+ if (filter.tag) {
20
+ if (!mem.tags.some((tag) => matchGlob(tag, filter.tag)))
21
+ return false;
22
+ }
23
+ if (filter.repo) {
24
+ if (!mem.repository || !matchGlob(mem.repository, filter.repo))
25
+ return false;
26
+ }
27
+ if (filter.ext) {
28
+ const ext = filter.ext.startsWith('.') ? filter.ext : `.${filter.ext}`;
29
+ if (!mem.title.toLowerCase().endsWith(ext.toLowerCase()))
30
+ return false;
31
+ }
32
+ return true;
33
+ }
34
+ function applyMemoryFilter(memories, filter) {
35
+ if (!filter || (!filter.type && !filter.tag && !filter.repo && !filter.ext))
36
+ return memories;
37
+ return memories.filter((mem) => matchMemory(mem, filter));
38
+ }
39
+ function applyResultFilter(results, filter) {
40
+ if (!filter || (!filter.type && !filter.tag && !filter.repo && !filter.ext))
41
+ return results;
42
+ return results.filter((r) => matchMemory(r.memory, filter));
43
+ }
44
+ export class LocalOperations {
45
+ config;
46
+ db;
47
+ provider;
48
+ lastSyncTime = 0;
49
+ constructor(config) {
50
+ this.config = config;
51
+ this.db = openDb(config);
52
+ this.provider = createProvider(config.embedding);
53
+ }
54
+ // ---- Index management ----
55
+ syncIndex() {
56
+ const files = listMemoryFiles(this.config);
57
+ const dbIds = getAllMemoryIds(this.db);
58
+ const seenIds = new Set();
59
+ for (const filePath of files) {
60
+ const result = readMemoryWithRaw(filePath);
61
+ if (!result)
62
+ continue;
63
+ const { mem, raw } = result;
64
+ seenIds.add(mem.id);
65
+ const existingHash = getContentHash(this.db, mem.id);
66
+ if (existingHash !== hashContent(raw)) {
67
+ this.upsertFromMemory(mem, raw);
68
+ }
69
+ }
70
+ for (const id of dbIds) {
71
+ if (!seenIds.has(id)) {
72
+ deleteMemoryFromDb(this.db, id);
73
+ }
74
+ }
75
+ }
76
+ syncIndexIfNeeded() {
77
+ const now = Date.now();
78
+ if (now - this.lastSyncTime < 200)
79
+ return;
80
+ this.lastSyncTime = now;
81
+ this.syncIndex();
82
+ }
83
+ upsertFromMemory(mem, raw) {
84
+ upsertMemoryChecked(this.db, {
85
+ id: mem.id,
86
+ title: mem.title,
87
+ tags: mem.tags,
88
+ type: mem.type,
89
+ repository: mem.repository,
90
+ created: mem.created,
91
+ updated: mem.updated,
92
+ content: mem.content,
93
+ filePath: mem.filePath,
94
+ contentHash: hashContent(raw),
95
+ });
96
+ }
97
+ async computeEmbedding(mem) {
98
+ if (this.provider.name === 'none')
99
+ return;
100
+ const text = `${mem.title}\n${mem.tags.join(', ')}\n${mem.content}`;
101
+ const embedding = await this.provider.embed(text);
102
+ const buffer = Buffer.from(new Float32Array(embedding).buffer);
103
+ upsertEmbedding(this.db, mem.id, buffer, this.provider.model, embedding.length);
104
+ }
105
+ // ---- Query resolution ----
106
+ resolveById(id) {
107
+ this.syncIndexIfNeeded();
108
+ if (UUID_RE.test(id)) {
109
+ const row = getMemoryById(this.db, id);
110
+ if (row)
111
+ return readMemory(row.file_path);
112
+ }
113
+ if (UUID_PREFIX_RE.test(id) && id.length >= 8) {
114
+ const row = getMemoryByPrefix(this.db, id);
115
+ if (row)
116
+ return readMemory(row.file_path);
117
+ }
118
+ return undefined;
119
+ }
120
+ resolveQuery(query) {
121
+ const byId = this.resolveById(query);
122
+ if (byId)
123
+ return byId;
124
+ const byFilename = getMemoryByFilename(this.db, query);
125
+ if (byFilename)
126
+ return readMemory(byFilename.file_path);
127
+ if (!query.endsWith('.md')) {
128
+ const byFilenameMd = getMemoryByFilename(this.db, query + '.md');
129
+ if (byFilenameMd)
130
+ return readMemory(byFilenameMd.file_path);
131
+ }
132
+ try {
133
+ const results = searchFts(this.db, query, 1);
134
+ if (results.length > 0) {
135
+ const row = getMemoryById(this.db, results[0].id);
136
+ if (row)
137
+ return readMemory(row.file_path);
138
+ }
139
+ }
140
+ catch {
141
+ // FTS query syntax error
142
+ }
143
+ return undefined;
144
+ }
145
+ // ---- Operations interface ----
146
+ async search(query, limit = 20, filter) {
147
+ this.syncIndexIfNeeded();
148
+ const ftsResults = searchFts(this.db, query, limit);
149
+ const ftsMap = new Map(ftsResults.map((r) => [r.id, r.score]));
150
+ if (this.provider.name !== 'none' && getEmbeddingCount(this.db) > 0) {
151
+ const queryEmbedding = await this.provider.embed(query);
152
+ const MIN_COSINE_SIMILARITY = 0.15;
153
+ const vectorScores = [];
154
+ for (const row of getAllEmbeddings(this.db)) {
155
+ const stored = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / 4);
156
+ if (queryEmbedding.length !== stored.length)
157
+ continue;
158
+ const sim = cosineSimilarity(queryEmbedding, Array.from(stored));
159
+ if (sim >= MIN_COSINE_SIMILARITY) {
160
+ vectorScores.push({ id: row.id, score: (sim + 1) / 2 });
161
+ }
162
+ }
163
+ vectorScores.sort((a, b) => b.score - a.score);
164
+ const topVector = vectorScores.slice(0, limit);
165
+ const vectorMap = new Map(topVector.map((r) => [r.id, r.score]));
166
+ const allIds = new Set([...ftsMap.keys(), ...vectorMap.keys()]);
167
+ const merged = [];
168
+ for (const id of allIds) {
169
+ const ftsScore = ftsMap.get(id) ?? 0;
170
+ const vecScore = vectorMap.get(id) ?? 0;
171
+ merged.push({ id, score: ftsScore * 0.6 + vecScore * 0.4 });
172
+ }
173
+ merged.sort((a, b) => b.score - a.score);
174
+ const results = [];
175
+ for (const r of merged.slice(0, limit)) {
176
+ const row = getMemoryById(this.db, r.id);
177
+ if (!row)
178
+ continue;
179
+ results.push({
180
+ memory: readMemory(row.file_path),
181
+ score: r.score,
182
+ matchType: vectorMap.has(r.id) ? 'vector' : 'fts',
183
+ });
184
+ }
185
+ return applyResultFilter(results, filter);
186
+ }
187
+ const results = [];
188
+ for (const r of ftsResults) {
189
+ const row = getMemoryById(this.db, r.id);
190
+ if (!row)
191
+ continue;
192
+ results.push({
193
+ memory: readMemory(row.file_path),
194
+ score: r.score,
195
+ matchType: 'fts',
196
+ });
197
+ }
198
+ return applyResultFilter(results, filter);
199
+ }
200
+ async read(query) {
201
+ return this.resolveQuery(query);
202
+ }
203
+ async add(opts) {
204
+ const { mem, raw } = createMemory(this.config, opts);
205
+ this.upsertFromMemory(mem, raw);
206
+ await this.computeEmbedding(mem);
207
+ return mem;
208
+ }
209
+ async update(query, updates) {
210
+ const existing = this.resolveById(query);
211
+ if (!existing)
212
+ throw new Error(`Memory not found for ID: ${query}. Use a full UUID or 8+ char prefix.`);
213
+ const { mem, raw } = updateMemory(existing.filePath, updates);
214
+ this.upsertFromMemory(mem, raw);
215
+ await this.computeEmbedding(mem);
216
+ return mem;
217
+ }
218
+ async remove(query) {
219
+ const mem = this.resolveById(query);
220
+ if (!mem)
221
+ throw new Error(`Memory not found for ID: ${query}. Use a full UUID or 8+ char prefix.`);
222
+ deleteMemory(mem.filePath);
223
+ deleteMemoryFromDb(this.db, mem.id);
224
+ return { title: mem.title, id: mem.id };
225
+ }
226
+ async grep(pattern, limit = 20, ignoreCase = false, filter) {
227
+ this.syncIndexIfNeeded();
228
+ const rows = grepMemories(this.db, pattern, limit, ignoreCase);
229
+ const memories = rows
230
+ .map((row) => safeReadMemory(row.file_path))
231
+ .filter((m) => m !== undefined);
232
+ return applyMemoryFilter(memories, filter);
233
+ }
234
+ async list(filter) {
235
+ this.syncIndex();
236
+ const files = listMemoryFiles(this.config);
237
+ const memories = files
238
+ .map((f) => safeReadMemory(f))
239
+ .filter((m) => m !== undefined);
240
+ memories.sort((a, b) => b.updated.localeCompare(a.updated));
241
+ return applyMemoryFilter(memories, filter);
242
+ }
243
+ async reindex() {
244
+ clearDb(this.db);
245
+ const files = listMemoryFiles(this.config);
246
+ let count = 0;
247
+ for (const filePath of files) {
248
+ const result = readMemoryWithRaw(filePath);
249
+ if (!result)
250
+ continue;
251
+ const { mem, raw } = result;
252
+ this.upsertFromMemory(mem, raw);
253
+ await this.computeEmbedding(mem);
254
+ count++;
255
+ }
256
+ return { count };
257
+ }
258
+ async sync() {
259
+ const dir = this.config.memoryDir;
260
+ const git = (args) => spawnSync('git', args, {
261
+ cwd: dir,
262
+ stdio: 'pipe',
263
+ encoding: 'utf-8',
264
+ });
265
+ const status = git(['status', '--porcelain']);
266
+ if (status.status !== 0) {
267
+ throw new Error('Memory folder is not a git repository');
268
+ }
269
+ const actions = [];
270
+ const pull = git(['pull', '--rebase', '--autostash']);
271
+ if (pull.status !== 0)
272
+ throw new Error(`git pull failed: ${pull.stderr.trim()}`);
273
+ if (!pull.stdout.includes('Already up to date')) {
274
+ actions.push('pulled');
275
+ this.syncIndex();
276
+ }
277
+ if (status.stdout.trim()) {
278
+ const add = git(['add', '-A']);
279
+ if (add.status !== 0)
280
+ throw new Error(`git add failed: ${add.stderr.trim()}`);
281
+ const commit = git(['commit', '-m', 'update memory']);
282
+ if (commit.status !== 0)
283
+ throw new Error(`git commit failed: ${commit.stderr.trim()}`);
284
+ const push = git(['push']);
285
+ if (push.status !== 0)
286
+ throw new Error(`git push failed: ${push.stderr.trim()}`);
287
+ actions.push('pushed');
288
+ }
289
+ return {
290
+ message: actions.length > 0 ? actions.join(' and ') : 'Already up to date',
291
+ };
292
+ }
293
+ close() {
294
+ this.db.close();
295
+ }
296
+ }
297
+ //# sourceMappingURL=operations-local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations-local.js","sourceRoot":"","sources":["../src/operations-local.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { Operations } from './operations.js';
2
+ import type { Config, Memory, SearchResult } from './types.js';
3
+ export declare class RemoteOperations implements Operations {
4
+ private baseUrl;
5
+ private token?;
6
+ constructor(config: Config);
7
+ private headers;
8
+ private request;
9
+ search(query: string, limit?: number): Promise<SearchResult[]>;
10
+ read(query: string): Promise<Memory | undefined>;
11
+ add(opts: {
12
+ title: string;
13
+ description?: string;
14
+ content: string;
15
+ tags?: string[];
16
+ type?: string;
17
+ repository?: string;
18
+ }): Promise<Memory>;
19
+ update(query: string, updates: {
20
+ title?: string;
21
+ description?: string;
22
+ content?: string;
23
+ tags?: string[];
24
+ type?: string;
25
+ }): Promise<Memory>;
26
+ remove(query: string): Promise<{
27
+ title: string;
28
+ id: string;
29
+ }>;
30
+ grep(pattern: string, limit?: number, ignoreCase?: boolean): Promise<Memory[]>;
31
+ list(): Promise<Memory[]>;
32
+ reindex(): Promise<{
33
+ count: number;
34
+ }>;
35
+ sync(): Promise<{
36
+ message: string;
37
+ }>;
38
+ close(): void;
39
+ }
@@ -0,0 +1,80 @@
1
+ class HttpError extends Error {
2
+ status;
3
+ constructor(status, message) {
4
+ super(message);
5
+ this.status = status;
6
+ }
7
+ }
8
+ export class RemoteOperations {
9
+ baseUrl;
10
+ token;
11
+ constructor(config) {
12
+ if (!config.server?.url)
13
+ throw new Error('No server URL configured');
14
+ this.baseUrl = config.server.url.replace(/\/+$/, '');
15
+ this.token = config.server.token;
16
+ }
17
+ headers() {
18
+ const h = { 'Content-Type': 'application/json' };
19
+ if (this.token)
20
+ h['Authorization'] = `Bearer ${this.token}`;
21
+ return h;
22
+ }
23
+ async request(method, path, body) {
24
+ const url = `${this.baseUrl}${path}`;
25
+ const res = await fetch(url, {
26
+ method,
27
+ headers: this.headers(),
28
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
29
+ });
30
+ const json = (await res.json());
31
+ if (!res.ok) {
32
+ throw new HttpError(res.status, json.error ?? `HTTP ${res.status}`);
33
+ }
34
+ return json.data;
35
+ }
36
+ async search(query, limit = 20) {
37
+ const params = new URLSearchParams({ q: query, limit: String(limit) });
38
+ return this.request('GET', `/memories/search?${params}`);
39
+ }
40
+ async read(query) {
41
+ try {
42
+ return await this.request('GET', `/memories/${encodeURIComponent(query)}`);
43
+ }
44
+ catch (e) {
45
+ if (e instanceof HttpError && e.status === 404)
46
+ return undefined;
47
+ throw e;
48
+ }
49
+ }
50
+ async add(opts) {
51
+ return this.request('POST', '/memories', opts);
52
+ }
53
+ async update(query, updates) {
54
+ return this.request('PUT', `/memories/${encodeURIComponent(query)}`, updates);
55
+ }
56
+ async remove(query) {
57
+ return this.request('DELETE', `/memories/${encodeURIComponent(query)}`);
58
+ }
59
+ async grep(pattern, limit = 20, ignoreCase = false) {
60
+ const params = new URLSearchParams({
61
+ q: pattern,
62
+ limit: String(limit),
63
+ ...(ignoreCase ? { ignoreCase: '1' } : {}),
64
+ });
65
+ return this.request('GET', `/memories/grep?${params}`);
66
+ }
67
+ async list() {
68
+ return this.request('GET', '/memories');
69
+ }
70
+ async reindex() {
71
+ return this.request('POST', '/reindex');
72
+ }
73
+ async sync() {
74
+ return this.request('POST', '/sync');
75
+ }
76
+ close() {
77
+ // no-op for remote
78
+ }
79
+ }
80
+ //# sourceMappingURL=operations-remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations-remote.js","sourceRoot":"","sources":["../src/operations-remote.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type http from 'node:http';
2
+ import type { Config } from './operations.js';
3
+ export declare function startServer(config: Config, opts: {
4
+ port: number;
5
+ host: string;
6
+ token?: string;
7
+ mcp?: boolean;
8
+ }): http.Server;
@@ -0,0 +1,218 @@
1
+ import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
2
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
3
+ import { serve } from '@hono/node-server';
4
+ import crypto from 'node:crypto';
5
+ import { Hono } from 'hono';
6
+ import { logger } from 'hono/logger';
7
+ import { createRequire } from 'node:module';
8
+ import { createMcpServer } from './mcp.js';
9
+ import { LocalOperations } from './operations-local.js';
10
+ const require = createRequire(import.meta.url);
11
+ const { version } = require('../package.json');
12
+ function log(msg) {
13
+ process.stderr.write(`[mor] ${msg}\n`);
14
+ }
15
+ function isLoopbackHost(host) {
16
+ const hostname = host.split(':')[0];
17
+ return ['127.0.0.1', 'localhost', '::1', '[::1]'].includes(hostname);
18
+ }
19
+ function parseLimit(raw) {
20
+ const n = parseInt(raw ?? '20', 10);
21
+ return Number.isNaN(n) || n < 1 ? 20 : n;
22
+ }
23
+ export function startServer(config, opts) {
24
+ const ops = new LocalOperations(config);
25
+ const mcpTransports = new Map();
26
+ const app = new Hono();
27
+ app.use(logger((msg) => log(msg)));
28
+ if (opts.token) {
29
+ app.use(async (c, next) => {
30
+ const provided = c.req.header('authorization')?.replace(/^Bearer\s+/i, '') ??
31
+ c.req.query('token') ??
32
+ '';
33
+ const expectedBuf = Buffer.from(opts.token);
34
+ const providedBuf = Buffer.from(provided);
35
+ if (expectedBuf.byteLength !== providedBuf.byteLength ||
36
+ !crypto.timingSafeEqual(providedBuf, expectedBuf)) {
37
+ return c.json({ error: 'Unauthorized' }, 401);
38
+ }
39
+ await next();
40
+ });
41
+ }
42
+ if (isLoopbackHost(opts.host) && !opts.token) {
43
+ app.use(async (c, next) => {
44
+ const reqHost = c.req.header('host');
45
+ if (!reqHost || !isLoopbackHost(reqHost)) {
46
+ return c.json({ error: 'Forbidden: DNS rebinding protection' }, 403);
47
+ }
48
+ await next();
49
+ });
50
+ }
51
+ app.get('/health', (c) => c.json({ ok: true, version }));
52
+ app.get('/memories/search', async (c) => {
53
+ const q = c.req.query('q');
54
+ if (!q)
55
+ return c.json({ error: 'Missing query parameter: q' }, 400);
56
+ return c.json({
57
+ data: await ops.search(q, parseLimit(c.req.query('limit'))),
58
+ });
59
+ });
60
+ app.get('/memories/grep', async (c) => {
61
+ const q = c.req.query('q');
62
+ if (!q)
63
+ return c.json({ error: 'Missing query parameter: q' }, 400);
64
+ const ignoreCase = c.req.query('ignoreCase') === '1';
65
+ return c.json({
66
+ data: await ops.grep(q, parseLimit(c.req.query('limit')), ignoreCase),
67
+ });
68
+ });
69
+ app.get('/memories', async (c) => c.json({ data: await ops.list() }));
70
+ app.post('/memories', async (c) => {
71
+ const body = await c.req.json();
72
+ if (!body.title || !body.content) {
73
+ return c.json({ error: 'Missing required fields: title, content' }, 400);
74
+ }
75
+ const { title, description, content, tags, type, repository } = body;
76
+ return c.json({
77
+ data: await ops.add({
78
+ title,
79
+ description,
80
+ content,
81
+ tags,
82
+ type,
83
+ repository,
84
+ }),
85
+ }, 201);
86
+ });
87
+ app.get('/memories/:query', async (c) => {
88
+ const mem = await ops.read(c.req.param('query'));
89
+ if (!mem) {
90
+ return c.json({ error: `Memory not found: ${c.req.param('query')}` }, 404);
91
+ }
92
+ return c.json({ data: mem });
93
+ });
94
+ app.put('/memories/:query', async (c) => {
95
+ const body = await c.req.json();
96
+ try {
97
+ const { title, description, content, tags, type } = body;
98
+ return c.json({
99
+ data: await ops.update(c.req.param('query'), {
100
+ title,
101
+ description,
102
+ content,
103
+ tags,
104
+ type,
105
+ }),
106
+ });
107
+ }
108
+ catch (e) {
109
+ const msg = e instanceof Error ? e.message : String(e);
110
+ return c.json({ error: msg }, msg.includes('Memory not found') ? 404 : 500);
111
+ }
112
+ });
113
+ app.delete('/memories/:query', async (c) => {
114
+ try {
115
+ return c.json({ data: await ops.remove(c.req.param('query')) });
116
+ }
117
+ catch (e) {
118
+ const msg = e instanceof Error ? e.message : String(e);
119
+ return c.json({ error: msg }, msg.includes('Memory not found') ? 404 : 500);
120
+ }
121
+ });
122
+ app.post('/reindex', async (c) => {
123
+ try {
124
+ return c.json({ data: await ops.reindex() });
125
+ }
126
+ catch (e) {
127
+ const msg = e instanceof Error ? e.message : String(e);
128
+ return c.json({ error: msg }, 500);
129
+ }
130
+ });
131
+ app.post('/sync', async (c) => {
132
+ try {
133
+ return c.json({ data: await ops.sync() });
134
+ }
135
+ catch (e) {
136
+ const msg = e instanceof Error ? e.message : String(e);
137
+ return c.json({ error: msg }, 500);
138
+ }
139
+ });
140
+ // MCP HTTP transport
141
+ if (opts.mcp) {
142
+ app.all('/mcp', async (c) => {
143
+ const sessionId = c.req.header('mcp-session-id');
144
+ if (c.req.method === 'GET' || c.req.method === 'DELETE') {
145
+ const transport = sessionId ? mcpTransports.get(sessionId) : undefined;
146
+ if (!transport) {
147
+ return c.json({
148
+ jsonrpc: '2.0',
149
+ error: {
150
+ code: -32000,
151
+ message: 'Invalid or missing session ID',
152
+ },
153
+ id: null,
154
+ }, 400);
155
+ }
156
+ return transport.handleRequest(c.req.raw);
157
+ }
158
+ // POST
159
+ const body = await c.req.json();
160
+ if (sessionId && mcpTransports.has(sessionId)) {
161
+ return mcpTransports
162
+ .get(sessionId)
163
+ .handleRequest(c.req.raw, { parsedBody: body });
164
+ }
165
+ if (!sessionId && isInitializeRequest(body)) {
166
+ const transport = new WebStandardStreamableHTTPServerTransport({
167
+ sessionIdGenerator: () => crypto.randomUUID(),
168
+ onsessioninitialized: (sid) => {
169
+ mcpTransports.set(sid, transport);
170
+ },
171
+ });
172
+ transport.onclose = () => {
173
+ if (transport.sessionId)
174
+ mcpTransports.delete(transport.sessionId);
175
+ };
176
+ const mcpServer = createMcpServer(new LocalOperations(config));
177
+ await mcpServer.connect(transport);
178
+ return transport.handleRequest(c.req.raw, { parsedBody: body });
179
+ }
180
+ return c.json({
181
+ jsonrpc: '2.0',
182
+ error: {
183
+ code: -32000,
184
+ message: 'Bad Request: invalid or missing session',
185
+ },
186
+ id: null,
187
+ }, 400);
188
+ });
189
+ }
190
+ const server = serve({ fetch: app.fetch, port: opts.port, hostname: opts.host }, (info) => {
191
+ log(`Listening on http://${opts.host}:${info.port}`);
192
+ if (opts.mcp)
193
+ log('MCP endpoint enabled at /mcp');
194
+ });
195
+ const shutdown = () => {
196
+ log('Shutting down...');
197
+ for (const transport of mcpTransports.values()) {
198
+ transport.close().catch(() => { });
199
+ }
200
+ mcpTransports.clear();
201
+ ops.close();
202
+ server.close();
203
+ };
204
+ process.on('SIGINT', () => {
205
+ shutdown();
206
+ process.exit(0);
207
+ });
208
+ process.on('SIGTERM', () => {
209
+ shutdown();
210
+ process.exit(0);
211
+ });
212
+ server.on('close', () => {
213
+ process.removeAllListeners('SIGINT');
214
+ process.removeAllListeners('SIGTERM');
215
+ });
216
+ return server;
217
+ }
218
+ //# sourceMappingURL=operations-server.js.map