stim 1.17.1 → 1.18.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 (150) hide show
  1. package/README.md +50 -1
  2. package/dist/{activity-IRvJY_ru.mjs → activity-BxnkDZDi.mjs} +4 -4
  3. package/dist/{agent-device-usage-output-BK41vnzN.mjs → agent-device-usage-output-CZ66YA4T.mjs} +8 -8
  4. package/dist/android-BKA16DTf.mjs +4 -0
  5. package/dist/{android-B6rDWrTm.mjs → android-BvwuMUJ9.mjs} +192 -98
  6. package/dist/{android-DE4v59Mb.mjs → android-CBwCNicO.mjs} +2 -2
  7. package/dist/{android-B_YNZy61.mjs → android-DC3ZLuw0.mjs} +9 -9
  8. package/dist/{android-cas-ZFpjJBL_.mjs → android-cas-BBFyr9CA.mjs} +5 -5
  9. package/dist/android-cas-compiler.mjs +2 -2
  10. package/dist/api-D0VtFhqk.mjs +152 -0
  11. package/dist/api-run.d.mts +1 -0
  12. package/dist/api-run.mjs +145 -0
  13. package/dist/api.d.mts +212 -0
  14. package/dist/api.mjs +2 -0
  15. package/dist/{app-install-VPkI5WEg.mjs → app-install-D-PUENug.mjs} +3 -3
  16. package/dist/{attempt-Bk2ouQVL.mjs → attempt-DW-mQqM7.mjs} +14 -4
  17. package/dist/{budget-CUDz870h.mjs → budget-DIhL2BKx.mjs} +1581 -61
  18. package/dist/build-facts-DcLv2HSd.d.mts +111 -0
  19. package/dist/{cache-manifest-DnUHBJaU.mjs → cache-manifest-BI7ozfmH.mjs} +3 -3
  20. package/dist/cache-manifest.mjs +1 -1
  21. package/dist/{chrome-BrcVWQyD.mjs → chrome-DJnmnS5q.mjs} +1 -1
  22. package/dist/cli-DNJMwADx.mjs +68 -0
  23. package/dist/cli.mjs +1 -1
  24. package/dist/{client-CxNotugx.mjs → client-DzhapoMB.mjs} +184 -49
  25. package/dist/collector-run.mjs +9 -9
  26. package/dist/{command-output-CDAIFD-8.mjs → command-output-CBtzyIgF.mjs} +2 -2
  27. package/dist/{config-D0DbpZtM.mjs → config-CgYMahz7.mjs} +5 -5
  28. package/dist/{created-devices-CpvzBL25.mjs → created-devices-DKbeWsiK.mjs} +2 -2
  29. package/dist/{dependency-state-CLcBHH2M.mjs → dependency-state-xKoOypBm.mjs} +2 -2
  30. package/dist/{detached-entry-CiTEgMR1.mjs → detached-entry-BLfcgc4O.mjs} +1 -1
  31. package/dist/{dev-client-B-XC4uRH.mjs → dev-client-CLnIH02x.mjs} +2 -2
  32. package/dist/{device-C8IOoJPv.mjs → device-D7oCHGWi.mjs} +8 -8
  33. package/dist/{device-capacity-ZC4XSZ4R.mjs → device-capacity-EUOOYr8T.mjs} +13 -13
  34. package/dist/device-host-worker.mjs +19 -19
  35. package/dist/{device-ios-C2Um-XUR.mjs → device-ios-6MewKKnc.mjs} +13 -13
  36. package/dist/{device-lease-DpoS36_u.mjs → device-lease-Co-EwifR.mjs} +9 -9
  37. package/dist/{device-lease-run-B8YltIH_.mjs → device-lease-run-w1xhCqIW.mjs} +4 -4
  38. package/dist/{device-pool-3o7s5lo4.mjs → device-pool-F5WvRPtD.mjs} +3 -3
  39. package/dist/{device-remote-hFSkpfQu.mjs → device-remote-tHbd1fKy.mjs} +15 -15
  40. package/dist/{doctor-BF_435OC.mjs → doctor-B9ckYUoU.mjs} +33 -30
  41. package/dist/{error-diagnostics-C-NMnhrk.mjs → error-diagnostics-BRXOqDzG.mjs} +11 -11
  42. package/dist/{exec-CaErVv7t.mjs → exec-DWhCZDGm.mjs} +65 -2
  43. package/dist/{gc-BsWKa_-d.mjs → gc-Dh6mXOKC.mjs} +23 -469
  44. package/dist/{gradle-bsBRy6i9.mjs → gradle-C6GKnunQ.mjs} +6 -6
  45. package/dist/{guide-status-Du-ZQ3FD.mjs → guide-status-Bhf9SJXc.mjs} +3 -3
  46. package/dist/{guide-DC3n6-NH.mjs → guide-tm6igAaa.mjs} +314 -72
  47. package/dist/{hosted-android-DgU31wY3.mjs → hosted-android-Bv285t_t.mjs} +3 -3
  48. package/dist/{hosted-client-CdSoH-G6.mjs → hosted-client-C-rMtppF.mjs} +2 -2
  49. package/dist/{hosted-ios-BTaVj4Jb.mjs → hosted-ios-D5mNmtj4.mjs} +1 -1
  50. package/dist/{hosted-logs-SxfBvSvc.mjs → hosted-logs-DN8Iz4mJ.mjs} +9 -9
  51. package/dist/{hosted-macos-CT9E3meL.mjs → hosted-macos-B-wEjlwG.mjs} +5 -5
  52. package/dist/{hosted-native-CZLOrMrN.mjs → hosted-native-DeeOG8xZ.mjs} +6 -6
  53. package/dist/{idle-shutdown-IGyNV8Cr.mjs → idle-shutdown-jjtxAn5C.mjs} +7 -8
  54. package/dist/{ios-DSfnPfse.mjs → ios-B3SbOWdw.mjs} +19 -7
  55. package/dist/{ios-Cr1h5gnO.mjs → ios-Bvbf0SGY.mjs} +1 -1
  56. package/dist/{ios-C7JuT1bI.mjs → ios-CPdkoKaa.mjs} +181 -95
  57. package/dist/ios-DNpdygiv.mjs +7 -0
  58. package/dist/{ios-device-Ee7sDXfp.mjs → ios-device-Bn7jWDB1.mjs} +1 -1
  59. package/dist/{ios-device-pAN4t54-.mjs → ios-device-vOaByMhw.mjs} +3 -3
  60. package/dist/{ios-state-8L3lgum6.mjs → ios-state-B2pES6Vs.mjs} +1 -1
  61. package/dist/{launch-verify-lTnWZikR.mjs → launch-verify-hskNcWkZ.mjs} +5 -5
  62. package/dist/{logs-DNxKcok9.mjs → logs-1Ylc1Ri6.mjs} +13 -13
  63. package/dist/macos-Ck7OBMNY.mjs +2 -0
  64. package/dist/{macos-BEs8cwKx.mjs → macos-DF6C7wUU.mjs} +118 -34
  65. package/dist/macos-run.mjs +1 -1
  66. package/dist/maintenance-run.mjs +349 -47
  67. package/dist/{metro-g8NqiJp-.mjs → metro-Dh8T4Ytd.mjs} +5 -5
  68. package/dist/{metro-gateway-CHqK0p-a.mjs → metro-gateway-CEUw9RMH.mjs} +4 -4
  69. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-BAHFcJm6.mjs} +16 -2
  70. package/dist/{named-ports-C8m8EQs2.mjs → named-ports-BFIz1Shz.mjs} +61 -6
  71. package/dist/{native-run-D1Q56rAD.mjs → native-run-B06FkmZx.mjs} +4 -4
  72. package/dist/{native-runtime-YhaSxP4v.mjs → native-runtime-D_603JiC.mjs} +6 -6
  73. package/dist/{ndjson-PclOIwVY.mjs → ndjson-LGZAMsqa.mjs} +8 -4
  74. package/dist/offload-worker.d.mts +3 -17
  75. package/dist/offload-worker.mjs +10 -11
  76. package/dist/{ownership-DyRQru0q.mjs → ownership-D6Se7gg_.mjs} +3 -3
  77. package/dist/{page-QF_Ke10s.mjs → page-Bv9s2KwY.mjs} +1 -1
  78. package/dist/placement-log-BcPhRwR4.mjs +84 -0
  79. package/dist/{build-plan-BJSXzsCL.mjs → plan-placement-CtxkCO54.mjs} +122 -48
  80. package/dist/{ports-4nYqxPWC.mjs → ports-BLTtfBq-.mjs} +3 -3
  81. package/dist/{prebuild-D7RUMkwN.mjs → prebuild-juCyreaR.mjs} +5 -5
  82. package/dist/preview-D5V07Nrl.mjs +987 -0
  83. package/dist/{rolldown-runtime-BsOwBuO_.mjs → process-identity-Biv3arCO.mjs} +11 -1
  84. package/dist/{project-D47Rh0ly.mjs → project-C3ChRLZy.mjs} +4 -68
  85. package/dist/projects-DD7dBTCj.mjs +92 -0
  86. package/dist/{pull-request-pxnDCQzn.mjs → pull-request-u4vp7zmK.mjs} +1 -1
  87. package/dist/pull-requests.mjs +1 -1
  88. package/dist/{recordings-BNgsD1Tr.mjs → recordings-BAWltsVt.mjs} +1 -1
  89. package/dist/{reload-byOjE3P4.mjs → reload-DvyWzVjJ.mjs} +15 -15
  90. package/dist/{remote-cache-Ci4DQYYj.mjs → remote-cache-BGg7aGIZ.mjs} +5 -5
  91. package/dist/{run-CkE9y0nc.mjs → run-vhtDgpHO.mjs} +5 -5
  92. package/dist/{server-bare-BkrSxSsZ.mjs → server-bare-B9E7S1aR.mjs} +4 -5
  93. package/dist/server-expo-Db9HBUoa.mjs +2 -0
  94. package/dist/{server-expo-CHTyXD0G.mjs → server-expo-DoBHhuzK.mjs} +5 -6
  95. package/dist/{settings-SR9OmW9h.mjs → settings-Byp2AQ_E.mjs} +6 -5
  96. package/dist/{settings-Cfd-0r2d.mjs → settings-CQyMElEP.mjs} +24 -763
  97. package/dist/{settings-BBpMcDbD.mjs → settings-Djl-mpjX.mjs} +1 -1
  98. package/dist/settings.schema.json +128 -5
  99. package/dist/{simslim-BWNrc3M3.mjs → simslim-EHBQ2R4G.mjs} +7 -7
  100. package/dist/{slot-launch-DmjBmpQV.mjs → slot-launch-C1nqh4mu.mjs} +6 -6
  101. package/dist/{spawn-claims-D0XfSe8u.mjs → spawn-claims-Kqiifsk2.mjs} +1 -1
  102. package/dist/{spawn-entry-jw19m8Nl.mjs → spawn-entry-Dcb7sDUz.mjs} +1 -0
  103. package/dist/{stage-CzaiwFsI.mjs → stage-g_WqWeaE.mjs} +14 -3
  104. package/dist/{start-BHIersSm.mjs → start-D-IeOyoK.mjs} +19 -19
  105. package/dist/{start-BEP-XmEA.mjs → start-NTc1Ifqs.mjs} +1 -1
  106. package/dist/{state-PeQiJuIu.mjs → state-BG-aAk4k.mjs} +4 -4
  107. package/dist/{state-B4PQFXMW.d.mts → state-lKh8shBp.d.mts} +2 -3
  108. package/dist/{state-DyaNTgfR.mjs → state-xnqpx3AD.mjs} +2 -2
  109. package/dist/{stats-Bq4fJOdB.mjs → stats-DY6ekz8F.mjs} +5 -5
  110. package/dist/{status-DlKvVJBE.mjs → status-BEeL6IKz.mjs} +6 -4
  111. package/dist/{status-CqeWbIWW.mjs → status-DCjZeE-K.mjs} +34 -35
  112. package/dist/{status-CzS4bPO9.mjs → status-Dcr0Qexd.mjs} +5 -5
  113. package/dist/{stim-desktop-Cnz8CPnl.mjs → stim-desktop-Behws_fi.mjs} +1 -1
  114. package/dist/{stim-installations-QMEI32v1.mjs → stim-installations-aR_WsNOA.mjs} +1 -1
  115. package/dist/{stop-D03prQnW.mjs → stop-BGhKMOuH.mjs} +5 -5
  116. package/dist/{stop-Cj01je8D.mjs → stop-BTjs3xbc.mjs} +17 -18
  117. package/dist/{stop-DOxwkH4j.mjs → stop-DIJtD2GL.mjs} +2 -2
  118. package/dist/supervisor-run.d.mts +24 -22
  119. package/dist/supervisor-run.mjs +21 -22
  120. package/dist/{support-BE-tbOP9.mjs → support-BaayeYCf.mjs} +7 -7
  121. package/dist/{support-WvD__tSV.mjs → support-Bwvbrq5-.mjs} +5 -5
  122. package/dist/{toolchain-CdrxgUeA.mjs → toolchain-Bnyd_K3A.mjs} +480 -27
  123. package/dist/{trigger-Bemu8ytw.mjs → trigger-CJMhwXe8.mjs} +5 -5
  124. package/dist/trigger-DWAq9hkw.mjs +2 -0
  125. package/dist/{warm-progress-DF4JRvYI.mjs → warm-progress-CjB-MP6H.mjs} +7 -7
  126. package/dist/web-B6_bFDn7.mjs +2 -0
  127. package/dist/{web-DddUdJzr.mjs → web-DTU0uiWj.mjs} +22 -22
  128. package/dist/web-run.mjs +10 -10
  129. package/dist/{workspace-state-DqcCbpE7.mjs → workspace-state-C59dqG-Z.mjs} +3 -3
  130. package/dist/{ownership-BMLqyBgJ.mjs → workspaces-xXItD8Dj.mjs} +577 -23
  131. package/dist/{worktree-CsAjSmEo.mjs → worktree-BJfnU6r5.mjs} +1 -1
  132. package/dist/{worktree-AK_m-d_m.mjs → worktree-BSYlofkq.mjs} +234 -35
  133. package/dist/worktree-CsKYexJM.mjs +753 -0
  134. package/package.json +10 -5
  135. package/dist/build-progress-D0WkjCPd.mjs +0 -1036
  136. package/dist/build-slots-41v_d2x9.mjs +0 -142
  137. package/dist/cli-BLQqDGlX.mjs +0 -51
  138. package/dist/deps-CVxlsRFn.mjs +0 -438
  139. package/dist/errors-B6W7FkxQ.mjs +0 -15
  140. package/dist/idle-BXG__1BA.mjs +0 -176
  141. package/dist/in-use-CvGOUn-x.mjs +0 -130
  142. package/dist/preview-C_JjkEy4.mjs +0 -318
  143. package/dist/process-identity-BmRm1qV0.mjs +0 -12
  144. package/dist/server-expo-CeakhI8P.mjs +0 -2
  145. package/dist/state-BKa8wNN5.mjs +0 -148
  146. package/dist/stop-cause-rhmGfwvt.mjs +0 -97
  147. package/dist/trigger-CXts6qt1.mjs +0 -2
  148. package/dist/watchman-APYjXSQa.mjs +0 -48
  149. package/dist/workspace-process-lock-2mNzOlaB.mjs +0 -58
  150. package/dist/workspaces-Bcib97-6.mjs +0 -306
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Stim
2
2
 
