stim 1.8.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +23 -155
  2. package/dist/{android-DWssqHQ8.mjs → android-787HhP5w.mjs} +168 -39
  3. package/dist/{android-BRgBYzU0.mjs → android-DBkeHwpm.mjs} +250 -200
  4. package/dist/{android-DeYdjjBP.mjs → android-DPov-fv2.mjs} +2 -2
  5. package/dist/{android-cas-SWIjzeuL.mjs → android-cas-CITmIeWY.mjs} +22 -14
  6. package/dist/android-cas-compiler.mjs +2 -2
  7. package/dist/app-install-CrJ6t_bv.mjs +819 -0
  8. package/dist/budget-BP4jZZL4.mjs +620 -0
  9. package/dist/build-progress-BXx-ZBe4.mjs +512 -0
  10. package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Pubfp8wV.mjs} +4 -4
  11. package/dist/cache-manifest.mjs +1 -1
  12. package/dist/cli.mjs +22 -19
  13. package/dist/collector-run.d.mts +7 -1
  14. package/dist/collector-run.mjs +10 -9
  15. package/dist/{command-output-B3ycpsYv.mjs → command-output-D5z2_bcF.mjs} +11 -2
  16. package/dist/{config-B7iyKWkL.mjs → config-BEq0Qw_a.mjs} +134 -121
  17. package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-r9UesA9a.mjs} +2 -2
  18. package/dist/{deps-oCXVNEqQ.mjs → deps-DP4eWqea.mjs} +8 -5
  19. package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-DJHoca49.mjs} +1 -1
  20. package/dist/{device-BUoZyMoR.mjs → dev-client-92U593KT.mjs} +1090 -660
  21. package/dist/{device-RdujjHLB.mjs → device-DhTZe7L4.mjs} +13 -20
  22. package/dist/{build-lock-CgtsB6TV.mjs → device-lease-CbEaIk7b.mjs} +481 -27
  23. package/dist/{device-pool-BxRwdufO.mjs → device-pool-Di8Q-9Ro.mjs} +7 -7
  24. package/dist/{device-remote-ByN8P6CB.mjs → device-remote-Dy1jGj3U.mjs} +645 -119
  25. package/dist/{doctor-BMt156qe.mjs → doctor-B-inAJmt.mjs} +137 -113
  26. package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-xxWKNO3-.mjs} +22 -17
  27. package/dist/exec-DK8KaVHK.mjs +133 -0
  28. package/dist/{gc-v2YcoJvm.mjs → gc-ChPFZMRC.mjs} +836 -813
  29. package/dist/{guide-BTAlD1_y.mjs → guide-DwmaD_Wa.mjs} +953 -209
  30. package/dist/guide-status-CWvti2Lg.mjs +125 -0
  31. package/dist/idle-DpRLp5lj.mjs +157 -0
  32. package/dist/{build-slots-C-oAHRT1.mjs → in-use-CjUluqIX.mjs} +102 -8
  33. package/dist/{ios-BcCiI4TS.mjs → ios-B2Fpqvlb.mjs} +60 -71
  34. package/dist/{ios-BP04pY5f.mjs → ios-Dd-hYJBh.mjs} +241 -234
  35. package/dist/{ios-device-CFXA46ez.mjs → ios-device-BiOsSVzd.mjs} +3 -2
  36. package/dist/{ios-device-CauFJiYx.mjs → ios-device-CtRdEVjx.mjs} +149 -52
  37. package/dist/launch-verify-CQS_Kg-C.mjs +365 -0
  38. package/dist/{logs-CakdBTtm.mjs → logs-MARIy6m9.mjs} +20 -15
  39. package/dist/metro-BWLOJ_Wr.mjs +409 -0
  40. package/dist/{named-ports-DzMOlmaX.mjs → named-ports-DiTfSz6p.mjs} +4 -4
  41. package/dist/{ndjson-BZwI1NBv.mjs → ndjson-CDY9IkVC.mjs} +23 -43
  42. package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
  43. package/dist/{ownership-Cp52QAK1.mjs → ownership-D2xbLVoz.mjs} +2 -3
  44. package/dist/{teardown-BJqEzPo_.mjs → ownership-DZjdx8Gq.mjs} +664 -612
  45. package/dist/{ports-BIbJaKNW.mjs → ports-CLSSFOuz.mjs} +3 -3
  46. package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
  47. package/dist/{project-DDZWf_16.mjs → project-BXZzOABt.mjs} +8 -3
  48. package/dist/{reload-BdypPbjM.mjs → reload-DoETJ3rl.mjs} +11 -13
  49. package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DVbb7kXH.mjs} +2 -2
  50. package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-BvOCeakp.mjs} +8 -7
  51. package/dist/server-expo-DBVTdult.mjs +2 -0
  52. package/dist/{settings-BXXorVM2.mjs → settings-DOugPlPz.mjs} +59 -159
  53. package/dist/settings-EWYNwe72.mjs +378 -0
  54. package/dist/settings.schema.json +1064 -0
  55. package/dist/simslim-Ca3487H6.mjs +436 -0
  56. package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-C5cg0fmk.mjs} +4 -4
  57. package/dist/spawn-claims-CUleC2cM.mjs +49 -0
  58. package/dist/{start-CmxtOKjU.mjs → start-F9gv5dcC.mjs} +159 -114
  59. package/dist/start-VoFsPSes.mjs +2 -0
  60. package/dist/{state-DZobzxLW.mjs → state-WGdIbi_d.mjs} +5 -3
  61. package/dist/{stats-BYcZ6n1h.mjs → stats-BAgJaLA4.mjs} +66 -5
  62. package/dist/{stats-CLg5RuDS.mjs → stats-DBYJ5bxL.mjs} +4 -4
  63. package/dist/status-DLCD-o3s.mjs +764 -0
  64. package/dist/status-Dr4fRkym.mjs +216 -0
  65. package/dist/{guide-status-Craq9z0t.mjs → stim-installations-CINXy_su.mjs} +3 -122
  66. package/dist/{stop-C8JW6kKp.mjs → stop-Dj4pfmyv.mjs} +111 -31
  67. package/dist/{stop-B1kOpYei.mjs → stop-ZvOeTmKb.mjs} +2 -2
  68. package/dist/supervisor-run.d.mts +11 -2
  69. package/dist/supervisor-run.mjs +228 -42
  70. package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-BXA96myD.mjs} +14 -9
  71. package/dist/workspaces-BTUyFfkv.mjs +1003 -0
  72. package/dist/worktree-4aZg2NnI.mjs +2 -0
  73. package/dist/{worktree-CFLA-6MX.mjs → worktree-BMS7GeqB.mjs} +523 -61
  74. package/dist/{worktree-FG_ZKKdb.mjs → worktree-BlizKDpZ.mjs} +200 -54
  75. package/dist/{xcode-B6YaWeg0.mjs → xcode-B2tBkf0m.mjs} +66 -57
  76. package/package.json +6 -6
  77. package/skill/SKILL.md +2 -0
  78. package/dist/app-install-BDbsSgCl.mjs +0 -1175
  79. package/dist/dev-client-BmT-ioFI.mjs +0 -797
  80. package/dist/device-lease-Ffz_M5t0.mjs +0 -359
  81. package/dist/exec-B29XNuhP.mjs +0 -77
  82. package/dist/logs-query-BGL-Pl12.mjs +0 -290
  83. package/dist/metro-C7bcJ0uo.mjs +0 -233
  84. package/dist/ownership-wolE-0Do.mjs +0 -470
  85. package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
  86. package/dist/reclaim-KJ-0uWEF.mjs +0 -361
  87. package/dist/server-expo-YDDG3eP7.mjs +0 -2
  88. package/dist/spawn-entry-nCE6m770.mjs +0 -14
  89. package/dist/status-DdkLki47.mjs +0 -426
  90. package/dist/workspace-state-BT5E3SA2.mjs +0 -146
  91. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
