mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-20 07:15:51 +00:00
通过 4 阶段 workflow(分析 → 计划 → 重构 → 验证)将 3 个超大的 ACP 源文件拆分为 28 个模块化子文件,每个均严格小于 500 行,且完整保留 所有公共 API(barrel 模式重导出)。 变更概要: - packages/acp-link/src/server.ts: 1800 → 20 行(barrel),新增 11 个子模块 (server/types、payload-decode、permission-mode、runtime-state、dispatch、 handlers-agent、handlers-session、acp-client、client-send、start-server、 testing-internals) - src/services/acp/agent.ts: 1297 → 33 行(barrel),新增 9 个子模块 (agent/AcpAgent、sessionTypes、permissionMode、configOptions、promptQueue、 internalAccessors、createSessionMethod、sessionLifecycle、promptFlow) - src/services/acp/bridge.ts: 1516 → 29 行(barrel),新增 8 个子模块 (bridge/types、paths、contentBlocks、toolInfo、toolResults、modelUsage、 notifications、forwarding) 验证: - bun run precheck 全通过(typecheck + lint + 5851 tests) - ACP service tests: 176 pass / 0 fail - ACP link tests: 47 pass / 0 fail - 所有外部消费者(entry.ts、permissions.ts、__tests__/)的 import 路径不变 - 测试文件零修改 迁移计划详见 docs/acp-refactor-plan.md。 Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>
103 lines
2.8 KiB
TypeScript
103 lines
2.8 KiB
TypeScript
import type { WSContext } from 'hono/ws'
|
|
import * as acp from '@agentclientprotocol/sdk'
|
|
import { send } from './client-send.js'
|
|
import {
|
|
PERMISSION_TIMEOUT_MS,
|
|
generateRequestId,
|
|
logPerm,
|
|
logWs,
|
|
} from './runtime-state.js'
|
|
import { clients } from './runtime-state.js'
|
|
import type { ClientState } from './types.js'
|
|
|
|
// Create a Client implementation that forwards events to WebSocket
|
|
export function createClient(
|
|
ws: WSContext,
|
|
clientState: ClientState,
|
|
): acp.Client {
|
|
return {
|
|
async requestPermission(params) {
|
|
const requestId = generateRequestId()
|
|
logPerm.debug({ requestId, title: params.toolCall.title }, 'requested')
|
|
|
|
const outcomePromise = new Promise<
|
|
{ outcome: 'cancelled' } | { outcome: 'selected'; optionId: string }
|
|
>(resolve => {
|
|
const timeout = setTimeout(() => {
|
|
logPerm.warn({ requestId }, 'timed out')
|
|
clientState.pendingPermissions.delete(requestId)
|
|
resolve({ outcome: 'cancelled' })
|
|
}, PERMISSION_TIMEOUT_MS)
|
|
|
|
clientState.pendingPermissions.set(requestId, { resolve, timeout })
|
|
})
|
|
|
|
send(ws, 'permission_request', {
|
|
requestId,
|
|
sessionId: params.sessionId,
|
|
options: params.options,
|
|
toolCall: params.toolCall,
|
|
})
|
|
|
|
const outcome = await outcomePromise
|
|
logPerm.debug({ requestId, outcome: outcome.outcome }, 'resolved')
|
|
|
|
return { outcome }
|
|
},
|
|
|
|
async sessionUpdate(params) {
|
|
send(ws, 'session_update', params)
|
|
},
|
|
|
|
async readTextFile(params) {
|
|
logWs.debug({ path: params.path }, 'readTextFile')
|
|
return { content: '' }
|
|
},
|
|
|
|
async writeTextFile(params) {
|
|
logWs.debug({ path: params.path }, 'writeTextFile')
|
|
return {}
|
|
},
|
|
}
|
|
}
|
|
|
|
// Handle permission response from client
|
|
export function handlePermissionResponse(
|
|
ws: WSContext,
|
|
payload: {
|
|
requestId: string
|
|
outcome:
|
|
| { outcome: 'cancelled' }
|
|
| { outcome: 'selected'; optionId: string }
|
|
},
|
|
): void {
|
|
const state = clients.get(ws)
|
|
if (!state) {
|
|
logPerm.warn('response from unknown client')
|
|
return
|
|
}
|
|
|
|
const pending = state.pendingPermissions.get(payload.requestId)
|
|
if (!pending) {
|
|
logPerm.warn(
|
|
{ requestId: payload.requestId },
|
|
'response for unknown request',
|
|
)
|
|
return
|
|
}
|
|
|
|
clearTimeout(pending.timeout)
|
|
state.pendingPermissions.delete(payload.requestId)
|
|
pending.resolve(payload.outcome)
|
|
}
|
|
|
|
// Cancel all pending permissions for a client (called on disconnect)
|
|
export function cancelPendingPermissions(clientState: ClientState): void {
|
|
for (const [requestId, pending] of clientState.pendingPermissions) {
|
|
logPerm.debug({ requestId }, 'cancelled on disconnect')
|
|
clearTimeout(pending.timeout)
|
|
pending.resolve({ outcome: 'cancelled' })
|
|
}
|
|
clientState.pendingPermissions.clear()
|
|
}
|