stim 1.0.0 → 1.2.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 +29 -10
- package/dist/android-BW8i_YxU.mjs +204 -0
- package/dist/android-Bcmev2s2.mjs +2334 -0
- package/dist/android-K63_0UfF.mjs +727 -0
- package/dist/android-cas-D6HPZ6UQ.mjs +1078 -0
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/app-install-NFxUahnH.mjs +1149 -0
- package/dist/build-lock-BsVoLDaq.mjs +645 -0
- package/dist/build-slots-D0b6sJyo.mjs +152 -0
- package/dist/{cache-manifest-4tH64LQ9.mjs → cache-manifest-Cu4_Znw9.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +24 -27166
- package/dist/collector-run.d.mts +1 -0
- package/dist/collector-run.mjs +7 -5
- package/dist/command-output-8wHMEde0.mjs +289 -0
- package/dist/{config-7kmtuhO2.mjs → config-CMtIaTk9.mjs} +44 -13
- package/dist/deps-BLBsTNDS.mjs +435 -0
- package/dist/dev-client-RpvXxCXV.mjs +620 -0
- package/dist/device-CoCPmazS.mjs +250 -0
- package/dist/device-lease-Dpik41RY.mjs +335 -0
- package/dist/device-pool-B5pEVa3t.mjs +408 -0
- package/dist/device-remote-E81KAdXf.mjs +1209 -0
- package/dist/doctor-CqPeMsSm.mjs +1130 -0
- package/dist/doctor-D_CX7ib5.mjs +530 -0
- package/dist/error-diagnostics-Dw8Gn3yw.mjs +636 -0
- package/dist/{exec-bsN9MJXb.mjs → exec-CyylIdq9.mjs} +4 -2
- package/dist/gc-B7KkDinF.mjs +1665 -0
- package/dist/guide-BS2Xc24o.mjs +4503 -0
- package/dist/ios-B7KLgIp5.mjs +2733 -0
- package/dist/ios-D1h1Di39.mjs +451 -0
- package/dist/ios-device-D3pzTKQE.mjs +81 -0
- package/dist/ios-device-xNt0Lg6v.mjs +413 -0
- package/dist/logs-DcX9AvRF.mjs +176 -0
- package/dist/logs-query-oqn47Ffx.mjs +273 -0
- package/dist/metro-COslRK_F.mjs +184 -0
- package/dist/metro-store-CDDZHtPl.mjs +92 -0
- package/dist/ownership-CGPart6T.mjs +60 -0
- package/dist/ownership-CYOyqxUo.mjs +475 -0
- package/dist/ownership-claim-7zRxxTfZ.mjs +569 -0
- package/dist/{project-Dit1CckW.mjs → project-BBTVABUz.mjs} +12 -3
- package/dist/reclaim-C5Jmuahx.mjs +348 -0
- package/dist/reload-Z8nKifAk.mjs +332 -0
- package/dist/remote-cache-DDmJ1Roq.mjs +994 -0
- package/dist/{server-bare-BmTVVmBB.mjs → server-bare-DEk37Mz2.mjs} +8 -2
- package/dist/{server-expo-CHJe1r6j.mjs → server-expo-C3ktAwUG.mjs} +6 -5
- package/dist/server-expo-Drd7ksHu.mjs +2 -0
- package/dist/settings-DwU1_U_q.mjs +708 -0
- package/dist/spawn-entry-nCE6m770.mjs +14 -0
- package/dist/start-BrY4EuIg.mjs +822 -0
- package/dist/{state-2nush3MK.mjs → state-CZ-ER-8d.mjs} +21 -22
- package/dist/stats-BCWEVLrM.mjs +307 -0
- package/dist/stats-BsF15Bti.mjs +62 -0
- package/dist/status-DFI1Hfpg.mjs +393 -0
- package/dist/stop-B7DR4awK.mjs +3 -0
- package/dist/stop-Btg770vV.mjs +591 -0
- package/dist/supervisor-run.d.mts +0 -1
- package/dist/supervisor-run.mjs +4 -4
- package/dist/workspace-process-lock-BKfdys1q.mjs +47 -0
- package/dist/worktree-BPTbI-LN.mjs +1322 -0
- package/dist/worktree-C79xx_kM.mjs +543 -0
- package/dist/xcode-DeKCribl.mjs +1832 -0
- package/package.json +5 -5
- package/shim/bundle-response.cjs +5 -0
- package/dist/android-jc8MnUyL.mjs +0 -514
- package/dist/metro-store-COLl1pOk.mjs +0 -1249
- package/dist/server-expo-BZJm--wA.mjs +0 -2
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
+
import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-C7IlIzd7.mjs";
|
|
3
|
+
import { mkdtempSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
//#region src/host-memory.ts
|
|
7
|
+
function readHostMemoryPressure(exec = getExecutor(), platform = process.platform) {
|
|
8
|
+
if (platform !== "darwin") return null;
|
|
9
|
+
try {
|
|
10
|
+
switch (exec.runFile("/usr/sbin/sysctl", ["-n", "kern.memorystatus_vm_pressure_level"], { timeoutMs: 2e3 }).trim()) {
|
|
11
|
+
case "1": return "normal";
|
|
12
|
+
case "2": return "warning";
|
|
13
|
+
case "4": return "critical";
|
|
14
|
+
default: return null;
|
|
15
|
+
}
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function hostMemoryPressureAdvice(pressure) {
|
|
21
|
+
if (pressure === null || pressure === "normal") return null;
|
|
22
|
+
return `macOS reports ${pressure} host memory pressure. Simulator processes can stall even when the device is Booted. Free memory before retrying: use \`stim stop\` only in workspaces you own, and ask before closing other apps or devices. For parallel iOS work, consider a reviewed SimSlim profile (\`stim guide lifecycle simslim\`). This observation does not establish an OOM crash.`;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/sim/ios.ts
|
|
26
|
+
function parseSimctlList(jsonOutput, { includeUnavailable = false } = {}) {
|
|
27
|
+
const parsed = JSON.parse(jsonOutput);
|
|
28
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("Expected simctl list to return a JSON object with a devices object.");
|
|
29
|
+
const data = parsed;
|
|
30
|
+
if (data.devices === null || typeof data.devices !== "object" || Array.isArray(data.devices)) throw new Error("Expected simctl list to return a JSON object with a devices object.");
|
|
31
|
+
const sims = [];
|
|
32
|
+
for (const [runtime, devices] of Object.entries(data.devices)) {
|
|
33
|
+
if (!Array.isArray(devices)) throw new Error(`Expected simctl devices for ${runtime} to be an array.`);
|
|
34
|
+
if (!/\.iOS-/.test(runtime)) continue;
|
|
35
|
+
for (const dev of devices) {
|
|
36
|
+
if (dev === null || typeof dev !== "object" || Array.isArray(dev)) throw new Error(`Expected every simctl device for ${runtime} to be an object.`);
|
|
37
|
+
const record = dev;
|
|
38
|
+
for (const field of [
|
|
39
|
+
"udid",
|
|
40
|
+
"name",
|
|
41
|
+
"state",
|
|
42
|
+
"deviceTypeIdentifier"
|
|
43
|
+
]) if (typeof record[field] !== "string" || record[field].length === 0) throw new Error(`Expected simctl device field ${field} for ${runtime} to be a non-empty string.`);
|
|
44
|
+
if (typeof record.isAvailable !== "boolean") throw new Error(`Expected simctl device field isAvailable for ${runtime} to be a boolean.`);
|
|
45
|
+
if (record.dataPath !== void 0 && typeof record.dataPath !== "string") throw new Error(`Expected simctl device field dataPath for ${runtime} to be a string.`);
|
|
46
|
+
if (record.dataPathSize !== void 0 && (typeof record.dataPathSize !== "number" || !Number.isFinite(record.dataPathSize))) throw new Error(`Expected simctl device field dataPathSize for ${runtime} to be a finite number.`);
|
|
47
|
+
const typed = record;
|
|
48
|
+
const available = Boolean(typed.isAvailable);
|
|
49
|
+
if (!available && !includeUnavailable) continue;
|
|
50
|
+
sims.push({
|
|
51
|
+
udid: typed.udid,
|
|
52
|
+
name: typed.name,
|
|
53
|
+
state: typed.state,
|
|
54
|
+
runtime,
|
|
55
|
+
deviceTypeIdentifier: typed.deviceTypeIdentifier,
|
|
56
|
+
available,
|
|
57
|
+
...typed.dataPath !== void 0 ? { dataPath: typed.dataPath } : {},
|
|
58
|
+
...typed.dataPathSize !== void 0 ? { dataPathSize: typed.dataPathSize } : {}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return sims;
|
|
63
|
+
}
|
|
64
|
+
function listAllIosSims({ timeoutMs = 3e4, includeUnavailable = false } = {}) {
|
|
65
|
+
return parseSimctlList(getExecutor().runFile("xcrun", [
|
|
66
|
+
"simctl",
|
|
67
|
+
"list",
|
|
68
|
+
"devices",
|
|
69
|
+
"--json"
|
|
70
|
+
], {
|
|
71
|
+
timeoutMs,
|
|
72
|
+
killSignal: "SIGKILL"
|
|
73
|
+
}), { includeUnavailable });
|
|
74
|
+
}
|
|
75
|
+
function parseRuntimeVersion(runtimeId) {
|
|
76
|
+
const m = runtimeId.match(/iOS-(\d+)(?:-(\d+))?$/);
|
|
77
|
+
if (!m) return runtimeId;
|
|
78
|
+
const major = m[1];
|
|
79
|
+
if (major === void 0) return runtimeId;
|
|
80
|
+
const minor = m[2];
|
|
81
|
+
return minor ? `${major}.${minor}` : major;
|
|
82
|
+
}
|
|
83
|
+
const IOS_BOOT_TIMEOUT_MS = 6e5;
|
|
84
|
+
const BOOTSTATUS_ATTEMPT_MS = 24e4;
|
|
85
|
+
const BOOTSTATUS_ATTEMPT_FLOOR_MS = 1e3;
|
|
86
|
+
const BOOT_STATE_LIST_TIMEOUT_MS = 3e4;
|
|
87
|
+
function iosSimulatorFailureAdvice(exec = getExecutor()) {
|
|
88
|
+
return hostMemoryPressureAdvice(readHostMemoryPressure(exec)) ?? "The simulator may be unresponsive. Check Activity Monitor for memory pressure and free host memory before retrying.";
|
|
89
|
+
}
|
|
90
|
+
function bootstatusTimeout(udid) {
|
|
91
|
+
const e = /* @__PURE__ */ new Error(`xcrun simctl bootstatus ${udid} -b timed out`);
|
|
92
|
+
e.code = "ETIMEDOUT";
|
|
93
|
+
return e;
|
|
94
|
+
}
|
|
95
|
+
async function awaitBootstatus(udid, attemptMs) {
|
|
96
|
+
const lines = [];
|
|
97
|
+
const reader = createLineReader((raw) => {
|
|
98
|
+
const line = stripAnsi(raw).trim();
|
|
99
|
+
if (!line) return;
|
|
100
|
+
lines.push(line);
|
|
101
|
+
if (lines.length > 10) lines.shift();
|
|
102
|
+
});
|
|
103
|
+
const child = getExecutor().spawn("xcrun", [
|
|
104
|
+
"simctl",
|
|
105
|
+
"bootstatus",
|
|
106
|
+
udid,
|
|
107
|
+
"-b"
|
|
108
|
+
], { stdio: [
|
|
109
|
+
"ignore",
|
|
110
|
+
"pipe",
|
|
111
|
+
"pipe"
|
|
112
|
+
] });
|
|
113
|
+
child.stdout?.on("data", (chunk) => reader.push(chunk));
|
|
114
|
+
child.stderr?.on("data", (chunk) => reader.push(chunk));
|
|
115
|
+
let timer;
|
|
116
|
+
const attempt = new Promise((resolve) => {
|
|
117
|
+
timer = setTimeout(() => resolve("timeout"), attemptMs);
|
|
118
|
+
});
|
|
119
|
+
const exited = new Promise((resolve) => child.once("exit", () => resolve()));
|
|
120
|
+
const result = await Promise.race([waitForChild(child), attempt]);
|
|
121
|
+
clearTimeout(timer);
|
|
122
|
+
reader.flush();
|
|
123
|
+
if (result === "timeout") {
|
|
124
|
+
try {
|
|
125
|
+
child.kill("SIGKILL");
|
|
126
|
+
} catch {}
|
|
127
|
+
const stopped = await Promise.race([exited.then(() => true), new Promise((resolve) => {
|
|
128
|
+
timer = setTimeout(() => resolve(false), 1e4);
|
|
129
|
+
})]);
|
|
130
|
+
clearTimeout(timer);
|
|
131
|
+
if (!stopped) {
|
|
132
|
+
child.stdout?.destroy?.();
|
|
133
|
+
child.stderr?.destroy?.();
|
|
134
|
+
child.unref();
|
|
135
|
+
throw new Error(`Timed out waiting for simctl bootstatus ${udid}; its process could not be confirmed stopped. Inspect pid ${child.pid ?? "unknown"} before retrying.`);
|
|
136
|
+
}
|
|
137
|
+
throw bootstatusTimeout(udid);
|
|
138
|
+
}
|
|
139
|
+
if (result.error) throw result.error;
|
|
140
|
+
if (result.code === 0) return;
|
|
141
|
+
const detail = lines.length ? `: ${lines.join(" | ")}` : "";
|
|
142
|
+
throw new Error(`xcrun simctl bootstatus ${udid} -b failed with exit code ${result.code ?? "unknown"}${detail}`);
|
|
143
|
+
}
|
|
144
|
+
async function bootIosSim(udid, { timeoutMs = IOS_BOOT_TIMEOUT_MS, attemptMs = BOOTSTATUS_ATTEMPT_MS } = {}) {
|
|
145
|
+
const exec = getExecutor();
|
|
146
|
+
const deadline = Date.now() + timeoutMs;
|
|
147
|
+
try {
|
|
148
|
+
exec.runFile("xcrun", [
|
|
149
|
+
"simctl",
|
|
150
|
+
"boot",
|
|
151
|
+
udid
|
|
152
|
+
], {
|
|
153
|
+
timeoutMs,
|
|
154
|
+
killSignal: "SIGKILL"
|
|
155
|
+
});
|
|
156
|
+
} catch (e) {
|
|
157
|
+
if (!String(e?.message || e).includes("Booted")) throw e;
|
|
158
|
+
}
|
|
159
|
+
for (;;) {
|
|
160
|
+
const remaining = deadline - Date.now();
|
|
161
|
+
if (remaining > 0) try {
|
|
162
|
+
await awaitBootstatus(udid, Math.min(Math.max(remaining, BOOTSTATUS_ATTEMPT_FLOOR_MS), attemptMs));
|
|
163
|
+
break;
|
|
164
|
+
} catch (e) {
|
|
165
|
+
if (e?.code !== "ETIMEDOUT") throw e;
|
|
166
|
+
}
|
|
167
|
+
let state;
|
|
168
|
+
try {
|
|
169
|
+
state = listAllIosSims({ timeoutMs: BOOT_STATE_LIST_TIMEOUT_MS }).find((s) => s.udid === udid)?.state ?? null;
|
|
170
|
+
} catch {
|
|
171
|
+
state = void 0;
|
|
172
|
+
}
|
|
173
|
+
if (state === "Booted") break;
|
|
174
|
+
const waited = Math.round((timeoutMs - Math.max(0, deadline - Date.now())) / 1e3);
|
|
175
|
+
if (state !== "Booting" && state !== void 0) throw new Error(`Simulator ${udid} reports "${state ?? "missing"}" after ${waited}s of boot wait.`);
|
|
176
|
+
if (deadline - Date.now() <= 0) throw new Error(`Simulator ${udid} did not finish booting within ${Math.round(timeoutMs / 1e3)}s.`);
|
|
177
|
+
}
|
|
178
|
+
exec.runFileQuiet("open", ["-a", "Simulator"], {
|
|
179
|
+
timeoutMs: 5e3,
|
|
180
|
+
killSignal: "SIGKILL"
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function shutdownIosSim(udid) {
|
|
184
|
+
getExecutor().runQuiet(`xcrun simctl shutdown ${udid}`);
|
|
185
|
+
}
|
|
186
|
+
function listIosDeviceTypes() {
|
|
187
|
+
const out = getExecutor().runFile("xcrun", [
|
|
188
|
+
"simctl",
|
|
189
|
+
"list",
|
|
190
|
+
"devicetypes",
|
|
191
|
+
"--json"
|
|
192
|
+
], {
|
|
193
|
+
timeoutMs: 3e4,
|
|
194
|
+
killSignal: "SIGKILL"
|
|
195
|
+
});
|
|
196
|
+
return (JSON.parse(out).devicetypes || []).map((dt) => ({
|
|
197
|
+
identifier: dt.identifier,
|
|
198
|
+
name: dt.name
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
function rankIphone(name) {
|
|
202
|
+
const gen = /^iPhone\s+(\d+)/i.exec(name);
|
|
203
|
+
return {
|
|
204
|
+
gen: gen ? Number(gen[1]) : -1,
|
|
205
|
+
variant: -name.length
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function iosRuntimeMatches(runtime, requested) {
|
|
209
|
+
return runtime.version === requested || runtime.name === requested;
|
|
210
|
+
}
|
|
211
|
+
function pickDefaultIosCreation(_deviceTypes, runtimes, { deviceType, runtime } = {}) {
|
|
212
|
+
const rts = runtimes.toSorted((a, b) => String(b.version).localeCompare(String(a.version), void 0, { numeric: true }));
|
|
213
|
+
const wantedRts = runtime ? rts.filter((r) => iosRuntimeMatches(r, runtime)) : rts;
|
|
214
|
+
for (const rt of wantedRts) {
|
|
215
|
+
const supported = (rt.supportedDeviceTypes || []).filter((d) => deviceType ? d.name === deviceType : /^iPhone/i.test(d.name));
|
|
216
|
+
if (supported.length === 0) continue;
|
|
217
|
+
const best = supported.toSorted((a, b) => {
|
|
218
|
+
const ra = rankIphone(a.name), rb = rankIphone(b.name);
|
|
219
|
+
return rb.gen - ra.gen || rb.variant - ra.variant || b.name.localeCompare(a.name, void 0, { numeric: true });
|
|
220
|
+
})[0];
|
|
221
|
+
if (best === void 0) continue;
|
|
222
|
+
return {
|
|
223
|
+
deviceTypeId: best.identifier,
|
|
224
|
+
runtimeId: rt.identifier
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
function sanitizeDeviceLabel(label) {
|
|
230
|
+
return String(label).replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
231
|
+
}
|
|
232
|
+
const SIM_NAME_MAX = 60;
|
|
233
|
+
const PARKED_SIM_LABEL = "parked";
|
|
234
|
+
function fitSimName(label, { model, runtime }, suffix = "", preserveLabel = false) {
|
|
235
|
+
const version = runtime ?? "";
|
|
236
|
+
const modelName = model ?? "";
|
|
237
|
+
if (!version && !modelName) return `stim-${label}${suffix}`;
|
|
238
|
+
let over = `stim-`.length + 2 + 1 + 1 + version.length + suffix.length + label.length + modelName.length - SIM_NAME_MAX;
|
|
239
|
+
let shortLabel = label;
|
|
240
|
+
if (over > 0 && !preserveLabel) {
|
|
241
|
+
const cut = Math.min(over, label.length);
|
|
242
|
+
shortLabel = label.slice(0, label.length - cut).replace(/[._-]+$/g, "");
|
|
243
|
+
over -= cut;
|
|
244
|
+
}
|
|
245
|
+
const shortModel = over > 0 ? modelName.slice(0, Math.max(0, modelName.length - over)) : modelName;
|
|
246
|
+
return `stim-${shortLabel} (${shortModel} ${version})${suffix}`;
|
|
247
|
+
}
|
|
248
|
+
function ownedSimName(label, model = {
|
|
249
|
+
model: null,
|
|
250
|
+
runtime: null
|
|
251
|
+
}, suffix = "") {
|
|
252
|
+
const clean = sanitizeDeviceLabel(label);
|
|
253
|
+
return fitSimName(clean.startsWith("stim-") ? clean.slice(5) : clean, model, suffix);
|
|
254
|
+
}
|
|
255
|
+
function parkedSimName(udid, model) {
|
|
256
|
+
return fitSimName(PARKED_SIM_LABEL, model, ` ${udid.replace(/-/g, "").slice(0, 4).toLowerCase()}`, true);
|
|
257
|
+
}
|
|
258
|
+
function resolveIosCreation({ deviceType, runtime } = {}) {
|
|
259
|
+
const deviceTypes = listIosDeviceTypes();
|
|
260
|
+
const runtimes = listIosRuntimes();
|
|
261
|
+
const pick = pickDefaultIosCreation(deviceTypes, runtimes, {
|
|
262
|
+
deviceType,
|
|
263
|
+
runtime
|
|
264
|
+
});
|
|
265
|
+
if (!pick) throw new Error("No matching simulator device type / runtime is installed. Install one via Xcode, or pass --device-type / --runtime.");
|
|
266
|
+
return {
|
|
267
|
+
deviceTypeId: pick.deviceTypeId,
|
|
268
|
+
runtimeId: pick.runtimeId,
|
|
269
|
+
deviceType: deviceTypes.find((d) => d.identifier === pick.deviceTypeId)?.name ?? null,
|
|
270
|
+
runtime: runtimes.find((r) => r.identifier === pick.runtimeId)?.version ?? null
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function createOwnedIosSim(label, { deviceType, runtime, suffix = "" } = {}, choice = resolveIosCreation({
|
|
274
|
+
deviceType,
|
|
275
|
+
runtime
|
|
276
|
+
})) {
|
|
277
|
+
const name = ownedSimName(label, {
|
|
278
|
+
model: choice.deviceType,
|
|
279
|
+
runtime: choice.runtime
|
|
280
|
+
}, suffix);
|
|
281
|
+
return {
|
|
282
|
+
udid: getExecutor().run(`xcrun simctl create "${name}" "${choice.deviceTypeId}" "${choice.runtimeId}"`).trim(),
|
|
283
|
+
name,
|
|
284
|
+
deviceType: choice.deviceType,
|
|
285
|
+
runtime: choice.runtime
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function renameIosSim(udid, name) {
|
|
289
|
+
getExecutor().runFile("xcrun", [
|
|
290
|
+
"simctl",
|
|
291
|
+
"rename",
|
|
292
|
+
udid,
|
|
293
|
+
name
|
|
294
|
+
]);
|
|
295
|
+
}
|
|
296
|
+
function resetIosPrivacy(udid) {
|
|
297
|
+
getExecutor().runFile("xcrun", [
|
|
298
|
+
"simctl",
|
|
299
|
+
"privacy",
|
|
300
|
+
udid,
|
|
301
|
+
"reset",
|
|
302
|
+
"all"
|
|
303
|
+
]);
|
|
304
|
+
}
|
|
305
|
+
function resetIosKeychain(udid) {
|
|
306
|
+
getExecutor().runFile("xcrun", [
|
|
307
|
+
"simctl",
|
|
308
|
+
"keychain",
|
|
309
|
+
udid,
|
|
310
|
+
"reset"
|
|
311
|
+
]);
|
|
312
|
+
}
|
|
313
|
+
function uninstallIosApp(udid, bundleId) {
|
|
314
|
+
getExecutor().runFile("xcrun", [
|
|
315
|
+
"simctl",
|
|
316
|
+
"uninstall",
|
|
317
|
+
udid,
|
|
318
|
+
bundleId
|
|
319
|
+
]);
|
|
320
|
+
}
|
|
321
|
+
function parseUserApps(jsonOutput) {
|
|
322
|
+
const data = JSON.parse(jsonOutput);
|
|
323
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) throw new Error("Expected simctl listapps to convert to a JSON object.");
|
|
324
|
+
const userApps = [];
|
|
325
|
+
for (const [bundleId, app] of Object.entries(data)) {
|
|
326
|
+
if (app === null || typeof app !== "object" || Array.isArray(app)) throw new Error(`Expected simctl listapps entry ${JSON.stringify(bundleId)} to be an object.`);
|
|
327
|
+
const applicationType = app.ApplicationType;
|
|
328
|
+
if (applicationType !== "User" && applicationType !== "System") throw new Error(`Expected simctl listapps entry ${JSON.stringify(bundleId)} to name a known ApplicationType.`);
|
|
329
|
+
if (applicationType === "User") userApps.push(bundleId);
|
|
330
|
+
}
|
|
331
|
+
return userApps;
|
|
332
|
+
}
|
|
333
|
+
const LISTAPPS_TIMEOUT_MS = 6e4;
|
|
334
|
+
function listUserApps(udid) {
|
|
335
|
+
const exec = getExecutor();
|
|
336
|
+
const dir = mkdtempSync(join(tmpdir(), "stim-listapps-"));
|
|
337
|
+
const plist = join(dir, "apps.plist");
|
|
338
|
+
try {
|
|
339
|
+
writeFileSync(plist, exec.runFile("xcrun", [
|
|
340
|
+
"simctl",
|
|
341
|
+
"listapps",
|
|
342
|
+
udid
|
|
343
|
+
], { timeoutMs: LISTAPPS_TIMEOUT_MS }));
|
|
344
|
+
return parseUserApps(exec.runFile("plutil", [
|
|
345
|
+
"-convert",
|
|
346
|
+
"json",
|
|
347
|
+
"-o",
|
|
348
|
+
"-",
|
|
349
|
+
plist
|
|
350
|
+
]));
|
|
351
|
+
} finally {
|
|
352
|
+
rmSync(dir, {
|
|
353
|
+
recursive: true,
|
|
354
|
+
force: true
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const CONTAINER_METADATA = ".com.apple.mobile_container_manager.metadata.plist";
|
|
359
|
+
const CLEARED_CONTAINER_DIRS = [
|
|
360
|
+
"Documents",
|
|
361
|
+
"Library",
|
|
362
|
+
"tmp",
|
|
363
|
+
"SystemData"
|
|
364
|
+
];
|
|
365
|
+
const PLUTIL_TIMEOUT_MS = 1e4;
|
|
366
|
+
function findAppDataContainer(dataPath, bundleId) {
|
|
367
|
+
const root = join(dataPath, "Containers", "Data", "Application");
|
|
368
|
+
let entries;
|
|
369
|
+
try {
|
|
370
|
+
entries = readdirSync(root);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
if (error?.code === "ENOENT") return null;
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
const exec = getExecutor();
|
|
376
|
+
for (const entry of entries) {
|
|
377
|
+
const dir = join(root, entry);
|
|
378
|
+
if (exec.runFile("plutil", [
|
|
379
|
+
"-extract",
|
|
380
|
+
"MCMMetadataIdentifier",
|
|
381
|
+
"raw",
|
|
382
|
+
join(dir, CONTAINER_METADATA)
|
|
383
|
+
], { timeoutMs: PLUTIL_TIMEOUT_MS }).trim() === bundleId) return dir;
|
|
384
|
+
}
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
function clearAppDataContainer(container) {
|
|
388
|
+
for (const name of CLEARED_CONTAINER_DIRS) {
|
|
389
|
+
const dir = join(container, name);
|
|
390
|
+
let children;
|
|
391
|
+
try {
|
|
392
|
+
if (!statSync(dir).isDirectory()) throw new Error(`Expected app data path ${dir} to be a directory.`);
|
|
393
|
+
children = readdirSync(dir);
|
|
394
|
+
} catch (error) {
|
|
395
|
+
if (error?.code === "ENOENT") continue;
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
for (const child of children) rmSync(join(dir, child), {
|
|
399
|
+
recursive: true,
|
|
400
|
+
force: true
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function resolveOwnedIosSim(udid) {
|
|
405
|
+
const sim = listAllIosSims().find((s) => s.udid === udid);
|
|
406
|
+
if (!sim) return { missing: true };
|
|
407
|
+
if (!sim.name?.startsWith("stim-")) return { notOwned: sim.name };
|
|
408
|
+
return { sim };
|
|
409
|
+
}
|
|
410
|
+
function deleteIosSim(udid) {
|
|
411
|
+
const result = resolveOwnedIosSim(udid);
|
|
412
|
+
if (result.missing) return;
|
|
413
|
+
if (result.notOwned) throw new Error(`Refusing to delete simulator "${result.notOwned}" (${udid}): not a Stim-owned sim (name must start with "stim-").`);
|
|
414
|
+
getExecutor().run(`xcrun simctl delete ${udid}`);
|
|
415
|
+
}
|
|
416
|
+
const PARKED_DELETE_TIMEOUT_MS = 3e4;
|
|
417
|
+
function deleteParkedIosSim(udid) {
|
|
418
|
+
const sim = listAllIosSims({
|
|
419
|
+
includeUnavailable: true,
|
|
420
|
+
timeoutMs: PARKED_DELETE_TIMEOUT_MS
|
|
421
|
+
}).find((entry) => entry.udid === udid);
|
|
422
|
+
if (!sim) return;
|
|
423
|
+
if (!sim.name.startsWith("stim-")) throw new Error(`Simulator ${udid} is now named "${sim.name}" and is not Stim-owned; refusing to delete it.`);
|
|
424
|
+
getExecutor().runFile("xcrun", [
|
|
425
|
+
"simctl",
|
|
426
|
+
"delete",
|
|
427
|
+
udid
|
|
428
|
+
], { timeoutMs: PARKED_DELETE_TIMEOUT_MS });
|
|
429
|
+
}
|
|
430
|
+
function listIosRuntimes() {
|
|
431
|
+
const out = getExecutor().runFile("xcrun", [
|
|
432
|
+
"simctl",
|
|
433
|
+
"list",
|
|
434
|
+
"runtimes",
|
|
435
|
+
"--json"
|
|
436
|
+
], {
|
|
437
|
+
timeoutMs: 3e4,
|
|
438
|
+
killSignal: "SIGKILL"
|
|
439
|
+
});
|
|
440
|
+
return (JSON.parse(out).runtimes || []).filter((r) => r.isAvailable && r.platform === "iOS").map((r) => ({
|
|
441
|
+
identifier: r.identifier,
|
|
442
|
+
name: r.name,
|
|
443
|
+
version: r.version,
|
|
444
|
+
supportedDeviceTypes: (r.supportedDeviceTypes || []).map((d) => ({
|
|
445
|
+
identifier: d.identifier,
|
|
446
|
+
name: d.name
|
|
447
|
+
}))
|
|
448
|
+
}));
|
|
449
|
+
}
|
|
450
|
+
//#endregion
|
|
451
|
+
export { shutdownIosSim as C, readHostMemoryPressure as E, sanitizeDeviceLabel as S, hostMemoryPressureAdvice as T, renameIosSim as _, deleteIosSim as a, resolveIosCreation as b, iosRuntimeMatches as c, listIosDeviceTypes as d, listIosRuntimes as f, parseRuntimeVersion as g, parkedSimName as h, createOwnedIosSim as i, iosSimulatorFailureAdvice as l, ownedSimName as m, bootIosSim as n, deleteParkedIosSim as o, listUserApps as p, clearAppDataContainer as r, findAppDataContainer as s, IOS_BOOT_TIMEOUT_MS as t, listAllIosSims as u, resetIosKeychain as v, uninstallIosApp as w, resolveOwnedIosSim as x, resetIosPrivacy as y };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
+
//#region src/collector/ios-device.ts
|
|
3
|
+
const CONSOLE_ENV = { OS_ACTIVITY_DT_MODE: "enable" };
|
|
4
|
+
const DEV_MENU_LAUNCH_ARGS = [
|
|
5
|
+
"-EXDevMenuShowsAtLaunch",
|
|
6
|
+
"0",
|
|
7
|
+
"-EXDevMenuShowFloatingActionButton",
|
|
8
|
+
"0"
|
|
9
|
+
];
|
|
10
|
+
const FATAL_MARKERS = [
|
|
11
|
+
/^\*\*\* Terminating app due to uncaught exception/,
|
|
12
|
+
/^libc\+\+abi: terminating/,
|
|
13
|
+
/^\*\*\* Assertion failure in/,
|
|
14
|
+
/^(?:\S+:\d+: )?Fatal error: /
|
|
15
|
+
];
|
|
16
|
+
const MIRROR_LINE = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+[+-]\d{4}) (.+?)\[(\d+):(\d+)\] ?(?:\[([^\]\s]+)\] )?([\s\S]*)$/;
|
|
17
|
+
function deviceConsoleLevel(message) {
|
|
18
|
+
const text = typeof message === "string" ? message : "";
|
|
19
|
+
return FATAL_MARKERS.some((marker) => marker.test(text)) ? "fatal" : "info";
|
|
20
|
+
}
|
|
21
|
+
function parseDeviceConsoleLine(line, { now = Date.now } = {}) {
|
|
22
|
+
if (typeof line !== "string") return null;
|
|
23
|
+
const text = line.replace(/\r$/, "").trimEnd();
|
|
24
|
+
if (!text.trim()) return null;
|
|
25
|
+
const match = MIRROR_LINE.exec(text);
|
|
26
|
+
const record = {
|
|
27
|
+
ts: now(),
|
|
28
|
+
src: "device",
|
|
29
|
+
level: "info",
|
|
30
|
+
msg: text,
|
|
31
|
+
raw: true
|
|
32
|
+
};
|
|
33
|
+
if (!match) {
|
|
34
|
+
record.level = text.startsWith("ERROR: ") ? "error" : deviceConsoleLevel(text);
|
|
35
|
+
return record;
|
|
36
|
+
}
|
|
37
|
+
const [, timestamp, proc, pid, , category, message] = match;
|
|
38
|
+
if (message === void 0 || !message.trim()) return null;
|
|
39
|
+
const parsed = Date.parse(timestamp);
|
|
40
|
+
if (Number.isFinite(parsed)) record.ts = parsed;
|
|
41
|
+
record.msg = message;
|
|
42
|
+
record.level = deviceConsoleLevel(message);
|
|
43
|
+
record.proc = `${proc}(${pid})`;
|
|
44
|
+
if (category) record.category = category;
|
|
45
|
+
return record;
|
|
46
|
+
}
|
|
47
|
+
function deviceConsoleArgs({ udid, bundleId, payloadUrl = null }) {
|
|
48
|
+
const args = [
|
|
49
|
+
"devicectl",
|
|
50
|
+
"device",
|
|
51
|
+
"process",
|
|
52
|
+
"launch",
|
|
53
|
+
"--quiet",
|
|
54
|
+
"--device",
|
|
55
|
+
udid,
|
|
56
|
+
"--console",
|
|
57
|
+
"--terminate-existing",
|
|
58
|
+
"--environment-variables",
|
|
59
|
+
JSON.stringify(CONSOLE_ENV)
|
|
60
|
+
];
|
|
61
|
+
if (payloadUrl) args.push("--payload-url", payloadUrl);
|
|
62
|
+
args.push(bundleId);
|
|
63
|
+
if (payloadUrl) args.push("--", ...DEV_MENU_LAUNCH_ARGS);
|
|
64
|
+
return args;
|
|
65
|
+
}
|
|
66
|
+
function startIosDeviceConsole({ udid, bundleId, payloadUrl = null, spawnFn = null }) {
|
|
67
|
+
return (spawnFn || ((cmd, args, opts) => getExecutor().spawn(cmd, args, opts)))("xcrun", deviceConsoleArgs({
|
|
68
|
+
udid,
|
|
69
|
+
bundleId,
|
|
70
|
+
payloadUrl
|
|
71
|
+
}), {
|
|
72
|
+
stdio: [
|
|
73
|
+
"ignore",
|
|
74
|
+
"pipe",
|
|
75
|
+
"pipe"
|
|
76
|
+
],
|
|
77
|
+
detached: false
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { startIosDeviceConsole as i, deviceConsoleLevel as n, parseDeviceConsoleLine as r, DEV_MENU_LAUNCH_ARGS as t };
|