@luolingp/request-doctor 0.15.0 → 0.15.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/docs/01-/350/275/257/344/273/266/351/234/200/346/261/202/350/247/204/346/240/274/350/257/264/346/230/216.md +1 -1
- package/docs/02-/350/275/257/344/273/266/350/256/276/350/256/241/350/257/264/346/230/216.md +1 -1
- package/docs/03-/350/275/257/344/273/266/346/265/213/350/257/225/346/212/245/345/221/212.md +5 -3
- package/docs/04-/345/256/211/350/243/205/351/203/250/347/275/262/350/257/264/346/230/216.md +1 -1
- package/docs/05-/347/224/250/346/210/267/346/211/213/345/206/214.md +1 -1
- package/docs/06-/350/275/257/344/273/266/350/264/250/351/207/217/345/272/246/351/207/217/346/212/245/345/221/212.md +1 -1
- package/docs/quality-report.json +8 -8
- package/index.js +499 -492
- package/lib/diagnostics.js +81 -77
- package/lib/skip-log.js +27 -0
- package/package.json +1 -1
package/lib/diagnostics.js
CHANGED
|
@@ -1,77 +1,81 @@
|
|
|
1
|
-
// 诊断记录。
|
|
2
|
-
//
|
|
3
|
-
// 管三件事:各项修复计数、最近若干条事件、可选的快照文件。单独拎出来是因为组合根要
|
|
4
|
-
// 关心的是"怎么修",不该顺带管状态怎么记。
|
|
5
|
-
import { writeFileSync } from 'node:fs';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
...
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
1
|
+
// 诊断记录。
|
|
2
|
+
//
|
|
3
|
+
// 管三件事:各项修复计数、最近若干条事件、可选的快照文件。单独拎出来是因为组合根要
|
|
4
|
+
// 关心的是"怎么修",不该顺带管状态怎么记。
|
|
5
|
+
import { writeFileSync } from 'node:fs';
|
|
6
|
+
import { createSkipLog } from './skip-log.js';
|
|
7
|
+
|
|
8
|
+
// 最近事件留多少条给 /doctor 看
|
|
9
|
+
const MAX_LOG = 30;
|
|
10
|
+
|
|
11
|
+
// 突发重试时按这个间隔合并写盘
|
|
12
|
+
const WRITE_COALESCE_MS = 250;
|
|
13
|
+
|
|
14
|
+
export function createDiagnostics(input) {
|
|
15
|
+
const stats = {
|
|
16
|
+
startedAt: Date.now(),
|
|
17
|
+
modelRepairs: 0,
|
|
18
|
+
modelSkips: 0,
|
|
19
|
+
toolRepairs: 0,
|
|
20
|
+
connectionRepairs: 0,
|
|
21
|
+
versionRepairs: 0,
|
|
22
|
+
last: [],
|
|
23
|
+
};
|
|
24
|
+
const diagnostics = { versionScan: null };
|
|
25
|
+
const skipped = createSkipLog();
|
|
26
|
+
let pending;
|
|
27
|
+
|
|
28
|
+
const snapshot = () => ({
|
|
29
|
+
pid: process.pid,
|
|
30
|
+
...stats,
|
|
31
|
+
diagnostics,
|
|
32
|
+
...(input.describe === undefined ? {} : { config: input.describe() }),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// 立刻写。诊断不是正确性数据,写失败就算了——磁盘满了也不该把请求修复带崩。
|
|
36
|
+
const writeNow = () => {
|
|
37
|
+
if (input.statusFile === '') return;
|
|
38
|
+
try {
|
|
39
|
+
writeFileSync(input.statusFile, `${JSON.stringify(snapshot(), null, 2)}\n`, 'utf8');
|
|
40
|
+
} catch {
|
|
41
|
+
// 只有诊断,丢了就丢了
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// 合并写。record 每次修复都会调,而修复是会扎堆的——网络抖一下一秒能来好几次,
|
|
46
|
+
// 每次都同步写盘等于在 agent 最忙的时候卡住事件循环。
|
|
47
|
+
const scheduleWrite = () => {
|
|
48
|
+
if (input.statusFile === '' || pending !== undefined) return;
|
|
49
|
+
pending = setTimeout(() => {
|
|
50
|
+
pending = undefined;
|
|
51
|
+
writeNow();
|
|
52
|
+
}, WRITE_COALESCE_MS);
|
|
53
|
+
pending.unref?.();
|
|
54
|
+
input.trackTimer?.(pending);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const record = (kind, detail) => {
|
|
58
|
+
stats.last.push({ at: new Date().toISOString(), kind, detail: String(detail).slice(0, 240) });
|
|
59
|
+
if (stats.last.length > MAX_LOG) stats.last.shift();
|
|
60
|
+
input.log('info', `request-doctor: ${kind}: ${detail}`);
|
|
61
|
+
// 没救回来的失败按类别归并:日志是流式的,翻起来费事。
|
|
62
|
+
if (kind === 'skip') skipped.add(detail);
|
|
63
|
+
scheduleWrite();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// 把扫描错误挂到当前这轮结果上,别让调用方去翻嵌套对象。
|
|
67
|
+
const scanError = (detail) => {
|
|
68
|
+
diagnostics.versionScan = { ...(diagnostics.versionScan ?? {}), ok: false, error: detail };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// 收尾时把攒着的写掉
|
|
72
|
+
const flush = () => {
|
|
73
|
+
if (pending !== undefined) {
|
|
74
|
+
clearTimeout(pending);
|
|
75
|
+
pending = undefined;
|
|
76
|
+
}
|
|
77
|
+
writeNow();
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return { record, scheduleWrite, writeNow, flush, scanError, stats, skipped: () => skipped.read(), diagnostics, snapshot };
|
|
81
|
+
}
|
package/lib/skip-log.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// 本次会话里没救回来的失败。
|
|
2
|
+
//
|
|
3
|
+
// 日志是流式的,翻起来费事。这里按类别归并,只留每类最后一条与出现次数,
|
|
4
|
+
// 让「刚才那次为什么没救回来」能一眼看到。
|
|
5
|
+
|
|
6
|
+
const MAX_KINDS = 12;
|
|
7
|
+
|
|
8
|
+
// 可读的汇总对象:add 记账,read 取回按次数排序的列表。
|
|
9
|
+
export function createSkipLog() {
|
|
10
|
+
const entries = new Map();
|
|
11
|
+
return {
|
|
12
|
+
add(detail) {
|
|
13
|
+
const kind = String(detail).split(':')[0].trim();
|
|
14
|
+
const previous = entries.get(kind);
|
|
15
|
+
entries.set(kind, {
|
|
16
|
+
kind,
|
|
17
|
+
note: String(detail),
|
|
18
|
+
count: (previous?.count ?? 0) + 1,
|
|
19
|
+
at: new Date().toISOString(),
|
|
20
|
+
});
|
|
21
|
+
while (entries.size > MAX_KINDS) entries.delete(entries.keys().next().value);
|
|
22
|
+
},
|
|
23
|
+
read() {
|
|
24
|
+
return [...entries.values()].sort((a, b) => b.count - a.count);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|