fix: Computer Use 点击功能修复

- ensureRuntimeFiles() 改为始终同步源文件而非仅缺失时复制
- pre-authorized apps 增加 tier: 'full',与现有 allowedApps 合并而非替换
- normalizeOsPermissions() 将 screenRecording: null 视为非阻塞
- config 路径改为 cc-haha/computer-use-config.json
- mac_helper.py 改进鼠标点击与坐标处理
This commit is contained in:
程序员阿江(Relakkes)
2026-04-15 14:18:37 +08:00
parent b002447436
commit 166fe5b676
9 changed files with 1173 additions and 17 deletions
+67
View File
@@ -27,6 +27,9 @@ import { registerEscHotkey } from './escHotkey.js';
import { getChicagoCoordinateMode } from './gates.js';
import { getComputerUseHostAdapter } from './hostAdapter.js';
import { getComputerUseMCPRenderingOverrides } from './toolRendering.js';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { homedir } from 'node:os';
type CallOverride = Pick<Tool, 'call'>['call'];
type Binding = {
ctx: ComputerUseSessionContext;
@@ -227,6 +230,64 @@ export function buildSessionContext(): ComputerUseSessionContext {
formatLockHeldMessage: formatLockHeld
};
}
/**
* Load pre-authorized apps from ~/.claude/cc-haha/computer-use-config.json.
* Called once when the binding is first created. Pre-authorized apps
* are injected into appState so `getAllowedApps()` returns them
* immediately — no runtime permission dialog needed.
*/
async function loadPreAuthorizedApps(): Promise<void> {
try {
const configPath = join(
process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), '.claude'),
'cc-haha',
'computer-use-config.json',
)
const raw = await readFile(configPath, 'utf8')
const config = JSON.parse(raw) as {
authorizedApps?: { bundleId: string; displayName: string }[]
grantFlags?: { clipboardRead?: boolean; clipboardWrite?: boolean; systemKeyCombos?: boolean }
}
if (!config.authorizedApps?.length) return
const apps = config.authorizedApps.map(a => ({
bundleId: a.bundleId,
displayName: a.displayName,
grantedAt: Date.now(),
tier: 'full' as const,
}))
const flags = {
...DEFAULT_GRANT_FLAGS,
...(config.grantFlags ?? {}),
}
// Inject into appState so getAllowedApps() returns them.
// Merge with existing allowedApps (from permission dialog) instead of replacing.
if (currentToolUseContext) {
currentToolUseContext.setAppState(prev => {
const existing = prev.computerUseMcpState?.allowedApps ?? []
const existingIds = new Set(existing.map(a => a.bundleId))
const merged = [...existing, ...apps.filter(a => !existingIds.has(a.bundleId))]
return {
...prev,
computerUseMcpState: {
...prev.computerUseMcpState,
allowedApps: merged,
grantFlags: flags,
},
}
})
}
logForDebugging(`[Computer Use] Loaded ${apps.length} pre-authorized apps from config`)
} catch {
// Config doesn't exist or is invalid — no pre-authorized apps
}
}
let preAuthLoaded = false
function getOrBind(): Binding {
if (binding) return binding;
const ctx = buildSessionContext();
@@ -251,6 +312,12 @@ export function getComputerUseMCPToolOverrides(toolName: string): ComputerUseMCP
const {
dispatch
} = getOrBind();
// Load pre-authorized apps on first tool call
if (!preAuthLoaded) {
preAuthLoaded = true;
await loadPreAuthorizedApps();
}
const {
telemetry,
...result