@mcesystems/usbmuxd-instance-manager 1.0.74 → 1.0.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts", "../src/UsbmuxdService.ts", "../src/InstanceManager.ts", "../src/usbipd.ts", "../src/wsl.ts"],
4
- "sourcesContent": ["export { UsbmuxdService } from \"./UsbmuxdService.js\";\nexport { InstanceManager } from \"./InstanceManager.js\";\nexport type {\n\tInstanceManagerConfig,\n\tUsbmuxdInstance,\n\tDeviceInfo,\n\tDeviceMapping,\n\tManagerStats,\n} from \"./types/index.js\";\n", "import { EventEmitter } from \"node:events\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport UsbDeviceListener from \"@mcesystems/usb-device-listener\";\nimport type { DeviceInfo } from \"@mcesystems/usb-device-listener\";\nimport { InstanceManager } from \"./InstanceManager.js\";\nimport type { DeviceMapping, InstanceManagerConfig, UsbmuxdInstance } from \"./types/index.js\";\n\nconst { logInfo, logError, logDataObject } = createLoggers(\"usbmuxd-service\");\n\n/**\n * Main service that integrates usb-device-listener with InstanceManager\n * Monitors iOS devices and manages usbmuxd instances automatically.\n * Extends EventEmitter: forwards instance-started, instance-stopped, device-assigned,\n * device-removed, instance-log, instance-exited from the underlying manager.\n */\nexport class UsbmuxdService extends EventEmitter {\n\tprivate manager: InstanceManager;\n\tprivate usbListener: UsbDeviceListener;\n\tprivate isListening = false;\n\n\tconstructor(config: Partial<InstanceManagerConfig> = {}) {\n\t\tsuper();\n\t\tthis.manager = new InstanceManager(config);\n\t\tthis.usbListener = new UsbDeviceListener();\n\t\tthis.setupEventHandlers();\n\t}\n\n\t/**\n\t * Forward manager events to service subscribers (no logging; CLI/examples handle logging).\n\t */\n\tprivate setupEventHandlers(): void {\n\t\tthis.manager.on(\"instance-started\", (instance: UsbmuxdInstance) => {\n\t\t\tthis.emit(\"instance-started\", instance);\n\t\t});\n\n\t\tthis.manager.on(\"instance-stopped\", (instance: UsbmuxdInstance) => {\n\t\t\tthis.emit(\"instance-stopped\", instance);\n\t\t});\n\n\t\tthis.manager.on(\n\t\t\t\"device-assigned\",\n\t\t\t(payload: { device: DeviceInfo; instance: UsbmuxdInstance; mapping: DeviceMapping }) => {\n\t\t\t\tlogInfo(\n\t\t\t\t\t`Device ${payload.device.deviceId} connected to usbmuxd instance at ${payload.mapping.host}:${payload.mapping.port}`\n\t\t\t\t);\n\t\t\t\tthis.emit(\"device-assigned\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"device-removed\",\n\t\t\t(payload: { device: DeviceInfo; instance: UsbmuxdInstance }) => {\n\t\t\t\tthis.emit(\"device-removed\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"instance-log\",\n\t\t\t(payload: { instanceId: number; level: string; message: string }) => {\n\t\t\t\tthis.emit(\"instance-log\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"instance-exited\",\n\t\t\t(payload: { instanceId: number; code: number | null; signal: string | null }) => {\n\t\t\t\tthis.emit(\"instance-exited\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\"device-paired\", (payload: { udid: string; mapping: DeviceMapping }) => {\n\t\t\tthis.emit(\"device-paired\", payload);\n\t\t});\n\t}\n\n\t/**\n\t * Start the service\n\t * Begins monitoring for iOS devices\n\t */\n\tpublic start(): void {\n\t\tif (this.isListening) {\n\t\t\tthrow new Error(\"Service is already running\");\n\t\t}\n\n\t\tconst config = this.manager.getConfig();\n\n\t\t// Convert Apple VID from hex to decimal for comparison\n\t\tconst appleVid = Number.parseInt(config.appleVendorId, 16);\n\n\t\tlogDataObject(\"Starting service...\", { config });\n\n\t\t// Set up device listener callbacks\n\t\tthis.usbListener.onDeviceAdd(async (device: DeviceInfo) => {\n\t\t\t// Filter for Apple devices only\n\t\t\tif (device.vid !== appleVid) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlogInfo(`Apple device connected: ${device.deviceId} (${device.deviceName || \"Unknown\"})`);\n\n\t\t\ttry {\n\t\t\t\tawait this.manager.onDeviceConnected(device);\n\t\t\t} catch (error) {\n\t\t\t\tlogError(\"Error handling device connection:\", error);\n\t\t\t}\n\t\t});\n\n\t\tthis.usbListener.onDeviceRemove(async (device: DeviceInfo) => {\n\t\t\t// Filter for Apple devices only\n\t\t\tif (device.vid !== appleVid) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlogInfo(`Apple device disconnected: ${device.deviceId}`);\n\n\t\t\ttry {\n\t\t\t\tawait this.manager.onDeviceDisconnected(device);\n\t\t\t} catch (error) {\n\t\t\t\tlogError(\"Error handling device disconnection:\", error);\n\t\t\t}\n\t\t});\n\n\t\t// Start listening for USB events\n\t\ttry {\n\t\t\tthis.usbListener.startListening({\n\t\t\t\ttargetDevices: [], // Monitor all devices, we'll filter by VID\n\t\t\t});\n\n\t\t\tthis.isListening = true;\n\t\t\tthis.manager.start();\n\n\t\t\tlogInfo(\"Service started successfully\");\n\t\t\tlogInfo(\"Waiting for iOS devices...\");\n\t\t} catch (error) {\n\t\t\tlogError(\"Failed to start USB listener:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Stop the service\n\t * Stops monitoring and terminates all instances\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tif (!this.isListening) {\n\t\t\treturn;\n\t\t}\n\n\t\tlogInfo(\"Stopping service...\");\n\n\t\t// Stop USB listener\n\t\tthis.usbListener.stopListening();\n\t\tthis.isListening = false;\n\n\t\t// Stop all instances\n\t\tawait this.manager.stop();\n\n\t\tlogInfo(\"Service stopped\");\n\t}\n\n\t/**\n\t * Get device port mapping\n\t */\n\tpublic getDevicePort(udid: string): number | null {\n\t\treturn this.manager.getDevicePort(udid);\n\t}\n\n\t/**\n\t * Get all device mappings\n\t */\n\tpublic getDeviceMappings() {\n\t\treturn this.manager.getDeviceMappings();\n\t}\n\n\t/**\n\t * Get all running instances\n\t */\n\tpublic getInstances() {\n\t\treturn this.manager.getInstances();\n\t}\n\n\t/**\n\t * Get service statistics\n\t */\n\tpublic getStats() {\n\t\treturn this.manager.getStats();\n\t}\n\n\t/**\n\t * Get current configuration\n\t */\n\tpublic getConfig() {\n\t\treturn this.manager.getConfig();\n\t}\n}\n", "import type { ChildProcess } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type {\n\tDeviceInfo,\n\tDeviceMapping,\n\tInstanceManagerConfig,\n\tManagerStats,\n\tUsbmuxdInstance,\n} from \"./types/index.js\";\nimport { Usbipd } from \"./usbipd.js\";\nimport { Wsl2 } from \"./wsl.js\";\n\nconst { logWarning, logDetail, logDataObject, logTask } = createLoggers(\"instance-manager\");\n\n/**\n * Default configuration for the instance manager\n */\nconst DEFAULT_CONFIG: InstanceManagerConfig = {\n\tbatchSize: 4,\n\tbasePort: 27015,\n\tmaxInstances: 5,\n\tusbmuxdPath: \"usbmuxd\", // Path inside WSL2\n\twslDistribution: \"alpine-usbmuxd-build\", // Alpine WSL2 distribution name\n\tverboseLogging: true,\n\tappleVendorId: \"05AC\",\n};\n\n/**\n * Manages multiple usbmuxd instances with batch device allocation\n *\n * @example\n * const manager = new InstanceManager({\n * batchSize: 4,\n * basePort: 27015,\n * });\n *\n * manager.on('instance-started', (instance) => {\n * logInfo(`Instance ${instance.id} started on port ${instance.port}`);\n * });\n *\n * manager.start();\n */\nexport class InstanceManager extends EventEmitter {\n\t/** Map of usbmuxd instance IDs to instances */\n\tprivate instances: Map<number, UsbmuxdInstance> = new Map();\n\t/** Map of device IDs to device mappings host:port */\n\tprivate deviceMappings: Map<string, DeviceMapping> = new Map();\n\t/** Map of usbmuxd instance IDs to child processes running it on wsl2*/\n\tprivate processes: Map<number, ChildProcess> = new Map();\n\t/** Tracks which devices have been attached to WSL */\n\tprivate attachedDevices: Set<string> = new Set();\n\t/** Device IDs currently in the attach flow\n\t * (ignore disconnect until attach completes) */\n\tprivate pendingAttachDevices: Set<string> = new Set();\n\n\tprivate config: InstanceManagerConfig;\n\tprivate nextInstanceId = 1;\n\tprivate startedAt: Date | null = null;\n\tprivate isRunning = false;\n\n\tprivate wsl: Wsl2;\n\tprivate usbipd: Usbipd;\n\tconstructor(config: Partial<InstanceManagerConfig> = {}) {\n\t\tsuper();\n\t\tthis.config = { ...DEFAULT_CONFIG, ...config };\n\t\tthis.wsl = new Wsl2(this.config.wslDistribution);\n\t\tthis.usbipd = new Usbipd(process.env.USBIPD_PATH ?? \"usbipd\");\n\t}\n\n\t/**\n\t * Start the instance manager\n\t */\n\tpublic start(): void {\n\t\tif (this.isRunning) {\n\t\t\tthrow new Error(\"Instance manager is already running\");\n\t\t}\n\n\t\tthis.isRunning = true;\n\t\tthis.startedAt = new Date();\n\t}\n\n\t/**\n\t * Stop the instance manager and all instances\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tif (!this.isRunning) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.isRunning = false;\n\n\t\t// Sync lockdown files from Alpine back to native Apple lockdown directory (before clearing mappings)\n\t\tconst udids = Array.from(this.deviceMappings.keys());\n\t\tawait this.wsl.syncFromAlpine(udids);\n\n\t\t// Detach all devices from WSL.\n\t\t// If you only called unbind without detach,\n\t\t// the device might still be attached to the WSL session\n\t\tawait this.usbipd.detachAllDevicesFromWsl();\n\t\tawait this.usbipd.unbindAllDevicesFromWsl();\n\t\tthis.attachedDevices.clear();\n\n\t\t// Stop all instances\n\t\tconst stopPromises = Array.from(this.instances.keys()).map((id) => this.stopInstance(id));\n\n\t\tawait Promise.all(stopPromises);\n\n\t\tthis.instances.clear();\n\t\tthis.deviceMappings.clear();\n\t\tthis.processes.clear();\n\t}\n\n\tprivate async attachToWsl({\n\t\tbusId,\n\t\tdeviceId,\n\t}: { busId: string; deviceId: string }): Promise<void> {\n\t\tif (!this.attachedDevices.has(busId)) {\n\t\t\tthis.pendingAttachDevices.add(deviceId);\n\t\t\tif (!this.config.wslDistribution) {\n\t\t\t\tthrow new Error(\"WSL distribution not configured\");\n\t\t\t}\n\t\t\tconst attached = await this.usbipd.attachDeviceToWsl(\n\t\t\t\tbusId,\n\t\t\t\tthis.wsl,\n\t\t\t\tthis.config.wslDistribution\n\t\t\t);\n\t\t\tif (attached) {\n\t\t\t\tthis.attachedDevices.add(busId);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async removeDevice(mapping: DeviceMapping): Promise<UsbmuxdInstance> {\n\t\tconst instance = await this.detachFromWsl({ deviceId: mapping.udid });\n\t\tif (!instance) {\n\t\t\tthrow new Error(`Instance ${mapping.instanceId} not found`);\n\t\t}\n\t\tif (mapping.busId) {\n\t\t\tawait this.usbipd.detachDeviceFromWsl(mapping.busId);\n\t\t\tthis.attachedDevices.delete(mapping.busId);\n\t\t}\n\t\tconst deviceIndex = instance.deviceUdids.indexOf(mapping.udid);\n\t\tif (deviceIndex > -1) {\n\t\t\tinstance.deviceUdids.splice(deviceIndex, 1);\n\t\t}\n\n\t\tthis.deviceMappings.delete(mapping.udid);\n\t\treturn instance;\n\t}\n\n\tprivate async onUsbmuxdInstanceEnd({\n\t\tinstanceId,\n\t\tcode,\n\t\tsignal,\n\t}: { instanceId: number; code: number; signal: string }): Promise<void> {\n\t\tthis.emit(\"instance-exited\", {\n\t\t\tinstanceId,\n\t\t\tcode,\n\t\t\tsignal,\n\t\t});\n\t\tfor (const [_, mapping] of this.deviceMappings.entries()) {\n\t\t\tif (mapping.instanceId === instanceId) {\n\t\t\t\tthis.removeDevice(mapping);\n\t\t\t}\n\t\t}\n\t\tthis.instances.delete(instanceId);\n\t\tthis.processes.get(instanceId)?.kill();\n\t\tthis.processes.delete(instanceId);\n\t}\n\n\tprivate async onUsbmuxdInstanceStart({\n\t\tinstance,\n\t\tprocess,\n\t}: { instance: UsbmuxdInstance; process: ChildProcess }): Promise<void> {\n\t\tthis.instances.set(instance.id, instance);\n\t\tthis.processes.set(instance.id, process);\n\t\tthis.emit(\"instance-started\", {\n\t\t\tinstanceId: instance.id,\n\t\t});\n\t}\n\n\tprivate async getInstance() {\n\t\tlet targetInstance = this.findInstanceWithCapacity();\n\n\t\t// If no instance has capacity, create a new one\n\t\tif (!targetInstance) {\n\t\t\tif (this.instances.size >= this.config.maxInstances) {\n\t\t\t\tthrow new Error(`Maximum number of instances (${this.config.maxInstances}) reached`);\n\t\t\t}\n\n\t\t\tconst newInstanceId = this.nextInstanceId++;\n\n\t\t\tlet onExit = (code: number, signal: string) => {\n\t\t\t\tthis.onUsbmuxdInstanceEnd({ instanceId: newInstanceId, code, signal });\n\t\t\t};\n\n\t\t\tonExit = onExit.bind(this);\n\n\t\t\tconst { instance, process } = await this.wsl.createInstance({\n\t\t\t\tid: newInstanceId,\n\t\t\t\tbasePort: this.config.basePort,\n\t\t\t\tverboseLogging: this.config.verboseLogging,\n\t\t\t\tusbmuxdPath: this.config.usbmuxdPath,\n\t\t\t\tonLog: (message) =>\n\t\t\t\t\tthis.emit(\"instance-log\", {\n\t\t\t\t\t\tinstanceId: newInstanceId,\n\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t}),\n\t\t\t\tonExit,\n\t\t\t});\n\t\t\tthis.onUsbmuxdInstanceStart({ instance, process });\n\t\t\ttargetInstance = instance;\n\t\t}\n\t\treturn targetInstance;\n\t}\n\n\t/**\n\t * Handle device connection only for not attached devices\n\t * Attaches device to WSL, then assigns to an existing instance or creates a new one\n\t */\n\tpublic async onDeviceConnected(device: DeviceInfo): Promise<void> {\n\t\tconst busId = await this.usbipd.findBusIdForDevice(device);\n\n\t\tif (this.attachedDevices.has(busId)) {\n\t\t\tlogTask(`Device ${device.deviceId} is already attached. skipping onDeviceConnected...`);\n\t\t\treturn;\n\t\t}\n\n\t\tlogDataObject(\"Device connected\", { device });\n\t\tawait this.wsl.syncToAlpine(device.deviceId);\n\n\t\tawait this.attachToWsl({ busId, deviceId: device.deviceId });\n\n\t\tconst targetInstance = await this.getInstance();\n\n\t\t// Create mapping (include host and busId for cleanup on disconnect)\n\t\tconst mapping: DeviceMapping = {\n\t\t\tudid: device.deviceId,\n\t\t\tinstanceId: targetInstance.id,\n\t\t\thost: targetInstance.host,\n\t\t\tport: targetInstance.port,\n\t\t\taddedAt: new Date(),\n\t\t\tbusId: busId ?? undefined,\n\t\t};\n\n\t\tthis.deviceMappings.set(device.deviceId, mapping);\n\n\t\t// Add device to instance\n\t\ttargetInstance.deviceUdids.push(device.deviceId);\n\n\t\tthis.emit(\"device-assigned\", {\n\t\t\tdevice,\n\t\t\tinstance: targetInstance,\n\t\t\tmapping,\n\t\t});\n\n\t\tthis.pendingAttachDevices.delete(device.deviceId);\n\t}\n\n\tprivate async detachFromWsl({ deviceId }: { deviceId: string }): Promise<UsbmuxdInstance> {\n\t\tconst mapping = this.deviceMappings.get(deviceId);\n\t\tif (!mapping) {\n\t\t\tthrow new Error(`Device ${deviceId} was not tracked`);\n\t\t}\n\n\t\t// Detach device from WSL if it was attached\n\t\tif (mapping.busId) {\n\t\t\tawait this.usbipd.detachDeviceFromWsl(mapping.busId);\n\t\t\tthis.attachedDevices.delete(mapping.busId);\n\t\t}\n\n\t\tconst instance = this.instances.get(mapping.instanceId);\n\t\tif (!instance) {\n\t\t\tlogWarning(`Instance ${mapping.instanceId} not found`);\n\t\t\tthis.deviceMappings.delete(deviceId);\n\t\t\tthrow new Error(`Instance ${mapping.instanceId} not found`);\n\t\t}\n\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Handle device disconnection only for attached devices\n\t * Detaches from WSL, removes device from instance, and stops instance if empty\n\t */\n\tpublic async onDeviceDisconnected(device: DeviceInfo): Promise<void> {\n\t\t// Ignore disconnect while we're still attaching this device (Windows sees disconnect when device moves to WSL)\n\t\tif (!this.attachedDevices.has(device.deviceId)) {\n\t\t\tlogTask(`Device ${device.deviceId} is not attached. skipping onDeviceDisconnected...`);\n\t\t\treturn;\n\t\t}\n\t\tlogDataObject(\"Device disconnected\", { device });\n\n\t\tconst mapping = this.deviceMappings.get(device.deviceId);\n\t\tif (!mapping) {\n\t\t\tthrow new Error(`Device ${device.deviceId} was not tracked`);\n\t\t}\n\t\t// Remove device from instance\n\t\tconst instance = await this.removeDevice(mapping);\n\n\t\tthis.emit(\"device-removed\", {\n\t\t\tdevice,\n\t\t\tinstance,\n\t\t});\n\n\t\t// If instance is now empty, stop it\n\t\tif (instance.deviceUdids.length === 0) {\n\t\t\tlogDetail(`Instance ${instance.id} is empty, stopping...`);\n\t\t\tawait this.stopInstance(instance.id);\n\t\t}\n\t}\n\n\t/**\n\t * Find an instance with available capacity\n\t */\n\tprivate findInstanceWithCapacity(): UsbmuxdInstance | null {\n\t\tfor (const instance of this.instances.values()) {\n\t\t\tif (instance.deviceUdids.length < this.config.batchSize) {\n\t\t\t\treturn instance;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Stop a specific instance\n\t */\n\tprivate async stopInstance(instanceId: number): Promise<void> {\n\t\tconst instance = this.instances.get(instanceId);\n\t\tconst childProcess = this.processes.get(instanceId);\n\n\t\tif (!instance || !childProcess) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const process of this.processes.values()) {\n\t\t\tprocess.kill(\"SIGKILL\");\n\t\t}\n\n\t\t// Clean up\n\t\tthis.instances.delete(instanceId);\n\t\tthis.processes.delete(instanceId);\n\n\t\t// Remove device mappings\n\t\tfor (const [udid, mapping] of this.deviceMappings.entries()) {\n\t\t\tif (mapping.instanceId === instanceId) {\n\t\t\t\tthis.deviceMappings.delete(udid);\n\t\t\t}\n\t\t}\n\n\t\tthis.emit(\"instance-stopped\", instance);\n\t}\n\n\t/**\n\t * Get device-to-port mapping for a specific UDID\n\t */\n\tpublic getDevicePort(udid: string): number | null {\n\t\tconst mapping = this.deviceMappings.get(udid);\n\t\treturn mapping ? mapping.port : null;\n\t}\n\n\t/**\n\t * Get all device mappings\n\t */\n\tpublic getDeviceMappings(): DeviceMapping[] {\n\t\treturn Array.from(this.deviceMappings.values());\n\t}\n\n\t/**\n\t * Get all running instances\n\t */\n\tpublic getInstances(): UsbmuxdInstance[] {\n\t\treturn Array.from(this.instances.values());\n\t}\n\n\t/**\n\t * Get manager statistics\n\t */\n\tpublic getStats(): ManagerStats {\n\t\tconst now = Date.now();\n\t\tconst startTime = this.startedAt?.getTime() || now;\n\t\tconst uptimeSeconds = Math.floor((now - startTime) / 1000);\n\n\t\treturn {\n\t\t\tinstanceCount: this.instances.size,\n\t\t\tdeviceCount: this.deviceMappings.size,\n\t\t\tuptimeSeconds,\n\t\t\tstartedAt: this.startedAt || new Date(),\n\t\t};\n\t}\n\n\t/**\n\t * Get current configuration\n\t */\n\tpublic getConfig(): InstanceManagerConfig {\n\t\treturn { ...this.config };\n\t}\n}\n", "import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type { DeviceInfo } from \"./types\";\nimport type { UsbipdDevice } from \"./types/usbipd.js\";\nimport type { Wsl2 } from \"./wsl.js\";\n\nconst { logInfo, logWarning } = createLoggers(\"usbipd\");\n\nconst execAsync = promisify(exec);\n\nexport class Usbipd {\n\tconstructor(private readonly usbipdPath: string) {}\n\n\tpublic async unbindAllDevicesFromWsl(): Promise<void> {\n\t\ttry {\n\t\t\tawait execAsync(`\"${this.usbipdPath}\" unbind -a`);\n\t\t\tlogInfo(\"All devices unbound from WSL\");\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to unbind all devices from WSL: ${error}`);\n\t\t}\n\t}\n\n\tpublic async detachAllDevicesFromWsl(): Promise<void> {\n\t\ttry {\n\t\t\tawait execAsync(`\"${this.usbipdPath}\" detach -a`);\n\t\t\tlogInfo(\"All devices detached from WSL\");\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to detach all devices from WSL: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Detach a device from WSL via usbipd\n\t */\n\tpublic async detachDeviceFromWsl(busId: string): Promise<void> {\n\t\ttry {\n\t\t\tawait execAsync(`\"${this.usbipdPath}\" detach --busid=${busId}`);\n\t\t\tlogInfo(`Device ${busId} detached from WSL`);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (message.includes(\"no device with busid\") || message.includes(\"There is no device\")) {\n\t\t\t\tlogInfo(`Device ${busId} already detached`);\n\t\t\t} else {\n\t\t\t\tlogWarning(`Failed to detach device ${busId}: ${error}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Attach a device to WSL via usbipd\n\t * Note: This requires administrator privileges\n\t */\n\tpublic async attachDeviceToWsl(busId: string, wsl: Wsl2, distro: string): Promise<boolean> {\n\t\ttry {\n\t\t\t// Ensure WSL is running first\n\t\t\tawait wsl.ensureWslRunning();\n\n\t\t\t// Bind the device (make it shareable)\n\t\t\tlogInfo(`Binding device ${busId}...`);\n\t\t\tawait execAsync(`\"${this.usbipdPath}\" bind --busid ${busId} --force`);\n\n\t\t\t// Attach to WSL (use specific distro if configured, otherwise let usbipd pick)\n\t\t\tif (distro) {\n\t\t\t\tlogInfo(`Attaching device ${busId} to WSL distribution ${distro}...`);\n\t\t\t\tawait execAsync(`\"${this.usbipdPath}\" attach --wsl=${distro} --busid=${busId}`);\n\t\t\t} else {\n\t\t\t\tlogInfo(`Attaching device ${busId} to default WSL...`);\n\t\t\t\tawait execAsync(`\"${this.usbipdPath}\" attach --wsl --busid=${busId}`);\n\t\t\t}\n\n\t\t\tlogInfo(`Device ${busId} attached to WSL successfully`);\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t// Device already attached (e.g. from a previous run) \u2014 treat as success so we still start usbmuxd\n\t\t\tif (message.includes(\"already attached to a client\")) {\n\t\t\t\tlogInfo(`Device ${busId} is already attached to WSL, continuing`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tlogWarning(`Failed to attach device ${busId} to WSL: ${error}`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Parse usbipd list output to extract device information\n\t */\n\tprivate parseUsbipdList(output: string): UsbipdDevice[] {\n\t\tconst devices: UsbipdDevice[] = [];\n\t\t// Handle both \\r\\n and \\n line endings\n\t\tconst lines = output.split(/\\r?\\n/);\n\n\t\tfor (const line of lines) {\n\t\t\t// Format: \"4-5 05ac:12a8 Apple Mobile Device USB Composite Device Shared (forced)\"\n\t\t\t// More flexible regex: busid, vid:pid, then anything else\n\t\t\tconst match = line.match(/^(\\d+-\\d+)\\s+([0-9a-f]{4}):([0-9a-f]{4})\\s+(.+)$/i);\n\t\t\tif (match) {\n\t\t\t\t// Split the rest into description and state (state is the last word(s) after multiple spaces)\n\t\t\t\tconst rest = match[4].trim();\n\t\t\t\tconst stateMatch = rest.match(/^(.+?)\\s{2,}(\\S.*)$/);\n\t\t\t\tconst description = stateMatch ? stateMatch[1].trim() : rest;\n\t\t\t\tconst state = stateMatch ? stateMatch[2].trim() : \"Unknown\";\n\n\t\t\t\tdevices.push({\n\t\t\t\t\tbusId: match[1],\n\t\t\t\t\tvid: match[2].toUpperCase(),\n\t\t\t\t\tpid: match[3].toUpperCase(),\n\t\t\t\t\tdescription,\n\t\t\t\t\tstate,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn devices;\n\t}\n\t/**\n\t * Find the usbipd bus ID for a device by matching VID/PID\n\t */\n\tpublic async findBusIdForDevice(device: DeviceInfo): Promise<string> {\n\t\ttry {\n\t\t\tconst { stdout } = await execAsync(`\"${this.usbipdPath}\" list`);\n\t\t\tconst usbipdDevices = this.parseUsbipdList(stdout);\n\n\t\t\t// Convert device VID/PID to hex string for comparison\n\t\t\tconst deviceVid = device.vid.toString(16).toUpperCase().padStart(4, \"0\");\n\t\t\tconst devicePid = device.pid.toString(16).toUpperCase().padStart(4, \"0\");\n\n\t\t\tlogInfo(`Looking for device with VID:PID ${deviceVid}:${devicePid}`);\n\t\t\tlogInfo(`Found ${usbipdDevices.length} devices from usbipd list`);\n\n\t\t\t// Find matching device by VID/PID\n\t\t\tconst match = usbipdDevices.find((d) => d.vid === deviceVid && d.pid === devicePid);\n\n\t\t\tif (match) {\n\t\t\t\tlogInfo(\n\t\t\t\t\t`Found usbipd bus ID ${match.busId} for device ${device.deviceId} (${deviceVid}:${devicePid})`\n\t\t\t\t);\n\t\t\t\treturn match.busId;\n\t\t\t}\n\n\t\t\tthrow new Error(\n\t\t\t\t`Could not find usbipd bus ID for device ${device.deviceId} (${deviceVid}:${devicePid})`\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Failed to run usbipd list: ${error}`);\n\t\t}\n\t}\n}\n", "import { type ChildProcess, exec, spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type { UsbmuxdInstance } from \"./types\";\n\nconst { logInfo, logWarning, logDetail } = createLoggers(\"wsl\");\n\nconst execAsync = promisify(exec);\n\nexport class Wsl2 {\n\tprivate readonly ALPINE_LOCKDOWN_DIR = \"/var/lib/lockdown\";\n\tprivate readonly SYSTEM_CONFIG_PLIST = \"SystemConfiguration.plist\";\n\n\tprivate wslIpAddress: string | null = null;\n\n\tconstructor(private wslDistribution: string | undefined) {}\n\n\t/**\n\t * Detect the WSL2 IP address for the configured distribution\n\t * This IP is needed to connect from Windows to services inside WSL\n\t */\n\tpublic async detectWslIpAddress(): Promise<string> {\n\t\tif (this.wslIpAddress) {\n\t\t\treturn this.wslIpAddress;\n\t\t}\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\n\t\ttry {\n\t\t\t// Try to get IP from eth0 interface\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- ip -4 addr show eth0`);\n\t\t\tconst match = stdout.match(/inet\\s+(\\d+\\.\\d+\\.\\d+\\.\\d+)/);\n\t\t\tif (match) {\n\t\t\t\tthis.wslIpAddress = match[1];\n\t\t\t\tlogInfo(`Detected WSL IP address: ${this.wslIpAddress}`);\n\t\t\t\treturn this.wslIpAddress;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to detect WSL IP via ip addr: ${error}`);\n\t\t}\n\n\t\t// Fallback: try hostname -I\n\t\ttry {\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- hostname -I`);\n\t\t\tconst ip = stdout.trim().split(/\\s+/)[0];\n\t\t\tif (ip && /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(ip)) {\n\t\t\t\tthis.wslIpAddress = ip;\n\t\t\t\tlogInfo(`Detected WSL IP address (hostname): ${this.wslIpAddress}`);\n\t\t\t\treturn this.wslIpAddress;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to detect WSL IP via hostname: ${error}`);\n\t\t}\n\n\t\t// Last resort fallback - localhost (may work with WSL2 mirrored networking)\n\t\tlogWarning(\"Could not detect WSL IP, falling back to localhost\");\n\t\tthis.wslIpAddress = \"127.0.0.1\";\n\t\treturn this.wslIpAddress;\n\t}\n\n\t/**\n\t * Ensure the WSL distribution is running\n\t */\n\tpublic async ensureWslRunning(): Promise<boolean> {\n\t\tconst distro = this.wslDistribution;\n\n\t\ttry {\n\t\t\tif (distro) {\n\t\t\t\t// Start specific distribution\n\t\t\t\tlogInfo(`Starting WSL distribution: ${distro}...`);\n\t\t\t\tawait execAsync(`wsl -d ${distro} -- echo \"WSL started\"`);\n\t\t\t} else {\n\t\t\t\t// Start default WSL\n\t\t\t\tlogInfo(\"Starting default WSL distribution...\");\n\t\t\t\tawait execAsync(`wsl -- echo \"WSL started\"`);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to start WSL: ${error}`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Create a new usbmuxd instance\n\t */\n\tpublic async createInstance({\n\t\tid,\n\t\tbasePort,\n\t\tverboseLogging,\n\t\tusbmuxdPath,\n\t\tonLog,\n\t\tonExit,\n\t}: {\n\t\tid: number;\n\t\tbasePort: number;\n\t\tverboseLogging: boolean;\n\t\tusbmuxdPath: string;\n\t\tonLog: (message: string) => void;\n\t\tonExit: (code: number, signal: string) => void;\n\t}): Promise<{ instance: UsbmuxdInstance; process: ChildProcess }> {\n\t\tconst port = basePort + id - 1;\n\n\t\tlogInfo(`Creating instance ${id} on port ${port}`);\n\n\t\t// Detect WSL IP for connecting from Windows\n\t\tconst host = await this.detectWslIpAddress();\n\n\t\t// Build usbmuxd arguments\n\t\tconst usbmuxdArgs = [\n\t\t\t\"-f\", // Foreground\n\t\t\t\"-v\", // Verbose (if enabled)\n\t\t\t\"-S\",\n\t\t\t`0.0.0.0:${port}`, // Listen on all interfaces (for Windows \u2192 WSL2)\n\t\t\t\"--pidfile\",\n\t\t\t\"NONE\",\n\t\t];\n\n\t\tif (!verboseLogging) {\n\t\t\t// Remove -v if not verbose\n\t\t\tusbmuxdArgs.splice(1, 1);\n\t\t}\n\n\t\t// Build WSL command: wsl -d <distro> usbmuxd <args>\n\t\tconst wslArgs = [\n\t\t\t\"-d\",\n\t\t\tthis.wslDistribution || \"alpine-usbmuxd-build\",\n\t\t\tusbmuxdPath,\n\t\t\t...usbmuxdArgs,\n\t\t];\n\n\t\t// Spawn via WSL\n\t\t// detached: true creates a new process group so the child does NOT receive\n\t\t// the parent's Ctrl+C. Without this, Windows delivers SIGINT to every process\n\t\t// in the console, killing usbmuxd before stop() can sync lockdown files back.\n\t\t// windowsHide: true prevents the detached process from opening a visible console window.\n\t\tconst process = spawn(\"wsl\", wslArgs, {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tdetached: true,\n\t\t\twindowsHide: false,\n\t\t});\n\n\t\t// Handle process output\n\t\tprocess.stdout?.on(\"data\", (data) => {\n\t\t\tonLog(data.toString().trim());\n\t\t});\n\n\t\tprocess.stderr?.on(\"data\", (data) => {\n\t\t\tonLog(data.toString().trim());\n\t\t});\n\n\t\t// Handle process exit\n\t\tprocess.on(\"exit\", onExit);\n\n\t\t// Create instance record (pid is set synchronously by Node after spawn)\n\t\tconst pid = process.pid;\n\t\tif (pid === undefined) {\n\t\t\tprocess.kill(\"SIGKILL\");\n\t\t\tthrow new Error(\"Failed to get PID for usbmuxd instance\");\n\t\t}\n\t\tconst instance: UsbmuxdInstance = {\n\t\t\tid,\n\t\t\thost,\n\t\t\tport,\n\t\t\tpid,\n\t\t\tdeviceUdids: [],\n\t\t\tstartedAt: new Date(),\n\t\t};\n\n\t\treturn { instance, process };\n\t}\n\n\t/**\n\t * Convert a Windows path to the equivalent path inside WSL (e.g. C:\\foo\\bar -> /mnt/c/foo/bar).\n\t */\n\tprivate windowsPathToWsl(windowsPath: string): string {\n\t\tconst normalized = windowsPath.replace(/\\\\/g, \"/\").trim();\n\t\tconst driveMatch = normalized.match(/^([a-zA-Z]):\\/?(.*)$/);\n\t\tif (driveMatch) {\n\t\t\tconst drive = driveMatch[1].toLowerCase();\n\t\t\tconst rest = driveMatch[2] || \"\";\n\t\t\treturn `/mnt/${drive}${rest ? `/${rest}` : \"\"}`;\n\t\t}\n\t\treturn normalized;\n\t}\n\n\t/**\n\t * Get the native Apple lockdown directory path for the current platform.\n\t * This is where Apple/iTunes stores pairing records natively.\n\t */\n\tprivate getAppleLockdownPath(): string | null {\n\t\tif (process.platform === \"win32\") {\n\t\t\treturn join(process.env.ProgramData ?? \"C:\\\\ProgramData\", \"Apple\", \"Lockdown\");\n\t\t}\n\t\tif (process.platform === \"darwin\") {\n\t\t\treturn \"/var/db/lockdown\";\n\t\t}\n\t\tif (process.platform === \"linux\") {\n\t\t\treturn \"/var/lib/lockdown\";\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Copy a single device's lockdown plist from the native Apple lockdown directory\n\t * to Alpine so usbmuxd can use it (skip pairing).\n\t *\n\t * Uses the platform-specific Apple/iTunes lockdown directory\n\t * (e.g. C:\\ProgramData\\Apple\\Lockdown on Windows, /var/db/lockdown on macOS).\n\t *\n\t * No-op if the lockdown directory cannot be determined or files don't exist.\n\t */\n\tpublic async syncToAlpine(udid: string): Promise<void> {\n\t\tconst lockdownDir = this.getAppleLockdownPath();\n\t\tif (!lockdownDir) {\n\t\t\tlogWarning(\"Lockdown sync to Alpine: unsupported platform, skipping\");\n\t\t\treturn;\n\t\t}\n\n\t\tlogDetail(`Lockdown sync to Alpine: source dir = ${lockdownDir}, udid = ${udid}`);\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\t\tconst wslDestDir = this.ALPINE_LOCKDOWN_DIR;\n\n\t\t// Sync the device-specific plist\n\t\tconst devicePlistFile = `${udid}.plist`;\n\t\tconst devicePlistPath = join(lockdownDir, devicePlistFile);\n\t\tif (existsSync(devicePlistPath)) {\n\t\t\tawait this.copyFileToAlpine(\n\t\t\t\tthis.windowsPathToWsl(devicePlistPath),\n\t\t\t\twslDestDir,\n\t\t\t\tdistro,\n\t\t\t\tdevicePlistFile\n\t\t\t);\n\t\t} else {\n\t\t\tlogDetail(\n\t\t\t\t`Lockdown sync to Alpine: ${devicePlistFile} not found at ${devicePlistPath}, skipping`\n\t\t\t);\n\t\t}\n\n\t\t// Sync SystemConfiguration.plist so Alpine usbmuxd uses the same SystemBUID\n\t\t// that was used during the original pairing on Windows.\n\t\t// Without this, lockdown sessions fail with InvalidHostID.\n\t\tconst systemConfigPath = join(lockdownDir, this.SYSTEM_CONFIG_PLIST);\n\t\tif (existsSync(systemConfigPath)) {\n\t\t\tawait this.copyFileToAlpine(\n\t\t\t\tthis.windowsPathToWsl(systemConfigPath),\n\t\t\t\twslDestDir,\n\t\t\t\tdistro,\n\t\t\t\tthis.SYSTEM_CONFIG_PLIST\n\t\t\t);\n\t\t} else {\n\t\t\tlogDetail(\n\t\t\t\t`Lockdown sync to Alpine: ${this.SYSTEM_CONFIG_PLIST} not found at ${systemConfigPath}, skipping`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Copy a single file into the Alpine lockdown directory.\n\t * Falls back to sudo if the initial copy fails.\n\t */\n\tprivate async copyFileToAlpine(\n\t\twslSource: string,\n\t\twslDestDir: string,\n\t\tdistro: string,\n\t\tfileName: string\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait execAsync(\n\t\t\t\t`wsl -d ${distro} -- sh -c \"mkdir -p ${wslDestDir} && cp '${wslSource}' '${wslDestDir}/'\"`\n\t\t\t);\n\t\t\tlogInfo(`Lockdown sync: copied ${fileName} to Alpine`);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait execAsync(\n\t\t\t\t\t`wsl -d ${distro} -- sh -c \"mkdir -p ${wslDestDir} && sudo cp '${wslSource}' '${wslDestDir}/'\"`\n\t\t\t\t);\n\t\t\t\tlogInfo(`Lockdown sync: copied ${fileName} to Alpine (via sudo)`);\n\t\t\t} catch (sudoError) {\n\t\t\t\tlogWarning(\n\t\t\t\t\t`Lockdown sync to Alpine failed for ${fileName}: ${error}. Sudo fallback failed: ${sudoError}. Ensure /var/lib/lockdown is writable or use passwordless sudo.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Copy lockdown plists from Alpine back to the native Apple lockdown directory\n\t * (for devices that were assigned this session).\n\t * Also copies SystemConfiguration.plist if present in Alpine.\n\t *\n\t * No-op if the lockdown directory cannot be determined.\n\t */\n\tpublic async syncFromAlpine(udids: string[]): Promise<void> {\n\t\tlogInfo(`Lockdown sync from Alpine: starting (${udids.length} device(s))`);\n\n\t\tconst lockdownDir = this.getAppleLockdownPath();\n\t\tif (!lockdownDir) {\n\t\t\tlogWarning(\"Lockdown sync: unsupported platform, skipping\");\n\t\t\treturn;\n\t\t}\n\n\t\tlogDetail(`Lockdown sync: target dir = ${lockdownDir}`);\n\n\t\ttry {\n\t\t\tmkdirSync(lockdownDir, { recursive: true });\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: could not create lockdown dir ${lockdownDir}: ${error}`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\t\tconst wslDestDir = this.windowsPathToWsl(lockdownDir);\n\n\t\t// List what Alpine actually has in its lockdown dir for debugging\n\t\ttry {\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- ls -la ${this.ALPINE_LOCKDOWN_DIR}/`);\n\t\t\tlogDetail(`Lockdown sync: Alpine ${this.ALPINE_LOCKDOWN_DIR} contents:\\n${stdout}`);\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: could not list Alpine lockdown dir: ${error}`);\n\t\t}\n\n\t\tconst filesToSync = [...udids.map((udid) => `${udid}.plist`), this.SYSTEM_CONFIG_PLIST];\n\n\t\tlogDetail(`Lockdown sync: files to sync = [${filesToSync.join(\", \")}]`);\n\n\t\tfor (const fileName of filesToSync) {\n\t\t\tawait this.copyFileFromAlpine(fileName, wslDestDir, distro, lockdownDir);\n\t\t}\n\n\t\tlogInfo(\"Lockdown sync from Alpine: done\");\n\t}\n\n\t/**\n\t * Copy a single file from the Alpine lockdown directory back to Windows.\n\t * Skips if the file does not exist in Alpine; logs a warning if the copy fails.\n\t */\n\tprivate async copyFileFromAlpine(\n\t\tfileName: string,\n\t\twslDestDir: string,\n\t\tdistro: string,\n\t\tlockdownDir: string\n\t): Promise<void> {\n\t\tconst alpinePath = `${this.ALPINE_LOCKDOWN_DIR}/${fileName}`;\n\n\t\t// Check if the file exists in Alpine first\n\t\ttry {\n\t\t\tawait execAsync(`wsl -d ${distro} -- test -f '${alpinePath}'`);\n\t\t} catch {\n\t\t\tlogDetail(`Lockdown sync: ${fileName} not found in Alpine (${alpinePath}), skipping`);\n\t\t\treturn;\n\t\t}\n\n\t\t// File exists \u2014 copy it to Windows\n\t\tlogDetail(`Lockdown sync: copying ${fileName} \u2192 ${wslDestDir}/`);\n\t\ttry {\n\t\t\tawait execAsync(`wsl -d ${distro} -- cp -f '${alpinePath}' '${wslDestDir}/'`);\n\t\t\tlogInfo(`Lockdown sync: copied ${fileName} from Alpine to ${lockdownDir}`);\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: failed to copy ${fileName} from Alpine to ${lockdownDir}: ${error}`);\n\t\t}\n\t}\n}\n"],
5
- "mappings": "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",
6
- "names": ["import_node_events", "import_tool_debug_g4", "import_tool_debug_g4", "import_node_child_process", "import_node_util", "import_tool_debug_g4", "logInfo", "logWarning", "execAsync", "process", "logWarning", "logDetail", "process", "logInfo", "logDataObject", "UsbDeviceListener"]
4
+ "sourcesContent": ["export { UsbmuxdService } from \"./UsbmuxdService.js\";\nexport { InstanceManager } from \"./InstanceManager.js\";\nexport type {\n\tInstanceManagerConfig,\n\tUsbmuxdInstance,\n\tDeviceInfo,\n\tDeviceMapping,\n\tManagerStats,\n} from \"./types/index.js\";\n", "import { EventEmitter } from \"node:events\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type { DeviceInfo } from \"@mcesystems/usb-device-listener\";\nimport { InstanceManager } from \"./InstanceManager.js\";\nimport type { DeviceMapping, InstanceManagerConfig, UsbmuxdInstance } from \"./types/index.js\";\n\nconst { logInfo, logError, logDataObject } = createLoggers(\"usbmuxd-service\");\n\n/**\n * Main service that integrates usb-device-listener with InstanceManager\n * Monitors iOS devices and manages usbmuxd instances automatically.\n * Extends EventEmitter: forwards instance-started, instance-stopped, device-assigned,\n * device-removed, instance-log, instance-exited from the underlying manager.\n */\nexport class UsbmuxdService extends EventEmitter {\n\tprivate manager: InstanceManager;\n\tprivate isListening = false;\n\n\tconstructor(config: Partial<InstanceManagerConfig> = {}) {\n\t\tsuper();\n\t\tthis.manager = new InstanceManager(config);\n\t\tthis.setupEventHandlers();\n\t}\n\n\t/**\n\t * Forward manager events to service subscribers (no logging; CLI/examples handle logging).\n\t */\n\tprivate setupEventHandlers(): void {\n\t\tthis.manager.on(\"instance-started\", (instance: UsbmuxdInstance) => {\n\t\t\tthis.emit(\"instance-started\", instance);\n\t\t});\n\n\t\tthis.manager.on(\"instance-stopped\", (instance: UsbmuxdInstance) => {\n\t\t\tthis.emit(\"instance-stopped\", instance);\n\t\t});\n\n\t\tthis.manager.on(\n\t\t\t\"device-assigned\",\n\t\t\t(payload: { device: DeviceInfo; instance: UsbmuxdInstance; mapping: DeviceMapping; }) => {\n\t\t\t\tlogInfo(\n\t\t\t\t\t`Device ${payload.device.deviceId} connected to usbmuxd instance at ${payload.mapping.host}:${payload.mapping.port}`\n\t\t\t\t);\n\t\t\t\tthis.emit(\"device-assigned\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"device-removed\",\n\t\t\t(payload: { device: DeviceInfo; instance: UsbmuxdInstance; }) => {\n\t\t\t\tthis.emit(\"device-removed\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"instance-log\",\n\t\t\t(payload: { instanceId: number; level: string; message: string; }) => {\n\t\t\t\tthis.emit(\"instance-log\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"instance-exited\",\n\t\t\t(payload: { instanceId: number; code: number | null; signal: string | null; }) => {\n\t\t\t\tthis.emit(\"instance-exited\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\"device-paired\", (payload: { udid: string; mapping: DeviceMapping; }) => {\n\t\t\tthis.emit(\"device-paired\", payload);\n\t\t});\n\t}\n\n\t/**\n\t * Start the service\n\t * Begins monitoring for iOS devices\n\t */\n\tpublic start() {\n\t\tif (this.isListening) {\n\t\t\tthrow new Error(\"Service is already running\");\n\t\t}\n\n\t\tconst config = this.manager.getConfig();\n\n\t\tlogDataObject(\"Starting service...\", { config });\n\n\t\t// Start listening for USB events\n\t\ttry {\n\n\t\t\tthis.isListening = true;\n\t\t\tthis.manager.start();\n\n\t\t\tlogInfo(\"Service started successfully\");\n\t\t\tlogInfo(\"Waiting for iOS devices...\");\n\t\t\tconst onAdd = this.manager.onDeviceConnected.bind(this.manager);\n\t\t\tconst onRemove = this.manager.onDeviceDisconnected.bind(this.manager);\n\t\t\treturn {\n\t\t\t\tonAdd,\n\t\t\t\tonRemove,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tlogError(\"Failed to start USB listener:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Stop the service\n\t * Stops monitoring and terminates all instances\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tif (!this.isListening) {\n\t\t\treturn;\n\t\t}\n\n\t\tlogInfo(\"Stopping service...\");\n\n\t\tthis.isListening = false;\n\n\t\t// Stop all instances\n\t\tawait this.manager.stop();\n\n\t\tlogInfo(\"Service stopped\");\n\t}\n\n\t/**\n\t * Get device port mapping\n\t */\n\tpublic getDevicePort(udid: string): number | null {\n\t\treturn this.manager.getDevicePort(udid);\n\t}\n\n\t/**\n\t * Get all device mappings\n\t */\n\tpublic getDeviceMappings() {\n\t\treturn this.manager.getDeviceMappings();\n\t}\n\n\t/**\n\t * Get all running instances\n\t */\n\tpublic getInstances() {\n\t\treturn this.manager.getInstances();\n\t}\n\n\t/**\n\t * Get service statistics\n\t */\n\tpublic getStats() {\n\t\treturn this.manager.getStats();\n\t}\n\n\t/**\n\t * Get current configuration\n\t */\n\tpublic getConfig() {\n\t\treturn this.manager.getConfig();\n\t}\n}\n", "import type { ChildProcess } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type {\n\tDeviceInfo,\n\tDeviceMapping,\n\tInstanceManagerConfig,\n\tManagerStats,\n\tUsbmuxdInstance,\n} from \"./types/index.js\";\nimport { Usbipd } from \"./usbipd.js\";\nimport { Wsl2 } from \"./wsl.js\";\n\nconst { logWarning, logDetail, logDataObject, logTask } = createLoggers(\"instance-manager\");\n\n/**\n * Default configuration for the instance manager\n */\nconst DEFAULT_CONFIG: InstanceManagerConfig = {\n\tbatchSize: 4,\n\tbasePort: 27015,\n\tmaxInstances: 5,\n\tusbmuxdPath: \"usbmuxd\", // Path inside WSL2\n\twslDistribution: \"alpine-usbmuxd-build\", // Alpine WSL2 distribution name\n\tverboseLogging: true,\n\tappleVendorId: \"05AC\",\n};\n\n/**\n * Manages multiple usbmuxd instances with batch device allocation\n *\n * @example\n * const manager = new InstanceManager({\n * batchSize: 4,\n * basePort: 27015,\n * });\n *\n * manager.on('instance-started', (instance) => {\n * logInfo(`Instance ${instance.id} started on port ${instance.port}`);\n * });\n *\n * manager.start();\n */\nexport class InstanceManager extends EventEmitter {\n\t/** Map of usbmuxd instance IDs to instances */\n\tprivate instances: Map<number, UsbmuxdInstance> = new Map();\n\t/** Map of device IDs to device mappings host:port */\n\tprivate deviceMappings: Map<string, DeviceMapping> = new Map();\n\t/** Map of usbmuxd instance IDs to child processes running it on wsl2*/\n\tprivate processes: Map<number, ChildProcess> = new Map();\n\t/** Tracks which devices have been attached to WSL */\n\tprivate attachedDevices: Set<string> = new Set();\n\t/** Device IDs currently in the attach flow\n\t * (ignore disconnect until attach completes) */\n\tprivate pendingAttachDevices: Set<string> = new Set();\n\n\tprivate config: InstanceManagerConfig;\n\tprivate nextInstanceId = 1;\n\tprivate startedAt: Date | null = null;\n\tprivate isRunning = false;\n\n\tprivate wsl: Wsl2;\n\tprivate usbipd: Usbipd;\n\tconstructor(config: Partial<InstanceManagerConfig> = {}) {\n\t\tsuper();\n\t\tthis.config = { ...DEFAULT_CONFIG, ...config };\n\t\tthis.wsl = new Wsl2(this.config.wslDistribution);\n\t\tthis.usbipd = new Usbipd(process.env.USBIPD_PATH ?? \"usbipd\");\n\t}\n\n\t/**\n\t * Start the instance manager\n\t */\n\tpublic start(): void {\n\t\tif (this.isRunning) {\n\t\t\tthrow new Error(\"Instance manager is already running\");\n\t\t}\n\n\t\tthis.isRunning = true;\n\t\tthis.startedAt = new Date();\n\t}\n\n\t/**\n\t * Stop the instance manager and all instances\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tif (!this.isRunning) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.isRunning = false;\n\n\t\t// Sync lockdown files from Alpine back to native Apple lockdown directory (before clearing mappings)\n\t\tconst udids = Array.from(this.deviceMappings.keys());\n\t\tawait this.wsl.syncFromAlpine(udids);\n\n\t\t// Detach all devices from WSL.\n\t\t// If you only called unbind without detach,\n\t\t// the device might still be attached to the WSL session\n\t\tawait this.usbipd.detachAllDevicesFromWsl();\n\t\tawait this.usbipd.unbindAllDevicesFromWsl();\n\t\tthis.attachedDevices.clear();\n\n\t\t// Stop all instances\n\t\tconst stopPromises = Array.from(this.instances.keys()).map((id) => this.stopInstance(id));\n\n\t\tawait Promise.all(stopPromises);\n\n\t\tthis.instances.clear();\n\t\tthis.deviceMappings.clear();\n\t\tthis.processes.clear();\n\t}\n\n\tprivate async attachToWsl({\n\t\tbusId,\n\t\tdeviceId,\n\t}: { busId: string; deviceId: string }): Promise<void> {\n\t\tif (!this.attachedDevices.has(busId)) {\n\t\t\tthis.pendingAttachDevices.add(deviceId);\n\t\t\tif (!this.config.wslDistribution) {\n\t\t\t\tthrow new Error(\"WSL distribution not configured\");\n\t\t\t}\n\t\t\tconst attached = await this.usbipd.attachDeviceToWsl(\n\t\t\t\tbusId,\n\t\t\t\tthis.wsl,\n\t\t\t\tthis.config.wslDistribution\n\t\t\t);\n\t\t\tif (attached) {\n\t\t\t\tthis.attachedDevices.add(busId);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async removeDevice(mapping: DeviceMapping): Promise<UsbmuxdInstance> {\n\t\tconst instance = await this.detachFromWsl({ deviceId: mapping.udid });\n\t\tif (!instance) {\n\t\t\tthrow new Error(`Instance ${mapping.instanceId} not found`);\n\t\t}\n\t\tif (mapping.busId) {\n\t\t\tawait this.usbipd.detachDeviceFromWsl(mapping.busId);\n\t\t\tthis.attachedDevices.delete(mapping.busId);\n\t\t}\n\t\tconst deviceIndex = instance.deviceUdids.indexOf(mapping.udid);\n\t\tif (deviceIndex > -1) {\n\t\t\tinstance.deviceUdids.splice(deviceIndex, 1);\n\t\t}\n\n\t\tthis.deviceMappings.delete(mapping.udid);\n\t\treturn instance;\n\t}\n\n\tprivate async onUsbmuxdInstanceEnd({\n\t\tinstanceId,\n\t\tcode,\n\t\tsignal,\n\t}: { instanceId: number; code: number; signal: string }): Promise<void> {\n\t\tthis.emit(\"instance-exited\", {\n\t\t\tinstanceId,\n\t\t\tcode,\n\t\t\tsignal,\n\t\t});\n\t\tfor (const [_, mapping] of this.deviceMappings.entries()) {\n\t\t\tif (mapping.instanceId === instanceId) {\n\t\t\t\tthis.removeDevice(mapping);\n\t\t\t}\n\t\t}\n\t\tthis.instances.delete(instanceId);\n\t\tthis.processes.get(instanceId)?.kill();\n\t\tthis.processes.delete(instanceId);\n\t}\n\n\tprivate async onUsbmuxdInstanceStart({\n\t\tinstance,\n\t\tprocess,\n\t}: { instance: UsbmuxdInstance; process: ChildProcess }): Promise<void> {\n\t\tthis.instances.set(instance.id, instance);\n\t\tthis.processes.set(instance.id, process);\n\t\tthis.emit(\"instance-started\", {\n\t\t\tinstanceId: instance.id,\n\t\t});\n\t}\n\n\tprivate async getInstance() {\n\t\tlet targetInstance = this.findInstanceWithCapacity();\n\n\t\t// If no instance has capacity, create a new one\n\t\tif (!targetInstance) {\n\t\t\tif (this.instances.size >= this.config.maxInstances) {\n\t\t\t\tthrow new Error(`Maximum number of instances (${this.config.maxInstances}) reached`);\n\t\t\t}\n\n\t\t\tconst newInstanceId = this.nextInstanceId++;\n\n\t\t\tlet onExit = (code: number, signal: string) => {\n\t\t\t\tthis.onUsbmuxdInstanceEnd({ instanceId: newInstanceId, code, signal });\n\t\t\t};\n\n\t\t\tonExit = onExit.bind(this);\n\n\t\t\tconst { instance, process } = await this.wsl.createInstance({\n\t\t\t\tid: newInstanceId,\n\t\t\t\tbasePort: this.config.basePort,\n\t\t\t\tverboseLogging: this.config.verboseLogging,\n\t\t\t\tusbmuxdPath: this.config.usbmuxdPath,\n\t\t\t\tonLog: (message) =>\n\t\t\t\t\tthis.emit(\"instance-log\", {\n\t\t\t\t\t\tinstanceId: newInstanceId,\n\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t}),\n\t\t\t\tonExit,\n\t\t\t});\n\t\t\tthis.onUsbmuxdInstanceStart({ instance, process });\n\t\t\ttargetInstance = instance;\n\t\t}\n\t\treturn targetInstance;\n\t}\n\n\t/**\n\t * Handle device connection only for not attached devices\n\t * Attaches device to WSL, then assigns to an existing instance or creates a new one\n\t */\n\tpublic async onDeviceConnected(device: DeviceInfo): Promise<void> {\n\t\tconst busId = await this.usbipd.findBusIdForDevice(device);\n\n\t\tif (this.attachedDevices.has(busId)) {\n\t\t\tlogTask(`Device ${device.deviceId} is already attached. skipping onDeviceConnected...`);\n\t\t\treturn;\n\t\t}\n\n\t\tlogDataObject(\"Device connected\", { device });\n\t\tawait this.wsl.syncToAlpine(device.deviceId);\n\n\t\tawait this.attachToWsl({ busId, deviceId: device.deviceId });\n\n\t\tconst targetInstance = await this.getInstance();\n\n\t\t// Create mapping (include host and busId for cleanup on disconnect)\n\t\tconst mapping: DeviceMapping = {\n\t\t\tudid: device.deviceId,\n\t\t\tinstanceId: targetInstance.id,\n\t\t\thost: targetInstance.host,\n\t\t\tport: targetInstance.port,\n\t\t\taddedAt: new Date(),\n\t\t\tbusId: busId ?? undefined,\n\t\t};\n\n\t\tthis.deviceMappings.set(device.deviceId, mapping);\n\n\t\t// Add device to instance\n\t\ttargetInstance.deviceUdids.push(device.deviceId);\n\n\t\tthis.emit(\"device-assigned\", {\n\t\t\tdevice,\n\t\t\tinstance: targetInstance,\n\t\t\tmapping,\n\t\t});\n\n\t\tthis.pendingAttachDevices.delete(device.deviceId);\n\t}\n\n\tprivate async detachFromWsl({ deviceId }: { deviceId: string }): Promise<UsbmuxdInstance> {\n\t\tconst mapping = this.deviceMappings.get(deviceId);\n\t\tif (!mapping) {\n\t\t\tthrow new Error(`Device ${deviceId} was not tracked`);\n\t\t}\n\n\t\t// Detach device from WSL if it was attached\n\t\tif (mapping.busId) {\n\t\t\tawait this.usbipd.detachDeviceFromWsl(mapping.busId);\n\t\t\tthis.attachedDevices.delete(mapping.busId);\n\t\t}\n\n\t\tconst instance = this.instances.get(mapping.instanceId);\n\t\tif (!instance) {\n\t\t\tlogWarning(`Instance ${mapping.instanceId} not found`);\n\t\t\tthis.deviceMappings.delete(deviceId);\n\t\t\tthrow new Error(`Instance ${mapping.instanceId} not found`);\n\t\t}\n\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Handle device disconnection only for attached devices\n\t * Detaches from WSL, removes device from instance, and stops instance if empty\n\t */\n\tpublic async onDeviceDisconnected(device: DeviceInfo): Promise<void> {\n\t\t// Ignore disconnect while we're still attaching this device (Windows sees disconnect when device moves to WSL)\n\t\tif (!this.attachedDevices.has(device.deviceId)) {\n\t\t\tlogTask(`Device ${device.deviceId} is not attached. skipping onDeviceDisconnected...`);\n\t\t\treturn;\n\t\t}\n\t\tlogDataObject(\"Device disconnected\", { device });\n\n\t\tconst mapping = this.deviceMappings.get(device.deviceId);\n\t\tif (!mapping) {\n\t\t\tthrow new Error(`Device ${device.deviceId} was not tracked`);\n\t\t}\n\t\t// Remove device from instance\n\t\tconst instance = await this.removeDevice(mapping);\n\n\t\tthis.emit(\"device-removed\", {\n\t\t\tdevice,\n\t\t\tinstance,\n\t\t});\n\n\t\t// If instance is now empty, stop it\n\t\tif (instance.deviceUdids.length === 0) {\n\t\t\tlogDetail(`Instance ${instance.id} is empty, stopping...`);\n\t\t\tawait this.stopInstance(instance.id);\n\t\t}\n\t}\n\n\t/**\n\t * Find an instance with available capacity\n\t */\n\tprivate findInstanceWithCapacity(): UsbmuxdInstance | null {\n\t\tfor (const instance of this.instances.values()) {\n\t\t\tif (instance.deviceUdids.length < this.config.batchSize) {\n\t\t\t\treturn instance;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Stop a specific instance\n\t */\n\tprivate async stopInstance(instanceId: number): Promise<void> {\n\t\tconst instance = this.instances.get(instanceId);\n\t\tconst childProcess = this.processes.get(instanceId);\n\n\t\tif (!instance || !childProcess) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const process of this.processes.values()) {\n\t\t\tprocess.kill(\"SIGKILL\");\n\t\t}\n\n\t\t// Clean up\n\t\tthis.instances.delete(instanceId);\n\t\tthis.processes.delete(instanceId);\n\n\t\t// Remove device mappings\n\t\tfor (const [udid, mapping] of this.deviceMappings.entries()) {\n\t\t\tif (mapping.instanceId === instanceId) {\n\t\t\t\tthis.deviceMappings.delete(udid);\n\t\t\t}\n\t\t}\n\n\t\tthis.emit(\"instance-stopped\", instance);\n\t}\n\n\t/**\n\t * Get device-to-port mapping for a specific UDID\n\t */\n\tpublic getDevicePort(udid: string): number | null {\n\t\tconst mapping = this.deviceMappings.get(udid);\n\t\treturn mapping ? mapping.port : null;\n\t}\n\n\t/**\n\t * Get all device mappings\n\t */\n\tpublic getDeviceMappings(): DeviceMapping[] {\n\t\treturn Array.from(this.deviceMappings.values());\n\t}\n\n\t/**\n\t * Get all running instances\n\t */\n\tpublic getInstances(): UsbmuxdInstance[] {\n\t\treturn Array.from(this.instances.values());\n\t}\n\n\t/**\n\t * Get manager statistics\n\t */\n\tpublic getStats(): ManagerStats {\n\t\tconst now = Date.now();\n\t\tconst startTime = this.startedAt?.getTime() || now;\n\t\tconst uptimeSeconds = Math.floor((now - startTime) / 1000);\n\n\t\treturn {\n\t\t\tinstanceCount: this.instances.size,\n\t\t\tdeviceCount: this.deviceMappings.size,\n\t\t\tuptimeSeconds,\n\t\t\tstartedAt: this.startedAt || new Date(),\n\t\t};\n\t}\n\n\t/**\n\t * Get current configuration\n\t */\n\tpublic getConfig(): InstanceManagerConfig {\n\t\treturn { ...this.config };\n\t}\n}\n", "import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type { DeviceInfo } from \"./types\";\nimport type { UsbipdDevice } from \"./types/usbipd.js\";\nimport type { Wsl2 } from \"./wsl.js\";\n\nconst { logInfo, logWarning } = createLoggers(\"usbipd\");\n\nconst execAsync = promisify(exec);\n\nexport class Usbipd {\n constructor(private readonly usbipdPath: string) { }\n\n public async unbindAllDevicesFromWsl(): Promise<void> {\n try {\n await execAsync(`\"${this.usbipdPath}\" unbind -a`);\n logInfo(\"All devices unbound from WSL\");\n } catch (error) {\n logWarning(`Failed to unbind all devices from WSL: ${error}`);\n }\n }\n\n public async detachAllDevicesFromWsl(): Promise<void> {\n try {\n await execAsync(`\"${this.usbipdPath}\" detach -a`);\n logInfo(\"All devices detached from WSL\");\n } catch (error) {\n logWarning(`Failed to detach all devices from WSL: ${error}`);\n }\n }\n\n /**\n * Detach a device from WSL via usbipd\n */\n public async detachDeviceFromWsl(busId: string): Promise<void> {\n try {\n await execAsync(`\"${this.usbipdPath}\" detach --busid=${busId}`);\n logInfo(`Device ${busId} detached from WSL`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"no device with busid\") || message.includes(\"There is no device\")) {\n logInfo(`Device ${busId} already detached`);\n } else {\n logWarning(`Failed to detach device ${busId}: ${error}`);\n }\n }\n }\n\n /**\n * Attach a device to WSL via usbipd\n * Note: This requires administrator privileges\n */\n public async attachDeviceToWsl(busId: string, wsl: Wsl2, distro: string): Promise<boolean> {\n try {\n // Ensure WSL is running first\n await wsl.ensureWslRunning();\n\n // Bind the device (make it shareable)\n logInfo(`Binding device ${busId}...`);\n await execAsync(`\"${this.usbipdPath}\" bind --busid ${busId} --force`);\n\n // Attach to WSL (use specific distro if configured, otherwise let usbipd pick)\n if (distro) {\n logInfo(`Attaching device ${busId} to WSL distribution ${distro}...`);\n await execAsync(`\"${this.usbipdPath}\" attach --wsl=${distro} --busid=${busId}`);\n } else {\n logInfo(`Attaching device ${busId} to default WSL...`);\n await execAsync(`\"${this.usbipdPath}\" attach --wsl --busid=${busId}`);\n }\n\n logInfo(`Device ${busId} attached to WSL successfully`);\n return true;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n // Device already attached (e.g. from a previous run) \u2014 treat as success so we still start usbmuxd\n if (message.includes(\"already attached to a client\")) {\n logInfo(`Device ${busId} is already attached to WSL, continuing`);\n return true;\n }\n logWarning(`Failed to attach device ${busId} to WSL: ${error}`);\n return false;\n }\n }\n\n /**\n * Parse usbipd list output to extract device information\n */\n private parseUsbipdList(output: string): UsbipdDevice[] {\n const devices: UsbipdDevice[] = [];\n // Handle both \\r\\n and \\n line endings\n const lines = output.split(/\\r?\\n/);\n\n for (const line of lines) {\n // Format: \"4-5 05ac:12a8 Apple Mobile Device USB Composite Device Shared (forced)\"\n // More flexible regex: busid, vid:pid, then anything else\n const match = line.match(/^(\\d+-\\d+)\\s+([0-9a-f]{4}):([0-9a-f]{4})\\s+(.+)$/i);\n if (match) {\n // Split the rest into description and state (state is the last word(s) after multiple spaces)\n const rest = match[4].trim();\n const stateMatch = rest.match(/^(.+?)\\s{2,}(\\S.*)$/);\n const description = stateMatch ? stateMatch[1].trim() : rest;\n const state = stateMatch ? stateMatch[2].trim() : \"Unknown\";\n\n devices.push({\n busId: match[1],\n vid: match[2].toUpperCase(),\n pid: match[3].toUpperCase(),\n description,\n state,\n });\n }\n }\n\n return devices;\n }\n /**\n * Find the usbipd bus ID for a device by matching VID/PID\n */\n public async findBusIdForDevice(device: DeviceInfo): Promise<string> {\n try {\n const { stdout } = await execAsync(`\"${this.usbipdPath}\" list`);\n const usbipdDevices = this.parseUsbipdList(stdout);\n\n // Convert device VID/PID to hex string for comparison\n const deviceVid = device.vid.toString(16).toUpperCase().padStart(4, \"0\");\n const devicePid = device.pid.toString(16).toUpperCase().padStart(4, \"0\");\n\n logInfo(`Looking for device with VID:PID ${deviceVid}:${devicePid}`);\n logInfo(`Found ${usbipdDevices.length} devices from usbipd list`);\n\n // Find matching device by VID/PID\n const match = usbipdDevices.find((d) => d.vid === deviceVid && d.pid === devicePid);\n\n if (match) {\n logInfo(\n `Found usbipd bus ID ${match.busId} for device ${device.deviceId} (${deviceVid}:${devicePid})`\n );\n return match.busId;\n }\n\n throw new Error(\n `Could not find usbipd bus ID for device ${device.deviceId} (${deviceVid}:${devicePid})`\n );\n } catch (error) {\n throw new Error(`Failed to run usbipd list: ${error}`);\n }\n }\n}\n", "import { type ChildProcess, exec, spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type { UsbmuxdInstance } from \"./types\";\n\nconst { logInfo, logWarning, logDetail } = createLoggers(\"wsl\");\n\nconst execAsync = promisify(exec);\n\nexport class Wsl2 {\n\tprivate readonly ALPINE_LOCKDOWN_DIR = \"/var/lib/lockdown\";\n\tprivate readonly SYSTEM_CONFIG_PLIST = \"SystemConfiguration.plist\";\n\n\tprivate wslIpAddress: string | null = null;\n\n\tconstructor(private wslDistribution: string | undefined) {}\n\n\t/**\n\t * Detect the WSL2 IP address for the configured distribution\n\t * This IP is needed to connect from Windows to services inside WSL\n\t */\n\tpublic async detectWslIpAddress(): Promise<string> {\n\t\tif (this.wslIpAddress) {\n\t\t\treturn this.wslIpAddress;\n\t\t}\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\n\t\ttry {\n\t\t\t// Try to get IP from eth0 interface\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- ip -4 addr show eth0`);\n\t\t\tconst match = stdout.match(/inet\\s+(\\d+\\.\\d+\\.\\d+\\.\\d+)/);\n\t\t\tif (match) {\n\t\t\t\tthis.wslIpAddress = match[1];\n\t\t\t\tlogInfo(`Detected WSL IP address: ${this.wslIpAddress}`);\n\t\t\t\treturn this.wslIpAddress;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to detect WSL IP via ip addr: ${error}`);\n\t\t}\n\n\t\t// Fallback: try hostname -I\n\t\ttry {\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- hostname -I`);\n\t\t\tconst ip = stdout.trim().split(/\\s+/)[0];\n\t\t\tif (ip && /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(ip)) {\n\t\t\t\tthis.wslIpAddress = ip;\n\t\t\t\tlogInfo(`Detected WSL IP address (hostname): ${this.wslIpAddress}`);\n\t\t\t\treturn this.wslIpAddress;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to detect WSL IP via hostname: ${error}`);\n\t\t}\n\n\t\t// Last resort fallback - localhost (may work with WSL2 mirrored networking)\n\t\tlogWarning(\"Could not detect WSL IP, falling back to localhost\");\n\t\tthis.wslIpAddress = \"127.0.0.1\";\n\t\treturn this.wslIpAddress;\n\t}\n\n\t/**\n\t * Ensure the WSL distribution is running\n\t */\n\tpublic async ensureWslRunning(): Promise<boolean> {\n\t\tconst distro = this.wslDistribution;\n\n\t\ttry {\n\t\t\tif (distro) {\n\t\t\t\t// Start specific distribution\n\t\t\t\tlogInfo(`Starting WSL distribution: ${distro}...`);\n\t\t\t\tawait execAsync(`wsl -d ${distro} -- echo \"WSL started\"`);\n\t\t\t} else {\n\t\t\t\t// Start default WSL\n\t\t\t\tlogInfo(\"Starting default WSL distribution...\");\n\t\t\t\tawait execAsync(`wsl -- echo \"WSL started\"`);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to start WSL: ${error}`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Create a new usbmuxd instance\n\t */\n\tpublic async createInstance({\n\t\tid,\n\t\tbasePort,\n\t\tverboseLogging,\n\t\tusbmuxdPath,\n\t\tonLog,\n\t\tonExit,\n\t}: {\n\t\tid: number;\n\t\tbasePort: number;\n\t\tverboseLogging: boolean;\n\t\tusbmuxdPath: string;\n\t\tonLog: (message: string) => void;\n\t\tonExit: (code: number, signal: string) => void;\n\t}): Promise<{ instance: UsbmuxdInstance; process: ChildProcess }> {\n\t\tconst port = basePort + id - 1;\n\n\t\tlogInfo(`Creating instance ${id} on port ${port}`);\n\n\t\t// Detect WSL IP for connecting from Windows\n\t\tconst host = await this.detectWslIpAddress();\n\n\t\t// Build usbmuxd arguments\n\t\tconst usbmuxdArgs = [\n\t\t\t\"-f\", // Foreground\n\t\t\t\"-v\", // Verbose (if enabled)\n\t\t\t\"-S\",\n\t\t\t`0.0.0.0:${port}`, // Listen on all interfaces (for Windows \u2192 WSL2)\n\t\t\t\"--pidfile\",\n\t\t\t\"NONE\",\n\t\t];\n\n\t\tif (!verboseLogging) {\n\t\t\t// Remove -v if not verbose\n\t\t\tusbmuxdArgs.splice(1, 1);\n\t\t}\n\n\t\t// Build WSL command: wsl -d <distro> usbmuxd <args>\n\t\tconst wslArgs = [\n\t\t\t\"-d\",\n\t\t\tthis.wslDistribution || \"alpine-usbmuxd-build\",\n\t\t\tusbmuxdPath,\n\t\t\t...usbmuxdArgs,\n\t\t];\n\n\t\t// Spawn via WSL\n\t\t// detached: true creates a new process group so the child does NOT receive\n\t\t// the parent's Ctrl+C. Without this, Windows delivers SIGINT to every process\n\t\t// in the console, killing usbmuxd before stop() can sync lockdown files back.\n\t\t// windowsHide: true prevents the detached process from opening a visible console window.\n\t\tconst process = spawn(\"wsl\", wslArgs, {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tdetached: true,\n\t\t\twindowsHide: false,\n\t\t});\n\n\t\t// Handle process output\n\t\tprocess.stdout?.on(\"data\", (data) => {\n\t\t\tonLog(data.toString().trim());\n\t\t});\n\n\t\tprocess.stderr?.on(\"data\", (data) => {\n\t\t\tonLog(data.toString().trim());\n\t\t});\n\n\t\t// Handle process exit\n\t\tprocess.on(\"exit\", onExit);\n\n\t\t// Create instance record (pid is set synchronously by Node after spawn)\n\t\tconst pid = process.pid;\n\t\tif (pid === undefined) {\n\t\t\tprocess.kill(\"SIGKILL\");\n\t\t\tthrow new Error(\"Failed to get PID for usbmuxd instance\");\n\t\t}\n\t\tconst instance: UsbmuxdInstance = {\n\t\t\tid,\n\t\t\thost,\n\t\t\tport,\n\t\t\tpid,\n\t\t\tdeviceUdids: [],\n\t\t\tstartedAt: new Date(),\n\t\t};\n\n\t\treturn { instance, process };\n\t}\n\n\t/**\n\t * Convert a Windows path to the equivalent path inside WSL (e.g. C:\\foo\\bar -> /mnt/c/foo/bar).\n\t */\n\tprivate windowsPathToWsl(windowsPath: string): string {\n\t\tconst normalized = windowsPath.replace(/\\\\/g, \"/\").trim();\n\t\tconst driveMatch = normalized.match(/^([a-zA-Z]):\\/?(.*)$/);\n\t\tif (driveMatch) {\n\t\t\tconst drive = driveMatch[1].toLowerCase();\n\t\t\tconst rest = driveMatch[2] || \"\";\n\t\t\treturn `/mnt/${drive}${rest ? `/${rest}` : \"\"}`;\n\t\t}\n\t\treturn normalized;\n\t}\n\n\t/**\n\t * Get the native Apple lockdown directory path for the current platform.\n\t * This is where Apple/iTunes stores pairing records natively.\n\t */\n\tprivate getAppleLockdownPath(): string | null {\n\t\tif (process.platform === \"win32\") {\n\t\t\treturn join(process.env.ProgramData ?? \"C:\\\\ProgramData\", \"Apple\", \"Lockdown\");\n\t\t}\n\t\tif (process.platform === \"darwin\") {\n\t\t\treturn \"/var/db/lockdown\";\n\t\t}\n\t\tif (process.platform === \"linux\") {\n\t\t\treturn \"/var/lib/lockdown\";\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Copy a single device's lockdown plist from the native Apple lockdown directory\n\t * to Alpine so usbmuxd can use it (skip pairing).\n\t *\n\t * Uses the platform-specific Apple/iTunes lockdown directory\n\t * (e.g. C:\\ProgramData\\Apple\\Lockdown on Windows, /var/db/lockdown on macOS).\n\t *\n\t * No-op if the lockdown directory cannot be determined or files don't exist.\n\t */\n\tpublic async syncToAlpine(udid: string): Promise<void> {\n\t\tconst lockdownDir = this.getAppleLockdownPath();\n\t\tif (!lockdownDir) {\n\t\t\tlogWarning(\"Lockdown sync to Alpine: unsupported platform, skipping\");\n\t\t\treturn;\n\t\t}\n\n\t\tlogDetail(`Lockdown sync to Alpine: source dir = ${lockdownDir}, udid = ${udid}`);\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\t\tconst wslDestDir = this.ALPINE_LOCKDOWN_DIR;\n\n\t\t// Sync the device-specific plist\n\t\tconst devicePlistFile = `${udid}.plist`;\n\t\tconst devicePlistPath = join(lockdownDir, devicePlistFile);\n\t\tif (existsSync(devicePlistPath)) {\n\t\t\tawait this.copyFileToAlpine(\n\t\t\t\tthis.windowsPathToWsl(devicePlistPath),\n\t\t\t\twslDestDir,\n\t\t\t\tdistro,\n\t\t\t\tdevicePlistFile\n\t\t\t);\n\t\t} else {\n\t\t\tlogDetail(\n\t\t\t\t`Lockdown sync to Alpine: ${devicePlistFile} not found at ${devicePlistPath}, skipping`\n\t\t\t);\n\t\t}\n\n\t\t// Sync SystemConfiguration.plist so Alpine usbmuxd uses the same SystemBUID\n\t\t// that was used during the original pairing on Windows.\n\t\t// Without this, lockdown sessions fail with InvalidHostID.\n\t\tconst systemConfigPath = join(lockdownDir, this.SYSTEM_CONFIG_PLIST);\n\t\tif (existsSync(systemConfigPath)) {\n\t\t\tawait this.copyFileToAlpine(\n\t\t\t\tthis.windowsPathToWsl(systemConfigPath),\n\t\t\t\twslDestDir,\n\t\t\t\tdistro,\n\t\t\t\tthis.SYSTEM_CONFIG_PLIST\n\t\t\t);\n\t\t} else {\n\t\t\tlogDetail(\n\t\t\t\t`Lockdown sync to Alpine: ${this.SYSTEM_CONFIG_PLIST} not found at ${systemConfigPath}, skipping`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Copy a single file into the Alpine lockdown directory.\n\t * Falls back to sudo if the initial copy fails.\n\t */\n\tprivate async copyFileToAlpine(\n\t\twslSource: string,\n\t\twslDestDir: string,\n\t\tdistro: string,\n\t\tfileName: string\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait execAsync(\n\t\t\t\t`wsl -d ${distro} -- sh -c \"mkdir -p ${wslDestDir} && cp '${wslSource}' '${wslDestDir}/'\"`\n\t\t\t);\n\t\t\tlogInfo(`Lockdown sync: copied ${fileName} to Alpine`);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait execAsync(\n\t\t\t\t\t`wsl -d ${distro} -- sh -c \"mkdir -p ${wslDestDir} && sudo cp '${wslSource}' '${wslDestDir}/'\"`\n\t\t\t\t);\n\t\t\t\tlogInfo(`Lockdown sync: copied ${fileName} to Alpine (via sudo)`);\n\t\t\t} catch (sudoError) {\n\t\t\t\tlogWarning(\n\t\t\t\t\t`Lockdown sync to Alpine failed for ${fileName}: ${error}. Sudo fallback failed: ${sudoError}. Ensure /var/lib/lockdown is writable or use passwordless sudo.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Copy lockdown plists from Alpine back to the native Apple lockdown directory\n\t * (for devices that were assigned this session).\n\t * Also copies SystemConfiguration.plist if present in Alpine.\n\t *\n\t * No-op if the lockdown directory cannot be determined.\n\t */\n\tpublic async syncFromAlpine(udids: string[]): Promise<void> {\n\t\tlogInfo(`Lockdown sync from Alpine: starting (${udids.length} device(s))`);\n\n\t\tconst lockdownDir = this.getAppleLockdownPath();\n\t\tif (!lockdownDir) {\n\t\t\tlogWarning(\"Lockdown sync: unsupported platform, skipping\");\n\t\t\treturn;\n\t\t}\n\n\t\tlogDetail(`Lockdown sync: target dir = ${lockdownDir}`);\n\n\t\ttry {\n\t\t\tmkdirSync(lockdownDir, { recursive: true });\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: could not create lockdown dir ${lockdownDir}: ${error}`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\t\tconst wslDestDir = this.windowsPathToWsl(lockdownDir);\n\n\t\t// List what Alpine actually has in its lockdown dir for debugging\n\t\ttry {\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- ls -la ${this.ALPINE_LOCKDOWN_DIR}/`);\n\t\t\tlogDetail(`Lockdown sync: Alpine ${this.ALPINE_LOCKDOWN_DIR} contents:\\n${stdout}`);\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: could not list Alpine lockdown dir: ${error}`);\n\t\t}\n\n\t\tconst filesToSync = [...udids.map((udid) => `${udid}.plist`), this.SYSTEM_CONFIG_PLIST];\n\n\t\tlogDetail(`Lockdown sync: files to sync = [${filesToSync.join(\", \")}]`);\n\n\t\tfor (const fileName of filesToSync) {\n\t\t\tawait this.copyFileFromAlpine(fileName, wslDestDir, distro, lockdownDir);\n\t\t}\n\n\t\tlogInfo(\"Lockdown sync from Alpine: done\");\n\t}\n\n\t/**\n\t * Copy a single file from the Alpine lockdown directory back to Windows.\n\t * Skips if the file does not exist in Alpine; logs a warning if the copy fails.\n\t */\n\tprivate async copyFileFromAlpine(\n\t\tfileName: string,\n\t\twslDestDir: string,\n\t\tdistro: string,\n\t\tlockdownDir: string\n\t): Promise<void> {\n\t\tconst alpinePath = `${this.ALPINE_LOCKDOWN_DIR}/${fileName}`;\n\n\t\t// Check if the file exists in Alpine first\n\t\ttry {\n\t\t\tawait execAsync(`wsl -d ${distro} -- test -f '${alpinePath}'`);\n\t\t} catch {\n\t\t\tlogDetail(`Lockdown sync: ${fileName} not found in Alpine (${alpinePath}), skipping`);\n\t\t\treturn;\n\t\t}\n\n\t\t// File exists \u2014 copy it to Windows\n\t\tlogDetail(`Lockdown sync: copying ${fileName} \u2192 ${wslDestDir}/`);\n\t\ttry {\n\t\t\tawait execAsync(`wsl -d ${distro} -- cp -f '${alpinePath}' '${wslDestDir}/'`);\n\t\t\tlogInfo(`Lockdown sync: copied ${fileName} from Alpine to ${lockdownDir}`);\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: failed to copy ${fileName} from Alpine to ${lockdownDir}: ${error}`);\n\t\t}\n\t}\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["import_node_events", "import_tool_debug_g4", "import_tool_debug_g4", "import_node_child_process", "import_node_util", "import_tool_debug_g4", "logInfo", "logWarning", "execAsync", "process", "logWarning", "logDetail", "process", "logInfo", "logDataObject"]
7
7
  }
