yaver-cli 1.99.28 → 1.99.30

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.
@@ -0,0 +1,29 @@
1
+ # hermesc binaries for yaver-cli
2
+
3
+ Hermes bytecode compiler binaries matched to `react-native@0.81.5`
4
+ (Hermes BC version 96, the version baked into the Yaver container app).
5
+
6
+ ## How this directory is populated
7
+
8
+ - `darwin-arm64/` and `darwin-x64/` ship with the tarball as a macOS
9
+ universal binary. They're small (~6 MB) and cover the vast majority
10
+ of `yaver-push` users.
11
+ - Other platforms (`linux-x64`, `win32-x64`) are installed at
12
+ `npm install -g yaver-cli` time by `src/postinstall.js` → it extracts
13
+ the right binary from the `react-native` npm tarball.
14
+ - `linux-arm64` has no upstream prebuilt. On first push, `bundler.js`
15
+ falls back to `getHermescPathAsync({ allowBuildFromSource: true })`,
16
+ which builds hermesc from the project's own
17
+ `node_modules/react-native/sdks/hermes/` sources via CMake. Takes
18
+ ~3–5 min, cached afterwards.
19
+
20
+ ## How bundler.js resolves the right one
21
+
22
+ 1. Platform-matched cache (this dir, per `<platform>-<arch>` key)
23
+ 2. `~/.yaver/hermesc/<key>/hermesc` (per-user mirror)
24
+ 3. Legacy flat layout (top-level `hermesc` or `hermesc.exe`)
25
+ 4. Project-local `node_modules/react-native/sdks/hermesc/...`
26
+
27
+ Never commit binaries into platform subdirs other than the two
28
+ macOS ones — the installer writes them at install time and
29
+ committing them would make every `git pull` churn large files.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaver-cli",
3
- "version": "1.99.28",
3
+ "version": "1.99.30",
4
4
  "mcpName": "io.github.kivanccakmak/yaver",
5
5
  "description": "Unified npm bootstrap for the Yaver agent, SDK injection, and local-first developer runtime",
