@expo/cli 0.1.3 → 0.1.6
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/build/bin/cli +8 -2
- package/build/bin/cli.map +1 -1
- package/build/src/export/index.js +1 -0
- package/build/src/export/index.js.map +1 -1
- package/build/src/run/android/runAndroidAsync.js +3 -3
- package/build/src/run/android/runAndroidAsync.js.map +1 -1
- package/build/src/run/ensureNativeProject.js +3 -0
- package/build/src/run/ensureNativeProject.js.map +1 -1
- package/build/src/run/hints.js +1 -1
- package/build/src/run/hints.js.map +1 -1
- package/build/src/run/ios/XcodeBuild.js +275 -0
- package/build/src/run/ios/XcodeBuild.js.map +1 -0
- package/build/src/run/ios/XcodeBuild.types.js +6 -0
- package/build/src/run/ios/XcodeBuild.types.js.map +1 -0
- package/build/src/run/ios/appleDevice/AppleDevice.js +161 -0
- package/build/src/run/ios/appleDevice/AppleDevice.js.map +1 -0
- package/build/src/run/ios/appleDevice/ClientManager.js +161 -0
- package/build/src/run/ios/appleDevice/ClientManager.js.map +1 -0
- package/build/src/run/ios/appleDevice/client/AFCClient.js +195 -0
- package/build/src/run/ios/appleDevice/client/AFCClient.js.map +1 -0
- package/build/src/run/ios/appleDevice/client/DebugserverClient.js +107 -0
- package/build/src/run/ios/appleDevice/client/DebugserverClient.js.map +1 -0
- package/build/src/run/ios/appleDevice/client/InstallationProxyClient.js +97 -0
- package/build/src/run/ios/appleDevice/client/InstallationProxyClient.js.map +1 -0
- package/build/src/run/ios/appleDevice/client/LockdowndClient.js +140 -0
- package/build/src/run/ios/appleDevice/client/LockdowndClient.js.map +1 -0
- package/build/src/run/ios/appleDevice/client/MobileImageMounterClient.js +91 -0
- package/build/src/run/ios/appleDevice/client/MobileImageMounterClient.js.map +1 -0
- package/build/src/run/ios/appleDevice/client/ServiceClient.js +21 -0
- package/build/src/run/ios/appleDevice/client/ServiceClient.js.map +1 -0
- package/build/src/run/ios/appleDevice/client/UsbmuxdClient.js +117 -0
- package/build/src/run/ios/appleDevice/client/UsbmuxdClient.js.map +1 -0
- package/build/src/run/ios/appleDevice/installOnDeviceAsync.js +90 -0
- package/build/src/run/ios/appleDevice/installOnDeviceAsync.js.map +1 -0
- package/build/src/run/ios/appleDevice/protocol/AFCProtocol.js +332 -0
- package/build/src/run/ios/appleDevice/protocol/AFCProtocol.js.map +1 -0
- package/build/src/run/ios/appleDevice/protocol/AbstractProtocol.js +115 -0
- package/build/src/run/ios/appleDevice/protocol/AbstractProtocol.js.map +1 -0
- package/build/src/run/ios/appleDevice/protocol/GDBProtocol.js +126 -0
- package/build/src/run/ios/appleDevice/protocol/GDBProtocol.js.map +1 -0
- package/build/src/run/ios/appleDevice/protocol/LockdownProtocol.js +74 -0
- package/build/src/run/ios/appleDevice/protocol/LockdownProtocol.js.map +1 -0
- package/build/src/run/ios/appleDevice/protocol/UsbmuxProtocol.js +65 -0
- package/build/src/run/ios/appleDevice/protocol/UsbmuxProtocol.js.map +1 -0
- package/build/src/run/ios/codeSigning/Security.js +85 -0
- package/build/src/run/ios/codeSigning/Security.js.map +1 -0
- package/build/src/run/ios/codeSigning/configureCodeSigning.js +77 -0
- package/build/src/run/ios/codeSigning/configureCodeSigning.js.map +1 -0
- package/build/src/run/ios/codeSigning/resolveCertificateSigningIdentity.js +102 -0
- package/build/src/run/ios/codeSigning/resolveCertificateSigningIdentity.js.map +1 -0
- package/build/src/run/ios/codeSigning/settings.js +21 -0
- package/build/src/run/ios/codeSigning/settings.js.map +1 -0
- package/build/src/run/ios/codeSigning/xcodeCodeSigning.js +76 -0
- package/build/src/run/ios/codeSigning/xcodeCodeSigning.js.map +1 -0
- package/build/src/run/ios/index.js +98 -0
- package/build/src/run/ios/index.js.map +1 -0
- package/build/src/run/ios/launchApp.js +75 -0
- package/build/src/run/ios/launchApp.js.map +1 -0
- package/build/src/run/ios/options/promptDevice.js +50 -0
- package/build/src/run/ios/options/promptDevice.js.map +1 -0
- package/build/src/run/ios/options/resolveDevice.js +92 -0
- package/build/src/run/ios/options/resolveDevice.js.map +1 -0
- package/build/src/run/ios/options/resolveNativeScheme.js +92 -0
- package/build/src/run/ios/options/resolveNativeScheme.js.map +1 -0
- package/build/src/run/ios/options/resolveOptions.js +44 -0
- package/build/src/run/ios/options/resolveOptions.js.map +1 -0
- package/build/src/run/ios/options/resolveXcodeProject.js +39 -0
- package/build/src/run/ios/options/resolveXcodeProject.js.map +1 -0
- package/build/src/run/ios/runIosAsync.js +85 -0
- package/build/src/run/ios/runIosAsync.js.map +1 -0
- package/build/src/run/resolveBundlerProps.js +9 -6
- package/build/src/run/resolveBundlerProps.js.map +1 -1
- package/build/src/run/startBundler.js +4 -2
- package/build/src/run/startBundler.js.map +1 -1
- package/build/src/start/doctor/Prerequisite.js +4 -4
- package/build/src/start/doctor/Prerequisite.js.map +1 -1
- package/build/src/start/doctor/SecurityBinPrerequisite.js +27 -0
- package/build/src/start/doctor/SecurityBinPrerequisite.js.map +1 -0
- package/build/src/start/doctor/apple/XcodeDeveloperDiskImagePrerequisite.js +69 -0
- package/build/src/start/doctor/apple/XcodeDeveloperDiskImagePrerequisite.js.map +1 -0
- package/build/src/start/platforms/android/emulator.js +8 -0
- package/build/src/start/platforms/android/emulator.js.map +1 -1
- package/build/src/start/platforms/android/promptAndroidDevice.js +1 -5
- package/build/src/start/platforms/android/promptAndroidDevice.js.map +1 -1
- package/build/src/start/platforms/ios/AppleDeviceManager.js +5 -1
- package/build/src/start/platforms/ios/AppleDeviceManager.js.map +1 -1
- package/build/src/start/platforms/ios/promptAppleDevice.js +1 -5
- package/build/src/start/platforms/ios/promptAppleDevice.js.map +1 -1
- package/build/src/start/platforms/ios/simctl.js +14 -0
- package/build/src/start/platforms/ios/simctl.js.map +1 -1
- package/build/src/start/platforms/ios/simctlLogging.js +220 -0
- package/build/src/start/platforms/ios/simctlLogging.js.map +1 -0
- package/build/src/start/server/middleware/ClassicManifestMiddleware.js +1 -1
- package/build/src/utils/analytics/rudderstackClient.js +30 -3
- package/build/src/utils/analytics/rudderstackClient.js.map +1 -1
- package/build/src/utils/cocoapods.js +47 -0
- package/build/src/utils/cocoapods.js.map +1 -1
- package/build/src/utils/dir.js +6 -1
- package/build/src/utils/dir.js.map +1 -1
- package/build/src/utils/plist.js +59 -0
- package/build/src/utils/plist.js.map +1 -0
- package/build/src/utils/prompts.js +16 -0
- package/build/src/utils/prompts.js.map +1 -1
- package/build/src/utils/resolveArgs.js +4 -1
- package/build/src/utils/resolveArgs.js.map +1 -1
- package/build/src/utils/terminal.js +10 -0
- package/build/src/utils/terminal.js.map +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
exports.getConnectedDevicesAsync = getConnectedDevicesAsync;
|
|
6
|
+
exports.getConnectedDeviceValuesAsync = getConnectedDeviceValuesAsync;
|
|
7
|
+
exports.runOnDevice = runOnDevice;
|
|
8
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
9
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
10
|
+
var _path = _interopRequireDefault(require("path"));
|
|
11
|
+
var _xcodeDeveloperDiskImagePrerequisite = require("../../../start/doctor/apple/XcodeDeveloperDiskImagePrerequisite");
|
|
12
|
+
var _delay = require("../../../utils/delay");
|
|
13
|
+
var _errors = require("../../../utils/errors");
|
|
14
|
+
var _exit = require("../../../utils/exit");
|
|
15
|
+
var _clientManager = require("./ClientManager");
|
|
16
|
+
var _lockdowndClient = require("./client/LockdowndClient");
|
|
17
|
+
var _usbmuxdClient = require("./client/UsbmuxdClient");
|
|
18
|
+
var _afcprotocol = require("./protocol/AFCProtocol");
|
|
19
|
+
function _interopRequireDefault(obj) {
|
|
20
|
+
return obj && obj.__esModule ? obj : {
|
|
21
|
+
default: obj
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const debug = (0, _debug).default("expo:apple-device");
|
|
25
|
+
async function getConnectedDevicesAsync() {
|
|
26
|
+
const results = await getConnectedDeviceValuesAsync();
|
|
27
|
+
var _DeviceName, ref;
|
|
28
|
+
// TODO: Add support for osType (ipad, watchos, etc)
|
|
29
|
+
return results.map((device)=>({
|
|
30
|
+
// TODO: Better name
|
|
31
|
+
name: (ref = (_DeviceName = device.DeviceName) != null ? _DeviceName : device.ProductType) != null ? ref : "unknown ios device",
|
|
32
|
+
model: device.ProductType,
|
|
33
|
+
osVersion: device.ProductVersion,
|
|
34
|
+
deviceType: "device",
|
|
35
|
+
udid: device.UniqueDeviceID
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
async function getConnectedDeviceValuesAsync() {
|
|
40
|
+
const client = new _usbmuxdClient.UsbmuxdClient(_usbmuxdClient.UsbmuxdClient.connectUsbmuxdSocket());
|
|
41
|
+
const devices = await client.getDevices();
|
|
42
|
+
client.socket.end();
|
|
43
|
+
return Promise.all(devices.map(async (device)=>{
|
|
44
|
+
const socket = await new _usbmuxdClient.UsbmuxdClient(_usbmuxdClient.UsbmuxdClient.connectUsbmuxdSocket()).connect(device, 62078);
|
|
45
|
+
const deviceValue = await new _lockdowndClient.LockdowndClient(socket).getAllValues();
|
|
46
|
+
socket.end();
|
|
47
|
+
return deviceValue;
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
async function runOnDevice({ udid , appPath , bundleId , waitForApp , deltaPath , onProgress }) {
|
|
51
|
+
const clientManager = await _clientManager.ClientManager.create(udid);
|
|
52
|
+
try {
|
|
53
|
+
await mountDeveloperDiskImage(clientManager);
|
|
54
|
+
const packageName = _path.default.basename(appPath);
|
|
55
|
+
const destPackagePath = _path.default.join("PublicStaging", packageName);
|
|
56
|
+
await uploadApp(clientManager, {
|
|
57
|
+
appBinaryPath: appPath,
|
|
58
|
+
destinationPath: destPackagePath
|
|
59
|
+
});
|
|
60
|
+
const installer = await clientManager.getInstallationProxyClient();
|
|
61
|
+
await installer.installApp(destPackagePath, bundleId, {
|
|
62
|
+
// https://github.com/ios-control/ios-deploy/blob/0f2ffb1e564aa67a2dfca7cdf13de47ce489d835/src/ios-deploy/ios-deploy.m#L2491-L2508
|
|
63
|
+
ApplicationsType: "Any",
|
|
64
|
+
CFBundleIdentifier: bundleId,
|
|
65
|
+
CloseOnInvalidate: "1",
|
|
66
|
+
InvalidateOnDetach: "1",
|
|
67
|
+
IsUserInitiated: "1",
|
|
68
|
+
// Disable checking for wifi devices, this is nominally faster.
|
|
69
|
+
PreferWifi: "0",
|
|
70
|
+
// Only info I could find on these:
|
|
71
|
+
// https://github.com/wwxxyx/Quectel_BG96/blob/310876f90fc1093a59e45e381160eddcc31697d0/Apple_Homekit/homekit_certification_tools/ATS%206/ATS%206/ATS.app/Contents/Frameworks/CaptureKit.framework/Versions/A/Resources/MobileDevice/MobileInstallation.h#L112-L121
|
|
72
|
+
PackageType: "Developer",
|
|
73
|
+
ShadowParentKey: deltaPath
|
|
74
|
+
}, onProgress);
|
|
75
|
+
const { [bundleId]: appInfo } = await installer.lookupApp([
|
|
76
|
+
bundleId
|
|
77
|
+
]);
|
|
78
|
+
// launch fails with EBusy or ENotFound if you try to launch immediately after install
|
|
79
|
+
await (0, _delay).delayAsync(200);
|
|
80
|
+
const debugServerClient = await launchApp(clientManager, {
|
|
81
|
+
appInfo,
|
|
82
|
+
detach: !waitForApp
|
|
83
|
+
});
|
|
84
|
+
if (waitForApp) {
|
|
85
|
+
(0, _exit).installExitHooks(async ()=>{
|
|
86
|
+
// causes continue() to return
|
|
87
|
+
debugServerClient.halt();
|
|
88
|
+
// give continue() time to return response
|
|
89
|
+
await (0, _delay).delayAsync(64);
|
|
90
|
+
});
|
|
91
|
+
debug(`Waiting for app to close...\n`);
|
|
92
|
+
const result = await debugServerClient.continue();
|
|
93
|
+
// TODO: I have no idea what this packet means yet (successful close?)
|
|
94
|
+
// if not a close (ie, most likely due to halt from onBeforeExit), then kill the app
|
|
95
|
+
if (result !== "W00") {
|
|
96
|
+
await debugServerClient.kill();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} finally{
|
|
100
|
+
clientManager.end();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Mount the developer disk image for Xcode. */ async function mountDeveloperDiskImage(clientManager) {
|
|
104
|
+
const imageMounter = await clientManager.getMobileImageMounterClient();
|
|
105
|
+
// Check if already mounted. If not, mount.
|
|
106
|
+
if (!(await imageMounter.lookupImage()).ImageSignature) {
|
|
107
|
+
// verify DeveloperDiskImage exists (TODO: how does this work on Windows/Linux?)
|
|
108
|
+
// TODO: if windows/linux, download?
|
|
109
|
+
const version = await (await clientManager.getLockdowndClient()).getValue("ProductVersion");
|
|
110
|
+
const developerDiskImagePath = await _xcodeDeveloperDiskImagePrerequisite.XcodeDeveloperDiskImagePrerequisite.instance.assertAsync({
|
|
111
|
+
version
|
|
112
|
+
});
|
|
113
|
+
const developerDiskImageSig = _fs.default.readFileSync(`${developerDiskImagePath}.signature`);
|
|
114
|
+
await imageMounter.uploadImage(developerDiskImagePath, developerDiskImageSig);
|
|
115
|
+
await imageMounter.mountImage(developerDiskImagePath, developerDiskImageSig);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function uploadApp(clientManager, { appBinaryPath , destinationPath }) {
|
|
119
|
+
const afcClient = await clientManager.getAFCClient();
|
|
120
|
+
try {
|
|
121
|
+
await afcClient.getFileInfo("PublicStaging");
|
|
122
|
+
} catch (err) {
|
|
123
|
+
if (err instanceof _afcprotocol.AFCError && err.status === _afcprotocol.AFC_STATUS.OBJECT_NOT_FOUND) {
|
|
124
|
+
await afcClient.makeDirectory("PublicStaging");
|
|
125
|
+
} else {
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
await afcClient.uploadDirectory(appBinaryPath, destinationPath);
|
|
130
|
+
}
|
|
131
|
+
async function launchApp(clientManager, { appInfo , detach }) {
|
|
132
|
+
let tries = 0;
|
|
133
|
+
while(tries < 3){
|
|
134
|
+
const debugServerClient = await clientManager.getDebugserverClient();
|
|
135
|
+
await debugServerClient.setMaxPacketSize(1024);
|
|
136
|
+
await debugServerClient.setWorkingDir(appInfo.Container);
|
|
137
|
+
await debugServerClient.launchApp(appInfo.Path, appInfo.CFBundleExecutable);
|
|
138
|
+
const result = await debugServerClient.checkLaunchSuccess();
|
|
139
|
+
if (result === "OK") {
|
|
140
|
+
if (detach) {
|
|
141
|
+
// https://github.com/libimobiledevice/libimobiledevice/blob/25059d4c7d75e03aab516af2929d7c6e6d4c17de/tools/idevicedebug.c#L455-L464
|
|
142
|
+
const res = await debugServerClient.sendCommand("D", []);
|
|
143
|
+
debug("Disconnect from debug server request:", res);
|
|
144
|
+
if (res !== "OK") {
|
|
145
|
+
console.warn("Something went wrong while attempting to disconnect from iOS debug server, you may need to reopen the app manually.");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return debugServerClient;
|
|
149
|
+
} else if (result === "EBusy" || result === "ENotFound") {
|
|
150
|
+
debug("Device busy or app not found, trying to launch again in .5s...");
|
|
151
|
+
tries++;
|
|
152
|
+
debugServerClient.socket.end();
|
|
153
|
+
await (0, _delay).delayAsync(500);
|
|
154
|
+
} else {
|
|
155
|
+
throw new _errors.CommandError(`There was an error launching app: ${result}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
throw new _errors.CommandError("Unable to launch app, number of tries exceeded");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
//# sourceMappingURL=AppleDevice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/run/ios/appleDevice/AppleDevice.ts"],"sourcesContent":["import Debug from 'debug';\nimport fs from 'fs';\nimport path from 'path';\n\nimport { XcodeDeveloperDiskImagePrerequisite } from '../../../start/doctor/apple/XcodeDeveloperDiskImagePrerequisite';\nimport { delayAsync } from '../../../utils/delay';\nimport { CommandError } from '../../../utils/errors';\nimport { installExitHooks } from '../../../utils/exit';\nimport { ClientManager } from './ClientManager';\nimport { IPLookupResult, OnInstallProgressCallback } from './client/InstallationProxyClient';\nimport { DeviceValues, LockdowndClient } from './client/LockdowndClient';\nimport { UsbmuxdClient } from './client/UsbmuxdClient';\nimport { AFC_STATUS, AFCError } from './protocol/AFCProtocol';\n\nconst debug = Debug('expo:apple-device');\n\n// NOTE(EvanBacon): I have a feeling this shape will change with new iOS versions (tested against iOS 15).\nexport interface ConnectedDevice {\n /** @example `00008101-001964A22629003A` */\n udid: string;\n /** @example `Evan's phone` */\n name: string;\n /** @example `iPhone13,4` */\n model: string;\n /** @example `device` */\n deviceType: 'device' | 'catalyst';\n /** @example `15.4.1` */\n osVersion: string;\n}\n\nexport async function getConnectedDevicesAsync(): Promise<ConnectedDevice[]> {\n const results = await getConnectedDeviceValuesAsync();\n // TODO: Add support for osType (ipad, watchos, etc)\n return results.map((device) => ({\n // TODO: Better name\n name: device.DeviceName ?? device.ProductType ?? 'unknown ios device',\n model: device.ProductType,\n osVersion: device.ProductVersion,\n deviceType: 'device',\n udid: device.UniqueDeviceID,\n }));\n}\n\n/** @returns a list of physically connected Apple devices. */\nexport async function getConnectedDeviceValuesAsync(): Promise<DeviceValues[]> {\n const client = new UsbmuxdClient(UsbmuxdClient.connectUsbmuxdSocket());\n const devices = await client.getDevices();\n client.socket.end();\n\n return Promise.all(\n devices.map(async (device): Promise<DeviceValues> => {\n const socket = await new UsbmuxdClient(UsbmuxdClient.connectUsbmuxdSocket()).connect(\n device,\n 62078\n );\n const deviceValue = await new LockdowndClient(socket).getAllValues();\n socket.end();\n return deviceValue;\n })\n );\n}\n\n/** Install and run an Apple app binary on a connected Apple device. */\nexport async function runOnDevice({\n udid,\n appPath,\n bundleId,\n waitForApp,\n deltaPath,\n onProgress,\n}: {\n /** Apple device UDID */\n udid: string;\n /** File path to the app binary (ipa) */\n appPath: string;\n /** Bundle identifier for the app at `appPath` */\n bundleId: string;\n /** Wait for the app to launch before returning */\n waitForApp: boolean;\n /** File path to the app deltas folder to use for faster subsequent installs */\n deltaPath: string;\n /** Callback to be called with progress updates */\n onProgress: OnInstallProgressCallback;\n}) {\n const clientManager = await ClientManager.create(udid);\n\n try {\n await mountDeveloperDiskImage(clientManager);\n\n const packageName = path.basename(appPath);\n const destPackagePath = path.join('PublicStaging', packageName);\n\n await uploadApp(clientManager, { appBinaryPath: appPath, destinationPath: destPackagePath });\n\n const installer = await clientManager.getInstallationProxyClient();\n await installer.installApp(\n destPackagePath,\n bundleId,\n {\n // https://github.com/ios-control/ios-deploy/blob/0f2ffb1e564aa67a2dfca7cdf13de47ce489d835/src/ios-deploy/ios-deploy.m#L2491-L2508\n ApplicationsType: 'Any',\n\n CFBundleIdentifier: bundleId,\n CloseOnInvalidate: '1',\n InvalidateOnDetach: '1',\n IsUserInitiated: '1',\n // Disable checking for wifi devices, this is nominally faster.\n PreferWifi: '0',\n // Only info I could find on these:\n // https://github.com/wwxxyx/Quectel_BG96/blob/310876f90fc1093a59e45e381160eddcc31697d0/Apple_Homekit/homekit_certification_tools/ATS%206/ATS%206/ATS.app/Contents/Frameworks/CaptureKit.framework/Versions/A/Resources/MobileDevice/MobileInstallation.h#L112-L121\n PackageType: 'Developer',\n ShadowParentKey: deltaPath,\n // SkipUninstall: '1'\n },\n onProgress\n );\n\n const { [bundleId]: appInfo } = await installer.lookupApp([bundleId]);\n // launch fails with EBusy or ENotFound if you try to launch immediately after install\n await delayAsync(200);\n const debugServerClient = await launchApp(clientManager, { appInfo, detach: !waitForApp });\n if (waitForApp) {\n installExitHooks(async () => {\n // causes continue() to return\n debugServerClient.halt();\n // give continue() time to return response\n await delayAsync(64);\n });\n\n debug(`Waiting for app to close...\\n`);\n const result = await debugServerClient.continue();\n // TODO: I have no idea what this packet means yet (successful close?)\n // if not a close (ie, most likely due to halt from onBeforeExit), then kill the app\n if (result !== 'W00') {\n await debugServerClient.kill();\n }\n }\n } finally {\n clientManager.end();\n }\n}\n\n/** Mount the developer disk image for Xcode. */\nasync function mountDeveloperDiskImage(clientManager: ClientManager) {\n const imageMounter = await clientManager.getMobileImageMounterClient();\n // Check if already mounted. If not, mount.\n if (!(await imageMounter.lookupImage()).ImageSignature) {\n // verify DeveloperDiskImage exists (TODO: how does this work on Windows/Linux?)\n // TODO: if windows/linux, download?\n const version = await (await clientManager.getLockdowndClient()).getValue('ProductVersion');\n const developerDiskImagePath = await XcodeDeveloperDiskImagePrerequisite.instance.assertAsync({\n version,\n });\n const developerDiskImageSig = fs.readFileSync(`${developerDiskImagePath}.signature`);\n await imageMounter.uploadImage(developerDiskImagePath, developerDiskImageSig);\n await imageMounter.mountImage(developerDiskImagePath, developerDiskImageSig);\n }\n}\n\nasync function uploadApp(\n clientManager: ClientManager,\n { appBinaryPath, destinationPath }: { appBinaryPath: string; destinationPath: string }\n) {\n const afcClient = await clientManager.getAFCClient();\n try {\n await afcClient.getFileInfo('PublicStaging');\n } catch (err: any) {\n if (err instanceof AFCError && err.status === AFC_STATUS.OBJECT_NOT_FOUND) {\n await afcClient.makeDirectory('PublicStaging');\n } else {\n throw err;\n }\n }\n await afcClient.uploadDirectory(appBinaryPath, destinationPath);\n}\n\nasync function launchApp(\n clientManager: ClientManager,\n { appInfo, detach }: { appInfo: IPLookupResult[string]; detach?: boolean }\n) {\n let tries = 0;\n while (tries < 3) {\n const debugServerClient = await clientManager.getDebugserverClient();\n await debugServerClient.setMaxPacketSize(1024);\n await debugServerClient.setWorkingDir(appInfo.Container);\n await debugServerClient.launchApp(appInfo.Path, appInfo.CFBundleExecutable);\n\n const result = await debugServerClient.checkLaunchSuccess();\n if (result === 'OK') {\n if (detach) {\n // https://github.com/libimobiledevice/libimobiledevice/blob/25059d4c7d75e03aab516af2929d7c6e6d4c17de/tools/idevicedebug.c#L455-L464\n const res = await debugServerClient.sendCommand('D', []);\n debug('Disconnect from debug server request:', res);\n if (res !== 'OK') {\n console.warn(\n 'Something went wrong while attempting to disconnect from iOS debug server, you may need to reopen the app manually.'\n );\n }\n }\n\n return debugServerClient;\n } else if (result === 'EBusy' || result === 'ENotFound') {\n debug('Device busy or app not found, trying to launch again in .5s...');\n tries++;\n debugServerClient.socket.end();\n await delayAsync(500);\n } else {\n throw new CommandError(`There was an error launching app: ${result}`);\n }\n }\n throw new CommandError('Unable to launch app, number of tries exceeded');\n}\n"],"names":["getConnectedDevicesAsync","getConnectedDeviceValuesAsync","runOnDevice","debug","Debug","results","device","map","name","DeviceName","ProductType","model","osVersion","ProductVersion","deviceType","udid","UniqueDeviceID","client","UsbmuxdClient","connectUsbmuxdSocket","devices","getDevices","socket","end","Promise","all","connect","deviceValue","LockdowndClient","getAllValues","appPath","bundleId","waitForApp","deltaPath","onProgress","clientManager","ClientManager","create","mountDeveloperDiskImage","packageName","path","basename","destPackagePath","join","uploadApp","appBinaryPath","destinationPath","installer","getInstallationProxyClient","installApp","ApplicationsType","CFBundleIdentifier","CloseOnInvalidate","InvalidateOnDetach","IsUserInitiated","PreferWifi","PackageType","ShadowParentKey","appInfo","lookupApp","delayAsync","debugServerClient","launchApp","detach","installExitHooks","halt","result","continue","kill","imageMounter","getMobileImageMounterClient","lookupImage","ImageSignature","version","getLockdowndClient","getValue","developerDiskImagePath","XcodeDeveloperDiskImagePrerequisite","instance","assertAsync","developerDiskImageSig","fs","readFileSync","uploadImage","mountImage","afcClient","getAFCClient","getFileInfo","err","AFCError","status","AFC_STATUS","OBJECT_NOT_FOUND","makeDirectory","uploadDirectory","tries","getDebugserverClient","setMaxPacketSize","setWorkingDir","Container","Path","CFBundleExecutable","checkLaunchSuccess","res","sendCommand","console","warn","CommandError"],"mappings":"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"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
var _stream = require("stream");
|
|
6
|
+
var tls = _interopRequireWildcard(require("tls"));
|
|
7
|
+
var _errors = require("../../../utils/errors");
|
|
8
|
+
var _afcclient = require("./client/AFCClient");
|
|
9
|
+
var _debugserverClient = require("./client/DebugserverClient");
|
|
10
|
+
var _installationProxyClient = require("./client/InstallationProxyClient");
|
|
11
|
+
var _lockdowndClient = require("./client/LockdowndClient");
|
|
12
|
+
var _mobileImageMounterClient = require("./client/MobileImageMounterClient");
|
|
13
|
+
var _usbmuxdClient = require("./client/UsbmuxdClient");
|
|
14
|
+
function _interopRequireWildcard(obj) {
|
|
15
|
+
if (obj && obj.__esModule) {
|
|
16
|
+
return obj;
|
|
17
|
+
} else {
|
|
18
|
+
var newObj = {};
|
|
19
|
+
if (obj != null) {
|
|
20
|
+
for(var key in obj){
|
|
21
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
22
|
+
var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {};
|
|
23
|
+
if (desc.get || desc.set) {
|
|
24
|
+
Object.defineProperty(newObj, key, desc);
|
|
25
|
+
} else {
|
|
26
|
+
newObj[key] = obj[key];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
newObj.default = obj;
|
|
32
|
+
return newObj;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
class ClientManager {
|
|
36
|
+
constructor(pairRecord, device, lockdowndClient){
|
|
37
|
+
this.pairRecord = pairRecord;
|
|
38
|
+
this.device = device;
|
|
39
|
+
this.lockdowndClient = lockdowndClient;
|
|
40
|
+
this.connections = [
|
|
41
|
+
lockdowndClient.socket
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
static async create(udid) {
|
|
45
|
+
const usbmuxClient = new _usbmuxdClient.UsbmuxdClient(_usbmuxdClient.UsbmuxdClient.connectUsbmuxdSocket());
|
|
46
|
+
const device = await usbmuxClient.getDevice(udid);
|
|
47
|
+
const pairRecord = await usbmuxClient.readPairRecord(device.Properties.SerialNumber);
|
|
48
|
+
const lockdownSocket = await usbmuxClient.connect(device, 62078);
|
|
49
|
+
const lockdownClient = new _lockdowndClient.LockdowndClient(lockdownSocket);
|
|
50
|
+
await lockdownClient.doHandshake(pairRecord);
|
|
51
|
+
return new ClientManager(pairRecord, device, lockdownClient);
|
|
52
|
+
}
|
|
53
|
+
async getUsbmuxdClient() {
|
|
54
|
+
const usbmuxClient = new _usbmuxdClient.UsbmuxdClient(_usbmuxdClient.UsbmuxdClient.connectUsbmuxdSocket());
|
|
55
|
+
this.connections.push(usbmuxClient.socket);
|
|
56
|
+
return usbmuxClient;
|
|
57
|
+
}
|
|
58
|
+
async getLockdowndClient() {
|
|
59
|
+
const usbmuxClient = new _usbmuxdClient.UsbmuxdClient(_usbmuxdClient.UsbmuxdClient.connectUsbmuxdSocket());
|
|
60
|
+
const lockdownSocket = await usbmuxClient.connect(this.device, 62078);
|
|
61
|
+
const lockdownClient = new _lockdowndClient.LockdowndClient(lockdownSocket);
|
|
62
|
+
this.connections.push(lockdownClient.socket);
|
|
63
|
+
return lockdownClient;
|
|
64
|
+
}
|
|
65
|
+
async getLockdowndClientWithHandshake() {
|
|
66
|
+
const lockdownClient = await this.getLockdowndClient();
|
|
67
|
+
await lockdownClient.doHandshake(this.pairRecord);
|
|
68
|
+
return lockdownClient;
|
|
69
|
+
}
|
|
70
|
+
async getAFCClient() {
|
|
71
|
+
return this.getServiceClient("com.apple.afc", _afcclient.AFCClient);
|
|
72
|
+
}
|
|
73
|
+
async getInstallationProxyClient() {
|
|
74
|
+
return this.getServiceClient("com.apple.mobile.installation_proxy", _installationProxyClient.InstallationProxyClient);
|
|
75
|
+
}
|
|
76
|
+
async getMobileImageMounterClient() {
|
|
77
|
+
return this.getServiceClient("com.apple.mobile.mobile_image_mounter", _mobileImageMounterClient.MobileImageMounterClient);
|
|
78
|
+
}
|
|
79
|
+
async getDebugserverClient() {
|
|
80
|
+
try {
|
|
81
|
+
// iOS 14 added support for a secure debug service so try to connect to that first
|
|
82
|
+
return await this.getServiceClient("com.apple.debugserver.DVTSecureSocketProxy", _debugserverClient.DebugserverClient);
|
|
83
|
+
} catch {
|
|
84
|
+
// otherwise, fall back to the previous implementation
|
|
85
|
+
return this.getServiceClient("com.apple.debugserver", _debugserverClient.DebugserverClient, true);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async getServiceClient(name, ServiceType, disableSSL = false) {
|
|
89
|
+
const { port: servicePort , enableServiceSSL } = await this.lockdowndClient.startService(name);
|
|
90
|
+
const usbmuxClient = new _usbmuxdClient.UsbmuxdClient(_usbmuxdClient.UsbmuxdClient.connectUsbmuxdSocket());
|
|
91
|
+
let usbmuxdSocket = await usbmuxClient.connect(this.device, servicePort);
|
|
92
|
+
if (enableServiceSSL) {
|
|
93
|
+
const tlsOptions = {
|
|
94
|
+
rejectUnauthorized: false,
|
|
95
|
+
secureContext: tls.createSecureContext({
|
|
96
|
+
secureProtocol: "TLSv1_method",
|
|
97
|
+
cert: this.pairRecord.RootCertificate,
|
|
98
|
+
key: this.pairRecord.RootPrivateKey
|
|
99
|
+
})
|
|
100
|
+
};
|
|
101
|
+
// Some services seem to not support TLS/SSL after the initial handshake
|
|
102
|
+
// More info: https://github.com/libimobiledevice/libimobiledevice/issues/793
|
|
103
|
+
if (disableSSL) {
|
|
104
|
+
// According to https://nodejs.org/api/tls.html#tls_tls_connect_options_callback we can
|
|
105
|
+
// pass any Duplex in to tls.connect instead of a Socket. So we'll use our proxy to keep
|
|
106
|
+
// the TLS wrapper and underlying usbmuxd socket separate.
|
|
107
|
+
const proxy = new UsbmuxdProxy(usbmuxdSocket);
|
|
108
|
+
tlsOptions.socket = proxy;
|
|
109
|
+
await new Promise((resolve, reject)=>{
|
|
110
|
+
const timeoutId = setTimeout(()=>{
|
|
111
|
+
reject(new _errors.CommandError("APPLE_DEVICE", "The TLS handshake failed to complete after 5s."));
|
|
112
|
+
}, 5000);
|
|
113
|
+
tls.connect(tlsOptions, function() {
|
|
114
|
+
clearTimeout(timeoutId);
|
|
115
|
+
// After the handshake, we don't need TLS or the proxy anymore,
|
|
116
|
+
// since we'll just pass in the naked usbmuxd socket to the service client
|
|
117
|
+
this.destroy();
|
|
118
|
+
resolve();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
tlsOptions.socket = usbmuxdSocket;
|
|
123
|
+
usbmuxdSocket = tls.connect(tlsOptions);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const client = new ServiceType(usbmuxdSocket);
|
|
127
|
+
this.connections.push(client.socket);
|
|
128
|
+
return client;
|
|
129
|
+
}
|
|
130
|
+
end() {
|
|
131
|
+
for (const socket of this.connections){
|
|
132
|
+
// may already be closed
|
|
133
|
+
try {
|
|
134
|
+
socket.end();
|
|
135
|
+
} catch {}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
exports.ClientManager = ClientManager;
|
|
140
|
+
class UsbmuxdProxy extends _stream.Duplex {
|
|
141
|
+
constructor(usbmuxdSock){
|
|
142
|
+
super();
|
|
143
|
+
this.usbmuxdSock = usbmuxdSock;
|
|
144
|
+
this.usbmuxdSock.on("data", (data)=>{
|
|
145
|
+
this.push(data);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
_write(chunk, encoding, callback) {
|
|
149
|
+
this.usbmuxdSock.write(chunk);
|
|
150
|
+
callback();
|
|
151
|
+
}
|
|
152
|
+
_read(size) {
|
|
153
|
+
// Stub so we don't error, since we push everything we get from usbmuxd as it comes in.
|
|
154
|
+
// TODO: better way to do this?
|
|
155
|
+
}
|
|
156
|
+
_destroy() {
|
|
157
|
+
this.usbmuxdSock.removeAllListeners();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
//# sourceMappingURL=ClientManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/run/ios/appleDevice/ClientManager.ts"],"sourcesContent":["/**\n * Copyright (c) 2021 Expo, Inc.\n * Copyright (c) 2018 Drifty Co.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport { Socket } from 'net';\nimport { Duplex } from 'stream';\nimport * as tls from 'tls';\n\nimport { CommandError } from '../../../utils/errors';\nimport { AFCClient } from './client/AFCClient';\nimport { DebugserverClient } from './client/DebugserverClient';\nimport { InstallationProxyClient } from './client/InstallationProxyClient';\nimport { LockdowndClient } from './client/LockdowndClient';\nimport { MobileImageMounterClient } from './client/MobileImageMounterClient';\nimport { ServiceClient } from './client/ServiceClient';\nimport { UsbmuxdClient, UsbmuxdDevice, UsbmuxdPairRecord } from './client/UsbmuxdClient';\n\nexport class ClientManager {\n private connections: Socket[];\n constructor(\n public pairRecord: UsbmuxdPairRecord,\n public device: UsbmuxdDevice,\n private lockdowndClient: LockdowndClient\n ) {\n this.connections = [lockdowndClient.socket];\n }\n\n static async create(udid?: string) {\n const usbmuxClient = new UsbmuxdClient(UsbmuxdClient.connectUsbmuxdSocket());\n const device = await usbmuxClient.getDevice(udid);\n const pairRecord = await usbmuxClient.readPairRecord(device.Properties.SerialNumber);\n const lockdownSocket = await usbmuxClient.connect(device, 62078);\n const lockdownClient = new LockdowndClient(lockdownSocket);\n await lockdownClient.doHandshake(pairRecord);\n return new ClientManager(pairRecord, device, lockdownClient);\n }\n\n async getUsbmuxdClient() {\n const usbmuxClient = new UsbmuxdClient(UsbmuxdClient.connectUsbmuxdSocket());\n this.connections.push(usbmuxClient.socket);\n return usbmuxClient;\n }\n\n async getLockdowndClient() {\n const usbmuxClient = new UsbmuxdClient(UsbmuxdClient.connectUsbmuxdSocket());\n const lockdownSocket = await usbmuxClient.connect(this.device, 62078);\n const lockdownClient = new LockdowndClient(lockdownSocket);\n this.connections.push(lockdownClient.socket);\n return lockdownClient;\n }\n\n async getLockdowndClientWithHandshake() {\n const lockdownClient = await this.getLockdowndClient();\n await lockdownClient.doHandshake(this.pairRecord);\n return lockdownClient;\n }\n\n async getAFCClient() {\n return this.getServiceClient('com.apple.afc', AFCClient);\n }\n\n async getInstallationProxyClient() {\n return this.getServiceClient('com.apple.mobile.installation_proxy', InstallationProxyClient);\n }\n\n async getMobileImageMounterClient() {\n return this.getServiceClient('com.apple.mobile.mobile_image_mounter', MobileImageMounterClient);\n }\n\n async getDebugserverClient() {\n try {\n // iOS 14 added support for a secure debug service so try to connect to that first\n return await this.getServiceClient(\n 'com.apple.debugserver.DVTSecureSocketProxy',\n DebugserverClient\n );\n } catch {\n // otherwise, fall back to the previous implementation\n return this.getServiceClient('com.apple.debugserver', DebugserverClient, true);\n }\n }\n\n private async getServiceClient<T extends ServiceClient<any>>(\n name: string,\n ServiceType: new (...args: any[]) => T,\n disableSSL = false\n ) {\n const { port: servicePort, enableServiceSSL } = await this.lockdowndClient.startService(name);\n const usbmuxClient = new UsbmuxdClient(UsbmuxdClient.connectUsbmuxdSocket());\n let usbmuxdSocket = await usbmuxClient.connect(this.device, servicePort);\n\n if (enableServiceSSL) {\n const tlsOptions: tls.ConnectionOptions = {\n rejectUnauthorized: false,\n secureContext: tls.createSecureContext({\n secureProtocol: 'TLSv1_method',\n cert: this.pairRecord.RootCertificate,\n key: this.pairRecord.RootPrivateKey,\n }),\n };\n\n // Some services seem to not support TLS/SSL after the initial handshake\n // More info: https://github.com/libimobiledevice/libimobiledevice/issues/793\n if (disableSSL) {\n // According to https://nodejs.org/api/tls.html#tls_tls_connect_options_callback we can\n // pass any Duplex in to tls.connect instead of a Socket. So we'll use our proxy to keep\n // the TLS wrapper and underlying usbmuxd socket separate.\n const proxy: any = new UsbmuxdProxy(usbmuxdSocket);\n tlsOptions.socket = proxy;\n\n await new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(\n new CommandError('APPLE_DEVICE', 'The TLS handshake failed to complete after 5s.')\n );\n }, 5000);\n tls.connect(tlsOptions, function (this: tls.TLSSocket) {\n clearTimeout(timeoutId);\n // After the handshake, we don't need TLS or the proxy anymore,\n // since we'll just pass in the naked usbmuxd socket to the service client\n this.destroy();\n resolve();\n });\n });\n } else {\n tlsOptions.socket = usbmuxdSocket;\n usbmuxdSocket = tls.connect(tlsOptions);\n }\n }\n const client = new ServiceType(usbmuxdSocket);\n this.connections.push(client.socket);\n return client;\n }\n\n end() {\n for (const socket of this.connections) {\n // may already be closed\n try {\n socket.end();\n } catch {}\n }\n }\n}\n\nclass UsbmuxdProxy extends Duplex {\n constructor(private usbmuxdSock: Socket) {\n super();\n\n this.usbmuxdSock.on('data', (data) => {\n this.push(data);\n });\n }\n\n _write(chunk: any, encoding: string, callback: (err?: Error) => void) {\n this.usbmuxdSock.write(chunk);\n callback();\n }\n\n _read(size: number) {\n // Stub so we don't error, since we push everything we get from usbmuxd as it comes in.\n // TODO: better way to do this?\n }\n\n _destroy() {\n this.usbmuxdSock.removeAllListeners();\n }\n}\n"],"names":["tls","ClientManager","constructor","pairRecord","device","lockdowndClient","connections","socket","create","udid","usbmuxClient","UsbmuxdClient","connectUsbmuxdSocket","getDevice","readPairRecord","Properties","SerialNumber","lockdownSocket","connect","lockdownClient","LockdowndClient","doHandshake","getUsbmuxdClient","push","getLockdowndClient","getLockdowndClientWithHandshake","getAFCClient","getServiceClient","AFCClient","getInstallationProxyClient","InstallationProxyClient","getMobileImageMounterClient","MobileImageMounterClient","getDebugserverClient","DebugserverClient","name","ServiceType","disableSSL","port","servicePort","enableServiceSSL","startService","usbmuxdSocket","tlsOptions","rejectUnauthorized","secureContext","createSecureContext","secureProtocol","cert","RootCertificate","key","RootPrivateKey","proxy","UsbmuxdProxy","Promise","resolve","reject","timeoutId","setTimeout","CommandError","clearTimeout","destroy","client","end","Duplex","usbmuxdSock","on","data","_write","chunk","encoding","callback","write","_read","size","_destroy","removeAllListeners"],"mappings":"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"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
6
|
+
var fs = _interopRequireWildcard(require("fs"));
|
|
7
|
+
var path = _interopRequireWildcard(require("path"));
|
|
8
|
+
var _util = require("util");
|
|
9
|
+
var _errors = require("../../../../utils/errors");
|
|
10
|
+
var _afcprotocol = require("../protocol/AFCProtocol");
|
|
11
|
+
var _serviceClient = require("./ServiceClient");
|
|
12
|
+
function _interopRequireDefault(obj) {
|
|
13
|
+
return obj && obj.__esModule ? obj : {
|
|
14
|
+
default: obj
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function _interopRequireWildcard(obj) {
|
|
18
|
+
if (obj && obj.__esModule) {
|
|
19
|
+
return obj;
|
|
20
|
+
} else {
|
|
21
|
+
var newObj = {};
|
|
22
|
+
if (obj != null) {
|
|
23
|
+
for(var key in obj){
|
|
24
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
25
|
+
var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {};
|
|
26
|
+
if (desc.get || desc.set) {
|
|
27
|
+
Object.defineProperty(newObj, key, desc);
|
|
28
|
+
} else {
|
|
29
|
+
newObj[key] = obj[key];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
newObj.default = obj;
|
|
35
|
+
return newObj;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const debug = (0, _debug).default("expo:apple-device:client:afc");
|
|
39
|
+
const MAX_OPEN_FILES = 240;
|
|
40
|
+
class AFCClient extends _serviceClient.ServiceClient {
|
|
41
|
+
constructor(socket){
|
|
42
|
+
super(socket, new _afcprotocol.AFCProtocolClient(socket));
|
|
43
|
+
this.socket = socket;
|
|
44
|
+
}
|
|
45
|
+
async getFileInfo(path1) {
|
|
46
|
+
debug(`getFileInfo: ${path1}`);
|
|
47
|
+
const response = await this.protocolClient.sendMessage({
|
|
48
|
+
operation: _afcprotocol.AFC_OPS.GET_FILE_INFO,
|
|
49
|
+
data: toCString(path1)
|
|
50
|
+
});
|
|
51
|
+
debug(`getFileInfo:response: %O`, response);
|
|
52
|
+
const strings = [];
|
|
53
|
+
let currentString = "";
|
|
54
|
+
const tokens = response.data;
|
|
55
|
+
tokens.forEach((token)=>{
|
|
56
|
+
if (token === 0) {
|
|
57
|
+
strings.push(currentString);
|
|
58
|
+
currentString = "";
|
|
59
|
+
} else {
|
|
60
|
+
currentString += String.fromCharCode(token);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return strings;
|
|
64
|
+
}
|
|
65
|
+
async writeFile(fd, data) {
|
|
66
|
+
debug(`writeFile: ${Array.prototype.toString.call(fd)} data size: ${data.length}`);
|
|
67
|
+
const response = await this.protocolClient.sendMessage({
|
|
68
|
+
operation: _afcprotocol.AFC_OPS.FILE_WRITE,
|
|
69
|
+
data: fd,
|
|
70
|
+
payload: data
|
|
71
|
+
});
|
|
72
|
+
debug(`writeFile:response:`, response);
|
|
73
|
+
return response;
|
|
74
|
+
}
|
|
75
|
+
async openFile(path2) {
|
|
76
|
+
debug(`openFile: ${path2}`);
|
|
77
|
+
// mode + path + null terminator
|
|
78
|
+
const data = Buffer.alloc(8 + path2.length + 1);
|
|
79
|
+
// write mode
|
|
80
|
+
data.writeUInt32LE(_afcprotocol.AFC_FILE_OPEN_FLAGS.WRONLY, 0);
|
|
81
|
+
// then path to file
|
|
82
|
+
toCString(path2).copy(data, 8);
|
|
83
|
+
const response = await this.protocolClient.sendMessage({
|
|
84
|
+
operation: _afcprotocol.AFC_OPS.FILE_OPEN,
|
|
85
|
+
data
|
|
86
|
+
});
|
|
87
|
+
// debug(`openFile:response:`, response);
|
|
88
|
+
if (response.operation === _afcprotocol.AFC_OPS.FILE_OPEN_RES) {
|
|
89
|
+
return response.data;
|
|
90
|
+
}
|
|
91
|
+
throw new _errors.CommandError("APPLE_DEVICE_AFC", `There was an unknown error opening file ${path2}, response: ${Array.prototype.toString.call(response.data)}`);
|
|
92
|
+
}
|
|
93
|
+
async closeFile(fd) {
|
|
94
|
+
debug(`closeFile fd: ${Array.prototype.toString.call(fd)}`);
|
|
95
|
+
const response = await this.protocolClient.sendMessage({
|
|
96
|
+
operation: _afcprotocol.AFC_OPS.FILE_CLOSE,
|
|
97
|
+
data: fd
|
|
98
|
+
});
|
|
99
|
+
debug(`closeFile:response:`, response);
|
|
100
|
+
return response;
|
|
101
|
+
}
|
|
102
|
+
async uploadFile(srcPath, destPath) {
|
|
103
|
+
debug(`uploadFile: ${srcPath}, ${destPath}`);
|
|
104
|
+
// read local file and get fd of destination
|
|
105
|
+
const [srcFile, destFile] = await Promise.all([
|
|
106
|
+
await (0, _util).promisify(fs.readFile)(srcPath),
|
|
107
|
+
await this.openFile(destPath),
|
|
108
|
+
]);
|
|
109
|
+
try {
|
|
110
|
+
await this.writeFile(destFile, srcFile);
|
|
111
|
+
await this.closeFile(destFile);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
await this.closeFile(destFile);
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async makeDirectory(path3) {
|
|
118
|
+
debug(`makeDirectory: ${path3}`);
|
|
119
|
+
const response = await this.protocolClient.sendMessage({
|
|
120
|
+
operation: _afcprotocol.AFC_OPS.MAKE_DIR,
|
|
121
|
+
data: toCString(path3)
|
|
122
|
+
});
|
|
123
|
+
debug(`makeDirectory:response:`, response);
|
|
124
|
+
return response;
|
|
125
|
+
}
|
|
126
|
+
async uploadDirectory(srcPath, destPath) {
|
|
127
|
+
debug(`uploadDirectory: ${srcPath}`);
|
|
128
|
+
await this.makeDirectory(destPath);
|
|
129
|
+
// AFC doesn't seem to give out more than 240 file handles,
|
|
130
|
+
// so we delay any requests that would push us over until more open up
|
|
131
|
+
let numOpenFiles = 0;
|
|
132
|
+
const pendingFileUploads = [];
|
|
133
|
+
const _this = this;
|
|
134
|
+
return uploadDir(srcPath);
|
|
135
|
+
async function uploadDir(dirPath) {
|
|
136
|
+
const promises = [];
|
|
137
|
+
for (const file of fs.readdirSync(dirPath)){
|
|
138
|
+
const filePath = path.join(dirPath, file);
|
|
139
|
+
const remotePath = path.join(destPath, path.relative(srcPath, filePath));
|
|
140
|
+
if (fs.lstatSync(filePath).isDirectory()) {
|
|
141
|
+
promises.push(_this.makeDirectory(remotePath).then(()=>uploadDir(filePath)
|
|
142
|
+
));
|
|
143
|
+
} else {
|
|
144
|
+
// Create promise to add to promises array
|
|
145
|
+
// this way it can be resolved once a pending upload has finished
|
|
146
|
+
let resolve;
|
|
147
|
+
let reject;
|
|
148
|
+
const promise = new Promise((res, rej)=>{
|
|
149
|
+
resolve = res;
|
|
150
|
+
reject = rej;
|
|
151
|
+
});
|
|
152
|
+
promises.push(promise);
|
|
153
|
+
// wrap upload in a function in case we need to save it for later
|
|
154
|
+
const uploadFile = (tries = 0)=>{
|
|
155
|
+
numOpenFiles++;
|
|
156
|
+
_this.uploadFile(filePath, remotePath).then(()=>{
|
|
157
|
+
resolve();
|
|
158
|
+
numOpenFiles--;
|
|
159
|
+
const fn = pendingFileUploads.pop();
|
|
160
|
+
if (fn) {
|
|
161
|
+
fn();
|
|
162
|
+
}
|
|
163
|
+
}).catch((err)=>{
|
|
164
|
+
// Couldn't get fd for whatever reason, try again
|
|
165
|
+
// # of retries is arbitrary and can be adjusted
|
|
166
|
+
if (err.status === _afcprotocol.AFC_STATUS.NO_RESOURCES && tries < 10) {
|
|
167
|
+
debug(`Received NO_RESOURCES from AFC, retrying ${filePath} upload. ${tries}`);
|
|
168
|
+
uploadFile(tries++);
|
|
169
|
+
} else {
|
|
170
|
+
numOpenFiles--;
|
|
171
|
+
reject(err);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
if (numOpenFiles < MAX_OPEN_FILES) {
|
|
176
|
+
uploadFile();
|
|
177
|
+
} else {
|
|
178
|
+
debug(`numOpenFiles >= ${MAX_OPEN_FILES}, adding to pending queue. Length: ${pendingFileUploads.length}`);
|
|
179
|
+
pendingFileUploads.push(uploadFile);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
await Promise.all(promises);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
exports.AFCClient = AFCClient;
|
|
188
|
+
function toCString(s) {
|
|
189
|
+
const buf = Buffer.alloc(s.length + 1);
|
|
190
|
+
const len = buf.write(s);
|
|
191
|
+
buf.writeUInt8(0, len);
|
|
192
|
+
return buf;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
//# sourceMappingURL=AFCClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/run/ios/appleDevice/client/AFCClient.ts"],"sourcesContent":["/**\n * Copyright (c) 2021 Expo, Inc.\n * Copyright (c) 2018 Drifty Co.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport Debug from 'debug';\nimport * as fs from 'fs';\nimport { Socket } from 'net';\nimport * as path from 'path';\nimport { promisify } from 'util';\n\nimport { CommandError } from '../../../../utils/errors';\nimport {\n AFC_FILE_OPEN_FLAGS,\n AFC_OPS,\n AFC_STATUS,\n AFCError,\n AFCProtocolClient,\n AFCResponse,\n} from '../protocol/AFCProtocol';\nimport { ServiceClient } from './ServiceClient';\n\nconst debug = Debug('expo:apple-device:client:afc');\nconst MAX_OPEN_FILES = 240;\n\nexport class AFCClient extends ServiceClient<AFCProtocolClient> {\n constructor(public socket: Socket) {\n super(socket, new AFCProtocolClient(socket));\n }\n\n async getFileInfo(path: string): Promise<string[]> {\n debug(`getFileInfo: ${path}`);\n\n const response = await this.protocolClient.sendMessage({\n operation: AFC_OPS.GET_FILE_INFO,\n data: toCString(path),\n });\n debug(`getFileInfo:response: %O`, response);\n\n const strings: string[] = [];\n let currentString = '';\n const tokens = response.data;\n tokens.forEach((token) => {\n if (token === 0) {\n strings.push(currentString);\n currentString = '';\n } else {\n currentString += String.fromCharCode(token);\n }\n });\n return strings;\n }\n\n async writeFile(fd: Buffer, data: Buffer): Promise<AFCResponse> {\n debug(`writeFile: ${Array.prototype.toString.call(fd)} data size: ${data.length}`);\n\n const response = await this.protocolClient.sendMessage({\n operation: AFC_OPS.FILE_WRITE,\n data: fd,\n payload: data,\n });\n\n debug(`writeFile:response:`, response);\n return response;\n }\n\n protected async openFile(path: string): Promise<Buffer> {\n debug(`openFile: ${path}`);\n // mode + path + null terminator\n const data = Buffer.alloc(8 + path.length + 1);\n // write mode\n data.writeUInt32LE(AFC_FILE_OPEN_FLAGS.WRONLY, 0);\n // then path to file\n toCString(path).copy(data, 8);\n\n const response = await this.protocolClient.sendMessage({\n operation: AFC_OPS.FILE_OPEN,\n data,\n });\n\n // debug(`openFile:response:`, response);\n\n if (response.operation === AFC_OPS.FILE_OPEN_RES) {\n return response.data;\n }\n\n throw new CommandError(\n 'APPLE_DEVICE_AFC',\n `There was an unknown error opening file ${path}, response: ${Array.prototype.toString.call(\n response.data\n )}`\n );\n }\n\n protected async closeFile(fd: Buffer): Promise<AFCResponse> {\n debug(`closeFile fd: ${Array.prototype.toString.call(fd)}`);\n const response = await this.protocolClient.sendMessage({\n operation: AFC_OPS.FILE_CLOSE,\n data: fd,\n });\n\n debug(`closeFile:response:`, response);\n return response;\n }\n\n protected async uploadFile(srcPath: string, destPath: string): Promise<void> {\n debug(`uploadFile: ${srcPath}, ${destPath}`);\n\n // read local file and get fd of destination\n const [srcFile, destFile] = await Promise.all([\n await promisify(fs.readFile)(srcPath),\n await this.openFile(destPath),\n ]);\n\n try {\n await this.writeFile(destFile, srcFile);\n await this.closeFile(destFile);\n } catch (err) {\n await this.closeFile(destFile);\n throw err;\n }\n }\n\n async makeDirectory(path: string): Promise<AFCResponse> {\n debug(`makeDirectory: ${path}`);\n\n const response = await this.protocolClient.sendMessage({\n operation: AFC_OPS.MAKE_DIR,\n data: toCString(path),\n });\n\n debug(`makeDirectory:response:`, response);\n return response;\n }\n\n async uploadDirectory(srcPath: string, destPath: string): Promise<void> {\n debug(`uploadDirectory: ${srcPath}`);\n await this.makeDirectory(destPath);\n\n // AFC doesn't seem to give out more than 240 file handles,\n // so we delay any requests that would push us over until more open up\n let numOpenFiles = 0;\n const pendingFileUploads: (() => void)[] = [];\n const _this = this;\n return uploadDir(srcPath);\n\n async function uploadDir(dirPath: string): Promise<void> {\n const promises: Promise<void>[] = [];\n for (const file of fs.readdirSync(dirPath)) {\n const filePath = path.join(dirPath, file);\n const remotePath = path.join(destPath, path.relative(srcPath, filePath));\n if (fs.lstatSync(filePath).isDirectory()) {\n promises.push(_this.makeDirectory(remotePath).then(() => uploadDir(filePath)));\n } else {\n // Create promise to add to promises array\n // this way it can be resolved once a pending upload has finished\n let resolve: (val?: any) => void;\n let reject: (err: AFCError) => void;\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n promises.push(promise);\n\n // wrap upload in a function in case we need to save it for later\n const uploadFile = (tries = 0) => {\n numOpenFiles++;\n _this\n .uploadFile(filePath, remotePath)\n .then(() => {\n resolve();\n numOpenFiles--;\n const fn = pendingFileUploads.pop();\n if (fn) {\n fn();\n }\n })\n .catch((err: AFCError) => {\n // Couldn't get fd for whatever reason, try again\n // # of retries is arbitrary and can be adjusted\n if (err.status === AFC_STATUS.NO_RESOURCES && tries < 10) {\n debug(`Received NO_RESOURCES from AFC, retrying ${filePath} upload. ${tries}`);\n uploadFile(tries++);\n } else {\n numOpenFiles--;\n reject(err);\n }\n });\n };\n\n if (numOpenFiles < MAX_OPEN_FILES) {\n uploadFile();\n } else {\n debug(\n `numOpenFiles >= ${MAX_OPEN_FILES}, adding to pending queue. Length: ${pendingFileUploads.length}`\n );\n pendingFileUploads.push(uploadFile);\n }\n }\n }\n await Promise.all(promises);\n }\n }\n}\n\nfunction toCString(s: string) {\n const buf = Buffer.alloc(s.length + 1);\n const len = buf.write(s);\n buf.writeUInt8(0, len);\n return buf;\n}\n"],"names":["fs","path","debug","Debug","MAX_OPEN_FILES","AFCClient","ServiceClient","constructor","socket","AFCProtocolClient","getFileInfo","response","protocolClient","sendMessage","operation","AFC_OPS","GET_FILE_INFO","data","toCString","strings","currentString","tokens","forEach","token","push","String","fromCharCode","writeFile","fd","Array","prototype","toString","call","length","FILE_WRITE","payload","openFile","Buffer","alloc","writeUInt32LE","AFC_FILE_OPEN_FLAGS","WRONLY","copy","FILE_OPEN","FILE_OPEN_RES","CommandError","closeFile","FILE_CLOSE","uploadFile","srcPath","destPath","srcFile","destFile","Promise","all","promisify","readFile","err","makeDirectory","MAKE_DIR","uploadDirectory","numOpenFiles","pendingFileUploads","_this","uploadDir","dirPath","promises","file","readdirSync","filePath","join","remotePath","relative","lstatSync","isDirectory","then","resolve","reject","promise","res","rej","tries","fn","pop","catch","status","AFC_STATUS","NO_RESOURCES","s","buf","len","write","writeUInt8"],"mappings":"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"}
|