package/dist/index.mjs CHANGED
@@ -1,7 +1,6 @@
1
1
  // src/UsbmuxdService.ts
2
2
  import { EventEmitter as EventEmitter2 } from "node:events";
3
3
  import { createLoggers as createLoggers4 } from "@mcesystems/tool-debug-g4";
4
- import UsbDeviceListener from "@mcesystems/usb-device-listener";
5
4
 
6
5
  // src/InstanceManager.ts
7
6
  import { EventEmitter } from "node:events";
@@ -722,12 +721,10 @@ var InstanceManager = class extends EventEmitter {
722
721
  var { logInfo: logInfo3, logError, logDataObject: logDataObject2 } = createLoggers4("usbmuxd-service");
723
722
  var UsbmuxdService = class extends EventEmitter2 {
724
723
  manager;
725
- usbListener;
726
724
  isListening = false;
727
725
  constructor(config = {}) {
728
726
  super();
729
727
  this.manager = new InstanceManager(config);
730
- this.usbListener = new UsbDeviceListener();
731
728
  this.setupEventHandlers();
732
729
  }
733
730
  /**
@@ -780,39 +777,18 @@ var UsbmuxdService = class extends EventEmitter2 {
780
777
  throw new Error("Service is already running");
781
778
  }
782
779
  const config = this.manager.getConfig();
783
- const appleVid = Number.parseInt(config.appleVendorId, 16);
784
780
  logDataObject2("Starting service...", { config });
785
- this.usbListener.onDeviceAdd(async (device) => {
786
- if (device.vid !== appleVid) {
787
- return;
788
- }
789
- logInfo3(`Apple device connected: ${device.deviceId} (${device.deviceName || "Unknown"})`);
790
- try {
791
- await this.manager.onDeviceConnected(device);
792
- } catch (error) {
793
- logError("Error handling device connection:", error);
794
- }
795
- });
796
- this.usbListener.onDeviceRemove(async (device) => {
797
- if (device.vid !== appleVid) {
798
- return;
799
- }
800
- logInfo3(`Apple device disconnected: ${device.deviceId}`);
801
- try {
802
- await this.manager.onDeviceDisconnected(device);
803
- } catch (error) {
804
- logError("Error handling device disconnection:", error);
805
- }
806
- });
807
781
  try {
808
- this.usbListener.startListening({
809
- targetDevices: []
810
- // Monitor all devices, we'll filter by VID
811
- });
812
782
  this.isListening = true;
813
783
  this.manager.start();
814
784
  logInfo3("Service started successfully");
815
785
  logInfo3("Waiting for iOS devices...");
786
+ const onAdd = this.manager.onDeviceConnected.bind(this.manager);
787
+ const onRemove = this.manager.onDeviceDisconnected.bind(this.manager);
788
+ return {
789
+ onAdd,
790
+ onRemove
791
+ };
816
792
  } catch (error) {
817
793
  logError("Failed to start USB listener:", error);
818
794
  throw error;
@@ -827,7 +803,6 @@ var UsbmuxdService = class extends EventEmitter2 {
827
803
  return;
828
804
  }
829
805
  logInfo3("Stopping service...");
830
- this.usbListener.stopListening();
831
806
  this.isListening = false;
832
807
  await this.manager.stop();
833
808
  logInfo3("Service stopped");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/UsbmuxdService.ts", "../src/InstanceManager.ts", "../src/usbipd.ts", "../src/wsl.ts"],
4
- "sourcesContent": ["import { EventEmitter } from \"node:events\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport UsbDeviceListener from \"@mcesystems/usb-device-listener\";\nimport type { DeviceInfo } from \"@mcesystems/usb-device-listener\";\nimport { InstanceManager } from \"./InstanceManager.js\";\nimport type { DeviceMapping, InstanceManagerConfig, UsbmuxdInstance } from \"./types/index.js\";\n\nconst { logInfo, logError, logDataObject } = createLoggers(\"usbmuxd-service\");\n\n/**\n * Main service that integrates usb-device-listener with InstanceManager\n * Monitors iOS devices and manages usbmuxd instances automatically.\n * Extends EventEmitter: forwards instance-started, instance-stopped, device-assigned,\n * device-removed, instance-log, instance-exited from the underlying manager.\n */\nexport class UsbmuxdService extends EventEmitter {\n\tprivate manager: InstanceManager;\n\tprivate usbListener: UsbDeviceListener;\n\tprivate isListening = false;\n\n\tconstructor(config: Partial<InstanceManagerConfig> = {}) {\n\t\tsuper();\n\t\tthis.manager = new InstanceManager(config);\n\t\tthis.usbListener = new UsbDeviceListener();\n\t\tthis.setupEventHandlers();\n\t}\n\n\t/**\n\t * Forward manager events to service subscribers (no logging; CLI/examples handle logging).\n\t */\n\tprivate setupEventHandlers(): void {\n\t\tthis.manager.on(\"instance-started\", (instance: UsbmuxdInstance) => {\n\t\t\tthis.emit(\"instance-started\", instance);\n\t\t});\n\n\t\tthis.manager.on(\"instance-stopped\", (instance: UsbmuxdInstance) => {\n\t\t\tthis.emit(\"instance-stopped\", instance);\n\t\t});\n\n\t\tthis.manager.on(\n\t\t\t\"device-assigned\",\n\t\t\t(payload: { device: DeviceInfo; instance: UsbmuxdInstance; mapping: DeviceMapping }) => {\n\t\t\t\tlogInfo(\n\t\t\t\t\t`Device ${payload.device.deviceId} connected to usbmuxd instance at ${payload.mapping.host}:${payload.mapping.port}`\n\t\t\t\t);\n\t\t\t\tthis.emit(\"device-assigned\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"device-removed\",\n\t\t\t(payload: { device: DeviceInfo; instance: UsbmuxdInstance }) => {\n\t\t\t\tthis.emit(\"device-removed\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"instance-log\",\n\t\t\t(payload: { instanceId: number; level: string; message: string }) => {\n\t\t\t\tthis.emit(\"instance-log\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"instance-exited\",\n\t\t\t(payload: { instanceId: number; code: number | null; signal: string | null }) => {\n\t\t\t\tthis.emit(\"instance-exited\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\"device-paired\", (payload: { udid: string; mapping: DeviceMapping }) => {\n\t\t\tthis.emit(\"device-paired\", payload);\n\t\t});\n\t}\n\n\t/**\n\t * Start the service\n\t * Begins monitoring for iOS devices\n\t */\n\tpublic start(): void {\n\t\tif (this.isListening) {\n\t\t\tthrow new Error(\"Service is already running\");\n\t\t}\n\n\t\tconst config = this.manager.getConfig();\n\n\t\t// Convert Apple VID from hex to decimal for comparison\n\t\tconst appleVid = Number.parseInt(config.appleVendorId, 16);\n\n\t\tlogDataObject(\"Starting service...\", { config });\n\n\t\t// Set up device listener callbacks\n\t\tthis.usbListener.onDeviceAdd(async (device: DeviceInfo) => {\n\t\t\t// Filter for Apple devices only\n\t\t\tif (device.vid !== appleVid) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlogInfo(`Apple device connected: ${device.deviceId} (${device.deviceName || \"Unknown\"})`);\n\n\t\t\ttry {\n\t\t\t\tawait this.manager.onDeviceConnected(device);\n\t\t\t} catch (error) {\n\t\t\t\tlogError(\"Error handling device connection:\", error);\n\t\t\t}\n\t\t});\n\n\t\tthis.usbListener.onDeviceRemove(async (device: DeviceInfo) => {\n\t\t\t// Filter for Apple devices only\n\t\t\tif (device.vid !== appleVid) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlogInfo(`Apple device disconnected: ${device.deviceId}`);\n\n\t\t\ttry {\n\t\t\t\tawait this.manager.onDeviceDisconnected(device);\n\t\t\t} catch (error) {\n\t\t\t\tlogError(\"Error handling device disconnection:\", error);\n\t\t\t}\n\t\t});\n\n\t\t// Start listening for USB events\n\t\ttry {\n\t\t\tthis.usbListener.startListening({\n\t\t\t\ttargetDevices: [], // Monitor all devices, we'll filter by VID\n\t\t\t});\n\n\t\t\tthis.isListening = true;\n\t\t\tthis.manager.start();\n\n\t\t\tlogInfo(\"Service started successfully\");\n\t\t\tlogInfo(\"Waiting for iOS devices...\");\n\t\t} catch (error) {\n\t\t\tlogError(\"Failed to start USB listener:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Stop the service\n\t * Stops monitoring and terminates all instances\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tif (!this.isListening) {\n\t\t\treturn;\n\t\t}\n\n\t\tlogInfo(\"Stopping service...\");\n\n\t\t// Stop USB listener\n\t\tthis.usbListener.stopListening();\n\t\tthis.isListening = false;\n\n\t\t// Stop all instances\n\t\tawait this.manager.stop();\n\n\t\tlogInfo(\"Service stopped\");\n\t}\n\n\t/**\n\t * Get device port mapping\n\t */\n\tpublic getDevicePort(udid: string): number | null {\n\t\treturn this.manager.getDevicePort(udid);\n\t}\n\n\t/**\n\t * Get all device mappings\n\t */\n\tpublic getDeviceMappings() {\n\t\treturn this.manager.getDeviceMappings();\n\t}\n\n\t/**\n\t * Get all running instances\n\t */\n\tpublic getInstances() {\n\t\treturn this.manager.getInstances();\n\t}\n\n\t/**\n\t * Get service statistics\n\t */\n\tpublic getStats() {\n\t\treturn this.manager.getStats();\n\t}\n\n\t/**\n\t * Get current configuration\n\t */\n\tpublic getConfig() {\n\t\treturn this.manager.getConfig();\n\t}\n}\n", "import type { ChildProcess } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type {\n\tDeviceInfo,\n\tDeviceMapping,\n\tInstanceManagerConfig,\n\tManagerStats,\n\tUsbmuxdInstance,\n} from \"./types/index.js\";\nimport { Usbipd } from \"./usbipd.js\";\nimport { Wsl2 } from \"./wsl.js\";\n\nconst { logWarning, logDetail, logDataObject, logTask } = createLoggers(\"instance-manager\");\n\n/**\n * Default configuration for the instance manager\n */\nconst DEFAULT_CONFIG: InstanceManagerConfig = {\n\tbatchSize: 4,\n\tbasePort: 27015,\n\tmaxInstances: 5,\n\tusbmuxdPath: \"usbmuxd\", // Path inside WSL2\n\twslDistribution: \"alpine-usbmuxd-build\", // Alpine WSL2 distribution name\n\tverboseLogging: true,\n\tappleVendorId: \"05AC\",\n};\n\n/**\n * Manages multiple usbmuxd instances with batch device allocation\n *\n * @example\n * const manager = new InstanceManager({\n * batchSize: 4,\n * basePort: 27015,\n * });\n *\n * manager.on('instance-started', (instance) => {\n * logInfo(`Instance ${instance.id} started on port ${instance.port}`);\n * });\n *\n * manager.start();\n */\nexport class InstanceManager extends EventEmitter {\n\t/** Map of usbmuxd instance IDs to instances */\n\tprivate instances: Map<number, UsbmuxdInstance> = new Map();\n\t/** Map of device IDs to device mappings host:port */\n\tprivate deviceMappings: Map<string, DeviceMapping> = new Map();\n\t/** Map of usbmuxd instance IDs to child processes running it on wsl2*/\n\tprivate processes: Map<number, ChildProcess> = new Map();\n\t/** Tracks which devices have been attached to WSL */\n\tprivate attachedDevices: Set<string> = new Set();\n\t/** Device IDs currently in the attach flow\n\t * (ignore disconnect until attach completes) */\n\tprivate pendingAttachDevices: Set<string> = new Set();\n\n\tprivate config: InstanceManagerConfig;\n\tprivate nextInstanceId = 1;\n\tprivate startedAt: Date | null = null;\n\tprivate isRunning = false;\n\n\tprivate wsl: Wsl2;\n\tprivate usbipd: Usbipd;\n\tconstructor(config: Partial<InstanceManagerConfig> = {}) {\n\t\tsuper();\n\t\tthis.config = { ...DEFAULT_CONFIG, ...config };\n\t\tthis.wsl = new Wsl2(this.config.wslDistribution);\n\t\tthis.usbipd = new Usbipd(process.env.USBIPD_PATH ?? \"usbipd\");\n\t}\n\n\t/**\n\t * Start the instance manager\n\t */\n\tpublic start(): void {\n\t\tif (this.isRunning) {\n\t\t\tthrow new Error(\"Instance manager is already running\");\n\t\t}\n\n\t\tthis.isRunning = true;\n\t\tthis.startedAt = new Date();\n\t}\n\n\t/**\n\t * Stop the instance manager and all instances\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tif (!this.isRunning) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.isRunning = false;\n\n\t\t// Sync lockdown files from Alpine back to native Apple lockdown directory (before clearing mappings)\n\t\tconst udids = Array.from(this.deviceMappings.keys());\n\t\tawait this.wsl.syncFromAlpine(udids);\n\n\t\t// Detach all devices from WSL.\n\t\t// If you only called unbind without detach,\n\t\t// the device might still be attached to the WSL session\n\t\tawait this.usbipd.detachAllDevicesFromWsl();\n\t\tawait this.usbipd.unbindAllDevicesFromWsl();\n\t\tthis.attachedDevices.clear();\n\n\t\t// Stop all instances\n\t\tconst stopPromises = Array.from(this.instances.keys()).map((id) => this.stopInstance(id));\n\n\t\tawait Promise.all(stopPromises);\n\n\t\tthis.instances.clear();\n\t\tthis.deviceMappings.clear();\n\t\tthis.processes.clear();\n\t}\n\n\tprivate async attachToWsl({\n\t\tbusId,\n\t\tdeviceId,\n\t}: { busId: string; deviceId: string }): Promise<void> {\n\t\tif (!this.attachedDevices.has(busId)) {\n\t\t\tthis.pendingAttachDevices.add(deviceId);\n\t\t\tif (!this.config.wslDistribution) {\n\t\t\t\tthrow new Error(\"WSL distribution not configured\");\n\t\t\t}\n\t\t\tconst attached = await this.usbipd.attachDeviceToWsl(\n\t\t\t\tbusId,\n\t\t\t\tthis.wsl,\n\t\t\t\tthis.config.wslDistribution\n\t\t\t);\n\t\t\tif (attached) {\n\t\t\t\tthis.attachedDevices.add(busId);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async removeDevice(mapping: DeviceMapping): Promise<UsbmuxdInstance> {\n\t\tconst instance = await this.detachFromWsl({ deviceId: mapping.udid });\n\t\tif (!instance) {\n\t\t\tthrow new Error(`Instance ${mapping.instanceId} not found`);\n\t\t}\n\t\tif (mapping.busId) {\n\t\t\tawait this.usbipd.detachDeviceFromWsl(mapping.busId);\n\t\t\tthis.attachedDevices.delete(mapping.busId);\n\t\t}\n\t\tconst deviceIndex = instance.deviceUdids.indexOf(mapping.udid);\n\t\tif (deviceIndex > -1) {\n\t\t\tinstance.deviceUdids.splice(deviceIndex, 1);\n\t\t}\n\n\t\tthis.deviceMappings.delete(mapping.udid);\n\t\treturn instance;\n\t}\n\n\tprivate async onUsbmuxdInstanceEnd({\n\t\tinstanceId,\n\t\tcode,\n\t\tsignal,\n\t}: { instanceId: number; code: number; signal: string }): Promise<void> {\n\t\tthis.emit(\"instance-exited\", {\n\t\t\tinstanceId,\n\t\t\tcode,\n\t\t\tsignal,\n\t\t});\n\t\tfor (const [_, mapping] of this.deviceMappings.entries()) {\n\t\t\tif (mapping.instanceId === instanceId) {\n\t\t\t\tthis.removeDevice(mapping);\n\t\t\t}\n\t\t}\n\t\tthis.instances.delete(instanceId);\n\t\tthis.processes.get(instanceId)?.kill();\n\t\tthis.processes.delete(instanceId);\n\t}\n\n\tprivate async onUsbmuxdInstanceStart({\n\t\tinstance,\n\t\tprocess,\n\t}: { instance: UsbmuxdInstance; process: ChildProcess }): Promise<void> {\n\t\tthis.instances.set(instance.id, instance);\n\t\tthis.processes.set(instance.id, process);\n\t\tthis.emit(\"instance-started\", {\n\t\t\tinstanceId: instance.id,\n\t\t});\n\t}\n\n\tprivate async getInstance() {\n\t\tlet targetInstance = this.findInstanceWithCapacity();\n\n\t\t// If no instance has capacity, create a new one\n\t\tif (!targetInstance) {\n\t\t\tif (this.instances.size >= this.config.maxInstances) {\n\t\t\t\tthrow new Error(`Maximum number of instances (${this.config.maxInstances}) reached`);\n\t\t\t}\n\n\t\t\tconst newInstanceId = this.nextInstanceId++;\n\n\t\t\tlet onExit = (code: number, signal: string) => {\n\t\t\t\tthis.onUsbmuxdInstanceEnd({ instanceId: newInstanceId, code, signal });\n\t\t\t};\n\n\t\t\tonExit = onExit.bind(this);\n\n\t\t\tconst { instance, process } = await this.wsl.createInstance({\n\t\t\t\tid: newInstanceId,\n\t\t\t\tbasePort: this.config.basePort,\n\t\t\t\tverboseLogging: this.config.verboseLogging,\n\t\t\t\tusbmuxdPath: this.config.usbmuxdPath,\n\t\t\t\tonLog: (message) =>\n\t\t\t\t\tthis.emit(\"instance-log\", {\n\t\t\t\t\t\tinstanceId: newInstanceId,\n\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t}),\n\t\t\t\tonExit,\n\t\t\t});\n\t\t\tthis.onUsbmuxdInstanceStart({ instance, process });\n\t\t\ttargetInstance = instance;\n\t\t}\n\t\treturn targetInstance;\n\t}\n\n\t/**\n\t * Handle device connection only for not attached devices\n\t * Attaches device to WSL, then assigns to an existing instance or creates a new one\n\t */\n\tpublic async onDeviceConnected(device: DeviceInfo): Promise<void> {\n\t\tconst busId = await this.usbipd.findBusIdForDevice(device);\n\n\t\tif (this.attachedDevices.has(busId)) {\n\t\t\tlogTask(`Device ${device.deviceId} is already attached. skipping onDeviceConnected...`);\n\t\t\treturn;\n\t\t}\n\n\t\tlogDataObject(\"Device connected\", { device });\n\t\tawait this.wsl.syncToAlpine(device.deviceId);\n\n\t\tawait this.attachToWsl({ busId, deviceId: device.deviceId });\n\n\t\tconst targetInstance = await this.getInstance();\n\n\t\t// Create mapping (include host and busId for cleanup on disconnect)\n\t\tconst mapping: DeviceMapping = {\n\t\t\tudid: device.deviceId,\n\t\t\tinstanceId: targetInstance.id,\n\t\t\thost: targetInstance.host,\n\t\t\tport: targetInstance.port,\n\t\t\taddedAt: new Date(),\n\t\t\tbusId: busId ?? undefined,\n\t\t};\n\n\t\tthis.deviceMappings.set(device.deviceId, mapping);\n\n\t\t// Add device to instance\n\t\ttargetInstance.deviceUdids.push(device.deviceId);\n\n\t\tthis.emit(\"device-assigned\", {\n\t\t\tdevice,\n\t\t\tinstance: targetInstance,\n\t\t\tmapping,\n\t\t});\n\n\t\tthis.pendingAttachDevices.delete(device.deviceId);\n\t}\n\n\tprivate async detachFromWsl({ deviceId }: { deviceId: string }): Promise<UsbmuxdInstance> {\n\t\tconst mapping = this.deviceMappings.get(deviceId);\n\t\tif (!mapping) {\n\t\t\tthrow new Error(`Device ${deviceId} was not tracked`);\n\t\t}\n\n\t\t// Detach device from WSL if it was attached\n\t\tif (mapping.busId) {\n\t\t\tawait this.usbipd.detachDeviceFromWsl(mapping.busId);\n\t\t\tthis.attachedDevices.delete(mapping.busId);\n\t\t}\n\n\t\tconst instance = this.instances.get(mapping.instanceId);\n\t\tif (!instance) {\n\t\t\tlogWarning(`Instance ${mapping.instanceId} not found`);\n\t\t\tthis.deviceMappings.delete(deviceId);\n\t\t\tthrow new Error(`Instance ${mapping.instanceId} not found`);\n\t\t}\n\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Handle device disconnection only for attached devices\n\t * Detaches from WSL, removes device from instance, and stops instance if empty\n\t */\n\tpublic async onDeviceDisconnected(device: DeviceInfo): Promise<void> {\n\t\t// Ignore disconnect while we're still attaching this device (Windows sees disconnect when device moves to WSL)\n\t\tif (!this.attachedDevices.has(device.deviceId)) {\n\t\t\tlogTask(`Device ${device.deviceId} is not attached. skipping onDeviceDisconnected...`);\n\t\t\treturn;\n\t\t}\n\t\tlogDataObject(\"Device disconnected\", { device });\n\n\t\tconst mapping = this.deviceMappings.get(device.deviceId);\n\t\tif (!mapping) {\n\t\t\tthrow new Error(`Device ${device.deviceId} was not tracked`);\n\t\t}\n\t\t// Remove device from instance\n\t\tconst instance = await this.removeDevice(mapping);\n\n\t\tthis.emit(\"device-removed\", {\n\t\t\tdevice,\n\t\t\tinstance,\n\t\t});\n\n\t\t// If instance is now empty, stop it\n\t\tif (instance.deviceUdids.length === 0) {\n\t\t\tlogDetail(`Instance ${instance.id} is empty, stopping...`);\n\t\t\tawait this.stopInstance(instance.id);\n\t\t}\n\t}\n\n\t/**\n\t * Find an instance with available capacity\n\t */\n\tprivate findInstanceWithCapacity(): UsbmuxdInstance | null {\n\t\tfor (const instance of this.instances.values()) {\n\t\t\tif (instance.deviceUdids.length < this.config.batchSize) {\n\t\t\t\treturn instance;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Stop a specific instance\n\t */\n\tprivate async stopInstance(instanceId: number): Promise<void> {\n\t\tconst instance = this.instances.get(instanceId);\n\t\tconst childProcess = this.processes.get(instanceId);\n\n\t\tif (!instance || !childProcess) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const process of this.processes.values()) {\n\t\t\tprocess.kill(\"SIGKILL\");\n\t\t}\n\n\t\t// Clean up\n\t\tthis.instances.delete(instanceId);\n\t\tthis.processes.delete(instanceId);\n\n\t\t// Remove device mappings\n\t\tfor (const [udid, mapping] of this.deviceMappings.entries()) {\n\t\t\tif (mapping.instanceId === instanceId) {\n\t\t\t\tthis.deviceMappings.delete(udid);\n\t\t\t}\n\t\t}\n\n\t\tthis.emit(\"instance-stopped\", instance);\n\t}\n\n\t/**\n\t * Get device-to-port mapping for a specific UDID\n\t */\n\tpublic getDevicePort(udid: string): number | null {\n\t\tconst mapping = this.deviceMappings.get(udid);\n\t\treturn mapping ? mapping.port : null;\n\t}\n\n\t/**\n\t * Get all device mappings\n\t */\n\tpublic getDeviceMappings(): DeviceMapping[] {\n\t\treturn Array.from(this.deviceMappings.values());\n\t}\n\n\t/**\n\t * Get all running instances\n\t */\n\tpublic getInstances(): UsbmuxdInstance[] {\n\t\treturn Array.from(this.instances.values());\n\t}\n\n\t/**\n\t * Get manager statistics\n\t */\n\tpublic getStats(): ManagerStats {\n\t\tconst now = Date.now();\n\t\tconst startTime = this.startedAt?.getTime() || now;\n\t\tconst uptimeSeconds = Math.floor((now - startTime) / 1000);\n\n\t\treturn {\n\t\t\tinstanceCount: this.instances.size,\n\t\t\tdeviceCount: this.deviceMappings.size,\n\t\t\tuptimeSeconds,\n\t\t\tstartedAt: this.startedAt || new Date(),\n\t\t};\n\t}\n\n\t/**\n\t * Get current configuration\n\t */\n\tpublic getConfig(): InstanceManagerConfig {\n\t\treturn { ...this.config };\n\t}\n}\n", "import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type { DeviceInfo } from \"./types\";\nimport type { UsbipdDevice } from \"./types/usbipd.js\";\nimport type { Wsl2 } from \"./wsl.js\";\n\nconst { logInfo, logWarning } = createLoggers(\"usbipd\");\n\nconst execAsync = promisify(exec);\n\nexport class Usbipd {\n\tconstructor(private readonly usbipdPath: string) {}\n\n\tpublic async unbindAllDevicesFromWsl(): Promise<void> {\n\t\ttry {\n\t\t\tawait execAsync(`\"${this.usbipdPath}\" unbind -a`);\n\t\t\tlogInfo(\"All devices unbound from WSL\");\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to unbind all devices from WSL: ${error}`);\n\t\t}\n\t}\n\n\tpublic async detachAllDevicesFromWsl(): Promise<void> {\n\t\ttry {\n\t\t\tawait execAsync(`\"${this.usbipdPath}\" detach -a`);\n\t\t\tlogInfo(\"All devices detached from WSL\");\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to detach all devices from WSL: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Detach a device from WSL via usbipd\n\t */\n\tpublic async detachDeviceFromWsl(busId: string): Promise<void> {\n\t\ttry {\n\t\t\tawait execAsync(`\"${this.usbipdPath}\" detach --busid=${busId}`);\n\t\t\tlogInfo(`Device ${busId} detached from WSL`);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (message.includes(\"no device with busid\") || message.includes(\"There is no device\")) {\n\t\t\t\tlogInfo(`Device ${busId} already detached`);\n\t\t\t} else {\n\t\t\t\tlogWarning(`Failed to detach device ${busId}: ${error}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Attach a device to WSL via usbipd\n\t * Note: This requires administrator privileges\n\t */\n\tpublic async attachDeviceToWsl(busId: string, wsl: Wsl2, distro: string): Promise<boolean> {\n\t\ttry {\n\t\t\t// Ensure WSL is running first\n\t\t\tawait wsl.ensureWslRunning();\n\n\t\t\t// Bind the device (make it shareable)\n\t\t\tlogInfo(`Binding device ${busId}...`);\n\t\t\tawait execAsync(`\"${this.usbipdPath}\" bind --busid ${busId} --force`);\n\n\t\t\t// Attach to WSL (use specific distro if configured, otherwise let usbipd pick)\n\t\t\tif (distro) {\n\t\t\t\tlogInfo(`Attaching device ${busId} to WSL distribution ${distro}...`);\n\t\t\t\tawait execAsync(`\"${this.usbipdPath}\" attach --wsl=${distro} --busid=${busId}`);\n\t\t\t} else {\n\t\t\t\tlogInfo(`Attaching device ${busId} to default WSL...`);\n\t\t\t\tawait execAsync(`\"${this.usbipdPath}\" attach --wsl --busid=${busId}`);\n\t\t\t}\n\n\t\t\tlogInfo(`Device ${busId} attached to WSL successfully`);\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t// Device already attached (e.g. from a previous run) \u2014 treat as success so we still start usbmuxd\n\t\t\tif (message.includes(\"already attached to a client\")) {\n\t\t\t\tlogInfo(`Device ${busId} is already attached to WSL, continuing`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tlogWarning(`Failed to attach device ${busId} to WSL: ${error}`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Parse usbipd list output to extract device information\n\t */\n\tprivate parseUsbipdList(output: string): UsbipdDevice[] {\n\t\tconst devices: UsbipdDevice[] = [];\n\t\t// Handle both \\r\\n and \\n line endings\n\t\tconst lines = output.split(/\\r?\\n/);\n\n\t\tfor (const line of lines) {\n\t\t\t// Format: \"4-5 05ac:12a8 Apple Mobile Device USB Composite Device Shared (forced)\"\n\t\t\t// More flexible regex: busid, vid:pid, then anything else\n\t\t\tconst match = line.match(/^(\\d+-\\d+)\\s+([0-9a-f]{4}):([0-9a-f]{4})\\s+(.+)$/i);\n\t\t\tif (match) {\n\t\t\t\t// Split the rest into description and state (state is the last word(s) after multiple spaces)\n\t\t\t\tconst rest = match[4].trim();\n\t\t\t\tconst stateMatch = rest.match(/^(.+?)\\s{2,}(\\S.*)$/);\n\t\t\t\tconst description = stateMatch ? stateMatch[1].trim() : rest;\n\t\t\t\tconst state = stateMatch ? stateMatch[2].trim() : \"Unknown\";\n\n\t\t\t\tdevices.push({\n\t\t\t\t\tbusId: match[1],\n\t\t\t\t\tvid: match[2].toUpperCase(),\n\t\t\t\t\tpid: match[3].toUpperCase(),\n\t\t\t\t\tdescription,\n\t\t\t\t\tstate,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn devices;\n\t}\n\t/**\n\t * Find the usbipd bus ID for a device by matching VID/PID\n\t */\n\tpublic async findBusIdForDevice(device: DeviceInfo): Promise<string> {\n\t\ttry {\n\t\t\tconst { stdout } = await execAsync(`\"${this.usbipdPath}\" list`);\n\t\t\tconst usbipdDevices = this.parseUsbipdList(stdout);\n\n\t\t\t// Convert device VID/PID to hex string for comparison\n\t\t\tconst deviceVid = device.vid.toString(16).toUpperCase().padStart(4, \"0\");\n\t\t\tconst devicePid = device.pid.toString(16).toUpperCase().padStart(4, \"0\");\n\n\t\t\tlogInfo(`Looking for device with VID:PID ${deviceVid}:${devicePid}`);\n\t\t\tlogInfo(`Found ${usbipdDevices.length} devices from usbipd list`);\n\n\t\t\t// Find matching device by VID/PID\n\t\t\tconst match = usbipdDevices.find((d) => d.vid === deviceVid && d.pid === devicePid);\n\n\t\t\tif (match) {\n\t\t\t\tlogInfo(\n\t\t\t\t\t`Found usbipd bus ID ${match.busId} for device ${device.deviceId} (${deviceVid}:${devicePid})`\n\t\t\t\t);\n\t\t\t\treturn match.busId;\n\t\t\t}\n\n\t\t\tthrow new Error(\n\t\t\t\t`Could not find usbipd bus ID for device ${device.deviceId} (${deviceVid}:${devicePid})`\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Failed to run usbipd list: ${error}`);\n\t\t}\n\t}\n}\n", "import { type ChildProcess, exec, spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type { UsbmuxdInstance } from \"./types\";\n\nconst { logInfo, logWarning, logDetail } = createLoggers(\"wsl\");\n\nconst execAsync = promisify(exec);\n\nexport class Wsl2 {\n\tprivate readonly ALPINE_LOCKDOWN_DIR = \"/var/lib/lockdown\";\n\tprivate readonly SYSTEM_CONFIG_PLIST = \"SystemConfiguration.plist\";\n\n\tprivate wslIpAddress: string | null = null;\n\n\tconstructor(private wslDistribution: string | undefined) {}\n\n\t/**\n\t * Detect the WSL2 IP address for the configured distribution\n\t * This IP is needed to connect from Windows to services inside WSL\n\t */\n\tpublic async detectWslIpAddress(): Promise<string> {\n\t\tif (this.wslIpAddress) {\n\t\t\treturn this.wslIpAddress;\n\t\t}\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\n\t\ttry {\n\t\t\t// Try to get IP from eth0 interface\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- ip -4 addr show eth0`);\n\t\t\tconst match = stdout.match(/inet\\s+(\\d+\\.\\d+\\.\\d+\\.\\d+)/);\n\t\t\tif (match) {\n\t\t\t\tthis.wslIpAddress = match[1];\n\t\t\t\tlogInfo(`Detected WSL IP address: ${this.wslIpAddress}`);\n\t\t\t\treturn this.wslIpAddress;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to detect WSL IP via ip addr: ${error}`);\n\t\t}\n\n\t\t// Fallback: try hostname -I\n\t\ttry {\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- hostname -I`);\n\t\t\tconst ip = stdout.trim().split(/\\s+/)[0];\n\t\t\tif (ip && /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(ip)) {\n\t\t\t\tthis.wslIpAddress = ip;\n\t\t\t\tlogInfo(`Detected WSL IP address (hostname): ${this.wslIpAddress}`);\n\t\t\t\treturn this.wslIpAddress;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to detect WSL IP via hostname: ${error}`);\n\t\t}\n\n\t\t// Last resort fallback - localhost (may work with WSL2 mirrored networking)\n\t\tlogWarning(\"Could not detect WSL IP, falling back to localhost\");\n\t\tthis.wslIpAddress = \"127.0.0.1\";\n\t\treturn this.wslIpAddress;\n\t}\n\n\t/**\n\t * Ensure the WSL distribution is running\n\t */\n\tpublic async ensureWslRunning(): Promise<boolean> {\n\t\tconst distro = this.wslDistribution;\n\n\t\ttry {\n\t\t\tif (distro) {\n\t\t\t\t// Start specific distribution\n\t\t\t\tlogInfo(`Starting WSL distribution: ${distro}...`);\n\t\t\t\tawait execAsync(`wsl -d ${distro} -- echo \"WSL started\"`);\n\t\t\t} else {\n\t\t\t\t// Start default WSL\n\t\t\t\tlogInfo(\"Starting default WSL distribution...\");\n\t\t\t\tawait execAsync(`wsl -- echo \"WSL started\"`);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to start WSL: ${error}`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Create a new usbmuxd instance\n\t */\n\tpublic async createInstance({\n\t\tid,\n\t\tbasePort,\n\t\tverboseLogging,\n\t\tusbmuxdPath,\n\t\tonLog,\n\t\tonExit,\n\t}: {\n\t\tid: number;\n\t\tbasePort: number;\n\t\tverboseLogging: boolean;\n\t\tusbmuxdPath: string;\n\t\tonLog: (message: string) => void;\n\t\tonExit: (code: number, signal: string) => void;\n\t}): Promise<{ instance: UsbmuxdInstance; process: ChildProcess }> {\n\t\tconst port = basePort + id - 1;\n\n\t\tlogInfo(`Creating instance ${id} on port ${port}`);\n\n\t\t// Detect WSL IP for connecting from Windows\n\t\tconst host = await this.detectWslIpAddress();\n\n\t\t// Build usbmuxd arguments\n\t\tconst usbmuxdArgs = [\n\t\t\t\"-f\", // Foreground\n\t\t\t\"-v\", // Verbose (if enabled)\n\t\t\t\"-S\",\n\t\t\t`0.0.0.0:${port}`, // Listen on all interfaces (for Windows \u2192 WSL2)\n\t\t\t\"--pidfile\",\n\t\t\t\"NONE\",\n\t\t];\n\n\t\tif (!verboseLogging) {\n\t\t\t// Remove -v if not verbose\n\t\t\tusbmuxdArgs.splice(1, 1);\n\t\t}\n\n\t\t// Build WSL command: wsl -d <distro> usbmuxd <args>\n\t\tconst wslArgs = [\n\t\t\t\"-d\",\n\t\t\tthis.wslDistribution || \"alpine-usbmuxd-build\",\n\t\t\tusbmuxdPath,\n\t\t\t...usbmuxdArgs,\n\t\t];\n\n\t\t// Spawn via WSL\n\t\t// detached: true creates a new process group so the child does NOT receive\n\t\t// the parent's Ctrl+C. Without this, Windows delivers SIGINT to every process\n\t\t// in the console, killing usbmuxd before stop() can sync lockdown files back.\n\t\t// windowsHide: true prevents the detached process from opening a visible console window.\n\t\tconst process = spawn(\"wsl\", wslArgs, {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tdetached: true,\n\t\t\twindowsHide: false,\n\t\t});\n\n\t\t// Handle process output\n\t\tprocess.stdout?.on(\"data\", (data) => {\n\t\t\tonLog(data.toString().trim());\n\t\t});\n\n\t\tprocess.stderr?.on(\"data\", (data) => {\n\t\t\tonLog(data.toString().trim());\n\t\t});\n\n\t\t// Handle process exit\n\t\tprocess.on(\"exit\", onExit);\n\n\t\t// Create instance record (pid is set synchronously by Node after spawn)\n\t\tconst pid = process.pid;\n\t\tif (pid === undefined) {\n\t\t\tprocess.kill(\"SIGKILL\");\n\t\t\tthrow new Error(\"Failed to get PID for usbmuxd instance\");\n\t\t}\n\t\tconst instance: UsbmuxdInstance = {\n\t\t\tid,\n\t\t\thost,\n\t\t\tport,\n\t\t\tpid,\n\t\t\tdeviceUdids: [],\n\t\t\tstartedAt: new Date(),\n\t\t};\n\n\t\treturn { instance, process };\n\t}\n\n\t/**\n\t * Convert a Windows path to the equivalent path inside WSL (e.g. C:\\foo\\bar -> /mnt/c/foo/bar).\n\t */\n\tprivate windowsPathToWsl(windowsPath: string): string {\n\t\tconst normalized = windowsPath.replace(/\\\\/g, \"/\").trim();\n\t\tconst driveMatch = normalized.match(/^([a-zA-Z]):\\/?(.*)$/);\n\t\tif (driveMatch) {\n\t\t\tconst drive = driveMatch[1].toLowerCase();\n\t\t\tconst rest = driveMatch[2] || \"\";\n\t\t\treturn `/mnt/${drive}${rest ? `/${rest}` : \"\"}`;\n\t\t}\n\t\treturn normalized;\n\t}\n\n\t/**\n\t * Get the native Apple lockdown directory path for the current platform.\n\t * This is where Apple/iTunes stores pairing records natively.\n\t */\n\tprivate getAppleLockdownPath(): string | null {\n\t\tif (process.platform === \"win32\") {\n\t\t\treturn join(process.env.ProgramData ?? \"C:\\\\ProgramData\", \"Apple\", \"Lockdown\");\n\t\t}\n\t\tif (process.platform === \"darwin\") {\n\t\t\treturn \"/var/db/lockdown\";\n\t\t}\n\t\tif (process.platform === \"linux\") {\n\t\t\treturn \"/var/lib/lockdown\";\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Copy a single device's lockdown plist from the native Apple lockdown directory\n\t * to Alpine so usbmuxd can use it (skip pairing).\n\t *\n\t * Uses the platform-specific Apple/iTunes lockdown directory\n\t * (e.g. C:\\ProgramData\\Apple\\Lockdown on Windows, /var/db/lockdown on macOS).\n\t *\n\t * No-op if the lockdown directory cannot be determined or files don't exist.\n\t */\n\tpublic async syncToAlpine(udid: string): Promise<void> {\n\t\tconst lockdownDir = this.getAppleLockdownPath();\n\t\tif (!lockdownDir) {\n\t\t\tlogWarning(\"Lockdown sync to Alpine: unsupported platform, skipping\");\n\t\t\treturn;\n\t\t}\n\n\t\tlogDetail(`Lockdown sync to Alpine: source dir = ${lockdownDir}, udid = ${udid}`);\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\t\tconst wslDestDir = this.ALPINE_LOCKDOWN_DIR;\n\n\t\t// Sync the device-specific plist\n\t\tconst devicePlistFile = `${udid}.plist`;\n\t\tconst devicePlistPath = join(lockdownDir, devicePlistFile);\n\t\tif (existsSync(devicePlistPath)) {\n\t\t\tawait this.copyFileToAlpine(\n\t\t\t\tthis.windowsPathToWsl(devicePlistPath),\n\t\t\t\twslDestDir,\n\t\t\t\tdistro,\n\t\t\t\tdevicePlistFile\n\t\t\t);\n\t\t} else {\n\t\t\tlogDetail(\n\t\t\t\t`Lockdown sync to Alpine: ${devicePlistFile} not found at ${devicePlistPath}, skipping`\n\t\t\t);\n\t\t}\n\n\t\t// Sync SystemConfiguration.plist so Alpine usbmuxd uses the same SystemBUID\n\t\t// that was used during the original pairing on Windows.\n\t\t// Without this, lockdown sessions fail with InvalidHostID.\n\t\tconst systemConfigPath = join(lockdownDir, this.SYSTEM_CONFIG_PLIST);\n\t\tif (existsSync(systemConfigPath)) {\n\t\t\tawait this.copyFileToAlpine(\n\t\t\t\tthis.windowsPathToWsl(systemConfigPath),\n\t\t\t\twslDestDir,\n\t\t\t\tdistro,\n\t\t\t\tthis.SYSTEM_CONFIG_PLIST\n\t\t\t);\n\t\t} else {\n\t\t\tlogDetail(\n\t\t\t\t`Lockdown sync to Alpine: ${this.SYSTEM_CONFIG_PLIST} not found at ${systemConfigPath}, skipping`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Copy a single file into the Alpine lockdown directory.\n\t * Falls back to sudo if the initial copy fails.\n\t */\n\tprivate async copyFileToAlpine(\n\t\twslSource: string,\n\t\twslDestDir: string,\n\t\tdistro: string,\n\t\tfileName: string\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait execAsync(\n\t\t\t\t`wsl -d ${distro} -- sh -c \"mkdir -p ${wslDestDir} && cp '${wslSource}' '${wslDestDir}/'\"`\n\t\t\t);\n\t\t\tlogInfo(`Lockdown sync: copied ${fileName} to Alpine`);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait execAsync(\n\t\t\t\t\t`wsl -d ${distro} -- sh -c \"mkdir -p ${wslDestDir} && sudo cp '${wslSource}' '${wslDestDir}/'\"`\n\t\t\t\t);\n\t\t\t\tlogInfo(`Lockdown sync: copied ${fileName} to Alpine (via sudo)`);\n\t\t\t} catch (sudoError) {\n\t\t\t\tlogWarning(\n\t\t\t\t\t`Lockdown sync to Alpine failed for ${fileName}: ${error}. Sudo fallback failed: ${sudoError}. Ensure /var/lib/lockdown is writable or use passwordless sudo.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Copy lockdown plists from Alpine back to the native Apple lockdown directory\n\t * (for devices that were assigned this session).\n\t * Also copies SystemConfiguration.plist if present in Alpine.\n\t *\n\t * No-op if the lockdown directory cannot be determined.\n\t */\n\tpublic async syncFromAlpine(udids: string[]): Promise<void> {\n\t\tlogInfo(`Lockdown sync from Alpine: starting (${udids.length} device(s))`);\n\n\t\tconst lockdownDir = this.getAppleLockdownPath();\n\t\tif (!lockdownDir) {\n\t\t\tlogWarning(\"Lockdown sync: unsupported platform, skipping\");\n\t\t\treturn;\n\t\t}\n\n\t\tlogDetail(`Lockdown sync: target dir = ${lockdownDir}`);\n\n\t\ttry {\n\t\t\tmkdirSync(lockdownDir, { recursive: true });\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: could not create lockdown dir ${lockdownDir}: ${error}`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\t\tconst wslDestDir = this.windowsPathToWsl(lockdownDir);\n\n\t\t// List what Alpine actually has in its lockdown dir for debugging\n\t\ttry {\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- ls -la ${this.ALPINE_LOCKDOWN_DIR}/`);\n\t\t\tlogDetail(`Lockdown sync: Alpine ${this.ALPINE_LOCKDOWN_DIR} contents:\\n${stdout}`);\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: could not list Alpine lockdown dir: ${error}`);\n\t\t}\n\n\t\tconst filesToSync = [...udids.map((udid) => `${udid}.plist`), this.SYSTEM_CONFIG_PLIST];\n\n\t\tlogDetail(`Lockdown sync: files to sync = [${filesToSync.join(\", \")}]`);\n\n\t\tfor (const fileName of filesToSync) {\n\t\t\tawait this.copyFileFromAlpine(fileName, wslDestDir, distro, lockdownDir);\n\t\t}\n\n\t\tlogInfo(\"Lockdown sync from Alpine: done\");\n\t}\n\n\t/**\n\t * Copy a single file from the Alpine lockdown directory back to Windows.\n\t * Skips if the file does not exist in Alpine; logs a warning if the copy fails.\n\t */\n\tprivate async copyFileFromAlpine(\n\t\tfileName: string,\n\t\twslDestDir: string,\n\t\tdistro: string,\n\t\tlockdownDir: string\n\t): Promise<void> {\n\t\tconst alpinePath = `${this.ALPINE_LOCKDOWN_DIR}/${fileName}`;\n\n\t\t// Check if the file exists in Alpine first\n\t\ttry {\n\t\t\tawait execAsync(`wsl -d ${distro} -- test -f '${alpinePath}'`);\n\t\t} catch {\n\t\t\tlogDetail(`Lockdown sync: ${fileName} not found in Alpine (${alpinePath}), skipping`);\n\t\t\treturn;\n\t\t}\n\n\t\t// File exists \u2014 copy it to Windows\n\t\tlogDetail(`Lockdown sync: copying ${fileName} \u2192 ${wslDestDir}/`);\n\t\ttry {\n\t\t\tawait execAsync(`wsl -d ${distro} -- cp -f '${alpinePath}' '${wslDestDir}/'`);\n\t\t\tlogInfo(`Lockdown sync: copied ${fileName} from Alpine to ${lockdownDir}`);\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: failed to copy ${fileName} from Alpine to ${lockdownDir}: ${error}`);\n\t\t}\n\t}\n}\n"],
5
- "mappings": ";AAAA,SAAS,gBAAAA,qBAAoB;AAC7B,SAAS,iBAAAC,sBAAqB;AAC9B,OAAO,uBAAuB;;;ACD9B,SAAS,oBAAoB;AAC7B,SAAS,iBAAAC,sBAAqB;;;ACF9B,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAK9B,IAAM,EAAE,SAAS,WAAW,IAAI,cAAc,QAAQ;AAEtD,IAAM,YAAY,UAAU,IAAI;AAEzB,IAAM,SAAN,MAAa;AAAA,EACnB,YAA6B,YAAoB;AAApB;AAAA,EAAqB;AAAA,EAElD,MAAa,0BAAyC;AACrD,QAAI;AACH,YAAM,UAAU,IAAI,KAAK,UAAU,aAAa;AAChD,cAAQ,8BAA8B;AAAA,IACvC,SAAS,OAAO;AACf,iBAAW,0CAA0C,KAAK,EAAE;AAAA,IAC7D;AAAA,EACD;AAAA,EAEA,MAAa,0BAAyC;AACrD,QAAI;AACH,YAAM,UAAU,IAAI,KAAK,UAAU,aAAa;AAChD,cAAQ,+BAA+B;AAAA,IACxC,SAAS,OAAO;AACf,iBAAW,0CAA0C,KAAK,EAAE;AAAA,IAC7D;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,oBAAoB,OAA8B;AAC9D,QAAI;AACH,YAAM,UAAU,IAAI,KAAK,UAAU,oBAAoB,KAAK,EAAE;AAC9D,cAAQ,UAAU,KAAK,oBAAoB;AAAA,IAC5C,SAAS,OAAO;AACf,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAI,QAAQ,SAAS,sBAAsB,KAAK,QAAQ,SAAS,oBAAoB,GAAG;AACvF,gBAAQ,UAAU,KAAK,mBAAmB;AAAA,MAC3C,OAAO;AACN,mBAAW,2BAA2B,KAAK,KAAK,KAAK,EAAE;AAAA,MACxD;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,kBAAkB,OAAe,KAAW,QAAkC;AAC1F,QAAI;AAEH,YAAM,IAAI,iBAAiB;AAG3B,cAAQ,kBAAkB,KAAK,KAAK;AACpC,YAAM,UAAU,IAAI,KAAK,UAAU,kBAAkB,KAAK,UAAU;AAGpE,UAAI,QAAQ;AACX,gBAAQ,oBAAoB,KAAK,wBAAwB,MAAM,KAAK;AACpE,cAAM,UAAU,IAAI,KAAK,UAAU,kBAAkB,MAAM,YAAY,KAAK,EAAE;AAAA,MAC/E,OAAO;AACN,gBAAQ,oBAAoB,KAAK,oBAAoB;AACrD,cAAM,UAAU,IAAI,KAAK,UAAU,0BAA0B,KAAK,EAAE;AAAA,MACrE;AAEA,cAAQ,UAAU,KAAK,+BAA+B;AACtD,aAAO;AAAA,IACR,SAAS,OAAO;AACf,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAErE,UAAI,QAAQ,SAAS,8BAA8B,GAAG;AACrD,gBAAQ,UAAU,KAAK,yCAAyC;AAChE,eAAO;AAAA,MACR;AACA,iBAAW,2BAA2B,KAAK,YAAY,KAAK,EAAE;AAC9D,aAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,QAAgC;AACvD,UAAM,UAA0B,CAAC;AAEjC,UAAM,QAAQ,OAAO,MAAM,OAAO;AAElC,eAAW,QAAQ,OAAO;AAGzB,YAAM,QAAQ,KAAK,MAAM,mDAAmD;AAC5E,UAAI,OAAO;AAEV,cAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,cAAM,aAAa,KAAK,MAAM,qBAAqB;AACnD,cAAM,cAAc,aAAa,WAAW,CAAC,EAAE,KAAK,IAAI;AACxD,cAAM,QAAQ,aAAa,WAAW,CAAC,EAAE,KAAK,IAAI;AAElD,gBAAQ,KAAK;AAAA,UACZ,OAAO,MAAM,CAAC;AAAA,UACd,KAAK,MAAM,CAAC,EAAE,YAAY;AAAA,UAC1B,KAAK,MAAM,CAAC,EAAE,YAAY;AAAA,UAC1B;AAAA,UACA;AAAA,QACD,CAAC;AAAA,MACF;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAIA,MAAa,mBAAmB,QAAqC;AACpE,QAAI;AACH,YAAM,EAAE,OAAO,IAAI,MAAM,UAAU,IAAI,KAAK,UAAU,QAAQ;AAC9D,YAAM,gBAAgB,KAAK,gBAAgB,MAAM;AAGjD,YAAM,YAAY,OAAO,IAAI,SAAS,EAAE,EAAE,YAAY,EAAE,SAAS,GAAG,GAAG;AACvE,YAAM,YAAY,OAAO,IAAI,SAAS,EAAE,EAAE,YAAY,EAAE,SAAS,GAAG,GAAG;AAEvE,cAAQ,mCAAmC,SAAS,IAAI,SAAS,EAAE;AACnE,cAAQ,SAAS,cAAc,MAAM,2BAA2B;AAGhE,YAAM,QAAQ,cAAc,KAAK,CAAC,MAAM,EAAE,QAAQ,aAAa,EAAE,QAAQ,SAAS;AAElF,UAAI,OAAO;AACV;AAAA,UACC,uBAAuB,MAAM,KAAK,eAAe,OAAO,QAAQ,KAAK,SAAS,IAAI,SAAS;AAAA,QAC5F;AACA,eAAO,MAAM;AAAA,MACd;AAEA,YAAM,IAAI;AAAA,QACT,2CAA2C,OAAO,QAAQ,KAAK,SAAS,IAAI,SAAS;AAAA,MACtF;AAAA,IACD,SAAS,OAAO;AACf,YAAM,IAAI,MAAM,8BAA8B,KAAK,EAAE;AAAA,IACtD;AAAA,EACD;AACD;;;ACpJA,SAA4B,QAAAC,OAAM,aAAa;AAC/C,SAAS,YAAY,iBAAiB;AACtC,SAAS,YAAY;AACrB,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,iBAAAC,sBAAqB;AAG9B,IAAM,EAAE,SAAAC,UAAS,YAAAC,aAAY,UAAU,IAAIF,eAAc,KAAK;AAE9D,IAAMG,aAAYJ,WAAUD,KAAI;AAEzB,IAAM,OAAN,MAAW;AAAA,EAMjB,YAAoB,iBAAqC;AAArC;AAAA,EAAsC;AAAA,EALzC,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EAE/B,eAA8B;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtC,MAAa,qBAAsC;AAClD,QAAI,KAAK,cAAc;AACtB,aAAO,KAAK;AAAA,IACb;AAEA,UAAM,SAAS,KAAK,mBAAmB;AAEvC,QAAI;AAEH,YAAM,EAAE,OAAO,IAAI,MAAMK,WAAU,UAAU,MAAM,0BAA0B;AAC7E,YAAM,QAAQ,OAAO,MAAM,6BAA6B;AACxD,UAAI,OAAO;AACV,aAAK,eAAe,MAAM,CAAC;AAC3B,QAAAF,SAAQ,4BAA4B,KAAK,YAAY,EAAE;AACvD,eAAO,KAAK;AAAA,MACb;AAAA,IACD,SAAS,OAAO;AACf,MAAAC,YAAW,wCAAwC,KAAK,EAAE;AAAA,IAC3D;AAGA,QAAI;AACH,YAAM,EAAE,OAAO,IAAI,MAAMC,WAAU,UAAU,MAAM,iBAAiB;AACpE,YAAM,KAAK,OAAO,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC;AACvC,UAAI,MAAM,uBAAuB,KAAK,EAAE,GAAG;AAC1C,aAAK,eAAe;AACpB,QAAAF,SAAQ,uCAAuC,KAAK,YAAY,EAAE;AAClE,eAAO,KAAK;AAAA,MACb;AAAA,IACD,SAAS,OAAO;AACf,MAAAC,YAAW,yCAAyC,KAAK,EAAE;AAAA,IAC5D;AAGA,IAAAA,YAAW,oDAAoD;AAC/D,SAAK,eAAe;AACpB,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,mBAAqC;AACjD,UAAM,SAAS,KAAK;AAEpB,QAAI;AACH,UAAI,QAAQ;AAEX,QAAAD,SAAQ,8BAA8B,MAAM,KAAK;AACjD,cAAME,WAAU,UAAU,MAAM,wBAAwB;AAAA,MACzD,OAAO;AAEN,QAAAF,SAAQ,sCAAsC;AAC9C,cAAME,WAAU,2BAA2B;AAAA,MAC5C;AACA,aAAO;AAAA,IACR,SAAS,OAAO;AACf,MAAAD,YAAW,wBAAwB,KAAK,EAAE;AAC1C,aAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,eAAe;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,GAOkE;AACjE,UAAM,OAAO,WAAW,KAAK;AAE7B,IAAAD,SAAQ,qBAAqB,EAAE,YAAY,IAAI,EAAE;AAGjD,UAAM,OAAO,MAAM,KAAK,mBAAmB;AAG3C,UAAM,cAAc;AAAA,MACnB;AAAA;AAAA,MACA;AAAA;AAAA,MACA;AAAA,MACA,WAAW,IAAI;AAAA;AAAA,MACf;AAAA,MACA;AAAA,IACD;AAEA,QAAI,CAAC,gBAAgB;AAEpB,kBAAY,OAAO,GAAG,CAAC;AAAA,IACxB;AAGA,UAAM,UAAU;AAAA,MACf;AAAA,MACA,KAAK,mBAAmB;AAAA,MACxB;AAAA,MACA,GAAG;AAAA,IACJ;AAOA,UAAMG,WAAU,MAAM,OAAO,SAAS;AAAA,MACrC,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,MAChC,UAAU;AAAA,MACV,aAAa;AAAA,IACd,CAAC;AAGD,IAAAA,SAAQ,QAAQ,GAAG,QAAQ,CAAC,SAAS;AACpC,YAAM,KAAK,SAAS,EAAE,KAAK,CAAC;AAAA,IAC7B,CAAC;AAED,IAAAA,SAAQ,QAAQ,GAAG,QAAQ,CAAC,SAAS;AACpC,YAAM,KAAK,SAAS,EAAE,KAAK,CAAC;AAAA,IAC7B,CAAC;AAGD,IAAAA,SAAQ,GAAG,QAAQ,MAAM;AAGzB,UAAM,MAAMA,SAAQ;AACpB,QAAI,QAAQ,QAAW;AACtB,MAAAA,SAAQ,KAAK,SAAS;AACtB,YAAM,IAAI,MAAM,wCAAwC;AAAA,IACzD;AACA,UAAM,WAA4B;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,CAAC;AAAA,MACd,WAAW,oBAAI,KAAK;AAAA,IACrB;AAEA,WAAO,EAAE,UAAU,SAAAA,SAAQ;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,aAA6B;AACrD,UAAM,aAAa,YAAY,QAAQ,OAAO,GAAG,EAAE,KAAK;AACxD,UAAM,aAAa,WAAW,MAAM,sBAAsB;AAC1D,QAAI,YAAY;AACf,YAAM,QAAQ,WAAW,CAAC,EAAE,YAAY;AACxC,YAAM,OAAO,WAAW,CAAC,KAAK;AAC9B,aAAO,QAAQ,KAAK,GAAG,OAAO,IAAI,IAAI,KAAK,EAAE;AAAA,IAC9C;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,uBAAsC;AAC7C,QAAI,QAAQ,aAAa,SAAS;AACjC,aAAO,KAAK,QAAQ,IAAI,eAAe,mBAAmB,SAAS,UAAU;AAAA,IAC9E;AACA,QAAI,QAAQ,aAAa,UAAU;AAClC,aAAO;AAAA,IACR;AACA,QAAI,QAAQ,aAAa,SAAS;AACjC,aAAO;AAAA,IACR;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAa,aAAa,MAA6B;AACtD,UAAM,cAAc,KAAK,qBAAqB;AAC9C,QAAI,CAAC,aAAa;AACjB,MAAAF,YAAW,yDAAyD;AACpE;AAAA,IACD;AAEA,cAAU,yCAAyC,WAAW,YAAY,IAAI,EAAE;AAEhF,UAAM,SAAS,KAAK,mBAAmB;AACvC,UAAM,aAAa,KAAK;AAGxB,UAAM,kBAAkB,GAAG,IAAI;AAC/B,UAAM,kBAAkB,KAAK,aAAa,eAAe;AACzD,QAAI,WAAW,eAAe,GAAG;AAChC,YAAM,KAAK;AAAA,QACV,KAAK,iBAAiB,eAAe;AAAA,QACrC;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,IACD,OAAO;AACN;AAAA,QACC,4BAA4B,eAAe,iBAAiB,eAAe;AAAA,MAC5E;AAAA,IACD;AAKA,UAAM,mBAAmB,KAAK,aAAa,KAAK,mBAAmB;AACnE,QAAI,WAAW,gBAAgB,GAAG;AACjC,YAAM,KAAK;AAAA,QACV,KAAK,iBAAiB,gBAAgB;AAAA,QACtC;AAAA,QACA;AAAA,QACA,KAAK;AAAA,MACN;AAAA,IACD,OAAO;AACN;AAAA,QACC,4BAA4B,KAAK,mBAAmB,iBAAiB,gBAAgB;AAAA,MACtF;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBACb,WACA,YACA,QACA,UACgB;AAChB,QAAI;AACH,YAAMC;AAAA,QACL,UAAU,MAAM,uBAAuB,UAAU,WAAW,SAAS,MAAM,UAAU;AAAA,MACtF;AACA,MAAAF,SAAQ,yBAAyB,QAAQ,YAAY;AAAA,IACtD,SAAS,OAAO;AACf,UAAI;AACH,cAAME;AAAA,UACL,UAAU,MAAM,uBAAuB,UAAU,gBAAgB,SAAS,MAAM,UAAU;AAAA,QAC3F;AACA,QAAAF,SAAQ,yBAAyB,QAAQ,uBAAuB;AAAA,MACjE,SAAS,WAAW;AACnB,QAAAC;AAAA,UACC,sCAAsC,QAAQ,KAAK,KAAK,2BAA2B,SAAS;AAAA,QAC7F;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,eAAe,OAAgC;AAC3D,IAAAD,SAAQ,wCAAwC,MAAM,MAAM,aAAa;AAEzE,UAAM,cAAc,KAAK,qBAAqB;AAC9C,QAAI,CAAC,aAAa;AACjB,MAAAC,YAAW,+CAA+C;AAC1D;AAAA,IACD;AAEA,cAAU,+BAA+B,WAAW,EAAE;AAEtD,QAAI;AACH,gBAAU,aAAa,EAAE,WAAW,KAAK,CAAC;AAAA,IAC3C,SAAS,OAAO;AACf,MAAAA,YAAW,gDAAgD,WAAW,KAAK,KAAK,EAAE;AAClF;AAAA,IACD;AAEA,UAAM,SAAS,KAAK,mBAAmB;AACvC,UAAM,aAAa,KAAK,iBAAiB,WAAW;AAGpD,QAAI;AACH,YAAM,EAAE,OAAO,IAAI,MAAMC,WAAU,UAAU,MAAM,cAAc,KAAK,mBAAmB,GAAG;AAC5F,gBAAU,yBAAyB,KAAK,mBAAmB;AAAA,EAAe,MAAM,EAAE;AAAA,IACnF,SAAS,OAAO;AACf,MAAAD,YAAW,sDAAsD,KAAK,EAAE;AAAA,IACzE;AAEA,UAAM,cAAc,CAAC,GAAG,MAAM,IAAI,CAAC,SAAS,GAAG,IAAI,QAAQ,GAAG,KAAK,mBAAmB;AAEtF,cAAU,mCAAmC,YAAY,KAAK,IAAI,CAAC,GAAG;AAEtE,eAAW,YAAY,aAAa;AACnC,YAAM,KAAK,mBAAmB,UAAU,YAAY,QAAQ,WAAW;AAAA,IACxE;AAEA,IAAAD,SAAQ,iCAAiC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBACb,UACA,YACA,QACA,aACgB;AAChB,UAAM,aAAa,GAAG,KAAK,mBAAmB,IAAI,QAAQ;AAG1D,QAAI;AACH,YAAME,WAAU,UAAU,MAAM,gBAAgB,UAAU,GAAG;AAAA,IAC9D,QAAQ;AACP,gBAAU,kBAAkB,QAAQ,yBAAyB,UAAU,aAAa;AACpF;AAAA,IACD;AAGA,cAAU,0BAA0B,QAAQ,WAAM,UAAU,GAAG;AAC/D,QAAI;AACH,YAAMA,WAAU,UAAU,MAAM,cAAc,UAAU,MAAM,UAAU,IAAI;AAC5E,MAAAF,SAAQ,yBAAyB,QAAQ,mBAAmB,WAAW,EAAE;AAAA,IAC1E,SAAS,OAAO;AACf,MAAAC,YAAW,iCAAiC,QAAQ,mBAAmB,WAAW,KAAK,KAAK,EAAE;AAAA,IAC/F;AAAA,EACD;AACD;;;AFhWA,IAAM,EAAE,YAAAG,aAAY,WAAAC,YAAW,eAAe,QAAQ,IAAIC,eAAc,kBAAkB;AAK1F,IAAM,iBAAwC;AAAA,EAC7C,WAAW;AAAA,EACX,UAAU;AAAA,EACV,cAAc;AAAA,EACd,aAAa;AAAA;AAAA,EACb,iBAAiB;AAAA;AAAA,EACjB,gBAAgB;AAAA,EAChB,eAAe;AAChB;AAiBO,IAAM,kBAAN,cAA8B,aAAa;AAAA;AAAA,EAEzC,YAA0C,oBAAI,IAAI;AAAA;AAAA,EAElD,iBAA6C,oBAAI,IAAI;AAAA;AAAA,EAErD,YAAuC,oBAAI,IAAI;AAAA;AAAA,EAE/C,kBAA+B,oBAAI,IAAI;AAAA;AAAA;AAAA,EAGvC,uBAAoC,oBAAI,IAAI;AAAA,EAE5C;AAAA,EACA,iBAAiB;AAAA,EACjB,YAAyB;AAAA,EACzB,YAAY;AAAA,EAEZ;AAAA,EACA;AAAA,EACR,YAAY,SAAyC,CAAC,GAAG;AACxD,UAAM;AACN,SAAK,SAAS,EAAE,GAAG,gBAAgB,GAAG,OAAO;AAC7C,SAAK,MAAM,IAAI,KAAK,KAAK,OAAO,eAAe;AAC/C,SAAK,SAAS,IAAI,OAAO,QAAQ,IAAI,eAAe,QAAQ;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKO,QAAc;AACpB,QAAI,KAAK,WAAW;AACnB,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACtD;AAEA,SAAK,YAAY;AACjB,SAAK,YAAY,oBAAI,KAAK;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,OAAsB;AAClC,QAAI,CAAC,KAAK,WAAW;AACpB;AAAA,IACD;AAEA,SAAK,YAAY;AAGjB,UAAM,QAAQ,MAAM,KAAK,KAAK,eAAe,KAAK,CAAC;AACnD,UAAM,KAAK,IAAI,eAAe,KAAK;AAKnC,UAAM,KAAK,OAAO,wBAAwB;AAC1C,UAAM,KAAK,OAAO,wBAAwB;AAC1C,SAAK,gBAAgB,MAAM;AAG3B,UAAM,eAAe,MAAM,KAAK,KAAK,UAAU,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,KAAK,aAAa,EAAE,CAAC;AAExF,UAAM,QAAQ,IAAI,YAAY;AAE9B,SAAK,UAAU,MAAM;AACrB,SAAK,eAAe,MAAM;AAC1B,SAAK,UAAU,MAAM;AAAA,EACtB;AAAA,EAEA,MAAc,YAAY;AAAA,IACzB;AAAA,IACA;AAAA,EACD,GAAuD;AACtD,QAAI,CAAC,KAAK,gBAAgB,IAAI,KAAK,GAAG;AACrC,WAAK,qBAAqB,IAAI,QAAQ;AACtC,UAAI,CAAC,KAAK,OAAO,iBAAiB;AACjC,cAAM,IAAI,MAAM,iCAAiC;AAAA,MAClD;AACA,YAAM,WAAW,MAAM,KAAK,OAAO;AAAA,QAClC;AAAA,QACA,KAAK;AAAA,QACL,KAAK,OAAO;AAAA,MACb;AACA,UAAI,UAAU;AACb,aAAK,gBAAgB,IAAI,KAAK;AAAA,MAC/B;AAAA,IACD;AAAA,EACD;AAAA,EAEA,MAAc,aAAa,SAAkD;AAC5E,UAAM,WAAW,MAAM,KAAK,cAAc,EAAE,UAAU,QAAQ,KAAK,CAAC;AACpE,QAAI,CAAC,UAAU;AACd,YAAM,IAAI,MAAM,YAAY,QAAQ,UAAU,YAAY;AAAA,IAC3D;AACA,QAAI,QAAQ,OAAO;AAClB,YAAM,KAAK,OAAO,oBAAoB,QAAQ,KAAK;AACnD,WAAK,gBAAgB,OAAO,QAAQ,KAAK;AAAA,IAC1C;AACA,UAAM,cAAc,SAAS,YAAY,QAAQ,QAAQ,IAAI;AAC7D,QAAI,cAAc,IAAI;AACrB,eAAS,YAAY,OAAO,aAAa,CAAC;AAAA,IAC3C;AAEA,SAAK,eAAe,OAAO,QAAQ,IAAI;AACvC,WAAO;AAAA,EACR;AAAA,EAEA,MAAc,qBAAqB;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,EACD,GAAwE;AACvE,SAAK,KAAK,mBAAmB;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,IACD,CAAC;AACD,eAAW,CAAC,GAAG,OAAO,KAAK,KAAK,eAAe,QAAQ,GAAG;AACzD,UAAI,QAAQ,eAAe,YAAY;AACtC,aAAK,aAAa,OAAO;AAAA,MAC1B;AAAA,IACD;AACA,SAAK,UAAU,OAAO,UAAU;AAChC,SAAK,UAAU,IAAI,UAAU,GAAG,KAAK;AACrC,SAAK,UAAU,OAAO,UAAU;AAAA,EACjC;AAAA,EAEA,MAAc,uBAAuB;AAAA,IACpC;AAAA,IACA,SAAAC;AAAA,EACD,GAAwE;AACvE,SAAK,UAAU,IAAI,SAAS,IAAI,QAAQ;AACxC,SAAK,UAAU,IAAI,SAAS,IAAIA,QAAO;AACvC,SAAK,KAAK,oBAAoB;AAAA,MAC7B,YAAY,SAAS;AAAA,IACtB,CAAC;AAAA,EACF;AAAA,EAEA,MAAc,cAAc;AAC3B,QAAI,iBAAiB,KAAK,yBAAyB;AAGnD,QAAI,CAAC,gBAAgB;AACpB,UAAI,KAAK,UAAU,QAAQ,KAAK,OAAO,cAAc;AACpD,cAAM,IAAI,MAAM,gCAAgC,KAAK,OAAO,YAAY,WAAW;AAAA,MACpF;AAEA,YAAM,gBAAgB,KAAK;AAE3B,UAAI,SAAS,CAAC,MAAc,WAAmB;AAC9C,aAAK,qBAAqB,EAAE,YAAY,eAAe,MAAM,OAAO,CAAC;AAAA,MACtE;AAEA,eAAS,OAAO,KAAK,IAAI;AAEzB,YAAM,EAAE,UAAU,SAAAA,SAAQ,IAAI,MAAM,KAAK,IAAI,eAAe;AAAA,QAC3D,IAAI;AAAA,QACJ,UAAU,KAAK,OAAO;AAAA,QACtB,gBAAgB,KAAK,OAAO;AAAA,QAC5B,aAAa,KAAK,OAAO;AAAA,QACzB,OAAO,CAAC,YACP,KAAK,KAAK,gBAAgB;AAAA,UACzB,YAAY;AAAA,UACZ,OAAO;AAAA,UACP;AAAA,QACD,CAAC;AAAA,QACF;AAAA,MACD,CAAC;AACD,WAAK,uBAAuB,EAAE,UAAU,SAAAA,SAAQ,CAAC;AACjD,uBAAiB;AAAA,IAClB;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,kBAAkB,QAAmC;AACjE,UAAM,QAAQ,MAAM,KAAK,OAAO,mBAAmB,MAAM;AAEzD,QAAI,KAAK,gBAAgB,IAAI,KAAK,GAAG;AACpC,cAAQ,UAAU,OAAO,QAAQ,qDAAqD;AACtF;AAAA,IACD;AAEA,kBAAc,oBAAoB,EAAE,OAAO,CAAC;AAC5C,UAAM,KAAK,IAAI,aAAa,OAAO,QAAQ;AAE3C,UAAM,KAAK,YAAY,EAAE,OAAO,UAAU,OAAO,SAAS,CAAC;AAE3D,UAAM,iBAAiB,MAAM,KAAK,YAAY;AAG9C,UAAM,UAAyB;AAAA,MAC9B,MAAM,OAAO;AAAA,MACb,YAAY,eAAe;AAAA,MAC3B,MAAM,eAAe;AAAA,MACrB,MAAM,eAAe;AAAA,MACrB,SAAS,oBAAI,KAAK;AAAA,MAClB,OAAO,SAAS;AAAA,IACjB;AAEA,SAAK,eAAe,IAAI,OAAO,UAAU,OAAO;AAGhD,mBAAe,YAAY,KAAK,OAAO,QAAQ;AAE/C,SAAK,KAAK,mBAAmB;AAAA,MAC5B;AAAA,MACA,UAAU;AAAA,MACV;AAAA,IACD,CAAC;AAED,SAAK,qBAAqB,OAAO,OAAO,QAAQ;AAAA,EACjD;AAAA,EAEA,MAAc,cAAc,EAAE,SAAS,GAAmD;AACzF,UAAM,UAAU,KAAK,eAAe,IAAI,QAAQ;AAChD,QAAI,CAAC,SAAS;AACb,YAAM,IAAI,MAAM,UAAU,QAAQ,kBAAkB;AAAA,IACrD;AAGA,QAAI,QAAQ,OAAO;AAClB,YAAM,KAAK,OAAO,oBAAoB,QAAQ,KAAK;AACnD,WAAK,gBAAgB,OAAO,QAAQ,KAAK;AAAA,IAC1C;AAEA,UAAM,WAAW,KAAK,UAAU,IAAI,QAAQ,UAAU;AACtD,QAAI,CAAC,UAAU;AACd,MAAAH,YAAW,YAAY,QAAQ,UAAU,YAAY;AACrD,WAAK,eAAe,OAAO,QAAQ;AACnC,YAAM,IAAI,MAAM,YAAY,QAAQ,UAAU,YAAY;AAAA,IAC3D;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,qBAAqB,QAAmC;AAEpE,QAAI,CAAC,KAAK,gBAAgB,IAAI,OAAO,QAAQ,GAAG;AAC/C,cAAQ,UAAU,OAAO,QAAQ,oDAAoD;AACrF;AAAA,IACD;AACA,kBAAc,uBAAuB,EAAE,OAAO,CAAC;AAE/C,UAAM,UAAU,KAAK,eAAe,IAAI,OAAO,QAAQ;AACvD,QAAI,CAAC,SAAS;AACb,YAAM,IAAI,MAAM,UAAU,OAAO,QAAQ,kBAAkB;AAAA,IAC5D;AAEA,UAAM,WAAW,MAAM,KAAK,aAAa,OAAO;AAEhD,SAAK,KAAK,kBAAkB;AAAA,MAC3B;AAAA,MACA;AAAA,IACD,CAAC;AAGD,QAAI,SAAS,YAAY,WAAW,GAAG;AACtC,MAAAC,WAAU,YAAY,SAAS,EAAE,wBAAwB;AACzD,YAAM,KAAK,aAAa,SAAS,EAAE;AAAA,IACpC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKQ,2BAAmD;AAC1D,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC/C,UAAI,SAAS,YAAY,SAAS,KAAK,OAAO,WAAW;AACxD,eAAO;AAAA,MACR;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,aAAa,YAAmC;AAC7D,UAAM,WAAW,KAAK,UAAU,IAAI,UAAU;AAC9C,UAAM,eAAe,KAAK,UAAU,IAAI,UAAU;AAElD,QAAI,CAAC,YAAY,CAAC,cAAc;AAC/B;AAAA,IACD;AAEA,eAAWE,YAAW,KAAK,UAAU,OAAO,GAAG;AAC9C,MAAAA,SAAQ,KAAK,SAAS;AAAA,IACvB;AAGA,SAAK,UAAU,OAAO,UAAU;AAChC,SAAK,UAAU,OAAO,UAAU;AAGhC,eAAW,CAAC,MAAM,OAAO,KAAK,KAAK,eAAe,QAAQ,GAAG;AAC5D,UAAI,QAAQ,eAAe,YAAY;AACtC,aAAK,eAAe,OAAO,IAAI;AAAA,MAChC;AAAA,IACD;AAEA,SAAK,KAAK,oBAAoB,QAAQ;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKO,cAAc,MAA6B;AACjD,UAAM,UAAU,KAAK,eAAe,IAAI,IAAI;AAC5C,WAAO,UAAU,QAAQ,OAAO;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKO,oBAAqC;AAC3C,WAAO,MAAM,KAAK,KAAK,eAAe,OAAO,CAAC;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKO,eAAkC;AACxC,WAAO,MAAM,KAAK,KAAK,UAAU,OAAO,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKO,WAAyB;AAC/B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,YAAY,KAAK,WAAW,QAAQ,KAAK;AAC/C,UAAM,gBAAgB,KAAK,OAAO,MAAM,aAAa,GAAI;AAEzD,WAAO;AAAA,MACN,eAAe,KAAK,UAAU;AAAA,MAC9B,aAAa,KAAK,eAAe;AAAA,MACjC;AAAA,MACA,WAAW,KAAK,aAAa,oBAAI,KAAK;AAAA,IACvC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKO,YAAmC;AACzC,WAAO,EAAE,GAAG,KAAK,OAAO;AAAA,EACzB;AACD;;;ADxYA,IAAM,EAAE,SAAAC,UAAS,UAAU,eAAAC,eAAc,IAAIC,eAAc,iBAAiB;AAQrE,IAAM,iBAAN,cAA6BC,cAAa;AAAA,EACxC;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EAEtB,YAAY,SAAyC,CAAC,GAAG;AACxD,UAAM;AACN,SAAK,UAAU,IAAI,gBAAgB,MAAM;AACzC,SAAK,cAAc,IAAI,kBAAkB;AACzC,SAAK,mBAAmB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAA2B;AAClC,SAAK,QAAQ,GAAG,oBAAoB,CAAC,aAA8B;AAClE,WAAK,KAAK,oBAAoB,QAAQ;AAAA,IACvC,CAAC;AAED,SAAK,QAAQ,GAAG,oBAAoB,CAAC,aAA8B;AAClE,WAAK,KAAK,oBAAoB,QAAQ;AAAA,IACvC,CAAC;AAED,SAAK,QAAQ;AAAA,MACZ;AAAA,MACA,CAAC,YAAuF;AACvF,QAAAH;AAAA,UACC,UAAU,QAAQ,OAAO,QAAQ,qCAAqC,QAAQ,QAAQ,IAAI,IAAI,QAAQ,QAAQ,IAAI;AAAA,QACnH;AACA,aAAK,KAAK,mBAAmB,OAAO;AAAA,MACrC;AAAA,IACD;AAEA,SAAK,QAAQ;AAAA,MACZ;AAAA,MACA,CAAC,YAA+D;AAC/D,aAAK,KAAK,kBAAkB,OAAO;AAAA,MACpC;AAAA,IACD;AAEA,SAAK,QAAQ;AAAA,MACZ;AAAA,MACA,CAAC,YAAoE;AACpE,aAAK,KAAK,gBAAgB,OAAO;AAAA,MAClC;AAAA,IACD;AAEA,SAAK,QAAQ;AAAA,MACZ;AAAA,MACA,CAAC,YAAgF;AAChF,aAAK,KAAK,mBAAmB,OAAO;AAAA,MACrC;AAAA,IACD;AAEA,SAAK,QAAQ,GAAG,iBAAiB,CAAC,YAAsD;AACvF,WAAK,KAAK,iBAAiB,OAAO;AAAA,IACnC,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,QAAc;AACpB,QAAI,KAAK,aAAa;AACrB,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC7C;AAEA,UAAM,SAAS,KAAK,QAAQ,UAAU;AAGtC,UAAM,WAAW,OAAO,SAAS,OAAO,eAAe,EAAE;AAEzD,IAAAC,eAAc,uBAAuB,EAAE,OAAO,CAAC;AAG/C,SAAK,YAAY,YAAY,OAAO,WAAuB;AAE1D,UAAI,OAAO,QAAQ,UAAU;AAC5B;AAAA,MACD;AAEA,MAAAD,SAAQ,2BAA2B,OAAO,QAAQ,KAAK,OAAO,cAAc,SAAS,GAAG;AAExF,UAAI;AACH,cAAM,KAAK,QAAQ,kBAAkB,MAAM;AAAA,MAC5C,SAAS,OAAO;AACf,iBAAS,qCAAqC,KAAK;AAAA,MACpD;AAAA,IACD,CAAC;AAED,SAAK,YAAY,eAAe,OAAO,WAAuB;AAE7D,UAAI,OAAO,QAAQ,UAAU;AAC5B;AAAA,MACD;AAEA,MAAAA,SAAQ,8BAA8B,OAAO,QAAQ,EAAE;AAEvD,UAAI;AACH,cAAM,KAAK,QAAQ,qBAAqB,MAAM;AAAA,MAC/C,SAAS,OAAO;AACf,iBAAS,wCAAwC,KAAK;AAAA,MACvD;AAAA,IACD,CAAC;AAGD,QAAI;AACH,WAAK,YAAY,eAAe;AAAA,QAC/B,eAAe,CAAC;AAAA;AAAA,MACjB,CAAC;AAED,WAAK,cAAc;AACnB,WAAK,QAAQ,MAAM;AAEnB,MAAAA,SAAQ,8BAA8B;AACtC,MAAAA,SAAQ,4BAA4B;AAAA,IACrC,SAAS,OAAO;AACf,eAAS,iCAAiC,KAAK;AAC/C,YAAM;AAAA,IACP;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,OAAsB;AAClC,QAAI,CAAC,KAAK,aAAa;AACtB;AAAA,IACD;AAEA,IAAAA,SAAQ,qBAAqB;AAG7B,SAAK,YAAY,cAAc;AAC/B,SAAK,cAAc;AAGnB,UAAM,KAAK,QAAQ,KAAK;AAExB,IAAAA,SAAQ,iBAAiB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKO,cAAc,MAA6B;AACjD,WAAO,KAAK,QAAQ,cAAc,IAAI;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKO,oBAAoB;AAC1B,WAAO,KAAK,QAAQ,kBAAkB;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKO,eAAe;AACrB,WAAO,KAAK,QAAQ,aAAa;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKO,WAAW;AACjB,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKO,YAAY;AAClB,WAAO,KAAK,QAAQ,UAAU;AAAA,EAC/B;AACD;",
4
+ "sourcesContent": ["import { EventEmitter } from \"node:events\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type { DeviceInfo } from \"@mcesystems/usb-device-listener\";\nimport { InstanceManager } from \"./InstanceManager.js\";\nimport type { DeviceMapping, InstanceManagerConfig, UsbmuxdInstance } from \"./types/index.js\";\n\nconst { logInfo, logError, logDataObject } = createLoggers(\"usbmuxd-service\");\n\n/**\n * Main service that integrates usb-device-listener with InstanceManager\n * Monitors iOS devices and manages usbmuxd instances automatically.\n * Extends EventEmitter: forwards instance-started, instance-stopped, device-assigned,\n * device-removed, instance-log, instance-exited from the underlying manager.\n */\nexport class UsbmuxdService extends EventEmitter {\n\tprivate manager: InstanceManager;\n\tprivate isListening = false;\n\n\tconstructor(config: Partial<InstanceManagerConfig> = {}) {\n\t\tsuper();\n\t\tthis.manager = new InstanceManager(config);\n\t\tthis.setupEventHandlers();\n\t}\n\n\t/**\n\t * Forward manager events to service subscribers (no logging; CLI/examples handle logging).\n\t */\n\tprivate setupEventHandlers(): void {\n\t\tthis.manager.on(\"instance-started\", (instance: UsbmuxdInstance) => {\n\t\t\tthis.emit(\"instance-started\", instance);\n\t\t});\n\n\t\tthis.manager.on(\"instance-stopped\", (instance: UsbmuxdInstance) => {\n\t\t\tthis.emit(\"instance-stopped\", instance);\n\t\t});\n\n\t\tthis.manager.on(\n\t\t\t\"device-assigned\",\n\t\t\t(payload: { device: DeviceInfo; instance: UsbmuxdInstance; mapping: DeviceMapping; }) => {\n\t\t\t\tlogInfo(\n\t\t\t\t\t`Device ${payload.device.deviceId} connected to usbmuxd instance at ${payload.mapping.host}:${payload.mapping.port}`\n\t\t\t\t);\n\t\t\t\tthis.emit(\"device-assigned\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"device-removed\",\n\t\t\t(payload: { device: DeviceInfo; instance: UsbmuxdInstance; }) => {\n\t\t\t\tthis.emit(\"device-removed\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"instance-log\",\n\t\t\t(payload: { instanceId: number; level: string; message: string; }) => {\n\t\t\t\tthis.emit(\"instance-log\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\n\t\t\t\"instance-exited\",\n\t\t\t(payload: { instanceId: number; code: number | null; signal: string | null; }) => {\n\t\t\t\tthis.emit(\"instance-exited\", payload);\n\t\t\t}\n\t\t);\n\n\t\tthis.manager.on(\"device-paired\", (payload: { udid: string; mapping: DeviceMapping; }) => {\n\t\t\tthis.emit(\"device-paired\", payload);\n\t\t});\n\t}\n\n\t/**\n\t * Start the service\n\t * Begins monitoring for iOS devices\n\t */\n\tpublic start() {\n\t\tif (this.isListening) {\n\t\t\tthrow new Error(\"Service is already running\");\n\t\t}\n\n\t\tconst config = this.manager.getConfig();\n\n\t\tlogDataObject(\"Starting service...\", { config });\n\n\t\t// Start listening for USB events\n\t\ttry {\n\n\t\t\tthis.isListening = true;\n\t\t\tthis.manager.start();\n\n\t\t\tlogInfo(\"Service started successfully\");\n\t\t\tlogInfo(\"Waiting for iOS devices...\");\n\t\t\tconst onAdd = this.manager.onDeviceConnected.bind(this.manager);\n\t\t\tconst onRemove = this.manager.onDeviceDisconnected.bind(this.manager);\n\t\t\treturn {\n\t\t\t\tonAdd,\n\t\t\t\tonRemove,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tlogError(\"Failed to start USB listener:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Stop the service\n\t * Stops monitoring and terminates all instances\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tif (!this.isListening) {\n\t\t\treturn;\n\t\t}\n\n\t\tlogInfo(\"Stopping service...\");\n\n\t\tthis.isListening = false;\n\n\t\t// Stop all instances\n\t\tawait this.manager.stop();\n\n\t\tlogInfo(\"Service stopped\");\n\t}\n\n\t/**\n\t * Get device port mapping\n\t */\n\tpublic getDevicePort(udid: string): number | null {\n\t\treturn this.manager.getDevicePort(udid);\n\t}\n\n\t/**\n\t * Get all device mappings\n\t */\n\tpublic getDeviceMappings() {\n\t\treturn this.manager.getDeviceMappings();\n\t}\n\n\t/**\n\t * Get all running instances\n\t */\n\tpublic getInstances() {\n\t\treturn this.manager.getInstances();\n\t}\n\n\t/**\n\t * Get service statistics\n\t */\n\tpublic getStats() {\n\t\treturn this.manager.getStats();\n\t}\n\n\t/**\n\t * Get current configuration\n\t */\n\tpublic getConfig() {\n\t\treturn this.manager.getConfig();\n\t}\n}\n", "import type { ChildProcess } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type {\n\tDeviceInfo,\n\tDeviceMapping,\n\tInstanceManagerConfig,\n\tManagerStats,\n\tUsbmuxdInstance,\n} from \"./types/index.js\";\nimport { Usbipd } from \"./usbipd.js\";\nimport { Wsl2 } from \"./wsl.js\";\n\nconst { logWarning, logDetail, logDataObject, logTask } = createLoggers(\"instance-manager\");\n\n/**\n * Default configuration for the instance manager\n */\nconst DEFAULT_CONFIG: InstanceManagerConfig = {\n\tbatchSize: 4,\n\tbasePort: 27015,\n\tmaxInstances: 5,\n\tusbmuxdPath: \"usbmuxd\", // Path inside WSL2\n\twslDistribution: \"alpine-usbmuxd-build\", // Alpine WSL2 distribution name\n\tverboseLogging: true,\n\tappleVendorId: \"05AC\",\n};\n\n/**\n * Manages multiple usbmuxd instances with batch device allocation\n *\n * @example\n * const manager = new InstanceManager({\n * batchSize: 4,\n * basePort: 27015,\n * });\n *\n * manager.on('instance-started', (instance) => {\n * logInfo(`Instance ${instance.id} started on port ${instance.port}`);\n * });\n *\n * manager.start();\n */\nexport class InstanceManager extends EventEmitter {\n\t/** Map of usbmuxd instance IDs to instances */\n\tprivate instances: Map<number, UsbmuxdInstance> = new Map();\n\t/** Map of device IDs to device mappings host:port */\n\tprivate deviceMappings: Map<string, DeviceMapping> = new Map();\n\t/** Map of usbmuxd instance IDs to child processes running it on wsl2*/\n\tprivate processes: Map<number, ChildProcess> = new Map();\n\t/** Tracks which devices have been attached to WSL */\n\tprivate attachedDevices: Set<string> = new Set();\n\t/** Device IDs currently in the attach flow\n\t * (ignore disconnect until attach completes) */\n\tprivate pendingAttachDevices: Set<string> = new Set();\n\n\tprivate config: InstanceManagerConfig;\n\tprivate nextInstanceId = 1;\n\tprivate startedAt: Date | null = null;\n\tprivate isRunning = false;\n\n\tprivate wsl: Wsl2;\n\tprivate usbipd: Usbipd;\n\tconstructor(config: Partial<InstanceManagerConfig> = {}) {\n\t\tsuper();\n\t\tthis.config = { ...DEFAULT_CONFIG, ...config };\n\t\tthis.wsl = new Wsl2(this.config.wslDistribution);\n\t\tthis.usbipd = new Usbipd(process.env.USBIPD_PATH ?? \"usbipd\");\n\t}\n\n\t/**\n\t * Start the instance manager\n\t */\n\tpublic start(): void {\n\t\tif (this.isRunning) {\n\t\t\tthrow new Error(\"Instance manager is already running\");\n\t\t}\n\n\t\tthis.isRunning = true;\n\t\tthis.startedAt = new Date();\n\t}\n\n\t/**\n\t * Stop the instance manager and all instances\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tif (!this.isRunning) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.isRunning = false;\n\n\t\t// Sync lockdown files from Alpine back to native Apple lockdown directory (before clearing mappings)\n\t\tconst udids = Array.from(this.deviceMappings.keys());\n\t\tawait this.wsl.syncFromAlpine(udids);\n\n\t\t// Detach all devices from WSL.\n\t\t// If you only called unbind without detach,\n\t\t// the device might still be attached to the WSL session\n\t\tawait this.usbipd.detachAllDevicesFromWsl();\n\t\tawait this.usbipd.unbindAllDevicesFromWsl();\n\t\tthis.attachedDevices.clear();\n\n\t\t// Stop all instances\n\t\tconst stopPromises = Array.from(this.instances.keys()).map((id) => this.stopInstance(id));\n\n\t\tawait Promise.all(stopPromises);\n\n\t\tthis.instances.clear();\n\t\tthis.deviceMappings.clear();\n\t\tthis.processes.clear();\n\t}\n\n\tprivate async attachToWsl({\n\t\tbusId,\n\t\tdeviceId,\n\t}: { busId: string; deviceId: string }): Promise<void> {\n\t\tif (!this.attachedDevices.has(busId)) {\n\t\t\tthis.pendingAttachDevices.add(deviceId);\n\t\t\tif (!this.config.wslDistribution) {\n\t\t\t\tthrow new Error(\"WSL distribution not configured\");\n\t\t\t}\n\t\t\tconst attached = await this.usbipd.attachDeviceToWsl(\n\t\t\t\tbusId,\n\t\t\t\tthis.wsl,\n\t\t\t\tthis.config.wslDistribution\n\t\t\t);\n\t\t\tif (attached) {\n\t\t\t\tthis.attachedDevices.add(busId);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async removeDevice(mapping: DeviceMapping): Promise<UsbmuxdInstance> {\n\t\tconst instance = await this.detachFromWsl({ deviceId: mapping.udid });\n\t\tif (!instance) {\n\t\t\tthrow new Error(`Instance ${mapping.instanceId} not found`);\n\t\t}\n\t\tif (mapping.busId) {\n\t\t\tawait this.usbipd.detachDeviceFromWsl(mapping.busId);\n\t\t\tthis.attachedDevices.delete(mapping.busId);\n\t\t}\n\t\tconst deviceIndex = instance.deviceUdids.indexOf(mapping.udid);\n\t\tif (deviceIndex > -1) {\n\t\t\tinstance.deviceUdids.splice(deviceIndex, 1);\n\t\t}\n\n\t\tthis.deviceMappings.delete(mapping.udid);\n\t\treturn instance;\n\t}\n\n\tprivate async onUsbmuxdInstanceEnd({\n\t\tinstanceId,\n\t\tcode,\n\t\tsignal,\n\t}: { instanceId: number; code: number; signal: string }): Promise<void> {\n\t\tthis.emit(\"instance-exited\", {\n\t\t\tinstanceId,\n\t\t\tcode,\n\t\t\tsignal,\n\t\t});\n\t\tfor (const [_, mapping] of this.deviceMappings.entries()) {\n\t\t\tif (mapping.instanceId === instanceId) {\n\t\t\t\tthis.removeDevice(mapping);\n\t\t\t}\n\t\t}\n\t\tthis.instances.delete(instanceId);\n\t\tthis.processes.get(instanceId)?.kill();\n\t\tthis.processes.delete(instanceId);\n\t}\n\n\tprivate async onUsbmuxdInstanceStart({\n\t\tinstance,\n\t\tprocess,\n\t}: { instance: UsbmuxdInstance; process: ChildProcess }): Promise<void> {\n\t\tthis.instances.set(instance.id, instance);\n\t\tthis.processes.set(instance.id, process);\n\t\tthis.emit(\"instance-started\", {\n\t\t\tinstanceId: instance.id,\n\t\t});\n\t}\n\n\tprivate async getInstance() {\n\t\tlet targetInstance = this.findInstanceWithCapacity();\n\n\t\t// If no instance has capacity, create a new one\n\t\tif (!targetInstance) {\n\t\t\tif (this.instances.size >= this.config.maxInstances) {\n\t\t\t\tthrow new Error(`Maximum number of instances (${this.config.maxInstances}) reached`);\n\t\t\t}\n\n\t\t\tconst newInstanceId = this.nextInstanceId++;\n\n\t\t\tlet onExit = (code: number, signal: string) => {\n\t\t\t\tthis.onUsbmuxdInstanceEnd({ instanceId: newInstanceId, code, signal });\n\t\t\t};\n\n\t\t\tonExit = onExit.bind(this);\n\n\t\t\tconst { instance, process } = await this.wsl.createInstance({\n\t\t\t\tid: newInstanceId,\n\t\t\t\tbasePort: this.config.basePort,\n\t\t\t\tverboseLogging: this.config.verboseLogging,\n\t\t\t\tusbmuxdPath: this.config.usbmuxdPath,\n\t\t\t\tonLog: (message) =>\n\t\t\t\t\tthis.emit(\"instance-log\", {\n\t\t\t\t\t\tinstanceId: newInstanceId,\n\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t}),\n\t\t\t\tonExit,\n\t\t\t});\n\t\t\tthis.onUsbmuxdInstanceStart({ instance, process });\n\t\t\ttargetInstance = instance;\n\t\t}\n\t\treturn targetInstance;\n\t}\n\n\t/**\n\t * Handle device connection only for not attached devices\n\t * Attaches device to WSL, then assigns to an existing instance or creates a new one\n\t */\n\tpublic async onDeviceConnected(device: DeviceInfo): Promise<void> {\n\t\tconst busId = await this.usbipd.findBusIdForDevice(device);\n\n\t\tif (this.attachedDevices.has(busId)) {\n\t\t\tlogTask(`Device ${device.deviceId} is already attached. skipping onDeviceConnected...`);\n\t\t\treturn;\n\t\t}\n\n\t\tlogDataObject(\"Device connected\", { device });\n\t\tawait this.wsl.syncToAlpine(device.deviceId);\n\n\t\tawait this.attachToWsl({ busId, deviceId: device.deviceId });\n\n\t\tconst targetInstance = await this.getInstance();\n\n\t\t// Create mapping (include host and busId for cleanup on disconnect)\n\t\tconst mapping: DeviceMapping = {\n\t\t\tudid: device.deviceId,\n\t\t\tinstanceId: targetInstance.id,\n\t\t\thost: targetInstance.host,\n\t\t\tport: targetInstance.port,\n\t\t\taddedAt: new Date(),\n\t\t\tbusId: busId ?? undefined,\n\t\t};\n\n\t\tthis.deviceMappings.set(device.deviceId, mapping);\n\n\t\t// Add device to instance\n\t\ttargetInstance.deviceUdids.push(device.deviceId);\n\n\t\tthis.emit(\"device-assigned\", {\n\t\t\tdevice,\n\t\t\tinstance: targetInstance,\n\t\t\tmapping,\n\t\t});\n\n\t\tthis.pendingAttachDevices.delete(device.deviceId);\n\t}\n\n\tprivate async detachFromWsl({ deviceId }: { deviceId: string }): Promise<UsbmuxdInstance> {\n\t\tconst mapping = this.deviceMappings.get(deviceId);\n\t\tif (!mapping) {\n\t\t\tthrow new Error(`Device ${deviceId} was not tracked`);\n\t\t}\n\n\t\t// Detach device from WSL if it was attached\n\t\tif (mapping.busId) {\n\t\t\tawait this.usbipd.detachDeviceFromWsl(mapping.busId);\n\t\t\tthis.attachedDevices.delete(mapping.busId);\n\t\t}\n\n\t\tconst instance = this.instances.get(mapping.instanceId);\n\t\tif (!instance) {\n\t\t\tlogWarning(`Instance ${mapping.instanceId} not found`);\n\t\t\tthis.deviceMappings.delete(deviceId);\n\t\t\tthrow new Error(`Instance ${mapping.instanceId} not found`);\n\t\t}\n\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Handle device disconnection only for attached devices\n\t * Detaches from WSL, removes device from instance, and stops instance if empty\n\t */\n\tpublic async onDeviceDisconnected(device: DeviceInfo): Promise<void> {\n\t\t// Ignore disconnect while we're still attaching this device (Windows sees disconnect when device moves to WSL)\n\t\tif (!this.attachedDevices.has(device.deviceId)) {\n\t\t\tlogTask(`Device ${device.deviceId} is not attached. skipping onDeviceDisconnected...`);\n\t\t\treturn;\n\t\t}\n\t\tlogDataObject(\"Device disconnected\", { device });\n\n\t\tconst mapping = this.deviceMappings.get(device.deviceId);\n\t\tif (!mapping) {\n\t\t\tthrow new Error(`Device ${device.deviceId} was not tracked`);\n\t\t}\n\t\t// Remove device from instance\n\t\tconst instance = await this.removeDevice(mapping);\n\n\t\tthis.emit(\"device-removed\", {\n\t\t\tdevice,\n\t\t\tinstance,\n\t\t});\n\n\t\t// If instance is now empty, stop it\n\t\tif (instance.deviceUdids.length === 0) {\n\t\t\tlogDetail(`Instance ${instance.id} is empty, stopping...`);\n\t\t\tawait this.stopInstance(instance.id);\n\t\t}\n\t}\n\n\t/**\n\t * Find an instance with available capacity\n\t */\n\tprivate findInstanceWithCapacity(): UsbmuxdInstance | null {\n\t\tfor (const instance of this.instances.values()) {\n\t\t\tif (instance.deviceUdids.length < this.config.batchSize) {\n\t\t\t\treturn instance;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Stop a specific instance\n\t */\n\tprivate async stopInstance(instanceId: number): Promise<void> {\n\t\tconst instance = this.instances.get(instanceId);\n\t\tconst childProcess = this.processes.get(instanceId);\n\n\t\tif (!instance || !childProcess) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const process of this.processes.values()) {\n\t\t\tprocess.kill(\"SIGKILL\");\n\t\t}\n\n\t\t// Clean up\n\t\tthis.instances.delete(instanceId);\n\t\tthis.processes.delete(instanceId);\n\n\t\t// Remove device mappings\n\t\tfor (const [udid, mapping] of this.deviceMappings.entries()) {\n\t\t\tif (mapping.instanceId === instanceId) {\n\t\t\t\tthis.deviceMappings.delete(udid);\n\t\t\t}\n\t\t}\n\n\t\tthis.emit(\"instance-stopped\", instance);\n\t}\n\n\t/**\n\t * Get device-to-port mapping for a specific UDID\n\t */\n\tpublic getDevicePort(udid: string): number | null {\n\t\tconst mapping = this.deviceMappings.get(udid);\n\t\treturn mapping ? mapping.port : null;\n\t}\n\n\t/**\n\t * Get all device mappings\n\t */\n\tpublic getDeviceMappings(): DeviceMapping[] {\n\t\treturn Array.from(this.deviceMappings.values());\n\t}\n\n\t/**\n\t * Get all running instances\n\t */\n\tpublic getInstances(): UsbmuxdInstance[] {\n\t\treturn Array.from(this.instances.values());\n\t}\n\n\t/**\n\t * Get manager statistics\n\t */\n\tpublic getStats(): ManagerStats {\n\t\tconst now = Date.now();\n\t\tconst startTime = this.startedAt?.getTime() || now;\n\t\tconst uptimeSeconds = Math.floor((now - startTime) / 1000);\n\n\t\treturn {\n\t\t\tinstanceCount: this.instances.size,\n\t\t\tdeviceCount: this.deviceMappings.size,\n\t\t\tuptimeSeconds,\n\t\t\tstartedAt: this.startedAt || new Date(),\n\t\t};\n\t}\n\n\t/**\n\t * Get current configuration\n\t */\n\tpublic getConfig(): InstanceManagerConfig {\n\t\treturn { ...this.config };\n\t}\n}\n", "import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type { DeviceInfo } from \"./types\";\nimport type { UsbipdDevice } from \"./types/usbipd.js\";\nimport type { Wsl2 } from \"./wsl.js\";\n\nconst { logInfo, logWarning } = createLoggers(\"usbipd\");\n\nconst execAsync = promisify(exec);\n\nexport class Usbipd {\n constructor(private readonly usbipdPath: string) { }\n\n public async unbindAllDevicesFromWsl(): Promise<void> {\n try {\n await execAsync(`\"${this.usbipdPath}\" unbind -a`);\n logInfo(\"All devices unbound from WSL\");\n } catch (error) {\n logWarning(`Failed to unbind all devices from WSL: ${error}`);\n }\n }\n\n public async detachAllDevicesFromWsl(): Promise<void> {\n try {\n await execAsync(`\"${this.usbipdPath}\" detach -a`);\n logInfo(\"All devices detached from WSL\");\n } catch (error) {\n logWarning(`Failed to detach all devices from WSL: ${error}`);\n }\n }\n\n /**\n * Detach a device from WSL via usbipd\n */\n public async detachDeviceFromWsl(busId: string): Promise<void> {\n try {\n await execAsync(`\"${this.usbipdPath}\" detach --busid=${busId}`);\n logInfo(`Device ${busId} detached from WSL`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"no device with busid\") || message.includes(\"There is no device\")) {\n logInfo(`Device ${busId} already detached`);\n } else {\n logWarning(`Failed to detach device ${busId}: ${error}`);\n }\n }\n }\n\n /**\n * Attach a device to WSL via usbipd\n * Note: This requires administrator privileges\n */\n public async attachDeviceToWsl(busId: string, wsl: Wsl2, distro: string): Promise<boolean> {\n try {\n // Ensure WSL is running first\n await wsl.ensureWslRunning();\n\n // Bind the device (make it shareable)\n logInfo(`Binding device ${busId}...`);\n await execAsync(`\"${this.usbipdPath}\" bind --busid ${busId} --force`);\n\n // Attach to WSL (use specific distro if configured, otherwise let usbipd pick)\n if (distro) {\n logInfo(`Attaching device ${busId} to WSL distribution ${distro}...`);\n await execAsync(`\"${this.usbipdPath}\" attach --wsl=${distro} --busid=${busId}`);\n } else {\n logInfo(`Attaching device ${busId} to default WSL...`);\n await execAsync(`\"${this.usbipdPath}\" attach --wsl --busid=${busId}`);\n }\n\n logInfo(`Device ${busId} attached to WSL successfully`);\n return true;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n // Device already attached (e.g. from a previous run) \u2014 treat as success so we still start usbmuxd\n if (message.includes(\"already attached to a client\")) {\n logInfo(`Device ${busId} is already attached to WSL, continuing`);\n return true;\n }\n logWarning(`Failed to attach device ${busId} to WSL: ${error}`);\n return false;\n }\n }\n\n /**\n * Parse usbipd list output to extract device information\n */\n private parseUsbipdList(output: string): UsbipdDevice[] {\n const devices: UsbipdDevice[] = [];\n // Handle both \\r\\n and \\n line endings\n const lines = output.split(/\\r?\\n/);\n\n for (const line of lines) {\n // Format: \"4-5 05ac:12a8 Apple Mobile Device USB Composite Device Shared (forced)\"\n // More flexible regex: busid, vid:pid, then anything else\n const match = line.match(/^(\\d+-\\d+)\\s+([0-9a-f]{4}):([0-9a-f]{4})\\s+(.+)$/i);\n if (match) {\n // Split the rest into description and state (state is the last word(s) after multiple spaces)\n const rest = match[4].trim();\n const stateMatch = rest.match(/^(.+?)\\s{2,}(\\S.*)$/);\n const description = stateMatch ? stateMatch[1].trim() : rest;\n const state = stateMatch ? stateMatch[2].trim() : \"Unknown\";\n\n devices.push({\n busId: match[1],\n vid: match[2].toUpperCase(),\n pid: match[3].toUpperCase(),\n description,\n state,\n });\n }\n }\n\n return devices;\n }\n /**\n * Find the usbipd bus ID for a device by matching VID/PID\n */\n public async findBusIdForDevice(device: DeviceInfo): Promise<string> {\n try {\n const { stdout } = await execAsync(`\"${this.usbipdPath}\" list`);\n const usbipdDevices = this.parseUsbipdList(stdout);\n\n // Convert device VID/PID to hex string for comparison\n const deviceVid = device.vid.toString(16).toUpperCase().padStart(4, \"0\");\n const devicePid = device.pid.toString(16).toUpperCase().padStart(4, \"0\");\n\n logInfo(`Looking for device with VID:PID ${deviceVid}:${devicePid}`);\n logInfo(`Found ${usbipdDevices.length} devices from usbipd list`);\n\n // Find matching device by VID/PID\n const match = usbipdDevices.find((d) => d.vid === deviceVid && d.pid === devicePid);\n\n if (match) {\n logInfo(\n `Found usbipd bus ID ${match.busId} for device ${device.deviceId} (${deviceVid}:${devicePid})`\n );\n return match.busId;\n }\n\n throw new Error(\n `Could not find usbipd bus ID for device ${device.deviceId} (${deviceVid}:${devicePid})`\n );\n } catch (error) {\n throw new Error(`Failed to run usbipd list: ${error}`);\n }\n }\n}\n", "import { type ChildProcess, exec, spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { createLoggers } from \"@mcesystems/tool-debug-g4\";\nimport type { UsbmuxdInstance } from \"./types\";\n\nconst { logInfo, logWarning, logDetail } = createLoggers(\"wsl\");\n\nconst execAsync = promisify(exec);\n\nexport class Wsl2 {\n\tprivate readonly ALPINE_LOCKDOWN_DIR = \"/var/lib/lockdown\";\n\tprivate readonly SYSTEM_CONFIG_PLIST = \"SystemConfiguration.plist\";\n\n\tprivate wslIpAddress: string | null = null;\n\n\tconstructor(private wslDistribution: string | undefined) {}\n\n\t/**\n\t * Detect the WSL2 IP address for the configured distribution\n\t * This IP is needed to connect from Windows to services inside WSL\n\t */\n\tpublic async detectWslIpAddress(): Promise<string> {\n\t\tif (this.wslIpAddress) {\n\t\t\treturn this.wslIpAddress;\n\t\t}\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\n\t\ttry {\n\t\t\t// Try to get IP from eth0 interface\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- ip -4 addr show eth0`);\n\t\t\tconst match = stdout.match(/inet\\s+(\\d+\\.\\d+\\.\\d+\\.\\d+)/);\n\t\t\tif (match) {\n\t\t\t\tthis.wslIpAddress = match[1];\n\t\t\t\tlogInfo(`Detected WSL IP address: ${this.wslIpAddress}`);\n\t\t\t\treturn this.wslIpAddress;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to detect WSL IP via ip addr: ${error}`);\n\t\t}\n\n\t\t// Fallback: try hostname -I\n\t\ttry {\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- hostname -I`);\n\t\t\tconst ip = stdout.trim().split(/\\s+/)[0];\n\t\t\tif (ip && /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(ip)) {\n\t\t\t\tthis.wslIpAddress = ip;\n\t\t\t\tlogInfo(`Detected WSL IP address (hostname): ${this.wslIpAddress}`);\n\t\t\t\treturn this.wslIpAddress;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to detect WSL IP via hostname: ${error}`);\n\t\t}\n\n\t\t// Last resort fallback - localhost (may work with WSL2 mirrored networking)\n\t\tlogWarning(\"Could not detect WSL IP, falling back to localhost\");\n\t\tthis.wslIpAddress = \"127.0.0.1\";\n\t\treturn this.wslIpAddress;\n\t}\n\n\t/**\n\t * Ensure the WSL distribution is running\n\t */\n\tpublic async ensureWslRunning(): Promise<boolean> {\n\t\tconst distro = this.wslDistribution;\n\n\t\ttry {\n\t\t\tif (distro) {\n\t\t\t\t// Start specific distribution\n\t\t\t\tlogInfo(`Starting WSL distribution: ${distro}...`);\n\t\t\t\tawait execAsync(`wsl -d ${distro} -- echo \"WSL started\"`);\n\t\t\t} else {\n\t\t\t\t// Start default WSL\n\t\t\t\tlogInfo(\"Starting default WSL distribution...\");\n\t\t\t\tawait execAsync(`wsl -- echo \"WSL started\"`);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogWarning(`Failed to start WSL: ${error}`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Create a new usbmuxd instance\n\t */\n\tpublic async createInstance({\n\t\tid,\n\t\tbasePort,\n\t\tverboseLogging,\n\t\tusbmuxdPath,\n\t\tonLog,\n\t\tonExit,\n\t}: {\n\t\tid: number;\n\t\tbasePort: number;\n\t\tverboseLogging: boolean;\n\t\tusbmuxdPath: string;\n\t\tonLog: (message: string) => void;\n\t\tonExit: (code: number, signal: string) => void;\n\t}): Promise<{ instance: UsbmuxdInstance; process: ChildProcess }> {\n\t\tconst port = basePort + id - 1;\n\n\t\tlogInfo(`Creating instance ${id} on port ${port}`);\n\n\t\t// Detect WSL IP for connecting from Windows\n\t\tconst host = await this.detectWslIpAddress();\n\n\t\t// Build usbmuxd arguments\n\t\tconst usbmuxdArgs = [\n\t\t\t\"-f\", // Foreground\n\t\t\t\"-v\", // Verbose (if enabled)\n\t\t\t\"-S\",\n\t\t\t`0.0.0.0:${port}`, // Listen on all interfaces (for Windows \u2192 WSL2)\n\t\t\t\"--pidfile\",\n\t\t\t\"NONE\",\n\t\t];\n\n\t\tif (!verboseLogging) {\n\t\t\t// Remove -v if not verbose\n\t\t\tusbmuxdArgs.splice(1, 1);\n\t\t}\n\n\t\t// Build WSL command: wsl -d <distro> usbmuxd <args>\n\t\tconst wslArgs = [\n\t\t\t\"-d\",\n\t\t\tthis.wslDistribution || \"alpine-usbmuxd-build\",\n\t\t\tusbmuxdPath,\n\t\t\t...usbmuxdArgs,\n\t\t];\n\n\t\t// Spawn via WSL\n\t\t// detached: true creates a new process group so the child does NOT receive\n\t\t// the parent's Ctrl+C. Without this, Windows delivers SIGINT to every process\n\t\t// in the console, killing usbmuxd before stop() can sync lockdown files back.\n\t\t// windowsHide: true prevents the detached process from opening a visible console window.\n\t\tconst process = spawn(\"wsl\", wslArgs, {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tdetached: true,\n\t\t\twindowsHide: false,\n\t\t});\n\n\t\t// Handle process output\n\t\tprocess.stdout?.on(\"data\", (data) => {\n\t\t\tonLog(data.toString().trim());\n\t\t});\n\n\t\tprocess.stderr?.on(\"data\", (data) => {\n\t\t\tonLog(data.toString().trim());\n\t\t});\n\n\t\t// Handle process exit\n\t\tprocess.on(\"exit\", onExit);\n\n\t\t// Create instance record (pid is set synchronously by Node after spawn)\n\t\tconst pid = process.pid;\n\t\tif (pid === undefined) {\n\t\t\tprocess.kill(\"SIGKILL\");\n\t\t\tthrow new Error(\"Failed to get PID for usbmuxd instance\");\n\t\t}\n\t\tconst instance: UsbmuxdInstance = {\n\t\t\tid,\n\t\t\thost,\n\t\t\tport,\n\t\t\tpid,\n\t\t\tdeviceUdids: [],\n\t\t\tstartedAt: new Date(),\n\t\t};\n\n\t\treturn { instance, process };\n\t}\n\n\t/**\n\t * Convert a Windows path to the equivalent path inside WSL (e.g. C:\\foo\\bar -> /mnt/c/foo/bar).\n\t */\n\tprivate windowsPathToWsl(windowsPath: string): string {\n\t\tconst normalized = windowsPath.replace(/\\\\/g, \"/\").trim();\n\t\tconst driveMatch = normalized.match(/^([a-zA-Z]):\\/?(.*)$/);\n\t\tif (driveMatch) {\n\t\t\tconst drive = driveMatch[1].toLowerCase();\n\t\t\tconst rest = driveMatch[2] || \"\";\n\t\t\treturn `/mnt/${drive}${rest ? `/${rest}` : \"\"}`;\n\t\t}\n\t\treturn normalized;\n\t}\n\n\t/**\n\t * Get the native Apple lockdown directory path for the current platform.\n\t * This is where Apple/iTunes stores pairing records natively.\n\t */\n\tprivate getAppleLockdownPath(): string | null {\n\t\tif (process.platform === \"win32\") {\n\t\t\treturn join(process.env.ProgramData ?? \"C:\\\\ProgramData\", \"Apple\", \"Lockdown\");\n\t\t}\n\t\tif (process.platform === \"darwin\") {\n\t\t\treturn \"/var/db/lockdown\";\n\t\t}\n\t\tif (process.platform === \"linux\") {\n\t\t\treturn \"/var/lib/lockdown\";\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Copy a single device's lockdown plist from the native Apple lockdown directory\n\t * to Alpine so usbmuxd can use it (skip pairing).\n\t *\n\t * Uses the platform-specific Apple/iTunes lockdown directory\n\t * (e.g. C:\\ProgramData\\Apple\\Lockdown on Windows, /var/db/lockdown on macOS).\n\t *\n\t * No-op if the lockdown directory cannot be determined or files don't exist.\n\t */\n\tpublic async syncToAlpine(udid: string): Promise<void> {\n\t\tconst lockdownDir = this.getAppleLockdownPath();\n\t\tif (!lockdownDir) {\n\t\t\tlogWarning(\"Lockdown sync to Alpine: unsupported platform, skipping\");\n\t\t\treturn;\n\t\t}\n\n\t\tlogDetail(`Lockdown sync to Alpine: source dir = ${lockdownDir}, udid = ${udid}`);\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\t\tconst wslDestDir = this.ALPINE_LOCKDOWN_DIR;\n\n\t\t// Sync the device-specific plist\n\t\tconst devicePlistFile = `${udid}.plist`;\n\t\tconst devicePlistPath = join(lockdownDir, devicePlistFile);\n\t\tif (existsSync(devicePlistPath)) {\n\t\t\tawait this.copyFileToAlpine(\n\t\t\t\tthis.windowsPathToWsl(devicePlistPath),\n\t\t\t\twslDestDir,\n\t\t\t\tdistro,\n\t\t\t\tdevicePlistFile\n\t\t\t);\n\t\t} else {\n\t\t\tlogDetail(\n\t\t\t\t`Lockdown sync to Alpine: ${devicePlistFile} not found at ${devicePlistPath}, skipping`\n\t\t\t);\n\t\t}\n\n\t\t// Sync SystemConfiguration.plist so Alpine usbmuxd uses the same SystemBUID\n\t\t// that was used during the original pairing on Windows.\n\t\t// Without this, lockdown sessions fail with InvalidHostID.\n\t\tconst systemConfigPath = join(lockdownDir, this.SYSTEM_CONFIG_PLIST);\n\t\tif (existsSync(systemConfigPath)) {\n\t\t\tawait this.copyFileToAlpine(\n\t\t\t\tthis.windowsPathToWsl(systemConfigPath),\n\t\t\t\twslDestDir,\n\t\t\t\tdistro,\n\t\t\t\tthis.SYSTEM_CONFIG_PLIST\n\t\t\t);\n\t\t} else {\n\t\t\tlogDetail(\n\t\t\t\t`Lockdown sync to Alpine: ${this.SYSTEM_CONFIG_PLIST} not found at ${systemConfigPath}, skipping`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Copy a single file into the Alpine lockdown directory.\n\t * Falls back to sudo if the initial copy fails.\n\t */\n\tprivate async copyFileToAlpine(\n\t\twslSource: string,\n\t\twslDestDir: string,\n\t\tdistro: string,\n\t\tfileName: string\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait execAsync(\n\t\t\t\t`wsl -d ${distro} -- sh -c \"mkdir -p ${wslDestDir} && cp '${wslSource}' '${wslDestDir}/'\"`\n\t\t\t);\n\t\t\tlogInfo(`Lockdown sync: copied ${fileName} to Alpine`);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait execAsync(\n\t\t\t\t\t`wsl -d ${distro} -- sh -c \"mkdir -p ${wslDestDir} && sudo cp '${wslSource}' '${wslDestDir}/'\"`\n\t\t\t\t);\n\t\t\t\tlogInfo(`Lockdown sync: copied ${fileName} to Alpine (via sudo)`);\n\t\t\t} catch (sudoError) {\n\t\t\t\tlogWarning(\n\t\t\t\t\t`Lockdown sync to Alpine failed for ${fileName}: ${error}. Sudo fallback failed: ${sudoError}. Ensure /var/lib/lockdown is writable or use passwordless sudo.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Copy lockdown plists from Alpine back to the native Apple lockdown directory\n\t * (for devices that were assigned this session).\n\t * Also copies SystemConfiguration.plist if present in Alpine.\n\t *\n\t * No-op if the lockdown directory cannot be determined.\n\t */\n\tpublic async syncFromAlpine(udids: string[]): Promise<void> {\n\t\tlogInfo(`Lockdown sync from Alpine: starting (${udids.length} device(s))`);\n\n\t\tconst lockdownDir = this.getAppleLockdownPath();\n\t\tif (!lockdownDir) {\n\t\t\tlogWarning(\"Lockdown sync: unsupported platform, skipping\");\n\t\t\treturn;\n\t\t}\n\n\t\tlogDetail(`Lockdown sync: target dir = ${lockdownDir}`);\n\n\t\ttry {\n\t\t\tmkdirSync(lockdownDir, { recursive: true });\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: could not create lockdown dir ${lockdownDir}: ${error}`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst distro = this.wslDistribution || \"alpine-usbmuxd-build\";\n\t\tconst wslDestDir = this.windowsPathToWsl(lockdownDir);\n\n\t\t// List what Alpine actually has in its lockdown dir for debugging\n\t\ttry {\n\t\t\tconst { stdout } = await execAsync(`wsl -d ${distro} -- ls -la ${this.ALPINE_LOCKDOWN_DIR}/`);\n\t\t\tlogDetail(`Lockdown sync: Alpine ${this.ALPINE_LOCKDOWN_DIR} contents:\\n${stdout}`);\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: could not list Alpine lockdown dir: ${error}`);\n\t\t}\n\n\t\tconst filesToSync = [...udids.map((udid) => `${udid}.plist`), this.SYSTEM_CONFIG_PLIST];\n\n\t\tlogDetail(`Lockdown sync: files to sync = [${filesToSync.join(\", \")}]`);\n\n\t\tfor (const fileName of filesToSync) {\n\t\t\tawait this.copyFileFromAlpine(fileName, wslDestDir, distro, lockdownDir);\n\t\t}\n\n\t\tlogInfo(\"Lockdown sync from Alpine: done\");\n\t}\n\n\t/**\n\t * Copy a single file from the Alpine lockdown directory back to Windows.\n\t * Skips if the file does not exist in Alpine; logs a warning if the copy fails.\n\t */\n\tprivate async copyFileFromAlpine(\n\t\tfileName: string,\n\t\twslDestDir: string,\n\t\tdistro: string,\n\t\tlockdownDir: string\n\t): Promise<void> {\n\t\tconst alpinePath = `${this.ALPINE_LOCKDOWN_DIR}/${fileName}`;\n\n\t\t// Check if the file exists in Alpine first\n\t\ttry {\n\t\t\tawait execAsync(`wsl -d ${distro} -- test -f '${alpinePath}'`);\n\t\t} catch {\n\t\t\tlogDetail(`Lockdown sync: ${fileName} not found in Alpine (${alpinePath}), skipping`);\n\t\t\treturn;\n\t\t}\n\n\t\t// File exists \u2014 copy it to Windows\n\t\tlogDetail(`Lockdown sync: copying ${fileName} \u2192 ${wslDestDir}/`);\n\t\ttry {\n\t\t\tawait execAsync(`wsl -d ${distro} -- cp -f '${alpinePath}' '${wslDestDir}/'`);\n\t\t\tlogInfo(`Lockdown sync: copied ${fileName} from Alpine to ${lockdownDir}`);\n\t\t} catch (error) {\n\t\t\tlogWarning(`Lockdown sync: failed to copy ${fileName} from Alpine to ${lockdownDir}: ${error}`);\n\t\t}\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["EventEmitter", "createLoggers", "createLoggers", "exec", "promisify", "createLoggers", "logInfo", "logWarning", "execAsync", "process", "logWarning", "logDetail", "createLoggers", "process", "logInfo", "logDataObject", "createLoggers", "EventEmitter"]
7
7
  }