stim 1.11.0 → 1.13.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 (82) hide show
  1. package/README.md +3 -1
  2. package/dist/{activity-BtG4Va9l.mjs → activity-DfVKZTEB.mjs} +44 -26
  3. package/dist/{android-BA2i_KAV.mjs → android-BHfAu2mf.mjs} +1 -1
  4. package/dist/{android-C1q4l523.mjs → android-CD8pr8Q2.mjs} +112 -54
  5. package/dist/{android-cas-DfJkwCXW.mjs → android-cas-B9Kv9aIy.mjs} +12 -10
  6. package/dist/{android-C4zG2Tuc.mjs → android-qx_31wJo.mjs} +414 -118
  7. package/dist/{app-install-DHut6JnV.mjs → app-install-CDFY2nUL.mjs} +1 -1
  8. package/dist/budget-E5o91NR1.mjs +1739 -0
  9. package/dist/{build-plan-CeAYgKAx.mjs → build-plan-BTa93o_r.mjs} +125 -107
  10. package/dist/{build-progress-CDwogz6R.mjs → build-progress-l7d-X-ZP.mjs} +76 -8
  11. package/dist/{cache-manifest-Pubfp8wV.mjs → cache-manifest-DWPF4RKJ.mjs} +1 -1
  12. package/dist/cache-manifest.mjs +1 -1
  13. package/dist/cdp-Dq_ypAlJ.mjs +115 -0
  14. package/dist/chrome-B5hWgQJS.mjs +61 -0
  15. package/dist/cli.mjs +17 -16
  16. package/dist/collector-run.mjs +15 -9
  17. package/dist/{command-output-D5z2_bcF.mjs → command-output-BIL7rlX7.mjs} +1 -1
  18. package/dist/{config-BEq0Qw_a.mjs → config-0jCcMM0E.mjs} +10 -1
  19. package/dist/created-devices-Cd558h31.mjs +37 -0
  20. package/dist/{dependency-state-r9UesA9a.mjs → dependency-state-DNN0yfx6.mjs} +1 -1
  21. package/dist/{deps-OF7CD5Dx.mjs → deps-2Wo81np9.mjs} +2 -2
  22. package/dist/{device-BwH2iI3O.mjs → device-Cf7vl-so.mjs} +7 -7
  23. package/dist/{device-lease-D83nqeMX.mjs → device-lease-CL9dZf73.mjs} +55 -6
  24. package/dist/{device-pool-DHj7cdPy.mjs → device-pool-MuoiyRcu.mjs} +4 -4
  25. package/dist/{device-remote-BQZSnnmE.mjs → device-remote-DT9ngZiD.mjs} +254 -579
  26. package/dist/{doctor-D8QdMRMf.mjs → doctor-D2iysNNH.mjs} +50 -20
  27. package/dist/{error-diagnostics-UYFSQwRy.mjs → error-diagnostics-BoiOhck8.mjs} +7 -7
  28. package/dist/{gc-COZteyiq.mjs → gc-eA-PRBX8.mjs} +1022 -131
  29. package/dist/{guide-NDvTmMi-.mjs → guide-BfZ0OmiP.mjs} +909 -132
  30. package/dist/{guide-status-CWvti2Lg.mjs → guide-status-BaMYu55M.mjs} +2 -2
  31. package/dist/{idle-NETbeNiQ.mjs → idle-DxLi5OkN.mjs} +43 -10
  32. package/dist/{in-use-DVhlSoyn.mjs → in-use-wYEaKQM9.mjs} +11 -7
  33. package/dist/{ios-CQLKpNfr.mjs → ios-CcfCeZ19.mjs} +103 -58
  34. package/dist/{ios-DArbmwer.mjs → ios-CmEzZiOj.mjs} +31 -22
  35. package/dist/{ios-device-CwDZo0X4.mjs → ios-device-BW-t_Lkv.mjs} +1 -1
  36. package/dist/{launch-verify-CuPDGXdr.mjs → launch-verify-CuJaBpEa.mjs} +33 -20
  37. package/dist/{logs-B44a1UeU.mjs → logs-DcSRbC2t.mjs} +12 -11
  38. package/dist/{merge-state-DDxS-XIZ.mjs → merge-state-ytFQCeh4.mjs} +38 -8
  39. package/dist/{metro-BWLOJ_Wr.mjs → metro-BPeY1YMh.mjs} +5 -5
  40. package/dist/{named-ports-Dc7neAlc.mjs → named-ports-n7tnJsI8.mjs} +78 -6
  41. package/dist/native-runtime-CiiFHt0e.mjs +74 -0
  42. package/dist/{ownership-D2xbLVoz.mjs → ownership-Bz2kETAS.mjs} +2 -2
  43. package/dist/{ownership-BiojdtAp.mjs → ownership-PUP4Dwai.mjs} +175 -217
  44. package/dist/page-BTu7I60K.mjs +29 -0
  45. package/dist/{ports-DwBaRFJs.mjs → ports-C5UutqpJ.mjs} +5 -5
  46. package/dist/{project-BXZzOABt.mjs → project-DGeclhvO.mjs} +55 -87
  47. package/dist/{reload-By9Dwsrt.mjs → reload-C-cV5yy_.mjs} +80 -21
  48. package/dist/remote-cache--aehr4Pr.mjs +526 -0
  49. package/dist/{server-bare-D_RK1RCj.mjs → server-bare-Bq8BpSIo.mjs} +1 -1
  50. package/dist/server-expo-BeYwP_Xy.mjs +2 -0
  51. package/dist/{server-expo-o6H5gcBc.mjs → server-expo-DUCg52-K.mjs} +2 -2
  52. package/dist/{settings-C0ugALSC.mjs → settings-ssVBToDR.mjs} +29 -17
  53. package/dist/{settings-B5oFZEri.mjs → settings-xB0Frkg6.mjs} +13 -4
  54. package/dist/settings.schema.json +74 -2
  55. package/dist/{simslim-CIR3IYJO.mjs → simslim-Bbg9UWrt.mjs} +13 -6
  56. package/dist/{slot-launch-rggO1Esw.mjs → slot-launch-BIWEW-wI.mjs} +4 -4
  57. package/dist/{spawn-claims-BJwXTMPt.mjs → spawn-claims-PT8aQpCa.mjs} +1 -1
  58. package/dist/{start-BSsf2tUs.mjs → start-BZhItkiL.mjs} +110 -18
  59. package/dist/{start-i8zc9VmS.mjs → start-Rw2GD3nV.mjs} +1 -1
  60. package/dist/{state-WGdIbi_d.mjs → state-DwAU2_-D.mjs} +15 -3
  61. package/dist/state-l1pYwOre.mjs +148 -0
  62. package/dist/{stats-DBYJ5bxL.mjs → stats-CaSwmsSZ.mjs} +4 -4
  63. package/dist/{stats-BAgJaLA4.mjs → stats-D8rUTPHd.mjs} +1 -1
  64. package/dist/{status-CitcBRsC.mjs → status-TJlL0HWo.mjs} +649 -116
  65. package/dist/{status-Q-WDEGCB.mjs → status-sszP4XKU.mjs} +130 -28
  66. package/dist/stim-desktop-CXt8-Z3J.mjs +40 -0
  67. package/dist/{stop-DIhrvu0B.mjs → stop-BZ2qMiBP.mjs} +50 -18
  68. package/dist/{stop-rst5mL14.mjs → stop-DskDKNTU.mjs} +2 -2
  69. package/dist/supervisor-run.mjs +11 -11
  70. package/dist/web-run.d.mts +22 -0
  71. package/dist/web-run.mjs +481 -0
  72. package/dist/web-sWXd5iek.mjs +394 -0
  73. package/dist/{worktree-DhxDTK44.mjs → worktree-3cBa-bQt.mjs} +1 -1
  74. package/dist/{worktree-KOjMX3zA.mjs → worktree-D4Do_XiQ.mjs} +229 -21
  75. package/dist/{worktree-BlizKDpZ.mjs → worktree-DeBQSVtR.mjs} +3 -1
  76. package/dist/{xcode-B5SPD--o.mjs → xcode-LM6A8cNZ.mjs} +7 -7
  77. package/package.json +5 -5
  78. package/dist/budget-De3P2rq_.mjs +0 -620
  79. package/dist/native-run-D7yB_AsB.mjs +0 -143
  80. package/dist/server-expo-B-CCUJZj.mjs +0 -2
  81. package/dist/workspace-process-lock-Cx74RSDZ.mjs +0 -57
  82. package/dist/workspaces-COD2xCz-.mjs +0 -1013
