refactor(cli): remove deprecated verbose logging flags

This commit is contained in:
2026-02-17 00:57:33 -08:00
parent 23b78e6c9b
commit 1cd1cdb11d
27 changed files with 213 additions and 208 deletions

View File

@@ -12,7 +12,7 @@ test("parseArgs parses booleans and value flags", () => {
"6000",
"--log-level",
"warn",
"--verbose",
"--debug",
]);
assert.equal(args.start, true);
@@ -20,7 +20,7 @@ test("parseArgs parses booleans and value flags", () => {
assert.equal(args.backend, "hyprland");
assert.equal(args.texthookerPort, 6000);
assert.equal(args.logLevel, "warn");
assert.equal(args.verbose, true);
assert.equal(args.debug, true);
});
test("parseArgs ignores missing value after --log-level", () => {
@@ -38,7 +38,7 @@ test("hasExplicitCommand and shouldStartApp preserve command intent", () => {
assert.equal(hasExplicitCommand(toggle), true);
assert.equal(shouldStartApp(toggle), true);
const noCommand = parseArgs(["--verbose"]);
const noCommand = parseArgs(["--log-level", "warn"]);
assert.equal(hasExplicitCommand(noCommand), false);
assert.equal(shouldStartApp(noCommand), false);

View File

@@ -31,7 +31,7 @@ export interface CliArgs {
socketPath?: string;
backend?: string;
texthookerPort?: number;
verbose: boolean;
debug: boolean;
logLevel?: "debug" | "info" | "warn" | "error";
}
@@ -67,7 +67,7 @@ export function parseArgs(argv: string[]): CliArgs {
autoStartOverlay: false,
generateConfig: false,
backupOverwrite: false,
verbose: false,
debug: false,
};
const readValue = (value?: string): string | undefined => {
@@ -114,7 +114,7 @@ export function parseArgs(argv: string[]): CliArgs {
else if (arg === "--generate-config") args.generateConfig = true;
else if (arg === "--backup-overwrite") args.backupOverwrite = true;
else if (arg === "--help") args.help = true;
else if (arg === "--verbose") args.verbose = true;
else if (arg === "--debug") args.debug = true;
else if (arg.startsWith("--log-level=")) {
const value = arg.split("=", 2)[1]?.toLowerCase();
if (

View File

@@ -26,16 +26,15 @@ SubMiner CLI commands:
--mark-audio-card Mark last card as audio card
--open-runtime-options Open runtime options palette
--auto-start-overlay Auto-hide mpv subtitles on connect (show overlay)
--socket PATH Override MPV IPC socket/pipe path
--backend BACKEND Override window tracker backend (auto, hyprland, sway, x11, macos)
--port PORT Texthooker server port (default: ${defaultTexthookerPort})
--verbose Enable debug logging (equivalent to --log-level debug)
--log-level LEVEL Set log level: debug, info, warn, error
--generate-config Generate default config.jsonc from centralized config registry
--config-path PATH Target config path for --generate-config
--backup-overwrite With --generate-config, backup and overwrite existing file
--dev Run in development mode
--debug Alias for --dev
--help Show this help
--socket PATH Override MPV IPC socket/pipe path
--backend BACKEND Override window tracker backend (auto, hyprland, sway, x11, macos)
--port PORT Texthooker server port (default: ${defaultTexthookerPort})
--debug Enable app/dev mode
--log-level LEVEL Set log level: debug, info, warn, error
--generate-config Generate default config.jsonc from centralized config registry
--config-path PATH Target config path for --generate-config
--backup-overwrite With --generate-config, backup and overwrite existing file
--dev Alias for --debug (app/dev mode)
--help Show this help
`);
}

View File

@@ -33,7 +33,7 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
autoStartOverlay: false,
generateConfig: false,
backupOverwrite: false,
verbose: false,
debug: false,
...overrides,
};
}

View File

@@ -178,7 +178,7 @@ export class MpvIpcClient implements MpvClient {
this.transport = new MpvSocketTransport({
socketPath,
onConnect: () => {
logger.info("Connected to MPV socket");
logger.debug("Connected to MPV socket");
this.connected = true;
this.connecting = false;
this.socket = this.transport.getSocket();
@@ -192,7 +192,7 @@ export class MpvIpcClient implements MpvClient {
this.deps.autoStartOverlay ||
this.deps.getResolvedConfig().auto_start_overlay === true;
if (this.firstConnection && shouldAutoStart) {
logger.info("Auto-starting overlay, hiding mpv subtitles");
logger.debug("Auto-starting overlay, hiding mpv subtitles");
setTimeout(() => {
this.deps.setOverlayVisible(true);
}, 100);

View File

@@ -35,7 +35,7 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
autoStartOverlay: false,
generateConfig: false,
backupOverwrite: false,
verbose: false,
debug: false,
...overrides,
};
}
@@ -43,7 +43,7 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
test("runStartupBootstrapRuntimeService configures startup state and starts lifecycle", () => {
const calls: string[] = [];
const args = makeArgs({
verbose: true,
logLevel: "debug",
socketPath: "/tmp/custom.sock",
texthookerPort: 9001,
backend: "x11",
@@ -52,7 +52,7 @@ test("runStartupBootstrapRuntimeService configures startup state and starts life
});
const result = runStartupBootstrapRuntimeService({
argv: ["node", "main.ts", "--verbose"],
argv: ["node", "main.ts", "--log-level", "debug"],
parseArgs: () => args,
setLogLevel: (level, source) => calls.push(`setLog:${level}:${source}`),
forceX11Backend: () => calls.push("forceX11"),
@@ -77,15 +77,14 @@ test("runStartupBootstrapRuntimeService configures startup state and starts life
]);
});
test("runStartupBootstrapRuntimeService prefers --log-level over --verbose", () => {
test("runStartupBootstrapRuntimeService keeps log-level precedence for repeated calls", () => {
const calls: string[] = [];
const args = makeArgs({
logLevel: "warn",
verbose: true,
});
runStartupBootstrapRuntimeService({
argv: ["node", "main.ts", "--log-level", "warn", "--verbose"],
argv: ["node", "main.ts", "--log-level", "warn"],
parseArgs: () => args,
setLogLevel: (level, source) => calls.push(`setLog:${level}:${source}`),
forceX11Backend: () => calls.push("forceX11"),
@@ -103,6 +102,27 @@ test("runStartupBootstrapRuntimeService prefers --log-level over --verbose", ()
]);
});
test("runStartupBootstrapRuntimeService keeps --debug separate from log verbosity", () => {
const calls: string[] = [];
const args = makeArgs({
debug: true,
});
runStartupBootstrapRuntimeService({
argv: ["node", "main.ts", "--debug"],
parseArgs: () => args,
setLogLevel: (level, source) => calls.push(`setLog:${level}:${source}`),
forceX11Backend: () => calls.push("forceX11"),
enforceUnsupportedWaylandMode: () => calls.push("enforceWayland"),
getDefaultSocketPath: () => "/tmp/default.sock",
defaultTexthookerPort: 5174,
runGenerateConfigFlow: () => false,
startAppLifecycle: () => calls.push("startLifecycle"),
});
assert.deepEqual(calls, ["forceX11", "enforceWayland", "startLifecycle"]);
});
test("runStartupBootstrapRuntimeService skips lifecycle when generate-config flow handled", () => {
const calls: string[] = [];
const args = makeArgs({ generateConfig: true, logLevel: "warn" });

View File

@@ -47,8 +47,6 @@ export function runStartupBootstrapRuntimeService(
if (initialArgs.logLevel) {
deps.setLogLevel(initialArgs.logLevel, "cli");
} else if (initialArgs.verbose) {
deps.setLogLevel("debug", "cli");
}
deps.forceX11Backend(initialArgs);

View File

@@ -791,7 +791,7 @@ async function enrichYomitanPos1(
mecabTokens = await deps.tokenizeWithMecab(text);
} catch (err) {
const error = err as Error;
console.warn(
logger.warn(
"Failed to enrich Yomitan tokens with MeCab POS:",
error.message,
`tokenCount=${tokens.length}`,
@@ -801,7 +801,7 @@ async function enrichYomitanPos1(
}
if (!mecabTokens || mecabTokens.length === 0) {
console.warn(
logger.warn(
"MeCab enrichment returned no tokens; preserving Yomitan token output.",
`tokenCount=${tokens.length}`,
`textLength=${text.length}`,
@@ -886,7 +886,7 @@ async function ensureYomitanParserWindow(
}
return true;
} catch (err) {
console.error(
logger.error(
"Failed to initialize Yomitan parser window:",
(err as Error).message,
);
@@ -977,8 +977,8 @@ async function parseWithYomitanInternalParser(
}
return enrichYomitanPos1(yomitanTokens, deps, text);
} catch (err) {
console.error("Yomitan parser request failed:", (err as Error).message);
} catch (err) {
logger.error("Yomitan parser request failed:", (err as Error).message);
return null;
}
}
@@ -1066,7 +1066,7 @@ export async function tokenizeSubtitleService(
};
}
} catch (err) {
console.error("Tokenization error:", (err as Error).message);
logger.error("Tokenization error:", (err as Error).message);
}
return { text: displayText, tokens: null };

View File

@@ -2,6 +2,9 @@ import * as fs from "fs";
import * as path from "path";
import * as readline from "readline";
import { CliArgs } from "../../cli/args";
import { createLogger } from "../../logger";
const logger = createLogger("core:config-gen");
function formatBackupTimestamp(date = new Date()): string {
const pad = (v: number): string => String(v).padStart(2, "0");
@@ -40,13 +43,13 @@ export async function generateDefaultConfigFile(
const backupPath = `${targetPath}.bak.${formatBackupTimestamp()}`;
fs.copyFileSync(targetPath, backupPath);
fs.writeFileSync(targetPath, template, "utf-8");
console.log(`Backed up existing config to ${backupPath}`);
console.log(`Generated config at ${targetPath}`);
logger.info(`Backed up existing config to ${backupPath}`);
logger.info(`Generated config at ${targetPath}`);
return 0;
}
if (!process.stdin.isTTY || !process.stdout.isTTY) {
console.error(
logger.error(
`Config exists at ${targetPath}. Re-run with --backup-overwrite to back up and overwrite.`,
);
return 1;
@@ -56,15 +59,15 @@ export async function generateDefaultConfigFile(
`Config exists at ${targetPath}. Back up and overwrite? [y/N] `,
);
if (!confirmed) {
console.log("Config generation cancelled.");
logger.info("Config generation cancelled.");
return 0;
}
const backupPath = `${targetPath}.bak.${formatBackupTimestamp()}`;
fs.copyFileSync(targetPath, backupPath);
fs.writeFileSync(targetPath, template, "utf-8");
console.log(`Backed up existing config to ${backupPath}`);
console.log(`Generated config at ${targetPath}`);
logger.info(`Backed up existing config to ${backupPath}`);
logger.info(`Generated config at ${targetPath}`);
return 0;
}
@@ -73,6 +76,6 @@ export async function generateDefaultConfigFile(
fs.mkdirSync(parentDir, { recursive: true });
}
fs.writeFileSync(targetPath, template, "utf-8");
console.log(`Generated config at ${targetPath}`);
logger.info(`Generated config at ${targetPath}`);
return 0;
}

View File

@@ -1,4 +1,7 @@
import { CliArgs, shouldStartApp } from "../../cli/args";
import { createLogger } from "../../logger";
const logger = createLogger("core:electron-backend");
function getElectronOzonePlatformHint(): string | null {
const hint = process.env.ELECTRON_OZONE_PLATFORM_HINT?.trim().toLowerCase();
@@ -34,6 +37,6 @@ export function enforceUnsupportedWaylandMode(args: CliArgs): void {
const message =
"Unsupported Electron backend: Wayland. Set ELECTRON_OZONE_PLATFORM_HINT=x11 and restart SubMiner.";
console.error(message);
logger.error(message);
throw new Error(message);
}

View File

@@ -1,5 +1,8 @@
import { Notification, nativeImage } from "electron";
import * as fs from "fs";
import { createLogger } from "../../logger";
const logger = createLogger("core:notification");
export function showDesktopNotification(
title: string,
@@ -24,7 +27,7 @@ export function showDesktopNotification(
if (fs.existsSync(options.icon)) {
notificationOptions.icon = options.icon;
} else {
console.warn("Notification icon file not found:", options.icon);
logger.warn("Notification icon file not found", options.icon);
}
} else if (
typeof options.icon === "string" &&
@@ -36,14 +39,14 @@ export function showDesktopNotification(
Buffer.from(base64Data, "base64"),
);
if (image.isEmpty()) {
console.warn(
logger.warn(
"Notification icon created from base64 is empty - image format may not be supported by Electron",
);
} else {
notificationOptions.icon = image;
}
} catch (err) {
console.error("Failed to create notification icon from base64:", err);
logger.error("Failed to create notification icon from base64", err);
}
} else {
notificationOptions.icon = options.icon;

View File

@@ -3,6 +3,7 @@ import * as https from "https";
import * as path from "path";
import * as fs from "fs";
import * as childProcess from "child_process";
import { createLogger } from "../logger";
import {
JimakuApiResponse,
JimakuConfig,
@@ -12,6 +13,8 @@ import {
JimakuMediaInfo,
} from "../types";
const logger = createLogger("main:jimaku");
function execCommand(
command: string,
): Promise<{ stdout: string; stderr: string }> {
@@ -30,28 +33,26 @@ export async function resolveJimakuApiKey(
config: JimakuConfig,
): Promise<string | null> {
if (config.apiKey && config.apiKey.trim()) {
console.log("[jimaku] API key found in config");
logger.debug("API key found in config");
return config.apiKey.trim();
}
if (config.apiKeyCommand && config.apiKeyCommand.trim()) {
try {
const { stdout } = await execCommand(config.apiKeyCommand);
const key = stdout.trim();
console.log(
`[jimaku] apiKeyCommand result: ${key.length > 0 ? "key obtained" : "empty output"}`,
logger.debug(
`apiKeyCommand result: ${key.length > 0 ? "key obtained" : "empty output"}`,
);
return key.length > 0 ? key : null;
} catch (err) {
console.error(
"Failed to run jimaku.apiKeyCommand:",
logger.error(
"Failed to run jimaku.apiKeyCommand",
(err as Error).message,
);
return null;
}
}
console.log(
"[jimaku] No API key configured (neither apiKey nor apiKeyCommand set)",
);
logger.debug("No API key configured (neither apiKey nor apiKeyCommand set)");
return null;
}
@@ -75,7 +76,7 @@ export async function jimakuFetchJson<T>(
url.searchParams.set(key, String(value));
}
console.log(`[jimaku] GET ${url.toString()}`);
logger.debug(`GET ${url.toString()}`);
const transport = url.protocol === "https:" ? https : http;
return new Promise((resolve) => {
@@ -95,13 +96,13 @@ export async function jimakuFetchJson<T>(
});
res.on("end", () => {
const status = res.statusCode || 0;
console.log(`[jimaku] Response HTTP ${status} for ${endpoint}`);
logger.debug(`Response HTTP ${status} for ${endpoint}`);
if (status >= 200 && status < 300) {
try {
const parsed = JSON.parse(data) as T;
resolve({ ok: true, data: parsed });
} catch {
console.error(`[jimaku] JSON parse error: ${data.slice(0, 200)}`);
logger.error(`JSON parse error: ${data.slice(0, 200)}`);
resolve({
ok: false,
error: { error: "Failed to parse Jimaku response JSON." },
@@ -119,7 +120,7 @@ export async function jimakuFetchJson<T>(
} catch {
// Ignore parse errors.
}
console.error(`[jimaku] API error: ${errorMessage}`);
logger.error(`API error: ${errorMessage}`);
resolve({
ok: false,
@@ -135,7 +136,7 @@ export async function jimakuFetchJson<T>(
);
req.on("error", (err) => {
console.error(`[jimaku] Network error: ${(err as Error).message}`);
logger.error(`Network error: ${(err as Error).message}`);
resolve({
ok: false,
error: { error: `Jimaku request failed: ${(err as Error).message}` },