Merge branch 'main' into refactor/ink-v2

This commit is contained in:
claude-code-best
2026-04-07 22:41:35 +08:00
5 changed files with 90 additions and 32 deletions

View File

@@ -26,7 +26,7 @@ export function registerEscHotkey(onEscape: () => void): boolean {
if (process.platform !== 'darwin') return false
if (registered) return true
const cu = requireComputerUseSwift()
if (!(cu as any).hotkey.registerEscape(onEscape)) {
if (!(cu as any).hotkey?.registerEscape(onEscape)) {
// CGEvent.tapCreate failed — typically missing Accessibility permission.
// CU still works, just without ESC abort. Mirrors Cowork's escAbort.ts:81.
logForDebugging('[cu-esc] registerEscape returned false', { level: 'warn' })
@@ -41,7 +41,7 @@ export function registerEscHotkey(onEscape: () => void): boolean {
export function unregisterEscHotkey(): void {
if (!registered) return
try {
(requireComputerUseSwift() as any).hotkey.unregister()
(requireComputerUseSwift() as any).hotkey?.unregister()
} finally {
releasePump()
registered = false
@@ -51,5 +51,5 @@ export function unregisterEscHotkey(): void {
export function notifyExpectedEscape(): void {
if (!registered) return
(requireComputerUseSwift() as any).hotkey.notifyExpectedEscape()
(requireComputerUseSwift() as any).hotkey?.notifyExpectedEscape()
}

View File

@@ -27,6 +27,38 @@ class DebugLogger implements Logger {
}
}
// ---------------------------------------------------------------------------
// JXA-based TCC permission probes (fallback when native .node module absent)
// ---------------------------------------------------------------------------
/** Probe accessibility by asking System Events for a process list. */
function checkAccessibilityJXA(): boolean {
try {
const result = Bun.spawnSync({
cmd: ['osascript', '-e', 'tell application "System Events" to get name of every process whose background only is false'],
stdout: 'pipe',
stderr: 'pipe',
})
return result.exitCode === 0
} catch {
return false
}
}
/** Probe screen recording by attempting a 1x1 screencapture. */
function checkScreenRecordingJXA(): boolean {
try {
const result = Bun.spawnSync({
cmd: ['screencapture', '-x', '-R', '0,0,1,1', '/dev/null'],
stdout: 'pipe',
stderr: 'pipe',
})
return result.exitCode === 0
} catch {
return false
}
}
let cached: ComputerUseHostAdapter | undefined
/**
@@ -47,8 +79,19 @@ export function getComputerUseHostAdapter(): ComputerUseHostAdapter {
ensureOsPermissions: async () => {
if (process.platform !== 'darwin') return { granted: true }
const cu = requireComputerUseSwift()
const accessibility = (cu as any).tcc.checkAccessibility()
const screenRecording = (cu as any).tcc.checkScreenRecording()
const tcc = (cu as any).tcc
// Native Swift .node module provides tcc.checkAccessibility/checkScreenRecording.
// When absent (decompiled/reverse-engineered build), fall back to JXA probes.
if (tcc) {
const accessibility = tcc.checkAccessibility()
const screenRecording = tcc.checkScreenRecording()
return accessibility && screenRecording
? { granted: true }
: { granted: false, accessibility, screenRecording }
}
// JXA fallback: try to query System Events (accessibility) and screencapture (screen recording).
const accessibility = checkAccessibilityJXA()
const screenRecording = checkScreenRecordingJXA()
return accessibility && screenRecording
? { granted: true }
: { granted: false, accessibility, screenRecording }