@@ -1,10 +1,12 @@
1
- import { H as projectDeviceSlots } from "./config-BEq0Qw_a.mjs";
2
- import { a as formatElapsed, o as formatLongDuration, r as clockTime, u as plural } from "./command-output-D5z2_bcF.mjs";
1
+ import { U as projectDeviceSlots, z as deviceSlotKey } from "./config-0jCcMM0E.mjs";
2
+ import { a as formatElapsed, o as formatLongDuration, r as clockTime, u as plural } from "./command-output-BIL7rlX7.mjs";
3
+ import { r as cdpEndpoint } from "./state-l1pYwOre.mjs";
3
4
  import { sep } from "path";
4
5
  //#region src/status.ts
5
6
  const IOS_SIM_MB = 1500;
6
7
  const ANDROID_EMULATOR_MB = 2500;
7
8
  const METRO_MB = 700;
9
+ const BROWSER_MB = 500;
8
10
  function remoteDeviceState(record, ledger, root) {
9
11
  if (!record) return null;
10
12
  const claim = ledger.claims.get(record.sessionId);
@@ -22,7 +24,27 @@ function poolLine({ platform, parked, max }) {
22
24
  const what = plural(parked, `parked ${platform === "ios" ? "iOS simulator" : "Android emulator"}`);
23
25
  return max > 0 ? `pool: ${what} (max ${max})` : `pool: ${what} (parking off; gc --delete removes them)`;
24
26
  }
25
- function environmentState(project, { simsByUdid = {}, metro = null, worktrees = [], simsAvailable = true, androidRuntime = null, androidRuntimes = {}, supervisor = null, logs = null, remote = null, idleStop = null } = {}) {
27
+ const RECENT_LAUNCH_MS = 18e5;
28
+ function avdExpected({ slot, serial, launches, leasedIds, running, now }) {
29
+ if (serial && leasedIds.has(serial)) return true;
30
+ const launch = launches[deviceSlotKey("android", slot)];
31
+ if (!launch) return false;
32
+ return running || now - Date.parse(launch.launchedAt) < 18e5;
33
+ }
34
+ function androidStatus(android, androidRuntime, androidDeviceProfile) {
35
+ if (!android) return null;
36
+ return {
37
+ name: android.avdName ?? android.serial,
38
+ owned: Boolean(android.owned),
39
+ physical: Boolean(android.serial && !android.avdName),
40
+ ...androidRuntime ? {
41
+ serial: androidRuntime.serial,
42
+ state: androidRuntime.state
43
+ } : {},
44
+ ...androidDeviceProfile ? { deviceProfile: androidDeviceProfile } : {}
45
+ };
46
+ }
47
+ function environmentState(project, { simsByUdid = {}, metro = null, worktrees = [], simsAvailable = true, androidRuntime = null, androidRuntimes = {}, androidDeviceProfile = null, androidDeviceProfiles = {}, supervisor = null, logs = null, remote = null, idleStop = null, launches = {}, leasedIds = /* @__PURE__ */ new Set(), now = Date.now(), slot = "default", workspaceRunning = false } = {}) {
26
48
  const ios = project.platforms?.ios;
27
49
  const android = project.platforms?.android;
28
50
  const sim = ios ? simsByUdid[ios.deviceUdid] : null;
@@ -34,20 +56,35 @@ function environmentState(project, { simsByUdid = {}, metro = null, worktrees =
34
56
  if (simBooted) memoryMb += IOS_SIM_MB;
35
57
  if (androidDetected) memoryMb += ANDROID_EMULATOR_MB;
36
58
  if (metroRunning) memoryMb += METRO_MB;
37
- const warnings = [];
38
- if (metro?.notOurs) warnings.push(`port ${project.metroPort}: ${metro.notOurs}`);
39
- if (ios && !sim && simsAvailable) warnings.push(`recorded sim ${ios.deviceUdid} no longer exists`);
40
- if (simBooted && project.metroPort && !metroRunning) warnings.push("simulator is booted with no Metro serving it");
59
+ const running = workspaceRunning || metroRunning || supervisor?.status === "ours";
60
+ const issues = [];
61
+ const add = (code, message, remedy, severity = "warning") => issues.push({
62
+ code,
63
+ severity,
64
+ message,
65
+ remedy,
66
+ workspace: project.__path,
67
+ ...slot === "default" ? {} : { slot }
68
+ });
69
+ const slotFlag = slot === "default" ? "" : ` --slot ${slot}`;
70
+ if (metro?.notOurs && slot === "default") add("port-not-ours", `port ${project.metroPort}: ${metro.notOurs}`, "stim guide errors teardown", "error");
71
+ if (ios && !sim && simsAvailable) add("sim-missing", `recorded sim ${ios.deviceUdid} no longer exists`, `stim ios${slotFlag}`);
72
+ if (simBooted && project.metroPort && !metroRunning) add("sim-without-metro", "simulator is booted with no Metro serving it", "stim start");
41
73
  if (androidRuntime && android?.avdName) {
42
74
  const recordedSerial = android.consolePort ? `emulator-${android.consolePort}` : android.serial;
43
- if (androidRuntime.serial && recordedSerial && androidRuntime.serial !== recordedSerial) warnings.push(`owned AVD ${android.avdName} changed serial (${recordedSerial} -> ${androidRuntime.serial}); rerun your \`stim android\` command in this workspace to restore Metro forwarding, then reopen agent-device on ${androidRuntime.serial}`);
44
- if (androidRuntime.state === "missing") warnings.push(`recorded AVD ${android.avdName} no longer exists`);
45
- if (androidRuntime.state === "not-detected") warnings.push(`owned AVD ${android.avdName} is not detected by adb; rerun your \`stim android\` command in this workspace to reconnect`);
46
- if (androidRuntime.error) warnings.push(`could not check owned AVD ${android.avdName}: ${androidRuntime.error}`);
75
+ const expected = avdExpected({
76
+ slot,
77
+ serial: recordedSerial,
78
+ launches,
79
+ leasedIds,
80
+ running,
81
+ now
82
+ });
83
+ for (const [code, message] of androidIssues(android, recordedSerial, androidRuntime, expected)) add(code, message, code === "avd-unchecked" ? "stim doctor" : `stim android${slotFlag}`);
47
84
  }
48
- if (supervisor && supervisor.alive === false) warnings.push(`stale supervisor record for ${project.__path}`);
85
+ if (supervisor?.status === "unverified") add("supervisor-unverified", `supervisor pid ${supervisor.pid} could not be verified${supervisor.reason ? `: ${supervisor.reason}` : ""}`, "stim guide errors teardown", "error");
49
86
  const slots = [];
50
- for (const { slot, platforms } of projectDeviceSlots(project).slice(1)) {
87
+ for (const { slot: name, platforms } of projectDeviceSlots(project).slice(1)) {
51
88
  const deviceState = environmentState({
52
89
  ...project,
53
90
  platforms,
@@ -56,45 +93,44 @@ function environmentState(project, { simsByUdid = {}, metro = null, worktrees =
56
93
  simsByUdid,
57
94
  simsAvailable,
58
95
  metro,
59
- androidRuntime: androidRuntimes[slot]
96
+ androidRuntime: androidRuntimes[name],
97
+ androidDeviceProfile: androidDeviceProfiles[name],
98
+ launches,
99
+ leasedIds,
100
+ now,
101
+ slot: name,
102
+ workspaceRunning: running
60
103
  });
61
104
  slots.push({
62
- slot,
105
+ slot: name,
63
106
  ios: deviceState.ios,
64
107
  android: deviceState.android
65
108
  });
66
109
  memoryMb += deviceState.memoryMb - (metroRunning ? METRO_MB : 0);
67
110
  live ||= deviceState.live;
68
- warnings.push(...deviceState.warnings.map((warning) => `${slot}: ${warning}`));
111
+ issues.push(...deviceState.issues);
69
112
  }
70
113
  return {
71
114
  path: project.__path,
72
115
  ...slots.length ? { slots } : {},
73
116
  live,
74
117
  memoryMb,
75
- warnings,
118
+ warnings: issues.map(issueText),
119
+ issues,
76
120
  ios: ios ? {
77
121
  name: sim?.name ?? null,
78
122
  udid: ios.deviceUdid,
79
123
  owned: Boolean(ios.owned),
80
124
  state: sim?.state ?? (simsAvailable ? "missing" : "unknown")
81
125
  } : null,
82
- android: android ? {
83
- name: android.avdName ?? android.serial,
84
- owned: Boolean(android.owned),
85
- physical: Boolean(android.serial && !android.avdName),
86
- ...androidRuntime ? {
87
- serial: androidRuntime.serial,
88
- state: androidRuntime.state
89
- } : {}
90
- } : null,
126
+ android: androidStatus(android, androidRuntime, androidDeviceProfile),
91
127
  metro: project.metroPort ? {
92
128
  port: project.metroPort,
93
129
  running: metroRunning,
94
130
  pid: metro?.metro?.pid ?? null,
95
131
  ...idleStop && !metroRunning ? { idleStop } : {}
96
132
  } : null,
97
- supervisor: supervisor ? {
133
+ supervisor: supervisor && supervisor.status !== "stale" ? {
98
134
  pid: supervisor.pid ?? null,
99
135
  mode: supervisor.mode ?? null,
100
136
  startedAt: supervisor.startedAt ?? null,
@@ -108,6 +144,72 @@ function environmentState(project, { simsByUdid = {}, metro = null, worktrees =
108
144
  remoteDevices: remote ? [remote] : []
109
145
  };
110
146
  }
147
+ /** Adds the workspace's owned Chrome to its status: the web entry, its memory, and an unverifiable browser. */
148
+ function withWebFacts(state, web) {
149
+ if (!web) return state;
150
+ const running = web.status === "running";
151
+ const issues = [...state.issues];
152
+ if (web.status === "orphaned") issues.push({
153
+ code: "browser-orphaned",
154
+ severity: "warning",
155
+ message: `the owned Chrome (pid ${web.record.chromeProcess?.pid}) runs without its supervisor, so its page logs are not captured`,
156
+ remedy: "stim stop",
157
+ workspace: state.path
158
+ });
159
+ if (web.status === "unverified") issues.push({
160
+ code: "browser-unverified",
161
+ severity: "error",
162
+ message: `browser supervisor pid ${web.record.pid} or its Chrome could not be verified`,
163
+ remedy: "stim guide errors teardown",
164
+ workspace: state.path
165
+ });
166
+ return {
167
+ ...state,
168
+ live: state.live || running || web.status === "orphaned",
169
+ memoryMb: state.memoryMb + (running || web.status === "orphaned" ? BROWSER_MB : 0),
170
+ warnings: issues.map(issueText),
171
+ issues,
172
+ web: webBrowserState(web)
173
+ };
174
+ }
175
+ function webBrowserState({ record, status }) {
176
+ const running = status === "running";
177
+ return {
178
+ browser: "chrome",
179
+ version: record.version ?? null,
180
+ running,
181
+ pid: running ? record.chromeProcess?.pid ?? null : null,
182
+ supervisorPid: running ? record.pid : null,
183
+ url: record.url,
184
+ headless: record.headless,
185
+ viewport: record.viewport,
186
+ profile: record.profile,
187
+ cdpEndpoint: running ? cdpEndpoint(record.cdpPort) : null
188
+ };
189
+ }
190
+ function androidIssues(android, recordedSerial, runtime, expected) {
191
+ const issues = [];
192
+ if (runtime.serial && recordedSerial && runtime.serial !== recordedSerial) issues.push(["avd-serial-changed", `owned AVD ${android.avdName} changed serial (${recordedSerial} -> ${runtime.serial}), so Metro forwarding is lost; rerun with the same build options, then reopen agent-device on ${runtime.serial}`]);
193
+ if (runtime.state === "missing") issues.push(["avd-missing", `recorded AVD ${android.avdName} no longer exists`]);
194
+ if (runtime.state === "not-detected" && expected) issues.push(["avd-not-detected", `owned AVD ${android.avdName} is not detected by adb`]);
195
+ if (runtime.error) issues.push(["avd-unchecked", `could not check owned AVD ${android.avdName}: ${runtime.error}`]);
196
+ return issues;
197
+ }
198
+ function issueText(issue) {
199
+ return `${issue.slot ? `${issue.slot}: ` : ""}${issue.message}; run \`${issue.remedy}\``;
200
+ }
201
+ /**
202
+ * Activity as status reports it: `lastActivityAt` rounded down to the minute, the precision its readers show, so a
203
+ * new log record within the same minute does not change the payload.
204
+ */
205
+ function statusActivity(activity) {
206
+ const at = Date.parse(activity.lastActivityAt ?? "");
207
+ if (!Number.isFinite(at)) return activity;
208
+ return {
209
+ ...activity,
210
+ lastActivityAt: (/* @__PURE__ */ new Date(at - at % 6e4)).toISOString()
211
+ };
212
+ }
111
213
  function activityLabel(activity, now) {
112
214
  if (!activity) return null;
113
215
  if (activity.state === "driven") {
@@ -227,4 +329,4 @@ function deviceLeaseLines(states, now) {
227
329
  return lines;
228
330
  }
229
331
  //#endregion
230
- export { diskLine as a, gitSummaryText as c, remoteDeviceLine as d, remoteDeviceState as f, deviceLeaseStates as i, parseDfFree as l, unprovisionedWorktrees as m, capacity as n, environmentState as o, tightVolumes as p, deviceLeaseLines as r, formatSpace as s, activityLabel as t, poolLine as u };
332
+ export { withWebFacts as _, deviceLeaseStates as a, formatSpace as c, poolLine as d, remoteDeviceLine as f, unprovisionedWorktrees as g, tightVolumes as h, deviceLeaseLines as i, gitSummaryText as l, statusActivity as m, activityLabel as n, diskLine as o, remoteDeviceState as p, capacity as r, environmentState as s, RECENT_LAUNCH_MS as t, parseDfFree as u };
@@ -0,0 +1,40 @@
1
+ import { t as getExecutor } from "./exec-DK8KaVHK.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 STIM_DESKTOP_INSTALLED = "Stim Desktop installed";
5
+ /**
6
+ * macOS `open` passes its environment to an app it launches, so Stim Desktop
7
+ * started by a command run under a scoped `STIM_HOME` would serve that home.
8
+ */
9
+ const STIM_DESKTOP_OPEN_OPTIONS = {
10
+ timeoutMs: 5e3,
11
+ killSignal: "SIGKILL",
12
+ omitEnv: ["STIM_HOME"]
13
+ };
14
+ /** Stim Desktop sets `STIM_DESKTOP_APP` to its bundle path for every command it runs; a non-empty value means Desktop is installed. */
15
+ function stimDesktopInstalled(platform = process.platform, env = process.env) {
16
+ if (platform !== "darwin") return false;
17
+ if (env.STIM_DESKTOP_APP) return true;
18
+ const found = getExecutor().runFileQuiet("osascript", [
19
+ "-l",
20
+ "JavaScript",
21
+ "-e",
22
+ LAUNCH_SERVICES_LOOKUP
23
+ ], {
24
+ timeoutMs: 5e3,
25
+ killSignal: "SIGKILL"
26
+ });
27
+ return Boolean(found);
28
+ }
29
+ function settingDefault(setting, desktopInstalled = stimDesktopInstalled) {
30
+ if (setting.desktopDefault !== void 0 && desktopInstalled()) return {
31
+ value: setting.desktopDefault,
32
+ reason: STIM_DESKTOP_INSTALLED
33
+ };
34
+ return {
35
+ value: setting.default,
36
+ reason: null
37
+ };
38
+ }
39
+ //#endregion
40
+ export { settingDefault as n, stimDesktopInstalled as r, STIM_DESKTOP_OPEN_OPTIONS as t };
@@ -1,14 +1,14 @@
1
- import { B as parseDeviceSlotKey, D as process_identity_exports, H as projectDeviceSlots, T as withConfigLock, V as parseDeviceSlotOption, W as validateDeviceSlot, a as clearSupervisor, f as getProject, k as ownership_claim_exports, w as upsertProject, z as deviceSlotPlatforms } from "./config-BEq0Qw_a.mjs";
2
- import { O as workspaceDir, _ as withWorkspaceStateLock, c as signalProcessTree, i as pidExists, m as readWorkspaceState, p as clearWorkspaceStateKeys, s as resolveProjectMetro, t as killMetroTree, y as writeWorkspaceState } from "./metro-BWLOJ_Wr.mjs";
3
- import { o as findProjectRoot } from "./project-BXZzOABt.mjs";
4
- import { a as clearRemoteSession, i as clearManagedMetroTunnel, l as readRemoteSession, o as clearWorkspaceSupervisor, s as readMetroTunnel } from "./state-WGdIbi_d.mjs";
5
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-Cx74RSDZ.mjs";
6
- import { f as refuseNoProject, l as phaseLine, p as releasedLeaseFact, u as plural } from "./command-output-D5z2_bcF.mjs";
7
- import { u as releaseWorkspaceLeases } from "./device-lease-D83nqeMX.mjs";
8
- import { d as teardownOwnedAvd, f as teardownOwnedIosSim, i as stopTunnel, t as resolveSupervisorTarget } from "./ownership-BiojdtAp.mjs";
9
- import { n as verifyCollectorOwnership } from "./ownership-D2xbLVoz.mjs";
10
- import { i as endRecordedSession, z as resolveEasCliBin } from "./device-remote-BQZSnnmE.mjs";
11
- 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-D7yB_AsB.mjs";
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-0jCcMM0E.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-BPeY1YMh.mjs";
3
+ import { i as findCommandWorkspace } from "./project-DGeclhvO.mjs";
4
+ import { a as clearRemoteSession, c as readMetroTunnel, i as clearManagedMetroTunnel, o as clearWorkspaceLaunches, s as clearWorkspaceSupervisor, u as readRemoteSession } from "./state-DwAU2_-D.mjs";
5
+ import { a as withWorkspaceProcessLock } from "./named-ports-n7tnJsI8.mjs";
6
+ import { f as refuseNoProject, l as phaseLine, p as releasedLeaseFact, u as plural } from "./command-output-BIL7rlX7.mjs";
7
+ import { u as releaseWorkspaceLeases } from "./device-lease-CL9dZf73.mjs";
8
+ import { f as teardownOwnedAvd, i as stopTunnel, m as teardownOwnedIosSim, p as teardownOwnedBrowser, t as resolveSupervisorTarget } from "./ownership-PUP4Dwai.mjs";
9
+ import { n as verifyCollectorOwnership } from "./ownership-Bz2kETAS.mjs";
10
+ import { f as resolveEasCliBin } from "./remote-cache--aehr4Pr.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-DT9ngZiD.mjs";
12
12
  import chalk from "chalk";
13
13
  //#region src/commands/stop.ts
14
14
  const DEFAULT_WAIT_MS = 1e4;
@@ -143,8 +143,12 @@ async function runStop(options) {
143
143
  clearCollectorState(projectRoot, expected);
144
144
  return !Object.keys(readCollectorState(projectRoot)).some((key) => parseDeviceSlotKey(key)?.slot === slot);
145
145
  }),
146
- clearState: () => true,
146
+ clearState: (projectRoot) => {
147
+ clearWorkspaceLaunches(projectRoot, slot);
148
+ return true;
149
+ },
147
150
  clearRegistration: async () => true,
151
+ teardownBrowser: null,
148
152
  releaseLeases: (projectRoot) => releaseWorkspaceLeases(projectRoot, { slot })
149
153
  });
150
154
  result.outcomes.port = {
@@ -159,7 +163,7 @@ async function runStop(options) {
159
163
  result.summary = summarize(root, result.outcomes, result.ok);
160
164
  return result;
161
165
  }
162
- 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, remoteDevice = void 0, teardownRemoteSession = defaultTeardownRemoteSession, metroTunnel = void 0, stopMetroTunnel = stopTunnel, releaseLeases = releaseWorkspaceLeases, freePort = defaultFreePort, clearRegistration = defaultClearRegistration, clearState = clearSupervisorState, report = (line) => console.error(line) }) {
166
+ 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) }) {
163
167
  const proj = project === void 0 ? getProject(root) : project;
164
168
  const sup = state === void 0 ? readSupervisorState(root) : state;
165
169
  const collectorRecords = collectors === void 0 ? readCollectorState(root) : collectors;
@@ -305,13 +309,15 @@ async function stopWorkspace({ root, project = void 0, state = void 0, collector
305
309
  };
306
310
  if (stillHolding) report(chalk.dim(phaseLine("device", "left alone (something is still running)")));
307
311
  else {
308
- outcomes.device = shutDownDevices(proj, {
312
+ outcomes.device = shutDownDevices(proj, root, {
309
313
  teardownIos,
310
314
  teardownAvd,
311
315
  report
312
316
  });
313
317
  if (Object.values(outcomes.device).some((device) => device?.status === "failed")) ok = false;
314
318
  }
319
+ outcomes.device.web = await closeBrowser(root, teardownBrowser, report);
320
+ if (outcomes.device.web && outcomes.device.web.status !== "shut-down") ok = false;
315
321
  const remote = remoteDevice === void 0 ? readRemoteSession(root) : remoteDevice;
316
322
  const sessionId = typeof remote?.sessionId === "string" ? remote.sessionId : null;
317
323
  if (sessionId) {
@@ -579,7 +585,7 @@ function occupiedSkipReason(reason, holders) {
579
585
  const named = (holders || []).filter(Boolean);
580
586
  return named.length ? `${reason} -- held by UI-test runner ${named.join(", ")}` : `${reason} -- ${OCCUPANCY_HINT}`;
581
587
  }
582
- function shutDownDevices(project, { teardownIos, teardownAvd, report }) {
588
+ function shutDownDevices(project, workspace, { teardownIos, teardownAvd, report }) {
583
589
  const device = {
584
590
  ios: null,
585
591
  android: null
@@ -601,7 +607,8 @@ function shutDownDevices(project, { teardownIos, teardownAvd, report }) {
601
607
  report(chalk.dim(phaseLine("device", `${iosUdid} is not Stim-owned, leaving it running`)));
602
608
  } else device[iosKey] = reportDevice(iosUdid, teardownIos(iosUdid, {
603
609
  del: false,
604
- label: iosName
610
+ label: iosName,
611
+ workspace
605
612
  }), report, "ios");
606
613
  }
607
614
  const android = platforms.android;
@@ -614,7 +621,10 @@ function shutDownDevices(project, { teardownIos, teardownAvd, report }) {
614
621
  reason: "Stim does not own this device"
615
622
  };
616
623
  report(chalk.dim(phaseLine("device", `${android.avdName} is not Stim-owned, leaving it running`)));
617
- } else device[androidKey] = reportDevice(android.avdName, teardownAvd(android.avdName, { del: false }), report, "android");
624
+ } else device[androidKey] = reportDevice(android.avdName, teardownAvd(android.avdName, {
625
+ del: false,
626
+ workspace
627
+ }), report, "android");
618
628
  }
619
629
  }
620
630
  return device;
@@ -657,6 +667,28 @@ function reportDevice(label, r, report, kind) {
657
667
  remedy
658
668
  };
659
669
  }
670
+ async function closeBrowser(root, teardownBrowser, report) {
671
+ if (!teardownBrowser) return null;
672
+ const r = await teardownBrowser(root);
673
+ if (r.status === "missing") return null;
674
+ if (r.status === "torn-down") {
675
+ report(chalk.green(phaseLine("device", `closed ${r.label}; its profile is kept`)));
676
+ return {
677
+ status: "shut-down",
678
+ label: "Chrome"
679
+ };
680
+ }
681
+ const remedy = "run `stim status`, then `stim guide errors teardown`";
682
+ report(chalk.red(phaseLine("device", `did not close the owned Chrome: ${r.reason}`)));
683
+ report(chalk.dim(phaseLine("", remedy)));
684
+ return {
685
+ status: r.status,
686
+ kind: r.kind ?? null,
687
+ label: "Chrome",
688
+ reason: r.reason,
689
+ remedy
690
+ };
691
+ }
660
692
  function summarize(root, outcomes, ok) {
661
693
  const parts = [];
662
694
  if (outcomes.supervisor.status === "stopped") parts.push(`supervisor pid ${outcomes.supervisor.pid} stopped`);
@@ -844,7 +876,7 @@ async function stopWorkspaceNow({ root, slot, report = (line) => console.error(l
844
876
  }
845
877
  function stopCommand(program) {
846
878
  program.command("stop").description("The inverse of `start`: halt this workspace's supervisor, shut the owned device down (never deleted), and free the reserved port. Non-destructive -- the device stays assigned, so coming back costs a boot. Acts on the current workspace.").option("--slot <name>", "Stop only this device slot, keeping the shared server running. Use 'default' for the workspace's default device.", parseDeviceSlotOption).option("--json", "print the per-step outcomes as JSON").action(async (opts) => {
847
- const root = findProjectRoot(process.cwd());
879
+ const root = findCommandWorkspace(process.cwd());
848
880
  if (!root) {
849
881
  refuseNoProject({ json: Boolean(opts.json) });
850
882
  return;
@@ -1,3 +1,3 @@
1
- import { t as resolveSupervisorTarget } from "./ownership-BiojdtAp.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-DIhrvu0B.mjs";
1
+ import { t as resolveSupervisorTarget } from "./ownership-PUP4Dwai.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-BZ2qMiBP.mjs";
3
3
  export { clearCollectorState, clearSupervisorState, stopCommand as default, readCollectorState, readSupervisorState, resolveCollectorTargets, resolveSupervisorTarget, runStop, stopWorkspaceNow };
@@ -1,16 +1,16 @@
1
- import { B as parseDeviceSlotKey, C as setSupervisor, D as process_identity_exports, H as projectDeviceSlots, a as clearSupervisor, f as getProject } from "./config-BEq0Qw_a.mjs";
1
+ import { O as process_identity_exports, U as projectDeviceSlots, V as parseDeviceSlotKey, o as clearSupervisor, p as getProject, w as setSupervisor } from "./config-0jCcMM0E.mjs";
2
2
  import { r as createNdjsonWriter } from "./ndjson-CDY9IkVC.mjs";
3
- import { _ as withWorkspaceStateLock, j as workspaceLogsDir, m as readWorkspaceState$1, p as clearWorkspaceStateKeys, y as writeWorkspaceState } from "./metro-BWLOJ_Wr.mjs";
4
- import { a as detectIsExpo } from "./project-BXZzOABt.mjs";
3
+ import { M as workspaceLogsDir, b as writeWorkspaceState, h as readWorkspaceState$1, m as clearWorkspaceStateKeys, v as withWorkspaceStateLock } from "./metro-BPeY1YMh.mjs";
4
+ import { r as detectIsExpo } from "./project-DGeclhvO.mjs";
5
5
  import { t as describeError } from "./errors-B6W7FkxQ.mjs";
6
6
  import { t as relaunchWithLogFile } from "./detached-entry-DJHoca49.mjs";
7
- import { c as readPidFile, f as writePidFile, n as MODE_EXPO, o as clearWorkspaceSupervisor, r as clearExpoMetroTunnel, t as MODE_BARE } from "./state-WGdIbi_d.mjs";
8
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-Cx74RSDZ.mjs";
9
- import { a as parseLease, r as leaseIsExpired, s as parseWorkspaceLeases, t as deviceLeasePath } from "./device-lease-D83nqeMX.mjs";
10
- import { n as createActivityReader } from "./activity-BtG4Va9l.mjs";
11
- import { I as ownedAvdSerialResolver } from "./android-C4zG2Tuc.mjs";
12
- import { s as workspaceBuildInProgress } from "./idle-NETbeNiQ.mjs";
13
- import { n as withIdleWorkspace } from "./in-use-DVhlSoyn.mjs";
7
+ import { l as readPidFile, n as MODE_EXPO, p as writePidFile, r as clearExpoMetroTunnel, s as clearWorkspaceSupervisor, t as MODE_BARE } from "./state-DwAU2_-D.mjs";
8
+ import { a as withWorkspaceProcessLock } from "./named-ports-n7tnJsI8.mjs";
9
+ import { a as parseLease, r as leaseIsExpired, s as parseWorkspaceLeases, t as deviceLeasePath } from "./device-lease-CL9dZf73.mjs";
10
+ import { n as createActivityReader } from "./activity-DfVKZTEB.mjs";
11
+ import { R as ownedAvdSerialResolver } from "./android-qx_31wJo.mjs";
12
+ import { s as workspaceBuildInProgress } from "./idle-DxLi5OkN.mjs";
13
+ import { n as withIdleWorkspace } from "./in-use-wYEaKQM9.mjs";
14
14
  import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
15
15
  import { LOG_ROTATE_BYTES } from "@stim-cli/core";
16
16
  import { IDLE_STOP_KEY } from "@stim-cli/core/state";
@@ -264,7 +264,7 @@ async function runSupervisor({ root, port, tunnel = false, resetCache = false, i
264
264
  return writer.write(entry);
265
265
  } });
266
266
  try {
267
- server = await (mode === "expo-child" ? startExpo || (await import("./server-expo-B-CCUJZj.mjs")).startExpoServer : startBare || (await import("./server-bare-D_RK1RCj.mjs")).startBareServer)({
267
+ server = await (mode === "expo-child" ? startExpo || (await import("./server-expo-BeYwP_Xy.mjs")).startExpoServer : startBare || (await import("./server-bare-Bq8BpSIo.mjs")).startBareServer)({
268
268
  root,
269
269
  port,
270
270
  logsDir,
@@ -0,0 +1,22 @@
1
+ import { WebViewport } from "@stim-cli/core/state";
2
+ //#region src/web/run.d.ts
3
+ interface WebSupervisorOptions {
4
+ root: string;
5
+ launchId: string | null;
6
+ chrome: string;
7
+ url: string;
8
+ port: number;
9
+ headless: boolean;
10
+ viewport: WebViewport;
11
+ ignoreCertificateErrors: boolean;
12
+ }
13
+ declare function parseArgs(argv: string[]): WebSupervisorOptions | {
14
+ error: string;
15
+ };
16
+ declare function runWebSupervisor(options: WebSupervisorOptions, { onExit, stderr }?: {
17
+ onExit?: (code: number) => void;
18
+ stderr?: (line: string) => void;
19
+ }): Promise<void>;
20
+ declare function main(argv?: string[]): Promise<void>;
21
+ //#endregion
22
+ export { WebSupervisorOptions, main, parseArgs, runWebSupervisor };