stim 1.8.0 → 1.9.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 (80) hide show
  1. package/README.md +23 -155
  2. package/dist/{android-BRgBYzU0.mjs → android-BPGizezh.mjs} +112 -135
  3. package/dist/{android-DeYdjjBP.mjs → android-DEgo4oCo.mjs} +2 -2
  4. package/dist/{android-DWssqHQ8.mjs → android-_y4Ohi5z.mjs} +54 -15
  5. package/dist/android-cas-compiler.mjs +2 -2
  6. package/dist/{android-cas-SWIjzeuL.mjs → android-cas-eiExNLf7.mjs} +21 -14
  7. package/dist/app-install-Uf3a83Tv.mjs +819 -0
  8. package/dist/{build-lock-CgtsB6TV.mjs → build-lock-DPXPLgTc.mjs} +120 -24
  9. package/dist/build-progress-CVqkfPC6.mjs +188 -0
  10. package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Dg7OnMrp.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 +2 -0
  14. package/dist/collector-run.mjs +10 -9
  15. package/dist/{command-output-B3ycpsYv.mjs → command-output-gByVmIjy.mjs} +11 -2
  16. package/dist/{config-B7iyKWkL.mjs → config-Cp9lM3wX.mjs} +187 -57
  17. package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-Bq5DcLGn.mjs} +2 -2
  18. package/dist/{deps-oCXVNEqQ.mjs → deps-Cip5vUm1.mjs} +8 -5
  19. package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-KEu0_AXL.mjs} +1 -1
  20. package/dist/{device-BUoZyMoR.mjs → dev-client-HyqISM0h.mjs} +1178 -635
  21. package/dist/{device-RdujjHLB.mjs → device-D4U-xLty.mjs} +13 -20
  22. package/dist/{device-lease-Ffz_M5t0.mjs → device-lease-Crs-HkJY.mjs} +16 -9
  23. package/dist/{device-pool-BxRwdufO.mjs → device-pool-CnTAz8X1.mjs} +7 -7
  24. package/dist/{device-remote-ByN8P6CB.mjs → device-remote-AQ5rQPqr.mjs} +482 -35
  25. package/dist/{doctor-BMt156qe.mjs → doctor-B_Ei3bZu.mjs} +95 -107
  26. package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-Vfz83GCF.mjs} +22 -16
  27. package/dist/exec-jIAlgilk.mjs +132 -0
  28. package/dist/{gc-v2YcoJvm.mjs → gc-Ckw8RQHi.mjs} +763 -126
  29. package/dist/{guide-BTAlD1_y.mjs → guide-BiAbhs1J.mjs} +891 -501
  30. package/dist/{guide-status-Craq9z0t.mjs → guide-status-BAMVU0ZA.mjs} +5 -3
  31. package/dist/{ios-BcCiI4TS.mjs → ios-BL2NFckr.mjs} +43 -20
  32. package/dist/{ios-BP04pY5f.mjs → ios-BzUJWk1K.mjs} +141 -175
  33. package/dist/{ios-device-CauFJiYx.mjs → ios-device-D972i9wf.mjs} +149 -52
  34. package/dist/{ios-device-CFXA46ez.mjs → ios-device-DPsSm0v_.mjs} +3 -2
  35. package/dist/launch-verify-Q_OtW1Rf.mjs +365 -0
  36. package/dist/{logs-CakdBTtm.mjs → logs-CI9NQSK7.mjs} +18 -13
  37. package/dist/{logs-query-BGL-Pl12.mjs → logs-query-Cqma3WIu.mjs} +36 -28
  38. package/dist/{metro-C7bcJ0uo.mjs → metro-DrLHaMn2.mjs} +78 -8
  39. package/dist/{named-ports-DzMOlmaX.mjs → named-ports-CmLQSIRj.mjs} +4 -4
  40. package/dist/{ndjson-BZwI1NBv.mjs → ndjson-9uFNRwfe.mjs} +36 -3
  41. package/dist/{ownership-Cp52QAK1.mjs → ownership-D15Op8vN.mjs} +3 -3
  42. package/dist/{ownership-wolE-0Do.mjs → ownership-DCQPuWEc.mjs} +26 -7
  43. package/dist/{ports-BIbJaKNW.mjs → ports-BNKoWVWZ.mjs} +3 -3
  44. package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
  45. package/dist/{project-DDZWf_16.mjs → project-DS112DYB.mjs} +8 -3
  46. package/dist/{reload-BdypPbjM.mjs → reload-ZNJUo1pl.mjs} +12 -13
  47. package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DWkkcIxV.mjs} +2 -2
  48. package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-B_lctBR3.mjs} +8 -7
  49. package/dist/server-expo-CeILEu86.mjs +2 -0
  50. package/dist/settings-BKNh5rl4.mjs +376 -0
  51. package/dist/{settings-BXXorVM2.mjs → settings-D030k0t4.mjs} +464 -152
  52. package/dist/settings.schema.json +989 -0
  53. package/dist/{teardown-BJqEzPo_.mjs → sim-pool-KaIsp2Ft.mjs} +14 -419
  54. package/dist/simslim-B_LsGLuJ.mjs +259 -0
  55. package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-DU9A-gYw.mjs} +4 -4
  56. package/dist/spawn-claims-Dn3C_N6w.mjs +49 -0
  57. package/dist/{start-CmxtOKjU.mjs → start-DYkkKF0s.mjs} +27 -31
  58. package/dist/{state-DZobzxLW.mjs → state-NcJzMEBF.mjs} +5 -3
  59. package/dist/{stats-CLg5RuDS.mjs → stats-B63-JVAY.mjs} +4 -4
  60. package/dist/{stats-BYcZ6n1h.mjs → stats-DG9e66Rz.mjs} +66 -5
  61. package/dist/status-S6ehL4Ui.mjs +932 -0
  62. package/dist/{stop-C8JW6kKp.mjs → stop-BO28HGqF.mjs} +101 -25
  63. package/dist/{stop-B1kOpYei.mjs → stop-T1R-cZRz.mjs} +2 -2
  64. package/dist/supervisor-run.mjs +52 -34
  65. package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-DWaiRD0e.mjs} +14 -9
  66. package/dist/{workspace-state-BT5E3SA2.mjs → workspace-state-DF2gCFhx.mjs} +52 -29
  67. package/dist/worktree-DKhSF0UX.mjs +2 -0
  68. package/dist/{worktree-CFLA-6MX.mjs → worktree-LGB-UwgG.mjs} +603 -59
  69. package/dist/{worktree-FG_ZKKdb.mjs → worktree-R_cUlE7s.mjs} +62 -41
  70. package/dist/{xcode-B6YaWeg0.mjs → xcode-BZqyxh3e.mjs} +216 -58
  71. package/package.json +5 -6
  72. package/skill/SKILL.md +2 -0
  73. package/dist/app-install-BDbsSgCl.mjs +0 -1175
  74. package/dist/build-slots-C-oAHRT1.mjs +0 -151
  75. package/dist/dev-client-BmT-ioFI.mjs +0 -797
  76. package/dist/exec-B29XNuhP.mjs +0 -77
  77. package/dist/reclaim-KJ-0uWEF.mjs +0 -361
  78. package/dist/server-expo-YDDG3eP7.mjs +0 -2
  79. package/dist/status-DdkLki47.mjs +0 -426
  80. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
