appium-ios-remotexpc 5.19.0 → 5.19.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [5.19.2](https://github.com/appium/appium-ios-remotexpc/compare/v5.19.1...v5.19.2) (2026-09-16)
2
+
3
+ ### Bug Fixes
4
+
5
+ * **scripts:** fall back to a free registry port when starting the Apple TV tunnel ([#327](https://github.com/appium/appium-ios-remotexpc/issues/327)) ([84dccad](https://github.com/appium/appium-ios-remotexpc/commit/84dccad88e3f52effc54f62130f33ab400f146de))
6
+
7
+ ## [5.19.1](https://github.com/appium/appium-ios-remotexpc/compare/v5.19.0...v5.19.1) (2026-09-15)
8
+
9
+ ### Bug Fixes
10
+
11
+ * **test:** stop registry server spec from overwriting the persisted tunnel port ([#326](https://github.com/appium/appium-ios-remotexpc/issues/326)) ([0b62a88](https://github.com/appium/appium-ios-remotexpc/commit/0b62a8889dfd1f9ac7c09fc7d5eeb7c3935a6d8e))
12
+
1
13
  ## [5.19.0](https://github.com/appium/appium-ios-remotexpc/compare/v5.18.9...v5.19.0) (2026-09-08)
2
14
 
3
15
  ### Features
package/README.md CHANGED
@@ -102,7 +102,7 @@ The `appium-ios-tuntap (previously tuntap-bridge)` module plays a crucial role i
102
102
  5. **Service Access**: Enables access to iOS shim services through the tunnel
103
103
 
104
104
  **Technical Details:**
105
- - **Platform Support**: Works on both macOS and Linux
105
+ - **Platform Support**: Works on macOS, Linux, and Windows (Windows uses WinTun and requires an elevated shell)
106
106
  - **IPv6 Support**: Creates IPv6 tunnels for modern iOS communication
107
107
  - **Packet Handling**: Manages packet routing between virtual interface and device
108
108
  - **Automatic Cleanup**: Properly closes tunnels and cleans up interfaces
@@ -118,7 +118,7 @@ The `appium-ios-tuntap (previously tuntap-bridge)` module plays a crucial role i
118
118
  ```typescript
119
119
  import {
120
120
  createLockdownServiceByUDID,
121
- rsdSessionLockKey,
121
+ discoverServices,
122
122
  startCoreDeviceProxyTcp,
123
123
  TunnelManager,
124
124
  } from 'appium-ios-remotexpc';
@@ -137,21 +137,13 @@ const { socket, cert, key } = await startCoreDeviceProxyTcp(
137
137
  const tunnel = await TunnelManager.getTunnel(socket, { cert, key });
138
138
  console.log(`Tunnel created at ${tunnel.Address} with RSD port ${tunnel.RsdPort}`);
139
139
 
140
- // Discover RSD services (serialized per tunnel; closed before return)
141
- await TunnelManager.runSerializedRsdSession(
142
- rsdSessionLockKey(tunnel.Address, tunnel.RsdPort),
143
- async () => {
144
- const remoteXPC = await TunnelManager.connectRemoteXPCUnlocked(
145
- tunnel.Address,
146
- tunnel.RsdPort,
147
- );
148
- try {
149
- console.log(remoteXPC.getServices());
150
- } finally {
151
- await remoteXPC.close();
152
- }
153
- },
154
- );
140
+ // Discover RSD services (concurrent calls for the same device are coalesced;
141
+ // the RSD connection is closed before the promise resolves)
142
+ if (!tunnel.RsdPort) {
143
+ throw new Error('Tunnel did not report an RSD port');
144
+ }
145
+ const services = await discoverServices(udid, tunnel.Address, tunnel.RsdPort);
146
+ console.log(services);
155
147
  ```
156
148
 
157
149
  ### iPhone / iPad over WiFi (usbmuxd “network” devices)
@@ -166,7 +158,7 @@ There is no separate “WiFi API” in this library: call `createUsbmux()` → `
166
158
  2. Allow the device to connect over WiFi (e.g. in Finder under the device, enable **Show this [device] when on WiFi**, or use Xcode **Devices and Simulators** with the equivalent option so lockdown can reach the device without USB).
167
159
  3. Confirm **usbmuxd** reports the device with **`ConnectionType: Network`**—for example by logging the result of `listDevices()` from this library, or by checking another usbmuxd client’s device list while the device is on the same network and not on USB.
168
160
 
169
- For an end-to-end tunnel smoke test with the tunnel registry HTTP API, use `npm run tunnel-creation` or `npm run test:tunnel-creation` (see `scripts/test-tunnel-creation.ts`), usually with **sudo** for TUN/TAP.
161
+ For an end-to-end tunnel smoke test with the tunnel registry HTTP API, use `npm run tunnel-creation` (see `scripts/tunnel-creation.mjs`), usually with **sudo** for TUN/TAP.
170
162
 
171
163
  ### Apple TV / tvOS over WiFi
172
164
 
@@ -219,12 +211,13 @@ All pull requests must pass these checks before merging. The workflows are defin
219
211
  - `npm run lint` - Run lint
220
212
  - `npm run format` - Run format
221
213
  - `npm run lint:fix` - Run lint with auto-fix
222
- - `npm test` - Run tests (requires sudo privileges for tunneling)
214
+ - `npm test` - Run unit tests
215
+ - `npm run test:all` - Run unit and integration tests (see [Testing](#testing) for integration test requirements)
223
216
 
224
217
  CLI helpers under `scripts/` are ESM (`.mjs`) and load the library via the package entrypoint. Run `npm run build` before using them so `appium-ios-remotexpc` resolves to `build/`.
225
218
 
226
- - `npm run tunnel-creation` / `npm run test:tunnel-creation` — Create USB tunnels and start the tunnel registry HTTP API (requires `sudo`)
227
- - `npm run test:tunnel-creation:lsof` — Same as above with `--keep-open` (for inspecting open sockets)
219
+ - `npm run tunnel-creation` — Create USB tunnels and start the tunnel registry HTTP API (requires `sudo`)
220
+ - `npm run tunnel-creation -- --keep-open` — Same as above with `--keep-open` (for inspecting open sockets)
228
221
  - `npm run pair-appletv` — Pair an Apple TV over WiFi for Remote XPC (requires `sudo`)
229
222
  - `npm run start-appletv-tunnel` — Start an Apple TV WiFi tunnel and tunnel registry (requires `sudo`)
230
223
 
@@ -250,8 +243,11 @@ Pass `--help` after `--` to any of these npm scripts to see CLI flags (for examp
250
243
  ## Testing
251
244
 
252
245
  ```bash
253
- # Run all tests
246
+ # Run unit tests
254
247
  npm test
248
+
249
+ # Run unit and integration tests
250
+ npm run test:all
255
251
  ```
256
252
 
257
253
  Note: Integration tests require:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-ios-remotexpc",
3
- "version": "5.19.0",
3
+ "version": "5.19.2",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "bugs": {
@@ -0,0 +1,39 @@
1
+ import net from 'node:net';
2
+
3
+ const TUNNEL_REGISTRY_HOST = '127.0.0.1';
4
+
5
+ /**
6
+ * Binds to `port` (0 = OS-assigned), then releases it and resolves the bound port.
7
+ * @param {number} port
8
+ * @returns {Promise<number>}
9
+ */
10
+ function bindFreePort(port) {
11
+ return new Promise((resolve, reject) => {
12
+ const probe = net.createServer();
13
+ probe.once('error', reject);
14
+ probe.listen(port, TUNNEL_REGISTRY_HOST, () => {
15
+ const address = probe.address();
16
+ if (!address || typeof address === 'string') {
17
+ probe.close(() => reject(new Error('Could not resolve a free port')));
18
+ return;
19
+ }
20
+ probe.close((err) => (err ? reject(err) : resolve(address.port)));
21
+ });
22
+ });
23
+ }
24
+
25
+ /**
26
+ * Returns `preferredPort` if free, else a free port chosen by the OS.
27
+ * @param {number} preferredPort
28
+ * @param {{warn: (message: string) => void}} log
29
+ * @returns {Promise<number>}
30
+ */
31
+ export async function resolveAvailableRegistryPort(preferredPort, log) {
32
+ try {
33
+ return await bindFreePort(preferredPort);
34
+ } catch {
35
+ const port = await bindFreePort(0);
36
+ log.warn(`Tunnel registry port ${preferredPort} is in use; using ${port} instead`);
37
+ return port;
38
+ }
39
+ }
@@ -21,6 +21,7 @@ import {Command} from 'commander';
21
21
  import {DEFAULT_TUNNEL_REGISTRY_PORT, DEFAULT_WIRELESS_APPLETV_DISCOVERY_TIMEOUT_MS} from './lib/constants.mjs';
22
22
  import {parseNonNegativeIntegerOption, parsePortOption, parsePositiveIntegerOption} from './lib/options.mjs';
23
23
  import {startTimeoutProgressLogger} from './lib/progress.mjs';
24
+ import {resolveAvailableRegistryPort} from './lib/registry-port.mjs';
24
25
  import {assertRoot} from './lib/root.mjs';
25
26
  import {sleep} from './lib/timers.mjs';
26
27
  import {
@@ -432,7 +433,8 @@ async function main() {
432
433
  program.parse(process.argv);
433
434
  const options = program.opts();
434
435
  const deviceIdentifier = program.args[0];
435
- const registryPort = options.tunnelRegistryPort ?? DEFAULT_TUNNEL_REGISTRY_PORT;
436
+ const registryPort =
437
+ options.tunnelRegistryPort ?? (await resolveAvailableRegistryPort(DEFAULT_TUNNEL_REGISTRY_PORT, log));
436
438
 
437
439
  await assertRoot(path.join('scripts', path.basename(fileURLToPath(import.meta.url))));
438
440