Download src/utils/computerUse/cleanup.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 3.3 kB
-
https://huggingface.co/chenbhao/codev/resolve/714ab7d15ffd78992a4c3d4e2bf153c2f62f4841/src/utils/computerUse/cleanup.ts
- Command line
-
hf download hf://chenbhao/codev@714ab7d15ffd78992a4c3d4e2bf153c2f62f4841/src/utils/computerUse/cleanup.ts
-
curl -L -o cleanup.ts https://huggingface.co/chenbhao/codev/resolve/714ab7d15ffd78992a4c3d4e2bf153c2f62f4841/src/utils/computerUse/cleanup.ts
3.3 kB
| import type { ToolUseContext } from '../../Tool.js' | |
| import { logForDebugging } from '../debug.js' | |
| import { errorMessage } from '../errors.js' | |
| import { withResolvers } from '../withResolvers.js' | |
| import { isLockHeldLocally, releaseComputerUseLock } from './computerUseLock.js' | |
| import { unregisterEscHotkey } from './escHotkey.js' | |
| // cu.apps.unhide is NOT one of the four @MainActor methods wrapped by | |
| // drainRunLoop's 30s backstop. On abort paths (where the user hit Ctrl+C | |
| // because something was slow) a hang here would wedge the abort. Generous | |
| // timeout — unhide should be ~instant; if it takes 5s something is wrong | |
| // and proceeding is better than waiting. The Swift call continues in the | |
| // background regardless; we just stop blocking on it. | |
| const UNHIDE_TIMEOUT_MS = 5000 | |
| /** | |
| * Turn-end cleanup for the chicago MCP surface: auto-unhide apps that | |
| * `prepareForAction` hid, then release the file-based lock. | |
| * | |
| * Called from three sites: natural turn end (`stopHooks.ts`), abort during | |
| * streaming (`query.ts` aborted_streaming), abort during tool execution | |
| * (`query.ts` aborted_tools). All three reach this via dynamic import gated | |
| * on `feature('CHICAGO_MCP')`. `executor.js` (which pulls both native | |
| * modules) is dynamic-imported below so non-CU turns don't load native | |
| * modules just to no-op. | |
| * | |
| * No-ops cheaply on non-CU turns: both gate checks are zero-syscall. | |
| */ | |
| export async function cleanupComputerUseAfterTurn( | |
| ctx: Pick< | |
| ToolUseContext, | |
| 'getAppState' | 'setAppState' | 'sendOSNotification' | |
| >, | |
| ): Promise<void> { | |
| const appState = ctx.getAppState() | |
| const hidden = appState.computerUseMcpState?.hiddenDuringTurn | |
| if (hidden && hidden.size > 0) { | |
| const { unhideComputerUseApps } = await import('./executor.js') | |
| const unhide = unhideComputerUseApps([...hidden]).catch(err => | |
| logForDebugging( | |
| `[Computer Use MCP] auto-unhide failed: ${errorMessage(err)}`, | |
| ), | |
| ) | |
| const timeout = withResolvers<void>() | |
| const timer = setTimeout(timeout.resolve, UNHIDE_TIMEOUT_MS) | |
| await Promise.race([unhide, timeout.promise]).finally(() => | |
| clearTimeout(timer), | |
| ) | |
| ctx.setAppState(prev => | |
| prev.computerUseMcpState?.hiddenDuringTurn === undefined | |
| ? prev | |
| : { | |
| ...prev, | |
| computerUseMcpState: { | |
| ...prev.computerUseMcpState, | |
| hiddenDuringTurn: undefined, | |
| }, | |
| }, | |
| ) | |
| } | |
| // Zero-syscall pre-check so non-CU turns don't touch disk. Release is still | |
| // idempotent (returns false if already released or owned by another session). | |
| if (!isLockHeldLocally()) return | |
| // Unregister before lock release so the pump-retain drops as soon as the | |
| // CU session ends. Idempotent — no-ops if registration failed at acquire. | |
| // Swallow throws so a NAPI unregister error never prevents lock release — | |
| // a held lock blocks the next CU session with "in use by another session". | |
| try { | |
| unregisterEscHotkey() | |
| } catch (err) { | |
| logForDebugging( | |
| `[Computer Use MCP] unregisterEscHotkey failed: ${errorMessage(err)}`, | |
| ) | |
| } | |
| if (await releaseComputerUseLock()) { | |
| ctx.sendOSNotification?.({ | |
| message: 'Claude is done using your computer', | |
| notificationType: 'computer_use_exit', | |
| }) | |
| } | |
| } | |