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,38 +1,39 @@
1
- import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
- import { B as validateDeviceSlot, E as ownership_claim_exports, F as deviceSlotKey, P as deviceSlotFileKey, S as upsertProject, f as getProject, l as getConcurrencyLimits } from "./config-B7iyKWkL.mjs";
3
- import { r as createNdjsonWriter } from "./ndjson-BZwI1NBv.mjs";
4
- import { _ as workspaceLogsDir, h as workspaceDir, o as writeWorkspaceState, r as readWorkspaceState } from "./workspace-state-BT5E3SA2.mjs";
5
- import { T as removeTemporaryEntry, g as repoRoot, s as gitCommonDir, w as makeTemporaryDirectory } from "./worktree-FG_ZKKdb.mjs";
6
- import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, r as detectBundleId, t as appProjectProblem } from "./project-DDZWf_16.mjs";
7
- import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-DZobzxLW.mjs";
8
- import { a as resolveProjectMetro, i as pidExists, o as signalProcessTree } from "./metro-C7bcJ0uo.mjs";
9
- import { n as verifyCollectorOwnership } from "./ownership-Cp52QAK1.mjs";
10
- import { A as resolveSettings, B as artifactCachePolicy, D as remoteDeviceSettingError, F as tunnelModeSetting, H as optimizationBuildProfile, I as unknownSettingKeys, N as settingShapeErrors, O as remoteIosSetting, R as detectProviders, T as publicUrlSetting, U as resolveOptimizations, _ as iosSigningIdentitySha1SettingError, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, f as iosLanHostSetting, g as iosSigningIdentitySha1Setting, h as iosSigningIdentitySettingError, k as resolveCacheProviderConfig, m as iosSigningIdentitySetting, p as iosLanHostSettingError, r as REMOTE_DEVICE_BACKENDS, x as metroWarmupUrlSetting } from "./settings-BXXorVM2.mjs";
11
- import { _ as launchErrorPreview, c as launchErrorReport, g as stepTimer, h as stepClock, i as formatDuration, l as phaseLine, m as shortUdid, n as appReadinessMessage, p as shortHash } from "./command-output-B3ycpsYv.mjs";
12
- import { C as untrackedNativeFiles, S as untrackedMissLine, _ as providerUploadOutcome, b as storeBuild, c as buildCacheKey, d as filesystemBuildCapability, f as fingerprintDiffRecord, g as providerDownloadPath, h as prepareProviderDownloadDir, i as releaseBuildLock, l as describeFingerprintMiss, m as fingerprintProject, n as acquireBuildLock, o as takeoverLine, p as fingerprintDiffSuffix, s as waitForBuild, t as WAIT_CEILING_MS, v as refingerprintAfterMutation, y as resolveBuild } from "./build-lock-CgtsB6TV.mjs";
13
- import { C as resolveRemote, S as resolveEasCliBin, _ as exitAfterFlush, a as POOL_SETTING_REMEDY, f as RESOLVE_TIMEOUT_MS, g as easAuthNote, h as checkEasAuth, l as parkedMaxSetting, m as cacheLevel, p as UPLOAD_TIMEOUT_MS, v as isEasAuthFailureText, w as uploadRemote, y as loadProjectProvider } from "./teardown-BJqEzPo_.mjs";
14
- import { _ as easDeviceBuildRemedy, a as swapJsBundle, c as ensureWorkspaceStorageSafely, d as launchOutcomeRecord, f as noMetroMessage, g as warmMetro, h as sleep, l as gateShouldRetry, m as resolveMetroWithRetry, n as pickDevClientScheme, o as needsPrebuild, p as noMetroRemedy, r as schemesFromInfoPlist, s as runPrebuild, t as devClientScheme, u as isPhysicalDeviceRequest, v as isEasBuildFailure, y as resolveEasDevelopmentBuild } from "./dev-client-BmT-ioFI.mjs";
15
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BYfZz_S-.mjs";
16
- import { C as launchIosApp, E as unverifiedLaunchLines, O as verifyLaunch, T as readMetroRecords, b as isBundleProof, c as RELEASE_VERIFY_WAIT_MS, f as clearOtherUserApps, i as LAUNCH_BUNDLING, k as verifyReleaseLaunch, m as devClientUrl, n as DEFAULT_METRO_PORT, o as LAUNCH_FATAL, s as LAUNCH_UNVERIFIED, v as installIosApp, w as readCollectorRecords, y as iosAppProcess } from "./app-install-BDbsSgCl.mjs";
17
- import { E as parseIosSimulatorApp, f as listIosRuntimes } from "./ios-BcCiI4TS.mjs";
18
- import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-BxRwdufO.mjs";
19
- import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-C-oAHRT1.mjs";
20
- import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-BYcZ6n1h.mjs";
21
- import { c as unknownIosRuntimeRefusal, i as ensureOwnedDevice, n as clearIosAdoptionPending, r as ensureBooted, s as unknownIosDeviceTypeRefusal, t as checkDeviceCapacity } from "./device-BUoZyMoR.mjs";
22
- import { a as ensureMetroReachable, b as ownedSessionName, c as remoteIosDeps, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, r as bundleEntryPoint, w as gateMetroOrigin } from "./device-remote-ByN8P6CB.mjs";
23
- import { a as compilationCacheActivityLine, c as readBundleExecutable, l as readBundleId, p as describeDiagnostic, r as buildIos, s as discoverXcodeProject, t as COMPILATION_CACHE_NOT_RUN, u as resolveScheme } from "./xcode-B6YaWeg0.mjs";
24
- import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-ZUUe0TQP.mjs";
1
+ import { G as parseDeviceSlotOption, H as deviceSlotKey, J as validateDeviceSlot, V as deviceSlotFileKey, f as getProject, k as ownership_claim_exports, l as getConcurrencyLimits, w as upsertProject } from "./config-Cp9lM3wX.mjs";
2
+ import { t as getExecutor } from "./exec-jIAlgilk.mjs";
3
+ import { r as createNdjsonWriter } from "./ndjson-9uFNRwfe.mjs";
4
+ import { _ as workspaceDir, b as workspaceLogsDir, c as writeWorkspaceState, i as recordWorkspaceUse, r as readWorkspaceState } from "./workspace-state-DF2gCFhx.mjs";
5
+ import { E as removeTemporaryEntry, T as makeTemporaryDirectory, _ as repoRoot, s as gitCommonDir } from "./worktree-R_cUlE7s.mjs";
6
+ import { a as detectIsExpo, i as detectBundleId, n as appProjectProblem, o as findProjectRoot, t as NO_PROJECT_REFUSAL } from "./project-DS112DYB.mjs";
7
+ import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-NcJzMEBF.mjs";
8
+ import { a as pidExists, c as resolveProjectMetro, l as signalProcessTree } from "./metro-DrLHaMn2.mjs";
9
+ import { n as verifyCollectorOwnership } from "./ownership-D15Op8vN.mjs";
10
+ import { D as remoteIosSetting, F as unknownSettingKeys, J as optimizationBuildProfile, K as artifactCachePolicy, L as REMOTE_DEVICE_BACKENDS, M as settingShapeErrors, O as resolveCacheProviderConfig, P as tunnelModeSetting, W as detectProviders, Y as resolveOptimizations, b as metroWarmupUrlSetting, d as iosLanHostSetting, f as iosLanHostSettingError, g as iosSigningIdentitySha1SettingError, h as iosSigningIdentitySha1Setting, i as SETTING_SHAPE_REMEDY, k as resolveSettings, m as iosSigningIdentitySettingError, p as iosSigningIdentitySetting, u as cacheProviderSettingError, w as publicUrlSetting } from "./settings-D030k0t4.mjs";
11
+ import { _ as stepTimer, c as launchErrorReport, g as stepClock, h as shortUdid, i as formatDuration, l as phaseLine, m as shortHash, n as appReadinessMessage, v as launchErrorPreview } from "./command-output-gByVmIjy.mjs";
12
+ import { S as untrackedNativeFiles, _ as refingerprintAfterMutation, a as waitForBuild, c as describeFingerprintMiss, d as fingerprintDiffRecord, f as fingerprintDiffSuffix, g as providerUploadOutcome, h as providerDownloadPath, m as prepareProviderDownloadDir, o as waitForSharedBuild, p as fingerprintProject, r as releaseBuildLock, s as buildCacheKey, t as acquireBuildLock, u as filesystemBuildCapability, v as resolveBuild, x as untrackedMissLine, y as storeBuild } from "./build-lock-DPXPLgTc.mjs";
13
+ import { S as uploadRemote, _ as loadProjectProvider, b as resolveEasCliBin, d as UPLOAD_TIMEOUT_MS, f as cacheLevel, g as isEasAuthFailureText, h as exitAfterFlush, m as easAuthNote, o as parkedMaxSetting, p as checkEasAuth, t as POOL_SETTING_REMEDY, u as RESOLVE_TIMEOUT_MS, x as resolveRemote } from "./sim-pool-KaIsp2Ft.mjs";
14
+ import { C as isEasBuildFailure, S as easDeviceBuildRemedy, _ as noMetroMessage, a as swapJsBundle, b as sleep, c as ensureBooted, f as clearIosAdoptionPending, g as launchOutcomeRecord, h as isPhysicalDeviceRequest, l as ensureOwnedDevice, m as gateShouldRetry, n as pickDevClientScheme, o as needsPrebuild, p as ensureWorkspaceStorageSafely, r as schemesFromInfoPlist, s as runPrebuild, t as devClientScheme, v as noMetroRemedy, w as resolveEasDevelopmentBuild, x as warmMetro, y as resolveMetroWithRetry } from "./dev-client-HyqISM0h.mjs";
15
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-DWaiRD0e.mjs";
16
+ import { n as NO_BUILD_PROGRESS, s as startBuildProgress } from "./build-progress-CVqkfPC6.mjs";
17
+ import { c as readMetroRecords, d as verifyLaunch, f as verifyReleaseLaunch, i as RELEASE_VERIFY_WAIT_MS, l as unverifiedLaunchLines, n as LAUNCH_FATAL, o as isBundleProof, r as LAUNCH_UNVERIFIED, s as readCollectorRecords, t as LAUNCH_BUNDLING } from "./launch-verify-Q_OtW1Rf.mjs";
18
+ import { c as clearOtherUserApps, h as iosAppProcess, m as installIosApp, r as DEFAULT_METRO_PORT, u as devClientUrl, v as launchIosApp } from "./app-install-Uf3a83Tv.mjs";
19
+ import { O as parseIosSimulatorApp, p as listIosRuntimes } from "./ios-BL2NFckr.mjs";
20
+ import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-CnTAz8X1.mjs";
21
+ import { a as compilationCacheActivityLine, c as readBundleExecutable, g as acquireBuildSlot, l as readBundleId, p as describeDiagnostic, r as buildIos, s as discoverXcodeProject, t as COMPILATION_CACHE_NOT_RUN, u as resolveScheme, v as releaseBuildSlot } from "./xcode-BZqyxh3e.mjs";
22
+ import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-DG9e66Rz.mjs";
23
+ import { c as unknownIosRuntimeRefusal, r as checkDeviceCapacity, s as unknownIosDeviceTypeRefusal } from "./simslim-B_LsGLuJ.mjs";
24
+ import { a as ensureMetroReachable, b as ownedSessionName, c as remoteIosDeps, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, r as bundleEntryPoint, w as gateMetroOrigin } from "./device-remote-AQ5rQPqr.mjs";
25
+ import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-DU9A-gYw.mjs";
25
26
  import { t as spawnEntry } from "./spawn-entry-nCE6m770.mjs";
