stim 1.8.0 → 1.9.0
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 +23 -155
- package/dist/{android-BRgBYzU0.mjs → android-BPGizezh.mjs} +112 -135
- package/dist/{android-DeYdjjBP.mjs → android-DEgo4oCo.mjs} +2 -2
- package/dist/{android-DWssqHQ8.mjs → android-_y4Ohi5z.mjs} +54 -15
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{android-cas-SWIjzeuL.mjs → android-cas-eiExNLf7.mjs} +21 -14
- package/dist/app-install-Uf3a83Tv.mjs +819 -0
- package/dist/{build-lock-CgtsB6TV.mjs → build-lock-DPXPLgTc.mjs} +120 -24
- package/dist/build-progress-CVqkfPC6.mjs +188 -0
- package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Dg7OnMrp.mjs} +4 -4
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +22 -19
- package/dist/collector-run.d.mts +2 -0
- package/dist/collector-run.mjs +10 -9
- package/dist/{command-output-B3ycpsYv.mjs → command-output-gByVmIjy.mjs} +11 -2
- package/dist/{config-B7iyKWkL.mjs → config-Cp9lM3wX.mjs} +187 -57
- package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-Bq5DcLGn.mjs} +2 -2
- package/dist/{deps-oCXVNEqQ.mjs → deps-Cip5vUm1.mjs} +8 -5
- package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-KEu0_AXL.mjs} +1 -1
- package/dist/{device-BUoZyMoR.mjs → dev-client-HyqISM0h.mjs} +1178 -635
- package/dist/{device-RdujjHLB.mjs → device-D4U-xLty.mjs} +13 -20
- package/dist/{device-lease-Ffz_M5t0.mjs → device-lease-Crs-HkJY.mjs} +16 -9
- package/dist/{device-pool-BxRwdufO.mjs → device-pool-CnTAz8X1.mjs} +7 -7
- package/dist/{device-remote-ByN8P6CB.mjs → device-remote-AQ5rQPqr.mjs} +482 -35
- package/dist/{doctor-BMt156qe.mjs → doctor-B_Ei3bZu.mjs} +95 -107
- package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-Vfz83GCF.mjs} +22 -16
- package/dist/exec-jIAlgilk.mjs +132 -0
- package/dist/{gc-v2YcoJvm.mjs → gc-Ckw8RQHi.mjs} +763 -126
- package/dist/{guide-BTAlD1_y.mjs → guide-BiAbhs1J.mjs} +891 -501
- package/dist/{guide-status-Craq9z0t.mjs → guide-status-BAMVU0ZA.mjs} +5 -3
- package/dist/{ios-BcCiI4TS.mjs → ios-BL2NFckr.mjs} +43 -20
- package/dist/{ios-BP04pY5f.mjs → ios-BzUJWk1K.mjs} +141 -175
- package/dist/{ios-device-CauFJiYx.mjs → ios-device-D972i9wf.mjs} +149 -52
- package/dist/{ios-device-CFXA46ez.mjs → ios-device-DPsSm0v_.mjs} +3 -2
- package/dist/launch-verify-Q_OtW1Rf.mjs +365 -0
- package/dist/{logs-CakdBTtm.mjs → logs-CI9NQSK7.mjs} +18 -13
- package/dist/{logs-query-BGL-Pl12.mjs → logs-query-Cqma3WIu.mjs} +36 -28
- package/dist/{metro-C7bcJ0uo.mjs → metro-DrLHaMn2.mjs} +78 -8
- package/dist/{named-ports-DzMOlmaX.mjs → named-ports-CmLQSIRj.mjs} +4 -4
- package/dist/{ndjson-BZwI1NBv.mjs → ndjson-9uFNRwfe.mjs} +36 -3
- package/dist/{ownership-Cp52QAK1.mjs → ownership-D15Op8vN.mjs} +3 -3
- package/dist/{ownership-wolE-0Do.mjs → ownership-DCQPuWEc.mjs} +26 -7
- package/dist/{ports-BIbJaKNW.mjs → ports-BNKoWVWZ.mjs} +3 -3
- package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
- package/dist/{project-DDZWf_16.mjs → project-DS112DYB.mjs} +8 -3
- package/dist/{reload-BdypPbjM.mjs → reload-ZNJUo1pl.mjs} +12 -13
- package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DWkkcIxV.mjs} +2 -2
- package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-B_lctBR3.mjs} +8 -7
- package/dist/server-expo-CeILEu86.mjs +2 -0
- package/dist/settings-BKNh5rl4.mjs +376 -0
- package/dist/{settings-BXXorVM2.mjs → settings-D030k0t4.mjs} +464 -152
- package/dist/settings.schema.json +989 -0
- package/dist/{teardown-BJqEzPo_.mjs → sim-pool-KaIsp2Ft.mjs} +14 -419
- package/dist/simslim-B_LsGLuJ.mjs +259 -0
- package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-DU9A-gYw.mjs} +4 -4
- package/dist/spawn-claims-Dn3C_N6w.mjs +49 -0
- package/dist/{start-CmxtOKjU.mjs → start-DYkkKF0s.mjs} +27 -31
- package/dist/{state-DZobzxLW.mjs → state-NcJzMEBF.mjs} +5 -3
- package/dist/{stats-CLg5RuDS.mjs → stats-B63-JVAY.mjs} +4 -4
- package/dist/{stats-BYcZ6n1h.mjs → stats-DG9e66Rz.mjs} +66 -5
- package/dist/status-S6ehL4Ui.mjs +932 -0
- package/dist/{stop-C8JW6kKp.mjs → stop-BO28HGqF.mjs} +101 -25
- package/dist/{stop-B1kOpYei.mjs → stop-T1R-cZRz.mjs} +2 -2
- package/dist/supervisor-run.mjs +52 -34
- package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-DWaiRD0e.mjs} +14 -9
- package/dist/{workspace-state-BT5E3SA2.mjs → workspace-state-DF2gCFhx.mjs} +52 -29
- package/dist/worktree-DKhSF0UX.mjs +2 -0
- package/dist/{worktree-CFLA-6MX.mjs → worktree-LGB-UwgG.mjs} +603 -59
- package/dist/{worktree-FG_ZKKdb.mjs → worktree-R_cUlE7s.mjs} +62 -41
- package/dist/{xcode-B6YaWeg0.mjs → xcode-BZqyxh3e.mjs} +216 -58
- package/package.json +5 -6
- package/skill/SKILL.md +2 -0
- package/dist/app-install-BDbsSgCl.mjs +0 -1175
- package/dist/build-slots-C-oAHRT1.mjs +0 -151
- package/dist/dev-client-BmT-ioFI.mjs +0 -797
- package/dist/exec-B29XNuhP.mjs +0 -77
- package/dist/reclaim-KJ-0uWEF.mjs +0 -361
- package/dist/server-expo-YDDG3eP7.mjs +0 -2
- package/dist/status-DdkLki47.mjs +0 -426
- package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
|
@@ -0,0 +1,819 @@
|
|
|
1
|
+
import { t as getExecutor } from "./exec-jIAlgilk.mjs";
|
|
2
|
+
import { E as uninstallIosApp, l as iosSimulatorFailureAdvice, m as listUserApps } from "./ios-BL2NFckr.mjs";
|
|
3
|
+
import { closeSync, lstatSync, openSync, readSync, readdirSync } from "node:fs";
|
|
4
|
+
import { isAbsolute, join } from "node:path";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
//#region src/engine/installed-artifact.ts
|
|
7
|
+
const READ_CHUNK_BYTES = 1048576;
|
|
8
|
+
const DEVICE_PATH_TIMEOUT_MS = 3e4;
|
|
9
|
+
const DEVICE_HASH_TIMEOUT_MS = 12e4;
|
|
10
|
+
const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
11
|
+
function parseInstalledApkPath(text) {
|
|
12
|
+
if (typeof text !== "string") return null;
|
|
13
|
+
const paths = [];
|
|
14
|
+
for (const raw of text.split("\n")) {
|
|
15
|
+
const line = raw.trim();
|
|
16
|
+
if (!line.startsWith("package:")) continue;
|
|
17
|
+
const path = line.slice(8);
|
|
18
|
+
if (path.startsWith("/")) paths.push(path);
|
|
19
|
+
}
|
|
20
|
+
return paths.length === 1 ? paths[0] : null;
|
|
21
|
+
}
|
|
22
|
+
function parseDeviceSha256(text) {
|
|
23
|
+
if (typeof text !== "string") return null;
|
|
24
|
+
const first = text.trim().split(/\s+/)[0] ?? "";
|
|
25
|
+
return SHA256_HEX.test(first) ? first : null;
|
|
26
|
+
}
|
|
27
|
+
function parseAppContainerPath(text) {
|
|
28
|
+
if (typeof text !== "string") return null;
|
|
29
|
+
const lines = text.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
30
|
+
return lines.length === 1 && isAbsolute(lines[0]) ? lines[0] : null;
|
|
31
|
+
}
|
|
32
|
+
function artifactsMatch(local, installed) {
|
|
33
|
+
return Boolean(local) && local === installed;
|
|
34
|
+
}
|
|
35
|
+
function hashFile(file) {
|
|
36
|
+
let fd = null;
|
|
37
|
+
try {
|
|
38
|
+
fd = openSync(file, "r");
|
|
39
|
+
const digest = createHash("sha256");
|
|
40
|
+
const buffer = Buffer.allocUnsafe(READ_CHUNK_BYTES);
|
|
41
|
+
for (;;) {
|
|
42
|
+
const read = readSync(fd, buffer, 0, READ_CHUNK_BYTES, null);
|
|
43
|
+
if (read <= 0) break;
|
|
44
|
+
digest.update(buffer.subarray(0, read));
|
|
45
|
+
}
|
|
46
|
+
return digest.digest("hex");
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
} finally {
|
|
50
|
+
if (fd !== null) try {
|
|
51
|
+
closeSync(fd);
|
|
52
|
+
} catch {}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function hashBundle(dir) {
|
|
56
|
+
const digest = createHash("sha256");
|
|
57
|
+
const walk = (abs, rel) => {
|
|
58
|
+
let names;
|
|
59
|
+
try {
|
|
60
|
+
names = readdirSync(abs);
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
for (const name of names.toSorted()) {
|
|
65
|
+
const child = join(abs, name);
|
|
66
|
+
const path = rel === "" ? name : `${rel}/${name}`;
|
|
67
|
+
let stat;
|
|
68
|
+
try {
|
|
69
|
+
stat = lstatSync(child);
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
if (stat.isDirectory()) {
|
|
74
|
+
if (!walk(child, path)) return false;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (!stat.isFile()) return false;
|
|
78
|
+
const file = hashFile(child);
|
|
79
|
+
if (file === null) return false;
|
|
80
|
+
digest.update(`${path}\0${file}\n`);
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
return walk(dir, "") ? digest.digest("hex") : null;
|
|
85
|
+
}
|
|
86
|
+
function deviceHoldsApk({ serial, packageName, apkPath }, { exec = null } = {}) {
|
|
87
|
+
const e = exec || getExecutor();
|
|
88
|
+
let path;
|
|
89
|
+
try {
|
|
90
|
+
path = parseInstalledApkPath(e.runFile("adb", [
|
|
91
|
+
"-s",
|
|
92
|
+
serial,
|
|
93
|
+
"shell",
|
|
94
|
+
"pm",
|
|
95
|
+
"path",
|
|
96
|
+
packageName
|
|
97
|
+
], { timeoutMs: DEVICE_PATH_TIMEOUT_MS }));
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (!path) return false;
|
|
102
|
+
let installed;
|
|
103
|
+
try {
|
|
104
|
+
installed = parseDeviceSha256(e.runFile("adb", [
|
|
105
|
+
"-s",
|
|
106
|
+
serial,
|
|
107
|
+
"shell",
|
|
108
|
+
"sha256sum",
|
|
109
|
+
path
|
|
110
|
+
], { timeoutMs: DEVICE_HASH_TIMEOUT_MS }));
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
if (!installed) return false;
|
|
115
|
+
return artifactsMatch(hashFile(apkPath), installed);
|
|
116
|
+
}
|
|
117
|
+
function deviceHoldsBundle({ udid, bundleId, appPath }, { exec = null } = {}) {
|
|
118
|
+
const e = exec || getExecutor();
|
|
119
|
+
let container;
|
|
120
|
+
try {
|
|
121
|
+
container = parseAppContainerPath(e.runFile("xcrun", [
|
|
122
|
+
"simctl",
|
|
123
|
+
"get_app_container",
|
|
124
|
+
udid,
|
|
125
|
+
bundleId
|
|
126
|
+
], { timeoutMs: DEVICE_PATH_TIMEOUT_MS }));
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
if (!container) return false;
|
|
131
|
+
const installed = hashBundle(container);
|
|
132
|
+
if (!installed) return false;
|
|
133
|
+
return artifactsMatch(hashBundle(appPath), installed);
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/engine/app-install.ts
|
|
137
|
+
const INSTALL_ERROR = "STIM_INSTALL_FAILED";
|
|
138
|
+
const LAUNCH_ERROR = "STIM_LAUNCH_FAILED";
|
|
139
|
+
const DEFAULT_METRO_PORT = 8081;
|
|
140
|
+
const IOS_SCHEME_APPROVAL_DOMAIN = "com.apple.launchservices.schemeapproval";
|
|
141
|
+
const IOS_SCHEME_APPROVAL_OPENER = "com.apple.CoreSimulator.CoreSimulatorBridge";
|
|
142
|
+
const IOS_DEV_MENU_OFF_KEYS = ["EXDevMenuShowsAtLaunch", "EXDevMenuShowFloatingActionButton"];
|
|
143
|
+
const DEV_CLIENT_ONBOARDING_QUERY = "disableOnboarding=1";
|
|
144
|
+
const DEV_CLIENT_DISABLE_FAB_QUERY = "disableFab=1";
|
|
145
|
+
const ANDROID_DISABLE_AUTO_LAUNCH_EXTRA = "EXDevMenuDisableAutoLaunch";
|
|
146
|
+
function describeIosSimulatorFailure(error, exec) {
|
|
147
|
+
const detail = describe(error);
|
|
148
|
+
return error?.code === "ETIMEDOUT" ? `${detail}. ${iosSimulatorFailureAdvice(exec)}` : detail;
|
|
149
|
+
}
|
|
150
|
+
function installIosApp({ udid, appPath, bundleId = null, devClientScheme = null, proveInstalled = true }, { exec = null, now = null } = {}) {
|
|
151
|
+
const e = exec || getExecutor();
|
|
152
|
+
const artifactStartedAt = now?.();
|
|
153
|
+
const skipped = bundleId && proveInstalled ? deviceHoldsBundle({
|
|
154
|
+
udid,
|
|
155
|
+
bundleId,
|
|
156
|
+
appPath
|
|
157
|
+
}, { exec: e }) : false;
|
|
158
|
+
if (!skipped) try {
|
|
159
|
+
e.runFile("xcrun", [
|
|
160
|
+
"simctl",
|
|
161
|
+
"install",
|
|
162
|
+
udid,
|
|
163
|
+
appPath
|
|
164
|
+
], {
|
|
165
|
+
timeoutMs: 3e5,
|
|
166
|
+
killSignal: "SIGKILL"
|
|
167
|
+
});
|
|
168
|
+
} catch (err) {
|
|
169
|
+
return {
|
|
170
|
+
failed: true,
|
|
171
|
+
code: INSTALL_ERROR,
|
|
172
|
+
reason: `simctl install failed for ${appPath}: ${describeIosSimulatorFailure(err, e)}`
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const artifactFinishedAt = now?.();
|
|
176
|
+
const artifactDurationMs = artifactStartedAt !== void 0 && artifactFinishedAt !== void 0 ? artifactFinishedAt - artifactStartedAt : void 0;
|
|
177
|
+
const preparationStartedAt = bundleId && devClientScheme ? artifactFinishedAt : void 0;
|
|
178
|
+
if (bundleId && devClientScheme) try {
|
|
179
|
+
for (const key of IOS_DEV_MENU_OFF_KEYS) e.runFile("xcrun", [
|
|
180
|
+
"simctl",
|
|
181
|
+
"spawn",
|
|
182
|
+
udid,
|
|
183
|
+
"defaults",
|
|
184
|
+
"write",
|
|
185
|
+
bundleId,
|
|
186
|
+
key,
|
|
187
|
+
"-bool",
|
|
188
|
+
"false"
|
|
189
|
+
], { timeoutMs: 6e4 });
|
|
190
|
+
for (const key of iosSchemeApprovalKeys(bundleId, devClientScheme)) e.runFile("xcrun", [
|
|
191
|
+
"simctl",
|
|
192
|
+
"spawn",
|
|
193
|
+
udid,
|
|
194
|
+
"defaults",
|
|
195
|
+
"write",
|
|
196
|
+
IOS_SCHEME_APPROVAL_DOMAIN,
|
|
197
|
+
key,
|
|
198
|
+
"-string",
|
|
199
|
+
bundleId
|
|
200
|
+
], { timeoutMs: 6e4 });
|
|
201
|
+
} catch (err) {
|
|
202
|
+
return {
|
|
203
|
+
failed: true,
|
|
204
|
+
code: INSTALL_ERROR,
|
|
205
|
+
reason: `Installed ${bundleId}, but could not prepare the dev client: ${describeIosSimulatorFailure(err, e)}`
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const devClientPreparationDurationMs = now && preparationStartedAt !== void 0 ? now() - preparationStartedAt : void 0;
|
|
209
|
+
const timing = {
|
|
210
|
+
...artifactDurationMs === void 0 ? {} : { artifactDurationMs },
|
|
211
|
+
...devClientPreparationDurationMs === void 0 ? {} : { devClientPreparationDurationMs }
|
|
212
|
+
};
|
|
213
|
+
return skipped ? {
|
|
214
|
+
ok: true,
|
|
215
|
+
appPath,
|
|
216
|
+
skipped: true,
|
|
217
|
+
...timing
|
|
218
|
+
} : {
|
|
219
|
+
ok: true,
|
|
220
|
+
appPath,
|
|
221
|
+
...timing
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function clearOtherUserApps({ udid, keep }, { list = listUserApps, uninstall = uninstallIosApp } = {}) {
|
|
225
|
+
const removed = [];
|
|
226
|
+
const failed = [];
|
|
227
|
+
let installed;
|
|
228
|
+
try {
|
|
229
|
+
installed = list(udid);
|
|
230
|
+
} catch {
|
|
231
|
+
return {
|
|
232
|
+
listed: false,
|
|
233
|
+
removed,
|
|
234
|
+
failed
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
for (const bundleId of installed) {
|
|
238
|
+
if (bundleId === keep) continue;
|
|
239
|
+
try {
|
|
240
|
+
uninstall(udid, bundleId);
|
|
241
|
+
removed.push(bundleId);
|
|
242
|
+
} catch {
|
|
243
|
+
failed.push(bundleId);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
listed: true,
|
|
248
|
+
removed,
|
|
249
|
+
failed
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function jsLocationValue(metroPort) {
|
|
253
|
+
return `localhost:${metroPort}`;
|
|
254
|
+
}
|
|
255
|
+
function devClientDeepLink(scheme, projectOrigin) {
|
|
256
|
+
const projectUrl = `${projectOrigin.replace(/\/+$/, "")}/?${DEV_CLIENT_ONBOARDING_QUERY}`;
|
|
257
|
+
return `${scheme}://expo-development-client/?url=${encodeURIComponent(projectUrl)}&${DEV_CLIENT_DISABLE_FAB_QUERY}`;
|
|
258
|
+
}
|
|
259
|
+
function devClientUrl(scheme, metroPort, host = "localhost") {
|
|
260
|
+
return devClientDeepLink(scheme, `http://${host}:${metroPort}`);
|
|
261
|
+
}
|
|
262
|
+
function iosSchemeApprovalKeys(bundleId, devClientScheme) {
|
|
263
|
+
return [.../* @__PURE__ */ new Set([bundleId, devClientScheme])].map((target) => `${IOS_SCHEME_APPROVAL_OPENER}-->${target}`);
|
|
264
|
+
}
|
|
265
|
+
function parseLaunchedPid(text) {
|
|
266
|
+
if (typeof text !== "string") return null;
|
|
267
|
+
const match = text.trim().match(/:\s*(\d+)\s*$/);
|
|
268
|
+
if (!match) return null;
|
|
269
|
+
const pid = Number(match[1]);
|
|
270
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
271
|
+
}
|
|
272
|
+
function iosAppProcess(udid, bundleId, { exec = null } = {}) {
|
|
273
|
+
const e = exec || getExecutor();
|
|
274
|
+
let out = "";
|
|
275
|
+
try {
|
|
276
|
+
out = e.runFile("xcrun", [
|
|
277
|
+
"simctl",
|
|
278
|
+
"spawn",
|
|
279
|
+
udid,
|
|
280
|
+
"launchctl",
|
|
281
|
+
"list"
|
|
282
|
+
], { timeoutMs: 2e3 });
|
|
283
|
+
} catch {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const label = `UIKitApplication:${bundleId}[`;
|
|
287
|
+
for (const line of out.split("\n")) {
|
|
288
|
+
if (!line.includes(label)) continue;
|
|
289
|
+
const pid = Number(line.trim().split(/\s+/)[0]);
|
|
290
|
+
if (Number.isInteger(pid) && pid > 0) return pid;
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
function launchedIosAppAfterNoHandle(error, udid, bundleId, exec) {
|
|
295
|
+
const stderr = String(error?.stderr ?? "");
|
|
296
|
+
if (!stderr.includes("domain=NSPOSIXErrorDomain, code=3") || !stderr.includes(`Application launch for '${bundleId}' did not return a process handle nor launch error.`)) return null;
|
|
297
|
+
return iosAppProcess(udid, bundleId, { exec }) ?? null;
|
|
298
|
+
}
|
|
299
|
+
function launchIosApp({ udid, bundleId, metroPort, devClientScheme = null, consolePaths }, { exec = null } = {}) {
|
|
300
|
+
const e = exec || getExecutor();
|
|
301
|
+
const launchArgs = [
|
|
302
|
+
"simctl",
|
|
303
|
+
"launch",
|
|
304
|
+
...consolePaths ? [`--stdout=${consolePaths.stdout}`, `--stderr=${consolePaths.stderr}`] : [],
|
|
305
|
+
udid,
|
|
306
|
+
bundleId
|
|
307
|
+
];
|
|
308
|
+
if (metroPort !== null) {
|
|
309
|
+
try {
|
|
310
|
+
e.runFile("xcrun", [
|
|
311
|
+
"simctl",
|
|
312
|
+
"spawn",
|
|
313
|
+
udid,
|
|
314
|
+
"defaults",
|
|
315
|
+
"write",
|
|
316
|
+
bundleId,
|
|
317
|
+
"RCT_jsLocation",
|
|
318
|
+
jsLocationValue(metroPort)
|
|
319
|
+
], { timeoutMs: 6e4 });
|
|
320
|
+
} catch (err) {
|
|
321
|
+
return {
|
|
322
|
+
failed: true,
|
|
323
|
+
code: LAUNCH_ERROR,
|
|
324
|
+
reason: `Could not point ${bundleId} at Metro port ${metroPort} (defaults write RCT_jsLocation): ${describeIosSimulatorFailure(err, e)}`
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
if (devClientScheme) {
|
|
328
|
+
const url = devClientUrl(devClientScheme, metroPort);
|
|
329
|
+
let launchedWithInitialUrl = false;
|
|
330
|
+
try {
|
|
331
|
+
if (consolePaths && iosAppProcess(udid, bundleId, { exec: e }) === null) {
|
|
332
|
+
const initialUrl = new URL(url).searchParams.get("url");
|
|
333
|
+
launchedWithInitialUrl = true;
|
|
334
|
+
const pid = parseLaunchedPid(e.runFile("xcrun", [
|
|
335
|
+
...launchArgs,
|
|
336
|
+
"--initialUrl",
|
|
337
|
+
initialUrl
|
|
338
|
+
], { timeoutMs: 6e4 }));
|
|
339
|
+
return {
|
|
340
|
+
ok: true,
|
|
341
|
+
mode: "launch",
|
|
342
|
+
url,
|
|
343
|
+
jsLocation: jsLocationValue(metroPort),
|
|
344
|
+
pid
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
e.runFile("xcrun", [
|
|
348
|
+
"simctl",
|
|
349
|
+
"openurl",
|
|
350
|
+
udid,
|
|
351
|
+
url
|
|
352
|
+
], { timeoutMs: 6e4 });
|
|
353
|
+
return {
|
|
354
|
+
ok: true,
|
|
355
|
+
mode: "openurl",
|
|
356
|
+
url,
|
|
357
|
+
jsLocation: jsLocationValue(metroPort)
|
|
358
|
+
};
|
|
359
|
+
} catch (err) {
|
|
360
|
+
const pid = launchedWithInitialUrl ? launchedIosAppAfterNoHandle(err, udid, bundleId, e) : null;
|
|
361
|
+
if (pid) return {
|
|
362
|
+
ok: true,
|
|
363
|
+
mode: "launch",
|
|
364
|
+
url,
|
|
365
|
+
jsLocation: jsLocationValue(metroPort),
|
|
366
|
+
pid
|
|
367
|
+
};
|
|
368
|
+
return {
|
|
369
|
+
failed: true,
|
|
370
|
+
code: LAUNCH_ERROR,
|
|
371
|
+
reason: `simctl ${launchedWithInitialUrl ? `launch ${bundleId}` : `openurl ${url}`} failed: ${describeIosSimulatorFailure(err, e)}`
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const preLaunchPid = iosAppProcess(udid, bundleId, { exec: e });
|
|
377
|
+
try {
|
|
378
|
+
const result = {
|
|
379
|
+
ok: true,
|
|
380
|
+
mode: "launch",
|
|
381
|
+
pid: parseLaunchedPid(e.runFile("xcrun", launchArgs, { timeoutMs: 6e4 }))
|
|
382
|
+
};
|
|
383
|
+
if (metroPort !== null) result.jsLocation = jsLocationValue(metroPort);
|
|
384
|
+
return result;
|
|
385
|
+
} catch (err) {
|
|
386
|
+
const pid = preLaunchPid === null ? launchedIosAppAfterNoHandle(err, udid, bundleId, e) : null;
|
|
387
|
+
if (pid) {
|
|
388
|
+
const result = {
|
|
389
|
+
ok: true,
|
|
390
|
+
mode: "launch",
|
|
391
|
+
pid
|
|
392
|
+
};
|
|
393
|
+
if (metroPort !== null) result.jsLocation = jsLocationValue(metroPort);
|
|
394
|
+
return result;
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
failed: true,
|
|
398
|
+
code: LAUNCH_ERROR,
|
|
399
|
+
reason: `simctl launch ${bundleId} failed: ${describeIosSimulatorFailure(err, e)}`
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
function installConflictKind(text) {
|
|
404
|
+
const out = String(text ?? "");
|
|
405
|
+
if (/INSTALL_FAILED_UPDATE_INCOMPATIBLE|INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES|signatures do not match/i.test(out)) return "signature";
|
|
406
|
+
if (/INSTALL_FAILED_VERSION_DOWNGRADE/i.test(out)) return "downgrade";
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const ADB_INSTALL_TIMEOUT_MS = 3e5;
|
|
410
|
+
const ADB_UNINSTALL_TIMEOUT_MS = 12e4;
|
|
411
|
+
const ADB_SHELL_OPTIONS = {
|
|
412
|
+
timeoutMs: 3e4,
|
|
413
|
+
killSignal: "SIGKILL"
|
|
414
|
+
};
|
|
415
|
+
function installAndroidApp({ serial, apkPath, packageName = null, allowUninstall = false }, { exec = null } = {}) {
|
|
416
|
+
const e = exec || getExecutor();
|
|
417
|
+
if (packageName && deviceHoldsApk({
|
|
418
|
+
serial,
|
|
419
|
+
packageName,
|
|
420
|
+
apkPath
|
|
421
|
+
}, { exec: e })) return {
|
|
422
|
+
ok: true,
|
|
423
|
+
apkPath,
|
|
424
|
+
skipped: true
|
|
425
|
+
};
|
|
426
|
+
const install = () => {
|
|
427
|
+
e.runFile("adb", [
|
|
428
|
+
"-s",
|
|
429
|
+
serial,
|
|
430
|
+
"install",
|
|
431
|
+
"-r",
|
|
432
|
+
apkPath
|
|
433
|
+
], { timeoutMs: ADB_INSTALL_TIMEOUT_MS });
|
|
434
|
+
};
|
|
435
|
+
try {
|
|
436
|
+
install();
|
|
437
|
+
return {
|
|
438
|
+
ok: true,
|
|
439
|
+
apkPath
|
|
440
|
+
};
|
|
441
|
+
} catch (err) {
|
|
442
|
+
const conflict = installConflictKind(describe(err));
|
|
443
|
+
if (!conflict || !allowUninstall || !packageName) return {
|
|
444
|
+
failed: true,
|
|
445
|
+
code: INSTALL_ERROR,
|
|
446
|
+
reason: `adb install failed for ${apkPath}: ${describe(err)}`
|
|
447
|
+
};
|
|
448
|
+
try {
|
|
449
|
+
e.runFile("adb", [
|
|
450
|
+
"-s",
|
|
451
|
+
serial,
|
|
452
|
+
"uninstall",
|
|
453
|
+
packageName
|
|
454
|
+
], {
|
|
455
|
+
timeoutMs: ADB_UNINSTALL_TIMEOUT_MS,
|
|
456
|
+
killSignal: "SIGKILL"
|
|
457
|
+
});
|
|
458
|
+
} catch (uninstallErr) {
|
|
459
|
+
return {
|
|
460
|
+
failed: true,
|
|
461
|
+
code: INSTALL_ERROR,
|
|
462
|
+
reason: `adb install failed for ${apkPath} (${conflict}) and ${packageName} could not be uninstalled: ` + describe(uninstallErr)
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
install();
|
|
467
|
+
} catch (retryErr) {
|
|
468
|
+
return {
|
|
469
|
+
failed: true,
|
|
470
|
+
code: INSTALL_ERROR,
|
|
471
|
+
reason: `adb install failed for ${apkPath} even after uninstalling ${packageName}: ${describe(retryErr)}`
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
return {
|
|
475
|
+
ok: true,
|
|
476
|
+
apkPath,
|
|
477
|
+
uninstalled: true,
|
|
478
|
+
note: conflict === "signature" ? `${packageName} was already installed with a different signer, so it was uninstalled (its data went with it) before this APK could be installed` : `${packageName} was already installed at a higher versionCode, so it was uninstalled (its data went with it) before this APK could be installed`
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function parseResolvedActivity(text) {
|
|
483
|
+
if (typeof text !== "string") return null;
|
|
484
|
+
for (const raw of text.split("\n")) {
|
|
485
|
+
const line = raw.trim();
|
|
486
|
+
if (!line) continue;
|
|
487
|
+
if (/^No activity found/i.test(line)) return null;
|
|
488
|
+
if (line.includes("=")) continue;
|
|
489
|
+
if (!line.includes("/")) continue;
|
|
490
|
+
return line;
|
|
491
|
+
}
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
function resolveLaunchActivity(serial, packageName, { exec = null } = {}) {
|
|
495
|
+
const e = exec || getExecutor();
|
|
496
|
+
try {
|
|
497
|
+
return parseResolvedActivity(e.runFile("adb", [
|
|
498
|
+
"-s",
|
|
499
|
+
serial,
|
|
500
|
+
"shell",
|
|
501
|
+
"cmd",
|
|
502
|
+
"package",
|
|
503
|
+
"resolve-activity",
|
|
504
|
+
"--brief",
|
|
505
|
+
"-c",
|
|
506
|
+
"android.intent.category.LAUNCHER",
|
|
507
|
+
packageName
|
|
508
|
+
], ADB_SHELL_OPTIONS));
|
|
509
|
+
} catch {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
function reverseMetroPorts({ serial, metroPort, devicePorts = null }, { exec = null } = {}) {
|
|
514
|
+
const e = exec || getExecutor();
|
|
515
|
+
const pairs = (devicePorts ?? [metroPort]).map((device) => [device, metroPort]);
|
|
516
|
+
for (const [device, host] of pairs) try {
|
|
517
|
+
e.runFile("adb", [
|
|
518
|
+
"-s",
|
|
519
|
+
serial,
|
|
520
|
+
"reverse",
|
|
521
|
+
`tcp:${device}`,
|
|
522
|
+
`tcp:${host}`
|
|
523
|
+
], ADB_SHELL_OPTIONS);
|
|
524
|
+
} catch (err) {
|
|
525
|
+
return {
|
|
526
|
+
failed: true,
|
|
527
|
+
code: LAUNCH_ERROR,
|
|
528
|
+
reason: `adb reverse tcp:${device} tcp:${host} failed on ${serial}: ${describe(err)}`
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
return {
|
|
532
|
+
ok: true,
|
|
533
|
+
reversed: pairs.map(([device, host]) => `tcp:${device}->tcp:${host}`)
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
const EMULATOR_HOST_LOOPBACK = "127.0.0.1";
|
|
537
|
+
const PHYSICAL_HOST_LOOPBACK = "localhost";
|
|
538
|
+
function androidMetroHost(physical) {
|
|
539
|
+
return physical ? PHYSICAL_HOST_LOOPBACK : EMULATOR_HOST_LOOPBACK;
|
|
540
|
+
}
|
|
541
|
+
function deviceShellArg(text) {
|
|
542
|
+
return `'${String(text).replace(/'/g, "'\\''")}'`;
|
|
543
|
+
}
|
|
544
|
+
function debugHttpHostScript({ packageName, host, dataDir = null }) {
|
|
545
|
+
const dir = dataDir || `/data/data/${packageName}`;
|
|
546
|
+
const prefs = `shared_prefs/${packageName}_preferences.xml`;
|
|
547
|
+
const tmp = `${prefs}.stim.tmp`;
|
|
548
|
+
return [
|
|
549
|
+
`cd ${dir} || exit 1`,
|
|
550
|
+
"mkdir -p shared_prefs || exit 1",
|
|
551
|
+
`printf '%s\\n' '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>' '<map>' > ${tmp} || exit 1`,
|
|
552
|
+
`if [ -f ${prefs} ]; then grep -v 'debug_http_host' ${prefs} | grep -v '<?xml' | grep -v '<map' | grep -v '</map>' >> ${tmp}; fi`,
|
|
553
|
+
`printf '%s\\n' ' <string name="debug_http_host">${host}</string>' '</map>' >> ${tmp} || exit 1`,
|
|
554
|
+
`mv ${tmp} ${prefs} || exit 1`,
|
|
555
|
+
`grep -q '>${host}<' ${prefs} || exit 1`
|
|
556
|
+
].join("\n");
|
|
557
|
+
}
|
|
558
|
+
function writeDebugHttpHost({ serial, packageName, metroPort, physical = false }, { exec = null } = {}) {
|
|
559
|
+
const e = exec || getExecutor();
|
|
560
|
+
const host = `${androidMetroHost(physical)}:${metroPort}`;
|
|
561
|
+
const script = debugHttpHostScript({
|
|
562
|
+
packageName,
|
|
563
|
+
host
|
|
564
|
+
});
|
|
565
|
+
try {
|
|
566
|
+
e.runFile("adb", [
|
|
567
|
+
"-s",
|
|
568
|
+
serial,
|
|
569
|
+
"shell",
|
|
570
|
+
"run-as",
|
|
571
|
+
packageName,
|
|
572
|
+
"sh",
|
|
573
|
+
"-c",
|
|
574
|
+
deviceShellArg(script)
|
|
575
|
+
], ADB_SHELL_OPTIONS);
|
|
576
|
+
return {
|
|
577
|
+
ok: true,
|
|
578
|
+
host
|
|
579
|
+
};
|
|
580
|
+
} catch (err) {
|
|
581
|
+
return {
|
|
582
|
+
ok: false,
|
|
583
|
+
reason: `debug_http_host not written (${describe(err)}); relying on adb reverse`
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function androidDevClientUrl(scheme, metroPort, physical = false) {
|
|
588
|
+
return devClientUrl(scheme, metroPort, androidMetroHost(physical));
|
|
589
|
+
}
|
|
590
|
+
function amStartError(text) {
|
|
591
|
+
const out = String(text ?? "");
|
|
592
|
+
for (const raw of out.split("\n")) {
|
|
593
|
+
const line = raw.trim();
|
|
594
|
+
if (/^Error:/i.test(line)) return line;
|
|
595
|
+
}
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
function openAndroidDevClientUrl({ serial, url, packageName }, { exec = null } = {}) {
|
|
599
|
+
const e = exec || getExecutor();
|
|
600
|
+
let out;
|
|
601
|
+
try {
|
|
602
|
+
const args = [
|
|
603
|
+
"-s",
|
|
604
|
+
serial,
|
|
605
|
+
"shell",
|
|
606
|
+
"am",
|
|
607
|
+
"start",
|
|
608
|
+
"-a",
|
|
609
|
+
"android.intent.action.VIEW",
|
|
610
|
+
"-d",
|
|
611
|
+
deviceShellArg(url),
|
|
612
|
+
...packageName ? ["-p", packageName] : [],
|
|
613
|
+
"--ez",
|
|
614
|
+
ANDROID_DISABLE_AUTO_LAUNCH_EXTRA,
|
|
615
|
+
"true"
|
|
616
|
+
];
|
|
617
|
+
out = e.runFile("adb", args, ADB_SHELL_OPTIONS);
|
|
618
|
+
} catch (err) {
|
|
619
|
+
return {
|
|
620
|
+
failed: true,
|
|
621
|
+
reason: `am start -d ${url} failed on ${serial}: ${describe(err)}`
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
const error = amStartError(out);
|
|
625
|
+
if (error) return {
|
|
626
|
+
failed: true,
|
|
627
|
+
reason: `am start -d ${url} did not start anything on ${serial}: ${error}`
|
|
628
|
+
};
|
|
629
|
+
return {
|
|
630
|
+
ok: true,
|
|
631
|
+
url
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
function launchAndroidApp({ serial, packageName, metroPort, devClientScheme = null, physical = false }, { exec = null } = {}) {
|
|
635
|
+
const e = exec || getExecutor();
|
|
636
|
+
const reversed = reverseMetroPorts({
|
|
637
|
+
serial,
|
|
638
|
+
metroPort
|
|
639
|
+
}, { exec: e });
|
|
640
|
+
if (reversed.failed) return reversed;
|
|
641
|
+
const prefs = writeDebugHttpHost({
|
|
642
|
+
serial,
|
|
643
|
+
packageName,
|
|
644
|
+
metroPort,
|
|
645
|
+
physical
|
|
646
|
+
}, { exec: e });
|
|
647
|
+
let reversedPairs = reversed.reversed ?? [];
|
|
648
|
+
if (!prefs.ok && Number(metroPort) !== 8081) {
|
|
649
|
+
const fallback = reverseMetroPorts({
|
|
650
|
+
serial,
|
|
651
|
+
metroPort,
|
|
652
|
+
devicePorts: [DEFAULT_METRO_PORT]
|
|
653
|
+
}, { exec: e });
|
|
654
|
+
if (fallback.failed) return fallback;
|
|
655
|
+
reversedPairs = [...reversedPairs, ...fallback.reversed ?? []];
|
|
656
|
+
}
|
|
657
|
+
const wiring = {
|
|
658
|
+
reversed: reversedPairs,
|
|
659
|
+
debugHttpHost: prefs.ok ? prefs.host : null,
|
|
660
|
+
debugHttpHostNote: prefs.ok ? null : prefs.reason
|
|
661
|
+
};
|
|
662
|
+
let devClientNote = null;
|
|
663
|
+
if (devClientScheme) {
|
|
664
|
+
const url = androidDevClientUrl(devClientScheme, metroPort, physical);
|
|
665
|
+
const opened = openAndroidDevClientUrl({
|
|
666
|
+
serial,
|
|
667
|
+
url
|
|
668
|
+
}, { exec: e });
|
|
669
|
+
if (opened.ok) return {
|
|
670
|
+
ok: true,
|
|
671
|
+
mode: "deep-link",
|
|
672
|
+
devClientUrl: url,
|
|
673
|
+
...wiring
|
|
674
|
+
};
|
|
675
|
+
devClientNote = `${opened.reason}; fell back to the launcher activity`;
|
|
676
|
+
}
|
|
677
|
+
const component = resolveLaunchActivity(serial, packageName, { exec: e });
|
|
678
|
+
if (component) try {
|
|
679
|
+
e.runFile("adb", [
|
|
680
|
+
"-s",
|
|
681
|
+
serial,
|
|
682
|
+
"shell",
|
|
683
|
+
"am",
|
|
684
|
+
"start",
|
|
685
|
+
"-n",
|
|
686
|
+
component
|
|
687
|
+
], ADB_SHELL_OPTIONS);
|
|
688
|
+
return {
|
|
689
|
+
ok: true,
|
|
690
|
+
mode: "am-start",
|
|
691
|
+
component,
|
|
692
|
+
devClientNote,
|
|
693
|
+
...wiring
|
|
694
|
+
};
|
|
695
|
+
} catch (err) {
|
|
696
|
+
return {
|
|
697
|
+
failed: true,
|
|
698
|
+
code: LAUNCH_ERROR,
|
|
699
|
+
reason: `am start -n ${component} failed on ${serial}: ${describe(err)}`
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
try {
|
|
703
|
+
e.runFile("adb", [
|
|
704
|
+
"-s",
|
|
705
|
+
serial,
|
|
706
|
+
"shell",
|
|
707
|
+
"monkey",
|
|
708
|
+
"-p",
|
|
709
|
+
packageName,
|
|
710
|
+
"1"
|
|
711
|
+
], ADB_SHELL_OPTIONS);
|
|
712
|
+
return {
|
|
713
|
+
ok: true,
|
|
714
|
+
mode: "monkey",
|
|
715
|
+
devClientNote,
|
|
716
|
+
...wiring
|
|
717
|
+
};
|
|
718
|
+
} catch (err) {
|
|
719
|
+
return {
|
|
720
|
+
failed: true,
|
|
721
|
+
code: LAUNCH_ERROR,
|
|
722
|
+
reason: `Could not launch ${packageName} on ${serial}: no launcher activity resolved and monkey failed: ${describe(err)}`
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function launchAndroidReleaseApp({ serial, packageName }, { exec = null } = {}) {
|
|
727
|
+
const e = exec || getExecutor();
|
|
728
|
+
const component = resolveLaunchActivity(serial, packageName, { exec: e });
|
|
729
|
+
if (component) try {
|
|
730
|
+
e.runFile("adb", [
|
|
731
|
+
"-s",
|
|
732
|
+
serial,
|
|
733
|
+
"shell",
|
|
734
|
+
"am",
|
|
735
|
+
"start",
|
|
736
|
+
"-n",
|
|
737
|
+
component
|
|
738
|
+
], ADB_SHELL_OPTIONS);
|
|
739
|
+
return {
|
|
740
|
+
ok: true,
|
|
741
|
+
mode: "am-start",
|
|
742
|
+
component
|
|
743
|
+
};
|
|
744
|
+
} catch (err) {
|
|
745
|
+
return {
|
|
746
|
+
failed: true,
|
|
747
|
+
code: LAUNCH_ERROR,
|
|
748
|
+
reason: `am start -n ${component} failed on ${serial}: ${describe(err)}`
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
try {
|
|
752
|
+
e.runFile("adb", [
|
|
753
|
+
"-s",
|
|
754
|
+
serial,
|
|
755
|
+
"shell",
|
|
756
|
+
"monkey",
|
|
757
|
+
"-p",
|
|
758
|
+
packageName,
|
|
759
|
+
"1"
|
|
760
|
+
], ADB_SHELL_OPTIONS);
|
|
761
|
+
return {
|
|
762
|
+
ok: true,
|
|
763
|
+
mode: "monkey"
|
|
764
|
+
};
|
|
765
|
+
} catch (err) {
|
|
766
|
+
return {
|
|
767
|
+
failed: true,
|
|
768
|
+
code: LAUNCH_ERROR,
|
|
769
|
+
reason: `Could not launch ${packageName} on ${serial}: no launcher activity resolved and monkey failed: ${describe(err)}`
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
function describe(err) {
|
|
774
|
+
const e = err;
|
|
775
|
+
const stderr = e?.stderr ? String(e.stderr).trim() : "";
|
|
776
|
+
const message = e?.message ? String(e.message).trim() : String(err);
|
|
777
|
+
return stderr ? `${message}: ${stderr}` : message;
|
|
778
|
+
}
|
|
779
|
+
function parsePidof(text) {
|
|
780
|
+
const first = String(text ?? "").trim().split(/\s+/)[0];
|
|
781
|
+
const pid = Number(first);
|
|
782
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
783
|
+
}
|
|
784
|
+
function parsePsPid(text, packageName) {
|
|
785
|
+
for (const raw of String(text ?? "").split("\n")) {
|
|
786
|
+
const cols = raw.trim().split(/\s+/);
|
|
787
|
+
if (cols.length < 2) continue;
|
|
788
|
+
if (cols[cols.length - 1] !== packageName) continue;
|
|
789
|
+
const pid = Number(cols[1]);
|
|
790
|
+
if (Number.isFinite(pid) && pid > 0) return pid;
|
|
791
|
+
}
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
function androidAppProcess(serial, packageName, { exec = null } = {}) {
|
|
795
|
+
const e = exec || getExecutor();
|
|
796
|
+
try {
|
|
797
|
+
const pid = parsePidof(e.runFile("adb", [
|
|
798
|
+
"-s",
|
|
799
|
+
serial,
|
|
800
|
+
"shell",
|
|
801
|
+
"pidof",
|
|
802
|
+
packageName
|
|
803
|
+
], ADB_SHELL_OPTIONS));
|
|
804
|
+
if (pid !== null) return pid;
|
|
805
|
+
} catch {}
|
|
806
|
+
try {
|
|
807
|
+
return parsePsPid(e.runFile("adb", [
|
|
808
|
+
"-s",
|
|
809
|
+
serial,
|
|
810
|
+
"shell",
|
|
811
|
+
"ps",
|
|
812
|
+
"-A"
|
|
813
|
+
], ADB_SHELL_OPTIONS), packageName);
|
|
814
|
+
} catch {
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
//#endregion
|
|
819
|
+
export { launchAndroidReleaseApp as _, LAUNCH_ERROR as a, clearOtherUserApps as c, deviceShellArg as d, installAndroidApp as f, launchAndroidApp as g, iosAppProcess as h, INSTALL_ERROR as i, devClientDeepLink as l, installIosApp as m, ANDROID_DISABLE_AUTO_LAUNCH_EXTRA as n, androidAppProcess as o, installConflictKind as p, DEFAULT_METRO_PORT as r, androidDevClientUrl as s, ADB_INSTALL_TIMEOUT_MS as t, devClientUrl as u, launchIosApp as v };
|