stim 1.3.1 → 1.5.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 +16 -3
- package/dist/{android-hMj83VpN.mjs → android-DJWiTBkz.mjs} +65 -72
- package/dist/{android-cas-DXorPCi_.mjs → android-cas-CSS8gjR7.mjs} +15 -12
- package/dist/{android-CT-PkXUh.mjs → android-sobvO9ia.mjs} +197 -114
- package/dist/{app-install-DIp9riNx.mjs → app-install--PAocWvq.mjs} +2 -2
- package/dist/{build-slots-C4AouyNz.mjs → build-slots-CuPszJDu.mjs} +7 -8
- package/dist/{cache-manifest-CXR-Y8pH.mjs → cache-manifest-uoOKhgUD.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +14 -13
- package/dist/collector-run.mjs +4 -4
- package/dist/{command-output-8wHMEde0.mjs → command-output-BuIb_ZOI.mjs} +1 -4
- package/dist/{config-CcefMY2Q.mjs → config-DMP7L7H6.mjs} +52 -52
- package/dist/dependency-state-CTL8A9p3.mjs +76 -0
- package/dist/{deps-0Yt0Inly.mjs → deps-BAGOnrc7.mjs} +1 -1
- package/dist/{dev-client-VfSZNLT4.mjs → dev-client-BoifdqkU.mjs} +12 -12
- package/dist/{device-Bbv45Z9Z.mjs → device-BUw4JBuH.mjs} +7 -7
- package/dist/{xcode-xnxyvJkD.mjs → device-DvKJ454A.mjs} +208 -967
- package/dist/{device-lease-CJwK7OZr.mjs → device-lease-r7yEQ1R8.mjs} +3 -3
- package/dist/{device-pool-CcBNTvM7.mjs → device-pool-DU4FQKp5.mjs} +4 -4
- package/dist/{device-remote-DAA0Zm6Z.mjs → device-remote-BTUqbl0i.mjs} +7 -6
- package/dist/{doctor-B4EITgQB.mjs → doctor-Di_B26uh.mjs} +396 -117
- package/dist/{error-diagnostics-DwhNBFDU.mjs → error-diagnostics-CTePWAzW.mjs} +7 -6
- package/dist/{gc-BRu62v4o.mjs → gc-C20dWw_8.mjs} +61 -27
- package/dist/{guide-D95E0u3i.mjs → guide-sAnoty2y.mjs} +151 -20
- package/dist/guide-status-BF6RsJJp.mjs +271 -0
- package/dist/{ios-DAju3TVy.mjs → ios-BdpkncL6.mjs} +2667 -2607
- package/dist/{ios-device-CWOM_S44.mjs → ios-device-DJOi9FR5.mjs} +1 -1
- package/dist/{logs-CAdwW6c2.mjs → logs-BfQdIhiF.mjs} +5 -5
- package/dist/metro-ato6ef3J.mjs +99 -0
- package/dist/named-ports-DH9SXd0f.mjs +137 -0
- package/dist/{ownership-8O073GIC.mjs → ownership-CD_XnpDu.mjs} +5 -5
- package/dist/{ownership-DXcol4oU.mjs → ownership-CVY9cqCr.mjs} +6 -6
- package/dist/ports-A6uTJSPj.mjs +39 -0
- package/dist/ports-BZprTJRa.mjs +90 -0
- package/dist/{project-fKmuIaMI.mjs → project-B_SDt4ip.mjs} +2 -2
- package/dist/{reclaim-CUmwucWG.mjs → reclaim-C_rB25f4.mjs} +20 -13
- package/dist/{reload-CWpXoOgN.mjs → reload-CYBJbJfI.mjs} +8 -8
- package/dist/rolldown-runtime-BsOwBuO_.mjs +30 -0
- package/dist/{server-bare-DEzPZLvf.mjs → server-bare-H3pAhbMs.mjs} +11 -35
- package/dist/server-expo-CSwUVYOa.mjs +2 -0
- package/dist/{server-expo-DizT4OMk.mjs → server-expo-Ds8_mfng.mjs} +6 -14
- package/dist/{settings-DEJf6_kJ.mjs → settings-kO2ZqIKy.mjs} +2 -2
- package/dist/{slot-launch-CZklomj6.mjs → slot-launch-BpHiHH6A.mjs} +4 -4
- package/dist/{start-B5G4NjiA.mjs → start-DiEfqEWk.mjs} +20 -22
- package/dist/state-CnWzvCWP.mjs +131 -0
- package/dist/{stats-CaaQr4yN.mjs → stats-DHrO0frH.mjs} +4 -4
- package/dist/{stats-BR-kwj0B.mjs → stats-Fo9ACIv7.mjs} +1 -1
- package/dist/{status-CI9clRSb.mjs → status-J5LAXpag.mjs} +15 -12
- package/dist/{stop-CjlgDk0a.mjs → stop-0jJNahwN.mjs} +15 -14
- package/dist/{stop-CQhffpmX.mjs → stop-CM1fejKW.mjs} +2 -2
- package/dist/supervisor-run.d.mts +7 -28
- package/dist/supervisor-run.mjs +20 -11
- package/dist/{teardown--gESs9ck.mjs → teardown-CAVcWeRD.mjs} +148 -109
- package/dist/{workspace-process-lock-IBj1Towr.mjs → workspace-process-lock-Bz19_R9R.mjs} +4 -4
- package/dist/workspace-state-WVwWw_hB.mjs +146 -0
- package/dist/{worktree-DtQGO9-s.mjs → worktree-CeUKDAQw.mjs} +38 -34
- package/dist/{worktree-Eq6TLH8N.mjs → worktree-DJnBbQZ9.mjs} +79 -22
- package/dist/xcode-BqDeRkpT.mjs +860 -0
- package/package.json +7 -7
- package/shim/expo-metro-config.cjs +11 -17
- package/dist/doctor-BueDfLYG.mjs +0 -530
- package/dist/metro-CORqXyTL.mjs +0 -184
- package/dist/ownership-claim-BtPtJVmq.mjs +0 -584
- package/dist/server-expo-44em5Pmg.mjs +0 -2
- package/dist/state-B3XA2wp2.mjs +0 -268
- package/shim/metro-cache-generation.cjs +0 -20
- package/shim/metro-cache-generation.d.cts +0 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { H as segment } from "./android-
|
|
1
|
+
import { D as withDirLock, I as parseDeviceSlotKey, P as deviceSlotKey, u as getConfigDir, z as validateDeviceSlot } from "./config-DMP7L7H6.mjs";
|
|
2
|
+
import { a as withWorkspaceStateLock, n as clearWorkspaceStateKeys, o as writeWorkspaceState, r as readWorkspaceState } from "./workspace-state-WVwWw_hB.mjs";
|
|
3
|
+
import { H as segment } from "./android-DJWiTBkz.mjs";
|
|
4
4
|
import { mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
6
|
import { join } from "node:path";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as formatElapsed, r as clockTime } from "./command-output-
|
|
3
|
-
import { A as APP_READINESS_TIMEOUT_MS, l as VERIFY_TIMEOUT_MS } from "./app-install
|
|
4
|
-
import { a as parseLease, c as releaseRunLease, d as selectPoolDevice, f as takeLease, n as fileLeaseIo, r as leaseIsExpired, s as raiseLease, t as deviceLeasePath } from "./device-lease-
|
|
1
|
+
import { P as deviceSlotKey } from "./config-DMP7L7H6.mjs";
|
|
2
|
+
import { a as formatElapsed, r as clockTime } from "./command-output-BuIb_ZOI.mjs";
|
|
3
|
+
import { A as APP_READINESS_TIMEOUT_MS, l as VERIFY_TIMEOUT_MS } from "./app-install--PAocWvq.mjs";
|
|
4
|
+
import { a as parseLease, c as releaseRunLease, d as selectPoolDevice, f as takeLease, n as fileLeaseIo, r as leaseIsExpired, s as raiseLease, t as deviceLeasePath } from "./device-lease-r7yEQ1R8.mjs";
|
|
5
5
|
//#region src/engine/device-lease-run.ts
|
|
6
6
|
const LEASE_STEP_FLOOR_MS = 6e4;
|
|
7
7
|
const DEBUG_VERIFY_STEP_MS = VERIFY_TIMEOUT_MS + APP_READINESS_TIMEOUT_MS;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
2
|
+
import { D as withDirLock, u as getConfigDir } from "./config-DMP7L7H6.mjs";
|
|
3
|
+
import { _ as workspaceLogsDir, h as workspaceDir, v as workspaceName, y as workspaceStateFile } from "./workspace-state-WVwWw_hB.mjs";
|
|
4
|
+
import { a as clearRemoteSession, l as readRemoteSession, s as readMetroTunnel } from "./state-CnWzvCWP.mjs";
|
|
5
|
+
import { r as pidExists } from "./metro-ato6ef3J.mjs";
|
|
6
|
+
import { z as planMetroReach } from "./settings-kO2ZqIKy.mjs";
|
|
7
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-Bz19_R9R.mjs";
|
|
8
|
+
import { T as readMetroRecords, a as LAUNCH_ERROR, b as isBundleProof, p as devClientDeepLink, r as INSTALL_ERROR } from "./app-install--PAocWvq.mjs";
|
|
8
9
|
import { S as sanitizeDeviceLabel } from "./ios-Deh5js5V.mjs";
|
|
9
10
|
import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
10
11
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
@@ -1,21 +1,229 @@
|
|
|
1
1
|
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
-
import { g as loadConfig, l as getConcurrencyLimits, o as configCorruptRepair } from "./config-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { i as detectIsExpo, t as appProjectProblem } from "./project-
|
|
6
|
-
import { d as quotedPath, u as plural } from "./command-output-
|
|
7
|
-
import { B as listBuildLocks, Z as fingerprintProject, q as diffFingerprintSources, y as listAdbDevices } from "./android-
|
|
8
|
-
import { A as resolveSettings, E as remoteAndroidSetting, I as unknownSettingKeys, M as settingOrigin, N as settingShapeErrors, O as remoteIosSetting, P as settingsLayers, U as resolveOptimizations, V as compilerCacheFallbackMessage, a as SETTING_SHAPE_REMEDY, i as SETTINGS_WITH_RESOLVE_TIME_FALLBACK, n as PATH_SETTINGS, v as iosSimSlimProfileSetting, w as projectOptimizations, y as mergeSettingsLayers } from "./settings-
|
|
9
|
-
import {
|
|
2
|
+
import { g as loadConfig, l as getConcurrencyLimits, o as configCorruptRepair } from "./config-DMP7L7H6.mjs";
|
|
3
|
+
import { l as sharedBuildCache, m as workspaceDerivedData } from "./workspace-state-WVwWw_hB.mjs";
|
|
4
|
+
import { C as filesystemDevice, E as temporaryRoot, a as dirtyFingerprintFiles, d as listWorktrees, f as locallyKnownUpstream, g as repoRoot, s as gitCommonDir, v as resolveSourceCheckout, w as makeTemporaryDirectory } from "./worktree-DJnBbQZ9.mjs";
|
|
5
|
+
import { a as findProjectRoot, i as detectIsExpo, t as appProjectProblem } from "./project-B_SDt4ip.mjs";
|
|
6
|
+
import { d as quotedPath, l as phaseLine, u as plural } from "./command-output-BuIb_ZOI.mjs";
|
|
7
|
+
import { B as listBuildLocks, Z as fingerprintProject, q as diffFingerprintSources, y as listAdbDevices } from "./android-DJWiTBkz.mjs";
|
|
8
|
+
import { A as resolveSettings, E as remoteAndroidSetting, I as unknownSettingKeys, M as settingOrigin, N as settingShapeErrors, O as remoteIosSetting, P as settingsLayers, U as resolveOptimizations, V as compilerCacheFallbackMessage, a as SETTING_SHAPE_REMEDY, i as SETTINGS_WITH_RESOLVE_TIME_FALLBACK, n as PATH_SETTINGS, v as iosSimSlimProfileSetting, w as projectOptimizations, y as mergeSettingsLayers } from "./settings-kO2ZqIKy.mjs";
|
|
9
|
+
import { S as resolveEasCliBin, a as POOL_SETTING_REMEDY, b as ownerFromConfig, h as checkEasAuth$1, l as parkedMaxSetting, x as providerFromConfig } from "./teardown-CAVcWeRD.mjs";
|
|
10
10
|
import { E as readHostMemoryPressure, T as hostMemoryPressureAdvice, u as listAllIosSims } from "./ios-Deh5js5V.mjs";
|
|
11
|
-
import { n as listBuildSlots } from "./build-slots-
|
|
12
|
-
import {
|
|
13
|
-
import { d as projectCmakeLauncher, i as apkOutputsDir, r as resolveAndroidCompilerCache, t as readAndroidCasToolchain } from "./android-cas-
|
|
11
|
+
import { n as listBuildSlots } from "./build-slots-CuPszJDu.mjs";
|
|
12
|
+
import { a as liveOwnedDeviceCount, d as simslimIsOnPath } from "./device-DvKJ454A.mjs";
|
|
13
|
+
import { d as projectCmakeLauncher, i as apkOutputsDir, r as resolveAndroidCompilerCache, t as readAndroidCasToolchain } from "./android-cas-CSS8gjR7.mjs";
|
|
14
|
+
import { h as podInstallCommand, i as ccacheEnabled, o as detectXcodeMajor } from "./xcode-BqDeRkpT.mjs";
|
|
15
|
+
import { i as inspectStimVersions, n as recordDoctorRun, r as compareStimVersions } from "./guide-status-BF6RsJJp.mjs";
|
|
16
|
+
import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-CTL8A9p3.mjs";
|
|
14
17
|
import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, rmSync } from "node:fs";
|
|
18
|
+
import { InvalidArgumentError } from "commander";
|
|
15
19
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
16
|
-
import { existsSync as existsSync$1, lstatSync as lstatSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1 } from "fs";
|
|
20
|
+
import { existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, renameSync as renameSync$1, rmSync as rmSync$1, writeFileSync as writeFileSync$1 } from "fs";
|
|
21
|
+
import { homedir } from "os";
|
|
17
22
|
import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1 } from "path";
|
|
18
23
|
import * as expoFingerprint from "@expo/fingerprint";
|
|
24
|
+
import chalk from "chalk";
|
|
25
|
+
//#region src/sandbox.ts
|
|
26
|
+
/**
|
|
27
|
+
* What a sandboxed harness has to permit for Stim to work: its state
|
|
28
|
+
* directory, the XPC service simctl talks to, and the port adb's server binds.
|
|
29
|
+
*/
|
|
30
|
+
function sandboxAllowance(stimHome = "~/.stim") {
|
|
31
|
+
return {
|
|
32
|
+
allowWrite: [stimHome],
|
|
33
|
+
allowMachLookup: ["com.apple.coresimulator.*"],
|
|
34
|
+
allowLocalBinding: true
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function detectHarness(env = process.env) {
|
|
38
|
+
if (env.CLAUDECODE || env.CLAUDE_CODE_ENTRYPOINT) return "claude-code";
|
|
39
|
+
if (Object.keys(env).some((k) => k.startsWith("CODEX_"))) return "codex";
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function expandHome(value, home) {
|
|
43
|
+
const v = value.trim();
|
|
44
|
+
if (v === "~" || v === "$HOME") return home;
|
|
45
|
+
if (v.startsWith("~/")) return join$1(home, v.slice(2));
|
|
46
|
+
if (v.startsWith("$HOME/")) return join$1(home, v.slice(6));
|
|
47
|
+
return v;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Whether the sandbox actually stops Stim, rather than whether a harness that
|
|
51
|
+
* can sandbox is present: a session with its sandbox off exports the same
|
|
52
|
+
* variables as one with it on, so only a write says which this is.
|
|
53
|
+
*/
|
|
54
|
+
function sandboxBlocksStimHome(stimHome, home = homedir()) {
|
|
55
|
+
const dir = expandHome(stimHome, home);
|
|
56
|
+
const probe = join$1(dir, `.probe-${process.pid}-${Math.random().toString(36).slice(2)}`);
|
|
57
|
+
try {
|
|
58
|
+
mkdirSync$1(dir, { recursive: true });
|
|
59
|
+
writeFileSync$1(probe, "");
|
|
60
|
+
rmSync$1(probe, { force: true });
|
|
61
|
+
return false;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const code = err.code;
|
|
64
|
+
return code === "EPERM" || code === "EACCES";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Where a per-user, gitignored Claude Code settings file belongs for this project. */
|
|
68
|
+
function claudeLocalSettingsPath(root) {
|
|
69
|
+
return join$1(root, ".claude", "settings.local.json");
|
|
70
|
+
}
|
|
71
|
+
/** Every settings file that may already carry the allowance, most local first. */
|
|
72
|
+
function allowanceSearchPaths(settingsRoot, home = homedir()) {
|
|
73
|
+
return [
|
|
74
|
+
claudeLocalSettingsPath(settingsRoot),
|
|
75
|
+
join$1(settingsRoot, ".claude", "settings.json"),
|
|
76
|
+
join$1(home, ".claude", "settings.json")
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
function isPlainObject(value) {
|
|
80
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Absent, unreadable and present-but-not-an-object are three different states.
|
|
84
|
+
* Collapsing them loses the one distinction that matters before a write: a
|
|
85
|
+
* file whose contents did not parse still holds settings we must not destroy.
|
|
86
|
+
*/
|
|
87
|
+
function readSettings(path) {
|
|
88
|
+
let raw;
|
|
89
|
+
try {
|
|
90
|
+
raw = readFileSync$1(path, "utf-8");
|
|
91
|
+
} catch (err) {
|
|
92
|
+
return err.code === "ENOENT" ? { kind: "absent" } : { kind: "invalid" };
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(raw);
|
|
96
|
+
return isPlainObject(parsed) ? {
|
|
97
|
+
kind: "object",
|
|
98
|
+
value: parsed
|
|
99
|
+
} : { kind: "invalid" };
|
|
100
|
+
} catch {
|
|
101
|
+
return { kind: "invalid" };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function nested(source, ...keys) {
|
|
105
|
+
let cursor = source;
|
|
106
|
+
for (const key of keys) {
|
|
107
|
+
if (!isPlainObject(cursor)) return void 0;
|
|
108
|
+
cursor = cursor[key];
|
|
109
|
+
}
|
|
110
|
+
return cursor;
|
|
111
|
+
}
|
|
112
|
+
/** Whether an allowWrite entry covers the Stim home, including as a parent. */
|
|
113
|
+
function covers(entry, target, home) {
|
|
114
|
+
const e = expandHome(entry.replace(/\/+\*+$/, "").replace(/\/+$/, ""), home);
|
|
115
|
+
const t = expandHome(target.replace(/\/+$/, ""), home);
|
|
116
|
+
return e === t || t.startsWith(`${e}/`);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Which parts of the allowance the settings files already carry. Any file may
|
|
120
|
+
* supply any part, so a project that solved this in one place is not asked to
|
|
121
|
+
* repeat it in another.
|
|
122
|
+
*/
|
|
123
|
+
function missingAllowance(paths, stimHome = "~/.stim", home = homedir()) {
|
|
124
|
+
const files = paths.map((p) => {
|
|
125
|
+
const read = readSettings(p);
|
|
126
|
+
return read.kind === "object" ? read.value : null;
|
|
127
|
+
});
|
|
128
|
+
const list = (...keys) => files.flatMap((f) => {
|
|
129
|
+
const value = nested(f, ...keys);
|
|
130
|
+
return Array.isArray(value) ? value.map(String) : [];
|
|
131
|
+
});
|
|
132
|
+
const missing = [];
|
|
133
|
+
if (!list("sandbox", "filesystem", "allowWrite").some((w) => covers(w, stimHome, home))) missing.push("sandbox.filesystem.allowWrite");
|
|
134
|
+
if (!list("sandbox", "network", "allowMachLookup").some((l) => l.toLowerCase().startsWith("com.apple.coresimulator"))) missing.push("sandbox.network.allowMachLookup");
|
|
135
|
+
if (!files.some((f) => nested(f, "sandbox", "network", "allowLocalBinding") === true)) missing.push("sandbox.network.allowLocalBinding");
|
|
136
|
+
return missing;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The key that stops a merge, or null when the settings can take the
|
|
140
|
+
* allowance. A value of an unexpected shape is someone else's, and rewriting
|
|
141
|
+
* it would lose whatever it means.
|
|
142
|
+
*/
|
|
143
|
+
function unmergeableKey(settings) {
|
|
144
|
+
if (!("sandbox" in settings)) return null;
|
|
145
|
+
if (!isPlainObject(settings.sandbox)) return "sandbox";
|
|
146
|
+
const sandbox = settings.sandbox;
|
|
147
|
+
if ("filesystem" in sandbox && !isPlainObject(sandbox.filesystem)) return "sandbox.filesystem";
|
|
148
|
+
if ("network" in sandbox && !isPlainObject(sandbox.network)) return "sandbox.network";
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
/** Merge the allowance into a settings object, leaving everything else as it is. */
|
|
152
|
+
function withAllowance(settings, allowance) {
|
|
153
|
+
const next = { ...settings };
|
|
154
|
+
const sandbox = isPlainObject(next.sandbox) ? { ...next.sandbox } : {};
|
|
155
|
+
const filesystem = isPlainObject(sandbox.filesystem) ? { ...sandbox.filesystem } : {};
|
|
156
|
+
const network = isPlainObject(sandbox.network) ? { ...sandbox.network } : {};
|
|
157
|
+
const writes = Array.isArray(filesystem.allowWrite) ? filesystem.allowWrite.map(String) : [];
|
|
158
|
+
const lookups = Array.isArray(network.allowMachLookup) ? network.allowMachLookup.map(String) : [];
|
|
159
|
+
filesystem.allowWrite = [.../* @__PURE__ */ new Set([...writes, ...allowance.allowWrite])];
|
|
160
|
+
network.allowMachLookup = [.../* @__PURE__ */ new Set([...lookups, ...allowance.allowMachLookup])];
|
|
161
|
+
network.allowLocalBinding = true;
|
|
162
|
+
sandbox.filesystem = filesystem;
|
|
163
|
+
sandbox.network = network;
|
|
164
|
+
next.sandbox = sandbox;
|
|
165
|
+
return next;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Write the allowance into a Claude Code local settings file, creating it if
|
|
169
|
+
* needed. Refuses whenever the current contents cannot be read back, because
|
|
170
|
+
* the merge would replace settings rather than extend them.
|
|
171
|
+
*/
|
|
172
|
+
function applyClaudeAllowance(path, allowance) {
|
|
173
|
+
const read = readSettings(path);
|
|
174
|
+
if (read.kind === "invalid") return {
|
|
175
|
+
status: "refused",
|
|
176
|
+
reason: `${path} is not a plain JSON object, so Stim cannot merge into it without losing what is there. Comments make a settings file unparseable here even though Claude Code accepts them.`
|
|
177
|
+
};
|
|
178
|
+
const existing = read.kind === "object" ? read.value : {};
|
|
179
|
+
const blocker = unmergeableKey(existing);
|
|
180
|
+
if (blocker) return {
|
|
181
|
+
status: "refused",
|
|
182
|
+
reason: `${path} holds a "${blocker}" that is not an object, so Stim cannot merge into it without discarding that value.`
|
|
183
|
+
};
|
|
184
|
+
mkdirSync$1(dirname$1(path), { recursive: true });
|
|
185
|
+
const tmp = `${path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
186
|
+
writeFileSync$1(tmp, `${JSON.stringify(withAllowance(existing, allowance), null, 2)}\n`);
|
|
187
|
+
try {
|
|
188
|
+
renameSync$1(tmp, path);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
rmSync$1(tmp, { force: true });
|
|
191
|
+
throw err;
|
|
192
|
+
}
|
|
193
|
+
return { status: read.kind === "absent" ? "created" : "updated" };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* The finding a sandboxed session should see: what fails, and the one command
|
|
197
|
+
* that fixes it where the harness has a per-path allowance.
|
|
198
|
+
*/
|
|
199
|
+
function sandboxFinding(settingsRoot, { env = process.env, home = homedir(), stimHome = process.env.STIM_HOME || "~/.stim", blocked } = {}) {
|
|
200
|
+
const harness = detectHarness(env);
|
|
201
|
+
if (!harness) return null;
|
|
202
|
+
if (!(blocked ?? sandboxBlocksStimHome(stimHome, home))) return null;
|
|
203
|
+
const symptoms = `Writes to ${stimHome} raise EPERM on a directory you can write, the simulator service looks dead, and the adb server looks unreachable.`;
|
|
204
|
+
const title = "This session sandboxes shell commands, and Stim is not allowed through";
|
|
205
|
+
if (harness === "codex") return {
|
|
206
|
+
level: "cost",
|
|
207
|
+
title,
|
|
208
|
+
detail: `${symptoms} Codex has one sandbox setting and no per-path allowance, so there is nothing to add.`,
|
|
209
|
+
fix: "Run Stim with the sandbox off, or start Codex with `codex -s danger-full-access`, which turns the whole sandbox off rather than allowing these three. See `stim guide errors sandbox`."
|
|
210
|
+
};
|
|
211
|
+
const paths = allowanceSearchPaths(settingsRoot, home);
|
|
212
|
+
const missing = missingAllowance(paths, stimHome, home);
|
|
213
|
+
if (missing.length === 0) return {
|
|
214
|
+
level: "cost",
|
|
215
|
+
title: "Stim is allowed through this sandbox, but the session has not picked it up",
|
|
216
|
+
detail: `${symptoms} ${paths[0]} already grants all three, so the settings were read before they were written.`,
|
|
217
|
+
fix: "Restart the session for the allowance to take effect."
|
|
218
|
+
};
|
|
219
|
+
return {
|
|
220
|
+
level: "cost",
|
|
221
|
+
title,
|
|
222
|
+
detail: `${symptoms} Missing: ${missing.join(", ")}.`,
|
|
223
|
+
fix: `Run \`stim doctor --fix\` to write sandbox.filesystem.allowWrite, sandbox.network.allowMachLookup, and sandbox.network.allowLocalBinding into ${paths[0]}, merged with what is there, or add the missing keys by hand. See \`stim guide errors sandbox\`.`
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
//#endregion
|
|
19
227
|
//#region src/doctor-storage.ts
|
|
20
228
|
function checkStorageLayout(projectRoot, { platform, device = filesystemDevice, stagingRoot = temporaryRoot } = {}) {
|
|
21
229
|
const findings = [];
|
|
@@ -31,7 +239,8 @@ function checkStorageLayout(projectRoot, { platform, device = filesystemDevice,
|
|
|
31
239
|
};
|
|
32
240
|
try {
|
|
33
241
|
const target = repoRoot(projectRoot) ?? projectRoot;
|
|
34
|
-
|
|
242
|
+
const source = resolveSourceCheckout(target);
|
|
243
|
+
check("Worktree copy", ["path" in source ? source.path : target, target], "Keep the source checkout and linked worktree on the same volume to share file blocks when warming.");
|
|
35
244
|
const cache = sharedBuildCache();
|
|
36
245
|
check("Cached app/APK staging", [cache, stagingRoot(join(cache, "artifact.app"))], temporaryFix);
|
|
37
246
|
const cacheFix = "Place STIM_BUILD_CACHE / machine caches.buildCache on the build-output volume, or accept the full-copy cost of keeping the cache on a separate volume.";
|
|
@@ -450,114 +659,15 @@ function mainCheckoutProjectRoot(projectRoot) {
|
|
|
450
659
|
const currentRepoRoot = repoRoot(projectRoot);
|
|
451
660
|
if (!currentRepoRoot) return projectRoot;
|
|
452
661
|
try {
|
|
453
|
-
const
|
|
454
|
-
if (
|
|
662
|
+
const source = resolveSourceCheckout(currentRepoRoot);
|
|
663
|
+
if ("refusal" in source) return projectRoot;
|
|
455
664
|
const projectRel = relative$1(currentRepoRoot, realpathSync$1(projectRoot));
|
|
456
665
|
if (projectRel.startsWith("..")) return projectRoot;
|
|
457
|
-
return resolve$1(
|
|
666
|
+
return resolve$1(source.path, projectRel);
|
|
458
667
|
} catch {
|
|
459
668
|
return projectRoot;
|
|
460
669
|
}
|
|
461
670
|
}
|
|
462
|
-
function installedNpmTreeIsValid(projectRoot) {
|
|
463
|
-
try {
|
|
464
|
-
getExecutor().runFile("npm", [
|
|
465
|
-
"ls",
|
|
466
|
-
"--all",
|
|
467
|
-
"--json",
|
|
468
|
-
"--silent"
|
|
469
|
-
], {
|
|
470
|
-
cwd: projectRoot,
|
|
471
|
-
timeoutMs: 3e4
|
|
472
|
-
});
|
|
473
|
-
return true;
|
|
474
|
-
} catch {
|
|
475
|
-
return false;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
const DEPENDENCY_STATES = [
|
|
479
|
-
{
|
|
480
|
-
lock: "pnpm-lock.yaml",
|
|
481
|
-
installed: ["node_modules"],
|
|
482
|
-
command: "pnpm install"
|
|
483
|
-
},
|
|
484
|
-
{
|
|
485
|
-
lock: "yarn.lock",
|
|
486
|
-
installed: [
|
|
487
|
-
"node_modules",
|
|
488
|
-
".pnp.cjs",
|
|
489
|
-
".pnp.js"
|
|
490
|
-
],
|
|
491
|
-
command: "yarn install"
|
|
492
|
-
},
|
|
493
|
-
{
|
|
494
|
-
lock: "bun.lock",
|
|
495
|
-
installed: ["node_modules"],
|
|
496
|
-
command: "bun install"
|
|
497
|
-
},
|
|
498
|
-
{
|
|
499
|
-
lock: "bun.lockb",
|
|
500
|
-
installed: ["node_modules"],
|
|
501
|
-
command: "bun install"
|
|
502
|
-
},
|
|
503
|
-
{
|
|
504
|
-
lock: "package-lock.json",
|
|
505
|
-
installed: ["node_modules"],
|
|
506
|
-
command: "npm ci"
|
|
507
|
-
}
|
|
508
|
-
];
|
|
509
|
-
function dependencyState(projectRoot) {
|
|
510
|
-
const root = repoRoot(projectRoot) ?? projectRoot;
|
|
511
|
-
let dir = projectRoot;
|
|
512
|
-
while (true) {
|
|
513
|
-
const state = DEPENDENCY_STATES.find((candidate) => existsSync$1(join$1(dir, candidate.lock)));
|
|
514
|
-
if (state) return {
|
|
515
|
-
...state,
|
|
516
|
-
root: dir
|
|
517
|
-
};
|
|
518
|
-
if (dir === root) return null;
|
|
519
|
-
const parent = dirname$1(dir);
|
|
520
|
-
if (parent === dir || relative$1(root, parent).startsWith("..")) return null;
|
|
521
|
-
dir = parent;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
function hasInstalledDependencies(projectRoot, markers = [
|
|
525
|
-
"node_modules",
|
|
526
|
-
".pnp.cjs",
|
|
527
|
-
".pnp.js"
|
|
528
|
-
]) {
|
|
529
|
-
return markers.some((entry) => existsSync$1(join$1(projectRoot, entry)));
|
|
530
|
-
}
|
|
531
|
-
function locallyKnownUpstream(projectRoot) {
|
|
532
|
-
try {
|
|
533
|
-
const name = getExecutor().runFile("git", [
|
|
534
|
-
"-C",
|
|
535
|
-
projectRoot,
|
|
536
|
-
"rev-parse",
|
|
537
|
-
"--abbrev-ref",
|
|
538
|
-
"--symbolic-full-name",
|
|
539
|
-
"@{upstream}"
|
|
540
|
-
], { timeoutMs: 5e3 });
|
|
541
|
-
const counts = getExecutor().runFile("git", [
|
|
542
|
-
"-C",
|
|
543
|
-
projectRoot,
|
|
544
|
-
"rev-list",
|
|
545
|
-
"--left-right",
|
|
546
|
-
"--count",
|
|
547
|
-
"HEAD...@{upstream}"
|
|
548
|
-
], { timeoutMs: 5e3 }).trim().split(/\s+/).map(Number);
|
|
549
|
-
const ahead = counts[0] ?? NaN;
|
|
550
|
-
const behind = counts[1] ?? NaN;
|
|
551
|
-
if (!name || !Number.isInteger(ahead) || !Number.isInteger(behind)) return null;
|
|
552
|
-
return {
|
|
553
|
-
name,
|
|
554
|
-
ahead,
|
|
555
|
-
behind
|
|
556
|
-
};
|
|
557
|
-
} catch {
|
|
558
|
-
return null;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
671
|
function brokenPodLinks(podsRoot) {
|
|
562
672
|
try {
|
|
563
673
|
return getExecutor().runFile("find", [
|
|
@@ -584,7 +694,7 @@ function hasIosWarmOutput(root) {
|
|
|
584
694
|
function hasLinkedWorktree(projectRoot) {
|
|
585
695
|
const root = repoRoot(projectRoot);
|
|
586
696
|
if (!root) return false;
|
|
587
|
-
return listWorktrees(root).filter((entry) => !entry.prunable).length > 1;
|
|
697
|
+
return listWorktrees(root).filter((entry) => !entry.prunable && !entry.bare).length > 1;
|
|
588
698
|
}
|
|
589
699
|
function headBranch(root) {
|
|
590
700
|
const ref = getExecutor().runFileQuiet("git", [
|
|
@@ -1126,4 +1236,173 @@ async function detectFingerprintParity(projectRoot, { createFingerprint = expoFi
|
|
|
1126
1236
|
}
|
|
1127
1237
|
}
|
|
1128
1238
|
//#endregion
|
|
1129
|
-
|
|
1239
|
+
//#region src/commands/doctor.ts
|
|
1240
|
+
function parseDoctorPlatform(value) {
|
|
1241
|
+
if (value === "ios" || value === "android") return value;
|
|
1242
|
+
throw new InvalidArgumentError("expected one of: ios, android");
|
|
1243
|
+
}
|
|
1244
|
+
function doctorTarget(platform) {
|
|
1245
|
+
if (platform === "ios") return "iOS";
|
|
1246
|
+
if (platform === "android") return "Android";
|
|
1247
|
+
return "all platforms";
|
|
1248
|
+
}
|
|
1249
|
+
function stimVersionLines(report) {
|
|
1250
|
+
const resolved = report.resolved ? `${report.resolved.version ?? "unknown version"} at ${report.resolved.path}` : "not found on PATH";
|
|
1251
|
+
const paths = `${report.installations.length} distinct PATH install${report.installations.length === 1 ? "" : "s"}`;
|
|
1252
|
+
const pathVersions = [...new Set(report.installations.map((entry) => entry.version).filter(Boolean))];
|
|
1253
|
+
const versions = pathVersions.length > 1 ? ` (${pathVersions.join(", ")})` : "";
|
|
1254
|
+
return [
|
|
1255
|
+
phaseLine("version", report.runningVersion),
|
|
1256
|
+
phaseLine("resolved", resolved),
|
|
1257
|
+
phaseLine("installs", paths + versions)
|
|
1258
|
+
];
|
|
1259
|
+
}
|
|
1260
|
+
function doctorSuccessLines(platform, stim) {
|
|
1261
|
+
const lines = [
|
|
1262
|
+
`Doctor (${doctorTarget(platform)})`,
|
|
1263
|
+
phaseLine("result", "PASS"),
|
|
1264
|
+
phaseLine("findings", "0"),
|
|
1265
|
+
...stimVersionLines(stim),
|
|
1266
|
+
"",
|
|
1267
|
+
"Project",
|
|
1268
|
+
phaseLine("project", "source checkout, dependencies, local upstream"),
|
|
1269
|
+
phaseLine("settings", "every Stim setting type, machine config paths, companions and inert keys")
|
|
1270
|
+
];
|
|
1271
|
+
if (platform !== "android") {
|
|
1272
|
+
lines.push("", "iOS");
|
|
1273
|
+
lines.push(phaseLine("setup", "CocoaPods, warm state, effective Debug simulator architectures"));
|
|
1274
|
+
lines.push(phaseLine("caches", "Metro, Xcode compilation, ccache, build provider"));
|
|
1275
|
+
lines.push(phaseLine("devices", "remote device, SimSlim profile"));
|
|
1276
|
+
}
|
|
1277
|
+
if (platform !== "ios") {
|
|
1278
|
+
lines.push("", "Android");
|
|
1279
|
+
lines.push(phaseLine("setup", "warm state"));
|
|
1280
|
+
lines.push(phaseLine("caches", "Metro, Gradle, ccache, build provider"));
|
|
1281
|
+
lines.push(phaseLine("devices", "remote device"));
|
|
1282
|
+
}
|
|
1283
|
+
lines.push("", "Shared");
|
|
1284
|
+
lines.push(phaseLine("services", "EAS session"));
|
|
1285
|
+
lines.push(phaseLine("storage", "temporary staging and build-cache volume placement"));
|
|
1286
|
+
lines.push(phaseLine("fingerprint", "parity when dependencies are absent"));
|
|
1287
|
+
lines.push("", "Handled automatically");
|
|
1288
|
+
const suppliedCaches = platform === "ios" ? "Metro transform store, Xcode compilation cache" : platform === "android" ? "Metro transform store, Gradle build cache, ccache" : "Metro transform store, Xcode compilation cache, Gradle build cache, ccache";
|
|
1289
|
+
lines.push(phaseLine("caches", suppliedCaches));
|
|
1290
|
+
lines.push(phaseLine("meaning", "missing project cache settings are healthy"));
|
|
1291
|
+
return lines;
|
|
1292
|
+
}
|
|
1293
|
+
function shadowedStimFinding(report) {
|
|
1294
|
+
if (!report.resolvedIsOlder || !report.resolved?.version || !report.highestVersion) return null;
|
|
1295
|
+
const newer = [...(compareStimVersions(report.runningVersion, report.resolved.version) ?? 0) > 0 ? [{
|
|
1296
|
+
path: report.runningPath,
|
|
1297
|
+
version: report.runningVersion
|
|
1298
|
+
}] : [], ...report.installations.filter((entry) => entry.version && (compareStimVersions(entry.version, report.resolved?.version ?? "") ?? 0) > 0)].find((entry) => entry.version === report.highestVersion);
|
|
1299
|
+
const newerLocation = newer?.path ? ` at ${newer.path}` : "";
|
|
1300
|
+
return {
|
|
1301
|
+
level: "cost",
|
|
1302
|
+
title: "The Stim resolved from PATH is older than another installation",
|
|
1303
|
+
detail: `${report.resolved.path} reports ${report.resolved.version}, while ${report.highestVersion} is also installed${newerLocation}. A shell command named stim uses the first executable on PATH, so newer commands and fixes can appear to be missing.`,
|
|
1304
|
+
fix: `Update or remove ${report.resolved.path}, or put the ${report.highestVersion} installation earlier on PATH. Then run \`stim doctor\` again.`
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Status goes to stderr so `--json --fix` still prints one parseable payload
|
|
1309
|
+
* on stdout, and the report that follows shows what the write left.
|
|
1310
|
+
*/
|
|
1311
|
+
function applySandboxFix(root, env = process.env) {
|
|
1312
|
+
const harness = detectHarness(env);
|
|
1313
|
+
if (harness === "codex") {
|
|
1314
|
+
console.error(chalk.yellow("Nothing to apply under Codex."));
|
|
1315
|
+
console.error(chalk.dim("Its sandbox is one setting, `sandbox_mode`, with no per-path allowance: the only value that clears Stim is `danger-full-access`, which turns the sandbox off rather than allowing these three. Run Stim with the sandbox off instead, or set it yourself."));
|
|
1316
|
+
process.exitCode = 1;
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (harness !== "claude-code") {
|
|
1320
|
+
console.error(chalk.dim("No sandboxing harness detected, so there is nothing to apply."));
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
const settingsRoot = repoRoot(root) ?? root;
|
|
1324
|
+
const stimHome = env.STIM_HOME || "~/.stim";
|
|
1325
|
+
const target = claudeLocalSettingsPath(settingsRoot);
|
|
1326
|
+
const missing = missingAllowance(allowanceSearchPaths(settingsRoot), stimHome);
|
|
1327
|
+
if (missing.length === 0) {
|
|
1328
|
+
console.error(chalk.green("Stim is already allowed through this sandbox. Nothing to apply."));
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
const result = applyClaudeAllowance(target, sandboxAllowance(stimHome));
|
|
1332
|
+
if (result.status === "refused") {
|
|
1333
|
+
console.error(chalk.red(result.reason));
|
|
1334
|
+
console.error(chalk.dim(`Nothing was written. Add ${missing.join(", ")} by hand.`));
|
|
1335
|
+
process.exitCode = 1;
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
console.error(chalk.green(`${result.status === "created" ? "Wrote" : "Updated"} ${target}`));
|
|
1339
|
+
console.error(chalk.dim("Added writes to Stim's state directory, the simulator XPC service, and local port binding. Claude Code reads project settings from the directory a session starts in, so this file only counts for sessions rooted there. Restart the session for it to take effect."));
|
|
1340
|
+
}
|
|
1341
|
+
function doctorCommand(program, version, inspectVersions = inspectStimVersions) {
|
|
1342
|
+
program.command("doctor").description("Inspect the source checkout and report project state that can make native worktrees slow or invalid. The checkout is left untouched unless --fix is passed; --platform filters native findings. Each run is recorded in Stim state so guide can say when doctor is due.").option("--json", "print the findings as JSON").option("--platform <platform>", "report shared findings plus only this native platform: ios or android", parseDoctorPlatform).option("--fix", "repair the sandbox allowance when the report names it, and stale Android .cxx configurations in this checkout. Stop native builds first. Generated CMake output must be ignored and untracked; custom launcher settings and source files are preserved.").action(async (opts) => {
|
|
1343
|
+
const root = findProjectRoot(process.cwd());
|
|
1344
|
+
if (!root) {
|
|
1345
|
+
console.error(chalk.red("Not in a React Native project (no package.json found)."));
|
|
1346
|
+
process.exitCode = 1;
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
if (opts.fix) {
|
|
1350
|
+
if (sandboxFinding(repoRoot(root) ?? root)) applySandboxFix(root);
|
|
1351
|
+
if (opts.platform !== "ios") try {
|
|
1352
|
+
const repair = repairCxxLauncherState(root);
|
|
1353
|
+
for (const path of repair.removed) console.error(phaseLine("cache", `removed ${path}; next build reconfigures`));
|
|
1354
|
+
for (const { path, reason } of repair.refused) console.error(phaseLine("cache", `kept ${path}: ${reason}`));
|
|
1355
|
+
if (repair.refused.length > 0) process.exitCode = 1;
|
|
1356
|
+
} catch (error) {
|
|
1357
|
+
console.error(phaseLine("cache", `CMake cleanup failed: ${error.message}`));
|
|
1358
|
+
process.exitCode = 1;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
const stim = await inspectVersions(version);
|
|
1362
|
+
const findings = runDoctor(root, {
|
|
1363
|
+
xcodeMajor: opts.platform === "android" ? null : detectXcodeMajor(),
|
|
1364
|
+
platform: opts.platform
|
|
1365
|
+
});
|
|
1366
|
+
const parity = await detectFingerprintParity(root, { platform: opts.platform });
|
|
1367
|
+
if (parity) findings.push(parity);
|
|
1368
|
+
const linkedGit = await detectLinkedLibraryGitMetadata(root, { platform: opts.platform });
|
|
1369
|
+
if (linkedGit) findings.push(linkedGit);
|
|
1370
|
+
if (detectHarness()) {
|
|
1371
|
+
const sandbox = sandboxFinding(repoRoot(root) ?? root);
|
|
1372
|
+
if (sandbox) findings.push(sandbox);
|
|
1373
|
+
}
|
|
1374
|
+
const shadowed = shadowedStimFinding(stim);
|
|
1375
|
+
if (shadowed) findings.push(shadowed);
|
|
1376
|
+
if (opts.json) {
|
|
1377
|
+
console.log(JSON.stringify({
|
|
1378
|
+
project: root,
|
|
1379
|
+
platform: opts.platform ?? null,
|
|
1380
|
+
stim,
|
|
1381
|
+
findings
|
|
1382
|
+
}));
|
|
1383
|
+
recordDoctorRun(root, opts.platform, version);
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
if (findings.length === 0) {
|
|
1387
|
+
const lines = doctorSuccessLines(opts.platform, stim);
|
|
1388
|
+
for (const [index, line] of lines.entries()) if (index === 1) console.log(chalk.green(line));
|
|
1389
|
+
else if (line && !line.startsWith(" ")) console.log(chalk.bold(line));
|
|
1390
|
+
else console.log(chalk.dim(line));
|
|
1391
|
+
recordDoctorRun(root, opts.platform, version);
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
const ordered = findings.toSorted((a, b) => a.level === b.level ? 0 : a.level === "cost" ? -1 : 1);
|
|
1395
|
+
console.log(chalk.bold(`Doctor (${doctorTarget(opts.platform)})`));
|
|
1396
|
+
for (const line of stimVersionLines(stim)) console.log(chalk.dim(line));
|
|
1397
|
+
for (const f of ordered) {
|
|
1398
|
+
const tag = f.level === "cost" ? chalk.yellow("costs time") : chalk.dim("note");
|
|
1399
|
+
console.log(`\n${tag} ${chalk.bold(f.title)}`);
|
|
1400
|
+
console.log(` ${f.detail}`);
|
|
1401
|
+
if (f.fix) console.log(chalk.dim(` -> ${f.fix}`));
|
|
1402
|
+
}
|
|
1403
|
+
console.log(chalk.dim(`\n${findings.length} finding(s). Fix relevant "costs time" findings before copying the source checkout into a native worktree.`));
|
|
1404
|
+
recordDoctorRun(root, opts.platform, version);
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
//#endregion
|
|
1408
|
+
export { applySandboxFix, doctorCommand as default, doctorSuccessLines, parseDoctorPlatform, shadowedStimFinding };
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { D as withDirLock, F as deviceSlotPlatforms, P as deviceSlotKey, f as getProject } from "./config-DMP7L7H6.mjs";
|
|
3
|
+
import { r as readWorkspaceState } from "./workspace-state-WVwWw_hB.mjs";
|
|
4
|
+
import { d as readWorkspaceLaunches } from "./state-CnWzvCWP.mjs";
|
|
4
5
|
import { n as deviceConsoleLevel } from "./ios-device-D3pzTKQE.mjs";
|
|
5
|
-
import { i as resolveProjectMetro } from "./metro-
|
|
6
|
+
import { i as resolveProjectMetro } from "./metro-ato6ef3J.mjs";
|
|
6
7
|
import { n as parseLogcatLine, t as androidClockOffset } from "./android-BW8i_YxU.mjs";
|
|
7
|
-
import { _ as launchErrorPreview } from "./command-output-
|
|
8
|
-
import { n as androidHome } from "./android-
|
|
9
|
-
import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-
|
|
8
|
+
import { _ as launchErrorPreview } from "./command-output-BuIb_ZOI.mjs";
|
|
9
|
+
import { n as androidHome } from "./android-DJWiTBkz.mjs";
|
|
10
|
+
import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-r7yEQ1R8.mjs";
|
|
10
11
|
import { l as readLogRecords, t as attachExpoErrorContext } from "./logs-query-Cdp5aaCH.mjs";
|
|
11
12
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
12
13
|
import { createHash, randomUUID } from "node:crypto";
|