26
- import { a as printNativeCrashReport, n as IOS_CRASH_REPORT_RETRY, o as simulatorConsolePaths, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-D1Kaf-eT.mjs";
27
- import { a as iosDeviceProcess, c as listIosDevices, d as verifyIosDeviceReleaseLaunch, i as installIosDeviceApp, l as localNetworkPending, n as LAUNCH_PROBE_TIMEOUT_MS, o as iosPoolCandidates, r as awaitIosDeviceLaunch, s as iosPoolNoCandidatesRefusal, t as DEVICECTL_INSTALL_TIMEOUT_MS, u as resolveIosPhysicalDevice } from "./ios-device-CauFJiYx.mjs";
28
- import { a as runPodInstall, n as podsAreStale, r as readPodState } from "./deps-oCXVNEqQ.mjs";
27
+ import { a as printNativeCrashReport, n as IOS_CRASH_REPORT_RETRY, o as simulatorConsolePaths, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-Vfz83GCF.mjs";
28
+ import { a as iosPoolCandidates, c as listIosDevices, d as verifyIosDeviceReleaseLaunch, i as iosDeviceProcess, l as localNetworkPending, n as installIosDeviceApp, o as iosPoolNoCandidatesRefusal, r as iosDeviceBounds, s as isWirelessIosDevice, t as awaitIosDeviceLaunch, u as resolveIosPhysicalDevice } from "./ios-device-D972i9wf.mjs";
29
+ import { a as runPodInstall, n as podsAreStale, r as readPodState } from "./deps-Cip5vUm1.mjs";
29
30
  import { existsSync, mkdirSync, mkdtempSync, openSync, rmSync, writeFileSync } from "node:fs";
30
31
  import { InvalidArgumentError } from "commander";
31
- import { X509Certificate } from "node:crypto";
32
32
  import { basename, join } from "node:path";
33
+ import { X509Certificate } from "node:crypto";
33
34
  import { networkInterfaces, tmpdir } from "node:os";
34
- import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
35
35
  import chalk from "chalk";
36
+ import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
36
37
  //#region src/engine/ios-lan.ts
37
38
  const IP_TXT = "ip.txt";
38
39
  function chooseLanAddress({ pinned = null, candidates }) {
@@ -91,6 +92,7 @@ function copyAppAside(appPath, { exec = null, mkdtemp = () => makeTemporaryDirec
91
92
  ]);
92
93
  } catch {
93
94
  try {
95
+ removeTemporaryEntry(copy);
94
96
  e.runFile("cp", [
95
97
  "-R",
96
98
  appPath,
@@ -170,7 +172,7 @@ function deviceModelRefusal({ slot, deviceTypeFlag, runtimeFlag, deviceType, run
170
172
  if (typeof deviceTypeFlag === "string" && deviceTypeFlag.trim() === "") return {
171
173
  code: "STIM_BAD_ARG",
172
174
  message: "--device-type was given an empty name.",
173
- remedy: "Pass `--device-type <name>` with a model `xcrun simctl list devicetypes` names, e.g. \"iPad Pro 13-inch (M4)\"."
175
+ remedy: "Pass `--device-type <name>` with a model `xcrun simctl list devicetypes` names, e.g. \"iPad Pro 13-inch (M5)\"."
174
176
  };
175
177
  if (typeof runtimeFlag === "string" && runtimeFlag.trim() === "") return {
176
178
  code: "STIM_BAD_ARG",
@@ -383,7 +385,7 @@ var ArtifactRefusal = class extends Error {
383
385
  }
384
386
  };
385
387
  async function acquireIosArtifact({ root, logFile, udid, remoteDestination, device, configuration, buildScheme, buildProfile, isExpo, optimizations, cache, easBuild, easProfile, maxBuilds, progress }, d) {
386
- const { phase, note, logWriter, estimates, stats } = progress;
388
+ const { phase, note, logWriter, estimates, stats, step } = progress;
387
389
  const physical = device !== null;
388
390
  const lanAddress = device?.lanAddress ?? null;
389
391
  const metroPort = device?.metroPort ?? null;
@@ -481,6 +483,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
481
483
  }
482
484
  return;
483
485
  }
486
+ step("cache-lookup");
484
487
  const fingerprintTimer = stepTimer(d.now);
485
488
  let computedFingerprint;
486
489
  try {
@@ -605,107 +608,40 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
605
608
  }
606
609
  return remote;
607
610
  }
608
- async function waitForSharedBuild() {
609
- if (!useBuildCache) return;
610
- const waitStarted = d.now();
611
- let failedHolder;
612
- while (!appPath) {
613
- let attempt = null;
614
- try {
615
- attempt = d.acquireBuildLock({
616
- platform: "ios",
617
- key: cacheKey,
618
- root,
619
- logFile
620
- });
621
- } catch (e) {
622
- const refusal = (0, ownership_claim_exports.claimFailure)(e, "stim ios");
623
- if (refusal) fail({
624
- code: refusal.code,
625
- message: refusal.message,
626
- remedy: refusal.remedy,
627
- build: {
628
- fingerprint,
629
- cacheKey,
630
- cacheHit,
631
- cacheSkipped: !useBuildCache
632
- }
633
- });
634
- note(chalk.yellow(phaseLine("build", `could not take the build lock: ${e?.message || e}; building anyway`)));
611
+ async function awaitSharedBuild() {
612
+ if (!useBuildCache || appPath) return;
613
+ const shared = await waitForSharedBuild({
614
+ platform: "ios",
615
+ key: cacheKey,
616
+ fingerprint,
617
+ root,
618
+ logFile,
619
+ command: "stim ios",
620
+ acquire: d.acquireBuildLock,
621
+ wait: d.waitForBuild,
622
+ now: d.now,
623
+ phase: (text) => {
624
+ step("wait");
625
+ phase("build", text);
626
+ },
627
+ warn: (text) => note(chalk.yellow(phaseLine("build", text))),
628
+ out: note
629
+ });
630
+ if (shared.refusal) fail({
631
+ ...shared.refusal,
632
+ build: {
633
+ fingerprint,
634
+ cacheKey,
635
+ cacheHit,
636
+ cacheSkipped: !useBuildCache
635
637
  }
636
- if (attempt?.acquired) {
637
- buildLock = attempt;
638
- const previous = attempt.tookOver ?? failedHolder;
639
- if (previous) note(chalk.yellow(phaseLine("build", takeoverLine(previous))));
640
- break;
641
- } else if (attempt?.held) {
642
- releasedWait = null;
643
- const held = attempt.held;
644
- const who = held.projectRoot || "another workspace";
645
- phase("build", `${who} is already building ${shortHash(fingerprint)} (pid ${held.pid})${held.logFile ? ` -- tail ${held.logFile}` : ""} -- stim guide lifecycle concurrency`);
646
- let waited = null;
647
- try {
648
- const ceilingMs = WAIT_CEILING_MS - (d.now() - waitStarted);
649
- if (ceilingMs <= 0) throw Object.assign(/* @__PURE__ */ new Error(`Waited ${formatDuration(d.now() - waitStarted)} for shared builds without an artifact; ${who} (pid ${held.pid}) holds ${attempt.path}.`), {
650
- code: "STIM_BUILD_WAIT_TIMEOUT",
651
- lockPath: attempt.path
652
- });
653
- waited = await d.waitForBuild({
654
- platform: "ios",
655
- key: cacheKey,
656
- out: note,
657
- ceilingMs
658
- });
659
- } catch (e) {
660
- const refusal = (0, ownership_claim_exports.claimFailure)(e, "stim ios");
661
- if (refusal) fail({
662
- code: refusal.code,
663
- message: refusal.message,
664
- remedy: refusal.remedy,
665
- build: {
666
- fingerprint,
667
- cacheKey,
668
- cacheHit,
669
- cacheSkipped: !useBuildCache
670
- }
671
- });
672
- const err = e;
673
- if (err?.code !== "STIM_BUILD_WAIT_TIMEOUT") throw e;
674
- fail({
675
- code: "STIM_BUILD_WAIT_TIMEOUT",
676
- message: err.message,
677
- remedy: `Check pid ${held.pid}; if it is not really building, remove ${err.lockPath} and run \`stim ios\` again.`,
678
- build: {
679
- fingerprint,
680
- cacheKey,
681
- cacheHit,
682
- cacheSkipped: !useBuildCache
683
- }
684
- });
685
- }
686
- if (waited?.hit) {
687
- appPath = waited.hit ?? null;
688
- cacheHit = "local";
689
- waitedForBuild = {
690
- pid: held.pid,
691
- ms: waited.waitedMs
692
- };
693
- phase("build", `waited ${formatDuration(waited.waitedMs)} for ${who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
694
- } else if (waited?.lockReleased) {
695
- failedHolder = void 0;
696
- releasedWait = {
697
- facts: {
698
- pid: held.pid,
699
- ms: waited.waitedMs
700
- },
701
- who
702
- };
703
- phase("build", `${who}'s build lock was released; rechecking this workspace before building`);
704
- } else {
705
- note(chalk.yellow(phaseLine("build", `${who}'s build ended without an artifact (${waited?.builderFailed}); retrying the build lock`)));
706
- failedHolder = held;
707
- }
708
- } else break;
638
+ });
639
+ buildLock = shared.lock;
640
+ releasedWait = shared.released;
641
+ if (shared.hit) {
642
+ appPath = shared.hit.path;
643
+ cacheHit = "local";
644
+ waitedForBuild = shared.hit.waited;
709
645
  }
710
646
  }
711
647
  const prepareDeviceApp = async (path, { fresh }) => {
@@ -827,6 +763,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
827
763
  }
828
764
  const mutatingSteps = [];
829
765
  if (d.needsPrebuild(root, "ios", isExpo)) {
766
+ step("prebuild");
830
767
  const result = await d.runPrebuild(root, "ios", logWriter());
831
768
  if (result?.failed) {
832
769
  phase("prebuild", "FAILED");
@@ -844,6 +781,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
844
781
  const podState = d.readPodState(root);
845
782
  const action = podAction(podState, d.podsAreStale(podState.lockText, podState.manifestText));
846
783
  if (action.install) {
784
+ step("pods");
847
785
  const result = await d.runPodInstall(root, logWriter(), { estimateMs: estimates().podsMs });
848
786
  const podCommand = result?.command || "pod install";
849
787
  for (const line of result?.notes || []) note(chalk.dim(phaseLine("pods", line)));
@@ -903,6 +841,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
903
841
  }
904
842
  }
905
843
  if (!appPath) {
844
+ step("compile");
906
845
  phase("build", `compiling ${configuration || "Debug"} with xcodebuild`);
907
846
  const result = await d.buildIos({
908
847
  root,
@@ -977,7 +916,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
977
916
  await resolveInitialFingerprint();
978
917
  if (!easBuild) {
979
918
  remote = await resolveRemoteArtifact();
980
- await waitForSharedBuild();
919
+ await awaitSharedBuild();
981
920
  await prepareCachedArtifact();
982
921
  await buildArtifact();
983
922
  }
@@ -1229,7 +1168,7 @@ function profileGate(input) {
1229
1168
  if (!input.profile) return {
1230
1169
  ok: false,
1231
1170
  code: SIGNING_CODES.noProfile,
1232
- reason: "`security cms -D` could not decode the app bundle's embedded.mobileprovision.",
1171
+ reason: "`openssl smime` could not decode the app bundle's embedded.mobileprovision.",
1233
1172
  remedy: `The profile inside the app is corrupt. Delete the build cache entry with \`stim gc --delete\`, then ${XCODE_STEP}`
1234
1173
  };
1235
1174
  const profile = input.profile;
@@ -1335,10 +1274,13 @@ function readEmbeddedProfile(appPath, { exec = null } = {}) {
1335
1274
  try {
1336
1275
  return {
1337
1276
  present: true,
1338
- profile: parseProvisioningProfilePlist(executor.runFile("security", [
1339
- "cms",
1340
- "-D",
1341
- "-i",
1277
+ profile: parseProvisioningProfilePlist(executor.runFile("/usr/bin/openssl", [
1278
+ "smime",
1279
+ "-verify",
1280
+ "-noverify",
1281
+ "-inform",
1282
+ "DER",
1283
+ "-in",
1342
1284
  path
1343
1285
  ], { timeoutMs: SIGNING_TIMEOUT_MS }))
1344
1286
  };
@@ -1624,7 +1566,6 @@ const DEFAULT_DEPS = {
1624
1566
  devClientScheme,
1625
1567
  getProject,
1626
1568
  upsertProject,
1627
- projectShortcut,
1628
1569
  checkDeviceCapacity,
1629
1570
  ensureOwnedDevice,
1630
1571
  listIosRuntimes,
@@ -1838,6 +1779,12 @@ function reportLaunchErrors(errors, note, root) {
1838
1779
  for (const line of report.lines) note(chalk.yellow(phaseLine("launch", line)));
1839
1780
  return report.lines.length > 0;
1840
1781
  }
1782
+ function installsOverWifi(d, udid, selectedWireless, note) {
1783
+ const current = d.listIosDevices().find((entry) => entry.udid === udid);
1784
+ const wireless = current ? isWirelessIosDevice(current) : selectedWireless;
1785
+ if (wireless) note(phaseLine("device", `${current?.name ?? udid} is paired over Wi-Fi, so the install and launch go over the network and take longer than over a cable`));
1786
+ return wireless;
1787
+ }
1841
1788
  function cleanAdoptedIosApps({ d, root, slot, udid, bundleId, phase, note }) {
1842
1789
  const swept = d.clearOtherUserApps({
1843
1790
  udid,
@@ -1878,7 +1825,7 @@ function recordIosReloadTarget({ d, root, physical, remoteDevice, bundleId, udid
1878
1825
  function simulatorLaunchFailureRemedy(remote, udid, bundleId, logFile) {
1879
1826
  return `${remote ? "Run" : "If the simulator timed out, run `stim doctor --platform ios` and resolve any reported host memory pressure before retrying. Otherwise run"} \`xcrun simctl launch --terminate-running-process --console ${udid} ${bundleId}\` to see what the app reports, and check ${logFile}.`;
1880
1827
  }
1881
- async function finishIosRun({ artifact, d, root, slot, json, release, configuration, buildScheme, isExpo, metroCheck, metroPort, logsDir, logFile, device, udid, physical, lanAddress, lanOriginUrl, remoteDevice, bootPromise, bootDuration, fail, phase, note, logWriter, elapsed, startedAt, closeWriter, lease, releaseLease, recordRun }) {
1828
+ async function finishIosRun({ artifact, d, root, slot, json, release, configuration, buildScheme, isExpo, metroCheck, metroPort, logsDir, logFile, device, udid, physical, wireless, lanAddress, lanOriginUrl, remoteDevice, bootPromise, bootDuration, fail, phase, note, logWriter, elapsed, startedAt, closeWriter, lease, releaseLease, recordRun, enterPhase }) {
1882
1829
  const { path: appPath, bundleId: initialBundleId, failureFields: buildFailure, cache } = artifact;
1883
1830
  const { hit: cacheHit, providerName, readEnabled: useBuildCache, waitedForBuild, compilation: compilationCache } = cache;
1884
1831
  const storeHash = cache.identity?.fingerprint ?? null;
@@ -1918,6 +1865,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
1918
1865
  }
1919
1866
  });
1920
1867
  if (bundleId) d.upsertProject(root, { bundleId });
1868
+ enterPhase("install");
1921
1869
  const booted = await bootPromise;
1922
1870
  if (!booted?.ok) return fail({
1923
1871
  code: booted?.code || "STIM_NO_DEVICE",
@@ -1934,7 +1882,9 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
1934
1882
  let launched = null;
1935
1883
  let launchedAt = d.now();
1936
1884
  if (physical) {
1937
- const lostBeforeInstall = raiseLeaseFor(DEVICECTL_INSTALL_TIMEOUT_MS, true);
1885
+ const overWifi = installsOverWifi(d, udid, wireless, note);
1886
+ const bounds = iosDeviceBounds(overWifi);
1887
+ const lostBeforeInstall = raiseLeaseFor(bounds.installMs, true);
1938
1888
  if (lostBeforeInstall) return fail(lostBeforeInstall);
1939
1889
  await d.stopPreviousCollector({
1940
1890
  root,
@@ -1944,8 +1894,9 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
1944
1894
  const installed = d.installIosDeviceApp({
1945
1895
  udid,
1946
1896
  appPath,
1947
- bundleId
1948
- });
1897
+ bundleId,
1898
+ wireless: overWifi
1899
+ }, { beforeStep: () => raiseLeaseFor(bounds.installMs, false) });
1949
1900
  if (installed?.failed) {
1950
1901
  dropSwapDir();
1951
1902
  return fail({
@@ -1970,8 +1921,9 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
1970
1921
  });
1971
1922
  }
1972
1923
  dropSwapDir();
1973
- raiseLeaseFor(COLLECTOR_EXIT_WAIT_MS + LAUNCH_PROBE_TIMEOUT_MS, false);
1924
+ raiseLeaseFor(COLLECTOR_EXIT_WAIT_MS + bounds.launchMs, false);
1974
1925
  const payloadUrl = scheme && metroPort !== null && lanAddress ? devClientUrl(scheme, metroPort, lanAddress) : null;
1926
+ enterPhase("launch");
1975
1927
  const launchTimer = stepTimer(d.now);
1976
1928
  launchedAt = d.now();
1977
1929
  logWriter().write({
@@ -2012,10 +1964,11 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2012
1964
  bundleId,
2013
1965
  appName: appName ?? bundleId,
2014
1966
  collectorPid: collector.pid,
1967
+ wireless: overWifi,
2015
1968
  readRecords: () => readCollectorRecords(logsDir).filter((entry) => Number(entry.ts) >= launchedAt && (entry.slot ?? "default") === (slot ?? "default"))
2016
1969
  });
2017
1970
  if (started.failed || !started.pid) return fail({
2018
- code: "STIM_LAUNCH_FAILED",
1971
+ code: started.code ?? "STIM_LAUNCH_FAILED",
2019
1972
  message: started.reason ?? `${bundleId} did not start on ${udid}.`,
2020
1973
  remedy: started.remedy ?? null,
2021
1974
  lines: started.lines ?? [],
@@ -2088,6 +2041,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2088
2041
  appExecutable,
2089
2042
  note
2090
2043
  });
2044
+ enterPhase("launch");
2091
2045
  const launchTimer = stepTimer(d.now);
2092
2046
  launchedAt = d.now();
2093
2047
  logWriter().write({
@@ -2257,18 +2211,33 @@ function iosCommand(program) {
2257
2211
  registerIos(program);
2258
2212
  }
2259
2213
  function registerIos(program, deps = {}) {
2260
- program.command("ios").description("Build (or restore from the fingerprint cache), install and launch this workspace's app on its owned simulator, wired to the reserved Metro port. Requires a running dev server (`stim start`).").option("--eas-profile <name>", "Download a matching EAS development build; on a miss, print the build command without running it").option("--slot <name>", "Reusable device slot within this workspace (default: default)", validateDeviceSlot).option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--scheme <name>", "Shared Xcode app scheme to build; overrides automatic scheme selection, not the app URL scheme").option("--no-metro-check", "Skip the \"is this workspace's dev server running?\" gate and build anyway").option("--no-build-cache", "Build fresh, ignoring cached artifacts (local and the project's build-cache provider); the fresh build still replaces the cache entry").option("--configuration <name>", "Xcode configuration to build (e.g. Release). A non-Debug configuration embeds the JS bundle and skips Metro entirely. Overrides the ios.configuration setting. Default: Debug").option("--device-type <name>", "Simulator model to create this workspace's owned sim as, exactly as `xcrun simctl list devicetypes` names it (e.g. \"iPad Pro 13-inch (M4)\"). Overrides the ios.deviceType setting for this invocation. A model no installed runtime can create refuses with STIM_BAD_ARG and prints the models they do offer.").option("--runtime <version>", "Simulator runtime to create this workspace's owned sim on, as a version (\"18.5\") or a runtime's full name (\"iOS 18.5\"); nothing else matches. Overrides the ios.runtime setting for this invocation. An unknown version refuses with STIM_BAD_ARG and prints the installed runtimes.").option("--simulator-app <app>", "Open the owned simulator in xcode or siniulator for this run, overriding the machine iosSimulatorApp setting. Also opens an already running simulator; local simulators only.").option("--device [udid]", "Build the iphoneos slice for a connected iPhone, install it, and launch it, instead of using this workspace's owned simulator. With no UDID, the first connected device this workspace can lease is used. In Debug the app is wired to this workspace's Metro over the LAN. Stim never creates, boots, or deletes a physical device.").option("--remote <backend>", "Install and launch on a remote device with proxy or EAS. Builds are local unless --eas-profile selects an existing EAS build.", (value) => {
2214
+ program.command("ios").description("Build (or restore from the fingerprint cache), install and launch this workspace's app on its owned simulator, wired to the reserved Metro port. Requires a running dev server (`stim start`).").option("--eas-profile <name>", "Download a matching EAS development build; on a miss, print the build command without running it").option("--slot <name>", "Reusable device slot within this workspace (default: default)", parseDeviceSlotOption).option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--scheme <name>", "Shared Xcode app scheme to build; overrides automatic scheme selection, not the app URL scheme").option("--no-metro-check", "Skip the \"is this workspace's dev server running?\" gate and build anyway").option("--no-build-cache", "Build fresh, ignoring cached artifacts (local and the project's build-cache provider); the fresh build still replaces the cache entry").option("--configuration <name>", "Xcode configuration to build (e.g. Release). A non-Debug configuration embeds the JS bundle and skips Metro entirely. Overrides the ios.configuration setting. Default: Debug").option("--device-type <name>", "Simulator model to create this workspace's owned sim as, exactly as `xcrun simctl list devicetypes` names it (e.g. \"iPad Pro 13-inch (M5)\"). Overrides the ios.deviceType setting for this invocation. A model no installed runtime can create refuses with STIM_BAD_ARG and prints the models they do offer.").option("--runtime <version>", "Simulator runtime to create this workspace's owned sim on, as a version (\"18.5\") or a runtime's full name (\"iOS 18.5\"); nothing else matches. Overrides the ios.runtime setting for this invocation. An unknown version refuses with STIM_BAD_ARG and prints the installed runtimes.").option("--simulator-app <app>", "Open the owned simulator in xcode, siniulator, or stim-desktop for this run, overriding the machine iosSimulatorApp setting. Also opens an already running simulator; local simulators only.").option("--device [udid]", "Build the iphoneos slice for a connected iPhone, install it, and launch it, instead of using this workspace's owned simulator. The phone can be cabled or paired over Wi-Fi. With no UDID, the first cabled device this workspace can lease is used, waiting for one that is busy; a Wi-Fi device is used only when no cabled one is connected and ready. In Debug the app is wired to this workspace's Metro over the LAN. Stim never creates, boots, or deletes a physical device.").option("--remote <backend>", "Install and launch on a remote device with proxy or EAS. Builds are local unless --eas-profile selects an existing EAS build.", (value) => {
2261
2215
  if (REMOTE_DEVICE_BACKENDS.includes(value)) return value;
2262
2216
  throw new InvalidArgumentError(`expected one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}`);
2263
2217
  }).option("--wait <seconds>", "How long to wait for another workspace to release the phone it leases, before refusing with STIM_DEVICE_BUSY (default 60, 0 refuses at once). Only with --device.").option("--no-wait", "Install on a phone another workspace leases instead of waiting: this run takes no lease and, when both workspaces build the same app id, the install terminates the holder's running app. Only with --device.").action(async (opts) => {
2264
2218
  const root = (deps.findProjectRoot ?? DEFAULT_DEPS.findProjectRoot)(process.cwd());
2265
- const run = () => runIos({
2219
+ const run = (progress) => runIos({
2266
2220
  ...opts,
2267
2221
  waitConflict: waitFlagConflict(process.argv)
2268
- }, deps);
2269
- const completion = root ? await withWorkspaceProcessLock(workspaceDir(root), "native-run", run, {
2222
+ }, deps, progress);
2223
+ const completion = root ? await withWorkspaceProcessLock(workspaceDir(root), "native-run", async (claim) => {
2224
+ recordWorkspaceUse(root);
2225
+ const progress = startBuildProgress({
2226
+ root,
2227
+ platform: "ios",
2228
+ slot: opts.slot ?? "default",
2229
+ claim,
2230
+ note: (line) => writeNote(chalk.dim(line))
2231
+ });
2232
+ try {
2233
+ return await run(progress);
2234
+ } finally {
2235
+ progress.clear();
2236
+ }
2237
+ }, {
2270
2238
  external: true,
2271
- waitMs: 18e5
2239
+ waitMs: 18e5,
2240
+ declareSpawns: true
2272
2241
  }) : await run();
2273
2242
  if (!completion) process.exit(1);
2274
2243
  else if (completion.uploadsAbandoned) exitAfterFlush(0);
@@ -2332,7 +2301,7 @@ function iosSlotDeps(d, slot) {
2332
2301
  }
2333
2302
  return d;
2334
2303
  }
2335
- async function runIos(opts = {}, overrides = {}) {
2304
+ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2336
2305
  const slot = validateDeviceSlot(opts.slot);
2337
2306
  let d = iosSlotDeps({
2338
2307
  ...DEFAULT_DEPS,
@@ -2346,16 +2315,7 @@ async function runIos(opts = {}, overrides = {}) {
2346
2315
  const started = d.now();
2347
2316
  const startedAt = new Date(started).toISOString();
2348
2317
  const elapsed = () => d.now() - started;
2349
- const foundRoot = d.findProjectRoot(process.cwd());
2350
- if (!foundRoot) {
2351
- note(chalk.red("Not in a React Native project (no package.json found)."));
2352
- process.exitCode = 1;
2353
- return null;
2354
- }
2355
- const root = foundRoot;
2356
- const projectProblem = appProjectProblem(root);
2357
- if (projectProblem) {
2358
- const { message, remedy } = projectProblem;
2318
+ const refuseProject = ({ message, remedy }) => {
2359
2319
  note(chalk.red(phaseLine("error", message)));
2360
2320
  note(chalk.dim(phaseLine("remedy", remedy)));
2361
2321
  note(chalk.red(phaseLine("failed", "STIM_NO_PROJECT")));
@@ -2366,7 +2326,12 @@ async function runIos(opts = {}, overrides = {}) {
2366
2326
  }));
2367
2327
  process.exitCode = 1;
2368
2328
  return null;
2369
- }
2329
+ };
2330
+ const foundRoot = d.findProjectRoot(process.cwd());
2331
+ if (!foundRoot) return refuseProject(NO_PROJECT_REFUSAL);
2332
+ const root = foundRoot;
2333
+ const projectProblem = appProjectProblem(root);
2334
+ if (projectProblem) return refuseProject(projectProblem);
2370
2335
  try {
2371
2336
  await d.ensureWorkspaceStorage(root, { note });
2372
2337
  } catch (error) {
@@ -2404,7 +2369,8 @@ async function runIos(opts = {}, overrides = {}) {
2404
2369
  platform: "ios",
2405
2370
  write: (statsRun, at) => d.recordStats(statsRun, at),
2406
2371
  now: () => d.now(),
2407
- note: (line) => note(chalk.dim(line))
2372
+ note: (line) => note(chalk.dim(line)),
2373
+ phases: () => progress.durations()
2408
2374
  });
2409
2375
  const recordRun = stats.record;
2410
2376
  let compilationCache = COMPILATION_CACHE_NOT_RUN;
@@ -2477,12 +2443,6 @@ async function runIos(opts = {}, overrides = {}) {
2477
2443
  for (const key of unknownSettingKeys(settings)) note(phaseLine("setting", chalk.yellow(`Warning: setting "${key}" is not read by Stim and will be ignored.`)));
2478
2444
  const cacheProviderError = cacheProviderSettingError(settings);
2479
2445
  if (cacheProviderError) note(chalk.yellow(phaseLine("cache", `${cacheProviderError} Using the local cache.`)));
2480
- const remoteSettingError = remoteDeviceSettingError(settings);
2481
- if (remoteSettingError) return fail({
2482
- code: "STIM_BAD_ARG",
2483
- message: remoteSettingError,
2484
- remedy: `Set ios.remote and android.remote to one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}.`
2485
- });
2486
2446
  const poolError = parkedMaxSetting("ios").error;
2487
2447
  if (poolError) return fail({
2488
2448
  code: "STIM_BAD_ARG",
@@ -2514,7 +2474,7 @@ async function runIos(opts = {}, overrides = {}) {
2514
2474
  });
2515
2475
  if (physical && opts.remote) return fail({
2516
2476
  code: "STIM_BAD_ARG",
2517
- message: "--device builds for a phone cabled to this machine, and --remote installs on a remote one.",
2477
+ message: "--device builds for a phone connected to this machine, and --remote installs on a remote one.",
2518
2478
  remedy: "Pass only one of --device and --remote."
2519
2479
  });
2520
2480
  const noWait = opts.wait === false;
@@ -2571,12 +2531,10 @@ async function runIos(opts = {}, overrides = {}) {
2571
2531
  });
2572
2532
  if (remoteBackend !== "eas") registerProject();
2573
2533
  const proj = d.getProject(root);
2574
- const label = d.projectShortcut(root, proj);
2575
2534
  let remoteDevice = null;
2576
2535
  if (remoteBackend) {
2577
2536
  const resolved = await d.resolveRemoteContext({
2578
2537
  root,
2579
- label,
2580
2538
  backend: remoteBackend,
2581
2539
  easBin: d.resolveEasCliBin(root)?.file ?? null
2582
2540
  });
@@ -2597,6 +2555,7 @@ async function runIos(opts = {}, overrides = {}) {
2597
2555
  }
2598
2556
  const limits = d.getConcurrencyLimits();
2599
2557
  let physicalDevice = null;
2558
+ let wireless = false;
2600
2559
  if (physical && typeof deviceFlag !== "string") {
2601
2560
  const pooled = await d.selectFromPool({
2602
2561
  root,
@@ -2604,7 +2563,8 @@ async function runIos(opts = {}, overrides = {}) {
2604
2563
  idLabel: "udid",
2605
2564
  list: () => iosPoolCandidates(d.listIosDevices()).map((entry) => ({
2606
2565
  id: entry.udid,
2607
- name: entry.name
2566
+ name: entry.name,
2567
+ fallback: isWirelessIosDevice(entry)
2608
2568
  })),
2609
2569
  noCandidates: () => {
2610
2570
  const resolved = iosPoolNoCandidatesRefusal(d.listIosDevices());
@@ -2628,6 +2588,7 @@ async function runIos(opts = {}, overrides = {}) {
2628
2588
  udid: pooled.candidate.id,
2629
2589
  name: pooled.candidate.name ?? pooled.candidate.id
2630
2590
  };
2591
+ wireless = pooled.candidate.fallback === true;
2631
2592
  } else if (physical) {
2632
2593
  const resolved = resolveIosPhysicalDevice(typeof deviceFlag === "string" ? deviceFlag : null, d.listIosDevices());
2633
2594
  if (!resolved.udid) return fail({
@@ -2639,6 +2600,7 @@ async function runIos(opts = {}, overrides = {}) {
2639
2600
  udid: resolved.udid,
2640
2601
  name: resolved.name ?? resolved.udid
2641
2602
  };
2603
+ wireless = resolved.wireless === true;
2642
2604
  }
2643
2605
  if (!physical) {
2644
2606
  const capacity = d.checkDeviceCapacity({
@@ -2814,6 +2776,7 @@ async function runIos(opts = {}, overrides = {}) {
2814
2776
  boot,
2815
2777
  createdSessionId: remoteDevice.createdSessionId,
2816
2778
  abandonCreatedSession: remoteDevice.abandonCreatedSession,
2779
+ webPreviewUrl: remoteDevice.webPreviewUrl,
2817
2780
  writeState: d.writeWorkspaceState,
2818
2781
  register: registerProject
2819
2782
  }) : boot()).then((result) => {
@@ -2850,7 +2813,8 @@ async function runIos(opts = {}, overrides = {}) {
2850
2813
  note,
2851
2814
  logWriter,
2852
2815
  estimates,
2853
- stats
2816
+ stats,
2817
+ step: progress.step
2854
2818
  }
2855
2819
  }, d);
2856
2820
  if (!acquiredArtifact.ok) {
@@ -2868,7 +2832,7 @@ async function runIos(opts = {}, overrides = {}) {
2868
2832
  idLabel: "udid",
2869
2833
  waitSeconds,
2870
2834
  noWait,
2871
- installBoundMs: DEVICECTL_INSTALL_TIMEOUT_MS,
2835
+ installBoundMs: iosDeviceBounds(wireless).installMs,
2872
2836
  appId: artifact.bundleId ?? proj?.bundleId ?? null,
2873
2837
  holderAppId: (holder) => d.getProject(holder)?.bundleId ?? null,
2874
2838
  now: d.now,
@@ -2908,6 +2872,7 @@ async function runIos(opts = {}, overrides = {}) {
2908
2872
  device,
2909
2873
  udid,
2910
2874
  physical,
2875
+ wireless,
2911
2876
  lanAddress,
2912
2877
  lanOriginUrl,
2913
2878
  remoteDevice,
@@ -2923,7 +2888,8 @@ async function runIos(opts = {}, overrides = {}) {
2923
2888
  closeWriter: () => writer?.close?.(),
2924
2889
  lease: leaseHandle,
2925
2890
  releaseLease,
2926
- recordRun
2891
+ recordRun,
2892
+ enterPhase: progress.step
2927
2893
  });
2928
2894
  } finally {
2929
2895
  releaseLease();