Files
platform/src/servers/api/task-logger.ts
T
2026-02-16 19:34:35 +00:00

96 lines
2.6 KiB
TypeScript

import { mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { getTaskLogsDir } from '@@/data-path';
import type { TaskInfo } from '@@/api/chat-types';
type ChatMessage =
| { role: 'user'; text: string }
| { role: 'assistant'; text: string }
| {
role: 'tool';
toolName: string;
toolInput: Record<string, unknown>;
toolUseId: string;
output?: string;
isError?: boolean;
}
| { role: 'result'; costUsd: number; durationMs: number; numTurns: number; isError: boolean }
| { role: 'error'; text: string };
type TaskLog = {
taskName: string;
taskDirName: string;
entryName: string;
entryType: 'file' | 'directory';
provider: string;
model: string;
startedAt: string;
completedAt: string | null;
messages: ChatMessage[];
};
type LogEntry = {
email: string;
filePath: string;
log: TaskLog;
};
const activeLogs = new Map<string, LogEntry>();
let logCounter = 0;
export function createTaskLog(email: string, taskInfo: TaskInfo, provider: string, model: string): string {
const logId = `log_${Date.now()}_${++logCounter}`;
const dir = getTaskLogsDir(email);
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `${timestamp}-${taskInfo.taskDirName}.json`;
const filePath = join(dir, filename);
const log: TaskLog = {
taskName: taskInfo.taskName,
taskDirName: taskInfo.taskDirName,
entryName: taskInfo.entryName,
entryType: taskInfo.entryType,
provider,
model,
startedAt: new Date().toISOString(),
completedAt: null,
messages: [],
};
activeLogs.set(logId, { email, filePath, log });
return logId;
}
export function appendToLog(logId: string, message: ChatMessage) {
const entry = activeLogs.get(logId);
if (!entry) return;
// For tool results, update the existing tool message instead of appending
if (message.role === 'tool' && message.output !== undefined) {
const existing = entry.log.messages.find((m) => m.role === 'tool' && m.toolUseId === message.toolUseId);
if (existing && existing.role === 'tool') {
existing.output = message.output;
existing.isError = message.isError;
return;
}
}
entry.log.messages.push(message);
}
export async function finalizeLog(logId: string) {
const entry = activeLogs.get(logId);
if (!entry) return;
entry.log.completedAt = new Date().toISOString();
try {
await mkdir(join(entry.filePath, '..'), { recursive: true });
await Bun.write(entry.filePath, JSON.stringify(entry.log, null, 2));
} catch (err) {
console.error('[task-logger] Failed to write log:', err);
}
activeLogs.delete(logId);
}