stim 1.8.0 → 1.10.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.
Files changed (91) hide show
  1. package/README.md +23 -155
  2. package/dist/{android-DWssqHQ8.mjs → android-787HhP5w.mjs} +168 -39
  3. package/dist/{android-BRgBYzU0.mjs → android-DBkeHwpm.mjs} +250 -200
  4. package/dist/{android-DeYdjjBP.mjs → android-DPov-fv2.mjs} +2 -2
  5. package/dist/{android-cas-SWIjzeuL.mjs → android-cas-CITmIeWY.mjs} +22 -14
  6. package/dist/android-cas-compiler.mjs +2 -2
  7. package/dist/app-install-CrJ6t_bv.mjs +819 -0
  8. package/dist/budget-BP4jZZL4.mjs +620 -0
  9. package/dist/build-progress-BXx-ZBe4.mjs +512 -0
  10. package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Pubfp8wV.mjs} +4 -4
  11. package/dist/cache-manifest.mjs +1 -1
  12. package/dist/cli.mjs +22 -19
  13. package/dist/collector-run.d.mts +7 -1
  14. package/dist/collector-run.mjs +10 -9
  15. package/dist/{command-output-B3ycpsYv.mjs → command-output-D5z2_bcF.mjs} +11 -2
  16. package/dist/{config-B7iyKWkL.mjs → config-BEq0Qw_a.mjs} +134 -121
  17. package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-r9UesA9a.mjs} +2 -2
  18. package/dist/{deps-oCXVNEqQ.mjs → deps-DP4eWqea.mjs} +8 -5
  19. package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-DJHoca49.mjs} +1 -1
  20. package/dist/{device-BUoZyMoR.mjs → dev-client-92U593KT.mjs} +1090 -660
  21. package/dist/{device-RdujjHLB.mjs → device-DhTZe7L4.mjs} +13 -20
  22. package/dist/{build-lock-CgtsB6TV.mjs → device-lease-CbEaIk7b.mjs} +481 -27
  23. package/dist/{device-pool-BxRwdufO.mjs → device-pool-Di8Q-9Ro.mjs} +7 -7
  24. package/dist/{device-remote-ByN8P6CB.mjs → device-remote-Dy1jGj3U.mjs} +645 -119
  25. package/dist/{doctor-BMt156qe.mjs → doctor-B-inAJmt.mjs} +137 -113
  26. package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-xxWKNO3-.mjs} +22 -17
  27. package/dist/exec-DK8KaVHK.mjs +133 -0
  28. package/dist/{gc-v2YcoJvm.mjs → gc-ChPFZMRC.mjs} +836 -813
  29. package/dist/{guide-BTAlD1_y.mjs → guide-DwmaD_Wa.mjs} +953 -209
  30. package/dist/guide-status-CWvti2Lg.mjs +125 -0
  31. package/dist/idle-DpRLp5lj.mjs +157 -0
  32. package/dist/{build-slots-C-oAHRT1.mjs → in-use-CjUluqIX.mjs} +102 -8
  33. package/dist/{ios-BcCiI4TS.mjs → ios-B2Fpqvlb.mjs} +60 -71
  34. package/dist/{ios-BP04pY5f.mjs → ios-Dd-hYJBh.mjs} +241 -234
  35. package/dist/{ios-device-CFXA46ez.mjs → ios-device-BiOsSVzd.mjs} +3 -2
  36. package/dist/{ios-device-CauFJiYx.mjs → ios-device-CtRdEVjx.mjs} +149 -52
  37. package/dist/launch-verify-CQS_Kg-C.mjs +365 -0
  38. package/dist/{logs-CakdBTtm.mjs → logs-MARIy6m9.mjs} +20 -15
  39. package/dist/metro-BWLOJ_Wr.mjs +409 -0
  40. package/dist/{named-ports-DzMOlmaX.mjs → named-ports-DiTfSz6p.mjs} +4 -4
  41. package/dist/{ndjson-BZwI1NBv.mjs → ndjson-CDY9IkVC.mjs} +23 -43
  42. package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
  43. package/dist/{ownership-Cp52QAK1.mjs → ownership-D2xbLVoz.mjs} +2 -3
  44. package/dist/{teardown-BJqEzPo_.mjs → ownership-DZjdx8Gq.mjs} +664 -612
  45. package/dist/{ports-BIbJaKNW.mjs → ports-CLSSFOuz.mjs} +3 -3
  46. package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
  47. package/dist/{project-DDZWf_16.mjs → project-BXZzOABt.mjs} +8 -3
  48. package/dist/{reload-BdypPbjM.mjs → reload-DoETJ3rl.mjs} +11 -13
  49. package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DVbb7kXH.mjs} +2 -2
  50. package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-BvOCeakp.mjs} +8 -7
  51. package/dist/server-expo-DBVTdult.mjs +2 -0
  52. package/dist/{settings-BXXorVM2.mjs → settings-DOugPlPz.mjs} +59 -159
  53. package/dist/settings-EWYNwe72.mjs +378 -0
  54. package/dist/settings.schema.json +1064 -0
  55. package/dist/simslim-Ca3487H6.mjs +436 -0
  56. package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-C5cg0fmk.mjs} +4 -4
  57. package/dist/spawn-claims-CUleC2cM.mjs +49 -0
  58. package/dist/{start-CmxtOKjU.mjs → start-F9gv5dcC.mjs} +159 -114
  59. package/dist/start-VoFsPSes.mjs +2 -0
  60. package/dist/{state-DZobzxLW.mjs → state-WGdIbi_d.mjs} +5 -3
  61. package/dist/{stats-BYcZ6n1h.mjs → stats-BAgJaLA4.mjs} +66 -5
  62. package/dist/{stats-CLg5RuDS.mjs → stats-DBYJ5bxL.mjs} +4 -4
  63. package/dist/status-DLCD-o3s.mjs +764 -0
  64. package/dist/status-Dr4fRkym.mjs +216 -0
  65. package/dist/{guide-status-Craq9z0t.mjs → stim-installations-CINXy_su.mjs} +3 -122
  66. package/dist/{stop-C8JW6kKp.mjs → stop-Dj4pfmyv.mjs} +111 -31
  67. package/dist/{stop-B1kOpYei.mjs → stop-ZvOeTmKb.mjs} +2 -2
  68. package/dist/supervisor-run.d.mts +11 -2
  69. package/dist/supervisor-run.mjs +228 -42
  70. package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-BXA96myD.mjs} +14 -9
  71. package/dist/workspaces-BTUyFfkv.mjs +1003 -0
  72. package/dist/worktree-4aZg2NnI.mjs +2 -0
  73. package/dist/{worktree-CFLA-6MX.mjs → worktree-BMS7GeqB.mjs} +523 -61
  74. package/dist/{worktree-FG_ZKKdb.mjs → worktree-BlizKDpZ.mjs} +200 -54
  75. package/dist/{xcode-B6YaWeg0.mjs → xcode-B2tBkf0m.mjs} +66 -57
  76. package/package.json +6 -6
  77. package/skill/SKILL.md +2 -0
  78. package/dist/app-install-BDbsSgCl.mjs +0 -1175
  79. package/dist/dev-client-BmT-ioFI.mjs +0 -797
  80. package/dist/device-lease-Ffz_M5t0.mjs +0 -359
  81. package/dist/exec-B29XNuhP.mjs +0 -77
  82. package/dist/logs-query-BGL-Pl12.mjs +0 -290
  83. package/dist/metro-C7bcJ0uo.mjs +0 -233
  84. package/dist/ownership-wolE-0Do.mjs +0 -470
  85. package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
  86. package/dist/reclaim-KJ-0uWEF.mjs +0 -361
  87. package/dist/server-expo-YDDG3eP7.mjs +0 -2
  88. package/dist/spawn-entry-nCE6m770.mjs +0 -14
  89. package/dist/status-DdkLki47.mjs +0 -426
  90. package/dist/workspace-state-BT5E3SA2.mjs +0 -146
  91. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
@@ -0,0 +1,819 @@
1
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
2
+ import { T as uninstallIosApp, c as iosSimulatorFailureAdvice, p as listUserApps } from "./ios-B2Fpqvlb.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 };