UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

203 lines (202 loc) 5.7 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import * as fs from "fs"; import * as path from "path"; const SENSITIVE_PATTERNS = [ /\b(api[_-]?key|apikey)\s*[:=]\s*['"]?[\w-]+['"]?/gi, /\b(secret|password|token|credential|auth)\s*[:=]\s*['"]?[\w-]+['"]?/gi, /\b(lin_api_[\w]+)/gi, /\b(lin_oauth_[\w]+)/gi, /\b(sk-[\w]+)/gi, /\b(npm_[\w]+)/gi, /\b(ghp_[\w]+)/gi, /\b(ghs_[\w]+)/gi, /Bearer\s+[\w.-]+/gi, /Basic\s+[\w=]+/gi, /postgres(ql)?:\/\/[^@\s]+:[^@\s]+@/gi ]; const SENSITIVE_FIELD_NAMES = [ "password", "token", "apikey", "api_key", "secret", "credential", "authorization", "auth", "accesstoken", "access_token", "refreshtoken", "refresh_token" ]; function redactString(input) { let result = input; for (const pattern of SENSITIVE_PATTERNS) { pattern.lastIndex = 0; result = result.replace(pattern, "[REDACTED]"); } return result; } function sanitizeForLogging(obj) { if (obj === null || obj === void 0) { return obj; } if (typeof obj === "string") { return redactString(obj); } if (Array.isArray(obj)) { return obj.map(sanitizeForLogging); } if (typeof obj === "object") { const sanitized = {}; for (const [key, value] of Object.entries(obj)) { if (SENSITIVE_FIELD_NAMES.some((sf) => key.toLowerCase().includes(sf))) { sanitized[key] = "[REDACTED]"; } else { sanitized[key] = sanitizeForLogging(value); } } return sanitized; } return obj; } var LogLevel = /* @__PURE__ */ ((LogLevel2) => { LogLevel2[LogLevel2["ERROR"] = 0] = "ERROR"; LogLevel2[LogLevel2["WARN"] = 1] = "WARN"; LogLevel2[LogLevel2["INFO"] = 2] = "INFO"; LogLevel2[LogLevel2["DEBUG"] = 3] = "DEBUG"; return LogLevel2; })(LogLevel || {}); class Logger { static instance; logLevel = 2 /* INFO */; logFile; fileLoggingDisabledNotified = false; constructor() { const envLevel = process.env["STACKMEMORY_LOG_LEVEL"]?.toUpperCase(); switch (envLevel) { case "ERROR": this.logLevel = 0 /* ERROR */; break; case "WARN": this.logLevel = 1 /* WARN */; break; case "DEBUG": this.logLevel = 3 /* DEBUG */; break; default: this.logLevel = 2 /* INFO */; } if (this.logLevel === 3 /* DEBUG */ || process.env["STACKMEMORY_LOG_FILE"]) { this.logFile = process.env["STACKMEMORY_LOG_FILE"] || path.join( process.env["HOME"] || ".", ".stackmemory", "logs", "cli.log" ); this.ensureLogDirectory(); } } static getInstance() { if (!Logger.instance) { Logger.instance = new Logger(); } return Logger.instance; } ensureLogDirectory() { if (!this.logFile) return; const logDir = path.dirname(this.logFile); try { if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } } catch (err) { this.logFile = void 0; if (!this.fileLoggingDisabledNotified) { this.fileLoggingDisabledNotified = true; const msg = "[Logger] File logging disabled (failed to create log directory). Falling back to console only."; console.warn(msg); } } } writeLog(entry) { const sanitizedEntry = { ...entry, message: redactString(entry.message), context: entry.context ? sanitizeForLogging(entry.context) : void 0 }; const logLine = JSON.stringify(sanitizedEntry) + "\n"; if (this.logFile) { try { fs.appendFileSync(this.logFile, logLine); } catch (err) { this.logFile = void 0; if (!this.fileLoggingDisabledNotified) { this.fileLoggingDisabledNotified = true; const msg = "[Logger] File logging disabled (write failed). Falling back to console only."; console.warn(msg); } } } if (entry.level <= this.logLevel) { const levelNames = ["ERROR", "WARN", "INFO", "DEBUG"]; const levelName = levelNames[entry.level] || "UNKNOWN"; const consoleMessage = `[${entry.timestamp}] ${levelName}: ${entry.message}`; if (entry.level === 0 /* ERROR */) { console.error(consoleMessage); if (entry.error) { console.error(entry.error.stack); } } else if (entry.level === 1 /* WARN */) { console.warn(consoleMessage); } else { console.log(consoleMessage); } } } error(message, errorOrContext, context) { const isError = errorOrContext instanceof Error; this.writeLog({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), level: 0 /* ERROR */, message, context: isError ? context : errorOrContext, error: isError ? errorOrContext : void 0 }); } warn(message, errorOrContext) { const isError = errorOrContext instanceof Error; this.writeLog({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), level: 1 /* WARN */, message, context: isError ? void 0 : errorOrContext, error: isError ? errorOrContext : void 0 }); } info(message, context) { this.writeLog({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), level: 2 /* INFO */, message, context }); } debug(message, context) { this.writeLog({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), level: 3 /* DEBUG */, message, context }); } } const logger = Logger.getInstance(); export { LogLevel, Logger, logger }; //# sourceMappingURL=logger.js.map