@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.js
CHANGED
|
@@ -33399,6 +33399,17 @@ function parseWifiEapType(value) {
|
|
|
33399
33399
|
// src/logic/iosCli.ts
|
|
33400
33400
|
var import_node_child_process2 = require("node:child_process");
|
|
33401
33401
|
var import_node_path5 = require("node:path");
|
|
33402
|
+
var ios;
|
|
33403
|
+
function iosCli() {
|
|
33404
|
+
const iosBinaryPath = resolveIosBinaryPath();
|
|
33405
|
+
if (!iosBinaryPath) {
|
|
33406
|
+
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
33407
|
+
}
|
|
33408
|
+
return createIosCli(iosBinaryPath);
|
|
33409
|
+
}
|
|
33410
|
+
if (!ios) {
|
|
33411
|
+
ios = iosCli();
|
|
33412
|
+
}
|
|
33402
33413
|
function runIosCommand(iosBinaryPath, args) {
|
|
33403
33414
|
return new Promise((resolve2, reject) => {
|
|
33404
33415
|
const child = (0, import_node_child_process2.spawn)(iosBinaryPath, args, {
|
|
@@ -33593,11 +33604,11 @@ function createIosCli(iosBinaryPath) {
|
|
|
33593
33604
|
}
|
|
33594
33605
|
};
|
|
33595
33606
|
}
|
|
33607
|
+
var iosCli_default = ios;
|
|
33596
33608
|
|
|
33597
33609
|
// src/logic/activationFlow.ts
|
|
33598
33610
|
var DEFAULT_RETRIES = 150;
|
|
33599
33611
|
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
33600
|
-
var MCE_MDM_PROFILE_PREFIX = "com.mce.mdm";
|
|
33601
33612
|
var ActivationFlow = class {
|
|
33602
33613
|
iosCli;
|
|
33603
33614
|
mdmClientPromise;
|
|
@@ -33611,38 +33622,12 @@ var ActivationFlow = class {
|
|
|
33611
33622
|
}
|
|
33612
33623
|
async run(udid) {
|
|
33613
33624
|
logTask(`Starting activation flow for device ${udid}`);
|
|
33614
|
-
|
|
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
|
-
}
|
|
33625
|
+
await this.retryActivateCommand("activate device", () => this.iosCli.activate(udid));
|
|
33629
33626
|
const wifiProfileIdentifier = await this.installWifiProfile(udid);
|
|
33630
33627
|
await this.installMdmProfile(udid);
|
|
33631
33628
|
await this.retryIosCommand("skip steps", () => this.iosCli.skipSteps(udid));
|
|
33632
33629
|
return () => this.removeWifiProfile(udid, wifiProfileIdentifier);
|
|
33633
33630
|
}
|
|
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;
|
|
33645
|
-
}
|
|
33646
33631
|
async installWifiProfile(udid) {
|
|
33647
33632
|
const wifiProfile = await generateWifiProfileFromEnv();
|
|
33648
33633
|
if (!wifiProfile) {
|
|
@@ -33657,14 +33642,6 @@ var ActivationFlow = class {
|
|
|
33657
33642
|
await removeTempFile(wifiProfilePath, "wifi profile");
|
|
33658
33643
|
return wifiProfileIdentifier;
|
|
33659
33644
|
}
|
|
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
33645
|
async installMdmProfile(udid) {
|
|
33669
33646
|
const client = await this.requireMdmClient();
|
|
33670
33647
|
logTask("Installing MDM enrollment profile");
|
|
@@ -33706,7 +33683,7 @@ var ActivationFlow = class {
|
|
|
33706
33683
|
async retryActivateCommand(label, command) {
|
|
33707
33684
|
return this.retry(label, command, (result) => isActivationSuccess(result));
|
|
33708
33685
|
}
|
|
33709
|
-
async retry(label, command, isSuccess, retries = DEFAULT_RETRIES, retryDelayMs = DEFAULT_RETRY_DELAY_MS) {
|
|
33686
|
+
async retry(label, command, isSuccess, retries = +(process.env.DEFAULT_RETRIES ?? DEFAULT_RETRIES), retryDelayMs = +(process.env.DEFAULT_RETRY_DELAY_MS ?? DEFAULT_RETRY_DELAY_MS)) {
|
|
33710
33687
|
for (let attempt = 0; attempt < retries; attempt += 1) {
|
|
33711
33688
|
try {
|
|
33712
33689
|
const result = await command();
|
|
@@ -33756,25 +33733,6 @@ function resolveIosBinaryPath() {
|
|
|
33756
33733
|
const binaryName = platform === "win32" ? "ios.exe" : "ios";
|
|
33757
33734
|
return (0, import_node_path6.join)(getResourcesBinPath(), binaryName);
|
|
33758
33735
|
}
|
|
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;
|
|
33777
|
-
}
|
|
33778
33736
|
function isActivationSuccess(result) {
|
|
33779
33737
|
if (result.exitCode !== 0) {
|
|
33780
33738
|
return false;
|
|
@@ -33785,19 +33743,6 @@ function isActivationSuccess(result) {
|
|
|
33785
33743
|
const hasFatal = result.logMessages.some((log) => log.level === "error");
|
|
33786
33744
|
return !hasFatal;
|
|
33787
33745
|
}
|
|
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
33746
|
|
|
33802
33747
|
// src/logic/dataParser.ts
|
|
33803
33748
|
function parsePlistOutput(output) {
|
|
@@ -33864,13 +33809,34 @@ async function info(udid) {
|
|
|
33864
33809
|
if (!iosBinaryPath) {
|
|
33865
33810
|
throw new Error("iosBinaryPath is required. Provide iosBinaryPath or resourcesDir.");
|
|
33866
33811
|
}
|
|
33867
|
-
const
|
|
33868
|
-
const result = await
|
|
33812
|
+
const iosCli2 = createIosCli(iosBinaryPath);
|
|
33813
|
+
const result = await iosCli2.info(udid);
|
|
33869
33814
|
if (!result) {
|
|
33870
33815
|
throw new Error("Failed to get device info");
|
|
33871
33816
|
}
|
|
33872
33817
|
return result.output[0];
|
|
33873
33818
|
}
|
|
33819
|
+
async function wipeDevice(udid) {
|
|
33820
|
+
if (!iosCli_default) {
|
|
33821
|
+
throw new Error("ios is not initialized");
|
|
33822
|
+
}
|
|
33823
|
+
logTask(`Wiping device ${udid}`);
|
|
33824
|
+
await iosCli_default.wipe(udid);
|
|
33825
|
+
}
|
|
33826
|
+
async function removeProfile(profileIdentifier, udid) {
|
|
33827
|
+
if (!iosCli_default) {
|
|
33828
|
+
throw new Error("ios is not initialized");
|
|
33829
|
+
}
|
|
33830
|
+
logTask(`Removing profile ${profileIdentifier} from device ${udid}`);
|
|
33831
|
+
await iosCli_default.removeProfile(udid, profileIdentifier);
|
|
33832
|
+
}
|
|
33833
|
+
async function listProfiles(udid) {
|
|
33834
|
+
if (!iosCli_default) {
|
|
33835
|
+
throw new Error("ios is not initialized");
|
|
33836
|
+
}
|
|
33837
|
+
logTask(`Listing profiles for device ${udid}`);
|
|
33838
|
+
return iosCli_default.listProfiles(udid);
|
|
33839
|
+
}
|
|
33874
33840
|
|
|
33875
33841
|
// src/logic/actions/pair.ts
|
|
33876
33842
|
async function isPaired(udid) {
|
|
@@ -33994,160 +33960,8 @@ async function isAppInstalled(bundleId, udid) {
|
|
|
33994
33960
|
const apps = await listApps(udid);
|
|
33995
33961
|
return apps.some((app) => app.bundleId === bundleId);
|
|
33996
33962
|
}
|
|
33997
|
-
async function
|
|
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) {
|
|
33963
|
+
async function launchApp(bundleId, _args, udid) {
|
|
34018
33964
|
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;
|
|
34150
|
-
}
|
|
34151
33965
|
}
|
|
34152
33966
|
|
|
34153
33967
|
// src/logic/actions/proxy.ts
|
|
@@ -34406,6 +34220,18 @@ var AppleDeviceKit = class {
|
|
|
34406
34220
|
this.ensureNotDisposed();
|
|
34407
34221
|
return getActivationState(this.deviceId);
|
|
34408
34222
|
}
|
|
34223
|
+
/**
|
|
34224
|
+
* Wipe the device
|
|
34225
|
+
*/
|
|
34226
|
+
async wipe() {
|
|
34227
|
+
return wipeDevice(this.deviceId);
|
|
34228
|
+
}
|
|
34229
|
+
removeProfile(profileIdentifier) {
|
|
34230
|
+
return removeProfile(profileIdentifier, this.deviceId);
|
|
34231
|
+
}
|
|
34232
|
+
listProfiles() {
|
|
34233
|
+
return listProfiles(this.deviceId);
|
|
34234
|
+
}
|
|
34409
34235
|
/**
|
|
34410
34236
|
* Activate the device using the activation flow.
|
|
34411
34237
|
*
|