stim 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +1 -1
  2. package/dist/{android-DJWiTBkz.mjs → android-DWssqHQ8.mjs} +165 -661
  3. package/dist/{android-sobvO9ia.mjs → android-Da-G_o3n.mjs} +646 -548
  4. package/dist/{android-BW8i_YxU.mjs → android-DeYdjjBP.mjs} +6 -2
  5. package/dist/{android-cas-CSS8gjR7.mjs → android-cas-SWIjzeuL.mjs} +54 -18
  6. package/dist/android-cas-compiler.mjs +1 -1
  7. package/dist/{app-install--PAocWvq.mjs → app-install-DJg7bVv-.mjs} +36 -10
  8. package/dist/build-lock-CgtsB6TV.mjs +604 -0
  9. package/dist/{build-slots-CuPszJDu.mjs → build-slots-C-oAHRT1.mjs} +2 -2
  10. package/dist/{cache-manifest-uoOKhgUD.mjs → cache-manifest-CYJi8-Hx.mjs} +2 -2
  11. package/dist/cache-manifest.d.mts +1 -1
  12. package/dist/cache-manifest.mjs +1 -1
  13. package/dist/cli.mjs +14 -14
  14. package/dist/collector-run.d.mts +2 -0
  15. package/dist/collector-run.mjs +7 -7
  16. package/dist/{command-output-BuIb_ZOI.mjs → command-output-B3ycpsYv.mjs} +2 -2
  17. package/dist/{config-DMP7L7H6.mjs → config-B7iyKWkL.mjs} +56 -16
  18. package/dist/{dependency-state-CTL8A9p3.mjs → dependency-state-D0M7oKA7.mjs} +2 -2
  19. package/dist/{deps-BAGOnrc7.mjs → deps-oCXVNEqQ.mjs} +6 -6
  20. package/dist/detached-entry-Bpo6ZY_I.mjs +65 -0
  21. package/dist/{dev-client-BoifdqkU.mjs → dev-client-DDUkWgJL.mjs} +12 -12
  22. package/dist/{device-BUw4JBuH.mjs → device-CBSaC6BU.mjs} +7 -7
  23. package/dist/{device-DvKJ454A.mjs → device-CLeDp-KH.mjs} +17 -15
  24. package/dist/{device-lease-r7yEQ1R8.mjs → device-lease-Ffz_M5t0.mjs} +3 -3
  25. package/dist/{device-pool-DU4FQKp5.mjs → device-pool-DNSHL5z8.mjs} +4 -4
  26. package/dist/{device-remote-BTUqbl0i.mjs → device-remote-BprxZp-m.mjs} +11 -11
  27. package/dist/{doctor-Di_B26uh.mjs → doctor-CTAx9gMu.mjs} +90 -46
  28. package/dist/{error-diagnostics-CTePWAzW.mjs → error-diagnostics-D1Kaf-eT.mjs} +23 -17
  29. package/dist/{exec-CyylIdq9.mjs → exec-B29XNuhP.mjs} +14 -2
  30. package/dist/{gc-C20dWw_8.mjs → gc-CX0KRfLG.mjs} +17 -16
  31. package/dist/{guide-sAnoty2y.mjs → guide-CPmAG9me.mjs} +61 -22
  32. package/dist/{guide-status-BF6RsJJp.mjs → guide-status-Craq9z0t.mjs} +3 -3
  33. package/dist/{ios-BdpkncL6.mjs → ios-BRXYSxSc.mjs} +34 -37
  34. package/dist/{ios-device-DJOi9FR5.mjs → ios-device-CDbvn5H2.mjs} +2 -2
  35. package/dist/{ios-device-D3pzTKQE.mjs → ios-device-CFXA46ez.mjs} +1 -1
  36. package/dist/{ios-Deh5js5V.mjs → ios-lEJ5pKsq.mjs} +3 -3
  37. package/dist/{logs-BfQdIhiF.mjs → logs-CakdBTtm.mjs} +6 -6
  38. package/dist/{logs-query-Cdp5aaCH.mjs → logs-query-BGL-Pl12.mjs} +1 -1
  39. package/dist/metro-C7bcJ0uo.mjs +233 -0
  40. package/dist/{named-ports-DH9SXd0f.mjs → named-ports-DzMOlmaX.mjs} +57 -42
  41. package/dist/{ownership-CD_XnpDu.mjs → ownership-Cp52QAK1.mjs} +3 -3
  42. package/dist/{ownership-CVY9cqCr.mjs → ownership-D5vkdm8H.mjs} +13 -13
  43. package/dist/{ports-A6uTJSPj.mjs → ports-BIbJaKNW.mjs} +3 -3
  44. package/dist/{process-output-C7IlIzd7.mjs → process-output-Dl5uKE03.mjs} +1 -1
  45. package/dist/{project-B_SDt4ip.mjs → project-DDZWf_16.mjs} +3 -3
  46. package/dist/{reclaim-C_rB25f4.mjs → reclaim-D614jI6_.mjs} +11 -11
  47. package/dist/{reload-CYBJbJfI.mjs → reload-ChW46VDb.mjs} +9 -9
  48. package/dist/{server-bare-H3pAhbMs.mjs → server-bare-BQkyxa_b.mjs} +1 -1
  49. package/dist/{server-expo-Ds8_mfng.mjs → server-expo-BVAA1OqJ.mjs} +23 -11
  50. package/dist/server-expo-YDDG3eP7.mjs +2 -0
  51. package/dist/{settings-kO2ZqIKy.mjs → settings-BXXorVM2.mjs} +8 -5
  52. package/dist/{slot-launch-BpHiHH6A.mjs → slot-launch-DbLvyrup.mjs} +4 -4
  53. package/dist/{start-DiEfqEWk.mjs → start-TTVILlUi.mjs} +111 -38
  54. package/dist/{state-CnWzvCWP.mjs → state-DZobzxLW.mjs} +2 -2
  55. package/dist/{stats-Fo9ACIv7.mjs → stats-BYcZ6n1h.mjs} +1 -1
  56. package/dist/{stats-DHrO0frH.mjs → stats-CLg5RuDS.mjs} +4 -4
  57. package/dist/{status-J5LAXpag.mjs → status-DUNy_HZs.mjs} +14 -15
  58. package/dist/{stop-CM1fejKW.mjs → stop-CnUpnFT5.mjs} +2 -2
  59. package/dist/{stop-0jJNahwN.mjs → stop-ZrfODZ51.mjs} +13 -13
  60. package/dist/supervisor-run.mjs +7 -5
  61. package/dist/{teardown-CAVcWeRD.mjs → teardown-5J5UXz2P.mjs} +14 -17
  62. package/dist/{workspace-process-lock-Bz19_R9R.mjs → workspace-process-lock-BYfZz_S-.mjs} +1 -1
  63. package/dist/{workspace-state-WVwWw_hB.mjs → workspace-state-BT5E3SA2.mjs} +3 -3
  64. package/dist/{worktree-CeUKDAQw.mjs → worktree-BfE4lzHK.mjs} +21 -16
  65. package/dist/{worktree-DJnBbQZ9.mjs → worktree-FG_ZKKdb.mjs} +13 -9
  66. package/dist/{xcode-BqDeRkpT.mjs → xcode-B6YaWeg0.mjs} +92 -17
  67. package/package.json +12 -8
  68. package/shim/android-optimizations.gradle +6 -0
  69. package/dist/metro-ato6ef3J.mjs +0 -99
  70. package/dist/ports-BZprTJRa.mjs +0 -90
  71. package/dist/server-expo-CSwUVYOa.mjs +0 -2
