stim 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{android-_y4Ohi5z.mjs → android-787HhP5w.mjs} +119 -29
- package/dist/{android-BPGizezh.mjs → android-DBkeHwpm.mjs} +168 -95
- package/dist/{android-DEgo4oCo.mjs → android-DPov-fv2.mjs} +2 -2
- package/dist/{android-cas-eiExNLf7.mjs → android-cas-CITmIeWY.mjs} +9 -8
- package/dist/android-cas-compiler.mjs +1 -1
- package/dist/{app-install-Uf3a83Tv.mjs → app-install-CrJ6t_bv.mjs} +2 -2
- package/dist/budget-BP4jZZL4.mjs +620 -0
- package/dist/build-progress-BXx-ZBe4.mjs +512 -0
- package/dist/{cache-manifest-Dg7OnMrp.mjs → cache-manifest-Pubfp8wV.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +16 -16
- package/dist/collector-run.d.mts +5 -1
- package/dist/collector-run.mjs +7 -7
- package/dist/{command-output-gByVmIjy.mjs → command-output-D5z2_bcF.mjs} +1 -1
- package/dist/{config-Cp9lM3wX.mjs → config-BEq0Qw_a.mjs} +14 -131
- package/dist/{dependency-state-Bq5DcLGn.mjs → dependency-state-r9UesA9a.mjs} +2 -2
- package/dist/{deps-Cip5vUm1.mjs → deps-DP4eWqea.mjs} +3 -3
- package/dist/{detached-entry-KEu0_AXL.mjs → detached-entry-DJHoca49.mjs} +1 -1
- package/dist/{dev-client-HyqISM0h.mjs → dev-client-92U593KT.mjs} +137 -250
- package/dist/{device-D4U-xLty.mjs → device-DhTZe7L4.mjs} +7 -7
- package/dist/{build-lock-DPXPLgTc.mjs → device-lease-CbEaIk7b.mjs} +370 -12
- package/dist/{device-pool-CnTAz8X1.mjs → device-pool-Di8Q-9Ro.mjs} +4 -4
- package/dist/{device-remote-AQ5rQPqr.mjs → device-remote-Dy1jGj3U.mjs} +547 -468
- package/dist/{doctor-B_Ei3bZu.mjs → doctor-B-inAJmt.mjs} +58 -22
- package/dist/{error-diagnostics-Vfz83GCF.mjs → error-diagnostics-xxWKNO3-.mjs} +11 -12
- package/dist/{exec-jIAlgilk.mjs → exec-DK8KaVHK.mjs} +3 -2
- package/dist/{gc-Ckw8RQHi.mjs → gc-ChPFZMRC.mjs} +500 -1114
- package/dist/{guide-BiAbhs1J.mjs → guide-DwmaD_Wa.mjs} +882 -528
- package/dist/guide-status-CWvti2Lg.mjs +125 -0
- package/dist/idle-DpRLp5lj.mjs +157 -0
- package/dist/in-use-CjUluqIX.mjs +245 -0
- package/dist/{ios-BL2NFckr.mjs → ios-B2Fpqvlb.mjs} +25 -59
- package/dist/{ios-BzUJWk1K.mjs → ios-Dd-hYJBh.mjs} +130 -89
- package/dist/{ios-device-DPsSm0v_.mjs → ios-device-BiOsSVzd.mjs} +1 -1
- package/dist/{ios-device-D972i9wf.mjs → ios-device-CtRdEVjx.mjs} +3 -3
- package/dist/{launch-verify-Q_OtW1Rf.mjs → launch-verify-CQS_Kg-C.mjs} +5 -5
- package/dist/{logs-CI9NQSK7.mjs → logs-MARIy6m9.mjs} +9 -9
- package/dist/{metro-DrLHaMn2.mjs → metro-BWLOJ_Wr.mjs} +116 -10
- package/dist/{named-ports-CmLQSIRj.mjs → named-ports-DiTfSz6p.mjs} +4 -4
- package/dist/{ndjson-9uFNRwfe.mjs → ndjson-CDY9IkVC.mjs} +3 -56
- package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
- package/dist/{ownership-D15Op8vN.mjs → ownership-D2xbLVoz.mjs} +2 -3
- package/dist/ownership-DZjdx8Gq.mjs +1180 -0
- package/dist/{ports-BNKoWVWZ.mjs → ports-CLSSFOuz.mjs} +3 -3
- package/dist/{project-DS112DYB.mjs → project-BXZzOABt.mjs} +2 -2
- package/dist/{reload-ZNJUo1pl.mjs → reload-DoETJ3rl.mjs} +9 -10
- package/dist/{server-bare-DWkkcIxV.mjs → server-bare-DVbb7kXH.mjs} +1 -1
- package/dist/{server-expo-B_lctBR3.mjs → server-expo-BvOCeakp.mjs} +4 -4
- package/dist/server-expo-DBVTdult.mjs +2 -0
- package/dist/{settings-D030k0t4.mjs → settings-DOugPlPz.mjs} +16 -428
- package/dist/{settings-BKNh5rl4.mjs → settings-EWYNwe72.mjs} +16 -14
- package/dist/settings.schema.json +75 -0
- package/dist/{simslim-B_LsGLuJ.mjs → simslim-Ca3487H6.mjs} +182 -5
- package/dist/{slot-launch-DU9A-gYw.mjs → slot-launch-C5cg0fmk.mjs} +4 -4
- package/dist/{spawn-claims-Dn3C_N6w.mjs → spawn-claims-CUleC2cM.mjs} +1 -1
- package/dist/{start-DYkkKF0s.mjs → start-F9gv5dcC.mjs} +147 -98
- package/dist/start-VoFsPSes.mjs +2 -0
- package/dist/{state-NcJzMEBF.mjs → state-WGdIbi_d.mjs} +2 -2
- package/dist/{stats-DG9e66Rz.mjs → stats-BAgJaLA4.mjs} +1 -1
- package/dist/{stats-B63-JVAY.mjs → stats-DBYJ5bxL.mjs} +4 -4
- package/dist/{status-S6ehL4Ui.mjs → status-DLCD-o3s.mjs} +63 -231
- package/dist/status-Dr4fRkym.mjs +216 -0
- package/dist/{guide-status-BAMVU0ZA.mjs → stim-installations-CINXy_su.mjs} +3 -124
- package/dist/{stop-BO28HGqF.mjs → stop-Dj4pfmyv.mjs} +22 -18
- package/dist/{stop-T1R-cZRz.mjs → stop-ZvOeTmKb.mjs} +2 -2
- package/dist/supervisor-run.d.mts +11 -2
- package/dist/supervisor-run.mjs +185 -17
- package/dist/{workspace-process-lock-DWaiRD0e.mjs → workspace-process-lock-BXA96myD.mjs} +2 -2
- package/dist/workspaces-BTUyFfkv.mjs +1003 -0
- package/dist/{worktree-DKhSF0UX.mjs → worktree-4aZg2NnI.mjs} +1 -1
- package/dist/{worktree-LGB-UwgG.mjs → worktree-BMS7GeqB.mjs} +40 -122
- package/dist/{worktree-R_cUlE7s.mjs → worktree-BlizKDpZ.mjs} +143 -18
- package/dist/{xcode-BZqyxh3e.mjs → xcode-B2tBkf0m.mjs} +9 -158
- package/package.json +6 -5
- package/dist/build-progress-CVqkfPC6.mjs +0 -188
- package/dist/device-lease-Crs-HkJY.mjs +0 -366
- package/dist/logs-query-Cqma3WIu.mjs +0 -298
- package/dist/ownership-DCQPuWEc.mjs +0 -489
- package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
- package/dist/server-expo-CeILEu86.mjs +0 -2
- package/dist/sim-pool-KaIsp2Ft.mjs +0 -723
- package/dist/spawn-entry-nCE6m770.mjs +0 -14
- package/dist/workspace-state-DF2gCFhx.mjs +0 -169
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as appProjectProblem } from "./project-DS112DYB.mjs";
|
|
4
|
-
import { accessSync, constants, mkdirSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
2
|
+
import { accessSync, constants, realpathSync, statSync } from "node:fs";
|
|
5
3
|
import { delimiter, join, resolve } from "node:path";
|
|
6
4
|
//#region src/diagnostics/stim-installations.ts
|
|
7
5
|
const VERSION_OUTPUT_LIMIT = 4096;
|
|
@@ -151,123 +149,4 @@ async function inspectStimVersions(runningVersion, { pathValue = process.env.PAT
|
|
|
151
149
|
return analyzeStimVersions(runningVersion, canonicalPath(runningPath), installations);
|
|
152
150
|
}
|
|
153
151
|
//#endregion
|
|
154
|
-
|
|
155
|
-
const DAY_MS = 864e5;
|
|
156
|
-
const DOCTOR_STALE_MS = 7 * DAY_MS;
|
|
157
|
-
const UPDATE_CHECK_INTERVAL_MS = DAY_MS;
|
|
158
|
-
const UPDATE_CHECK_TIMEOUT_MS = 2e3;
|
|
159
|
-
const LATEST_URL = "https://registry.npmjs.org/stim/latest";
|
|
160
|
-
const PLATFORMS = ["ios", "android"];
|
|
161
|
-
function recordDoctorRun(projectRoot, platform, version, now = /* @__PURE__ */ new Date()) {
|
|
162
|
-
const run = {
|
|
163
|
-
at: now.toISOString(),
|
|
164
|
-
version
|
|
165
|
-
};
|
|
166
|
-
try {
|
|
167
|
-
if (appProjectProblem(projectRoot)) return;
|
|
168
|
-
upsertProject(projectRoot, (existing) => {
|
|
169
|
-
const doctorRuns = { ...existing.doctorRuns };
|
|
170
|
-
for (const target of platform ? [platform] : PLATFORMS) doctorRuns[target] = run;
|
|
171
|
-
return { doctorRuns };
|
|
172
|
-
});
|
|
173
|
-
} catch {}
|
|
174
|
-
}
|
|
175
|
-
function doctorDueReason(record, running, now) {
|
|
176
|
-
const at = record ? Date.parse(record.at) : NaN;
|
|
177
|
-
if (!record || Number.isNaN(at)) return "never run";
|
|
178
|
-
if (record.version !== running) return `last run with stim ${record.version}`;
|
|
179
|
-
const age = now.getTime() - at;
|
|
180
|
-
if (age > DOCTOR_STALE_MS) return `last run ${Math.floor(age / DAY_MS)} days ago`;
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
function updateCacheFile() {
|
|
184
|
-
return join(getConfigDir(), "update-check.json");
|
|
185
|
-
}
|
|
186
|
-
function readUpdateCache() {
|
|
187
|
-
try {
|
|
188
|
-
const parsed = JSON.parse(readFileSync(updateCacheFile(), "utf-8"));
|
|
189
|
-
if (typeof parsed?.checkedAt !== "string") return null;
|
|
190
|
-
return {
|
|
191
|
-
checkedAt: parsed.checkedAt,
|
|
192
|
-
latest: typeof parsed.latest === "string" ? parsed.latest : null
|
|
193
|
-
};
|
|
194
|
-
} catch {
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
function writeUpdateCache(cache) {
|
|
199
|
-
const target = updateCacheFile();
|
|
200
|
-
const tmp = `${target}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
201
|
-
try {
|
|
202
|
-
mkdirSync(getConfigDir(), { recursive: true });
|
|
203
|
-
writeFileSync(tmp, `${JSON.stringify(cache)}\n`);
|
|
204
|
-
renameSync(tmp, target);
|
|
205
|
-
} catch {
|
|
206
|
-
try {
|
|
207
|
-
unlinkSync(tmp);
|
|
208
|
-
} catch {}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
async function fetchLatest(fetchImpl) {
|
|
212
|
-
const res = await fetchImpl(LATEST_URL, { signal: AbortSignal.timeout(UPDATE_CHECK_TIMEOUT_MS) });
|
|
213
|
-
if (!res.ok) return null;
|
|
214
|
-
const body = await res.json();
|
|
215
|
-
return typeof body.version === "string" ? body.version : null;
|
|
216
|
-
}
|
|
217
|
-
async function checkForUpdate(running, { now = /* @__PURE__ */ new Date(), fetch: fetchImpl = fetch } = {}) {
|
|
218
|
-
if (process.env.STIM_NO_UPDATE_CHECK) return null;
|
|
219
|
-
const cached = readUpdateCache();
|
|
220
|
-
const checkedAt = cached ? Date.parse(cached.checkedAt) : NaN;
|
|
221
|
-
let latest = cached?.latest ?? null;
|
|
222
|
-
if (Number.isNaN(checkedAt) || now.getTime() - checkedAt > UPDATE_CHECK_INTERVAL_MS) {
|
|
223
|
-
try {
|
|
224
|
-
latest = await fetchLatest(fetchImpl);
|
|
225
|
-
} catch {
|
|
226
|
-
latest = null;
|
|
227
|
-
}
|
|
228
|
-
writeUpdateCache({
|
|
229
|
-
checkedAt: now.toISOString(),
|
|
230
|
-
latest
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
if (!latest) return null;
|
|
234
|
-
const comparison = compareStimVersions(latest, running);
|
|
235
|
-
return comparison !== null && comparison > 0 ? latest : null;
|
|
236
|
-
}
|
|
237
|
-
function renderGuideStatus({ running, doctor, latest }) {
|
|
238
|
-
const lines = [];
|
|
239
|
-
if (doctor.length) {
|
|
240
|
-
const named = doctor.map((due) => `${due.platform} (${due.reason})`).join(" and ");
|
|
241
|
-
const command = doctor.length === 1 ? `stim doctor --platform ${doctor[0]?.platform}` : "stim doctor";
|
|
242
|
-
lines.push(` Doctor is due for ${named}.`, ` Run before native work: ${command}`);
|
|
243
|
-
}
|
|
244
|
-
if (latest) lines.push(` stim ${latest} is available (running ${running}): npm install -g stim@latest`);
|
|
245
|
-
if (!lines.length) return null;
|
|
246
|
-
return ["STATUS", ...lines].join("\n");
|
|
247
|
-
}
|
|
248
|
-
async function guideStatus({ projectRoot, running, now = /* @__PURE__ */ new Date(), fetch: fetchImpl = fetch }) {
|
|
249
|
-
const doctor = [];
|
|
250
|
-
if (projectRoot && !appProjectProblem(projectRoot)) {
|
|
251
|
-
let runs = null;
|
|
252
|
-
try {
|
|
253
|
-
runs = getProject(projectRoot)?.doctorRuns ?? {};
|
|
254
|
-
} catch {}
|
|
255
|
-
if (runs) for (const platform of PLATFORMS) {
|
|
256
|
-
const reason = doctorDueReason(runs[platform], running, now);
|
|
257
|
-
if (reason) doctor.push({
|
|
258
|
-
platform,
|
|
259
|
-
reason
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return renderGuideStatus({
|
|
264
|
-
running,
|
|
265
|
-
doctor,
|
|
266
|
-
latest: await checkForUpdate(running, {
|
|
267
|
-
now,
|
|
268
|
-
fetch: fetchImpl
|
|
269
|
-
})
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
//#endregion
|
|
273
|
-
export { inspectStimVersions as i, recordDoctorRun as n, compareStimVersions as r, guideStatus as t };
|
|
152
|
+
export { inspectStimVersions as n, compareStimVersions as t };
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { o as findProjectRoot } from "./project-
|
|
4
|
-
import { a as clearRemoteSession, i as clearManagedMetroTunnel, l as readRemoteSession, o as clearWorkspaceSupervisor, s as readMetroTunnel } from "./state-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { E as teardownOwnedIosSim, T as teardownOwnedAvd, i as endRecordedSession } from "./device-remote-AQ5rQPqr.mjs";
|
|
12
|
-
import { i as stopTunnel, t as resolveSupervisorTarget } from "./ownership-DCQPuWEc.mjs";
|
|
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, 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-BXA96myD.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-CbEaIk7b.mjs";
|
|
8
|
+
import { d as teardownOwnedAvd, f as teardownOwnedIosSim, i as stopTunnel, t as resolveSupervisorTarget } from "./ownership-DZjdx8Gq.mjs";
|
|
9
|
+
import { n as verifyCollectorOwnership } from "./ownership-D2xbLVoz.mjs";
|
|
10
|
+
import { i as endRecordedSession, z as resolveEasCliBin } from "./device-remote-Dy1jGj3U.mjs";
|
|
13
11
|
import chalk from "chalk";
|
|
14
12
|
//#region src/commands/stop.ts
|
|
15
13
|
const DEFAULT_WAIT_MS = 1e4;
|
|
@@ -597,7 +595,7 @@ function shutDownDevices(project, { teardownIos, teardownAvd, report }) {
|
|
|
597
595
|
} else device[iosKey] = reportDevice(iosUdid, teardownIos(iosUdid, {
|
|
598
596
|
del: false,
|
|
599
597
|
label: iosName
|
|
600
|
-
}), report);
|
|
598
|
+
}), report, "ios");
|
|
601
599
|
}
|
|
602
600
|
const android = platforms.android;
|
|
603
601
|
if (android?.avdName) {
|
|
@@ -609,12 +607,15 @@ function shutDownDevices(project, { teardownIos, teardownAvd, report }) {
|
|
|
609
607
|
reason: "Stim does not own this device"
|
|
610
608
|
};
|
|
611
609
|
report(chalk.dim(phaseLine("device", `${android.avdName} is not Stim-owned, leaving it running`)));
|
|
612
|
-
} else device[androidKey] = reportDevice(android.avdName, teardownAvd(android.avdName, { del: false }), report);
|
|
610
|
+
} else device[androidKey] = reportDevice(android.avdName, teardownAvd(android.avdName, { del: false }), report, "android");
|
|
613
611
|
}
|
|
614
612
|
}
|
|
615
613
|
return device;
|
|
616
614
|
}
|
|
617
|
-
function
|
|
615
|
+
function deviceShutdownRemedy(kind, label) {
|
|
616
|
+
return kind === "ios" ? `check it with \`xcrun simctl list devices\`, then \`xcrun simctl shutdown ${label}\` yourself -- if it will not respond, \`stim gc --delete\` reclaims it` : `check it with \`adb devices\`, then \`adb -s <serial> emu kill\` yourself -- if it will not respond, \`stim gc --delete\` reclaims it`;
|
|
617
|
+
}
|
|
618
|
+
function reportDevice(label, r, report, kind) {
|
|
618
619
|
if (r.status === "torn-down") {
|
|
619
620
|
report(chalk.green(phaseLine("device", `shut down ${r.label ?? label}`)));
|
|
620
621
|
return {
|
|
@@ -639,11 +640,14 @@ function reportDevice(label, r, report) {
|
|
|
639
640
|
reason
|
|
640
641
|
};
|
|
641
642
|
}
|
|
642
|
-
|
|
643
|
+
const remedy = deviceShutdownRemedy(kind, r.label ?? label);
|
|
644
|
+
report(chalk.red(phaseLine("device", `failed to shut down ${r.label ?? label}: ${r.reason}`)));
|
|
645
|
+
report(chalk.dim(phaseLine("", remedy)));
|
|
643
646
|
return {
|
|
644
647
|
status: "failed",
|
|
645
|
-
label,
|
|
646
|
-
reason: r.reason
|
|
648
|
+
label: r.label ?? label,
|
|
649
|
+
reason: r.reason,
|
|
650
|
+
remedy
|
|
647
651
|
};
|
|
648
652
|
}
|
|
649
653
|
function summarize(root, outcomes, ok) {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as resolveSupervisorTarget } from "./ownership-
|
|
2
|
-
import { a as resolveCollectorTargets, i as readSupervisorState, n as clearSupervisorState, o as runStop, r as readCollectorState, s as stopCommand, t as clearCollectorState } from "./stop-
|
|
1
|
+
import { t as resolveSupervisorTarget } from "./ownership-DZjdx8Gq.mjs";
|
|
2
|
+
import { a as resolveCollectorTargets, i as readSupervisorState, n as clearSupervisorState, o as runStop, r as readCollectorState, s as stopCommand, t as clearCollectorState } from "./stop-Dj4pfmyv.mjs";
|
|
3
3
|
export { clearCollectorState, clearSupervisorState, stopCommand as default, readCollectorState, readSupervisorState, resolveCollectorTargets, resolveSupervisorTarget, runStop };
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import { t as NdjsonWriter } from "./ndjson-
|
|
1
|
+
import { t as NdjsonWriter } from "./ndjson-Du3j0AQ9.mjs";
|
|
2
|
+
import "@stim-cli/core/state";
|
|
3
|
+
//#region src/supervisor/idle-stop.d.ts
|
|
4
|
+
interface IdleProbe {
|
|
5
|
+
lastActivityAt(): number;
|
|
6
|
+
blocker(): string | null;
|
|
7
|
+
}
|
|
2
8
|
import * as import__stim_cli_core_process_identity from "@stim-cli/core/process-identity";
|
|
3
9
|
//#endregion
|
|
4
10
|
//#region src/supervisor/state.d.ts
|
|
@@ -15,6 +21,7 @@ interface ParsedSupervisorArgs {
|
|
|
15
21
|
port?: number;
|
|
16
22
|
tunnel?: boolean;
|
|
17
23
|
resetCache?: boolean;
|
|
24
|
+
idleStopMinutes?: number;
|
|
18
25
|
error?: string;
|
|
19
26
|
}
|
|
20
27
|
declare function parseArgs(argv: string[]): ParsedSupervisorArgs;
|
|
@@ -43,6 +50,8 @@ interface RunSupervisorOptions {
|
|
|
43
50
|
port: number;
|
|
44
51
|
tunnel?: boolean;
|
|
45
52
|
resetCache?: boolean;
|
|
53
|
+
idleStopMinutes?: number;
|
|
54
|
+
idleProbe?: IdleProbe;
|
|
46
55
|
isExpo?: (projectRoot: string) => boolean;
|
|
47
56
|
startBare?: ServerStarter | null;
|
|
48
57
|
startExpo?: ServerStarter | null;
|
|
@@ -51,7 +60,7 @@ interface RunSupervisorOptions {
|
|
|
51
60
|
attachSignals?: boolean;
|
|
52
61
|
stderr?: (line: string) => void;
|
|
53
62
|
}
|
|
54
|
-
declare function runSupervisor({ root, port, tunnel, resetCache, isExpo, startBare, startExpo, now, onExit, attachSignals, stderr }: RunSupervisorOptions): Promise<{
|
|
63
|
+
declare function runSupervisor({ root, port, tunnel, resetCache, idleStopMinutes, idleProbe, isExpo, startBare, startExpo, now, onExit, attachSignals, stderr }: RunSupervisorOptions): Promise<{
|
|
55
64
|
mode: string;
|
|
56
65
|
server: ServerHandle | undefined;
|
|
57
66
|
shutdown: (code: number, event: string, msg: string) => Promise<void>;
|
package/dist/supervisor-run.mjs
CHANGED
|
@@ -1,20 +1,139 @@
|
|
|
1
|
-
import { C as setSupervisor, D as process_identity_exports, a as clearSupervisor } from "./config-
|
|
2
|
-
import { r as createNdjsonWriter } from "./ndjson-
|
|
3
|
-
import {
|
|
4
|
-
import { a as detectIsExpo } from "./project-
|
|
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";
|
|
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";
|
|
5
5
|
import { t as describeError } from "./errors-B6W7FkxQ.mjs";
|
|
6
|
-
import { t as relaunchWithLogFile } from "./detached-entry-
|
|
7
|
-
import { c as readPidFile, f as writePidFile, n as MODE_EXPO, o as clearWorkspaceSupervisor, r as clearExpoMetroTunnel, t as MODE_BARE } from "./state-
|
|
8
|
-
import {
|
|
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-BXA96myD.mjs";
|
|
9
|
+
import { a as parseLease, r as leaseIsExpired, s as parseWorkspaceLeases, t as deviceLeasePath } from "./device-lease-CbEaIk7b.mjs";
|
|
10
|
+
import { c as createActivityReader } from "./build-progress-BXx-ZBe4.mjs";
|
|
11
|
+
import { N as ownedAvdSerialResolver } from "./android-787HhP5w.mjs";
|
|
12
|
+
import { s as workspaceBuildInProgress } from "./idle-DpRLp5lj.mjs";
|
|
13
|
+
import { n as withIdleWorkspace } from "./in-use-CjUluqIX.mjs";
|
|
14
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
9
15
|
import { LOG_ROTATE_BYTES } from "@stim-cli/core";
|
|
10
|
-
import {
|
|
16
|
+
import { IDLE_STOP_KEY } from "@stim-cli/core/state";
|
|
17
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
11
18
|
import { fileURLToPath } from "node:url";
|
|
19
|
+
//#region src/supervisor/idle-stop.ts
|
|
20
|
+
const IDLE_CHECK_MS = 6e4;
|
|
21
|
+
const MINUTE_MS = 6e4;
|
|
22
|
+
function trackDevServerActivity(now) {
|
|
23
|
+
let last = now();
|
|
24
|
+
const open = /* @__PURE__ */ new Set();
|
|
25
|
+
return {
|
|
26
|
+
record(entry) {
|
|
27
|
+
const { event, requestId } = entry ?? {};
|
|
28
|
+
if (event === "bundle_response_started") {
|
|
29
|
+
if (typeof requestId === "string") open.add(requestId);
|
|
30
|
+
} else if (event === "bundle_response_finished" || event === "bundle_response_failed") {
|
|
31
|
+
if (typeof requestId === "string") open.delete(requestId);
|
|
32
|
+
} else if (event !== "expo_stdout") return;
|
|
33
|
+
last = now();
|
|
34
|
+
},
|
|
35
|
+
lastActivityAt: () => open.size > 0 ? now() : last
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function deviceTargets(root) {
|
|
39
|
+
const serialOf = ownedAvdSerialResolver({ timeoutMs: 5e3 });
|
|
40
|
+
return projectDeviceSlots(getProject(root)).flatMap(({ slot, platforms }) => {
|
|
41
|
+
const targets = [];
|
|
42
|
+
if (platforms.ios?.deviceUdid) targets.push({
|
|
43
|
+
platform: "ios",
|
|
44
|
+
id: platforms.ios.deviceUdid,
|
|
45
|
+
slot
|
|
46
|
+
});
|
|
47
|
+
const android = platforms.android;
|
|
48
|
+
const serial = android?.avdName ? serialOf(android.avdName).serial : android?.serial;
|
|
49
|
+
if (serial) targets.push({
|
|
50
|
+
platform: "android",
|
|
51
|
+
id: serial,
|
|
52
|
+
slot
|
|
53
|
+
});
|
|
54
|
+
return targets;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function heldDeviceLease(root, now) {
|
|
58
|
+
for (const [key, record] of Object.entries(parseWorkspaceLeases(readWorkspaceState$1(root)?.deviceLeases))) {
|
|
59
|
+
const platform = parseDeviceSlotKey(key)?.platform;
|
|
60
|
+
if (!platform) continue;
|
|
61
|
+
let raw;
|
|
62
|
+
try {
|
|
63
|
+
raw = readFileSync(deviceLeasePath(platform, record.id), "utf8");
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (error.code === "ENOENT") continue;
|
|
66
|
+
return `${platform} device ${record.id} has a lease that cannot be read (${describeError(error)})`;
|
|
67
|
+
}
|
|
68
|
+
const lease = parseLease(raw);
|
|
69
|
+
if (!lease) return `${platform} device ${record.id} has a lease that cannot be read`;
|
|
70
|
+
if (lease.token !== record.token || leaseIsExpired(lease, now)) continue;
|
|
71
|
+
const by = record.kind === "declared" ? "stim device lock" : "a stim ios or android run";
|
|
72
|
+
return `${platform} device ${record.id} is leased by ${by} until ${lease.expiresAt}`;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function workspaceIdleProbe(root, { platform = process.platform } = {}) {
|
|
77
|
+
return {
|
|
78
|
+
lastActivityAt() {
|
|
79
|
+
const used = Date.parse(String(readWorkspaceState$1(root)?.lastUsedAt ?? ""));
|
|
80
|
+
let clientLog = NaN;
|
|
81
|
+
try {
|
|
82
|
+
clientLog = statSync(join(workspaceLogsDir(root), "client.ndjson")).mtimeMs;
|
|
83
|
+
} catch {}
|
|
84
|
+
const times = [used, clientLog].filter(Number.isFinite);
|
|
85
|
+
return times.length ? Math.max(...times) : NaN;
|
|
86
|
+
},
|
|
87
|
+
blocker() {
|
|
88
|
+
if (workspaceBuildInProgress(root)) return "a build is in progress";
|
|
89
|
+
const lease = heldDeviceLease(root, Date.now());
|
|
90
|
+
if (lease) return lease;
|
|
91
|
+
const readActivity = createActivityReader();
|
|
92
|
+
for (const target of deviceTargets(root)) {
|
|
93
|
+
const activity = readActivity({
|
|
94
|
+
...target,
|
|
95
|
+
workspace: root
|
|
96
|
+
});
|
|
97
|
+
if (activity.state === "driven") return `${target.platform} device ${target.id} is driven by ${activity.driver?.tool ?? "an unknown tool"}`;
|
|
98
|
+
const unknown = platform === "win32" ? activity.basis.filter((basis) => basis !== "driver-process") : activity.basis;
|
|
99
|
+
if (activity.state === "unknown" && unknown.length > 0) return `${target.platform} device ${target.id} has unknown activity (${unknown.join(", ")})`;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function watchIdleDevServer({ idleStopMs, now, serverActivityAt, probe, onIdle, checkMs = IDLE_CHECK_MS }) {
|
|
106
|
+
const idleMinutes = () => {
|
|
107
|
+
const last = Math.max(serverActivityAt(), ...[probe.lastActivityAt()].filter(Number.isFinite));
|
|
108
|
+
if (now() - last < idleStopMs) return null;
|
|
109
|
+
let blocker;
|
|
110
|
+
try {
|
|
111
|
+
blocker = probe.blocker();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
blocker = describeError(error);
|
|
114
|
+
}
|
|
115
|
+
return blocker ? null : Math.floor((now() - last) / MINUTE_MS);
|
|
116
|
+
};
|
|
117
|
+
let deciding = false;
|
|
118
|
+
const timer = setInterval(() => {
|
|
119
|
+
if (deciding || idleMinutes() === null) return;
|
|
120
|
+
deciding = true;
|
|
121
|
+
onIdle(idleMinutes).finally(() => {
|
|
122
|
+
deciding = false;
|
|
123
|
+
});
|
|
124
|
+
}, Math.min(checkMs, idleStopMs));
|
|
125
|
+
timer.unref?.();
|
|
126
|
+
return () => clearInterval(timer);
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
12
129
|
//#region src/supervisor/run.ts
|
|
130
|
+
const USAGE = "Usage: run.js --root <path> --port <n> [--tunnel] [--reset-cache] [--idle-stop-minutes <n>]";
|
|
13
131
|
function parseArgs(argv) {
|
|
14
132
|
let root;
|
|
15
133
|
let port;
|
|
16
134
|
let tunnel = false;
|
|
17
135
|
let resetCache = false;
|
|
136
|
+
let idleStopMinutes = "0";
|
|
18
137
|
for (let i = 0; i < argv.length; i++) {
|
|
19
138
|
const arg = argv[i];
|
|
20
139
|
if (arg === "--root") {
|
|
@@ -33,20 +152,26 @@ function parseArgs(argv) {
|
|
|
33
152
|
resetCache = true;
|
|
34
153
|
continue;
|
|
35
154
|
}
|
|
36
|
-
|
|
155
|
+
if (arg === "--idle-stop-minutes") {
|
|
156
|
+
idleStopMinutes = argv[++i] ?? "";
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
return { error: `Unknown supervisor argument "${arg}". ${USAGE}` };
|
|
37
160
|
}
|
|
38
|
-
if (!root) return { error:
|
|
161
|
+
if (!root) return { error: `Missing --root. ${USAGE}` };
|
|
39
162
|
if (!isAbsolute(root)) return { error: `--root must be an absolute path, got "${root}".` };
|
|
40
163
|
const parsedPort = Number(port);
|
|
41
164
|
if (!Number.isInteger(parsedPort) || parsedPort <= 0 || parsedPort > 65535) return { error: `--port must be a TCP port number, got "${port}".` };
|
|
165
|
+
if (!/^\d+$/.test(idleStopMinutes)) return { error: `--idle-stop-minutes must be a whole number of minutes, got "${idleStopMinutes}".` };
|
|
42
166
|
return {
|
|
43
167
|
root: resolve(root),
|
|
44
168
|
port: parsedPort,
|
|
45
169
|
tunnel,
|
|
46
|
-
resetCache
|
|
170
|
+
resetCache,
|
|
171
|
+
idleStopMinutes: Number(idleStopMinutes)
|
|
47
172
|
};
|
|
48
173
|
}
|
|
49
|
-
async function runSupervisor({ root, port, tunnel = false, resetCache = false, isExpo = detectIsExpo, startBare = null, startExpo = null, now = Date.now, onExit = (code) => process.exit(code), attachSignals = true, stderr = (line) => console.error(line) }) {
|
|
174
|
+
async function runSupervisor({ root, port, tunnel = false, resetCache = false, idleStopMinutes = 0, idleProbe, isExpo = detectIsExpo, startBare = null, startExpo = null, now = Date.now, onExit = (code) => process.exit(code), attachSignals = true, stderr = (line) => console.error(line) }) {
|
|
50
175
|
root = realpathSync(root);
|
|
51
176
|
const logsDir = workspaceLogsDir(root);
|
|
52
177
|
const writer = createNdjsonWriter(join(logsDir, "metro.ndjson"), { maxBytes: LOG_ROTATE_BYTES });
|
|
@@ -69,6 +194,7 @@ async function runSupervisor({ root, port, tunnel = false, resetCache = false, i
|
|
|
69
194
|
clearExpoMetroTunnel(root);
|
|
70
195
|
writePidFile(root, process.pid);
|
|
71
196
|
writeWorkspaceState(root, { supervisor: record });
|
|
197
|
+
clearWorkspaceStateKeys(root, [IDLE_STOP_KEY]);
|
|
72
198
|
try {
|
|
73
199
|
setSupervisor(root, record);
|
|
74
200
|
} catch (err) {
|
|
@@ -87,7 +213,9 @@ async function runSupervisor({ root, port, tunnel = false, resetCache = false, i
|
|
|
87
213
|
msg: `supervisor pid ${process.pid} starting the ${mode} dev server on port ${port} for ${root}`
|
|
88
214
|
});
|
|
89
215
|
let stopping = false;
|
|
216
|
+
let stopWatchingIdle = null;
|
|
90
217
|
const finish = (code, event, level, msg) => {
|
|
218
|
+
stopWatchingIdle?.();
|
|
91
219
|
writer.write({
|
|
92
220
|
src: "metro",
|
|
93
221
|
level,
|
|
@@ -108,6 +236,7 @@ async function runSupervisor({ root, port, tunnel = false, resetCache = false, i
|
|
|
108
236
|
const shutdown = async (code, event, msg) => {
|
|
109
237
|
if (stopping || !server) return;
|
|
110
238
|
stopping = true;
|
|
239
|
+
stopWatchingIdle?.();
|
|
111
240
|
try {
|
|
112
241
|
await server.close();
|
|
113
242
|
} catch (err) {
|
|
@@ -129,18 +258,23 @@ async function runSupervisor({ root, port, tunnel = false, resetCache = false, i
|
|
|
129
258
|
stopping = true;
|
|
130
259
|
finish(signal === "SIGINT" ? 130 : 143, "supervisor_stopped", "warn", `received ${signal} before the ${mode} dev server started; stopping`);
|
|
131
260
|
});
|
|
261
|
+
const serverActivity = trackDevServerActivity(now);
|
|
262
|
+
const serverWriter = Object.assign(Object.create(writer), { write(entry) {
|
|
263
|
+
serverActivity.record(entry);
|
|
264
|
+
return writer.write(entry);
|
|
265
|
+
} });
|
|
132
266
|
try {
|
|
133
|
-
server = await (mode === "expo-child" ? startExpo || (await import("./server-expo-
|
|
267
|
+
server = await (mode === "expo-child" ? startExpo || (await import("./server-expo-DBVTdult.mjs")).startExpoServer : startBare || (await import("./server-bare-DVbb7kXH.mjs")).startBareServer)({
|
|
134
268
|
root,
|
|
135
269
|
port,
|
|
136
270
|
logsDir,
|
|
137
|
-
writer,
|
|
271
|
+
writer: serverWriter,
|
|
138
272
|
tunnel,
|
|
139
273
|
resetCache,
|
|
140
274
|
onTunnelUrl: (url) => {
|
|
141
275
|
try {
|
|
142
276
|
withWorkspaceStateLock(root, () => {
|
|
143
|
-
if (readWorkspaceState(root)?.supervisor?.processToken === processToken) writeWorkspaceState(root, { metroTunnel: {
|
|
277
|
+
if (readWorkspaceState$1(root)?.supervisor?.processToken === processToken) writeWorkspaceState(root, { metroTunnel: {
|
|
144
278
|
kind: "expo",
|
|
145
279
|
url
|
|
146
280
|
} });
|
|
@@ -172,7 +306,7 @@ async function runSupervisor({ root, port, tunnel = false, resetCache = false, i
|
|
|
172
306
|
if (serverPid) {
|
|
173
307
|
const serverProcessToken = (0, process_identity_exports.captureProcessToken)(serverPid);
|
|
174
308
|
withWorkspaceStateLock(root, () => {
|
|
175
|
-
if (readWorkspaceState(root)?.supervisor?.processToken === processToken) writeWorkspaceState(root, { supervisor: {
|
|
309
|
+
if (readWorkspaceState$1(root)?.supervisor?.processToken === processToken) writeWorkspaceState(root, { supervisor: {
|
|
176
310
|
...record,
|
|
177
311
|
serverPid,
|
|
178
312
|
...serverProcessToken ? { serverProcessToken } : {}
|
|
@@ -185,6 +319,39 @@ async function runSupervisor({ root, port, tunnel = false, resetCache = false, i
|
|
|
185
319
|
event: "server_started",
|
|
186
320
|
msg: `${mode} dev server listening on port ${port}`
|
|
187
321
|
});
|
|
322
|
+
if (idleStopMinutes > 0) stopWatchingIdle = watchIdleDevServer({
|
|
323
|
+
idleStopMs: idleStopMinutes * 6e4,
|
|
324
|
+
now,
|
|
325
|
+
serverActivityAt: serverActivity.lastActivityAt,
|
|
326
|
+
probe: idleProbe ?? workspaceIdleProbe(root),
|
|
327
|
+
onIdle: async (idleMinutesNow) => {
|
|
328
|
+
const stopIfStillIdle = async () => {
|
|
329
|
+
const idleMinutes = idleMinutesNow();
|
|
330
|
+
if (stopping || idleMinutes === null) return;
|
|
331
|
+
withWorkspaceStateLock(root, () => {
|
|
332
|
+
if (readWorkspaceState$1(root)?.supervisor?.processToken === processToken) writeWorkspaceState(root, { [IDLE_STOP_KEY]: {
|
|
333
|
+
reason: "idle",
|
|
334
|
+
at: new Date(now()).toISOString(),
|
|
335
|
+
idleMinutes
|
|
336
|
+
} });
|
|
337
|
+
});
|
|
338
|
+
await shutdown(0, "supervisor_idle_stopped", `no bundle request, client log or Stim command for ${idleMinutes} minutes (metro.idleStopMinutes is ${idleStopMinutes}); stopped the ${mode} dev server`);
|
|
339
|
+
};
|
|
340
|
+
try {
|
|
341
|
+
await withIdleWorkspace(root, () => withWorkspaceProcessLock(dirname(logsDir), "metro-start", stopIfStillIdle, {
|
|
342
|
+
external: true,
|
|
343
|
+
waitMs: 0,
|
|
344
|
+
ownerPurpose: "idle stop"
|
|
345
|
+
}), {
|
|
346
|
+
purpose: "idle stop",
|
|
347
|
+
supervisor: false,
|
|
348
|
+
managedLocks: false
|
|
349
|
+
});
|
|
350
|
+
} catch (err) {
|
|
351
|
+
stderr(`Stim supervisor: could not stop the idle dev server: ${describeError(err)}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
});
|
|
188
355
|
server.onExit?.((info) => {
|
|
189
356
|
if (stopping) return;
|
|
190
357
|
const detail = info?.signal ? `signal ${info.signal}` : `exit code ${info?.code ?? "unknown"}`;
|
|
@@ -219,7 +386,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
219
386
|
root,
|
|
220
387
|
port: parsed.port,
|
|
221
388
|
tunnel: parsed.tunnel ?? false,
|
|
222
|
-
resetCache: parsed.resetCache ?? false
|
|
389
|
+
resetCache: parsed.resetCache ?? false,
|
|
390
|
+
idleStopMinutes: parsed.idleStopMinutes ?? 0
|
|
223
391
|
});
|
|
224
392
|
}
|
|
225
393
|
function invokedDirectly() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { k as ownership_claim_exports } from "./config-
|
|
2
|
-
import { r as stopDeclaringSpawnsOn, t as declareSpawnsOn } from "./spawn-claims-
|
|
1
|
+
import { k as ownership_claim_exports } from "./config-BEq0Qw_a.mjs";
|
|
2
|
+
import { r as stopDeclaringSpawnsOn, t as declareSpawnsOn } from "./spawn-claims-CUleC2cM.mjs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
//#region src/engine/workspace-process-lock.ts
|
|
5
5
|
const DEFAULT_WAIT_MS = 6e4;
|