@mnemonik/scanner 5.120.1 → 5.131.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/doctor.d.ts +1 -0
- package/dist/doctor.js +201 -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/index.js +13 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/doctor.ts +207 -0
- package/src/fileLog.ts +70 -0
- package/src/index.ts +15 -16
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runDoctor(): Promise<void>;
|
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
/**
|
|
7
|
+
* `mnemonik-scanner doctor` — the guard that turns the prose upgrade procedure
|
|
8
|
+
* into an executable check. It asserts the invariants that every past botched
|
|
9
|
+
* reinstall violated, and prints the one blessed remediation. Any single ✗
|
|
10
|
+
* exits non-zero so a wrapper (or a human) can react.
|
|
11
|
+
*
|
|
12
|
+
* Invariants:
|
|
13
|
+
* 1. Exactly one `mnemonik-scanner` on PATH, inside the user npm prefix
|
|
14
|
+
* (a second copy under /usr is the classic PATH-shadow version skew).
|
|
15
|
+
* 2. If a systemd user unit exists, it is active and its ExecStart points at
|
|
16
|
+
* that same binary.
|
|
17
|
+
* 3. Exactly one live daemon, and under systemd its PID == the unit MainPID
|
|
18
|
+
* (a stray `node …/scanner/dist/index.js` is a duplicate local-build daemon).
|
|
19
|
+
* 4. No leftover legacy per-project daemons.
|
|
20
|
+
* 5. Installed version is not behind npm latest (warning, not failure).
|
|
21
|
+
*/
|
|
22
|
+
const HOME = homedir();
|
|
23
|
+
const MNEMONIK_DIR = join(HOME, '.mnemonik');
|
|
24
|
+
const PID_FILE = join(MNEMONIK_DIR, 'daemon.pid');
|
|
25
|
+
const LEGACY_DAEMONS_DIR = join(MNEMONIK_DIR, 'daemons');
|
|
26
|
+
const UNIT_FILE = join(HOME, '.config/systemd/user/mnemonik-scanner.service');
|
|
27
|
+
function sh(cmd, args, timeoutMs = 5000) {
|
|
28
|
+
try {
|
|
29
|
+
return execFileSync(cmd, args, {
|
|
30
|
+
encoding: 'utf-8',
|
|
31
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
32
|
+
timeout: timeoutMs,
|
|
33
|
+
}).trim();
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function pidAlive(pid) {
|
|
40
|
+
try {
|
|
41
|
+
process.kill(pid, 0);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function installedVersion() {
|
|
49
|
+
try {
|
|
50
|
+
const here = fileURLToPath(new URL('.', import.meta.url));
|
|
51
|
+
const pkg = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf-8'));
|
|
52
|
+
return pkg.version ?? null;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export async function runDoctor() {
|
|
59
|
+
const checks = [];
|
|
60
|
+
// 1. Binary uniqueness + location.
|
|
61
|
+
const prefix = sh('npm', ['config', 'get', 'prefix']);
|
|
62
|
+
const whichOut = sh('which', ['-a', 'mnemonik-scanner']);
|
|
63
|
+
const binPaths = whichOut ? whichOut.split('\n').map((s) => s.trim()).filter(Boolean) : [];
|
|
64
|
+
if (binPaths.length === 0) {
|
|
65
|
+
checks.push({ level: 'fail', label: 'binary on PATH', detail: 'mnemonik-scanner not found on PATH' });
|
|
66
|
+
}
|
|
67
|
+
else if (binPaths.length > 1) {
|
|
68
|
+
checks.push({
|
|
69
|
+
level: 'fail',
|
|
70
|
+
label: 'single binary on PATH',
|
|
71
|
+
detail: `found ${binPaths.length} copies (PATH shadow → version skew):\n ${binPaths.join('\n ')}`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else if (prefix && !binPaths[0].startsWith(prefix)) {
|
|
75
|
+
checks.push({
|
|
76
|
+
level: 'warn',
|
|
77
|
+
label: 'binary in user npm prefix',
|
|
78
|
+
detail: `${binPaths[0]} is not under npm prefix ${prefix}`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
checks.push({ level: 'ok', label: 'single binary on PATH', detail: binPaths[0] });
|
|
83
|
+
}
|
|
84
|
+
const binPath = binPaths.length === 1 ? binPaths[0] : null;
|
|
85
|
+
// 2. systemd unit health + ExecStart match.
|
|
86
|
+
let unitMainPid = null;
|
|
87
|
+
const hasUnit = existsSync(UNIT_FILE);
|
|
88
|
+
if (hasUnit) {
|
|
89
|
+
const show = sh('systemctl', [
|
|
90
|
+
'--user',
|
|
91
|
+
'show',
|
|
92
|
+
'mnemonik-scanner',
|
|
93
|
+
'-p',
|
|
94
|
+
'ActiveState',
|
|
95
|
+
'-p',
|
|
96
|
+
'MainPID',
|
|
97
|
+
'-p',
|
|
98
|
+
'ExecStart',
|
|
99
|
+
]);
|
|
100
|
+
const active = /ActiveState=active/.test(show ?? '');
|
|
101
|
+
const mainPidMatch = /MainPID=(\d+)/.exec(show ?? '');
|
|
102
|
+
unitMainPid = mainPidMatch ? parseInt(mainPidMatch[1], 10) : null;
|
|
103
|
+
const execMatch = /path=([^\s;]+)/.exec(show ?? '');
|
|
104
|
+
const execPath = execMatch ? execMatch[1] : null;
|
|
105
|
+
if (!active) {
|
|
106
|
+
checks.push({ level: 'fail', label: 'systemd unit active', detail: 'unit is not active' });
|
|
107
|
+
}
|
|
108
|
+
else if (binPath && execPath && execPath !== binPath) {
|
|
109
|
+
checks.push({
|
|
110
|
+
level: 'fail',
|
|
111
|
+
label: 'ExecStart matches binary',
|
|
112
|
+
detail: `unit runs ${execPath} but PATH resolves ${binPath}`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
checks.push({ level: 'ok', label: 'systemd unit active', detail: `MainPID ${unitMainPid ?? '?'}` });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
checks.push({ level: 'warn', label: 'systemd unit', detail: 'no user unit — daemon must be supervised another way' });
|
|
121
|
+
}
|
|
122
|
+
// 3. Single live daemon; under systemd it must equal MainPID.
|
|
123
|
+
let filePid = null;
|
|
124
|
+
try {
|
|
125
|
+
filePid = parseInt(readFileSync(PID_FILE, 'utf-8').trim(), 10);
|
|
126
|
+
if (Number.isNaN(filePid))
|
|
127
|
+
filePid = null;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
filePid = null;
|
|
131
|
+
}
|
|
132
|
+
const psOut = sh('ps', ['-eo', 'pid,cmd']);
|
|
133
|
+
const daemonPids = (psOut ?? '')
|
|
134
|
+
.split('\n')
|
|
135
|
+
// Only the long-running `start` daemon counts — never a `doctor`/`status`/
|
|
136
|
+
// `log`/`stop` invocation (which also runs …/scanner/dist/index.js), and
|
|
137
|
+
// never this doctor process itself.
|
|
138
|
+
.filter((l) => /(mnemonik-scanner|scanner\/dist\/index\.js)/.test(l) && /\bstart\b/.test(l))
|
|
139
|
+
.map((l) => parseInt(l.trim().split(/\s+/)[0], 10))
|
|
140
|
+
.filter((n) => !Number.isNaN(n) && n !== process.pid);
|
|
141
|
+
if (daemonPids.length === 0) {
|
|
142
|
+
checks.push({ level: 'fail', label: 'daemon running', detail: 'no daemon process found' });
|
|
143
|
+
}
|
|
144
|
+
else if (daemonPids.length > 1) {
|
|
145
|
+
checks.push({
|
|
146
|
+
level: 'fail',
|
|
147
|
+
label: 'single daemon',
|
|
148
|
+
detail: `${daemonPids.length} daemon processes (duplicate) — PIDs ${daemonPids.join(', ')}`,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
else if (hasUnit && unitMainPid && daemonPids[0] !== unitMainPid) {
|
|
152
|
+
checks.push({
|
|
153
|
+
level: 'fail',
|
|
154
|
+
label: 'daemon is the systemd one',
|
|
155
|
+
detail: `running PID ${daemonPids[0]} != unit MainPID ${unitMainPid} (stray local-build daemon)`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else if (filePid && !pidAlive(filePid)) {
|
|
159
|
+
checks.push({ level: 'warn', label: 'PID file fresh', detail: `stale ${PID_FILE} (PID ${filePid} dead)` });
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
checks.push({ level: 'ok', label: 'single daemon', detail: `PID ${daemonPids[0]}` });
|
|
163
|
+
}
|
|
164
|
+
// 4. Legacy per-project daemons.
|
|
165
|
+
try {
|
|
166
|
+
const legacy = readdirSync(LEGACY_DAEMONS_DIR)
|
|
167
|
+
.filter((f) => f.endsWith('.pid'))
|
|
168
|
+
.map((f) => parseInt(readFileSync(join(LEGACY_DAEMONS_DIR, f), 'utf-8').trim(), 10))
|
|
169
|
+
.filter((n) => !Number.isNaN(n) && pidAlive(n));
|
|
170
|
+
if (legacy.length > 0) {
|
|
171
|
+
checks.push({ level: 'fail', label: 'no legacy daemons', detail: `legacy per-project daemons alive: ${legacy.join(', ')}` });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// dir absent — nothing to check
|
|
176
|
+
}
|
|
177
|
+
// 5. Version vs npm latest (informational).
|
|
178
|
+
const local = installedVersion();
|
|
179
|
+
const latest = sh('npm', ['view', '@mnemonik/scanner', 'version'], 15000);
|
|
180
|
+
if (local && latest && local !== latest) {
|
|
181
|
+
checks.push({ level: 'warn', label: 'up to date', detail: `installed ${local}, npm latest ${latest} — run: make scanner-deploy` });
|
|
182
|
+
}
|
|
183
|
+
else if (local) {
|
|
184
|
+
checks.push({ level: 'ok', label: 'up to date', detail: `v${local}` });
|
|
185
|
+
}
|
|
186
|
+
// Report.
|
|
187
|
+
const icon = { ok: '✓', warn: '⚠', fail: '✗' };
|
|
188
|
+
console.log('mnemonik-scanner doctor\n');
|
|
189
|
+
for (const c of checks) {
|
|
190
|
+
console.log(` ${icon[c.level]} ${c.label}${c.detail ? `: ${c.detail}` : ''}`);
|
|
191
|
+
}
|
|
192
|
+
const failed = checks.filter((c) => c.level === 'fail');
|
|
193
|
+
if (failed.length > 0) {
|
|
194
|
+
console.log(`\n${failed.length} problem(s). Canonical fix: \`make scanner-deploy\` (build → publish shared-first → npm i -g → systemctl restart → re-verify).\n` +
|
|
195
|
+
'Do NOT hand-copy dist/ or run `make daemon-*` on a systemd host — those are what cause this.');
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
console.log('\nAll invariants hold.');
|
|
199
|
+
process.exit(0);
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append-only sink that mirrors the daemon's console output to a file the
|
|
3
|
+
* `mnemonik-scanner log` command can tail. Rotates to `<file>.old` once the
|
|
4
|
+
* file would pass `maxSize`, so a daemon that runs for weeks can't grow it
|
|
5
|
+
* without bound. Every operation is best-effort: a logging failure must never
|
|
6
|
+
* crash the daemon, so all filesystem errors are swallowed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createRotatingFileSink(logFile: string, maxSize: number): (chunk: string) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Tee `process.stdout`/`process.stderr` into `logFile` on top of their normal
|
|
11
|
+
* destination. This is what makes `mnemonik-scanner log` reflect live activity
|
|
12
|
+
* regardless of how the daemon is supervised: under systemd stdout is wired to
|
|
13
|
+
* the journal (a socket), under a bare shell to the terminal — either way the
|
|
14
|
+
* file now receives the same lines. Call once at daemon start, before the
|
|
15
|
+
* daemon writes anything worth capturing.
|
|
16
|
+
*/
|
|
17
|
+
export declare function installFileLogging(logFile: string, maxSize: number): void;
|
package/dist/fileLog.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { appendFileSync, renameSync, statSync } from 'fs';
|
|
2
|
+
/**
|
|
3
|
+
* Append-only sink that mirrors the daemon's console output to a file the
|
|
4
|
+
* `mnemonik-scanner log` command can tail. Rotates to `<file>.old` once the
|
|
5
|
+
* file would pass `maxSize`, so a daemon that runs for weeks can't grow it
|
|
6
|
+
* without bound. Every operation is best-effort: a logging failure must never
|
|
7
|
+
* crash the daemon, so all filesystem errors are swallowed.
|
|
8
|
+
*/
|
|
9
|
+
export function createRotatingFileSink(logFile, maxSize) {
|
|
10
|
+
let size = 0;
|
|
11
|
+
try {
|
|
12
|
+
size = statSync(logFile).size;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
size = 0;
|
|
16
|
+
}
|
|
17
|
+
return (chunk) => {
|
|
18
|
+
if (!chunk)
|
|
19
|
+
return;
|
|
20
|
+
try {
|
|
21
|
+
const bytes = Buffer.byteLength(chunk);
|
|
22
|
+
if (size > 0 && size + bytes > maxSize) {
|
|
23
|
+
try {
|
|
24
|
+
renameSync(logFile, logFile + '.old');
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Rotation failed (e.g. cross-device) — keep appending to the
|
|
28
|
+
// current file rather than losing the line.
|
|
29
|
+
}
|
|
30
|
+
size = 0;
|
|
31
|
+
}
|
|
32
|
+
appendFileSync(logFile, chunk);
|
|
33
|
+
size += bytes;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Never let logging break the daemon.
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Tee `process.stdout`/`process.stderr` into `logFile` on top of their normal
|
|
42
|
+
* destination. This is what makes `mnemonik-scanner log` reflect live activity
|
|
43
|
+
* regardless of how the daemon is supervised: under systemd stdout is wired to
|
|
44
|
+
* the journal (a socket), under a bare shell to the terminal — either way the
|
|
45
|
+
* file now receives the same lines. Call once at daemon start, before the
|
|
46
|
+
* daemon writes anything worth capturing.
|
|
47
|
+
*/
|
|
48
|
+
export function installFileLogging(logFile, maxSize) {
|
|
49
|
+
const sink = createRotatingFileSink(logFile, maxSize);
|
|
50
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
51
|
+
const original = stream.write.bind(stream);
|
|
52
|
+
stream.write = ((chunk, encoding, cb) => {
|
|
53
|
+
try {
|
|
54
|
+
if (typeof chunk === 'string') {
|
|
55
|
+
sink(chunk);
|
|
56
|
+
}
|
|
57
|
+
else if (Buffer.isBuffer(chunk)) {
|
|
58
|
+
const enc = typeof encoding === 'string' ? encoding : 'utf8';
|
|
59
|
+
sink(chunk.toString(enc));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// ignore — mirroring is best-effort
|
|
64
|
+
}
|
|
65
|
+
// Preserve the real stream's overloads (chunk, cb) / (chunk, enc, cb).
|
|
66
|
+
return original(chunk, encoding, cb);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=fileLog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileLog.js","sourceRoot":"","sources":["../src/fileLog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAe,EACf,OAAe;IAEf,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,CAAC;QACH,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,GAAG,CAAC,CAAC;IACX,CAAC;IAED,OAAO,CAAC,KAAa,EAAQ,EAAE;QAC7B,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YACvC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,GAAG,OAAO,EAAE,CAAC;gBACvC,IAAI,CAAC;oBACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;gBACxC,CAAC;gBAAC,MAAM,CAAC;oBACP,8DAA8D;oBAC9D,4CAA4C;gBAC9C,CAAC;gBACD,IAAI,GAAG,CAAC,CAAC;YACX,CAAC;YACD,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC/B,IAAI,IAAI,KAAK,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,sCAAsC;QACxC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe,EAAE,OAAe;IACjE,MAAM,IAAI,GAAG,sBAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAEtD,KAAK,MAAM,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACtD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAoC,CAAC;QAC9E,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,KAAc,EAAE,QAAkB,EAAE,EAAY,EAAW,EAAE;YAC5E,IAAI,CAAC;gBACH,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAC9B,IAAI,CAAC,KAAK,CAAC,CAAC;gBACd,CAAC;qBAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;oBAClC,MAAM,GAAG,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAE,QAA2B,CAAC,CAAC,CAAC,MAAM,CAAC;oBACjF,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,oCAAoC;YACtC,CAAC;YACD,uEAAuE;YACvE,OAAO,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAwB,CAAC;IAC5B,CAAC;AACH,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,8 @@ import { existsSync } from 'fs';
|
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { homedir } from 'os';
|
|
6
6
|
import { ScannerDaemon } from './daemon.js';
|
|
7
|
+
import { installFileLogging } from './fileLog.js';
|
|
8
|
+
import { runDoctor } from './doctor.js';
|
|
7
9
|
const DEFAULT_SERVER = 'https://api.mnemonik.dev';
|
|
8
10
|
const MNEMONIK_DIR = join(homedir(), '.mnemonik');
|
|
9
11
|
const PID_FILE = join(MNEMONIK_DIR, 'daemon.pid');
|
|
@@ -13,7 +15,7 @@ const MAX_LOG_SIZE = 5 * 1024 * 1024; // 5MB
|
|
|
13
15
|
function parseCliArgs() {
|
|
14
16
|
const args = process.argv.slice(2);
|
|
15
17
|
const command = (args[0] ?? 'help');
|
|
16
|
-
if (!['start', 'stop', 'status', 'log', 'help'].includes(command)) {
|
|
18
|
+
if (!['start', 'stop', 'status', 'log', 'doctor', 'help'].includes(command)) {
|
|
17
19
|
return { command: 'help' };
|
|
18
20
|
}
|
|
19
21
|
let key;
|
|
@@ -58,18 +60,6 @@ async function checkConfigPermissions() {
|
|
|
58
60
|
// Config file doesn't exist yet
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
|
-
async function rotateLogIfNeeded() {
|
|
62
|
-
try {
|
|
63
|
-
const { rename } = await import('fs/promises');
|
|
64
|
-
const s = await stat(LOG_FILE);
|
|
65
|
-
if (s.size > MAX_LOG_SIZE) {
|
|
66
|
-
await rename(LOG_FILE, LOG_FILE + '.old');
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
// Log file doesn't exist yet
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
63
|
async function acquireLock(retried = false) {
|
|
74
64
|
try {
|
|
75
65
|
const { open: fsOpen } = await import('fs/promises');
|
|
@@ -130,6 +120,7 @@ Usage:
|
|
|
130
120
|
mnemonik-scanner stop Stop the running daemon
|
|
131
121
|
mnemonik-scanner status Show daemon status
|
|
132
122
|
mnemonik-scanner log Tail the scanner log file
|
|
123
|
+
mnemonik-scanner doctor Check install health (drift detection)
|
|
133
124
|
mnemonik-scanner help Show this help
|
|
134
125
|
|
|
135
126
|
Options (for start):
|
|
@@ -188,6 +179,12 @@ async function handleStart(cli) {
|
|
|
188
179
|
const server = cli.server || config?.server || DEFAULT_SERVER;
|
|
189
180
|
// Ensure directories exist
|
|
190
181
|
await mkdir(MNEMONIK_DIR, { recursive: true });
|
|
182
|
+
// Mirror all console output into scanner.log so `mnemonik-scanner log`
|
|
183
|
+
// reflects live activity regardless of supervisor. Under systemd, stdout is
|
|
184
|
+
// wired to the journal socket, not this file — without the tee the on-disk
|
|
185
|
+
// log goes stale and `log` shows nothing current. Install before any daemon
|
|
186
|
+
// output so the whole session is captured.
|
|
187
|
+
installFileLogging(LOG_FILE, MAX_LOG_SIZE);
|
|
191
188
|
// Save config for future runs (never save env var key to file)
|
|
192
189
|
const configToSave = {
|
|
193
190
|
roots,
|
|
@@ -203,7 +200,6 @@ async function handleStart(cli) {
|
|
|
203
200
|
}
|
|
204
201
|
await writeConfig(configToSave);
|
|
205
202
|
await checkConfigPermissions();
|
|
206
|
-
await rotateLogIfNeeded();
|
|
207
203
|
const locked = await acquireLock();
|
|
208
204
|
if (!locked) {
|
|
209
205
|
const pid = await readPid();
|
|
@@ -341,6 +337,9 @@ async function main() {
|
|
|
341
337
|
case 'log':
|
|
342
338
|
await handleLog();
|
|
343
339
|
break;
|
|
340
|
+
case 'doctor':
|
|
341
|
+
await runDoctor();
|
|
342
|
+
break;
|
|
344
343
|
case 'help':
|
|
345
344
|
default:
|
|
346
345
|
printHelp();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/doctor.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `mnemonik-scanner doctor` — the guard that turns the prose upgrade procedure
|
|
9
|
+
* into an executable check. It asserts the invariants that every past botched
|
|
10
|
+
* reinstall violated, and prints the one blessed remediation. Any single ✗
|
|
11
|
+
* exits non-zero so a wrapper (or a human) can react.
|
|
12
|
+
*
|
|
13
|
+
* Invariants:
|
|
14
|
+
* 1. Exactly one `mnemonik-scanner` on PATH, inside the user npm prefix
|
|
15
|
+
* (a second copy under /usr is the classic PATH-shadow version skew).
|
|
16
|
+
* 2. If a systemd user unit exists, it is active and its ExecStart points at
|
|
17
|
+
* that same binary.
|
|
18
|
+
* 3. Exactly one live daemon, and under systemd its PID == the unit MainPID
|
|
19
|
+
* (a stray `node …/scanner/dist/index.js` is a duplicate local-build daemon).
|
|
20
|
+
* 4. No leftover legacy per-project daemons.
|
|
21
|
+
* 5. Installed version is not behind npm latest (warning, not failure).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const HOME = homedir();
|
|
25
|
+
const MNEMONIK_DIR = join(HOME, '.mnemonik');
|
|
26
|
+
const PID_FILE = join(MNEMONIK_DIR, 'daemon.pid');
|
|
27
|
+
const LEGACY_DAEMONS_DIR = join(MNEMONIK_DIR, 'daemons');
|
|
28
|
+
const UNIT_FILE = join(HOME, '.config/systemd/user/mnemonik-scanner.service');
|
|
29
|
+
|
|
30
|
+
type Level = 'ok' | 'warn' | 'fail';
|
|
31
|
+
interface Check {
|
|
32
|
+
level: Level;
|
|
33
|
+
label: string;
|
|
34
|
+
detail?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function sh(cmd: string, args: string[], timeoutMs = 5000): string | null {
|
|
38
|
+
try {
|
|
39
|
+
return execFileSync(cmd, args, {
|
|
40
|
+
encoding: 'utf-8',
|
|
41
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
42
|
+
timeout: timeoutMs,
|
|
43
|
+
}).trim();
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function pidAlive(pid: number): boolean {
|
|
50
|
+
try {
|
|
51
|
+
process.kill(pid, 0);
|
|
52
|
+
return true;
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function installedVersion(): string | null {
|
|
59
|
+
try {
|
|
60
|
+
const here = fileURLToPath(new URL('.', import.meta.url));
|
|
61
|
+
const pkg = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf-8')) as {
|
|
62
|
+
version?: string;
|
|
63
|
+
};
|
|
64
|
+
return pkg.version ?? null;
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function runDoctor(): Promise<void> {
|
|
71
|
+
const checks: Check[] = [];
|
|
72
|
+
|
|
73
|
+
// 1. Binary uniqueness + location.
|
|
74
|
+
const prefix = sh('npm', ['config', 'get', 'prefix']);
|
|
75
|
+
const whichOut = sh('which', ['-a', 'mnemonik-scanner']);
|
|
76
|
+
const binPaths = whichOut ? whichOut.split('\n').map((s) => s.trim()).filter(Boolean) : [];
|
|
77
|
+
if (binPaths.length === 0) {
|
|
78
|
+
checks.push({ level: 'fail', label: 'binary on PATH', detail: 'mnemonik-scanner not found on PATH' });
|
|
79
|
+
} else if (binPaths.length > 1) {
|
|
80
|
+
checks.push({
|
|
81
|
+
level: 'fail',
|
|
82
|
+
label: 'single binary on PATH',
|
|
83
|
+
detail: `found ${binPaths.length} copies (PATH shadow → version skew):\n ${binPaths.join('\n ')}`,
|
|
84
|
+
});
|
|
85
|
+
} else if (prefix && !binPaths[0]!.startsWith(prefix)) {
|
|
86
|
+
checks.push({
|
|
87
|
+
level: 'warn',
|
|
88
|
+
label: 'binary in user npm prefix',
|
|
89
|
+
detail: `${binPaths[0]} is not under npm prefix ${prefix}`,
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
checks.push({ level: 'ok', label: 'single binary on PATH', detail: binPaths[0] });
|
|
93
|
+
}
|
|
94
|
+
const binPath = binPaths.length === 1 ? binPaths[0]! : null;
|
|
95
|
+
|
|
96
|
+
// 2. systemd unit health + ExecStart match.
|
|
97
|
+
let unitMainPid: number | null = null;
|
|
98
|
+
const hasUnit = existsSync(UNIT_FILE);
|
|
99
|
+
if (hasUnit) {
|
|
100
|
+
const show = sh('systemctl', [
|
|
101
|
+
'--user',
|
|
102
|
+
'show',
|
|
103
|
+
'mnemonik-scanner',
|
|
104
|
+
'-p',
|
|
105
|
+
'ActiveState',
|
|
106
|
+
'-p',
|
|
107
|
+
'MainPID',
|
|
108
|
+
'-p',
|
|
109
|
+
'ExecStart',
|
|
110
|
+
]);
|
|
111
|
+
const active = /ActiveState=active/.test(show ?? '');
|
|
112
|
+
const mainPidMatch = /MainPID=(\d+)/.exec(show ?? '');
|
|
113
|
+
unitMainPid = mainPidMatch ? parseInt(mainPidMatch[1]!, 10) : null;
|
|
114
|
+
const execMatch = /path=([^\s;]+)/.exec(show ?? '');
|
|
115
|
+
const execPath = execMatch ? execMatch[1]! : null;
|
|
116
|
+
|
|
117
|
+
if (!active) {
|
|
118
|
+
checks.push({ level: 'fail', label: 'systemd unit active', detail: 'unit is not active' });
|
|
119
|
+
} else if (binPath && execPath && execPath !== binPath) {
|
|
120
|
+
checks.push({
|
|
121
|
+
level: 'fail',
|
|
122
|
+
label: 'ExecStart matches binary',
|
|
123
|
+
detail: `unit runs ${execPath} but PATH resolves ${binPath}`,
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
checks.push({ level: 'ok', label: 'systemd unit active', detail: `MainPID ${unitMainPid ?? '?'}` });
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
checks.push({ level: 'warn', label: 'systemd unit', detail: 'no user unit — daemon must be supervised another way' });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 3. Single live daemon; under systemd it must equal MainPID.
|
|
133
|
+
let filePid: number | null = null;
|
|
134
|
+
try {
|
|
135
|
+
filePid = parseInt(readFileSync(PID_FILE, 'utf-8').trim(), 10);
|
|
136
|
+
if (Number.isNaN(filePid)) filePid = null;
|
|
137
|
+
} catch {
|
|
138
|
+
filePid = null;
|
|
139
|
+
}
|
|
140
|
+
const psOut = sh('ps', ['-eo', 'pid,cmd']);
|
|
141
|
+
const daemonPids = (psOut ?? '')
|
|
142
|
+
.split('\n')
|
|
143
|
+
// Only the long-running `start` daemon counts — never a `doctor`/`status`/
|
|
144
|
+
// `log`/`stop` invocation (which also runs …/scanner/dist/index.js), and
|
|
145
|
+
// never this doctor process itself.
|
|
146
|
+
.filter((l) => /(mnemonik-scanner|scanner\/dist\/index\.js)/.test(l) && /\bstart\b/.test(l))
|
|
147
|
+
.map((l) => parseInt(l.trim().split(/\s+/)[0]!, 10))
|
|
148
|
+
.filter((n) => !Number.isNaN(n) && n !== process.pid);
|
|
149
|
+
if (daemonPids.length === 0) {
|
|
150
|
+
checks.push({ level: 'fail', label: 'daemon running', detail: 'no daemon process found' });
|
|
151
|
+
} else if (daemonPids.length > 1) {
|
|
152
|
+
checks.push({
|
|
153
|
+
level: 'fail',
|
|
154
|
+
label: 'single daemon',
|
|
155
|
+
detail: `${daemonPids.length} daemon processes (duplicate) — PIDs ${daemonPids.join(', ')}`,
|
|
156
|
+
});
|
|
157
|
+
} else if (hasUnit && unitMainPid && daemonPids[0] !== unitMainPid) {
|
|
158
|
+
checks.push({
|
|
159
|
+
level: 'fail',
|
|
160
|
+
label: 'daemon is the systemd one',
|
|
161
|
+
detail: `running PID ${daemonPids[0]} != unit MainPID ${unitMainPid} (stray local-build daemon)`,
|
|
162
|
+
});
|
|
163
|
+
} else if (filePid && !pidAlive(filePid)) {
|
|
164
|
+
checks.push({ level: 'warn', label: 'PID file fresh', detail: `stale ${PID_FILE} (PID ${filePid} dead)` });
|
|
165
|
+
} else {
|
|
166
|
+
checks.push({ level: 'ok', label: 'single daemon', detail: `PID ${daemonPids[0]}` });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 4. Legacy per-project daemons.
|
|
170
|
+
try {
|
|
171
|
+
const legacy = readdirSync(LEGACY_DAEMONS_DIR)
|
|
172
|
+
.filter((f) => f.endsWith('.pid'))
|
|
173
|
+
.map((f) => parseInt(readFileSync(join(LEGACY_DAEMONS_DIR, f), 'utf-8').trim(), 10))
|
|
174
|
+
.filter((n) => !Number.isNaN(n) && pidAlive(n));
|
|
175
|
+
if (legacy.length > 0) {
|
|
176
|
+
checks.push({ level: 'fail', label: 'no legacy daemons', detail: `legacy per-project daemons alive: ${legacy.join(', ')}` });
|
|
177
|
+
}
|
|
178
|
+
} catch {
|
|
179
|
+
// dir absent — nothing to check
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 5. Version vs npm latest (informational).
|
|
183
|
+
const local = installedVersion();
|
|
184
|
+
const latest = sh('npm', ['view', '@mnemonik/scanner', 'version'], 15000);
|
|
185
|
+
if (local && latest && local !== latest) {
|
|
186
|
+
checks.push({ level: 'warn', label: 'up to date', detail: `installed ${local}, npm latest ${latest} — run: make scanner-deploy` });
|
|
187
|
+
} else if (local) {
|
|
188
|
+
checks.push({ level: 'ok', label: 'up to date', detail: `v${local}` });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Report.
|
|
192
|
+
const icon = { ok: '✓', warn: '⚠', fail: '✗' } as const;
|
|
193
|
+
console.log('mnemonik-scanner doctor\n');
|
|
194
|
+
for (const c of checks) {
|
|
195
|
+
console.log(` ${icon[c.level]} ${c.label}${c.detail ? `: ${c.detail}` : ''}`);
|
|
196
|
+
}
|
|
197
|
+
const failed = checks.filter((c) => c.level === 'fail');
|
|
198
|
+
if (failed.length > 0) {
|
|
199
|
+
console.log(
|
|
200
|
+
`\n${failed.length} problem(s). Canonical fix: \`make scanner-deploy\` (build → publish shared-first → npm i -g → systemctl restart → re-verify).\n` +
|
|
201
|
+
'Do NOT hand-copy dist/ or run `make daemon-*` on a systemd host — those are what cause this.'
|
|
202
|
+
);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
console.log('\nAll invariants hold.');
|
|
206
|
+
process.exit(0);
|
|
207
|
+
}
|
package/src/fileLog.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { appendFileSync, renameSync, statSync } from 'fs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Append-only sink that mirrors the daemon's console output to a file the
|
|
5
|
+
* `mnemonik-scanner log` command can tail. Rotates to `<file>.old` once the
|
|
6
|
+
* file would pass `maxSize`, so a daemon that runs for weeks can't grow it
|
|
7
|
+
* without bound. Every operation is best-effort: a logging failure must never
|
|
8
|
+
* crash the daemon, so all filesystem errors are swallowed.
|
|
9
|
+
*/
|
|
10
|
+
export function createRotatingFileSink(
|
|
11
|
+
logFile: string,
|
|
12
|
+
maxSize: number
|
|
13
|
+
): (chunk: string) => void {
|
|
14
|
+
let size = 0;
|
|
15
|
+
try {
|
|
16
|
+
size = statSync(logFile).size;
|
|
17
|
+
} catch {
|
|
18
|
+
size = 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (chunk: string): void => {
|
|
22
|
+
if (!chunk) return;
|
|
23
|
+
try {
|
|
24
|
+
const bytes = Buffer.byteLength(chunk);
|
|
25
|
+
if (size > 0 && size + bytes > maxSize) {
|
|
26
|
+
try {
|
|
27
|
+
renameSync(logFile, logFile + '.old');
|
|
28
|
+
} catch {
|
|
29
|
+
// Rotation failed (e.g. cross-device) — keep appending to the
|
|
30
|
+
// current file rather than losing the line.
|
|
31
|
+
}
|
|
32
|
+
size = 0;
|
|
33
|
+
}
|
|
34
|
+
appendFileSync(logFile, chunk);
|
|
35
|
+
size += bytes;
|
|
36
|
+
} catch {
|
|
37
|
+
// Never let logging break the daemon.
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Tee `process.stdout`/`process.stderr` into `logFile` on top of their normal
|
|
44
|
+
* destination. This is what makes `mnemonik-scanner log` reflect live activity
|
|
45
|
+
* regardless of how the daemon is supervised: under systemd stdout is wired to
|
|
46
|
+
* the journal (a socket), under a bare shell to the terminal — either way the
|
|
47
|
+
* file now receives the same lines. Call once at daemon start, before the
|
|
48
|
+
* daemon writes anything worth capturing.
|
|
49
|
+
*/
|
|
50
|
+
export function installFileLogging(logFile: string, maxSize: number): void {
|
|
51
|
+
const sink = createRotatingFileSink(logFile, maxSize);
|
|
52
|
+
|
|
53
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
54
|
+
const original = stream.write.bind(stream) as (...args: unknown[]) => boolean;
|
|
55
|
+
stream.write = ((chunk: unknown, encoding?: unknown, cb?: unknown): boolean => {
|
|
56
|
+
try {
|
|
57
|
+
if (typeof chunk === 'string') {
|
|
58
|
+
sink(chunk);
|
|
59
|
+
} else if (Buffer.isBuffer(chunk)) {
|
|
60
|
+
const enc = typeof encoding === 'string' ? (encoding as BufferEncoding) : 'utf8';
|
|
61
|
+
sink(chunk.toString(enc));
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// ignore — mirroring is best-effort
|
|
65
|
+
}
|
|
66
|
+
// Preserve the real stream's overloads (chunk, cb) / (chunk, enc, cb).
|
|
67
|
+
return original(chunk, encoding, cb);
|
|
68
|
+
}) as typeof stream.write;
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { existsSync } from 'fs';
|
|
|
5
5
|
import { join } from 'path';
|
|
6
6
|
import { homedir } from 'os';
|
|
7
7
|
import { ScannerDaemon } from './daemon.js';
|
|
8
|
+
import { installFileLogging } from './fileLog.js';
|
|
9
|
+
import { runDoctor } from './doctor.js';
|
|
8
10
|
|
|
9
11
|
const DEFAULT_SERVER = 'https://api.mnemonik.dev';
|
|
10
12
|
const MNEMONIK_DIR = join(homedir(), '.mnemonik');
|
|
@@ -21,7 +23,7 @@ interface ScannerConfig {
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
interface CliArgs {
|
|
24
|
-
command: 'start' | 'stop' | 'status' | 'log' | 'help';
|
|
26
|
+
command: 'start' | 'stop' | 'status' | 'log' | 'doctor' | 'help';
|
|
25
27
|
key?: string;
|
|
26
28
|
server?: string;
|
|
27
29
|
roots?: string[];
|
|
@@ -31,7 +33,7 @@ function parseCliArgs(): CliArgs {
|
|
|
31
33
|
const args = process.argv.slice(2);
|
|
32
34
|
const command = (args[0] ?? 'help') as CliArgs['command'];
|
|
33
35
|
|
|
34
|
-
if (!['start', 'stop', 'status', 'log', 'help'].includes(command)) {
|
|
36
|
+
if (!['start', 'stop', 'status', 'log', 'doctor', 'help'].includes(command)) {
|
|
35
37
|
return { command: 'help' };
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -81,18 +83,6 @@ async function checkConfigPermissions(): Promise<void> {
|
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
async function rotateLogIfNeeded(): Promise<void> {
|
|
85
|
-
try {
|
|
86
|
-
const { rename } = await import('fs/promises');
|
|
87
|
-
const s = await stat(LOG_FILE);
|
|
88
|
-
if (s.size > MAX_LOG_SIZE) {
|
|
89
|
-
await rename(LOG_FILE, LOG_FILE + '.old');
|
|
90
|
-
}
|
|
91
|
-
} catch {
|
|
92
|
-
// Log file doesn't exist yet
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
86
|
async function acquireLock(retried = false): Promise<boolean> {
|
|
97
87
|
try {
|
|
98
88
|
const { open: fsOpen } = await import('fs/promises');
|
|
@@ -154,6 +144,7 @@ Usage:
|
|
|
154
144
|
mnemonik-scanner stop Stop the running daemon
|
|
155
145
|
mnemonik-scanner status Show daemon status
|
|
156
146
|
mnemonik-scanner log Tail the scanner log file
|
|
147
|
+
mnemonik-scanner doctor Check install health (drift detection)
|
|
157
148
|
mnemonik-scanner help Show this help
|
|
158
149
|
|
|
159
150
|
Options (for start):
|
|
@@ -225,6 +216,13 @@ async function handleStart(cli: CliArgs): Promise<void> {
|
|
|
225
216
|
// Ensure directories exist
|
|
226
217
|
await mkdir(MNEMONIK_DIR, { recursive: true });
|
|
227
218
|
|
|
219
|
+
// Mirror all console output into scanner.log so `mnemonik-scanner log`
|
|
220
|
+
// reflects live activity regardless of supervisor. Under systemd, stdout is
|
|
221
|
+
// wired to the journal socket, not this file — without the tee the on-disk
|
|
222
|
+
// log goes stale and `log` shows nothing current. Install before any daemon
|
|
223
|
+
// output so the whole session is captured.
|
|
224
|
+
installFileLogging(LOG_FILE, MAX_LOG_SIZE);
|
|
225
|
+
|
|
228
226
|
// Save config for future runs (never save env var key to file)
|
|
229
227
|
const configToSave: ScannerConfig = {
|
|
230
228
|
roots,
|
|
@@ -240,8 +238,6 @@ async function handleStart(cli: CliArgs): Promise<void> {
|
|
|
240
238
|
await writeConfig(configToSave);
|
|
241
239
|
await checkConfigPermissions();
|
|
242
240
|
|
|
243
|
-
await rotateLogIfNeeded();
|
|
244
|
-
|
|
245
241
|
const locked = await acquireLock();
|
|
246
242
|
if (!locked) {
|
|
247
243
|
const pid = await readPid();
|
|
@@ -388,6 +384,9 @@ async function main(): Promise<void> {
|
|
|
388
384
|
case 'log':
|
|
389
385
|
await handleLog();
|
|
390
386
|
break;
|
|
387
|
+
case 'doctor':
|
|
388
|
+
await runDoctor();
|
|
389
|
+
break;
|
|
391
390
|
case 'help':
|
|
392
391
|
default:
|
|
393
392
|
printHelp();
|