rnxsim 0.1.408 → 0.1.409

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.
Files changed (69) hide show
  1. package/README.md +11 -10
  2. package/cli/bin.ts +16 -643
  3. package/cli/commands/assert.ts +6 -6
  4. package/cli/commands/box/rnx-command.ts +3 -11
  5. package/cli/commands/control.ts +3 -9
  6. package/cli/commands/daemon-mac-app.ts +5 -11
  7. package/cli/commands/daemon.ts +63 -113
  8. package/cli/drivers/playwright-provisioning.ts +33 -29
  9. package/cli/drivers/playwright-sim-host.ts +501 -0
  10. package/cli/drivers/playwright.ts +35 -522
  11. package/cli/hints.ts +2 -4
  12. package/cli/internal-child.ts +176 -0
  13. package/cli/maestro-js.ts +28 -39
  14. package/cli/main.ts +645 -0
  15. package/cli/outbound-endpoints.ts +8 -0
  16. package/cli/self-invocation.ts +34 -0
  17. package/dist-lib/agent-daemon-client.cjs +35 -25
  18. package/dist-lib/agent-events.cjs +1 -1
  19. package/dist-lib/agent-identity.cjs +1 -1
  20. package/dist-lib/agent-sessions.cjs +35 -25
  21. package/dist-lib/attached-projects.cjs +1 -1
  22. package/dist-lib/auth/shared-session.cjs +1 -1
  23. package/dist-lib/backend-origin.cjs +1 -1
  24. package/dist-lib/beta.cjs +1 -1
  25. package/dist-lib/beta.mjs +1 -1
  26. package/dist-lib/bridge-constants.cjs +1 -1
  27. package/dist-lib/bridge-contract-input.cjs +1 -1
  28. package/dist-lib/bridge-contract-input.mjs +1 -1
  29. package/dist-lib/bridge-contract.cjs +1 -1
  30. package/dist-lib/bridge-contract.mjs +1 -1
  31. package/dist-lib/capture-contract.cjs +1 -1
  32. package/dist-lib/capture-contract.mjs +1 -1
  33. package/dist-lib/cli-constants.cjs +1 -1
  34. package/dist-lib/cloud-contract.cjs +1 -4
  35. package/dist-lib/cloud-contract.mjs +1 -3
  36. package/dist-lib/config.cjs +1 -1
  37. package/dist-lib/detox/index.cjs +1 -1
  38. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  39. package/dist-lib/home-paths.cjs +1 -1
  40. package/dist-lib/host/bridge-host.cjs +25 -15
  41. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  42. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  43. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  44. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  45. package/dist-lib/host/websocket-proxy.cjs +1 -1
  46. package/dist-lib/index.cjs +1 -1
  47. package/dist-lib/jump-to-source-babel.cjs +1 -1
  48. package/dist-lib/menu.cjs +1 -1
  49. package/dist-lib/menu.mjs +1 -1
  50. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  51. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  52. package/dist-lib/metro-production-bundle.cjs +1 -1
  53. package/dist-lib/metro-production-bundle.mjs +1 -1
  54. package/dist-lib/metro.cjs +1 -1
  55. package/dist-lib/profiles.cjs +1 -1
  56. package/dist-lib/public-brand.cjs +1 -1
  57. package/dist-lib/react-native-host-modules.cjs +1 -1
  58. package/dist-lib/react-native-host-modules.mjs +1 -1
  59. package/dist-lib/render-mode.cjs +1 -1
  60. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  61. package/dist-lib/sdk.cjs +1 -1
  62. package/dist-lib/sdk.mjs +1 -1
  63. package/dist-lib/skills.cjs +134 -608
  64. package/dist-lib/vite.cjs +1 -1
  65. package/package.json +1 -1
  66. package/src/agent-daemon-client.ts +2 -2
  67. package/src/agent-sessions.ts +29 -24
  68. package/src/cloud-contract.ts +0 -1
  69. package/src/vite-plugin.ts +62 -0
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.408 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.409 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -16769,6 +16769,32 @@ var init_runtime_delivery = __esm({
16769
16769
  }
16770
16770
  });
16771
16771
 
16772
+ // cli/standalone.ts
16773
+ var IS_STANDALONE;
16774
+ var init_standalone = __esm({
16775
+ "cli/standalone.ts"() {
16776
+ "use strict";
16777
+ IS_STANDALONE = typeof __SOOTSIM_STANDALONE__ !== "undefined" && __SOOTSIM_STANDALONE__;
16778
+ }
16779
+ });
16780
+
16781
+ // cli/self-invocation.ts
16782
+ function rnxSelfInvocation() {
16783
+ if (IS_STANDALONE) return { executable: process.execPath, prefixArgs: [] };
16784
+ const entry = process.argv[1];
16785
+ if (!entry) {
16786
+ throw new Error(`could not locate the ${rnxPublicBrand.commandName} entry script`);
16787
+ }
16788
+ return { executable: process.execPath, prefixArgs: [entry] };
16789
+ }
16790
+ var init_self_invocation = __esm({
16791
+ "cli/self-invocation.ts"() {
16792
+ "use strict";
16793
+ init_public_brand();
16794
+ init_standalone();
16795
+ }
16796
+ });
16797
+
16772
16798
  // src/agent-events.ts
