rnxsim 0.1.431 → 0.1.433

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 (78) hide show
  1. package/README.md +20 -50
  2. package/cli/auth.ts +33 -3
  3. package/cli/cloud-client.ts +524 -88
  4. package/cli/cloud-dispatch.ts +5 -10
  5. package/cli/cloud-session.ts +5 -5
  6. package/cli/commands/auth.ts +13 -2
  7. package/cli/commands/box.ts +189 -20
  8. package/cli/commands/control.ts +40 -13
  9. package/cli/commands/device.ts +1 -1
  10. package/cli/commands/flow.ts +189 -43
  11. package/cli/commands/inspect/get-layout.ts +1 -1
  12. package/cli/commands/inspect.ts +8 -1
  13. package/cli/commands/login.ts +79 -14
  14. package/cli/commands/maestro-generate.ts +70 -54
  15. package/cli/commands/maestro.ts +69 -20
  16. package/cli/commands/platform.ts +30 -32
  17. package/cli/commands/screenshot.ts +3 -2
  18. package/cli/commands/upload.ts +28 -10
  19. package/cli/help-core.ts +11 -1
  20. package/cli/outbound-endpoints.ts +27 -5
  21. package/cli/parse-args.ts +17 -0
  22. package/cli/shell-init.ts +4 -4
  23. package/cli/ws-bridge.ts +109 -62
  24. package/dist-lib/agent-daemon-client.cjs +1 -1
  25. package/dist-lib/agent-events.cjs +1 -1
  26. package/dist-lib/agent-identity.cjs +1 -1
  27. package/dist-lib/agent-sessions.cjs +1 -1
  28. package/dist-lib/attached-projects.cjs +1 -1
  29. package/dist-lib/auth/shared-session.cjs +17 -4
  30. package/dist-lib/backend-origin.cjs +1 -1
  31. package/dist-lib/beta.cjs +1 -1
  32. package/dist-lib/beta.mjs +1 -1
  33. package/dist-lib/bridge-constants.cjs +1 -1
  34. package/dist-lib/bridge-contract-input.cjs +1 -1
  35. package/dist-lib/bridge-contract-input.mjs +1 -1
  36. package/dist-lib/bridge-contract.cjs +1 -1
  37. package/dist-lib/bridge-contract.mjs +1 -1
  38. package/dist-lib/capture-contract.cjs +1 -1
  39. package/dist-lib/capture-contract.mjs +1 -1
  40. package/dist-lib/cli-constants.cjs +1 -1
  41. package/dist-lib/cloud-contract.cjs +1 -1
  42. package/dist-lib/cloud-contract.mjs +1 -1
  43. package/dist-lib/cloud.cjs +108 -93
  44. package/dist-lib/cloud.mjs +106 -91
  45. package/dist-lib/config.cjs +1 -1
  46. package/dist-lib/detox/index.cjs +1 -1
  47. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  48. package/dist-lib/home-paths.cjs +1 -1
  49. package/dist-lib/host/bridge-host.cjs +1 -1
  50. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  51. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  52. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  53. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  54. package/dist-lib/host/websocket-proxy.cjs +1 -1
  55. package/dist-lib/index.cjs +58 -139
  56. package/dist-lib/jump-to-source-babel.cjs +1 -1
  57. package/dist-lib/menu.cjs +1 -1
  58. package/dist-lib/menu.mjs +1 -1
  59. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  60. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  61. package/dist-lib/metro-production-bundle.cjs +1 -1
  62. package/dist-lib/metro-production-bundle.mjs +1 -1
  63. package/dist-lib/metro.cjs +33 -212
  64. package/dist-lib/profiles.cjs +1 -1
  65. package/dist-lib/public-brand.cjs +1 -1
  66. package/dist-lib/react-native-host-modules.cjs +1 -1
  67. package/dist-lib/react-native-host-modules.mjs +1 -1
  68. package/dist-lib/render-mode.cjs +1 -1
  69. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  70. package/dist-lib/sdk.cjs +1 -1
  71. package/dist-lib/sdk.mjs +1 -1
  72. package/dist-lib/skills.cjs +436 -351
  73. package/dist-lib/vite.cjs +1 -1
  74. package/package.json +2 -1
  75. package/src/auth/shared-session.ts +31 -0
  76. package/src/cloud.ts +83 -113
  77. package/src/metro-plugin.ts +30 -113
  78. package/src/vite-plugin.ts +11 -2
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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;
@@ -21683,6 +21683,197 @@ var init_metro_production_bundle = __esm({
21683
21683
  }
21684
21684
  });
21685
21685
 
21686
+ // src/auth/shared-session.ts
21687
+ function sharedDesktopAuthAccounts(user) {
21688
+ return [
21689
+ ...user?.personalAccountId ? [{ id: user.personalAccountId, name: "Personal" }] : [],
21690
+ ...(user?.teams ?? []).map((team) => ({ id: team.id, name: team.name }))
21691
+ ];
21692
+ }
21693
+ function getBaseDir() {
21694
+ const explicit = process.env[SESSION_FILE_ENV];
21695
+ if (explicit?.trim()) {
21696
+ return (0, import_node_path14.dirname)((0, import_node_path14.resolve)(explicit));
21697
+ }
21698
+ return electronUserDataDir();
21699
+ }
21700
+ function getSharedDesktopAuthFilePath() {
21701
+ const explicit = process.env[SESSION_FILE_ENV];
21702
+ if (explicit?.trim()) return (0, import_node_path14.resolve)(explicit);
21703
+ return (0, import_node_path14.join)(getBaseDir(), "desktop-auth.json");
21704
+ }
21705
+ function normalizeUser(input) {
21706
+ if (!input || typeof input !== "object") return null;
21707
+ const value = input;
21708
+ if (typeof value.id !== "string" || !value.id.trim()) return null;
21709
+ return {
21710
+ id: value.id.trim(),
21711
+ name: typeof value.name === "string" ? value.name : void 0,
21712
+ email: typeof value.email === "string" ? value.email : void 0,
21713
+ image: typeof value.image === "string" ? value.image : void 0,
21714
+ githubUsername: typeof value.githubUsername === "string" ? value.githubUsername : void 0,
21715
+ personalAccountId: typeof value.personalAccountId === "string" && value.personalAccountId.trim() ? value.personalAccountId.trim() : void 0,
21716
+ isAdmin: value.isAdmin === true,
21717
+ teams: Array.isArray(value.teams) ? value.teams.map((team) => {
21718
+ if (!team || typeof team !== "object") return null;
21719
+ const t = team;
21720
+ if (typeof t.id !== "string" || typeof t.name !== "string") return null;
21721
+ return {
21722
+ id: t.id,
21723
+ name: t.name,
21724
+ role: t.role === "owner" ? "owner" : "member",
21725
+ githubOrg: typeof t.githubOrg === "string" ? t.githubOrg : null
21726
+ };
21727
+ }).filter((team) => !!team) : void 0
21728
+ };
21729
+ }
21730
+ function normalizeSession(input) {
21731
+ if (!input || typeof input !== "object") return null;
21732
+ const value = input;
21733
+ if (value.version !== SESSION_VERSION) return null;
21734
+ if (typeof value.token !== "string" || !value.token.trim()) return null;
21735
+ const origin = typeof value.origin === "string" && value.origin.trim() ? value.origin.trim() : DEFAULT_ORIGIN;
21736
+ const source = value.source === "cli" || value.source === "electron" || value.source === "browser" || value.source === "unknown" ? value.source : "unknown";
21737
+ const updatedAt = typeof value.updatedAt === "string" && value.updatedAt ? value.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
21738
+ const validatedAt = typeof value.validatedAt === "string" && value.validatedAt ? value.validatedAt : void 0;
21739
+ const accountId = typeof value.accountId === "string" && value.accountId.trim() ? value.accountId.trim() : void 0;
21740
+ return {
21741
+ version: SESSION_VERSION,
21742
+ token: value.token.trim(),
21743
+ user: normalizeUser(value.user),
21744
+ origin,
21745
+ source,
21746
+ updatedAt,
21747
+ validatedAt,
21748
+ accountId
21749
+ };
21750
+ }
21751
+ function readSharedDesktopAuthSession() {
21752
+ const filepath = getSharedDesktopAuthFilePath();
21753
+ if (!(0, import_node_fs13.existsSync)(filepath)) return null;
21754
+ try {
21755
+ const parsed = JSON.parse((0, import_node_fs13.readFileSync)(filepath, "utf8"));
21756
+ const normalized = normalizeSession(parsed);
21757
+ if (!normalized) {
21758
+ (0, import_node_fs13.rmSync)(filepath, { force: true });
21759
+ return null;
21760
+ }
21761
+ return normalized;
21762
+ } catch {
21763
+ (0, import_node_fs13.rmSync)(filepath, { force: true });
21764
+ return null;
21765
+ }
21766
+ }
21767
+ var import_node_fs13, import_node_path14, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
21768
+ var init_shared_session = __esm({
21769
+ "src/auth/shared-session.ts"() {
21770
+ "use strict";
21771
+ import_node_fs13 = require("node:fs");
21772
+ import_node_path14 = require("node:path");
21773
+ init_home_paths();
21774
+ SESSION_VERSION = 1;
21775
+ SESSION_FILE_ENV = "SOOTSIM_SHARED_AUTH_FILE";
21776
+ DEFAULT_ORIGIN = "https://contrast.dev";
21777
+ }
21778
+ });
21779
+
21780
+ // cli/run-rnx.ts
21781
+ function rethrowIfExit(error) {
21782
+ if (error instanceof RnxExit) throw error;
21783
+ }
21784
+ function rnxExit(code) {
21785
+ throw new RnxExit(code);
21786
+ }
21787
+ var import_node_async_hooks, RnxExit, sinks;
21788
+ var init_run_rnx = __esm({
21789
+ "cli/run-rnx.ts"() {
21790
+ "use strict";
21791
+ import_node_async_hooks = require("node:async_hooks");
21792
+ RnxExit = class extends Error {
21793
+ code;
21794
+ constructor(code) {
21795
+ super(`rnx exited with code ${code}`);
21796
+ this.name = "RnxExit";
21797
+ this.code = code;
21798
+ }
21799
+ };
21800
+ sinks = new import_node_async_hooks.AsyncLocalStorage();
21801
+ }
21802
+ });
21803
+
21804
+ // cli/auth.ts
21805
+ function looksLikeGitHubToken(token) {
21806
+ return Boolean(
21807
+ token && token.length >= 20 && GITHUB_TOKEN_PREFIXES.some((prefix) => token.startsWith(prefix))
21808
+ );
21809
+ }
21810
+ function resolveGitHubActionsAuth() {
21811
+ const contrastRunnerToken = process.env.CONTRAST_INSTALLATION_TOKEN?.trim();
21812
+ const githubActionsToken = process.env.GITHUB_TOKEN?.trim();
21813
+ const token = contrastRunnerToken || githubActionsToken;
21814
+ if (!looksLikeGitHubToken(token)) return null;
21815
+ const repoSlug = (process.env.CONTRAST_REPO || process.env.GITHUB_REPOSITORY || "").trim();
21816
+ if (!repoSlug) return null;
21817
+ const [owner, repo] = repoSlug.includes("/") ? repoSlug.split("/", 2) : [null, null];
21818
+ const repositoryId = (process.env.CONTRAST_REPO_ID || process.env.GITHUB_REPOSITORY_ID || "").trim();
21819
+ const installationId = (process.env.CONTRAST_INSTALLATION_ID || process.env.GITHUB_APP_INSTALLATION_ID || "").trim();
21820
+ return {
21821
+ kind: "github",
21822
+ token,
21823
+ repoId: repoSlug,
21824
+ repositoryId: repositoryId || null,
21825
+ owner: owner || null,
21826
+ repo: repo || null,
21827
+ installationId: installationId || null,
21828
+ source: contrastRunnerToken ? "contrast-runner" : "github-actions"
21829
+ };
21830
+ }
21831
+ function isCliApiKeySecret(secret) {
21832
+ return secret.startsWith("sk_rnx_");
21833
+ }
21834
+ function readApiKeyFromEnv() {
21835
+ const envName = "RNX_API_KEY";
21836
+ const secret = process.env[envName]?.trim();
21837
+ return secret && isCliApiKeySecret(secret) ? { secret, envName } : null;
21838
+ }
21839
+ function resolveCliAuth() {
21840
+ const envKey = readApiKeyFromEnv();
21841
+ if (envKey) {
21842
+ return {
21843
+ kind: "api-key",
21844
+ secret: envKey.secret,
21845
+ source: "env",
21846
+ envName: envKey.envName
21847
+ };
21848
+ }
21849
+ const githubAuth = resolveGitHubActionsAuth();
21850
+ if (githubAuth) return githubAuth;
21851
+ const session = readSharedDesktopAuthSession();
21852
+ if (session?.token) {
21853
+ const accounts = sharedDesktopAuthAccounts(session.user);
21854
+ return {
21855
+ kind: "session",
21856
+ token: session.token,
21857
+ origin: session.origin,
21858
+ accountId: session.accountId ?? (accounts.length === 1 ? accounts[0]?.id ?? null : null)
21859
+ };
21860
+ }
21861
+ return null;
21862
+ }
21863
+ function authHeaderValue(auth) {
21864
+ return auth.kind === "api-key" ? `Bearer ${auth.secret}` : `Bearer ${auth.token}`;
21865
+ }
21866
+ var GITHUB_TOKEN_PREFIXES, CHOOSE_ACCOUNT_HINT;
21867
+ var init_auth = __esm({
21868
+ "cli/auth.ts"() {
21869
+ "use strict";
21870
+ init_shared_session();
21871
+ init_run_rnx();
21872
+ GITHUB_TOKEN_PREFIXES = ["ghs_", "ghp_", "gho_", "ghu_", "github_pat_"];
21873
+ CHOOSE_ACCOUNT_HINT = "this sign-in belongs to several accounts; run `rnx login --account <name>` to choose the one to bill";
21874
+ }
21875
+ });
21876
+
21686
21877
  // cli/cloud-session.ts
