@mcesystems/apple-kit 1.0.62 → 1.0.64

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/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # @mcesystems/apple-kit
2
2
 
3
- iOS device management toolkit using libimobiledevice command-line tools. Provides device detection, app installation/uninstallation, port forwarding, activation, and device property access.
3
+ iOS device management toolkit using libimobiledevice and go-ios command-line tools. We use both because they each provide capabilities that the other does not. The package provides app installation/uninstallation, port forwarding, activation, and device property access.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Device Detection**: Monitor iOS device connections via USB plug-and-play
8
7
  - **App Management**: Install and uninstall iOS applications (.ipa files)
9
- - **Launch Apps**: Start applications on the device
10
8
  - **Port Forwarding**: Forward local ports to device ports (for debugging, etc.)
11
- - **Device Activation**: Check and manage device activation state
9
+ - **Device Activation**: Activate devices and skip setup steps
12
10
  - **Trust/Pairing**: Handle device trust and pairing
13
11
  - **Device Info**: Access device properties (name, model, iOS version, UDID, etc.)
12
+ - **Profile Management**: List and remove configuration profiles
13
+ - **Device Wipe**: Erase device data (factory reset)
14
14
  - **Cross-platform**: Works on Windows, macOS, and Linux
15
15
 
16
16
  ## Installation
@@ -24,17 +24,20 @@ npm install @mcesystems/apple-kit
24
24
  - Node.js 18+
25
25
  - iOS device connected via USB
26
26
  - Device must be trusted/paired with the computer
27
+ - libimobiledevice tools (idevice\*)
28
+ - go-ios `ios` CLI binary
27
29
 
28
30
  ### Platform-specific Requirements
29
31
 
30
32
  #### Windows
31
33
  - libimobiledevice binaries - use the export script (see below)
34
+ - go-ios `ios.exe` (see Resources section below)
32
35
  - iTunes installed OR Apple Mobile Device Support
33
36
 
34
37
  #### macOS
35
38
  **Option 1: Use bundled binaries (recommended for distribution)**
36
39
 
37
- Use the export script to bundle binaries for your application:
40
+ Use the export script to bundle libimobiledevice for your application:
38
41
  ```bash
39
42
  # Using npx (after installing the package)
40
43
  npx export-apple-resources /path/to/your-app/resources/apple-kit
@@ -53,42 +56,35 @@ See [scripts/README.md](./scripts/README.md) for detailed prerequisites and inst
53
56
  - libimobiledevice-utils: `sudo apt install libimobiledevice-utils`
54
57
  - Tools are auto-detected from `/usr/bin` or `/usr/local/bin`
55
58
 
56
- ### Binary Resolution Order
59
+ ### Resources and Binary Resolution
57
60
 
58
- The package looks for idevice tools in this order:
59
- 1. `IDeviceBinPath` environment variable (for custom paths)
60
- 2. Bundled resources in your app's `resources/bin/{platform}/` directory
61
- 3. Homebrew paths on macOS (`/opt/homebrew/bin`, `/usr/local/bin`)
62
- 4. System PATH (for global installations)
61
+ Set a resources directory once so both toolchains can be located:
63
62
 
64
- ## Usage
63
+ ```typescript
64
+ import path from "node:path";
65
+ import { AppleDeviceKit } from "@mcesystems/apple-kit";
65
66
 
66
- ### Device Monitoring
67
+ AppleDeviceKit.setResourcesDir(path.join(process.cwd(), "resources"));
68
+ ```
67
69
 
68
- ```typescript
69
- import { DevicesMonitor } from '@mcesystems/apple-kit';
70
+ If you do not set a resources directory, make sure both toolchains are on your PATH.
70
71
 
71
- const monitor = new DevicesMonitor({
72
- logicalPortMap: {
73
- "Port_#0005.Hub_#0002": 1,
74
- "Port_#0006.Hub_#0002": 2
75
- }
76
- });
72
+ The package looks for tools in this order:
73
+ 1. `resourcesDir/ios/bin/{platform}/` (go-ios `ios` and libimobiledevice tools)
74
+ 2. Homebrew paths on macOS (`/opt/homebrew/bin`, `/usr/local/bin`)
75
+ 3. System PATH (for global installations)
77
76
 
