@mcesystems/apple-kit 1.0.96 → 1.0.97

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 CHANGED
@@ -38620,31 +38620,34 @@ function escapeXml(str) {
38620
38620
  var { logInfo: logInfo5 } = createLoggers("apple-kit:wifi-profile");
38621
38621
  async function generateWifiProfile(config, options) {
38622
38622
  const {
38623
- ssid,
38624
38623
  encryptionType,
38625
38624
  password,
38626
38625
  autoJoin = true,
38627
38626
  organizationName = "MCE Systems",
38628
- displayName = `WiFi - ${ssid}`,
38629
38627
  enterprise = false,
38630
38628
  username,
38631
38629
  acceptAnyCertificate = true
38632
38630
  } = config;
38631
+ const resolvedSsid = config.ssid ?? process.env.WIFI_SSID ?? "";
38632
+ if (!resolvedSsid) {
38633
+ throw new Error("WiFi SSID is required. Set config.ssid or WIFI_SSID environment variable.");
38634
+ }
38633
38635
  const profileUuid = (0, import_node_crypto2.randomUUID)().toUpperCase();
38634
38636
  const payloadUuid = (0, import_node_crypto2.randomUUID)().toUpperCase();
38635
- const profileId = `com.mce.wifi.${ssid.replace(/[^a-zA-Z0-9]/g, "")}.${Date.now()}`;
38637
+ const profileId = `com.mce.wifi.${resolvedSsid.replace(/[^a-zA-Z0-9]/g, "")}.${Date.now()}`;
38636
38638
  const payloadId = `${profileId}.payload`;
38637
- logInfo5(`Generating WiFi profile for SSID: ${ssid}, encryption: ${encryptionType}`);
38639
+ const displayNameToUse = config.displayName ?? `WiFi - ${resolvedSsid}`;
38640
+ logInfo5(`Generating WiFi profile for SSID: ${resolvedSsid}, encryption: ${encryptionType}`);
38638
38641
  const templateName = enterprise ? "wifi-enterprise.xml" : "wifi-standard.xml";
38639
38642
  const template = await loadTemplate(templateName, options?.plistDir);
38640
38643
  const variables = {
38641
- ssid: config.ssid ?? process.env.WIFI_SSID,
38644
+ ssid: resolvedSsid,
38642
38645
  password: config.password ?? process.env.WIFI_PASSWORD,
38643
38646
  encryptionType: parseWifiEncryptionType(config.encryptionType ?? process.env.WIFI_ENCRYPTION),
38644
38647
  autoJoin: autoJoin ? "true" : "false",
38645
38648
  hiddenNetwork: config.hiddenNetwork ?? process.env.WIFI_HIDDEN === "true",
38646
38649
  organizationName,
38647
- displayName,
38650
+ displayName: displayNameToUse,
38648
38651
  profileId,
38649
38652
  profileUuid,
38650
38653
  payloadId,
@@ -38739,10 +38742,9 @@ function isAbortError(error) {
38739
38742
  return error instanceof DOMException && error.name === "AbortError";
38740
38743
  }
38741
38744
  var ActivationFlow = class {
38742
- constructor(udid, iosClient, deviceActions) {
38745
+ constructor(udid, iosClient) {
38743
38746
  this.udid = udid;
38744
38747
  this.iosClient = iosClient;
38745
- this.deviceActions = deviceActions;
38746
38748
  setNamespace4(`${udid}`);
38747
38749
  }
38748
38750
  mdmClient;
@@ -38778,14 +38780,6 @@ var ActivationFlow = class {
38778
38780
  signal
38779
38781
  );
38780
38782
  throwIfAborted2(signal);
38781
- const expectedSsid = config.wifiProfileConfig?.ssid;
38782
- if (expectedSsid) {
38783
- if (await this.deviceActions.isWifiConnected(15e3) !== expectedSsid) {
38784
- throw new Error(
38785
- `WiFi profile not installed correctly, expected ${expectedSsid} but got ${await this.deviceActions.isWifiConnected(15e3)}`
38786
- );
38787
- }
38788
- }
38789
38783
  await this.installMdmProfile(events, signal);
38790
38784
  throwIfAborted2(signal);
38791
38785
  }
@@ -39189,45 +39183,59 @@ var IosClient = class {
39189
39183
  return this.runIosCommand(["devmode", "enable", "--udid", this.udid]);
39190
39184
  }
39191
39185
  async tunnelStart(userspace = false, pairRecordPath) {
39192
- const args = ["tunnel", "start", `--pair-record-path=${pairRecordPath ?? "default"}`, "--udid", this.udid, ...userspace ? ["--userspace"] : []];
39193
- logDetail(`Spawning tunnel: ${this.iosPath} ${args.join(" ")}`);
39194
- const child = (0, import_node_child_process.spawn)(this.iosPath, args, {
39195
- windowsHide: true,
39196
- env: {
39197
- ...process.env,
39198
- USBMUXD_SOCKET_ADDRESS: this.usbmuxdAddress
39199
- },
39200
- stdio: ["ignore", "pipe", "pipe"]
39186
+ return new Promise((resolve2, reject) => {
39187
+ const args = [
39188
+ "tunnel",
39189
+ "start",
39190
+ `--pair-record-path=${pairRecordPath ?? "default"}`,
39191
+ "--udid",
39192
+ this.udid,
39193
+ ...userspace ? ["--userspace"] : []
39194
+ ];
39195
+ logDetail(`Spawning tunnel: ${this.iosPath} ${args.join(" ")}`);
39196
+ const child = (0, import_node_child_process.spawn)(this.iosPath, args, {
39197
+ windowsHide: true,
39198
+ env: {
39199
+ ...process.env,
39200
+ USBMUXD_SOCKET_ADDRESS: this.usbmuxdAddress
39201
+ },
39202
+ stdio: ["ignore", "pipe", "pipe"]
39203
+ });
39204
+ let settled = false;
39205
+ const cleanup = () => {
39206
+ child.off("error", onError);
39207
+ child.off("exit", onExit);
39208
+ child.stdout?.off("data", onReadyCheck);
39209
+ child.stderr?.off("data", onReadyCheck);
39210
+ clearTimeout(readyTimeout);
39211
+ };
39212
+ const finish = (result) => {
39213
+ if (settled) {
39214
+ return;
39215
+ }
39216
+ settled = true;
39217
+ cleanup();
39218
+ if (result.reject) {
39219
+ reject(result.reject);
39220
+ return;
39221
+ }
39222
+ if (result.resolve !== void 0) {
39223
+ resolve2(result.resolve);
39224
+ }
39225
+ };
39226
+ const onError = (err) => finish({ reject: err });
39227
+ const onExit = (code) => finish({ reject: new Error(`Tunnel exited early (code=${code})`) });
39228
+ const onReadyCheck = (data) => {
39229
+ const line = data.toString();
39230
+ const isReady = line.includes("Tunnel server started") || line.includes("tunnel") && (line.includes("listening") || line.includes("connected"));
39231
+ if (isReady) finish({ resolve: child });
39232
+ };
39233
+ const readyTimeout = setTimeout(() => finish({ resolve: child }), 1e4);
39234
+ child.once("error", onError);
39235
+ child.once("exit", onExit);
39236
+ child.stdout?.on("data", onReadyCheck);
39237
+ child.stderr?.on("data", onReadyCheck);
39201
39238
  });
39202
- let settled = false;
39203
- const cleanup = () => {
39204
- child.off("error", onError);
39205
- child.off("exit", onExit);
39206
- child.stdout?.off("data", onReadyCheck);
39207
- child.stderr?.off("data", onReadyCheck);
39208
- clearTimeout(readyTimeout);
39209
- };
39210
- const finish = (result) => {
39211
- if (settled) return;
39212
- settled = true;
39213
- cleanup();
39214
- if (result.reject) throw result.reject;
39215
- if (result.resolve !== void 0) return result.resolve;
39216
- throw new Error("Tunnel process not found");
39217
- };
39218
- const onError = (err) => finish({ reject: err });
39219
- const onExit = (code) => finish({ reject: new Error(`Tunnel exited early (code=${code})`) });
39220
- const onReadyCheck = (data) => {
39221
- const line = data.toString();
39222
- const isReady = line.includes("Tunnel server started") || line.includes("tunnel") && (line.includes("listening") || line.includes("connected"));
39223
- if (isReady) finish({ resolve: child });
39224
- };
39225
- const readyTimeout = setTimeout(() => finish({ resolve: child }), 1e4);
39226
- child.once("error", onError);
39227
- child.once("exit", onExit);
39228
- child.stdout?.on("data", onReadyCheck);
39229
- child.stderr?.on("data", onReadyCheck);
39230
- return child;
39231
39239
  }
39232
39240
  async fsyncPull({
39233
39241
  app,
@@ -39349,9 +39357,9 @@ var AppleDeviceKit = class _AppleDeviceKit {
39349
39357
  this.iosClient = new IosClient(iosBinaryPath, udid, usbmuxdAddress);
39350
39358
  this.deviceActions = new DeviceActions(this.deviceId, this.iosClient);
39351
39359
  this.installActions = new InstallActions(this.deviceId, this.iosClient);
39352
- this.activationFlow = new ActivationFlow(this.deviceId, this.iosClient, this.deviceActions);
39360
+ this.activationFlow = new ActivationFlow(this.deviceId, this.iosClient);
39353
39361
  this.proxyActions = new ProxyActions(this.deviceId, this.iosClient);
39354
- this.ensureTunnel();
39362
+ this.getTunnelReady();
39355
39363
  }
39356
39364
  deviceId;
39357
39365
  proxyProcess = null;
@@ -39363,7 +39371,18 @@ var AppleDeviceKit = class _AppleDeviceKit {
39363
39371
  activationFlow;
39364
39372
  proxyActions;
39365
39373
  tunnelProcess = null;
39374
+ tunnelReadyPromise = null;
39366
39375
  static IOS_17_VERSION = 17;
39376
+ /**
39377
+ * Returns a promise that resolves when the tunnel is ready (or immediately if not needed).
39378
+ * Reuses the same in-flight promise so constructor and methods don't race.
39379
+ */
39380
+ getTunnelReady() {
39381
+ if (!this.tunnelReadyPromise) {
39382
+ this.tunnelReadyPromise = this.runEnsureTunnel();
39383
+ }
39384
+ return this.tunnelReadyPromise;
39385
+ }
39367
39386
  /**
39368
39387
  * Check if iOS version requires tunneling (iOS 17+)
39369
39388
  */
@@ -39381,9 +39400,15 @@ var AppleDeviceKit = class _AppleDeviceKit {
39381
39400
  }
39382
39401
  }
39383
39402
  /**
39384
- * Ensure tunnel is started for iOS 17+ devices
39403
+ * Ensure tunnel is started for iOS 17+ devices. Safe to call multiple times; reuses in-flight setup.
39385
39404
  */
39386
39405
  async ensureTunnel() {
39406
+ await this.getTunnelReady();
39407
+ }
39408
+ /**
39409
+ * Internal tunnel startup. Called once per tunnel lifecycle; getTunnelReady() caches this promise.
39410
+ */
39411
+ async runEnsureTunnel() {
39387
39412
  const requiresTunnel = await this.requiresTunneling();
39388
39413
  if (!requiresTunnel) {
39389
39414
  logDetail2(`Device ${this.deviceId} does not require tunneling (iOS < 17)`);
@@ -39396,21 +39421,28 @@ var AppleDeviceKit = class _AppleDeviceKit {
39396
39421
  }
39397
39422
  logInfo7(`Starting tunnel for device ${this.deviceId} (iOS 17+)`);
39398
39423
  try {
39399
- const pairRecordPath = (0, import_node_path5.join)(this.deviceActions.getLockdownPath() ?? "", "RemotePairing", `${this.deviceId}`);
39424
+ const pairRecordPath = (0, import_node_path5.join)(
39425
+ this.deviceActions.getLockdownPath() ?? "",
39426
+ "RemotePairing",
39427
+ `${this.deviceId}`
39428
+ );
39400
39429
  await (0, import_promises3.mkdir)(pairRecordPath, { recursive: true });
39401
39430
  const tunnelProcess = await this.iosClient.tunnelStart(true, pairRecordPath);
39402
39431
  this.tunnelProcess = tunnelProcess;
39403
39432
  tunnelProcess.on("exit", (code) => {
39404
39433
  logError3(`Tunnel process for device ${this.deviceId} exited with code ${code}`);
39405
39434
  this.tunnelProcess = null;
39435
+ this.tunnelReadyPromise = null;
39406
39436
  });
39407
39437
  tunnelProcess.on("error", (error) => {
39408
39438
  logError3(`Tunnel process error for device ${this.deviceId}: ${error.message}`);
39409
39439
  this.tunnelProcess = null;
39440
+ this.tunnelReadyPromise = null;
39410
39441
  });
39411
39442
  await new Promise((resolve2) => setTimeout(resolve2, 1e3));
39412
39443
  logInfo7(`Tunnel started successfully for device ${this.deviceId}`);
39413
39444
  } catch (error) {
39445
+ this.tunnelReadyPromise = null;
39414
39446
  logError3(
39415
39447
  `Failed to start tunnel for device ${this.deviceId}: ${error instanceof Error ? error.message : String(error)}`
39416
39448
  );
@@ -39648,9 +39680,6 @@ var AppleDeviceKit = class _AppleDeviceKit {
39648
39680
  }
39649
39681
  logInfo7(`File ${fileName} pushed to device ${this.deviceId}`);
39650
39682
  } finally {
39651
- if (requiresTunnel) {
39652
- this.stopTunnel();
39653
- }
39654
39683
  try {
39655
39684
  (0, import_node_fs5.unlinkSync)(tmpFilePath);
39656
39685
  } catch (_error) {
@@ -39687,9 +39716,6 @@ var AppleDeviceKit = class _AppleDeviceKit {
39687
39716
  logInfo7(`File ${fileName} pulled from device ${this.deviceId}`);
39688
39717
  return fileData;
39689
39718
  } finally {
39690
- if (requiresTunnel) {
39691
- this.stopTunnel();
39692
- }
39693
39719
  try {
39694
39720
  (0, import_node_fs5.unlinkSync)(tmpFilePath);
39695
39721
  } catch (_error) {