stim 1.16.0 → 1.17.1
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.
- package/dist/{activity-BxwdD81S.mjs → activity-IRvJY_ru.mjs} +23 -3
- package/dist/{agent-device-usage-output-Dpih1Tfa.mjs → agent-device-usage-output-BK41vnzN.mjs} +3 -3
- package/dist/{android-Ift4kVvt.mjs → android-B6rDWrTm.mjs} +681 -319
- package/dist/{android-B_NumVyQ.mjs → android-B_YNZy61.mjs} +7 -43
- package/dist/{android-r2rBL8kV.mjs → android-DE4v59Mb.mjs} +12 -3
- package/dist/{android-cas-8U4DPqeR.mjs → android-cas-ZFpjJBL_.mjs} +6 -5
- package/dist/{app-install-CKWdbXIX.mjs → app-install-VPkI5WEg.mjs} +6 -5
- package/dist/{budget-DjrURfQn.mjs → budget-CUDz870h.mjs} +62 -25
- package/dist/{build-plan-DjeSb2Xl.mjs → build-plan-BJSXzsCL.mjs} +345 -86
- package/dist/{idle-DYVymG92.mjs → build-progress-D0WkjCPd.mjs} +170 -233
- package/dist/{build-slots-rUk77xqh.mjs → build-slots-41v_d2x9.mjs} +35 -25
- package/dist/{cli-BkYZ_6hy.mjs → cli-BLQqDGlX.mjs} +17 -17
- package/dist/cli.mjs +1 -1
- package/dist/{client-BTY686R6.mjs → client-CxNotugx.mjs} +313 -44
- package/dist/collector-run.mjs +3 -3
- package/dist/{command-output-DnJMdEY-.mjs → command-output-CDAIFD-8.mjs} +3 -3
- package/dist/{created-devices-D5owjN4C.mjs → created-devices-CpvzBL25.mjs} +1 -1
- package/dist/{dependency-state-B6cMXZab.mjs → dependency-state-CLcBHH2M.mjs} +1 -1
- package/dist/{deps-urbmfXig.mjs → deps-CVxlsRFn.mjs} +1 -1
- package/dist/{dev-client-CroAIjjj.mjs → dev-client-B-XC4uRH.mjs} +2 -2
- package/dist/{device-DZQjF7H5.mjs → device-C8IOoJPv.mjs} +7 -7
- package/dist/device-capacity-ZC4XSZ4R.mjs +580 -0
- package/dist/device-host-worker.mjs +436 -154
- package/dist/{device-ios-BadmeL2V.mjs → device-ios-C2Um-XUR.mjs} +75 -44
- package/dist/{device-lease-D-hLqx1b.mjs → device-lease-DpoS36_u.mjs} +2 -2
- package/dist/{device-lease-run-CqjShqWW.mjs → device-lease-run-B8YltIH_.mjs} +3 -3
- package/dist/{device-pool-CUaEWMMm.mjs → device-pool-3o7s5lo4.mjs} +2 -2
- package/dist/{device-remote-D5WVWqOq.mjs → device-remote-hFSkpfQu.mjs} +12 -12
- package/dist/{doctor-Cimf-gGB.mjs → doctor-BF_435OC.mjs} +40 -58
- package/dist/{error-diagnostics-B9L4Ae0x.mjs → error-diagnostics-C-NMnhrk.mjs} +7 -7
- package/dist/{gc-Bqy4bjEu.mjs → gc-BsWKa_-d.mjs} +313 -30
- package/dist/{gradle-BsO1MdsH.mjs → gradle-bsBRy6i9.mjs} +3 -3
- package/dist/{guide-B_uyKTxW.mjs → guide-DC3n6-NH.mjs} +1776 -655
- package/dist/{guide-status-CjEZMAD4.mjs → guide-status-Du-ZQ3FD.mjs} +1 -1
- package/dist/hosted-android-DgU31wY3.mjs +32 -0
- package/dist/{hosted-client-a-WmqE2L.mjs → hosted-client-CdSoH-G6.mjs} +5 -6
- package/dist/hosted-ios-BTaVj4Jb.mjs +15 -0
- package/dist/{hosted-logs-D5g041Q3.mjs → hosted-logs-SxfBvSvc.mjs} +16 -16
- package/dist/{hosted-macos-BSZyIb3y.mjs → hosted-macos-CT9E3meL.mjs} +4 -4
- package/dist/{hosted-ios-CIWelIqf.mjs → hosted-native-CZLOrMrN.mjs} +77 -42
- package/dist/idle-BXG__1BA.mjs +176 -0
- package/dist/{idle-shutdown-CTsDnAde.mjs → idle-shutdown-IGyNV8Cr.mjs} +25 -17
- package/dist/{in-use-DvBw0Kxo.mjs → in-use-CvGOUn-x.mjs} +11 -11
- package/dist/{ios-aq3QoGI3.mjs → ios-C7JuT1bI.mjs} +173 -91
- package/dist/{ios-BaInn3sW.mjs → ios-DSfnPfse.mjs} +4 -4
- package/dist/{ios-device-FfCN_C8p.mjs → ios-device-pAN4t54-.mjs} +1 -1
- package/dist/ios-state-8L3lgum6.mjs +89 -0
- package/dist/{launch-verify-2DBRizjo.mjs → launch-verify-lTnWZikR.mjs} +2 -2
- package/dist/{logs-CHEVK8zD.mjs → logs-DNxKcok9.mjs} +36 -23
- package/dist/{macos-CSYEAFmF.mjs → macos-BEs8cwKx.mjs} +75 -34
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +2 -2
- package/dist/{metro-BeK1ZPz1.mjs → metro-g8NqiJp-.mjs} +1 -1
- package/dist/{metro-gateway-BJ-rxQAB.mjs → metro-gateway-CHqK0p-a.mjs} +22 -3
- package/dist/{named-ports-Bk_YXEmE.mjs → named-ports-C8m8EQs2.mjs} +2 -2
- package/dist/{native-run-NaBrRFtQ.mjs → native-run-D1Q56rAD.mjs} +17 -6
- package/dist/{native-runtime-DBzRnEKS.mjs → native-runtime-YhaSxP4v.mjs} +6 -6
- package/dist/offload-worker.mjs +9 -9
- package/dist/{teardown-CL6vDs2Y.mjs → ownership-BMLqyBgJ.mjs} +603 -26
- package/dist/{ownership-DiA16AsN.mjs → ownership-DyRQru0q.mjs} +2 -2
- package/dist/{ports-R2k4Knz3.mjs → ports-4nYqxPWC.mjs} +2 -2
- package/dist/{prebuild-D2fsca_Z.mjs → prebuild-D7RUMkwN.mjs} +3 -3
- package/dist/{preview-DhVbvPAZ.mjs → preview-C_JjkEy4.mjs} +7 -7
- package/dist/{project-Du91Ilvc.mjs → project-D47Rh0ly.mjs} +7 -2
- package/dist/{recordings-BIXOzVnr.mjs → recordings-BNgsD1Tr.mjs} +1 -1
- package/dist/{reload-rM9jV1fz.mjs → reload-byOjE3P4.mjs} +62 -35
- package/dist/{remote-cache-C8-V13XN.mjs → remote-cache-Ci4DQYYj.mjs} +3 -3
- package/dist/{run-f4vI6EnK.mjs → run-CkE9y0nc.mjs} +1 -1
- package/dist/{server-bare-CpPGmnmO.mjs → server-bare-BkrSxSsZ.mjs} +1 -1
- package/dist/{server-expo-MC0rHlOC.mjs → server-expo-CHTyXD0G.mjs} +2 -2
- package/dist/server-expo-CeakhI8P.mjs +2 -0
- package/dist/{settings-oTSkkNrM.mjs → settings-Cfd-0r2d.mjs} +10 -9
- package/dist/{settings-ZkDzkzvB.mjs → settings-SR9OmW9h.mjs} +3 -3
- package/dist/settings.schema.json +123 -76
- package/dist/{simslim-B8-7AoTI.mjs → simslim-BWNrc3M3.mjs} +3 -3
- package/dist/{slot-launch-B8VrHxn1.mjs → slot-launch-DmjBmpQV.mjs} +8 -8
- package/dist/{start-CX8Ig-U2.mjs → start-BEP-XmEA.mjs} +1 -1
- package/dist/{start-ClDfwXa4.mjs → start-BHIersSm.mjs} +11 -11
- package/dist/{state-DcW6vOd_.mjs → state-BKa8wNN5.mjs} +1 -1
- package/dist/{state-3DwSQIBK.mjs → state-DyaNTgfR.mjs} +1 -1
- package/dist/{state-C1jB7Q8q.mjs → state-PeQiJuIu.mjs} +2 -2
- package/dist/{stats-CEZ_i5je.mjs → stats-Bq4fJOdB.mjs} +7 -4
- package/dist/{status-Cs7VLg9D.mjs → status-CqeWbIWW.mjs} +128 -38
- package/dist/{status-BuvBFeII.mjs → status-CzS4bPO9.mjs} +4 -4
- package/dist/{stop-Bffozsac.mjs → stop-Cj01je8D.mjs} +48 -27
- package/dist/{stop-B0Yxtmiy.mjs → stop-D03prQnW.mjs} +4 -4
- package/dist/{stop-Dt_dM16A.mjs → stop-DOxwkH4j.mjs} +2 -2
- package/dist/{stop-cause-CidxIqup.mjs → stop-cause-rhmGfwvt.mjs} +1 -1
- package/dist/supervisor-run.d.mts +7 -4
- package/dist/supervisor-run.mjs +26 -28
- package/dist/{support-D4aYJniO.mjs → support-BE-tbOP9.mjs} +45 -20
- package/dist/{support-CAP8qDUF.mjs → support-WvD__tSV.mjs} +7 -7
- package/dist/{toolchain-DkUflduf.mjs → toolchain-CdrxgUeA.mjs} +8 -8
- package/dist/{warm-progress-CpGv3Wb6.mjs → warm-progress-DF4JRvYI.mjs} +3 -3
- package/dist/watchman-APYjXSQa.mjs +48 -0
- package/dist/{web-DWUaMp8P.mjs → web-DddUdJzr.mjs} +12 -13
- package/dist/web-run.mjs +4 -4
- package/dist/{workspace-process-lock-BL7Q0HCO.mjs → workspace-process-lock-2mNzOlaB.mjs} +3 -2
- package/dist/{workspace-state-B8-aJ1_5.mjs → workspace-state-DqcCbpE7.mjs} +2 -2
- package/dist/{workspaces-BV4pdHFz.mjs → workspaces-Bcib97-6.mjs} +3 -3
- package/dist/{worktree-K-xj9Db0.mjs → worktree-AK_m-d_m.mjs} +141 -40
- package/dist/{worktree-jnG8Iph5.mjs → worktree-CsAjSmEo.mjs} +1 -1
- package/package.json +5 -5
- package/dist/device-capacity-BHwxNBlu.mjs +0 -136
- package/dist/ios-state-DS5UpYTH.mjs +0 -48
- package/dist/machines-BIBUdhpj.mjs +0 -265
- package/dist/ownership-hCkPzGmr.mjs +0 -571
- package/dist/server-expo-CVqO2c35.mjs +0 -2
|
@@ -2,49 +2,433 @@ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
|
|
|
2
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
3
|
import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.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-
|
|
6
|
-
import { B as settingsLayers, F as settingFile, G as detectProviders, H as unknownSettingKeys, J as artifactCachePolicy, L as settingOriginScope, N as resolveCacheProviderConfig, P as resolveSettings, R as settingShapeErrors,
|
|
7
|
-
import {
|
|
8
|
-
import { l as resolveProjectMetro, o as pidExists, u as signalProcessTree } from "./metro-
|
|
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-
|
|
10
|
-
import { t as budgetGate } from "./budget-
|
|
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-
|
|
12
|
-
import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-
|
|
13
|
-
import { m as writeWorkspaceLaunch, n as MODE_EXPO, t as MODE_BARE } from "./state-
|
|
14
|
-
import {
|
|
15
|
-
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_NumVyQ.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";
|
|
16
15
|
import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-Cnz8CPnl.mjs";
|
|
17
|
-
import {
|
|
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
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-
|
|
20
|
-
import { t as cleanLine } from "./server-expo-
|
|
21
|
-
import { a as offloadBuild, c as placementLoad, d as OffloadRefusal, f as namedBuildMachine,
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
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";
|
|
26
27
|
import { t as spawnEntry } from "./spawn-entry-jw19m8Nl.mjs";
|
|
27
|
-
import { i as staleNativeDirRefusal, n as recordPrebuild, r as runPrebuild, t as planPrebuild } from "./prebuild-
|
|
28
|
-
import { c as formatDiagnostic, i as buildAndroid, o as productFlavorRefusal, s as readProductFlavors } from "./gradle-
|
|
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-
|
|
30
|
-
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-
|
|
31
|
-
import { a as
|
|
32
|
-
import { a as
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import { c as
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
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";
|
|
42
45
|
import { constants, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync } from "node:fs";
|
|
43
46
|
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
47
|
+
import { workspaceId } from "@stim-cli/core";
|
|
44
48
|
import { InvalidArgumentError } from "commander";
|
|
45
|
-
import { buildDiagnostics, machineCapacity, parseMachine } from "@stim-cli/core/state";
|
|
49
|
+
import { buildDiagnostics, hostedAndroidStatus, machineCapacity, parseMachine } from "@stim-cli/core/state";
|
|
46
50
|
import chalk from "chalk";
|
|
47
51
|
import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
|
|
52
|
+
//#region src/commands/android/remote.ts
|
|
53
|
+
async function connectAndroidHosting({ root, slot, machine, release, metroCheck, selectors, read = readHostedAndroid, resolveSerial = resolveOwnedAvdSerial, prepare = prepareHostedAndroid }) {
|
|
54
|
+
let recorded;
|
|
55
|
+
try {
|
|
56
|
+
recorded = read(root, slot)[slot];
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return { failure: {
|
|
59
|
+
code: "STIM_HOSTING_REFUSED",
|
|
60
|
+
message: error.message,
|
|
61
|
+
remedy: `Restore that slot's recorded machine and session from the host, then run stim stop${slot === "default" ? "" : ` --slot ${slot}`} to reconcile it.`
|
|
62
|
+
} };
|
|
63
|
+
}
|
|
64
|
+
if (recorded && recorded.machine !== machine) return { failure: {
|
|
65
|
+
code: "STIM_BAD_ARG",
|
|
66
|
+
message: `This workspace's Android emulator runs on ${recorded.machine}; run stim stop first.`
|
|
67
|
+
} };
|
|
68
|
+
if (!machine) return { target: null };
|
|
69
|
+
if (!release && !metroCheck) return { failure: {
|
|
70
|
+
code: "STIM_BAD_ARG",
|
|
71
|
+
message: "Hosted Debug runs require the local Metro supervisor; --no-metro-check cannot be used.",
|
|
72
|
+
remedy: "Run stim stop; stim start, then retry without --no-metro-check."
|
|
73
|
+
} };
|
|
74
|
+
const local = deviceSlotPlatforms(getProject(root), slot)?.android;
|
|
75
|
+
if (local?.owned && local.avdName) {
|
|
76
|
+
let running = true;
|
|
77
|
+
try {
|
|
78
|
+
const resolved = resolveSerial(local.avdName, { timeoutMs: 5e3 });
|
|
79
|
+
running = Boolean(resolved.serial) || !resolved.notRunning && !resolved.missing;
|
|
80
|
+
} catch {}
|
|
81
|
+
if (running) return { failure: {
|
|
82
|
+
code: "STIM_BAD_ARG",
|
|
83
|
+
message: `This workspace's Android emulator for slot ${slot} runs on this Mac; run stim stop first.`
|
|
84
|
+
} };
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
return { target: await prepare(machine, selectors, recorded) };
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return { failure: {
|
|
90
|
+
code: error.code ?? "STIM_HOSTING_REFUSED",
|
|
91
|
+
message: error.message,
|
|
92
|
+
...error instanceof Error && "remedy" in error && typeof error.remedy === "string" ? { remedy: error.remedy } : {}
|
|
93
|
+
} };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function selectAndroidPlacement({ target: selected, automatic = automaticDevicePlacement, checkBudget = budgetGate, note, phase, noWait, buildMachine, localSelectors, ...args }) {
|
|
97
|
+
const selectors = selected.kind === "hosted" ? {
|
|
98
|
+
...selected.systemImage ? { systemImage: selected.systemImage } : {},
|
|
99
|
+
...selected.deviceProfile ? { deviceProfile: selected.deviceProfile } : {}
|
|
100
|
+
} : {};
|
|
101
|
+
if (selected.kind !== "hosted" || selected.machine !== "auto") {
|
|
102
|
+
const connected = await connectAndroidHosting({
|
|
103
|
+
...args,
|
|
104
|
+
selectors,
|
|
105
|
+
machine: selected.kind === "hosted" ? selected.machine : null
|
|
106
|
+
});
|
|
107
|
+
if ("failure" in connected) return connected;
|
|
108
|
+
return {
|
|
109
|
+
target: selected,
|
|
110
|
+
hostedTarget: connected.target,
|
|
111
|
+
selectors,
|
|
112
|
+
budget: connected.target ? {
|
|
113
|
+
reclaimed: [],
|
|
114
|
+
refusal: null
|
|
115
|
+
} : await checkBudget({
|
|
116
|
+
root: args.root,
|
|
117
|
+
note
|
|
118
|
+
})
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const refusal = localSelectors(selected);
|
|
122
|
+
if (refusal) return { failure: refusal };
|
|
123
|
+
try {
|
|
124
|
+
const placed = await automatic({
|
|
125
|
+
root: args.root,
|
|
126
|
+
slot: args.slot,
|
|
127
|
+
platform: "android",
|
|
128
|
+
selectors,
|
|
129
|
+
buildMachine,
|
|
130
|
+
noWait
|
|
131
|
+
});
|
|
132
|
+
phase("placement:", devicePlacementLine(placed.placement, placed.skipped));
|
|
133
|
+
if (placed.target && !args.release && !args.metroCheck) return { failure: {
|
|
134
|
+
code: "STIM_BAD_ARG",
|
|
135
|
+
message: "Hosted Debug runs require the local Metro supervisor; --no-metro-check cannot be used.",
|
|
136
|
+
remedy: "Run stim stop; stim start, then retry without --no-metro-check."
|
|
137
|
+
} };
|
|
138
|
+
return {
|
|
139
|
+
target: placed.target ? {
|
|
140
|
+
...selected,
|
|
141
|
+
machine: placed.target.host.machine
|
|
142
|
+
} : {
|
|
143
|
+
kind: "emulator",
|
|
144
|
+
systemImage: selected.systemImage,
|
|
145
|
+
deviceProfile: selected.deviceProfile
|
|
146
|
+
},
|
|
147
|
+
hostedTarget: placed.target,
|
|
148
|
+
selectors,
|
|
149
|
+
budget: placed.target ? {
|
|
150
|
+
reclaimed: [],
|
|
151
|
+
refusal: null
|
|
152
|
+
} : await checkBudget({
|
|
153
|
+
root: args.root,
|
|
154
|
+
note
|
|
155
|
+
}),
|
|
156
|
+
devicePlacement: placed.placement
|
|
157
|
+
};
|
|
158
|
+
} catch (error) {
|
|
159
|
+
return { failure: {
|
|
160
|
+
code: error.code ?? "STIM_HOSTING_REFUSED",
|
|
161
|
+
message: error.message,
|
|
162
|
+
...error instanceof Error && "remedy" in error && typeof error.remedy === "string" ? { remedy: error.remedy } : {}
|
|
163
|
+
} };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/commands/android/result.ts
|
|
168
|
+
function androidFacts({ root, slot, serial, avdName = null, deviceName = null, systemImage = null, deviceProfile = null, fingerprint, cacheKey = null, variant = null, metroPort = null, cacheHit, cacheSkipped = false, waitedForBuild = null, appPath, bundleId, installSkipped = false, launched, ccache = CCACHE_UNAVAILABLE, logs, debugHttpHost = null, debugHttpHostNote = null, devClientUrl = null, durationMs, lease, devServer = null, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null, host }) {
|
|
169
|
+
return {
|
|
170
|
+
platform: PLATFORM,
|
|
171
|
+
...host ? { host } : {},
|
|
172
|
+
...slot && slot !== "default" ? { slot } : {},
|
|
173
|
+
serial: serial ?? null,
|
|
174
|
+
avdName: avdName ?? null,
|
|
175
|
+
deviceName: deviceName ?? avdName ?? null,
|
|
176
|
+
systemImage: systemImage ?? null,
|
|
177
|
+
deviceProfile: deviceProfile ?? null,
|
|
178
|
+
fingerprint: fingerprint ?? null,
|
|
179
|
+
cacheKey: cacheKey ?? null,
|
|
180
|
+
variant: variant ?? null,
|
|
181
|
+
metroPort: metroPort ?? null,
|
|
182
|
+
cacheHit: cacheLevel(cacheHit),
|
|
183
|
+
cacheSkipped: Boolean(cacheSkipped),
|
|
184
|
+
buildMachine,
|
|
185
|
+
...builtOn ? { builtOn } : {},
|
|
186
|
+
...offloadedTo ? { offloadedTo } : {},
|
|
187
|
+
...offloadFallback ? { offloadFallback } : {},
|
|
188
|
+
waitedForBuild: waitedForBuild ? {
|
|
189
|
+
pid: waitedForBuild.pid ?? null,
|
|
190
|
+
ms: waitedForBuild.ms ?? 0
|
|
191
|
+
} : null,
|
|
192
|
+
appPath: appPath ?? null,
|
|
193
|
+
bundleId: bundleId ?? null,
|
|
194
|
+
installSkipped: Boolean(installSkipped),
|
|
195
|
+
launched: launched === "unverified" || launched === "bundling" ? launched : Boolean(launched),
|
|
196
|
+
ccache,
|
|
197
|
+
debugHttpHost: debugHttpHost ?? null,
|
|
198
|
+
debugHttpHostNote: debugHttpHostNote ?? null,
|
|
199
|
+
devClientUrl: devClientUrl ?? null,
|
|
200
|
+
logs: logs ?? null,
|
|
201
|
+
...root && !host ? { agentDevice: { stateDir: workspaceAgentDeviceDir(root) } } : {},
|
|
202
|
+
durationMs: typeof durationMs === "number" && Number.isFinite(durationMs) ? durationMs : null,
|
|
203
|
+
...lease === void 0 ? {} : { lease },
|
|
204
|
+
...devServer ? { devServer } : {}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function lastBuildRecord({ devicePlacement, fingerprint, cacheKey, cacheHit, cacheSkipped = false, durationMs, appPath, bundleId, startedAt, status, errorCode = null, avdName = null, deviceName = null, missReason = null, diagnostics = null, configuration = null, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null }) {
|
|
208
|
+
const record = {
|
|
209
|
+
platform: PLATFORM,
|
|
210
|
+
avdName: avdName ?? null,
|
|
211
|
+
deviceName: deviceName ?? avdName ?? null,
|
|
212
|
+
fingerprint: fingerprint ?? null,
|
|
213
|
+
cacheKey: cacheKey ?? null,
|
|
214
|
+
cacheHit: cacheLevel(cacheHit),
|
|
215
|
+
cacheSkipped: Boolean(cacheSkipped),
|
|
216
|
+
durationMs: Number.isFinite(durationMs) ? durationMs : null,
|
|
217
|
+
appPath: appPath ?? null,
|
|
218
|
+
bundleId: bundleId ?? null,
|
|
219
|
+
startedAt,
|
|
220
|
+
status,
|
|
221
|
+
configuration: configuration ?? null
|
|
222
|
+
};
|
|
223
|
+
if (errorCode) record.errorCode = errorCode;
|
|
224
|
+
if (devicePlacement) record.devicePlacement = devicePlacement;
|
|
225
|
+
record.buildMachine = buildMachine;
|
|
226
|
+
if (builtOn) record.builtOn = builtOn;
|
|
227
|
+
if (offloadedTo) record.offloadedTo = offloadedTo;
|
|
228
|
+
if (offloadFallback) record.offloadFallback = offloadFallback;
|
|
229
|
+
if (missReason && !cacheLevel(cacheHit)) record.missReason = missReason;
|
|
230
|
+
const recorded = status === "failed" ? buildDiagnostics(diagnostics) : [];
|
|
231
|
+
if (recorded.length) record.diagnostics = recorded;
|
|
232
|
+
return record;
|
|
233
|
+
}
|
|
234
|
+
async function finishAndroidUpload(uploadPending, remote, phase) {
|
|
235
|
+
if (!uploadPending) return false;
|
|
236
|
+
const upload = await uploadPending;
|
|
237
|
+
if (upload?.uploaded) phase("cache", `uploaded (${remote?.name})`);
|
|
238
|
+
else if (upload?.timedOut) {
|
|
239
|
+
phase("cache", chalk.yellow(`${remote?.name} upload still running after ${formatDuration(UPLOAD_TIMEOUT_MS)}; not waiting`));
|
|
240
|
+
return true;
|
|
241
|
+
} else if (upload?.failed) {
|
|
242
|
+
const authNote = remote?.name === "eas" && isEasAuthFailureText(upload.failed) ? easAuthNote({
|
|
243
|
+
code: "logged-out",
|
|
244
|
+
reason: upload.failed,
|
|
245
|
+
phase: "upload"
|
|
246
|
+
}) : null;
|
|
247
|
+
phase("cache", chalk.yellow(authNote || `${remote?.name} upload failed: ${upload.failed}`));
|
|
248
|
+
}
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
function reportAndroidResult({ host, root, slot, json, useBuildCache, variant, release, metroCheck, metroPort, logsDir, serial, apkPath, androidPackage, installSkipped, record, waitedForBuild, remote, providerName, launchState, launchWarning, launched, ccache, durationMs, writer, emit, lease, recordRun, reclaimed = [], devServer = null, links }) {
|
|
252
|
+
const offloadedTo = record.offloadedTo ?? null;
|
|
253
|
+
recordRun({
|
|
254
|
+
failed: false,
|
|
255
|
+
cacheHit: cacheLevel(record.cacheHit),
|
|
256
|
+
waited: waitedForBuild,
|
|
257
|
+
durationMs,
|
|
258
|
+
offloadedTo
|
|
259
|
+
});
|
|
260
|
+
const facts = androidFacts({
|
|
261
|
+
host,
|
|
262
|
+
root,
|
|
263
|
+
slot,
|
|
264
|
+
serial,
|
|
265
|
+
avdName: record.avdName,
|
|
266
|
+
deviceName: record.deviceName,
|
|
267
|
+
systemImage: record.systemImage,
|
|
268
|
+
deviceProfile: record.deviceProfile,
|
|
269
|
+
debugHttpHost: launched.debugHttpHost ?? null,
|
|
270
|
+
debugHttpHostNote: launched.debugHttpHostNote ?? null,
|
|
271
|
+
devClientUrl: launched.devClientUrl ?? null,
|
|
272
|
+
fingerprint: record.fingerprint,
|
|
273
|
+
cacheKey: record.cacheKey,
|
|
274
|
+
variant,
|
|
275
|
+
metroPort,
|
|
276
|
+
cacheHit: record.cacheHit,
|
|
277
|
+
cacheSkipped: !useBuildCache,
|
|
278
|
+
waitedForBuild,
|
|
279
|
+
appPath: apkPath,
|
|
280
|
+
bundleId: androidPackage,
|
|
281
|
+
installSkipped,
|
|
282
|
+
launched: launchState,
|
|
283
|
+
ccache,
|
|
284
|
+
logs: logsDir,
|
|
285
|
+
durationMs,
|
|
286
|
+
lease,
|
|
287
|
+
devServer,
|
|
288
|
+
buildMachine: record.buildMachine,
|
|
289
|
+
builtOn: record.builtOn ?? void 0,
|
|
290
|
+
offloadedTo,
|
|
291
|
+
offloadFallback: record.offloadFallback ?? null
|
|
292
|
+
});
|
|
293
|
+
writer.close();
|
|
294
|
+
if (record.devicePlacement) facts.devicePlacement = record.devicePlacement;
|
|
295
|
+
if (json) emit(JSON.stringify({
|
|
296
|
+
...facts,
|
|
297
|
+
...links ? { links } : {},
|
|
298
|
+
...reclaimed.length ? { reclaimed } : {}
|
|
299
|
+
}));
|
|
300
|
+
else {
|
|
301
|
+
const summary = `${launchWarning ? "WARNING" : "OK"}: ${androidPackage} launched on ${host ? `${host.device?.name ?? "Android emulator"} on ${host.machine}` : serial}, ${release ? `${variant} (embedded JS, no Metro)` : `Metro port ${metroPort}`} (${cacheOutcome(record.cacheHit, remote?.name ?? providerName, offloadedTo)})`;
|
|
302
|
+
const outcome = launchWarning ? chalk.yellow(`${summary} -- ${launchWarning}`) : launchState === "unverified" ? chalk.yellow(`${summary} -- launch UNVERIFIED`) : launchState === "bundling" ? chalk.green(`${summary} -- bundle requested, still building`) : chalk.green(summary);
|
|
303
|
+
const deviceName = record.avdName || record.deviceName || serial;
|
|
304
|
+
const cacheResult = useBuildCache ? cacheOutcome(record.cacheHit, remote?.name ?? providerName, offloadedTo) : `bypassed; ${cacheOutcome(false, null, offloadedTo)}`;
|
|
305
|
+
const metroResult = release ? `embedded (${variant})` : !metroCheck ? `check skipped on port ${metroPort}` : launchState === "unverified" ? `state unverified on port ${metroPort}` : `running on port ${metroPort}${devServer ? ` (started: ${devServer.reason})` : ""}`;
|
|
306
|
+
emit([
|
|
307
|
+
outcome,
|
|
308
|
+
phaseLine("device", host ? `${deviceName} on ${host.machine}` : `${deviceName} (${serial})`),
|
|
309
|
+
phaseLine("app", androidPackage),
|
|
310
|
+
phaseLine("metro", metroResult),
|
|
311
|
+
phaseLine("cache", cacheResult),
|
|
312
|
+
...ccache.status === "not-run" ? [phaseLine("compilation cache", ccacheActivityLine(ccache))] : [],
|
|
313
|
+
phaseLine("logs", logsDir || "unavailable (remote device)"),
|
|
314
|
+
...!host ? [phaseLine("agent-device", `AGENT_DEVICE_STATE_DIR=${workspaceAgentDeviceDir(root)}`)] : []
|
|
315
|
+
].join("\n"));
|
|
316
|
+
if (links) console.error(chalk.dim(workspaceLinkLine(links)));
|
|
317
|
+
}
|
|
318
|
+
return facts;
|
|
319
|
+
}
|
|
320
|
+
function cacheOutcome(cacheHit, providerName = null, offloadedTo = null) {
|
|
321
|
+
if (cacheHit === "remote") return `cache hit from ${providerName || "the remote cache"}`;
|
|
322
|
+
if (cacheHit === "local") return "cache hit";
|
|
323
|
+
return offloadedTo ? `built on ${offloadedTo}` : "built";
|
|
324
|
+
}
|
|
325
|
+
function persistLastBuild({ recordBuild = recordFinishedBuild, root, record, startedAt, durationMs, status, errorCode = null, diagnostics = null, out }) {
|
|
326
|
+
const lastBuild = lastBuildRecord({
|
|
327
|
+
...record,
|
|
328
|
+
startedAt,
|
|
329
|
+
durationMs,
|
|
330
|
+
status,
|
|
331
|
+
errorCode,
|
|
332
|
+
diagnostics
|
|
333
|
+
});
|
|
334
|
+
try {
|
|
335
|
+
recordBuild(root, lastBuild);
|
|
336
|
+
} catch (err) {
|
|
337
|
+
out(phaseLine("state", chalk.yellow(`could not record lastBuild: ${err?.message || err}`)));
|
|
338
|
+
}
|
|
339
|
+
return lastBuild;
|
|
340
|
+
}
|
|
341
|
+
//#endregion
|
|
342
|
+
//#region src/commands/android/hosted.ts
|
|
343
|
+
async function finishHostedAndroidRun({ target, artifact, root, slot = "default", release, isExpo, metroPort, logsDir, selectors, place, writePlacement, verifyLaunch: verify, writeLaunch, readApkPackage, resolveDevClientScheme, now, started, startedAt, fail, phase, out, recordBuild, ...report }) {
|
|
344
|
+
const packageName = artifact.androidPackage ?? readApkPackage(artifact.apkPath);
|
|
345
|
+
if (!artifact.apkPath || !packageName) return fail("STIM_INSTALL_FAILED", "The built APK has no package identity.");
|
|
346
|
+
const launchedAt = now();
|
|
347
|
+
try {
|
|
348
|
+
const run = await place(target, {
|
|
349
|
+
root,
|
|
350
|
+
slot,
|
|
351
|
+
bundle: artifact.apkPath,
|
|
352
|
+
handoff: artifact.handoff,
|
|
353
|
+
bundleId: packageName,
|
|
354
|
+
release,
|
|
355
|
+
metroPort,
|
|
356
|
+
selectors,
|
|
357
|
+
...!release && isExpo ? { devClientScheme: resolveDevClientScheme(root, artifact.apkPath) ?? void 0 } : {},
|
|
358
|
+
reserved: (placement) => writePlacement(root, slot, placement),
|
|
359
|
+
note: out
|
|
360
|
+
});
|
|
361
|
+
writePlacement(root, slot, run.placement);
|
|
362
|
+
writeLaunch(root, "android", {
|
|
363
|
+
appId: packageName,
|
|
364
|
+
deviceId: run.placement.session,
|
|
365
|
+
metroPort,
|
|
366
|
+
release,
|
|
367
|
+
launchedAt: new Date(launchedAt).toISOString()
|
|
368
|
+
});
|
|
369
|
+
let launched = run.launched;
|
|
370
|
+
if (!release) {
|
|
371
|
+
const evidence = await verify({
|
|
372
|
+
requireBundleResponse: true,
|
|
373
|
+
platform: "android",
|
|
374
|
+
logsDir: logsDir ?? void 0,
|
|
375
|
+
since: launchedAt,
|
|
376
|
+
metroPort,
|
|
377
|
+
mode: isExpo ? MODE_EXPO : MODE_BARE,
|
|
378
|
+
slot: launchSlotScope(root, slot),
|
|
379
|
+
platformShared: siblingPlatformSlots(root, "android", slot).length > 0
|
|
380
|
+
});
|
|
381
|
+
if (evidence.fatal) return fail("STIM_LAUNCH_FAILED", "Metro could not deliver the hosted app bundle.", "Run stim logs --errors and fix the bundle error.");
|
|
382
|
+
launched = evidence.verified ? true : evidence.requested ? "bundling" : "unverified";
|
|
383
|
+
}
|
|
384
|
+
if (launched === "unverified") out(`Launch on ${target.host.machine} is unverified; check the hosted app, then retry stim android --remote ${target.host.machine}.`);
|
|
385
|
+
const host = hostedAndroidStatus(run.placement);
|
|
386
|
+
report.record.deviceName = host.device?.name ?? null;
|
|
387
|
+
report.record.bundleId = packageName;
|
|
388
|
+
persistLastBuild({
|
|
389
|
+
recordBuild,
|
|
390
|
+
root,
|
|
391
|
+
record: report.record,
|
|
392
|
+
startedAt,
|
|
393
|
+
durationMs: now() - started,
|
|
394
|
+
status: "success",
|
|
395
|
+
out
|
|
396
|
+
});
|
|
397
|
+
await finishAndroidUpload(artifact.uploadPending, artifact.remote, phase);
|
|
398
|
+
await artifact.providerUpload;
|
|
399
|
+
return {
|
|
400
|
+
ok: true,
|
|
401
|
+
facts: reportAndroidResult({
|
|
402
|
+
...report,
|
|
403
|
+
root,
|
|
404
|
+
slot,
|
|
405
|
+
release,
|
|
406
|
+
metroPort,
|
|
407
|
+
logsDir,
|
|
408
|
+
host,
|
|
409
|
+
serial: null,
|
|
410
|
+
apkPath: artifact.apkPath,
|
|
411
|
+
androidPackage: packageName,
|
|
412
|
+
installSkipped: false,
|
|
413
|
+
waitedForBuild: artifact.waitedForBuild,
|
|
414
|
+
remote: artifact.remote,
|
|
415
|
+
providerName: artifact.providerName,
|
|
416
|
+
launchState: launched,
|
|
417
|
+
launched: {},
|
|
418
|
+
ccache: artifact.ccache,
|
|
419
|
+
durationMs: now() - started
|
|
420
|
+
})
|
|
421
|
+
};
|
|
422
|
+
} catch (error) {
|
|
423
|
+
return fail(error.code ?? "STIM_HOSTING_REFUSED", error.message, `Retry stim android --remote ${target.host.machine}, or run stim stop to reconcile the recorded placement.`);
|
|
424
|
+
} finally {
|
|
425
|
+
if (artifact.swapDir) rmSync(artifact.swapDir, {
|
|
426
|
+
recursive: true,
|
|
427
|
+
force: true
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
//#endregion
|
|
48
432
|
//#region src/engine/apk-swap.ts
|
|
49
433
|
const ANDROID_BUNDLE_NAME = "index.android.bundle";
|
|
50
434
|
const LAST_LINES = 5;
|
|
@@ -419,10 +803,10 @@ function fail$1(code, message, remedy, extra = {}) {
|
|
|
419
803
|
extra
|
|
420
804
|
};
|
|
421
805
|
}
|
|
422
|
-
async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo, device, physical, remote: remoteTarget, buildPlan, cacheProviderConfig, requestedBuildCache, easBuild, androidPackage: initialPackage, record, maxBuilds, progress }, { deviceAbi, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, loadCacheProviderModule, acquireSlot, releaseSlot, planPrebuildFor, prebuild, build, ccacheFor, swapApk, readState, now }) {
|
|
423
|
-
const { phase, out, estimates, stats, step, miss, hit: lateHit, place, waitingOn } = progress;
|
|
806
|
+
async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo, device, physical, hostedAbi, remote: remoteTarget, buildPlan, cacheProviderConfig, requestedBuildCache, easBuild, androidPackage: initialPackage, record, maxBuilds, progress }, { deviceAbi, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, loadCacheProviderModule, acquireSlot, releaseSlot, planPrebuildFor, prebuild, build, ccacheFor, swapApk, readState, now }) {
|
|
807
|
+
const { phase, out, estimates, stats, step, miss, hit: lateHit, place, waitingOn, waitingFor } = progress;
|
|
424
808
|
let fallbackMachine = null;
|
|
425
|
-
let hereReason = "no
|
|
809
|
+
let hereReason = "no remote Mac is paired";
|
|
426
810
|
let slotWaitMs;
|
|
427
811
|
const buildMachine = record.buildMachine ?? "auto";
|
|
428
812
|
const fallBack = (reason, line = reason) => {
|
|
@@ -470,7 +854,8 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
470
854
|
deviceAbi,
|
|
471
855
|
compiler: cas?.id,
|
|
472
856
|
buildProfile,
|
|
473
|
-
targetAbiOnly: buildPlan.targetAbiOnly
|
|
857
|
+
targetAbiOnly: buildPlan.targetAbiOnly,
|
|
858
|
+
hostedAbi
|
|
474
859
|
});
|
|
475
860
|
let hash = "";
|
|
476
861
|
let providerUpload = null;
|
|
@@ -487,6 +872,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
487
872
|
let storeKey = "";
|
|
488
873
|
let storeSources = [];
|
|
489
874
|
let apkPath = null;
|
|
875
|
+
let handoff = null;
|
|
490
876
|
let swapFellBack = false;
|
|
491
877
|
async function resolveInitialFingerprint() {
|
|
492
878
|
if (easBuild?.ok) {
|
|
@@ -501,7 +887,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
501
887
|
stats.setCacheKey(cacheKey);
|
|
502
888
|
return true;
|
|
503
889
|
}
|
|
504
|
-
step("cache-lookup");
|
|
890
|
+
if (useBuildCache) step("cache-lookup");
|
|
505
891
|
const fingerprintTimer = stepTimer(now);
|
|
506
892
|
try {
|
|
507
893
|
const computed = await fingerprint(root, { platform: PLATFORM });
|
|
@@ -727,7 +1113,8 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
727
1113
|
max: maxBuilds,
|
|
728
1114
|
root,
|
|
729
1115
|
logFile: buildLog,
|
|
730
|
-
out
|
|
1116
|
+
out,
|
|
1117
|
+
waitingFor: (info) => waitingFor?.(info, "build-slot")
|
|
731
1118
|
});
|
|
732
1119
|
slotWaitMs = buildSlot.slotWaitMs;
|
|
733
1120
|
} catch (err) {
|
|
@@ -870,6 +1257,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
870
1257
|
}
|
|
871
1258
|
const { timings } = outcome;
|
|
872
1259
|
apkPath = stored;
|
|
1260
|
+
handoff = outcome.handoff ?? null;
|
|
873
1261
|
record.builtOn = outcome.machine;
|
|
874
1262
|
record.offloadedTo = outcome.machine;
|
|
875
1263
|
stats.setPlacement({
|
|
@@ -977,13 +1365,15 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
977
1365
|
if (offload) stats.setPlacement({
|
|
978
1366
|
decision: "fell-back",
|
|
979
1367
|
slotWaitMs,
|
|
1368
|
+
deviceSlotWaitMs: stats.deviceSlotWaitMs(),
|
|
980
1369
|
reason: record.offloadFallback ?? "offload failed",
|
|
981
1370
|
...fallbackMachine ? { machine: fallbackMachine } : {}
|
|
982
1371
|
});
|
|
983
1372
|
else stats.setPlacement({
|
|
984
1373
|
decision: "here",
|
|
985
1374
|
reason: hereReason,
|
|
986
|
-
slotWaitMs
|
|
1375
|
+
slotWaitMs,
|
|
1376
|
+
deviceSlotWaitMs: stats.deviceSlotWaitMs()
|
|
987
1377
|
});
|
|
988
1378
|
record.builtOn = "here";
|
|
989
1379
|
phase("build", `compiling ${variant || "debug"} with Gradle`);
|
|
@@ -1083,223 +1473,53 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
1083
1473
|
}
|
|
1084
1474
|
}
|
|
1085
1475
|
if (remote) uploadPending = uploadRemoteBuild({
|
|
1086
|
-
logWriter: writer,
|
|
1087
|
-
provider: remote.provider,
|
|
1088
|
-
platform: PLATFORM,
|
|
1089
|
-
projectRoot: root,
|
|
1090
|
-
fingerprintHash: storeHash,
|
|
1091
|
-
buildPath: apkPath,
|
|
1092
|
-
runOptions: remoteRunOptions
|
|
1093
|
-
});
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
} catch (error) {
|
|
1097
|
-
if (!(error instanceof OffloadRefusal)) throw error;
|
|
1098
|
-
const refusal = error;
|
|
1099
|
-
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
1100
|
-
return false;
|
|
1101
|
-
} finally {
|
|
1102
|
-
if (openOffload.choice) closeOffload(openOffload.choice);
|
|
1103
|
-
releaseHeldLock();
|
|
1104
|
-
releaseHeldSlot();
|
|
1105
|
-
}
|
|
1106
|
-
return true;
|
|
1107
|
-
}
|
|
1108
|
-
if (!easBuild) {
|
|
1109
|
-
await prepareCachedArtifact();
|
|
1110
|
-
if (apkPath) lateHit();
|
|
1111
|
-
else if (swapFellBack) miss(reasonForMiss([]).reason);
|
|
1112
|
-
if (!await buildArtifact()) return refused();
|
|
1113
|
-
}
|
|
1114
|
-
return {
|
|
1115
|
-
ok: true,
|
|
1116
|
-
artifact: {
|
|
1117
|
-
apkPath,
|
|
1118
|
-
androidPackage,
|
|
1119
|
-
swapDir,
|
|
1120
|
-
waitedForBuild,
|
|
1121
|
-
ccache: ccacheActivity,
|
|
1122
|
-
uploadPending,
|
|
1123
|
-
providerUpload,
|
|
1124
|
-
providerName,
|
|
1125
|
-
remote,
|
|
1126
|
-
abandonedRemote
|
|
1127
|
-
}
|
|
1128
|
-
};
|
|
1129
|
-
}
|
|
1130
|
-
function tail(lines, n = FALLBACK_LINES) {
|
|
1131
|
-
return (Array.isArray(lines) ? lines : []).slice(-n);
|
|
1132
|
-
}
|
|
1133
|
-
//#endregion
|
|
1134
|
-
//#region src/commands/android/result.ts
|
|
1135
|
-
function androidFacts({ root, slot, serial, avdName = null, deviceName = null, systemImage = null, deviceProfile = null, fingerprint, cacheKey = null, variant = null, metroPort = null, cacheHit, cacheSkipped = false, waitedForBuild = null, appPath, bundleId, installSkipped = false, launched, ccache = CCACHE_UNAVAILABLE, logs, debugHttpHost = null, debugHttpHostNote = null, devClientUrl = null, durationMs, lease, devServer = null, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null }) {
|
|
1136
|
-
return {
|
|
1137
|
-
platform: PLATFORM,
|
|
1138
|
-
...slot && slot !== "default" ? { slot } : {},
|
|
1139
|
-
serial: serial ?? null,
|
|
1140
|
-
avdName: avdName ?? null,
|
|
1141
|
-
deviceName: deviceName ?? avdName ?? null,
|
|
1142
|
-
systemImage: systemImage ?? null,
|
|
1143
|
-
deviceProfile: deviceProfile ?? null,
|
|
1144
|
-
fingerprint: fingerprint ?? null,
|
|
1145
|
-
cacheKey: cacheKey ?? null,
|
|
1146
|
-
variant: variant ?? null,
|
|
1147
|
-
metroPort: metroPort ?? null,
|
|
1148
|
-
cacheHit: cacheLevel(cacheHit),
|
|
1149
|
-
cacheSkipped: Boolean(cacheSkipped),
|
|
1150
|
-
buildMachine,
|
|
1151
|
-
...builtOn ? { builtOn } : {},
|
|
1152
|
-
...offloadedTo ? { offloadedTo } : {},
|
|
1153
|
-
...offloadFallback ? { offloadFallback } : {},
|
|
1154
|
-
waitedForBuild: waitedForBuild ? {
|
|
1155
|
-
pid: waitedForBuild.pid ?? null,
|
|
1156
|
-
ms: waitedForBuild.ms ?? 0
|
|
1157
|
-
} : null,
|
|
1158
|
-
appPath: appPath ?? null,
|
|
1159
|
-
bundleId: bundleId ?? null,
|
|
1160
|
-
installSkipped: Boolean(installSkipped),
|
|
1161
|
-
launched: launched === "unverified" || launched === "bundling" ? launched : Boolean(launched),
|
|
1162
|
-
ccache,
|
|
1163
|
-
debugHttpHost: debugHttpHost ?? null,
|
|
1164
|
-
debugHttpHostNote: debugHttpHostNote ?? null,
|
|
1165
|
-
devClientUrl: devClientUrl ?? null,
|
|
1166
|
-
logs: logs ?? null,
|
|
1167
|
-
...root ? { agentDevice: { stateDir: workspaceAgentDeviceDir(root) } } : {},
|
|
1168
|
-
durationMs: typeof durationMs === "number" && Number.isFinite(durationMs) ? durationMs : null,
|
|
1169
|
-
...lease === void 0 ? {} : { lease },
|
|
1170
|
-
...devServer ? { devServer } : {}
|
|
1171
|
-
};
|
|
1172
|
-
}
|
|
1173
|
-
function lastBuildRecord({ fingerprint, cacheKey, cacheHit, cacheSkipped = false, durationMs, appPath, bundleId, startedAt, status, errorCode = null, avdName = null, deviceName = null, missReason = null, diagnostics = null, configuration = null, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null }) {
|
|
1174
|
-
const record = {
|
|
1175
|
-
platform: PLATFORM,
|
|
1176
|
-
avdName: avdName ?? null,
|
|
1177
|
-
deviceName: deviceName ?? avdName ?? null,
|
|
1178
|
-
fingerprint: fingerprint ?? null,
|
|
1179
|
-
cacheKey: cacheKey ?? null,
|
|
1180
|
-
cacheHit: cacheLevel(cacheHit),
|
|
1181
|
-
cacheSkipped: Boolean(cacheSkipped),
|
|
1182
|
-
durationMs: Number.isFinite(durationMs) ? durationMs : null,
|
|
1183
|
-
appPath: appPath ?? null,
|
|
1184
|
-
bundleId: bundleId ?? null,
|
|
1185
|
-
startedAt,
|
|
1186
|
-
status,
|
|
1187
|
-
configuration: configuration ?? null
|
|
1188
|
-
};
|
|
1189
|
-
if (errorCode) record.errorCode = errorCode;
|
|
1190
|
-
record.buildMachine = buildMachine;
|
|
1191
|
-
if (builtOn) record.builtOn = builtOn;
|
|
1192
|
-
if (offloadedTo) record.offloadedTo = offloadedTo;
|
|
1193
|
-
if (offloadFallback) record.offloadFallback = offloadFallback;
|
|
1194
|
-
if (missReason && !cacheLevel(cacheHit)) record.missReason = missReason;
|
|
1195
|
-
const recorded = status === "failed" ? buildDiagnostics(diagnostics) : [];
|
|
1196
|
-
if (recorded.length) record.diagnostics = recorded;
|
|
1197
|
-
return record;
|
|
1198
|
-
}
|
|
1199
|
-
async function finishAndroidUpload(uploadPending, remote, phase) {
|
|
1200
|
-
if (!uploadPending) return false;
|
|
1201
|
-
const upload = await uploadPending;
|
|
1202
|
-
if (upload?.uploaded) phase("cache", `uploaded (${remote?.name})`);
|
|
1203
|
-
else if (upload?.timedOut) {
|
|
1204
|
-
phase("cache", chalk.yellow(`${remote?.name} upload still running after ${formatDuration(UPLOAD_TIMEOUT_MS)}; not waiting`));
|
|
1476
|
+
logWriter: writer,
|
|
1477
|
+
provider: remote.provider,
|
|
1478
|
+
platform: PLATFORM,
|
|
1479
|
+
projectRoot: root,
|
|
1480
|
+
fingerprintHash: storeHash,
|
|
1481
|
+
buildPath: apkPath,
|
|
1482
|
+
runOptions: remoteRunOptions
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
} catch (error) {
|
|
1487
|
+
if (!(error instanceof OffloadRefusal)) throw error;
|
|
1488
|
+
const refusal = error;
|
|
1489
|
+
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
1490
|
+
return false;
|
|
1491
|
+
} finally {
|
|
1492
|
+
if (openOffload.choice) closeOffload(openOffload.choice);
|
|
1493
|
+
releaseHeldLock();
|
|
1494
|
+
releaseHeldSlot();
|
|
1495
|
+
}
|
|
1205
1496
|
return true;
|
|
1206
|
-
} else if (upload?.failed) {
|
|
1207
|
-
const authNote = remote?.name === "eas" && isEasAuthFailureText(upload.failed) ? easAuthNote({
|
|
1208
|
-
code: "logged-out",
|
|
1209
|
-
reason: upload.failed,
|
|
1210
|
-
phase: "upload"
|
|
1211
|
-
}) : null;
|
|
1212
|
-
phase("cache", chalk.yellow(authNote || `${remote?.name} upload failed: ${upload.failed}`));
|
|
1213
1497
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
failed: false,
|
|
1220
|
-
cacheHit: cacheLevel(record.cacheHit),
|
|
1221
|
-
waited: waitedForBuild,
|
|
1222
|
-
durationMs,
|
|
1223
|
-
offloadedTo
|
|
1224
|
-
});
|
|
1225
|
-
const facts = androidFacts({
|
|
1226
|
-
root,
|
|
1227
|
-
slot,
|
|
1228
|
-
serial,
|
|
1229
|
-
avdName: record.avdName,
|
|
1230
|
-
deviceName: record.deviceName,
|
|
1231
|
-
systemImage: record.systemImage,
|
|
1232
|
-
deviceProfile: record.deviceProfile,
|
|
1233
|
-
debugHttpHost: launched.debugHttpHost ?? null,
|
|
1234
|
-
debugHttpHostNote: launched.debugHttpHostNote ?? null,
|
|
1235
|
-
devClientUrl: launched.devClientUrl ?? null,
|
|
1236
|
-
fingerprint: record.fingerprint,
|
|
1237
|
-
cacheKey: record.cacheKey,
|
|
1238
|
-
variant,
|
|
1239
|
-
metroPort,
|
|
1240
|
-
cacheHit: record.cacheHit,
|
|
1241
|
-
cacheSkipped: !useBuildCache,
|
|
1242
|
-
waitedForBuild,
|
|
1243
|
-
appPath: apkPath,
|
|
1244
|
-
bundleId: androidPackage,
|
|
1245
|
-
installSkipped,
|
|
1246
|
-
launched: launchState,
|
|
1247
|
-
ccache,
|
|
1248
|
-
logs: logsDir,
|
|
1249
|
-
durationMs,
|
|
1250
|
-
lease,
|
|
1251
|
-
devServer,
|
|
1252
|
-
buildMachine: record.buildMachine,
|
|
1253
|
-
builtOn: record.builtOn ?? void 0,
|
|
1254
|
-
offloadedTo,
|
|
1255
|
-
offloadFallback: record.offloadFallback ?? null
|
|
1256
|
-
});
|
|
1257
|
-
writer.close();
|
|
1258
|
-
if (json) emit(JSON.stringify({
|
|
1259
|
-
...facts,
|
|
1260
|
-
...links ? { links } : {},
|
|
1261
|
-
...reclaimed.length ? { reclaimed } : {}
|
|
1262
|
-
}));
|
|
1263
|
-
else {
|
|
1264
|
-
const summary = `${launchWarning ? "WARNING" : "OK"}: ${androidPackage} launched on ${serial}, ${release ? `${variant} (embedded JS, no Metro)` : `Metro port ${metroPort}`} (${cacheOutcome(record.cacheHit, remote?.name ?? providerName, offloadedTo)})`;
|
|
1265
|
-
const outcome = launchWarning ? chalk.yellow(`${summary} -- ${launchWarning}`) : launchState === "unverified" ? chalk.yellow(`${summary} -- launch UNVERIFIED`) : launchState === "bundling" ? chalk.green(`${summary} -- bundle requested, still building`) : chalk.green(summary);
|
|
1266
|
-
const deviceName = record.avdName || record.deviceName || serial;
|
|
1267
|
-
const cacheResult = useBuildCache ? cacheOutcome(record.cacheHit, remote?.name ?? providerName, offloadedTo) : `bypassed; ${cacheOutcome(false, null, offloadedTo)}`;
|
|
1268
|
-
const metroResult = release ? `embedded (${variant})` : !metroCheck ? `check skipped on port ${metroPort}` : launchState === "unverified" ? `state unverified on port ${metroPort}` : `running on port ${metroPort}${devServer ? ` (started: ${devServer.reason})` : ""}`;
|
|
1269
|
-
emit([
|
|
1270
|
-
outcome,
|
|
1271
|
-
phaseLine("device", `${deviceName} (${serial})`),
|
|
1272
|
-
phaseLine("app", androidPackage),
|
|
1273
|
-
phaseLine("metro", metroResult),
|
|
1274
|
-
phaseLine("cache", cacheResult),
|
|
1275
|
-
...ccache.status === "not-run" ? [phaseLine("compilation cache", ccacheActivityLine(ccache))] : [],
|
|
1276
|
-
phaseLine("logs", logsDir || "unavailable (remote device)"),
|
|
1277
|
-
phaseLine("agent-device", `AGENT_DEVICE_STATE_DIR=${workspaceAgentDeviceDir(root)}`)
|
|
1278
|
-
].join("\n"));
|
|
1279
|
-
if (links) console.error(chalk.dim(workspaceLinkLine(links)));
|
|
1498
|
+
if (!easBuild) {
|
|
1499
|
+
await prepareCachedArtifact();
|
|
1500
|
+
if (apkPath) lateHit();
|
|
1501
|
+
else if (swapFellBack) miss(reasonForMiss([]).reason);
|
|
1502
|
+
if (!await buildArtifact()) return refused();
|
|
1280
1503
|
}
|
|
1281
|
-
return
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1504
|
+
return {
|
|
1505
|
+
ok: true,
|
|
1506
|
+
artifact: {
|
|
1507
|
+
apkPath,
|
|
1508
|
+
handoff,
|
|
1509
|
+
androidPackage,
|
|
1510
|
+
swapDir,
|
|
1511
|
+
waitedForBuild,
|
|
1512
|
+
ccache: ccacheActivity,
|
|
1513
|
+
uploadPending,
|
|
1514
|
+
providerUpload,
|
|
1515
|
+
providerName,
|
|
1516
|
+
remote,
|
|
1517
|
+
abandonedRemote
|
|
1518
|
+
}
|
|
1519
|
+
};
|
|
1287
1520
|
}
|
|
1288
|
-
function
|
|
1289
|
-
|
|
1290
|
-
...record,
|
|
1291
|
-
startedAt,
|
|
1292
|
-
durationMs,
|
|
1293
|
-
status,
|
|
1294
|
-
errorCode,
|
|
1295
|
-
diagnostics
|
|
1296
|
-
});
|
|
1297
|
-
try {
|
|
1298
|
-
recordBuild(root, lastBuild);
|
|
1299
|
-
} catch (err) {
|
|
1300
|
-
out(phaseLine("state", chalk.yellow(`could not record lastBuild: ${err?.message || err}`)));
|
|
1301
|
-
}
|
|
1302
|
-
return lastBuild;
|
|
1521
|
+
function tail(lines, n = FALLBACK_LINES) {
|
|
1522
|
+
return (Array.isArray(lines) ? lines : []).slice(-n);
|
|
1303
1523
|
}
|
|
1304
1524
|
//#endregion
|
|
1305
1525
|
//#region src/commands/android/collector.ts
|
|
@@ -1618,7 +1838,7 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1618
1838
|
});
|
|
1619
1839
|
if (booted.failed) {
|
|
1620
1840
|
const diag = diagnoseBootFailure(booted, emuLog, runCommand, physical);
|
|
1621
|
-
return fail(
|
|
1841
|
+
return fail(diag.code, diag.message, diag.remedy, {
|
|
1622
1842
|
lines: diag.lines,
|
|
1623
1843
|
logPath: diag.logPath ? displayPath(root, diag.logPath) : null
|
|
1624
1844
|
});
|
|
@@ -1951,12 +2171,23 @@ function reportMetroRoute({ launched, remote, metroPort, androidPackage, serial,
|
|
|
1951
2171
|
});
|
|
1952
2172
|
}
|
|
1953
2173
|
function diagnoseBootFailure(booted, logFile, runCommand, physical) {
|
|
1954
|
-
return
|
|
2174
|
+
if (booted.code) return {
|
|
2175
|
+
code: booted.code,
|
|
2176
|
+
message: booted.reason ?? "The emulator did not boot.",
|
|
2177
|
+
remedy: booted.remedy ?? null,
|
|
2178
|
+
lines: [],
|
|
2179
|
+
logPath: null
|
|
2180
|
+
};
|
|
2181
|
+
const diag = noDeviceDiagnostic({
|
|
1955
2182
|
reason: booted.reason ?? "The emulator did not boot.",
|
|
1956
2183
|
logFile,
|
|
1957
2184
|
remedy: booted.remedy ?? `Run \`stim status\` to see what Stim thinks it owns; re-running \`${runCommand}\` creates a fresh owned AVD.`,
|
|
1958
2185
|
localEmulator: !physical
|
|
1959
2186
|
});
|
|
2187
|
+
return {
|
|
2188
|
+
code: NO_DEVICE,
|
|
2189
|
+
...diag
|
|
2190
|
+
};
|
|
1960
2191
|
}
|
|
1961
2192
|
//#endregion
|
|
1962
2193
|
//#region src/commands/android/plan.ts
|
|
@@ -2045,13 +2276,78 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
2045
2276
|
if (physical && deviceFlag === "") return fail("STIM_BAD_ARG", "--device was given an empty serial.", "Pass `--device` on its own to take the first connected device this workspace can lease, or `--device <serial>` to name one.");
|
|
2046
2277
|
if (physical && commandRemoteBackend) return fail("STIM_BAD_ARG", "--device installs on a device connected to this machine, and --remote installs on a remote one.", "Pass only one of --device and --remote.");
|
|
2047
2278
|
const noWait = waitFlag === false;
|
|
2048
|
-
|
|
2049
|
-
if (waitConflict) return fail("STIM_BAD_ARG", "--wait and --no-wait ask for opposite things.", "Pass `--wait <seconds>` to wait for the lease, or `--no-wait` to install without one.");
|
|
2050
|
-
if (waitFlagged && !physical) return fail("STIM_BAD_ARG", "--wait and --no-wait only apply to a `--device` run.", "This workspace owns its emulator, so nothing contends for it. Drop the flag, or pass `--device`.");
|
|
2279
|
+
if (waitConflict) return fail("STIM_BAD_ARG", "--wait and --no-wait ask for opposite things.", "Pass only one of `--wait <seconds>` and `--no-wait`.");
|
|
2051
2280
|
const waitParsed = parseDeviceWait(noWait ? void 0 : waitFlag);
|
|
2052
|
-
if ("error" in waitParsed) return fail("STIM_BAD_ARG", waitParsed.error, "Pass a whole number of seconds, e.g. --wait 90. `--wait 0` refuses a
|
|
2281
|
+
if ("error" in waitParsed) return fail("STIM_BAD_ARG", waitParsed.error, "Pass a whole number of seconds, e.g. --wait 90. `--wait 0` refuses a busy lease or device slot at once.");
|
|
2053
2282
|
const waitSeconds = waitParsed.seconds;
|
|
2054
|
-
const
|
|
2283
|
+
const remoteTarget = commandRemoteBackend !== null ? parseIosRemote(commandRemoteBackend) : remoteAndroidSetting(settings);
|
|
2284
|
+
if (physical && remoteTarget?.kind === "machine") return fail("STIM_BAD_ARG", "--device installs on a phone connected to this machine, and --remote installs on a remote one.", "Pass only one of --device and --remote; unset android.remote for a local phone.");
|
|
2285
|
+
const machine = remoteTarget?.kind === "machine" ? remoteTarget.machine : null;
|
|
2286
|
+
const remoteBackend = !physical && remoteTarget?.kind === "backend" ? remoteTarget.backend : null;
|
|
2287
|
+
if (!machine) {
|
|
2288
|
+
const refusal = androidDeviceSelectorRefusal({
|
|
2289
|
+
settingsContext,
|
|
2290
|
+
slot,
|
|
2291
|
+
systemImageFlag,
|
|
2292
|
+
deviceProfileFlag,
|
|
2293
|
+
systemImage,
|
|
2294
|
+
deviceProfile,
|
|
2295
|
+
physical,
|
|
2296
|
+
remoteBackend
|
|
2297
|
+
}, {
|
|
2298
|
+
listSystemImages,
|
|
2299
|
+
listDeviceProfiles,
|
|
2300
|
+
supportsFold,
|
|
2301
|
+
ownedAvd
|
|
2302
|
+
});
|
|
2303
|
+
if (refusal) return refusal;
|
|
2304
|
+
}
|
|
2305
|
+
const target = physical ? {
|
|
2306
|
+
kind: "physical",
|
|
2307
|
+
serial: typeof deviceFlag === "string" ? deviceFlag : null,
|
|
2308
|
+
lease: {
|
|
2309
|
+
waitSeconds,
|
|
2310
|
+
noWait
|
|
2311
|
+
}
|
|
2312
|
+
} : machine ? {
|
|
2313
|
+
kind: "hosted",
|
|
2314
|
+
machine,
|
|
2315
|
+
systemImage,
|
|
2316
|
+
deviceProfile
|
|
2317
|
+
} : remoteBackend ? {
|
|
2318
|
+
kind: "remote",
|
|
2319
|
+
backend: remoteBackend,
|
|
2320
|
+
systemImage,
|
|
2321
|
+
deviceProfile
|
|
2322
|
+
} : {
|
|
2323
|
+
kind: "emulator",
|
|
2324
|
+
systemImage,
|
|
2325
|
+
deviceProfile
|
|
2326
|
+
};
|
|
2327
|
+
return {
|
|
2328
|
+
ok: true,
|
|
2329
|
+
plan: {
|
|
2330
|
+
build: {
|
|
2331
|
+
variant,
|
|
2332
|
+
release,
|
|
2333
|
+
profile: buildProfile,
|
|
2334
|
+
cas,
|
|
2335
|
+
cache: cachePolicy,
|
|
2336
|
+
compilerCache: optimizations.android.compilerCache,
|
|
2337
|
+
gradleBuildCache: optimizations.android.gradleBuildCache,
|
|
2338
|
+
pch: optimizations.android.pch,
|
|
2339
|
+
targetAbiOnly: optimizations.android.targetAbiOnly
|
|
2340
|
+
},
|
|
2341
|
+
target,
|
|
2342
|
+
deviceSlotWaitMs: noWait ? 0 : waitFlag === void 0 && !physical ? DEFAULT_DEVICE_SLOT_WAIT_MS : waitSeconds * 1e3,
|
|
2343
|
+
isExpo,
|
|
2344
|
+
metroWarmup: optimizations.metroWarmup,
|
|
2345
|
+
cacheProviderConfig
|
|
2346
|
+
}
|
|
2347
|
+
};
|
|
2348
|
+
}
|
|
2349
|
+
function androidDeviceSelectorRefusal({ settingsContext, slot, systemImageFlag, deviceProfileFlag, systemImage, deviceProfile, physical, remoteBackend }, { listSystemImages = listInstalledSystemImages, listDeviceProfiles = listAvdDeviceProfiles, supportsFold = systemImageSupportsFold, ownedAvd = slotOwnedAvd } = {}) {
|
|
2350
|
+
const root = settingsContext.projectPath;
|
|
2055
2351
|
const settingsLayersForOrigin = settingsLayers(settingsContext);
|
|
2056
2352
|
const imageRefusal = systemImageRefusal({
|
|
2057
2353
|
slot,
|
|
@@ -2093,43 +2389,7 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
2093
2389
|
});
|
|
2094
2390
|
if (foldRefusal) return fail(foldRefusal.code, foldRefusal.message, foldRefusal.remedy);
|
|
2095
2391
|
}
|
|
2096
|
-
|
|
2097
|
-
kind: "physical",
|
|
2098
|
-
serial: typeof deviceFlag === "string" ? deviceFlag : null,
|
|
2099
|
-
lease: {
|
|
2100
|
-
waitSeconds,
|
|
2101
|
-
noWait
|
|
2102
|
-
}
|
|
2103
|
-
} : remoteBackend ? {
|
|
2104
|
-
kind: "remote",
|
|
2105
|
-
backend: remoteBackend,
|
|
2106
|
-
systemImage,
|
|
2107
|
-
deviceProfile
|
|
2108
|
-
} : {
|
|
2109
|
-
kind: "emulator",
|
|
2110
|
-
systemImage,
|
|
2111
|
-
deviceProfile
|
|
2112
|
-
};
|
|
2113
|
-
return {
|
|
2114
|
-
ok: true,
|
|
2115
|
-
plan: {
|
|
2116
|
-
build: {
|
|
2117
|
-
variant,
|
|
2118
|
-
release,
|
|
2119
|
-
profile: buildProfile,
|
|
2120
|
-
cas,
|
|
2121
|
-
cache: cachePolicy,
|
|
2122
|
-
compilerCache: optimizations.android.compilerCache,
|
|
2123
|
-
gradleBuildCache: optimizations.android.gradleBuildCache,
|
|
2124
|
-
pch: optimizations.android.pch,
|
|
2125
|
-
targetAbiOnly: optimizations.android.targetAbiOnly
|
|
2126
|
-
},
|
|
2127
|
-
target,
|
|
2128
|
-
isExpo,
|
|
2129
|
-
metroWarmup: optimizations.metroWarmup,
|
|
2130
|
-
cacheProviderConfig
|
|
2131
|
-
}
|
|
2132
|
-
};
|
|
2392
|
+
return null;
|
|
2133
2393
|
}
|
|
2134
2394
|
//#endregion
|
|
2135
2395
|
//#region src/commands/android/next-build.ts
|
|
@@ -2268,7 +2528,7 @@ async function planAndroid(opts, overrides = {}) {
|
|
|
2268
2528
|
if (target.kind === "physical") return refuse(planFlagRefusal("--device"));
|
|
2269
2529
|
return refuse({
|
|
2270
2530
|
code: "STIM_BAD_ARG",
|
|
2271
|
-
message: `android.remote routes this workspace's runs to a ${target.backend} device, whose ABI --plan cannot read without a session.`,
|
|
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.`,
|
|
2272
2532
|
remedy: "Run `stim android` to build for the remote device, or unset android.remote to plan the owned emulator."
|
|
2273
2533
|
});
|
|
2274
2534
|
}
|
|
@@ -2324,11 +2584,10 @@ function androidCommand(program) {
|
|
|
2324
2584
|
registerAndroid(program);
|
|
2325
2585
|
}
|
|
2326
2586
|
function registerAndroid(program) {
|
|
2327
|
-
program.command("android").description("Build (or install from the shared cache), install and launch this workspace's Android app on its owned emulator, wired to the reserved Metro port. A Debug run starts the dev server when it is not running.").option("--eas-profile <name>", "Download a matching EAS development build; on a miss, print the build command without running it").option("--slot <name>", "Reusable device slot within this workspace (default: default)", parseDeviceSlotOption).option("--build
|
|
2328
|
-
if (
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
}).option("--wait <seconds>", "How long to wait for another workspace to release the device it leases, before refusing with STIM_DEVICE_BUSY (default 60, 0 refuses at once). Only with --device.").option("--no-wait", "Install on a device another workspace leases instead of waiting: this run takes no lease and, when both workspaces build the same app id, the install terminates the holder's running app. Only with --device.").action(async (opts) => {
|
|
2587
|
+
program.command("android").description("Build (or install from the shared cache), install and launch this workspace's Android app on its owned emulator, wired to the reserved Metro port. A Debug run starts the dev server when it is not running.").option("--eas-profile <name>", "Download a matching EAS development build; on a miss, print the build command without running it").option("--slot <name>", "Reusable device slot within this workspace (default: default)", parseDeviceSlotOption).option("--remote-build <value>", "Build on auto, local, or one named machine; a name refuses without fallback", parseBuildMachineOption).option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--plan", "Predict the next build without building, booting or installing: resolve the fingerprint, check the local then remote cache, and print the expected cache result and duration. Writes no Stim state.").option("--no-metro-check", "Skip the reserved-port Metro health check and do not start it (the app will load no bundle unless something else serves it)").option("--no-build-cache", "Build fresh, ignoring cached artifacts (local and the project's build-cache provider); the fresh build still replaces the cache entry").option("--variant <name>", "Gradle variant to assemble and install (e.g. productionDebug on a flavored project); overrides the android.variant setting. A variant ending in Release embeds the JS bundle and skips Metro entirely. Default: debug").option("--system-image <id>", "Android system image to create this workspace's owned AVD from, as the sdkmanager package id (e.g. \"system-images;android-36;google_apis;arm64-v8a\"). Overrides the android.systemImage setting for this invocation. An unknown id refuses with STIM_BAD_ARG and prints the installed images.").option("--device-profile <id>", "Hardware profile to create this workspace's owned AVD with, as an `avdmanager list device -c` id (e.g. \"pixel_fold\", \"pixel_tablet\"). Overrides the android.deviceProfile setting for this invocation. An unknown id refuses with STIM_BAD_ARG and prints the available ids.").option("--device [serial]", "Install and launch on a connected physical device instead of this workspace's owned emulator. With no serial, the first connected device this workspace can lease is used. Stim never creates, boots, or deletes a physical device.").option("--remote <target>", "Run on eas, proxy, or a named approved Mac from remote.machines; auto places on an approved Mac when this Mac is full or busy", (value) => {
|
|
2588
|
+
if (!parseMachine(value)) throw new InvalidArgumentError("expected eas, proxy, auto, or a tailnet machine name");
|
|
2589
|
+
return value;
|
|
2590
|
+
}).option("--wait <seconds>", "How long to wait for another workspace to release the device it leases, before refusing with STIM_DEVICE_BUSY (default 60, 0 refuses at once). Without --device, wait for an owned-device slot (default 600 seconds; --no-wait refuses immediately).").option("--no-wait", "Install on a device another workspace leases instead of waiting: this run takes no lease and, when both workspaces build the same app id, the install terminates the holder's running app. Without --device, refuse a full device cap or existing slot queue immediately.").action(async (opts) => {
|
|
2332
2591
|
if (opts.plan) return planAndroid(opts);
|
|
2333
2592
|
const root = findProjectRoot$1(process.cwd());
|
|
2334
2593
|
if (!root) {
|
|
@@ -2351,7 +2610,7 @@ function registerAndroid(program) {
|
|
|
2351
2610
|
try {
|
|
2352
2611
|
return await runAndroid({
|
|
2353
2612
|
root,
|
|
2354
|
-
buildMachine: opts.
|
|
2613
|
+
buildMachine: opts.remoteBuild,
|
|
2355
2614
|
slot: opts.slot,
|
|
2356
2615
|
easProfile: opts.easProfile,
|
|
2357
2616
|
json: Boolean(opts.json),
|
|
@@ -2372,7 +2631,7 @@ function registerAndroid(program) {
|
|
|
2372
2631
|
}, { write: (line) => console.error(chalk.dim(phaseLine("lock", line))) })).ok) process.exit(runCancellation() ? 130 : 1);
|
|
2373
2632
|
});
|
|
2374
2633
|
}
|
|
2375
|
-
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, readEstimates = readRunEstimates, now = Date.now, out = (line) => console.error(line), emit = (line) => console.log(line) } = {}) {
|
|
2634
|
+
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) } = {}) {
|
|
2376
2635
|
return {
|
|
2377
2636
|
root,
|
|
2378
2637
|
json,
|
|
@@ -2456,6 +2715,7 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
2456
2715
|
writeState,
|
|
2457
2716
|
recordBuild,
|
|
2458
2717
|
recordStats,
|
|
2718
|
+
recordCapacityRefusal: recordRefusal,
|
|
2459
2719
|
readEstimates,
|
|
2460
2720
|
now,
|
|
2461
2721
|
out,
|
|
@@ -2498,6 +2758,11 @@ function androidSlotOptions(options) {
|
|
|
2498
2758
|
function avdSetupFailure(error, root, logFile) {
|
|
2499
2759
|
const refusal = (0, ownership_claim_exports.claimFailure)(error, "stim android");
|
|
2500
2760
|
if (refusal) return refusal;
|
|
2761
|
+
if (error instanceof DeviceAdmissionRefusal) return {
|
|
2762
|
+
code: error.code,
|
|
2763
|
+
message: error.message,
|
|
2764
|
+
remedy: error.remedy
|
|
2765
|
+
};
|
|
2501
2766
|
const diag = noDeviceDiagnostic({
|
|
2502
2767
|
reason: `Could not ensure an owned Android emulator: ${error?.message || error}`,
|
|
2503
2768
|
logFile,
|
|
@@ -2515,7 +2780,7 @@ function avdSetupFailure(error, root, logFile) {
|
|
|
2515
2780
|
};
|
|
2516
2781
|
}
|
|
2517
2782
|
async function runAndroid(options = {}) {
|
|
2518
|
-
let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, easProfile, resolveEasBuild, variantFlag, systemImageFlag, deviceProfileFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, listDeviceProfiles, ensureDeviceBooted, resolveAvdSerial, waitForDeviceBoot, dataFreeBytes, trimCaches, wipeDevice, resolveMetro, prewarmMetro, startServer, readState, pidAlive, verifyCollector, verifyLaunched, ensureStorage, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, resolveCacheProvider, loadCacheProviderModule, planPrebuildFor, prebuild, build, ccacheFor, install, launch, launchRelease, verifyReleaseLaunched, swapApk, resolveDevClientScheme, spawn, kill, createWriter, writeLaunch, writeState, recordBuild, recordStats, readEstimates, now, out, emit } = androidSlotOptions(options);
|
|
2783
|
+
let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, easProfile, resolveEasBuild, variantFlag, systemImageFlag, deviceProfileFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, listDeviceProfiles, ensureDeviceBooted, resolveAvdSerial, waitForDeviceBoot, dataFreeBytes, trimCaches, wipeDevice, resolveMetro, prewarmMetro, startServer, readState, pidAlive, verifyCollector, verifyLaunched, ensureStorage, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, resolveCacheProvider, loadCacheProviderModule, planPrebuildFor, prebuild, build, ccacheFor, install, launch, launchRelease, verifyReleaseLaunched, swapApk, resolveDevClientScheme, spawn, kill, createWriter, writeLaunch, writeState, recordBuild, recordStats, recordCapacityRefusal: recordRefusal = recordCapacityRefusal, readEstimates, now, out, emit } = androidSlotOptions(options);
|
|
2519
2784
|
const progress = options.progress ?? NO_BUILD_PROGRESS;
|
|
2520
2785
|
const slot = validateDeviceSlot(options.slot);
|
|
2521
2786
|
const started = now();
|
|
@@ -2621,6 +2886,7 @@ async function runAndroid(options = {}) {
|
|
|
2621
2886
|
code,
|
|
2622
2887
|
message,
|
|
2623
2888
|
remedy: remedy ?? null,
|
|
2889
|
+
...record.devicePlacement ? { devicePlacement: record.devicePlacement } : {},
|
|
2624
2890
|
...ccacheActivity.status === "not-run" ? {} : { ccache: ccacheActivity },
|
|
2625
2891
|
...lease === void 0 ? {} : { lease },
|
|
2626
2892
|
...reclaimed.length ? { reclaimed } : {}
|
|
@@ -2682,16 +2948,59 @@ async function runAndroid(options = {}) {
|
|
|
2682
2948
|
});
|
|
2683
2949
|
if (!planned.ok) return fail(planned.code, planned.message, planned.remedy, { lines: planned.lines });
|
|
2684
2950
|
const { plan } = planned;
|
|
2685
|
-
const
|
|
2951
|
+
const deviceSlotWait = {
|
|
2952
|
+
signal: runCancellationSignal(),
|
|
2953
|
+
waitMs: plan.deviceSlotWaitMs,
|
|
2954
|
+
displayName: basename(root),
|
|
2955
|
+
waitingFor: (info) => progress.waitingFor(info, "device-slot"),
|
|
2956
|
+
onWait: (ms) => {
|
|
2957
|
+
stats.addDeviceSlotWaitMs(ms);
|
|
2958
|
+
if (record.devicePlacement?.decision === "local" && ms > 0) record.devicePlacement.decision = "waited-locally";
|
|
2959
|
+
}
|
|
2960
|
+
};
|
|
2961
|
+
const { build: buildPlan, isExpo, cacheProviderConfig } = plan;
|
|
2686
2962
|
const { variant, release, cache: cachePolicy } = buildPlan;
|
|
2687
2963
|
record.configuration = variant ?? "debug";
|
|
2688
2964
|
const useBuildCache = cachePolicy.read;
|
|
2689
|
-
const
|
|
2690
|
-
const remoteBackend = target.kind === "remote" ? target.backend : null;
|
|
2691
|
-
const budget = await (options.checkBudget ?? budgetGate)({
|
|
2965
|
+
const hosting = await selectAndroidPlacement({
|
|
2692
2966
|
root,
|
|
2693
|
-
|
|
2967
|
+
slot,
|
|
2968
|
+
target: plan.target,
|
|
2969
|
+
release,
|
|
2970
|
+
metroCheck,
|
|
2971
|
+
noWait: plan.deviceSlotWaitMs === 0,
|
|
2972
|
+
buildMachine: options.buildMachine,
|
|
2973
|
+
read: options.readHostedAndroid,
|
|
2974
|
+
resolveSerial: resolveAvdSerial,
|
|
2975
|
+
prepare: options.prepareHostedAndroid,
|
|
2976
|
+
automatic: options.automaticDevicePlacement,
|
|
2977
|
+
localSelectors: (target) => androidDeviceSelectorRefusal({
|
|
2978
|
+
settingsContext,
|
|
2979
|
+
slot,
|
|
2980
|
+
systemImageFlag,
|
|
2981
|
+
deviceProfileFlag,
|
|
2982
|
+
systemImage: target.systemImage,
|
|
2983
|
+
deviceProfile: target.deviceProfile,
|
|
2984
|
+
physical: false,
|
|
2985
|
+
remoteBackend: null
|
|
2986
|
+
}, {
|
|
2987
|
+
listSystemImages,
|
|
2988
|
+
listDeviceProfiles
|
|
2989
|
+
}),
|
|
2990
|
+
checkBudget: options.checkBudget,
|
|
2991
|
+
note: out,
|
|
2992
|
+
phase
|
|
2694
2993
|
});
|
|
2994
|
+
if ("failure" in hosting) return fail(hosting.failure.code, hosting.failure.message, hosting.failure.remedy);
|
|
2995
|
+
const { target, hostedTarget, budget, selectors } = hosting;
|
|
2996
|
+
record.devicePlacement = hosting.devicePlacement;
|
|
2997
|
+
writeDevicePlacement(root, slot, PLATFORM, record.devicePlacement);
|
|
2998
|
+
const physical = target.kind === "physical";
|
|
2999
|
+
const remoteBackend = target.kind === "remote" ? target.backend : null;
|
|
3000
|
+
if (hostedTarget) {
|
|
3001
|
+
if (settings.androidEmulatorApp !== void 0) phase("device", "androidEmulatorApp is ignored on a hosting Mac; the emulator boots headless.");
|
|
3002
|
+
if (publicUrlSetting(settings) || tunnelModeSetting(settings)) phase("metro", "metro.publicUrl and metro.tunnel are ignored on a hosting Mac; Metro uses the private tailnet bridge.");
|
|
3003
|
+
}
|
|
2695
3004
|
reclaimed = budget.reclaimed;
|
|
2696
3005
|
if (budget.refusal) return fail(budget.refusal.code, budget.refusal.message, budget.refusal.remedy);
|
|
2697
3006
|
const remoteContext = remoteBackend ? await resolveRemoteDeviceContext({
|
|
@@ -2734,7 +3043,7 @@ async function runAndroid(options = {}) {
|
|
|
2734
3043
|
const gate = await ensureDevServer({
|
|
2735
3044
|
root,
|
|
2736
3045
|
port: reservedPort,
|
|
2737
|
-
settings,
|
|
3046
|
+
settings: hostedMetroSettings(settings, Boolean(hostedTarget)),
|
|
2738
3047
|
remote: remoteContext !== null,
|
|
2739
3048
|
note: out,
|
|
2740
3049
|
resolve: resolveMetro,
|
|
@@ -2788,7 +3097,14 @@ async function runAndroid(options = {}) {
|
|
|
2788
3097
|
let bootDuration = "";
|
|
2789
3098
|
let bootPromise;
|
|
2790
3099
|
let startRemoteBoot = null;
|
|
2791
|
-
if (
|
|
3100
|
+
if (hostedTarget) {
|
|
3101
|
+
device = {
|
|
3102
|
+
owned: false,
|
|
3103
|
+
systemImage: hostedTarget.choice.systemImage,
|
|
3104
|
+
deviceProfile: hostedTarget.choice.deviceProfile
|
|
3105
|
+
};
|
|
3106
|
+
bootPromise = Promise.resolve({ ok: true });
|
|
3107
|
+
} else if (target.kind === "physical" && !target.serial) {
|
|
2792
3108
|
const pooled = await pooledAndroidDevice({
|
|
2793
3109
|
root,
|
|
2794
3110
|
selectPool,
|
|
@@ -2824,12 +3140,19 @@ async function runAndroid(options = {}) {
|
|
|
2824
3140
|
} catch (err) {
|
|
2825
3141
|
return fail("STIM_BAD_ARG", err.message, "Run `stim settings set androidEmulatorApp emulator` or `stim settings set androidEmulatorApp stim-desktop`.");
|
|
2826
3142
|
}
|
|
2827
|
-
const capacity = checkCapacity({
|
|
3143
|
+
const capacity = plan.deviceSlotWaitMs === 0 ? checkCapacity({
|
|
2828
3144
|
platform: PLATFORM,
|
|
2829
3145
|
project,
|
|
2830
3146
|
max: limits.maxDevices
|
|
2831
|
-
});
|
|
2832
|
-
if (capacity)
|
|
3147
|
+
}) : null;
|
|
3148
|
+
if (capacity) {
|
|
3149
|
+
if (capacity.code === "STIM_AT_CAPACITY") recordRefusal({
|
|
3150
|
+
platform: PLATFORM,
|
|
3151
|
+
max: limits.maxDevices,
|
|
3152
|
+
workspace: workspaceId(root)
|
|
3153
|
+
}, now());
|
|
3154
|
+
return fail(capacity.code, capacity.message, capacity.remedy);
|
|
3155
|
+
}
|
|
2833
3156
|
const prepare = stepClock(now);
|
|
2834
3157
|
try {
|
|
2835
3158
|
device = await ensureDevice({
|
|
@@ -2839,6 +3162,7 @@ async function runAndroid(options = {}) {
|
|
|
2839
3162
|
settingsRoot,
|
|
2840
3163
|
settings,
|
|
2841
3164
|
flags: {
|
|
3165
|
+
deviceSlotWait,
|
|
2842
3166
|
systemImage: target.systemImage,
|
|
2843
3167
|
systemImageFlag: systemImageFlag?.trim() || null,
|
|
2844
3168
|
deviceProfile: target.deviceProfile
|
|
@@ -2858,6 +3182,7 @@ async function runAndroid(options = {}) {
|
|
|
2858
3182
|
platform: PLATFORM,
|
|
2859
3183
|
device,
|
|
2860
3184
|
projectPath: root,
|
|
3185
|
+
deviceSlotWait,
|
|
2861
3186
|
out,
|
|
2862
3187
|
logFile: emuLog
|
|
2863
3188
|
})).catch((e) => ({
|
|
@@ -2907,6 +3232,7 @@ async function runAndroid(options = {}) {
|
|
|
2907
3232
|
isExpo,
|
|
2908
3233
|
device,
|
|
2909
3234
|
physical,
|
|
3235
|
+
hostedAbi: hostedTarget?.choice.architecture,
|
|
2910
3236
|
remote: Boolean(remoteDevice),
|
|
2911
3237
|
buildPlan,
|
|
2912
3238
|
cacheProviderConfig,
|
|
@@ -2924,7 +3250,8 @@ async function runAndroid(options = {}) {
|
|
|
2924
3250
|
miss: progress.miss,
|
|
2925
3251
|
hit: progress.hit,
|
|
2926
3252
|
place: progress.place,
|
|
2927
|
-
waitingOn: progress.waitingOn
|
|
3253
|
+
waitingOn: progress.waitingOn,
|
|
3254
|
+
waitingFor: progress.waitingFor
|
|
2928
3255
|
}
|
|
2929
3256
|
}, {
|
|
2930
3257
|
deviceAbi,
|
|
@@ -2963,6 +3290,40 @@ async function runAndroid(options = {}) {
|
|
|
2963
3290
|
androidPackage = artifact.androidPackage;
|
|
2964
3291
|
record.appPath = apkPath;
|
|
2965
3292
|
if (runCancellation()) return fail("STIM_CANCELLED", "before install");
|
|
3293
|
+
if (hostedTarget) return finishHostedAndroidRun({
|
|
3294
|
+
target: hostedTarget,
|
|
3295
|
+
artifact,
|
|
3296
|
+
root,
|
|
3297
|
+
slot,
|
|
3298
|
+
release,
|
|
3299
|
+
isExpo,
|
|
3300
|
+
metroPort,
|
|
3301
|
+
logsDir,
|
|
3302
|
+
selectors,
|
|
3303
|
+
place: options.placeHostedAndroid ?? placeHostedAndroid,
|
|
3304
|
+
writePlacement: options.writeHostedAndroid ?? writeHostedAndroid,
|
|
3305
|
+
verifyLaunch: verifyLaunched,
|
|
3306
|
+
writeLaunch,
|
|
3307
|
+
readApkPackage,
|
|
3308
|
+
resolveDevClientScheme,
|
|
3309
|
+
now,
|
|
3310
|
+
started,
|
|
3311
|
+
startedAt,
|
|
3312
|
+
record,
|
|
3313
|
+
fail,
|
|
3314
|
+
phase,
|
|
3315
|
+
out,
|
|
3316
|
+
json,
|
|
3317
|
+
useBuildCache,
|
|
3318
|
+
variant,
|
|
3319
|
+
metroCheck,
|
|
3320
|
+
writer,
|
|
3321
|
+
emit,
|
|
3322
|
+
recordRun,
|
|
3323
|
+
reclaimed,
|
|
3324
|
+
devServer,
|
|
3325
|
+
recordBuild
|
|
3326
|
+
});
|
|
2966
3327
|
if (startRemoteBoot) {
|
|
2967
3328
|
progress.step("device");
|
|
2968
3329
|
bootPromise = startRemoteBoot();
|
|
@@ -3091,6 +3452,7 @@ async function runAndroid(options = {}) {
|
|
|
3091
3452
|
serial: void 0
|
|
3092
3453
|
},
|
|
3093
3454
|
projectPath: root,
|
|
3455
|
+
deviceSlotWait,
|
|
3094
3456
|
out,
|
|
3095
3457
|
logFile: emuLog
|
|
3096
3458
|
})).catch((e) => ({
|