stim 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -155
- package/dist/{android-BRgBYzU0.mjs → android-BPGizezh.mjs} +112 -135
- package/dist/{android-DeYdjjBP.mjs → android-DEgo4oCo.mjs} +2 -2
- package/dist/{android-DWssqHQ8.mjs → android-_y4Ohi5z.mjs} +54 -15
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{android-cas-SWIjzeuL.mjs → android-cas-eiExNLf7.mjs} +21 -14
- package/dist/app-install-Uf3a83Tv.mjs +819 -0
- package/dist/{build-lock-CgtsB6TV.mjs → build-lock-DPXPLgTc.mjs} +120 -24
- package/dist/build-progress-CVqkfPC6.mjs +188 -0
- package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Dg7OnMrp.mjs} +4 -4
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +22 -19
- package/dist/collector-run.d.mts +2 -0
- package/dist/collector-run.mjs +10 -9
- package/dist/{command-output-B3ycpsYv.mjs → command-output-gByVmIjy.mjs} +11 -2
- package/dist/{config-B7iyKWkL.mjs → config-Cp9lM3wX.mjs} +187 -57
- package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-Bq5DcLGn.mjs} +2 -2
- package/dist/{deps-oCXVNEqQ.mjs → deps-Cip5vUm1.mjs} +8 -5
- package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-KEu0_AXL.mjs} +1 -1
- package/dist/{device-BUoZyMoR.mjs → dev-client-HyqISM0h.mjs} +1178 -635
- package/dist/{device-RdujjHLB.mjs → device-D4U-xLty.mjs} +13 -20
- package/dist/{device-lease-Ffz_M5t0.mjs → device-lease-Crs-HkJY.mjs} +16 -9
- package/dist/{device-pool-BxRwdufO.mjs → device-pool-CnTAz8X1.mjs} +7 -7
- package/dist/{device-remote-ByN8P6CB.mjs → device-remote-AQ5rQPqr.mjs} +482 -35
- package/dist/{doctor-BMt156qe.mjs → doctor-B_Ei3bZu.mjs} +95 -107
- package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-Vfz83GCF.mjs} +22 -16
- package/dist/exec-jIAlgilk.mjs +132 -0
- package/dist/{gc-v2YcoJvm.mjs → gc-Ckw8RQHi.mjs} +763 -126
- package/dist/{guide-BTAlD1_y.mjs → guide-BiAbhs1J.mjs} +891 -501
- package/dist/{guide-status-Craq9z0t.mjs → guide-status-BAMVU0ZA.mjs} +5 -3
- package/dist/{ios-BcCiI4TS.mjs → ios-BL2NFckr.mjs} +43 -20
- package/dist/{ios-BP04pY5f.mjs → ios-BzUJWk1K.mjs} +141 -175
- package/dist/{ios-device-CauFJiYx.mjs → ios-device-D972i9wf.mjs} +149 -52
- package/dist/{ios-device-CFXA46ez.mjs → ios-device-DPsSm0v_.mjs} +3 -2
- package/dist/launch-verify-Q_OtW1Rf.mjs +365 -0
- package/dist/{logs-CakdBTtm.mjs → logs-CI9NQSK7.mjs} +18 -13
- package/dist/{logs-query-BGL-Pl12.mjs → logs-query-Cqma3WIu.mjs} +36 -28
- package/dist/{metro-C7bcJ0uo.mjs → metro-DrLHaMn2.mjs} +78 -8
- package/dist/{named-ports-DzMOlmaX.mjs → named-ports-CmLQSIRj.mjs} +4 -4
- package/dist/{ndjson-BZwI1NBv.mjs → ndjson-9uFNRwfe.mjs} +36 -3
- package/dist/{ownership-Cp52QAK1.mjs → ownership-D15Op8vN.mjs} +3 -3
- package/dist/{ownership-wolE-0Do.mjs → ownership-DCQPuWEc.mjs} +26 -7
- package/dist/{ports-BIbJaKNW.mjs → ports-BNKoWVWZ.mjs} +3 -3
- package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
- package/dist/{project-DDZWf_16.mjs → project-DS112DYB.mjs} +8 -3
- package/dist/{reload-BdypPbjM.mjs → reload-ZNJUo1pl.mjs} +12 -13
- package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DWkkcIxV.mjs} +2 -2
- package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-B_lctBR3.mjs} +8 -7
- package/dist/server-expo-CeILEu86.mjs +2 -0
- package/dist/settings-BKNh5rl4.mjs +376 -0
- package/dist/{settings-BXXorVM2.mjs → settings-D030k0t4.mjs} +464 -152
- package/dist/settings.schema.json +989 -0
- package/dist/{teardown-BJqEzPo_.mjs → sim-pool-KaIsp2Ft.mjs} +14 -419
- package/dist/simslim-B_LsGLuJ.mjs +259 -0
- package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-DU9A-gYw.mjs} +4 -4
- package/dist/spawn-claims-Dn3C_N6w.mjs +49 -0
- package/dist/{start-CmxtOKjU.mjs → start-DYkkKF0s.mjs} +27 -31
- package/dist/{state-DZobzxLW.mjs → state-NcJzMEBF.mjs} +5 -3
- package/dist/{stats-CLg5RuDS.mjs → stats-B63-JVAY.mjs} +4 -4
- package/dist/{stats-BYcZ6n1h.mjs → stats-DG9e66Rz.mjs} +66 -5
- package/dist/status-S6ehL4Ui.mjs +932 -0
- package/dist/{stop-C8JW6kKp.mjs → stop-BO28HGqF.mjs} +101 -25
- package/dist/{stop-B1kOpYei.mjs → stop-T1R-cZRz.mjs} +2 -2
- package/dist/supervisor-run.mjs +52 -34
- package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-DWaiRD0e.mjs} +14 -9
- package/dist/{workspace-state-BT5E3SA2.mjs → workspace-state-DF2gCFhx.mjs} +52 -29
- package/dist/worktree-DKhSF0UX.mjs +2 -0
- package/dist/{worktree-CFLA-6MX.mjs → worktree-LGB-UwgG.mjs} +603 -59
- package/dist/{worktree-FG_ZKKdb.mjs → worktree-R_cUlE7s.mjs} +62 -41
- package/dist/{xcode-B6YaWeg0.mjs → xcode-BZqyxh3e.mjs} +216 -58
- package/package.json +5 -6
- package/skill/SKILL.md +2 -0
- package/dist/app-install-BDbsSgCl.mjs +0 -1175
- package/dist/build-slots-C-oAHRT1.mjs +0 -151
- package/dist/dev-client-BmT-ioFI.mjs +0 -797
- package/dist/exec-B29XNuhP.mjs +0 -77
- package/dist/reclaim-KJ-0uWEF.mjs +0 -361
- package/dist/server-expo-YDDG3eP7.mjs +0 -2
- package/dist/status-DdkLki47.mjs +0 -426
- package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { D as process_identity_exports, K as projectDeviceSlots, U as deviceSlotPlatforms, g as loadConfig, k as ownership_claim_exports, u as getConfigDir } from "./config-Cp9lM3wX.mjs";
|
|
2
|
+
import { t as getExecutor } from "./exec-jIAlgilk.mjs";
|
|
3
|
+
import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
|
|
4
|
+
import { C as listAdbDevices } from "./android-_y4Ohi5z.mjs";
|
|
5
|
+
import { c as iosRuntimeMatches, u as listAllIosSims } from "./ios-BL2NFckr.mjs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
//#region src/engine/device-capacity.ts
|
|
8
|
+
function liveOwnedDeviceCount({ sims = [], adbEmulators = [], config = null } = {}) {
|
|
9
|
+
let count = 0;
|
|
10
|
+
for (const sim of sims) if (sim?.state === "Booted" && sim.name?.startsWith("stim-")) count++;
|
|
11
|
+
const livePorts = new Set(adbEmulators.map((e) => e.consolePort));
|
|
12
|
+
for (const proj of Object.values(config?.projects || {})) for (const { platforms } of projectDeviceSlots(proj)) {
|
|
13
|
+
const android = platforms.android;
|
|
14
|
+
if (android?.owned && android.avdName && typeof android.consolePort === "number" && livePorts.has(android.consolePort)) count++;
|
|
15
|
+
}
|
|
16
|
+
return count;
|
|
17
|
+
}
|
|
18
|
+
function workspaceHasLiveDevice({ platform, project, slot = "default", sims = [], adbEmulators = [] } = {}) {
|
|
19
|
+
if (!platform) return false;
|
|
20
|
+
const record = deviceSlotPlatforms(project, slot)?.[platform];
|
|
21
|
+
if (!record) return false;
|
|
22
|
+
if (platform === "ios") return sims.some((s) => s.udid === record.deviceUdid && s.state === "Booted");
|
|
23
|
+
return typeof record.consolePort === "number" && adbEmulators.some((e) => e.consolePort === record.consolePort);
|
|
24
|
+
}
|
|
25
|
+
function deviceCapacityRefusal({ platform, project, slot = "default", max, sims = [], adb = null, config = null } = {}) {
|
|
26
|
+
if (!max || max <= 0) return null;
|
|
27
|
+
const adbEmulators = adb?.emulators || [];
|
|
28
|
+
if (workspaceHasLiveDevice({
|
|
29
|
+
platform,
|
|
30
|
+
project,
|
|
31
|
+
slot,
|
|
32
|
+
sims,
|
|
33
|
+
adbEmulators
|
|
34
|
+
})) return null;
|
|
35
|
+
const count = liveOwnedDeviceCount({
|
|
36
|
+
sims,
|
|
37
|
+
adbEmulators,
|
|
38
|
+
config
|
|
39
|
+
});
|
|
40
|
+
if (count < max) return null;
|
|
41
|
+
return {
|
|
42
|
+
code: "STIM_AT_CAPACITY",
|
|
43
|
+
message: `${count} Stim device(s) are already booted and concurrency.maxDevices is ${max}, so booting another would exceed the cap.`,
|
|
44
|
+
remedy: "stop an environment (stim stop) or raise concurrency.maxDevices"
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function checkDeviceCapacity({ platform, project, slot = "default", max, sims = listAllIosSims, adb = listAdbDevices, config = loadConfig } = {}) {
|
|
48
|
+
if (!max || max <= 0) return null;
|
|
49
|
+
let simList = [];
|
|
50
|
+
let adbRes = {
|
|
51
|
+
emulators: [],
|
|
52
|
+
physical: [],
|
|
53
|
+
unhealthy: []
|
|
54
|
+
};
|
|
55
|
+
try {
|
|
56
|
+
simList = typeof sims === "function" ? sims() || [] : sims || [];
|
|
57
|
+
} catch {}
|
|
58
|
+
try {
|
|
59
|
+
adbRes = typeof adb === "function" ? adb() || adbRes : adb || adbRes;
|
|
60
|
+
} catch {}
|
|
61
|
+
let cfg = null;
|
|
62
|
+
try {
|
|
63
|
+
cfg = typeof config === "function" ? config() : config ?? null;
|
|
64
|
+
} catch {}
|
|
65
|
+
return deviceCapacityRefusal({
|
|
66
|
+
platform,
|
|
67
|
+
project,
|
|
68
|
+
slot,
|
|
69
|
+
max,
|
|
70
|
+
sims: simList,
|
|
71
|
+
adb: adbRes,
|
|
72
|
+
config: cfg
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function deviceTypeMismatch(recordedTypeId, requestedName, deviceTypes) {
|
|
76
|
+
if (!requestedName || !recordedTypeId) return null;
|
|
77
|
+
const wanted = (deviceTypes || []).find((d) => d.name === requestedName);
|
|
78
|
+
if (!wanted) return null;
|
|
79
|
+
if (wanted.identifier === recordedTypeId) return null;
|
|
80
|
+
const recorded = (deviceTypes || []).find((d) => d.identifier === recordedTypeId);
|
|
81
|
+
return `this project's sim is ${recorded ? recorded.name : recordedTypeId}, but --device-type asked for ${requestedName}`;
|
|
82
|
+
}
|
|
83
|
+
function installedNames(names) {
|
|
84
|
+
const unique = [...new Set(names.filter((n) => typeof n === "string" && n !== ""))];
|
|
85
|
+
return unique.length > 0 ? unique.join(", ") : "none";
|
|
86
|
+
}
|
|
87
|
+
function unknownIosRuntimeRefusal(requested, runtimes) {
|
|
88
|
+
if (!requested) return null;
|
|
89
|
+
if ((runtimes || []).some((r) => iosRuntimeMatches(r, requested))) return null;
|
|
90
|
+
return {
|
|
91
|
+
message: `No installed simulator runtime matches "${requested}". Installed runtimes: ${installedNames((runtimes || []).map((r) => r.version))}.`,
|
|
92
|
+
remedy: "Pass `--runtime` (or set ios.runtime) a version printed above (\"26.5\") or a runtime's full name (\"iOS 26.5\"); nothing else matches. Install more runtimes through Xcode."
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function creatableIosDeviceTypeNames(runtimes, runtime) {
|
|
96
|
+
return (runtime ? (runtimes || []).filter((r) => iosRuntimeMatches(r, runtime)) : runtimes || []).flatMap((r) => (r.supportedDeviceTypes || []).map((d) => d.name));
|
|
97
|
+
}
|
|
98
|
+
function unknownIosDeviceTypeRefusal(requested, runtimes, runtime) {
|
|
99
|
+
if (!requested) return null;
|
|
100
|
+
const creatable = creatableIosDeviceTypeNames(runtimes, runtime);
|
|
101
|
+
if (creatable.includes(requested)) return null;
|
|
102
|
+
return {
|
|
103
|
+
message: `No device type named "${requested}" can be created on ${runtime ? `runtime ${runtime}` : "any installed simulator runtime"}. ${runtime ? `Device types runtime ${runtime} supports` : "Device types the installed runtimes support"}: ${installedNames(creatable)}.`,
|
|
104
|
+
remedy: "Pass `--device-type` (or set ios.deviceType) one of the names printed above, exactly as `xcrun simctl list devicetypes` spells it. `xcrun simctl list devicetypes` also lists watchOS, tvOS and visionOS models, which no iOS runtime can create. Install more models through Xcode."
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function unknownAndroidSystemImageRefusal(requested, images) {
|
|
108
|
+
if (!requested) return null;
|
|
109
|
+
if ((images || []).some((i) => i.pkg === requested)) return null;
|
|
110
|
+
return {
|
|
111
|
+
message: `No installed Android system image is named "${requested}". Installed system images: ${installedNames((images || []).map((i) => i.pkg))}.`,
|
|
112
|
+
remedy: "Pass `--system-image` (or set android.systemImage) to one of the package ids printed above. Install more with `sdkmanager \"system-images;android-36;google_apis;arm64-v8a\"`."
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/engine/simslim.ts
|
|
117
|
+
async function reconcileSimSlim({ udid, profile, previouslyManaged = false, out = () => {}, spawn, timeoutMs = 72e4, cleanupMs = 1e4 }) {
|
|
118
|
+
if (!profile && !previouslyManaged) return {
|
|
119
|
+
managed: false,
|
|
120
|
+
profile: null
|
|
121
|
+
};
|
|
122
|
+
const action = profile ? "on" : "off";
|
|
123
|
+
const args = profile ? [
|
|
124
|
+
"on",
|
|
125
|
+
udid,
|
|
126
|
+
"--profile",
|
|
127
|
+
profile
|
|
128
|
+
] : ["off", udid];
|
|
129
|
+
const lines = [];
|
|
130
|
+
const onLine = (raw) => {
|
|
131
|
+
const line = stripAnsi(raw).trim();
|
|
132
|
+
if (!line) return;
|
|
133
|
+
lines.push(line);
|
|
134
|
+
if (lines.length > 20) lines.shift();
|
|
135
|
+
out(`SimSlim: ${line}`);
|
|
136
|
+
};
|
|
137
|
+
const stdout = createLineReader(onLine);
|
|
138
|
+
const stderr = createLineReader(onLine);
|
|
139
|
+
const root = join(getConfigDir(), "simslim-locks", `${encodeURIComponent(udid.toLowerCase())}.lock`);
|
|
140
|
+
const label = `SimSlim for ${udid}`;
|
|
141
|
+
const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
|
|
142
|
+
root,
|
|
143
|
+
mode: "exclusive",
|
|
144
|
+
label
|
|
145
|
+
});
|
|
146
|
+
if (attempt.pending) (0, ownership_claim_exports.releaseClaim)(attempt.pending);
|
|
147
|
+
const claim = attempt.acquired;
|
|
148
|
+
if (!claim) throw new ownership_claim_exports.ClaimRefusedError({
|
|
149
|
+
root,
|
|
150
|
+
claimPath: attempt.held?.path ?? root,
|
|
151
|
+
label,
|
|
152
|
+
reason: "another SimSlim operation is still using this simulator"
|
|
153
|
+
});
|
|
154
|
+
let child;
|
|
155
|
+
let result;
|
|
156
|
+
let identity;
|
|
157
|
+
let cancellation;
|
|
158
|
+
const stopGroup = () => {
|
|
159
|
+
if (child?.pid !== void 0 && identity && (0, process_identity_exports.inspectProcessIdentity)(identity) === "same") try {
|
|
160
|
+
process.kill(-child.pid, "SIGKILL");
|
|
161
|
+
} catch {}
|
|
162
|
+
};
|
|
163
|
+
const cancel = (signal) => {
|
|
164
|
+
cancellation ??= signal;
|
|
165
|
+
stopGroup();
|
|
166
|
+
};
|
|
167
|
+
const onInterrupt = () => cancel("SIGINT");
|
|
168
|
+
const onTerminate = () => cancel("SIGTERM");
|
|
169
|
+
const groupAlive = () => child?.pid !== void 0 && (0, ownership_claim_exports.processGroupAlive)(child.pid);
|
|
170
|
+
const settled = () => result !== void 0 && !groupAlive();
|
|
171
|
+
const waitUntil = async (deadline, cancellable = false) => {
|
|
172
|
+
while (!settled() && Date.now() < deadline) {
|
|
173
|
+
if (cancellable && cancellation) break;
|
|
174
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(50, deadline - Date.now())));
|
|
175
|
+
}
|
|
176
|
+
return settled();
|
|
177
|
+
};
|
|
178
|
+
process.on("SIGINT", onInterrupt);
|
|
179
|
+
process.on("SIGTERM", onTerminate);
|
|
180
|
+
process.on("exit", stopGroup);
|
|
181
|
+
try {
|
|
182
|
+
(0, ownership_claim_exports.markClaimChildPending)(claim);
|
|
183
|
+
try {
|
|
184
|
+
child = (spawn ?? ((cmd, childArgs, opts) => getExecutor().spawn(cmd, childArgs, opts)))("simslim", args, {
|
|
185
|
+
stdio: [
|
|
186
|
+
"ignore",
|
|
187
|
+
"pipe",
|
|
188
|
+
"pipe"
|
|
189
|
+
],
|
|
190
|
+
detached: true
|
|
191
|
+
});
|
|
192
|
+
} catch (error) {
|
|
193
|
+
throw simslimLaunchError(error);
|
|
194
|
+
}
|
|
195
|
+
child.stdout?.on("data", (chunk) => stdout.push(chunk));
|
|
196
|
+
child.stderr?.on("data", (chunk) => stderr.push(chunk));
|
|
197
|
+
waitForChild(child).then((value) => {
|
|
198
|
+
result = value;
|
|
199
|
+
});
|
|
200
|
+
if (child.pid !== void 0) {
|
|
201
|
+
const captured = (0, process_identity_exports.captureProcessIdentity)(child.pid);
|
|
202
|
+
if (captured.ok) {
|
|
203
|
+
identity = {
|
|
204
|
+
pid: child.pid,
|
|
205
|
+
processToken: captured.token
|
|
206
|
+
};
|
|
207
|
+
try {
|
|
208
|
+
(0, ownership_claim_exports.setClaimChild)(claim, identity);
|
|
209
|
+
} catch {}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const completed = await waitUntil(Date.now() + timeoutMs, true);
|
|
213
|
+
if (cancellation || !completed) {
|
|
214
|
+
stopGroup();
|
|
215
|
+
const stopped = await waitUntil(Date.now() + cleanupMs);
|
|
216
|
+
const detail = lines.length ? ` ${lines.join(" | ")}` : "";
|
|
217
|
+
const recovery = stopped ? "Its process group has stopped. Retry stim ios to reconcile the retained simulator settings." : `Its process group could not be confirmed stopped. The claim remains at ${claim.path}. Inspect the SimSlim process group ${child.pid ?? "unknown"} before retrying. Only when nothing is using it, remove the claim: ${(0, ownership_claim_exports.claimRemoveCommand)(claim.path)}`;
|
|
218
|
+
const message = `SimSlim ${action} ${cancellation ? `interrupted by ${cancellation}` : `exceeded ${Math.round(timeoutMs / 1e3)}s`}. ${recovery}${detail}`;
|
|
219
|
+
if (cancellation) out(message);
|
|
220
|
+
throw Object.assign(new Error(message), { code: cancellation ? "EINTR" : "ETIMEDOUT" });
|
|
221
|
+
}
|
|
222
|
+
} finally {
|
|
223
|
+
if (!child || settled()) (0, ownership_claim_exports.releaseClaim)(claim);
|
|
224
|
+
else {
|
|
225
|
+
child.stdout?.destroy?.();
|
|
226
|
+
child.stderr?.destroy?.();
|
|
227
|
+
child.unref();
|
|
228
|
+
}
|
|
229
|
+
process.off("SIGINT", onInterrupt);
|
|
230
|
+
process.off("SIGTERM", onTerminate);
|
|
231
|
+
process.off("exit", stopGroup);
|
|
232
|
+
if (cancellation) process.exit(cancellation === "SIGINT" ? 130 : 143);
|
|
233
|
+
}
|
|
234
|
+
stdout.flush();
|
|
235
|
+
stderr.flush();
|
|
236
|
+
if (result?.error) throw simslimLaunchError(result.error);
|
|
237
|
+
if (result?.code !== 0) {
|
|
238
|
+
const detail = lines.length ? ` ${lines.join(" | ")}` : "";
|
|
239
|
+
throw new Error(`SimSlim ${action} failed with exit code ${result?.code ?? "unknown"}.${detail}`);
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
managed: Boolean(profile),
|
|
243
|
+
profile: profile ?? null
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function simslimLaunchError(error) {
|
|
247
|
+
const cause = error;
|
|
248
|
+
if (cause?.code === "ENOENT") return new Error("SimSlim is configured but the `simslim` command is not installed. Run `brew install mobai-app/tap/simslim`.", { cause });
|
|
249
|
+
return new Error(`Could not start SimSlim: ${String(cause?.message || cause)}`, { cause });
|
|
250
|
+
}
|
|
251
|
+
function simslimIsOnPath() {
|
|
252
|
+
try {
|
|
253
|
+
return Boolean(getExecutor().findExecutable("simslim"));
|
|
254
|
+
} catch {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
//#endregion
|
|
259
|
+
export { liveOwnedDeviceCount as a, unknownIosRuntimeRefusal as c, deviceTypeMismatch as i, simslimIsOnPath as n, unknownAndroidSystemImageRefusal as o, checkDeviceCapacity as r, unknownIosDeviceTypeRefusal as s, reconcileSimSlim as t };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { f as getProject } from "./config-
|
|
2
|
-
import { r as readWorkspaceState } from "./workspace-state-
|
|
3
|
-
import {
|
|
4
|
-
import { n as fileLeaseIo } from "./device-lease-
|
|
1
|
+
import { f as getProject } from "./config-Cp9lM3wX.mjs";
|
|
2
|
+
import { r as readWorkspaceState } from "./workspace-state-DF2gCFhx.mjs";
|
|
3
|
+
import { d as deviceShellArg } from "./app-install-Uf3a83Tv.mjs";
|
|
4
|
+
import { n as fileLeaseIo } from "./device-lease-Crs-HkJY.mjs";
|
|
5
5
|
//#region src/engine/slot-launch.ts
|
|
6
6
|
function launchSlotScope(root, slot = "default") {
|
|
7
7
|
if (slot !== "default") return slot;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { D as process_identity_exports, k as ownership_claim_exports } from "./config-Cp9lM3wX.mjs";
|
|
2
|
+
//#region src/engine/spawn-claims.ts
|
|
3
|
+
const declaring = /* @__PURE__ */ new Set();
|
|
4
|
+
/** Record every child later started through `spawnDeclared` on `claim` until `stopDeclaringSpawnsOn(claim)`. */
|
|
5
|
+
function declareSpawnsOn(claim) {
|
|
6
|
+
if (claim) declaring.add(claim);
|
|
7
|
+
}
|
|
8
|
+
function stopDeclaringSpawnsOn(claim) {
|
|
9
|
+
if (claim) declaring.delete(claim);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Start a child that every claim passed to `declareSpawnsOn` records until it exits. The child keeps
|
|
13
|
+
* the caller's process group, so a terminal interrupt still reaches it; the claim follows the child
|
|
14
|
+
* itself. A claim that cannot be marked stops the spawn rather than letting it run unprotected. A claim
|
|
15
|
+
* records one child, so declared spawns must not overlap.
|
|
16
|
+
*/
|
|
17
|
+
function spawnDeclared(spawn) {
|
|
18
|
+
const claims = [...declaring];
|
|
19
|
+
if (claims.length === 0) return spawn();
|
|
20
|
+
const forget = () => {
|
|
21
|
+
for (const claim of claims) try {
|
|
22
|
+
(0, ownership_claim_exports.clearClaimChild)(claim);
|
|
23
|
+
} catch {}
|
|
24
|
+
};
|
|
25
|
+
let child;
|
|
26
|
+
try {
|
|
27
|
+
for (const claim of claims) (0, ownership_claim_exports.markClaimChildPending)(claim);
|
|
28
|
+
child = spawn();
|
|
29
|
+
} catch (error) {
|
|
30
|
+
forget();
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
const pid = child.pid;
|
|
34
|
+
if (pid === void 0) {
|
|
35
|
+
forget();
|
|
36
|
+
return child;
|
|
37
|
+
}
|
|
38
|
+
const captured = (0, process_identity_exports.captureProcessIdentity)(pid);
|
|
39
|
+
if (captured.ok) for (const claim of claims) try {
|
|
40
|
+
(0, ownership_claim_exports.setClaimChild)(claim, {
|
|
41
|
+
pid,
|
|
42
|
+
processToken: captured.token
|
|
43
|
+
});
|
|
44
|
+
} catch {}
|
|
45
|
+
child.once("exit", forget);
|
|
46
|
+
return child;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { spawnDeclared as n, stopDeclaringSpawnsOn as r, declareSpawnsOn as t };
|
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { D as process_identity_exports, a as clearSupervisor, f as getProject, w as upsertProject } from "./config-Cp9lM3wX.mjs";
|
|
2
|
+
import { t as getExecutor } from "./exec-jIAlgilk.mjs";
|
|
3
|
+
import { i as levelRank } from "./ndjson-9uFNRwfe.mjs";
|
|
4
|
+
import { b as workspaceLogsDir, c as writeWorkspaceState, i as recordWorkspaceUse, m as supervisorLogFile, r as readWorkspaceState, u as ensureWorkspaceStorage } from "./workspace-state-DF2gCFhx.mjs";
|
|
5
|
+
import { _ as repoRoot, s as gitCommonDir } from "./worktree-R_cUlE7s.mjs";
|
|
6
|
+
import { a as detectIsExpo, i as detectBundleId, n as appProjectProblem, o as findProjectRoot, r as detectAndroidPackage, t as NO_PROJECT_REFUSAL } from "./project-DS112DYB.mjs";
|
|
6
7
|
import { n as supervisorError } from "./errors-B6W7FkxQ.mjs";
|
|
7
|
-
import { n as windowsLauncherArgs } from "./detached-entry-
|
|
8
|
-
import { i as clearManagedMetroTunnel, o as clearWorkspaceSupervisor, s as readMetroTunnel } from "./state-
|
|
9
|
-
import { a as
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { n as workspaceProcessLockError, t as withWorkspaceProcessLock } from "./workspace-process-lock-
|
|
8
|
+
import { n as windowsLauncherArgs } from "./detached-entry-KEu0_AXL.mjs";
|
|
9
|
+
import { i as clearManagedMetroTunnel, o as clearWorkspaceSupervisor, s as readMetroTunnel } from "./state-NcJzMEBF.mjs";
|
|
10
|
+
import { a as pidExists, c as resolveProjectMetro, f as reserveMetroPort, l as signalProcessTree, n as killMetroTree } from "./metro-DrLHaMn2.mjs";
|
|
11
|
+
import { D as remoteIosSetting, E as remoteAndroidSetting, F as unknownSettingKeys, G as planMetroReach, M as settingShapeErrors, O as resolveCacheProviderConfig, P as tunnelModeSetting, U as PUBLIC_METRO_ENV, W as detectProviders, i as SETTING_SHAPE_REMEDY, k as resolveSettings, u as cacheProviderSettingError, w as publicUrlSetting, x as ngrokUrlSetting, y as metroTunnelSettingError } from "./settings-D030k0t4.mjs";
|
|
12
|
+
import { _ as stepTimer, l as phaseLine } from "./command-output-gByVmIjy.mjs";
|
|
13
|
+
import { n as workspaceProcessLockError, t as withWorkspaceProcessLock } from "./workspace-process-lock-DWaiRD0e.mjs";
|
|
13
14
|
import { t as spawnEntry } from "./spawn-entry-nCE6m770.mjs";
|
|
14
|
-
import {
|
|
15
|
-
import { a as
|
|
15
|
+
import { l as queryLogs } from "./logs-query-Cqma3WIu.mjs";
|
|
16
|
+
import { a as terminateChild, c as withManagedTunnelLock, i as stopTunnel, o as withManagedRemoteWorktreeLock, r as startTunnelSequence, t as resolveSupervisorTarget } from "./ownership-DCQPuWEc.mjs";
|
|
16
17
|
import { appendFileSync, mkdirSync, openSync, readFileSync } from "node:fs";
|
|
17
18
|
import { dirname } from "node:path";
|
|
18
19
|
import { EventEmitter } from "node:events";
|
|
19
|
-
import { CACHE_PROVIDER_ENV, cacheProviderEnv } from "@stim-cli/cache";
|
|
20
20
|
import chalk from "chalk";
|
|
21
|
+
import { CACHE_PROVIDER_ENV, cacheProviderEnv } from "@stim-cli/cache";
|
|
21
22
|
//#region src/supervisor/cache-reset.ts
|
|
22
23
|
async function stopOwnedMetroForReset(root) {
|
|
23
24
|
const project = getProject(root);
|
|
@@ -179,11 +180,7 @@ function registerStart(program, overrides = {}) {
|
|
|
179
180
|
});
|
|
180
181
|
const waitSeconds = wait.seconds;
|
|
181
182
|
const root = findProjectRoot(process.cwd());
|
|
182
|
-
if (!root) return fail(
|
|
183
|
-
code: "STIM_NO_PROJECT",
|
|
184
|
-
message: "Not in a React Native project (no package.json found).",
|
|
185
|
-
remedy: "Run this from the app directory -- the one holding package.json."
|
|
186
|
-
});
|
|
183
|
+
if (!root) return fail(NO_PROJECT_REFUSAL);
|
|
187
184
|
const projectProblem = appProjectProblem(root);
|
|
188
185
|
if (projectProblem) return fail({
|
|
189
186
|
code: "STIM_NO_PROJECT",
|
|
@@ -199,6 +196,7 @@ function registerStart(program, overrides = {}) {
|
|
|
199
196
|
remedy: "Check that STIM_HOME is writable and has free space. An EPERM on a directory you can write is a sandbox: allow writes to STIM_HOME, or run Stim with the sandbox disabled (`stim guide errors sandbox`)."
|
|
200
197
|
});
|
|
201
198
|
}
|
|
199
|
+
recordWorkspaceUse(root);
|
|
202
200
|
const isExpo = detectIsExpo(root);
|
|
203
201
|
const worktreeRoot = repoRoot(root) ?? root;
|
|
204
202
|
const settingsContext = {
|
|
@@ -222,13 +220,7 @@ function registerStart(program, overrides = {}) {
|
|
|
222
220
|
if (settingError) return fail({
|
|
223
221
|
code: "STIM_BAD_ARG",
|
|
224
222
|
message: settingError,
|
|
225
|
-
remedy:
|
|
226
|
-
});
|
|
227
|
-
const remoteSettingError = remoteDeviceSettingError(settings);
|
|
228
|
-
if (remoteSettingError) return fail({
|
|
229
|
-
code: "STIM_BAD_ARG",
|
|
230
|
-
message: remoteSettingError,
|
|
231
|
-
remedy: "Set ios.remote and android.remote to either proxy or eas."
|
|
223
|
+
remedy: "Set metro.tunnel to \"ngrok\" and metro.ngrokUrl to an HTTPS URL, or remove metro.ngrokUrl."
|
|
232
224
|
});
|
|
233
225
|
const remote = Boolean(opts.remote) || remoteIosSetting(settings) !== null || remoteAndroidSetting(settings) !== null;
|
|
234
226
|
const tunnelMode = tunnelModeSetting(settings) ?? "auto";
|
|
@@ -820,8 +812,15 @@ function registerStart(program, overrides = {}) {
|
|
|
820
812
|
});
|
|
821
813
|
}
|
|
822
814
|
async function resolvePort(root, note) {
|
|
823
|
-
const
|
|
815
|
+
const project = getProject(root);
|
|
816
|
+
const recorded = project?.metroPort;
|
|
824
817
|
if (!recorded) return await reserveMetroPort(root);
|
|
818
|
+
const supervisor = resolveSupervisorTarget({
|
|
819
|
+
state: readWorkspaceState(root)?.supervisor,
|
|
820
|
+
record: project.supervisor,
|
|
821
|
+
reservedPort: recorded
|
|
822
|
+
});
|
|
823
|
+
if (supervisor.status !== "none" && supervisor.status !== "stale") return recorded;
|
|
825
824
|
const held = await resolveProjectMetro(recorded, root);
|
|
826
825
|
if (!held.notOurs) return recorded;
|
|
827
826
|
const fresh = await reserveMetroPort(root);
|
|
@@ -859,11 +858,8 @@ function failureEvidence({ logFile, logsDir, sinceTs }) {
|
|
|
859
858
|
let errors = [];
|
|
860
859
|
let all = [];
|
|
861
860
|
try {
|
|
862
|
-
errors = queryLogs({
|
|
863
|
-
dir: logsDir,
|
|
864
|
-
minLevel: "error"
|
|
865
|
-
});
|
|
866
861
|
all = queryLogs({ dir: logsDir });
|
|
862
|
+
errors = all.filter((record) => levelRank(record.level) >= levelRank("error"));
|
|
867
863
|
} catch {}
|
|
868
864
|
const since = (rs) => rs.filter((r) => typeof r.ts === "number" && r.ts >= sinceTs).slice(-8);
|
|
869
865
|
let recent = since(errors);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { D as process_identity_exports, H as deviceSlotKey, W as parseDeviceSlotKey } from "./config-Cp9lM3wX.mjs";
|
|
2
|
+
import { S as workspaceStateFile, a as updateWorkspaceState, h as supervisorPidFile, o as withWorkspaceStateLock, r as readWorkspaceState, t as clearWorkspaceStateKey } from "./workspace-state-DF2gCFhx.mjs";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname } from "node:path";
|
|
5
5
|
//#region src/supervisor/state.ts
|
|
@@ -75,10 +75,12 @@ function readRemoteSession(root) {
|
|
|
75
75
|
if (typeof id !== "string" || id.length === 0) return null;
|
|
76
76
|
const platform = record.platform;
|
|
77
77
|
const startedAt = record.startedAt;
|
|
78
|
+
const webPreviewUrl = record.webPreviewUrl;
|
|
78
79
|
return {
|
|
79
80
|
platform: platform === "ios" || platform === "android" ? platform : null,
|
|
80
81
|
sessionId: id,
|
|
81
|
-
startedAt: typeof startedAt === "string" ? startedAt : null
|
|
82
|
+
startedAt: typeof startedAt === "string" ? startedAt : null,
|
|
83
|
+
webPreviewUrl: typeof webPreviewUrl === "string" && webPreviewUrl.length > 0 ? webPreviewUrl : null
|
|
82
84
|
};
|
|
83
85
|
}
|
|
84
86
|
function readRemoteSessionId(root) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { o as formatLongDuration } from "./command-output-
|
|
4
|
-
import { a as statsProjectKey, r as readStats } from "./stats-
|
|
1
|
+
import { _ as repoRoot, s as gitCommonDir } from "./worktree-R_cUlE7s.mjs";
|
|
2
|
+
import { o as findProjectRoot } from "./project-DS112DYB.mjs";
|
|
3
|
+
import { o as formatLongDuration } from "./command-output-gByVmIjy.mjs";
|
|
4
|
+
import { a as statsProjectKey, r as readStats } from "./stats-DG9e66Rz.mjs";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
//#region src/commands/stats.ts
|
|
7
7
|
const PLATFORMS = ["ios", "android"];
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { T as withConfigLock, u as getConfigDir } from "./config-Cp9lM3wX.mjs";
|
|
2
2
|
import { readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
4
4
|
const PLATFORMS = ["ios", "android"];
|
|
5
|
+
const OUTCOMES = ["hit", "cold"];
|
|
5
6
|
function statsFile() {
|
|
6
7
|
return join(getConfigDir(), "stats.json");
|
|
7
8
|
}
|
|
@@ -41,10 +42,25 @@ function updateStats(record, run, now) {
|
|
|
41
42
|
credit,
|
|
42
43
|
phases
|
|
43
44
|
});
|
|
45
|
+
const history = { ...record.history };
|
|
46
|
+
if (run.phases && !run.failed && !run.waitedForBuild) {
|
|
47
|
+
const outcome = isHit(run.cacheHit) ? "hit" : "cold";
|
|
48
|
+
const project = { ...history[run.projectKey] };
|
|
49
|
+
const lists = { ...project[run.platform] };
|
|
50
|
+
const sample = {
|
|
51
|
+
at,
|
|
52
|
+
durationMs,
|
|
53
|
+
phases: wholePhases(run.phases)
|
|
54
|
+
};
|
|
55
|
+
lists[outcome] = [...lists[outcome] ?? [], sample].slice(-10);
|
|
56
|
+
project[run.platform] = lists;
|
|
57
|
+
history[run.projectKey] = project;
|
|
58
|
+
}
|
|
44
59
|
return {
|
|
45
60
|
version: 1,
|
|
46
61
|
machine,
|
|
47
|
-
projects
|
|
62
|
+
projects,
|
|
63
|
+
...Object.keys(history).length ? { history } : {}
|
|
48
64
|
};
|
|
49
65
|
}
|
|
50
66
|
function readStats() {
|
|
@@ -108,7 +124,7 @@ function recordRunStats(run, now) {
|
|
|
108
124
|
};
|
|
109
125
|
});
|
|
110
126
|
}
|
|
111
|
-
function createRunRecorder({ platform, write, now, note }) {
|
|
127
|
+
function createRunRecorder({ platform, write, now, note, phases }) {
|
|
112
128
|
let projectKey = null;
|
|
113
129
|
let cacheKey = null;
|
|
114
130
|
let coldBuildMs = 0;
|
|
@@ -130,6 +146,7 @@ function createRunRecorder({ platform, write, now, note }) {
|
|
|
130
146
|
record({ failed, cacheHit = false, waited = null, durationMs }) {
|
|
131
147
|
if (!projectKey || !cacheKey || recorded) return;
|
|
132
148
|
recorded = true;
|
|
149
|
+
const ran = failed ? {} : phases?.() ?? {};
|
|
133
150
|
try {
|
|
134
151
|
const outcome = write({
|
|
135
152
|
platform,
|
|
@@ -139,7 +156,8 @@ function createRunRecorder({ platform, write, now, note }) {
|
|
|
139
156
|
waitedForBuild: Boolean(waited),
|
|
140
157
|
durationMs,
|
|
141
158
|
...coldBuildMs > 0 ? { coldBuildMs } : {},
|
|
142
|
-
...podsMs > 0 ? { podsMs } : {}
|
|
159
|
+
...podsMs > 0 ? { podsMs } : {},
|
|
160
|
+
...Object.keys(ran).length ? { phases: ran } : {}
|
|
143
161
|
}, now());
|
|
144
162
|
if (outcome?.note) note(outcome.note);
|
|
145
163
|
} catch (error) {
|
|
@@ -206,12 +224,55 @@ function normalize(record) {
|
|
|
206
224
|
const projects = {};
|
|
207
225
|
const source = record.projects;
|
|
208
226
|
if (source && typeof source === "object" && !Array.isArray(source)) for (const [key, scope] of Object.entries(source)) projects[key] = normalizeScope(scope);
|
|
227
|
+
const history = {};
|
|
228
|
+
if (isObject(record.history)) for (const [key, scope] of Object.entries(record.history)) {
|
|
229
|
+
const normalized = normalizeHistory(scope);
|
|
230
|
+
if (Object.keys(normalized).length) history[key] = normalized;
|
|
231
|
+
}
|
|
209
232
|
return {
|
|
210
233
|
version: 1,
|
|
211
234
|
machine: normalizeScope(record.machine),
|
|
212
|
-
projects
|
|
235
|
+
projects,
|
|
236
|
+
...Object.keys(history).length ? { history } : {}
|
|
213
237
|
};
|
|
214
238
|
}
|
|
239
|
+
function isObject(value) {
|
|
240
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
241
|
+
}
|
|
242
|
+
function normalizeHistory(scope) {
|
|
243
|
+
const history = {};
|
|
244
|
+
if (!isObject(scope)) return history;
|
|
245
|
+
for (const platform of PLATFORMS) {
|
|
246
|
+
const lists = scope[platform];
|
|
247
|
+
if (!isObject(lists)) continue;
|
|
248
|
+
const normalized = {};
|
|
249
|
+
for (const outcome of OUTCOMES) {
|
|
250
|
+
const samples = lists[outcome];
|
|
251
|
+
if (!Array.isArray(samples)) continue;
|
|
252
|
+
const kept = samples.filter(isObject).flatMap((sample) => {
|
|
253
|
+
const durationMs = wholeMs(sample.durationMs);
|
|
254
|
+
if (durationMs <= 0) return [];
|
|
255
|
+
return [{
|
|
256
|
+
at: timestamp(sample.at),
|
|
257
|
+
durationMs,
|
|
258
|
+
phases: wholePhases(sample.phases)
|
|
259
|
+
}];
|
|
260
|
+
});
|
|
261
|
+
if (kept.length) normalized[outcome] = kept.slice(-10);
|
|
262
|
+
}
|
|
263
|
+
if (Object.keys(normalized).length) history[platform] = normalized;
|
|
264
|
+
}
|
|
265
|
+
return history;
|
|
266
|
+
}
|
|
267
|
+
function wholePhases(phases) {
|
|
268
|
+
const out = {};
|
|
269
|
+
if (!isObject(phases)) return out;
|
|
270
|
+
for (const [name, ms] of Object.entries(phases)) {
|
|
271
|
+
const value = Number(ms);
|
|
272
|
+
if (Number.isFinite(value) && value >= 0) out[name] = Math.round(value);
|
|
273
|
+
}
|
|
274
|
+
return out;
|
|
275
|
+
}
|
|
215
276
|
function normalizeScope(scope) {
|
|
216
277
|
const normalized = {};
|
|
217
278
|
if (!scope || typeof scope !== "object" || Array.isArray(scope)) return normalized;
|