From cff6b16ab66aeba210a5c6cb42a5e01659957091 Mon Sep 17 00:00:00 2001 From: xiaoFjun-eng <66506420+xiaoFjun-eng@users.noreply.github.com> Date: Tue, 19 May 2026 15:04:59 +0800 Subject: [PATCH] Fix type (#1242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 完善所有用到的type对象,并添加中文注释 * 补充遗失的type * 修复claude-for-chrome-mcp中的type和interface类型缺失 * 完善注释 --- .../claude-for-chrome-mcp/src/bridgeClient.ts | 19 ++-- .../@ant/claude-for-chrome-mcp/src/index.ts | 5 +- .../src/mcpSocketClient.ts | 28 ++++-- .../claude-for-chrome-mcp/src/toolCalls.ts | 13 ++- .../@ant/claude-for-chrome-mcp/src/types.ts | 97 ++++++++++++++++--- .../@ant/computer-use-mcp/src/pixelCompare.ts | 11 ++- .../@ant/computer-use-mcp/src/toolCalls.ts | 12 ++- packages/@ant/computer-use-mcp/src/types.ts | 23 +++-- packages/@ant/ink/src/core/dom.ts | 8 +- .../ink/src/core/events/terminal-event.ts | 7 +- packages/@ant/ink/src/core/reconciler.ts | 23 ++++- src/utils/auth.ts | 4 +- src/utils/claudeInChrome/mcpServer.ts | 21 ++-- src/utils/computerUse/hostAdapter.ts | 21 ++-- 14 files changed, 213 insertions(+), 79 deletions(-) diff --git a/packages/@ant/claude-for-chrome-mcp/src/bridgeClient.ts b/packages/@ant/claude-for-chrome-mcp/src/bridgeClient.ts index 846284778..22fbb7c87 100644 --- a/packages/@ant/claude-for-chrome-mcp/src/bridgeClient.ts +++ b/packages/@ant/claude-for-chrome-mcp/src/bridgeClient.ts @@ -9,6 +9,7 @@ import { SocketConnectionError } from "./mcpSocketClient.js"; import { localPlatformLabel, type BridgePermissionRequest, + toLoggerDetail, type ChromeExtensionInfo, type ClaudeForChromeContext, type PermissionMode, @@ -579,9 +580,9 @@ export class BridgeClient implements SocketClient { const durationMs = Date.now() - this.connectionStartTime; logger.error( `[${serverName}] Failed to create WebSocket after ${durationMs}ms:`, - error, - ); - trackEvent?.("chrome_bridge_connection_failed", { + toLoggerDetail(error), + ) + trackEvent?.('chrome_bridge_connection_failed', { duration_ms: durationMs, error_type: "websocket_error", reconnect_attempt: this.reconnectAttempts, @@ -619,7 +620,10 @@ export class BridgeClient implements SocketClient { ); this.handleMessage(message); } catch (error) { - logger.error(`[${serverName}] Failed to parse bridge message:`, error); + logger.error( + `[${serverName}] Failed to parse bridge message:`, + toLoggerDetail(error), + ) } }); @@ -865,8 +869,11 @@ export class BridgeClient implements SocketClient { const allowed = await pending.onPermissionRequest(request); this.sendPermissionResponse(requestId, allowed); } catch (error) { - logger.error(`[${serverName}] Error handling permission request:`, error); - this.sendPermissionResponse(requestId, false); + logger.error( + `[${serverName}] Error handling permission request:`, + toLoggerDetail(error), + ) + this.sendPermissionResponse(requestId, false) } } diff --git a/packages/@ant/claude-for-chrome-mcp/src/index.ts b/packages/@ant/claude-for-chrome-mcp/src/index.ts index 68b694069..928e3fc14 100644 --- a/packages/@ant/claude-for-chrome-mcp/src/index.ts +++ b/packages/@ant/claude-for-chrome-mcp/src/index.ts @@ -8,8 +8,11 @@ export { localPlatformLabel } from "./types.js"; export type { BridgeConfig, ChromeExtensionInfo, + ChromeBridgeTrackEventMetadata, ClaudeForChromeContext, Logger, + LoggerDetail, PermissionMode, SocketClient, -} from "./types.js"; +} from './types.js' +export { toLoggerDetail } from './types.js' diff --git a/packages/@ant/claude-for-chrome-mcp/src/mcpSocketClient.ts b/packages/@ant/claude-for-chrome-mcp/src/mcpSocketClient.ts index 1443d7f5c..2b11b0f0d 100644 --- a/packages/@ant/claude-for-chrome-mcp/src/mcpSocketClient.ts +++ b/packages/@ant/claude-for-chrome-mcp/src/mcpSocketClient.ts @@ -8,7 +8,8 @@ import type { ClaudeForChromeContext, PermissionMode, PermissionOverrides, -} from "./types.js"; +} from './types.js' +import { toLoggerDetail } from './types.js' export class SocketConnectionError extends Error { constructor(message: string) { @@ -87,8 +88,11 @@ class McpSocketClient { try { await this.validateSocketSecurity(socketPath); } catch (error) { - this.connecting = false; - logger.info(`[${serverName}] Security validation failed:`, error); + this.connecting = false + logger.info( + `[${serverName}] Security validation failed:`, + toLoggerDetail(error), + ) // Don't retry on security failures (wrong perms/owner) - those won't // self-resolve. Only the error handler retries on transient errors. return; @@ -148,16 +152,22 @@ class McpSocketClient { logger.info(`[${serverName}] Received unknown message: ${message}`); } } catch (error) { - logger.info(`[${serverName}] Failed to parse message:`, error); + logger.info( + `[${serverName}] Failed to parse message:`, + toLoggerDetail(error), + ) } } }); - this.socket.on("error", (error: Error & { code?: string }) => { - clearTimeout(connectTimeout); - logger.info(`[${serverName}] Socket error (code: ${error.code}):`, error); - this.connected = false; - this.connecting = false; + this.socket.on('error', (error: Error & { code?: string }) => { + clearTimeout(connectTimeout) + logger.info( + `[${serverName}] Socket error (code: ${error.code}):`, + toLoggerDetail(error), + ) + this.connected = false + this.connecting = false if ( error.code && diff --git a/packages/@ant/claude-for-chrome-mcp/src/toolCalls.ts b/packages/@ant/claude-for-chrome-mcp/src/toolCalls.ts index 65bd8d178..06b5b27fc 100644 --- a/packages/@ant/claude-for-chrome-mcp/src/toolCalls.ts +++ b/packages/@ant/claude-for-chrome-mcp/src/toolCalls.ts @@ -6,7 +6,8 @@ import type { PermissionMode, PermissionOverrides, SocketClient, -} from "./types.js"; +} from './types.js' +import { toLoggerDetail } from './types.js' export const handleToolCall = async ( context: ClaudeForChromeContext, @@ -44,7 +45,10 @@ export const handleToolCall = async ( return handleToolCallDisconnected(context); } catch (error) { - context.logger.info(`[${context.serverName}] Error calling tool:`, error); + context.logger.info( + `[${context.serverName}] Error calling tool:`, + toLoggerDetail(error), + ) if (error instanceof SocketConnectionError) { return handleToolCallDisconnected(context); @@ -165,9 +169,8 @@ async function handleToolCallConnected( // Fallback for unexpected result format context.logger.warn( - `[${context.serverName}] Unexpected result format from socket bridge`, - response, - ); + `[${context.serverName}] Unexpected result format from socket bridge: ${JSON.stringify(response)}`, + ) return { content: [{ type: "text", text: JSON.stringify(response) }], diff --git a/packages/@ant/claude-for-chrome-mcp/src/types.ts b/packages/@ant/claude-for-chrome-mcp/src/types.ts index 0334882b9..caa5b6bcd 100644 --- a/packages/@ant/claude-for-chrome-mcp/src/types.ts +++ b/packages/@ant/claude-for-chrome-mcp/src/types.ts @@ -1,11 +1,84 @@ -export interface Logger { - info: (message: string, ...args: unknown[]) => void; - error: (message: string, ...args: unknown[]) => void; - warn: (message: string, ...args: unknown[]) => void; - debug: (message: string, ...args: unknown[]) => void; - silly: (message: string, ...args: unknown[]) => void; +/** + * Logger 第二参数的可选类型。 + * 调用方通过 util.format 追加详情,实践中多为 catch 到的异常对象。 + */ +export type LoggerDetail = Error | NodeJS.ErrnoException + +/** 将 unknown 收窄为 LoggerDetail,供 catch 块传给 logger 使用。 */ +export function toLoggerDetail(detail: unknown): LoggerDetail | undefined { + return detail instanceof Error ? detail : undefined } +/** 宿主注入的日志接口,与 DebugLogger(util.format)对齐。 */ +export interface Logger { + info: (message: string, detail?: LoggerDetail) => void // 信息 + error: (message: string, detail?: LoggerDetail) => void // 错误 + warn: (message: string, detail?: LoggerDetail) => void // 警告 + debug: (message: string, detail?: LoggerDetail) => void // 调试 + silly: (message: string, detail?: LoggerDetail) => void // 最细粒度调试 +} + +/** + * Bridge 连接失败时的 error_type 枚举。 + * 由 bridgeClient 在 getUserId / getOAuthToken / WebSocket 创建失败时上报。 + */ +export type ChromeBridgeConnectionErrorType = + | 'no_user_id' // 无法获取用户 UUID + | 'no_oauth_token' // 无法获取 OAuth token + | 'websocket_error' // WebSocket 创建或运行异常 + +/** 工具调用相关遥测元数据(started / completed / timeout / error)。 */ +export type ChromeBridgeToolCallMetadata = { + tool_name: string // MCP 工具名 + tool_use_id: string // 本次调用的 UUID + duration_ms?: number // 耗时(毫秒) + timeout_ms?: number // 超时阈值(毫秒),仅 timeout 事件 + error_message?: string // 错误摘要(截断),仅 error 事件 +} + +/** Bridge 连接失败遥测元数据。 */ +export type ChromeBridgeConnectionFailedMetadata = { + duration_ms: number // 自连接开始到失败的耗时(毫秒) + error_type: ChromeBridgeConnectionErrorType // 失败原因分类 + reconnect_attempt: number // 当前重连尝试次数 +} + +/** Bridge 开始连接遥测元数据。 */ +export type ChromeBridgeConnectionStartedMetadata = { + bridge_url: string // 目标 WebSocket URL(含用户路径) +} + +/** Bridge 断开连接遥测元数据。 */ +export type ChromeBridgeDisconnectedMetadata = { + close_code: number // WebSocket 关闭码 + duration_since_connect_ms: number // 自连接成功到断开的时长(毫秒) + reconnect_attempt: number // 即将进行的重连序号 +} + +/** Bridge 连接成功遥测元数据。 */ +export type ChromeBridgeConnectionSucceededMetadata = { + duration_ms: number // 自开始到连接就绪的耗时(毫秒) + status: 'paired' | 'waiting' // paired=已配对扩展;waiting=等待扩展接入 +} + +/** Bridge 重连次数耗尽遥测元数据。 */ +export type ChromeBridgeReconnectExhaustedMetadata = { + total_attempts: number // 累计重连次数上限 +} + +/** + * trackEvent 回调的 metadata 联合类型。 + * 各变体对应 bridgeClient 内 chrome_bridge_* 事件;null 表示无附加字段。 + */ +export type ChromeBridgeTrackEventMetadata = + | ChromeBridgeToolCallMetadata + | ChromeBridgeConnectionFailedMetadata + | ChromeBridgeConnectionStartedMetadata + | ChromeBridgeDisconnectedMetadata + | ChromeBridgeConnectionSucceededMetadata + | ChromeBridgeReconnectExhaustedMetadata + | null // 无元数据(如 peer_connected / peer_disconnected) + export type PermissionMode = | "ask" | "skip_all_permission_checks" @@ -47,12 +120,12 @@ export interface ClaudeForChromeContext { /** Bridge WebSocket configuration. When provided, uses bridge instead of socket. */ bridgeConfig?: BridgeConfig; /** If set, permission mode is sent to the extension immediately on bridge connection. */ - initialPermissionMode?: PermissionMode; - /** Optional callback to track telemetry events for bridge connections */ - trackEvent?: ( - eventName: K, - metadata: Record | null, - ) => void; + initialPermissionMode?: PermissionMode + /** Bridge 遥测回调;eventName 为 chrome_bridge_* 事件名 */ + trackEvent?: ( + eventName: string, // 事件名 + metadata: ChromeBridgeTrackEventMetadata, // 事件元数据 + ) => void /** Called when user pairs with an extension via the browser pairing flow. */ onExtensionPaired?: (deviceId: string, name: string) => void; /** Returns the previously paired deviceId, if any. */ diff --git a/packages/@ant/computer-use-mcp/src/pixelCompare.ts b/packages/@ant/computer-use-mcp/src/pixelCompare.ts index 05153f602..2349cf6c6 100644 --- a/packages/@ant/computer-use-mcp/src/pixelCompare.ts +++ b/packages/@ant/computer-use-mcp/src/pixelCompare.ts @@ -19,8 +19,8 @@ * this package never imports it — the crop is a function parameter. */ -import type { ScreenshotResult } from "./executor.js"; -import type { Logger } from "./types.js"; +import type { ScreenshotResult } from './executor.js' +import { type Logger, toLoggerDetail } from './types.js' /** Injected by the host. See `ComputerUseHostAdapter.cropRawPatch`. */ export type CropRawPatchFn = ( @@ -165,7 +165,10 @@ export async function validateClickTarget( } catch (err) { // Skip validation on technical errors, execute action anyway. // Battle-tested: validation failure must never block the click. - logger.debug("[pixelCompare] validation error, skipping", err); - return { valid: true, skipped: true }; + logger.debug( + '[pixelCompare] validation error, skipping', + toLoggerDetail(err), + ) + return { valid: true, skipped: true } } } diff --git a/packages/@ant/computer-use-mcp/src/toolCalls.ts b/packages/@ant/computer-use-mcp/src/toolCalls.ts index f40302fcf..bf9e87552 100644 --- a/packages/@ant/computer-use-mcp/src/toolCalls.ts +++ b/packages/@ant/computer-use-mcp/src/toolCalls.ts @@ -60,7 +60,8 @@ import type { Logger, ResolvedAppRequest, TeachStepRequest, -} from "./types.js"; +} from './types.js' +import { toLoggerDetail } from './types.js' /** * Finder is never hidden by the hide loop (hiding Finder kills the Desktop), @@ -4183,9 +4184,12 @@ export async function handleToolCall( // the gated tools — the gates run before the executor in every handler. // For ungated tools, the executor may have been mid-call; that's fine — // the result is still a tool error, never an implicit success. - const msg = err instanceof Error ? err.message : String(err); - logger.error(`[${serverName}] tool=${name} threw: ${msg}`, err); - return errorResult(`Tool "${name}" failed: ${msg}`, "executor_threw"); + const msg = err instanceof Error ? err.message : String(err) + logger.error( + `[${serverName}] tool=${name} threw: ${msg}`, + toLoggerDetail(err), + ) + return errorResult(`Tool "${name}" failed: ${msg}`, 'executor_threw') } } diff --git a/packages/@ant/computer-use-mcp/src/types.ts b/packages/@ant/computer-use-mcp/src/types.ts index 656f795dc..70746dcda 100644 --- a/packages/@ant/computer-use-mcp/src/types.ts +++ b/packages/@ant/computer-use-mcp/src/types.ts @@ -8,13 +8,24 @@ import type { * cross-respawn `scaleCoord` survival. */ export type ScreenshotDims = Omit; -/** Shape mirrors claude-for-chrome-mcp/src/types.ts:1-7 */ +/** + * Logger 第二参数的可选类型(与 claude-for-chrome-mcp 对齐)。 + * 实践中多为 catch 到的 Error。 + */ +export type LoggerDetail = Error | NodeJS.ErrnoException + +/** 将 unknown 收窄为 LoggerDetail,供 catch 块传给 logger 使用。 */ +export function toLoggerDetail(detail: unknown): LoggerDetail | undefined { + return detail instanceof Error ? detail : undefined +} + +/** 宿主注入的日志接口(与 claude-for-chrome-mcp/src/types.ts 对齐)。 */ export interface Logger { - info: (message: string, ...args: unknown[]) => void; - error: (message: string, ...args: unknown[]) => void; - warn: (message: string, ...args: unknown[]) => void; - debug: (message: string, ...args: unknown[]) => void; - silly: (message: string, ...args: unknown[]) => void; + info: (message: string, detail?: LoggerDetail) => void // 信息 + error: (message: string, detail?: LoggerDetail) => void // 错误 + warn: (message: string, detail?: LoggerDetail) => void // 警告 + debug: (message: string, detail?: LoggerDetail) => void // 调试 + silly: (message: string, detail?: LoggerDetail) => void // 最细粒度调试 } /** diff --git a/packages/@ant/ink/src/core/dom.ts b/packages/@ant/ink/src/core/dom.ts index 993dadd3a..3699ce622 100644 --- a/packages/@ant/ink/src/core/dom.ts +++ b/packages/@ant/ink/src/core/dom.ts @@ -1,3 +1,4 @@ +import type { EventHandlerProps } from './events/event-handlers.js' import type { FocusManager } from './focus.js' import { createLayoutNode } from './layout/engine.js' import type { LayoutNode } from './layout/node.js' @@ -45,10 +46,9 @@ export type DOMElement = { dirty: boolean // Set by the reconciler's hideInstance/unhideInstance; survives style updates. isHidden?: boolean - // Event handlers set by the reconciler for the capture/bubble dispatcher. - // Stored separately from attributes so handler identity changes don't - // mark dirty and defeat the blit optimization. - _eventHandlers?: Record + // 协调器写入的事件处理器(捕获/冒泡分发用)。 + // 与 attributes 分离,避免 handler 引用变化触发 dirty 破坏 blit 优化。 + _eventHandlers?: Partial // 见 event-handlers.ts EventHandlerProps // Scroll state for overflow: 'scroll' boxes. scrollTop is the number of // rows the content is scrolled down by. scrollHeight/scrollViewportHeight diff --git a/packages/@ant/ink/src/core/events/terminal-event.ts b/packages/@ant/ink/src/core/events/terminal-event.ts index 9a86bf8b2..99b49d4c9 100644 --- a/packages/@ant/ink/src/core/events/terminal-event.ts +++ b/packages/@ant/ink/src/core/events/terminal-event.ts @@ -101,7 +101,10 @@ export class TerminalEvent extends Event { _prepareForTarget(_target: EventTarget): void {} } +import type { EventHandlerProps } from './event-handlers.js' + +/** 终端事件系统的目标节点(DOM 树节点或根节点)。 */ export type EventTarget = { - parentNode: EventTarget | undefined - _eventHandlers?: Record + parentNode: EventTarget | undefined // 父节点,根节点为 undefined + _eventHandlers?: Partial // 事件处理器,与 dom.ts DOMElement 同构 } diff --git a/packages/@ant/ink/src/core/reconciler.ts b/packages/@ant/ink/src/core/reconciler.ts index 4d75923b8..49efe3255 100644 --- a/packages/@ant/ink/src/core/reconciler.ts +++ b/packages/@ant/ink/src/core/reconciler.ts @@ -20,7 +20,10 @@ import { type TextNode, } from './dom.js' import { Dispatcher } from './events/dispatcher.js' -import { EVENT_HANDLER_PROPS } from './events/event-handlers.js' +import { + EVENT_HANDLER_PROPS, + type EventHandlerProps, +} from './events/event-handlers.js' import { getFocusManager, getRootNode } from './focus.js' import { LayoutDisplay } from './layout/node.js' import applyStyles, { type Styles, type TextStyles } from './styles.js' @@ -109,7 +112,11 @@ type HostContext = { isInsideText: boolean } -function setEventHandler(node: DOMElement, key: string, value: unknown): void { +function setEventHandler( + node: DOMElement, + key: K, + value: EventHandlerProps[K], +): void { if (!node._eventHandlers) { node._eventHandlers = {} } @@ -133,7 +140,11 @@ function applyProp(node: DOMElement, key: string, value: unknown): void { } if (EVENT_HANDLER_PROPS.has(key)) { - setEventHandler(node, key, value) + setEventHandler( + node, + key as keyof EventHandlerProps, + value as EventHandlerProps[keyof EventHandlerProps], + ) return } @@ -442,7 +453,11 @@ const reconciler = createReconciler< } if (EVENT_HANDLER_PROPS.has(key)) { - setEventHandler(node, key, value) + setEventHandler( + node, + key as keyof EventHandlerProps, + value as EventHandlerProps[keyof EventHandlerProps], + ) continue } diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 5b913fcfe..8012361f8 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -1901,12 +1901,12 @@ export function getAccountInformation() { accountInfo.apiKeySource = apiKeySource } - // We don't know the organization if we're relying on an external API key or auth token + // 如果我们依赖外部 API 密钥或认证令牌,则不知道组织 if ( authTokenSource === 'claude.ai' || apiKeySource === '/login managed key' ) { - // Get organization name from OAuth account info + // 从 OAuth 账户信息获取组织名称 const orgName = getOauthAccountInfo()?.organizationName if (orgName) { accountInfo.organization = orgName diff --git a/src/utils/claudeInChrome/mcpServer.ts b/src/utils/claudeInChrome/mcpServer.ts index 02f0d629c..58907afd8 100644 --- a/src/utils/claudeInChrome/mcpServer.ts +++ b/src/utils/claudeInChrome/mcpServer.ts @@ -2,6 +2,7 @@ import { type ClaudeForChromeContext, createClaudeForChromeMcpServer, type Logger, + type LoggerDetail, type PermissionMode, } from '@ant/claude-for-chrome-mcp' import { initializeAnalyticsSink } from '../../services/analytics/sink.js' @@ -276,19 +277,19 @@ export async function runClaudeInChromeMcpServer(): Promise { } class DebugLogger implements Logger { - silly(message: string, ...args: unknown[]): void { - logForDebugging(format(message, ...args), { level: 'debug' }) + silly(message: string, detail?: LoggerDetail): void { + logForDebugging(format(message, detail ?? ''), { level: 'debug' }) } - debug(message: string, ...args: unknown[]): void { - logForDebugging(format(message, ...args), { level: 'debug' }) + debug(message: string, detail?: LoggerDetail): void { + logForDebugging(format(message, detail ?? ''), { level: 'debug' }) } - info(message: string, ...args: unknown[]): void { - logForDebugging(format(message, ...args), { level: 'info' }) + info(message: string, detail?: LoggerDetail): void { + logForDebugging(format(message, detail ?? ''), { level: 'info' }) } - warn(message: string, ...args: unknown[]): void { - logForDebugging(format(message, ...args), { level: 'warn' }) + warn(message: string, detail?: LoggerDetail): void { + logForDebugging(format(message, detail ?? ''), { level: 'warn' }) } - error(message: string, ...args: unknown[]): void { - logForDebugging(format(message, ...args), { level: 'error' }) + error(message: string, detail?: LoggerDetail): void { + logForDebugging(format(message, detail ?? ''), { level: 'error' }) } } diff --git a/src/utils/computerUse/hostAdapter.ts b/src/utils/computerUse/hostAdapter.ts index ae99d0b18..4a21b71e8 100644 --- a/src/utils/computerUse/hostAdapter.ts +++ b/src/utils/computerUse/hostAdapter.ts @@ -1,6 +1,7 @@ import type { ComputerUseHostAdapter, Logger, + LoggerDetail, } from '@ant/computer-use-mcp/types' import { format } from 'util' import { logForDebugging } from '../debug.js' @@ -10,20 +11,20 @@ import { getChicagoEnabled, getChicagoSubGates } from './gates.js' import { requireComputerUseSwift } from './swiftLoader.js' class DebugLogger implements Logger { - silly(message: string, ...args: unknown[]): void { - logForDebugging(format(message, ...args), { level: 'debug' }) + silly(message: string, detail?: LoggerDetail): void { + logForDebugging(format(message, detail ?? ''), { level: 'debug' }) } - debug(message: string, ...args: unknown[]): void { - logForDebugging(format(message, ...args), { level: 'debug' }) + debug(message: string, detail?: LoggerDetail): void { + logForDebugging(format(message, detail ?? ''), { level: 'debug' }) } - info(message: string, ...args: unknown[]): void { - logForDebugging(format(message, ...args), { level: 'info' }) + info(message: string, detail?: LoggerDetail): void { + logForDebugging(format(message, detail ?? ''), { level: 'info' }) } - warn(message: string, ...args: unknown[]): void { - logForDebugging(format(message, ...args), { level: 'warn' }) + warn(message: string, detail?: LoggerDetail): void { + logForDebugging(format(message, detail ?? ''), { level: 'warn' }) } - error(message: string, ...args: unknown[]): void { - logForDebugging(format(message, ...args), { level: 'error' }) + error(message: string, detail?: LoggerDetail): void { + logForDebugging(format(message, detail ?? ''), { level: 'error' }) } }