21687
21878
  function isRecord3(value) {
21688
21879
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -21692,13 +21883,13 @@ function normalizeCloudOrigin(source) {
21692
21883
  try {
21693
21884
  parsed = new URL(source);
21694
21885
  } catch {
21695
- throw new Error(`invalid RNX Cloud origin: ${source}`);
21886
+ throw new Error(`invalid remote simulator origin: ${source}`);
21696
21887
  }
21697
21888
  if (parsed.protocol !== "https:" && parsed.protocol !== "http:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
21698
- throw new Error(`RNX Cloud origin must be an HTTP(S) origin: ${source}`);
21889
+ throw new Error(`remote simulator origin must be an HTTP(S) origin: ${source}`);
21699
21890
  }
21700
21891
  if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname)) {
21701
- throw new Error(`RNX Cloud origin requires HTTPS outside loopback: ${source}`);
21892
+ throw new Error(`remote simulator origin requires HTTPS outside loopback: ${source}`);
21702
21893
  }
21703
21894
  return parsed.origin;
21704
21895
  }
@@ -21768,23 +21959,37 @@ var init_cloud_session = __esm({
21768
21959
  }
21769
21960
  });
21770
21961
 
21962
+ // cli/commands/detect.ts
21963
+ var init_detect = __esm({
21964
+ "cli/commands/detect.ts"() {
21965
+ "use strict";
21966
+ }
21967
+ });
21968
+
21771
21969
  // cli/cloud-client.ts
21772
21970
  function isRecord4(value) {
21773
21971
  return value !== null && typeof value === "object" && !Array.isArray(value);
21774
21972
  }
