appium-xcuitest-driver 10.14.13 → 10.16.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.
@@ -0,0 +1,422 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AfcClient = void 0;
7
+ const stream_1 = require("stream");
8
+ const promises_1 = require("stream/promises");
9
+ const path_1 = __importDefault(require("path"));
10
+ const lodash_1 = __importDefault(require("lodash"));
11
+ const bluebird_1 = __importDefault(require("bluebird"));
12
+ const support_1 = require("appium/support");
13
+ const appium_ios_device_1 = require("appium-ios-device");
14
+ const remotexpc_utils_1 = require("./remotexpc-utils");
15
+ const logger_1 = require("../logger");
16
+ const real_device_management_1 = require("./real-device-management");
17
+ /**
18
+ * Unified AFC Client
19
+ *
20
+ * Provides a unified interface for file operations on iOS devices,
21
+ * automatically handling the differences between iOS < 18 (appium-ios-device)
22
+ * and iOS 18 and above (appium-ios-remotexpc).
23
+ */
24
+ class AfcClient {
25
+ service;
26
+ remoteXPCConnection;
27
+ constructor(service, remoteXPCConnection) {
28
+ this.service = service;
29
+ this.remoteXPCConnection = remoteXPCConnection;
30
+ }
31
+ //#region Public Methods
32
+ /**
33
+ * Create an AFC client for device
34
+ *
35
+ * @param udid - Device UDID
36
+ * @param useRemoteXPC - Whether to use remotexpc (use isIos18OrNewer(opts) to determine)
37
+ * @returns AFC client instance
38
+ */
39
+ static async createForDevice(udid, useRemoteXPC) {
40
+ if (useRemoteXPC) {
41
+ const client = await AfcClient.withRemoteXpcConnection(async () => {
42
+ const Services = await (0, remotexpc_utils_1.getRemoteXPCServices)();
43
+ const connectionResult = await Services.createRemoteXPCConnection(udid);
44
+ const afcService = await Services.startAfcService(udid);
45
+ return {
46
+ service: afcService,
47
+ connection: connectionResult.remoteXPC,
48
+ };
49
+ });
50
+ if (client) {
51
+ return client;
52
+ }
53
+ }
54
+ const afcService = await appium_ios_device_1.services.startAfcService(udid);
55
+ return new AfcClient(afcService);
56
+ }
57
+ /**
58
+ * Create an AFC client for app container access
59
+ *
60
+ * @param udid - Device UDID
61
+ * @param bundleId - App bundle identifier
62
+ * @param useRemoteXPC - Whether to use remotexpc (use isIos18OrNewer(opts) to determine)
63
+ * @param options - Optional configuration for container access
64
+ * @returns AFC client instance
65
+ */
66
+ static async createForApp(udid, bundleId, useRemoteXPC, options) {
67
+ const { containerType = null, skipDocumentsCheck = false } = options ?? {};
68
+ const isDocuments = !skipDocumentsCheck && containerType?.toLowerCase() === 'documents';
69
+ if (useRemoteXPC) {
70
+ const client = await AfcClient.withRemoteXpcConnection(async () => {
71
+ const Services = await (0, remotexpc_utils_1.getRemoteXPCServices)();
72
+ const connectionResult = await Services.createRemoteXPCConnection(udid);
73
+ const { houseArrestService, remoteXPC: houseArrestRemoteXPC } = await Services.startHouseArrestService(udid);
74
+ const afcService = isDocuments
75
+ ? await houseArrestService.vendDocuments(bundleId)
76
+ : await houseArrestService.vendContainer(bundleId);
77
+ // Use the remoteXPC from house arrest service if available, otherwise use the one from connection
78
+ const connection = houseArrestRemoteXPC ?? connectionResult.remoteXPC;
79
+ return {
80
+ service: afcService,
81
+ connection,
82
+ };
83
+ });
84
+ if (client) {
85
+ return client;
86
+ }
87
+ }
88
+ const houseArrestService = await appium_ios_device_1.services.startHouseArrestService(udid);
89
+ const afcService = isDocuments
90
+ ? await houseArrestService.vendDocuments(bundleId)
91
+ : await houseArrestService.vendContainer(bundleId);
92
+ return new AfcClient(afcService);
93
+ }
94
+ /**
95
+ * Check if a path is a directory
96
+ */
97
+ async isDirectory(path) {
98
+ if (this.isRemoteXPC) {
99
+ return await this.remoteXPCAfcService.isdir(path);
100
+ }
101
+ const fileInfo = await this.iosDeviceAfcService.getFileInfo(path);
102
+ return fileInfo.isDirectory();
103
+ }
104
+ /**
105
+ * List directory contents
106
+ */
107
+ async listDirectory(path) {
108
+ if (this.isRemoteXPC) {
109
+ return await this.remoteXPCAfcService.listdir(path);
110
+ }
111
+ return await this.iosDeviceAfcService.listDirectory(path);
112
+ }
113
+ /**
114
+ * Create a directory
115
+ */
116
+ async createDirectory(path) {
117
+ if (this.isRemoteXPC) {
118
+ await this.remoteXPCAfcService.mkdir(path);
119
+ }
120
+ else {
121
+ await this.iosDeviceAfcService.createDirectory(path);
122
+ }
123
+ }
124
+ /**
125
+ * Delete a directory or file
126
+ */
127
+ async deleteDirectory(path) {
128
+ if (this.isRemoteXPC) {
129
+ await this.remoteXPCAfcService.rm(path, true);
130
+ }
131
+ else {
132
+ await this.iosDeviceAfcService.deleteDirectory(path);
133
+ }
134
+ }
135
+ /**
136
+ * Get file contents as a buffer
137
+ */
138
+ async getFileContents(path) {
139
+ if (this.isRemoteXPC) {
140
+ return await this.remoteXPCAfcService.getFileContents(path);
141
+ }
142
+ // For ios-device, use stream-based approach
143
+ const stream = await this.iosDeviceAfcService.createReadStream(path, {
144
+ autoDestroy: true,
145
+ });
146
+ const buffers = [];
147
+ return new Promise((resolve, reject) => {
148
+ stream.on('data', (data) => buffers.push(data));
149
+ stream.on('end', () => resolve(Buffer.concat(buffers)));
150
+ stream.on('error', reject);
151
+ });
152
+ }
153
+ /**
154
+ * Set file contents from a buffer
155
+ */
156
+ async setFileContents(path, data) {
157
+ if (this.isRemoteXPC) {
158
+ await this.remoteXPCAfcService.setFileContents(path, data);
159
+ return;
160
+ }
161
+ // For ios-device, convert buffer to stream and use writeFromStream
162
+ const bufferStream = stream_1.Readable.from([data]);
163
+ return await this.writeFromStream(path, bufferStream);
164
+ }
165
+ /**
166
+ * Write file contents from a readable stream
167
+ */
168
+ async writeFromStream(path, stream) {
169
+ if (this.isRemoteXPC) {
170
+ await this.remoteXPCAfcService.writeFromStream(path, stream);
171
+ return;
172
+ }
173
+ const writeStream = await this.iosDeviceAfcService.createWriteStream(path, {
174
+ autoDestroy: true,
175
+ });
176
+ writeStream.on('finish', () => {
177
+ if (typeof writeStream.destroy === 'function') {
178
+ writeStream.destroy();
179
+ }
180
+ });
181
+ return new Promise((resolve, reject) => {
182
+ writeStream.on('close', resolve);
183
+ const onError = (e) => {
184
+ stream.unpipe(writeStream);
185
+ reject(e);
186
+ };
187
+ writeStream.on('error', onError);
188
+ stream.on('error', onError);
189
+ stream.pipe(writeStream);
190
+ });
191
+ }
192
+ /**
193
+ * Pull files/folders from device to local filesystem.
194
+ * Uses the appropriate mechanism (walkDir for ios-device, pull for remotexpc).
195
+ *
196
+ * @param remotePath - Remote path on the device (file or directory)
197
+ * @param localPath - Local destination path
198
+ * @param options - Pull options (recursive, overwrite, onEntry)
199
+ */
200
+ async pull(remotePath, localPath, options = {}) {
201
+ if (this.isRemoteXPC) {
202
+ // RemoteXPC expects 'callback' property, so map onEntry -> callback
203
+ const remoteXpcOptions = {
204
+ ...options,
205
+ callback: options.onEntry,
206
+ };
207
+ delete remoteXpcOptions.onEntry;
208
+ await this.remoteXPCAfcService.pull(remotePath, localPath, remoteXpcOptions);
209
+ }
210
+ else {
211
+ await this.pullWithWalkDir(remotePath, localPath, options);
212
+ }
213
+ }
214
+ /**
215
+ * Close the AFC service connection and remoteXPC connection if present
216
+ */
217
+ async close() {
218
+ this.service.close();
219
+ if (this.remoteXPCConnection) {
220
+ try {
221
+ await this.remoteXPCConnection.close();
222
+ }
223
+ catch { }
224
+ }
225
+ }
226
+ //#endregion
227
+ //#region Private Methods
228
+ /**
229
+ * Check if this client is using RemoteXPC
230
+ */
231
+ get isRemoteXPC() {
232
+ return !!this.remoteXPCConnection;
233
+ }
234
+ /**
235
+ * Helper to safely execute remoteXPC operations with connection cleanup
236
+ * @param operation - Async operation that returns an AfcClient
237
+ * @returns AfcClient on success, null on failure
238
+ */
239
+ static async withRemoteXpcConnection(operation) {
240
+ let remoteXPCConnection;
241
+ let succeeded = false;
242
+ try {
243
+ const { service, connection } = await operation();
244
+ remoteXPCConnection = connection;
245
+ const client = new AfcClient(service, remoteXPCConnection);
246
+ succeeded = true;
247
+ return client;
248
+ }
249
+ catch (err) {
250
+ logger_1.log.error(`Failed to create AFC client via RemoteXPC: ${err.message}, falling back to appium-ios-device`);
251
+ return null;
252
+ }
253
+ finally {
254
+ // Only close connection if we failed (if succeeded, the client owns it)
255
+ if (remoteXPCConnection && !succeeded) {
256
+ try {
257
+ await remoteXPCConnection.close();
258
+ }
259
+ catch {
260
+ // Ignore cleanup errors
261
+ }
262
+ }
263
+ }
264
+ }
265
+ /**
266
+ * Get service as RemoteXPC AFC service
267
+ */
268
+ get remoteXPCAfcService() {
269
+ return this.service;
270
+ }
271
+ /**
272
+ * Get service as iOS Device AFC service
273
+ */
274
+ get iosDeviceAfcService() {
275
+ return this.service;
276
+ }
277
+ /**
278
+ * Create a read stream for a file (internal use only).
279
+ */
280
+ async createReadStream(remotePath, options) {
281
+ if (this.isRemoteXPC) {
282
+ // Use readToStream which returns a streaming Readable
283
+ return await this.remoteXPCAfcService.readToStream(remotePath);
284
+ }
285
+ return await this.iosDeviceAfcService.createReadStream(remotePath, options);
286
+ }
287
+ /**
288
+ * Internal implementation of pull for ios-device using walkDir.
289
+ * Walks the remote directory tree and pulls files to local filesystem.
290
+ */
291
+ async pullWithWalkDir(remotePath, localPath, options) {
292
+ const { recursive = false, overwrite = true, onEntry } = options;
293
+ const isDir = await this.isDirectory(remotePath);
294
+ if (!isDir) {
295
+ // Single file pull
296
+ const localFilePath = (await this.isLocalDirectory(localPath))
297
+ ? path_1.default.join(localPath, path_1.default.posix.basename(remotePath))
298
+ : localPath;
299
+ await this.checkOverwrite(localFilePath, overwrite);
300
+ await this.pullSingleFile(remotePath, localFilePath);
301
+ if (onEntry) {
302
+ await onEntry(remotePath, localFilePath, false);
303
+ }
304
+ return;
305
+ }
306
+ // Directory pull requires recursive option
307
+ if (!recursive) {
308
+ throw new Error(`Cannot pull directory '${remotePath}' without recursive option. Set recursive: true to pull directories.`);
309
+ }
310
+ // Determine local root directory
311
+ const localDstIsDirectory = await this.isLocalDirectory(localPath);
312
+ const localRootDir = localDstIsDirectory
313
+ ? path_1.default.join(localPath, path_1.default.posix.basename(remotePath))
314
+ : localPath;
315
+ // Create the root directory
316
+ await (0, support_1.mkdirp)(localRootDir);
317
+ if (onEntry) {
318
+ await onEntry(remotePath, localRootDir, true);
319
+ }
320
+ const pullPromises = [];
321
+ // Walk the remote directory and pull files in parallel
322
+ await this.iosDeviceAfcService.walkDir(remotePath, true, async (entryPath, isDirectory) => {
323
+ // Calculate relative path from remote root
324
+ const relativePath = entryPath.startsWith(remotePath + '/')
325
+ ? entryPath.slice(remotePath.length + 1)
326
+ : entryPath.slice(remotePath.length);
327
+ const localEntryPath = path_1.default.join(localRootDir, relativePath);
328
+ if (isDirectory) {
329
+ await (0, support_1.mkdirp)(localEntryPath);
330
+ if (onEntry) {
331
+ await onEntry(entryPath, localEntryPath, true);
332
+ }
333
+ }
334
+ else {
335
+ await this.checkOverwrite(localEntryPath, overwrite);
336
+ // Ensure parent directory exists
337
+ const parentDir = path_1.default.dirname(localEntryPath);
338
+ await (0, support_1.mkdirp)(parentDir);
339
+ // Start async file pull (non-blocking)
340
+ const readStream = await this.iosDeviceAfcService.createReadStream(entryPath, {
341
+ autoDestroy: true,
342
+ });
343
+ const writeStream = support_1.fs.createWriteStream(localEntryPath, { autoClose: true });
344
+ pullPromises.push(new bluebird_1.default((resolve) => {
345
+ writeStream.on('close', async () => {
346
+ // Invoke onEntry callback after successful pull
347
+ if (onEntry) {
348
+ try {
349
+ await onEntry(entryPath, localEntryPath, false);
350
+ }
351
+ catch (err) {
352
+ logger_1.log.warn(`onEntry callback failed for '${entryPath}': ${err.message}`);
353
+ }
354
+ }
355
+ resolve();
356
+ });
357
+ const onStreamingError = (e) => {
358
+ readStream.unpipe(writeStream);
359
+ logger_1.log.warn(`Cannot pull '${entryPath}' to '${localEntryPath}'. ` +
360
+ `The file will be skipped. Original error: ${e.message}`);
361
+ resolve();
362
+ };
363
+ writeStream.on('error', onStreamingError);
364
+ readStream.on('error', onStreamingError);
365
+ }).timeout(real_device_management_1.IO_TIMEOUT_MS));
366
+ readStream.pipe(writeStream);
367
+ if (pullPromises.length >= real_device_management_1.MAX_IO_CHUNK_SIZE) {
368
+ await bluebird_1.default.any(pullPromises);
369
+ for (let i = pullPromises.length - 1; i >= 0; i--) {
370
+ if (pullPromises[i].isFulfilled()) {
371
+ pullPromises.splice(i, 1);
372
+ }
373
+ }
374
+ }
375
+ }
376
+ });
377
+ // Wait for remaining files to be pulled
378
+ if (!lodash_1.default.isEmpty(pullPromises)) {
379
+ await bluebird_1.default.all(pullPromises);
380
+ }
381
+ }
382
+ /**
383
+ * Check if local file exists and should not be overwritten.
384
+ * Throws an error if the file exists and overwrite is false.
385
+ *
386
+ * @param localPath - Local file path to check
387
+ * @param overwrite - Whether to allow overwriting existing files
388
+ */
389
+ async checkOverwrite(localPath, overwrite) {
390
+ if (!overwrite && await support_1.fs.exists(localPath)) {
391
+ throw new Error(`Local file already exists: ${localPath}`);
392
+ }
393
+ }
394
+ /**
395
+ * Pull a single file from device to local filesystem using streams.
396
+ * This method only works for ios-device.
397
+ *
398
+ * @param remotePath - Remote file path
399
+ * @param localPath - Local destination path
400
+ */
401
+ async pullSingleFile(remotePath, localPath) {
402
+ const readStream = await this.iosDeviceAfcService.createReadStream(remotePath, {
403
+ autoDestroy: true,
404
+ });
405
+ const writeStream = support_1.fs.createWriteStream(localPath, { autoClose: true });
406
+ await (0, promises_1.pipeline)(readStream, writeStream);
407
+ }
408
+ /**
409
+ * Check if a local path exists and is a directory.
410
+ */
411
+ async isLocalDirectory(localPath) {
412
+ try {
413
+ const stats = await support_1.fs.stat(localPath);
414
+ return stats.isDirectory();
415
+ }
416
+ catch {
417
+ return false;
418
+ }
419
+ }
420
+ }
421
+ exports.AfcClient = AfcClient;
422
+ //# sourceMappingURL=afc-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"afc-client.js","sourceRoot":"","sources":["../../../lib/device/afc-client.ts"],"names":[],"mappings":";;;;;;AACA,mCAAkD;AAClD,8CAAyC;AACzC,gDAAwB;AACxB,oDAAuB;AACvB,wDAAyB;AACzB,4CAA0C;AAC1C,yDAA2C;AAE3C,uDAAuD;AACvD,sCAA8B;AAK9B,qEAA0E;AAoB1E;;;;;;GAMG;AACH,MAAa,SAAS;IACH,OAAO,CAA4C;IACnD,mBAAmB,CAAuB;IAE3D,YACE,OAAkD,EAClD,mBAAyC;QAEzC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;IACjD,CAAC;IAED,wBAAwB;IAExB;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,YAAqB;QAC9D,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,uBAAuB,CAAC,KAAK,IAAI,EAAE;gBAChE,MAAM,QAAQ,GAAG,MAAM,IAAA,sCAAoB,GAAE,CAAC;gBAC9C,MAAM,gBAAgB,GAAG,MAAM,QAAQ,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC;gBACxE,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;gBACxD,OAAO;oBACL,OAAO,EAAE,UAAU;oBACnB,UAAU,EAAE,gBAAgB,CAAC,SAAS;iBACvC,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,4BAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACxD,OAAO,IAAI,SAAS,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,KAAK,CAAC,YAAY,CACvB,IAAY,EACZ,QAAgB,EAChB,YAAqB,EACrB,OAA6B;QAE7B,MAAM,EAAC,aAAa,GAAG,IAAI,EAAE,kBAAkB,GAAG,KAAK,EAAC,GAAG,OAAO,IAAI,EAAE,CAAC;QACzE,MAAM,WAAW,GAAG,CAAC,kBAAkB,IAAI,aAAa,EAAE,WAAW,EAAE,KAAK,WAAW,CAAC;QAExF,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,uBAAuB,CAAC,KAAK,IAAI,EAAE;gBAChE,MAAM,QAAQ,GAAG,MAAM,IAAA,sCAAoB,GAAE,CAAC;gBAC9C,MAAM,gBAAgB,GAAG,MAAM,QAAQ,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC;gBACxE,MAAM,EAAC,kBAAkB,EAAE,SAAS,EAAE,oBAAoB,EAAC,GAAG,MAAM,QAAQ,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC;gBAC3G,MAAM,UAAU,GAAG,WAAW;oBAC5B,CAAC,CAAC,MAAM,kBAAkB,CAAC,aAAa,CAAC,QAAQ,CAAC;oBAClD,CAAC,CAAC,MAAM,kBAAkB,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;gBACrD,kGAAkG;gBAClG,MAAM,UAAU,GAAG,oBAAoB,IAAI,gBAAgB,CAAC,SAAS,CAAC;gBACtE,OAAO;oBACL,OAAO,EAAE,UAAU;oBACnB,UAAU;iBACX,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAED,MAAM,kBAAkB,GAAG,MAAM,4BAAQ,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC;QACxE,MAAM,UAAU,GAAG,WAAW;YAC5B,CAAC,CAAC,MAAM,kBAAkB,CAAC,aAAa,CAAC,QAAQ,CAAC;YAClD,CAAC,CAAC,MAAM,kBAAkB,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACrD,OAAO,IAAI,SAAS,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,IAAY;QAC5B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAClE,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,IAAY;QAC9B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,MAAM,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,MAAM,IAAI,CAAC,mBAAmB,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,IAAY;QAChC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,IAAY;QAChC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,IAAY;QAChC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,MAAM,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC9D,CAAC;QAED,4CAA4C;QAC5C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,IAAI,EAAE;YACnE,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QACH,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACxD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACxD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,IAAY;QAC9C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC3D,OAAO;QACT,CAAC;QACD,mEAAmE;QACnE,MAAM,YAAY,GAAG,iBAAc,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACjD,OAAO,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACxD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,MAAgB;QAClD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,IAAI,EAAE;YACzE,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QAEH,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC5B,IAAI,OAAO,WAAW,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;gBAC9C,WAAW,CAAC,OAAO,EAAE,CAAC;YACxB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACjC,MAAM,OAAO,GAAG,CAAC,CAAQ,EAAE,EAAE;gBAC3B,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;gBAC3B,MAAM,CAAC,CAAC,CAAC,CAAC;YACZ,CAAC,CAAC;YACF,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACjC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,CACR,UAAkB,EAClB,SAAiB,EACjB,UAA0B,EAAE;QAE5B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,oEAAoE;YACpE,MAAM,gBAAgB,GAAG;gBACvB,GAAG,OAAO;gBACV,QAAQ,EAAE,OAAO,CAAC,OAAO;aAC1B,CAAC;YACF,OAAO,gBAAgB,CAAC,OAAO,CAAC;YAChC,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;QAC/E,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACZ,CAAC;IACH,CAAC;IAED,YAAY;IAEZ,yBAAyB;IAEzB;;OAEG;IACH,IAAY,WAAW;QACrB,OAAO,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC;IACpC,CAAC;IAED;;;;OAIG;IACK,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAC1C,SAAuE;QAEvE,IAAI,mBAAoD,CAAC;QACzD,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,EAAC,OAAO,EAAE,UAAU,EAAC,GAAG,MAAM,SAAS,EAAE,CAAC;YAChD,mBAAmB,GAAG,UAAU,CAAC;YACjC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;YAC3D,SAAS,GAAG,IAAI,CAAC;YACjB,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,YAAG,CAAC,KAAK,CAAC,8CAA8C,GAAG,CAAC,OAAO,qCAAqC,CAAC,CAAC;YAC1G,OAAO,IAAI,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,wEAAwE;YACxE,IAAI,mBAAmB,IAAI,CAAC,SAAS,EAAE,CAAC;gBACtC,IAAI,CAAC;oBACH,MAAM,mBAAmB,CAAC,KAAK,EAAE,CAAC;gBACpC,CAAC;gBAAC,MAAM,CAAC;oBACP,wBAAwB;gBAC1B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAY,mBAAmB;QAC7B,OAAO,IAAI,CAAC,OAA8B,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,IAAY,mBAAmB;QAC7B,OAAO,IAAI,CAAC,OAA8B,CAAC;IAC7C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAAC,UAAkB,EAAE,OAAiC;QAClF,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,sDAAsD;YACtD,OAAO,MAAM,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,MAAM,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC9E,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,eAAe,CAC3B,UAAkB,EAClB,SAAiB,EACjB,OAAuB;QAEvB,MAAM,EAAC,SAAS,GAAG,KAAK,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,EAAC,GAAG,OAAO,CAAC;QAE/D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QAEjD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,mBAAmB;YACnB,MAAM,aAAa,GAAG,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;gBAC5D,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;gBACvD,CAAC,CAAC,SAAS,CAAC;YAEd,MAAM,IAAI,CAAC,cAAc,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;YACpD,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;YAErD,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,OAAO,CAAC,UAAU,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC;YAClD,CAAC;YACD,OAAO;QACT,CAAC;QAED,2CAA2C;QAC3C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,0BAA0B,UAAU,sEAAsE,CAC3G,CAAC;QACJ,CAAC;QAED,iCAAiC;QACjC,MAAM,mBAAmB,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACnE,MAAM,YAAY,GAAG,mBAAmB;YACtC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YACvD,CAAC,CAAC,SAAS,CAAC;QAEd,4BAA4B;QAC5B,MAAM,IAAA,gBAAM,EAAC,YAAY,CAAC,CAAC;QAE3B,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,OAAO,CAAC,UAAU,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,YAAY,GAAc,EAAE,CAAC;QAEnC,uDAAuD;QACvD,MAAM,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,SAAiB,EAAE,WAAoB,EAAE,EAAE;YACzG,2CAA2C;YAC3C,MAAM,YAAY,GAAG,SAAS,CAAC,UAAU,CAAC,UAAU,GAAG,GAAG,CAAC;gBACzD,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;gBACxC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YACvC,MAAM,cAAc,GAAG,cAAI,CAAC,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;YAE7D,IAAI,WAAW,EAAE,CAAC;gBAChB,MAAM,IAAA,gBAAM,EAAC,cAAc,CAAC,CAAC;gBAC7B,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,OAAO,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;gBACjD,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,CAAC,cAAc,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;gBAErD,iCAAiC;gBACjC,MAAM,SAAS,GAAG,cAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;gBAC/C,MAAM,IAAA,gBAAM,EAAC,SAAS,CAAC,CAAC;gBAExB,uCAAuC;gBACvC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,SAAS,EAAE;oBAC5E,WAAW,EAAE,IAAI;iBAClB,CAAC,CAAC;gBACH,MAAM,WAAW,GAAG,YAAE,CAAC,iBAAiB,CAAC,cAAc,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;gBAE5E,YAAY,CAAC,IAAI,CACf,IAAI,kBAAC,CAAO,CAAC,OAAO,EAAE,EAAE;oBACtB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;wBACjC,gDAAgD;wBAChD,IAAI,OAAO,EAAE,CAAC;4BACZ,IAAI,CAAC;gCACH,MAAM,OAAO,CAAC,SAAS,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC;4BAClD,CAAC;4BAAC,OAAO,GAAQ,EAAE,CAAC;gCAClB,YAAG,CAAC,IAAI,CAAC,gCAAgC,SAAS,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;4BACzE,CAAC;wBACH,CAAC;wBACD,OAAO,EAAE,CAAC;oBACZ,CAAC,CAAC,CAAC;oBACH,MAAM,gBAAgB,GAAG,CAAC,CAAQ,EAAE,EAAE;wBACpC,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;wBAC/B,YAAG,CAAC,IAAI,CACN,gBAAgB,SAAS,SAAS,cAAc,KAAK;4BACnD,6CAA6C,CAAC,CAAC,OAAO,EAAE,CAC3D,CAAC;wBACF,OAAO,EAAE,CAAC;oBACZ,CAAC,CAAC;oBACF,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;oBAC1C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;gBAC3C,CAAC,CAAC,CAAC,OAAO,CAAC,sCAAa,CAAC,CAC1B,CAAC;gBACF,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBAE7B,IAAI,YAAY,CAAC,MAAM,IAAI,0CAAiB,EAAE,CAAC;oBAC7C,MAAM,kBAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;oBAC1B,KAAK,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;wBAClD,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;4BAClC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;wBAC5B,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,wCAAwC;QACxC,IAAI,CAAC,gBAAC,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,MAAM,kBAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,cAAc,CAAC,SAAiB,EAAE,SAAkB;QAChE,IAAI,CAAC,SAAS,IAAI,MAAM,YAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,8BAA8B,SAAS,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,cAAc,CAAC,UAAkB,EAAE,SAAiB;QAChE,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,UAAU,EAAE;YAC7E,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QACH,MAAM,WAAW,GAAG,YAAE,CAAC,iBAAiB,CAAC,SAAS,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;QAEvE,MAAM,IAAA,mBAAQ,EAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAAC,SAAiB;QAC9C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,YAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvC,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CAGF;AA5cD,8BA4cC"}
@@ -0,0 +1,73 @@
1
+ import type { AppiumLogger } from '@appium/types';
2
+ import type { CertificateList } from '../commands/types';
3
+ /**
4
+ * Options for installing a certificate
5
+ */
6
+ export interface InstallCertificateOptions {
7
+ payload: Buffer;
8
+ }
9
+ /**
10
+ * Unified Certificate Client
11
+ *
12
+ * Provides a unified interface for certificate operations on iOS devices,
13
+ * automatically handling the differences between iOS < 18 (py-ios-device)
14
+ * and iOS 18 and above (appium-ios-remotexpc MobileConfigService).
15
+ */
16
+ export declare class CertificateClient {
17
+ private readonly service;
18
+ private readonly remoteXPCConnection?;
19
+ private readonly log;
20
+ private constructor();
21
+ /**
22
+ * Create a certificate client for device
23
+ *
24
+ * @param udid - Device UDID
25
+ * @param log - Appium logger instance
26
+ * @param useRemoteXPC - Whether to use remotexpc (use isIos18OrNewer(opts) to determine)
27
+ * @returns Certificate client instance
28
+ */
29
+ static create(udid: string, log: AppiumLogger, useRemoteXPC: boolean): Promise<CertificateClient>;
30
+ /**
31
+ * Install a certificate profile
32
+ *
33
+ * @param options - Installation options containing the certificate payload
34
+ */
35
+ installCertificate(options: InstallCertificateOptions): Promise<void>;
36
+ /**
37
+ * Remove a certificate profile by name
38
+ *
39
+ * @param name - Name of the profile to remove
40
+ * @returns Status message ('Acknowledged' for RemoteXPC, or pyidevice output)
41
+ */
42
+ removeCertificate(name: string): Promise<string>;
43
+ /**
44
+ * List installed certificate profiles
45
+ *
46
+ * @returns List of installed certificates
47
+ */
48
+ listCertificates(): Promise<CertificateList>;
49
+ /**
50
+ * Close the certificate service connection and remoteXPC connection if present
51
+ */
52
+ close(): Promise<void>;
53
+ /**
54
+ * Check if this client is using RemoteXPC
55
+ */
56
+ private get isRemoteXPC();
57
+ /**
58
+ * Helper to safely execute remoteXPC operations with connection cleanup
59
+ * @param operation - Async operation that returns service and connection
60
+ * @param log - Logger instance
61
+ * @returns CertificateClient on success, null on failure
62
+ */
63
+ private static withRemoteXpcConnection;
64
+ /**
65
+ * Get service as RemoteXPC MobileConfigService
66
+ */
67
+ private get mobileConfigService();
68
+ /**
69
+ * Get service as Pyidevice client
70
+ */
71
+ private get pyideviceClient();
72
+ }
73
+ //# sourceMappingURL=certificate-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"certificate-client.d.ts","sourceRoot":"","sources":["../../../lib/device/certificate-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,eAAe,CAAC;AAGhD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAMvD;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;IACnE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAsB;IAC3D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IAEnC,OAAO;IAYP;;;;;;;OAOG;WACU,MAAM,CACjB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,YAAY,EACjB,YAAY,EAAE,OAAO,GACpB,OAAO,CAAC,iBAAiB,CAAC;IAwB7B;;;;OAIG;IACG,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;IAU3E;;;;;OAKG;IACG,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAStD;;;;OAIG;IACG,gBAAgB,IAAI,OAAO,CAAC,eAAe,CAAC;IAQlD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;OAEG;IACH,OAAO,KAAK,WAAW,GAEtB;IAED;;;;;OAKG;mBACkB,uBAAuB;IA6B5C;;OAEG;IACH,OAAO,KAAK,mBAAmB,GAE9B;IAED;;OAEG;IACH,OAAO,KAAK,eAAe,GAE1B;CAGF"}
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CertificateClient = void 0;
4
+ const py_ios_device_client_1 = require("./clients/py-ios-device-client");
5
+ const remotexpc_utils_1 = require("./remotexpc-utils");
6
+ /**
7
+ * Unified Certificate Client
8
+ *
9
+ * Provides a unified interface for certificate operations on iOS devices,
10
+ * automatically handling the differences between iOS < 18 (py-ios-device)
11
+ * and iOS 18 and above (appium-ios-remotexpc MobileConfigService).
12
+ */
13
+ class CertificateClient {
14
+ service;
15
+ remoteXPCConnection;
16
+ log;
17
+ constructor(service, log, remoteXPCConnection) {
18
+ this.service = service;
19
+ this.log = log;
20
+ this.remoteXPCConnection = remoteXPCConnection;
21
+ }
22
+ //#region Public Methods
23
+ /**
24
+ * Create a certificate client for device
25
+ *
26
+ * @param udid - Device UDID
27
+ * @param log - Appium logger instance
28
+ * @param useRemoteXPC - Whether to use remotexpc (use isIos18OrNewer(opts) to determine)
29
+ * @returns Certificate client instance
30
+ */
31
+ static async create(udid, log, useRemoteXPC) {
32
+ if (useRemoteXPC) {
33
+ const client = await CertificateClient.withRemoteXpcConnection(async () => {
34
+ const Services = await (0, remotexpc_utils_1.getRemoteXPCServices)();
35
+ const { mobileConfigService, remoteXPC } = await Services.startMobileConfigService(udid);
36
+ return {
37
+ service: mobileConfigService,
38
+ connection: remoteXPC,
39
+ };
40
+ }, log);
41
+ if (client) {
42
+ return client;
43
+ }
44
+ }
45
+ // Fallback to py-ios-device
46
+ const pyideviceClient = new py_ios_device_client_1.Pyidevice({
47
+ udid,
48
+ log,
49
+ });
50
+ await pyideviceClient.assertExists(true);
51
+ return new CertificateClient(pyideviceClient, log);
52
+ }
53
+ /**
54
+ * Install a certificate profile
55
+ *
56
+ * @param options - Installation options containing the certificate payload
57
+ */
58
+ async installCertificate(options) {
59
+ const { payload } = options;
60
+ if (this.isRemoteXPC) {
61
+ await this.mobileConfigService.installProfileFromBuffer(payload);
62
+ }
63
+ else {
64
+ await this.pyideviceClient.installProfile({ payload });
65
+ }
66
+ }
67
+ /**
68
+ * Remove a certificate profile by name
69
+ *
70
+ * @param name - Name of the profile to remove
71
+ * @returns Status message ('Acknowledged' for RemoteXPC, or pyidevice output)
72
+ */
73
+ async removeCertificate(name) {
74
+ if (this.isRemoteXPC) {
75
+ await this.mobileConfigService.removeProfile(name);
76
+ return 'Acknowledged';
77
+ }
78
+ else {
79
+ return await this.pyideviceClient.removeProfile(name);
80
+ }
81
+ }
82
+ /**
83
+ * List installed certificate profiles
84
+ *
85
+ * @returns List of installed certificates
86
+ */
87
+ async listCertificates() {
88
+ if (this.isRemoteXPC) {
89
+ return await this.mobileConfigService.getProfileList();
90
+ }
91
+ else {
92
+ return await this.pyideviceClient.listProfiles();
93
+ }
94
+ }
95
+ /**
96
+ * Close the certificate service connection and remoteXPC connection if present
97
+ */
98
+ async close() {
99
+ if (this.remoteXPCConnection) {
100
+ try {
101
+ this.log.debug(`Closing remoteXPC connection`);
102
+ await this.remoteXPCConnection.close();
103
+ }
104
+ catch (err) {
105
+ this.log.debug(`Error closing remoteXPC connection: ${err.message}`);
106
+ }
107
+ }
108
+ }
109
+ //#endregion
110
+ //#region Private Methods
111
+ /**
112
+ * Check if this client is using RemoteXPC
113
+ */
114
+ get isRemoteXPC() {
115
+ return !!this.remoteXPCConnection;
116
+ }
117
+ /**
118
+ * Helper to safely execute remoteXPC operations with connection cleanup
119
+ * @param operation - Async operation that returns service and connection
120
+ * @param log - Logger instance
121
+ * @returns CertificateClient on success, null on failure
122
+ */
123
+ static async withRemoteXpcConnection(operation, log) {
124
+ let remoteXPCConnection;
125
+ let succeeded = false;
126
+ try {
127
+ const { service, connection } = await operation();
128
+ remoteXPCConnection = connection;
129
+ const client = new CertificateClient(service, log, remoteXPCConnection);
130
+ succeeded = true;
131
+ return client;
132
+ }
133
+ catch (err) {
134
+ log.error(`Failed to create certificate client via RemoteXPC: ${err.message}, falling back to py-ios-device`);
135
+ return null;
136
+ }
137
+ finally {
138
+ // Only close connection if we failed (if succeeded, the client owns it)
139
+ if (remoteXPCConnection && !succeeded) {
140
+ try {
141
+ await remoteXPCConnection.close();
142
+ }
143
+ catch {
144
+ // Ignore cleanup errors
145
+ }
146
+ }
147
+ }
148
+ }
149
+ /**
150
+ * Get service as RemoteXPC MobileConfigService
151
+ */
152
+ get mobileConfigService() {
153
+ return this.service;
154
+ }
155
+ /**
156
+ * Get service as Pyidevice client
157
+ */
158
+ get pyideviceClient() {
159
+ return this.service;
160
+ }
161
+ }
162
+ exports.CertificateClient = CertificateClient;
163
+ //# sourceMappingURL=certificate-client.js.map