* 完善所有用到的type对象,并添加中文注释

* 补充遗失的type

* 修复claude-for-chrome-mcp中的type和interface类型缺失

* 完善注释
This commit is contained in:
xiaoFjun-eng 2026-05-19 15:04:59 +08:00 committed by James Feng
parent b78beb7c39
commit cff6b16ab6
14 changed files with 213 additions and 79 deletions

View File

@ -9,6 +9,7 @@ import { SocketConnectionError } from "./mcpSocketClient.js";
import { import {
localPlatformLabel, localPlatformLabel,
type BridgePermissionRequest, type BridgePermissionRequest,
toLoggerDetail,
type ChromeExtensionInfo, type ChromeExtensionInfo,
type ClaudeForChromeContext, type ClaudeForChromeContext,
type PermissionMode, type PermissionMode,
@ -579,9 +580,9 @@ export class BridgeClient implements SocketClient {
const durationMs = Date.now() - this.connectionStartTime; const durationMs = Date.now() - this.connectionStartTime;
logger.error( logger.error(
`[${serverName}] Failed to create WebSocket after ${durationMs}ms:`, `[${serverName}] Failed to create WebSocket after ${durationMs}ms:`,
error, toLoggerDetail(error),
); )
trackEvent?.("chrome_bridge_connection_failed", { trackEvent?.('chrome_bridge_connection_failed', {
duration_ms: durationMs, duration_ms: durationMs,
error_type: "websocket_error", error_type: "websocket_error",
reconnect_attempt: this.reconnectAttempts, reconnect_attempt: this.reconnectAttempts,
@ -619,7 +620,10 @@ export class BridgeClient implements SocketClient {
); );
this.handleMessage(message); this.handleMessage(message);
} catch (error) { } 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); const allowed = await pending.onPermissionRequest(request);
this.sendPermissionResponse(requestId, allowed); this.sendPermissionResponse(requestId, allowed);
} catch (error) { } catch (error) {
logger.error(`[${serverName}] Error handling permission request:`, error); logger.error(
this.sendPermissionResponse(requestId, false); `[${serverName}] Error handling permission request:`,
toLoggerDetail(error),
)
this.sendPermissionResponse(requestId, false)
} }
} }

View File

@ -8,8 +8,11 @@ export { localPlatformLabel } from "./types.js";
export type { export type {
BridgeConfig, BridgeConfig,
ChromeExtensionInfo, ChromeExtensionInfo,
ChromeBridgeTrackEventMetadata,
ClaudeForChromeContext, ClaudeForChromeContext,
Logger, Logger,
LoggerDetail,
PermissionMode, PermissionMode,
SocketClient, SocketClient,
} from "./types.js"; } from './types.js'
export { toLoggerDetail } from './types.js'

View File

