rnxsim 0.1.370 → 0.1.372

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 (207) hide show
  1. package/cli/bin.ts +2 -2
  2. package/cli/commands/box/checkout-plane.ts +65 -14
  3. package/cli/commands/box/cloud-box.ts +294 -0
  4. package/cli/commands/box/plane-share.ts +208 -0
  5. package/cli/commands/box/rnx-command.ts +2 -2
  6. package/cli/commands/box/search-command.ts +157 -0
  7. package/cli/commands/box.ts +269 -13
  8. package/cli/commands/control.ts +6 -1
  9. package/cli/outbound-endpoints.ts +37 -1
  10. package/dist-cli/bin.js +5 -5
  11. package/dist-cli/chunks/{agent-3R4424LW.js → agent-RTAKJ5UM.js} +2 -2
  12. package/dist-cli/chunks/{agent-wrapper-4PPWZWSY.js → agent-wrapper-JWRDI26D.js} +2 -2
  13. package/dist-cli/chunks/{app-fonts-KYXB6CXQ.js → app-fonts-RKO3N4PB.js} +2 -2
  14. package/dist-cli/chunks/{assert-7EZOWGYQ.js → assert-Y5LXPWC6.js} +2 -2
  15. package/dist-cli/chunks/{auth-L7M36H6C.js → auth-4SPTRWTM.js} +2 -2
  16. package/dist-cli/chunks/{beta-FKAF6YWJ.js → beta-KYS22QPY.js} +2 -2
  17. package/dist-cli/chunks/{box-5Y5VBMHV.js → box-7CK72ZPF.js} +617 -596
  18. package/dist-cli/chunks/{camera-XHF2RPFG.js → camera-RUL426MQ.js} +2 -2
  19. package/dist-cli/chunks/{chunk-EQ3OE2AV.js → chunk-2MA3Q44D.js} +1 -1
  20. package/dist-cli/chunks/{chunk-DHYTRCIP.js → chunk-2OA2WE7G.js} +2 -2
  21. package/dist-cli/chunks/{chunk-DMHSD6YJ.js → chunk-3C6LTIDV.js} +2 -2
  22. package/dist-cli/chunks/{chunk-NR2V2ONN.js → chunk-4UMODU6C.js} +2 -2
  23. package/dist-cli/chunks/{chunk-Z2VBSNFI.js → chunk-5Q6ZEZFF.js} +1 -1
  24. package/dist-cli/chunks/{chunk-LUOHRBJG.js → chunk-7FYOZQBM.js} +1 -1
  25. package/dist-cli/chunks/{chunk-Q4MRJ6PO.js → chunk-BRRWRBW7.js} +2 -2
  26. package/dist-cli/chunks/chunk-CEQXZ3GA.js +11 -0
  27. package/dist-cli/chunks/{chunk-DUXHYPBP.js → chunk-CZ2FOZVC.js} +2 -2
  28. package/dist-cli/chunks/{chunk-CMVRTZ5G.js → chunk-D5AT7UT5.js} +2 -2
  29. package/dist-cli/chunks/{chunk-D2GZASRV.js → chunk-DQOQQFMP.js} +1 -1
  30. package/dist-cli/chunks/{chunk-3UXXYJVD.js → chunk-EKSSAC4L.js} +1 -1
  31. package/dist-cli/chunks/{chunk-5BR33NAZ.js → chunk-EY5OHL2M.js} +1 -1
  32. package/dist-cli/chunks/{chunk-FK4JSTZ3.js → chunk-FOW2LPM7.js} +2 -2
  33. package/dist-cli/chunks/{chunk-WFQHT4QD.js → chunk-G5G7PQKO.js} +2 -2
  34. package/dist-cli/chunks/{chunk-WPV4H5S4.js → chunk-GPYG2L7R.js} +2 -2
  35. package/dist-cli/chunks/{chunk-3SOPYG7S.js → chunk-H2SKPUSI.js} +2 -2
  36. package/dist-cli/chunks/{chunk-6A6L3VJR.js → chunk-H567W7JT.js} +2 -2
  37. package/dist-cli/chunks/{chunk-PAUEZO6F.js → chunk-HDGZAVH3.js} +2 -2
  38. package/dist-cli/chunks/{chunk-AALKIAUZ.js → chunk-I37LRPZ3.js} +1 -1
  39. package/dist-cli/chunks/{chunk-JZLDHPB7.js → chunk-I7LRVN5M.js} +2 -2
  40. package/dist-cli/chunks/{chunk-3BG6UCQY.js → chunk-IX25FFF3.js} +11 -11
  41. package/dist-cli/chunks/{chunk-72S5FCBY.js → chunk-J2TJRPDB.js} +2 -2
  42. package/dist-cli/chunks/{chunk-RRX52O37.js → chunk-JPRHJMUJ.js} +1 -1
  43. package/dist-cli/chunks/{chunk-BMIJZNKD.js → chunk-K4TGUDY4.js} +2 -2
  44. package/dist-cli/chunks/{chunk-7C7TJYZ3.js → chunk-KSE5XGYU.js} +2 -2
  45. package/dist-cli/chunks/{chunk-OJZHECGH.js → chunk-KVVNCIJA.js} +1 -1
  46. package/dist-cli/chunks/{chunk-LXPHL6FO.js → chunk-KWLUOLYQ.js} +2 -2
  47. package/dist-cli/chunks/{chunk-C7BA5CCM.js → chunk-KXAE64HF.js} +2 -2
  48. package/dist-cli/chunks/{chunk-EWSWOWXK.js → chunk-L5BEWXQZ.js} +2 -2
  49. package/dist-cli/chunks/{chunk-D24EOJ4C.js → chunk-LAQKTHRM.js} +2 -2
  50. package/dist-cli/chunks/{chunk-IDLFVRPN.js → chunk-LWLHVF6Y.js} +1 -1
  51. package/dist-cli/chunks/{chunk-EOIBLKZO.js → chunk-MKHW3JNL.js} +2 -2
  52. package/dist-cli/chunks/{chunk-64JAEJ6N.js → chunk-MSM2E74F.js} +1 -1
  53. package/dist-cli/chunks/{chunk-DXV37CJN.js → chunk-NBEOA72C.js} +1 -1
  54. package/dist-cli/chunks/{chunk-H5X5FJ63.js → chunk-NLMDMNO6.js} +2 -2
  55. package/dist-cli/chunks/{chunk-6W4HIWC7.js → chunk-NT43X5LI.js} +2 -2
  56. package/dist-cli/chunks/{chunk-PNOENVMJ.js → chunk-NWPYM54M.js} +1 -1
  57. package/dist-cli/chunks/{chunk-O6QLKSAM.js → chunk-OLHHB6LD.js} +2 -2
  58. package/dist-cli/chunks/{chunk-MDCY532E.js → chunk-P6K6XFWE.js} +2 -2
  59. package/dist-cli/chunks/{chunk-LIMWG54N.js → chunk-PETP6LVI.js} +1 -1
  60. package/dist-cli/chunks/{chunk-YIZGNT7A.js → chunk-PKY76S57.js} +1 -1
  61. package/dist-cli/chunks/chunk-POR7R3TP.js +4 -0
  62. package/dist-cli/chunks/chunk-PUH6RQBJ.js +7 -0
  63. package/dist-cli/chunks/{chunk-NJMU5BN6.js → chunk-Q5WPPOFV.js} +1 -1
  64. package/dist-cli/chunks/{chunk-HQQTS2CB.js → chunk-Q7JSU5DI.js} +3 -3
  65. package/dist-cli/chunks/{chunk-U63T6LIQ.js → chunk-QA2PTSOC.js} +1 -1
  66. package/dist-cli/chunks/{chunk-2FOGXVOK.js → chunk-R7HMFLXI.js} +2 -2
  67. package/dist-cli/chunks/{chunk-FA75F34Z.js → chunk-RXKZJAGY.js} +1 -1
  68. package/dist-cli/chunks/{chunk-5RKRVS5A.js → chunk-SC7PGULZ.js} +2 -2
  69. package/dist-cli/chunks/{chunk-UFVAAWGZ.js → chunk-SLTEYNAE.js} +1 -1
  70. package/dist-cli/chunks/{chunk-6GPS7U4X.js → chunk-SSQCKJQZ.js} +2 -2
  71. package/dist-cli/chunks/{chunk-ARMXC3C3.js → chunk-SZX6ARJX.js} +3 -3
  72. package/dist-cli/chunks/{chunk-6S5LJV2O.js → chunk-U3XYGLXG.js} +2 -2
  73. package/dist-cli/chunks/chunk-UA5WQCBU.js +7 -0
  74. package/dist-cli/chunks/{chunk-VVQCZ42C.js → chunk-UVKX6FQ4.js} +1 -1
  75. package/dist-cli/chunks/{chunk-Z3DENGXW.js → chunk-VQQXUXXF.js} +2 -2
  76. package/dist-cli/chunks/{chunk-I52NT6VG.js → chunk-WR7LRCYW.js} +1 -1
  77. package/dist-cli/chunks/{chunk-HEKHLQ3B.js → chunk-XA4YU3A4.js} +3 -3
  78. package/dist-cli/chunks/{chunk-GRO74J2X.js → chunk-YEGWC2OY.js} +1 -1
  79. package/dist-cli/chunks/{chunk-AVJBT5LF.js → chunk-YWTBN7SD.js} +1 -1
  80. package/dist-cli/chunks/{chunk-PIZXOXBL.js → chunk-ZRQKEKHX.js} +2 -2
  81. package/dist-cli/chunks/{chunk-X6WQ4YWE.js → chunk-ZTZTRD5R.js} +1 -1
  82. package/dist-cli/chunks/{cleanup-MXMMJ7JW.js → cleanup-7ATBNZHC.js} +2 -2
  83. package/dist-cli/chunks/cli-version-D2IDQO7X.js +4 -0
  84. package/dist-cli/chunks/{compat-E6KE5KDI.js → compat-4ZC6ZMMA.js} +2 -2
  85. package/dist-cli/chunks/{config-QSLLVZDS.js → config-EOBODQ32.js} +2 -2
  86. package/dist-cli/chunks/{connect-QE4VU6GB.js → connect-ZQ72CZ7O.js} +2 -2
  87. package/dist-cli/chunks/control-FKPKLOE2.js +4 -0
  88. package/dist-cli/chunks/daemon-WFHWS3JU.js +4 -0
  89. package/dist-cli/chunks/{debug-5FG3MBJS.js → debug-7Z325GNU.js} +3 -3
  90. package/dist-cli/chunks/{desktop-5EZX7NX5.js → desktop-CR7ALX7H.js} +3 -3
  91. package/dist-cli/chunks/{detox-WJGQP5TM.js → detox-RZ37736M.js} +2 -2
  92. package/dist-cli/chunks/{device-D7LKCAMS.js → device-TA3SQP6K.js} +2 -2
  93. package/dist-cli/chunks/{diagnose-A3W6ZTGA.js → diagnose-JFKFKR3S.js} +2 -2
  94. package/dist-cli/chunks/disk-cleanup-HZM7VM4S.js +4 -0
  95. package/dist-cli/chunks/{do-chain-RUJNRPPF.js → do-chain-MTT65YNX.js} +2 -2
  96. package/dist-cli/chunks/drivers-BXEXWRVJ.js +4 -0
  97. package/dist-cli/chunks/{env-U67SQVTV.js → env-SFI24PLS.js} +2 -2
  98. package/dist-cli/chunks/{film-XC6UAQ3V.js → film-GPHSJLIH.js} +3 -3
  99. package/dist-cli/chunks/flow-FBCU3TZN.js +4 -0
  100. package/dist-cli/chunks/help-6MLROWYO.js +4 -0
  101. package/dist-cli/chunks/{hidden-runtime-alias-7S2TNZXG.js → hidden-runtime-alias-7Z6PYH53.js} +2 -2
  102. package/dist-cli/chunks/{home-paths-LMO5LB4F.js → home-paths-MEGVA6ET.js} +2 -2
  103. package/dist-cli/chunks/inspect-EWT5SXBD.js +4 -0
  104. package/dist-cli/chunks/install-desktop-IFBUH7MH.js +4 -0
  105. package/dist-cli/chunks/{login-JIU42S4C.js → login-EHUZYRAM.js} +2 -2
  106. package/dist-cli/chunks/{logout-JTAWQXZO.js → logout-WPGT62N6.js} +2 -2
  107. package/dist-cli/chunks/{maestro-TYBQYOGB.js → maestro-4HVDLCTI.js} +3 -3
  108. package/dist-cli/chunks/{maestro-generate-YLAGWVKE.js → maestro-generate-VBS2EZMM.js} +3 -3
  109. package/dist-cli/chunks/{mode-DV24ZRZ6.js → mode-MGTS4QTF.js} +2 -2
  110. package/dist-cli/chunks/{optional-demo-registry-JZH66VIM.js → optional-demo-registry-F3FQT5WT.js} +2 -2
  111. package/dist-cli/chunks/{perf-LXXJ4EAX.js → perf-47R3OEI7.js} +2 -2
  112. package/dist-cli/chunks/{permissions-KAOVZUPW.js → permissions-ZOLFZWR2.js} +2 -2
  113. package/dist-cli/chunks/{platform-KW35446W.js → platform-C4E26ZBZ.js} +3 -3
  114. package/dist-cli/chunks/privacy-WXGGG4ZI.js +4 -0
  115. package/dist-cli/chunks/{record-EK3ETFHZ.js → record-OUXWCWUF.js} +3 -3
  116. package/dist-cli/chunks/{report-issue-NW5PCAZF.js → report-issue-LJJXONXQ.js} +2 -2
  117. package/dist-cli/chunks/reset-ZZYLPETS.js +4 -0
  118. package/dist-cli/chunks/resolve-target-MVZZR2JW.js +4 -0
  119. package/dist-cli/chunks/runtime-DEC5AF3M.js +4 -0
  120. package/dist-cli/chunks/{screenshot-command-TQSPTFT2.js → screenshot-command-CUHDOS5D.js} +3 -3
  121. package/dist-cli/chunks/{screenshot-layers-IZP3L3AQ.js → screenshot-layers-DVWQVKT6.js} +2 -2
  122. package/dist-cli/chunks/{screenshots-capture-K7LJOVAE.js → screenshots-capture-GWIUJ3A6.js} +2 -2
  123. package/dist-cli/chunks/{serve-MMX4WXXG.js → serve-TX5OKGJM.js} +19 -19
  124. package/dist-cli/chunks/setup-X7LLGW67.js +4 -0
  125. package/dist-cli/chunks/{sim-client-KLAYIFOC.js → sim-client-W4WZOZ3P.js} +2 -2
  126. package/dist-cli/chunks/{skills-N4D3POIS.js → skills-TDFQAZ6A.js} +2 -2
  127. package/dist-cli/chunks/{state-ES6TFQZF.js → state-P2Z25OLQ.js} +2 -2
  128. package/dist-cli/chunks/{storage-52DR5V2C.js → storage-DWEOFXEB.js} +2 -2
  129. package/dist-cli/chunks/store-TKBLMYCV.js +4 -0
  130. package/dist-cli/chunks/telemetry-E3CBKJMD.js +4 -0
  131. package/dist-cli/chunks/{timeline-NRVNB5JR.js → timeline-55XNA4GX.js} +2 -2
  132. package/dist-cli/chunks/{upgrade-LNED5YQ2.js → upgrade-3XRCVDVC.js} +2 -2
  133. package/dist-cli/chunks/upload-QB77GSMQ.js +4 -0
  134. package/dist-cli/chunks/{version-VEF2DYGG.js → version-WTYGLXBJ.js} +2 -2
  135. package/dist-cli/chunks/{web-CGDE6S73.js → web-ZWRY3VHM.js} +2 -2
  136. package/dist-cli/chunks/welcome-7ATM5EGX.js +5 -0
  137. package/dist-cli/chunks/{what-happened-FSIDWL32.js → what-happened-Q3IGIH4K.js} +2 -2
  138. package/dist-cli/chunks/{ws-bridge-QXFQLRE7.js → ws-bridge-KCPZSBBX.js} +2 -2
  139. package/dist-lib/agent-daemon-client.cjs +1 -1
  140. package/dist-lib/agent-events.cjs +1 -1
  141. package/dist-lib/agent-identity.cjs +1 -1
  142. package/dist-lib/agent-sessions.cjs +1 -1
  143. package/dist-lib/attached-projects.cjs +1 -1
  144. package/dist-lib/auth/shared-session.cjs +1 -1
  145. package/dist-lib/backend-origin.cjs +1 -1
  146. package/dist-lib/beta.cjs +1 -1
  147. package/dist-lib/beta.mjs +1 -1
  148. package/dist-lib/bridge-constants.cjs +1 -1
  149. package/dist-lib/bridge-contract-input.cjs +1 -1
  150. package/dist-lib/bridge-contract-input.mjs +1 -1
  151. package/dist-lib/bridge-contract.cjs +1 -1
  152. package/dist-lib/bridge-contract.mjs +1 -1
  153. package/dist-lib/cli-constants.cjs +1 -1
  154. package/dist-lib/config.cjs +1 -1
  155. package/dist-lib/detox/index.cjs +1 -1
  156. package/dist-lib/dev-bundle-resolution.cjs +69 -34
  157. package/dist-lib/home-paths.cjs +1 -1
  158. package/dist-lib/host/bridge-host.cjs +430 -106
  159. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  160. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  161. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  162. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  163. package/dist-lib/host/websocket-proxy.cjs +1 -1
  164. package/dist-lib/index.cjs +1 -1
  165. package/dist-lib/jump-to-source-babel.cjs +1 -1
  166. package/dist-lib/menu.cjs +1 -1
  167. package/dist-lib/menu.mjs +1 -1
  168. package/dist-lib/metro-production-bundle.cjs +1 -1
  169. package/dist-lib/metro-production-bundle.mjs +1 -1
  170. package/dist-lib/metro.cjs +1 -1
  171. package/dist-lib/profiles.cjs +1 -1
  172. package/dist-lib/public-brand.cjs +1 -1
  173. package/dist-lib/react-native-host-modules.cjs +1 -1
  174. package/dist-lib/react-native-host-modules.mjs +1 -1
  175. package/dist-lib/render-mode.cjs +1 -1
  176. package/dist-lib/scripts/dev-server-scanner.cjs +149 -93
  177. package/dist-lib/sdk.cjs +1 -1
  178. package/dist-lib/sdk.mjs +1 -1
  179. package/dist-lib/skills.cjs +679 -307
  180. package/dist-lib/vite.cjs +1 -1
  181. package/package.json +1 -1
  182. package/scripts/dev-server-scanner.ts +275 -92
  183. package/src/dev-bundle-resolution.ts +123 -40
  184. package/src/disk-cleanup.ts +44 -0
  185. package/src/host/bridge-host.ts +11 -0
  186. package/src/host/plane-host.ts +203 -0
  187. package/dist-cli/chunks/chunk-GJRLPJQL.js +0 -6
  188. package/dist-cli/chunks/chunk-NTC46LSA.js +0 -4
  189. package/dist-cli/chunks/chunk-OTFTMGAL.js +0 -7
  190. package/dist-cli/chunks/cli-version-MLS55UTY.js +0 -4
  191. package/dist-cli/chunks/control-XD5F6K5T.js +0 -4
  192. package/dist-cli/chunks/daemon-UN5SE2JO.js +0 -4
  193. package/dist-cli/chunks/disk-cleanup-BDRHX5ZG.js +0 -4
  194. package/dist-cli/chunks/drivers-3NJ5RL6X.js +0 -4
  195. package/dist-cli/chunks/flow-FHZZD3P6.js +0 -4
  196. package/dist-cli/chunks/help-BYYDUSXF.js +0 -4
  197. package/dist-cli/chunks/inspect-RQCTWDCC.js +0 -4
  198. package/dist-cli/chunks/install-desktop-GOWXCQUE.js +0 -4
  199. package/dist-cli/chunks/privacy-MIFE72CG.js +0 -4
  200. package/dist-cli/chunks/reset-RZ4O5AFJ.js +0 -4
  201. package/dist-cli/chunks/resolve-target-7R5BNYMM.js +0 -4
  202. package/dist-cli/chunks/runtime-MZHLSRS6.js +0 -4
  203. package/dist-cli/chunks/setup-YFP3KEQZ.js +0 -4
  204. package/dist-cli/chunks/store-HOL45WCI.js +0 -4
  205. package/dist-cli/chunks/telemetry-BV6OTYPF.js +0 -4
  206. package/dist-cli/chunks/upload-RXKHFIBU.js +0 -4
  207. package/dist-cli/chunks/welcome-FT2AMUWW.js +0 -5
