doc2vec 1.3.0 → 2.2.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 +216 -8
- package/dist/code-chunker.js +179 -0
- package/dist/content-processor copy.js +819 -0
- package/dist/content-processor.js +620 -72
- package/dist/database.js +205 -19
- package/dist/doc2vec.js +520 -8
- package/dist/electron-app/src/database.js +218 -0
- package/dist/electron-app/src/embeddings.js +80 -0
- package/dist/electron-app/src/main.js +627 -0
- package/dist/electron-app/src/mcp-server.js +496 -0
- package/dist/electron-app/src/preload.js +24 -0
- package/dist/electron-app/src/processor.js +248 -0
- package/dist/electron-app/src/syncer.js +146 -0
- package/dist/macos-app/migrate-to-vec0.js +156 -0
- package/dist/vitest.config.js +16 -0
- package/package.json +13 -3
- package/dist/embedding-providers.js +0 -157
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.McpServer = void 0;
|
|
40
|
+
const express_1 = __importDefault(require("express"));
|
|
41
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
42
|
+
const sqliteVec = __importStar(require("sqlite-vec"));
|
|
43
|
+
const openai_1 = __importDefault(require("openai"));
|
|
44
|
+
const crypto_1 = require("crypto");
|
|
45
|
+
class McpServer {
|
|
46
|
+
constructor(port = 3333) {
|
|
47
|
+
this.server = null;
|
|
48
|
+
this.dbPath = null;
|
|
49
|
+
this.openaiApiKey = null;
|
|
50
|
+
this.sessions = new Map();
|
|
51
|
+
this.port = port;
|
|
52
|
+
this.app = (0, express_1.default)();
|
|
53
|
+
this.app.use(express_1.default.json());
|
|
54
|
+
this.setupRoutes();
|
|
55
|
+
}
|
|
56
|
+
setupRoutes() {
|
|
57
|
+
// Health check
|
|
58
|
+
this.app.get('/health', (_req, res) => {
|
|
59
|
+
res.json({ status: 'ok', database: this.dbPath ? 'connected' : 'not connected' });
|
|
60
|
+
});
|
|
61
|
+
// MCP-style tool listing
|
|
62
|
+
this.app.get('/tools', (_req, res) => {
|
|
63
|
+
res.json({
|
|
64
|
+
tools: [{
|
|
65
|
+
name: 'query_documents',
|
|
66
|
+
description: 'Search through synced documents using semantic vector search.',
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {
|
|
70
|
+
query: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'The natural language query to search for.'
|
|
73
|
+
},
|
|
74
|
+
limit: {
|
|
75
|
+
type: 'number',
|
|
76
|
+
description: 'Maximum number of results to return (1-20). Defaults to 5.',
|
|
77
|
+
default: 5
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
required: ['query']
|
|
81
|
+
}
|
|
82
|
+
}]
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
// Query endpoint
|
|
86
|
+
this.app.post('/query', async (req, res) => {
|
|
87
|
+
try {
|
|
88
|
+
const { query, queryText, limit = 5 } = req.body;
|
|
89
|
+
const searchQuery = query || queryText; // Support both parameter names
|
|
90
|
+
if (!searchQuery) {
|
|
91
|
+
res.status(400).json({ error: 'query is required' });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!this.dbPath) {
|
|
95
|
+
res.status(503).json({ error: 'Database not configured. Please select a database in the app.' });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (!this.openaiApiKey) {
|
|
99
|
+
res.status(503).json({ error: 'OpenAI API key not configured. Please add your API key in the app.' });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const results = await this.queryDatabase(searchQuery, Math.min(limit, 20));
|
|
103
|
+
res.json({
|
|
104
|
+
query: searchQuery,
|
|
105
|
+
count: results.length,
|
|
106
|
+
results
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.error('Query error:', error);
|
|
111
|
+
res.status(500).json({ error: error.message });
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// MCP Streamable HTTP endpoint - proper JSON-RPC handling
|
|
115
|
+
this.app.post('/mcp', async (req, res) => {
|
|
116
|
+
try {
|
|
117
|
+
const request = req.body;
|
|
118
|
+
const { jsonrpc, id, method, params } = request;
|
|
119
|
+
// Get or create session
|
|
120
|
+
let sessionId = req.headers['mcp-session-id'];
|
|
121
|
+
if (!sessionId && method === 'initialize') {
|
|
122
|
+
sessionId = (0, crypto_1.randomUUID)();
|
|
123
|
+
this.sessions.set(sessionId, true);
|
|
124
|
+
}
|
|
125
|
+
// Set session header
|
|
126
|
+
if (sessionId) {
|
|
127
|
+
res.setHeader('mcp-session-id', sessionId);
|
|
128
|
+
}
|
|
129
|
+
console.log(`MCP Request: ${method}`, params ? JSON.stringify(params).substring(0, 100) : '');
|
|
130
|
+
// Handle MCP methods
|
|
131
|
+
switch (method) {
|
|
132
|
+
case 'initialize':
|
|
133
|
+
res.json({
|
|
134
|
+
jsonrpc: '2.0',
|
|
135
|
+
id,
|
|
136
|
+
result: {
|
|
137
|
+
protocolVersion: '2024-11-05',
|
|
138
|
+
capabilities: {
|
|
139
|
+
tools: {}
|
|
140
|
+
},
|
|
141
|
+
serverInfo: {
|
|
142
|
+
name: 'docs4ai',
|
|
143
|
+
version: '1.0.0'
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
case 'notifications/initialized':
|
|
149
|
+
// Client acknowledged initialization - no response needed for notifications
|
|
150
|
+
res.status(204).send();
|
|
151
|
+
return;
|
|
152
|
+
case 'tools/list':
|
|
153
|
+
res.json({
|
|
154
|
+
jsonrpc: '2.0',
|
|
155
|
+
id,
|
|
156
|
+
result: {
|
|
157
|
+
tools: [
|
|
158
|
+
{
|
|
159
|
+
name: 'query_documents',
|
|
160
|
+
description: 'Search through synced documents using semantic vector search. Returns relevant document chunks with their chunk_index and total_chunks so you can retrieve additional chunks from the same document using get_chunks.',
|
|
161
|
+
inputSchema: {
|
|
162
|
+
type: 'object',
|
|
163
|
+
properties: {
|
|
164
|
+
query: {
|
|
165
|
+
type: 'string',
|
|
166
|
+
description: 'The natural language search query'
|
|
167
|
+
},
|
|
168
|
+
limit: {
|
|
169
|
+
type: 'number',
|
|
170
|
+
description: 'Maximum number of results to return (1-20)',
|
|
171
|
+
default: 5
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
required: ['query']
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'get_chunks',
|
|
179
|
+
description: 'Retrieve specific chunks from a document by file path. Use this to get more context from a document after finding it with query_documents. You can retrieve individual chunks or a range of chunks.',
|
|
180
|
+
inputSchema: {
|
|
181
|
+
type: 'object',
|
|
182
|
+
properties: {
|
|
183
|
+
file_path: {
|
|
184
|
+
type: 'string',
|
|
185
|
+
description: 'The file path (url) of the document to retrieve chunks from'
|
|
186
|
+
},
|
|
187
|
+
chunk_indices: {
|
|
188
|
+
type: 'array',
|
|
189
|
+
items: { type: 'number' },
|
|
190
|
+
description: 'Array of chunk indices to retrieve (0-based). If not provided, returns all chunks.'
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
required: ['file_path']
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
return;
|
|
200
|
+
case 'tools/call':
|
|
201
|
+
const { name, arguments: args } = params || {};
|
|
202
|
+
if (name === 'query_documents') {
|
|
203
|
+
if (!this.dbPath || !this.openaiApiKey) {
|
|
204
|
+
res.json({
|
|
205
|
+
jsonrpc: '2.0',
|
|
206
|
+
id,
|
|
207
|
+
result: {
|
|
208
|
+
content: [{ type: 'text', text: 'Error: Database or API key not configured in Docs4ai app. Please configure them in the app settings.' }],
|
|
209
|
+
isError: true
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const query = args?.query || args?.queryText || '';
|
|
215
|
+
if (!query) {
|
|
216
|
+
res.json({
|
|
217
|
+
jsonrpc: '2.0',
|
|
218
|
+
id,
|
|
219
|
+
result: {
|
|
220
|
+
content: [{ type: 'text', text: 'Error: query parameter is required' }],
|
|
221
|
+
isError: true
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const results = await this.queryDatabase(query, Math.min(args?.limit || 5, 20));
|
|
228
|
+
if (results.length === 0) {
|
|
229
|
+
res.json({
|
|
230
|
+
jsonrpc: '2.0',
|
|
231
|
+
id,
|
|
232
|
+
result: {
|
|
233
|
+
content: [{ type: 'text', text: `No results found for "${query}"` }]
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const formatted = results.map((r, i) => `**Result ${i + 1}** (distance: ${r.distance.toFixed(4)})\n` +
|
|
239
|
+
`File: ${r.url}\n` +
|
|
240
|
+
`Section: ${r.section}\n` +
|
|
241
|
+
`Chunk: ${r.chunk_index + 1} of ${r.total_chunks}\n` +
|
|
242
|
+
`${r.content}\n` +
|
|
243
|
+
`---`).join('\n\n');
|
|
244
|
+
res.json({
|
|
245
|
+
jsonrpc: '2.0',
|
|
246
|
+
id,
|
|
247
|
+
result: {
|
|
248
|
+
content: [{ type: 'text', text: `Found ${results.length} results for "${query}":\n\n${formatted}` }]
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
catch (queryError) {
|
|
254
|
+
res.json({
|
|
255
|
+
jsonrpc: '2.0',
|
|
256
|
+
id,
|
|
257
|
+
result: {
|
|
258
|
+
content: [{ type: 'text', text: `Error querying database: ${queryError.message}` }],
|
|
259
|
+
isError: true
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (name === 'get_chunks') {
|
|
266
|
+
if (!this.dbPath) {
|
|
267
|
+
res.json({
|
|
268
|
+
jsonrpc: '2.0',
|
|
269
|
+
id,
|
|
270
|
+
result: {
|
|
271
|
+
content: [{ type: 'text', text: 'Error: Database not configured in Docs4ai app.' }],
|
|
272
|
+
isError: true
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const filePath = args?.file_path || '';
|
|
278
|
+
if (!filePath) {
|
|
279
|
+
res.json({
|
|
280
|
+
jsonrpc: '2.0',
|
|
281
|
+
id,
|
|
282
|
+
result: {
|
|
283
|
+
content: [{ type: 'text', text: 'Error: file_path parameter is required' }],
|
|
284
|
+
isError: true
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
const chunks = this.getChunksForFile(filePath, args?.chunk_indices);
|
|
291
|
+
if (chunks.length === 0) {
|
|
292
|
+
res.json({
|
|
293
|
+
jsonrpc: '2.0',
|
|
294
|
+
id,
|
|
295
|
+
result: {
|
|
296
|
+
content: [{ type: 'text', text: `No chunks found for file: ${filePath}` }]
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const formatted = chunks.map((c) => `**Chunk ${c.chunk_index + 1} of ${c.total_chunks}**\n` +
|
|
302
|
+
`Section: ${c.section}\n` +
|
|
303
|
+
`${c.content}\n` +
|
|
304
|
+
`---`).join('\n\n');
|
|
305
|
+
res.json({
|
|
306
|
+
jsonrpc: '2.0',
|
|
307
|
+
id,
|
|
308
|
+
result: {
|
|
309
|
+
content: [{ type: 'text', text: `Retrieved ${chunks.length} chunk(s) from "${filePath}":\n\n${formatted}` }]
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
catch (chunkError) {
|
|
315
|
+
res.json({
|
|
316
|
+
jsonrpc: '2.0',
|
|
317
|
+
id,
|
|
318
|
+
result: {
|
|
319
|
+
content: [{ type: 'text', text: `Error retrieving chunks: ${chunkError.message}` }],
|
|
320
|
+
isError: true
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
res.json({
|
|
327
|
+
jsonrpc: '2.0',
|
|
328
|
+
id,
|
|
329
|
+
error: {
|
|
330
|
+
code: -32601,
|
|
331
|
+
message: `Unknown tool: ${name}`
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
return;
|
|
335
|
+
default:
|
|
336
|
+
res.json({
|
|
337
|
+
jsonrpc: '2.0',
|
|
338
|
+
id,
|
|
339
|
+
error: {
|
|
340
|
+
code: -32601,
|
|
341
|
+
message: `Method not found: ${method}`
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
console.error('MCP error:', error);
|
|
349
|
+
res.json({
|
|
350
|
+
jsonrpc: '2.0',
|
|
351
|
+
id: req.body?.id,
|
|
352
|
+
error: {
|
|
353
|
+
code: -32603,
|
|
354
|
+
message: error.message
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
// Handle GET for SSE (not implemented, return 404)
|
|
360
|
+
this.app.get('/mcp', (_req, res) => {
|
|
361
|
+
res.status(404).json({ error: 'SSE not supported, use POST for Streamable HTTP' });
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
async queryDatabase(queryText, limit) {
|
|
365
|
+
if (!this.dbPath || !this.openaiApiKey) {
|
|
366
|
+
throw new Error('Database or API key not configured');
|
|
367
|
+
}
|
|
368
|
+
// Generate embedding for query
|
|
369
|
+
const openai = new openai_1.default({ apiKey: this.openaiApiKey });
|
|
370
|
+
const embeddingResponse = await openai.embeddings.create({
|
|
371
|
+
model: 'text-embedding-3-large',
|
|
372
|
+
input: queryText
|
|
373
|
+
});
|
|
374
|
+
const queryEmbedding = embeddingResponse.data[0].embedding;
|
|
375
|
+
// Query database
|
|
376
|
+
const db = new better_sqlite3_1.default(this.dbPath);
|
|
377
|
+
sqliteVec.load(db);
|
|
378
|
+
try {
|
|
379
|
+
const stmt = db.prepare(`
|
|
380
|
+
SELECT
|
|
381
|
+
chunk_id,
|
|
382
|
+
distance,
|
|
383
|
+
content,
|
|
384
|
+
url,
|
|
385
|
+
section,
|
|
386
|
+
heading_hierarchy,
|
|
387
|
+
chunk_index,
|
|
388
|
+
total_chunks
|
|
389
|
+
FROM vec_items
|
|
390
|
+
WHERE embedding MATCH ?
|
|
391
|
+
ORDER BY distance
|
|
392
|
+
LIMIT ?
|
|
393
|
+
`);
|
|
394
|
+
const rows = stmt.all(new Float32Array(queryEmbedding), limit);
|
|
395
|
+
return rows;
|
|
396
|
+
}
|
|
397
|
+
finally {
|
|
398
|
+
db.close();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
getChunksForFile(filePath, chunkIndices) {
|
|
402
|
+
if (!this.dbPath) {
|
|
403
|
+
throw new Error('Database not configured');
|
|
404
|
+
}
|
|
405
|
+
const db = new better_sqlite3_1.default(this.dbPath);
|
|
406
|
+
sqliteVec.load(db);
|
|
407
|
+
try {
|
|
408
|
+
let rows;
|
|
409
|
+
if (chunkIndices && chunkIndices.length > 0) {
|
|
410
|
+
// Get specific chunks by index
|
|
411
|
+
const placeholders = chunkIndices.map(() => '?').join(',');
|
|
412
|
+
const stmt = db.prepare(`
|
|
413
|
+
SELECT
|
|
414
|
+
chunk_id,
|
|
415
|
+
content,
|
|
416
|
+
section,
|
|
417
|
+
heading_hierarchy,
|
|
418
|
+
chunk_index,
|
|
419
|
+
total_chunks
|
|
420
|
+
FROM vec_items
|
|
421
|
+
WHERE url = ? AND chunk_index IN (${placeholders})
|
|
422
|
+
ORDER BY chunk_index
|
|
423
|
+
`);
|
|
424
|
+
rows = stmt.all(filePath, ...chunkIndices);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
// Get all chunks for the file
|
|
428
|
+
const stmt = db.prepare(`
|
|
429
|
+
SELECT
|
|
430
|
+
chunk_id,
|
|
431
|
+
content,
|
|
432
|
+
section,
|
|
433
|
+
heading_hierarchy,
|
|
434
|
+
chunk_index,
|
|
435
|
+
total_chunks
|
|
436
|
+
FROM vec_items
|
|
437
|
+
WHERE url = ?
|
|
438
|
+
ORDER BY chunk_index
|
|
439
|
+
`);
|
|
440
|
+
rows = stmt.all(filePath);
|
|
441
|
+
}
|
|
442
|
+
return rows;
|
|
443
|
+
}
|
|
444
|
+
finally {
|
|
445
|
+
db.close();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
setDatabase(dbPath) {
|
|
449
|
+
this.dbPath = dbPath;
|
|
450
|
+
console.log(`MCP Server: Database set to ${dbPath}`);
|
|
451
|
+
}
|
|
452
|
+
setApiKey(apiKey) {
|
|
453
|
+
this.openaiApiKey = apiKey;
|
|
454
|
+
}
|
|
455
|
+
start() {
|
|
456
|
+
return new Promise((resolve, reject) => {
|
|
457
|
+
try {
|
|
458
|
+
const server = this.app.listen(this.port, () => {
|
|
459
|
+
console.log(`MCP Server running on http://localhost:${this.port}`);
|
|
460
|
+
resolve();
|
|
461
|
+
});
|
|
462
|
+
server.on('error', (error) => {
|
|
463
|
+
if (error.code === 'EADDRINUSE') {
|
|
464
|
+
console.error(`MCP Server: Port ${this.port} already in use`);
|
|
465
|
+
}
|
|
466
|
+
reject(error);
|
|
467
|
+
});
|
|
468
|
+
this.server = server;
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
reject(error);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
stop() {
|
|
476
|
+
return new Promise((resolve) => {
|
|
477
|
+
if (this.server) {
|
|
478
|
+
this.server.close(() => {
|
|
479
|
+
console.log('MCP Server stopped');
|
|
480
|
+
this.server = null;
|
|
481
|
+
resolve();
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
resolve();
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
isRunning() {
|
|
490
|
+
return this.server !== null;
|
|
491
|
+
}
|
|
492
|
+
getPort() {
|
|
493
|
+
return this.port;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
exports.McpServer = McpServer;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const electron_1 = require("electron");
|
|
4
|
+
electron_1.contextBridge.exposeInMainWorld('api', {
|
|
5
|
+
getSettings: () => electron_1.ipcRenderer.invoke('get-settings'),
|
|
6
|
+
saveSettings: (settings) => electron_1.ipcRenderer.invoke('save-settings', settings),
|
|
7
|
+
selectFolder: () => electron_1.ipcRenderer.invoke('select-folder'),
|
|
8
|
+
selectDatabase: () => electron_1.ipcRenderer.invoke('select-database'),
|
|
9
|
+
getStats: () => electron_1.ipcRenderer.invoke('get-stats'),
|
|
10
|
+
getFiles: () => electron_1.ipcRenderer.invoke('get-files'),
|
|
11
|
+
getChunks: (filePath) => electron_1.ipcRenderer.invoke('get-chunks', filePath),
|
|
12
|
+
startWatching: () => electron_1.ipcRenderer.invoke('start-watching'),
|
|
13
|
+
stopWatching: () => electron_1.ipcRenderer.invoke('stop-watching'),
|
|
14
|
+
forceSync: () => electron_1.ipcRenderer.invoke('force-sync'),
|
|
15
|
+
startMcpServer: () => electron_1.ipcRenderer.invoke('start-mcp-server'),
|
|
16
|
+
stopMcpServer: () => electron_1.ipcRenderer.invoke('stop-mcp-server'),
|
|
17
|
+
getMcpStatus: () => electron_1.ipcRenderer.invoke('get-mcp-status'),
|
|
18
|
+
onStatsUpdate: (callback) => {
|
|
19
|
+
electron_1.ipcRenderer.on('stats-update', (_event, stats) => callback(stats));
|
|
20
|
+
},
|
|
21
|
+
onApiKeyError: (callback) => {
|
|
22
|
+
electron_1.ipcRenderer.on('api-key-error', (_event, message) => callback(message));
|
|
23
|
+
}
|
|
24
|
+
});
|