appium-ios-remotexpc 0.21.2 → 0.23.0

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 (114) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/src/index.d.ts +1 -1
  3. package/build/src/index.d.ts.map +1 -1
  4. package/build/src/lib/apple-tv/constants.d.ts +4 -3
  5. package/build/src/lib/apple-tv/constants.d.ts.map +1 -1
  6. package/build/src/lib/apple-tv/constants.js +10 -3
  7. package/build/src/lib/apple-tv/discovery/device-discovery.d.ts.map +1 -1
  8. package/build/src/lib/apple-tv/discovery/device-discovery.js +2 -2
  9. package/build/src/lib/apple-tv/encryption/index.d.ts +1 -0
  10. package/build/src/lib/apple-tv/encryption/index.d.ts.map +1 -1
  11. package/build/src/lib/apple-tv/encryption/index.js +1 -0
  12. package/build/src/lib/apple-tv/encryption/x25519.d.ts +8 -0
  13. package/build/src/lib/apple-tv/encryption/x25519.d.ts.map +1 -0
  14. package/build/src/lib/apple-tv/encryption/x25519.js +52 -0
  15. package/build/src/lib/apple-tv/index.d.ts +1 -0
  16. package/build/src/lib/apple-tv/index.d.ts.map +1 -1
  17. package/build/src/lib/apple-tv/index.js +1 -0
  18. package/build/src/lib/apple-tv/network/network-client.js +2 -2
  19. package/build/src/lib/apple-tv/pairing/user-input-service.d.ts.map +1 -1
  20. package/build/src/lib/apple-tv/pairing/user-input-service.js +2 -2
  21. package/build/src/lib/apple-tv/pairing-protocol/constants.d.ts +17 -0
  22. package/build/src/lib/apple-tv/pairing-protocol/constants.d.ts.map +1 -1
  23. package/build/src/lib/apple-tv/pairing-protocol/constants.js +25 -0
  24. package/build/src/lib/apple-tv/pairing-protocol/index.d.ts +2 -1
  25. package/build/src/lib/apple-tv/pairing-protocol/index.d.ts.map +1 -1
  26. package/build/src/lib/apple-tv/pairing-protocol/index.js +2 -1
  27. package/build/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.d.ts +66 -0
  28. package/build/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.d.ts.map +1 -0
  29. package/build/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.js +178 -0
  30. package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.d.ts +35 -2
  31. package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.d.ts.map +1 -1
  32. package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.js +48 -14
  33. package/build/src/lib/apple-tv/storage/index.d.ts +1 -1
  34. package/build/src/lib/apple-tv/storage/index.d.ts.map +1 -1
  35. package/build/src/lib/apple-tv/storage/pairing-storage.d.ts +4 -1
  36. package/build/src/lib/apple-tv/storage/pairing-storage.d.ts.map +1 -1
  37. package/build/src/lib/apple-tv/storage/pairing-storage.js +59 -4
  38. package/build/src/lib/apple-tv/storage/types.d.ts +7 -1
  39. package/build/src/lib/apple-tv/storage/types.d.ts.map +1 -1
  40. package/build/src/lib/apple-tv/tunnel/index.d.ts +3 -0
  41. package/build/src/lib/apple-tv/tunnel/index.d.ts.map +1 -0
  42. package/build/src/lib/apple-tv/tunnel/index.js +1 -0
  43. package/build/src/lib/apple-tv/tunnel/tunnel-service.d.ts +57 -0
  44. package/build/src/lib/apple-tv/tunnel/tunnel-service.d.ts.map +1 -0
  45. package/build/src/lib/apple-tv/tunnel/tunnel-service.js +357 -0
  46. package/build/src/lib/apple-tv/tunnel/types.d.ts +22 -0
  47. package/build/src/lib/apple-tv/tunnel/types.d.ts.map +1 -0
  48. package/build/src/lib/apple-tv/tunnel/types.js +1 -0
  49. package/build/src/lib/bonjour/bonjour-discovery.d.ts.map +1 -1
  50. package/build/src/lib/bonjour/bonjour-discovery.js +3 -3
  51. package/build/src/lib/lockdown/index.d.ts.map +1 -1
  52. package/build/src/lib/plist/length-based-splitter.d.ts.map +1 -1
  53. package/build/src/lib/plist/length-based-splitter.js +0 -7
  54. package/build/src/lib/tunnel/index.d.ts +1 -0
  55. package/build/src/lib/tunnel/index.d.ts.map +1 -1
  56. package/build/src/lib/tunnel/packet-stream-server.d.ts.map +1 -1
  57. package/build/src/lib/tunnel/tunnel-registry-server.d.ts +1 -0
  58. package/build/src/lib/tunnel/tunnel-registry-server.d.ts.map +1 -1
  59. package/build/src/lib/tunnel/tunnel-registry-server.js +1 -1
  60. package/build/src/lib/types.d.ts +59 -0
  61. package/build/src/lib/types.d.ts.map +1 -1
  62. package/build/src/services/index.d.ts +2 -1
  63. package/build/src/services/index.d.ts.map +1 -1
  64. package/build/src/services/index.js +2 -1
  65. package/build/src/services/ios/afc/codec.d.ts +12 -0
  66. package/build/src/services/ios/afc/codec.d.ts.map +1 -1
  67. package/build/src/services/ios/afc/codec.js +26 -0
  68. package/build/src/services/ios/afc/index.d.ts.map +1 -1
  69. package/build/src/services/ios/afc/index.js +3 -14
  70. package/build/src/services/ios/afc/stream-utils.d.ts.map +1 -1
  71. package/build/src/services/ios/afc/stream-utils.js +0 -2
  72. package/build/src/services/ios/crash-reports/index.d.ts +54 -0
  73. package/build/src/services/ios/crash-reports/index.d.ts.map +1 -0
  74. package/build/src/services/ios/crash-reports/index.js +136 -0
  75. package/build/src/services/ios/mobile-config/index.js +2 -2
  76. package/build/src/services.d.ts +6 -1
  77. package/build/src/services.d.ts.map +1 -1
  78. package/build/src/services.js +14 -0
  79. package/package.json +3 -1
  80. package/scripts/pair-appletv.ts +2 -2
  81. package/scripts/start-appletv-tunnel.ts +178 -0
  82. package/scripts/test-tunnel-creation.ts +32 -23
  83. package/src/index.ts +3 -0
  84. package/src/lib/apple-tv/constants.ts +11 -3
  85. package/src/lib/apple-tv/discovery/device-discovery.ts +2 -3
  86. package/src/lib/apple-tv/encryption/index.ts +6 -0
  87. package/src/lib/apple-tv/encryption/x25519.ts +79 -0
  88. package/src/lib/apple-tv/index.ts +1 -0
  89. package/src/lib/apple-tv/network/network-client.ts +2 -2
  90. package/src/lib/apple-tv/pairing/user-input-service.ts +2 -2
  91. package/src/lib/apple-tv/pairing-protocol/constants.ts +29 -0
  92. package/src/lib/apple-tv/pairing-protocol/index.ts +12 -1
  93. package/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.ts +329 -0
  94. package/src/lib/apple-tv/pairing-protocol/pairing-protocol.ts +49 -19
  95. package/src/lib/apple-tv/storage/index.ts +1 -1
  96. package/src/lib/apple-tv/storage/pairing-storage.ts +73 -5
  97. package/src/lib/apple-tv/storage/types.ts +8 -1
  98. package/src/lib/apple-tv/tunnel/index.ts +2 -0
  99. package/src/lib/apple-tv/tunnel/tunnel-service.ts +543 -0
  100. package/src/lib/apple-tv/tunnel/types.ts +23 -0
  101. package/src/lib/bonjour/bonjour-discovery.ts +3 -5
  102. package/src/lib/lockdown/index.ts +0 -7
  103. package/src/lib/plist/length-based-splitter.ts +0 -22
  104. package/src/lib/tunnel/index.ts +2 -8
  105. package/src/lib/tunnel/packet-stream-server.ts +0 -8
  106. package/src/lib/tunnel/tunnel-registry-server.ts +1 -1
  107. package/src/lib/types.ts +70 -0
  108. package/src/services/index.ts +2 -0
  109. package/src/services/ios/afc/codec.ts +34 -0
  110. package/src/services/ios/afc/index.ts +4 -17
  111. package/src/services/ios/afc/stream-utils.ts +0 -2
  112. package/src/services/ios/crash-reports/index.ts +180 -0
  113. package/src/services/ios/mobile-config/index.ts +2 -2
  114. package/src/services.ts +27 -0
