mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 08:02:21 +00:00
* perf(windows): stop the capability poll respawning blocking wsl.exe probes #11295 added a 30s renderer interval to `useWindowsTerminalCapabilities` whose early-return only fires when WSL is available with at least one distro, so on the common Windows host (no WSL) it re-ran a full capability read forever. Each read IPCs four probes whose main-process handlers were synchronous `execFileSync` calls to wsl.exe/pwsh.exe, blocking the Electron main event loop for up to 5s a time. The un-latching intent is kept: a host that answers "no WSL" is still re-checked, now on an exponential backoff (30s, +60s, +120s) that parks once the answer stops moving, re-arms on window focus, is shared by all consumers of an owner key, and stops entirely when the last consumer unmounts. The wsl/pwsh IPC handlers now use async twins that share the existing caches and back off identically. * fix(windows): classify async wsl/pwsh probe failures with the execFile error shape The async twins feed `execFile` callback errors into classifiers written for `execFileSync`: a non-zero exit lands on `error.code` as a number rather than `error.status`, and a timeout is a SIGTERM kill rather than ETIMEDOUT. So a Windows host without WSL (wsl.exe ships in System32, so it exits non-zero instead of ENOENT) was cached as retryable, shrinking the shared window from 10min to 45s and making the still-sync callers re-pay their blocking spawn ~13x more often; and a pwsh cold start past 5s cached "pwsh missing" for 30s, demoting the user's PowerShell 7 preference — the exact case the ETIMEDOUT branch exists to prevent. Also drops a literal NUL byte from the new re-probe module's signature separator, which made the file binary to git, and seeds `lastProbeAt` at registration so focus churn right after mount cannot defer the first re-probe indefinitely. Co-authored-by: Orca <help@stably.ai> * perf(windows): route relay host-capability probes through the async wsl/pwsh twins A paired web/mobile client resolves `useWindowsTerminalCapabilities` to a local target (TabBar's `isWebClient` gate, and `useSettingsNavigationMetadata` forces `{kind:'local'}`), so the new re-probe arms there too. But `window.api.wsl/pwsh` on a web client is not the ipc/app.ts channel — it is `host.wsl.*`/`host.pwsh.*` over the runtime RPC, which still ran the sync probes and blocked the desktop main event loop on `execFileSync('wsl.exe' | 'pwsh.exe')` for up to 5s per call. Switch those handlers and the relay preflight capability probe to the async twins added here; they share the same caches, dedupe and backoff, so remote callers see no behavior change. * fix(windows): harden async capability reprobes * fix(windows): dedupe PowerShell shell probes --------- Co-authored-by: Orca <help@stably.ai> Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
99 lines
3.5 KiB
TypeScript
99 lines
3.5 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { RpcDispatcher } from '../dispatcher'
|
|
import type { RpcRequest } from '../core'
|
|
import type { OrcaRuntimeService } from '../../orca-runtime'
|
|
|
|
const {
|
|
isPwshAvailable,
|
|
isPwshAvailableAsync,
|
|
isWslAvailable,
|
|
isWslAvailableAsync,
|
|
listWslDistros,
|
|
listWslDistrosAsync,
|
|
isGitBashAvailable
|
|
} = vi.hoisted(() => ({
|
|
isPwshAvailable: vi.fn(),
|
|
isPwshAvailableAsync: vi.fn(),
|
|
isWslAvailable: vi.fn(),
|
|
isWslAvailableAsync: vi.fn(),
|
|
listWslDistros: vi.fn(),
|
|
listWslDistrosAsync: vi.fn(),
|
|
isGitBashAvailable: vi.fn()
|
|
}))
|
|
|
|
vi.mock('../../../pwsh', () => ({ isPwshAvailable, isPwshAvailableAsync }))
|
|
vi.mock('../../../wsl', () => ({
|
|
isWslAvailable,
|
|
isWslAvailableAsync,
|
|
listWslDistros,
|
|
listWslDistrosAsync
|
|
}))
|
|
vi.mock('../../../git-bash', () => ({ isGitBashAvailable }))
|
|
|
|
import { HOST_CAPABILITY_METHODS } from './host-capabilities'
|
|
|
|
function makeRequest(method: string, params?: unknown): RpcRequest {
|
|
return { id: 'req-1', authToken: 'tok', method, params }
|
|
}
|
|
|
|
describe('host capability RPC methods', () => {
|
|
beforeEach(() => {
|
|
isPwshAvailable.mockReset()
|
|
isPwshAvailableAsync.mockReset()
|
|
isWslAvailable.mockReset()
|
|
isWslAvailableAsync.mockReset()
|
|
listWslDistros.mockReset()
|
|
listWslDistrosAsync.mockReset()
|
|
isGitBashAvailable.mockReset()
|
|
})
|
|
|
|
it('reports Windows shell capability probes through explicit methods', async () => {
|
|
isPwshAvailableAsync.mockResolvedValue(true)
|
|
isWslAvailableAsync.mockResolvedValue(true)
|
|
listWslDistrosAsync.mockResolvedValue(['Ubuntu'])
|
|
isGitBashAvailable.mockReturnValue(true)
|
|
const runtime = { getRuntimeId: () => 'test-runtime' } as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: HOST_CAPABILITY_METHODS })
|
|
|
|
await expect(dispatcher.dispatch(makeRequest('host.pwsh.isAvailable'))).resolves.toMatchObject({
|
|
ok: true,
|
|
result: true
|
|
})
|
|
await expect(dispatcher.dispatch(makeRequest('host.wsl.isAvailable'))).resolves.toMatchObject({
|
|
ok: true,
|
|
result: true
|
|
})
|
|
await expect(dispatcher.dispatch(makeRequest('host.wsl.listDistros'))).resolves.toMatchObject({
|
|
ok: true,
|
|
result: ['Ubuntu']
|
|
})
|
|
await expect(
|
|
dispatcher.dispatch(makeRequest('host.gitBash.isAvailable'))
|
|
).resolves.toMatchObject({
|
|
ok: true,
|
|
result: true
|
|
})
|
|
})
|
|
|
|
// Why: web/mobile clients reach these over the relay, so a sync probe would stall the
|
|
// desktop main event loop on execFileSync wsl.exe/pwsh.exe for up to 5s per call.
|
|
it('answers the Windows shell capability methods without a blocking spawn', async () => {
|
|
isPwshAvailableAsync.mockResolvedValue(false)
|
|
isWslAvailableAsync.mockResolvedValue(false)
|
|
listWslDistrosAsync.mockResolvedValue([])
|
|
const runtime = { getRuntimeId: () => 'test-runtime' } as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: HOST_CAPABILITY_METHODS })
|
|
|
|
await dispatcher.dispatch(makeRequest('host.wsl.isAvailable'))
|
|
await dispatcher.dispatch(makeRequest('host.wsl.listDistros'))
|
|
await dispatcher.dispatch(makeRequest('host.pwsh.isAvailable'))
|
|
|
|
expect(isWslAvailableAsync).toHaveBeenCalledTimes(1)
|
|
expect(listWslDistrosAsync).toHaveBeenCalledTimes(1)
|
|
expect(isPwshAvailableAsync).toHaveBeenCalledTimes(1)
|
|
expect(isWslAvailable).not.toHaveBeenCalled()
|
|
expect(listWslDistros).not.toHaveBeenCalled()
|
|
expect(isPwshAvailable).not.toHaveBeenCalled()
|
|
})
|
|
})
|