@@ -1,6 +1,6 @@
1
- import { a as findProjectRoot } from "./project-B_SDt4ip.mjs";
2
- import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-kO2ZqIKy.mjs";
3
- import { t as guideStatus } from "./guide-status-BF6RsJJp.mjs";
1
+ import { a as findProjectRoot } from "./project-DDZWf_16.mjs";
2
+ import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-BXXorVM2.mjs";
3
+ import { t as guideStatus } from "./guide-status-Craq9z0t.mjs";
4
4
  import chalk from "chalk";
5
5
  //#endregion
6
6
  //#region src/guide/index.ts
@@ -516,7 +516,7 @@ line by design (see \`guide logs\`), not this single-payload contract.`,
516
516
  the remedies all target -- read from the BUILT APK's
517
517
  manifest, which on a flavored project is the flavor's
518
518
  applicationId, not what the project files say
519
- debugHttpHost "10.0.2.2:<port>" on an emulator, "localhost:<port>" on a
519
+ debugHttpHost "127.0.0.1:<port>" on an emulator, "localhost:<port>" on a
520
520
  physical device, when the app's SharedPreferences were
521
521
  pointed at this workspace's Metro; null when they were not.
522
522
  A healthy run reverses only <port> -> <port>, which is what
@@ -927,8 +927,9 @@ metro is reserved; use stim start and stim stop for managed Metro.
927
927
 
928
928
  New allocations scan TCP ports 8900-8999. They skip registry reservations
929
929
  and existing listeners, announcing occupied ports and upward retries on
930
- stderr. Allocation requires lsof. All 100 ports occupied or reserved is a
931
- refusal; stop or release unused allocations in their owning workspaces.
930
+ stderr. Listener checks require lsof, or netstat on Windows. All 100 ports
931
+ occupied or reserved is a refusal; stop or release unused allocations in
932
+ their owning workspaces.
932
933
 
933
934
  The machine registry, under STIM_HOME, serializes allocation and cleanup.
934
935
  The workspace is the nearest package.json directory, resolved through
@@ -940,9 +941,11 @@ supervise, or capture logs for these servers.
940
941
 
941
942
  ports lists named labels and ports, plus Metro marked managed.
942
943
  ports stop [label] kills TCP listeners on those named ports and releases
943
- the allocations. It sends SIGTERM, waits two seconds, then SIGKILL if needed.
944
- It prints the PID and command for each stopped process. The listener's cwd
945
- can be anywhere: the named reservation is permission to stop that listener.
944
+ the allocations. It sends SIGTERM, waits two seconds, then SIGKILL if needed;
945
+ on Windows it terminates the listener's process tree with taskkill.
946
+ It prints the PID and command (the image name on Windows) for each stopped
947
+ process. The listener's cwd can be anywhere: the named reservation is
948
+ permission to stop that listener.
946
949
  Reserve only services this workspace may stop. --dry-run prints what would
947
950
  be stopped and released, without doing either. A failed inspection or stop
948
951
  keeps that allocation for retry; other labels are still processed.
@@ -1380,6 +1383,25 @@ code, never on the message.`,
1380
1383
  object, and upgrading the CLI does not clear it. Empty that one cache with
1381
1384
  \`gc --delete --cache "compilation cache"\`, then build again. The next
1382
1385
  build is a cold one.`
1386
+ },
1387
+ STIM_PATH_TOO_LONG: {
1388
+ summary: "Windows only: the project root leaves no room for the NDK object paths ninja must open",
1389
+ body: () => `STIM_PATH_TOO_LONG (android, Windows only)
1390
+ The NDK's ninja is not long-path aware, so every CMake object path has to
1391
+ stay under Windows' MAX_PATH. React Native's codegen objects are named
1392
+ after their mangled absolute source path, so each carries the project root
1393
+ twice unless CMake shortens it, and CMake only shortens a name that then
1394
+ fits the object path limit Stim's Gradle shim sets. The message names the
1395
+ longest known object (measured on the e2e fixture, for the ABI being
1396
+ built) and the longest root it leaves room for. Refused BEFORE Gradle runs
1397
+ -- a cache hit still installs -- because the build would otherwise fail
1398
+ deep inside ninja ("mkdir ... No such file or directory" or "Filename
1399
+ longer than 260 characters"). Map the project to a drive letter (\`subst
1400
+ X: <root>\`, then run Stim from X:\\) or move it under a shorter root.
1401
+ A custom Gradle buildStagingDirectory can be shorter than the default; this
1402
+ preflight does not inspect that configured path and may refuse it too.
1403
+ \`doctor --platform android\` reports the same root as a finding for the
1404
+ emulator's ABI.`
1383
1405
  },
