mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 02:27:43 +00:00
* fix(computer-use): replace the macOS permission status gate with one 5s launch floor (#34390) Follows up #36028. The earlier gate grew background/explicit flags, epochs, backoff and a setup pgrep path that reviewers could not make correct. - Status: one probe at a time and one 5s launch floor shared by every reader, so every path stays at <=12 helper launches/min. Readers inside the floor join the running probe or ride one trailing probe that fires only if someone waits. Only grants are cached (until the floor expires); denials and errors are never served, so a grant shows within one floor locally and on web. No app epochs: without a denial cache they bought nothing. Off macOS the gate is skipped. - Setup: one launch per 5s across every permission, claimed synchronously; a request inside the window is queued (latest wins) and reported as launchQueued. Kill-and-relaunch stays so a click always re-shows the helper. Setup reuses the gate's in-flight/cached/last result instead of probing. - Reset checks helper availability from the bundle and invalidates after any tccutil attempt, including partial failures. - Remove the background flag from preload/IPC/renderer/web, the app-activity binding, and the sidecar permission_denied invalidation. * fix(computer-use): close the remaining gaps in the permission launch floor (#34390) - Clear the trailing timer whenever any probe launches and re-check the floor when it fires, so a late timer can never launch inside the floor. - Setup reuses a denial only until the floor expires. - A reader that joins a probe older than 300ms and gets a denial rides the next probe, so a grant shows within about one floor even after a stale web/RPC probe. - Setup requests are numbered on arrival, including ones that coalesce, so the latest request wins (A, B, A launches A); a queued launch already covered by a newer launch is dropped. Reset and will-quit cancel any queued launch. - The settings pane says "Opening…" and the CLI says queued for queued launches. * fix(computer-use): measure permission launch floors from real spawns and cancel setup on every host (#34390) - Both 5s floors use performance.now, so a wall-clock step cannot stretch or skip them. - The status floor counts from the later of claim and the actual `open` spawn, and the setup window from after its spawn, so a slow mkdtemp or pkill can't allow a 13th spawn in a 60s window. - Setup cancellation registers through AppEnvironment quit handlers (desktop, headless Electron serve, orcad) and orcad's shutdown trigger; reset and shutdown bump a generation so an Open still waiting on status never launches. - Reset cancels queued setup before its first await. - A setup request leaves the coalescing map when it launches, so a later request for the same permission queues and wins (latest-wins across microtasks). - A failed probe clears the remembered denial. * fix(computer-use): drop setup coalescing and gate setup on a host-wide shutdown flag (#34390) - Remove the per-permission setupInFlight map. Every Open goes through the 5s window and seq-ordered queue; a queued request with the same args that arrived before the last spawn was already shown by it, so concurrent same-permission Opens still spawn once. This fixes stale entries after reset turning the next Open into a no-op or a cancelled one. - A cancelled Open (reset or shutdown) rejects with an error instead of returning launchedHelper: false, which every client rendered as "setup complete". - Replace the per-host cancel calls with src/main/host-shutdown-state.ts, set synchronously by Electron will-quit and orcad's shutdown admission; setup checks it before every spawn, including the queued timer. - Track the status cache's expiry from the probe that produced it, so starting a new probe cannot revive an expired grant. - The queued setup timer re-checks its deadline and re-arms when early, and both timers round delays up; a throwing spawn there is logged, not uncaught. * fix(computer-use): keep setup latest-wins after slow probes and surface cancelled setups (#34390) - Record the launching request's seq; an older request that resumes or is queued after a newer one spawned is dropped, so latest-wins holds when requests resume out of order behind a probe. - orcad sets the host shutdown flag on every cleanup entry, including failed startup. - The web facade only falls back for hosts without computer.permissions; other failures, such as a cancelled Open, reject so the pane and onboarding show them. - The cancellation check runs before an untargeted Open can report everything granted. - Opens that arrive mid-reset wait for it, so reset's helper cleanup cannot kill what they open. - Queued wording now says a newer request or a reset may replace it. * fix(computer-use): wait out a reset before an Open reads permission status (#34390)
160 lines
4.2 KiB
TypeScript
160 lines
4.2 KiB
TypeScript
import {
|
|
callComputerSidecarAction,
|
|
callComputerSidecarCapabilities,
|
|
callComputerSidecarListApps,
|
|
callComputerSidecarListWindows,
|
|
callComputerSidecarSnapshot,
|
|
resetComputerSidecarForTest
|
|
} from '../../../computer/sidecar-client'
|
|
import { defineMethod } from '../core'
|
|
import {
|
|
Click,
|
|
ComputerObserveTarget,
|
|
ComputerPermissions,
|
|
Drag,
|
|
Hotkey,
|
|
ListApps,
|
|
ListWindows,
|
|
PasteText,
|
|
PerformSecondaryAction,
|
|
PressKey,
|
|
Scroll,
|
|
SetValue,
|
|
TypeText
|
|
} from '../../../../shared/rpc-contract/computer-schemas-params'
|
|
import {
|
|
ComputerCapabilitiesParams,
|
|
ComputerPermissionsStatusParams
|
|
} from '../../../../shared/rpc-contract/computer-params'
|
|
|
|
export function resetComputerSessionsForTest(): void {
|
|
resetComputerSidecarForTest()
|
|
}
|
|
|
|
export const COMPUTER_METHODS = [
|
|
defineMethod({
|
|
name: 'computer.capabilities',
|
|
permission: 'workspace',
|
|
params: ComputerCapabilitiesParams,
|
|
handler: async () => {
|
|
return await callComputerSidecarCapabilities()
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.listApps',
|
|
permission: 'desktop-control',
|
|
params: ListApps,
|
|
handler: async () => {
|
|
return await callComputerSidecarListApps()
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.permissions',
|
|
permission: 'desktop-control',
|
|
params: ComputerPermissions,
|
|
handler: async (params) => {
|
|
const { openComputerUsePermissions } =
|
|
await import('../../../computer/macos-computer-use-permissions')
|
|
return openComputerUsePermissions(params.id)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.permissionsStatus',
|
|
permission: 'workspace',
|
|
params: ComputerPermissionsStatusParams,
|
|
handler: async () => {
|
|
const { computerUsePermissionStatusGate } =
|
|
await import('../../../computer/macos-computer-use-permission-status-gate')
|
|
return computerUsePermissionStatusGate.read()
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.listWindows',
|
|
permission: 'desktop-control',
|
|
params: ListWindows,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarListWindows(params)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.getAppState',
|
|
permission: 'desktop-control',
|
|
params: ComputerObserveTarget,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarSnapshot(params)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.click',
|
|
permission: 'desktop-control',
|
|
params: Click,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarAction('click', params)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.performSecondaryAction',
|
|
permission: 'desktop-control',
|
|
params: PerformSecondaryAction,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarAction('performSecondaryAction', params)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.scroll',
|
|
permission: 'desktop-control',
|
|
params: Scroll,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarAction('scroll', params)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.drag',
|
|
permission: 'desktop-control',
|
|
params: Drag,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarAction('drag', params)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.typeText',
|
|
permission: 'desktop-control',
|
|
params: TypeText,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarAction('typeText', params)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.pressKey',
|
|
permission: 'desktop-control',
|
|
params: PressKey,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarAction('pressKey', params)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.hotkey',
|
|
permission: 'desktop-control',
|
|
params: Hotkey,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarAction('hotkey', params)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.pasteText',
|
|
permission: 'desktop-control',
|
|
params: PasteText,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarAction('pasteText', params)
|
|
}
|
|
}),
|
|
defineMethod({
|
|
name: 'computer.setValue',
|
|
permission: 'desktop-control',
|
|
params: SetValue,
|
|
handler: async (params) => {
|
|
return await callComputerSidecarAction('setValue', params)
|
|
}
|
|
})
|
|
]
|