@ -8,7 +8,8 @@ import type {
ClaudeForChromeContext, ClaudeForChromeContext,
PermissionMode, PermissionMode,
PermissionOverrides, PermissionOverrides,
} from "./types.js"; } from './types.js'
import { toLoggerDetail } from './types.js'
export class SocketConnectionError extends Error { export class SocketConnectionError extends Error {
constructor(message: string) { constructor(message: string) {
@ -87,8 +88,11 @@ class McpSocketClient {
try { try {
await this.validateSocketSecurity(socketPath); await this.validateSocketSecurity(socketPath);
} catch (error) { } catch (error) {
this.connecting = false; this.connecting = false
logger.info(`[${serverName}] Security validation failed:`, error); logger.info(
`[${serverName}] Security validation failed:`,
toLoggerDetail(error),
)
// Don't retry on security failures (wrong perms/owner) - those won't // Don't retry on security failures (wrong perms/owner) - those won't
// self-resolve. Only the error handler retries on transient errors. // self-resolve. Only the error handler retries on transient errors.
return; return;
@ -148,16 +152,22 @@ class McpSocketClient {
logger.info(`[${serverName}] Received unknown message: ${message}`); logger.info(`[${serverName}] Received unknown message: ${message}`);
} }
} catch (error) { } 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 }) => { this.socket.on('error', (error: Error & { code?: string }) => {
clearTimeout(connectTimeout); clearTimeout(connectTimeout)
logger.info(`[${serverName}] Socket error (code: ${error.code}):`, error); logger.info(
this.connected = false; `[${serverName}] Socket error (code: ${error.code}):`,
this.connecting = false; toLoggerDetail(error),
)
this.connected = false
this.connecting = false
if ( if (
error.code && error.code &&

View File

@ -6,7 +6,8 @@ import type {
PermissionMode, PermissionMode,
PermissionOverrides, PermissionOverrides,
SocketClient, SocketClient,
} from "./types.js"; } from './types.js'
import { toLoggerDetail } from './types.js'
export const handleToolCall = async ( export const handleToolCall = async (
context: ClaudeForChromeContext, context: ClaudeForChromeContext,
@ -44,7 +45,10 @@ export const handleToolCall = async (
return handleToolCallDisconnected(context); return handleToolCallDisconnected(context);
} catch (error) { } 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) { if (error instanceof SocketConnectionError) {
return handleToolCallDisconnected(context); return handleToolCallDisconnected(context);
@ -165,9 +169,8 @@ async function handleToolCallConnected(
// Fallback for unexpected result format // Fallback for unexpected result format
context.logger.warn( context.logger.warn(
`[${context.serverName}] Unexpected result format from socket bridge`, `[${context.serverName}] Unexpected result format from socket bridge: ${JSON.stringify(response)}`,
response, )
);
return { return {
content: [{ type: "text", text: JSON.stringify(response) }], content: [{ type: "text", text: JSON.stringify(response) }],

View File

@ -1,11 +1,84 @@
export interface Logger { /**
info: (message: string, ...args: unknown[]) => void; * Logger
error: (message: string, ...args: unknown[]) => void; * util.format catch
warn: (message: string, ...args: unknown[]) => void; */
debug: (message: string, ...args: unknown[]) => void; export type LoggerDetail = Error | NodeJS.ErrnoException
silly: (message: string, ...args: unknown[]) => void;
/** 将 unknown 收窄为 LoggerDetail供 catch 块传给 logger 使用。 */
export function toLoggerDetail(detail: unknown): LoggerDetail | undefined {
return detail instanceof Error ? detail : undefined
} }
/** 宿主注入的日志接口,与 DebugLoggerutil.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 = export type PermissionMode =
| "ask" | "ask"
| "skip_all_permission_checks" | "skip_all_permission_checks"
@ -47,12 +120,12 @@ export interface ClaudeForChromeContext {
/** Bridge WebSocket configuration. When provided, uses bridge instead of socket. */ /** Bridge WebSocket configuration. When provided, uses bridge instead of socket. */
bridgeConfig?: BridgeConfig; bridgeConfig?: BridgeConfig;
/** If set, permission mode is sent to the extension immediately on bridge connection. */ /** If set, permission mode is sent to the extension immediately on bridge connection. */
initialPermissionMode?: PermissionMode; initialPermissionMode?: PermissionMode
/** Optional callback to track telemetry events for bridge connections */ /** Bridge 遥测回调eventName 为 chrome_bridge_* 事件名 */
trackEvent?: <K extends string>( trackEvent?: (
eventName: K, eventName: string, // 事件名
metadata: Record<string, unknown> | null, metadata: ChromeBridgeTrackEventMetadata, // 事件元数据
) => void; ) => void
/** Called when user pairs with an extension via the browser pairing flow. */ /** Called when user pairs with an extension via the browser pairing flow. */
onExtensionPaired?: (deviceId: string, name: string) => void; onExtensionPaired?: (deviceId: string, name: string) => void;
/** Returns the previously paired deviceId, if any. */ /** Returns the previously paired deviceId, if any. */

View File

@ -19,8 +19,8 @@
* this package never imports it the crop is a function parameter. * this package never imports it the crop is a function parameter.
*/ */
import type { ScreenshotResult } from "./executor.js"; import type { ScreenshotResult } from './executor.js'
import type { Logger } from "./types.js"; import { type Logger, toLoggerDetail } from './types.js'
/** Injected by the host. See `ComputerUseHostAdapter.cropRawPatch`. */ /** Injected by the host. See `ComputerUseHostAdapter.cropRawPatch`. */
export type CropRawPatchFn = ( export type CropRawPatchFn = (
@ -165,7 +165,10 @@ export async function validateClickTarget(
} catch (err) { } catch (err) {
// Skip validation on technical errors, execute action anyway. // Skip validation on technical errors, execute action anyway.
// Battle-tested: validation failure must never block the click. // Battle-tested: validation failure must never block the click.
logger.debug("[pixelCompare] validation error, skipping", err); logger.debug(
return { valid: true, skipped: true }; '[pixelCompare] validation error, skipping',
toLoggerDetail(err),
)
return { valid: true, skipped: true }
} }
} }

View File

@ -60,7 +60,8 @@ import type {
Logger, Logger,
ResolvedAppRequest, ResolvedAppRequest,
TeachStepRequest, 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), * 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. // 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 — // For ungated tools, the executor may have been mid-call; that's fine —
// the result is still a tool error, never an implicit success. // the result is still a tool error, never an implicit success.
const msg = err instanceof Error ? err.message : String(err); const msg = err instanceof Error ? err.message : String(err)
logger.error(`[${serverName}] tool=${name} threw: ${msg}`, err); logger.error(
return errorResult(`Tool "${name}" failed: ${msg}`, "executor_threw"); `[${serverName}] tool=${name} threw: ${msg}`,
toLoggerDetail(err),
)
return errorResult(`Tool "${name}" failed: ${msg}`, 'executor_threw')
} }
} }

View File

@ -8,13 +8,24 @@ import type {
* cross-respawn `scaleCoord` survival. */ * cross-respawn `scaleCoord` survival. */
export type ScreenshotDims = Omit<ScreenshotResult, "base64">; export type ScreenshotDims = Omit<ScreenshotResult, "base64">;
/** 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 { export interface Logger {
info: (message: string, ...args: unknown[]) => void; info: (message: string, detail?: LoggerDetail) => void // 信息
error: (message: string, ...args: unknown[]) => void; error: (message: string, detail?: LoggerDetail) => void // 错误
warn: (message: string, ...args: unknown[]) => void; warn: (message: string, detail?: LoggerDetail) => void // 警告
debug: (message: string, ...args: unknown[]) => void; debug: (message: string, detail?: LoggerDetail) => void // 调试
silly: (message: string, ...args: unknown[]) => void; silly: (message: string, detail?: LoggerDetail) => void // 最细粒度调试
} }
/** /**

View File

@ -1,3 +1,4 @@
import type { EventHandlerProps } from './events/event-handlers.js'
import type { FocusManager } from './focus.js' import type { FocusManager } from './focus.js'
import { createLayoutNode } from './layout/engine.js' import { createLayoutNode } from './layout/engine.js'
import type { LayoutNode } from './layout/node.js' import type { LayoutNode } from './layout/node.js'
@ -45,10 +46,9 @@ export type DOMElement = {
dirty: boolean dirty: boolean
// Set by the reconciler's hideInstance/unhideInstance; survives style updates. // Set by the reconciler's hideInstance/unhideInstance; survives style updates.
isHidden?: boolean isHidden?: boolean
// Event handlers set by the reconciler for the capture/bubble dispatcher. // 协调器写入的事件处理器(捕获/冒泡分发用)。
// Stored separately from attributes so handler identity changes don't // 与 attributes 分离,避免 handler 引用变化触发 dirty 破坏 blit 优化。
// mark dirty and defeat the blit optimization. _eventHandlers?: Partial<EventHandlerProps> // 见 event-handlers.ts EventHandlerProps
_eventHandlers?: Record<string, unknown>
// Scroll state for overflow: 'scroll' boxes. scrollTop is the number of // Scroll state for overflow: 'scroll' boxes. scrollTop is the number of
// rows the content is scrolled down by. scrollHeight/scrollViewportHeight // rows the content is scrolled down by. scrollHeight/scrollViewportHeight

View File

@ -101,7 +101,10 @@ export class TerminalEvent extends Event {
_prepareForTarget(_target: EventTarget): void {} _prepareForTarget(_target: EventTarget): void {}
} }
import type { EventHandlerProps } from './event-handlers.js'
/** 终端事件系统的目标节点DOM 树节点或根节点)。 */
export type EventTarget = { export type EventTarget = {
parentNode: EventTarget | undefined parentNode: EventTarget | undefined // 父节点,根节点为 undefined
_eventHandlers?: Record<string, unknown> _eventHandlers?: Partial<EventHandlerProps> // 事件处理器,与 dom.ts DOMElement 同构
} }

View File

@ -20,7 +20,10 @@ import {
type TextNode, type TextNode,
} from './dom.js' } from './dom.js'
import { Dispatcher } from './events/dispatcher.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 { getFocusManager, getRootNode } from './focus.js'
import { LayoutDisplay } from './layout/node.js' import { LayoutDisplay } from './layout/node.js'
import applyStyles, { type Styles, type TextStyles } from './styles.js' import applyStyles, { type Styles, type TextStyles } from './styles.js'
@ -109,7 +112,11 @@ type HostContext = {
isInsideText: boolean isInsideText: boolean
} }
function setEventHandler(node: DOMElement, key: string, value: unknown): void { function setEventHandler<K extends keyof EventHandlerProps>(
node: DOMElement,
key: K,
value: EventHandlerProps[K],
): void {
if (!node._eventHandlers) { if (!node._eventHandlers) {
node._eventHandlers = {} node._eventHandlers = {}
} }
@ -133,7 +140,11 @@ function applyProp(node: DOMElement, key: string, value: unknown): void {
} }
if (EVENT_HANDLER_PROPS.has(key)) { if (EVENT_HANDLER_PROPS.has(key)) {
setEventHandler(node, key, value) setEventHandler(
node,
key as keyof EventHandlerProps,
value as EventHandlerProps[keyof EventHandlerProps],
)
return return
} }
@ -442,7 +453,11 @@ const reconciler = createReconciler<
} }
if (EVENT_HANDLER_PROPS.has(key)) { if (EVENT_HANDLER_PROPS.has(key)) {
setEventHandler(node, key, value) setEventHandler(
node,
key as keyof EventHandlerProps,
value as EventHandlerProps[keyof EventHandlerProps],
)
continue continue
} }

View File

@ -1901,12 +1901,12 @@ export function getAccountInformation() {
accountInfo.apiKeySource = apiKeySource accountInfo.apiKeySource = apiKeySource
} }
// We don't know the organization if we're relying on an external API key or auth token // 如果我们依赖外部 API 密钥或认证令牌,则不知道组织
if ( if (
authTokenSource === 'claude.ai' || authTokenSource === 'claude.ai' ||
apiKeySource === '/login managed key' apiKeySource === '/login managed key'
) { ) {
// Get organization name from OAuth account info // 从 OAuth 账户信息获取组织名称
const orgName = getOauthAccountInfo()?.organizationName const orgName = getOauthAccountInfo()?.organizationName
if (orgName) { if (orgName) {
accountInfo.organization = orgName accountInfo.organization = orgName

View File

@ -2,6 +2,7 @@ import {
type ClaudeForChromeContext, type ClaudeForChromeContext,
createClaudeForChromeMcpServer, createClaudeForChromeMcpServer,
type Logger, type Logger,
type LoggerDetail,
type PermissionMode, type PermissionMode,
} from '@ant/claude-for-chrome-mcp' } from '@ant/claude-for-chrome-mcp'
import { initializeAnalyticsSink } from '../../services/analytics/sink.js' import { initializeAnalyticsSink } from '../../services/analytics/sink.js'
@ -276,19 +277,19 @@ export async function runClaudeInChromeMcpServer(): Promise<void> {
} }
class DebugLogger implements Logger { class DebugLogger implements Logger {
silly(message: string, ...args: unknown[]): void { silly(message: string, detail?: LoggerDetail): void {
logForDebugging(format(message, ...args), { level: 'debug' }) logForDebugging(format(message, detail ?? ''), { level: 'debug' })
} }
debug(message: string, ...args: unknown[]): void { debug(message: string, detail?: LoggerDetail): void {
logForDebugging(format(message, ...args), { level: 'debug' }) logForDebugging(format(message, detail ?? ''), { level: 'debug' })
} }
info(message: string, ...args: unknown[]): void { info(message: string, detail?: LoggerDetail): void {
logForDebugging(format(message, ...args), { level: 'info' }) logForDebugging(format(message, detail ?? ''), { level: 'info' })
} }
warn(message: string, ...args: unknown[]): void { warn(message: string, detail?: LoggerDetail): void {
logForDebugging(format(message, ...args), { level: 'warn' }) logForDebugging(format(message, detail ?? ''), { level: 'warn' })
} }
error(message: string, ...args: unknown[]): void { error(message: string, detail?: LoggerDetail): void {
logForDebugging(format(message, ...args), { level: 'error' }) logForDebugging(format(message, detail ?? ''), { level: 'error' })
} }
} }

View File

@ -1,6 +1,7 @@
import type { import type {
ComputerUseHostAdapter, ComputerUseHostAdapter,
Logger, Logger,
LoggerDetail,
} from '@ant/computer-use-mcp/types' } from '@ant/computer-use-mcp/types'
import { format } from 'util' import { format } from 'util'
import { logForDebugging } from '../debug.js' import { logForDebugging } from '../debug.js'
@ -10,20 +11,20 @@ import { getChicagoEnabled, getChicagoSubGates } from './gates.js'
import { requireComputerUseSwift } from './swiftLoader.js' import { requireComputerUseSwift } from './swiftLoader.js'
class DebugLogger implements Logger { class DebugLogger implements Logger {
silly(message: string, ...args: unknown[]): void { silly(message: string, detail?: LoggerDetail): void {
logForDebugging(format(message, ...args), { level: 'debug' }) logForDebugging(format(message, detail ?? ''), { level: 'debug' })
} }
debug(message: string, ...args: unknown[]): void { debug(message: string, detail?: LoggerDetail): void {
logForDebugging(format(message, ...args), { level: 'debug' }) logForDebugging(format(message, detail ?? ''), { level: 'debug' })
} }
info(message: string, ...args: unknown[]): void { info(message: string, detail?: LoggerDetail): void {
logForDebugging(format(message, ...args), { level: 'info' }) logForDebugging(format(message, detail ?? ''), { level: 'info' })
} }
warn(message: string, ...args: unknown[]): void { warn(message: string, detail?: LoggerDetail): void {
logForDebugging(format(message, ...args), { level: 'warn' }) logForDebugging(format(message, detail ?? ''), { level: 'warn' })
} }
error(message: string, ...args: unknown[]): void { error(message: string, detail?: LoggerDetail): void {
logForDebugging(format(message, ...args), { level: 'error' }) logForDebugging(format(message, detail ?? ''), { level: 'error' })
} }
} }