@mnemonik/scanner 5.136.2 → 5.151.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/dist/index.js DELETED
@@ -1,399 +0,0 @@
1
- #!/usr/bin/env node
2
- import { readFile, writeFile, unlink, mkdir, stat, chmod } from 'fs/promises';
3
- import { existsSync } from 'fs';
4
- import { join } from 'path';
5
- import { homedir } from 'os';
6
- import { ScannerDaemon } from './daemon.js';
7
- import { installFileLogging } from './fileLog.js';
8
- import { runDoctor } from './doctor.js';
9
- import { pidIsScanner } from './pid.js';
10
- const DEFAULT_SERVER = 'https://api.mnemonik.dev';
11
- const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);
12
- /**
13
- * Reject plaintext http:// server URLs except when talking to a local
14
- * daemon on the same machine (localhost/127.0.0.1/[::1]) — e.g. during
15
- * development against a locally-run server. Everything else must be https://
16
- * since the scanner pushes full file content (see O2/O8b,
17
- * codebase-indexing-audit-2026-07-02.md §5) and a plaintext channel would
18
- * expose that content (and the API key) to network eavesdropping.
19
- * Extracted as a pure function so the guard is unit-testable without
20
- * spinning up the CLI.
21
- */
22
- export function validateServerUrl(url) {
23
- let parsed;
24
- try {
25
- parsed = new URL(url);
26
- }
27
- catch {
28
- return { valid: false, error: `[mnemonik] Invalid --server URL: "${url}"` };
29
- }
30
- if (parsed.protocol !== 'https:' && !LOCAL_HOSTS.has(parsed.hostname)) {
31
- return {
32
- valid: false,
33
- error: `[mnemonik] Refusing insecure server URL "${url}".\n` +
34
- ' Only https:// is allowed for --server, except for localhost/127.0.0.1/[::1]\n' +
35
- ' (used for local development). Scanned file content and your API key would\n' +
36
- ' otherwise be sent in plaintext.',
37
- };
38
- }
39
- return { valid: true };
40
- }
41
- const MNEMONIK_DIR = join(homedir(), '.mnemonik');
42
- const PID_FILE = join(MNEMONIK_DIR, 'daemon.pid');
43
- const LOG_FILE = join(MNEMONIK_DIR, 'scanner.log');
44
- const CONFIG_FILE = join(MNEMONIK_DIR, 'scanner.json');
45
- const MAX_LOG_SIZE = 5 * 1024 * 1024; // 5MB
46
- function parseCliArgs() {
47
- const args = process.argv.slice(2);
48
- const command = (args[0] ?? 'help');
49
- if (!['start', 'stop', 'status', 'log', 'doctor', 'help'].includes(command)) {
50
- return { command: 'help' };
51
- }
52
- let key;
53
- let server;
54
- let roots;
55
- for (let i = 1; i < args.length; i++) {
56
- if (args[i] === '--key' && args[i + 1])
57
- key = args[++i];
58
- else if (args[i] === '--server' && args[i + 1])
59
- server = args[++i];
60
- else if (args[i] === '--roots' && args[i + 1]) {
61
- roots = args[++i].split(',').map((r) => r.trim());
62
- }
63
- }
64
- return { command, key, server, roots };
65
- }
66
- async function readConfig() {
67
- try {
68
- const raw = await readFile(CONFIG_FILE, 'utf-8');
69
- return JSON.parse(raw);
70
- }
71
- catch {
72
- return null;
73
- }
74
- }
75
- async function writeConfig(config) {
76
- await mkdir(MNEMONIK_DIR, { recursive: true });
77
- await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
78
- }
79
- async function checkConfigPermissions() {
80
- try {
81
- const s = await stat(CONFIG_FILE);
82
- // Check if group or other have read/write permissions
83
- const mode = s.mode & 0o077;
84
- if (mode !== 0) {
85
- console.warn(`[mnemonik] WARNING: ${CONFIG_FILE} has overly permissive permissions (${(s.mode & 0o777).toString(8)}).`);
86
- console.warn(`[mnemonik] Fixing to 0600 (owner read/write only).`);
87
- await chmod(CONFIG_FILE, 0o600);
88
- }
89
- }
90
- catch {
91
- // Config file doesn't exist yet
92
- }
93
- }
94
- async function acquireLock(retried = false) {
95
- try {
96
- const { open: fsOpen } = await import('fs/promises');
97
- const { constants } = await import('fs');
98
- const fd = await fsOpen(PID_FILE, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 0o644);
99
- await fd.writeFile(String(process.pid));
100
- await fd.close();
101
- return true;
102
- }
103
- catch (err) {
104
- if (err.code !== 'EEXIST')
105
- return false;
106
- const existing = await readFile(PID_FILE, 'utf-8').catch(() => null);
107
- if (existing) {
108
- const pid = parseInt(existing.trim(), 10);
109
- try {
110
- process.kill(pid, 0);
111
- // Alive is not enough — after PID reuse the number can belong to an
112
- // unrelated process. Only an actual scanner holds the lock.
113
- if (pidIsScanner(pid)) {
114
- return false; // Process alive and is the scanner — lock is valid
115
- }
116
- // Live but foreign PID — treat as stale and reclaim below.
117
- }
118
- catch {
119
- // Holder is dead — remove stale lock and retry once
120
- }
121
- }
122
- if (retried)
123
- return false;
124
- await unlink(PID_FILE).catch(() => { });
125
- return acquireLock(true);
126
- }
127
- }
128
- async function releaseLock() {
129
- await unlink(PID_FILE).catch(() => { });
130
- }
131
- async function readPid() {
132
- try {
133
- const raw = await readFile(PID_FILE, 'utf-8');
134
- const pid = parseInt(raw.trim(), 10);
135
- if (isNaN(pid))
136
- return null;
137
- // Check if process is actually alive
138
- try {
139
- process.kill(pid, 0);
140
- }
141
- catch {
142
- return null; // Stale PID
143
- }
144
- // Alive but foreign (PID reuse) is just as stale — never report it as
145
- // the daemon, and never let handleStop() SIGTERM it.
146
- if (!pidIsScanner(pid))
147
- return null;
148
- return pid;
149
- }
150
- catch {
151
- return null;
152
- }
153
- }
154
- function printHelp() {
155
- console.log(`
156
- mnemonik-scanner - Automatic codebase indexing daemon
157
-
158
- Usage:
159
- mnemonik-scanner start [options] Start the scanner daemon
160
- mnemonik-scanner stop Stop the running daemon
161
- mnemonik-scanner status Show daemon status
162
- mnemonik-scanner log Tail the scanner log file
163
- mnemonik-scanner doctor Check install health (drift detection)
164
- mnemonik-scanner help Show this help
165
-
166
- Options (for start):
167
- --key <api-key> API key (or set MNEMONIK_API_KEY env var)
168
- --server <url> Server URL (default: https://api.mnemonik.dev)
169
- --roots <dirs> Comma-separated directories to scan for projects
170
-
171
- Examples:
172
- mnemonik-scanner start --key mnk_... --roots ~/Projects,~/work
173
- mnemonik-scanner status
174
- mnemonik-scanner stop
175
-
176
- Configuration is saved to ~/.mnemonik/scanner.json after first run.
177
- Subsequent starts use saved config (CLI args override).
178
-
179
- Environment variables:
180
- MNEMONIK_API_KEY API key (takes precedence over config file)
181
- `);
182
- }
183
- async function handleStart(cli) {
184
- // Guardrail — this daemon is meant to run under the systemd user service
185
- // (`systemctl --user start mnemonik-scanner`; see BUILD_AND_DEPLOY.md
186
- // "Scanner daemon"). systemd sets INVOCATION_ID on processes it spawns. A
187
- // manual foreground `start` attaches the daemon to the invoking shell: it
188
- // dies when the shell exits and can spawn duplicate/stale daemons — the exact
189
- // failure mode behind version skew and apparent "restart loops". If a unit
190
- // exists but we were NOT launched by systemd, refuse (overridable).
191
- if (!process.env.INVOCATION_ID && !process.env.MNEMONIK_SCANNER_FOREGROUND) {
192
- const unitPath = join(homedir(), '.config/systemd/user/mnemonik-scanner.service');
193
- if (existsSync(unitPath)) {
194
- console.error('[mnemonik] A systemd unit already manages this scanner.\n' +
195
- ' Start/restart it with:\n' +
196
- ' systemctl --user restart mnemonik-scanner\n' +
197
- ' Running `mnemonik-scanner start` by hand attaches the daemon to your shell — it\n' +
198
- ' dies when the shell exits and can leave duplicate/stale daemons running (version\n' +
199
- ' skew, apparent "restart loops"). To force a foreground run, set\n' +
200
- ' MNEMONIK_SCANNER_FOREGROUND=1.');
201
- process.exit(1);
202
- }
203
- }
204
- const config = await readConfig();
205
- // Resolve API key: env var > CLI > config
206
- const apiKey = process.env.MNEMONIK_API_KEY || cli.key || config?.apiKey;
207
- if (!apiKey) {
208
- console.error('[mnemonik] Missing API key.\n' +
209
- ' Use --key <api-key>, set MNEMONIK_API_KEY env var,\n' +
210
- ' or run once with --key to save to config.');
211
- process.exit(1);
212
- }
213
- const roots = cli.roots || config?.roots;
214
- if (!roots || roots.length === 0) {
215
- console.error('[mnemonik] No roots specified.\n' +
216
- ' Use --roots ~/Projects,~/work to specify directories to scan.');
217
- process.exit(1);
218
- }
219
- const server = cli.server || config?.server || DEFAULT_SERVER;
220
- const serverUrlCheck = validateServerUrl(server);
221
- if (!serverUrlCheck.valid) {
222
- console.error(serverUrlCheck.error);
223
- process.exit(1);
224
- }
225
- // Ensure directories exist
226
- await mkdir(MNEMONIK_DIR, { recursive: true });
227
- // Mirror all console output into scanner.log so `mnemonik-scanner log`
228
- // reflects live activity regardless of supervisor. Under systemd, stdout is
229
- // wired to the journal socket, not this file — without the tee the on-disk
230
- // log goes stale and `log` shows nothing current. Install before any daemon
231
- // output so the whole session is captured.
232
- installFileLogging(LOG_FILE, MAX_LOG_SIZE);
233
- // Save config for future runs (never save env var key to file)
234
- const configToSave = {
235
- roots,
236
- server,
237
- refreshIntervalMs: config?.refreshIntervalMs,
238
- };
239
- // Only save API key to config if it came from CLI (not env var)
240
- if (cli.key) {
241
- configToSave.apiKey = cli.key;
242
- }
243
- else if (config?.apiKey && !process.env.MNEMONIK_API_KEY) {
244
- configToSave.apiKey = config.apiKey;
245
- }
246
- await writeConfig(configToSave);
247
- await checkConfigPermissions();
248
- const locked = await acquireLock();
249
- if (!locked) {
250
- const pid = await readPid();
251
- console.log(`[mnemonik] Scanner daemon already running (PID ${pid}). Use 'mnemonik-scanner stop' first.`);
252
- process.exit(0);
253
- }
254
- const daemon = new ScannerDaemon({
255
- serverUrl: server,
256
- apiKey,
257
- roots,
258
- refreshIntervalMs: config?.refreshIntervalMs,
259
- });
260
- const shutdown = async () => {
261
- console.log('\n[mnemonik] Shutting down...');
262
- await daemon.stop();
263
- await releaseLock();
264
- process.exit(0);
265
- };
266
- process.on('SIGINT', shutdown);
267
- process.on('SIGTERM', shutdown);
268
- process.on('SIGHUP', shutdown); // Graceful restart — external tooling sends SIGHUP then relaunches
269
- try {
270
- await daemon.start();
271
- }
272
- catch (err) {
273
- console.error(`[mnemonik] ${err.message}`);
274
- await releaseLock();
275
- process.exit(1);
276
- }
277
- }
278
- async function handleStop() {
279
- const pid = await readPid();
280
- if (!pid) {
281
- console.log('[mnemonik] Scanner daemon is not running.');
282
- // Clean up stale PID file if it exists
283
- await unlink(PID_FILE).catch(() => { });
284
- return;
285
- }
286
- try {
287
- process.kill(pid, 'SIGTERM');
288
- console.log(`[mnemonik] Sent SIGTERM to daemon (PID ${pid}).`);
289
- // Wait for process to exit (up to 5 seconds)
290
- for (let i = 0; i < 50; i++) {
291
- await new Promise((r) => setTimeout(r, 100));
292
- try {
293
- process.kill(pid, 0);
294
- }
295
- catch {
296
- console.log('[mnemonik] Daemon stopped.');
297
- return;
298
- }
299
- }
300
- console.warn('[mnemonik] Daemon did not exit within 5 seconds.');
301
- }
302
- catch {
303
- console.log('[mnemonik] Daemon process not found. Cleaning up PID file.');
304
- await unlink(PID_FILE).catch(() => { });
305
- }
306
- }
307
- async function handleStatus() {
308
- const pid = await readPid();
309
- const config = await readConfig();
310
- if (!pid) {
311
- console.log('Status: not running');
312
- if (config?.roots) {
313
- console.log(`Configured roots: ${config.roots.join(', ')}`);
314
- }
315
- console.log(`\nStart with: mnemonik-scanner start`);
316
- return;
317
- }
318
- console.log(`Status: running (PID ${pid})`);
319
- if (config?.roots) {
320
- console.log(`Roots: ${config.roots.join(', ')}`);
321
- }
322
- if (config?.server) {
323
- console.log(`Server: ${config.server}`);
324
- }
325
- // Check for old per-project daemons
326
- try {
327
- const { readdir: readdirAsync } = await import('fs/promises');
328
- const oldDaemonsDir = join(MNEMONIK_DIR, 'daemons');
329
- const oldPidFiles = await readdirAsync(oldDaemonsDir).catch(() => []);
330
- const staleOldDaemons = [];
331
- for (const f of oldPidFiles) {
332
- if (!f.endsWith('.pid'))
333
- continue;
334
- const content = await readFile(join(oldDaemonsDir, f), 'utf-8').catch(() => null);
335
- if (content) {
336
- const oldPid = parseInt(content.trim(), 10);
337
- try {
338
- process.kill(oldPid, 0);
339
- // Same PID-reuse guard as readPid(): only report genuinely live
340
- // scanner processes, not whatever now occupies a recycled PID.
341
- if (pidIsScanner(oldPid)) {
342
- staleOldDaemons.push(`${f} (PID ${oldPid})`);
343
- }
344
- }
345
- catch {
346
- // Dead process, just a stale file
347
- }
348
- }
349
- }
350
- if (staleOldDaemons.length > 0) {
351
- console.log(`\nLegacy per-project daemons still running:`);
352
- for (const d of staleOldDaemons) {
353
- console.log(` ${d}`);
354
- }
355
- console.log(' Consider stopping these — the global daemon handles all projects.');
356
- }
357
- }
358
- catch {
359
- // Old daemons dir doesn't exist
360
- }
361
- }
362
- async function handleLog() {
363
- try {
364
- const content = await readFile(LOG_FILE, 'utf-8');
365
- // Show last 50 lines
366
- const lines = content.split('\n');
367
- const tail = lines.slice(-50).join('\n');
368
- console.log(tail);
369
- }
370
- catch {
371
- console.log('[mnemonik] No log file found at', LOG_FILE);
372
- }
373
- }
374
- async function main() {
375
- const cli = parseCliArgs();
376
- switch (cli.command) {
377
- case 'start':
378
- await handleStart(cli);
379
- break;
380
- case 'stop':
381
- await handleStop();
382
- break;
383
- case 'status':
384
- await handleStatus();
385
- break;
386
- case 'log':
387
- await handleLog();
388
- break;
389
- case 'doctor':
390
- await runDoctor();
391
- break;
392
- case 'help':
393
- default:
394
- printHelp();
395
- break;
396
- }
397
- }
398
- main();
399
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/pid.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare function pidIsScanner(pid: number): boolean;
package/dist/pid.js DELETED
@@ -1,37 +0,0 @@
1
- import { execFileSync } from 'child_process';
2
- import { readFileSync } from 'fs';
3
- /**
4
- * PID-reuse guard shared by the CLI lock paths (index.ts) and doctor.
5
- *
6
- * `process.kill(pid, 0)` only proves that *some* process is alive at that
7
- * PID, not that it is ours. After a reboot or plain PID recycling the number
8
- * in daemon.pid can belong to an unrelated process — SIGTERMing it from
9
- * `stop`, or refusing to `start` because of it, would be wrong. So a live PID
10
- * only counts as the scanner when its command line matches the daemon's
11
- * signature (same pattern doctor uses to find daemon processes via `ps`).
12
- */
13
- const SCANNER_CMD_PATTERN = /(mnemonik-scanner|scanner\/dist\/index\.js)/;
14
- function processCmdline(pid) {
15
- // Linux: /proc/<pid>/cmdline is NUL-separated argv — cheap and exact.
16
- try {
17
- return readFileSync(`/proc/${pid}/cmdline`, 'utf-8').replace(/\0/g, ' ');
18
- }
19
- catch {
20
- // /proc unavailable (macOS) or unreadable — fall back to ps below.
21
- }
22
- try {
23
- return execFileSync('ps', ['-o', 'args=', '-p', String(pid)], {
24
- encoding: 'utf-8',
25
- stdio: ['ignore', 'pipe', 'ignore'],
26
- timeout: 5000,
27
- }).trim();
28
- }
29
- catch {
30
- return null; // No such process, or ps unavailable — cannot confirm identity.
31
- }
32
- }
33
- export function pidIsScanner(pid) {
34
- const cmdline = processCmdline(pid);
35
- return cmdline !== null && SCANNER_CMD_PATTERN.test(cmdline);
36
- }
37
- //# sourceMappingURL=pid.js.map
package/dist/pid.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"pid.js","sourceRoot":"","sources":["../src/pid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAElC;;;;;;;;;GASG;AACH,MAAM,mBAAmB,GAAG,6CAA6C,CAAC;AAE1E,SAAS,cAAc,CAAC,GAAW;IACjC,sEAAsE;IACtE,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,SAAS,GAAG,UAAU,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;IACrE,CAAC;IACD,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE;YAC5D,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;YACnC,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,gEAAgE;IAC/E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACpC,OAAO,OAAO,KAAK,IAAI,IAAI,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC/D,CAAC"}
package/dist/watcher.d.ts DELETED
@@ -1,30 +0,0 @@
1
- export type ChangeHandler = (changedFiles: string[]) => void;
2
- export type ErrorHandler = (err: Error) => void;
3
- export declare class FileWatcher {
4
- private rootPath;
5
- private onChange;
6
- private watchers;
7
- private watchedDirs;
8
- private watchedDirInodes;
9
- private pendingFiles;
10
- private flushTimer;
11
- private debounceMs;
12
- private onError;
13
- constructor(rootPath: string, onChange: ChangeHandler, debounceMs?: number, onError?: ErrorHandler);
14
- start(): Promise<void>;
15
- stop(): void;
16
- private scheduleFlush;
17
- private watchDir;
18
- /**
19
- * Attach a watcher to a directory created after start(). Called from the
20
- * per-directory change callback with every event path; stats the path and
21
- * only recurses when it is a genuinely new, watchable directory.
22
- */
23
- private watchNewDir;
24
- /**
25
- * Forget a deleted directory and everything watched beneath it. fs.watch
26
- * emits no per-descendant events on a recursive delete, so the whole
27
- * prefix must be purged here for a recreate to re-watch the full subtree.
28
- */
29
- private unwatchSubtree;
30
- }