6
6
  "bin": {
package/src/bundler.js CHANGED
@@ -2,31 +2,57 @@ const { execSync, execFileSync } = require('child_process');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
  const os = require('os');
5
+ const { findExistingHermesc, ensureHermesc } = require('./hermesc-runtime');
5
6
 
6
7
  function getHermescPath() {
7
8
  const key = `${os.platform()}-${os.arch()}`;
8
9
  const dir = path.join(__dirname, '..', 'hermesc');
9
10
  const ext = os.platform() === 'win32' ? 'hermesc.exe' : 'hermesc';
10
11
 
12
+ // 1. Cache populated by postinstall (or previous on-demand install).
13
+ // Covers the common case without any network access at push time.
14
+ const cached = findExistingHermesc();
15
+ if (cached) return cached;
16
+
17
+ // 2. Legacy/redundant lookup: packages built before the platform-aware
18
+ // install may have raw hermesc subdirs left over, and third-party
19
+ // forks sometimes drop their own binaries into hermesc/ directly.
11
20
  const candidates = [
12
21
  path.join(dir, key, ext),
13
22
  path.join(dir, 'darwin-arm64', ext),
14
23
  path.join(dir, 'linux-x64', ext),
24
+ path.join(dir, ext),
15
25
  ];
16
-
17
26
  const found = candidates.find(p => fs.existsSync(p));
18
- if (!found) {
19
- // Fall back to project's hermesc (react-native ships one)
27
+ if (found) {
28
+ try { fs.chmodSync(found, 0o755); } catch {}
29
+ return found;
30
+ }
31
+
32
+ // 3. Project's own react-native installation (ships hermesc at
33
+ // RN 0.81+). Covers `--ignore-scripts` installs and linux-arm64
34
+ // where we don't have a prebuilt binary.
35
+ const rnHermesc = findRNHermesc();
36
+ if (rnHermesc) return rnHermesc;
37
+
38
+ throw new Error(
39
+ `hermesc not found for ${key}. Run \`npm rebuild yaver-cli\` to re-trigger the platform-aware installer, or ensure react-native is installed locally.`,
40
+ );
41
+ }
42
+
43
+ // Async variant that will provision hermesc on demand if the sync path
44
+ // can't find one. Callers that can `await` (push command, etc.) should
45
+ // prefer this so the first push works even when postinstall was skipped.
46
+ async function getHermescPathAsync({ quiet = true, allowBuildFromSource = false } = {}) {
47
+ try {
48
+ return getHermescPath();
49
+ } catch (_err) {
50
+ const provisioned = await ensureHermesc({ quiet, allowBuildFromSource });
51
+ if (provisioned) return provisioned;
20
52
  const rnHermesc = findRNHermesc();
21
53
  if (rnHermesc) return rnHermesc;
22
-
23
- throw new Error(
24
- `hermesc not found for ${key}. Install yaver-cli hermesc binaries or ensure react-native is installed.`
25
- );
54
+ throw _err;
26
55
  }
27
-
28
- try { fs.chmodSync(found, 0o755); } catch {}
29
- return found;
30
56
  }
31
57
 
32
58
  /** Find hermesc from the project's react-native installation */
@@ -84,7 +110,10 @@ async function bundle({ platform, entryFile, outputDir, dev = false, minify = tr
84
110
  }
85
111
 
86
112
  async function compileHermes({ inputPath, outputPath }) {
87
- const hermesc = getHermescPath();
113
+ // Prefer the async resolver: if the cache is empty (install did
114
+ // --ignore-scripts, or this is linux-arm64 and we need a build-
115
+ // from-source) it'll provision one on demand instead of throwing.
116
+ const hermesc = await getHermescPathAsync({ quiet: false, allowBuildFromSource: true });
88
117
  const tmp = inputPath + '.tmp';
89
118
  fs.renameSync(inputPath, tmp);
90
119
 
@@ -112,4 +141,10 @@ function readBytecodeVersion(hbcPath) {
112
141
  return buf.readUInt32LE(8);
113
142
  }
114
143
 
115
- module.exports = { bundle, compileHermes, readBytecodeVersion, getHermescPath };
144
+ module.exports = {
145
+ bundle,
146
+ compileHermes,
147
+ readBytecodeVersion,
148
+ getHermescPath,
149
+ getHermescPathAsync,
150
+ };
@@ -0,0 +1,330 @@
1
+ // Platform-aware hermesc provisioner for yaver-cli.
2
+ //
3
+ // Runs from `postinstall` on global installs and on-demand from the
4
+ // bundler. Avoids shipping a ~6 MB binary inside the tarball that's
5
+ // only correct for one platform — instead we detect the host and
6
+ // install a matching hermesc at install time.
7
+ //
8
+ // Lookup order (redundant by design; any one succeeding is enough):
9
+ // 1. Cached install under node_modules/yaver-cli/hermesc/<key>/hermesc
10
+ // 2. Per-user cache under ~/.yaver/hermesc/<key>/hermesc
11
+ // 3. Download from the react-native npm tarball and extract just
12
+ // sdks/hermesc/<rn-dir>/hermesc for this platform
13
+ // 4. Build from the project's node_modules/react-native hermes
14
+ // sources (handles linux-arm64 where no prebuilt exists)
15
+ // 5. Fall through — bundler.js then resolves from project-local RN
16
+ //
17
+ // Must never block `npm install`. Every failure is logged + swallowed;
18
+ // the CLI still installs fine, the bundler just takes the runtime
19
+ // fallback path the next time someone actually tries to push a bundle.
20
+
21
+ const fs = require("fs");
22
+ const os = require("os");
23
+ const path = require("path");
24
+ const https = require("https");
25
+ const { spawnSync } = require("child_process");
26
+
27
+ const HERMES_RN_VERSION = process.env.YAVER_HERMES_RN_VERSION || "0.81.5";
28
+
29
+ // Maps node's platform-arch to the subdirectory react-native ships its
30
+ // prebuilt hermesc under in the npm tarball (sdks/hermesc/<dir>/hermesc).
31
+ // react-native 0.81.x does NOT ship a prebuilt for linux-arm64 —
32
+ // those machines build from source instead (see buildFromProject()).
33
+ const RN_PREBUILT_DIR = {
34
+ "darwin-arm64": "osx-bin",
35
+ "darwin-x64": "osx-bin",
36
+ "linux-x64": "linux64-bin",
37
+ "win32-x64": "win64-bin",
38
+ };
39
+
40
+ function platformKey() {
41
+ return `${process.platform}-${process.arch}`;
42
+ }
43
+
44
+ function hermescBasename() {
45
+ return process.platform === "win32" ? "hermesc.exe" : "hermesc";
46
+ }
47
+
48
+ function cliInstallDir() {
49
+ return path.join(__dirname, "..", "hermesc", platformKey());
50
+ }
51
+
52
+ function cliInstallPath() {
53
+ return path.join(cliInstallDir(), hermescBasename());
54
+ }
55
+
56
+ function userCacheDir() {
57
+ return path.join(os.homedir(), ".yaver", "hermesc", platformKey());
58
+ }
59
+
60
+ function userCachePath() {
61
+ return path.join(userCacheDir(), hermescBasename());
62
+ }
63
+
64
+ function hermescBinaryRunnable(binaryPath) {
65
+ try {
66
+ const res = spawnSync(binaryPath, ["--version"], {
67
+ stdio: ["ignore", "pipe", "pipe"],
68
+ timeout: 5000,
69
+ });
70
+ if (res.status !== 0) return false;
71
+ const out = (res.stdout?.toString() || "") + (res.stderr?.toString() || "");
72
+ return /hermes/i.test(out) || /bytecode/i.test(out);
73
+ } catch (_err) {
74
+ return false;
75
+ }
76
+ }
77
+
78
+ function chmodExec(binaryPath) {
79
+ if (process.platform === "win32") return;
80
+ try {
81
+ fs.chmodSync(binaryPath, 0o755);
82
+ } catch (_err) {
83
+ /* ignore */
84
+ }
85
+ }
86
+
87
+ function log(message, { quiet }) {
88
+ if (quiet) return;
89
+ console.error(`[yaver hermesc] ${message}`);
90
+ }
91
+
92
+ async function downloadToFile(url, outPath, { maxRedirects = 5 } = {}) {
93
+ return new Promise((resolve, reject) => {
94
+ const fetch = (u, redirectsLeft) => {
95
+ const req = https.get(u, (res) => {
96
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
97
+ if (redirectsLeft <= 0) {
98
+ reject(new Error(`too many redirects for ${url}`));
99
+ return;
100
+ }
101
+ res.resume();
102
+ const next = new URL(res.headers.location, u).toString();
103
+ fetch(next, redirectsLeft - 1);
104
+ return;
105
+ }
106
+ if (res.statusCode !== 200) {
107
+ reject(new Error(`HTTP ${res.statusCode} for ${u}`));
108
+ res.resume();
109
+ return;
110
+ }
111
+ const out = fs.createWriteStream(outPath);
112
+ res.pipe(out);
113
+ out.on("finish", () => out.close(() => resolve()));
114
+ out.on("error", (err) => {
115
+ try {
116
+ fs.rmSync(outPath, { force: true });
117
+ } catch (_err) {
118
+ /* ignore */
119
+ }
120
+ reject(err);
121
+ });
122
+ });
123
+ req.on("error", reject);
124
+ req.setTimeout(60000, () => {
125
+ req.destroy(new Error(`download timed out: ${u}`));
126
+ });
127
+ };
128
+ fetch(url, maxRedirects);
129
+ });
130
+ }
131
+
132
+ async function downloadFromRNTarball(destPath, { quiet }) {
133
+ const rnDir = RN_PREBUILT_DIR[platformKey()];
134
+ if (!rnDir) {
135
+ throw new Error(`no react-native prebuilt for ${platformKey()}`);
136
+ }
137
+
138
+ // Redundant registries — npm.js primary, unpkg as fallback CDN.
139
+ const candidates = [
140
+ `https://registry.npmjs.org/react-native/-/react-native-${HERMES_RN_VERSION}.tgz`,
141
+ `https://unpkg.com/react-native@${HERMES_RN_VERSION}/react-native-${HERMES_RN_VERSION}.tgz`,
142
+ ];
143
+
144
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "yaver-hermesc-"));
145
+ const tarball = path.join(tmpRoot, "rn.tgz");
146
+
147
+ let lastErr = null;
148
+ for (const url of candidates) {
149
+ try {
150
+ log(`fetching ${url}`, { quiet });
151
+ await downloadToFile(url, tarball);
152
+ lastErr = null;
153
+ break;
154
+ } catch (err) {
155
+ lastErr = err;
156
+ log(`download failed: ${err.message}`, { quiet });
157
+ try {
158
+ fs.rmSync(tarball, { force: true });
159
+ } catch (_) {
160
+ /* ignore */
161
+ }
162
+ }
163
+ }
164
+ if (lastErr) {
165
+ try {
166
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
167
+ } catch (_) {
168
+ /* ignore */
169
+ }
170
+ throw lastErr;
171
+ }
172
+
173
+ const rel = `package/sdks/hermesc/${rnDir}/${hermescBasename()}`;
174
+ const res = spawnSync("tar", ["-xzf", tarball, "-C", tmpRoot, rel], {
175
+ stdio: ["ignore", "pipe", "pipe"],
176
+ });
177
+ if (res.status !== 0) {
178
+ const stderr = res.stderr?.toString() || "unknown tar failure";
179
+ try {
180
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
181
+ } catch (_) {
182
+ /* ignore */
183
+ }
184
+ throw new Error(`tar extract failed: ${stderr}`);
185
+ }
186
+
187
+ const extracted = path.join(tmpRoot, rel);
188
+ if (!fs.existsSync(extracted)) {
189
+ try {
190
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
191
+ } catch (_) {
192
+ /* ignore */
193
+ }
194
+ throw new Error(`extracted hermesc missing: ${extracted}`);
195
+ }
196
+
197
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
198
+ fs.copyFileSync(extracted, destPath);
199
+ chmodExec(destPath);
200
+
201
+ try {
202
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
203
+ } catch (_) {
204
+ /* ignore */
205
+ }
206
+ }
207
+
208
+ // Build hermesc from the project's own node_modules/react-native
209
+ // sources. Covers linux-arm64 and any other platform where we don't
210
+ // have a prebuilt. Mirrors desktop/agent/hermesc_resolver.go's
211
+ // buildProjectHermesc path.
212
+ function buildFromProject(destPath, { quiet }) {
213
+ const projectRoots = [
214
+ process.cwd(),
215
+ path.join(process.cwd(), ".."),
216
+ ];
217
+ for (const root of projectRoots) {
218
+ const hermesSrc = path.join(
219
+ root,
220
+ "node_modules",
221
+ "react-native",
222
+ "sdks",
223
+ "hermes",
224
+ );
225
+ if (!fs.existsSync(path.join(hermesSrc, "CMakeLists.txt"))) continue;
226
+ try {
227
+ log(`building hermesc from ${hermesSrc} (one-time, ~3–5 min)…`, { quiet });
228
+ const buildDir = fs.mkdtempSync(path.join(os.tmpdir(), "hermes-build-"));
229
+ const cmake = spawnSync(
230
+ "cmake",
231
+ ["-S", hermesSrc, "-B", buildDir, "-DCMAKE_BUILD_TYPE=Release"],
232
+ { stdio: quiet ? "ignore" : "inherit" },
233
+ );
234
+ if (cmake.status !== 0) throw new Error(`cmake configure failed`);
235
+ const build = spawnSync(
236
+ "cmake",
237
+ ["--build", buildDir, "--target", "hermesc", "--config", "Release", "-j"],
238
+ { stdio: quiet ? "ignore" : "inherit" },
239
+ );
240
+ if (build.status !== 0) throw new Error(`cmake build failed`);
241
+ const built = path.join(buildDir, "bin", hermescBasename());
242
+ if (!fs.existsSync(built)) throw new Error(`hermesc missing after build: ${built}`);
243
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
244
+ fs.copyFileSync(built, destPath);
245
+ chmodExec(destPath);
246
+ try {
247
+ fs.rmSync(buildDir, { recursive: true, force: true });
248
+ } catch (_) {
249
+ /* ignore */
250
+ }
251
+ return true;
252
+ } catch (err) {
253
+ log(`build-from-source failed at ${hermesSrc}: ${err.message}`, { quiet });
254
+ }
255
+ }
256
+ return false;
257
+ }
258
+
259
+ function findExistingHermesc() {
260
+ const candidates = [cliInstallPath(), userCachePath()];
261
+ for (const p of candidates) {
262
+ if (fs.existsSync(p)) {
263
+ chmodExec(p);
264
+ if (hermescBinaryRunnable(p)) return p;
265
+ }
266
+ }
267
+ return null;
268
+ }
269
+
270
+ async function ensureHermesc({ quiet = false, allowBuildFromSource = false } = {}) {
271
+ const existing = findExistingHermesc();
272
+ if (existing) return existing;
273
+
274
+ const key = platformKey();
275
+
276
+ if (RN_PREBUILT_DIR[key]) {
277
+ // Prefer cli-local install so `yaver-push` works offline after the
278
+ // first install (user cache also works; we write to both so either
279
+ // path resolves).
280
+ const dest = cliInstallPath();
281
+ try {
282
+ await downloadFromRNTarball(dest, { quiet });
283
+ if (hermescBinaryRunnable(dest)) {
284
+ // Also mirror into ~/.yaver so a later `npm uninstall` + reinstall
285
+ // doesn't re-download. Redundancy is the point.
286
+ try {
287
+ fs.mkdirSync(userCacheDir(), { recursive: true });
288
+ fs.copyFileSync(dest, userCachePath());
289
+ chmodExec(userCachePath());
290
+ } catch (_) {
291
+ /* ignore mirror errors */
292
+ }
293
+ log(`installed at ${dest}`, { quiet });
294
+ return dest;
295
+ }
296
+ log(`downloaded binary at ${dest} did not run — discarding`, { quiet });
297
+ try {
298
+ fs.rmSync(dest, { force: true });
299
+ } catch (_) {
300
+ /* ignore */
301
+ }
302
+ } catch (err) {
303
+ log(`prebuilt download failed: ${err.message}`, { quiet });
304
+ }
305
+ } else {
306
+ log(`no prebuilt available for ${key} — will try build-from-source on demand`, { quiet });
307
+ }
308
+
309
+ if (allowBuildFromSource) {
310
+ const dest = cliInstallPath();
311
+ if (buildFromProject(dest, { quiet }) && hermescBinaryRunnable(dest)) {
312
+ log(`built hermesc from source at ${dest}`, { quiet });
313
+ return dest;
314
+ }
315
+ }
316
+
317
+ // Fall through — caller should rely on bundler.js's project-local RN
318
+ // fallback (node_modules/react-native/sdks/hermesc/...). Return null
319
+ // rather than throwing so postinstall never fails npm install.
320
+ return null;
321
+ }
322
+
323
+ module.exports = {
324
+ ensureHermesc,
325
+ findExistingHermesc,
326
+ cliInstallPath,
327
+ userCachePath,
328
+ platformKey,
329
+ hermescBasename,
330
+ };
@@ -7,6 +7,7 @@
7
7
  // Must NEVER fail npm install. This is best-effort bootstrap only.
8
8
 
9
9
  const { ensureAgentBinary, runAgentCommand } = require("./agent-runtime");
10
+ const { ensureHermesc } = require("./hermesc-runtime");
10
11
  const { execSync } = require("child_process");
11
12
  const fs = require("fs");
12
13
  const os = require("os");
@@ -69,6 +70,71 @@ function installMissingCodingRunners() {
69
70
  }
70
71
  }
