From 2acc7ca3879a46a363da33911d1db39f47c1f6b4 Mon Sep 17 00:00:00 2001 From: Sarfraz Alam Date: Sat, 8 Aug 2026 11:14:25 +0530 Subject: [PATCH] feat: cloakbrowser --- src/doctor.test.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/doctor.ts | 9 ++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/doctor.test.ts b/src/doctor.test.ts index 7e2b9479..0072e1a4 100644 --- a/src/doctor.test.ts +++ b/src/doctor.test.ts @@ -6,12 +6,21 @@ const { mockClose, mockFindShadowedUserAdapters, mockSetDaemonCommandTimeoutSeconds, + mockBinaryInfo, + mockEnsureBinary, } = vi.hoisted(() => ({ mockGetDaemonHealth: vi.fn(), mockConnect: vi.fn(), mockClose: vi.fn(), mockFindShadowedUserAdapters: vi.fn(), mockSetDaemonCommandTimeoutSeconds: vi.fn(), + mockBinaryInfo: vi.fn(), + mockEnsureBinary: vi.fn(), +})); + +vi.mock('cloakbrowser', () => ({ + binaryInfo: mockBinaryInfo, + ensureBinary: mockEnsureBinary, })); vi.mock('./browser/daemon-transport.js', () => ({ @@ -46,6 +55,8 @@ describe('doctor report rendering', () => { vi.clearAllMocks(); mockFindShadowedUserAdapters.mockReturnValue([]); mockSetDaemonCommandTimeoutSeconds.mockClear(); + mockBinaryInfo.mockReturnValue({ installed: true }); + mockEnsureBinary.mockResolvedValue('/cache/cloak/Chromium'); // Doctor always runs live connectivity. Tests that want connect to fail override. mockConnect.mockResolvedValue({ evaluate: vi.fn().mockResolvedValue(2), @@ -290,6 +301,39 @@ describe('doctor report rendering', () => { expect(mockSetDaemonCommandTimeoutSeconds).toHaveBeenLastCalledWith(null); }); + it('installs the CloakBrowser binary before starting the timed live probe', async () => { + mockBinaryInfo.mockReturnValueOnce({ installed: false }); + let finishInstall!: () => void; + mockEnsureBinary.mockReturnValueOnce(new Promise((resolve) => { + finishInstall = () => resolve('/cache/cloak/Chromium'); + })); + + const connectivity = checkConnectivity(); + await vi.waitFor(() => expect(mockEnsureBinary).toHaveBeenCalledTimes(1)); + + expect(mockConnect).not.toHaveBeenCalled(); + expect(mockSetDaemonCommandTimeoutSeconds).not.toHaveBeenCalled(); + + finishInstall(); + await expect(connectivity).resolves.toMatchObject({ ok: true }); + expect(mockConnect).toHaveBeenCalledTimes(1); + expect(mockSetDaemonCommandTimeoutSeconds).toHaveBeenNthCalledWith(1, 8); + expect(mockSetDaemonCommandTimeoutSeconds).toHaveBeenLastCalledWith(null); + }); + + it('reports binary installation failures without starting the daemon probe', async () => { + mockBinaryInfo.mockReturnValueOnce({ installed: false }); + mockEnsureBinary.mockRejectedValueOnce(new Error('binary download failed')); + + await expect(checkConnectivity()).resolves.toMatchObject({ + ok: false, + error: 'binary download failed', + }); + expect(mockConnect).not.toHaveBeenCalled(); + expect(mockSetDaemonCommandTimeoutSeconds).toHaveBeenCalledTimes(1); + expect(mockSetDaemonCommandTimeoutSeconds).toHaveBeenCalledWith(null); + }); + it('does not report an issue when the connected Cloak runtime does not report a version', async () => { const status = { state: 'ready' as const, diff --git a/src/doctor.ts b/src/doctor.ts index 5a2af86a..f23c4cc7 100644 --- a/src/doctor.ts +++ b/src/doctor.ts @@ -5,6 +5,7 @@ */ import { DEFAULT_DAEMON_PORT } from './constants.js'; +import { binaryInfo, ensureBinary } from 'cloakbrowser'; import { BrowserBridge } from './browser/index.js'; import { setDaemonCommandTimeoutSeconds } from './browser/daemon-client.js'; import { getDaemonHealth } from './browser/daemon-transport.js'; @@ -52,8 +53,14 @@ export type DoctorReport = { export async function checkConnectivity(opts?: { timeout?: number }): Promise { const start = Date.now(); const timeoutSeconds = opts?.timeout ?? DOCTOR_LIVE_TIMEOUT_SECONDS; - setDaemonCommandTimeoutSeconds(timeoutSeconds); try { + // CloakBrowser installs Chromium lazily. Keep that potentially long first-use + // download outside the daemon's deliberately short live-probe deadline; + // otherwise doctor reports a timeout while the download keeps running and + // holds the profile command queue. + if (!binaryInfo().installed) await ensureBinary(); + + setDaemonCommandTimeoutSeconds(timeoutSeconds); const bridge = new BrowserBridge(); const page = await bridge.connect({ timeout: timeoutSeconds,