@@ -0,0 +1,157 @@
1
+ // `rg` inside a local box, run by git rather than by a JavaScript matcher.
2
+ //
3
+ // just-bash implements rg in JavaScript and reads every file body through the
4
+ // plane, so one search of this repo is ~15,000 plane reads. MEASURED in the box
5
+ // shell: 14,093ms and two lines of output, which are its abort message. It does
6
+ // not return results at all, and searching is the most common thing an agent
7
+ // does, so that is the box being unusable on a real project.
8
+ //
9
+ // WHY GIT GREP AND NOT RIPGREP. The first version of this shelled out to `rg`,
10
+ // which turned CI red: the Linux runners have no ripgrep binary, and a box that
11
+ // depends on whatever happens to be installed on the host is not a box. Git is
12
+ // different in kind, not degree: `CheckoutFilePlane` already REFUSES to open a
13
+ // project that is not a git repository and runs `git ls-files` before every
14
+ // single command, so git is a hard, already-enforced requirement. Searching
15
+ // with it adds no dependency and cannot be absent where the box already runs.
16
+ //
17
+ // It also makes the file set correct BY CONSTRUCTION rather than by agreement.
18
+ // The box indexes `git ls-files -co --exclude-standard`, tracked AND
19
+ // untracked-not-ignored, and `git grep --untracked` applies those same rules
20
+ // from the same tool. RAN against a checkout built to expose the difference:
21
+ // tracked, untracked-not-ignored, a dotfile and a dotdir file all match, and
22
+ // gitignored trees do not. Ripgrep needed the plane's path list piped into it
23
+ // to get there, because plain `rg .` silently misses dotfiles and `rg --hidden`
24
+ // searches .git/**.
25
+ //
26
+ // MEASURED on this repo, 14,949 indexed files: 391ms p50 against ripgrep's
27
+ // 260ms and just-bash's 14,093ms. Giving up 130ms to remove a host dependency
28
+ // and an entire class of "works on my machine" is the right trade.
29
+ //
30
+ // It also removes the ARG_MAX chunking the ripgrep version needed. Git walks
31
+ // its own index, so there is one process and one exit code rather than several
32
+ // whose "no match here" had to be merged.
33
+
34
+ import { spawn } from 'node:child_process'
35
+ import { defineCommand, type CustomCommand } from '@contrast/harness/shell'
36
+ import { PROJECT_MOUNT } from '@contrast/harness/shell'
37
+
38
+ // generous next to a 414ms p95: this bounds a wedged child, it does not cut off
39
+ // a large search.
40
+ const SEARCH_DEADLINE_MS = 20_000
41
+
42
+ /**
43
+ * THE DEADLINE SETTLES THIS PROMISE ITSELF rather than only killing the child.
44
+ * A spawned child's answer has been observed going missing while the process
45
+ * stayed alive, with no one able to say which event never arrived, so nothing
46
+ * that waits on a child event can be trusted to settle. A timer in this process
47
+ * cannot be missed, so it is the authority and the kill is a side effect.
48
+ * See docs/testing.md.
49
+ */
50
+ function runGitGrep(args: string[], cwd: string) {
51
+ return new Promise<{ stdout: string; stderr: string; exitCode: number }>((resolve) => {
52
+ const child = spawn('git', args, {
53
+ cwd,
54
+ env: process.env,
55
+ stdio: ['ignore', 'pipe', 'pipe'],
56
+ })
57
+ let stdout = ''
58
+ let stderr = ''
59
+ let settled = false
60
+ const finish = (exitCode: number) => {
61
+ if (settled) return
62
+ settled = true
63
+ clearTimeout(timer)
64
+ resolve({ stdout, stderr, exitCode })
65
+ }
66
+ const timer = setTimeout(() => {
67
+ try {
68
+ child.kill('SIGKILL')
69
+ } catch {}
70
+ stderr += `rg: search timed out after ${SEARCH_DEADLINE_MS}ms\n`
71
+ finish(2)
72
+ }, SEARCH_DEADLINE_MS)
73
+
74
+ child.stdout.on('data', (chunk) => {
75
+ stdout += String(chunk)
76
+ })
77
+ child.stderr.on('data', (chunk) => {
78
+ stderr += String(chunk)
79
+ })
80
+ child.once('error', (error) => {
81
+ stderr += `rg: ${error.message}\n`
82
+ finish(2)
83
+ })
84
+ child.once('close', (code) => finish(code ?? 0))
85
+ })
86
+ }
87
+
88
+ // the rg flags worth honouring, mapped onto git grep's spelling. anything else
89
+ // is REFUSED BY NAME rather than dropped, because a search that silently
90
+ // ignores `-i` returns a confidently wrong answer.
91
+ const FLAG_MAP: Record<string, string> = {
92
+ '-i': '-i',
93
+ '--ignore-case': '-i',
94
+ '-l': '-l',
95
+ '--files-with-matches': '-l',
96
+ '-w': '-w',
97
+ '--word-regexp': '-w',
98
+ '-c': '-c',
99
+ '--count': '-c',
100
+ '-v': '-v',
101
+ '--invert-match': '-v',
102
+ '-F': '-F',
103
+ '--fixed-strings': '-F',
104
+ }
105
+
106
+ export function createSearchCommand(options: { root: string }): CustomCommand {
107
+ return defineCommand('rg', async (args) => {
108
+ const flags: string[] = []
109
+ const bare: string[] = []
110
+ for (const arg of args) {
111
+ if (!arg.startsWith('-')) {
112
+ bare.push(arg)
113
+ continue
114
+ }
115
+ const mapped = FLAG_MAP[arg]
116
+ if (!mapped) {
117
+ return {
118
+ stdout: '',
119
+ stderr: `rg: this box does not support ${arg}\n`,
120
+ exitCode: 2,
121
+ }
122
+ }
123
+ flags.push(mapped)
124
+ }
125
+
126
+ const pattern = bare.shift()
127
+ if (pattern === undefined) {
128
+ return { stdout: '', stderr: 'rg: no pattern given\n', exitCode: 2 }
129
+ }
130
+
131
+ // paths are project-relative to git; strip the shell's mount prefix so
132
+ // `rg x /project/src` and `rg x src` mean the same thing.
133
+ const scopes = bare.map((p) =>
134
+ p.startsWith(`${PROJECT_MOUNT}/`) ? p.slice(PROJECT_MOUNT.length + 1) : p,
135
+ )
136
+
137
+ // --untracked is what makes this the plane's set rather than git's tracked
138
+ // set. -E for extended regex, the closest of git's grammars to ripgrep's.
139
+ // -e so a pattern beginning with a dash is a pattern, not a flag.
140
+ const result = await runGitGrep(
141
+ [
142
+ 'grep',
143
+ '--no-color',
144
+ '-n',
145
+ '-I',
146
+ '--untracked',
147
+ '-E',
148
+ ...flags,
149
+ '-e',
150
+ pattern,
151
+ ...(scopes.length > 0 ? ['--', ...scopes] : []),
152
+ ],
153
+ options.root,
154
+ )
155
+ return result
156
+ })
157
+ }
@@ -8,19 +8,41 @@
8
8
 
