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
@@ -1,528 +1,84 @@
1
- import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
- import { C as withConfigLock, E as ownership_claim_exports, I as deviceSlotPlatforms, N as assignSlotDevice, R as projectDeviceSlots, T as acquireAvdClaim, d as getConfigPath, g as loadConfig, s as ensureConfig, u as getConfigDir, y as saveConfig, z as removeSlotDevice } from "./config-B7iyKWkL.mjs";
3
- import { r as createNdjsonWriter } from "./ndjson-BZwI1NBv.mjs";
4
- import { _ as workspaceLogsDir } from "./workspace-state-BT5E3SA2.mjs";
5
- import { l as resolvePackageJson } from "./project-DDZWf_16.mjs";
6
- import { n as stripAnsi } from "./process-output-Dl5uKE03.mjs";
7
- import { I as resolveOwnedAvdSerial, O as ownedAvdDirectory, R as shutdownAndroidEmulator, T as listOrphanedAvdDirectories, c as assertOwnedAvdStopped, d as avdStorageRoots, h as deleteAvd, j as ownedAvdSystemImage, k as ownedAvdMatchesConfiguration, l as avdPathExists, z as waitForAndroidEmulatorShutdown } from "./android-DWssqHQ8.mjs";
8
- import { l as phaseLine } from "./command-output-B3ycpsYv.mjs";
9
- import { n as expoBinFromPackage, r as expoBinPath, s as findBinUpward } from "./server-expo-BVAA1OqJ.mjs";
10
- import { C as shutdownIosSim, _ as renameIosSim, a as deleteIosSim, d as listIosDeviceTypes, g as parseRuntimeVersion, h as parkedSimName, o as deleteParkedIosSim, r as clearAppDataContainer, s as findAppDataContainer, x as resolveOwnedIosSim } from "./ios-BcCiI4TS.mjs";
11
- import { createRequire } from "node:module";
12
- import { existsSync, lstatSync, readFileSync, renameSync, rmSync } from "node:fs";
13
- import { randomUUID } from "node:crypto";
14
- import { dirname, join } from "node:path";
15
- import { pathToFileURL } from "node:url";
16
- import { format, isDeepStrictEqual } from "node:util";
17
- import { AsyncLocalStorage } from "node:async_hooks";
18
- //#region src/engine/remote-cache.ts
19
- const EAS_PROVIDER_PACKAGE = "eas-build-cache-provider";
20
- const RESOLVE_TIMEOUT_MS = 3e4;
21
- const UPLOAD_TIMEOUT_MS = 6e4;
22
- const CONFIG_TIMEOUT_MS = 3e4;
23
- const DYNAMIC_CONFIG_FILES = [
24
- "app.config.ts",
25
- "app.config.js",
26
- "app.config.mjs",
27
- "app.config.cjs"
28
- ];
29
- const TIMED_OUT = Symbol("timed-out");
30
- function dynamicConfigFile(root) {
31
- for (const name of DYNAMIC_CONFIG_FILES) if (existsSync(join(root, name))) return name;
32
- return null;
33
- }
34
- function providerFromConfig(config) {
35
- if (!config || typeof config !== "object") return null;
36
- const expoField = config.expo;
37
- const exp = expoField && typeof expoField === "object" ? expoField : config;
38
- return exp.buildCacheProvider ?? exp.experiments?.buildCacheProvider ?? null;
39
- }
40
- function ownerFromConfig(config) {
41
- if (!config || typeof config !== "object") return null;
42
- const expoField = config.expo;
43
- const owner = (expoField && typeof expoField === "object" ? expoField : config).owner;
44
- return typeof owner === "string" && owner.trim() !== "" ? owner.trim() : null;
45
- }
46
- function normalizeProvider(raw) {
47
- if (raw === null || raw === void 0 || raw === "") return null;
48
- if (raw === "eas") return {
49
- name: "eas",
50
- reference: EAS_PROVIDER_PACKAGE,
51
- options: {}
52
- };
53
- if (typeof raw === "object") {
54
- const obj = raw;
55
- if (typeof obj.plugin === "string" && obj.plugin.trim() !== "") {
56
- const reference = obj.plugin.trim();
57
- return {
58
- name: reference,
59
- reference,
60
- options: obj.options && typeof obj.options === "object" ? obj.options : {}
61
- };
62
- }
63
- }
64
- return { invalid: typeof raw === "string" ? `"${raw}"` : JSON.stringify(raw) };
65
- }
66
- function runOptionsFor(platform) {
67
- return platform === "android" ? { variant: "debug" } : { configuration: "Debug" };
68
- }
69
- function isProviderPlugin(plugin) {
70
- if (!plugin || typeof plugin !== "object") return false;
71
- const p = plugin;
72
- const resolves = typeof p.resolveBuildCache === "function" || typeof p.resolveRemoteBuildCache === "function";
73
- const uploads = typeof p.uploadBuildCache === "function" || typeof p.uploadRemoteBuildCache === "function";
74
- return resolves && uploads;
75
- }
76
- function cacheLevel(value) {
77
- return value === "local" || value === "remote" ? value : false;
78
- }
79
- function exitAfterFlush(code = 0, { exit = (c) => process.exit(c), stream = process.stdout } = {}) {
80
- try {
81
- stream.write("", () => exit(code));
82
- } catch {
83
- exit(code);
84
- }
85
- }
86
- function readProjectConfig(root, { run = null, timeoutMs = CONFIG_TIMEOUT_MS } = {}) {
87
- const dynamic = dynamicConfigFile(root);
88
- if (!dynamic) {
89
- const file = join(root, "app.json");
90
- if (!existsSync(file)) return {
91
- config: null,
92
- source: null
93
- };
94
- try {
95
- return {
96
- config: JSON.parse(readFileSync(file, "utf-8")),
97
- source: "app.json"
98
- };
99
- } catch (err) {
100
- return { unavailable: `app.json could not be parsed: ${firstLine(err)}` };
101
- }
102
- }
103
- const bin = expoBinPath(root);
104
- if (!bin) return { unavailable: `${dynamic} is code, so it has to be evaluated to read its buildCacheProvider, and the \`expo\` package is not resolvable from this project (no expo/package.json to read a bin from, and no node_modules/.bin/expo here or in any parent)` };
105
- const exec = run || ((file, args, opts) => getExecutor().runFile(file, args, opts));
106
- let stdout;
107
- try {
108
- stdout = exec(bin, [
109
- "config",
110
- "--json",
111
- root
112
- ], { timeoutMs });
113
- } catch (err) {
114
- return { unavailable: `\`expo config --json\` failed: ${firstLine(err)}` };
115
- }
116
- try {
117
- return {
118
- config: JSON.parse(String(stdout)),
119
- source: dynamic
120
- };
121
- } catch (err) {
122
- return { unavailable: `\`expo config --json\` did not print JSON: ${firstLine(err)}` };
123
- }
124
- }
125
- async function loadPlugin(projectRoot, reference, { requireFrom = null } = {}) {
126
- const localRequire = requireFrom ? requireFrom(projectRoot) : createRequire(join(projectRoot, "package.json"));
127
- const file = localRequire.resolve(reference);
128
- let mod;
129
- try {
130
- mod = localRequire(file);
131
- } catch (err) {
132
- if (err?.code !== "ERR_REQUIRE_ESM") throw err;
133
- mod = await import(pathToFileURL(file).href);
134
- }
135
- const plugin = mod?.default ?? mod;
136
- if (!isProviderPlugin(plugin)) throw new Error(`"${reference}" does not export resolveBuildCache and uploadBuildCache functions`);
137
- return plugin;
138
- }
139
- async function loadProjectProvider(projectRoot, { isExpo = true, run = null, requireFrom = null, timeoutMs = CONFIG_TIMEOUT_MS } = {}) {
140
- if (!isExpo) return { none: true };
141
- const read = readProjectConfig(projectRoot, {
142
- run,
143
- timeoutMs
144
- });
145
- if (read.unavailable) return { unavailable: read.unavailable };
146
- const normalized = normalizeProvider(providerFromConfig(read.config));
147
- if (!normalized) return { none: true };
148
- if (normalized.invalid) return { unavailable: `buildCacheProvider is ${normalized.invalid}, which is not "eas" or { plugin: <module> }` };
149
- if (normalized.reference === "@stim-cli/expo-build-cache") return { none: true };
150
- let plugin;
151
- try {
152
- plugin = await loadPlugin(projectRoot, normalized.reference, { requireFrom });
153
- } catch (err) {
154
- const missing = err?.code === "MODULE_NOT_FOUND";
155
- return { unavailable: normalized.name === "eas" && missing ? `the EAS build cache needs the \`${EAS_PROVIDER_PACKAGE}\` package, which is not installed in this project` : `${normalized.name} could not be loaded: ${firstLine(err)}` };
156
- }
157
- return {
158
- provider: {
159
- plugin,
160
- options: normalized.options ?? {}
161
- },
162
- name: normalized.name,
163
- owner: ownerFromConfig(read.config)
164
- };
165
- }
166
- const WHOAMI_TIMEOUT_MS = 15e3;
167
- const EAS_CLI_PACKAGE = "eas-cli";
168
- const EAS_CLI_BIN = "eas";
169
- function parseWhoami({ stdout = "", stderr = "", exitCode = 0 } = {}) {
170
- const out = stripAnsi(stdout);
171
- const combined = `${out}\n${stripAnsi(stderr)}`;
172
- if (/^\s*Not logged in\s*$/m.test(combined)) return { loggedOut: true };
173
- if (exitCode !== 0) return { unknown: firstLine(combined.trim() || `eas whoami exited ${exitCode}`) };
174
- const lines = out.split("\n").map((line) => line.trim()).filter((line) => line !== "");
175
- if (lines.length === 0) return { unknown: "eas whoami printed nothing" };
176
- const raw = lines[0];
177
- if (raw === void 0) return { unknown: "eas whoami printed nothing" };
178
- const viaToken = /\(authenticated using EXPO_TOKEN\)\s*$/.test(raw);
179
- const display = raw.replace(/\s*\(authenticated using EXPO_TOKEN\)\s*$/, "").trim();
180
- const accounts = [];
181
- for (const line of lines) {
182
- const name = /^\u2022\s*(\S+)\s*\(Role:/.exec(line)?.[1];
183
- if (name !== void 0) accounts.push(name);
184
- }
185
- if (accounts.length === 0 && !display.endsWith("(robot)") && display !== "robot") accounts.push(display);
186
- return {
187
- loggedIn: true,
188
- account: display,
189
- accounts: accounts.length ? accounts : null,
190
- viaToken
191
- };
192
- }
193
- function isEasAuthFailureText(text) {
194
- const t = stripAnsi(text || "");
195
- if (t.trim() === "") return false;
196
- return /not logged in/i.test(t) || /\beas login\b/i.test(t) || /EXPO_TOKEN/.test(t) || /unauthorized/i.test(t) || /not authorized/i.test(t) || /authentication (failed|required)/i.test(t);
197
- }
198
- function easAuthNote(status) {
199
- const { code, account, owner, reason, phase = "resolve" } = status || {};
200
- const because = reason ? ` (${reason})` : "";
201
- if (code === "logged-out") return `eas is not authenticated${because} -- run \`eas login\` (or set EXPO_TOKEN); ` + (phase === "upload" ? "this build stayed in the local cache" : "building with the local cache only");
202
- if (code === "wrong-account") return `eas is authenticated as ${account}, and this project's owner is ${owner} -- run \`eas login\` as a member of ${owner} (or set EXPO_TOKEN); consulting the EAS cache anyway`;
203
- return null;
204
- }
205
- function resolveEasCliBin(projectRoot, { lookupPath = null } = {}) {
206
- const fromPackage = expoBinFromPackage(resolvePackageJson(projectRoot, EAS_CLI_PACKAGE), EAS_CLI_BIN);
207
- if (fromPackage) return {
208
- file: fromPackage,
209
- source: "project"
210
- };
211
- const shim = findBinUpward(projectRoot, EAS_CLI_BIN);
212
- if (shim) return {
213
- file: shim,
214
- source: "project"
215
- };
216
- const onPath = lookupPath ? lookupPath() : getExecutor().findExecutable(EAS_CLI_BIN);
217
- const file = (String(onPath || "").split("\n")[0] ?? "").trim();
218
- return file ? {
219
- file,
220
- source: "path"
221
- } : null;
222
- }
223
- const easAuthCache = /* @__PURE__ */ new Map();
224
- function checkEasAuth({ projectRoot, owner = null, run = null, resolveBin = null, timeoutMs = WHOAMI_TIMEOUT_MS, cache = easAuthCache }) {
225
- const key = `${projectRoot}::${owner || ""}`;
226
- if (cache?.has(key)) return cache.get(key);
227
- const answer = probeEasAuth({
228
- projectRoot,
229
- owner,
230
- run,
231
- resolveBin,
232
- timeoutMs
233
- });
234
- cache?.set(key, answer);
235
- return answer;
236
- }
237
- function probeEasAuth({ projectRoot, owner, run, resolveBin, timeoutMs }) {
238
- const bin = (resolveBin || resolveEasCliBin)(projectRoot);
239
- if (!bin) return {
240
- failed: true,
241
- code: "no-cli",
242
- reason: "no `eas` executable is resolvable from this project or on PATH",
243
- remedy: "Install eas-cli (`npm i -g eas-cli`, or as a project devDependency), then run `eas login`."
244
- };
245
- const exec = run || ((file, args, opts) => getExecutor().runFile(file, args, opts));
246
- let result;
247
- try {
248
- result = {
249
- stdout: String(exec(bin.file, ["whoami"], { timeoutMs }) ?? ""),
250
- exitCode: 0
251
- };
252
- } catch (e) {
253
- const err = e;
254
- if (err?.code === "ETIMEDOUT" || err?.signal === "SIGTERM" || err?.killed) return { unknown: `eas whoami timed out after ${timeoutMs}ms` };
255
- result = {
256
- stdout: String(err?.stdout ?? ""),
257
- stderr: String(err?.stderr ?? err?.message ?? ""),
258
- exitCode: typeof err?.status === "number" ? err.status : 1
259
- };
260
- }
261
- const parsed = parseWhoami(result);
262
- if (parsed.loggedOut) return {
263
- failed: true,
264
- code: "logged-out",
265
- reason: "eas whoami says \"Not logged in\"",
266
- remedy: "Run `eas login` (or set EXPO_TOKEN)."
267
- };
268
- if (parsed.unknown) return { unknown: parsed.unknown };
269
- if (owner && Array.isArray(parsed.accounts) && !parsed.accounts.includes(owner)) return {
270
- failed: true,
271
- code: "wrong-account",
272
- account: parsed.account,
273
- accounts: parsed.accounts,
274
- owner,
275
- reason: `eas is authenticated as ${parsed.account} (accounts: ${parsed.accounts.join(", ")}), which does not include this project's owner ${owner}`,
276
- remedy: `Run \`eas login\` as a member of ${owner}, or set EXPO_TOKEN to a token for it.`
277
- };
278
- return {
279
- ok: true,
280
- account: parsed.account,
281
- accounts: parsed.accounts,
282
- viaToken: parsed.viaToken,
283
- source: bin.source
284
- };
285
- }
286
- const capturing = [];
287
- const abandoned = /* @__PURE__ */ new Set();
288
- let patched = null;
289
- const PROVIDER_CONTEXT = new AsyncLocalStorage();
290
- function uploadDestination(lines) {
291
- const text = (lines || []).map(stripAnsi);
292
- for (const line of text) {
293
- const url = /(https?:\/\/[^\s'"]+)/.exec(line)?.[1];
294
- if (url !== void 0) return url.replace(/[.,)\]]+$/, "");
295
- }
296
- for (const line of text) {
297
- const slug = /\bto\s+(@?[\w.-]+\/[\w.-]+)/i.exec(line)?.[1];
298
- if (slug !== void 0) return slug.replace(/[.,)\]]+$/, "");
299
- }
300
- return null;
301
- }
302
- function providerNote(text) {
303
- return `${"cache".padEnd(11)} ${text}`;
304
- }
305
- function currentFrame() {
306
- const store = PROVIDER_CONTEXT.getStore();
307
- return store && (capturing.includes(store) || abandoned.has(store)) ? store : null;
308
- }
309
- function install() {
310
- if (patched && process.stdout.write === patched.patchedWrite) return;
311
- const stdout = process.stdout;
312
- const write = stdout.write.bind(stdout);
313
- const log = console.log;
314
- const passthrough = write;
315
- const patchedWrite = ((chunk, encoding, callback) => {
316
- const frame = currentFrame();
317
- if (!frame) return passthrough(chunk, encoding, callback);
318
- const cb = typeof encoding === "function" ? encoding : callback;
319
- absorb(frame, typeof chunk === "string" ? chunk : String(chunk));
320
- if (typeof cb === "function") cb();
321
- return true;
1
+ import { D as process_identity_exports, H as projectDeviceSlots, I as assignSlotDevice, O as acquireAvdClaim, T as withConfigLock, U as removeSlotDevice, d as getConfigPath, g as loadConfig, k as ownership_claim_exports, s as ensureConfig, u as getConfigDir, x as saveConfig, z as deviceSlotPlatforms } from "./config-BEq0Qw_a.mjs";
2
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
+ import { c as signalProcessTree, i as pidExists } from "./metro-BWLOJ_Wr.mjs";
4
+ import { r as workspaceProcessLockPath, t as withWorkspaceProcessLock } from "./workspace-process-lock-BXA96myD.mjs";
5
+ import { t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
6
+ import { l as phaseLine } from "./command-output-D5z2_bcF.mjs";
7
+ import { A as ownedAvdDirectory, C as isStimOwnedAvdName, D as listOrphanedAvdDirectories, G as wipeAvdUserData, H as sleepSync, P as ownedAvdSystemImage, U as waitForAndroidEmulatorShutdown, V as shutdownAndroidEmulator, f as avdStorageRoots, g as deleteAvd, j as ownedAvdMatchesConfiguration, l as assertOwnedAvdStopped, u as avdPathExists, z as resolveOwnedAvdSerial } from "./android-787HhP5w.mjs";
8
+ import { S as resolveOwnedIosSim, a as deleteParkedIosSim, d as listIosDeviceTypes, g as parseRuntimeVersion, h as parkedSimName, i as deleteIosSim, o as eraseIosSim, v as renameIosSim, w as shutdownIosSim } from "./ios-B2Fpqvlb.mjs";
9
+ import { closeSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, unlinkSync } from "node:fs";
10
+ import { basename, dirname, join, resolve, sep } from "node:path";
11
+ import { createHash, randomUUID } from "node:crypto";
12
+ import { isDeepStrictEqual } from "node:util";
13
+ import { resolve4 } from "node:dns/promises";
14
+ import { request } from "node:https";
15
+ //#region src/devices/agent-device-cleanup.ts
16
+ function parseAgentDeviceSessions(output) {
17
+ const result = JSON.parse(output);
18
+ if (result?.success !== true || !Array.isArray(result.data?.sessions)) throw new Error("agent-device did not return a successful session list");
19
+ return result.data.sessions.flatMap((entry) => {
20
+ if (!entry || typeof entry !== "object") return [];
21
+ const row = entry;
22
+ const id = row.platform === "ios" ? row.device_udid : row.id;
23
+ if (row.platform !== "ios" && row.platform !== "android" || typeof row.name !== "string" || !row.name.trim() || typeof id !== "string" || !id || typeof row.createdAt !== "number" || !Number.isFinite(row.createdAt) || row.platform === "ios" && row.id !== void 0 && row.id !== id) return [];
24
+ return [{
25
+ name: row.name,
26
+ platform: row.platform,
27
+ id,
28
+ createdAt: row.createdAt
29
+ }];
322
30
  });
323
- const patchedLog = (...args) => {
324
- const frame = currentFrame();
325
- if (!frame) return log(...args);
326
- absorb(frame, `${format(...args)}\n`);
327
- };
328
- stdout.write = patchedWrite;
329
- console.log = patchedLog;
330
- patched = {
331
- stdout,
332
- write,
333
- log,
334
- patchedWrite,
335
- patchedLog
336
- };
337
- }
338
- function uninstall() {
339
- if (!patched || capturing.length || abandoned.size) return;
340
- if (patched.stdout.write === patched.patchedWrite) patched.stdout.write = patched.write;
341
- if (console.log === patched.patchedLog) console.log = patched.log;
342
- patched = null;
343
31
  }
344
- function absorb(frame, chunk) {
345
- try {
346
- process.stderr.write(chunk);
347
- } catch {}
348
- frame.pending += chunk;
349
- let index = frame.pending.indexOf("\n");
350
- while (index !== -1) {
351
- emitLine(frame, frame.pending.slice(0, index));
352
- frame.pending = frame.pending.slice(index + 1);
353
- index = frame.pending.indexOf("\n");
354
- }
32
+ function report(message) {
33
+ process.stderr.write(`${phaseLine("device", message)}\n`);
355
34
  }
356
- function emitLine(frame, raw) {
357
- const line = stripAnsi(raw).trim();
358
- if (line === "") return;
359
- frame.lines.push(line);
360
- const writer = frame.writer();
361
- try {
362
- writer?.write({
363
- src: "build",
364
- level: "debug",
365
- event: "provider",
366
- msg: line
35
+ function closeOwnedDeviceSessions(device, stillOwned) {
36
+ const exec = getExecutor();
37
+ const deadline = Date.now() + 15e3;
38
+ const run = (args) => {
39
+ const remaining = deadline - Date.now();
40
+ if (remaining <= 0) throw new Error("agent-device cleanup timed out");
41
+ return exec.runFile("agent-device", [
42
+ ...args,
43
+ "--json",
44
+ "--daemon-transport",
45
+ "socket"
46
+ ], {
47
+ timeoutMs: Math.min(5e3, remaining),
48
+ killSignal: "SIGKILL"
367
49
  });
368
- } catch {}
369
- frame.onLine?.(line);
370
- }
371
- function beginCapture({ logWriter = null, projectRoot = null, platform = null, onLine = null } = {}) {
372
- let own = null;
373
- const frame = {
374
- lines: [],
375
- pending: "",
376
- onLine,
377
- writer: () => {
378
- if (logWriter) return logWriter;
379
- if (own) return own;
380
- if (!projectRoot || !platform) return null;
381
- own = createNdjsonWriter(join(workspaceLogsDir(projectRoot), `build-${platform}.ndjson`));
382
- return own;
383
- },
384
- close: () => {
385
- own?.close?.();
386
- own = null;
387
- }
388
50
  };
389
- capturing.push(frame);
390
- install();
391
- return frame;
392
- }
393
- function endCapture(frame) {
394
- if (!frame) return;
395
- const at = capturing.lastIndexOf(frame);
396
- if (at !== -1) capturing.splice(at, 1);
397
- abandoned.delete(frame);
398
- if (frame.pending !== "") {
399
- const rest = frame.pending;
400
- frame.pending = "";
401
- emitLine(frame, rest);
402
- }
403
- frame.close();
404
- uninstall();
405
- }
406
- function abandonCapture(frame, work) {
407
- if (!frame) return;
408
- const at = capturing.lastIndexOf(frame);
409
- if (at !== -1) capturing.splice(at, 1);
410
- abandoned.add(frame);
411
- Promise.resolve(work).then(() => endCapture(frame), () => endCapture(frame));
412
- }
413
- async function resolveRemote({ provider, platform, projectRoot, fingerprintHash, runOptions = null, timeoutMs = RESOLVE_TIMEOUT_MS, logWriter = null } = {}) {
414
- if (!provider?.plugin || !fingerprintHash) return null;
415
- const opts = runOptions || runOptionsFor(platform);
416
- const outcome = await withBudget(async () => {
417
- const props = {
418
- fingerprintHash: await providerFingerprint({
419
- provider,
420
- platform,
421
- projectRoot,
422
- runOptions: opts
423
- }) ?? fingerprintHash,
424
- platform,
425
- runOptions: opts,
426
- projectRoot
427
- };
428
- return typeof provider.plugin.resolveBuildCache === "function" ? provider.plugin.resolveBuildCache(props, provider.options) : provider.plugin.resolveRemoteBuildCache(props, provider.options);
429
- }, timeoutMs, {
430
- logWriter,
431
- projectRoot,
432
- platform
433
- });
434
- if (outcome.timedOut) return { timedOut: true };
435
- if (outcome.error) return { failed: firstLine(outcome.error) };
436
- const appPath = typeof outcome.value === "string" ? outcome.value.trim() : "";
437
- if (!appPath) return null;
438
- if (!existsSync(appPath)) return { failed: `returned ${appPath}, which does not exist` };
439
- return { appPath };
440
- }
441
- async function uploadRemote({ provider, platform, projectRoot, fingerprintHash, buildPath, runOptions = null, timeoutMs = UPLOAD_TIMEOUT_MS, logWriter = null, note = (line) => console.error(line) } = {}) {
442
- if (!provider?.plugin || !fingerprintHash || !buildPath) return { skipped: true };
443
- const opts = runOptions || runOptionsFor(platform);
444
- let announced = false;
445
- const onLine = (line) => {
446
- if (announced) return;
447
- const dest = uploadDestination([line]);
448
- if (!dest) return;
449
- announced = true;
450
- note(providerNote(`uploading to ${dest}`));
451
- };
452
- const outcome = await withBudget(async () => {
453
- const props = {
454
- projectRoot,
455
- platform,
456
- fingerprintHash: await providerFingerprint({
457
- provider,
458
- platform,
459
- projectRoot,
460
- runOptions: opts
461
- }) ?? fingerprintHash,
462
- buildPath,
463
- runOptions: opts
464
- };
465
- return typeof provider.plugin.uploadBuildCache === "function" ? provider.plugin.uploadBuildCache(props, provider.options) : provider.plugin.uploadRemoteBuildCache(props, provider.options);
466
- }, timeoutMs, {
467
- logWriter,
468
- projectRoot,
469
- platform,
470
- onLine
471
- });
472
- if (outcome.timedOut) return { timedOut: true };
473
- if (outcome.error) return { failed: firstLine(outcome.error) };
474
- const destination = uploadDestination(outcome.lines);
475
- return destination ? {
476
- uploaded: true,
477
- destination
478
- } : { uploaded: true };
479
- }
480
- async function providerFingerprint({ provider, platform, projectRoot, runOptions }) {
481
- if (typeof provider.plugin.calculateFingerprintHash !== "function") return null;
482
- try {
483
- const hash = await provider.plugin.calculateFingerprintHash({
484
- projectRoot,
485
- platform,
486
- runOptions
487
- }, provider.options);
488
- return typeof hash === "string" && hash.trim() !== "" ? hash.trim() : null;
489
- } catch {
490
- return null;
491
- }
492
- }
493
- async function withBudget(factory, ms, capture) {
494
- let timer = null;
495
- const timeout = new Promise((resolve) => {
496
- timer = setTimeout(() => resolve(TIMED_OUT), ms);
497
- timer?.unref?.();
498
- });
499
- const frame = beginCapture(capture || {});
500
- const work = Promise.resolve().then(() => PROVIDER_CONTEXT.run(frame, factory)).then((value) => ({ value }), (error) => ({ error }));
51
+ const list = () => parseAgentDeviceSessions(run([
52
+ "session",
53
+ "list",
54
+ "--session",
55
+ "stim-teardown-inventory"
56
+ ]));
501
57
  try {
502
- const settled = await Promise.race([work, timeout]);
503
- if (settled === TIMED_OUT) {
504
- abandonCapture(frame, work);
505
- return {
506
- timedOut: true,
507
- lines: frame.lines
508
- };
58
+ if (!exec.findExecutable("agent-device")) return;
59
+ const sessions = list().filter((session) => session.platform === device.platform && session.id === device.id);
60
+ for (const session of sessions) try {
61
+ if (!list().some((current) => current.name === session.name && current.platform === session.platform && current.id === session.id && current.createdAt === session.createdAt)) continue;
62
+ if (!stillOwned()) return;
63
+ if (JSON.parse(run([
64
+ "close",
65
+ "--session",
66
+ session.name,
67
+ "--session-lock",
68
+ "reject",
69
+ "--platform",
70
+ device.platform,
71
+ device.platform === "ios" ? "--udid" : "--serial",
72
+ device.id
73
+ ]))?.success !== true) throw new Error("agent-device did not confirm session close");
74
+ report(`closed agent-device session ${session.name} on ${device.id}`);
75
+ } catch (error) {
76
+ report(`could not close agent-device session ${session.name} on ${device.id}: ${error.message}`);
509
77
  }
510
- endCapture(frame);
511
- return {
512
- ...settled,
513
- lines: frame.lines
514
- };
515
- } catch (err) {
516
- endCapture(frame);
517
- throw err;
518
- } finally {
519
- if (timer) clearTimeout(timer);
78
+ } catch (error) {
79
+ report(`could not list agent-device sessions for ${device.id}: ${error.message}`);
520
80
  }
521
81
  }
522
- function firstLine(err) {
523
- const line = (String(err?.message || err || "unknown error").trim().split("\n")[0] ?? "").trim();
524
- return line.length > 200 ? `${line.slice(0, 197)}...` : line;
525
- }
526
82
  const POOL_SETTING_REMEDY = "Run `stim guide settings` for the device pool bounds and where it can be set.";
527
83
  const MAX_SETTING = {
528
84
  ios: {
@@ -720,74 +276,6 @@ function dropParked(platform, udid) {
720
276
  return removeParkedAfter(platform, udid, () => {}) !== null;
721
277
  }
722
278
  //#endregion
723
- //#region src/devices/agent-device-cleanup.ts
724
- function parseAgentDeviceSessions(output) {
725
- const result = JSON.parse(output);
726
- if (result?.success !== true || !Array.isArray(result.data?.sessions)) throw new Error("agent-device did not return a successful session list");
727
- return result.data.sessions.flatMap((entry) => {
728
- if (!entry || typeof entry !== "object") return [];
729
- const row = entry;
730
- const id = row.platform === "ios" ? row.device_udid : row.id;
731
- if (row.platform !== "ios" && row.platform !== "android" || typeof row.name !== "string" || !row.name.trim() || typeof id !== "string" || !id || typeof row.createdAt !== "number" || !Number.isFinite(row.createdAt) || row.platform === "ios" && row.id !== void 0 && row.id !== id) return [];
732
- return [{
733
- name: row.name,
734
- platform: row.platform,
735
- id,
736
- createdAt: row.createdAt
737
- }];
738
- });
739
- }
740
- function report(message) {
741
- process.stderr.write(`${phaseLine("device", message)}\n`);
742
- }
743
- function closeOwnedDeviceSessions(device, stillOwned) {
744
- const exec = getExecutor();
745
- const deadline = Date.now() + 15e3;
746
- const run = (args) => {
747
- const remaining = deadline - Date.now();
748
- if (remaining <= 0) throw new Error("agent-device cleanup timed out");
749
- return exec.runFile("agent-device", [
750
- ...args,
751
- "--json",
752
- "--daemon-transport",
753
- "socket"
754
- ], {
755
- timeoutMs: Math.min(5e3, remaining),
756
- killSignal: "SIGKILL"
757
- });
758
- };
759
- const list = () => parseAgentDeviceSessions(run([
760
- "session",
761
- "list",
762
- "--session",
763
- "stim-teardown-inventory"
764
- ]));
765
- try {
766
- if (!exec.findExecutable("agent-device")) return;
767
- const sessions = list().filter((session) => session.platform === device.platform && session.id === device.id);
768
- for (const session of sessions) try {
769
- if (!list().some((current) => current.name === session.name && current.platform === session.platform && current.id === session.id && current.createdAt === session.createdAt)) continue;
770
- if (!stillOwned()) return;
771
- if (JSON.parse(run([
772
- "close",
773
- "--session",
774
- session.name,
775
- "--session-lock",
776
- "reject",
777
- "--platform",
778
- device.platform,
779
- device.platform === "ios" ? "--udid" : "--serial",
780
- device.id
781
- ]))?.success !== true) throw new Error("agent-device did not confirm session close");
782
- report(`closed agent-device session ${session.name} on ${device.id}`);
783
- } catch (error) {
784
- report(`could not close agent-device session ${session.name} on ${device.id}: ${error.message}`);
785
- }
786
- } catch (error) {
787
- report(`could not list agent-device sessions for ${device.id}: ${error.message}`);
788
- }
789
- }
790
- //#endregion
791
279
  //#region src/devices/teardown.ts
792
280
  function teardownParkedAvd(avdName) {
793
281
  try {
@@ -837,19 +325,33 @@ function teardownParkedIosSim(udid, { label, deleteSim = deleteParkedIosSim } =
837
325
  };
838
326
  }
839
327
  }
840
- function parkOwnedIosSim(udid, park) {
841
- const resolved = resolveOwnedIosSim(udid);
842
- if (resolved.missing) throw new Error(`simulator ${udid} disappeared after shutdown`);
843
- if (resolved.notOwned) throw new Error(`simulator ${udid} is now named ${JSON.stringify(resolved.notOwned)}`);
844
- const sim = resolved.sim;
845
- if (sim.state !== "Shutdown") throw new Error(`simulator ${udid} is still ${sim.state} after shutdown`);
328
+ const IOS_SHUTDOWN_SETTLE_MS = 15e3;
329
+ const IOS_SHUTDOWN_POLL_MS = 250;
330
+ const IOS_SHUTDOWN_ATTEMPTS = 2;
331
+ function settleIosSimShutdown(udid, { now = Date.now, sleep = sleepSync }) {
332
+ let resolved = resolveOwnedIosSim(udid);
333
+ for (let attempt = 1;; attempt++) {
334
+ const deadline = now() + IOS_SHUTDOWN_SETTLE_MS;
335
+ while (resolved.sim && resolved.sim.state !== "Shutdown" && now() < deadline) {
336
+ sleep(IOS_SHUTDOWN_POLL_MS);
337
+ resolved = resolveOwnedIosSim(udid);
338
+ }
339
+ if (!resolved.sim || resolved.sim.state === "Shutdown" || attempt === IOS_SHUTDOWN_ATTEMPTS) return resolved;
340
+ shutdownIosSim(udid);
341
+ resolved = resolveOwnedIosSim(udid);
342
+ }
343
+ }
344
+ function iosShutdownFailureReason(udid, state) {
345
+ return `simulator ${udid} is still ${state} after ${IOS_SHUTDOWN_ATTEMPTS} shutdown attempts and ${IOS_SHUTDOWN_ATTEMPTS * IOS_SHUTDOWN_SETTLE_MS / 1e3}s of waiting`;
346
+ }
347
+ function parkOwnedIosSim(settled, udid, park) {
348
+ if (settled.missing) throw new Error(`simulator ${udid} disappeared after shutdown`);
349
+ if (settled.notOwned) throw new Error(`simulator ${udid} is now named ${JSON.stringify(settled.notOwned)}`);
350
+ const sim = settled.sim;
351
+ if (sim.state !== "Shutdown") throw new Error(iosShutdownFailureReason(udid, sim.state));
846
352
  const model = listIosDeviceTypes().find((d) => d.identifier === sim.deviceTypeIdentifier)?.name ?? null;
847
353
  const runtime = parseRuntimeVersion(sim.runtime);
848
- if (park.bundleId) {
849
- if (!sim.dataPath) throw new Error(`simulator ${udid} did not report a data path for app cleanup`);
850
- const container = findAppDataContainer(sim.dataPath, park.bundleId);
851
- if (container) clearAppDataContainer(container);
852
- }
354
+ eraseIosSim(sim.udid);
853
355
  const name = parkedSimName(sim.udid, {
854
356
  model,
855
357
  runtime
@@ -861,9 +363,7 @@ function parkOwnedIosSim(udid, park) {
861
363
  deviceTypeIdentifier: sim.deviceTypeIdentifier,
862
364
  runtimeIdentifier: sim.runtime,
863
365
  parkedAt: (/* @__PURE__ */ new Date()).toISOString(),
864
- simslimManaged: Boolean(park.simslimManaged),
865
- ...park.bundleId ? { bundleId: park.bundleId } : {},
866
- ...park.cacheKey ? { cacheKey: park.cacheKey } : {}
366
+ simslimManaged: Boolean(park.simslimManaged)
867
367
  };
868
368
  return {
869
369
  record,
@@ -876,14 +376,14 @@ function parkOwnedIosSim(udid, park) {
876
376
  })
877
377
  };
878
378
  }
879
- function teardownOwnedIosSim(udid, { del = false, label, park } = {}) {
379
+ function teardownOwnedIosSim(udid, { del = false, label, park, shutdownClock = {} } = {}) {
880
380
  let parkFallback;
881
381
  try {
882
382
  const resolved = resolveOwnedIosSim(udid);
883
383
  if (resolved.notOwned) return {
884
384
  status: "skipped",
885
385
  kind: "not-owned",
886
- reason: `sim is now named "${resolved.notOwned}", not Stim-owned by name`
386
+ reason: `sim "${resolved.notOwned}" is not Stim-owned: Stim has no record of creating it`
887
387
  };
888
388
  if (resolved.missing) return { status: "missing" };
889
389
  closeOwnedDeviceSessions({
@@ -900,7 +400,7 @@ function teardownOwnedIosSim(udid, { del = false, label, park } = {}) {
900
400
  shutdownIosSim(udid);
901
401
  const sim = resolved.sim;
902
402
  if (del && park && park.max > 0) try {
903
- const { record, evicted } = parkOwnedIosSim(udid, park);
403
+ const { record, evicted } = parkOwnedIosSim(settleIosSimShutdown(udid, shutdownClock), udid, park);
904
404
  const removed = [];
905
405
  const failures = [];
906
406
  for (const entry of evicted) {
@@ -924,11 +424,23 @@ function teardownOwnedIosSim(udid, { del = false, label, park } = {}) {
924
424
  } catch (e) {
925
425
  parkFallback = String(e?.message || e);
926
426
  }
927
- if (del) deleteIosSim(udid);
427
+ if (del) {
428
+ deleteIosSim(udid);
429
+ return {
430
+ status: "torn-down",
431
+ label: label ?? sim.name ?? udid,
432
+ ...parkFallback ? { parkFallback } : {}
433
+ };
434
+ }
435
+ const settled = settleIosSimShutdown(udid, shutdownClock);
436
+ if (settled.sim && settled.sim.state !== "Shutdown") return {
437
+ status: "failed",
438
+ label: label ?? sim.name ?? udid,
439
+ reason: iosShutdownFailureReason(udid, settled.sim.state)
440
+ };
928
441
  return {
929
442
  status: "torn-down",
930
- label: label ?? sim.name ?? udid,
931
- ...parkFallback ? { parkFallback } : {}
443
+ label: label ?? sim.name ?? udid
932
444
  };
933
445
  } catch (e) {
934
446
  return {
@@ -988,13 +500,13 @@ function teardownUnregisteredAvd(avdName, del, owner, surveyed) {
988
500
  };
989
501
  }
990
502
  function teardownOwnedAvd(avdName, options = {}) {
991
- if (!/^stim-[A-Za-z0-9._-]+$/.test(avdName)) return {
992
- status: "skipped",
993
- kind: "not-owned",
994
- reason: `AVD ${avdName} is not Stim-owned by name`
995
- };
996
503
  let claim;
997
504
  try {
505
+ if (!isStimOwnedAvdName(avdName, options.orphanedDirectory ? options.orphanedDirectory.directory : void 0)) return {
506
+ status: "skipped",
507
+ kind: "not-owned",
508
+ reason: `AVD ${avdName} is not Stim-owned: Stim has no record of creating it`
509
+ };
998
510
  claim = acquireAvdClaim(avdName);
999
511
  const result = teardownClaimedAvd(avdName, claim, options);
1000
512
  if (result.status === "torn-down" || result.status === "missing") options.onRemoved?.();
@@ -1016,7 +528,7 @@ function teardownClaimedAvd(avdName, claim, { del = false, park, owner, orphaned
1016
528
  if (resolved.notOwned) return {
1017
529
  status: "skipped",
1018
530
  kind: "not-owned",
1019
- reason: `AVD ${avdName} is not Stim-owned by name`
531
+ reason: `AVD ${avdName} is not Stim-owned: Stim has no record of creating it`
1020
532
  };
1021
533
  if (resolved.missing) return teardownUnregisteredAvd(avdName, del, owner, orphanedDirectory);
1022
534
  if (onlyIfMissing) return {
@@ -1052,7 +564,7 @@ function teardownClaimedAvd(avdName, claim, { del = false, park, owner, orphaned
1052
564
  if (current.notOwned) return {
1053
565
  status: "skipped",
1054
566
  kind: "not-owned",
1055
- reason: `AVD ${avdName} is not Stim-owned by name`
567
+ reason: `AVD ${avdName} is not Stim-owned: Stim has no record of creating it`
1056
568
  };
1057
569
  if (current.missing) return teardownUnregisteredAvd(avdName, del, owner);
1058
570
  if (current.serial) throw new Error(`Owned AVD ${avdName} started again before deletion.`);
@@ -1061,6 +573,9 @@ function teardownClaimedAvd(avdName, claim, { del = false, park, owner, orphaned
1061
573
  if (park && park.max > 0) try {
1062
574
  const systemImage = ownedAvdSystemImage(avdName);
1063
575
  if (!systemImage || !park.configuration || !ownedAvdMatchesConfiguration(avdName, park.configuration)) throw new Error("the AVD has no verified creation configuration");
576
+ const directory = ownedAvdDirectory(avdName);
577
+ if (!directory) throw new Error("the AVD data directory could not be resolved");
578
+ wipeAvdUserData(directory);
1064
579
  const evicted = parkSim({
1065
580
  platform: "android",
1066
581
  projectPath: park.projectPath,
@@ -1125,4 +640,541 @@ function teardownClaimedAvd(avdName, claim, { del = false, park, owner, orphaned
1125
640
  }
1126
641
  }
1127
642
  //#endregion
1128
- export { resolveRemote as C, resolveEasCliBin as S, exitAfterFlush as _, POOL_SETTING_REMEDY as a, ownerFromConfig as b, isLegacyDeletionClaim as c, selectParked as d, RESOLVE_TIMEOUT_MS as f, easAuthNote as g, checkEasAuth as h, teardownParkedIosSim as i, parkedMaxSetting as l, cacheLevel as m, teardownOwnedIosSim as n, adoptParked as o, UPLOAD_TIMEOUT_MS as p, teardownParkedAvd as r, dropParked as s, teardownOwnedAvd as t, readParked as u, isEasAuthFailureText as v, uploadRemote as w, providerFromConfig as x, loadProjectProvider as y };
643
+ //#region src/engine/public-http-probe.ts
644
+ const DNS_TIMEOUT_MS = 5e3;
645
+ async function resolveAddresses(hostname, signal) {
646
+ if (signal.aborted) throw signal.reason;
647
+ let timer;
648
+ let onAbort;
649
+ const unavailable = new Promise((_resolve, reject) => {
650
+ onAbort = () => reject(signal.reason);
651
+ signal.addEventListener("abort", onAbort, { once: true });
652
+ if (signal.aborted) onAbort();
653
+ else timer = setTimeout(() => reject(/* @__PURE__ */ new Error("DNS lookup timed out")), DNS_TIMEOUT_MS);
654
+ });
655
+ try {
656
+ return await Promise.race([resolve4(hostname), unavailable]);
657
+ } finally {
658
+ clearTimeout(timer);
659
+ if (onAbort) signal.removeEventListener("abort", onAbort);
660
+ }
661
+ }
662
+ async function probePublicHttp(url, signal) {
663
+ try {
664
+ return (await fetch(url, {
665
+ signal,
666
+ redirect: "follow"
667
+ })).status;
668
+ } catch (error) {
669
+ const cause = error?.cause;
670
+ if (cause?.code !== "ENOTFOUND" && cause?.code !== "EAI_AGAIN") return null;
671
+ }
672
+ const hostname = new URL(url).hostname;
673
+ if (!hostname.endsWith(".trycloudflare.com")) return null;
674
+ let addresses;
675
+ try {
676
+ addresses = await resolveAddresses(hostname, signal);
677
+ } catch {
678
+ return null;
679
+ }
680
+ const firstAddress = addresses[0];
681
+ if (!firstAddress) return null;
682
+ return new Promise((resolve) => {
683
+ const request$1 = request(url, {
684
+ signal,
685
+ lookup: (_hostname, options, callback) => {
686
+ if (options.all) callback(null, addresses.map((address) => ({
687
+ address,
688
+ family: 4
689
+ })));
690
+ else callback(null, firstAddress, 4);
691
+ }
692
+ }, (response) => {
693
+ response.resume();
694
+ resolve(response.statusCode ?? null);
695
+ });
696
+ request$1.on("error", () => resolve(null));
697
+ request$1.end();
698
+ });
699
+ }
700
+ //#endregion
701
+ //#region src/engine/tunnel.ts
702
+ function tunnelArgv(provider, port, ngrokUrl, logFile) {
703
+ if (provider === "cloudflared") return {
704
+ bin: "cloudflared",
705
+ args: [
706
+ "tunnel",
707
+ "--url",
708
+ `http://127.0.0.1:${port}`,
709
+ ...logFile ? ["--logfile", logFile] : []
710
+ ]
711
+ };
712
+ return {
713
+ bin: "ngrok",
714
+ args: [
715
+ "http",
716
+ String(port),
717
+ `--log=${logFile || "stdout"}`,
718
+ "--log-format=json",
719
+ ...ngrokUrl ? ["--url", ngrokUrl] : []
720
+ ]
721
+ };
722
+ }
723
+ const CLOUDFLARED_URL_RE = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/i;
724
+ function parseCloudflaredLine(line) {
725
+ const match = line.match(CLOUDFLARED_URL_RE);
726
+ return match ? match[0] : null;
727
+ }
728
+ function parseNgrokLine(line) {
729
+ let data;
730
+ try {
731
+ data = JSON.parse(line);
732
+ } catch {
733
+ return null;
734
+ }
735
+ if (typeof data !== "object" || data === null) return null;
736
+ const url = data.url;
737
+ return typeof url === "string" && /^https?:\/\//i.test(url) ? url : null;
738
+ }
739
+ function parserFor(provider) {
740
+ return provider === "cloudflared" ? parseCloudflaredLine : parseNgrokLine;
741
+ }
742
+ const URL_TIMEOUT_MS = 15e3;
743
+ const REACHABLE_TIMEOUT_MS = 24e4;
744
+ const REACHABLE_POLL_MS = 2e3;
745
+ const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
746
+ async function withManagedTunnelLock(root, fn, options = {}) {
747
+ return withWorkspaceProcessLock(managedTunnelLockRoot(root), "metro-tunnel", fn, {
748
+ ...options,
749
+ rejectOwnerPurposes: ["workspace removal"]
750
+ });
751
+ }
752
+ async function withManagedTunnelRemovalLock(root, fn, options = {}) {
753
+ return withWorkspaceProcessLock(managedTunnelLockRoot(root), "metro-tunnel", fn, {
754
+ ...options,
755
+ ownerPurpose: "workspace removal"
756
+ });
757
+ }
758
+ async function withManagedRemoteWorktreeLock(worktreeRoot, fn, options = {}) {
759
+ return withWorkspaceProcessLock(managedRemoteWorktreeLockRoot(worktreeRoot), "managed-remote", fn, {
760
+ ...options,
761
+ external: true,
762
+ rejectOwnerPurposes: ["worktree removal"]
763
+ });
764
+ }
765
+ async function withManagedRemoteWorktreeRemovalLock(worktreeRoot, fn, options = {}) {
766
+ return withWorkspaceProcessLock(managedRemoteWorktreeLockRoot(worktreeRoot), "managed-remote", fn, {
767
+ ...options,
768
+ external: true,
769
+ ownerPurpose: "worktree removal"
770
+ });
771
+ }
772
+ function managedLockHolders(root) {
773
+ const locks = [{
774
+ label: "managed tunnel",
775
+ path: workspaceProcessLockPath(managedTunnelLockRoot(root), "metro-tunnel", false)
776
+ }, {
777
+ label: "managed remote",
778
+ path: workspaceProcessLockPath(managedRemoteWorktreeLockRoot(root), "managed-remote", true)
779
+ }];
780
+ const reasons = [];
781
+ for (const { label, path } of locks) {
782
+ const claims = (0, ownership_claim_exports.readClaimSet)(path);
783
+ const holder = claims.live[0];
784
+ if (holder) {
785
+ const purpose = typeof holder.details.purpose === "string" ? ` for ${holder.details.purpose}` : "";
786
+ reasons.push(`the ${label} lock is held${purpose}`);
787
+ } else if (claims.unresolved[0]) reasons.push(`the ${label} lock cannot be resolved: ${claims.unresolved[0].reason}`);
788
+ }
789
+ return reasons;
790
+ }
791
+ function managedTunnelLockRoot(root) {
792
+ const key = createHash("sha256").update(resolve(root)).digest("hex");
793
+ return join(getConfigDir(), "process-locks", key);
794
+ }
795
+ function managedRemoteWorktreeLockRoot(worktreeRoot) {
796
+ const key = createHash("sha256").update(resolve(worktreeRoot)).digest("hex");
797
+ return join(getConfigDir(), "process-locks", "worktrees", key);
798
+ }
799
+ async function defaultProbeReachable(url, signal) {
800
+ const status = await probePublicHttp(url, signal);
801
+ return status !== null && status > 0;
802
+ }
803
+ function waitForUrl(child, parseLine, timeoutMs) {
804
+ return new Promise((resolve) => {
805
+ let settled = false;
806
+ let timer;
807
+ let onOut;
808
+ let onErr;
809
+ let onError;
810
+ let onExit;
811
+ const finish = (url, exited = false) => {
812
+ if (settled) return;
813
+ settled = true;
814
+ clearTimeout(timer);
815
+ child.stdout?.removeListener("data", onOut);
816
+ child.stderr?.removeListener("data", onErr);
817
+ child.removeListener("error", onError);
818
+ child.removeListener("exit", onExit);
819
+ resolve({
820
+ url,
821
+ exited
822
+ });
823
+ };
824
+ const onLine = (line) => {
825
+ if (settled) return;
826
+ const url = parseLine(line);
827
+ if (url) finish(url);
828
+ };
829
+ const outReader = createLineReader(onLine);
830
+ const errReader = createLineReader(onLine);
831
+ onOut = (chunk) => outReader.push(chunk);
832
+ onErr = (chunk) => errReader.push(chunk);
833
+ onError = () => finish(null);
834
+ onExit = () => finish(null, true);
835
+ child.stdout?.setEncoding?.("utf-8");
836
+ child.stderr?.setEncoding?.("utf-8");
837
+ child.stdout?.on("data", onOut);
838
+ child.stderr?.on("data", onErr);
839
+ child.on("error", onError);
840
+ child.on("exit", onExit);
841
+ timer = setTimeout(() => finish(null), timeoutMs);
842
+ });
843
+ }
844
+ function waitForFileUrl(child, logFile, parseLine, timeoutMs) {
845
+ return new Promise((resolve) => {
846
+ let settled = false;
847
+ const finish = (url, exited = false) => {
848
+ if (settled) return;
849
+ settled = true;
850
+ clearInterval(interval);
851
+ clearTimeout(timer);
852
+ child.removeListener("error", onError);
853
+ child.removeListener("exit", onExit);
854
+ resolve({
855
+ url,
856
+ exited
857
+ });
858
+ };
859
+ const probe = () => {
860
+ try {
861
+ for (const line of readFileSync(logFile, "utf-8").split(/\r?\n/)) {
862
+ const url = parseLine(line);
863
+ if (url) return finish(url);
864
+ }
865
+ } catch {}
866
+ };
867
+ const onError = () => finish(null);
868
+ const onExit = () => finish(null, true);
869
+ child.on("error", onError);
870
+ child.on("exit", onExit);
871
+ const interval = setInterval(probe, 25);
872
+ const timer = setTimeout(() => finish(null), timeoutMs);
873
+ probe();
874
+ });
875
+ }
876
+ function createTunnelLogFile(provider) {
877
+ const dir = join(getConfigDir(), "tunnel-logs");
878
+ mkdirSync(dir, { recursive: true });
879
+ const path = join(dir, `${provider}-${randomUUID()}.log`);
880
+ closeSync(openSync(path, "wx", 384));
881
+ return path;
882
+ }
883
+ function removeTunnelLogFile(path) {
884
+ if (!path) return;
885
+ try {
886
+ unlinkSync(path);
887
+ } catch {}
888
+ }
889
+ function removeRecordedTunnelLogFile(path) {
890
+ if (!path) return;
891
+ const dir = resolve(join(getConfigDir(), "tunnel-logs"));
892
+ const resolved = resolve(path);
893
+ if (!resolved.startsWith(`${dir}${sep}`) || resolve(join(dir, basename(resolved))) !== resolved) return;
894
+ removeTunnelLogFile(resolved);
895
+ }
896
+ async function waitUntilReachable({ url, now, sleep, probe, timeoutMs }) {
897
+ const deadline = now() + timeoutMs;
898
+ for (;;) {
899
+ if (await probe(url, new AbortController().signal).catch(() => false)) return true;
900
+ if (now() >= deadline) return false;
901
+ await sleep(REACHABLE_POLL_MS);
902
+ }
903
+ }
904
+ const CLEANUP_TIMEOUT_MS = 1e3;
905
+ const CLEANUP_POLL_MS = 25;
906
+ async function startTunnel({ provider, port, spawnFn = null, urlTimeoutMs = URL_TIMEOUT_MS, reachableTimeoutMs = REACHABLE_TIMEOUT_MS, probeReachable = defaultProbeReachable, now = Date.now, sleep = defaultSleep, ngrokUrl = null, requireReachable = true, cleanupTimeoutMs = CLEANUP_TIMEOUT_MS, isChildAlive = pidExists, readProcessToken = process_identity_exports.captureProcessToken, logFile = null }) {
907
+ const spawn = spawnFn || ((cmd, args, opts) => getExecutor().spawn(cmd, args, opts));
908
+ const outputFile = logFile || (spawnFn ? null : createTunnelLogFile(provider));
909
+ const { bin, args } = tunnelArgv(provider, port, ngrokUrl, outputFile);
910
+ let child;
911
+ try {
912
+ child = spawn(bin, args, {
913
+ stdio: outputFile ? [
914
+ "ignore",
915
+ "ignore",
916
+ "ignore"
917
+ ] : [
918
+ "ignore",
919
+ "pipe",
920
+ "pipe"
921
+ ],
922
+ detached: true
923
+ });
924
+ } catch (err) {
925
+ removeTunnelLogFile(outputFile);
926
+ return {
927
+ failed: true,
928
+ reason: `Could not start ${provider}: ${describe(err)}`
929
+ };
930
+ }
931
+ let childExited = false;
932
+ child.once("exit", () => {
933
+ childExited = true;
934
+ });
935
+ const cleanup = async (alreadyExited = childExited) => {
936
+ const stopped = await terminateChild(child, {
937
+ alreadyExited,
938
+ timeoutMs: cleanupTimeoutMs,
939
+ now,
940
+ sleep,
941
+ isAlive: isChildAlive
942
+ });
943
+ if (stopped) removeTunnelLogFile(outputFile);
944
+ return stopped ? { status: "stopped" } : {
945
+ status: "failed",
946
+ reason: `Sent SIGKILL but could not confirm that pid ${child.pid ?? "unknown"} exited.`
947
+ };
948
+ };
949
+ const failAfterCleanup = async (reason, alreadyExited = childExited) => {
950
+ const stopped = await cleanup(alreadyExited);
951
+ return stopped.status === "stopped" ? {
952
+ failed: true,
953
+ reason
954
+ } : {
955
+ failed: true,
956
+ reason: `${reason} ${stopped.reason}`,
957
+ cleanupFailed: true
958
+ };
959
+ };
960
+ const pid = child.pid;
961
+ if (!pid) return failAfterCleanup(`${provider} started but reported no pid.`);
962
+ const captureChildIdentity = () => {
963
+ try {
964
+ return readProcessToken(pid);
965
+ } catch {
966
+ return null;
967
+ }
968
+ };
969
+ const initialProcessToken = captureChildIdentity();
970
+ if (!initialProcessToken) return failAfterCleanup(`${provider} started but its process identity token could not be read.`);
971
+ const { url, exited } = outputFile ? await waitForFileUrl(child, outputFile, parserFor(provider), urlTimeoutMs) : await waitForUrl(child, parserFor(provider), urlTimeoutMs);
972
+ if (!url) return failAfterCleanup(exited ? `${provider} exited before printing a tunnel URL.` : `${provider} did not print a tunnel URL within ${urlTimeoutMs}ms.`, exited || childExited);
973
+ if (!outputFile) resumeChildPipes(child);
974
+ if (requireReachable) {
975
+ if (!await waitUntilReachable({
976
+ url,
977
+ now,
978
+ sleep,
979
+ probe: probeReachable,
980
+ timeoutMs: reachableTimeoutMs
981
+ })) return failAfterCleanup(`${url} did not become reachable within ${reachableTimeoutMs}ms; ${provider} may still be registering, or the network path is blocked.`);
982
+ }
983
+ const finalProcessToken = captureChildIdentity();
984
+ if (!finalProcessToken || finalProcessToken !== initialProcessToken) return failAfterCleanup(`${provider} process identity changed before its tunnel could be recorded.`);
985
+ if (!outputFile) unrefChildPipes(child);
986
+ child.unref?.();
987
+ if (childExited) return failAfterCleanup(`${provider} exited before its tunnel could be recorded.`, true);
988
+ return {
989
+ url,
990
+ pid,
991
+ processToken: initialProcessToken,
992
+ logFile: outputFile,
993
+ cleanup: () => cleanup()
994
+ };
995
+ }
996
+ async function startTunnelSequence({ providers, port, ngrokUrl = null, requireReachable = true, start = startTunnel }) {
997
+ const failures = [];
998
+ for (const provider of providers) {
999
+ const result = await start({
1000
+ provider,
1001
+ port,
1002
+ ngrokUrl: provider === "ngrok" ? ngrokUrl : null,
1003
+ requireReachable
1004
+ });
1005
+ if (!("failed" in result)) return {
1006
+ provider,
1007
+ ...result
1008
+ };
1009
+ failures.push(`${provider}: ${result.reason}`);
1010
+ if (result.cleanupFailed) return {
1011
+ failed: true,
1012
+ reason: failures.join(" ")
1013
+ };
1014
+ }
1015
+ return {
1016
+ failed: true,
1017
+ reason: failures.join(" ") || "No managed tunnel provider was selected."
1018
+ };
1019
+ }
1020
+ function closeChildPipes(child) {
1021
+ child.stdout?.destroy?.();
1022
+ child.stderr?.destroy?.();
1023
+ }
1024
+ function unrefChildPipes(child) {
1025
+ resumeChildPipes(child);
1026
+ for (const stream of [child.stdout, child.stderr]) stream?.unref?.();
1027
+ }
1028
+ function resumeChildPipes(child) {
1029
+ child.stdout?.resume?.();
1030
+ child.stderr?.resume?.();
1031
+ }
1032
+ async function signalAndWaitForExit(child, signal, { timeoutMs, now, sleep, isAlive, platform }) {
1033
+ let exited = false;
1034
+ const onExit = () => {
1035
+ exited = true;
1036
+ };
1037
+ child.once("exit", onExit);
1038
+ try {
1039
+ if (platform === "win32" && child.pid) signalProcessTree(child.pid, signal, { platform });
1040
+ child.kill(signal);
1041
+ } catch (err) {
1042
+ child.removeListener("exit", onExit);
1043
+ return isEsrch(err);
1044
+ }
1045
+ const hasExited = () => exited;
1046
+ const deadline = now() + timeoutMs;
1047
+ while (!hasExited() && now() < deadline) await sleep(CLEANUP_POLL_MS);
1048
+ if (!hasExited() && child.pid && !isAlive(child.pid)) exited = true;
1049
+ child.removeListener("exit", onExit);
1050
+ return hasExited();
1051
+ }
1052
+ async function terminateChild(child, { alreadyExited, timeoutMs, now, sleep, isAlive, platform = process.platform }) {
1053
+ if (alreadyExited) {
1054
+ closeChildPipes(child);
1055
+ return true;
1056
+ }
1057
+ if (await signalAndWaitForExit(child, "SIGTERM", {
1058
+ timeoutMs,
1059
+ now,
1060
+ sleep,
1061
+ isAlive,
1062
+ platform
1063
+ })) {
1064
+ closeChildPipes(child);
1065
+ return true;
1066
+ }
1067
+ const killed = await signalAndWaitForExit(child, "SIGKILL", {
1068
+ timeoutMs,
1069
+ now,
1070
+ sleep,
1071
+ isAlive,
1072
+ platform
1073
+ });
1074
+ closeChildPipes(child);
1075
+ return killed;
1076
+ }
1077
+ function describe(err) {
1078
+ return err?.message || String(err);
1079
+ }
1080
+ const STOP_TIMEOUT_MS = 5e3;
1081
+ const STOP_POLL_MS = 100;
1082
+ function isEsrch(err) {
1083
+ return err?.code === "ESRCH";
1084
+ }
1085
+ async function stopTunnel(record, { isAlive = pidExists, inspectIdentity = process_identity_exports.inspectProcessIdentity, platform = process.platform, kill = (pid) => signalProcessTree(pid, "SIGTERM", { platform }), now = Date.now, sleep = defaultSleep, timeoutMs = STOP_TIMEOUT_MS } = {}) {
1086
+ const missing = () => {
1087
+ removeRecordedTunnelLogFile(record?.logFile);
1088
+ return { status: "missing" };
1089
+ };
1090
+ const pid = record?.pid;
1091
+ if (!pid || !isAlive(pid)) return missing();
1092
+ if (!record.processToken) return {
1093
+ status: "failed",
1094
+ reason: `tunnel pid ${pid} has no process identity token, so Stim cannot verify ownership. Keep the record. Inspect the process, stop it with the provider tooling, remove the stale metroTunnel state, and retry.`
1095
+ };
1096
+ const identity = inspectIdentity(record);
1097
+ if (identity === "gone" || identity === "different") return missing();
1098
+ if (identity !== "same") return {
1099
+ status: "failed",
1100
+ reason: `could not verify the process identity for tunnel pid ${pid}; refusing to signal it.`
1101
+ };
1102
+ try {
1103
+ kill(pid);
1104
+ } catch (err) {
1105
+ return isEsrch(err) ? missing() : {
1106
+ status: "failed",
1107
+ reason: describe(err)
1108
+ };
1109
+ }
1110
+ const deadline = now() + timeoutMs;
1111
+ const exited = () => {
1112
+ if (!isAlive(pid)) return true;
1113
+ const current = inspectIdentity(record);
1114
+ return current === "gone" || current === "different";
1115
+ };
1116
+ while (now() < deadline && !exited()) await sleep(STOP_POLL_MS);
1117
+ if (!exited()) return {
1118
+ status: "failed",
1119
+ reason: `pid ${pid} did not exit within ${timeoutMs}ms.`
1120
+ };
1121
+ removeRecordedTunnelLogFile(record.logFile);
1122
+ return { status: "stopped" };
1123
+ }
1124
+ //#endregion
1125
+ //#region src/supervisor/ownership.ts
1126
+ function resolveSupervisorTarget({ state, record, reservedPort, isAlive = pidExists, inspectIdentity = process_identity_exports.inspectProcessIdentity } = {}) {
1127
+ const statePid = numberOrNull(state?.pid);
1128
+ const recordPid = numberOrNull(record?.pid);
1129
+ const pid = statePid ?? recordPid;
1130
+ if (!pid) return { status: "none" };
1131
+ if (statePid && recordPid && statePid !== recordPid) return {
1132
+ status: "unverified",
1133
+ pid,
1134
+ reason: `workspace state.json records supervisor pid ${statePid} but the registry records pid ${recordPid}`
1135
+ };
1136
+ if (!isAlive(pid)) return {
1137
+ status: "stale",
1138
+ pid
1139
+ };
1140
+ const port = numberOrNull(state?.port) ?? numberOrNull(record?.port);
1141
+ if (reservedPort !== null && reservedPort !== void 0 && port !== null && port !== reservedPort) return {
1142
+ status: "unverified",
1143
+ pid,
1144
+ reason: `supervisor pid ${pid} records port ${port}, but this project reserved port ${reservedPort}`
1145
+ };
1146
+ if (reservedPort !== null && reservedPort !== void 0 && port === null) return {
1147
+ status: "unverified",
1148
+ pid,
1149
+ reason: `supervisor pid ${pid} has no recorded port, so it cannot be matched against reserved port ${reservedPort}`
1150
+ };
1151
+ const identityRecord = statePid ? state : record;
1152
+ if (statePid && recordPid && state?.processToken !== record?.processToken) return {
1153
+ status: "unverified",
1154
+ pid,
1155
+ reason: "workspace state and registry process identities disagree"
1156
+ };
1157
+ const identity = inspectIdentity(identityRecord);
1158
+ if (identity === "gone" || identity === "different") return {
1159
+ status: "stale",
1160
+ pid
1161
+ };
1162
+ if (identity !== "same") return {
1163
+ status: "unverified",
1164
+ pid,
1165
+ reason: "the recorded process identity could not be verified"
1166
+ };
1167
+ return {
1168
+ status: "ours",
1169
+ processToken: identityRecord.processToken,
1170
+ pid,
1171
+ port,
1172
+ mode: state?.mode ?? record?.mode ?? null,
1173
+ startedAt: state?.startedAt ?? record?.startedAt ?? null
1174
+ };
1175
+ }
1176
+ function numberOrNull(v) {
1177
+ return typeof v === "number" && Number.isSafeInteger(v) && v > 0 ? v : null;
1178
+ }
1179
+ //#endregion
1180
+ export { dropParked as _, terminateChild as a, readParked as b, withManagedTunnelLock as c, teardownOwnedAvd as d, teardownOwnedIosSim as f, adoptParked as g, POOL_SETTING_REMEDY as h, stopTunnel as i, withManagedTunnelRemovalLock as l, teardownParkedIosSim as m, managedLockHolders as n, withManagedRemoteWorktreeLock as o, teardownParkedAvd as p, startTunnelSequence as r, withManagedRemoteWorktreeRemovalLock as s, resolveSupervisorTarget as t, probePublicHttp as u, isLegacyDeletionClaim as v, selectParked as x, parkedMaxSetting as y };