sin-code-ai / src /lib /telemetry /SentinelLogger.ts
GitHub Actions
Deploy SIN-Code-AI from GitHub Actions
dbeb746
Raw
History Blame Contribute Delete
4.21 kB
import { randomUUID } from 'node:crypto';
import { appendFile, mkdir } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { homedir, hostname } from 'node:os';
import { TEMPLATE_AGENT_ID, TEMPLATE_AGENT_NAME, TEMPLATE_AGENT_NAMESPACE } from '../../metadata.js';
import { getSupabaseClient } from '../../supabase.js';
export type TemplateLogLevel = 'info' | 'warn' | 'error' | 'debug';
export type TemplateLogStatus = 'started' | 'accepted' | 'completed' | 'failed';
export type TemplateLogEntry = {
agentId: string;
agentName: string;
namespace: string;
runId: string;
requestId: string;
surface: string;
event: string;
action?: string;
level: TemplateLogLevel;
status: TemplateLogStatus;
details?: Record<string, unknown>;
durationMs?: number;
createdAt: string;
};
export const TEMPLATE_LOG_PATH = join(homedir(), '.config', 'sin', TEMPLATE_AGENT_ID, 'events.ndjson');
export const TEMPLATE_TELEMETRY_TABLE = 'fleet_telemetry';
export class TemplateLogger {
readonly runId: string;
constructor(runId: string = randomUUID()) {
this.runId = runId;
}
async log(input: {
surface: string;
event: string;
level?: TemplateLogLevel;
status?: TemplateLogStatus;
action?: string;
details?: Record<string, unknown>;
durationMs?: number;
requestId?: string;
}) {
const entry: TemplateLogEntry = {
agentId: TEMPLATE_AGENT_ID,
agentName: TEMPLATE_AGENT_NAME,
namespace: TEMPLATE_AGENT_NAMESPACE,
runId: this.runId,
requestId: input.requestId || randomUUID(),
surface: input.surface,
event: input.event,
action: input.action,
level: input.level || 'info',
status: input.status || 'completed',
details: input.details,
durationMs: input.durationMs,
createdAt: new Date().toISOString(),
};
await mkdir(dirname(TEMPLATE_LOG_PATH), { recursive: true });
await appendFile(TEMPLATE_LOG_PATH, `${JSON.stringify(entry)}\n`, 'utf8');
try {
await getSupabaseClient().from(TEMPLATE_TELEMETRY_TABLE).insert({
agent_id: entry.agentId,
run_id: entry.runId,
level: entry.level,
event: entry.event,
details: {
...(entry.details || {}),
action: entry.action,
requestId: entry.requestId,
surface: entry.surface,
status: entry.status,
hostname: hostname(),
},
duration_ms: entry.durationMs,
created_at: entry.createdAt,
});
} catch {}
const line = `[${entry.createdAt}] [${entry.agentId}] [${entry.surface}] [${entry.level.toUpperCase()}] ${entry.event}`;
if (entry.level === 'error') {
console.error(line, entry.details || '');
} else if (entry.level === 'warn') {
console.warn(line, entry.details || '');
} else {
console.log(line, entry.details || '');
}
return entry;
}
started(surface: string, event: string, details?: Record<string, unknown>, requestId?: string, action?: string) {
return this.log({ surface, event, details, requestId, action, level: 'info', status: 'started' });
}
accepted(surface: string, event: string, details?: Record<string, unknown>, requestId?: string, action?: string) {
return this.log({ surface, event, details, requestId, action, level: 'info', status: 'accepted' });
}
succeeded(
surface: string,
event: string,
details?: Record<string, unknown>,
durationMs?: number,
requestId?: string,
action?: string,
) {
return this.log({
surface,
event,
details,
durationMs,
requestId,
action,
level: 'info',
status: 'completed',
});
}
failed(surface: string, event: string, error: unknown, durationMs?: number, requestId?: string, action?: string) {
const details = error instanceof Error ? { error: error.message } : { error: String(error) };
return this.log({
surface,
event,
details,
durationMs,
requestId,
action,
level: 'error',
status: 'failed',
});
}
}
export function createTemplateLogger(runId?: string) {
return new TemplateLogger(runId);
}