21775
- function responseError(status, text) {
21776
- if (text) {
21777
- try {
21778
- const parsed = JSON.parse(text);
21779
- const error = isRecord4(parsed) ? parsed.error : null;
21780
- if (isRecord4(error) && typeof error.message === "string") {
21781
- return new Error(`RNX Cloud request failed (${status}): ${error.message}`);
21782
- }
21783
- } catch {
21784
- }
21973
+ function failureSentence(text) {
21974
+ let parsed;
21975
+ try {
21976
+ parsed = JSON.parse(text);
21977
+ } catch {
21978
+ return null;
21979
+ }
21980
+ if (!isRecord4(parsed)) return null;
21981
+ if (isRecord4(parsed.error) && typeof parsed.error.message === "string") {
21982
+ return parsed.error.message;
21785
21983
  }
21984
+ if (typeof parsed.message === "string") return parsed.message;
21985
+ if (typeof parsed.error === "string") return parsed.error;
21986
+ return null;
21987
+ }
21988
+ function responseError(status, text) {
21989
+ const parsed = text ? failureSentence(text) : null;
21990
+ const sentence = parsed && ACCOUNT_CHOICE_REFUSALS.has(parsed) ? CHOOSE_ACCOUNT_HINT : parsed;
21786
21991
  return new Error(
21787
- `RNX Cloud request failed (${status})${text.trim() ? `: ${text.trim()}` : ""}`
21992
+ `remote simulator request failed (${status})${sentence ? `: ${sentence}` : text.trim() ? `: ${text.trim()}` : ""}`
21788
21993
  );
21789
21994
  }
21790
21995
  function resolveCloudSessionForParsed(parsed) {
@@ -21792,7 +21997,7 @@ function resolveCloudSessionForParsed(parsed) {
21792
21997
  if (!session) return null;
21793
21998
  if (parsed.simIdSource === "flag" && parsed.simId !== session.simId) {
21794
21999
  throw new Error(
21795
- `this shell is connected to RNX Cloud simulator ${session.simId}; ${parsed.simId ? `--sim ${parsed.simId}` : "the requested simulator"} is not available in this shell`
22000
+ `this shell is connected to remote simulator ${session.simId}; ${parsed.simId ? `--sim ${parsed.simId}` : "the requested simulator"} is not available in this shell`
21796
22001
  );
21797
22002
  }
21798
22003
  return session;
@@ -21803,13 +22008,15 @@ function cloudCommand(command) {
21803
22008
  if (key !== "id" && key !== "simId") clean[key] = value;
21804
22009
  }
21805
22010
  if (isRnxCloudCommandType(command.type)) return clean;
21806
- throw new Error(`RNX Cloud does not support bridge command type ${command.type}`);
22011
+ throw new Error(
22012
+ `a remote simulator does not support bridge command type ${command.type}`
22013
+ );
21807
22014
  }
21808
22015
  function createCloudBridgeForParsed(parsed) {
21809
22016
  const session = resolveCloudSessionForParsed(parsed);
21810
22017
  return session ? new CloudBridge(session) : null;
21811
22018
  }
21812
- var import_node_crypto5, DEFAULT_RNX_CLOUD_ORIGIN, CloudBridge;
22019
+ var import_node_crypto5, DEFAULT_RNX_CLOUD_ORIGIN, MAX_RNX_CLOUD_BUNDLE_BYTES, WARN_ARTIFACT_BYTES, ACCOUNT_CHOICE_REFUSALS, CloudBridge;
21813
22020
  var init_cloud_client = __esm({
21814
22021
  "cli/cloud-client.ts"() {
21815
22022
  "use strict";
@@ -21819,8 +22026,16 @@ var init_cloud_client = __esm({
21819
22026
  init_cloud_contract();
21820
22027
  init_metro_production_bundle();
21821
22028
  init_public_brand();
22029
+ init_auth();
21822
22030
  init_cloud_session();
22031
+ init_detect();
21823
22032
  DEFAULT_RNX_CLOUD_ORIGIN = rnxPublicBrand.origin;
22033
+ MAX_RNX_CLOUD_BUNDLE_BYTES = RNX_CLOUD_MAX_ARTIFACT_BYTES;
22034
+ WARN_ARTIFACT_BYTES = Math.floor(MAX_RNX_CLOUD_BUNDLE_BYTES * 0.8);
22035
+ ACCOUNT_CHOICE_REFUSALS = /* @__PURE__ */ new Set([
22036
+ "accountId is required",
22037
+ "you do not have access to this account"
22038
+ ]);
21824
22039
  CloudBridge = class {
21825
22040
  constructor(session) {
21826
22041
  this.session = session;
@@ -21848,7 +22063,7 @@ var init_cloud_client = __esm({
21848
22063
  );
21849
22064
  } catch (error) {
21850
22065
  throw new Error(
21851
- `could not reach RNX Cloud at ${this.session.apiOrigin}: ${error instanceof Error ? error.message : String(error)}`
22066
+ `could not reach the remote simulator at ${this.session.apiOrigin}: ${error instanceof Error ? error.message : String(error)}`
21852
22067
  );
21853
22068
  }
21854
22069
  const text = await response.text();
@@ -21857,30 +22072,34 @@ var init_cloud_client = __esm({
21857
22072
  try {
21858
22073
  parsed = JSON.parse(text);
21859
22074
  } catch {
21860
- throw new Error("RNX Cloud command returned unreadable JSON");
22075
+ throw new Error("remote simulator command returned unreadable JSON");
21861
22076
  }
21862
22077
  if (!isRecord4(parsed) || parsed.id !== body.id) {
21863
- throw new Error("RNX Cloud command returned an invalid command receipt");
22078
+ throw new Error("remote simulator command returned an invalid command receipt");
21864
22079
  }
21865
22080
  if (typeof parsed.error === "string" && parsed.error) throw new Error(parsed.error);
21866
22081
  if (!Object.hasOwn(parsed, "result")) {
21867
- throw new Error("RNX Cloud command receipt has no result");
22082
+ throw new Error("remote simulator command receipt has no result");
21868
22083
  }
21869
22084
  return parsed.result;
21870
22085
  }
21871
22086
  listSims() {
21872
- return Promise.reject(new Error("RNX Cloud does not expose a local simulator list"));
21873
- }
21874
- focusSim() {
21875
22087
  return Promise.reject(
21876
- new Error("RNX Cloud simulators have no browser window to focus")
22088
+ new Error("a remote simulator does not expose a local simulator list")
21877
22089
  );
21878
22090
  }
22091
+ focusSim() {
22092
+ return Promise.reject(new Error("remote simulators have no browser window to focus"));
22093
+ }
21879
22094
  closeSim() {
21880
- return Promise.reject(new Error("RNX Cloud close uses the simulator DELETE route"));
22095
+ return Promise.reject(
22096
+ new Error("remote simulator close uses the simulator DELETE route")
22097
+ );
21881
22098
  }
21882
22099
  claim() {
21883
- return Promise.reject(new Error("RNX Cloud claim uses the simulator claim route"));
22100
+ return Promise.reject(
22101
+ new Error("remote simulator claim uses the simulator claim route")
22102
+ );
21884
22103
  }
21885
22104
  close() {
21886
22105
  }
@@ -22049,31 +22268,31 @@ var init_parent_pid = __esm({
22049
22268
 
22050
22269
  // cli/secure-file.ts
22051
22270
  function writeSecureJsonAtomic(filepath, value) {
22052
- (0, import_node_fs13.mkdirSync)((0, import_node_path14.dirname)(filepath), { recursive: true });
22271
+ (0, import_node_fs14.mkdirSync)((0, import_node_path15.dirname)(filepath), { recursive: true });
22053
22272
  const temporary = `${filepath}.tmp-${process.pid}-${(0, import_node_crypto6.randomUUID)()}`;
22054
22273
  let descriptor = null;
22055
22274
  try {
22056
- descriptor = (0, import_node_fs13.openSync)(temporary, "wx", 384);
22057
- (0, import_node_fs13.fchmodSync)(descriptor, 384);
22058
- (0, import_node_fs13.writeFileSync)(descriptor, `${JSON.stringify(value, null, 2)}
22275
+ descriptor = (0, import_node_fs14.openSync)(temporary, "wx", 384);
22276
+ (0, import_node_fs14.fchmodSync)(descriptor, 384);
22277
+ (0, import_node_fs14.writeFileSync)(descriptor, `${JSON.stringify(value, null, 2)}
22059
22278
  `);
22060
- (0, import_node_fs13.fsyncSync)(descriptor);
22061
- (0, import_node_fs13.closeSync)(descriptor);
22279
+ (0, import_node_fs14.fsyncSync)(descriptor);
22280
+ (0, import_node_fs14.closeSync)(descriptor);
22062
22281
  descriptor = null;
22063
- (0, import_node_fs13.renameSync)(temporary, filepath);
22282
+ (0, import_node_fs14.renameSync)(temporary, filepath);
22064
22283
  } catch (error) {
22065
- if (descriptor !== null) (0, import_node_fs13.closeSync)(descriptor);
22066
- (0, import_node_fs13.rmSync)(temporary, { force: true });
22284
+ if (descriptor !== null) (0, import_node_fs14.closeSync)(descriptor);
22285
+ (0, import_node_fs14.rmSync)(temporary, { force: true });
22067
22286
  throw error;
22068
22287
  }
22069
22288
  }
22070
- var import_node_crypto6, import_node_fs13, import_node_path14;
22289
+ var import_node_crypto6, import_node_fs14, import_node_path15;
22071
22290
  var init_secure_file = __esm({
22072
22291
  "cli/secure-file.ts"() {
22073
22292
  "use strict";
22074
22293
  import_node_crypto6 = require("node:crypto");
22075
- import_node_fs13 = require("node:fs");
22076
- import_node_path14 = require("node:path");
22294
+ import_node_fs14 = require("node:fs");
22295
+ import_node_path15 = require("node:path");
22077
22296
  }
22078
22297
  });
22079
22298
 
@@ -22220,6 +22439,7 @@ var init_current_sim = __esm({
22220
22439
  // cli/ws-bridge.ts
22221
22440
  var ws_bridge_exports = {};
22222
22441
  __export(ws_bridge_exports, {
22442
+ BRIDGE_VALUE_FLAGS: () => BRIDGE_VALUE_FLAGS,
22223
22443
  BridgeSimLockedError: () => BridgeSimLockedError,
22224
22444
  RNX_PLACEMENT_ENV: () => RNX_PLACEMENT_ENV,
22225
22445
  bridgePortForShellBaseUrl: () => bridgePortForShellBaseUrl,
@@ -22232,6 +22452,7 @@ __export(ws_bridge_exports, {
22232
22452
  evalInBridge: () => evalInBridge,
22233
22453
  parseBridgeCliArgs: () => parseBridgeCliArgs,
22234
22454
  printBridgeWorldNotice: () => printBridgeWorldNotice,
22455
+ resolveBridgePortForPin: () => resolveBridgePortForPin,
22235
22456
  resolveBridgeWorld: () => resolveBridgeWorld,
22236
22457
  resolveDefaultBridgePort: () => resolveDefaultBridgePort,
22237
22458
  resolvePlacement: () => resolvePlacement,
@@ -22247,17 +22468,17 @@ function liveDevBridgeLockfiles() {
22247
22468
  return readDevBridgeLockfiles().filter((lockfile) => isDevBridgeLockfileFresh(lockfile));
22248
22469
  }
22249
22470
  function checkoutRoot(cwd) {
22250
- let current = import_node_path15.default.resolve(cwd);
22471
+ let current = import_node_path16.default.resolve(cwd);
22251
22472
  while (true) {
22252
- if (import_node_fs14.default.existsSync(import_node_path15.default.join(current, ".git"))) return current;
22253
- const parent = import_node_path15.default.dirname(current);
22473
+ if (import_node_fs15.default.existsSync(import_node_path16.default.join(current, ".git"))) return current;
22474
+ const parent = import_node_path16.default.dirname(current);
22254
22475
  if (parent === current) return null;
22255
22476
  current = parent;
22256
22477
  }
22257
22478
  }
22258
22479
  function pathContains(parent, child) {
22259
- const relative = import_node_path15.default.relative(parent, child);
22260
- return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path15.default.sep}`) && !import_node_path15.default.isAbsolute(relative);
22480
+ const relative = import_node_path16.default.relative(parent, child);
22481
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path16.default.sep}`) && !import_node_path16.default.isAbsolute(relative);
22261
22482
  }
22262
22483
  function resolveDefaultDevBridgeLockfile() {
22263
22484
  const lockfiles = liveDevBridgeLockfiles();
@@ -22360,6 +22581,21 @@ function bridgePortForShellBaseUrl(baseUrl) {
22360
22581
  }
22361
22582
  return null;
22362
22583
  }
22584
+ function resolveBridgePortForPin(pin) {
22585
+ const served = bridgePortForShellBaseUrl(pin.baseUrl);
22586
+ if (served === null) {
22587
+ if (pin.explicitPort) return { port: pin.wsPort };
22588
+ return {
22589
+ error: `no live rnx world serves ${pin.baseUrl} \u2014 start that shell first, or name its bridge with --port <port>`
22590
+ };
22591
+ }
22592
+ if (pin.explicitPort && pin.wsPort !== served) {
22593
+ return {
22594
+ error: `--base-url ${pin.baseUrl} belongs to bridge port ${served}, but --port ${pin.wsPort} names another world`
22595
+ };
22596
+ }
22597
+ return { port: served };
22598
+ }
22363
22599
  function resolveDefaultBridgePort() {
22364
22600
  return resolveBridgeWorld().bridgePort;
22365
22601
  }
@@ -22380,57 +22616,33 @@ function describeSimIdSource(source) {
22380
22616
  }
22381
22617
  function parseBridgeCliArgs(args, opts = {}) {
22382
22618
  const flagIndices = /* @__PURE__ */ new Set();
22383
- let wsPort = opts.port ?? resolveDefaultBridgePort();
22384
- let explicitPort = opts.port !== void 0;
22385
- let commandTimeoutMs = opts.commandTimeoutMs ?? 15e3;
22386
- let simId;
22387
- let simIdSource = "none";
22619
+ const state = {
22620
+ wsPort: opts.port ?? resolveDefaultBridgePort(),
22621
+ explicitPort: opts.port !== void 0,
22622
+ commandTimeoutMs: opts.commandTimeoutMs ?? 15e3,
22623
+ simId: void 0,
22624
+ simIdSource: "none"
22625
+ };
22388
22626
  const stripBooleanFlags = new Set(opts.stripBooleanFlags ?? []);
22389
22627
  const stripValueFlags = new Set(opts.stripValueFlags ?? []);
22390
22628
  for (let i = 0; i < args.length; i++) {
22391
22629
  const arg = args[i];
22392
- if (arg === "--port" || arg === "-p") {
22393
- flagIndices.add(i);
22394
- explicitPort = true;
22395
- if (i + 1 < args.length) {
22396
- flagIndices.add(i + 1);
22397
- wsPort = Number(args[i + 1]);
22398
- }
22399
- i++;
22400
- continue;
22401
- }
22402
- if (arg.startsWith("--port=")) {
22403
- flagIndices.add(i);
22404
- explicitPort = true;
22405
- wsPort = Number(arg.slice("--port=".length));
22406
- continue;
22407
- }
22408
- if (arg === "--timeout") {
22409
- flagIndices.add(i);
22410
- if (i + 1 < args.length) {
22411
- flagIndices.add(i + 1);
22412
- commandTimeoutMs = Number(args[i + 1]);
22413
- }
22414
- i++;
22415
- continue;
22416
- }
22417
- if (arg === "--sim" || arg === "--session" || arg === "--tab") {
22630
+ const valueFlag = BRIDGE_VALUE_FLAGS_TABLE.find(
22631
+ (flag) => flag.names.some((name2) => arg === name2 || arg.startsWith(`${name2}=`))
22632
+ );
22633
+ if (valueFlag) {
22418
22634
  flagIndices.add(i);
22419
- if (i + 1 < args.length) {
22420
- flagIndices.add(i + 1);
22421
- simId = args[i + 1]?.trim() || void 0;
22422
- simIdSource = "flag";
22635
+ const inline = valueFlag.names.find((name2) => arg.startsWith(`${name2}=`));
22636
+ if (inline) {
22637
+ valueFlag.apply(state, arg.slice(inline.length + 1));
22638
+ continue;
22423
22639
  }
22640
+ const value = i + 1 < args.length ? args[i + 1] : void 0;
22641
+ if (value !== void 0) flagIndices.add(i + 1);
22642
+ valueFlag.apply(state, value);
22424
22643
  i++;
22425
22644
  continue;
22426
22645
  }
22427
- const simEq = arg.startsWith("--sim=") ? arg.slice("--sim=".length) : arg.startsWith("--session=") ? arg.slice("--session=".length) : arg.startsWith("--tab=") ? arg.slice("--tab=".length) : void 0;
22428
- if (simEq !== void 0) {
22429
- flagIndices.add(i);
22430
- simId = simEq.trim() || void 0;
22431
- simIdSource = "flag";
22432
- continue;
22433
- }
22434
22646
  if (stripBooleanFlags.has(arg)) {
22435
22647
  flagIndices.add(i);
22436
22648
  continue;
@@ -22443,20 +22655,20 @@ function parseBridgeCliArgs(args, opts = {}) {
22443
22655
  i++;
22444
22656
  }
22445
22657
  }
22446
- if (!simId) {
22658
+ if (!state.simId) {
22447
22659
  const savedSim = readCurrentSim();
22448
22660
  if (savedSim) {
22449
- simId = savedSim.simId.trim() || void 0;
22450
- simIdSource = "saved";
22661
+ state.simId = savedSim.simId.trim() || void 0;
22662
+ state.simIdSource = "saved";
22451
22663
  }
22452
22664
  }
22453
22665
  return {
22454
22666
  positional: args.filter((_, i) => !flagIndices.has(i)),
22455
- wsPort,
22456
- explicitPort,
22457
- simId,
22458
- simIdSource,
22459
- commandTimeoutMs
22667
+ wsPort: state.wsPort,
22668
+ explicitPort: state.explicitPort,
22669
+ simId: state.simId,
22670
+ simIdSource: state.simIdSource,
22671
+ commandTimeoutMs: state.commandTimeoutMs
22460
22672
  };
22461
22673
  }
22462
22674
  function createBridge(wsPort, opts = {}) {
@@ -22690,12 +22902,12 @@ async function callInBridge(bridge, path20, ...args) {
22690
22902
  async function callInBridgeWrite(bridge, path20, ...args) {
22691
22903
  return bridge.send({ type: "call", path: path20, args, acquireLock: true });
22692
22904
  }
22693
- var import_node_fs14, import_node_path15, import_ws2, RNX_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError;
22905
+ var import_node_fs15, import_node_path16, import_ws2, RNX_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError, BRIDGE_VALUE_FLAGS_TABLE, BRIDGE_VALUE_FLAGS;
22694
22906
  var init_ws_bridge = __esm({
22695
22907
  "cli/ws-bridge.ts"() {
22696
22908
  "use strict";
22697
- import_node_fs14 = __toESM(require("node:fs"), 1);
22698
- import_node_path15 = __toESM(require("node:path"), 1);
22909
+ import_node_fs15 = __toESM(require("node:fs"), 1);
22910
+ import_node_path16 = __toESM(require("node:path"), 1);
22699
22911
  import_ws2 = require("ws");
22700
22912
  init_bridge_constants();
22701
22913
  init_cli_constants();
@@ -22712,6 +22924,39 @@ var init_ws_bridge = __esm({
22712
22924
  this.lock = lock;
22713
22925
  }
22714
22926
  };
22927
+ BRIDGE_VALUE_FLAGS_TABLE = [
22928
+ {
22929
+ names: ["--port", "-p"],
22930
+ // naming the flag pins the world even when the value is missing or junk,
22931
+ // so a later --base-url cannot quietly retarget the run to another shell.
22932
+ apply: (state, value) => {
22933
+ state.explicitPort = true;
22934
+ if (value !== void 0) state.wsPort = Number(value);
22935
+ }
22936
+ },
22937
+ {
22938
+ names: ["--timeout"],
22939
+ apply: (state, value) => {
22940
+ if (value !== void 0) state.commandTimeoutMs = Number(value);
22941
+ }
22942
+ },
22943
+ {
22944
+ // `--sim` is the canonical target flag; `--session` / `--tab` are
22945
+ // documented aliases (CLAUDE.md, the rnx agent skills, and engine docs all
22946
+ // say `--session <id>`). without these aliases an agent following the docs
22947
+ // would have `--session ef` silently ignored and get the *currently
22948
+ // pinned* sim's tree instead — wrong-app data with no error.
22949
+ names: ["--sim", "--session", "--tab"],
22950
+ apply: (state, value) => {
22951
+ if (value === void 0) return;
22952
+ state.simId = value.trim() || void 0;
22953
+ state.simIdSource = "flag";
22954
+ }
22955
+ }
22956
+ ];
22957
+ BRIDGE_VALUE_FLAGS = BRIDGE_VALUE_FLAGS_TABLE.flatMap(
22958
+ (flag) => [...flag.names]
22959
+ );
22715
22960
  }
22716
22961
  });
22717
22962
 
@@ -23060,9 +23305,9 @@ var init_profiles = __esm({
23060
23305
  // cli/app-project.ts
23061
23306
  function firstNamedFile(dir, names) {
23062
23307
  for (const name2 of names) {
23063
- const candidate = import_node_path16.default.join(dir, name2);
23308
+ const candidate = import_node_path17.default.join(dir, name2);
23064
23309
  try {
23065
- if (import_node_fs15.default.statSync(candidate).isFile()) return candidate;
23310
+ if (import_node_fs16.default.statSync(candidate).isFile()) return candidate;
23066
23311
  } catch {
23067
23312
  }
23068
23313
  }
@@ -23075,24 +23320,24 @@ function findRNXAppConfig(dir) {
23075
23320
  return firstNamedFile(dir, RNX_APP_CONFIG_NAMES);
23076
23321
  }
23077
23322
  function resolveAppProject(projectRoot) {
23078
- const appDir = import_node_path16.default.resolve(projectRoot);
23323
+ const appDir = import_node_path17.default.resolve(projectRoot);
23079
23324
  try {
23080
- if (!import_node_fs15.default.statSync(appDir).isDirectory()) return null;
23325
+ if (!import_node_fs16.default.statSync(appDir).isDirectory()) return null;
23081
23326
  } catch {
23082
23327
  return null;
23083
23328
  }
23084
23329
  const configPath = findRNXAppConfig(appDir);
23085
- if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs15.default.existsSync(import_node_path16.default.join(appDir, "package.json"))) {
23330
+ if (!configPath && !findExpoAppConfig(appDir) && !import_node_fs16.default.existsSync(import_node_path17.default.join(appDir, "package.json"))) {
23086
23331
  return null;
23087
23332
  }
23088
23333
  return { appDir, configPath };
23089
23334
  }
23090
- var import_node_fs15, import_node_path16, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
23335
+ var import_node_fs16, import_node_path17, EXPO_APP_CONFIG_NAMES, RNX_APP_CONFIG_NAMES;
23091
23336
  var init_app_project = __esm({
23092
23337
  "cli/app-project.ts"() {
23093
23338
  "use strict";
23094
- import_node_fs15 = __toESM(require("node:fs"), 1);
23095
- import_node_path16 = __toESM(require("node:path"), 1);
23339
+ import_node_fs16 = __toESM(require("node:fs"), 1);
23340
+ import_node_path17 = __toESM(require("node:path"), 1);
23096
23341
  EXPO_APP_CONFIG_NAMES = [
23097
23342
  "app.config.js",
23098
23343
  "app.config.ts",
@@ -23121,13 +23366,13 @@ function isRNXConfig(value) {
23121
23366
  }
23122
23367
  function resolveConfigReplacementFiles(config, configPath) {
23123
23368
  if (!config.modules) return config;
23124
- const configDir = import_node_path17.default.dirname(configPath);
23369
+ const configDir = import_node_path18.default.dirname(configPath);
23125
23370
  return {
23126
23371
  ...config,
23127
23372
  modules: Object.fromEntries(
23128
23373
  Object.entries(config.modules).map(([name2, resolution]) => [
23129
23374
  name2,
23130
- resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path17.default.resolve(configDir, resolution.file) } : resolution
23375
+ resolution && typeof resolution === "object" && "file" in resolution ? { ...resolution, file: import_node_path18.default.resolve(configDir, resolution.file) } : resolution
23131
23376
  ])
23132
23377
  )
23133
23378
  };
@@ -23157,12 +23402,12 @@ async function resolveRNXAppConfig(startDir = process.cwd()) {
23157
23402
  const project = resolveAppProject(startDir);
23158
23403
  return { project, config: await loadRNXAppConfig(project) };
23159
23404
  }
23160
- var import_node_module, import_node_path17, import_jiti, require2;
23405
+ var import_node_module, import_node_path18, import_jiti, require2;
23161
23406
  var init_app_config = __esm({
23162
23407
  "cli/app-config.ts"() {
23163
23408
  "use strict";
23164
23409
  import_node_module = require("node:module");
23165
- import_node_path17 = __toESM(require("node:path"), 1);
23410
+ import_node_path18 = __toESM(require("node:path"), 1);
23166
23411
  init_src();
23167
23412
  import_jiti = require("jiti");
23168
23413
  init_public_brand();
@@ -23230,13 +23475,13 @@ function discoverNativeLinkedAppFonts(opts) {
23230
23475
  scannedFontDirectories++;
23231
23476
  let entries;
23232
23477
  try {
23233
- entries = import_node_fs16.default.readdirSync(current.dir, { withFileTypes: true });
23478
+ entries = import_node_fs17.default.readdirSync(current.dir, { withFileTypes: true });
23234
23479
  } catch {
23235
23480
  continue;
23236
23481
  }
23237
23482
  for (const entry of entries) {
23238
23483
  if (entry.isSymbolicLink()) continue;
23239
- const fullPath = import_node_path18.default.join(current.dir, entry.name);
23484
+ const fullPath = import_node_path19.default.join(current.dir, entry.name);
23240
23485
  if (entry.isDirectory()) {
23241
23486
  if (current.depth < 4 && pending.length < 64 && !entry.name.startsWith(".")) {
23242
23487
  pending.push({ dir: fullPath, depth: current.depth + 1 });
@@ -23244,15 +23489,15 @@ function discoverNativeLinkedAppFonts(opts) {
23244
23489
  continue;
23245
23490
  }
23246
23491
  if (!entry.isFile() || !/\.(ttf|otf|woff|woff2)$/i.test(entry.name)) continue;
23247
- const relative = import_node_path18.default.relative(root, fullPath).split(import_node_path18.default.sep).join("/");
23248
- fontPaths.push(import_node_path18.default.posix.join(metroPathPrefix, relative));
23492
+ const relative = import_node_path19.default.relative(root, fullPath).split(import_node_path19.default.sep).join("/");
23493
+ fontPaths.push(import_node_path19.default.posix.join(metroPathPrefix, relative));
23249
23494
  if (fontPaths.length >= 256) break;
23250
23495
  }
23251
23496
  }
23252
23497
  };
23253
23498
  try {
23254
23499
  const packageJson = JSON.parse(
23255
- import_node_fs16.default.readFileSync(import_node_path18.default.join(selectedAppDir, "package.json"), "utf8")
23500
+ import_node_fs17.default.readFileSync(import_node_path19.default.join(selectedAppDir, "package.json"), "utf8")
23256
23501
  );
23257
23502
  const dependencyNames = /* @__PURE__ */ new Set();
23258
23503
  for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
@@ -23267,24 +23512,24 @@ function discoverNativeLinkedAppFonts(opts) {
23267
23512
  const config = Reflect.get(packageJson, "reactNativeVectorIcons");
23268
23513
  const configuredFontDir = config && typeof config === "object" ? Reflect.get(config, "fontDir") : null;
23269
23514
  const configuredDir = typeof configuredFontDir === "string" ? configuredFontDir : "rnvi-fonts";
23270
- const root = import_node_path18.default.resolve(selectedAppDir, configuredDir);
23271
- const relativeRoot = import_node_path18.default.relative(selectedAppDir, root);
23272
- if (!relativeRoot.startsWith("..") && !import_node_path18.default.isAbsolute(relativeRoot)) {
23273
- collectFontFiles(root, relativeRoot.split(import_node_path18.default.sep).join("/"));
23515
+ const root = import_node_path19.default.resolve(selectedAppDir, configuredDir);
23516
+ const relativeRoot = import_node_path19.default.relative(selectedAppDir, root);
23517
+ if (!relativeRoot.startsWith("..") && !import_node_path19.default.isAbsolute(relativeRoot)) {
23518
+ collectFontFiles(root, relativeRoot.split(import_node_path19.default.sep).join("/"));
23274
23519
  }
23275
- const vectorIconRequire = (0, import_node_module2.createRequire)(import_node_path18.default.join(selectedAppDir, "package.json"));
23520
+ const vectorIconRequire = (0, import_node_module2.createRequire)(import_node_path19.default.join(selectedAppDir, "package.json"));
23276
23521
  for (const dependencyName of vectorIconDependencies) {
23277
23522
  try {
23278
23523
  const entryPath = vectorIconRequire.resolve(dependencyName);
23279
- const dependencyPath = dependencyName.split("/").join(import_node_path18.default.sep);
23280
- const marker = `${import_node_path18.default.sep}node_modules${import_node_path18.default.sep}${dependencyPath}${import_node_path18.default.sep}`;
23524
+ const dependencyPath = dependencyName.split("/").join(import_node_path19.default.sep);
23525
+ const marker = `${import_node_path19.default.sep}node_modules${import_node_path19.default.sep}${dependencyPath}${import_node_path19.default.sep}`;
23281
23526
  const markerIndex = entryPath.lastIndexOf(marker);
23282
23527
  if (markerIndex === -1) continue;
23283
23528
  const packageRoot = entryPath.slice(0, markerIndex + marker.length - 1);
23284
23529
  const fontsDir = dependencyName === "react-native-vector-icons" ? "Fonts" : "fonts";
23285
23530
  collectFontFiles(
23286
- import_node_path18.default.join(packageRoot, fontsDir),
23287
- import_node_path18.default.posix.join("node_modules", dependencyName, fontsDir)
23531
+ import_node_path19.default.join(packageRoot, fontsDir),
23532
+ import_node_path19.default.posix.join("node_modules", dependencyName, fontsDir)
23288
23533
  );
23289
23534
  } catch {
23290
23535
  }
@@ -23293,37 +23538,37 @@ function discoverNativeLinkedAppFonts(opts) {
23293
23538
  } catch {
23294
23539
  }
23295
23540
  try {
23296
- const configPath = import_node_path18.default.join(selectedAppDir, "react-native.config.js");
23297
- if (import_node_fs16.default.statSync(configPath).isFile()) {
23298
- const appConfigRequire = (0, import_node_module2.createRequire)(import_node_path18.default.join(selectedAppDir, "package.json"));
23541
+ const configPath = import_node_path19.default.join(selectedAppDir, "react-native.config.js");
23542
+ if (import_node_fs17.default.statSync(configPath).isFile()) {
23543
+ const appConfigRequire = (0, import_node_module2.createRequire)(import_node_path19.default.join(selectedAppDir, "package.json"));
23299
23544
  const rnConfig = appConfigRequire(configPath);
23300
23545
  const assets = rnConfig && typeof rnConfig === "object" ? Reflect.get(rnConfig, "assets") : null;
23301
23546
  if (Array.isArray(assets)) {
23302
23547
  for (const assetDir of assets) {
23303
23548
  if (typeof assetDir !== "string") continue;
23304
- const root = import_node_path18.default.resolve(selectedAppDir, assetDir);
23305
- const relativeRoot = import_node_path18.default.relative(selectedAppDir, root);
23306
- if (relativeRoot.startsWith("..") || import_node_path18.default.isAbsolute(relativeRoot)) continue;
23307
- collectFontFiles(root, relativeRoot.split(import_node_path18.default.sep).join("/"));
23549
+ const root = import_node_path19.default.resolve(selectedAppDir, assetDir);
23550
+ const relativeRoot = import_node_path19.default.relative(selectedAppDir, root);
23551
+ if (relativeRoot.startsWith("..") || import_node_path19.default.isAbsolute(relativeRoot)) continue;
23552
+ collectFontFiles(root, relativeRoot.split(import_node_path19.default.sep).join("/"));
23308
23553
  }
23309
23554
  }
23310
23555
  }
23311
23556
  } catch {
23312
23557
  }
23313
23558
  if (platform === "ios") {
23314
- const iosDir = import_node_path18.default.join(selectedAppDir, "ios");
23559
+ const iosDir = import_node_path19.default.join(selectedAppDir, "ios");
23315
23560
  try {
23316
- const targetDirs = import_node_fs16.default.readdirSync(iosDir, { withFileTypes: true }).filter(
23561
+ const targetDirs = import_node_fs17.default.readdirSync(iosDir, { withFileTypes: true }).filter(
23317
23562
  (entry) => entry.isDirectory() && !entry.isSymbolicLink() && entry.name !== "Pods" && entry.name !== "build" && !entry.name.startsWith(".")
23318
23563
  ).slice(0, 64);
23319
- const plistPaths = [import_node_path18.default.join(iosDir, "Info.plist")];
23564
+ const plistPaths = [import_node_path19.default.join(iosDir, "Info.plist")];
23320
23565
  for (const entry of targetDirs) {
23321
- plistPaths.push(import_node_path18.default.join(iosDir, entry.name, "Info.plist"));
23566
+ plistPaths.push(import_node_path19.default.join(iosDir, entry.name, "Info.plist"));
23322
23567
  }
23323
23568
  for (const plistPath of plistPaths) {
23324
23569
  let parsed;
23325
23570
  try {
23326
- parsed = parsePlistSource(import_node_fs16.default.readFileSync(plistPath, "utf8"));
23571
+ parsed = parsePlistSource(import_node_fs17.default.readFileSync(plistPath, "utf8"));
23327
23572
  } catch {
23328
23573
  continue;
23329
23574
  }
@@ -23333,21 +23578,21 @@ function discoverNativeLinkedAppFonts(opts) {
23333
23578
  for (const declaredFont of declaredFonts) {
23334
23579
  if (typeof declaredFont !== "string") continue;
23335
23580
  const candidates = [
23336
- import_node_path18.default.resolve(import_node_path18.default.dirname(plistPath), declaredFont),
23337
- import_node_path18.default.resolve(iosDir, declaredFont)
23581
+ import_node_path19.default.resolve(import_node_path19.default.dirname(plistPath), declaredFont),
23582
+ import_node_path19.default.resolve(iosDir, declaredFont)
23338
23583
  ];
23339
23584
  const fontPath = candidates.find((candidate) => {
23340
- const relative = import_node_path18.default.relative(selectedAppDir, candidate);
23341
- if (relative.startsWith("..") || import_node_path18.default.isAbsolute(relative)) return false;
23585
+ const relative = import_node_path19.default.relative(selectedAppDir, candidate);
23586
+ if (relative.startsWith("..") || import_node_path19.default.isAbsolute(relative)) return false;
23342
23587
  try {
23343
- return import_node_fs16.default.statSync(candidate).isFile();
23588
+ return import_node_fs17.default.statSync(candidate).isFile();
23344
23589
  } catch {
23345
23590
  return false;
23346
23591
  }
23347
23592
  });
23348
23593
  if (!fontPath) continue;
23349
23594
  fontPaths.push(
23350
- import_node_path18.default.relative(selectedAppDir, fontPath).split(import_node_path18.default.sep).join("/")
23595
+ import_node_path19.default.relative(selectedAppDir, fontPath).split(import_node_path19.default.sep).join("/")
23351
23596
  );
23352
23597
  }
23353
23598
  }
@@ -23386,13 +23631,13 @@ async function resolveMetroProjectRoot(bundleUrl, opts) {
23386
23631
  function encodeAppFontsWire(specs) {
23387
23632
  return specs.map((spec) => `${encodeURIComponent(spec.url)}::${encodeURIComponent(spec.family)}`).join(",");
23388
23633
  }
23389
- var import_node_fs16, import_node_module2, import_node_path18, import_config_plugin_fonts, require3;
23634
+ var import_node_fs17, import_node_module2, import_node_path19, import_config_plugin_fonts, require3;
23390
23635
  var init_app_fonts = __esm({
23391
23636
  "cli/app-fonts.ts"() {
23392
23637
  "use strict";
23393
- import_node_fs16 = __toESM(require("node:fs"), 1);
23638
+ import_node_fs17 = __toESM(require("node:fs"), 1);
23394
23639
  import_node_module2 = require("node:module");
23395
- import_node_path18 = __toESM(require("node:path"), 1);
23640
+ import_node_path19 = __toESM(require("node:path"), 1);
23396
23641
  import_config_plugin_fonts = require("sootsim-engine/engine/config-plugin-fonts");
23397
23642
  init_dev_server_scanner();
23398
23643
  init_plist();
@@ -24488,7 +24733,7 @@ function resolvePlaywright() {
24488
24733
  try {
24489
24734
  const modulePath = requireFromRoot.resolve(spec);
24490
24735
  const packageJsonPath = requireFromRoot.resolve(`${spec}/package.json`);
24491
- const parsed = JSON.parse((0, import_node_fs17.readFileSync)(packageJsonPath, "utf8"));
24736
+ const parsed = JSON.parse((0, import_node_fs18.readFileSync)(packageJsonPath, "utf8"));
24492
24737
  if (!isRecord5(parsed) || typeof parsed.version !== "string") continue;
24493
24738
  const bin = parsed.bin;
24494
24739
  let binPath = null;
@@ -24500,8 +24745,8 @@ function resolvePlaywright() {
24500
24745
  binPath = typeof named === "string" ? named : typeof first === "string" ? first : null;
24501
24746
  }
24502
24747
  if (!binPath) continue;
24503
- const cliPath = (0, import_node_path19.join)((0, import_node_path19.dirname)(packageJsonPath), binPath);
24504
- if (!(0, import_node_fs17.existsSync)(cliPath)) continue;
24748
+ const cliPath = (0, import_node_path20.join)((0, import_node_path20.dirname)(packageJsonPath), binPath);
24749
+ if (!(0, import_node_fs18.existsSync)(cliPath)) continue;
24505
24750
  return {
24506
24751
  spec,
24507
24752
  modulePath,
@@ -24545,7 +24790,7 @@ function probeBrowser(resolved, env) {
24545
24790
  return {
24546
24791
  ok: true,
24547
24792
  executablePath: parsed.executablePath,
24548
- exists: parsed.exists && (0, import_node_fs17.existsSync)(parsed.executablePath)
24793
+ exists: parsed.exists && (0, import_node_fs18.existsSync)(parsed.executablePath)
24549
24794
  };
24550
24795
  } catch (error) {
24551
24796
  return {
@@ -24596,14 +24841,14 @@ ${detail}` : ""}`
24596
24841
  }
24597
24842
  return { ok: true, executablePath: after.executablePath, installed: true };
24598
24843
  }
24599
- var import_node_child_process6, import_node_fs17, import_node_module4, import_node_path19;
24844
+ var import_node_child_process6, import_node_fs18, import_node_module4, import_node_path20;
24600
24845
  var init_playwright_provisioning = __esm({
24601
24846
  "cli/drivers/playwright-provisioning.ts"() {
24602
24847
  "use strict";
24603
24848
  import_node_child_process6 = require("node:child_process");
24604
- import_node_fs17 = require("node:fs");
24849
+ import_node_fs18 = require("node:fs");
24605
24850
  import_node_module4 = require("node:module");
24606
- import_node_path19 = require("node:path");
24851
+ import_node_path20 = require("node:path");
24607
24852
  init_internal_child();
24608
24853
  init_self_invocation();
24609
24854
  }
@@ -24784,30 +25029,6 @@ var init_open_url2 = __esm({
24784
25029
  }
24785
25030
  });
24786
25031
 
24787
- // cli/run-rnx.ts
24788
- function rethrowIfExit(error) {
24789
- if (error instanceof RnxExit) throw error;
24790
- }
24791
- function rnxExit(code) {
24792
- throw new RnxExit(code);
24793
- }
24794
- var import_node_async_hooks, RnxExit, sinks;
24795
- var init_run_rnx = __esm({
24796
- "cli/run-rnx.ts"() {
24797
- "use strict";
24798
- import_node_async_hooks = require("node:async_hooks");
24799
- RnxExit = class extends Error {
24800
- code;
24801
- constructor(code) {
24802
- super(`rnx exited with code ${code}`);
24803
- this.name = "RnxExit";
24804
- this.code = code;
24805
- }
24806
- };
24807
- sinks = new import_node_async_hooks.AsyncLocalStorage();
24808
- }
24809
- });
24810
-
24811
25032
  // cli/commands/inspect/env.ts
24812
25033
  function isAgentEnv(env = process.env) {
24813
25034
  if (env.RNX_AGENT === "0") return false;
@@ -25289,7 +25510,7 @@ function assertExplicitBridgeLock(preferredPort, explicitPort, lock) {
25289
25510
  }
25290
25511
  async function ensureRuntimeForOpen() {
25291
25512
  const active = readActiveRuntime();
25292
- if (active && import_node_fs18.default.existsSync(runtimeDir(active))) return;
25513
+ if (active && import_node_fs19.default.existsSync(runtimeDir(active))) return;
25293
25514
  console.error(" installing rnx engine runtime...");
25294
25515
  const { runRuntime: runRuntime2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
25295
25516
  await runRuntime2(["install"], {});
@@ -25436,10 +25657,10 @@ async function buildResolvedBundleOpenUrl(target, baseUrl, opts = {}) {
25436
25657
  }
25437
25658
  function normalizeReplacementPath(filePath) {
25438
25659
  if (filePath.startsWith("~/")) {
25439
- return import_node_path20.default.join(import_node_os3.default.homedir(), filePath.slice(2));
25660
+ return import_node_path21.default.join(import_node_os3.default.homedir(), filePath.slice(2));
25440
25661
  }
25441
- if (import_node_path20.default.isAbsolute(filePath)) return filePath;
25442
- return import_node_path20.default.resolve(process.cwd(), filePath);
25662
+ if (import_node_path21.default.isAbsolute(filePath)) return filePath;
25663
+ return import_node_path21.default.resolve(process.cwd(), filePath);
25443
25664
  }
25444
25665
  function parseOpenRuntimeConfig(args) {
25445
25666
  const modules = {};
@@ -25458,7 +25679,7 @@ function parseOpenRuntimeConfig(args) {
25458
25679
  }
25459
25680
  const moduleName = raw.slice(0, eqIndex).trim();
25460
25681
  const filePath = normalizeReplacementPath(raw.slice(eqIndex + 1).trim());
25461
- if (!import_node_fs18.default.existsSync(filePath)) {
25682
+ if (!import_node_fs19.default.existsSync(filePath)) {
25462
25683
  console.error(` rnx open: replacement file not found: ${filePath}`);
25463
25684
  rnxExit(1);
25464
25685
  }
@@ -25489,7 +25710,7 @@ async function resolveAppProjectForBundleUrl(bundleUrl) {
25489
25710
  return advertisedRoot ? resolveAppProject(advertisedRoot) : null;
25490
25711
  }
25491
25712
  async function ensureConfiguredRuntime(version) {
25492
- if (import_node_fs18.default.existsSync(import_node_path20.default.join(runtimeDir(version), "index.html"))) return;
25713
+ if (import_node_fs19.default.existsSync(import_node_path21.default.join(runtimeDir(version), "index.html"))) return;
25493
25714
  console.error(` installing configured engine runtime ${version}...`);
25494
25715
  const result = await rnxRuntime.install({
25495
25716
  version,
@@ -25631,9 +25852,10 @@ async function buildShellUrl(target, baseUrl = resolveDefaultShellBaseUrl(), opt
25631
25852
  const resolved = await resolveShellUrl(target, baseUrl, opts);
25632
25853
  const project = opts.project ?? await resolveAppProjectForBundleUrl(bundleUrlOf(resolved));
25633
25854
  const stamped = await stampAppSplash(await stampAppFonts(resolved, project), project);
25634
- if (!opts.device) return stamped;
25855
+ if (!opts.device && !opts.proof) return stamped;
25635
25856
  const url = new URL(stamped);
25636
- url.searchParams.set("device", opts.device);
25857
+ if (opts.device) url.searchParams.set("device", opts.device);
25858
+ if (opts.proof) url.searchParams.set("proof", "1");
25637
25859
  return url.toString();
25638
25860
  }
25639
25861
  function deriveCurrentSimBaseUrl(sim, fallback) {
@@ -25860,7 +26082,7 @@ function playwrightHostPidForSim(sim) {
25860
26082
  function signalDriverLaunchConnected(result) {
25861
26083
  if (!result.connectAckFile) return false;
25862
26084
  try {
25863
- import_node_fs18.default.writeFileSync(
26085
+ import_node_fs19.default.writeFileSync(
25864
26086
  result.connectAckFile,
25865
26087
  `${JSON.stringify({ connectedAt: Date.now(), pid: result.pid ?? null })}
25866
26088
  `,
@@ -25874,19 +26096,19 @@ function signalDriverLaunchConnected(result) {
25874
26096
  function printDriverDiagnosticLogTail(result) {
25875
26097
  if (!result.diagnosticLogPath) return;
25876
26098
  try {
25877
- const detail = import_node_fs18.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
26099
+ const detail = import_node_fs19.default.readFileSync(result.diagnosticLogPath, "utf8").trim();
25878
26100
  if (!detail) {
25879
- console.error(` ${import_node_path20.default.basename(result.diagnosticLogPath)} was empty`);
26101
+ console.error(` ${import_node_path21.default.basename(result.diagnosticLogPath)} was empty`);
25880
26102
  return;
25881
26103
  }
25882
26104
  const shown = detail.length > 4e3 ? `\u2026
25883
26105
  ${detail.slice(-4e3)}` : detail;
25884
- console.error(` ${import_node_path20.default.basename(result.diagnosticLogPath)}:`);
26106
+ console.error(` ${import_node_path21.default.basename(result.diagnosticLogPath)}:`);
25885
26107
  console.error(shown);
25886
26108
  } catch (err) {
25887
26109
  const message = err instanceof Error ? err.message : String(err);
25888
26110
  console.error(
25889
- ` failed to read ${import_node_path20.default.basename(result.diagnosticLogPath)}: ${message}`
26111
+ ` failed to read ${import_node_path21.default.basename(result.diagnosticLogPath)}: ${message}`
25890
26112
  );
25891
26113
  }
25892
26114
  }
@@ -26120,7 +26342,8 @@ async function runOpenCommand(args, opts = {}) {
26120
26342
  "--hot",
26121
26343
  "--no-hmr",
26122
26344
  "--no-describe",
26123
- "--quiet"
26345
+ "--quiet",
26346
+ "--proof"
26124
26347
  ],
26125
26348
  stripValueFlags: [
26126
26349
  "--base-url",
@@ -26208,9 +26431,18 @@ async function runOpenCommand(args, opts = {}) {
26208
26431
  await ensureConfiguredRuntime(configuredRuntimeVersion);
26209
26432
  }
26210
26433
  const explicitBaseUrl = args.find((_, i) => args[i - 1] === "--base-url");
26211
- if (explicitBaseUrl && !parsed.explicitPort) {
26212
- const matched = bridgePortForShellBaseUrl(explicitBaseUrl);
26213
- if (matched) parsed.wsPort = matched;
26434
+ if (explicitBaseUrl) {
26435
+ const pinned = resolveBridgePortForPin({
26436
+ baseUrl: explicitBaseUrl,
26437
+ wsPort: parsed.wsPort,
26438
+ explicitPort: parsed.explicitPort
26439
+ });
26440
+ if (pinned.error !== void 0) {
26441
+ console.error(` error: ${pinned.error}`);
26442
+ rnxExit(1);
26443
+ } else {
26444
+ parsed.wsPort = pinned.port;
26445
+ }
26214
26446
  }
26215
26447
  parsed.wsPort = await ensureBridgeForOpen(parsed.wsPort, parsed.explicitPort);
26216
26448
  const requestedDriverId = args.find((_, i) => args[i - 1] === "--driver") || "";
@@ -26249,7 +26481,8 @@ async function runOpenCommand(args, opts = {}) {
26249
26481
  hot: hotMode,
26250
26482
  device: deviceArg,
26251
26483
  resolvedBundle,
26252
- project
26484
+ project,
26485
+ proof: args.includes("--proof")
26253
26486
  };
26254
26487
  const selectedShellBaseUrl = configuredRuntimeVersion ? `http://localhost:${parsed.wsPort}/` : explicitBaseUrl || resolveShellBaseUrlForBridgePort(parsed.wsPort);
26255
26488
  const baseUrl = applyRuntimeGenerationOrigin(
@@ -26384,9 +26617,12 @@ async function runOpenCommand(args, opts = {}) {
26384
26617
  const result = await driver.launch({
26385
26618
  url: openUrl2,
26386
26619
  // the shell serves the sim page but its WS bridge may have drifted off
26387
- // the shell-port-implied default. inject the resolved bridge port so the
26388
- // page does not reconnect to a stale world.
26389
- bridgePort: bridgePortForShellBaseUrl(baseUrl) ?? parsed.wsPort,
26620
+ // the shell-port-implied default. inject the bridge this command settled
26621
+ // on so the page does not reconnect to a stale world. re-deriving it from
26622
+ // the shell URL could name a third port, since a bridge with no lockfile
26623
+ // resolves back to the default shell URL and that shell may belong to a
26624
+ // live world of its own.
26625
+ bridgePort: parsed.wsPort,
26390
26626
  headless: driverHeadless,
26391
26627
  profileId,
26392
26628
  ephemeralProfile,
@@ -26726,14 +26962,14 @@ async function runCloseCommand(args, opts = {}) {
26726
26962
  bridge.close();
26727
26963
  }
26728
26964
  }
26729
- 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;
26965
+ var import_node_child_process7, import_node_fs19, import_node_os3, import_node_path21, 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;
26730
26966
  var init_control = __esm({
26731
26967
  "cli/commands/control.ts"() {
26732
26968
  "use strict";
26733
26969
  import_node_child_process7 = require("node:child_process");
26734
- import_node_fs18 = __toESM(require("node:fs"), 1);
26970
+ import_node_fs19 = __toESM(require("node:fs"), 1);
26735
26971
  import_node_os3 = __toESM(require("node:os"), 1);
26736
- import_node_path20 = __toESM(require("node:path"), 1);
26972
+ import_node_path21 = __toESM(require("node:path"), 1);
26737
26973
  import_settings = require("sootsim-engine/settings");
26738
26974
  import_ws4 = require("ws");
26739
26975
  init_dev_server_scanner();
@@ -27583,157 +27819,6 @@ var init_maestro_js = __esm({
27583
27819
  }
27584
27820
  });
27585
27821
 
27586
- // src/auth/shared-session.ts
27587
- function getBaseDir() {
27588
- const explicit = process.env[SESSION_FILE_ENV];
27589
- if (explicit?.trim()) {
27590
- return (0, import_node_path21.dirname)((0, import_node_path21.resolve)(explicit));
27591
- }
27592
- return electronUserDataDir();
27593
- }
27594
- function getSharedDesktopAuthFilePath() {
27595
- const explicit = process.env[SESSION_FILE_ENV];
27596
- if (explicit?.trim()) return (0, import_node_path21.resolve)(explicit);
27597
- return (0, import_node_path21.join)(getBaseDir(), "desktop-auth.json");
27598
- }
27599
- function normalizeUser(input) {
27600
- if (!input || typeof input !== "object") return null;
27601
- const value = input;
27602
- if (typeof value.id !== "string" || !value.id.trim()) return null;
27603
- return {
27604
- id: value.id.trim(),
27605
- name: typeof value.name === "string" ? value.name : void 0,
27606
- email: typeof value.email === "string" ? value.email : void 0,
27607
- image: typeof value.image === "string" ? value.image : void 0,
27608
- githubUsername: typeof value.githubUsername === "string" ? value.githubUsername : void 0,
27609
- isAdmin: value.isAdmin === true,
27610
- teams: Array.isArray(value.teams) ? value.teams.map((team) => {
27611
- if (!team || typeof team !== "object") return null;
27612
- const t = team;
27613
- if (typeof t.id !== "string" || typeof t.name !== "string") return null;
27614
- return {
27615
- id: t.id,
27616
- name: t.name,
27617
- role: t.role === "owner" ? "owner" : "member",
27618
- githubOrg: typeof t.githubOrg === "string" ? t.githubOrg : null
27619
- };
27620
- }).filter((team) => !!team) : void 0
27621
- };
27622
- }
27623
- function normalizeSession(input) {
27624
- if (!input || typeof input !== "object") return null;
27625
- const value = input;
27626
- if (value.version !== SESSION_VERSION) return null;
27627
- if (typeof value.token !== "string" || !value.token.trim()) return null;
27628
- const origin = typeof value.origin === "string" && value.origin.trim() ? value.origin.trim() : DEFAULT_ORIGIN;
27629
- const source = value.source === "cli" || value.source === "electron" || value.source === "browser" || value.source === "unknown" ? value.source : "unknown";
27630
- const updatedAt = typeof value.updatedAt === "string" && value.updatedAt ? value.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
27631
- const validatedAt = typeof value.validatedAt === "string" && value.validatedAt ? value.validatedAt : void 0;
27632
- return {
27633
- version: SESSION_VERSION,
27634
- token: value.token.trim(),
27635
- user: normalizeUser(value.user),
27636
- origin,
27637
- source,
27638
- updatedAt,
27639
- validatedAt
27640
- };
27641
- }
27642
- function readSharedDesktopAuthSession() {
27643
- const filepath = getSharedDesktopAuthFilePath();
27644
- if (!(0, import_node_fs19.existsSync)(filepath)) return null;
27645
- try {
27646
- const parsed = JSON.parse((0, import_node_fs19.readFileSync)(filepath, "utf8"));
27647
- const normalized = normalizeSession(parsed);
27648
- if (!normalized) {
27649
- (0, import_node_fs19.rmSync)(filepath, { force: true });
27650
- return null;
27651
- }
27652
- return normalized;
27653
- } catch {
27654
- (0, import_node_fs19.rmSync)(filepath, { force: true });
27655
- return null;
27656
- }
27657
- }
27658
- var import_node_fs19, import_node_path21, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
27659
- var init_shared_session = __esm({
27660
- "src/auth/shared-session.ts"() {
27661
- "use strict";
27662
- import_node_fs19 = require("node:fs");
27663
- import_node_path21 = require("node:path");
27664
- init_home_paths();
27665
- SESSION_VERSION = 1;
27666
- SESSION_FILE_ENV = "SOOTSIM_SHARED_AUTH_FILE";
27667
- DEFAULT_ORIGIN = "https://contrast.dev";
27668
- }
27669
- });
27670
-
27671
- // cli/auth.ts
27672
- function looksLikeGitHubToken(token) {
27673
- return Boolean(
27674
- token && token.length >= 20 && GITHUB_TOKEN_PREFIXES.some((prefix) => token.startsWith(prefix))
27675
- );
27676
- }
27677
- function resolveGitHubActionsAuth() {
27678
- const contrastRunnerToken = process.env.CONTRAST_INSTALLATION_TOKEN?.trim();
27679
- const githubActionsToken = process.env.GITHUB_TOKEN?.trim();
27680
- const token = contrastRunnerToken || githubActionsToken;
27681
- if (!looksLikeGitHubToken(token)) return null;
27682
- const repoSlug = (process.env.CONTRAST_REPO || process.env.GITHUB_REPOSITORY || "").trim();
27683
- if (!repoSlug) return null;
27684
- const [owner, repo] = repoSlug.includes("/") ? repoSlug.split("/", 2) : [null, null];
27685
- const repositoryId = (process.env.CONTRAST_REPO_ID || process.env.GITHUB_REPOSITORY_ID || "").trim();
27686
- const installationId = (process.env.CONTRAST_INSTALLATION_ID || process.env.GITHUB_APP_INSTALLATION_ID || "").trim();
27687
- return {
27688
- kind: "github",
27689
- token,
27690
- repoId: repoSlug,
27691
- repositoryId: repositoryId || null,
27692
- owner: owner || null,
27693
- repo: repo || null,
27694
- installationId: installationId || null,
27695
- source: contrastRunnerToken ? "contrast-runner" : "github-actions"
27696
- };
27697
- }
27698
- function isCliApiKeySecret(secret) {
27699
- return secret.startsWith("sk_rnx_");
27700
- }
27701
- function readApiKeyFromEnv() {
27702
- const envName = "RNX_API_KEY";
27703
- const secret = process.env[envName]?.trim();
27704
- return secret && isCliApiKeySecret(secret) ? { secret, envName } : null;
27705
- }
27706
- function resolveCliAuth() {
27707
- const envKey = readApiKeyFromEnv();
27708
- if (envKey) {
27709
- return {
27710
- kind: "api-key",
27711
- secret: envKey.secret,
27712
- source: "env",
27713
- envName: envKey.envName
27714
- };
27715
- }
27716
- const githubAuth = resolveGitHubActionsAuth();
27717
- if (githubAuth) return githubAuth;
27718
- const session = readSharedDesktopAuthSession();
27719
- if (session?.token) {
27720
- return { kind: "session", token: session.token, origin: session.origin };
27721
- }
27722
- return null;
27723
- }
27724
- function authHeaderValue(auth) {
27725
- return auth.kind === "api-key" ? `Bearer ${auth.secret}` : `Bearer ${auth.token}`;
27726
- }
27727
- var GITHUB_TOKEN_PREFIXES;
27728
- var init_auth = __esm({
27729
- "cli/auth.ts"() {
27730
- "use strict";
27731
- init_shared_session();
27732
- init_run_rnx();
27733
- GITHUB_TOKEN_PREFIXES = ["ghs_", "ghp_", "gho_", "ghu_", "github_pat_"];
27734
- }
27735
- });
27736
-
27737
27822
  // cli/run-registry.ts
27738
27823
  var init_run_registry = __esm({
27739
27824
  "cli/run-registry.ts"() {