@@ -1,38 +1,40 @@
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";
25
- 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";
1
+ import { L as deviceSlotFileKey, R as deviceSlotKey, V as parseDeviceSlotOption, W as validateDeviceSlot, f as getProject, k as ownership_claim_exports, l as getConcurrencyLimits, w as upsertProject } from "./config-BEq0Qw_a.mjs";
2
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
+ import { r as createNdjsonWriter } from "./ndjson-CDY9IkVC.mjs";
4
+ import { O as workspaceDir, c as signalProcessTree, h as recordWorkspaceUse, i as pidExists, j as workspaceLogsDir, m as readWorkspaceState, s as resolveProjectMetro, y as writeWorkspaceState } from "./metro-BWLOJ_Wr.mjs";
5
+ import { D as makeTemporaryDirectory, O as removeTemporaryEntry, s as gitCommonDir, y as repoRoot } from "./worktree-BlizKDpZ.mjs";
6
+ import { a as detectIsExpo, i as detectBundleId, n as appProjectProblem, o as findProjectRoot, t as NO_PROJECT_REFUSAL } from "./project-BXZzOABt.mjs";
7
+ import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-WGdIbi_d.mjs";
8
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BXA96myD.mjs";
9
+ 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-D5z2_bcF.mjs";
10
+ import { A as resolveBuild, C as fingerprintDiffRecord, D as providerDownloadPath, E as prepareProviderDownloadDir, N as untrackedMissLine, O as providerUploadOutcome, P as untrackedNativeFiles, S as filesystemBuildCapability, T as fingerprintProject, _ as waitForBuild, b as describeFingerprintMiss, g as releaseBuildLock, j as storeBuild, k as refingerprintAfterMutation, m as acquireBuildLock, v as waitForSharedBuild, w as fingerprintDiffSuffix, y as buildCacheKey } from "./device-lease-CbEaIk7b.mjs";
11
+ import { n as NO_BUILD_PROGRESS, s as startBuildProgress } from "./build-progress-BXx-ZBe4.mjs";
12
+ import { A as resolveCacheProviderConfig, E as publicUrlSetting, I as tunnelModeSetting, L as unknownSettingKeys, P as settingShapeErrors, S as metroWarmupUrlSetting, U as optimizationBuildProfile, V as artifactCachePolicy, W as resolveOptimizations, _ as iosSigningIdentitySha1SettingError, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, f as iosLanHostSetting, g as iosSigningIdentitySha1Setting, h as iosSigningIdentitySettingError, j as resolveSettings, k as remoteIosSetting, m as iosSigningIdentitySetting, p as iosLanHostSettingError, r as REMOTE_DEVICE_BACKENDS, z as detectProviders } from "./settings-DOugPlPz.mjs";
13
+ import { h as POOL_SETTING_REMEDY, y as parkedMaxSetting } from "./ownership-DZjdx8Gq.mjs";
14
+ import { D as parseIosSimulatorApp, f as listIosRuntimes } from "./ios-B2Fpqvlb.mjs";
15
+ import { a as acquireBuildSlot, s as releaseBuildSlot } from "./in-use-CjUluqIX.mjs";
16
+ import { n as verifyCollectorOwnership } from "./ownership-D2xbLVoz.mjs";
17
+ import { A as UPLOAD_TIMEOUT_MS, B as resolveRemote, F as isEasAuthFailureText, I as loadProjectProvider, M as checkEasAuth, N as easAuthNote, O as gateMetroOrigin, P as exitAfterFlush, V as uploadRemote, a as ensureMetroReachable, j as cacheLevel, k as RESOLVE_TIMEOUT_MS, l as remoteIosDeps, n as binOnPath, o as ensureRemoteBootOwned, r as bundleEntryPoint, u as resolveRemoteContext, w as ownedSessionName, z as resolveEasCliBin } from "./device-remote-Dy1jGj3U.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-CrJ6t_bv.mjs";
19
+ 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-CQS_Kg-C.mjs";
20
+ import { c as unknownIosRuntimeRefusal, d as isEasBuildFailure, f as resolveEasDevelopmentBuild, r as checkDeviceCapacity, s as unknownIosDeviceTypeRefusal, u as easDeviceBuildRemedy } from "./simslim-Ca3487H6.mjs";
21
+ 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-Di8Q-9Ro.mjs";
22
+ import { _ as isPhysicalDeviceRequest, a as swapJsBundle, b as warmMetro, c as runPrebuild, d as ensureOwnedDevice, g as ensureWorkspaceStorageSafely, h as ensureDevServer, l as staleNativeDirRefusal, m as clearIosAdoptionPending, n as pickDevClientScheme, o as planPrebuild, r as schemesFromInfoPlist, s as recordPrebuild, t as devClientScheme, u as ensureBooted, v as launchOutcomeRecord, y as sleep } from "./dev-client-92U593KT.mjs";
23
+ import { t as budgetGate } from "./budget-BP4jZZL4.mjs";
24
+ import { f as spawnEntry, s as startDevServer } from "./start-F9gv5dcC.mjs";
25
+ import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-BAgJaLA4.mjs";
26
+ 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-B2tBkf0m.mjs";
27
+ import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-C5cg0fmk.mjs";
28
+ import { a as printNativeCrashReport, n as IOS_CRASH_REPORT_RETRY, o as simulatorConsolePaths, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-xxWKNO3-.mjs";
29
+ 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-CtRdEVjx.mjs";
30
+ import { a as runPodInstall, n as podsAreStale, r as readPodState } from "./deps-DP4eWqea.mjs";
29
31
  import { existsSync, mkdirSync, mkdtempSync, openSync, rmSync, writeFileSync } from "node:fs";
30
32
  import { InvalidArgumentError } from "commander";
31
- import { X509Certificate } from "node:crypto";
32
33
  import { basename, join } from "node:path";
34
+ import { X509Certificate } from "node:crypto";
33
35
  import { networkInterfaces, tmpdir } from "node:os";
34
- import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
35
36
  import chalk from "chalk";
37
+ import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
36
38
  //#region src/engine/ios-lan.ts
37
39
  const IP_TXT = "ip.txt";
38
40
  function chooseLanAddress({ pinned = null, candidates }) {
@@ -91,6 +93,7 @@ function copyAppAside(appPath, { exec = null, mkdtemp = () => makeTemporaryDirec
91
93
  ]);
92
94
  } catch {
93
95
  try {
96
+ removeTemporaryEntry(copy);
94
97
  e.runFile("cp", [
95
98
  "-R",
96
99
  appPath,
@@ -170,7 +173,7 @@ function deviceModelRefusal({ slot, deviceTypeFlag, runtimeFlag, deviceType, run
170
173
  if (typeof deviceTypeFlag === "string" && deviceTypeFlag.trim() === "") return {
171
174
  code: "STIM_BAD_ARG",
172
175
  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)\"."
176
+ remedy: "Pass `--device-type <name>` with a model `xcrun simctl list devicetypes` names, e.g. \"iPad Pro 13-inch (M5)\"."
174
177
  };
175
178
  if (typeof runtimeFlag === "string" && runtimeFlag.trim() === "") return {
176
179
  code: "STIM_BAD_ARG",
@@ -248,7 +251,7 @@ function lastBuildRecord({ fingerprint = null, cacheKey = null, cacheHit = false
248
251
  if (errorCode) record.errorCode = errorCode;
249
252
  return record;
250
253
  }
251
- function iosFacts({ slot, udid, deviceName, deviceType = null, runtime = null, fingerprint, configuration = null, scheme = null, cacheKey, cacheHit, cacheSkipped = false, compilationCache = COMPILATION_CACHE_NOT_RUN, waitedForBuild = null, appPath, bundleId, installSkipped = false, metroPort, logsDir, durationMs, launched = true, webPreviewUrl = null, lease }) {
254
+ function iosFacts({ slot, udid, deviceName, deviceType = null, runtime = null, fingerprint, configuration = null, scheme = null, cacheKey, cacheHit, cacheSkipped = false, compilationCache = COMPILATION_CACHE_NOT_RUN, waitedForBuild = null, appPath, bundleId, installSkipped = false, metroPort, logsDir, durationMs, launched = true, webPreviewUrl = null, lease, devServer = null }) {
252
255
  return {
253
256
  platform: "ios",
254
257
  ...slot && slot !== "default" ? { slot } : {},
@@ -275,7 +278,8 @@ function iosFacts({ slot, udid, deviceName, deviceType = null, runtime = null, f
275
278
  logs: { dir: logsDir },
276
279
  durationMs,
277
280
  ...webPreviewUrl ? { webPreviewUrl } : {},
278
- ...lease === void 0 ? {} : { lease }
281
+ ...lease === void 0 ? {} : { lease },
282
+ ...devServer ? { devServer } : {}
279
283
  };
280
284
  }
281
285
  function writeLastBuild(root, record, { write = writeWorkspaceState } = {}) {
@@ -301,7 +305,7 @@ async function finishIosUpload(uploadPending, remote, phase, note) {
301
305
  }
302
306
  return false;
303
307
  }
304
- function reportIosResult({ slot, d, root, json, release, configuration, buildScheme = null, metroCheck, metroPort, logsDir, device, udid, appPath, bundleId, installSkipped, elapsed, startedAt, storeHash, storeKey, cacheHit, compilationCache, useBuildCache, waitedForBuild, launchState, launchWarning, providerName, closeWriter, webPreviewUrl, lease, recordRun }) {
308
+ function reportIosResult({ slot, d, root, json, release, configuration, buildScheme = null, metroCheck, metroPort, logsDir, device, udid, appPath, bundleId, installSkipped, elapsed, startedAt, storeHash, storeKey, cacheHit, compilationCache, useBuildCache, waitedForBuild, launchState, launchWarning, providerName, closeWriter, webPreviewUrl, lease, recordRun, reclaimed = [], devServer = null }) {
305
309
  const durationMs = elapsed();
306
310
  recordRun({
307
311
  failed: false,
@@ -343,15 +347,19 @@ function reportIosResult({ slot, d, root, json, release, configuration, buildSch
343
347
  durationMs,
344
348
  launched: launchState,
345
349
  webPreviewUrl,
346
- lease
350
+ lease,
351
+ devServer
347
352
  });
348
- if (json) console.log(JSON.stringify(facts));
353
+ if (json) console.log(JSON.stringify(reclaimed.length ? {
354
+ ...facts,
355
+ reclaimed
356
+ } : facts));
349
357
  else {
350
358
  const summary = `${launchWarning ? "WARNING" : "OK"}: ${bundleId} on ${deviceLabel(device, udid)}, ` + (release ? `${configuration} (embedded JS, no Metro)` : `Metro port ${metroPort}`) + ` (${cacheDescription(cacheHit, providerName)}, ${formatDuration(durationMs)})`;
351
359
  const outcome = launchWarning ? chalk.yellow(`${summary} -- ${launchWarning}`) : launchState === "unverified" ? chalk.yellow(`${summary} -- launch UNVERIFIED`) : launchState === "bundling" ? chalk.green(`${summary} -- bundle requested, still building`) : chalk.green(summary);
352
360
  const deviceName = device?.deviceName ?? device?.name ?? udid;
353
361
  const cacheResult = useBuildCache ? cacheDescription(cacheHit, providerName) : "bypassed; built";
354
- const metroResult = release ? `embedded (${configuration})` : !metroCheck ? `check skipped on port ${metroPort}` : launchState === "unverified" ? `state unverified on port ${metroPort}` : `running on port ${metroPort}`;
362
+ const metroResult = release ? `embedded (${configuration})` : !metroCheck ? `check skipped on port ${metroPort}` : launchState === "unverified" ? `state unverified on port ${metroPort}` : `running on port ${metroPort}${devServer ? ` (started: ${devServer.reason})` : ""}`;
355
363
  console.log([
356
364
  outcome,
357
365
  phaseLine("device", `${deviceName} (${udid})`),
@@ -383,7 +391,7 @@ var ArtifactRefusal = class extends Error {
383
391
  }
384
392
  };
385
393
  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;
394
+ const { phase, note, logWriter, estimates, stats, step } = progress;
387
395
  const physical = device !== null;
388
396
  const lanAddress = device?.lanAddress ?? null;
389
397
  const metroPort = device?.metroPort ?? null;
@@ -481,6 +489,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
481
489
  }
482
490
  return;
483
491
  }
492
+ step("cache-lookup");
484
493
  const fingerprintTimer = stepTimer(d.now);
485
494
  let computedFingerprint;
486
495
  try {
@@ -605,107 +614,40 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
605
614
  }
606
615
  return remote;
607
616
  }
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`)));
617
+ async function awaitSharedBuild() {
618
+ if (!useBuildCache || appPath) return;
619
+ const shared = await waitForSharedBuild({
620
+ platform: "ios",
621
+ key: cacheKey,
622
+ fingerprint,
623
+ root,
624
+ logFile,
625
+ command: "stim ios",
626
+ acquire: d.acquireBuildLock,
627
+ wait: d.waitForBuild,
628
+ now: d.now,
629
+ phase: (text) => {
630
+ step("wait");
631
+ phase("build", text);
632
+ },
633
+ warn: (text) => note(chalk.yellow(phaseLine("build", text))),
634
+ out: note
635
+ });
636
+ if (shared.refusal) fail({
637
+ ...shared.refusal,
638
+ build: {
639
+ fingerprint,
640
+ cacheKey,
641
+ cacheHit,
642
+ cacheSkipped: !useBuildCache
635
643
  }
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;
644
+ });
645
+ buildLock = shared.lock;
646
+ releasedWait = shared.released;
647
+ if (shared.hit) {
648
+ appPath = shared.hit.path;
649
+ cacheHit = "local";
650
+ waitedForBuild = shared.hit.waited;
709
651
  }
710
652
  }
711
653
  const prepareDeviceApp = async (path, { fresh }) => {
@@ -826,8 +768,19 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
826
768
  note(chalk.yellow(phaseLine("build", `could not take a build slot: ${e?.message || e}; building anyway`)));
827
769
  }
828
770
  const mutatingSteps = [];
829
- if (d.needsPrebuild(root, "ios", isExpo)) {
830
- const result = await d.runPrebuild(root, "ios", logWriter());
771
+ const prebuild = d.planPrebuild(root, "ios", {
772
+ isExpo,
773
+ fingerprint,
774
+ sources: fingerprintSources
775
+ });
776
+ if (prebuild === "refuse") fail({
777
+ ...staleNativeDirRefusal("ios"),
778
+ build: buildFailure
779
+ });
780
+ if (prebuild === "generate" || prebuild === "regenerate") {
781
+ step("prebuild");
782
+ recordPrebuild(root, "ios", null);
783
+ const result = await d.runPrebuild(root, "ios", logWriter(), { clean: prebuild === "regenerate" });
831
784
  if (result?.failed) {
832
785
  phase("prebuild", "FAILED");
833
786
  fail({
@@ -838,12 +791,25 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
838
791
  build: buildFailure
839
792
  });
840
793
  }
841
- phase("prebuild", `ios/ absent -> generated (${formatDuration(result?.durationMs ?? 0)})`);
794
+ phase("prebuild", `${prebuild === "generate" ? "ios/ absent -> generated" : "ios/ not generated from this fingerprint -> regenerated with --clean"} (${formatDuration(result?.durationMs ?? 0)})`);
842
795
  mutatingSteps.push("prebuild");
843
796
  }
797
+ if (isExpo && buildScheme !== void 0) {
798
+ const project = d.discoverXcodeProject(root);
799
+ if (project.error) fail({
800
+ ...project.error,
801
+ build: buildFailure
802
+ });
803
+ const schemeError = d.resolveScheme(project, { scheme: buildScheme }).error;
804
+ if (schemeError) fail({
805
+ ...schemeError,
806
+ build: buildFailure
807
+ });
808
+ }
844
809
  const podState = d.readPodState(root);
845
810
  const action = podAction(podState, d.podsAreStale(podState.lockText, podState.manifestText));
846
811
  if (action.install) {
812
+ step("pods");
847
813
  const result = await d.runPodInstall(root, logWriter(), { estimateMs: estimates().podsMs });
848
814
  const podCommand = result?.command || "pod install";
849
815
  for (const line of result?.notes || []) note(chalk.dim(phaseLine("pods", line)));
@@ -868,6 +834,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
868
834
  previousHash: fingerprint,
869
835
  fingerprint: d.fingerprintProject
870
836
  });
837
+ if (after && mutatingSteps.includes("prebuild")) recordPrebuild(root, "ios", after.hash);
871
838
  if (!after) {
872
839
  storeHash = null;
873
840
  storeKey = null;
@@ -903,6 +870,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
903
870
  }
904
871
  }
905
872
  if (!appPath) {
873
+ step("compile");
906
874
  phase("build", `compiling ${configuration || "Debug"} with xcodebuild`);
907
875
  const result = await d.buildIos({
908
876
  root,
@@ -977,7 +945,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
977
945
  await resolveInitialFingerprint();
978
946
  if (!easBuild) {
979
947
  remote = await resolveRemoteArtifact();
980
- await waitForSharedBuild();
948
+ await awaitSharedBuild();
981
949
  await prepareCachedArtifact();
982
950
  await buildArtifact();
983
951
  }
@@ -1229,7 +1197,7 @@ function profileGate(input) {
1229
1197
  if (!input.profile) return {
1230
1198
  ok: false,
1231
1199
  code: SIGNING_CODES.noProfile,
1232
- reason: "`security cms -D` could not decode the app bundle's embedded.mobileprovision.",
1200
+ reason: "`openssl smime` could not decode the app bundle's embedded.mobileprovision.",
1233
1201
  remedy: `The profile inside the app is corrupt. Delete the build cache entry with \`stim gc --delete\`, then ${XCODE_STEP}`
1234
1202
  };
1235
1203
  const profile = input.profile;
@@ -1335,10 +1303,13 @@ function readEmbeddedProfile(appPath, { exec = null } = {}) {
1335
1303
  try {
1336
1304
  return {
1337
1305
  present: true,
1338
- profile: parseProvisioningProfilePlist(executor.runFile("security", [
1339
- "cms",
1340
- "-D",
1341
- "-i",
1306
+ profile: parseProvisioningProfilePlist(executor.runFile("/usr/bin/openssl", [
1307
+ "smime",
1308
+ "-verify",
1309
+ "-noverify",
1310
+ "-inform",
1311
+ "DER",
1312
+ "-in",
1342
1313
  path
1343
1314
  ], { timeoutMs: SIGNING_TIMEOUT_MS }))
1344
1315
  };
@@ -1624,8 +1595,8 @@ const DEFAULT_DEPS = {
1624
1595
  devClientScheme,
1625
1596
  getProject,
1626
1597
  upsertProject,
1627
- projectShortcut,
1628
1598
  checkDeviceCapacity,
1599
+ budgetGate,
1629
1600
  ensureOwnedDevice,
1630
1601
  listIosRuntimes,
1631
1602
  ensureBooted,
@@ -1635,7 +1606,7 @@ const DEFAULT_DEPS = {
1635
1606
  ensureRemoteBootOwned,
1636
1607
  detectProviders,
1637
1608
  resolveProjectMetro,
1638
- resolveMetroWithRetry,
1609
+ startDevServer,
1639
1610
  warmMetro,
1640
1611
  readWorkspaceState,
1641
1612
  pidExists,
@@ -1656,7 +1627,7 @@ const DEFAULT_DEPS = {
1656
1627
  resolveEasCliBin,
1657
1628
  resolveRemote,
1658
1629
  uploadRemote,
1659
- needsPrebuild,
1630
+ planPrebuild,
1660
1631
  runPrebuild,
1661
1632
  readPodState,
1662
1633
  podsAreStale,
@@ -1838,6 +1809,12 @@ function reportLaunchErrors(errors, note, root) {
1838
1809
  for (const line of report.lines) note(chalk.yellow(phaseLine("launch", line)));
1839
1810
  return report.lines.length > 0;
1840
1811
  }
1812
+ function installsOverWifi(d, udid, selectedWireless, note) {
1813
+ const current = d.listIosDevices().find((entry) => entry.udid === udid);
1814
+ const wireless = current ? isWirelessIosDevice(current) : selectedWireless;
1815
+ 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`));
1816
+ return wireless;
1817
+ }
1841
1818
  function cleanAdoptedIosApps({ d, root, slot, udid, bundleId, phase, note }) {
1842
1819
  const swept = d.clearOtherUserApps({
1843
1820
  udid,
@@ -1875,10 +1852,14 @@ function recordIosReloadTarget({ d, root, physical, remoteDevice, bundleId, udid
1875
1852
  note(chalk.yellow(phaseLine("state", `could not record iOS launch: ${error?.message || error}`)));
1876
1853
  }
1877
1854
  }
1878
- function simulatorLaunchFailureRemedy(remote, udid, bundleId, logFile) {
1879
- 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}.`;
1855
+ function installFailureRemedy(remoteDevice) {
1856
+ return remoteDevice?.failureRemedy() ?? "Check that the simulator is booted and that the app was built for the simulator SDK.";
1857
+ }
1858
+ function launchFailureRemedy(remoteDevice, udid, bundleId, logFile) {
1859
+ if (remoteDevice) return remoteDevice.failureRemedy();
1860
+ return `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
1861
  }
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 }) {
1862
+ 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, reclaimed, devServer, enterPhase }) {
1882
1863
  const { path: appPath, bundleId: initialBundleId, failureFields: buildFailure, cache } = artifact;
1883
1864
  const { hit: cacheHit, providerName, readEnabled: useBuildCache, waitedForBuild, compilation: compilationCache } = cache;
1884
1865
  const storeHash = cache.identity?.fingerprint ?? null;
@@ -1918,6 +1899,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
1918
1899
  }
1919
1900
  });
1920
1901
  if (bundleId) d.upsertProject(root, { bundleId });
1902
+ enterPhase("install");
1921
1903
  const booted = await bootPromise;
1922
1904
  if (!booted?.ok) return fail({
1923
1905
  code: booted?.code || "STIM_NO_DEVICE",
@@ -1934,7 +1916,9 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
1934
1916
  let launched = null;
1935
1917
  let launchedAt = d.now();
1936
1918
  if (physical) {
1937
- const lostBeforeInstall = raiseLeaseFor(DEVICECTL_INSTALL_TIMEOUT_MS, true);
1919
+ const overWifi = installsOverWifi(d, udid, wireless, note);
1920
+ const bounds = iosDeviceBounds(overWifi);
1921
+ const lostBeforeInstall = raiseLeaseFor(bounds.installMs, true);
1938
1922
  if (lostBeforeInstall) return fail(lostBeforeInstall);
1939
1923
  await d.stopPreviousCollector({
1940
1924
  root,
@@ -1944,8 +1928,9 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
1944
1928
  const installed = d.installIosDeviceApp({
1945
1929
  udid,
1946
1930
  appPath,
1947
- bundleId
1948
- });
1931
+ bundleId,
1932
+ wireless: overWifi
1933
+ }, { beforeStep: () => raiseLeaseFor(bounds.installMs, false) });
1949
1934
  if (installed?.failed) {
1950
1935
  dropSwapDir();
1951
1936
  return fail({
@@ -1970,8 +1955,9 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
1970
1955
  });
1971
1956
  }
1972
1957
  dropSwapDir();
1973
- raiseLeaseFor(COLLECTOR_EXIT_WAIT_MS + LAUNCH_PROBE_TIMEOUT_MS, false);
1958
+ raiseLeaseFor(COLLECTOR_EXIT_WAIT_MS + bounds.launchMs, false);
1974
1959
  const payloadUrl = scheme && metroPort !== null && lanAddress ? devClientUrl(scheme, metroPort, lanAddress) : null;
1960
+ enterPhase("launch");
1975
1961
  const launchTimer = stepTimer(d.now);
1976
1962
  launchedAt = d.now();
1977
1963
  logWriter().write({
@@ -2012,10 +1998,11 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2012
1998
  bundleId,
2013
1999
  appName: appName ?? bundleId,
2014
2000
  collectorPid: collector.pid,
2001
+ wireless: overWifi,
2015
2002
  readRecords: () => readCollectorRecords(logsDir).filter((entry) => Number(entry.ts) >= launchedAt && (entry.slot ?? "default") === (slot ?? "default"))
2016
2003
  });
2017
2004
  if (started.failed || !started.pid) return fail({
2018
- code: "STIM_LAUNCH_FAILED",
2005
+ code: started.code ?? "STIM_LAUNCH_FAILED",
2019
2006
  message: started.reason ?? `${bundleId} did not start on ${udid}.`,
2020
2007
  remedy: started.remedy ?? null,
2021
2008
  lines: started.lines ?? [],
@@ -2067,7 +2054,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2067
2054
  if (installed?.failed) return fail({
2068
2055
  code: installed.code || "STIM_INSTALL_FAILED",
2069
2056
  message: installed.reason,
2070
- remedy: "Check that the simulator is booted and that the app was built for the simulator SDK.",
2057
+ remedy: installFailureRemedy(remoteDevice),
2071
2058
  build: {
2072
2059
  ...buildFailure,
2073
2060
  appPath,
@@ -2088,6 +2075,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2088
2075
  appExecutable,
2089
2076
  note
2090
2077
  });
2078
+ enterPhase("launch");
2091
2079
  const launchTimer = stepTimer(d.now);
2092
2080
  launchedAt = d.now();
2093
2081
  logWriter().write({
@@ -2127,7 +2115,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2127
2115
  return fail({
2128
2116
  code: launched.code || "STIM_LAUNCH_FAILED",
2129
2117
  message: launched.reason,
2130
- remedy: simulatorLaunchFailureRemedy(Boolean(remoteDevice), udid, bundleId, logFile),
2118
+ remedy: launchFailureRemedy(remoteDevice, udid, bundleId, logFile),
2131
2119
  build: {
2132
2120
  ...buildFailure,
2133
2121
  appPath,
@@ -2240,7 +2228,9 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2240
2228
  closeWriter,
2241
2229
  webPreviewUrl: remoteDevice?.webPreviewUrl() ?? null,
2242
2230
  lease: physical ? leaseFacts : void 0,
2243
- recordRun
2231
+ recordRun,
2232
+ reclaimed,
2233
+ devServer
2244
2234
  }),
2245
2235
  uploadsAbandoned
2246
2236
  };
@@ -2257,18 +2247,33 @@ function iosCommand(program) {
2257
2247
  registerIos(program);
2258
2248
  }
2259
2249
  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) => {
2250
+ 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. A Debug run starts the dev server when it is not running.").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?\" check: do not start it, 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
2251
  if (REMOTE_DEVICE_BACKENDS.includes(value)) return value;
2262
2252
  throw new InvalidArgumentError(`expected one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}`);
2263
2253
  }).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
2254
  const root = (deps.findProjectRoot ?? DEFAULT_DEPS.findProjectRoot)(process.cwd());
2265
- const run = () => runIos({
2255
+ const run = (progress) => runIos({
2266
2256
  ...opts,
2267
2257
  waitConflict: waitFlagConflict(process.argv)
2268
- }, deps);
2269
- const completion = root ? await withWorkspaceProcessLock(workspaceDir(root), "native-run", run, {
2258
+ }, deps, progress);
2259
+ const completion = root ? await withWorkspaceProcessLock(workspaceDir(root), "native-run", async (claim) => {
2260
+ recordWorkspaceUse(root);
2261
+ const progress = startBuildProgress({
2262
+ root,
2263
+ platform: "ios",
2264
+ slot: opts.slot ?? "default",
2265
+ claim,
2266
+ note: (line) => writeNote(chalk.dim(line))
2267
+ });
2268
+ try {
2269
+ return await run(progress);
2270
+ } finally {
2271
+ progress.clear();
2272
+ }
2273
+ }, {
2270
2274
  external: true,
2271
- waitMs: 18e5
2275
+ waitMs: 18e5,
2276
+ declareSpawns: true
2272
2277
  }) : await run();
2273
2278
  if (!completion) process.exit(1);
2274
2279
  else if (completion.uploadsAbandoned) exitAfterFlush(0);
@@ -2281,7 +2286,7 @@ function explicitSchemeRefusal(root, scheme, isExpo, d) {
2281
2286
  message: "--scheme must name a non-empty shared Xcode app scheme.",
2282
2287
  remedy: "Pass the exact scheme name shown by xcodebuild -list."
2283
2288
  };
2284
- if (d.needsPrebuild(root, "ios", isExpo)) return null;
2289
+ if (isExpo) return null;
2285
2290
  const project = d.discoverXcodeProject(root);
2286
2291
  if (project.error) return project.error;
2287
2292
  return d.resolveScheme(project, { scheme }).error ?? null;
@@ -2332,7 +2337,7 @@ function iosSlotDeps(d, slot) {
2332
2337
  }
2333
2338
  return d;
2334
2339
  }
2335
- async function runIos(opts = {}, overrides = {}) {
2340
+ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2336
2341
  const slot = validateDeviceSlot(opts.slot);
2337
2342
  let d = iosSlotDeps({
2338
2343
  ...DEFAULT_DEPS,
@@ -2346,16 +2351,7 @@ async function runIos(opts = {}, overrides = {}) {
2346
2351
  const started = d.now();
2347
2352
  const startedAt = new Date(started).toISOString();
2348
2353
  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;
2354
+ const refuseProject = ({ message, remedy }) => {
2359
2355
  note(chalk.red(phaseLine("error", message)));
2360
2356
  note(chalk.dim(phaseLine("remedy", remedy)));
2361
2357
  note(chalk.red(phaseLine("failed", "STIM_NO_PROJECT")));
@@ -2366,7 +2362,12 @@ async function runIos(opts = {}, overrides = {}) {
2366
2362
  }));
2367
2363
  process.exitCode = 1;
2368
2364
  return null;
2369
- }
2365
+ };
2366
+ const foundRoot = d.findProjectRoot(process.cwd());
2367
+ if (!foundRoot) return refuseProject(NO_PROJECT_REFUSAL);
2368
+ const root = foundRoot;
2369
+ const projectProblem = appProjectProblem(root);
2370
+ if (projectProblem) return refuseProject(projectProblem);
2370
2371
  try {
2371
2372
  await d.ensureWorkspaceStorage(root, { note });
2372
2373
  } catch (error) {
@@ -2404,10 +2405,12 @@ async function runIos(opts = {}, overrides = {}) {
2404
2405
  platform: "ios",
2405
2406
  write: (statsRun, at) => d.recordStats(statsRun, at),
2406
2407
  now: () => d.now(),
2407
- note: (line) => note(chalk.dim(line))
2408
+ note: (line) => note(chalk.dim(line)),
2409
+ phases: () => progress.durations()
2408
2410
  });
2409
2411
  const recordRun = stats.record;
2410
2412
  let compilationCache = COMPILATION_CACHE_NOT_RUN;
2413
+ let reclaimed = [];
2411
2414
  const fail = ({ code, message, remedy = null, lines = [], logPath = null, build = null, lease }) => {
2412
2415
  releaseLease();
2413
2416
  if (message) note(chalk.red(phaseLine("error", message)));
@@ -2431,7 +2434,8 @@ async function runIos(opts = {}, overrides = {}) {
2431
2434
  message: message ?? null,
2432
2435
  remedy: remedy ?? null,
2433
2436
  ...compilationCache.status === "not-run" ? {} : { compilationCache },
2434
- ...lease === void 0 ? {} : { lease }
2437
+ ...lease === void 0 ? {} : { lease },
2438
+ ...reclaimed.length ? { reclaimed } : {}
2435
2439
  }));
2436
2440
  writer?.close?.();
2437
2441
  process.exitCode = 1;
@@ -2477,12 +2481,6 @@ async function runIos(opts = {}, overrides = {}) {
2477
2481
  for (const key of unknownSettingKeys(settings)) note(phaseLine("setting", chalk.yellow(`Warning: setting "${key}" is not read by Stim and will be ignored.`)));
2478
2482
  const cacheProviderError = cacheProviderSettingError(settings);
2479
2483
  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
2484
  const poolError = parkedMaxSetting("ios").error;
2487
2485
  if (poolError) return fail({
2488
2486
  code: "STIM_BAD_ARG",
@@ -2514,7 +2512,7 @@ async function runIos(opts = {}, overrides = {}) {
2514
2512
  });
2515
2513
  if (physical && opts.remote) return fail({
2516
2514
  code: "STIM_BAD_ARG",
2517
- message: "--device builds for a phone cabled to this machine, and --remote installs on a remote one.",
2515
+ message: "--device builds for a phone connected to this machine, and --remote installs on a remote one.",
2518
2516
  remedy: "Pass only one of --device and --remote."
2519
2517
  });
2520
2518
  const noWait = opts.wait === false;
@@ -2554,29 +2552,16 @@ async function runIos(opts = {}, overrides = {}) {
2554
2552
  listRuntimes: d.listIosRuntimes
2555
2553
  });
2556
2554
  if (modelRefusal) return fail(modelRefusal);
2557
- const easBuild = await d.resolveEasDevelopmentBuild({
2555
+ const budget = await d.budgetGate({
2558
2556
  root,
2559
- platform: "ios",
2560
- profile: opts.easProfile,
2561
- note,
2562
- isExpo,
2563
- physical,
2564
- selectors: [opts.scheme, opts.configuration],
2565
- buildCache: opts.buildCache
2566
- });
2567
- if (isEasBuildFailure(easBuild)) return fail(easBuild);
2568
- const registerProject = () => d.upsertProject(root, {
2569
- bundleId: d.detectBundleId(root) ?? void 0,
2570
- isExpo
2557
+ note
2571
2558
  });
2572
- if (remoteBackend !== "eas") registerProject();
2573
- const proj = d.getProject(root);
2574
- const label = d.projectShortcut(root, proj);
2559
+ reclaimed = budget.reclaimed;
2560
+ if (budget.refusal) return fail(budget.refusal);
2575
2561
  let remoteDevice = null;
2576
2562
  if (remoteBackend) {
2577
2563
  const resolved = await d.resolveRemoteContext({
2578
2564
  root,
2579
- label,
2580
2565
  backend: remoteBackend,
2581
2566
  easBin: d.resolveEasCliBin(root)?.file ?? null
2582
2567
  });
@@ -2595,8 +2580,26 @@ async function runIos(opts = {}, overrides = {}) {
2595
2580
  launchIosApp: remoteDevice.launchIosApp
2596
2581
  };
2597
2582
  }
2583
+ const easBuild = await d.resolveEasDevelopmentBuild({
2584
+ root,
2585
+ platform: "ios",
2586
+ profile: opts.easProfile,
2587
+ note,
2588
+ isExpo,
2589
+ physical,
2590
+ selectors: [opts.scheme, opts.configuration],
2591
+ buildCache: opts.buildCache
2592
+ });
2593
+ if (isEasBuildFailure(easBuild)) return fail(easBuild);
2594
+ const registerProject = () => d.upsertProject(root, {
2595
+ bundleId: d.detectBundleId(root) ?? void 0,
2596
+ isExpo
2597
+ });
2598
+ if (remoteBackend !== "eas") registerProject();
2599
+ const proj = d.getProject(root);
2598
2600
  const limits = d.getConcurrencyLimits();
2599
2601
  let physicalDevice = null;
2602
+ let wireless = false;
2600
2603
  if (physical && typeof deviceFlag !== "string") {
2601
2604
  const pooled = await d.selectFromPool({
2602
2605
  root,
@@ -2604,7 +2607,8 @@ async function runIos(opts = {}, overrides = {}) {
2604
2607
  idLabel: "udid",
2605
2608
  list: () => iosPoolCandidates(d.listIosDevices()).map((entry) => ({
2606
2609
  id: entry.udid,
2607
- name: entry.name
2610
+ name: entry.name,
2611
+ fallback: isWirelessIosDevice(entry)
2608
2612
  })),
2609
2613
  noCandidates: () => {
2610
2614
  const resolved = iosPoolNoCandidatesRefusal(d.listIosDevices());
@@ -2628,6 +2632,7 @@ async function runIos(opts = {}, overrides = {}) {
2628
2632
  udid: pooled.candidate.id,
2629
2633
  name: pooled.candidate.name ?? pooled.candidate.id
2630
2634
  };
2635
+ wireless = pooled.candidate.fallback === true;
2631
2636
  } else if (physical) {
2632
2637
  const resolved = resolveIosPhysicalDevice(typeof deviceFlag === "string" ? deviceFlag : null, d.listIosDevices());
2633
2638
  if (!resolved.udid) return fail({
@@ -2639,6 +2644,7 @@ async function runIos(opts = {}, overrides = {}) {
2639
2644
  udid: resolved.udid,
2640
2645
  name: resolved.name ?? resolved.udid
2641
2646
  };
2647
+ wireless = resolved.wireless === true;
2642
2648
  }
2643
2649
  if (!physical) {
2644
2650
  const capacity = d.checkDeviceCapacity({
@@ -2651,6 +2657,7 @@ async function runIos(opts = {}, overrides = {}) {
2651
2657
  let metroPort = proj?.metroPort ?? null;
2652
2658
  let lanAddress = null;
2653
2659
  let lanOriginUrl = null;
2660
+ let devServer = null;
2654
2661
  if (!await resolveMetroPort()) return null;
2655
2662
  let device;
2656
2663
  if (physicalDevice) device = {
@@ -2693,34 +2700,28 @@ async function runIos(opts = {}, overrides = {}) {
2693
2700
  metroPort = null;
2694
2701
  phase("metro", `skipped (${configuration}: the JS bundle is embedded, no dev server is used)`);
2695
2702
  } else if (metroCheck) {
2696
- if (!metroPort) {
2697
- fail({
2698
- code: "STIM_NO_METRO",
2699
- message: "No Metro port is reserved for this workspace, so there is no dev server to build against.",
2700
- remedy: "Run `stim start` first, or pass --no-metro-check."
2701
- });
2702
- return false;
2703
- }
2704
- const resolution = await d.resolveMetroWithRetry(d.resolveProjectMetro, metroPort, root, { onRetry: ({ delayMs }) => note(chalk.dim(phaseLine("metro", `port ${metroPort} did not verify yet; retrying in ${Math.round(delayMs / 1e3)}s (Metro may still be indexing)`))) });
2705
- if (!resolution?.metro) {
2706
- const supervisor = d.readWorkspaceState(root)?.supervisor ?? null;
2707
- const supervisorAlive = Boolean(supervisor?.pid && d.pidExists(supervisor.pid));
2703
+ const gate = await ensureDevServer({
2704
+ root,
2705
+ port: metroPort,
2706
+ settings,
2707
+ remote: Boolean(remoteDevice),
2708
+ note,
2709
+ resolve: d.resolveProjectMetro,
2710
+ start: d.startDevServer,
2711
+ readState: d.readWorkspaceState
2712
+ });
2713
+ reclaimed = [...reclaimed, ...gate.reclaimed];
2714
+ if (!gate.ok) {
2708
2715
  fail({
2709
- code: "STIM_NO_METRO",
2710
- message: noMetroMessage({
2711
- port: metroPort,
2712
- resolution,
2713
- supervisor,
2714
- supervisorAlive
2715
- }),
2716
- remedy: noMetroRemedy({
2717
- port: metroPort,
2718
- supervisor,
2719
- supervisorAlive
2720
- })
2716
+ code: gate.code,
2717
+ message: gate.message,
2718
+ remedy: gate.remedy,
2719
+ lines: gate.lines
2721
2720
  });
2722
2721
  return false;
2723
2722
  }
2723
+ metroPort = gate.port;
2724
+ devServer = gate.devServer;
2724
2725
  } else if (!metroPort) {
2725
2726
  metroPort = DEFAULT_METRO_PORT;
2726
2727
  note(chalk.yellow(`No Metro port is reserved for this workspace; wiring the app to ${metroPort}.`));
@@ -2814,6 +2815,7 @@ async function runIos(opts = {}, overrides = {}) {
2814
2815
  boot,
2815
2816
  createdSessionId: remoteDevice.createdSessionId,
2816
2817
  abandonCreatedSession: remoteDevice.abandonCreatedSession,
2818
+ webPreviewUrl: remoteDevice.webPreviewUrl,
2817
2819
  writeState: d.writeWorkspaceState,
2818
2820
  register: registerProject
2819
2821
  }) : boot()).then((result) => {
@@ -2850,7 +2852,8 @@ async function runIos(opts = {}, overrides = {}) {
2850
2852
  note,
2851
2853
  logWriter,
2852
2854
  estimates,
2853
- stats
2855
+ stats,
2856
+ step: progress.step
2854
2857
  }
2855
2858
  }, d);
2856
2859
  if (!acquiredArtifact.ok) {
@@ -2868,7 +2871,7 @@ async function runIos(opts = {}, overrides = {}) {
2868
2871
  idLabel: "udid",
2869
2872
  waitSeconds,
2870
2873
  noWait,
2871
- installBoundMs: DEVICECTL_INSTALL_TIMEOUT_MS,
2874
+ installBoundMs: iosDeviceBounds(wireless).installMs,
2872
2875
  appId: artifact.bundleId ?? proj?.bundleId ?? null,
2873
2876
  holderAppId: (holder) => d.getProject(holder)?.bundleId ?? null,
2874
2877
  now: d.now,
@@ -2908,6 +2911,7 @@ async function runIos(opts = {}, overrides = {}) {
2908
2911
  device,
2909
2912
  udid,
2910
2913
  physical,
2914
+ wireless,
2911
2915
  lanAddress,
2912
2916
  lanOriginUrl,
2913
2917
  remoteDevice,
@@ -2923,7 +2927,10 @@ async function runIos(opts = {}, overrides = {}) {
2923
2927
  closeWriter: () => writer?.close?.(),
2924
2928
  lease: leaseHandle,
2925
2929
  releaseLease,
2926
- recordRun
2930
+ recordRun,
2931
+ reclaimed,
2932
+ devServer,
2933
+ enterPhase: progress.step
2927
2934
  });
2928
2935
  } finally {
2929
2936
  releaseLease();
@@ -2939,4 +2946,4 @@ async function runIos(opts = {}, overrides = {}) {
2939
2946
  }
2940
2947
  }
2941
2948
  //#endregion
2942
- export { appNameFromPath, buildLogFile, cacheDescription, collectorEntry, iosCommand as default, devClientScheme, deviceLabel, ensureWorkspaceStorageSafely, formatDuration, gateShouldRetry, iosConfigurationSetting, iosFacts, isReleaseConfiguration, lastBuildRecord, noMetroMessage, phaseLine, pickDevClientScheme, podAction, registerIos, replaceCollector, resolveConfiguration, resolveDeviceType, resolveMetroWithRetry, resolveRuntime, schemesFromInfoPlist, shortHash, shortUdid, writeLastBuild };
2949
+ export { appNameFromPath, buildLogFile, cacheDescription, collectorEntry, iosCommand as default, devClientScheme, deviceLabel, ensureWorkspaceStorageSafely, formatDuration, iosConfigurationSetting, iosFacts, isReleaseConfiguration, lastBuildRecord, phaseLine, pickDevClientScheme, podAction, registerIos, replaceCollector, resolveConfiguration, resolveDeviceType, resolveRuntime, schemesFromInfoPlist, shortHash, shortUdid, writeLastBuild };