appium-ios-remotexpc 0.12.0 → 0.13.1

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.
Files changed (90) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/src/constants.d.ts +2 -0
  3. package/build/src/constants.d.ts.map +1 -0
  4. package/build/src/constants.js +3 -0
  5. package/build/src/index.d.ts +2 -1
  6. package/build/src/index.d.ts.map +1 -1
  7. package/build/src/index.js +2 -1
  8. package/build/src/lib/apple-tv/constants.d.ts +0 -1
  9. package/build/src/lib/apple-tv/constants.d.ts.map +1 -1
  10. package/build/src/lib/apple-tv/constants.js +0 -1
  11. package/build/src/lib/apple-tv/discovery/device-discovery.d.ts +10 -0
  12. package/build/src/lib/apple-tv/discovery/device-discovery.d.ts.map +1 -0
  13. package/build/src/lib/apple-tv/discovery/device-discovery.js +22 -0
  14. package/build/src/lib/apple-tv/discovery/index.d.ts +2 -0
  15. package/build/src/lib/apple-tv/discovery/index.d.ts.map +1 -0
  16. package/build/src/lib/apple-tv/discovery/index.js +1 -0
  17. package/build/src/lib/apple-tv/index.d.ts +5 -0
  18. package/build/src/lib/apple-tv/index.d.ts.map +1 -1
  19. package/build/src/lib/apple-tv/index.js +5 -0
  20. package/build/src/lib/apple-tv/network/constants.d.ts +10 -0
  21. package/build/src/lib/apple-tv/network/constants.d.ts.map +1 -0
  22. package/build/src/lib/apple-tv/network/constants.js +9 -0
  23. package/build/src/lib/apple-tv/network/index.d.ts +4 -0
  24. package/build/src/lib/apple-tv/network/index.d.ts.map +1 -0
  25. package/build/src/lib/apple-tv/network/index.js +2 -0
  26. package/build/src/lib/apple-tv/network/network-client.d.ts +16 -0
  27. package/build/src/lib/apple-tv/network/network-client.d.ts.map +1 -0
  28. package/build/src/lib/apple-tv/network/network-client.js +169 -0
  29. package/build/src/lib/apple-tv/network/types.d.ts +8 -0
  30. package/build/src/lib/apple-tv/network/types.d.ts.map +1 -0
  31. package/build/src/lib/apple-tv/network/types.js +1 -0
  32. package/build/src/lib/apple-tv/pairing/index.d.ts +3 -0
  33. package/build/src/lib/apple-tv/pairing/index.d.ts.map +1 -0
  34. package/build/src/lib/apple-tv/pairing/index.js +2 -0
  35. package/build/src/lib/apple-tv/pairing/pairing-service.d.ts +15 -0
  36. package/build/src/lib/apple-tv/pairing/pairing-service.d.ts.map +1 -0
  37. package/build/src/lib/apple-tv/pairing/pairing-service.js +112 -0
  38. package/build/src/lib/apple-tv/pairing/user-input-service.d.ts +8 -0
  39. package/build/src/lib/apple-tv/pairing/user-input-service.d.ts.map +1 -0
  40. package/build/src/lib/apple-tv/pairing/user-input-service.js +61 -0
  41. package/build/src/lib/apple-tv/pairing-protocol/constants.d.ts +18 -0
  42. package/build/src/lib/apple-tv/pairing-protocol/constants.d.ts.map +1 -0
  43. package/build/src/lib/apple-tv/pairing-protocol/constants.js +17 -0
  44. package/build/src/lib/apple-tv/pairing-protocol/index.d.ts +4 -0
  45. package/build/src/lib/apple-tv/pairing-protocol/index.d.ts.map +1 -0
  46. package/build/src/lib/apple-tv/pairing-protocol/index.js +2 -0
  47. package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.d.ts +159 -0
  48. package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.d.ts.map +1 -0
  49. package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.js +494 -0
  50. package/build/src/lib/apple-tv/pairing-protocol/types.d.ts +57 -0
  51. package/build/src/lib/apple-tv/pairing-protocol/types.d.ts.map +1 -0
  52. package/build/src/lib/apple-tv/pairing-protocol/types.js +1 -0
  53. package/build/src/lib/apple-tv/storage/index.d.ts +3 -0
  54. package/build/src/lib/apple-tv/storage/index.d.ts.map +1 -0
  55. package/build/src/lib/apple-tv/storage/index.js +1 -0
  56. package/build/src/lib/apple-tv/storage/pairing-storage.d.ts +12 -0
  57. package/build/src/lib/apple-tv/storage/pairing-storage.d.ts.map +1 -0
  58. package/build/src/lib/apple-tv/storage/pairing-storage.js +36 -0
  59. package/build/src/lib/apple-tv/storage/types.d.ts +5 -0
  60. package/build/src/lib/apple-tv/storage/types.d.ts.map +1 -0
  61. package/build/src/lib/apple-tv/storage/types.js +1 -0
  62. package/build/src/lib/apple-tv/types.d.ts +0 -1
  63. package/build/src/lib/apple-tv/types.d.ts.map +1 -1
  64. package/build/src/lib/bonjour/bonjour-discovery.d.ts.map +1 -1
  65. package/build/src/lib/bonjour/bonjour-discovery.js +2 -0
  66. package/package.json +5 -3
  67. package/scripts/pair-appletv.ts +79 -0
  68. package/scripts/test-tunnel-creation.ts +1 -1
  69. package/src/constants.ts +4 -0
  70. package/src/index.ts +2 -0
  71. package/src/lib/apple-tv/constants.ts +0 -1
  72. package/src/lib/apple-tv/discovery/device-discovery.ts +34 -0
  73. package/src/lib/apple-tv/discovery/index.ts +1 -0
  74. package/src/lib/apple-tv/index.ts +5 -0
  75. package/src/lib/apple-tv/network/constants.ts +9 -0
  76. package/src/lib/apple-tv/network/index.ts +3 -0
  77. package/src/lib/apple-tv/network/network-client.ts +214 -0
  78. package/src/lib/apple-tv/network/types.ts +7 -0
  79. package/src/lib/apple-tv/pairing/index.ts +2 -0
  80. package/src/lib/apple-tv/pairing/pairing-service.ts +175 -0
  81. package/src/lib/apple-tv/pairing/user-input-service.ts +71 -0
  82. package/src/lib/apple-tv/pairing-protocol/constants.ts +19 -0
  83. package/src/lib/apple-tv/pairing-protocol/index.ts +8 -0
  84. package/src/lib/apple-tv/pairing-protocol/pairing-protocol.ts +636 -0
  85. package/src/lib/apple-tv/pairing-protocol/types.ts +60 -0
  86. package/src/lib/apple-tv/storage/index.ts +2 -0
  87. package/src/lib/apple-tv/storage/pairing-storage.ts +60 -0
  88. package/src/lib/apple-tv/storage/types.ts +9 -0
  89. package/src/lib/apple-tv/types.ts +0 -1
  90. package/src/lib/bonjour/bonjour-discovery.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.13.1](https://github.com/appium/appium-ios-remotexpc/compare/v0.13.0...v0.13.1) (2025-11-15)
2
+
3
+ ### Miscellaneous Chores
4
+
5
+ * publish via trusted publisher ([#97](https://github.com/appium/appium-ios-remotexpc/issues/97)) ([0f96a35](https://github.com/appium/appium-ios-remotexpc/commit/0f96a3576e726370a8f13b13d51ba5eaa6402bef))
6
+
7
+ ## [0.13.0](https://github.com/appium/appium-ios-remotexpc/compare/v0.12.0...v0.13.0) (2025-11-07)
8
+
9
+ ### Features
10
+
11
+ * appletv pairing ([#76](https://github.com/appium/appium-ios-remotexpc/issues/76)) ([f974b12](https://github.com/appium/appium-ios-remotexpc/commit/f974b12f137152421f359f667d60d7e116a89353))
12
+
1
13
  ## [0.12.0](https://github.com/appium/appium-ios-remotexpc/compare/v0.11.0...v0.12.0) (2025-11-05)
2
14
 
3
15
  ### Features
@@ -0,0 +1,2 @@
1
+ export declare const STRONGBOX_CONTAINER_NAME = "appium-ios-remotexpc";
2
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,wBAAwB,yBAAyB,CAAC"}
@@ -0,0 +1,3 @@
1
+ // Global constants used across the appium-ios-remotexpc package
2
+ // Strongbox container name for storing credentials and configuration
3
+ export const STRONGBOX_CONTAINER_NAME = 'appium-ios-remotexpc';
@@ -1,3 +1,4 @@
1
+ import { STRONGBOX_CONTAINER_NAME } from './constants.js';
1
2
  import { createLockdownServiceByUDID } from './lib/lockdown/index.js';
2
3
  import { PacketStreamClient, PacketStreamServer, TunnelManager } from './lib/tunnel/index.js';
3
4
  import { TunnelRegistryServer, startTunnelRegistryServer } from './lib/tunnel/tunnel-registry-server.js';
@@ -6,5 +7,5 @@ import * as Services from './services.js';
6
7
  import { startCoreDeviceProxy } from './services/ios/tunnel-service/index.js';
7
8
  export type { DiagnosticsService, MobileImageMounterService, NotificationProxyService, MobileConfigService, PowerAssertionService, PowerAssertionOptions, SpringboardService, WebInspectorService, MisagentService, SyslogService, SocketInfo, TunnelResult, TunnelRegistry, TunnelRegistryEntry, DiagnosticsServiceWithConnection, MobileImageMounterServiceWithConnection, NotificationProxyServiceWithConnection, MobileConfigServiceWithConnection, PowerAssertionServiceWithConnection, SpringboardServiceWithConnection, WebInspectorServiceWithConnection, MisagentServiceWithConnection, } from './lib/types.js';
8
9
  export { PowerAssertionType } from './lib/types.js';
9
- export { createUsbmux, Services, Usbmux, TunnelManager, PacketStreamServer, PacketStreamClient, createLockdownServiceByUDID, startCoreDeviceProxy, TunnelRegistryServer, startTunnelRegistryServer, };
10
+ export { STRONGBOX_CONTAINER_NAME, createUsbmux, Services, Usbmux, TunnelManager, PacketStreamServer, PacketStreamClient, createLockdownServiceByUDID, startCoreDeviceProxy, TunnelRegistryServer, startTunnelRegistryServer, };
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAE9E,YAAY,EACV,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,UAAU,EACV,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,gCAAgC,EAChC,uCAAuC,EACvC,sCAAsC,EACtC,iCAAiC,EACjC,mCAAmC,EACnC,gCAAgC,EAChC,iCAAiC,EACjC,6BAA6B,GAC9B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,EAC3B,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,GAC1B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAE9E,YAAY,EACV,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,UAAU,EACV,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,gCAAgC,EAChC,uCAAuC,EACvC,sCAAsC,EACtC,iCAAiC,EACjC,mCAAmC,EACnC,gCAAgC,EAChC,iCAAiC,EACjC,6BAA6B,GAC9B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EACL,wBAAwB,EACxB,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,EAC3B,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,GAC1B,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { STRONGBOX_CONTAINER_NAME } from './constants.js';
1
2
  import { createLockdownServiceByUDID } from './lib/lockdown/index.js';
2
3
  import { PacketStreamClient, PacketStreamServer, TunnelManager, } from './lib/tunnel/index.js';
3
4
  import { TunnelRegistryServer, startTunnelRegistryServer, } from './lib/tunnel/tunnel-registry-server.js';
@@ -5,4 +6,4 @@ import { Usbmux, createUsbmux } from './lib/usbmux/index.js';
5
6
  import * as Services from './services.js';
6
7
  import { startCoreDeviceProxy } from './services/ios/tunnel-service/index.js';
7
8
  export { PowerAssertionType } from './lib/types.js';
8
- export { createUsbmux, Services, Usbmux, TunnelManager, PacketStreamServer, PacketStreamClient, createLockdownServiceByUDID, startCoreDeviceProxy, TunnelRegistryServer, startTunnelRegistryServer, };
9
+ export { STRONGBOX_CONTAINER_NAME, createUsbmux, Services, Usbmux, TunnelManager, PacketStreamServer, PacketStreamClient, createLockdownServiceByUDID, startCoreDeviceProxy, TunnelRegistryServer, startTunnelRegistryServer, };
@@ -2,7 +2,6 @@ export declare const DEFAULT_PAIRING_CONFIG: {
2
2
  readonly timeout: 30000;
3
3
  readonly discoveryTimeout: 5000;
4
4
  readonly maxRetries: 3;
5
- readonly pairingDirectory: ".pairing";
6
5
  };
7
6
  export declare const PairingDataComponentType: {
8
7
  readonly METHOD: 0;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/lib/apple-tv/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,sBAAsB;;;;;CAKzB,CAAC;AAGX,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgC3B,CAAC;AAGX,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAG1C,eAAO,MAAM,cAAc,QAc1B,CAAC;AAGF,eAAO,MAAM,aAAa,QAAY,CAAC;AAGvC,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAG3C,eAAO,MAAM,YAAY,eAAe,CAAC;AAGzC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAGxC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAGxC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAG5C,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAGnC,eAAO,MAAM,WAAW,IAAO,CAAC;AAChC,eAAO,MAAM,WAAW,IAAO,CAAC;AAChC,eAAO,MAAM,YAAY,IAAO,CAAC;AACjC,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,oBAAoB,KAAO,CAAC;AACzC,eAAO,MAAM,uBAAuB,KAAO,CAAC;AAC5C,eAAO,MAAM,sBAAsB,KAAO,CAAC;AAC3C,eAAO,MAAM,sBAAsB,KAAK,CAAC;AACzC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAGxC,eAAO,MAAM,kBAAkB,KAAO,CAAC;AACvC,eAAO,MAAM,mBAAmB,KAAO,CAAC;AACxC,eAAO,MAAM,mBAAmB,KAAO,CAAC;AACxC,eAAO,MAAM,mBAAmB,KAAO,CAAC;AAGxC,eAAO,MAAM,wBAAwB,KAAO,CAAC;AAC7C,eAAO,MAAM,6BAA6B,KAAO,CAAC;AAClD,eAAO,MAAM,8BAA8B,KAAO,CAAC;AACnD,eAAO,MAAM,8BAA8B,KAAO,CAAC;AAGnD,eAAO,MAAM,uBAAuB,MAAO,CAAC;AAC5C,eAAO,MAAM,4BAA4B,MAAO,CAAC;AACjD,eAAO,MAAM,6BAA6B,MAAO,CAAC;AAClD,eAAO,MAAM,6BAA6B,MAAO,CAAC;AAGlD,eAAO,MAAM,uBAAuB,MAAO,CAAC;AAC5C,eAAO,MAAM,4BAA4B,MAAO,CAAC;AAGjD,eAAO,MAAM,sBAAsB,MAAO,CAAC;AAC3C,eAAO,MAAM,2BAA2B,MAAO,CAAC;AAGhD,eAAO,MAAM,gBAAgB,MAAO,CAAC;AACrC,eAAO,MAAM,iBAAiB,QAAS,CAAC;AACxC,eAAO,MAAM,iBAAiB,aAAa,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/lib/apple-tv/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC;AAGX,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgC3B,CAAC;AAGX,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAG1C,eAAO,MAAM,cAAc,QAc1B,CAAC;AAGF,eAAO,MAAM,aAAa,QAAY,CAAC;AAGvC,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAG3C,eAAO,MAAM,YAAY,eAAe,CAAC;AAGzC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAGxC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAGxC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAG5C,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAGnC,eAAO,MAAM,WAAW,IAAO,CAAC;AAChC,eAAO,MAAM,WAAW,IAAO,CAAC;AAChC,eAAO,MAAM,YAAY,IAAO,CAAC;AACjC,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,oBAAoB,KAAO,CAAC;AACzC,eAAO,MAAM,uBAAuB,KAAO,CAAC;AAC5C,eAAO,MAAM,sBAAsB,KAAO,CAAC;AAC3C,eAAO,MAAM,sBAAsB,KAAK,CAAC;AACzC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAGxC,eAAO,MAAM,kBAAkB,KAAO,CAAC;AACvC,eAAO,MAAM,mBAAmB,KAAO,CAAC;AACxC,eAAO,MAAM,mBAAmB,KAAO,CAAC;AACxC,eAAO,MAAM,mBAAmB,KAAO,CAAC;AAGxC,eAAO,MAAM,wBAAwB,KAAO,CAAC;AAC7C,eAAO,MAAM,6BAA6B,KAAO,CAAC;AAClD,eAAO,MAAM,8BAA8B,KAAO,CAAC;AACnD,eAAO,MAAM,8BAA8B,KAAO,CAAC;AAGnD,eAAO,MAAM,uBAAuB,MAAO,CAAC;AAC5C,eAAO,MAAM,4BAA4B,MAAO,CAAC;AACjD,eAAO,MAAM,6BAA6B,MAAO,CAAC;AAClD,eAAO,MAAM,6BAA6B,MAAO,CAAC;AAGlD,eAAO,MAAM,uBAAuB,MAAO,CAAC;AAC5C,eAAO,MAAM,4BAA4B,MAAO,CAAC;AAGjD,eAAO,MAAM,sBAAsB,MAAO,CAAC;AAC3C,eAAO,MAAM,2BAA2B,MAAO,CAAC;AAGhD,eAAO,MAAM,gBAAgB,MAAO,CAAC;AACrC,eAAO,MAAM,iBAAiB,QAAS,CAAC;AACxC,eAAO,MAAM,iBAAiB,aAAa,CAAC"}
@@ -3,7 +3,6 @@ export const DEFAULT_PAIRING_CONFIG = {
3
3
  timeout: 30000,
4
4
  discoveryTimeout: 5000,
5
5
  maxRetries: 3,
6
- pairingDirectory: '.pairing',
7
6
  };
8
7
  // TLV8 component type identifiers used in pairing data exchange
9
8
  export const PairingDataComponentType = {
@@ -0,0 +1,10 @@
1
+ import { type AppleTVDevice } from '../../bonjour/bonjour-discovery.js';
2
+ import type { PairingConfig } from '../types.js';
3
+ /** Discovers Apple TV devices on the local network using Bonjour */
4
+ export declare class DeviceDiscoveryService {
5
+ private readonly config;
6
+ private readonly log;
7
+ constructor(config: PairingConfig);
8
+ discoverDevices(): Promise<AppleTVDevice[]>;
9
+ }
10
+ //# sourceMappingURL=device-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-discovery.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/discovery/device-discovery.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAEnB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,oEAAoE;AACpE,qBAAa,sBAAsB;IAGrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA8C;gBAErC,MAAM,EAAE,aAAa;IAE5C,eAAe,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;CAkBlD"}
@@ -0,0 +1,22 @@
1
+ import { logger } from '@appium/support';
2
+ import { BonjourDiscovery, } from '../../bonjour/bonjour-discovery.js';
3
+ import { PairingError } from '../errors.js';
4
+ /** Discovers Apple TV devices on the local network using Bonjour */
5
+ export class DeviceDiscoveryService {
6
+ config;
7
+ log = logger.getLogger('DeviceDiscoveryService');
8
+ constructor(config) {
9
+ this.config = config;
10
+ }
11
+ async discoverDevices() {
12
+ try {
13
+ const discovery = new BonjourDiscovery();
14
+ this.log.info(`Discovering Apple TV devices (waiting ${this.config.discoveryTimeout / 1000} seconds)...`);
15
+ return await discovery.discoverAppleTVDevicesWithIP(this.config.discoveryTimeout);
16
+ }
17
+ catch (error) {
18
+ this.log.error('Device discovery failed:', error);
19
+ throw new PairingError('Device discovery failed', 'DISCOVERY_ERROR', error);
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,2 @@
1
+ export { DeviceDiscoveryService } from './device-discovery.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/discovery/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1 @@
1
+ export { DeviceDiscoveryService } from './device-discovery.js';
@@ -6,4 +6,9 @@ export * from './deviceInfo/index.js';
6
6
  export * from './encryption/index.js';
7
7
  export * from './tlv/index.js';
8
8
  export * from './srp/index.js';
9
+ export * from './network/index.js';
10
+ export * from './pairing-protocol/index.js';
11
+ export * from './storage/index.js';
12
+ export * from './discovery/index.js';
13
+ export * from './pairing/index.js';
9
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/apple-tv/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,aAAa,EACb,QAAQ,EACR,6BAA6B,EAC7B,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/apple-tv/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,aAAa,EACb,QAAQ,EACR,6BAA6B,EAC7B,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC"}
@@ -5,3 +5,8 @@ export * from './deviceInfo/index.js';
5
5
  export * from './encryption/index.js';
6
6
  export * from './tlv/index.js';
7
7
  export * from './srp/index.js';
8
+ export * from './network/index.js';
9
+ export * from './pairing-protocol/index.js';
10
+ export * from './storage/index.js';
11
+ export * from './discovery/index.js';
12
+ export * from './pairing/index.js';
@@ -0,0 +1,10 @@
1
+ /** Constants for network protocol */
2
+ export declare const NETWORK_CONSTANTS: {
3
+ readonly MAGIC: "RPPairing";
4
+ readonly MAGIC_LENGTH: 9;
5
+ readonly HEADER_LENGTH: 11;
6
+ readonly LENGTH_FIELD_SIZE: 2;
7
+ readonly MAX_TLV_FRAGMENT_SIZE: 255;
8
+ readonly PIN_INPUT_TIMEOUT_MS: 30000;
9
+ };
10
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/network/constants.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,eAAO,MAAM,iBAAiB;;;;;;;CAOpB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /** Constants for network protocol */
2
+ export const NETWORK_CONSTANTS = {
3
+ MAGIC: 'RPPairing',
4
+ MAGIC_LENGTH: 9,
5
+ HEADER_LENGTH: 11,
6
+ LENGTH_FIELD_SIZE: 2,
7
+ MAX_TLV_FRAGMENT_SIZE: 255,
8
+ PIN_INPUT_TIMEOUT_MS: 30000,
9
+ };
@@ -0,0 +1,4 @@
1
+ export { NetworkClient } from './network-client.js';
2
+ export { NETWORK_CONSTANTS } from './constants.js';
3
+ export type { NetworkClientInterface } from './types.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/network/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,YAAY,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { NetworkClient } from './network-client.js';
2
+ export { NETWORK_CONSTANTS } from './constants.js';
@@ -0,0 +1,16 @@
1
+ import type { PairingConfig } from '../types.js';
2
+ import type { NetworkClientInterface } from './types.js';
3
+ /** Handles TCP socket communication with Apple TV devices */
4
+ export declare class NetworkClient implements NetworkClientInterface {
5
+ private readonly config;
6
+ private socket;
7
+ private connectionTimeoutId;
8
+ constructor(config: PairingConfig);
9
+ connect(ip: string, port: number): Promise<void>;
10
+ sendPacket(data: any): Promise<void>;
11
+ receiveResponse(): Promise<any>;
12
+ disconnect(): void;
13
+ private createRPPairingPacket;
14
+ private cleanup;
15
+ }
16
+ //# sourceMappingURL=network-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-client.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/network/network-client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAIzD,6DAA6D;AAC7D,qBAAa,aAAc,YAAW,sBAAsB;IAI9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,mBAAmB,CAA+B;gBAE7B,MAAM,EAAE,aAAa;IAE5C,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgDhD,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBpC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC;IAmGrC,UAAU,IAAI,IAAI;IAIlB,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,OAAO;CAWhB"}
@@ -0,0 +1,169 @@
1
+ import { logger } from '@appium/support';
2
+ import * as net from 'node:net';
3
+ import { NetworkError } from '../errors.js';
4
+ import { NETWORK_CONSTANTS } from './constants.js';
5
+ const log = logger.getLogger('NetworkClient');
6
+ /** Handles TCP socket communication with Apple TV devices */
7
+ export class NetworkClient {
8
+ config;
9
+ socket = null;
10
+ connectionTimeoutId = null;
11
+ constructor(config) {
12
+ this.config = config;
13
+ }
14
+ async connect(ip, port) {
15
+ log.debug(`Connecting to ${ip}:${port}`);
16
+ return new Promise((resolve, reject) => {
17
+ const cancelTimeout = () => {
18
+ if (this.connectionTimeoutId) {
19
+ clearTimeout(this.connectionTimeoutId);
20
+ this.connectionTimeoutId = null;
21
+ }
22
+ };
23
+ this.socket = new net.Socket();
24
+ this.socket.setTimeout(this.config.timeout);
25
+ this.socket.once('connect', () => {
26
+ log.debug('Connected successfully');
27
+ cancelTimeout();
28
+ resolve();
29
+ });
30
+ this.socket.once('error', (error) => {
31
+ log.error('Connection error:', error);
32
+ cancelTimeout();
33
+ reject(new NetworkError(`Connection failed: ${error.message}`));
34
+ });
35
+ this.socket.once('timeout', () => {
36
+ log.error('Socket timeout');
37
+ cancelTimeout();
38
+ reject(new NetworkError('Socket timeout'));
39
+ });
40
+ this.socket.once('close', () => {
41
+ cancelTimeout();
42
+ });
43
+ this.connectionTimeoutId = setTimeout(() => {
44
+ log.error('Connection attempt timeout');
45
+ this.cleanup();
46
+ reject(new NetworkError(`Connection timeout after ${this.config.timeout}ms`));
47
+ }, this.config.timeout);
48
+ this.socket.connect(port, ip);
49
+ });
50
+ }
51
+ async sendPacket(data) {
52
+ if (!this.socket) {
53
+ throw new NetworkError('Socket not connected');
54
+ }
55
+ const packet = this.createRPPairingPacket(data);
56
+ log.debug('Sending packet:', { size: packet.length });
57
+ return new Promise((resolve, reject) => {
58
+ if (!this.socket) {
59
+ reject(new NetworkError('Socket disconnected during send'));
60
+ return;
61
+ }
62
+ this.socket.write(packet, (error) => {
63
+ if (error) {
64
+ log.error('Send packet error:', error);
65
+ reject(new NetworkError('Failed to send packet'));
66
+ }
67
+ else {
68
+ resolve();
69
+ }
70
+ });
71
+ });
72
+ }
73
+ async receiveResponse() {
74
+ if (!this.socket) {
75
+ throw new NetworkError('Socket not connected');
76
+ }
77
+ return new Promise((resolve, reject) => {
78
+ let buffer = Buffer.alloc(0);
79
+ let expectedLength = null;
80
+ let headerRead = false;
81
+ let timeoutId = null;
82
+ const cleanup = () => {
83
+ if (timeoutId) {
84
+ clearTimeout(timeoutId);
85
+ timeoutId = null;
86
+ }
87
+ if (this.socket) {
88
+ this.socket.removeListener('data', onData);
89
+ this.socket.removeListener('error', onError);
90
+ }
91
+ };
92
+ const onData = (chunk) => {
93
+ try {
94
+ buffer = Buffer.concat([buffer, chunk]);
95
+ if (!headerRead && buffer.length >= NETWORK_CONSTANTS.HEADER_LENGTH) {
96
+ const magic = buffer
97
+ .slice(0, NETWORK_CONSTANTS.MAGIC_LENGTH)
98
+ .toString('ascii');
99
+ if (magic !== NETWORK_CONSTANTS.MAGIC) {
100
+ throw new NetworkError(`Invalid protocol magic: expected '${NETWORK_CONSTANTS.MAGIC}', got '${magic}'`);
101
+ }
102
+ expectedLength = buffer.readUInt16BE(NETWORK_CONSTANTS.MAGIC_LENGTH);
103
+ headerRead = true;
104
+ log.debug(`Response header parsed: expecting ${expectedLength} bytes`);
105
+ }
106
+ if (headerRead &&
107
+ expectedLength !== null &&
108
+ buffer.length >= NETWORK_CONSTANTS.HEADER_LENGTH + expectedLength) {
109
+ const bodyBytes = buffer.slice(NETWORK_CONSTANTS.HEADER_LENGTH, NETWORK_CONSTANTS.HEADER_LENGTH + expectedLength);
110
+ const response = JSON.parse(bodyBytes.toString('utf8'));
111
+ log.debug('Response received and parsed successfully');
112
+ cleanup();
113
+ resolve(response);
114
+ }
115
+ }
116
+ catch (error) {
117
+ log.error('Parse response error:', error);
118
+ cleanup();
119
+ reject(new NetworkError(`Failed to parse response: ${error.message}`));
120
+ }
121
+ };
122
+ const onError = (error) => {
123
+ log.error('Socket error during receive:', error);
124
+ cleanup();
125
+ reject(new NetworkError(`Socket error: ${error.message}`));
126
+ };
127
+ const onClose = () => {
128
+ if (timeoutId) {
129
+ clearTimeout(timeoutId);
130
+ }
131
+ };
132
+ if (this.socket) {
133
+ this.socket.once('data', onData);
134
+ this.socket.once('error', onError);
135
+ this.socket.once('close', onClose);
136
+ timeoutId = setTimeout(() => {
137
+ log.error(`Response timeout after ${this.config.timeout}ms`);
138
+ cleanup();
139
+ reject(new NetworkError(`Response timeout after ${this.config.timeout}ms`));
140
+ }, this.config.timeout);
141
+ }
142
+ else {
143
+ reject(new NetworkError('Socket not available'));
144
+ }
145
+ });
146
+ }
147
+ disconnect() {
148
+ this.cleanup();
149
+ }
150
+ createRPPairingPacket(jsonData) {
151
+ const jsonString = JSON.stringify(jsonData);
152
+ const bodyBytes = Buffer.from(jsonString, 'utf8');
153
+ const magic = Buffer.from(NETWORK_CONSTANTS.MAGIC, 'ascii');
154
+ const length = Buffer.alloc(NETWORK_CONSTANTS.LENGTH_FIELD_SIZE);
155
+ length.writeUInt16BE(bodyBytes.length, 0);
156
+ return Buffer.concat([magic, length, bodyBytes]);
157
+ }
158
+ cleanup() {
159
+ if (this.connectionTimeoutId) {
160
+ clearTimeout(this.connectionTimeoutId);
161
+ this.connectionTimeoutId = null;
162
+ }
163
+ if (this.socket) {
164
+ this.socket.removeAllListeners();
165
+ this.socket.destroy();
166
+ this.socket = null;
167
+ }
168
+ }
169
+ }
@@ -0,0 +1,8 @@
1
+ /** Interface for network communication with Apple TV devices */
2
+ export interface NetworkClientInterface {
3
+ connect(ip: string, port: number): Promise<void>;
4
+ sendPacket(data: any): Promise<void>;
5
+ receiveResponse(): Promise<any>;
6
+ disconnect(): void;
7
+ }
8
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/network/types.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,UAAU,IAAI,IAAI,CAAC;CACpB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export { UserInputService } from './user-input-service.js';
2
+ export { AppleTVPairingService } from './pairing-service.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/pairing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { UserInputService } from './user-input-service.js';
2
+ export { AppleTVPairingService } from './pairing-service.js';
@@ -0,0 +1,15 @@
1
+ import type { AppleTVDevice } from '../../bonjour/bonjour-discovery.js';
2
+ import type { UserInputInterface } from '../pairing-protocol/types.js';
3
+ import type { PairingConfig, PairingResult } from '../types.js';
4
+ /** Main service orchestrating Apple TV device discovery and pairing */
5
+ export declare class AppleTVPairingService {
6
+ private readonly networkClient;
7
+ private readonly discoveryService;
8
+ private readonly userInput;
9
+ private readonly pairingProtocol;
10
+ constructor(userInput: UserInputInterface, config?: PairingConfig);
11
+ discoverAndPair(deviceSelector?: string): Promise<PairingResult>;
12
+ pairWithDevice(device: AppleTVDevice): Promise<string>;
13
+ private selectDevice;
14
+ }
15
+ //# sourceMappingURL=pairing-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing-service.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/pairing/pairing-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAOxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIhE,uEAAuE;AACvE,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAyB;IAC1D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;gBAGhD,SAAS,EAAE,kBAAkB,EAC7B,MAAM,GAAE,aAAsC;IAW1C,eAAe,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA6BhE,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;YAyB9C,YAAY;CAwF3B"}
@@ -0,0 +1,112 @@
1
+ import { getLogger } from '../../logger.js';
2
+ import { DEFAULT_PAIRING_CONFIG } from '../constants.js';
3
+ import { DeviceDiscoveryService } from '../discovery/index.js';
4
+ import { PairingError } from '../errors.js';
5
+ import { NetworkClient } from '../network/index.js';
6
+ import { PairingProtocol } from '../pairing-protocol/index.js';
7
+ const log = getLogger('AppleTVPairingService');
8
+ /** Main service orchestrating Apple TV device discovery and pairing */
9
+ export class AppleTVPairingService {
10
+ networkClient;
11
+ discoveryService;
12
+ userInput;
13
+ pairingProtocol;
14
+ constructor(userInput, config = DEFAULT_PAIRING_CONFIG) {
15
+ this.networkClient = new NetworkClient(config);
16
+ this.discoveryService = new DeviceDiscoveryService(config);
17
+ this.userInput = userInput;
18
+ this.pairingProtocol = new PairingProtocol(this.networkClient, this.userInput);
19
+ }
20
+ async discoverAndPair(deviceSelector) {
21
+ try {
22
+ const devices = await this.discoveryService.discoverDevices();
23
+ if (devices.length === 0) {
24
+ const errorMessage = 'No Apple TV pairing devices found. Please ensure your Apple TV is on the same network and in pairing mode.';
25
+ log.error(errorMessage);
26
+ throw new PairingError(errorMessage, 'NO_DEVICES');
27
+ }
28
+ const device = await this.selectDevice(devices, deviceSelector);
29
+ const pairingFile = await this.pairWithDevice(device);
30
+ return {
31
+ success: true,
32
+ deviceId: device.identifier,
33
+ pairingFile,
34
+ };
35
+ }
36
+ catch (error) {
37
+ log.error('Pairing failed:', error);
38
+ return {
39
+ success: false,
40
+ deviceId: 'unknown',
41
+ error: error instanceof Error ? error : new Error(String(error)),
42
+ };
43
+ }
44
+ }
45
+ async pairWithDevice(device) {
46
+ try {
47
+ // Use IP if available, otherwise fall back to hostname
48
+ const connectionTarget = device.ip ?? device.hostname;
49
+ if (!connectionTarget) {
50
+ throw new PairingError('Neither IP address nor hostname available for device', 'NO_CONNECTION_TARGET');
51
+ }
52
+ log.info(`Connecting to device ${device.name} at ${connectionTarget}:${device.port}`);
53
+ await this.networkClient.connect(connectionTarget, device.port);
54
+ return await this.pairingProtocol.executePairingFlow(device);
55
+ }
56
+ catch (error) {
57
+ log.error(`Pairing with device ${device.name} failed:`, error);
58
+ throw error;
59
+ }
60
+ finally {
61
+ this.networkClient.disconnect();
62
+ }
63
+ }
64
+ async selectDevice(devices, deviceSelector) {
65
+ // If no selector provided, always prompt user to choose (even for single device)
66
+ if (!deviceSelector) {
67
+ log.info(`Found ${devices.length} device(s):`);
68
+ devices.forEach((device, index) => {
69
+ log.info(` [${index}] ${device.name} (${device.identifier}) - ${device.model} v${device.version}`);
70
+ });
71
+ const prompt = devices.length === 1
72
+ ? 'Press Enter to select device [0], or enter index: '
73
+ : `Select device by index (0-${devices.length - 1}): `;
74
+ const indexStr = await this.userInput.promptForInput(prompt);
75
+ const trimmed = indexStr.trim();
76
+ // If user just presses Enter and there's only one device, select it
77
+ if (trimmed === '' && devices.length === 1) {
78
+ log.info(`Selected device: ${devices[0].name} (${devices[0].identifier})`);
79
+ return devices[0];
80
+ }
81
+ const index = parseInt(trimmed, 10);
82
+ if (isNaN(index) || index < 0 || index >= devices.length) {
83
+ throw new PairingError(`Invalid device index: ${trimmed}. Must be between 0 and ${devices.length - 1}`, 'INVALID_DEVICE_SELECTION');
84
+ }
85
+ log.info(`Selected device: ${devices[index].name} (${devices[index].identifier})`);
86
+ return devices[index];
87
+ }
88
+ // Try to match by index first
89
+ const indexMatch = parseInt(deviceSelector, 10);
90
+ if (!isNaN(indexMatch) && indexMatch >= 0 && indexMatch < devices.length) {
91
+ log.info(`Selected device by index ${indexMatch}: ${devices[indexMatch].name}`);
92
+ return devices[indexMatch];
93
+ }
94
+ // Try to match by name (case-insensitive)
95
+ const nameMatch = devices.find((device) => device.name.toLowerCase() === deviceSelector.toLowerCase());
96
+ if (nameMatch) {
97
+ log.info(`Selected device by name: ${nameMatch.name} (${nameMatch.identifier})`);
98
+ return nameMatch;
99
+ }
100
+ // Try to match by identifier (case-insensitive)
101
+ const identifierMatch = devices.find((device) => device.identifier.toLowerCase() === deviceSelector.toLowerCase());
102
+ if (identifierMatch) {
103
+ log.info(`Selected device by identifier: ${identifierMatch.name} (${identifierMatch.identifier})`);
104
+ return identifierMatch;
105
+ }
106
+ // No match found
107
+ const availableDevices = devices
108
+ .map((device, index) => ` [${index}] ${device.name} (${device.identifier})`)
109
+ .join('\n');
110
+ throw new PairingError(`Device '${deviceSelector}' not found. Available devices:\n${availableDevices}`, 'DEVICE_NOT_FOUND');
111
+ }
112
+ }
@@ -0,0 +1,8 @@
1
+ import type { UserInputInterface } from '../pairing-protocol/types.js';
2
+ /** Handles user interaction for PIN input during pairing */
3
+ export declare class UserInputService implements UserInputInterface {
4
+ private readonly log;
5
+ promptForPIN(): Promise<string>;
6
+ promptForInput(prompt: string): Promise<string>;
7
+ }
8
+ //# sourceMappingURL=user-input-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-input-service.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/pairing/user-input-service.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,4DAA4D;AAC5D,qBAAa,gBAAiB,YAAW,kBAAkB;IACzD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAwC;IAEtD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAyC/B,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAkBtD"}
@@ -0,0 +1,61 @@
1
+ import { logger } from '@appium/support';
2
+ import { createInterface } from 'node:readline';
3
+ import { PairingError } from '../errors.js';
4
+ import { NETWORK_CONSTANTS } from '../network/constants.js';
5
+ /** Handles user interaction for PIN input during pairing */
6
+ export class UserInputService {
7
+ log = logger.getLogger('UserInputService');
8
+ async promptForPIN() {
9
+ const rl = createInterface({
10
+ input: process.stdin,
11
+ output: process.stdout,
12
+ });
13
+ let timeoutId = null;
14
+ try {
15
+ const pin = await new Promise((resolve, reject) => {
16
+ timeoutId = setTimeout(() => {
17
+ reject(new PairingError('PIN input timeout', 'INPUT_TIMEOUT'));
18
+ }, NETWORK_CONSTANTS.PIN_INPUT_TIMEOUT_MS);
19
+ rl.question('Enter PIN from Apple TV screen: ', (answer) => {
20
+ // Clear timeout since we got the PIN
21
+ if (timeoutId) {
22
+ clearTimeout(timeoutId);
23
+ timeoutId = null;
24
+ }
25
+ resolve(answer);
26
+ });
27
+ });
28
+ const cleanPin = pin.trim();
29
+ if (!/^\d+$/.test(cleanPin)) {
30
+ this.log.error('Invalid PIN format');
31
+ throw new PairingError('PIN must contain only digits', 'INVALID_PIN');
32
+ }
33
+ this.log.debug('PIN received successfully');
34
+ return cleanPin;
35
+ }
36
+ finally {
37
+ // Clean up timeout if error occurred before clearing
38
+ if (timeoutId) {
39
+ clearTimeout(timeoutId);
40
+ }
41
+ rl.close();
42
+ }
43
+ }
44
+ async promptForInput(prompt) {
45
+ const rl = createInterface({
46
+ input: process.stdin,
47
+ output: process.stdout,
48
+ });
49
+ try {
50
+ const input = await new Promise((resolve) => {
51
+ rl.question(prompt, (answer) => {
52
+ resolve(answer);
53
+ });
54
+ });
55
+ return input.trim();
56
+ }
57
+ finally {
58
+ rl.close();
59
+ }
60
+ }
61
+ }