@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.
- package/dist/index.js +465 -404
- package/dist/index.js.map +4 -4
- package/dist/types/index.d.ts +6 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/logic/actions/activation.d.ts +12 -0
- package/dist/types/logic/actions/activation.d.ts.map +1 -0
- package/dist/types/logic/actions/device.d.ts +27 -0
- package/dist/types/logic/actions/device.d.ts.map +1 -0
- package/dist/types/logic/actions/install.d.ts +9 -0
- package/dist/types/logic/actions/install.d.ts.map +1 -0
- package/dist/types/logic/actions/pair.d.ts +6 -0
- package/dist/types/logic/actions/pair.d.ts.map +1 -0
- package/dist/types/logic/actions/proxy.d.ts +3 -0
- package/dist/types/logic/actions/proxy.d.ts.map +1 -0
- package/dist/types/logic/appleDeviceKit.d.ts +53 -7
- package/dist/types/logic/appleDeviceKit.d.ts.map +1 -1
- package/dist/types/logic/dataParser.d.ts +23 -0
- package/dist/types/logic/dataParser.d.ts.map +1 -0
- package/dist/types/logic/devicesMonitor.d.ts.map +1 -1
- package/dist/types/logic/enrollment.d.ts +56 -0
- package/dist/types/logic/enrollment.d.ts.map +1 -0
- package/dist/types/logic/skipSetup.d.ts +81 -0
- package/dist/types/logic/skipSetup.d.ts.map +1 -0
- package/dist/types/types/skipSteps.d.ts +136 -0
- package/dist/types/types/skipSteps.d.ts.map +1 -0
- package/dist/types/types.d.ts +22 -5
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/debug.d.ts +8 -0
- package/dist/types/utils/debug.d.ts.map +1 -1
- package/dist/types/utils/idevicePath.d.ts +3 -0
- package/dist/types/utils/idevicePath.d.ts.map +1 -1
- package/dist/types/utils/usbmuxd.d.ts +19 -0
- package/dist/types/utils/usbmuxd.d.ts.map +1 -0
- package/dist/types/utils/wifiProfile.d.ts +72 -0
- package/dist/types/utils/wifiProfile.d.ts.map +1 -0
- package/package.json +3 -2
- package/resources/bin/windows/ideviceactivation.exe +0 -0
- package/resources/bin/windows/libideviceactivation-1.0.dll +0 -0
- package/resources/bin/windows/libimobiledevice-1.0.dll +0 -0
- package/resources/bin/windows/libimobiledevice-glue-1.0.dll +0 -0
- package/resources/bin/windows/libplist-2.0.dll +0 -0
- package/resources/bin/windows/libusbmuxd-2.0.dll +0 -0
- package/resources/lib/include/libimobiledevice/afc.h +413 -0
- package/resources/lib/include/libimobiledevice/bt_packet_logger.h +156 -0
- package/resources/lib/include/libimobiledevice/companion_proxy.h +212 -0
- package/resources/lib/include/libimobiledevice/debugserver.h +272 -0
- package/resources/lib/include/libimobiledevice/diagnostics_relay.h +228 -0
- package/resources/lib/include/libimobiledevice/file_relay.h +166 -0
- package/resources/lib/include/libimobiledevice/heartbeat.h +137 -0
- package/resources/lib/include/libimobiledevice/house_arrest.h +180 -0
- package/resources/lib/include/libimobiledevice/installation_proxy.h +505 -0
- package/resources/lib/include/libimobiledevice/libimobiledevice.h +444 -0
- package/resources/lib/include/libimobiledevice/lockdown.h +577 -0
- package/resources/lib/include/libimobiledevice/misagent.h +168 -0
- package/resources/lib/include/libimobiledevice/mobile_image_mounter.h +275 -0
- package/resources/lib/include/libimobiledevice/mobileactivation.h +192 -0
- package/resources/lib/include/libimobiledevice/mobilebackup.h +246 -0
- package/resources/lib/include/libimobiledevice/mobilebackup2.h +214 -0
- package/resources/lib/include/libimobiledevice/mobilesync.h +359 -0
- package/resources/lib/include/libimobiledevice/notification_proxy.h +202 -0
- package/resources/lib/include/libimobiledevice/ostrace.h +198 -0
- package/resources/lib/include/libimobiledevice/preboard.h +187 -0
- package/resources/lib/include/libimobiledevice/property_list_service.h +184 -0
- package/resources/lib/include/libimobiledevice/restore.h +179 -0
- package/resources/lib/include/libimobiledevice/reverse_proxy.h +213 -0
- package/resources/lib/include/libimobiledevice/sbservices.h +175 -0
- package/resources/lib/include/libimobiledevice/screenshotr.h +118 -0
- package/resources/lib/include/libimobiledevice/service.h +202 -0
- package/resources/lib/include/libimobiledevice/syslog_relay.h +184 -0
- package/resources/lib/include/libimobiledevice/webinspector.h +137 -0
- package/resources/lib/include/plist/Array.h +80 -0
- package/resources/lib/include/plist/Boolean.h +48 -0
- package/resources/lib/include/plist/Data.h +50 -0
- package/resources/lib/include/plist/Date.h +54 -0
- package/resources/lib/include/plist/Dictionary.h +73 -0
- package/resources/lib/include/plist/Integer.h +54 -0
- package/resources/lib/include/plist/Key.h +49 -0
- package/resources/lib/include/plist/Node.h +57 -0
- package/resources/lib/include/plist/Real.h +48 -0
- package/resources/lib/include/plist/String.h +52 -0
- package/resources/lib/include/plist/Structure.h +62 -0
- package/resources/lib/include/plist/Uid.h +48 -0
- package/resources/lib/include/plist/plist++.h +39 -0
- package/resources/lib/include/plist/plist.h +1448 -0
- package/resources/lib/libimobiledevice-1.0.dll +0 -0
- package/resources/lib/libimobiledevice-1.0.dll.a +0 -0
- package/resources/lib/libplist-2.0.dll +0 -0
- 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/
|
|
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 {
|
|
22
|
-
import {
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
307
|
-
* @param args
|
|
564
|
+
*
|
|
565
|
+
* @param bundleId Application bundle identifier
|
|
566
|
+
* @param args Application arguments
|
|
308
567
|
*/
|
|
309
568
|
async launchApp(bundleId, args = []) {
|
|
310
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|