rnxsim 0.1.427 → 0.1.428

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 (59) hide show
  1. package/cli/commands/control.ts +17 -1
  2. package/cli/commands/flow.ts +5 -9
  3. package/cli/commands/inspect/core.ts +1 -0
  4. package/cli/outbound-endpoints.ts +5 -2
  5. package/cli/self-invocation.ts +24 -0
  6. package/detox/index.ts +10 -36
  7. package/dist-lib/agent-daemon-client.cjs +48 -24
  8. package/dist-lib/agent-events.cjs +1 -1
  9. package/dist-lib/agent-identity.cjs +1 -1
  10. package/dist-lib/agent-sessions.cjs +100 -76
  11. package/dist-lib/attached-projects.cjs +1 -1
  12. package/dist-lib/auth/shared-session.cjs +1 -1
  13. package/dist-lib/backend-origin.cjs +1 -1
  14. package/dist-lib/beta.cjs +1 -1
  15. package/dist-lib/beta.mjs +1 -1
  16. package/dist-lib/bridge-constants.cjs +1 -1
  17. package/dist-lib/bridge-contract-input.cjs +1 -1
  18. package/dist-lib/bridge-contract-input.mjs +1 -1
  19. package/dist-lib/bridge-contract.cjs +1 -1
  20. package/dist-lib/bridge-contract.mjs +1 -1
  21. package/dist-lib/capture-contract.cjs +1 -1
  22. package/dist-lib/capture-contract.mjs +1 -1
  23. package/dist-lib/cli-constants.cjs +1 -1
  24. package/dist-lib/cloud-contract.cjs +1 -1
  25. package/dist-lib/cloud-contract.mjs +1 -1
  26. package/dist-lib/cloud.cjs +1 -1
  27. package/dist-lib/cloud.mjs +1 -1
  28. package/dist-lib/config.cjs +1 -1
  29. package/dist-lib/detox/index.cjs +2 -25
  30. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  31. package/dist-lib/home-paths.cjs +1 -1
  32. package/dist-lib/host/bridge-host.cjs +324 -218
  33. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  34. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  35. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  36. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  37. package/dist-lib/host/websocket-proxy.cjs +162 -78
  38. package/dist-lib/index.cjs +1 -1
  39. package/dist-lib/jump-to-source-babel.cjs +1 -1
  40. package/dist-lib/menu.cjs +1 -1
  41. package/dist-lib/menu.mjs +1 -1
  42. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  43. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  44. package/dist-lib/metro-production-bundle.cjs +43 -24
  45. package/dist-lib/metro-production-bundle.mjs +43 -24
  46. package/dist-lib/metro.cjs +1 -1
  47. package/dist-lib/profiles.cjs +1 -1
  48. package/dist-lib/public-brand.cjs +1 -1
  49. package/dist-lib/react-native-host-modules.cjs +1 -1
  50. package/dist-lib/react-native-host-modules.mjs +1 -1
  51. package/dist-lib/render-mode.cjs +1 -1
  52. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  53. package/dist-lib/sdk.cjs +1 -1
  54. package/dist-lib/sdk.mjs +1 -1
  55. package/dist-lib/skills.cjs +452 -336
  56. package/dist-lib/vite.cjs +1 -1
  57. package/package.json +1 -1
  58. package/src/host/websocket-proxy.ts +197 -92
  59. package/src/metro-production-bundle.ts +55 -30
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.427 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.428 | (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;
@@ -16782,17 +16782,40 @@ var init_standalone = __esm({
16782
16782
  });
16783
16783
 
16784
16784
  // cli/self-invocation.ts
16785
+ function sourceEntryPath() {
16786
+ try {
16787
+ const candidate = (0, import_node_path7.join)((0, import_node_path7.dirname)((0, import_node_url3.fileURLToPath)(__sootsim_import_meta_url)), "bin.ts");
16788
+ return (0, import_node_fs7.existsSync)(candidate) ? candidate : null;
16789
+ } catch {
16790
+ return null;
16791
+ }
16792
+ }
16793
+ function isCliEntry(path20) {
16794
+ const name2 = (0, import_node_path7.basename)(path20);
16795
+ return name2 === "bin.ts" || name2 === "bin.js";
16796
+ }
16785
16797
  function rnxSelfInvocation() {
16786
16798
  if (IS_STANDALONE) return { executable: process.execPath, prefixArgs: [] };
16787
16799
  const entry = process.argv[1];
16800
+ if (entry && isCliEntry(entry)) {
16801
+ return { executable: process.execPath, prefixArgs: [entry] };
16802
+ }
16803
+ const sourceEntry = sourceEntryPath();
16804
+ if (sourceEntry) {
16805
+ return { executable: process.execPath, prefixArgs: [sourceEntry] };
16806
+ }
16788
16807
  if (!entry) {
16789
16808
  throw new Error(`could not locate the ${rnxPublicBrand.commandName} entry script`);
16790
16809
  }
16791
16810
  return { executable: process.execPath, prefixArgs: [entry] };
16792
16811
  }
16812
+ var import_node_fs7, import_node_path7, import_node_url3;
16793
16813
  var init_self_invocation = __esm({
16794
16814
  "cli/self-invocation.ts"() {
16795
16815
  "use strict";
16816
+ import_node_fs7 = require("node:fs");
16817
+ import_node_path7 = require("node:path");
16818
+ import_node_url3 = require("node:url");
16796
16819
  init_public_brand();
16797
16820
  init_standalone();
16798
16821
  }
@@ -16859,7 +16882,7 @@ function getUserDataDir() {
16859
16882
  return userDataDir();
16860
16883
  }
16861
16884
  function storeFile() {
16862
- return import_node_path7.default.join(userDataDir(), "attached-projects.json");
16885
+ return import_node_path8.default.join(userDataDir(), "attached-projects.json");
16863
16886
  }
16864
16887
  function cloneEmpty() {
16865
16888
  return {
@@ -16873,7 +16896,7 @@ function loadStore() {
16873
16896
  const file = storeFile();
16874
16897
  let raw;
16875
16898
  try {
16876
- raw = import_node_fs7.default.readFileSync(file, "utf8");
16899
+ raw = import_node_fs8.default.readFileSync(file, "utf8");
16877
16900
  } catch (err) {
16878
16901
  if (err.code === "ENOENT") return cloneEmpty();
16879
16902
  throw err;
@@ -16890,7 +16913,7 @@ function loadStore() {
16890
16913
  } catch (err) {
16891
16914
  const quarantine = `${file}.corrupt-${Date.now()}`;
16892
16915
  try {
16893
- import_node_fs7.default.renameSync(file, quarantine);
16916
+ import_node_fs8.default.renameSync(file, quarantine);
16894
16917
  console.warn(
16895
16918
  `[sootsim] attached-projects.json was unparseable; quarantined to ${quarantine}. original error: ${err.message}`
16896
16919
  );
@@ -16901,16 +16924,16 @@ function loadStore() {
16901
16924
  }
16902
16925
  function writeStore(store) {
16903
16926
  const file = storeFile();
16904
- import_node_fs7.default.mkdirSync(import_node_path7.default.dirname(file), { recursive: true });
16927
+ import_node_fs8.default.mkdirSync(import_node_path8.default.dirname(file), { recursive: true });
16905
16928
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
16906
- const fd = import_node_fs7.default.openSync(tmp, "w", 384);
16929
+ const fd = import_node_fs8.default.openSync(tmp, "w", 384);
16907
16930
  try {
16908
- import_node_fs7.default.writeFileSync(fd, JSON.stringify(store, null, 2));
16909
- import_node_fs7.default.fsyncSync(fd);
16931
+ import_node_fs8.default.writeFileSync(fd, JSON.stringify(store, null, 2));
16932
+ import_node_fs8.default.fsyncSync(fd);
16910
16933
  } finally {
16911
- import_node_fs7.default.closeSync(fd);
16934
+ import_node_fs8.default.closeSync(fd);
16912
16935
  }
16913
- import_node_fs7.default.renameSync(tmp, file);
16936
+ import_node_fs8.default.renameSync(tmp, file);
16914
16937
  }
16915
16938
  function mutateStore(fn) {
16916
16939
  const store = loadStore();
@@ -16919,13 +16942,13 @@ function mutateStore(fn) {
16919
16942
  return store;
16920
16943
  }
16921
16944
  function projectIdForCwd(cwd) {
16922
- return (0, import_node_crypto3.createHash)("sha256").update(import_node_path7.default.resolve(cwd)).digest("hex").slice(0, 16);
16945
+ return (0, import_node_crypto3.createHash)("sha256").update(import_node_path8.default.resolve(cwd)).digest("hex").slice(0, 16);
16923
16946
  }
16924
16947
  function newSessionId() {
16925
16948
  return `s_${(0, import_node_crypto3.randomBytes)(10).toString("hex")}`;
16926
16949
  }
16927
16950
  function upsertProject(input) {
16928
- const cwd = import_node_path7.default.resolve(input.cwd);
16951
+ const cwd = import_node_path8.default.resolve(input.cwd);
16929
16952
  const id = projectIdForCwd(cwd);
16930
16953
  let result;
16931
16954
  mutateStore((store) => {
@@ -16951,7 +16974,7 @@ function upsertProject(input) {
16951
16974
  const now = Date.now();
16952
16975
  const created = {
16953
16976
  id,
16954
- name: input.name ?? import_node_path7.default.basename(cwd),
16977
+ name: input.name ?? import_node_path8.default.basename(cwd),
16955
16978
  cwd,
16956
16979
  repoRoot: input.repoRoot,
16957
16980
  sourceRoots: input.sourceRoots ?? [cwd],
@@ -17096,8 +17119,8 @@ async function seedFromDemoAppRegistry() {
17096
17119
  if (apps.length === 0) return;
17097
17120
  mutateStore((store) => {
17098
17121
  for (const app of apps) {
17099
- if (!import_node_fs7.default.existsSync(app.dir)) continue;
17100
- const cwd = import_node_path7.default.resolve(app.dir);
17122
+ if (!import_node_fs8.default.existsSync(app.dir)) continue;
17123
+ const cwd = import_node_path8.default.resolve(app.dir);
17101
17124
  const id = projectIdForCwd(cwd);
17102
17125
  if (store.attachedProjects.some((p) => p.id === id)) continue;
17103
17126
  const now = Date.now();
@@ -17118,13 +17141,13 @@ async function seedFromDemoAppRegistry() {
17118
17141
  }
17119
17142
  });
17120
17143
  }
17121
- var import_node_crypto3, import_node_fs7, import_node_path7, overrideDir, COST_HISTORY_MAX_AGE_MS;
17144
+ var import_node_crypto3, import_node_fs8, import_node_path8, overrideDir, COST_HISTORY_MAX_AGE_MS;
17122
17145
  var init_attached_projects = __esm({
17123
17146
  "src/attached-projects.ts"() {
17124
17147
  "use strict";
17125
17148
  import_node_crypto3 = require("node:crypto");
17126
- import_node_fs7 = __toESM(require("node:fs"), 1);
17127
- import_node_path7 = __toESM(require("node:path"), 1);
17149
+ import_node_fs8 = __toESM(require("node:fs"), 1);
17150
+ import_node_path8 = __toESM(require("node:path"), 1);
17128
17151
  init_home_paths();
17129
17152
  overrideDir = null;
17130
17153
  COST_HISTORY_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
@@ -17133,16 +17156,16 @@ var init_attached_projects = __esm({
17133
17156
 
17134
17157
  // src/agent-sessions.ts
17135
17158
  function sessionDir(sessionId) {
17136
- return import_node_path8.default.join(getUserDataDir(), "sessions", sessionId);
17159
+ return import_node_path9.default.join(getUserDataDir(), "sessions", sessionId);
17137
17160
  }
17138
17161
  function promptFifoPath(sessionId) {
17139
- return import_node_path8.default.join(sessionDir(sessionId), "prompt.in");
17162
+ return import_node_path9.default.join(sessionDir(sessionId), "prompt.in");
17140
17163
  }
17141
17164
  function eventsFifoPath(sessionId) {
17142
- return import_node_path8.default.join(sessionDir(sessionId), "events.out");
17165
+ return import_node_path9.default.join(sessionDir(sessionId), "events.out");
17143
17166
  }
17144
17167
  function transcriptPath(sessionId) {
17145
- return import_node_path8.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
17168
+ return import_node_path9.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
17146
17169
  }
17147
17170
  function pidIsAlive(pid, sessionId) {
17148
17171
  if (!pid) return false;
@@ -17152,7 +17175,7 @@ function pidIsAlive(pid, sessionId) {
17152
17175
  return false;
17153
17176
  }
17154
17177
  if (sessionId) {
17155
- if (!import_node_fs8.default.existsSync(sessionDir(sessionId))) return false;
17178
+ if (!import_node_fs9.default.existsSync(sessionDir(sessionId))) return false;
17156
17179
  }
17157
17180
  return true;
17158
17181
  }
@@ -17165,11 +17188,11 @@ function resolveSootsimInvocation() {
17165
17188
  const resourcesPath = process.resourcesPath;
17166
17189
  if (resourcesPath) {
17167
17190
  const candidates = [
17168
- import_node_path8.default.join(resourcesPath, "bin", "sootsim"),
17169
- import_node_path8.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
17191
+ import_node_path9.default.join(resourcesPath, "bin", "sootsim"),
17192
+ import_node_path9.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
17170
17193
  ];
17171
17194
  for (const c of candidates) {
17172
- if (import_node_fs8.default.existsSync(c)) return { executable: c, prefixArgs: [] };
17195
+ if (import_node_fs9.default.existsSync(c)) return { executable: c, prefixArgs: [] };
17173
17196
  }
17174
17197
  }
17175
17198
  }
@@ -17188,15 +17211,15 @@ function tryWorkspaceSootsim() {
17188
17211
  const sootsimDir = resolveSootsimPackageDir();
17189
17212
  if (!sootsimDir) return null;
17190
17213
  const binaryName = `rnx-${process.platform === "win32" ? "windows" : process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`;
17191
- const distBinary = import_node_path8.default.join(sootsimDir, "dist-bin", binaryName);
17192
- if (import_node_fs8.default.existsSync(distBinary)) return { executable: distBinary, prefixArgs: [] };
17193
- const distBin = import_node_path8.default.join(sootsimDir, "dist-cli", "bin.js");
17194
- if (import_node_fs8.default.existsSync(distBin)) {
17214
+ const distBinary = import_node_path9.default.join(sootsimDir, "dist-bin", binaryName);
17215
+ if (import_node_fs9.default.existsSync(distBinary)) return { executable: distBinary, prefixArgs: [] };
17216
+ const distBin = import_node_path9.default.join(sootsimDir, "dist-cli", "bin.js");
17217
+ if (import_node_fs9.default.existsSync(distBin)) {
17195
17218
  try {
17196
- const src = import_node_path8.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
17197
- if (import_node_fs8.default.existsSync(src)) {
17198
- const srcMtime = import_node_fs8.default.statSync(src).mtimeMs;
17199
- const buildMtime = import_node_fs8.default.statSync(distBin).mtimeMs;
17219
+ const src = import_node_path9.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
17220
+ if (import_node_fs9.default.existsSync(src)) {
17221
+ const srcMtime = import_node_fs9.default.statSync(src).mtimeMs;
17222
+ const buildMtime = import_node_fs9.default.statSync(distBin).mtimeMs;
17200
17223
  if (buildMtime < srcMtime) {
17201
17224
  console.warn(
17202
17225
  `[rnx] dist-cli/bin.js is older than agent-wrapper.ts \u2014 rebuild with \`bun run --cwd packages/sootsim build:cli\`.`
@@ -17215,22 +17238,22 @@ function tryWorkspaceSootsim() {
17215
17238
  function resolveSootsimPackageDir() {
17216
17239
  try {
17217
17240
  const resolved = require.resolve(`${rnxPublicBrand.packageName}/package.json`);
17218
- return import_node_path8.default.dirname(resolved);
17241
+ return import_node_path9.default.dirname(resolved);
17219
17242
  } catch {
17220
17243
  }
17221
17244
  const here = fileFromImportMeta();
17222
17245
  if (!here) return null;
17223
- let cur = import_node_path8.default.dirname(here);
17246
+ let cur = import_node_path9.default.dirname(here);
17224
17247
  for (let i = 0; i < 8; i++) {
17225
- const pkg = import_node_path8.default.join(cur, "package.json");
17248
+ const pkg = import_node_path9.default.join(cur, "package.json");
17226
17249
  try {
17227
- if (import_node_fs8.default.existsSync(pkg)) {
17228
- const parsed = JSON.parse(import_node_fs8.default.readFileSync(pkg, "utf8"));
17250
+ if (import_node_fs9.default.existsSync(pkg)) {
17251
+ const parsed = JSON.parse(import_node_fs9.default.readFileSync(pkg, "utf8"));
17229
17252
  if (parsed.name === rnxPublicBrand.packageName) return cur;
17230
17253
  }
17231
17254
  } catch {
17232
17255
  }
17233
- const parent = import_node_path8.default.dirname(cur);
17256
+ const parent = import_node_path9.default.dirname(cur);
17234
17257
  if (parent === cur) break;
17235
17258
  cur = parent;
17236
17259
  }
@@ -17246,28 +17269,28 @@ function fileFromImportMeta() {
17246
17269
  }
17247
17270
  }
17248
17271
  async function withStartLock(projectId, provider, fn) {
17249
- const lockDir = import_node_path8.default.join(getUserDataDir(), "locks");
17250
- import_node_fs8.default.mkdirSync(lockDir, { recursive: true });
17272
+ const lockDir = import_node_path9.default.join(getUserDataDir(), "locks");
17273
+ import_node_fs9.default.mkdirSync(lockDir, { recursive: true });
17251
17274
  try {
17252
- import_node_fs8.default.chmodSync(lockDir, 448);
17275
+ import_node_fs9.default.chmodSync(lockDir, 448);
17253
17276
  } catch {
17254
17277
  }
17255
- const lockPath = import_node_path8.default.join(lockDir, `start-${projectId}-${provider}.lock`);
17278
+ const lockPath = import_node_path9.default.join(lockDir, `start-${projectId}-${provider}.lock`);
17256
17279
  const deadline = Date.now() + 4e3;
17257
17280
  let fd = null;
17258
17281
  while (fd === null) {
17259
17282
  try {
17260
- fd = import_node_fs8.default.openSync(
17283
+ fd = import_node_fs9.default.openSync(
17261
17284
  lockPath,
17262
- import_node_fs8.constants.O_WRONLY | import_node_fs8.constants.O_CREAT | import_node_fs8.constants.O_EXCL,
17285
+ import_node_fs9.constants.O_WRONLY | import_node_fs9.constants.O_CREAT | import_node_fs9.constants.O_EXCL,
17263
17286
  384
17264
17287
  );
17265
17288
  } catch (err) {
17266
17289
  if (err.code !== "EEXIST") throw err;
17267
17290
  try {
17268
- const stale = Number(import_node_fs8.default.readFileSync(lockPath, "utf8").trim());
17291
+ const stale = Number(import_node_fs9.default.readFileSync(lockPath, "utf8").trim());
17269
17292
  if (stale && !isProcessAlive(stale)) {
17270
- import_node_fs8.default.unlinkSync(lockPath);
17293
+ import_node_fs9.default.unlinkSync(lockPath);
17271
17294
  continue;
17272
17295
  }
17273
17296
  } catch {
@@ -17281,15 +17304,15 @@ async function withStartLock(projectId, provider, fn) {
17281
17304
  }
17282
17305
  }
17283
17306
  try {
17284
- import_node_fs8.default.writeFileSync(fd, String(process.pid));
17307
+ import_node_fs9.default.writeFileSync(fd, String(process.pid));
17285
17308
  return await fn();
17286
17309
  } finally {
17287
17310
  try {
17288
- import_node_fs8.default.closeSync(fd);
17311
+ import_node_fs9.default.closeSync(fd);
17289
17312
  } catch {
17290
17313
  }
17291
17314
  try {
17292
- import_node_fs8.default.unlinkSync(lockPath);
17315
+ import_node_fs9.default.unlinkSync(lockPath);
17293
17316
  } catch {
17294
17317
  }
17295
17318
  }
@@ -17303,25 +17326,25 @@ function isProcessAlive(pid) {
17303
17326
  }
17304
17327
  }
17305
17328
  function mkfifoSync(p) {
17306
- const parent = import_node_path8.default.dirname(p);
17307
- import_node_fs8.default.mkdirSync(parent, { recursive: true });
17329
+ const parent = import_node_path9.default.dirname(p);
17330
+ import_node_fs9.default.mkdirSync(parent, { recursive: true });
17308
17331
  try {
17309
- import_node_fs8.default.chmodSync(parent, 448);
17332
+ import_node_fs9.default.chmodSync(parent, 448);
17310
17333
  } catch {
17311
17334
  }
17312
- if (import_node_fs8.default.existsSync(p)) {
17335
+ if (import_node_fs9.default.existsSync(p)) {
17313
17336
  try {
17314
- const stat2 = import_node_fs8.default.statSync(p);
17337
+ const stat2 = import_node_fs9.default.statSync(p);
17315
17338
  if (stat2.isFIFO()) {
17316
17339
  try {
17317
- import_node_fs8.default.chmodSync(p, 384);
17340
+ import_node_fs9.default.chmodSync(p, 384);
17318
17341
  } catch {
17319
17342
  }
17320
17343
  return;
17321
17344
  }
17322
- import_node_fs8.default.unlinkSync(p);
17345
+ import_node_fs9.default.unlinkSync(p);
17323
17346
  } catch {
17324
- import_node_fs8.default.unlinkSync(p);
17347
+ import_node_fs9.default.unlinkSync(p);
17325
17348
  }
17326
17349
  }
17327
17350
  const result = (0, import_node_child_process3.spawnSync)("mkfifo", ["-m", "600", p]);
@@ -17361,10 +17384,10 @@ async function startSession(opts) {
17361
17384
  const transcript = transcriptPath(session.id);
17362
17385
  mkfifoSync(promptIn);
17363
17386
  mkfifoSync(eventsOut);
17364
- const transcriptDir = import_node_path8.default.dirname(transcript);
17365
- import_node_fs8.default.mkdirSync(transcriptDir, { recursive: true });
17387
+ const transcriptDir = import_node_path9.default.dirname(transcript);
17388
+ import_node_fs9.default.mkdirSync(transcriptDir, { recursive: true });
17366
17389
  try {
17367
- import_node_fs8.default.chmodSync(transcriptDir, 448);
17390
+ import_node_fs9.default.chmodSync(transcriptDir, 448);
17368
17391
  } catch {
17369
17392
  }
17370
17393
  const { executable, prefixArgs } = resolveSootsimInvocation();
@@ -17415,7 +17438,7 @@ async function startSession(opts) {
17415
17438
  }
17416
17439
  }
17417
17440
  try {
17418
- import_node_fs8.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
17441
+ import_node_fs9.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
17419
17442
  } catch {
17420
17443
  }
17421
17444
  updateSessionStatus(session.id, { status: "ended" });
@@ -17443,18 +17466,18 @@ async function sendPrompt(sessionId, prompt) {
17443
17466
  );
17444
17467
  }
17445
17468
  const fifo = promptFifoPath(sessionId);
17446
- if (!import_node_fs8.default.existsSync(fifo)) {
17469
+ if (!import_node_fs9.default.existsSync(fifo)) {
17447
17470
  throw new AgentSessionError("NO_FIFO", `prompt FIFO missing: ${fifo}`);
17448
17471
  }
17449
- const fd = import_node_fs8.default.openSync(fifo, import_node_fs8.constants.O_WRONLY);
17472
+ const fd = import_node_fs9.default.openSync(fifo, import_node_fs9.constants.O_WRONLY);
17450
17473
  try {
17451
17474
  const wireText = encodeAgentPromptEnvelope(prompt);
17452
17475
  if (!wireText) {
17453
17476
  throw new AgentSessionError("EMPTY_PROMPT", "prompt text is empty");
17454
17477
  }
17455
- import_node_fs8.default.writeSync(fd, wireText + "\n");
17478
+ import_node_fs9.default.writeSync(fd, wireText + "\n");
17456
17479
  } finally {
17457
- import_node_fs8.default.closeSync(fd);
17480
+ import_node_fs9.default.closeSync(fd);
17458
17481
  }
17459
17482
  updateSessionStatus(sessionId, {
17460
17483
  lastPrompt: prompt.displayText ?? prompt.text,
@@ -17476,7 +17499,7 @@ async function endSession(sessionId) {
17476
17499
  const base = getUserDataDir();
17477
17500
  if (dir.startsWith(base)) {
17478
17501
  try {
17479
- import_node_fs8.default.rmSync(dir, { recursive: true, force: true });
17502
+ import_node_fs9.default.rmSync(dir, { recursive: true, force: true });
17480
17503
  } catch {
17481
17504
  }
17482
17505
  }
@@ -17484,11 +17507,11 @@ async function endSession(sessionId) {
17484
17507
  }
17485
17508
  function subscribeEvents(sessionId, onEvent) {
17486
17509
  const fifo = eventsFifoPath(sessionId);
17487
- if (!import_node_fs8.default.existsSync(fifo)) {
17510
+ if (!import_node_fs9.default.existsSync(fifo)) {
17488
17511
  throw new AgentSessionError("NO_FIFO", `events FIFO missing: ${fifo}`);
17489
17512
  }
17490
- const fd = import_node_fs8.default.openSync(fifo, import_node_fs8.constants.O_RDWR);
17491
- const stream = import_node_fs8.default.createReadStream("", { fd, autoClose: true });
17513
+ const fd = import_node_fs9.default.openSync(fifo, import_node_fs9.constants.O_RDWR);
17514
+ const stream = import_node_fs9.default.createReadStream("", { fd, autoClose: true });
17492
17515
  const rl = import_node_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
17493
17516
  rl.on("line", (line) => {
17494
17517
  const event = parseAgentEventLine(line);
@@ -17499,7 +17522,7 @@ function subscribeEvents(sessionId, onEvent) {
17499
17522
  if (closed) return;
17500
17523
  closed = true;
17501
17524
  try {
17502
- import_node_fs8.default.writeSync(fd, "\n");
17525
+ import_node_fs9.default.writeSync(fd, "\n");
17503
17526
  } catch {
17504
17527
  }
17505
17528
  try {
@@ -17513,7 +17536,7 @@ function subscribeEvents(sessionId, onEvent) {
17513
17536
  };
17514
17537
  }
17515
17538
  async function waitForFirstEvent(fifo, predicate, timeoutMs) {
17516
- const fd = import_node_fs8.default.openSync(fifo, import_node_fs8.constants.O_RDWR | import_node_fs8.constants.O_NONBLOCK);
17539
+ const fd = import_node_fs9.default.openSync(fifo, import_node_fs9.constants.O_RDWR | import_node_fs9.constants.O_NONBLOCK);
17517
17540
  const buf = Buffer.alloc(8192);
17518
17541
  let leftover = "";
17519
17542
  const deadline = Date.now() + timeoutMs;
@@ -17521,7 +17544,7 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
17521
17544
  while (Date.now() < deadline) {
17522
17545
  let n = 0;
17523
17546
  try {
17524
- n = import_node_fs8.default.readSync(fd, buf, 0, buf.length, null);
17547
+ n = import_node_fs9.default.readSync(fd, buf, 0, buf.length, null);
17525
17548
  } catch (err) {
17526
17549
  if (err.code !== "EAGAIN") throw err;
17527
17550
  n = 0;
@@ -17541,17 +17564,17 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
17541
17564
  }
17542
17565
  return null;
17543
17566
  } finally {
17544
- import_node_fs8.default.closeSync(fd);
17567
+ import_node_fs9.default.closeSync(fd);
17545
17568
  }
17546
17569
  }
17547
- var import_node_child_process3, import_node_crypto4, import_node_fs8, import_node_path8, import_node_readline, AgentSessionError;
17570
+ var import_node_child_process3, import_node_crypto4, import_node_fs9, import_node_path9, import_node_readline, AgentSessionError;
17548
17571
  var init_agent_sessions = __esm({
17549
17572
  "src/agent-sessions.ts"() {
17550
17573
  "use strict";
17551
17574
  import_node_child_process3 = require("node:child_process");
17552
17575
  import_node_crypto4 = require("node:crypto");
17553
- import_node_fs8 = __toESM(require("node:fs"), 1);
17554
- import_node_path8 = __toESM(require("node:path"), 1);
17576
+ import_node_fs9 = __toESM(require("node:fs"), 1);
17577
+ import_node_path9 = __toESM(require("node:path"), 1);
17555
17578
  import_node_readline = __toESM(require("node:readline"), 1);
17556
17579
  init_self_invocation();
17557
17580
  init_standalone();
@@ -17666,12 +17689,12 @@ function mapFrameworkToProjectFramework(fw) {
17666
17689
  if (fw === "one" || fw === "vxrn") return "one";
17667
17690
  return "unknown";
17668
17691
  }
17669
- var import_node_fs9, import_node_path9, WS_OPEN, SESSION_STATUS_PERSIST_DELAY_MS, AgentHost;
17692
+ var import_node_fs10, import_node_path10, WS_OPEN, SESSION_STATUS_PERSIST_DELAY_MS, AgentHost;
17670
17693
  var init_agent_host = __esm({
17671
17694
  "src/host/agent-host.ts"() {
17672
17695
  "use strict";
17673
- import_node_fs9 = __toESM(require("node:fs"), 1);
17674
- import_node_path9 = __toESM(require("node:path"), 1);
17696
+ import_node_fs10 = __toESM(require("node:fs"), 1);
17697
+ import_node_path10 = __toESM(require("node:path"), 1);
17675
17698
  init_dev_server_scanner();
17676
17699
  init_agent_sessions();
17677
17700
  init_attached_projects();
@@ -17870,7 +17893,7 @@ var init_agent_host = __esm({
17870
17893
  );
17871
17894
  const project = upsertProject({
17872
17895
  cwd: match.cwd,
17873
- name: match.projectName ?? import_node_path9.default.basename(match.cwd),
17896
+ name: match.projectName ?? import_node_path10.default.basename(match.cwd),
17874
17897
  preferredProvider: input.provider ?? existing?.preferredProvider,
17875
17898
  sourceRoots: existing?.sourceRoots ?? [match.cwd],
17876
17899
  knownBundleUrls,
@@ -17881,18 +17904,18 @@ var init_agent_host = __esm({
17881
17904
  }
17882
17905
  getTranscript(sessionId) {
17883
17906
  const p = transcriptPath(sessionId);
17884
- if (!import_node_fs9.default.existsSync(p)) {
17907
+ if (!import_node_fs10.default.existsSync(p)) {
17885
17908
  return { error: "transcript not found", code: "NO_TRANSCRIPT" };
17886
17909
  }
17887
- return import_node_fs9.default.readFileSync(p, "utf8");
17910
+ return import_node_fs10.default.readFileSync(p, "utf8");
17888
17911
  }
17889
17912
  getPaths() {
17890
17913
  const dir = getUserDataDir();
17891
17914
  return {
17892
17915
  userDataDir: dir,
17893
- storeFile: import_node_path9.default.join(dir, "attached-projects.json"),
17894
- sessionsDir: import_node_path9.default.join(dir, "sessions"),
17895
- transcriptsDir: import_node_path9.default.join(dir, "transcripts")
17916
+ storeFile: import_node_path10.default.join(dir, "attached-projects.json"),
17917
+ sessionsDir: import_node_path10.default.join(dir, "sessions"),
17918
+ transcriptsDir: import_node_path10.default.join(dir, "transcripts")
17896
17919
  };
17897
17920
  }
17898
17921
  // --- subscription management ---
@@ -18739,23 +18762,23 @@ function looksBinary(filePath, size) {
18739
18762
  if (size === 0) return false;
18740
18763
  let fd;
18741
18764
  try {
18742
- fd = import_node_fs10.default.openSync(filePath, "r");
18765
+ fd = import_node_fs11.default.openSync(filePath, "r");
18743
18766
  const buffer = Buffer.alloc(Math.min(BINARY_SNIFF_BYTES, size));
18744
- const read = import_node_fs10.default.readSync(fd, buffer, 0, buffer.length, 0);
18767
+ const read = import_node_fs11.default.readSync(fd, buffer, 0, buffer.length, 0);
18745
18768
  return buffer.subarray(0, read).includes(0);
18746
18769
  } catch {
18747
18770
  return false;
18748
18771
  } finally {
18749
- if (fd !== void 0) import_node_fs10.default.closeSync(fd);
18772
+ if (fd !== void 0) import_node_fs11.default.closeSync(fd);
18750
18773
  }
18751
18774
  }
18752
- var import_node_child_process5, import_node_fs10, import_node_path10, CheckoutRequiresGitError, BINARY_SNIFF_BYTES, LIST_FILES_TIMEOUT_MS, LIST_FILES_ATTEMPTS, LIST_FILES_RETRY_MS, CheckoutFilePlane;
18775
+ var import_node_child_process5, import_node_fs11, import_node_path11, CheckoutRequiresGitError, BINARY_SNIFF_BYTES, LIST_FILES_TIMEOUT_MS, LIST_FILES_ATTEMPTS, LIST_FILES_RETRY_MS, CheckoutFilePlane;
18753
18776
  var init_checkout_plane = __esm({
18754
18777
  "cli/commands/box/checkout-plane.ts"() {
18755
18778
  "use strict";
18756
18779
  import_node_child_process5 = require("node:child_process");
18757
- import_node_fs10 = __toESM(require("node:fs"), 1);
18758
- import_node_path10 = __toESM(require("node:path"), 1);
18780
+ import_node_fs11 = __toESM(require("node:fs"), 1);
18781
+ import_node_path11 = __toESM(require("node:path"), 1);
18759
18782
  CheckoutRequiresGitError = class extends Error {
18760
18783
  };
18761
18784
  BINARY_SNIFF_BYTES = 8192;
@@ -18772,7 +18795,7 @@ var init_checkout_plane = __esm({
18772
18795
  listTimeoutMs;
18773
18796
  trackedOnly;
18774
18797
  constructor(root, options = {}) {
18775
- this.root = import_node_path10.default.resolve(root);
18798
+ this.root = import_node_path11.default.resolve(root);
18776
18799
  this.listTimeoutMs = options.listTimeoutMs ?? LIST_FILES_TIMEOUT_MS;
18777
18800
  this.trackedOnly = options.trackedOnly ?? false;
18778
18801
  }
@@ -18834,13 +18857,13 @@ var init_checkout_plane = __esm({
18834
18857
  if (!relative) continue;
18835
18858
  let stat2;
18836
18859
  try {
18837
- stat2 = import_node_fs10.default.statSync(import_node_path10.default.join(this.root, relative));
18860
+ stat2 = import_node_fs11.default.statSync(import_node_path11.default.join(this.root, relative));
18838
18861
  } catch {
18839
18862
  continue;
18840
18863
  }
18841
18864
  if (!stat2.isFile()) continue;
18842
18865
  const seen = this.sniffed.get(relative);
18843
- const binary = seen && seen.size === stat2.size && seen.mtimeMs === stat2.mtimeMs ? seen.binary : looksBinary(import_node_path10.default.join(this.root, relative), stat2.size);
18866
+ const binary = seen && seen.size === stat2.size && seen.mtimeMs === stat2.mtimeMs ? seen.binary : looksBinary(import_node_path11.default.join(this.root, relative), stat2.size);
18844
18867
  nextSniffed.set(relative, { size: stat2.size, mtimeMs: stat2.mtimeMs, binary });
18845
18868
  next.set(relative, {
18846
18869
  size: stat2.size,
@@ -18854,22 +18877,22 @@ var init_checkout_plane = __esm({
18854
18877
  this.sniffed = nextSniffed;
18855
18878
  }
18856
18879
  absolute(planePath) {
18857
- const resolved = import_node_path10.default.resolve(this.root, planePath);
18858
- if (resolved !== this.root && !resolved.startsWith(`${this.root}${import_node_path10.default.sep}`)) {
18880
+ const resolved = import_node_path11.default.resolve(this.root, planePath);
18881
+ if (resolved !== this.root && !resolved.startsWith(`${this.root}${import_node_path11.default.sep}`)) {
18859
18882
  throw new Error(`path escapes the project: ${planePath}`);
18860
18883
  }
18861
18884
  return resolved;
18862
18885
  }
18863
18886
  async read(planePath) {
18864
- return import_node_fs10.default.promises.readFile(this.absolute(planePath), "utf8");
18887
+ return import_node_fs11.default.promises.readFile(this.absolute(planePath), "utf8");
18865
18888
  }
18866
18889
  async readBytes(planePath) {
18867
- return import_node_fs10.default.promises.readFile(this.absolute(planePath));
18890
+ return import_node_fs11.default.promises.readFile(this.absolute(planePath));
18868
18891
  }
18869
18892
  async write(planePath, content) {
18870
18893
  const absolute = this.absolute(planePath);
18871
- await import_node_fs10.default.promises.mkdir(import_node_path10.default.dirname(absolute), { recursive: true });
18872
- await import_node_fs10.default.promises.writeFile(absolute, content);
18894
+ await import_node_fs11.default.promises.mkdir(import_node_path11.default.dirname(absolute), { recursive: true });
18895
+ await import_node_fs11.default.promises.writeFile(absolute, content);
18873
18896
  this.entries.set(planePath, {
18874
18897
  size: content.byteLength,
18875
18898
  readOnly: false,
@@ -18878,7 +18901,7 @@ var init_checkout_plane = __esm({
18878
18901
  });
18879
18902
  }
18880
18903
  async delete(planePath) {
18881
- await import_node_fs10.default.promises.rm(this.absolute(planePath), { force: true });
18904
+ await import_node_fs11.default.promises.rm(this.absolute(planePath), { force: true });
18882
18905
  this.entries.delete(planePath);
18883
18906
  }
18884
18907
  };
@@ -18886,12 +18909,12 @@ var init_checkout_plane = __esm({
18886
18909
  });
18887
18910
 
18888
18911
  // src/host/plane-host.ts
18889
- var import_node_fs11, import_node_path11, WS_OPEN2, PlaneNotRegisteredError, PlaneHost;
18912
+ var import_node_fs12, import_node_path12, WS_OPEN2, PlaneNotRegisteredError, PlaneHost;
18890
18913
  var init_plane_host = __esm({
18891
18914
  "src/host/plane-host.ts"() {
18892
18915
  "use strict";
18893
- import_node_fs11 = __toESM(require("node:fs"), 1);
18894
- import_node_path11 = __toESM(require("node:path"), 1);
18916
+ import_node_fs12 = __toESM(require("node:fs"), 1);
18917
+ import_node_path12 = __toESM(require("node:path"), 1);
18895
18918
  init_plane();
18896
18919
  init_checkout_plane();
18897
18920
  WS_OPEN2 = 1;
@@ -19014,14 +19037,14 @@ var init_plane_host = __esm({
19014
19037
  // root and an opened one cannot disagree about the same directory.
19015
19038
  realRoot(root) {
19016
19039
  if (!root) throw new Error("root is required");
19017
- const resolved = import_node_path11.default.resolve(root);
19040
+ const resolved = import_node_path12.default.resolve(root);
19018
19041
  let real;
19019
19042
  try {
19020
- real = import_node_fs11.default.realpathSync(resolved);
19043
+ real = import_node_fs12.default.realpathSync(resolved);
19021
19044
  } catch {
19022
19045
  throw new Error(`no such directory: ${resolved}`);
19023
19046
  }
19024
- if (!import_node_fs11.default.statSync(real).isDirectory()) throw new Error(`not a directory: ${real}`);
19047
+ if (!import_node_fs12.default.statSync(real).isDirectory()) throw new Error(`not a directory: ${real}`);
19025
19048
  return real;
19026
19049
  }
19027
19050
  respond(ws, id, result) {
@@ -19044,7 +19067,7 @@ var init_plane_host = __esm({
19044
19067
 
19045
19068
  // src/host/replacement-module-handler.ts
19046
19069
  function getReplacementLoader(filePath) {
19047
- const ext = import_node_path12.default.extname(filePath).toLowerCase();
19070
+ const ext = import_node_path13.default.extname(filePath).toLowerCase();
19048
19071
  switch (ext) {
19049
19072
  case ".js":
19050
19073
  return "js";
@@ -19126,7 +19149,7 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
19126
19149
  res.end("replacement files must be .js, .jsx, .ts, or .tsx");
19127
19150
  return true;
19128
19151
  }
19129
- if (!import_node_path12.default.isAbsolute(targetPath)) {
19152
+ if (!import_node_path13.default.isAbsolute(targetPath)) {
19130
19153
  res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
19131
19154
  res.end("replacement file path must be absolute");
19132
19155
  return true;
@@ -19134,10 +19157,10 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
19134
19157
  const runtimePackages = new Set(url.searchParams.getAll("runtimePackage"));
19135
19158
  let resolvedProjectRoot;
19136
19159
  if (options.projectRoot) {
19137
- const lexicalProjectRoot = import_node_path12.default.resolve(options.projectRoot);
19160
+ const lexicalProjectRoot = import_node_path13.default.resolve(options.projectRoot);
19138
19161
  resolvedProjectRoot = await import_promises5.default.realpath(options.projectRoot);
19139
- const lexicalTarget = import_node_path12.default.relative(lexicalProjectRoot, targetPath);
19140
- if (lexicalTarget.startsWith("..") || import_node_path12.default.isAbsolute(lexicalTarget)) {
19162
+ const lexicalTarget = import_node_path13.default.relative(lexicalProjectRoot, targetPath);
19163
+ if (lexicalTarget.startsWith("..") || import_node_path13.default.isAbsolute(lexicalTarget)) {
19141
19164
  res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
19142
19165
  res.end("replacement file must be inside the project root");
19143
19166
  return true;
@@ -19153,8 +19176,8 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
19153
19176
  return true;
19154
19177
  }
19155
19178
  if (resolvedProjectRoot) {
19156
- const relativeTarget = import_node_path12.default.relative(resolvedProjectRoot, resolvedTargetPath);
19157
- if (relativeTarget.startsWith("..") || import_node_path12.default.isAbsolute(relativeTarget)) {
19179
+ const relativeTarget = import_node_path13.default.relative(resolvedProjectRoot, resolvedTargetPath);
19180
+ if (relativeTarget.startsWith("..") || import_node_path13.default.isAbsolute(relativeTarget)) {
19158
19181
  res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
19159
19182
  res.end("replacement file must be inside the project root");
19160
19183
  return true;
@@ -19239,12 +19262,12 @@ exports.jsxDEV = exports.jsx`,
19239
19262
  }
19240
19263
  return true;
19241
19264
  }
19242
- var import_promises5, import_node_path12, import_esbuild, REPLACEMENT_MODULE_ROUTE;
19265
+ var import_promises5, import_node_path13, import_esbuild, REPLACEMENT_MODULE_ROUTE;
19243
19266
  var init_replacement_module_handler = __esm({
19244
19267
  "src/host/replacement-module-handler.ts"() {
19245
19268
  "use strict";
19246
19269
  import_promises5 = __toESM(require("node:fs/promises"), 1);
19247
- import_node_path12 = __toESM(require("node:path"), 1);
19270
+ import_node_path13 = __toESM(require("node:path"), 1);
19248
19271
  import_esbuild = require("esbuild");
19249
19272
  init_backend_origin();
19250
19273
  REPLACEMENT_MODULE_ROUTE = "/__sootsim-replacement-module";
@@ -19252,20 +19275,62 @@ var init_replacement_module_handler = __esm({
19252
19275
  });
19253
19276
 
19254
19277
  // src/host/websocket-proxy.ts
19255
- function rejectUpgrade(socket, status, message) {
19278
+ function rejectUpgrade(socket, status, message, headers = {}) {
19256
19279
  try {
19280
+ const extraHeaders = Object.entries(headers).map(([name2, value]) => `${name2}: ${value}\r
19281
+ `).join("");
19257
19282
  socket.write(
19258
19283
  `HTTP/1.1 ${status} ${message}\r
19259
19284
  Connection: close\r
19260
19285
  Content-Type: text/plain\r
19261
19286
  Content-Length: ${message.length}\r
19262
- \r
19287
+ ${extraHeaders}\r
19263
19288
  ${message}`
19264
19289
  );
19265
19290
  } catch {
19266
19291
  }
19267
19292
  socket.destroy();
19268
19293
  }
19294
+ function hasHeaderToken(value, token) {
19295
+ return (value || "").split(",").some((part) => part.trim().toLowerCase() === token);
19296
+ }
19297
+ function hasValidProtocolOffer(value) {
19298
+ if (value === void 0) return true;
19299
+ if (!WEBSOCKET_PROTOCOL_TOKEN.test(value[0] || "")) return false;
19300
+ if (!WEBSOCKET_PROTOCOL_TOKEN.test(value[value.length - 1] || "")) return false;
19301
+ const protocols = /* @__PURE__ */ new Set();
19302
+ for (const part of value.split(",")) {
19303
+ const protocol = part.trim();
19304
+ if (!WEBSOCKET_PROTOCOL_TOKEN.test(protocol) || protocols.has(protocol)) {
19305
+ return false;
19306
+ }
19307
+ protocols.add(protocol);
19308
+ }
19309
+ return true;
19310
+ }
19311
+ function validateUpgradeRequest(req) {
19312
+ if (req.method !== "GET") {
19313
+ return { status: 405, message: "invalid websocket http method" };
19314
+ }
19315
+ if (!hasHeaderToken(req.headers.connection, "upgrade") || !hasHeaderToken(req.headers.upgrade, "websocket")) {
19316
+ return { status: 400, message: "invalid websocket upgrade headers" };
19317
+ }
19318
+ const key = req.headers["sec-websocket-key"];
19319
+ if (typeof key !== "string" || !WEBSOCKET_KEY.test(key) || Buffer.from(key, "base64").byteLength !== 16 || Buffer.from(key, "base64").toString("base64") !== key) {
19320
+ return { status: 400, message: "invalid websocket key" };
19321
+ }
19322
+ if (req.headers["sec-websocket-version"] !== "13") {
19323
+ return {
19324
+ status: 400,
19325
+ message: "invalid websocket version",
19326
+ headers: { "Sec-WebSocket-Version": "13" }
19327
+ };
19328
+ }
19329
+ if (!hasValidProtocolOffer(req.headers["sec-websocket-protocol"])) {
19330
+ return { status: 400, message: "invalid websocket protocol offer" };
19331
+ }
19332
+ return null;
19333
+ }
19269
19334
  function isSameOriginUpgrade(req) {
19270
19335
  const origin = req.headers.origin;
19271
19336
  const host = req.headers.host;
@@ -19295,61 +19360,29 @@ function getDefaultWebSocketOrigin(targetUrl) {
19295
19360
  origin.protocol = targetUrl.protocol === "wss:" ? "https:" : "http:";
19296
19361
  return origin.origin;
19297
19362
  }
19298
- function getRequestedProtocols(req) {
19299
- const header = req.headers["sec-websocket-protocol"];
19300
- const value = Array.isArray(header) ? header.join(",") : header || "";
19301
- return value.split(",").map((part) => part.trim()).filter(Boolean);
19302
- }
19303
- function safeClose(ws, code, reason) {
19304
- if (ws.readyState === import_ws.WebSocket.CLOSED || ws.readyState === import_ws.WebSocket.CLOSING) {
19305
- return;
19306
- }
19307
- try {
19308
- ws.close(code, reason);
19309
- } catch {
19310
- ws.terminate();
19311
- }
19312
- }
19313
- function connectProxyPair(clientWs, upstream) {
19314
- let closing = false;
19315
- const closeBoth = (source, target, code, reason) => {
19316
- if (closing) return;
19317
- closing = true;
19318
- safeClose(target, code, reason.toString());
19319
- if (source.readyState === import_ws.WebSocket.OPEN) {
19320
- safeClose(source, code, reason.toString());
19321
- }
19322
- };
19323
- clientWs.on("message", (data, isBinary) => {
19324
- if (upstream.readyState === import_ws.WebSocket.OPEN) {
19325
- upstream.send(data, { binary: isBinary });
19326
- }
19327
- });
19328
- upstream.on("message", (data, isBinary) => {
19329
- if (clientWs.readyState === import_ws.WebSocket.OPEN) {
19330
- clientWs.send(data, { binary: isBinary });
19331
- }
19332
- });
19333
- clientWs.on("close", (code, reason) => closeBoth(clientWs, upstream, code, reason));
19334
- upstream.on("close", (code, reason) => closeBoth(upstream, clientWs, code, reason));
19335
- clientWs.on("error", () => safeClose(upstream, 1011, "proxy client error"));
19336
- upstream.on("error", () => safeClose(clientWs, 1011, "upstream websocket error"));
19337
- }
19338
- function createUpstreamWebSocket(targetUrl, protocols, headers, forwardOrigin) {
19363
+ function createUpstreamHeaders(req, targetUrl, headers, forwardOrigin) {
19339
19364
  const upstreamHeaders = { ...headers };
19340
19365
  const originKeys = Object.keys(upstreamHeaders).filter(
19341
- (key) => key.toLowerCase() === "origin"
19366
+ (key2) => key2.toLowerCase() === "origin"
19342
19367
  );
19343
19368
  if (forwardOrigin && originKeys.length === 0) {
19344
19369
  upstreamHeaders.origin = getDefaultWebSocketOrigin(targetUrl);
19345
19370
  } else if (!forwardOrigin) {
19346
- for (const key of originKeys) delete upstreamHeaders[key];
19347
- }
19348
- if (Object.keys(upstreamHeaders).length === 0)
19349
- return new import_ws.WebSocket(targetUrl.href, protocols);
19350
- return new import_ws.WebSocket(targetUrl.href, protocols, {
19351
- headers: upstreamHeaders
19352
- });
19371
+ for (const key2 of originKeys) delete upstreamHeaders[key2];
19372
+ }
19373
+ upstreamHeaders.connection = "Upgrade";
19374
+ upstreamHeaders.upgrade = "websocket";
19375
+ const key = req.headers["sec-websocket-key"];
19376
+ if (typeof key === "string") upstreamHeaders["sec-websocket-key"] = key;
19377
+ const version = req.headers["sec-websocket-version"];
19378
+ if (typeof version === "string") upstreamHeaders["sec-websocket-version"] = version;
19379
+ const protocol = req.headers["sec-websocket-protocol"];
19380
+ if (typeof protocol === "string") upstreamHeaders["sec-websocket-protocol"] = protocol;
19381
+ const extensions = req.headers["sec-websocket-extensions"];
19382
+ if (typeof extensions === "string") {
19383
+ upstreamHeaders["sec-websocket-extensions"] = extensions;
19384
+ }
19385
+ return upstreamHeaders;
19353
19386
  }
19354
19387
  function isWebSocketProxyRequestUrl(rawUrl) {
19355
19388
  if (!rawUrl) return false;
@@ -19365,6 +19398,16 @@ function handleWebSocketProxyUpgrade(req, socket, head, forwardOrigin = true) {
19365
19398
  rejectUpgrade(socket, 403, "forbidden websocket proxy origin");
19366
19399
  return true;
19367
19400
  }
19401
+ const upgradeRejection = validateUpgradeRequest(req);
19402
+ if (upgradeRejection) {
19403
+ rejectUpgrade(
19404
+ socket,
19405
+ upgradeRejection.status,
19406
+ upgradeRejection.message,
19407
+ upgradeRejection.headers
19408
+ );
19409
+ return true;
19410
+ }
19368
19411
  let targetUrl;
19369
19412
  let headers;
19370
19413
  try {
@@ -19384,45 +19427,107 @@ function handleWebSocketProxyUpgrade(req, socket, head, forwardOrigin = true) {
19384
19427
  rejectUpgrade(socket, 400, "invalid websocket proxy request");
19385
19428
  return true;
19386
19429
  }
19387
- const protocols = getRequestedProtocols(req);
19388
- const upstream = createUpstreamWebSocket(targetUrl, protocols, headers, forwardOrigin);
19389
- let completed = false;
19430
+ const upstreamHeaders = createUpstreamHeaders(req, targetUrl, headers, forwardOrigin);
19431
+ let settled = false;
19432
+ const request = targetUrl.protocol === "wss:" ? import_https2.request : import_http3.request;
19433
+ const upstreamRequestUrl = new URL(targetUrl);
19434
+ upstreamRequestUrl.protocol = targetUrl.protocol === "wss:" ? "https:" : "http:";
19435
+ const upstreamRequest = request(upstreamRequestUrl, {
19436
+ method: "GET",
19437
+ headers: upstreamHeaders
19438
+ });
19439
+ let destroyUpstreamSocket = () => {
19440
+ };
19441
+ upstreamRequest.once("socket", (upstreamSocket) => {
19442
+ destroyUpstreamSocket = () => upstreamSocket.destroy();
19443
+ });
19444
+ const fail = (status, message) => {
19445
+ if (settled) return;
19446
+ settled = true;
19447
+ clearTimeout(timer);
19448
+ upstreamRequest.destroy();
19449
+ destroyUpstreamSocket();
19450
+ rejectUpgrade(socket, status, message);
19451
+ };
19452
+ const timer = setTimeout(() => {
19453
+ fail(504, "upstream websocket handshake timeout");
19454
+ }, WEBSOCKET_PROXY_HANDSHAKE_TIMEOUT_MS);
19390
19455
  socket.once("close", () => {
19391
- if (!completed) upstream.terminate();
19456
+ if (!settled) {
19457
+ settled = true;
19458
+ clearTimeout(timer);
19459
+ }
19460
+ upstreamRequest.destroy();
19461
+ destroyUpstreamSocket();
19392
19462
  });
19393
- upstream.once("open", () => {
19394
- if (completed) return;
19395
- completed = true;
19396
- const selectedProtocol = upstream.protocol;
19397
- const proxyServer = new import_ws.WebSocketServer({
19398
- noServer: true,
19399
- clientTracking: false,
19400
- handleProtocols(requestedProtocols) {
19401
- return selectedProtocol || requestedProtocols.values().next().value || false;
19402
- }
19403
- });
19404
- proxyServer.handleUpgrade(req, socket, head, (clientWs) => {
19405
- connectProxyPair(clientWs, upstream);
19406
- });
19463
+ upstreamRequest.once("response", (response) => {
19464
+ response.resume();
19465
+ fail(502, "upstream websocket rejected upgrade");
19407
19466
  });
19408
- upstream.once("error", () => {
19409
- if (completed) return;
19410
- completed = true;
19411
- rejectUpgrade(socket, 502, "upstream websocket error");
19467
+ upstreamRequest.once("error", () => {
19468
+ fail(502, "upstream websocket error");
19412
19469
  });
19413
- upstream.once("close", () => {
19414
- if (completed) return;
19415
- completed = true;
19416
- rejectUpgrade(socket, 502, "upstream websocket closed");
19470
+ upstreamRequest.once("upgrade", (response, upstreamSocket, upstreamHead) => {
19471
+ if (settled) {
19472
+ upstreamSocket.destroy();
19473
+ return;
19474
+ }
19475
+ const accept = response.headers["sec-websocket-accept"];
19476
+ const upgrade = response.headers.upgrade;
19477
+ const requestKey = req.headers["sec-websocket-key"];
19478
+ const expectedAccept = typeof requestKey === "string" ? (0, import_crypto.createHash)("sha1").update(`${requestKey}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`).digest("base64") : "";
19479
+ if (typeof accept !== "string" || accept !== expectedAccept || typeof upgrade !== "string" || upgrade.toLowerCase() !== "websocket") {
19480
+ upstreamSocket.destroy();
19481
+ fail(502, "upstream websocket handshake is invalid");
19482
+ return;
19483
+ }
19484
+ const selectedProtocol = response.headers["sec-websocket-protocol"];
19485
+ const requestedProtocols = (req.headers["sec-websocket-protocol"] || "").split(",").map((protocol) => protocol.trim());
19486
+ if (typeof selectedProtocol === "string" && !requestedProtocols.includes(selectedProtocol)) {
19487
+ upstreamSocket.destroy();
19488
+ fail(502, "upstream websocket selected an unrequested protocol");
19489
+ return;
19490
+ }
19491
+ settled = true;
19492
+ clearTimeout(timer);
19493
+ const responseHeaders = [
19494
+ "HTTP/1.1 101 Switching Protocols",
19495
+ "Upgrade: websocket",
19496
+ "Connection: Upgrade",
19497
+ `Sec-WebSocket-Accept: ${accept}`
19498
+ ];
19499
+ if (typeof selectedProtocol === "string") {
19500
+ responseHeaders.push(`Sec-WebSocket-Protocol: ${selectedProtocol}`);
19501
+ }
19502
+ const selectedExtensions = response.headers["sec-websocket-extensions"];
19503
+ if (typeof selectedExtensions === "string") {
19504
+ responseHeaders.push(`Sec-WebSocket-Extensions: ${selectedExtensions}`);
19505
+ }
19506
+ socket.write(`${responseHeaders.join("\r\n")}\r
19507
+ \r
19508
+ `);
19509
+ if (head.length > 0) upstreamSocket.write(head);
19510
+ if (upstreamHead.length > 0) socket.write(upstreamHead);
19511
+ socket.once("close", () => upstreamSocket.destroy());
19512
+ socket.once("error", () => upstreamSocket.destroy());
19513
+ upstreamSocket.once("error", () => socket.destroy());
19514
+ socket.pipe(upstreamSocket);
19515
+ upstreamSocket.pipe(socket);
19417
19516
  });
19517
+ upstreamRequest.end();
19418
19518
  return true;
19419
19519
  }
19420
- var import_ws, WEBSOCKET_PROXY_PATH, STRIP_UPSTREAM_HEADERS;
19520
+ var import_crypto, import_http3, import_https2, WEBSOCKET_PROXY_PATH, WEBSOCKET_PROXY_HANDSHAKE_TIMEOUT_MS, WEBSOCKET_PROTOCOL_TOKEN, WEBSOCKET_KEY, STRIP_UPSTREAM_HEADERS;
19421
19521
  var init_websocket_proxy = __esm({
19422
19522
  "src/host/websocket-proxy.ts"() {
19423
19523
  "use strict";
19424
- import_ws = require("ws");
19524
+ import_crypto = require("crypto");
19525
+ import_http3 = require("http");
19526
+ import_https2 = require("https");
19425
19527
  WEBSOCKET_PROXY_PATH = "/__websocket-proxy";
19528
+ WEBSOCKET_PROXY_HANDSHAKE_TIMEOUT_MS = 1e4;
19529
+ WEBSOCKET_PROTOCOL_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
19530
+ WEBSOCKET_KEY = /^[+/0-9A-Za-z]{22}==$/;
19426
19531
  STRIP_UPSTREAM_HEADERS = /* @__PURE__ */ new Set([
19427
19532
  "host",
19428
19533
  "connection",
@@ -19473,15 +19578,15 @@ function unrefTimer(timer) {
19473
19578
  timer.unref();
19474
19579
  }
19475
19580
  }
19476
- var import_child_process3, import_fs2, import_http3, import_path2, import_ws2, SOOTSIM_CROSS_ORIGIN_EMBEDDER_POLICY, WRITE_COMMAND_TYPES, FORCE_CLOSE_GRACE_MS, FORCE_CLOSE_TERMINATE_MS, DAEMON_HEARTBEAT_INTERVAL_MS, DEFAULT_RUNTIME_UPDATE_INTERVAL_MS, RUNTIME_UPDATE_INTERVAL_ENV, HTTP_MIME_TYPES, CAMERA_FIXTURE_ROUTE, CAMERA_FIXTURE_NAME_RE, SootSimBridgeHost;
19581
+ var import_child_process3, import_fs2, import_http4, import_path2, import_ws, SOOTSIM_CROSS_ORIGIN_EMBEDDER_POLICY, WRITE_COMMAND_TYPES, FORCE_CLOSE_GRACE_MS, FORCE_CLOSE_TERMINATE_MS, DAEMON_HEARTBEAT_INTERVAL_MS, DEFAULT_RUNTIME_UPDATE_INTERVAL_MS, RUNTIME_UPDATE_INTERVAL_ENV, HTTP_MIME_TYPES, CAMERA_FIXTURE_ROUTE, CAMERA_FIXTURE_NAME_RE, SootSimBridgeHost;
19477
19582
  var init_bridge_host = __esm({
19478
19583
  "src/host/bridge-host.ts"() {
19479
19584
  "use strict";
19480
19585
  import_child_process3 = require("child_process");
19481
19586
  import_fs2 = __toESM(require("fs"), 1);
19482
- import_http3 = require("http");
19587
+ import_http4 = require("http");
19483
19588
  import_path2 = __toESM(require("path"), 1);
19484
- import_ws2 = require("ws");
19589
+ import_ws = require("ws");
19485
19590
  init_dev_server_scanner();
19486
19591
  init_bridge_constants();
19487
19592
  init_cli_version();
@@ -19705,7 +19810,7 @@ var init_bridge_host = __esm({
19705
19810
  }
19706
19811
  bindOnce(port, _silent) {
19707
19812
  return new Promise((resolve7, reject) => {
19708
- const server = (0, import_http3.createServer)((req, res) => this.handleHttpRequest(req, res));
19813
+ const server = (0, import_http4.createServer)((req, res) => this.handleHttpRequest(req, res));
19709
19814
  let settled = false;
19710
19815
  const onError = (err) => {
19711
19816
  if (settled) return;
@@ -19728,7 +19833,7 @@ var init_bridge_host = __esm({
19728
19833
  `);
19729
19834
  });
19730
19835
  this.httpServer = server;
19731
- this.wss = new import_ws2.WebSocketServer({ noServer: true });
19836
+ this.wss = new import_ws.WebSocketServer({ noServer: true });
19732
19837
  this.wireWebSocketServer();
19733
19838
  server.on("upgrade", (req, socket, head) => {
19734
19839
  if (handleWebSocketProxyUpgrade(req, socket, head)) return;
@@ -19966,7 +20071,7 @@ var init_bridge_host = __esm({
19966
20071
  const entry = this.cliBySentId.get(msg.id);
19967
20072
  if (entry) {
19968
20073
  this.cliBySentId.delete(msg.id);
19969
- if (entry.ws.readyState === import_ws2.WebSocket.OPEN) {
20074
+ if (entry.ws.readyState === import_ws.WebSocket.OPEN) {
19970
20075
  const otherCliCount = this.getOtherCliIdentityCount(entry.ws, entry.simId);
19971
20076
  const response = otherCliCount > 0 ? { ...msg, id: entry.originalId, _otherCliCount: otherCliCount } : { ...msg, id: entry.originalId };
19972
20077
  entry.ws.send(JSON.stringify(response));
@@ -19994,7 +20099,7 @@ var init_bridge_host = __esm({
19994
20099
  if (typeof msg.cliLabel === "string" && msg.cliLabel.trim()) {
19995
20100
  this.cliLabelBySocket.set(ws, msg.cliLabel.trim());
19996
20101
  }
19997
- if (ws.readyState === import_ws2.WebSocket.OPEN) {
20102
+ if (ws.readyState === import_ws.WebSocket.OPEN) {
19998
20103
  ws.send(
19999
20104
  JSON.stringify({
20000
20105
  id: msg.id,
@@ -20009,7 +20114,7 @@ var init_bridge_host = __esm({
20009
20114
  return;
20010
20115
  }
20011
20116
  if (msg.type === "bridge:list-sims") {
20012
- if (ws.readyState === import_ws2.WebSocket.OPEN) {
20117
+ if (ws.readyState === import_ws.WebSocket.OPEN) {
20013
20118
  ws.send(
20014
20119
  JSON.stringify({
20015
20120
  id: msg.id,
@@ -20025,7 +20130,7 @@ var init_bridge_host = __esm({
20025
20130
  force: msg.force === true
20026
20131
  });
20027
20132
  if (!outcome.granted) {
20028
- if (ws.readyState === import_ws2.WebSocket.OPEN) {
20133
+ if (ws.readyState === import_ws.WebSocket.OPEN) {
20029
20134
  ws.send(
20030
20135
  JSON.stringify({
20031
20136
  id: msg.id,
@@ -20041,7 +20146,7 @@ var init_bridge_host = __esm({
20041
20146
  targetSim2.id,
20042
20147
  outcome.bootedCount > 0 ? `cli force-claimed sim (booted ${outcome.bootedCount})` : "cli claimed sim"
20043
20148
  );
20044
- if (ws.readyState === import_ws2.WebSocket.OPEN) {
20149
+ if (ws.readyState === import_ws.WebSocket.OPEN) {
20045
20150
  ws.send(
20046
20151
  JSON.stringify({
20047
20152
  id: msg.id,
@@ -20060,7 +20165,7 @@ var init_bridge_host = __esm({
20060
20165
  if (shouldAcquireLease(msg)) {
20061
20166
  const outcome = this.tryAcquireLease(ws, targetSim);
20062
20167
  if (!outcome.granted) {
20063
- if (ws.readyState === import_ws2.WebSocket.OPEN) {
20168
+ if (ws.readyState === import_ws.WebSocket.OPEN) {
20064
20169
  ws.send(
20065
20170
  JSON.stringify({
20066
20171
  id: msg.id,
@@ -20086,7 +20191,7 @@ var init_bridge_host = __esm({
20086
20191
  targetSim.ws.send(JSON.stringify({ ...forwarded, id: sentId }));
20087
20192
  if (forwarded.type === "close") {
20088
20193
  this.cliBySentId.delete(sentId);
20089
- if (ws.readyState === import_ws2.WebSocket.OPEN) {
20194
+ if (ws.readyState === import_ws.WebSocket.OPEN) {
20090
20195
  ws.send(
20091
20196
  JSON.stringify({
20092
20197
  id: msg.id,
@@ -20101,7 +20206,7 @@ var init_bridge_host = __esm({
20101
20206
  unrefTimer(closeTimer);
20102
20207
  }
20103
20208
  } catch (err) {
20104
- if (ws.readyState === import_ws2.WebSocket.OPEN) {
20209
+ if (ws.readyState === import_ws.WebSocket.OPEN) {
20105
20210
  ws.send(
20106
20211
  JSON.stringify({
20107
20212
  id: msg.id,
@@ -20127,7 +20232,7 @@ var init_bridge_host = __esm({
20127
20232
  }
20128
20233
  for (const [sentId, entry] of this.cliBySentId) {
20129
20234
  if (entry.simId !== sim.id) continue;
20130
- if (entry.ws.readyState === import_ws2.WebSocket.OPEN) {
20235
+ if (entry.ws.readyState === import_ws.WebSocket.OPEN) {
20131
20236
  entry.ws.send(
20132
20237
  JSON.stringify({
20133
20238
  id: entry.originalId,
@@ -20499,7 +20604,7 @@ var init_bridge_host = __esm({
20499
20604
  if (runtimeVersionFromHostname(simUrl.hostname)) continue;
20500
20605
  } catch {
20501
20606
  }
20502
- if (sim.ws.readyState === import_ws2.WebSocket.OPEN) {
20607
+ if (sim.ws.readyState === import_ws.WebSocket.OPEN) {
20503
20608
  try {
20504
20609
  sim.ws.send(payload);
20505
20610
  } catch {
@@ -20883,7 +20988,7 @@ var init_bridge_host = __esm({
20883
20988
  sweepDeadWebSockets() {
20884
20989
  if (!this.wss) return;
20885
20990
  for (const ws of this.wss.clients) {
20886
- if (ws.readyState !== import_ws2.WebSocket.OPEN) continue;
20991
+ if (ws.readyState !== import_ws.WebSocket.OPEN) continue;
20887
20992
  const alive = this.wsIsAlive.get(ws);
20888
20993
  if (alive === false) {
20889
20994
  try {
@@ -20904,7 +21009,7 @@ var init_bridge_host = __esm({
20904
21009
  }
20905
21010
  }
20906
21011
  closeSimSocketFromHost(ws) {
20907
- if (ws.readyState !== import_ws2.WebSocket.OPEN) return;
21012
+ if (ws.readyState !== import_ws.WebSocket.OPEN) return;
20908
21013
  try {
20909
21014
  ws.close(SOOTSIM_BRIDGE_SIM_CLOSE_CODE, SOOTSIM_BRIDGE_SIM_CLOSE_REASON);
20910
21015
  } catch {
@@ -20915,7 +21020,7 @@ var init_bridge_host = __esm({
20915
21020
  return;
20916
21021
  }
20917
21022
  const terminateTimer = setTimeout(() => {
20918
- if (ws.readyState === import_ws2.WebSocket.CLOSED) return;
21023
+ if (ws.readyState === import_ws.WebSocket.CLOSED) return;
20919
21024
  try {
20920
21025
  ws.terminate();
20921
21026
  } catch {
@@ -21078,7 +21183,7 @@ var init_bridge_host = __esm({
21078
21183
  try {
21079
21184
  readyState = sim.ws.readyState;
21080
21185
  } catch {
21081
- readyState = import_ws2.WebSocket.CLOSED;
21186
+ readyState = import_ws.WebSocket.CLOSED;
21082
21187
  }
21083
21188
  const lease = this.getActiveLease(sim);
21084
21189
  return {
@@ -21091,7 +21196,7 @@ var init_bridge_host = __esm({
21091
21196
  lastSeenAt: sim.lastSeenAt,
21092
21197
  lastActiveAt: sim.lastActiveAt || void 0,
21093
21198
  isPrimary: sim.id === this.primarySimId,
21094
- readyState: readyState === import_ws2.WebSocket.OPEN ? "open" : readyState === import_ws2.WebSocket.CLOSING ? "closing" : "closed",
21199
+ readyState: readyState === import_ws.WebSocket.OPEN ? "open" : readyState === import_ws.WebSocket.CLOSING ? "closing" : "closed",
21095
21200
  attachedCliCount: this.getAttachedCliCount(sim.id),
21096
21201
  lockedBy: lease ? lease.cliLabel || lease.cliIdentityKey : void 0,
21097
21202
  lockedByKind: lease ? lease.kind : void 0,
@@ -21208,7 +21313,7 @@ var init_bridge_host = __esm({
21208
21313
  getOpenSim(simId) {
21209
21314
  if (simId) {
21210
21315
  const sim = this.sims.get(simId);
21211
- if (sim?.ws.readyState === import_ws2.WebSocket.OPEN) return sim;
21316
+ if (sim?.ws.readyState === import_ws.WebSocket.OPEN) return sim;
21212
21317
  return null;
21213
21318
  }
21214
21319
  const candidates = this.getDefaultSimCandidates();
@@ -21216,7 +21321,7 @@ var init_bridge_host = __esm({
21216
21321
  }
21217
21322
  getDefaultSimCandidates() {
21218
21323
  const open = Array.from(this.sims.values()).filter(
21219
- (sim) => sim.ws.readyState === import_ws2.WebSocket.OPEN
21324
+ (sim) => sim.ws.readyState === import_ws.WebSocket.OPEN
21220
21325
  );
21221
21326
  const registered = open.filter((sim) => sim.url);
21222
21327
  return registered.length > 0 ? registered : open;
@@ -21249,7 +21354,7 @@ var init_bridge_host = __esm({
21249
21354
  shouldPromoteSim(sim) {
21250
21355
  const current = this.primarySimId ? this.sims.get(this.primarySimId) : null;
21251
21356
  if (!sim.url) return !current;
21252
- const currentAlive = current?.ws.readyState === import_ws2.WebSocket.OPEN;
21357
+ const currentAlive = current?.ws.readyState === import_ws.WebSocket.OPEN;
21253
21358
  if (!current || !currentAlive || !current.url) return true;
21254
21359
  const isPrimaryCandidate = sim.origin?.includes(":5173");
21255
21360
  const currentIsPrimary = current.origin?.includes(":5173");
@@ -21257,7 +21362,7 @@ var init_bridge_host = __esm({
21257
21362
  }
21258
21363
  broadcastSimAssignments() {
21259
21364
  for (const sim of this.sims.values()) {
21260
- if (sim.ws.readyState !== import_ws2.WebSocket.OPEN) continue;
21365
+ if (sim.ws.readyState !== import_ws.WebSocket.OPEN) continue;
21261
21366
  sim.ws.send(
21262
21367
  JSON.stringify({
21263
21368
  type: "bridge:welcome",
@@ -21274,7 +21379,7 @@ var init_bridge_host = __esm({
21274
21379
  config: next
21275
21380
  });
21276
21381
  for (const peer of this.sims.values()) {
21277
- if (peer.ws.readyState !== import_ws2.WebSocket.OPEN) continue;
21382
+ if (peer.ws.readyState !== import_ws.WebSocket.OPEN) continue;
21278
21383
  try {
21279
21384
  peer.ws.send(payload);
21280
21385
  } catch {
@@ -21284,7 +21389,7 @@ var init_bridge_host = __esm({
21284
21389
  }
21285
21390
  broadcastSimClientStates() {
21286
21391
  for (const sim of this.sims.values()) {
21287
- if (sim.ws.readyState !== import_ws2.WebSocket.OPEN) continue;
21392
+ if (sim.ws.readyState !== import_ws.WebSocket.OPEN) continue;
21288
21393
  const lease = this.getActiveLease(sim);
21289
21394
  const message = {
21290
21395
  type: "bridge:client-state",
@@ -21355,7 +21460,7 @@ var init_bridge_host = __esm({
21355
21460
  const keys = /* @__PURE__ */ new Set();
21356
21461
  for (const [ws, attachedSimId] of this.cliSimBySocket) {
21357
21462
  if (attachedSimId !== simId) continue;
21358
- if (ws.readyState !== import_ws2.WebSocket.OPEN) continue;
21463
+ if (ws.readyState !== import_ws.WebSocket.OPEN) continue;
21359
21464
  const key = this.cliIdentityKeyBySocket.get(ws);
21360
21465
  keys.add(key ?? `ws-unknown-${keys.size}`);
21361
21466
  }
@@ -21368,7 +21473,7 @@ var init_bridge_host = __esm({
21368
21473
  const keys = /* @__PURE__ */ new Set();
21369
21474
  for (const [ws, attachedSimId] of this.cliSimBySocket) {
21370
21475
  if (attachedSimId !== simId) continue;
21371
- if (ws.readyState !== import_ws2.WebSocket.OPEN) continue;
21476
+ if (ws.readyState !== import_ws.WebSocket.OPEN) continue;
21372
21477
  const key = this.cliIdentityKeyBySocket.get(ws);
21373
21478
  if (key && key === selfKey) continue;
21374
21479
  keys.add(key ?? `ws-unknown-${keys.size}`);
@@ -21393,7 +21498,7 @@ var init_bridge_host = __esm({
21393
21498
  const nextId = requestedId?.trim();
21394
21499
  if (!nextId || nextId === sim.id) return false;
21395
21500
  const existing = this.sims.get(nextId);
21396
- if (existing && existing !== sim && existing.ws.readyState === import_ws2.WebSocket.OPEN) {
21501
+ if (existing && existing !== sim && existing.ws.readyState === import_ws.WebSocket.OPEN) {
21397
21502
  return false;
21398
21503
  }
21399
21504
  const restorable = this.getRestorableSimState(nextId);
@@ -21943,31 +22048,31 @@ var init_parent_pid = __esm({
21943
22048
 
21944
22049
  // cli/secure-file.ts
21945
22050
  function writeSecureJsonAtomic(filepath, value) {
21946
- (0, import_node_fs12.mkdirSync)((0, import_node_path13.dirname)(filepath), { recursive: true });
22051
+ (0, import_node_fs13.mkdirSync)((0, import_node_path14.dirname)(filepath), { recursive: true });
21947
22052
  const temporary = `${filepath}.tmp-${process.pid}-${(0, import_node_crypto6.randomUUID)()}`;
21948
22053
  let descriptor = null;
21949
22054
  try {
21950
- descriptor = (0, import_node_fs12.openSync)(temporary, "wx", 384);
21951
- (0, import_node_fs12.fchmodSync)(descriptor, 384);
21952
- (0, import_node_fs12.writeFileSync)(descriptor, `${JSON.stringify(value, null, 2)}
22055
+ descriptor = (0, import_node_fs13.openSync)(temporary, "wx", 384);
22056
+ (0, import_node_fs13.fchmodSync)(descriptor, 384);
22057
+ (0, import_node_fs13.writeFileSync)(descriptor, `${JSON.stringify(value, null, 2)}
21953
22058
  `);
21954
- (0, import_node_fs12.fsyncSync)(descriptor);
21955
- (0, import_node_fs12.closeSync)(descriptor);
22059
+ (0, import_node_fs13.fsyncSync)(descriptor);
22060
+ (0, import_node_fs13.closeSync)(descriptor);
21956
22061
  descriptor = null;
21957
- (0, import_node_fs12.renameSync)(temporary, filepath);
22062
+ (0, import_node_fs13.renameSync)(temporary, filepath);
21958
22063
  } catch (error) {
21959
- if (descriptor !== null) (0, import_node_fs12.closeSync)(descriptor);
21960
- (0, import_node_fs12.rmSync)(temporary, { force: true });
22064
+ if (descriptor !== null) (0, import_node_fs13.closeSync)(descriptor);
22065
+ (0, import_node_fs13.rmSync)(temporary, { force: true });
21961
22066
  throw error;
21962
22067
  }
21963
22068
  }
21964
- var import_node_crypto6, import_node_fs12, import_node_path13;
22069
+ var import_node_crypto6, import_node_fs13, import_node_path14;
21965
22070
  var init_secure_file = __esm({
21966
22071
  "cli/secure-file.ts"() {
21967
22072
  "use strict";
21968
22073
  import_node_crypto6 = require("node:crypto");
21969
- import_node_fs12 = require("node:fs");
21970
- import_node_path13 = require("node:path");
22074
+ import_node_fs13 = require("node:fs");
22075
+ import_node_path14 = require("node:path");
21971
22076
  }
21972
22077
  });
21973
22078
 
@@ -22141,17 +22246,17 @@ function liveDevBridgeLockfiles() {
22141
22246
  return readDevBridgeLockfiles().filter((lockfile) => isDevBridgeLockfileFresh(lockfile));
22142
22247
  }
22143
22248
  function checkoutRoot(cwd) {
22144
- let current = import_node_path14.default.resolve(cwd);
22249
+ let current = import_node_path15.default.resolve(cwd);
22145
22250
  while (true) {
22146
- if (import_node_fs13.default.existsSync(import_node_path14.default.join(current, ".git"))) return current;
22147
- const parent = import_node_path14.default.dirname(current);
22251
+ if (import_node_fs14.default.existsSync(import_node_path15.default.join(current, ".git"))) return current;
22252
+ const parent = import_node_path15.default.dirname(current);
22148
22253
  if (parent === current) return null;
22149
22254
  current = parent;
22150
22255
  }
22151
22256
  }
22152
22257
  function pathContains(parent, child) {
22153
- const relative = import_node_path14.default.relative(parent, child);
22154
- return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path14.default.sep}`) && !import_node_path14.default.isAbsolute(relative);
22258
+ const relative = import_node_path15.default.relative(parent, child);
22259
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path15.default.sep}`) && !import_node_path15.default.isAbsolute(relative);
22155
22260
  }
22156
22261
  function resolveDefaultDevBridgeLockfile() {
22157
22262
  const lockfiles = liveDevBridgeLockfiles();
@@ -22357,7 +22462,7 @@ function createBridge(wsPort, opts = {}) {
22357
22462
  let nextId = 1;
22358
22463
  const commandTimeoutMs = opts.commandTimeoutMs ?? 15e3;
22359
22464
  const pending = /* @__PURE__ */ new Map();
22360
- const ws = new import_ws3.WebSocket(bridgeWsUrl(wsPort));
22465
+ const ws = new import_ws2.WebSocket(bridgeWsUrl(wsPort));
22361
22466
  const identity = getCliIdentity();
22362
22467
  const ready = new Promise((resolve7, reject) => {
22363
22468
  ws.on("open", () => {
@@ -22504,14 +22609,14 @@ function createBridge(wsPort, opts = {}) {
22504
22609
  },
22505
22610
  close() {
22506
22611
  try {
22507
- if (ws.readyState === import_ws3.WebSocket.OPEN) {
22612
+ if (ws.readyState === import_ws2.WebSocket.OPEN) {
22508
22613
  ws.send(JSON.stringify({ type: "bridge:bye", id: 0 }));
22509
22614
  }
22510
22615
  } catch {
22511
22616
  }
22512
22617
  ws.close();
22513
22618
  const terminateGrace = setTimeout(() => {
22514
- if (ws.readyState !== import_ws3.WebSocket.CLOSED) {
22619
+ if (ws.readyState !== import_ws2.WebSocket.CLOSED) {
22515
22620
  ws.terminate();
22516
22621
  }
22517
22622
  }, 250);
@@ -22584,13 +22689,13 @@ async function callInBridge(bridge, path20, ...args) {
22584
22689
  async function callInBridgeWrite(bridge, path20, ...args) {
22585
22690
  return bridge.send({ type: "call", path: path20, args, acquireLock: true });
22586
22691
  }
22587
- var import_node_fs13, import_node_path14, import_ws3, RNX_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError;
22692
+ var import_node_fs14, import_node_path15, import_ws2, RNX_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError;
22588
22693
  var init_ws_bridge = __esm({
22589
22694
  "cli/ws-bridge.ts"() {
22590
22695
  "use strict";
22591
- import_node_fs13 = __toESM(require("node:fs"), 1);
22592
- import_node_path14 = __toESM(require("node:path"), 1);
22593
- import_ws3 = require("ws");
22696
+ import_node_fs14 = __toESM(require("node:fs"), 1);
22697
+ import_node_path15 = __toESM(require("node:path"), 1);
22698
+ import_ws2 = require("ws");
22594
22699
  init_bridge_constants();
22595
22700
  init_cli_constants();
22596
22701
  init_home_paths();
@@ -22954,9 +23059,9 @@ var init_profiles = __esm({
22954
23059
  // cli/app-project.ts
22955
23060
  function firstNamedFile(dir, names) {
22956
23061
  for (const name2 of names) {
22957
- const candidate = import_node_path15.default.join(dir, name2);
23062
+ const candidate = import_node_path16.default.join(dir, name2);
22958
23063
  try {
22959
- if (import_node_fs14.default.statSync(candidate).isFile()) return candidate;
23064
+ if (import_node_fs15.default.statSync(candidate).isFile()) return candidate;
22960
23065
  } catch {
22961
23066
  }
22962
23067
  }
@@ -22969,24 +23074,24 @@ function findRNXAppConfig(dir) {
22969
23074
  return firstNamedFile(dir, RNX_APP_CONFIG_NAMES);
22970
23075
  }
22971
23076
  function resolveAppProject(projectRoot) {
22972
- const appDir = import_node_path15.default.resolve(projectRoot);
23077
+ const appDir = import_node_path16.default.resolve(projectRoot);
22973
23078
  try {
22974
- if (!import_node_fs14.default.statSync(appDir).isDirectory()) return null;
23079
+ if (!import_node_fs15.default.statSync(appDir).isDirectory()) return null;
22975
23080
  } catch {
22976
23081
  return null;
22977
23082
  }
22978
23083
  const configPath = findRNXAppConfig(appDir);
22979
- if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs14.default.existsSync(import_node_path15.default.join(appDir, "package.json"))) {
23084
+ if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs15.default.existsSync(import_node_path16.default.join(appDir, "package.json"))) {
22980
23085
  return null;
22981
23086
  }
22982
23087
  return { appDir, configPath };
22983
23088
  }
22984
- var import_node_fs14, import_node_path15, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
23089
+ var import_node_fs15, import_node_path16, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
22985
23090
  var init_app_project = __esm({
22986
23091
  "cli/app-project.ts"() {
22987
23092
  "use strict";
22988
- import_node_fs14 = __toESM(require("node:fs"), 1);
22989
- import_node_path15 = __toESM(require("node:path"), 1);
23093
+ import_node_fs15 = __toESM(require("node:fs"), 1);
23094
+ import_node_path16 = __toESM(require("node:path"), 1);
22990
23095
  EXPO_APP_CONFIG_NAMES = [
22991
23096
  "app.config.js",
22992
23097
  "app.config.ts",
@@ -23015,13 +23120,13 @@ function isRNXConfig(value) {
23015
23120
  }
23016
23121
  function resolveConfigReplacementFiles(config, configPath) {
23017
23122
  if (!config.modules) return config;
23018
- const configDir = import_node_path16.default.dirname(configPath);
23123
+ const configDir = import_node_path17.default.dirname(configPath);
23019
23124
  return {
23020
23125
  ...config,
23021
23126
  modules: Object.fromEntries(
23022
23127
  Object.entries(config.modules).map(([name2, resolution]) => [
23023
23128
  name2,
23024
- resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path16.default.resolve(configDir, resolution.file) } : resolution
23129
+ resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path17.default.resolve(configDir, resolution.file) } : resolution
23025
23130
  ])
23026
23131
  )
23027
23132
  };
@@ -23051,12 +23156,12 @@ async function resolveRNXAppConfig(startDir = process.cwd()) {
23051
23156
  const project = resolveAppProject(startDir);
23052
23157
  return { project, config: await loadRNXAppConfig(project) };
23053
23158
  }
23054
- var import_node_module, import_node_path16, import_jiti, require2;
23159
+ var import_node_module, import_node_path17, import_jiti, require2;
23055
23160
  var init_app_config = __esm({
23056
23161
  "cli/app-config.ts"() {
23057
23162
  "use strict";
23058
23163
  import_node_module = require("node:module");
23059
- import_node_path16 = __toESM(require("node:path"), 1);
23164
+ import_node_path17 = __toESM(require("node:path"), 1);
23060
23165
  init_src();
23061
23166
  import_jiti = require("jiti");
23062
23167
  init_public_brand();
@@ -23124,13 +23229,13 @@ function discoverNativeLinkedAppFonts(opts) {
23124
23229
  scannedFontDirectories++;
23125
23230
  let entries;
23126
23231
  try {
23127
- entries = import_node_fs15.default.readdirSync(current.dir, { withFileTypes: true });
23232
+ entries = import_node_fs16.default.readdirSync(current.dir, { withFileTypes: true });
23128
23233
  } catch {
23129
23234
  continue;
23130
23235
  }
23131
23236
  for (const entry of entries) {
23132
23237
  if (entry.isSymbolicLink()) continue;
23133
- const fullPath = import_node_path17.default.join(current.dir, entry.name);
23238
+ const fullPath = import_node_path18.default.join(current.dir, entry.name);
23134
23239
  if (entry.isDirectory()) {
23135
23240
  if (current.depth < 4 && pending.length < 64 && !entry.name.startsWith(".")) {
23136
23241
  pending.push({ dir: fullPath, depth: current.depth + 1 });
@@ -23138,15 +23243,15 @@ function discoverNativeLinkedAppFonts(opts) {
23138
23243
  continue;
23139
23244
  }
23140
23245
  if (!entry.isFile() || !/\.(ttf|otf|woff|woff2)$/i.test(entry.name)) continue;
23141
- const relative = import_node_path17.default.relative(root, fullPath).split(import_node_path17.default.sep).join("/");
23142
- fontPaths.push(import_node_path17.default.posix.join(metroPathPrefix, relative));
23246
+ const relative = import_node_path18.default.relative(root, fullPath).split(import_node_path18.default.sep).join("/");
23247
+ fontPaths.push(import_node_path18.default.posix.join(metroPathPrefix, relative));
23143
23248
  if (fontPaths.length >= 256) break;
23144
23249
  }
23145
23250
  }
23146
23251
  };
23147
23252
  try {
23148
23253
  const packageJson = JSON.parse(
23149
- import_node_fs15.default.readFileSync(import_node_path17.default.join(selectedAppDir, "package.json"), "utf8")
23254
+ import_node_fs16.default.readFileSync(import_node_path18.default.join(selectedAppDir, "package.json"), "utf8")
23150
23255
  );
23151
23256
  const dependencyNames = /* @__PURE__ */ new Set();
23152
23257
  for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
@@ -23161,24 +23266,24 @@ function discoverNativeLinkedAppFonts(opts) {
23161
23266
  const config = Reflect.get(packageJson, "reactNativeVectorIcons");
23162
23267
  const configuredFontDir = config && typeof config === "object" ? Reflect.get(config, "fontDir") : null;
23163
23268
  const configuredDir = typeof configuredFontDir === "string" ? configuredFontDir : "rnvi-fonts";
23164
- const root = import_node_path17.default.resolve(selectedAppDir, configuredDir);
23165
- const relativeRoot = import_node_path17.default.relative(selectedAppDir, root);
23166
- if (!relativeRoot.startsWith("..") && !import_node_path17.default.isAbsolute(relativeRoot)) {
23167
- collectFontFiles(root, relativeRoot.split(import_node_path17.default.sep).join("/"));
23269
+ const root = import_node_path18.default.resolve(selectedAppDir, configuredDir);
23270
+ const relativeRoot = import_node_path18.default.relative(selectedAppDir, root);
23271
+ if (!relativeRoot.startsWith("..") && !import_node_path18.default.isAbsolute(relativeRoot)) {
23272
+ collectFontFiles(root, relativeRoot.split(import_node_path18.default.sep).join("/"));
23168
23273
  }
23169
- const vectorIconRequire = (0, import_node_module2.createRequire)(import_node_path17.default.join(selectedAppDir, "package.json"));
23274
+ const vectorIconRequire = (0, import_node_module2.createRequire)(import_node_path18.default.join(selectedAppDir, "package.json"));
23170
23275
  for (const dependencyName of vectorIconDependencies) {
23171
23276
  try {
23172
23277
  const entryPath = vectorIconRequire.resolve(dependencyName);
23173
- const dependencyPath = dependencyName.split("/").join(import_node_path17.default.sep);
23174
- const marker = `${import_node_path17.default.sep}node_modules${import_node_path17.default.sep}${dependencyPath}${import_node_path17.default.sep}`;
23278
+ const dependencyPath = dependencyName.split("/").join(import_node_path18.default.sep);
23279
+ const marker = `${import_node_path18.default.sep}node_modules${import_node_path18.default.sep}${dependencyPath}${import_node_path18.default.sep}`;
23175
23280
  const markerIndex = entryPath.lastIndexOf(marker);
23176
23281
  if (markerIndex === -1) continue;
23177
23282
  const packageRoot = entryPath.slice(0, markerIndex + marker.length - 1);
23178
23283
  const fontsDir = dependencyName === "react-native-vector-icons" ? "Fonts" : "fonts";
23179
23284
  collectFontFiles(
23180
- import_node_path17.default.join(packageRoot, fontsDir),
23181
- import_node_path17.default.posix.join("node_modules", dependencyName, fontsDir)
23285
+ import_node_path18.default.join(packageRoot, fontsDir),
23286
+ import_node_path18.default.posix.join("node_modules", dependencyName, fontsDir)
23182
23287
  );
23183
23288
  } catch {
23184
23289
  }
@@ -23187,37 +23292,37 @@ function discoverNativeLinkedAppFonts(opts) {
23187
23292
  } catch {
23188
23293
  }
23189
23294
  try {
23190
- const configPath = import_node_path17.default.join(selectedAppDir, "react-native.config.js");
23191
- if (import_node_fs15.default.statSync(configPath).isFile()) {
23192
- const appConfigRequire = (0, import_node_module2.createRequire)(import_node_path17.default.join(selectedAppDir, "package.json"));
23295
+ const configPath = import_node_path18.default.join(selectedAppDir, "react-native.config.js");
23296
+ if (import_node_fs16.default.statSync(configPath).isFile()) {
23297
+ const appConfigRequire = (0, import_node_module2.createRequire)(import_node_path18.default.join(selectedAppDir, "package.json"));
23193
23298
  const rnConfig = appConfigRequire(configPath);
23194
23299
  const assets = rnConfig && typeof rnConfig === "object" ? Reflect.get(rnConfig, "assets") : null;
23195
23300
  if (Array.isArray(assets)) {
23196
23301
  for (const assetDir of assets) {
23197
23302
  if (typeof assetDir !== "string") continue;
23198
- const root = import_node_path17.default.resolve(selectedAppDir, assetDir);
23199
- const relativeRoot = import_node_path17.default.relative(selectedAppDir, root);
23200
- if (relativeRoot.startsWith("..") || import_node_path17.default.isAbsolute(relativeRoot)) continue;
23201
- collectFontFiles(root, relativeRoot.split(import_node_path17.default.sep).join("/"));
23303
+ const root = import_node_path18.default.resolve(selectedAppDir, assetDir);
23304
+ const relativeRoot = import_node_path18.default.relative(selectedAppDir, root);
23305
+ if (relativeRoot.startsWith("..") || import_node_path18.default.isAbsolute(relativeRoot)) continue;
23306
+ collectFontFiles(root, relativeRoot.split(import_node_path18.default.sep).join("/"));
23202
23307
  }
23203
23308
  }
23204
23309
  }
23205
23310
  } catch {
23206
23311
  }
23207
23312
  if (platform === "ios") {
23208
- const iosDir = import_node_path17.default.join(selectedAppDir, "ios");
23313
+ const iosDir = import_node_path18.default.join(selectedAppDir, "ios");
23209
23314
  try {
23210
- const targetDirs = import_node_fs15.default.readdirSync(iosDir, { withFileTypes: true }).filter(
23315
+ const targetDirs = import_node_fs16.default.readdirSync(iosDir, { withFileTypes: true }).filter(
23211
23316
  (entry) => entry.isDirectory() && !entry.isSymbolicLink() && entry.name !== "Pods" && entry.name !== "build" && !entry.name.startsWith(".")
23212
23317
  ).slice(0, 64);
23213
- const plistPaths = [import_node_path17.default.join(iosDir, "Info.plist")];
23318
+ const plistPaths = [import_node_path18.default.join(iosDir, "Info.plist")];
23214
23319
  for (const entry of targetDirs) {
23215
- plistPaths.push(import_node_path17.default.join(iosDir, entry.name, "Info.plist"));
23320
+ plistPaths.push(import_node_path18.default.join(iosDir, entry.name, "Info.plist"));
23216
23321
  }
23217
23322
  for (const plistPath of plistPaths) {
23218
23323
  let parsed;
23219
23324
  try {
23220
- parsed = parsePlistSource(import_node_fs15.default.readFileSync(plistPath, "utf8"));
23325
+ parsed = parsePlistSource(import_node_fs16.default.readFileSync(plistPath, "utf8"));
23221
23326
  } catch {
23222
23327
  continue;
23223
23328
  }
@@ -23227,21 +23332,21 @@ function discoverNativeLinkedAppFonts(opts) {
23227
23332
  for (const declaredFont of declaredFonts) {
23228
23333
  if (typeof declaredFont !== "string") continue;
23229
23334
  const candidates = [
23230
- import_node_path17.default.resolve(import_node_path17.default.dirname(plistPath), declaredFont),
23231
- import_node_path17.default.resolve(iosDir, declaredFont)
23335
+ import_node_path18.default.resolve(import_node_path18.default.dirname(plistPath), declaredFont),
23336
+ import_node_path18.default.resolve(iosDir, declaredFont)
23232
23337
  ];
23233
23338
  const fontPath = candidates.find((candidate) => {
23234
- const relative = import_node_path17.default.relative(selectedAppDir, candidate);
23235
- if (relative.startsWith("..") || import_node_path17.default.isAbsolute(relative)) return false;
23339
+ const relative = import_node_path18.default.relative(selectedAppDir, candidate);
23340
+ if (relative.startsWith("..") || import_node_path18.default.isAbsolute(relative)) return false;
23236
23341
  try {
23237
- return import_node_fs15.default.statSync(candidate).isFile();
23342
+ return import_node_fs16.default.statSync(candidate).isFile();
23238
23343
  } catch {
23239
23344
  return false;
23240
23345
  }
23241
23346
  });
23242
23347
  if (!fontPath) continue;
23243
23348
  fontPaths.push(
23244
- import_node_path17.default.relative(selectedAppDir, fontPath).split(import_node_path17.default.sep).join("/")
23349
+ import_node_path18.default.relative(selectedAppDir, fontPath).split(import_node_path18.default.sep).join("/")
23245
23350
  );
23246
23351
  }
23247
23352
  }
@@ -23280,13 +23385,13 @@ async function resolveMetroProjectRoot(bundleUrl, opts) {
23280
23385
  function encodeAppFontsWire(specs) {
23281
23386
  return specs.map((spec) => `${encodeURIComponent(spec.url)}::${encodeURIComponent(spec.family)}`).join(",");
23282
23387
  }
23283
- var import_node_fs15, import_node_module2, import_node_path17, import_config_plugin_fonts, require3;
23388
+ var import_node_fs16, import_node_module2, import_node_path18, import_config_plugin_fonts, require3;
23284
23389
  var init_app_fonts = __esm({
23285
23390
  "cli/app-fonts.ts"() {
23286
23391
  "use strict";
23287
- import_node_fs15 = __toESM(require("node:fs"), 1);
23392
+ import_node_fs16 = __toESM(require("node:fs"), 1);
23288
23393
  import_node_module2 = require("node:module");
23289
- import_node_path17 = __toESM(require("node:path"), 1);
23394
+ import_node_path18 = __toESM(require("node:path"), 1);
23290
23395
  import_config_plugin_fonts = require("sootsim-engine/engine/config-plugin-fonts");
23291
23396
  init_dev_server_scanner();
23292
23397
  init_plist();
@@ -24382,7 +24487,7 @@ function resolvePlaywright() {
24382
24487
  try {
24383
24488
  const modulePath = requireFromRoot.resolve(spec);
24384
24489
  const packageJsonPath = requireFromRoot.resolve(`${spec}/package.json`);
24385
- const parsed = JSON.parse((0, import_node_fs16.readFileSync)(packageJsonPath, "utf8"));
24490
+ const parsed = JSON.parse((0, import_node_fs17.readFileSync)(packageJsonPath, "utf8"));
24386
24491
  if (!isRecord5(parsed) || typeof parsed.version !== "string") continue;
24387
24492
  const bin = parsed.bin;
24388
24493
  let binPath = null;
@@ -24394,8 +24499,8 @@ function resolvePlaywright() {
24394
24499
  binPath = typeof named === "string" ? named : typeof first === "string" ? first : null;
24395
24500
  }
24396
24501
  if (!binPath) continue;
24397
- const cliPath = (0, import_node_path18.join)((0, import_node_path18.dirname)(packageJsonPath), binPath);
24398
- if (!(0, import_node_fs16.existsSync)(cliPath)) continue;
24502
+ const cliPath = (0, import_node_path19.join)((0, import_node_path19.dirname)(packageJsonPath), binPath);
24503
+ if (!(0, import_node_fs17.existsSync)(cliPath)) continue;
24399
24504
  return {
24400
24505
  spec,
24401
24506
  modulePath,
@@ -24439,7 +24544,7 @@ function probeBrowser(resolved, env) {
24439
24544
  return {
24440
24545
  ok: true,
24441
24546
  executablePath: parsed.executablePath,
24442
- exists: parsed.exists && (0, import_node_fs16.existsSync)(parsed.executablePath)
24547
+ exists: parsed.exists && (0, import_node_fs17.existsSync)(parsed.executablePath)
24443
24548
  };
24444
24549
  } catch (error) {
24445
24550
  return {
@@ -24490,14 +24595,14 @@ ${detail}` : ""}`
24490
24595
  }
24491
24596
  return { ok: true, executablePath: after.executablePath, installed: true };
24492
24597
  }
24493
- var import_node_child_process6, import_node_fs16, import_node_module4, import_node_path18;
24598
+ var import_node_child_process6, import_node_fs17, import_node_module4, import_node_path19;
24494
24599
  var init_playwright_provisioning = __esm({
24495
24600
  "cli/drivers/playwright-provisioning.ts"() {
24496
24601
  "use strict";
24497
24602
  import_node_child_process6 = require("node:child_process");
24498
- import_node_fs16 = require("node:fs");
24603
+ import_node_fs17 = require("node:fs");
24499
24604
  import_node_module4 = require("node:module");
24500
- import_node_path18 = require("node:path");
24605
+ import_node_path19 = require("node:path");
24501
24606
  init_internal_child();
24502
24607
  init_self_invocation();
24503
24608
  }
@@ -24888,7 +24993,7 @@ async function tellDaemonRuntimeChanged(version) {
24888
24993
  settled = true;
24889
24994
  resolve7(ok);
24890
24995
  };
24891
- const ws = new import_ws4.WebSocket(`ws://127.0.0.1:${port}`, {
24996
+ const ws = new import_ws3.WebSocket(`ws://127.0.0.1:${port}`, {
24892
24997
  handshakeTimeout: 800
24893
24998
  });
24894
24999
  const timer = setTimeout(() => {
@@ -25042,13 +25147,13 @@ function describeError(err) {
25042
25147
  if (err instanceof Error) return err.message;
25043
25148
  return String(err);
25044
25149
  }
25045
- var import_fs8, import_ws4;
25150
+ var import_fs8, import_ws3;
25046
25151
  var init_runtime = __esm({
25047
25152
  "cli/commands/runtime.ts"() {
25048
25153
  "use strict";
25049
25154
  import_fs8 = __toESM(require("fs"), 1);
25050
25155
  init_src();
25051
- import_ws4 = require("ws");
25156
+ import_ws3 = require("ws");
25052
25157
  init_home_paths();
25053
25158
  init_runtime_delivery();
25054
25159
  init_app_config();
@@ -25172,13 +25277,18 @@ function assertExplicitBridgeLock(preferredPort, explicitPort, lock) {
25172
25277
  if (!explicitPort || !lock || !isDaemonLockfileFresh(lock) || lock.bridgePort === preferredPort) {
25173
25278
  return;
25174
25279
  }
25280
+ if (readDevBridgeLockfiles().some(
25281
+ (devBridge) => devBridge.bridgePort === preferredPort && isDevBridgeLockfileFresh(devBridge)
25282
+ )) {
25283
+ return;
25284
+ }
25175
25285
  throw new Error(
25176
25286
  `${rnxPublicBrand.commandName} open: requested bridge port ${preferredPort}, but the active daemon owns port ${lock.bridgePort}. explicit --port never selects another bridge; stop that daemon or use an isolated RNX_HOME.`
25177
25287
  );
25178
25288
  }
25179
25289
  async function ensureRuntimeForOpen() {
25180
25290
  const active = readActiveRuntime();
25181
- if (active && import_node_fs17.default.existsSync(runtimeDir(active))) return;
25291
+ if (active && import_node_fs18.default.existsSync(runtimeDir(active))) return;
25182
25292
  console.error(" installing rnx engine runtime...");
25183
25293
  const { runRuntime: runRuntime2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
25184
25294
  await runRuntime2(["install"], {});
@@ -25193,13 +25303,13 @@ function spawnSootsim(args) {
25193
25303
  }
25194
25304
  function canConnectToBridge(port, timeoutMs) {
25195
25305
  return new Promise((resolve7) => {
25196
- const ws = new import_ws5.WebSocket(`ws://127.0.0.1:${port}`, { handshakeTimeout: timeoutMs });
25306
+ const ws = new import_ws4.WebSocket(`ws://127.0.0.1:${port}`, { handshakeTimeout: timeoutMs });
25197
25307
  let settled = false;
25198
25308
  const finish = (ok) => {
25199
25309
  if (settled) return;
25200
25310
  settled = true;
25201
- if (ws.readyState === import_ws5.WebSocket.OPEN) ws.close();
25202
- else if (ws.readyState === import_ws5.WebSocket.CONNECTING) ws.terminate();
25311
+ if (ws.readyState === import_ws4.WebSocket.OPEN) ws.close();
25312
+ else if (ws.readyState === import_ws4.WebSocket.CONNECTING) ws.terminate();
25203
25313
  resolve7(ok);
25204
25314
  };
25205
25315
  ws.once("open", () => finish(true));
@@ -25325,10 +25435,10 @@ async function buildResolvedBundleOpenUrl(target, baseUrl, opts = {}) {
25325
25435
  }
25326
25436
  function normalizeReplacementPath(filePath) {
25327
25437
  if (filePath.startsWith("~/")) {
25328
- return import_node_path19.default.join(import_node_os3.default.homedir(), filePath.slice(2));
25438
+ return import_node_path20.default.join(import_node_os3.default.homedir(), filePath.slice(2));
25329
25439
  }
25330
- if (import_node_path19.default.isAbsolute(filePath)) return filePath;
25331
- return import_node_path19.default.resolve(process.cwd(), filePath);
25440
+ if (import_node_path20.default.isAbsolute(filePath)) return filePath;
25441
+ return import_node_path20.default.resolve(process.cwd(), filePath);
25332
25442
  }
25333
25443
  function parseOpenRuntimeConfig(args) {
25334
25444
  const modules = {};
@@ -25347,7 +25457,7 @@ function parseOpenRuntimeConfig(args) {
25347
25457
  }
25348
25458
  const moduleName = raw.slice(0, eqIndex).trim();
25349
25459
  const filePath = normalizeReplacementPath(raw.slice(eqIndex + 1).trim());
25350
- if (!import_node_fs17.default.existsSync(filePath)) {
25460
+ if (!import_node_fs18.default.existsSync(filePath)) {
25351
25461
  console.error(` rnx open: replacement file not found: ${filePath}`);
25352
25462
  rnxExit(1);
25353
25463
  }
@@ -25378,7 +25488,7 @@ async function resolveOpenAppProject(resolvedBundle) {
25378
25488
  return advertisedRoot ? resolveAppProject(advertisedRoot) : null;
25379
25489
  }
25380
25490
  async function ensureConfiguredRuntime(version) {
25381
- if (import_node_fs17.default.existsSync(import_node_path19.default.join(runtimeDir(version), "index.html"))) return;
25491
+ if (import_node_fs18.default.existsSync(import_node_path20.default.join(runtimeDir(version), "index.html"))) return;
25382
25492
  console.error(` installing configured engine runtime ${version}...`);
25383
25493
  const result = await rnxRuntime.install({
25384
25494
  version,
@@ -25454,7 +25564,13 @@ async function resolveShellUrl(target, baseUrl, opts = {}) {
25454
25564
  return new URL(baseUrl).toString();
25455
25565
  }
25456
25566
  if (looksLikeSootsimUrl(target)) {
25457
- return new URL(target).toString();
25567
+ const url = new URL(target);
25568
+ const base = new URL(baseUrl);
25569
+ const baseConfig = base.searchParams.get(RNX_CONFIG_QUERY_PARAM);
25570
+ if (baseConfig && !url.searchParams.has(RNX_CONFIG_QUERY_PARAM)) {
25571
+ url.searchParams.set(RNX_CONFIG_QUERY_PARAM, baseConfig);
25572
+ }
25573
+ return url.toString();
25458
25574
  }
25459
25575
  if (looksLikeShellPathTarget(target)) {
25460
25576
  return buildShellPathOpenUrl(target, baseUrl);
@@ -25738,7 +25854,7 @@ function playwrightHostPidForSim(sim) {
25738
25854
  function signalDriverLaunchConnected(result) {
25739
25855
  if (!result.connectAckFile) return false;
25740
25856
  try {
25741
- import_node_fs17.default.writeFileSync(
25857
+ import_node_fs18.default.writeFileSync(
25742
25858
  result.connectAckFile,
25743
25859
  `${JSON.stringify({ connectedAt: Date.now(), pid: result.pid ?? null })}
25744
25860
  `,
@@ -25752,19 +25868,19 @@ function signalDriverLaunchConnected(result) {
25752
25868
  function printDriverDiagnosticLogTail(result) {
25753
25869
  if (!result.diagnosticLogPath) return;
25754
25870
  try {
25755
- const detail = import_node_fs17.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
25871
+ const detail = import_node_fs18.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
25756
25872
  if (!detail) {
25757
- console.error(` ${import_node_path19.default.basename(result.diagnosticLogPath)} was empty`);
25873
+ console.error(` ${import_node_path20.default.basename(result.diagnosticLogPath)} was empty`);
25758
25874
  return;
25759
25875
  }
25760
25876
  const shown = detail.length > 4e3 ? `\u2026
25761
25877
  ${detail.slice(-4e3)}` : detail;
25762
- console.error(` ${import_node_path19.default.basename(result.diagnosticLogPath)}:`);
25878
+ console.error(` ${import_node_path20.default.basename(result.diagnosticLogPath)}:`);
25763
25879
  console.error(shown);
25764
25880
  } catch (err) {
25765
25881
  const message = err instanceof Error ? err.message : String(err);
25766
25882
  console.error(
25767
- ` failed to read ${import_node_path19.default.basename(result.diagnosticLogPath)}: ${message}`
25883
+ ` failed to read ${import_node_path20.default.basename(result.diagnosticLogPath)}: ${message}`
25768
25884
  );
25769
25885
  }
25770
25886
  }
@@ -26575,16 +26691,16 @@ async function runCloseCommand(args, opts = {}) {
26575
26691
  bridge.close();
26576
26692
  }
26577
26693
  }
26578
- var import_node_child_process7, import_node_fs17, import_node_os3, import_node_path19, import_settings, import_ws5, DEFAULT_DRIVER_CONNECT_TIMEOUT_MS, DRIVER_CONNECT_INTERVAL_MS, DRIVER_CONNECT_HOST_GRACE_MS, OPEN_BRIDGE_SPAWN_TIMEOUT_MS, SHELL_APPS;
26694
+ var import_node_child_process7, import_node_fs18, import_node_os3, import_node_path20, import_settings, import_ws4, DEFAULT_DRIVER_CONNECT_TIMEOUT_MS, DRIVER_CONNECT_INTERVAL_MS, DRIVER_CONNECT_HOST_GRACE_MS, OPEN_BRIDGE_SPAWN_TIMEOUT_MS, SHELL_APPS;
26579
26695
  var init_control = __esm({
26580
26696
  "cli/commands/control.ts"() {
26581
26697
  "use strict";
26582
26698
  import_node_child_process7 = require("node:child_process");
26583
- import_node_fs17 = __toESM(require("node:fs"), 1);
26699
+ import_node_fs18 = __toESM(require("node:fs"), 1);
26584
26700
  import_node_os3 = __toESM(require("node:os"), 1);
26585
- import_node_path19 = __toESM(require("node:path"), 1);
26701
+ import_node_path20 = __toESM(require("node:path"), 1);
26586
26702
  import_settings = require("sootsim-engine/settings");
26587
- import_ws5 = require("ws");
26703
+ import_ws4 = require("ws");
26588
26704
  init_dev_server_scanner();
26589
26705
  init_app_url();
26590
26706
  init_backend_origin();
@@ -27436,14 +27552,14 @@ var init_maestro_js = __esm({
27436
27552
  function getBaseDir() {
27437
27553
  const explicit = process.env[SESSION_FILE_ENV];
27438
27554
  if (explicit?.trim()) {
27439
- return (0, import_node_path20.dirname)((0, import_node_path20.resolve)(explicit));
27555
+ return (0, import_node_path21.dirname)((0, import_node_path21.resolve)(explicit));
27440
27556
  }
27441
27557
  return electronUserDataDir();
27442
27558
  }
27443
27559
  function getSharedDesktopAuthFilePath() {
27444
27560
  const explicit = process.env[SESSION_FILE_ENV];
27445
- if (explicit?.trim()) return (0, import_node_path20.resolve)(explicit);
27446
- return (0, import_node_path20.join)(getBaseDir(), "desktop-auth.json");
27561
+ if (explicit?.trim()) return (0, import_node_path21.resolve)(explicit);
27562
+ return (0, import_node_path21.join)(getBaseDir(), "desktop-auth.json");
27447
27563
  }
27448
27564
  function normalizeUser(input) {
27449
27565
  if (!input || typeof input !== "object") return null;
@@ -27490,26 +27606,26 @@ function normalizeSession(input) {
27490
27606
  }
27491
27607
  function readSharedDesktopAuthSession() {
27492
27608
  const filepath = getSharedDesktopAuthFilePath();
27493
- if (!(0, import_node_fs18.existsSync)(filepath)) return null;
27609
+ if (!(0, import_node_fs19.existsSync)(filepath)) return null;
27494
27610
  try {
27495
- const parsed = JSON.parse((0, import_node_fs18.readFileSync)(filepath, "utf8"));
27611
+ const parsed = JSON.parse((0, import_node_fs19.readFileSync)(filepath, "utf8"));
27496
27612
  const normalized = normalizeSession(parsed);
27497
27613
  if (!normalized) {
27498
- (0, import_node_fs18.rmSync)(filepath, { force: true });
27614
+ (0, import_node_fs19.rmSync)(filepath, { force: true });
27499
27615
  return null;
27500
27616
  }
27501
27617
  return normalized;
27502
27618
  } catch {
27503
- (0, import_node_fs18.rmSync)(filepath, { force: true });
27619
+ (0, import_node_fs19.rmSync)(filepath, { force: true });
27504
27620
  return null;
27505
27621
  }
27506
27622
  }
27507
- var import_node_fs18, import_node_path20, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
27623
+ var import_node_fs19, import_node_path21, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
27508
27624
  var init_shared_session = __esm({
27509
27625
  "src/auth/shared-session.ts"() {
27510
27626
  "use strict";
27511
- import_node_fs18 = require("node:fs");
27512
- import_node_path20 = require("node:path");
27627
+ import_node_fs19 = require("node:fs");
27628
+ import_node_path21 = require("node:path");
27513
27629
  init_home_paths();
27514
27630
  SESSION_VERSION = 1;
27515
27631
  SESSION_FILE_ENV = "SOOTSIM_SHARED_AUTH_FILE";