stim 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -155
- package/dist/{android-DWssqHQ8.mjs → android-787HhP5w.mjs} +168 -39
- package/dist/{android-BRgBYzU0.mjs → android-DBkeHwpm.mjs} +250 -200
- package/dist/{android-DeYdjjBP.mjs → android-DPov-fv2.mjs} +2 -2
- package/dist/{android-cas-SWIjzeuL.mjs → android-cas-CITmIeWY.mjs} +22 -14
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/app-install-CrJ6t_bv.mjs +819 -0
- package/dist/budget-BP4jZZL4.mjs +620 -0
- package/dist/build-progress-BXx-ZBe4.mjs +512 -0
- package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Pubfp8wV.mjs} +4 -4
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +22 -19
- package/dist/collector-run.d.mts +7 -1
- package/dist/collector-run.mjs +10 -9
- package/dist/{command-output-B3ycpsYv.mjs → command-output-D5z2_bcF.mjs} +11 -2
- package/dist/{config-B7iyKWkL.mjs → config-BEq0Qw_a.mjs} +134 -121
- package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-r9UesA9a.mjs} +2 -2
- package/dist/{deps-oCXVNEqQ.mjs → deps-DP4eWqea.mjs} +8 -5
- package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-DJHoca49.mjs} +1 -1
- package/dist/{device-BUoZyMoR.mjs → dev-client-92U593KT.mjs} +1090 -660
- package/dist/{device-RdujjHLB.mjs → device-DhTZe7L4.mjs} +13 -20
- package/dist/{build-lock-CgtsB6TV.mjs → device-lease-CbEaIk7b.mjs} +481 -27
- package/dist/{device-pool-BxRwdufO.mjs → device-pool-Di8Q-9Ro.mjs} +7 -7
- package/dist/{device-remote-ByN8P6CB.mjs → device-remote-Dy1jGj3U.mjs} +645 -119
- package/dist/{doctor-BMt156qe.mjs → doctor-B-inAJmt.mjs} +137 -113
- package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-xxWKNO3-.mjs} +22 -17
- package/dist/exec-DK8KaVHK.mjs +133 -0
- package/dist/{gc-v2YcoJvm.mjs → gc-ChPFZMRC.mjs} +836 -813
- package/dist/{guide-BTAlD1_y.mjs → guide-DwmaD_Wa.mjs} +953 -209
- package/dist/guide-status-CWvti2Lg.mjs +125 -0
- package/dist/idle-DpRLp5lj.mjs +157 -0
- package/dist/{build-slots-C-oAHRT1.mjs → in-use-CjUluqIX.mjs} +102 -8
- package/dist/{ios-BcCiI4TS.mjs → ios-B2Fpqvlb.mjs} +60 -71
- package/dist/{ios-BP04pY5f.mjs → ios-Dd-hYJBh.mjs} +241 -234
- package/dist/{ios-device-CFXA46ez.mjs → ios-device-BiOsSVzd.mjs} +3 -2
- package/dist/{ios-device-CauFJiYx.mjs → ios-device-CtRdEVjx.mjs} +149 -52
- package/dist/launch-verify-CQS_Kg-C.mjs +365 -0
- package/dist/{logs-CakdBTtm.mjs → logs-MARIy6m9.mjs} +20 -15
- package/dist/metro-BWLOJ_Wr.mjs +409 -0
- package/dist/{named-ports-DzMOlmaX.mjs → named-ports-DiTfSz6p.mjs} +4 -4
- package/dist/{ndjson-BZwI1NBv.mjs → ndjson-CDY9IkVC.mjs} +23 -43
- package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
- package/dist/{ownership-Cp52QAK1.mjs → ownership-D2xbLVoz.mjs} +2 -3
- package/dist/{teardown-BJqEzPo_.mjs → ownership-DZjdx8Gq.mjs} +664 -612
- package/dist/{ports-BIbJaKNW.mjs → ports-CLSSFOuz.mjs} +3 -3
- package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
- package/dist/{project-DDZWf_16.mjs → project-BXZzOABt.mjs} +8 -3
- package/dist/{reload-BdypPbjM.mjs → reload-DoETJ3rl.mjs} +11 -13
- package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DVbb7kXH.mjs} +2 -2
- package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-BvOCeakp.mjs} +8 -7
- package/dist/server-expo-DBVTdult.mjs +2 -0
- package/dist/{settings-BXXorVM2.mjs → settings-DOugPlPz.mjs} +59 -159
- package/dist/settings-EWYNwe72.mjs +378 -0
- package/dist/settings.schema.json +1064 -0
- package/dist/simslim-Ca3487H6.mjs +436 -0
- package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-C5cg0fmk.mjs} +4 -4
- package/dist/spawn-claims-CUleC2cM.mjs +49 -0
- package/dist/{start-CmxtOKjU.mjs → start-F9gv5dcC.mjs} +159 -114
- package/dist/start-VoFsPSes.mjs +2 -0
- package/dist/{state-DZobzxLW.mjs → state-WGdIbi_d.mjs} +5 -3
- package/dist/{stats-BYcZ6n1h.mjs → stats-BAgJaLA4.mjs} +66 -5
- package/dist/{stats-CLg5RuDS.mjs → stats-DBYJ5bxL.mjs} +4 -4
- package/dist/status-DLCD-o3s.mjs +764 -0
- package/dist/status-Dr4fRkym.mjs +216 -0
- package/dist/{guide-status-Craq9z0t.mjs → stim-installations-CINXy_su.mjs} +3 -122
- package/dist/{stop-C8JW6kKp.mjs → stop-Dj4pfmyv.mjs} +111 -31
- package/dist/{stop-B1kOpYei.mjs → stop-ZvOeTmKb.mjs} +2 -2
- package/dist/supervisor-run.d.mts +11 -2
- package/dist/supervisor-run.mjs +228 -42
- package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-BXA96myD.mjs} +14 -9
- package/dist/workspaces-BTUyFfkv.mjs +1003 -0
- package/dist/worktree-4aZg2NnI.mjs +2 -0
- package/dist/{worktree-CFLA-6MX.mjs → worktree-BMS7GeqB.mjs} +523 -61
- package/dist/{worktree-FG_ZKKdb.mjs → worktree-BlizKDpZ.mjs} +200 -54
- package/dist/{xcode-B6YaWeg0.mjs → xcode-B2tBkf0m.mjs} +66 -57
- package/package.json +6 -6
- package/skill/SKILL.md +2 -0
- package/dist/app-install-BDbsSgCl.mjs +0 -1175
- package/dist/dev-client-BmT-ioFI.mjs +0 -797
- package/dist/device-lease-Ffz_M5t0.mjs +0 -359
- package/dist/exec-B29XNuhP.mjs +0 -77
- package/dist/logs-query-BGL-Pl12.mjs +0 -290
- package/dist/metro-C7bcJ0uo.mjs +0 -233
- package/dist/ownership-wolE-0Do.mjs +0 -470
- package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
- package/dist/reclaim-KJ-0uWEF.mjs +0 -361
- package/dist/server-expo-YDDG3eP7.mjs +0 -2
- package/dist/spawn-entry-nCE6m770.mjs +0 -14
- package/dist/status-DdkLki47.mjs +0 -426
- package/dist/workspace-state-BT5E3SA2.mjs +0 -146
- package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
|
@@ -1,28 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { a as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
1
|
+
import { g as loadConfig$1, l as getConcurrencyLimits, o as configCorruptRepair } from "./config-BEq0Qw_a.mjs";
|
|
2
|
+
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
3
|
+
import { D as workspaceDerivedData, S as sharedBuildCache } from "./metro-BWLOJ_Wr.mjs";
|
|
4
|
+
import { D as makeTemporaryDirectory, E as filesystemDevice, a as dirtyFingerprintFiles, h as locallyKnownUpstream, k as temporaryRoot, m as listWorktrees, s as gitCommonDir, x as resolveSourceCheckout, y as repoRoot } from "./worktree-BlizKDpZ.mjs";
|
|
5
|
+
import { a as detectIsExpo, n as appProjectProblem, o as findProjectRoot } from "./project-BXZzOABt.mjs";
|
|
6
|
+
import { d as quotedPath, f as refuseNoProject, l as phaseLine, u as plural } from "./command-output-D5z2_bcF.mjs";
|
|
7
|
+
import { T as fingerprintProject, h as listBuildLocks, x as diffFingerprintSources } from "./device-lease-CbEaIk7b.mjs";
|
|
8
|
+
import { S as hostSystemImageArch, w as listAdbDevices } from "./android-787HhP5w.mjs";
|
|
9
|
+
import { F as settingsLayers, H as compilerCacheFallbackMessage, L as unknownSettingKeys, N as settingOrigin, O as remoteAndroidSetting, P as settingShapeErrors, T as projectOptimizations, W as resolveOptimizations, a as SETTING_SHAPE_REMEDY, i as SETTINGS_WITH_RESOLVE_TIME_FALLBACK, j as resolveSettings, k as remoteIosSetting, n as PATH_SETTINGS, v as iosSimSlimProfileSetting, y as mergeSettingsLayers } from "./settings-DOugPlPz.mjs";
|
|
10
|
+
import { h as POOL_SETTING_REMEDY, y as parkedMaxSetting } from "./ownership-DZjdx8Gq.mjs";
|
|
11
|
+
import { O as hostMemoryPressureAdvice, k as readHostMemoryPressure, l as listAllIosSims } from "./ios-B2Fpqvlb.mjs";
|
|
12
|
+
import { o as listBuildSlots } from "./in-use-CjUluqIX.mjs";
|
|
13
|
+
import { L as ownerFromConfig, M as checkEasAuth$1, R as providerFromConfig, _ as easCliSupport, g as MIN_EAS_CLI_SIMULATOR_VERSION, s as readInstalledEasCliVersion, v as easCliUpgradeRemedy, z as resolveEasCliBin } from "./device-remote-Dy1jGj3U.mjs";
|
|
14
|
+
import { n as inspectStimVersions, t as compareStimVersions } from "./stim-installations-CINXy_su.mjs";
|
|
15
|
+
import { a as liveOwnedDeviceCount, l as MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION, n as simslimIsOnPath } from "./simslim-Ca3487H6.mjs";
|
|
16
|
+
import { n as budgetLine, r as inspectBudget } from "./budget-BP4jZZL4.mjs";
|
|
17
|
+
import { d as projectCmakeLauncher, g as androidPathRoomRemedy, h as androidPathRoomMessage, i as apkOutputsDir, m as androidPathRoom, r as resolveAndroidCompilerCache, t as readAndroidCasToolchain } from "./android-cas-CITmIeWY.mjs";
|
|
18
|
+
import { h as podInstallCommand, i as ccacheEnabled, o as detectXcodeMajor } from "./xcode-B2tBkf0m.mjs";
|
|
19
|
+
import { n as recordDoctorRun } from "./guide-status-CWvti2Lg.mjs";
|
|
20
|
+
import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-r9UesA9a.mjs";
|
|
18
21
|
import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, rmSync } from "node:fs";
|
|
19
22
|
import { InvalidArgumentError } from "commander";
|
|
20
|
-
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
21
23
|
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";
|
|
24
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "path";
|
|
25
|
+
import { readJsonObject, settingDefinition } from "@stim-cli/core/state";
|
|
26
|
+
import { basename as basename$1, dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "node:path";
|
|
22
27
|
import { homedir } from "os";
|
|
23
|
-
import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
|
|
24
|
-
import * as expoFingerprint from "@expo/fingerprint";
|
|
25
28
|
import chalk from "chalk";
|
|
29
|
+
import * as expoFingerprint from "@expo/fingerprint";
|
|
26
30
|
//#region src/diagnostics/sandbox.ts
|
|
27
31
|
/**
|
|
28
32
|
* What a sandboxed harness has to permit for Stim to work: its state
|
|
@@ -43,8 +47,8 @@ function detectHarness(env = process.env) {
|
|
|
43
47
|
function expandHome(value, home) {
|
|
44
48
|
const v = value.trim();
|
|
45
49
|
if (v === "~" || v === "$HOME") return home;
|
|
46
|
-
if (v.startsWith("~/")) return join
|
|
47
|
-
if (v.startsWith("$HOME/")) return join
|
|
50
|
+
if (v.startsWith("~/")) return join(home, v.slice(2));
|
|
51
|
+
if (v.startsWith("$HOME/")) return join(home, v.slice(6));
|
|
48
52
|
return v;
|
|
49
53
|
}
|
|
50
54
|
/**
|
|
@@ -54,7 +58,7 @@ function expandHome(value, home) {
|
|
|
54
58
|
*/
|
|
55
59
|
function sandboxBlocksStimHome(stimHome, home = homedir()) {
|
|
56
60
|
const dir = expandHome(stimHome, home);
|
|
57
|
-
const probe = join
|
|
61
|
+
const probe = join(dir, `.probe-${process.pid}-${Math.random().toString(36).slice(2)}`);
|
|
58
62
|
try {
|
|
59
63
|
mkdirSync$1(dir, { recursive: true });
|
|
60
64
|
writeFileSync$1(probe, "");
|
|
@@ -67,14 +71,14 @@ function sandboxBlocksStimHome(stimHome, home = homedir()) {
|
|
|
67
71
|
}
|
|
68
72
|
/** Where a per-user, gitignored Claude Code settings file belongs for this project. */
|
|
69
73
|
function claudeLocalSettingsPath(root) {
|
|
70
|
-
return join
|
|
74
|
+
return join(root, ".claude", "settings.local.json");
|
|
71
75
|
}
|
|
72
76
|
/** Every settings file that may already carry the allowance, most local first. */
|
|
73
77
|
function allowanceSearchPaths(settingsRoot, home = homedir()) {
|
|
74
78
|
return [
|
|
75
79
|
claudeLocalSettingsPath(settingsRoot),
|
|
76
|
-
join
|
|
77
|
-
join
|
|
80
|
+
join(settingsRoot, ".claude", "settings.json"),
|
|
81
|
+
join(home, ".claude", "settings.json")
|
|
78
82
|
];
|
|
79
83
|
}
|
|
80
84
|
function isPlainObject(value) {
|
|
@@ -114,10 +118,10 @@ function nested(source, ...keys) {
|
|
|
114
118
|
function covers(entry, target, home) {
|
|
115
119
|
const posix = (value) => {
|
|
116
120
|
const expanded = expandHome(value, home);
|
|
117
|
-
return sep
|
|
121
|
+
return sep === "/" ? expanded : expanded.replaceAll(sep, "/");
|
|
118
122
|
};
|
|
119
|
-
const e = posix(entry.replace(/\/+\*+$/, "").replace(/\/+$/, "")
|
|
120
|
-
const t = posix(target.replace(/\/+$/, "")
|
|
123
|
+
const e = posix(entry).replace(/\/+\*+$/, "").replace(/\/+$/, "");
|
|
124
|
+
const t = posix(target).replace(/\/+$/, "");
|
|
121
125
|
return e === t || t.startsWith(`${e}/`);
|
|
122
126
|
}
|
|
123
127
|
/**
|
|
@@ -186,7 +190,7 @@ function applyClaudeAllowance(path, allowance) {
|
|
|
186
190
|
status: "refused",
|
|
187
191
|
reason: `${path} holds a "${blocker}" that is not an object, so Stim cannot merge into it without discarding that value.`
|
|
188
192
|
};
|
|
189
|
-
mkdirSync$1(dirname
|
|
193
|
+
mkdirSync$1(dirname(path), { recursive: true });
|
|
190
194
|
const tmp = `${path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
191
195
|
writeFileSync$1(tmp, `${JSON.stringify(withAllowance(existing, allowance), null, 2)}\n`);
|
|
192
196
|
try {
|
|
@@ -247,14 +251,14 @@ function checkStorageLayout(projectRoot, { platform, host = process.platform, de
|
|
|
247
251
|
const source = resolveSourceCheckout(target);
|
|
248
252
|
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.");
|
|
249
253
|
const cache = sharedBuildCache();
|
|
250
|
-
check("Cached app/APK staging", [cache, stagingRoot(join(cache, "artifact.app"))], temporaryFix);
|
|
254
|
+
check("Cached app/APK staging", [cache, stagingRoot(join$1(cache, "artifact.app"))], temporaryFix);
|
|
251
255
|
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.";
|
|
252
|
-
if ((platform === "ios" || platform !== "android" && existsSync(join(projectRoot, "ios"))) && host === "darwin") {
|
|
253
|
-
const output = join(workspaceDerivedData(projectRoot), "Build", "Products");
|
|
256
|
+
if ((platform === "ios" || platform !== "android" && existsSync(join$1(projectRoot, "ios"))) && host === "darwin") {
|
|
257
|
+
const output = join$1(workspaceDerivedData(projectRoot), "Build", "Products");
|
|
254
258
|
check("iOS build-cache storage", [output, cache], cacheFix);
|
|
255
|
-
check("iOS device app staging", [output, stagingRoot(join(output, "artifact.app"))], temporaryFix);
|
|
259
|
+
check("iOS device app staging", [output, stagingRoot(join$1(output, "artifact.app"))], temporaryFix);
|
|
256
260
|
}
|
|
257
|
-
if (platform === "android" || platform !== "ios" && existsSync(join(projectRoot, "android"))) check("Android build-cache storage", [apkOutputsDir(projectRoot), cache], cacheFix);
|
|
261
|
+
if (platform === "android" || platform !== "ios" && existsSync(join$1(projectRoot, "android"))) check("Android build-cache storage", [apkOutputsDir(projectRoot), cache], cacheFix);
|
|
258
262
|
} catch (error) {
|
|
259
263
|
findings.push({
|
|
260
264
|
level: "cost",
|
|
@@ -338,23 +342,23 @@ function parseIosDebugArchitectures(output) {
|
|
|
338
342
|
};
|
|
339
343
|
}
|
|
340
344
|
function inspectIosDebugArchitectures(projectRoot) {
|
|
341
|
-
const iosRoot = join(projectRoot, "ios");
|
|
345
|
+
const iosRoot = join$1(projectRoot, "ios");
|
|
342
346
|
if (!existsSync(iosRoot)) return [];
|
|
343
347
|
const findings = [];
|
|
344
348
|
const unknown = [];
|
|
345
349
|
let projects;
|
|
346
350
|
try {
|
|
347
|
-
projects = readdirSync(iosRoot).filter((name) => name.endsWith(".xcodeproj")).toSorted().map((name) => join(iosRoot, name));
|
|
351
|
+
projects = readdirSync(iosRoot).filter((name) => name.endsWith(".xcodeproj")).toSorted().map((name) => join$1(iosRoot, name));
|
|
348
352
|
} catch {
|
|
349
353
|
projects = [];
|
|
350
354
|
}
|
|
351
355
|
if (!projects.length) unknown.push("app project");
|
|
352
|
-
const pods = join(iosRoot, "Pods", "Pods.xcodeproj");
|
|
356
|
+
const pods = join$1(iosRoot, "Pods", "Pods.xcodeproj");
|
|
353
357
|
if (existsSync(pods)) projects.push(pods);
|
|
354
|
-
else if (existsSync(join(iosRoot, "Podfile"))) unknown.push("Pods project (not generated)");
|
|
358
|
+
else if (existsSync(join$1(iosRoot, "Podfile"))) unknown.push("Pods project (not generated)");
|
|
355
359
|
const deadline = Date.now() + 6e4;
|
|
356
360
|
for (const project of projects) {
|
|
357
|
-
const label = relative(projectRoot, project);
|
|
361
|
+
const label = relative$1(projectRoot, project);
|
|
358
362
|
const timeoutMs = Math.min(3e4, deadline - Date.now());
|
|
359
363
|
if (timeoutMs <= 0) {
|
|
360
364
|
unknown.push(label);
|
|
@@ -416,13 +420,13 @@ function children(path) {
|
|
|
416
420
|
}
|
|
417
421
|
}
|
|
418
422
|
function moduleRoots(root) {
|
|
419
|
-
const modules = [join(root, "android", "app")];
|
|
420
|
-
const dependencies = join(root, "node_modules");
|
|
423
|
+
const modules = [join$1(root, "android", "app")];
|
|
424
|
+
const dependencies = join$1(root, "node_modules");
|
|
421
425
|
for (const name of children(dependencies)) {
|
|
422
426
|
if (name.startsWith(".")) continue;
|
|
423
|
-
const entry = join(dependencies, name);
|
|
424
|
-
const packages = name.startsWith("@") ? children(entry).map((child) => join(entry, child)) : [entry];
|
|
425
|
-
modules.push(...packages.map((pkg) => join(pkg, "android")));
|
|
427
|
+
const entry = join$1(dependencies, name);
|
|
428
|
+
const packages = name.startsWith("@") ? children(entry).map((child) => join$1(entry, child)) : [entry];
|
|
429
|
+
modules.push(...packages.map((pkg) => join$1(pkg, "android")));
|
|
426
430
|
}
|
|
427
431
|
return modules;
|
|
428
432
|
}
|
|
@@ -430,7 +434,7 @@ function cacheFiles(base, depth = 0) {
|
|
|
430
434
|
const files = [];
|
|
431
435
|
for (const name of children(base)) {
|
|
432
436
|
if (depth === 0 && /^stim-[a-f0-9]{16}$/.test(name)) continue;
|
|
433
|
-
const path = join(base, name);
|
|
437
|
+
const path = join$1(base, name);
|
|
434
438
|
const entry = lstatSync(path);
|
|
435
439
|
if (entry.isSymbolicLink()) continue;
|
|
436
440
|
if (name === "CMakeCache.txt" && entry.isFile()) files.push(path);
|
|
@@ -439,8 +443,8 @@ function cacheFiles(base, depth = 0) {
|
|
|
439
443
|
return files;
|
|
440
444
|
}
|
|
441
445
|
function readCxxLauncherStates(root) {
|
|
442
|
-
return moduleRoots(root).flatMap((module) => cacheFiles(join(module, ".cxx")).map((path) => ({
|
|
443
|
-
path: relative(root, path),
|
|
446
|
+
return moduleRoots(root).flatMap((module) => cacheFiles(join$1(module, ".cxx")).map((path) => ({
|
|
447
|
+
path: relative$1(root, path),
|
|
444
448
|
launcher: parseCmakeCacheLauncher(readFileSync(path, "utf8"))
|
|
445
449
|
})));
|
|
446
450
|
}
|
|
@@ -450,13 +454,13 @@ function declaredLauncher(module) {
|
|
|
450
454
|
"build.gradle.kts",
|
|
451
455
|
"CMakeLists.txt"
|
|
452
456
|
].some((name) => {
|
|
453
|
-
const path = join(module, name);
|
|
457
|
+
const path = join$1(module, name);
|
|
454
458
|
return existsSync(path) && Boolean(projectCmakeLauncher(readFileSync(path, "utf8")));
|
|
455
459
|
});
|
|
456
460
|
}
|
|
457
461
|
function contained(root, path) {
|
|
458
|
-
const rel = relative(root, path);
|
|
459
|
-
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
462
|
+
const rel = relative$1(root, path);
|
|
463
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep$1}`) && !isAbsolute$1(rel);
|
|
460
464
|
}
|
|
461
465
|
function selectedCompilerCache(root) {
|
|
462
466
|
try {
|
|
@@ -480,24 +484,24 @@ function repairCxxLauncherState(root) {
|
|
|
480
484
|
};
|
|
481
485
|
if (selectedCompilerCache(root) !== "ccache") return result;
|
|
482
486
|
if (!getExecutor().findExecutable("ccache")) return result;
|
|
483
|
-
const appOverrides = declaredLauncher(join(root, "android", "app"));
|
|
487
|
+
const appOverrides = declaredLauncher(join$1(root, "android", "app"));
|
|
484
488
|
const targets = readCxxLauncherStates(root).filter(({ launcher }) => {
|
|
485
489
|
if (!launcher) return true;
|
|
486
490
|
const command = launcher.split(";")[0]?.trim();
|
|
487
|
-
return Boolean(command && isAbsolute(command) && basename(command) === "ccache" && !existsSync(command));
|
|
491
|
+
return Boolean(command && isAbsolute$1(command) && basename$1(command) === "ccache" && !existsSync(command));
|
|
488
492
|
});
|
|
489
493
|
for (const state of targets) {
|
|
490
|
-
const target = dirname(join(root, state.path));
|
|
491
|
-
const label = relative(root, target);
|
|
494
|
+
const target = dirname$1(join$1(root, state.path));
|
|
495
|
+
const label = relative$1(root, target);
|
|
492
496
|
try {
|
|
493
497
|
let cxx = target;
|
|
494
|
-
while (contained(root, cxx) && basename(cxx) !== ".cxx") cxx = dirname(cxx);
|
|
495
|
-
if (basename(cxx) !== ".cxx" || target === cxx) throw new Error("unrecognized generated CMake layout");
|
|
496
|
-
if (appOverrides || declaredLauncher(dirname(cxx))) throw new Error("project configures its own compiler launcher");
|
|
498
|
+
while (contained(root, cxx) && basename$1(cxx) !== ".cxx") cxx = dirname$1(cxx);
|
|
499
|
+
if (basename$1(cxx) !== ".cxx" || target === cxx) throw new Error("unrecognized generated CMake layout");
|
|
500
|
+
if (appOverrides || declaredLauncher(dirname$1(cxx))) throw new Error("project configures its own compiler launcher");
|
|
497
501
|
const canonicalTarget = realpathSync(target);
|
|
498
502
|
if (!contained(canonicalRoot, canonicalTarget)) throw new Error("generated directory resolves outside this checkout");
|
|
499
|
-
if (realpathSync(cxx) !== join(realpathSync(dirname(cxx)), ".cxx")) throw new Error(".cxx is a symbolic link");
|
|
500
|
-
const gitPath = relative(canonicalRoot, canonicalTarget);
|
|
503
|
+
if (realpathSync(cxx) !== join$1(realpathSync(dirname$1(cxx)), ".cxx")) throw new Error(".cxx is a symbolic link");
|
|
504
|
+
const gitPath = relative$1(canonicalRoot, canonicalTarget);
|
|
501
505
|
if (getExecutor().runFile("git", [
|
|
502
506
|
"ls-files",
|
|
503
507
|
"-z",
|
|
@@ -551,14 +555,13 @@ function readMachineSettings(context) {
|
|
|
551
555
|
layers: [],
|
|
552
556
|
corrupt: {
|
|
553
557
|
level: "note",
|
|
554
|
-
title: "The Stim config is not valid JSON",
|
|
558
|
+
title: "The Stim config is not a valid JSON object",
|
|
555
559
|
detail: `${String(error.message).split("\n")[0]}. Every setting and device record lives in that file, so no other machine-level check can run until it parses.`,
|
|
556
560
|
fix: configCorruptRepair()
|
|
557
561
|
}
|
|
558
562
|
};
|
|
559
563
|
}
|
|
560
564
|
}
|
|
561
|
-
const PATH_SETTING_ENVIRONMENT = { "optimizations.android.casToolchain": "STIM_ANDROID_CAS_TOOLCHAIN" };
|
|
562
565
|
const PATH_SETTINGS_TRIMMED_BY_THEIR_CONSUMER = /* @__PURE__ */ new Set(["android.keystore"]);
|
|
563
566
|
function checkMachineSettings({ settings, layers, projectRoot, optimizations = null, reportedElsewhere = [], exists = existsSync, env = process.env }) {
|
|
564
567
|
const findings = [];
|
|
@@ -570,7 +573,7 @@ function checkMachineSettings({ settings, layers, projectRoot, optimizations = n
|
|
|
570
573
|
if (reported) skip.add(reported.key);
|
|
571
574
|
if (!optimizations) for (const key of SETTINGS_WITH_RESOLVE_TIME_FALLBACK) skip.add(key);
|
|
572
575
|
for (const key of PATH_SETTINGS) {
|
|
573
|
-
const variable =
|
|
576
|
+
const variable = settingDefinition(key)?.env;
|
|
574
577
|
if (skip.has(key) || variable && skip.has(variable)) continue;
|
|
575
578
|
const override = variable ? env[variable] : void 0;
|
|
576
579
|
if (variable && override) {
|
|
@@ -639,7 +642,7 @@ function compilerCacheFinding({ fallback, optimizations, file }) {
|
|
|
639
642
|
function missingPath(value, projectRoot, exists, trim = false) {
|
|
640
643
|
const opened = trim ? value.trim() : value;
|
|
641
644
|
if (opened.trim() === "") return null;
|
|
642
|
-
const path = isAbsolute(opened) ? opened : resolve(projectRoot, opened);
|
|
645
|
+
const path = isAbsolute$1(opened) ? opened : resolve$1(projectRoot, opened);
|
|
643
646
|
if (exists(path)) return null;
|
|
644
647
|
return opened === opened.trim() ? path : JSON.stringify(path);
|
|
645
648
|
}
|
|
@@ -653,22 +656,15 @@ function finding(level, title, detail, fix) {
|
|
|
653
656
|
fix
|
|
654
657
|
};
|
|
655
658
|
}
|
|
656
|
-
function readJson(path) {
|
|
657
|
-
try {
|
|
658
|
-
return JSON.parse(readFileSync$1(path, "utf-8"));
|
|
659
|
-
} catch {
|
|
660
|
-
return null;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
659
|
function mainCheckoutProjectRoot(projectRoot) {
|
|
664
660
|
const currentRepoRoot = repoRoot(projectRoot);
|
|
665
661
|
if (!currentRepoRoot) return projectRoot;
|
|
666
662
|
try {
|
|
667
663
|
const source = resolveSourceCheckout(currentRepoRoot);
|
|
668
664
|
if ("refusal" in source) return projectRoot;
|
|
669
|
-
const projectRel = relative
|
|
665
|
+
const projectRel = relative(currentRepoRoot, realpathSync$1(projectRoot));
|
|
670
666
|
if (projectRel.startsWith("..")) return projectRoot;
|
|
671
|
-
return resolve
|
|
667
|
+
return resolve(source.path, projectRel);
|
|
672
668
|
} catch {
|
|
673
669
|
return projectRoot;
|
|
674
670
|
}
|
|
@@ -687,11 +683,11 @@ function brokenPodLinks(podsRoot) {
|
|
|
687
683
|
}
|
|
688
684
|
}
|
|
689
685
|
function hasIosWarmOutput(root) {
|
|
690
|
-
if (existsSync$1(join
|
|
686
|
+
if (existsSync$1(join(root, "ios", "build"))) return true;
|
|
691
687
|
const derivedData = workspaceDerivedData(root);
|
|
692
|
-
if (existsSync$1(join
|
|
688
|
+
if (existsSync$1(join(derivedData, "Build", "Products"))) return true;
|
|
693
689
|
try {
|
|
694
|
-
return readdirSync$1(derivedData).some((entry) => /^scheme-[a-f0-9]{64}$/.test(entry) && existsSync$1(join
|
|
690
|
+
return readdirSync$1(derivedData).some((entry) => /^scheme-[a-f0-9]{64}$/.test(entry) && existsSync$1(join(derivedData, entry, "Build", "Products")));
|
|
695
691
|
} catch {
|
|
696
692
|
return false;
|
|
697
693
|
}
|
|
@@ -720,8 +716,8 @@ function mainOperation(root) {
|
|
|
720
716
|
"--git-dir"
|
|
721
717
|
])?.trim();
|
|
722
718
|
if (!gitDir) return null;
|
|
723
|
-
if (existsSync$1(join
|
|
724
|
-
return existsSync$1(join
|
|
719
|
+
if (existsSync$1(join(gitDir, "rebase-merge")) || existsSync$1(join(gitDir, "rebase-apply"))) return "rebase";
|
|
720
|
+
return existsSync$1(join(gitDir, "MERGE_HEAD")) ? "merge" : null;
|
|
725
721
|
}
|
|
726
722
|
function dirtyTrackedPaths(root) {
|
|
727
723
|
return (getExecutor().runFileQuiet("git", [
|
|
@@ -785,9 +781,9 @@ function checkMainCheckout(projectRoot, { npmTreeValid, brokenPods = void 0, ups
|
|
|
785
781
|
if ((npmTreeValid === void 0 ? installedNpmTreeIsValid(dependencies.root) : npmTreeValid) === false) findings.push(finding("cost", "The source checkout dependency tree is stale", `npm reports that ${dependencies.root}/node_modules does not match the project dependency graph. Copying it makes each worktree start from the same invalid state.`, `Run \`${installCommand}\` before creating native worktrees, or let \`stim worktree warm --refresh\` run it from a linked worktree.`));
|
|
786
782
|
}
|
|
787
783
|
}
|
|
788
|
-
const podfileLock = join
|
|
789
|
-
const podManifest = join
|
|
790
|
-
const podsRoot = join
|
|
784
|
+
const podfileLock = join(mainRoot, "ios", "Podfile.lock");
|
|
785
|
+
const podManifest = join(mainRoot, "ios", "Pods", "Manifest.lock");
|
|
786
|
+
const podsRoot = join(mainRoot, "ios", "Pods");
|
|
791
787
|
if (localIos && existsSync$1(podfileLock)) {
|
|
792
788
|
let podsState = null;
|
|
793
789
|
if (!existsSync$1(podManifest)) podsState = "missing";
|
|
@@ -797,18 +793,18 @@ function checkMainCheckout(projectRoot, { npmTreeValid, brokenPods = void 0, ups
|
|
|
797
793
|
podsState = "stale";
|
|
798
794
|
}
|
|
799
795
|
if (podsState) {
|
|
800
|
-
const iosRoot = join
|
|
796
|
+
const iosRoot = join(mainRoot, "ios");
|
|
801
797
|
const podCommand = `cd ${quotedPath(iosRoot)} && ${podInstallCommand(mainRoot)}`;
|
|
802
798
|
findings.push(finding("cost", `The source checkout CocoaPods state is ${podsState}`, `ios/Pods cannot be reused safely because its Manifest.lock ${podsState === "missing" ? "is absent" : "does not match ios/Podfile.lock"}.`, `Run \`${podCommand}\` before creating native worktrees, or let \`stim worktree warm --refresh\` run it from a linked worktree.`));
|
|
803
799
|
}
|
|
804
800
|
const broken = brokenPods === void 0 && existsSync$1(podsRoot) ? brokenPodLinks(podsRoot) : brokenPods || [];
|
|
805
801
|
if (broken.length) {
|
|
806
|
-
const iosRoot = join
|
|
802
|
+
const iosRoot = join(mainRoot, "ios");
|
|
807
803
|
const podCommand = `cd ${quotedPath(iosRoot)} && ${podInstallCommand(mainRoot, "--clean-install")}`;
|
|
808
804
|
findings.push(finding("cost", "The source checkout CocoaPods state has broken links", `${broken.length} symlink${broken.length === 1 ? "" : "s"} under ios/Pods point to missing files. Worktrees copy these broken links and can fail during compilation. First: ${broken[0]}.`, `Run \`${podCommand}\` before creating native worktrees.`));
|
|
809
805
|
}
|
|
810
806
|
}
|
|
811
|
-
const coldPlatforms = [localIos && existsSync$1(join
|
|
807
|
+
const coldPlatforms = [localIos && existsSync$1(join(mainRoot, "ios")) && !hasIosWarmOutput(mainRoot) ? "iOS" : null, platform !== "ios" && existsSync$1(join(mainRoot, "android")) && !existsSync$1(join(mainRoot, "android", "build")) && !existsSync$1(join(mainRoot, "android", "app", "build")) ? "Android" : null].filter((coldPlatform) => coldPlatform !== null);
|
|
812
808
|
if (coldPlatforms.length) findings.push(finding("note", `The source checkout has no ${coldPlatforms.join(" or ")} warm build output`, "The shared Stim artifact or compilation cache can still be warm. Building the source checkout once gives later native worktrees the strongest warm starting point.", `When more native worktrees are expected, run \`stim start\`, \`stim ${coldPlatforms[0] === "iOS" ? "ios" : "android"}\`, and \`stim stop\` from ${mainRoot}.`));
|
|
813
809
|
if (linkedWorktrees === void 0 ? hasLinkedWorktree(projectRoot) : linkedWorktrees) findings.push(...seedFindings(mainRoot, upstream === void 0 ? locallyKnownUpstream(mainRoot) : upstream));
|
|
814
810
|
return findings;
|
|
@@ -855,7 +851,7 @@ function checkCcacheInstalled(onPath) {
|
|
|
855
851
|
}
|
|
856
852
|
function launcherPath(launcher) {
|
|
857
853
|
const command = launcher?.split(";")[0]?.trim();
|
|
858
|
-
return command && isAbsolute
|
|
854
|
+
return command && isAbsolute(command) ? command : null;
|
|
859
855
|
}
|
|
860
856
|
function checkCxxCompilerLauncher({ states, ccacheOnPath, launcherExists = existsSync$1 }) {
|
|
861
857
|
const missing = states.map((state) => ({
|
|
@@ -904,7 +900,11 @@ function checkConcurrency({ maxBuilds = 0, maxDevices = 0, liveDevices = 0, acti
|
|
|
904
900
|
if (!maxBuilds && !maxDevices) return null;
|
|
905
901
|
return finding("note", "Concurrency limits are set", `${`maxBuilds ${maxBuilds || "unlimited"}, maxDevices ${maxDevices || "unlimited"}`}. Right now ${liveDevices} Stim device(s) are booted and ${activeBuilds} build slot(s) are in use on this machine. At the device cap a new \`stim ios\`/\`android\` is refused with STIM_AT_CAPACITY (stop an environment or raise it); at the build cap a compile waits for a free slot.`, null);
|
|
906
902
|
}
|
|
907
|
-
function
|
|
903
|
+
function readProjectEasCliVersion(projectRoot) {
|
|
904
|
+
const bin = resolveEasCliBin(projectRoot);
|
|
905
|
+
return bin ? readInstalledEasCliVersion(bin.file, projectRoot) : null;
|
|
906
|
+
}
|
|
907
|
+
function checkRemoteDevice({ configured = null, daemonInEnv = false, agentDeviceOnPath = false, easCliResolvable = false, readEasCliVersion = () => null } = {}) {
|
|
908
908
|
if (!configured) return null;
|
|
909
909
|
if (!agentDeviceOnPath) return finding("cost", "A remote device is configured, but agent-device is missing", `agent-device drives the selected remote backend. Without it, \`stim ios --remote ${configured}\` and \`stim android --remote ${configured}\` refuse before device work.`, "npm i -g agent-device");
|
|
910
910
|
if (configured === "proxy") {
|
|
@@ -912,8 +912,25 @@ function checkRemoteDevice({ configured = null, daemonInEnv = false, agentDevice
|
|
|
912
912
|
return finding("cost", "The remote proxy credentials are missing", "The proxy backend requires AGENT_DEVICE_DAEMON_BASE_URL and AGENT_DEVICE_DAEMON_AUTH_TOKEN.", "Export AGENT_DEVICE_DAEMON_BASE_URL and AGENT_DEVICE_DAEMON_AUTH_TOKEN.");
|
|
913
913
|
}
|
|
914
914
|
if (!easCliResolvable) return finding("cost", "A remote device is configured, but there is no eas-cli to create a session with", "The eas backend creates an EAS Simulator session. It needs eas-cli and an account with EAS Simulator access. Neither a project copy nor one on PATH was found.", "Install eas-cli.");
|
|
915
|
+
const easCli = easCliSupport(readEasCliVersion(), MIN_EAS_CLI_SIMULATOR_VERSION);
|
|
916
|
+
if (!easCli.supported) return finding("cost", easCli.version ? `A remote device is configured, but eas-cli ${easCli.version} has no EAS Simulator commands` : "A remote device is configured, but the eas-cli version could not be read", `The eas backend runs \`eas simulator:*\` commands, which need eas-cli ${MIN_EAS_CLI_SIMULATOR_VERSION} or later. \`stim ios --remote eas\` and \`stim android --remote eas\` refuse with STIM_REMOTE_EAS_UNAVAILABLE before device work.`, `${easCliUpgradeRemedy(MIN_EAS_CLI_SIMULATOR_VERSION)}.`);
|
|
915
917
|
return finding("note", "This project uses a remote device", "`ios --remote eas` / `android --remote eas` create an EAS Simulator session named stim-<label> and end it on `stop` and `worktree remove`. The build still runs on this machine; only the device is elsewhere. Native device logs are not captured on a remote device -- the Metro half of the timeline is unaffected.", null);
|
|
916
918
|
}
|
|
919
|
+
function checkEasBuildDownload({ easJson = false, remoteEas = false, easCliResolvable = false, readEasCliVersion = () => null } = {}) {
|
|
920
|
+
if (!easJson || remoteEas || !easCliResolvable) return null;
|
|
921
|
+
const easCli = easCliSupport(readEasCliVersion(), MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION);
|
|
922
|
+
if (easCli.supported) return null;
|
|
923
|
+
return finding("cost", easCli.version ? `eas-cli ${easCli.version} cannot download EAS builds for --eas-profile` : "The eas-cli version could not be read for --eas-profile", `\`stim ios --eas-profile\` and \`stim android --eas-profile\` download a finished EAS development build with \`eas build:download --build-id\`, which needs eas-cli ${MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION} or later. Older versions refuse with STIM_EAS_UNAVAILABLE before any download.`, `${easCliUpgradeRemedy(MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION)}.`);
|
|
924
|
+
}
|
|
925
|
+
function projectEasBuildDownloadFinding(projectRoot, remoteBackends, lookupEasCli) {
|
|
926
|
+
const easJson = existsSync$1(join(projectRoot, "eas.json"));
|
|
927
|
+
return checkEasBuildDownload({
|
|
928
|
+
easJson,
|
|
929
|
+
remoteEas: remoteBackends.includes("eas"),
|
|
930
|
+
easCliResolvable: easJson && (lookupEasCli ? lookupEasCli() : Boolean(resolveEasCliBin(projectRoot))),
|
|
931
|
+
readEasCliVersion: () => readProjectEasCliVersion(projectRoot)
|
|
932
|
+
});
|
|
933
|
+
}
|
|
917
934
|
function checkSimSlim({ configured = false, profileError = null, onPath = false } = {}) {
|
|
918
935
|
if (profileError) return finding("cost", "The SimSlim profile is invalid", profileError, "Set ios.simslimProfile to a readable JSON profile inside the repository.");
|
|
919
936
|
if (!configured) return finding("note", "SimSlim is recommended for parallel iOS simulator work", "A reviewed profile can reduce background simulator services and memory use. It is optional: disabling services can affect tests that depend on them, and installing the binary alone does not apply a profile.", "Run `stim guide lifecycle simslim` to install SimSlim and configure ios.simslimProfile. Stim does not enable a profile through doctor --fix.");
|
|
@@ -926,7 +943,7 @@ function checkIosHost(platform, host) {
|
|
|
926
943
|
}
|
|
927
944
|
function checkXcodeEnvLineEndings(projectRoot, platform, host) {
|
|
928
945
|
if (platform === "android" || host !== "win32") return null;
|
|
929
|
-
const xcodeEnv = join
|
|
946
|
+
const xcodeEnv = join(projectRoot, "ios", ".xcode.env");
|
|
930
947
|
if (!existsSync$1(xcodeEnv)) return null;
|
|
931
948
|
let content;
|
|
932
949
|
try {
|
|
@@ -940,7 +957,7 @@ function checkXcodeEnvLineEndings(projectRoot, platform, host) {
|
|
|
940
957
|
function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null, easAuth = checkEasAuth$1, concurrency = getConcurrencyLimits, liveDevices = null, activeBuilds = null, remoteEnv = process.env, lookupAgentDevice = null, lookupEasCli = null, lookupSimSlim = null, memoryPressure = readHostMemoryPressure, lookupCcache = null, platform, host = process.platform } = {}) {
|
|
941
958
|
const localIos = platform !== "android" && host === "darwin";
|
|
942
959
|
const read = (rel) => {
|
|
943
|
-
const p = join
|
|
960
|
+
const p = join(projectRoot, rel);
|
|
944
961
|
if (!existsSync$1(p)) return null;
|
|
945
962
|
try {
|
|
946
963
|
return readFile(p, "utf-8");
|
|
@@ -956,15 +973,15 @@ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null,
|
|
|
956
973
|
});
|
|
957
974
|
if (machineSettings.corrupt) return [machineSettings.corrupt];
|
|
958
975
|
const projectSettings = machineSettings.settings;
|
|
959
|
-
const pkg =
|
|
960
|
-
const appConfig =
|
|
976
|
+
const pkg = readJsonObject(join(projectRoot, "package.json"));
|
|
977
|
+
const appConfig = readJsonObject(join(projectRoot, "app.json"));
|
|
961
978
|
const dynamicConfig = appConfig ? null : [
|
|
962
979
|
"app.config.ts",
|
|
963
980
|
"app.config.js",
|
|
964
981
|
"app.config.mjs"
|
|
965
|
-
].find((f) => existsSync$1(join
|
|
966
|
-
const podfileProperties =
|
|
967
|
-
const podfile = read(join
|
|
982
|
+
].find((f) => existsSync$1(join(projectRoot, f))) || null;
|
|
983
|
+
const podfileProperties = readJsonObject(join(projectRoot, "ios", "Podfile.properties.json"));
|
|
984
|
+
const podfile = read(join("ios", "Podfile"));
|
|
968
985
|
const metroConfig = read("metro.config.js") ?? read("metro.config.cjs");
|
|
969
986
|
const isExpo = detectIsExpo(projectRoot);
|
|
970
987
|
const expoRange = (pkg?.dependencies)?.expo || "";
|
|
@@ -986,9 +1003,7 @@ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null,
|
|
|
986
1003
|
optimizations: resolveOptimizations(projectSettings),
|
|
987
1004
|
use: readAndroidCasToolchain
|
|
988
1005
|
}).optimizations;
|
|
989
|
-
} catch
|
|
990
|
-
settingShapeFindings.push(finding("cost", "Invalid optimization setting", error.message, SETTING_SHAPE_REMEDY));
|
|
991
|
-
}
|
|
1006
|
+
} catch {}
|
|
992
1007
|
const remoteBuildCache = optimizations?.buildCache && optimizations.remoteBuildCache;
|
|
993
1008
|
const easFinding = remoteBuildCache && provider === "eas" ? checkEasAuth({
|
|
994
1009
|
provider,
|
|
@@ -1023,8 +1038,10 @@ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null,
|
|
|
1023
1038
|
configured: backend,
|
|
1024
1039
|
daemonInEnv,
|
|
1025
1040
|
agentDeviceOnPath,
|
|
1026
|
-
easCliResolvable
|
|
1041
|
+
easCliResolvable,
|
|
1042
|
+
readEasCliVersion: () => readProjectEasCliVersion(projectRoot)
|
|
1027
1043
|
})).filter((remoteFinding) => remoteFinding !== null);
|
|
1044
|
+
const easBuildDownloadFinding = projectEasBuildDownloadFinding(projectRoot, remoteBackends, lookupEasCli);
|
|
1028
1045
|
const memoryAdvice = localIos && !remoteIosSetting(projectSettings) ? hostMemoryPressureAdvice(memoryPressure()) : null;
|
|
1029
1046
|
return [
|
|
1030
1047
|
checkAppProject(projectRoot),
|
|
@@ -1046,6 +1063,7 @@ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null,
|
|
|
1046
1063
|
checkAndroidPathRoom(projectRoot, platform, host),
|
|
1047
1064
|
remoteBuildCache ? checkBuildCacheProvider(appConfig, sdkMajor, isExpo, dynamicConfig) : null,
|
|
1048
1065
|
easFinding,
|
|
1066
|
+
easBuildDownloadFinding,
|
|
1049
1067
|
concurrencyFinding,
|
|
1050
1068
|
memoryAdvice ? finding("cost", "Host memory pressure can stall the iOS simulator", memoryAdvice, "Free host memory, then retry. Run `stim guide lifecycle simslim` for the optional memory reduction setup.") : null,
|
|
1051
1069
|
simslimFinding,
|
|
@@ -1072,7 +1090,7 @@ function checkAndroidPathRoom(projectRoot, platform, host, hostArch = process.ar
|
|
|
1072
1090
|
};
|
|
1073
1091
|
}
|
|
1074
1092
|
function androidCcacheFindings(projectRoot, platform, lookupCcache) {
|
|
1075
|
-
if (platform !== "android" && !existsSync$1(join
|
|
1093
|
+
if (platform !== "android" && !existsSync$1(join(projectRoot, "android"))) return [];
|
|
1076
1094
|
const onPath = lookupCcache ? lookupCcache() : ccacheIsOnPath();
|
|
1077
1095
|
let cxx;
|
|
1078
1096
|
try {
|
|
@@ -1113,7 +1131,7 @@ function countLiveDevices() {
|
|
|
1113
1131
|
adb = listAdbDevices() || adb;
|
|
1114
1132
|
} catch {}
|
|
1115
1133
|
try {
|
|
1116
|
-
config = loadConfig();
|
|
1134
|
+
config = loadConfig$1();
|
|
1117
1135
|
} catch {}
|
|
1118
1136
|
return liveOwnedDeviceCount({
|
|
1119
1137
|
sims,
|
|
@@ -1135,7 +1153,7 @@ function checkFingerprintParity({ projectHash, worktreeHash, changed = [], dirty
|
|
|
1135
1153
|
}
|
|
1136
1154
|
function gitMetadataAt(path) {
|
|
1137
1155
|
try {
|
|
1138
|
-
const git = lstatSync$1(join
|
|
1156
|
+
const git = lstatSync$1(join(path, ".git"));
|
|
1139
1157
|
return git.isFile() || git.isDirectory();
|
|
1140
1158
|
} catch {
|
|
1141
1159
|
return false;
|
|
@@ -1150,14 +1168,14 @@ function listDirectory(dir) {
|
|
|
1150
1168
|
}
|
|
1151
1169
|
function hasLinkedPackageWithGit(projectRoot) {
|
|
1152
1170
|
const root = repoRoot(projectRoot) ?? projectRoot;
|
|
1153
|
-
for (let dir = projectRoot;; dir = dirname
|
|
1154
|
-
if (linkedPackageWithGitIn(join
|
|
1155
|
-
if (dir === root || dirname
|
|
1171
|
+
for (let dir = projectRoot;; dir = dirname(dir)) {
|
|
1172
|
+
if (linkedPackageWithGitIn(join(dir, "node_modules"))) return true;
|
|
1173
|
+
if (dir === root || dirname(dir) === dir || relative(root, dirname(dir)).startsWith("..")) return false;
|
|
1156
1174
|
}
|
|
1157
1175
|
}
|
|
1158
1176
|
function linkedPackageWithGitIn(nodeModules) {
|
|
1159
|
-
return listDirectory(nodeModules).filter((entry) => !entry.startsWith(".")).flatMap((entry) => entry.startsWith("@") ? listDirectory(join
|
|
1160
|
-
const root = join
|
|
1177
|
+
return listDirectory(nodeModules).filter((entry) => !entry.startsWith(".")).flatMap((entry) => entry.startsWith("@") ? listDirectory(join(nodeModules, entry)).map((child) => join(entry, child)) : [entry]).some((name) => {
|
|
1178
|
+
const root = join(nodeModules, name);
|
|
1161
1179
|
try {
|
|
1162
1180
|
return lstatSync$1(root).isSymbolicLink() && gitMetadataAt(root);
|
|
1163
1181
|
} catch {
|
|
@@ -1209,14 +1227,14 @@ async function detectFingerprintParity(projectRoot, { createFingerprint = expoFi
|
|
|
1209
1227
|
"--git-dir"
|
|
1210
1228
|
], { timeoutMs: 1e4 }) == null) return null;
|
|
1211
1229
|
if (hasInstalledDependencies(projectRoot)) return null;
|
|
1212
|
-
const platform = selectedPlatform ?? (existsSync$1(join
|
|
1230
|
+
const platform = selectedPlatform ?? (existsSync$1(join(projectRoot, "ios")) ? "ios" : existsSync$1(join(projectRoot, "android")) ? "android" : void 0);
|
|
1213
1231
|
let base;
|
|
1214
1232
|
try {
|
|
1215
1233
|
base = makeTemporaryDirectory(projectRoot, "stim-parity-");
|
|
1216
1234
|
} catch {
|
|
1217
1235
|
return null;
|
|
1218
1236
|
}
|
|
1219
|
-
const worktree = join
|
|
1237
|
+
const worktree = join(base, "head");
|
|
1220
1238
|
if (exec.runFileQuiet("git", [
|
|
1221
1239
|
"-C",
|
|
1222
1240
|
projectRoot,
|
|
@@ -1300,12 +1318,16 @@ function stimVersionLines(report) {
|
|
|
1300
1318
|
phaseLine("installs", paths + versions)
|
|
1301
1319
|
];
|
|
1302
1320
|
}
|
|
1303
|
-
function
|
|
1321
|
+
function budgetLines(budget) {
|
|
1322
|
+
return budget ? [phaseLine("budget", budgetLine(budget))] : [];
|
|
1323
|
+
}
|
|
1324
|
+
function doctorSuccessLines(platform, stim, budget = null) {
|
|
1304
1325
|
const lines = [
|
|
1305
1326
|
`Doctor (${doctorTarget(platform)})`,
|
|
1306
1327
|
phaseLine("result", "PASS"),
|
|
1307
1328
|
phaseLine("findings", "0"),
|
|
1308
1329
|
...stimVersionLines(stim),
|
|
1330
|
+
...budgetLines(budget),
|
|
1309
1331
|
"",
|
|
1310
1332
|
"Project",
|
|
1311
1333
|
phaseLine("project", "source checkout, dependencies, local upstream"),
|
|
@@ -1382,11 +1404,10 @@ function applySandboxFix(root, env = process.env) {
|
|
|
1382
1404
|
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."));
|
|
1383
1405
|
}
|
|
1384
1406
|
function doctorCommand(program, version, inspectVersions = inspectStimVersions, host = process.platform) {
|
|
1385
|
-
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) => {
|
|
1407
|
+
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 in a React Native or Expo app 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) => {
|
|
1386
1408
|
const root = findProjectRoot(process.cwd());
|
|
1387
1409
|
if (!root) {
|
|
1388
|
-
|
|
1389
|
-
process.exitCode = 1;
|
|
1410
|
+
refuseNoProject({ json: Boolean(opts.json) });
|
|
1390
1411
|
return;
|
|
1391
1412
|
}
|
|
1392
1413
|
if (opts.fix) {
|
|
@@ -1417,18 +1438,21 @@ function doctorCommand(program, version, inspectVersions = inspectStimVersions,
|
|
|
1417
1438
|
}
|
|
1418
1439
|
const shadowed = shadowedStimFinding(stim);
|
|
1419
1440
|
if (shadowed) findings.push(shadowed);
|
|
1441
|
+
const budget = await inspectBudget(root);
|
|
1442
|
+
findings.push(...budget.findings);
|
|
1420
1443
|
if (opts.json) {
|
|
1421
1444
|
console.log(JSON.stringify({
|
|
1422
1445
|
project: root,
|
|
1423
1446
|
platform: opts.platform ?? null,
|
|
1424
1447
|
stim,
|
|
1448
|
+
budget: budget.report,
|
|
1425
1449
|
findings
|
|
1426
1450
|
}));
|
|
1427
1451
|
recordDoctorRun(root, opts.platform, version);
|
|
1428
1452
|
return;
|
|
1429
1453
|
}
|
|
1430
1454
|
if (findings.length === 0) {
|
|
1431
|
-
const lines = doctorSuccessLines(opts.platform, stim);
|
|
1455
|
+
const lines = doctorSuccessLines(opts.platform, stim, budget.report);
|
|
1432
1456
|
for (const [index, line] of lines.entries()) if (index === 1) console.log(chalk.green(line));
|
|
1433
1457
|
else if (line && !line.startsWith(" ")) console.log(chalk.bold(line));
|
|
1434
1458
|
else console.log(chalk.dim(line));
|
|
@@ -1437,7 +1461,7 @@ function doctorCommand(program, version, inspectVersions = inspectStimVersions,
|
|
|
1437
1461
|
}
|
|
1438
1462
|
const ordered = findings.toSorted((a, b) => a.level === b.level ? 0 : a.level === "cost" ? -1 : 1);
|
|
1439
1463
|
console.log(chalk.bold(`Doctor (${doctorTarget(opts.platform)})`));
|
|
1440
|
-
for (const line of stimVersionLines(stim)) console.log(chalk.dim(line));
|
|
1464
|
+
for (const line of [...stimVersionLines(stim), ...budgetLines(budget.report)]) console.log(chalk.dim(line));
|
|
1441
1465
|
for (const f of ordered) {
|
|
1442
1466
|
const tag = f.level === "cost" ? chalk.yellow("costs time") : chalk.dim("note");
|
|
1443
1467
|
console.log(`\n${tag} ${chalk.bold(f.title)}`);
|