Files
2026-07-13 12:20:32 +08:00

68 lines
2.4 KiB
JavaScript

const { LOG_BUFFER_MAX, VM_LOG_BUFFER_MAX } = require("./config");
const { workspace } = require("./workspace");
const { logChatMessage } = require("./session/chat-log");
// Ring buffer of VM-level log lines (server stdout/stderr) replayed to new
// WebSocket clients on connect.
const vmLogBuffer = [];
function broadcastVmLog(line) {
const ts = new Date().toISOString();
vmLogBuffer.push({ ts, line });
if (vmLogBuffer.length > VM_LOG_BUFFER_MAX)
vmLogBuffer.splice(0, vmLogBuffer.length - VM_LOG_BUFFER_MAX);
const payload = JSON.stringify({ type: "vm_log", ts, content: line });
for (const client of workspace.streamClients) {
if (client.readyState === 1) client.send(payload);
}
}
// Patch console so every log line is also broadcast to connected clients.
// This runs as a side effect on first require so all subsequent console calls
// are captured, including those from submodules loaded after this one.
const _origLog = console.log;
const _origWarn = console.warn;
const _origError = console.error;
console.log = (...args) => {
_origLog(...args);
broadcastVmLog(args.map(String).join(" "));
};
console.warn = (...args) => {
_origWarn(...args);
broadcastVmLog("[warn] " + args.map(String).join(" "));
};
console.error = (...args) => {
_origError(...args);
broadcastVmLog("[error] " + args.map(String).join(" "));
};
function broadcast(message) {
const msgObj =
typeof message === "string" ? { type: "raw", content: message } : message;
const payload =
typeof message === "string" ? message : JSON.stringify(message);
// Persist chat messages (user + assistant turns) to the session log.
if (msgObj.type === "chat_message" && msgObj.role && msgObj.content) {
logChatMessage({ role: msgObj.role, content: msgObj.content });
}
// Transient events that update the UI in real-time but carry no useful
// state for late-joining clients. Skip buffering to keep the log lean.
const TRANSIENT_TYPES = new Set(["llm_status", "proxy_usage"]);
const ts = new Date().toISOString();
if (!TRANSIENT_TYPES.has(msgObj.type)) {
workspace.logBuffer.push({ ...msgObj, _ts: ts });
if (workspace.logBuffer.length > LOG_BUFFER_MAX) {
workspace.logBuffer = workspace.logBuffer.slice(-LOG_BUFFER_MAX);
}
}
for (const client of workspace.streamClients) {
if (client.readyState === 1) client.send(payload);
}
}
module.exports = { broadcast, broadcastVmLog, vmLogBuffer };