rnxsim 0.1.369 → 0.1.371
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.
- package/cli/auth.ts +2 -1
- package/cli/bin.ts +283 -273
- package/cli/commands/app-fonts.ts +3 -2
- package/cli/commands/assert.ts +8 -7
- package/cli/commands/auth.ts +2 -1
- package/cli/commands/box/checkout-plane.ts +109 -29
- package/cli/commands/box/cloud-box.ts +294 -0
- package/cli/commands/box/plane-share.ts +208 -0
- package/cli/commands/box/rnx-command.ts +2 -2
- package/cli/commands/box/search-command.ts +157 -0
- package/cli/commands/box.ts +269 -13
- package/cli/commands/config.ts +2 -1
- package/cli/commands/control.ts +43 -34
- package/cli/commands/daemon.ts +22 -21
- package/cli/commands/debug.ts +17 -15
- package/cli/commands/detox.ts +5 -4
- package/cli/commands/device.ts +6 -5
- package/cli/commands/electron.ts +6 -5
- package/cli/commands/flow.ts +12 -11
- package/cli/commands/inspect/find.ts +2 -1
- package/cli/commands/inspect/keyboard.ts +2 -1
- package/cli/commands/inspect/sleep.ts +2 -1
- package/cli/commands/inspect/wait-event.ts +4 -3
- package/cli/commands/inspect/wait-idle.ts +2 -1
- package/cli/commands/inspect/wait-ready.ts +3 -2
- package/cli/commands/inspect/wait-selector.ts +4 -3
- package/cli/commands/inspect/wait.ts +2 -1
- package/cli/commands/inspect.ts +82 -80
- package/cli/commands/install-desktop.ts +5 -4
- package/cli/commands/login.ts +3 -2
- package/cli/commands/mode.ts +2 -1
- package/cli/commands/record.ts +38 -37
- package/cli/commands/runtime.ts +9 -8
- package/cli/commands/screenshot-layers.ts +4 -3
- package/cli/commands/screenshot-mode.ts +2 -1
- package/cli/commands/screenshot.ts +8 -7
- package/cli/commands/screenshots-capture.ts +2 -1
- package/cli/commands/serve.ts +5 -4
- package/cli/commands/shell-boolean-mode.ts +2 -1
- package/cli/commands/skills.ts +11 -10
- package/cli/commands/telemetry.ts +2 -1
- package/cli/commands/three-mode.ts +2 -1
- package/cli/commands/timeline.ts +4 -3
- package/cli/commands/upload.ts +14 -13
- package/cli/commands/what-happened.ts +2 -1
- package/cli/outbound-endpoints.ts +37 -1
- package/cli/recording-access.ts +6 -5
- package/cli/run-rnx.ts +140 -0
- package/dist-cli/bin.js +6 -6
- package/dist-cli/chunks/{agent-OUUFWDLF.js → agent-W2D2V7WF.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-7BE7DR4R.js → agent-wrapper-GVNM2Z4S.js} +2 -2
- package/dist-cli/chunks/app-fonts-J6R4TTXD.js +4 -0
- package/dist-cli/chunks/{assert-I3CMRNS7.js → assert-YN6U4ZTB.js} +11 -11
- package/dist-cli/chunks/auth-XGYZAK2P.js +12 -0
- package/dist-cli/chunks/{beta-BHSKY4K4.js → beta-WRDMHHWJ.js} +2 -2
- package/dist-cli/chunks/{box-3CVWXVTT.js → box-ML5TFUQW.js} +617 -596
- package/dist-cli/chunks/{camera-MNK73TYV.js → camera-OBEI4FZA.js} +2 -2
- package/dist-cli/chunks/{chunk-JXARZBWS.js → chunk-2FIT64GR.js} +1 -1
- package/dist-cli/chunks/{chunk-RYGQ6LZN.js → chunk-2LH4DYPM.js} +2 -2
- package/dist-cli/chunks/chunk-2QW3JH6T.js +99 -0
- package/dist-cli/chunks/{chunk-2APXX4RN.js → chunk-2W4YBCPX.js} +2 -2
- package/dist-cli/chunks/{chunk-X3PEDNLV.js → chunk-42YOIDLR.js} +2 -2
- package/dist-cli/chunks/{chunk-6BMM6OCW.js → chunk-47MSXLWK.js} +2 -2
- package/dist-cli/chunks/chunk-4OLUFHEZ.js +74 -0
- package/dist-cli/chunks/{chunk-WUR4FXWT.js → chunk-5PPVVGLH.js} +25 -25
- package/dist-cli/chunks/{chunk-AKJYEX54.js → chunk-6AZKZ5AJ.js} +2 -2
- package/dist-cli/chunks/{chunk-2PVVIPQS.js → chunk-6FPXLSBJ.js} +1 -1
- package/dist-cli/chunks/{chunk-CCI3DZKE.js → chunk-763GBI7G.js} +2 -2
- package/dist-cli/chunks/{chunk-CZ2HCNRK.js → chunk-7ASCDKA3.js} +1 -1
- package/dist-cli/chunks/{chunk-ZNNE5MAV.js → chunk-7Q4WHJDH.js} +1 -1
- package/dist-cli/chunks/{chunk-IYPMULI7.js → chunk-7TWD4P6J.js} +2 -2
- package/dist-cli/chunks/{chunk-45X3D6CI.js → chunk-7YB46LGI.js} +1 -1
- package/dist-cli/chunks/{chunk-3454VMHD.js → chunk-ADJSGO55.js} +2 -2
- package/dist-cli/chunks/{chunk-F4OTMARL.js → chunk-AGZZWLEL.js} +2 -2
- package/dist-cli/chunks/{chunk-UDXQCEJU.js → chunk-AVIH3TAZ.js} +11 -11
- package/dist-cli/chunks/{chunk-DCPDMF27.js → chunk-B26ZJUQA.js} +2 -2
- package/dist-cli/chunks/{chunk-D6MGELKZ.js → chunk-BNS5LNEU.js} +1 -1
- package/dist-cli/chunks/{chunk-FHRG772G.js → chunk-CJQXP4XM.js} +1 -1
- package/dist-cli/chunks/{chunk-OROUEG22.js → chunk-DP6RGTNN.js} +2 -2
- package/dist-cli/chunks/{chunk-V7I4CTAX.js → chunk-DYFOLOYL.js} +2 -2
- package/dist-cli/chunks/chunk-ERBGQEJ5.js +4 -0
- package/dist-cli/chunks/{chunk-H2GB2PLY.js → chunk-ESWSXF55.js} +2 -2
- package/dist-cli/chunks/{chunk-FGGESBWD.js → chunk-FLNGSIBL.js} +2 -2
- package/dist-cli/chunks/{chunk-4SCVGBOX.js → chunk-GQGS7TRK.js} +1 -1
- package/dist-cli/chunks/{chunk-WGE2P25F.js → chunk-H4DU457S.js} +1 -1
- package/dist-cli/chunks/{chunk-TBK5UXW2.js → chunk-HFY35VAG.js} +2 -2
- package/dist-cli/chunks/{chunk-JLOOLA47.js → chunk-HV3WOWQM.js} +1 -1
- package/dist-cli/chunks/{chunk-RV76U3EI.js → chunk-HZ365EMS.js} +10 -10
- package/dist-cli/chunks/chunk-IPNDXGVA.js +11 -0
- package/dist-cli/chunks/{chunk-TAC4CEIX.js → chunk-IX7Z6ZVJ.js} +2 -2
- package/dist-cli/chunks/{chunk-UQJY6O72.js → chunk-IYU2EOIU.js} +1 -1
- package/dist-cli/chunks/chunk-J6A5W2DI.js +30 -0
- package/dist-cli/chunks/chunk-JKRMBNPO.js +28 -0
- package/dist-cli/chunks/{chunk-VGJLCFSC.js → chunk-JUTLQ3GX.js} +1 -1
- package/dist-cli/chunks/{chunk-5QFJFASW.js → chunk-KHGXEU4L.js} +1 -1
- package/dist-cli/chunks/{chunk-A56GQVNF.js → chunk-KL34TRNW.js} +1 -1
- package/dist-cli/chunks/chunk-LFANRLWK.js +7 -0
- package/dist-cli/chunks/{chunk-NPVEBXU3.js → chunk-MOIQZVRU.js} +2 -2
- package/dist-cli/chunks/chunk-MW4E5EED.js +497 -0
- package/dist-cli/chunks/{chunk-AJJCYLBC.js → chunk-N3LAMTGS.js} +2 -2
- package/dist-cli/chunks/{chunk-QE5MC4V3.js → chunk-NQ4CI2VI.js} +1 -1
- package/dist-cli/chunks/{chunk-6F3WOWFC.js → chunk-OF3U4SZO.js} +2 -2
- package/dist-cli/chunks/{chunk-DKSZZABN.js → chunk-OF5OBB67.js} +1 -1
- package/dist-cli/chunks/{chunk-MXWME7ET.js → chunk-OQPZOEEP.js} +1 -1
- package/dist-cli/chunks/{chunk-SSBZA5VZ.js → chunk-OWDJQWJ7.js} +1 -1
- package/dist-cli/chunks/{chunk-YLNSLMQM.js → chunk-PTWMX4AA.js} +2 -2
- package/dist-cli/chunks/{chunk-TTUZ5MCK.js → chunk-RYJTPZCX.js} +1 -1
- package/dist-cli/chunks/{chunk-A72MIIJI.js → chunk-S3AH2O67.js} +1 -1
- package/dist-cli/chunks/{chunk-ZW6JXEVX.js → chunk-SH2HXHR2.js} +2 -2
- package/dist-cli/chunks/{chunk-EKAEEWC2.js → chunk-SHLJYXIN.js} +1 -1
- package/dist-cli/chunks/{chunk-4G4VCT2M.js → chunk-TL22EBF5.js} +2 -2
- package/dist-cli/chunks/{chunk-LU3AHE6T.js → chunk-TWNLRDIH.js} +2 -2
- package/dist-cli/chunks/{chunk-B5NW4TT3.js → chunk-UHYAY3RU.js} +2 -2
- package/dist-cli/chunks/{chunk-N5KDDGV6.js → chunk-UPMR6Y5P.js} +2 -2
- package/dist-cli/chunks/chunk-VEFSHPSU.js +24 -0
- package/dist-cli/chunks/{chunk-YPBDPVLR.js → chunk-WCB5XPCH.js} +2 -2
- package/dist-cli/chunks/{chunk-USHV3QFZ.js → chunk-WHZE63R2.js} +2 -2
- package/dist-cli/chunks/{chunk-ZEF57HQO.js → chunk-WLRIPAGJ.js} +1 -1
- package/dist-cli/chunks/{chunk-FBMNYSSW.js → chunk-Y7SKGRAV.js} +1 -1
- package/dist-cli/chunks/chunk-Z6CQDU74.js +4 -0
- package/dist-cli/chunks/{cleanup-H4XFRMZV.js → cleanup-PTJBMC34.js} +2 -2
- package/dist-cli/chunks/cli-version-6ZBD6GKP.js +4 -0
- package/dist-cli/chunks/{compat-NKGYERLN.js → compat-FJ2HWRAG.js} +2 -2
- package/dist-cli/chunks/config-PYGH7JPN.js +13 -0
- package/dist-cli/chunks/{connect-WWWAVVCF.js → connect-AJV4PZA4.js} +2 -2
- package/dist-cli/chunks/control-667PAYX4.js +4 -0
- package/dist-cli/chunks/daemon-EIBOHZYQ.js +4 -0
- package/dist-cli/chunks/{debug-RO6GLDIM.js → debug-XF2XUDHN.js} +20 -20
- package/dist-cli/chunks/desktop-HEZYNFYW.js +31 -0
- package/dist-cli/chunks/detox-JCFZJRNE.js +52 -0
- package/dist-cli/chunks/device-KGUVZHFW.js +18 -0
- package/dist-cli/chunks/{diagnose-HDRTUPDO.js → diagnose-KVJQ3TOI.js} +2 -2
- package/dist-cli/chunks/{disk-cleanup-QP2NYN43.js → disk-cleanup-7HQXJFUX.js} +2 -2
- package/dist-cli/chunks/{do-chain-6LP6SV5J.js → do-chain-3FYJNA2N.js} +2 -2
- package/dist-cli/chunks/drivers-5S6M5TJB.js +4 -0
- package/dist-cli/chunks/{env-4XD5D2G6.js → env-BX6S2SOM.js} +2 -2
- package/dist-cli/chunks/{film-I5SIXLS5.js → film-2WZUVYFA.js} +3 -3
- package/dist-cli/chunks/flow-6BZSWNHG.js +4 -0
- package/dist-cli/chunks/help-OGJBD4NP.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-LJV2S4M2.js → hidden-runtime-alias-PGF55OFH.js} +2 -2
- package/dist-cli/chunks/{home-paths-RFI2QRC3.js → home-paths-EGRMTSGE.js} +2 -2
- package/dist-cli/chunks/inspect-MZVDGAN4.js +4 -0
- package/dist-cli/chunks/install-desktop-QB76DNZF.js +4 -0
- package/dist-cli/chunks/login-HRFPYPZ3.js +26 -0
- package/dist-cli/chunks/{logout-DKXZZ6HU.js → logout-LRH5HZGP.js} +2 -2
- package/dist-cli/chunks/{maestro-WKGVVWRA.js → maestro-S53T4PPE.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-KCK6RK2C.js → maestro-generate-YKIVKOB3.js} +3 -3
- package/dist-cli/chunks/mode-47JKNUNS.js +26 -0
- package/dist-cli/chunks/{optional-demo-registry-EUSKBIPP.js → optional-demo-registry-ARZSPS5Y.js} +2 -2
- package/dist-cli/chunks/{perf-IMZMA5MT.js → perf-J73OKB4T.js} +2 -2
- package/dist-cli/chunks/{permissions-SLWXTLH6.js → permissions-L3FPI56H.js} +2 -2
- package/dist-cli/chunks/{platform-HQ735LAE.js → platform-YAROAHRM.js} +3 -3
- package/dist-cli/chunks/privacy-JZQCXHKN.js +4 -0
- package/dist-cli/chunks/record-QAQMGYOO.js +72 -0
- package/dist-cli/chunks/report-issue-4FVZCAFQ.js +29 -0
- package/dist-cli/chunks/reset-NYFUFZCG.js +4 -0
- package/dist-cli/chunks/resolve-target-SP22ZGFZ.js +4 -0
- package/dist-cli/chunks/runtime-WTS2QMXZ.js +4 -0
- package/dist-cli/chunks/{screenshot-command-HAJB5FHK.js → screenshot-command-Q5MGGEWY.js} +11 -11
- package/dist-cli/chunks/screenshot-layers-4JW7DRL7.js +20 -0
- package/dist-cli/chunks/screenshots-capture-X5PMO44E.js +42 -0
- package/dist-cli/chunks/{serve-TM46TKFW.js → serve-3JWJ45J3.js} +20 -20
- package/dist-cli/chunks/setup-USPDJESI.js +4 -0
- package/dist-cli/chunks/{sim-client-LQUZSO45.js → sim-client-RL6DGOJE.js} +2 -2
- package/dist-cli/chunks/skills-QNO6GDJB.js +50 -0
- package/dist-cli/chunks/{state-RUVM4Z4S.js → state-OGUCSUC3.js} +2 -2
- package/dist-cli/chunks/{storage-GQ33UTYN.js → storage-TG7KFQZ3.js} +2 -2
- package/dist-cli/chunks/store-ZYMQW2LN.js +4 -0
- package/dist-cli/chunks/telemetry-7CSLUUIW.js +4 -0
- package/dist-cli/chunks/timeline-R4G5P335.js +24 -0
- package/dist-cli/chunks/{upgrade-AZDZLUIT.js → upgrade-RDTXJ6O3.js} +2 -2
- package/dist-cli/chunks/upload-PLF2DPMS.js +4 -0
- package/dist-cli/chunks/{version-N3ALB75G.js → version-GU2NUZDS.js} +2 -2
- package/dist-cli/chunks/{web-XPHFZATA.js → web-7PUVG3GE.js} +2 -2
- package/dist-cli/chunks/welcome-4GGUV7FY.js +5 -0
- package/dist-cli/chunks/what-happened-OHAAX6F6.js +25 -0
- package/dist-cli/chunks/{ws-bridge-RUXOYOU6.js → ws-bridge-Z3O35MNF.js} +2 -2
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +69 -34
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +430 -106
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +1 -1
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +2 -1
- package/dist-lib/react-native-host-modules.mjs +2 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +149 -93
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +757 -353
- package/dist-lib/vite.cjs +1 -1
- package/package.json +1 -1
- package/scripts/dev-server-scanner.ts +275 -92
- package/src/dev-bundle-resolution.ts +123 -40
- package/src/host/bridge-host.ts +11 -0
- package/src/host/plane-host.ts +203 -0
- package/src/react-native-host-modules.ts +1 -0
- package/dist-cli/chunks/app-fonts-66V7TL26.js +0 -4
- package/dist-cli/chunks/auth-3FMWUVJH.js +0 -12
- package/dist-cli/chunks/chunk-3NLB5LCN.js +0 -28
- package/dist-cli/chunks/chunk-5RWUFIZW.js +0 -24
- package/dist-cli/chunks/chunk-COXCR6UU.js +0 -7
- package/dist-cli/chunks/chunk-JH5KE2OI.js +0 -4
- package/dist-cli/chunks/chunk-ROISJKH3.js +0 -99
- package/dist-cli/chunks/chunk-SUHAD6N2.js +0 -30
- package/dist-cli/chunks/chunk-TIB3CTRX.js +0 -497
- package/dist-cli/chunks/chunk-YFZLDFZK.js +0 -74
- package/dist-cli/chunks/cli-version-MJWLDXBW.js +0 -4
- package/dist-cli/chunks/config-WK7467KI.js +0 -13
- package/dist-cli/chunks/control-ESI7IZNN.js +0 -4
- package/dist-cli/chunks/daemon-UCLO27YP.js +0 -4
- package/dist-cli/chunks/desktop-FF34Z5X4.js +0 -31
- package/dist-cli/chunks/detox-Q3CWY5UX.js +0 -52
- package/dist-cli/chunks/device-WN4ZWLES.js +0 -18
- package/dist-cli/chunks/drivers-OR2JPAXD.js +0 -4
- package/dist-cli/chunks/flow-4GWW5DYT.js +0 -4
- package/dist-cli/chunks/help-MIIWOT3C.js +0 -4
- package/dist-cli/chunks/inspect-3ZGNJENJ.js +0 -4
- package/dist-cli/chunks/install-desktop-BYOJVTLS.js +0 -4
- package/dist-cli/chunks/login-GNIFSAFE.js +0 -26
- package/dist-cli/chunks/mode-YVMX7OJY.js +0 -26
- package/dist-cli/chunks/privacy-EDC7PF7G.js +0 -4
- package/dist-cli/chunks/record-N4KTTVDI.js +0 -72
- package/dist-cli/chunks/report-issue-PP752HAD.js +0 -29
- package/dist-cli/chunks/reset-DRMKGVP4.js +0 -4
- package/dist-cli/chunks/resolve-target-7WWI32R3.js +0 -4
- package/dist-cli/chunks/runtime-EKJV2ZVI.js +0 -4
- package/dist-cli/chunks/screenshot-layers-GOCJVM2G.js +0 -20
- package/dist-cli/chunks/screenshots-capture-YYJLYA3V.js +0 -42
- package/dist-cli/chunks/setup-FVPVIYE4.js +0 -4
- package/dist-cli/chunks/skills-V5TE57FN.js +0 -50
- package/dist-cli/chunks/store-IKNBDDKM.js +0 -4
- package/dist-cli/chunks/telemetry-2ZVU6WLK.js +0 -4
- package/dist-cli/chunks/timeline-LP7DMJYT.js +0 -24
- package/dist-cli/chunks/upload-DFQK2Q7X.js +0 -4
- package/dist-cli/chunks/welcome-2NFYLWLJ.js +0 -5
- package/dist-cli/chunks/what-happened-EJ5PHKT7.js +0 -25
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// a local box authorizing its own checkout for browser access.
|
|
2
|
+
//
|
|
3
|
+
// the bridge daemon serves the `plane:*` envelope, but it will not open a root
|
|
4
|
+
// nobody has authorized, because a WebSocket to 127.0.0.1 is not subject to
|
|
5
|
+
// CORS and any page the user visits can open one. so the box registers its
|
|
6
|
+
// root and holds the socket for as long as it runs; when the box exits, the
|
|
7
|
+
// registration goes with it and the tab loses reach.
|
|
8
|
+
//
|
|
9
|
+
// THIS NEVER BLOCKS THE BOX. an earlier version awaited the socket before
|
|
10
|
+
// printing the prompt and cost 1450ms of start time on every box, because a
|
|
11
|
+
// daemon built before `plane:*` existed accepts the socket, ignores the
|
|
12
|
+
// message, and never replies, so the client sat on its timeout. a box that
|
|
13
|
+
// cannot share its plane is a box with one capability missing, not a box that
|
|
14
|
+
// starts slowly.
|
|
15
|
+
//
|
|
16
|
+
// IT ALSO REFUSES TO GUESS A PORT. when no lockfile claims a bridge,
|
|
17
|
+
// resolveBridgeWorld reports source 'default' and hands back
|
|
18
|
+
// DEFAULT_SOOTSIM_BRIDGE_PORT, which on a machine running a real daemon is
|
|
19
|
+
// that daemon. registering there would attach a test's or a benchmark's box to
|
|
20
|
+
// another session's daemon, which is exactly what a private SOOTSIM_HOME
|
|
21
|
+
// exists to prevent. so a guessed port is refused by name.
|
|
22
|
+
|
|
23
|
+
import WebSocket from 'ws'
|
|
24
|
+
import { bridgeWsUrl, resolveBridgeWorld } from '../../ws-bridge'
|
|
25
|
+
|
|
26
|
+
const REGISTER_TIMEOUT_MS = 1500
|
|
27
|
+
|
|
28
|
+
export interface PlaneShare {
|
|
29
|
+
/**
|
|
30
|
+
* resolves when the daemon has accepted the root, or with a reason it did
|
|
31
|
+
* not. `holders` is how many boxes now hold this checkout, including this
|
|
32
|
+
* one, so more than one means another box is writing the same tree.
|
|
33
|
+
*/
|
|
34
|
+
readonly settled: Promise<{ port: number; holders: number } | { declined: string }>
|
|
35
|
+
close(): void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function target(
|
|
39
|
+
explicitPort: number | undefined,
|
|
40
|
+
): { port: number } | { declined: string } {
|
|
41
|
+
if (explicitPort) return { port: explicitPort }
|
|
42
|
+
const world = resolveBridgeWorld()
|
|
43
|
+
if (world.source === 'default') {
|
|
44
|
+
return {
|
|
45
|
+
declined:
|
|
46
|
+
'no daemon or dev bridge claims a port, and the default is another session’s daemon on a shared machine',
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { port: world.bridgePort }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* register `root` with the bridge daemon in the background. returns
|
|
54
|
+
* immediately; the box never waits on it.
|
|
55
|
+
*/
|
|
56
|
+
export function sharePlane(root: string, opts: { port?: number } = {}): PlaneShare {
|
|
57
|
+
const chosen = target(opts.port)
|
|
58
|
+
if ('declined' in chosen) {
|
|
59
|
+
return { settled: Promise.resolve(chosen), close: () => {} }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const port = chosen.port
|
|
63
|
+
let socket: WebSocket | null = null
|
|
64
|
+
let closed = false
|
|
65
|
+
|
|
66
|
+
const settled = new Promise<{ port: number; holders: number } | { declined: string }>(
|
|
67
|
+
(resolve) => {
|
|
68
|
+
let done = false
|
|
69
|
+
const finish = (
|
|
70
|
+
value: { port: number; holders: number } | { declined: string },
|
|
71
|
+
) => {
|
|
72
|
+
if (done) return
|
|
73
|
+
done = true
|
|
74
|
+
clearTimeout(timer)
|
|
75
|
+
resolve(value)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const timer = setTimeout(() => {
|
|
79
|
+
// an older daemon accepts the socket and ignores plane:* entirely, so
|
|
80
|
+
// silence is the expected shape of "not supported" rather than a fault.
|
|
81
|
+
socket?.close()
|
|
82
|
+
finish({ declined: `bridge on port ${port} did not answer plane:register` })
|
|
83
|
+
}, REGISTER_TIMEOUT_MS)
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
socket = new WebSocket(bridgeWsUrl(port))
|
|
87
|
+
} catch (error) {
|
|
88
|
+
finish({ declined: error instanceof Error ? error.message : String(error) })
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
if (closed) {
|
|
92
|
+
socket.close()
|
|
93
|
+
finish({ declined: 'box exited before the plane registered' })
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
socket.on('error', (error) => finish({ declined: error.message }))
|
|
98
|
+
socket.on('close', () => finish({ declined: `bridge on port ${port} closed` }))
|
|
99
|
+
socket.on('open', () => {
|
|
100
|
+
// node sends no Origin, so this socket is already cli-role. a browser
|
|
101
|
+
// client has to send bridge:hello to be corrected off the provisional
|
|
102
|
+
// sim role; see plans/rnx-cloud/transport-decision.md.
|
|
103
|
+
socket?.send(JSON.stringify({ type: 'plane:register', id: 'register', root }))
|
|
104
|
+
})
|
|
105
|
+
socket.on('message', (data) => {
|
|
106
|
+
let parsed: unknown
|
|
107
|
+
try {
|
|
108
|
+
parsed = JSON.parse(String(data))
|
|
109
|
+
} catch {
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
if (typeof parsed !== 'object' || parsed === null) return
|
|
113
|
+
const reply: Record<string, unknown> = { ...parsed }
|
|
114
|
+
if (reply.id !== 'register') return
|
|
115
|
+
if (reply.error) {
|
|
116
|
+
socket?.close()
|
|
117
|
+
finish({ declined: String(reply.error) })
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
// the daemon nests its payload under `result`; reading holders off the
|
|
121
|
+
// envelope silently reported 1 forever, which is the exact silence this
|
|
122
|
+
// field exists to remove.
|
|
123
|
+
const result = (reply.result ?? {}) as Record<string, unknown>
|
|
124
|
+
finish({ port, holders: Number(result.holders ?? 1) })
|
|
125
|
+
})
|
|
126
|
+
},
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
settled,
|
|
131
|
+
close: () => {
|
|
132
|
+
closed = true
|
|
133
|
+
try {
|
|
134
|
+
socket?.close()
|
|
135
|
+
} catch {}
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* How many boxes hold `root` right now, without registering anything.
|
|
142
|
+
*
|
|
143
|
+
* `plane:register` cannot answer this: registering is what makes a root
|
|
144
|
+
* reachable over a loopback socket, so a readiness probe that asked with it
|
|
145
|
+
* would both authorize the root and count itself in the answer.
|
|
146
|
+
*/
|
|
147
|
+
export async function planeStatus(
|
|
148
|
+
root: string,
|
|
149
|
+
opts: { port?: number } = {},
|
|
150
|
+
): Promise<{ holders: number; registered: boolean } | { declined: string }> {
|
|
151
|
+
const chosen = target(opts.port)
|
|
152
|
+
if ('declined' in chosen) return chosen
|
|
153
|
+
const port = chosen.port
|
|
154
|
+
|
|
155
|
+
return new Promise((resolve) => {
|
|
156
|
+
let done = false
|
|
157
|
+
let socket: WebSocket | null = null
|
|
158
|
+
const finish = (
|
|
159
|
+
value: { holders: number; registered: boolean } | { declined: string },
|
|
160
|
+
) => {
|
|
161
|
+
if (done) return
|
|
162
|
+
done = true
|
|
163
|
+
clearTimeout(timer)
|
|
164
|
+
try {
|
|
165
|
+
socket?.close()
|
|
166
|
+
} catch {}
|
|
167
|
+
resolve(value)
|
|
168
|
+
}
|
|
169
|
+
const timer = setTimeout(
|
|
170
|
+
() => finish({ declined: `bridge on port ${port} did not answer plane:status` }),
|
|
171
|
+
REGISTER_TIMEOUT_MS,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
socket = new WebSocket(bridgeWsUrl(port))
|
|
176
|
+
} catch (error) {
|
|
177
|
+
finish({ declined: error instanceof Error ? error.message : String(error) })
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
socket.on('error', (error) => finish({ declined: error.message }))
|
|
181
|
+
socket.on('close', () => finish({ declined: `bridge on port ${port} closed` }))
|
|
182
|
+
socket.on('open', () => {
|
|
183
|
+
socket?.send(JSON.stringify({ type: 'plane:status', id: 'status', root }))
|
|
184
|
+
})
|
|
185
|
+
socket.on('message', (data) => {
|
|
186
|
+
let parsed: unknown
|
|
187
|
+
try {
|
|
188
|
+
parsed = JSON.parse(String(data))
|
|
189
|
+
} catch {
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
if (typeof parsed !== 'object' || parsed === null) return
|
|
193
|
+
const reply: Record<string, unknown> = { ...parsed }
|
|
194
|
+
if (reply.id !== 'status') return
|
|
195
|
+
if (reply.error) {
|
|
196
|
+
finish({ declined: String(reply.error) })
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
// the daemon nests its payload under `result`, the same shape
|
|
200
|
+
// plane:register answers in.
|
|
201
|
+
const result = (reply.result ?? {}) as Record<string, unknown>
|
|
202
|
+
finish({
|
|
203
|
+
holders: Number(result.holders ?? 0),
|
|
204
|
+
registered: Boolean(result.registered),
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import { spawn } from 'node:child_process'
|
|
10
10
|
import path from 'node:path'
|
|
11
|
-
import { defineCommand, type CustomCommand } from '@contrast/harness'
|
|
12
|
-
import { PROJECT_MOUNT } from '@contrast/harness'
|
|
11
|
+
import { defineCommand, type CustomCommand } from '@contrast/harness/shell'
|
|
12
|
+
import { PROJECT_MOUNT } from '@contrast/harness/shell'
|
|
13
13
|
import { rnxPublicBrand } from '../../../src/public-brand'
|
|
14
14
|
import { RNX_PLACEMENT_ENV } from '../../ws-bridge'
|
|
15
15
|
|
|
@@ -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
|
+
}
|