9
9
  import path from 'node:path'
10
10
  import { createInterface } from 'node:readline'
11
- import { createHarnessShell, HarnessProjectFs, PROJECT_MOUNT } from '@contrast/harness'
11
+ import {
12
+ createHarnessShell,
13
+ HarnessProjectFs,
14
+ PROJECT_MOUNT,
15
+ } from '@contrast/harness/shell'
12
16
  import { rnxPublicBrand } from '../../src/public-brand'
17
+ import { authHeaderOrExit } from '../auth'
13
18
  import { findRepositoryPackageRoot } from '../setup-repository'
14
19
  import { resolvePlacement } from '../ws-bridge'
15
20
  import { CheckoutFilePlane, CheckoutRequiresGitError } from './box/checkout-plane'
21
+ import {
22
+ CloudBoxError,
23
+ connectCloudBox,
24
+ createCloudBox,
25
+ heartbeatSession,
26
+ listCloudBoxes,
27
+ stopCloudBox,
28
+ } from './box/cloud-box'
29
+ import { sharePlane } from './box/plane-share'
16
30
  import { createRnxCommand } from './box/rnx-command'
31
+ import { createSearchCommand } from './box/search-command'
32
+ import type { HarnessShellResult } from '@contrast/harness/shell'
17
33
 
