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,29 @@
|
|
|
1
|
+
gradle.beforeProject { project ->
|
|
2
|
+
["com.android.application", "com.android.library"].each { plugin ->
|
|
3
|
+
project.pluginManager.withPlugin(plugin) {
|
|
4
|
+
project.extensions.getByName("androidComponents").finalizeDsl { android ->
|
|
5
|
+
def reactApplication = project.plugins.hasPlugin("com.android.application") && project.plugins.hasPlugin("com.facebook.react")
|
|
6
|
+
if (!android.externalNativeBuild.cmake.path && !reactApplication) return
|
|
7
|
+
def expectedNdk = System.getenv("STIM_ANDROID_CAS_NDK_VERSION")
|
|
8
|
+
if (android.ndkVersion && android.ndkVersion != expectedNdk) {
|
|
9
|
+
throw new GradleException("Stim CAS toolchain requires NDK ${expectedNdk}; ${project.path} selects ${android.ndkVersion}.")
|
|
10
|
+
}
|
|
11
|
+
if (android.ndkPath && new File(android.ndkPath).canonicalPath != new File(System.getenv("STIM_ANDROID_CAS_NDK")).canonicalPath) {
|
|
12
|
+
throw new GradleException("Stim CAS toolchain does not match ${project.path}'s explicit ndkPath.")
|
|
13
|
+
}
|
|
14
|
+
def configs = [android.defaultConfig] + android.buildTypes.toList() + android.productFlavors.toList()
|
|
15
|
+
if (configs.any { config -> config.externalNativeBuild.cmake.arguments.any { arg ->
|
|
16
|
+
arg =~ /^(?:-D\s*)?CMAKE_(?:TOOLCHAIN_FILE|PROJECT_INCLUDE)(?::[^=]+)?=/
|
|
17
|
+
} }) {
|
|
18
|
+
throw new GradleException("Stim CAS prototype cannot replace ${project.path}'s custom CMake toolchain or project hook.")
|
|
19
|
+
}
|
|
20
|
+
android.defaultConfig.externalNativeBuild.cmake.arguments.addAll([
|
|
21
|
+
"-DCMAKE_TOOLCHAIN_FILE=" + System.getenv("STIM_ANDROID_CAS_CMAKE_TOOLCHAIN"),
|
|
22
|
+
"-DCMAKE_PROJECT_INCLUDE=" + System.getenv("STIM_ANDROID_CAS_CMAKE_PCH"),
|
|
23
|
+
"-DSTIM_ANDROID_CAS_STATE=" + System.getenv("STIM_ANDROID_CAS_STATE"),
|
|
24
|
+
"-DCCACHE_FOUND=OFF"
|
|
25
|
+
])
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
set(ANDROID_NDK "$ENV{STIM_ANDROID_CAS_NDK}")
|
|
2
|
+
include("$ENV{STIM_ANDROID_CAS_NDK}/build/cmake/android.toolchain.cmake")
|
|
3
|
+
set(CMAKE_C_COMPILER "${STIM_ANDROID_CAS_STATE}/clang")
|
|
4
|
+
set(CMAKE_CXX_COMPILER "${STIM_ANDROID_CAS_STATE}/clang++")
|
|
5
|
+
set(CMAKE_ASM_COMPILER "${STIM_ANDROID_CAS_STATE}/clang")
|
|
6
|
+
list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES STIM_ANDROID_CAS_STATE)
|
|
7
|
+
set(CMAKE_AR "$ENV{STIM_ANDROID_CAS_AR}" CACHE FILEPATH "Archiver" FORCE)
|
|
8
|
+
set(CMAKE_RANLIB "$ENV{STIM_ANDROID_CAS_RANLIB}" CACHE FILEPATH "Ranlib" FORCE)
|
|
9
|
+
set(CMAKE_C_COMPILER_AR "$ENV{STIM_ANDROID_CAS_AR}" CACHE FILEPATH "Compiler archiver" FORCE)
|
|
10
|
+
set(CMAKE_CXX_COMPILER_AR "$ENV{STIM_ANDROID_CAS_AR}" CACHE FILEPATH "Compiler archiver" FORCE)
|
|
11
|
+
set(CMAKE_C_COMPILER_RANLIB "$ENV{STIM_ANDROID_CAS_RANLIB}" CACHE FILEPATH "Compiler ranlib" FORCE)
|
|
12
|
+
set(CMAKE_CXX_COMPILER_RANLIB "$ENV{STIM_ANDROID_CAS_RANLIB}" CACHE FILEPATH "Compiler ranlib" FORCE)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
gradle.beforeProject { project ->
|
|
2
|
+
["com.android.application", "com.android.library"].each { plugin ->
|
|
3
|
+
project.pluginManager.withPlugin(plugin) {
|
|
4
|
+
project.extensions.getByName("androidComponents").finalizeDsl { android ->
|
|
5
|
+
def reactApplication = project.plugins.hasPlugin("com.android.application") && project.plugins.hasPlugin("com.facebook.react")
|
|
6
|
+
if (!android.externalNativeBuild.cmake.path && !reactApplication) return
|
|
7
|
+
def profile = System.getenv("STIM_ANDROID_NATIVE_PROFILE")
|
|
8
|
+
def staging = android.externalNativeBuild.cmake.buildStagingDirectory ?: new File(project.projectDir, ".cxx")
|
|
9
|
+
android.externalNativeBuild.cmake.buildStagingDirectory = new File(staging, "stim-" + profile)
|
|
10
|
+
def configs = [android.defaultConfig] + android.buildTypes.toList() + android.productFlavors.toList()
|
|
11
|
+
def pch = System.getenv("STIM_ANDROID_PCH")
|
|
12
|
+
def ccache = System.getenv("STIM_ANDROID_CCACHE")
|
|
13
|
+
def pchArgument = { arg -> arg =~ /^(?:-D\s*)?CMAKE_DISABLE_PRECOMPILE_HEADERS(?::[^=]+)?=/ }
|
|
14
|
+
if (pch != "auto") {
|
|
15
|
+
configs.each { config -> config.externalNativeBuild.cmake.arguments.removeAll(pchArgument) }
|
|
16
|
+
}
|
|
17
|
+
if (pch != "auto" || (ccache == "on" && !configs.any { config -> config.externalNativeBuild.cmake.arguments.any(pchArgument) })) {
|
|
18
|
+
android.defaultConfig.externalNativeBuild.cmake.arguments.add("-DCMAKE_DISABLE_PRECOMPILE_HEADERS=" + (pch == "on" ? "OFF" : "ON"))
|
|
19
|
+
}
|
|
20
|
+
if (ccache == "off") {
|
|
21
|
+
android.defaultConfig.externalNativeBuild.cmake.arguments.addAll([
|
|
22
|
+
"-DCMAKE_C_COMPILER_LAUNCHER=",
|
|
23
|
+
"-DCMAKE_CXX_COMPILER_LAUNCHER=",
|
|
24
|
+
"-DCCACHE_FOUND=OFF"
|
|
25
|
+
])
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { randomUUID } = require('node:crypto');
|
|
4
|
+
|
|
5
|
+
function bundleResponseMiddleware(write) {
|
|
6
|
+
return (req, res, next) => {
|
|
7
|
+
let url;
|
|
8
|
+
try {
|
|
9
|
+
url = new URL(req.url, 'http://localhost');
|
|
10
|
+
} catch {
|
|
11
|
+
return next();
|
|
12
|
+
}
|
|
13
|
+
const platform = url.searchParams.get('platform');
|
|
14
|
+
if (req.method !== 'GET' || !/\.bundle\/*$/.test(url.pathname) || !['ios', 'android'].includes(platform)) {
|
|
15
|
+
return next();
|
|
16
|
+
}
|
|
17
|
+
const requestId = randomUUID();
|
|
18
|
+
const emit = (event, statusCode) => {
|
|
19
|
+
try {
|
|
20
|
+
write({
|
|
21
|
+
ts: Date.now(),
|
|
22
|
+
src: 'metro',
|
|
23
|
+
level: event === 'bundle_response_failed' ? 'error' : 'debug',
|
|
24
|
+
event,
|
|
25
|
+
platform,
|
|
26
|
+
requestId,
|
|
27
|
+
statusCode,
|
|
28
|
+
msg: `${platform} bundle response ${event.slice('bundle_response_'.length)}`,
|
|
29
|
+
});
|
|
30
|
+
} catch {}
|
|
31
|
+
};
|
|
32
|
+
emit('bundle_response_started');
|
|
33
|
+
let ended = false;
|
|
34
|
+
const finish = (complete) => {
|
|
35
|
+
if (ended) return;
|
|
36
|
+
ended = true;
|
|
37
|
+
const success = complete && (res.statusCode === 200 || res.statusCode === 304);
|
|
38
|
+
emit(success ? 'bundle_response_finished' : 'bundle_response_failed', res.statusCode);
|
|
39
|
+
};
|
|
40
|
+
res.once('finish', () => finish(true));
|
|
41
|
+
res.once('close', () => finish(false));
|
|
42
|
+
return next();
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { bundleResponseMiddleware };
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { createRequire } = require('node:module');
|
|
7
|
+
const { pathToFileURL } = require('node:url');
|
|
8
|
+
const { bundleResponseMiddleware } = require('./bundle-response.cjs');
|
|
9
|
+
const { applyMetroCacheGeneration } = require('./metro-cache-generation.cjs');
|
|
10
|
+
|
|
11
|
+
const projectRoot = process.env.STIM_PROJECT_ROOT;
|
|
12
|
+
const storeRoot = process.env.STIM_METRO_STORE;
|
|
13
|
+
const priorOverride = process.env.STIM_EXPO_METRO_CONFIG;
|
|
14
|
+
const adapterPath = __filename;
|
|
15
|
+
const STORE_ROOT_TAG = 'stimStoreRoot';
|
|
16
|
+
const OK_PREFIX = 'stim-metro-store: sharing Metro transforms through ';
|
|
17
|
+
|
|
18
|
+
let announced = false;
|
|
19
|
+
let warned = false;
|
|
20
|
+
|
|
21
|
+
function warn(reason) {
|
|
22
|
+
if (warned) return;
|
|
23
|
+
warned = true;
|
|
24
|
+
process.stderr.write(
|
|
25
|
+
`warning: Stim could not share this project's Metro transform cache (${String(reason).replace(/\s+/g, ' ')}); ` +
|
|
26
|
+
'the dev server is running with its normal cache.\n',
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function announce() {
|
|
31
|
+
if (announced) return;
|
|
32
|
+
announced = true;
|
|
33
|
+
process.stderr.write(`${OK_PREFIX}${storeRoot}\n`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isFile(file) {
|
|
37
|
+
try {
|
|
38
|
+
return fs.statSync(file).isFile();
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function packageHasMetro(file) {
|
|
45
|
+
try {
|
|
46
|
+
return Object.prototype.hasOwnProperty.call(JSON.parse(fs.readFileSync(file, 'utf8')), 'metro');
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const searchPlaces = [
|
|
53
|
+
'metro.config.js',
|
|
54
|
+
'metro.config.cjs',
|
|
55
|
+
'metro.config.mjs',
|
|
56
|
+
'metro.config.json',
|
|
57
|
+
'metro.config.ts',
|
|
58
|
+
'metro.config.cts',
|
|
59
|
+
'metro.config.mts',
|
|
60
|
+
path.join('.config', 'metro.js'),
|
|
61
|
+
path.join('.config', 'metro.cjs'),
|
|
62
|
+
path.join('.config', 'metro.mjs'),
|
|
63
|
+
path.join('.config', 'metro.json'),
|
|
64
|
+
path.join('.config', 'metro.ts'),
|
|
65
|
+
path.join('.config', 'metro.cts'),
|
|
66
|
+
path.join('.config', 'metro.mts'),
|
|
67
|
+
'package.json',
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
function findProjectConfig() {
|
|
71
|
+
if (priorOverride) {
|
|
72
|
+
let candidate;
|
|
73
|
+
try {
|
|
74
|
+
candidate = createRequire(path.join(projectRoot, 'package.json')).resolve(priorOverride);
|
|
75
|
+
} catch {
|
|
76
|
+
candidate = path.isAbsolute(priorOverride) ? priorOverride : path.resolve(projectRoot, priorOverride);
|
|
77
|
+
}
|
|
78
|
+
if (path.resolve(candidate) !== path.resolve(adapterPath)) return candidate;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const stop = os.homedir();
|
|
82
|
+
let dir = path.resolve(projectRoot);
|
|
83
|
+
while (true) {
|
|
84
|
+
for (const relative of searchPlaces) {
|
|
85
|
+
const candidate = path.join(dir, relative);
|
|
86
|
+
if (!isFile(candidate)) continue;
|
|
87
|
+
if (relative === 'package.json' && !packageHasMetro(candidate)) continue;
|
|
88
|
+
return candidate;
|
|
89
|
+
}
|
|
90
|
+
if (dir === stop || dir === path.dirname(dir)) return null;
|
|
91
|
+
dir = path.dirname(dir);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function unwrap(file, value) {
|
|
96
|
+
if (file.endsWith(`${path.sep}package.json`)) return value.metro;
|
|
97
|
+
return value && value['__esModule'] ? value.default : value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function loadProjectConfig(file) {
|
|
101
|
+
if (!file) return {};
|
|
102
|
+
try {
|
|
103
|
+
return unwrap(file, require(file));
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (error && (error.code === 'ERR_REQUIRE_ESM' || error.code === 'ERR_UNKNOWN_FILE_EXTENSION')) {
|
|
106
|
+
return import(pathToFileURL(file).href).then((module) => unwrap(file, module));
|
|
107
|
+
}
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function then(value, next) {
|
|
113
|
+
// oxlint-disable-next-line promise/no-callback-in-promise -- Metro accepts synchronous values and promises.
|
|
114
|
+
return value && typeof value.then === 'function' ? value.then(next) : next(value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function storeAtRoot(store) {
|
|
118
|
+
if (!store || typeof store !== 'object') return false;
|
|
119
|
+
return store[STORE_ROOT_TAG] === storeRoot || store['_root'] === storeRoot;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function appendStore(config, defaultConfig) {
|
|
123
|
+
const output = config && typeof config === 'object' ? config : {};
|
|
124
|
+
const configuredStores = output.cacheStores != null ? output.cacheStores : defaultConfig.cacheStores;
|
|
125
|
+
const server = { ...defaultConfig.server, ...output.server };
|
|
126
|
+
const enhanceMiddleware = server.enhanceMiddleware;
|
|
127
|
+
|
|
128
|
+
const observed = applyMetroCacheGeneration(
|
|
129
|
+
{
|
|
130
|
+
...output,
|
|
131
|
+
cacheVersion: output.cacheVersion ?? defaultConfig.cacheVersion,
|
|
132
|
+
server: {
|
|
133
|
+
...server,
|
|
134
|
+
enhanceMiddleware(middleware, metroServer) {
|
|
135
|
+
const enhanced = enhanceMiddleware ? enhanceMiddleware(middleware, metroServer) : middleware;
|
|
136
|
+
const observe = bundleResponseMiddleware((record) =>
|
|
137
|
+
process.stderr.write(`stim-bundle-response: ${JSON.stringify(record)}\n`),
|
|
138
|
+
);
|
|
139
|
+
return (req, res, next) => observe(req, res, () => enhanced(req, res, next));
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
process.env.STIM_METRO_CACHE_GENERATION,
|
|
144
|
+
process.env.STIM_METRO_FILE_MAP,
|
|
145
|
+
);
|
|
146
|
+
if (!storeRoot) return observed;
|
|
147
|
+
return {
|
|
148
|
+
...observed,
|
|
149
|
+
cacheStores(MetroCache) {
|
|
150
|
+
const resolved = typeof configuredStores === 'function' ? configuredStores(MetroCache) : configuredStores;
|
|
151
|
+
const stores = Array.isArray(resolved) ? resolved : [];
|
|
152
|
+
if (stores.some(storeAtRoot)) {
|
|
153
|
+
announce();
|
|
154
|
+
return stores;
|
|
155
|
+
}
|
|
156
|
+
if (!MetroCache || typeof MetroCache.FileStore !== 'function') {
|
|
157
|
+
warn('Metro exposes no FileStore constructor');
|
|
158
|
+
return stores;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const store = new MetroCache.FileStore({ root: storeRoot });
|
|
162
|
+
try {
|
|
163
|
+
Object.defineProperty(store, STORE_ROOT_TAG, { value: storeRoot, configurable: true });
|
|
164
|
+
} catch {}
|
|
165
|
+
announce();
|
|
166
|
+
return [...stores, store];
|
|
167
|
+
} catch (error) {
|
|
168
|
+
warn(error instanceof Error ? error.message : error);
|
|
169
|
+
return stores;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!projectRoot) {
|
|
176
|
+
module.exports = {};
|
|
177
|
+
} else {
|
|
178
|
+
const projectRequire = createRequire(path.join(projectRoot, 'package.json'));
|
|
179
|
+
const expoMetroConfig = projectRequire('expo/metro-config');
|
|
180
|
+
const defaultConfig = expoMetroConfig.getDefaultConfig(projectRoot);
|
|
181
|
+
const projectConfig = loadProjectConfig(findProjectConfig());
|
|
182
|
+
|
|
183
|
+
module.exports = then(defaultConfig, (base) =>
|
|
184
|
+
then(projectConfig, (loaded) =>
|
|
185
|
+
then(typeof loaded === 'function' ? loaded(base) : loaded, (resolved) => appendStore(resolved, base)),
|
|
186
|
+
),
|
|
187
|
+
);
|
|
188
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { createHash } = require('node:crypto');
|
|
4
|
+
const { mkdirSync } = require('node:fs');
|
|
5
|
+
const { join } = require('node:path');
|
|
6
|
+
|
|
7
|
+
function applyMetroCacheGeneration(config, generation, directory) {
|
|
8
|
+
if (!generation) return config;
|
|
9
|
+
const fileMapCacheDirectory = join(directory, generation);
|
|
10
|
+
mkdirSync(fileMapCacheDirectory, { recursive: true });
|
|
11
|
+
return {
|
|
12
|
+
...config,
|
|
13
|
+
cacheVersion: createHash('sha256')
|
|
14
|
+
.update(JSON.stringify([config.cacheVersion ?? '1.0', generation]))
|
|
15
|
+
.digest('hex'),
|
|
16
|
+
fileMapCacheDirectory,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { applyMetroCacheGeneration };
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stim
|
|
3
|
+
description: The React Native / Expo CLI for AI agents. Use when running or building a React Native or Expo app on a simulator, emulator, or device, including expo run:ios, expo run:android, react-native run-ios, react-native run-android, expo start, and Metro; when reading build, launch, redbox, or runtime errors from the app logs; when working in parallel worktrees; or when identifying the correct device for UI interaction.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Stim
|
|
7
|
+
|
|
8
|
+
Before using Stim, load the workflow guide from the installed CLI:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
stim guide agent
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Follow the version-matched instructions it prints.
|