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.
- package/README.md +23 -155
- package/dist/{android-DWssqHQ8.mjs → android-787HhP5w.mjs} +168 -39
- package/dist/{android-BRgBYzU0.mjs → android-DBkeHwpm.mjs} +250 -200
- package/dist/{android-DeYdjjBP.mjs → android-DPov-fv2.mjs} +2 -2
- package/dist/{android-cas-SWIjzeuL.mjs → android-cas-CITmIeWY.mjs} +22 -14
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/app-install-CrJ6t_bv.mjs +819 -0
- package/dist/budget-BP4jZZL4.mjs +620 -0
- package/dist/build-progress-BXx-ZBe4.mjs +512 -0
- package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Pubfp8wV.mjs} +4 -4
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +22 -19
- package/dist/collector-run.d.mts +7 -1
- package/dist/collector-run.mjs +10 -9
- package/dist/{command-output-B3ycpsYv.mjs → command-output-D5z2_bcF.mjs} +11 -2
- package/dist/{config-B7iyKWkL.mjs → config-BEq0Qw_a.mjs} +134 -121
- package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-r9UesA9a.mjs} +2 -2
- package/dist/{deps-oCXVNEqQ.mjs → deps-DP4eWqea.mjs} +8 -5
- package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-DJHoca49.mjs} +1 -1
- package/dist/{device-BUoZyMoR.mjs → dev-client-92U593KT.mjs} +1090 -660
- package/dist/{device-RdujjHLB.mjs → device-DhTZe7L4.mjs} +13 -20
- package/dist/{build-lock-CgtsB6TV.mjs → device-lease-CbEaIk7b.mjs} +481 -27
- package/dist/{device-pool-BxRwdufO.mjs → device-pool-Di8Q-9Ro.mjs} +7 -7
- package/dist/{device-remote-ByN8P6CB.mjs → device-remote-Dy1jGj3U.mjs} +645 -119
- package/dist/{doctor-BMt156qe.mjs → doctor-B-inAJmt.mjs} +137 -113
- package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-xxWKNO3-.mjs} +22 -17
- package/dist/exec-DK8KaVHK.mjs +133 -0
- package/dist/{gc-v2YcoJvm.mjs → gc-ChPFZMRC.mjs} +836 -813
- package/dist/{guide-BTAlD1_y.mjs → guide-DwmaD_Wa.mjs} +953 -209
- package/dist/guide-status-CWvti2Lg.mjs +125 -0
- package/dist/idle-DpRLp5lj.mjs +157 -0
- package/dist/{build-slots-C-oAHRT1.mjs → in-use-CjUluqIX.mjs} +102 -8
- package/dist/{ios-BcCiI4TS.mjs → ios-B2Fpqvlb.mjs} +60 -71
- package/dist/{ios-BP04pY5f.mjs → ios-Dd-hYJBh.mjs} +241 -234
- package/dist/{ios-device-CFXA46ez.mjs → ios-device-BiOsSVzd.mjs} +3 -2
- package/dist/{ios-device-CauFJiYx.mjs → ios-device-CtRdEVjx.mjs} +149 -52
- package/dist/launch-verify-CQS_Kg-C.mjs +365 -0
- package/dist/{logs-CakdBTtm.mjs → logs-MARIy6m9.mjs} +20 -15
- package/dist/metro-BWLOJ_Wr.mjs +409 -0
- package/dist/{named-ports-DzMOlmaX.mjs → named-ports-DiTfSz6p.mjs} +4 -4
- package/dist/{ndjson-BZwI1NBv.mjs → ndjson-CDY9IkVC.mjs} +23 -43
- package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
- package/dist/{ownership-Cp52QAK1.mjs → ownership-D2xbLVoz.mjs} +2 -3
- package/dist/{teardown-BJqEzPo_.mjs → ownership-DZjdx8Gq.mjs} +664 -612
- package/dist/{ports-BIbJaKNW.mjs → ports-CLSSFOuz.mjs} +3 -3
- package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
- package/dist/{project-DDZWf_16.mjs → project-BXZzOABt.mjs} +8 -3
- package/dist/{reload-BdypPbjM.mjs → reload-DoETJ3rl.mjs} +11 -13
- package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DVbb7kXH.mjs} +2 -2
- package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-BvOCeakp.mjs} +8 -7
- package/dist/server-expo-DBVTdult.mjs +2 -0
- package/dist/{settings-BXXorVM2.mjs → settings-DOugPlPz.mjs} +59 -159
- package/dist/settings-EWYNwe72.mjs +378 -0
- package/dist/settings.schema.json +1064 -0
- package/dist/simslim-Ca3487H6.mjs +436 -0
- package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-C5cg0fmk.mjs} +4 -4
- package/dist/spawn-claims-CUleC2cM.mjs +49 -0
- package/dist/{start-CmxtOKjU.mjs → start-F9gv5dcC.mjs} +159 -114
- package/dist/start-VoFsPSes.mjs +2 -0
- package/dist/{state-DZobzxLW.mjs → state-WGdIbi_d.mjs} +5 -3
- package/dist/{stats-BYcZ6n1h.mjs → stats-BAgJaLA4.mjs} +66 -5
- package/dist/{stats-CLg5RuDS.mjs → stats-DBYJ5bxL.mjs} +4 -4
- package/dist/status-DLCD-o3s.mjs +764 -0
- package/dist/status-Dr4fRkym.mjs +216 -0
- package/dist/{guide-status-Craq9z0t.mjs → stim-installations-CINXy_su.mjs} +3 -122
- package/dist/{stop-C8JW6kKp.mjs → stop-Dj4pfmyv.mjs} +111 -31
- package/dist/{stop-B1kOpYei.mjs → stop-ZvOeTmKb.mjs} +2 -2
- package/dist/supervisor-run.d.mts +11 -2
- package/dist/supervisor-run.mjs +228 -42
- package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-BXA96myD.mjs} +14 -9
- package/dist/workspaces-BTUyFfkv.mjs +1003 -0
- package/dist/worktree-4aZg2NnI.mjs +2 -0
- package/dist/{worktree-CFLA-6MX.mjs → worktree-BMS7GeqB.mjs} +523 -61
- package/dist/{worktree-FG_ZKKdb.mjs → worktree-BlizKDpZ.mjs} +200 -54
- package/dist/{xcode-B6YaWeg0.mjs → xcode-B2tBkf0m.mjs} +66 -57
- package/package.json +6 -6
- package/skill/SKILL.md +2 -0
- package/dist/app-install-BDbsSgCl.mjs +0 -1175
- package/dist/dev-client-BmT-ioFI.mjs +0 -797
- package/dist/device-lease-Ffz_M5t0.mjs +0 -359
- package/dist/exec-B29XNuhP.mjs +0 -77
- package/dist/logs-query-BGL-Pl12.mjs +0 -290
- package/dist/metro-C7bcJ0uo.mjs +0 -233
- package/dist/ownership-wolE-0Do.mjs +0 -470
- package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
- package/dist/reclaim-KJ-0uWEF.mjs +0 -361
- package/dist/server-expo-YDDG3eP7.mjs +0 -2
- package/dist/spawn-entry-nCE6m770.mjs +0 -14
- package/dist/status-DdkLki47.mjs +0 -426
- package/dist/workspace-state-BT5E3SA2.mjs +0 -146
- package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@ keep native and JavaScript builds warm across worktrees.
|
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
|
-
If you previously installed `stim-cli`
|
|
12
|
-
[migration instructions](#migrating-from-stim-cli) first.
|
|
11
|
+
Node 22.12.0 or later is required. If you previously installed `stim-cli`
|
|
12
|
+
globally, follow the [migration instructions](#migrating-from-stim-cli) first.
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
npm install --global stim
|
|
@@ -22,18 +22,6 @@ Run without a global install when needed:
|
|
|
22
22
|
npx stim <command>
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Node 22.12.0 or later is required.
|
|
26
|
-
|
|
27
|
-
Machine defaults in `~/.stim/config.json` can enable or disable native artifact
|
|
28
|
-
caching, remote caches, Metro sharing, iOS compiler caching and prefix mapping,
|
|
29
|
-
and Android ccache/CAS, PCH, Gradle caching, and target ABI narrowing. The
|
|
30
|
-
top-level `iosSimulatorApp` setting selects Xcode's viewer or an installed
|
|
31
|
-
Siniulator.
|
|
32
|
-
`.stim.json` runtime overrides apply per app, beside its `package.json`; monorepo
|
|
33
|
-
apps do not inherit the repository-root file. Worktree-copy rules stay at the
|
|
34
|
-
repository root. Run `stim guide settings` for the
|
|
35
|
-
`optimizations` schema; existing defaults remain unchanged.
|
|
36
|
-
|
|
37
25
|
## Normal workflow
|
|
38
26
|
|
|
39
27
|
```bash
|
|
@@ -44,115 +32,29 @@ stim logs --errors
|
|
|
44
32
|
stim stop
|
|
45
33
|
```
|
|
46
34
|
|
|
47
|
-
For `stim logs --errors`, a clean check requires exit code 0 and no matching
|
|
48
|
-
errors in the captured logs. Exit code 0 alone means the query succeeded, even
|
|
49
|
-
when it prints errors. A workspace that has never produced a log timeline
|
|
50
|
-
refuses with `STIM_NO_PROJECT`; in a monorepo, the error names the nearest
|
|
51
|
-
registered descendant app with logs when one exists.
|
|
52
|
-
|
|
53
|
-
Use `stim start --reset-cache` to recover from stale Metro transforms or file-map
|
|
54
|
-
state. It restarts only this app's verified owned Metro, keeping its port and
|
|
55
|
-
devices, with Metro's own reset: every store in the app's Metro config is cleared,
|
|
56
|
-
including this app's shared transform store, so other worktrees of the same app
|
|
57
|
-
rebuild their transforms too. Other apps and native build caches are unchanged.
|
|
58
|
-
|
|
59
|
-
Use `stim doctor --platform ios` or `stim doctor --platform android` when only
|
|
60
|
-
one native platform is in scope; shared project checks still run. Doctor also
|
|
61
|
-
prints the running CLI version and the `stim` installation resolved from PATH,
|
|
62
|
-
and flags a resolved installation that is older than another available one.
|
|
63
|
-
|
|
64
|
-
For parallel iOS work, SimSlim is recommended as an optional way to reduce
|
|
65
|
-
simulator background services and memory use. Run `stim guide lifecycle simslim`
|
|
66
|
-
to review the service tradeoffs and configure a profile. Doctor recommends the
|
|
67
|
-
setup without installing or enabling it. When a simulator cannot start
|
|
68
|
-
processes, Stim bounds the wait and reports observed host memory pressure when
|
|
69
|
-
available; free memory before retrying. A timeout alone does not prove OOM.
|
|
70
|
-
|
|
71
|
-
For stale Android CMake launcher findings, stop native builds and run
|
|
72
|
-
`stim doctor --fix --platform android` in the affected checkout. It clears
|
|
73
|
-
affected ignored, untracked generated `.cxx` configurations in the app and
|
|
74
|
-
installed native modules, then reports remaining findings. The next build
|
|
75
|
-
recreates that output; source files, custom launcher settings, and shared
|
|
76
|
-
ccache entries are preserved. Its cache-lock check cannot detect uncached,
|
|
77
|
-
release-swap fallback, or direct Gradle builds; stop all native builds first.
|
|
78
|
-
|
|
79
35
|
Stim builds or restores the app, installs it, launches it, and checks launch
|
|
80
36
|
readiness. Plain output streams progress and reports the complete result. Use
|
|
81
37
|
`--json` when a script needs structured data.
|
|
82
38
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
The
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
without a package or SDK. A captured pending message extends the default
|
|
103
|
-
three-second stability window after bundle delivery to a bounded wait for ready.
|
|
104
|
-
Managed Metro response capture distinguishes a finished build from a finished
|
|
105
|
-
response; servers without capture use the build-complete marker. Run
|
|
106
|
-
`stim guide lifecycle readiness` for implementation instructions; verify the
|
|
107
|
-
expected screen separately.
|
|
108
|
-
|
|
109
|
-
`stim reload [ios|android]` requests a JavaScript reload in the live app on this
|
|
110
|
-
workspace's owned local device. Use it when an error screen remains after a fix,
|
|
111
|
-
and on Android after a failed first bundle load. An iOS app whose first bundle
|
|
112
|
-
failed never connects to Metro, so reload cannot reach it. It reloads
|
|
113
|
-
JavaScript and never restarts the app. It is not part of the normal workflow
|
|
114
|
-
and does not build, install, boot, or launch an app. The platform is optional
|
|
115
|
-
when only one app is live.
|
|
116
|
-
Success confirms that the request was sent; Stim does not observe completion.
|
|
117
|
-
Verify the expected UI on the reported device and inspect `stim logs --errors`
|
|
118
|
-
before claiming recovery.
|
|
119
|
-
|
|
120
|
-
For an isolated branch, create the worktree with Git, then warm it:
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
git worktree add -b feature/settings ../feature-settings HEAD
|
|
124
|
-
cd ../feature-settings
|
|
125
|
-
stim worktree warm
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
If a harness already created the linked worktree, run only `stim worktree warm`
|
|
129
|
-
there. Warm copies missing ignored state from the source checkout, including
|
|
130
|
-
eligible `.env` and local configuration files. Existing entries are preserved;
|
|
131
|
-
existing ignored directories such as `node_modules` are skipped whole.
|
|
132
|
-
|
|
133
|
-
`stim worktree warm --refresh` brings the source checkout up to date first: it
|
|
134
|
-
checks the upstream, fetches changes when needed, fast-forwards whatever branch is checked out there, and installs what
|
|
135
|
-
the new commits moved before copying. It refuses a source checkout it cannot
|
|
136
|
-
move (uncommitted tracked changes, a rebase or merge in progress, a detached
|
|
137
|
-
HEAD, a diverged branch) and never switches branches. One lock per repository
|
|
138
|
-
keeps a copy from reading a `node_modules` a refresh is rewriting; plain warms
|
|
139
|
-
still run side by side. A plain warm also refuses (`STIM_DEPS_INCOMPLETE`) when the last
|
|
140
|
-
install of the lockfile on disk did not finish, instead of copying a partial
|
|
141
|
-
`node_modules`; `stim worktree warm --refresh` reinstalls it.
|
|
142
|
-
|
|
143
|
-
Wait for warm to finish before editing, installing dependencies, starting
|
|
144
|
-
Metro/builds, or running another warm in that worktree. Concurrent writes are
|
|
145
|
-
unsafe: entries created after the initial check can be overwritten or removed.
|
|
146
|
-
|
|
147
|
-
After the work is preserved, `stim worktree remove` removes any linked
|
|
148
|
-
worktree, warmed or not. Git-created branches stay. See the
|
|
149
|
-
[worktree guide](https://stim.appandflow.com/docs/worktrees) for exclusions
|
|
150
|
-
and cleanup rules.
|
|
151
|
-
|
|
152
|
-
## Reference
|
|
153
|
-
|
|
154
|
-
The [documentation website](https://stim.appandflow.com/) explains the
|
|
155
|
-
human workflow and all commands.
|
|
39
|
+
## Documentation
|
|
40
|
+
|
|
41
|
+
The [documentation website](https://stim.appandflow.com/) is the full
|
|
42
|
+
reference:
|
|
43
|
+
|
|
44
|
+
- [Getting started](https://stim.appandflow.com/docs/getting-started): terms,
|
|
45
|
+
the first run, parallel worktrees, and what to do when a run fails.
|
|
46
|
+
- [Worktrees](https://stim.appandflow.com/docs/worktrees): `worktree warm`,
|
|
47
|
+
`--refresh`, `worktree remove`, and `gc --worktrees` to remove finished
|
|
48
|
+
worktrees in bulk.
|
|
49
|
+
- [Build caches](https://stim.appandflow.com/docs/build-caches): what `gc`
|
|
50
|
+
reports and trims, including the build outputs of idle workspaces.
|
|
51
|
+
- [Devices](https://stim.appandflow.com/docs/owned-devices): owned simulators
|
|
52
|
+
and emulators, physical devices, slots, and remote devices.
|
|
53
|
+
- [EAS development builds](https://stim.appandflow.com/docs/eas-builds).
|
|
54
|
+
- [Commands](https://stim.appandflow.com/docs/commands) and
|
|
55
|
+
[settings](https://stim.appandflow.com/docs/settings).
|
|
56
|
+
- [Troubleshooting](https://stim.appandflow.com/docs/troubleshooting): every
|
|
57
|
+
refusal code and its remedy.
|
|
156
58
|
|
|
157
59
|
The installed CLI contains version-matched operational guidance:
|
|
158
60
|
|
|
@@ -163,10 +65,6 @@ stim <command> --help
|
|
|
163
65
|
stim guide
|
|
164
66
|
```
|
|
165
67
|
|
|
166
|
-
Runtime state defaults to `~/.stim`. Set `STIM_HOME` to move it. Stim manages
|
|
167
|
-
owned simulators and emulators, leases connected physical devices, and supports
|
|
168
|
-
configured remote devices.
|
|
169
|
-
|
|
170
68
|
## Migrating from stim-cli
|
|
171
69
|
|
|
172
70
|
Remove the old global package before installing `stim`, since both provide the
|
|
@@ -180,36 +78,6 @@ npm install --global stim
|
|
|
180
78
|
Update programmatic imports from `stim-cli/cache-manifest` to
|
|
181
79
|
`stim/cache-manifest`. The `@stim-cli/*` packages keep their names.
|
|
182
80
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
### Multiple devices in one workspace
|
|
186
|
-
|
|
187
|
-
Use a stable slot name for each simulator, emulator, or physical device. Slots
|
|
188
|
-
can use identical models and all share the workspace's Metro server and build
|
|
189
|
-
cache. Omit `--slot` to reuse your existing default device.
|
|
190
|
-
|
|
191
|
-
```sh
|
|
192
|
-
stim ios --slot phone
|
|
193
|
-
stim ios --slot tablet --device-type "iPad Pro 13-inch (M4)"
|
|
194
|
-
stim ios --slot hardware --device <udid>
|
|
195
|
-
stim logs --slot tablet --source device
|
|
196
|
-
stim stop --slot tablet
|
|
197
|
-
```
|
|
81
|
+
## License
|
|
198
82
|
|
|
199
|
-
|
|
200
|
-
`--slot` preserves the shared server and other devices. `stim reload ios` or
|
|
201
|
-
`stim reload android` can reload multiple connected devices. Read
|
|
202
|
-
`stim guide lifecycle options` for launch verification and recycling behavior.
|
|
203
|
-
|
|
204
|
-
### Named server ports
|
|
205
|
-
|
|
206
|
-
Use `stim ports get web` to reserve a workspace port for a server Stim does not
|
|
207
|
-
manage, then pass that number to the server, for example
|
|
208
|
-
`pnpm exec vite --port "$(stim ports get web)" --strictPort`.
|
|
209
|
-
`stim ports` lists named allocations and managed Metro. `stim ports stop web`
|
|
210
|
-
stops the TCP listener and releases its port; `--dry-run` previews the action.
|
|
211
|
-
`stim ports release web` releases without stopping. Omitting the label selects
|
|
212
|
-
all named ports. These commands leave Metro alone, and `stim stop` leaves
|
|
213
|
-
named ports alone. Worktree removal and `gc --delete` stop and release named
|
|
214
|
-
ports for removed workspaces. Read `stim guide ports` for the reserved band,
|
|
215
|
-
listener ownership, and allocation limits.
|
|
83
|
+
MIT
|
|
@@ -1,9 +1,68 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { c as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { u as getConfigDir } from "./config-BEq0Qw_a.mjs";
|
|
2
|
+
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
3
|
+
import { P as withDirLock, c as signalProcessTree, i as pidExists } from "./metro-BWLOJ_Wr.mjs";
|
|
4
|
+
import { c as androidDataPartitionSizeBytes } from "./settings-DOugPlPz.mjs";
|
|
5
|
+
import { mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, openSync as openSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, renameSync as renameSync$1, rmSync as rmSync$1, statSync as statSync$1, writeFileSync as writeFileSync$1 } from "fs";
|
|
6
7
|
import { dirname, isAbsolute, join, resolve } from "path";
|
|
8
|
+
import { createdDevicesFile, createdDevicesLock, readCreatedDevices, readCreatedDevices as readCreatedDevices$1 } from "@stim-cli/core/state";
|
|
9
|
+
import { homedir, tmpdir } from "os";
|
|
10
|
+
//#region src/devices/created-devices.ts
|
|
11
|
+
function update(platform, change) {
|
|
12
|
+
const dir = getConfigDir();
|
|
13
|
+
withDirLock(createdDevicesLock(), () => {
|
|
14
|
+
const current = readCreatedDevices();
|
|
15
|
+
const entries = new Set(current[platform]);
|
|
16
|
+
if (!change(entries)) return;
|
|
17
|
+
const next = {
|
|
18
|
+
version: 1,
|
|
19
|
+
ios: [...current.ios],
|
|
20
|
+
android: [...current.android],
|
|
21
|
+
[platform]: [...entries]
|
|
22
|
+
};
|
|
23
|
+
const file = createdDevicesFile();
|
|
24
|
+
const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
25
|
+
writeFileSync(tmp, `${JSON.stringify(next, null, 2)}\n`);
|
|
26
|
+
try {
|
|
27
|
+
renameSync(tmp, file);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
rmSync(tmp, { force: true });
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}, { ensureParent: () => mkdirSync(dir, { recursive: true }) });
|
|
33
|
+
}
|
|
34
|
+
function recordCreatedDevice(platform, id) {
|
|
35
|
+
update(platform, (entries) => entries.size !== entries.add(id).size);
|
|
36
|
+
}
|
|
37
|
+
function forgetCreatedDevice(platform, id) {
|
|
38
|
+
update(platform, (entries) => entries.delete(id));
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/devices/device-ownership.ts
|
|
42
|
+
const OWNED_SIM_NAME = /^stim-[A-Za-z0-9._-]+ \(.* \d+(?:\.\d+)*\)(?: [A-Za-z0-9-]+)?$/;
|
|
43
|
+
const OWNED_AVD_NAME = /^stim-[A-Za-z0-9._-]+$/;
|
|
44
|
+
const GIB = 1024 ** 3;
|
|
45
|
+
function isOwnedSimName(name) {
|
|
46
|
+
return OWNED_SIM_NAME.test(name);
|
|
47
|
+
}
|
|
48
|
+
function isOwnedAvdName(name) {
|
|
49
|
+
return OWNED_AVD_NAME.test(name);
|
|
50
|
+
}
|
|
51
|
+
function avdConfigWrittenByStim(configIni) {
|
|
52
|
+
for (const line of String(configIni ?? "").split(/\r?\n/)) {
|
|
53
|
+
const match = /^disk\.dataPartition\.size=(\d+)$/.exec(line.trim());
|
|
54
|
+
if (match) return Number(match[1]) > 0 && Number(match[1]) % GIB === 0;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
function isStimOwnedSim(sim, created = readCreatedDevices$1()) {
|
|
59
|
+
const name = sim.name ?? "";
|
|
60
|
+
return name.startsWith("stim-") && (created.ios.has(sim.udid) || isOwnedSimName(name));
|
|
61
|
+
}
|
|
62
|
+
function isStimOwnedAvd(name, readConfig, created = readCreatedDevices$1()) {
|
|
63
|
+
return isOwnedAvdName(name) && (created.android.has(name) || avdConfigWrittenByStim(readConfig()));
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
7
66
|
//#region src/devices/android.ts
|
|
8
67
|
const ANDROID_ABIS = /* @__PURE__ */ new Set([
|
|
9
68
|
"armeabi-v7a",
|
|
@@ -38,7 +97,7 @@ function androidToolPath(tool, platform = process.platform) {
|
|
|
38
97
|
const bare = location.at(-1);
|
|
39
98
|
const name = platform === "win32" ? `${bare}${WINDOWS_SDK_TOOL_EXTENSIONS[tool]}` : bare;
|
|
40
99
|
const abs = join(androidHome(), ...location.slice(0, -1), name);
|
|
41
|
-
return existsSync(abs) ? abs : bare;
|
|
100
|
+
return existsSync$1(abs) ? abs : bare;
|
|
42
101
|
}
|
|
43
102
|
const WINDOWS_BUILD_TOOL_EXTENSIONS = { apksigner: ".bat" };
|
|
44
103
|
function androidBuildToolName(tool, platform = process.platform) {
|
|
@@ -60,7 +119,7 @@ function buildToolsMajor(version) {
|
|
|
60
119
|
const major = Number(String(version ?? "").split(/[.-]/)[0]);
|
|
61
120
|
return Number.isFinite(major) ? major : 0;
|
|
62
121
|
}
|
|
63
|
-
function findBuildTool(tools, { home = androidHome(), readDir = readdirSync, exists = existsSync, platform = process.platform } = {}) {
|
|
122
|
+
function findBuildTool(tools, { home = androidHome(), readDir = readdirSync$1, exists = existsSync$1, platform = process.platform } = {}) {
|
|
64
123
|
const root = join(home, "build-tools");
|
|
65
124
|
let versions = [];
|
|
66
125
|
try {
|
|
@@ -91,7 +150,7 @@ function androidTool(tool) {
|
|
|
91
150
|
function listInstalledSystemImages() {
|
|
92
151
|
const root = join(androidHome(), "system-images");
|
|
93
152
|
const images = [];
|
|
94
|
-
if (!existsSync(root)) return images;
|
|
153
|
+
if (!existsSync$1(root)) return images;
|
|
95
154
|
for (const apiDir of safeList(root)) {
|
|
96
155
|
const m = apiDir.match(/^android-(\d+)$/);
|
|
97
156
|
if (!m) continue;
|
|
@@ -107,7 +166,7 @@ function listInstalledSystemImages() {
|
|
|
107
166
|
}
|
|
108
167
|
function safeList(dir) {
|
|
109
168
|
try {
|
|
110
|
-
return readdirSync(dir);
|
|
169
|
+
return readdirSync$1(dir);
|
|
111
170
|
} catch {
|
|
112
171
|
return [];
|
|
113
172
|
}
|
|
@@ -132,6 +191,7 @@ async function createOwnedAvd(label, { systemImage, spawn = (...args) => getExec
|
|
|
132
191
|
throw new Error(`No ${arch} Android system image is installed. Install one, e.g.: sdkmanager "system-images;android-36;google_apis;${arch}"`);
|
|
133
192
|
}
|
|
134
193
|
const avdName = ownedAvdName(label);
|
|
194
|
+
recordCreatedDevice("android", avdName);
|
|
135
195
|
const tool = androidToolPath("avdmanager");
|
|
136
196
|
const args = [
|
|
137
197
|
"create",
|
|
@@ -183,7 +243,7 @@ function parseAvdSystemImage(configIni) {
|
|
|
183
243
|
}
|
|
184
244
|
return null;
|
|
185
245
|
}
|
|
186
|
-
function ownedAvdSystemImage(avdName, { avdDirectory = ownedAvdDirectory, readFile = (path) => readFileSync(path, "utf8") } = {}) {
|
|
246
|
+
function ownedAvdSystemImage(avdName, { avdDirectory = ownedAvdDirectory, readFile = (path) => readFileSync$1(path, "utf8") } = {}) {
|
|
187
247
|
const directory = avdDirectory(avdName);
|
|
188
248
|
if (!directory) return null;
|
|
189
249
|
try {
|
|
@@ -195,13 +255,32 @@ function ownedAvdSystemImage(avdName, { avdDirectory = ownedAvdDirectory, readFi
|
|
|
195
255
|
function sanitizeAvdLabel(label) {
|
|
196
256
|
return String(label).replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
197
257
|
}
|
|
258
|
+
function avdConfigIni(avdName, directory = ownedAvdDirectory(avdName)) {
|
|
259
|
+
if (!directory) return null;
|
|
260
|
+
try {
|
|
261
|
+
return readFileSync$1(join(directory, "config.ini"), "utf8");
|
|
262
|
+
} catch {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function registeredOrOrphanedAvdDirectory(avdName) {
|
|
267
|
+
return ownedAvdDirectory(avdName) ?? listOrphanedAvdDirectories(avdName)[0]?.directory ?? null;
|
|
268
|
+
}
|
|
269
|
+
function isStimOwnedAvdName(avdName, directory) {
|
|
270
|
+
return isStimOwnedAvd(avdName, () => avdConfigIni(avdName, directory === void 0 ? registeredOrOrphanedAvdDirectory(avdName) : directory));
|
|
271
|
+
}
|
|
198
272
|
function ownedAvdName(label) {
|
|
199
273
|
const clean = sanitizeAvdLabel(label);
|
|
200
274
|
return `stim-${clean.startsWith("stim-") ? clean.slice(5) : clean}`;
|
|
201
275
|
}
|
|
276
|
+
const AVDMANAGER_DELETE_TIMEOUT_MS = 12e4;
|
|
202
277
|
function deleteAvd(avdName) {
|
|
203
|
-
if (!avdName
|
|
204
|
-
getExecutor().run(`${androidTool("avdmanager")} delete avd -n "${avdName}"
|
|
278
|
+
if (!isStimOwnedAvdName(avdName)) throw new Error(`Refusing to delete AVD "${avdName}": not a Stim-owned AVD; Stim has no record of creating it.`);
|
|
279
|
+
getExecutor().run(`${androidTool("avdmanager")} delete avd -n "${avdName}"`, {
|
|
280
|
+
timeoutMs: AVDMANAGER_DELETE_TIMEOUT_MS,
|
|
281
|
+
killSignal: "SIGKILL"
|
|
282
|
+
});
|
|
283
|
+
forgetCreatedDevice("android", avdName);
|
|
205
284
|
}
|
|
206
285
|
function parseAvdList(text) {
|
|
207
286
|
return text.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("INFO") && !l.startsWith("WARNING"));
|
|
@@ -416,7 +495,7 @@ function avdStorageRoots() {
|
|
|
416
495
|
}
|
|
417
496
|
function avdPathExists(path) {
|
|
418
497
|
try {
|
|
419
|
-
lstatSync(path);
|
|
498
|
+
lstatSync$1(path);
|
|
420
499
|
return true;
|
|
421
500
|
} catch (error) {
|
|
422
501
|
if (error.code === "ENOENT") return false;
|
|
@@ -427,17 +506,17 @@ function listOrphanedAvdDirectories(avdName) {
|
|
|
427
506
|
const roots = avdStorageRoots();
|
|
428
507
|
const stores = roots.filter(avdPathExists).map((root) => ({
|
|
429
508
|
root,
|
|
430
|
-
canonicalRoot: realpathSync(root),
|
|
431
|
-
names: readdirSync(root)
|
|
509
|
+
canonicalRoot: realpathSync$1(root),
|
|
510
|
+
names: readdirSync$1(root)
|
|
432
511
|
}));
|
|
433
512
|
const registered = /* @__PURE__ */ new Set();
|
|
434
513
|
for (const { root, canonicalRoot, names } of stores) for (const name of names.filter((entry) => entry.endsWith(".ini"))) {
|
|
435
514
|
const path = join(root, name);
|
|
436
|
-
const ini = readFileSync(path, "utf8");
|
|
515
|
+
const ini = readFileSync$1(path, "utf8");
|
|
437
516
|
const targets = /* @__PURE__ */ new Set([...avdIniPaths(root, ini), ...avdIniPaths(canonicalRoot, ini)]);
|
|
438
517
|
if (!targets.size) throw new Error(`Cannot verify AVD registration at ${path}; its data was kept.`);
|
|
439
518
|
for (const target of targets) try {
|
|
440
|
-
registered.add(realpathSync(target));
|
|
519
|
+
registered.add(realpathSync$1(target));
|
|
441
520
|
} catch (error) {
|
|
442
521
|
const code = error.code;
|
|
443
522
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
@@ -452,8 +531,8 @@ function listOrphanedAvdDirectories(avdName) {
|
|
|
452
531
|
if (roots.some((candidate) => avdPathExists(join(candidate, `${name}.ini`)))) continue;
|
|
453
532
|
const directory = join(canonicalRoot, entry);
|
|
454
533
|
if (registered.has(directory) || !avdPathExists(directory)) continue;
|
|
455
|
-
const stat = lstatSync(directory);
|
|
456
|
-
if (!stat.isDirectory() || stat.isSymbolicLink() || realpathSync(directory) !== directory) throw new Error(`Cannot verify AVD data at ${directory}; it was kept.`);
|
|
534
|
+
const stat = lstatSync$1(directory);
|
|
535
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || realpathSync$1(directory) !== directory) throw new Error(`Cannot verify AVD data at ${directory}; it was kept.`);
|
|
457
536
|
if (!orphaned.some((candidate) => candidate.directory === directory)) orphaned.push({
|
|
458
537
|
name,
|
|
459
538
|
directory,
|
|
@@ -464,7 +543,7 @@ function listOrphanedAvdDirectories(avdName) {
|
|
|
464
543
|
}
|
|
465
544
|
return orphaned;
|
|
466
545
|
}
|
|
467
|
-
function ownedAvdDirectory(avdName, { env = process.env, home = homedir(), readFile = (path) => readFileSync(path, "utf8"), realpath = realpathSync, isDirectory = (path) => statSync(path).isDirectory() } = {}) {
|
|
546
|
+
function ownedAvdDirectory(avdName, { env = process.env, home = homedir(), readFile = (path) => readFileSync$1(path, "utf8"), realpath = realpathSync$1, isDirectory = (path) => statSync$1(path).isDirectory() } = {}) {
|
|
468
547
|
if (!/^stim-[A-Za-z0-9._-]+$/.test(avdName)) return null;
|
|
469
548
|
const roots = [
|
|
470
549
|
env.ANDROID_AVD_HOME,
|
|
@@ -488,6 +567,21 @@ function ownedAvdDirectory(avdName, { env = process.env, home = homedir(), readF
|
|
|
488
567
|
}
|
|
489
568
|
return null;
|
|
490
569
|
}
|
|
570
|
+
const AVD_USER_DATA = [
|
|
571
|
+
"userdata-qemu.img",
|
|
572
|
+
"userdata-qemu.img.qcow2",
|
|
573
|
+
"encryptionkey.img",
|
|
574
|
+
"encryptionkey.img.qcow2",
|
|
575
|
+
"cache.img",
|
|
576
|
+
"cache.img.qcow2",
|
|
577
|
+
"snapshots"
|
|
578
|
+
];
|
|
579
|
+
function wipeAvdUserData(directory) {
|
|
580
|
+
for (const name of AVD_USER_DATA) rmSync$1(join(directory, name), {
|
|
581
|
+
recursive: true,
|
|
582
|
+
force: true
|
|
583
|
+
});
|
|
584
|
+
}
|
|
491
585
|
function withAvdConfigOverrides(contents, overrides) {
|
|
492
586
|
const newline = contents.includes("\r\n") ? "\r\n" : "\n";
|
|
493
587
|
const trailingNewline = /\r?\n$/.test(contents);
|
|
@@ -509,10 +603,10 @@ function withAvdConfigOverrides(contents, overrides) {
|
|
|
509
603
|
return updated.join(newline) + (trailingNewline ? newline : "");
|
|
510
604
|
}
|
|
511
605
|
let avdConfigWriteSequence = 0;
|
|
512
|
-
function configureNewOwnedAvd(avdName, { dataPartitionSizeGb, avdConfig = {} }, { avdDirectory = ownedAvdDirectory, readFile = (path) => readFileSync(path, "utf8"), writeFile = (path, contents) => writeFileSync(path, contents, {
|
|
606
|
+
function configureNewOwnedAvd(avdName, { dataPartitionSizeGb, avdConfig = {} }, { avdDirectory = ownedAvdDirectory, readFile = (path) => readFileSync$1(path, "utf8"), writeFile = (path, contents) => writeFileSync$1(path, contents, {
|
|
513
607
|
encoding: "utf8",
|
|
514
608
|
flag: "wx"
|
|
515
|
-
}), rename = renameSync, remove = (path) => rmSync(path, { force: true }) } = {}) {
|
|
609
|
+
}), rename = renameSync$1, remove = (path) => rmSync$1(path, { force: true }) } = {}) {
|
|
516
610
|
const sizeBytes = androidDataPartitionSizeBytes(dataPartitionSizeGb);
|
|
517
611
|
const directory = avdDirectory(avdName);
|
|
518
612
|
if (!directory) throw new Error(`Could not resolve the content directory for newly created AVD ${avdName}.`);
|
|
@@ -547,7 +641,7 @@ function avdPoolConfiguration(dataPartitionSizeGb, avdConfig) {
|
|
|
547
641
|
function ownedAvdMatchesConfiguration(avdName, configuration) {
|
|
548
642
|
const directory = ownedAvdDirectory(avdName);
|
|
549
643
|
if (!directory) return false;
|
|
550
|
-
const contents = readFileSync(join(directory, "config.ini"), "utf8");
|
|
644
|
+
const contents = readFileSync$1(join(directory, "config.ini"), "utf8");
|
|
551
645
|
const expected = JSON.parse(configuration);
|
|
552
646
|
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]}`));
|
|
553
647
|
}
|
|
@@ -630,7 +724,7 @@ const CRASH_REPORT_MODE_EMULATOR_VERSION = 37;
|
|
|
630
724
|
function emulatorCrashDatabases(env = process.env) {
|
|
631
725
|
const root = join(env.TMP || env.TEMP || tmpdir(), "AndroidEmulator");
|
|
632
726
|
try {
|
|
633
|
-
return readdirSync(root).filter((name) => /^emu-crash-.*\.db$/.test(name)).map((name) => join(root, name));
|
|
727
|
+
return readdirSync$1(root).filter((name) => /^emu-crash-.*\.db$/.test(name)).map((name) => join(root, name));
|
|
634
728
|
} catch {
|
|
635
729
|
return [];
|
|
636
730
|
}
|
|
@@ -644,7 +738,7 @@ function emulatorCrashDatabases(env = process.env) {
|
|
|
644
738
|
* removed instead, which has the same effect unless a crash handler is still writing into it.
|
|
645
739
|
* https://github.com/appandflow/stim/issues/918
|
|
646
740
|
*/
|
|
647
|
-
function suppressEmulatorCrashConsent({ platform = process.platform, version = emulatorMajorVersion, databases = emulatorCrashDatabases, remove = (path) => rmSync(path, {
|
|
741
|
+
function suppressEmulatorCrashConsent({ platform = process.platform, version = emulatorMajorVersion, databases = emulatorCrashDatabases, remove = (path) => rmSync$1(path, {
|
|
648
742
|
recursive: true,
|
|
649
743
|
force: true
|
|
650
744
|
}) } = {}) {
|
|
@@ -662,6 +756,9 @@ function bootAndroidEmulator(avdName, consolePort, { logFile, platform = process
|
|
|
662
756
|
avdName,
|
|
663
757
|
"-port",
|
|
664
758
|
String(consolePort),
|
|
759
|
+
"-grpc",
|
|
760
|
+
String(consolePort + 3e3),
|
|
761
|
+
"-grpc-use-token",
|
|
665
762
|
...headlessEmulatorArgs(),
|
|
666
763
|
...suppressEmulatorCrashConsent({ platform })
|
|
667
764
|
], {
|
|
@@ -675,8 +772,8 @@ function bootAndroidEmulator(avdName, consolePort, { logFile, platform = process
|
|
|
675
772
|
function emulatorStdio(logFile) {
|
|
676
773
|
if (!logFile) return "ignore";
|
|
677
774
|
try {
|
|
678
|
-
mkdirSync(dirname(logFile), { recursive: true });
|
|
679
|
-
const fd = openSync(logFile, "w");
|
|
775
|
+
mkdirSync$1(dirname(logFile), { recursive: true });
|
|
776
|
+
const fd = openSync$1(logFile, "w");
|
|
680
777
|
return [
|
|
681
778
|
"ignore",
|
|
682
779
|
fd,
|
|
@@ -781,7 +878,7 @@ function avdProcessLockPaths(avdDirectory, platform) {
|
|
|
781
878
|
}
|
|
782
879
|
function readAvdProcessId(path) {
|
|
783
880
|
try {
|
|
784
|
-
const pid = Number(readFileSync(path, "utf8").split("\0")[0]);
|
|
881
|
+
const pid = Number(readFileSync$1(path, "utf8").split("\0")[0]);
|
|
785
882
|
if (!Number.isSafeInteger(pid) || pid <= 0) throw new Error(`Could not read the emulator PID from process lock ${path}.`);
|
|
786
883
|
return pid;
|
|
787
884
|
} catch (error) {
|
|
@@ -824,7 +921,7 @@ function emulatorCrashHandlerPids(qemuPid) {
|
|
|
824
921
|
], { timeoutMs: 1e4 });
|
|
825
922
|
return String(out ?? "").split(/\r?\n/).map((line) => Number(line.trim())).filter((pid) => Number.isSafeInteger(pid) && pid > 0);
|
|
826
923
|
}
|
|
827
|
-
function waitForAndroidEmulatorShutdown(avdName, shutdown, { timeoutMs = ANDROID_EMULATOR_SHUTDOWN_TIMEOUT_MS, pollMs = ANDROID_EMULATOR_SHUTDOWN_POLL_MS, platform = process.platform, resolveDirectory = ownedAvdDirectory, readProcessId = readAvdProcessId, processAlive = pidExists, directoryExists = (path) => statSync(path).isDirectory(), crashHandlerPids = emulatorCrashHandlerPids, killCrashHandler = (pid) => signalProcessTree(pid, "SIGKILL", { platform }), now = Date.now, sleep = sleepSync } = {}) {
|
|
924
|
+
function waitForAndroidEmulatorShutdown(avdName, shutdown, { timeoutMs = ANDROID_EMULATOR_SHUTDOWN_TIMEOUT_MS, pollMs = ANDROID_EMULATOR_SHUTDOWN_POLL_MS, platform = process.platform, resolveDirectory = ownedAvdDirectory, readProcessId = readAvdProcessId, processAlive = pidExists, directoryExists = (path) => statSync$1(path).isDirectory(), crashHandlerPids = emulatorCrashHandlerPids, killCrashHandler = (pid) => signalProcessTree(pid, "SIGKILL", { platform }), now = Date.now, sleep = sleepSync } = {}) {
|
|
828
925
|
const { directory, processId } = resolveAvdProcess(avdName, {
|
|
829
926
|
platform,
|
|
830
927
|
resolveDirectory,
|
|
@@ -852,20 +949,52 @@ function waitForAndroidEmulatorShutdown(avdName, shutdown, { timeoutMs = ANDROID
|
|
|
852
949
|
}
|
|
853
950
|
if (!directoryExists(directory)) throw new Error(`Could not verify the content directory for owned AVD ${avdName} after shutdown.`);
|
|
854
951
|
}
|
|
855
|
-
|
|
856
|
-
|
|
952
|
+
const EMU_AVD_NAME_TIMEOUT_MS = 1e4;
|
|
953
|
+
function getAvdNameForSerial(serial, { timeoutMs = EMU_AVD_NAME_TIMEOUT_MS } = {}) {
|
|
954
|
+
const out = getExecutor().runQuiet(`${androidTool("adb")} -s ${serial} emu avd name`, {
|
|
955
|
+
timeoutMs: Math.min(timeoutMs, EMU_AVD_NAME_TIMEOUT_MS),
|
|
956
|
+
killSignal: "SIGKILL"
|
|
957
|
+
});
|
|
857
958
|
if (!out) return null;
|
|
858
959
|
return out.split("\n")[0]?.trim() || null;
|
|
859
960
|
}
|
|
860
|
-
function resolveOwnedAvdSerial(avdName,
|
|
961
|
+
function resolveOwnedAvdSerial(avdName, opts = {}) {
|
|
962
|
+
return ownedAvdSerialResolver(opts)(avdName);
|
|
963
|
+
}
|
|
964
|
+
function once(read) {
|
|
965
|
+
let result;
|
|
966
|
+
return () => {
|
|
967
|
+
if (!result) try {
|
|
968
|
+
result = { value: read() };
|
|
969
|
+
} catch (error) {
|
|
970
|
+
result = { error };
|
|
971
|
+
}
|
|
972
|
+
if ("error" in result) throw result.error;
|
|
973
|
+
return result.value;
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* `resolveOwnedAvdSerial` for several AVDs against one reading of the AVD list, `adb devices`, and
|
|
978
|
+
* each emulator's AVD name. The timeout budget starts when the resolver is created.
|
|
979
|
+
*/
|
|
980
|
+
function ownedAvdSerialResolver({ timeoutMs } = {}) {
|
|
861
981
|
const started = Date.now();
|
|
862
982
|
const remaining = () => ({ timeoutMs: timeoutMs === void 0 ? void 0 : Math.max(1, timeoutMs - (Date.now() - started)) });
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
const
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
|
|
983
|
+
const avds = once(() => listAvds(remaining()));
|
|
984
|
+
const adb = once(() => listAdbDevices(remaining()));
|
|
985
|
+
const names = /* @__PURE__ */ new Map();
|
|
986
|
+
const avdNameOf = (serial) => {
|
|
987
|
+
if (!names.has(serial)) names.set(serial, getAvdNameForSerial(serial, remaining()));
|
|
988
|
+
return names.get(serial);
|
|
989
|
+
};
|
|
990
|
+
return (avdName) => {
|
|
991
|
+
if (!avds().includes(avdName)) return { missing: true };
|
|
992
|
+
if (!isStimOwnedAvdName(avdName)) return { notOwned: true };
|
|
993
|
+
const devices = adb();
|
|
994
|
+
const match = [...devices.emulators, ...devices.unhealthy.filter((entry) => entry.kind === "emulator" && entry.consolePort !== void 0)].find((e) => avdNameOf(e.serial) === avdName);
|
|
995
|
+
if (match) return { serial: match.serial };
|
|
996
|
+
return { notRunning: true };
|
|
997
|
+
};
|
|
869
998
|
}
|
|
870
999
|
//#endregion
|
|
871
|
-
export {
|
|
1000
|
+
export { ownedAvdDirectory as A, resolvePhysicalDevice as B, isStimOwnedAvdName as C, listOrphanedAvdDirectories as D, listInstalledSystemImages as E, physicalDeviceModel as F, wipeAvdUserData as G, sleepSync as H, pickDefaultSystemImage as I, recordCreatedDevice as J, isStimOwnedSim as K, probeEmulatorSerial as L, ownedAvdName as M, ownedAvdSerialResolver as N, memoizeEmulatorProbe as O, ownedAvdSystemImage as P, resetAdoptedAvd as R, hostSystemImageArch as S, listAvds as T, waitForAndroidEmulatorShutdown as U, shutdownAndroidEmulator as V, waitForBoot as W, emulatorDiskSpaceRemedy as _, androidPoolNoCandidatesRefusal as a, findBuildTool as b, androidToolPath as c, avdPoolConfiguration as d, avdStorageRoots as f, deleteAvd as g, createOwnedAvd as h, androidPoolCandidates as i, ownedAvdMatchesConfiguration as j, nextConsolePort as k, assertOwnedAvdStopped as l, configureNewOwnedAvd as m, androidDeviceAbi as n, androidSystemImageAbi as o, bootAndroidEmulator as p, forgetCreatedDevice as q, androidHome as r, androidToolCwd as s, androidBuildToolName as t, avdPathExists as u, emulatorFailureRemedy as v, listAdbDevices as w, getAvdNameForSerial as x, extractEmulatorFailure as y, resolveOwnedAvdSerial as z };
|