stim 1.33.0 → 1.35.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 (125) hide show
  1. package/dist/{acquire-artifact-DTh_LxrE.mjs → acquire-artifact-0E_iw7Rf.mjs} +1 -1
  2. package/dist/{act-Cur4YKtK.mjs → act-CyxFP8Vj.mjs} +9 -10
  3. package/dist/{activity-C8kBq9w0.mjs → activity-BQpJpAjA.mjs} +2 -2
  4. package/dist/{agent-device-usage-output-BTRaK_up.mjs → agent-device-usage-output-BoOC3ZF9.mjs} +3 -3
  5. package/dist/{android-D5mCdVl2.mjs → android-CjcJE7uA.mjs} +5 -5
  6. package/dist/{android-BL-QqHjc.mjs → android-YRb1UzSy.mjs} +27 -29
  7. package/dist/{android-project-BDlPnfmL.d.mts → android-project-DhO6el8N.d.mts} +1 -1
  8. package/dist/api.d.mts +1 -1
  9. package/dist/api.mjs +21 -21
  10. package/dist/{artifact-BufzdGbT.mjs → artifact-2NIRz6SC.mjs} +19 -15
  11. package/dist/{artifact-BMjubIP2.mjs → artifact-BG4h_WTC.mjs} +11 -11
  12. package/dist/{auto-placement-D7P98glJ.mjs → auto-placement-CBdWa87P.mjs} +4 -5
  13. package/dist/{browser-web-C74et-Nv.mjs → browser-web-CNlRugJv.mjs} +5 -5
  14. package/dist/{build-DFBGWOTc.mjs → build-CNE3Wo5-.mjs} +15 -16
  15. package/dist/build-artifact-cache-D7B6seMj.mjs +174 -0
  16. package/dist/build-artifact-cache.d.mts +25 -0
  17. package/dist/build-artifact-cache.mjs +2 -0
  18. package/dist/{build-cache-Cqj4FGnX.mjs → build-cache-_fkewiwH.mjs} +6 -1
  19. package/dist/{build-plan-T3Hi24ax.mjs → build-plan-BSgR9yKo.mjs} +5 -5
  20. package/dist/{cli-B2YSjc9s.mjs → cli-DK24VzHi.mjs} +17 -17
  21. package/dist/cli.mjs +1 -1
  22. package/dist/{client-0_Mnfz0a.mjs → client-Dy0eJJtz.mjs} +49 -11
  23. package/dist/{command-output-DWI9tGPU.mjs → command-output-CGxrQ3Bd.mjs} +1 -1
  24. package/dist/{dependencies-CgvBZbXD.mjs → dependencies-IVl2Ga2D.mjs} +27 -30
  25. package/dist/{dev-client-ChsHwgrn.mjs → dev-client-DDApoVvd.mjs} +2 -2
  26. package/dist/{device-android-B8TQP5AX.mjs → device-android-C-biM49_.mjs} +3 -3
  27. package/dist/{ccache-BXtzEL6M.mjs → device-capacity-C0PiTyRb.mjs} +1004 -63
  28. package/dist/device-host-worker.mjs +6 -7
  29. package/dist/{device-B0htCt2u.mjs → device-mOFYiHiw.mjs} +8 -8
  30. package/dist/{device-DOehZpyx.mjs → device-p_cp8IzW.mjs} +6 -6
  31. package/dist/{device-pool-DP4sCeCC.mjs → device-pool-nsMvt2X2.mjs} +2 -2
  32. package/dist/{device-provision-CnNOIxCF.mjs → device-provision-BYYJuRKZ.mjs} +7 -7
  33. package/dist/{doctor-C6Xn96RS.mjs → doctor-BBAb8NLe.mjs} +1 -1
  34. package/dist/{doctor-9GrW5YSP.mjs → doctor-BptE4PaK.mjs} +10 -11
  35. package/dist/{doctor-nRLBoajo.mjs → doctor-RosbBfTV.mjs} +11 -12
  36. package/dist/doctor-fingerprint.mjs +1 -1
  37. package/dist/{doctor-watchman-CDhpC3Oh.mjs → doctor-watchman-CKOnCINh.mjs} +2 -2
  38. package/dist/{eas-build-CpJ4h9tG.mjs → eas-build-BxJdzQnF.mjs} +2 -2
  39. package/dist/{eas-fallback-B3z5nL1g.mjs → eas-fallback-DU2WDF8r.mjs} +2 -2
  40. package/dist/{eas-session-ledger-Bspy-VJQ.mjs → eas-session-ledger-DCUfh6m7.mjs} +1 -1
  41. package/dist/{error-diagnostics-DoLGklYX.mjs → error-diagnostics-D6wlK-yX.mjs} +3 -3
  42. package/dist/{gc-DolXpMSB.mjs → gc-BUkonUfz.mjs} +10 -10
  43. package/dist/{gc-BWoOp9q7.mjs → gc-CvMEytXz.mjs} +2 -2
  44. package/dist/{guide-CmZB0GtP.mjs → guide-BWozWG7D.mjs} +25 -15
  45. package/dist/{hosted-android-ByG-ICuh.mjs → hosted-android-y6K3DKQT.mjs} +2 -2
  46. package/dist/{hosted-client-D19Lv0tg.mjs → hosted-client-Bb89P6an.mjs} +2 -2
  47. package/dist/{hosted-logs-D7anR6Dk.mjs → hosted-logs-DYPsnWMF.mjs} +2 -2
  48. package/dist/{hosted-logs-sync-D182b1eJ.mjs → hosted-logs-sync-DfUJ2GpF.mjs} +4 -4
  49. package/dist/{hosted-macos-D0DDQxvl.mjs → hosted-macos-DsEzYVY_.mjs} +4 -4
  50. package/dist/{hosted-native-BzXIy97W.mjs → hosted-native-C9xQYYFb.mjs} +4 -4
  51. package/dist/{ios-AdhPKXO3.mjs → ios-BCKxjpRp.mjs} +8 -8
  52. package/dist/{ios-dbXzjTxx.mjs → ios-FhMkDfNC.mjs} +19 -21
  53. package/dist/{ios-device-DXbHZfFe.mjs → ios-device-DMOnaH5n.mjs} +1 -1
  54. package/dist/{ios-project-BzfLigfh.mjs → ios-project-PXBDoaii.mjs} +5 -5
  55. package/dist/ios-signing-CCFNjjWU.mjs +202 -0
  56. package/dist/{js-swap-EDVOJmqx.mjs → js-swap-07ikLRJJ.mjs} +3 -3
  57. package/dist/{launch-CKHpthsw.mjs → launch-CTq5gBim.mjs} +13 -13
  58. package/dist/{lightened-Cv4ToJRX.mjs → lightened-DikgC3-S.mjs} +2 -2
  59. package/dist/{logs-IagR9dWI.mjs → logs-DwI3Rkpf.mjs} +5 -5
  60. package/dist/{logs-DCQ3_tGS.mjs → logs-YDMfEohb.mjs} +3 -3
  61. package/dist/{macos-Ch7vuM-G.mjs → macos-BoNM-l6f.mjs} +14 -14
  62. package/dist/{macos-pc0aVVbN.mjs → macos-C_e9f5-5.mjs} +1 -1
  63. package/dist/maintenance-run.mjs +3 -3
  64. package/dist/{native-android-B3CEDq4R.mjs → native-android-Mv4YTrVN.mjs} +11 -11
  65. package/dist/{native-gradle-artifact-inputs-BLnO2hHR.mjs → native-gradle-artifact-inputs-C_TU6wPq.mjs} +2 -2
  66. package/dist/{native-runtime-e1OU8gEN.mjs → native-runtime-oDqT4UES.mjs} +3 -3
  67. package/dist/{native-xcode-inputs-DL3ETb8s.mjs → native-xcode-inputs-DsozghTX.mjs} +1 -1
  68. package/dist/{native-xcode-ios-D4e8k2Jy.mjs → native-xcode-ios-UXnH9NRY.mjs} +13 -13
  69. package/dist/offload-worker.d.mts +6 -1
  70. package/dist/offload-worker.mjs +40 -15
  71. package/dist/{phone-reverses-rvyxW1Fl.mjs → phone-reverses-B9WyCBKQ.mjs} +1 -1
  72. package/dist/{placement-log-nnGnu8mo.mjs → placement-log-D0DZLcJk.mjs} +1 -1
  73. package/dist/{plan-placement-D3JD-T-K.mjs → plan-placement-DqmLrWoX.mjs} +4 -4
  74. package/dist/{ports-CZJJfJ8Q.mjs → ports-C1_1Qgdy.mjs} +1 -1
  75. package/dist/{prebuild-Bhs7TTcz.mjs → prebuild-BptrXmh_.mjs} +6 -6
  76. package/dist/{project-DWEXiBA9.mjs → project-Dqqj3IkW.mjs} +11 -11
  77. package/dist/{react-native-android-C6Y270GS.mjs → react-native-android-Bsm_o4A8.mjs} +18 -18
  78. package/dist/{react-native-build-DaPfzf_e.mjs → react-native-build-BTUcE926.mjs} +4 -4
  79. package/dist/{react-native-doctor-DGMPxkPS.mjs → react-native-doctor-CfsCTKZt.mjs} +4 -4
  80. package/dist/{react-native-ios-dX48_92n.mjs → react-native-ios-_cK2pjZn.mjs} +25 -19
  81. package/dist/{reload-BGwblBJ9.mjs → reload-CXgce4Yo.mjs} +1 -1
  82. package/dist/{reload-DsOByDfy.mjs → reload-DsTMtMDU.mjs} +6 -6
  83. package/dist/{remote-D0uJZ76R.mjs → remote-BsmYHUxH.mjs} +6 -6
  84. package/dist/{result-DuimMZKJ.mjs → result-DK6nTO-M.mjs} +9 -10
  85. package/dist/{server-command-BC0EgMfY.mjs → server-command-Cu2w-UjB.mjs} +2 -2
  86. package/dist/{server-expo-15RjL0ir.mjs → server-expo-CjQwxvIi.mjs} +1 -1
  87. package/dist/server-expo-DbSypdNl.mjs +2 -0
  88. package/dist/{settings-CuP55ieT.mjs → settings-DcmVICCn.mjs} +2 -2
  89. package/dist/{settings-payload-BIGGQ3or.mjs → settings-payload-BoiFMiFq.mjs} +1 -1
  90. package/dist/{settings-write-D85j8mej.mjs → settings-write-B3LzEVwf.mjs} +2 -2
  91. package/dist/{slot-launch-D4oREwhx.mjs → slot-launch-DsQmPdlg.mjs} +2 -2
  92. package/dist/{stage-DKC6tDXu.mjs → stage-Dqk0qvaI.mjs} +1 -1
  93. package/dist/{start-BsEV4S-s.mjs → start-DIFiT2P5.mjs} +5 -5
  94. package/dist/{start-DfPSLc__.mjs → start-GkRBR1e3.mjs} +1 -1
  95. package/dist/{stats-ByEUblPr.mjs → stats-Dz08IopP.mjs} +3 -3
  96. package/dist/{status-C6hLlDqW.mjs → status-DhKv_e8a.mjs} +11 -12
  97. package/dist/{status-FP1MH9qn.mjs → status-Dost__Vn.mjs} +1 -1
  98. package/dist/{status-watch-D8s823CZ.mjs → status-watch-UecLnPQD.mjs} +1 -1
  99. package/dist/status-worker.mjs +1 -1
  100. package/dist/{stop-CpTy6bod.mjs → stop-CcZISot5.mjs} +2 -2
  101. package/dist/{stop-5LKfVv-0.mjs → stop-QFOqgICJ.mjs} +2 -2
  102. package/dist/{stop-BVI2-L7s.mjs → stop-vRe6TQk5.mjs} +9 -9
  103. package/dist/supervisor-run.mjs +7 -8
  104. package/dist/{support-CqyWfZZI.mjs → support-CNqJDBrH.mjs} +3 -3
  105. package/dist/support-DW575Dnx.mjs +2050 -0
  106. package/dist/{swiftpm-artifact-inputs-uq5oV7xR.mjs → swiftpm-artifact-inputs-DQTJTczX.mjs} +1 -1
  107. package/dist/{swiftpm-macos-BuhqYB3n.mjs → swiftpm-macos-BKczZ0k9.mjs} +4 -4
  108. package/dist/{toolchain-D_jotUG7.mjs → toolchain-BDlugtoh.mjs} +1 -1
  109. package/dist/{toolchain-s5ZmG42Z.mjs → toolchain-DRQyCvlp.mjs} +406 -8
  110. package/dist/web-D1m1LKEz.mjs +2 -0
  111. package/dist/{web-DwYCTFdh.mjs → web-TDWjHUT3.mjs} +3 -3
  112. package/dist/web-run.mjs +1 -1
  113. package/dist/{workspaces-B3ahAHZc.mjs → workspaces-CY3KSdC0.mjs} +2 -2
  114. package/dist/{worktree-CrjsPIs2.mjs → worktree-D6myrQ2H.mjs} +1 -1
  115. package/dist/{worktree-BhsML6GV.mjs → worktree-fKKe8j5M.mjs} +10 -10
  116. package/package.json +12 -7
  117. package/dist/device-capacity-BAkETfIl.mjs +0 -867
  118. package/dist/device-ios-DFaYFdar.mjs +0 -716
  119. package/dist/device-lease-run-DpiCnRbZ.mjs +0 -282
  120. package/dist/idle-shutdown-6i8CHoNq.mjs +0 -142
  121. package/dist/ios-signing-D-tSX1iF.mjs +0 -489
  122. package/dist/selection-xS50Mt0V.mjs +0 -791
  123. package/dist/server-expo-gYIypb0-.mjs +0 -2
  124. package/dist/support-D8OH0z98.mjs +0 -287
  125. package/dist/web-cCfM8kwV.mjs +0 -2
