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
@@ -0,0 +1,772 @@
1
+ import { n as ownership_claim_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { A as providerDownloadPath, k as prepareProviderDownloadDir, v as waitForSharedBuild } from "./device-lease-DHOBXGgS.mjs";
3
+ import { x as workspaceDir } from "./workspace-state-CSIFuuDa.mjs";
4
+ import { _ as stepTimer, i as formatDuration, l as phaseLine, m as shortHash } from "./command-output-BjPN99ZB.mjs";
5
+ import { x as runCancellationSignal } from "./tailnet-CoJUb2tB.mjs";
6
+ import { a as easAuthNote, s as isEasAuthFailureText, t as RESOLVE_TIMEOUT_MS } from "./remote-cache-Bsts6f6Y.mjs";
7
+ import { _ as requireLocalBuild, c as offloadPlacement, h as namedBuildMachine, i as closeOffload, l as placementLoad, n as buildPlacementCandidates, o as offloadBuild, p as OffloadRefusal, r as chooseBuildMachine, u as remotePhaseText } from "./client-BLy1Pw2V.mjs";
8
+ import { i as skippedMissReason } from "./miss-reason-BPAzZS0r.mjs";
9
+ import { t as buildPlacementRecord } from "./placement-log-reFbjqEr.mjs";
10
+ import { t as runArtifactLifecycle } from "./artifact-lifecycle-DzPpBYvK.mjs";
11
+ import { n as CCACHE_UNAVAILABLE, r as ccacheActivityLine, t as CCACHE_NOT_RUN } from "./ccache-CjHQ5F8g.mjs";
12
+ import { c as formatDiagnostic } from "./gradle-C2OEKCCI.mjs";
13
+ import { a as PLATFORM, h as displayPath } from "./support-CnE_SIjt.mjs";
14
+ import { i as AndroidRecipeRefusal } from "./plan-BDk2AeY_.mjs";
15
+ import { rmSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ import { machineCapacity } from "@stim-cli/core/state";
18
+ import { createWarnOnce, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
19
+ import chalk from "chalk";
20
+ //#region src/commands/android/artifact.ts
21
+ const FALLBACK_LINES = 5;
22
+ function fail(code, message, remedy, extra = {}) {
23
+ return {
24
+ code,
25
+ message,
26
+ remedy,
27
+ extra
28
+ };
29
+ }
30
+ async function acquireAndroidArtifact({ root, buildLog, writer, recipe, targetOffloadRefusal, buildPlan, cacheProviderConfig, requestedBuildCache, easBuild, androidPackage: initialPackage, record, maxBuilds, progress }, { acquireLock, releaseLock, waitForBuild, easAuth, resolveRemoteBuild, uploadRemoteBuild, loadCacheProviderModule, acquireSlot, releaseSlot, now }) {
31
+ const { phase, out, stats, step, miss, hit: lateHit, place, waitingOn, waitingFor } = progress;
32
+ let fallbackMachine = null;
33
+ let hereReason = "no remote Mac is paired";
34
+ let slotWaitMs;
35
+ const buildMachine = record.buildMachine ?? "auto";
36
+ const fallBack = (reason, line = reason, info = {}) => {
37
+ writer.write(buildPlacementRecord({
38
+ platform: PLATFORM,
39
+ buildMachine,
40
+ candidates: info.candidates,
41
+ event: "placement_fallback",
42
+ fallback: {
43
+ code: info.code ?? "fallback",
44
+ reason,
45
+ machine: info.machine
46
+ }
47
+ }));
48
+ if (namedBuildMachine(buildMachine)) throw new OffloadRefusal(buildMachine, reason);
49
+ requireLocalBuild(buildMachine);
50
+ record.offloadFallback = reason;
51
+ phase("build", `${line} -> building here`);
52
+ };
53
+ const { variant, cache: cachePolicy } = buildPlan;
54
+ const signal = runCancellationSignal() ?? new AbortController().signal;
55
+ const cacheTarget = (key) => ({
56
+ projectRoot: root,
57
+ platform: "android",
58
+ key,
59
+ signal
60
+ });
61
+ let useBuildCache = cachePolicy.read;
62
+ let cacheIneligible = null;
63
+ let androidPackage = initialPackage;
64
+ let ccacheActivity = CCACHE_NOT_RUN;
65
+ let phaseFailure = null;
66
+ let buildLock = null;
67
+ const releaseHeldLock = () => {
68
+ if (!buildLock) return;
69
+ const held = buildLock;
70
+ buildLock = null;
71
+ try {
72
+ releaseLock(held);
73
+ } catch (err) {
74
+ out(phaseLine("build", chalk.dim(`could not release the build lock at ${held.path}: ${err?.message || err}`)));
75
+ }
76
+ };
77
+ let buildSlot = null;
78
+ const releaseHeldSlot = () => {
79
+ if (!buildSlot) return;
80
+ const held = buildSlot;
81
+ buildSlot = null;
82
+ try {
83
+ releaseSlot(held);
84
+ } catch (err) {
85
+ out(phaseLine("build", chalk.dim(`could not release the build slot: ${err?.message || err}`)));
86
+ }
87
+ };
88
+ let hash = "";
89
+ let providerUpload = null;
90
+ let providerName = null;
91
+ let providerLoad = null;
92
+ const cacheWarn = createWarnOnce((line) => phase("cache", chalk.yellow(line)));
93
+ const loadTieredProvider = cachePolicy.remote && cacheProviderConfig ? () => providerLoad ??= loadCacheProviderModule({
94
+ projectRoot: root,
95
+ config: cacheProviderConfig
96
+ }) : null;
97
+ let cacheKey = "";
98
+ let storeHash = "";
99
+ let storeKey = "";
100
+ let apkPath = null;
101
+ let handoff = null;
102
+ let swapFellBack = false;
103
+ async function resolveInitialFingerprint() {
104
+ if (easBuild?.ok) {
105
+ hash = storeHash = easBuild.fingerprint;
106
+ cacheKey = storeKey = easBuild.cacheKey;
107
+ apkPath = easBuild.path;
108
+ record.fingerprint = hash;
109
+ record.cacheKey = cacheKey;
110
+ record.cacheHit = easBuild.cacheHit;
111
+ record.cacheSkipped = !useBuildCache;
112
+ providerName = "eas";
113
+ stats.setCacheKey(cacheKey);
114
+ return true;
115
+ }
116
+ if (useBuildCache) step("cache-lookup");
117
+ const fingerprintTimer = stepTimer(now);
118
+ let computed;
119
+ try {
120
+ computed = await recipe.identity();
121
+ } catch (error) {
122
+ record.cacheSkipped = !useBuildCache;
123
+ if (error instanceof AndroidRecipeRefusal && error.missReason) record.missReason = error.missReason;
124
+ throw error;
125
+ }
126
+ if ("cacheIneligible" in computed) {
127
+ cacheIneligible = computed.cacheIneligible;
128
+ useBuildCache = false;
129
+ record.fingerprint = null;
130
+ record.cacheKey = null;
131
+ record.cacheHit = false;
132
+ record.cacheSkipped = true;
133
+ phase("fingerprint", `unavailable: ${cacheIneligible}`);
134
+ return true;
135
+ }
136
+ hash = computed.hash;
137
+ record.fingerprint = hash;
138
+ cacheKey = computed.key;
139
+ stats.setCacheKey(cacheKey);
140
+ record.cacheKey = cacheKey;
141
+ storeHash = hash;
142
+ storeKey = cacheKey;
143
+ const found = await resolveTieredBuild({
144
+ local: recipe.cache(),
145
+ loadProvider: loadTieredProvider,
146
+ target: {
147
+ projectRoot: root,
148
+ platform: PLATFORM,
149
+ key: cacheKey
150
+ },
151
+ destinationDir: providerDownloadPath(workspaceDir(root)),
152
+ ensureDestination: prepareProviderDownloadDir,
153
+ skipRead: !useBuildCache,
154
+ warn: cacheWarn
155
+ });
156
+ const cached = found?.tier === "local" ? found.path : null;
157
+ record.cacheHit = cached ? "local" : false;
158
+ record.cacheSkipped = !useBuildCache;
159
+ phase("fingerprint", `${shortHash(hash)} ${cached ? "hit" : "miss"}${useBuildCache ? "" : !requestedBuildCache ? " (--no-build-cache)" : " (cache reuse off in config)"} ${fingerprintTimer()}`);
160
+ if (found?.tier === "provider") {
161
+ record.cacheHit = "remote";
162
+ providerName = found.providerName ?? null;
163
+ phase("cache", `provider hit (${providerName})${found.storedLocally ? " -> stored locally" : ""}`);
164
+ }
165
+ apkPath = found?.path ?? null;
166
+ return true;
167
+ }
168
+ let remote = null;
169
+ let abandonedRemote = false;
170
+ let uploadPending = null;
171
+ async function resolveRemoteArtifact() {
172
+ if (cacheIneligible || !recipe.legacyCache) return;
173
+ if (!apkPath) {
174
+ const loaded = await recipe.legacyCache.load();
175
+ if (loaded?.unavailable) phase("cache", chalk.yellow(`provider not usable: ${loaded.unavailable}`));
176
+ else if (loaded?.provider) remote = loaded;
177
+ if (remote?.name === "eas") {
178
+ const auth = easAuth({
179
+ projectRoot: root,
180
+ owner: loaded?.owner || null
181
+ });
182
+ const authNote = easAuthNote(auth);
183
+ if (authNote) phase("cache", chalk.yellow(authNote));
184
+ if (auth?.code === "logged-out") remote = null;
185
+ }
186
+ }
187
+ if (remote && useBuildCache) {
188
+ const remoteTimer = stepTimer(now);
189
+ const hit = await resolveRemoteBuild({
190
+ logWriter: writer,
191
+ provider: remote.provider,
192
+ platform: PLATFORM,
193
+ projectRoot: root,
194
+ fingerprintHash: hash,
195
+ runOptions: recipe.legacyCache?.runOptions ?? null
196
+ });
197
+ if (hit?.appPath) {
198
+ let stored = null;
199
+ try {
200
+ stored = await recipe.cache().store({
201
+ ...cacheTarget(cacheKey),
202
+ sourcePath: hit.appPath,
203
+ overwrite: false
204
+ }) ?? null;
205
+ } catch (err) {
206
+ phase("cache", chalk.yellow(`remote hit could not be stored locally: ${err?.message || err}`));
207
+ }
208
+ apkPath = stored || hit.appPath;
209
+ record.cacheHit = "remote";
210
+ phase("cache", `remote hit (${remote.name})${stored ? " -> stored locally" : ""} ${remoteTimer()}`);
211
+ } else if (hit?.timedOut) {
212
+ abandonedRemote = true;
213
+ phase("cache", chalk.yellow(`${remote.name} did not answer within ${formatDuration(RESOLVE_TIMEOUT_MS)}; building instead`));
214
+ } else if (hit?.failed) {
215
+ const authNote = remote.name === "eas" && isEasAuthFailureText(hit.failed) ? easAuthNote({
216
+ code: "logged-out",
217
+ reason: hit.failed
218
+ }) : null;
219
+ phase("cache", chalk.yellow(authNote || `${remote.name} could not be used: ${hit.failed}; building instead`));
220
+ } else phase("cache", `remote miss (${remote.name}) ${remoteTimer()}`);
221
+ }
222
+ }
223
+ let waitedForBuild = null;
224
+ let releasedWait = null;
225
+ async function awaitSharedBuild() {
226
+ if (!useBuildCache) return null;
227
+ const shared = await waitForSharedBuild({
228
+ platform: PLATFORM,
229
+ key: cacheKey,
230
+ fingerprint: hash,
231
+ root,
232
+ logFile: buildLog,
233
+ command: "stim android",
234
+ acquire: acquireLock,
235
+ wait: waitForBuild,
236
+ now,
237
+ phase: (text) => {
238
+ step("wait");
239
+ phase("build", text);
240
+ },
241
+ waitingOn,
242
+ warn: (text) => phase("build", chalk.yellow(text)),
243
+ out
244
+ });
245
+ if (shared.refusal) {
246
+ const { code, message, remedy } = shared.refusal;
247
+ phaseFailure = fail(code, message, remedy, { lastBuildStatus: true });
248
+ throw new AndroidRecipeRefusal(phaseFailure);
249
+ }
250
+ buildLock = shared.lock;
251
+ releasedWait = shared.released;
252
+ if (shared.hit) {
253
+ apkPath = shared.hit.path;
254
+ record.cacheHit = "local";
255
+ waitedForBuild = shared.hit.waited;
256
+ }
257
+ return apkPath;
258
+ }
259
+ let swapDir = null;
260
+ const releaseArtifact = () => {
261
+ if (!swapDir) return;
262
+ try {
263
+ rmSync(swapDir, {
264
+ recursive: true,
265
+ force: true
266
+ });
267
+ swapDir = null;
268
+ } catch (error) {
269
+ out(phaseLine("cleanup", chalk.yellow(`could not remove the temporary APK: ${error.message}`)));
270
+ }
271
+ };
272
+ const installableCachedApk = async (key, cachedPath) => {
273
+ const prepared = await recipe.materialize(key, cachedPath);
274
+ if (!prepared) {
275
+ swapFellBack = true;
276
+ return null;
277
+ }
278
+ swapDir = prepared.directory;
279
+ return prepared.apkPath;
280
+ };
281
+ async function prepareCachedArtifact(cachedPath) {
282
+ const prepared = await installableCachedApk(cacheKey, cachedPath);
283
+ if (!prepared) {
284
+ record.cacheHit = false;
285
+ waitedForBuild = null;
286
+ }
287
+ return prepared;
288
+ }
289
+ function reasonForMiss(rekeyedBy) {
290
+ if (swapFellBack) return {
291
+ reason: skippedMissReason("the cached APK could not be reused, so this run built it fresh"),
292
+ diff: null
293
+ };
294
+ if (cacheIneligible) return {
295
+ reason: skippedMissReason(cacheIneligible),
296
+ diff: null
297
+ };
298
+ if (!useBuildCache) return {
299
+ reason: skippedMissReason(requestedBuildCache ? "cache reuse off in config" : "cache reuse turned off by --no-build-cache"),
300
+ diff: null
301
+ };
302
+ return recipe.explain(rekeyedBy);
303
+ }
304
+ function explainMiss(rekeyedBy) {
305
+ const explained = reasonForMiss(rekeyedBy);
306
+ record.missReason = explained.reason;
307
+ if (explained.diff) writer.write(explained.diff);
308
+ miss(record.missReason);
309
+ phase("cache", `miss: ${record.missReason.summary}`);
310
+ if (record.missReason.kind === "no-baseline") {
311
+ const line = recipe.untrackedLine();
312
+ if (line) phase("fingerprint", chalk.dim(line));
313
+ }
314
+ }
315
+ async function takeBuildSlot() {
316
+ requireLocalBuild(buildMachine);
317
+ if (!maxBuilds) return true;
318
+ try {
319
+ buildSlot = await acquireSlot({
320
+ automatic: buildMachine === "auto",
321
+ max: maxBuilds,
322
+ root,
323
+ logFile: buildLog,
324
+ out,
325
+ waitingFor: (info) => waitingFor?.(info, "build-slot")
326
+ });
327
+ slotWaitMs = buildSlot.slotWaitMs;
328
+ } catch (err) {
329
+ requireLocalBuild(buildMachine);
330
+ const refusal = (0, ownership_claim_exports.claimFailure)(err, "stim android");
331
+ if (refusal) {
332
+ phaseFailure = fail(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
333
+ return false;
334
+ }
335
+ phase("build", chalk.yellow(`could not take a build slot: ${err?.message || err}; building anyway`));
336
+ }
337
+ return true;
338
+ }
339
+ /** Whether this build should leave this Mac, before any machine is asked; null builds here. */
340
+ function placeBuild() {
341
+ if (cacheIneligible && !recipe.offload?.supportsUncachedArtifacts) {
342
+ if (namedBuildMachine(buildMachine)) throw new OffloadRefusal(buildMachine, cacheIneligible);
343
+ hereReason = cacheIneligible;
344
+ return null;
345
+ }
346
+ const { mode, machines, localEnabled } = buildPlacementCandidates(buildMachine);
347
+ if (localEnabled && machines.length === 0 && !namedBuildMachine(buildMachine) && buildMachine !== "local") return null;
348
+ const unsupported = targetOffloadRefusal ?? (recipe.offload ? recipe.offload.unsupported : "this project integration does not support offloaded builds");
349
+ const here = machineCapacity();
350
+ const placement = offloadPlacement({
351
+ mode,
352
+ localEnabled,
353
+ machines: machines.length,
354
+ here,
355
+ unsupported,
356
+ selected: buildMachine
357
+ });
358
+ if (!placement.offload) {
359
+ hereReason = placement.reason;
360
+ writer.write(buildPlacementRecord({
361
+ platform: PLATFORM,
362
+ buildMachine,
363
+ stays: {
364
+ code: placement.code,
365
+ reason: placement.reason
366
+ }
367
+ }));
368
+ if (mode !== "off") phase("build", `placement: here (${placement.reason})`);
369
+ return null;
370
+ }
371
+ return {
372
+ mode,
373
+ here,
374
+ code: placement.code,
375
+ reason: placement.reason,
376
+ machines
377
+ };
378
+ }
379
+ const openOffload = { choice: null };
380
+ /** Asks the paired machines once the post-mutation key is known; null builds here. */
381
+ async function chooseMachine(candidate) {
382
+ let asked = [];
383
+ let target;
384
+ try {
385
+ target = recipe.offload.target();
386
+ } catch (error) {
387
+ fallBack(error.message);
388
+ return null;
389
+ }
390
+ const choice = await chooseBuildMachine({
391
+ projectRoot: root,
392
+ target,
393
+ mode: candidate.mode,
394
+ here: candidate.here,
395
+ note: (line) => phase("build", chalk.dim(`offload: ${line}`)),
396
+ machines: candidate.machines,
397
+ selected: buildMachine,
398
+ onCandidates: (each) => asked = each
399
+ });
400
+ if (typeof choice === "string") {
401
+ const only = candidate.machines.length === 1 ? candidate.machines[0].machine : null;
402
+ if (only && choice.startsWith(`${only}: `)) fallbackMachine = only;
403
+ fallBack(choice, choice, {
404
+ code: "no-remote-mac-took-it",
405
+ candidates: asked
406
+ });
407
+ return null;
408
+ }
409
+ openOffload.choice = choice;
410
+ writer.write(buildPlacementRecord({
411
+ platform: PLATFORM,
412
+ buildMachine,
413
+ candidates: asked,
414
+ chose: {
415
+ machine: choice.machine,
416
+ reason: `${candidate.reason}${placementLoad(choice)}`
417
+ }
418
+ }));
419
+ phase("build", `placement: ${choice.machine} (${candidate.reason}${placementLoad(choice)})`);
420
+ return choice;
421
+ }
422
+ /** Builds on the chosen machine and stores the APK under the post-mutation key; false builds here instead unless a machine was named. */
423
+ async function compileElsewhere(choice, candidate) {
424
+ const uncached = Boolean(cacheIneligible && recipe.offload?.supportsUncachedArtifacts);
425
+ if ((!storeKey || !storeHash) && !uncached) {
426
+ const reason = "the build fingerprint or cache key is unavailable";
427
+ if (namedBuildMachine(buildMachine)) fallBack(reason, reason, { machine: choice.machine });
428
+ else writer.write(buildPlacementRecord({
429
+ platform: PLATFORM,
430
+ buildMachine,
431
+ event: "placement_fallback",
432
+ fallback: {
433
+ code: "fallback",
434
+ reason,
435
+ machine: choice.machine
436
+ }
437
+ }));
438
+ return false;
439
+ }
440
+ const stagingDir = join(workspaceDir(root), "offload", PLATFORM);
441
+ if (uncached) swapDir = stagingDir;
442
+ const outcome = await offloadBuild({
443
+ choice,
444
+ expectedFingerprint: uncached ? null : storeHash,
445
+ request: recipe.offload.request,
446
+ stagingDir,
447
+ onPhase: (name, msg) => phase(name, remotePhaseText(name, msg, choice.machine)),
448
+ onEnter: (name) => {
449
+ if (name === "compile" || name === "build" || name === "prebuild" || name === "pods") record.builtOn = choice.machine;
450
+ place({
451
+ host: choice.machine,
452
+ phase: name
453
+ });
454
+ step(name === "prebuild" ? name : "compile");
455
+ },
456
+ onRecord: (entry) => writer.write({
457
+ ...entry,
458
+ offloadedTo: choice.machine
459
+ }),
460
+ note: (line) => phase("build", line)
461
+ });
462
+ place(null);
463
+ let stored = null;
464
+ let reason = outcome.ok ? null : outcome.reason;
465
+ if (outcome.ok) {
466
+ let unchanged = false;
467
+ try {
468
+ unchanged = await recipe.offload.unchanged();
469
+ } catch (error) {
470
+ reason = error.message;
471
+ }
472
+ if (!unchanged) reason ??= "the checkout here changed while it built";
473
+ else if (uncached) {
474
+ if (!outcome.androidPackage) reason = "the worker returned no verified APK package";
475
+ else {
476
+ stored = outcome.artifactPath;
477
+ androidPackage = outcome.androidPackage;
478
+ swapDir = stagingDir;
479
+ }
480
+ } else try {
481
+ stored = await recipe.cache().store({
482
+ ...cacheTarget(storeKey),
483
+ sourcePath: outcome.artifactPath,
484
+ overwrite: !useBuildCache
485
+ }) ?? null;
486
+ } catch (err) {
487
+ reason = `could not store the APK: ${err?.message || err}`;
488
+ }
489
+ }
490
+ try {
491
+ if (!uncached || !stored) {
492
+ rmSync(stagingDir, {
493
+ recursive: true,
494
+ force: true
495
+ });
496
+ if (swapDir === stagingDir) swapDir = null;
497
+ }
498
+ } catch {}
499
+ step("compile");
500
+ if (!outcome.ok || !stored) {
501
+ const why = reason ?? "the APK was not stored";
502
+ fallbackMachine = choice.machine;
503
+ fallBack(`${choice.machine}: ${why}`, `offload failed: ${why}`, {
504
+ code: "offload-failed",
505
+ machine: choice.machine
506
+ });
507
+ writer.write({
508
+ src: "build",
509
+ level: "warn",
510
+ event: "offload_failed",
511
+ msg: reason,
512
+ machine: choice.machine
513
+ });
514
+ return false;
515
+ }
516
+ const { timings } = outcome;
517
+ apkPath = stored;
518
+ handoff = outcome.handoff ?? null;
519
+ record.builtOn = outcome.machine;
520
+ record.offloadedTo = outcome.machine;
521
+ stats.setPlacement({
522
+ decision: "offloaded",
523
+ machine: outcome.machine,
524
+ reason: `${candidate.reason}${placementLoad(choice)}`,
525
+ buildMs: timings.totalMs
526
+ });
527
+ ccacheActivity = outcome.ccache;
528
+ phase("build", `built on ${outcome.machine} in ${formatDuration(timings.totalMs)}: offer ${formatDuration(timings.offerMs)}, sync ${formatDuration(timings.syncMs)}, build ${formatDuration(timings.workerMs)}, fetch ${formatDuration(timings.fetchMs)}`);
529
+ phase("cache", `compilation cache on ${outcome.machine} ${ccacheActivityLine(ccacheActivity)}`);
530
+ writer.write({
531
+ src: "build",
532
+ level: "info",
533
+ event: "offload_done",
534
+ msg: `built on ${outcome.machine}`,
535
+ timings
536
+ });
537
+ return true;
538
+ }
539
+ async function prepareSource() {
540
+ await recipe.prepare(() => miss(reasonForMiss([]).reason, true));
541
+ return recipe.reconcile();
542
+ }
543
+ async function revalidateSource(preparation) {
544
+ const { identity, rekeyedBy } = preparation;
545
+ if (preparation.androidPackage) {
546
+ androidPackage = preparation.androidPackage;
547
+ record.bundleId = androidPackage;
548
+ }
549
+ if (cacheIneligible || !identity) {
550
+ storeHash = "";
551
+ storeKey = "";
552
+ record.fingerprint = null;
553
+ record.cacheKey = null;
554
+ return null;
555
+ }
556
+ if (identity.key !== storeKey) {
557
+ storeHash = identity.hash;
558
+ storeKey = identity.key;
559
+ record.fingerprint = storeHash;
560
+ record.cacheKey = storeKey;
561
+ const late = useBuildCache ? await recipe.cache().resolve({
562
+ ...cacheTarget(storeKey),
563
+ destinationDir: providerDownloadPath(workspaceDir(root))
564
+ }) : null;
565
+ if (late) {
566
+ const prepared = await installableCachedApk(storeKey, late);
567
+ if (prepared) {
568
+ apkPath = prepared;
569
+ record.cacheHit = "local";
570
+ lateHit();
571
+ phase("cache", `hit ${shortHash(storeHash)} (post-${rekeyedBy.join("/")} key)`);
572
+ if (releasedWait) {
573
+ waitedForBuild = releasedWait.facts;
574
+ phase("build", `waited ${formatDuration(waitedForBuild.ms)} for ${releasedWait.who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
575
+ }
576
+ }
577
+ }
578
+ }
579
+ return apkPath ? { ready: apkPath } : null;
580
+ }
581
+ async function acquireRemoteArtifact(offload, { rekeyedBy, cacheRefusal }) {
582
+ explainMiss(rekeyedBy);
583
+ step("compile");
584
+ if (cacheRefusal) fallBack(cacheRefusal, `offload failed: ${cacheRefusal}`);
585
+ else {
586
+ const choice = await chooseMachine(offload);
587
+ if (choice) await compileElsewhere(choice, offload);
588
+ }
589
+ return apkPath ? { ready: apkPath } : null;
590
+ }
591
+ async function compileHere(offload, { rekeyedBy }) {
592
+ if (!offload) {
593
+ explainMiss(rekeyedBy);
594
+ step("compile");
595
+ }
596
+ if (offload) stats.setPlacement({
597
+ decision: "fell-back",
598
+ slotWaitMs,
599
+ deviceSlotWaitMs: stats.deviceSlotWaitMs(),
600
+ reason: record.offloadFallback ?? "offload failed",
601
+ ...fallbackMachine ? { machine: fallbackMachine } : {}
602
+ });
603
+ else stats.setPlacement({
604
+ decision: "here",
605
+ reason: hereReason,
606
+ slotWaitMs,
607
+ deviceSlotWaitMs: stats.deviceSlotWaitMs()
608
+ });
609
+ requireLocalBuild(buildMachine);
610
+ record.builtOn = "here";
611
+ phase("build", `compiling ${variant || "debug"} with Gradle`);
612
+ const built = await recipe.compile();
613
+ ccacheActivity = built.ccache ?? CCACHE_UNAVAILABLE;
614
+ phase("cache", `compilation cache ${ccacheActivityLine(ccacheActivity)}`);
615
+ if (!built.ok) {
616
+ const diagnostics = built.diagnostics;
617
+ for (const diag of diagnostics) writer.write({
618
+ src: "build",
619
+ level: "error",
620
+ event: "gradle_diagnostic",
621
+ msg: formatDiagnostic(diag)
622
+ });
623
+ phase("build", chalk.red(`FAILED after ${formatDuration(built.durationMs)}`));
624
+ const extracted = diagnostics.map(formatDiagnostic);
625
+ if (built.truncated > 0) extracted.push(`... and ${built.truncated} more diagnostic(s) in the log`);
626
+ phaseFailure = fail(built.code, built.reason, diagnostics.find((d) => d.remedy)?.remedy || built.remedy || null, {
627
+ lastBuildStatus: true,
628
+ diagnostics: extracted,
629
+ buildDiagnostics: diagnostics,
630
+ lines: extracted.length ? [] : tail(built.lastLines),
631
+ logPath: displayPath(root, buildLog)
632
+ });
633
+ throw new AndroidRecipeRefusal(phaseFailure);
634
+ }
635
+ apkPath = built.apkPath;
636
+ if (built.androidPackage) {
637
+ androidPackage = built.androidPackage;
638
+ record.bundleId = androidPackage;
639
+ }
640
+ stats.setBuildMs(built.durationMs);
641
+ phase("build", `ok (${formatDuration(built.durationMs)})`);
642
+ if (built.apkNote) phase("build", chalk.yellow(built.apkNote));
643
+ return apkPath;
644
+ }
645
+ async function validateCompiled() {
646
+ if (cacheIneligible) return "uncacheable";
647
+ const identity = await recipe.validate();
648
+ if (!identity) {
649
+ record.fingerprint = null;
650
+ record.cacheKey = null;
651
+ return "uncacheable";
652
+ }
653
+ storeHash = identity.hash;
654
+ storeKey = identity.key;
655
+ record.fingerprint = storeHash;
656
+ record.cacheKey = storeKey;
657
+ return "cacheable";
658
+ }
659
+ async function storeArtifact(artifactPath) {
660
+ if (cachePolicy.write) {
661
+ const local = recipe.cache(true);
662
+ try {
663
+ const stored = await storeTieredBuild({
664
+ local,
665
+ loadProvider: loadTieredProvider,
666
+ target: {
667
+ projectRoot: root,
668
+ platform: PLATFORM,
669
+ key: storeKey
670
+ },
671
+ sourcePath: artifactPath,
672
+ overwrite: !useBuildCache || swapFellBack,
673
+ warn: cacheWarn
674
+ });
675
+ providerUpload = stored.providerUpload;
676
+ providerName = stored.providerName ?? providerName;
677
+ } catch (err) {
678
+ phase("cache", chalk.yellow(`could not store the build: ${err?.message || err}`));
679
+ }
680
+ }
681
+ if (remote) uploadPending = uploadRemoteBuild({
682
+ logWriter: writer,
683
+ provider: remote.provider,
684
+ platform: PLATFORM,
685
+ projectRoot: root,
686
+ fingerprintHash: storeHash,
687
+ buildPath: artifactPath,
688
+ runOptions: recipe.legacyCache?.runOptions ?? null
689
+ });
690
+ }
691
+ try {
692
+ apkPath = await runArtifactLifecycle({
693
+ resolve: async () => {
694
+ if (!await resolveInitialFingerprint()) throw new AndroidRecipeRefusal(phaseFailure);
695
+ if (easBuild) return {
696
+ kind: "ready",
697
+ artifact: apkPath
698
+ };
699
+ await resolveRemoteArtifact();
700
+ if (apkPath) return {
701
+ kind: "cached",
702
+ artifact: apkPath
703
+ };
704
+ miss(reasonForMiss([]).reason);
705
+ return { kind: "miss" };
706
+ },
707
+ claim: useBuildCache ? awaitSharedBuild : void 0,
708
+ reuse: async (cached) => {
709
+ apkPath = await prepareCachedArtifact(cached);
710
+ if (apkPath) lateHit();
711
+ else if (swapFellBack) miss(reasonForMiss([]).reason);
712
+ return apkPath;
713
+ },
714
+ build: {
715
+ placement: {
716
+ select: placeBuild,
717
+ acquire: acquireRemoteArtifact
718
+ },
719
+ admit: async () => {
720
+ if (!await takeBuildSlot()) throw new AndroidRecipeRefusal(phaseFailure);
721
+ },
722
+ prepare: prepareSource,
723
+ revalidate: revalidateSource,
724
+ compile: compileHere,
725
+ validate: validateCompiled,
726
+ store: storeArtifact
727
+ },
728
+ release: () => {
729
+ if (openOffload.choice) closeOffload(openOffload.choice);
730
+ releaseHeldLock();
731
+ releaseHeldSlot();
732
+ }
733
+ });
734
+ } catch (error) {
735
+ releaseArtifact();
736
+ if (error instanceof OffloadRefusal) {
737
+ const { code, message, remedy } = error;
738
+ return {
739
+ ok: false,
740
+ failure: fail(code, message, remedy, { lastBuildStatus: true }),
741
+ ccache: ccacheActivity
742
+ };
743
+ }
744
+ if (error instanceof AndroidRecipeRefusal) return {
745
+ ok: false,
746
+ failure: error.failure,
747
+ ccache: ccacheActivity
748
+ };
749
+ throw error;
750
+ }
751
+ return {
752
+ ok: true,
753
+ artifact: {
754
+ apkPath,
755
+ handoff,
756
+ androidPackage,
757
+ release: releaseArtifact,
758
+ waitedForBuild,
759
+ ccache: ccacheActivity,
760
+ uploadPending,
761
+ providerUpload,
762
+ providerName,
763
+ remote,
764
+ abandonedRemote
765
+ }
766
+ };
767
+ }
768
+ function tail(lines, n = FALLBACK_LINES) {
769
+ return (Array.isArray(lines) ? lines : []).slice(-n);
770
+ }
771
+ //#endregion
772
+ export { acquireAndroidArtifact as t };