stim 1.2.0 → 1.3.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 (59) hide show
  1. package/README.md +30 -0
  2. package/dist/android-BqVIJoG7.mjs +1368 -0
  3. package/dist/{android-Bcmev2s2.mjs → android-CqcW1hrP.mjs} +148 -56
  4. package/dist/{android-cas-D6HPZ6UQ.mjs → android-cas-BR5YJBYv.mjs} +5 -5
  5. package/dist/{app-install-NFxUahnH.mjs → app-install-DIp9riNx.mjs} +6 -6
  6. package/dist/{build-slots-D0b6sJyo.mjs → build-slots-C3-R4XQr.mjs} +2 -2
  7. package/dist/{cache-manifest-Cu4_Znw9.mjs → cache-manifest-CXR-Y8pH.mjs} +1 -1
  8. package/dist/cache-manifest.mjs +1 -1
  9. package/dist/cli.mjs +13 -13
  10. package/dist/collector-run.d.mts +3 -1
  11. package/dist/collector-run.mjs +33 -8
  12. package/dist/{config-CMtIaTk9.mjs → config-CcefMY2Q.mjs} +93 -12
  13. package/dist/{deps-BLBsTNDS.mjs → deps-Spz8ozgo.mjs} +1 -1
  14. package/dist/{dev-client-RpvXxCXV.mjs → dev-client-BjisHDB5.mjs} +186 -9
  15. package/dist/{device-CoCPmazS.mjs → device-BkiW-gkk.mjs} +19 -11
  16. package/dist/{device-lease-Dpik41RY.mjs → device-lease-BJYybF-2.mjs} +107 -83
  17. package/dist/{device-pool-B5pEVa3t.mjs → device-pool-CPbLGfrB.mjs} +18 -12
  18. package/dist/{device-remote-E81KAdXf.mjs → device-remote-AkYCKS8U.mjs} +7 -7
  19. package/dist/{doctor-CqPeMsSm.mjs → doctor-Bmv86UUW.mjs} +11 -12
  20. package/dist/{doctor-D_CX7ib5.mjs → doctor-fAMXC4vl.mjs} +4 -4
  21. package/dist/{error-diagnostics-Dw8Gn3yw.mjs → error-diagnostics-DHC31rXA.mjs} +62 -52
  22. package/dist/{gc-B7KkDinF.mjs → gc-GGu-8ULN.mjs} +66 -57
  23. package/dist/{guide-BS2Xc24o.mjs → guide-92CPA21s.mjs} +196 -13
  24. package/dist/{ios-B7KLgIp5.mjs → ios-BSXFeO5H.mjs} +183 -54
  25. package/dist/{ios-D1h1Di39.mjs → ios-Deh5js5V.mjs} +78 -40
  26. package/dist/{ios-device-xNt0Lg6v.mjs → ios-device-CWOM_S44.mjs} +1 -1
  27. package/dist/{logs-DcX9AvRF.mjs → logs-DPNF4C9s.mjs} +9 -6
  28. package/dist/{logs-query-oqn47Ffx.mjs → logs-query-R-Cqc6Qv.mjs} +22 -5
  29. package/dist/{metro-COslRK_F.mjs → metro-CORqXyTL.mjs} +2 -2
  30. package/dist/{ndjson-DcAtEx_K.mjs → ndjson-BZwI1NBv.mjs} +5 -2
  31. package/dist/{ownership-CGPart6T.mjs → ownership-8O073GIC.mjs} +3 -3
  32. package/dist/{ownership-CYOyqxUo.mjs → ownership-BpCKiRlQ.mjs} +3 -3
  33. package/dist/{ownership-claim-7zRxxTfZ.mjs → ownership-claim-Bf2CAfw3.mjs} +4 -3
  34. package/dist/{project-BBTVABUz.mjs → project-fKmuIaMI.mjs} +2 -2
  35. package/dist/{reclaim-C5Jmuahx.mjs → reclaim-iW9Cad7k.mjs} +80 -74
  36. package/dist/{reload-Z8nKifAk.mjs → reload-DazHDSFA.mjs} +32 -24
  37. package/dist/{server-bare-DEk37Mz2.mjs → server-bare-DEzPZLvf.mjs} +2 -2
  38. package/dist/server-expo-44em5Pmg.mjs +2 -0
  39. package/dist/{server-expo-C3ktAwUG.mjs → server-expo-DizT4OMk.mjs} +4 -4
  40. package/dist/{settings-DwU1_U_q.mjs → settings-DEJf6_kJ.mjs} +2 -2
  41. package/dist/slot-launch-cci0xvmC.mjs +19 -0
  42. package/dist/{start-BrY4EuIg.mjs → start-mObjQH2W.mjs} +11 -11
  43. package/dist/{state-CZ-ER-8d.mjs → state-B3XA2wp2.mjs} +9 -9
  44. package/dist/{stats-BCWEVLrM.mjs → stats-BR-kwj0B.mjs} +1 -1
  45. package/dist/{stats-BsF15Bti.mjs → stats-CaaQr4yN.mjs} +3 -3
  46. package/dist/{status-DFI1Hfpg.mjs → status-BxQNqu9t.mjs} +55 -24
  47. package/dist/{stop-B7DR4awK.mjs → stop-CrHGcjrV.mjs} +2 -2
  48. package/dist/{stop-Btg770vV.mjs → stop-DimGXEKY.mjs} +93 -42
  49. package/dist/supervisor-run.d.mts +1 -2
  50. package/dist/supervisor-run.mjs +5 -5
  51. package/dist/{remote-cache-DDmJ1Roq.mjs → teardown-ClFp1Aax.mjs} +887 -888
  52. package/dist/{workspace-process-lock-BKfdys1q.mjs → workspace-process-lock-3NcQnZ0X.mjs} +1 -1
  53. package/dist/{worktree-BPTbI-LN.mjs → worktree-DxTgN1CJ.mjs} +12 -12
  54. package/dist/{worktree-C79xx_kM.mjs → worktree-Eq6TLH8N.mjs} +1 -1
  55. package/dist/{xcode-DeKCribl.mjs → xcode-D7sAp-Zy.mjs} +82 -56
  56. package/package.json +5 -5
  57. package/dist/android-K63_0UfF.mjs +0 -727
  58. package/dist/build-lock-BsVoLDaq.mjs +0 -645
  59. package/dist/server-expo-Drd7ksHu.mjs +0 -2
