@mnemonik/scanner 5.151.0 → 5.151.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +99 -0
- package/dist/client.js +140 -0
- package/dist/client.js.map +1 -0
- package/dist/daemon.d.ts +51 -0
- package/dist/daemon.js +550 -0
- package/dist/daemon.js.map +1 -0
- package/dist/discovery.d.ts +21 -0
- package/dist/discovery.js +107 -0
- package/dist/discovery.js.map +1 -0
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +233 -0
- package/dist/doctor.js.map +1 -0
- package/dist/fileLog.d.ts +17 -0
- package/dist/fileLog.js +70 -0
- package/dist/fileLog.js.map +1 -0
- package/dist/git.d.ts +31 -0
- package/dist/git.js +111 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +399 -0
- package/dist/index.js.map +1 -0
- package/dist/pid.d.ts +1 -0
- package/dist/pid.js +37 -0
- package/dist/pid.js.map +1 -0
- package/dist/watcher.d.ts +30 -0
- package/dist/watcher.js +214 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +7 -3
- package/src/client.ts +0 -216
- package/src/daemon.ts +0 -679
- package/src/discovery.ts +0 -124
- package/src/doctor.ts +0 -239
- package/src/fileLog.ts +0 -67
- package/src/git.ts +0 -122
- package/src/index.ts +0 -446
- package/src/pid.ts +0 -37
- package/src/watcher.ts +0 -219
- package/tests/validateServerUrl.test.ts +0 -41
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -13
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { CodeScanner, scrubSecrets } from '@mnemonik/shared';
|
|
5
|
+
/** Maximum raw content bytes accepted by the server's scanFileSchema. */
|
|
6
|
+
const MAX_PUSH_CONTENT_BYTES = 5_000_000;
|
|
7
|
+
import { MnemonikClient } from './client.js';
|
|
8
|
+
import { FileWatcher } from './watcher.js';
|
|
9
|
+
import { ProjectDiscovery } from './discovery.js';
|
|
10
|
+
import { probeGit, fetchCommits } from './git.js';
|
|
11
|
+
export class ScannerDaemon {
|
|
12
|
+
config;
|
|
13
|
+
client;
|
|
14
|
+
scanner;
|
|
15
|
+
projects = new Map();
|
|
16
|
+
refreshTimer = null;
|
|
17
|
+
heartbeatTimer = null;
|
|
18
|
+
discovery;
|
|
19
|
+
refreshIntervalMs;
|
|
20
|
+
maxConcurrentScans;
|
|
21
|
+
scannerVersion;
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.config = config;
|
|
24
|
+
this.client = new MnemonikClient(config.serverUrl, config.apiKey);
|
|
25
|
+
this.scanner = new CodeScanner();
|
|
26
|
+
this.discovery = new ProjectDiscovery(config.roots);
|
|
27
|
+
this.refreshIntervalMs = config.refreshIntervalMs ?? 300_000; // 5 min
|
|
28
|
+
this.maxConcurrentScans = config.maxConcurrentScans ?? 5;
|
|
29
|
+
}
|
|
30
|
+
async start() {
|
|
31
|
+
console.log(`[scanner] Starting daemon`);
|
|
32
|
+
console.log(`[scanner] Server: ${this.config.serverUrl}`);
|
|
33
|
+
console.log(`[scanner] Roots: ${this.config.roots.join(', ')}`);
|
|
34
|
+
await this.waitForServer();
|
|
35
|
+
await this.refreshProjects();
|
|
36
|
+
this.refreshTimer = setInterval(() => {
|
|
37
|
+
this.refreshProjects().catch((err) => {
|
|
38
|
+
console.warn('[scanner] Refresh failed:', err.message);
|
|
39
|
+
});
|
|
40
|
+
}, this.refreshIntervalMs);
|
|
41
|
+
this.refreshTimer.unref();
|
|
42
|
+
// Send heartbeat immediately, then every 60s so session_bootstrap
|
|
43
|
+
// can reliably detect daemon liveness without waiting for a file scan.
|
|
44
|
+
await this.sendHeartbeats();
|
|
45
|
+
this.heartbeatTimer = setInterval(() => {
|
|
46
|
+
this.sendHeartbeats().catch((err) => {
|
|
47
|
+
console.warn('[scanner] Heartbeat failed:', err.message);
|
|
48
|
+
});
|
|
49
|
+
}, 60_000);
|
|
50
|
+
this.heartbeatTimer.unref();
|
|
51
|
+
console.log('[scanner] Watching for changes.');
|
|
52
|
+
}
|
|
53
|
+
async stop() {
|
|
54
|
+
if (this.refreshTimer) {
|
|
55
|
+
clearInterval(this.refreshTimer);
|
|
56
|
+
this.refreshTimer = null;
|
|
57
|
+
}
|
|
58
|
+
if (this.heartbeatTimer) {
|
|
59
|
+
clearInterval(this.heartbeatTimer);
|
|
60
|
+
this.heartbeatTimer = null;
|
|
61
|
+
}
|
|
62
|
+
for (const project of this.projects.values()) {
|
|
63
|
+
project.watcher.stop();
|
|
64
|
+
if (project.retryTimer)
|
|
65
|
+
clearInterval(project.retryTimer);
|
|
66
|
+
}
|
|
67
|
+
this.projects.clear();
|
|
68
|
+
console.log('[scanner] Daemon stopped');
|
|
69
|
+
}
|
|
70
|
+
async sendHeartbeats() {
|
|
71
|
+
const version = await this.getScannerVersion();
|
|
72
|
+
for (const { projectId } of this.projects.values()) {
|
|
73
|
+
await this.client
|
|
74
|
+
.sendHeartbeat(projectId, {
|
|
75
|
+
scope: 'global',
|
|
76
|
+
...(version && { version }),
|
|
77
|
+
})
|
|
78
|
+
.catch((err) => {
|
|
79
|
+
console.warn(`[scanner] Heartbeat failed for ${projectId}:`, err.message);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async getScannerVersion() {
|
|
84
|
+
if (this.scannerVersion !== undefined) {
|
|
85
|
+
return this.scannerVersion ?? undefined;
|
|
86
|
+
}
|
|
87
|
+
this.scannerVersion = await readScannerPackageVersion();
|
|
88
|
+
return this.scannerVersion ?? undefined;
|
|
89
|
+
}
|
|
90
|
+
getWatchedProjects() {
|
|
91
|
+
return Array.from(this.projects.values()).map((p) => ({
|
|
92
|
+
projectId: p.projectId,
|
|
93
|
+
path: p.path,
|
|
94
|
+
name: p.name,
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Discover projects from configured roots and reconcile with current watch list.
|
|
99
|
+
*/
|
|
100
|
+
async refreshProjects() {
|
|
101
|
+
const discovered = await this.discovery.discover();
|
|
102
|
+
const discoveredMap = new Map(discovered.map((d) => [d.projectId, d]));
|
|
103
|
+
// Remove projects no longer discovered
|
|
104
|
+
for (const [projectId, project] of this.projects) {
|
|
105
|
+
if (!discoveredMap.has(projectId)) {
|
|
106
|
+
console.log(`[scanner] Project removed: ${project.name ?? projectId} (${project.path})`);
|
|
107
|
+
project.watcher.stop();
|
|
108
|
+
if (project.retryTimer)
|
|
109
|
+
clearInterval(project.retryTimer);
|
|
110
|
+
this.projects.delete(projectId);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Add new projects or handle path changes
|
|
114
|
+
for (const discovered_project of discoveredMap.values()) {
|
|
115
|
+
const existing = this.projects.get(discovered_project.projectId);
|
|
116
|
+
if (!existing) {
|
|
117
|
+
// New project
|
|
118
|
+
await this.addProject(discovered_project);
|
|
119
|
+
}
|
|
120
|
+
else if (existing.path !== discovered_project.path) {
|
|
121
|
+
// Path changed (folder renamed/moved)
|
|
122
|
+
console.log(`[scanner] Project moved: ${existing.name ?? existing.projectId} ` +
|
|
123
|
+
`${existing.path} → ${discovered_project.path}`);
|
|
124
|
+
existing.watcher.stop();
|
|
125
|
+
if (existing.retryTimer)
|
|
126
|
+
clearInterval(existing.retryTimer);
|
|
127
|
+
this.projects.delete(discovered_project.projectId);
|
|
128
|
+
await this.addProject(discovered_project);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Periodic removed-files diff for already-watched projects. Cheap
|
|
132
|
+
// path-only walk (no chunking, no file reads), compare against the
|
|
133
|
+
// in-memory snapshot from last tick, send only the disappeared paths.
|
|
134
|
+
// Picks up deletions that fs.watch delivered as unlink events but
|
|
135
|
+
// that handleChanges couldn't push (it only knows how to push
|
|
136
|
+
// chunked content, not "this file no longer exists").
|
|
137
|
+
for (const project of this.projects.values()) {
|
|
138
|
+
try {
|
|
139
|
+
const status = await this.client.getStatus(project.projectId);
|
|
140
|
+
if (hasInvalidatedHashes(status.fileHashes)) {
|
|
141
|
+
if (project.fullRescanInProgress) {
|
|
142
|
+
console.log(`[scanner] [${project.projectId.slice(0, 8)}] full rescan already running; skipping refresh tick`);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
console.log(`[scanner] [${project.projectId.slice(0, 8)}] server hashes invalidated; running full rescan`);
|
|
146
|
+
project.fullRescanInProgress = true;
|
|
147
|
+
try {
|
|
148
|
+
const scanResult = await this.initialScan(project.projectId, project.path);
|
|
149
|
+
project.isGitRepo = scanResult.isGitRepo;
|
|
150
|
+
project.gitMiningEnabled = scanResult.gitMiningEnabled;
|
|
151
|
+
project.lastMinedCommit = scanResult.lastMinedCommit;
|
|
152
|
+
project.lastInventory = new Set(scanResult.currentPaths);
|
|
153
|
+
// Re-seed from the post-rescan authoritative view so an invalidated
|
|
154
|
+
// hash never leaves a stale "already pushed" entry behind.
|
|
155
|
+
project.cachedFileHashes = new Map(scanResult.fileHashes);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
project.fullRescanInProgress = false;
|
|
159
|
+
}
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const scanned = await this.scanner.listFilesWithStatus(project.path);
|
|
163
|
+
// I1: include authority file paths so they are never treated as removed
|
|
164
|
+
const authority = await this.scanner.collectAuthorityFilesWithStatus(project.path);
|
|
165
|
+
// A partial walk (fs error swallowed mid-traversal) reads as mass
|
|
166
|
+
// deletion: files that still exist on disk vanish from currentSet
|
|
167
|
+
// and would be deprecated server-side. Withhold the diff AND keep
|
|
168
|
+
// lastInventory untouched so the next clean tick diffs against the
|
|
169
|
+
// trusted baseline.
|
|
170
|
+
if (!scanned.complete || !authority.complete) {
|
|
171
|
+
console.warn(`[scanner] [${project.projectId.slice(0, 8)}] incomplete file walk; withholding removed-files diff this tick`);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const currentSet = new Set([...scanned.paths, ...authority.files.map((a) => a.path)]);
|
|
175
|
+
const removedFiles = [...project.lastInventory].filter((p) => !currentSet.has(p));
|
|
176
|
+
await this.sendRemovedFiles(project.projectId, removedFiles);
|
|
177
|
+
project.lastInventory = currentSet;
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
console.warn(`[scanner] [${project.projectId.slice(0, 8)}] periodic removed-files report failed: ${err.message}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
console.log(`[scanner] Watching ${this.projects.size} project(s)`);
|
|
184
|
+
}
|
|
185
|
+
async addProject(discovered) {
|
|
186
|
+
const label = discovered.projectName ?? discovered.projectId.slice(0, 8);
|
|
187
|
+
console.log(`[scanner] Adding project: ${label} (${discovered.path})`);
|
|
188
|
+
let scanResult = {
|
|
189
|
+
isGitRepo: false,
|
|
190
|
+
gitMiningEnabled: false,
|
|
191
|
+
lastMinedCommit: null,
|
|
192
|
+
currentPaths: [],
|
|
193
|
+
fileHashes: new Map(),
|
|
194
|
+
};
|
|
195
|
+
try {
|
|
196
|
+
scanResult = await this.initialScan(discovered.projectId, discovered.path);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
console.warn(`[scanner] Initial scan failed for ${label}:`, err.message);
|
|
200
|
+
}
|
|
201
|
+
const watcher = new FileWatcher(discovered.path, (changedFiles) => this.handleChanges(discovered.projectId, discovered.path, changedFiles), 500, (err) => {
|
|
202
|
+
console.warn(`[scanner] Root watcher error for ${label}: ${err.message}. Removing project.`);
|
|
203
|
+
const project = this.projects.get(discovered.projectId);
|
|
204
|
+
if (project) {
|
|
205
|
+
project.watcher.stop();
|
|
206
|
+
if (project.retryTimer)
|
|
207
|
+
clearInterval(project.retryTimer);
|
|
208
|
+
this.projects.delete(discovered.projectId);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
try {
|
|
212
|
+
await watcher.start();
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
console.warn(`[scanner] Failed to start watcher for ${label}:`, err.message);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
this.projects.set(discovered.projectId, {
|
|
219
|
+
projectId: discovered.projectId,
|
|
220
|
+
path: discovered.path,
|
|
221
|
+
name: discovered.projectName,
|
|
222
|
+
watcher,
|
|
223
|
+
pendingRetries: new Set(),
|
|
224
|
+
retryTimer: null,
|
|
225
|
+
isGitRepo: scanResult.isGitRepo,
|
|
226
|
+
gitMiningEnabled: scanResult.gitMiningEnabled,
|
|
227
|
+
lastMinedCommit: scanResult.lastMinedCommit,
|
|
228
|
+
fullRescanInProgress: false,
|
|
229
|
+
lastInventory: new Set(scanResult.currentPaths),
|
|
230
|
+
cachedFileHashes: new Map(scanResult.fileHashes),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async waitForServer() {
|
|
234
|
+
let logged = false;
|
|
235
|
+
let delay = 3000;
|
|
236
|
+
const maxDelay = 30000;
|
|
237
|
+
const maxRetries = 20;
|
|
238
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
239
|
+
if (await this.client.healthCheck()) {
|
|
240
|
+
if (logged)
|
|
241
|
+
console.log('[scanner] Server is back');
|
|
242
|
+
else
|
|
243
|
+
console.log('[scanner] Server health check passed');
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (!logged) {
|
|
247
|
+
console.log('[scanner] Server unreachable, waiting...');
|
|
248
|
+
logged = true;
|
|
249
|
+
}
|
|
250
|
+
const jitter = delay * (0.5 + Math.random());
|
|
251
|
+
await new Promise((r) => setTimeout(r, jitter));
|
|
252
|
+
delay = Math.min(delay * 1.5, maxDelay);
|
|
253
|
+
}
|
|
254
|
+
throw new Error(`Server unreachable after ${maxRetries} attempts`);
|
|
255
|
+
}
|
|
256
|
+
async initialScan(projectId, projectRoot) {
|
|
257
|
+
const startTime = Date.now();
|
|
258
|
+
const scan = await this.scanner.scanDirectoryWithStatus(projectRoot);
|
|
259
|
+
const chunks = scan.chunks;
|
|
260
|
+
const status = await this.client.getStatus(projectId);
|
|
261
|
+
const files = await this.groupChunksByFile(chunks, projectRoot);
|
|
262
|
+
const authority = await this.collectAuthorityPushFiles(projectRoot);
|
|
263
|
+
// C1: dedupe — chunk-scanned entries win over authority duplicates
|
|
264
|
+
// (e.g. setup.py matches both .py includeExtensions and AUTHORITY_FILE_MATCHERS)
|
|
265
|
+
const seen = new Set(files.map((f) => f.path));
|
|
266
|
+
const allFiles = [...files, ...authority.files.filter((a) => !seen.has(a.path))];
|
|
267
|
+
const filesToPush = allFiles.filter((f) => {
|
|
268
|
+
const serverHash = status.fileHashes.get(f.path);
|
|
269
|
+
return !serverHash || serverHash !== f.hash;
|
|
270
|
+
});
|
|
271
|
+
// collect new commits since last mine when this is a git repo and
|
|
272
|
+
// the server has mining enabled for this project (Pro+ tier + not opted out).
|
|
273
|
+
const isGitRepo = await probeGit(projectRoot);
|
|
274
|
+
const commits = isGitRepo && status.gitMining.enabled
|
|
275
|
+
? await this.collectCommits(projectRoot, status.gitMining.lastMinedCommit)
|
|
276
|
+
: [];
|
|
277
|
+
// push when there's anything to send — file changes OR new
|
|
278
|
+
// commits. Previously a stable repo with new commits never pushed,
|
|
279
|
+
// silently losing those commits forever.
|
|
280
|
+
let watermark = status.gitMining.lastMinedCommit;
|
|
281
|
+
let pushOk = false;
|
|
282
|
+
if (filesToPush.length > 0 || commits.length > 0) {
|
|
283
|
+
console.log(`[scanner] Pushing ${filesToPush.length} changed files + ${commits.length} commits for ${projectId.slice(0, 8)}...`);
|
|
284
|
+
try {
|
|
285
|
+
await this.client.pushFiles(projectId, filesToPush, commits);
|
|
286
|
+
pushOk = true;
|
|
287
|
+
// Only advance the watermark after a successful push that actually
|
|
288
|
+
// carried the commits.
|
|
289
|
+
if (commits.length > 0) {
|
|
290
|
+
watermark = commits[0]?.sha ?? watermark;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
console.warn(`[scanner] Initial push failed for ${projectId.slice(0, 8)}: ${err.message}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Initial-scan removed-files diff: bootstrap "before" from the
|
|
298
|
+
// server's known-paths set (status.fileHashes is what the server
|
|
299
|
+
// currently has for this project). Anything the server knows that
|
|
300
|
+
// the daemon doesn't see on disk now = removed. Closes the
|
|
301
|
+
// daemon-was-offline case and the newly-added-ignore-pattern case.
|
|
302
|
+
// Gated on walk completeness: adds/changes above are not
|
|
303
|
+
// completeness-sensitive (a missing file just isn't pushed this
|
|
304
|
+
// pass), but deriving removals from a partial walk deprecates
|
|
305
|
+
// memories for files that still exist. Skip the diff entirely when
|
|
306
|
+
// either walk was truncated.
|
|
307
|
+
const currentPaths = allFiles.map((f) => f.path);
|
|
308
|
+
const currentPathSet = new Set(currentPaths);
|
|
309
|
+
let removedFiles = [];
|
|
310
|
+
if (scan.complete && authority.complete) {
|
|
311
|
+
removedFiles = [...status.fileHashes.keys()].filter((p) => !currentPathSet.has(p));
|
|
312
|
+
await this.sendRemovedFiles(projectId, removedFiles).catch((err) => {
|
|
313
|
+
console.warn(`[scanner] [${projectId.slice(0, 8)}] removedFiles report failed (non-blocking): ${err.message}`);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
console.warn(`[scanner] [${projectId.slice(0, 8)}] incomplete file walk; withholding removed-files diff for this scan`);
|
|
318
|
+
}
|
|
319
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
320
|
+
console.log(`[scanner] Scan complete for ${projectId.slice(0, 8)}: ` +
|
|
321
|
+
`${chunks.length} chunks, ${filesToPush.length} pushed, ${commits.length} commits (${duration}s)`);
|
|
322
|
+
// Build the server's authoritative file_path -> hash view after this scan:
|
|
323
|
+
// the server's pre-scan set, overlaid with the files we just pushed (only
|
|
324
|
+
// if the push succeeded), minus the files we just reported removed.
|
|
325
|
+
const fileHashes = new Map(status.fileHashes);
|
|
326
|
+
if (pushOk) {
|
|
327
|
+
for (const f of filesToPush)
|
|
328
|
+
fileHashes.set(f.path, f.hash);
|
|
329
|
+
}
|
|
330
|
+
for (const p of removedFiles)
|
|
331
|
+
fileHashes.delete(p);
|
|
332
|
+
return {
|
|
333
|
+
isGitRepo,
|
|
334
|
+
gitMiningEnabled: status.gitMining.enabled,
|
|
335
|
+
lastMinedCommit: watermark,
|
|
336
|
+
currentPaths,
|
|
337
|
+
fileHashes,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Send the explicit list of paths that vanished since the previous scan.
|
|
342
|
+
* Empty lists short-circuit so we don't pay an HTTP round-trip when
|
|
343
|
+
* nothing was removed this tick.
|
|
344
|
+
*/
|
|
345
|
+
async sendRemovedFiles(projectId, removedFiles) {
|
|
346
|
+
if (removedFiles.length === 0)
|
|
347
|
+
return;
|
|
348
|
+
const result = await this.client.reportRemovedFiles(projectId, removedFiles);
|
|
349
|
+
if (result.deprecated > 0 || result.couplingsRemoved > 0) {
|
|
350
|
+
console.log(`[scanner] [${projectId.slice(0, 8)}] Removed: ` +
|
|
351
|
+
`${removedFiles.length} paths sent, ${result.deprecated} memories deprecated, ` +
|
|
352
|
+
`${result.couplingsRemoved} couplings removed`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Fetch commits since the last mine. Swallows errors — a git failure must
|
|
357
|
+
* not block the file scan push.
|
|
358
|
+
*/
|
|
359
|
+
async collectCommits(projectRoot, lastMinedCommit) {
|
|
360
|
+
try {
|
|
361
|
+
const commits = await fetchCommits(projectRoot, lastMinedCommit);
|
|
362
|
+
return commits;
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
console.warn(`[scanner] git log failed in ${projectRoot}:`, err.message);
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
async handleChanges(projectId, projectRoot, changedFiles) {
|
|
370
|
+
const project = this.projects.get(projectId);
|
|
371
|
+
if (!project)
|
|
372
|
+
return;
|
|
373
|
+
try {
|
|
374
|
+
const absPaths = changedFiles.map((rel) => join(projectRoot, rel));
|
|
375
|
+
const chunks = await this.scanner.scanFiles(absPaths, projectRoot);
|
|
376
|
+
if (chunks.length === 0)
|
|
377
|
+
return;
|
|
378
|
+
const files = await this.groupChunksByFile(chunks, projectRoot);
|
|
379
|
+
const authority = await this.collectAuthorityPushFiles(projectRoot);
|
|
380
|
+
// C1: dedupe — chunk-scanned entries win over authority duplicates
|
|
381
|
+
const seen = new Set(files.map((f) => f.path));
|
|
382
|
+
const allFiles = [...files, ...authority.files.filter((a) => !seen.has(a.path))];
|
|
383
|
+
// Skip files whose content hash matches what the server already has —
|
|
384
|
+
// fs.watch fires on no-op events (editor flush, chmod, mtime touch) that
|
|
385
|
+
// would otherwise re-chunk, re-push, and re-ingest an identical file
|
|
386
|
+
// (and re-trigger the server's doc-truth diff pipeline). Files this
|
|
387
|
+
// daemon has never confirmed-pushed have no cache entry and are always
|
|
388
|
+
// pushed; the cache is updated only on a successful push and reset on a
|
|
389
|
+
// full rescan, so a needed push is never dropped.
|
|
390
|
+
const toPush = allFiles.filter((f) => project.cachedFileHashes.get(f.path) !== f.hash);
|
|
391
|
+
const skipped = allFiles.length - toPush.length;
|
|
392
|
+
if (skipped > 0) {
|
|
393
|
+
console.log(`[scanner] [${projectId.slice(0, 8)}] Skipped ${skipped} unchanged file(s)`);
|
|
394
|
+
}
|
|
395
|
+
// attach any new commits since our cached watermark. Only the
|
|
396
|
+
// first push carries them (idempotent jobId on the server collapses
|
|
397
|
+
// duplicates anyway, but one payload saves bandwidth).
|
|
398
|
+
const commits = project.isGitRepo && project.gitMiningEnabled
|
|
399
|
+
? await this.collectCommits(projectRoot, project.lastMinedCommit)
|
|
400
|
+
: [];
|
|
401
|
+
const batchSize = 25;
|
|
402
|
+
const succeededPaths = new Set();
|
|
403
|
+
let hadFailure = false;
|
|
404
|
+
let firstBatchSucceeded = false;
|
|
405
|
+
if (toPush.length > 0) {
|
|
406
|
+
for (let i = 0; i < toPush.length; i += batchSize) {
|
|
407
|
+
const batch = toPush.slice(i, i + batchSize);
|
|
408
|
+
try {
|
|
409
|
+
// Only attach commits to the first batch.
|
|
410
|
+
await this.client.pushFiles(projectId, batch, i === 0 ? commits : undefined);
|
|
411
|
+
for (const f of batch) {
|
|
412
|
+
succeededPaths.add(f.path);
|
|
413
|
+
project.cachedFileHashes.set(f.path, f.hash);
|
|
414
|
+
}
|
|
415
|
+
if (i === 0)
|
|
416
|
+
firstBatchSucceeded = true;
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
hadFailure = true;
|
|
420
|
+
for (const f of batch)
|
|
421
|
+
project.pendingRetries.add(f.path);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else if (commits.length > 0) {
|
|
426
|
+
// No file changes but new commits exist — fire a commit-only push so
|
|
427
|
+
// they reach the server. 1 schema allows files=[].
|
|
428
|
+
try {
|
|
429
|
+
await this.client.pushFiles(projectId, [], commits);
|
|
430
|
+
firstBatchSucceeded = true;
|
|
431
|
+
}
|
|
432
|
+
catch (err) {
|
|
433
|
+
hadFailure = true;
|
|
434
|
+
console.warn(`[scanner] [${projectId.slice(0, 8)}] Commit-only push failed: ${err.message}`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
// Advance the watermark only if commits actually made it to the server.
|
|
438
|
+
if (commits.length > 0 && firstBatchSucceeded) {
|
|
439
|
+
project.lastMinedCommit = commits[0]?.sha ?? project.lastMinedCommit;
|
|
440
|
+
}
|
|
441
|
+
if (succeededPaths.size > 0) {
|
|
442
|
+
console.log(`[scanner] [${projectId.slice(0, 8)}] Pushed ${succeededPaths.size} file(s): ${[...succeededPaths].join(', ')}`);
|
|
443
|
+
}
|
|
444
|
+
if (hadFailure) {
|
|
445
|
+
console.warn(`[scanner] [${projectId.slice(0, 8)}] ${project.pendingRetries.size} file(s) failed, queued for retry`);
|
|
446
|
+
this.startRetryLoop(project);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
catch (err) {
|
|
450
|
+
console.error(`[scanner] [${projectId.slice(0, 8)}] Error handling changes:`, err);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
startRetryLoop(project) {
|
|
454
|
+
if (project.retryTimer)
|
|
455
|
+
return;
|
|
456
|
+
project.retryTimer = setInterval(async () => {
|
|
457
|
+
if (project.pendingRetries.size === 0) {
|
|
458
|
+
if (project.retryTimer)
|
|
459
|
+
clearInterval(project.retryTimer);
|
|
460
|
+
project.retryTimer = null;
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const files = [...project.pendingRetries];
|
|
464
|
+
project.pendingRetries.clear();
|
|
465
|
+
await this.handleChanges(project.projectId, project.path, files);
|
|
466
|
+
}, 10_000);
|
|
467
|
+
project.retryTimer.unref();
|
|
468
|
+
}
|
|
469
|
+
async collectAuthorityPushFiles(projectRoot) {
|
|
470
|
+
const authority = await this.scanner.collectAuthorityFilesWithStatus(projectRoot);
|
|
471
|
+
return {
|
|
472
|
+
// Whole-file `content` must honor the same daemon-side redaction
|
|
473
|
+
// invariant as chunk content: no credential leaves this process.
|
|
474
|
+
// `hash` stays computed over the RAW bytes so change detection
|
|
475
|
+
// against the server's known-hash map is unaffected; the server
|
|
476
|
+
// recomputes its own stored content_hash from what arrives.
|
|
477
|
+
files: authority.files.map((f) => ({
|
|
478
|
+
path: f.path,
|
|
479
|
+
hash: f.hash,
|
|
480
|
+
chunks: [],
|
|
481
|
+
content: f.content === undefined ? undefined : scrubSecrets(f.content),
|
|
482
|
+
})),
|
|
483
|
+
complete: authority.complete,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
async groupChunksByFile(chunks, projectRoot) {
|
|
487
|
+
const fileMap = new Map();
|
|
488
|
+
for (const chunk of chunks) {
|
|
489
|
+
const key = chunk.filePath;
|
|
490
|
+
if (!fileMap.has(key)) {
|
|
491
|
+
fileMap.set(key, { path: key, hash: '', chunks: [] });
|
|
492
|
+
}
|
|
493
|
+
const file = fileMap.get(key);
|
|
494
|
+
file.chunks.push({
|
|
495
|
+
content: chunk.content,
|
|
496
|
+
startLine: chunk.startLine,
|
|
497
|
+
endLine: chunk.endLine,
|
|
498
|
+
chunkType: chunk.chunkType,
|
|
499
|
+
language: chunk.language,
|
|
500
|
+
contentHash: chunk.contentHash,
|
|
501
|
+
metadata: chunk.metadata,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
for (const file of fileMap.values()) {
|
|
505
|
+
try {
|
|
506
|
+
const absPath = join(projectRoot, file.path);
|
|
507
|
+
const raw = await readFile(absPath, 'utf-8');
|
|
508
|
+
// Hash the RAW bytes (change detection against the server's
|
|
509
|
+
// known-hash map keys on what's on disk), but never ship them:
|
|
510
|
+
// whole-file `content` honors the same daemon-side redaction
|
|
511
|
+
// invariant as chunk content. The server recomputes its stored
|
|
512
|
+
// content_hash from the scrubbed bytes it receives.
|
|
513
|
+
file.hash = createHash('sha256').update(raw).digest('hex');
|
|
514
|
+
// C2: drop content if it would exceed the server schema cap (5MB)
|
|
515
|
+
file.content = raw.length <= MAX_PUSH_CONTENT_BYTES ? scrubSecrets(raw) : undefined;
|
|
516
|
+
}
|
|
517
|
+
catch (err) {
|
|
518
|
+
console.warn(`[scanner] Cannot read ${file.path} for hashing, using chunk-based fallback`, {
|
|
519
|
+
error: err instanceof Error ? err.message : String(err),
|
|
520
|
+
});
|
|
521
|
+
// Chunk content is already scrubbed by CodeScanner; scrubSecrets is
|
|
522
|
+
// idempotent so re-applying keeps the invariant explicit.
|
|
523
|
+
const allContent = file.chunks.map((c) => c.content).join('\n');
|
|
524
|
+
file.hash = createHash('sha256').update(allContent).digest('hex');
|
|
525
|
+
// C2: drop content if it would exceed the server schema cap (5MB)
|
|
526
|
+
file.content =
|
|
527
|
+
allContent.length <= MAX_PUSH_CONTENT_BYTES ? scrubSecrets(allContent) : undefined;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return Array.from(fileMap.values());
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function hasInvalidatedHashes(fileHashes) {
|
|
534
|
+
for (const hash of fileHashes.values()) {
|
|
535
|
+
if (hash === '')
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
async function readScannerPackageVersion() {
|
|
541
|
+
try {
|
|
542
|
+
const raw = await readFile(new URL('../package.json', import.meta.url), 'utf-8');
|
|
543
|
+
const parsed = JSON.parse(raw);
|
|
544
|
+
return typeof parsed.version === 'string' ? parsed.version : null;
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface DiscoveredProject {
|
|
2
|
+
projectId: string;
|
|
3
|
+
path: string;
|
|
4
|
+
projectName?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class ProjectDiscovery {
|
|
7
|
+
private roots;
|
|
8
|
+
private maxDepth;
|
|
9
|
+
private timeoutMs;
|
|
10
|
+
constructor(roots: string[], options?: {
|
|
11
|
+
maxDepth?: number;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Discover all projects with .mnemonik.json files under the configured roots.
|
|
16
|
+
* Deduplicates by projectId (same project found at multiple paths = first wins).
|
|
17
|
+
*/
|
|
18
|
+
discover(): Promise<DiscoveredProject[]>;
|
|
19
|
+
private walkWithTimeout;
|
|
20
|
+
private walk;
|
|
21
|
+
}
|