stim 0.0.0 → 1.0.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/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/android-cas-compiler.d.mts +1 -0
- package/dist/android-cas-compiler.mjs +71 -0
- package/dist/android-jc8MnUyL.mjs +514 -0
- package/dist/cache-manifest-4tH64LQ9.mjs +66 -0
- package/dist/cache-manifest.d.mts +27 -0
- package/dist/cache-manifest.mjs +2 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +27182 -0
- package/dist/collector-run.d.mts +119 -0
- package/dist/collector-run.mjs +539 -0
- package/dist/config-7kmtuhO2.mjs +350 -0
- package/dist/errors-B6W7FkxQ.mjs +15 -0
- package/dist/exec-bsN9MJXb.mjs +63 -0
- package/dist/metro-store-COLl1pOk.mjs +1249 -0
- package/dist/ndjson-DcAtEx_K.mjs +131 -0
- package/dist/ndjson-DvHSsNU9.d.mts +16 -0
- package/dist/process-output-C7IlIzd7.mjs +40 -0
- package/dist/project-Dit1CckW.mjs +234 -0
- package/dist/server-bare-BmTVVmBB.mjs +351 -0
- package/dist/server-expo-BZJm--wA.mjs +2 -0
- package/dist/server-expo-CHJe1r6j.mjs +311 -0
- package/dist/state-2nush3MK.mjs +269 -0
- package/dist/supervisor-run.d.mts +85 -0
- package/dist/supervisor-run.mjs +210 -0
- package/package.json +61 -8
- package/shim/android-cas-pch.cmake +35 -0
- package/shim/android-cas.gradle +29 -0
- package/shim/android-cas.toolchain.cmake +12 -0
- package/shim/android-optimizations.gradle +30 -0
- package/shim/bundle-response.cjs +46 -0
- package/shim/bundle-response.d.cts +5 -0
- package/shim/expo-metro-config.cjs +188 -0
- package/shim/metro-cache-generation.cjs +20 -0
- package/shim/metro-cache-generation.d.cts +5 -0
- package/skill/SKILL.md +14 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { E as withDirLock, l as getConfigDir, w as sameProcessRecord } from "./config-7kmtuhO2.mjs";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join, resolve } from "path";
|
|
4
|
+
import { buildCacheRoot as sharedBuildCache, workspaceName, workspaceStateDir as workspaceDir } from "@stim-cli/core";
|
|
5
|
+
import { dirname as dirname$1 } from "node:path";
|
|
6
|
+
//#region src/paths.ts
|
|
7
|
+
const WORKSPACE_METADATA_FILE_NAME = "workspace.json";
|
|
8
|
+
function workspaceMetadataFile(projectRoot) {
|
|
9
|
+
return join(workspaceDir(projectRoot), WORKSPACE_METADATA_FILE_NAME);
|
|
10
|
+
}
|
|
11
|
+
function ensureWorkspaceStorage(projectRoot) {
|
|
12
|
+
const canonicalRoot = resolve(projectRoot);
|
|
13
|
+
const dir = workspaceDir(canonicalRoot);
|
|
14
|
+
const file = workspaceMetadataFile(canonicalRoot);
|
|
15
|
+
mkdirSync(dir, { recursive: true });
|
|
16
|
+
return withDirLock(join(dir, "metadata.lock"), () => {
|
|
17
|
+
if (!existsSync(file)) {
|
|
18
|
+
const metadata = {
|
|
19
|
+
projectRoot: canonicalRoot,
|
|
20
|
+
workspace: workspaceName(canonicalRoot),
|
|
21
|
+
version: 1
|
|
22
|
+
};
|
|
23
|
+
const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
24
|
+
writeFileSync(tmp, `${JSON.stringify(metadata, null, 2)}\n`);
|
|
25
|
+
try {
|
|
26
|
+
renameSync(tmp, file);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
rmSync(tmp, { force: true });
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
return dir;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
if (JSON.parse(readFileSync(file, "utf-8")).projectRoot === canonicalRoot) return dir;
|
|
35
|
+
} catch {}
|
|
36
|
+
const error = /* @__PURE__ */ new Error(`Stim workspace collision at ${dir}: its workspace.json does not belong to ${canonicalRoot}.`);
|
|
37
|
+
error.code = "STIM_WORKSPACE_COLLISION";
|
|
38
|
+
throw error;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function workspaceLogsDir(projectRoot) {
|
|
42
|
+
return join(workspaceDir(projectRoot), "logs");
|
|
43
|
+
}
|
|
44
|
+
function workspaceDerivedData(projectRoot) {
|
|
45
|
+
return join(workspaceDir(projectRoot), "derived-data");
|
|
46
|
+
}
|
|
47
|
+
function supervisorPidFile(projectRoot) {
|
|
48
|
+
return join(workspaceDir(projectRoot), "supervisor.pid");
|
|
49
|
+
}
|
|
50
|
+
function workspaceStateFile(projectRoot) {
|
|
51
|
+
return join(workspaceDir(projectRoot), "state.json");
|
|
52
|
+
}
|
|
53
|
+
function workspaceStateLock(projectRoot) {
|
|
54
|
+
return join(workspaceDir(projectRoot), "state.lock");
|
|
55
|
+
}
|
|
56
|
+
function supervisorLogFile(projectRoot) {
|
|
57
|
+
return join(workspaceLogsDir(projectRoot), "supervisor.log");
|
|
58
|
+
}
|
|
59
|
+
function emulatorLogFile(projectRoot) {
|
|
60
|
+
return join(workspaceLogsDir(projectRoot), "emulator.log");
|
|
61
|
+
}
|
|
62
|
+
function sharedCompilationCache() {
|
|
63
|
+
return join(getConfigDir(), "compilation-cache");
|
|
64
|
+
}
|
|
65
|
+
function sharedCcache() {
|
|
66
|
+
return join(getConfigDir(), "ccache");
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/supervisor/state.ts
|
|
70
|
+
const MODE_BARE = "bare-inproc";
|
|
71
|
+
const MODE_EXPO = "expo-child";
|
|
72
|
+
function readMetroTunnel(root) {
|
|
73
|
+
const record = readWorkspaceState(root)?.metroTunnel;
|
|
74
|
+
if (!record || typeof record !== "object") return null;
|
|
75
|
+
const kind = record.kind;
|
|
76
|
+
const url = record.url;
|
|
77
|
+
if (typeof url !== "string" || url.length === 0) return null;
|
|
78
|
+
if (kind === "expo") return {
|
|
79
|
+
kind: "expo",
|
|
80
|
+
url
|
|
81
|
+
};
|
|
82
|
+
if (kind === "managed") {
|
|
83
|
+
const provider = record.provider;
|
|
84
|
+
const pid = record.pid;
|
|
85
|
+
const port = record.port;
|
|
86
|
+
const startedAt = record.startedAt;
|
|
87
|
+
const processToken = record.processToken;
|
|
88
|
+
const logFile = record.logFile;
|
|
89
|
+
if (provider !== "ngrok" && provider !== "cloudflared" || typeof pid !== "number" || typeof port !== "number") return null;
|
|
90
|
+
return {
|
|
91
|
+
kind: "managed",
|
|
92
|
+
provider,
|
|
93
|
+
pid,
|
|
94
|
+
url,
|
|
95
|
+
port,
|
|
96
|
+
startedAt: typeof startedAt === "string" ? startedAt : "",
|
|
97
|
+
processToken: typeof processToken === "string" && processToken.length > 0 ? processToken : null,
|
|
98
|
+
...typeof logFile === "string" && logFile.length > 0 ? { logFile } : {}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
function readWorkspaceState(root) {
|
|
104
|
+
try {
|
|
105
|
+
const parsed = JSON.parse(readFileSync(workspaceStateFile(root), "utf-8"));
|
|
106
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
107
|
+
return parsed;
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function parseWorkspaceLaunchRecord(value) {
|
|
113
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
114
|
+
const record = value;
|
|
115
|
+
if (typeof record.appId !== "string" || record.appId.length === 0) return null;
|
|
116
|
+
if (typeof record.deviceId !== "string" || record.deviceId.length === 0) return null;
|
|
117
|
+
if (record.metroPort !== null && typeof record.metroPort !== "number") return null;
|
|
118
|
+
if (typeof record.release !== "boolean") return null;
|
|
119
|
+
if (record.deepLinkUrl !== null && typeof record.deepLinkUrl !== "string") return null;
|
|
120
|
+
if (typeof record.launchedAt !== "string") return null;
|
|
121
|
+
return record;
|
|
122
|
+
}
|
|
123
|
+
function readWorkspaceLaunches(root) {
|
|
124
|
+
const launches = readWorkspaceState(root)?.launches;
|
|
125
|
+
if (!launches || typeof launches !== "object" || Array.isArray(launches)) return {};
|
|
126
|
+
const ios = parseWorkspaceLaunchRecord(launches.ios);
|
|
127
|
+
const android = parseWorkspaceLaunchRecord(launches.android);
|
|
128
|
+
return {
|
|
129
|
+
...ios ? { ios } : {},
|
|
130
|
+
...android ? { android } : {}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function writeWorkspaceLaunch(root, platform, record) {
|
|
134
|
+
withWorkspaceStateLock(root, () => {
|
|
135
|
+
const state = readWorkspaceState(root) ?? {};
|
|
136
|
+
const launches = state.launches && typeof state.launches === "object" ? state.launches : {};
|
|
137
|
+
replaceWorkspaceState(root, {
|
|
138
|
+
...state,
|
|
139
|
+
launches: {
|
|
140
|
+
...launches,
|
|
141
|
+
[platform]: record
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function readRemoteSession(root) {
|
|
147
|
+
const record = readWorkspaceState(root)?.remoteDevice;
|
|
148
|
+
if (!record || typeof record !== "object") return null;
|
|
149
|
+
const id = record.sessionId;
|
|
150
|
+
if (typeof id !== "string" || id.length === 0) return null;
|
|
151
|
+
const platform = record.platform;
|
|
152
|
+
const startedAt = record.startedAt;
|
|
153
|
+
return {
|
|
154
|
+
platform: platform === "ios" || platform === "android" ? platform : null,
|
|
155
|
+
sessionId: id,
|
|
156
|
+
startedAt: typeof startedAt === "string" ? startedAt : null
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function readRemoteSessionId(root) {
|
|
160
|
+
return readRemoteSession(root)?.sessionId ?? null;
|
|
161
|
+
}
|
|
162
|
+
function clearRemoteSession(root, expectedSessionId) {
|
|
163
|
+
clearWorkspaceStateKey(root, "remoteDevice", (value) => {
|
|
164
|
+
if (typeof value !== "object" || value === null) return false;
|
|
165
|
+
return value.sessionId === expectedSessionId;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
function withWorkspaceStateLock(root, fn) {
|
|
169
|
+
const file = workspaceStateFile(root);
|
|
170
|
+
return withDirLock(workspaceStateLock(root), fn, { ensureParent: () => {
|
|
171
|
+
ensureWorkspaceStorage(root);
|
|
172
|
+
mkdirSync(dirname$1(file), { recursive: true });
|
|
173
|
+
} });
|
|
174
|
+
}
|
|
175
|
+
function writeWorkspaceState(root, patch) {
|
|
176
|
+
return withWorkspaceStateLock(root, () => replaceWorkspaceState(root, {
|
|
177
|
+
...readWorkspaceState(root),
|
|
178
|
+
...patch
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
function replaceWorkspaceState(root, state) {
|
|
182
|
+
const file = workspaceStateFile(root);
|
|
183
|
+
mkdirSync(dirname$1(file), { recursive: true });
|
|
184
|
+
const tmp = `${file}.tmp-${process.pid}`;
|
|
185
|
+
writeFileSync(tmp, `${JSON.stringify(state, null, 2)}\n`);
|
|
186
|
+
renameSync(tmp, file);
|
|
187
|
+
return state;
|
|
188
|
+
}
|
|
189
|
+
function clearWorkspaceSupervisor(root, expected) {
|
|
190
|
+
return withWorkspaceStateLock(root, () => {
|
|
191
|
+
const current = readWorkspaceState(root)?.supervisor;
|
|
192
|
+
if (current && expected !== void 0 && !sameProcessRecord(current, expected)) return false;
|
|
193
|
+
clearWorkspaceStateKey(root, "supervisor", () => true);
|
|
194
|
+
clearExpoMetroTunnel(root);
|
|
195
|
+
if (expected === void 0 || readPidFile(root) === expected?.pid) rmSync(supervisorPidFile(root), { force: true });
|
|
196
|
+
return true;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function clearExpoMetroTunnel(root) {
|
|
200
|
+
clearWorkspaceStateKey(root, "metroTunnel", (value) => {
|
|
201
|
+
return typeof value === "object" && value !== null && value.kind === "expo";
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function clearManagedMetroTunnel(root, expected) {
|
|
205
|
+
if (!existsSync(workspaceStateFile(root))) return true;
|
|
206
|
+
return clearWorkspaceStateKey(root, "metroTunnel", (value) => {
|
|
207
|
+
if (typeof value !== "object" || value === null) return false;
|
|
208
|
+
const record = value;
|
|
209
|
+
return record.kind === "managed" && record.provider === expected.provider && record.pid === expected.pid && record.url === expected.url && record.port === expected.port && record.startedAt === expected.startedAt && (record.processToken ?? null) === (expected.processToken ?? null) && (record.logFile ?? null) === (expected.logFile ?? null);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
function clearWorkspaceStateKeys(root, keys) {
|
|
213
|
+
if (!existsSync(workspaceStateFile(root))) return;
|
|
214
|
+
withWorkspaceStateLock(root, () => {
|
|
215
|
+
const state = readWorkspaceState(root);
|
|
216
|
+
const file = workspaceStateFile(root);
|
|
217
|
+
if (!state) {
|
|
218
|
+
try {
|
|
219
|
+
rmSync(file, { force: true });
|
|
220
|
+
} catch {}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
let changed = false;
|
|
224
|
+
for (const key of keys) {
|
|
225
|
+
if (!(key in state)) continue;
|
|
226
|
+
delete state[key];
|
|
227
|
+
changed = true;
|
|
228
|
+
}
|
|
229
|
+
if (!changed) return;
|
|
230
|
+
if (Object.keys(state).length === 0) {
|
|
231
|
+
rmSync(file, { force: true });
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
replaceWorkspaceState(root, state);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function clearWorkspaceStateKey(root, key, shouldClear) {
|
|
238
|
+
return withWorkspaceStateLock(root, () => {
|
|
239
|
+
const state = readWorkspaceState(root);
|
|
240
|
+
if (!state || !(key in state)) return true;
|
|
241
|
+
if (!shouldClear(state[key])) return false;
|
|
242
|
+
delete state[key];
|
|
243
|
+
const file = workspaceStateFile(root);
|
|
244
|
+
if (Object.keys(state).length === 0) {
|
|
245
|
+
try {
|
|
246
|
+
rmSync(file, { force: true });
|
|
247
|
+
} catch {}
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
replaceWorkspaceState(root, state);
|
|
251
|
+
return true;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
function writePidFile(root, pid) {
|
|
255
|
+
const file = supervisorPidFile(root);
|
|
256
|
+
mkdirSync(dirname$1(file), { recursive: true });
|
|
257
|
+
writeFileSync(file, `${pid}\n`);
|
|
258
|
+
return file;
|
|
259
|
+
}
|
|
260
|
+
function readPidFile(root) {
|
|
261
|
+
try {
|
|
262
|
+
const pid = parseInt(readFileSync(supervisorPidFile(root), "utf-8").trim(), 10);
|
|
263
|
+
return Number.isFinite(pid) ? pid : null;
|
|
264
|
+
} catch {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
//#endregion
|
|
269
|
+
export { supervisorLogFile as C, workspaceName as D, workspaceLogsDir as E, workspaceStateFile as O, sharedCompilationCache as S, workspaceDir as T, writeWorkspaceState as _, clearRemoteSession as a, sharedBuildCache as b, readMetroTunnel as c, readRemoteSessionId as d, readWorkspaceLaunches as f, writeWorkspaceLaunch as g, writePidFile as h, clearManagedMetroTunnel as i, readPidFile as l, withWorkspaceStateLock as m, MODE_EXPO as n, clearWorkspaceStateKeys as o, readWorkspaceState as p, clearExpoMetroTunnel as r, clearWorkspaceSupervisor as s, MODE_BARE as t, readRemoteSession as u, emulatorLogFile as v, workspaceDerivedData as w, sharedCcache as x, ensureWorkspaceStorage as y };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { t as NdjsonWriter } from "./ndjson-DvHSsNU9.mjs";
|
|
2
|
+
//#region src/process-identity.d.ts
|
|
3
|
+
interface ProcessRecord {
|
|
4
|
+
pid?: unknown;
|
|
5
|
+
processToken?: unknown;
|
|
6
|
+
}
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/supervisor/state.d.ts
|
|
9
|
+
declare const MODE_BARE = "bare-inproc";
|
|
10
|
+
declare const MODE_EXPO = "expo-child";
|
|
11
|
+
interface WorkspaceState {
|
|
12
|
+
metroCacheGeneration?: string;
|
|
13
|
+
supervisor?: Record<string, unknown>;
|
|
14
|
+
collectors?: Record<string, unknown>;
|
|
15
|
+
lastBuild?: Record<string, unknown>;
|
|
16
|
+
launches?: Partial<Record<WorkspaceLaunchPlatform, WorkspaceLaunchRecord>>;
|
|
17
|
+
remoteDevice?: Record<string, unknown>;
|
|
18
|
+
metroTunnel?: Record<string, unknown>;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
type WorkspaceLaunchPlatform = "ios" | "android";
|
|
22
|
+
interface WorkspaceLaunchRecord {
|
|
23
|
+
appId: string;
|
|
24
|
+
deviceId: string;
|
|
25
|
+
metroPort: number | null;
|
|
26
|
+
release: boolean;
|
|
27
|
+
deepLinkUrl: string | null;
|
|
28
|
+
launchedAt: string;
|
|
29
|
+
}
|
|
30
|
+
declare function readWorkspaceState(root: string): WorkspaceState | null;
|
|
31
|
+
declare function withWorkspaceStateLock<T>(root: string, fn: () => T): T;
|
|
32
|
+
declare function writeWorkspaceState(root: string, patch: WorkspaceState): WorkspaceState;
|
|
33
|
+
declare function clearWorkspaceSupervisor(root: string, expected?: ProcessRecord | null): boolean;
|
|
34
|
+
declare function clearExpoMetroTunnel(root: string): void;
|
|
35
|
+
declare function writePidFile(root: string, pid: number): string;
|
|
36
|
+
declare function readPidFile(root: string): number | null;
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/supervisor/run.d.ts
|
|
39
|
+
interface ParsedSupervisorArgs {
|
|
40
|
+
root?: string;
|
|
41
|
+
port?: number;
|
|
42
|
+
tunnel?: boolean;
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
declare function parseArgs(argv: string[]): ParsedSupervisorArgs;
|
|
46
|
+
interface ServerExitInfo {
|
|
47
|
+
code?: number | null;
|
|
48
|
+
signal?: NodeJS.Signals | null;
|
|
49
|
+
reason?: string;
|
|
50
|
+
error?: Error;
|
|
51
|
+
}
|
|
52
|
+
interface ServerHandle {
|
|
53
|
+
close(): Promise<void> | void;
|
|
54
|
+
onExit?(cb: (info?: ServerExitInfo | null) => void): void;
|
|
55
|
+
serverPid?: number | null;
|
|
56
|
+
}
|
|
57
|
+
type ServerStarter = (opts: {
|
|
58
|
+
root: string;
|
|
59
|
+
port: number;
|
|
60
|
+
logsDir: string;
|
|
61
|
+
writer?: NdjsonWriter | null;
|
|
62
|
+
tunnel?: boolean;
|
|
63
|
+
onTunnelUrl?: ((url: string) => void) | null;
|
|
64
|
+
}) => Promise<ServerHandle>;
|
|
65
|
+
interface RunSupervisorOptions {
|
|
66
|
+
root: string;
|
|
67
|
+
port: number;
|
|
68
|
+
tunnel?: boolean;
|
|
69
|
+
isExpo?: (projectRoot: string) => boolean;
|
|
70
|
+
startBare?: ServerStarter | null;
|
|
71
|
+
startExpo?: ServerStarter | null;
|
|
72
|
+
now?: () => number;
|
|
73
|
+
onExit?: (code: number) => void;
|
|
74
|
+
attachSignals?: boolean;
|
|
75
|
+
stderr?: (line: string) => void;
|
|
76
|
+
}
|
|
77
|
+
declare function runSupervisor({ root, port, tunnel, isExpo, startBare, startExpo, now, onExit, attachSignals, stderr }: RunSupervisorOptions): Promise<{
|
|
78
|
+
mode: string;
|
|
79
|
+
server: ServerHandle | undefined;
|
|
80
|
+
shutdown: (code: number, event: string, msg: string) => Promise<void>;
|
|
81
|
+
startedAt: string;
|
|
82
|
+
} | null>;
|
|
83
|
+
declare function main(argv?: string[]): Promise<void>;
|
|
84
|
+
//#endregion
|
|
85
|
+
export { MODE_BARE, MODE_EXPO, RunSupervisorOptions, ServerExitInfo, ServerHandle, type WorkspaceState, clearExpoMetroTunnel, clearWorkspaceSupervisor, main, parseArgs, readPidFile, readWorkspaceState, runSupervisor, withWorkspaceStateLock, writePidFile, writeWorkspaceState };
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { S as captureProcessToken, a as clearSupervisor, y as setSupervisor } from "./config-7kmtuhO2.mjs";
|
|
2
|
+
import { i as detectIsExpo } from "./project-Dit1CckW.mjs";
|
|
3
|
+
import { E as workspaceLogsDir, _ as writeWorkspaceState, h as writePidFile, l as readPidFile, m as withWorkspaceStateLock, n as MODE_EXPO, p as readWorkspaceState, r as clearExpoMetroTunnel, s as clearWorkspaceSupervisor, t as MODE_BARE } from "./state-2nush3MK.mjs";
|
|
4
|
+
import { r as createNdjsonWriter } from "./ndjson-DcAtEx_K.mjs";
|
|
5
|
+
import { t as describeError } from "./errors-B6W7FkxQ.mjs";
|
|
6
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
7
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
//#region src/supervisor/run.ts
|
|
10
|
+
function parseArgs(argv) {
|
|
11
|
+
let root;
|
|
12
|
+
let port;
|
|
13
|
+
let tunnel = false;
|
|
14
|
+
for (let i = 0; i < argv.length; i++) {
|
|
15
|
+
const arg = argv[i];
|
|
16
|
+
if (arg === "--root") {
|
|
17
|
+
root = argv[++i];
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (arg === "--port") {
|
|
21
|
+
port = argv[++i];
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (arg === "--tunnel") {
|
|
25
|
+
tunnel = true;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
return { error: `Unknown supervisor argument "${arg}". Usage: run.js --root <path> --port <n> [--tunnel]` };
|
|
29
|
+
}
|
|
30
|
+
if (!root) return { error: "Missing --root. Usage: run.js --root <path> --port <n> [--tunnel]" };
|
|
31
|
+
if (!isAbsolute(root)) return { error: `--root must be an absolute path, got "${root}".` };
|
|
32
|
+
const parsedPort = Number(port);
|
|
33
|
+
if (!Number.isInteger(parsedPort) || parsedPort <= 0 || parsedPort > 65535) return { error: `--port must be a TCP port number, got "${port}".` };
|
|
34
|
+
return {
|
|
35
|
+
root: resolve(root),
|
|
36
|
+
port: parsedPort,
|
|
37
|
+
tunnel
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async function runSupervisor({ root, port, tunnel = false, isExpo = detectIsExpo, startBare = null, startExpo = null, now = Date.now, onExit = (code) => process.exit(code), attachSignals = true, stderr = (line) => console.error(line) }) {
|
|
41
|
+
root = realpathSync(root);
|
|
42
|
+
const logsDir = workspaceLogsDir(root);
|
|
43
|
+
const writer = createNdjsonWriter(join(logsDir, "metro.ndjson"));
|
|
44
|
+
const mode = isExpo(root) ? MODE_EXPO : MODE_BARE;
|
|
45
|
+
const startedAt = new Date(now()).toISOString();
|
|
46
|
+
const processToken = captureProcessToken(process.pid);
|
|
47
|
+
if (!processToken) {
|
|
48
|
+
stderr("Stim supervisor: could not capture process identity; refusing to start an unmanaged server.");
|
|
49
|
+
writer.close();
|
|
50
|
+
onExit(1);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const record = {
|
|
54
|
+
pid: process.pid,
|
|
55
|
+
processToken,
|
|
56
|
+
port,
|
|
57
|
+
mode,
|
|
58
|
+
startedAt
|
|
59
|
+
};
|
|
60
|
+
clearExpoMetroTunnel(root);
|
|
61
|
+
writePidFile(root, process.pid);
|
|
62
|
+
writeWorkspaceState(root, { supervisor: record });
|
|
63
|
+
try {
|
|
64
|
+
setSupervisor(root, record);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
writer.write({
|
|
67
|
+
src: "metro",
|
|
68
|
+
level: "warn",
|
|
69
|
+
event: "supervisor_registration_failed",
|
|
70
|
+
msg: `Could not record the supervisor in the global config: ${describeError(err)}`
|
|
71
|
+
});
|
|
72
|
+
stderr(`Stim supervisor: global registration failed: ${describeError(err)}`);
|
|
73
|
+
}
|
|
74
|
+
writer.write({
|
|
75
|
+
src: "metro",
|
|
76
|
+
level: "info",
|
|
77
|
+
event: "supervisor_started",
|
|
78
|
+
msg: `supervisor pid ${process.pid} starting the ${mode} dev server on port ${port} for ${root}`
|
|
79
|
+
});
|
|
80
|
+
let stopping = false;
|
|
81
|
+
const finish = (code, event, level, msg) => {
|
|
82
|
+
writer.write({
|
|
83
|
+
src: "metro",
|
|
84
|
+
level,
|
|
85
|
+
event,
|
|
86
|
+
msg
|
|
87
|
+
});
|
|
88
|
+
try {
|
|
89
|
+
clearSupervisor(root, record);
|
|
90
|
+
} catch {}
|
|
91
|
+
try {
|
|
92
|
+
clearWorkspaceSupervisor(root, record);
|
|
93
|
+
} catch {}
|
|
94
|
+
const closed = writer.close();
|
|
95
|
+
if (closed.dropped > 0) stderr(`Stim supervisor: dropped ${closed.dropped} log record(s); last error: ${describeError(closed.lastError)}`);
|
|
96
|
+
onExit(code);
|
|
97
|
+
};
|
|
98
|
+
let server;
|
|
99
|
+
try {
|
|
100
|
+
server = await (mode === "expo-child" ? startExpo || (await import("./server-expo-BZJm--wA.mjs")).startExpoServer : startBare || (await import("./server-bare-BmTVVmBB.mjs")).startBareServer)({
|
|
101
|
+
root,
|
|
102
|
+
port,
|
|
103
|
+
logsDir,
|
|
104
|
+
writer,
|
|
105
|
+
tunnel,
|
|
106
|
+
onTunnelUrl: (url) => {
|
|
107
|
+
try {
|
|
108
|
+
withWorkspaceStateLock(root, () => {
|
|
109
|
+
if (readWorkspaceState(root)?.supervisor?.processToken === processToken) writeWorkspaceState(root, { metroTunnel: {
|
|
110
|
+
kind: "expo",
|
|
111
|
+
url
|
|
112
|
+
} });
|
|
113
|
+
});
|
|
114
|
+
} catch (err) {
|
|
115
|
+
stderr(`Stim supervisor: could not record the Expo tunnel URL: ${describeError(err)}`);
|
|
116
|
+
}
|
|
117
|
+
writer.write({
|
|
118
|
+
src: "metro",
|
|
119
|
+
level: "info",
|
|
120
|
+
event: "expo_tunnel_ready",
|
|
121
|
+
msg: `Expo tunnel ready: ${url}`
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
} catch (err) {
|
|
126
|
+
stderr(`Stim supervisor: failed to start the ${mode} dev server: ${describeError(err)}`);
|
|
127
|
+
finish(1, "supervisor_failed", "fatal", `failed to start the ${mode} dev server: ${describeError(err)}`);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const readyServer = server;
|
|
131
|
+
if (readyServer.serverPid) withWorkspaceStateLock(root, () => {
|
|
132
|
+
if (readWorkspaceState(root)?.supervisor?.processToken === processToken) writeWorkspaceState(root, { supervisor: {
|
|
133
|
+
...record,
|
|
134
|
+
serverPid: readyServer.serverPid
|
|
135
|
+
} });
|
|
136
|
+
});
|
|
137
|
+
writer.write({
|
|
138
|
+
src: "metro",
|
|
139
|
+
level: "info",
|
|
140
|
+
event: "server_started",
|
|
141
|
+
msg: `${mode} dev server listening on port ${port}`
|
|
142
|
+
});
|
|
143
|
+
const shutdown = async (code, event, msg) => {
|
|
144
|
+
if (stopping) return;
|
|
145
|
+
stopping = true;
|
|
146
|
+
try {
|
|
147
|
+
await readyServer.close();
|
|
148
|
+
} catch (err) {
|
|
149
|
+
writer.write({
|
|
150
|
+
src: "metro",
|
|
151
|
+
level: "warn",
|
|
152
|
+
event: "server_close_failed",
|
|
153
|
+
msg: describeError(err)
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
finish(code, event, code === 0 ? "info" : "error", msg);
|
|
157
|
+
};
|
|
158
|
+
readyServer.onExit?.((info) => {
|
|
159
|
+
if (stopping) return;
|
|
160
|
+
const detail = info?.signal ? `signal ${info.signal}` : `exit code ${info?.code ?? "unknown"}`;
|
|
161
|
+
shutdown(1, "supervisor_stopped", `the ${mode} dev server exited unexpectedly (${detail}); shutting the supervisor down`);
|
|
162
|
+
});
|
|
163
|
+
if (attachSignals) for (const signal of ["SIGTERM", "SIGINT"]) process.on(signal, () => {
|
|
164
|
+
shutdown(0, "supervisor_stopped", `received ${signal}; stopping the ${mode} dev server`);
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
mode,
|
|
168
|
+
server,
|
|
169
|
+
shutdown,
|
|
170
|
+
startedAt
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async function main(argv = process.argv.slice(2)) {
|
|
174
|
+
const parsed = parseArgs(argv);
|
|
175
|
+
if (parsed.error) {
|
|
176
|
+
console.error(`Stim supervisor: ${parsed.error}`);
|
|
177
|
+
process.exit(2);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const root = parsed.root;
|
|
181
|
+
if (!existsSync(root)) {
|
|
182
|
+
console.error(`Stim supervisor: --root ${root} does not exist.`);
|
|
183
|
+
process.exit(2);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
process.chdir(root);
|
|
188
|
+
} catch {}
|
|
189
|
+
process.title = "stim-supervisor";
|
|
190
|
+
await runSupervisor({
|
|
191
|
+
root,
|
|
192
|
+
port: parsed.port,
|
|
193
|
+
tunnel: parsed.tunnel ?? false
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function invokedDirectly() {
|
|
197
|
+
const entry = process.argv[1];
|
|
198
|
+
if (!entry) return false;
|
|
199
|
+
try {
|
|
200
|
+
return resolve(entry) === fileURLToPath(import.meta.url);
|
|
201
|
+
} catch {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (invokedDirectly()) main().catch((err) => {
|
|
206
|
+
console.error(`Stim supervisor: ${describeError(err)}`);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
});
|
|
209
|
+
//#endregion
|
|
210
|
+
export { MODE_BARE, MODE_EXPO, clearExpoMetroTunnel, clearWorkspaceSupervisor, main, parseArgs, readPidFile, readWorkspaceState, runSupervisor, withWorkspaceStateLock, writePidFile, writeWorkspaceState };
|
package/package.json
CHANGED
|
@@ -1,11 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stim",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Stim",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Stim: fast, isolated React Native environments for coding agents",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agent",
|
|
7
|
+
"emulator",
|
|
8
|
+
"expo",
|
|
9
|
+
"metro",
|
|
10
|
+
"react-native",
|
|
11
|
+
"simulator",
|
|
12
|
+
"worktree"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/appandflow/stim#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/appandflow/stim/issues"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Janic Duplessis",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/appandflow/stim.git",
|
|
23
|
+
"directory": "packages/stim-cli"
|
|
24
|
+
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"stim": "dist/cli.mjs"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"shim",
|
|
31
|
+
"skill",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"type": "module",
|
|
36
|
+
"exports": {
|
|
37
|
+
"./cache-manifest": {
|
|
38
|
+
"types": "./dist/cache-manifest.d.mts",
|
|
39
|
+
"module-sync": "./dist/cache-manifest.mjs",
|
|
40
|
+
"default": "./dist/cache-manifest.mjs"
|
|
41
|
+
}
|
|
8
42
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@expo/fingerprint": "^0.20.10",
|
|
45
|
+
"@stim-cli/cache": "^1.0.0",
|
|
46
|
+
"@stim-cli/core": "^1.0.0",
|
|
47
|
+
"@stim-cli/metro": "^1.0.0",
|
|
48
|
+
"chalk": "^5.4.1",
|
|
49
|
+
"commander": "^13.1.0",
|
|
50
|
+
"unique-pid": "0.1.1",
|
|
51
|
+
"ws": "^8.21.3"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@stim-cli/expo-build-cache": "1.0.0",
|
|
55
|
+
"@types/ws": "^8.18.1"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": "^20.19.4 || >=22.12.0"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"test": "node --test test/*.test.js",
|
|
62
|
+
"build": "tsdown"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
function(_stim_cas_pch directory)
|
|
2
|
+
get_property(targets DIRECTORY "${directory}" PROPERTY BUILDSYSTEM_TARGETS)
|
|
3
|
+
foreach(target IN LISTS targets)
|
|
4
|
+
get_target_property(headers "${target}" PRECOMPILE_HEADERS)
|
|
5
|
+
if(NOT headers)
|
|
6
|
+
continue()
|
|
7
|
+
endif()
|
|
8
|
+
set(portable)
|
|
9
|
+
foreach(header IN LISTS headers)
|
|
10
|
+
set(mapped "${header}")
|
|
11
|
+
if(IS_ABSOLUTE "${header}")
|
|
12
|
+
foreach(root IN ITEMS "${CMAKE_BINARY_DIR}" "$ENV{STIM_ANDROID_CAS_SOURCE_ROOT}")
|
|
13
|
+
string(FIND "${header}" "${root}/" position)
|
|
14
|
+
if(position EQUAL 0)
|
|
15
|
+
string(LENGTH "${root}/" length)
|
|
16
|
+
string(SUBSTRING "${header}" ${length} -1 suffix)
|
|
17
|
+
if(root STREQUAL CMAKE_BINARY_DIR)
|
|
18
|
+
set(mapped "\"/^build/${suffix}\"")
|
|
19
|
+
else()
|
|
20
|
+
set(mapped "\"/^src/${suffix}\"")
|
|
21
|
+
endif()
|
|
22
|
+
break()
|
|
23
|
+
endif()
|
|
24
|
+
endforeach()
|
|
25
|
+
endif()
|
|
26
|
+
list(APPEND portable "${mapped}")
|
|
27
|
+
endforeach()
|
|
28
|
+
set_property(TARGET "${target}" PROPERTY PRECOMPILE_HEADERS "${portable}")
|
|
29
|
+
endforeach()
|
|
30
|
+
get_property(children DIRECTORY "${directory}" PROPERTY SUBDIRECTORIES)
|
|
31
|
+
foreach(child IN LISTS children)
|
|
32
|
+
_stim_cas_pch("${child}")
|
|
33
|
+
endforeach()
|
|
34
|
+
endfunction()
|
|
35
|
+
cmake_language(DEFER CALL _stim_cas_pch "${CMAKE_CURRENT_SOURCE_DIR}")
|