78
- const events = await monitor.startTracking();
77
+ ## Usage
79
78
 
80
- events.on('added', async (deviceKit) => {
81
- console.log(`iOS device connected: ${deviceKit.getDeviceId()}`);
82
- const info = await deviceKit.getDeviceInfo();
83
- console.log(` ${info.deviceName} - iOS ${info.productVersion}`);
84
- });
79
+ ### List Devices (go-ios)
85
80
 
86
- events.on('removed', (deviceId, port) => {
87
- console.log(`iOS device disconnected: ${deviceId}`);
88
- });
81
+ ```typescript
82
+ import { createIosCli } from "@mcesystems/apple-kit";
89
83
 
90
- // Later: stop monitoring
91
- await monitor.stopTracking();
84
+ // Example: <resourcesDir>/ios/bin/{platform}/ios(.exe)
85
+ const cli = createIosCli("path/to/ios");
86
+ const list = await cli.listDevices();
87
+ console.log(list.udids);
92
88
  ```
93
89
 
94
90
  ### Install/Uninstall Agent
@@ -98,17 +94,11 @@ import { AppleDeviceKit } from '@mcesystems/apple-kit';
98
94
 
99
95
  const device = new AppleDeviceKit('device-udid', 1);
100
96
 
101
- // Install an agent/app locally
102
- await device.installApp('/path/to/agent.ipa');
103
-
104
- // Install via MDM (ignores ipaPath, uses appId or url)
97
+ // Install app locally, then (if MDM is configured) take over management
105
98
  await device.installApp('/path/to/agent.ipa', {
106
- installViaMdm: true,
107
- mdm: {
108
- appId: 'com.example.agent',
109
- url: 'https://example.com/agent.ipa',
110
- waitForInstalled: true
111
- }
99
+ appId: 'com.example.agent',
100
+ url: 'https://example.com/agent.ipa',
101
+ waitForInstalled: true
112
102
  });
113
103
 
114
104
  // Check if installed
@@ -126,12 +116,12 @@ await device.uninstallApp('com.example.agent');
126
116
  ```typescript
127
117
  const device = new AppleDeviceKit('device-udid', 1);
128
118
 
129
- const info = await device.getDeviceInfo();
130
- console.log(`Device: ${info.deviceName}`);
131
- console.log(`Model: ${info.productType}`);
132
- console.log(`iOS: ${info.productVersion} (${info.buildVersion})`);
133
- console.log(`Serial: ${info.serialNumber}`);
134
- console.log(`UDID: ${info.udid}`);
119
+ const info = await device.info();
120
+ console.log(`Device: ${info.DeviceName}`);
121
+ console.log(`Model: ${info.ProductType}`);
122
+ console.log(`iOS: ${info.ProductVersion} (${info.BuildVersion})`);
123
+ console.log(`Serial: ${info.SerialNumber}`);
124
+ console.log(`UDID: ${info.UniqueDeviceID}`);
135
125
  ```
136
126
 
137
127
  ### Trust/Pairing
@@ -142,44 +132,27 @@ const device = new AppleDeviceKit('device-udid', 1);
142
132
  // Check if device is trusted
143
133
  const isPaired = await device.isPaired();
144
134
 
145
- // Initiate pairing (user must accept on device)
146
- await device.pair();
147
-
148
- // Wait for user to accept trust dialog
149
- await device.waitForPairing(60000); // 60 second timeout
135
+ // Trust the device (initiates pairing and waits for user acceptance)
136
+ await device.trustDevice(60000);
150
137
 
151
138
  // Unpair device
152
139
  await device.unpair();
