import { callInBridge, callInBridgeWrite, createBridgeFromParsed, parseBridgeCliArgs, } from '../ws-bridge' import type { AppSettingsSnapshot, SystemPermissionApp, SystemPermissionKind, SystemPermissionMutation, SystemPermissionStatus, } from '@rnx/globals' interface PermissionsOptions { port?: number verbose?: boolean } const STATUSES: readonly SystemPermissionStatus[] = [ 'not-determined', 'restricted', 'denied', 'authorized', 'limited', 'provisional', 'ephemeral', 'unavailable', ] const KINDS: readonly SystemPermissionKind[] = [ 'app-tracking-transparency', 'bluetooth', 'calendars', 'camera', 'contacts', 'face-id', 'local-network', 'location-always', 'location-when-in-use', 'media-library', 'microphone', 'motion', 'notifications', 'photo-library', 'photo-library-add-only', 'reminders', 'siri', 'speech-recognition', ] function printHelp(): void { console.log(` rnx permissions — inspect and change simulated operating-system permissions usage: rnx permissions list [--json] rnx permissions get [--json] rnx permissions set [--once] [--accuracy full|reduced] [--json] rnx permissions reset [kind] [--json] `) } function print(value: unknown, json: boolean): void { console.log(json ? JSON.stringify(value, null, 2) : value) } function flagValue(args: string[], flag: string): string | undefined { const index = args.indexOf(flag) return index >= 0 ? args[index + 1] : undefined } function permissionKind(value: string): SystemPermissionKind | null { return KINDS.find((candidate) => candidate === value) ?? null } function permissionStatus(value: string): SystemPermissionStatus | null { return STATUSES.find((candidate) => candidate === value) ?? null } function mutationFor( status: SystemPermissionStatus, once: boolean, accuracy: 'full' | 'reduced' | undefined, ): SystemPermissionMutation { switch (status) { case 'authorized': return { status, ...(once ? { authorizationScope: 'once' } : null), ...(accuracy ? { locationAccuracy: accuracy } : null), } case 'not-determined': case 'restricted': case 'denied': case 'limited': case 'provisional': case 'ephemeral': case 'unavailable': return { status } } } export async function runPermissions( args: string[], opts: PermissionsOptions, ): Promise { if (args.includes('--help') || args.includes('-h')) { printHelp() return 0 } const parsed = parseBridgeCliArgs(args, { port: opts.port, stripBooleanFlags: ['--json', '--once'], stripValueFlags: ['--accuracy'], }) const [command, appId, kindValue, statusValue] = parsed.positional if (!command) { printHelp() return 1 } const json = args.includes('--json') const bridge = createBridgeFromParsed(parsed) try { if (command === 'list') { const apps = await callInBridge( bridge, 'SootSim.bridges.test.permissionListApps', ) print( json ? apps : apps.map((app) => `${app.appId}\t${app.displayName}`).join('\n'), json, ) return 0 } if (!appId) throw new Error(`${command} requires an app id`) if (command === 'get') { const snapshot = await callInBridge( bridge, 'SootSim.bridges.test.permissionGetAppSettings', appId, ) print(snapshot, json) return 0 } if (command === 'reset') { if (kindValue) { const kind = permissionKind(kindValue) if (!kind) throw new Error(`unknown permission kind: ${kindValue}`) await callInBridgeWrite( bridge, 'SootSim.bridges.test.permissionSetAppPermission', appId, kind, { status: 'not-determined' }, ) } else { await callInBridgeWrite( bridge, 'SootSim.bridges.test.permissionResetApp', appId, ) } const snapshot = await callInBridge( bridge, 'SootSim.bridges.test.permissionGetAppSettings', appId, ) print(snapshot, json) return 0 } if (command !== 'set') throw new Error(`unknown permissions command: ${command}`) if (!kindValue || !statusValue) { throw new Error('set requires an app id, permission kind, and status') } const kind = permissionKind(kindValue) if (!kind) throw new Error(`unknown permission kind: ${kindValue}`) const status = permissionStatus(statusValue) if (!status) throw new Error(`unknown permission status: ${statusValue}`) const accuracyValue = flagValue(args, '--accuracy') const accuracy = accuracyValue === 'full' || accuracyValue === 'reduced' ? accuracyValue : undefined if (accuracyValue && !accuracy) { throw new Error(`unknown location accuracy: ${accuracyValue}`) } const snapshot = await callInBridgeWrite( bridge, 'SootSim.bridges.test.permissionSetAppPermission', appId, kind, mutationFor(status, args.includes('--once'), accuracy), ) print(snapshot, json) return 0 } catch (error) { console.error( ` permissions failed: ${error instanceof Error ? error.message : error}`, ) return 1 } finally { bridge.close() } }