@mcesystems/apple-kit 1.0.60 → 1.0.62
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 +612 -799
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +613 -800
- package/dist/index.mjs.map +4 -4
- package/dist/types/logic/actions/device.d.ts +5 -1
- package/dist/types/logic/actions/device.d.ts.map +1 -1
- package/dist/types/logic/actions/install.d.ts +1 -2
- package/dist/types/logic/actions/install.d.ts.map +1 -1
- package/dist/types/logic/activationFlow.d.ts +2 -4
- package/dist/types/logic/activationFlow.d.ts.map +1 -1
- package/dist/types/logic/appleDeviceKit.d.ts +8 -0
- package/dist/types/logic/appleDeviceKit.d.ts.map +1 -1
- package/dist/types/logic/iosCli.d.ts +3 -0
- package/dist/types/logic/iosCli.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32389,16 +32389,91 @@ async function getActivationState(udid) {
|
|
|
32389
32389
|
}
|
|
32390
32390
|
|
|
32391
32391
|
// src/logic/actions/device.ts
|
|
32392
|
-
var
|
|
32393
|
-
var
|
|
32392
|
+
var import_node_fs2 = require("node:fs");
|
|
32393
|
+
var import_node_path3 = require("node:path");
|
|
32394
32394
|
|
|
32395
|
-
// src/logic/
|
|
32396
|
-
|
|
32397
|
-
|
|
32398
|
-
|
|
32395
|
+
// src/logic/dataParser.ts
|
|
32396
|
+
function parsePlistOutput(output) {
|
|
32397
|
+
const result = {};
|
|
32398
|
+
const lines = output.split("\n");
|
|
32399
|
+
for (const line of lines) {
|
|
32400
|
+
const colonIndex = line.indexOf(":");
|
|
32401
|
+
if (colonIndex > 0) {
|
|
32402
|
+
const key = line.substring(0, colonIndex).trim();
|
|
32403
|
+
const value = line.substring(colonIndex + 1).trim();
|
|
32404
|
+
result[key] = value;
|
|
32405
|
+
}
|
|
32406
|
+
}
|
|
32407
|
+
return result;
|
|
32408
|
+
}
|
|
32409
|
+
function parseAppList(output) {
|
|
32410
|
+
const apps = [];
|
|
32411
|
+
const lines = output.trim().split("\n");
|
|
32412
|
+
for (const line of lines) {
|
|
32413
|
+
const match = line.match(/^([^,]+),\s*([^,]+),\s*"?([^"]+)"?/);
|
|
32414
|
+
if (match) {
|
|
32415
|
+
apps.push({
|
|
32416
|
+
bundleId: match[1].trim(),
|
|
32417
|
+
version: match[2].trim(),
|
|
32418
|
+
displayName: match[3].trim(),
|
|
32419
|
+
bundleVersion: ""
|
|
32420
|
+
});
|
|
32421
|
+
}
|
|
32422
|
+
}
|
|
32423
|
+
return apps;
|
|
32424
|
+
}
|
|
32425
|
+
|
|
32426
|
+
// src/logic/actions/device.ts
|
|
32427
|
+
async function getDeviceInfo(udid) {
|
|
32428
|
+
logTask(`Getting device info for ${udid}`);
|
|
32429
|
+
const result = await runIDeviceTool("ideviceinfo", ["-u", udid]);
|
|
32430
|
+
if (!result) {
|
|
32431
|
+
throw new Error("Failed to get device info");
|
|
32432
|
+
}
|
|
32433
|
+
const props = parsePlistOutput(result.stdout);
|
|
32434
|
+
return {
|
|
32435
|
+
deviceName: props.DeviceName || "",
|
|
32436
|
+
productType: props.ProductType || "",
|
|
32437
|
+
productVersion: props.ProductVersion || "",
|
|
32438
|
+
buildVersion: props.BuildVersion || "",
|
|
32439
|
+
serialNumber: props.SerialNumber || "",
|
|
32440
|
+
udid: props.UniqueDeviceID || udid,
|
|
32441
|
+
wifiAddress: props.WiFiAddress || "",
|
|
32442
|
+
bluetoothAddress: props.BluetoothAddress || "",
|
|
32443
|
+
phoneNumber: props.PhoneNumber || "",
|
|
32444
|
+
cpuArchitecture: props.CPUArchitecture || "",
|
|
32445
|
+
hardwareModel: props.HardwareModel || "",
|
|
32446
|
+
modelNumber: props.ModelNumber || "",
|
|
32447
|
+
regionInfo: props.RegionInfo || "",
|
|
32448
|
+
timeZone: props.TimeZone || "",
|
|
32449
|
+
uniqueChipID: props.UniqueChipID || "",
|
|
32450
|
+
isPaired: true
|
|
32451
|
+
// If we can get info, device is paired
|
|
32452
|
+
};
|
|
32453
|
+
}
|
|
32454
|
+
async function info(udid, iosCli2) {
|
|
32455
|
+
logTask(`Getting device info for ${udid}`);
|
|
32456
|
+
const result = await iosCli2.info(udid);
|
|
32457
|
+
if (!result) {
|
|
32458
|
+
throw new Error("Failed to get device info");
|
|
32459
|
+
}
|
|
32460
|
+
return result.output[0];
|
|
32461
|
+
}
|
|
32462
|
+
async function wipeDevice(udid, iosCli2) {
|
|
32463
|
+
logTask(`Wiping device ${udid}`);
|
|
32464
|
+
await iosCli2.wipe(udid);
|
|
32465
|
+
}
|
|
32466
|
+
async function removeProfile(profileIdentifier, udid, iosCli2) {
|
|
32467
|
+
logTask(`Removing profile ${profileIdentifier} from device ${udid}`);
|
|
32468
|
+
await iosCli2.removeProfile(udid, profileIdentifier);
|
|
32469
|
+
}
|
|
32470
|
+
async function listProfiles(udid, iosCli2) {
|
|
32471
|
+
logTask(`Listing profiles for device ${udid}`);
|
|
32472
|
+
return iosCli2.listProfiles(udid);
|
|
32473
|
+
}
|
|
32399
32474
|
|
|
32400
32475
|
// src/utils/mdmClient.ts
|
|
32401
|
-
var
|
|
32476
|
+
var import_node_path4 = require("node:path");
|
|
32402
32477
|
|
|
32403
32478
|
// ../../node_modules/.pnpm/graphql-request@5.2.0_encoding@0.1.13_graphql@14.7.0/node_modules/graphql-request/build/esm/defaultJsonSerializer.js
|
|
32404
32479
|
var defaultJsonSerializer = {
|
|
@@ -33168,7 +33243,7 @@ function createMdmClient(config) {
|
|
|
33168
33243
|
}
|
|
33169
33244
|
async function createMDMClient() {
|
|
33170
33245
|
const endpoint = process.env.MDM_ENDPOINT;
|
|
33171
|
-
const credPath = (0,
|
|
33246
|
+
const credPath = (0, import_node_path4.join)(getResourcesPath(), "@clientLocal.json");
|
|
33172
33247
|
if (!endpoint || !credPath) {
|
|
33173
33248
|
return void 0;
|
|
33174
33249
|
}
|
|
@@ -33181,111 +33256,317 @@ async function createMDMClient() {
|
|
|
33181
33256
|
});
|
|
33182
33257
|
}
|
|
33183
33258
|
|
|
33184
|
-
// src/
|
|
33185
|
-
|
|
33186
|
-
|
|
33187
|
-
|
|
33188
|
-
|
|
33189
|
-
|
|
33190
|
-
|
|
33191
|
-
|
|
33192
|
-
|
|
33193
|
-
|
|
33194
|
-
|
|
33195
|
-
if (!(0, import_node_fs2.existsSync)(envPath)) {
|
|
33196
|
-
return null;
|
|
33197
|
-
}
|
|
33198
|
-
const absolutePath = (0, import_node_path4.isAbsolute)(envPath) ? envPath : (0, import_node_path4.join)(process.cwd(), envPath);
|
|
33199
|
-
if ((0, import_node_fs2.existsSync)(absolutePath)) {
|
|
33200
|
-
return absolutePath;
|
|
33259
|
+
// src/logic/actions/pair.ts
|
|
33260
|
+
async function isPaired(udid) {
|
|
33261
|
+
logTask(`Checking pairing status for ${udid}`);
|
|
33262
|
+
try {
|
|
33263
|
+
const result = await runIDeviceTool("idevicepair", ["-u", udid, "validate"]);
|
|
33264
|
+
if (!result) {
|
|
33265
|
+
return false;
|
|
33266
|
+
}
|
|
33267
|
+
return result.stdout.toLowerCase().includes("success");
|
|
33268
|
+
} catch {
|
|
33269
|
+
return false;
|
|
33201
33270
|
}
|
|
33202
|
-
return null;
|
|
33203
33271
|
}
|
|
33204
|
-
function
|
|
33205
|
-
|
|
33206
|
-
|
|
33207
|
-
|
|
33208
|
-
|
|
33209
|
-
if (envPlistDir) {
|
|
33210
|
-
return envPlistDir;
|
|
33272
|
+
async function trustDevice(udid, timeout2 = 6e4, onWaitingForTrust) {
|
|
33273
|
+
logTask(`Trusting device ${udid}`);
|
|
33274
|
+
if (await isPaired(udid)) {
|
|
33275
|
+
logInfo(`Device ${udid} is already trusted`);
|
|
33276
|
+
return true;
|
|
33211
33277
|
}
|
|
33212
|
-
|
|
33278
|
+
logInfo(`Initiating pairing for device ${udid}`);
|
|
33213
33279
|
try {
|
|
33214
|
-
|
|
33215
|
-
|
|
33216
|
-
|
|
33217
|
-
|
|
33218
|
-
baseDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
33280
|
+
const pairResult = await pair(udid);
|
|
33281
|
+
if (pairResult) {
|
|
33282
|
+
logInfo(`Device ${udid} paired successfully`);
|
|
33283
|
+
return true;
|
|
33219
33284
|
}
|
|
33220
|
-
} catch {
|
|
33221
|
-
|
|
33285
|
+
} catch (error) {
|
|
33286
|
+
logError(`Pairing failed with ${udid}: ${error}`);
|
|
33222
33287
|
}
|
|
33223
|
-
|
|
33224
|
-
|
|
33225
|
-
|
|
33288
|
+
logInfo("Please accept the trust dialog on the device...");
|
|
33289
|
+
onWaitingForTrust?.();
|
|
33290
|
+
try {
|
|
33291
|
+
await waitForPairing(udid, timeout2, 1e3);
|
|
33292
|
+
logInfo(`Device ${udid} is now trusted`);
|
|
33293
|
+
return true;
|
|
33294
|
+
} catch {
|
|
33295
|
+
logInfo(`Timeout waiting for trust acceptance on device ${udid}`);
|
|
33296
|
+
return false;
|
|
33226
33297
|
}
|
|
33227
|
-
|
|
33228
|
-
|
|
33229
|
-
|
|
33298
|
+
}
|
|
33299
|
+
async function waitForPairing(udid, timeout2 = 12e4, pollInterval = 1e3) {
|
|
33300
|
+
logTask(`Waiting for pairing on device ${udid}`);
|
|
33301
|
+
const startTime = Date.now();
|
|
33302
|
+
while (Date.now() - startTime < timeout2) {
|
|
33303
|
+
if (await isPaired(udid)) {
|
|
33304
|
+
logInfo(`Device ${udid} is now paired`);
|
|
33305
|
+
return true;
|
|
33306
|
+
}
|
|
33307
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
33230
33308
|
}
|
|
33231
|
-
|
|
33232
|
-
|
|
33233
|
-
|
|
33309
|
+
throw new Error(`Timeout waiting for device pairing after ${timeout2}ms`);
|
|
33310
|
+
}
|
|
33311
|
+
async function pair(udid) {
|
|
33312
|
+
logTask(`Initiating pairing for device ${udid}`);
|
|
33313
|
+
try {
|
|
33314
|
+
const result = await runIDeviceTool("idevicepair", ["-u", udid, "pair"]);
|
|
33315
|
+
if (!result) {
|
|
33316
|
+
return false;
|
|
33317
|
+
}
|
|
33318
|
+
return result.stdout.toLowerCase().includes("success");
|
|
33319
|
+
} catch (error) {
|
|
33320
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
33321
|
+
if (errorMsg.includes("Please accept the trust dialog")) {
|
|
33322
|
+
return false;
|
|
33323
|
+
}
|
|
33324
|
+
throw error;
|
|
33234
33325
|
}
|
|
33235
|
-
|
|
33236
|
-
|
|
33237
|
-
|
|
33326
|
+
}
|
|
33327
|
+
async function unpair(udid) {
|
|
33328
|
+
logTask(`Un-pairing device ${udid}`);
|
|
33329
|
+
try {
|
|
33330
|
+
const result = await runIDeviceTool("idevicepair", ["-u", udid, "unpair"]);
|
|
33331
|
+
if (!result) {
|
|
33332
|
+
return false;
|
|
33333
|
+
}
|
|
33334
|
+
return result.stdout.toLowerCase().includes("success");
|
|
33335
|
+
} catch {
|
|
33336
|
+
return false;
|
|
33238
33337
|
}
|
|
33239
|
-
|
|
33240
|
-
|
|
33241
|
-
|
|
33338
|
+
}
|
|
33339
|
+
|
|
33340
|
+
// src/logic/actions/install.ts
|
|
33341
|
+
async function installLocalApp(ipaPath, udid) {
|
|
33342
|
+
logTask(`Installing app ${ipaPath} on device ${udid}`);
|
|
33343
|
+
await runIDeviceTool("ideviceinstaller", ["-u", udid, "install", ipaPath]);
|
|
33344
|
+
}
|
|
33345
|
+
async function installManagedApp(ipaPath, udid, options) {
|
|
33346
|
+
await installLocalApp(ipaPath, udid);
|
|
33347
|
+
const client = await createMDMClient();
|
|
33348
|
+
logTask("Installing app via MDM for management takeover");
|
|
33349
|
+
if (client) {
|
|
33350
|
+
client.installApp(udid, options);
|
|
33242
33351
|
}
|
|
33243
|
-
return (0, import_node_path4.join)(process.cwd(), "src/plist");
|
|
33244
33352
|
}
|
|
33245
|
-
async function
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
|
|
33353
|
+
async function uninstallApp(bundleId, udid) {
|
|
33354
|
+
logTask(`Uninstalling app ${bundleId} from device ${udid}`);
|
|
33355
|
+
if (!await isPaired(udid)) {
|
|
33356
|
+
await waitForPairing(udid, 1e4);
|
|
33357
|
+
}
|
|
33358
|
+
await runIDeviceTool("ideviceinstaller", ["-u", udid, "uninstall", bundleId]);
|
|
33249
33359
|
}
|
|
33250
|
-
function
|
|
33251
|
-
|
|
33252
|
-
|
|
33253
|
-
|
|
33254
|
-
|
|
33255
|
-
|
|
33256
|
-
|
|
33257
|
-
|
|
33258
|
-
|
|
33259
|
-
result = result.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
33260
|
-
const value = variables[key];
|
|
33261
|
-
if (value === void 0 || value === null) {
|
|
33262
|
-
return "";
|
|
33263
|
-
}
|
|
33264
|
-
if (typeof value === "boolean") {
|
|
33265
|
-
return value ? "true" : "false";
|
|
33360
|
+
async function listApps(udid) {
|
|
33361
|
+
logTask(`Listing apps on device ${udid}`);
|
|
33362
|
+
if (!await isPaired(udid)) {
|
|
33363
|
+
await waitForPairing(udid, 1e4);
|
|
33364
|
+
}
|
|
33365
|
+
try {
|
|
33366
|
+
const result = await runIDeviceTool("ideviceinstaller", ["-u", udid, "list"]);
|
|
33367
|
+
if (!result) {
|
|
33368
|
+
return [];
|
|
33266
33369
|
}
|
|
33267
|
-
|
|
33268
|
-
|
|
33269
|
-
|
|
33370
|
+
const { stdout } = result;
|
|
33371
|
+
return parseAppList(stdout);
|
|
33372
|
+
} catch {
|
|
33373
|
+
return [];
|
|
33374
|
+
}
|
|
33270
33375
|
}
|
|
33271
|
-
function
|
|
33272
|
-
|
|
33376
|
+
async function isAppInstalled(bundleId, udid) {
|
|
33377
|
+
logTask(`Checking if app ${bundleId} is installed on device ${udid}`);
|
|
33378
|
+
const apps = await listApps(udid);
|
|
33379
|
+
return apps.some((app) => app.bundleId === bundleId);
|
|
33380
|
+
}
|
|
33381
|
+
async function launchApp(bundleId, _args, udid) {
|
|
33382
|
+
logTask(`Launching app ${bundleId} on device ${udid}`);
|
|
33273
33383
|
}
|
|
33274
33384
|
|
|
33275
|
-
// src/
|
|
33276
|
-
|
|
33277
|
-
|
|
33278
|
-
|
|
33279
|
-
|
|
33280
|
-
|
|
33281
|
-
|
|
33282
|
-
|
|
33283
|
-
|
|
33284
|
-
|
|
33285
|
-
|
|
33286
|
-
|
|
33287
|
-
|
|
33288
|
-
|
|
33385
|
+
// src/logic/actions/proxy.ts
|
|
33386
|
+
var import_node_child_process2 = require("node:child_process");
|
|
33387
|
+
var import_node_path5 = require("node:path");
|
|
33388
|
+
function startPortForward(localPort, devicePort, udid, startupTimeout = 500) {
|
|
33389
|
+
return new Promise((resolve2, reject) => {
|
|
33390
|
+
logTask(`Starting port forward ${localPort} -> ${devicePort} for device ${udid}`);
|
|
33391
|
+
const binPath = getResourcesBinPath();
|
|
33392
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
33393
|
+
const toolPath = binPath ? (0, import_node_path5.join)(binPath, `iproxy${ext}`) : `iproxy${ext}`;
|
|
33394
|
+
const spawnOptions = {
|
|
33395
|
+
windowsHide: true,
|
|
33396
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
33397
|
+
};
|
|
33398
|
+
if (binPath) {
|
|
33399
|
+
spawnOptions.cwd = binPath;
|
|
33400
|
+
}
|
|
33401
|
+
logInfo(`Spawning iproxy with path: ${toolPath}`);
|
|
33402
|
+
logInfo(`Arguments: ${[localPort.toString(), devicePort.toString(), "-u", udid].join(" ")}`);
|
|
33403
|
+
logInfo(`Options: ${JSON.stringify(spawnOptions)}`);
|
|
33404
|
+
const child = (0, import_node_child_process2.spawn)(
|
|
33405
|
+
toolPath,
|
|
33406
|
+
[localPort.toString(), devicePort.toString(), "-u", udid],
|
|
33407
|
+
spawnOptions
|
|
33408
|
+
);
|
|
33409
|
+
let hasResolved = false;
|
|
33410
|
+
let stderrOutput = "";
|
|
33411
|
+
child.stdout?.on("data", (data) => {
|
|
33412
|
+
logTask(`${data.toString()}`);
|
|
33413
|
+
});
|
|
33414
|
+
child.stderr?.on("data", (data) => {
|
|
33415
|
+
logError(`${data.toString()}`);
|
|
33416
|
+
const msg = data.toString();
|
|
33417
|
+
stderrOutput += msg;
|
|
33418
|
+
if (msg.toLowerCase().includes("error") && !hasResolved) {
|
|
33419
|
+
hasResolved = true;
|
|
33420
|
+
child.kill();
|
|
33421
|
+
reject(new Error(`Port forwarding failed: ${msg}`));
|
|
33422
|
+
}
|
|
33423
|
+
});
|
|
33424
|
+
child.on("error", (error) => {
|
|
33425
|
+
if (!hasResolved) {
|
|
33426
|
+
hasResolved = true;
|
|
33427
|
+
reject(new Error(`Failed to start iproxy: ${error.message}`));
|
|
33428
|
+
}
|
|
33429
|
+
});
|
|
33430
|
+
child.on("exit", (code) => {
|
|
33431
|
+
if (!hasResolved) {
|
|
33432
|
+
hasResolved = true;
|
|
33433
|
+
if (code !== 0 && code !== null) {
|
|
33434
|
+
reject(new Error(`iproxy exited with code ${code}: ${stderrOutput}`));
|
|
33435
|
+
}
|
|
33436
|
+
}
|
|
33437
|
+
});
|
|
33438
|
+
setTimeout(() => {
|
|
33439
|
+
if (!hasResolved) {
|
|
33440
|
+
hasResolved = true;
|
|
33441
|
+
logTask(`Port forward started: ${localPort} -> ${devicePort} for device ${udid}`);
|
|
33442
|
+
resolve2({
|
|
33443
|
+
result: {
|
|
33444
|
+
localPort,
|
|
33445
|
+
devicePort
|
|
33446
|
+
},
|
|
33447
|
+
process: child
|
|
33448
|
+
});
|
|
33449
|
+
}
|
|
33450
|
+
}, startupTimeout);
|
|
33451
|
+
});
|
|
33452
|
+
}
|
|
33453
|
+
function killPortForwardProcess(process2) {
|
|
33454
|
+
if (process2 && !process2.killed) {
|
|
33455
|
+
logTask("Killing port forward process");
|
|
33456
|
+
process2.kill();
|
|
33457
|
+
}
|
|
33458
|
+
}
|
|
33459
|
+
|
|
33460
|
+
// src/logic/activationFlow.ts
|
|
33461
|
+
var import_promises2 = require("node:fs/promises");
|
|
33462
|
+
var import_node_os2 = require("node:os");
|
|
33463
|
+
var import_node_path7 = require("node:path");
|
|
33464
|
+
|
|
33465
|
+
// src/utils/wifiProfile.ts
|
|
33466
|
+
var import_node_crypto2 = require("node:crypto");
|
|
33467
|
+
|
|
33468
|
+
// src/utils/templateLoader.ts
|
|
33469
|
+
var import_node_fs3 = require("node:fs");
|
|
33470
|
+
var import_promises = require("node:fs/promises");
|
|
33471
|
+
var import_node_path6 = require("node:path");
|
|
33472
|
+
var import_node_url = require("node:url");
|
|
33473
|
+
var import_meta = {};
|
|
33474
|
+
function resolveEnvPlistDir() {
|
|
33475
|
+
const envPath = (0, import_node_path6.join)(getResourcesPath(), "plist");
|
|
33476
|
+
if (!(0, import_node_fs3.existsSync)(envPath)) {
|
|
33477
|
+
return null;
|
|
33478
|
+
}
|
|
33479
|
+
const absolutePath = (0, import_node_path6.isAbsolute)(envPath) ? envPath : (0, import_node_path6.join)(process.cwd(), envPath);
|
|
33480
|
+
if ((0, import_node_fs3.existsSync)(absolutePath)) {
|
|
33481
|
+
return absolutePath;
|
|
33482
|
+
}
|
|
33483
|
+
return null;
|
|
33484
|
+
}
|
|
33485
|
+
function getPlistDir(overrideDir) {
|
|
33486
|
+
if (overrideDir) {
|
|
33487
|
+
return (0, import_node_path6.resolve)(overrideDir);
|
|
33488
|
+
}
|
|
33489
|
+
const envPlistDir = resolveEnvPlistDir();
|
|
33490
|
+
if (envPlistDir) {
|
|
33491
|
+
return envPlistDir;
|
|
33492
|
+
}
|
|
33493
|
+
let baseDir;
|
|
33494
|
+
try {
|
|
33495
|
+
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
33496
|
+
const currentFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
33497
|
+
baseDir = (0, import_node_path6.dirname)(currentFile);
|
|
33498
|
+
} else {
|
|
33499
|
+
baseDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
33500
|
+
}
|
|
33501
|
+
} catch {
|
|
33502
|
+
baseDir = process.cwd();
|
|
33503
|
+
}
|
|
33504
|
+
const sourcePlistDir = (0, import_node_path6.join)(baseDir, "../plist");
|
|
33505
|
+
if ((0, import_node_fs3.existsSync)(sourcePlistDir)) {
|
|
33506
|
+
return sourcePlistDir;
|
|
33507
|
+
}
|
|
33508
|
+
const distPlistDir = (0, import_node_path6.join)(baseDir, "../plist");
|
|
33509
|
+
if ((0, import_node_fs3.existsSync)(distPlistDir)) {
|
|
33510
|
+
return distPlistDir;
|
|
33511
|
+
}
|
|
33512
|
+
const distPlistDir2 = (0, import_node_path6.join)(baseDir, "../dist/plist");
|
|
33513
|
+
if ((0, import_node_fs3.existsSync)(distPlistDir2)) {
|
|
33514
|
+
return distPlistDir2;
|
|
33515
|
+
}
|
|
33516
|
+
const srcPlistFallback = (0, import_node_path6.join)(process.cwd(), "src/plist");
|
|
33517
|
+
if ((0, import_node_fs3.existsSync)(srcPlistFallback)) {
|
|
33518
|
+
return srcPlistFallback;
|
|
33519
|
+
}
|
|
33520
|
+
const distPlistFallback = (0, import_node_path6.join)(process.cwd(), "dist/plist");
|
|
33521
|
+
if ((0, import_node_fs3.existsSync)(distPlistFallback)) {
|
|
33522
|
+
return distPlistFallback;
|
|
33523
|
+
}
|
|
33524
|
+
return (0, import_node_path6.join)(process.cwd(), "src/plist");
|
|
33525
|
+
}
|
|
33526
|
+
async function loadTemplate(templateName, plistDir) {
|
|
33527
|
+
const templatePath = (0, import_node_path6.join)(getPlistDir(plistDir), templateName);
|
|
33528
|
+
const content = await (0, import_promises.readFile)(templatePath, "utf-8");
|
|
33529
|
+
return content;
|
|
33530
|
+
}
|
|
33531
|
+
function processTemplate(template, variables) {
|
|
33532
|
+
let result = template;
|
|
33533
|
+
result = result.replace(/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_match, key, content) => {
|
|
33534
|
+
const value = variables[key];
|
|
33535
|
+
if (value && value !== false && value !== "" && value !== null && value !== void 0) {
|
|
33536
|
+
return processTemplate(content, variables);
|
|
33537
|
+
}
|
|
33538
|
+
return "";
|
|
33539
|
+
});
|
|
33540
|
+
result = result.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
33541
|
+
const value = variables[key];
|
|
33542
|
+
if (value === void 0 || value === null) {
|
|
33543
|
+
return "";
|
|
33544
|
+
}
|
|
33545
|
+
if (typeof value === "boolean") {
|
|
33546
|
+
return value ? "true" : "false";
|
|
33547
|
+
}
|
|
33548
|
+
return escapeXml(String(value));
|
|
33549
|
+
});
|
|
33550
|
+
return result;
|
|
33551
|
+
}
|
|
33552
|
+
function escapeXml(str) {
|
|
33553
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
33554
|
+
}
|
|
33555
|
+
|
|
33556
|
+
// src/utils/wifiProfile.ts
|
|
33557
|
+
async function generateWifiProfile(config, options) {
|
|
33558
|
+
const {
|
|
33559
|
+
ssid,
|
|
33560
|
+
password,
|
|
33561
|
+
encryptionType = "WPA2",
|
|
33562
|
+
autoJoin = true,
|
|
33563
|
+
hiddenNetwork = false,
|
|
33564
|
+
organizationName = "MCE Systems",
|
|
33565
|
+
displayName = `WiFi - ${ssid}`,
|
|
33566
|
+
enterprise = false,
|
|
33567
|
+
username,
|
|
33568
|
+
eapType = "PEAP",
|
|
33569
|
+
acceptAnyCertificate = true
|
|
33289
33570
|
} = config;
|
|
33290
33571
|
const profileUuid = (0, import_node_crypto2.randomUUID)().toUpperCase();
|
|
33291
33572
|
const payloadUuid = (0, import_node_crypto2.randomUUID)().toUpperCase();
|
|
@@ -33396,252 +33677,23 @@ function parseWifiEapType(value) {
|
|
|
33396
33677
|
}
|
|
33397
33678
|
}
|
|
33398
33679
|
|
|
33399
|
-
// src/logic/
|
|
33400
|
-
var
|
|
33401
|
-
var
|
|
33402
|
-
|
|
33403
|
-
|
|
33404
|
-
|
|
33405
|
-
|
|
33406
|
-
|
|
33407
|
-
|
|
33408
|
-
let stdout = "";
|
|
33409
|
-
let stderr = "";
|
|
33410
|
-
child.stdout?.on("data", (data) => {
|
|
33411
|
-
stdout += data.toString();
|
|
33412
|
-
});
|
|
33413
|
-
child.stderr?.on("data", (data) => {
|
|
33414
|
-
stderr += data.toString();
|
|
33415
|
-
});
|
|
33416
|
-
child.on("error", (error) => {
|
|
33417
|
-
reject(error);
|
|
33418
|
-
});
|
|
33419
|
-
child.on("close", (code) => {
|
|
33420
|
-
console.log(`stdout: ${stdout}`);
|
|
33421
|
-
console.log(`stderr: ${stderr}`);
|
|
33422
|
-
console.log(`exitCode: ${code}`);
|
|
33423
|
-
const output = stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => safeParseJson(line)).filter((item) => Boolean(item));
|
|
33424
|
-
const logMessages = stderr.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => safeParseJson(line)).filter((item) => Boolean(item)).map((lineJson) => ({
|
|
33425
|
-
level: lineJson.level,
|
|
33426
|
-
msg: lineJson.msg,
|
|
33427
|
-
timestamp: lineJson.timestamp
|
|
33428
|
-
}));
|
|
33429
|
-
resolve2({
|
|
33430
|
-
command: iosBinaryPath,
|
|
33431
|
-
args,
|
|
33432
|
-
output,
|
|
33433
|
-
logMessages,
|
|
33434
|
-
exitCode: code ?? 0
|
|
33435
|
-
});
|
|
33436
|
-
});
|
|
33437
|
-
});
|
|
33438
|
-
}
|
|
33439
|
-
function safeParseJson(line) {
|
|
33440
|
-
try {
|
|
33441
|
-
const parsed = JSON.parse(line);
|
|
33442
|
-
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
33443
|
-
} catch {
|
|
33444
|
-
return void 0;
|
|
33445
|
-
}
|
|
33446
|
-
}
|
|
33447
|
-
function isRecord(value) {
|
|
33448
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
33449
|
-
}
|
|
33450
|
-
function getStringField(record, key) {
|
|
33451
|
-
const value = record[key];
|
|
33452
|
-
return typeof value === "string" ? value : void 0;
|
|
33453
|
-
}
|
|
33454
|
-
function getBooleanField(record, key) {
|
|
33455
|
-
const value = record[key];
|
|
33456
|
-
return typeof value === "boolean" ? value : void 0;
|
|
33457
|
-
}
|
|
33458
|
-
function getNumberField(record, key) {
|
|
33459
|
-
const value = record[key];
|
|
33460
|
-
return typeof value === "number" ? value : void 0;
|
|
33461
|
-
}
|
|
33462
|
-
function getRecordField(record, key) {
|
|
33463
|
-
const value = record[key];
|
|
33464
|
-
return isRecord(value) ? value : void 0;
|
|
33465
|
-
}
|
|
33466
|
-
function toProfileManifest(record) {
|
|
33467
|
-
if (!record) {
|
|
33468
|
-
return void 0;
|
|
33680
|
+
// src/logic/activationFlow.ts
|
|
33681
|
+
var DEFAULT_RETRIES = 150;
|
|
33682
|
+
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
33683
|
+
var ActivationFlow = class {
|
|
33684
|
+
iosCli;
|
|
33685
|
+
mdmClientPromise;
|
|
33686
|
+
constructor(iosCli2) {
|
|
33687
|
+
this.iosCli = iosCli2;
|
|
33688
|
+
this.mdmClientPromise = createMDMClient();
|
|
33469
33689
|
}
|
|
33470
|
-
|
|
33471
|
-
|
|
33472
|
-
|
|
33473
|
-
|
|
33474
|
-
|
|
33475
|
-
|
|
33476
|
-
|
|
33477
|
-
IsActive: isActive
|
|
33478
|
-
};
|
|
33479
|
-
}
|
|
33480
|
-
function toProfileMetadata(record) {
|
|
33481
|
-
if (!record) {
|
|
33482
|
-
return void 0;
|
|
33483
|
-
}
|
|
33484
|
-
const payloadDescription = getStringField(record, "PayloadDescription");
|
|
33485
|
-
const payloadDisplayName = getStringField(record, "PayloadDisplayName");
|
|
33486
|
-
const payloadRemovalDisallowed = getBooleanField(record, "PayloadRemovalDisallowed");
|
|
33487
|
-
const payloadUuid = getStringField(record, "PayloadUUID");
|
|
33488
|
-
const payloadVersion = getNumberField(record, "PayloadVersion");
|
|
33489
|
-
if (payloadDescription === void 0 && payloadDisplayName === void 0 && payloadRemovalDisallowed === void 0 && payloadUuid === void 0 && payloadVersion === void 0) {
|
|
33490
|
-
return void 0;
|
|
33491
|
-
}
|
|
33492
|
-
return {
|
|
33493
|
-
PayloadDescription: payloadDescription,
|
|
33494
|
-
PayloadDisplayName: payloadDisplayName,
|
|
33495
|
-
PayloadRemovalDisallowed: payloadRemovalDisallowed,
|
|
33496
|
-
PayloadUUID: payloadUuid,
|
|
33497
|
-
PayloadVersion: payloadVersion
|
|
33498
|
-
};
|
|
33499
|
-
}
|
|
33500
|
-
function toProfileListEntry(item) {
|
|
33501
|
-
const identifier = getStringField(item, "Identifier");
|
|
33502
|
-
const manifest = toProfileManifest(getRecordField(item, "Manifest"));
|
|
33503
|
-
const metadata = toProfileMetadata(getRecordField(item, "Metadata"));
|
|
33504
|
-
const status = getStringField(item, "Status");
|
|
33505
|
-
if (!identifier && !manifest && !metadata && !status) {
|
|
33506
|
-
return void 0;
|
|
33507
|
-
}
|
|
33508
|
-
return {
|
|
33509
|
-
Identifier: identifier,
|
|
33510
|
-
Manifest: manifest,
|
|
33511
|
-
Metadata: metadata,
|
|
33512
|
-
Status: status
|
|
33513
|
-
};
|
|
33514
|
-
}
|
|
33515
|
-
function parseProfileListOutput(output) {
|
|
33516
|
-
const profiles = [];
|
|
33517
|
-
for (const item of output) {
|
|
33518
|
-
if (Array.isArray(item)) {
|
|
33519
|
-
for (const entry2 of item) {
|
|
33520
|
-
if (!isRecord(entry2)) {
|
|
33521
|
-
continue;
|
|
33522
|
-
}
|
|
33523
|
-
const profileEntry = toProfileListEntry(entry2);
|
|
33524
|
-
if (profileEntry) {
|
|
33525
|
-
profiles.push(profileEntry);
|
|
33526
|
-
}
|
|
33527
|
-
}
|
|
33528
|
-
continue;
|
|
33529
|
-
}
|
|
33530
|
-
if (!isRecord(item)) {
|
|
33531
|
-
continue;
|
|
33532
|
-
}
|
|
33533
|
-
const entry = toProfileListEntry(item);
|
|
33534
|
-
if (entry) {
|
|
33535
|
-
profiles.push(entry);
|
|
33536
|
-
}
|
|
33537
|
-
}
|
|
33538
|
-
return profiles;
|
|
33539
|
-
}
|
|
33540
|
-
function createIosCli(iosBinaryPath) {
|
|
33541
|
-
return {
|
|
33542
|
-
async listDevices() {
|
|
33543
|
-
const raw = await runIosCommand(iosBinaryPath, ["list"]);
|
|
33544
|
-
const output = raw.output[0];
|
|
33545
|
-
return {
|
|
33546
|
-
udids: output.deviceList,
|
|
33547
|
-
raw
|
|
33548
|
-
};
|
|
33549
|
-
},
|
|
33550
|
-
async wipe(deviceId) {
|
|
33551
|
-
return runIosCommand(iosBinaryPath, ["erase", "--force", "--udid", deviceId]);
|
|
33552
|
-
},
|
|
33553
|
-
async installProfile(deviceId, profilePath) {
|
|
33554
|
-
return runIosCommand(iosBinaryPath, ["profile", "add", profilePath, "--udid", deviceId]);
|
|
33555
|
-
},
|
|
33556
|
-
async removeProfile(deviceId, profileName) {
|
|
33557
|
-
return runIosCommand(iosBinaryPath, ["profile", "remove", profileName, "--udid", deviceId]);
|
|
33558
|
-
},
|
|
33559
|
-
async skipSteps(deviceId) {
|
|
33560
|
-
const resourcesDir2 = getResourcesPath();
|
|
33561
|
-
return runIosCommand(iosBinaryPath, [
|
|
33562
|
-
"prepare",
|
|
33563
|
-
"--skip-all",
|
|
33564
|
-
`--certfile=${(0, import_node_path5.join)(resourcesDir2, "cert.der")}`,
|
|
33565
|
-
`--orgname=${process.env.ORGANIZATION_NAME}`,
|
|
33566
|
-
"--udid",
|
|
33567
|
-
deviceId
|
|
33568
|
-
]);
|
|
33569
|
-
},
|
|
33570
|
-
async activate(deviceId) {
|
|
33571
|
-
return runIosCommand(iosBinaryPath, ["activate", "--udid", deviceId]);
|
|
33572
|
-
},
|
|
33573
|
-
async pair(deviceId) {
|
|
33574
|
-
return runIosCommand(iosBinaryPath, ["pair", "--udid", deviceId]);
|
|
33575
|
-
},
|
|
33576
|
-
async forward(deviceId, fromPort, toPort) {
|
|
33577
|
-
return runIosCommand(iosBinaryPath, [
|
|
33578
|
-
"forward",
|
|
33579
|
-
`--port=${fromPort}:${toPort}`,
|
|
33580
|
-
"--udid",
|
|
33581
|
-
deviceId
|
|
33582
|
-
]);
|
|
33583
|
-
},
|
|
33584
|
-
async info(deviceId) {
|
|
33585
|
-
return runIosCommand(iosBinaryPath, ["info", "--udid", deviceId]);
|
|
33586
|
-
},
|
|
33587
|
-
async listProfiles(deviceId) {
|
|
33588
|
-
const raw = await runIosCommand(iosBinaryPath, ["profile", "list", "--udid", deviceId]);
|
|
33589
|
-
return {
|
|
33590
|
-
profiles: parseProfileListOutput(raw.output),
|
|
33591
|
-
raw
|
|
33592
|
-
};
|
|
33593
|
-
}
|
|
33594
|
-
};
|
|
33595
|
-
}
|
|
33596
|
-
|
|
33597
|
-
// src/logic/activationFlow.ts
|
|
33598
|
-
var DEFAULT_RETRIES = 150;
|
|
33599
|
-
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
33600
|
-
var MCE_MDM_PROFILE_PREFIX = "com.mce.mdm";
|
|
33601
|
-
var ActivationFlow = class {
|
|
33602
|
-
iosCli;
|
|
33603
|
-
mdmClientPromise;
|
|
33604
|
-
constructor() {
|
|
33605
|
-
const iosBinaryPath = resolveIosBinaryPath();
|
|
33606
|
-
if (!iosBinaryPath) {
|
|
33607
|
-
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
33608
|
-
}
|
|
33609
|
-
this.iosCli = createIosCli(iosBinaryPath);
|
|
33610
|
-
this.mdmClientPromise = createMDMClient();
|
|
33611
|
-
}
|
|
33612
|
-
async run(udid) {
|
|
33613
|
-
logTask(`Starting activation flow for device ${udid}`);
|
|
33614
|
-
const activationState = await this.getActivationState(udid);
|
|
33615
|
-
if (activationState === "Activated") {
|
|
33616
|
-
const isEnrolled = await this.isEnrolledToMceMdm(udid);
|
|
33617
|
-
if (isEnrolled) {
|
|
33618
|
-
logInfo("Device already activated and enrolled to MCE MDM. Skipping activation flow.");
|
|
33619
|
-
return async () => {
|
|
33620
|
-
};
|
|
33621
|
-
}
|
|
33622
|
-
} else if (activationState) {
|
|
33623
|
-
logInfo(`Activation state is ${activationState}, activating device`);
|
|
33624
|
-
await this.retryActivateCommand("activate device", () => this.iosCli.activate(udid));
|
|
33625
|
-
} else {
|
|
33626
|
-
await this.retryIosCommand("wipe", () => this.iosCli.wipe(udid));
|
|
33627
|
-
return void 0;
|
|
33628
|
-
}
|
|
33629
|
-
const wifiProfileIdentifier = await this.installWifiProfile(udid);
|
|
33630
|
-
await this.installMdmProfile(udid);
|
|
33631
|
-
await this.retryIosCommand("skip steps", () => this.iosCli.skipSteps(udid));
|
|
33632
|
-
return () => this.removeWifiProfile(udid, wifiProfileIdentifier);
|
|
33633
|
-
}
|
|
33634
|
-
async isEnrolledToMceMdm(udid) {
|
|
33635
|
-
const profiles = await this.listProfiles(udid);
|
|
33636
|
-
return profiles.some((profile) => isMceMdmProfile(profile));
|
|
33637
|
-
}
|
|
33638
|
-
async listProfiles(udid) {
|
|
33639
|
-
const result = await this.retry(
|
|
33640
|
-
"list profiles",
|
|
33641
|
-
() => this.iosCli.listProfiles(udid),
|
|
33642
|
-
(response) => response.raw.exitCode === 0
|
|
33643
|
-
);
|
|
33644
|
-
return result.profiles;
|
|
33690
|
+
async run(udid) {
|
|
33691
|
+
logTask(`Starting activation flow for device ${udid}`);
|
|
33692
|
+
await this.retryActivateCommand("activate device", () => this.iosCli.activate(udid));
|
|
33693
|
+
const wifiProfileIdentifier = await this.installWifiProfile(udid);
|
|
33694
|
+
await this.installMdmProfile(udid);
|
|
33695
|
+
await this.retryIosCommand("skip steps", () => this.iosCli.skipSteps(udid));
|
|
33696
|
+
return () => this.removeWifiProfile(udid, wifiProfileIdentifier);
|
|
33645
33697
|
}
|
|
33646
33698
|
async installWifiProfile(udid) {
|
|
33647
33699
|
const wifiProfile = await generateWifiProfileFromEnv();
|
|
@@ -33657,14 +33709,6 @@ var ActivationFlow = class {
|
|
|
33657
33709
|
await removeTempFile(wifiProfilePath, "wifi profile");
|
|
33658
33710
|
return wifiProfileIdentifier;
|
|
33659
33711
|
}
|
|
33660
|
-
async getActivationState(udid) {
|
|
33661
|
-
const infoResult = await this.retryIosCommand("device info", () => this.iosCli.info(udid));
|
|
33662
|
-
const activationState = getActivationState2(infoResult.output);
|
|
33663
|
-
if (!activationState) {
|
|
33664
|
-
logInfo("Activation state not found");
|
|
33665
|
-
}
|
|
33666
|
-
return activationState;
|
|
33667
|
-
}
|
|
33668
33712
|
async installMdmProfile(udid) {
|
|
33669
33713
|
const client = await this.requireMdmClient();
|
|
33670
33714
|
logTask("Installing MDM enrollment profile");
|
|
@@ -33706,7 +33750,7 @@ var ActivationFlow = class {
|
|
|
33706
33750
|
async retryActivateCommand(label, command) {
|
|
33707
33751
|
return this.retry(label, command, (result) => isActivationSuccess(result));
|
|
33708
33752
|
}
|
|
33709
|
-
async retry(label, command, isSuccess, retries = DEFAULT_RETRIES, retryDelayMs = DEFAULT_RETRY_DELAY_MS) {
|
|
33753
|
+
async retry(label, command, isSuccess, retries = +(process.env.DEFAULT_RETRIES ?? DEFAULT_RETRIES), retryDelayMs = +(process.env.DEFAULT_RETRY_DELAY_MS ?? DEFAULT_RETRY_DELAY_MS)) {
|
|
33710
33754
|
for (let attempt = 0; attempt < retries; attempt += 1) {
|
|
33711
33755
|
try {
|
|
33712
33756
|
const result = await command();
|
|
@@ -33724,7 +33768,7 @@ var ActivationFlow = class {
|
|
|
33724
33768
|
}
|
|
33725
33769
|
};
|
|
33726
33770
|
async function saveProfileToTemp(profile, prefix) {
|
|
33727
|
-
const tempFilePath = (0,
|
|
33771
|
+
const tempFilePath = (0, import_node_path7.join)((0, import_node_os2.tmpdir)(), `mce_${prefix}_${Date.now()}.mobileconfig`);
|
|
33728
33772
|
await (0, import_promises2.writeFile)(tempFilePath, profile, "utf-8");
|
|
33729
33773
|
logInfo(`Profile saved to: ${tempFilePath}`);
|
|
33730
33774
|
return tempFilePath;
|
|
@@ -33754,475 +33798,226 @@ function getProfileIdentifierFromProfile(profile) {
|
|
|
33754
33798
|
function resolveIosBinaryPath() {
|
|
33755
33799
|
const platform = process.platform;
|
|
33756
33800
|
const binaryName = platform === "win32" ? "ios.exe" : "ios";
|
|
33757
|
-
return (0,
|
|
33758
|
-
}
|
|
33759
|
-
function getActivationState2(output) {
|
|
33760
|
-
if (!Array.isArray(output) || output.length === 0) {
|
|
33761
|
-
return void 0;
|
|
33762
|
-
}
|
|
33763
|
-
for (const item of output) {
|
|
33764
|
-
if (!item || typeof item !== "object") {
|
|
33765
|
-
continue;
|
|
33766
|
-
}
|
|
33767
|
-
if ("ActivationState" in item) {
|
|
33768
|
-
const value = item.ActivationState;
|
|
33769
|
-
return typeof value === "string" ? value : void 0;
|
|
33770
|
-
}
|
|
33771
|
-
if ("activationState" in item) {
|
|
33772
|
-
const value = item.activationState;
|
|
33773
|
-
return typeof value === "string" ? value : void 0;
|
|
33774
|
-
}
|
|
33775
|
-
}
|
|
33776
|
-
return void 0;
|
|
33801
|
+
return (0, import_node_path7.join)(getResourcesBinPath(), binaryName);
|
|
33777
33802
|
}
|
|
33778
33803
|
function isActivationSuccess(result) {
|
|
33779
33804
|
if (result.exitCode !== 0) {
|
|
33780
33805
|
return false;
|
|
33781
33806
|
}
|
|
33782
|
-
if (result.logMessages.length === 0) {
|
|
33783
|
-
return true;
|
|
33784
|
-
}
|
|
33785
|
-
const hasFatal = result.logMessages.some((log) => log.level === "error");
|
|
33786
|
-
return !hasFatal;
|
|
33787
|
-
}
|
|
33788
|
-
function isMceMdmProfile(profile) {
|
|
33789
|
-
const identifier = profile.Identifier;
|
|
33790
|
-
if (!identifier) {
|
|
33791
|
-
return false;
|
|
33792
|
-
}
|
|
33793
|
-
if (!identifier.startsWith(MCE_MDM_PROFILE_PREFIX)) {
|
|
33794
|
-
return false;
|
|
33795
|
-
}
|
|
33796
|
-
if (profile.Manifest?.IsActive === false) {
|
|
33797
|
-
return false;
|
|
33798
|
-
}
|
|
33799
|
-
return true;
|
|
33800
|
-
}
|
|
33801
|
-
|
|
33802
|
-
// src/logic/dataParser.ts
|
|
33803
|
-
function parsePlistOutput(output) {
|
|
33804
|
-
const result = {};
|
|
33805
|
-
const lines = output.split("\n");
|
|
33806
|
-
for (const line of lines) {
|
|
33807
|
-
const colonIndex = line.indexOf(":");
|
|
33808
|
-
if (colonIndex > 0) {
|
|
33809
|
-
const key = line.substring(0, colonIndex).trim();
|
|
33810
|
-
const value = line.substring(colonIndex + 1).trim();
|
|
33811
|
-
result[key] = value;
|
|
33812
|
-
}
|
|
33813
|
-
}
|
|
33814
|
-
return result;
|
|
33815
|
-
}
|
|
33816
|
-
function parseAppList(output) {
|
|
33817
|
-
const apps = [];
|
|
33818
|
-
const lines = output.trim().split("\n");
|
|
33819
|
-
for (const line of lines) {
|
|
33820
|
-
const match = line.match(/^([^,]+),\s*([^,]+),\s*"?([^"]+)"?/);
|
|
33821
|
-
if (match) {
|
|
33822
|
-
apps.push({
|
|
33823
|
-
bundleId: match[1].trim(),
|
|
33824
|
-
version: match[2].trim(),
|
|
33825
|
-
displayName: match[3].trim(),
|
|
33826
|
-
bundleVersion: ""
|
|
33827
|
-
});
|
|
33828
|
-
}
|
|
33829
|
-
}
|
|
33830
|
-
return apps;
|
|
33831
|
-
}
|
|
33832
|
-
|
|
33833
|
-
// src/logic/actions/device.ts
|
|
33834
|
-
async function getDeviceInfo(udid) {
|
|
33835
|
-
logTask(`Getting device info for ${udid}`);
|
|
33836
|
-
const result = await runIDeviceTool("ideviceinfo", ["-u", udid]);
|
|
33837
|
-
if (!result) {
|
|
33838
|
-
throw new Error("Failed to get device info");
|
|
33839
|
-
}
|
|
33840
|
-
const props = parsePlistOutput(result.stdout);
|
|
33841
|
-
return {
|
|
33842
|
-
deviceName: props.DeviceName || "",
|
|
33843
|
-
productType: props.ProductType || "",
|
|
33844
|
-
productVersion: props.ProductVersion || "",
|
|
33845
|
-
buildVersion: props.BuildVersion || "",
|
|
33846
|
-
serialNumber: props.SerialNumber || "",
|
|
33847
|
-
udid: props.UniqueDeviceID || udid,
|
|
33848
|
-
wifiAddress: props.WiFiAddress || "",
|
|
33849
|
-
bluetoothAddress: props.BluetoothAddress || "",
|
|
33850
|
-
phoneNumber: props.PhoneNumber || "",
|
|
33851
|
-
cpuArchitecture: props.CPUArchitecture || "",
|
|
33852
|
-
hardwareModel: props.HardwareModel || "",
|
|
33853
|
-
modelNumber: props.ModelNumber || "",
|
|
33854
|
-
regionInfo: props.RegionInfo || "",
|
|
33855
|
-
timeZone: props.TimeZone || "",
|
|
33856
|
-
uniqueChipID: props.UniqueChipID || "",
|
|
33857
|
-
isPaired: true
|
|
33858
|
-
// If we can get info, device is paired
|
|
33859
|
-
};
|
|
33860
|
-
}
|
|
33861
|
-
async function info(udid) {
|
|
33862
|
-
logTask(`Getting device info for ${udid}`);
|
|
33863
|
-
const iosBinaryPath = resolveIosBinaryPath();
|
|
33864
|
-
if (!iosBinaryPath) {
|
|
33865
|
-
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
33866
|
-
}
|
|
33867
|
-
const iosCli = createIosCli(iosBinaryPath);
|
|
33868
|
-
const result = await iosCli.info(udid);
|
|
33869
|
-
if (!result) {
|
|
33870
|
-
throw new Error("Failed to get device info");
|
|
33871
|
-
}
|
|
33872
|
-
return result.output[0];
|
|
33873
|
-
}
|
|
33874
|
-
|
|
33875
|
-
// src/logic/actions/pair.ts
|
|
33876
|
-
async function isPaired(udid) {
|
|
33877
|
-
logTask(`Checking pairing status for ${udid}`);
|
|
33878
|
-
try {
|
|
33879
|
-
const result = await runIDeviceTool("idevicepair", ["-u", udid, "validate"]);
|
|
33880
|
-
if (!result) {
|
|
33881
|
-
return false;
|
|
33882
|
-
}
|
|
33883
|
-
return result.stdout.toLowerCase().includes("success");
|
|
33884
|
-
} catch {
|
|
33885
|
-
return false;
|
|
33886
|
-
}
|
|
33887
|
-
}
|
|
33888
|
-
async function trustDevice(udid, timeout2 = 6e4, onWaitingForTrust) {
|
|
33889
|
-
logTask(`Trusting device ${udid}`);
|
|
33890
|
-
if (await isPaired(udid)) {
|
|
33891
|
-
logInfo(`Device ${udid} is already trusted`);
|
|
33892
|
-
return true;
|
|
33893
|
-
}
|
|
33894
|
-
logInfo(`Initiating pairing for device ${udid}`);
|
|
33895
|
-
try {
|
|
33896
|
-
const pairResult = await pair(udid);
|
|
33897
|
-
if (pairResult) {
|
|
33898
|
-
logInfo(`Device ${udid} paired successfully`);
|
|
33899
|
-
return true;
|
|
33900
|
-
}
|
|
33901
|
-
} catch (error) {
|
|
33902
|
-
logError(`Pairing failed with ${udid}: ${error}`);
|
|
33903
|
-
}
|
|
33904
|
-
logInfo("Please accept the trust dialog on the device...");
|
|
33905
|
-
onWaitingForTrust?.();
|
|
33906
|
-
try {
|
|
33907
|
-
await waitForPairing(udid, timeout2, 1e3);
|
|
33908
|
-
logInfo(`Device ${udid} is now trusted`);
|
|
33909
|
-
return true;
|
|
33910
|
-
} catch {
|
|
33911
|
-
logInfo(`Timeout waiting for trust acceptance on device ${udid}`);
|
|
33912
|
-
return false;
|
|
33913
|
-
}
|
|
33914
|
-
}
|
|
33915
|
-
async function waitForPairing(udid, timeout2 = 12e4, pollInterval = 1e3) {
|
|
33916
|
-
logTask(`Waiting for pairing on device ${udid}`);
|
|
33917
|
-
const startTime = Date.now();
|
|
33918
|
-
while (Date.now() - startTime < timeout2) {
|
|
33919
|
-
if (await isPaired(udid)) {
|
|
33920
|
-
logInfo(`Device ${udid} is now paired`);
|
|
33921
|
-
return true;
|
|
33922
|
-
}
|
|
33923
|
-
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
33924
|
-
}
|
|
33925
|
-
throw new Error(`Timeout waiting for device pairing after ${timeout2}ms`);
|
|
33926
|
-
}
|
|
33927
|
-
async function pair(udid) {
|
|
33928
|
-
logTask(`Initiating pairing for device ${udid}`);
|
|
33929
|
-
try {
|
|
33930
|
-
const result = await runIDeviceTool("idevicepair", ["-u", udid, "pair"]);
|
|
33931
|
-
if (!result) {
|
|
33932
|
-
return false;
|
|
33933
|
-
}
|
|
33934
|
-
return result.stdout.toLowerCase().includes("success");
|
|
33935
|
-
} catch (error) {
|
|
33936
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
33937
|
-
if (errorMsg.includes("Please accept the trust dialog")) {
|
|
33938
|
-
return false;
|
|
33939
|
-
}
|
|
33940
|
-
throw error;
|
|
33941
|
-
}
|
|
33942
|
-
}
|
|
33943
|
-
async function unpair(udid) {
|
|
33944
|
-
logTask(`Un-pairing device ${udid}`);
|
|
33945
|
-
try {
|
|
33946
|
-
const result = await runIDeviceTool("idevicepair", ["-u", udid, "unpair"]);
|
|
33947
|
-
if (!result) {
|
|
33948
|
-
return false;
|
|
33949
|
-
}
|
|
33950
|
-
return result.stdout.toLowerCase().includes("success");
|
|
33951
|
-
} catch {
|
|
33952
|
-
return false;
|
|
33953
|
-
}
|
|
33954
|
-
}
|
|
33955
|
-
|
|
33956
|
-
// src/logic/actions/install.ts
|
|
33957
|
-
async function installLocalApp(ipaPath, udid) {
|
|
33958
|
-
logTask(`Installing app ${ipaPath} on device ${udid}`);
|
|
33959
|
-
await runIDeviceTool("ideviceinstaller", ["-u", udid, "install", ipaPath]);
|
|
33960
|
-
}
|
|
33961
|
-
async function installManagedApp(ipaPath, udid, options) {
|
|
33962
|
-
await installLocalApp(ipaPath, udid);
|
|
33963
|
-
const client = await createMDMClient();
|
|
33964
|
-
logTask("Installing app via MDM for management takeover");
|
|
33965
|
-
if (client) {
|
|
33966
|
-
client.installApp(udid, options);
|
|
33967
|
-
}
|
|
33968
|
-
}
|
|
33969
|
-
async function uninstallApp(bundleId, udid) {
|
|
33970
|
-
logTask(`Uninstalling app ${bundleId} from device ${udid}`);
|
|
33971
|
-
if (!await isPaired(udid)) {
|
|
33972
|
-
await waitForPairing(udid, 1e4);
|
|
33973
|
-
}
|
|
33974
|
-
await runIDeviceTool("ideviceinstaller", ["-u", udid, "uninstall", bundleId]);
|
|
33975
|
-
}
|
|
33976
|
-
async function listApps(udid) {
|
|
33977
|
-
logTask(`Listing apps on device ${udid}`);
|
|
33978
|
-
if (!await isPaired(udid)) {
|
|
33979
|
-
await waitForPairing(udid, 1e4);
|
|
33980
|
-
}
|
|
33981
|
-
try {
|
|
33982
|
-
const result = await runIDeviceTool("ideviceinstaller", ["-u", udid, "list"]);
|
|
33983
|
-
if (!result) {
|
|
33984
|
-
return [];
|
|
33985
|
-
}
|
|
33986
|
-
const { stdout } = result;
|
|
33987
|
-
return parseAppList(stdout);
|
|
33988
|
-
} catch {
|
|
33989
|
-
return [];
|
|
33990
|
-
}
|
|
33991
|
-
}
|
|
33992
|
-
async function isAppInstalled(bundleId, udid) {
|
|
33993
|
-
logTask(`Checking if app ${bundleId} is installed on device ${udid}`);
|
|
33994
|
-
const apps = await listApps(udid);
|
|
33995
|
-
return apps.some((app) => app.bundleId === bundleId);
|
|
33996
|
-
}
|
|
33997
|
-
async function wakeDevice(udid) {
|
|
33998
|
-
try {
|
|
33999
|
-
logInfo("Attempting to wake device screen...");
|
|
34000
|
-
await runIDeviceTool("ideviceinfo", ["-u", udid, "-k", "DeviceName"]);
|
|
34001
|
-
try {
|
|
34002
|
-
await runIDeviceTool("ideviceinfo", ["-u", udid, "-k", "ActivationState"]);
|
|
34003
|
-
} catch {
|
|
34004
|
-
}
|
|
34005
|
-
try {
|
|
34006
|
-
await runIDeviceTool("idevicepair", ["-u", udid, "validate"]);
|
|
34007
|
-
} catch {
|
|
34008
|
-
}
|
|
34009
|
-
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
34010
|
-
logInfo("Device wake attempt completed");
|
|
34011
|
-
} catch (error) {
|
|
34012
|
-
logInfo(
|
|
34013
|
-
`Device wake attempt failed (non-critical): ${error instanceof Error ? error.message : String(error)}`
|
|
34014
|
-
);
|
|
34015
|
-
}
|
|
34016
|
-
}
|
|
34017
|
-
async function launchApp(bundleId, args, udid) {
|
|
34018
|
-
logTask(`Launching app ${bundleId} on device ${udid}`);
|
|
34019
|
-
if (!await isPaired(udid)) {
|
|
34020
|
-
await waitForPairing(udid, 1e4);
|
|
34021
|
-
}
|
|
34022
|
-
const installed = await isAppInstalled(bundleId, udid);
|
|
34023
|
-
if (!installed) {
|
|
34024
|
-
throw new Error(
|
|
34025
|
-
`App "${bundleId}" is not installed on the device. Install the app first or verify the bundle identifier is correct.`
|
|
34026
|
-
);
|
|
34027
|
-
}
|
|
34028
|
-
await wakeDevice(udid);
|
|
34029
|
-
try {
|
|
34030
|
-
logInfo(`Attempting to launch ${bundleId} using idevicedebug...`);
|
|
34031
|
-
const result = await runIDeviceTool("idevicedebug", ["-u", udid, "run", bundleId, ...args]);
|
|
34032
|
-
const output = (result?.stdout ?? "") + (result?.stderr ?? "");
|
|
34033
|
-
if (output.trim()) {
|
|
34034
|
-
logInfo(`idevicedebug output: ${output.substring(0, 200)}`);
|
|
34035
|
-
}
|
|
34036
|
-
if (output.toLowerCase().includes("could not start") && output.toLowerCase().includes("debugserver")) {
|
|
34037
|
-
logInfo("idevicedebug requires debugserver, falling back to pymobiledevice3...");
|
|
34038
|
-
throw new Error("debugserver_not_available");
|
|
34039
|
-
}
|
|
34040
|
-
logInfo(`App ${bundleId} launched successfully using idevicedebug`);
|
|
34041
|
-
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
34042
|
-
return;
|
|
34043
|
-
} catch (error) {
|
|
34044
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
34045
|
-
if (errorMsg.includes("debugserver_not_available") || errorMsg.toLowerCase().includes("could not start") || errorMsg.toLowerCase().includes("debugserver")) {
|
|
34046
|
-
logInfo("idevicedebug failed, trying pymobiledevice3 for iOS 17+...");
|
|
34047
|
-
await launchAppWithPymobiledevice3(bundleId, args, udid);
|
|
34048
|
-
return;
|
|
34049
|
-
}
|
|
34050
|
-
throw error;
|
|
34051
|
-
}
|
|
34052
|
-
}
|
|
34053
|
-
async function launchAppWithPymobiledevice3(bundleId, args, udid) {
|
|
34054
|
-
logTask(`Launching app ${bundleId} using pymobiledevice3`);
|
|
34055
|
-
const { exec } = await import("node:child_process");
|
|
34056
|
-
const { promisify: promisify3 } = await import("node:util");
|
|
34057
|
-
const execAsync2 = promisify3(exec);
|
|
34058
|
-
try {
|
|
34059
|
-
let cmdArgs = [
|
|
34060
|
-
"-m",
|
|
34061
|
-
"pymobiledevice3",
|
|
34062
|
-
"developer",
|
|
34063
|
-
"dvt",
|
|
34064
|
-
"launch",
|
|
34065
|
-
"--udid",
|
|
34066
|
-
udid,
|
|
34067
|
-
"--kill-existing",
|
|
34068
|
-
// Kill existing instance to bring app to foreground
|
|
34069
|
-
bundleId,
|
|
34070
|
-
...args
|
|
34071
|
-
];
|
|
34072
|
-
let command = `python ${cmdArgs.map((a) => `"${a}"`).join(" ")}`;
|
|
34073
|
-
logInfo(`Executing: ${command}`);
|
|
34074
|
-
const result = await execAsync2(command, {
|
|
34075
|
-
windowsHide: true,
|
|
34076
|
-
encoding: "utf8",
|
|
34077
|
-
timeout: 3e4
|
|
34078
|
-
// 30 second timeout
|
|
34079
|
-
});
|
|
34080
|
-
const output = result.stdout.toString() + result.stderr.toString();
|
|
34081
|
-
if (output.trim()) {
|
|
34082
|
-
logInfo(`pymobiledevice3 output: ${output.substring(0, 200)}`);
|
|
34083
|
-
}
|
|
34084
|
-
if (output.toLowerCase().includes("developer mode") && output.toLowerCase().includes("not enabled")) {
|
|
34085
|
-
throw new Error(
|
|
34086
|
-
"Developer Mode is not enabled on the device.\nPlease enable it: Settings \u2192 Privacy & Security \u2192 Developer Mode \u2192 Enable"
|
|
34087
|
-
);
|
|
34088
|
-
}
|
|
34089
|
-
if (output.toLowerCase().includes("tunneld") && (output.toLowerCase().includes("unable to connect") || output.toLowerCase().includes("invalidserviceerror"))) {
|
|
34090
|
-
logInfo("Tunnel required, retrying with tunnel option...");
|
|
34091
|
-
cmdArgs = [
|
|
34092
|
-
"-m",
|
|
34093
|
-
"pymobiledevice3",
|
|
34094
|
-
"developer",
|
|
34095
|
-
"dvt",
|
|
34096
|
-
"launch",
|
|
34097
|
-
"--udid",
|
|
34098
|
-
udid,
|
|
34099
|
-
"--tunnel",
|
|
34100
|
-
udid,
|
|
34101
|
-
// Use UDID for tunnel
|
|
34102
|
-
"--kill-existing",
|
|
34103
|
-
bundleId,
|
|
34104
|
-
...args
|
|
34105
|
-
];
|
|
34106
|
-
command = `python ${cmdArgs.map((a) => `"${a}"`).join(" ")}`;
|
|
34107
|
-
logInfo(`Retrying with tunnel: ${command}`);
|
|
34108
|
-
try {
|
|
34109
|
-
const retryResult = await execAsync2(command, {
|
|
34110
|
-
windowsHide: true,
|
|
34111
|
-
encoding: "utf8",
|
|
34112
|
-
timeout: 3e4
|
|
34113
|
-
});
|
|
34114
|
-
const retryOutput = retryResult.stdout.toString() + retryResult.stderr.toString();
|
|
34115
|
-
if (retryOutput.trim()) {
|
|
34116
|
-
logInfo(`pymobiledevice3 retry output: ${retryOutput.substring(0, 200)}`);
|
|
34117
|
-
}
|
|
34118
|
-
if (retryOutput.toLowerCase().includes("tunneld") && retryOutput.toLowerCase().includes("unable to connect")) {
|
|
34119
|
-
throw new Error(
|
|
34120
|
-
"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."
|
|
34121
|
-
);
|
|
34122
|
-
}
|
|
34123
|
-
logInfo(`App ${bundleId} launched successfully using pymobiledevice3 with tunnel`);
|
|
34124
|
-
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
34125
|
-
return;
|
|
34126
|
-
} catch {
|
|
34127
|
-
throw new Error(
|
|
34128
|
-
"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."
|
|
34129
|
-
);
|
|
34130
|
-
}
|
|
34131
|
-
}
|
|
34132
|
-
if (output.toLowerCase().includes("not found") || output.toLowerCase().includes("command not found") || output.toLowerCase().includes("cannot find")) {
|
|
34133
|
-
throw new Error(
|
|
34134
|
-
"pymobiledevice3 is not installed.\nInstall it with: python -m pip install --user pymobiledevice3"
|
|
34135
|
-
);
|
|
34136
|
-
}
|
|
34137
|
-
if (output.toLowerCase().includes("error") && !output.toLowerCase().includes("warning")) {
|
|
34138
|
-
logInfo(`Warning: pymobiledevice3 reported errors: ${output.substring(0, 300)}`);
|
|
34139
|
-
}
|
|
34140
|
-
logInfo(`App ${bundleId} launched successfully using pymobiledevice3`);
|
|
34141
|
-
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
34142
|
-
} catch (error) {
|
|
34143
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
34144
|
-
if (errorMsg.includes("not found") || errorMsg.includes("command not found") || errorMsg.includes("cannot find") || errorMsg.includes("No module named")) {
|
|
34145
|
-
throw new Error(
|
|
34146
|
-
"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."
|
|
34147
|
-
);
|
|
34148
|
-
}
|
|
34149
|
-
throw error;
|
|
33807
|
+
if (result.logMessages.length === 0) {
|
|
33808
|
+
return true;
|
|
34150
33809
|
}
|
|
33810
|
+
const hasFatal = result.logMessages.some((log) => log.level === "error");
|
|
33811
|
+
return !hasFatal;
|
|
34151
33812
|
}
|
|
34152
33813
|
|
|
34153
|
-
// src/logic/
|
|
33814
|
+
// src/logic/iosCli.ts
|
|
34154
33815
|
var import_node_child_process3 = require("node:child_process");
|
|
34155
33816
|
var import_node_path8 = require("node:path");
|
|
34156
|
-
|
|
33817
|
+
var ios;
|
|
33818
|
+
function iosCli() {
|
|
33819
|
+
const iosBinaryPath = resolveIosBinaryPath();
|
|
33820
|
+
if (!iosBinaryPath) {
|
|
33821
|
+
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
33822
|
+
}
|
|
33823
|
+
return createIosCli(iosBinaryPath);
|
|
33824
|
+
}
|
|
33825
|
+
if (!ios) {
|
|
33826
|
+
ios = iosCli();
|
|
33827
|
+
}
|
|
33828
|
+
function runIosCommand(iosBinaryPath, args) {
|
|
34157
33829
|
return new Promise((resolve2, reject) => {
|
|
34158
|
-
|
|
34159
|
-
const binPath = getResourcesBinPath();
|
|
34160
|
-
const ext = process.platform === "win32" ? ".exe" : "";
|
|
34161
|
-
const toolPath = binPath ? (0, import_node_path8.join)(binPath, `iproxy${ext}`) : `iproxy${ext}`;
|
|
34162
|
-
const spawnOptions = {
|
|
33830
|
+
const child = (0, import_node_child_process3.spawn)(iosBinaryPath, args, {
|
|
34163
33831
|
windowsHide: true,
|
|
34164
33832
|
stdio: ["ignore", "pipe", "pipe"]
|
|
34165
|
-
};
|
|
34166
|
-
|
|
34167
|
-
|
|
34168
|
-
}
|
|
34169
|
-
logInfo(`Spawning iproxy with path: ${toolPath}`);
|
|
34170
|
-
logInfo(`Arguments: ${[localPort.toString(), devicePort.toString(), "-u", udid].join(" ")}`);
|
|
34171
|
-
logInfo(`Options: ${JSON.stringify(spawnOptions)}`);
|
|
34172
|
-
const child = (0, import_node_child_process3.spawn)(
|
|
34173
|
-
toolPath,
|
|
34174
|
-
[localPort.toString(), devicePort.toString(), "-u", udid],
|
|
34175
|
-
spawnOptions
|
|
34176
|
-
);
|
|
34177
|
-
let hasResolved = false;
|
|
34178
|
-
let stderrOutput = "";
|
|
33833
|
+
});
|
|
33834
|
+
let stdout = "";
|
|
33835
|
+
let stderr = "";
|
|
34179
33836
|
child.stdout?.on("data", (data) => {
|
|
34180
|
-
|
|
33837
|
+
stdout += data.toString();
|
|
34181
33838
|
});
|
|
34182
33839
|
child.stderr?.on("data", (data) => {
|
|
34183
|
-
|
|
34184
|
-
const msg = data.toString();
|
|
34185
|
-
stderrOutput += msg;
|
|
34186
|
-
if (msg.toLowerCase().includes("error") && !hasResolved) {
|
|
34187
|
-
hasResolved = true;
|
|
34188
|
-
child.kill();
|
|
34189
|
-
reject(new Error(`Port forwarding failed: ${msg}`));
|
|
34190
|
-
}
|
|
33840
|
+
stderr += data.toString();
|
|
34191
33841
|
});
|
|
34192
33842
|
child.on("error", (error) => {
|
|
34193
|
-
|
|
34194
|
-
hasResolved = true;
|
|
34195
|
-
reject(new Error(`Failed to start iproxy: ${error.message}`));
|
|
34196
|
-
}
|
|
33843
|
+
reject(error);
|
|
34197
33844
|
});
|
|
34198
|
-
child.on("
|
|
34199
|
-
|
|
34200
|
-
|
|
34201
|
-
|
|
34202
|
-
|
|
34203
|
-
|
|
34204
|
-
|
|
33845
|
+
child.on("close", (code) => {
|
|
33846
|
+
console.log(`stdout: ${stdout}`);
|
|
33847
|
+
console.log(`stderr: ${stderr}`);
|
|
33848
|
+
console.log(`exitCode: ${code}`);
|
|
33849
|
+
const output = stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => safeParseJson(line)).filter((item) => Boolean(item));
|
|
33850
|
+
const logMessages = stderr.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => safeParseJson(line)).filter((item) => Boolean(item)).map((lineJson) => ({
|
|
33851
|
+
level: lineJson.level,
|
|
33852
|
+
msg: lineJson.msg,
|
|
33853
|
+
timestamp: lineJson.timestamp
|
|
33854
|
+
}));
|
|
33855
|
+
resolve2({
|
|
33856
|
+
command: iosBinaryPath,
|
|
33857
|
+
args,
|
|
33858
|
+
output,
|
|
33859
|
+
logMessages,
|
|
33860
|
+
exitCode: code ?? 0
|
|
33861
|
+
});
|
|
34205
33862
|
});
|
|
34206
|
-
setTimeout(() => {
|
|
34207
|
-
if (!hasResolved) {
|
|
34208
|
-
hasResolved = true;
|
|
34209
|
-
logTask(`Port forward started: ${localPort} -> ${devicePort} for device ${udid}`);
|
|
34210
|
-
resolve2({
|
|
34211
|
-
result: {
|
|
34212
|
-
localPort,
|
|
34213
|
-
devicePort
|
|
34214
|
-
},
|
|
34215
|
-
process: child
|
|
34216
|
-
});
|
|
34217
|
-
}
|
|
34218
|
-
}, startupTimeout);
|
|
34219
33863
|
});
|
|
34220
33864
|
}
|
|
34221
|
-
function
|
|
34222
|
-
|
|
34223
|
-
|
|
34224
|
-
|
|
33865
|
+
function safeParseJson(line) {
|
|
33866
|
+
try {
|
|
33867
|
+
const parsed = JSON.parse(line);
|
|
33868
|
+
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
33869
|
+
} catch {
|
|
33870
|
+
return void 0;
|
|
33871
|
+
}
|
|
33872
|
+
}
|
|
33873
|
+
function isRecord(value) {
|
|
33874
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
33875
|
+
}
|
|
33876
|
+
function getStringField(record, key) {
|
|
33877
|
+
const value = record[key];
|
|
33878
|
+
return typeof value === "string" ? value : void 0;
|
|
33879
|
+
}
|
|
33880
|
+
function getBooleanField(record, key) {
|
|
33881
|
+
const value = record[key];
|
|
33882
|
+
return typeof value === "boolean" ? value : void 0;
|
|
33883
|
+
}
|
|
33884
|
+
function getNumberField(record, key) {
|
|
33885
|
+
const value = record[key];
|
|
33886
|
+
return typeof value === "number" ? value : void 0;
|
|
33887
|
+
}
|
|
33888
|
+
function getRecordField(record, key) {
|
|
33889
|
+
const value = record[key];
|
|
33890
|
+
return isRecord(value) ? value : void 0;
|
|
33891
|
+
}
|
|
33892
|
+
function toProfileManifest(record) {
|
|
33893
|
+
if (!record) {
|
|
33894
|
+
return void 0;
|
|
33895
|
+
}
|
|
33896
|
+
const description = getStringField(record, "Description");
|
|
33897
|
+
const isActive = getBooleanField(record, "IsActive");
|
|
33898
|
+
if (description === void 0 && isActive === void 0) {
|
|
33899
|
+
return void 0;
|
|
33900
|
+
}
|
|
33901
|
+
return {
|
|
33902
|
+
Description: description,
|
|
33903
|
+
IsActive: isActive
|
|
33904
|
+
};
|
|
33905
|
+
}
|
|
33906
|
+
function toProfileMetadata(record) {
|
|
33907
|
+
if (!record) {
|
|
33908
|
+
return void 0;
|
|
33909
|
+
}
|
|
33910
|
+
const payloadDescription = getStringField(record, "PayloadDescription");
|
|
33911
|
+
const payloadDisplayName = getStringField(record, "PayloadDisplayName");
|
|
33912
|
+
const payloadRemovalDisallowed = getBooleanField(record, "PayloadRemovalDisallowed");
|
|
33913
|
+
const payloadUuid = getStringField(record, "PayloadUUID");
|
|
33914
|
+
const payloadVersion = getNumberField(record, "PayloadVersion");
|
|
33915
|
+
if (payloadDescription === void 0 && payloadDisplayName === void 0 && payloadRemovalDisallowed === void 0 && payloadUuid === void 0 && payloadVersion === void 0) {
|
|
33916
|
+
return void 0;
|
|
33917
|
+
}
|
|
33918
|
+
return {
|
|
33919
|
+
PayloadDescription: payloadDescription,
|
|
33920
|
+
PayloadDisplayName: payloadDisplayName,
|
|
33921
|
+
PayloadRemovalDisallowed: payloadRemovalDisallowed,
|
|
33922
|
+
PayloadUUID: payloadUuid,
|
|
33923
|
+
PayloadVersion: payloadVersion
|
|
33924
|
+
};
|
|
33925
|
+
}
|
|
33926
|
+
function toProfileListEntry(item) {
|
|
33927
|
+
const identifier = getStringField(item, "Identifier");
|
|
33928
|
+
const manifest = toProfileManifest(getRecordField(item, "Manifest"));
|
|
33929
|
+
const metadata = toProfileMetadata(getRecordField(item, "Metadata"));
|
|
33930
|
+
const status = getStringField(item, "Status");
|
|
33931
|
+
if (!identifier && !manifest && !metadata && !status) {
|
|
33932
|
+
return void 0;
|
|
33933
|
+
}
|
|
33934
|
+
return {
|
|
33935
|
+
Identifier: identifier,
|
|
33936
|
+
Manifest: manifest,
|
|
33937
|
+
Metadata: metadata,
|
|
33938
|
+
Status: status
|
|
33939
|
+
};
|
|
33940
|
+
}
|
|
33941
|
+
function parseProfileListOutput(output) {
|
|
33942
|
+
const profiles = [];
|
|
33943
|
+
for (const item of output) {
|
|
33944
|
+
if (Array.isArray(item)) {
|
|
33945
|
+
for (const entry2 of item) {
|
|
33946
|
+
if (!isRecord(entry2)) {
|
|
33947
|
+
continue;
|
|
33948
|
+
}
|
|
33949
|
+
const profileEntry = toProfileListEntry(entry2);
|
|
33950
|
+
if (profileEntry) {
|
|
33951
|
+
profiles.push(profileEntry);
|
|
33952
|
+
}
|
|
33953
|
+
}
|
|
33954
|
+
continue;
|
|
33955
|
+
}
|
|
33956
|
+
if (!isRecord(item)) {
|
|
33957
|
+
continue;
|
|
33958
|
+
}
|
|
33959
|
+
const entry = toProfileListEntry(item);
|
|
33960
|
+
if (entry) {
|
|
33961
|
+
profiles.push(entry);
|
|
33962
|
+
}
|
|
34225
33963
|
}
|
|
33964
|
+
return profiles;
|
|
33965
|
+
}
|
|
33966
|
+
function createIosCli(iosBinaryPath) {
|
|
33967
|
+
return {
|
|
33968
|
+
async listDevices() {
|
|
33969
|
+
const raw = await runIosCommand(iosBinaryPath, ["list"]);
|
|
33970
|
+
const output = raw.output[0];
|
|
33971
|
+
return {
|
|
33972
|
+
udids: output.deviceList,
|
|
33973
|
+
raw
|
|
33974
|
+
};
|
|
33975
|
+
},
|
|
33976
|
+
async wipe(deviceId) {
|
|
33977
|
+
return runIosCommand(iosBinaryPath, ["erase", "--force", "--udid", deviceId]);
|
|
33978
|
+
},
|
|
33979
|
+
async installProfile(deviceId, profilePath) {
|
|
33980
|
+
return runIosCommand(iosBinaryPath, ["profile", "add", profilePath, "--udid", deviceId]);
|
|
33981
|
+
},
|
|
33982
|
+
async removeProfile(deviceId, profileName) {
|
|
33983
|
+
return runIosCommand(iosBinaryPath, ["profile", "remove", profileName, "--udid", deviceId]);
|
|
33984
|
+
},
|
|
33985
|
+
async skipSteps(deviceId) {
|
|
33986
|
+
const resourcesDir2 = getResourcesPath();
|
|
33987
|
+
return runIosCommand(iosBinaryPath, [
|
|
33988
|
+
"prepare",
|
|
33989
|
+
"--skip-all",
|
|
33990
|
+
`--certfile=${(0, import_node_path8.join)(resourcesDir2, "cert.der")}`,
|
|
33991
|
+
`--orgname=${process.env.ORGANIZATION_NAME}`,
|
|
33992
|
+
"--udid",
|
|
33993
|
+
deviceId
|
|
33994
|
+
]);
|
|
33995
|
+
},
|
|
33996
|
+
async activate(deviceId) {
|
|
33997
|
+
return runIosCommand(iosBinaryPath, ["activate", "--udid", deviceId]);
|
|
33998
|
+
},
|
|
33999
|
+
async pair(deviceId) {
|
|
34000
|
+
return runIosCommand(iosBinaryPath, ["pair", "--udid", deviceId]);
|
|
34001
|
+
},
|
|
34002
|
+
async forward(deviceId, fromPort, toPort) {
|
|
34003
|
+
return runIosCommand(iosBinaryPath, [
|
|
34004
|
+
"forward",
|
|
34005
|
+
`--port=${fromPort}:${toPort}`,
|
|
34006
|
+
"--udid",
|
|
34007
|
+
deviceId
|
|
34008
|
+
]);
|
|
34009
|
+
},
|
|
34010
|
+
async info(deviceId) {
|
|
34011
|
+
return runIosCommand(iosBinaryPath, ["info", "--udid", deviceId]);
|
|
34012
|
+
},
|
|
34013
|
+
async listProfiles(deviceId) {
|
|
34014
|
+
const raw = await runIosCommand(iosBinaryPath, ["profile", "list", "--udid", deviceId]);
|
|
34015
|
+
return {
|
|
34016
|
+
profiles: parseProfileListOutput(raw.output),
|
|
34017
|
+
raw
|
|
34018
|
+
};
|
|
34019
|
+
}
|
|
34020
|
+
};
|
|
34226
34021
|
}
|
|
34227
34022
|
|
|
34228
34023
|
// src/logic/appleDeviceKit.ts
|
|
@@ -34233,11 +34028,17 @@ var AppleDeviceKit = class {
|
|
|
34233
34028
|
logInfo(
|
|
34234
34029
|
`AppleDeviceKit initialized for device: ${this.deviceId}, logical port: ${this.logicalPort}`
|
|
34235
34030
|
);
|
|
34031
|
+
const iosBinaryPath = resolveIosBinaryPath();
|
|
34032
|
+
if (!iosBinaryPath) {
|
|
34033
|
+
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
34034
|
+
}
|
|
34035
|
+
this.iosCli = createIosCli(iosBinaryPath);
|
|
34236
34036
|
}
|
|
34237
34037
|
deviceId;
|
|
34238
34038
|
proxyProcess = null;
|
|
34239
34039
|
localDevicePort = null;
|
|
34240
34040
|
isDisposed = false;
|
|
34041
|
+
iosCli;
|
|
34241
34042
|
static setResourcesDir(dir) {
|
|
34242
34043
|
setResourcesDir(dir);
|
|
34243
34044
|
}
|
|
@@ -34258,7 +34059,7 @@ var AppleDeviceKit = class {
|
|
|
34258
34059
|
}
|
|
34259
34060
|
async info() {
|
|
34260
34061
|
this.ensureNotDisposed();
|
|
34261
|
-
return info(this.deviceId);
|
|
34062
|
+
return info(this.deviceId, this.iosCli);
|
|
34262
34063
|
}
|
|
34263
34064
|
/**
|
|
34264
34065
|
* Check if device is paired/trusted with this computer
|
|
@@ -34406,6 +34207,18 @@ var AppleDeviceKit = class {
|
|
|
34406
34207
|
this.ensureNotDisposed();
|
|
34407
34208
|
return getActivationState(this.deviceId);
|
|
34408
34209
|
}
|
|
34210
|
+
/**
|
|
34211
|
+
* Wipe the device
|
|
34212
|
+
*/
|
|
34213
|
+
async wipe() {
|
|
34214
|
+
return wipeDevice(this.deviceId, this.iosCli);
|
|
34215
|
+
}
|
|
34216
|
+
async removeProfile(profileIdentifier) {
|
|
34217
|
+
return removeProfile(profileIdentifier, this.deviceId, this.iosCli);
|
|
34218
|
+
}
|
|
34219
|
+
async listProfiles() {
|
|
34220
|
+
return listProfiles(this.deviceId, this.iosCli);
|
|
34221
|
+
}
|
|
34409
34222
|
/**
|
|
34410
34223
|
* Activate the device using the activation flow.
|
|
34411
34224
|
*
|
|
@@ -34417,7 +34230,7 @@ var AppleDeviceKit = class {
|
|
|
34417
34230
|
*/
|
|
34418
34231
|
async activate() {
|
|
34419
34232
|
this.ensureNotDisposed();
|
|
34420
|
-
const flow = new ActivationFlow();
|
|
34233
|
+
const flow = new ActivationFlow(this.iosCli);
|
|
34421
34234
|
return await flow.run(this.deviceId);
|
|
34422
34235
|
}
|
|
34423
34236
|
/**
|