@@ -1,10 +1,10 @@
1
- import { B as validateDeviceSlot } from "./config-B7iyKWkL.mjs";
2
- import { a as findProjectRoot } from "./project-DDZWf_16.mjs";
3
- import { E as memoizeEmulatorProbe, L as resolvePhysicalDevice, M as physicalDeviceModel, P as probeEmulatorSerial, S as listAdbDevices, a as androidPoolNoCandidatesRefusal, i as androidPoolCandidates } from "./android-DWssqHQ8.mjs";
4
- import { r as clockTime } from "./command-output-B3ycpsYv.mjs";
5
- import { f as takeLease, l as releaseWorkspaceLeases, n as fileLeaseIo, o as parseLeaseDuration } from "./device-lease-Ffz_M5t0.mjs";
6
- import { d as waitForDevice, s as parseDeviceWait, t as selectFromPool } from "./device-pool-BxRwdufO.mjs";
7
- import { c as listIosDevices, o as iosPoolCandidates, s as iosPoolNoCandidatesRefusal, u as resolveIosPhysicalDevice } from "./ios-device-CauFJiYx.mjs";
1
+ import { G as parseDeviceSlotOption } from "./config-Cp9lM3wX.mjs";
2
+ import { o as findProjectRoot, t as NO_PROJECT_REFUSAL } from "./project-DS112DYB.mjs";
3
+ import { C as listAdbDevices, D as memoizeEmulatorProbe, I as probeEmulatorSerial, P as physicalDeviceModel, a as androidPoolNoCandidatesRefusal, i as androidPoolCandidates, z as resolvePhysicalDevice } from "./android-_y4Ohi5z.mjs";
4
+ import { r as clockTime } from "./command-output-gByVmIjy.mjs";
5
+ import { f as takeLease, l as releaseWorkspaceLeases, n as fileLeaseIo, o as parseLeaseDuration } from "./device-lease-Crs-HkJY.mjs";
6
+ import { d as waitForDevice, s as parseDeviceWait, t as selectFromPool } from "./device-pool-CnTAz8X1.mjs";
7
+ import { a as iosPoolCandidates, c as listIosDevices, o as iosPoolNoCandidatesRefusal, s as isWirelessIosDevice, u as resolveIosPhysicalDevice } from "./ios-device-D972i9wf.mjs";
8
8
  import chalk from "chalk";
