@mcesystems/apple-kit 1.0.0 → 1.0.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 (88) hide show
  1. package/dist/index.js +465 -404
  2. package/dist/index.js.map +4 -4
  3. package/dist/types/index.d.ts +6 -3
  4. package/dist/types/index.d.ts.map +1 -1
  5. package/dist/types/logic/actions/activation.d.ts +12 -0
  6. package/dist/types/logic/actions/activation.d.ts.map +1 -0
  7. package/dist/types/logic/actions/device.d.ts +27 -0
  8. package/dist/types/logic/actions/device.d.ts.map +1 -0
  9. package/dist/types/logic/actions/install.d.ts +9 -0
  10. package/dist/types/logic/actions/install.d.ts.map +1 -0
  11. package/dist/types/logic/actions/pair.d.ts +6 -0
  12. package/dist/types/logic/actions/pair.d.ts.map +1 -0
  13. package/dist/types/logic/actions/proxy.d.ts +3 -0
  14. package/dist/types/logic/actions/proxy.d.ts.map +1 -0
  15. package/dist/types/logic/appleDeviceKit.d.ts +53 -7
  16. package/dist/types/logic/appleDeviceKit.d.ts.map +1 -1
  17. package/dist/types/logic/dataParser.d.ts +23 -0
  18. package/dist/types/logic/dataParser.d.ts.map +1 -0
  19. package/dist/types/logic/devicesMonitor.d.ts.map +1 -1
  20. package/dist/types/logic/enrollment.d.ts +56 -0
  21. package/dist/types/logic/enrollment.d.ts.map +1 -0
  22. package/dist/types/logic/skipSetup.d.ts +81 -0
  23. package/dist/types/logic/skipSetup.d.ts.map +1 -0
  24. package/dist/types/types/skipSteps.d.ts +136 -0
  25. package/dist/types/types/skipSteps.d.ts.map +1 -0
  26. package/dist/types/types.d.ts +22 -5
  27. package/dist/types/types.d.ts.map +1 -1
  28. package/dist/types/utils/debug.d.ts +8 -0
  29. package/dist/types/utils/debug.d.ts.map +1 -1
  30. package/dist/types/utils/idevicePath.d.ts +3 -0
  31. package/dist/types/utils/idevicePath.d.ts.map +1 -1
  32. package/dist/types/utils/usbmuxd.d.ts +19 -0
  33. package/dist/types/utils/usbmuxd.d.ts.map +1 -0
  34. package/dist/types/utils/wifiProfile.d.ts +72 -0
  35. package/dist/types/utils/wifiProfile.d.ts.map +1 -0
  36. package/package.json +3 -2
  37. package/resources/bin/windows/ideviceactivation.exe +0 -0
  38. package/resources/bin/windows/libideviceactivation-1.0.dll +0 -0
  39. package/resources/bin/windows/libimobiledevice-1.0.dll +0 -0
  40. package/resources/bin/windows/libimobiledevice-glue-1.0.dll +0 -0
  41. package/resources/bin/windows/libplist-2.0.dll +0 -0
  42. package/resources/bin/windows/libusbmuxd-2.0.dll +0 -0
  43. package/resources/lib/include/libimobiledevice/afc.h +413 -0
  44. package/resources/lib/include/libimobiledevice/bt_packet_logger.h +156 -0
  45. package/resources/lib/include/libimobiledevice/companion_proxy.h +212 -0
  46. package/resources/lib/include/libimobiledevice/debugserver.h +272 -0
  47. package/resources/lib/include/libimobiledevice/diagnostics_relay.h +228 -0
  48. package/resources/lib/include/libimobiledevice/file_relay.h +166 -0
  49. package/resources/lib/include/libimobiledevice/heartbeat.h +137 -0
  50. package/resources/lib/include/libimobiledevice/house_arrest.h +180 -0
  51. package/resources/lib/include/libimobiledevice/installation_proxy.h +505 -0
  52. package/resources/lib/include/libimobiledevice/libimobiledevice.h +444 -0
  53. package/resources/lib/include/libimobiledevice/lockdown.h +577 -0
  54. package/resources/lib/include/libimobiledevice/misagent.h +168 -0
  55. package/resources/lib/include/libimobiledevice/mobile_image_mounter.h +275 -0
  56. package/resources/lib/include/libimobiledevice/mobileactivation.h +192 -0
  57. package/resources/lib/include/libimobiledevice/mobilebackup.h +246 -0
  58. package/resources/lib/include/libimobiledevice/mobilebackup2.h +214 -0
  59. package/resources/lib/include/libimobiledevice/mobilesync.h +359 -0
  60. package/resources/lib/include/libimobiledevice/notification_proxy.h +202 -0
  61. package/resources/lib/include/libimobiledevice/ostrace.h +198 -0
  62. package/resources/lib/include/libimobiledevice/preboard.h +187 -0
  63. package/resources/lib/include/libimobiledevice/property_list_service.h +184 -0
  64. package/resources/lib/include/libimobiledevice/restore.h +179 -0
  65. package/resources/lib/include/libimobiledevice/reverse_proxy.h +213 -0
  66. package/resources/lib/include/libimobiledevice/sbservices.h +175 -0
  67. package/resources/lib/include/libimobiledevice/screenshotr.h +118 -0
  68. package/resources/lib/include/libimobiledevice/service.h +202 -0
  69. package/resources/lib/include/libimobiledevice/syslog_relay.h +184 -0
  70. package/resources/lib/include/libimobiledevice/webinspector.h +137 -0
  71. package/resources/lib/include/plist/Array.h +80 -0
  72. package/resources/lib/include/plist/Boolean.h +48 -0
  73. package/resources/lib/include/plist/Data.h +50 -0
  74. package/resources/lib/include/plist/Date.h +54 -0
  75. package/resources/lib/include/plist/Dictionary.h +73 -0
  76. package/resources/lib/include/plist/Integer.h +54 -0
  77. package/resources/lib/include/plist/Key.h +49 -0
  78. package/resources/lib/include/plist/Node.h +57 -0
  79. package/resources/lib/include/plist/Real.h +48 -0
  80. package/resources/lib/include/plist/String.h +52 -0
  81. package/resources/lib/include/plist/Structure.h +62 -0
  82. package/resources/lib/include/plist/Uid.h +48 -0
  83. package/resources/lib/include/plist/plist++.h +39 -0
  84. package/resources/lib/include/plist/plist.h +1448 -0
  85. package/resources/lib/libimobiledevice-1.0.dll +0 -0
  86. package/resources/lib/libimobiledevice-1.0.dll.a +0 -0
  87. package/resources/lib/libplist-2.0.dll +0 -0
  88. package/resources/lib/libplist-2.0.dll.a +0 -0