package/src/lib/types.ts CHANGED
@@ -1495,3 +1495,73 @@ export interface MisagentServiceWithConnection {
1495
1495
  misagentService: MisagentService;
1496
1496
  remoteXPC: RemoteXpcConnection;
1497
1497
  }
1498
+
1499
+ /**
1500
+ * Options for pulling crash reports from device to local
1501
+ */
1502
+ export interface CrashReportsPullOptions {
1503
+ /**
1504
+ * If true, deletes crash reports from the remote device after pulling to local.
1505
+ * @default false
1506
+ */
1507
+ erase?: boolean;
1508
+ /**
1509
+ * Glob pattern to filter crash reports (e.g., '*.ips', 'Siri*', '**\/*.crash')
1510
+ */
1511
+ match?: string;
1512
+ }
1513
+
1514
+ /**
1515
+ * CrashReportsService provides an API to manage crash reports on iOS devices
1516
+ */
1517
+ export interface CrashReportsService extends BaseService {
1518
+ /**
1519
+ * List files and folders in the crash report's directory
1520
+ *
1521
+ * Crash reports are primarily stored as .ips files, which contain detailed information about
1522
+ * app crashes, including stack traces, thread states, and device information.
1523
+ * In addition to .ips files, the crash reports directory may contain:
1524
+ * - Sysdiagnose tarballs (comprehensive system diagnostic archives)
1525
+ * - Special directories like /Retired, /Cloud, /Assistant, etc.
1526
+ *
1527
+ * For details on the .ips file format, see:
1528
+ * https://developer.apple.com/documentation/xcode/analyzing-a-crash-report
1529
+ *
1530
+ * @param dirPath Path to list, defaults to "/"
1531
+ * @param depth Listing depth: 1 for immediate children, -1 for infinite
1532
+ * @returns List of file paths (e.g., .ips files, sysdiagnose tarballs, directories like /Retired, /Cloud, etc.)
1533
+ */
1534
+ ls(dirPath?: string, depth?: number): Promise<string[]>;
1535
+
1536
+ /**
1537
+ * Pull crash reports from device to local machine
1538
+ * @param out Local directory path
1539
+ * @param entry Remote path on device, defaults to "/"
1540
+ * @param options Pull options (erase, match pattern)
1541
+ */
1542
+ pull(
1543
+ out: string,
1544
+ entry?: string,
1545
+ options?: CrashReportsPullOptions,
1546
+ ): Promise<void>;
1547
+
1548
+ /**
1549
+ * Clear all crash reports from the device
1550
+ */
1551
+ clear(): Promise<void>;
1552
+
1553
+ /**
1554
+ * Flush pending crash reports into CrashReports directory
1555
+ */
1556
+ flush(): Promise<void>;
1557
+
1558
+ /**
1559
+ * Close the service and release resources
1560
+ */
1561
+ close(): void;
1562
+ }
1563
+
1564
+ export interface CrashReportsServiceWithConnection {
1565
+ crashReportsService: CrashReportsService;
1566
+ remoteXPC: RemoteXpcConnection;
1567
+ }
@@ -3,6 +3,7 @@ import {
3
3
  startTunnelRegistryServer,
4
4
  } from '../lib/tunnel/tunnel-registry-server.js';
