@mcesystems/apple-kit 1.0.61 → 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 +606 -619
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +605 -618
- package/dist/index.mjs.map +4 -4
- package/dist/types/logic/actions/device.d.ts +5 -5
- package/dist/types/logic/actions/device.d.ts.map +1 -1
- package/dist/types/logic/activationFlow.d.ts +2 -1
- package/dist/types/logic/activationFlow.d.ts.map +1 -1
- package/dist/types/logic/appleDeviceKit.d.ts +1 -0
- package/dist/types/logic/appleDeviceKit.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -32380,16 +32380,91 @@ async function getActivationState(udid) {
|
|
|
32380
32380
|
}
|
|
32381
32381
|
|
|
32382
32382
|
// src/logic/actions/device.ts
|
|
32383
|
-
import { existsSync as
|
|
32384
|
-
import { join as
|
|
32383
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
32384
|
+
import { join as join3 } from "node:path";
|
|
32385
32385
|
|
|
32386
|
-
// src/logic/
|
|
32387
|
-
|
|
32388
|
-
|
|
32389
|
-
|
|
32386
|
+
// src/logic/dataParser.ts
|
|
32387
|
+
function parsePlistOutput(output) {
|
|
32388
|
+
const result = {};
|
|
32389
|
+
const lines = output.split("\n");
|
|
32390
|
+
for (const line of lines) {
|
|
32391
|
+
const colonIndex = line.indexOf(":");
|
|
32392
|
+
if (colonIndex > 0) {
|
|
32393
|
+
const key = line.substring(0, colonIndex).trim();
|
|
32394
|
+
const value = line.substring(colonIndex + 1).trim();
|
|
32395
|
+
result[key] = value;
|
|
32396
|
+
}
|
|
32397
|
+
}
|
|
32398
|
+
return result;
|
|
32399
|
+
}
|
|
32400
|
+
function parseAppList(output) {
|
|
32401
|
+
const apps = [];
|
|
32402
|
+
const lines = output.trim().split("\n");
|
|
32403
|
+
for (const line of lines) {
|
|
32404
|
+
const match = line.match(/^([^,]+),\s*([^,]+),\s*"?([^"]+)"?/);
|
|
32405
|
+
if (match) {
|
|
32406
|
+
apps.push({
|
|
32407
|
+
bundleId: match[1].trim(),
|
|
32408
|
+
version: match[2].trim(),
|
|
32409
|
+
displayName: match[3].trim(),
|
|
32410
|
+
bundleVersion: ""
|
|
32411
|
+
});
|
|
32412
|
+
}
|
|
32413
|
+
}
|
|
32414
|
+
return apps;
|
|
32415
|
+
}
|
|
32416
|
+
|
|
32417
|
+
// src/logic/actions/device.ts
|
|
32418
|
+
async function getDeviceInfo(udid) {
|
|
32419
|
+
logTask(`Getting device info for ${udid}`);
|
|
32420
|
+
const result = await runIDeviceTool("ideviceinfo", ["-u", udid]);
|
|
32421
|
+
if (!result) {
|
|
32422
|
+
throw new Error("Failed to get device info");
|
|
32423
|
+
}
|
|
32424
|
+
const props = parsePlistOutput(result.stdout);
|
|
32425
|
+
return {
|
|
32426
|
+
deviceName: props.DeviceName || "",
|
|
32427
|
+
productType: props.ProductType || "",
|
|
32428
|
+
productVersion: props.ProductVersion || "",
|
|
32429
|
+
buildVersion: props.BuildVersion || "",
|
|
32430
|
+
serialNumber: props.SerialNumber || "",
|
|
32431
|
+
udid: props.UniqueDeviceID || udid,
|
|
32432
|
+
wifiAddress: props.WiFiAddress || "",
|
|
32433
|
+
bluetoothAddress: props.BluetoothAddress || "",
|
|
32434
|
+
phoneNumber: props.PhoneNumber || "",
|
|
32435
|
+
cpuArchitecture: props.CPUArchitecture || "",
|
|
32436
|
+
hardwareModel: props.HardwareModel || "",
|
|
32437
|
+
modelNumber: props.ModelNumber || "",
|
|
32438
|
+
regionInfo: props.RegionInfo || "",
|
|
32439
|
+
timeZone: props.TimeZone || "",
|
|
32440
|
+
uniqueChipID: props.UniqueChipID || "",
|
|
32441
|
+
isPaired: true
|
|
32442
|
+
// If we can get info, device is paired
|
|
32443
|
+
};
|
|
32444
|
+
}
|
|
32445
|
+
async function info(udid, iosCli2) {
|
|
32446
|
+
logTask(`Getting device info for ${udid}`);
|
|
32447
|
+
const result = await iosCli2.info(udid);
|
|
32448
|
+
if (!result) {
|
|
32449
|
+
throw new Error("Failed to get device info");
|
|
32450
|
+
}
|
|
32451
|
+
return result.output[0];
|
|
32452
|
+
}
|
|
32453
|
+
async function wipeDevice(udid, iosCli2) {
|
|
32454
|
+
logTask(`Wiping device ${udid}`);
|
|
32455
|
+
await iosCli2.wipe(udid);
|
|
32456
|
+
}
|
|
32457
|
+
async function removeProfile(profileIdentifier, udid, iosCli2) {
|
|
32458
|
+
logTask(`Removing profile ${profileIdentifier} from device ${udid}`);
|
|
32459
|
+
await iosCli2.removeProfile(udid, profileIdentifier);
|
|
32460
|
+
}
|
|
32461
|
+
async function listProfiles(udid, iosCli2) {
|
|
32462
|
+
logTask(`Listing profiles for device ${udid}`);
|
|
32463
|
+
return iosCli2.listProfiles(udid);
|
|
32464
|
+
}
|
|
32390
32465
|
|
|
32391
32466
|
// src/utils/mdmClient.ts
|
|
32392
|
-
import { join as
|
|
32467
|
+
import { join as join4 } from "node:path";
|
|
32393
32468
|
|
|
32394
32469
|
// ../../node_modules/.pnpm/graphql-request@5.2.0_encoding@0.1.13_graphql@14.7.0/node_modules/graphql-request/build/esm/defaultJsonSerializer.js
|
|
32395
32470
|
var defaultJsonSerializer = {
|
|
@@ -33159,7 +33234,7 @@ function createMdmClient(config) {
|
|
|
33159
33234
|
}
|
|
33160
33235
|
async function createMDMClient() {
|
|
33161
33236
|
const endpoint = process.env.MDM_ENDPOINT;
|
|
33162
|
-
const credPath =
|
|
33237
|
+
const credPath = join4(getResourcesPath(), "@clientLocal.json");
|
|
33163
33238
|
if (!endpoint || !credPath) {
|
|
33164
33239
|
return void 0;
|
|
33165
33240
|
}
|
|
@@ -33172,107 +33247,313 @@ async function createMDMClient() {
|
|
|
33172
33247
|
});
|
|
33173
33248
|
}
|
|
33174
33249
|
|
|
33175
|
-
// src/
|
|
33176
|
-
|
|
33177
|
-
|
|
33178
|
-
|
|
33179
|
-
|
|
33180
|
-
|
|
33181
|
-
|
|
33182
|
-
|
|
33183
|
-
|
|
33184
|
-
|
|
33185
|
-
|
|
33186
|
-
return null;
|
|
33187
|
-
}
|
|
33188
|
-
const absolutePath = isAbsolute(envPath) ? envPath : join4(process.cwd(), envPath);
|
|
33189
|
-
if (existsSync2(absolutePath)) {
|
|
33190
|
-
return absolutePath;
|
|
33250
|
+
// src/logic/actions/pair.ts
|
|
33251
|
+
async function isPaired(udid) {
|
|
33252
|
+
logTask(`Checking pairing status for ${udid}`);
|
|
33253
|
+
try {
|
|
33254
|
+
const result = await runIDeviceTool("idevicepair", ["-u", udid, "validate"]);
|
|
33255
|
+
if (!result) {
|
|
33256
|
+
return false;
|
|
33257
|
+
}
|
|
33258
|
+
return result.stdout.toLowerCase().includes("success");
|
|
33259
|
+
} catch {
|
|
33260
|
+
return false;
|
|
33191
33261
|
}
|
|
33192
|
-
return null;
|
|
33193
33262
|
}
|
|
33194
|
-
function
|
|
33195
|
-
|
|
33196
|
-
|
|
33197
|
-
|
|
33198
|
-
|
|
33199
|
-
if (envPlistDir) {
|
|
33200
|
-
return envPlistDir;
|
|
33263
|
+
async function trustDevice(udid, timeout2 = 6e4, onWaitingForTrust) {
|
|
33264
|
+
logTask(`Trusting device ${udid}`);
|
|
33265
|
+
if (await isPaired(udid)) {
|
|
33266
|
+
logInfo(`Device ${udid} is already trusted`);
|
|
33267
|
+
return true;
|
|
33201
33268
|
}
|
|
33202
|
-
|
|
33269
|
+
logInfo(`Initiating pairing for device ${udid}`);
|
|
33203
33270
|
try {
|
|
33204
|
-
|
|
33205
|
-
|
|
33206
|
-
|
|
33207
|
-
|
|
33208
|
-
baseDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
33271
|
+
const pairResult = await pair(udid);
|
|
33272
|
+
if (pairResult) {
|
|
33273
|
+
logInfo(`Device ${udid} paired successfully`);
|
|
33274
|
+
return true;
|
|
33209
33275
|
}
|
|
33210
|
-
} catch {
|
|
33211
|
-
|
|
33276
|
+
} catch (error) {
|
|
33277
|
+
logError(`Pairing failed with ${udid}: ${error}`);
|
|
33212
33278
|
}
|
|
33213
|
-
|
|
33214
|
-
|
|
33215
|
-
|
|
33279
|
+
logInfo("Please accept the trust dialog on the device...");
|
|
33280
|
+
onWaitingForTrust?.();
|
|
33281
|
+
try {
|
|
33282
|
+
await waitForPairing(udid, timeout2, 1e3);
|
|
33283
|
+
logInfo(`Device ${udid} is now trusted`);
|
|
33284
|
+
return true;
|
|
33285
|
+
} catch {
|
|
33286
|
+
logInfo(`Timeout waiting for trust acceptance on device ${udid}`);
|
|
33287
|
+
return false;
|
|
33216
33288
|
}
|
|
33217
|
-
|
|
33218
|
-
|
|
33219
|
-
|
|
33289
|
+
}
|
|
33290
|
+
async function waitForPairing(udid, timeout2 = 12e4, pollInterval = 1e3) {
|
|
33291
|
+
logTask(`Waiting for pairing on device ${udid}`);
|
|
33292
|
+
const startTime = Date.now();
|
|
33293
|
+
while (Date.now() - startTime < timeout2) {
|
|
33294
|
+
if (await isPaired(udid)) {
|
|
33295
|
+
logInfo(`Device ${udid} is now paired`);
|
|
33296
|
+
return true;
|
|
33297
|
+
}
|
|
33298
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
33220
33299
|
}
|
|
33221
|
-
|
|
33222
|
-
|
|
33223
|
-
|
|
33300
|
+
throw new Error(`Timeout waiting for device pairing after ${timeout2}ms`);
|
|
33301
|
+
}
|
|
33302
|
+
async function pair(udid) {
|
|
33303
|
+
logTask(`Initiating pairing for device ${udid}`);
|
|
33304
|
+
try {
|
|
33305
|
+
const result = await runIDeviceTool("idevicepair", ["-u", udid, "pair"]);
|
|
33306
|
+
if (!result) {
|
|
33307
|
+
return false;
|
|
33308
|
+
}
|
|
33309
|
+
return result.stdout.toLowerCase().includes("success");
|
|
33310
|
+
} catch (error) {
|
|
33311
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
33312
|
+
if (errorMsg.includes("Please accept the trust dialog")) {
|
|
33313
|
+
return false;
|
|
33314
|
+
}
|
|
33315
|
+
throw error;
|
|
33224
33316
|
}
|
|
33225
|
-
|
|
33226
|
-
|
|
33227
|
-
|
|
33317
|
+
}
|
|
33318
|
+
async function unpair(udid) {
|
|
33319
|
+
logTask(`Un-pairing device ${udid}`);
|
|
33320
|
+
try {
|
|
33321
|
+
const result = await runIDeviceTool("idevicepair", ["-u", udid, "unpair"]);
|
|
33322
|
+
if (!result) {
|
|
33323
|
+
return false;
|
|
33324
|
+
}
|
|
33325
|
+
return result.stdout.toLowerCase().includes("success");
|
|
33326
|
+
} catch {
|
|
33327
|
+
return false;
|
|
33228
33328
|
}
|
|
33229
|
-
|
|
33230
|
-
|
|
33231
|
-
|
|
33329
|
+
}
|
|
33330
|
+
|
|
33331
|
+
// src/logic/actions/install.ts
|
|
33332
|
+
async function installLocalApp(ipaPath, udid) {
|
|
33333
|
+
logTask(`Installing app ${ipaPath} on device ${udid}`);
|
|
33334
|
+
await runIDeviceTool("ideviceinstaller", ["-u", udid, "install", ipaPath]);
|
|
33335
|
+
}
|
|
33336
|
+
async function installManagedApp(ipaPath, udid, options) {
|
|
33337
|
+
await installLocalApp(ipaPath, udid);
|
|
33338
|
+
const client = await createMDMClient();
|
|
33339
|
+
logTask("Installing app via MDM for management takeover");
|
|
33340
|
+
if (client) {
|
|
33341
|
+
client.installApp(udid, options);
|
|
33232
33342
|
}
|
|
33233
|
-
return join4(process.cwd(), "src/plist");
|
|
33234
33343
|
}
|
|
33235
|
-
async function
|
|
33236
|
-
|
|
33237
|
-
|
|
33238
|
-
|
|
33344
|
+
async function uninstallApp(bundleId, udid) {
|
|
33345
|
+
logTask(`Uninstalling app ${bundleId} from device ${udid}`);
|
|
33346
|
+
if (!await isPaired(udid)) {
|
|
33347
|
+
await waitForPairing(udid, 1e4);
|
|
33348
|
+
}
|
|
33349
|
+
await runIDeviceTool("ideviceinstaller", ["-u", udid, "uninstall", bundleId]);
|
|
33239
33350
|
}
|
|
33240
|
-
function
|
|
33241
|
-
|
|
33242
|
-
|
|
33243
|
-
|
|
33244
|
-
|
|
33245
|
-
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
|
|
33249
|
-
result = result.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
33250
|
-
const value = variables[key];
|
|
33251
|
-
if (value === void 0 || value === null) {
|
|
33252
|
-
return "";
|
|
33253
|
-
}
|
|
33254
|
-
if (typeof value === "boolean") {
|
|
33255
|
-
return value ? "true" : "false";
|
|
33351
|
+
async function listApps(udid) {
|
|
33352
|
+
logTask(`Listing apps on device ${udid}`);
|
|
33353
|
+
if (!await isPaired(udid)) {
|
|
33354
|
+
await waitForPairing(udid, 1e4);
|
|
33355
|
+
}
|
|
33356
|
+
try {
|
|
33357
|
+
const result = await runIDeviceTool("ideviceinstaller", ["-u", udid, "list"]);
|
|
33358
|
+
if (!result) {
|
|
33359
|
+
return [];
|
|
33256
33360
|
}
|
|
33257
|
-
|
|
33258
|
-
|
|
33259
|
-
|
|
33361
|
+
const { stdout } = result;
|
|
33362
|
+
return parseAppList(stdout);
|
|
33363
|
+
} catch {
|
|
33364
|
+
return [];
|
|
33365
|
+
}
|
|
33260
33366
|
}
|
|
33261
|
-
function
|
|
33262
|
-
|
|
33367
|
+
async function isAppInstalled(bundleId, udid) {
|
|
33368
|
+
logTask(`Checking if app ${bundleId} is installed on device ${udid}`);
|
|
33369
|
+
const apps = await listApps(udid);
|
|
33370
|
+
return apps.some((app) => app.bundleId === bundleId);
|
|
33371
|
+
}
|
|
33372
|
+
async function launchApp(bundleId, _args, udid) {
|
|
33373
|
+
logTask(`Launching app ${bundleId} on device ${udid}`);
|
|
33263
33374
|
}
|
|
33264
33375
|
|
|
33265
|
-
// src/
|
|
33266
|
-
|
|
33267
|
-
|
|
33268
|
-
|
|
33269
|
-
|
|
33270
|
-
|
|
33271
|
-
|
|
33272
|
-
|
|
33273
|
-
|
|
33274
|
-
|
|
33275
|
-
|
|
33376
|
+
// src/logic/actions/proxy.ts
|
|
33377
|
+
import { spawn } from "node:child_process";
|
|
33378
|
+
import { join as join5 } from "node:path";
|
|
33379
|
+
function startPortForward(localPort, devicePort, udid, startupTimeout = 500) {
|
|
33380
|
+
return new Promise((resolve2, reject) => {
|
|
33381
|
+
logTask(`Starting port forward ${localPort} -> ${devicePort} for device ${udid}`);
|
|
33382
|
+
const binPath = getResourcesBinPath();
|
|
33383
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
33384
|
+
const toolPath = binPath ? join5(binPath, `iproxy${ext}`) : `iproxy${ext}`;
|
|
33385
|
+
const spawnOptions = {
|
|
33386
|
+
windowsHide: true,
|
|
33387
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
33388
|
+
};
|
|
33389
|
+
if (binPath) {
|
|
33390
|
+
spawnOptions.cwd = binPath;
|
|
33391
|
+
}
|
|
33392
|
+
logInfo(`Spawning iproxy with path: ${toolPath}`);
|
|
33393
|
+
logInfo(`Arguments: ${[localPort.toString(), devicePort.toString(), "-u", udid].join(" ")}`);
|
|
33394
|
+
logInfo(`Options: ${JSON.stringify(spawnOptions)}`);
|
|
33395
|
+
const child = spawn(
|
|
33396
|
+
toolPath,
|
|
33397
|
+
[localPort.toString(), devicePort.toString(), "-u", udid],
|
|
33398
|
+
spawnOptions
|
|
33399
|
+
);
|
|
33400
|
+
let hasResolved = false;
|
|
33401
|
+
let stderrOutput = "";
|
|
33402
|
+
child.stdout?.on("data", (data) => {
|
|
33403
|
+
logTask(`${data.toString()}`);
|
|
33404
|
+
});
|
|
33405
|
+
child.stderr?.on("data", (data) => {
|
|
33406
|
+
logError(`${data.toString()}`);
|
|
33407
|
+
const msg = data.toString();
|
|
33408
|
+
stderrOutput += msg;
|
|
33409
|
+
if (msg.toLowerCase().includes("error") && !hasResolved) {
|
|
33410
|
+
hasResolved = true;
|
|
33411
|
+
child.kill();
|
|
33412
|
+
reject(new Error(`Port forwarding failed: ${msg}`));
|
|
33413
|
+
}
|
|
33414
|
+
});
|
|
33415
|
+
child.on("error", (error) => {
|
|
33416
|
+
if (!hasResolved) {
|
|
33417
|
+
hasResolved = true;
|
|
33418
|
+
reject(new Error(`Failed to start iproxy: ${error.message}`));
|
|
33419
|
+
}
|
|
33420
|
+
});
|
|
33421
|
+
child.on("exit", (code) => {
|
|
33422
|
+
if (!hasResolved) {
|
|
33423
|
+
hasResolved = true;
|
|
33424
|
+
if (code !== 0 && code !== null) {
|
|
33425
|
+
reject(new Error(`iproxy exited with code ${code}: ${stderrOutput}`));
|
|
33426
|
+
}
|
|
33427
|
+
}
|
|
33428
|
+
});
|
|
33429
|
+
setTimeout(() => {
|
|
33430
|
+
if (!hasResolved) {
|
|
33431
|
+
hasResolved = true;
|
|
33432
|
+
logTask(`Port forward started: ${localPort} -> ${devicePort} for device ${udid}`);
|
|
33433
|
+
resolve2({
|
|
33434
|
+
result: {
|
|
33435
|
+
localPort,
|
|
33436
|
+
devicePort
|
|
33437
|
+
},
|
|
33438
|
+
process: child
|
|
33439
|
+
});
|
|
33440
|
+
}
|
|
33441
|
+
}, startupTimeout);
|
|
33442
|
+
});
|
|
33443
|
+
}
|
|
33444
|
+
function killPortForwardProcess(process2) {
|
|
33445
|
+
if (process2 && !process2.killed) {
|
|
33446
|
+
logTask("Killing port forward process");
|
|
33447
|
+
process2.kill();
|
|
33448
|
+
}
|
|
33449
|
+
}
|
|
33450
|
+
|
|
33451
|
+
// src/logic/activationFlow.ts
|
|
33452
|
+
import { unlink, writeFile } from "node:fs/promises";
|
|
33453
|
+
import { tmpdir } from "node:os";
|
|
33454
|
+
import { join as join7 } from "node:path";
|
|
33455
|
+
|
|
33456
|
+
// src/utils/wifiProfile.ts
|
|
33457
|
+
import { randomUUID } from "node:crypto";
|
|
33458
|
+
|
|
33459
|
+
// src/utils/templateLoader.ts
|
|
33460
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
33461
|
+
import { readFile } from "node:fs/promises";
|
|
33462
|
+
import { dirname, isAbsolute, join as join6, resolve } from "node:path";
|
|
33463
|
+
import { fileURLToPath } from "node:url";
|
|
33464
|
+
function resolveEnvPlistDir() {
|
|
33465
|
+
const envPath = join6(getResourcesPath(), "plist");
|
|
33466
|
+
if (!existsSync3(envPath)) {
|
|
33467
|
+
return null;
|
|
33468
|
+
}
|
|
33469
|
+
const absolutePath = isAbsolute(envPath) ? envPath : join6(process.cwd(), envPath);
|
|
33470
|
+
if (existsSync3(absolutePath)) {
|
|
33471
|
+
return absolutePath;
|
|
33472
|
+
}
|
|
33473
|
+
return null;
|
|
33474
|
+
}
|
|
33475
|
+
function getPlistDir(overrideDir) {
|
|
33476
|
+
if (overrideDir) {
|
|
33477
|
+
return resolve(overrideDir);
|
|
33478
|
+
}
|
|
33479
|
+
const envPlistDir = resolveEnvPlistDir();
|
|
33480
|
+
if (envPlistDir) {
|
|
33481
|
+
return envPlistDir;
|
|
33482
|
+
}
|
|
33483
|
+
let baseDir;
|
|
33484
|
+
try {
|
|
33485
|
+
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
33486
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
33487
|
+
baseDir = dirname(currentFile);
|
|
33488
|
+
} else {
|
|
33489
|
+
baseDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
33490
|
+
}
|
|
33491
|
+
} catch {
|
|
33492
|
+
baseDir = process.cwd();
|
|
33493
|
+
}
|
|
33494
|
+
const sourcePlistDir = join6(baseDir, "../plist");
|
|
33495
|
+
if (existsSync3(sourcePlistDir)) {
|
|
33496
|
+
return sourcePlistDir;
|
|
33497
|
+
}
|
|
33498
|
+
const distPlistDir = join6(baseDir, "../plist");
|
|
33499
|
+
if (existsSync3(distPlistDir)) {
|
|
33500
|
+
return distPlistDir;
|
|
33501
|
+
}
|
|
33502
|
+
const distPlistDir2 = join6(baseDir, "../dist/plist");
|
|
33503
|
+
if (existsSync3(distPlistDir2)) {
|
|
33504
|
+
return distPlistDir2;
|
|
33505
|
+
}
|
|
33506
|
+
const srcPlistFallback = join6(process.cwd(), "src/plist");
|
|
33507
|
+
if (existsSync3(srcPlistFallback)) {
|
|
33508
|
+
return srcPlistFallback;
|
|
33509
|
+
}
|
|
33510
|
+
const distPlistFallback = join6(process.cwd(), "dist/plist");
|
|
33511
|
+
if (existsSync3(distPlistFallback)) {
|
|
33512
|
+
return distPlistFallback;
|
|
33513
|
+
}
|
|
33514
|
+
return join6(process.cwd(), "src/plist");
|
|
33515
|
+
}
|
|
33516
|
+
async function loadTemplate(templateName, plistDir) {
|
|
33517
|
+
const templatePath = join6(getPlistDir(plistDir), templateName);
|
|
33518
|
+
const content = await readFile(templatePath, "utf-8");
|
|
33519
|
+
return content;
|
|
33520
|
+
}
|
|
33521
|
+
function processTemplate(template, variables) {
|
|
33522
|
+
let result = template;
|
|
33523
|
+
result = result.replace(/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_match, key, content) => {
|
|
33524
|
+
const value = variables[key];
|
|
33525
|
+
if (value && value !== false && value !== "" && value !== null && value !== void 0) {
|
|
33526
|
+
return processTemplate(content, variables);
|
|
33527
|
+
}
|
|
33528
|
+
return "";
|
|
33529
|
+
});
|
|
33530
|
+
result = result.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
33531
|
+
const value = variables[key];
|
|
33532
|
+
if (value === void 0 || value === null) {
|
|
33533
|
+
return "";
|
|
33534
|
+
}
|
|
33535
|
+
if (typeof value === "boolean") {
|
|
33536
|
+
return value ? "true" : "false";
|
|
33537
|
+
}
|
|
33538
|
+
return escapeXml(String(value));
|
|
33539
|
+
});
|
|
33540
|
+
return result;
|
|
33541
|
+
}
|
|
33542
|
+
function escapeXml(str) {
|
|
33543
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
33544
|
+
}
|
|
33545
|
+
|
|
33546
|
+
// src/utils/wifiProfile.ts
|
|
33547
|
+
async function generateWifiProfile(config, options) {
|
|
33548
|
+
const {
|
|
33549
|
+
ssid,
|
|
33550
|
+
password,
|
|
33551
|
+
encryptionType = "WPA2",
|
|
33552
|
+
autoJoin = true,
|
|
33553
|
+
hiddenNetwork = false,
|
|
33554
|
+
organizationName = "MCE Systems",
|
|
33555
|
+
displayName = `WiFi - ${ssid}`,
|
|
33556
|
+
enterprise = false,
|
|
33276
33557
|
username,
|
|
33277
33558
|
eapType = "PEAP",
|
|
33278
33559
|
acceptAnyCertificate = true
|
|
@@ -33386,89 +33667,223 @@ function parseWifiEapType(value) {
|
|
|
33386
33667
|
}
|
|
33387
33668
|
}
|
|
33388
33669
|
|
|
33389
|
-
// src/logic/
|
|
33390
|
-
|
|
33391
|
-
|
|
33392
|
-
var
|
|
33393
|
-
|
|
33394
|
-
|
|
33395
|
-
|
|
33396
|
-
|
|
33397
|
-
|
|
33398
|
-
return createIosCli(iosBinaryPath);
|
|
33399
|
-
}
|
|
33400
|
-
if (!ios) {
|
|
33401
|
-
ios = iosCli();
|
|
33402
|
-
}
|
|
33403
|
-
function runIosCommand(iosBinaryPath, args) {
|
|
33404
|
-
return new Promise((resolve2, reject) => {
|
|
33405
|
-
const child = spawn(iosBinaryPath, args, {
|
|
33406
|
-
windowsHide: true,
|
|
33407
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
33408
|
-
});
|
|
33409
|
-
let stdout = "";
|
|
33410
|
-
let stderr = "";
|
|
33411
|
-
child.stdout?.on("data", (data) => {
|
|
33412
|
-
stdout += data.toString();
|
|
33413
|
-
});
|
|
33414
|
-
child.stderr?.on("data", (data) => {
|
|
33415
|
-
stderr += data.toString();
|
|
33416
|
-
});
|
|
33417
|
-
child.on("error", (error) => {
|
|
33418
|
-
reject(error);
|
|
33419
|
-
});
|
|
33420
|
-
child.on("close", (code) => {
|
|
33421
|
-
console.log(`stdout: ${stdout}`);
|
|
33422
|
-
console.log(`stderr: ${stderr}`);
|
|
33423
|
-
console.log(`exitCode: ${code}`);
|
|
33424
|
-
const output = stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => safeParseJson(line)).filter((item) => Boolean(item));
|
|
33425
|
-
const logMessages = stderr.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => safeParseJson(line)).filter((item) => Boolean(item)).map((lineJson) => ({
|
|
33426
|
-
level: lineJson.level,
|
|
33427
|
-
msg: lineJson.msg,
|
|
33428
|
-
timestamp: lineJson.timestamp
|
|
33429
|
-
}));
|
|
33430
|
-
resolve2({
|
|
33431
|
-
command: iosBinaryPath,
|
|
33432
|
-
args,
|
|
33433
|
-
output,
|
|
33434
|
-
logMessages,
|
|
33435
|
-
exitCode: code ?? 0
|
|
33436
|
-
});
|
|
33437
|
-
});
|
|
33438
|
-
});
|
|
33439
|
-
}
|
|
33440
|
-
function safeParseJson(line) {
|
|
33441
|
-
try {
|
|
33442
|
-
const parsed = JSON.parse(line);
|
|
33443
|
-
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
33444
|
-
} catch {
|
|
33445
|
-
return void 0;
|
|
33670
|
+
// src/logic/activationFlow.ts
|
|
33671
|
+
var DEFAULT_RETRIES = 150;
|
|
33672
|
+
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
33673
|
+
var ActivationFlow = class {
|
|
33674
|
+
iosCli;
|
|
33675
|
+
mdmClientPromise;
|
|
33676
|
+
constructor(iosCli2) {
|
|
33677
|
+
this.iosCli = iosCli2;
|
|
33678
|
+
this.mdmClientPromise = createMDMClient();
|
|
33446
33679
|
}
|
|
33447
|
-
|
|
33448
|
-
|
|
33449
|
-
|
|
33450
|
-
|
|
33451
|
-
|
|
33452
|
-
|
|
33453
|
-
|
|
33454
|
-
}
|
|
33455
|
-
function getBooleanField(record, key) {
|
|
33456
|
-
const value = record[key];
|
|
33457
|
-
return typeof value === "boolean" ? value : void 0;
|
|
33458
|
-
}
|
|
33459
|
-
function getNumberField(record, key) {
|
|
33460
|
-
const value = record[key];
|
|
33461
|
-
return typeof value === "number" ? value : void 0;
|
|
33462
|
-
}
|
|
33463
|
-
function getRecordField(record, key) {
|
|
33464
|
-
const value = record[key];
|
|
33465
|
-
return isRecord(value) ? value : void 0;
|
|
33466
|
-
}
|
|
33467
|
-
function toProfileManifest(record) {
|
|
33468
|
-
if (!record) {
|
|
33469
|
-
return void 0;
|
|
33680
|
+
async run(udid) {
|
|
33681
|
+
logTask(`Starting activation flow for device ${udid}`);
|
|
33682
|
+
await this.retryActivateCommand("activate device", () => this.iosCli.activate(udid));
|
|
33683
|
+
const wifiProfileIdentifier = await this.installWifiProfile(udid);
|
|
33684
|
+
await this.installMdmProfile(udid);
|
|
33685
|
+
await this.retryIosCommand("skip steps", () => this.iosCli.skipSteps(udid));
|
|
33686
|
+
return () => this.removeWifiProfile(udid, wifiProfileIdentifier);
|
|
33470
33687
|
}
|
|
33471
|
-
|
|
33688
|
+
async installWifiProfile(udid) {
|
|
33689
|
+
const wifiProfile = await generateWifiProfileFromEnv();
|
|
33690
|
+
if (!wifiProfile) {
|
|
33691
|
+
return void 0;
|
|
33692
|
+
}
|
|
33693
|
+
const wifiProfilePath = await saveWifiProfileToTemp(wifiProfile);
|
|
33694
|
+
const wifiProfileIdentifier = getProfileIdentifierFromProfile(wifiProfile);
|
|
33695
|
+
await this.retryIosCommand(
|
|
33696
|
+
"install wifi profile",
|
|
33697
|
+
() => this.iosCli.installProfile(udid, wifiProfilePath)
|
|
33698
|
+
);
|
|
33699
|
+
await removeTempFile(wifiProfilePath, "wifi profile");
|
|
33700
|
+
return wifiProfileIdentifier;
|
|
33701
|
+
}
|
|
33702
|
+
async installMdmProfile(udid) {
|
|
33703
|
+
const client = await this.requireMdmClient();
|
|
33704
|
+
logTask("Installing MDM enrollment profile");
|
|
33705
|
+
const enrollmentProfile = await this.retry(
|
|
33706
|
+
"generate mdm enrollment profile",
|
|
33707
|
+
() => client.generateEnrollmentProfile(udid),
|
|
33708
|
+
(result) => result.status === "OK" && Boolean(result.profile)
|
|
33709
|
+
);
|
|
33710
|
+
if (!enrollmentProfile.profile) {
|
|
33711
|
+
throw new Error("MDM enrollment profile missing from response");
|
|
33712
|
+
}
|
|
33713
|
+
const profilePath = await saveProfileToTemp(enrollmentProfile.profile, "mdm_profile");
|
|
33714
|
+
await this.retryIosCommand(
|
|
33715
|
+
"install mdm profile",
|
|
33716
|
+
() => this.iosCli.installProfile(udid, profilePath)
|
|
33717
|
+
);
|
|
33718
|
+
await removeTempFile(profilePath, "mdm profile");
|
|
33719
|
+
}
|
|
33720
|
+
async removeWifiProfile(udid, profileIdentifier) {
|
|
33721
|
+
if (!profileIdentifier) {
|
|
33722
|
+
return;
|
|
33723
|
+
}
|
|
33724
|
+
logTask("Removing WiFi profile");
|
|
33725
|
+
await this.retryIosCommand(
|
|
33726
|
+
"remove wifi profile",
|
|
33727
|
+
() => this.iosCli.removeProfile(udid, profileIdentifier)
|
|
33728
|
+
);
|
|
33729
|
+
}
|
|
33730
|
+
async requireMdmClient() {
|
|
33731
|
+
const client = await this.mdmClientPromise;
|
|
33732
|
+
if (!client) {
|
|
33733
|
+
throw new Error("MDM client not configured. Set mdm endpoint and credential path.");
|
|
33734
|
+
}
|
|
33735
|
+
return client;
|
|
33736
|
+
}
|
|
33737
|
+
async retryIosCommand(label, command) {
|
|
33738
|
+
return this.retry(label, command, (result) => result.exitCode === 0);
|
|
33739
|
+
}
|
|
33740
|
+
async retryActivateCommand(label, command) {
|
|
33741
|
+
return this.retry(label, command, (result) => isActivationSuccess(result));
|
|
33742
|
+
}
|
|
33743
|
+
async retry(label, command, isSuccess, retries = +(process.env.DEFAULT_RETRIES ?? DEFAULT_RETRIES), retryDelayMs = +(process.env.DEFAULT_RETRY_DELAY_MS ?? DEFAULT_RETRY_DELAY_MS)) {
|
|
33744
|
+
for (let attempt = 0; attempt < retries; attempt += 1) {
|
|
33745
|
+
try {
|
|
33746
|
+
const result = await command();
|
|
33747
|
+
if (isSuccess(result)) {
|
|
33748
|
+
return result;
|
|
33749
|
+
}
|
|
33750
|
+
} catch (error) {
|
|
33751
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
33752
|
+
logError(`${label} failed: ${errorMsg}`);
|
|
33753
|
+
}
|
|
33754
|
+
logInfo(`Retrying ${label}... ${attempt + 1} of ${retries}`);
|
|
33755
|
+
await new Promise((resolve2) => setTimeout(resolve2, retryDelayMs));
|
|
33756
|
+
}
|
|
33757
|
+
throw new Error(`Failed to ${label} after ${retries} attempts`);
|
|
33758
|
+
}
|
|
33759
|
+
};
|
|
33760
|
+
async function saveProfileToTemp(profile, prefix) {
|
|
33761
|
+
const tempFilePath = join7(tmpdir(), `mce_${prefix}_${Date.now()}.mobileconfig`);
|
|
33762
|
+
await writeFile(tempFilePath, profile, "utf-8");
|
|
33763
|
+
logInfo(`Profile saved to: ${tempFilePath}`);
|
|
33764
|
+
return tempFilePath;
|
|
33765
|
+
}
|
|
33766
|
+
async function removeTempFile(filePath, label) {
|
|
33767
|
+
try {
|
|
33768
|
+
await unlink(filePath);
|
|
33769
|
+
logInfo(`Removed ${label} temp file: ${filePath}`);
|
|
33770
|
+
} catch (error) {
|
|
33771
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
33772
|
+
logError(`Failed to remove ${label} temp file: ${errorMsg}`);
|
|
33773
|
+
}
|
|
33774
|
+
}
|
|
33775
|
+
function getProfileIdentifierFromProfile(profile) {
|
|
33776
|
+
const pattern = /<key>PayloadIdentifier<\/key>\s*<string>([^<]+)<\/string>/g;
|
|
33777
|
+
const matches = [];
|
|
33778
|
+
let match = pattern.exec(profile);
|
|
33779
|
+
while (match) {
|
|
33780
|
+
matches.push(match[1].trim());
|
|
33781
|
+
match = pattern.exec(profile);
|
|
33782
|
+
}
|
|
33783
|
+
if (matches.length === 0) {
|
|
33784
|
+
return void 0;
|
|
33785
|
+
}
|
|
33786
|
+
return matches[matches.length - 1];
|
|
33787
|
+
}
|
|
33788
|
+
function resolveIosBinaryPath() {
|
|
33789
|
+
const platform = process.platform;
|
|
33790
|
+
const binaryName = platform === "win32" ? "ios.exe" : "ios";
|
|
33791
|
+
return join7(getResourcesBinPath(), binaryName);
|
|
33792
|
+
}
|
|
33793
|
+
function isActivationSuccess(result) {
|
|
33794
|
+
if (result.exitCode !== 0) {
|
|
33795
|
+
return false;
|
|
33796
|
+
}
|
|
33797
|
+
if (result.logMessages.length === 0) {
|
|
33798
|
+
return true;
|
|
33799
|
+
}
|
|
33800
|
+
const hasFatal = result.logMessages.some((log) => log.level === "error");
|
|
33801
|
+
return !hasFatal;
|
|
33802
|
+
}
|
|
33803
|
+
|
|
33804
|
+
// src/logic/iosCli.ts
|
|
33805
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
33806
|
+
import { join as join8 } from "node:path";
|
|
33807
|
+
var ios;
|
|
33808
|
+
function iosCli() {
|
|
33809
|
+
const iosBinaryPath = resolveIosBinaryPath();
|
|
33810
|
+
if (!iosBinaryPath) {
|
|
33811
|
+
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
33812
|
+
}
|
|
33813
|
+
return createIosCli(iosBinaryPath);
|
|
33814
|
+
}
|
|
33815
|
+
if (!ios) {
|
|
33816
|
+
ios = iosCli();
|
|
33817
|
+
}
|
|
33818
|
+
function runIosCommand(iosBinaryPath, args) {
|
|
33819
|
+
return new Promise((resolve2, reject) => {
|
|
33820
|
+
const child = spawn2(iosBinaryPath, args, {
|
|
33821
|
+
windowsHide: true,
|
|
33822
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
33823
|
+
});
|
|
33824
|
+
let stdout = "";
|
|
33825
|
+
let stderr = "";
|
|
33826
|
+
child.stdout?.on("data", (data) => {
|
|
33827
|
+
stdout += data.toString();
|
|
33828
|
+
});
|
|
33829
|
+
child.stderr?.on("data", (data) => {
|
|
33830
|
+
stderr += data.toString();
|
|
33831
|
+
});
|
|
33832
|
+
child.on("error", (error) => {
|
|
33833
|
+
reject(error);
|
|
33834
|
+
});
|
|
33835
|
+
child.on("close", (code) => {
|
|
33836
|
+
console.log(`stdout: ${stdout}`);
|
|
33837
|
+
console.log(`stderr: ${stderr}`);
|
|
33838
|
+
console.log(`exitCode: ${code}`);
|
|
33839
|
+
const output = stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => safeParseJson(line)).filter((item) => Boolean(item));
|
|
33840
|
+
const logMessages = stderr.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => safeParseJson(line)).filter((item) => Boolean(item)).map((lineJson) => ({
|
|
33841
|
+
level: lineJson.level,
|
|
33842
|
+
msg: lineJson.msg,
|
|
33843
|
+
timestamp: lineJson.timestamp
|
|
33844
|
+
}));
|
|
33845
|
+
resolve2({
|
|
33846
|
+
command: iosBinaryPath,
|
|
33847
|
+
args,
|
|
33848
|
+
output,
|
|
33849
|
+
logMessages,
|
|
33850
|
+
exitCode: code ?? 0
|
|
33851
|
+
});
|
|
33852
|
+
});
|
|
33853
|
+
});
|
|
33854
|
+
}
|
|
33855
|
+
function safeParseJson(line) {
|
|
33856
|
+
try {
|
|
33857
|
+
const parsed = JSON.parse(line);
|
|
33858
|
+
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
33859
|
+
} catch {
|
|
33860
|
+
return void 0;
|
|
33861
|
+
}
|
|
33862
|
+
}
|
|
33863
|
+
function isRecord(value) {
|
|
33864
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
33865
|
+
}
|
|
33866
|
+
function getStringField(record, key) {
|
|
33867
|
+
const value = record[key];
|
|
33868
|
+
return typeof value === "string" ? value : void 0;
|
|
33869
|
+
}
|
|
33870
|
+
function getBooleanField(record, key) {
|
|
33871
|
+
const value = record[key];
|
|
33872
|
+
return typeof value === "boolean" ? value : void 0;
|
|
33873
|
+
}
|
|
33874
|
+
function getNumberField(record, key) {
|
|
33875
|
+
const value = record[key];
|
|
33876
|
+
return typeof value === "number" ? value : void 0;
|
|
33877
|
+
}
|
|
33878
|
+
function getRecordField(record, key) {
|
|
33879
|
+
const value = record[key];
|
|
33880
|
+
return isRecord(value) ? value : void 0;
|
|
33881
|
+
}
|
|
33882
|
+
function toProfileManifest(record) {
|
|
33883
|
+
if (!record) {
|
|
33884
|
+
return void 0;
|
|
33885
|
+
}
|
|
33886
|
+
const description = getStringField(record, "Description");
|
|
33472
33887
|
const isActive = getBooleanField(record, "IsActive");
|
|
33473
33888
|
if (description === void 0 && isActive === void 0) {
|
|
33474
33889
|
return void 0;
|
|
@@ -33562,7 +33977,7 @@ function createIosCli(iosBinaryPath) {
|
|
|
33562
33977
|
return runIosCommand(iosBinaryPath, [
|
|
33563
33978
|
"prepare",
|
|
33564
33979
|
"--skip-all",
|
|
33565
|
-
`--certfile=${
|
|
33980
|
+
`--certfile=${join8(resourcesDir2, "cert.der")}`,
|
|
33566
33981
|
`--orgname=${process.env.ORGANIZATION_NAME}`,
|
|
33567
33982
|
"--udid",
|
|
33568
33983
|
deviceId
|
|
@@ -33594,454 +34009,26 @@ function createIosCli(iosBinaryPath) {
|
|
|
33594
34009
|
}
|
|
33595
34010
|
};
|
|
33596
34011
|
}
|
|
33597
|
-
var iosCli_default = ios;
|
|
33598
34012
|
|
|
33599
|
-
// src/logic/
|
|
33600
|
-
var
|
|
33601
|
-
|
|
33602
|
-
|
|
33603
|
-
|
|
33604
|
-
|
|
33605
|
-
|
|
34013
|
+
// src/logic/appleDeviceKit.ts
|
|
34014
|
+
var AppleDeviceKit = class {
|
|
34015
|
+
constructor(udid, logicalPort) {
|
|
34016
|
+
this.logicalPort = logicalPort;
|
|
34017
|
+
this.deviceId = udid;
|
|
34018
|
+
logInfo(
|
|
34019
|
+
`AppleDeviceKit initialized for device: ${this.deviceId}, logical port: ${this.logicalPort}`
|
|
34020
|
+
);
|
|
33606
34021
|
const iosBinaryPath = resolveIosBinaryPath();
|
|
33607
34022
|
if (!iosBinaryPath) {
|
|
33608
34023
|
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
33609
34024
|
}
|
|
33610
34025
|
this.iosCli = createIosCli(iosBinaryPath);
|
|
33611
|
-
this.mdmClientPromise = createMDMClient();
|
|
33612
|
-
}
|
|
33613
|
-
async run(udid) {
|
|
33614
|
-
logTask(`Starting activation flow for device ${udid}`);
|
|
33615
|
-
await this.retryActivateCommand("activate device", () => this.iosCli.activate(udid));
|
|
33616
|
-
const wifiProfileIdentifier = await this.installWifiProfile(udid);
|
|
33617
|
-
await this.installMdmProfile(udid);
|
|
33618
|
-
await this.retryIosCommand("skip steps", () => this.iosCli.skipSteps(udid));
|
|
33619
|
-
return () => this.removeWifiProfile(udid, wifiProfileIdentifier);
|
|
33620
|
-
}
|
|
33621
|
-
async installWifiProfile(udid) {
|
|
33622
|
-
const wifiProfile = await generateWifiProfileFromEnv();
|
|
33623
|
-
if (!wifiProfile) {
|
|
33624
|
-
return void 0;
|
|
33625
|
-
}
|
|
33626
|
-
const wifiProfilePath = await saveWifiProfileToTemp(wifiProfile);
|
|
33627
|
-
const wifiProfileIdentifier = getProfileIdentifierFromProfile(wifiProfile);
|
|
33628
|
-
await this.retryIosCommand(
|
|
33629
|
-
"install wifi profile",
|
|
33630
|
-
() => this.iosCli.installProfile(udid, wifiProfilePath)
|
|
33631
|
-
);
|
|
33632
|
-
await removeTempFile(wifiProfilePath, "wifi profile");
|
|
33633
|
-
return wifiProfileIdentifier;
|
|
33634
|
-
}
|
|
33635
|
-
async installMdmProfile(udid) {
|
|
33636
|
-
const client = await this.requireMdmClient();
|
|
33637
|
-
logTask("Installing MDM enrollment profile");
|
|
33638
|
-
const enrollmentProfile = await this.retry(
|
|
33639
|
-
"generate mdm enrollment profile",
|
|
33640
|
-
() => client.generateEnrollmentProfile(udid),
|
|
33641
|
-
(result) => result.status === "OK" && Boolean(result.profile)
|
|
33642
|
-
);
|
|
33643
|
-
if (!enrollmentProfile.profile) {
|
|
33644
|
-
throw new Error("MDM enrollment profile missing from response");
|
|
33645
|
-
}
|
|
33646
|
-
const profilePath = await saveProfileToTemp(enrollmentProfile.profile, "mdm_profile");
|
|
33647
|
-
await this.retryIosCommand(
|
|
33648
|
-
"install mdm profile",
|
|
33649
|
-
() => this.iosCli.installProfile(udid, profilePath)
|
|
33650
|
-
);
|
|
33651
|
-
await removeTempFile(profilePath, "mdm profile");
|
|
33652
|
-
}
|
|
33653
|
-
async removeWifiProfile(udid, profileIdentifier) {
|
|
33654
|
-
if (!profileIdentifier) {
|
|
33655
|
-
return;
|
|
33656
|
-
}
|
|
33657
|
-
logTask("Removing WiFi profile");
|
|
33658
|
-
await this.retryIosCommand(
|
|
33659
|
-
"remove wifi profile",
|
|
33660
|
-
() => this.iosCli.removeProfile(udid, profileIdentifier)
|
|
33661
|
-
);
|
|
33662
|
-
}
|
|
33663
|
-
async requireMdmClient() {
|
|
33664
|
-
const client = await this.mdmClientPromise;
|
|
33665
|
-
if (!client) {
|
|
33666
|
-
throw new Error("MDM client not configured. Set mdm endpoint and credential path.");
|
|
33667
|
-
}
|
|
33668
|
-
return client;
|
|
33669
|
-
}
|
|
33670
|
-
async retryIosCommand(label, command) {
|
|
33671
|
-
return this.retry(label, command, (result) => result.exitCode === 0);
|
|
33672
|
-
}
|
|
33673
|
-
async retryActivateCommand(label, command) {
|
|
33674
|
-
return this.retry(label, command, (result) => isActivationSuccess(result));
|
|
33675
|
-
}
|
|
33676
|
-
async retry(label, command, isSuccess, retries = +(process.env.DEFAULT_RETRIES ?? DEFAULT_RETRIES), retryDelayMs = +(process.env.DEFAULT_RETRY_DELAY_MS ?? DEFAULT_RETRY_DELAY_MS)) {
|
|
33677
|
-
for (let attempt = 0; attempt < retries; attempt += 1) {
|
|
33678
|
-
try {
|
|
33679
|
-
const result = await command();
|
|
33680
|
-
if (isSuccess(result)) {
|
|
33681
|
-
return result;
|
|
33682
|
-
}
|
|
33683
|
-
} catch (error) {
|
|
33684
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
33685
|
-
logError(`${label} failed: ${errorMsg}`);
|
|
33686
|
-
}
|
|
33687
|
-
logInfo(`Retrying ${label}... ${attempt + 1} of ${retries}`);
|
|
33688
|
-
await new Promise((resolve2) => setTimeout(resolve2, retryDelayMs));
|
|
33689
|
-
}
|
|
33690
|
-
throw new Error(`Failed to ${label} after ${retries} attempts`);
|
|
33691
|
-
}
|
|
33692
|
-
};
|
|
33693
|
-
async function saveProfileToTemp(profile, prefix) {
|
|
33694
|
-
const tempFilePath = join6(tmpdir(), `mce_${prefix}_${Date.now()}.mobileconfig`);
|
|
33695
|
-
await writeFile(tempFilePath, profile, "utf-8");
|
|
33696
|
-
logInfo(`Profile saved to: ${tempFilePath}`);
|
|
33697
|
-
return tempFilePath;
|
|
33698
|
-
}
|
|
33699
|
-
async function removeTempFile(filePath, label) {
|
|
33700
|
-
try {
|
|
33701
|
-
await unlink(filePath);
|
|
33702
|
-
logInfo(`Removed ${label} temp file: ${filePath}`);
|
|
33703
|
-
} catch (error) {
|
|
33704
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
33705
|
-
logError(`Failed to remove ${label} temp file: ${errorMsg}`);
|
|
33706
|
-
}
|
|
33707
|
-
}
|
|
33708
|
-
function getProfileIdentifierFromProfile(profile) {
|
|
33709
|
-
const pattern = /<key>PayloadIdentifier<\/key>\s*<string>([^<]+)<\/string>/g;
|
|
33710
|
-
const matches = [];
|
|
33711
|
-
let match = pattern.exec(profile);
|
|
33712
|
-
while (match) {
|
|
33713
|
-
matches.push(match[1].trim());
|
|
33714
|
-
match = pattern.exec(profile);
|
|
33715
|
-
}
|
|
33716
|
-
if (matches.length === 0) {
|
|
33717
|
-
return void 0;
|
|
33718
|
-
}
|
|
33719
|
-
return matches[matches.length - 1];
|
|
33720
|
-
}
|
|
33721
|
-
function resolveIosBinaryPath() {
|
|
33722
|
-
const platform = process.platform;
|
|
33723
|
-
const binaryName = platform === "win32" ? "ios.exe" : "ios";
|
|
33724
|
-
return join6(getResourcesBinPath(), binaryName);
|
|
33725
|
-
}
|
|
33726
|
-
function isActivationSuccess(result) {
|
|
33727
|
-
if (result.exitCode !== 0) {
|
|
33728
|
-
return false;
|
|
33729
|
-
}
|
|
33730
|
-
if (result.logMessages.length === 0) {
|
|
33731
|
-
return true;
|
|
33732
|
-
}
|
|
33733
|
-
const hasFatal = result.logMessages.some((log) => log.level === "error");
|
|
33734
|
-
return !hasFatal;
|
|
33735
|
-
}
|
|
33736
|
-
|
|
33737
|
-
// src/logic/dataParser.ts
|
|
33738
|
-
function parsePlistOutput(output) {
|
|
33739
|
-
const result = {};
|
|
33740
|
-
const lines = output.split("\n");
|
|
33741
|
-
for (const line of lines) {
|
|
33742
|
-
const colonIndex = line.indexOf(":");
|
|
33743
|
-
if (colonIndex > 0) {
|
|
33744
|
-
const key = line.substring(0, colonIndex).trim();
|
|
33745
|
-
const value = line.substring(colonIndex + 1).trim();
|
|
33746
|
-
result[key] = value;
|
|
33747
|
-
}
|
|
33748
|
-
}
|
|
33749
|
-
return result;
|
|
33750
|
-
}
|
|
33751
|
-
function parseAppList(output) {
|
|
33752
|
-
const apps = [];
|
|
33753
|
-
const lines = output.trim().split("\n");
|
|
33754
|
-
for (const line of lines) {
|
|
33755
|
-
const match = line.match(/^([^,]+),\s*([^,]+),\s*"?([^"]+)"?/);
|
|
33756
|
-
if (match) {
|
|
33757
|
-
apps.push({
|
|
33758
|
-
bundleId: match[1].trim(),
|
|
33759
|
-
version: match[2].trim(),
|
|
33760
|
-
displayName: match[3].trim(),
|
|
33761
|
-
bundleVersion: ""
|
|
33762
|
-
});
|
|
33763
|
-
}
|
|
33764
|
-
}
|
|
33765
|
-
return apps;
|
|
33766
|
-
}
|
|
33767
|
-
|
|
33768
|
-
// src/logic/actions/device.ts
|
|
33769
|
-
async function getDeviceInfo(udid) {
|
|
33770
|
-
logTask(`Getting device info for ${udid}`);
|
|
33771
|
-
const result = await runIDeviceTool("ideviceinfo", ["-u", udid]);
|
|
33772
|
-
if (!result) {
|
|
33773
|
-
throw new Error("Failed to get device info");
|
|
33774
|
-
}
|
|
33775
|
-
const props = parsePlistOutput(result.stdout);
|
|
33776
|
-
return {
|
|
33777
|
-
deviceName: props.DeviceName || "",
|
|
33778
|
-
productType: props.ProductType || "",
|
|
33779
|
-
productVersion: props.ProductVersion || "",
|
|
33780
|
-
buildVersion: props.BuildVersion || "",
|
|
33781
|
-
serialNumber: props.SerialNumber || "",
|
|
33782
|
-
udid: props.UniqueDeviceID || udid,
|
|
33783
|
-
wifiAddress: props.WiFiAddress || "",
|
|
33784
|
-
bluetoothAddress: props.BluetoothAddress || "",
|
|
33785
|
-
phoneNumber: props.PhoneNumber || "",
|
|
33786
|
-
cpuArchitecture: props.CPUArchitecture || "",
|
|
33787
|
-
hardwareModel: props.HardwareModel || "",
|
|
33788
|
-
modelNumber: props.ModelNumber || "",
|
|
33789
|
-
regionInfo: props.RegionInfo || "",
|
|
33790
|
-
timeZone: props.TimeZone || "",
|
|
33791
|
-
uniqueChipID: props.UniqueChipID || "",
|
|
33792
|
-
isPaired: true
|
|
33793
|
-
// If we can get info, device is paired
|
|
33794
|
-
};
|
|
33795
|
-
}
|
|
33796
|
-
async function info(udid) {
|
|
33797
|
-
logTask(`Getting device info for ${udid}`);
|
|
33798
|
-
const iosBinaryPath = resolveIosBinaryPath();
|
|
33799
|
-
if (!iosBinaryPath) {
|
|
33800
|
-
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
33801
|
-
}
|
|
33802
|
-
const iosCli2 = createIosCli(iosBinaryPath);
|
|
33803
|
-
const result = await iosCli2.info(udid);
|
|
33804
|
-
if (!result) {
|
|
33805
|
-
throw new Error("Failed to get device info");
|
|
33806
|
-
}
|
|
33807
|
-
return result.output[0];
|
|
33808
|
-
}
|
|
33809
|
-
async function wipeDevice(udid) {
|
|
33810
|
-
if (!iosCli_default) {
|
|
33811
|
-
throw new Error("ios is not initialized");
|
|
33812
|
-
}
|
|
33813
|
-
logTask(`Wiping device ${udid}`);
|
|
33814
|
-
await iosCli_default.wipe(udid);
|
|
33815
|
-
}
|
|
33816
|
-
async function removeProfile(profileIdentifier, udid) {
|
|
33817
|
-
if (!iosCli_default) {
|
|
33818
|
-
throw new Error("ios is not initialized");
|
|
33819
|
-
}
|
|
33820
|
-
logTask(`Removing profile ${profileIdentifier} from device ${udid}`);
|
|
33821
|
-
await iosCli_default.removeProfile(udid, profileIdentifier);
|
|
33822
|
-
}
|
|
33823
|
-
async function listProfiles(udid) {
|
|
33824
|
-
if (!iosCli_default) {
|
|
33825
|
-
throw new Error("ios is not initialized");
|
|
33826
|
-
}
|
|
33827
|
-
logTask(`Listing profiles for device ${udid}`);
|
|
33828
|
-
return iosCli_default.listProfiles(udid);
|
|
33829
|
-
}
|
|
33830
|
-
|
|
33831
|
-
// src/logic/actions/pair.ts
|
|
33832
|
-
async function isPaired(udid) {
|
|
33833
|
-
logTask(`Checking pairing status for ${udid}`);
|
|
33834
|
-
try {
|
|
33835
|
-
const result = await runIDeviceTool("idevicepair", ["-u", udid, "validate"]);
|
|
33836
|
-
if (!result) {
|
|
33837
|
-
return false;
|
|
33838
|
-
}
|
|
33839
|
-
return result.stdout.toLowerCase().includes("success");
|
|
33840
|
-
} catch {
|
|
33841
|
-
return false;
|
|
33842
|
-
}
|
|
33843
|
-
}
|
|
33844
|
-
async function trustDevice(udid, timeout2 = 6e4, onWaitingForTrust) {
|
|
33845
|
-
logTask(`Trusting device ${udid}`);
|
|
33846
|
-
if (await isPaired(udid)) {
|
|
33847
|
-
logInfo(`Device ${udid} is already trusted`);
|
|
33848
|
-
return true;
|
|
33849
|
-
}
|
|
33850
|
-
logInfo(`Initiating pairing for device ${udid}`);
|
|
33851
|
-
try {
|
|
33852
|
-
const pairResult = await pair(udid);
|
|
33853
|
-
if (pairResult) {
|
|
33854
|
-
logInfo(`Device ${udid} paired successfully`);
|
|
33855
|
-
return true;
|
|
33856
|
-
}
|
|
33857
|
-
} catch (error) {
|
|
33858
|
-
logError(`Pairing failed with ${udid}: ${error}`);
|
|
33859
|
-
}
|
|
33860
|
-
logInfo("Please accept the trust dialog on the device...");
|
|
33861
|
-
onWaitingForTrust?.();
|
|
33862
|
-
try {
|
|
33863
|
-
await waitForPairing(udid, timeout2, 1e3);
|
|
33864
|
-
logInfo(`Device ${udid} is now trusted`);
|
|
33865
|
-
return true;
|
|
33866
|
-
} catch {
|
|
33867
|
-
logInfo(`Timeout waiting for trust acceptance on device ${udid}`);
|
|
33868
|
-
return false;
|
|
33869
|
-
}
|
|
33870
|
-
}
|
|
33871
|
-
async function waitForPairing(udid, timeout2 = 12e4, pollInterval = 1e3) {
|
|
33872
|
-
logTask(`Waiting for pairing on device ${udid}`);
|
|
33873
|
-
const startTime = Date.now();
|
|
33874
|
-
while (Date.now() - startTime < timeout2) {
|
|
33875
|
-
if (await isPaired(udid)) {
|
|
33876
|
-
logInfo(`Device ${udid} is now paired`);
|
|
33877
|
-
return true;
|
|
33878
|
-
}
|
|
33879
|
-
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
33880
|
-
}
|
|
33881
|
-
throw new Error(`Timeout waiting for device pairing after ${timeout2}ms`);
|
|
33882
|
-
}
|
|
33883
|
-
async function pair(udid) {
|
|
33884
|
-
logTask(`Initiating pairing for device ${udid}`);
|
|
33885
|
-
try {
|
|
33886
|
-
const result = await runIDeviceTool("idevicepair", ["-u", udid, "pair"]);
|
|
33887
|
-
if (!result) {
|
|
33888
|
-
return false;
|
|
33889
|
-
}
|
|
33890
|
-
return result.stdout.toLowerCase().includes("success");
|
|
33891
|
-
} catch (error) {
|
|
33892
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
33893
|
-
if (errorMsg.includes("Please accept the trust dialog")) {
|
|
33894
|
-
return false;
|
|
33895
|
-
}
|
|
33896
|
-
throw error;
|
|
33897
|
-
}
|
|
33898
|
-
}
|
|
33899
|
-
async function unpair(udid) {
|
|
33900
|
-
logTask(`Un-pairing device ${udid}`);
|
|
33901
|
-
try {
|
|
33902
|
-
const result = await runIDeviceTool("idevicepair", ["-u", udid, "unpair"]);
|
|
33903
|
-
if (!result) {
|
|
33904
|
-
return false;
|
|
33905
|
-
}
|
|
33906
|
-
return result.stdout.toLowerCase().includes("success");
|
|
33907
|
-
} catch {
|
|
33908
|
-
return false;
|
|
33909
|
-
}
|
|
33910
|
-
}
|
|
33911
|
-
|
|
33912
|
-
// src/logic/actions/install.ts
|
|
33913
|
-
async function installLocalApp(ipaPath, udid) {
|
|
33914
|
-
logTask(`Installing app ${ipaPath} on device ${udid}`);
|
|
33915
|
-
await runIDeviceTool("ideviceinstaller", ["-u", udid, "install", ipaPath]);
|
|
33916
|
-
}
|
|
33917
|
-
async function installManagedApp(ipaPath, udid, options) {
|
|
33918
|
-
await installLocalApp(ipaPath, udid);
|
|
33919
|
-
const client = await createMDMClient();
|
|
33920
|
-
logTask("Installing app via MDM for management takeover");
|
|
33921
|
-
if (client) {
|
|
33922
|
-
client.installApp(udid, options);
|
|
33923
|
-
}
|
|
33924
|
-
}
|
|
33925
|
-
async function uninstallApp(bundleId, udid) {
|
|
33926
|
-
logTask(`Uninstalling app ${bundleId} from device ${udid}`);
|
|
33927
|
-
if (!await isPaired(udid)) {
|
|
33928
|
-
await waitForPairing(udid, 1e4);
|
|
33929
|
-
}
|
|
33930
|
-
await runIDeviceTool("ideviceinstaller", ["-u", udid, "uninstall", bundleId]);
|
|
33931
|
-
}
|
|
33932
|
-
async function listApps(udid) {
|
|
33933
|
-
logTask(`Listing apps on device ${udid}`);
|
|
33934
|
-
if (!await isPaired(udid)) {
|
|
33935
|
-
await waitForPairing(udid, 1e4);
|
|
33936
|
-
}
|
|
33937
|
-
try {
|
|
33938
|
-
const result = await runIDeviceTool("ideviceinstaller", ["-u", udid, "list"]);
|
|
33939
|
-
if (!result) {
|
|
33940
|
-
return [];
|
|
33941
|
-
}
|
|
33942
|
-
const { stdout } = result;
|
|
33943
|
-
return parseAppList(stdout);
|
|
33944
|
-
} catch {
|
|
33945
|
-
return [];
|
|
33946
|
-
}
|
|
33947
|
-
}
|
|
33948
|
-
async function isAppInstalled(bundleId, udid) {
|
|
33949
|
-
logTask(`Checking if app ${bundleId} is installed on device ${udid}`);
|
|
33950
|
-
const apps = await listApps(udid);
|
|
33951
|
-
return apps.some((app) => app.bundleId === bundleId);
|
|
33952
|
-
}
|
|
33953
|
-
async function launchApp(bundleId, _args, udid) {
|
|
33954
|
-
logTask(`Launching app ${bundleId} on device ${udid}`);
|
|
33955
|
-
}
|
|
33956
|
-
|
|
33957
|
-
// src/logic/actions/proxy.ts
|
|
33958
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
33959
|
-
import { join as join8 } from "node:path";
|
|
33960
|
-
function startPortForward(localPort, devicePort, udid, startupTimeout = 500) {
|
|
33961
|
-
return new Promise((resolve2, reject) => {
|
|
33962
|
-
logTask(`Starting port forward ${localPort} -> ${devicePort} for device ${udid}`);
|
|
33963
|
-
const binPath = getResourcesBinPath();
|
|
33964
|
-
const ext = process.platform === "win32" ? ".exe" : "";
|
|
33965
|
-
const toolPath = binPath ? join8(binPath, `iproxy${ext}`) : `iproxy${ext}`;
|
|
33966
|
-
const spawnOptions = {
|
|
33967
|
-
windowsHide: true,
|
|
33968
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
33969
|
-
};
|
|
33970
|
-
if (binPath) {
|
|
33971
|
-
spawnOptions.cwd = binPath;
|
|
33972
|
-
}
|
|
33973
|
-
logInfo(`Spawning iproxy with path: ${toolPath}`);
|
|
33974
|
-
logInfo(`Arguments: ${[localPort.toString(), devicePort.toString(), "-u", udid].join(" ")}`);
|
|
33975
|
-
logInfo(`Options: ${JSON.stringify(spawnOptions)}`);
|
|
33976
|
-
const child = spawn2(
|
|
33977
|
-
toolPath,
|
|
33978
|
-
[localPort.toString(), devicePort.toString(), "-u", udid],
|
|
33979
|
-
spawnOptions
|
|
33980
|
-
);
|
|
33981
|
-
let hasResolved = false;
|
|
33982
|
-
let stderrOutput = "";
|
|
33983
|
-
child.stdout?.on("data", (data) => {
|
|
33984
|
-
logTask(`${data.toString()}`);
|
|
33985
|
-
});
|
|
33986
|
-
child.stderr?.on("data", (data) => {
|
|
33987
|
-
logError(`${data.toString()}`);
|
|
33988
|
-
const msg = data.toString();
|
|
33989
|
-
stderrOutput += msg;
|
|
33990
|
-
if (msg.toLowerCase().includes("error") && !hasResolved) {
|
|
33991
|
-
hasResolved = true;
|
|
33992
|
-
child.kill();
|
|
33993
|
-
reject(new Error(`Port forwarding failed: ${msg}`));
|
|
33994
|
-
}
|
|
33995
|
-
});
|
|
33996
|
-
child.on("error", (error) => {
|
|
33997
|
-
if (!hasResolved) {
|
|
33998
|
-
hasResolved = true;
|
|
33999
|
-
reject(new Error(`Failed to start iproxy: ${error.message}`));
|
|
34000
|
-
}
|
|
34001
|
-
});
|
|
34002
|
-
child.on("exit", (code) => {
|
|
34003
|
-
if (!hasResolved) {
|
|
34004
|
-
hasResolved = true;
|
|
34005
|
-
if (code !== 0 && code !== null) {
|
|
34006
|
-
reject(new Error(`iproxy exited with code ${code}: ${stderrOutput}`));
|
|
34007
|
-
}
|
|
34008
|
-
}
|
|
34009
|
-
});
|
|
34010
|
-
setTimeout(() => {
|
|
34011
|
-
if (!hasResolved) {
|
|
34012
|
-
hasResolved = true;
|
|
34013
|
-
logTask(`Port forward started: ${localPort} -> ${devicePort} for device ${udid}`);
|
|
34014
|
-
resolve2({
|
|
34015
|
-
result: {
|
|
34016
|
-
localPort,
|
|
34017
|
-
devicePort
|
|
34018
|
-
},
|
|
34019
|
-
process: child
|
|
34020
|
-
});
|
|
34021
|
-
}
|
|
34022
|
-
}, startupTimeout);
|
|
34023
|
-
});
|
|
34024
|
-
}
|
|
34025
|
-
function killPortForwardProcess(process2) {
|
|
34026
|
-
if (process2 && !process2.killed) {
|
|
34027
|
-
logTask("Killing port forward process");
|
|
34028
|
-
process2.kill();
|
|
34029
|
-
}
|
|
34030
|
-
}
|
|
34031
|
-
|
|
34032
|
-
// src/logic/appleDeviceKit.ts
|
|
34033
|
-
var AppleDeviceKit = class {
|
|
34034
|
-
constructor(udid, logicalPort) {
|
|
34035
|
-
this.logicalPort = logicalPort;
|
|
34036
|
-
this.deviceId = udid;
|
|
34037
|
-
logInfo(
|
|
34038
|
-
`AppleDeviceKit initialized for device: ${this.deviceId}, logical port: ${this.logicalPort}`
|
|
34039
|
-
);
|
|
34040
34026
|
}
|
|
34041
34027
|
deviceId;
|
|
34042
34028
|
proxyProcess = null;
|
|
34043
34029
|
localDevicePort = null;
|
|
34044
34030
|
isDisposed = false;
|
|
34031
|
+
iosCli;
|
|
34045
34032
|
static setResourcesDir(dir) {
|
|
34046
34033
|
setResourcesDir(dir);
|
|
34047
34034
|
}
|
|
@@ -34062,7 +34049,7 @@ var AppleDeviceKit = class {
|
|
|
34062
34049
|
}
|
|
34063
34050
|
async info() {
|
|
34064
34051
|
this.ensureNotDisposed();
|
|
34065
|
-
return info(this.deviceId);
|
|
34052
|
+
return info(this.deviceId, this.iosCli);
|
|
34066
34053
|
}
|
|
34067
34054
|
/**
|
|
34068
34055
|
* Check if device is paired/trusted with this computer
|
|
@@ -34214,13 +34201,13 @@ var AppleDeviceKit = class {
|
|
|
34214
34201
|
* Wipe the device
|
|
34215
34202
|
*/
|
|
34216
34203
|
async wipe() {
|
|
34217
|
-
return wipeDevice(this.deviceId);
|
|
34204
|
+
return wipeDevice(this.deviceId, this.iosCli);
|
|
34218
34205
|
}
|
|
34219
|
-
removeProfile(profileIdentifier) {
|
|
34220
|
-
return removeProfile(profileIdentifier, this.deviceId);
|
|
34206
|
+
async removeProfile(profileIdentifier) {
|
|
34207
|
+
return removeProfile(profileIdentifier, this.deviceId, this.iosCli);
|
|
34221
34208
|
}
|
|
34222
|
-
listProfiles() {
|
|
34223
|
-
return listProfiles(this.deviceId);
|
|
34209
|
+
async listProfiles() {
|
|
34210
|
+
return listProfiles(this.deviceId, this.iosCli);
|
|
34224
34211
|
}
|
|
34225
34212
|
/**
|
|
34226
34213
|
* Activate the device using the activation flow.
|
|
@@ -34233,7 +34220,7 @@ var AppleDeviceKit = class {
|
|
|
34233
34220
|
*/
|
|
34234
34221
|
async activate() {
|
|
34235
34222
|
this.ensureNotDisposed();
|
|
34236
|
-
const flow = new ActivationFlow();
|
|
34223
|
+
const flow = new ActivationFlow(this.iosCli);
|
|
34237
34224
|
return await flow.run(this.deviceId);
|
|
34238
34225
|
}
|
|
34239
34226
|
/**
|