stim 1.14.0 → 1.16.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 (152) hide show
  1. package/README.md +2 -0
  2. package/dist/activity-BxwdD81S.mjs +609 -0
  3. package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
  4. package/dist/{android-d6rGq1pA.mjs → android-B_NumVyQ.mjs} +169 -81
  5. package/dist/{android-LbPFRWVL.mjs → android-Ift4kVvt.mjs} +517 -413
  6. package/dist/android-cas-8U4DPqeR.mjs +167 -0
  7. package/dist/android-cas-compiler.mjs +2 -2
  8. package/dist/{android-XpvCrV7q.mjs → android-r2rBL8kV.mjs} +2 -2
  9. package/dist/{app-install-BpgSgkJj.mjs → app-install-CKWdbXIX.mjs} +128 -151
  10. package/dist/attempt-Bk2ouQVL.mjs +39 -0
  11. package/dist/budget-DjrURfQn.mjs +2700 -0
  12. package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-DjeSb2Xl.mjs} +192 -653
  13. package/dist/build-slots-rUk77xqh.mjs +132 -0
  14. package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
  15. package/dist/cache-manifest.mjs +1 -1
  16. package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
  17. package/dist/cli-BkYZ_6hy.mjs +51 -0
  18. package/dist/cli.mjs +6 -42
  19. package/dist/client-BTY686R6.mjs +1368 -0
  20. package/dist/collector-run.mjs +9 -195
  21. package/dist/{command-output-DVcpw6eE.mjs → command-output-DnJMdEY-.mjs} +1 -1
  22. package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
  23. package/dist/{created-devices-CllH_8eb.mjs → created-devices-D5owjN4C.mjs} +2 -2
  24. package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-B6cMXZab.mjs} +2 -2
  25. package/dist/{deps-D9IcKqXS.mjs → deps-urbmfXig.mjs} +3 -3
  26. package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
  27. package/dist/dev-client-CroAIjjj.mjs +43 -0
  28. package/dist/{device-CjuuAJjQ.mjs → device-DZQjF7H5.mjs} +8 -7
  29. package/dist/device-capacity-BHwxNBlu.mjs +136 -0
  30. package/dist/device-host-worker.d.mts +1 -0
  31. package/dist/device-host-worker.mjs +964 -0
  32. package/dist/device-ios-BadmeL2V.mjs +381 -0
  33. package/dist/{device-lease-5c3WrVRm.mjs → device-lease-D-hLqx1b.mjs} +43 -36
  34. package/dist/{device-pool-BiyDyPP-.mjs → device-lease-run-CqjShqWW.mjs} +6 -126
  35. package/dist/device-pool-CUaEWMMm.mjs +125 -0
  36. package/dist/{device-remote-D87zEzaq.mjs → device-remote-D5WVWqOq.mjs} +69 -163
  37. package/dist/{doctor-x9pnU9fq.mjs → doctor-Cimf-gGB.mjs} +241 -103
  38. package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-B9L4Ae0x.mjs} +15 -18
  39. package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
  40. package/dist/{gc-ko6mUAYD.mjs → gc-Bqy4bjEu.mjs} +599 -108
  41. package/dist/{android-cas-iZK3DOCr.mjs → gradle-BsO1MdsH.mjs} +8 -294
  42. package/dist/{guide-6R0VNFqI.mjs → guide-B_uyKTxW.mjs} +2511 -239
  43. package/dist/{guide-status-BZrFPIA_.mjs → guide-status-CjEZMAD4.mjs} +3 -3
  44. package/dist/hosted-client-a-WmqE2L.mjs +197 -0
  45. package/dist/hosted-ios-CIWelIqf.mjs +220 -0
  46. package/dist/hosted-logs-D5g041Q3.mjs +187 -0
  47. package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
  48. package/dist/idle-DYVymG92.mjs +1099 -0
  49. package/dist/idle-shutdown-CTsDnAde.mjs +122 -0
  50. package/dist/in-use-DvBw0Kxo.mjs +130 -0
  51. package/dist/{ios-BwDPrnYQ.mjs → ios-BaInn3sW.mjs} +201 -10
  52. package/dist/ios-Cr1h5gnO.mjs +189 -0
  53. package/dist/{ios-P1f5nm2n.mjs → ios-aq3QoGI3.mjs} +1159 -633
  54. package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
  55. package/dist/{ios-device-DGglU3E_.mjs → ios-device-FfCN_C8p.mjs} +12 -7
  56. package/dist/ios-state-DS5UpYTH.mjs +48 -0
  57. package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-2DBRizjo.mjs} +5 -5
  58. package/dist/logs-CHEVK8zD.mjs +371 -0
  59. package/dist/machines-BIBUdhpj.mjs +265 -0
  60. package/dist/macos-CSYEAFmF.mjs +473 -0
  61. package/dist/macos-run.d.mts +5 -0
  62. package/dist/macos-run.mjs +2 -0
  63. package/dist/maintenance-run.d.mts +4 -0
  64. package/dist/maintenance-run.mjs +254 -0
  65. package/dist/{metro-CcEo8Fl8.mjs → metro-BeK1ZPz1.mjs} +20 -120
  66. package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
  67. package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Bk_YXEmE.mjs} +6 -59
  68. package/dist/native-run-NaBrRFtQ.mjs +147 -0
  69. package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-DBzRnEKS.mjs} +9 -5
  70. package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
  71. package/dist/offload-worker.d.mts +122 -0
  72. package/dist/offload-worker.mjs +606 -0
  73. package/dist/{ownership-DyYDF5ww.mjs → ownership-DiA16AsN.mjs} +3 -2
  74. package/dist/ownership-hCkPzGmr.mjs +571 -0
  75. package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
  76. package/dist/{ports-BguS83ex.mjs → ports-R2k4Knz3.mjs} +3 -3
  77. package/dist/prebuild-D2fsca_Z.mjs +213 -0
  78. package/dist/preview-DhVbvPAZ.mjs +318 -0
  79. package/dist/process-identity-BmRm1qV0.mjs +12 -0
  80. package/dist/{project-CWlvEaeN.mjs → project-Du91Ilvc.mjs} +74 -19
  81. package/dist/pull-request-pxnDCQzn.mjs +322 -0
  82. package/dist/pull-requests.d.mts +120 -0
  83. package/dist/pull-requests.mjs +2 -0
  84. package/dist/recordings-BIXOzVnr.mjs +37 -0
  85. package/dist/{reload-BiniwsSZ.mjs → reload-rM9jV1fz.mjs} +109 -24
  86. package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-C8-V13XN.mjs} +5 -5
  87. package/dist/run-f4vI6EnK.mjs +119 -0
  88. package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-CpPGmnmO.mjs} +33 -15
  89. package/dist/server-expo-CVqO2c35.mjs +2 -0
  90. package/dist/{server-expo-BVuwQ65v.mjs → server-expo-MC0rHlOC.mjs} +8 -12
  91. package/dist/settings-BBpMcDbD.mjs +25 -0
  92. package/dist/{settings-B-fsC-Om.mjs → settings-ZkDzkzvB.mjs} +22 -10
  93. package/dist/settings-oTSkkNrM.mjs +1439 -0
  94. package/dist/settings.schema.json +845 -97
  95. package/dist/{simslim-DrDN4WuV.mjs → simslim-B8-7AoTI.mjs} +8 -132
  96. package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-B8VrHxn1.mjs} +11 -8
  97. package/dist/{spawn-claims-Drw_o-Fb.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
  98. package/dist/spawn-entry-jw19m8Nl.mjs +17 -0
  99. package/dist/stage-CzaiwFsI.mjs +143 -0
  100. package/dist/start-CX8Ig-U2.mjs +2 -0
  101. package/dist/{start-s4RXp1wy.mjs → start-ClDfwXa4.mjs} +116 -65
  102. package/dist/state-3DwSQIBK.mjs +41 -0
  103. package/dist/state-B4PQFXMW.d.mts +12 -0
  104. package/dist/{state-Dk9RVXI7.mjs → state-C1jB7Q8q.mjs} +70 -8
  105. package/dist/{state-DHqvsbqC.mjs → state-DcW6vOd_.mjs} +7 -4
  106. package/dist/stats-CEZ_i5je.mjs +77 -0
  107. package/dist/{status-BgAuxESg.mjs → status-BuvBFeII.mjs} +118 -20
  108. package/dist/{status-BsPAnfOI.mjs → status-Cs7VLg9D.mjs} +542 -283
  109. package/dist/status-DlKvVJBE.mjs +49 -0
  110. package/dist/stdout-reader-C8yjre1x.mjs +23 -0
  111. package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
  112. package/dist/stim-footprint.swift +32 -0
  113. package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
  114. package/dist/stop-B0Yxtmiy.mjs +48 -0
  115. package/dist/{stop-BzreOWJp.mjs → stop-Bffozsac.mjs} +83 -16
  116. package/dist/{stop-BWIF_6BK.mjs → stop-Dt_dM16A.mjs} +2 -2
  117. package/dist/stop-cause-CidxIqup.mjs +97 -0
  118. package/dist/supervisor-run.d.mts +43 -11
  119. package/dist/supervisor-run.mjs +363 -24
  120. package/dist/support-CAP8qDUF.mjs +316 -0
  121. package/dist/support-D4aYJniO.mjs +201 -0
  122. package/dist/teardown-CL6vDs2Y.mjs +727 -0
  123. package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DkUflduf.mjs} +364 -14
  124. package/dist/trigger-Bemu8ytw.mjs +65 -0
  125. package/dist/trigger-CXts6qt1.mjs +2 -0
  126. package/dist/warm-progress-CpGv3Wb6.mjs +732 -0
  127. package/dist/{web-BsQs1WlO.mjs → web-DWUaMp8P.mjs} +37 -21
  128. package/dist/web-run.mjs +310 -13
  129. package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
  130. package/dist/workspace-state-B8-aJ1_5.mjs +371 -0
  131. package/dist/workspaces-BV4pdHFz.mjs +306 -0
  132. package/dist/{worktree-UrQWXSeb.mjs → worktree-K-xj9Db0.mjs} +625 -256
  133. package/dist/worktree-jnG8Iph5.mjs +2 -0
  134. package/package.json +10 -5
  135. package/shim/bundle-response.cjs +28 -12
  136. package/dist/activity-CYVDcrt3.mjs +0 -356
  137. package/dist/budget-CGDkhvv_.mjs +0 -1745
  138. package/dist/build-progress-DxKpwnaS.mjs +0 -247
  139. package/dist/idle-DXtx3RbX.mjs +0 -191
  140. package/dist/in-use-DlqBDKqY.mjs +0 -249
  141. package/dist/logs-Civ4gbBH.mjs +0 -486
  142. package/dist/merge-state-ytFQCeh4.mjs +0 -262
  143. package/dist/server-expo-DIHDhvGg.mjs +0 -2
  144. package/dist/settings-D61iNOsB.mjs +0 -629
  145. package/dist/start-Cetfvy8l.mjs +0 -2
  146. package/dist/stats-CFpFGlJV.mjs +0 -62
  147. package/dist/stats-CX1Z8Tca.mjs +0 -368
  148. package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
  149. package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
  150. package/dist/worktree-BVrHsYZG.mjs +0 -752
  151. package/dist/worktree-CHWWvm5v.mjs +0 -2
  152. package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
