stim 1.21.0 → 1.22.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/README.md +60 -7
- package/dist/{acquire-artifact-CD4lbiWE.mjs → acquire-artifact-Co2k4f7O.mjs} +21 -3
- package/dist/{act-DHAw2A2S.mjs → act-DsuqEkiM.mjs} +298 -164
- package/dist/{activity-BzVfHSrY.mjs → activity-CMLmzp7o.mjs} +33 -12
- package/dist/{agent-device-usage-output-zh1vhbLp.mjs → agent-device-usage-output-DUFaTMdd.mjs} +7 -7
- package/dist/{android-BLgbNqox.mjs → android-ByRMUoiJ.mjs} +66 -52
- package/dist/{android-DUYkNF4u.mjs → android-C7sVmyI9.mjs} +160 -59
- package/dist/{android-CV_GCELZ.mjs → android-DGkNqa_c.mjs} +5 -5
- package/dist/{android-BEJ8Jpl7.mjs → android-DR5lZ2Az.mjs} +2 -2
- package/dist/android-cas-compiler.mjs +1 -1
- package/dist/{android-cas-Ct5b1xCK.mjs → android-cas-ngferVq2.mjs} +3 -3
- package/dist/{client-B7QVOlCr.d.mts → android-project-DVWyCbWU.d.mts} +145 -7
- package/dist/api.d.mts +132 -118
- package/dist/api.mjs +239 -42
- package/dist/{artifact-B8T3S5gi.mjs → artifact-8EY8SAPw.mjs} +40 -35
- package/dist/{artifact-DAuZ-FNU.mjs → artifact-DJPy518Y.mjs} +69 -40
- package/dist/artifact-platform-CXcmX_j3.mjs +27 -0
- package/dist/{browser-web-QQzVjz3O.mjs → browser-web-Bcv99PBM.mjs} +187 -59
- package/dist/{budget-Bs6SxxzO.mjs → budget-Cvwn54qV.mjs} +830 -699
- package/dist/{build-CoPzQRNw.mjs → build-CabC3G23.mjs} +34 -27
- package/dist/{build-cache-DmyR9Zzc.mjs → build-cache-Dv7RN200.mjs} +4 -4
- package/dist/{build-plan-CaFskB7_.mjs → build-plan-DUC8Ka9y.mjs} +74 -14
- package/dist/{cache-manifest-DkkqguoK.mjs → cache-manifest-DSeQjt-Q.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{ccache-BJRZ9pG3.mjs → ccache-Btwc36-I.mjs} +6 -6
- package/dist/{cdp-CWmIUMbw.mjs → cdp-3fwICjzi.mjs} +1 -1
- package/dist/{chrome-B6tfCta1.mjs → chrome-Bbw689-6.mjs} +1 -1
- package/dist/{cli-0DYf8QhQ.mjs → cli-CHZa9Lvk.mjs} +20 -20
- package/dist/cli.mjs +1 -1
- package/dist/{client-UXD70ZbM.mjs → client-OyRoEuSr.mjs} +218 -47
- package/dist/collector-run.d.mts +1 -1
- package/dist/collector-run.mjs +6 -6
- package/dist/{command-output-B1UrjZHL.mjs → command-output-gwpm28fu.mjs} +1 -1
- package/dist/{config-CYcBF478.mjs → config-DlrsduwG.mjs} +17 -11
- package/dist/{created-devices-CqeBlrEn.mjs → created-devices-x9rN6bhs.mjs} +13 -3
- package/dist/{dependency-state-BbKQW83h.mjs → dependency-state-1I9KZT1Y.mjs} +2 -2
- package/dist/{detached-entry-DFRyG1_o.mjs → detached-entry-BwNEtIrP.mjs} +1 -1
- package/dist/{dev-client-UOeESKm6.mjs → dev-client-BSDR5h3F.mjs} +2 -2
- package/dist/{device-ExJq8Ei7.mjs → device-B-L3Fbxy.mjs} +71 -14
- package/dist/{device-DCxH7_By.mjs → device-DklWZLqb.mjs} +16 -16
- package/dist/{device-capacity-CMlqEcvw.mjs → device-capacity-DK5_EwRS.mjs} +9 -10
- package/dist/device-host-worker.mjs +25 -32
- package/dist/{device-ios-DRUeKaFV.mjs → device-ios-DoB6KFCO.mjs} +39 -16
- package/dist/{device-lease-run-C8raGFoP.mjs → device-lease-run-xJPQ_QQG.mjs} +5 -5
- package/dist/{device-pool-DMRpVX9a.mjs → device-pool-BMkdsefq.mjs} +3 -3
- package/dist/{doctor-DTvaDOda.mjs → doctor-Bo7BXLux.mjs} +24 -37
- package/dist/{doctor-Dhqebm0q.mjs → doctor-D5_EzwfA.mjs} +19 -16
- package/dist/doctor-fingerprint-DZLoYFVb.mjs +16 -0
- package/dist/doctor-fingerprint.mjs +1 -1
- package/dist/{doctor-BLHCYxzi.mjs → doctor-thjWAV0z.mjs} +1 -1
- package/dist/{doctor-watchman-Cv1F10Kj.mjs → doctor-watchman-N2wDotwQ.mjs} +5 -5
- package/dist/{eas-build-BomfRnFT.mjs → eas-build-CwknaQYj.mjs} +4 -4
- package/dist/{eas-fallback-DJwZpy0c.mjs → eas-fallback-CSgAulr5.mjs} +27 -18
- package/dist/{eas-session-ledger-op6A54yi.mjs → eas-session-ledger-DlY_serQ.mjs} +8 -7
- package/dist/{error-diagnostics-Bf0rZvG9.mjs → error-diagnostics-DBWO2rxE.mjs} +10 -10
- package/dist/{exec-_UbtCQcG.mjs → exec-DEoAhcze.mjs} +155 -47
- package/dist/{exec-H20xSg2T.d.mts → exec-DnYc4HGH.d.mts} +17 -4
- package/dist/{gc-_-dCXdyL.mjs → gc-CXxN-4SE.mjs} +587 -313
- package/dist/gc-DnNDQyaP.mjs +3 -0
- package/dist/{guide-Yqwt2bd6.mjs → guide-Br0LEfKy.mjs} +239 -43
- package/dist/{hosted-android-Bw-honsd.mjs → hosted-android-B-9f-jkd.mjs} +6 -6
- package/dist/{hosted-client-DB_gBPfY.mjs → hosted-client-DZAPrcro.mjs} +4 -4
- package/dist/{hosted-ios-CUvOf4Vr.mjs → hosted-ios-CsCS21hn.mjs} +1 -1
- package/dist/{hosted-logs-DL_P5AdN.mjs → hosted-logs-FIRx9X2f.mjs} +5 -5
- package/dist/{hosted-macos-QI141yKk.mjs → hosted-macos-_awYSpfv.mjs} +21 -12
- package/dist/{hosted-native-Ksyjgzq-.mjs → hosted-native-BdzE668-.mjs} +27 -14
- package/dist/{idle-BVeRGroT.d.mts → idle-C4cZ757K.d.mts} +3 -3
- package/dist/{idle-shutdown-DaKmZi0z.mjs → idle-shutdown-DDg3SdvY.mjs} +7 -7
- package/dist/ios-CDvlmUs7.d.mts +24 -0
- package/dist/{ios-CQ68hG9s.mjs → ios-Ds1clbIt.mjs} +8 -8
- package/dist/{ios-device-CwFMehUS.mjs → ios-device--h_-8ENa.mjs} +3 -3
- package/dist/{ios-device-CXGP1bsD.mjs → ios-device-BXZ0c7Kb.mjs} +1 -1
- package/dist/{ios-CpByZy12.mjs → ios-hcBpnEGk.mjs} +81 -34
- package/dist/{ios-ubFsBzwq.mjs → ios-pm4nL6By.mjs} +59 -50
- package/dist/{ios-project-WuwwBdvW.mjs → ios-project-CZhKODF3.mjs} +5 -5
- package/dist/{ios-signing-DTTIChGs.mjs → ios-signing-BtAffm0A.mjs} +2 -2
- package/dist/{ios-state-DvFX-K_l.mjs → ios-state-Dtw5K2aI.mjs} +1 -1
- package/dist/{ios-qNJt4dFm.mjs → ios-xtlRHaeB.mjs} +1 -1
- package/dist/{js-swap-Cc1dttps.mjs → js-swap-Bwhh4S7V.mjs} +7 -29
- package/dist/{launch-Zq2mOSDy.mjs → launch-BjxemNpK.mjs} +40 -39
- package/dist/{launch-CD1yVc7U.mjs → launch-CCzXlc8r.mjs} +16 -16
- package/dist/{logs-BS00Anxf.mjs → logs-BfwwadsZ.mjs} +151 -24
- package/dist/{logs-CMm05HxN.mjs → logs-jTS_lHSo.mjs} +10 -17
- package/dist/{macos-B65--pPd.mjs → macos-DVlSSyUg.mjs} +1 -1
- package/dist/{macos-C-0qJ6zq.mjs → macos-NfV2D7dO.mjs} +143 -46
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +8 -8
- package/dist/{metro-DEXpIhHC.mjs → metro-DiXtA3Ty.mjs} +59 -37
- package/dist/{metro-gateway-BMJNlj3E.mjs → metro-gateway-CKZqKqx7.mjs} +120 -22
- package/dist/{named-ports-BWTzt6mu.mjs → named-ports-DkEzOfzO.mjs} +40 -33
- package/dist/{native-android-JACyjTZs.mjs → native-android-CqgnOgsT.mjs} +43 -242
- package/dist/native-gradle-artifact-inputs-DHFnmrVf.mjs +270 -0
- package/dist/{native-gradle-inputs-Bep9JcSF.mjs → native-gradle-inputs-Dnh84Zbd.mjs} +3 -129
- package/dist/{native-run-DY_RLx3C.mjs → native-run-NwUuxbkg.mjs} +4 -5
- package/dist/{native-runtime-Dg1Q7tfF.mjs → native-runtime-B7KIafpM.mjs} +8 -7
- package/dist/native-source-Y0yLhFyQ.mjs +133 -0
- package/dist/{native-xcode-inputs-CB2iQeWq.mjs → native-xcode-inputs-D_VOfFL7.mjs} +2 -3
- package/dist/{native-xcode-ios-CXX39F7o.mjs → native-xcode-ios-BYDOHWLN.mjs} +27 -32
- package/dist/offload-worker.d.mts +65 -2
- package/dist/offload-worker.mjs +305 -73
- package/dist/operation-exec-BW0em61G.mjs +171 -0
- package/dist/{ownership-DEmM5vhh.mjs → ownership-C5fo5Edb.mjs} +3 -3
- package/dist/{page-CPaHeYqj.mjs → page-DBL2PiUU.mjs} +8 -4
- package/dist/{placement-log-BqGbTa61.mjs → placement-log-BnGgJRRZ.mjs} +1 -1
- package/dist/{plan-placement-Bwj-IjY1.mjs → plan-placement-DIsMtN-d.mjs} +12 -8
- package/dist/{ports-D-MmCTdv.mjs → ports-Cy1RkmuQ.mjs} +3 -3
- package/dist/{prebuild-uD_ipCxF.mjs → prebuild-D4JoEAEs.mjs} +49 -14
- package/dist/{project-BZNjmW_8.mjs → project-wM_XFTvE.mjs} +16 -16
- package/dist/{pull-request-Lfetn8Kr.mjs → pull-request-W1IuLXDt.mjs} +102 -20
- package/dist/{pull-request-zjCSAryH.d.mts → pull-request-eODyIqZF.d.mts} +6 -2
- package/dist/pull-requests.d.mts +2 -2
- package/dist/pull-requests.mjs +2 -2
- package/dist/{react-native-android-DuzH0cn3.mjs → react-native-android-6OZ7MSM7.mjs} +71 -48
- package/dist/{react-native-build-rhk4smwQ.mjs → react-native-build-Dvd8hC21.mjs} +8 -8
- package/dist/{react-native-doctor-CQpuIMso.mjs → react-native-doctor-DsoSF1Y9.mjs} +7 -7
- package/dist/{react-native-ios-Bhq1rJeV.mjs → react-native-ios-4u1Rn-GR.mjs} +40 -26
- package/dist/{recordings-DYteQSu4.mjs → recordings-CQmfzAh7.mjs} +1 -1
- package/dist/registration-BcRgF6Ox.mjs +51 -0
- package/dist/{reload-CmTRFJaX.mjs → reload-BzkYabB-.mjs} +16 -15
- package/dist/{reload-DzjSl5P_.mjs → reload-D7AVlb8N.mjs} +1 -1
- package/dist/{remote-ByYkXdGD.mjs → remote-DjGDmlTZ.mjs} +18 -15
- package/dist/{result-DNiS-tTP.mjs → result-D2Ykz_iD.mjs} +24 -23
- package/dist/{run-D23HmJMs.mjs → run-DidHghD1.mjs} +3 -2
- package/dist/{selection-CSl9i5qU.mjs → selection-DSMbWEX2.mjs} +6 -5
- package/dist/{server-bare-CxTrvDg8.mjs → server-bare-CafzuyFB.mjs} +2 -2
- package/dist/{server-command-CtzloH3v.mjs → server-command-C6QjeupS.mjs} +5 -5
- package/dist/{server-expo-DT_F_uVI.mjs → server-expo-CrjB29pq.mjs} +7 -5
- package/dist/server-expo-D58c9L0M.mjs +2 -0
- package/dist/{settings-CsJDEXgK.mjs → settings-C7O8A6ZI.mjs} +223 -243
- package/dist/{settings-T1gtc3bf.mjs → settings-DpR-1Wnn.mjs} +1 -1
- package/dist/{settings-write-HyVZ8eQ8.mjs → settings-write-CzD60rOD.mjs} +7 -6
- package/dist/settings.schema.json +13 -0
- package/dist/{simslim-Cgk17SXV.mjs → simslim-BNckWpXi.mjs} +2 -2
- package/dist/{slot-launch-DF8VfT64.mjs → slot-launch-CbfmNgM6.mjs} +6 -6
- package/dist/{stage-BIS0ydsV.mjs → stage-Bps5oius.mjs} +21 -4
- package/dist/{start-RChynRDp.mjs → start-Br-oxqMD.mjs} +1 -1
- package/dist/{start-7lpH7hmN.mjs → start-EojN4WFz.mjs} +622 -393
- package/dist/{state-C4ZtUisW.mjs → state--smjFSTL.mjs} +22 -7
- package/dist/{state-XMziexv2.mjs → state-08I3hw3_.mjs} +2 -2
- package/dist/{stats-53T7OTb9.mjs → stats-D3GerlFW.mjs} +5 -5
- package/dist/{stats-report-BFfMpWWF.mjs → stats-report-BzaPrRm6.mjs} +1 -1
- package/dist/{status-DqEUSSxs.mjs → status-BRROykC8.mjs} +2 -2
- package/dist/{status-CSi6tlRq.mjs → status-EvrK72L2.mjs} +1 -1
- package/dist/{status-BL7jVyAB.mjs → status-WBDajew2.mjs} +43 -29
- package/dist/{status-watch-GDCfqpTE.mjs → status-watch-Bg_u-pwR.mjs} +48 -16
- package/dist/status-worker.mjs +1 -1
- package/dist/{stim-desktop-C12we7lz.mjs → stim-desktop-D5OGpYZM.mjs} +2 -2
- package/dist/{stop-Bk_1qiV6.mjs → stop-DTZ9uTw3.mjs} +2 -2
- package/dist/{stop-D7h6jAXQ.mjs → stop-Dky10AEJ.mjs} +31 -22
- package/dist/{stop-CPzlM1L-.mjs → stop-Mv6W9FAk.mjs} +15 -16
- package/dist/supervisor-run.d.mts +3 -3
- package/dist/supervisor-run.mjs +28 -36
- package/dist/{support-Dlu37g6H.mjs → support-CNioewsd.mjs} +10 -10
- package/dist/{support-67vraa1Q.mjs → support-yUiOjnVY.mjs} +8 -8
- package/dist/swiftpm-artifact-inputs-DFcGsXDP.mjs +153 -0
- package/dist/swiftpm-macos-BIak-Rxv.mjs +532 -0
- package/dist/toolchain-BY8X8HXx.mjs +2 -0
- package/dist/{toolchain-Hp9sCpK4.mjs → toolchain-DpJMHq0n.mjs} +9 -6
- package/dist/{trigger-BAahxUPq.mjs → trigger-Cbyc41d_.mjs} +3 -3
- package/dist/trigger-yb_nFzPF.mjs +2 -0
- package/dist/{warm-progress-BHX5r0Ei.mjs → warm-progress-CA3TgT3-.mjs} +29 -16
- package/dist/web-CZ07gd77.mjs +2 -0
- package/dist/{web-CJmIj6AI.mjs → web-DKx6yoFA.mjs} +11 -10
- package/dist/web-run.mjs +11 -9
- package/dist/windows-report.mjs +1 -1
- package/dist/{workspace-state-Cjuigeeu.mjs → workspace-state-U08dEzhG.mjs} +3 -3
- package/dist/{workspaces-BccMFmGQ.mjs → workspaces-CpANmBlq.mjs} +594 -266
- package/dist/{worktree-DQ6Zi2Mv.mjs → worktree-CHecZ4Oq.mjs} +850 -551
- package/dist/worktree-ZqZVjD9K.mjs +2 -0
- package/dist/{xcode-Zlx3qjEs.mjs → xcode-BXn9-xYx.mjs} +5 -5
- package/package.json +7 -7
- package/shim/android-audio/Main.java +24 -0
- package/shim/android-audio/README.md +17 -0
- package/shim/android-audio.jar +0 -0
- package/dist/gc-C-WwL0yi.mjs +0 -3
- package/dist/git-C3iTsJaZ.mjs +0 -73
- package/dist/server-expo-C-90EhcG.mjs +0 -2
- package/dist/settings-DkdmlelE.d.mts +0 -15
- package/dist/swiftpm-macos-p8WZbxKU.mjs +0 -130
- package/dist/toolchain-DhKwp9iI.mjs +0 -2
- package/dist/trigger-Bf3uDcpv.mjs +0 -2
- package/dist/web-DknQETPs.mjs +0 -2
- package/dist/workspace-process-lock-1_1OoVW5.mjs +0 -57
- package/dist/worktree-CK2MpJW5.mjs +0 -2
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as workspaceProcessLockError, g as process_identity_exports, h as withDrainingCancellation, i as withWorkspaceProcessLock, r as WorkspaceProcessCleanupError, t as getExecutor, x as ownership_claim_exports } from "./exec-DEoAhcze.mjs";
|
|
2
2
|
import { l as workspaceAgentDeviceDir, m as workspaceLogsDir$1, n as ensureWorkspaceStorage, s as supervisorLogFile } from "./paths-DQtTgsBf.mjs";
|
|
3
|
-
import { E as upsertProject, p as getProject } from "./config-
|
|
4
|
-
import { _ as stepTimer, l as phaseLine } from "./command-output-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { E as upsertProject, O as withConfigLockAsync$1, p as getProject } from "./config-DlrsduwG.mjs";
|
|
4
|
+
import { _ as stepTimer, l as phaseLine } from "./command-output-gwpm28fu.mjs";
|
|
5
|
+
import { f as throwIfCancelled, u as cancellableSleep } from "./operation-exec-BW0em61G.mjs";
|
|
6
|
+
import { i as recordWorkspaceUse, l as withWorkspaceStateLockAsync, n as clearWorkspaceStateKeys, r as readWorkspaceState$1, s as updateWorkspaceStateAsync } from "./workspace-state-U08dEzhG.mjs";
|
|
7
|
+
import { A as metroIdleStopMinutesSetting, G as resolveCacheProviderConfig, J as resolveProjectSettingsAsync, K as resolveIosProjectDir, M as metroTunnelSettingError, O as metroCommandSetting, P as ngrokUrlSetting, R as publicUrlSetting, U as remoteIosSetting, V as remoteAndroidSetting, W as resolveAndroidLayout, a as SETTING_SHAPE_REMEDY, ct as detectProviders, et as settingShapeErrors, h as deviceIdleShutdownMinutesSetting, it as unknownSettingKeys, j as metroPortSetting, k as metroCommandSettingError, lt as planMetroReach, m as defaultAndroidLayout, p as cacheProviderSettingError, rt as tunnelModeSetting, st as PUBLIC_METRO_ENV } from "./settings-C7O8A6ZI.mjs";
|
|
8
|
+
import { E as MODE_EXPO, M as readMetroTunnel, O as clearManagedMetroTunnel, S as readAppJson, T as MODE_COMMAND, c as projectProblem, i as findProjectRoot$1, t as NO_PROJECT_REFUSAL, v as detectIsExpo, w as MODE_BARE, x as readAppConfigText } from "./project-wM_XFTvE.mjs";
|
|
8
9
|
import { i as levelRank } from "./ndjson-CgE_o3Dl.mjs";
|
|
9
|
-
import { _ as
|
|
10
|
-
import { Kn as readProjectConfig, p as stopOwnedMetroForReset, t as budgetGate } from "./budget-
|
|
11
|
-
import { _ as
|
|
12
|
-
import { a as workspaceLinkLine, o as workspaceLinks } from "./stim-desktop-
|
|
13
|
-
import { n as workspaceProcessLockError, t as withWorkspaceProcessLock } from "./workspace-process-lock-1_1OoVW5.mjs";
|
|
10
|
+
import { _ as PORT_INSPECTION_REMEDY, a as listeningPidsAsync, c as pidExists, d as resolveProjectMetro, f as signalProcessTree, g as reserveMetroPort, h as isMetroRunning, u as processCwds, v as PortInspectionError } from "./metro-DiXtA3Ty.mjs";
|
|
11
|
+
import { In as runProcessOperation, Kn as readProjectConfig, Pn as exitAfterFlush, Zn as remoteCliInvocation, p as stopOwnedMetroForReset, t as budgetGate } from "./budget-Cvwn54qV.mjs";
|
|
12
|
+
import { _ as stopTunnel, g as startTunnelSequence, h as resolveSupervisorTarget, v as terminateChild, x as withManagedTunnelLock, y as withManagedRemoteWorktreeLock } from "./workspaces-CpANmBlq.mjs";
|
|
13
|
+
import { a as workspaceLinkLine, o as workspaceLinks } from "./stim-desktop-D5OGpYZM.mjs";
|
|
14
14
|
import { t as spawnEntry } from "./spawn-entry-jw19m8Nl.mjs";
|
|
15
|
-
import { n as windowsLauncherArgs } from "./detached-entry-
|
|
16
|
-
import { appendFileSync, mkdirSync, openSync, readFileSync } from "node:fs";
|
|
15
|
+
import { n as windowsLauncherArgs } from "./detached-entry-BwNEtIrP.mjs";
|
|
16
|
+
import { appendFileSync, closeSync, mkdirSync, openSync, readFileSync } from "node:fs";
|
|
17
17
|
import { dirname } from "node:path";
|
|
18
18
|
import { ambientContext, ambientEnv } from "@stim-cli/core";
|
|
19
19
|
import { IDLE_STOP_KEY, queryLogs } from "@stim-cli/core/state";
|
|
@@ -42,8 +42,8 @@ function configString(config, platform, key) {
|
|
|
42
42
|
function literalString(text, key) {
|
|
43
43
|
return text?.match(new RegExp(`${key}\\s*:\\s*["']([^"']+)["']`))?.[1] ?? null;
|
|
44
44
|
}
|
|
45
|
-
async function idsFromConfig(projectRoot) {
|
|
46
|
-
const read = await readProjectConfig(projectRoot);
|
|
45
|
+
async function idsFromConfig(projectRoot, options) {
|
|
46
|
+
const read = await readProjectConfig(projectRoot, options);
|
|
47
47
|
if (!read.unavailable) return {
|
|
48
48
|
bundleId: configString(read.config, "ios", "bundleIdentifier"),
|
|
49
49
|
androidPackage: configString(read.config, "android", "package")
|
|
@@ -55,8 +55,8 @@ async function idsFromConfig(projectRoot) {
|
|
|
55
55
|
androidPackage: configString(appJson, "android", "package") ?? literalString(text, "package")
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
-
async function detectAppIds(projectRoot, iosDir = join$1(projectRoot, "ios"), androidModuleDir = defaultAndroidLayout(projectRoot).moduleDir) {
|
|
59
|
-
const ids = await idsFromConfig(projectRoot);
|
|
58
|
+
async function detectAppIds(projectRoot, iosDir = join$1(projectRoot, "ios"), androidModuleDir = defaultAndroidLayout(projectRoot).moduleDir, config = {}) {
|
|
59
|
+
const ids = await idsFromConfig(projectRoot, config);
|
|
60
60
|
const problems = [];
|
|
61
61
|
const accept = androidPackageFilter(problems);
|
|
62
62
|
return {
|
|
@@ -127,8 +127,8 @@ async function resolveStartupMetro(port, root) {
|
|
|
127
127
|
const signal = ambientContext()?.signal;
|
|
128
128
|
throwIfCancelled(signal, "Metro startup");
|
|
129
129
|
const result = await resolveProjectMetro(port, root, {
|
|
130
|
-
pidsOf: listeningPidsAsync,
|
|
131
|
-
cwdOf: async (pid) => (await processCwds([pid])).get(pid) ?? null,
|
|
130
|
+
pidsOf: (candidate) => listeningPidsAsync(candidate, process.platform, signal),
|
|
131
|
+
cwdOf: async (pid) => (await processCwds([pid], signal)).get(pid) ?? null,
|
|
132
132
|
probe: (candidate) => isMetroRunning(candidate, signal)
|
|
133
133
|
});
|
|
134
134
|
throwIfCancelled(signal, "Metro startup");
|
|
@@ -227,20 +227,33 @@ function providersOnPath(mode) {
|
|
|
227
227
|
const DEFAULT_START_DEPS = {
|
|
228
228
|
providers: providersOnPath,
|
|
229
229
|
startTunnelSequence,
|
|
230
|
-
isTunnelAlive: pidExists,
|
|
231
|
-
|
|
230
|
+
isTunnelAlive: (pid, processToken) => processToken === void 0 ? pidExists(pid) : (0, process_identity_exports.inspectProcessIdentity)({
|
|
231
|
+
pid,
|
|
232
|
+
processToken
|
|
233
|
+
}) === "same",
|
|
234
|
+
writeTunnelRecord: (root, patch) => updateWorkspaceStateAsync(root, (state) => ({
|
|
235
|
+
...state,
|
|
236
|
+
...patch
|
|
237
|
+
})),
|
|
232
238
|
stopTunnel,
|
|
233
|
-
writeSupervisorRecord:
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
writeSupervisorRecord: (root, patch) => updateWorkspaceStateAsync(root, (state) => ({
|
|
240
|
+
...state,
|
|
241
|
+
...patch
|
|
242
|
+
})),
|
|
243
|
+
terminateSupervisorChild: async (child) => {
|
|
244
|
+
if (child.pid && process.platform !== "win32") try {
|
|
245
|
+
signalProcessTree(child.pid, "SIGTERM", { group: true });
|
|
246
|
+
} catch {}
|
|
247
|
+
return terminateChild(child, {
|
|
248
|
+
alreadyExited: false,
|
|
249
|
+
timeoutMs: 1e3,
|
|
250
|
+
isAlive: pidExists,
|
|
251
|
+
waitForGroup: process.platform !== "win32"
|
|
252
|
+
});
|
|
253
|
+
},
|
|
241
254
|
withWorktreeLock: withManagedRemoteWorktreeLock,
|
|
242
255
|
withTunnelLock: withManagedTunnelLock,
|
|
243
|
-
clearTunnelRecord: clearManagedMetroTunnel,
|
|
256
|
+
clearTunnelRecord: (root, record) => withWorkspaceStateLockAsync(root, () => clearManagedMetroTunnel(root, record)),
|
|
244
257
|
budgetGate,
|
|
245
258
|
platform: process.platform
|
|
246
259
|
};
|
|
@@ -259,7 +272,7 @@ function registerStart(program, overrides = {}) {
|
|
|
259
272
|
else console.log(line);
|
|
260
273
|
};
|
|
261
274
|
const note = writeNote;
|
|
262
|
-
const fail = ({ code, message, remedy = null, lines = [], reclaimed = [] }) => {
|
|
275
|
+
const fail = ({ code, message, remedy = null, lines = [], reclaimed = [], exitCode = 1 }) => {
|
|
263
276
|
note(chalk.red(message));
|
|
264
277
|
for (const line of lines) note(chalk.dim(` ${line}`));
|
|
265
278
|
if (remedy) note(chalk.dim(remedy));
|
|
@@ -272,7 +285,7 @@ function registerStart(program, overrides = {}) {
|
|
|
272
285
|
}),
|
|
273
286
|
...reclaimed.length ? { reclaimed } : {}
|
|
274
287
|
}));
|
|
275
|
-
process.exit(
|
|
288
|
+
process.exit(exitCode);
|
|
276
289
|
};
|
|
277
290
|
const wait = parseWait(opts.wait);
|
|
278
291
|
if (wait.error) return fail({
|
|
@@ -310,20 +323,36 @@ function registerStart(program, overrides = {}) {
|
|
|
310
323
|
for (const key of unknownSettingKeys(settings)) note(chalk.yellow(`Warning: setting "${key}" is not read by Stim and will be ignored.`));
|
|
311
324
|
const cacheProviderError = cacheProviderSettingError(settings);
|
|
312
325
|
if (cacheProviderError) note(chalk.yellow(`Warning: ${cacheProviderError} No cache provider is used.`));
|
|
313
|
-
const result = await
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
326
|
+
const { value: result, interrupted } = await runProcessOperation(root, (context) => withDrainingCancellation(context, context.signal, async () => {
|
|
327
|
+
try {
|
|
328
|
+
return await startDevServer({
|
|
329
|
+
root,
|
|
330
|
+
settings,
|
|
331
|
+
settingsContext,
|
|
332
|
+
waitSeconds,
|
|
333
|
+
remote: Boolean(opts.remote),
|
|
334
|
+
resetCache: Boolean(opts.resetCache),
|
|
335
|
+
out,
|
|
336
|
+
note
|
|
337
|
+
}, d);
|
|
338
|
+
} catch (error) {
|
|
339
|
+
if (!(error instanceof Error) || !("code" in error) || error.code !== "STIM_CANCELLED") throw error;
|
|
340
|
+
return {
|
|
341
|
+
ok: false,
|
|
342
|
+
error: startError({
|
|
343
|
+
code: "STIM_CANCELLED",
|
|
344
|
+
message: error.message
|
|
345
|
+
}),
|
|
346
|
+
lines: [],
|
|
347
|
+
reclaimed: []
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}));
|
|
323
351
|
if (!result.ok) return fail({
|
|
324
352
|
...result.error,
|
|
325
353
|
lines: result.lines,
|
|
326
|
-
reclaimed: result.reclaimed
|
|
354
|
+
reclaimed: result.reclaimed,
|
|
355
|
+
exitCode: interrupted ?? 1
|
|
327
356
|
});
|
|
328
357
|
report({
|
|
329
358
|
json,
|
|
@@ -333,6 +362,7 @@ function registerStart(program, overrides = {}) {
|
|
|
333
362
|
reclaimed: result.reclaimed,
|
|
334
363
|
links: await workspaceLinks(root)
|
|
335
364
|
});
|
|
365
|
+
if (interrupted !== null) exitAfterFlush(interrupted);
|
|
336
366
|
});
|
|
337
367
|
}
|
|
338
368
|
var StartRefusal = class extends Error {
|
|
@@ -347,6 +377,8 @@ async function startDevServer({ root, settings, settingsContext, waitSeconds = D
|
|
|
347
377
|
...DEFAULT_START_DEPS,
|
|
348
378
|
...overrides
|
|
349
379
|
};
|
|
380
|
+
const operationSignal = ambientContext()?.signal;
|
|
381
|
+
throwIfCancelled(operationSignal, "Metro startup");
|
|
350
382
|
const waitTimer = stepTimer();
|
|
351
383
|
let reclaimed = [];
|
|
352
384
|
const fail = (refusal) => {
|
|
@@ -392,18 +424,63 @@ async function startDevServer({ root, settings, settingsContext, waitSeconds = D
|
|
|
392
424
|
});
|
|
393
425
|
if (tunnel) note(chalk.dim("note requesting an Expo tunnel for this workspace's dev server"));
|
|
394
426
|
const managedRemote = remote && !tunnel && !publicUrl && tunnelMode !== "off";
|
|
395
|
-
const runStart = async () => {
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
427
|
+
const runStart = async (claim, tunnelClaim) => {
|
|
428
|
+
const claims = [claim, tunnelClaim];
|
|
429
|
+
const appIds = await detectAppIds(root, resolveIosProjectDir(settings, root).dir, resolveAndroidLayout(settings, root, settingsContext.repoRoot ?? root).moduleDir, {
|
|
430
|
+
context: ambientContext(),
|
|
431
|
+
run: async (file, args, options) => {
|
|
432
|
+
for (const held of claims) (0, ownership_claim_exports.markClaimChildPending)(held);
|
|
433
|
+
let child;
|
|
434
|
+
let identity;
|
|
435
|
+
let output = "";
|
|
436
|
+
let failure;
|
|
437
|
+
try {
|
|
438
|
+
const invocation = remoteCliInvocation(file, args, "expo");
|
|
439
|
+
output = await getExecutor().runFileAsync(invocation.file, invocation.args, {
|
|
440
|
+
...options,
|
|
441
|
+
cwd: root,
|
|
442
|
+
containProcessTree: true,
|
|
443
|
+
signal: operationSignal ?? new AbortController().signal,
|
|
444
|
+
onSpawn: (spawned) => {
|
|
445
|
+
child = spawned;
|
|
446
|
+
if (!spawned.pid) return;
|
|
447
|
+
const processToken = (0, process_identity_exports.captureProcessToken)(spawned.pid);
|
|
448
|
+
if (!processToken) throw new Error("Cannot record the Expo config process identity.");
|
|
449
|
+
identity = {
|
|
450
|
+
pid: spawned.pid,
|
|
451
|
+
processToken
|
|
452
|
+
};
|
|
453
|
+
for (const held of claims) (0, ownership_claim_exports.setClaimChild)(held, identity);
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
} catch (error) {
|
|
457
|
+
failure = { error };
|
|
458
|
+
}
|
|
459
|
+
const pid = child?.pid;
|
|
460
|
+
if (pid) {
|
|
461
|
+
const alive = () => d.platform === "win32" ? !identity || (0, process_identity_exports.inspectProcessIdentity)(identity) !== "gone" : (0, ownership_claim_exports.processGroupAlive)(pid);
|
|
462
|
+
const deadline = Date.now() + 1e3;
|
|
463
|
+
while (alive() && Date.now() < deadline) await cancellableSleep(25);
|
|
464
|
+
if (alive()) throw new WorkspaceProcessCleanupError(claims, `Could not confirm cleanup of Expo config process ${pid}.`, "Inspect and stop the Expo config process and its helpers before retrying.");
|
|
465
|
+
}
|
|
466
|
+
for (const held of claims) (0, ownership_claim_exports.clearClaimChild)(held);
|
|
467
|
+
if (failure) throw failure.error;
|
|
468
|
+
return output;
|
|
469
|
+
}
|
|
401
470
|
});
|
|
471
|
+
await withConfigLockAsync$1(() => {
|
|
472
|
+
upsertProject(root, {
|
|
473
|
+
bundleId: appIds.bundleId ?? void 0,
|
|
474
|
+
androidPackage: appIds.androidPackage ?? void 0,
|
|
475
|
+
isExpo
|
|
476
|
+
});
|
|
477
|
+
}, operationSignal);
|
|
402
478
|
const gateBudget = async () => {
|
|
403
479
|
const budget = await d.budgetGate({
|
|
404
480
|
root,
|
|
405
481
|
note,
|
|
406
|
-
env: ambientEnv()
|
|
482
|
+
env: ambientEnv(),
|
|
483
|
+
signal: operationSignal
|
|
407
484
|
});
|
|
408
485
|
reclaimed = budget.reclaimed;
|
|
409
486
|
if (budget.refusal) return fail(budget.refusal);
|
|
@@ -416,7 +493,7 @@ async function startDevServer({ root, settings, settingsContext, waitSeconds = D
|
|
|
416
493
|
if (typeof pinCheck !== "number") return fail(pinCheck);
|
|
417
494
|
}
|
|
418
495
|
try {
|
|
419
|
-
await stopOwnedMetroForReset(root);
|
|
496
|
+
await stopOwnedMetroForReset(root, operationSignal);
|
|
420
497
|
} catch (error) {
|
|
421
498
|
const failure = error;
|
|
422
499
|
return fail({
|
|
@@ -449,21 +526,44 @@ async function startDevServer({ root, settings, settingsContext, waitSeconds = D
|
|
|
449
526
|
});
|
|
450
527
|
if (!resetCache && !resolution.metro && !supervisor) await gateBudget();
|
|
451
528
|
let managedTunnel = null;
|
|
452
|
-
|
|
529
|
+
const spawned = { child: null };
|
|
453
530
|
let spawnedTs = null;
|
|
454
531
|
let childExit = null;
|
|
532
|
+
let spawnedIdentity = null;
|
|
533
|
+
let unknownWindowsSpawn = false;
|
|
534
|
+
let windowsLauncher = null;
|
|
535
|
+
let tunnelPending = null;
|
|
536
|
+
const handedOffSupervisor = () => {
|
|
537
|
+
if (!spawnedIdentity) return null;
|
|
538
|
+
const target = resolveSupervisorTarget({
|
|
539
|
+
state: readWorkspaceState$1(root)?.supervisor,
|
|
540
|
+
record: getProject(root)?.supervisor,
|
|
541
|
+
reservedPort: port
|
|
542
|
+
});
|
|
543
|
+
return target.status === "ours" && typeof target.pid === "number" && target.pid === spawnedIdentity.pid && target.processToken === spawnedIdentity.processToken && target.port === port ? {
|
|
544
|
+
pid: target.pid,
|
|
545
|
+
port,
|
|
546
|
+
mode: target.mode ?? null,
|
|
547
|
+
startedAt: target.startedAt ?? null
|
|
548
|
+
} : null;
|
|
549
|
+
};
|
|
455
550
|
const spawnDirect = (supervisorArgs, childEnv) => {
|
|
456
551
|
const fd = openSync(logFile, "a");
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
552
|
+
let child;
|
|
553
|
+
try {
|
|
554
|
+
child = getExecutor().spawn(process.execPath, supervisorArgs, {
|
|
555
|
+
cwd: root,
|
|
556
|
+
detached: true,
|
|
557
|
+
stdio: [
|
|
558
|
+
"ignore",
|
|
559
|
+
fd,
|
|
560
|
+
fd
|
|
561
|
+
],
|
|
562
|
+
env: childEnv
|
|
563
|
+
});
|
|
564
|
+
} finally {
|
|
565
|
+
closeSync(fd);
|
|
566
|
+
}
|
|
467
567
|
child.unref?.();
|
|
468
568
|
child.on?.("exit", (code, signal) => {
|
|
469
569
|
childExit = {
|
|
@@ -480,8 +580,8 @@ async function startDevServer({ root, settings, settingsContext, waitSeconds = D
|
|
|
480
580
|
});
|
|
481
581
|
return child;
|
|
482
582
|
};
|
|
483
|
-
const recordedSupervisor = async (since) => {
|
|
484
|
-
const deadline = Date.now() +
|
|
583
|
+
const recordedSupervisor = async (since, waitMs = RECORDED_SUPERVISOR_WAIT_MS) => {
|
|
584
|
+
const deadline = Date.now() + waitMs;
|
|
485
585
|
while (Date.now() < deadline) {
|
|
486
586
|
const record = readWorkspaceState$1(root)?.supervisor;
|
|
487
587
|
if (record?.pid && record.port === port && Date.parse(String(record.startedAt)) >= since && pidExists(record.pid)) return {
|
|
@@ -514,21 +614,48 @@ async function startDevServer({ root, settings, settingsContext, waitSeconds = D
|
|
|
514
614
|
},
|
|
515
615
|
windowsHide: true
|
|
516
616
|
});
|
|
617
|
+
const launch = {
|
|
618
|
+
child: shell,
|
|
619
|
+
since,
|
|
620
|
+
closed: false
|
|
621
|
+
};
|
|
622
|
+
windowsLauncher = launch;
|
|
623
|
+
unknownWindowsSpawn = shell.pid !== void 0;
|
|
517
624
|
const stderr = [];
|
|
518
625
|
shell.stderr?.on("data", (chunk) => stderr.push(String(chunk)));
|
|
519
|
-
const
|
|
520
|
-
shell.
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
626
|
+
const closed = new Promise((resolve) => {
|
|
627
|
+
shell.once("close", (code, signal) => {
|
|
628
|
+
launch.closed = true;
|
|
629
|
+
resolve({
|
|
630
|
+
code,
|
|
631
|
+
signal
|
|
632
|
+
});
|
|
633
|
+
});
|
|
634
|
+
shell.once("error", (error) => resolve({
|
|
525
635
|
code: null,
|
|
526
636
|
signal: null,
|
|
527
637
|
error
|
|
528
638
|
}));
|
|
529
639
|
});
|
|
640
|
+
const waiting = new AbortController();
|
|
641
|
+
const signal = operationSignal ? AbortSignal.any([operationSignal, waiting.signal]) : waiting.signal;
|
|
642
|
+
let exit;
|
|
643
|
+
try {
|
|
644
|
+
exit = await Promise.race([closed, cancellableSleep(Math.min(RECORDED_SUPERVISOR_WAIT_MS, waitSeconds * 1e3), {
|
|
645
|
+
signal,
|
|
646
|
+
what: "Windows supervisor launcher"
|
|
647
|
+
}).then(() => fail({
|
|
648
|
+
code: "STIM_METRO_TIMEOUT",
|
|
649
|
+
message: "The Windows supervisor launcher did not finish before the startup deadline.",
|
|
650
|
+
remedy: "Inspect the launcher and supervisor before retrying."
|
|
651
|
+
}))]);
|
|
652
|
+
} finally {
|
|
653
|
+
waiting.abort();
|
|
654
|
+
}
|
|
530
655
|
const launcherFailed = exit.code !== 0 || exit.error;
|
|
656
|
+
unknownWindowsSpawn = !launcherFailed;
|
|
531
657
|
const recorded = launcherFailed ? null : await recordedSupervisor(since);
|
|
658
|
+
if (recorded) unknownWindowsSpawn = false;
|
|
532
659
|
if (recorded === null) {
|
|
533
660
|
const reason = launcherFailed ? `the supervisor launcher exited (${exit.error ? exit.error.message : `code ${exit.code}`})` : `the supervisor did not record itself within ${RECORDED_SUPERVISOR_WAIT_MS / 1e3}s`;
|
|
534
661
|
appendFileSync(logFile, `Stim start: ${reason}.\n${stderr.join("")}`);
|
|
@@ -538,6 +665,7 @@ async function startDevServer({ root, settings, settingsContext, waitSeconds = D
|
|
|
538
665
|
...exit.error ? { error: exit.error } : {}
|
|
539
666
|
};
|
|
540
667
|
}
|
|
668
|
+
windowsLauncher = null;
|
|
541
669
|
return recordedSupervisorProcess(recorded);
|
|
542
670
|
};
|
|
543
671
|
const spawnSupervisor = async (origin) => {
|
|
@@ -564,404 +692,497 @@ async function startDevServer({ root, settings, settingsContext, waitSeconds = D
|
|
|
564
692
|
} : {}
|
|
565
693
|
};
|
|
566
694
|
childEnv[CACHE_PROVIDER_ENV] = cacheProviderEnv(cacheProvider);
|
|
695
|
+
for (const held of claims) (0, ownership_claim_exports.markClaimChildPending)(held);
|
|
567
696
|
const child = d.platform === "win32" ? await spawnThroughWindowsShell(supervisorArgs, childEnv) : spawnDirect(supervisorArgs, childEnv);
|
|
697
|
+
spawned.child = child;
|
|
698
|
+
if (child.pid) {
|
|
699
|
+
const processToken = child.recordedIdentity?.processToken ?? (0, process_identity_exports.captureProcessToken)(child.pid);
|
|
700
|
+
if (processToken) {
|
|
701
|
+
spawnedIdentity = {
|
|
702
|
+
pid: child.pid,
|
|
703
|
+
processToken
|
|
704
|
+
};
|
|
705
|
+
for (const held of claims) (0, ownership_claim_exports.setClaimChild)(held, spawnedIdentity);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
568
708
|
out(chalk.dim(phaseLine("metro", `starting on port ${port} (${serverMode}, supervisor pid ${child.pid})`)));
|
|
569
|
-
spawnedChild = child;
|
|
570
709
|
return child;
|
|
571
710
|
};
|
|
572
711
|
const waitForSupervisorHandoff = async (child) => {
|
|
573
712
|
const deadline = Date.now() + 5e3;
|
|
574
713
|
while (Date.now() < deadline) {
|
|
575
|
-
const found =
|
|
576
|
-
|
|
577
|
-
project: getProject(root),
|
|
578
|
-
port
|
|
579
|
-
});
|
|
580
|
-
if (found?.pid === child.pid) return found;
|
|
714
|
+
const found = handedOffSupervisor();
|
|
715
|
+
if (found) return found;
|
|
581
716
|
if (childExit !== null || (child.pid ? !pidExists(child.pid) : true)) return null;
|
|
582
717
|
await cancellableSleep(25);
|
|
583
718
|
}
|
|
584
719
|
return null;
|
|
585
720
|
};
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
721
|
+
const reconcileSpawnedSupervisor = async () => {
|
|
722
|
+
try {
|
|
723
|
+
const launch = windowsLauncher;
|
|
724
|
+
if (launch) {
|
|
725
|
+
if (!await terminateChild(launch.child, {
|
|
726
|
+
alreadyExited: launch.closed || launch.child.exitCode !== null || launch.child.signalCode !== null,
|
|
727
|
+
timeoutMs: 1e3,
|
|
728
|
+
isAlive: pidExists
|
|
729
|
+
})) throw new WorkspaceProcessCleanupError(claims, `Could not confirm cleanup of Windows supervisor launcher pid ${launch.child.pid}.`, "Inspect and stop the launcher and its supervisor with their original tooling before retrying.");
|
|
730
|
+
if (unknownWindowsSpawn) {
|
|
731
|
+
const recorded = await recordedSupervisor(launch.since, 1e3);
|
|
732
|
+
if (recorded) {
|
|
733
|
+
spawned.child = recordedSupervisorProcess(recorded);
|
|
734
|
+
spawnedIdentity = recorded;
|
|
735
|
+
for (const held of claims) (0, ownership_claim_exports.setClaimChild)(held, recorded);
|
|
736
|
+
unknownWindowsSpawn = false;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
windowsLauncher = null;
|
|
740
|
+
}
|
|
741
|
+
const child = spawned.child;
|
|
742
|
+
if (tunnelPending) throw tunnelPending;
|
|
743
|
+
if (child?.pid && !handedOffSupervisor()) {
|
|
744
|
+
const hasPublishedSupervisor = () => {
|
|
745
|
+
const identity = spawnedIdentity;
|
|
746
|
+
return identity !== null && [readWorkspaceState$1(root)?.supervisor, getProject(root)?.supervisor].some((record) => record?.pid === identity.pid && record?.processToken === identity.processToken);
|
|
747
|
+
};
|
|
748
|
+
if (hasPublishedSupervisor()) {
|
|
749
|
+
for (const held of claims) (0, ownership_claim_exports.markClaimChildPending)(held);
|
|
750
|
+
throw new WorkspaceProcessCleanupError(claims, `Supervisor pid ${child.pid} published ownership that cannot be reconciled with the workspace registry.`, "Inspect the supervisor and its dev server, then stop them with their original tooling before retrying.");
|
|
751
|
+
}
|
|
752
|
+
const same = spawnedIdentity && (0, process_identity_exports.inspectProcessIdentity)(spawnedIdentity) === "same";
|
|
753
|
+
const supervisorCleanup = childExit !== null && !(0, ownership_claim_exports.processGroupAlive)(child.pid) || Boolean(same && supervisorSignalable(child) && await d.terminateSupervisorChild(child));
|
|
754
|
+
const publicationRacedCleanup = hasPublishedSupervisor();
|
|
755
|
+
if (!supervisorCleanup || d.platform !== "win32" && (0, ownership_claim_exports.processGroupAlive)(child.pid) || publicationRacedCleanup) {
|
|
756
|
+
if (publicationRacedCleanup) for (const held of claims) (0, ownership_claim_exports.markClaimChildPending)(held);
|
|
757
|
+
throw new WorkspaceProcessCleanupError(claims, `Could not confirm cleanup of supervisor pid ${child.pid}.`, `Stop supervisor pid ${child.pid} with its original tooling before retrying.`);
|
|
758
|
+
}
|
|
759
|
+
for (const held of claims) (0, ownership_claim_exports.clearClaimChild)(held);
|
|
760
|
+
} else if (unknownWindowsSpawn) throw new WorkspaceProcessCleanupError(claims, "The Windows launcher started a supervisor but its identity was not recorded.", "Inspect and stop it with its original tooling before retrying.");
|
|
761
|
+
} catch (cause) {
|
|
762
|
+
if (cause instanceof WorkspaceProcessCleanupError) throw cause;
|
|
763
|
+
throw new WorkspaceProcessCleanupError(claims, `Could not reconcile supervisor ownership: ${cause.message}`, "Inspect and stop the supervisor with its original tooling before retrying.");
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
try {
|
|
767
|
+
if (remote && !tunnel && !publicUrl && tunnelMode !== "off") {
|
|
768
|
+
const available = d.providers(tunnelMode);
|
|
769
|
+
const plan = planMetroReach({
|
|
770
|
+
mode: tunnelMode,
|
|
771
|
+
metroPort: port,
|
|
772
|
+
publicUrl,
|
|
773
|
+
isExpo: expoServer,
|
|
774
|
+
available
|
|
775
|
+
});
|
|
776
|
+
if ("failed" in plan) return fail({
|
|
777
|
+
code: "STIM_REMOTE_METRO_UNREACHABLE",
|
|
778
|
+
message: plan.failed,
|
|
779
|
+
remedy: plan.remedy
|
|
780
|
+
});
|
|
781
|
+
if ("start" in plan) {
|
|
782
|
+
const candidates = tunnelMode === "auto" ? available.filter((provider) => provider !== "tailscale") : [plan.start];
|
|
783
|
+
const expectedStableUrl = ngrokUrlSetting(settings);
|
|
784
|
+
let acquisition;
|
|
785
|
+
try {
|
|
786
|
+
acquisition = await (async () => {
|
|
787
|
+
let tracking;
|
|
788
|
+
let startedCleanup = null;
|
|
789
|
+
const recorded = readMetroTunnel(root);
|
|
790
|
+
if (recorded?.kind === "managed" && d.isTunnelAlive(recorded.pid)) {
|
|
791
|
+
if (!recorded.processToken) return { failed: {
|
|
792
|
+
code: "STIM_REMOTE_START_REQUIRED",
|
|
793
|
+
message: "The recorded managed Metro tunnel has no process identity token.",
|
|
794
|
+
remedy: "Inspect the process, stop it with the provider tooling, remove the stale metroTunnel state, and retry."
|
|
795
|
+
} };
|
|
796
|
+
if (!d.isTunnelAlive(recorded.pid, recorded.processToken)) return { failed: {
|
|
797
|
+
code: "STIM_REMOTE_START_REQUIRED",
|
|
798
|
+
message: `The recorded ${recorded.provider} tunnel pid ${recorded.pid} no longer has its recorded process identity.`,
|
|
799
|
+
remedy: "Run `stim stop`, resolve any ownership refusal, then retry `stim start --remote`."
|
|
800
|
+
} };
|
|
801
|
+
if (!(recorded.port === port && candidates.includes(recorded.provider) && (!expectedStableUrl || normalizeManagedTunnelUrl(recorded.url) === expectedStableUrl))) return { failed: {
|
|
802
|
+
code: "STIM_REMOTE_START_REQUIRED",
|
|
803
|
+
message: `A different managed Metro tunnel is already running for this workspace.`,
|
|
804
|
+
remedy: "Run `stim stop`, then `stim start --remote`."
|
|
805
|
+
} };
|
|
806
|
+
tracking = {
|
|
807
|
+
record: {
|
|
808
|
+
provider: recorded.provider,
|
|
809
|
+
pid: recorded.pid,
|
|
810
|
+
url: normalizeManagedTunnelUrl(recorded.url),
|
|
811
|
+
port: recorded.port,
|
|
812
|
+
startedAt: recorded.startedAt,
|
|
813
|
+
processToken: recorded.processToken,
|
|
814
|
+
logFile: recorded.logFile
|
|
815
|
+
},
|
|
816
|
+
startedHere: false
|
|
817
|
+
};
|
|
818
|
+
} else {
|
|
819
|
+
const currentResolution = await resolveStartupMetro(port, root);
|
|
820
|
+
const currentSupervisor = liveSupervisor({
|
|
821
|
+
state: readWorkspaceState$1(root),
|
|
822
|
+
project: getProject(root),
|
|
823
|
+
port
|
|
824
|
+
});
|
|
825
|
+
if (currentResolution.metro || currentSupervisor) return { failed: {
|
|
826
|
+
code: "STIM_REMOTE_START_REQUIRED",
|
|
827
|
+
message: `The dev server on port ${port} is local-only and cannot gain a managed tunnel while it is running.`,
|
|
828
|
+
remedy: "Run `stim stop`, then `stim start --remote`."
|
|
829
|
+
} };
|
|
830
|
+
throwIfCancelled(operationSignal, "Metro tunnel startup");
|
|
831
|
+
for (const held of claims) (0, ownership_claim_exports.markClaimChildPending)(held);
|
|
832
|
+
tunnelPending = new WorkspaceProcessCleanupError(claims, "A new managed Metro tunnel could not be recorded or confirmed stopped.", "Inspect and stop the tunnel with its provider tooling before retrying.");
|
|
833
|
+
const started = await d.startTunnelSequence({
|
|
834
|
+
claims,
|
|
835
|
+
signal: operationSignal,
|
|
836
|
+
providers: candidates,
|
|
837
|
+
port,
|
|
838
|
+
ngrokUrl: expectedStableUrl,
|
|
839
|
+
requireReachable: false
|
|
840
|
+
});
|
|
841
|
+
if ("failed" in started) {
|
|
842
|
+
if (started.cleanupFailed) {
|
|
843
|
+
tunnelPending = new WorkspaceProcessCleanupError(claims, started.reason, "Stop the tunnel with its provider tooling before retrying.");
|
|
844
|
+
throw tunnelPending;
|
|
845
|
+
}
|
|
846
|
+
for (const held of claims) (0, ownership_claim_exports.clearClaimChild)(held);
|
|
847
|
+
tunnelPending = null;
|
|
848
|
+
throwIfCancelled(operationSignal, "Metro tunnel startup");
|
|
849
|
+
return { failed: {
|
|
850
|
+
code: "STIM_REMOTE_METRO_UNREACHABLE",
|
|
851
|
+
message: `Could not start a managed Metro tunnel for port ${port}.`,
|
|
852
|
+
remedy: started.reason
|
|
853
|
+
} };
|
|
854
|
+
}
|
|
855
|
+
for (const held of claims) (0, ownership_claim_exports.setClaimChild)(held, {
|
|
856
|
+
pid: started.pid,
|
|
857
|
+
processToken: started.processToken
|
|
858
|
+
});
|
|
859
|
+
const record = {
|
|
860
|
+
provider: started.provider,
|
|
861
|
+
pid: started.pid,
|
|
862
|
+
url: normalizeManagedTunnelUrl(started.url),
|
|
863
|
+
port,
|
|
864
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
865
|
+
processToken: started.processToken,
|
|
866
|
+
logFile: started.logFile
|
|
867
|
+
};
|
|
868
|
+
startedCleanup = started.cleanup;
|
|
869
|
+
try {
|
|
870
|
+
await d.writeTunnelRecord(root, { metroTunnel: {
|
|
871
|
+
kind: "managed",
|
|
872
|
+
...record
|
|
873
|
+
} });
|
|
874
|
+
const stored = readMetroTunnel(root);
|
|
875
|
+
if (stored?.kind !== "managed" || stored.pid !== record.pid || stored.processToken !== record.processToken || stored.port !== record.port || stored.provider !== record.provider || stored.url !== record.url) throw new Error("The workspace did not retain this tunnel ownership record.");
|
|
876
|
+
for (const held of claims) (0, ownership_claim_exports.clearClaimChild)(held);
|
|
877
|
+
tunnelPending = null;
|
|
878
|
+
} catch (err) {
|
|
879
|
+
const recordError = err instanceof Error ? err.message : String(err);
|
|
880
|
+
const stopped = await started.cleanup();
|
|
881
|
+
if (stopped.status === "stopped") {
|
|
882
|
+
for (const held of claims) (0, ownership_claim_exports.clearClaimChild)(held);
|
|
883
|
+
tunnelPending = null;
|
|
884
|
+
} else tunnelPending = new WorkspaceProcessCleanupError(claims, `Could not record the managed Metro tunnel: ${recordError}`, `Cleanup failed. Unmanaged pid ${record.pid} may still be running: ${stopped.reason ?? "unknown error"}`);
|
|
885
|
+
return { failed: {
|
|
886
|
+
code: "STIM_REMOTE_METRO_UNREACHABLE",
|
|
887
|
+
message: `Could not record the managed Metro tunnel: ${recordError}`,
|
|
888
|
+
remedy: stopped.status === "failed" ? `Cleanup failed. Unmanaged pid ${record.pid} may still be running: ${stopped.reason ?? "unknown error"}` : "The tunnel process was stopped. Fix the workspace write error, then retry `stim start --remote`."
|
|
889
|
+
} };
|
|
890
|
+
}
|
|
891
|
+
tracking = {
|
|
892
|
+
record,
|
|
893
|
+
startedHere: true
|
|
894
|
+
};
|
|
895
|
+
}
|
|
633
896
|
const currentResolution = await resolveStartupMetro(port, root);
|
|
634
897
|
const currentSupervisor = liveSupervisor({
|
|
635
898
|
state: readWorkspaceState$1(root),
|
|
636
899
|
project: getProject(root),
|
|
637
900
|
port
|
|
638
901
|
});
|
|
639
|
-
if (currentResolution.metro || currentSupervisor)
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
} });
|
|
670
|
-
} catch (err) {
|
|
671
|
-
const stopped = await started.cleanup();
|
|
902
|
+
if (currentResolution.metro || currentSupervisor) {
|
|
903
|
+
if (tracking.startedHere && startedCleanup) {
|
|
904
|
+
const stopped = await startedCleanup();
|
|
905
|
+
if (stopped.status === "failed") return { failed: {
|
|
906
|
+
code: "STIM_REMOTE_START_REQUIRED",
|
|
907
|
+
message: `The dev server on port ${port} started before the managed tunnel was ready.`,
|
|
908
|
+
remedy: `Tunnel cleanup failed for pid ${tracking.record.pid}: ${stopped.reason ?? "unknown error"}. The tunnel record remains available to \`stim stop\`.`
|
|
909
|
+
} };
|
|
910
|
+
await d.clearTunnelRecord(root, tracking.record);
|
|
911
|
+
return { failed: {
|
|
912
|
+
code: "STIM_REMOTE_START_REQUIRED",
|
|
913
|
+
message: `The dev server on port ${port} started before the managed tunnel was ready.`,
|
|
914
|
+
remedy: "Run `stim stop`, then `stim start --remote`."
|
|
915
|
+
} };
|
|
916
|
+
}
|
|
917
|
+
return {
|
|
918
|
+
origin: tracking.record.url,
|
|
919
|
+
tunnel: tracking
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
if (!d.isTunnelAlive(tracking.record.pid, tracking.record.processToken)) {
|
|
923
|
+
if (tracking.startedHere && startedCleanup) {
|
|
924
|
+
const stopped = await startedCleanup();
|
|
925
|
+
if (stopped.status === "failed") return { failed: {
|
|
926
|
+
code: "STIM_REMOTE_METRO_UNREACHABLE",
|
|
927
|
+
message: `The managed ${tracking.record.provider} tunnel is no longer verified running before supervisor startup.`,
|
|
928
|
+
remedy: `Cleanup failed. Unmanaged pid ${tracking.record.pid} may still be running: ${stopped.reason ?? "unknown error"}. The tunnel record remains available to \`stim stop\`.`
|
|
929
|
+
} };
|
|
930
|
+
}
|
|
931
|
+
await d.clearTunnelRecord(root, tracking.record);
|
|
672
932
|
return { failed: {
|
|
673
933
|
code: "STIM_REMOTE_METRO_UNREACHABLE",
|
|
674
|
-
message: `
|
|
675
|
-
remedy:
|
|
934
|
+
message: `The managed ${tracking.record.provider} tunnel is no longer verified running before supervisor startup.`,
|
|
935
|
+
remedy: "Retry `stim start --remote`."
|
|
676
936
|
} };
|
|
677
937
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
const currentResolution = await resolveStartupMetro(port, root);
|
|
684
|
-
const currentSupervisor = liveSupervisor({
|
|
685
|
-
state: readWorkspaceState$1(root),
|
|
686
|
-
project: getProject(root),
|
|
687
|
-
port
|
|
688
|
-
});
|
|
689
|
-
if (currentResolution.metro || currentSupervisor) {
|
|
690
|
-
if (tracking.startedHere && startedCleanup) {
|
|
691
|
-
const stopped = await startedCleanup();
|
|
692
|
-
if (stopped.status === "failed") return { failed: {
|
|
693
|
-
code: "STIM_REMOTE_START_REQUIRED",
|
|
694
|
-
message: `The dev server on port ${port} started before the managed tunnel was ready.`,
|
|
695
|
-
remedy: `Tunnel cleanup failed for pid ${tracking.record.pid}: ${stopped.reason ?? "unknown error"}. The tunnel record remains available to \`stim stop\`.`
|
|
696
|
-
} };
|
|
697
|
-
d.clearTunnelRecord(root, tracking.record);
|
|
938
|
+
let child;
|
|
939
|
+
try {
|
|
940
|
+
child = await spawnSupervisor(tracking.record.url);
|
|
941
|
+
} catch (err) {
|
|
698
942
|
return { failed: {
|
|
699
|
-
code: "
|
|
700
|
-
message: `
|
|
701
|
-
remedy: "
|
|
943
|
+
code: "STIM_SUPERVISOR_EXITED",
|
|
944
|
+
message: `Could not spawn the dev server supervisor: ${err?.message || err}`,
|
|
945
|
+
remedy: "Fix the spawn error, then retry `stim start --remote`."
|
|
702
946
|
} };
|
|
703
947
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
948
|
+
const handoffRecord = {
|
|
949
|
+
pid: child.pid,
|
|
950
|
+
processToken: child.recordedIdentity ? child.recordedIdentity.processToken ?? null : child.pid ? (0, process_identity_exports.captureProcessToken)(child.pid) : null,
|
|
951
|
+
port,
|
|
952
|
+
mode: serverMode,
|
|
953
|
+
startedAt: new Date(spawnedTs ?? Date.now()).toISOString()
|
|
707
954
|
};
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
if (stopped.status === "failed") return { failed: {
|
|
713
|
-
code: "STIM_REMOTE_METRO_UNREACHABLE",
|
|
714
|
-
message: `The managed ${tracking.record.provider} tunnel exited before the supervisor started.`,
|
|
715
|
-
remedy: `Cleanup failed. Unmanaged pid ${tracking.record.pid} may still be running: ${stopped.reason ?? "unknown error"}. The tunnel record remains available to \`stim stop\`.`
|
|
716
|
-
} };
|
|
717
|
-
}
|
|
718
|
-
d.clearTunnelRecord(root, tracking.record);
|
|
719
|
-
return { failed: {
|
|
720
|
-
code: "STIM_REMOTE_METRO_UNREACHABLE",
|
|
721
|
-
message: `The managed ${tracking.record.provider} tunnel exited before the supervisor started.`,
|
|
722
|
-
remedy: "Retry `stim start --remote`."
|
|
723
|
-
} };
|
|
724
|
-
}
|
|
725
|
-
let child;
|
|
726
|
-
try {
|
|
727
|
-
child = await spawnSupervisor(tracking.record.url);
|
|
728
|
-
} catch (err) {
|
|
729
|
-
return { failed: {
|
|
730
|
-
code: "STIM_SUPERVISOR_EXITED",
|
|
731
|
-
message: `Could not spawn the dev server supervisor: ${err?.message || err}`,
|
|
732
|
-
remedy: "Fix the spawn error, then retry `stim start --remote`."
|
|
733
|
-
} };
|
|
734
|
-
}
|
|
735
|
-
const handoffRecord = {
|
|
736
|
-
pid: child.pid,
|
|
737
|
-
processToken: child.recordedIdentity ? child.recordedIdentity.processToken ?? null : child.pid ? (0, process_identity_exports.captureProcessToken)(child.pid) : null,
|
|
738
|
-
port,
|
|
739
|
-
mode: serverMode,
|
|
740
|
-
startedAt: new Date(spawnedTs ?? Date.now()).toISOString()
|
|
741
|
-
};
|
|
742
|
-
try {
|
|
743
|
-
d.writeSupervisorRecord(root, { supervisor: handoffRecord });
|
|
744
|
-
} catch {
|
|
745
|
-
if (!await waitForSupervisorHandoff(child)) {
|
|
746
|
-
const stopped = supervisorSignalable(child) && await d.terminateSupervisorChild(child);
|
|
747
|
-
return { failed: {
|
|
955
|
+
try {
|
|
956
|
+
await d.writeSupervisorRecord(root, { supervisor: handoffRecord });
|
|
957
|
+
} catch {
|
|
958
|
+
if (!await waitForSupervisorHandoff(child)) return { failed: {
|
|
748
959
|
code: "STIM_SUPERVISOR_EXITED",
|
|
749
960
|
message: `Could not record supervisor pid ${child.pid ?? "unknown"} before releasing the managed start lock.`,
|
|
750
|
-
remedy:
|
|
961
|
+
remedy: "The supervisor process was stopped. Retry `stim start --remote`."
|
|
751
962
|
} };
|
|
752
963
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
};
|
|
964
|
+
return {
|
|
965
|
+
origin: tracking.record.url,
|
|
966
|
+
tunnel: tracking
|
|
967
|
+
};
|
|
968
|
+
})();
|
|
969
|
+
} catch (err) {
|
|
970
|
+
if (err instanceof WorkspaceProcessCleanupError) throw err;
|
|
971
|
+
throwIfCancelled(operationSignal, "Metro startup");
|
|
972
|
+
if (err instanceof Error && "code" in err && err.code === "STIM_CANCELLED") throw err;
|
|
973
|
+
return fail({
|
|
974
|
+
code: "STIM_REMOTE_METRO_UNREACHABLE",
|
|
975
|
+
message: `Could not start the managed Metro tunnel: ${err?.message || err}`,
|
|
976
|
+
remedy: "Retry `stim start --remote` after the other start command finishes."
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
if ("failed" in acquisition) return fail(acquisition.failed);
|
|
980
|
+
publicOrigin = acquisition.origin;
|
|
981
|
+
managedTunnel = acquisition.tunnel;
|
|
982
|
+
if (managedTunnel.record.provider === "tailscale") note(phaseLine("lan", `tailnet-only Metro: ${publicOrigin}`));
|
|
983
|
+
resolution = await resolveStartupMetro(port, root);
|
|
984
|
+
supervisor = liveSupervisor({
|
|
985
|
+
state: readWorkspaceState$1(root),
|
|
986
|
+
project: getProject(root),
|
|
987
|
+
port
|
|
758
988
|
});
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const managedTunnelExited = () => managedTunnel !== null && !d.isTunnelAlive(managedTunnel.record.pid, managedTunnel.record.processToken);
|
|
992
|
+
const failExitedManagedTunnel = async () => {
|
|
993
|
+
const tracked = managedTunnel;
|
|
994
|
+
const record = tracked.record;
|
|
995
|
+
const stopped = tracked.startedHere ? await d.stopTunnel(record) : { status: "missing" };
|
|
996
|
+
if (stopped.status !== "failed") await d.clearTunnelRecord(root, record);
|
|
997
|
+
return fail({
|
|
998
|
+
code: "STIM_REMOTE_METRO_UNREACHABLE",
|
|
999
|
+
message: `The managed ${record.provider} tunnel is no longer verified running before the dev server became ready.`,
|
|
1000
|
+
remedy: stopped.status === "failed" ? `The tunnel cleanup also failed: ${stopped.reason ?? "unknown error"}. Run \`stim stop\`, then retry.` : "Run `stim stop`, then `stim start --remote`."
|
|
1001
|
+
});
|
|
1002
|
+
};
|
|
1003
|
+
const requireExpoTunnel = () => {
|
|
1004
|
+
if (tunnel && (!supervisor || readMetroTunnel(root)?.kind !== "expo")) fail({
|
|
1005
|
+
code: "STIM_REMOTE_START_REQUIRED",
|
|
1006
|
+
message: `The Expo dev server on port ${port} is local-only and cannot gain a tunnel while it is running.`,
|
|
1007
|
+
remedy: "Run `stim stop`, then `stim start --remote`."
|
|
1008
|
+
});
|
|
1009
|
+
};
|
|
1010
|
+
if (!spawned.child && resolution.metro) {
|
|
1011
|
+
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
1012
|
+
if (tunnel && supervisor) {
|
|
1013
|
+
if (!await waitForExpoTunnel({
|
|
1014
|
+
root,
|
|
1015
|
+
seconds: waitSeconds,
|
|
1016
|
+
aborted: () => !liveSupervisor({
|
|
1017
|
+
state: readWorkspaceState$1(root),
|
|
1018
|
+
project: getProject(root),
|
|
1019
|
+
port
|
|
1020
|
+
})
|
|
1021
|
+
})) {
|
|
1022
|
+
const stillLive = liveSupervisor({
|
|
1023
|
+
state: readWorkspaceState$1(root),
|
|
1024
|
+
project: getProject(root),
|
|
1025
|
+
port
|
|
1026
|
+
});
|
|
1027
|
+
return fail({
|
|
1028
|
+
code: "STIM_REMOTE_START_REQUIRED",
|
|
1029
|
+
message: stillLive ? `The Expo dev server on port ${port} is local-only and cannot gain a tunnel while it is running.` : `The Expo dev server on port ${port} stopped before its tunnel became ready.`,
|
|
1030
|
+
remedy: stillLive ? "Run `stim stop`, then `stim start --remote`." : "Run `stim start --remote` again."
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
supervisor = liveSupervisor({
|
|
1034
|
+
state: readWorkspaceState$1(root),
|
|
1035
|
+
project: getProject(root),
|
|
1036
|
+
port
|
|
765
1037
|
});
|
|
766
1038
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
1039
|
+
requireExpoTunnel();
|
|
1040
|
+
if (!supervisor) {
|
|
1041
|
+
note(chalk.dim(`A dev server for this project already answers on port ${port}, started outside Stim.`));
|
|
1042
|
+
note(chalk.dim("Leaving it alone: Stim will not start a second bundler over a working one."));
|
|
1043
|
+
}
|
|
1044
|
+
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
1045
|
+
await withWorkspaceStateLockAsync(root, () => clearWorkspaceStateKeys(root, [IDLE_STOP_KEY]), operationSignal);
|
|
1046
|
+
return startFacts({
|
|
1047
|
+
root,
|
|
1048
|
+
port,
|
|
1049
|
+
supervisor,
|
|
1050
|
+
logsDir,
|
|
1051
|
+
alreadyRunning: true
|
|
776
1052
|
});
|
|
777
1053
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
const tracked = managedTunnel;
|
|
782
|
-
const record = tracked.record;
|
|
783
|
-
const stopped = tracked.startedHere ? await d.stopTunnel(record) : { status: "missing" };
|
|
784
|
-
if (stopped.status !== "failed") d.clearTunnelRecord(root, record);
|
|
785
|
-
return fail({
|
|
786
|
-
code: "STIM_REMOTE_METRO_UNREACHABLE",
|
|
787
|
-
message: `The managed ${record.provider} tunnel exited before the dev server became ready.`,
|
|
788
|
-
remedy: stopped.status === "failed" ? `The tunnel cleanup also failed: ${stopped.reason ?? "unknown error"}. Run \`stim stop\`, then retry.` : "Run `stim stop`, then `stim start --remote`."
|
|
789
|
-
});
|
|
790
|
-
};
|
|
791
|
-
const requireExpoTunnel = () => {
|
|
792
|
-
if (tunnel && (!supervisor || readMetroTunnel(root)?.kind !== "expo")) fail({
|
|
793
|
-
code: "STIM_REMOTE_START_REQUIRED",
|
|
794
|
-
message: `The Expo dev server on port ${port} is local-only and cannot gain a tunnel while it is running.`,
|
|
795
|
-
remedy: "Run `stim stop`, then `stim start --remote`."
|
|
796
|
-
});
|
|
797
|
-
};
|
|
798
|
-
if (!spawnedChild && resolution.metro) {
|
|
799
|
-
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
800
|
-
if (tunnel && supervisor) {
|
|
801
|
-
if (!await waitForExpoTunnel({
|
|
1054
|
+
if (!spawned.child && supervisor) {
|
|
1055
|
+
note(chalk.dim(`Supervisor pid ${supervisor.pid} is already running for this workspace; waiting for it to answer on port ${port}...`));
|
|
1056
|
+
if (!await waitForMetro({
|
|
802
1057
|
root,
|
|
1058
|
+
port,
|
|
803
1059
|
seconds: waitSeconds,
|
|
804
|
-
aborted:
|
|
805
|
-
state: readWorkspaceState$1(root),
|
|
806
|
-
project: getProject(root),
|
|
807
|
-
port
|
|
808
|
-
})
|
|
1060
|
+
aborted: managedTunnel ? managedTunnelExited : void 0
|
|
809
1061
|
})) {
|
|
810
|
-
|
|
811
|
-
state: readWorkspaceState$1(root),
|
|
812
|
-
project: getProject(root),
|
|
813
|
-
port
|
|
814
|
-
});
|
|
1062
|
+
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
815
1063
|
return fail({
|
|
816
|
-
code: "
|
|
817
|
-
message:
|
|
818
|
-
|
|
1064
|
+
code: "STIM_METRO_TIMEOUT",
|
|
1065
|
+
message: `Supervisor pid ${supervisor.pid} did not serve port ${port} within ${waitSeconds}s.`,
|
|
1066
|
+
lines: logTailLines(logFile),
|
|
1067
|
+
remedy: "Run `stim stop` to halt it, then `stim start` again."
|
|
819
1068
|
});
|
|
820
1069
|
}
|
|
1070
|
+
if (tunnel) {
|
|
1071
|
+
if (!await waitForExpoTunnel({
|
|
1072
|
+
root,
|
|
1073
|
+
seconds: waitSeconds,
|
|
1074
|
+
aborted: () => !liveSupervisor({
|
|
1075
|
+
state: readWorkspaceState$1(root),
|
|
1076
|
+
project: getProject(root),
|
|
1077
|
+
port
|
|
1078
|
+
})
|
|
1079
|
+
})) {
|
|
1080
|
+
const stillLive = liveSupervisor({
|
|
1081
|
+
state: readWorkspaceState$1(root),
|
|
1082
|
+
project: getProject(root),
|
|
1083
|
+
port
|
|
1084
|
+
});
|
|
1085
|
+
return fail({
|
|
1086
|
+
code: "STIM_REMOTE_START_REQUIRED",
|
|
1087
|
+
message: stillLive ? `The Expo dev server on port ${port} is local-only and cannot gain a tunnel while it is running.` : `The Expo dev server on port ${port} stopped before its tunnel became ready.`,
|
|
1088
|
+
remedy: stillLive ? "Run `stim stop`, then `stim start --remote`." : "Run `stim start --remote` again."
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
821
1092
|
supervisor = liveSupervisor({
|
|
822
1093
|
state: readWorkspaceState$1(root),
|
|
823
1094
|
project: getProject(root),
|
|
824
1095
|
port
|
|
1096
|
+
}) || (tunnel ? null : supervisor);
|
|
1097
|
+
requireExpoTunnel();
|
|
1098
|
+
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
1099
|
+
return startFacts({
|
|
1100
|
+
root,
|
|
1101
|
+
port,
|
|
1102
|
+
supervisor,
|
|
1103
|
+
logsDir,
|
|
1104
|
+
alreadyRunning: true
|
|
825
1105
|
});
|
|
826
1106
|
}
|
|
827
|
-
requireExpoTunnel();
|
|
828
|
-
if (!supervisor) {
|
|
829
|
-
note(chalk.dim(`A dev server for this project already answers on port ${port}, started outside Stim.`));
|
|
830
|
-
note(chalk.dim("Leaving it alone: Stim will not start a second bundler over a working one."));
|
|
831
|
-
}
|
|
832
1107
|
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
root,
|
|
836
|
-
port,
|
|
837
|
-
supervisor,
|
|
838
|
-
logsDir,
|
|
839
|
-
alreadyRunning: true
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
if (!spawnedChild && supervisor) {
|
|
843
|
-
note(chalk.dim(`Supervisor pid ${supervisor.pid} is already running for this workspace; waiting for it to answer on port ${port}...`));
|
|
1108
|
+
const child = spawned.child ?? await spawnSupervisor(publicOrigin);
|
|
1109
|
+
const attemptStartedTs = spawnedTs ?? Date.now();
|
|
844
1110
|
if (!await waitForMetro({
|
|
845
1111
|
root,
|
|
846
1112
|
port,
|
|
847
1113
|
seconds: waitSeconds,
|
|
848
|
-
aborted:
|
|
1114
|
+
aborted: () => childExit !== null || (child.pid ? !pidExists(child.pid) : false) || managedTunnelExited()
|
|
849
1115
|
})) {
|
|
850
1116
|
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
851
|
-
|
|
1117
|
+
const gone = childExit !== null || (child.pid ? !pidExists(child.pid) : false);
|
|
1118
|
+
const exitInfo = childExit;
|
|
1119
|
+
const how = exitInfo ? exitInfo.signal ? `signal ${exitInfo.signal}` : `code ${exitInfo.code}` : "without being observed";
|
|
1120
|
+
return fail(gone ? {
|
|
1121
|
+
code: "STIM_SUPERVISOR_EXITED",
|
|
1122
|
+
message: `The supervisor exited (${how}) before the dev server came up on port ${port}.`,
|
|
1123
|
+
lines: failureEvidence({
|
|
1124
|
+
logFile,
|
|
1125
|
+
logsDir,
|
|
1126
|
+
sinceTs: attemptStartedTs
|
|
1127
|
+
}),
|
|
1128
|
+
remedy: "Fix the error above and run `stim start` again; `stim logs --errors` has the full records."
|
|
1129
|
+
} : {
|
|
852
1130
|
code: "STIM_METRO_TIMEOUT",
|
|
853
|
-
message: `
|
|
854
|
-
lines:
|
|
855
|
-
|
|
1131
|
+
message: `The dev server did not answer on port ${port} within ${waitSeconds}s.`,
|
|
1132
|
+
lines: failureEvidence({
|
|
1133
|
+
logFile,
|
|
1134
|
+
logsDir,
|
|
1135
|
+
sinceTs: attemptStartedTs
|
|
1136
|
+
}),
|
|
1137
|
+
remedy: "It may still be starting. Run `stim stop` to halt it, or `stim logs` to follow along."
|
|
856
1138
|
});
|
|
857
1139
|
}
|
|
1140
|
+
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
858
1141
|
if (tunnel) {
|
|
859
1142
|
if (!await waitForExpoTunnel({
|
|
860
1143
|
root,
|
|
861
1144
|
seconds: waitSeconds,
|
|
862
|
-
aborted: () => !
|
|
863
|
-
state: readWorkspaceState$1(root),
|
|
864
|
-
project: getProject(root),
|
|
865
|
-
port
|
|
866
|
-
})
|
|
1145
|
+
aborted: () => childExit !== null || (child.pid ? !pidExists(child.pid) : false)
|
|
867
1146
|
})) {
|
|
868
|
-
const
|
|
869
|
-
state: readWorkspaceState$1(root),
|
|
870
|
-
project: getProject(root),
|
|
871
|
-
port
|
|
872
|
-
});
|
|
1147
|
+
const gone = childExit !== null || (child.pid ? !pidExists(child.pid) : false);
|
|
873
1148
|
return fail({
|
|
874
|
-
code: "
|
|
875
|
-
message:
|
|
876
|
-
remedy:
|
|
1149
|
+
code: gone ? "STIM_SUPERVISOR_EXITED" : "STIM_METRO_TIMEOUT",
|
|
1150
|
+
message: gone ? `The supervisor exited before the Expo tunnel became ready on port ${port}.` : `Expo did not report a tunnel URL within ${waitSeconds}s.`,
|
|
1151
|
+
remedy: "Run `stim stop`, then `stim start --remote`."
|
|
877
1152
|
});
|
|
878
1153
|
}
|
|
879
1154
|
}
|
|
880
|
-
supervisor =
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
1155
|
+
supervisor = await waitForSupervisorHandoff(child);
|
|
1156
|
+
if (!supervisor) return fail({
|
|
1157
|
+
code: "STIM_SUPERVISOR_EXITED",
|
|
1158
|
+
message: "The dev server answered before its supervisor ownership could be verified.",
|
|
1159
|
+
remedy: "Retry `stim start` after the supervisor exits or records its ownership."
|
|
1160
|
+
});
|
|
887
1161
|
return startFacts({
|
|
888
1162
|
root,
|
|
889
1163
|
port,
|
|
890
1164
|
supervisor,
|
|
891
1165
|
logsDir,
|
|
892
|
-
alreadyRunning:
|
|
1166
|
+
alreadyRunning: false
|
|
893
1167
|
});
|
|
1168
|
+
} finally {
|
|
1169
|
+
await reconcileSpawnedSupervisor();
|
|
894
1170
|
}
|
|
895
|
-
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
896
|
-
const child = spawnedChild ?? await spawnSupervisor(publicOrigin);
|
|
897
|
-
const attemptStartedTs = spawnedTs ?? Date.now();
|
|
898
|
-
if (!await waitForMetro({
|
|
899
|
-
root,
|
|
900
|
-
port,
|
|
901
|
-
seconds: waitSeconds,
|
|
902
|
-
aborted: () => childExit !== null || (child.pid ? !pidExists(child.pid) : false) || managedTunnelExited()
|
|
903
|
-
})) {
|
|
904
|
-
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
905
|
-
const gone = childExit !== null || (child.pid ? !pidExists(child.pid) : false);
|
|
906
|
-
const exitInfo = childExit;
|
|
907
|
-
const how = exitInfo ? exitInfo.signal ? `signal ${exitInfo.signal}` : `code ${exitInfo.code}` : "without being observed";
|
|
908
|
-
return fail(gone ? {
|
|
909
|
-
code: "STIM_SUPERVISOR_EXITED",
|
|
910
|
-
message: `The supervisor exited (${how}) before the dev server came up on port ${port}.`,
|
|
911
|
-
lines: failureEvidence({
|
|
912
|
-
logFile,
|
|
913
|
-
logsDir,
|
|
914
|
-
sinceTs: attemptStartedTs
|
|
915
|
-
}),
|
|
916
|
-
remedy: "Fix the error above and run `stim start` again; `stim logs --errors` has the full records."
|
|
917
|
-
} : {
|
|
918
|
-
code: "STIM_METRO_TIMEOUT",
|
|
919
|
-
message: `The dev server did not answer on port ${port} within ${waitSeconds}s.`,
|
|
920
|
-
lines: failureEvidence({
|
|
921
|
-
logFile,
|
|
922
|
-
logsDir,
|
|
923
|
-
sinceTs: attemptStartedTs
|
|
924
|
-
}),
|
|
925
|
-
remedy: "It may still be starting. Run `stim stop` to halt it, or `stim logs` to follow along."
|
|
926
|
-
});
|
|
927
|
-
}
|
|
928
|
-
if (managedTunnelExited()) return failExitedManagedTunnel();
|
|
929
|
-
if (tunnel) {
|
|
930
|
-
if (!await waitForExpoTunnel({
|
|
931
|
-
root,
|
|
932
|
-
seconds: waitSeconds,
|
|
933
|
-
aborted: () => childExit !== null || (child.pid ? !pidExists(child.pid) : false)
|
|
934
|
-
})) {
|
|
935
|
-
const gone = childExit !== null || (child.pid ? !pidExists(child.pid) : false);
|
|
936
|
-
return fail({
|
|
937
|
-
code: gone ? "STIM_SUPERVISOR_EXITED" : "STIM_METRO_TIMEOUT",
|
|
938
|
-
message: gone ? `The supervisor exited before the Expo tunnel became ready on port ${port}.` : `Expo did not report a tunnel URL within ${waitSeconds}s.`,
|
|
939
|
-
remedy: "Run `stim stop`, then `stim start --remote`."
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
supervisor = liveSupervisor({
|
|
944
|
-
state: readWorkspaceState$1(root),
|
|
945
|
-
project: getProject(root),
|
|
946
|
-
port
|
|
947
|
-
}) || {
|
|
948
|
-
pid: child.pid,
|
|
949
|
-
port,
|
|
950
|
-
mode: null,
|
|
951
|
-
startedAt: null
|
|
952
|
-
};
|
|
953
|
-
return startFacts({
|
|
954
|
-
root,
|
|
955
|
-
port,
|
|
956
|
-
supervisor,
|
|
957
|
-
logsDir,
|
|
958
|
-
alreadyRunning: false
|
|
959
|
-
});
|
|
960
1171
|
};
|
|
961
1172
|
const startLocked = async () => {
|
|
962
1173
|
try {
|
|
963
|
-
return await withWorkspaceProcessLock(dirname(workspaceLogsDir$1(root)), "metro-start", runStart, {
|
|
1174
|
+
return await d.withTunnelLock(root, (tunnelClaim) => withWorkspaceProcessLock(dirname(workspaceLogsDir$1(root)), "metro-start", (claim) => runStart(claim, tunnelClaim), {
|
|
1175
|
+
external: true,
|
|
1176
|
+
signal: operationSignal
|
|
1177
|
+
}), { signal: operationSignal });
|
|
964
1178
|
} catch (error) {
|
|
1179
|
+
if (error instanceof WorkspaceProcessCleanupError || error instanceof StartRefusal) throw error;
|
|
1180
|
+
throwIfCancelled(operationSignal, "Metro startup");
|
|
1181
|
+
if (workspaceProcessLockError(error) === "refused") return fail({
|
|
1182
|
+
code: "STIM_WORKTREE_REMOVAL_IN_PROGRESS",
|
|
1183
|
+
message: `The workspace at ${root} is being removed.`,
|
|
1184
|
+
remedy: "Retry `stim start` after workspace removal finishes."
|
|
1185
|
+
});
|
|
965
1186
|
if (workspaceProcessLockError(error) !== "timeout") throw error;
|
|
966
1187
|
return fail({
|
|
967
1188
|
code: "STIM_METRO_TIMEOUT",
|
|
@@ -972,7 +1193,7 @@ async function startDevServer({ root, settings, settingsContext, waitSeconds = D
|
|
|
972
1193
|
};
|
|
973
1194
|
if (!managedRemote) return startLocked();
|
|
974
1195
|
try {
|
|
975
|
-
return await d.withWorktreeLock(worktreeRoot, startLocked);
|
|
1196
|
+
return await d.withWorktreeLock(worktreeRoot, startLocked, { signal: operationSignal });
|
|
976
1197
|
} catch (err) {
|
|
977
1198
|
const lockError = workspaceProcessLockError(err);
|
|
978
1199
|
if (lockError === "refused") return fail({
|
|
@@ -996,6 +1217,13 @@ async function startDevServer({ root, settings, settingsContext, waitSeconds = D
|
|
|
996
1217
|
reclaimed
|
|
997
1218
|
};
|
|
998
1219
|
} catch (error) {
|
|
1220
|
+
if (error instanceof WorkspaceProcessCleanupError) return {
|
|
1221
|
+
ok: false,
|
|
1222
|
+
error: startError(error),
|
|
1223
|
+
lines: [],
|
|
1224
|
+
reclaimed,
|
|
1225
|
+
retainedClaimRoots: [...new Set(error.claims.map((claim) => claim.root))]
|
|
1226
|
+
};
|
|
999
1227
|
if (!(error instanceof StartRefusal)) throw error;
|
|
1000
1228
|
return {
|
|
1001
1229
|
ok: false,
|
|
@@ -1063,8 +1291,9 @@ async function resolveWorkspaceMetroPort(root, settings, note, command, reserve
|
|
|
1063
1291
|
}
|
|
1064
1292
|
async function reservePort(root, pinned) {
|
|
1065
1293
|
try {
|
|
1066
|
-
return await reserveMetroPort(root, void 0, void 0, pinned);
|
|
1294
|
+
return await reserveMetroPort(root, void 0, void 0, pinned, ambientContext()?.signal);
|
|
1067
1295
|
} catch (error) {
|
|
1296
|
+
throwIfCancelled(ambientContext()?.signal, "Metro startup");
|
|
1068
1297
|
if (error instanceof PortInspectionError) return {
|
|
1069
1298
|
code: error.code,
|
|
1070
1299
|
message: error.reason,
|