71
72
 
73
+ function ensureLinuxRunnerSandboxPackages() {
74
+ try {
75
+ if (process.platform !== "linux") return;
76
+ if (typeof process.geteuid !== "function" || process.geteuid() !== 0) return;
77
+ const missing = [];
78
+ if (!commandExists("bwrap")) missing.push("bubblewrap");
79
+ if (!commandExists("newuidmap")) missing.push("uidmap");
80
+ if (missing.length === 0) return;
81
+ if (!commandExists("apt-get")) {
82
+ log(`Runner sandbox packages missing (${missing.join(", ")}) and no apt-get is available for auto-install.`);
83
+ return;
84
+ }
85
+ execSync("apt-get update -y", { stdio: ["ignore", "ignore", "ignore"] });
86
+ execSync(`DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ${missing.join(" ")}`, {
87
+ stdio: "inherit",
88
+ });
89
+ log(`Installed Linux runner sandbox packages: ${missing.join(", ")}.`);
90
+ } catch (error) {
91
+ log(`Skipping Linux runner sandbox package bootstrap: ${error.message}`);
92
+ }
93
+ }
94
+
95
+ function ensureLinuxRunnerSandboxSupport() {
96
+ try {
97
+ if (process.platform !== "linux") return;
98
+ if (typeof process.geteuid !== "function" || process.geteuid() !== 0) return;
99
+ const confPath = "/etc/sysctl.d/99-yaver-runner-sandbox.conf";
100
+ let body = "kernel.unprivileged_userns_clone=1\nuser.max_user_namespaces=1048576\n";
101
+ if (fs.existsSync("/proc/sys/kernel/apparmor_restrict_unprivileged_userns")) {
102
+ body += "kernel.apparmor_restrict_unprivileged_userns=0\n";
103
+ }
104
+ fs.writeFileSync(confPath, body);
105
+ execSync("sysctl --system", { stdio: ["ignore", "ignore", "ignore"] });
106
+ log("Enabled Linux user-namespace prerequisites for Codex/runner sandboxes.");
107
+ } catch (error) {
108
+ log(`Skipping Linux runner sandbox bootstrap: ${error.message}`);
109
+ }
110
+ }
111
+
112
+ function reportLinuxRunnerSandboxStatus() {
113
+ try {
114
+ if (process.platform !== "linux") return;
115
+ const issues = [];
116
+ if (!commandExists("bwrap")) issues.push("bubblewrap");
117
+ if (!commandExists("newuidmap")) issues.push("uidmap");
118
+ try {
119
+ const userns = fs.readFileSync("/proc/sys/kernel/unprivileged_userns_clone", "utf8").trim();
120
+ if (userns === "0") issues.push("kernel.unprivileged_userns_clone=0");
121
+ } catch (_) {}
122
+ try {
123
+ const maxUserns = fs.readFileSync("/proc/sys/user/max_user_namespaces", "utf8").trim();
124
+ if (!maxUserns || maxUserns === "0") issues.push("user.max_user_namespaces=0");
125
+ } catch (_) {}
126
+ try {
127
+ const apparmor = fs.readFileSync("/proc/sys/kernel/apparmor_restrict_unprivileged_userns", "utf8").trim();
128
+ if (apparmor === "1") issues.push("kernel.apparmor_restrict_unprivileged_userns=1");
129
+ } catch (_) {}
130
+ if (issues.length > 0) {
131
+ log(`Linux runner sandbox still has blockers: ${issues.join(", ")}. Yaver will mark Codex blocked until the host allows it.`);
132
+ }
133
+ } catch (_) {
134
+ // Best-effort only.
135
+ }
136
+ }
137
+
72
138
  function installMissingMobileTools() {
73
139
  const missing = MOBILE_TOOL_BOOTSTRAP.filter((entry) => !commandExists(entry.command));
74
140
  if (missing.length === 0) {
@@ -157,7 +223,26 @@ async function main() {
157
223
  return;
158
224
  }
159
225
 
226
+ // Platform-aware hermesc provisioning. Downloads the binary matching
227
+ // this host from the react-native npm tarball, caches it under the
228
+ // CLI install dir + ~/.yaver so `yaver-push` works offline after.
229
+ // Never blocks install — the bundler falls back to project-local RN
230
+ // if this step skipped or failed.
231
+ try {
232
+ const hermescPath = await ensureHermesc({ quiet: true });
233
+ if (hermescPath) {
234
+ log(`Hermes compiler ready at ${hermescPath}.`);
235
+ } else {
236
+ log(`Hermes compiler not pre-provisioned for ${process.platform}-${process.arch}; bundler will resolve project-local hermesc at push time.`);
237
+ }
238
+ } catch (error) {
239
+ log(`Skipping hermesc install: ${error.message}`);
240
+ }
241
+
160
242
  ensurePathOnUnix();
243
+ ensureLinuxRunnerSandboxPackages();
244
+ ensureLinuxRunnerSandboxSupport();
245
+ reportLinuxRunnerSandboxStatus();
161
246
 
162
247
  if (process.platform !== "linux" && process.platform !== "darwin") {
163
248
  return;
File without changes