chore: migrate to oxlint and oxfmt
Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
This commit is contained in:
@@ -4,38 +4,28 @@ import { stripRedundantSubsystemPrefixForConsole } from "../logging.js";
|
||||
|
||||
describe("stripRedundantSubsystemPrefixForConsole", () => {
|
||||
it("drops '<subsystem>:' prefix", () => {
|
||||
expect(
|
||||
stripRedundantSubsystemPrefixForConsole("discord: hello", "discord"),
|
||||
).toBe("hello");
|
||||
expect(stripRedundantSubsystemPrefixForConsole("discord: hello", "discord")).toBe("hello");
|
||||
});
|
||||
|
||||
it("drops '<Subsystem>:' prefix case-insensitively", () => {
|
||||
expect(
|
||||
stripRedundantSubsystemPrefixForConsole("WhatsApp: hello", "whatsapp"),
|
||||
).toBe("hello");
|
||||
expect(stripRedundantSubsystemPrefixForConsole("WhatsApp: hello", "whatsapp")).toBe("hello");
|
||||
});
|
||||
|
||||
it("drops '<subsystem> ' prefix", () => {
|
||||
expect(
|
||||
stripRedundantSubsystemPrefixForConsole(
|
||||
"discord gateway: closed",
|
||||
"discord",
|
||||
),
|
||||
).toBe("gateway: closed");
|
||||
expect(stripRedundantSubsystemPrefixForConsole("discord gateway: closed", "discord")).toBe(
|
||||
"gateway: closed",
|
||||
);
|
||||
});
|
||||
|
||||
it("drops '[subsystem]' prefix", () => {
|
||||
expect(
|
||||
stripRedundantSubsystemPrefixForConsole(
|
||||
"[discord] connection stalled",
|
||||
"discord",
|
||||
),
|
||||
).toBe("connection stalled");
|
||||
expect(stripRedundantSubsystemPrefixForConsole("[discord] connection stalled", "discord")).toBe(
|
||||
"connection stalled",
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps messages that do not start with the subsystem", () => {
|
||||
expect(
|
||||
stripRedundantSubsystemPrefixForConsole("discordant: hello", "discord"),
|
||||
).toBe("discordant: hello");
|
||||
expect(stripRedundantSubsystemPrefixForConsole("discordant: hello", "discord")).toBe(
|
||||
"discordant: hello",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,8 +28,7 @@ function normalizeConsoleStyle(style?: string): ConsoleStyle {
|
||||
|
||||
function resolveConsoleSettings(): ConsoleSettings {
|
||||
const cfg: ClawdbotConfig["logging"] | undefined =
|
||||
(loggingState.overrideSettings as LoggerSettings | null) ??
|
||||
loadConfig().logging;
|
||||
(loggingState.overrideSettings as LoggerSettings | null) ?? loadConfig().logging;
|
||||
const level = normalizeConsoleLevel(cfg?.consoleLevel);
|
||||
const style = normalizeConsoleStyle(cfg?.consoleStyle);
|
||||
return { level, style };
|
||||
@@ -64,11 +63,8 @@ export function setConsoleSubsystemFilter(filters?: string[] | null): void {
|
||||
loggingState.consoleSubsystemFilter = null;
|
||||
return;
|
||||
}
|
||||
const normalized = filters
|
||||
.map((value) => value.trim())
|
||||
.filter((value) => value.length > 0);
|
||||
loggingState.consoleSubsystemFilter =
|
||||
normalized.length > 0 ? normalized : null;
|
||||
const normalized = filters.map((value) => value.trim()).filter((value) => value.length > 0);
|
||||
loggingState.consoleSubsystemFilter = normalized.length > 0 ? normalized : null;
|
||||
}
|
||||
|
||||
export function shouldLogSubsystemToConsole(subsystem: string): boolean {
|
||||
@@ -76,9 +72,7 @@ export function shouldLogSubsystemToConsole(subsystem: string): boolean {
|
||||
if (!filter || filter.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return filter.some(
|
||||
(prefix) => subsystem === prefix || subsystem.startsWith(`${prefix}/`),
|
||||
);
|
||||
return filter.some((prefix) => subsystem === prefix || subsystem.startsWith(`${prefix}/`));
|
||||
}
|
||||
|
||||
const SUPPRESSED_CONSOLE_PREFIXES = [
|
||||
@@ -91,9 +85,7 @@ const SUPPRESSED_CONSOLE_PREFIXES = [
|
||||
|
||||
function shouldSuppressConsoleMessage(message: string): boolean {
|
||||
if (isVerbose()) return false;
|
||||
return SUPPRESSED_CONSOLE_PREFIXES.some((prefix) =>
|
||||
message.startsWith(prefix),
|
||||
);
|
||||
return SUPPRESSED_CONSOLE_PREFIXES.some((prefix) => message.startsWith(prefix));
|
||||
}
|
||||
|
||||
function isEpipeError(err: unknown): boolean {
|
||||
|
||||
@@ -12,9 +12,7 @@ export type LogLevel = (typeof ALLOWED_LOG_LEVELS)[number];
|
||||
|
||||
export function normalizeLogLevel(level?: string, fallback: LogLevel = "info") {
|
||||
const candidate = (level ?? fallback).trim();
|
||||
return ALLOWED_LOG_LEVELS.includes(candidate as LogLevel)
|
||||
? (candidate as LogLevel)
|
||||
: fallback;
|
||||
return ALLOWED_LOG_LEVELS.includes(candidate as LogLevel) ? (candidate as LogLevel) : fallback;
|
||||
}
|
||||
|
||||
export function levelToMinLevel(level: LogLevel): number {
|
||||
|
||||
@@ -34,8 +34,7 @@ export type LoggerResolvedSettings = ResolvedSettings;
|
||||
|
||||
function resolveSettings(): ResolvedSettings {
|
||||
const cfg: ClawdbotConfig["logging"] | undefined =
|
||||
(loggingState.overrideSettings as LoggerSettings | null) ??
|
||||
loadConfig().logging;
|
||||
(loggingState.overrideSettings as LoggerSettings | null) ?? loadConfig().logging;
|
||||
const level = normalizeLogLevel(cfg?.level, "info");
|
||||
const file = cfg?.file ?? defaultRollingPathForToday();
|
||||
return { level, file };
|
||||
@@ -47,9 +46,7 @@ function settingsChanged(a: ResolvedSettings | null, b: ResolvedSettings) {
|
||||
}
|
||||
|
||||
export function isFileLogLevelEnabled(level: LogLevel): boolean {
|
||||
const settings =
|
||||
(loggingState.cachedSettings as ResolvedSettings | null) ??
|
||||
resolveSettings();
|
||||
const settings = (loggingState.cachedSettings as ResolvedSettings | null) ?? resolveSettings();
|
||||
if (!loggingState.cachedSettings) loggingState.cachedSettings = settings;
|
||||
if (settings.level === "silent") return false;
|
||||
return levelToMinLevel(level) <= levelToMinLevel(settings.level);
|
||||
@@ -106,10 +103,7 @@ export function getChildLogger(
|
||||
}
|
||||
|
||||
// Baileys expects a pino-like logger shape. Provide a lightweight adapter.
|
||||
export function toPinoLikeLogger(
|
||||
logger: TsLogger<LogObj>,
|
||||
level: LogLevel,
|
||||
): PinoLikeLogger {
|
||||
export function toPinoLikeLogger(logger: TsLogger<LogObj>, level: LogLevel): PinoLikeLogger {
|
||||
const buildChild = (bindings?: Record<string, unknown>) =>
|
||||
toPinoLikeLogger(
|
||||
logger.getSubLogger({
|
||||
@@ -180,11 +174,7 @@ function pruneOldRollingLogs(dir: string): void {
|
||||
const cutoff = Date.now() - MAX_LOG_AGE_MS;
|
||||
for (const entry of entries) {
|
||||
if (!entry.isFile()) continue;
|
||||
if (
|
||||
!entry.name.startsWith(`${LOG_PREFIX}-`) ||
|
||||
!entry.name.endsWith(LOG_SUFFIX)
|
||||
)
|
||||
continue;
|
||||
if (!entry.name.startsWith(`${LOG_PREFIX}-`) || !entry.name.endsWith(LOG_SUFFIX)) continue;
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
try {
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
@@ -20,9 +20,7 @@ describe("parseLogLine", () => {
|
||||
expect(parsed?.time).toBe("2026-01-09T01:38:41.523Z");
|
||||
expect(parsed?.level).toBe("info");
|
||||
expect(parsed?.subsystem).toBe("gateway/channels/whatsapp");
|
||||
expect(parsed?.message).toBe(
|
||||
'{"subsystem":"gateway/channels/whatsapp"} connected',
|
||||
);
|
||||
expect(parsed?.message).toBe('{"subsystem":"gateway/channels/whatsapp"} connected');
|
||||
expect(parsed?.raw).toBe(line);
|
||||
});
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@ function parseMetaName(raw?: unknown): { subsystem?: string; module?: string } {
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
||||
return {
|
||||
subsystem:
|
||||
typeof parsed.subsystem === "string" ? parsed.subsystem : undefined,
|
||||
subsystem: typeof parsed.subsystem === "string" ? parsed.subsystem : undefined,
|
||||
module: typeof parsed.module === "string" ? parsed.module : undefined,
|
||||
};
|
||||
} catch {
|
||||
@@ -40,8 +39,7 @@ export function parseLogLine(raw: string): ParsedLogLine | null {
|
||||
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
||||
const meta = parsed._meta as Record<string, unknown> | undefined;
|
||||
const nameMeta = parseMetaName(meta?.name);
|
||||
const levelRaw =
|
||||
typeof meta?.logLevelName === "string" ? meta.logLevelName : undefined;
|
||||
const levelRaw = typeof meta?.logLevelName === "string" ? meta.logLevelName : undefined;
|
||||
return {
|
||||
time:
|
||||
typeof parsed.time === "string"
|
||||
|
||||
@@ -71,11 +71,7 @@ describe("redactSensitiveText", () => {
|
||||
patterns: defaults,
|
||||
});
|
||||
expect(output).toBe(
|
||||
[
|
||||
"-----BEGIN PRIVATE KEY-----",
|
||||
"…redacted…",
|
||||
"-----END PRIVATE KEY-----",
|
||||
].join("\n"),
|
||||
["-----BEGIN PRIVATE KEY-----", "…redacted…", "-----END PRIVATE KEY-----"].join("\n"),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -76,9 +76,7 @@ function redactPemBlock(block: string): string {
|
||||
function redactMatch(match: string, groups: string[]): string {
|
||||
if (match.includes("PRIVATE KEY-----")) return redactPemBlock(match);
|
||||
const token =
|
||||
groups
|
||||
.filter((value) => typeof value === "string" && value.length > 0)
|
||||
.at(-1) ?? match;
|
||||
groups.filter((value) => typeof value === "string" && value.length > 0).at(-1) ?? match;
|
||||
const masked = maskToken(token);
|
||||
if (token === match) return masked;
|
||||
return match.replace(token, masked);
|
||||
@@ -102,10 +100,7 @@ function resolveConfigRedaction(): RedactOptions {
|
||||
};
|
||||
}
|
||||
|
||||
export function redactSensitiveText(
|
||||
text: string,
|
||||
options?: RedactOptions,
|
||||
): string {
|
||||
export function redactSensitiveText(text: string, options?: RedactOptions): string {
|
||||
if (!text) return text;
|
||||
const resolved = options ?? resolveConfigRedaction();
|
||||
if (normalizeMode(resolved.mode) === "off") return text;
|
||||
|
||||
@@ -22,10 +22,7 @@ export type SubsystemLogger = {
|
||||
child: (name: string) => SubsystemLogger;
|
||||
};
|
||||
|
||||
function shouldLogToConsole(
|
||||
level: LogLevel,
|
||||
settings: { level: LogLevel },
|
||||
): boolean {
|
||||
function shouldLogToConsole(level: LogLevel, settings: { level: LogLevel }): boolean {
|
||||
if (settings.level === "silent") return false;
|
||||
const current = levelToMinLevel(level);
|
||||
const min = levelToMinLevel(settings.level);
|
||||
@@ -47,37 +44,18 @@ function getColorForConsole(): ChalkInstance {
|
||||
process.env.FORCE_COLOR.trim() !== "0";
|
||||
if (process.env.NO_COLOR && !hasForceColor) return new Chalk({ level: 0 });
|
||||
const hasTty = Boolean(process.stdout.isTTY || process.stderr.isTTY);
|
||||
return hasTty || isRichConsoleEnv()
|
||||
? new Chalk({ level: 1 })
|
||||
: new Chalk({ level: 0 });
|
||||
return hasTty || isRichConsoleEnv() ? new Chalk({ level: 1 }) : new Chalk({ level: 0 });
|
||||
}
|
||||
|
||||
const SUBSYSTEM_COLORS = [
|
||||
"cyan",
|
||||
"green",
|
||||
"yellow",
|
||||
"blue",
|
||||
"magenta",
|
||||
"red",
|
||||
] as const;
|
||||
const SUBSYSTEM_COLOR_OVERRIDES: Record<
|
||||
string,
|
||||
(typeof SUBSYSTEM_COLORS)[number]
|
||||
> = {
|
||||
const SUBSYSTEM_COLORS = ["cyan", "green", "yellow", "blue", "magenta", "red"] as const;
|
||||
const SUBSYSTEM_COLOR_OVERRIDES: Record<string, (typeof SUBSYSTEM_COLORS)[number]> = {
|
||||
"gmail-watcher": "blue",
|
||||
};
|
||||
const SUBSYSTEM_PREFIXES_TO_DROP = [
|
||||
"gateway",
|
||||
"channels",
|
||||
"providers",
|
||||
] as const;
|
||||
const SUBSYSTEM_PREFIXES_TO_DROP = ["gateway", "channels", "providers"] as const;
|
||||
const SUBSYSTEM_MAX_SEGMENTS = 2;
|
||||
const CHANNEL_SUBSYSTEM_PREFIXES = new Set<string>(CHAT_CHANNEL_ORDER);
|
||||
|
||||
function pickSubsystemColor(
|
||||
color: ChalkInstance,
|
||||
subsystem: string,
|
||||
): ChalkInstance {
|
||||
function pickSubsystemColor(color: ChalkInstance, subsystem: string): ChalkInstance {
|
||||
const override = SUBSYSTEM_COLOR_OVERRIDES[subsystem];
|
||||
if (override) return color[override];
|
||||
let hash = 0;
|
||||
@@ -94,9 +72,7 @@ function formatSubsystemForConsole(subsystem: string): string {
|
||||
const original = parts.join("/") || subsystem;
|
||||
while (
|
||||
parts.length > 0 &&
|
||||
SUBSYSTEM_PREFIXES_TO_DROP.includes(
|
||||
parts[0] as (typeof SUBSYSTEM_PREFIXES_TO_DROP)[number],
|
||||
)
|
||||
SUBSYSTEM_PREFIXES_TO_DROP.includes(parts[0] as (typeof SUBSYSTEM_PREFIXES_TO_DROP)[number])
|
||||
) {
|
||||
parts.shift();
|
||||
}
|
||||
@@ -132,10 +108,7 @@ export function stripRedundantSubsystemPrefixForConsole(
|
||||
const prefix = message.slice(0, displaySubsystem.length);
|
||||
if (prefix.toLowerCase() !== displaySubsystem.toLowerCase()) return message;
|
||||
|
||||
const next = message.slice(
|
||||
displaySubsystem.length,
|
||||
displaySubsystem.length + 1,
|
||||
);
|
||||
const next = message.slice(displaySubsystem.length, displaySubsystem.length + 1);
|
||||
if (next !== ":" && next !== " ") return message;
|
||||
|
||||
let i = displaySubsystem.length;
|
||||
@@ -153,9 +126,7 @@ function formatConsoleLine(opts: {
|
||||
meta?: Record<string, unknown>;
|
||||
}): string {
|
||||
const displaySubsystem =
|
||||
opts.style === "json"
|
||||
? opts.subsystem
|
||||
: formatSubsystemForConsole(opts.subsystem);
|
||||
opts.style === "json" ? opts.subsystem : formatSubsystemForConsole(opts.subsystem);
|
||||
if (opts.style === "json") {
|
||||
return JSON.stringify({
|
||||
time: new Date().toISOString(),
|
||||
@@ -176,14 +147,8 @@ function formatConsoleLine(opts: {
|
||||
: opts.level === "debug" || opts.level === "trace"
|
||||
? color.gray
|
||||
: color.cyan;
|
||||
const displayMessage = stripRedundantSubsystemPrefixForConsole(
|
||||
opts.message,
|
||||
displaySubsystem,
|
||||
);
|
||||
const time =
|
||||
opts.style === "pretty"
|
||||
? color.gray(new Date().toISOString().slice(11, 19))
|
||||
: "";
|
||||
const displayMessage = stripRedundantSubsystemPrefixForConsole(opts.message, displaySubsystem);
|
||||
const time = opts.style === "pretty" ? color.gray(new Date().toISOString().slice(11, 19)) : "";
|
||||
const prefixToken = prefixColor(prefix);
|
||||
const head = [time, prefixToken].filter(Boolean).join(" ");
|
||||
return `${head} ${levelColor(displayMessage)}`;
|
||||
@@ -192,16 +157,10 @@ function formatConsoleLine(opts: {
|
||||
function writeConsoleLine(level: LogLevel, line: string) {
|
||||
const sanitized =
|
||||
process.platform === "win32" && process.env.GITHUB_ACTIONS === "true"
|
||||
? line
|
||||
.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "?")
|
||||
.replace(/[\uD800-\uDFFF]/g, "?")
|
||||
? line.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "?").replace(/[\uD800-\uDFFF]/g, "?")
|
||||
: line;
|
||||
const sink = loggingState.rawConsole ?? console;
|
||||
if (
|
||||
loggingState.forceConsoleToStderr ||
|
||||
level === "error" ||
|
||||
level === "fatal"
|
||||
) {
|
||||
if (loggingState.forceConsoleToStderr || level === "error" || level === "fatal") {
|
||||
(sink.error ?? console.error)(sanitized);
|
||||
} else if (level === "warn") {
|
||||
(sink.warn ?? console.warn)(sanitized);
|
||||
@@ -218,9 +177,9 @@ function logToFile(
|
||||
) {
|
||||
if (level === "silent") return;
|
||||
const safeLevel = level as Exclude<LogLevel, "silent">;
|
||||
const method = (fileLogger as unknown as Record<string, unknown>)[
|
||||
safeLevel
|
||||
] as unknown as ((...args: unknown[]) => void) | undefined;
|
||||
const method = (fileLogger as unknown as Record<string, unknown>)[safeLevel] as unknown as
|
||||
| ((...args: unknown[]) => void)
|
||||
| undefined;
|
||||
if (typeof method !== "function") return;
|
||||
if (meta && Object.keys(meta).length > 0) {
|
||||
method.call(fileLogger, meta, message);
|
||||
@@ -235,11 +194,7 @@ export function createSubsystemLogger(subsystem: string): SubsystemLogger {
|
||||
if (!fileLogger) fileLogger = getChildLogger({ subsystem });
|
||||
return fileLogger;
|
||||
};
|
||||
const emit = (
|
||||
level: LogLevel,
|
||||
message: string,
|
||||
meta?: Record<string, unknown>,
|
||||
) => {
|
||||
const emit = (level: LogLevel, message: string, meta?: Record<string, unknown>) => {
|
||||
const consoleSettings = getConsoleSettings();
|
||||
let consoleMessageOverride: string | undefined;
|
||||
let fileMeta = meta;
|
||||
@@ -258,10 +213,7 @@ export function createSubsystemLogger(subsystem: string): SubsystemLogger {
|
||||
const line = formatConsoleLine({
|
||||
level,
|
||||
subsystem,
|
||||
message:
|
||||
consoleSettings.style === "json"
|
||||
? message
|
||||
: (consoleMessageOverride ?? message),
|
||||
message: consoleSettings.style === "json" ? message : (consoleMessageOverride ?? message),
|
||||
style: consoleSettings.style,
|
||||
meta: fileMeta,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user