@mcesystems/apple-kit 1.0.60 → 1.0.61
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 +50 -224
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +50 -224
- package/dist/index.mjs.map +3 -3
- package/dist/types/logic/actions/device.d.ts +4 -0
- 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 +0 -3
- package/dist/types/logic/activationFlow.d.ts.map +1 -1
- package/dist/types/logic/appleDeviceKit.d.ts +7 -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.mjs
CHANGED
|
@@ -33389,6 +33389,17 @@ function parseWifiEapType(value) {
|
|
|
33389
33389
|
// src/logic/iosCli.ts
|
|
33390
33390
|
import { spawn } from "node:child_process";
|
|
33391
33391
|
import { join as join5 } from "node:path";
|
|
33392
|
+
var ios;
|
|
33393
|
+
function iosCli() {
|
|
33394
|
+
const iosBinaryPath = resolveIosBinaryPath();
|
|
33395
|
+
if (!iosBinaryPath) {
|
|
33396
|
+
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
33397
|
+
}
|
|
33398
|
+
return createIosCli(iosBinaryPath);
|
|
33399
|
+
}
|
|
33400
|
+
if (!ios) {
|
|
33401
|
+
ios = iosCli();
|
|
33402
|
+
}
|
|
33392
33403
|
function runIosCommand(iosBinaryPath, args) {
|
|
33393
33404
|
return new Promise((resolve2, reject) => {
|
|
33394
33405
|
const child = spawn(iosBinaryPath, args, {
|
|
@@ -33583,11 +33594,11 @@ function createIosCli(iosBinaryPath) {
|
|
|
33583
33594
|
}
|
|
33584
33595
|
};
|
|
33585
33596
|
}
|
|
33597
|
+
var iosCli_default = ios;
|
|
33586
33598
|
|
|
33587
33599
|
// src/logic/activationFlow.ts
|
|
33588
33600
|
var DEFAULT_RETRIES = 150;
|
|
33589
33601
|
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
33590
|
-
var MCE_MDM_PROFILE_PREFIX = "com.mce.mdm";
|
|
33591
33602
|
var ActivationFlow = class {
|
|
33592
33603
|
iosCli;
|
|
33593
33604
|
mdmClientPromise;
|
|
@@ -33601,38 +33612,12 @@ var ActivationFlow = class {
|
|
|
33601
33612
|
}
|
|
33602
33613
|
async run(udid) {
|
|
33603
33614
|
logTask(`Starting activation flow for device ${udid}`);
|
|
33604
|
-
|
|
33605
|
-
if (activationState === "Activated") {
|
|
33606
|
-
const isEnrolled = await this.isEnrolledToMceMdm(udid);
|
|
33607
|
-
if (isEnrolled) {
|
|
33608
|
-
logInfo("Device already activated and enrolled to MCE MDM. Skipping activation flow.");
|
|
33609
|
-
return async () => {
|
|
33610
|
-
};
|
|
33611
|
-
}
|
|
33612
|
-
} else if (activationState) {
|
|
33613
|
-
logInfo(`Activation state is ${activationState}, activating device`);
|
|
33614
|
-
await this.retryActivateCommand("activate device", () => this.iosCli.activate(udid));
|
|
33615
|
-
} else {
|
|
33616
|
-
await this.retryIosCommand("wipe", () => this.iosCli.wipe(udid));
|
|
33617
|
-
return void 0;
|
|
33618
|
-
}
|
|
33615
|
+
await this.retryActivateCommand("activate device", () => this.iosCli.activate(udid));
|
|
33619
33616
|
const wifiProfileIdentifier = await this.installWifiProfile(udid);
|
|
33620
33617
|
await this.installMdmProfile(udid);
|
|
33621
33618
|
await this.retryIosCommand("skip steps", () => this.iosCli.skipSteps(udid));
|
|
33622
33619
|
return () => this.removeWifiProfile(udid, wifiProfileIdentifier);
|
|
33623
33620
|
}
|
|
33624
|
-
async isEnrolledToMceMdm(udid) {
|
|
33625
|
-
const profiles = await this.listProfiles(udid);
|
|
33626
|
-
return profiles.some((profile) => isMceMdmProfile(profile));
|
|
33627
|
-
}
|
|
33628
|
-
async listProfiles(udid) {
|
|
33629
|
-
const result = await this.retry(
|
|
33630
|
-
"list profiles",
|
|
33631
|
-
() => this.iosCli.listProfiles(udid),
|
|
33632
|
-
(response) => response.raw.exitCode === 0
|
|
33633
|
-
);
|
|
33634
|
-
return result.profiles;
|
|
33635
|
-
}
|
|
33636
33621
|
async installWifiProfile(udid) {
|
|
33637
33622
|
const wifiProfile = await generateWifiProfileFromEnv();
|
|
33638
33623
|
if (!wifiProfile) {
|
|
@@ -33647,14 +33632,6 @@ var ActivationFlow = class {
|
|
|
33647
33632
|
await removeTempFile(wifiProfilePath, "wifi profile");
|
|
33648
33633
|
return wifiProfileIdentifier;
|
|
33649
33634
|
}
|
|
33650
|
-
async getActivationState(udid) {
|
|
33651
|
-
const infoResult = await this.retryIosCommand("device info", () => this.iosCli.info(udid));
|
|
33652
|
-
const activationState = getActivationState2(infoResult.output);
|
|
33653
|
-
if (!activationState) {
|
|
33654
|
-
logInfo("Activation state not found");
|
|
33655
|
-
}
|
|
33656
|
-
return activationState;
|
|
33657
|
-
}
|
|
33658
33635
|
async installMdmProfile(udid) {
|
|
33659
33636
|
const client = await this.requireMdmClient();
|
|
33660
33637
|
logTask("Installing MDM enrollment profile");
|
|
@@ -33696,7 +33673,7 @@ var ActivationFlow = class {
|
|
|
33696
33673
|
async retryActivateCommand(label, command) {
|
|
33697
33674
|
return this.retry(label, command, (result) => isActivationSuccess(result));
|
|
33698
33675
|
}
|
|
33699
|
-
async retry(label, command, isSuccess, retries = DEFAULT_RETRIES, retryDelayMs = DEFAULT_RETRY_DELAY_MS) {
|
|
33676
|
+
async retry(label, command, isSuccess, retries = +(process.env.DEFAULT_RETRIES ?? DEFAULT_RETRIES), retryDelayMs = +(process.env.DEFAULT_RETRY_DELAY_MS ?? DEFAULT_RETRY_DELAY_MS)) {
|
|
33700
33677
|
for (let attempt = 0; attempt < retries; attempt += 1) {
|
|
33701
33678
|
try {
|
|
33702
33679
|
const result = await command();
|
|
@@ -33746,25 +33723,6 @@ function resolveIosBinaryPath() {
|
|
|
33746
33723
|
const binaryName = platform === "win32" ? "ios.exe" : "ios";
|
|
33747
33724
|
return join6(getResourcesBinPath(), binaryName);
|
|
33748
33725
|
}
|
|
33749
|
-
function getActivationState2(output) {
|
|
33750
|
-
if (!Array.isArray(output) || output.length === 0) {
|
|
33751
|
-
return void 0;
|
|
33752
|
-
}
|
|
33753
|
-
for (const item of output) {
|
|
33754
|
-
if (!item || typeof item !== "object") {
|
|
33755
|
-
continue;
|
|
33756
|
-
}
|
|
33757
|
-
if ("ActivationState" in item) {
|
|
33758
|
-
const value = item.ActivationState;
|
|
33759
|
-
return typeof value === "string" ? value : void 0;
|
|
33760
|
-
}
|
|
33761
|
-
if ("activationState" in item) {
|
|
33762
|
-
const value = item.activationState;
|
|
33763
|
-
return typeof value === "string" ? value : void 0;
|
|
33764
|
-
}
|
|
33765
|
-
}
|
|
33766
|
-
return void 0;
|
|
33767
|
-
}
|
|
33768
33726
|
function isActivationSuccess(result) {
|
|
33769
33727
|
if (result.exitCode !== 0) {
|
|
33770
33728
|
return false;
|
|
@@ -33775,19 +33733,6 @@ function isActivationSuccess(result) {
|
|
|
33775
33733
|
const hasFatal = result.logMessages.some((log) => log.level === "error");
|
|
33776
33734
|
return !hasFatal;
|
|
33777
33735
|
}
|
|
33778
|
-
function isMceMdmProfile(profile) {
|
|
33779
|
-
const identifier = profile.Identifier;
|
|
33780
|
-
if (!identifier) {
|
|
33781
|
-
return false;
|
|
33782
|
-
}
|
|
33783
|
-
if (!identifier.startsWith(MCE_MDM_PROFILE_PREFIX)) {
|
|
33784
|
-
return false;
|
|
33785
|
-
}
|
|
33786
|
-
if (profile.Manifest?.IsActive === false) {
|
|
33787
|
-
return false;
|
|
33788
|
-
}
|
|
33789
|
-
return true;
|
|
33790
|
-
}
|
|
33791
33736
|
|
|
33792
33737
|
// src/logic/dataParser.ts
|
|
33793
33738
|
function parsePlistOutput(output) {
|
|
@@ -33854,13 +33799,34 @@ async function info(udid) {
|
|
|
33854
33799
|
if (!iosBinaryPath) {
|
|
33855
33800
|
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
33856
33801
|
}
|
|
33857
|
-
const
|
|
33858
|
-
const result = await
|
|
33802
|
+
const iosCli2 = createIosCli(iosBinaryPath);
|
|
33803
|
+
const result = await iosCli2.info(udid);
|
|
33859
33804
|
if (!result) {
|
|
33860
33805
|
throw new Error("Failed to get device info");
|
|
33861
33806
|
}
|
|
33862
33807
|
return result.output[0];
|
|
33863
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
|
+
}
|
|
33864
33830
|
|
|
33865
33831
|
// src/logic/actions/pair.ts
|
|
33866
33832
|
async function isPaired(udid) {
|
|
@@ -33984,160 +33950,8 @@ async function isAppInstalled(bundleId, udid) {
|
|
|
33984
33950
|
const apps = await listApps(udid);
|
|
33985
33951
|
return apps.some((app) => app.bundleId === bundleId);
|
|
33986
33952
|
}
|
|
33987
|
-
async function
|
|
33988
|
-
try {
|
|
33989
|
-
logInfo("Attempting to wake device screen...");
|
|
33990
|
-
await runIDeviceTool("ideviceinfo", ["-u", udid, "-k", "DeviceName"]);
|
|
33991
|
-
try {
|
|
33992
|
-
await runIDeviceTool("ideviceinfo", ["-u", udid, "-k", "ActivationState"]);
|
|
33993
|
-
} catch {
|
|
33994
|
-
}
|
|
33995
|
-
try {
|
|
33996
|
-
await runIDeviceTool("idevicepair", ["-u", udid, "validate"]);
|
|
33997
|
-
} catch {
|
|
33998
|
-
}
|
|
33999
|
-
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
34000
|
-
logInfo("Device wake attempt completed");
|
|
34001
|
-
} catch (error) {
|
|
34002
|
-
logInfo(
|
|
34003
|
-
`Device wake attempt failed (non-critical): ${error instanceof Error ? error.message : String(error)}`
|
|
34004
|
-
);
|
|
34005
|
-
}
|
|
34006
|
-
}
|
|
34007
|
-
async function launchApp(bundleId, args, udid) {
|
|
33953
|
+
async function launchApp(bundleId, _args, udid) {
|
|
34008
33954
|
logTask(`Launching app ${bundleId} on device ${udid}`);
|
|
34009
|
-
if (!await isPaired(udid)) {
|
|
34010
|
-
await waitForPairing(udid, 1e4);
|
|
34011
|
-
}
|
|
34012
|
-
const installed = await isAppInstalled(bundleId, udid);
|
|
34013
|
-
if (!installed) {
|
|
34014
|
-
throw new Error(
|
|
34015
|
-
`App "${bundleId}" is not installed on the device. Install the app first or verify the bundle identifier is correct.`
|
|
34016
|
-
);
|
|
34017
|
-
}
|
|
34018
|
-
await wakeDevice(udid);
|
|
34019
|
-
try {
|
|
34020
|
-
logInfo(`Attempting to launch ${bundleId} using idevicedebug...`);
|
|
34021
|
-
const result = await runIDeviceTool("idevicedebug", ["-u", udid, "run", bundleId, ...args]);
|
|
34022
|
-
const output = (result?.stdout ?? "") + (result?.stderr ?? "");
|
|
34023
|
-
if (output.trim()) {
|
|
34024
|
-
logInfo(`idevicedebug output: ${output.substring(0, 200)}`);
|
|
34025
|
-
}
|
|
34026
|
-
if (output.toLowerCase().includes("could not start") && output.toLowerCase().includes("debugserver")) {
|
|
34027
|
-
logInfo("idevicedebug requires debugserver, falling back to pymobiledevice3...");
|
|
34028
|
-
throw new Error("debugserver_not_available");
|
|
34029
|
-
}
|
|
34030
|
-
logInfo(`App ${bundleId} launched successfully using idevicedebug`);
|
|
34031
|
-
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
34032
|
-
return;
|
|
34033
|
-
} catch (error) {
|
|
34034
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
34035
|
-
if (errorMsg.includes("debugserver_not_available") || errorMsg.toLowerCase().includes("could not start") || errorMsg.toLowerCase().includes("debugserver")) {
|
|
34036
|
-
logInfo("idevicedebug failed, trying pymobiledevice3 for iOS 17+...");
|
|
34037
|
-
await launchAppWithPymobiledevice3(bundleId, args, udid);
|
|
34038
|
-
return;
|
|
34039
|
-
}
|
|
34040
|
-
throw error;
|
|
34041
|
-
}
|
|
34042
|
-
}
|
|
34043
|
-
async function launchAppWithPymobiledevice3(bundleId, args, udid) {
|
|
34044
|
-
logTask(`Launching app ${bundleId} using pymobiledevice3`);
|
|
34045
|
-
const { exec } = await import("node:child_process");
|
|
34046
|
-
const { promisify: promisify3 } = await import("node:util");
|
|
34047
|
-
const execAsync2 = promisify3(exec);
|
|
34048
|
-
try {
|
|
34049
|
-
let cmdArgs = [
|
|
34050
|
-
"-m",
|
|
34051
|
-
"pymobiledevice3",
|
|
34052
|
-
"developer",
|
|
34053
|
-
"dvt",
|
|
34054
|
-
"launch",
|
|
34055
|
-
"--udid",
|
|
34056
|
-
udid,
|
|
34057
|
-
"--kill-existing",
|
|
34058
|
-
// Kill existing instance to bring app to foreground
|
|
34059
|
-
bundleId,
|
|
34060
|
-
...args
|
|
34061
|
-
];
|
|
34062
|
-
let command = `python ${cmdArgs.map((a) => `"${a}"`).join(" ")}`;
|
|
34063
|
-
logInfo(`Executing: ${command}`);
|
|
34064
|
-
const result = await execAsync2(command, {
|
|
34065
|
-
windowsHide: true,
|
|
34066
|
-
encoding: "utf8",
|
|
34067
|
-
timeout: 3e4
|
|
34068
|
-
// 30 second timeout
|
|
34069
|
-
});
|
|
34070
|
-
const output = result.stdout.toString() + result.stderr.toString();
|
|
34071
|
-
if (output.trim()) {
|
|
34072
|
-
logInfo(`pymobiledevice3 output: ${output.substring(0, 200)}`);
|
|
34073
|
-
}
|
|
34074
|
-
if (output.toLowerCase().includes("developer mode") && output.toLowerCase().includes("not enabled")) {
|
|
34075
|
-
throw new Error(
|
|
34076
|
-
"Developer Mode is not enabled on the device.\nPlease enable it: Settings \u2192 Privacy & Security \u2192 Developer Mode \u2192 Enable"
|
|
34077
|
-
);
|
|
34078
|
-
}
|
|
34079
|
-
if (output.toLowerCase().includes("tunneld") && (output.toLowerCase().includes("unable to connect") || output.toLowerCase().includes("invalidserviceerror"))) {
|
|
34080
|
-
logInfo("Tunnel required, retrying with tunnel option...");
|
|
34081
|
-
cmdArgs = [
|
|
34082
|
-
"-m",
|
|
34083
|
-
"pymobiledevice3",
|
|
34084
|
-
"developer",
|
|
34085
|
-
"dvt",
|
|
34086
|
-
"launch",
|
|
34087
|
-
"--udid",
|
|
34088
|
-
udid,
|
|
34089
|
-
"--tunnel",
|
|
34090
|
-
udid,
|
|
34091
|
-
// Use UDID for tunnel
|
|
34092
|
-
"--kill-existing",
|
|
34093
|
-
bundleId,
|
|
34094
|
-
...args
|
|
34095
|
-
];
|
|
34096
|
-
command = `python ${cmdArgs.map((a) => `"${a}"`).join(" ")}`;
|
|
34097
|
-
logInfo(`Retrying with tunnel: ${command}`);
|
|
34098
|
-
try {
|
|
34099
|
-
const retryResult = await execAsync2(command, {
|
|
34100
|
-
windowsHide: true,
|
|
34101
|
-
encoding: "utf8",
|
|
34102
|
-
timeout: 3e4
|
|
34103
|
-
});
|
|
34104
|
-
const retryOutput = retryResult.stdout.toString() + retryResult.stderr.toString();
|
|
34105
|
-
if (retryOutput.trim()) {
|
|
34106
|
-
logInfo(`pymobiledevice3 retry output: ${retryOutput.substring(0, 200)}`);
|
|
34107
|
-
}
|
|
34108
|
-
if (retryOutput.toLowerCase().includes("tunneld") && retryOutput.toLowerCase().includes("unable to connect")) {
|
|
34109
|
-
throw new Error(
|
|
34110
|
-
"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."
|
|
34111
|
-
);
|
|
34112
|
-
}
|
|
34113
|
-
logInfo(`App ${bundleId} launched successfully using pymobiledevice3 with tunnel`);
|
|
34114
|
-
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
34115
|
-
return;
|
|
34116
|
-
} catch {
|
|
34117
|
-
throw new Error(
|
|
34118
|
-
"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."
|
|
34119
|
-
);
|
|
34120
|
-
}
|
|
34121
|
-
}
|
|
34122
|
-
if (output.toLowerCase().includes("not found") || output.toLowerCase().includes("command not found") || output.toLowerCase().includes("cannot find")) {
|
|
34123
|
-
throw new Error(
|
|
34124
|
-
"pymobiledevice3 is not installed.\nInstall it with: python -m pip install --user pymobiledevice3"
|
|
34125
|
-
);
|
|
34126
|
-
}
|
|
34127
|
-
if (output.toLowerCase().includes("error") && !output.toLowerCase().includes("warning")) {
|
|
34128
|
-
logInfo(`Warning: pymobiledevice3 reported errors: ${output.substring(0, 300)}`);
|
|
34129
|
-
}
|
|
34130
|
-
logInfo(`App ${bundleId} launched successfully using pymobiledevice3`);
|
|
34131
|
-
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
34132
|
-
} catch (error) {
|
|
34133
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
34134
|
-
if (errorMsg.includes("not found") || errorMsg.includes("command not found") || errorMsg.includes("cannot find") || errorMsg.includes("No module named")) {
|
|
34135
|
-
throw new Error(
|
|
34136
|
-
"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."
|
|
34137
|
-
);
|
|
34138
|
-
}
|
|
34139
|
-
throw error;
|
|
34140
|
-
}
|
|
34141
33955
|
}
|
|
34142
33956
|
|
|
34143
33957
|
// src/logic/actions/proxy.ts
|
|
@@ -34396,6 +34210,18 @@ var AppleDeviceKit = class {
|
|
|
34396
34210
|
this.ensureNotDisposed();
|
|
34397
34211
|
return getActivationState(this.deviceId);
|
|
34398
34212
|
}
|
|
34213
|
+
/**
|
|
34214
|
+
* Wipe the device
|
|
34215
|
+
*/
|
|
34216
|
+
async wipe() {
|
|
34217
|
+
return wipeDevice(this.deviceId);
|
|
34218
|
+
}
|
|
34219
|
+
removeProfile(profileIdentifier) {
|
|
34220
|
+
return removeProfile(profileIdentifier, this.deviceId);
|
|
34221
|
+
}
|
|
34222
|
+
listProfiles() {
|
|
34223
|
+
return listProfiles(this.deviceId);
|
|
34224
|
+
}
|
|
34399
34225
|
/**
|
|
34400
34226
|
* Activate the device using the activation flow.
|
|
34401
34227
|
*
|