UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

180 lines 5.26 kB
/** * Output formatters for JSON and pretty logging */ /** * Format log level as uppercase string */ export function formatLevel(label, _number) { return { level: label.toUpperCase(), }; } /** * Custom timestamp formatter */ export function formatTimestamp(time) { const date = new Date(time); return { time: date.toISOString(), }; } /** * Format timestamp for development (human readable) */ export function formatTimestampDev(time) { const date = new Date(time); return { time: date.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: 'UTC', }) + ' Z', }; } /** * Sanitize error objects for JSON serialization */ // biome-ignore lint/suspicious/noExplicitAny: Dynamic return type for serialized errors export function serializeError(err) { if (err instanceof Error) { return { name: err.name, message: err.message, stack: process.env.NODE_ENV === 'production' ? err.stack?.split('\n').slice(0, 3).join('\n') // Limit stack traces in production : err.stack, // biome-ignore lint/suspicious/noExplicitAny: Dynamic error properties code: err.code, // biome-ignore lint/suspicious/noExplicitAny: Dynamic error properties statusCode: err.statusCode, // biome-ignore lint/suspicious/noExplicitAny: Dynamic error properties status: err.status, }; } return err; } /** * Format log entry for production JSON output */ export function formatProductionLog(log) { const formatted = { level: log.level, time: log.time, pid: log.pid, hostname: log.hostname, msg: log.msg, }; // Add correlation ID if present if (log.correlationId) { formatted.correlationId = log.correlationId; } // Add any additional properties, but handle special cases Object.keys(log).forEach(key => { if (['level', 'time', 'pid', 'hostname', 'msg', 'correlationId'].includes(key)) { return; } const value = log[key]; // Handle Error objects if (value instanceof Error) { formatted[key] = serializeError(value); } // Handle circular references else if (typeof value === 'object' && value !== null) { try { // Quick check for circular references JSON.stringify(value); formatted[key] = value; } catch { formatted[key] = '[Circular Reference]'; } } else { formatted[key] = value; } }); return formatted; } /** * Format log entry for development pretty output */ export function formatDevelopmentLog(log) { const formatted = { ...formatProductionLog(log), // Add development-specific formatting prettyTime: formatTimestampDev(Date.now()).time, }; return formatted; } /** * Create Pino formatters configuration */ export function createFormatters(isProduction = false) { return { level: formatLevel, log: isProduction ? formatProductionLog : formatDevelopmentLog, time: isProduction ? formatTimestamp : formatTimestampDev, }; } /** * Message formatter for custom log messages */ export function formatMessage(template, // biome-ignore lint/suspicious/noExplicitAny: Dynamic bindings for template bindings, level) { // Simple template replacement for {key} patterns return template.replace(/\{(\w+)\}/g, (match, key) => { if (key === 'level') return level.toUpperCase(); if (key === 'levelLabel') return level; return bindings[key] || match; }); } /** * Color mapping for different log levels (development only) */ export const levelColors = { fatal: '\x1b[41m', // Red background error: '\x1b[31m', // Red text warn: '\x1b[33m', // Yellow text info: '\x1b[36m', // Cyan text http: '\x1b[35m', // Magenta text debug: '\x1b[90m', // Gray text trace: '\x1b[37m', // White text reset: '\x1b[0m', // Reset }; /** * Get color for log level */ export function getLevelColor(level) { return levelColors[level.toLowerCase()] || levelColors.info; } /** * Create pretty print formatter for development */ export function createPrettyFormatter() { return { translateTime: 'SYS:standard', ignore: 'pid,hostname,time', messageFormat: '{levelLabel} - {msg}', customPrettifiers: { time: (timestamp) => { return new Date(timestamp).toLocaleTimeString(); }, level: (label) => { const color = getLevelColor(label); const reset = levelColors.reset; return `${color}${label.toUpperCase()}${reset}`; }, hostname: () => { return process.env.NODE_ENV || 'development'; }, }, colorize: true, levelFirst: true, }; } //# sourceMappingURL=formatters.js.map