153
140
  ```
154
141
 
155
- ### Launch Application
156
-
157
- ```typescript
158
- const device = new AppleDeviceKit('device-udid', 1);
159
-
160
- // Launch an app
161
- await device.launchApp('com.example.myapp');
162
-
163
- // Launch with arguments
164
- await device.launchApp('com.example.myapp', ['--debug', '--port=8080']);
165
- ```
166
-
167
142
  ### Port Forwarding
168
143
 
169
144
  ```typescript
170
145
  const device = new AppleDeviceKit('device-udid', 1);
171
146
 
172
- // Forward local port 8080 to device port 8080
173
- const forward = device.startPortForward(8080, 8080);
147
+ // Forward device port 8080 to a local port (auto-allocated)
148
+ const forward = await device.startPortForwardAsync(8080);
149
+ console.log(`Local port: ${forward.localPort}`);
174
150
 
175
151
  // Use the forwarded connection...
176
152
  // connect to localhost:8080 to reach device:8080
177
153
 
178
154
  // Stop forwarding when done
179
- forward.stop();
180
-
181
- // Or use async version that waits for ready
182
- const forwardAsync = await device.startPortForwardAsync(8080, 8080);
155
+ device.closePortForward();
183
156
  ```
184
157
 
185
158
  ### Activation
@@ -192,107 +165,86 @@ const state = await device.getActivationState();
192
165
  console.log(`Activated: ${state.isActivated}`);
193
166
  console.log(`State: ${state.activationState}`);
194
167
 
195
- // Activate device (requires valid activation record)
196
- await device.activate({
197
- resourcesDir: process.env.APPLE_KIT_RESOURCES_DIR,
198
- });
199
-
200
- // Deactivate device
201
- await device.deactivate();
168
+ // Activate device (uses go-ios and MDM client)
169
+ const cleanup = await device.activate();
170
+ if (cleanup) {
171
+ await cleanup(); // removes WiFi profile when done
172
+ }
202
173
  ```
203
174
 
204
- When `resourcesDir` is set, the activation flow reads MDM credentials from
205
- `@clientLocal.json` inside that folder, plist templates from `resourcesDir/plist`,
206
- and the iOS CLI binary from `resourcesDir/ios/bin/{platform}`.
207
- If `resourcesDir/mdm/enrollment_{udid}.mobileconfig` or
208
- `resourcesDir/mdm/enrollment.mobileconfig` exists, it is installed instead of
209
- calling the MDM API (offline enrollment).
210
-
211
- ### Running Without iTunes (usbmuxd)
175
+ The activation flow can install a WiFi profile based on environment variables:
176
+ `WIFI_SSID`, `WIFI_PASSWORD`, `WIFI_ENCRYPTION`, `WIFI_HIDDEN`, `WIFI_ENTERPRISE`,
177
+ `WIFI_USERNAME`, and `WIFI_EAP_TYPE`.
212
178
 
213
- On Windows, iTunes provides the Apple Mobile Device Service for USB communication. If you don't have iTunes installed, you can run the bundled `usbmuxd`:
179
+ ### Profiles
214
180
 
215
181
  ```typescript
216
- import { startUsbmuxd, stopUsbmuxd, ensureUsbmuxd } from '@mcesystems/apple-kit';
182
+ const device = new AppleDeviceKit('device-udid', 1);
217
183
 
218
- // Start usbmuxd daemon (required if iTunes is not installed)
219
- const daemon = startUsbmuxd();
184
+ // List profiles
185
+ const profiles = await device.listProfiles();
186
+ console.log(profiles.profiles);
220
187
 
221
- // Or ensure it's running (starts if not already running)
222
- ensureUsbmuxd();
188
+ // Remove a profile by identifier
189
+ await device.removeProfile("com.example.profile");
190
+ ```
223
191
 
224
- // Now you can use device operations...
225
- const devices = await AppleDeviceKit.listDevices();
192
+ ### Wipe Device
226
193
 
227
- // When done, stop the daemon
228
- stopUsbmuxd();
229
- // or
230
- daemon.stop();
231
- ```
194
+ ```typescript
195
+ const device = new AppleDeviceKit('device-udid', 1);
232
196
 