@@ -0,0 +1,49 @@
1
+ import { t as resolveMaintenanceSettings } from "./settings-BBpMcDbD.mjs";
2
+ import { maintenanceRunClaims, readMaintenanceState } from "@stim-cli/core/state";
3
+ import { claimRemoveCommand, readClaimSet } from "@stim-cli/core/ownership-claim";
4
+ //#region src/maintenance/status.ts
5
+ function maintenanceStatus() {
6
+ const state = readMaintenanceState();
7
+ const claims = readClaimSet(maintenanceRunClaims());
8
+ const holder = claims.live[0];
9
+ const unresolved = claims.unresolved[0];
10
+ const settings = resolveMaintenanceSettings();
11
+ return {
12
+ mode: settings.mode,
13
+ ...settings.invalid ? { invalid: settings.invalid } : {},
14
+ ...unresolved ? { claim: {
15
+ unresolved: `${unresolved.path}: ${unresolved.reason}`,
16
+ removeCommand: claimRemoveCommand(unresolved.path)
17
+ } } : {},
18
+ lastChecks: {
19
+ pressure: state?.lastAt.pressure ?? null,
20
+ size: state?.lastAt.size ?? null
21
+ },
22
+ pressure: state?.pressure ?? null,
23
+ sizes: state?.sizes ?? [],
24
+ lastPass: state?.lastPass ?? null,
25
+ running: holder ? {
26
+ startedAt: holder.startedAt,
27
+ trigger: String(holder.details.trigger ?? "command")
28
+ } : null,
29
+ recent: state?.recent ?? [],
30
+ plan: state?.plan ?? []
31
+ };
32
+ }
33
+ function maintenanceLine(status, includeLabel = true) {
34
+ if (status.mode === "off" && !status.lastPass && !status.running && !status.invalid && !status.claim) return null;
35
+ const at = status.lastPass ? new Date(status.lastPass.startedAt).toLocaleTimeString("en-GB", {
36
+ hour: "2-digit",
37
+ minute: "2-digit"
38
+ }) : null;
39
+ const outputs = status.plan.filter((action) => action.kind === "would-clear-outputs");
40
+ const summary = outputs.length ? `would clear build outputs of ${outputs.length} ${outputs.length === 1 ? "workspace" : "workspaces"} (${(outputs.reduce((sum, action) => sum + action.bytes, 0) / 1024 ** 3).toFixed(1)} GB)` : `${status.plan.length} planned actions`;
41
+ const disk = status.pressure?.disk.map((volume) => `disk ${(volume.freeMb / 1024).toFixed(1)} GB free on ${volume.volume}`).join(", ");
42
+ return `${includeLabel ? `Auto maintenance (${status.mode === "report" ? "report only" : "off"}): ` : ""}${at ? `${at}: ` : ""}${status.lastPass ? summary : "no pass has run yet"}${disk ? `; ${disk}` : ""}${status.running ? "; running" : ""}${status.lastPass?.blocked.length ? `; ${status.lastPass.blocked.join("; ")}` : ""}${status.invalid ? `; invalid: ${status.invalid}` : ""}${status.claim ? `; unresolved claim: ${status.claim.unresolved}; ${status.claim.removeCommand}` : ""}`;
43
+ }
44
+ function maintenanceLines(status) {
45
+ const line = maintenanceLine(status);
46
+ return line === null ? [] : [line];
47
+ }
48
+ //#endregion
49
+ export { maintenanceLines as n, maintenanceStatus as r, maintenanceLine as t };
@@ -0,0 +1,23 @@
1
+ import { fstatSync } from "node:fs";
2
+ //#region src/stdout-reader.ts
3
+ const READER_CHECK_MS = 2e3;
4
+ /**
5
+ * Calls `onGone` once the process reading stdout is gone, for a streaming command that can stay quiet for a long
6
+ * time. Returns a function that stops watching.
7
+ */
8
+ function watchStdoutReader(onGone) {
9
+ const parent = process.ppid;
10
+ const stdout = fstatSync(1);
11
+ const linuxPipe = process.platform === "linux" && (stdout.isFIFO() || stdout.isSocket());
12
+ const timer = setInterval(() => {
13
+ if (linuxPipe && process.ppid !== parent) onGone();
14
+ else process.stdout.write("");
15
+ }, READER_CHECK_MS);
16
+ process.stdout.on("error", onGone);
17
+ return () => {
18
+ clearInterval(timer);
19
+ process.stdout.off("error", onGone);
20
+ };
21
+ }
22
+ //#endregion
23
+ export { watchStdoutReader as t };
@@ -0,0 +1,63 @@
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ //#region src/devices/stim-desktop.ts
3
+ const LAUNCH_SERVICES_LOOKUP = `ObjC.import('AppKit'); const url = $.NSWorkspace.sharedWorkspace.URLForApplicationWithBundleIdentifier('dev.stim.desktop'); url.isNil() ? '' : url.path.js`;
4
+ const SCHEME_HANDLER_LOOKUP = `ObjC.import('AppKit'); const url = $.NSWorkspace.sharedWorkspace.URLForApplicationToOpenURL($.NSURL.URLWithString('stim-desktop://workspace')); url.isNil() ? '' : url.path.js`;
5
+ const STIM_DESKTOP_INSTALLED = "Stim Desktop installed";
6
+ /**
7
+ * macOS `open` passes its environment to an app it launches, so Stim Desktop
8
+ * started by a command run under a scoped `STIM_HOME` would serve that home.
9
+ */
10
+ const STIM_DESKTOP_OPEN_OPTIONS = {
11
+ timeoutMs: 5e3,
12
+ killSignal: "SIGKILL",
13
+ omitEnv: ["STIM_HOME"]
14
+ };
15
+ /** Stim Desktop sets `STIM_DESKTOP_APP` to its bundle path for every command it runs; a non-empty value means Desktop is installed. */
16
+ function stimDesktopInstalled(platform = process.platform, env = process.env) {
17
+ if (platform !== "darwin") return false;
18
+ if (env.STIM_DESKTOP_APP) return true;
19
+ return launchServicesFinds(LAUNCH_SERVICES_LOOKUP);
20
+ }
21
+ function launchServicesFinds(script) {
22
+ return Boolean(getExecutor().runFileQuiet("osascript", [
23
+ "-l",
24
+ "JavaScript",
25
+ "-e",
26
+ script
27
+ ], {
28
+ timeoutMs: 5e3,
29
+ killSignal: "SIGKILL"
30
+ }));
31
+ }
32
+ function stimDesktopHandlesLinks(platform = process.platform, env = process.env) {
33
+ if (platform !== "darwin") return false;
34
+ if (env.STIM_DESKTOP_APP) return true;
35
+ return launchServicesFinds(SCHEME_HANDLER_LOOKUP);
36
+ }
37
+ /**
38
+ * The links that open this workspace for a human, or undefined when no app on this Mac handles
39
+ * `stim-desktop://`. `target` names the device Stim Desktop focuses when the user opens the workspace.
40
+ */
41
+ function workspaceLinks(root, target, handlesLinks = stimDesktopHandlesLinks) {
42
+ if (!handlesLinks()) return void 0;
43
+ let desktop = `stim-desktop://workspace?path=${linkParam(root)}`;
44
+ if (target) desktop += `&platform=${target.platform}`;
45
+ if (target?.slot && target.slot !== "default") desktop += `&slot=${linkParam(target.slot)}`;
46
+ return { desktop };
47
+ }
48
+ const linkParam = (value) => encodeURIComponent(value).replaceAll("%2F", "/");
49
+ function workspaceLinkLine(links) {
50
+ return `Open in Stim Desktop: ${links.desktop}`;
51
+ }
52
+ function settingDefault(setting, desktopInstalled = stimDesktopInstalled) {
53
+ if (setting.desktopDefault !== void 0 && desktopInstalled()) return {
54
+ value: setting.desktopDefault,
55
+ reason: STIM_DESKTOP_INSTALLED
56
+ };
57
+ return {
58
+ value: setting.default,
59
+ reason: null
60
+ };
61
+ }
62
+ //#endregion
63
+ export { workspaceLinks as a, workspaceLinkLine as i, settingDefault as n, stimDesktopInstalled as r, STIM_DESKTOP_OPEN_OPTIONS as t };
@@ -0,0 +1,32 @@
1
+ // Prints "<pid> <bytes>" for each pid, the pid's physical footprint: the value Activity Monitor's Memory column and
2
+ // top's MEM column show. With no arguments it reads every pid proc_listallpids(3) returns.
3
+ //
4
+ // The footprint comes from proc_pid_rusage(pid, RUSAGE_INFO_V4, ...) in <libproc.h>, field ri_phys_footprint of
5
+ // struct rusage_info_v4 in <sys/resource.h> (macOS 10.14+). The call fails with EPERM for another user's process
6
+ // unless the caller is root, and with ESRCH for a pid that has exited; those pids are left out of the output.
7
+
8
+ import Darwin
9
+
10
+ func allPids() -> [pid_t] {
11
+ var capacity = Int(proc_listallpids(nil, 0)) + 64
12
+ while true {
13
+ var pids = [pid_t](repeating: 0, count: capacity)
14
+ let count = pids.withUnsafeMutableBytes { proc_listallpids($0.baseAddress, Int32($0.count)) }
15
+ if count < 0 { return [] }
16
+ if Int(count) < capacity { return Array(pids.prefix(Int(count))) }
17
+ capacity *= 2
18
+ }
19
+ }
20
+
21
+ let arguments = CommandLine.arguments.dropFirst()
22
+ let pids = arguments.isEmpty ? allPids() : arguments.compactMap { pid_t($0) }
23
+ var output = ""
24
+ output.reserveCapacity(pids.count * 20)
25
+ var info = rusage_info_v4()
26
+ for pid in pids where pid > 0 {
27
+ let status = withUnsafeMutablePointer(to: &info) {
28
+ $0.withMemoryRebound(to: rusage_info_t?.self, capacity: 1) { proc_pid_rusage(pid, RUSAGE_INFO_V4, $0) }
29
+ }
30
+ if status == 0 { output += "\(pid) \(info.ri_phys_footprint)\n" }
31
+ }
32
+ fputs(output, stdout)
@@ -1,4 +1,4 @@
1
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { accessSync, constants, realpathSync, statSync } from "node:fs";
3
3
  import { delimiter, join, resolve } from "node:path";