@@ -1,6 +1,6 @@
1
- import { k as withDirLock, u as getConfigDir } from "./config-CMtIaTk9.mjs";
2
- import { _ as writeWorkspaceState, o as clearWorkspaceStateKeys, p as readWorkspaceState } from "./state-CZ-ER-8d.mjs";
3
- import { a as segment } from "./build-lock-BsVoLDaq.mjs";
1
+ import { I as deviceSlotKey, R as parseDeviceSlotKey, V as validateDeviceSlot, k as withDirLock, u as getConfigDir } from "./config-CcefMY2Q.mjs";
2
+ import { _ as writeWorkspaceState, m as withWorkspaceStateLock, o as clearWorkspaceStateKeys, p as readWorkspaceState } from "./state-B3XA2wp2.mjs";
3
+ import { H as segment } from "./android-BqVIJoG7.mjs";
4
4
  import { mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
5
5
  import { randomBytes } from "node:crypto";
6
6
  import { join } from "node:path";
@@ -21,6 +21,7 @@ function deviceLeaseLockPath(platform, id) {
21
21
  return join(deviceLocksDir(), `${leaseStem(platform, id)}${LOCK_SUFFIX}`);
22
22
  }
23
23
  const fileLeaseIo = {
24
+ withHolderLock: withWorkspaceStateLock,
24
25
  now: () => Date.now(),
25
26
  readLease(path) {
26
27
  try {
@@ -83,8 +84,17 @@ function parseLease(raw) {
83
84
  const token = text(entry.token);
84
85
  const expiresAt = text(entry.expiresAt);
85
86
  if (!id || !holder || !token || !expiresAt || !Number.isFinite(Date.parse(expiresAt))) return null;
87
+ if (entry.slot !== void 0) {
88
+ if (typeof entry.slot !== "string") return null;
89
+ try {
90
+ validateDeviceSlot(entry.slot);
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
86
95
  return {
87
96
  version: 1,
97
+ ...typeof entry.slot === "string" ? { slot: entry.slot } : {},
88
98
  platform: entry.platform,
89
99
  id,
90
100
  deviceName: text(entry.deviceName),
@@ -98,7 +108,7 @@ function parseWorkspaceLeases(value) {
98
108
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
99
109
  const leases = {};
100
110
  for (const [platform, entry] of Object.entries(value)) {
101
- if (!isPlatform(platform) || !entry || typeof entry !== "object") continue;
111
+ if (!parseDeviceSlotKey(platform) || !entry || typeof entry !== "object") continue;
102
112
  const record = entry;
103
113
  const id = text(record.id);
104
114
  const token = text(record.token);
@@ -145,70 +155,74 @@ function writeLease(lease, io) {
145
155
  io.writeLease(deviceLeasePath(lease.platform, lease.id), `${JSON.stringify(lease, null, 2)}\n`);
146
156
  return lease;
147
157
  }
148
- function takeLease({ root, platform, id, deviceName = null, kind, durationMs = DEFAULT_LEASE_MS }, io = fileLeaseIo) {
149
- const previous = io.readHolder(root)[platform];
150
- if (previous && previous.id !== id) releaseHeld(root, platform, previous, io);
151
- for (const entry of listLeaseFiles(io)) {
152
- const other = entry.lease;
153
- if (!other || other.platform !== platform || other.holder !== root || other.id === id) continue;
154
- releaseByRoot(root, platform, other.id, io);
155
- }
156
- const declared = previous?.kind === "declared" && previous.id === id;
157
- const path = deviceLeasePath(platform, id);
158
- const result = io.withLeaseLock(deviceLeaseLockPath(platform, id), () => {
159
- const now = io.now();
160
- const raw = io.readLease(path);
161
- const current = parseLease(raw);
162
- if (raw !== null && !current) return {
163
- status: "unreadable",
164
- path
165
- };
166
- const asked = now + durationMs;
167
- if (current && !leaseIsExpired(current, now)) {
168
- if (current.holder !== root) return {
169
- status: "held",
170
- lease: current
158
+ function takeLease({ root, platform, slot = "default", id, deviceName = null, kind, durationMs = DEFAULT_LEASE_MS }, io = fileLeaseIo) {
159
+ return withHolderLock(io, root, () => {
160
+ const key = deviceSlotKey(platform, slot);
161
+ const previous = io.readHolder(root)[key];
162
+ if (previous && previous.id !== id) releaseHeld(root, platform, previous, io, slot);
163
+ for (const entry of listLeaseFiles(io)) {
164
+ const other = entry.lease;
165
+ if (!other || other.platform !== platform || other.holder !== root || (other.slot ?? "default") !== slot || other.id === id) continue;
166
+ releaseByRoot(root, platform, other.id, io);
167
+ }
168
+ const declared = previous?.kind === "declared" && previous.id === id;
169
+ const path = deviceLeasePath(platform, id);
170
+ const result = io.withLeaseLock(deviceLeaseLockPath(platform, id), () => {
171
+ const now = io.now();
172
+ const raw = io.readLease(path);
173
+ const current = parseLease(raw);
174
+ if (raw !== null && !current) return {
175
+ status: "unreadable",
176
+ path
171
177
  };
172
- const floor = declared && kind === "run" ? Math.max(asked, Date.parse(current.expiresAt)) : asked;
178
+ const asked = now + durationMs;
179
+ if (current && !leaseIsExpired(current, now)) {
180
+ if (current.holder !== root || (current.slot ?? "default") !== slot) return {
181
+ status: "held",
182
+ lease: current
183
+ };
184
+ const floor = declared && kind === "run" ? Math.max(asked, Date.parse(current.expiresAt)) : asked;
185
+ return {
186
+ status: "set",
187
+ lease: writeLease({
188
+ ...current,
189
+ deviceName: deviceName ?? current.deviceName,
190
+ expiresAt: new Date(floor).toISOString()
191
+ }, io)
192
+ };
193
+ }
194
+ const expiresAt = new Date(asked).toISOString();
173
195
  return {
174
- status: "set",
196
+ status: "taken",
175
197
  lease: writeLease({
176
- ...current,
177
- deviceName: deviceName ?? current.deviceName,
178
- expiresAt: new Date(floor).toISOString()
198
+ version: 1,
199
+ ...slot === "default" ? {} : { slot },
200
+ platform,
201
+ id,
202
+ deviceName,
203
+ holder: root,
204
+ token: randomBytes(12).toString("hex"),
205
+ grantedAt: new Date(now).toISOString(),
206
+ expiresAt
179
207
  }, io)
180
208
  };
209
+ });
210
+ if (result.status === "taken" || result.status === "set") {
211
+ const recorded = result.status === "set" && declared ? "declared" : kind;
212
+ io.writeHolder(root, {
213
+ ...io.readHolder(root),
214
+ [key]: {
215
+ id,
216
+ token: result.lease.token,
217
+ kind: recorded
218
+ }
219
+ });
181
220
  }
182
- const expiresAt = new Date(asked).toISOString();
183
- return {
184
- status: "taken",
185
- lease: writeLease({
186
- version: 1,
187
- platform,
188
- id,
189
- deviceName,
190
- holder: root,
191
- token: randomBytes(12).toString("hex"),
192
- grantedAt: new Date(now).toISOString(),
193
- expiresAt
194
- }, io)
195
- };
221
+ return result;
196
222
  });
197
- if (result.status === "taken" || result.status === "set") {
198
- const recorded = result.status === "set" && declared ? "declared" : kind;
199
- io.writeHolder(root, {
200
- ...io.readHolder(root),
201
- [platform]: {
202
- id,
203
- token: result.lease.token,
204
- kind: recorded
205
- }
206
- });
207
- }
208
- return result;
209
223
  }
210
- function raiseLease({ root, platform, minMs }, io = fileLeaseIo) {
211
- const record = io.readHolder(root)[platform];
224
+ function raiseLease({ root, platform, slot = "default", minMs }, io = fileLeaseIo) {
225
+ const record = io.readHolder(root)[deviceSlotKey(platform, slot)];
212
226
  if (!record) return { status: "none" };
213
227
  const path = deviceLeasePath(platform, record.id);
214
228
  return io.withLeaseLock(deviceLeaseLockPath(platform, record.id), () => {
@@ -239,7 +253,8 @@ function summarize(lease) {
239
253
  expiresAt: lease.expiresAt
240
254
  };
241
255
  }
242
- function releaseHeld(root, platform, record, io) {
256
+ function releaseHeld(root, platform, record, io, slot = "default") {
257
+ const key = deviceSlotKey(platform, slot);
243
258
  const path = deviceLeasePath(platform, record.id);
244
259
  const released = io.withLeaseLock(deviceLeaseLockPath(platform, record.id), () => {
245
260
  const current = parseLease(io.readLease(path));
@@ -248,8 +263,8 @@ function releaseHeld(root, platform, record, io) {
248
263
  return current;
249
264
  });
250
265
  const held = io.readHolder(root);
251
- if (held[platform]?.token === record.token) {
252
- delete held[platform];
266
+ if (held[key]?.token === record.token) {
267
+ delete held[key];
253
268
  io.writeHolder(root, held);
254
269
  }
255
270
  return released;
@@ -263,27 +278,33 @@ function releaseByRoot(root, platform, id, io) {
263
278
  return current;
264
279
  });
265
280
  }
266
- function releaseRunLease({ root, platform }, io = fileLeaseIo) {
267
- const record = io.readHolder(root)[platform];
268
- if (!record || record.kind !== "run") return null;
269
- const released = releaseHeld(root, platform, record, io);
270
- return released ? summarize(released) : null;
281
+ function releaseRunLease({ root, platform, slot = "default" }, io = fileLeaseIo) {
282
+ return withHolderLock(io, root, () => {
283
+ const record = io.readHolder(root)[deviceSlotKey(platform, slot)];
284
+ if (!record || record.kind !== "run") return null;
285
+ const released = releaseHeld(root, platform, record, io, slot);
286
+ return released ? summarize(released) : null;
287
+ });
271
288
  }
272
- function releaseWorkspaceLeases(root, { platform = null } = {}, io = fileLeaseIo) {
273
- const released = [];
274
- for (const [name, record] of Object.entries(io.readHolder(root))) {
275
- if (!isPlatform(name) || platform && name !== platform) continue;
276
- const lease = releaseHeld(root, name, record, io);
277
- if (lease) released.push(summarize(lease));
278
- }
279
- for (const entry of listLeaseFiles(io)) {
280
- const lease = entry.lease;
281
- if (!lease || lease.holder !== root) continue;
282
- if (platform && lease.platform !== platform) continue;
283
- const byRoot = releaseByRoot(root, lease.platform, lease.id, io);
284
- if (byRoot) released.push(summarize(byRoot));
285
- }
286
- return released;
289
+ function releaseWorkspaceLeases(root, { platform = null, slot = null } = {}, io = fileLeaseIo) {
290
+ return withHolderLock(io, root, () => {
291
+ const released = [];
292
+ for (const [name, record] of Object.entries(io.readHolder(root))) {
293
+ const parsed = parseDeviceSlotKey(name);
294
+ if (!parsed || platform && parsed.platform !== platform || slot !== null && parsed.slot !== slot) continue;
295
+ const lease = releaseHeld(root, parsed.platform, record, io, parsed.slot);
296
+ if (lease) released.push(summarize(lease));
297
+ }
298
+ for (const entry of listLeaseFiles(io)) {
299
+ const lease = entry.lease;
300
+ if (!lease || lease.holder !== root) continue;
301
+ if (platform && lease.platform !== platform) continue;
302
+ if (slot !== null && (lease.slot ?? "default") !== slot) continue;
303
+ const byRoot = releaseByRoot(root, lease.platform, lease.id, io);
304
+ if (byRoot) released.push(summarize(byRoot));
305
+ }
306
+ return released;
307
+ });
287
308
  }
288
309
  function removeExpiredLease(entry, io = fileLeaseIo) {
289
310
  const lease = entry.lease;
@@ -331,5 +352,8 @@ function selectPoolDevice({ candidates, leases, held = null, now }) {
331
352
  holders
332
353
  };
333
354
  }
355
+ function withHolderLock(io, root, fn) {
356
+ return io.withHolderLock ? io.withHolderLock(root, fn) : fn();
357
+ }
334
358
  //#endregion
335
359
  export { parseLease as a, releaseRunLease as c, selectPoolDevice as d, takeLease as f, listLeaseFiles as i, releaseWorkspaceLeases as l, fileLeaseIo as n, parseLeaseDuration as o, leaseIsExpired as r, raiseLease as s, deviceLeasePath as t, removeExpiredLease as u };
@@ -1,6 +1,7 @@
1
+ import { I as deviceSlotKey } from "./config-CcefMY2Q.mjs";
1
2
  import { a as formatElapsed, r as clockTime } from "./command-output-8wHMEde0.mjs";
2
- import { A as APP_READINESS_TIMEOUT_MS, l as VERIFY_TIMEOUT_MS } from "./app-install-NFxUahnH.mjs";
3
- import { a as parseLease, c as releaseRunLease, d as selectPoolDevice, f as takeLease, n as fileLeaseIo, r as leaseIsExpired, s as raiseLease, t as deviceLeasePath } from "./device-lease-Dpik41RY.mjs";
3
+ import { A as APP_READINESS_TIMEOUT_MS, l as VERIFY_TIMEOUT_MS } from "./app-install-DIp9riNx.mjs";
4
+ import { a as parseLease, c as releaseRunLease, d as selectPoolDevice, f as takeLease, n as fileLeaseIo, r as leaseIsExpired, s as raiseLease, t as deviceLeasePath } from "./device-lease-BJYybF-2.mjs";
4
5
  //#region src/engine/device-lease-run.ts
5
6
  const LEASE_STEP_FLOOR_MS = 6e4;
6
7
  const DEBUG_VERIFY_STEP_MS = VERIFY_TIMEOUT_MS + APP_READINESS_TIMEOUT_MS;
@@ -101,7 +102,7 @@ function lostRefusal(holder, expiresAt, now) {
101
102
  lease: null
102
103
  };
103
104
  }
104
- function runLease({ root, platform, kind, expiresAt, io = fileLeaseIo }) {
105
+ function runLease({ root, platform, slot = "default", kind, expiresAt, io = fileLeaseIo }) {
105
106
  const handle = {
106
107
  kind,
107
108
  expiresAt,
@@ -115,6 +116,7 @@ function runLease({ root, platform, kind, expiresAt, io = fileLeaseIo }) {
115
116
  const result = raiseLease({
116
117
  root,
117
118
  platform,
119
+ slot,
118
120
  minMs: leaseStepMs(boundMs)
119
121
  }, io);
120
122
  if (result.status === "raised") {
@@ -137,7 +139,8 @@ function runLease({ root, platform, kind, expiresAt, io = fileLeaseIo }) {
137
139
  if (handle.kind === null) return;
138
140
  releaseRunLease({
139
141
  root,
140
- platform
142
+ platform,
143
+ slot
141
144
  }, io);
142
145
  },
143
146
  facts() {
@@ -173,7 +176,7 @@ function releaseLeaseOnSignal(release, { on = (signal, handler) => {
173
176
  installed.length = 0;
174
177
  };
175
178
  }
176
- async function waitForDevice({ root, platform, id, idLabel, waitSeconds, noWait = false, appId = null, holderAppId = () => null, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), warn = () => {}, io = fileLeaseIo, deadline, lastLine }) {
179
+ async function waitForDevice({ root, platform, slot = "default", id, idLabel, waitSeconds, noWait = false, appId = null, holderAppId = () => null, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), warn = () => {}, io = fileLeaseIo, deadline, lastLine }) {
177
180
  const path = deviceLeasePath(platform, id);
178
181
  for (;;) {
179
182
  const raw = io.readLease(path);
@@ -183,7 +186,7 @@ async function waitForDevice({ root, platform, id, idLabel, waitSeconds, noWait
183
186
  refusal: unreadableRefusal(path)
184
187
  };
185
188
  if (!current || leaseIsExpired(current, now())) return { status: "free" };
186
- if (current.holder === root) return {
189
+ if (current.holder === root && (current.slot ?? "default") === slot) return {
187
190
  status: "ours",
188
191
  lease: current
189
192
  };
@@ -210,8 +213,8 @@ async function waitForDevice({ root, platform, id, idLabel, waitSeconds, noWait
210
213
  await sleep(DEVICE_WAIT_POLL_MS);
211
214
  }
212
215
  }
213
- async function acquireRunLease({ root, platform, id, deviceName = null, idLabel, waitSeconds, noWait, installBoundMs, appId = null, holderAppId = () => null, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), warn = () => {}, io = fileLeaseIo }) {
214
- const recorded = io.readHolder(root)[platform];
216
+ async function acquireRunLease({ root, platform, slot = "default", id, deviceName = null, idLabel, waitSeconds, noWait, installBoundMs, appId = null, holderAppId = () => null, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), warn = () => {}, io = fileLeaseIo }) {
217
+ const recorded = io.readHolder(root)[deviceSlotKey(platform, slot)];
215
218
  let held = recorded;
216
219
  if (recorded && recorded.id !== id) {
217
220
  const other = parseLease(io.readLease(deviceLeasePath(platform, recorded.id)));
@@ -234,6 +237,7 @@ async function acquireRunLease({ root, platform, id, deviceName = null, idLabel,
234
237
  const raised = raiseLease({
235
238
  root,
236
239
  platform,
240
+ slot,
237
241
  minMs: leaseStepMs(installBoundMs)
238
242
  }, io);
239
243
  if (raised.status === "raised") return {
@@ -245,6 +249,7 @@ async function acquireRunLease({ root, platform, id, deviceName = null, idLabel,
245
249
  const outcome = await waitForDevice({
246
250
  root,
247
251
  platform,
252
+ slot,
248
253
  id,
249
254
  idLabel,
250
255
  waitSeconds,
@@ -267,6 +272,7 @@ async function acquireRunLease({ root, platform, id, deviceName = null, idLabel,
267
272
  const taken = takeLease({
268
273
  root,
269
274
  platform,
275
+ slot,
270
276
  id,
271
277
  deviceName,
272
278
  kind: "run",
@@ -285,8 +291,8 @@ async function acquireRunLease({ root, platform, id, deviceName = null, idLabel,
285
291
  }
286
292
  //#endregion
287
293
  //#region src/engine/device-pool.ts
288
- function heldPoolId(root, platform, now, io = fileLeaseIo) {
289
- const record = io.readHolder(root)[platform];
294
+ function heldPoolId(root, platform, now, io = fileLeaseIo, slot = "default") {
295
+ const record = io.readHolder(root)[deviceSlotKey(platform, slot)];
290
296
  if (!record) return null;
291
297
  const lease = parseLease(io.readLease(deviceLeasePath(platform, record.id)));
292
298
  if (!lease || lease.token !== record.token || leaseIsExpired(lease, now)) return null;
@@ -352,8 +358,8 @@ function poolBusyRefusal(holders, leases, unreadable, now, waitSeconds) {
352
358
  }
353
359
  };
354
360
  }
355
- async function selectFromPool({ root, platform, idLabel, list, noCandidates, waitSeconds, deadline, noWait = false, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), warn = () => {}, io = fileLeaseIo }) {
356
- const held = heldPoolId(root, platform, now(), io);
361
+ async function selectFromPool({ root, platform, slot = "default", idLabel, list, noCandidates, waitSeconds, deadline, noWait = false, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), warn = () => {}, io = fileLeaseIo }) {
362
+ const held = heldPoolId(root, platform, now(), io, slot);
357
363
  const until = deadline ?? now() + waitSeconds * 1e3;
358
364
  let lastLine = null;
359
365
  for (;;) {
@@ -1,11 +1,11 @@
1
1
  import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { k as withDirLock, u as getConfigDir } from "./config-CMtIaTk9.mjs";
3
- import { D as workspaceLogsDir, O as workspaceName, T as workspaceDir, a as clearRemoteSession, c as readMetroTunnel, k as workspaceStateFile, u as readRemoteSession } from "./state-CZ-ER-8d.mjs";
4
- import { r as pidExists } from "./metro-COslRK_F.mjs";
5
- import { z as planMetroReach } from "./settings-DwU1_U_q.mjs";
6
- import { T as readMetroRecords, a as LAUNCH_ERROR, b as isBundleProof, p as devClientDeepLink, r as INSTALL_ERROR } from "./app-install-NFxUahnH.mjs";
7
- import { S as sanitizeDeviceLabel } from "./ios-D1h1Di39.mjs";
8
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BKfdys1q.mjs";
2
+ import { k as withDirLock, u as getConfigDir } from "./config-CcefMY2Q.mjs";
3
+ import { D as workspaceLogsDir, O as workspaceName, T as workspaceDir, a as clearRemoteSession, c as readMetroTunnel, k as workspaceStateFile, u as readRemoteSession } from "./state-B3XA2wp2.mjs";
4
+ import { r as pidExists } from "./metro-CORqXyTL.mjs";
5
+ import { z as planMetroReach } from "./settings-DEJf6_kJ.mjs";
6
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-3NcQnZ0X.mjs";
7
+ import { T as readMetroRecords, a as LAUNCH_ERROR, b as isBundleProof, p as devClientDeepLink, r as INSTALL_ERROR } from "./app-install-DIp9riNx.mjs";
8
+ import { S as sanitizeDeviceLabel } from "./ios-Deh5js5V.mjs";
9
9
  import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
10
10
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
11
11
  import { homedir } from "node:os";
@@ -1,17 +1,16 @@
1
1
  import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { g as loadConfig, l as getConcurrencyLimits, o as configCorruptRepair } from "./config-CMtIaTk9.mjs";
3
- import { b as sharedBuildCache, w as workspaceDerivedData } from "./state-CZ-ER-8d.mjs";
4
- import { S as makeTemporaryDirectory, a as dirtyFingerprintFiles, f as listWorktrees, g as repoRoot, s as gitCommonDir, w as temporaryRoot, x as filesystemDevice } from "./worktree-C79xx_kM.mjs";
5
- import { i as detectIsExpo, t as appProjectProblem } from "./project-BBTVABUz.mjs";
6
- import { d as resolveEasCliBin, i as checkEasAuth$1, l as ownerFromConfig, u as providerFromConfig, v as POOL_SETTING_REMEDY, x as parkedMaxSetting } from "./remote-cache-DDmJ1Roq.mjs";
7
- import { A as resolveSettings, E as remoteAndroidSetting, I as unknownSettingKeys, M as settingOrigin, N as settingShapeErrors, O as remoteIosSetting, P as settingsLayers, U as resolveOptimizations, V as compilerCacheFallbackMessage, a as SETTING_SHAPE_REMEDY, i as SETTINGS_WITH_RESOLVE_TIME_FALLBACK, n as PATH_SETTINGS, v as iosSimSlimProfileSetting, w as projectOptimizations, y as mergeSettingsLayers } from "./settings-DwU1_U_q.mjs";
2
+ import { g as loadConfig, l as getConcurrencyLimits, o as configCorruptRepair } from "./config-CcefMY2Q.mjs";
3
+ import { b as sharedBuildCache, w as workspaceDerivedData } from "./state-B3XA2wp2.mjs";
4
+ import { S as makeTemporaryDirectory, a as dirtyFingerprintFiles, f as listWorktrees, g as repoRoot, s as gitCommonDir, w as temporaryRoot, x as filesystemDevice } from "./worktree-Eq6TLH8N.mjs";
5
+ import { i as detectIsExpo, t as appProjectProblem } from "./project-fKmuIaMI.mjs";
8
6
  import { d as quotedPath, u as plural } from "./command-output-8wHMEde0.mjs";
9
- import { E as readHostMemoryPressure, T as hostMemoryPressureAdvice, u as listAllIosSims } from "./ios-D1h1Di39.mjs";
10
- import { m as fingerprintProject, r as listBuildLocks, u as diffFingerprintSources } from "./build-lock-BsVoLDaq.mjs";
11
- import { n as listBuildSlots } from "./build-slots-D0b6sJyo.mjs";
12
- import { y as listAdbDevices } from "./android-K63_0UfF.mjs";
13
- import { E as simslimIsOnPath, S as liveOwnedDeviceCount, a as ccacheEnabled, g as podInstallCommand } from "./xcode-DeKCribl.mjs";
14
- import { d as projectCmakeLauncher, i as apkOutputsDir, r as resolveAndroidCompilerCache, t as readAndroidCasToolchain } from "./android-cas-D6HPZ6UQ.mjs";
7
+ import { B as listBuildLocks, Z as fingerprintProject, q as diffFingerprintSources, y as listAdbDevices } from "./android-BqVIJoG7.mjs";
8
+ import { A as resolveSettings, E as remoteAndroidSetting, I as unknownSettingKeys, M as settingOrigin, N as settingShapeErrors, O as remoteIosSetting, P as settingsLayers, U as resolveOptimizations, V as compilerCacheFallbackMessage, a as SETTING_SHAPE_REMEDY, i as SETTINGS_WITH_RESOLVE_TIME_FALLBACK, n as PATH_SETTINGS, v as iosSimSlimProfileSetting, w as projectOptimizations, y as mergeSettingsLayers } from "./settings-DEJf6_kJ.mjs";
9
+ import { a as POOL_SETTING_REMEDY, b as providerFromConfig, c as parkedMaxSetting, m as checkEasAuth$1, x as resolveEasCliBin, y as ownerFromConfig } from "./teardown-ClFp1Aax.mjs";
10
+ import { E as readHostMemoryPressure, T as hostMemoryPressureAdvice, u as listAllIosSims } from "./ios-Deh5js5V.mjs";
11
+ import { n as listBuildSlots } from "./build-slots-C3-R4XQr.mjs";
12
+ import { E as simslimIsOnPath, S as liveOwnedDeviceCount, a as ccacheEnabled, g as podInstallCommand } from "./xcode-D7sAp-Zy.mjs";
13
+ import { d as projectCmakeLauncher, i as apkOutputsDir, r as resolveAndroidCompilerCache, t as readAndroidCasToolchain } from "./android-cas-BR5YJBYv.mjs";
15
14
  import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, rmSync } from "node:fs";
16
15
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
17
16
  import { existsSync as existsSync$1, lstatSync as lstatSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1 } from "fs";
@@ -1,9 +1,9 @@
1
1
  import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { g as repoRoot } from "./worktree-C79xx_kM.mjs";
3
- import { a as findProjectRoot } from "./project-BBTVABUz.mjs";
2
+ import { g as repoRoot } from "./worktree-Eq6TLH8N.mjs";
3
+ import { a as findProjectRoot } from "./project-fKmuIaMI.mjs";
4
4
  import { l as phaseLine } from "./command-output-8wHMEde0.mjs";
5
- import { s as detectXcodeMajor } from "./xcode-DeKCribl.mjs";
6
- import { c as repairCxxLauncherState, n as detectFingerprintParity, r as detectLinkedLibraryGitMetadata, s as runDoctor } from "./doctor-CqPeMsSm.mjs";
5
+ import { s as detectXcodeMajor } from "./xcode-D7sAp-Zy.mjs";
6
+ import { c as repairCxxLauncherState, n as detectFingerprintParity, r as detectLinkedLibraryGitMetadata, s as runDoctor } from "./doctor-Bmv86UUW.mjs";
7
7
  import { accessSync, constants, realpathSync, statSync } from "node:fs";
8
8
  import { InvalidArgumentError } from "commander";
9
9
  import { delimiter, join, resolve } from "node:path";
@@ -1,13 +1,13 @@
1
1
  import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { f as getProject, k as withDirLock } from "./config-CMtIaTk9.mjs";
3
- import { f as readWorkspaceLaunches, p as readWorkspaceState } from "./state-CZ-ER-8d.mjs";
2
+ import { I as deviceSlotKey, L as deviceSlotPlatforms, f as getProject, k as withDirLock } from "./config-CcefMY2Q.mjs";
3
+ import { f as readWorkspaceLaunches, p as readWorkspaceState } from "./state-B3XA2wp2.mjs";
4
4
  import { n as deviceConsoleLevel } from "./ios-device-D3pzTKQE.mjs";
5
- import { i as resolveProjectMetro } from "./metro-COslRK_F.mjs";
5
+ import { i as resolveProjectMetro } from "./metro-CORqXyTL.mjs";
6
6
  import { n as parseLogcatLine, t as androidClockOffset } from "./android-BW8i_YxU.mjs";
7
7
  import { _ as launchErrorPreview } from "./command-output-8wHMEde0.mjs";
8
- import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-Dpik41RY.mjs";
9
- import { n as androidHome } from "./android-K63_0UfF.mjs";
10
- import { c as readLogRecords, t as attachExpoErrorContext } from "./logs-query-oqn47Ffx.mjs";
8
+ import { n as androidHome } from "./android-BqVIJoG7.mjs";
9
+ import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-BJYybF-2.mjs";
10
+ import { c as readLogRecords, t as attachExpoErrorContext } from "./logs-query-R-Cqc6Qv.mjs";
11
11
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
12
12
  import { createHash, randomUUID } from "node:crypto";
13
13
  import { basename, dirname, join, resolve, sep } from "node:path";
@@ -333,14 +333,18 @@ function captureNativeCrashes(target, logsDir) {
333
333
  if (text) records = parseAndroidCrashes(text, target, offset).map((record) => symbolicateAndroid(record, target.root, deadline));
334
334
  }
335
335
  } catch {}
336
- for (const record of records) try {
337
- const key = createHash("sha256").update(JSON.stringify([
338
- record.deviceId,
339
- record.incident ?? record.deviceTs,
340
- record.rawReport
341
- ])).digest("hex");
342
- writeDiagnosticOnce(join(logsDir, `native-crash-${key}.ndjson`), `${JSON.stringify(record)}\n`);
343
- } catch {}
336
+ for (const record of records) {
337
+ if (target.slot && target.slot !== "default") record.slot = target.slot;
338
+ try {
339
+ const key = createHash("sha256").update(JSON.stringify([
340
+ record.slot,
341
+ record.deviceId,
342
+ record.incident ?? record.deviceTs,
343
+ record.rawReport
344
+ ])).digest("hex");
345
+ writeDiagnosticOnce(join(logsDir, `native-crash-${key}.ndjson`), `${JSON.stringify(record)}\n`);
346
+ } catch {}
347
+ }
344
348
  return records;
345
349
  }
346
350
  function printNativeCrashReport(target, logsDir, emit, remote) {
@@ -350,45 +354,51 @@ function printNativeCrashReport(target, logsDir, emit, remote) {
350
354
  function captureWorkspaceCrashes(root, logsDir) {
351
355
  const attempts = readLogRecords(logsDir).filter((record) => record.event === "launch_attempt");
352
356
  const launches = readWorkspaceLaunches(root);
353
- for (const platform of ["ios", "android"]) try {
354
- const attempt = attempts.findLast((record) => record.platform === platform);
355
- if (!attempt || attempt.remote) continue;
356
- const { deviceId, appId } = attempt;
357
- const since = attempt.ts;
358
- const until = Date.now();
359
- if (typeof deviceId !== "string" || typeof appId !== "string" || typeof since !== "number" || !Number.isFinite(since)) continue;
360
- if (attempt.physical) {
361
- const holder = fileLeaseIo.readHolder(root)[platform];
362
- const lease = parseLease(fileLeaseIo.readLease(deviceLeasePath(platform, deviceId)));
363
- if (holder?.id !== deviceId || !lease || lease.platform !== platform || lease.id !== deviceId || lease.holder !== root || lease.token !== holder.token || Date.parse(lease.expiresAt) <= until || !lease.grantedAt || !Number.isFinite(Date.parse(lease.grantedAt)) || Date.parse(lease.grantedAt) > since) continue;
364
- } else {
365
- const launch = launches[platform];
366
- if (!launch || launch.deviceId !== deviceId || launch.appId !== appId || Date.parse(launch.launchedAt) !== since) continue;
367
- const configured = getProject(root)?.platforms?.[platform];
368
- if (!configured?.owned) continue;
369
- if (platform === "ios" && configured.deviceUdid !== deviceId) continue;
370
- if (platform === "android") {
371
- if (!configured.avdName) continue;
372
- if (getExecutor().runFileQuiet("adb", [
373
- "-s",
374
- deviceId,
375
- "emu",
376
- "avd",
377
- "name"
378
- ], { timeoutMs: 2e3 })?.split("\n")[0]?.trim() !== configured.avdName) continue;
357
+ const latest = /* @__PURE__ */ new Map();
358
+ for (const attempt of attempts) if (attempt.platform === "ios" || attempt.platform === "android") latest.set(deviceSlotKey(attempt.platform, String(attempt.slot ?? "default")), attempt);
359
+ for (const [key, attempt] of latest) {
360
+ const platform = attempt.platform;
361
+ const slot = String(attempt.slot ?? "default");
362
+ try {
363
+ if (!attempt || attempt.remote) continue;
364
+ const { deviceId, appId } = attempt;
365
+ const since = attempt.ts;
366
+ const until = Date.now();
367
+ if (typeof deviceId !== "string" || typeof appId !== "string" || typeof since !== "number" || !Number.isFinite(since)) continue;
368
+ if (attempt.physical) {
369
+ const holder = fileLeaseIo.readHolder(root)[key];
370
+ const lease = parseLease(fileLeaseIo.readLease(deviceLeasePath(platform, deviceId)));
371
+ if (holder?.id !== deviceId || !lease || lease.platform !== platform || lease.id !== deviceId || lease.holder !== root || lease.token !== holder.token || Date.parse(lease.expiresAt) <= until || !lease.grantedAt || !Number.isFinite(Date.parse(lease.grantedAt)) || Date.parse(lease.grantedAt) > since) continue;
372
+ } else {
373
+ const launch = launches[key];
374
+ if (!launch || launch.deviceId !== deviceId || launch.appId !== appId || Date.parse(launch.launchedAt) !== since) continue;
375
+ const configured = deviceSlotPlatforms(getProject(root), slot)?.[platform];
376
+ if (!configured?.owned) continue;
377
+ if (platform === "ios" && configured.deviceUdid !== deviceId) continue;
378
+ if (platform === "android") {
379
+ if (!configured.avdName) continue;
380
+ if (getExecutor().runFileQuiet("adb", [
381
+ "-s",
382
+ deviceId,
383
+ "emu",
384
+ "avd",
385
+ "name"
386
+ ], { timeoutMs: 2e3 })?.split("\n")[0]?.trim() !== configured.avdName) continue;
387
+ }
379
388
  }
380
- }
381
- captureNativeCrashes({
382
- root,
383
- platform,
384
- deviceId,
385
- appId,
386
- since: Number(since),
387
- until,
388
- physical: attempt?.physical === true,
389
- appPath: attempt?.appPath ?? readWorkspaceState(root)?.lastBuild?.appPath
390
- }, logsDir);
391
- } catch {}
389
+ captureNativeCrashes({
390
+ root,
391
+ slot,
392
+ platform,
393
+ deviceId,
394
+ appId,
395
+ since: Number(since),
396
+ until,
397
+ physical: attempt?.physical === true,
398
+ appPath: attempt?.appPath ?? readWorkspaceState(root)?.lastBuild?.appPath
399
+ }, logsDir);
400
+ } catch {}
401
+ }
392
402
  }
393
403
  //#endregion
394
404
  //#region src/error-symbolication.ts