stim 1.18.0 → 1.20.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 (178) hide show
  1. package/README.md +21 -4
  2. package/dist/{activity-BxnkDZDi.mjs → activity-BrGh9EAI.mjs} +3 -3
  3. package/dist/{agent-device-usage-output-CZ66YA4T.mjs → agent-device-usage-output-DVyv_hWj.mjs} +7 -7
  4. package/dist/android-0vv-OruW.mjs +6 -0
  5. package/dist/android-Brx6EKvJ.mjs +1258 -0
  6. package/dist/{android-DC3ZLuw0.mjs → android-BsYNR81W.mjs} +103 -45
  7. package/dist/{android-CBwCNicO.mjs → android-CK3fpEyZ.mjs} +2 -2
  8. package/dist/{android-cas-BBFyr9CA.mjs → android-cas-DCAuXryb.mjs} +9 -5
  9. package/dist/android-cas-compiler.mjs +1 -1
  10. package/dist/api-run.mjs +159 -8
  11. package/dist/{api-D0VtFhqk.mjs → api-zuX3ynYN.mjs} +9 -7
  12. package/dist/api.d.mts +104 -4
  13. package/dist/api.mjs +1 -1
  14. package/dist/{app-install-D-PUENug.mjs → app-install-0LgaQRT7.mjs} +39 -18
  15. package/dist/artifact-CnC0mfov.mjs +807 -0
  16. package/dist/artifact-DClXJNWx.mjs +772 -0
  17. package/dist/artifact-lifecycle-DzPpBYvK.mjs +30 -0
  18. package/dist/{web-DTU0uiWj.mjs → browser-web-CzBmhmgs.mjs} +136 -174
  19. package/dist/{budget-DIhL2BKx.mjs → budget-BONq9vXY.mjs} +3096 -3244
  20. package/dist/build-BixRI6sB.mjs +462 -0
  21. package/dist/build-plan-CP0tckNU.mjs +553 -0
  22. package/dist/{cache-manifest-BI7ozfmH.mjs → cache-manifest-qA3dT2x2.mjs} +1 -1
  23. package/dist/cache-manifest.mjs +1 -1
  24. package/dist/ccache-CjHQ5F8g.mjs +142 -0
  25. package/dist/{chrome-DJnmnS5q.mjs → chrome-Dp2lMLBe.mjs} +1 -1
  26. package/dist/{cli-DNJMwADx.mjs → cli-B2R8wH1F.mjs} +20 -20
  27. package/dist/cli.mjs +1 -1
  28. package/dist/{client-DzhapoMB.mjs → client-BLy1Pw2V.mjs} +172 -165
  29. package/dist/collector-run.d.mts +10 -0
  30. package/dist/collector-run.mjs +7 -7
  31. package/dist/{command-output-CBtzyIgF.mjs → command-output-BjPN99ZB.mjs} +2 -2
  32. package/dist/{config-CgYMahz7.mjs → config-D0LhTY3S.mjs} +13 -13
  33. package/dist/{created-devices-DKbeWsiK.mjs → created-devices-HU_DAb_Q.mjs} +7 -6
  34. package/dist/{dependency-state-xKoOypBm.mjs → dependency-state-B2m8eqsS.mjs} +2 -2
  35. package/dist/{detached-entry-BLfcgc4O.mjs → detached-entry-BXq4fIl-.mjs} +1 -1
  36. package/dist/{dev-client-CLnIH02x.mjs → dev-client-ClATaJoH.mjs} +2 -2
  37. package/dist/{device-D7oCHGWi.mjs → device-B7lsC0K1.mjs} +8 -8
  38. package/dist/device-DVCwAFlX.mjs +727 -0
  39. package/dist/{device-capacity-EUOOYr8T.mjs → device-capacity-Ca_ke1LE.mjs} +25 -15
  40. package/dist/device-host-worker.mjs +38 -18
  41. package/dist/{device-ios-6MewKKnc.mjs → device-ios-CRUqPxKw.mjs} +12 -11
  42. package/dist/{device-lease-Co-EwifR.mjs → device-lease-DHOBXGgS.mjs} +135 -22
  43. package/dist/{device-lease-run-w1xhCqIW.mjs → device-lease-run-CvDeHTkG.mjs} +4 -4
  44. package/dist/{device-pool-F5WvRPtD.mjs → device-pool-L2-5GxMy.mjs} +3 -3
  45. package/dist/{device-remote-tHbd1fKy.mjs → device-remote-Bmg42063.mjs} +17 -69
  46. package/dist/doctor-C2MSuMMg.mjs +515 -0
  47. package/dist/{doctor-B9ckYUoU.mjs → doctor-DPAqhVCK.mjs} +334 -721
  48. package/dist/doctor-watchman-CB4NnVZm.mjs +260 -0
  49. package/dist/{simslim-EHBQ2R4G.mjs → eas-build-Dw_ec2Km.mjs} +6 -152
  50. package/dist/{stim-installations-aR_WsNOA.mjs → eas-session-ledger-DZlC-Fq3.mjs} +57 -4
  51. package/dist/{error-diagnostics-BRXOqDzG.mjs → error-diagnostics-Du7I_GuY.mjs} +17 -12
  52. package/dist/errors-DzkMYh8Q.mjs +205 -0
  53. package/dist/{exec-DWhCZDGm.mjs → exec-B81Qodjn.mjs} +69 -15
  54. package/dist/{gc-Dh6mXOKC.mjs → gc-PWp34zGb.mjs} +23 -22
  55. package/dist/{gradle-C6GKnunQ.mjs → gradle-C2OEKCCI.mjs} +50 -125
  56. package/dist/{guide-tm6igAaa.mjs → guide-Dai9lphv.mjs} +862 -538
  57. package/dist/{hosted-android-Bv285t_t.mjs → hosted-android-DQ-Agsd8.mjs} +5 -5
  58. package/dist/{hosted-client-C-rMtppF.mjs → hosted-client-Io01s5By.mjs} +9 -3
  59. package/dist/{hosted-ios-D5mNmtj4.mjs → hosted-ios-DIxFnYp-.mjs} +3 -3
  60. package/dist/{hosted-logs-DN8Iz4mJ.mjs → hosted-logs-DP-ABU_M.mjs} +8 -8
  61. package/dist/{hosted-macos-B-wEjlwG.mjs → hosted-macos-Aziw9T_9.mjs} +7 -6
  62. package/dist/{hosted-native-DeeOG8xZ.mjs → hosted-native-BdAQi21M.mjs} +23 -16
  63. package/dist/{idle-shutdown-jjtxAn5C.mjs → idle-shutdown-DdMqoSUB.mjs} +7 -7
  64. package/dist/ios-BZ9dg2Fr.mjs +9 -0
  65. package/dist/{ios-B3SbOWdw.mjs → ios-CXLIUxXJ.mjs} +180 -24
  66. package/dist/ios-CZuv6gRv.mjs +1112 -0
  67. package/dist/{ios-Bvbf0SGY.mjs → ios-VGvNdsv2.mjs} +1 -1
  68. package/dist/{ios-device-Bn7jWDB1.mjs → ios-device-B1B14xND.mjs} +1 -1
  69. package/dist/{ios-device-vOaByMhw.mjs → ios-device-BreZOpM0.mjs} +3 -3
  70. package/dist/ios-project-B9drt_aY.mjs +197 -0
  71. package/dist/{ios-state-B2pES6Vs.mjs → ios-state-CjrL2qv-.mjs} +1 -1
  72. package/dist/js-swap-C8icNYIY.mjs +432 -0
  73. package/dist/launch-DxH0THLP.mjs +745 -0
  74. package/dist/launch-YrbqaIFj.mjs +1530 -0
  75. package/dist/{launch-verify-hskNcWkZ.mjs → launch-verify-BzRdbuO1.mjs} +26 -7
  76. package/dist/{logs-1Ylc1Ri6.mjs → logs-DHaRNfg7.mjs} +11 -11
  77. package/dist/macos-C0iljhsN.mjs +2 -0
  78. package/dist/{macos-DF6C7wUU.mjs → macos-V_3Ui7g4.mjs} +125 -155
  79. package/dist/macos-run.mjs +1 -1
  80. package/dist/maintenance-run.mjs +13 -10
  81. package/dist/{metro-Dh8T4Ytd.mjs → metro-D_j9sN-B.mjs} +209 -43
  82. package/dist/{metro-gateway-CEUw9RMH.mjs → metro-gateway-BBJldOdi.mjs} +2 -2
  83. package/dist/miss-reason-BPAzZS0r.mjs +238 -0
  84. package/dist/{named-ports-BFIz1Shz.mjs → named-ports-C2lvoPsb.mjs} +12 -5
  85. package/dist/native-android-lHzaew7r.mjs +226 -0
  86. package/dist/native-gradle-inputs-BQnIdAlL.mjs +356 -0
  87. package/dist/{native-runtime-D_603JiC.mjs → native-runtime-BRmWL7ru.mjs} +9 -8
  88. package/dist/native-xcode-inputs-eutS19jU.mjs +307 -0
  89. package/dist/native-xcode-ios-DP3cHLwu.mjs +434 -0
  90. package/dist/{ndjson-LGZAMsqa.mjs → ndjson-GOnbyvso.mjs} +6 -6
  91. package/dist/offload-worker.d.mts +12 -6
  92. package/dist/offload-worker.mjs +409 -73
  93. package/dist/{ownership-D6Se7gg_.mjs → ownership-DXzMMSx6.mjs} +2 -2
  94. package/dist/{placement-log-BcPhRwR4.mjs → placement-log-reFbjqEr.mjs} +7 -3
  95. package/dist/plan-BDk2AeY_.mjs +260 -0
  96. package/dist/plan-placement-D_YYlE6N.mjs +73 -0
  97. package/dist/{ports-BLTtfBq-.mjs → ports-jzvAJXXL.mjs} +3 -3
  98. package/dist/{prebuild-juCyreaR.mjs → prebuild-Bw44QxVh.mjs} +4 -4
  99. package/dist/{preview-D5V07Nrl.mjs → preview-BaBJ72Al.mjs} +51 -34
  100. package/dist/project-B8_duAzW.mjs +1067 -0
  101. package/dist/project-plan-BPwbLwQu.mjs +10 -0
  102. package/dist/{pull-request-u4vp7zmK.mjs → pull-request-vOhLNmIb.mjs} +1 -1
  103. package/dist/pull-requests.mjs +1 -1
  104. package/dist/react-native-android-chm1DMlA.mjs +921 -0
  105. package/dist/react-native-build-oAw6gQeW.mjs +239 -0
  106. package/dist/react-native-doctor-DYXoh-6I.mjs +78 -0
  107. package/dist/react-native-ios-SCTK_CT5.mjs +595 -0
  108. package/dist/{recordings-BAWltsVt.mjs → recordings-Bk-VivUV.mjs} +1 -1
  109. package/dist/{reload-DvyWzVjJ.mjs → reload-D34GkY78.mjs} +17 -14
  110. package/dist/remote-BU9UyhCJ.mjs +192 -0
  111. package/dist/{remote-cache-BGg7aGIZ.mjs → remote-cache-Bsts6f6Y.mjs} +5 -5
  112. package/dist/result-BDoRk1k3.mjs +186 -0
  113. package/dist/{run-vhtDgpHO.mjs → run-qvff78hv.mjs} +3 -3
  114. package/dist/{server-bare-B9E7S1aR.mjs → server-bare-dWjAarki.mjs} +6 -6
  115. package/dist/server-command-7XIKlWtO.mjs +69 -0
  116. package/dist/{server-expo-DoBHhuzK.mjs → server-expo-CsYu2aKL.mjs} +107 -82
  117. package/dist/server-expo-DV43vep4.mjs +2 -0
  118. package/dist/settings-Bbztm4oV.mjs +1503 -0
  119. package/dist/{settings-Byp2AQ_E.mjs → settings-CT3E71-i.mjs} +6 -7
  120. package/dist/{state-lKh8shBp.d.mts → settings-vk3mJGSm.d.mts} +4 -2
  121. package/dist/settings.schema.json +156 -6
  122. package/dist/simslim-ChMfe8rg.mjs +149 -0
  123. package/dist/{slot-launch-C1nqh4mu.mjs → slot-launch-B9hn96wd.mjs} +6 -6
  124. package/dist/{stage-g_WqWeaE.mjs → stage-BNKKdDNc.mjs} +6 -3
  125. package/dist/start-CbQCSnXQ.mjs +2 -0
  126. package/dist/{start-D-IeOyoK.mjs → start-CdvcTaXr.mjs} +113 -77
  127. package/dist/{state-xnqpx3AD.mjs → state-Boo0IMJq.mjs} +4 -2
  128. package/dist/{state-BG-aAk4k.mjs → state-DI84A-uu.mjs} +29 -10
  129. package/dist/{stats-DY6ekz8F.mjs → stats-BIIDrjA8.mjs} +6 -6
  130. package/dist/{status-BEeL6IKz.mjs → status-BDGHwkCs.mjs} +3 -1
  131. package/dist/{status-DCjZeE-K.mjs → status-DARMkc9q.mjs} +80 -294
  132. package/dist/status-watch-DvmI3SmN.mjs +296 -0
  133. package/dist/{stim-desktop-Behws_fi.mjs → stim-desktop-8GQX2OV8.mjs} +1 -1
  134. package/dist/{stop-DIJtD2GL.mjs → stop-CCZS-C9m.mjs} +2 -2
  135. package/dist/stop-WQPQHLuy.mjs +72 -0
  136. package/dist/{stop-BTjs3xbc.mjs → stop-iNemHsJC.mjs} +81 -33
  137. package/dist/supervisor-run.d.mts +7 -2
  138. package/dist/supervisor-run.mjs +32 -20
  139. package/dist/{support-Bwvbrq5-.mjs → support-CnE_SIjt.mjs} +17 -7
  140. package/dist/{support-BaayeYCf.mjs → support-DCco5fut.mjs} +61 -8
  141. package/dist/swiftpm-macos-afeNUExW.mjs +119 -0
  142. package/dist/{native-run-B06FkmZx.mjs → tailnet-CoJUb2tB.mjs} +113 -6
  143. package/dist/toolchain-BIupIro1.mjs +2 -0
  144. package/dist/{toolchain-Bnyd_K3A.mjs → toolchain-DqyDXs30.mjs} +225 -315
  145. package/dist/{trigger-CJMhwXe8.mjs → trigger-C0MWBi8O.mjs} +3 -3
  146. package/dist/trigger-SHLOWJ4O.mjs +2 -0
  147. package/dist/{warm-progress-CjB-MP6H.mjs → warm-progress-C3ASD6z5.mjs} +134 -37
  148. package/dist/{status-Dcr0Qexd.mjs → watchman-CQl51YZu.mjs} +75 -7
  149. package/dist/web-B3eldHFH.mjs +123 -0
  150. package/dist/web-BLXpOe6b.mjs +2 -0
  151. package/dist/web-run.mjs +10 -10
  152. package/dist/{workspace-state-C59dqG-Z.mjs → workspace-state-CSIFuuDa.mjs} +33 -33
  153. package/dist/{workspaces-xXItD8Dj.mjs → workspaces-D2xSHBDY.mjs} +1297 -1211
  154. package/dist/{worktree-BSYlofkq.mjs → worktree-BxDqaTev.mjs} +102 -63
  155. package/dist/worktree-q7_OZNY0.mjs +2 -0
  156. package/package.json +8 -6
  157. package/shim/bundle-response.cjs +2 -1
  158. package/shim/bundle-response.d.cts +1 -1
  159. package/shim/expo-metro-config.cjs +1 -1
  160. package/shim/native-android.gradle +101 -0
  161. package/dist/android-BKA16DTf.mjs +0 -4
  162. package/dist/android-BvwuMUJ9.mjs +0 -3572
  163. package/dist/guide-status-Bhf9SJXc.mjs +0 -125
  164. package/dist/ios-CPdkoKaa.mjs +0 -3953
  165. package/dist/ios-DNpdygiv.mjs +0 -7
  166. package/dist/macos-Ck7OBMNY.mjs +0 -2
  167. package/dist/metro-store-BAHFcJm6.mjs +0 -106
  168. package/dist/plan-placement-CtxkCO54.mjs +0 -1859
  169. package/dist/project-C3ChRLZy.mjs +0 -212
  170. package/dist/projects-DD7dBTCj.mjs +0 -92
  171. package/dist/server-expo-Db9HBUoa.mjs +0 -2
  172. package/dist/settings-CQyMElEP.mjs +0 -701
  173. package/dist/start-NTc1Ifqs.mjs +0 -2
  174. package/dist/stop-BGhKMOuH.mjs +0 -48
  175. package/dist/trigger-DWAq9hkw.mjs +0 -2
  176. package/dist/web-B6_bFDn7.mjs +0 -2
  177. package/dist/worktree-BJfnU6r5.mjs +0 -2
  178. package/dist/worktree-CsKYexJM.mjs +0 -753
