refactor(cua): ignore native PID metadata in TypeScript
Remove PID fields and dependencies from TypeScript discovery, permission metadata and helper handshakes while preserving native implementations and responses. Accept unused response fields, select applications by provider ID, bundle ID or name, and update consumers, guidance and fixtures.
This commit is contained in:
1 parent
8a5ebd2351
commit
b42e744c3c
17 files changed
+63
-45
No files matched your search
@@ -13,8 +13,8 @@ export type { NativeChannel } from '@deepseek-ai/dsh-cua/native-client'
|
||||
|
||||
const Ready = z.object({
|
||||
type: z.literal('ready'), protocol: z.literal(1), runtimeKey: z.string(),
|
||||
pid: z.number().int().positive(), instanceId: z.string().min(1),
|
||||
}).strict()
|
||||
instanceId: z.string().min(1),
|
||||
})
|
||||
const Refusal = z.object({ type: z.literal('error'), code: z.string().min(1), message: z.string() }).strict()
|
||||
const handshakeByteLimit = 8192
|
||||
type LauncherExit = [PromiseSettledResult<SubprocessOutcome>, PromiseSettledResult<boolean>]
|
||||
|
||||
@@ -56,8 +56,8 @@ it('inlines discovery, restoration, screenshot, and mouse-move return records',
|
||||
expect(lines.find(line => line.startsWith(`- ${name}(`))).toMatch(/ -> \(?\{ /)
|
||||
}
|
||||
expect(COMMON_GUIDANCE).toContain("{ platform: string, permissions: { accessibility: boolean, screenRecording: boolean }, appLevelAccessibility: boolean, keyboard: ('app' | 'window')[], sessionLocked: boolean | null, methods: string[], coordinateSpace: 'screenshot-pixels', browser: false }")
|
||||
expect(COMMON_GUIDANCE).toContain('{ id: string, displayName: string, isRunning: true, pid: number }[]')
|
||||
expect(COMMON_GUIDANCE).toContain('({ id: string, displayName: string, path: string } & ({ isRunning: true, pid: number } | { isRunning: false }))[]')
|
||||
expect(COMMON_GUIDANCE).toContain('{ id: string, displayName: string, isRunning: true }[]')
|
||||
expect(COMMON_GUIDANCE).toContain('{ id: string, displayName: string, path: string, isRunning: boolean }[]')
|
||||
expect(COMMON_GUIDANCE).toContain('bounds: { x: number, y: number, width: number, height: number }, isOnScreen: boolean, onCurrentSpace: boolean | null')
|
||||
})
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ import { z } from 'zod'
|
||||
import { connectWindowsPipe, type WindowsConnection, type WindowsTransport } from './native-transport.ts'
|
||||
|
||||
const Ready = z.object({
|
||||
type: z.literal('ready'), protocol: z.literal(1), runtimeKey: z.string(), instanceId: z.string().min(1),
|
||||
}).strict()
|
||||
const Started = Ready.extend({ endpoint: z.string().min(1), launcherId: z.string().min(1) }).strict()
|
||||
type: z.literal('ready'), protocol: z.literal(1), runtimeKey: z.string().min(1), instanceId: z.string().min(1),
|
||||
})
|
||||
const Started = Ready.extend({ endpoint: z.string().min(1), launcherId: z.string().min(1) })
|
||||
// Private transport control frames have fixed encoding; operation replies pass through unchanged.
|
||||
const closedFrame = Buffer.from('{"type":"closed"}\n')
|
||||
type Owner = { subprocess: Pick<SubprocessRuntime, 'spawn'> }
|
||||
@@ -238,7 +238,7 @@ class WindowsChannel implements NativeChannel {
|
||||
}
|
||||
const response = await frame
|
||||
const accepted = Ready.parse(response.value)
|
||||
if (accepted.runtimeKey !== ready.runtimeKey || accepted.instanceId !== ready.instanceId || accepted.pid !== ready.pid)
|
||||
if (accepted.runtimeKey !== ready.runtimeKey || accepted.instanceId !== ready.instanceId)
|
||||
throw new NativeError('NATIVE_PROTOCOL', 'Windows pipe belongs to another helper instance')
|
||||
this.startup.signal.throwIfAborted()
|
||||
admitted = true
|
||||
|
||||
@@ -199,7 +199,12 @@ it.each(['wrong-runtime', 'wrong-instance', 'extra', 'malformed', 'oversized', '
|
||||
},
|
||||
)
|
||||
|
||||
it.each(['wrong-instance', 'wrong-pid', 'extra', 'malformed', 'oversized'] as const)(
|
||||
it.each(['wrong-pid', 'extra'] as const)('ignores unused %s handshake metadata', async (handshake) => {
|
||||
const f = await fixture({ handshake })
|
||||
await expect(f.client().request('input', {}, new AbortController().signal)).resolves.toMatchObject({ connection: 1 })
|
||||
})
|
||||
|
||||
it.each(['wrong-instance', 'malformed', 'oversized'] as const)(
|
||||
'rejects a %s Agent handshake before dispatch', async (handshake) => {
|
||||
const f = await fixture({ handshake })
|
||||
await expect(f.client().request('input', {}, new AbortController().signal)).rejects.toThrow()
|
||||
|
||||
@@ -6,7 +6,6 @@ export type { CuaApplicationGrant, CuaApplicationId } from './application-types.
|
||||
export type CuaSystemPermission = 'accessibility' | 'screenRecording'
|
||||
/** Observed process; neither the helper nor its parent is assumed to be the TCC owner. */
|
||||
export interface CuaPermissionProcess {
|
||||
pid: number
|
||||
path?: string | undefined
|
||||
name?: string | undefined
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ interface ControlOptions extends AccessOptions {
|
||||
permissionParameters: (method: 'requestPermission' | 'openPermissionSettings', permission: CuaSystemPermission, locale: string) => Record<string, JsonValue>
|
||||
}
|
||||
type Control = (method: string, params: Record<string, JsonValue>) => Promise<JsonValue>
|
||||
const permissionProcess = z.object({ pid: z.number().int().positive(), path: z.string().optional(), name: z.string().optional() })
|
||||
const permissionProcess = z.object({ path: z.string().optional(), name: z.string().optional() })
|
||||
const systemPermissions = z.discriminatedUnion('platform', [z.object({
|
||||
platform: z.literal('darwin'), accessibility: z.boolean(), screenRecording: z.boolean(), helper: permissionProcess, parent: permissionProcess,
|
||||
launchApplication: z.object({ name: z.string().min(1), path: z.string().min(1), icon: ApplicationIcon.optional() }).optional(),
|
||||
|
||||
@@ -59,7 +59,7 @@ export function createCuaApi(dispatch: CuaDispatch, sink?: CuaObservationSink):
|
||||
},
|
||||
async listApps(options) {
|
||||
const result = AppsResult.parse(await call('listApps', {})).map(app => ({
|
||||
id: app.bundleId || String(app.pid), displayName: app.name, isRunning: true as const, pid: app.pid,
|
||||
id: app.id ?? (app.bundleId || app.name), displayName: app.name, isRunning: true as const,
|
||||
}))
|
||||
await emit(sink, result, options)
|
||||
return result
|
||||
|
||||
@@ -8,10 +8,10 @@ Signatures show resolved return values after await. ActionResult = { dispatched:
|
||||
|
||||
Setup and discovery:
|
||||
- cua.getCapabilities(options?: { emit?: boolean }) -> { platform: string, permissions: { accessibility: boolean, screenRecording: boolean }, appLevelAccessibility: boolean, keyboard: ('app' | 'window')[], sessionLocked: boolean | null, methods: string[], coordinateSpace: 'screenshot-pixels', browser: false } — inspect reported permissions, sessionLocked, methods, and keyboard scopes without requesting permissions.
|
||||
- cua.listApps(options?: { emit?: boolean }) -> { id: string, displayName: string, isRunning: true, pid: number }[] — enumerate running applications.
|
||||
- cua.findApps(query: string, options?: { emit?: boolean }) -> ({ id: string, displayName: string, path: string } & ({ isRunning: true, pid: number } | { isRunning: false }))[] — search installed applications, including stopped matches; only running matches carry pid.
|
||||
- cua.getState(options?: { emit?: boolean }) -> { apps: { id: string, displayName: string, isRunning: true, pid: number }[], browsers: [] } — summarize running applications; this is not an accessibility observation.
|
||||
- cua.getApp(selector: string) -> App — select an application by name, bundle ID, absolute path, or running PID string, obtain consent, launch in the background if needed, and read initial AX state silently; it does not activate the app.
|
||||
- cua.listApps(options?: { emit?: boolean }) -> { id: string, displayName: string, isRunning: true }[] — enumerate running applications.
|
||||
- cua.findApps(query: string, options?: { emit?: boolean }) -> { id: string, displayName: string, path: string, isRunning: boolean }[] — search installed applications, including stopped matches.
|
||||
- cua.getState(options?: { emit?: boolean }) -> { apps: { id: string, displayName: string, isRunning: true }[], browsers: [] } — summarize running applications; this is not an accessibility observation.
|
||||
- cua.getApp(selector: string) -> App — select an application by provider-issued discovery ID, name, bundle ID, or absolute path where supported, obtain consent, launch in the background if needed, and read initial AX state silently; it does not activate the app.
|
||||
- app.getWindows(options?: { emit?: boolean }) -> { windowId: number, appName: string, title: string, bounds: { x: number, y: number, width: number, height: number }, isOnScreen: boolean, onCurrentSpace: boolean | null }[] — enumerate windows with global native bounds; null onCurrentSpace means unknown Space membership.
|
||||
- app.getWindow(windowId: number) -> Window — select one window and read its initial AX state silently; do this before any window screenshot or coordinate input.
|
||||
- app.activate() -> ActionResult — request explicit foreground activation; use only when foreground access is required.
|
||||
|
||||
@@ -4,7 +4,6 @@ import { z } from 'zod'
|
||||
import type { CuaAppHandle, CuaSnapshotId } from './types.ts'
|
||||
|
||||
const id = z.number().int().positive().max(2 ** 32 - 1)
|
||||
const pid = z.number().int().positive().max(2 ** 31 - 1)
|
||||
const point = z.number().nonnegative()
|
||||
const relativePoint = z.number().int().min(0).max(999)
|
||||
const app = { appHandle: z.string().min(1) }
|
||||
@@ -101,11 +100,12 @@ export const AppResult = z.object({
|
||||
appHandle: z.string().min(1).transform(value => value as CuaAppHandle),
|
||||
name: z.string(), bundleId: z.string().optional(), running: z.boolean(),
|
||||
})
|
||||
/** Running application records from the helper. */
|
||||
export const AppsResult = z.array(z.object({ pid, name: z.string(), bundleId: z.string().optional() }))
|
||||
/** Installed application matches keep running and stopped records distinct. */
|
||||
export const FoundAppsResult = z.array(z.intersection(z.object({ id: z.string().min(1), displayName: z.string(), path: z.string().min(1) }),
|
||||
z.discriminatedUnion('isRunning', [z.object({ isRunning: z.literal(true), pid }), z.object({ isRunning: z.literal(false) })])))
|
||||
/** Running application metadata used for selection; other native fields are ignored. */
|
||||
export const AppsResult = z.array(z.object({ id: z.string().min(1).optional(), name: z.string(), bundleId: z.string().optional() }))
|
||||
/** Installed application matches and their current running state. */
|
||||
export const FoundAppsResult = z.array(z.object({
|
||||
id: z.string().min(1), displayName: z.string(), path: z.string().min(1), isRunning: z.boolean(),
|
||||
}))
|
||||
/** Current windows and their global native bounds. */
|
||||
export const WindowsResult = z.array(z.object({
|
||||
windowId: id, appName: z.string(), title: z.string(), bounds,
|
||||
|
||||
@@ -51,8 +51,8 @@ export type CuaStartupCapabilities = Pick<
|
||||
'platform' | 'methods' | 'appLevelAccessibility' | 'keyboard' | 'browser' | 'coordinateSpace'
|
||||
>
|
||||
/** App discovery result accepted by getApp. */
|
||||
export type CuaAppInfo = Pick<Extract<CuaFoundAppInfo, { isRunning: true }>, 'id' | 'displayName' | 'isRunning' | 'pid'>
|
||||
/** An installed application match; running matches include a process identifier. */
|
||||
export type CuaAppInfo = Pick<CuaFoundAppInfo, 'id' | 'displayName'> & { isRunning: true }
|
||||
/** An installed application match with its current running state. */
|
||||
export type CuaFoundAppInfo = z.output<typeof FoundAppsResult>[number]
|
||||
/** Window bounds in the provider's global native units. Local operations use an origin of [0,0]. */
|
||||
export type CuaBounds = CuaWindowInfo['bounds']
|
||||
@@ -273,7 +273,7 @@ export interface CuaApi {
|
||||
getState(options?: CuaObservationOptions): Promise<{ apps: CuaAppInfo[]; browsers: [] }>
|
||||
/**
|
||||
* Select an app, obtain consent, launch it in the background if necessary, and silently retain initial AX state.
|
||||
* @param selector - Exact name, bundle ID, absolute application path or running PID string; ambiguous names reject.
|
||||
* @param selector - Provider-issued identifier, exact name, bundle ID or absolute application path; ambiguous selectors reject.
|
||||
* @returns An application handle tied to this process lifetime; its initial tree read becomes the current snapshot.
|
||||
*/
|
||||
getApp(selector: string): Promise<CuaApp>
|
||||
|
||||
@@ -398,7 +398,8 @@ it('preserves captured launch metadata separately from the current helper parent
|
||||
const launchApplication = { name: 'Visual Studio Code', path: '/Applications/Visual Studio Code.app', icon: 'data:image/png;base64,aWNvbg==' }
|
||||
control.request.mockResolvedValueOnce({ platform: 'darwin' as const, accessibility: false, screenRecording: false,
|
||||
helper: { pid: 9 }, parent: { pid: 1, path: '/sbin/launchd' }, launchApplication })
|
||||
expect(await f.ctx.cuaAccess.systemStatus()).toMatchObject({ parent: { pid: 1 }, launchApplication })
|
||||
expect(await f.ctx.cuaAccess.systemStatus()).toEqual({ platform: 'darwin', accessibility: false, screenRecording: false,
|
||||
helper: {}, parent: { path: '/sbin/launchd' }, launchApplication })
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ function fixture(withSink = true) {
|
||||
const dispatch = vi.fn(async ({ method, params }: CuaOperation): Promise<JsonValue> => {
|
||||
switch (method) {
|
||||
case 'getCapabilities': return currentCapabilities
|
||||
case 'listApps': return [selected, { pid: 18, name: 'Utility' }]
|
||||
case 'listApps': return [selected, { id: 'utility-selector', pid: 4, name: 'Utility' }]
|
||||
case 'getApp': return selected
|
||||
case 'listWindows': return windows
|
||||
case 'mouseMove': return { ...delivered, hoverRoute: 'accessibility', systemPointerMoved: false }
|
||||
@@ -59,12 +59,12 @@ function screenshot(overrides: Record<string, JsonValue> = {}): JsonValue {
|
||||
}
|
||||
|
||||
describe('application discovery', () => {
|
||||
it('reports permissions and discovers both bundle and PID selectors without activating an app', async () => {
|
||||
it('reports permissions and discovers provider-issued selectors without activating an app', async () => {
|
||||
const f = fixture()
|
||||
expect(await f.api.getCapabilities()).toEqual(capabilities)
|
||||
expect(await f.api.getState()).toEqual({ apps: [
|
||||
{ id: 'test.editor', displayName: 'Editor', isRunning: true, pid: 17 },
|
||||
{ id: '18', displayName: 'Utility', isRunning: true, pid: 18 },
|
||||
{ id: 'test.editor', displayName: 'Editor', isRunning: true },
|
||||
{ id: 'utility-selector', displayName: 'Utility', isRunning: true },
|
||||
], browsers: [] })
|
||||
expect(f.dispatch.mock.calls.map(([operation]) => operation.method)).toEqual(['getCapabilities', 'listApps'])
|
||||
const emitted = f.sink.text.mock.calls.map(([text]): unknown => JSON.parse(text))
|
||||
@@ -72,6 +72,19 @@ describe('application discovery', () => {
|
||||
expect(f.sink.text).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('passes a discovered selector unchanged to application selection', async () => {
|
||||
const f = fixture()
|
||||
const apps = await f.api.listApps({ emit: false })
|
||||
await f.api.getApp(apps[1]!.id)
|
||||
expect(f.dispatch).toHaveBeenCalledWith({ method: 'getApp', params: { selector: 'utility-selector' } })
|
||||
})
|
||||
|
||||
it('uses the application name when discovery has no identifier', async () => {
|
||||
const f = fixture()
|
||||
f.dispatch.mockResolvedValueOnce([{ pid: 18, name: 'Utility' }])
|
||||
expect(await f.api.listApps({ emit: false })).toEqual([{ id: 'Utility', displayName: 'Utility', isRunning: true }])
|
||||
})
|
||||
|
||||
it('returns observations without requiring an output sink', async () => {
|
||||
const f = fixture(false)
|
||||
expect(await f.api.getCapabilities({ emit: false })).toEqual(capabilities)
|
||||
@@ -94,9 +107,9 @@ describe('application discovery', () => {
|
||||
const f = fixture()
|
||||
const matches = [
|
||||
{ id: 'test.stopped', displayName: 'Stopped', path: '/Applications/Stopped.app', isRunning: false },
|
||||
{ id: 'test.running', displayName: 'Running', path: '/Applications/Running.app', isRunning: true, pid: 17 },
|
||||
{ id: 'test.running', displayName: 'Running', path: '/Applications/Running.app', isRunning: true },
|
||||
]
|
||||
f.dispatch.mockResolvedValueOnce(matches)
|
||||
f.dispatch.mockResolvedValueOnce([matches[0]!, { ...matches[1]!, pid: 17 }])
|
||||
expect(await f.api.findApps('Editor')).toEqual(matches)
|
||||
expect(f.dispatch).toHaveBeenLastCalledWith({ method: 'findApps', params: { query: 'Editor' } })
|
||||
expect(f.sink.text).toHaveBeenLastCalledWith(JSON.stringify(matches))
|
||||
@@ -113,7 +126,7 @@ describe('application discovery', () => {
|
||||
const f = fixture()
|
||||
f.dispatch.mockResolvedValueOnce({ platform: 'macos' })
|
||||
await expect(f.api.getCapabilities()).rejects.toThrow()
|
||||
f.dispatch.mockResolvedValueOnce([{ pid: 0, name: 'Invalid' }])
|
||||
f.dispatch.mockResolvedValueOnce([{ id: '', name: 'Invalid' }])
|
||||
await expect(f.api.listApps()).rejects.toThrow()
|
||||
f.dispatch.mockResolvedValueOnce({ ...selected, appHandle: '' })
|
||||
await expect(f.api.getApp('Editor')).rejects.toThrow()
|
||||
|
||||
@@ -16,7 +16,7 @@ const capabilities: CuaCapabilities = {
|
||||
keyboard: ['app'], sessionLocked: false, methods: ['getCapabilities', 'getApp', 'getState', 'screenshot'],
|
||||
coordinateSpace: 'screenshot-pixels', browser: false,
|
||||
}
|
||||
const selected = { appHandle: 'app-17', pid: 17, name: 'Editor', running: true }
|
||||
const selected = { appHandle: 'app-17', name: 'Editor', running: true }
|
||||
const bounds = { x: 0, y: 0, width: 640, height: 480 }
|
||||
const snapshot = {
|
||||
appHandle: 'app-17', windowId: null, snapshotId: 'snapshot-1', tree: '0 button', truncated: false,
|
||||
@@ -56,8 +56,8 @@ it('propagates a denied action without retrying it', async () => {
|
||||
})
|
||||
|
||||
it('supports callers that retain observations without a text callback', async () => {
|
||||
const cua = connectCua(() => async () => [{ pid: 17, name: 'Editor' }])
|
||||
await expect(cua.listApps()).resolves.toEqual([{ id: '17', displayName: 'Editor', isRunning: true, pid: 17 }])
|
||||
const cua = connectCua(() => async () => [{ id: 'editor-selector', name: 'Editor' }])
|
||||
await expect(cua.listApps()).resolves.toEqual([{ id: 'editor-selector', displayName: 'Editor', isRunning: true }])
|
||||
})
|
||||
|
||||
it.each([null, 'text only', { content: [{ type: 'text', text: '{}' }] }])('rejects an unusable tool result %j', async (result) => {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { expect, it, onTestFinished, vi } from 'vitest'
|
||||
import type { CuaApplicationId, CuaApplicationState, CuaSystemPermissionState } from '../src/access-types.ts'
|
||||
import { PermissionController } from '../src/client/controller.ts'
|
||||
|
||||
const value: CuaSystemPermissionState = { platform: 'darwin' as const, accessibility: true, screenRecording: false, helper: { pid: 9 }, parent: { pid: 8 } }
|
||||
const value: CuaSystemPermissionState = { platform: 'darwin' as const, accessibility: true, screenRecording: false, helper: {}, parent: {} }
|
||||
const applications: CuaApplicationState = {
|
||||
allowedApps: [], blockedApps: [], persistentAvailable: true }
|
||||
type CuaRemote = Context['remote']['cuaAccess']
|
||||
|
||||
@@ -14,7 +14,7 @@ import type { ApplicationApprovalProps } from '../src/client/ApplicationApproval
|
||||
|
||||
const contribution: TypertRemoteContribution = { package: '@deepseek-ai/dsh-cua', descriptors: [] }
|
||||
const value: CuaApplicationState & { system: CuaSystemPermissionState } = {
|
||||
system: { platform: 'darwin' as const, accessibility: false, screenRecording: false, helper: { pid: 2 }, parent: { pid: 1 } },
|
||||
system: { platform: 'darwin' as const, accessibility: false, screenRecording: false, helper: {}, parent: {} },
|
||||
allowedApps: [], blockedApps: [], persistentAvailable: true }
|
||||
|
||||
function actionsOf(value: Record<string, unknown>): asserts value is Record<string, unknown> & PermissionFace {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { en, zh } from '../src/client/locales.ts'
|
||||
|
||||
afterEach(() => { cleanup(); vi.useRealTimers() })
|
||||
const value: CuaApplicationState & { system: Extract<CuaSystemPermissionState, { platform: 'darwin' }> } = { system: { platform: 'darwin' as const, accessibility: false, screenRecording: true,
|
||||
helper: { pid: 9, path: '/native/helper' }, parent: { pid: 8, path: '/host/node' } },
|
||||
helper: { path: '/native/helper' }, parent: { path: '/host/node' } },
|
||||
allowedApps: [], blockedApps: [], persistentAvailable: true }
|
||||
|
||||
function observe(value: CuaApplicationState & { system: CuaSystemPermissionState }) {
|
||||
@@ -232,7 +232,7 @@ it.each([en, zh])('shows four requirements with the target application beside ea
|
||||
const f = fixture()
|
||||
render(<PermissionPanel {...f.props} t={makeTranslate(dictionary)} />)
|
||||
const application = { name: 'Visual Studio Code.app', path: '/Applications/Visual Studio Code.app', icon: 'data:image/png;base64,aWNvbg==' }
|
||||
f.publish({ ...observe({ ...value, system: { ...value.system, parent: { pid: 1, path: '/sbin/launchd' }, launchApplication: application } }) })
|
||||
f.publish({ ...observe({ ...value, system: { ...value.system, parent: { path: '/sbin/launchd' }, launchApplication: application } }) })
|
||||
const requirements = screen.getByRole('region', { name: dictionary.title })
|
||||
expect(within(requirements).getAllByRole('group')).toHaveLength(4)
|
||||
expect(screen.getByRole('region', { name: dictionary.applicationsTitle })).toBeTruthy()
|
||||
@@ -440,7 +440,7 @@ it('retains a known read-only configuration and query failure throughout managem
|
||||
it.each([true, false, null] as const)('shows Windows desktop access for lock state %s without macOS permission actions', (sessionLocked) => {
|
||||
const f = fixture()
|
||||
f.publish({ ...observe(value), system: { value: { platform: 'windows', sessionLocked, captureAvailable: true,
|
||||
elevated: false, helper: { pid: 9, path: 'C:\\helper.exe' } }, error: null, busy: false } })
|
||||
elevated: false, helper: { path: 'C:\\helper.exe' } }, error: null, busy: false } })
|
||||
render(<PermissionPanel {...f.props} />)
|
||||
const desktop = screen.getByRole('group', { name: en.windowsDesktop })
|
||||
const capture = screen.getByRole('group', { name: en.windowsCapture })
|
||||
@@ -452,7 +452,7 @@ it.each([true, false, null] as const)('shows Windows desktop access for lock sta
|
||||
expect(screen.queryByRole('button', { name: en.granted })).toBeNull()
|
||||
expect(screen.queryByText('C:\\helper.exe')).toBeNull()
|
||||
f.publish({ system: { value: { platform: 'windows', sessionLocked: false, captureAvailable: false,
|
||||
elevated: true, helper: { pid: 9 } }, error: null, busy: false } })
|
||||
elevated: true, helper: {} }, error: null, busy: false } })
|
||||
expect(within(capture).getByText(en.unavailable)).toBeTruthy()
|
||||
expect(screen.getByText(en.elevated, { exact: false })).toBeTruthy()
|
||||
f.publish({ system: { ...f.state.getSnapshot().system, error: 'offline' } })
|
||||
@@ -462,7 +462,7 @@ it.each([true, false, null] as const)('shows Windows desktop access for lock sta
|
||||
it.each([[true, true], [false, true], [false, false], [null, true]] as const)('offers Windows setup for lock=%s and capture=%s', (sessionLocked, captureAvailable) => {
|
||||
const f = fixture(), onDismiss = vi.fn()
|
||||
f.publish({ system: { value: { platform: 'windows', sessionLocked, captureAvailable,
|
||||
elevated: false, helper: { pid: 9 } }, error: null, busy: false } })
|
||||
elevated: false, helper: {} }, error: null, busy: false } })
|
||||
render(<PermissionSetup {...f.props} onDismiss={onDismiss} onOpenDetails={vi.fn()} />)
|
||||
const needsSetup = sessionLocked !== false || !captureAvailable
|
||||
expect(screen.queryByRole('dialog') !== null).toBe(needsSetup)
|
||||
|
||||
@@ -6324,7 +6324,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'CuaPermissionProcess',
|
||||
declaration: 'export interface CuaPermissionProcess {\n pid: number;\n path?: string | undefined;\n name?: string | undefined;\n}',
|
||||
declaration: 'export interface CuaPermissionProcess {\n path?: string | undefined;\n name?: string | undefined;\n}',
|
||||
},
|
||||
{
|
||||
name: 'CuaSystemPermission',
|
||||
@@ -6332,7 +6332,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'CuaSystemPermissionState',
|
||||
declaration: 'export type CuaSystemPermissionState = {\n platform: \'darwin\';\n accessibility: boolean;\n screenRecording: boolean;\n helper: CuaPermissionProcess;\n parent: CuaPermissionProcess;\n launchApplication?: {\n name: string;\n path: string;\n icon?: string | undefined;\n } | undefined;\n} | {\n platform: \'win32\';\n sessionLocked: boolean | null;\n captureAvailable: boolean;\n elevated: boolean;\n helper: CuaPermissionProcess;\n};',
|
||||
declaration: 'export type CuaSystemPermissionState = {\n platform: \'darwin\';\n accessibility: boolean;\n screenRecording: boolean;\n helper: CuaPermissionProcess;\n parent: CuaPermissionProcess;\n launchApplication?: {\n name: string;\n path: string;\n icon?: string | undefined;\n } | undefined;\n} | {\n platform: \'windows\';\n sessionLocked: boolean | null;\n captureAvailable: boolean;\n elevated: boolean;\n helper: CuaPermissionProcess;\n};',
|
||||
},
|
||||
{
|
||||
name: 'DailyInput',
|
||||
|
||||
Reference in new issue
Block a user