@mrxkun/mcfast-mcp 4.0.6 → 4.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +93 -34
- package/src/memory/memory-engine.js +28 -38
- package/src/memory/stores/database.js +6 -2
- package/src/memory/utils/daily-logs.js +6 -22
- package/src/memory/utils/indexer.js +6 -5
- package/src/memory/utils/sync-engine.js +7 -18
- package/src/utils/audit-queue.js +127 -0
- package/src/utils/colors.js +31 -0
- package/src/utils/context-prefetcher.js +170 -0
- package/src/utils/intelligence-cache.js +114 -0
- package/src/utils/parallel-search.js +130 -0
- package/src/utils/streaming-api.js +168 -0
- package/src/utils/streaming.js +180 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrxkun/mcfast-mcp",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.11",
|
|
4
4
|
"description": "Ultra-fast code editing with WASM acceleration, fuzzy patching, multi-layer caching, and 8 unified tools. Optimized for AI code assistants with 80-98% latency reduction. Phase 4: ML Intelligence Layer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/index.js
CHANGED
|
@@ -41,6 +41,10 @@ import {
|
|
|
41
41
|
import { safeEdit } from './utils/backup.js';
|
|
42
42
|
import { formatError } from './utils/error-formatter.js';
|
|
43
43
|
import { MemoryEngine } from './memory/index.js';
|
|
44
|
+
import { getAuditQueue } from './utils/audit-queue.js';
|
|
45
|
+
import { getIntelligenceCache } from './utils/intelligence-cache.js';
|
|
46
|
+
import { getContextPrefetcher } from './utils/context-prefetcher.js';
|
|
47
|
+
import { parallelMemorySearch } from './utils/parallel-search.js';
|
|
44
48
|
|
|
45
49
|
const execAsync = promisify(exec);
|
|
46
50
|
|
|
@@ -108,6 +112,62 @@ async function searchMemoryContext(instruction, maxResults = 5) {
|
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Auto-trigger intelligence tools after successful edit
|
|
117
|
+
* Runs async in background to not block user
|
|
118
|
+
*/
|
|
119
|
+
async function autoTriggerIntelligence(filePath, instruction) {
|
|
120
|
+
try {
|
|
121
|
+
// Only trigger if intelligence is enabled
|
|
122
|
+
if (process.env.MCFAST_INTELLIGENCE_AUTO !== 'true') {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const engine = await getMemoryEngine();
|
|
127
|
+
const cache = getIntelligenceCache();
|
|
128
|
+
|
|
129
|
+
// 1. Detect patterns (cached for 5 minutes)
|
|
130
|
+
const patternCacheKey = { file: filePath, type: 'patterns' };
|
|
131
|
+
let patterns = cache.get('patterns', patternCacheKey);
|
|
132
|
+
|
|
133
|
+
if (!patterns) {
|
|
134
|
+
patterns = await engine.detectPatterns();
|
|
135
|
+
if (patterns && patterns.length > 0) {
|
|
136
|
+
cache.set('patterns', patternCacheKey, patterns);
|
|
137
|
+
console.error(`${colors.cyan}[Intelligence]${colors.reset} 💡 Detected ${patterns.length} patterns`);
|
|
138
|
+
|
|
139
|
+
// Log high-confidence patterns
|
|
140
|
+
patterns.filter(p => p.confidence > 0.8).forEach(p => {
|
|
141
|
+
console.error(`${colors.yellow}[Pattern]${colors.reset} ${p.type}: ${p.message}`);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 2. Get suggestions (cached for 5 minutes)
|
|
147
|
+
const suggestionCacheKey = { file: filePath, type: 'suggestions' };
|
|
148
|
+
let suggestions = cache.get('suggestions', suggestionCacheKey);
|
|
149
|
+
|
|
150
|
+
if (!suggestions) {
|
|
151
|
+
suggestions = await engine.getSuggestions({ currentFile: filePath });
|
|
152
|
+
if (suggestions && suggestions.length > 0) {
|
|
153
|
+
cache.set('suggestions', suggestionCacheKey, suggestions);
|
|
154
|
+
console.error(`${colors.cyan}[Intelligence]${colors.reset} 💡 ${suggestions.length} suggestions available`);
|
|
155
|
+
|
|
156
|
+
// Show top suggestions
|
|
157
|
+
suggestions.slice(0, 3).forEach(s => {
|
|
158
|
+
console.error(`${colors.yellow}[Suggestion]${colors.reset} ${s.type}: ${s.message}`);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
} catch (error) {
|
|
164
|
+
// Silent fail - intelligence should not break the tool
|
|
165
|
+
if (VERBOSE) {
|
|
166
|
+
console.error(`${colors.yellow}[Intelligence]${colors.reset} Auto-trigger failed: ${error.message}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
111
171
|
/**
|
|
112
172
|
* Log edit to memory after successful edit
|
|
113
173
|
*/
|
|
@@ -728,6 +788,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
728
788
|
/**
|
|
729
789
|
* Report audit log to Dashboard
|
|
730
790
|
* Sends operation logs to mcfast Dashboard for monitoring
|
|
791
|
+
* Now uses AuditQueue for batching to reduce HTTP overhead
|
|
731
792
|
*/
|
|
732
793
|
async function reportAudit(data) {
|
|
733
794
|
try {
|
|
@@ -755,42 +816,33 @@ async function reportAudit(data) {
|
|
|
755
816
|
latency_ms: data.latency_ms || 0,
|
|
756
817
|
instruction: data.instruction || '',
|
|
757
818
|
files_count: data.files_count || 0,
|
|
819
|
+
diff_size: data.diff_size || 0, // ✅ Thêm diff_size
|
|
758
820
|
input_tokens: data.input_tokens || 0,
|
|
759
821
|
output_tokens: data.output_tokens || 0,
|
|
760
822
|
strategy: data.strategy || null,
|
|
761
823
|
error: data.error || null,
|
|
824
|
+
error_message: data.error || null, // ✅ Thêm error_message cho server
|
|
825
|
+
result_summary: data.result_summary || null, // ✅ Thêm result_summary
|
|
762
826
|
metadata: data.metadata || {}
|
|
763
827
|
};
|
|
764
828
|
|
|
765
|
-
//
|
|
766
|
-
const
|
|
829
|
+
// Use AuditQueue for batching instead of immediate send
|
|
830
|
+
const queue = getAuditQueue({
|
|
831
|
+
token: TOKEN,
|
|
832
|
+
apiUrl: process.env.MCFAST_DASHBOARD_URL || 'https://mcfast.vercel.app/api/v1/logs/audit',
|
|
833
|
+
batchSize: 5,
|
|
834
|
+
flushInterval: 5000,
|
|
835
|
+
verbose: VERBOSE
|
|
836
|
+
});
|
|
767
837
|
|
|
768
|
-
|
|
769
|
-
console.error(`${colors.dim}[Audit] Sending to: ${DASHBOARD_API_URL}${colors.reset}`);
|
|
770
|
-
console.error(`${colors.dim}[Audit] Tool: ${auditData.tool}, Status: ${auditData.status}${colors.reset}`);
|
|
771
|
-
}
|
|
838
|
+
queue.add(auditData);
|
|
772
839
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
'Content-Type': 'application/json',
|
|
777
|
-
'Authorization': `Bearer ${TOKEN}`
|
|
778
|
-
},
|
|
779
|
-
body: JSON.stringify(auditData)
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
if (!response.ok) {
|
|
783
|
-
const errorText = await response.text().catch(() => 'Unknown error');
|
|
784
|
-
if (VERBOSE) {
|
|
785
|
-
console.error(`${colors.yellow}[Audit] Failed - HTTP ${response.status}: ${errorText}${colors.reset}`);
|
|
786
|
-
}
|
|
787
|
-
} else {
|
|
788
|
-
if (VERBOSE) {
|
|
789
|
-
console.error(`${colors.green}[Audit] Logged successfully ✓${colors.reset}`);
|
|
790
|
-
}
|
|
840
|
+
if (VERBOSE) {
|
|
841
|
+
const stats = queue.getStats();
|
|
842
|
+
console.error(`${colors.dim}[Audit] Queued for batching. Queue size: ${stats.queued}${colors.reset}`);
|
|
791
843
|
}
|
|
792
844
|
|
|
793
|
-
// Also log to local file if configured
|
|
845
|
+
// Also log to local file if configured (immediate)
|
|
794
846
|
if (process.env.MCFAST_AUDIT_FILE) {
|
|
795
847
|
const logLine = JSON.stringify(auditData) + '\n';
|
|
796
848
|
await fs.appendFile(process.env.MCFAST_AUDIT_FILE, logLine).catch((err) => {
|
|
@@ -1631,6 +1683,12 @@ async function handleEdit({ instruction, files, code_edit, dryRun = false }) {
|
|
|
1631
1683
|
durationMs: Date.now() - editStartTime
|
|
1632
1684
|
});
|
|
1633
1685
|
|
|
1686
|
+
// Auto-trigger intelligence tools in background (non-blocking)
|
|
1687
|
+
if (!result.isError) {
|
|
1688
|
+
const firstFile = Object.keys(files)[0];
|
|
1689
|
+
autoTriggerIntelligence(firstFile, instruction).catch(() => {});
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1634
1692
|
return result;
|
|
1635
1693
|
}
|
|
1636
1694
|
|
|
@@ -1864,7 +1922,7 @@ async function handleReadFileInternal({ path: filePath, start_line, end_line, ma
|
|
|
1864
1922
|
} else if (startLine && currentLine >= startLine) {
|
|
1865
1923
|
lines.push(line);
|
|
1866
1924
|
if (max_lines && lines.length >= max_lines) break;
|
|
1867
|
-
} else if (lines.length <
|
|
1925
|
+
} else if (max_lines && lines.length < max_lines) {
|
|
1868
1926
|
lines.push(line);
|
|
1869
1927
|
} else {
|
|
1870
1928
|
break;
|
|
@@ -2155,7 +2213,7 @@ async function handleSearchCode({ query, files, regex = false, caseSensitive = f
|
|
|
2155
2213
|
});
|
|
2156
2214
|
return {
|
|
2157
2215
|
content: [{ type: "text", text: `❌ Search error: ${error.message}` }],
|
|
2158
|
-
isError: true
|
|
2216
|
+
isError: true,
|
|
2159
2217
|
};
|
|
2160
2218
|
}
|
|
2161
2219
|
}
|
|
@@ -2169,7 +2227,7 @@ async function handleSearchCodeAI({ query, files, contextLines = 2 }) {
|
|
|
2169
2227
|
if (!TOKEN) {
|
|
2170
2228
|
return {
|
|
2171
2229
|
content: [{ type: "text", text: "❌ Error: MCFAST_TOKEN is missing. Please set it in your MCP config." }],
|
|
2172
|
-
isError: true
|
|
2230
|
+
isError: true,
|
|
2173
2231
|
};
|
|
2174
2232
|
}
|
|
2175
2233
|
try {
|
|
@@ -2744,13 +2802,14 @@ async function handleMemorySearch({ query, type = 'all', maxResults = 6, minScor
|
|
|
2744
2802
|
score: result.finalScore || result.score
|
|
2745
2803
|
}));
|
|
2746
2804
|
} else {
|
|
2747
|
-
|
|
2748
|
-
|
|
2805
|
+
// Use parallel search for 'all' type - faster!
|
|
2806
|
+
const parallelResult = await parallelMemorySearch(engine, query, maxResults);
|
|
2807
|
+
results = parallelResult.results.map(result => ({
|
|
2749
2808
|
type: 'chunk',
|
|
2750
|
-
content: result.
|
|
2751
|
-
file: result.filePath || result.path,
|
|
2752
|
-
score: result.
|
|
2753
|
-
method:
|
|
2809
|
+
content: result.content || result.code,
|
|
2810
|
+
file: result.file || result.filePath || result.path,
|
|
2811
|
+
score: result._score || result.score || 0.5,
|
|
2812
|
+
method: result._source
|
|
2754
2813
|
}));
|
|
2755
2814
|
}
|
|
2756
2815
|
|
|
@@ -13,7 +13,6 @@ import { DailyLogs } from './utils/daily-logs.js';
|
|
|
13
13
|
import { SyncEngine } from './utils/sync-engine.js';
|
|
14
14
|
import { PatternDetector, SuggestionEngine, StrategySelector } from '../intelligence/index.js';
|
|
15
15
|
import path from 'path';
|
|
16
|
-
import os from 'os';
|
|
17
16
|
|
|
18
17
|
export class MemoryEngine {
|
|
19
18
|
constructor(options = {}) {
|
|
@@ -23,25 +22,19 @@ export class MemoryEngine {
|
|
|
23
22
|
this.projectPath = null;
|
|
24
23
|
this.isInitialized = false;
|
|
25
24
|
|
|
26
|
-
// Memory path
|
|
27
|
-
this.memoryPath = options.memoryPath ||
|
|
25
|
+
// Memory path - now stored in project directory
|
|
26
|
+
this.memoryPath = options.memoryPath || null; // Will be set on initialize
|
|
28
27
|
|
|
29
28
|
// Daily Logs - auto-generated markdown notes
|
|
30
|
-
this.dailyLogs =
|
|
31
|
-
memoryPath: this.memoryPath
|
|
32
|
-
});
|
|
29
|
+
this.dailyLogs = null; // Will be initialized on initialize
|
|
33
30
|
|
|
34
31
|
// Sync Engine - local ↔ cloud (optional, auto-detect from MCFAST_TOKEN)
|
|
35
32
|
this.syncEngine = null;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
baseUrl: options.baseUrl || process.env.MCFAST_DASHBOARD_URL || 'https://mcfast.vercel.app',
|
|
42
|
-
syncInterval: options.syncInterval || 5 * 60 * 1000
|
|
43
|
-
});
|
|
44
|
-
}
|
|
33
|
+
this.syncEngineOptions = {
|
|
34
|
+
apiKey: options.apiKey || process.env.MCFAST_TOKEN,
|
|
35
|
+
baseUrl: options.baseUrl || process.env.MCFAST_DASHBOARD_URL || 'https://mcfast.vercel.app',
|
|
36
|
+
syncInterval: options.syncInterval || 5 * 60 * 1000
|
|
37
|
+
};
|
|
45
38
|
|
|
46
39
|
// Sử dụng UltraEnhancedEmbedder cho 90% accuracy
|
|
47
40
|
this.embedder = new UltraEnhancedEmbedder({
|
|
@@ -69,41 +62,38 @@ export class MemoryEngine {
|
|
|
69
62
|
this.patternDetector = null;
|
|
70
63
|
this.suggestionEngine = null;
|
|
71
64
|
this.strategySelector = null;
|
|
72
|
-
|
|
73
|
-
if (this.intelligenceEnabled) {
|
|
74
|
-
this.patternDetector = new PatternDetector({ db: this.db });
|
|
75
|
-
this.suggestionEngine = new SuggestionEngine({
|
|
76
|
-
db: this.db,
|
|
77
|
-
memoryEngine: this
|
|
78
|
-
});
|
|
79
|
-
this.strategySelector = new StrategySelector({ db: this.db });
|
|
80
|
-
}
|
|
81
65
|
}
|
|
82
66
|
|
|
83
67
|
async initialize(projectPath) {
|
|
84
68
|
if (this.isInitialized) return;
|
|
85
69
|
|
|
86
70
|
this.projectPath = projectPath;
|
|
87
|
-
await this.db.initialize();
|
|
88
71
|
|
|
89
|
-
//
|
|
90
|
-
if (this.
|
|
91
|
-
|
|
92
|
-
console.log('[MemoryEngine] Smart router config loaded');
|
|
72
|
+
// Set memory path to project directory
|
|
73
|
+
if (!this.memoryPath) {
|
|
74
|
+
this.memoryPath = path.join(projectPath, '.mcfast', 'memory');
|
|
93
75
|
}
|
|
94
76
|
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
console.log('[MemoryEngine] ✅ Connected to Dashboard');
|
|
99
|
-
} else {
|
|
100
|
-
console.log('[MemoryEngine] ⚠️ Dashboard connection failed, using offline mode');
|
|
77
|
+
// Update database path if not explicitly set
|
|
78
|
+
if (!this.db.dbPath) {
|
|
79
|
+
this.db.dbPath = path.join(this.memoryPath, 'index.db');
|
|
101
80
|
}
|
|
102
81
|
|
|
82
|
+
await this.db.initialize();
|
|
83
|
+
|
|
84
|
+
// Initialize Daily Logs with project-specific path
|
|
85
|
+
this.dailyLogs = new DailyLogs({
|
|
86
|
+
memoryPath: this.memoryPath
|
|
87
|
+
});
|
|
88
|
+
|
|
103
89
|
// Initialize Sync Engine if configured
|
|
104
|
-
if (this.
|
|
105
|
-
|
|
106
|
-
|
|
90
|
+
if (this.syncEngineOptions.apiKey && this.syncEngineOptions.enableSync !== false) {
|
|
91
|
+
this.syncEngine = new SyncEngine({
|
|
92
|
+
memoryPath: this.memoryPath,
|
|
93
|
+
apiKey: this.syncEngineOptions.apiKey,
|
|
94
|
+
baseUrl: this.syncEngineOptions.baseUrl,
|
|
95
|
+
syncInterval: this.syncEngineOptions.syncInterval
|
|
96
|
+
});
|
|
107
97
|
}
|
|
108
98
|
|
|
109
99
|
this.watcher = new FileWatcher(projectPath, this);
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
3
|
import fs from 'fs/promises';
|
|
5
4
|
|
|
6
5
|
export class MemoryDatabase {
|
|
7
|
-
constructor(dbPath =
|
|
6
|
+
constructor(dbPath = null) {
|
|
8
7
|
this.dbPath = dbPath;
|
|
9
8
|
this.db = null;
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
async initialize() {
|
|
12
|
+
// If dbPath not set, create in current directory
|
|
13
|
+
if (!this.dbPath) {
|
|
14
|
+
this.dbPath = path.join(process.cwd(), '.mcfast', 'memory', 'index.db');
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
await fs.mkdir(path.dirname(this.dbPath), { recursive: true });
|
|
14
18
|
this.db = new Database(this.dbPath);
|
|
15
19
|
this.db.pragma('journal_mode = WAL');
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import os from 'os';
|
|
9
8
|
|
|
10
9
|
export class DailyLogs {
|
|
11
10
|
constructor(options = {}) {
|
|
12
|
-
this.memoryPath = options.memoryPath
|
|
13
|
-
this.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
this.memoryPath = options.memoryPath;
|
|
12
|
+
if (!this.memoryPath) {
|
|
13
|
+
throw new Error('memoryPath is required for DailyLogs');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Ensure directory exists
|
|
17
17
|
if (!fs.existsSync(this.memoryPath)) {
|
|
18
18
|
fs.mkdirSync(this.memoryPath, { recursive: true });
|
|
19
19
|
}
|
|
@@ -47,8 +47,6 @@ export class DailyLogs {
|
|
|
47
47
|
* Log a file creation event
|
|
48
48
|
*/
|
|
49
49
|
logFileCreated(filePath, facts = []) {
|
|
50
|
-
this.ensureMemoryDir();
|
|
51
|
-
|
|
52
50
|
const content = [
|
|
53
51
|
`## ${this.formatTime()} - File Created`,
|
|
54
52
|
`**File:** \`${filePath}\``,
|
|
@@ -63,8 +61,6 @@ export class DailyLogs {
|
|
|
63
61
|
* Log a file modification event
|
|
64
62
|
*/
|
|
65
63
|
logFileModified(filePath, changes = {}) {
|
|
66
|
-
this.ensureMemoryDir();
|
|
67
|
-
|
|
68
64
|
const parts = [`**File:** \`${filePath}\``];
|
|
69
65
|
|
|
70
66
|
if (changes.added && changes.added.length > 0) {
|
|
@@ -92,8 +88,6 @@ export class DailyLogs {
|
|
|
92
88
|
* Log a file deletion event
|
|
93
89
|
*/
|
|
94
90
|
logFileDeleted(filePath) {
|
|
95
|
-
this.ensureMemoryDir();
|
|
96
|
-
|
|
97
91
|
const content = [
|
|
98
92
|
`## ${this.formatTime()} - File Deleted`,
|
|
99
93
|
`**File:** \`${filePath}\``,
|
|
@@ -107,8 +101,6 @@ export class DailyLogs {
|
|
|
107
101
|
* Log an edit session
|
|
108
102
|
*/
|
|
109
103
|
logEdit(edit) {
|
|
110
|
-
this.ensureMemoryDir();
|
|
111
|
-
|
|
112
104
|
const {
|
|
113
105
|
instruction,
|
|
114
106
|
files = [],
|
|
@@ -139,8 +131,6 @@ export class DailyLogs {
|
|
|
139
131
|
* Log a search query
|
|
140
132
|
*/
|
|
141
133
|
logSearch(query, resultsCount, duration) {
|
|
142
|
-
this.ensureMemoryDir();
|
|
143
|
-
|
|
144
134
|
const content = [
|
|
145
135
|
`## ${this.formatTime()} - Search`,
|
|
146
136
|
`**Query:** "${query}"`,
|
|
@@ -156,8 +146,6 @@ export class DailyLogs {
|
|
|
156
146
|
* Add a curated memory entry to MEMORY.md
|
|
157
147
|
*/
|
|
158
148
|
addCuratedMemory(title, content, tags = []) {
|
|
159
|
-
this.ensureMemoryDir();
|
|
160
|
-
|
|
161
149
|
const memoryPath = this.getMemoryFilePath();
|
|
162
150
|
const timestamp = new Date().toISOString();
|
|
163
151
|
|
|
@@ -219,8 +207,6 @@ export class DailyLogs {
|
|
|
219
207
|
* Get logs for a date range
|
|
220
208
|
*/
|
|
221
209
|
getLogsInRange(startDate, endDate) {
|
|
222
|
-
this.ensureMemoryDir();
|
|
223
|
-
|
|
224
210
|
const logs = [];
|
|
225
211
|
const files = fs.readdirSync(this.memoryPath)
|
|
226
212
|
.filter(f => f.match(/^\d{4}-\d{2}-\d{2}\.md$/))
|
|
@@ -241,8 +227,6 @@ export class DailyLogs {
|
|
|
241
227
|
* Clean up old logs (keep last N days)
|
|
242
228
|
*/
|
|
243
229
|
cleanupOldLogs(keepDays = 30) {
|
|
244
|
-
this.ensureMemoryDir();
|
|
245
|
-
|
|
246
230
|
const cutoffDate = new Date();
|
|
247
231
|
cutoffDate.setDate(cutoffDate.getDate() - keepDays);
|
|
248
232
|
const cutoffStr = cutoffDate.toISOString().split('T')[0].replace(/-/g, '');
|
|
@@ -7,6 +7,7 @@ import { parse } from '../../strategies/tree-sitter/index.js';
|
|
|
7
7
|
import { detectLanguage } from '../../strategies/syntax-validator.js';
|
|
8
8
|
import { Chunker } from './chunker.js';
|
|
9
9
|
import { Embedder } from './embedder.js';
|
|
10
|
+
import { getQuery } from '../../strategies/tree-sitter/languages.js';
|
|
10
11
|
import crypto from 'crypto';
|
|
11
12
|
|
|
12
13
|
export class CodeIndexer {
|
|
@@ -58,14 +59,14 @@ export class CodeIndexer {
|
|
|
58
59
|
const fileId = this.generateFileId(filePath);
|
|
59
60
|
|
|
60
61
|
try {
|
|
61
|
-
|
|
62
|
-
const
|
|
62
|
+
// Use getQuery to compile the query properly
|
|
63
|
+
const query = await getQuery(language, 'definitions');
|
|
63
64
|
|
|
64
|
-
if (
|
|
65
|
-
const captures =
|
|
65
|
+
if (query) {
|
|
66
|
+
const captures = query.captures(ast.rootNode);
|
|
66
67
|
|
|
67
68
|
for (const capture of captures) {
|
|
68
|
-
if (capture.name === 'name') {
|
|
69
|
+
if (capture.name === 'name' || capture.name === 'function') {
|
|
69
70
|
facts.push({
|
|
70
71
|
id: this.generateFactId(fileId, capture.node.text, 'function'),
|
|
71
72
|
file_id: fileId,
|
|
@@ -6,30 +6,19 @@
|
|
|
6
6
|
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
|
-
import os from 'os';
|
|
10
9
|
import crypto from 'crypto';
|
|
11
10
|
|
|
12
11
|
export class SyncEngine {
|
|
13
12
|
constructor(options = {}) {
|
|
14
|
-
this.memoryPath = options.memoryPath
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
this.apiKey = options.apiKey || process.env.MCFAST_TOKEN;
|
|
13
|
+
this.memoryPath = options.memoryPath;
|
|
14
|
+
if (!this.memoryPath) {
|
|
15
|
+
throw new Error('memoryPath is required for SyncEngine');
|
|
16
|
+
}
|
|
19
17
|
|
|
18
|
+
this.apiKey = options.apiKey;
|
|
19
|
+
this.baseUrl = options.baseUrl || 'https://mcfast.vercel.app';
|
|
20
20
|
this.syncInterval = options.syncInterval || 5 * 60 * 1000; // 5 minutes
|
|
21
|
-
this.
|
|
22
|
-
this.syncQueue = [];
|
|
23
|
-
this.isOnline = true;
|
|
24
|
-
this.isSyncing = false;
|
|
25
|
-
this.listeners = new Set();
|
|
26
|
-
|
|
27
|
-
// Check online status
|
|
28
|
-
if (typeof window !== 'undefined') {
|
|
29
|
-
this.isOnline = navigator.onLine;
|
|
30
|
-
window.addEventListener('online', () => this.handleOnline());
|
|
31
|
-
window.addEventListener('offline', () => this.handleOffline());
|
|
32
|
-
}
|
|
21
|
+
this.syncTimer = null;
|
|
33
22
|
}
|
|
34
23
|
|
|
35
24
|
/**
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit Queue with Batching
|
|
3
|
+
* Buffers audit logs and sends them in batches to reduce HTTP overhead
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { colors } from './colors.js';
|
|
7
|
+
|
|
8
|
+
export class AuditQueue {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.queue = [];
|
|
11
|
+
this.batchSize = options.batchSize || 5;
|
|
12
|
+
this.flushInterval = options.flushInterval || 5000; // 5 seconds
|
|
13
|
+
this.apiUrl = options.apiUrl || 'https://mcfast.vercel.app/api/v1/logs/batch';
|
|
14
|
+
this.token = options.token || process.env.MCFAST_TOKEN;
|
|
15
|
+
this.verbose = options.verbose || false;
|
|
16
|
+
|
|
17
|
+
// Start flush timer
|
|
18
|
+
this.flushTimer = setInterval(() => this.flush(), this.flushInterval);
|
|
19
|
+
|
|
20
|
+
// Flush on process exit
|
|
21
|
+
process.on('beforeExit', () => this.flushSync());
|
|
22
|
+
process.on('SIGINT', () => this.flushSync());
|
|
23
|
+
process.on('SIGTERM', () => this.flushSync());
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
add(data) {
|
|
27
|
+
// Add timestamp if not present
|
|
28
|
+
const auditData = {
|
|
29
|
+
timestamp: new Date().toISOString(),
|
|
30
|
+
...data
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
this.queue.push(auditData);
|
|
34
|
+
|
|
35
|
+
if (this.verbose) {
|
|
36
|
+
console.error(`${colors.dim}[AuditQueue] Added to queue. Size: ${this.queue.length}${colors.reset}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Flush immediately if batch size reached
|
|
40
|
+
if (this.queue.length >= this.batchSize) {
|
|
41
|
+
this.flush();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async flush() {
|
|
46
|
+
if (this.queue.length === 0) return;
|
|
47
|
+
if (!this.token) return; // Skip if no token
|
|
48
|
+
|
|
49
|
+
// Take batch from queue
|
|
50
|
+
const batch = this.queue.splice(0, this.batchSize);
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
if (this.verbose) {
|
|
54
|
+
console.error(`${colors.dim}[AuditQueue] Flushing ${batch.length} logs...${colors.reset}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Send batch using the batch API
|
|
58
|
+
const response = await fetch(this.apiUrl, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
'Content-Type': 'application/json',
|
|
62
|
+
'Authorization': `Bearer ${this.token}`
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify({ logs: batch, stream: false })
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
// Put back in queue if failed (will retry next flush)
|
|
69
|
+
this.queue.unshift(...batch);
|
|
70
|
+
if (this.verbose) {
|
|
71
|
+
console.error(`${colors.yellow}[AuditQueue] Flush failed, will retry${colors.reset}`);
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
const result = await response.json();
|
|
75
|
+
if (this.verbose) {
|
|
76
|
+
console.error(`${colors.green}[AuditQueue] Flushed ${result.stored || batch.length} logs ✓${colors.reset}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
// Put back in queue if failed
|
|
81
|
+
this.queue.unshift(...batch);
|
|
82
|
+
if (this.verbose) {
|
|
83
|
+
console.error(`${colors.yellow}[AuditQueue] Network error, will retry: ${error.message}${colors.reset}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
flushSync() {
|
|
89
|
+
// Synchronous flush for process exit
|
|
90
|
+
if (this.queue.length === 0) return;
|
|
91
|
+
|
|
92
|
+
// In real implementation, you might want to use sync HTTP or write to file
|
|
93
|
+
if (this.verbose) {
|
|
94
|
+
console.error(`${colors.dim}[AuditQueue] ${this.queue.length} logs remaining on exit${colors.reset}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getStats() {
|
|
99
|
+
return {
|
|
100
|
+
queued: this.queue.length,
|
|
101
|
+
batchSize: this.batchSize,
|
|
102
|
+
flushInterval: this.flushInterval
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
destroy() {
|
|
107
|
+
clearInterval(this.flushTimer);
|
|
108
|
+
return this.flush();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Singleton instance
|
|
113
|
+
let auditQueue = null;
|
|
114
|
+
|
|
115
|
+
export function getAuditQueue(options = {}) {
|
|
116
|
+
if (!auditQueue) {
|
|
117
|
+
auditQueue = new AuditQueue(options);
|
|
118
|
+
}
|
|
119
|
+
return auditQueue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function resetAuditQueue() {
|
|
123
|
+
if (auditQueue) {
|
|
124
|
+
auditQueue.destroy();
|
|
125
|
+
auditQueue = null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color utilities for terminal output
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const colors = {
|
|
6
|
+
reset: '\x1b[0m',
|
|
7
|
+
bright: '\x1b[1m',
|
|
8
|
+
dim: '\x1b[2m',
|
|
9
|
+
underscore: '\x1b[4m',
|
|
10
|
+
blink: '\x1b[5m',
|
|
11
|
+
reverse: '\x1b[7m',
|
|
12
|
+
hidden: '\x1b[8m',
|
|
13
|
+
|
|
14
|
+
black: '\x1b[30m',
|
|
15
|
+
red: '\x1b[31m',
|
|
16
|
+
green: '\x1b[32m',
|
|
17
|
+
yellow: '\x1b[33m',
|
|
18
|
+
blue: '\x1b[34m',
|
|
19
|
+
magenta: '\x1b[35m',
|
|
20
|
+
cyan: '\x1b[36m',
|
|
21
|
+
white: '\x1b[37m',
|
|
22
|
+
|
|
23
|
+
bgBlack: '\x1b[40m',
|
|
24
|
+
bgRed: '\x1b[41m',
|
|
25
|
+
bgGreen: '\x1b[42m',
|
|
26
|
+
bgYellow: '\x1b[43m',
|
|
27
|
+
bgBlue: '\x1b[44m',
|
|
28
|
+
bgMagenta: '\x1b[45m',
|
|
29
|
+
bgCyan: '\x1b[46m',
|
|
30
|
+
bgWhite: '\x1b[47m'
|
|
31
|
+
};
|