@@ -1,13 +1,23 @@
1
- import { t as getExecutor } from "./exec-DWhCZDGm.mjs";
1
+ import { t as getExecutor } from "./exec-B81Qodjn.mjs";
2
+ import { O as writeConfigSetting } from "./config-D0LhTY3S.mjs";
3
+ import { d as androidLayoutSetting, y as iosProjectDirSetting } from "./settings-Bbztm4oV.mjs";
4
+ import { D as fingerprintProject, M as refingerprintAfterMutation } from "./device-lease-DHOBXGgS.mjs";
5
+ import { w as workspaceLogsDir, x as workspaceDir } from "./workspace-state-CSIFuuDa.mjs";
2
6
  import { n as stripAnsi } from "./process-output-Fpl8PYi8.mjs";
3
- import { D as fingerprintProject, M as refingerprintAfterMutation } from "./device-lease-Co-EwifR.mjs";
4
- import { F as resolveCcache, _ as buildIos, j as CCACHE_UNAVAILABLE, l as podsAreStale, o as workerToolchain, p as runPodInstall, s as manifestDigest, u as readPodState } from "./toolchain-Bnyd_K3A.mjs";
5
- import { n as recordPrebuild, r as runPrebuild, t as planPrebuild } from "./prebuild-juCyreaR.mjs";
6
- import { a as gradlewPath, i as buildAndroid, r as assembleTaskFor } from "./gradle-C6GKnunQ.mjs";
7
- import { d as podAction } from "./support-BaayeYCf.mjs";
8
- import { t as logLines } from "./run-vhtDgpHO.mjs";
9
- import { r as stageBundle, t as resolveBundleExtras } from "./stage-g_WqWeaE.mjs";
10
- import { chmodSync, constants, copyFileSync, cpSync, createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
7
+ import { d as projectRegistry, h as selectNativeXcodeProject } from "./project-B8_duAzW.mjs";
8
+ import { o as parseNdjsonText } from "./ndjson-GOnbyvso.mjs";
9
+ import { n as CCACHE_UNAVAILABLE, o as resolveCcache } from "./ccache-CjHQ5F8g.mjs";
10
+ import { i as assembleTaskFor } from "./gradle-C2OEKCCI.mjs";
11
+ import { f as podsAreStale, g as runPodInstall, p as readPodState, u as workerToolchain } from "./toolchain-DqyDXs30.mjs";
12
+ import { a as validateGradleManifest, d as verifyNativeTransfer, m as manifestDigest, n as gradleStateDirectories, o as verifyGradleTransfer, r as nativeGradleOutputs, t as gradleOffloadInputs } from "./native-gradle-inputs-BQnIdAlL.mjs";
13
+ import { n as buildAndroid, o as gradlewPath, r as buildIos } from "./react-native-build-oAw6gQeW.mjs";
14
+ import { n as buildAndroidOperation, t as buildIosOperation } from "./build-BixRI6sB.mjs";
15
+ import { n as recordPrebuild, r as runPrebuild, t as planPrebuild } from "./prebuild-Bw44QxVh.mjs";
16
+ import { f as podAction } from "./support-DCco5fut.mjs";
17
+ import { r as stageBundle, t as resolveBundleExtras } from "./stage-BNKKdDNc.mjs";
18
+ import { t as logLines } from "./run-qvff78hv.mjs";
19
+ import { r as nativeXcodeMetadataDirectories } from "./native-xcode-inputs-eutS19jU.mjs";
20
+ import { chmodSync, constants, copyFileSync, cpSync, createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
11
21
  import { basename, dirname, join } from "node:path";
12
22
  import { createHash } from "node:crypto";
13
23
  //#region src/offload/worker.ts
@@ -79,12 +89,6 @@ function ensureParents(root, path) {
79
89
  mkdirSync(current);
80
90
  }
81
91
  }
82
- /**
83
- * Makes `src` hold exactly the manifest's files: a file the manifest names is rewritten unless this process
84
- * wrote that same content there and nothing touched it since; a file that git would list as untracked and not
85
- * ignored, and that the manifest does not name, is deleted, as is one the previous manifest named. Ignored
86
- * files (dependencies, generated native projects) stay, and the fingerprint check covers them.
87
- */
88
92
  function materialize(job, src) {
89
93
  const recordFile = join(job.area, "mirror.json");
90
94
  let previous = {};
@@ -106,10 +110,52 @@ function materialize(job, src) {
106
110
  "/dev/null"
107
111
  ], { timeoutMs: 3e4 });
108
112
  }
