stim 1.13.0 → 1.15.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 (133) hide show
  1. package/README.md +2 -0
  2. package/dist/{activity-DfVKZTEB.mjs → activity-B2-kb7k0.mjs} +281 -41
  3. package/dist/{android-BHfAu2mf.mjs → android-CYmq0jNi.mjs} +2 -2
  4. package/dist/{android-CD8pr8Q2.mjs → android-Cu_L4RpL.mjs} +524 -412
  5. package/dist/{android-qx_31wJo.mjs → android-Db2eEMaf.mjs} +152 -65
  6. package/dist/android-cas-PnKfAdB2.mjs +167 -0
  7. package/dist/android-cas-compiler.mjs +2 -2
  8. package/dist/{app-install-CDFY2nUL.mjs → app-install-CAAplXAt.mjs} +162 -173
  9. package/dist/{budget-E5o91NR1.mjs → budget-CMiqghig.mjs} +1048 -77
  10. package/dist/{build-plan-BTa93o_r.mjs → build-plan-aeGMFX7e.mjs} +193 -650
  11. package/dist/build-progress-C2Jv1AtA.mjs +872 -0
  12. package/dist/build-slots-Df1ApSuH.mjs +127 -0
  13. package/dist/{cache-manifest-DWPF4RKJ.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
  14. package/dist/cache-manifest.mjs +1 -1
  15. package/dist/{chrome-B5hWgQJS.mjs → chrome-BrcVWQyD.mjs} +4 -4
  16. package/dist/cli-0cX5s4NM.mjs +45 -0
  17. package/dist/cli.mjs +6 -42
  18. package/dist/client-C2IS36Qf.mjs +1333 -0
  19. package/dist/collector-run.mjs +8 -7
  20. package/dist/{command-output-BIL7rlX7.mjs → command-output-CliHHn7a.mjs} +1 -1
  21. package/dist/{config-0jCcMM0E.mjs → config-D0DbpZtM.mjs} +29 -29
  22. package/dist/{created-devices-Cd558h31.mjs → created-devices-BwVb3liH.mjs} +2 -2
  23. package/dist/{dependency-state-DNN0yfx6.mjs → dependency-state-BmsjKZ2H.mjs} +2 -2
  24. package/dist/{deps-2Wo81np9.mjs → deps-BFlKbOIp.mjs} +3 -3
  25. package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
  26. package/dist/dev-client-CjzLoNs0.mjs +80 -0
  27. package/dist/{device-Cf7vl-so.mjs → device-BZ1IyS5A.mjs} +7 -7
  28. package/dist/device-capacity-DMuKC7nx.mjs +136 -0
  29. package/dist/device-host-worker.d.mts +1 -0
  30. package/dist/device-host-worker.mjs +769 -0
  31. package/dist/device-ios-DMQBUPus.mjs +381 -0
  32. package/dist/{device-lease-CL9dZf73.mjs → device-lease-W5Pi1q38.mjs} +43 -36
  33. package/dist/{device-pool-MuoiyRcu.mjs → device-pool-B2r38YjS.mjs} +4 -4
  34. package/dist/{device-remote-DT9ngZiD.mjs → device-remote-9ulPsdhY.mjs} +68 -162
  35. package/dist/{doctor-D2iysNNH.mjs → doctor-BcE5xsBn.mjs} +191 -99
  36. package/dist/{error-diagnostics-BoiOhck8.mjs → error-diagnostics-Cb0P1WLS.mjs} +12 -11
  37. package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
  38. package/dist/{gc-eA-PRBX8.mjs → gc-BuL1Genr.mjs} +430 -102
  39. package/dist/{android-cas-B9Kv9aIy.mjs → gradle-CmdOBq1d.mjs} +8 -294
  40. package/dist/{guide-BfZ0OmiP.mjs → guide-cCSGRFd3.mjs} +2052 -240
  41. package/dist/{guide-status-BaMYu55M.mjs → guide-status-CIoWK6mG.mjs} +3 -3
  42. package/dist/hosted-macos-DwowNU7W.mjs +447 -0
  43. package/dist/idle-shutdown-B5LPaNyY.mjs +122 -0
  44. package/dist/in-use-CndPMilN.mjs +110 -0
  45. package/dist/{ios-CmEzZiOj.mjs → ios-3CtCRhwr.mjs} +201 -10
  46. package/dist/{ios-CcfCeZ19.mjs → ios-Chob8ny7.mjs} +560 -259
  47. package/dist/{ios-device-BW-t_Lkv.mjs → ios-device-DERmMmk-.mjs} +12 -7
  48. package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
  49. package/dist/{launch-verify-CuJaBpEa.mjs → launch-verify-CLlofxRt.mjs} +42 -11
  50. package/dist/{logs-DcSRbC2t.mjs → logs-h9h6MUvE.mjs} +95 -13
  51. package/dist/machine-usage-D_YYMzfR.mjs +283 -0
  52. package/dist/machines-CYtOCqcj.mjs +259 -0
  53. package/dist/macos-BwtbUtTN.mjs +468 -0
  54. package/dist/macos-run.d.mts +5 -0
  55. package/dist/macos-run.mjs +2 -0
  56. package/dist/{merge-state-ytFQCeh4.mjs → merge-state-BnxLNQiM.mjs} +22 -12
  57. package/dist/{metro-BPeY1YMh.mjs → metro-Di8EN2Tb.mjs} +20 -120
  58. package/dist/{named-ports-n7tnJsI8.mjs → named-ports-Dj_XBNaL.mjs} +8 -64
  59. package/dist/native-run-DFDoIRbY.mjs +147 -0
  60. package/dist/{native-runtime-CiiFHt0e.mjs → native-runtime-i6OkVg-w.mjs} +11 -6
  61. package/dist/offload-worker.d.mts +121 -0
  62. package/dist/offload-worker.mjs +601 -0
  63. package/dist/{ownership-Bz2kETAS.mjs → ownership-BkYxUtuj.mjs} +3 -2
  64. package/dist/{page-BTu7I60K.mjs → page-QF_Ke10s.mjs} +7 -2
  65. package/dist/{ports-C5UutqpJ.mjs → ports-DddwttKM.mjs} +3 -3
  66. package/dist/prebuild-BvxDlD6C.mjs +213 -0
  67. package/dist/process-identity-BmRm1qV0.mjs +12 -0
  68. package/dist/{project-DGeclhvO.mjs → project-CV3tDFHl.mjs} +9 -18
  69. package/dist/pull-request-pxnDCQzn.mjs +322 -0
  70. package/dist/pull-requests.d.mts +120 -0
  71. package/dist/pull-requests.mjs +2 -0
  72. package/dist/recordings-GEXp4iBT.mjs +37 -0
  73. package/dist/{reload-C-cV5yy_.mjs → reload-ChPiwvot.mjs} +70 -22
  74. package/dist/{remote-cache--aehr4Pr.mjs → remote-cache-CO2elm_b.mjs} +4 -4
  75. package/dist/run-B-xqRrsg.mjs +119 -0
  76. package/dist/{server-bare-Bq8BpSIo.mjs → server-bare-X7PUgizL.mjs} +60 -13
  77. package/dist/{server-expo-DUCg52-K.mjs → server-expo-DhtX6bom.mjs} +6 -10
  78. package/dist/server-expo-KJeD109I.mjs +2 -0
  79. package/dist/{settings-xB0Frkg6.mjs → settings-CduVcoxj.mjs} +77 -26
  80. package/dist/{settings-ssVBToDR.mjs → settings-DbL6uhH8.mjs} +16 -10
  81. package/dist/settings.schema.json +357 -2
  82. package/dist/{simslim-Bbg9UWrt.mjs → simslim-Ciluxlq4.mjs} +8 -132
  83. package/dist/slot-launch-BY0BrOLh.mjs +65 -0
  84. package/dist/{spawn-claims-PT8aQpCa.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
  85. package/dist/spawn-entry-DncpJvWL.mjs +16 -0
  86. package/dist/stage-CzaiwFsI.mjs +143 -0
  87. package/dist/start-CPO4wRg7.mjs +2 -0
  88. package/dist/{start-BZhItkiL.mjs → start-CUD9nasA.mjs} +108 -61
  89. package/dist/state-B4PQFXMW.d.mts +12 -0
  90. package/dist/state-BxCgeTQu.mjs +41 -0
  91. package/dist/{state-DwAU2_-D.mjs → state-CaeaHbDA.mjs} +7 -4
  92. package/dist/{state-l1pYwOre.mjs → state-DFm77wRn.mjs} +67 -5
  93. package/dist/stats-DBLLFmKd.mjs +66 -0
  94. package/dist/{status-TJlL0HWo.mjs → status-DYXxNOT0.mjs} +425 -260
  95. package/dist/{status-sszP4XKU.mjs → status-l84Gbvp2.mjs} +122 -20
  96. package/dist/stdout-reader-C8yjre1x.mjs +23 -0
  97. package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
  98. package/dist/stim-footprint.swift +32 -0
  99. package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
  100. package/dist/{stop-BZ2qMiBP.mjs → stop-Bad61z_2.mjs} +128 -19
  101. package/dist/stop-Bvf1MflS.mjs +48 -0
  102. package/dist/{stop-DskDKNTU.mjs → stop-CceYh1yG.mjs} +2 -2
  103. package/dist/stop-cause-CZ_Mr-v9.mjs +665 -0
  104. package/dist/supervisor-run.d.mts +44 -12
  105. package/dist/supervisor-run.mjs +276 -33
  106. package/dist/support-CDQbDkL5.mjs +315 -0
  107. package/dist/support-WLOzVKUv.mjs +176 -0
  108. package/dist/teardown-BYuQafiA.mjs +718 -0
  109. package/dist/{xcode-LM6A8cNZ.mjs → toolchain-DmeYjXD9.mjs} +363 -13
  110. package/dist/warm-progress-DVHLN4uC.mjs +463 -0
  111. package/dist/{idle-DxLi5OkN.mjs → watchman-Bx0wceoF.mjs} +55 -8
  112. package/dist/{web-sWXd5iek.mjs → web-k6w8-HnE.mjs} +39 -22
  113. package/dist/web-run.mjs +326 -13
  114. package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
  115. package/dist/workspace-state-CL2V2Ons.mjs +371 -0
  116. package/dist/worktree-BT_uULFx.mjs +2 -0
  117. package/dist/{worktree-DeBQSVtR.mjs → worktree-C0c5Udwa.mjs} +55 -54
  118. package/dist/{worktree-D4Do_XiQ.mjs → worktree-qXKzADSC.mjs} +161 -255
  119. package/package.json +10 -5
  120. package/shim/bundle-response.cjs +55 -10
  121. package/shim/bundle-response.d.cts +1 -0
  122. package/shim/expo-metro-config.cjs +10 -2
  123. package/dist/build-progress-l7d-X-ZP.mjs +0 -247
  124. package/dist/in-use-wYEaKQM9.mjs +0 -249
  125. package/dist/ownership-PUP4Dwai.mjs +0 -1138
  126. package/dist/server-expo-BeYwP_Xy.mjs +0 -2
  127. package/dist/slot-launch-BIWEW-wI.mjs +0 -19
  128. package/dist/start-Rw2GD3nV.mjs +0 -2
  129. package/dist/stats-CaSwmsSZ.mjs +0 -62
  130. package/dist/stats-D8rUTPHd.mjs +0 -368
  131. package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
  132. package/dist/worktree-3cBa-bQt.mjs +0 -2
  133. package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
@@ -1,39 +1,50 @@
1
- import { A as ownership_claim_exports, G as validateDeviceSlot, H as parseDeviceSlotOption, R as deviceSlotFileKey, T as upsertProject, p as getProject, u as getConcurrencyLimits, z as deviceSlotKey } from "./config-0jCcMM0E.mjs";
2
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { E as upsertProject, L as deviceSlotFileKey, R as deviceSlotKey, V as parseDeviceSlotOption, W as validateDeviceSlot, p as getProject, u as getConcurrencyLimits, v as recordIosSchemeApprovals } from "./config-D0DbpZtM.mjs";
3
+ import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
3
4
  import { r as createNdjsonWriter } from "./ndjson-CDY9IkVC.mjs";
4
- import { M as workspaceLogsDir, a as pidExists, b as writeWorkspaceState, c as resolveProjectMetro, g as recordWorkspaceUse, h as readWorkspaceState$1, k as workspaceDir, l as signalProcessTree } from "./metro-BPeY1YMh.mjs";
5
- import { D as makeTemporaryDirectory, O as removeTemporaryEntry, s as gitCommonDir, y as repoRoot } from "./worktree-DeBQSVtR.mjs";
6
- import { a as findProjectRoot, n as appProjectProblem, r as detectIsExpo, t as NO_PROJECT_REFUSAL } from "./project-DGeclhvO.mjs";
7
- import { m as writeWorkspaceLaunch, n as MODE_EXPO, t as MODE_BARE } from "./state-DwAU2_-D.mjs";
8
- 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-BIL7rlX7.mjs";
9
- import { A as providerDownloadPath, D as fingerprintProject, E as fingerprintDiffRecord, L as untrackedMissLine, M as refingerprintAfterMutation, N as resolveBuild, O as inputsChangedDuringBuild, P as storeBuild, R as untrackedNativeFiles, S as configInputsChanged, T as filesystemBuildCapability, _ as waitForBuild, b as buildCacheKey, g as releaseBuildLock, j as providerUploadOutcome, k as prepareProviderDownloadDir, m as acquireBuildLock, v as waitForSharedBuild, x as changedDuringBuildLine } from "./device-lease-CL9dZf73.mjs";
10
- import { ct as parkedMaxSetting, rt as POOL_SETTING_REMEDY } from "./android-qx_31wJo.mjs";
11
- import { A as resolveCacheProviderConfig, B as detectProviders, E as publicUrlSetting, G as resolveOptimizations, H as artifactCachePolicy, I as tunnelModeSetting, L as unknownSettingKeys, P as settingShapeErrors, S as metroWarmupUrlSetting, W as optimizationBuildProfile, _ 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 } from "./settings-xB0Frkg6.mjs";
12
- import { D as parseIosSimulatorApp, f as listIosRuntimes } from "./ios-CmEzZiOj.mjs";
13
- import { c as recordFinishedBuild, l as startBuildProgress, n as NO_BUILD_PROGRESS } from "./build-progress-l7d-X-ZP.mjs";
14
- import { a as acquireBuildSlot, s as releaseBuildSlot } from "./in-use-wYEaKQM9.mjs";
15
- import { n as verifyCollectorOwnership } from "./ownership-Bz2kETAS.mjs";
16
- import { a as easAuthNote, c as loadProjectProvider, f as resolveEasCliBin, i as checkEasAuth, m as uploadRemote, n as UPLOAD_TIMEOUT_MS, o as exitAfterFlush, p as resolveRemote, r as cacheLevel, s as isEasAuthFailureText, t as RESOLVE_TIMEOUT_MS } from "./remote-cache--aehr4Pr.mjs";
17
- import { F as ownedSessionName, S as withNativeBuildRun, a as ensureMetroReachable, l as remoteIosDeps, m as cancelledFailure, n as binOnPath, o as ensureRemoteBootOwned, r as bundleEntryPoint, u as resolveRemoteContext, x as runCancellation, z as gateMetroOrigin } from "./device-remote-DT9ngZiD.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-CDFY2nUL.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-CuJaBpEa.mjs";
20
- import { f as easDeviceBuildRemedy, h as resolveEasDevelopmentBuild, l as unknownIosDeviceTypeRefusal, m as planEasDevelopmentBuild, p as isEasBuildFailure, r as checkDeviceCapacity, u as unknownIosRuntimeRefusal } from "./simslim-Bbg9UWrt.mjs";
21
- import { f as spawnEntry, h as detectBundleId, s as startDevServer } from "./start-BZhItkiL.mjs";
22
- 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-MuoiyRcu.mjs";
23
- import { C as warmMetro, S as clearIosAdoptionPending, _ as staleNativeDirRefusal, a as refusePlan, c as skippedMissReason, d as schemesFromInfoPlist, g as runPrebuild, h as recordPrebuild, i as printPlan, l as devClientScheme, m as planPrebuild, n as planFlagRefusal, o as explainBuildMiss, p as swapJsBundle, r as planPayload, s as fingerprintErrorMissReason, t as planCachedBuild, u as pickDevClientScheme, v as ensureBooted, y as ensureOwnedDevice } from "./build-plan-BTa93o_r.mjs";
24
- import { t as budgetGate } from "./budget-E5o91NR1.mjs";
25
- import { a as sleep, i as launchOutcomeRecord, n as ensureWorkspaceStorageSafely, r as isPhysicalDeviceRequest, t as ensureDevServer } from "./native-runtime-CiiFHt0e.mjs";
26
- import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-D8rUTPHd.mjs";
27
- 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-LM6A8cNZ.mjs";
28
- import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-BIWEW-wI.mjs";
29
- import { a as printNativeCrashReport, n as IOS_CRASH_REPORT_RETRY, o as simulatorConsolePaths, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-BoiOhck8.mjs";
30
- 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-BW-t_Lkv.mjs";
31
- import { a as runPodInstall, n as podsAreStale, r as readPodState } from "./deps-2Wo81np9.mjs";
5
+ import { C as workspaceLogsDir, b as workspaceDir, c as writeWorkspaceState, i as recordWorkspaceUse, r as readWorkspaceState$1 } from "./workspace-state-CL2V2Ons.mjs";
6
+ import { D as makeTemporaryDirectory, O as removeTemporaryEntry, s as gitCommonDir, y as repoRoot } from "./worktree-C0c5Udwa.mjs";
7
+ import { a as findProjectRoot$1, n as appProjectProblem, r as detectIsExpo, t as NO_PROJECT_REFUSAL } from "./project-CV3tDFHl.mjs";
8
+ import { m as writeWorkspaceLaunch, n as MODE_EXPO, t as MODE_BARE } from "./state-CaeaHbDA.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-CliHHn7a.mjs";
10
+ import { A as providerDownloadPath, D as fingerprintProject, E as fingerprintDiffRecord, L as untrackedMissLine, M as refingerprintAfterMutation, N as resolveBuild, O as inputsChangedDuringBuild, P as storeBuild, R as untrackedNativeFiles, S as configInputsChanged, T as filesystemBuildCapability, _ as waitForBuild, b as buildCacheKey, g as releaseBuildLock, j as providerUploadOutcome, k as prepareProviderDownloadDir, m as acquireBuildLock, v as waitForSharedBuild, x as changedDuringBuildLine } from "./device-lease-W5Pi1q38.mjs";
11
+ import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-Cnz8CPnl.mjs";
12
+ import { at as POOL_SETTING_REMEDY, dt as parkedMaxSetting } from "./android-Db2eEMaf.mjs";
13
+ import { l as resolveProjectMetro, o as pidExists, u as signalProcessTree } from "./metro-Di8EN2Tb.mjs";
14
+ import { B as unknownSettingKeys, I as settingOriginScope, J as optimizationBuildProfile, K as artifactCachePolicy, L as settingShapeErrors, M as resolveCacheProviderConfig, N as resolveSettings, O as publicUrlSetting, R as settingsLayers, S as metroPortSetting, U as detectProviders, Y as resolveOptimizations, _ as iosSigningIdentitySha1Setting, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, g as iosSigningIdentitySettingError, h as iosSigningIdentitySetting, j as remoteIosSetting, m as iosLanHostSettingError, p as iosLanHostSetting, r as REMOTE_DEVICE_BACKENDS, v as iosSigningIdentitySha1SettingError, w as metroWarmupUrlSetting, z as tunnelModeSetting } from "./settings-CduVcoxj.mjs";
15
+ import { p as listIosRuntimes, r as clearIosAppData } from "./ios-3CtCRhwr.mjs";
16
+ import { c as recordFinishedBuild, d as createRunRecorder, f as readRunEstimates, h as statsProjectKey$1, l as startBuildProgress, m as recordRunStats, n as NO_BUILD_PROGRESS, u as tapBuildLog } from "./build-progress-C2Jv1AtA.mjs";
17
+ import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-Df1ApSuH.mjs";
18
+ import { n as verifyCollectorOwnership } from "./ownership-BkYxUtuj.mjs";
19
+ import { S as restartedAppNote, _ as installIosApp, a as IOS_DEV_MENU_OFF_DEFAULTS_PLIST, d as devClientUrl, l as clearOtherUserApps, r as DEFAULT_METRO_PORT, v as iosAppProcess, x as launchIosApp } from "./app-install-CAAplXAt.mjs";
20
+ import { t as checkDeviceCapacity } from "./device-capacity-DMuKC7nx.mjs";
21
+ import { i as schemesFromInfoPlist, n as devClientTakesDevMenuParams, r as pickDevClientScheme, t as devClientScheme } from "./dev-client-CjzLoNs0.mjs";
22
+ import { S as bundlerPin, _ as readBundleId, c as COMPILATION_CACHE_NOT_RUN, d as buildIos, g as readBundleExecutable, h as discoverXcodeProject, p as compilationCacheActivityLine, r as iosToolchain, v as resolveScheme } from "./toolchain-DmeYjXD9.mjs";
23
+ import { a as offloadBuild, c as placementLoad, d as OffloadRefusal, f as namedBuildMachine, i as closeOffload, l as remotePhaseText, m as resolveBuildPlacement, n as buildPlacementCandidates, p as parseBuildMachineOption, r as chooseBuildMachine, s as offloadPlacement, u as simulatorRuntime } from "./client-C2IS36Qf.mjs";
24
+ import { t as spawnEntry } from "./spawn-entry-DncpJvWL.mjs";
25
+ import { a as runPodInstall, n as podsAreStale, r as readPodState } from "./deps-BFlKbOIp.mjs";
26
+ import { i as staleNativeDirRefusal, n as recordPrebuild, r as runPrebuild, t as planPrebuild } from "./prebuild-BvxDlD6C.mjs";
27
+ import { n as clearIosAdoptionPending } from "./device-ios-DMQBUPus.mjs";
28
+ import { _ as xcodeFailureReport, a as deviceShortName, c as isReleaseConfiguration, d as printDiagnostics, f as resolveConfiguration, g as simulatorBuildArch, h as resolveSimulatorAppFlag, i as deviceModelRefusal, l as ownedSimFailure, m as resolveRuntime, n as buildLogFile, o as iosConfigurationSetting, p as resolveDeviceType, r as deviceLabel, s as iosProviderRunOptions, t as appNameFromPath, u as podAction } from "./support-WLOzVKUv.mjs";
29
+ import { a as easAuthNote, c as loadProjectProvider, f as resolveEasCliBin, i as checkEasAuth, m as uploadRemote, n as UPLOAD_TIMEOUT_MS, o as exitAfterFlush, p as resolveRemote, r as cacheLevel, s as isEasAuthFailureText, t as RESOLVE_TIMEOUT_MS } from "./remote-cache-CO2elm_b.mjs";
30
+ import { O as readRemoteSimulatorArch, a as ensureMetroReachable, k as gateMetroOrigin, l as remoteIosDeps, n as binOnPath, o as ensureRemoteBootOwned, r as bundleEntryPoint, u as resolveRemoteContext, w as ownedSessionName } from "./device-remote-9ulPsdhY.mjs";
31
+ import { c as readMetroRecords, f as verifyLaunch, i as RELEASE_VERIFY_WAIT_MS, l as unattributedLaunchLines, n as LAUNCH_FATAL, o as isBundleProof, p as verifyReleaseLaunch, r as LAUNCH_UNVERIFIED, s as readCollectorRecords, t as LAUNCH_BUNDLING, u as unverifiedLaunchLines } from "./launch-verify-CLlofxRt.mjs";
32
+ import { d as withNativeBuildRun, r as cancelledFailure, u as runCancellation } from "./native-run-DFDoIRbY.mjs";
33
+ import { a as isEasBuildFailure, i as easDeviceBuildRemedy, o as planEasDevelopmentBuild, s as resolveEasDevelopmentBuild } from "./simslim-Ciluxlq4.mjs";
34
+ import { c as startDevServer, h as detectBundleId } from "./start-CUD9nasA.mjs";
35
+ 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-B2r38YjS.mjs";
36
+ import { a as refusePlan, c as skippedMissReason, f as swapJsBundle, h as ensureOwnedDevice, i as printPlan, m as ensureBooted, n as planFlagRefusal, o as explainBuildMiss, p as didSetUpDevice, r as planPayload, s as fingerprintErrorMissReason, t as planCachedBuild, v as warmMetro } from "./build-plan-aeGMFX7e.mjs";
37
+ import { t as budgetGate } from "./budget-CMiqghig.mjs";
38
+ import { a as sleep, i as launchOutcomeRecord, n as ensureWorkspaceStorageSafely, r as isPhysicalDeviceRequest, t as ensureDevServer } from "./native-runtime-i6OkVg-w.mjs";
39
+ import { n as nativeRunCommand, r as siblingPlatformSlots, t as launchSlotScope } from "./slot-launch-BY0BrOLh.mjs";
40
+ import { a as printNativeCrashReport, n as IOS_CRASH_REPORT_RETRY, o as simulatorConsolePaths, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-Cb0P1WLS.mjs";
41
+ import { a as iosPoolCandidates, c as isWirelessIosDevice, f as resolveIosPhysicalDevice, i as iosDeviceProcess, l as listIosDevices, n as installIosDeviceApp, o as iosPoolNoCandidatesRefusal, p as verifyIosDeviceReleaseLaunch, r as iosDeviceBounds, t as awaitIosDeviceLaunch, u as localNetworkPending } from "./ios-device-DERmMmk-.mjs";
42
+ import { X509Certificate } from "node:crypto";
32
43
  import { existsSync, mkdirSync, mkdtempSync, openSync, rmSync, writeFileSync } from "node:fs";
33
- import { InvalidArgumentError } from "commander";
34
- import { buildDiagnostics } from "@stim-cli/core/state";
35
44
  import { basename, join } from "node:path";
36
- import { X509Certificate } from "node:crypto";
45
+ import { hostSimulatorArch } from "@stim-cli/core";
46
+ import { InvalidArgumentError } from "commander";
47
+ import { buildDiagnostics, machineCapacity } from "@stim-cli/core/state";
37
48
  import { networkInterfaces, tmpdir } from "node:os";
38
49
  import chalk from "chalk";
39
50
  import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
@@ -111,133 +122,9 @@ function copyAppAside(appPath, { exec = null, mkdtemp = () => makeTemporaryDirec
111
122
  appPath: copy
112
123
  };
113
124
  }
114
- function buildLogFile(root) {
115
- return join(workspaceLogsDir(root), `build-ios.ndjson`);
116
- }
117
- const MAX_PRINTED_DIAGNOSTICS = 6;
118
- function deviceLabel(device, udid) {
119
- const name = device?.deviceName || device?.name || null;
120
- return name ? `${name} (${shortUdid(udid)})` : shortUdid(udid);
121
- }
122
- function deviceShortName(device, udid) {
123
- return device?.deviceName || device?.name || shortUdid(udid);
124
- }
125
- function appNameFromPath(appPath) {
126
- if (typeof appPath !== "string" || appPath.trim() === "") return null;
127
- const name = basename(appPath).replace(/\.app$/i, "");
128
- return name === "" ? null : name;
129
- }
130
- function iosConfigurationSetting(settings) {
131
- const ios = settings?.["ios"];
132
- if (!ios || typeof ios !== "object" || Array.isArray(ios)) return null;
133
- const raw = ios["configuration"];
134
- return typeof raw === "string" && raw.trim() !== "" ? raw.trim() : null;
135
- }
136
- function resolveConfiguration(flag, settings) {
137
- return (typeof flag === "string" && flag.trim() !== "" ? flag.trim() : null) || iosConfigurationSetting(settings);
138
- }
139
- function iosStringSetting(settings, key) {
140
- const ios = settings?.["ios"];
141
- if (!ios || typeof ios !== "object" || Array.isArray(ios)) return null;
142
- const raw = ios[key];
143
- return typeof raw === "string" && raw.trim() !== "" ? raw.trim() : null;
144
- }
145
- function resolveDeviceType(flag, settings) {
146
- return (typeof flag === "string" && flag.trim() !== "" ? flag.trim() : null) || iosStringSetting(settings, "deviceType");
147
- }
148
- function resolveRuntime(flag, settings) {
149
- return (typeof flag === "string" && flag.trim() !== "" ? flag.trim() : null) || iosStringSetting(settings, "runtime");
150
- }
151
- function resolveSimulatorAppFlag(flag, physical, remoteBackend) {
152
- if (flag === void 0) return {};
153
- let simulatorApp;
154
- try {
155
- simulatorApp = parseIosSimulatorApp(flag, "--simulator-app");
156
- } catch (error) {
157
- return { refusal: {
158
- code: "STIM_BAD_ARG",
159
- message: error.message
160
- } };
161
- }
162
- if (physical || remoteBackend) return { refusal: {
163
- code: "STIM_BAD_ARG",
164
- message: "--simulator-app only applies to a local owned iOS simulator.",
165
- remedy: "Drop --simulator-app when using a physical or remote device."
166
- } };
167
- return { simulatorApp };
168
- }
169
- function deviceModelRefusal({ slot, deviceTypeFlag, runtimeFlag, deviceType, runtime, physical, remoteBackend, listRuntimes }) {
170
- if (slot && slot !== "default" && remoteBackend) return {
171
- code: "STIM_BAD_ARG",
172
- message: "Named slots currently support local simulators and physical devices.",
173
- remedy: "Use the default slot for a remote session."
174
- };
175
- if (typeof deviceTypeFlag === "string" && deviceTypeFlag.trim() === "") return {
176
- code: "STIM_BAD_ARG",
177
- message: "--device-type was given an empty name.",
178
- remedy: "Pass `--device-type <name>` with a model `xcrun simctl list devicetypes` names, e.g. \"iPad Pro 13-inch (M5)\"."
179
- };
180
- if (typeof runtimeFlag === "string" && runtimeFlag.trim() === "") return {
181
- code: "STIM_BAD_ARG",
182
- message: "--runtime was given an empty version.",
183
- remedy: "Pass `--runtime <version>` with a runtime `xcrun simctl list runtimes` reports, e.g. \"18.5\"."
184
- };
185
- if (physical || remoteBackend) return null;
186
- if (!deviceType && !runtime) return null;
187
- let runtimes;
188
- try {
189
- runtimes = listRuntimes();
190
- } catch (error) {
191
- return {
192
- code: "STIM_NO_DEVICE",
193
- message: `Could not read the installed simulator runtimes: ${error?.message || error}`,
194
- remedy: "Run `stim doctor` to check the simulator toolchain, then try again."
195
- };
196
- }
197
- const refusal = unknownIosRuntimeRefusal(runtime, runtimes) ?? unknownIosDeviceTypeRefusal(deviceType, runtimes, runtime);
198
- return refusal ? {
199
- code: "STIM_BAD_ARG",
200
- message: refusal.message,
201
- remedy: refusal.remedy
202
- } : null;
203
- }
204
- function isReleaseConfiguration(configuration) {
205
- return typeof configuration === "string" && configuration.trim() !== "" && configuration.trim().toLowerCase() !== "debug";
206
- }
207
- function podAction(podState, verdict) {
208
- if (verdict?.stale) return {
209
- install: true,
210
- reason: verdict.reason
211
- };
212
- if (verdict?.noPods && podState?.hasPodfile) return {
213
- install: true,
214
- reason: "ios/Podfile exists but no pods are installed"
215
- };
216
- return { install: false };
217
- }
218
- function xcodeFailureReport(result, logPath) {
219
- const diagnostics = result.diagnostics;
220
- const code = result.exitCode;
221
- const how = code === null ? "" : ` (exit code ${code})`;
222
- return {
223
- message: diagnostics.length ? `\`xcodebuild\` failed${how} with ${diagnostics.length} diagnostic${diagnostics.length === 1 ? "" : "s"}.` : `\`xcodebuild\` failed${how} with no recognizable diagnostic.`,
224
- remedy: diagnostics.find((d) => d?.remedy)?.remedy || `See ${logPath} for the transcript.`
225
- };
226
- }
227
- function printDiagnostics(note, result) {
228
- const diagnostics = result.diagnostics;
229
- const shown = diagnostics.slice(0, MAX_PRINTED_DIAGNOSTICS);
230
- for (const diagnostic of shown) note(chalk.red(phaseLine("error", describeDiagnostic(diagnostic))));
231
- const hidden = diagnostics.length - shown.length + result.truncated;
232
- if (hidden > 0) note(chalk.dim(phaseLine("error", `... and ${hidden} more diagnostic${hidden === 1 ? "" : "s"} in the log`)));
233
- if (diagnostics.length === 0) {
234
- note(chalk.red(phaseLine("error", "xcodebuild failed with no recognizable diagnostic; last lines:")));
235
- for (const line of result.tail.slice(-5)) note(chalk.dim(phaseLine("", line)));
236
- }
237
- }
238
125
  //#endregion
239
126
  //#region src/commands/ios/result.ts
240
- function lastBuildRecord({ fingerprint = null, cacheKey = null, cacheHit = false, cacheSkipped = false, durationMs = 0, appPath = null, bundleId = null, startedAt, status, errorCode = null, missReason = null, diagnostics = null, configuration = null }) {
127
+ function lastBuildRecord({ fingerprint = null, cacheKey = null, cacheHit = false, cacheSkipped = false, durationMs = 0, appPath = null, bundleId = null, startedAt, status, errorCode = null, missReason = null, diagnostics = null, configuration = null, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null }) {
241
128
  const record = {
242
129
  platform: "ios",
243
130
  fingerprint,
@@ -252,12 +139,16 @@ function lastBuildRecord({ fingerprint = null, cacheKey = null, cacheHit = false
252
139
  configuration
253
140
  };
254
141
  if (errorCode) record.errorCode = errorCode;
142
+ record.buildMachine = buildMachine;
143
+ if (builtOn) record.builtOn = builtOn;
144
+ if (offloadedTo) record.offloadedTo = offloadedTo;
145
+ if (offloadFallback) record.offloadFallback = offloadFallback;
255
146
  if (missReason && !cacheLevel(cacheHit)) record.missReason = missReason;
256
147
  const recorded = status === "failed" ? buildDiagnostics(diagnostics) : [];
257
148
  if (recorded.length) record.diagnostics = recorded;
258
149
  return record;
259
150
  }
260
- 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 }) {
151
+ 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, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null }) {
261
152
  return {
262
153
  platform: "ios",
263
154
  ...slot && slot !== "default" ? { slot } : {},
@@ -271,6 +162,10 @@ function iosFacts({ slot, udid, deviceName, deviceType = null, runtime = null, f
271
162
  cacheKey,
272
163
  cacheHit: cacheLevel(cacheHit),
273
164
  cacheSkipped: Boolean(cacheSkipped),
165
+ buildMachine,
166
+ ...builtOn ? { builtOn } : {},
167
+ ...offloadedTo ? { offloadedTo } : {},
168
+ ...offloadFallback ? { offloadFallback } : {},
274
169
  compilationCache,
275
170
  waitedForBuild: waitedForBuild ? {
276
171
  pid: waitedForBuild.pid ?? null,
@@ -311,13 +206,14 @@ async function finishIosUpload(uploadPending, remote, phase, note) {
311
206
  }
312
207
  return false;
313
208
  }
314
- function reportIosResult({ slot, root, json, release, configuration, buildScheme = null, metroCheck, metroPort, logsDir, device, udid, appPath, bundleId, installSkipped, elapsed, startedAt, storeHash, storeKey, cacheHit, missReason, compilationCache, useBuildCache, waitedForBuild, launchState, launchWarning, providerName, closeWriter, webPreviewUrl, lease, recordRun, reclaimed = [], devServer = null }) {
209
+ function reportIosResult({ slot, root, json, release, configuration, buildScheme = null, metroCheck, metroPort, logsDir, device, udid, appPath, bundleId, installSkipped, elapsed, startedAt, storeHash, storeKey, cacheHit, missReason, compilationCache, useBuildCache, waitedForBuild, launchState, launchWarning, providerName, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null, closeWriter, webPreviewUrl, lease, recordRun, reclaimed = [], devServer = null, links }) {
315
210
  const durationMs = elapsed();
316
211
  recordRun({
317
212
  failed: false,
318
213
  cacheHit,
319
214
  waited: waitedForBuild,
320
- durationMs
215
+ durationMs,
216
+ offloadedTo
321
217
  });
322
218
  writeLastBuild(root, lastBuildRecord({
323
219
  fingerprint: storeHash,
@@ -330,7 +226,11 @@ function reportIosResult({ slot, root, json, release, configuration, buildScheme
330
226
  bundleId,
331
227
  startedAt,
332
228
  status: "ok",
333
- configuration: configuration ?? "Debug"
229
+ configuration: configuration ?? "Debug",
230
+ buildMachine,
231
+ builtOn,
232
+ offloadedTo,
233
+ offloadFallback
334
234
  }));
335
235
  closeWriter();
336
236
  const facts = iosFacts({
@@ -356,17 +256,22 @@ function reportIosResult({ slot, root, json, release, configuration, buildScheme
356
256
  launched: launchState,
357
257
  webPreviewUrl,
358
258
  lease,
359
- devServer
259
+ devServer,
260
+ buildMachine,
261
+ builtOn,
262
+ offloadedTo,
263
+ offloadFallback
360
264
  });
361
- if (json) console.log(JSON.stringify(reclaimed.length ? {
265
+ if (json) console.log(JSON.stringify({
362
266
  ...facts,
363
- reclaimed
364
- } : facts));
267
+ ...links ? { links } : {},
268
+ ...reclaimed.length ? { reclaimed } : {}
269
+ }));
365
270
  else {
366
- const summary = `${launchWarning ? "WARNING" : "OK"}: ${bundleId} on ${deviceLabel(device, udid)}, ` + (release ? `${configuration} (embedded JS, no Metro)` : `Metro port ${metroPort}`) + ` (${cacheDescription(cacheHit, providerName)}, ${formatDuration(durationMs)})`;
271
+ const summary = `${launchWarning ? "WARNING" : "OK"}: ${bundleId} on ${deviceLabel(device, udid)}, ` + (release ? `${configuration} (embedded JS, no Metro)` : `Metro port ${metroPort}`) + ` (${cacheDescription(cacheHit, providerName, offloadedTo)}, ${formatDuration(durationMs)})`;
367
272
  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);
368
273
  const deviceName = device?.deviceName ?? device?.name ?? udid;
369
- const cacheResult = useBuildCache ? cacheDescription(cacheHit, providerName) : "bypassed; built";
274
+ const cacheResult = useBuildCache ? cacheDescription(cacheHit, providerName, offloadedTo) : `bypassed; ${cacheDescription(false, null, offloadedTo)}`;
370
275
  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})` : ""}`;
371
276
  console.log([
372
277
  outcome,
@@ -378,13 +283,14 @@ function reportIosResult({ slot, root, json, release, configuration, buildScheme
378
283
  phaseLine("logs", logsDir)
379
284
  ].join("\n"));
380
285
  if (facts.webPreviewUrl) console.error(chalk.dim(`Watch this device: ${facts.webPreviewUrl}`));
286
+ if (links) console.error(chalk.dim(workspaceLinkLine(links)));
381
287
  }
382
288
  return facts;
383
289
  }
384
- function cacheDescription(cacheHit, providerName = null) {
290
+ function cacheDescription(cacheHit, providerName = null, offloadedTo = null) {
385
291
  if (cacheHit === "remote") return `from ${providerName || "the remote cache"}`;
386
292
  if (cacheHit === "local") return "from cache";
387
- return "built";
293
+ return offloadedTo ? `built on ${offloadedTo}` : "built";
388
294
  }
389
295
  //#endregion
390
296
  //#region src/commands/ios/artifact.ts
@@ -398,15 +304,17 @@ var ArtifactRefusal = class extends Error {
398
304
  this.failure = failure;
399
305
  }
400
306
  };
401
- async function acquireIosArtifact({ root, logFile, udid, remoteDestination, device, configuration, buildScheme, buildProfile, isExpo, optimizations, cache, easBuild, easProfile, maxBuilds, progress }, d) {
402
- const { phase, note, logWriter, estimates, stats, step } = progress;
307
+ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, simulatorArch, device, configuration, buildScheme, buildProfile, buildMachine = "auto", isExpo, optimizations, cache, easBuild, easProfile, maxBuilds, progress }, d) {
308
+ const { phase, note, logWriter, estimates, stats, step, miss, hit: lateHit, place, waitingOn } = progress;
403
309
  const physical = device !== null;
404
310
  const keyOptions = {
405
311
  scheme: buildScheme,
406
312
  ...configuration ? { configuration } : {},
407
313
  isSimulator: !physical,
314
+ ...simulatorArch ? { arch: simulatorArch } : {},
408
315
  ...buildProfile ? { buildProfile } : {}
409
316
  };
317
+ const providerRunOptions = iosProviderRunOptions(configuration, simulatorArch);
410
318
  const lanAddress = device?.lanAddress ?? null;
411
319
  const metroPort = device?.metroPort ?? null;
412
320
  const release = isReleaseConfiguration(configuration);
@@ -460,6 +368,21 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
460
368
  let appPath = null;
461
369
  let bundleId = null;
462
370
  let cacheHit = false;
371
+ let offloadedTo = null;
372
+ let offloadFallback = null;
373
+ let fallbackMachine = null;
374
+ let hereReason = null;
375
+ let builtOn;
376
+ const fallBack = (reason, line = reason) => {
377
+ if (namedBuildMachine(buildMachine)) throw new OffloadRefusal(buildMachine, reason);
378
+ offloadFallback = reason;
379
+ buildFailure = {
380
+ ...buildFailure,
381
+ offloadFallback: reason
382
+ };
383
+ phase("build", `${line} -> building here`);
384
+ };
385
+ const openOffload = { choice: null };
463
386
  let remote = null;
464
387
  let abandonedRemote = false;
465
388
  let uploadPending = null;
@@ -583,7 +506,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
583
506
  platform: "ios",
584
507
  projectRoot: root,
585
508
  fingerprintHash: fingerprint,
586
- runOptions: configuration ? { configuration } : null
509
+ runOptions: providerRunOptions
587
510
  });
588
511
  if (hit?.appPath) {
589
512
  let stored = null;
@@ -624,6 +547,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
624
547
  step("wait");
625
548
  phase("build", text);
626
549
  },
550
+ waitingOn,
627
551
  warn: (text) => note(chalk.yellow(phaseLine("build", text))),
628
552
  out: note
629
553
  });
@@ -736,32 +660,48 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
736
660
  }
737
661
  }
738
662
  }
739
- function explainMiss(rekeyedBy) {
740
- if (swapFellBack) missReason = skippedMissReason("the cached app could not be reused, so this run built it fresh");
741
- else if (!useBuildCache) missReason = skippedMissReason(cache.disabledByFlag ? "cache reuse turned off by --no-build-cache" : "cache reuse off in config");
742
- else {
743
- const current = {
744
- hash: storeHash ?? fingerprint,
745
- sources: storeSources
746
- };
747
- const explained = explainBuildMiss({
748
- root,
749
- platform: "ios",
750
- current,
751
- rekeyedBy,
752
- baselineDeps: { readState: d.readWorkspaceState }
753
- });
754
- missReason = explained.reason;
755
- if (explained.previousHash && explained.changedNames.length) logWriter().write(fingerprintDiffRecord({
663
+ function reasonForMiss(rekeyedBy) {
664
+ if (swapFellBack) return {
665
+ reason: skippedMissReason("the cached app could not be reused, so this run built it fresh"),
666
+ diff: null
667
+ };
668
+ if (!useBuildCache) return {
669
+ reason: skippedMissReason(cache.disabledByFlag ? "cache reuse turned off by --no-build-cache" : "cache reuse off in config"),
670
+ diff: null
671
+ };
672
+ const current = {
673
+ hash: storeHash ?? fingerprint,
674
+ sources: storeSources
675
+ };
676
+ const explained = explainBuildMiss({
677
+ root,
678
+ platform: "ios",
679
+ current,
680
+ rekeyedBy,
681
+ baselineDeps: { readState: d.readWorkspaceState }
682
+ });
683
+ return {
684
+ reason: explained.reason,
685
+ diff: explained.previousHash && explained.changedNames.length ? fingerprintDiffRecord({
756
686
  changed: explained.changedNames,
757
687
  previousHash: explained.previousHash,
758
688
  hash: current.hash
759
- }));
760
- }
689
+ }) : null
690
+ };
691
+ }
692
+ function reportPendingRecheck(prebuild) {
693
+ const pods = d.readPodState(root);
694
+ if ((prebuild === "generate" || prebuild === "regenerate" || podAction(pods, d.podsAreStale(pods.lockText, pods.manifestText)).install) && useBuildCache) miss(reasonForMiss([]).reason, true);
695
+ }
696
+ function explainMiss(rekeyedBy) {
697
+ const explained = reasonForMiss(rekeyedBy);
698
+ missReason = explained.reason;
699
+ if (explained.diff) logWriter().write(explained.diff);
761
700
  buildFailure = {
762
701
  ...buildFailure,
763
702
  missReason
764
703
  };
704
+ miss(missReason);
765
705
  phase("cache", `miss: ${missReason.summary}`);
766
706
  if (missReason.kind === "no-baseline") {
767
707
  const untracked = untrackedMissLine(d.untrackedNativeFiles({ projectRoot: root }));
@@ -805,31 +745,194 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
805
745
  note(chalk.dim(phaseLine("fingerprint", `${shortHash(beforeBuildHash)} -> ${shortHash(storeHash)} (after xcodebuild)`)));
806
746
  }
807
747
  }
748
+ async function takeBuildSlot() {
749
+ if (!maxBuilds) return;
750
+ try {
751
+ buildSlot = await d.acquireBuildSlot({
752
+ max: maxBuilds,
753
+ root,
754
+ logFile,
755
+ out: note
756
+ });
757
+ } catch (e) {
758
+ const refusal = (0, ownership_claim_exports.claimFailure)(e, "stim ios");
759
+ if (refusal) fail({
760
+ code: refusal.code,
761
+ message: refusal.message,
762
+ remedy: refusal.remedy,
763
+ build: buildFailure
764
+ });
765
+ note(chalk.yellow(phaseLine("build", `could not take a build slot: ${e?.message || e}; building anyway`)));
766
+ }
767
+ }
768
+ /** Whether this build should leave this Mac, before any machine is asked; null builds here. */
769
+ function placeBuild() {
770
+ const { mode, machines } = buildPlacementCandidates(buildMachine);
771
+ if (machines.length === 0 && !namedBuildMachine(buildMachine) && buildMachine !== "local") return null;
772
+ const runtime = physical || remoteDestination || release ? null : simulatorRuntime(udid);
773
+ const unsupported = physical ? "device builds build here" : remoteDestination ? "--remote builds build here" : release ? `${configuration} builds build here` : !cachePolicy.write ? "the build cache is off" : !runtime ? `the runtime of simulator ${udid} is unknown` : null;
774
+ const here = machineCapacity();
775
+ const placement = offloadPlacement({
776
+ mode,
777
+ machines: machines.length,
778
+ here,
779
+ unsupported,
780
+ selected: buildMachine
781
+ });
782
+ if (!placement.offload) {
783
+ hereReason = placement.reason;
784
+ if (mode !== "off") phase("build", `placement: here (${placement.reason})`);
785
+ return null;
786
+ }
787
+ return {
788
+ mode,
789
+ here,
790
+ reason: placement.reason,
791
+ runtime,
792
+ machines
793
+ };
794
+ }
795
+ /** Asks the paired machines once the post-mutation key is known; null builds here. */
796
+ async function chooseMachine(candidate) {
797
+ const choice = await chooseBuildMachine({
798
+ projectRoot: root,
799
+ target: {
800
+ platform: "ios",
801
+ local: iosToolchain(),
802
+ runtime: candidate.runtime,
803
+ cocoapodsPinned: bundlerPin(root) !== null
804
+ },
805
+ mode: candidate.mode,
806
+ here: candidate.here,
807
+ note: (line) => note(chalk.dim(phaseLine("build", `offload: ${line}`))),
808
+ machines: candidate.machines,
809
+ selected: buildMachine
810
+ });
811
+ if (typeof choice === "string") {
812
+ const only = candidate.machines.length === 1 ? candidate.machines[0].machine : null;
813
+ if (only && choice.startsWith(`${only}: `)) fallbackMachine = only;
814
+ fallBack(choice);
815
+ return null;
816
+ }
817
+ openOffload.choice = choice;
818
+ phase("build", `placement: ${choice.machine} (${candidate.reason}${placementLoad(choice)})`);
819
+ return choice;
820
+ }
821
+ /** Builds on the chosen machine and stores the app under the post-mutation key; false builds here instead unless a machine was named. */
822
+ async function compileElsewhere({ choice, candidate }) {
823
+ const runtime = candidate.runtime;
824
+ if (!storeKey || !storeHash) {
825
+ if (namedBuildMachine(buildMachine)) fallBack("the build fingerprint or cache key is unavailable");
826
+ return false;
827
+ }
828
+ const stagingDir = join(workspaceDir(root), "offload", "ios");
829
+ const outcome = await offloadBuild({
830
+ choice,
831
+ expectedFingerprint: storeHash,
832
+ request: {
833
+ platform: "ios",
834
+ runtime,
835
+ configuration,
836
+ scheme: buildScheme ?? null,
837
+ isExpo,
838
+ optimizations
839
+ },
840
+ stagingDir,
841
+ onPhase: (name, msg) => phase(name, remotePhaseText(name, msg, choice.machine)),
842
+ onEnter: (name) => {
843
+ if (name === "compile" || name === "build" || name === "prebuild" || name === "pods") {
844
+ builtOn = choice.machine;
845
+ buildFailure = {
846
+ ...buildFailure,
847
+ builtOn
848
+ };
849
+ }
850
+ place({
851
+ host: choice.machine,
852
+ phase: name
853
+ });
854
+ step(name === "prebuild" || name === "pods" ? name : "compile");
855
+ },
856
+ onRecord: (record) => logWriter().write({
857
+ ...record,
858
+ offloadedTo: choice.machine
859
+ }),
860
+ note: (line) => phase("build", line)
861
+ });
862
+ place(null);
863
+ let stored = null;
864
+ let reason = outcome.ok ? null : outcome.reason;
865
+ if (outcome.ok) {
866
+ const settled = await refingerprintAfterMutation({
867
+ projectRoot: root,
868
+ platform: "ios",
869
+ previousHash: storeHash,
870
+ fingerprint: d.fingerprintProject
871
+ });
872
+ if (!settled || settled.moved) reason = "the checkout here changed while it built";
873
+ else try {
874
+ stored = d.storeBuild("ios", storeKey, outcome.artifactPath, {
875
+ sources: storeSources,
876
+ overwrite: !useBuildCache
877
+ });
878
+ } catch (e) {
879
+ reason = `could not store the app: ${e?.message || e}`;
880
+ }
881
+ }
882
+ try {
883
+ rmSync(stagingDir, {
884
+ recursive: true,
885
+ force: true
886
+ });
887
+ } catch {}
888
+ step("compile");
889
+ const prepared = stored ? await installableCachedApp(stored) : null;
890
+ if (!outcome.ok || !prepared) {
891
+ const why = reason ?? "the stored app is not installable";
892
+ fallbackMachine = choice.machine;
893
+ fallBack(`${choice.machine}: ${why}`, `offload failed: ${why}`);
894
+ logWriter().write({
895
+ src: "build",
896
+ level: "warn",
897
+ event: "offload_failed",
898
+ msg: reason,
899
+ machine: choice.machine
900
+ });
901
+ return false;
902
+ }
903
+ const { timings } = outcome;
904
+ appPath = prepared;
905
+ builtOn = outcome.machine;
906
+ offloadedTo = outcome.machine;
907
+ stats.setPlacement({
908
+ decision: "offloaded",
909
+ machine: outcome.machine,
910
+ reason: `${candidate.reason}${placementLoad(choice)}`,
911
+ buildMs: timings.totalMs
912
+ });
913
+ phase("build", `built on ${outcome.machine} in ${formatDuration(timings.totalMs)}: offer ${formatDuration(timings.offerMs)}, sync ${formatDuration(timings.syncMs)}, build ${formatDuration(timings.workerMs)}, fetch ${formatDuration(timings.fetchMs)}`);
914
+ compilationCache = outcome.compilationCache;
915
+ phase("cache", `compilation cache on ${outcome.machine} ${compilationCacheActivityLine(compilationCache)}`);
916
+ logWriter().write({
917
+ src: "build",
918
+ level: "info",
919
+ event: "offload_done",
920
+ msg: `built on ${outcome.machine}`,
921
+ timings
922
+ });
923
+ return true;
924
+ }
808
925
  async function buildArtifact() {
809
926
  buildFailure = {
810
927
  fingerprint,
811
928
  cacheKey,
812
929
  cacheHit,
813
- cacheSkipped: !useBuildCache
930
+ cacheSkipped: !useBuildCache,
931
+ buildMachine
814
932
  };
815
933
  if (!appPath) {
816
- if (maxBuilds) try {
817
- buildSlot = await d.acquireBuildSlot({
818
- max: maxBuilds,
819
- root,
820
- logFile,
821
- out: note
822
- });
823
- } catch (e) {
824
- const refusal = (0, ownership_claim_exports.claimFailure)(e, "stim ios");
825
- if (refusal) fail({
826
- code: refusal.code,
827
- message: refusal.message,
828
- remedy: refusal.remedy,
829
- build: buildFailure
830
- });
831
- note(chalk.yellow(phaseLine("build", `could not take a build slot: ${e?.message || e}; building anyway`)));
832
- }
934
+ const offload = placeBuild();
935
+ if (!offload) await takeBuildSlot();
833
936
  const mutatingSteps = [];
834
937
  const rekeyedBy = [];
835
938
  const prebuild = d.planPrebuild(root, "ios", {
@@ -841,6 +944,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
841
944
  ...staleNativeDirRefusal("ios"),
842
945
  build: buildFailure
843
946
  });
947
+ reportPendingRecheck(prebuild);
844
948
  if (prebuild === "generate" || prebuild === "regenerate") {
845
949
  step("prebuild");
846
950
  recordPrebuild(root, "ios", null);
@@ -871,7 +975,8 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
871
975
  });
872
976
  }
873
977
  const podState = d.readPodState(root);
874
- const action = podAction(podState, d.podsAreStale(podState.lockText, podState.manifestText));
978
+ const verdict = d.podsAreStale(podState.lockText, podState.manifestText);
979
+ const action = podAction(podState, verdict);
875
980
  if (action.install) {
876
981
  step("pods");
877
982
  const result = await d.runPodInstall(root, logWriter(), { estimateMs: estimates().podsMs });
@@ -927,6 +1032,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
927
1032
  if (prepared) {
928
1033
  appPath = prepared;
929
1034
  cacheHit = "local";
1035
+ lateHit();
930
1036
  phase("cache", `hit ${shortHash(storeHash)} (post-${mutatingSteps.join("/")} key)`);
931
1037
  if (releasedWait) {
932
1038
  waitedForBuild = releasedWait.facts;
@@ -936,15 +1042,40 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
936
1042
  }
937
1043
  }
938
1044
  }
939
- if (!appPath) {
1045
+ if (offload && !appPath) {
940
1046
  explainMiss(rekeyedBy);
941
1047
  step("compile");
1048
+ if (!storeKey) fallBack("no cache key to store the app under", "offload failed: no cache key to store the app under");
1049
+ const choice = storeKey ? await chooseMachine(offload) : null;
1050
+ if (!choice || !await compileElsewhere({
1051
+ choice,
1052
+ candidate: offload
1053
+ })) await takeBuildSlot();
1054
+ }
1055
+ if (!appPath) {
1056
+ if (!offload) explainMiss(rekeyedBy);
1057
+ step("compile");
1058
+ if (offload) stats.setPlacement({
1059
+ decision: "fell-back",
1060
+ reason: offloadFallback ?? "offload failed",
1061
+ ...fallbackMachine ? { machine: fallbackMachine } : {}
1062
+ });
1063
+ else if (hereReason) stats.setPlacement({
1064
+ decision: "here",
1065
+ reason: hereReason
1066
+ });
1067
+ builtOn = "here";
1068
+ buildFailure = {
1069
+ ...buildFailure,
1070
+ builtOn
1071
+ };
942
1072
  phase("build", `compiling ${configuration || "Debug"} with xcodebuild`);
943
1073
  const result = await d.buildIos({
944
1074
  root,
945
1075
  scheme: buildScheme,
946
1076
  udid,
947
1077
  destination: remoteDestination ? GENERIC_SIM_DESTINATION : null,
1078
+ arch: remoteDestination ? simulatorArch : null,
948
1079
  ...physical ? { sdk: IPHONEOS_SDK } : {},
949
1080
  logWriter: logWriter(),
950
1081
  ...configuration ? { configuration } : {},
@@ -1007,7 +1138,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
1007
1138
  projectRoot: root,
1008
1139
  fingerprintHash: storeHash,
1009
1140
  buildPath: appPath,
1010
- runOptions: configuration ? { configuration } : null
1141
+ runOptions: providerRunOptions
1011
1142
  });
1012
1143
  }
1013
1144
  }
@@ -1017,8 +1148,11 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
1017
1148
  await resolveInitialFingerprint();
1018
1149
  if (!easBuild) {
1019
1150
  remote = await resolveRemoteArtifact();
1151
+ if (!appPath) miss(reasonForMiss([]).reason);
1020
1152
  await awaitSharedBuild();
1021
1153
  await prepareCachedArtifact();
1154
+ if (appPath) lateHit();
1155
+ else if (swapFellBack) miss(reasonForMiss([]).reason);
1022
1156
  await buildArtifact();
1023
1157
  }
1024
1158
  const artifact = {
@@ -1031,6 +1165,10 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
1031
1165
  } : null,
1032
1166
  hit: cacheHit,
1033
1167
  providerName: remote?.name ?? providerName,
1168
+ buildMachine,
1169
+ ...builtOn ? { builtOn } : {},
1170
+ offloadedTo,
1171
+ offloadFallback: offloadedTo ? null : offloadFallback,
1034
1172
  readEnabled: useBuildCache,
1035
1173
  missReason: cacheHit ? null : missReason,
1036
1174
  waitedForBuild,
@@ -1038,6 +1176,8 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
1038
1176
  },
1039
1177
  failureFields: {
1040
1178
  ...buildFailure,
1179
+ buildMachine,
1180
+ ...builtOn ? { builtOn } : {},
1041
1181
  fingerprint: storeHash,
1042
1182
  cacheKey: storeKey,
1043
1183
  cacheHit,
@@ -1060,6 +1200,23 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
1060
1200
  artifact
1061
1201
  };
1062
1202
  } catch (error) {
1203
+ if (error instanceof OffloadRefusal) {
1204
+ const refusal = error;
1205
+ return {
1206
+ ok: false,
1207
+ failure: {
1208
+ code: refusal.code,
1209
+ message: refusal.message,
1210
+ remedy: refusal.remedy,
1211
+ build: {
1212
+ ...buildFailure,
1213
+ buildMachine,
1214
+ ...builtOn ? { builtOn } : {}
1215
+ }
1216
+ },
1217
+ compilationCache
1218
+ };
1219
+ }
1063
1220
  if (error instanceof ArtifactRefusal) return {
1064
1221
  ok: false,
1065
1222
  failure: error.failure,
@@ -1067,6 +1224,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
1067
1224
  };
1068
1225
  throw error;
1069
1226
  } finally {
1227
+ if (openOffload.choice) closeOffload(openOffload.choice);
1070
1228
  releaseLock();
1071
1229
  releaseSlot();
1072
1230
  if (!transferred) releaseArtifact();
@@ -1665,15 +1823,19 @@ async function replaceCollector({ root, slot = "default", udid, bundleId, appNam
1665
1823
  //#region src/commands/ios/dependencies.ts
1666
1824
  const DEFAULT_DEPS = {
1667
1825
  resolveEasDevelopmentBuild,
1668
- findProjectRoot,
1826
+ findProjectRoot: findProjectRoot$1,
1669
1827
  resolveSettings,
1828
+ settingsLayers,
1829
+ settingOriginScope,
1670
1830
  gitCommonDir,
1671
1831
  repoRoot,
1672
1832
  detectBundleId,
1673
1833
  detectIsExpo,
1674
1834
  devClientScheme,
1835
+ devClientTakesDevMenuParams,
1675
1836
  getProject,
1676
1837
  upsertProject,
1838
+ recordIosSchemeApprovals,
1677
1839
  checkDeviceCapacity,
1678
1840
  budgetGate,
1679
1841
  ensureOwnedDevice,
@@ -1681,6 +1843,8 @@ const DEFAULT_DEPS = {
1681
1843
  ensureBooted,
1682
1844
  resolveRemoteContext,
1683
1845
  remoteIosDeps,
1846
+ readRemoteSimulatorArch,
1847
+ hostSimulatorArch,
1684
1848
  ensureMetroReachable,
1685
1849
  ensureRemoteBootOwned,
1686
1850
  detectProviders,
@@ -1732,6 +1896,7 @@ const DEFAULT_DEPS = {
1732
1896
  swapJsBundle,
1733
1897
  installIosApp,
1734
1898
  clearOtherUserApps,
1899
+ clearIosAppData,
1735
1900
  clearIosAdoptionPending,
1736
1901
  launchIosApp,
1737
1902
  verifyLaunch,
@@ -1793,9 +1958,12 @@ async function verifyIosRun({ root, slot, appPath, d, release, launched, configu
1793
1958
  note(chalk.yellow(phaseLine("", "Release readiness was not established. Run `stim logs --errors` for captured crash reports, or `stim logs --source device` for the full device output.")));
1794
1959
  return { state: crashes.length || processCheck?.reason === "exited" ? LAUNCH_FATAL : LAUNCH_UNVERIFIED };
1795
1960
  }
1961
+ const siblings = metroCheck ? siblingPlatformSlots(root, "ios", slot) : [];
1796
1962
  const verification = metroCheck ? await d.verifyLaunch({
1797
1963
  requireBundleResponse: true,
1798
1964
  slot: launchSlotScope(root, slot),
1965
+ appPid: physical || remoteDevice ? null : launched?.pid,
1966
+ platformShared: siblings.length > 0,
1799
1967
  onReadinessPending: () => phase("readiness", "waiting for app readiness (up to 30s after bundle load)"),
1800
1968
  logsDir,
1801
1969
  since: launchedAt,
@@ -1860,14 +2028,50 @@ async function verifyIosRun({ root, slot, appPath, d, release, launched, configu
1860
2028
  note(chalk.dim(phaseLine("", "Nothing to do: `stim logs --source metro` shows the build finishing, usually within a minute.")));
1861
2029
  return { state: LAUNCH_BUNDLING };
1862
2030
  }
2031
+ return reportUnverified({
2032
+ verification,
2033
+ siblings,
2034
+ slot,
2035
+ phase,
2036
+ note,
2037
+ metroPort,
2038
+ bundleId,
2039
+ udid,
2040
+ scheme,
2041
+ lanAddress,
2042
+ isExpo,
2043
+ remoteDevice,
2044
+ physical,
2045
+ lanOrigin,
2046
+ metroOrigin,
2047
+ logsDir,
2048
+ launchedAt,
2049
+ launched
2050
+ });
2051
+ }
2052
+ function reportUnverified({ verification, siblings, slot, phase, note, metroPort, bundleId, udid, scheme, lanAddress, isExpo, remoteDevice, physical, lanOrigin, metroOrigin, logsDir, launchedAt, launched }) {
2053
+ if (verification.unattributed) {
2054
+ const [headline, ...lines] = unattributedLaunchLines({
2055
+ platform: "ios",
2056
+ metroPort,
2057
+ slot,
2058
+ siblings
2059
+ });
2060
+ phase("verify", chalk.yellow(headline));
2061
+ for (const line of lines) note(chalk.yellow(phaseLine("", line)));
2062
+ return {
2063
+ state: LAUNCH_UNVERIFIED,
2064
+ unattributed: true
2065
+ };
2066
+ }
1863
2067
  phase("verify", chalk.yellow("UNVERIFIED: no bundle request reached this workspace's Metro"));
1864
2068
  for (const line of unverifiedLaunchLines({
1865
2069
  platform: "ios",
1866
2070
  metroPort: metroPort ?? 8081,
1867
- waitedMs: verification?.waitedMs,
2071
+ waitedMs: verification.waitedMs,
1868
2072
  bundleId,
1869
2073
  udid,
1870
- devClientUrl: scheme ? devClientUrl(scheme, metroPort ?? 8081, lanAddress ?? void 0) : null,
2074
+ devClientUrl: scheme ? launched?.url ?? devClientUrl(scheme, metroPort ?? 8081, lanAddress ?? void 0) : null,
1871
2075
  mode: isExpo ? MODE_EXPO : MODE_BARE,
1872
2076
  remote: remoteDevice,
1873
2077
  physical,
@@ -1894,20 +2098,30 @@ function installsOverWifi(d, udid, selectedWireless, note) {
1894
2098
  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`));
1895
2099
  return wireless;
1896
2100
  }
1897
- function cleanAdoptedIosApps({ d, root, slot, udid, bundleId, phase, note }) {
2101
+ function cleanAdoptedIosApps({ d, udid, bundleId, devClient, phase, note }) {
1898
2102
  const swept = d.clearOtherUserApps({
1899
2103
  udid,
1900
2104
  keep: bundleId
1901
2105
  });
1902
2106
  if (swept.removed.length) phase("install", `removed ${swept.removed.join(", ")}, left by the previous workspace`);
1903
2107
  if (swept.listed && swept.failed.length === 0) {
1904
- d.clearIosAdoptionPending(root, slot);
2108
+ if (swept.kept && bundleId) {
2109
+ try {
2110
+ d.clearIosAppData(udid, bundleId, { defaultsPlist: devClient ? IOS_DEV_MENU_OFF_DEFAULTS_PLIST : null });
2111
+ } catch (error) {
2112
+ return `Could not clear the data ${bundleId} kept from the previous workspace: ${String(error?.message || error)}`;
2113
+ }
2114
+ phase("install", `cleared ${bundleId} data left by the previous workspace`);
2115
+ }
1905
2116
  return null;
1906
2117
  }
1907
2118
  if (!swept.listed) return "Could not list apps left by the previous workspace.";
1908
2119
  for (const failure of swept.failed) note(chalk.yellow(phaseLine("install", `could not remove ${failure}, left by the previous workspace`)));
1909
2120
  return `Could not remove ${swept.failed.join(", ")}, left by the previous workspace.`;
1910
2121
  }
2122
+ function installMayBeProven(adopting, parkedCacheKey, storeKey) {
2123
+ return !adopting || !parkedCacheKey || parkedCacheKey === storeKey;
2124
+ }
1911
2125
  function resolveRunBundleId(d, root, appPath, bundleId) {
1912
2126
  if (!appPath || bundleId) return bundleId;
1913
2127
  return d.readBundleId(appPath) || d.detectBundleId(root);
@@ -1940,7 +2154,7 @@ function launchFailureRemedy(remoteDevice, udid, bundleId, logFile) {
1940
2154
  }
1941
2155
  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 }) {
1942
2156
  const { path: appPath, bundleId: initialBundleId, failureFields: buildFailure, cache } = artifact;
1943
- const { hit: cacheHit, providerName, readEnabled: useBuildCache, missReason, waitedForBuild, compilation: compilationCache } = cache;
2157
+ const { hit: cacheHit, providerName, buildMachine, builtOn, offloadedTo, offloadFallback, readEnabled: useBuildCache, missReason, waitedForBuild, compilation: compilationCache } = cache;
1944
2158
  const storeHash = cache.identity?.fingerprint ?? null;
1945
2159
  const storeKey = cache.identity?.key ?? null;
1946
2160
  const runCommand = nativeRunCommand("ios", slot, {
@@ -1978,7 +2192,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
1978
2192
  }
1979
2193
  });
1980
2194
  if (bundleId) d.upsertProject(root, { bundleId });
1981
- enterPhase("install");
2195
+ enterPhase("device");
1982
2196
  const booted = await bootPromise;
1983
2197
  if (!booted?.ok) return fail({
1984
2198
  code: booted?.code || "STIM_NO_DEVICE",
@@ -1988,11 +2202,12 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
1988
2202
  const deviceOutcome = physical ? "connected" : `${device?.adopted ? "adopted" : "booted"} ${bootDuration()}`;
1989
2203
  phase("device", `${deviceLabel(device, udid)} ${deviceOutcome}`);
1990
2204
  const scheme = release ? void 0 : d.devClientScheme(root, appPath);
2205
+ const devMenuParams = d.devClientTakesDevMenuParams(root);
1991
2206
  const appName = appNameFromPath(appPath);
1992
2207
  const appExecutable = readRunExecutable(d, appPath, note);
1993
2208
  const dropSwapDir = artifact.release;
1994
2209
  let installSkipped = false;
1995
- let launched = null;
2210
+ let launched;
1996
2211
  let launchedAt = d.now();
1997
2212
  if (physical) {
1998
2213
  const overWifi = installsOverWifi(d, udid, wireless, note);
@@ -2003,6 +2218,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2003
2218
  root,
2004
2219
  note
2005
2220
  });
2221
+ enterPhase("install");
2006
2222
  const installTimer = stepTimer(d.now);
2007
2223
  const installed = d.installIosDeviceApp({
2008
2224
  udid,
@@ -2105,9 +2321,9 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2105
2321
  if (adopting) {
2106
2322
  const cleanupFailure = cleanAdoptedIosApps({
2107
2323
  d,
2108
- root,
2109
2324
  udid,
2110
2325
  bundleId,
2326
+ devClient: Boolean(scheme),
2111
2327
  phase,
2112
2328
  note
2113
2329
  });
@@ -2122,13 +2338,15 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2122
2338
  }
2123
2339
  });
2124
2340
  }
2341
+ enterPhase("install");
2125
2342
  const installTimer = stepTimer(d.now);
2126
2343
  const installed = d.installIosApp({
2127
2344
  udid,
2128
2345
  appPath,
2129
2346
  bundleId,
2130
2347
  devClientScheme: scheme,
2131
- proveInstalled: !adopting || device?.parkedCacheKey === storeKey
2348
+ schemeApprovals: device.schemeApprovals,
2349
+ proveInstalled: installMayBeProven(adopting, device?.parkedCacheKey, storeKey)
2132
2350
  }, { now: d.now });
2133
2351
  if (installed?.failed) return fail({
2134
2352
  code: installed.code || "STIM_INSTALL_FAILED",
@@ -2140,10 +2358,12 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2140
2358
  bundleId
2141
2359
  }
2142
2360
  });
2361
+ if (adopting) d.clearIosAdoptionPending(root, slot);
2143
2362
  installSkipped = Boolean(installed?.skipped);
2144
2363
  const artifactDuration = installed?.artifactDurationMs === void 0 ? installTimer() : `(${formatDuration(installed.artifactDurationMs)})`;
2145
2364
  phase("install", installSkipped ? `unchanged (${deviceShortName(device, udid)} already has this build) ${artifactDuration}` : `${basename(appPath)} -> ${deviceLabel(device, udid)} ${artifactDuration}`);
2146
2365
  if (!remoteDevice && installed?.devClientPreparationDurationMs !== void 0) phase("install", `dev client prepared (${formatDuration(installed.devClientPreparationDurationMs)})`);
2366
+ if (installed.schemeApprovals) d.recordIosSchemeApprovals(root, udid, installed.schemeApprovals, slot);
2147
2367
  dropSwapDir();
2148
2368
  if (!remoteDevice) await d.replaceCollector({
2149
2369
  root,
@@ -2175,13 +2395,14 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2175
2395
  bundleId,
2176
2396
  metroPort,
2177
2397
  devClientScheme: scheme,
2398
+ devMenuParams,
2178
2399
  consolePaths: simulatorConsolePaths({
2179
2400
  deviceId: udid,
2180
2401
  appId: bundleId,
2181
2402
  since: launchedAt
2182
2403
  }, true, Boolean(remoteDevice))
2183
2404
  });
2184
- if (launched?.failed) {
2405
+ if (launched.failed) {
2185
2406
  printNativeCrashReport({
2186
2407
  root,
2187
2408
  slot,
@@ -2202,7 +2423,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2202
2423
  }
2203
2424
  });
2204
2425
  }
2205
- phase("launch", `${bundleId} ${launchTimer()}`);
2426
+ phase("launch", `${bundleId}${restartedAppNote(launched.restartedPid)} ${launchTimer()}`);
2206
2427
  }
2207
2428
  recordIosReloadTarget({
2208
2429
  d,
@@ -2221,7 +2442,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2221
2442
  src: "build",
2222
2443
  level: "info",
2223
2444
  event: "launch",
2224
- msg: release ? `launched ${bundleId} on ${udid} (${configuration}, embedded JS bundle, no Metro)` : `launched ${bundleId} on ${udid} against Metro ${lanOriginUrl ?? `port ${metroPort}`}` + (launched?.mode === "openurl" || launched?.mode === "payload-url" ? " (expo-dev-client)" : "")
2445
+ msg: release ? `launched ${bundleId} on ${udid} (${configuration}, embedded JS bundle, no Metro)` : `launched ${bundleId} on ${udid} against Metro ${lanOriginUrl ?? `port ${metroPort}`}` + (launched?.mode === "openurl" || launched?.mode === "payload-url" ? " (expo-dev-client)" : "") + restartedAppNote(launched.restartedPid, "; ")
2225
2446
  });
2226
2447
  if (remoteDevice) logWriter().write({
2227
2448
  src: "build",
@@ -2230,7 +2451,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2230
2451
  msg: `remote session ${udid}: device logs come from agent-device/EAS, so no simctl log stream is attached`
2231
2452
  });
2232
2453
  if (physical) raiseLeaseFor(release ? RELEASE_VERIFY_WAIT_MS : DEBUG_VERIFY_STEP_MS, false);
2233
- const { state: launchState, warning: launchWarning } = await verifyIosRun({
2454
+ const { state: launchState, warning: launchWarning, unattributed } = await verifyIosRun({
2234
2455
  root,
2235
2456
  slot,
2236
2457
  appPath,
@@ -2271,7 +2492,8 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2271
2492
  release,
2272
2493
  bundleId,
2273
2494
  configuration,
2274
- metroPort
2495
+ metroPort,
2496
+ unattributed
2275
2497
  }));
2276
2498
  const leaseFacts = lease?.facts() ?? null;
2277
2499
  releaseLease();
@@ -2304,12 +2526,20 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2304
2526
  launchState,
2305
2527
  launchWarning,
2306
2528
  providerName,
2529
+ buildMachine,
2530
+ builtOn,
2531
+ offloadedTo,
2532
+ offloadFallback,
2307
2533
  closeWriter,
2308
2534
  webPreviewUrl: remoteDevice?.webPreviewUrl() ?? null,
2309
2535
  lease: physical ? leaseFacts : void 0,
2310
2536
  recordRun,
2311
2537
  reclaimed,
2312
- devServer
2538
+ devServer,
2539
+ links: workspaceLinks(root, {
2540
+ platform: "ios",
2541
+ slot
2542
+ })
2313
2543
  }),
2314
2544
  uploadsAbandoned
2315
2545
  };
@@ -2368,19 +2598,22 @@ async function planIos(opts, d, schemeRefusal) {
2368
2598
  platform: "ios",
2369
2599
  slot,
2370
2600
  root,
2371
- projectKey: statsProjectKey({
2601
+ projectKey: statsProjectKey$1({
2372
2602
  root,
2373
2603
  commonDir: settingsContext.gitCommonDir,
2374
2604
  repoRoot: settingsContext.repoRoot
2375
2605
  })
2376
2606
  };
2377
2607
  const isExpo = d.detectIsExpo(root);
2608
+ const settingsLayersForOrigin = d.settingsLayers(settingsContext);
2378
2609
  const modelRefusal = deviceModelRefusal({
2379
2610
  slot,
2380
2611
  deviceTypeFlag: opts.deviceType,
2381
2612
  runtimeFlag: opts.runtime,
2382
2613
  deviceType: resolveDeviceType(opts.deviceType, settings),
2383
2614
  runtime: resolveRuntime(opts.runtime, settings),
2615
+ deviceTypeOrigin: d.settingOriginScope(settingsLayersForOrigin, "ios.deviceType"),
2616
+ runtimeOrigin: d.settingOriginScope(settingsLayersForOrigin, "ios.runtime"),
2384
2617
  physical: false,
2385
2618
  remoteBackend: remoteIosSetting(settings),
2386
2619
  listRuntimes: d.listIosRuntimes
@@ -2407,6 +2640,12 @@ async function planIos(opts, d, schemeRefusal) {
2407
2640
  refusal: eas.missing
2408
2641
  }), json);
2409
2642
  }
2643
+ const remoteBackend = remoteIosSetting(settings);
2644
+ if (remoteBackend) return refuse({
2645
+ code: "STIM_BAD_ARG",
2646
+ message: `ios.remote routes this workspace's runs to a ${remoteBackend} device, whose architecture --plan cannot read without a session.`,
2647
+ remedy: "Run `stim ios` to build for the remote device, or unset ios.remote to plan the owned simulator."
2648
+ });
2410
2649
  const refusal = schemeRefusal(root, opts.scheme, isExpo);
2411
2650
  if (refusal) return refuse({
2412
2651
  code: refusal.code,
@@ -2427,10 +2666,17 @@ async function planIos(opts, d, schemeRefusal) {
2427
2666
  message: `Could not fingerprint ${root}: @expo/fingerprint produced no hash for it.`,
2428
2667
  remedy: "Check the project native inputs and the @expo/fingerprint error above, then retry."
2429
2668
  });
2669
+ const arch = simulatorBuildArch({
2670
+ physical: false,
2671
+ remoteArch: null,
2672
+ hostArch: d.hostSimulatorArch(),
2673
+ configuration
2674
+ });
2430
2675
  const cacheKey = buildCacheKey("ios", fingerprint.hash, {
2431
2676
  scheme: opts.scheme,
2432
2677
  ...configuration ? { configuration } : {},
2433
2678
  isSimulator: true,
2679
+ ...arch ? { arch } : {},
2434
2680
  ...buildProfile ? { buildProfile } : {}
2435
2681
  });
2436
2682
  const plan = await planCachedBuild({
@@ -2441,7 +2687,7 @@ async function planIos(opts, d, schemeRefusal) {
2441
2687
  cacheKey,
2442
2688
  cachePolicy,
2443
2689
  providerConfig: d.resolveCacheProviderConfig(settingsContext),
2444
- expoRemote: cachePolicy.remote && !buildProfile && !opts.scheme ? { runOptions: configuration ? { configuration } : null } : null
2690
+ expoRemote: cachePolicy.remote && !buildProfile && !opts.scheme ? { runOptions: iosProviderRunOptions(configuration, arch) } : null
2445
2691
  }, {
2446
2692
  loadCacheProvider: d.loadCacheProvider,
2447
2693
  loadProjectProvider: d.loadProjectProvider,
@@ -2464,7 +2710,7 @@ function iosCommand(program) {
2464
2710
  registerIos(program);
2465
2711
  }
2466
2712
  function registerIos(program, deps = {}) {
2467
- 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("--plan", "Predict the next build without building, booting or installing: resolve the fingerprint, check the local then remote cache, and print the expected cache result and duration. Writes no Stim state.").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) => {
2713
+ 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("--build-machine <value>", "Build on auto, local, or one named machine; a name refuses without fallback", parseBuildMachineOption).option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--plan", "Predict the next build without building, booting or installing: resolve the fingerprint, check the local then remote cache, and print the expected cache result and duration. Writes no Stim state.").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) => {
2468
2714
  if (REMOTE_DEVICE_BACKENDS.includes(value)) return value;
2469
2715
  throw new InvalidArgumentError(`expected one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}`);
2470
2716
  }).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) => {
@@ -2527,6 +2773,32 @@ function explicitSchemeRefusal(root, scheme, isExpo, d) {
2527
2773
  if (project.error) return project.error;
2528
2774
  return d.resolveScheme(project, { scheme }).error ?? null;
2529
2775
  }
2776
+ function resolveIosBuildSetup(flag, settings) {
2777
+ const placement = resolveBuildPlacement(flag);
2778
+ if (placement.failure) return {
2779
+ ok: false,
2780
+ failure: {
2781
+ ...placement.failure,
2782
+ setup: true
2783
+ }
2784
+ };
2785
+ try {
2786
+ return {
2787
+ ok: true,
2788
+ buildMachine: placement.selected,
2789
+ optimizations: resolveOptimizations(settings)
2790
+ };
2791
+ } catch (error) {
2792
+ return {
2793
+ ok: false,
2794
+ failure: {
2795
+ code: "STIM_BAD_ARG",
2796
+ message: error.message,
2797
+ remedy: SETTING_SHAPE_REMEDY
2798
+ }
2799
+ };
2800
+ }
2801
+ }
2530
2802
  function iosSlotLogFile(root, slot) {
2531
2803
  return slot === "default" ? buildLogFile(root) : join(workspaceLogsDir(root), `build-${deviceSlotFileKey("ios", slot)}.ndjson`);
2532
2804
  }
@@ -2622,7 +2894,13 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2622
2894
  const logsDir = workspaceLogsDir(root);
2623
2895
  const logFile = iosSlotLogFile(root, slot);
2624
2896
  let writer = null;
2625
- const logWriter = () => writer ||= d.createWriter(logFile, { truncate: true });
2897
+ const logWriter = () => writer ||= tapBuildLog(d.createWriter(logFile, {
2898
+ truncate: true,
2899
+ fields: {
2900
+ platform: "ios",
2901
+ slot
2902
+ }
2903
+ }), progress);
2626
2904
  let leaseHandle = null;
2627
2905
  let stopLeaseSignals = null;
2628
2906
  const releaseLease = () => {
@@ -2642,13 +2920,15 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2642
2920
  write: (statsRun, at) => d.recordStats(statsRun, at),
2643
2921
  now: () => d.now(),
2644
2922
  note: (line) => note(chalk.dim(line)),
2645
- phases: () => progress.durations()
2923
+ phases: () => progress.durations(),
2924
+ deviceSetup: () => progress.deviceSetupKnown()
2646
2925
  });
2647
2926
  const recordRun = stats.record;
2648
2927
  let compilationCache = COMPILATION_CACHE_NOT_RUN;
2649
2928
  let reclaimed = [];
2929
+ let buildMachine = "auto";
2650
2930
  let builtConfiguration = null;
2651
- const fail = ({ code, message, remedy = null, lines = [], logPath = null, build = null, lease }) => {
2931
+ const fail = ({ code, message, remedy = null, lines = [], logPath = null, build = null, lease, setup = false }) => {
2652
2932
  ({code, message, remedy, lines} = cancelledFailure("ios", {
2653
2933
  code,
2654
2934
  message
@@ -2664,6 +2944,7 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2664
2944
  if (remedy) note(chalk.dim(phaseLine("remedy", remedy)));
2665
2945
  if (logPath) note(chalk.dim(phaseLine("log", logPath)));
2666
2946
  if (build) writeLastBuild(root, lastBuildRecord({
2947
+ buildMachine,
2667
2948
  ...build,
2668
2949
  configuration: builtConfiguration,
2669
2950
  startedAt,
@@ -2672,7 +2953,7 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2672
2953
  durationMs: elapsed()
2673
2954
  }));
2674
2955
  note(chalk.red(phaseLine("failed", code)));
2675
- recordRun({
2956
+ if (!setup) recordRun({
2676
2957
  failed: true,
2677
2958
  durationMs: elapsed()
2678
2959
  });
@@ -2694,12 +2975,13 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2694
2975
  gitCommonDir: d.gitCommonDir(root),
2695
2976
  repoRoot: settingsRepoRoot
2696
2977
  };
2697
- const projectKey = statsProjectKey({
2978
+ const projectKey = statsProjectKey$1({
2698
2979
  root,
2699
2980
  commonDir: settingsContext.gitCommonDir,
2700
2981
  repoRoot: settingsRepoRoot
2701
2982
  });
2702
2983
  stats.setProject(projectKey);
2984
+ progress.estimate(projectKey);
2703
2985
  let estimatesRead = null;
2704
2986
  const estimates = () => estimatesRead ??= d.readEstimates({
2705
2987
  projectKey,
@@ -2713,16 +2995,10 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2713
2995
  lines: moreShapeErrors,
2714
2996
  remedy: SETTING_SHAPE_REMEDY
2715
2997
  });
2716
- let optimizations;
2717
- try {
2718
- optimizations = resolveOptimizations(settings);
2719
- } catch (error) {
2720
- return fail({
2721
- code: "STIM_BAD_ARG",
2722
- message: error.message,
2723
- remedy: SETTING_SHAPE_REMEDY
2724
- });
2725
- }
2998
+ const setup = resolveIosBuildSetup(opts.buildMachine, settings);
2999
+ if (!setup.ok) return fail(setup.failure);
3000
+ buildMachine = setup.buildMachine;
3001
+ const { optimizations } = setup;
2726
3002
  const buildProfile = optimizationBuildProfile("ios", optimizations);
2727
3003
  const cacheProviderConfig = d.resolveCacheProviderConfig(settingsContext);
2728
3004
  for (const key of unknownSettingKeys(settings)) note(phaseLine("setting", chalk.yellow(`Warning: setting "${key}" is not read by Stim and will be ignored.`)));
@@ -2789,12 +3065,15 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2789
3065
  const viewer = resolveSimulatorAppFlag(opts.simulatorApp, physical, remoteBackend);
2790
3066
  if ("refusal" in viewer) return fail(viewer.refusal);
2791
3067
  const { simulatorApp } = viewer;
3068
+ const settingsLayersForOrigin = d.settingsLayers(settingsContext);
2792
3069
  const modelRefusal = deviceModelRefusal({
2793
3070
  slot,
2794
3071
  deviceTypeFlag: opts.deviceType,
2795
3072
  runtimeFlag: opts.runtime,
2796
3073
  deviceType,
2797
3074
  runtime,
3075
+ deviceTypeOrigin: d.settingOriginScope(settingsLayersForOrigin, "ios.deviceType"),
3076
+ runtimeOrigin: d.settingOriginScope(settingsLayersForOrigin, "ios.runtime"),
2798
3077
  physical,
2799
3078
  remoteBackend,
2800
3079
  listRuntimes: d.listIosRuntimes
@@ -2807,11 +3086,13 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2807
3086
  reclaimed = budget.reclaimed;
2808
3087
  if (budget.refusal) return fail(budget.refusal);
2809
3088
  let remoteDevice = null;
3089
+ let remoteArch = null;
2810
3090
  if (remoteBackend) {
2811
3091
  const resolved = await d.resolveRemoteContext({
2812
3092
  root,
2813
3093
  backend: remoteBackend,
2814
- easBin: d.resolveEasCliBin(root)?.file ?? null
3094
+ easBin: d.resolveEasCliBin(root)?.file ?? null,
3095
+ deviceType: opts.deviceType?.trim() || null
2815
3096
  });
2816
3097
  if ("failed" in resolved) return fail({
2817
3098
  code: resolved.code ?? "STIM_NO_REMOTE_SESSION",
@@ -2819,6 +3100,7 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2819
3100
  remedy: resolved.remedy
2820
3101
  });
2821
3102
  remoteDevice = d.remoteIosDeps(resolved.ctx);
3103
+ remoteArch = await d.readRemoteSimulatorArch(resolved.ctx.existingDaemon);
2822
3104
  d = {
2823
3105
  ...d,
2824
3106
  checkDeviceCapacity: remoteDevice.checkDeviceCapacity,
@@ -2932,12 +3214,9 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2932
3214
  out: note
2933
3215
  });
2934
3216
  } catch (e) {
2935
- return fail({
2936
- code: "STIM_NO_DEVICE",
2937
- message: `Could not ensure an owned iOS simulator: ${e?.message || e}`,
2938
- remedy: "Run `stim doctor` to check the simulator toolchain, then try again."
2939
- });
3217
+ return fail(ownedSimFailure(e));
2940
3218
  }
3219
+ progress.deviceSetup(didSetUpDevice(device, Boolean(remoteDevice)));
2941
3220
  const prepareMs = prepare();
2942
3221
  if (device.created || prepareMs >= 2e3) phase("device", `${deviceLabel(device, device.deviceUdid)} ${device.created ? "created" : "prepared"} (${formatDuration(prepareMs)})`);
2943
3222
  }
@@ -2971,9 +3250,18 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2971
3250
  }
2972
3251
  metroPort = gate.port;
2973
3252
  devServer = gate.devServer;
2974
- } else if (!metroPort) {
2975
- metroPort = DEFAULT_METRO_PORT;
2976
- note(chalk.yellow(`No Metro port is reserved for this workspace; wiring the app to ${metroPort}.`));
3253
+ } else {
3254
+ const pin = metroPortSetting(root);
3255
+ if (pin.error) {
3256
+ fail({
3257
+ code: "STIM_BAD_ARG",
3258
+ message: pin.error,
3259
+ remedy: SETTING_SHAPE_REMEDY
3260
+ });
3261
+ return false;
3262
+ }
3263
+ if (pin.port === null && !metroPort) note(chalk.yellow(`No Metro port is reserved for this workspace; wiring the app to ${DEFAULT_METRO_PORT}.`));
3264
+ metroPort = pin.port ?? metroPort ?? 8081;
2977
3265
  }
2978
3266
  if (physical && metroPort !== null && !await resolveLanOrigin()) return false;
2979
3267
  if (remoteDevice && metroPort !== null) {
@@ -2983,7 +3271,7 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
2983
3271
  isExpo,
2984
3272
  tunnelMode: tunnelModeSetting(settings) ?? void 0,
2985
3273
  publicUrl: publicUrlSetting(settings),
2986
- available: d.detectProviders(binOnPath)
3274
+ available: d.detectProviders(binOnPath, tunnelModeSetting(settings) ?? "auto")
2987
3275
  });
2988
3276
  if ("failed" in reachable) {
2989
3277
  fail({
@@ -3062,6 +3350,7 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
3062
3350
  platform: "ios",
3063
3351
  sessionName: ownedSessionName(remoteDevice.ctx.label),
3064
3352
  startedAt: new Date(d.now()).toISOString(),
3353
+ deviceType: remoteDevice.ctx.deviceType ?? null,
3065
3354
  boot,
3066
3355
  createdSessionId: remoteDevice.createdSessionId,
3067
3356
  abandonCreatedSession: remoteDevice.abandonCreatedSession,
@@ -3084,8 +3373,15 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
3084
3373
  configuration,
3085
3374
  buildScheme,
3086
3375
  buildProfile,
3376
+ buildMachine,
3087
3377
  isExpo,
3088
3378
  remoteDestination: Boolean(remoteDevice),
3379
+ simulatorArch: simulatorBuildArch({
3380
+ physical,
3381
+ remoteArch,
3382
+ hostArch: d.hostSimulatorArch(),
3383
+ configuration
3384
+ }),
3089
3385
  device: physical ? {
3090
3386
  lanAddress,
3091
3387
  metroPort,
@@ -3107,7 +3403,11 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
3107
3403
  logWriter,
3108
3404
  estimates,
3109
3405
  stats,
3110
- step: progress.step
3406
+ step: progress.step,
3407
+ miss: progress.miss,
3408
+ hit: progress.hit,
3409
+ place: progress.place,
3410
+ waitingOn: progress.waitingOn
3111
3411
  }
3112
3412
  }, d));
3113
3413
  if (!acquiredArtifact.ok) {
@@ -3117,10 +3417,11 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
3117
3417
  artifact = acquiredArtifact.artifact;
3118
3418
  compilationCache = artifact.cache.compilation;
3119
3419
  if (!localBoot) {
3120
- progress.step("install");
3420
+ progress.step("device");
3121
3421
  udid = await startBoot();
3122
3422
  }
3123
3423
  if (physicalDevice) {
3424
+ progress.step("device");
3124
3425
  const acquired = await d.acquireRunLease({
3125
3426
  root,
3126
3427
  platform: "ios",