1384
1406
  fallbacks: {
1385
1407
  summary: "release cache-hit notes that are not codes: swap failure, asset gate, uninstall, device fallbacks",
@@ -2317,7 +2339,7 @@ not on any remote" (worktree remove)
2317
2339
  the no-install form.
2318
2340
 
2319
2341
  "Unsupported engine" or a syntax error before Stim starts
2320
- Stim requires Node 20.19.4 or later on Node 20, or Node 22.12.0 or later.
2342
+ Stim requires Node 22.12.0 or later.
2321
2343
  Switch Node versions, then run the command again.
2322
2344
 
2323
2345
 
@@ -2527,7 +2549,9 @@ from bundle delivery and readiness verification: it gives CoreSimulator time
2527
2549
  to accept the launch, not the app extra time to report readiness. A timeout
2528
2550
  still fails the launch; Stim does not automatically retry it.
2529
2551
 
2530
- For an unverified Android launch, follow the emitted device-specific remedy.
2552
+ For an unverified local Android launch, follow the emitted device-specific remedy.
2553
+ Only a dev client gets the Expo development-server picker step; a bare app
2554
+ starts with the printed process restart.
2531
2555
  If the app is stuck before loading its first bundle, restart its process with
2532
2556
  the printed force-stop and launcher commands. Foregrounding the same process
2533
2557
  does not restart initialization. Confirm the bundle request and expected UI.
@@ -3077,8 +3101,13 @@ with machine or project optimization settings; see \`guide settings\`:
3077
3101
  ios xcodebuild carries COMPILATION_CACHE_ENABLE_CACHING, a shared
3078
3102
  COMPILATION_CACHE_CAS_PATH and a clang prefix mapping of this
3079
3103
  workspace's root, so compiled output crosses worktrees with no
3080
- Podfile post_install block. Xcode 26+ only, and skipped entirely
3081
- when the project configured ccache (the two defeat each other).
3104
+ Podfile post_install block. Swift joins in, with its own prefix
3105
+ mapping, once xcrun swift reports 6.4 or newer (Xcode 27) and
3106
+ react-native is 0.87 or newer; the cache line names Swift on or
3107
+ off and why. Xcode still excludes Swift targets built without
3108
+ explicit modules; the post-build cache line counts those.
3109
+ Xcode 26+ only, and skipped entirely when the project
3110
+ configured ccache (the two defeat each other).
3082
3111
  android gradlew carries --build-cache, so task outputs cross worktrees with
3083
3112
  no org.gradle.caching=true in gradle.properties. Debug builds also
3084
3113
  carry -PreactNativeArchitectures=<target ABI>, using the owned
@@ -3606,9 +3635,8 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
3606
3635
  The build, the fingerprint, the build cache and the Metro port gate are
3607
3636
  unchanged. What is skipped is everything that manages an owned device:
3608
3637
  no capacity check, no AVD creation, no boot wait, and no owned-device
3609
- registry entry. The app is pointed at
3610
- localhost:<port>, which the adb reverse serves, instead of the emulator's
3611
- 10.0.2.2. Stim never creates, boots, shuts down, or deletes hardware.
3638
+ registry entry. A debug app uses localhost:<port> through adb reverse.
3639
+ Stim never creates, boots, shuts down, or deletes hardware.
3612
3640
 
3613
3641
  \`ios --device [udid]\` selects a connected iPhone, the same way
3614
3642
  \`android --device\` selects a connected phone: with no UDID it takes the
@@ -3817,7 +3845,7 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
3817
3845
  workspace's bundle with the project's own tools (\`expo export:embed\` /
3818
3846
  \`react-native bundle\`, then the project's own hermesc when
3819
3847
  \`hermesEnabled\` is not false in android/gradle.properties), re-packs it
3820
- into the copy with plain zip surgery (stored, not deflated -- the runtime
3848
+ into the copy with the JDK's jar tool (stored, not deflated -- the runtime
3821
3849
  mmaps it), then zipaligns and re-signs with apksigner. The keystore defaults
3822
3850
  to android/app/debug.keystore with the standard password; android.keystore /
3823
3851
  android.keystorePassword override it (see \`guide settings\`). The cache
@@ -4542,9 +4570,10 @@ be setup steps are supplied by Stim on the command lines it composes itself:
4542
4570
 
4543
4571
  xcodebuild COMPILATION_CACHE_ENABLE_CACHING / COMPILATION_CACHE_CAS_PATH /
4544
4572
  SWIFT_ENABLE_COMPILE_CACHE / CLANG_ENABLE_PREFIX_MAPPING /
4545
- CLANG_OTHER_PREFIX_MAPPINGS -- so no Podfile post_install block
4546
- (Xcode 26+ only, and skipped when the project configured ccache,
4547
- which defeats it)
4573
+ CLANG_OTHER_PREFIX_MAPPINGS, plus SWIFT_ENABLE_PREFIX_MAPPING /
4574
+ SWIFT_OTHER_PREFIX_MAPPINGS on a Swift 6.4+ toolchain -- so no
4575
+ Podfile post_install block (Xcode 26+ only, and skipped when the
4576
+ project configured ccache, which defeats it)
4548
4577
  gradlew --build-cache -- so no org.gradle.caching=true in a committed
4549
4578
  gradle.properties. Debug builds add
4550
4579
  -PreactNativeArchitectures=<target ABI> when the owned
@@ -4582,7 +4611,6 @@ key inherits the next layer. Changes apply on the next build or Metro restart.
4582
4611
  "metroWarmup": true,
4583
4612
  "ios": {
4584
4613
  "compilationCache": true,
4585
- "swiftCompilationCache": false,
4586
4614
  "prefixMapping": true
4587
4615
  },
4588
4616
  "android": {
@@ -4619,7 +4647,18 @@ The example shows the defaults. Full setting names and behavior:
4619
4647
  optimizations.ios.compilationCache
4620
4648
  controls Xcode compilation caching (Xcode 26+).
4621
4649
  optimizations.ios.swiftCompilationCache
4622
- opts into experimental Swift caching, requiring compilationCache=true.
4650
+ unset by default: Swift caching turns on, with Swift prefix mapping, when
4651
+ xcrun swift reports Swift 6.4 or newer (Xcode 27), where swift-frontend no
4652
+ longer crashes on prefix-mapped batches, and the project's react-native is
4653
+ 0.87 or newer. Older toolchains leave it off. React Native before 0.87
4654
+ sets SWIFT_ENABLE_EXPLICIT_MODULES=NO on every target for its prebuilt
4655
+ core, and Xcode refuses Swift caching without explicit modules, so those
4656
+ projects stay off too; the cache line names which gate applied.
4657
+ true forces it on any toolchain and React Native version, unmapped below
4658
+ Swift 6.4, so entries only hit within one checkout. false keeps it off.
4659
+ Requires compilationCache=true. A project that still disables explicit
4660
+ modules itself sees the excluded target count on the post-build cache
4661
+ line. Stim does not override that project setting.
4623
4662
  optimizations.ios.prefixMapping
4624
4663
  controls Clang source/DerivedData prefix mapping. false clears Stim's
4625
4664
  mappings. The existing Xcode version and project-ccache guards still apply.
@@ -1,8 +1,8 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { S as upsertProject, f as getProject, u as getConfigDir } from "./config-DMP7L7H6.mjs";
1
+ import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
+ import { S as upsertProject, f as getProject, u as getConfigDir } from "./config-B7iyKWkL.mjs";
3
3
  import { accessSync, constants, mkdirSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { delimiter, join, resolve } from "node:path";
5
- //#region src/stim-installations.ts
5
+ //#region src/diagnostics/stim-installations.ts
6
6
  const VERSION_OUTPUT_LIMIT = 4096;
7
7
  function parsedVersion(version) {
8
8
  const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(version);
@@ -1,31 +1,31 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { E as ownership_claim_exports, P as deviceSlotKey, S as upsertProject, f as getProject, l as getConcurrencyLimits, z as validateDeviceSlot } from "./config-DMP7L7H6.mjs";
1
+ import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
+ import { B as validateDeviceSlot, E as ownership_claim_exports, F as deviceSlotKey, P as deviceSlotFileKey, S as upsertProject, f as getProject, l as getConcurrencyLimits } from "./config-B7iyKWkL.mjs";
3
3
  import { r as createNdjsonWriter } from "./ndjson-BZwI1NBv.mjs";
4
- import { _ as workspaceLogsDir, h as workspaceDir, o as writeWorkspaceState, r as readWorkspaceState } from "./workspace-state-WVwWw_hB.mjs";
5
- import { T as removeTemporaryEntry, g as repoRoot, s as gitCommonDir, w as makeTemporaryDirectory } from "./worktree-DJnBbQZ9.mjs";
6
- import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, r as detectBundleId, t as appProjectProblem } from "./project-B_SDt4ip.mjs";
7
- import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-CnWzvCWP.mjs";
8
- import { i as resolveProjectMetro, r as pidExists } from "./metro-ato6ef3J.mjs";
9
- import { n as verifyCollectorOwnership } from "./ownership-CD_XnpDu.mjs";
10
- import { _ as launchErrorPreview, c as launchErrorReport, g as stepTimer, h as stepClock, i as formatDuration, l as phaseLine, m as shortUdid, n as appReadinessMessage, p as shortHash } from "./command-output-BuIb_ZOI.mjs";
11
- import { $ as providerDownloadPath, G as buildCacheKey, J as filesystemBuildCapability, K as describeFingerprintMiss, Q as prepareProviderDownloadDir, R as WAIT_CEILING_MS, U as takeoverLine, V as releaseBuildLock, W as waitForBuild, X as fingerprintDiffSuffix, Y as fingerprintDiffRecord, Z as fingerprintProject, at as untrackedMissLine, et as providerUploadOutcome, nt as resolveBuild, ot as untrackedNativeFiles, rt as storeBuild, tt as refingerprintAfterMutation, z as acquireBuildLock } from "./android-DJWiTBkz.mjs";
12
- import { A as resolveSettings, B as artifactCachePolicy, D as remoteDeviceSettingError, F as tunnelModeSetting, H as optimizationBuildProfile, I as unknownSettingKeys, N as settingShapeErrors, O as remoteIosSetting, R as detectProviders, T as publicUrlSetting, U as resolveOptimizations, _ as iosSigningIdentitySha1SettingError, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, f as iosLanHostSetting, g as iosSigningIdentitySha1Setting, h as iosSigningIdentitySettingError, k as resolveCacheProviderConfig, m as iosSigningIdentitySetting, p as iosLanHostSettingError, r as REMOTE_DEVICE_BACKENDS, x as metroWarmupUrlSetting } from "./settings-kO2ZqIKy.mjs";
13
- import { C as resolveRemote, S as resolveEasCliBin, _ as exitAfterFlush, a as POOL_SETTING_REMEDY, f as RESOLVE_TIMEOUT_MS, g as easAuthNote, h as checkEasAuth, l as parkedMaxSetting, m as cacheLevel, p as UPLOAD_TIMEOUT_MS, v as isEasAuthFailureText, w as uploadRemote, y as loadProjectProvider } from "./teardown-CAVcWeRD.mjs";
14
- import { _ as easDeviceBuildRemedy, a as swapJsBundle, c as ensureWorkspaceStorageSafely, d as launchOutcomeRecord, f as noMetroMessage, g as warmMetro, h as sleep, l as gateShouldRetry, m as resolveMetroWithRetry, n as pickDevClientScheme, o as needsPrebuild, p as noMetroRemedy, r as schemesFromInfoPlist, s as runPrebuild, t as devClientScheme, u as isPhysicalDeviceRequest, v as isEasBuildFailure, y as resolveEasDevelopmentBuild } from "./dev-client-BoifdqkU.mjs";
15
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-Bz19_R9R.mjs";
16
- import { C as launchIosApp, E as unverifiedLaunchLines, O as verifyLaunch, T as readMetroRecords, b as isBundleProof, c as RELEASE_VERIFY_WAIT_MS, f as clearOtherUserApps, i as LAUNCH_BUNDLING, k as verifyReleaseLaunch, m as devClientUrl, n as DEFAULT_METRO_PORT, o as LAUNCH_FATAL, s as LAUNCH_UNVERIFIED, v as installIosApp, w as readCollectorRecords, y as iosAppProcess } from "./app-install--PAocWvq.mjs";
17
- import { f as listIosRuntimes } from "./ios-Deh5js5V.mjs";
18
- import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-DU4FQKp5.mjs";
19
- import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-CuPszJDu.mjs";
20
- import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-Fo9ACIv7.mjs";
21
- import { c as unknownIosRuntimeRefusal, i as ensureOwnedDevice, n as clearIosAdoptionPending, r as ensureBooted, s as unknownIosDeviceTypeRefusal, t as checkDeviceCapacity } from "./device-DvKJ454A.mjs";
22
- import { a as ensureMetroReachable, b as ownedSessionName, c as remoteIosDeps, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, r as bundleEntryPoint, w as gateMetroOrigin } from "./device-remote-BTUqbl0i.mjs";
23
- import { a as compilationCacheActivityLine, c as readBundleExecutable, l as readBundleId, p as describeDiagnostic, r as buildIos, s as discoverXcodeProject, t as COMPILATION_CACHE_NOT_RUN, u as resolveScheme } from "./xcode-BqDeRkpT.mjs";
24
- import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-BpHiHH6A.mjs";
4
+ import { _ as workspaceLogsDir, h as workspaceDir, o as writeWorkspaceState, r as readWorkspaceState } from "./workspace-state-BT5E3SA2.mjs";
5
+ import { T as removeTemporaryEntry, g as repoRoot, s as gitCommonDir, w as makeTemporaryDirectory } from "./worktree-FG_ZKKdb.mjs";
6
+ import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, r as detectBundleId, t as appProjectProblem } from "./project-DDZWf_16.mjs";
7
+ import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-DZobzxLW.mjs";
8
+ import { a as resolveProjectMetro, i as pidExists, o as signalProcessTree } from "./metro-C7bcJ0uo.mjs";
9
+ import { n as verifyCollectorOwnership } from "./ownership-Cp52QAK1.mjs";
10
+ import { A as resolveSettings, B as artifactCachePolicy, D as remoteDeviceSettingError, F as tunnelModeSetting, H as optimizationBuildProfile, I as unknownSettingKeys, N as settingShapeErrors, O as remoteIosSetting, R as detectProviders, T as publicUrlSetting, U as resolveOptimizations, _ as iosSigningIdentitySha1SettingError, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, f as iosLanHostSetting, g as iosSigningIdentitySha1Setting, h as iosSigningIdentitySettingError, k as resolveCacheProviderConfig, m as iosSigningIdentitySetting, p as iosLanHostSettingError, r as REMOTE_DEVICE_BACKENDS, x as metroWarmupUrlSetting } from "./settings-BXXorVM2.mjs";
11
+ import { _ as launchErrorPreview, c as launchErrorReport, g as stepTimer, h as stepClock, i as formatDuration, l as phaseLine, m as shortUdid, n as appReadinessMessage, p as shortHash } from "./command-output-B3ycpsYv.mjs";
12
+ import { C as untrackedNativeFiles, S as untrackedMissLine, _ as providerUploadOutcome, b as storeBuild, c as buildCacheKey, d as filesystemBuildCapability, f as fingerprintDiffRecord, g as providerDownloadPath, h as prepareProviderDownloadDir, i as releaseBuildLock, l as describeFingerprintMiss, m as fingerprintProject, n as acquireBuildLock, o as takeoverLine, p as fingerprintDiffSuffix, s as waitForBuild, t as WAIT_CEILING_MS, v as refingerprintAfterMutation, y as resolveBuild } from "./build-lock-CgtsB6TV.mjs";
13
+ import { C as resolveRemote, S as resolveEasCliBin, _ as exitAfterFlush, a as POOL_SETTING_REMEDY, f as RESOLVE_TIMEOUT_MS, g as easAuthNote, h as checkEasAuth, l as parkedMaxSetting, m as cacheLevel, p as UPLOAD_TIMEOUT_MS, v as isEasAuthFailureText, w as uploadRemote, y as loadProjectProvider } from "./teardown-5J5UXz2P.mjs";
14
+ import { _ as easDeviceBuildRemedy, a as swapJsBundle, c as ensureWorkspaceStorageSafely, d as launchOutcomeRecord, f as noMetroMessage, g as warmMetro, h as sleep, l as gateShouldRetry, m as resolveMetroWithRetry, n as pickDevClientScheme, o as needsPrebuild, p as noMetroRemedy, r as schemesFromInfoPlist, s as runPrebuild, t as devClientScheme, u as isPhysicalDeviceRequest, v as isEasBuildFailure, y as resolveEasDevelopmentBuild } from "./dev-client-DDUkWgJL.mjs";
15
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BYfZz_S-.mjs";
16
+ import { C as launchIosApp, E as unverifiedLaunchLines, O as verifyLaunch, T as readMetroRecords, b as isBundleProof, c as RELEASE_VERIFY_WAIT_MS, f as clearOtherUserApps, i as LAUNCH_BUNDLING, k as verifyReleaseLaunch, m as devClientUrl, n as DEFAULT_METRO_PORT, o as LAUNCH_FATAL, s as LAUNCH_UNVERIFIED, v as installIosApp, w as readCollectorRecords, y as iosAppProcess } from "./app-install-DJg7bVv-.mjs";
17
+ import { f as listIosRuntimes } from "./ios-lEJ5pKsq.mjs";
18
+ import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-DNSHL5z8.mjs";
19
+ import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-C-oAHRT1.mjs";
20
+ import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-BYcZ6n1h.mjs";
21
+ import { c as unknownIosRuntimeRefusal, i as ensureOwnedDevice, n as clearIosAdoptionPending, r as ensureBooted, s as unknownIosDeviceTypeRefusal, t as checkDeviceCapacity } from "./device-CLeDp-KH.mjs";
22
+ import { a as ensureMetroReachable, b as ownedSessionName, c as remoteIosDeps, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, r as bundleEntryPoint, w as gateMetroOrigin } from "./device-remote-BprxZp-m.mjs";
23
+ import { a as compilationCacheActivityLine, c as readBundleExecutable, l as readBundleId, p as describeDiagnostic, r as buildIos, s as discoverXcodeProject, t as COMPILATION_CACHE_NOT_RUN, u as resolveScheme } from "./xcode-B6YaWeg0.mjs";
24
+ import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-DbLvyrup.mjs";
25
25
  import { t as spawnEntry } from "./spawn-entry-nCE6m770.mjs";
26
- import { a as printNativeCrashReport, n as IOS_CRASH_REPORT_RETRY, o as simulatorConsolePaths, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-CTePWAzW.mjs";
27
- import { a as iosDeviceProcess, c as listIosDevices, d as verifyIosDeviceReleaseLaunch, i as installIosDeviceApp, l as localNetworkPending, n as LAUNCH_PROBE_TIMEOUT_MS, o as iosPoolCandidates, r as awaitIosDeviceLaunch, s as iosPoolNoCandidatesRefusal, t as DEVICECTL_INSTALL_TIMEOUT_MS, u as resolveIosPhysicalDevice } from "./ios-device-DJOi9FR5.mjs";
28
- import { a as runPodInstall, n as podsAreStale, r as readPodState } from "./deps-BAGOnrc7.mjs";
26
+ import { a as printNativeCrashReport, n as IOS_CRASH_REPORT_RETRY, o as simulatorConsolePaths, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-D1Kaf-eT.mjs";
27
+ import { a as iosDeviceProcess, c as listIosDevices, d as verifyIosDeviceReleaseLaunch, i as installIosDeviceApp, l as localNetworkPending, n as LAUNCH_PROBE_TIMEOUT_MS, o as iosPoolCandidates, r as awaitIosDeviceLaunch, s as iosPoolNoCandidatesRefusal, t as DEVICECTL_INSTALL_TIMEOUT_MS, u as resolveIosPhysicalDevice } from "./ios-device-CDbvn5H2.mjs";
28
+ import { a as runPodInstall, n as podsAreStale, r as readPodState } from "./deps-oCXVNEqQ.mjs";
29
29
  import { existsSync, mkdirSync, mkdtempSync, openSync, rmSync, writeFileSync } from "node:fs";
30
30
  import { InvalidArgumentError } from "commander";
31
31
  import { X509Certificate } from "node:crypto";
@@ -1503,14 +1503,14 @@ function hostLanCandidates(interfaces = networkInterfaces) {
1503
1503
  //#endregion
1504
1504
  //#region src/commands/ios/collector.ts
1505
1505
  function collectorLogFile(root, slot) {
1506
- return join(workspaceLogsDir(root), `collector-${deviceSlotKey("ios", slot)}.log`);
1506
+ return join(workspaceLogsDir(root), `collector-${deviceSlotFileKey("ios", slot)}.log`);
1507
1507
  }
1508
1508
  function collectorEntry() {
1509
1509
  return spawnEntry("collector-run");
1510
1510
  }
1511
1511
  const COLLECTOR_EXIT_WAIT_MS = 2e3;
1512
1512
  const COLLECTOR_POLL_MS = 25;
1513
- async function stopPreviousCollector({ root, slot = "default", kill = (pid, signal) => process.kill(pid, signal), alive = pidExists, readState = readWorkspaceState, verify = verifyCollectorOwnership, waitMs = COLLECTOR_EXIT_WAIT_MS, note = (_line) => {} }) {
1513
+ async function stopPreviousCollector({ root, slot = "default", kill = (pid, signal) => signalProcessTree(pid, signal), alive = pidExists, readState = readWorkspaceState, verify = verifyCollectorOwnership, waitMs = COLLECTOR_EXIT_WAIT_MS, note = (_line) => {} }) {
1514
1514
  const previous = (readState(root)?.collectors)?.[deviceSlotKey("ios", slot)] || null;
1515
1515
  const previousPid = Number(previous?.pid) || null;
1516
1516
  let killed = null;
@@ -2138,14 +2138,11 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
2138
2138
  event: "launch",
2139
2139
  msg: release ? `launched ${bundleId} on ${udid} (${configuration}, embedded JS bundle, no Metro)` : `launched ${bundleId} on ${udid} against Metro ${lanOriginUrl ?? `port ${metroPort}`}` + (launched?.mode === "openurl" || launched?.mode === "payload-url" ? " (expo-dev-client)" : "")
2140
2140
  });
2141
- if (remoteDevice) await d.replaceCollector({
2142
- root,
2143
- slot,
2144
- udid,
2145
- bundleId,
2146
- appName,
2147
- appExecutable,
2148
- note
2141
+ if (remoteDevice) logWriter().write({
2142
+ src: "build",
2143
+ level: "info",
2144
+ event: "collector_skipped",
2145
+ msg: `remote session ${udid}: device logs come from agent-device/EAS, so no simctl log stream is attached`
2149
2146
  });
2150
2147
  if (physical) raiseLeaseFor(release ? RELEASE_VERIFY_WAIT_MS : DEBUG_VERIFY_STEP_MS, false);
2151
2148
  const { state: launchState, warning: launchWarning } = await verifyIosRun({
@@ -2272,7 +2269,7 @@ function explicitSchemeRefusal(root, scheme, isExpo, d) {
2272
2269
  return d.resolveScheme(project, { scheme }).error ?? null;
2273
2270
  }
2274
2271
  function iosSlotLogFile(root, slot) {
2275
- return slot === "default" ? buildLogFile(root) : join(workspaceLogsDir(root), `build-${deviceSlotKey("ios", slot)}.ndjson`);
2272
+ return slot === "default" ? buildLogFile(root) : join(workspaceLogsDir(root), `build-${deviceSlotFileKey("ios", slot)}.ndjson`);
2276
2273
  }
2277
2274
  function iosSlotDeps(d, slot) {
2278
2275
  if (slot !== "default") {
@@ -1,5 +1,5 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { r as INSTALL_ERROR } from "./app-install--PAocWvq.mjs";
1
+ import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
+ import { r as INSTALL_ERROR } from "./app-install-DJg7bVv-.mjs";
3
3
  import { mkdtempSync, readFileSync, rmSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
@@ -1,4 +1,4 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
1
+ import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
2
  //#region src/collector/ios-device.ts
3
3
  const CONSOLE_ENV = { OS_ACTIVITY_DT_MODE: "enable" };
4
4
  const DEV_MENU_LAUNCH_ARGS = [
@@ -1,5 +1,5 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-C7IlIzd7.mjs";
1
+ import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
+ import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Dl5uKE03.mjs";
3
3
  import { mkdtempSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
@@ -22,7 +22,7 @@ function hostMemoryPressureAdvice(pressure) {
22
22
  return `macOS reports ${pressure} host memory pressure. Simulator processes can stall even when the device is Booted. Free memory before retrying: use \`stim stop\` only in workspaces you own, and ask before closing other apps or devices. For parallel iOS work, consider a reviewed SimSlim profile (\`stim guide lifecycle simslim\`). This observation does not establish an OOM crash.`;
23
23
  }