3
- The `stim` npm package installs the `stim` command.
3
+ The `stim` npm package installs the `stim` command and exports a typed lifecycle API.
4
4
 
5
5
  Stim gives coding agents fast, isolated React Native and Expo environments. Each
6
6
  project or git worktree gets its own Metro port and owned device. Shared caches
@@ -36,6 +36,55 @@ Stim builds or restores the app, installs it, launches it, and checks launch
36
36
  readiness. Plain output streams progress and reports the complete result. Use
37
37
  `--json` when a script needs structured data.
38
38
 
39
+ ## Programmatic API
40
+
41
+ Install `stim` as a dependency with `npm install --save-dev stim`, then import
42
+ from the main package:
43
+
44
+ ```ts
45
+ import { createStim } from 'stim';
46
+
47
+ const stim = createStim({ projectRoot: process.cwd() });
48
+ try {
49
+ const result = await stim.run({ platform: 'ios' });
50
+ console.log(result.facts);
51
+ } finally {
52
+ try {
53
+ console.log(await stim.diagnostics({ errors: true }));
54
+ } finally {
55
+ const cleanup = await stim.stop();
56
+ if (!cleanup.ok) throw new Error(cleanup.summary);
57
+ }
58
+ }
59
+ ```
60
+
61
+ `run` supports iOS and Android React Native/Expo apps, SwiftPM macOS apps, and
62
+ web pages with the same requirements as their CLI commands. Web runs require an
63
+ already running server. `run` builds, installs and launches; there is no
64
+ build-only API yet. It preserves the CLI's launch evidence, including
65
+ `'bundling'` and `'unverified'` results.
66
+
67
+ Each operation uses a separate bundled worker so its home, cancellation and
68
+ process state cannot change the importing process. The CLI and workers call
69
+ the same native lifecycle operations. Progress is silent unless `onProgress`
70
+ is provided; its `message` is an output chunk. Operations accept `signal`.
71
+ Cancellation waits for the worker to exit, and does not replace `stop()`.
72
+ Call cleanup with a fresh signal after cancellation or partial failure.
73
+
74
+ `home` and `buildCache` accept absolute paths. Omit them to use normal Stim
75
+ settings. A cache used by concurrent writers needs the same coordinating Stim
76
+ home; do not point independent homes at a concurrently writable artifact cache.
77
+ Ownership checks and cache locks remain active. `stop()` acts on this workspace,
78
+ including resources from earlier runs, so use a dedicated workspace in CI.
79
+
80
+ Stim failures are `StimError` instances with `code`, `message`, `remedy`, and
81
+ `details`. An exception thrown by `onProgress` cancels the worker and is
82
+ propagated unchanged.
83
+ `diagnostics()` returns the workspace log directory and up to 200 local records,
84
+ even if no run has succeeded. It does not fetch remote logs or capture new crashes.
85
+ See [the API reference](https://stim.appandflow.com/docs/programmatic-api) or
86
+ `stim guide api` for options and result types.
87
+
39
88
  ## Documentation
40
89
 
41
90
  The [documentation website](https://stim.appandflow.com/) is the full
@@ -1,7 +1,7 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
3
- import { r as readWorkspaceState$1, x as workspaceLogsDir$1 } from "./workspace-state-DqcCbpE7.mjs";
4
- import { i as listLeaseFiles, r as leaseIsExpired } from "./device-lease-DpoS36_u.mjs";
1
+ import { t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { t as getExecutor } from "./exec-DWhCZDGm.mjs";
3
+ import { r as readWorkspaceState$1, x as workspaceLogsDir$1 } from "./workspace-state-C59dqG-Z.mjs";
4
+ import { i as listLeaseFiles, r as leaseIsExpired } from "./device-lease-Co-EwifR.mjs";
5
5
  import { closeSync, fstatSync, openSync, readFileSync, readSync, readdirSync, statSync } from "node:fs";
6
6
  import { basename, join, resolve } from "node:path";
7
7
  import { agentDeviceStateDirs, readViewedDevices } from "@stim-cli/core/state";
@@ -1,16 +1,16 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { j as formatBytes } from "./config-D0DbpZtM.mjs";
3
- import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
- import { o as formatLongDuration } from "./command-output-CDAIFD-8.mjs";
5
- import { a as envDir, l as processMatches, s as parseAgentDeviceRecord } from "./activity-IRvJY_ru.mjs";
6
- import { c as listWorkspaceDirs } from "./workspaces-Bcib97-6.mjs";
7
- import { randomUUID } from "node:crypto";
1
+ import { t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { t as getExecutor } from "./exec-DWhCZDGm.mjs";
3
+ import { o as formatLongDuration } from "./command-output-CBtzyIgF.mjs";
4
+ import { j as formatBytes } from "./config-CgYMahz7.mjs";
5
+ import { c as listWorkspaceDirs } from "./workspaces-xXItD8Dj.mjs";
6
+ import { a as envDir, l as processMatches, s as parseAgentDeviceRecord } from "./activity-BxnkDZDi.mjs";
8
7
  import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
9
8
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
10
- import { configDir } from "@stim-cli/core";
11
9
  import { agentDeviceUsageFile, readAgentDeviceUsageCache, readSwiftpmCacheUsage, swiftpmCacheUsageFile } from "@stim-cli/core/state";
12
10
  import { homedir } from "node:os";
13
11
  import chalk from "chalk";
12
+ import { configDir } from "@stim-cli/core";
13
+ import { randomUUID } from "node:crypto";
14
14
  //#region src/devices/report-only-usage.ts
15
15
  function canonical(path) {
16
16
  try {
@@ -0,0 +1,4 @@
1
+ import { i as formatDuration, l as phaseLine, m as shortHash } from "./command-output-CBtzyIgF.mjs";
2
+ import { a as collectorLogFile, c as androidFacts, i as runAndroidOperation, l as lastBuildRecord, n as registerAndroid, o as killPreviousCollector, r as runAndroid, s as startCollector, t as androidCommand } from "./android-BvwuMUJ9.mjs";
3
+ import { C as resolveSystemImage, _ as isReleaseVariant, c as androidSystemImageSetting, d as apkPackage, h as findAapt, i as NO_FINGERPRINT, l as androidVariantSetting, m as dumpApkManifest, p as displayPath, r as NO_DEVICE, s as androidDevClientScheme, u as apkDevClientFacts, v as noDeviceDiagnostic, w as resolveVariant, y as parseXmltree } from "./support-Bwvbrq5-.mjs";
4
+ export { NO_DEVICE, NO_FINGERPRINT, androidDevClientScheme, androidFacts, androidSystemImageSetting, androidVariantSetting, apkDevClientFacts, apkPackage, collectorLogFile, androidCommand as default, displayPath, dumpApkManifest, findAapt, formatDuration, isReleaseVariant, killPreviousCollector, lastBuildRecord, noDeviceDiagnostic, parseXmltree, phaseLine, registerAndroid, resolveSystemImage, resolveVariant, runAndroid, runAndroidOperation, shortHash, startCollector };
@@ -1,53 +1,52 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { C as saveConfig, D as withConfigLock$1, E as upsertProject, L as deviceSlotFileKey, R as deviceSlotKey, V as parseDeviceSlotOption, W as validateDeviceSlot, _ as loadConfig$1, j as formatBytes, p as getProject, u as getConcurrencyLimits, w as setDevice, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
3
- import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
1
+ import { n as ownership_claim_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { t as getExecutor } from "./exec-DWhCZDGm.mjs";
3
+ import { t as spawnEntry } from "./spawn-entry-Dcb7sDUz.mjs";
4
4
  import { r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
5
- import { _ as stepTimer, c as launchErrorReport, f as refuseNoProject, g as stepClock, i as formatDuration, l as phaseLine, m as shortHash, n as appReadinessMessage, v as launchErrorPreview } from "./command-output-CDAIFD-8.mjs";
6
- import { B as settingsLayers, C as metroWarmupUrlSetting, F as settingFile, G as detectProviders, H as unknownSettingKeys, J as artifactCachePolicy, L as settingOriginScope, N as resolveCacheProviderConfig, P as resolveSettings, R as settingShapeErrors, T as parseIosRemote, Tt as removeTemporaryEntry, V as tunnelModeSetting, X as optimizationBuildProfile, Y as compilerCacheFallbackMessage, Z as resolveOptimizations, gt as repoRoot, i as SETTING_SHAPE_REMEDY, it as gitCommonDir, j as remoteAndroidSetting, k as publicUrlSetting, l as androidDataPartitionSizeGbSettingError, o as androidAvdConfigSettingError, u as cacheProviderSettingError, wt as makeTemporaryDirectory, x as metroPortSetting } from "./settings-Cfd-0r2d.mjs";
7
- import { d as projectShortcut, n as appProjectProblem, r as detectIsExpo, s as findProjectRoot$1, t as NO_PROJECT_REFUSAL } from "./project-D47Rh0ly.mjs";
8
- import { l as resolveProjectMetro, o as pidExists, u as signalProcessTree } from "./metro-g8NqiJp-.mjs";
9
- import { c as writeWorkspaceState, g as workspaceAgentDeviceDir, i as recordWorkspaceUse, l as emulatorLogFile, r as readWorkspaceState$1, v as workspaceDir, x as workspaceLogsDir$1 } from "./workspace-state-DqcCbpE7.mjs";
10
- import { t as budgetGate } from "./budget-CUDz870h.mjs";
11
- import { A as providerDownloadPath, B as captureAssetManifest, D as fingerprintProject, E as fingerprintDiffRecord, F as storedAssetManifest, H as readAssetManifest, L as untrackedMissLine, M as refingerprintAfterMutation, N as resolveBuild, O as inputsChangedDuringBuild, P as storeBuild, R as untrackedNativeFiles, S as configInputsChanged, T as filesystemBuildCapability, V as compareAssetManifests, _ as waitForBuild, b as buildCacheKey, g as releaseBuildLock, j as providerUploadOutcome, k as prepareProviderDownloadDir, m as acquireBuildLock, v as waitForSharedBuild, x as changedDuringBuildLine, z as assetDiffReason } from "./device-lease-DpoS36_u.mjs";
12
- import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-41v_d2x9.mjs";
13
- import { m as writeWorkspaceLaunch, n as MODE_EXPO, t as MODE_BARE } from "./state-BKa8wNN5.mjs";
14
- import { $ as trimAndroidCaches, A as listAvds, B as ownedAvdSystemImage, D as hostSystemImageArch, F as ownedAvdDeviceProfile, G as probeEmulatorSerial, I as ownedAvdDirectory, J as resolveOwnedAvdSerial, K as profileNeedsFoldFeature, O as listAdbDevices, Q as systemImageSupportsFold, T as findBuildTool, U as physicalDeviceModel, W as pickDefaultSystemImage, Y as resolvePhysicalDevice, a as androidDeviceAbi, ft as parkedMaxSetting, i as androidDataFreeBytes, j as listInstalledSystemImages, k as listAvdDeviceProfiles, q as resetAdoptedAvd, r as androidBuildToolName, rt as configuredAndroidEmulatorApp, t as DEFAULT_AVD_DEVICE_PROFILE, tt as waitForBoot, u as androidToolCwd } from "./android-B_YNZy61.mjs";
15
- import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-Cnz8CPnl.mjs";
16
- import { m as teardownOwnedAvd } from "./ownership-BMLqyBgJ.mjs";
17
- import { _ as statsProjectKey$1, c as recordFinishedBuild, d as createRunRecorder, f as readRunEstimates, g as recordRunStats, l as startBuildProgress, m as recordCapacityRefusal, n as NO_BUILD_PROGRESS, u as tapBuildLog } from "./build-progress-D0WkjCPd.mjs";
18
- import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.mjs";
19
- import { E as ccacheActivityLine, T as CCACHE_UNAVAILABLE, b as tailLines, k as resolveCcache, n as androidToolchain, t as androidRequirements, u as HEARTBEAT_INTERVAL_MS, w as CCACHE_NOT_RUN, y as startBuildHeartbeat } from "./toolchain-CdrxgUeA.mjs";
20
- import { t as cleanLine } from "./server-expo-CHTyXD0G.mjs";
21
- import { a as offloadBuild, c as placementLoad, d as OffloadRefusal, f as namedBuildMachine, h as resolveBuildPlacement, i as closeOffload, l as remotePhaseText, n as buildPlacementCandidates, p as parseBuildMachineOption, r as chooseBuildMachine, s as offloadPlacement } from "./client-CxNotugx.mjs";
22
- import { r as hostedMetroSettings } from "./metro-gateway-CHqK0p-a.mjs";
23
- import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-DyRQru0q.mjs";
24
- import { n as DeviceAdmissionRefusal, r as checkDeviceCapacity, t as DEFAULT_DEVICE_SLOT_WAIT_MS } from "./device-capacity-ZC4XSZ4R.mjs";
25
- import { S as restartedAppNote, b as launchAndroidReleaseApp, c as androidDevClientUrl, g as installConflictKind, h as installAndroidApp, r as DEFAULT_METRO_PORT, s as androidAppProcess, t as ADB_INSTALL_TIMEOUT_MS, y as launchAndroidApp } from "./app-install-VPkI5WEg.mjs";
26
- import { C as resolveSystemImage, S as resolveDeviceProfile, T as systemImageRefusal, _ as isReleaseVariant, a as PLATFORM, b as pooledAndroidDevice, c as androidSystemImageSetting, d as apkPackage, f as deviceProfileRefusal, g as foldableImageRefusal, h as findAapt, i as NO_FINGERPRINT, l as androidVariantSetting, m as dumpApkManifest, n as LAUNCH_FAILED, o as androidBuildOptions, p as displayPath, r as NO_DEVICE, s as androidDevClientScheme, t as INSTALL_FAILED, u as apkDevClientFacts, v as noDeviceDiagnostic, w as resolveVariant, x as remoteAvdFlagRefusal, y as parseXmltree } from "./support-WvD__tSV.mjs";
27
- import { t as spawnEntry } from "./spawn-entry-jw19m8Nl.mjs";
28
- import { i as staleNativeDirRefusal, n as recordPrebuild, r as runPrebuild, t as planPrebuild } from "./prebuild-D7RUMkwN.mjs";
29
- import { c as formatDiagnostic, i as buildAndroid, o as productFlavorRefusal, s as readProductFlavors } from "./gradle-bsBRy6i9.mjs";
30
- import { a as VERIFY_TIMEOUT_MS, d as verifyAndroidReleaseLaunch, f as verifyLaunch, i as RELEASE_VERIFY_WAIT_MS, l as unattributedLaunchLines, n as LAUNCH_FATAL, r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING, u as unverifiedLaunchLines } from "./launch-verify-lTnWZikR.mjs";
31
- import { a as acquireRunLease, c as lostRefusal, d as runLease, f as waitFlagConflict, l as parseDeviceWait, o as leaseExpiryText, s as lostLine, t as DEBUG_VERIFY_STEP_MS, u as releaseLeaseOnSignal } from "./device-lease-run-B8YltIH_.mjs";
32
- import { a as writeHostedAndroid, i as writeDevicePlacement, t as readHostedAndroid } from "./ios-state-8L3lgum6.mjs";
33
- import { _ as AvdRecoveryError, a as refusePlan, b as devicePlacementLine, c as skippedMissReason, d as refreshUpdatesManifestFile, g as AvdBootError, h as ensureOwnedDevice, i as printPlan, l as UPDATES_MANIFEST_NAME, m as ensureBooted, n as planFlagRefusal, o as explainBuildMiss, p as didSetUpDevice, r as planPayload, s as fingerprintErrorMissReason, t as planCachedBuild, u as detectEntryFile, v as warmMetro, y as automaticDevicePlacement } from "./build-plan-BJSXzsCL.mjs";
34
- import { n as prepareHostedAndroid, t as placeHostedAndroid } from "./hosted-android-DgU31wY3.mjs";
35
- import { n as nativeRunCommand, r as siblingPlatformSlots, t as launchSlotScope } from "./slot-launch-DmjBmpQV.mjs";
36
- 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-Ci4DQYYj.mjs";
37
- import { a as ensureMetroReachable, c as remoteAndroidDeps, n as binOnPath, o as ensureRemoteBootOwned, u as resolveRemoteContext, w as ownedSessionName } from "./device-remote-hFSkpfQu.mjs";
38
- import { d as runCancellationSignal, f as withNativeBuildRun, r as cancelledFailure, u as runCancellation } from "./native-run-D1Q56rAD.mjs";
39
- import { a as isEasBuildFailure, o as planEasDevelopmentBuild, s as resolveEasDevelopmentBuild } from "./simslim-BWNrc3M3.mjs";
40
- import { c as startDevServer, m as detectAppIds, p as detectAndroidPackage } from "./start-BHIersSm.mjs";
41
- import { i as launchOutcomeRecord, n as ensureWorkspaceStorageSafely, r as isPhysicalDeviceRequest, t as ensureDevServer } from "./native-runtime-YhaSxP4v.mjs";
42
- import { t as selectFromPool } from "./device-pool-3o7s5lo4.mjs";
43
- import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-C-NMnhrk.mjs";
44
- import { n as resolveAndroidCas, r as resolveAndroidCompilerCache } from "./android-cas-ZFpjJBL_.mjs";
5
+ import { _ as stepTimer, c as launchErrorReport, f as refuseNoProject, g as stepClock, i as formatDuration, l as phaseLine, m as shortHash, n as appReadinessMessage, v as launchErrorPreview } from "./command-output-CBtzyIgF.mjs";
6
+ import { C as saveConfig, D as withConfigLock$1, E as upsertProject, L as deviceSlotFileKey, R as deviceSlotKey, V as parseDeviceSlotOption, W as validateDeviceSlot, _ as loadConfig$1, j as formatBytes, p as getProject, u as getConcurrencyLimits, w as setDevice, z as deviceSlotPlatforms } from "./config-CgYMahz7.mjs";
7
+ import { D as makeTemporaryDirectory, O as removeTemporaryEntry, s as gitCommonDir, y as repoRoot } from "./worktree-CsKYexJM.mjs";
8
+ import { d as projectShortcut, i as detectIsExpo, n as appProjectProblem, s as findProjectRoot$1, t as NO_PROJECT_REFUSAL } from "./project-C3ChRLZy.mjs";
9
+ import { l as resolveProjectMetro, o as pidExists, u as signalProcessTree } from "./metro-Dh8T4Ytd.mjs";
10
+ import { c as writeWorkspaceState, g as workspaceAgentDeviceDir, i as recordWorkspaceUse, l as emulatorLogFile, r as readWorkspaceState$1, v as workspaceDir, x as workspaceLogsDir$1 } from "./workspace-state-C59dqG-Z.mjs";
11
+ import { F as recordFinishedBuild, Ft as MODE_BARE, I as startBuildProgress, It as MODE_EXPO, Jt as writeWorkspaceLaunch, L as tapBuildLog, O as NO_BUILD_PROGRESS, R as createRunRecorder, U as recordRunStats, V as recordCapacityRefusal, W as statsProjectKey$1, t as budgetGate, z as readRunEstimates } from "./budget-DIhL2BKx.mjs";
12
+ import { E as teardownOwnedAvd, N as releaseBuildSlot, j as acquireBuildSlot } from "./workspaces-xXItD8Dj.mjs";
13
+ import { A as providerDownloadPath, B as captureAssetManifest, D as fingerprintProject, E as fingerprintDiffRecord, F as storedAssetManifest, H as readAssetManifest, L as untrackedMissLine, M as refingerprintAfterMutation, N as resolveBuild, O as inputsChangedDuringBuild, P as storeBuild, R as untrackedNativeFiles, S as configInputsChanged, T as filesystemBuildCapability, V as compareAssetManifests, _ as waitForBuild, b as buildCacheKey, g as releaseBuildLock, j as providerUploadOutcome, k as prepareProviderDownloadDir, m as acquireBuildLock, v as waitForSharedBuild, x as changedDuringBuildLine, z as assetDiffReason } from "./device-lease-Co-EwifR.mjs";
14
+ import { $ as trimAndroidCaches, A as listAvds, B as ownedAvdSystemImage, D as hostSystemImageArch, F as ownedAvdDeviceProfile, G as probeEmulatorSerial, I as ownedAvdDirectory, J as resolveOwnedAvdSerial, K as profileNeedsFoldFeature, O as listAdbDevices, Q as systemImageSupportsFold, T as findBuildTool, U as physicalDeviceModel, W as pickDefaultSystemImage, Y as resolvePhysicalDevice, a as androidDeviceAbi, ft as parkedMaxSetting, i as androidDataFreeBytes, j as listInstalledSystemImages, k as listAvdDeviceProfiles, l as androidSystemImageAbi, q as resetAdoptedAvd, r as androidBuildToolName, rt as configuredAndroidEmulatorApp, t as DEFAULT_AVD_DEVICE_PROFILE, tt as waitForBoot, u as androidToolCwd } from "./android-DC3ZLuw0.mjs";
15
+ import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-Behws_fi.mjs";
16
+ import { A as publicUrlSetting, C as metroWarmupUrlSetting, F as resolveSettings, H as tunnelModeSetting, I as settingFile, K as detectProviders, M as remoteAndroidSetting, P as resolveCacheProviderConfig, Q as resolveOptimizations, R as settingOriginScope, T as parseIosRemote, U as unknownSettingKeys, V as settingsLayers, X as compilerCacheFallbackMessage, Y as artifactCachePolicy, Z as optimizationBuildProfile, i as SETTING_SHAPE_REMEDY, l as androidDataPartitionSizeGbSettingError, o as androidAvdConfigSettingError, u as cacheProviderSettingError, x as metroPortSetting, z as settingShapeErrors } from "./settings-CQyMElEP.mjs";
17
+ import { r as createNdjsonWriter } from "./ndjson-LGZAMsqa.mjs";
18
+ import { _ as resolveBuildPlacement, a as offloadBuild, c as placementLoad, d as setRemoteLogSink, f as OffloadRefusal, h as parseBuildMachineOption, i as closeOffload, l as remotePhaseText, m as namedBuildMachine, n as buildPlacementCandidates, r as chooseBuildMachine, s as offloadPlacement } from "./client-DzhapoMB.mjs";
19
+ import { n as DeviceAdmissionRefusal, r as checkDeviceCapacity, t as DEFAULT_DEVICE_SLOT_WAIT_MS } from "./device-capacity-EUOOYr8T.mjs";
20
+ import { A as CCACHE_NOT_RUN, E as tailLines, F as resolveCcache, M as ccacheActivityLine, T as startBuildHeartbeat, g as HEARTBEAT_INTERVAL_MS, j as CCACHE_UNAVAILABLE, n as androidToolchain, t as androidRequirements } from "./toolchain-Bnyd_K3A.mjs";
21
+ import { t as cleanLine } from "./server-expo-DoBHhuzK.mjs";
22
+ import { r as hostedMetroSettings } from "./metro-gateway-CEUw9RMH.mjs";
23
+ import { a as writeHostedAndroid, i as writeDevicePlacement, t as readHostedAndroid } from "./ios-state-B2pES6Vs.mjs";
24
+ import { _ as AvdBootError, a as printPlan, b as automaticDevicePlacement, c as fingerprintErrorMissReason, d as detectEntryFile, f as refreshUpdatesManifestFile, g as ensureOwnedDevice, h as ensureBooted, i as planPayload, l as skippedMissReason, m as didSetUpDevice, n as planCachedBuild, o as refusePlan, r as planFlagRefusal, s as explainBuildMiss, t as planHostedDevice, u as UPDATES_MANIFEST_NAME, v as AvdRecoveryError, x as devicePlacementLine, y as warmMetro } from "./plan-placement-CtxkCO54.mjs";
25
+ import { n as devicePlacementRecord, t as buildPlacementRecord } from "./placement-log-BcPhRwR4.mjs";
26
+ import { n as prepareHostedAndroid, t as placeHostedAndroid } from "./hosted-android-Bv285t_t.mjs";
27
+ import { S as restartedAppNote, b as launchAndroidReleaseApp, c as androidDevClientUrl, g as installConflictKind, h as installAndroidApp, r as DEFAULT_METRO_PORT, s as androidAppProcess, t as ADB_INSTALL_TIMEOUT_MS, y as launchAndroidApp } from "./app-install-D-PUENug.mjs";
28
+ import { n as nativeRunCommand, r as siblingPlatformSlots, t as launchSlotScope } from "./slot-launch-C1nqh4mu.mjs";
29
+ import { a as VERIFY_TIMEOUT_MS, d as verifyAndroidReleaseLaunch, f as verifyLaunch, i as RELEASE_VERIFY_WAIT_MS, l as unattributedLaunchLines, n as LAUNCH_FATAL, r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING, u as unverifiedLaunchLines } from "./launch-verify-hskNcWkZ.mjs";
30
+ 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-BGg7aGIZ.mjs";
31
+ import { C as resolveSystemImage, S as resolveDeviceProfile, T as systemImageRefusal, _ as isReleaseVariant, a as PLATFORM, b as pooledAndroidDevice, d as apkPackage, f as deviceProfileRefusal, g as foldableImageRefusal, i as NO_FINGERPRINT, m as dumpApkManifest, n as LAUNCH_FAILED, o as androidBuildOptions, p as displayPath, r as NO_DEVICE, s as androidDevClientScheme, t as INSTALL_FAILED, v as noDeviceDiagnostic, w as resolveVariant, x as remoteAvdFlagRefusal } from "./support-Bwvbrq5-.mjs";
32
+ import { a as ensureMetroReachable, c as remoteAndroidDeps, n as binOnPath, o as ensureRemoteBootOwned, u as resolveRemoteContext, w as ownedSessionName } from "./device-remote-tHbd1fKy.mjs";
33
+ import { d as runCancellationSignal, f as withNativeBuildRun, r as cancelledFailure, u as runCancellation } from "./native-run-B06FkmZx.mjs";
34
+ import { a as isEasBuildFailure, o as planEasDevelopmentBuild, s as resolveEasDevelopmentBuild } from "./simslim-EHBQ2R4G.mjs";
35
+ import { c as startDevServer, m as detectAppIds, p as detectAndroidPackage } from "./start-D-IeOyoK.mjs";
36
+ import { a as acquireRunLease, c as lostRefusal, d as runLease, f as waitFlagConflict, l as parseDeviceWait, o as leaseExpiryText, s as lostLine, t as DEBUG_VERIFY_STEP_MS, u as releaseLeaseOnSignal } from "./device-lease-run-w1xhCqIW.mjs";
37
+ import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-D6Se7gg_.mjs";
38
+ import { i as launchOutcomeRecord, n as ensureWorkspaceStorageSafely, r as isPhysicalDeviceRequest, t as ensureDevServer } from "./native-runtime-D_603JiC.mjs";
39
+ import { t as selectFromPool } from "./device-pool-F5WvRPtD.mjs";
40
+ import { i as staleNativeDirRefusal, n as recordPrebuild, r as runPrebuild, t as planPrebuild } from "./prebuild-juCyreaR.mjs";
41
+ import { c as formatDiagnostic, i as buildAndroid, o as productFlavorRefusal, s as readProductFlavors } from "./gradle-C6GKnunQ.mjs";
42
+ import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-BRXOqDzG.mjs";
43
+ import { n as resolveAndroidCas, r as resolveAndroidCompilerCache } from "./android-cas-BBFyr9CA.mjs";
45
44
  import { constants, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync } from "node:fs";
46
45
  import { basename, dirname, isAbsolute, join } from "node:path";
47
- import { workspaceId } from "@stim-cli/core";
48
- import { InvalidArgumentError } from "commander";
49
46
  import { buildDiagnostics, hostedAndroidStatus, machineCapacity, parseMachine } from "@stim-cli/core/state";
50
47
  import chalk from "chalk";
48
+ import { InvalidArgumentError } from "commander";
49
+ import { workspaceId } from "@stim-cli/core";
51
50
  import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
52
51
  //#region src/commands/android/remote.ts
53
52
  async function connectAndroidHosting({ root, slot, machine, release, metroCheck, selectors, read = readHostedAndroid, resolveSerial = resolveOwnedAvdSerial, prepare = prepareHostedAndroid }) {
@@ -93,7 +92,7 @@ async function connectAndroidHosting({ root, slot, machine, release, metroCheck,
93
92
  } };
94
93
  }
95
94
  }
96
- async function selectAndroidPlacement({ target: selected, automatic = automaticDevicePlacement, checkBudget = budgetGate, note, phase, noWait, buildMachine, localSelectors, ...args }) {
95
+ async function selectAndroidPlacement({ target: selected, automatic = automaticDevicePlacement, checkBudget = budgetGate, note, phase, log, fromFlag, noWait, buildMachine, localSelectors, ...args }) {
97
96
  const selectors = selected.kind === "hosted" ? {
98
97
  ...selected.systemImage ? { systemImage: selected.systemImage } : {},
99
98
  ...selected.deviceProfile ? { deviceProfile: selected.deviceProfile } : {}
@@ -130,6 +129,11 @@ async function selectAndroidPlacement({ target: selected, automatic = automaticD
130
129
  noWait
131
130
  });
132
131
  phase("placement:", devicePlacementLine(placed.placement, placed.skipped));
132
+ log?.(devicePlacementRecord({
133
+ platform: "android",
134
+ fromFlag,
135
+ placed
136
+ }));
133
137
  if (placed.target && !args.release && !args.metroCheck) return { failure: {
134
138
  code: "STIM_BAD_ARG",
135
139
  message: "Hosted Debug runs require the local Metro supervisor; --no-metro-check cannot be used.",
@@ -809,7 +813,18 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
809
813
  let hereReason = "no remote Mac is paired";
810
814
  let slotWaitMs;
811
815
  const buildMachine = record.buildMachine ?? "auto";
812
- const fallBack = (reason, line = reason) => {
816
+ const fallBack = (reason, line = reason, info = {}) => {
817
+ writer.write(buildPlacementRecord({
818
+ platform: PLATFORM,
819
+ buildMachine,
820
+ candidates: info.candidates,
821
+ event: "placement_fallback",
822
+ fallback: {
823
+ code: info.code ?? "fallback",
824
+ reason,
825
+ machine: info.machine
826
+ }
827
+ }));
813
828
  if (namedBuildMachine(buildMachine)) throw new OffloadRefusal(buildMachine, reason);
814
829
  record.offloadFallback = reason;
815
830
  phase("build", `${line} -> building here`);
@@ -1142,12 +1157,21 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
1142
1157
  });
1143
1158
  if (!placement.offload) {
1144
1159
  hereReason = placement.reason;
1160
+ writer.write(buildPlacementRecord({
1161
+ platform: PLATFORM,
1162
+ buildMachine,
1163
+ stays: {
1164
+ code: placement.code,
1165
+ reason: placement.reason
1166
+ }
1167
+ }));
1145
1168
  if (mode !== "off") phase("build", `placement: here (${placement.reason})`);
1146
1169
  return null;
1147
1170
  }
1148
1171
  return {
1149
1172
  mode,
1150
1173
  here,
1174
+ code: placement.code,
1151
1175
  reason: placement.reason,
1152
1176
  machines
1153
1177
  };
@@ -1155,6 +1179,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
1155
1179
  const openOffload = { choice: null };
1156
1180
  /** Asks the paired machines once the post-mutation key is known; null builds here. */
1157
1181
  async function chooseMachine(candidate) {
1182
+ let asked = [];
1158
1183
  const choice = await chooseBuildMachine({
1159
1184
  projectRoot: root,
1160
1185
  target: {
@@ -1166,22 +1191,46 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
1166
1191
  here: candidate.here,
1167
1192
  note: (line) => phase("build", chalk.dim(`offload: ${line}`)),
1168
1193
  machines: candidate.machines,
1169
- selected: buildMachine
1194
+ selected: buildMachine,
1195
+ onCandidates: (each) => asked = each
1170
1196
  });
1171
1197
  if (typeof choice === "string") {
1172
1198
  const only = candidate.machines.length === 1 ? candidate.machines[0].machine : null;
1173
1199
  if (only && choice.startsWith(`${only}: `)) fallbackMachine = only;
1174
- fallBack(choice);
1200
+ fallBack(choice, choice, {
1201
+ code: "no-remote-mac-took-it",
1202
+ candidates: asked
1203
+ });
1175
1204
  return null;
1176
1205
  }
1177
1206
  openOffload.choice = choice;
1207
+ writer.write(buildPlacementRecord({
1208
+ platform: PLATFORM,
1209
+ buildMachine,
1210
+ candidates: asked,
1211
+ chose: {
1212
+ machine: choice.machine,
1213
+ reason: `${candidate.reason}${placementLoad(choice)}`
1214
+ }
1215
+ }));
1178
1216
  phase("build", `placement: ${choice.machine} (${candidate.reason}${placementLoad(choice)})`);
1179
1217
  return choice;
1180
1218
  }
1181
1219
  /** Builds on the chosen machine and stores the APK under the post-mutation key; false builds here instead unless a machine was named. */
1182
1220
  async function compileElsewhere(choice, candidate) {
1183
1221
  if (!storeKey || !storeHash) {
1184
- if (namedBuildMachine(buildMachine)) fallBack("the build fingerprint or cache key is unavailable");
1222
+ const reason = "the build fingerprint or cache key is unavailable";
1223
+ if (namedBuildMachine(buildMachine)) fallBack(reason, reason, { machine: choice.machine });
1224
+ else writer.write(buildPlacementRecord({
1225
+ platform: PLATFORM,
1226
+ buildMachine,
1227
+ event: "placement_fallback",
1228
+ fallback: {
1229
+ code: "fallback",
1230
+ reason,
1231
+ machine: choice.machine
1232
+ }
1233
+ }));
1185
1234
  return false;
1186
1235
  }
1187
1236
  const stagingDir = join(workspaceDir(root), "offload", PLATFORM);
@@ -1245,7 +1294,10 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
1245
1294
  if (!outcome.ok || !stored) {
1246
1295
  const why = reason ?? "the APK was not stored";
1247
1296
  fallbackMachine = choice.machine;
1248
- fallBack(`${choice.machine}: ${why}`, `offload failed: ${why}`);
1297
+ fallBack(`${choice.machine}: ${why}`, `offload failed: ${why}`, {
1298
+ code: "offload-failed",
1299
+ machine: choice.machine
1300
+ });
1249
1301
  writer.write({
1250
1302
  src: "build",
1251
1303
  level: "warn",
@@ -2524,20 +2576,49 @@ async function planAndroid(opts, overrides = {}) {
2524
2576
  refusal: eas.missing
2525
2577
  }), json);
2526
2578
  }
2527
- if (target.kind !== "emulator") {
2528
- if (target.kind === "physical") return refuse(planFlagRefusal("--device"));
2529
- return refuse({
2530
- code: "STIM_BAD_ARG",
2531
- message: `android.remote routes this workspace's runs to a ${target.kind === "hosted" ? target.machine : target.backend} device, whose ABI --plan cannot read without a session.`,
2532
- remedy: "Run `stim android` to build for the remote device, or unset android.remote to plan the owned emulator."
2533
- });
2534
- }
2579
+ if (target.kind === "physical") return refuse(planFlagRefusal("--device"));
2580
+ if (target.kind === "remote") return refuse({
2581
+ code: "STIM_BAD_ARG",
2582
+ message: `android.remote is ${target.backend}, whose ABI --plan cannot read without a session.`,
2583
+ remedy: "Run `stim android` to build for the remote device, or unset android.remote to plan the owned emulator."
2584
+ });
2535
2585
  if (build.compilerCache === "cas") return refuse({
2536
2586
  code: "STIM_BAD_ARG",
2537
2587
  message: "The experimental Android compiler CAS keys builds by a toolchain Stim sets up, which --plan does not.",
2538
2588
  remedy: "Run `stim android` to build with the CAS, or set optimizations.android.compilerCache to ccache."
2539
2589
  });
2540
- const image = build.targetAbiOnly && !build.release ? emulatorImage(root, slot, target.systemImage, deps) : { systemImage: null };
2590
+ const abiKeyed = build.targetAbiOnly && !build.release;
2591
+ const localImage = () => abiKeyed ? emulatorImage(root, slot, target.systemImage, deps) : { systemImage: null };
2592
+ let image;
2593
+ let hostedAbi = null;
2594
+ let placement;
2595
+ if (target.kind === "hosted") {
2596
+ const hostedPlan = await planHostedDevice({
2597
+ root,
2598
+ slot,
2599
+ platform: "android",
2600
+ machine: target.machine,
2601
+ selectors: {
2602
+ ...target.systemImage ? { systemImage: target.systemImage } : {},
2603
+ ...target.deviceProfile ? { deviceProfile: target.deviceProfile } : {}
2604
+ },
2605
+ sameKey: (choice) => {
2606
+ if (build.targetAbiOnly && build.release) return !("architecture" in choice && choice.architecture);
2607
+ if (!abiKeyed) return true;
2608
+ const local = localImage();
2609
+ if ("refusal" in local || !("systemImage" in choice)) return null;
2610
+ return androidSystemImageAbi(local.systemImage) === androidSystemImageAbi(choice.systemImage);
2611
+ }
2612
+ });
2613
+ if (hostedPlan.kind === "unknown") return refuse({
2614
+ code: "STIM_BAD_ARG",
2615
+ message: `android.remote is ${target.machine}, and its emulator ABI is unknown here: ${hostedPlan.reason}.`,
2616
+ remedy: "Run `stim android` to build for the remote device, or unset android.remote to plan the owned emulator."
2617
+ });
2618
+ placement = hostedPlan.placement;
2619
+ if (hostedPlan.kind === "hosted" && "architecture" in hostedPlan.choice) hostedAbi = hostedPlan.choice.architecture ?? null;
2620
+ image = hostedPlan.kind === "hosted" ? { systemImage: abiKeyed && "systemImage" in hostedPlan.choice ? hostedPlan.choice.systemImage : null } : localImage();
2621
+ } else image = localImage();
2541
2622
  if ("refusal" in image) return refuse(image.refusal);
2542
2623
  const { abi, runOptions, remoteRunOptions } = androidBuildOptions({
2543
2624
  release: build.release,
@@ -2546,7 +2627,8 @@ async function planAndroid(opts, overrides = {}) {
2546
2627
  variant: build.variant,
2547
2628
  deviceAbi: () => null,
2548
2629
  buildProfile: build.profile,
2549
- targetAbiOnly: build.targetAbiOnly
2630
+ targetAbiOnly: build.targetAbiOnly,
2631
+ hostedAbi
2550
2632
  });
2551
2633
  let fingerprint;
2552
2634
  try {
@@ -2576,7 +2658,10 @@ async function planAndroid(opts, overrides = {}) {
2576
2658
  ...deps,
2577
2659
  note
2578
2660
  });
2579
- printPlan(plan, json);
2661
+ printPlan(placement ? {
2662
+ ...plan,
2663
+ placement
2664
+ } : plan, json);
2580
2665
  }
2581
2666
  //#endregion
2582
2667
  //#region src/commands/android.ts
@@ -2594,42 +2679,48 @@ function registerAndroid(program) {
2594
2679
  refuseNoProject({ json: Boolean(opts.json) });
2595
2680
  return;
2596
2681
  }
2597
- if (!(await withNativeBuildRun(root, {
2598
- command: "android",
2599
- platform: "android",
2600
- slot: opts.slot ?? "default"
2601
- }, async (claim) => {
2602
- recordWorkspaceUse(root);
2603
- const progress = startBuildProgress({
2682
+ if (!(await runAndroidOperation(root, {
2683
+ ...opts,
2684
+ waitConflict: waitFlagConflict(process.argv)
2685
+ })).ok) process.exit(runCancellation() ? 130 : 1);
2686
+ });
2687
+ }
2688
+ function runAndroidOperation(root, opts = {}) {
2689
+ return withNativeBuildRun(root, {
2690
+ command: "android",
2691
+ platform: PLATFORM,
2692
+ slot: opts.slot ?? "default"
2693
+ }, async (claim) => {
2694
+ recordWorkspaceUse(root);
2695
+ const progress = startBuildProgress({
2696
+ root,
2697
+ platform: PLATFORM,
2698
+ slot: opts.slot ?? "default",
2699
+ claim,
2700
+ note: (line) => console.error(chalk.dim(line))
2701
+ });
2702
+ try {
2703
+ return await runAndroid({
2604
2704
  root,
2605
- platform: "android",
2606
- slot: opts.slot ?? "default",
2607
- claim,
2608
- note: (line) => console.error(chalk.dim(line))
2705
+ buildMachine: opts.remoteBuild,
2706
+ slot: opts.slot,
2707
+ easProfile: opts.easProfile,
2708
+ json: Boolean(opts.json),
2709
+ metroCheck: opts.metroCheck !== false,
2710
+ useBuildCache: opts.buildCache !== false,
2711
+ variant: opts.variant ?? null,
2712
+ systemImage: opts.systemImage ?? null,
2713
+ deviceProfile: opts.deviceProfile ?? null,
2714
+ remoteDevice: opts.remote ?? null,
2715
+ device: opts.device ?? null,
2716
+ wait: opts.wait,
2717
+ waitConflict: opts.waitConflict,
2718
+ progress
2609
2719
  });
2610
- try {
2611
- return await runAndroid({
2612
- root,
2613
- buildMachine: opts.remoteBuild,
2614
- slot: opts.slot,
2615
- easProfile: opts.easProfile,
2616
- json: Boolean(opts.json),
2617
- metroCheck: opts.metroCheck !== false,
2618
- useBuildCache: opts.buildCache !== false,
2619
- variant: opts.variant ?? null,
2620
- systemImage: opts.systemImage ?? null,
2621
- deviceProfile: opts.deviceProfile ?? null,
2622
- remoteDevice: opts.remote ?? null,
2623
- device: opts.device ?? null,
2624
- wait: opts.wait,
2625
- waitConflict: waitFlagConflict(process.argv),
2626
- progress
2627
- });
2628
- } finally {
2629
- progress.clear();
2630
- }
2631
- }, { write: (line) => console.error(chalk.dim(phaseLine("lock", line))) })).ok) process.exit(runCancellation() ? 130 : 1);
2632
- });
2720
+ } finally {
2721
+ progress.clear();
2722
+ }
2723
+ }, { write: (line) => console.error(chalk.dim(phaseLine("lock", line))) });
2633
2724
  }
2634
2725
  function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRemoteBackend = null, resolveSettingsFor = resolveSettings, resolveRemoteDeviceContext = resolveRemoteContext, remoteDeviceDeps: makeRemoteDeviceDeps = remoteAndroidDeps, resolveEasBin = resolveEasCliBin, ensureMetroReachable: ensureRemoteMetro = ensureMetroReachable, ensureRemoteBootOwned: ensureRemoteOwned = ensureRemoteBootOwned, detectRemoteProviders = detectProviders, metroCheck = true, useBuildCache = true, easProfile, resolveEasDevelopmentBuild: resolveEasBuild = resolveEasDevelopmentBuild, variant: variantFlag = null, systemImage: systemImageFlag = null, deviceProfile: deviceProfileFlag, device: deviceFlag = null, wait: waitFlag = void 0, waitConflict = false, acquireLease = acquireRunLease, makeRunLease = runLease, selectPool = selectFromPool, onLeaseSignal = releaseLeaseOnSignal, listDevices = listAdbDevices, deviceModel = physicalDeviceModel, deviceAbi = androidDeviceAbi, isEmulatorDevice = probeEmulatorSerial, readApkPackage = (apkPath) => apkPackage(dumpApkManifest(apkPath)), getLimits = getConcurrencyLimits, checkCapacity = checkDeviceCapacity, acquireSlot = acquireBuildSlot, releaseSlot = releaseBuildSlot, ensureDevice = ensureOwnedDevice, listSystemImages = listInstalledSystemImages, listDeviceProfiles, ensureDeviceBooted = ensureBooted, resolveAvdSerial = resolveOwnedAvdSerial, waitForDeviceBoot = waitForBoot, dataFreeBytes, trimCaches, wipeDevice, resolveMetro = resolveProjectMetro, warmMetro: prewarmMetro = warmMetro, startServer = startDevServer, readState = readWorkspaceState$1, pidAlive = pidExists, verifyCollector = verifyCollectorOwnership, verifyLaunched = verifyLaunch, ensureStorage = ensureWorkspaceStorageSafely, fingerprint = fingerprintProject, untracked = untrackedNativeFiles, resolveCached = resolveBuild, storeCached = storeBuild, storedAssets = storedAssetManifest, captureAssets = captureAssetManifest, acquireLock = acquireBuildLock, releaseLock = releaseBuildLock, waitForBuild: waitForBuild$1 = waitForBuild, loadProvider = loadProjectProvider, easAuth = checkEasAuth, resolveRemoteBuild = resolveRemote, uploadRemoteBuild = uploadRemote, resolveCacheProvider = resolveCacheProviderConfig, loadCacheProviderModule = loadCacheProvider, planPrebuildFor = planPrebuild, prebuild = runPrebuild, build = buildAndroid, ccacheFor = resolveCcache, install = installAndroidApp, launch = launchAndroidApp, launchRelease = launchAndroidReleaseApp, verifyReleaseLaunched = verifyAndroidReleaseLaunch, swapApk = swapApkBundle, resolveDevClientScheme = androidDevClientScheme, spawn = (cmd, args, opts) => getExecutor().spawn(cmd, args, opts), kill = (pid, signal) => process.kill(pid, signal), createWriter = createNdjsonWriter, writeLaunch = writeWorkspaceLaunch, writeState = writeWorkspaceState, recordBuild, recordStats = recordRunStats, recordCapacityRefusal: recordRefusal, readEstimates = readRunEstimates, now = Date.now, out = (line) => console.error(line), emit = (line) => console.log(line) } = {}) {
2635
2726
  return {
@@ -2835,6 +2926,7 @@ async function runAndroid(options = {}) {
2835
2926
  slot
2836
2927
  }
2837
2928
  }), progress);
2929
+ setRemoteLogSink((entry) => writer.write(entry));
2838
2930
  const record = {
2839
2931
  fingerprint: null,
2840
2932
  cacheKey: null,
@@ -2989,7 +3081,9 @@ async function runAndroid(options = {}) {
2989
3081
  }),
2990
3082
  checkBudget: options.checkBudget,
2991
3083
  note: out,
2992
- phase
3084
+ phase,
3085
+ log: (entry) => writer.write(entry),
3086
+ fromFlag: commandRemoteBackend !== null
2993
3087
  });
2994
3088
  if ("failure" in hosting) return fail(hosting.failure.code, hosting.failure.message, hosting.failure.remedy);
2995
3089
  const { target, hostedTarget, budget, selectors } = hosting;
@@ -3475,4 +3569,4 @@ async function runAndroid(options = {}) {
3475
3569
  }
3476
3570
  }
3477
3571
  //#endregion
3478
- export { NO_DEVICE, NO_FINGERPRINT, androidDevClientScheme, androidFacts, androidSystemImageSetting, androidVariantSetting, apkDevClientFacts, apkPackage, collectorLogFile, androidCommand as default, displayPath, dumpApkManifest, findAapt, formatDuration, isReleaseVariant, killPreviousCollector, lastBuildRecord, noDeviceDiagnostic, parseXmltree, phaseLine, registerAndroid, resolveSystemImage, resolveVariant, runAndroid, shortHash, startCollector };
3572
+ export { collectorLogFile as a, androidFacts as c, runAndroidOperation as i, lastBuildRecord as l, registerAndroid as n, killPreviousCollector as o, runAndroid as r, startCollector as s, androidCommand as t };
@@ -1,5 +1,5 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { u as androidToolCwd } from "./android-B_YNZy61.mjs";
1
+ import { t as getExecutor } from "./exec-DWhCZDGm.mjs";
2
+ import { u as androidToolCwd } from "./android-DC3ZLuw0.mjs";
3
3
  //#region src/collector/android.ts
4
4
  const LEVEL_BY_LETTER = {
5
5
  V: "debug",
@@ -1,16 +1,16 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { C as saveConfig, D as withConfigLock$1, H as projectDeviceSlots, I as assignSlotDevice, U as removeSlotDevice, _ as loadConfig$1, c as ensureConfig, d as getConfigDir, f as getConfigPath$1, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
3
- import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
- import { s as androidDataPartitionSizeBytes } from "./settings-Cfd-0r2d.mjs";
5
- import { o as pidExists, u as signalProcessTree } from "./metro-g8NqiJp-.mjs";
6
- import { n as readCreatedDevices$1, r as recordCreatedDevice, t as forgetCreatedDevice } from "./created-devices-CpvzBL25.mjs";
7
- import { n as settingDefault, r as stimDesktopInstalled, t as STIM_DESKTOP_OPEN_OPTIONS } from "./stim-desktop-Cnz8CPnl.mjs";
1
+ import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { t as getExecutor } from "./exec-DWhCZDGm.mjs";
3
+ import { C as saveConfig, D as withConfigLock$1, H as projectDeviceSlots, I as assignSlotDevice, U as removeSlotDevice, _ as loadConfig$1, c as ensureConfig, d as getConfigDir, f as getConfigPath$1, z as deviceSlotPlatforms } from "./config-CgYMahz7.mjs";
4
+ import { o as pidExists, u as signalProcessTree } from "./metro-Dh8T4Ytd.mjs";
5
+ import { n as readCreatedDevices$1, r as recordCreatedDevice, t as forgetCreatedDevice } from "./created-devices-DKbeWsiK.mjs";
6
+ import { n as settingDefault, r as stimDesktopInstalled, t as STIM_DESKTOP_OPEN_OPTIONS } from "./stim-desktop-Behws_fi.mjs";
7
+ import { s as androidDataPartitionSizeBytes } from "./settings-CQyMElEP.mjs";
8
8
  import { join } from "node:path";
9
- import { existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
10
- import { homedir, tmpdir } from "os";
11
9
  import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, resolve as resolve$1 } from "path";
12
10
  import { ANDROID_EMULATOR_APPS, getParkedMax, loadConfig, settingDefinition } from "@stim-cli/core/state";
13
11
  import { isDeepStrictEqual } from "node:util";
12
+ import { existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
13
+ import { homedir, tmpdir } from "os";
14
14
  //#region src/devices/sim-pool.ts
15
15
  const POOL_SETTING_REMEDY = "Run `stim guide settings` for the device pool bounds and where it can be set.";
16
16
  function parkedMaxSetting(platform, options = {}) {
@@ -1,12 +1,12 @@
1
- import { d as getConfigDir } from "./config-D0DbpZtM.mjs";
2
- import { r as register } from "./cache-manifest-DnUHBJaU.mjs";
3
- import { u as ensureWorkspaceStorage, w as withDirLock$1 } from "./workspace-state-DqcCbpE7.mjs";
4
- import { createHash, randomUUID } from "node:crypto";
1
+ import { d as getConfigDir } from "./config-CgYMahz7.mjs";
2
+ import { u as ensureWorkspaceStorage, w as withDirLock$1 } from "./workspace-state-C59dqG-Z.mjs";
3
+ import { r as register } from "./cache-manifest-BI7ozfmH.mjs";
5
4
  import { accessSync, constants, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
6
5
  import { join } from "node:path";
7
- import { quotedPath } from "@stim-cli/core";
8
6
  import { isJsonObject, readJsonFile } from "@stim-cli/core/state";
9
7
  import { fileURLToPath, pathToFileURL } from "node:url";
8
+ import { quotedPath } from "@stim-cli/core";
9
+ import { createHash, randomUUID } from "node:crypto";
10
10
  //#region src/engine/android-cas.ts
11
11
  const CAS_TOOLCHAIN_FIELDS = [
12
12
  "clang",