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,16 +1,538 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
1
|
+
import { u as getConfigDir } from "./config-BEq0Qw_a.mjs";
|
|
2
|
+
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
3
|
+
import { r as createNdjsonWriter } from "./ndjson-CDY9IkVC.mjs";
|
|
4
|
+
import { N as workspaceStateFile, O as workspaceDir, P as withDirLock, i as pidExists, j as workspaceLogsDir, k as workspaceId } from "./metro-BWLOJ_Wr.mjs";
|
|
5
|
+
import { c as ownedDeviceLabel, u as resolvePackageJson } from "./project-BXZzOABt.mjs";
|
|
6
|
+
import { a as clearRemoteSession, l as readRemoteSession, s as readMetroTunnel } from "./state-WGdIbi_d.mjs";
|
|
7
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BXA96myD.mjs";
|
|
8
|
+
import { n as stripAnsi } from "./process-output-Fpl8PYi8.mjs";
|
|
9
|
+
import { B as planMetroReach } from "./settings-DOugPlPz.mjs";
|
|
10
|
+
import { u as probePublicHttp } from "./ownership-DZjdx8Gq.mjs";
|
|
11
|
+
import { C as sanitizeDeviceLabel } from "./ios-B2Fpqvlb.mjs";
|
|
12
|
+
import { n as expoBinFromPackage, r as expoBinPath, s as findBinUpward } from "./server-expo-BvOCeakp.mjs";
|
|
13
|
+
import { a as LAUNCH_ERROR, i as INSTALL_ERROR, l as devClientDeepLink } from "./app-install-CrJ6t_bv.mjs";
|
|
14
|
+
import { c as readMetroRecords, o as isBundleProof } from "./launch-verify-CQS_Kg-C.mjs";
|
|
15
|
+
import { t as compareStimVersions } from "./stim-installations-CINXy_su.mjs";
|
|
16
|
+
import { createRequire } from "node:module";
|
|
11
17
|
import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
18
|
+
import { easMachineStateRoot, easMachineStateRoot as easMachineStateRoot$1, easSessionLedgerFile, easSessionLedgerLock, readEasSessionLedger, readEasSessionLedger as readEasSessionLedger$1, readJsonObject, validEasSessionClaim } from "@stim-cli/core/state";
|
|
19
|
+
import { dirname, join, resolve } from "node:path";
|
|
20
|
+
import { pathToFileURL } from "node:url";
|
|
21
|
+
import { format } from "node:util";
|
|
22
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
23
|
+
//#region src/engine/remote-cache.ts
|
|
24
|
+
const EAS_PROVIDER_PACKAGE = "eas-build-cache-provider";
|
|
25
|
+
const RESOLVE_TIMEOUT_MS = 3e4;
|
|
26
|
+
const UPLOAD_TIMEOUT_MS = 6e4;
|
|
27
|
+
const CONFIG_TIMEOUT_MS = 3e4;
|
|
28
|
+
const DYNAMIC_CONFIG_FILES = [
|
|
29
|
+
"app.config.ts",
|
|
30
|
+
"app.config.js",
|
|
31
|
+
"app.config.mjs",
|
|
32
|
+
"app.config.cjs"
|
|
33
|
+
];
|
|
34
|
+
const TIMED_OUT = Symbol("timed-out");
|
|
35
|
+
function dynamicConfigFile(root) {
|
|
36
|
+
for (const name of DYNAMIC_CONFIG_FILES) if (existsSync(join(root, name))) return name;
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
function providerFromConfig(config) {
|
|
40
|
+
if (!config || typeof config !== "object") return null;
|
|
41
|
+
const expoField = config.expo;
|
|
42
|
+
const exp = expoField && typeof expoField === "object" ? expoField : config;
|
|
43
|
+
return exp.buildCacheProvider ?? exp.experiments?.buildCacheProvider ?? null;
|
|
44
|
+
}
|
|
45
|
+
function ownerFromConfig(config) {
|
|
46
|
+
if (!config || typeof config !== "object") return null;
|
|
47
|
+
const expoField = config.expo;
|
|
48
|
+
const owner = (expoField && typeof expoField === "object" ? expoField : config).owner;
|
|
49
|
+
return typeof owner === "string" && owner.trim() !== "" ? owner.trim() : null;
|
|
50
|
+
}
|
|
51
|
+
function normalizeProvider(raw) {
|
|
52
|
+
if (raw === null || raw === void 0 || raw === "") return null;
|
|
53
|
+
if (raw === "eas") return {
|
|
54
|
+
name: "eas",
|
|
55
|
+
reference: EAS_PROVIDER_PACKAGE,
|
|
56
|
+
options: {}
|
|
57
|
+
};
|
|
58
|
+
if (typeof raw === "object") {
|
|
59
|
+
const obj = raw;
|
|
60
|
+
if (typeof obj.plugin === "string" && obj.plugin.trim() !== "") {
|
|
61
|
+
const reference = obj.plugin.trim();
|
|
62
|
+
return {
|
|
63
|
+
name: reference,
|
|
64
|
+
reference,
|
|
65
|
+
options: obj.options && typeof obj.options === "object" ? obj.options : {}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { invalid: typeof raw === "string" ? `"${raw}"` : JSON.stringify(raw) };
|
|
70
|
+
}
|
|
71
|
+
function runOptionsFor(platform) {
|
|
72
|
+
return platform === "android" ? { variant: "debug" } : { configuration: "Debug" };
|
|
73
|
+
}
|
|
74
|
+
function isProviderPlugin(plugin) {
|
|
75
|
+
if (!plugin || typeof plugin !== "object") return false;
|
|
76
|
+
const p = plugin;
|
|
77
|
+
const resolves = typeof p.resolveBuildCache === "function" || typeof p.resolveRemoteBuildCache === "function";
|
|
78
|
+
const uploads = typeof p.uploadBuildCache === "function" || typeof p.uploadRemoteBuildCache === "function";
|
|
79
|
+
return resolves && uploads;
|
|
80
|
+
}
|
|
81
|
+
function cacheLevel(value) {
|
|
82
|
+
return value === "local" || value === "remote" ? value : false;
|
|
83
|
+
}
|
|
84
|
+
function exitAfterFlush(code = 0, { exit = (c) => process.exit(c), stream = process.stdout } = {}) {
|
|
85
|
+
try {
|
|
86
|
+
stream.write("", () => exit(code));
|
|
87
|
+
} catch {
|
|
88
|
+
exit(code);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function readProjectConfig(root, { run = null, timeoutMs = CONFIG_TIMEOUT_MS } = {}) {
|
|
92
|
+
const dynamic = dynamicConfigFile(root);
|
|
93
|
+
if (!dynamic) {
|
|
94
|
+
const file = join(root, "app.json");
|
|
95
|
+
if (!existsSync(file)) return {
|
|
96
|
+
config: null,
|
|
97
|
+
source: null
|
|
98
|
+
};
|
|
99
|
+
try {
|
|
100
|
+
return {
|
|
101
|
+
config: JSON.parse(readFileSync(file, "utf-8")),
|
|
102
|
+
source: "app.json"
|
|
103
|
+
};
|
|
104
|
+
} catch (err) {
|
|
105
|
+
return { unavailable: `app.json could not be parsed: ${firstLine(err)}` };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const bin = expoBinPath(root);
|
|
109
|
+
if (!bin) return { unavailable: `${dynamic} is code, so it has to be evaluated to read its buildCacheProvider, and the \`expo\` package is not resolvable from this project (no expo/package.json to read a bin from, and no node_modules/.bin/expo here or in any parent)` };
|
|
110
|
+
const exec = run || ((file, args, opts) => getExecutor().runFile(file, args, opts));
|
|
111
|
+
let stdout;
|
|
112
|
+
try {
|
|
113
|
+
stdout = exec(bin, [
|
|
114
|
+
"config",
|
|
115
|
+
"--json",
|
|
116
|
+
root
|
|
117
|
+
], { timeoutMs });
|
|
118
|
+
} catch (err) {
|
|
119
|
+
return { unavailable: `\`expo config --json\` failed: ${firstLine(err)}` };
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
return {
|
|
123
|
+
config: JSON.parse(String(stdout)),
|
|
124
|
+
source: dynamic
|
|
125
|
+
};
|
|
126
|
+
} catch (err) {
|
|
127
|
+
return { unavailable: `\`expo config --json\` did not print JSON: ${firstLine(err)}` };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function loadPlugin(projectRoot, reference, { requireFrom = null } = {}) {
|
|
131
|
+
const localRequire = requireFrom ? requireFrom(projectRoot) : createRequire(join(projectRoot, "package.json"));
|
|
132
|
+
const file = localRequire.resolve(reference);
|
|
133
|
+
let mod;
|
|
134
|
+
try {
|
|
135
|
+
mod = localRequire(file);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
if (err?.code !== "ERR_REQUIRE_ESM") throw err;
|
|
138
|
+
mod = await import(pathToFileURL(file).href);
|
|
139
|
+
}
|
|
140
|
+
const plugin = mod?.default ?? mod;
|
|
141
|
+
if (!isProviderPlugin(plugin)) throw new Error(`"${reference}" does not export resolveBuildCache and uploadBuildCache functions`);
|
|
142
|
+
return plugin;
|
|
143
|
+
}
|
|
144
|
+
async function loadProjectProvider(projectRoot, { isExpo = true, run = null, requireFrom = null, timeoutMs = CONFIG_TIMEOUT_MS } = {}) {
|
|
145
|
+
if (!isExpo) return { none: true };
|
|
146
|
+
const read = readProjectConfig(projectRoot, {
|
|
147
|
+
run,
|
|
148
|
+
timeoutMs
|
|
149
|
+
});
|
|
150
|
+
if (read.unavailable) return { unavailable: read.unavailable };
|
|
151
|
+
const normalized = normalizeProvider(providerFromConfig(read.config));
|
|
152
|
+
if (!normalized) return { none: true };
|
|
153
|
+
if (normalized.invalid) return { unavailable: `buildCacheProvider is ${normalized.invalid}, which is not "eas" or { plugin: <module> }` };
|
|
154
|
+
if (normalized.reference === "@stim-cli/expo-build-cache") return { none: true };
|
|
155
|
+
let plugin;
|
|
156
|
+
try {
|
|
157
|
+
plugin = await loadPlugin(projectRoot, normalized.reference, { requireFrom });
|
|
158
|
+
} catch (err) {
|
|
159
|
+
const missing = err?.code === "MODULE_NOT_FOUND";
|
|
160
|
+
return { unavailable: normalized.name === "eas" && missing ? `the EAS build cache needs the \`${EAS_PROVIDER_PACKAGE}\` package, which is not installed in this project` : `${normalized.name} could not be loaded: ${firstLine(err)}` };
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
provider: {
|
|
164
|
+
plugin,
|
|
165
|
+
options: normalized.options ?? {}
|
|
166
|
+
},
|
|
167
|
+
name: normalized.name,
|
|
168
|
+
owner: ownerFromConfig(read.config)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const WHOAMI_TIMEOUT_MS = 15e3;
|
|
172
|
+
const EAS_CLI_PACKAGE = "eas-cli";
|
|
173
|
+
const EAS_CLI_BIN = "eas";
|
|
174
|
+
function parseWhoami({ stdout = "", stderr = "", exitCode = 0 } = {}) {
|
|
175
|
+
const out = stripAnsi(stdout);
|
|
176
|
+
const combined = `${out}\n${stripAnsi(stderr)}`;
|
|
177
|
+
if (/^\s*Not logged in\s*$/m.test(combined)) return { loggedOut: true };
|
|
178
|
+
if (exitCode !== 0) return { unknown: firstLine(combined.trim() || `eas whoami exited ${exitCode}`) };
|
|
179
|
+
const lines = out.split("\n").map((line) => line.trim()).filter((line) => line !== "");
|
|
180
|
+
if (lines.length === 0) return { unknown: "eas whoami printed nothing" };
|
|
181
|
+
const raw = lines[0];
|
|
182
|
+
if (raw === void 0) return { unknown: "eas whoami printed nothing" };
|
|
183
|
+
const viaToken = /\(authenticated using EXPO_TOKEN\)\s*$/.test(raw);
|
|
184
|
+
const display = raw.replace(/\s*\(authenticated using EXPO_TOKEN\)\s*$/, "").trim();
|
|
185
|
+
const accounts = [];
|
|
186
|
+
for (const line of lines) {
|
|
187
|
+
const name = /^\u2022\s*(\S+)\s*\(Role:/.exec(line)?.[1];
|
|
188
|
+
if (name !== void 0) accounts.push(name);
|
|
189
|
+
}
|
|
190
|
+
if (accounts.length === 0 && !display.endsWith("(robot)") && display !== "robot") accounts.push(display);
|
|
191
|
+
return {
|
|
192
|
+
loggedIn: true,
|
|
193
|
+
account: display,
|
|
194
|
+
accounts: accounts.length ? accounts : null,
|
|
195
|
+
viaToken
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function isEasAuthFailureText(text) {
|
|
199
|
+
const t = stripAnsi(text || "");
|
|
200
|
+
if (t.trim() === "") return false;
|
|
201
|
+
return /not logged in/i.test(t) || /\beas login\b/i.test(t) || /EXPO_TOKEN/.test(t) || /unauthorized/i.test(t) || /not authorized/i.test(t) || /authentication (failed|required)/i.test(t);
|
|
202
|
+
}
|
|
203
|
+
function easAuthNote(status) {
|
|
204
|
+
const { code, account, owner, reason, phase = "resolve" } = status || {};
|
|
205
|
+
const because = reason ? ` (${reason})` : "";
|
|
206
|
+
if (code === "logged-out") return `eas is not authenticated${because} -- run \`eas login\` (or set EXPO_TOKEN); ` + (phase === "upload" ? "this build stayed in the local cache" : "building with the local cache only");
|
|
207
|
+
if (code === "wrong-account") return `eas is authenticated as ${account}, and this project's owner is ${owner} -- run \`eas login\` as a member of ${owner} (or set EXPO_TOKEN); consulting the EAS cache anyway`;
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
function resolveEasCliBin(projectRoot, { lookupPath = null } = {}) {
|
|
211
|
+
const fromPackage = expoBinFromPackage(resolvePackageJson(projectRoot, EAS_CLI_PACKAGE), EAS_CLI_BIN);
|
|
212
|
+
if (fromPackage) return {
|
|
213
|
+
file: fromPackage,
|
|
214
|
+
source: "project"
|
|
215
|
+
};
|
|
216
|
+
const shim = findBinUpward(projectRoot, EAS_CLI_BIN);
|
|
217
|
+
if (shim) return {
|
|
218
|
+
file: shim,
|
|
219
|
+
source: "project"
|
|
220
|
+
};
|
|
221
|
+
const onPath = lookupPath ? lookupPath() : getExecutor().findExecutable(EAS_CLI_BIN);
|
|
222
|
+
const file = (String(onPath || "").split("\n")[0] ?? "").trim();
|
|
223
|
+
return file ? {
|
|
224
|
+
file,
|
|
225
|
+
source: "path"
|
|
226
|
+
} : null;
|
|
227
|
+
}
|
|
228
|
+
const easAuthCache = /* @__PURE__ */ new Map();
|
|
229
|
+
function checkEasAuth({ projectRoot, owner = null, run = null, resolveBin = null, timeoutMs = WHOAMI_TIMEOUT_MS, cache = easAuthCache }) {
|
|
230
|
+
const key = `${projectRoot}::${owner || ""}`;
|
|
231
|
+
if (cache?.has(key)) return cache.get(key);
|
|
232
|
+
const answer = probeEasAuth({
|
|
233
|
+
projectRoot,
|
|
234
|
+
owner,
|
|
235
|
+
run,
|
|
236
|
+
resolveBin,
|
|
237
|
+
timeoutMs
|
|
238
|
+
});
|
|
239
|
+
cache?.set(key, answer);
|
|
240
|
+
return answer;
|
|
241
|
+
}
|
|
242
|
+
function probeEasAuth({ projectRoot, owner, run, resolveBin, timeoutMs }) {
|
|
243
|
+
const bin = (resolveBin || resolveEasCliBin)(projectRoot);
|
|
244
|
+
if (!bin) return {
|
|
245
|
+
failed: true,
|
|
246
|
+
code: "no-cli",
|
|
247
|
+
reason: "no `eas` executable is resolvable from this project or on PATH",
|
|
248
|
+
remedy: "Install eas-cli (`npm i -g eas-cli`, or as a project devDependency), then run `eas login`."
|
|
249
|
+
};
|
|
250
|
+
const exec = run || ((file, args, opts) => getExecutor().runFile(file, args, opts));
|
|
251
|
+
let result;
|
|
252
|
+
try {
|
|
253
|
+
result = {
|
|
254
|
+
stdout: String(exec(bin.file, ["whoami"], { timeoutMs }) ?? ""),
|
|
255
|
+
exitCode: 0
|
|
256
|
+
};
|
|
257
|
+
} catch (e) {
|
|
258
|
+
const err = e;
|
|
259
|
+
if (err?.code === "ETIMEDOUT" || err?.signal === "SIGTERM" || err?.killed) return { unknown: `eas whoami timed out after ${timeoutMs}ms` };
|
|
260
|
+
result = {
|
|
261
|
+
stdout: String(err?.stdout ?? ""),
|
|
262
|
+
stderr: String(err?.stderr ?? err?.message ?? ""),
|
|
263
|
+
exitCode: typeof err?.status === "number" ? err.status : 1
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const parsed = parseWhoami(result);
|
|
267
|
+
if (parsed.loggedOut) return {
|
|
268
|
+
failed: true,
|
|
269
|
+
code: "logged-out",
|
|
270
|
+
reason: "eas whoami says \"Not logged in\"",
|
|
271
|
+
remedy: "Run `eas login` (or set EXPO_TOKEN)."
|
|
272
|
+
};
|
|
273
|
+
if (parsed.unknown) return { unknown: parsed.unknown };
|
|
274
|
+
if (owner && Array.isArray(parsed.accounts) && !parsed.accounts.includes(owner)) return {
|
|
275
|
+
failed: true,
|
|
276
|
+
code: "wrong-account",
|
|
277
|
+
account: parsed.account,
|
|
278
|
+
accounts: parsed.accounts,
|
|
279
|
+
owner,
|
|
280
|
+
reason: `eas is authenticated as ${parsed.account} (accounts: ${parsed.accounts.join(", ")}), which does not include this project's owner ${owner}`,
|
|
281
|
+
remedy: `Run \`eas login\` as a member of ${owner}, or set EXPO_TOKEN to a token for it.`
|
|
282
|
+
};
|
|
283
|
+
return {
|
|
284
|
+
ok: true,
|
|
285
|
+
account: parsed.account,
|
|
286
|
+
accounts: parsed.accounts,
|
|
287
|
+
viaToken: parsed.viaToken,
|
|
288
|
+
source: bin.source
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
const capturing = [];
|
|
292
|
+
const abandoned = /* @__PURE__ */ new Set();
|
|
293
|
+
let patched = null;
|
|
294
|
+
const PROVIDER_CONTEXT = new AsyncLocalStorage();
|
|
295
|
+
function uploadDestination(lines) {
|
|
296
|
+
const text = (lines || []).map(stripAnsi);
|
|
297
|
+
for (const line of text) {
|
|
298
|
+
const url = /(https?:\/\/[^\s'"]+)/.exec(line)?.[1];
|
|
299
|
+
if (url !== void 0) return url.replace(/[.,)\]]+$/, "");
|
|
300
|
+
}
|
|
301
|
+
for (const line of text) {
|
|
302
|
+
const slug = /\bto\s+(@?[\w.-]+\/[\w.-]+)/i.exec(line)?.[1];
|
|
303
|
+
if (slug !== void 0) return slug.replace(/[.,)\]]+$/, "");
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
function providerNote(text) {
|
|
308
|
+
return `${"cache".padEnd(11)} ${text}`;
|
|
309
|
+
}
|
|
310
|
+
function currentFrame() {
|
|
311
|
+
const store = PROVIDER_CONTEXT.getStore();
|
|
312
|
+
return store && (capturing.includes(store) || abandoned.has(store)) ? store : null;
|
|
313
|
+
}
|
|
314
|
+
function install() {
|
|
315
|
+
if (patched && process.stdout.write === patched.patchedWrite) return;
|
|
316
|
+
const stdout = process.stdout;
|
|
317
|
+
const write = stdout.write.bind(stdout);
|
|
318
|
+
const log = console.log;
|
|
319
|
+
const passthrough = write;
|
|
320
|
+
const patchedWrite = ((chunk, encoding, callback) => {
|
|
321
|
+
const frame = currentFrame();
|
|
322
|
+
if (!frame) return passthrough(chunk, encoding, callback);
|
|
323
|
+
const cb = typeof encoding === "function" ? encoding : callback;
|
|
324
|
+
absorb(frame, typeof chunk === "string" ? chunk : String(chunk));
|
|
325
|
+
if (typeof cb === "function") cb();
|
|
326
|
+
return true;
|
|
327
|
+
});
|
|
328
|
+
const patchedLog = (...args) => {
|
|
329
|
+
const frame = currentFrame();
|
|
330
|
+
if (!frame) return log(...args);
|
|
331
|
+
absorb(frame, `${format(...args)}\n`);
|
|
332
|
+
};
|
|
333
|
+
stdout.write = patchedWrite;
|
|
334
|
+
console.log = patchedLog;
|
|
335
|
+
patched = {
|
|
336
|
+
stdout,
|
|
337
|
+
write,
|
|
338
|
+
log,
|
|
339
|
+
patchedWrite,
|
|
340
|
+
patchedLog
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function uninstall() {
|
|
344
|
+
if (!patched || capturing.length || abandoned.size) return;
|
|
345
|
+
if (patched.stdout.write === patched.patchedWrite) patched.stdout.write = patched.write;
|
|
346
|
+
if (console.log === patched.patchedLog) console.log = patched.log;
|
|
347
|
+
patched = null;
|
|
348
|
+
}
|
|
349
|
+
function absorb(frame, chunk) {
|
|
350
|
+
try {
|
|
351
|
+
process.stderr.write(chunk);
|
|
352
|
+
} catch {}
|
|
353
|
+
frame.pending += chunk;
|
|
354
|
+
let index = frame.pending.indexOf("\n");
|
|
355
|
+
while (index !== -1) {
|
|
356
|
+
emitLine(frame, frame.pending.slice(0, index));
|
|
357
|
+
frame.pending = frame.pending.slice(index + 1);
|
|
358
|
+
index = frame.pending.indexOf("\n");
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function emitLine(frame, raw) {
|
|
362
|
+
const line = stripAnsi(raw).trim();
|
|
363
|
+
if (line === "") return;
|
|
364
|
+
frame.lines.push(line);
|
|
365
|
+
const writer = frame.writer();
|
|
366
|
+
try {
|
|
367
|
+
writer?.write({
|
|
368
|
+
src: "build",
|
|
369
|
+
level: "debug",
|
|
370
|
+
event: "provider",
|
|
371
|
+
msg: line
|
|
372
|
+
});
|
|
373
|
+
} catch {}
|
|
374
|
+
frame.onLine?.(line);
|
|
375
|
+
}
|
|
376
|
+
function beginCapture({ logWriter = null, projectRoot = null, platform = null, onLine = null } = {}) {
|
|
377
|
+
let own = null;
|
|
378
|
+
const frame = {
|
|
379
|
+
lines: [],
|
|
380
|
+
pending: "",
|
|
381
|
+
onLine,
|
|
382
|
+
writer: () => {
|
|
383
|
+
if (logWriter) return logWriter;
|
|
384
|
+
if (own) return own;
|
|
385
|
+
if (!projectRoot || !platform) return null;
|
|
386
|
+
own = createNdjsonWriter(join(workspaceLogsDir(projectRoot), `build-${platform}.ndjson`));
|
|
387
|
+
return own;
|
|
388
|
+
},
|
|
389
|
+
close: () => {
|
|
390
|
+
own?.close?.();
|
|
391
|
+
own = null;
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
capturing.push(frame);
|
|
395
|
+
install();
|
|
396
|
+
return frame;
|
|
397
|
+
}
|
|
398
|
+
function endCapture(frame) {
|
|
399
|
+
if (!frame) return;
|
|
400
|
+
const at = capturing.lastIndexOf(frame);
|
|
401
|
+
if (at !== -1) capturing.splice(at, 1);
|
|
402
|
+
abandoned.delete(frame);
|
|
403
|
+
if (frame.pending !== "") {
|
|
404
|
+
const rest = frame.pending;
|
|
405
|
+
frame.pending = "";
|
|
406
|
+
emitLine(frame, rest);
|
|
407
|
+
}
|
|
408
|
+
frame.close();
|
|
409
|
+
uninstall();
|
|
410
|
+
}
|
|
411
|
+
function abandonCapture(frame, work) {
|
|
412
|
+
if (!frame) return;
|
|
413
|
+
const at = capturing.lastIndexOf(frame);
|
|
414
|
+
if (at !== -1) capturing.splice(at, 1);
|
|
415
|
+
abandoned.add(frame);
|
|
416
|
+
Promise.resolve(work).then(() => endCapture(frame), () => endCapture(frame));
|
|
417
|
+
}
|
|
418
|
+
async function resolveRemote({ provider, platform, projectRoot, fingerprintHash, runOptions = null, timeoutMs = RESOLVE_TIMEOUT_MS, logWriter = null } = {}) {
|
|
419
|
+
if (!provider?.plugin || !fingerprintHash) return null;
|
|
420
|
+
const opts = runOptions || runOptionsFor(platform);
|
|
421
|
+
const outcome = await withBudget(async () => {
|
|
422
|
+
const props = {
|
|
423
|
+
fingerprintHash: await providerFingerprint({
|
|
424
|
+
provider,
|
|
425
|
+
platform,
|
|
426
|
+
projectRoot,
|
|
427
|
+
runOptions: opts
|
|
428
|
+
}) ?? fingerprintHash,
|
|
429
|
+
platform,
|
|
430
|
+
runOptions: opts,
|
|
431
|
+
projectRoot
|
|
432
|
+
};
|
|
433
|
+
return typeof provider.plugin.resolveBuildCache === "function" ? provider.plugin.resolveBuildCache(props, provider.options) : provider.plugin.resolveRemoteBuildCache(props, provider.options);
|
|
434
|
+
}, timeoutMs, {
|
|
435
|
+
logWriter,
|
|
436
|
+
projectRoot,
|
|
437
|
+
platform
|
|
438
|
+
});
|
|
439
|
+
if (outcome.timedOut) return { timedOut: true };
|
|
440
|
+
if (outcome.error) return { failed: firstLine(outcome.error) };
|
|
441
|
+
const appPath = typeof outcome.value === "string" ? outcome.value.trim() : "";
|
|
442
|
+
if (!appPath) return null;
|
|
443
|
+
if (!existsSync(appPath)) return { failed: `returned ${appPath}, which does not exist` };
|
|
444
|
+
return { appPath };
|
|
445
|
+
}
|
|
446
|
+
async function uploadRemote({ provider, platform, projectRoot, fingerprintHash, buildPath, runOptions = null, timeoutMs = UPLOAD_TIMEOUT_MS, logWriter = null, note = (line) => console.error(line) } = {}) {
|
|
447
|
+
if (!provider?.plugin || !fingerprintHash || !buildPath) return { skipped: true };
|
|
448
|
+
const opts = runOptions || runOptionsFor(platform);
|
|
449
|
+
let announced = false;
|
|
450
|
+
const onLine = (line) => {
|
|
451
|
+
if (announced) return;
|
|
452
|
+
const dest = uploadDestination([line]);
|
|
453
|
+
if (!dest) return;
|
|
454
|
+
announced = true;
|
|
455
|
+
note(providerNote(`uploading to ${dest}`));
|
|
456
|
+
};
|
|
457
|
+
const outcome = await withBudget(async () => {
|
|
458
|
+
const props = {
|
|
459
|
+
projectRoot,
|
|
460
|
+
platform,
|
|
461
|
+
fingerprintHash: await providerFingerprint({
|
|
462
|
+
provider,
|
|
463
|
+
platform,
|
|
464
|
+
projectRoot,
|
|
465
|
+
runOptions: opts
|
|
466
|
+
}) ?? fingerprintHash,
|
|
467
|
+
buildPath,
|
|
468
|
+
runOptions: opts
|
|
469
|
+
};
|
|
470
|
+
return typeof provider.plugin.uploadBuildCache === "function" ? provider.plugin.uploadBuildCache(props, provider.options) : provider.plugin.uploadRemoteBuildCache(props, provider.options);
|
|
471
|
+
}, timeoutMs, {
|
|
472
|
+
logWriter,
|
|
473
|
+
projectRoot,
|
|
474
|
+
platform,
|
|
475
|
+
onLine
|
|
476
|
+
});
|
|
477
|
+
if (outcome.timedOut) return { timedOut: true };
|
|
478
|
+
if (outcome.error) return { failed: firstLine(outcome.error) };
|
|
479
|
+
if (typeof outcome.value !== "string" || outcome.value.trim() === "") {
|
|
480
|
+
const authLine = outcome.lines.find((line) => isEasAuthFailureText(line));
|
|
481
|
+
return { failed: authLine ? firstLine(authLine) : "the provider returned no upload destination" };
|
|
482
|
+
}
|
|
483
|
+
const destination = uploadDestination(outcome.lines);
|
|
484
|
+
return destination ? {
|
|
485
|
+
uploaded: true,
|
|
486
|
+
destination
|
|
487
|
+
} : { uploaded: true };
|
|
488
|
+
}
|
|
489
|
+
async function providerFingerprint({ provider, platform, projectRoot, runOptions }) {
|
|
490
|
+
if (typeof provider.plugin.calculateFingerprintHash !== "function") return null;
|
|
491
|
+
try {
|
|
492
|
+
const hash = await provider.plugin.calculateFingerprintHash({
|
|
493
|
+
projectRoot,
|
|
494
|
+
platform,
|
|
495
|
+
runOptions
|
|
496
|
+
}, provider.options);
|
|
497
|
+
return typeof hash === "string" && hash.trim() !== "" ? hash.trim() : null;
|
|
498
|
+
} catch {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
async function withBudget(factory, ms, capture) {
|
|
503
|
+
let timer = null;
|
|
504
|
+
const timeout = new Promise((resolve) => {
|
|
505
|
+
timer = setTimeout(() => resolve(TIMED_OUT), ms);
|
|
506
|
+
timer?.unref?.();
|
|
507
|
+
});
|
|
508
|
+
const frame = beginCapture(capture || {});
|
|
509
|
+
const work = Promise.resolve().then(() => PROVIDER_CONTEXT.run(frame, factory)).then((value) => ({ value }), (error) => ({ error }));
|
|
510
|
+
try {
|
|
511
|
+
const settled = await Promise.race([work, timeout]);
|
|
512
|
+
if (settled === TIMED_OUT) {
|
|
513
|
+
abandonCapture(frame, work);
|
|
514
|
+
return {
|
|
515
|
+
timedOut: true,
|
|
516
|
+
lines: frame.lines
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
endCapture(frame);
|
|
520
|
+
return {
|
|
521
|
+
...settled,
|
|
522
|
+
lines: frame.lines
|
|
523
|
+
};
|
|
524
|
+
} catch (err) {
|
|
525
|
+
endCapture(frame);
|
|
526
|
+
throw err;
|
|
527
|
+
} finally {
|
|
528
|
+
if (timer) clearTimeout(timer);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function firstLine(err) {
|
|
532
|
+
const line = (String(err?.message || err || "unknown error").trim().split("\n")[0] ?? "").trim();
|
|
533
|
+
return line.length > 200 ? `${line.slice(0, 197)}...` : line;
|
|
534
|
+
}
|
|
535
|
+
//#endregion
|
|
14
536
|
//#region src/engine/metro-gate.ts
|
|
15
537
|
const REMOTE_METRO_WRONG = "STIM_REMOTE_METRO_WRONG";
|
|
16
538
|
const GATE_TIMEOUT_MS = 25e3;
|
|
@@ -81,7 +603,11 @@ function remoteProfile({ daemon, platform, label }) {
|
|
|
81
603
|
session: scope,
|
|
82
604
|
tenant: scope,
|
|
83
605
|
runId: scope,
|
|
84
|
-
sessionIsolation: "tenant"
|
|
606
|
+
sessionIsolation: "tenant",
|
|
607
|
+
...platform === "android" ? {
|
|
608
|
+
leaseProvider: "proxy",
|
|
609
|
+
clientId: scope
|
|
610
|
+
} : {}
|
|
85
611
|
};
|
|
86
612
|
}
|
|
87
613
|
function daemonEnv(daemon) {
|
|
@@ -144,6 +670,18 @@ const LIVE_STATUSES$1 = ["new", "in-progress"];
|
|
|
144
670
|
const LIVE_SESSION_STATUSES = /* @__PURE__ */ new Set(["NEW", "IN_PROGRESS"]);
|
|
145
671
|
const TERMINAL_SESSION_STATUSES = /* @__PURE__ */ new Set(["STOPPED", "ERRORED"]);
|
|
146
672
|
const SESSION_PLATFORMS = /* @__PURE__ */ new Set(["ios", "android"]);
|
|
673
|
+
const MIN_EAS_CLI_SIMULATOR_VERSION = "21.6.0";
|
|
674
|
+
function easCliUpgradeRemedy(minimum) {
|
|
675
|
+
return `Upgrade eas-cli to ${minimum} or later (\`npm install --global eas-cli@latest\`, or the project's eas-cli dependency)`;
|
|
676
|
+
}
|
|
677
|
+
function easCliSupport(versionOutput, minimum) {
|
|
678
|
+
const version = /\beas-cli\/(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/.exec(versionOutput ?? "")?.[1] ?? null;
|
|
679
|
+
const order = version ? compareStimVersions(version, minimum) : null;
|
|
680
|
+
return {
|
|
681
|
+
supported: order !== null && order >= 0,
|
|
682
|
+
version
|
|
683
|
+
};
|
|
684
|
+
}
|
|
147
685
|
function ownedSessionName(label) {
|
|
148
686
|
const clean = sanitizeDeviceLabel(label);
|
|
149
687
|
return `${OWNED_PREFIX}${clean.startsWith(OWNED_PREFIX) ? clean.slice(5) : clean}`;
|
|
@@ -413,80 +951,14 @@ function parseStoppedSession(stdout) {
|
|
|
413
951
|
};
|
|
414
952
|
}
|
|
415
953
|
//#endregion
|
|
954
|
+
//#region src/engine/eas-machine-root-guard-env.ts
|
|
955
|
+
const EAS_TEST_GUARD_ROOT_ENV = "STIM_TEST_GUARD_REAL_EAS_ROOT";
|
|
956
|
+
//#endregion
|
|
416
957
|
//#region src/engine/eas-session-ledger.ts
|
|
417
|
-
function
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
return join(root, "sessions.json");
|
|
422
|
-
}
|
|
423
|
-
function validClaim(id, value) {
|
|
424
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
425
|
-
const claim = value;
|
|
426
|
-
if (claim.sessionId !== id) return null;
|
|
427
|
-
if (typeof claim.name !== "string" || !claim.name.startsWith("stim-")) return null;
|
|
428
|
-
if (claim.platform !== "ios" && claim.platform !== "android") return null;
|
|
429
|
-
if (typeof claim.workspaceRoot !== "string" || !isAbsolute(claim.workspaceRoot)) return null;
|
|
430
|
-
if (typeof claim.workspaceHome !== "string" || !isAbsolute(claim.workspaceHome)) return null;
|
|
431
|
-
if (typeof claim.stateFile !== "string" || !isAbsolute(claim.stateFile)) return null;
|
|
432
|
-
const workspaceRoot = resolve(claim.workspaceRoot);
|
|
433
|
-
const workspaceHome = resolve(claim.workspaceHome);
|
|
434
|
-
const stateFile = resolve(claim.stateFile);
|
|
435
|
-
const stateRelative = relative(workspaceHome, stateFile);
|
|
436
|
-
if (!stateRelative || stateRelative.startsWith("..") || isAbsolute(stateRelative)) return null;
|
|
437
|
-
if (stateFile !== join(workspaceHome, "workspaces", workspaceName(workspaceRoot), "state.json")) return null;
|
|
438
|
-
return {
|
|
439
|
-
sessionId: id,
|
|
440
|
-
name: claim.name,
|
|
441
|
-
platform: claim.platform,
|
|
442
|
-
workspaceRoot,
|
|
443
|
-
workspaceHome,
|
|
444
|
-
stateFile
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
function readEasSessionLedger(root = easMachineStateRoot()) {
|
|
448
|
-
const file = easSessionLedgerFile(root);
|
|
449
|
-
if (!existsSync(file)) return {
|
|
450
|
-
claims: /* @__PURE__ */ new Map(),
|
|
451
|
-
notice: null,
|
|
452
|
-
safe: true
|
|
453
|
-
};
|
|
454
|
-
let parsed;
|
|
455
|
-
try {
|
|
456
|
-
parsed = JSON.parse(readFileSync(file, "utf-8"));
|
|
457
|
-
} catch (error) {
|
|
458
|
-
return {
|
|
459
|
-
claims: /* @__PURE__ */ new Map(),
|
|
460
|
-
notice: `EAS ownership ledger ${file} is unreadable: ${error.message}`,
|
|
461
|
-
safe: false
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {
|
|
465
|
-
claims: /* @__PURE__ */ new Map(),
|
|
466
|
-
notice: `EAS ownership ledger ${file} is malformed.`,
|
|
467
|
-
safe: false
|
|
468
|
-
};
|
|
469
|
-
const ledger = parsed;
|
|
470
|
-
if (ledger.version !== 1 || !ledger.claims || typeof ledger.claims !== "object" || Array.isArray(ledger.claims)) return {
|
|
471
|
-
claims: /* @__PURE__ */ new Map(),
|
|
472
|
-
notice: `EAS ownership ledger ${file} is malformed.`,
|
|
473
|
-
safe: false
|
|
474
|
-
};
|
|
475
|
-
const claims = /* @__PURE__ */ new Map();
|
|
476
|
-
for (const [id, value] of Object.entries(ledger.claims)) {
|
|
477
|
-
const claim = validClaim(id, value);
|
|
478
|
-
if (!claim) return {
|
|
479
|
-
claims: /* @__PURE__ */ new Map(),
|
|
480
|
-
notice: `EAS ownership ledger ${file} is malformed.`,
|
|
481
|
-
safe: false
|
|
482
|
-
};
|
|
483
|
-
claims.set(id, claim);
|
|
484
|
-
}
|
|
485
|
-
return {
|
|
486
|
-
claims,
|
|
487
|
-
notice: null,
|
|
488
|
-
safe: true
|
|
489
|
-
};
|
|
958
|
+
function assertEasMachineRootWritable(root) {
|
|
959
|
+
const guardedRoot = process.env[EAS_TEST_GUARD_ROOT_ENV];
|
|
960
|
+
if (!guardedRoot || resolve(root) !== resolve(guardedRoot)) return;
|
|
961
|
+
throw new Error(`Refusing to write the real EAS machine root ${root} from a test process. Pass a temporary ledgerRoot, machineRoot, or easLedgerRoot, or point HOME (and USERPROFILE on Windows) at a temporary directory.`);
|
|
490
962
|
}
|
|
491
963
|
function writeLedger(root, ledger) {
|
|
492
964
|
mkdirSync(root, { recursive: true });
|
|
@@ -501,9 +973,10 @@ function writeLedger(root, ledger) {
|
|
|
501
973
|
}
|
|
502
974
|
}
|
|
503
975
|
function recordEasSessionClaim(claim, root = easMachineStateRoot()) {
|
|
504
|
-
|
|
976
|
+
assertEasMachineRootWritable(root);
|
|
977
|
+
const normalized = validEasSessionClaim(claim.sessionId, claim);
|
|
505
978
|
if (!normalized) throw new Error(`Invalid EAS session claim for ${claim.sessionId}.`);
|
|
506
|
-
withDirLock(
|
|
979
|
+
withDirLock(easSessionLedgerLock(root), () => {
|
|
507
980
|
const current = readEasSessionLedger(root);
|
|
508
981
|
if (!current.safe) throw new Error(current.notice ?? "EAS ownership ledger is unreadable.");
|
|
509
982
|
current.claims.set(normalized.sessionId, normalized);
|
|
@@ -511,12 +984,13 @@ function recordEasSessionClaim(claim, root = easMachineStateRoot()) {
|
|
|
511
984
|
version: 1,
|
|
512
985
|
claims: Object.fromEntries(current.claims)
|
|
513
986
|
});
|
|
514
|
-
}, { ensureParent: () => mkdirSync(dirname(
|
|
987
|
+
}, { ensureParent: () => mkdirSync(dirname(easSessionLedgerLock(root)), { recursive: true }) });
|
|
515
988
|
}
|
|
516
989
|
function removeEasSessionClaim(sessionId, root = easMachineStateRoot()) {
|
|
517
990
|
const file = easSessionLedgerFile(root);
|
|
518
991
|
if (!existsSync(file)) return false;
|
|
519
|
-
|
|
992
|
+
assertEasMachineRootWritable(root);
|
|
993
|
+
return withDirLock(easSessionLedgerLock(root), () => {
|
|
520
994
|
const current = readEasSessionLedger(root);
|
|
521
995
|
if (!current.safe) return false;
|
|
522
996
|
if (!current.claims.delete(sessionId)) return false;
|
|
@@ -531,7 +1005,8 @@ function removeEasSessionClaim(sessionId, root = easMachineStateRoot()) {
|
|
|
531
1005
|
//#region src/engine/eas-project-lock.ts
|
|
532
1006
|
const EAS_PROJECT_LOCK_WAIT_MS = 24e4;
|
|
533
1007
|
function withEasProjectLock(_root, fn, options = {}) {
|
|
534
|
-
const { machineRoot = easMachineStateRoot(), ...lockOptions } = options;
|
|
1008
|
+
const { machineRoot = easMachineStateRoot$1(), ...lockOptions } = options;
|
|
1009
|
+
assertEasMachineRootWritable(machineRoot);
|
|
535
1010
|
return withWorkspaceProcessLock(machineRoot, "eas-project", fn, {
|
|
536
1011
|
waitMs: EAS_PROJECT_LOCK_WAIT_MS,
|
|
537
1012
|
...lockOptions,
|
|
@@ -559,7 +1034,9 @@ function resolveMetroOrigin({ metroPort, publicUrl = null, mode = "auto", isExpo
|
|
|
559
1034
|
}
|
|
560
1035
|
function describe(err) {
|
|
561
1036
|
const e = err;
|
|
562
|
-
|
|
1037
|
+
const stderr = typeof e?.stderr === "string" ? e.stderr.trim() : "";
|
|
1038
|
+
if (err?.code === "ETIMEDOUT") return String(e?.message ?? err);
|
|
1039
|
+
return stderr || String(e?.message ?? err);
|
|
563
1040
|
}
|
|
564
1041
|
const PROXY_CREDENTIAL_ENV = ["AGENT_DEVICE_DAEMON_BASE_URL", "AGENT_DEVICE_DAEMON_AUTH_TOKEN"];
|
|
565
1042
|
function easExecOptions(root) {
|
|
@@ -569,6 +1046,9 @@ function easExecOptions(root) {
|
|
|
569
1046
|
};
|
|
570
1047
|
}
|
|
571
1048
|
const EAS_OPERATION_TIMEOUT_MS = 3e4;
|
|
1049
|
+
const EAS_SESSION_CREATE_TIMEOUT_MS = 6e5;
|
|
1050
|
+
const AGENT_DEVICE_TIMEOUT_MS = 12e4;
|
|
1051
|
+
const AGENT_DEVICE_INSTALL_TIMEOUT_MS = 6e5;
|
|
572
1052
|
function easBoundedExecOptions(root) {
|
|
573
1053
|
return {
|
|
574
1054
|
...easExecOptions(root),
|
|
@@ -643,8 +1123,22 @@ function remoteDeviceDeps(ctx) {
|
|
|
643
1123
|
label: ctx.label,
|
|
644
1124
|
platform: ctx.platform ?? "ios",
|
|
645
1125
|
maxDurationMinutes: ctx.maxDurationMinutes ?? null
|
|
646
|
-
}),
|
|
1126
|
+
}), {
|
|
1127
|
+
...easEnv,
|
|
1128
|
+
timeoutMs: EAS_SESSION_CREATE_TIMEOUT_MS,
|
|
1129
|
+
killSignal: "SIGKILL"
|
|
1130
|
+
});
|
|
647
1131
|
} catch (err) {
|
|
1132
|
+
if (err?.code === "ETIMEDOUT") {
|
|
1133
|
+
const partial = parseCreatedSession(String(err.stdout ?? ""));
|
|
1134
|
+
if (partial) return abandonSession(ctx, partial.id, `eas sim did not finish within ${EAS_SESSION_CREATE_TIMEOUT_MS / 6e4} minutes after creating session ${partial.id}.`);
|
|
1135
|
+
const name = ownedSessionName(ctx.label);
|
|
1136
|
+
return {
|
|
1137
|
+
failed: true,
|
|
1138
|
+
reason: `eas sim did not finish within ${EAS_SESSION_CREATE_TIMEOUT_MS / 6e4} minutes and the eas process was killed. EAS may still have started session ${name}; it has no Stim ownership claim, so \`stim gc\` will not stop it.`,
|
|
1139
|
+
remedy: `Run \`eas simulator:list --name ${name}\` and stop any running session with \`eas simulator:stop --id <id>\`, then retry.`
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
648
1142
|
return {
|
|
649
1143
|
failed: true,
|
|
650
1144
|
reason: `eas sim failed: ${describe(err)}`
|
|
@@ -674,13 +1168,17 @@ function remoteDeviceDeps(ctx) {
|
|
|
674
1168
|
try {
|
|
675
1169
|
getExecutor().runFile(ctx.agentDeviceBin, closeArgs(profilePath), {
|
|
676
1170
|
cwd: ctx.root,
|
|
677
|
-
env: daemonEnv(daemon)
|
|
1171
|
+
env: daemonEnv(daemon),
|
|
1172
|
+
timeoutMs: AGENT_DEVICE_TIMEOUT_MS,
|
|
1173
|
+
killSignal: "SIGKILL"
|
|
678
1174
|
});
|
|
679
1175
|
} catch {}
|
|
680
1176
|
try {
|
|
681
1177
|
getExecutor().runFile(ctx.agentDeviceBin, connectArgs(profilePath), {
|
|
682
1178
|
cwd: ctx.root,
|
|
683
|
-
env: daemonEnv(daemon)
|
|
1179
|
+
env: daemonEnv(daemon),
|
|
1180
|
+
timeoutMs: AGENT_DEVICE_TIMEOUT_MS,
|
|
1181
|
+
killSignal: "SIGKILL"
|
|
684
1182
|
});
|
|
685
1183
|
} catch (err) {
|
|
686
1184
|
if (createdHere) return abandonSession(ctx, createdHere, `agent-device connect failed: ${describe(err)}`);
|
|
@@ -706,7 +1204,9 @@ function remoteDeviceDeps(ctx) {
|
|
|
706
1204
|
try {
|
|
707
1205
|
getExecutor().runFile(ctx.agentDeviceBin, installArgs(session.profilePath, artifactPath), {
|
|
708
1206
|
cwd: ctx.root,
|
|
709
|
-
env: daemonEnv(session.daemon)
|
|
1207
|
+
env: daemonEnv(session.daemon),
|
|
1208
|
+
timeoutMs: AGENT_DEVICE_INSTALL_TIMEOUT_MS,
|
|
1209
|
+
killSignal: "SIGKILL"
|
|
710
1210
|
});
|
|
711
1211
|
return {
|
|
712
1212
|
ok: true,
|
|
@@ -725,7 +1225,9 @@ function remoteDeviceDeps(ctx) {
|
|
|
725
1225
|
if (metroPort === null) try {
|
|
726
1226
|
getExecutor().runFile(ctx.agentDeviceBin, openArgs(session.profilePath, appId, null, null), {
|
|
727
1227
|
cwd: ctx.root,
|
|
728
|
-
env: daemonEnv(session.daemon)
|
|
1228
|
+
env: daemonEnv(session.daemon),
|
|
1229
|
+
timeoutMs: AGENT_DEVICE_TIMEOUT_MS,
|
|
1230
|
+
killSignal: "SIGKILL"
|
|
729
1231
|
});
|
|
730
1232
|
return {
|
|
731
1233
|
ok: true,
|
|
@@ -753,12 +1255,16 @@ function remoteDeviceDeps(ctx) {
|
|
|
753
1255
|
try {
|
|
754
1256
|
getExecutor().runFile(ctx.agentDeviceBin, openArgs(session.profilePath, appId, url, metroHintFrom(origin.origin)), {
|
|
755
1257
|
cwd: ctx.root,
|
|
756
|
-
env: daemonEnv(session.daemon)
|
|
1258
|
+
env: daemonEnv(session.daemon),
|
|
1259
|
+
timeoutMs: AGENT_DEVICE_TIMEOUT_MS,
|
|
1260
|
+
killSignal: "SIGKILL"
|
|
757
1261
|
});
|
|
758
1262
|
if (url) try {
|
|
759
1263
|
getExecutor().runFile(ctx.agentDeviceBin, acceptAlertArgs(session.profilePath), {
|
|
760
1264
|
cwd: ctx.root,
|
|
761
|
-
env: daemonEnv(session.daemon)
|
|
1265
|
+
env: daemonEnv(session.daemon),
|
|
1266
|
+
timeoutMs: AGENT_DEVICE_TIMEOUT_MS,
|
|
1267
|
+
killSignal: "SIGKILL"
|
|
762
1268
|
});
|
|
763
1269
|
} catch {}
|
|
764
1270
|
return {
|
|
@@ -788,10 +1294,11 @@ function remoteDeviceDeps(ctx) {
|
|
|
788
1294
|
}
|
|
789
1295
|
return stopped;
|
|
790
1296
|
},
|
|
791
|
-
webPreviewUrl: () => session?.daemon.webPreviewUrl ?? null
|
|
1297
|
+
webPreviewUrl: () => session?.daemon.webPreviewUrl ?? null,
|
|
1298
|
+
failureRemedy: () => ctx.backend === "eas" && session?.id ? `EAS Simulator session ${session.id} is still running and billed until it reaches its time limit or \`stim stop\` ends it. Fix the error above and run this command again to reuse the session, or run \`stim stop\` to end it.` : "Check that the remote daemon is reachable and still has the device, then run this command again."
|
|
792
1299
|
};
|
|
793
1300
|
}
|
|
794
|
-
async function resolveRemoteContext({ root,
|
|
1301
|
+
async function resolveRemoteContext({ root, backend, platform = "ios", easBin, env = process.env, lookupAgentDevice = defaultLookupAgentDevice, readEasCliVersion = readInstalledEasCliVersion, maxDurationMinutes = null }) {
|
|
795
1302
|
const agentDeviceBin = lookupAgentDevice();
|
|
796
1303
|
if (!agentDeviceBin) return {
|
|
797
1304
|
failed: "agent-device is not on PATH, and it is what drives a remote device.",
|
|
@@ -825,9 +1332,17 @@ async function resolveRemoteContext({ root, label, backend, platform = "ios", ea
|
|
|
825
1332
|
remedy: "Install eas-cli, then run the device command with `--remote eas` again.",
|
|
826
1333
|
code: "STIM_REMOTE_EAS_UNAVAILABLE"
|
|
827
1334
|
};
|
|
1335
|
+
else {
|
|
1336
|
+
const { supported, version } = easCliSupport(readEasCliVersion(easBin, root), MIN_EAS_CLI_SIMULATOR_VERSION);
|
|
1337
|
+
if (!supported) return {
|
|
1338
|
+
failed: version ? `eas-cli ${version} (${easBin}) has no EAS Simulator commands; the eas backend needs eas-cli ${MIN_EAS_CLI_SIMULATOR_VERSION} or later.` : `Could not read an eas-cli version from \`${easBin} --version\`; the eas backend needs eas-cli ${MIN_EAS_CLI_SIMULATOR_VERSION} or later.`,
|
|
1339
|
+
remedy: `${easCliUpgradeRemedy(MIN_EAS_CLI_SIMULATOR_VERSION)}, then run the device command with \`--remote eas\` again.`,
|
|
1340
|
+
code: "STIM_REMOTE_EAS_UNAVAILABLE"
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
828
1343
|
return { ctx: {
|
|
829
1344
|
root,
|
|
830
|
-
label
|
|
1345
|
+
label: `${ownedDeviceLabel(root)}-${workspaceId(root)}`,
|
|
831
1346
|
backend,
|
|
832
1347
|
platform,
|
|
833
1348
|
easBin: easBin ?? "",
|
|
@@ -837,6 +1352,13 @@ async function resolveRemoteContext({ root, label, backend, platform = "ios", ea
|
|
|
837
1352
|
existingDaemon
|
|
838
1353
|
} };
|
|
839
1354
|
}
|
|
1355
|
+
function readInstalledEasCliVersion(easBin, root) {
|
|
1356
|
+
try {
|
|
1357
|
+
return getExecutor().runFile(easBin, ["--version"], easBoundedExecOptions(root));
|
|
1358
|
+
} catch {
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
840
1362
|
function binOnPath(bin) {
|
|
841
1363
|
try {
|
|
842
1364
|
return Boolean(getExecutor().findExecutable(bin));
|
|
@@ -915,7 +1437,7 @@ function withRemoteSessionLock(root, fn, options = {}) {
|
|
|
915
1437
|
...options
|
|
916
1438
|
});
|
|
917
1439
|
}
|
|
918
|
-
async function ensureRemoteBootOwned({ root, platform, sessionName, startedAt, boot, createdSessionId, abandonCreatedSession, writeState, register = () => {}, withProjectLock = withEasProjectLock, withLock = withRemoteSessionLock, removeEasSessionClaim: removeClaim = removeEasSessionClaim, ledgerRoot = easMachineStateRoot() }) {
|
|
1440
|
+
async function ensureRemoteBootOwned({ root, platform, sessionName, startedAt, boot, createdSessionId, abandonCreatedSession, webPreviewUrl = () => null, writeState, register = () => {}, withProjectLock = withEasProjectLock, withLock = withRemoteSessionLock, removeEasSessionClaim: removeClaim = removeEasSessionClaim, ledgerRoot = easMachineStateRoot$1() }) {
|
|
919
1441
|
try {
|
|
920
1442
|
return await withProjectLock(root, () => withLock(root, async () => {
|
|
921
1443
|
register();
|
|
@@ -932,10 +1454,12 @@ async function ensureRemoteBootOwned({ root, platform, sessionName, startedAt, b
|
|
|
932
1454
|
workspaceHome: getConfigDir(),
|
|
933
1455
|
stateFile: workspaceStateFile(root)
|
|
934
1456
|
}, ledgerRoot);
|
|
1457
|
+
const preview = webPreviewUrl();
|
|
935
1458
|
writeState(root, { remoteDevice: {
|
|
936
1459
|
platform,
|
|
937
1460
|
sessionId,
|
|
938
|
-
startedAt
|
|
1461
|
+
startedAt,
|
|
1462
|
+
...preview ? { webPreviewUrl: preview } : {}
|
|
939
1463
|
} });
|
|
940
1464
|
return booted;
|
|
941
1465
|
} catch (err) {
|
|
@@ -1041,7 +1565,8 @@ function remoteIosDeps(ctx) {
|
|
|
1041
1565
|
}),
|
|
1042
1566
|
createdSessionId: core.createdSessionId,
|
|
1043
1567
|
abandonCreatedSession: core.abandonCreatedSession,
|
|
1044
|
-
webPreviewUrl: core.webPreviewUrl
|
|
1568
|
+
webPreviewUrl: core.webPreviewUrl,
|
|
1569
|
+
failureRemedy: core.failureRemedy
|
|
1045
1570
|
};
|
|
1046
1571
|
}
|
|
1047
1572
|
function remoteAndroidDeps(ctx) {
|
|
@@ -1069,7 +1594,8 @@ function remoteAndroidDeps(ctx) {
|
|
|
1069
1594
|
}),
|
|
1070
1595
|
createdSessionId: core.createdSessionId,
|
|
1071
1596
|
abandonCreatedSession: core.abandonCreatedSession,
|
|
1072
|
-
webPreviewUrl: core.webPreviewUrl
|
|
1597
|
+
webPreviewUrl: core.webPreviewUrl,
|
|
1598
|
+
failureRemedy: core.failureRemedy
|
|
1073
1599
|
};
|
|
1074
1600
|
}
|
|
1075
1601
|
function endRecordedSession({ root, sessionId, easBin, lookupAgentDevice, ledgerRoot }) {
|
|
@@ -1091,10 +1617,14 @@ function endRecordedSession({ root, sessionId, easBin, lookupAgentDevice, ledger
|
|
|
1091
1617
|
function teardownRemote(ctx, { sessionId }) {
|
|
1092
1618
|
const profilePath = remoteProfilePath(ctx.root);
|
|
1093
1619
|
try {
|
|
1094
|
-
getExecutor().runFile(ctx.agentDeviceBin, disconnectArgs(profilePath), {
|
|
1620
|
+
getExecutor().runFile(ctx.agentDeviceBin, disconnectArgs(profilePath), {
|
|
1621
|
+
cwd: ctx.root,
|
|
1622
|
+
timeoutMs: AGENT_DEVICE_TIMEOUT_MS,
|
|
1623
|
+
killSignal: "SIGKILL"
|
|
1624
|
+
});
|
|
1095
1625
|
} catch {}
|
|
1096
1626
|
if (!sessionId) return { status: "torn-down" };
|
|
1097
|
-
const ledger = readEasSessionLedger(ctx.easLedgerRoot);
|
|
1627
|
+
const ledger = readEasSessionLedger$1(ctx.easLedgerRoot);
|
|
1098
1628
|
const claimNeedsRemoval = !ledger.safe || ledger.claims.has(sessionId);
|
|
1099
1629
|
const removeClaim = () => {
|
|
1100
1630
|
try {
|
|
@@ -1202,16 +1732,12 @@ async function ensureMetroReachable({ ctx, metroPort, isExpo, tunnelMode = "auto
|
|
|
1202
1732
|
}
|
|
1203
1733
|
function bundleEntryPoint(root, isExpo) {
|
|
1204
1734
|
if (!isExpo) return "index";
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
return local ? normalized : `node_modules/${normalized}`;
|
|
1212
|
-
} catch {
|
|
1213
|
-
return "index";
|
|
1214
|
-
}
|
|
1735
|
+
const declared = readJsonObject(resolve(root, "package.json"))?.main;
|
|
1736
|
+
if (typeof declared !== "string" || !declared.trim()) return "index";
|
|
1737
|
+
const main = declared.trim();
|
|
1738
|
+
const local = main.startsWith(".") || /\.[cm]?[jt]sx?$/.test(main) || existsSync(resolve(root, main));
|
|
1739
|
+
const normalized = main.replace(/^\.\//, "").replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
1740
|
+
return local ? normalized : `node_modules/${normalized}`;
|
|
1215
1741
|
}
|
|
1216
1742
|
//#endregion
|
|
1217
|
-
export {
|
|
1743
|
+
export { UPLOAD_TIMEOUT_MS as A, resolveRemote as B, listOwnedSessionsArgs as C, verifyStoppedSession as D, stopSessionArgs as E, isEasAuthFailureText as F, loadProjectProvider as I, ownerFromConfig as L, checkEasAuth as M, easAuthNote as N, gateMetroOrigin as O, exitAfterFlush as P, providerFromConfig as R, isDefinitiveMissingSessionError as S, parseSessionListPage as T, uploadRemote as V, easCliSupport as _, ensureMetroReachable as a, getSessionArgs as b, remoteAndroidDeps as c, withRemoteSessionLock as d, withEasProjectLock as f, MIN_EAS_CLI_SIMULATOR_VERSION as g, removeEasSessionClaim as h, endRecordedSession as i, cacheLevel as j, RESOLVE_TIMEOUT_MS as k, remoteIosDeps as l, readEasSessionLedger$1 as m, binOnPath as n, ensureRemoteBootOwned as o, easMachineStateRoot$1 as p, bundleEntryPoint as r, readInstalledEasCliVersion as s, REMOTE_SESSION_ERROR as t, resolveRemoteContext as u, easCliUpgradeRemedy as v, ownedSessionName as w, inspectSessionForTeardown as x, findOrphanedOwnedSessions as y, resolveEasCliBin as z };
|