24
24
  //#endregion
25
- //#region src/sim/ios.ts
25
+ //#region src/devices/ios.ts
26
26
  function parseSimctlList(jsonOutput, { includeUnavailable = false } = {}) {
27
27
  const parsed = JSON.parse(jsonOutput);
28
28
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("Expected simctl list to return a JSON object with a devices object.");
@@ -1,10 +1,10 @@
1
- import { g as loadConfig, h as isPathPrefix, z as validateDeviceSlot } from "./config-DMP7L7H6.mjs";
1
+ import { B as validateDeviceSlot, g as loadConfig, h as isPathPrefix } from "./config-B7iyKWkL.mjs";
2
2
  import { n as SOURCES, t as LEVELS } from "./ndjson-BZwI1NBv.mjs";
3
- import { _ as workspaceLogsDir, r as readWorkspaceState } from "./workspace-state-WVwWw_hB.mjs";
4
- import { a as findProjectRoot } from "./project-B_SDt4ip.mjs";
5
- import { _ as launchErrorPreview } from "./command-output-BuIb_ZOI.mjs";
6
- import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-CTePWAzW.mjs";
7
- import { a as followLogs, c as queryLogs, i as fileSizes, n as buildCriteria, o as logFiles, r as compileGrep, s as parseSince } from "./logs-query-Cdp5aaCH.mjs";
3
+ import { _ as workspaceLogsDir, r as readWorkspaceState } from "./workspace-state-BT5E3SA2.mjs";
4
+ import { a as findProjectRoot } from "./project-DDZWf_16.mjs";
5
+ import { _ as launchErrorPreview } from "./command-output-B3ycpsYv.mjs";
6
+ import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-D1Kaf-eT.mjs";
7
+ import { a as followLogs, c as queryLogs, i as fileSizes, n as buildCriteria, o as logFiles, r as compileGrep, s as parseSince } from "./logs-query-BGL-Pl12.mjs";
8
8
  import { realpathSync } from "node:fs";
9
9
  import { relative, sep } from "node:path";
10
10
  import chalk from "chalk";
@@ -2,7 +2,7 @@ import { a as parseNdjsonLine, i as levelRank, o as parseNdjsonText } from "./nd
2
2
  import { closeSync, openSync, readFileSync, readSync, readdirSync, statSync } from "fs";
3
3
  import { join } from "path";
4
4
  import { StringDecoder } from "string_decoder";
5
- //#region src/logs-query.ts
5
+ //#region src/diagnostics/logs-query.ts
6
6
  const SINCE_UNITS = {
7
7
  s: 1e3,
8
8
  m: 6e4,
@@ -0,0 +1,233 @@
1
+ import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
+ import { A as isOnMountedVolume, g as loadConfig, j as listMountedVolumes, n as allMetroPorts, r as claimMetroPort, v as removeProject, w as process_identity_exports } from "./config-B7iyKWkL.mjs";
3
+ import { r as readWorkspaceState } from "./workspace-state-BT5E3SA2.mjs";
4
+ import { existsSync, readlinkSync, realpathSync } from "fs";
5
+ import { sep } from "path";
6
+ import { request } from "http";
7
+ import { connect } from "net";
8
+ //#region src/ports.ts
9
+ function isMetroRunning(port) {
10
+ return new Promise((resolve) => {
11
+ const req = request({
12
+ hostname: "localhost",
13
+ port,
14
+ path: "/status",
15
+ timeout: 2e3
16
+ }, (res) => {
17
+ let data = "";
18
+ res.on("data", (chunk) => {
19
+ data += chunk;
20
+ });
21
+ res.on("end", () => resolve(data.includes("packager-status:running")));
22
+ });
23
+ req.on("error", () => resolve(false));
24
+ req.on("timeout", () => {
25
+ req.destroy();
26
+ resolve(false);
27
+ });
28
+ req.end();
29
+ });
30
+ }
31
+ function isPortFree(port, { timeoutMs = 400 } = {}) {
32
+ return new Promise((resolve) => {
33
+ const sock = connect({
34
+ port,
35
+ host: "127.0.0.1"
36
+ });
37
+ const done = (free) => {
38
+ sock.removeAllListeners();
39
+ sock.destroy();
40
+ resolve(free);
41
+ };
42
+ sock.setTimeout(timeoutMs);
43
+ sock.once("connect", () => done(false));
44
+ sock.once("error", () => done(true));
45
+ sock.once("timeout", () => done(false));
46
+ });
47
+ }
48
+ const FIRST_PORT = 8082;
49
+ async function computeNextPort(isFree = isPortFree) {
50
+ const taken = new Set(allMetroPorts());
51
+ for (let port = FIRST_PORT; port < 8282; port++) {
52
+ if (taken.has(port)) continue;
53
+ if (await isFree(port)) return port;
54
+ }
55
+ throw new Error(`Found no free Metro port between ${FIRST_PORT} and 8281. Free one up, or stop a stale bundler (\`stim status\`, \`stim stop <port>\`).`);
56
+ }
57
+ async function findReclaimablePort(excludeProjectPath, probe = isMetroRunning, { isMounted = isOnMountedVolume, mountedVolumes } = {}) {
58
+ const cfg = loadConfig();
59
+ if (!cfg?.projects) return null;
60
+ const mounted = mountedVolumes || listMountedVolumes();
61
+ const candidates = [];
62
+ for (const [path, proj] of Object.entries(cfg.projects)) {
63
+ if (path === excludeProjectPath) continue;
64
+ if (Object.keys(proj.ports ?? {}).length) continue;
65
+ if (existsSync(path)) continue;
66
+ if (!isMounted(path, mounted)) continue;
67
+ if (typeof proj.metroPort === "number") candidates.push({
68
+ port: proj.metroPort,
69
+ ownerPath: path
70
+ });
71
+ }
72
+ for (const c of candidates) if (!await probe(c.port)) return c;
73
+ return null;
74
+ }
75
+ async function allocatePort(projectPath, probe = isMetroRunning, isFree = isPortFree) {
76
+ const reclaim = await findReclaimablePort(projectPath, probe);
77
+ if (reclaim && await isFree(reclaim.port)) {
78
+ removeProject(reclaim.ownerPath);
79
+ return reclaim.port;
80
+ }
81
+ return computeNextPort(isFree);
82
+ }
83
+ const RESERVE_ATTEMPTS = 5;
84
+ async function reserveMetroPort(projectPath, probe = isMetroRunning, isFree = isPortFree) {
85
+ for (let attempt = 0; attempt < RESERVE_ATTEMPTS; attempt++) {
86
+ const port = await allocatePort(projectPath, probe, isFree);
87
+ const claimed = claimMetroPort(projectPath, port);
88
+ if (claimed !== null) return claimed;
89
+ }
90
+ throw new Error(`Could not reserve a Metro port after ${RESERVE_ATTEMPTS} attempts: another Stim run claimed each one first. Retry.`);
91
+ }
92
+ //#endregion
93
+ //#region src/metro.ts
94
+ /**
95
+ * True when some process with this pid exists right now. It cannot tell a recycled pid from the
96
+ * process that first held it, so it is only valid for a pid this run spawned and still holds a
97
+ * handle on. For a pid read from a record on disk, decide with `inspectProcessIdentity` on that
98
+ * record's process token, or take an ownership claim (`ownership-claim.ts`): this answer would keep
99
+ * a dead holder's record looking live until some unrelated process exits.
100
+ */
101
+ function pidExists(pid) {
102
+ if (!Number.isSafeInteger(pid) || pid <= 0) return false;
103
+ try {
104
+ process.kill(pid, 0);
105
+ return true;
106
+ } catch (error) {
107
+ return error.code !== "ESRCH";
108
+ }
109
+ }
110
+ function parseLsofPids(out) {
111
+ if (!out) return [];
112
+ return String(out).split("\n").map((s) => parseInt(s.trim(), 10)).filter((n) => Number.isFinite(n));
113
+ }
114
+ function addressPort(address) {
115
+ const colon = String(address ?? "").lastIndexOf(":");
116
+ return colon < 0 ? NaN : Number(String(address).slice(colon + 1));
117
+ }
118
+ function parseNetstatPids(out, port) {
119
+ const pids = [];
120
+ for (const line of String(out ?? "").split("\n")) {
121
+ const cols = line.trim().split(/\s+/);
122
+ if (cols.length < 5 || cols[0]?.toUpperCase() !== "TCP") continue;
123
+ if (addressPort(cols[1]) !== port || addressPort(cols[2]) !== 0) continue;
124
+ const pid = Number(cols.at(-1));
125
+ if (Number.isSafeInteger(pid) && pid > 0 && !pids.includes(pid)) pids.push(pid);
126
+ }
127
+ return pids;
128
+ }
129
+ function listeningPids(port, platform = process.platform) {
130
+ const pids = parseLsofPids(getExecutor().runQuiet(`lsof -nP -iTCP:${port} -sTCP:LISTEN -t`));
131
+ if (pids.length > 0 || platform !== "win32") return pids;
132
+ return parseNetstatPids(getExecutor().runQuiet("netstat -ano"), port);
133
+ }
134
+ function parseLsofCwd(out) {
135
+ if (!out) return null;
136
+ const lines = String(out).split("\n");
137
+ const idx = lines.findIndex((l) => l === "fcwd");
138
+ if (idx === -1) return null;
139
+ const nLine = lines.slice(idx + 1).find((l) => l.startsWith("n"));
140
+ return nLine ? nLine.slice(1) : null;
141
+ }
142
+ function processCwd(pid) {
143
+ if (process.platform === "linux") try {
144
+ return readlinkSync(`/proc/${pid}/cwd`);
145
+ } catch {}
146
+ return parseLsofCwd(getExecutor().runQuiet(`lsof -a -p ${pid} -d cwd -Fn`));
147
+ }
148
+ function canonicalPath(path) {
149
+ try {
150
+ return realpathSync(path);
151
+ } catch {
152
+ return path;
153
+ }
154
+ }
155
+ function isInsideProject(cwd, projectPath) {
156
+ if (!cwd || !projectPath) return false;
157
+ const a = canonicalPath(cwd);
158
+ const b = canonicalPath(projectPath);
159
+ return a === b || a.startsWith(b.endsWith(sep) ? b : b + sep);
160
+ }
161
+ const NOT_OURS_UNRESPONSIVE = "unresponsive";
162
+ const NOT_OURS_UNREADABLE_CWD = "unreadable-cwd";
163
+ const NOT_OURS_FOREIGN_CWD = "foreign-cwd";
164
+ function ownedSupervisor(projectPath, port) {
165
+ const supervisor = readWorkspaceState(canonicalPath(projectPath))?.supervisor;
166
+ if (supervisor?.port !== port || (0, process_identity_exports.inspectProcessIdentity)(supervisor) !== "same") return null;
167
+ return supervisor;
168
+ }
169
+ async function resolveProjectMetro(port, projectPath, { probe = isMetroRunning, cwdOf = processCwd } = {}) {
170
+ const pid = listeningPids(port)[0];
171
+ if (pid === void 0) return { missing: true };
172
+ if (!await probe(port)) return {
173
+ notOurs: `pid ${pid} on port ${port} does not answer Metro's /status`,
174
+ kind: NOT_OURS_UNRESPONSIVE,
175
+ pid
176
+ };
177
+ const cwd = cwdOf(pid);
178
+ if (!cwd) {
179
+ const owner = ownedSupervisor(projectPath, port);
180
+ if (owner && (pid === owner.pid || pid === owner.serverPid)) return { metro: {
181
+ pid,
182
+ leader: owner.pid,
183
+ cwd: canonicalPath(projectPath),
184
+ processToken: owner.processToken
185
+ } };
186
+ return {
187
+ notOurs: `pid ${pid} on port ${port}: working directory could not be read`,
188
+ kind: NOT_OURS_UNREADABLE_CWD,
189
+ pid
190
+ };
191
+ }
192
+ if (!isInsideProject(cwd, projectPath)) return {
193
+ notOurs: `pid ${pid} on port ${port} runs from ${cwd}, outside ${projectPath}`,
194
+ kind: NOT_OURS_FOREIGN_CWD,
195
+ pid
196
+ };
197
+ const owner = ownedSupervisor(projectPath, port);
198
+ return { metro: {
199
+ pid,
200
+ leader: owner ? owner.pid : pid,
201
+ cwd,
202
+ ...owner ? { processToken: owner.processToken } : {}
203
+ } };
204
+ }
205
+ /**
206
+ * Terminate a process and everything it spawned. On POSIX `group` signals the process group, which
207
+ * is what a detached leader owns. Windows has no process groups, and its SIGTERM is
208
+ * TerminateProcess: no handler runs and no child is reached, so a spawned dev server or `adb
209
+ * logcat` would outlive its parent. taskkill /T is the only route to the tree there, and it is
210
+ * always forceful.
211
+ */
212
+ function signalProcessTree(pid, signal = "SIGTERM", { group = false, platform = process.platform } = {}) {
213
+ if (platform !== "win32") return process.kill(group ? -pid : pid, signal);
214
+ return getExecutor().runFileQuiet("taskkill", [
215
+ "/PID",
216
+ String(pid),
217
+ "/T",
218
+ "/F"
219
+ ]) !== null;
220
+ }
221
+ function killMetroTree(leader, processToken) {
222
+ if (!leader || leader === process.pid || (0, process_identity_exports.inspectProcessIdentity)({
223
+ pid: leader,
224
+ processToken
225
+ }) !== "same") return false;
226
+ try {
227
+ return signalProcessTree(leader, "SIGTERM", { group: true });
228
+ } catch {
229
+ return false;
230
+ }
231
+ }
232
+ //#endregion
233
+ export { resolveProjectMetro as a, isPortFree as c, pidExists as i, reserveMetroPort as l, killMetroTree as n, signalProcessTree as o, listeningPids as r, isMetroRunning as s, NOT_OURS_FOREIGN_CWD as t };