4
4
  //#region src/diagnostics/stim-installations.ts
@@ -0,0 +1,48 @@
1
+ import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
2
+ import { c as writeWorkspaceState, v as workspaceDir } from "./workspace-state-B8-aJ1_5.mjs";
3
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
4
+ import { a as requiredMacosRecord, i as macosRuntimeClaim } from "./state-3DwSQIBK.mjs";
5
+ import { n as stopHostedMacos } from "./hosted-macos-BSZyIb3y.mjs";
6
+ import { readMacosRecord } from "@stim-cli/core/state";
7
+ //#region src/macos/stop.ts
8
+ async function stopProcess(record) {
9
+ if (!record) return;
10
+ for (const signal of ["SIGTERM", "SIGKILL"]) {
11
+ const identity = (0, process_identity_exports.inspectProcessIdentity)(record);
12
+ if (identity === "gone" || identity === "different") return;
13
+ if (identity !== "same") throw Object.assign(/* @__PURE__ */ new Error(`Cannot verify macOS owner pid ${record.pid}; no signal was sent.`), { code: "STIM_MACOS_OWNER_UNVERIFIED" });
14
+ process.kill(record.pid, signal);
15
+ if (await (0, process_identity_exports.waitForProcessExit)(record, 5e3)) return;
16
+ }
17
+ throw new Error(`Owned macOS process ${record.pid} did not exit.`);
18
+ }
19
+ async function stopMacosAppHeld(root) {
20
+ const record = requiredMacosRecord(root);
21
+ if (!record) return false;
22
+ await stopProcess(record.supervisor);
23
+ await stopProcess(readMacosRecord(root)?.app ?? record.app);
24
+ const claims = (0, ownership_claim_exports.readClaimSet)(macosRuntimeClaim(root));
25
+ if (claims.unresolved.length || claims.live.length) throw Object.assign(/* @__PURE__ */ new Error("The macOS runtime claim cannot be released; inspect its recorded owner before cleanup."), { code: "STIM_MACOS_OWNER_UNVERIFIED" });
26
+ return true;
27
+ }
28
+ /** Stops the workspace's app here, or its session on the hosting Mac, which must confirm the session stopped. */
29
+ function stopMacosApp(root) {
30
+ const stop = async () => {
31
+ const record = requiredMacosRecord(root);
32
+ if (!record?.host) return stopMacosAppHeld(root);
33
+ await stopHostedMacos(root, record.host);
34
+ const { host: _host, hostLaunched: _launched, ...stopped } = record;
35
+ writeWorkspaceState(root, { macos: {
36
+ ...stopped,
37
+ supervisor: void 0
38
+ } });
39
+ return true;
40
+ };
41
+ return withWorkspaceProcessLock(workspaceDir(root), "macos-launch", stop, {
42
+ external: true,
43
+ waitMs: 0,
44
+ ownerPurpose: "stop macOS app"
45
+ });
46
+ }
47
+ //#endregion
48
+ export { stopMacosAppHeld as n, stopMacosApp as t };
@@ -1,23 +1,31 @@
1
- import { A as ownership_claim_exports, B as deviceSlotPlatforms, E as withConfigLock, G as validateDeviceSlot, H as parseDeviceSlotOption, O as process_identity_exports, T as upsertProject, U as projectDeviceSlots, V as parseDeviceSlotKey, o as clearSupervisor, p as getProject } from "./config-lvc7d3C2.mjs";
2
- import { a as pidExists, b as writeWorkspaceState, c as resolveProjectMetro, h as readWorkspaceState, k as workspaceDir, l as signalProcessTree, m as clearWorkspaceStateKeys, n as killMetroTree, v as withWorkspaceStateLock } from "./metro-CcEo8Fl8.mjs";
3
- import { i as findCommandWorkspace } from "./project-CWlvEaeN.mjs";
4
- import { a as clearRemoteSession, c as readMetroTunnel, i as clearManagedMetroTunnel, o as clearWorkspaceLaunches, s as clearWorkspaceSupervisor, u as readRemoteSession } from "./state-DHqvsbqC.mjs";
5
- import { a as withWorkspaceProcessLock } from "./named-ports-Blg8IxOh.mjs";
6
- import { f as refuseNoProject, l as phaseLine, p as releasedLeaseFact, u as plural } from "./command-output-DVcpw6eE.mjs";
7
- import { n as fileLeaseIo, u as releaseWorkspaceLeases } from "./device-lease-5c3WrVRm.mjs";
8
- import { C as teardownOwnedIosSim, S as teardownOwnedBrowser, a as logVanishedSupervisor, l as withdrawDevServerStopRequest, p as stopTunnel, s as requestDevServerStop, u as resolveSupervisorTarget, x as teardownOwnedAvd } from "./stop-cause-hjGBOZTG.mjs";
9
- import { n as verifyCollectorOwnership } from "./ownership-DyYDF5ww.mjs";
10
- import { f as resolveEasCliBin } from "./remote-cache-CcR6o6lI.mjs";
11
- import { _ as describeNativeRunHolder, b as requestNativeRunCancel, f as NATIVE_RUN_LOCK, g as decideStopAction, h as clearNativeRunCancel, i as endRecordedSession, p as NATIVE_RUN_WAIT_MS, v as nativeRunHolder, y as nativeRunWaitNotice } from "./device-remote-D87zEzaq.mjs";
1
+ import { B as parseDeviceSlotKey, D as withConfigLock$1, E as upsertProject, H as projectDeviceSlots, V as parseDeviceSlotOption, W as validateDeviceSlot, o as clearSupervisor, p as getProject, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
2
+ import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
3
+ import { f as refuseNoProject, l as phaseLine, p as releasedLeaseFact, u as plural } from "./command-output-DnJMdEY-.mjs";
4
+ import { a as findCommandWorkspace } from "./project-Du91Ilvc.mjs";
5
+ import { l as resolveProjectMetro, o as pidExists, r as killMetroTree, u as signalProcessTree } from "./metro-BeK1ZPz1.mjs";
6
+ import { c as writeWorkspaceState, n as clearWorkspaceStateKeys, o as withWorkspaceStateLock, r as readWorkspaceState$1, v as workspaceDir } from "./workspace-state-B8-aJ1_5.mjs";
7
+ import { n as fileLeaseIo, u as releaseWorkspaceLeases } from "./device-lease-D-hLqx1b.mjs";
8
+ import { a as clearRemoteSession, c as readMetroTunnel, i as clearManagedMetroTunnel, o as clearWorkspaceLaunches, s as clearWorkspaceSupervisor, u as readRemoteSession } from "./state-DcW6vOd_.mjs";
9
+ import { a as teardownOwnedBrowser, i as teardownOwnedAvd, o as teardownOwnedIosSim } from "./teardown-CL6vDs2Y.mjs";
10
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
11
+ import { i as stopTunnel, t as resolveSupervisorTarget } from "./ownership-hCkPzGmr.mjs";
12
+ import { a as logVanishedSupervisor, l as withdrawDevServerStopRequest, s as requestDevServerStop } from "./stop-cause-CidxIqup.mjs";
13
+ import { n as verifyCollectorOwnership } from "./ownership-DiA16AsN.mjs";
14
+ import { t as stopMacosApp } from "./stop-B0Yxtmiy.mjs";
15
+ import { f as resolveEasCliBin } from "./remote-cache-C8-V13XN.mjs";
16
+ import { i as endRecordedSession } from "./device-remote-D5WVWqOq.mjs";
17
+ import { a as decideStopAction, c as nativeRunWaitNotice, i as clearNativeRunCancel, l as requestNativeRunCancel, n as NATIVE_RUN_WAIT_MS, o as describeNativeRunHolder, s as nativeRunHolder, t as NATIVE_RUN_LOCK } from "./native-run-NaBrRFtQ.mjs";
18
+ import { r as stopHostedIos } from "./hosted-ios-CIWelIqf.mjs";
19
+ import { hostedIosRecords, parseHostedIosPlacement } from "@stim-cli/core/state";
12
20
  import chalk from "chalk";
13
21
  //#region src/commands/stop.ts
14
22
  const DEFAULT_WAIT_MS = 1e4;
15
23
  function readSupervisorState(root) {
16
- const sup = readWorkspaceState(root)?.supervisor;
24
+ const sup = readWorkspaceState$1(root)?.supervisor;
17
25
  return sup && typeof sup === "object" ? sup : null;
18
26
  }
19
27
  function readCollectorState(root) {
20
- const collectors = readWorkspaceState(root)?.collectors;
28
+ const collectors = readWorkspaceState$1(root)?.collectors;
21
29
  return collectors && typeof collectors === "object" ? collectors : {};
22
30
  }
23
31
  function clearSupervisorState(root, expected) {
@@ -29,7 +37,7 @@ function clearSupervisorState(root, expected) {
29
37
  }
30
38
  function clearCollectorState(root, expected) {
31
39
  return withWorkspaceStateLock(root, () => {
32
- const current = { ...readWorkspaceState(root)?.collectors };
40
+ const current = { ...readWorkspaceState$1(root)?.collectors };
33
41
  if (expected === void 0) {
34
42
  clearWorkspaceStateKeys(root, ["collectors"]);
35
43
  return true;
@@ -150,6 +158,8 @@ async function runStop(options) {
150
158
  },
151
159
  clearRegistration: async () => true,
152
160
  teardownBrowser: null,
161
+ stopMacos: false,
162
+ hostedSlot: slot,
153
163
  releaseLeases: (projectRoot) => releaseWorkspaceLeases(projectRoot, { slot })
154
164
  });
155
165
  result.outcomes.port = {
@@ -208,7 +218,52 @@ async function stopBrowserOnly({ root, project = void 0, teardownBrowser = (proj
208
218
  summary: summarize(root, outcomes, ok)
209
219
  };
210
220
  }
211
- async function stopWorkspace({ root, project = void 0, state = void 0, collectors = void 0, signalCollector = (pid) => signalProcessTree(pid), verifyCollector = verifyCollectorOwnership, clearCollectors = clearCollectorState, isAlive = pidExists, killGroup = killMetroTree, signalServer = (pid) => signalProcessTree(pid), inspectIdentity = process_identity_exports.inspectProcessIdentity, waitForDeath = void 0, waitMs = DEFAULT_WAIT_MS, resolveMetro = resolveProjectMetro, teardownIos = teardownOwnedIosSim, teardownAvd = teardownOwnedAvd, teardownBrowser = (projectRoot) => teardownOwnedBrowser(projectRoot), remoteDevice = void 0, teardownRemoteSession = defaultTeardownRemoteSession, metroTunnel = void 0, stopMetroTunnel = stopTunnel, releaseLeases = releaseWorkspaceLeases, freePort = defaultFreePort, clearRegistration = defaultClearRegistration, clearState = clearSupervisorState, report = (line) => console.error(line) }) {
221
+ async function stopWorkspaceHostedIos(root, slot, report, failed) {
222
+ const devices = {};
223
+ let anyFailed = false;
224
+ for (const [name, record] of Object.entries(hostedIosRecords(readWorkspaceState$1(root)))) {
225
+ if (slot !== void 0 && name !== slot) continue;
226
+ const placement = parseHostedIosPlacement(record);
227
+ const label = placement ? `${placement.device?.name ?? "iOS simulator"} on ${placement.machine}` : `hosted iOS simulator for slot ${name}`;
228
+ try {
229
+ await stopHostedIos(root, name);
230
+ devices[`ios:host:${name}`] = {
231
+ status: "shut-down",
232
+ label
233
+ };
234
+ report(chalk.dim(phaseLine("device", `stopped ${label}`)));
235
+ } catch (error) {
236
+ const reason = error.message;
237
+ devices[`ios:host:${name}`] = {
238
+ status: "failed",
239
+ label,
240
+ reason
241
+ };
242
+ report(chalk.red(phaseLine("device", reason)));
243
+ failed();
244
+ anyFailed = true;
245
+ }
246
+ }
247
+ return {
248
+ devices,
249
+ failed: anyFailed
250
+ };
251
+ }
252
+ async function stopWorkspaceMacos(root, failed, enabled) {
253
+ if (enabled === false || readWorkspaceState$1(root)?.macos === void 0) return void 0;
254
+ try {
255
+ await stopMacosApp(root);
256
+ return { status: "stopped" };
257
+ } catch (error) {
258
+ const reason = error instanceof Error ? error.message : String(error);
259
+ failed(reason);
260
+ return {
261
+ status: "failed",
262
+ reason
263
+ };
264
+ }
265
+ }
266
+ async function stopWorkspace({ root, project = void 0, state = void 0, collectors = void 0, signalCollector = (pid) => signalProcessTree(pid), verifyCollector = verifyCollectorOwnership, clearCollectors = clearCollectorState, isAlive = pidExists, killGroup = killMetroTree, signalServer = (pid) => signalProcessTree(pid), inspectIdentity = process_identity_exports.inspectProcessIdentity, waitForDeath = void 0, waitMs = DEFAULT_WAIT_MS, resolveMetro = resolveProjectMetro, teardownIos = teardownOwnedIosSim, teardownAvd = teardownOwnedAvd, teardownBrowser = (projectRoot) => teardownOwnedBrowser(projectRoot), stopMacos, hostedSlot, remoteDevice = void 0, teardownRemoteSession = defaultTeardownRemoteSession, metroTunnel = void 0, stopMetroTunnel = stopTunnel, releaseLeases = releaseWorkspaceLeases, freePort = defaultFreePort, clearRegistration = defaultClearRegistration, clearState = clearSupervisorState, report = (line) => console.error(line) }) {
212
267
  const proj = project === void 0 ? getProject(root) : project;
213
268
  const sup = state === void 0 ? readSupervisorState(root) : state;
214
269
  const collectorRecords = collectors === void 0 ? readCollectorState(root) : collectors;
@@ -365,6 +420,14 @@ async function stopWorkspace({ root, project = void 0, state = void 0, collector
365
420
  }
366
421
  outcomes.device.web = await closeBrowser(root, teardownBrowser, report);
367
422
  if (outcomes.device.web && outcomes.device.web.status !== "shut-down") ok = false;
423
+ outcomes.macos = await stopWorkspaceMacos(root, (reason) => {
424
+ ok = false;
425
+ report(reason);
426
+ }, stopMacos);
427
+ const hosted = await stopWorkspaceHostedIos(root, hostedSlot, report, () => {
428
+ ok = false;
429
+ });
430
+ Object.assign(outcomes.device, hosted.devices);
368
431
  const remote = remoteDevice === void 0 ? readRemoteSession(root) : remoteDevice;
369
432
  const sessionId = typeof remote?.sessionId === "string" ? remote.sessionId : null;
370
433
  if (sessionId) {
@@ -740,6 +803,8 @@ async function closeBrowser(root, teardownBrowser, report) {
740
803
  }
741
804
  function summarize(root, outcomes, ok) {
742
805
  const parts = [];
806
+ if (outcomes.macos?.status === "stopped") parts.push("macOS app stopped");
807
+ if (outcomes.macos?.status === "failed") parts.push("macOS app could not be stopped");
743
808
  if (outcomes.supervisor.status === "stopped") parts.push(`supervisor pid ${outcomes.supervisor.pid} stopped`);
744
809
  if (outcomes.supervisor.status === "already-stopped") parts.push("supervisor already stopped");
745
810
  if (outcomes.supervisor.status === "timeout") parts.push(`supervisor pid ${outcomes.supervisor.pid} still running`);
@@ -769,7 +834,7 @@ function summarize(root, outcomes, ok) {
769
834
  return `${ok ? "Stopped" : "Stopped with problems"}: ${what} (${root})`;
770
835
  }
771
836
  function defaultFreePort(root, port) {
772
- return withConfigLock(() => {
837
+ return withConfigLock$1(() => {
773
838
  const current = getProject(root);
774
839
  if (!current) return true;
775
840
  if (current.supervisor || current.metroPort !== port) return false;
@@ -810,10 +875,12 @@ function recordedDeviceSlots(root) {
810
875
  const parsed = parseDeviceSlotKey(key);
811
876
  if (parsed) slots.add(parsed.slot);
812
877
  }
878
+ for (const slot of Object.keys(hostedIosRecords(readWorkspaceState$1(root)))) slots.add(slot);
813
879
  return [...slots].toSorted();
814
880
  }
815
881
  function workspaceDeviceSlots(root) {
816
882
  const slots = projectDeviceSlots(getProject(root)).filter(({ platforms }) => Object.values(platforms).some(Boolean)).map(({ slot }) => slot);
883
+ for (const slot of Object.keys(hostedIosRecords(readWorkspaceState$1(root)))) if (!slots.includes(slot)) slots.push(slot);
817
884
  if (typeof readRemoteSession(root)?.sessionId === "string" && !slots.includes("default")) slots.push("default");
818
885
  return slots;
819
886
  }
@@ -1,3 +1,3 @@
1
- import { u as resolveSupervisorTarget } from "./stop-cause-hjGBOZTG.mjs";
2
- import { a as resolveCollectorTargets, c as stopWorkspaceNow, i as readSupervisorState, n as clearSupervisorState, o as runStop, r as readCollectorState, s as stopCommand, t as clearCollectorState } from "./stop-BzreOWJp.mjs";
1
+ import { t as resolveSupervisorTarget } from "./ownership-hCkPzGmr.mjs";
2
+ import { a as resolveCollectorTargets, c as stopWorkspaceNow, i as readSupervisorState, n as clearSupervisorState, o as runStop, r as readCollectorState, s as stopCommand, t as clearCollectorState } from "./stop-Bffozsac.mjs";
3
3
  export { clearCollectorState, clearSupervisorState, stopCommand as default, readCollectorState, readSupervisorState, resolveCollectorTargets, resolveSupervisorTarget, runStop, stopWorkspaceNow };
@@ -0,0 +1,97 @@
1
+ import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
2
+ import { c as writeWorkspaceState, t as clearWorkspaceStateKey, x as workspaceLogsDir$1 } from "./workspace-state-B8-aJ1_5.mjs";
3
+ import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.mjs";
4
+ import { join } from "node:path";
5
+ import { LOG_ROTATE_BYTES } from "@stim-cli/core";
6
+ import { DEV_SERVER_STOP_REQUEST_KEY, readDevServerStop, readDevServerStopRequest } from "@stim-cli/core/state";
7
+ //#region src/supervisor/stop-cause.ts
8
+ function requestDevServerStop(root, processToken, requester) {
9
+ if (!processToken) return;
10
+ try {
11
+ writeWorkspaceState(root, { [DEV_SERVER_STOP_REQUEST_KEY]: {
12
+ processToken,
13
+ by: requester.by,
14
+ pid: process.pid,
15
+ at: (/* @__PURE__ */ new Date()).toISOString(),
16
+ ...requester.workspace ? { workspace: requester.workspace } : {}
17
+ } });
18
+ } catch {}
19
+ }
20
+ function withdrawDevServerStopRequest(root, processToken) {
21
+ if (!processToken) return;
22
+ try {
23
+ clearWorkspaceStateKey(root, DEV_SERVER_STOP_REQUEST_KEY, (value) => {
24
+ return readDevServerStopRequest({ [DEV_SERVER_STOP_REQUEST_KEY]: value })?.processToken === processToken;
25
+ });
26
+ } catch {}
27
+ }
28
+ function devServerStopRecord(trigger, request, at) {
29
+ if (request) return {
30
+ reason: "requested",
31
+ at,
32
+ by: request.by,
33
+ byPid: request.pid,
34
+ ...request.workspace ? { byWorkspace: request.workspace } : {}
35
+ };
36
+ return trigger.kind === "signal" ? {
37
+ reason: "signal",
38
+ at,
39
+ signal: trigger.signal
40
+ } : {
41
+ reason: "server-exited",
42
+ at,
43
+ mode: trigger.mode,
44
+ code: trigger.code,
45
+ signal: trigger.signal
46
+ };
47
+ }
48
+ function describeDevServerStop(record) {
49
+ if (record.reason === "idle") return `idle for ${record.idleMinutes} minutes (metro.idleStopMinutes)`;
50
+ if (record.reason === "requested") return `stop requested by ${record.by} (pid ${record.byPid}${record.byWorkspace ? ` in ${record.byWorkspace}` : ""})`;
51
+ if (record.reason === "signal") return `received ${record.signal} with no Stim stop request recorded, so it likely came from outside Stim`;
52
+ const detail = record.signal ? `signal ${record.signal}` : `exit code ${record.code ?? "unknown"}`;
53
+ const expoSignal = record.mode === "expo-child" && record.code === 0 ? "; Expo CLI also exits 0 on SIGTERM, and no Stim stop request was recorded" : "";
54
+ return `the ${record.mode} dev server exited unexpectedly (${detail})${expoSignal}`;
55
+ }
56
+ function devServerStopLevel(record) {
57
+ if (record.reason === "server-exited") return "error";
58
+ return record.reason === "signal" ? "warn" : "info";
59
+ }
60
+ /** The log line for a recorded supervisor whose process is proven gone, or null while it may still run. */
61
+ function vanishedSupervisorMessage(record) {
62
+ if (!record || typeof record.pid !== "number") return null;
63
+ const identity = (0, process_identity_exports.inspectProcessIdentity)(record);
64
+ if (identity !== "gone" && identity !== "different") return null;
65
+ const started = typeof record.startedAt === "string" ? ` (started ${record.startedAt})` : "";
66
+ return `supervisor pid ${record.pid}${started} ${VANISHED}`;
67
+ }
68
+ const VANISHED = "is gone and recorded no cause: usually SIGKILL, a crash or a machine restart";
69
+ function logVanishedSupervisor(root, record) {
70
+ const msg = vanishedSupervisorMessage(record);
71
+ if (!msg) return;
72
+ const writer = createNdjsonWriter(join(workspaceLogsDir$1(root), "metro.ndjson"), { maxBytes: LOG_ROTATE_BYTES });
73
+ writer.write({
74
+ src: "metro",
75
+ level: "warn",
76
+ event: "supervisor_vanished",
77
+ msg
78
+ });
79
+ writer.close();
80
+ }
81
+ /** The recorded stop, else a vanished supervisor when status proved the recorded one gone. */
82
+ function metroLastStop(state, supervisor) {
83
+ const recorded = readDevServerStop(state);
84
+ if (recorded) return recorded;
85
+ if (supervisor?.status !== "stale" || typeof supervisor.pid !== "number") return null;
86
+ const startedAt = typeof supervisor.startedAt === "string" ? supervisor.startedAt : null;
87
+ return {
88
+ reason: "vanished",
89
+ pid: supervisor.pid,
90
+ startedAt
91
+ };
92
+ }
93
+ function describeMetroLastStop(stop) {
94
+ return stop.reason === "vanished" ? `supervisor pid ${stop.pid} ${VANISHED}` : describeDevServerStop(stop);
95
+ }
96
+ //#endregion
97
+ export { logVanishedSupervisor as a, vanishedSupervisorMessage as c, devServerStopRecord as i, withdrawDevServerStopRequest as l, describeMetroLastStop as n, metroLastStop as o, devServerStopLevel as r, requestDevServerStop as s, describeDevServerStop as t };
@@ -1,19 +1,43 @@
1
1
  import { t as NdjsonWriter } from "./ndjson-Du3j0AQ9.mjs";
2
- import { DevServerStopRecord } from "@stim-cli/core/state";
2
+ import { a as readPidFile, i as clearWorkspaceSupervisor, n as MODE_EXPO, o as writePidFile, r as clearExpoMetroTunnel, t as MODE_BARE } from "./state-B4PQFXMW.mjs";
3
+ import { DevServerStopRecord, DeviceActivity, IOS_SIMULATOR_APPS } from "@stim-cli/core/state";
3
4
  //#region src/supervisor/idle-stop.d.ts
4
5
  interface IdleProbe {
5
6
  lastActivityAt(): number;
6
7
  blocker(): string | null;
7
8
  }
8
- import * as import__stim_cli_core_process_identity from "@stim-cli/core/process-identity";
9
9
  //#endregion
10
- //#region src/supervisor/state.d.ts
11
- declare const MODE_BARE = "bare-inproc";
12
- declare const MODE_EXPO = "expo-child";
13
- declare function clearWorkspaceSupervisor(root: string, expected?: process_identity_d_exports.ProcessRecord | null): boolean;
14
- declare function clearExpoMetroTunnel(root: string): void;
15
- declare function writePidFile(root: string, pid: number): string;
16
- declare function readPidFile(root: string): number | null;
10
+ //#region src/commands/gc/idle.d.ts
11
+ interface OwnedDeviceActivity {
12
+ kind: "ios" | "android";
13
+ id: string;
14
+ name: string;
15
+ project: string;
16
+ slot: string;
17
+ activity: DeviceActivity;
18
+ }
19
+ //#endregion
20
+ //#region src/devices/idle-shutdown.d.ts
21
+ interface DueDevice {
22
+ device: OwnedDeviceActivity;
23
+ idleForMs: number;
24
+ }
25
+ /** The workspace's booted owned simulators and emulators that have been idle for `idleMs` or more. */
26
+ declare function dueIdleDevices(root: string, idleMs: number, now?: number): DueDevice[];
27
+ interface IdleShutdownEvent {
28
+ level: "info" | "warn";
29
+ event: "device_idle_shutdown" | "device_idle_shutdown_failed";
30
+ msg: string;
31
+ }
32
+ /**
33
+ * Shuts down, through centralized teardown, each of the workspace's owned devices that is still idle for `idleMs`,
34
+ * and records why. The caller holds the workspace's native-run lock, so no `ios` or `android` run is booting or
35
+ * installing on them.
36
+ */
37
+ declare function shutDownIdleDevices(root: string, idleMs: number, log: (event: IdleShutdownEvent) => void, now?: number): number;
38
+ //#endregion
39
+ //#region src/watchman.d.ts
40
+ type WatchmanCommand = (args: string[], timeoutMs: number) => Promise<unknown>;
17
41
  //#endregion
18
42
  //#region src/supervisor/run.d.ts
19
43
  interface ParsedSupervisorArgs {
@@ -22,6 +46,7 @@ interface ParsedSupervisorArgs {
22
46
  tunnel?: boolean;
23
47
  resetCache?: boolean;
24
48
  idleStopMinutes?: number;
49
+ deviceIdleMinutes?: number;
25
50
  error?: string;
26
51
  }
27
52
  declare function parseArgs(argv: string[]): ParsedSupervisorArgs;
@@ -45,6 +70,10 @@ type ServerStarter = (opts: {
45
70
  resetCache?: boolean;
46
71
  onTunnelUrl?: ((url: string) => void) | null;
47
72
  }) => Promise<ServerHandle>;
73
+ interface DeviceIdleOps {
74
+ due: typeof dueIdleDevices;
75
+ shutDown: typeof shutDownIdleDevices;
76
+ }
48
77
  interface RunSupervisorOptions {
49
78
  root: string;
50
79
  port: number;
@@ -52,6 +81,8 @@ interface RunSupervisorOptions {
52
81
  resetCache?: boolean;
53
82
  idleStopMinutes?: number;
54
83
  idleProbe?: IdleProbe;
84
+ deviceIdleMinutes?: number;
85
+ deviceIdle?: DeviceIdleOps;
55
86
  isExpo?: (projectRoot: string) => boolean;
56
87
  startBare?: ServerStarter | null;
57
88
  startExpo?: ServerStarter | null;
@@ -59,8 +90,9 @@ interface RunSupervisorOptions {
59
90
  onExit?: (code: number) => void;
60
91
  attachSignals?: boolean;
61
92
  stderr?: (line: string) => void;
93
+ watchman?: WatchmanCommand;
62
94
  }
63
- declare function runSupervisor({ root, port, tunnel, resetCache, idleStopMinutes, idleProbe, isExpo, startBare, startExpo, now, onExit, attachSignals, stderr }: RunSupervisorOptions): Promise<{
95
+ declare function runSupervisor({ root, port, tunnel, resetCache, idleStopMinutes, idleProbe, deviceIdleMinutes, deviceIdle, isExpo, startBare, startExpo, now, onExit, attachSignals, stderr, watchman }: RunSupervisorOptions): Promise<{
64
96
  mode: string;
65
97
  server: ServerHandle | undefined;
66
98
  shutdown: (code: number, event: string, msg: string, stop?: DevServerStopRecord) => Promise<void>;
@@ -68,4 +100,4 @@ declare function runSupervisor({ root, port, tunnel, resetCache, idleStopMinutes
68
100
  } | null>;
69
101
  declare function main(argv?: string[]): Promise<void>;
70
102
  //#endregion
71
- export { MODE_BARE, MODE_EXPO, RunSupervisorOptions, ServerExitInfo, ServerHandle, clearExpoMetroTunnel, clearWorkspaceSupervisor, main, parseArgs, readPidFile, runSupervisor, writePidFile };
103
+ export { DeviceIdleOps, MODE_BARE, MODE_EXPO, RunSupervisorOptions, ServerExitInfo, ServerHandle, clearExpoMetroTunnel, clearWorkspaceSupervisor, main, parseArgs, readPidFile, runSupervisor, writePidFile };