18
34
  interface BoxCommandOptions {
19
35
  port?: number
20
36
  verbose?: boolean
21
37
  }
22
38
 
23
- const CLOUD_SUBCOMMANDS = new Set(['create', 'ls', 'connect', 'stop', 'delete'])
39
+ const CLOUD_SUBCOMMANDS = new Set(['connect'])
40
+
41
+ // no default. a cloud box service has to be deployed before one can exist, and
42
+ // defaulting to an origin that answers nothing would advertise a capability
43
+ // that always fails.
44
+ const ENDPOINT_ENV = 'RNX_BOX_ENDPOINT'
45
+ const API_ORIGIN_ENV = 'RNX_API_ORIGIN'
24
46
 
25
47
  function usage(): void {
26
48
  const rnx = rnxPublicBrand.commandName
@@ -31,6 +53,12 @@ function usage(): void {
31
53
  ${rnx} box start a local box over the current checkout
32
54
  ${rnx} box create create a cloud box and enter its shell
33
55
  ${rnx} box create --name=<n> create a named cloud box
56
+ ${rnx} box create --endpoint=<url>
57
+ the box service to create it in, or set
58
+ ${ENDPOINT_ENV}
59
+ ${rnx} box ls cloud boxes running on this account
60
+ ${rnx} box stop --name=<n> stop a cloud box and its billing
61
+ ${rnx} box delete --name=<n> stop it and wipe its files
34
62
 
35
63
  a local box mounts this checkout at ${PROJECT_MOUNT}, gives you a shell with
36
64
  ls, cat, find, rg, sed, pipes and redirects, and registers ${rnx} itself as a
@@ -60,6 +88,11 @@ export async function runBox(
60
88
  }
61
89
 
62
90
  const subcommand = args.find((arg) => !arg.startsWith('-'))
91
+ if (subcommand === 'create') return runCloudBoxCreate(args, opts)
92
+ if (subcommand === 'stop' || subcommand === 'delete') {
93
+ return runCloudBoxStop(args, subcommand === 'delete')
94
+ }
95
+ if (subcommand === 'ls') return runCloudBoxList(args)
63
96
  if (subcommand !== undefined) {
64
97
  if (CLOUD_SUBCOMMANDS.has(subcommand)) {
65
98
  console.error(
@@ -78,6 +111,177 @@ export async function runBox(
78
111
  return runLocalBox(opts)
79
112
  }
80
113
 
114
+ function flag(args: string[], name: string): string | undefined {
115
+ const inline = args.find((arg) => arg.startsWith(`--${name}=`))
116
+ if (inline) return inline.slice(name.length + 3)
117
+ const index = args.indexOf(`--${name}`)
118
+ if (index >= 0 && args[index + 1] && !args[index + 1].startsWith('-')) {
119
+ return args[index + 1]
120
+ }
121
+ return undefined
122
+ }
123
+
124
+ function apiOrigin(args: string[]): string {
125
+ return (
126
+ flag(args, 'api-origin') ??
127
+ process.env[API_ORIGIN_ENV]?.trim() ??
128
+ rnxPublicBrand.origin
129
+ )
130
+ }
131
+
132
+ function requireEndpoint(args: string[], label: string): string | null {
133
+ const endpoint = flag(args, 'endpoint') ?? process.env[ENDPOINT_ENV]?.trim()
134
+ if (endpoint) return endpoint
135
+ const rnx = rnxPublicBrand.commandName
136
+ console.error(
137
+ ` ${rnx} box ${label}: no cloud box service to talk to.\n` +
138
+ ` set ${ENDPOINT_ENV} or pass --endpoint=<url>.`,
139
+ )
140
+ return null
141
+ }
142
+
143
+ async function runCloudBoxStop(args: string[], purge: boolean): Promise<number> {
144
+ const rnx = rnxPublicBrand.commandName
145
+ const label = purge ? 'delete' : 'stop'
146
+ const endpoint = requireEndpoint(args, label)
147
+ if (!endpoint) return 1
148
+ const name =
149
+ flag(args, 'name') ?? path.basename(findRepositoryPackageRoot(process.cwd()) ?? '')
150
+ if (!name) {
151
+ console.error(` ${rnx} box ${label}: which box? pass --name=<n>.`)
152
+ return 1
153
+ }
154
+ const { header } = authHeaderOrExit(`box ${label}`)
155
+ try {
156
+ const result = await stopCloudBox({
157
+ endpoint,
158
+ name,
159
+ apiKey: header.replace(/^Bearer /, ''),
160
+ purge,
161
+ })
162
+ console.log(
163
+ purge
164
+ ? ` deleted ${name}, freeing ${Number(result.freedBytes ?? 0)} bytes`
165
+ : ` stopped ${name}`,
166
+ )
167
+ return 0
168
+ } catch (error) {
169
+ if (error instanceof CloudBoxError) {
170
+ console.error(` ${rnx} box ${label}: ${error.message}`)
171
+ return 1
172
+ }
173
+ throw error
174
+ }
175
+ }
176
+
177
+ async function runCloudBoxList(args: string[]): Promise<number> {
178
+ const rnx = rnxPublicBrand.commandName
179
+ const { header } = authHeaderOrExit('box ls')
180
+ try {
181
+ const boxes = await listCloudBoxes({
182
+ apiOrigin: apiOrigin(args),
183
+ apiKey: header.replace(/^Bearer /, ''),
184
+ })
185
+ if (boxes.length === 0) {
186
+ console.log(` no cloud boxes running on this account`)
187
+ return 0
188
+ }
189
+ for (const box of boxes) {
190
+ const minutes = Math.round(box.meteredMs / 60_000)
191
+ console.log(` ${box.name ?? box.id} running ${minutes}m since ${box.startedAt}`)
192
+ }
193
+ return 0
194
+ } catch (error) {
195
+ if (error instanceof CloudBoxError) {
196
+ console.error(` ${rnx} box ls: ${error.message}`)
197
+ return 1
198
+ }
199
+ throw error
200
+ }
201
+ }
202
+
203
+ async function runCloudBoxCreate(
204
+ args: string[],
205
+ opts: BoxCommandOptions,
206
+ ): Promise<number> {
207
+ const rnx = rnxPublicBrand.commandName
208
+ const endpoint = flag(args, 'endpoint') ?? process.env[ENDPOINT_ENV]?.trim()
209
+ if (!endpoint) {
210
+ console.error(
211
+ ` ${rnx} box create: no cloud box service to create a box in.\n` +
212
+ ` set ${ENDPOINT_ENV} or pass --endpoint=<url>. \`${rnx} box\` runs the\n` +
213
+ ` same shell locally over this checkout.`,
214
+ )
215
+ return 1
216
+ }
217
+
218
+ const root = findRepositoryPackageRoot(process.cwd())
219
+ if (!root) {
220
+ console.error(
221
+ ` ${rnx} box create: no project here. a box is created from a checkout,\n` +
222
+ ` so run this inside a git repository that has a package.json.`,
223
+ )
224
+ return 1
225
+ }
226
+
227
+ const plane = new CheckoutFilePlane(root)
228
+ try {
229
+ await plane.refresh()
230
+ } catch (error) {
231
+ if (error instanceof CheckoutRequiresGitError) {
232
+ console.error(` ${rnx} box create: ${error.message}`)
233
+ return 1
234
+ }
235
+ throw error
236
+ }
237
+
238
+ const name = flag(args, 'name') ?? path.basename(root)
239
+ // the ACCOUNT's key. the box service forwards it to /v1/rnx/sessions, which
240
+ // is what authenticates this caller, names the account, and starts the
241
+ // session the box is billed against.
242
+ const { header } = authHeaderOrExit('box create')
243
+ const apiKey = header.replace(/^Bearer /, '')
244
+
245
+ console.log(` ...creating cloud box ${name}`)
246
+ let session: Awaited<ReturnType<typeof connectCloudBox>> | null = null
247
+ let heartbeat: { stop: () => void } | null = null
248
+ try {
249
+ const created = await createCloudBox({
250
+ endpoint,
251
+ name,
252
+ apiKey,
253
+ plane,
254
+ onProgress: opts.verbose
255
+ ? (seeded, total) => console.log(` ...seeded ${seeded}/${total} files`)
256
+ : undefined,
257
+ })
258
+ console.log(` ...seeded ${created.files} project files`)
259
+ // shown once and never stored: this process does not own a credential
260
+ // store, and inventing one here would be a second place secrets live.
261
+ console.log(` ...box token (shown once): ${created.token}`)
262
+ console.log(` ...accessing shell\n`)
263
+ session = await connectCloudBox({ endpoint, name, token: created.token })
264
+ // bank the wall clock while the shell is open. the box holds no account
265
+ // credential, so the client that is using it is what keeps the session
266
+ // alive; when this process dies the sweep closes it at the last heartbeat.
267
+ heartbeat = heartbeatSession({
268
+ apiOrigin: apiOrigin(args),
269
+ apiKey,
270
+ sessionId: created.sessionId,
271
+ })
272
+ return await repl(session, '')
273
+ } catch (error) {
274
+ if (error instanceof CloudBoxError) {
275
+ console.error(` ${rnx} box create: ${error.message}`)
276
+ return 1
277
+ }
278
+ throw error
279
+ } finally {
280
+ heartbeat?.stop()
281
+ session?.close()
282
+ }
283
+ }
284
+
81
285
  async function runLocalBox(opts: BoxCommandOptions): Promise<number> {
82
286
  // findRepositoryPackageRoot answers both halves of what a box needs: the
83
287
  // nearest package.json, and only when it sits inside a git repository. git
@@ -109,35 +313,90 @@ async function runLocalBox(opts: BoxCommandOptions): Promise<number> {
109
313
 
110
314
  const shell = createHarnessShell({
111
315
  projectFs: new HarnessProjectFs(plane),
112
- commands: [createRnxCommand({ root, shellCwd: () => shell.cwd })],
316
+ commands: [
317
+ createRnxCommand({ root, shellCwd: () => shell.cwd }),
318
+ // overrides just-bash's JavaScript rg, which reads every file body
319
+ // through the plane. see search-command.ts for the numbers.
320
+ createSearchCommand({ root }),
321
+ ],
113
322
  })
114
323
 
115
324
  const relative = path.relative(root, process.cwd())
116
325
  console.log(` ...starting local box`)
117
326
  console.log(` ...mounting ${root} at ${PROJECT_MOUNT}`)
327
+
328
+ // authorize this checkout for the bridge daemon so a browser tab can reach
329
+ // this box's plane. deliberately NOT awaited: a box must not pay for a
330
+ // bridge that may not be there, and an older daemon never answers at all.
331
+ const shared = sharePlane(root, { port: opts.port })
332
+ // two boxes over one checkout is worth SAYING, not refusing. a person with
333
+ // two terminals in one project is doing something reasonable, and the plane
334
+ // re-reads the file list before every command, so neither box works from a
335
+ // stale one. what it does break is an agent mid-run, whose file set changes
336
+ // underneath it because something else is writing the tree. that is the wave
337
+ // case, and the wave's own isolation check is where a refusal belongs; this
338
+ // reports the fact so that check has something to refuse on.
339
+ void shared.settled.then((outcome) => {
340
+ if ('declined' in outcome) {
341
+ if (opts.verbose) console.log(` ...plane not shared: ${outcome.declined}`)
342
+ return
343
+ }
344
+ if (outcome.holders > 1) {
345
+ console.error(
346
+ `\n ${rnxPublicBrand.commandName} box: ${outcome.holders - 1} other box${
347
+ outcome.holders > 2 ? 'es are' : ' is'
348
+ } already open on this checkout.\n` +
349
+ ` writes from either land on the same files. that is fine for a person\n` +
350
+ ` with two terminals, and not fine for an agent, whose file set will\n` +
351
+ ` change underneath it mid-run.\n`,
352
+ )
353
+ return
354
+ }
355
+ if (opts.verbose) {
356
+ console.log(` ...plane shared on the bridge (port ${outcome.port})`)
357
+ }
358
+ })
359
+
118
360
  console.log(` ...accessing shell\n`)
119
361
  if (opts.verbose) {
120
362
  console.log(` ${plane.index().size} project files indexed\n`)
121
363
  }
122
364
 
123
- return repl(shell, plane, relative)
365
+ try {
366
+ // the checkout is shared with editors, agents, and the user's own terminal,
367
+ // so re-read the file set before every command rather than trusting a
368
+ // snapshot taken when the box started. a cloud box has no equivalent: its
369
+ // store is the box, and the box refreshes on its own side.
370
+ return await repl(shell, relative, () => plane.refresh())
371
+ } finally {
372
+ shared?.close()
373
+ }
374
+ }
375
+
376
+ // the REPL drives whatever holds the shell. locally that is an in-process
377
+ // HarnessShell over this checkout; for a cloud box it is a socket to the box,
378
+ // which holds both the files and the shell. same loop, because the only
379
+ // difference is where exec runs.
380
+ interface BoxSession {
381
+ readonly cwd: string
382
+ exec(command: string): Promise<HarnessShellResult>
124
383
  }
125
384
 
126
385
  async function repl(
127
- shell: ReturnType<typeof createHarnessShell>,
128
- plane: CheckoutFilePlane,
386
+ session: BoxSession,
129
387
  startRelative: string,
388
+ beforeCommand?: () => Promise<void>,
130
389
  ): Promise<number> {
131
390
  const rl = createInterface({ input: process.stdin, output: process.stdout })
132
391
 
133
392
  // enter where the user was standing, so `rnx box` from a subdirectory does
134
393
  // not silently teleport them to the project root.
135
394
  if (startRelative && !startRelative.startsWith('..')) {
136
- await shell.exec(`cd ${JSON.stringify(startRelative)}`)
395
+ await session.exec(`cd ${JSON.stringify(startRelative)}`)
137
396
  }
138
397
 
139
398
  const prompt = () => {
140
- rl.setPrompt(`${shell.cwd} > `)
399
+ rl.setPrompt(`${session.cwd} > `)
141
400
  rl.prompt()
142
401
  }
143
402
 
@@ -155,11 +414,8 @@ async function repl(
155
414
  const run = async (command: string) => {
156
415
  if (stopped) return
157
416
  try {
158
- // the checkout is shared with editors, agents, and the user's own
159
- // terminal, so re-read the file set before every command rather than
160
- // trusting a snapshot taken when the box started.
161
- await plane.refresh()
162
- const result = await shell.exec(command)
417
+ await beforeCommand?.()
418
+ const result = await session.exec(command)
163
419
  if (result.text && result.text !== '(no output)') console.log(result.text)
164
420
  } catch (error) {
165
421
  console.error(error instanceof Error ? error.message : String(error))
@@ -388,7 +388,12 @@ async function resolveConnectionInputForCli(target: string): Promise<ResolvedDev
388
388
 
389
389
  if (registeredPort && registeredPort > 0) {
390
390
  const discovered = await probePort(registeredPort)
391
- if (discovered) {
391
+ // a provisional bundle URL means the scanner's fast manifest probe got no
392
+ // answer and fell back to the generic `/index.bundle`. that is enough to
393
+ // LIST the server and not enough to open it, so hand the question to
394
+ // `resolveConnectionInput`, which asks the manifest again with a budget
395
+ // that fits a real dev server and returns the entry point the app names.
396
+ if (discovered && !discovered.bundleUrlProvisional) {
392
397
  return {
393
398
  bundleUrl: discovered.bundleUrl,
394
399
  port: discovered.port,
@@ -82,6 +82,18 @@ export const OUTBOUND_ENDPOINTS: Record<string, OutboundEndpointDeclaration> = {
82
82
  preConsent: false,
83
83
  payload: 'preview objects selected by the user for upload',
84
84
  },
85
+ // its own entry, deliberately not folded into contrast_user_action. this is
86
+ // the only egress that sends the CONTENTS of the user's project somewhere,
87
+ // and to an operator-chosen host rather than a Contrast one, so it must not
88
+ // be readable as "the usual Contrast call".
89
+ rnx_cloud_box: {
90
+ category: 'explicit-user-action',
91
+ destination:
92
+ 'the RNX cloud box service named by --endpoint or RNX_BOX_ENDPOINT; no default, so this call cannot happen unless the user names a host',
93
+ preConsent: false,
94
+ payload:
95
+ 'every file git reports for the current checkout, then shell commands and file reads and writes for as long as the box shell is open',
96
+ },
85
97
  }
86
98
 
87
99
  export interface OutboundCallDeclaration {
@@ -93,6 +105,30 @@ export interface OutboundCallDeclaration {
93
105
  // a new fetch, websocket, node HTTP request, or TCP connection has no entry and
94
106
  // fails the package test until its destination and consent behavior are declared.
95
107
  export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> = {
108
+ // `rnx box create` uploads the checkout to the box service and then talks to
109
+ // it. the destination is whatever the user named; there is no default.
110
+ 'packages/sootsim/cli/commands/box/cloud-box.ts :: fetch :: endpointOf(base, name, verb)':
111
+ { category: 'rnx_cloud_box', count: 1 },
112
+ 'packages/sootsim/cli/commands/box/cloud-box.ts :: websocket :: url': {
113
+ category: 'rnx_cloud_box',
114
+ count: 1,
115
+ },
116
+ // the account's own session meter: starting, listing and heartbeating the
117
+ // session a cloud box is billed against. no project content.
118
+ "packages/sootsim/cli/commands/box/cloud-box.ts :: fetch :: `${options.apiOrigin.replace(/\\/+$/, '')}/v1/rnx/sessions`":
119
+ { category: 'contrast_user_action', count: 1 },
120
+ 'packages/sootsim/cli/commands/box/cloud-box.ts :: fetch :: url': {
121
+ category: 'contrast_user_action',
122
+ count: 1,
123
+ },
124
+ // a LOCAL box authorizing its own checkout with the loopback bridge daemon.
125
+ // this one refuses to guess a port, so it cannot reach anything but a bridge
126
+ // the user's own session is running; see the note on ws-bridge.ts below
127
+ // about splitting these if bridgeWsUrl ever returns a remote endpoint.
128
+ 'packages/sootsim/cli/commands/box/plane-share.ts :: websocket :: bridgeWsUrl(port)': {
129
+ category: 'local_development',
130
+ count: 2,
131
+ },
96
132
  'packages/contrast-runtime-delivery/src/index.ts :: fetch :: url': {
97
133
  category: 'runtime_delivery',
98
134
  count: 2,
@@ -101,7 +137,7 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
101
137
  category: 'local_development',
102
138
  count: 1,
103
139
  },
104
- 'packages/sootsim/scripts/dev-server-scanner.ts :: http.request :: { hostname, port, path, method, timeout, headers }':
140
+ 'packages/sootsim/scripts/dev-server-scanner.ts :: http.request :: { hostname, port, path, method, headers }':
105
141
  {
106
142
  category: 'local_development',
107
143
  count: 1,
package/dist-cli/bin.js CHANGED
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- /*! rnx v0.1.370 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ /*! rnx v0.1.372 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
3
3
  import { createRequire as __sootsimCreateRequire } from 'node:module'
4
4
  var require = __sootsimCreateRequire(import.meta.url)
5
- import{a as g,b as f}from"./chunks/chunk-YIZGNT7A.js";import{a as h}from"./chunks/chunk-AVJBT5LF.js";import{a as m}from"./chunks/chunk-64JAEJ6N.js";import"./chunks/chunk-I52NT6VG.js";function k(a){let t=a instanceof Error?a.message:String(a);(/^command timed out after \d+s$/.test(t)||t.startsWith("sim disconnected:")||t.startsWith("bridge never reconnected")||t.startsWith("multiple sims are connected:")||t.startsWith("saved sim ")||t.startsWith("no sim connected with id ")||t.startsWith("could not connect to ws://")||t.startsWith("rnx bridge daemon is not running")||t.startsWith("rnx bridge lockfile is fresh")||t.startsWith("rnx bridge exited before becoming ready")||t.startsWith(`${m.commandName} open: requested bridge port`)||t.startsWith("background daemon setup is unavailable")||t.startsWith("rnx bridge did not start within"))&&!process.env.SOOTSIM_VERBOSE&&(process.stderr.write(` ${t}
5
+ import{a as g,b as f}from"./chunks/chunk-PKY76S57.js";import{a as h}from"./chunks/chunk-YWTBN7SD.js";import{a as m}from"./chunks/chunk-MSM2E74F.js";import"./chunks/chunk-WR7LRCYW.js";function k(a){let t=a instanceof Error?a.message:String(a);(/^command timed out after \d+s$/.test(t)||t.startsWith("sim disconnected:")||t.startsWith("bridge never reconnected")||t.startsWith("multiple sims are connected:")||t.startsWith("saved sim ")||t.startsWith("no sim connected with id ")||t.startsWith("could not connect to ws://")||t.startsWith("rnx bridge daemon is not running")||t.startsWith("rnx bridge lockfile is fresh")||t.startsWith("rnx bridge exited before becoming ready")||t.startsWith(`${m.commandName} open: requested bridge port`)||t.startsWith("background daemon setup is unavailable")||t.startsWith("rnx bridge did not start within"))&&!process.env.SOOTSIM_VERBOSE&&(process.stderr.write(` ${t}
6
6
  `),process.exit(1)),a instanceof Error&&a.stack?process.stderr.write(`${a.stack}
7
7
  `):process.stderr.write(`${t}
8
8
  `),process.exit(1)}process.on("unhandledRejection",k);process.on("uncaughtException",k);var v=typeof __SOOTSIM_STANDALONE__<"u"&&__SOOTSIM_STANDALONE__,C=new Set(["desktop"]);function $(a){!v||!C.has(a)||(process.stderr.write(` ${m.commandName} ${a} isn't available in the standalone binary \u2014
9
9
  it needs vite / electron / playwright from a project's node_modules.
10
10
  run the npm package instead:
11
11
  ${m.commandName} ${a}
12
- `),process.exit(1))}var e=f(process.argv);if(!e.command&&e.commandArgs.length>0){let{resolveHiddenRuntimeAlias:a}=await import("./chunks/hidden-runtime-alias-7S2TNZXG.js");e=a(e)}var _=e.command==="cleanup"&&e.commandArgs.length===1&&e.commandArgs[0]==="--automatic-worker",x=!e.command&&e.commandArgs.length===0;if(!_&&!x&&!e.help&&!e.version&&e.command!=="config"&&e.command!=="setup"&&e.command!=="telemetry"){let{ensurePrivacyChoice:a}=await import("./chunks/privacy-MIFE72CG.js");await a({prompt:!0})}if(e.command!=="serve"&&e.command!=="daemon"){let{consumeRuntimeUpgradeNotice:a}=await import("./chunks/home-paths-LMO5LB4F.js"),t=a();t&&process.stderr.write(` ${m.name} engine upgraded to v${t.to}`+(t.from?` (from v${t.from})`:"")+` \xB7 what's new: ${m.origin}/changelog
13
- `)}var b=!1;async function S(){if(!b&&(b=!0,!(e.command==="cleanup"||e.command==="serve"||e.command==="agent-wrapper"||e.command==="daemon"&&e.commandArgs[0]==="uninstall")))try{let{isAutomaticSootsimCleanupComplete:a}=await import("./chunks/disk-cleanup-BDRHX5ZG.js");if(a())return;let{spawn:t}=await import("node:child_process"),r=["cleanup","--automatic-worker"],d=process.argv[1],p=v?t(process.execPath,r,{detached:!0,stdio:["ignore","ignore","inherit"],env:process.env}):d?t(process.execPath,[d,...r],{detached:!0,stdio:["ignore","ignore","inherit"],env:process.env}):t(m.commandName,r,{detached:!0,stdio:["ignore","ignore","inherit"],env:process.env});p.once("error",w=>{process.stderr.write(` ${m.name} automatic cleanup will retry on the next run: ${w.message}
12
+ `),process.exit(1))}var e=f(process.argv);if(!e.command&&e.commandArgs.length>0){let{resolveHiddenRuntimeAlias:a}=await import("./chunks/hidden-runtime-alias-7Z6PYH53.js");e=a(e)}var _=e.command==="cleanup"&&e.commandArgs.length===1&&e.commandArgs[0]==="--automatic-worker",x=!e.command&&e.commandArgs.length===0;if(!_&&!x&&!e.help&&!e.version&&e.command!=="config"&&e.command!=="setup"&&e.command!=="telemetry"){let{ensurePrivacyChoice:a}=await import("./chunks/privacy-WXGGG4ZI.js");await a({prompt:!0})}if(e.command!=="serve"&&e.command!=="daemon"){let{consumeRuntimeUpgradeNotice:a}=await import("./chunks/home-paths-MEGVA6ET.js"),t=a();t&&process.stderr.write(` ${m.name} engine upgraded to v${t.to}`+(t.from?` (from v${t.from})`:"")+` \xB7 what's new: ${m.origin}/changelog
13
+ `)}var b=!1;async function S(){if(!b&&(b=!0,!(e.command==="cleanup"||e.command==="serve"||e.command==="agent-wrapper"||e.command==="daemon"&&e.commandArgs[0]==="uninstall")))try{let{shouldSkipAutomaticSootsimCleanup:a}=await import("./chunks/disk-cleanup-HZM7VM4S.js");if(a())return;let{spawn:t}=await import("node:child_process"),r=["cleanup","--automatic-worker"],d=process.argv[1],p=v?t(process.execPath,r,{detached:!0,stdio:["ignore","ignore","inherit"],env:process.env}):d?t(process.execPath,[d,...r],{detached:!0,stdio:["ignore","ignore","inherit"],env:process.env}):t(m.commandName,r,{detached:!0,stdio:["ignore","ignore","inherit"],env:process.env});p.once("error",w=>{process.stderr.write(` ${m.name} automatic cleanup will retry on the next run: ${w.message}
14
14
  `)}),p.unref()}catch(a){process.stderr.write(` ${m.name} automatic cleanup will retry on the next run: ${a instanceof Error?a.message:String(a)}
15
- `)}}async function n(a){let{flushCliTelemetry:t}=await import("./chunks/telemetry-BV6OTYPF.js");await t(),await S(),process.exit(a)}if(e.version){let{getCliVersion:a}=await import("./chunks/cli-version-MLS55UTY.js"),{IS_BETA:t,BETA_LABEL:r}=await import("./chunks/beta-FKAF6YWJ.js"),d=t?` \xB7 ${r}`:"";console.log(`${m.commandName} v${a()}${d}`);let{readActiveRuntime:p}=await import("./chunks/home-paths-LMO5LB4F.js"),w=p();console.log(w?`runtime v${w}`:"runtime not installed"),await n(0)}if(e.help&&!e.command){let{printHelp:a}=await import("./chunks/help-BYYDUSXF.js");a(),await n(0)}var o=e.globalFlags.port,c=e.verbose,u=e.globalFlags.device,l=e.globalFlags.theme,E=e.globalFlags.driver,O=e.globalFlags.headless===!0,A=e.globalFlags.sim??e.globalFlags.session??e.globalFlags.tab,i=A?["--sim",A,...e.commandArgs]:e.commandArgs;if(u||l){let{settingsStore:a}=await import("./chunks/store-HOL45WCI.js"),t={};u&&(t.deviceModel=u),l&&(t.colorScheme=l),a.apply(t)}if(!e.command&&e.commandArgs.length===0){let{runWelcome:a}=await import("./chunks/welcome-FT2AMUWW.js");await n(await a())}var s=e.command??"";if(!s){if(e.commandArgs.length>0){let t=e.commandArgs.find(r=>!r.startsWith("-"))??e.commandArgs[0];console.error(` unknown command: ${t}`),console.error(` run \`${m.commandName} --help\` to see the full surface.`),await n(1)}let{printHelp:a}=await import("./chunks/help-BYYDUSXF.js");a(),await n(0)}if(e.help||e.commandArgs.includes("--help")||e.commandArgs.includes("-h")){if(s==="skill"){let{runSkill:r}=await import("./chunks/skills-N4D3POIS.js");await r(e.commandArgs),await n(0)}if(s==="state"){let{runState:r}=await import("./chunks/state-ES6TFQZF.js");await n(await r(i,{port:o}))}if(s==="reset"){let{runReset:r}=await import("./chunks/reset-RZ4O5AFJ.js");await n(await r(i,{port:o}))}let a=s==="do"||s==="get"||s==="debug"||s==="shell"||s==="perf"||s==="wait",t=e.commandArgs.find(r=>!r.startsWith("-"));if(!(s==="shell"||s==="perf"))if(a&&!t){let{printGroupHelp:r}=await import("./chunks/help-BYYDUSXF.js");r(s)&&await n(0)}else{let{printCommandHelp:r}=await import("./chunks/help-BYYDUSXF.js");r(a&&t?t:s,{prefer:a&&t?"verb":"command",group:a&&t?s:void 0}),await n(0)}}$(s);try{if(g.has(s)){if(s==="do"){let{hasDoChain:t,runDoChain:r}=await import("./chunks/do-chain-RUJNRPPF.js");t(i)&&await n(await r(i,{port:o}))}let{runInspect:a}=await import("./chunks/inspect-RQCTWDCC.js");await a([s,...i],{port:o,verbose:c})}else switch(s){case"assert":{let{runAssert:a}=await import("./chunks/assert-7EZOWGYQ.js");await a(e.commandArgs);break}case"detox":{let{runDetox:a}=await import("./chunks/detox-WJGQP5TM.js");await a(e.commandArgs,{port:o,verbose:c});break}case"maestro":{let{runMaestro:a}=await import("./chunks/maestro-TYBQYOGB.js"),t=await a(i,{port:o,verbose:c});await n(typeof t=="number"?t:0)}case"record":{let{runRecord:a}=await import("./chunks/record-EK3ETFHZ.js");await a(i,{port:o,verbose:c});break}case"film":{let{runFilm:a}=await import("./chunks/film-XC6UAQ3V.js"),t=await a(i,{port:o,verbose:c});await n(typeof t=="number"?t:0)}case"storage":{let{runStorage:a}=await import("./chunks/storage-52DR5V2C.js"),t=await a(i,{port:o,verbose:c});await n(typeof t=="number"?t:0)}case"state":{let{runState:a}=await import("./chunks/state-ES6TFQZF.js"),t=await a(i,{port:o});await n(t)}case"reset":{let{runReset:a}=await import("./chunks/reset-RZ4O5AFJ.js"),t=await a(i,{port:o});await n(t)}case"perf":{let{runPerf:a}=await import("./chunks/perf-LXXJ4EAX.js"),t=await a(i,{port:o,verbose:c});await n(typeof t=="number"?t:0)}case"screenshot":{let{runScreenshotCommand:a}=await import("./chunks/screenshot-command-TQSPTFT2.js"),t=await a(i,{port:o,verbose:c});await n(typeof t=="number"?t:0)}case"camera":{let{runCamera:a}=await import("./chunks/camera-XHF2RPFG.js"),t=await a(i,{port:o});await n(typeof t=="number"?t:0)}case"mode":{let{runMode:a}=await import("./chunks/mode-DV24ZRZ6.js");await a(i,{port:o,verbose:c});break}case"permissions":{let{runPermissions:a}=await import("./chunks/permissions-KAOVZUPW.js"),t=await a(i,{port:o,verbose:c});await n(t)}case"debug":{let{runDebug:a}=await import("./chunks/debug-5FG3MBJS.js");await a(i,{port:o,verbose:c});break}case"timeline":{let{runTimeline:a}=await import("./chunks/timeline-NRVNB5JR.js");await a(i,{port:o,verbose:c});break}case"what-happened":{let{runWhatHappened:a}=await import("./chunks/what-happened-FSIDWL32.js");await a(i,{port:o,verbose:c});break}case"open":{let{runOpenCommand:a}=await import("./chunks/control-XD5F6K5T.js");await a(i,{port:o});break}case"ios":case"android":{let{runPlatformCommand:a}=await import("./chunks/platform-KW35446W.js"),t=await a(s,i,{port:o});await n(typeof t=="number"?t:0)}case"box":{let{runBox:a}=await import("./chunks/box-5Y5VBMHV.js");await n(await a(i,{port:o,verbose:c}))}case"use":{let{runUseCommand:a}=await import("./chunks/control-XD5F6K5T.js");await a(i,{port:o});break}case"claim":{let{runClaimCommand:a}=await import("./chunks/control-XD5F6K5T.js");await a(i,{port:o});break}case"close":{let{runCloseCommand:a}=await import("./chunks/control-XD5F6K5T.js");await a(i,{port:o});break}case"device":{let{runDeviceCommand:a}=await import("./chunks/device-D7LKCAMS.js");await a(i,{port:o});break}case"compat":{let{runCompat:a}=await import("./chunks/compat-E6KE5KDI.js");await a(e.commandArgs);break}case"report-issue":{let{runReportIssue:a}=await import("./chunks/report-issue-NW5PCAZF.js"),t=await a(e.commandArgs);await n(t)}case"desktop":{let{runDesktop:a}=await import("./chunks/desktop-5EZX7NX5.js");await a(e.commandArgs,{port:o,device:u});break}case"login":{let{runLogin:a}=await import("./chunks/login-JIU42S4C.js");await a(e.commandArgs);break}case"logout":{let{runLogout:a}=await import("./chunks/logout-JTAWQXZO.js");await a();break}case"auth":{let{runAuth:a}=await import("./chunks/auth-L7M36H6C.js");await a(e.commandArgs);break}case"setup":{let{runSetup:a}=await import("./chunks/setup-YFP3KEQZ.js");await a(e.commandArgs);break}case"serve":{let{runServe:a}=await import("./chunks/serve-MMX4WXXG.js");await a(e.commandArgs,{port:o});break}case"daemon":{let{runDaemon:a}=await import("./chunks/daemon-UN5SE2JO.js");await a(e.commandArgs,{port:o});break}case"runtime":{let{runRuntime:a}=await import("./chunks/runtime-MZHLSRS6.js");await a(e.commandArgs);break}case"upgrade":{let{runUpgrade:a}=await import("./chunks/upgrade-LNED5YQ2.js");await a(e.commandArgs);break}case"version":{let{runVersion:a}=await import("./chunks/version-VEF2DYGG.js");await a(e.commandArgs);break}case"agent":{let{runAgentCommand:a}=await import("./chunks/agent-3R4424LW.js"),t=await a(e.commandArgs);await n(t)}case"agent-wrapper":{let{runAgentWrapper:a}=await import("./chunks/agent-wrapper-4PPWZWSY.js"),t=await a(e.commandArgs);await n(t)}case"skill":{let{runSkill:a}=await import("./chunks/skills-N4D3POIS.js");await a(e.commandArgs);break}case"app-fonts":{let{runAppFonts:a}=await import("./chunks/app-fonts-KYXB6CXQ.js");await a(e.commandArgs);break}case"config":{let{runConfig:a}=await import("./chunks/config-QSLLVZDS.js");await a(e.commandArgs);break}case"cleanup":{let{runCleanup:a}=await import("./chunks/cleanup-MXMMJ7JW.js"),t=await a(e.commandArgs);await n(t)}default:console.error(` unknown command: ${s}`),console.error(` run \`${m.commandName} --help\` to see the full surface.`),await n(1)}}catch(a){throw a instanceof h&&await n(a.code),a}await S();
15
+ `)}}async function n(a){let{flushCliTelemetry:t}=await import("./chunks/telemetry-E3CBKJMD.js");await t(),await S(),process.exit(a)}if(e.version){let{getCliVersion:a}=await import("./chunks/cli-version-D2IDQO7X.js"),{IS_BETA:t,BETA_LABEL:r}=await import("./chunks/beta-KYS22QPY.js"),d=t?` \xB7 ${r}`:"";console.log(`${m.commandName} v${a()}${d}`);let{readActiveRuntime:p}=await import("./chunks/home-paths-MEGVA6ET.js"),w=p();console.log(w?`runtime v${w}`:"runtime not installed"),await n(0)}if(e.help&&!e.command){let{printHelp:a}=await import("./chunks/help-6MLROWYO.js");a(),await n(0)}var o=e.globalFlags.port,c=e.verbose,u=e.globalFlags.device,l=e.globalFlags.theme,E=e.globalFlags.driver,O=e.globalFlags.headless===!0,A=e.globalFlags.sim??e.globalFlags.session??e.globalFlags.tab,i=A?["--sim",A,...e.commandArgs]:e.commandArgs;if(u||l){let{settingsStore:a}=await import("./chunks/store-TKBLMYCV.js"),t={};u&&(t.deviceModel=u),l&&(t.colorScheme=l),a.apply(t)}if(!e.command&&e.commandArgs.length===0){let{runWelcome:a}=await import("./chunks/welcome-7ATM5EGX.js");await n(await a())}var s=e.command??"";if(!s){if(e.commandArgs.length>0){let t=e.commandArgs.find(r=>!r.startsWith("-"))??e.commandArgs[0];console.error(` unknown command: ${t}`),console.error(` run \`${m.commandName} --help\` to see the full surface.`),await n(1)}let{printHelp:a}=await import("./chunks/help-6MLROWYO.js");a(),await n(0)}if(e.help||e.commandArgs.includes("--help")||e.commandArgs.includes("-h")){if(s==="skill"){let{runSkill:r}=await import("./chunks/skills-TDFQAZ6A.js");await r(e.commandArgs),await n(0)}if(s==="state"){let{runState:r}=await import("./chunks/state-P2Z25OLQ.js");await n(await r(i,{port:o}))}if(s==="reset"){let{runReset:r}=await import("./chunks/reset-ZZYLPETS.js");await n(await r(i,{port:o}))}let a=s==="do"||s==="get"||s==="debug"||s==="shell"||s==="perf"||s==="wait",t=e.commandArgs.find(r=>!r.startsWith("-"));if(!(s==="shell"||s==="perf"))if(a&&!t){let{printGroupHelp:r}=await import("./chunks/help-6MLROWYO.js");r(s)&&await n(0)}else{let{printCommandHelp:r}=await import("./chunks/help-6MLROWYO.js");r(a&&t?t:s,{prefer:a&&t?"verb":"command",group:a&&t?s:void 0}),await n(0)}}$(s);try{if(g.has(s)){if(s==="do"){let{hasDoChain:t,runDoChain:r}=await import("./chunks/do-chain-MTT65YNX.js");t(i)&&await n(await r(i,{port:o}))}let{runInspect:a}=await import("./chunks/inspect-EWT5SXBD.js");await a([s,...i],{port:o,verbose:c})}else switch(s){case"assert":{let{runAssert:a}=await import("./chunks/assert-Y5LXPWC6.js");await a(e.commandArgs);break}case"detox":{let{runDetox:a}=await import("./chunks/detox-RZ37736M.js");await a(e.commandArgs,{port:o,verbose:c});break}case"maestro":{let{runMaestro:a}=await import("./chunks/maestro-4HVDLCTI.js"),t=await a(i,{port:o,verbose:c});await n(typeof t=="number"?t:0)}case"record":{let{runRecord:a}=await import("./chunks/record-OUXWCWUF.js");await a(i,{port:o,verbose:c});break}case"film":{let{runFilm:a}=await import("./chunks/film-GPHSJLIH.js"),t=await a(i,{port:o,verbose:c});await n(typeof t=="number"?t:0)}case"storage":{let{runStorage:a}=await import("./chunks/storage-DWEOFXEB.js"),t=await a(i,{port:o,verbose:c});await n(typeof t=="number"?t:0)}case"state":{let{runState:a}=await import("./chunks/state-P2Z25OLQ.js"),t=await a(i,{port:o});await n(t)}case"reset":{let{runReset:a}=await import("./chunks/reset-ZZYLPETS.js"),t=await a(i,{port:o});await n(t)}case"perf":{let{runPerf:a}=await import("./chunks/perf-47R3OEI7.js"),t=await a(i,{port:o,verbose:c});await n(typeof t=="number"?t:0)}case"screenshot":{let{runScreenshotCommand:a}=await import("./chunks/screenshot-command-CUHDOS5D.js"),t=await a(i,{port:o,verbose:c});await n(typeof t=="number"?t:0)}case"camera":{let{runCamera:a}=await import("./chunks/camera-RUL426MQ.js"),t=await a(i,{port:o});await n(typeof t=="number"?t:0)}case"mode":{let{runMode:a}=await import("./chunks/mode-MGTS4QTF.js");await a(i,{port:o,verbose:c});break}case"permissions":{let{runPermissions:a}=await import("./chunks/permissions-ZOLFZWR2.js"),t=await a(i,{port:o,verbose:c});await n(t)}case"debug":{let{runDebug:a}=await import("./chunks/debug-7Z325GNU.js");await a(i,{port:o,verbose:c});break}case"timeline":{let{runTimeline:a}=await import("./chunks/timeline-55XNA4GX.js");await a(i,{port:o,verbose:c});break}case"what-happened":{let{runWhatHappened:a}=await import("./chunks/what-happened-Q3IGIH4K.js");await a(i,{port:o,verbose:c});break}case"open":{let{runOpenCommand:a}=await import("./chunks/control-FKPKLOE2.js");await a(i,{port:o});break}case"ios":case"android":{let{runPlatformCommand:a}=await import("./chunks/platform-C4E26ZBZ.js"),t=await a(s,i,{port:o});await n(typeof t=="number"?t:0)}case"box":{let{runBox:a}=await import("./chunks/box-7CK72ZPF.js");await n(await a(i,{port:o,verbose:c}))}case"use":{let{runUseCommand:a}=await import("./chunks/control-FKPKLOE2.js");await a(i,{port:o});break}case"claim":{let{runClaimCommand:a}=await import("./chunks/control-FKPKLOE2.js");await a(i,{port:o});break}case"close":{let{runCloseCommand:a}=await import("./chunks/control-FKPKLOE2.js");await a(i,{port:o});break}case"device":{let{runDeviceCommand:a}=await import("./chunks/device-TA3SQP6K.js");await a(i,{port:o});break}case"compat":{let{runCompat:a}=await import("./chunks/compat-4ZC6ZMMA.js");await a(e.commandArgs);break}case"report-issue":{let{runReportIssue:a}=await import("./chunks/report-issue-LJJXONXQ.js"),t=await a(e.commandArgs);await n(t)}case"desktop":{let{runDesktop:a}=await import("./chunks/desktop-CR7ALX7H.js");await a(e.commandArgs,{port:o,device:u});break}case"login":{let{runLogin:a}=await import("./chunks/login-EHUZYRAM.js");await a(e.commandArgs);break}case"logout":{let{runLogout:a}=await import("./chunks/logout-WPGT62N6.js");await a();break}case"auth":{let{runAuth:a}=await import("./chunks/auth-4SPTRWTM.js");await a(e.commandArgs);break}case"setup":{let{runSetup:a}=await import("./chunks/setup-X7LLGW67.js");await a(e.commandArgs);break}case"serve":{let{runServe:a}=await import("./chunks/serve-TX5OKGJM.js");await a(e.commandArgs,{port:o});break}case"daemon":{let{runDaemon:a}=await import("./chunks/daemon-WFHWS3JU.js");await a(e.commandArgs,{port:o});break}case"runtime":{let{runRuntime:a}=await import("./chunks/runtime-DEC5AF3M.js");await a(e.commandArgs);break}case"upgrade":{let{runUpgrade:a}=await import("./chunks/upgrade-3XRCVDVC.js");await a(e.commandArgs);break}case"version":{let{runVersion:a}=await import("./chunks/version-WTYGLXBJ.js");await a(e.commandArgs);break}case"agent":{let{runAgentCommand:a}=await import("./chunks/agent-RTAKJ5UM.js"),t=await a(e.commandArgs);await n(t)}case"agent-wrapper":{let{runAgentWrapper:a}=await import("./chunks/agent-wrapper-JWRDI26D.js"),t=await a(e.commandArgs);await n(t)}case"skill":{let{runSkill:a}=await import("./chunks/skills-TDFQAZ6A.js");await a(e.commandArgs);break}case"app-fonts":{let{runAppFonts:a}=await import("./chunks/app-fonts-RKO3N4PB.js");await a(e.commandArgs);break}case"config":{let{runConfig:a}=await import("./chunks/config-EOBODQ32.js");await a(e.commandArgs);break}case"cleanup":{let{runCleanup:a}=await import("./chunks/cleanup-7ATBNZHC.js"),t=await a(e.commandArgs);await n(t)}default:console.error(` unknown command: ${s}`),console.error(` run \`${m.commandName} --help\` to see the full surface.`),await n(1)}}catch(a){throw a instanceof h&&await n(a.code),a}await S();