16773
16799
  function isAgentEvent(value) {
16774
16800
  if (!value || typeof value !== "object") return false;
@@ -17129,8 +17155,9 @@ function pidIsAlive(pid, sessionId) {
17129
17155
  }
17130
17156
  function resolveSootsimInvocation() {
17131
17157
  if (process.env.RNX_BIN) {
17132
- return { cmd: process.env.RNX_BIN, prefixArgs: [] };
17158
+ return { executable: process.env.RNX_BIN, prefixArgs: [] };
17133
17159
  }
17160
+ if (IS_STANDALONE) return rnxSelfInvocation();
17134
17161
  if (process.versions.electron) {
17135
17162
  const resourcesPath = process.resourcesPath;
17136
17163
  if (resourcesPath) {
@@ -17139,23 +17166,19 @@ function resolveSootsimInvocation() {
17139
17166
  import_node_path8.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
17140
17167
  ];
17141
17168
  for (const c of candidates) {
17142
- if (import_node_fs8.default.existsSync(c)) return { cmd: c, prefixArgs: [] };
17169
+ if (import_node_fs8.default.existsSync(c)) return { executable: c, prefixArgs: [] };
17143
17170
  }
17144
17171
  }
17145
17172
  }
17146
17173
  const workspace = tryWorkspaceSootsim();
17147
17174
  if (workspace) return workspace;
17148
- const argv0 = process.argv[0];
17149
17175
  const argv1 = process.argv[1];
17150
17176
  if (argv1 && /\.(ts|tsx|mjs|cjs|js)$/.test(argv1)) {
17151
- return { cmd: argv0, prefixArgs: [argv1] };
17152
- }
17153
- if (!argv1 || argv1.includes("/.bin/")) {
17154
- throw new Error(
17155
- "rnx CLI not found. set RNX_BIN to the path of the rnx binary, or build the workspace CLI via `bun run --cwd packages/sootsim build:cli`."
17156
- );
17177
+ return { executable: process.argv[0], prefixArgs: [argv1] };
17157
17178
  }
17158
- return { cmd: argv0, prefixArgs: [] };
17179
+ throw new Error(
17180
+ "rnx CLI not found. set RNX_BIN to the path of the rnx binary, or build the workspace CLI via `bun run --cwd packages/sootsim build:cli`."
17181
+ );
17159
17182
  }
17160
17183
  function tryWorkspaceSootsim() {
17161
17184
  try {
@@ -17163,7 +17186,7 @@ function tryWorkspaceSootsim() {
17163
17186
  if (!sootsimDir) return null;
17164
17187
  const binaryName = `rnx-${process.platform === "win32" ? "windows" : process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`;
17165
17188
  const distBinary = import_node_path8.default.join(sootsimDir, "dist-bin", binaryName);
17166
- if (import_node_fs8.default.existsSync(distBinary)) return { cmd: distBinary, prefixArgs: [] };
17189
+ if (import_node_fs8.default.existsSync(distBinary)) return { executable: distBinary, prefixArgs: [] };
17167
17190
  const distBin = import_node_path8.default.join(sootsimDir, "dist-cli", "bin.js");
17168
17191
  if (import_node_fs8.default.existsSync(distBin)) {
17169
17192
  try {
@@ -17179,7 +17202,7 @@ function tryWorkspaceSootsim() {
17179
17202
  }
17180
17203
  } catch {
17181
17204
  }
17182
- return { cmd: process.execPath, prefixArgs: [distBin] };
17205
+ return { executable: process.execPath, prefixArgs: [distBin] };
17183
17206
  }
17184
17207
  return null;
17185
17208
  } catch {
@@ -17341,7 +17364,7 @@ async function startSession(opts) {
17341
17364
  import_node_fs8.default.chmodSync(transcriptDir, 448);
17342
17365
  } catch {
17343
17366
  }
17344
- const { cmd, prefixArgs } = resolveSootsimInvocation();
17367
+ const { executable, prefixArgs } = resolveSootsimInvocation();
17345
17368
  const wrapperArgs = [
17346
17369
  ...prefixArgs,
17347
17370
  "agent-wrapper",
@@ -17366,7 +17389,7 @@ async function startSession(opts) {
17366
17389
  if (claudeSessionUuid) {
17367
17390
  wrapperArgs.push("--claude-session-uuid", claudeSessionUuid);
17368
17391
  }
17369
- const child = (0, import_node_child_process3.spawn)(cmd, wrapperArgs, {
17392
+ const child = (0, import_node_child_process3.spawn)(executable, wrapperArgs, {
17370
17393
  detached: true,
17371
17394
  stdio: "ignore",
17372
17395
  env: {
@@ -17527,6 +17550,8 @@ var init_agent_sessions = __esm({
17527
17550
  import_node_fs8 = __toESM(require("node:fs"), 1);
17528
17551
  import_node_path8 = __toESM(require("node:path"), 1);
17529
17552
  import_node_readline = __toESM(require("node:readline"), 1);
17553
+ init_self_invocation();
17554
+ init_standalone();
17530
17555
  init_agent_events();
17531
17556
  init_agent_prompt();
17532
17557
  init_attached_projects();
@@ -24220,22 +24245,18 @@ var init_electron = __esm({
24220
24245
  }
24221
24246
  });
24222
24247
 
24223
- // src/browser-cache.ts
24224
- var DISPOSABLE_BROWSER_CACHE_DIR_NAMES, disposableBrowserCacheDirs;
24225
- var init_browser_cache = __esm({
24226
- "src/browser-cache.ts"() {
24248
+ // cli/internal-child.ts
24249
+ var RNX_INTERNAL_COMMAND, RNX_INTERNAL_CHILDREN;
24250
+ var init_internal_child = __esm({
24251
+ "cli/internal-child.ts"() {
24227
24252
  "use strict";
24228
- DISPOSABLE_BROWSER_CACHE_DIR_NAMES = [
24229
- "Cache",
24230
- "Code Cache",
24231
- "DawnCache",
24232
- "DawnGraphiteCache",
24233
- "DawnWebGPUCache",
24234
- "GPUCache",
24235
- "GrShaderCache",
24236
- "ShaderCache"
24237
- ];
24238
- disposableBrowserCacheDirs = new Set(DISPOSABLE_BROWSER_CACHE_DIR_NAMES);
24253
+ RNX_INTERNAL_COMMAND = "__internal";
24254
+ RNX_INTERNAL_CHILDREN = {
24255
+ playwrightProbe: "playwright-probe",
24256
+ playwrightInstall: "playwright-install",
24257
+ playwrightHost: "playwright-host",
24258
+ syncHttp: "sync-http"
24259
+ };
24239
24260
  }
24240
24261
  });
24241
24262
 
@@ -24282,14 +24303,19 @@ function resolvePlaywright() {
24282
24303
  return null;
24283
24304
  }
24284
24305
  function probeBrowser(resolved, env) {
24285
- const probe3 = (0, import_node_child_process6.spawnSync)(process.execPath, ["-e", PLAYWRIGHT_BROWSER_PROBE], {
24286
- env: {
24287
- ...env,
24288
- SOOTSIM_PW_MODULE: resolved.modulePath
24289
- },
24290
- encoding: "utf8",
24291
- timeout: 3e4
24292
- });
24306
+ const self = rnxSelfInvocation();
24307
+ const probe3 = (0, import_node_child_process6.spawnSync)(
24308
+ self.executable,
24309
+ [...self.prefixArgs, RNX_INTERNAL_COMMAND, RNX_INTERNAL_CHILDREN.playwrightProbe],
24310
+ {
24311
+ env: {
24312
+ ...env,
24313
+ SOOTSIM_PW_MODULE: resolved.modulePath
24314
+ },
24315
+ encoding: "utf8",
24316
+ timeout: 3e4
24317
+ }
24318
+ );
24293
24319
  if (probe3.status !== 0) {
24294
24320
  const detail = (probe3.stderr || probe3.error?.message || "browser probe failed").trim();
24295
24321
  return {
@@ -24320,11 +24346,23 @@ function ensurePlaywrightBrowser(resolved, env = process.env) {
24320
24346
  if (before.exists) {
24321
24347
  return { ok: true, executablePath: before.executablePath, installed: false };
24322
24348
  }
24323
- const install = (0, import_node_child_process6.spawnSync)(process.execPath, [resolved.cliPath, "install", "chromium"], {
24324
- env,
24325
- encoding: "utf8",
24326
- timeout: 10 * 6e4
24327
- });
24349
+ const installer = rnxSelfInvocation();
24350
+ const install = (0, import_node_child_process6.spawnSync)(
24351
+ installer.executable,
24352
+ [
24353
+ ...installer.prefixArgs,
24354
+ RNX_INTERNAL_COMMAND,
24355
+ RNX_INTERNAL_CHILDREN.playwrightInstall,
24356
+ resolved.cliPath,
24357
+ "install",
24358
+ "chromium"
24359
+ ],
24360
+ {
24361
+ env,
24362
+ encoding: "utf8",
24363
+ timeout: 10 * 6e4
24364
+ }
24365
+ );
24328
24366
  if (install.status !== 0) {
24329
24367
  const rawDetail = (install.stderr || install.stdout || install.error?.message || "browser install failed").trim();
24330
24368
  const detail = rawDetail.length <= 4e3 ? rawDetail : rawDetail.slice(-4e3);
@@ -24344,7 +24382,7 @@ ${detail}` : ""}`
24344
24382
  }
24345
24383
  return { ok: true, executablePath: after.executablePath, installed: true };
24346
24384
  }
24347
- var import_node_child_process6, import_node_fs16, import_node_module4, import_node_path18, PLAYWRIGHT_BROWSER_PROBE;
24385
+ var import_node_child_process6, import_node_fs16, import_node_module4, import_node_path18;
24348
24386
  var init_playwright_provisioning = __esm({
24349
24387
  "cli/drivers/playwright-provisioning.ts"() {
24350
24388
  "use strict";
@@ -24352,22 +24390,8 @@ var init_playwright_provisioning = __esm({
24352
24390
  import_node_fs16 = require("node:fs");
24353
24391
  import_node_module4 = require("node:module");
24354
24392
  import_node_path18 = require("node:path");
24355
- PLAYWRIGHT_BROWSER_PROBE = `
24356
- const { existsSync } = require('node:fs');
24357
- const modulePath = process.env.SOOTSIM_PW_MODULE;
24358
- try {
24359
- const playwright = require(modulePath);
24360
- const browser = playwright && playwright.chromium;
24361
- if (!browser || typeof browser.executablePath !== 'function') {
24362
- throw new Error('resolved package does not expose browser "chromium"');
24363
- }
24364
- const executablePath = browser.executablePath();
24365
- process.stdout.write(JSON.stringify({ executablePath, exists: existsSync(executablePath) }));
24366
- } catch (error) {
24367
- process.stderr.write(String((error && error.stack) || error));
24368
- process.exit(1);
24369
- }
24370
- `;
24393
+ init_internal_child();
24394
+ init_self_invocation();
24371
24395
  }
24372
24396
  });
24373
24397
 
@@ -24437,26 +24461,31 @@ async function launch2(opts) {
24437
24461
  const connectAckFile = `${errLog}.connected`;
24438
24462
  const errFd = (0, import_fs7.openSync)(errLog, "a");
24439
24463
  try {
24440
- const child = (0, import_child_process6.spawn)(process.execPath, ["-e", PLAYWRIGHT_SIM_HOST], {
24441
- detached: true,
24442
- stdio: ["ignore", "ignore", errFd],
24443
- env: {
24444
- ...playwrightEnv,
24445
- SOOTSIM_PW_MODULE: resolved.modulePath,
24446
- SOOTSIM_PW_URL: opts.url,
24447
- SOOTSIM_PW_BRIDGE_PORT: opts.bridgePort ? String(opts.bridgePort) : "",
24448
- SOOTSIM_PW_HEADLESS: opts.headless ?? true ? "1" : "0",
24449
- SOOTSIM_PW_USERDATADIR: resolveUserDataDir(opts),
24450
- SOOTSIM_PW_CDP_PORT: opts.cdpPort ? String(opts.cdpPort) : "",
24451
- // when the caller passed no viewport, leave the inherited
24452
- // SOOTSIM_PW_VIEWPORT (from playwrightEnv) alone instead of
24453
- // clobbering it with '' — the driver reads it as its own default.
24454
- ...opts.viewport ? { SOOTSIM_PW_VIEWPORT: `${opts.viewport.width}x${opts.viewport.height}` } : {},
24455
- SOOTSIM_PW_CONNECTED_ACK_FILE: connectAckFile,
24456
- SOOTSIM_PW_CONNECT_TIMEOUT_MS: String(opts.connectTimeoutMs ?? 12e4),
24457
- SOOTSIM_PW_OWNER_PID: String(ownerPid)
24464
+ const self = rnxSelfInvocation();
24465
+ const child = (0, import_child_process6.spawn)(
24466
+ self.executable,
24467
+ [...self.prefixArgs, RNX_INTERNAL_COMMAND, RNX_INTERNAL_CHILDREN.playwrightHost],
24468
+ {
24469
+ detached: true,
24470
+ stdio: ["ignore", "ignore", errFd],
24471
+ env: {
24472
+ ...playwrightEnv,
24473
+ SOOTSIM_PW_MODULE: resolved.modulePath,
24474
+ SOOTSIM_PW_URL: opts.url,
24475
+ SOOTSIM_PW_BRIDGE_PORT: opts.bridgePort ? String(opts.bridgePort) : "",
24476
+ SOOTSIM_PW_HEADLESS: opts.headless ?? true ? "1" : "0",
24477
+ SOOTSIM_PW_USERDATADIR: resolveUserDataDir(opts),
24478
+ SOOTSIM_PW_CDP_PORT: opts.cdpPort ? String(opts.cdpPort) : "",
24479
+ // when the caller passed no viewport, leave the inherited
24480
+ // SOOTSIM_PW_VIEWPORT (from playwrightEnv) alone instead of
24481
+ // clobbering it with '' — the driver reads it as its own default.
24482
+ ...opts.viewport ? { SOOTSIM_PW_VIEWPORT: `${opts.viewport.width}x${opts.viewport.height}` } : {},
24483
+ SOOTSIM_PW_CONNECTED_ACK_FILE: connectAckFile,
24484
+ SOOTSIM_PW_CONNECT_TIMEOUT_MS: String(opts.connectTimeoutMs ?? 12e4),
24485
+ SOOTSIM_PW_OWNER_PID: String(ownerPid)
24486
+ }
24458
24487
  }
24459
- });
24488
+ );
24460
24489
  child.unref();
24461
24490
  const earlyExit = await new Promise((resolve7) => {
24462
24491
  const timer = setTimeout(() => resolve7(null), 4e3);
@@ -24495,7 +24524,7 @@ ${hint}` : ""}`
24495
24524
  (0, import_fs7.closeSync)(errFd);
24496
24525
  }
24497
24526
  }
24498
- var import_child_process6, import_fs7, import_os2, import_path6, PLAYWRIGHT_SIM_HOST, playwrightDriver;
24527
+ var import_child_process6, import_fs7, import_os2, import_path6, playwrightDriver;
24499
24528
  var init_playwright = __esm({
24500
24529
  "cli/drivers/playwright.ts"() {
24501
24530
  "use strict";
@@ -24503,497 +24532,11 @@ var init_playwright = __esm({
24503
24532
  import_fs7 = require("fs");
24504
24533
  import_os2 = require("os");
24505
24534
  import_path6 = require("path");
24506
- init_browser_cache();
24507
24535
  init_profiles();
24536
+ init_internal_child();
24508
24537
  init_parent_pid();
24538
+ init_self_invocation();
24509
24539
  init_playwright_provisioning();
24510
- PLAYWRIGHT_SIM_HOST = `
24511
- const modulePath = process.env.SOOTSIM_PW_MODULE;
24512
- const url = process.env.SOOTSIM_PW_URL;
24513
- // the WS bridge port the sim page must register with, injected below as
24514
- // window.__sootsimBridgePort so the page skips its shell-port heuristic (which
24515
- // assumes the default offset and breaks when the dev bridge drifted).
24516
- const bridgePort = process.env.SOOTSIM_PW_BRIDGE_PORT || '';
24517
- const headless = process.env.SOOTSIM_PW_HEADLESS === '1';
24518
- const callerUserDataDir = process.env.SOOTSIM_PW_USERDATADIR || '';
24519
- // pid of the session that launched us (its topmost non-init ancestor). when
24520
- // that process exits we tear Chrome down \u2014 see startOwnerWatch below. it is a
24521
- // required lifetime bound: an invalid value aborts before Chrome launches.
24522
- // SOOTSIM_PW_OWNER_POLL_MS is a test-only override of the 5s poll interval;
24523
- // production never sets it.
24524
- const ownerPid = Number(process.env.SOOTSIM_PW_OWNER_PID || 0);
24525
- const ownerPollMs = Number(process.env.SOOTSIM_PW_OWNER_POLL_MS || 5000);
24526
- // optional CDP port \u2014 exposes Chrome's remote-debugging endpoint so a
24527
- // bridge-driven sim can also be cpu-profiled (rnx perf cpu --cdp-port).
24528
- // playwright keeps its own pipe connection; the port is an additional endpoint.
24529
- const cdpPort = process.env.SOOTSIM_PW_CDP_PORT || '';
24530
- // extra chromium launch args, space-separated. a linux GPU runner injects
24531
- // '--use-angle=vulkan --enable-features=Vulkan' here so chrome binds the
24532
- // real GPU instead of silently falling back to swiftshader software
24533
- // rendering (which pegs cores and produces ~1fps canvas capture).
24534
- const extraChromeArgs = (process.env.SOOTSIM_PW_CHROME_ARGS || '')
24535
- .split(' ')
24536
- .filter(Boolean);
24537
- // Local CanvasKit work must never fall back to Playwright's software
24538
- // renderer. CI may use the bundled browser for non-interactive coverage, but
24539
- // local test drives fail closed unless the hardware-backed test browser is
24540
- // proven after launch.
24541
- const hardwareGpuRequired = process.env.CI !== '1' && process.env.CI !== 'true';
24542
- const softwareRendererPattern = /swiftshader|software(?: rasterizer)?|llvmpipe|lavapipe|softpipe/i;
24543
- // page viewport as '<w>x<h>'. the caller sizes it from the device profile;
24544
- // the fallback is phone-shaped because the page hosts a phone sim \u2014 the
24545
- // playwright default (1280x720 desktop) letterboxes the device frame.
24546
- const viewportSpec = /^(\\d+)x(\\d+)$/.exec(process.env.SOOTSIM_PW_VIEWPORT || '');
24547
- const viewport = viewportSpec
24548
- ? { width: Number(viewportSpec[1]), height: Number(viewportSpec[2]) }
24549
- : { width: 600, height: 900 };
24550
- // device pixels per css pixel. playwright's default is 1, which makes the
24551
- // engine allocate its canvas backing store at HALF the resolution a normal
24552
- // retina-mac chrome would (dpr 2) \u2014 the sim looks visibly soft. default to 2
24553
- // so a driver-launched sim matches what the user sees in their own browser.
24554
- const deviceScaleFactor = Number(process.env.SOOTSIM_PW_DSF || 2);
24555
- const closeTimeoutMs = Number(process.env.SOOTSIM_PW_CLOSE_TIMEOUT_MS || 2500);
24556
- const connectAckFile = process.env.SOOTSIM_PW_CONNECTED_ACK_FILE || '';
24557
- const disposableBrowserCacheDirs = new Set(${JSON.stringify(
24558
- DISPOSABLE_BROWSER_CACHE_DIR_NAMES
24559
- )});
24560
- const { existsSync, mkdtempSync, readdirSync, rmSync, unlinkSync } = require('fs');
24561
- const { execSync } = require('child_process');
24562
- const { tmpdir } = require('os');
24563
- const { join } = require('path');
24564
- // chrome tree reaper \u2014 playwright's browser.close() and even SIGKILLing the
24565
- // root chrome process do NOT reliably take down the helper tree (gpu, renderer,
24566
- // network/storage utilities). they reparent to launchd/init and survive, each
24567
- // one busy enough to peg CPU at 100%+. close() only reaches the root.
24568
- //
24569
- // the prior approach was \`pgrep -f ms-playwright/[c]hromium\`. it never matched
24570
- // system Chrome, so the reaper was a no-op in the common case and every
24571
- // aborted/closed \`rnx open\` leaked a renderer + gpu helper burning 100%+
24572
- // CPU until manually killed (multiple incidents 2026-06).
24573
- //
24574
- // fix: identify our chrome tree by its UNIQUE user-data-dir. every helper a
24575
- // chrome root spawns inherits \`--user-data-dir=<dir>\` in its cmdline, so
24576
- // \`pgrep -f <profileDir>\` finds exactly the tree we launched and nothing else
24577
- // \u2014 Chrome for Testing and bundled playwright chromium alike. as a side benefit we
24578
- // no longer need the launch / launchPersistentContext fork: we ALWAYS use a
24579
- // profile dir (mkdtemp'd if the caller didn't pass one, then removed at exit).
24580
- const reapEnabled = process.platform !== 'win32';
24581
- let profileDir = callerUserDataDir;
24582
- let ephemeralProfile = false;
24583
- if (!profileDir) {
24584
- profileDir = mkdtempSync(join(tmpdir(), 'rnx-playwright-host-'));
24585
- ephemeralProfile = true;
24586
- }
24587
- // browser HTTP caches are acceleration data, not profile identity. give each
24588
- // host a bounded temporary cache so persistent named profiles retain cookies
24589
- // and storage without multiplying a private cache for every profile.
24590
- const diskCacheDir = mkdtempSync(join(tmpdir(), 'rnx-playwright-cache-'));
24591
- const chromePidsByProfile = () => {
24592
- if (!reapEnabled) return [];
24593
- try {
24594
- return execSync('pgrep -f ' + profileDir + ' || true', { stdio: ['ignore', 'pipe', 'ignore'] })
24595
- .toString().split('\\n').filter(Boolean).map((p) => Number(p))
24596
- .filter((p) => p && p !== process.pid);
24597
- } catch { return []; }
24598
- };
24599
- const chromeCommandsByProfile = () => {
24600
- if (!reapEnabled) return [];
24601
- const pids = chromePidsByProfile();
24602
- if (pids.length === 0) return [];
24603
- try {
24604
- return execSync('ps -p ' + pids.join(',') + ' -o command=', {
24605
- stdio: ['ignore', 'pipe', 'ignore'],
24606
- }).toString().split('\\n').filter(Boolean);
24607
- } catch { return []; }
24608
- };
24609
- const assertNoUnsafeSwiftShaderFlag = () => {
24610
- if (!hardwareGpuRequired) return;
24611
- const unsafe = chromeCommandsByProfile().find((command) =>
24612
- command.includes('--enable-unsafe-swiftshader') ||
24613
- /--use-angle=(?:swiftshader|swiftshader-webgl)(?:\\s|$)/i.test(command)
24614
- );
24615
- if (unsafe) {
24616
- throw new Error(
24617
- '[playwright-driver] refused software-rendering Chrome: launched process contains a SwiftShader flag'
24618
- );
24619
- }
24620
- };
24621
- const killTree = (pid) => {
24622
- let kids = [];
24623
- try {
24624
- kids = execSync('pgrep -P ' + pid + ' || true', { stdio: ['ignore', 'pipe', 'ignore'] })
24625
- .toString().split('\\n').filter(Boolean);
24626
- } catch {}
24627
- for (const k of kids) killTree(Number(k));
24628
- try { process.kill(pid, 'SIGKILL'); } catch {}
24629
- };
24630
- const reapChrome = () => { for (const pid of chromePidsByProfile()) killTree(pid); };
24631
- const removeDisposableCaches = (root) => {
24632
- let children = [];
24633
- try { children = readdirSync(root, { withFileTypes: true }); } catch { return; }
24634
- for (const child of children) {
24635
- if (!child.isDirectory() || child.isSymbolicLink()) continue;
24636
- const childPath = join(root, child.name);
24637
- if (disposableBrowserCacheDirs.has(child.name)) {
24638
- try { rmSync(childPath, { recursive: true, force: true }); } catch {}
24639
- continue;
24640
- }
24641
- removeDisposableCaches(childPath);
24642
- }
24643
- };
24644
- const cleanupProfile = () => {
24645
- try { rmSync(diskCacheDir, { recursive: true, force: true }); } catch {}
24646
- if (ephemeralProfile) {
24647
- try { rmSync(profileDir, { recursive: true, force: true }); } catch {}
24648
- return;
24649
- }
24650
- removeDisposableCaches(profileDir);
24651
- };
24652
- (async () => {
24653
- const pw = require(modulePath);
24654
- let context;
24655
- let keepAlive = null;
24656
- let connectTimer = null;
24657
- let connectAckPoll = null;
24658
- let ownerWatch = null;
24659
- let connected = false;
24660
- let shuttingDown = false;
24661
- const clearConnectTimer = () => {
24662
- if (connectTimer) {
24663
- clearTimeout(connectTimer);
24664
- connectTimer = null;
24665
- }
24666
- if (connectAckPoll) {
24667
- clearInterval(connectAckPoll);
24668
- connectAckPoll = null;
24669
- }
24670
- };
24671
- const markConnected = () => {
24672
- connected = true;
24673
- clearConnectTimer();
24674
- };
24675
- const ackFileExists = () => {
24676
- if (!connectAckFile) return false;
24677
- try { return existsSync(connectAckFile); } catch { return false; }
24678
- };
24679
- const bounded = async (label, work, timeoutMs) => {
24680
- let timer = null;
24681
- try {
24682
- const timeout = new Promise((resolve) => {
24683
- timer = setTimeout(() => resolve({ timedOut: true }), timeoutMs);
24684
- });
24685
- if (typeof timer.unref === 'function') timer.unref();
24686
- const result = await Promise.race([
24687
- Promise.resolve().then(work).then(
24688
- () => ({ timedOut: false }),
24689
- (error) => ({ timedOut: false, error }),
24690
- ),
24691
- timeout,
24692
- ]);
24693
- if (result && result.timedOut) {
24694
- process.stderr.write('[shutdown-timeout] ' + label + ' exceeded ' + timeoutMs + 'ms\\n');
24695
- return false;
24696
- }
24697
- if (result && result.error) {
24698
- process.stderr.write('[shutdown-error] ' + label + ': ' + String((result.error && result.error.message) || result.error) + '\\n');
24699
- return false;
24700
- }
24701
- return true;
24702
- } finally {
24703
- if (timer) clearTimeout(timer);
24704
- }
24705
- };
24706
- const browserProcess = () => {
24707
- if (context && typeof context.browser === 'function') {
24708
- try {
24709
- const b = context.browser();
24710
- if (b && typeof b.process === 'function') return b.process();
24711
- } catch {}
24712
- }
24713
- return null;
24714
- };
24715
- const processIsAlive = (proc) => {
24716
- if (!proc || !proc.pid) return false;
24717
- try {
24718
- process.kill(proc.pid, 0);
24719
- return true;
24720
- } catch (err) {
24721
- return err && err.code === 'EPERM';
24722
- }
24723
- };
24724
- const terminateBrowserProcess = async () => {
24725
- const proc = browserProcess();
24726
- if (!proc || !proc.pid || !processIsAlive(proc)) return;
24727
- try { proc.kill('SIGTERM'); } catch {}
24728
- await bounded('browser process exit after SIGTERM', async () => {
24729
- while (processIsAlive(proc)) {
24730
- await new Promise((resolve) => setTimeout(resolve, 50));
24731
- }
24732
- }, 600);
24733
- if (!processIsAlive(proc)) return;
24734
- try { proc.kill('SIGKILL'); } catch {}
24735
- };
24736
- const shutdown = async (code = 0) => {
24737
- if (shuttingDown) return;
24738
- shuttingDown = true;
24739
- clearConnectTimer();
24740
- if (keepAlive) clearInterval(keepAlive);
24741
- if (ownerWatch) clearInterval(ownerWatch);
24742
- const timeoutMs = Number.isFinite(closeTimeoutMs) && closeTimeoutMs > 0 ? closeTimeoutMs : 2500;
24743
- if (context) await bounded('context.close', () => context.close(), timeoutMs);
24744
- await terminateBrowserProcess();
24745
- // close() + root-SIGKILL above handle the graceful case; the helper tree
24746
- // (gpu/renderer/utilities) reparents and survives every one of those, so
24747
- // we always sweep by our unique profile dir as the load-bearing teardown.
24748
- reapChrome();
24749
- cleanupProfile();
24750
- if (connectAckFile) {
24751
- try { unlinkSync(connectAckFile); } catch {}
24752
- }
24753
- process.exit(code);
24754
- };
24755
- process.once('SIGTERM', () => { void shutdown(0); });
24756
- process.once('SIGINT', () => { void shutdown(0); });
24757
- // the launch-failure path at the bottom of this script exits without going
24758
- // through shutdown(); the exit hook (sync sweep) still reaps the tree.
24759
- process.on('exit', () => { reapChrome(); cleanupProfile(); });
24760
- // owner-liveness watchdog \u2014 the load-bearing fix for leaked Chrome hosts.
24761
- // we are spawned detached + unref'd so \`rnx open\` returns immediately,
24762
- // which means nothing in the OS process tree ties Chrome's lifetime to the
24763
- // session that asked for it. once the sim connects, the connect-timeout
24764
- // safety net is cleared, so absent an explicit \`rnx close\` (or the
24765
- // daemon's idle reaper closing the sim socket) the host would hold Chrome
24766
- // open forever after its owner exits. poll the owning session's pid and tear
24767
- // down when it's gone. ESRCH (kill throws) = owner exited; EPERM = alive but
24768
- // not signalable by us, which still counts as alive.
24769
- const startOwnerWatch = () => {
24770
- if (!Number.isInteger(ownerPid) || ownerPid <= 1) {
24771
- throw new Error('[playwright-driver] missing valid owning process; refusing unsupervised browser host');
24772
- }
24773
- const intervalMs = Number.isFinite(ownerPollMs) && ownerPollMs > 0 ? ownerPollMs : 5000;
24774
- ownerWatch = setInterval(() => {
24775
- try {
24776
- process.kill(ownerPid, 0);
24777
- } catch (err) {
24778
- if (err && err.code === 'EPERM') return;
24779
- process.stderr.write('[owner-gone] owning process ' + ownerPid + ' exited; closing playwright host\\n');
24780
- void shutdown(0);
24781
- }
24782
- }, intervalMs);
24783
- if (typeof ownerWatch.unref === 'function') ownerWatch.unref();
24784
- };
24785
- startOwnerWatch();
24786
- // Local rendering uses Playwright's full Chrome for Testing app. On macOS its
24787
- // com.google.chrome.for.testing identity cannot intercept open/quit events for
24788
- // the user's com.google.Chrome browser. CI may use chromium_headless_shell;
24789
- // local rendering fails closed because that shell has no GPU and silently
24790
- // chooses SwiftShader. NOTE: this is NOT enough to make \`rnx perf cpu\`
24791
- // work \u2014 that profiles the tenant *worker*, and the JS Self-Profiler API
24792
- // cannot be constructed in a dedicated worker (see
24793
- // worker-handlers/cpu-profile.ts). cpu-profile needs a CDP-based profiler
24794
- // instead.
24795
- if (
24796
- hardwareGpuRequired &&
24797
- extraChromeArgs.some((arg) =>
24798
- arg === '--enable-unsafe-swiftshader' ||
24799
- /^--use-angle=(?:swiftshader|swiftshader-webgl)$/i.test(arg)
24800
- )
24801
- ) {
24802
- throw new Error('[playwright-driver] refused SwiftShader launch arguments');
24803
- }
24804
- // one browser path per environment: the full, separately identified test app
24805
- // locally; chromium_headless_shell on CI. never launch a branded user browser.
24806
- const channelsToTry = hardwareGpuRequired ? ['chromium'] : [null];
24807
- const launchWith = async (channel) => {
24808
- const opts = channel
24809
- ? { headless, channel, viewport, deviceScaleFactor }
24810
- : { headless, viewport, deviceScaleFactor };
24811
- // playwright injects --force-color-profile=srgb by default, which reads
24812
- // visibly washed-out next to a normal chrome window on a wide-gamut mac
24813
- // display. strip it so driver-launched sims color-match the user's own
24814
- // browser (real incident 2026-07-09: "low res and whitewashed" sim).
24815
- opts.ignoreDefaultArgs = ['--force-color-profile=srgb'];
24816
- // headless chromium backgrounds/occludes the renderer and throttles rAF +
24817
- // timers to ~1fps, which silently starves combined/video frame capture
24818
- // (was 47 frames over 63s -> frozen-looking mp4). keep the render loop at
24819
- // full rate so the recorder captures real motion. document.hidden stays
24820
- // false in headless, so assertTabVisibleForCapture never caught this \u2014 it
24821
- // has to be fixed at the launch flags.
24822
- opts.args = [
24823
- // the compositor renders through skia graphite on webgpu, so a launch
24824
- // without navigator.gpu boots to a fatal instead of a sim.
24825
- '--enable-unsafe-webgpu',
24826
- '--disk-cache-dir=' + diskCacheDir,
24827
- '--disk-cache-size=67108864',
24828
- '--disable-background-timer-throttling',
24829
- '--disable-backgrounding-occluded-windows',
24830
- '--disable-renderer-backgrounding',
24831
- ];
24832
- if (channel) {
24833
- // the full test browser can bind the real platform GPU \u2014 force the native
24834
- // ANGLE backend and strip playwright's --enable-unsafe-swiftshader
24835
- // default arg. without both, headless chrome silently falls back to
24836
- // swiftshader software rendering for the CanvasKit/WebGL canvas and
24837
- // pegs every core. SOOTSIM_PW_CHROME_ARGS is appended later, so an
24838
- // env-injected --use-angle=... still overrides (last flag wins).
24839
- const angle = process.platform === 'darwin' ? 'metal'
24840
- : process.platform === 'linux' ? 'vulkan' : 'default';
24841
- opts.args.unshift(
24842
- '--use-angle=' + angle,
24843
- '--enable-gpu',
24844
- '--enable-gpu-rasterization',
24845
- '--enable-zero-copy',
24846
- '--ignore-gpu-blocklist',
24847
- );
24848
- opts.ignoreDefaultArgs.push('--enable-unsafe-swiftshader');
24849
- } else {
24850
- // playwright's bundled software chromium exposes no navigator.gpu until
24851
- // Vulkan is named explicitly, and webgpu is the only backend there is.
24852
- opts.args.push('--enable-features=Vulkan');
24853
- }
24854
- if (cdpPort) opts.args.push('--remote-debugging-port=' + cdpPort);
24855
- if (extraChromeArgs.length) opts.args.push(...extraChromeArgs);
24856
- // headed: size the OS window to the (phone-shaped) viewport too, plus
24857
- // room for chrome's own toolbar, so the window isn't desktop-shaped
24858
- // around a letterboxed page.
24859
- if (!headless) opts.args.push('--window-size=' + viewport.width + ',' + (viewport.height + 88));
24860
- // ALWAYS persistent: every chrome process inherits --user-data-dir=<dir>
24861
- // in its cmdline, which is how reapChrome() finds the whole tree (root +
24862
- // helpers) on shutdown. without a unique dir there is no way to attribute
24863
- // an orphaned helper back to us, which is the leak we just fixed.
24864
- return await pw.chromium.launchPersistentContext(profileDir, opts);
24865
- };
24866
- let launchErr = null;
24867
- let launchedChannel = null;
24868
- for (const channel of channelsToTry) {
24869
- try {
24870
- context = await launchWith(channel);
24871
- launchedChannel = channel || 'chromium';
24872
- launchErr = null;
24873
- break;
24874
- } catch (err) {
24875
- launchErr = err;
24876
- const msg = err && err.message ? String(err.message) : '';
24877
- const isMissingChannel =
24878
- /not found at/i.test(msg) ||
24879
- /not installed/i.test(msg) ||
24880
- /Chromium distribution/i.test(msg);
24881
- if (!isMissingChannel) throw err; // unexpected failure: bubble up
24882
- if (channel) {
24883
- const next = hardwareGpuRequired
24884
- ? '; bundled Chromium is disabled for local GPU work\\n'
24885
- : ', falling back to bundled chromium\\n';
24886
- process.stderr.write('[playwright-driver] channel "' + channel + '" not available' + next);
24887
- }
24888
- }
24889
- }
24890
- if (launchErr) throw launchErr;
24891
- if (launchedChannel) {
24892
- process.stderr.write('[playwright-driver] launched ' + launchedChannel + '\\n');
24893
- }
24894
- // grant camera up front so the sim's Camera app reaches the host webcam
24895
- // through getUserMedia, mirroring a real iOS simulator where the camera just
24896
- // works. without this the unhandled chrome permission prompt auto-denies and
24897
- // CameraApp renders its "Camera Access Required" placeholder.
24898
- await context.grantPermissions(['camera']);
24899
- const hostMeta = {
24900
- sootsimHostDriver: 'playwright',
24901
- sootsimHostPid: process.pid,
24902
- sootsimHostOwnerPid: ownerPid,
24903
- sootsimHostStartedAt: Date.now(),
24904
- };
24905
- // inject the resolved bridge port (numeric literal) BEFORE the page runs, so
24906
- // the shell/engine talk to the daemon this driver owns. addInitScript runs
24907
- // ahead of every page script, and the vite dev plugin only fills the value in
24908
- // when it is still unset, so this one wins. plain assignment, NOT
24909
- // defineProperty: other injectors also assign to it, and a non-writable
24910
- // property would throw at that assignment and crash boot.
24911
- const bridgePortInject = bridgePort
24912
- ? 'window.__sootsimBridgePort = ' + Number(bridgePort) + ';'
24913
- : '';
24914
- await context.addInitScript({
24915
- content: 'Object.defineProperty(window, "__sootsimHostMeta", { value: ' + JSON.stringify(hostMeta) + ', configurable: true });' + bridgePortInject,
24916
- });
24917
- await context.exposeBinding('__sootsimHostClose', async () => {
24918
- await shutdown(0);
24919
- });
24920
- const connectTimeoutMs = Number(process.env.SOOTSIM_PW_CONNECT_TIMEOUT_MS || 120000);
24921
- if (connectAckFile) {
24922
- connectAckPoll = setInterval(() => {
24923
- if (ackFileExists()) markConnected();
24924
- }, 500);
24925
- if (typeof connectAckPoll.unref === 'function') connectAckPoll.unref();
24926
- }
24927
- connectTimer = setTimeout(() => {
24928
- if (ackFileExists()) {
24929
- markConnected();
24930
- return;
24931
- }
24932
- if (connected) return;
24933
- process.stderr.write('[connect-timeout] sim did not register with the bridge within ' + connectTimeoutMs + 'ms; closing playwright host\\n');
24934
- void shutdown(2);
24935
- }, Number.isFinite(connectTimeoutMs) && connectTimeoutMs > 0 ? connectTimeoutMs : 120000);
24936
- if (typeof connectTimer.unref === 'function') connectTimer.unref();
24937
- const page = await context.newPage();
24938
- if (hardwareGpuRequired) {
24939
- assertNoUnsafeSwiftShaderFlag();
24940
- const gpu = await page.evaluate(() => {
24941
- const canvas = document.createElement('canvas');
24942
- const gl =
24943
- canvas.getContext('webgl2', { powerPreference: 'high-performance' }) ||
24944
- canvas.getContext('webgl', { powerPreference: 'high-performance' });
24945
- if (!gl) return { renderer: null, vendor: null };
24946
- const info = gl.getExtension('WEBGL_debug_renderer_info');
24947
- return {
24948
- renderer: info ? String(gl.getParameter(info.UNMASKED_RENDERER_WEBGL)) : null,
24949
- vendor: info ? String(gl.getParameter(info.UNMASKED_VENDOR_WEBGL)) : null,
24950
- };
24951
- });
24952
- if (!gpu.renderer) {
24953
- throw new Error(
24954
- '[playwright-driver] could not prove a hardware WebGL renderer; refusing local render-heavy launch'
24955
- );
24956
- }
24957
- if (softwareRendererPattern.test(gpu.renderer)) {
24958
- throw new Error('[playwright-driver] refused software WebGL renderer: ' + gpu.renderer);
24959
- }
24960
- assertNoUnsafeSwiftShaderFlag();
24961
- process.stderr.write(
24962
- '[playwright-driver] hardware WebGL renderer: ' + gpu.renderer +
24963
- (gpu.vendor ? ' (' + gpu.vendor + ')' : '') + '\\n'
24964
- );
24965
- }
24966
- // surface page-side failures (engine boot crash, failed WS registration)
24967
- // to stderr so a sim that never connects produces a real diagnostic
24968
- // instead of a silent "timed out waiting for opened sim to connect".
24969
- page.on('pageerror', (e) => {
24970
- process.stderr.write('[pageerror] ' + ((e && e.stack) || e) + '\\n');
24971
- });
24972
- page.on('console', (m) => {
24973
- if (m.type() === 'error') {
24974
- process.stderr.write('[console.error] ' + m.text() + '\\n');
24975
- }
24976
- });
24977
- page.on('crash', () => process.stderr.write('[page] renderer crashed\\n'));
24978
- try {
24979
- await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
24980
- } catch (err) {
24981
- process.stderr.write('[goto] ' + String((err && err.message) || err) + '\\n');
24982
- }
24983
- keepAlive = setInterval(() => {}, 1 << 30);
24984
- // browser disconnect (e.g. user killed chrome from outside) \u2192 tear down.
24985
- // we are persistent-only now, so .browser() is the only handle to it.
24986
- try {
24987
- const b = context.browser && context.browser();
24988
- if (b && typeof b.on === 'function') b.on('disconnected', () => { void shutdown(0); });
24989
- } catch {}
24990
- context.on('close', () => { void shutdown(0); });
24991
- page.on('close', () => { void shutdown(0); });
24992
- })().catch((err) => {
24993
- process.stderr.write(String((err && err.stack) || err) + '\\n');
24994
- process.exit(1);
24995
- });
24996
- `;
24997
24540
  playwrightDriver = {
24998
24541
  id: "playwright",
24999
24542
  name: "playwright",
@@ -25533,15 +25076,8 @@ async function ensureRuntimeForOpen() {
25533
25076
  await runRuntime2(["install"], {});
25534
25077
  }
25535
25078
  function spawnSootsim(args) {
25536
- const entry = process.argv[1];
25537
- if (entry && import_node_fs17.default.existsSync(entry)) {
25538
- return (0, import_node_child_process7.spawn)(process.execPath, [entry, ...args], {
25539
- detached: true,
25540
- stdio: "ignore",
25541
- env: process.env
25542
- });
25543
- }
25544
- return (0, import_node_child_process7.spawn)(rnxPublicBrand.commandName, args, {
25079
+ const { executable, prefixArgs } = rnxSelfInvocation();
25080
+ return (0, import_node_child_process7.spawn)(executable, [...prefixArgs, ...args], {
25545
25081
  detached: true,
25546
25082
  stdio: "ignore",
25547
25083
  env: process.env
@@ -26962,6 +26498,7 @@ var init_control = __esm({
26962
26498
  init_registry2();
26963
26499
  init_open_url2();
26964
26500
  init_run_rnx();
26501
+ init_self_invocation();
26965
26502
  init_ws_bridge();
26966
26503
  init_core();
26967
26504
  init_env();
@@ -27571,13 +27108,23 @@ function executeSyncHttp(url, method, params) {
27571
27108
  delete childEnv[key];
27572
27109
  }
27573
27110
  }
27574
- const child = (0, import_child_process7.spawnSync)(process.execPath, ["-e", HTTP_CHILD_SRC], {
27575
- input: JSON.stringify({ url, method, headers: params?.headers, body: params?.body }),
27576
- encoding: "utf8",
27577
- timeout: HTTP_TIMEOUT_MS,
27578
- maxBuffer: 64 * 1024 * 1024,
27579
- env: childEnv
27580
- });
27111
+ const self = rnxSelfInvocation();
27112
+ const child = (0, import_child_process7.spawnSync)(
27113
+ self.executable,
27114
+ [...self.prefixArgs, RNX_INTERNAL_COMMAND, RNX_INTERNAL_CHILDREN.syncHttp],
27115
+ {
27116
+ input: JSON.stringify({
27117
+ url,
27118
+ method,
27119
+ headers: params?.headers,
27120
+ body: params?.body
27121
+ }),
27122
+ encoding: "utf8",
27123
+ timeout: HTTP_TIMEOUT_MS,
27124
+ maxBuffer: 64 * 1024 * 1024,
27125
+ env: childEnv
27126
+ }
27127
+ );
27581
27128
  if (child.error) {
27582
27129
  throw new Error(`http ${method} ${url} failed: ${child.error.message}`);
27583
27130
  }
@@ -27618,36 +27165,15 @@ function scriptConditionIsTruthy(value) {
27618
27165
  if (!Number.isNaN(num) && num === 0) return false;
27619
27166
  return true;
27620
27167
  }
27621
- var import_child_process7, vm, HTTP_TIMEOUT_MS, HTTP_CHILD_SRC, TEMPLATE_RE, ESCAPED_TEMPLATE_RE, BARE_IDENTIFIER_RE, MaestroJsContext;
27168
+ var import_child_process7, vm, HTTP_TIMEOUT_MS, TEMPLATE_RE, ESCAPED_TEMPLATE_RE, BARE_IDENTIFIER_RE, MaestroJsContext;
27622
27169
  var init_maestro_js = __esm({
27623
27170
  "cli/maestro-js.ts"() {
27624
27171
  "use strict";
27625
27172
  import_child_process7 = require("child_process");
27626
27173
  vm = __toESM(require("vm"), 1);
27174
+ init_internal_child();
27175
+ init_self_invocation();
27627
27176
  HTTP_TIMEOUT_MS = 3e5;
27628
- HTTP_CHILD_SRC = `
27629
- let input = '';
27630
- process.stdin.on('data', (c) => { input += c });
27631
- process.stdin.on('end', async () => {
27632
- try {
27633
- const req = JSON.parse(input);
27634
- const res = await fetch(req.url, {
27635
- method: req.method,
27636
- headers: req.headers,
27637
- body: req.body === undefined || req.body === null ? undefined : req.body,
27638
- });
27639
- const body = await res.text();
27640
- const headers = {};
27641
- for (const [k, v] of res.headers.entries()) {
27642
- headers[k] = headers[k] ? headers[k] + ',' + v : v;
27643
- }
27644
- process.stdout.write(JSON.stringify({ ok: res.ok, status: res.status, body, headers }));
27645
- } catch (err) {
27646
- process.stdout.write(JSON.stringify({ __error: (err && err.message) || String(err) }));
27647
- process.exitCode = 1;
27648
- }
27649
- });
27650
- `;
27651
27177
  TEMPLATE_RE = /(?<!\\)\$\{([^$]*)\}/g;
27652
27178
  ESCAPED_TEMPLATE_RE = /\\(\$\{[^$]*\})/g;
27653
27179
  BARE_IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;