feat: 升级至 v0.3.16 — 工单修复 51 项 + 审查修复 30 项(安全加固/适配器/工具系统/数据层/前端)

This commit is contained in:
2026-07-22 10:31:13 +08:00
parent 516d8a728f
commit 2c2ca4a692
43 changed files with 1454 additions and 301 deletions
+68 -4
View File
@@ -13,7 +13,7 @@
*/
import { join } from 'path';
import { appendFileSync, existsSync, mkdirSync } from 'fs';
import { appendFileSync, existsSync, mkdirSync, promises } from 'fs';
import log from 'electron-log';
// ===== 事件类型 =====
@@ -43,6 +43,10 @@ export class SessionRecorder {
private filePath: string | null = null;
private seq = 0;
private sessionId: string | null = null;
// #35 修复: 缓冲写入,避免高频 appendFileSync 阻塞主进程(30-50 次/秒 → 每 100ms 批量异步 flush
private buffer: string[] = [];
private flushTimer: NodeJS.Timeout | null = null;
private flushing = false;
constructor(private workspacePath: string) {}
@@ -91,6 +95,9 @@ export class SessionRecorder {
terminationReason: params.terminationReason,
});
// #35 修复: 同步 flush 确保最后的 session_end 事件写入文件
this.flushSync();
log.info(`Session recording stopped: ${this.filePath}`);
this.filePath = null;
this.sessionId = null;
@@ -222,7 +229,10 @@ export class SessionRecorder {
// ===== 私有方法 =====
/**
* 写入事件到 JSONL 文件
* 写入事件到缓冲区
*
* #35 修复: 改为缓冲写入,定时异步 flush,避免每次 appendFileSync 阻塞主进程
* 高频事件(30-50 次/秒)先 push 到内存 buffer,每 100ms 批量异步写入文件
*/
private writeEvent(data: Record<string, unknown>): void {
if (!this.filePath) return;
@@ -234,10 +244,64 @@ export class SessionRecorder {
...data,
} as TraceEvent;
const line = JSON.stringify(event);
// 审查修复: buffer 上限防止 OOM — 高频事件持续 flush 失败时避免内存无限增长
const MAX_BUFFER_SIZE = 1000;
if (this.buffer.length >= MAX_BUFFER_SIZE) {
// 超限时强制同步写入,避免内存无限增长
this.flushSync();
}
this.buffer.push(line);
if (!this.flushTimer) {
this.flushTimer = setTimeout(() => {
this.flushTimer = null;
void this.flush();
}, 100);
}
}
/**
* #35 修复: 异步 flush 缓冲区到文件
* 审查修复: 用局部变量保存 filePath,防止 stopRecording 将 filePath 置 null 后 appendFile 抛错;
* 失败时将数据 unshift 回 buffer 避免丢整批数据
*/
private async flush(): Promise<void> {
if (this.flushing || this.buffer.length === 0 || !this.filePath) return;
this.flushing = true;
const filePath = this.filePath; // 局部变量,防止中途变 null
const data = this.buffer.join('\n') + '\n';
this.buffer = [];
try {
appendFileSync(this.filePath, JSON.stringify(event) + '\n', 'utf-8');
await promises.appendFile(filePath, data, 'utf-8');
} catch (error) {
log.error('Trace event write failed:', error);
log.error('Trace event flush failed:', error);
// 审查修复: 失败时将数据放回 buffer 头部,下次 flush/flushSync 重试
this.buffer.unshift(data.trimEnd());
} finally {
this.flushing = false;
}
}
/**
* #35 修复: 同步 flush 缓冲区到文件
* 用于 stopRecording 确保最后的数据(如 session_end 事件)写入文件
* 审查修复: 如果异步 flush 正在进行(flushing=true),等待其完成后再写入,避免数据交叉/丢失
*/
private flushSync(): void {
if (this.flushTimer) {
clearTimeout(this.flushTimer);
this.flushTimer = null;
}
if (this.buffer.length === 0 || !this.filePath) return;
const data = this.buffer.join('\n') + '\n';
this.buffer = [];
try {
appendFileSync(this.filePath, data, 'utf-8');
} catch (error) {
log.error('Trace event flushSync failed:', error);
// 审查修复: 失败时将数据放回 buffer,避免数据丢失
this.buffer.unshift(data.trimEnd());
}
}
}