5
5
  import * as afc from './ios/afc/index.js';
6
+ import * as crashReports from './ios/crash-reports/index.js';
6
7
  import * as diagnostics from './ios/diagnostic-service/index.js';
7
8
  import * as mobileImageMounter from './ios/mobile-image-mounter/index.js';
8
9
  import * as powerAssertion from './ios/power-assertion/index.js';
@@ -11,6 +12,7 @@ import * as tunnel from './ios/tunnel-service/index.js';
11
12
  import * as webinspector from './ios/webinspector/index.js';
12
13
 
13
14
  export {
15
+ crashReports,
14
16
  diagnostics,
15
17
  mobileImageMounter,
16
18
  powerAssertion,
@@ -350,6 +350,40 @@ export async function rsdHandshakeForRawService(
350
350
  }
351
351
  }
352
352
 
353
+ /**
354
+ * Create a raw socket connection to an RSD service.
355
+ * Optionally performs RSD handshake if performHandshake is true.
356
+ * @param host - Hostname to connect to
357
+ * @param port - Port number
358
+ * @param options - Connection options
359
+ * @returns Connected socket (with handshake completed if requested)
360
+ */
361
+ export async function createRawServiceSocket(
362
+ host: string,
363
+ port: number,
364
+ options: { timeoutMs?: number; performHandshake?: boolean } = {},
365
+ ): Promise<net.Socket> {
366
+ const { timeoutMs = 10000, performHandshake = true } = options;
367
+
368
+ const socket = await new Promise<net.Socket>((resolve, reject) => {
369
+ const conn = net.createConnection({ host, port }, () => {
370
+ conn.setKeepAlive(true);
371
+ resolve(conn);
372
+ });
373
+ conn.setTimeout(timeoutMs, () => {
374
+ conn.destroy();
375
+ reject(new Error('Connection timed out'));
376
+ });
377
+ conn.on('error', reject);
378
+ });
379
+
380
+ if (performHandshake) {
381
+ await rsdHandshakeForRawService(socket);
382
+ }
383
+
384
+ return socket;
385
+ }
386
+
353
387
  export function nextReadChunkSize(left: bigint | number): number {
354
388
  const leftNum = typeof left === 'bigint' ? Number(left) : left;
355
389
  return leftNum;
@@ -1,7 +1,7 @@
1
1
  import { minimatch } from 'minimatch';
2
2
  import fs from 'node:fs';
3
3
  import fsp from 'node:fs/promises';
4
- import net from 'node:net';
4
+ import type net from 'node:net';
5
5
  import path from 'node:path';
6
6
  import { Readable, Writable } from 'node:stream';
7
7
  import { pipeline } from 'node:stream/promises';
@@ -11,16 +11,14 @@ import {
11
11
  buildClosePayload,
12
12
  buildFopenPayload,
13
13
  buildMkdirPayload,
14
- buildReadPayload,
15
14
  buildRemovePayload,
16
15
  buildRenamePayload,
17
16
  buildStatPayload,
17
+ createRawServiceSocket,
18
18
  nanosecondsToMilliseconds,
19
- nextReadChunkSize,
20
19
  parseCStringArray,
21
20
  parseKeyValueNullList,
22
21
  readAfcResponse,
23
- rsdHandshakeForRawService,
24
22
  sendAfcPacket,
25
23
  writeUInt64LE,
26
24
  } from './codec.js';
@@ -692,20 +690,9 @@ export class AfcService {
692
690
  }
693
691
  const [host, rsdPort] = this.address;
694
692
 
695
- this.socket = await new Promise<net.Socket>((resolve, reject) => {
696
- const s = net.createConnection({ host, port: rsdPort }, () => {
697
- s.setTimeout(0);
698
- s.setKeepAlive(true);
699
- resolve(s);
700
- });
701
- s.once('error', reject);
702
- s.setTimeout(30000, () => {
703
- s.destroy();
704
- reject(new Error('AFC connect timed out'));
705
- });
693
+ this.socket = await createRawServiceSocket(host, rsdPort, {
694
+ timeoutMs: 30000,
706
695
  });
707
-
708
- await rsdHandshakeForRawService(this.socket);
709
696
  log.debug('RSD handshake complete; switching to raw AFC');
710
697
 
711
698
  return this.socket;
@@ -19,7 +19,6 @@ export function createAfcReadStream(
19
19
  receive: () => Promise<{ status: AfcError; data: Buffer }>,
20
20
  ): Readable {
21
21
  let left = size;
22
- let totalRead = 0n;
23
22
 
24
23
  return new Readable({
25
24
  async read() {
@@ -39,7 +38,6 @@ export function createAfcReadStream(
39
38
  return;
40
39
  }
41
40
 
42
- totalRead += BigInt(data.length);
43
41
  left -= BigInt(data.length);
44
42
 
45
43
  this.push(data);
@@ -0,0 +1,180 @@
1
+ import fs from 'node:fs/promises';
2
+ import posixpath from 'node:path/posix';
3
+
4
+ import { getLogger } from '../../../lib/logger.js';
5
+ import type { CrashReportsPullOptions } from '../../../lib/types.js';
6
+ import { createRawServiceSocket, readExact } from '../afc/codec.js';
7
+ import { AfcService } from '../afc/index.js';
8
+ import { BaseService } from '../base-service.js';
9
+
10
+ const log = getLogger('CrashReportsService');
11
+
12
+ /**
13
+ * Path that is sometimes auto-created after deletion
14
+ */
15
+ const APPSTORED_PATH = '/com.apple.appstored';
16
+
17
+ /**
18
+ * CrashReportsService provides an API to:
19
+ * - List crash reports on the device (ls)
20
+ * - Pull crash reports from the device to the local machine (pull)
21
+ * - Clear all crash reports from the device (clear)
22
+ * - Flush crash report products into CrashReports directory (flush)
23
+ *
24
+ * This service uses the com.apple.crashreportcopymobile.shim.remote for AFC operations
25
+ * and com.apple.crashreportmover.shim.remote for flush operations.
26
+ */
27
+ export class CrashReportsService extends BaseService {
28
+ static readonly RSD_COPY_MOBILE_NAME =
29
+ 'com.apple.crashreportcopymobile.shim.remote';
30
+ static readonly RSD_CRASH_MOVER_NAME =
31
+ 'com.apple.crashreportmover.shim.remote';
32
+
33
+ private readonly afc: AfcService;
34
+ private readonly crashMoverAddress: [string, number];
35
+
36
+ /**
37
+ * Creates a new CrashReportsService instance
38
+ * @param afcAddress Tuple containing [host, port] for the AFC service
39
+ * @param crashMoverAddress Tuple containing [host, port] for the crash mover service
40
+ */
41
+ constructor(
42
+ afcAddress: [string, number],
43
+ crashMoverAddress: [string, number],
44
+ ) {
45
+ super(afcAddress);
46
+ this.afc = new AfcService(afcAddress, true);
47
+ this.crashMoverAddress = crashMoverAddress;
48
+ }
49
+
50
+ /**
51
+ * List files and folders in the crash report's directory.
52
+ * @param dirPath Path to list, relative to the crash report's directory. Defaults to "/"
53
+ * @param depth Listing depth. 1 for immediate children, -1 (or any negative number) for infinite depth
54
+ * @returns List of file paths listed
55
+ */
56
+ async ls(dirPath = '/', depth = 1): Promise<string[]> {
57
+ log.debug(`Listing crash reports at path: ${dirPath}, depth: ${depth}`);
58
+
59
+ if (depth === 0) {
60
+ return [];
61
+ }
62
+
63
+ const results: string[] = [];
64
+ const entries = await this.afc.listdir(dirPath);
65
+
66
+ for (const entry of entries) {
67
+ const entryPath = posixpath.join(dirPath, entry);
68
+ results.push(entryPath);
69
+
70
+ if (depth !== 1) {
71
+ try {
72
+ if (await this.afc.isdir(entryPath)) {
73
+ const newDepth = depth < 0 ? -1 : depth - 1;
74
+ const subEntries = await this.ls(entryPath, newDepth);
75
+ results.push(...subEntries);
76
+ }
77
+ } catch {
78
+ // Skip entries we can't access
79
+ }
80
+ }
81
+ }
82
+
83
+ return results;
84
+ }
85
+
86
+ /**
87
+ * Pull crash reports from the device to the local machine.
88
+ * @param out Local directory path
89
+ * @param entry Remote path on device, defaults to "/"
90
+ * @param options Pull options (erase, match pattern)
91
+ */
92
+ async pull(
93
+ out: string,
94
+ entry = '/',
95
+ options?: CrashReportsPullOptions,
96
+ ): Promise<void> {
97
+ const { erase = false, match } = options ?? {};
98
+
99
+ log.debug(
100
+ `Pulling crash reports from '${entry}' to '${out}', erase: ${erase}`,
101
+ );
102
+
103
+ await fs.mkdir(out, { recursive: true });
104
+
105
+ await this.afc.pull(entry, out, {
106
+ recursive: true,
107
+ match,
108
+ callback: erase
109
+ ? async (remotePath) => void (await this.afc.rmSingle(remotePath, true))
110
+ : undefined,
111
+ });
112
+ }
113
+
114
+ /**
115
+ * Clear all crash reports from the device.
116
+ * Removes all files and folders from the crash reports directory.
117
+ * @throws Error if some items could not be deleted (except for auto-created paths)
118
+ */
119
+ async clear(): Promise<void> {
120
+ log.debug('Clearing all crash reports');
121
+
122
+ const entries = await this.afc.listdir('/');
123
+ const nonDeletedItems: string[] = [];
124
+
125
+ for (const entry of entries) {
126
+ const fullPath = posixpath.join('/', entry);
127
+ const failedPaths = await this.afc.rm(fullPath, true);
128
+ nonDeletedItems.push(...failedPaths);
129
+ }
130
+
131
+ // Filter out special paths that are auto-created
132
+ const realFailures = nonDeletedItems.filter(
133
+ (item) => item !== APPSTORED_PATH,
134
+ );
135
+
136
+ if (realFailures.length > 0) {
137
+ throw new Error(
138
+ `Failed to clear crash reports directory, undeleted items: ${realFailures.join(', ')}`,
139
+ );
140
+ }
141
+
142
+ log.debug('Successfully cleared all crash reports');
143
+ }
144
+
145
+ /**
146
+ * Trigger com.apple.crashreportmover to flush all products into CrashReports directory
147
+ */
148
+ async flush(): Promise<void> {
149
+ log.debug('Flushing crash reports');
150
+
151
+ const socket = await createRawServiceSocket(
152
+ this.crashMoverAddress[0],
153
+ this.crashMoverAddress[1],
154
+ );
155
+ try {
156
+ const ack = await readExact(socket, 5, 10000);
157
+ const expectedAck = Buffer.from('ping\0', 'utf8');
158
+ if (!ack.equals(expectedAck)) {
159
+ throw new Error(
160
+ `Unexpected flush acknowledgment. Expected: ${expectedAck.toString('hex')}, Got: ${ack.toString('hex')}`,
161
+ );
162
+ }
163
+ log.debug('Successfully flushed crash reports');
164
+ } finally {
165
+ socket.destroy();
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Close the service and release resources
171
+ */
172
+ close(): void {
173
+ log.debug('Closing CrashReportsService');
174
+ try {
175
+ this.afc.close();
176
+ } catch {}
177
+ }
178
+ }
179
+
180
+ export default CrashReportsService;
@@ -85,7 +85,7 @@ class MobileConfigService
85
85
  // Check if file exists
86
86
  try {
87
87
  await fs.access(filePath);
88
- } catch (error) {
88
+ } catch {
89
89
  throw new Error(`Profile filepath does not exist: ${filePath}`);
90
90
  }
91
91
 
@@ -163,7 +163,7 @@ class MobileConfigService
163
163
  ProfileIdentifier: createPlist(payloadData, true),
164
164
  };
165
165
 
166
- log.info(req);
166
+ log.debug('Removing profile with request:', req);
167
167
 
168
168
  await this._sendPlistAndReceive(req);
169
169
  }
package/src/services.ts CHANGED
@@ -4,6 +4,7 @@ import { RemoteXpcConnection } from './lib/remote-xpc/remote-xpc-connection.js';
4
4
  import { TunnelManager } from './lib/tunnel/index.js';
5
5
  import { TunnelApiClient } from './lib/tunnel/tunnel-api-client.js';
6
6
  import type {
7
+ CrashReportsServiceWithConnection,
7
8
  DVTServiceWithConnection,
8
9
  DiagnosticsServiceWithConnection,
9
10
  MisagentServiceWithConnection,
@@ -16,6 +17,7 @@ import type {
16
17
  WebInspectorServiceWithConnection,
17
18
  } from './lib/types.js';
18
19
  import AfcService from './services/ios/afc/index.js';
20
+ import { CrashReportsService } from './services/ios/crash-reports/index.js';
19
21
  import DiagnosticsService from './services/ios/diagnostic-service/index.js';
20
22
  import { DVTSecureSocketProxyService } from './services/ios/dvt/index.js';
21
23
  import { ApplicationListing } from './services/ios/dvt/instruments/application-listing.js';
@@ -170,6 +172,31 @@ export async function startAfcService(udid: string): Promise<AfcService> {
170
172
  ]);
171
173
  }
172
174
 
175
+ /**
176
+ * Start CrashReportsService over RemoteXPC shim.
177
+ * Resolves the crash report copy mobile and crash mover service ports via RemoteXPC.
178
+ */
179
+ export async function startCrashReportsService(
180
+ udid: string,
181
+ ): Promise<CrashReportsServiceWithConnection> {
182
+ const { remoteXPC, tunnelConnection } = await createRemoteXPCConnection(udid);
183
+
184
+ const copyMobileDescriptor = remoteXPC.findService(
185
+ CrashReportsService.RSD_COPY_MOBILE_NAME,
186
+ );
187
+ const crashMoverDescriptor = remoteXPC.findService(
188
+ CrashReportsService.RSD_CRASH_MOVER_NAME,
189
+ );
190
+
191
+ return {
192
+ remoteXPC: remoteXPC as RemoteXpcConnection,
193
+ crashReportsService: new CrashReportsService(
194
+ [tunnelConnection.host, parseInt(copyMobileDescriptor.port, 10)],
195
+ [tunnelConnection.host, parseInt(crashMoverDescriptor.port, 10)],
196
+ ),
197
+ };
198
+ }
199
+
173
200
  export async function startWebInspectorService(
174
201
  udid: string,
175
202
  ): Promise<WebInspectorServiceWithConnection> {