feat: 完善堡垒机与 Agent 智能运维能力

- 支持 Windows Agent 构建、安装脚本和统一上报协议

- 增强 RDP 剪贴板、虚拟盘和文件传输能力

- 新增 AIOps 上下文工具、告警 RCA trace 落库和回放接口

- 重构 docs 模块文档并补充终端、堡垒机、监控、AI Agent 设计
This commit is contained in:
Logan.Li committed 2026-07-05 23:49:36 +08:00
1 parent a9513c2056
commit e0d652d211
169 files changed
+12192 -9116

No files matched your search

@@ -78,6 +78,7 @@ export function RdpAuditViewer({ open, onOpenChange, sessionId, mode }: RdpAudit
const canvasRef = useRef<HTMLCanvasElement>(null);
const wsRef = useRef<WebSocket | null>(null);
const timerRef = useRef<number | null>(null);
const replaySeqRef = useRef(0);
const [fullscreen, setFullscreen] = useState(false);
const [status, setStatus] = useState('');
const [loading, setLoading] = useState(false);
@@ -100,13 +101,26 @@ export function RdpAuditViewer({ open, onOpenChange, sessionId, mode }: RdpAudit
ctx.putImageData(new ImageData(pixels, frame.width, frame.height), frame.x, frame.y);
};
const drawReplay = async (nextIndex: number) => {
const frame = playableFrames[nextIndex];
const clearReplayCanvas = () => {
const canvas = canvasRef.current;
const ctx = canvas?.getContext('2d');
if (!frame || !canvas || !ctx) return;
if (!canvas || !ctx) return null;
ctx.imageSmoothingEnabled = false;
await drawImageFrame(ctx, frame);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
return ctx;
};
const drawReplay = async (nextIndex: number, rebuild = false) => {
const ctx = rebuild ? clearReplayCanvas() : canvasRef.current?.getContext('2d');
if (!ctx) return;
ctx.imageSmoothingEnabled = false;
const start = rebuild ? 0 : nextIndex;
for (let i = start; i <= nextIndex; i += 1) {
const frame = playableFrames[i];
if (frame) await drawImageFrame(ctx, frame);
}
};
useEffect(() => {
@@ -161,6 +175,11 @@ export function RdpAuditViewer({ open, onOpenChange, sessionId, mode }: RdpAudit
const text = typeof raw === 'string' ? raw : String(raw || '');
const record = JSON.parse(text || '{}') as RdpRecord;
const list = (record.frames || []).filter((f) => f.type === 'bitmap' && f.image);
const sessionFrame = (record.frames || []).find((f) => f.type === 'session' && f.width && f.height);
const sizeFrame = sessionFrame || list.find((f) => f.width && f.height);
if (sizeFrame?.width && sizeFrame?.height) {
setCanvasSize({ width: sizeFrame.width, height: sizeFrame.height });
}
setFrames(list);
setStatus(list.length ? '' : '该 RDP 会话没有可播放画面帧');
setPlaying(list.length > 0);
@@ -196,7 +215,12 @@ export function RdpAuditViewer({ open, onOpenChange, sessionId, mode }: RdpAudit
}, [playing, mode, playableFrames.length]);
useEffect(() => {
if (mode === 'replay' && playableFrames.length > 0) void drawReplay(index);
if (mode !== 'replay' || playableFrames.length === 0) return;
const seq = replaySeqRef.current + 1;
replaySeqRef.current = seq;
void drawReplay(index, true).then(() => {
if (replaySeqRef.current !== seq) return;
});
}, [index, playableFrames.length, mode]);
const displayHeight = fullscreen ? 'calc(100vh - 88px)' : '560px';