@@ -1,716 +0,0 @@
1
- import { D as ownership_claim_exports, l as cancellableSleep, n as isTimeoutError, p as withWorkspaceProcessLock, t as getExecutor } from "./exec-CGuDzt40.mjs";
2
- import { D as workspaceId$1, k as workspaceLogsDir$1 } from "./workspace-state-BdZ-QgmB.mjs";
3
- import { l as phaseLine } from "./command-output-DWI9tGPU.mjs";
4
- import { T as setDevice, a as clearDevice, d as getConfigDir, ft as deviceSlotPlatforms, ht as projectDeviceSlots, k as withConfigLockAsync$1, v as loadConfig$1, w as saveConfig } from "./config-BW0fAtoK.mjs";
5
- import { D as iosSimSlimProfileSetting, E as iosSimSlimDefaultSetting } from "./settings-B5IBDfTx.mjs";
6
- import { Ct as selectParked, bt as parkedMaxSetting, gt as dropParked, ht as adoptParked, xt as readParked } from "./android-Iq0M2t2d.mjs";
7
- import { i as createNdjsonWriter } from "./ndjson-CrjIsGzw.mjs";
8
- import { _t as teardownParkedIosSim, ht as teardownOwnedIosSim } from "./workspaces-B3ahAHZc.mjs";
9
- import { A as springBoardRunning, B as reportMemoryCulprits, C as renameIosSim, D as resolveOwnedIosSim, E as resolveIosCreation, F as hostMemoryPressureAdvice, M as waitForSpringBoard, N as configuredIosSimulatorViewer, R as readHostMemoryPressure, S as readSpringBoardPid, T as resetIosPrivacy, _ as ownedSimName, a as createOwnedIosSim, d as listAllIosSims, f as listAllIosSimsAsync, h as listIosRuntimesAsync, n as bootIosSim, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, w as resetIosKeychain, y as parseRuntimeVersion } from "./ios-BdZdNsAj.mjs";
10
- import { _ as withDeviceBootAdmission, f as runtimeMismatch, l as deviceTypeMismatch, n as DeviceAdmissionRefusal } from "./device-capacity-BAkETfIl.mjs";
11
- import { createHash } from "node:crypto";
12
- import { readFileSync } from "node:fs";
13
- import { homedir } from "node:os";
14
- import { join } from "node:path";
15
- import { LOG_ROTATE_BYTES, ambientContext } from "@stim-cli/core";
16
- import { isJsonObject } from "@stim-cli/core/state";
17
- import chalk from "chalk";
18
- import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
19
- //#region src/devices/springboard-crash-reports.ts
20
- const REPORT_WINDOW_MS = 6e5;
21
- const MAX_CANDIDATES = 100;
22
- const MAX_STORED = 40;
23
- const MAX_REPORT_BYTES = 1048576;
24
- const MAX_COPIED_BYTES = 8388608;
25
- function selectSpringBoardReports(entries, udid, sinceMs) {
26
- const owner = `.${udid}`.toLowerCase();
27
- return entries.flatMap(({ name, text }) => {
28
- if (!/^SpringBoard-.+\.ips$/.test(name)) return [];
29
- try {
30
- const report = JSON.parse(text.slice(text.indexOf("\n") + 1));
31
- if (!isJsonObject(report)) return [];
32
- const { coalitionName, captureTime } = report;
33
- const capturedAt = Date.parse(typeof captureTime === "string" ? captureTime : "");
34
- return typeof coalitionName === "string" && coalitionName.toLowerCase().endsWith(owner) && capturedAt >= sinceMs ? [name] : [];
35
- } catch {
36
- return [];
37
- }
38
- });
39
- }
40
- /**
41
- * Copies this simulator's recent SpringBoard crash reports from DiagnosticReports and its Retired folder into
42
- * `destDir`, which keeps at most 40 of them. The source folders are only read; macOS deletes their reports after
43
- * about two days. Returns the matching reports now in `destDir`, how many matched but did not fit its limits, and
44
- * the message of a write that failed.
45
- */
46
- async function copySpringBoardCrashReports({ udid, destDir, now = Date.now(), home = homedir() }) {
47
- const since = now - REPORT_WINDOW_MS;
48
- const reportsDir = join(home, "Library/Logs/DiagnosticReports");
49
- const candidates = /* @__PURE__ */ new Map();
50
- for (const directory of [reportsDir, join(reportsDir, "Retired")]) {
51
- let names;
52
- try {
53
- names = await readdir(directory);
54
- } catch (error) {
55
- if (error instanceof Error && "code" in error && error.code === "ENOENT") continue;
56
- throw error;
57
- }
58
- for (const name of names) {
59
- if (!/^SpringBoard-.+\.ips$/.test(name) || candidates.has(name)) continue;
60
- try {
61
- const path = join(directory, name);
62
- const { mtimeMs, size } = await stat(path);
63
- if (mtimeMs >= since && size <= MAX_REPORT_BYTES) candidates.set(name, {
64
- path,
65
- mtimeMs
66
- });
67
- } catch {}
68
- }
69
- }
70
- const newest = [...candidates].toSorted((a, b) => b[1].mtimeMs - a[1].mtimeMs).slice(0, MAX_CANDIDATES);
71
- const entries = [];
72
- for (const [name, { path }] of newest) try {
73
- entries.push({
74
- name,
75
- text: await readFile(path, "utf8")
76
- });
77
- } catch {}
78
- const matching = selectSpringBoardReports(entries, udid, since);
79
- if (matching.length === 0) return {
80
- files: [],
81
- dropped: 0
82
- };
83
- await mkdir(destDir, {
84
- recursive: true,
85
- mode: 448
86
- });
87
- const present = new Set(await readdir(destDir));
88
- let stored = [...present].filter((name) => /^SpringBoard-.+\.ips$/.test(name)).length;
89
- let copiedBytes = 0;
90
- const files = [];
91
- for (const { name, text } of entries) {
92
- if (!matching.includes(name)) continue;
93
- if (present.has(name)) {
94
- files.push(name);
95
- continue;
96
- }
97
- const bytes = Buffer.byteLength(text);
98
- if (stored >= MAX_STORED || copiedBytes + bytes > MAX_COPIED_BYTES) continue;
99
- try {
100
- await writeFile(join(destDir, name), text, {
101
- flag: "wx",
102
- mode: 384
103
- });
104
- } catch (error) {
105
- if (error instanceof Error && "code" in error && error.code === "EEXIST") {
106
- files.push(name);
107
- continue;
108
- }
109
- await rm(join(destDir, name), { force: true }).catch(() => {});
110
- return {
111
- files,
112
- dropped: 0,
113
- failure: error instanceof Error ? error.message : String(error)
114
- };
115
- }
116
- stored++;
117
- copiedBytes += bytes;
118
- files.push(name);
119
- }
120
- return {
121
- files,
122
- dropped: matching.length - files.length
123
- };
124
- }
125
- //#endregion
126
- //#region src/engine/ios-boot-log.ts
127
- function iosBootRecorder(root, slot, udid) {
128
- return (event, { level = "info", ...fields }) => {
129
- try {
130
- const writer = createNdjsonWriter(join(workspaceLogsDir$1(root), "device.ndjson"), { maxBytes: LOG_ROTATE_BYTES });
131
- writer.write({
132
- src: "device",
133
- platform: "ios",
134
- level,
135
- event,
136
- deviceId: udid,
137
- ...slot && slot !== "default" ? { slot } : {},
138
- ...fields
139
- });
140
- writer.close();
141
- } catch {}
142
- };
143
- }
144
- //#endregion
145
- //#region src/engine/device-ios.ts
146
- function recordBootPhase(record, udid, origin) {
147
- return (event) => {
148
- const pid = "pid" in event ? event.pid : void 0;
149
- const boot = event.phase === "boot_requested" ? origin : void 0;
150
- record("ios_boot", {
151
- phase: event.phase,
152
- ...pid === void 0 ? {} : { pid },
153
- ...boot === void 0 ? {} : { boot },
154
- msg: `ios boot ${udid}: ${event.phase}${pid === void 0 ? "" : ` pid ${pid}`}${boot === void 0 ? "" : ` (${boot})`}`
155
- });
156
- };
157
- }
158
- function startIosBoot(udid, configure, label, out, simulatorApp, root, slot, origin, deviceSlotWait) {
159
- const done = withDeviceBootAdmission({
160
- platform: "ios",
161
- key: udid
162
- }, async () => {
163
- out(chalk.dim(phaseLine("device", `booting ${label} (${udid})`)));
164
- await bootIosSim(udid, {
165
- label,
166
- out,
167
- simulatorApp,
168
- env: ambientContext()?.env,
169
- onPhase: recordBootPhase(iosBootRecorder(root, slot, udid), udid, origin)
170
- });
171
- await configure();
172
- await muteBootedSimulator(udid, out);
173
- }, {
174
- ...deviceSlotWait,
175
- root,
176
- out
177
- });
178
- done.catch(() => {});
179
- return {
180
- udid,
181
- done
182
- };
183
- }
184
- const SPRINGBOARD_MUTE_WAIT_MS = 6e4;
185
- async function muteBootedSimulator(udid, out) {
186
- try {
187
- const springBoard = await waitForSpringBoard(udid, Date.now() + SPRINGBOARD_MUTE_WAIT_MS);
188
- if (springBoard.state === "unreadable") throw new Error(springBoard.reason);
189
- if (springBoard.state === "timed-out") throw new Error(`SpringBoard did not finish starting within ${SPRINGBOARD_MUTE_WAIT_MS / 1e3}s.`);
190
- const response = JSON.parse(await getExecutor().runFileAsync("xcrun", [
191
- "devicectl",
192
- "device",
193
- "settings",
194
- "audio",
195
- "--device",
196
- udid,
197
- "--volume",
198
- "0",
199
- "--json-output",
200
- "-"
201
- ], { timeoutMs: 3e4 }));
202
- if (!isJsonObject(response) || !isJsonObject(response.info) || response.info.outcome !== "success" || !isJsonObject(response.result) || typeof response.result.deviceIdentifier !== "string" || response.result.deviceIdentifier.toUpperCase() !== udid.toUpperCase() || response.result.volume !== 0) throw new Error("Xcode did not confirm volume zero for this simulator.");
203
- out(chalk.dim(phaseLine("device", "simulator muted; turn on Sound in its Stim Desktop options")));
204
- } catch (error) {
205
- out(chalk.yellow(phaseLine("device", `Could not mute this simulator: ${error instanceof Error ? error.message : String(error)}`)));
206
- }
207
- }
208
- var IosDeviceMismatchError = class extends Error {
209
- remedy = "Run `stim worktree remove` (or `stim gc --delete`) to reap the current sim, then `stim ios` again to create the requested one, or pass `--slot <name>` to create it beside the current one.";
210
- };
211
- async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, label, settings, flags, note, out, reconcileIosSimulator, simSnapshot }) {
212
- const pressure = readHostMemoryPressure();
213
- const memoryAdvice = hostMemoryPressureAdvice(pressure);
214
- if (memoryAdvice) out(chalk.yellow(phaseLine("memory", memoryAdvice)));
215
- reportMemoryCulprits(pressure, (named) => out(chalk.yellow(phaseLine("memory", named))));
216
- const simslimProfile = iosSimSlimProfileSetting(settings, settingsRoot);
217
- const simslimDefault = iosSimSlimDefaultSetting(settings);
218
- if (record?.deviceUdid) {
219
- if (record.owned) {
220
- const listed = simSnapshot ? simSnapshot.read() : listAllIosSims({ includeUnavailable: true });
221
- const resolved = resolveOwnedIosSim(record.deviceUdid, listed);
222
- if (resolved.notOwned) note(chalk.yellow(`Note: recorded sim is now named "${resolved.notOwned}", not Stim-owned -- creating a fresh owned sim instead of booting it.`));
223
- else if (resolved.missing) {} else {
224
- const sim = resolved.sim;
225
- const wantedType = flags.deviceType || settings?.ios?.deviceType;
226
- const deviceTypes = await listIosDeviceTypesAsync({ snapshot: simSnapshot });
227
- const mismatch = deviceTypeMismatch(sim.deviceTypeIdentifier, wantedType, deviceTypes);
228
- if (mismatch) throw new IosDeviceMismatchError(`${mismatch}. Stim will not silently boot a different model.`);
229
- const versionMismatch = flags.runtimeFlag ? runtimeMismatch(sim.runtime, flags.runtimeFlag, await listIosRuntimesAsync({ snapshot: simSnapshot })) : null;
230
- if (versionMismatch) throw new IosDeviceMismatchError(`${versionMismatch}. Stim will not silently boot a different iOS version.`);
231
- const model = {
232
- model: deviceTypes.find((d) => d.identifier === sim.deviceTypeIdentifier)?.name ?? null,
233
- runtime: parseRuntimeVersion(sim.runtime)
234
- };
235
- const name = ownedIosName(sim, label, model, projectPath, listed) === sim.name ? sim.name : await withIosDeviceNameLock(() => renameToOwnedName(sim, label, model, projectPath));
236
- const updated = {
237
- deviceUdid: sim.udid,
238
- owned: true,
239
- deviceName: name,
240
- ...record.simslimManaged ? { simslimManaged: true } : {},
241
- ...record.simslimApplied ? { simslimApplied: record.simslimApplied } : {},
242
- ...record.adopted ? { adopted: true } : {},
243
- ...record.adoptionPending ? { adoptionPending: true } : {},
244
- ...record.parkedCacheKey ? { parkedCacheKey: record.parkedCacheKey } : {},
245
- ...record.schemeApprovals ? { schemeApprovals: record.schemeApprovals } : {}
246
- };
247
- const configure = async (freshBoot) => {
248
- if (record.adoptionPending) await resetAdoptedSim(sim.udid, out);
249
- return configureOwnedIosSim({
250
- record: updated,
251
- projectPath,
252
- slot,
253
- profile: simslimProfile,
254
- slimByDefault: simslimDefault,
255
- freshBoot,
256
- out,
257
- reconcileIosSimulator
258
- });
259
- };
260
- const facts = {
261
- deviceType: model.model,
262
- runtime: model.runtime
263
- };
264
- if (sim.state !== "Booted") return {
265
- ...updated,
266
- booting: startIosBoot(sim.udid, () => configure(true), name, out, flags.simulatorApp, projectPath, slot, record.adoptionPending ? "adopted" : "existing", flags.deviceSlotWait),
267
- ...facts
268
- };
269
- const { record: configured, rebooted } = await configure(false);
270
- if (rebooted) await muteBootedSimulator(sim.udid, out);
271
- return {
272
- ...configured,
273
- ...facts,
274
- booted: sim.udid
275
- };
276
- }
277
- } else {
278
- const sim = (await listAllIosSimsAsync()).find((s) => s.udid === record.deviceUdid);
279
- if (sim) {
280
- if (sim.state !== "Booted") {
281
- note(chalk.yellow(`Note: assigned sim ${sim.name} (${sim.udid}) is shut down and is not owned by Stim, so it will not be booted automatically.`));
282
- note(chalk.dim("Boot it yourself, or run `stim gc --delete` to clear the assignment so Stim can create an owned sim."));
283
- }
284
- return record;
285
- }
286
- }
287
- await withConfigLockAsync$1(() => {
288
- const current = deviceSlotPlatforms(loadConfig$1()?.projects?.[projectPath], slot)?.ios;
289
- if (!current) return;
290
- if (current.deviceUdid !== record.deviceUdid || current.owned !== record.owned) throw new Error("The simulator assignment changed during recovery. Run `stim ios` again.");
291
- clearDevice(projectPath, "ios", slot);
292
- });
293
- }
294
- const choice = await resolveIosCreation({
295
- deviceType: flags.deviceType || settings.ios?.deviceType,
296
- runtime: flags.runtime || settings.ios?.runtime
297
- });
298
- const adopted = parkedMaxSetting("ios").max > 0 ? await withIosDeviceNameLock(() => takeParkedIosSim({
299
- projectPath,
300
- slot,
301
- label,
302
- choice,
303
- out
304
- })) : null;
305
- if (adopted) {
306
- const booting = startIosBoot(adopted.deviceUdid, async () => {
307
- await resetAdoptedSim(adopted.deviceUdid, out);
308
- await configureOwnedIosSim({
309
- record: adopted,
310
- projectPath,
311
- slot,
312
- profile: simslimProfile,
313
- slimByDefault: simslimDefault,
314
- freshBoot: true,
315
- out,
316
- reconcileIosSimulator
317
- });
318
- }, adopted.deviceName, out, flags.simulatorApp, projectPath, slot, "adopted", flags.deviceSlotWait);
319
- return {
320
- ...adopted,
321
- booting,
322
- deviceType: choice.deviceType,
323
- runtime: choice.runtime
324
- };
325
- }
326
- const created = await withIosDeviceNameLock(async () => {
327
- const suffix = ownedIosNameSuffix(label, {
328
- model: choice.deviceType,
329
- runtime: choice.runtime
330
- }, projectPath, void 0, await listAllIosSimsAsync({ includeUnavailable: true }));
331
- const result = await createOwnedIosSim(label, { suffix }, choice);
332
- await withConfigLockAsync$1(() => setDevice(projectPath, "ios", {
333
- deviceUdid: result.udid,
334
- owned: true,
335
- deviceName: result.name
336
- }, slot));
337
- return result;
338
- });
339
- const newRecord = {
340
- deviceUdid: created.udid,
341
- owned: true,
342
- deviceName: created.name
343
- };
344
- const booting = startIosBoot(created.udid, () => configureOwnedIosSim({
345
- record: newRecord,
346
- projectPath,
347
- slot,
348
- profile: simslimProfile,
349
- slimByDefault: simslimDefault,
350
- freshBoot: true,
351
- out,
352
- reconcileIosSimulator
353
- }), created.name, out, flags.simulatorApp, projectPath, slot, "created", flags.deviceSlotWait);
354
- return {
355
- ...newRecord,
356
- created: true,
357
- booting,
358
- deviceType: created.deviceType,
359
- runtime: created.runtime
360
- };
361
- }
362
- function withIosDeviceNameLock(fn) {
363
- return withWorkspaceProcessLock(getConfigDir(), "ios-device-names", async () => fn(), { external: true });
364
- }
365
- function ownedIosNameSuffix(label, model, projectPath, udid, listed) {
366
- const names = new Set(listed.filter((sim) => sim.udid !== udid).map((sim) => sim.name));
367
- for (const project of Object.values(loadConfig$1()?.projects ?? {})) for (const { platforms } of projectDeviceSlots(project)) {
368
- const record = platforms.ios;
369
- if (record?.deviceUdid !== udid && record?.deviceName) names.add(record.deviceName);
370
- }
371
- for (const parked of readParked("ios")) if (parked.udid !== udid) names.add(parked.name);
372
- let suffix = "";
373
- for (let attempt = 0; names.has(ownedSimName(label, model, suffix)); attempt++) suffix = ` ${workspaceId$1(projectPath)}${attempt ? `-${attempt}` : ""}`;
374
- return suffix;
375
- }
376
- function ownedIosName(sim, label, model, projectPath, listed) {
377
- return ownedSimName(label, model, ownedIosNameSuffix(label, model, projectPath, sim.udid, listed));
378
- }
379
- async function renameToOwnedName(sim, label, model, projectPath) {
380
- const wanted = ownedIosName(sim, label, model, projectPath, await listAllIosSimsAsync({ includeUnavailable: true }));
381
- if (sim.name === wanted) return wanted;
382
- try {
383
- await renameIosSim(sim.udid, wanted);
384
- return wanted;
385
- } catch {
386
- return sim.name;
387
- }
388
- }
389
- async function resetAdoptedSim(udid, out) {
390
- out(chalk.dim(phaseLine("device", `resetting privacy grants and the keychain on ${udid}`)));
391
- await resetIosPrivacy(udid);
392
- await resetIosKeychain(udid);
393
- }
394
- async function takeParkedIosSim({ projectPath, slot, label, choice, out }) {
395
- const candidates = selectParked(readParked("ios"), {
396
- deviceTypeIdentifier: choice.deviceTypeId,
397
- runtimeIdentifier: choice.runtimeId
398
- });
399
- if (candidates.length === 0) return null;
400
- const listed = new Map((await listAllIosSimsAsync({ includeUnavailable: true })).map((sim) => [sim.udid, sim]));
401
- for (const parked of candidates) {
402
- const sim = listed.get(parked.udid);
403
- if (!sim || !sim.available) {
404
- const result = sim ? await teardownParkedIosSim(parked.udid, { label: parked.name }) : null;
405
- if (sim ? result?.status === "torn-down" : await dropParked("ios", parked.udid)) out(chalk.dim(phaseLine("device", `deleted parked ${parked.name} (${parked.udid}): ${sim ? "unavailable" : "gone"}`)));
406
- else if (result?.status === "failed") out(chalk.yellow(phaseLine("device", `could not delete unavailable parked ${parked.name} (${parked.udid}): ${result.reason}`)));
407
- continue;
408
- }
409
- if (!sim.name.startsWith("stim-")) {
410
- out(chalk.yellow(phaseLine("device", `kept parked record for ${parked.udid}: simulator is now named ${JSON.stringify(sim.name)} and is not Stim-owned`)));
411
- continue;
412
- }
413
- const model = {
414
- model: choice.deviceType,
415
- runtime: choice.runtime
416
- };
417
- const name = ownedSimName(label, model, ownedIosNameSuffix(label, model, projectPath, parked.udid, [...listed.values()]));
418
- const device = {
419
- deviceUdid: parked.udid,
420
- owned: true,
421
- deviceName: name,
422
- adopted: true,
423
- adoptionPending: true,
424
- ...parked.simslimManaged ? { simslimManaged: true } : {},
425
- ...parked.cacheKey ? { parkedCacheKey: parked.cacheKey } : {}
426
- };
427
- const adopted = await adoptParked({
428
- platform: "ios",
429
- projectPath,
430
- slot,
431
- udid: parked.udid,
432
- device
433
- });
434
- if (!adopted) continue;
435
- try {
436
- await renameIosSim(parked.udid, name);
437
- } catch {}
438
- return adopted.schemeApprovals && adopted.schemeApprovals.length > 0 ? {
439
- ...device,
440
- schemeApprovals: adopted.schemeApprovals
441
- } : device;
442
- }
443
- return null;
444
- }
445
- function clearIosAdoptionPending(projectPath, slot = "default") {
446
- return withConfigLockAsync$1(() => {
447
- const cfg = loadConfig$1();
448
- const ios = deviceSlotPlatforms(cfg?.projects?.[projectPath], slot)?.ios;
449
- if (!cfg || !ios?.adoptionPending) return;
450
- delete ios.adopted;
451
- delete ios.adoptionPending;
452
- delete ios.parkedCacheKey;
453
- saveConfig(cfg);
454
- });
455
- }
456
- function simslimProfileIdentity(profile, slimByDefault) {
457
- if (profile) return `profile:${createHash("sha256").update(readFileSync(profile)).digest("hex")}`;
458
- return slimByDefault ? "default" : null;
459
- }
460
- async function startedSpringBoardPid(udid) {
461
- const springBoard = await waitForSpringBoard(udid, Date.now() + SPRINGBOARD_MUTE_WAIT_MS);
462
- return springBoard.state === "started" ? springBoard.pid : null;
463
- }
464
- async function configureOwnedIosSim({ record, projectPath, slot, profile, slimByDefault, freshBoot, out, reconcileIosSimulator }) {
465
- const identity = simslimProfileIdentity(profile, slimByDefault);
466
- const slimConfigured = identity !== null || Boolean(record.simslimManaged);
467
- const applied = record.simslimApplied;
468
- const springBoardBefore = slimConfigured ? await readSpringBoardPid(record.deviceUdid) : null;
469
- if (identity !== null && record.simslimManaged && !freshBoot && applied?.profile === identity && springBoardBefore !== null && springBoardBefore === applied.springBoardPid) {
470
- await withConfigLockAsync$1(() => setDevice(projectPath, "ios", { ...record }, slot));
471
- return {
472
- record,
473
- rebooted: false
474
- };
475
- }
476
- if (profile) out(chalk.dim(phaseLine("device", `applying the SimSlim profile to ${record.deviceUdid}`)));
477
- else if (slimByDefault) out(chalk.dim(phaseLine("device", `applying SimSlim's default services to ${record.deviceUdid}`)));
478
- else if (record.simslimManaged) out(chalk.dim(phaseLine("device", `restoring stock simulator services on ${record.deviceUdid}`)));
479
- const previouslyManaged = Boolean(record.simslimManaged);
480
- if (identity !== null && !record.simslimManaged || applied) {
481
- const { simslimApplied: _applied, ...rest } = record;
482
- const pending = identity !== null ? {
483
- ...rest,
484
- simslimManaged: true
485
- } : rest;
486
- await withConfigLockAsync$1(() => setDevice(projectPath, "ios", pending, slot));
487
- record = pending;
488
- }
489
- const result = await reconcileIosSimulator({
490
- context: ambientContext(),
491
- udid: record.deviceUdid,
492
- profile,
493
- slimByDefault,
494
- previouslyManaged,
495
- out
496
- });
497
- const updated = { ...record };
498
- if (result.managed) updated.simslimManaged = true;
499
- else delete updated.simslimManaged;
500
- const springBoardAfter = slimConfigured ? await startedSpringBoardPid(record.deviceUdid) : null;
501
- if (result.managed && !result.notInstalled && identity !== null && springBoardAfter !== null) updated.simslimApplied = {
502
- profile: identity,
503
- springBoardPid: springBoardAfter
504
- };
505
- await withConfigLockAsync$1(() => setDevice(projectPath, "ios", updated, slot));
506
- const rebooted = slimConfigured && springBoardAfter !== springBoardBefore;
507
- if (rebooted && !result.notInstalled) iosBootRecorder(projectPath, slot, record.deviceUdid)("ios_boot", {
508
- phase: "simslim_reboot",
509
- pidBefore: springBoardBefore,
510
- pidAfter: springBoardAfter,
511
- msg: `ios boot ${record.deviceUdid}: simslim_reboot (SpringBoard pid ${springBoardBefore ?? "unknown"} to ${springBoardAfter ?? "unknown"})`
512
- });
513
- return {
514
- record: updated,
515
- rebooted
516
- };
517
- }
518
- const SPRINGBOARD_RESPAWN_POLLS = 20;
519
- const SPAWN_STALL_TOLERANCE_MS = 18e4;
520
- async function ensureIosBooted(args) {
521
- const { springBoardDown, ...first } = await bootOwnedIosSim(args);
522
- const { device, projectPath, out } = args;
523
- const udid = device?.deviceUdid;
524
- if (!springBoardDown || !device || !udid) return first;
525
- out(chalk.yellow(phaseLine("device", `SpringBoard is not running on ${udid}; restarting the simulator once`)));
526
- const record = iosBootRecorder(projectPath, args.slot, udid);
527
- await preserveSpringBoardReports(record, udid, projectPath);
528
- const stopped = await teardownOwnedIosSim(udid, { workspace: projectPath });
529
- if (stopped.status !== "torn-down") return {
530
- ...first,
531
- reason: `${first.reason} The restart did not run: ${stopped.reason ?? stopped.status}.`
532
- };
533
- record("ios_boot", {
534
- phase: "restart",
535
- level: "warn",
536
- msg: `ios boot ${udid}: restart (SpringBoard is not running after boot; restarting once)`
537
- });
538
- const { booted: _booted, booting: _booting, ...unbooted } = device;
539
- const { springBoardDown: stillDown, ...second } = await bootOwnedIosSim({
540
- ...args,
541
- device: unbooted,
542
- origin: "restart"
543
- });
544
- if (stillDown) await preserveSpringBoardReports(record, udid, projectPath);
545
- return stillDown ? {
546
- ...second,
547
- reason: `${second.reason} Stim already restarted the simulator once, so it did not restart it again.`
548
- } : second;
549
- }
550
- async function preserveSpringBoardReports(record, udid, root) {
551
- const dir = workspaceLogsDir$1(root);
552
- try {
553
- const { files, dropped, failure } = await copySpringBoardCrashReports({
554
- udid,
555
- destDir: dir
556
- });
557
- const limit = dropped ? `; ${dropped} more matched but exceeded the copy limits` : "";
558
- record("springboard_crash_reports", {
559
- level: files.length || dropped || failure ? "warn" : "info",
560
- files,
561
- dropped,
562
- ...failure ? { failure } : {},
563
- dir,
564
- msg: failure ? `could not copy a SpringBoard crash report for ${udid} (${files.length} copied before it): ${failure}` : files.length ? `copied ${files.length} SpringBoard crash report(s) for ${udid} to ${dir}: ${files.join(", ")}${limit}` : dropped ? `${dropped} SpringBoard crash report(s) for ${udid} matched but exceeded the copy limits` : `no SpringBoard crash report for ${udid} from the last 10 minutes`
565
- });
566
- } catch (error) {
567
- record("springboard_crash_reports", {
568
- level: "warn",
569
- files: [],
570
- dropped: 0,
571
- failure: error instanceof Error ? error.message : String(error),
572
- dir,
573
- msg: `could not copy SpringBoard crash reports for ${udid}: ${error instanceof Error ? error.message : String(error)}`
574
- });
575
- }
576
- }
577
- async function bootOwnedIosSim({ device, projectPath, slot, origin, deviceSlotWait, simulatorApp, timeoutMs, pollMs, out }) {
578
- const udid = device?.deviceUdid;
579
- if (!udid) return {
580
- failed: true,
581
- reason: "No iOS simulator is recorded for this project."
582
- };
583
- const onPhase = recordBootPhase(iosBootRecorder(projectPath, slot, udid), udid, origin ?? (device.adoptionPending ? "adopted" : "existing"));
584
- const openViewer = async () => {
585
- await configuredIosSimulatorViewer(simulatorApp, ambientContext()?.env).open(udid);
586
- onPhase({ phase: "viewer_opened" });
587
- };
588
- const ready = async () => {
589
- try {
590
- const stallDeadline = Date.now() + SPAWN_STALL_TOLERANCE_MS;
591
- const listSpringBoard = async () => {
592
- for (;;) try {
593
- return await getExecutor().runFileAsync("xcrun", [
594
- "simctl",
595
- "spawn",
596
- udid,
597
- "launchctl",
598
- "list"
599
- ], { timeoutMs: 3e4 });
600
- } catch (error) {
601
- if (!isTimeoutError(error) || Date.now() >= stallDeadline) throw error;
602
- const sims = await listAllIosSimsAsync().catch(() => null);
603
- if (sims && sims.find((s) => s.udid === udid)?.state !== "Booted") throw error;
604
- }
605
- };
606
- let listing = await listSpringBoard();
607
- for (let poll = 0; !springBoardRunning(listing) && poll < SPRINGBOARD_RESPAWN_POLLS; poll++) {
608
- await cancellableSleep(pollMs);
609
- listing = await listSpringBoard();
610
- }
611
- if (!springBoardRunning(listing)) return {
612
- failed: true,
613
- springBoardDown: true,
614
- reason: `Simulator ${udid} reports Booted, but SpringBoard (the system shell) is not running, so apps cannot launch. Run \`stim stop\`, then \`stim ios\` again. If it repeats, look for SpringBoard crash reports in ~/Library/Logs/DiagnosticReports.`
615
- };
616
- return {
617
- ok: true,
618
- udid
619
- };
620
- } catch (error) {
621
- return {
622
- failed: true,
623
- reason: `Simulator ${udid} failed its process-spawn readiness check: ${error?.message || error}. ${await iosSimulatorFailureAdvice()}`
624
- };
625
- }
626
- };
627
- const booting = device?.booting;
628
- if (booting?.udid === udid) {
629
- try {
630
- await booting.done;
631
- } catch (e) {
632
- return bootFailure(udid, e);
633
- }
634
- return ready();
635
- }
636
- const readiness = device?.booted === udid ? await ready() : null;
637
- if (readiness?.ok) {
638
- if (simulatorApp !== void 0) await openViewer();
639
- return readiness;
640
- }
641
- let resolved;
642
- try {
643
- resolved = resolveOwnedIosSim(udid, await listAllIosSimsAsync());
644
- } catch (e) {
645
- return {
646
- failed: true,
647
- reason: `Could not list simulators: ${e?.message || e}`
648
- };
649
- }
650
- if (resolved.missing) return {
651
- failed: true,
652
- reason: `Simulator ${udid} no longer exists. Run \`stim ios\` again to create a fresh owned sim.`
653
- };
654
- if (resolved.notOwned) return {
655
- failed: true,
656
- reason: `Simulator ${udid} is now named "${resolved.notOwned}" and is not Stim-owned; refusing to boot it.`
657
- };
658
- const sim = resolved.sim;
659
- if (sim.state === "Booted") {
660
- const result = readiness ?? await ready();
661
- if (result.ok && simulatorApp !== void 0) await openViewer();
662
- return result;
663
- }
664
- const bootDeadline = Date.now() + timeoutMs;
665
- try {
666
- await withDeviceBootAdmission({
667
- platform: "ios",
668
- key: udid
669
- }, async () => {
670
- out(chalk.dim(phaseLine("device", `booting ${sim.name} (${udid})`)));
671
- await bootIosSim(udid, {
672
- timeoutMs,
673
- label: sim.name,
674
- out,
675
- simulatorApp,
676
- env: ambientContext()?.env,
677
- onPhase
678
- });
679
- await muteBootedSimulator(udid, out);
680
- }, {
681
- ...deviceSlotWait,
682
- root: projectPath,
683
- out
684
- });
685
- } catch (e) {
686
- return bootFailure(udid, e);
687
- }
688
- const deadline = Math.max(bootDeadline, Date.now() + 2 * pollMs);
689
- while (Date.now() < deadline) {
690
- await cancellableSleep(pollMs);
691
- let state = null;
692
- try {
693
- state = (await listAllIosSimsAsync({ timeoutMs: 3e4 })).find((s) => s.udid === udid)?.state ?? null;
694
- } catch {}
695
- if (state === "Booted") return ready();
696
- }
697
- return {
698
- failed: true,
699
- reason: `Simulator ${udid} did not reach the Booted state within ${Math.round(timeoutMs / 1e3)}s.`
700
- };
701
- }
702
- function bootFailure(udid, error) {
703
- const refusal = error instanceof DeviceAdmissionRefusal ? error : (0, ownership_claim_exports.claimFailure)(error, "stim ios");
704
- if (refusal) return {
705
- failed: true,
706
- code: refusal.code,
707
- reason: refusal.message,
708
- remedy: refusal.remedy
709
- };
710
- return {
711
- failed: true,
712
- reason: `Could not boot simulator ${udid}: ${error?.message || error}`
713
- };
714
- }
715
- //#endregion
716
- export { muteBootedSimulator as a, ensureOwnedIosDevice as i, clearIosAdoptionPending as n, ensureIosBooted as r, IosDeviceMismatchError as t };