113
+ getExecutor().runFile("git", [
114
+ "-C",
115
+ src,
116
+ "read-tree",
117
+ "--empty"
118
+ ], { timeoutMs: 3e4 });
119
+ let retained = [];
120
+ if (job.native) {
121
+ if (job.native.provider === "gradle") {
122
+ const declaration = gradleOffloadInputs(job.native.inputs);
123
+ validateGradleManifest(job.project, declaration, job.manifest);
124
+ let receipt = {};
125
+ try {
126
+ receipt = JSON.parse(readFileSync(join(job.area, "gradle-outputs.json"), "utf8"));
127
+ } catch {}
128
+ const allowed = [...declaration.outputs, ...gradleStateDirectories(job.project)];
129
+ if (receipt.project === job.project && Array.isArray(receipt.directories)) retained = allowed.filter((path) => receipt.directories.includes(path));
130
+ retained = nativeGradleOutputs(src, {
131
+ complete: true,
132
+ ignored: [],
133
+ outputs: retained
134
+ }, retained.map((path) => join(src, path)));
135
+ }
136
+ const keep = new Set(job.manifest.flatMap((entry) => {
137
+ const parts = entry.path.split("/");
138
+ return parts.map((_, index) => parts.slice(0, index + 1).join("/"));
139
+ }));
140
+ const removeUnknown = (directory, prefix) => {
141
+ for (const name of readdirSync(directory)) {
142
+ if (!prefix && name === ".git") continue;
143
+ const path = prefix ? `${prefix}/${name}` : name;
144
+ const absolute = join(directory, name);
145
+ if (retained.includes(path)) continue;
146
+ if (!keep.has(path) && !retained.some((output) => output.startsWith(`${path}/`))) rmSync(absolute, {
147
+ recursive: true,
148
+ force: true
149
+ });
150
+ else if (lstatSync(absolute).isDirectory()) removeUnknown(absolute, path);
151
+ }
152
+ };
153
+ removeUnknown(src, "");
154
+ }
109
155
  const next = {};