9
9
  //#region src/commands/device.ts
10
10
  const DEFAULT_FOR = "5m";
@@ -69,7 +69,8 @@ async function poolDevice(platform, idLabel, waitSeconds, deadline, root, d, slo
69
69
  idLabel,
70
70
  list: () => platform === "ios" ? iosPoolCandidates(d.listIosDevices()).map((entry) => ({
71
71
  id: entry.udid,
72
- name: entry.name
72
+ name: entry.name,
73
+ fallback: isWirelessIosDevice(entry)
73
74
  })) : androidPoolCandidates(d.listAdbDevices(), isEmulator).map((entry) => ({ id: entry.serial })),
74
75
  noCandidates: () => {
75
76
  const resolved = platform === "ios" ? iosPoolNoCandidatesRefusal(d.listIosDevices()) : androidPoolNoCandidatesRefusal(d.listAdbDevices(), isEmulator);
@@ -121,11 +122,7 @@ async function runLock(platformArg, idArg, opts = {}, overrides = {}) {
121
122
  remedy: "Run `stim device lock ios` or `stim device lock android`."
122
123
  });
123
124
  const root = d.findProjectRoot(process.cwd());
124
- if (!root) return report({
125
- code: "STIM_NO_PROJECT",
126
- message: "Not in a React Native project (no package.json found).",
127
- remedy: "Run this from the app directory -- the one holding package.json."
128
- });
125
+ if (!root) return report(NO_PROJECT_REFUSAL);
129
126
  const durationText = String(opts.for ?? DEFAULT_FOR).trim();
130
127
  const duration = parseLeaseDuration(durationText);
131
128
  if ("error" in duration) return report({
@@ -224,11 +221,7 @@ async function runUnlock(platformArg, opts = {}, overrides = {}) {
224
221
  remedy: "Run `stim device unlock` for every lease this workspace holds, or name one platform."
225
222
  });
226
223
  const root = d.findProjectRoot(process.cwd());
227
- if (!root) return report({
228
- code: "STIM_NO_PROJECT",
229
- message: "Not in a React Native project (no package.json found).",
230
- remedy: "Run this from the app directory -- the one holding package.json."
231
- });
224
+ if (!root) return report(NO_PROJECT_REFUSAL);
232
225
  const released = d.releaseLeases(root, {
233
226
  platform,
234
227
  ...opts.slot ? { slot: opts.slot } : {}
@@ -243,10 +236,10 @@ async function runUnlock(platformArg, opts = {}, overrides = {}) {
243
236
  }
244
237
  function registerDevice(program, deps = {}) {
245
238
  const device = program.command("device").description("Lease a connected physical device to this workspace, and give it back");
246
- device.command("lock").argument("<platform>", "ios or android").argument("[id]", "the UDID or serial to lease; without one, the first free connected device is used").description("Lease a connected physical device to this workspace for a declared time, so another workspace's `--device` run waits instead of installing over it. Nothing but this command and a `--device` run moves the expiry.").option("--for <duration>", `How long to hold it: a whole number of seconds or minutes from 10s to 30m (default ${DEFAULT_FOR})`).option("--wait <seconds>", "How long to wait for a device another workspace holds, before refusing with STIM_DEVICE_BUSY (default 60, 0 refuses at once)").option("--slot <name>", "Use this workspace device slot", validateDeviceSlot).option("--json", "print the lease as JSON on stdout").action(async (platform, id, opts) => {
239
+ device.command("lock").argument("<platform>", "ios or android").argument("[id]", "the UDID or serial to lease; without one, the first free connected device is used").description("Lease a connected physical device to this workspace for a declared time, so another workspace's `--device` run waits instead of installing over it. Nothing but this command and a `--device` run moves the expiry.").option("--for <duration>", `How long to hold it: a whole number of seconds or minutes from 10s to 30m (default ${DEFAULT_FOR})`).option("--wait <seconds>", "How long to wait for a device another workspace holds, before refusing with STIM_DEVICE_BUSY (default 60, 0 refuses at once)").option("--slot <name>", "Use this workspace device slot", parseDeviceSlotOption).option("--json", "print the lease as JSON on stdout").action(async (platform, id, opts) => {
247
240
  if ("code" in await runLock(platform, id, opts, deps)) process.exit(1);
248
241
  });
249
- device.command("unlock").argument("[platform]", "ios or android; without one, every lease this workspace holds is released").description("Release the device lease or leases this workspace holds. Releasing nothing is not an error.").option("--slot <name>", "Use this workspace device slot", validateDeviceSlot).option("--json", "print the released leases as JSON on stdout").action(async (platform, opts) => {
242
+ device.command("unlock").argument("[platform]", "ios or android; without one, every lease this workspace holds is released").description("Release the device lease or leases this workspace holds. Releasing nothing is not an error.").option("--slot <name>", "Use this workspace device slot", parseDeviceSlotOption).option("--json", "print the released leases as JSON on stdout").action(async (platform, opts) => {
250
243
  const result = await runUnlock(platform, opts, deps);
251
244
  if (!Array.isArray(result)) process.exit(1);
252
245
  });
@@ -1,9 +1,9 @@
1
- import { B as validateDeviceSlot, D as withDirLock, F as deviceSlotKey, L as parseDeviceSlotKey, u as getConfigDir } from "./config-B7iyKWkL.mjs";
2
- import { a as withWorkspaceStateLock, n as clearWorkspaceStateKeys, o as writeWorkspaceState, r as readWorkspaceState } from "./workspace-state-BT5E3SA2.mjs";
3
- import { a as segment } from "./build-lock-CgtsB6TV.mjs";
1
+ import { A as withDirLock, H as deviceSlotKey, J as validateDeviceSlot, W as parseDeviceSlotKey, u as getConfigDir } from "./config-Cp9lM3wX.mjs";
2
+ import { c as writeWorkspaceState, n as clearWorkspaceStateKeys, o as withWorkspaceStateLock, r as readWorkspaceState } from "./workspace-state-DF2gCFhx.mjs";
3
+ import { i as segment } from "./build-lock-DPXPLgTc.mjs";
4
4
  import { mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
5
- import { randomBytes } from "node:crypto";
6
5
  import { join } from "node:path";
6
+ import { randomBytes } from "node:crypto";
7
7
  const DEFAULT_LEASE_MS = 3e5;
8
8
  const LEASE_SUFFIX = ".json";
9
9
  const LOCK_SUFFIX = ".lock";
@@ -332,15 +332,21 @@ function selectPoolDevice({ candidates, leases, held = null, now }) {
332
332
  id: held
333
333
  };
334
334
  }
335
- if (ordered.length === 0) return { status: "none" };
335
+ const preferred = ordered.filter((candidate) => !candidate.fallback);
336
+ const pool = preferred.length > 0 ? preferred : ordered;
337
+ if (pool.length === 0) return { status: "none" };
336
338
  const byId = new Map(leases.map((lease) => [lease.id, lease]));
337
- const holders = [];
338
- for (const candidate of ordered) {
339
+ const free = (candidate) => {
339
340
  const lease = byId.get(candidate.id);
340
- if (!lease || leaseIsExpired(lease, now)) return {
341
+ return !lease || leaseIsExpired(lease, now);
342
+ };
343
+ const holders = [];
344
+ for (const candidate of pool) {
345
+ if (free(candidate)) return {
341
346
  status: "selected",
342
347
  candidate
343
348
  };
349
+ const lease = byId.get(candidate.id);
344
350
  holders.push({
345
351
  id: lease.id,
346
352
  holder: lease.holder,
@@ -349,7 +355,8 @@ function selectPoolDevice({ candidates, leases, held = null, now }) {
349
355
  }
350
356
  return {
351
357
  status: "busy",
352
- holders
358
+ holders,
359
+ spare: ordered.filter((candidate) => candidate.fallback && free(candidate)).map((candidate) => candidate.id)
353
360
  };
354
361
  }
355
362
  function withHolderLock(io, root, fn) {
@@ -1,7 +1,7 @@
1
- import { F as deviceSlotKey } from "./config-B7iyKWkL.mjs";
2
- import { a as formatElapsed, r as clockTime } from "./command-output-B3ycpsYv.mjs";
3
- import { A as APP_READINESS_TIMEOUT_MS, l as VERIFY_TIMEOUT_MS } from "./app-install-BDbsSgCl.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-Ffz_M5t0.mjs";
1
+ import { H as deviceSlotKey } from "./config-Cp9lM3wX.mjs";
2
+ import { a as formatElapsed, r as clockTime } from "./command-output-gByVmIjy.mjs";
3
+ import { a as VERIFY_TIMEOUT_MS, p as APP_READINESS_TIMEOUT_MS } from "./launch-verify-Q_OtW1Rf.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-Crs-HkJY.mjs";
5
5
  //#region src/engine/device-lease-run.ts
6
6
  const LEASE_STEP_FLOOR_MS = 6e4;
7
7
  const DEBUG_VERIFY_STEP_MS = VERIFY_TIMEOUT_MS + APP_READINESS_TIMEOUT_MS;
@@ -335,7 +335,7 @@ const NO_LEASE_FACTS = {
335
335
  function unreadableSentence(unreadable) {
336
336
  return unreadable.length ? ` Unreadable lease file, so no run may take that device around it: ${unreadable.join("; ")}.` : "";
337
337
  }
338
- function poolBusyRefusal(holders, leases, unreadable, now, waitSeconds) {
338
+ function poolBusyRefusal(holders, leases, unreadable, now, waitSeconds, spare = [], idLabel = "id") {
339
339
  const named = holders.map((holder) => `${holder.id} (${holder.holder}, until ${leaseExpiryText(holder.expiresAt, now)})`);
340
340
  const first = holders[0];
341
341
  const lease = first ? leases.find((entry) => entry.id === first.id) : void 0;
@@ -348,7 +348,7 @@ function poolBusyRefusal(holders, leases, unreadable, now, waitSeconds) {
348
348
  return {
349
349
  code: DEVICE_BUSY,
350
350
  message: `Every connected device is leased by another workspace${waitSeconds > 0 ? `, and this run waited ${waitSeconds}s for one` : ""}: ${named.join("; ")}.${unreadableSentence(unreadable)}`,
351
- remedy: "Wait longer with `--wait <seconds>`, connect another device, or pass `--no-wait` to install anyway -- which takes no lease and, when both workspaces build the same app id, terminates the holder's running app.",
351
+ remedy: (spare.length ? `Also connected and free: ${spare.join(", ")}. Name one with \`--device <${idLabel}>\` to use it now. ` : "") + "Wait longer with `--wait <seconds>`, connect another device, or pass `--no-wait` to install anyway -- which takes no lease and, when both workspaces build the same app id, terminates the holder's running app.",
352
352
  lease: {
353
353
  platform: lease?.platform ?? null,
354
354
  id: first.id,
@@ -401,7 +401,7 @@ async function selectFromPool({ root, platform, slot = "default", idLabel, list,
401
401
  }
402
402
  if (now() >= until) return {
403
403
  status: "refused",
404
- refusal: poolBusyRefusal(selection.holders, leases, unreadable, now(), waitSeconds)
404
+ refusal: poolBusyRefusal(selection.holders, leases, unreadable, now(), waitSeconds, selection.spare, idLabel)
405
405
  };
406
406
  if (lastLine === null || now() - lastLine >= 3e4) {
407
407
  lastLine = now();