package/dist/index.js CHANGED
@@ -1,10 +1,9 @@
1
- // src/logic/appleDeviceKit.ts
2
- import { spawn } from "node:child_process";
3
-
4
1
  // src/utils/debug.ts
5
2
  import createDebug from "debug";
6
3
  var debug = createDebug("apple-kit");
7
4
  var debugTask = createDebug("apple-kit:task");
5
+ var debugWarning = createDebug("apple-kit:warning");
6
+ var debugError = createDebug("apple-kit:error");
8
7
  function logInfo(message) {
9
8
  debug(message);
10
9
  }
@@ -12,73 +11,13 @@ function logTask(message) {
12
11
  debugTask(message);
13
12
  }
14
13
 
15
- // src/utils/exec.ts
14
+ // src/logic/actions/device.ts
16
15
  import { exec as execCallback } from "node:child_process";
17
- import { promisify } from "node:util";
18
-
19
- // src/utils/idevicePath.ts
20
16
  import { existsSync } from "node:fs";
21
- import { dirname, join } from "node:path";
22
- import { fileURLToPath } from "node:url";
23
- var __filename = fileURLToPath(import.meta.url);
24
- var __dirname = dirname(__filename);
25
- function getResourcesBinPath() {
26
- const packageRoot = dirname(dirname(__dirname));
27
- const platform = process.platform;
28
- let platformDir;
29
- switch (platform) {
30
- case "win32":
31
- platformDir = "windows";
32
- break;
33
- case "darwin":
34
- platformDir = "darwin";
35
- break;
36
- case "linux":
37
- platformDir = "linux";
38
- break;
39
- default:
40
- throw new Error(`Unsupported platform: ${platform}`);
41
- }
42
- return join(packageRoot, "resources", "bin", platformDir);
43
- }
44
- function getIDeviceToolPath(toolName) {
45
- const binPath = getResourcesBinPath();
46
- const ext = process.platform === "win32" ? ".exe" : "";
47
- const toolPath = join(binPath, `${toolName}${ext}`);
48
- if (existsSync(toolPath)) {
49
- return toolPath;
50
- }
51
- return null;
52
- }
53
- function getIDeviceBinPath() {
54
- return getResourcesBinPath();
55
- }
56
-
57
- // src/utils/exec.ts
58
- var execAsync = promisify(execCallback);
59
- async function execIDevice(command, options = {}) {
60
- const binPath = getIDeviceBinPath();
61
- const env = {
62
- ...process.env,
63
- PATH: `${binPath};${process.env.PATH}`
64
- };
65
- const result = await execAsync(command, {
66
- ...options,
67
- env,
68
- windowsHide: true,
69
- encoding: "utf8"
70
- });
71
- return {
72
- stdout: result.stdout.toString(),
73
- stderr: result.stderr.toString()
74
- };
75
- }
76
- async function runIDeviceTool(toolPath, args = [], options = {}) {
77
- const command = `"${toolPath}" ${args.map((a) => `"${a}"`).join(" ")}`;
78
- return execIDevice(command, options);
79
- }
17
+ import { join } from "node:path";
18
+ import { promisify } from "node:util";
80
19
 
81
- // src/logic/appleDeviceKit.ts
20
+ // src/logic/dataParser.ts
82
21
  function parsePlistOutput(output) {
83
22
  const result = {};
84
23
  const lines = output.split("\n");
@@ -125,6 +64,413 @@ function parseAppList(output) {
125
64
  }
126
65
  return apps;
127
66
  }
