stim 1.14.0 → 1.16.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 (152) hide show
  1. package/README.md +2 -0
  2. package/dist/activity-BxwdD81S.mjs +609 -0
  3. package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
  4. package/dist/{android-d6rGq1pA.mjs → android-B_NumVyQ.mjs} +169 -81
  5. package/dist/{android-LbPFRWVL.mjs → android-Ift4kVvt.mjs} +517 -413
  6. package/dist/android-cas-8U4DPqeR.mjs +167 -0
  7. package/dist/android-cas-compiler.mjs +2 -2
  8. package/dist/{android-XpvCrV7q.mjs → android-r2rBL8kV.mjs} +2 -2
  9. package/dist/{app-install-BpgSgkJj.mjs → app-install-CKWdbXIX.mjs} +128 -151
  10. package/dist/attempt-Bk2ouQVL.mjs +39 -0
  11. package/dist/budget-DjrURfQn.mjs +2700 -0
  12. package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-DjeSb2Xl.mjs} +192 -653
  13. package/dist/build-slots-rUk77xqh.mjs +132 -0
  14. package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
  15. package/dist/cache-manifest.mjs +1 -1
  16. package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
  17. package/dist/cli-BkYZ_6hy.mjs +51 -0
  18. package/dist/cli.mjs +6 -42
  19. package/dist/client-BTY686R6.mjs +1368 -0
  20. package/dist/collector-run.mjs +9 -195
  21. package/dist/{command-output-DVcpw6eE.mjs → command-output-DnJMdEY-.mjs} +1 -1
  22. package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
  23. package/dist/{created-devices-CllH_8eb.mjs → created-devices-D5owjN4C.mjs} +2 -2
  24. package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-B6cMXZab.mjs} +2 -2
  25. package/dist/{deps-D9IcKqXS.mjs → deps-urbmfXig.mjs} +3 -3
  26. package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
  27. package/dist/dev-client-CroAIjjj.mjs +43 -0
  28. package/dist/{device-CjuuAJjQ.mjs → device-DZQjF7H5.mjs} +8 -7
  29. package/dist/device-capacity-BHwxNBlu.mjs +136 -0
  30. package/dist/device-host-worker.d.mts +1 -0
  31. package/dist/device-host-worker.mjs +964 -0
  32. package/dist/device-ios-BadmeL2V.mjs +381 -0
  33. package/dist/{device-lease-5c3WrVRm.mjs → device-lease-D-hLqx1b.mjs} +43 -36
  34. package/dist/{device-pool-BiyDyPP-.mjs → device-lease-run-CqjShqWW.mjs} +6 -126
  35. package/dist/device-pool-CUaEWMMm.mjs +125 -0
  36. package/dist/{device-remote-D87zEzaq.mjs → device-remote-D5WVWqOq.mjs} +69 -163
  37. package/dist/{doctor-x9pnU9fq.mjs → doctor-Cimf-gGB.mjs} +241 -103
  38. package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-B9L4Ae0x.mjs} +15 -18
  39. package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
  40. package/dist/{gc-ko6mUAYD.mjs → gc-Bqy4bjEu.mjs} +599 -108
  41. package/dist/{android-cas-iZK3DOCr.mjs → gradle-BsO1MdsH.mjs} +8 -294
  42. package/dist/{guide-6R0VNFqI.mjs → guide-B_uyKTxW.mjs} +2511 -239
  43. package/dist/{guide-status-BZrFPIA_.mjs → guide-status-CjEZMAD4.mjs} +3 -3
  44. package/dist/hosted-client-a-WmqE2L.mjs +197 -0
  45. package/dist/hosted-ios-CIWelIqf.mjs +220 -0
  46. package/dist/hosted-logs-D5g041Q3.mjs +187 -0
  47. package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
  48. package/dist/idle-DYVymG92.mjs +1099 -0
  49. package/dist/idle-shutdown-CTsDnAde.mjs +122 -0
  50. package/dist/in-use-DvBw0Kxo.mjs +130 -0
  51. package/dist/{ios-BwDPrnYQ.mjs → ios-BaInn3sW.mjs} +201 -10
  52. package/dist/ios-Cr1h5gnO.mjs +189 -0
  53. package/dist/{ios-P1f5nm2n.mjs → ios-aq3QoGI3.mjs} +1159 -633
  54. package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
  55. package/dist/{ios-device-DGglU3E_.mjs → ios-device-FfCN_C8p.mjs} +12 -7
  56. package/dist/ios-state-DS5UpYTH.mjs +48 -0
  57. package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-2DBRizjo.mjs} +5 -5
  58. package/dist/logs-CHEVK8zD.mjs +371 -0
  59. package/dist/machines-BIBUdhpj.mjs +265 -0
  60. package/dist/macos-CSYEAFmF.mjs +473 -0
  61. package/dist/macos-run.d.mts +5 -0
  62. package/dist/macos-run.mjs +2 -0
  63. package/dist/maintenance-run.d.mts +4 -0
  64. package/dist/maintenance-run.mjs +254 -0
  65. package/dist/{metro-CcEo8Fl8.mjs → metro-BeK1ZPz1.mjs} +20 -120
  66. package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
  67. package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Bk_YXEmE.mjs} +6 -59
  68. package/dist/native-run-NaBrRFtQ.mjs +147 -0
  69. package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-DBzRnEKS.mjs} +9 -5
  70. package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
  71. package/dist/offload-worker.d.mts +122 -0
  72. package/dist/offload-worker.mjs +606 -0
  73. package/dist/{ownership-DyYDF5ww.mjs → ownership-DiA16AsN.mjs} +3 -2
  74. package/dist/ownership-hCkPzGmr.mjs +571 -0
  75. package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
  76. package/dist/{ports-BguS83ex.mjs → ports-R2k4Knz3.mjs} +3 -3
  77. package/dist/prebuild-D2fsca_Z.mjs +213 -0
  78. package/dist/preview-DhVbvPAZ.mjs +318 -0
  79. package/dist/process-identity-BmRm1qV0.mjs +12 -0
  80. package/dist/{project-CWlvEaeN.mjs → project-Du91Ilvc.mjs} +74 -19
  81. package/dist/pull-request-pxnDCQzn.mjs +322 -0
  82. package/dist/pull-requests.d.mts +120 -0
  83. package/dist/pull-requests.mjs +2 -0
  84. package/dist/recordings-BIXOzVnr.mjs +37 -0
  85. package/dist/{reload-BiniwsSZ.mjs → reload-rM9jV1fz.mjs} +109 -24
  86. package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-C8-V13XN.mjs} +5 -5
  87. package/dist/run-f4vI6EnK.mjs +119 -0
  88. package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-CpPGmnmO.mjs} +33 -15
  89. package/dist/server-expo-CVqO2c35.mjs +2 -0
  90. package/dist/{server-expo-BVuwQ65v.mjs → server-expo-MC0rHlOC.mjs} +8 -12
  91. package/dist/settings-BBpMcDbD.mjs +25 -0
  92. package/dist/{settings-B-fsC-Om.mjs → settings-ZkDzkzvB.mjs} +22 -10
  93. package/dist/settings-oTSkkNrM.mjs +1439 -0
  94. package/dist/settings.schema.json +845 -97
  95. package/dist/{simslim-DrDN4WuV.mjs → simslim-B8-7AoTI.mjs} +8 -132
  96. package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-B8VrHxn1.mjs} +11 -8
  97. package/dist/{spawn-claims-Drw_o-Fb.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
  98. package/dist/spawn-entry-jw19m8Nl.mjs +17 -0
  99. package/dist/stage-CzaiwFsI.mjs +143 -0
  100. package/dist/start-CX8Ig-U2.mjs +2 -0
  101. package/dist/{start-s4RXp1wy.mjs → start-ClDfwXa4.mjs} +116 -65
  102. package/dist/state-3DwSQIBK.mjs +41 -0
  103. package/dist/state-B4PQFXMW.d.mts +12 -0
  104. package/dist/{state-Dk9RVXI7.mjs → state-C1jB7Q8q.mjs} +70 -8
  105. package/dist/{state-DHqvsbqC.mjs → state-DcW6vOd_.mjs} +7 -4
  106. package/dist/stats-CEZ_i5je.mjs +77 -0
  107. package/dist/{status-BgAuxESg.mjs → status-BuvBFeII.mjs} +118 -20
  108. package/dist/{status-BsPAnfOI.mjs → status-Cs7VLg9D.mjs} +542 -283
  109. package/dist/status-DlKvVJBE.mjs +49 -0
  110. package/dist/stdout-reader-C8yjre1x.mjs +23 -0
  111. package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
  112. package/dist/stim-footprint.swift +32 -0
  113. package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
  114. package/dist/stop-B0Yxtmiy.mjs +48 -0
  115. package/dist/{stop-BzreOWJp.mjs → stop-Bffozsac.mjs} +83 -16
  116. package/dist/{stop-BWIF_6BK.mjs → stop-Dt_dM16A.mjs} +2 -2
  117. package/dist/stop-cause-CidxIqup.mjs +97 -0
  118. package/dist/supervisor-run.d.mts +43 -11
  119. package/dist/supervisor-run.mjs +363 -24
  120. package/dist/support-CAP8qDUF.mjs +316 -0
  121. package/dist/support-D4aYJniO.mjs +201 -0
  122. package/dist/teardown-CL6vDs2Y.mjs +727 -0
  123. package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DkUflduf.mjs} +364 -14
  124. package/dist/trigger-Bemu8ytw.mjs +65 -0
  125. package/dist/trigger-CXts6qt1.mjs +2 -0
  126. package/dist/warm-progress-CpGv3Wb6.mjs +732 -0
  127. package/dist/{web-BsQs1WlO.mjs → web-DWUaMp8P.mjs} +37 -21
  128. package/dist/web-run.mjs +310 -13
  129. package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
  130. package/dist/workspace-state-B8-aJ1_5.mjs +371 -0
  131. package/dist/workspaces-BV4pdHFz.mjs +306 -0
  132. package/dist/{worktree-UrQWXSeb.mjs → worktree-K-xj9Db0.mjs} +625 -256
  133. package/dist/worktree-jnG8Iph5.mjs +2 -0
  134. package/package.json +10 -5
  135. package/shim/bundle-response.cjs +28 -12
  136. package/dist/activity-CYVDcrt3.mjs +0 -356
  137. package/dist/budget-CGDkhvv_.mjs +0 -1745
  138. package/dist/build-progress-DxKpwnaS.mjs +0 -247
  139. package/dist/idle-DXtx3RbX.mjs +0 -191
  140. package/dist/in-use-DlqBDKqY.mjs +0 -249
  141. package/dist/logs-Civ4gbBH.mjs +0 -486
  142. package/dist/merge-state-ytFQCeh4.mjs +0 -262
  143. package/dist/server-expo-DIHDhvGg.mjs +0 -2
  144. package/dist/settings-D61iNOsB.mjs +0 -629
  145. package/dist/start-Cetfvy8l.mjs +0 -2
  146. package/dist/stats-CFpFGlJV.mjs +0 -62
  147. package/dist/stats-CX1Z8Tca.mjs +0 -368
  148. package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
  149. package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
  150. package/dist/worktree-BVrHsYZG.mjs +0 -752
  151. package/dist/worktree-CHWWvm5v.mjs +0 -2
  152. package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