233
- **Note:** The usbmuxd daemon must be running before connecting devices. Start it before plugging in your iOS device.
197
+ // WARNING: this erases all data
198
+ await device.wipe();
199
+ ```
234
200
 
235
201
  ## API Reference
236
202
 
237
203
  ### AppleDeviceKit
238
204
 
239
205
  **Static methods:**
240
- - `listDevices()`: Get all connected iOS devices
206
+ - `setResourcesDir(dir)`: Configure resources location
241
207
 
242
208
  **Device Info:**
243
- - `getDeviceInfo()`: Get device properties
209
+ - `info()`: Get device properties
244
210
  - `getDeviceId()`: Get the device UDID
245
- - `getPort()`: Get the logical port number
211
+ - `getLogicalPort()`: Get the logical port number
212
+ - `getDevicePort()`: Get current local forwarded port (or null)
246
213
 
247
214
  **App Management:**
248
- - `installApp(ipaPath, options?)`: Install an IPA file or via MDM
215
+ - `installApp(ipaPath, options)`: Install IPA and take over via MDM if configured
249
216
  - `uninstallApp(bundleId)`: Uninstall an app by bundle ID
250
217
  - `isAppInstalled(bundleId)`: Check if app is installed
251
218
  - `listApps()`: List all installed user apps
252
- - `launchApp(bundleId, args?)`: Launch an application
253
219
 
254
220
  **Trust/Pairing:**
255
221
  - `isPaired()`: Check if device is paired/trusted
256
222
  - `pair()`: Initiate pairing (user must accept on device)
223
+ - `trustDevice(timeout?, onWaiting?)`: Pair and wait for user acceptance
257
224
  - `unpair()`: Remove pairing/trust
258
- - `waitForPairing(timeout?)`: Wait for device to be paired
225
+ - `waitForPairing(timeout?, pollInterval?)`: Wait for device to be paired
259
226
 
260
227
  **Port Forwarding:**
261
- - `startPortForward(localPort, devicePort)`: Start port forwarding
262
- - `startPortForwardAsync(localPort, devicePort)`: Start and wait for ready
228
+ - `startPortForwardAsync(devicePort, startupTimeout?)`: Start and wait for ready
229
+ - `closePortForward()`: Stop forwarding
230
+
231
+ **Profiles:**
232
+ - `listProfiles()`: List installed profiles
233
+ - `removeProfile(identifier)`: Remove profile by identifier
263
234
 
264
235
  **Activation:**
265
236
  - `getActivationState()`: Get activation state
266
- - `activate()`: Activate the device
267
- - `deactivate()`: Deactivate the device
268
-
269
- ### DevicesMonitor
237
+ - `activate()`: Activate the device (returns cleanup function)
270
238
 
271
- - `startTracking()`: Start monitoring for iOS device connections
272
- - `stopTracking()`: Stop monitoring
273
- - `getKits()`: Get all currently tracked devices
274
- - `getKit(udid)`: Get a specific device kit by UDID
239
+ **Device Wipe:**
240
+ - `wipe()`: Erase device data
275
241
 
276
- ### usbmuxd Functions
277
-
278
- - `startUsbmuxd(foreground?)`: Start the usbmuxd daemon
279
- - `stopUsbmuxd()`: Stop the daemon
280
- - `isUsbmuxdRunning()`: Check if daemon is running
281
- - `ensureUsbmuxd()`: Start if not already running
282
-
283
- To use a custom resources folder, pass it as the second parameter:
284
- `startUsbmuxd(false, { resourcesDir: "..." })`.
242
+ **Lifecycle:**
243
+ - `dispose()`: Clean up resources and port forwards
285
244
 
286
245
  ### Types
287
246
 
288
247
  ```typescript
289
- interface PortForwardHandle {
290
- stop: () => void;
291
- localPort: number;
292
- devicePort: number;
293
- process: ChildProcess;
294
- }
295
-
296
248
  interface ActivationState {
297
249
  isActivated: boolean;
298
250
  activationState: string;