67
+
68
+ // src/logic/actions/device.ts
69
+ var execAsync = promisify(execCallback);
70
+ async function getDeviceInfo(udid) {
71
+ logTask(`Getting device info for ${udid}`);
72
+ const result = await runIDeviceTool("ideviceinfo", ["-u", udid]);
73
+ if (!result) {
74
+ throw new Error("Failed to get device info");
75
+ }
76
+ const props = parsePlistOutput(result.stdout);
77
+ return {
78
+ deviceName: props.DeviceName || "",
79
+ productType: props.ProductType || "",
80
+ productVersion: props.ProductVersion || "",
81
+ buildVersion: props.BuildVersion || "",
82
+ serialNumber: props.SerialNumber || "",
83
+ udid: props.UniqueDeviceID || udid,
84
+ wifiAddress: props.WiFiAddress || "",
85
+ bluetoothAddress: props.BluetoothAddress || "",
86
+ phoneNumber: props.PhoneNumber || "",
87
+ cpuArchitecture: props.CPUArchitecture || "",
88
+ hardwareModel: props.HardwareModel || "",
89
+ modelNumber: props.ModelNumber || "",
90
+ regionInfo: props.RegionInfo || "",
91
+ timeZone: props.TimeZone || "",
92
+ uniqueChipID: props.UniqueChipID || "",
93
+ isPaired: true
94
+ // If we can get info, device is paired
95
+ };
96
+ }
97
+ async function listDevices() {
98
+ try {
99
+ const result = await runIDeviceTool("idevice_id", ["-l"]);
100
+ if (!result) {
101
+ return [];
102
+ }
103
+ return parseDeviceList(result.stdout);
104
+ } catch {
105
+ return [];
106
+ }
107
+ }
108
+ async function runIDeviceTool(toolName, args = [], options = {}) {
109
+ const command = `"${toolName}" ${args.map((a) => `"${a}"`).join(" ")}`;
110
+ return execIDevice(command, options);
111
+ }
112
+ async function execIDevice(command, options = {}) {
113
+ const binPath = getIDeviceBinPath();
114
+ const env = {
115
+ ...process.env,
116
+ PATH: `${binPath};${process.env.PATH}`
117
+ };
118
+ const result = await execAsync(command, {
119
+ ...options,
120
+ env,
121
+ windowsHide: true,
122
+ encoding: "utf8"
123
+ });
124
+ return {
125
+ stdout: result.stdout.toString(),
126
+ stderr: result.stderr.toString()
127
+ };
128
+ }
129
+ function getResourcesBinPath() {
130
+ const binPath = process.env.IDeviceBinPath;
131
+ if (!binPath) {
132
+ throw new Error("IDeviceBinPath is not set");
133
+ }
134
+ return binPath;
135
+ }
136
+ function getIDeviceBinPath() {
137
+ return getResourcesBinPath();
138
+ }
139
+
140
+ // src/logic/actions/activation.ts
141
+ async function getActivationState(udid) {
142
+ logTask(`Getting activation state for device ${udid}`);
143
+ try {
144
+ const result = await runIDeviceTool("ideviceinfo", ["-u", udid, "-k", "ActivationState"]);
145
+ if (!result) {
146
+ return {
147
+ isActivated: false,
148
+ activationState: "Unknown"
149
+ };
150
+ }
151
+ const { stdout } = result;
152
+ const state = stdout.trim();
153
+ return {
154
+ isActivated: state === "Activated",
155
+ activationState: state
156
+ };
157
+ } catch {
158
+ return {
159
+ isActivated: false,
160
+ activationState: "Unknown"
161
+ };
162
+ }
163
+ }
164
+ async function activate(udid) {
165
+ logTask(`Activating device ${udid}`);
166
+ try {
167
+ const result = await runIDeviceTool("ideviceactivation", ["-u", udid, "activate"]);
168
+ if (!result) {
169
+ return false;
170
+ }
171
+ return result.stdout.toLowerCase().includes("success") || result.stdout.toLowerCase().includes("activated");
172
+ } catch (error) {
173
+ const errorMsg = error instanceof Error ? error.message : String(error);
174
+ throw new Error(`Activation failed: ${errorMsg}`);
175
+ }
176
+ }
177
+
178
+ // src/logic/actions/pair.ts
179
+ async function isPaired(udid) {
180
+ logTask(`Checking pairing status for ${udid}`);
181
+ try {
182
+ const result = await runIDeviceTool("idevicepair", [
183
+ "-u",
184
+ udid,
185
+ "validate"
186
+ ]);
187
+ if (!result) {
188
+ return false;
189
+ }
190
+ return result.stdout.toLowerCase().includes("success");
191
+ } catch {
192
+ return false;
193
+ }
194
+ }
195
+ async function trustDevice(udid, timeout = 6e4, onWaitingForTrust) {
196
+ logTask(`Trusting device ${udid}`);
197
+ if (await isPaired(udid)) {
198
+ logInfo(`Device ${udid} is already trusted`);
199
+ return true;
200
+ }
201
+ logInfo(`Initiating pairing for device ${udid}`);
202
+ const pairResult = await pair(udid);
203
+ if (pairResult) {
204
+ logInfo(`Device ${udid} paired successfully`);
205
+ return true;
206
+ }
207
+ logInfo("Please accept the trust dialog on the device...");
208
+ onWaitingForTrust?.();
209
+ try {
210
+ await waitForPairing(udid, timeout, 1e3);
211
+ logInfo(`Device ${udid} is now trusted`);
212
+ return true;
213
+ } catch {
214
+ logInfo(`Timeout waiting for trust acceptance on device ${udid}`);
215
+ return false;
216
+ }
217
+ }
218
+ async function waitForPairing(udid, timeout = 12e4, pollInterval = 1e3) {
219
+ logTask(`Waiting for pairing on device ${udid}`);
220
+ const startTime = Date.now();
221
+ while (Date.now() - startTime < timeout) {
222
+ if (await isPaired(udid)) {
223
+ logInfo(`Device ${udid} is now paired`);
224
+ return true;
225
+ }
226
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
227
+ }
228
+ throw new Error(`Timeout waiting for device pairing after ${timeout}ms`);
229
+ }
230
+ async function pair(udid) {
231
+ logTask(`Initiating pairing for device ${udid}`);
232
+ try {
233
+ const result = await runIDeviceTool("idevicepair", ["-u", udid, "pair"]);
234
+ if (!result) {
235
+ return false;
236
+ }
237
+ return result.stdout.toLowerCase().includes("success");
238
+ } catch (error) {
239
+ const errorMsg = error instanceof Error ? error.message : String(error);
240
+ if (errorMsg.includes("Please accept the trust dialog")) {
241
+ return false;
242
+ }
243
+ throw error;
244
+ }
245
+ }
246
+ async function unpair(udid) {
247
+ logTask(`Un-pairing device ${udid}`);
248
+ try {
249
+ const result = await runIDeviceTool("idevicepair", [
250
+ "-u",
251
+ udid,
252
+ "unpair"
253
+ ]);
254
+ if (!result) {
255
+ return false;
256
+ }
257
+ return result.stdout.toLowerCase().includes("success");
258
+ } catch {
259
+ return false;
260
+ }
261
+ }
262
+
263
+ // src/logic/actions/install.ts
264
+ async function installApp(ipaPath, udid) {
265
+ logTask(`Installing app ${ipaPath} on device ${udid}`);
266
+ if (!await isPaired(udid)) {
267
+ await waitForPairing(udid, 1e4);
268
+ }
269
+ await runIDeviceTool("ideviceinstaller", ["-u", udid, "-i", ipaPath]);
270
+ }
271
+ async function uninstallApp(bundleId, udid) {
272
+ logTask(`Uninstalling app ${bundleId} from device ${udid}`);
273
+ if (!await isPaired(udid)) {
274
+ await waitForPairing(udid, 1e4);
275
+ }
276
+ await runIDeviceTool("ideviceinstaller", ["-u", udid, "-U", bundleId]);
277
+ }
278
+ async function listApps(udid) {
279
+ logTask(`Listing apps on device ${udid}`);
280
+ if (!await isPaired(udid)) {
281
+ await waitForPairing(udid, 1e4);
282
+ }
283
+ try {
284
+ const result = await runIDeviceTool("ideviceinstaller", ["-u", udid, "-l"]);
285
+ if (!result) {
286
+ return [];
287
+ }
288
+ const { stdout } = result;
289
+ return parseAppList(stdout);
290
+ } catch {
291
+ return [];
292
+ }
293
+ }
294
+ async function isAppInstalled(bundleId, udid) {
295
+ logTask(`Checking if app ${bundleId} is installed on device ${udid}`);
296
+ const apps = await listApps(udid);
297
+ return apps.some((app) => app.bundleId === bundleId);
298
+ }
299
+ async function wakeDevice(udid) {
300
+ try {
301
+ logInfo("Attempting to wake device screen...");
302
+ await runIDeviceTool("ideviceinfo", ["-u", udid, "-k", "DeviceName"]);
303
+ try {
304
+ await runIDeviceTool("ideviceinfo", ["-u", udid, "-k", "ActivationState"]);
305
+ } catch {
306
+ }
307
+ try {
308
+ await runIDeviceTool("idevicepair", ["-u", udid, "validate"]);
309
+ } catch {
310
+ }
311
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
312
+ logInfo("Device wake attempt completed");
313
+ } catch (error) {
314
+ logInfo(
315
+ `Device wake attempt failed (non-critical): ${error instanceof Error ? error.message : String(error)}`
316
+ );
317
+ }
318
+ }
319
+ async function launchApp(bundleId, args, udid) {
320
+ logTask(`Launching app ${bundleId} on device ${udid}`);
321
+ if (!await isPaired(udid)) {
322
+ await waitForPairing(udid, 1e4);
323
+ }
324
+ await wakeDevice(udid);
325
+ try {
326
+ logInfo(`Attempting to launch ${bundleId} using idevicedebug...`);
327
+ const result = await runIDeviceTool("idevicedebug", ["-u", udid, "run", bundleId, ...args]);
328
+ const output = (result?.stdout ?? "") + (result?.stderr ?? "");
329
+ if (output.trim()) {
330
+ logInfo(`idevicedebug output: ${output.substring(0, 200)}`);
331
+ }
332
+ if (output.toLowerCase().includes("could not start") && output.toLowerCase().includes("debugserver")) {
333
+ logInfo("idevicedebug requires debugserver, falling back to pymobiledevice3...");
334
+ throw new Error("debugserver_not_available");
335
+ }
336
+ logInfo(`App ${bundleId} launched successfully using idevicedebug`);
337
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
338
+ return;
339
+ } catch (error) {
340
+ const errorMsg = error instanceof Error ? error.message : String(error);
341
+ if (errorMsg.includes("debugserver_not_available") || errorMsg.toLowerCase().includes("could not start") || errorMsg.toLowerCase().includes("debugserver")) {
342
+ logInfo("idevicedebug failed, trying pymobiledevice3 for iOS 17+...");
343
+ await launchAppWithPymobiledevice3(bundleId, args, udid);
344
+ return;
345
+ }
346
+ throw error;
347
+ }
348
+ }
349
+ async function launchAppWithPymobiledevice3(bundleId, args, udid) {
350
+ logTask(`Launching app ${bundleId} using pymobiledevice3`);
351
+ const { exec } = await import("node:child_process");
352
+ const { promisify: promisify2 } = await import("node:util");
353
+ const execAsync2 = promisify2(exec);
354
+ try {
355
+ let cmdArgs = [
356
+ "-m",
357
+ "pymobiledevice3",
358
+ "developer",
359
+ "dvt",
360
+ "launch",
361
+ "--udid",
362
+ udid,
363
+ "--kill-existing",
364
+ // Kill existing instance to bring app to foreground
365
+ bundleId,
366
+ ...args
367
+ ];
368
+ let command = `python ${cmdArgs.map((a) => `"${a}"`).join(" ")}`;
369
+ logInfo(`Executing: ${command}`);
370
+ const result = await execAsync2(command, {
371
+ windowsHide: true,
372
+ encoding: "utf8",
373
+ timeout: 3e4
374
+ // 30 second timeout
375
+ });
376
+ const output = result.stdout.toString() + result.stderr.toString();
377
+ if (output.trim()) {
378
+ logInfo(`pymobiledevice3 output: ${output.substring(0, 200)}`);
379
+ }
380
+ if (output.toLowerCase().includes("developer mode") && output.toLowerCase().includes("not enabled")) {
381
+ throw new Error(
382
+ "Developer Mode is not enabled on the device.\nPlease enable it: Settings \u2192 Privacy & Security \u2192 Developer Mode \u2192 Enable"
383
+ );
384
+ }
385
+ if (output.toLowerCase().includes("tunneld") && (output.toLowerCase().includes("unable to connect") || output.toLowerCase().includes("invalidserviceerror"))) {
386
+ logInfo("Tunnel required, retrying with tunnel option...");
387
+ cmdArgs = [
388
+ "-m",
389
+ "pymobiledevice3",
390
+ "developer",
391
+ "dvt",
392
+ "launch",
393
+ "--udid",
394
+ udid,
395
+ "--tunnel",
396
+ udid,
397
+ // Use UDID for tunnel
398
+ "--kill-existing",
399
+ bundleId,
400
+ ...args
401
+ ];
402
+ command = `python ${cmdArgs.map((a) => `"${a}"`).join(" ")}`;
403
+ logInfo(`Retrying with tunnel: ${command}`);
404
+ try {
405
+ const retryResult = await execAsync2(command, {
406
+ windowsHide: true,
407
+ encoding: "utf8",
408
+ timeout: 3e4
409
+ });
410
+ const retryOutput = retryResult.stdout.toString() + retryResult.stderr.toString();
411
+ if (retryOutput.trim()) {
412
+ logInfo(`pymobiledevice3 retry output: ${retryOutput.substring(0, 200)}`);
413
+ }
414
+ if (retryOutput.toLowerCase().includes("tunneld") && retryOutput.toLowerCase().includes("unable to connect")) {
415
+ throw new Error(
416
+ "Tunnel connection failed. For iOS 17+, you may need to start tunneld:\n python -m pymobiledevice3 remote tunneld\nOr ensure Developer Mode is enabled and device is unlocked."
417
+ );
418
+ }
419
+ logInfo(`App ${bundleId} launched successfully using pymobiledevice3 with tunnel`);
420
+ await new Promise((resolve) => setTimeout(resolve, 500));
421
+ return;
422
+ } catch {
423
+ throw new Error(
424
+ "Tunnel connection failed. For iOS 17+, you may need to start tunneld:\n python -m pymobiledevice3 remote tunneld\nOr ensure Developer Mode is enabled and device is unlocked."
425
+ );
426
+ }
427
+ }
428
+ if (output.toLowerCase().includes("not found") || output.toLowerCase().includes("command not found") || output.toLowerCase().includes("cannot find")) {
429
+ throw new Error(
430
+ "pymobiledevice3 is not installed.\nInstall it with: python -m pip install --user pymobiledevice3"
431
+ );
432
+ }
433
+ if (output.toLowerCase().includes("error") && !output.toLowerCase().includes("warning")) {
434
+ logInfo(`Warning: pymobiledevice3 reported errors: ${output.substring(0, 300)}`);
435
+ }
436
+ logInfo(`App ${bundleId} launched successfully using pymobiledevice3`);
437
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
438
+ } catch (error) {
439
+ const errorMsg = error instanceof Error ? error.message : String(error);
440
+ if (errorMsg.includes("not found") || errorMsg.includes("command not found") || errorMsg.includes("cannot find") || errorMsg.includes("No module named")) {
441
+ throw new Error(
442
+ "pymobiledevice3 is not installed or Python is not available.\nInstall it with: python -m pip install --user pymobiledevice3\nFor iOS 17+, Developer Mode must also be enabled on the device."
443
+ );
444
+ }
445
+ throw error;
446
+ }
447
+ }
448
+
449
+ // src/logic/actions/proxy.ts
450
+ async function startPortForward(localPort, devicePort, udid) {
451
+ logTask(`Starting port forward ${localPort} -> ${devicePort} for device ${udid}`);
452
+ const result = await runIDeviceTool("iproxy", [
453
+ localPort.toString(),
454
+ devicePort.toString(),
455
+ "-u",
456
+ udid
457
+ ]);
458
+ return {
459
+ localPort,
460
+ devicePort,
461
+ ...result
462
+ };
463
+ }
464
+ async function startPortForwardAsync(localPort, devicePort, udid, _timeout = 5e3) {
465
+ const result = await startPortForward(localPort, devicePort, udid);
466
+ await new Promise((resolve) => setTimeout(resolve, 500));
467
+ if (result.stdout.includes("error") || result.stderr.includes("error")) {
468
+ throw new Error("Port forwarding failed to start");
469
+ }
470
+ return result;
471
+ }
472
+
473
+ // src/logic/appleDeviceKit.ts
128
474
  var AppleDeviceKit = class {
129
475
  constructor(udid, port) {
130
476
  this.port = port;
@@ -132,314 +478,127 @@ var AppleDeviceKit = class {
132
478
  logInfo(`AppleDeviceKit initialized for device: ${this.deviceId}`);
133
479
  }
134
480
  deviceId;
135
- // ==================== Device Info ====================
136
481
  /**
137
482
  * Get detailed device information
138
483
  */
139
484
  async getDeviceInfo() {
140
- logTask(`Getting device info for ${this.deviceId}`);
141
- const toolPath = getIDeviceToolPath("ideviceinfo");
142
- if (!toolPath) {
143
- throw new Error("ideviceinfo tool not found");
144
- }
145
- const { stdout } = await runIDeviceTool(toolPath, ["-u", this.deviceId]);
146
- const props = parsePlistOutput(stdout);
147
- return {
148
- deviceName: props.DeviceName || "",
149
- productType: props.ProductType || "",
150
- productVersion: props.ProductVersion || "",
151
- buildVersion: props.BuildVersion || "",
152
- serialNumber: props.SerialNumber || "",
153
- udid: props.UniqueDeviceID || this.deviceId,
154
- wifiAddress: props.WiFiAddress || "",
155
- bluetoothAddress: props.BluetoothAddress || "",
156
- phoneNumber: props.PhoneNumber || "",
157
- cpuArchitecture: props.CPUArchitecture || "",
158
- hardwareModel: props.HardwareModel || "",
159
- modelNumber: props.ModelNumber || "",
160
- regionInfo: props.RegionInfo || "",
161
- timeZone: props.TimeZone || "",
162
- uniqueChipID: props.UniqueChipID || "",
163
- isPaired: true
164
- // If we can get info, device is paired
165
- };
485
+ return getDeviceInfo(this.deviceId);
166
486
  }
167
- // ==================== Trust/Pairing ====================
168
487
  /**
169
488
  * Check if device is paired/trusted with this computer
170
489
  */
171
490
  async isPaired() {
172
- logTask(`Checking pairing status for ${this.deviceId}`);
173
- const toolPath = getIDeviceToolPath("idevicepair");
174
- if (!toolPath) {
175
- throw new Error("idevicepair tool not found");
176
- }
177
- try {
178
- const { stdout } = await runIDeviceTool(toolPath, ["-u", this.deviceId, "validate"]);
179
- return stdout.toLowerCase().includes("success");
180
- } catch {
181
- return false;
182
- }
491
+ return isPaired(this.deviceId);
183
492
  }
184
493
  /**
185
494
  * Wait for device to be paired
186
495
  * Polls the pairing status until successful or timeout
187
- *
496
+ *
188
497
  * @param timeout Timeout in milliseconds (default: 120000)
189
498
  * @param pollInterval Poll interval in milliseconds (default: 1000)
190
499
  */
191
500
  async waitForPairing(timeout = 12e4, pollInterval = 1e3) {
192
- logTask(`Waiting for pairing on device ${this.deviceId}`);
193
- const startTime = Date.now();
194
- while (Date.now() - startTime < timeout) {
195
- if (await this.isPaired()) {
196
- logInfo(`Device ${this.deviceId} is now paired`);
197
- return true;
198
- }
199
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
200
- }
201
- throw new Error(`Timeout waiting for device pairing after ${timeout}ms`);
501
+ return waitForPairing(this.deviceId, timeout, pollInterval);
202
502
  }
203
503
  /**
204
504
  * Attempt to pair/trust the device
205
505
  * User must accept the trust dialog on the device
206
506
  */
207
507
  async pair() {
208
- logTask(`Initiating pairing for device ${this.deviceId}`);
209
- const toolPath = getIDeviceToolPath("idevicepair");
210
- if (!toolPath) {
211
- throw new Error("idevicepair tool not found");
212
- }
213
- try {
214
- const { stdout } = await runIDeviceTool(toolPath, ["-u", this.deviceId, "pair"]);
215
- return stdout.toLowerCase().includes("success");
216
- } catch (error) {
217
- const errorMsg = error instanceof Error ? error.message : String(error);
218
- if (errorMsg.includes("Please accept the trust dialog")) {
219
- return false;
220
- }
221
- throw error;
222
- }
508
+ return pair(this.deviceId);
509
+ }
510
+ /**
511
+ * Trust/pair the device - initiates pairing and waits for user to accept
512
+ *
513
+ * This is the recommended method for establishing trust with a device.
514
+ * It will:
515
+ * 1. Check if already paired
516
+ * 2. If not, initiate pairing (shows "Trust This Computer?" on device)
517
+ * 3. Wait for user to accept the trust dialog
518
+ *
519
+ * @param timeout Timeout in milliseconds to wait for user acceptance (default: 60000)
520
+ * @param onWaitingForTrust Callback when waiting for user to accept trust dialog
521
+ * @returns true if device is now trusted
522
+ */
523
+ async trustDevice(timeout = 6e4, onWaitingForTrust) {
524
+ return trustDevice(this.deviceId, timeout, onWaitingForTrust);
223
525
  }
224
526
  /**
225
527
  * Unpair/untrust the device
226
528
  */
227
529
  async unpair() {
228
- logTask(`Unpairing device ${this.deviceId}`);
229
- const toolPath = getIDeviceToolPath("idevicepair");
230
- if (!toolPath) {
231
- throw new Error("idevicepair tool not found");
232
- }
233
- try {
234
- const { stdout } = await runIDeviceTool(toolPath, ["-u", this.deviceId, "unpair"]);
235
- return stdout.toLowerCase().includes("success");
236
- } catch {
237
- return false;
238
- }
530
+ return unpair(this.deviceId);
239
531
  }
240
- // ==================== App Installation ====================
241
532
  /**
242
533
  * Install an IPA file on the device (install agent)
243
- *
534
+ *
244
535
  * @param ipaPath Path to the IPA file
245
536
  */
246
537
  async installApp(ipaPath) {
247
- logTask(`Installing app ${ipaPath} on device ${this.deviceId}`);
248
- if (!await this.isPaired()) {
249
- await this.waitForPairing(1e4);
250
- }
251
- const toolPath = getIDeviceToolPath("ideviceinstaller");
252
- if (!toolPath) {
253
- throw new Error("ideviceinstaller tool not found");
254
- }
255
- await runIDeviceTool(toolPath, ["-u", this.deviceId, "-i", ipaPath]);
538
+ installApp(ipaPath, this.deviceId);
256
539
  }
257
540
  /**
258
541
  * Uninstall an app by bundle ID (uninstall agent)
259
- *
542
+ *
260
543
  * @param bundleId Application bundle identifier
261
544
  */
262
545
  async uninstallApp(bundleId) {
263
- logTask(`Uninstalling app ${bundleId} from device ${this.deviceId}`);
264
- if (!await this.isPaired()) {
265
- await this.waitForPairing(1e4);
266
- }
267
- const toolPath = getIDeviceToolPath("ideviceinstaller");
268
- if (!toolPath) {
269
- throw new Error("ideviceinstaller tool not found");
270
- }
271
- await runIDeviceTool(toolPath, ["-u", this.deviceId, "-U", bundleId]);
546
+ uninstallApp(bundleId, this.deviceId);
272
547
  }
273
548
  /**
274
549
  * Check if an app is installed on the device
275
- *
550
+ *
276
551
  * @param bundleId Application bundle identifier
277
552
  */
278
553
  async isAppInstalled(bundleId) {
279
- logTask(`Checking if app ${bundleId} is installed on device ${this.deviceId}`);
280
- const apps = await this.listApps();
281
- return apps.some((app) => app.bundleId === bundleId);
554
+ return isAppInstalled(bundleId, this.deviceId);
282
555
  }
283
556
  /**
284
557
  * List all installed user applications
285
558
  */
286
559
  async listApps() {
287
- logTask(`Listing apps on device ${this.deviceId}`);
288
- if (!await this.isPaired()) {
289
- await this.waitForPairing(1e4);
290
- }
291
- const toolPath = getIDeviceToolPath("ideviceinstaller");
292
- if (!toolPath) {
293
- throw new Error("ideviceinstaller tool not found");
294
- }
295
- try {
296
- const { stdout } = await runIDeviceTool(toolPath, ["-u", this.deviceId, "-l"]);
297
- return parseAppList(stdout);
298
- } catch {
299
- return [];
300
- }
560
+ return listApps(this.deviceId);
301
561
  }
302
- // ==================== Launch Application ====================
303
562
  /**
304
563
  * Launch an application on the device
305
- *
306
- * @param bundleId Application bundle identifier to launch
307
- * @param args Optional arguments to pass to the app
564
+ *
565
+ * @param bundleId Application bundle identifier
566
+ * @param args Application arguments
308
567
  */
309
568
  async launchApp(bundleId, args = []) {
310
- logTask(`Launching app ${bundleId} on device ${this.deviceId}`);
311
- if (!await this.isPaired()) {
312
- await this.waitForPairing(1e4);
313
- }
314
- const toolPath = getIDeviceToolPath("idevicedebug");
315
- if (!toolPath) {
316
- throw new Error("idevicedebug tool not found");
317
- }
318
- const toolArgs = ["-u", this.deviceId, "run", bundleId, ...args];
319
- await runIDeviceTool(toolPath, toolArgs);
320
- }
321
- // ==================== Port Forwarding ====================
322
- /**
323
- * Start port forwarding from local port to device port
324
- *
325
- * @param localPort Local port to listen on
326
- * @param devicePort Device port to forward to
327
- * @returns Handle to stop the port forwarding
328
- */
329
- startPortForward(localPort, devicePort) {
330
- logTask(`Starting port forward ${localPort} -> ${devicePort} for device ${this.deviceId}`);
331
- const toolPath = getIDeviceToolPath("iproxy");
332
- if (!toolPath) {
333
- throw new Error("iproxy tool not found");
334
- }
335
- const binPath = getIDeviceBinPath();
336
- const env = {
337
- ...process.env,
338
- PATH: `${binPath};${process.env.PATH}`
339
- };
340
- const proc = spawn(toolPath, [
341
- localPort.toString(),
342
- devicePort.toString(),
343
- "-u",
344
- this.deviceId
345
- ], {
346
- env,
347
- windowsHide: true,
348
- stdio: "pipe"
349
- });
350
- proc.on("error", (err) => {
351
- logInfo(`Port forward error: ${err.message}`);
352
- });
353
- return {
354
- stop: () => {
355
- proc.kill();
356
- },
357
- localPort,
358
- devicePort,
359
- process: proc
360
- };
569
+ return launchApp(bundleId, args, this.deviceId);
361
570
  }
362
571
  /**
363
- * Start port forwarding and wait for it to be ready
364
- *
572
+ * Start port forwarding and wait for it to be ready.
573
+ * we need port forwarding to be able to connect to the device from the computer
574
+ * and communicate with it using the local port.
575
+ *
365
576
  * @param localPort Local port to listen on
366
577
  * @param devicePort Device port to forward to
367
578
  * @param _timeout Timeout in milliseconds (reserved for future use)
368
579
  */
369
580
  async startPortForwardAsync(localPort, devicePort, _timeout = 5e3) {
370
- const handle = this.startPortForward(localPort, devicePort);
371
- await new Promise((resolve) => setTimeout(resolve, 500));
372
- if (handle.process.killed || handle.process.exitCode !== null) {
373
- throw new Error("Port forwarding failed to start");
374
- }
375
- return handle;
581
+ return startPortForwardAsync(localPort, devicePort, this.deviceId);
376
582
  }
377
- // ==================== Activation ====================
378
583
  /**
379
584
  * Get the activation state of the device
380
585
  */
381
586
  async getActivationState() {
382
- logTask(`Getting activation state for device ${this.deviceId}`);
383
- const toolPath = getIDeviceToolPath("ideviceinfo");
384
- if (!toolPath) {
385
- throw new Error("ideviceinfo tool not found");
386
- }
387
- try {
388
- const { stdout } = await runIDeviceTool(toolPath, ["-u", this.deviceId, "-k", "ActivationState"]);
389
- const state = stdout.trim();
390
- return {
391
- isActivated: state === "Activated",
392
- activationState: state
393
- };
394
- } catch {
395
- return {
396
- isActivated: false,
397
- activationState: "Unknown"
398
- };
399
- }
587
+ return getActivationState(this.deviceId);
400
588
  }
401
589
  /**
402
- * Activate the device
590
+ * Activate the device (register in apple servers), an activated device belongs to someone (a user/company).
591
+ * A device that is on hello screen cannot pass the wifi step unless it is activated.
592
+ * the activate save us the need of the device to be connected to the internet to do the activation
593
+ * and register in apple servers.
594
+ *
403
595
  * Note: This requires a valid activation record or Apple server access
596
+ *
597
+ * precondition: the device must be paired and trusted
404
598
  */
405
599
  async activate() {
406
- logTask(`Activating device ${this.deviceId}`);
407
- const toolPath = getIDeviceToolPath("ideviceactivation");
408
- if (!toolPath) {
409
- throw new Error("ideviceactivation tool not found");
410
- }
411
- try {
412
- const { stdout } = await runIDeviceTool(toolPath, ["-u", this.deviceId, "activate"]);
413
- return stdout.toLowerCase().includes("success") || stdout.toLowerCase().includes("activated");
414
- } catch (error) {
415
- const errorMsg = error instanceof Error ? error.message : String(error);
416
- throw new Error(`Activation failed: ${errorMsg}`);
417
- }
418
- }
419
- /**
420
- * Deactivate the device
421
- */
422
- async deactivate() {
423
- logTask(`Deactivating device ${this.deviceId}`);
424
- const toolPath = getIDeviceToolPath("ideviceactivation");
425
- if (!toolPath) {
426
- throw new Error("ideviceactivation tool not found");
427
- }
428
- try {
429
- const { stdout } = await runIDeviceTool(toolPath, ["-u", this.deviceId, "deactivate"]);
430
- return stdout.toLowerCase().includes("success");
431
- } catch {
432
- return false;
433
- }
434
- }
435
- /**
436
- * Get activation state as string
437
- */
438
- async getActivationStateString() {
439
- const state = await this.getActivationState();
440
- return state.activationState;
600
+ return activate(this.deviceId);
441
601
  }
442
- // ==================== Device Identifiers ====================
443
602
  /**
444
603
  * Get the device UDID
445
604
  */
@@ -452,22 +611,6 @@ var AppleDeviceKit = class {
452
611
  getPort() {
453
612
  return this.port;
454
613
  }
455
- // ==================== Static Methods ====================
456
- /**
457
- * Static method to list all connected iOS devices
458
- */
459
- static async listDevices() {
460
- const toolPath = getIDeviceToolPath("idevice_id");
461
- if (!toolPath) {
462
- throw new Error("idevice_id tool not found");
463
- }
464
- try {
465
- const { stdout } = await runIDeviceTool(toolPath, ["-l"]);
466
- return parseDeviceList(stdout);
467
- } catch {
468
- return [];
469
- }
470
- }
471
614
  };
472
615
 
473
616
  // src/logic/devicesMonitor.ts
@@ -483,7 +626,7 @@ var DevicesMonitor = class {
483
626
  eventEmitter;
484
627
  /**
485
628
  * Start tracking iOS device connections
486
- *
629
+ *
487
630
  * @returns EventEmitter that emits:
488
631
  * - 'added': (deviceKit: AppleDeviceKit) when a device is connected
489
632
  * - 'removed': (deviceId: string, port: number) when a device is disconnected
@@ -493,9 +636,7 @@ var DevicesMonitor = class {
493
636
  const listenerConfig = {
494
637
  logicalPortMap: this.config.logicalPortMap,
495
638
  // Filter for Apple devices only
496
- listenOnlyDevices: [
497
- { vid: APPLE_VID.toString(16).toUpperCase().padStart(4, "0"), pid: "" }
498
- ]
639
+ listenOnlyDevices: [{ vid: APPLE_VID.toString(16).toUpperCase().padStart(4, "0"), pid: "" }]
499
640
  };
500
641
  usbDeviceListener.startListening(listenerConfig);
501
642
  this.eventEmitter = new EventEmitter();
@@ -553,7 +694,7 @@ var DevicesMonitor = class {
553
694
  * Find connected iOS device and emit event
554
695
  */
555
696
  async findAndEmitDevice(usbDevice) {
556
- const iOSDevices = await AppleDeviceKit.listDevices();
697
+ const iOSDevices = await listDevices();
557
698
  if (iOSDevices.length === 0) {
558
699
  setTimeout(() => this.findAndEmitDevice(usbDevice), 500);
559
700
  return;
@@ -576,7 +717,7 @@ var DevicesMonitor = class {
576
717
  */
577
718
  async identifyConnectedDevices() {
578
719
  logInfo("Identifying already connected iOS devices");
579
- const iOSDevices = await AppleDeviceKit.listDevices();
720
+ const iOSDevices = await listDevices();
580
721
  const usbDevices = usbDeviceListener.listDevices().filter((d) => d.vid === APPLE_VID);
581
722
  for (let i = 0; i < iOSDevices.length; i++) {
582
723
  const iDevice = iOSDevices[i];
@@ -593,88 +734,8 @@ var DevicesMonitor = class {
593
734
  }
594
735
  }
595
736
  };
596
-
597
- // src/utils/usbmuxd.ts
598
- import { spawn as spawn2 } from "node:child_process";
599
- var usbmuxdProcess = null;
600
- function startUsbmuxd(foreground = false) {
601
- if (usbmuxdProcess && !usbmuxdProcess.killed) {
602
- logInfo("usbmuxd is already running");
603
- return {
604
- stop: () => stopUsbmuxd(),
605
- isRunning: () => !usbmuxdProcess?.killed,
606
- process: usbmuxdProcess
607
- };
608
- }
609
- const toolPath = getIDeviceToolPath("usbmuxd");
610
- if (!toolPath) {
611
- throw new Error("usbmuxd executable not found");
612
- }
613
- const binPath = getIDeviceBinPath();
614
- const env = {
615
- ...process.env,
616
- PATH: `${binPath};${process.env.PATH}`
617
- };
618
- const args = [];
619
- if (foreground) {
620
- args.push("-f");
621
- }
622
- args.push("-v");
623
- logInfo(`Starting usbmuxd: ${toolPath}`);
624
- usbmuxdProcess = spawn2(toolPath, args, {
625
- env,
626
- windowsHide: true,
627
- stdio: "pipe",
628
- detached: !foreground
629
- });
630
- usbmuxdProcess.stdout?.on("data", (data) => {
631
- logInfo(`usbmuxd: ${data.toString().trim()}`);
632
- });
633
- usbmuxdProcess.stderr?.on("data", (data) => {
634
- logInfo(`usbmuxd error: ${data.toString().trim()}`);
635
- });
636
- usbmuxdProcess.on("error", (err) => {
637
- logInfo(`usbmuxd failed to start: ${err.message}`);
638
- });
639
- usbmuxdProcess.on("exit", (code) => {
640
- logInfo(`usbmuxd exited with code ${code}`);
641
- usbmuxdProcess = null;
642
- });
643
- if (!foreground) {
644
- usbmuxdProcess.unref();
645
- }
646
- return {
647
- stop: () => stopUsbmuxd(),
648
- isRunning: () => usbmuxdProcess !== null && !usbmuxdProcess.killed,
649
- process: usbmuxdProcess
650
- };
651
- }
652
- function stopUsbmuxd() {
653
- if (usbmuxdProcess && !usbmuxdProcess.killed) {
654
- logInfo("Stopping usbmuxd");
655
- usbmuxdProcess.kill();
656
- usbmuxdProcess = null;
657
- }
658
- }
659
- function isUsbmuxdRunning() {
660
- return usbmuxdProcess !== null && !usbmuxdProcess.killed;
661
- }
662
- function ensureUsbmuxd() {
663
- if (isUsbmuxdRunning()) {
664
- return {
665
- stop: () => stopUsbmuxd(),
666
- isRunning: () => isUsbmuxdRunning(),
667
- process: usbmuxdProcess
668
- };
669
- }
670
- return startUsbmuxd();
671
- }
672
737
  export {
673
738
  AppleDeviceKit,
674
- DevicesMonitor,
675
- ensureUsbmuxd,
676
- isUsbmuxdRunning,
677
- startUsbmuxd,
678
- stopUsbmuxd
739
+ DevicesMonitor
679
740
  };
680
741
  //# sourceMappingURL=index.js.map