stim 1.33.0 → 1.35.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.
- package/dist/{acquire-artifact-DTh_LxrE.mjs → acquire-artifact-0E_iw7Rf.mjs} +1 -1
- package/dist/{act-Cur4YKtK.mjs → act-CyxFP8Vj.mjs} +9 -10
- package/dist/{activity-C8kBq9w0.mjs → activity-BQpJpAjA.mjs} +2 -2
- package/dist/{agent-device-usage-output-BTRaK_up.mjs → agent-device-usage-output-BoOC3ZF9.mjs} +3 -3
- package/dist/{android-D5mCdVl2.mjs → android-CjcJE7uA.mjs} +5 -5
- package/dist/{android-BL-QqHjc.mjs → android-YRb1UzSy.mjs} +27 -29
- package/dist/{android-project-BDlPnfmL.d.mts → android-project-DhO6el8N.d.mts} +1 -1
- package/dist/api.d.mts +1 -1
- package/dist/api.mjs +21 -21
- package/dist/{artifact-BufzdGbT.mjs → artifact-2NIRz6SC.mjs} +19 -15
- package/dist/{artifact-BMjubIP2.mjs → artifact-BG4h_WTC.mjs} +11 -11
- package/dist/{auto-placement-D7P98glJ.mjs → auto-placement-CBdWa87P.mjs} +4 -5
- package/dist/{browser-web-C74et-Nv.mjs → browser-web-CNlRugJv.mjs} +5 -5
- package/dist/{build-DFBGWOTc.mjs → build-CNE3Wo5-.mjs} +15 -16
- package/dist/build-artifact-cache-D7B6seMj.mjs +174 -0
- package/dist/build-artifact-cache.d.mts +25 -0
- package/dist/build-artifact-cache.mjs +2 -0
- package/dist/{build-cache-Cqj4FGnX.mjs → build-cache-_fkewiwH.mjs} +6 -1
- package/dist/{build-plan-T3Hi24ax.mjs → build-plan-BSgR9yKo.mjs} +5 -5
- package/dist/{cli-B2YSjc9s.mjs → cli-DK24VzHi.mjs} +17 -17
- package/dist/cli.mjs +1 -1
- package/dist/{client-0_Mnfz0a.mjs → client-Dy0eJJtz.mjs} +49 -11
- package/dist/{command-output-DWI9tGPU.mjs → command-output-CGxrQ3Bd.mjs} +1 -1
- package/dist/{dependencies-CgvBZbXD.mjs → dependencies-IVl2Ga2D.mjs} +27 -30
- package/dist/{dev-client-ChsHwgrn.mjs → dev-client-DDApoVvd.mjs} +2 -2
- package/dist/{device-android-B8TQP5AX.mjs → device-android-C-biM49_.mjs} +3 -3
- package/dist/{ccache-BXtzEL6M.mjs → device-capacity-C0PiTyRb.mjs} +1004 -63
- package/dist/device-host-worker.mjs +6 -7
- package/dist/{device-B0htCt2u.mjs → device-mOFYiHiw.mjs} +8 -8
- package/dist/{device-DOehZpyx.mjs → device-p_cp8IzW.mjs} +6 -6
- package/dist/{device-pool-DP4sCeCC.mjs → device-pool-nsMvt2X2.mjs} +2 -2
- package/dist/{device-provision-CnNOIxCF.mjs → device-provision-BYYJuRKZ.mjs} +7 -7
- package/dist/{doctor-C6Xn96RS.mjs → doctor-BBAb8NLe.mjs} +1 -1
- package/dist/{doctor-9GrW5YSP.mjs → doctor-BptE4PaK.mjs} +10 -11
- package/dist/{doctor-nRLBoajo.mjs → doctor-RosbBfTV.mjs} +11 -12
- package/dist/doctor-fingerprint.mjs +1 -1
- package/dist/{doctor-watchman-CDhpC3Oh.mjs → doctor-watchman-CKOnCINh.mjs} +2 -2
- package/dist/{eas-build-CpJ4h9tG.mjs → eas-build-BxJdzQnF.mjs} +2 -2
- package/dist/{eas-fallback-B3z5nL1g.mjs → eas-fallback-DU2WDF8r.mjs} +2 -2
- package/dist/{eas-session-ledger-Bspy-VJQ.mjs → eas-session-ledger-DCUfh6m7.mjs} +1 -1
- package/dist/{error-diagnostics-DoLGklYX.mjs → error-diagnostics-D6wlK-yX.mjs} +3 -3
- package/dist/{gc-DolXpMSB.mjs → gc-BUkonUfz.mjs} +10 -10
- package/dist/{gc-BWoOp9q7.mjs → gc-CvMEytXz.mjs} +2 -2
- package/dist/{guide-CmZB0GtP.mjs → guide-BWozWG7D.mjs} +25 -15
- package/dist/{hosted-android-ByG-ICuh.mjs → hosted-android-y6K3DKQT.mjs} +2 -2
- package/dist/{hosted-client-D19Lv0tg.mjs → hosted-client-Bb89P6an.mjs} +2 -2
- package/dist/{hosted-logs-D7anR6Dk.mjs → hosted-logs-DYPsnWMF.mjs} +2 -2
- package/dist/{hosted-logs-sync-D182b1eJ.mjs → hosted-logs-sync-DfUJ2GpF.mjs} +4 -4
- package/dist/{hosted-macos-D0DDQxvl.mjs → hosted-macos-DsEzYVY_.mjs} +4 -4
- package/dist/{hosted-native-BzXIy97W.mjs → hosted-native-C9xQYYFb.mjs} +4 -4
- package/dist/{ios-AdhPKXO3.mjs → ios-BCKxjpRp.mjs} +8 -8
- package/dist/{ios-dbXzjTxx.mjs → ios-FhMkDfNC.mjs} +19 -21
- package/dist/{ios-device-DXbHZfFe.mjs → ios-device-DMOnaH5n.mjs} +1 -1
- package/dist/{ios-project-BzfLigfh.mjs → ios-project-PXBDoaii.mjs} +5 -5
- package/dist/ios-signing-CCFNjjWU.mjs +202 -0
- package/dist/{js-swap-EDVOJmqx.mjs → js-swap-07ikLRJJ.mjs} +3 -3
- package/dist/{launch-CKHpthsw.mjs → launch-CTq5gBim.mjs} +13 -13
- package/dist/{lightened-Cv4ToJRX.mjs → lightened-DikgC3-S.mjs} +2 -2
- package/dist/{logs-IagR9dWI.mjs → logs-DwI3Rkpf.mjs} +5 -5
- package/dist/{logs-DCQ3_tGS.mjs → logs-YDMfEohb.mjs} +3 -3
- package/dist/{macos-Ch7vuM-G.mjs → macos-BoNM-l6f.mjs} +14 -14
- package/dist/{macos-pc0aVVbN.mjs → macos-C_e9f5-5.mjs} +1 -1
- package/dist/maintenance-run.mjs +3 -3
- package/dist/{native-android-B3CEDq4R.mjs → native-android-Mv4YTrVN.mjs} +11 -11
- package/dist/{native-gradle-artifact-inputs-BLnO2hHR.mjs → native-gradle-artifact-inputs-C_TU6wPq.mjs} +2 -2
- package/dist/{native-runtime-e1OU8gEN.mjs → native-runtime-oDqT4UES.mjs} +3 -3
- package/dist/{native-xcode-inputs-DL3ETb8s.mjs → native-xcode-inputs-DsozghTX.mjs} +1 -1
- package/dist/{native-xcode-ios-D4e8k2Jy.mjs → native-xcode-ios-UXnH9NRY.mjs} +13 -13
- package/dist/offload-worker.d.mts +6 -1
- package/dist/offload-worker.mjs +40 -15
- package/dist/{phone-reverses-rvyxW1Fl.mjs → phone-reverses-B9WyCBKQ.mjs} +1 -1
- package/dist/{placement-log-nnGnu8mo.mjs → placement-log-D0DZLcJk.mjs} +1 -1
- package/dist/{plan-placement-D3JD-T-K.mjs → plan-placement-DqmLrWoX.mjs} +4 -4
- package/dist/{ports-CZJJfJ8Q.mjs → ports-C1_1Qgdy.mjs} +1 -1
- package/dist/{prebuild-Bhs7TTcz.mjs → prebuild-BptrXmh_.mjs} +6 -6
- package/dist/{project-DWEXiBA9.mjs → project-Dqqj3IkW.mjs} +11 -11
- package/dist/{react-native-android-C6Y270GS.mjs → react-native-android-Bsm_o4A8.mjs} +18 -18
- package/dist/{react-native-build-DaPfzf_e.mjs → react-native-build-BTUcE926.mjs} +4 -4
- package/dist/{react-native-doctor-DGMPxkPS.mjs → react-native-doctor-CfsCTKZt.mjs} +4 -4
- package/dist/{react-native-ios-dX48_92n.mjs → react-native-ios-_cK2pjZn.mjs} +25 -19
- package/dist/{reload-BGwblBJ9.mjs → reload-CXgce4Yo.mjs} +1 -1
- package/dist/{reload-DsOByDfy.mjs → reload-DsTMtMDU.mjs} +6 -6
- package/dist/{remote-D0uJZ76R.mjs → remote-BsmYHUxH.mjs} +6 -6
- package/dist/{result-DuimMZKJ.mjs → result-DK6nTO-M.mjs} +9 -10
- package/dist/{server-command-BC0EgMfY.mjs → server-command-Cu2w-UjB.mjs} +2 -2
- package/dist/{server-expo-15RjL0ir.mjs → server-expo-CjQwxvIi.mjs} +1 -1
- package/dist/server-expo-DbSypdNl.mjs +2 -0
- package/dist/{settings-CuP55ieT.mjs → settings-DcmVICCn.mjs} +2 -2
- package/dist/{settings-payload-BIGGQ3or.mjs → settings-payload-BoiFMiFq.mjs} +1 -1
- package/dist/{settings-write-D85j8mej.mjs → settings-write-B3LzEVwf.mjs} +2 -2
- package/dist/{slot-launch-D4oREwhx.mjs → slot-launch-DsQmPdlg.mjs} +2 -2
- package/dist/{stage-DKC6tDXu.mjs → stage-Dqk0qvaI.mjs} +1 -1
- package/dist/{start-BsEV4S-s.mjs → start-DIFiT2P5.mjs} +5 -5
- package/dist/{start-DfPSLc__.mjs → start-GkRBR1e3.mjs} +1 -1
- package/dist/{stats-ByEUblPr.mjs → stats-Dz08IopP.mjs} +3 -3
- package/dist/{status-C6hLlDqW.mjs → status-DhKv_e8a.mjs} +11 -12
- package/dist/{status-FP1MH9qn.mjs → status-Dost__Vn.mjs} +1 -1
- package/dist/{status-watch-D8s823CZ.mjs → status-watch-UecLnPQD.mjs} +1 -1
- package/dist/status-worker.mjs +1 -1
- package/dist/{stop-CpTy6bod.mjs → stop-CcZISot5.mjs} +2 -2
- package/dist/{stop-5LKfVv-0.mjs → stop-QFOqgICJ.mjs} +2 -2
- package/dist/{stop-BVI2-L7s.mjs → stop-vRe6TQk5.mjs} +9 -9
- package/dist/supervisor-run.mjs +7 -8
- package/dist/{support-CqyWfZZI.mjs → support-CNqJDBrH.mjs} +3 -3
- package/dist/support-DW575Dnx.mjs +2050 -0
- package/dist/{swiftpm-artifact-inputs-uq5oV7xR.mjs → swiftpm-artifact-inputs-DQTJTczX.mjs} +1 -1
- package/dist/{swiftpm-macos-BuhqYB3n.mjs → swiftpm-macos-BKczZ0k9.mjs} +4 -4
- package/dist/{toolchain-D_jotUG7.mjs → toolchain-BDlugtoh.mjs} +1 -1
- package/dist/{toolchain-s5ZmG42Z.mjs → toolchain-DRQyCvlp.mjs} +406 -8
- package/dist/web-D1m1LKEz.mjs +2 -0
- package/dist/{web-DwYCTFdh.mjs → web-TDWjHUT3.mjs} +3 -3
- package/dist/web-run.mjs +1 -1
- package/dist/{workspaces-B3ahAHZc.mjs → workspaces-CY3KSdC0.mjs} +2 -2
- package/dist/{worktree-CrjsPIs2.mjs → worktree-D6myrQ2H.mjs} +1 -1
- package/dist/{worktree-BhsML6GV.mjs → worktree-fKKe8j5M.mjs} +10 -10
- package/package.json +12 -7
- package/dist/device-capacity-BAkETfIl.mjs +0 -867
- package/dist/device-ios-DFaYFdar.mjs +0 -716
- package/dist/device-lease-run-DpiCnRbZ.mjs +0 -282
- package/dist/idle-shutdown-6i8CHoNq.mjs +0 -142
- package/dist/ios-signing-D-tSX1iF.mjs +0 -489
- package/dist/selection-xS50Mt0V.mjs +0 -791
- package/dist/server-expo-gYIypb0-.mjs +0 -2
- package/dist/support-D8OH0z98.mjs +0 -287
- package/dist/web-cCfM8kwV.mjs +0 -2
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { C as operationState, D as ownership_claim_exports, E as debugLog, S as isTermination, T as process_identity_exports, d as throwIfCancelled, f as WorkspaceProcessCleanupError, l as cancellableSleep, m as workspaceProcessLockError, n as isTimeoutError, p as withWorkspaceProcessLock, t as getExecutor, u as cancelledError } from "./exec-CGuDzt40.mjs";
|
|
2
|
-
import { D as workspaceId$1, E as workspaceDir, M as withDirLock$1, b as sharedCcache$1, d as writeWorkspaceState, j as workspaceStateFile$1, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, r as readWorkspaceState$1, t as clearWorkspaceStateKey, y as sharedBuildCache } from "./workspace-state-BdZ-QgmB.mjs";
|
|
2
|
+
import { D as workspaceId$1, E as workspaceDir, M as withDirLock$1, b as sharedCcache$1, d as writeWorkspaceState, j as workspaceStateFile$1, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, r as readWorkspaceState$1, s as updateWorkspaceStateAsync, t as clearWorkspaceStateKey, y as sharedBuildCache } from "./workspace-state-BdZ-QgmB.mjs";
|
|
3
3
|
import { n as stripAnsi } from "./process-output-Fpl8PYi8.mjs";
|
|
4
|
-
import { a as formatElapsed, l as phaseLine, o as formatLongDuration, s as isAppLaunchError, u as plural } from "./command-output-
|
|
5
|
-
import { a as clearDevice, at as isOnMountedVolume, ct as volumeRootFor, d as getConfigDir, ft as deviceSlotPlatforms, ht as projectDeviceSlots, k as withConfigLockAsync$1, m as getProject, nt as directorySizeAsync, o as clearSupervisor, rt as formatBytes, st as measuredDirectorySizeAsync, tt as directorySize, v as loadConfig$1 } from "./config-BW0fAtoK.mjs";
|
|
4
|
+
import { a as formatElapsed, l as phaseLine, o as formatLongDuration, s as isAppLaunchError, u as plural } from "./command-output-CGxrQ3Bd.mjs";
|
|
5
|
+
import { a as clearDevice, at as isOnMountedVolume, ct as volumeRootFor, d as getConfigDir, dt as deviceSlotKey, ft as deviceSlotPlatforms, ht as projectDeviceSlots, k as withConfigLockAsync$1, m as getProject, nt as directorySizeAsync, o as clearSupervisor, p as getPlatformDeviceLimits, rt as formatBytes, st as measuredDirectorySizeAsync, tt as directorySize, u as getConcurrencyLimits, v as loadConfig$1 } from "./config-BW0fAtoK.mjs";
|
|
6
6
|
import { a as killOnAbort, n as runOperationFileQuiet, r as childEnvironment, t as runOperationFile } from "./operation-exec-DKlBcKAL.mjs";
|
|
7
|
-
import { Z as resolveProjectSettingsAsync, d as androidLayoutSetting, ft as planMetroReach, m as defaultAndroidLayout } from "./settings-B5IBDfTx.mjs";
|
|
8
|
-
import { C as resolvePackageJson, M as readMetroTunnel, N as readPidFile, P as readRemoteSession, i as findProjectRoot$1, j as clearWorkspaceSupervisor, k as clearRemoteSession, s as ownedDeviceLabel, v as detectIsExpo } from "./project-
|
|
9
|
-
import { R as listAvdsAsync, U as ownedAvdDirectory, X as parseAvdSystemImage, Y as parseAvdDeviceProfile, d as androidToolPath, ft as isStimOwnedAvd, gt as dropParked, m as avdIniPaths, o as androidHome, pt as isStimOwnedSim, q as ownedAvdSerialResolverAsync, xt as readParked, y as avdStorageRoots, z as listInstalledSystemImages } from "./android-Iq0M2t2d.mjs";
|
|
7
|
+
import { Z as resolveProjectSettingsAsync, d as androidLayoutSetting, ft as planMetroReach, g as deviceReclaimIdleMinutesSetting, m as defaultAndroidLayout } from "./settings-B5IBDfTx.mjs";
|
|
8
|
+
import { C as resolvePackageJson, M as readMetroTunnel, N as readPidFile, P as readRemoteSession, i as findProjectRoot$1, j as clearWorkspaceSupervisor, k as clearRemoteSession, s as ownedDeviceLabel, v as detectIsExpo } from "./project-Dqqj3IkW.mjs";
|
|
9
|
+
import { N as listAdbDevices, P as listAdbDevicesAsync, R as listAvdsAsync, U as ownedAvdDirectory, X as parseAvdSystemImage, Y as parseAvdDeviceProfile, d as androidToolPath, ft as isStimOwnedAvd, gt as dropParked, m as avdIniPaths, o as androidHome, pt as isStimOwnedSim, q as ownedAvdSerialResolverAsync, xt as readParked, y as avdStorageRoots, z as listInstalledSystemImages } from "./android-Iq0M2t2d.mjs";
|
|
10
10
|
import { a as listeningPidsAsync, c as pidExists, d as resolveProjectMetro, h as isMetroRunning, r as killMetroTreeAsync, u as processCwds } from "./metro-B5PVz0Wv.mjs";
|
|
11
11
|
import { c as readNdjsonGenerations$1, i as createNdjsonWriter, r as createNdjsonTail } from "./ndjson-CrjIsGzw.mjs";
|
|
12
|
-
import { T as formatSpace, a as removeEasSessionClaim, d as parseWatchRoots, f as parseWatchmanClients, i as recordEasSessionClaim, l as parseSubscriberNames, n as easMachineStateRoot$1, o as removeEasSessionClaimAsync, p as parseWatchmanPid, r as readEasSessionLedger$1, s as compareStimVersions, t as assertEasMachineRootWritable, u as parseTriggerCount, w as environmentState, y as capacity } from "./eas-session-ledger-
|
|
12
|
+
import { T as formatSpace, a as removeEasSessionClaim, d as parseWatchRoots, f as parseWatchmanClients, i as recordEasSessionClaim, l as parseSubscriberNames, n as easMachineStateRoot$1, o as removeEasSessionClaimAsync, p as parseWatchmanPid, r as readEasSessionLedger$1, s as compareStimVersions, t as assertEasMachineRootWritable, u as parseTriggerCount, w as environmentState, y as capacity } from "./eas-session-ledger-DCUfh6m7.mjs";
|
|
13
13
|
import { i as registeredCaches, r as register } from "./cache-manifest-8JX9S1QJ.mjs";
|
|
14
|
-
import { At as mapLimited, B as workspaceBuildInProgress, Dt as createGcResults, Et as GC_OUTPUT, F as collectOwnedDeviceActivityAsync, L as idleShutdownCandidates, Mt as sizeConcurrency, N as collectIdleDevicesAsync, _t as teardownParkedIosSim, bt as measureDu, dt as eraseParkedIosSim, gt as teardownParkedAvd, ht as teardownOwnedIosSim, i as clearWorkspaceOutputs, l as keptByLock, o as collectWorkspaceOutputs, pt as teardownOwnedAvd, ut as eraseParkedAvd, v as withIdleWorkspace, vt as probePublicHttp, wt as listBuildSlots, x as resolveSupervisorTarget, xt as writeUsageCache, yt as canonical$1, z as shutDownIdleDevices } from "./workspaces-
|
|
14
|
+
import { At as mapLimited, B as workspaceBuildInProgress, Dt as createGcResults, Et as GC_OUTPUT, F as collectOwnedDeviceActivityAsync, L as idleShutdownCandidates, Mt as sizeConcurrency, N as collectIdleDevicesAsync, P as collectOwnedDeviceActivity, _t as teardownParkedIosSim, bt as measureDu, dt as eraseParkedIosSim, gt as teardownParkedAvd, ht as teardownOwnedIosSim, i as clearWorkspaceOutputs, l as keptByLock, o as collectWorkspaceOutputs, ot as recordCapacityRefusal, pt as teardownOwnedAvd, st as recordCapacityWait, ut as eraseParkedAvd, v as withIdleWorkspace, vt as probePublicHttp, wt as listBuildSlots, x as resolveSupervisorTarget, xt as writeUsageCache, yt as canonical$1, z as shutDownIdleDevices$1 } from "./workspaces-CY3KSdC0.mjs";
|
|
15
15
|
import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
|
|
16
|
-
import {
|
|
17
|
-
import { N as listBuildLocks, b as leaseIsExpired, g as workspaceActivity, o as envDir, p as readHostProcessTableAsync, x as listLeaseFiles } from "./activity-
|
|
18
|
-
import { u as expoConfigContents } from "./build-cache-
|
|
19
|
-
import { G as deviceHoldsBundle, O as sanitizeDeviceLabel, W as deviceHoldsApk, b as parseSimctlList, f as listAllIosSimsAsync, g as listUserApps, j as uninstallIosApp, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, y as parseRuntimeVersion } from "./ios-BdZdNsAj.mjs";
|
|
16
|
+
import { $ as readBundleSchemes, Z as approvableSchemes, _t as runCancellationSignal, bt as claimForEmptying, mt as nativeRunWaitNotice } from "./toolchain-DRQyCvlp.mjs";
|
|
17
|
+
import { N as listBuildLocks, _ as workspaceDeviceLastUseAt, b as leaseIsExpired, g as workspaceActivity, o as envDir, p as readHostProcessTableAsync, x as listLeaseFiles } from "./activity-BQpJpAjA.mjs";
|
|
18
|
+
import { u as expoConfigContents } from "./build-cache-_fkewiwH.mjs";
|
|
19
|
+
import { G as deviceHoldsBundle, O as sanitizeDeviceLabel, W as deviceHoldsApk, b as parseSimctlList, d as listAllIosSims, f as listAllIosSimsAsync, g as listUserApps, j as uninstallIosApp, l as iosRuntimeMatches, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, y as parseRuntimeVersion } from "./ios-BdZdNsAj.mjs";
|
|
20
20
|
import { n as supervisorError } from "./errors-DHm_U5gv.mjs";
|
|
21
|
-
import { c as findBinUpward, n as expoBinFromPackage, r as expoBinPath, s as expoTelemetryEnv } from "./server-expo-
|
|
21
|
+
import { c as findBinUpward, n as expoBinFromPackage, r as expoBinPath, s as expoTelemetryEnv } from "./server-expo-CjQwxvIi.mjs";
|
|
22
22
|
import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-DLdEDWJj.mjs";
|
|
23
23
|
import { t as resolveMaintenanceSettings } from "./settings-D9LtQaUw.mjs";
|
|
24
24
|
import { createRequire } from "node:module";
|
|
@@ -26,12 +26,13 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
26
26
|
import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, statfsSync, unlinkSync, writeFileSync } from "node:fs";
|
|
27
27
|
import { homedir, platform, totalmem } from "node:os";
|
|
28
28
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
29
|
-
import { LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, ambientContext, cacheDirRefusal, compiledHelper, configDir, ensurePrivateDir, rotatedLogPath, withAmbientContext, withDirLock } from "@stim-cli/core";
|
|
30
|
-
import { DEV_SERVER_STOP_REQUEST_KEY, MACHINE_OWNER_KINDS, MAINTENANCE_LOG_NAME, budgetSweepFile, budgetSweepLock, buildWorkerRoot, gcLogFile, isJsonObject, loadConfig, machineCacheUsageFile, maintenanceNdjsonFile, readCreatedDevices, readDevServerStop, readDevServerStopRequest, readJsonObject, readMachineCacheUsage, runId, settingDefinition, settingValueError, workspaceLogsDir } from "@stim-cli/core/state";
|
|
29
|
+
import { LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, ambientContext, cacheDirRefusal, compiledHelper, configDir, ensurePrivateDir, rotatedLogPath, withAmbientContext, withDirLock, workspaceId } from "@stim-cli/core";
|
|
30
|
+
import { DEVICE_IDLE_SHUTDOWN_KEY, DEVICE_RECLAIM_REASON, DEV_SERVER_STOP_REQUEST_KEY, MACHINE_OWNER_KINDS, MAINTENANCE_LOG_NAME, automaticMachineEnabled, budgetSweepFile, budgetSweepLock, buildWorkerRoot, gcLogFile, isJsonObject, loadConfig, machineCacheUsageFile, maintenanceNdjsonFile, readCreatedDevices, readDevServerStop, readDevServerStopRequest, readDeviceIdleShutdowns, readJsonObject, readMachineCacheUsage, runId, settingDefinition, settingValueError, workspaceLogsDir } from "@stim-cli/core/state";
|
|
31
31
|
import { basename as basename$1, dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1 } from "path";
|
|
32
32
|
import chalk from "chalk";
|
|
33
33
|
import { existsSync as existsSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1, statSync as statSync$1 } from "fs";
|
|
34
34
|
import { fileURLToPath } from "node:url";
|
|
35
|
+
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
35
36
|
import { access, lstat, readFile, readdir, rm, stat, statfs } from "node:fs/promises";
|
|
36
37
|
import { homedir as homedir$1, tmpdir as tmpdir$1 } from "os";
|
|
37
38
|
//#region src/budget-sweep.ts
|
|
@@ -2761,48 +2762,6 @@ async function readRemoteSimulatorArch(daemon, { fetch: fetchImpl = fetch, timeo
|
|
|
2761
2762
|
}
|
|
2762
2763
|
}
|
|
2763
2764
|
//#endregion
|
|
2764
|
-
//#region src/engine/app-schemes.ts
|
|
2765
|
-
function schemesFromInfoPlist(plist) {
|
|
2766
|
-
const types = plist?.CFBundleURLTypes;
|
|
2767
|
-
if (!Array.isArray(types)) return [];
|
|
2768
|
-
const out = [];
|
|
2769
|
-
for (const type of types) {
|
|
2770
|
-
const schemes = type?.CFBundleURLSchemes;
|
|
2771
|
-
if (Array.isArray(schemes)) out.push(...schemes.filter((s) => typeof s === "string" && s.trim() !== ""));
|
|
2772
|
-
}
|
|
2773
|
-
return out;
|
|
2774
|
-
}
|
|
2775
|
-
function readBundleSchemes(appPath, { exec = null } = {}) {
|
|
2776
|
-
if (typeof appPath !== "string" || appPath.trim() === "") return [];
|
|
2777
|
-
const e = exec || getExecutor();
|
|
2778
|
-
let out;
|
|
2779
|
-
try {
|
|
2780
|
-
out = e.runFile("plutil", [
|
|
2781
|
-
"-convert",
|
|
2782
|
-
"json",
|
|
2783
|
-
"-o",
|
|
2784
|
-
"-",
|
|
2785
|
-
join(appPath, "Info.plist")
|
|
2786
|
-
], { timeoutMs: 1e4 });
|
|
2787
|
-
} catch {
|
|
2788
|
-
return [];
|
|
2789
|
-
}
|
|
2790
|
-
try {
|
|
2791
|
-
return schemesFromInfoPlist(JSON.parse(String(out)));
|
|
2792
|
-
} catch {
|
|
2793
|
-
return [];
|
|
2794
|
-
}
|
|
2795
|
-
}
|
|
2796
|
-
const THIRD_PARTY_SCHEME = /^(?:fb\d+|com\.googleusercontent\.apps\.|msauth\.|msauthv2|twitterkit-|db-[a-z0-9]+$|spotify|snapchat|com\.facebook)/i;
|
|
2797
|
-
function approvableSchemes(schemes) {
|
|
2798
|
-
return (Array.isArray(schemes) ? schemes : []).filter((s) => typeof s === "string" && s.trim() !== "").map((s) => s.trim()).filter((s) => !/^(?:https?|mailto|tel|sms|itms(?:-apps)?)$/i.test(s) && !THIRD_PARTY_SCHEME.test(s));
|
|
2799
|
-
}
|
|
2800
|
-
function pickDevClientScheme(schemes) {
|
|
2801
|
-
const all = approvableSchemes(schemes);
|
|
2802
|
-
const expo = all.filter((s) => s.startsWith("exp+"));
|
|
2803
|
-
return (expo.length ? expo : all).toSorted((a, b) => b.length - a.length)[0] ?? null;
|
|
2804
|
-
}
|
|
2805
|
-
//#endregion
|
|
2806
2765
|
//#region src/engine/app-install.ts
|
|
2807
2766
|
const INSTALL_ERROR = "STIM_INSTALL_FAILED";
|
|
2808
2767
|
const LAUNCH_ERROR = "STIM_LAUNCH_FAILED";
|
|
@@ -7817,7 +7776,7 @@ function estimateMemoryUse({ config, sims, androidSerial, supervisorRunning }) {
|
|
|
7817
7776
|
liveWorkspaces: liveCount
|
|
7818
7777
|
};
|
|
7819
7778
|
}
|
|
7820
|
-
async function listSims(signal, operation) {
|
|
7779
|
+
async function listSims$1(signal, operation) {
|
|
7821
7780
|
try {
|
|
7822
7781
|
return await listAllIosSimsAsync({
|
|
7823
7782
|
timeoutMs: DEVICE_LIST_TIMEOUT_MS,
|
|
@@ -7853,7 +7812,7 @@ async function readMemoryUse(simSnapshot, signal, operation) {
|
|
|
7853
7812
|
operation
|
|
7854
7813
|
});
|
|
7855
7814
|
const config = loadConfig$1();
|
|
7856
|
-
const sims = simSnapshot && !signal ? await snapshotSims(simSnapshot) : await listSims(signal, operation);
|
|
7815
|
+
const sims = simSnapshot && !signal ? await snapshotSims(simSnapshot) : await listSims$1(signal, operation);
|
|
7857
7816
|
signal?.throwIfAborted();
|
|
7858
7817
|
const serials = /* @__PURE__ */ new Map();
|
|
7859
7818
|
for (const record of Object.values(config?.projects ?? {})) for (const { platforms } of projectDeviceSlots(record)) {
|
|
@@ -7911,7 +7870,7 @@ async function reclaimIdleDevices({ root, dryRun, note, record, now, env, signal
|
|
|
7911
7870
|
const other = isOtherWorkspace(root);
|
|
7912
7871
|
const collect = async (at) => {
|
|
7913
7872
|
signal?.throwIfAborted();
|
|
7914
|
-
const devices = await collectIdleDevicesAsync(loadConfig$1(), await listSims(signal, operation) ?? [], [], at, env, signal, operation);
|
|
7873
|
+
const devices = await collectIdleDevicesAsync(loadConfig$1(), await listSims$1(signal, operation) ?? [], [], at, env, signal, operation);
|
|
7915
7874
|
signal?.throwIfAborted();
|
|
7916
7875
|
return idleShutdownCandidates(devices.filter((device) => other(device.project)), 0);
|
|
7917
7876
|
};
|
|
@@ -7920,7 +7879,7 @@ async function reclaimIdleDevices({ root, dryRun, note, record, now, env, signal
|
|
|
7920
7879
|
targets: idle.map(deviceLabel),
|
|
7921
7880
|
failures: 0
|
|
7922
7881
|
};
|
|
7923
|
-
const { failures, shutDown } = await shutDownIdleDevices(idle, 0, () => collect(Date.now()), stepOutput(note, record), signal, operation);
|
|
7882
|
+
const { failures, shutDown } = await shutDownIdleDevices$1(idle, 0, () => collect(Date.now()), stepOutput(note, record), signal, operation);
|
|
7924
7883
|
return {
|
|
7925
7884
|
targets: shutDown.map(deviceLabel),
|
|
7926
7885
|
failures
|
|
@@ -7945,7 +7904,7 @@ async function reclaimIdleDevServers({ root, dryRun, note, record, now, env, sig
|
|
|
7945
7904
|
targets: [],
|
|
7946
7905
|
failures: 0
|
|
7947
7906
|
};
|
|
7948
|
-
const sims = await listSims(signal, operation);
|
|
7907
|
+
const sims = await listSims$1(signal, operation);
|
|
7949
7908
|
signal?.throwIfAborted();
|
|
7950
7909
|
if (sims === null) {
|
|
7951
7910
|
note(chalk.dim(phaseLine("budget", "dev servers left alone: simulators could not be listed to prove them idle")));
|
|
@@ -8515,4 +8474,986 @@ function resolveCcache({ root, layout = defaultAndroidLayout(root), dir = shared
|
|
|
8515
8474
|
};
|
|
8516
8475
|
}
|
|
8517
8476
|
//#endregion
|
|
8518
|
-
|
|
8477
|
+
//#region src/workspace/display-label.ts
|
|
8478
|
+
function baseLabel({ root, label }) {
|
|
8479
|
+
return label || basename(root);
|
|
8480
|
+
}
|
|
8481
|
+
function pathSegments(root) {
|
|
8482
|
+
return root.split(/[\\/]+/).filter(Boolean).toReversed();
|
|
8483
|
+
}
|
|
8484
|
+
function workspaceLabels(workspaces) {
|
|
8485
|
+
const labels = /* @__PURE__ */ new Map();
|
|
8486
|
+
const groups = /* @__PURE__ */ new Map();
|
|
8487
|
+
for (const workspace of workspaces) {
|
|
8488
|
+
const base = baseLabel(workspace);
|
|
8489
|
+
const group = groups.get(base) ?? [];
|
|
8490
|
+
if (!group.some(({ root }) => root === workspace.root)) group.push(workspace);
|
|
8491
|
+
groups.set(base, group);
|
|
8492
|
+
}
|
|
8493
|
+
for (const [base, group] of groups) {
|
|
8494
|
+
const [only] = group;
|
|
8495
|
+
if (group.length === 1 && only) {
|
|
8496
|
+
labels.set(only.root, base);
|
|
8497
|
+
continue;
|
|
8498
|
+
}
|
|
8499
|
+
const segments = group.map(({ root }) => pathSegments(root));
|
|
8500
|
+
const longest = Math.max(...segments.map((parts) => parts.length));
|
|
8501
|
+
let depth = 0;
|
|
8502
|
+
while (depth < longest && segments.every((parts) => parts[depth] === segments[0]?.[depth])) depth++;
|
|
8503
|
+
const suffixes = segments.map((parts) => parts[depth]);
|
|
8504
|
+
const distinct = new Set(suffixes).size === group.length;
|
|
8505
|
+
group.forEach(({ root }, index) => {
|
|
8506
|
+
const suffix = distinct ? suffixes[index] : root;
|
|
8507
|
+
labels.set(root, suffix ? `${base} (${suffix})` : base);
|
|
8508
|
+
});
|
|
8509
|
+
}
|
|
8510
|
+
return labels;
|
|
8511
|
+
}
|
|
8512
|
+
function workspaceDisplayLabel(root, config) {
|
|
8513
|
+
const registered = Object.entries(config?.projects ?? {}).map(([path, project]) => ({
|
|
8514
|
+
root: path,
|
|
8515
|
+
label: project.label
|
|
8516
|
+
}));
|
|
8517
|
+
const own = registered.find((entry) => entry.root === root);
|
|
8518
|
+
return workspaceLabels([...registered, own ?? { root }]).get(root) ?? basename(root);
|
|
8519
|
+
}
|
|
8520
|
+
//#endregion
|
|
8521
|
+
//#region src/devices/idle-shutdown.ts
|
|
8522
|
+
const SIM_LIST_TIMEOUT_MS = 1e4;
|
|
8523
|
+
const MINUTE_MS = 6e4;
|
|
8524
|
+
/**
|
|
8525
|
+
* How long a booted owned device has been idle, when that is at least `idleMs` and nothing keeps it in use: no
|
|
8526
|
+
* driver, Stim or agent-device lock, or unreadable activity evidence, no build in the workspace, and no activity
|
|
8527
|
+
* (including an open stim-server viewer) for `idleMs`. Null keeps the device running.
|
|
8528
|
+
*/
|
|
8529
|
+
function idleShutdownDueMs(activity, { idleMs, now, buildInProgress, platform = process.platform }) {
|
|
8530
|
+
if (buildInProgress || activity.state === "driven") return null;
|
|
8531
|
+
const unknown = platform === "win32" ? activity.basis.filter((basis) => basis !== "driver-process") : activity.basis;
|
|
8532
|
+
if (activity.state === "unknown" && unknown.length > 0) return null;
|
|
8533
|
+
const last = Date.parse(activity.lastActivityAt ?? "");
|
|
8534
|
+
if (!Number.isFinite(last)) return null;
|
|
8535
|
+
const idle = now - last;
|
|
8536
|
+
return idle >= idleMs ? idle : null;
|
|
8537
|
+
}
|
|
8538
|
+
function listSims() {
|
|
8539
|
+
try {
|
|
8540
|
+
return listAllIosSims({ timeoutMs: SIM_LIST_TIMEOUT_MS });
|
|
8541
|
+
} catch {
|
|
8542
|
+
return [];
|
|
8543
|
+
}
|
|
8544
|
+
}
|
|
8545
|
+
/** Booted owned devices idle for `idleMs`, in one workspace or all registered workspaces when root is absent. */
|
|
8546
|
+
function dueIdleDevices(root, idleMs, now = Date.now()) {
|
|
8547
|
+
const config = loadConfig$1();
|
|
8548
|
+
if (!config) return [];
|
|
8549
|
+
const self = root === void 0 ? void 0 : canonicalPath(root);
|
|
8550
|
+
const projects = Object.fromEntries(Object.entries(config.projects).filter(([path]) => {
|
|
8551
|
+
if (self !== void 0 && canonicalPath(path) !== self) return false;
|
|
8552
|
+
const usedAt = workspaceDeviceLastUseAt(path);
|
|
8553
|
+
return !Number.isFinite(usedAt) || now - usedAt >= idleMs;
|
|
8554
|
+
}));
|
|
8555
|
+
if (!Object.keys(projects).length) return [];
|
|
8556
|
+
const builds = new Map(Object.keys(projects).map((path) => [path, workspaceBuildInProgress(path)]));
|
|
8557
|
+
return collectOwnedDeviceActivity({
|
|
8558
|
+
...config,
|
|
8559
|
+
projects
|
|
8560
|
+
}, listSims(), now).flatMap((device) => {
|
|
8561
|
+
const idleForMs = idleShutdownDueMs(device.activity, {
|
|
8562
|
+
idleMs,
|
|
8563
|
+
now,
|
|
8564
|
+
buildInProgress: builds.get(device.project) ?? true
|
|
8565
|
+
});
|
|
8566
|
+
return idleForMs === null ? [] : [{
|
|
8567
|
+
device,
|
|
8568
|
+
idleForMs
|
|
8569
|
+
}];
|
|
8570
|
+
});
|
|
8571
|
+
}
|
|
8572
|
+
/** Appends an idle shutdown record to the workspace's metro.ndjson, where the supervisor writes its own. */
|
|
8573
|
+
function writeIdleShutdownEvent(root, event) {
|
|
8574
|
+
const writer = createNdjsonWriter(join(workspaceLogsDir$1(root), "metro.ndjson"), { maxBytes: LOG_ROTATE_BYTES });
|
|
8575
|
+
writer.write({
|
|
8576
|
+
src: "metro",
|
|
8577
|
+
...event
|
|
8578
|
+
});
|
|
8579
|
+
writer.close();
|
|
8580
|
+
}
|
|
8581
|
+
/**
|
|
8582
|
+
* Shuts down, through centralized teardown, each of the workspace's owned devices that is still idle for `idleMs`,
|
|
8583
|
+
* and records why. The caller holds the workspace's native-run lock, so no `ios` or `android` run is booting or
|
|
8584
|
+
* installing on them.
|
|
8585
|
+
*/
|
|
8586
|
+
async function shutDownIdleDevices(root, idleMs, log, now = Date.now(), { only, reason } = {}) {
|
|
8587
|
+
let shutDown = 0;
|
|
8588
|
+
for (const { device, idleForMs } of dueIdleDevices(root, idleMs, now)) {
|
|
8589
|
+
if (only && (device.kind !== only.kind || device.id !== only.id || device.project !== only.project || device.slot !== only.slot)) continue;
|
|
8590
|
+
const what = `${device.kind === "ios" ? "simulator" : "emulator"} ${device.name}`;
|
|
8591
|
+
const outcome = device.kind === "ios" ? await teardownOwnedIosSim(device.id, {
|
|
8592
|
+
label: device.name,
|
|
8593
|
+
workspace: root
|
|
8594
|
+
}) : await teardownOwnedAvd(device.id, {
|
|
8595
|
+
owner: {
|
|
8596
|
+
projectPath: device.project,
|
|
8597
|
+
slot: device.slot
|
|
8598
|
+
},
|
|
8599
|
+
workspace: root
|
|
8600
|
+
});
|
|
8601
|
+
if (outcome.status === "torn-down") {
|
|
8602
|
+
shutDown++;
|
|
8603
|
+
const idleMinutes = Math.floor(idleForMs / MINUTE_MS);
|
|
8604
|
+
try {
|
|
8605
|
+
await updateWorkspaceStateAsync(root, (state) => ({
|
|
8606
|
+
...state,
|
|
8607
|
+
[DEVICE_IDLE_SHUTDOWN_KEY]: {
|
|
8608
|
+
...readDeviceIdleShutdowns(state),
|
|
8609
|
+
[deviceSlotKey(device.kind, device.slot)]: {
|
|
8610
|
+
at: new Date(now).toISOString(),
|
|
8611
|
+
idleMinutes,
|
|
8612
|
+
...reason ? { reason } : {}
|
|
8613
|
+
}
|
|
8614
|
+
}
|
|
8615
|
+
}));
|
|
8616
|
+
} catch (error) {
|
|
8617
|
+
log({
|
|
8618
|
+
level: "warn",
|
|
8619
|
+
event: "device_idle_shutdown_failed",
|
|
8620
|
+
msg: `shut down idle ${what} but could not record it: ${error?.message || error}`
|
|
8621
|
+
});
|
|
8622
|
+
}
|
|
8623
|
+
log({
|
|
8624
|
+
level: "info",
|
|
8625
|
+
event: "device_idle_shutdown",
|
|
8626
|
+
msg: `shut down ${what}, idle ${formatLongDuration(idleForMs)}${reason === DEVICE_RECLAIM_REASON ? ", reclaimed for a waiting run" : ""}`
|
|
8627
|
+
});
|
|
8628
|
+
} else if (outcome.status !== "missing") log({
|
|
8629
|
+
level: "warn",
|
|
8630
|
+
event: "device_idle_shutdown_failed",
|
|
8631
|
+
msg: `could not shut down idle ${what}: ${outcome.reason}`,
|
|
8632
|
+
...outcome.status === "skipped" ? { skipped: true } : {}
|
|
8633
|
+
});
|
|
8634
|
+
}
|
|
8635
|
+
return shutDown;
|
|
8636
|
+
}
|
|
8637
|
+
/** Forgets the slot's idle shutdown once a run boots its device again. */
|
|
8638
|
+
async function clearDeviceIdleShutdown(root, platform, slot = "default") {
|
|
8639
|
+
const key = deviceSlotKey(platform, slot);
|
|
8640
|
+
if (!(key in readDeviceIdleShutdowns(readWorkspaceState$1(root)))) return;
|
|
8641
|
+
await updateWorkspaceStateAsync(root, (state) => {
|
|
8642
|
+
const { [key]: _cleared, ...rest } = readDeviceIdleShutdowns(state);
|
|
8643
|
+
const { [DEVICE_IDLE_SHUTDOWN_KEY]: _all, ...others } = state;
|
|
8644
|
+
return Object.keys(rest).length ? {
|
|
8645
|
+
...others,
|
|
8646
|
+
[DEVICE_IDLE_SHUTDOWN_KEY]: rest
|
|
8647
|
+
} : others;
|
|
8648
|
+
});
|
|
8649
|
+
}
|
|
8650
|
+
//#endregion
|
|
8651
|
+
//#region src/devices/queue-reclaim.ts
|
|
8652
|
+
async function reclaimIdleDevice(root, idleMs, out, now, skipped, platform) {
|
|
8653
|
+
try {
|
|
8654
|
+
const self = canonicalPath(root);
|
|
8655
|
+
const candidate = dueIdleDevices(void 0, idleMs, now()).filter(({ device }) => {
|
|
8656
|
+
try {
|
|
8657
|
+
return realpathSync(device.project) !== self;
|
|
8658
|
+
} catch {
|
|
8659
|
+
return false;
|
|
8660
|
+
}
|
|
8661
|
+
}).filter(({ device }) => !platform || device.kind === platform).toSorted((a, b) => b.idleForMs - a.idleForMs).find(({ device }) => !skipped.has(`${device.kind}:${device.id}`));
|
|
8662
|
+
if (!candidate) return 0;
|
|
8663
|
+
const { device, idleForMs } = candidate;
|
|
8664
|
+
const label = workspaceDisplayLabel(device.project, loadConfig$1());
|
|
8665
|
+
const log = (event) => {
|
|
8666
|
+
writeIdleShutdownEvent(device.project, event);
|
|
8667
|
+
if (event.level === "warn") out(`${"device".padEnd(11)} ${event.msg}`);
|
|
8668
|
+
};
|
|
8669
|
+
skipped.add(`${device.kind}:${device.id}`);
|
|
8670
|
+
const run = await withIdleWorkspace(device.project, () => shutDownIdleDevices(device.project, idleMs, log, now(), {
|
|
8671
|
+
only: device,
|
|
8672
|
+
reason: DEVICE_RECLAIM_REASON
|
|
8673
|
+
}), {
|
|
8674
|
+
purpose: "device queue reclaim",
|
|
8675
|
+
supervisor: false,
|
|
8676
|
+
managedLocks: false
|
|
8677
|
+
});
|
|
8678
|
+
if (!run.ran) {
|
|
8679
|
+
log({
|
|
8680
|
+
level: "warn",
|
|
8681
|
+
event: "device_idle_shutdown_failed",
|
|
8682
|
+
msg: `could not reclaim ${device.name}: ${run.reasons.join("; ")}`
|
|
8683
|
+
});
|
|
8684
|
+
return 0;
|
|
8685
|
+
}
|
|
8686
|
+
if (!run.value) return 0;
|
|
8687
|
+
skipped.delete(`${device.kind}:${device.id}`);
|
|
8688
|
+
out(`${"device".padEnd(11)} reclaimed ${device.kind === "ios" ? "iOS simulator" : "Android emulator"} ${device.name} (workspace ${label}, idle ${Math.floor(idleForMs / 6e4)}m) for this waiting run`);
|
|
8689
|
+
return run.value;
|
|
8690
|
+
} catch (error) {
|
|
8691
|
+
out(`${"device".padEnd(11)} could not reclaim an idle device: ${error?.message || error}`);
|
|
8692
|
+
return 0;
|
|
8693
|
+
}
|
|
8694
|
+
}
|
|
8695
|
+
//#endregion
|
|
8696
|
+
//#region src/engine/device-capacity.ts
|
|
8697
|
+
const LIVE_SIM_STATES = /* @__PURE__ */ new Set(["Booted", "Booting"]);
|
|
8698
|
+
const ADMISSION_LOCK = "device-admission";
|
|
8699
|
+
const ADMISSION_LOCK_WAIT_MS = 3e5;
|
|
8700
|
+
const LOCK_QUIET_MS = 5e3;
|
|
8701
|
+
const LOCK_PROGRESS_MS = 3e4;
|
|
8702
|
+
const INVENTORY_REFRESH_MS = 1e4;
|
|
8703
|
+
const LISTING_TIMEOUT_MS = 3e4;
|
|
8704
|
+
const NO_ADB = {
|
|
8705
|
+
emulators: [],
|
|
8706
|
+
physical: [],
|
|
8707
|
+
unhealthy: []
|
|
8708
|
+
};
|
|
8709
|
+
var DeviceAdmissionRefusal = class extends Error {
|
|
8710
|
+
code;
|
|
8711
|
+
remedy;
|
|
8712
|
+
constructor(refusal) {
|
|
8713
|
+
super(refusal.message);
|
|
8714
|
+
this.code = refusal.code;
|
|
8715
|
+
this.remedy = refusal.remedy;
|
|
8716
|
+
}
|
|
8717
|
+
};
|
|
8718
|
+
var DeviceCountUnavailable = class extends Error {};
|
|
8719
|
+
function deviceKey(platform, key) {
|
|
8720
|
+
return `${platform}:${key}`;
|
|
8721
|
+
}
|
|
8722
|
+
function bootingDevicesRoot() {
|
|
8723
|
+
return join(getConfigDir(), "device-boots");
|
|
8724
|
+
}
|
|
8725
|
+
function liveEmulatorPorts(adb) {
|
|
8726
|
+
return [...adb.emulators, ...adb.unhealthy.filter((entry) => entry.kind === "emulator")];
|
|
8727
|
+
}
|
|
8728
|
+
function readBootingDevices() {
|
|
8729
|
+
const survey = (0, ownership_claim_exports.readClaimSet)(bootingDevicesRoot());
|
|
8730
|
+
const unresolved = survey.unresolved[0];
|
|
8731
|
+
if (unresolved) throw new ownership_claim_exports.ClaimRefusedError({
|
|
8732
|
+
claimPath: unresolved.path,
|
|
8733
|
+
root: bootingDevicesRoot(),
|
|
8734
|
+
reason: unresolved.reason,
|
|
8735
|
+
label: "device boot"
|
|
8736
|
+
});
|
|
8737
|
+
const booting = [];
|
|
8738
|
+
for (const holder of survey.live) {
|
|
8739
|
+
const { platform, key } = holder.details;
|
|
8740
|
+
if (typeof platform === "string" && typeof key === "string") booting.push({
|
|
8741
|
+
platform,
|
|
8742
|
+
key,
|
|
8743
|
+
displayName: typeof holder.details.displayName === "string" ? holder.details.displayName : void 0
|
|
8744
|
+
});
|
|
8745
|
+
}
|
|
8746
|
+
return booting;
|
|
8747
|
+
}
|
|
8748
|
+
function listSimsForCount() {
|
|
8749
|
+
return process.platform === "darwin" ? listAllIosSims({ timeoutMs: LISTING_TIMEOUT_MS }) : [];
|
|
8750
|
+
}
|
|
8751
|
+
function listAdbForCount() {
|
|
8752
|
+
return listAdbDevices({ timeoutMs: LISTING_TIMEOUT_MS });
|
|
8753
|
+
}
|
|
8754
|
+
function toolAbsent(error) {
|
|
8755
|
+
const { code, status, stderr } = error ?? {};
|
|
8756
|
+
return code === "ENOENT" || status === 127 || /unable to find utility|invalid active developer path|Xcode license/.test(String(stderr ?? ""));
|
|
8757
|
+
}
|
|
8758
|
+
function recordsOwnedEmulator(config) {
|
|
8759
|
+
return Object.values(config?.projects || {}).some((project) => projectDeviceSlots(project).some(({ platforms }) => platforms.android?.owned && typeof platforms.android.consolePort === "number"));
|
|
8760
|
+
}
|
|
8761
|
+
function liveOwnedDeviceKeys({ sims = [], adbEmulators = [], config = null, booting = [], exclude = [] }) {
|
|
8762
|
+
const keys = /* @__PURE__ */ new Set();
|
|
8763
|
+
for (const sim of sims) {
|
|
8764
|
+
if (!sim?.name?.startsWith("stim-")) continue;
|
|
8765
|
+
if (exclude.some((device) => device.platform === "ios" && device.key === sim.udid)) continue;
|
|
8766
|
+
if (LIVE_SIM_STATES.has(sim.state)) keys.add(deviceKey("ios", sim.udid));
|
|
8767
|
+
}
|
|
8768
|
+
const livePorts = new Set(adbEmulators.map((e) => e.consolePort));
|
|
8769
|
+
for (const proj of Object.values(config?.projects || {})) for (const { platforms } of projectDeviceSlots(proj)) {
|
|
8770
|
+
const android = platforms.android;
|
|
8771
|
+
if (android?.owned && android.avdName && typeof android.consolePort === "number" && livePorts.has(android.consolePort)) keys.add(deviceKey("android", android.avdName));
|
|
8772
|
+
}
|
|
8773
|
+
for (const device of booting) if (!exclude.some((excluded) => excluded.platform === device.platform && excluded.key === device.key)) keys.add(deviceKey(device.platform, device.key));
|
|
8774
|
+
return keys;
|
|
8775
|
+
}
|
|
8776
|
+
function inventoryKeys(inventory, exclude = []) {
|
|
8777
|
+
return liveOwnedDeviceKeys({
|
|
8778
|
+
...inventory,
|
|
8779
|
+
adbEmulators: liveEmulatorPorts(inventory.adb),
|
|
8780
|
+
exclude
|
|
8781
|
+
});
|
|
8782
|
+
}
|
|
8783
|
+
const PLATFORM_CAPS = {
|
|
8784
|
+
ios: {
|
|
8785
|
+
setting: "concurrency.maxIosDevices",
|
|
8786
|
+
noun: "iOS simulator"
|
|
8787
|
+
},
|
|
8788
|
+
android: {
|
|
8789
|
+
setting: "concurrency.maxAndroidDevices",
|
|
8790
|
+
noun: "Android emulator"
|
|
8791
|
+
}
|
|
8792
|
+
};
|
|
8793
|
+
function isCappedPlatform(platform) {
|
|
8794
|
+
return platform === "ios" || platform === "android";
|
|
8795
|
+
}
|
|
8796
|
+
function platformCount(keys, platform) {
|
|
8797
|
+
return [...keys].filter((key) => key.startsWith(`${platform}:`)).length;
|
|
8798
|
+
}
|
|
8799
|
+
/** The caps that apply to a boot on `platform` given the booted `keys`, the platform's own cap first. */
|
|
8800
|
+
function deviceLimits(keys, platform, max, platformMax) {
|
|
8801
|
+
const limits = [];
|
|
8802
|
+
if (platformMax > 0 && isCappedPlatform(platform)) limits.push({
|
|
8803
|
+
...PLATFORM_CAPS[platform],
|
|
8804
|
+
count: platformCount(keys, platform),
|
|
8805
|
+
max: platformMax,
|
|
8806
|
+
platform
|
|
8807
|
+
});
|
|
8808
|
+
if (max > 0) limits.push({
|
|
8809
|
+
setting: "concurrency.maxDevices",
|
|
8810
|
+
noun: "device",
|
|
8811
|
+
count: keys.size,
|
|
8812
|
+
max
|
|
8813
|
+
});
|
|
8814
|
+
return limits;
|
|
8815
|
+
}
|
|
8816
|
+
/** Whether a queued waiter is held back by its own platform cap, so it does not hold up the runs behind it. */
|
|
8817
|
+
function platformWalled(keys, waiter, platformMax) {
|
|
8818
|
+
const platform = String(waiter.details.platform);
|
|
8819
|
+
if (!isCappedPlatform(platform)) return false;
|
|
8820
|
+
const cap = platformMax[platform] ?? 0;
|
|
8821
|
+
return cap > 0 && platformCount(keys, platform) >= cap;
|
|
8822
|
+
}
|
|
8823
|
+
/** The first cap with no room left, or the first cap when all have room (for a run waiting behind another). */
|
|
8824
|
+
function bindingLimit(limits) {
|
|
8825
|
+
return limits.find((limit) => limit.count >= limit.max) ?? limits[0];
|
|
8826
|
+
}
|
|
8827
|
+
function atCapacityRefusal({ count, max, setting, noun }) {
|
|
8828
|
+
return {
|
|
8829
|
+
code: "STIM_AT_CAPACITY",
|
|
8830
|
+
message: count < max ? `${count}/${max} Stim ${noun}s are in use, but another run is ahead in the device slot queue.` : `${count} Stim ${noun}(s) are already booted and ${setting} is ${max}, so booting another would exceed the cap.`,
|
|
8831
|
+
remedy: `stop an environment (stim stop) or raise ${setting}`
|
|
8832
|
+
};
|
|
8833
|
+
}
|
|
8834
|
+
function uncountedRefusal(error) {
|
|
8835
|
+
return {
|
|
8836
|
+
code: "STIM_NO_DEVICE",
|
|
8837
|
+
message: `A device cap (concurrency.maxDevices, concurrency.maxIosDevices or concurrency.maxAndroidDevices) is set, and Stim could not count the booted devices: ${error?.message || error}`,
|
|
8838
|
+
remedy: `Listing devices times out when the machine is overloaded: retry once the load falls. Otherwise run \`stim doctor\` to check the simulator and adb toolchains, and check that ${join(getConfigDir(), "config.json")} and ${bootingDevicesRoot()} are readable.`
|
|
8839
|
+
};
|
|
8840
|
+
}
|
|
8841
|
+
function listed(source, empty, absent = () => false) {
|
|
8842
|
+
try {
|
|
8843
|
+
return (typeof source === "function" ? source() : source) ?? empty;
|
|
8844
|
+
} catch (error) {
|
|
8845
|
+
if (absent(error)) return empty;
|
|
8846
|
+
throw new DeviceCountUnavailable("", { cause: error });
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
async function listedAwait(read, empty, absent = () => false) {
|
|
8850
|
+
try {
|
|
8851
|
+
return await read() ?? empty;
|
|
8852
|
+
} catch (error) {
|
|
8853
|
+
if (absent(error)) return empty;
|
|
8854
|
+
throw new DeviceCountUnavailable("", { cause: error });
|
|
8855
|
+
}
|
|
8856
|
+
}
|
|
8857
|
+
async function readInventoryAsync({ sims = () => process.platform === "darwin" ? listAllIosSimsAsync({ timeoutMs: LISTING_TIMEOUT_MS }) : [], adb = () => listAdbDevicesAsync({ timeoutMs: LISTING_TIMEOUT_MS }), config = loadConfig$1, booting = readBootingDevices }) {
|
|
8858
|
+
try {
|
|
8859
|
+
const recorded = await listedAwait(() => typeof config === "function" ? config() : config, null);
|
|
8860
|
+
return {
|
|
8861
|
+
booting: await listedAwait(() => typeof booting === "function" ? booting() : booting, []),
|
|
8862
|
+
sims: await listedAwait(() => typeof sims === "function" ? sims() : sims, [], toolAbsent),
|
|
8863
|
+
adb: recordsOwnedEmulator(recorded) ? await listedAwait(() => typeof adb === "function" ? adb() : adb, NO_ADB, toolAbsent) : NO_ADB,
|
|
8864
|
+
config: recorded
|
|
8865
|
+
};
|
|
8866
|
+
} catch (error) {
|
|
8867
|
+
if (error instanceof DeviceCountUnavailable) {
|
|
8868
|
+
if ((0, ownership_claim_exports.isClaimRefusal)(error.cause)) throw error.cause;
|
|
8869
|
+
return uncountedRefusal(error.cause);
|
|
8870
|
+
}
|
|
8871
|
+
throw error;
|
|
8872
|
+
}
|
|
8873
|
+
}
|
|
8874
|
+
function readInventory({ sims = listSimsForCount, adb = listAdbForCount, config = loadConfig$1, booting = readBootingDevices }) {
|
|
8875
|
+
try {
|
|
8876
|
+
const recorded = listed(config, null);
|
|
8877
|
+
return {
|
|
8878
|
+
booting: listed(booting, []),
|
|
8879
|
+
sims: listed(sims, [], toolAbsent),
|
|
8880
|
+
adb: recordsOwnedEmulator(recorded) ? listed(adb, NO_ADB, toolAbsent) : NO_ADB,
|
|
8881
|
+
config: recorded
|
|
8882
|
+
};
|
|
8883
|
+
} catch (error) {
|
|
8884
|
+
if (error instanceof DeviceCountUnavailable) {
|
|
8885
|
+
if ((0, ownership_claim_exports.isClaimRefusal)(error.cause)) throw error.cause;
|
|
8886
|
+
return uncountedRefusal(error.cause);
|
|
8887
|
+
}
|
|
8888
|
+
throw error;
|
|
8889
|
+
}
|
|
8890
|
+
}
|
|
8891
|
+
/**
|
|
8892
|
+
* Owned simulators that are booted or booting, owned emulators adb lists in any state, and devices another
|
|
8893
|
+
* run is booting under `concurrency.maxDevices`, each counted once unless excluded; when a listing fails, why the count is unknown.
|
|
8894
|
+
*/
|
|
8895
|
+
function countLiveOwnedDevices(sources = {}, { exclude = [] } = {}) {
|
|
8896
|
+
const counts = countLiveOwnedDevicesByPlatform(sources, { exclude });
|
|
8897
|
+
return "unknown" in counts ? counts : counts.ios + counts.android;
|
|
8898
|
+
}
|
|
8899
|
+
/** The counts of {@link countLiveOwnedDevices} split by platform. */
|
|
8900
|
+
function countLiveOwnedDevicesByPlatform(sources = {}, { exclude = [] } = {}) {
|
|
8901
|
+
try {
|
|
8902
|
+
const inventory = readInventory(sources);
|
|
8903
|
+
if ("code" in inventory) return { unknown: inventory.message };
|
|
8904
|
+
const keys = inventoryKeys(inventory, exclude);
|
|
8905
|
+
return {
|
|
8906
|
+
ios: platformCount(keys, "ios"),
|
|
8907
|
+
android: platformCount(keys, "android")
|
|
8908
|
+
};
|
|
8909
|
+
} catch (error) {
|
|
8910
|
+
if ((0, ownership_claim_exports.isClaimRefusal)(error)) return { unknown: error.message };
|
|
8911
|
+
throw error;
|
|
8912
|
+
}
|
|
8913
|
+
}
|
|
8914
|
+
async function listedAsync(source, empty) {
|
|
8915
|
+
try {
|
|
8916
|
+
return await source();
|
|
8917
|
+
} catch (error) {
|
|
8918
|
+
if (toolAbsent(error)) return empty;
|
|
8919
|
+
throw new DeviceCountUnavailable("", { cause: error });
|
|
8920
|
+
}
|
|
8921
|
+
}
|
|
8922
|
+
async function countLiveOwnedDevicesFrom(sources, { exclude = [] } = {}) {
|
|
8923
|
+
try {
|
|
8924
|
+
const inventory = await readInventoryAsync(sources);
|
|
8925
|
+
return "code" in inventory ? { unknown: inventory.message } : inventoryKeys(inventory, exclude).size;
|
|
8926
|
+
} catch (error) {
|
|
8927
|
+
if ((0, ownership_claim_exports.isClaimRefusal)(error)) return { unknown: error.message };
|
|
8928
|
+
throw error;
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
8931
|
+
async function countLiveOwnedDevicesAsync(operation) {
|
|
8932
|
+
try {
|
|
8933
|
+
operation.signal.throwIfAborted();
|
|
8934
|
+
const config = loadConfig$1();
|
|
8935
|
+
const booting = readBootingDevices();
|
|
8936
|
+
const [sims, adb] = await Promise.all([process.platform === "darwin" ? listedAsync(() => listAllIosSimsAsync({ timeoutMs: LISTING_TIMEOUT_MS }), []) : [], recordsOwnedEmulator(config) ? listedAsync(() => listAdbDevicesAsync({ timeoutMs: LISTING_TIMEOUT_MS }), NO_ADB) : NO_ADB]);
|
|
8937
|
+
operation.signal.throwIfAborted();
|
|
8938
|
+
return countLiveOwnedDevices({
|
|
8939
|
+
config,
|
|
8940
|
+
booting,
|
|
8941
|
+
sims,
|
|
8942
|
+
adb
|
|
8943
|
+
});
|
|
8944
|
+
} catch (error) {
|
|
8945
|
+
operation.signal.throwIfAborted();
|
|
8946
|
+
if ((0, ownership_claim_exports.isClaimRefusal)(error)) return { unknown: error.message };
|
|
8947
|
+
if (error instanceof DeviceCountUnavailable) return { unknown: uncountedRefusal(error.cause).message };
|
|
8948
|
+
throw error;
|
|
8949
|
+
}
|
|
8950
|
+
}
|
|
8951
|
+
function workspaceHasLiveDevice({ platform, project, slot = "default", sims = [], adbEmulators = [] } = {}) {
|
|
8952
|
+
if (!platform) return false;
|
|
8953
|
+
const record = deviceSlotPlatforms(project, slot)?.[platform];
|
|
8954
|
+
if (!record) return false;
|
|
8955
|
+
if (platform === "ios") return sims.some((s) => s.udid === record.deviceUdid && LIVE_SIM_STATES.has(s.state));
|
|
8956
|
+
return typeof record.consolePort === "number" && adbEmulators.some((e) => e.consolePort === record.consolePort);
|
|
8957
|
+
}
|
|
8958
|
+
function deviceCapacityRefusal({ platform, project, slot = "default", max, platformMax, sims = [], adb = null, config = null, booting = [] } = {}) {
|
|
8959
|
+
if (!(max && max > 0) && !(platformMax && platformMax > 0)) return null;
|
|
8960
|
+
const adbEmulators = liveEmulatorPorts(adb ?? NO_ADB);
|
|
8961
|
+
if (workspaceHasLiveDevice({
|
|
8962
|
+
platform,
|
|
8963
|
+
project,
|
|
8964
|
+
slot,
|
|
8965
|
+
sims,
|
|
8966
|
+
adbEmulators
|
|
8967
|
+
})) return null;
|
|
8968
|
+
const full = deviceLimits(liveOwnedDeviceKeys({
|
|
8969
|
+
sims,
|
|
8970
|
+
adbEmulators,
|
|
8971
|
+
config,
|
|
8972
|
+
booting
|
|
8973
|
+
}), platform ?? "", max ?? 0, platformMax ?? 0).find((limit) => limit.count >= limit.max);
|
|
8974
|
+
return full ? {
|
|
8975
|
+
...atCapacityRefusal(full),
|
|
8976
|
+
max: full.max
|
|
8977
|
+
} : null;
|
|
8978
|
+
}
|
|
8979
|
+
/**
|
|
8980
|
+
* The early `concurrency.maxDevices` check, before Metro starts or a device is created. It is advisory, so an
|
|
8981
|
+
* unknown count passes: `withDeviceBootAdmission` makes the binding decision when the boot starts.
|
|
8982
|
+
*/
|
|
8983
|
+
async function checkDeviceCapacity({ platform, project, slot = "default", max, platformMax = platform && isCappedPlatform(platform) ? getPlatformDeviceLimits()[platform] : 0, ...sources } = {}) {
|
|
8984
|
+
if (!(max && max > 0) && !(platformMax && platformMax > 0)) return null;
|
|
8985
|
+
let inventory;
|
|
8986
|
+
try {
|
|
8987
|
+
inventory = await readInventoryAsync(sources);
|
|
8988
|
+
} catch (error) {
|
|
8989
|
+
if ((0, ownership_claim_exports.isClaimRefusal)(error)) return null;
|
|
8990
|
+
throw error;
|
|
8991
|
+
}
|
|
8992
|
+
if ("code" in inventory) return null;
|
|
8993
|
+
return deviceCapacityRefusal({
|
|
8994
|
+
platform,
|
|
8995
|
+
project,
|
|
8996
|
+
slot,
|
|
8997
|
+
max,
|
|
8998
|
+
platformMax,
|
|
8999
|
+
...inventory
|
|
9000
|
+
});
|
|
9001
|
+
}
|
|
9002
|
+
async function peekDeviceSlots({ platform, project, slot = "default", max = getConcurrencyLimits().maxDevices, platformMax = getPlatformDeviceLimits()[platform], sources = {} }) {
|
|
9003
|
+
try {
|
|
9004
|
+
const inventory = await readInventoryAsync(sources);
|
|
9005
|
+
if ("code" in inventory) return {
|
|
9006
|
+
count: null,
|
|
9007
|
+
max,
|
|
9008
|
+
queued: 0,
|
|
9009
|
+
localLive: false
|
|
9010
|
+
};
|
|
9011
|
+
const owned = deviceSlotPlatforms(project, slot)?.[platform]?.owned === true;
|
|
9012
|
+
const keys = inventoryKeys(inventory);
|
|
9013
|
+
const limits = deviceLimits(keys, platform, max, platformMax);
|
|
9014
|
+
const tightest = limits.length ? limits.reduce((a, b) => b.max - b.count < a.max - a.count ? b : a) : void 0;
|
|
9015
|
+
return {
|
|
9016
|
+
count: tightest?.count ?? keys.size,
|
|
9017
|
+
max: tightest?.max ?? max,
|
|
9018
|
+
queued: liveWaiters().filter((waiter) => !platformWalled(keys, waiter, {
|
|
9019
|
+
...getPlatformDeviceLimits(),
|
|
9020
|
+
[platform]: platformMax
|
|
9021
|
+
})).length,
|
|
9022
|
+
localLive: owned && workspaceHasLiveDevice({
|
|
9023
|
+
platform,
|
|
9024
|
+
project,
|
|
9025
|
+
slot,
|
|
9026
|
+
sims: inventory.sims,
|
|
9027
|
+
adbEmulators: liveEmulatorPorts(inventory.adb)
|
|
9028
|
+
}),
|
|
9029
|
+
...tightest?.platform ? { limit: tightest.setting } : {}
|
|
9030
|
+
};
|
|
9031
|
+
} catch (error) {
|
|
9032
|
+
if (!(0, ownership_claim_exports.isClaimRefusal)(error)) throw error;
|
|
9033
|
+
return {
|
|
9034
|
+
count: null,
|
|
9035
|
+
max,
|
|
9036
|
+
queued: 0,
|
|
9037
|
+
localLive: false
|
|
9038
|
+
};
|
|
9039
|
+
}
|
|
9040
|
+
}
|
|
9041
|
+
const DEFAULT_DEVICE_SLOT_WAIT_MS = 6e5;
|
|
9042
|
+
function deviceSlotWaitingLine({ count, max, holders, elapsedMs, queue, setting }) {
|
|
9043
|
+
return `${"device".padEnd(11)} waiting for a device slot (${count}/${max} in use${setting ? ` by ${setting}` : ""}${holders.length ? `: ${holders.join(", ")}` : ""}), ${formatElapsed(elapsedMs)} elapsed -- stim guide lifecycle concurrency${queue ? `; ${queue}` : ""}`;
|
|
9044
|
+
}
|
|
9045
|
+
function unrecordedHolder(label) {
|
|
9046
|
+
return {
|
|
9047
|
+
label,
|
|
9048
|
+
workspace: null,
|
|
9049
|
+
slot: null
|
|
9050
|
+
};
|
|
9051
|
+
}
|
|
9052
|
+
function slotHolders(inventory, keys) {
|
|
9053
|
+
const owners = /* @__PURE__ */ new Map();
|
|
9054
|
+
const projects = Object.entries(inventory.config?.projects ?? {});
|
|
9055
|
+
const labels = workspaceLabels(projects.map(([root, project]) => ({
|
|
9056
|
+
root,
|
|
9057
|
+
label: project.label
|
|
9058
|
+
})));
|
|
9059
|
+
for (const [root, project] of projects) {
|
|
9060
|
+
const label = labels.get(root) ?? basename(root);
|
|
9061
|
+
for (const { slot, platforms } of projectDeviceSlots(project)) {
|
|
9062
|
+
if (platforms.ios?.deviceUdid) owners.set(deviceKey("ios", platforms.ios.deviceUdid), {
|
|
9063
|
+
label,
|
|
9064
|
+
workspace: root,
|
|
9065
|
+
slot
|
|
9066
|
+
});
|
|
9067
|
+
if (platforms.android?.avdName) owners.set(deviceKey("android", platforms.android.avdName), {
|
|
9068
|
+
label,
|
|
9069
|
+
workspace: root,
|
|
9070
|
+
slot
|
|
9071
|
+
});
|
|
9072
|
+
}
|
|
9073
|
+
}
|
|
9074
|
+
for (const sim of inventory.sims) {
|
|
9075
|
+
const key = deviceKey("ios", sim.udid);
|
|
9076
|
+
if (!owners.has(key)) owners.set(key, unrecordedHolder(sim.name));
|
|
9077
|
+
}
|
|
9078
|
+
const booting = /* @__PURE__ */ new Set();
|
|
9079
|
+
for (const device of inventory.booting) {
|
|
9080
|
+
const key = deviceKey(device.platform, device.key);
|
|
9081
|
+
booting.add(key);
|
|
9082
|
+
if (!owners.has(key)) owners.set(key, unrecordedHolder(device.displayName ?? device.key));
|
|
9083
|
+
}
|
|
9084
|
+
return [...keys].map((key) => {
|
|
9085
|
+
const { label, workspace, slot } = owners.get(key) ?? unrecordedHolder(key.slice(key.indexOf(":") + 1));
|
|
9086
|
+
const platform = key.startsWith("android:") ? "android" : "ios";
|
|
9087
|
+
return booting.has(key) ? {
|
|
9088
|
+
platform,
|
|
9089
|
+
label,
|
|
9090
|
+
workspace,
|
|
9091
|
+
slot,
|
|
9092
|
+
booting: true
|
|
9093
|
+
} : {
|
|
9094
|
+
platform,
|
|
9095
|
+
label,
|
|
9096
|
+
workspace,
|
|
9097
|
+
slot
|
|
9098
|
+
};
|
|
9099
|
+
}).toSorted((a, b) => a.label.localeCompare(b.label) || a.platform.localeCompare(b.platform));
|
|
9100
|
+
}
|
|
9101
|
+
function deviceHolders(inventory, keys) {
|
|
9102
|
+
return [...new Set(slotHolders(inventory, keys).map((holder) => holder.label))].toSorted();
|
|
9103
|
+
}
|
|
9104
|
+
/**
|
|
9105
|
+
* `concurrency.maxDevices` usage for status (the owned devices the cap counts and who holds each) and the usage of each
|
|
9106
|
+
* per-platform cap that is set, from one inventory read. `sims` is the listing status already read; an unknown count
|
|
9107
|
+
* reports why instead of failing.
|
|
9108
|
+
*/
|
|
9109
|
+
async function deviceSlotUsages(max, platformMax, sources = {}) {
|
|
9110
|
+
const capped = [];
|
|
9111
|
+
for (const platform of ["ios", "android"]) {
|
|
9112
|
+
const cap = platformMax[platform] ?? 0;
|
|
9113
|
+
if (cap > 0) capped.push([platform, cap]);
|
|
9114
|
+
}
|
|
9115
|
+
const platformDevices = (count) => capped.length ? { platformDevices: Object.fromEntries(capped.map(([platform, cap]) => [platform, {
|
|
9116
|
+
max: cap,
|
|
9117
|
+
...count(platform)
|
|
9118
|
+
}])) } : {};
|
|
9119
|
+
try {
|
|
9120
|
+
const inventory = await readInventoryAsync(sources);
|
|
9121
|
+
if ("code" in inventory) return {
|
|
9122
|
+
...max > 0 ? { devices: {
|
|
9123
|
+
max,
|
|
9124
|
+
inUse: null,
|
|
9125
|
+
unknown: inventory.message,
|
|
9126
|
+
holders: []
|
|
9127
|
+
} } : {},
|
|
9128
|
+
...platformDevices(() => ({
|
|
9129
|
+
inUse: null,
|
|
9130
|
+
unknown: inventory.message
|
|
9131
|
+
}))
|
|
9132
|
+
};
|
|
9133
|
+
const keys = inventoryKeys(inventory);
|
|
9134
|
+
return {
|
|
9135
|
+
...max > 0 ? { devices: {
|
|
9136
|
+
max,
|
|
9137
|
+
inUse: keys.size,
|
|
9138
|
+
holders: slotHolders(inventory, keys)
|
|
9139
|
+
} } : {},
|
|
9140
|
+
...platformDevices((platform) => ({ inUse: platformCount(keys, platform) }))
|
|
9141
|
+
};
|
|
9142
|
+
} catch (error) {
|
|
9143
|
+
if (!(0, ownership_claim_exports.isClaimRefusal)(error)) throw error;
|
|
9144
|
+
return {
|
|
9145
|
+
...max > 0 ? { devices: {
|
|
9146
|
+
max,
|
|
9147
|
+
inUse: null,
|
|
9148
|
+
unknown: error.message,
|
|
9149
|
+
holders: []
|
|
9150
|
+
} } : {},
|
|
9151
|
+
...platformDevices(() => ({
|
|
9152
|
+
inUse: null,
|
|
9153
|
+
unknown: error.message
|
|
9154
|
+
}))
|
|
9155
|
+
};
|
|
9156
|
+
}
|
|
9157
|
+
}
|
|
9158
|
+
function liveWaiters() {
|
|
9159
|
+
const root = join(getConfigDir(), "device-waits");
|
|
9160
|
+
const survey = (0, ownership_claim_exports.inspectClaimSet)(root, { label: "device wait" });
|
|
9161
|
+
if (survey.exclusive) throw new ownership_claim_exports.ClaimRefusedError({
|
|
9162
|
+
root,
|
|
9163
|
+
claimPath: survey.exclusive.path,
|
|
9164
|
+
reason: "the wait queue is held exclusively",
|
|
9165
|
+
label: "device wait"
|
|
9166
|
+
});
|
|
9167
|
+
return survey.shared.toSorted((a, b) => (Number(a.details.sequence) || 0) - (Number(b.details.sequence) || 0) || a.startedAt.localeCompare(b.startedAt) || a.claimId.localeCompare(b.claimId));
|
|
9168
|
+
}
|
|
9169
|
+
function takeWaitTicket(device, waiters) {
|
|
9170
|
+
const root = join(getConfigDir(), "device-waits");
|
|
9171
|
+
const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
|
|
9172
|
+
root,
|
|
9173
|
+
mode: "shared",
|
|
9174
|
+
label: "device wait",
|
|
9175
|
+
details: {
|
|
9176
|
+
...device,
|
|
9177
|
+
sequence: Math.max(0, ...waiters.map((holder) => Number(holder.details.sequence) || 0)) + 1
|
|
9178
|
+
}
|
|
9179
|
+
});
|
|
9180
|
+
if (attempt.acquired) return attempt.acquired;
|
|
9181
|
+
throw new ownership_claim_exports.ClaimRefusedError({
|
|
9182
|
+
root,
|
|
9183
|
+
claimPath: attempt.held?.path ?? root,
|
|
9184
|
+
reason: "the wait queue is held exclusively",
|
|
9185
|
+
label: "device wait"
|
|
9186
|
+
});
|
|
9187
|
+
}
|
|
9188
|
+
function takeBootMarker(device) {
|
|
9189
|
+
const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
|
|
9190
|
+
root: bootingDevicesRoot(),
|
|
9191
|
+
mode: "shared",
|
|
9192
|
+
label: "device boot",
|
|
9193
|
+
details: { ...device }
|
|
9194
|
+
});
|
|
9195
|
+
if (attempt.acquired) return attempt.acquired;
|
|
9196
|
+
throw new DeviceAdmissionRefusal({
|
|
9197
|
+
code: "STIM_NO_DEVICE",
|
|
9198
|
+
message: `Another process holds ${bootingDevicesRoot()} exclusively, so this boot cannot be counted toward the device caps.`,
|
|
9199
|
+
remedy: "Retry once that process finishes."
|
|
9200
|
+
});
|
|
9201
|
+
}
|
|
9202
|
+
async function admissionTransaction(action, lockWaitMs, out, signal) {
|
|
9203
|
+
const started = Date.now();
|
|
9204
|
+
let lastLine = null;
|
|
9205
|
+
const onHeld = () => {
|
|
9206
|
+
const elapsed = Date.now() - started;
|
|
9207
|
+
if (elapsed < LOCK_QUIET_MS || lastLine !== null && Date.now() - lastLine < LOCK_PROGRESS_MS) return;
|
|
9208
|
+
lastLine = Date.now();
|
|
9209
|
+
out(phaseLine("device", `waiting for other runs to finish counting booted devices (${formatElapsed(elapsed)})`));
|
|
9210
|
+
};
|
|
9211
|
+
try {
|
|
9212
|
+
return await withWorkspaceProcessLock(getConfigDir(), ADMISSION_LOCK, action, {
|
|
9213
|
+
external: true,
|
|
9214
|
+
waitMs: lockWaitMs,
|
|
9215
|
+
onHeld,
|
|
9216
|
+
signal
|
|
9217
|
+
});
|
|
9218
|
+
} catch (error) {
|
|
9219
|
+
if (workspaceProcessLockError(error) !== "timeout") throw error;
|
|
9220
|
+
throw new DeviceAdmissionRefusal({
|
|
9221
|
+
code: "STIM_NO_DEVICE",
|
|
9222
|
+
message: `Waited ${formatElapsed(lockWaitMs)} for other runs to finish counting booted devices for the device caps.`,
|
|
9223
|
+
remedy: "Listing devices is slow, which usually means the machine is overloaded. Retry once the load falls."
|
|
9224
|
+
});
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
9227
|
+
/** Boots an owned device under the Stim home's cap, reserving capacity before releasing admission. */
|
|
9228
|
+
async function withDeviceBootAdmission(device, boot, { root, max = getConcurrencyLimits().maxDevices, platformMax = getPlatformDeviceLimits(), sources = {}, lockWaitMs = ADMISSION_LOCK_WAIT_MS, out = () => {}, waitMs = DEFAULT_DEVICE_SLOT_WAIT_MS, noWait = false, automatic = false, displayName = workspaceDisplayLabel(root, loadConfig$1()), reclaimIdleMinutes = deviceReclaimIdleMinutesSetting({}), now = Date.now, signal, sleep = (ms) => setTimeout$1(ms, void 0, { signal }), waitingFor = () => {}, onWait = () => {}, onBootStart = () => {} }) {
|
|
9229
|
+
const requireMembership = () => {
|
|
9230
|
+
if (automatic && !automaticMachineEnabled("device", "local")) throw new DeviceAdmissionRefusal({
|
|
9231
|
+
code: "STIM_HOSTING_REFUSED",
|
|
9232
|
+
message: "local is disabled in this requester's automatic device pool.",
|
|
9233
|
+
remedy: "Enable local in remote.devicePoolDisabled or select a machine explicitly."
|
|
9234
|
+
});
|
|
9235
|
+
};
|
|
9236
|
+
signal?.throwIfAborted();
|
|
9237
|
+
requireMembership();
|
|
9238
|
+
const ownCap = isCappedPlatform(device.platform) ? platformMax[device.platform] ?? 0 : 0;
|
|
9239
|
+
if (!(max > 0) && !(ownCap > 0)) {
|
|
9240
|
+
onBootStart();
|
|
9241
|
+
return boot();
|
|
9242
|
+
}
|
|
9243
|
+
const workspace = workspaceId(root);
|
|
9244
|
+
device = {
|
|
9245
|
+
...device,
|
|
9246
|
+
workspace,
|
|
9247
|
+
displayName
|
|
9248
|
+
};
|
|
9249
|
+
let ticket;
|
|
9250
|
+
let marker;
|
|
9251
|
+
let started;
|
|
9252
|
+
let lastLine = -Infinity;
|
|
9253
|
+
let visibleWait = false;
|
|
9254
|
+
let lastInventory;
|
|
9255
|
+
let lastTurn = false;
|
|
9256
|
+
let binding;
|
|
9257
|
+
let lastListed = -Infinity;
|
|
9258
|
+
let reclaimed = 0;
|
|
9259
|
+
let lastReclaim = -Infinity;
|
|
9260
|
+
const skippedReclaims = /* @__PURE__ */ new Set();
|
|
9261
|
+
try {
|
|
9262
|
+
try {
|
|
9263
|
+
for (;;) {
|
|
9264
|
+
const result = await admissionTransaction(async () => {
|
|
9265
|
+
signal?.throwIfAborted();
|
|
9266
|
+
requireMembership();
|
|
9267
|
+
const waiters = liveWaiters();
|
|
9268
|
+
const first = waiters[0];
|
|
9269
|
+
const mine = waiters.findIndex((holder) => holder.claimId === ticket?.claimId);
|
|
9270
|
+
const ahead = mine < 0 ? waiters : waiters.slice(0, mine);
|
|
9271
|
+
const admit = () => {
|
|
9272
|
+
const admitted = takeBootMarker(device);
|
|
9273
|
+
if ((0, ownership_claim_exports.releaseClaim)(ticket)) ticket = void 0;
|
|
9274
|
+
return { marker: admitted };
|
|
9275
|
+
};
|
|
9276
|
+
const wait = ({ limit, holders }, turn) => {
|
|
9277
|
+
const ticketId = ticket?.claimId;
|
|
9278
|
+
const position = ticketId ? waiters.findIndex((holder) => holder.claimId === ticketId) + 1 : waiters.length + 1;
|
|
9279
|
+
const headName = first?.details.displayName ?? displayName;
|
|
9280
|
+
const queue = `queue position ${position}${position > 1 ? " behind" : ", head"} ${headName}; queue: ${join(getConfigDir(), "device-waits")}`;
|
|
9281
|
+
if (noWait || waitMs === 0) throw new DeviceAdmissionRefusal(atCapacityRefusal(limit));
|
|
9282
|
+
if (started !== void 0 && now() - started >= waitMs) throw new DeviceAdmissionRefusal({
|
|
9283
|
+
code: "STIM_AT_CAPACITY",
|
|
9284
|
+
message: `Waited ${formatElapsed(now() - started)} for a device slot; ${limit.count}/${limit.max} Stim ${limit.noun}s are in use (${limit.setting}); ${queue}.`,
|
|
9285
|
+
remedy: `Stop an environment (stim stop), retry with a longer --wait <seconds>, or raise ${limit.setting}.`
|
|
9286
|
+
});
|
|
9287
|
+
if (!ticket) {
|
|
9288
|
+
ticket = takeWaitTicket(device, waiters);
|
|
9289
|
+
started = now();
|
|
9290
|
+
}
|
|
9291
|
+
const firstOfPlatform = ahead.every((holder) => holder.details.platform !== device.platform);
|
|
9292
|
+
return {
|
|
9293
|
+
limit,
|
|
9294
|
+
holders,
|
|
9295
|
+
queue,
|
|
9296
|
+
reclaim: turn && firstOfPlatform && limit.count >= limit.max
|
|
9297
|
+
};
|
|
9298
|
+
};
|
|
9299
|
+
if (ticket && lastInventory && ahead.length > 0 && !lastTurn && now() - lastListed < INVENTORY_REFRESH_MS) {
|
|
9300
|
+
const booting = sources.booting ?? readBootingDevices;
|
|
9301
|
+
const own = deviceKey(device.platform, device.key);
|
|
9302
|
+
if ((typeof booting === "function" ? booting() : booting).some((entry) => deviceKey(entry.platform, entry.key) === own)) return admit();
|
|
9303
|
+
return wait(lastInventory, false);
|
|
9304
|
+
}
|
|
9305
|
+
const inventory = await readInventoryAsync(sources);
|
|
9306
|
+
if ("code" in inventory) throw new DeviceAdmissionRefusal(inventory);
|
|
9307
|
+
const keys = inventoryKeys(inventory);
|
|
9308
|
+
const own = deviceKey(device.platform, device.key);
|
|
9309
|
+
const ownBooting = inventory.booting.some((entry) => deviceKey(entry.platform, entry.key) === own);
|
|
9310
|
+
if (device.platform === "ios" && keys.has(own) || ownBooting) return admit();
|
|
9311
|
+
keys.delete(own);
|
|
9312
|
+
const limits = deviceLimits(keys, device.platform, max, ownCap);
|
|
9313
|
+
const turn = ahead.every((waiter) => platformWalled(keys, waiter, platformMax));
|
|
9314
|
+
if (turn && limits.every((limit) => limit.count < limit.max)) return admit();
|
|
9315
|
+
lastListed = now();
|
|
9316
|
+
lastTurn = turn;
|
|
9317
|
+
const limit = bindingLimit(limits);
|
|
9318
|
+
if (!limit) return admit();
|
|
9319
|
+
binding = limit;
|
|
9320
|
+
lastInventory = {
|
|
9321
|
+
limit,
|
|
9322
|
+
holders: deviceHolders(inventory, limit.platform ? new Set([...keys].filter((key) => key.startsWith(`${limit.platform}:`))) : keys)
|
|
9323
|
+
};
|
|
9324
|
+
return wait(lastInventory, turn);
|
|
9325
|
+
}, lockWaitMs, out, signal);
|
|
9326
|
+
if ("marker" in result) {
|
|
9327
|
+
marker = result.marker;
|
|
9328
|
+
signal?.throwIfAborted();
|
|
9329
|
+
break;
|
|
9330
|
+
}
|
|
9331
|
+
signal?.throwIfAborted();
|
|
9332
|
+
const elapsedMs = now() - started;
|
|
9333
|
+
visibleWait = true;
|
|
9334
|
+
waitingFor({
|
|
9335
|
+
kind: "device-slot",
|
|
9336
|
+
inUse: result.limit.count,
|
|
9337
|
+
max: result.limit.max,
|
|
9338
|
+
since: new Date(started).toISOString()
|
|
9339
|
+
});
|
|
9340
|
+
if (now() - lastLine >= 1e4) {
|
|
9341
|
+
lastLine = now();
|
|
9342
|
+
out(deviceSlotWaitingLine({
|
|
9343
|
+
count: result.limit.count,
|
|
9344
|
+
max: result.limit.max,
|
|
9345
|
+
holders: result.holders,
|
|
9346
|
+
elapsedMs,
|
|
9347
|
+
queue: result.queue,
|
|
9348
|
+
...result.limit.platform ? { setting: result.limit.setting } : {}
|
|
9349
|
+
}));
|
|
9350
|
+
}
|
|
9351
|
+
signal?.throwIfAborted();
|
|
9352
|
+
if (result.reclaim && now() - lastReclaim >= 15e3) {
|
|
9353
|
+
lastReclaim = now();
|
|
9354
|
+
try {
|
|
9355
|
+
if (reclaimIdleMinutes > 0) reclaimed += await reclaimIdleDevice(root, reclaimIdleMinutes * 6e4, out, now, skippedReclaims, result.limit.platform);
|
|
9356
|
+
} catch (error) {
|
|
9357
|
+
out(phaseLine("device", `could not reclaim an idle device: ${error?.message || error}`));
|
|
9358
|
+
}
|
|
9359
|
+
}
|
|
9360
|
+
await sleep(Math.min(2e3, Math.max(0, waitMs - elapsedMs)));
|
|
9361
|
+
}
|
|
9362
|
+
} catch (error) {
|
|
9363
|
+
if (error instanceof DeviceAdmissionRefusal && error.code === "STIM_AT_CAPACITY") {
|
|
9364
|
+
const at = now();
|
|
9365
|
+
await withConfigLockAsync$1(() => recordCapacityRefusal({
|
|
9366
|
+
platform: device.platform,
|
|
9367
|
+
max: binding?.max ?? max,
|
|
9368
|
+
workspace
|
|
9369
|
+
}, at)).catch(() => {});
|
|
9370
|
+
}
|
|
9371
|
+
throw error;
|
|
9372
|
+
} finally {
|
|
9373
|
+
(0, ownership_claim_exports.releaseClaim)(ticket);
|
|
9374
|
+
if (visibleWait) waitingFor(null);
|
|
9375
|
+
if (started !== void 0) {
|
|
9376
|
+
const ms = Math.max(0, Math.round(now() - started));
|
|
9377
|
+
const at = now();
|
|
9378
|
+
await withConfigLockAsync$1(() => recordCapacityWait({
|
|
9379
|
+
platform: device.platform,
|
|
9380
|
+
ms,
|
|
9381
|
+
max: binding?.max ?? max,
|
|
9382
|
+
workspace,
|
|
9383
|
+
...reclaimed ? { reclaimed } : {}
|
|
9384
|
+
}, at)).catch(() => {});
|
|
9385
|
+
onWait(ms);
|
|
9386
|
+
}
|
|
9387
|
+
}
|
|
9388
|
+
requireMembership();
|
|
9389
|
+
onBootStart();
|
|
9390
|
+
return await boot();
|
|
9391
|
+
} finally {
|
|
9392
|
+
(0, ownership_claim_exports.releaseClaim)(marker);
|
|
9393
|
+
}
|
|
9394
|
+
}
|
|
9395
|
+
function deviceTypeMismatch(recordedTypeId, requestedName, deviceTypes) {
|
|
9396
|
+
if (!requestedName || !recordedTypeId) return null;
|
|
9397
|
+
const wanted = (deviceTypes || []).find((d) => d.name === requestedName);
|
|
9398
|
+
if (!wanted) return null;
|
|
9399
|
+
if (wanted.identifier === recordedTypeId) return null;
|
|
9400
|
+
const recorded = (deviceTypes || []).find((d) => d.identifier === recordedTypeId);
|
|
9401
|
+
return `this project's sim is ${recorded ? recorded.name : recordedTypeId}, but --device-type asked for ${requestedName}`;
|
|
9402
|
+
}
|
|
9403
|
+
function runtimeMismatch(recordedRuntimeId, requested, runtimes) {
|
|
9404
|
+
if (!requested || !recordedRuntimeId) return null;
|
|
9405
|
+
const wanted = runtimes.find((r) => iosRuntimeMatches(r, requested));
|
|
9406
|
+
if (!wanted || wanted.identifier === recordedRuntimeId) return null;
|
|
9407
|
+
return `this project's sim runs iOS ${parseRuntimeVersion(recordedRuntimeId)}, but --runtime asked for ${requested}`;
|
|
9408
|
+
}
|
|
9409
|
+
function installedNames(names) {
|
|
9410
|
+
const unique = [...new Set(names.filter((n) => typeof n === "string" && n !== ""))];
|
|
9411
|
+
return unique.length > 0 ? unique.join(", ") : "none";
|
|
9412
|
+
}
|
|
9413
|
+
/** Appends which settings layer supplied an unresolved device selector, so the user knows where to fix it. */
|
|
9414
|
+
function layerNote(refusal, key, flag, origin) {
|
|
9415
|
+
if (flag !== void 0 && flag !== null) return refusal;
|
|
9416
|
+
if (!origin) return refusal;
|
|
9417
|
+
return {
|
|
9418
|
+
message: `${refusal.message} ${key} is set at the ${origin} layer.`,
|
|
9419
|
+
remedy: `${refusal.remedy} Fix it with \`stim settings set ${key} <value> --scope ${origin}\` or \`stim settings unset ${key} --scope ${origin}\`.`
|
|
9420
|
+
};
|
|
9421
|
+
}
|
|
9422
|
+
function unknownIosRuntimeRefusal(requested, runtimes) {
|
|
9423
|
+
if (!requested) return null;
|
|
9424
|
+
if ((runtimes || []).some((r) => iosRuntimeMatches(r, requested))) return null;
|
|
9425
|
+
return {
|
|
9426
|
+
message: `No installed simulator runtime matches "${requested}". Installed runtimes: ${installedNames((runtimes || []).map((r) => r.version))}.`,
|
|
9427
|
+
remedy: "Pass `--runtime` (or set ios.runtime) a version printed above (\"26.5\") or a runtime's full name (\"iOS 26.5\"); nothing else matches. Install more runtimes through Xcode."
|
|
9428
|
+
};
|
|
9429
|
+
}
|
|
9430
|
+
function creatableIosDeviceTypeNames(runtimes, runtime) {
|
|
9431
|
+
return (runtime ? (runtimes || []).filter((r) => iosRuntimeMatches(r, runtime)) : runtimes || []).flatMap((r) => (r.supportedDeviceTypes || []).map((d) => d.name));
|
|
9432
|
+
}
|
|
9433
|
+
function unknownIosDeviceTypeRefusal(requested, runtimes, runtime) {
|
|
9434
|
+
if (!requested) return null;
|
|
9435
|
+
const creatable = creatableIosDeviceTypeNames(runtimes, runtime);
|
|
9436
|
+
if (creatable.includes(requested)) return null;
|
|
9437
|
+
return {
|
|
9438
|
+
message: `No device type named "${requested}" can be created on ${runtime ? `runtime ${runtime}` : "any installed simulator runtime"}. ${runtime ? `Device types runtime ${runtime} supports` : "Device types the installed runtimes support"}: ${installedNames(creatable)}.`,
|
|
9439
|
+
remedy: "Pass `--device-type` (or set ios.deviceType) one of the names printed above, exactly as `xcrun simctl list devicetypes` spells it. `xcrun simctl list devicetypes` also lists watchOS, tvOS and visionOS models, which no iOS runtime can create. Install more models through Xcode."
|
|
9440
|
+
};
|
|
9441
|
+
}
|
|
9442
|
+
function unknownAndroidSystemImageRefusal(requested, images) {
|
|
9443
|
+
if (!requested) return null;
|
|
9444
|
+
if ((images || []).some((i) => i.pkg === requested)) return null;
|
|
9445
|
+
return {
|
|
9446
|
+
message: `No installed Android system image is named "${requested}". Installed system images: ${installedNames((images || []).map((i) => i.pkg))}.`,
|
|
9447
|
+
remedy: "Pass `--system-image` (or set android.systemImage) to one of the package ids printed above."
|
|
9448
|
+
};
|
|
9449
|
+
}
|
|
9450
|
+
function unknownAndroidDeviceProfileRefusal(requested, profiles) {
|
|
9451
|
+
if (!requested) return null;
|
|
9452
|
+
if (profiles.includes(requested)) return null;
|
|
9453
|
+
return {
|
|
9454
|
+
message: `No Android hardware profile is named "${requested}". Profiles avdmanager offers: ${installedNames(profiles)}.`,
|
|
9455
|
+
remedy: "Pass `--device-profile` (or set android.deviceProfile) to one of the ids printed above, exactly as `avdmanager list device -c` spells it, e.g. \"pixel_fold\" or \"pixel_tablet\"."
|
|
9456
|
+
};
|
|
9457
|
+
}
|
|
9458
|
+
//#endregion
|
|
9459
|
+
export { levelStillShort as $, providerFromConfig as $n, remoteAndroidDeps as $t, budgetLine as A, installAndroidApp as An, sizeCaches as Ar, deleteParkedAvds as At, stopOwnedMetro as B, readRemoteSimulatorArch as Bn, unverifiedDeviceCommand as Bt, CCACHE_UNAVAILABLE as C, androidDevClientUrl as Cn, scopedMemoryReport as Cr, waitForGradleExit as Ct, readCcacheActivity as D, deviceShellArg as Dn, readFootprints as Dr, collectDeviceLeases as Dt, projectCmakeLauncher as E, devClientUrl as En, attributeMachineUsage as Er, DEVICE_LIST_TIMEOUT_MS$1 as Et, peekBudget as F, launchAndroidReleaseApp as Fn, deviceSweepIsScoped as Ft, devServerStopRecord as G, RESOLVE_TIMEOUT_MS as Gn, withRemoteSessionGcLocks as Gt, describeDevServerStop as H, exitAfterFlush as Hn, collectEasSessionSweep as Ht, readVolumeSpace as I, launchIosApp as In, eraseParkedDevices as It, requestDevServerStop as J, checkEasAuth as Jn, bundleEntryPoint as Jt, logVanishedSupervisor as K, UPLOAD_TIMEOUT_MS as Kn, REMOTE_SESSION_ERROR as Kt, readVolumeSpaceAsync as L, restartedAppNote as Ln, findOrphanedDevices as Lt, enforceBudget as M, installIosApp as Mn, deleteProjectDevices as Mt, inspectBudget as N, iosAppProcess as Nn, describeParkedSims as Nt, resolveCcache as O, ensureMetroReverse as On, discoverCaches as Or, collectParkedAvds as Ot, machineNumber as P, launchAndroidApp as Pn, describeUnverifiableDevices as Pt, levelFloors as Q, ownerFromConfig as Qn, readInstalledEasCliVersionAsync as Qt, resolveBudget as R, reverseMetroPorts as Rn, findStaleDeviceRecords as Rt, CCACHE_NOT_RUN as S, androidAppProcess as Sn, reclaimMemory as Sr, includesGradleVersions as St, ccacheInstallCommand as T, clearOtherUserApps as Tn, phase as Tr, collectInventory as Tt, describeMetroLastStop as U, processContext as Un, deleteEasSessions as Ut, stopOwnedMetroForReset as V, gateMetroOrigin as Vn, withAndroidAvdSizes as Vt, devServerStopLevel as W, runProcessOperation as Wn, describeError as Wt, withdrawDevServerStopRequest as X, isEasAuthFailureText as Xn, ensureMetroReachable as Xt, vanishedSupervisorMessage as Y, easAuthNote as Yn, endRecordedSession as Yt, diskPressureLevel as Z, loadProjectProvider as Zn, ensureRemoteBootOwned as Zt, withDeviceBootAdmission as _, APP_READINESS_TIMEOUT_MS as _n, memoryCacheKind as _r, collectGradleVersions as _t, countLiveOwnedDevicesAsync as a, LAUNCH_UNVERIFIED as an, remoteCliInvocation as ar, maintenanceLogger as at, shutDownIdleDevices as b, INSTALL_ERROR as bn, memorySweepIsScoped as br, gradleVersionLines as bt, deviceSlotUsages as c, VERIFY_TIMEOUT_MS as cn, MIN_EAS_CLI_SIMULATOR_VERSION as cr, includesParkedDevices as ct, peekDeviceSlots as d, readMetroRecords as dn, ownedSessionName as dr, isWorkspacesScope as dt, remoteIosDeps as en, readProjectConfig as er, lowestFree as et, runtimeMismatch as f, unattributedLaunchLines as fn, parseArchiveSelector as fr, planCacheEmptying as ft, unknownIosRuntimeRefusal as g, verifyReleaseLaunch as gn, inspectWatchmanMemory as gr, trimCaches as gt, unknownIosDeviceTypeRefusal as h, verifyLaunch as hn, collectMemoryReport as hr, selectsBeyondCaches as ht, countLiveOwnedDevices as i, LAUNCH_FATAL as in, bundleInvocation as ir, openGcLog as it, cacheLabel as j, installConflictKind as jn, deleteParkedSims as jt, budgetGate as k, hasMetroReverse as kn, pruneCache as kr, collectParkedSims as kt, deviceTypeMismatch as l, isBundleProof as ln, easCliSupport as lr, includesRecordings as lt, unknownAndroidSystemImageRefusal as m, verifyAndroidReleaseLaunch as mn, WATCHMAN_KIND as mr, selectCaches as mt, DeviceAdmissionRefusal as n, withEasProjectLock as nn, resolveRemote as nr, gcOptionsOf as nt, countLiveOwnedDevicesByPlatform as o, MANIFEST_VERIFY_TIMEOUT_MS as on, remoteCommand as or, emptyCaches as ot, unknownAndroidDeviceProfileRefusal as p, unverifiedLaunchLines as pn, GRADLE_DAEMONS_KIND as pr, previewScopedEmptying as pt, metroLastStop as q, cacheLevel as qn, binOnPath as qt, checkDeviceCapacity as r, LAUNCH_BUNDLING as rn, uploadRemote as rr, maintenanceItem as rt, countLiveOwnedDevicesFrom as s, RELEASE_VERIFY_WAIT_MS as sn, MIN_EAS_CLI_DEVICE_VERSION as sr, includesArchives as st, DEFAULT_DEVICE_SLOT_WAIT_MS as t, resolveRemoteContext as tn, resolveEasCliBin as tr, protectWindowMs as tt, layerNote as u, readCollectorRecords as un, easCliUpgradeRemedy as ur, includesWorkspaceOutputs as ut, clearDeviceIdleShutdown as v, ADB_INSTALL_TIMEOUT_MS as vn, memoryLines as vr, deleteGradleVersion as vt, ccacheActivityLine as w, appProcessFromPid as wn, stopIdleGradleDaemons as wr, getMachineCacheReport as wt, writeIdleShutdownEvent as x, IOS_DEV_MENU_OFF_DEFAULTS_PLIST as xn, memoryWorkPending as xr, gradleVersionsPending as xt, dueIdleDevices as y, DEFAULT_METRO_PORT as yn, memoryScope as yr, deleteGradleVersions as yt, trimmableCaches as z, writeDebugHttpHost as zn, findStaleProjectDevices as zt };
|