rnxsim 0.1.365 → 0.1.367
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/README.md +14 -0
- package/cli/bin.ts +18 -0
- package/cli/bridge-flow-runner.ts +16 -153
- package/cli/commands/assert.ts +1 -1
- package/cli/commands/box/checkout-plane.ts +126 -0
- package/cli/commands/box/rnx-command.ts +68 -0
- package/cli/commands/box.ts +200 -0
- package/cli/commands/control.ts +15 -76
- package/cli/commands/detox.ts +18 -1
- package/cli/commands/do-chain.ts +412 -0
- package/cli/commands/flow.ts +2 -11
- package/cli/commands/inspect/core.ts +302 -725
- package/cli/commands/inspect/describe.ts +3 -76
- package/cli/commands/inspect/find.ts +3 -6
- package/cli/commands/inspect/get-layout.ts +14 -1
- package/cli/commands/inspect/resolve-target.ts +41 -47
- package/cli/commands/inspect/shared.ts +28 -123
- package/cli/commands/inspect/tree.ts +6 -15
- package/cli/commands/inspect/wait-ready.ts +2 -2
- package/cli/commands/inspect/wait-selector.ts +4 -8
- package/cli/commands/inspect.ts +23 -17
- package/cli/commands/install-desktop.ts +44 -7
- package/cli/commands/maestro-generate.ts +0 -2
- package/cli/commands/platform.ts +68 -0
- package/cli/commands/state.ts +5 -2
- package/cli/outbound-endpoints.ts +6 -1
- package/cli/parse-args.ts +13 -2
- package/cli/ws-bridge.ts +30 -2
- package/detox/index.ts +37 -18
- package/dist-cli/bin.js +5 -5
- package/dist-cli/chunks/{agent-BVMNQFKF.js → agent-EKKIAKP5.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-B6YFXGC3.js → agent-wrapper-MHLQUGA6.js} +2 -2
- package/dist-cli/chunks/{app-fonts-MEI6R6XR.js → app-fonts-HN6ADRTQ.js} +2 -2
- package/dist-cli/chunks/{assert-JGHI53HV.js → assert-IMR6QCN6.js} +3 -3
- package/dist-cli/chunks/{auth-STEOXUS7.js → auth-27LPMQNH.js} +2 -2
- package/dist-cli/chunks/{beta-HT3FLS5T.js → beta-6RXBB5AI.js} +2 -2
- package/dist-cli/chunks/box-DORLF3GP.js +1709 -0
- package/dist-cli/chunks/{camera-ZCLFSLON.js → camera-2G7JZ5DL.js} +2 -2
- package/dist-cli/chunks/{chunk-RI3NS77J.js → chunk-3QZJLNYY.js} +1 -1
- package/dist-cli/chunks/{chunk-FHWZPUGC.js → chunk-3VBVEDDA.js} +1 -1
- package/dist-cli/chunks/{chunk-EAFKG5R3.js → chunk-4BWGLTKU.js} +2 -2
- package/dist-cli/chunks/chunk-4XSNO374.js +423 -0
- package/dist-cli/chunks/{chunk-GZEUDDAW.js → chunk-5DGMFUZG.js} +2 -2
- package/dist-cli/chunks/{chunk-TKCGP3DD.js → chunk-6DBS5EGX.js} +2 -2
- package/dist-cli/chunks/{chunk-HIDS3PYC.js → chunk-6SO6I3D5.js} +2 -2
- package/dist-cli/chunks/chunk-75EFOPXE.js +32 -0
- package/dist-cli/chunks/{chunk-FIXRGPUT.js → chunk-A5AJFVOF.js} +1 -1
- package/dist-cli/chunks/{chunk-2SFMW5K6.js → chunk-AM2ODYAN.js} +1 -1
- package/dist-cli/chunks/{chunk-BGRGP3QR.js → chunk-AVOROGA3.js} +1 -1
- package/dist-cli/chunks/chunk-AXMMLO7D.js +130 -0
- package/dist-cli/chunks/{chunk-XXNPISER.js → chunk-BSEXEYNX.js} +2 -2
- package/dist-cli/chunks/{chunk-67ATAITF.js → chunk-CM6ZKZW7.js} +2 -2
- package/dist-cli/chunks/{chunk-MR3YPNUI.js → chunk-DNVABCEC.js} +2 -2
- package/dist-cli/chunks/{chunk-5AS7S6HY.js → chunk-E33A4AIR.js} +2 -2
- package/dist-cli/chunks/{chunk-DBJZUCFP.js → chunk-EFMOXYDD.js} +2 -2
- package/dist-cli/chunks/{chunk-ZTWUNK45.js → chunk-EV4P5UBP.js} +2 -2
- package/dist-cli/chunks/{chunk-EVBQDQVY.js → chunk-G5IUIGGC.js} +1 -1
- package/dist-cli/chunks/chunk-GMLOCV3E.js +312 -0
- package/dist-cli/chunks/{chunk-DQFOHIRF.js → chunk-GRN2FHVW.js} +1 -1
- package/dist-cli/chunks/{chunk-CYIE65QN.js → chunk-HFFXPUEP.js} +1 -1
- package/dist-cli/chunks/chunk-HG6T4OOR.js +4 -0
- package/dist-cli/chunks/{chunk-GK7PGYFH.js → chunk-HNT2OXPZ.js} +1 -1
- package/dist-cli/chunks/chunk-IPEURNLI.js +24 -0
- package/dist-cli/chunks/chunk-IR44SFBA.js +5 -0
- package/dist-cli/chunks/chunk-JMSLXZFW.js +4 -0
- package/dist-cli/chunks/{chunk-WGNDJARS.js → chunk-JPXQKLNH.js} +1 -1
- package/dist-cli/chunks/chunk-K3EC6JDE.js +4 -0
- package/dist-cli/chunks/chunk-KHX65HAU.js +4 -0
- package/dist-cli/chunks/{chunk-MACI34ST.js → chunk-LWOVKHRC.js} +2 -2
- package/dist-cli/chunks/{chunk-QIQL7Y5V.js → chunk-MQN5DWTI.js} +2 -2
- package/dist-cli/chunks/{chunk-5VHWQGGF.js → chunk-N3BXQEUF.js} +1 -1
- package/dist-cli/chunks/{chunk-O2ORLK6W.js → chunk-N5OSV26B.js} +2 -2
- package/dist-cli/chunks/chunk-PHNURIH3.js +4 -0
- package/dist-cli/chunks/{chunk-JRPU5TFI.js → chunk-PP2KNVXP.js} +3 -3
- package/dist-cli/chunks/{chunk-UR345KM3.js → chunk-PROKNJVY.js} +2 -2
- package/dist-cli/chunks/chunk-QXLLY4IM.js +7 -0
- package/dist-cli/chunks/{chunk-2CXHDTQ5.js → chunk-RNQTOGM7.js} +1 -1
- package/dist-cli/chunks/{chunk-M5KJCLUW.js → chunk-RPNKKVBN.js} +27 -27
- package/dist-cli/chunks/{chunk-EFXNSJ55.js → chunk-RSNLI2NH.js} +2 -2
- package/dist-cli/chunks/{chunk-25BTVHK2.js → chunk-RZGHV2WI.js} +2 -2
- package/dist-cli/chunks/chunk-SGGL3AFV.js +497 -0
- package/dist-cli/chunks/chunk-SH5ETJKG.js +4 -0
- package/dist-cli/chunks/{chunk-MRNW4GVL.js → chunk-T6J4YP56.js} +2 -2
- package/dist-cli/chunks/{chunk-R4FQZXJC.js → chunk-TWUE7WF5.js} +2 -2
- package/dist-cli/chunks/{chunk-X5ZOZVHC.js → chunk-U44VEQRN.js} +1 -1
- package/dist-cli/chunks/{chunk-XRO5WQXP.js → chunk-URLVQHC3.js} +1 -1
- package/dist-cli/chunks/{chunk-JAFF4RAU.js → chunk-VANZFTUE.js} +1 -1
- package/dist-cli/chunks/{chunk-6BAF6GFS.js → chunk-VB2YBH46.js} +1 -1
- package/dist-cli/chunks/{chunk-O6P4N2JD.js → chunk-VFU2SJ7M.js} +2 -2
- package/dist-cli/chunks/{chunk-XPOTXF5Q.js → chunk-VUBJ6FXU.js} +15 -15
- package/dist-cli/chunks/{chunk-XR4KID25.js → chunk-W5PATILQ.js} +2 -2
- package/dist-cli/chunks/chunk-WI5V4TYG.js +38 -0
- package/dist-cli/chunks/{chunk-NEH4T67X.js → chunk-XTRHQAOV.js} +2 -2
- package/dist-cli/chunks/chunk-XWUTJLBM.js +27 -0
- package/dist-cli/chunks/{chunk-TULWPOAY.js → chunk-XYRUQQUR.js} +2 -2
- package/dist-cli/chunks/chunk-YBFB3RM7.js +4 -0
- package/dist-cli/chunks/chunk-YGQVQ2VG.js +4 -0
- package/dist-cli/chunks/{chunk-J66US2OZ.js → chunk-YINACV32.js} +1 -1
- package/dist-cli/chunks/chunk-YV7ERO2V.js +5 -0
- package/dist-cli/chunks/{cleanup-G4TEYVKY.js → cleanup-GVFHQ3KH.js} +2 -2
- package/dist-cli/chunks/cli-version-DPX5MA2M.js +4 -0
- package/dist-cli/chunks/{compat-EZ5NP32J.js → compat-AYRTXYAM.js} +2 -2
- package/dist-cli/chunks/{config-27M2DGCS.js → config-K7LOOV62.js} +2 -2
- package/dist-cli/chunks/connect-H3CNY4IO.js +4 -0
- package/dist-cli/chunks/control-SMHZ2CF5.js +4 -0
- package/dist-cli/chunks/daemon-TEZUNI73.js +4 -0
- package/dist-cli/chunks/{debug-EXU3D7PC.js → debug-ELGHG57T.js} +3 -3
- package/dist-cli/chunks/{desktop-SHHPZAWQ.js → desktop-4G7FK3MP.js} +3 -3
- package/dist-cli/chunks/detox-JKXGEJIZ.js +52 -0
- package/dist-cli/chunks/{device-4NI26HG6.js → device-IV3AR6KG.js} +2 -2
- package/dist-cli/chunks/{diagnose-P4SFXH6J.js → diagnose-3ERGF7EW.js} +2 -2
- package/dist-cli/chunks/{disk-cleanup-BUAKWYMY.js → disk-cleanup-ALFEVZ4B.js} +2 -2
- package/dist-cli/chunks/do-chain-K2B4OHM7.js +5 -0
- package/dist-cli/chunks/drivers-KJYXA3M3.js +4 -0
- package/dist-cli/chunks/env-2KF6IYZE.js +4 -0
- package/dist-cli/chunks/{film-Y2BAQINB.js → film-UQXJFAD5.js} +3 -3
- package/dist-cli/chunks/flow-ASTUH56E.js +4 -0
- package/dist-cli/chunks/help-SNNYFELA.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-U2MAY2XV.js → hidden-runtime-alias-VJCWCFVO.js} +2 -2
- package/dist-cli/chunks/{home-paths-PAJMZCOS.js → home-paths-EGUZZ6J6.js} +2 -2
- package/dist-cli/chunks/inspect-XANAADQT.js +4 -0
- package/dist-cli/chunks/install-desktop-JKQA7UY6.js +4 -0
- package/dist-cli/chunks/{login-D53XKBEO.js → login-6KQGD7HK.js} +2 -2
- package/dist-cli/chunks/{logout-HZQKLNUJ.js → logout-2FCOVLBC.js} +2 -2
- package/dist-cli/chunks/{maestro-XSS2KX6L.js → maestro-HPSSEVJD.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-DNDQX3JQ.js → maestro-generate-KQQ6JHMR.js} +5 -5
- package/dist-cli/chunks/{mode-LEVGOKI3.js → mode-SH7KRZET.js} +2 -2
- package/dist-cli/chunks/{optional-demo-registry-2XZIJNDG.js → optional-demo-registry-NAMCAVFX.js} +2 -2
- package/dist-cli/chunks/{perf-OKMAXDLB.js → perf-LWKAHXUL.js} +2 -2
- package/dist-cli/chunks/{permissions-VG5TBUTN.js → permissions-EZTKWTXV.js} +2 -2
- package/dist-cli/chunks/platform-KSOJIVN5.js +6 -0
- package/dist-cli/chunks/privacy-EJULWP7L.js +4 -0
- package/dist-cli/chunks/{record-BQYGZOMV.js → record-2IC7BAQN.js} +3 -3
- package/dist-cli/chunks/{report-issue-HCPZESWE.js → report-issue-MMX6HOQO.js} +2 -2
- package/dist-cli/chunks/reset-PKFZA4NC.js +4 -0
- package/dist-cli/chunks/resolve-target-AFKU3BRE.js +4 -0
- package/dist-cli/chunks/runtime-XHWWPOIN.js +4 -0
- package/dist-cli/chunks/{screenshot-command-DWEYPJE7.js → screenshot-command-JYUXDVSA.js} +3 -3
- package/dist-cli/chunks/{screenshot-layers-5GMBXLO7.js → screenshot-layers-OGRY6AA3.js} +2 -2
- package/dist-cli/chunks/{screenshots-capture-5KCI7IZD.js → screenshots-capture-QHDN65XS.js} +2 -2
- package/dist-cli/chunks/{serve-QNKTA2HR.js → serve-W4BKAG4J.js} +3 -3
- package/dist-cli/chunks/setup-WJPMPSAC.js +4 -0
- package/dist-cli/chunks/sim-client-UOQGFBPO.js +4 -0
- package/dist-cli/chunks/{skills-DOEALD3X.js → skills-OPPXOD63.js} +2 -2
- package/dist-cli/chunks/state-TDAN7BOM.js +14 -0
- package/dist-cli/chunks/{storage-E7WUWN3E.js → storage-CFB2YGLY.js} +2 -2
- package/dist-cli/chunks/store-4TEED4ZU.js +4 -0
- package/dist-cli/chunks/telemetry-S6N7CV6G.js +4 -0
- package/dist-cli/chunks/{timeline-IKNUGQ2A.js → timeline-ASOOVP4C.js} +2 -2
- package/dist-cli/chunks/{upgrade-VS3OKXE2.js → upgrade-6MVJISHG.js} +2 -2
- package/dist-cli/chunks/upload-CBNIRFEW.js +4 -0
- package/dist-cli/chunks/{version-F4XHNTBZ.js → version-5GS7CWRL.js} +2 -2
- package/dist-cli/chunks/{web-F72724QL.js → web-QRRMYMV4.js} +2 -2
- package/dist-cli/chunks/welcome-33FXMAMB.js +5 -0
- package/dist-cli/chunks/{what-happened-APK7YKOV.js → what-happened-GAZJGKXX.js} +2 -2
- package/dist-cli/chunks/ws-bridge-DIXUAPLQ.js +4 -0
- 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 +336 -0
- package/dist-lib/bridge-contract-input.mjs +303 -0
- package/dist-lib/bridge-contract.cjs +29 -1
- package/dist-lib/bridge-contract.mjs +19 -0
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +35 -19
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +5 -1
- 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 +244 -594
- 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 +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +303 -657
- package/dist-lib/sdk.mjs +301 -652
- package/dist-lib/skills.cjs +256 -339
- package/dist-lib/vite.cjs +1 -1
- package/package.json +10 -1
- package/skills/contrast/SKILL.md +3 -0
- package/skills/rnx-debug/SKILL.md +8 -0
- package/skills/rnx-test/SKILL.md +21 -0
- package/src/app-url.ts +40 -0
- package/src/bridge-contract-input.ts +359 -0
- package/src/bridge-contract.ts +121 -1
- package/src/host/bridge-host.ts +7 -12
- package/src/index.ts +7 -0
- package/src/sim-client.ts +24 -9
- package/dist-cli/chunks/chunk-2JFZ5Z7Q.js +0 -4
- package/dist-cli/chunks/chunk-4WFN4DFR.js +0 -5
- package/dist-cli/chunks/chunk-52ES4API.js +0 -38
- package/dist-cli/chunks/chunk-622E4JET.js +0 -767
- package/dist-cli/chunks/chunk-EFL2ASZO.js +0 -124
- package/dist-cli/chunks/chunk-IWPS3EKJ.js +0 -15
- package/dist-cli/chunks/chunk-MCHKBFUL.js +0 -4
- package/dist-cli/chunks/chunk-OSZ5HZSI.js +0 -24
- package/dist-cli/chunks/chunk-W3UIDI3W.js +0 -5
- package/dist-cli/chunks/chunk-XKRUNS7A.js +0 -32
- package/dist-cli/chunks/chunk-YE3M3YD4.js +0 -944
- package/dist-cli/chunks/chunk-ZCP4CRP6.js +0 -4
- package/dist-cli/chunks/chunk-ZGD7GZRI.js +0 -4
- package/dist-cli/chunks/chunk-ZGH5U4AJ.js +0 -27
- package/dist-cli/chunks/cli-version-ZM3PVKQ5.js +0 -4
- package/dist-cli/chunks/control-4QR43FXV.js +0 -4
- package/dist-cli/chunks/daemon-2U4CJ6AT.js +0 -4
- package/dist-cli/chunks/detox-RQ6FFSBS.js +0 -52
- package/dist-cli/chunks/drivers-OVCUPQTM.js +0 -4
- package/dist-cli/chunks/flow-ELTLKKZ3.js +0 -4
- package/dist-cli/chunks/help-CDEZAG2L.js +0 -4
- package/dist-cli/chunks/inspect-VVQRPJLS.js +0 -4
- package/dist-cli/chunks/install-desktop-YXSRKR35.js +0 -4
- package/dist-cli/chunks/privacy-MNRHREME.js +0 -4
- package/dist-cli/chunks/reset-R4E4W4YI.js +0 -4
- package/dist-cli/chunks/runtime-XRQZ7W34.js +0 -4
- package/dist-cli/chunks/setup-RNMM5GO5.js +0 -4
- package/dist-cli/chunks/state-3HH2IHIF.js +0 -14
- package/dist-cli/chunks/store-HUPZUCFG.js +0 -4
- package/dist-cli/chunks/telemetry-IJ53XR2Y.js +0 -4
- package/dist-cli/chunks/upload-A57NAAHF.js +0 -4
- package/dist-cli/chunks/welcome-B72RRMBW.js +0 -5
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// `rnx box` — the box environment.
|
|
2
|
+
//
|
|
3
|
+
// bare `rnx box` is LOCAL and mounts the current checkout at /project. `rnx box
|
|
4
|
+
// create` provisions a durable cloud box. everything else in the namespace is
|
|
5
|
+
// cloud lifecycle. the cloud side is not built yet; those verbs fail by name
|
|
6
|
+
// and say what works instead, so nothing advertises a capability that always
|
|
7
|
+
// fails.
|
|
8
|
+
|
|
9
|
+
import path from 'node:path'
|
|
10
|
+
import { createInterface } from 'node:readline'
|
|
11
|
+
import { createHarnessShell, HarnessProjectFs, PROJECT_MOUNT } from '@contrast/harness'
|
|
12
|
+
import { rnxPublicBrand } from '../../src/public-brand'
|
|
13
|
+
import { findRepositoryPackageRoot } from '../setup-repository'
|
|
14
|
+
import { resolvePlacement } from '../ws-bridge'
|
|
15
|
+
import { CheckoutFilePlane, CheckoutRequiresGitError } from './box/checkout-plane'
|
|
16
|
+
import { createRnxCommand } from './box/rnx-command'
|
|
17
|
+
|
|
18
|
+
interface BoxCommandOptions {
|
|
19
|
+
port?: number
|
|
20
|
+
verbose?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const CLOUD_SUBCOMMANDS = new Set(['create', 'ls', 'connect', 'stop', 'delete'])
|
|
24
|
+
|
|
25
|
+
function usage(): void {
|
|
26
|
+
const rnx = rnxPublicBrand.commandName
|
|
27
|
+
console.log(`
|
|
28
|
+
${rnx} box — start a local box and enter its shell
|
|
29
|
+
|
|
30
|
+
usage:
|
|
31
|
+
${rnx} box start a local box over the current checkout
|
|
32
|
+
${rnx} box create create a cloud box and enter its shell
|
|
33
|
+
${rnx} box create --name=<n> create a named cloud box
|
|
34
|
+
|
|
35
|
+
a local box mounts this checkout at ${PROJECT_MOUNT}, gives you a shell with
|
|
36
|
+
ls, cat, find, rg, sed, pipes and redirects, and registers ${rnx} itself as a
|
|
37
|
+
shell command. writes go straight to the checkout — there is no second copy.
|
|
38
|
+
`)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function runBox(
|
|
42
|
+
args: string[],
|
|
43
|
+
opts: BoxCommandOptions = {},
|
|
44
|
+
): Promise<number> {
|
|
45
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
46
|
+
usage()
|
|
47
|
+
return 0
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// a box inside a box would mount the same checkout twice and give the
|
|
51
|
+
// user two shells writing through one filesystem. refuse at the top rather
|
|
52
|
+
// than letting the second one start and quietly race the first.
|
|
53
|
+
const placement = resolvePlacement()
|
|
54
|
+
if (placement === 'local-box') {
|
|
55
|
+
console.error(
|
|
56
|
+
` ${rnxPublicBrand.commandName} box: you are already in a box.\n` +
|
|
57
|
+
` this shell is the box — run ${rnxPublicBrand.commandName} commands directly, or \`exit\` to leave.`,
|
|
58
|
+
)
|
|
59
|
+
return 1
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const subcommand = args.find((arg) => !arg.startsWith('-'))
|
|
63
|
+
if (subcommand !== undefined) {
|
|
64
|
+
if (CLOUD_SUBCOMMANDS.has(subcommand)) {
|
|
65
|
+
console.error(
|
|
66
|
+
` ${rnxPublicBrand.commandName} box ${subcommand} needs a cloud box, which is not built yet.\n` +
|
|
67
|
+
` \`${rnxPublicBrand.commandName} box\` runs the same shell locally over this checkout.`,
|
|
68
|
+
)
|
|
69
|
+
return 1
|
|
70
|
+
}
|
|
71
|
+
console.error(
|
|
72
|
+
` unknown command: ${rnxPublicBrand.commandName} box ${subcommand}\n` +
|
|
73
|
+
` run \`${rnxPublicBrand.commandName} box --help\` to see the box surface.`,
|
|
74
|
+
)
|
|
75
|
+
return 1
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return runLocalBox(opts)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function runLocalBox(opts: BoxCommandOptions): Promise<number> {
|
|
82
|
+
// findRepositoryPackageRoot answers both halves of what a box needs: the
|
|
83
|
+
// nearest package.json, and only when it sits inside a git repository. git
|
|
84
|
+
// is what decides which files belong to the project, so a box without it
|
|
85
|
+
// would have to walk the whole directory tree — measured at 230k files on
|
|
86
|
+
// this repo against 15k from git.
|
|
87
|
+
const root = findRepositoryPackageRoot(process.cwd())
|
|
88
|
+
if (!root) {
|
|
89
|
+
console.error(
|
|
90
|
+
` ${rnxPublicBrand.commandName} box: no project here.\n` +
|
|
91
|
+
` the box needs a directory inside a git repository that has a\n` +
|
|
92
|
+
` package.json. it asks git which files belong to the project, so it\n` +
|
|
93
|
+
` never walks node_modules or build output. run \`git init\` if this is\n` +
|
|
94
|
+
` a new project.`,
|
|
95
|
+
)
|
|
96
|
+
return 1
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const plane = new CheckoutFilePlane(root)
|
|
100
|
+
try {
|
|
101
|
+
await plane.refresh()
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error instanceof CheckoutRequiresGitError) {
|
|
104
|
+
console.error(` ${rnxPublicBrand.commandName} box: ${error.message}`)
|
|
105
|
+
return 1
|
|
106
|
+
}
|
|
107
|
+
throw error
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const shell = createHarnessShell({
|
|
111
|
+
projectFs: new HarnessProjectFs(plane),
|
|
112
|
+
commands: [createRnxCommand({ root, shellCwd: () => shell.cwd })],
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const relative = path.relative(root, process.cwd())
|
|
116
|
+
console.log(` ...starting local box`)
|
|
117
|
+
console.log(` ...mounting ${root} at ${PROJECT_MOUNT}`)
|
|
118
|
+
console.log(` ...accessing shell\n`)
|
|
119
|
+
if (opts.verbose) {
|
|
120
|
+
console.log(` ${plane.index().size} project files indexed\n`)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return repl(shell, plane, relative)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function repl(
|
|
127
|
+
shell: ReturnType<typeof createHarnessShell>,
|
|
128
|
+
plane: CheckoutFilePlane,
|
|
129
|
+
startRelative: string,
|
|
130
|
+
): Promise<number> {
|
|
131
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
132
|
+
|
|
133
|
+
// enter where the user was standing, so `rnx box` from a subdirectory does
|
|
134
|
+
// not silently teleport them to the project root.
|
|
135
|
+
if (startRelative && !startRelative.startsWith('..')) {
|
|
136
|
+
await shell.exec(`cd ${JSON.stringify(startRelative)}`)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const prompt = () => {
|
|
140
|
+
rl.setPrompt(`${shell.cwd} > `)
|
|
141
|
+
rl.prompt()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// readline delivers a pasted block, or a piped script, as a burst of `line`
|
|
145
|
+
// events. run them one at a time in order — dropping the ones that arrive
|
|
146
|
+
// while a command is running would silently execute only the first line of
|
|
147
|
+
// a paste.
|
|
148
|
+
let queue: Promise<void> = Promise.resolve()
|
|
149
|
+
// stdin ended: stop printing prompts, but still finish what is already
|
|
150
|
+
// queued — piping a script in must run every line, not just the first.
|
|
151
|
+
let inputEnded = false
|
|
152
|
+
// the user typed `exit`: abandon anything still queued behind it.
|
|
153
|
+
let stopped = false
|
|
154
|
+
|
|
155
|
+
const run = async (command: string) => {
|
|
156
|
+
if (stopped) return
|
|
157
|
+
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)
|
|
163
|
+
if (result.text && result.text !== '(no output)') console.log(result.text)
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
166
|
+
}
|
|
167
|
+
if (!inputEnded && !stopped) prompt()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return new Promise<number>((resolve) => {
|
|
171
|
+
prompt()
|
|
172
|
+
|
|
173
|
+
rl.on('line', (line) => {
|
|
174
|
+
const command = line.trim()
|
|
175
|
+
if (!command) {
|
|
176
|
+
queue = queue.then(() => {
|
|
177
|
+
if (!inputEnded && !stopped) prompt()
|
|
178
|
+
})
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
if (command === 'exit' || command === 'quit') {
|
|
182
|
+
queue = queue.then(() => {
|
|
183
|
+
stopped = true
|
|
184
|
+
rl.close()
|
|
185
|
+
})
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
queue = queue.then(() => run(command))
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
rl.on('close', () => {
|
|
192
|
+
inputEnded = true
|
|
193
|
+
// let any already-queued command finish before the process exits.
|
|
194
|
+
queue.then(() => {
|
|
195
|
+
console.log()
|
|
196
|
+
resolve(0)
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
}
|
package/cli/commands/control.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from 'sootsim-engine/settings'
|
|
10
10
|
import { WebSocket } from 'ws'
|
|
11
11
|
import { probePort } from '../../scripts/dev-server-scanner'
|
|
12
|
+
import { isAppDeepLinkTarget, openAppUrl } from '../../src/app-url'
|
|
12
13
|
import { isLoopbackHost } from '../../src/backend-origin'
|
|
13
14
|
import { resolveSootsimShellBaseUrlForBridgePort } from '../../src/bridge-constants'
|
|
14
15
|
import {
|
|
@@ -65,7 +66,7 @@ import {
|
|
|
65
66
|
worldForBridgePort,
|
|
66
67
|
type BridgeSimInfo,
|
|
67
68
|
} from '../ws-bridge'
|
|
68
|
-
import {
|
|
69
|
+
import { inspectDescribe, inspectWaitReady } from './inspect/core'
|
|
69
70
|
import { isAgentEnv } from './inspect/env'
|
|
70
71
|
import type { DriverId, DriverLaunchResult } from '../drivers/types'
|
|
71
72
|
|
|
@@ -161,31 +162,6 @@ function normalizeKnownTarget(target: string): string {
|
|
|
161
162
|
return target.trim()
|
|
162
163
|
}
|
|
163
164
|
|
|
164
|
-
const DEEP_LINK_SCHEME_RE = /^[A-Za-z][A-Za-z0-9+.-]*:/
|
|
165
|
-
|
|
166
|
-
export function normalizeDeepLinkTarget(target: string): string {
|
|
167
|
-
const trimmed = target.trim()
|
|
168
|
-
if (!trimmed) {
|
|
169
|
-
throw new Error('expected <url-or-route>')
|
|
170
|
-
}
|
|
171
|
-
if (DEEP_LINK_SCHEME_RE.test(trimmed) || trimmed.startsWith('/')) {
|
|
172
|
-
return trimmed
|
|
173
|
-
}
|
|
174
|
-
return `/${trimmed}`
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function isObjectRecord(value: unknown): value is Record<string, unknown> {
|
|
178
|
-
return value !== null && typeof value === 'object'
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function shouldOpenAsDeepLink(target: string): boolean {
|
|
182
|
-
const trimmed = target.trim()
|
|
183
|
-
if (!trimmed) return false
|
|
184
|
-
if (trimmed.startsWith('/')) return true
|
|
185
|
-
if (!DEEP_LINK_SCHEME_RE.test(trimmed)) return false
|
|
186
|
-
return !/^https?:\/\//i.test(trimmed)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
165
|
export async function ensureBridgeForOpen(
|
|
190
166
|
preferredPort: number,
|
|
191
167
|
explicitPort = false,
|
|
@@ -1272,19 +1248,12 @@ async function maybeDescribeAfterOpen(wsPort: number, simId: string, args: strin
|
|
|
1272
1248
|
simIdSource: 'flag',
|
|
1273
1249
|
})
|
|
1274
1250
|
try {
|
|
1275
|
-
let readyProbe:
|
|
1251
|
+
let readyProbe: Awaited<ReturnType<typeof inspectWaitReady>> | null = null
|
|
1276
1252
|
try {
|
|
1277
|
-
readyProbe =
|
|
1278
|
-
type: 'evaluate',
|
|
1279
|
-
code: WAIT_READY_PROBE,
|
|
1280
|
-
})) as ReadyProbe | null
|
|
1253
|
+
readyProbe = await inspectWaitReady(bridge, 1)
|
|
1281
1254
|
} catch {}
|
|
1282
1255
|
if (readyProbe) {
|
|
1283
|
-
|
|
1284
|
-
readyProbe.flag === true &&
|
|
1285
|
-
!readyProbe.loadingText &&
|
|
1286
|
-
readyProbeHasContent(readyProbe)
|
|
1287
|
-
if (!readyEnough) {
|
|
1256
|
+
if (!readyProbe.ready) {
|
|
1288
1257
|
const reason = readyProbe.loadingText
|
|
1289
1258
|
? `still showing "${readyProbe.loadingText}"`
|
|
1290
1259
|
: readyProbe.flag !== true
|
|
@@ -1298,29 +1267,16 @@ async function maybeDescribeAfterOpen(wsPort: number, simId: string, args: strin
|
|
|
1298
1267
|
console.log(' before interacting, run: rnx wait ready --max-ms 120000')
|
|
1299
1268
|
}
|
|
1300
1269
|
}
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
return { tree, shell }
|
|
1310
|
-
})()`
|
|
1311
|
-
const result = (await bridge.send({ type: 'evaluate', code })) as {
|
|
1312
|
-
tree: string | null
|
|
1313
|
-
shell: { state?: string; activeApp?: string } | null
|
|
1314
|
-
} | null
|
|
1270
|
+
const result = await inspectDescribe(bridge, {
|
|
1271
|
+
describe: true,
|
|
1272
|
+
verbose: false,
|
|
1273
|
+
filter: '',
|
|
1274
|
+
compact: true,
|
|
1275
|
+
hideXy: false,
|
|
1276
|
+
maxDepth: 12,
|
|
1277
|
+
})
|
|
1315
1278
|
if (!result?.tree) return
|
|
1316
1279
|
console.log('')
|
|
1317
|
-
if (result.shell?.state) {
|
|
1318
|
-
const parts = [
|
|
1319
|
-
`state=${result.shell.state}`,
|
|
1320
|
-
result.shell.activeApp ? `app=${result.shell.activeApp}` : null,
|
|
1321
|
-
].filter(Boolean)
|
|
1322
|
-
console.log(` shell: ${parts.join(' ')}`)
|
|
1323
|
-
}
|
|
1324
1280
|
console.log(result.tree)
|
|
1325
1281
|
} finally {
|
|
1326
1282
|
bridge.close()
|
|
@@ -1385,27 +1341,10 @@ export async function runLinkCommand(args: string[], opts: ControlCommandOptions
|
|
|
1385
1341
|
process.exit(1)
|
|
1386
1342
|
}
|
|
1387
1343
|
|
|
1388
|
-
let target: string
|
|
1389
|
-
try {
|
|
1390
|
-
target = normalizeDeepLinkTarget(parsed.positional[0] ?? '')
|
|
1391
|
-
} catch (err) {
|
|
1392
|
-
console.error(` rnx open: ${err instanceof Error ? err.message : String(err)}`)
|
|
1393
|
-
process.exit(1)
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
1344
|
const bridge = createBridgeFromParsed(parsed)
|
|
1397
1345
|
const simHint = parsed.simId ? ` --sim ${parsed.simId}` : ''
|
|
1398
1346
|
try {
|
|
1399
|
-
const
|
|
1400
|
-
type: 'call',
|
|
1401
|
-
path: 'SootSim.bridges.test.openDeepLink',
|
|
1402
|
-
args: [target],
|
|
1403
|
-
})
|
|
1404
|
-
if (isObjectRecord(result) && result.ok === false) {
|
|
1405
|
-
throw new Error(
|
|
1406
|
-
typeof result.error === 'string' ? result.error : 'deep-link dispatch failed',
|
|
1407
|
-
)
|
|
1408
|
-
}
|
|
1347
|
+
const target = await openAppUrl(bridge, parsed.positional[0] ?? '')
|
|
1409
1348
|
console.log(` opened deep link: ${target}`)
|
|
1410
1349
|
} catch (err) {
|
|
1411
1350
|
console.error(` link failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
@@ -1475,7 +1414,7 @@ export async function runOpenCommand(
|
|
|
1475
1414
|
}
|
|
1476
1415
|
const target = parsed.positional[0] || ''
|
|
1477
1416
|
|
|
1478
|
-
if (
|
|
1417
|
+
if (isAppDeepLinkTarget(target)) {
|
|
1479
1418
|
if (openInNewSim) {
|
|
1480
1419
|
console.error(
|
|
1481
1420
|
' rnx open: deep links target the current app; remove --new, --profile, or --ephemeral',
|
package/cli/commands/detox.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { spawn } from 'child_process'
|
|
14
14
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'fs'
|
|
15
|
+
import { createRequire } from 'module'
|
|
15
16
|
import { tmpdir } from 'os'
|
|
16
17
|
import { dirname, resolve, join, relative } from 'path'
|
|
17
18
|
import { rnxPublicBrand } from '../../src/public-brand'
|
|
@@ -195,7 +196,23 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
195
196
|
console.log(` port: ${port}${headed ? ' (headed)' : ''}`)
|
|
196
197
|
if (localConfig) console.log(` config: ${localConfig}`)
|
|
197
198
|
|
|
198
|
-
|
|
199
|
+
// proof capture retains several full-resolution frames while jest tears its
|
|
200
|
+
// browser down. node 24.16.0's maglev collector can crash in
|
|
201
|
+
// ClearStaleLeftTrimmedPointerVisitor after every assertion has passed.
|
|
202
|
+
// the conformance lane pins that exact node runtime, so disable only its
|
|
203
|
+
// unstable optimization tier. normal user detox runs keep their own node
|
|
204
|
+
// defaults and still resolve jest through npx.
|
|
205
|
+
const proofCapture =
|
|
206
|
+
process.env.CONFORMANCE_PROOF === '1' || !!process.env.CONFORMANCE_PROOF_DIR
|
|
207
|
+
const command = proofCapture ? 'node' : 'npx'
|
|
208
|
+
const commandArgs = proofCapture
|
|
209
|
+
? [
|
|
210
|
+
'--no-maglev',
|
|
211
|
+
createRequire(join(process.cwd(), 'package.json')).resolve('jest/bin/jest'),
|
|
212
|
+
...jestArgs.slice(1),
|
|
213
|
+
]
|
|
214
|
+
: jestArgs
|
|
215
|
+
const child = spawn(command, commandArgs, {
|
|
199
216
|
cwd: process.cwd(),
|
|
200
217
|
stdio: 'inherit',
|
|
201
218
|
env: {
|