110
156
  const wanted = /* @__PURE__ */ new Set();
111
157
  let written = 0;
112
- for (const entry of job.manifest) {
158
+ for (const entry of job.manifest.toSorted((a, b) => a.path.split("/").length - b.path.split("/").length)) {
113
159
  wanted.add(entry.path);
114
160
  const target = join(src, entry.path);
115
161
  const known = previous[entry.path];
@@ -117,7 +163,16 @@ function materialize(job, src) {
117
163
  try {
118
164
  stat = lstatSync(target);
119
165
  } catch {}
120
- if (known && stat && known.sha256 === entry.sha256 && known.kind === entry.kind && stat.mtimeMs === known.mtimeMs && stat.size === known.size) {
166
+ if (entry.kind === "directory" && stat?.isDirectory()) {
167
+ next[entry.path] = {
168
+ sha256: entry.sha256,
169
+ kind: entry.kind,
170
+ mtimeMs: stat.mtimeMs,
171
+ size: stat.size
172
+ };
173
+ continue;
174
+ }
175
+ if (known && stat && (!job.native || (entry.kind === "link" ? stat.isSymbolicLink() : stat.isFile()) && createHash("sha256").update(entry.kind === "link" ? readlinkSync(target) : readFileSync(target)).digest("hex") === entry.sha256 && (entry.kind === "link" || (stat.mode & 73) === (entry.kind === "exec" ? 73 : 0))) && known.sha256 === entry.sha256 && known.kind === entry.kind && stat.mtimeMs === known.mtimeMs && stat.size === known.size) {
121
176
  next[entry.path] = known;
122
177
  continue;
123
178
  }
@@ -127,7 +182,8 @@ function materialize(job, src) {
127
182
  force: true
128
183
  });
129
184
  const blob = blobPath(job.blobs, entry.sha256);
130
- if (entry.kind === "link") symlinkSync(readFileSync(blob, "utf8"), target);
185
+ if (entry.kind === "directory") mkdirSync(target);
186
+ else if (entry.kind === "link") symlinkSync(readFileSync(blob, "utf8"), target);
131
187
  else {
132
188
  copyFileSync(blob, target, constants.COPYFILE_FICLONE);
133
189
  chmodSync(target, entry.kind === "exec" ? 493 : 420);
@@ -141,7 +197,22 @@ function materialize(job, src) {
141
197
  };
142
198
  written += 1;
143
199
  }
144
- const untracked = getExecutor().runFile("git", [
200
+ if (job.native) {
201
+ const indexed = job.manifest.filter((entry) => entry.kind !== "directory").map((entry) => `${entry.path}\0`);
202
+ if (indexed.length) getExecutor().runFile("git", [
203
+ "-C",
204
+ src,
205
+ "update-index",
206
+ "--add",
207
+ "--info-only",
208
+ "-z",
209
+ "--stdin"
210
+ ], {
211
+ input: indexed.join(""),
212
+ timeoutMs: 12e4
213
+ });
214
+ }
215
+ const untracked = job.native ? [] : getExecutor().runFile("git", [
145
216
  "-C",
146
217
  src,
147
218
  "ls-files",
@@ -154,9 +225,12 @@ function materialize(job, src) {
154
225
  }).split("\0").filter(Boolean);
155
226
  let removed = 0;
156
227
  for (const path of /* @__PURE__ */ new Set([...untracked, ...Object.keys(previous)])) {
157
- if (wanted.has(path) || !realParents(src, path)) continue;
228
+ if (wanted.has(path) || retained.some((output) => output === path || output.startsWith(`${path}/`)) || !realParents(src, path)) continue;
158
229
  try {
159
- rmSync(join(src, path), { force: true });
230
+ rmSync(join(src, path), {
231
+ force: true,
232
+ ...job.native ? { recursive: true } : {}
233
+ });
160
234
  removed += 1;
161
235
  } catch {}
162
236
  }
@@ -287,9 +361,62 @@ async function build(job) {
287
361
  note("sync", `${job.manifest.length} files, ${mirrored.written} written, ${mirrored.removed} removed`);
288
362
  let fingerprint;
289
363
  let compiled;
290
- if (job.platform === "macos") {
364
+ if (job.native?.provider === "gradle") {
365
+ if (job.platform !== "android" || job.expectedFingerprint !== null) return failed("unsupported-provider", "Native Gradle needs an Android request with no reusable artifact identity.");
366
+ const selected = projectRegistry.selectAndroid(root);
367
+ if ("problem" in selected || selected.id !== "native-android") return failed("provider-mismatch", "The transferred project did not select the native Android integration.");
368
+ if (!verifyGradleTransfer(src, job.project, job.native.inputs, job.manifest, job.native.sourceDigest)) return failed("source-mismatch", "The materialized native inputs do not match the source transfer.");
369
+ compiled = await compileNativeAndroid(job, root, log, time);
370
+ if (!compiled.ok) return failed(compiled.code, compiled.message);
371
+ try {
372
+ const model = JSON.parse(readFileSync(join(workspaceDir(root), "gradle-build", `native-apk-${encodeURIComponent(job.android?.variant ?? "debug")}.json`), "utf8"));
373
+ const outputs = nativeGradleOutputs(src, job.native.inputs, model.buildDirectories);
374
+ if (!verifyGradleTransfer(src, job.project, job.native.inputs, job.manifest, job.native.sourceDigest)) throw new Error("The native source changed while the worker compiled it.");
375
+ const directories = [...outputs, ...gradleStateDirectories(job.project).filter((path) => lstatSync(join(src, path), { throwIfNoEntry: false })?.isDirectory())];
376
+ const receipt = join(job.area, "gradle-outputs.json");
377
+ const tmp = `${receipt}.${process.pid}.tmp`;
378
+ writeFileSync(tmp, JSON.stringify({
379
+ project: job.project,
380
+ directories
381
+ }));
382
+ renameSync(tmp, receipt);
383
+ } catch (error) {
384
+ if (compiled.temporary) rmSync(compiled.temporary, {
385
+ recursive: true,
386
+ force: true
387
+ });
388
+ return failed("source-moved", error.message);
389
+ }
390
+ fingerprint = null;
391
+ } else if (job.native) {
392
+ if (job.platform !== "ios" || job.native.provider !== "xcode") return failed("unsupported-provider", "The worker does not implement this native provider.");
393
+ const selected = projectRegistry.selectIos(root);
394
+ if ("problem" in selected || selected.id !== "native-xcode") return failed("provider-mismatch", "The transferred project did not select the native Xcode integration.");
395
+ let metadataDirectories;
396
+ try {
397
+ metadataDirectories = nativeXcodeMetadataDirectories(selectNativeXcodeProject(root, job.scheme ?? void 0, job.configuration ?? void 0));
398
+ if (!verifyNativeTransfer(src, job.manifest, job.native.sourceDigest, metadataDirectories)) return failed("source-mismatch", "The materialized native inputs do not match the source transfer.");
399
+ } catch (error) {
400
+ return failed("source-mismatch", error.message);
401
+ }
402
+ const native = job.native;
403
+ const refused = await time("fingerprintMs", () => nativeIdentityRefusal(job, native, root, () => selected.load({})));
404
+ if (refused) return failed("identity-mismatch", refused);
405
+ compiled = await compileNativeIos(job, root, log, time);
406
+ if (!compiled.ok) return failed(compiled.code, compiled.message);
407
+ try {
408
+ if (!verifyNativeTransfer(src, job.manifest, job.native.sourceDigest, metadataDirectories)) throw new Error("The native source changed while the worker compiled it.");
409
+ } catch (error) {
410
+ if (compiled.temporary) rmSync(compiled.temporary, {
411
+ recursive: true,
412
+ force: true
413
+ });
414
+ return failed("source-moved", error.message);
415
+ }
416
+ fingerprint = job.expectedFingerprint;
417
+ } else if (job.platform === "macos") {
291
418
  fingerprint = await time("fingerprintMs", () => manifestDigest(job.manifest));
292
- if (fingerprint !== job.expectedFingerprint) return failed("fingerprint-mismatch", `the manifest there fingerprints ${fingerprint.slice(0, 12)}, here ${job.expectedFingerprint.slice(0, 12)}`);
419
+ if (fingerprint !== job.expectedFingerprint) return failed("fingerprint-mismatch", `the manifest there fingerprints ${fingerprint.slice(0, 12)}, here ${job.expectedFingerprint?.slice(0, 12)}`);
293
420
  compiled = await compileMacos(job, root, log, time);
294
421
  if (!compiled.ok) return failed(compiled.code, compiled.message);
295
422
  } else {
@@ -299,7 +426,26 @@ async function build(job) {
299
426
  return failed("deps-failed", error.message);
300
427
  }
301
428
  const platform = job.platform;
302
- const initial = await time("fingerprintMs", () => fingerprintProject(root, { platform }));
429
+ let iosProject;
430
+ try {
431
+ iosProject = iosProjectDirSetting({ ios: { projectPath: job.iosProjectPath ?? void 0 } }, root);
432
+ } catch (error) {
433
+ return failed("bad-request", error.message);
434
+ }
435
+ let androidLayout;
436
+ try {
437
+ androidLayout = androidLayoutSetting({ android: {
438
+ gradleRoot: job.android?.gradleRoot ?? void 0,
439
+ module: job.android?.module ?? void 0
440
+ } }, root, realSrc);
441
+ } catch (error) {
442
+ return failed("bad-request", error.message);
443
+ }
444
+ const initial = await time("fingerprintMs", () => fingerprintProject(root, {
445
+ platform,
446
+ iosProjectPath: iosProject.relative,
447
+ androidLayout
448
+ }));
303
449
  if (!initial) return failed("no-fingerprint", "The remote Mac could not fingerprint the project.");
304
450
  const plan = planPrebuild(root, platform, {
305
451
  isExpo: job.isExpo,
@@ -316,11 +462,14 @@ async function build(job) {
316
462
  mutations.push("prebuild");
317
463
  }
318
464
  if (platform === "ios") {
319
- const podState = readPodState(root);
320
- const pods = podAction(podState, podsAreStale(podState.lockText, podState.manifestText));
465
+ const podState = readPodState(root, iosProject.dir);
466
+ const pods = podAction(podState, podsAreStale(podState.lockText, podState.manifestText, podState.dir));
321
467
  if (pods.install) {
322
468
  note("pods", `${pods.reason ?? "Pods are stale"} -> pod install`);
323
- const result = await time("podsMs", () => runPodInstall(root, log, { onHeartbeat: (line) => note("pods", line) }));
469
+ const result = await time("podsMs", () => runPodInstall(root, log, {
470
+ directory: iosProject.dir,
471
+ onHeartbeat: (line) => note("pods", line)
472
+ }));
324
473
  if (result?.failed) return failed("pods-failed", result.reason ?? "pod install failed.");
325
474
  mutations.push("pods");
326
475
  }
@@ -330,57 +479,240 @@ async function build(job) {
330
479
  const after = await time("fingerprintMs", () => refingerprintAfterMutation({
331
480
  projectRoot: root,
332
481
  platform,
333
- previousHash: initial.hash
482
+ previousHash: initial.hash,
483
+ iosProjectPath: iosProject.relative,
484
+ androidLayout
334
485
  }));
335
486
  if (!after) return failed("no-fingerprint", `No fingerprint after ${mutations.join(", ")}.`);
336
487
  fingerprint = after.hash;
337
488
  if (mutations.includes("prebuild")) recordPrebuild(root, platform, after.hash);
338
489
  }
339
- if (fingerprint !== job.expectedFingerprint) return failed("fingerprint-mismatch", `the checkout there fingerprints ${fingerprint.slice(0, 12)}, here ${job.expectedFingerprint.slice(0, 12)}`);
340
- compiled = platform === "android" ? await compileAndroid(job, root, log, time) : await compileIos(job, root, log, time);
490
+ if (fingerprint !== job.expectedFingerprint) return failed("fingerprint-mismatch", `the checkout there fingerprints ${fingerprint.slice(0, 12)}, here ${job.expectedFingerprint?.slice(0, 12)}`);
491
+ compiled = platform === "android" ? await compileAndroid(job, root, log, time, androidLayout) : await compileIos(job, root, log, time);
341
492
  if (!compiled.ok) return failed(compiled.code, compiled.message);
493
+ const builtFingerprint = fingerprint;
342
494
  const settled = await time("fingerprintMs", () => refingerprintAfterMutation({
343
495
  projectRoot: root,
344
496
  platform,
345
- previousHash: fingerprint
497
+ previousHash: builtFingerprint,
498
+ iosProjectPath: iosProject.relative,
499
+ androidLayout
346
500
  }));
347
501
  if (!settled || settled.moved) return failed("fingerprint-moved", "The inputs changed during the build there.");
348
502
  }
349
- const out = join(job.area, "out", job.job);
350
- if (job.platform !== "macos") rmSync(out, {
351
- recursive: true,
352
- force: true
353
- });
354
- mkdirSync(out, { recursive: true });
355
- const archive = join(out, "app.tgz");
356
- const name = basename(compiled.path);
357
- if (job.platform === "ios" || job.platform === "android") cpSync(compiled.path, join(out, name), {
358
- recursive: true,
359
- verbatimSymlinks: true,
360
- mode: constants.COPYFILE_FICLONE
361
- });
362
- await time("packageMs", () => getExecutor().runFileAsync("tar", [
363
- "-czf",
364
- archive,
365
- "--options",
366
- "gzip:compression-level=1",
367
- "-C",
368
- dirname(compiled.path),
369
- name
370
- ], { timeoutMs: 6e5 }));
371
- const sha256 = await time("packageMs", () => sha256Of(archive));
372
- return {
373
- ok: true,
374
- artifact: {
375
- path: archive,
376
- name,
377
- size: statSync(archive).size,
378
- sha256
379
- },
380
- fingerprint,
381
- compilationCache: compiled.cache,
382
- timings
503
+ try {
504
+ const out = join(job.area, "out", job.job);
505
+ if (job.platform !== "macos") rmSync(out, {
506
+ recursive: true,
507
+ force: true
508
+ });
509
+ mkdirSync(out, { recursive: true });
510
+ const archive = join(out, "app.tgz");
511
+ const name = basename(compiled.path);
512
+ if (job.platform === "ios" || job.platform === "android") cpSync(compiled.path, join(out, name), {
513
+ recursive: true,
514
+ verbatimSymlinks: true,
515
+ mode: constants.COPYFILE_FICLONE
516
+ });
517
+ await time("packageMs", () => getExecutor().runFileAsync("tar", [
518
+ "-czf",
519
+ archive,
520
+ "--options",
521
+ "gzip:compression-level=1",
522
+ "-C",
523
+ dirname(compiled.path),
524
+ name
525
+ ], { timeoutMs: 6e5 }));
526
+ const sha256 = await time("packageMs", () => sha256Of(archive));
527
+ return {
528
+ ok: true,
529
+ artifact: {
530
+ path: archive,
531
+ name,
532
+ size: statSync(archive).size,
533
+ sha256
534
+ },
535
+ fingerprint,
536
+ ...compiled.androidPackage ? { androidPackage: compiled.androidPackage } : {},
537
+ ...job.native ? { sourceDigest: job.native.sourceDigest } : {},
538
+ compilationCache: compiled.cache,
539
+ timings
540
+ };
541
+ } finally {
542
+ if (compiled.temporary) rmSync(compiled.temporary, {
543
+ recursive: true,
544
+ force: true
545
+ });
546
+ }
547
+ }
548
+ function isRecord(value) {
549
+ return typeof value === "object" && value !== null && !Array.isArray(value);
550
+ }
551
+ function differingParameters(here, there, path) {
552
+ if (JSON.stringify(here) === JSON.stringify(there)) return [];
553
+ if (!isRecord(here) || !isRecord(there)) return [path];
554
+ return [.../* @__PURE__ */ new Set([...Object.keys(here), ...Object.keys(there)])].toSorted().flatMap((key) => differingParameters(here[key], there[key], `${path}.${key}`));
555
+ }
556
+ function unused() {
557
+ throw new Error("The native identity check does not prepare or compile.");
558
+ }
559
+ async function nativeIdentityRefusal(job, native, root, load) {
560
+ if (!job.optimizations) return "The native Xcode build has no compiler options.";
561
+ try {
562
+ const recipe = (await load()).artifact({
563
+ root,
564
+ logFile: join(job.area, "build.ndjson"),
565
+ configuration: job.configuration,
566
+ ...job.scheme ? { buildScheme: job.scheme } : {},
567
+ target: {
568
+ udid: null,
569
+ destination: "generic/platform=iOS Simulator",
570
+ sdk: "iphonesimulator",
571
+ arch: native.arch,
572
+ keyArch: native.arch,
573
+ offloadRuntime: () => job.runtime,
574
+ offloadRefusal: null
575
+ },
576
+ device: null,
577
+ optimizations: job.optimizations,
578
+ cache: {
579
+ read: true,
580
+ write: true,
581
+ remote: false
582
+ },
583
+ phase: unused,
584
+ note: unused,
585
+ logWriter: unused,
586
+ estimates: unused,
587
+ step: unused,
588
+ setPodsMs: unused
589
+ });
590
+ const identity = await recipe.identity();
591
+ if ("cacheIneligible" in identity) return `This Mac cannot key the native build: ${identity.cacheIneligible}`;
592
+ if (identity.key === native.cacheKey) return null;
593
+ const here = recipe.offload?.request(job.runtime ?? "").native?.snapshot.parameters;
594
+ const differing = differingParameters(native.parameters, here, "parameters");
595
+ return differing.length ? `This Mac keys the native build differently in ${differing.join(", ")}.` : "This Mac keys the native build differently from the same parameters.";
596
+ } catch (error) {
597
+ return `This Mac cannot key the native build: ${error.message}`;
598
+ }
599
+ }
600
+ async function compileNativeIos(job, root, log, time) {
601
+ if (job.native?.provider !== "xcode" || !job.optimizations) return {
602
+ ok: false,
603
+ code: "bad-request",
604
+ message: "The native Xcode build has no compiler options."
605
+ };
606
+ const native = job.native;
607
+ for (const key of [
608
+ "compilationCache",
609
+ "swiftCompilationCache",
610
+ "prefixMapping"
611
+ ]) {
612
+ const value = job.optimizations[key];
613
+ writeConfigSetting({
614
+ scope: "workspace",
615
+ projectPath: root
616
+ }, `optimizations.ios.${key}`, value ?? void 0);
617
+ }
618
+ note("build", `native Xcode ${job.configuration ?? "Debug"}`);
619
+ const logFile = join(workspaceLogsDir(root), "build-ios.ndjson");
620
+ try {
621
+ rmSync(logFile, { force: true });
622
+ const built = await time("buildMs", () => buildIosOperation(root, {
623
+ scheme: job.scheme ?? void 0,
624
+ configuration: job.configuration ?? void 0,
625
+ arch: native.arch ?? "all",
626
+ remoteBuild: "local"
627
+ }));
628
+ if (built.cacheKey !== native.cacheKey || built.cacheSkipped) {
629
+ rmSync(dirname(built.appPath), {
630
+ recursive: true,
631
+ force: true
632
+ });
633
+ return {
634
+ ok: false,
635
+ code: "fingerprint-mismatch",
636
+ message: "The worker resolved a different native artifact identity."
637
+ };
638
+ }
639
+ return {
640
+ ok: true,
641
+ path: built.appPath,
642
+ cache: built.compilationCache,
643
+ temporary: dirname(built.appPath)
644
+ };
645
+ } catch (error) {
646
+ return {
647
+ ok: false,
648
+ code: "native-build-failed",
649
+ message: error.message
650
+ };
651
+ } finally {
652
+ if (existsSync(logFile)) for (const record of parseNdjsonText(readFileSync(logFile, "utf8"))) log.write(record);
653
+ }
654
+ }
655
+ async function compileNativeAndroid(job, root, log, time) {
656
+ const options = job.android;
657
+ if (!options) return {
658
+ ok: false,
659
+ code: "bad-request",
660
+ message: "The native Gradle job has no build options."
383
661
  };
662
+ for (const key of [
663
+ "compilerCache",
664
+ "pch",
665
+ "gradleBuildCache"
666
+ ]) writeConfigSetting({
667
+ scope: "workspace",
668
+ projectPath: root
669
+ }, `optimizations.android.${key}`, options[key]);
670
+ const idleMs = job.gradleDaemonIdleMs ?? 0;
671
+ limitDaemonIdle(idleMs > 0 ? idleMs : 6e4);
672
+ const gradlew = join(root, process.platform === "win32" ? "gradlew.bat" : "gradlew");
673
+ const stop = () => {
674
+ stopGradleDaemons(gradlew, 4e3);
675
+ process.exit(143);
676
+ };
677
+ process.once("SIGTERM", stop);
678
+ const logFile = join(workspaceLogsDir(root), "build-android.ndjson");
679
+ note("build", `native Gradle ${options.variant ?? "debug"}`);
680
+ try {
681
+ rmSync(logFile, { force: true });
682
+ const built = await time("buildMs", () => buildAndroidOperation(root, {
683
+ variant: options.variant ?? void 0,
684
+ abi: options.abi ?? "all",
685
+ remoteBuild: "local"
686
+ }));
687
+ if (built.cacheKey !== null || !built.cacheSkipped || !built.androidPackage) {
688
+ rmSync(dirname(built.apkPath), {
689
+ recursive: true,
690
+ force: true
691
+ });
692
+ return {
693
+ ok: false,
694
+ code: "unexpected-cache-identity",
695
+ message: "The native Gradle worker returned an unsupported artifact identity or package."
696
+ };
697
+ }
698
+ return {
699
+ ok: true,
700
+ path: built.apkPath,
701
+ temporary: dirname(built.apkPath),
702
+ cache: built.ccache,
703
+ androidPackage: built.androidPackage
704
+ };
705
+ } catch (error) {
706
+ return {
707
+ ok: false,
708
+ code: "native-build-failed",
709
+ message: error.message
710
+ };
711
+ } finally {
712
+ process.off("SIGTERM", stop);
713
+ if (idleMs <= 0) stopGradleDaemons(gradlew, 6e4);
714
+ if (existsSync(logFile)) for (const record of parseNdjsonText(readFileSync(logFile, "utf8"))) log.write(record);
715
+ }
384
716
  }
385
717
  async function compileMacos(job, root, log, time) {
386
718
  const options = job.macos;
@@ -478,6 +810,7 @@ async function compileIos(job, root, log, time) {
478
810
  note("build", `xcodebuild for ${runtime}`);
479
811
  const built = await time("buildMs", () => buildIos({
480
812
  root,
813
+ iosDir: iosProjectDirSetting({ ios: { projectPath: job.iosProjectPath ?? void 0 } }, root).dir,
481
814
  destination: target,
482
815
  logWriter: log,
483
816
  ...job.scheme ? { scheme: job.scheme } : {},
@@ -504,8 +837,7 @@ async function compileIos(job, root, log, time) {
504
837
  * Stops the Gradle daemons of this client's Gradle home. A daemon calls setsid, so it leaves the build's
505
838
  * process group and would outlive the job and its claim.
506
839
  */
507
- function stopGradleDaemons(root, timeoutMs) {
508
- const gradlew = gradlewPath(root);
840
+ function stopGradleDaemons(gradlew, timeoutMs) {
509
841
  if (!existsSync(gradlew)) return;
510
842
  getExecutor().runFileQuiet(gradlew, ["--stop"], {
511
843
  cwd: dirname(gradlew),
@@ -527,7 +859,7 @@ function limitDaemonIdle(idleMs) {
527
859
  mkdirSync(home, { recursive: true });
528
860
  writeFileSync(file, wanted);
529
861
  }
530
- async function compileAndroid(job, root, log, time) {
862
+ async function compileAndroid(job, root, log, time, layout) {
531
863
  const options = job.android;
532
864
  if (!options) return {
533
865
  ok: false,
@@ -539,23 +871,25 @@ async function compileAndroid(job, root, log, time) {
539
871
  limitDaemonIdle(keepDaemon ? idleMs : 6e4);
540
872
  const onNote = (line) => note("build", line);
541
873
  const stop = () => {
542
- stopGradleDaemons(root, 4e3);
874
+ stopGradleDaemons(gradlewPath(layout), 4e3);
543
875
  process.exit(143);
544
876
  };
545
877
  process.once("SIGTERM", stop);
546
- note("build", `gradle ${assembleTaskFor(options.variant)} for ${options.abi ?? "every ABI"}`);
878
+ note("build", `gradle ${layout.custom ? `${layout.module}:` : ""}${assembleTaskFor(options.variant)} for ${options.abi ?? "every ABI"}`);
547
879
  try {
548
880
  const built = await time("buildMs", () => buildAndroid({
549
881
  root,
550
882
  logWriter: log,
551
883
  variant: options.variant,
552
- abi: options.abi
884
+ abi: options.abi,
885
+ layout
553
886
  }, {
554
887
  buildCache: options.gradleBuildCache,
555
888
  pch: options.pch,
556
889
  compilerCacheDisabled: options.compilerCache === "none",
557
890
  ccache: options.compilerCache === "ccache" ? resolveCcache({
558
891
  root,
892
+ layout,
559
893
  onNote
560
894
  }) : null,
561
895
  onHeartbeat: onNote,
@@ -576,12 +910,14 @@ async function compileAndroid(job, root, log, time) {
576
910
  };
577
911
  } finally {
578
912
  process.off("SIGTERM", stop);
579
- if (!keepDaemon) stopGradleDaemons(root, 6e4);
913
+ if (!keepDaemon) stopGradleDaemons(gradlewPath(layout), 6e4);
580
914
  }
581
915
  }
582
916
  async function main() {
583
917
  const mode = process.argv[2];
584
918
  if (mode === "offer") return emit(workerToolchain(process.argv[3] ?? null));
919
+ if (mode === "offer-native-xcode") return emit(workerToolchain(null, "xcode"));
920
+ if (mode === "offer-native-gradle") return emit(workerToolchain(null, "gradle"));
585
921
  if (mode === "build") {
586
922
  const chunks = [];
587
923
  for await (const chunk of process.stdin) chunks.push(chunk);
@@ -590,7 +926,7 @@ async function main() {
590
926
  ...await build(JSON.parse(Buffer.concat(chunks).toString("utf8")))
591
927
  });
592
928
  }
593
- process.stderr.write("usage: offload-worker offer | build < job.json\n");
929
+ process.stderr.write("usage: offload-worker offer | offer-native-xcode | offer-native-gradle | build < job.json\n");
594
930
  process.exitCode = 2;
595
931
  }
596
932
  main().catch((error) => {
@@ -1,6 +1,6 @@
1
1
  import { t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
- import { o as pidExists } from "./metro-Dh8T4Ytd.mjs";
3
- import { c as writeWorkspaceState, o as withWorkspaceStateLock, r as readWorkspaceState } from "./workspace-state-C59dqG-Z.mjs";
2
+ import { c as writeWorkspaceState, o as withWorkspaceStateLock, r as readWorkspaceState } from "./workspace-state-CSIFuuDa.mjs";
3
+ import { s as pidExists } from "./metro-D_j9sN-B.mjs";
4
4
  import { realpathSync } from "node:fs";
5
5
  //#region src/collector/state.ts
6
6
  function collectorRoot(root) {
@@ -1,4 +1,4 @@
1
- import { p as buildPlacementSettings } from "./client-DzhapoMB.mjs";
1
+ import { m as buildPlacementSettings } from "./client-BLy1Pw2V.mjs";
2
2
  //#region src/placement-log.ts
3
3
  /** `machine` is `local` when the run stays on this Mac. */
4
4
  function placementRecord(input) {
@@ -20,7 +20,7 @@ function skippedCandidates(skipped) {
20
20
  msg: reason
21
21
  }));
22
22
  }
23
- function devicePlacementRecord({ platform, fromFlag, placed }) {
23
+ function devicePlacementRecord({ platform, fromFlag, easFallback = false, placed }) {
24
24
  return placementRecord({
25
25
  kind: "device",
26
26
  platform,
@@ -28,7 +28,11 @@ function devicePlacementRecord({ platform, fromFlag, placed }) {
28
28
  key: `${platform}.remote`,
29
29
  value: "auto",
30
30
  from: fromFlag ? "flag" : "setting"
31
- }],
31
+ }, ...easFallback ? [{
32
+ key: "remote.easFallback",
33
+ value: "true",
34
+ from: "setting"
35
+ }] : []],
32
36
  candidates: skippedCandidates(placed.skipped),
33
37
  choice: {
34
38
  machine: placed.placement.machine ?? "local",