@@ -1,31 +1,16 @@
1
- import { A as ownership_claim_exports, B as deviceSlotPlatforms, E as withConfigLock$1, L as assignSlotDevice, O as process_identity_exports, S as saveConfig, U as projectDeviceSlots, W as removeSlotDevice, _ as loadConfig$1, c as ensureConfig, d as getConfigDir, f as getConfigPath$1 } from "./config-lvc7d3C2.mjs";
2
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
- import { a as pidExists, l as signalProcessTree } from "./metro-CcEo8Fl8.mjs";
4
- import { n as settingDefault, r as stimDesktopInstalled, t as STIM_DESKTOP_OPEN_OPTIONS } from "./stim-desktop-CXt8-Z3J.mjs";
5
- import { n as readCreatedDevices$1, r as recordCreatedDevice, t as forgetCreatedDevice } from "./created-devices-CllH_8eb.mjs";
6
- import { c as androidDataPartitionSizeBytes } from "./settings-D61iNOsB.mjs";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { C as saveConfig, D as withConfigLock$1, H as projectDeviceSlots, I as assignSlotDevice, U as removeSlotDevice, _ as loadConfig$1, c as ensureConfig, d as getConfigDir, f as getConfigPath$1, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
3
+ import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
+ import { c as androidDataPartitionSizeBytes } from "./settings-oTSkkNrM.mjs";
5
+ import { o as pidExists, u as signalProcessTree } from "./metro-BeK1ZPz1.mjs";
6
+ import { n as readCreatedDevices$1, r as recordCreatedDevice, t as forgetCreatedDevice } from "./created-devices-D5owjN4C.mjs";
7
+ import { n as settingDefault, r as stimDesktopInstalled, t as STIM_DESKTOP_OPEN_OPTIONS } from "./stim-desktop-Cnz8CPnl.mjs";
8
+ import { join } from "node:path";
7
9
  import { existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
8
- import { dirname, isAbsolute, join, resolve } from "path";
9
- import { ANDROID_EMULATOR_APPS, loadConfig, settingDefinition } from "@stim-cli/core/state";
10
- import { join as join$1 } from "node:path";
11
10
  import { homedir, tmpdir } from "os";
11
+ import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, resolve as resolve$1 } from "path";
12
+ import { ANDROID_EMULATOR_APPS, loadConfig, settingDefinition } from "@stim-cli/core/state";
12
13
  import { isDeepStrictEqual } from "node:util";
13
- //#region src/devices/android-emulator-viewer.ts
14
- function configuredAndroidEmulatorApp(platform = process.platform) {
15
- const value = loadConfig$1()?.androidEmulatorApp ?? settingDefault(settingDefinition("androidEmulatorApp"), () => stimDesktopInstalled(platform)).value;
16
- if (ANDROID_EMULATOR_APPS.includes(value)) return value;
17
- const error = /* @__PURE__ */ new Error("Invalid androidEmulatorApp in machine config. Use \"emulator\" or \"stim-desktop\".");
18
- Object.assign(error, { code: "STIM_BAD_ARG" });
19
- throw error;
20
- }
21
- function openEmulatorInStimDesktop(serial) {
22
- getExecutor().runFileQuiet("open", [
23
- "-g",
24
- "-a",
25
- "Stim",
26
- `stim-desktop://open?serial=${serial}`
27
- ], STIM_DESKTOP_OPEN_OPTIONS);
28
- }
29
14
  const POOL_SETTING_REMEDY = "Run `stim guide settings` for the device pool bounds and where it can be set.";
30
15
  const MAX_SETTING = {
31
16
  ios: {
@@ -70,7 +55,7 @@ function parkedMaxSetting(platform, { config, env = process.env } = {}) {
70
55
  function isParkedRecord(value, platform) {
71
56
  if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
72
57
  const record = value;
73
- return typeof record.udid === "string" && typeof record.name === "string" && record.name.startsWith("stim-") && typeof record.parkedAt === "string" && (platform === "android" ? record.udid === record.name && /^stim-[A-Za-z0-9._-]+$/.test(record.name) && typeof record.systemImage === "string" && typeof record.configuration === "string" : typeof record.deviceTypeIdentifier === "string" && typeof record.runtimeIdentifier === "string" && typeof record.simslimManaged === "boolean" && (record.bundleId === void 0 || typeof record.bundleId === "string") && (record.cacheKey === void 0 || typeof record.cacheKey === "string"));
58
+ return typeof record.udid === "string" && typeof record.name === "string" && record.name.startsWith("stim-") && typeof record.parkedAt === "string" && (platform === "android" ? record.udid === record.name && /^stim-[A-Za-z0-9._-]+$/.test(record.name) && typeof record.systemImage === "string" && typeof record.configuration === "string" && (record.packageName === void 0 || typeof record.packageName === "string") && (record.cacheKey === void 0 || typeof record.cacheKey === "string") : typeof record.deviceTypeIdentifier === "string" && typeof record.runtimeIdentifier === "string" && typeof record.simslimManaged === "boolean" && (record.bundleId === void 0 || typeof record.bundleId === "string") && (record.cacheKey === void 0 || typeof record.cacheKey === "string") && (record.schemeApprovals === void 0 || Array.isArray(record.schemeApprovals) && record.schemeApprovals.every((v) => typeof v === "string")));
74
59
  }
75
60
  function poolBlock(config) {
76
61
  const raw = config?.parked;
@@ -124,7 +109,13 @@ function parkSim({ platform, projectPath, slot = "default", record, max }) {
124
109
  const current = deviceSlotPlatforms(cfg.projects[projectPath], slot)?.[platform];
125
110
  const currentId = platform === "ios" ? current?.deviceUdid : current?.avdName;
126
111
  if (!current?.owned || currentId !== record.udid) throw new Error(`The ${platform === "ios" ? "simulator" : "emulator"} assignment changed before parking.`);
127
- const { keep, evicted } = evictOverflow([...readParked(platform, { config: cfg }).filter((r) => r.udid !== record.udid), record], max);
112
+ const currentApprovals = platform === "ios" ? current.schemeApprovals : void 0;
113
+ const schemeApprovals = Array.isArray(currentApprovals) && currentApprovals.every((v) => typeof v === "string") ? currentApprovals : void 0;
114
+ const carried = schemeApprovals && schemeApprovals.length > 0 ? {
115
+ ...record,
116
+ schemeApprovals
117
+ } : record;
118
+ const { keep, evicted } = evictOverflow([...readParked(platform, { config: cfg }).filter((r) => r.udid !== record.udid), carried], max);
128
119
  writeParked(cfg, platform, [...keep, ...evicted]);
129
120
  const project = cfg.projects[projectPath];
130
121
  if (project) removeSlotDevice(project, platform, slot);
@@ -145,10 +136,15 @@ function adoptParked({ platform, projectPath, slot = "default", udid, device })
145
136
  writeParked(cfg, platform, records.filter((r) => r.udid !== udid));
146
137
  const project = cfg.projects[projectPath];
147
138
  if (!project) throw new Error(`Project not registered: ${projectPath}`);
148
- assignSlotDevice(project, platform, device, slot);
149
- saveConfig(cfg);
150
- const adopted = { ...taken };
139
+ const adopted = taken.deletionClaim === void 0 ? { ...taken } : erasedRecord(taken);
151
140
  delete adopted.deletionClaim;
141
+ const takenApprovals = platform === "ios" ? adopted.schemeApprovals : void 0;
142
+ const assigned = takenApprovals && takenApprovals.length > 0 ? {
143
+ ...device,
144
+ schemeApprovals: takenApprovals
145
+ } : device;
146
+ assignSlotDevice(project, platform, assigned, slot);
147
+ saveConfig(cfg);
152
148
  return adopted;
153
149
  });
154
150
  } finally {
@@ -157,7 +153,7 @@ function adoptParked({ platform, projectPath, slot = "default", udid, device })
157
153
  }
158
154
  function claimParkedOperation(platform, udid) {
159
155
  const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
160
- root: join$1(getConfigDir(), "pool-locks", platform, `${encodeURIComponent(udid.toLowerCase())}.lock`),
156
+ root: join(getConfigDir(), "pool-locks", platform, `${encodeURIComponent(udid.toLowerCase())}.lock`),
161
157
  mode: "exclusive",
162
158
  label: `parked ${platform} device ${udid}`
163
159
  });
@@ -165,6 +161,20 @@ function claimParkedOperation(platform, udid) {
165
161
  return attempt.acquired ?? null;
166
162
  }
167
163
  function removeParkedAfter(platform, udid, beforeRemove) {
164
+ return settleParkedAfter(platform, udid, beforeRemove, null);
165
+ }
166
+ function eraseParkedAfter(platform, udid, erase) {
167
+ return settleParkedAfter(platform, udid, erase, erasedRecord);
168
+ }
169
+ function erasedRecord(record) {
170
+ const erased = { ...record };
171
+ delete erased.bundleId;
172
+ delete erased.packageName;
173
+ delete erased.cacheKey;
174
+ delete erased.schemeApprovals;
175
+ return erased;
176
+ }
177
+ function settleParkedAfter(platform, udid, action, keep) {
168
178
  const claim = claimParkedOperation(platform, udid);
169
179
  if (!claim) return null;
170
180
  try {
@@ -193,11 +203,14 @@ function removeParkedAfter(platform, udid, beforeRemove) {
193
203
  });
194
204
  if (!claimed) return null;
195
205
  let completed = false;
196
- let removed = null;
206
+ let started = false;
207
+ let settled = null;
197
208
  try {
198
209
  (0, ownership_claim_exports.markClaimChildPending)(claim);
199
210
  try {
200
- beforeRemove(claimed.record);
211
+ action(claimed.record, () => {
212
+ started = true;
213
+ });
201
214
  } finally {
202
215
  (0, ownership_claim_exports.clearClaimChild)(claim);
203
216
  }
@@ -209,12 +222,13 @@ function removeParkedAfter(platform, udid, beforeRemove) {
209
222
  const records = readParked(platform, { config: cfg });
210
223
  const current = records.find((candidate) => candidate.udid === udid);
211
224
  if (!isDeepStrictEqual(current, claimed.marked)) return;
212
- writeParked(cfg, platform, completed ? records.filter((candidate) => candidate.udid !== udid) : records.map((candidate) => candidate.udid === udid ? claimed.record : candidate));
225
+ const after = (completed || started) && keep ? keep(claimed.record) : claimed.record;
226
+ writeParked(cfg, platform, completed && !keep ? records.filter((candidate) => candidate.udid !== udid) : records.map((candidate) => candidate.udid === udid ? after : candidate));
213
227
  saveConfig(cfg);
214
- if (completed) removed = claimed.record;
228
+ if (completed) settled = claimed.record;
215
229
  });
216
230
  }
217
- return removed;
231
+ return settled;
218
232
  } finally {
219
233
  (0, ownership_claim_exports.releaseClaim)(claim);
220
234
  }
@@ -252,6 +266,23 @@ function isStimOwnedAvd(name) {
252
266
  return name.startsWith("stim-") && isStimOwnedDevice("android", name);
253
267
  }
254
268
  //#endregion
269
+ //#region src/devices/android-emulator-viewer.ts
270
+ function configuredAndroidEmulatorApp(platform = process.platform) {
271
+ const value = loadConfig$1()?.androidEmulatorApp ?? settingDefault(settingDefinition("androidEmulatorApp"), () => stimDesktopInstalled(platform)).value;
272
+ if (ANDROID_EMULATOR_APPS.includes(value)) return value;
273
+ const error = /* @__PURE__ */ new Error("Invalid androidEmulatorApp in machine config. Use \"emulator\" or \"stim-desktop\".");
274
+ Object.assign(error, { code: "STIM_BAD_ARG" });
275
+ throw error;
276
+ }
277
+ function openEmulatorInStimDesktop(serial) {
278
+ getExecutor().runFileQuiet("open", [
279
+ "-g",
280
+ "-a",
281
+ "Stim",
282
+ `stim-desktop://open?serial=${serial}`
283
+ ], STIM_DESKTOP_OPEN_OPTIONS);
284
+ }
285
+ //#endregion
255
286
  //#region src/devices/android.ts
256
287
  const ANDROID_ABIS = /* @__PURE__ */ new Set([
257
288
  "armeabi-v7a",
@@ -263,8 +294,8 @@ const DEFAULT_AVD_DEVICE_PROFILE = String(settingDefinition("android.deviceProfi
263
294
  function androidHome(env = process.env) {
264
295
  const configured = env.ANDROID_HOME || env.ANDROID_SDK_ROOT;
265
296
  if (configured) return configured;
266
- if (process.platform === "win32") return join(env.LOCALAPPDATA || join(homedir(), "AppData", "Local"), "Android", "Sdk");
267
- return join(homedir(), "Library", "Android", "sdk");
297
+ if (process.platform === "win32") return join$1(env.LOCALAPPDATA || join$1(homedir(), "AppData", "Local"), "Android", "Sdk");
298
+ return join$1(homedir(), "Library", "Android", "sdk");
268
299
  }
269
300
  const SDK_TOOL_LOCATIONS = {
270
301
  emulator: ["emulator", "emulator"],
@@ -292,7 +323,7 @@ function androidToolPath(tool, platform = process.platform) {
292
323
  const location = SDK_TOOL_LOCATIONS[tool];
293
324
  const bare = location.at(-1);
294
325
  const name = platform === "win32" ? `${bare}${WINDOWS_SDK_TOOL_EXTENSIONS[tool]}` : bare;
295
- const abs = join(androidHome(), ...location.slice(0, -1), name);
326
+ const abs = join$1(androidHome(), ...location.slice(0, -1), name);
296
327
  return existsSync(abs) ? abs : bare;
297
328
  }
298
329
  const WINDOWS_BUILD_TOOL_EXTENSIONS = { apksigner: ".bat" };
@@ -316,7 +347,7 @@ function buildToolsMajor(version) {
316
347
  return Number.isFinite(major) ? major : 0;
317
348
  }
318
349
  function findBuildTool(tools, { home = androidHome(), readDir = readdirSync, exists = existsSync, platform = process.platform } = {}) {
319
- const root = join(home, "build-tools");
350
+ const root = join$1(home, "build-tools");
320
351
  let versions = [];
321
352
  try {
322
353
  versions = readDir(root);
@@ -327,7 +358,7 @@ function findBuildTool(tools, { home = androidHome(), readDir = readdirSync, exi
327
358
  const version = newestBuildTools(versions);
328
359
  if (!version) return null;
329
360
  for (const tool of tools) {
330
- const path = join(root, version, androidBuildToolName(tool, platform));
361
+ const path = join$1(root, version, androidBuildToolName(tool, platform));
331
362
  if (exists(path)) return {
332
363
  path,
333
364
  tool,
@@ -344,7 +375,7 @@ function androidTool(tool) {
344
375
  return resolved === tool ? tool : `"${resolved}"`;
345
376
  }
346
377
  function listInstalledSystemImages(onUnreadable) {
347
- const root = join(androidHome(), "system-images");
378
+ const root = join$1(androidHome(), "system-images");
348
379
  const images = [];
349
380
  const list = (dir) => {
350
381
  try {
@@ -358,8 +389,8 @@ function listInstalledSystemImages(onUnreadable) {
358
389
  for (const apiDir of list(root)) {
359
390
  const m = apiDir.match(/^android-(\d+)$/);
360
391
  if (!m) continue;
361
- const apiPath = join(root, apiDir);
362
- for (const tag of list(apiPath)) for (const arch of list(join(apiPath, tag))) images.push({
392
+ const apiPath = join$1(root, apiDir);
393
+ for (const tag of list(apiPath)) for (const arch of list(join$1(apiPath, tag))) images.push({
363
394
  api: Number(m[1]),
364
395
  tag,
365
396
  arch,
@@ -387,7 +418,7 @@ function profileNeedsFoldFeature(profile) {
387
418
  }
388
419
  function systemImageSupportsFold(pkg, home = androidHome()) {
389
420
  try {
390
- const ini = readFileSync(join(home, ...pkg.split(";"), "advancedFeatures.ini"), "utf8");
421
+ const ini = readFileSync(join$1(home, ...pkg.split(";"), "advancedFeatures.ini"), "utf8");
391
422
  return /^\s*SupportPixelFold\s*=\s*on\s*$/im.test(ini);
392
423
  } catch {
393
424
  return false;
@@ -412,13 +443,23 @@ async function createOwnedAvd(label, { systemImage, deviceProfile = DEFAULT_AVD_
412
443
  "--device",
413
444
  deviceProfile
414
445
  ];
446
+ let env = process.env;
447
+ if (!env.ANDROID_AVD_HOME) {
448
+ const root = avdLookupRoots(env, homedir())[0];
449
+ mkdirSync(root, { recursive: true });
450
+ env = {
451
+ ...env,
452
+ ANDROID_AVD_HOME: root
453
+ };
454
+ }
415
455
  const child = spawn(tool, args, {
416
456
  detached: true,
417
457
  stdio: [
418
458
  "pipe",
419
459
  "pipe",
420
460
  "pipe"
421
- ]
461
+ ],
462
+ env
422
463
  });
423
464
  let stderr = "";
424
465
  child.stdout?.on("data", () => {});
@@ -463,7 +504,7 @@ function ownedAvdSystemImage(avdName, { avdDirectory = ownedAvdDirectory, readFi
463
504
  const directory = avdDirectory(avdName);
464
505
  if (!directory) return null;
465
506
  try {
466
- return parseAvdSystemImage(readFile(join(directory, "config.ini")));
507
+ return parseAvdSystemImage(readFile(join$1(directory, "config.ini")));
467
508
  } catch {
468
509
  return null;
469
510
  }
@@ -486,7 +527,7 @@ function sanitizeAvdLabel(label) {
486
527
  function avdConfigIni(avdName, directory = ownedAvdDirectory(avdName)) {
487
528
  if (!directory) return null;
488
529
  try {
489
- return readFileSync(join(directory, "config.ini"), "utf8");
530
+ return readFileSync(join$1(directory, "config.ini"), "utf8");
490
531
  } catch {
491
532
  return null;
492
533
  }
@@ -498,9 +539,11 @@ function ownedAvdName(label) {
498
539
  const AVDMANAGER_DELETE_TIMEOUT_MS = 12e4;
499
540
  function deleteAvd(avdName) {
500
541
  if (!isStimOwnedAvd(avdName)) throw new Error(`Refusing to delete AVD "${avdName}": not a Stim-owned AVD; Stim has no record of creating it.`);
542
+ const root = process.env.ANDROID_AVD_HOME ? void 0 : avdStorageRoots().find((candidate) => avdPathExists(join$1(candidate, `${avdName}.ini`)));
501
543
  getExecutor().run(`${androidTool("avdmanager")} delete avd -n "${avdName}"`, {
502
544
  timeoutMs: AVDMANAGER_DELETE_TIMEOUT_MS,
503
- killSignal: "SIGKILL"
545
+ killSignal: "SIGKILL",
546
+ ...root ? { env: { ANDROID_AVD_HOME: root } } : {}
504
547
  });
505
548
  forgetCreatedDevice("android", avdName);
506
549
  }
@@ -703,17 +746,17 @@ function parseAvdRootIni(contents) {
703
746
  }
704
747
  function avdIniPaths(root, ini) {
705
748
  const parsed = parseAvdRootIni(ini);
706
- return [parsed.relativePath && !isAbsolute(parsed.relativePath) ? resolve(dirname(root), parsed.relativePath) : null, parsed.path && isAbsolute(parsed.path) ? parsed.path : null].filter((candidate) => candidate !== null);
749
+ return [parsed.relativePath && !isAbsolute$1(parsed.relativePath) ? resolve$1(dirname$1(root), parsed.relativePath) : null, parsed.path && isAbsolute$1(parsed.path) ? parsed.path : null].filter((candidate) => candidate !== null);
707
750
  }
708
751
  function avdStorageRoots() {
709
752
  const env = process.env;
710
753
  return [...new Set([
711
754
  env.ANDROID_AVD_HOME,
712
- env.ANDROID_EMULATOR_HOME ? join(env.ANDROID_EMULATOR_HOME, "avd") : null,
713
- env.ANDROID_USER_HOME ? join(env.ANDROID_USER_HOME, "avd") : null,
714
- env.ANDROID_SDK_HOME ? join(env.ANDROID_SDK_HOME, ".android", "avd") : null,
715
- env.ANDROID_SDK_HOME ? join(env.ANDROID_SDK_HOME, "avd") : null,
716
- join(homedir(), ".android", "avd")
755
+ env.ANDROID_EMULATOR_HOME ? join$1(env.ANDROID_EMULATOR_HOME, "avd") : null,
756
+ env.ANDROID_USER_HOME ? join$1(env.ANDROID_USER_HOME, "avd") : null,
757
+ env.ANDROID_SDK_HOME ? join$1(env.ANDROID_SDK_HOME, ".android", "avd") : null,
758
+ env.ANDROID_SDK_HOME ? join$1(env.ANDROID_SDK_HOME, "avd") : null,
759
+ join$1(homedir(), ".android", "avd")
717
760
  ].filter((value) => Boolean(value)))];
718
761
  }
719
762
  function avdPathExists(path) {
@@ -732,7 +775,7 @@ function avdPathExists(path) {
732
775
  */
733
776
  function avdNameAbsent(avdName, roots = avdStorageRoots()) {
734
777
  if (!roots[0] || !isDirectoryPath(roots[0])) return false;
735
- return roots.every((root) => !avdPathExists(join(root, `${avdName}.ini`)) && !avdPathExists(join(root, `${avdName}.avd`)));
778
+ return roots.every((root) => !avdPathExists(join$1(root, `${avdName}.ini`)) && !avdPathExists(join$1(root, `${avdName}.avd`)));
736
779
  }
737
780
  function isDirectoryPath(path) {
738
781
  try {
@@ -751,7 +794,7 @@ function listOrphanedAvdDirectories(avdName) {
751
794
  }));
752
795
  const registered = /* @__PURE__ */ new Set();
753
796
  for (const { root, canonicalRoot, names } of stores) for (const name of names.filter((entry) => entry.endsWith(".ini"))) {
754
- const path = join(root, name);
797
+ const path = join$1(root, name);
755
798
  const ini = readFileSync(path, "utf8");
756
799
  const targets = /* @__PURE__ */ new Set([...avdIniPaths(root, ini), ...avdIniPaths(canonicalRoot, ini)]);
757
800
  if (!targets.size) throw new Error(`Cannot verify AVD registration at ${path}; its data was kept.`);
@@ -768,8 +811,8 @@ function listOrphanedAvdDirectories(avdName) {
768
811
  for (const entry of names) {
769
812
  if (!/^stim-[A-Za-z0-9._-]+\.avd$/.test(entry)) continue;
770
813
  const name = entry.slice(0, -4);
771
- if (roots.some((candidate) => avdPathExists(join(candidate, `${name}.ini`)))) continue;
772
- const directory = join(canonicalRoot, entry);
814
+ if (roots.some((candidate) => avdPathExists(join$1(candidate, `${name}.ini`)))) continue;
815
+ const directory = join$1(canonicalRoot, entry);
773
816
  if (registered.has(directory) || !avdPathExists(directory)) continue;
774
817
  const stat = lstatSync(directory);
775
818
  if (!stat.isDirectory() || stat.isSymbolicLink() || realpathSync(directory) !== directory) throw new Error(`Cannot verify AVD data at ${directory}; it was kept.`);
@@ -783,17 +826,20 @@ function listOrphanedAvdDirectories(avdName) {
783
826
  }
784
827
  return orphaned;
785
828
  }
786
- function ownedAvdDirectory(avdName, { env = process.env, home = homedir(), readFile = (path) => readFileSync(path, "utf8"), realpath = realpathSync, isDirectory = (path) => statSync(path).isDirectory() } = {}) {
787
- if (!/^stim-[A-Za-z0-9._-]+$/.test(avdName)) return null;
829
+ function avdLookupRoots(env, home) {
788
830
  const roots = [
789
831
  env.ANDROID_AVD_HOME,
790
- env.ANDROID_SDK_HOME ? join(env.ANDROID_SDK_HOME, "avd") : null,
791
- join(home, ".android", "avd")
832
+ env.ANDROID_SDK_HOME ? join$1(env.ANDROID_SDK_HOME, "avd") : null,
833
+ join$1(home, ".android", "avd")
792
834
  ];
793
- for (const root of new Set(roots.filter((value) => Boolean(value)))) {
835
+ return [...new Set(roots.filter((value) => Boolean(value)))];
836
+ }
837
+ function ownedAvdDirectory(avdName, { env = process.env, home = homedir(), readFile = (path) => readFileSync(path, "utf8"), realpath = realpathSync, isDirectory = (path) => statSync(path).isDirectory() } = {}) {
838
+ if (!/^stim-[A-Za-z0-9._-]+$/.test(avdName)) return null;
839
+ for (const root of avdLookupRoots(env, home)) {
794
840
  let ini;
795
841
  try {
796
- ini = readFile(join(root, `${avdName}.ini`));
842
+ ini = readFile(join$1(root, `${avdName}.ini`));
797
843
  } catch (error) {
798
844
  const code = error.code;
799
845
  if (code === "ENOENT" || code === "ENOTDIR") continue;
@@ -817,7 +863,7 @@ const AVD_USER_DATA = [
817
863
  "snapshots"
818
864
  ];
819
865
  function wipeAvdUserData(directory) {
820
- for (const name of AVD_USER_DATA) rmSync(join(directory, name), {
866
+ for (const name of AVD_USER_DATA) rmSync(join$1(directory, name), {
821
867
  recursive: true,
822
868
  force: true
823
869
  });
@@ -850,14 +896,14 @@ function configureNewOwnedAvd(avdName, { dataPartitionSizeGb, avdConfig = {} },
850
896
  const sizeBytes = androidDataPartitionSizeBytes(dataPartitionSizeGb);
851
897
  const directory = avdDirectory(avdName);
852
898
  if (!directory) throw new Error(`Could not resolve the content directory for newly created AVD ${avdName}.`);
853
- const configPath = join(directory, "config.ini");
899
+ const configPath = join$1(directory, "config.ini");
854
900
  const original = readFile(configPath);
855
901
  const expected = {
856
902
  ...avdConfig,
857
903
  "disk.dataPartition.size": String(sizeBytes)
858
904
  };
859
905
  const updated = withAvdConfigOverrides(original, expected);
860
- const tempPath = join(directory, `.config.ini.stim-${process.pid}-${++avdConfigWriteSequence}.tmp`);
906
+ const tempPath = join$1(directory, `.config.ini.stim-${process.pid}-${++avdConfigWriteSequence}.tmp`);
861
907
  try {
862
908
  writeFile(tempPath, updated);
863
909
  rename(tempPath, configPath);
@@ -886,7 +932,7 @@ function avdPoolConfiguration(dataPartitionSizeGb, avdConfig, deviceProfile = DE
886
932
  function ownedAvdMatchesConfiguration(avdName, configuration) {
887
933
  const directory = ownedAvdDirectory(avdName);
888
934
  if (!directory) return false;
889
- const contents = readFileSync(join(directory, "config.ini"), "utf8");
935
+ const contents = readFileSync(join$1(directory, "config.ini"), "utf8");
890
936
  const expected = JSON.parse(configuration);
891
937
  return Array.isArray(expected) && expected.length > 0 && expected.every((entry) => Array.isArray(entry) && entry.length === 2 && entry.every((value) => typeof value === "string") && contents.split(/\r?\n/).some((line) => line.trim() === `${entry[0]}=${entry[1]}`));
892
938
  }
@@ -953,6 +999,35 @@ async function resetAdoptedAvd(avdName, serial, keepPackage) {
953
999
  }
954
1000
  }
955
1001
  }
1002
+ function parseDfAvailableBytes(text) {
1003
+ const lines = String(text ?? "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1004
+ if (lines.length !== 2 || !/^Filesystem\s/.test(lines[0])) return null;
1005
+ const available = lines[1].split(/\s+/)[3];
1006
+ return available && /^\d+$/.test(available) ? Number(available) * 1024 : null;
1007
+ }
1008
+ function androidDataFreeBytes(serial) {
1009
+ return parseDfAvailableBytes(getExecutor().runFileQuiet("adb", [
1010
+ "-s",
1011
+ serial,
1012
+ "shell",
1013
+ "df",
1014
+ "-k",
1015
+ "/data"
1016
+ ], { timeoutMs: 3e4 }));
1017
+ }
1018
+ function trimAndroidCaches(serial, desiredFreeBytes) {
1019
+ getExecutor().runFile("adb", [
1020
+ "-s",
1021
+ serial,
1022
+ "shell",
1023
+ "pm",
1024
+ "trim-caches",
1025
+ String(desiredFreeBytes)
1026
+ ], {
1027
+ timeoutMs: 12e4,
1028
+ killSignal: "SIGKILL"
1029
+ });
1030
+ }
956
1031
  function parseEmulatorVersion(text) {
957
1032
  const match = /Android emulator version (\d+)\./.exec(String(text ?? ""));
958
1033
  return match ? Number(match[1]) : null;
@@ -967,9 +1042,9 @@ const CRASH_REPORT_MODE_EMULATOR_VERSION = 37;
967
1042
  * under GetTempPath, which reads TMP before TEMP.
968
1043
  */
969
1044
  function emulatorCrashDatabases(env = process.env) {
970
- const root = join(env.TMP || env.TEMP || tmpdir(), "AndroidEmulator");
1045
+ const root = join$1(env.TMP || env.TEMP || tmpdir(), "AndroidEmulator");
971
1046
  try {
972
- return readdirSync(root).filter((name) => /^emu-crash-.*\.db$/.test(name)).map((name) => join(root, name));
1047
+ return readdirSync(root).filter((name) => /^emu-crash-.*\.db$/.test(name)).map((name) => join$1(root, name));
973
1048
  } catch {
974
1049
  return [];
975
1050
  }
@@ -995,7 +1070,7 @@ function suppressEmulatorCrashConsent({ platform = process.platform, version = e
995
1070
  } catch {}
996
1071
  return [];
997
1072
  }
998
- function bootAndroidEmulator(avdName, consolePort, { logFile, platform = process.platform } = {}) {
1073
+ function bootAndroidEmulator(avdName, consolePort, { logFile, platform = process.platform, openViewer = true } = {}) {
999
1074
  const exec = getExecutor();
1000
1075
  const app = configuredAndroidEmulatorApp(platform);
1001
1076
  const child = exec.spawn(androidToolPath("emulator"), [
@@ -1006,7 +1081,7 @@ function bootAndroidEmulator(avdName, consolePort, { logFile, platform = process
1006
1081
  "-grpc",
1007
1082
  String(consolePort + 3e3),
1008
1083
  "-grpc-use-token",
1009
- ...headlessEmulatorArgs(process.env, platform, app),
1084
+ ...headlessEmulatorArgs(process.env, platform, openViewer ? app : "stim-desktop"),
1010
1085
  ...suppressEmulatorCrashConsent({ platform })
1011
1086
  ], {
1012
1087
  cwd: androidToolCwd(platform),
@@ -1015,13 +1090,13 @@ function bootAndroidEmulator(avdName, consolePort, { logFile, platform = process
1015
1090
  });
1016
1091
  child?.unref?.();
1017
1092
  const pid = child?.pid ?? null;
1018
- if (pid !== null && platform === "darwin" && app === "stim-desktop") openEmulatorInStimDesktop(`emulator-${consolePort}`);
1093
+ if (openViewer && pid !== null && platform === "darwin" && app === "stim-desktop") openEmulatorInStimDesktop(`emulator-${consolePort}`);
1019
1094
  return pid;
1020
1095
  }
1021
1096
  function emulatorStdio(logFile) {
1022
1097
  if (!logFile) return "ignore";
1023
1098
  try {
1024
- mkdirSync(dirname(logFile), { recursive: true });
1099
+ mkdirSync(dirname$1(logFile), { recursive: true });
1025
1100
  const fd = openSync(logFile, "w");
1026
1101
  return [
1027
1102
  "ignore",
@@ -1123,7 +1198,7 @@ function sleepSync(ms) {
1123
1198
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
1124
1199
  }
1125
1200
  function avdProcessLockPaths(avdDirectory, platform) {
1126
- return ["hardware-qemu.ini.lock", "userdata-qemu.img.lock"].map((name) => join(avdDirectory, name, ...platform === "win32" ? ["pid"] : []));
1201
+ return ["hardware-qemu.ini.lock", "userdata-qemu.img.lock"].map((name) => join$1(avdDirectory, name, ...platform === "win32" ? ["pid"] : []));
1127
1202
  }
1128
1203
  function readAvdProcessId(path) {
1129
1204
  try {
@@ -1177,7 +1252,18 @@ function scanAvdEmulatorProcesses(avdName, platform, listProcesses) {
1177
1252
  if (pids === null) throw new Error(`Could not read the process table to verify that owned AVD ${avdName} stopped.`);
1178
1253
  return pids;
1179
1254
  }
1180
- function assertOwnedAvdStopped(avdName, { processAlive = pidExists, listProcesses = listAvdEmulatorProcesses, ...resolveOptions } = {}) {
1255
+ function emulatorProcessRunning(pid) {
1256
+ if (!pidExists(pid)) return false;
1257
+ if (process.platform === "win32") return true;
1258
+ const stat = getExecutor().runFileQuiet("ps", [
1259
+ "-o",
1260
+ "stat=",
1261
+ "-p",
1262
+ String(pid)
1263
+ ], { timeoutMs: 5e3 });
1264
+ return stat === null || !stat.trim().startsWith("Z");
1265
+ }
1266
+ function assertOwnedAvdStopped(avdName, { processAlive = emulatorProcessRunning, listProcesses = listAvdEmulatorProcesses, ...resolveOptions } = {}) {
1181
1267
  const { processId } = resolveAvdProcess(avdName, resolveOptions);
1182
1268
  const live = new Set(scanAvdEmulatorProcesses(avdName, resolveOptions.platform ?? process.platform, listProcesses));
1183
1269
  if (processId !== null && processAlive(processId)) live.add(processId);
@@ -1312,20 +1398,22 @@ function once(read) {
1312
1398
  return result.value;
1313
1399
  };
1314
1400
  }
1401
+ /** Floor for a single `listAvds`, `adb devices`, or `emu avd name` call in {@link ownedAvdSerialResolver}. */
1402
+ const MIN_LOOKUP_TIMEOUT_MS = 1e3;
1315
1403
  /**
1316
1404
  * `resolveOwnedAvdSerial` for several AVDs against one reading of the AVD list, `adb devices`, and
1317
1405
  * each emulator's AVD name. The timeout budget starts when the resolver is created.
1318
1406
  */
1319
1407
  function ownedAvdSerialResolver({ timeoutMs } = {}) {
1320
1408
  const started = Date.now();
1321
- const remaining = () => ({ timeoutMs: timeoutMs === void 0 ? void 0 : Math.max(1, timeoutMs - (Date.now() - started)) });
1409
+ const remaining = () => ({ timeoutMs: timeoutMs === void 0 ? void 0 : Math.max(MIN_LOOKUP_TIMEOUT_MS, timeoutMs - (Date.now() - started)) });
1322
1410
  const avds = once(() => listAvds(remaining()));
1323
1411
  const adb = once(() => listAdbDevices(remaining()));
1324
1412
  const names = /* @__PURE__ */ new Map();
1325
1413
  const avdNameOf = (serial) => {
1326
- if (names.has(serial)) return names.get(serial);
1414
+ if (names.has(serial)) return names.get(serial) ?? null;
1327
1415
  const name = getAvdNameForSerial(serial, remaining());
1328
- if (name !== null) names.set(serial, name);
1416
+ names.set(serial, name);
1329
1417
  return name;
1330
1418
  };
1331
1419
  return (avdName) => {
@@ -1338,4 +1426,4 @@ function ownedAvdSerialResolver({ timeoutMs } = {}) {
1338
1426
  };
1339
1427
  }
1340
1428
  //#endregion
1341
- export { waitForBoot as $, listInstalledSystemImages as A, parseAvdDeviceProfile as B, extractEmulatorFailure as C, listAdbDevices as D, hostSystemImageArch as E, ownedAvdDirectory as F, profileNeedsFoldFeature as G, physicalDeviceModel as H, ownedAvdMatchesConfiguration as I, resolvePhysicalDevice as J, resetAdoptedAvd as K, ownedAvdName as L, memoizeEmulatorProbe as M, nextConsolePort as N, listAvdDeviceProfiles as O, ownedAvdDeviceProfile as P, waitForAndroidEmulatorShutdown as Q, ownedAvdSerialResolver as R, emulatorFailureRemedy as S, getAvdNameForSerial as T, pickDefaultSystemImage as U, parseAvdSystemImage as V, probeEmulatorSerial as W, sleepSync as X, shutdownAndroidEmulator as Y, systemImageSupportsFold as Z, bootAndroidEmulator as _, androidHome as a, dropParked as at, deleteAvd as b, androidSystemImageAbi as c, parkedMaxSetting as ct, assertOwnedAvdStopped as d, selectParked as dt, wipeAvdUserData as et, avdIniPaths as f, configuredAndroidEmulatorApp as ft, avdStorageRoots as g, avdPoolConfiguration as h, androidDeviceAbi as i, adoptParked as it, listOrphanedAvdDirectories as j, listAvds as k, androidToolCwd as l, readParked as lt, avdPathExists as m, OWNED_AVD_CONFIG_DEFAULTS as n, isStimOwnedSim as nt, androidPoolCandidates as o, isLegacyDeletionClaim as ot, avdNameAbsent as p, resolveOwnedAvdSerial as q, androidBuildToolName as r, POOL_SETTING_REMEDY as rt, androidPoolNoCandidatesRefusal as s, parkSim as st, DEFAULT_AVD_DEVICE_PROFILE as t, isStimOwnedAvd as tt, androidToolPath as u, removeParkedAfter as ut, configureNewOwnedAvd as v, findBuildTool as w, emulatorDiskSpaceRemedy as x, createOwnedAvd as y, ownedAvdSystemImage as z };
1429
+ export { trimAndroidCaches as $, listAvds as A, ownedAvdSystemImage as B, emulatorFailureRemedy as C, hostSystemImageArch as D, getAvdNameForSerial as E, ownedAvdDeviceProfile as F, probeEmulatorSerial as G, parseAvdSystemImage as H, ownedAvdDirectory as I, resolveOwnedAvdSerial as J, profileNeedsFoldFeature as K, ownedAvdMatchesConfiguration as L, listOrphanedAvdDirectories as M, memoizeEmulatorProbe as N, listAdbDevices as O, nextConsolePort as P, systemImageSupportsFold as Q, ownedAvdName as R, emulatorDiskSpaceRemedy as S, findBuildTool as T, physicalDeviceModel as U, parseAvdDeviceProfile as V, pickDefaultSystemImage as W, shutdownAndroidEmulator as X, resolvePhysicalDevice as Y, sleepSync as Z, avdStorageRoots as _, androidDeviceAbi as a, isStimOwnedSim as at, createOwnedAvd as b, androidPoolNoCandidatesRefusal as c, dropParked as ct, androidToolPath as d, parkSim as dt, waitForAndroidEmulatorShutdown as et, assertOwnedAvdStopped as f, parkedMaxSetting as ft, avdPoolConfiguration as g, avdPathExists as h, selectParked as ht, androidDataFreeBytes as i, isStimOwnedAvd as it, listInstalledSystemImages as j, listAvdDeviceProfiles as k, androidSystemImageAbi as l, eraseParkedAfter as lt, avdNameAbsent as m, removeParkedAfter as mt, OWNED_AVD_CONFIG_DEFAULTS as n, wipeAvdUserData as nt, androidHome as o, POOL_SETTING_REMEDY as ot, avdIniPaths as p, readParked as pt, resetAdoptedAvd as q, androidBuildToolName as r, configuredAndroidEmulatorApp as rt, androidPoolCandidates as s, adoptParked as st, DEFAULT_AVD_DEVICE_PROFILE as t, waitForBoot as tt, androidToolCwd as u, isLegacyDeletionClaim as ut, bootAndroidEmulator as v, extractEmulatorFailure as w, deleteAvd as x, configureNewOwnedAvd as y, ownedAvdSerialResolver as z };