sootsim 0.1.304 → 0.1.306
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 +10 -4
- package/dist-cli/bin.js +5 -5
- package/dist-cli/chunks/{agent-EK355OQM.js → agent-43LRLQBS.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-LCJJWHZE.js → agent-wrapper-QYKQDC2Y.js} +2 -2
- package/dist-cli/chunks/{app-fonts-LIPU2DLT.js → app-fonts-IHE34DCY.js} +2 -2
- package/dist-cli/chunks/{assert-OASKCPBX.js → assert-LAXNLFZ6.js} +2 -2
- package/dist-cli/chunks/{auth-XYYKD33F.js → auth-CFTFPYKX.js} +2 -2
- package/dist-cli/chunks/{beta-TOL55XPM.js → beta-YPZFX3DF.js} +2 -2
- package/dist-cli/chunks/{camera-PHKVEQHR.js → camera-FHHHH6GY.js} +2 -2
- package/dist-cli/chunks/{chunk-XHYSASAX.js → chunk-2UC72ZR6.js} +1 -1
- package/dist-cli/chunks/{chunk-BJOT2LL6.js → chunk-3XUXMKKJ.js} +2 -2
- package/dist-cli/chunks/{chunk-HFA5ZACE.js → chunk-4735ZG3S.js} +1 -1
- package/dist-cli/chunks/{chunk-5NJTX5OP.js → chunk-4KABOHME.js} +2 -2
- package/dist-cli/chunks/chunk-4MLH5CMV.js +125 -0
- package/dist-cli/chunks/chunk-4TN4CD7M.js +15 -0
- package/dist-cli/chunks/{chunk-ITDTSNN2.js → chunk-4XGRVAXA.js} +2 -2
- package/dist-cli/chunks/{chunk-2MEUZOSD.js → chunk-6PIGQNS4.js} +1 -1
- package/dist-cli/chunks/{chunk-INBBOCU4.js → chunk-6SBFFSXO.js} +2 -2
- package/dist-cli/chunks/chunk-6SIWYEK5.js +4 -0
- package/dist-cli/chunks/{chunk-YAV6BH3A.js → chunk-6WCW6PES.js} +2 -2
- package/dist-cli/chunks/{chunk-2RLFUM3C.js → chunk-74Z7ENQL.js} +104 -127
- package/dist-cli/chunks/{chunk-RQJDIDJ3.js → chunk-7RZYUZEK.js} +1 -1
- package/dist-cli/chunks/{chunk-GF3LDVWF.js → chunk-7VVWOKJE.js} +2 -2
- package/dist-cli/chunks/chunk-B3CXF3EF.js +5 -0
- package/dist-cli/chunks/{chunk-RT7TUHH7.js → chunk-BPNVJLGT.js} +2 -2
- package/dist-cli/chunks/{chunk-3UUHIHA6.js → chunk-C6TKPNCZ.js} +2 -2
- package/dist-cli/chunks/{chunk-IWRRIQDJ.js → chunk-CLN6VD2I.js} +2 -2
- package/dist-cli/chunks/{chunk-FNI75AD7.js → chunk-FXC6BYKX.js} +1 -1
- package/dist-cli/chunks/{chunk-RJWQ3VAD.js → chunk-G73JPCHX.js} +11 -11
- package/dist-cli/chunks/{chunk-AJYA6R3N.js → chunk-HMW7QKJV.js} +2 -2
- package/dist-cli/chunks/{chunk-NXYKNKNL.js → chunk-HPCKCWSQ.js} +2 -2
- package/dist-cli/chunks/{chunk-EXW6TH3O.js → chunk-HTR4DSA5.js} +2 -2
- package/dist-cli/chunks/{chunk-RYQUO6LT.js → chunk-IGKZOI2V.js} +1 -1
- package/dist-cli/chunks/{chunk-VUR5QXVX.js → chunk-IXPUTLYO.js} +2 -2
- package/dist-cli/chunks/{chunk-GMILMS7P.js → chunk-JPQSUHXW.js} +2 -2
- package/dist-cli/chunks/chunk-KNPXTXMI.js +4 -0
- package/dist-cli/chunks/{chunk-Y5YM7XHU.js → chunk-LHCC656T.js} +1 -1
- package/dist-cli/chunks/{chunk-A4UYZTV7.js → chunk-LOXCEKKF.js} +2 -2
- package/dist-cli/chunks/{chunk-CMYVGJBQ.js → chunk-MBF4LLVB.js} +1 -1
- package/dist-cli/chunks/{chunk-V6JDDX4B.js → chunk-MCGRZYAP.js} +7 -7
- package/dist-cli/chunks/chunk-NH2KYSY7.js +4 -0
- package/dist-cli/chunks/{chunk-QWQHSUZC.js → chunk-NOLQ4UEP.js} +2 -2
- package/dist-cli/chunks/chunk-NQ56D5JF.js +4 -0
- package/dist-cli/chunks/{chunk-G4R7CPCY.js → chunk-OYFRTYRJ.js} +2 -2
- package/dist-cli/chunks/{chunk-ENXLKDQ6.js → chunk-QHGQTHHD.js} +3 -3
- package/dist-cli/chunks/{chunk-SVFN66PF.js → chunk-RYXC27XV.js} +15 -12
- package/dist-cli/chunks/{chunk-ZONISYZ5.js → chunk-TCB56ABT.js} +1 -1
- package/dist-cli/chunks/{chunk-UB3OWUVK.js → chunk-TR4FKAKQ.js} +1 -1
- package/dist-cli/chunks/{chunk-XXS4TCOO.js → chunk-U6NDLMBM.js} +24 -25
- package/dist-cli/chunks/{chunk-SN6ARNCL.js → chunk-UAYFVJ7N.js} +2 -2
- package/dist-cli/chunks/{chunk-HFKQUS4I.js → chunk-UBLLWSSF.js} +3 -3
- package/dist-cli/chunks/{chunk-MSYPDBFG.js → chunk-UTE2W4WC.js} +2 -2
- package/dist-cli/chunks/{chunk-XCVSB47R.js → chunk-UTHRVXG5.js} +2 -2
- package/dist-cli/chunks/{chunk-LRXSLMX4.js → chunk-V5PJXD2O.js} +1 -1
- package/dist-cli/chunks/{chunk-JDKWIB6V.js → chunk-VLKENZFJ.js} +1 -1
- package/dist-cli/chunks/{chunk-7RFXZXKH.js → chunk-VTPICQDM.js} +1 -1
- package/dist-cli/chunks/chunk-XZSXFV33.js +71 -0
- package/dist-cli/chunks/chunk-YBO7SFWN.js +6 -0
- package/dist-cli/chunks/{chunk-OSH6526H.js → chunk-YVWYARHY.js} +1 -1
- package/dist-cli/chunks/{cleanup-GCTEGJMP.js → cleanup-GYDCGNEJ.js} +2 -2
- package/dist-cli/chunks/{cli-version-G72HUH4E.js → cli-version-3HG7ILRV.js} +2 -2
- package/dist-cli/chunks/{compat-IC4JHGBH.js → compat-GS3SVTB4.js} +2 -2
- package/dist-cli/chunks/{config-C65MZFU3.js → config-W5LN7HWO.js} +2 -2
- package/dist-cli/chunks/control-BE55SA2W.js +4 -0
- package/dist-cli/chunks/daemon-3YMH272E.js +4 -0
- package/dist-cli/chunks/{debug-LEL32PDN.js → debug-2BBR66MJ.js} +3 -3
- package/dist-cli/chunks/desktop-PPB66SZO.js +31 -0
- package/dist-cli/chunks/{detox-SCCWUIKK.js → detox-P7SDHLB4.js} +2 -2
- package/dist-cli/chunks/{device-MTDEKDUK.js → device-JB4NSOZV.js} +2 -2
- package/dist-cli/chunks/{diagnose-UZJCKLPZ.js → diagnose-M662DIPM.js} +2 -2
- package/dist-cli/chunks/disk-cleanup-HN662P2U.js +4 -0
- package/dist-cli/chunks/drivers-ALHRJJGF.js +4 -0
- package/dist-cli/chunks/{film-YBE544PG.js → film-QZMDVCZI.js} +3 -3
- package/dist-cli/chunks/flow-BHSACBQ5.js +4 -0
- package/dist-cli/chunks/help-V2KS2AUT.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-KJYCJLSR.js → hidden-runtime-alias-LJXH6WPY.js} +2 -2
- package/dist-cli/chunks/{home-paths-NSHWNEGK.js → home-paths-TQ53NFW3.js} +2 -2
- package/dist-cli/chunks/inspect-6PW37GVK.js +4 -0
- package/dist-cli/chunks/install-desktop-VMVPZSXF.js +4 -0
- package/dist-cli/chunks/{login-AEVUU72R.js → login-7XXY5JP4.js} +4 -4
- package/dist-cli/chunks/{logout-72XHF6J2.js → logout-UNARNKTJ.js} +2 -2
- package/dist-cli/chunks/{maestro-OXSKQJ6B.js → maestro-66RNLMLU.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-AKZFORVJ.js → maestro-generate-IO25AY3E.js} +3 -3
- package/dist-cli/chunks/{mode-U6KRG2PE.js → mode-DPGASAG4.js} +2 -2
- package/dist-cli/chunks/optional-demo-registry-O5GGFNAI.js +4 -0
- package/dist-cli/chunks/{perf-KF2BJZIN.js → perf-CBUP2ERS.js} +2 -2
- package/dist-cli/chunks/{permissions-TRJG4QAI.js → permissions-3AOMCAT3.js} +2 -2
- package/dist-cli/chunks/{record-TICJYZOA.js → record-Q7VRLH7A.js} +3 -3
- package/dist-cli/chunks/{report-issue-OQPLBQMJ.js → report-issue-DTWZECYM.js} +2 -2
- package/dist-cli/chunks/runtime-VGYKLLTE.js +4 -0
- package/dist-cli/chunks/{screenshot-command-XLERKHVW.js → screenshot-command-SSWKZ4U3.js} +6 -6
- package/dist-cli/chunks/screenshot-layers-2ABSTF2O.js +20 -0
- package/dist-cli/chunks/{screenshots-capture-B6OORSD4.js → screenshots-capture-QA7T5DED.js} +2 -2
- package/dist-cli/chunks/{serve-5M7MIPWF.js → serve-T4EQBJM6.js} +3 -3
- package/dist-cli/chunks/{setup-HJZDC4PK.js → setup-BXQI3YT4.js} +2 -2
- package/dist-cli/chunks/{skills-RXHLMGGL.js → skills-AYSO5FJB.js} +2 -2
- package/dist-cli/chunks/{storage-MMPD2XBS.js → storage-KBHG5M5F.js} +2 -2
- package/dist-cli/chunks/store-GUNEQ4EC.js +4 -0
- package/dist-cli/chunks/telemetry-4VZ43AHH.js +4 -0
- package/dist-cli/chunks/{timeline-6PF5BZ37.js → timeline-AB2R26RE.js} +2 -2
- package/dist-cli/chunks/{upgrade-QSMIK3RC.js → upgrade-5XXQ7OCE.js} +2 -2
- package/dist-cli/chunks/upload-M55IQQHQ.js +4 -0
- package/dist-cli/chunks/{version-WLTFXEDC.js → version-PH3OUNPP.js} +2 -2
- package/dist-cli/chunks/{web-DI73LQ7N.js → web-XTOGNFZ7.js} +2 -2
- package/dist-cli/chunks/{what-happened-4SAMGZWR.js → what-happened-JLI3NTEU.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 +43 -21
- 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 +8 -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 +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +222 -302
- 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/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.cjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +43 -13
- package/dist-lib/sdk.cjs +47 -10
- package/dist-lib/sdk.mjs +47 -10
- package/dist-lib/skills.cjs +5708 -1217
- package/dist-lib/vite.cjs +1 -1
- package/package.json +1 -1
- package/scripts/dev-server-scanner.ts +9 -15
- package/scripts/optional-demo-registry.ts +52 -10
- package/skills/sootsim-debug/SKILL.md +4 -5
- package/skills/sootsim-setup/SKILL.md +29 -49
- package/src/bridge-constants.ts +15 -0
- package/src/host/bridge-host.ts +0 -16
- package/src/host/open-url.ts +2 -147
- package/src/plist.ts +27 -0
- package/src/settings-bundle.ts +2 -23
- package/src/skills/builtin/maestro-test.ts +4 -1
- package/src/vite-plugin.ts +2 -1
- package/dist-cli/chunks/chunk-76OLOD2G.js +0 -15
- package/dist-cli/chunks/chunk-ABHNOM2C.js +0 -4
- package/dist-cli/chunks/chunk-BPRMA4IC.js +0 -4
- package/dist-cli/chunks/chunk-M3ETYVNL.js +0 -4
- package/dist-cli/chunks/chunk-W3CSYOSQ.js +0 -4
- package/dist-cli/chunks/chunk-WNDYTKLC.js +0 -4
- package/dist-cli/chunks/chunk-YXHO2OUL.js +0 -7
- package/dist-cli/chunks/chunk-ZDRI2LUG.js +0 -125
- package/dist-cli/chunks/control-RWLYBKQR.js +0 -4
- package/dist-cli/chunks/daemon-D77E5G6Z.js +0 -4
- package/dist-cli/chunks/desktop-RDSISJH2.js +0 -34
- package/dist-cli/chunks/disk-cleanup-HLWRVTVW.js +0 -4
- package/dist-cli/chunks/drivers-Q7GMKTBM.js +0 -4
- package/dist-cli/chunks/flow-5VOVAVZN.js +0 -4
- package/dist-cli/chunks/help-2PESJ7V2.js +0 -4
- package/dist-cli/chunks/inspect-OIA3Q5MD.js +0 -4
- package/dist-cli/chunks/install-desktop-7KLACQUW.js +0 -4
- package/dist-cli/chunks/optional-demo-registry-L3VIWBI2.js +0 -4
- package/dist-cli/chunks/runtime-HDMLWNXW.js +0 -4
- package/dist-cli/chunks/store-APKY72ZY.js +0 -4
- package/dist-cli/chunks/telemetry-W3YRHE67.js +0 -4
- package/dist-cli/chunks/upload-HQEOYILQ.js +0 -4
package/dist-lib/vite.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
1
|
+
/*! sootsim v0.1.306 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
package/package.json
CHANGED
|
@@ -29,14 +29,6 @@ import {
|
|
|
29
29
|
} from '../src/settings-bundle.ts'
|
|
30
30
|
import { loadOptionalDemoApps, optionalDemoAppsSync } from './optional-demo-registry.ts'
|
|
31
31
|
|
|
32
|
-
// pre-warm the optional internal demo registry once at module load. in-repo
|
|
33
|
-
// (electron tray / `bun run demo` discovery) this resolves the source registry
|
|
34
|
-
// so a launched demo's runtimeConfig is folded into its bundle URL; on a
|
|
35
|
-
// published install the registry was never shipped, so this resolves to [] and
|
|
36
|
-
// every dev server is treated generically. either way `withRuntimeConfig` below
|
|
37
|
-
// reads the synchronous cached view.
|
|
38
|
-
void loadOptionalDemoApps()
|
|
39
|
-
|
|
40
32
|
export interface DiscoveredServer {
|
|
41
33
|
port: number
|
|
42
34
|
framework: 'metro' | 'expo' | 'vxrn' | 'one' | 'unknown'
|
|
@@ -556,6 +548,11 @@ export async function probePort(
|
|
|
556
548
|
port: number,
|
|
557
549
|
buildIconProxyUrl?: (externalUrl: string) => string,
|
|
558
550
|
): Promise<DiscoveredServer | null> {
|
|
551
|
+
// callers such as `sootsim open <port>` probe one port directly instead of
|
|
552
|
+
// going through scanDevServers. wait for the optional in-repo registry here
|
|
553
|
+
// too, or a cold CLI process can build the known app's bundle url before its
|
|
554
|
+
// runtime config is available.
|
|
555
|
+
await loadOptionalDemoApps()
|
|
559
556
|
if (!(await tcpPing(port))) return null
|
|
560
557
|
|
|
561
558
|
const onePath = `/node_modules/one/metro-entry.bundle?platform=ios&dev=true`
|
|
@@ -824,13 +821,10 @@ export const __attachAppSettingsBundleForTests = attachAppSettingsBundle
|
|
|
824
821
|
export async function scanDevServers(
|
|
825
822
|
opts: ScanOptions = {},
|
|
826
823
|
): Promise<DiscoveredServer[]> {
|
|
827
|
-
//
|
|
828
|
-
//
|
|
829
|
-
//
|
|
830
|
-
//
|
|
831
|
-
// back a bundle URL with no sootsimConfig at all. it is already resolved by
|
|
832
|
-
// the time any real scan runs, which is exactly why the window is easy to
|
|
833
|
-
// miss; it opens when disk is contended. memoized, so this is free after the
|
|
824
|
+
// optionalDemoAppsSync() returns [] until the dynamic import resolves, and
|
|
825
|
+
// an empty registry is indistinguishable from a published install. await it
|
|
826
|
+
// before cache validation so a known app cannot lose its runtimeConfig when
|
|
827
|
+
// disk contention delays the import. memoized, so this is free after the
|
|
834
828
|
// first call.
|
|
835
829
|
await loadOptionalDemoApps()
|
|
836
830
|
const processes = await discoverListeningProcesses(opts.excludePorts)
|
|
@@ -8,17 +8,54 @@
|
|
|
8
8
|
// apps — shipping the registry once matched a brand-new user's `expo start` on
|
|
9
9
|
// the default port 8081 against the 3pc `one` entry and crashed their metro
|
|
10
10
|
// (repo-agnostic-pipeline violation). so the registry is NOT in the package
|
|
11
|
-
// `files` list and is
|
|
12
|
-
//
|
|
13
|
-
//
|
|
11
|
+
// `files` list, and the `import()` below is resolved to an absolute path at
|
|
12
|
+
// runtime: it finds the source registry in-repo and nothing (empty list) on a
|
|
13
|
+
// published install. no bundler can see through that, so it never inlines.
|
|
14
14
|
//
|
|
15
15
|
// one path, no env toggle: every consumer goes through this loader; whether the
|
|
16
16
|
// registry resolves is purely a function of whether the file exists on disk.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
import { existsSync } from 'node:fs'
|
|
19
|
+
import { basename, dirname, join } from 'node:path'
|
|
20
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
21
|
+
|
|
22
|
+
// this module is emitted at three different depths — the source at
|
|
23
|
+
// packages/sootsim/scripts/, the built CLI at packages/sootsim/dist-cli/chunks/,
|
|
24
|
+
// and the electron main bundle at packages/sootsim-engine/dist-electron/ — so a
|
|
25
|
+
// relative './demo-app-registry.ts' is only correct from the source copy. from
|
|
26
|
+
// the other two it points at a file that does not exist, the catch below
|
|
27
|
+
// swallows that, and every demo app then boots with no runtimeConfig at all
|
|
28
|
+
// (mattermost stops on the server-select screen because AutoSelectServerUrl
|
|
29
|
+
// never reaches it). the one description that holds from every emit site is the
|
|
30
|
+
// ancestor that holds the registry at its repo path, which in-repo is the
|
|
31
|
+
// monorepo root.
|
|
32
|
+
const REGISTRY_REPO_PATH = 'packages/sootsim/scripts/demo-app-registry.ts'
|
|
33
|
+
|
|
34
|
+
// the walk must never leave the installed package. `sootsim` publishes this
|
|
35
|
+
// loader's source, so on a published install it runs from
|
|
36
|
+
// node_modules/sootsim/scripts/ (or dist-cli/chunks/) with the consumer's whole
|
|
37
|
+
// filesystem above it — and consumers include this repo's own templates and
|
|
38
|
+
// examples, which install sootsim while sitting inside the monorepo. an
|
|
39
|
+
// unbounded walk from there reaches the real registry and hands a user's
|
|
40
|
+
// `expo start` on port 8081 the internal 3pc entry, which is the exact incident
|
|
41
|
+
// the header describes. a node_modules ancestor means "installed as a
|
|
42
|
+
// dependency", so the search stops there and the registry stays absent.
|
|
43
|
+
const MAX_ANCESTOR_STEPS = 8
|
|
44
|
+
|
|
45
|
+
/** absolute path to the internal registry, or null when this copy is running
|
|
46
|
+
* outside the monorepo (a published install) and must not have one. */
|
|
47
|
+
export function findInternalDemoRegistry(startDir: string): string | null {
|
|
48
|
+
let dir = startDir
|
|
49
|
+
for (let step = 0; step < MAX_ANCESTOR_STEPS; step++) {
|
|
50
|
+
if (basename(dir) === 'node_modules') return null
|
|
51
|
+
const candidate = join(dir, REGISTRY_REPO_PATH)
|
|
52
|
+
if (existsSync(candidate)) return candidate
|
|
53
|
+
const parent = dirname(dir)
|
|
54
|
+
if (parent === dir) return null
|
|
55
|
+
dir = parent
|
|
56
|
+
}
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
22
59
|
|
|
23
60
|
export interface OptionalDemoApp {
|
|
24
61
|
name: string
|
|
@@ -35,8 +72,13 @@ let cached: OptionalDemoApp[] | null = null
|
|
|
35
72
|
* returns [] on a published install where the registry was not shipped. */
|
|
36
73
|
export async function loadOptionalDemoApps(): Promise<OptionalDemoApp[]> {
|
|
37
74
|
if (cached) return cached
|
|
75
|
+
const registryPath = findInternalDemoRegistry(dirname(fileURLToPath(import.meta.url)))
|
|
76
|
+
if (!registryPath) {
|
|
77
|
+
cached = []
|
|
78
|
+
return cached
|
|
79
|
+
}
|
|
38
80
|
try {
|
|
39
|
-
const mod = (await import(/* @vite-ignore */
|
|
81
|
+
const mod = (await import(/* @vite-ignore */ pathToFileURL(registryPath).href)) as {
|
|
40
82
|
APPS?: OptionalDemoApp[]
|
|
41
83
|
}
|
|
42
84
|
cached = Array.isArray(mod.APPS) ? mod.APPS : []
|
|
@@ -47,8 +89,8 @@ export async function loadOptionalDemoApps(): Promise<OptionalDemoApp[]> {
|
|
|
47
89
|
}
|
|
48
90
|
|
|
49
91
|
/** synchronous view of the registry — empty until `loadOptionalDemoApps()` has
|
|
50
|
-
* resolved at least once.
|
|
51
|
-
*
|
|
92
|
+
* resolved at least once. every caller that builds a result from the registry
|
|
93
|
+
* awaits the load first. */
|
|
52
94
|
export function optionalDemoAppsSync(): OptionalDemoApp[] {
|
|
53
95
|
return cached ?? []
|
|
54
96
|
}
|
|
@@ -64,11 +64,10 @@ regression test. a pile of ad-hoc logs helps no one and gets deleted.
|
|
|
64
64
|
- **pin a sim once.** if more than one is live, an unpinned command
|
|
65
65
|
refuses to choose. run `sootsim use <id>` at the start of the workflow.
|
|
66
66
|
- **keep one sim and reuse its browser tab.** before `sootsim open`, run
|
|
67
|
-
`sootsim list --all`.
|
|
68
|
-
`sootsim
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if the current sim must be replaced, close it before opening the replacement.
|
|
67
|
+
`sootsim list --all`. pin the target and use `sootsim do reload`; a plain
|
|
68
|
+
`sootsim open <target>` also navigates the saved sim. reserve `--new` for a
|
|
69
|
+
genuinely concurrent sim. repeated browser trees consume memory and CPU on a
|
|
70
|
+
shared machine.
|
|
72
71
|
- **`debug eval` is an escape hatch.** if a top-level command feels
|
|
73
72
|
broken or missing, file an issue — every agent after you benefits from
|
|
74
73
|
a better CLI, not a pile of ad-hoc scripts.
|
|
@@ -15,60 +15,45 @@ debugging or `/sootsim-test` for writing tests.
|
|
|
15
15
|
## anti-patterns (read first)
|
|
16
16
|
|
|
17
17
|
- **never `sootsim claim --force` a sim held by another CLI.** the lease
|
|
18
|
-
exists for a reason
|
|
18
|
+
exists for a reason. forcing strands the other agent and corrupts the
|
|
19
19
|
bridge state. use `sootsim open --new` to start a fresh sim instead.
|
|
20
20
|
- **don't switch sims mid-investigation.** pin once with `sootsim use
|
|
21
21
|
<id>` and keep it pinned. an unpinned command refuses to choose when more
|
|
22
22
|
than one driveable sim is live.
|
|
23
|
-
- **an empty `sootsim list` is not "broken sootsim"
|
|
23
|
+
- **an empty `sootsim list` is not "broken sootsim".** it usually means the
|
|
24
24
|
daemon is down, the dev server port is wrong, or the runtime is missing.
|
|
25
25
|
walk the recovery checklist before reinstalling.
|
|
26
|
-
- **one sim for your whole session
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
- **keep one sim for your whole session.** the first `sootsim open <port>`
|
|
27
|
+
launches Chrome for Testing with an isolated profile. later opens reuse the
|
|
28
|
+
saved sim and navigate it in place. use `sootsim do reload` when the target
|
|
29
|
+
has not changed:
|
|
30
30
|
|
|
31
31
|
```sh
|
|
32
|
-
sootsim open 8086
|
|
33
|
-
sootsim open 8090
|
|
34
|
-
sootsim reload
|
|
35
|
-
sootsim close <id>
|
|
32
|
+
sootsim open 8086 # first call creates your isolated sim
|
|
33
|
+
sootsim open 8090 # same sim, different app
|
|
34
|
+
sootsim do reload # same sim, same app
|
|
35
|
+
sootsim close <id> # dispose it when you are done
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- **
|
|
43
|
-
|
|
44
|
-
an unpinned command has no sim of its own, so it falls back to whatever sim is
|
|
45
|
-
currently PRIMARY — and that can be another agent's live sim, mid-run. a plain
|
|
46
|
-
`sootsim open <port>` from an unpinned CLI will happily retarget somebody
|
|
47
|
-
else's sim to your port and take over their session; the warning it prints is
|
|
48
|
-
easy to miss in a transcript. the current-sim pin is stored per CLI identity
|
|
49
|
-
(`sootsim-current-sim-<identity>.json`, keyed by your process-tree root), so
|
|
50
|
-
pinning is genuinely per-agent and does not fight anyone else:
|
|
38
|
+
`--new`, `--profile`, and `--ephemeral` create another isolated browser
|
|
39
|
+
tree. use them only when separate storage or a genuinely concurrent sim is
|
|
40
|
+
required. repeated new trees consume enough memory and CPU to starve other
|
|
41
|
+
work on a shared machine.
|
|
42
|
+
- **pin the sim before driving it.** `sootsim open` records the new sim for the
|
|
43
|
+
current CLI identity. read its id and pin it explicitly before a longer flow:
|
|
51
44
|
|
|
52
45
|
```sh
|
|
53
|
-
sootsim open
|
|
54
|
-
sootsim list
|
|
55
|
-
sootsim use <id>
|
|
56
|
-
sootsim describe --sim <id>
|
|
46
|
+
sootsim open 8086
|
|
47
|
+
sootsim list
|
|
48
|
+
sootsim use <id>
|
|
49
|
+
sootsim describe --sim <id>
|
|
57
50
|
```
|
|
58
51
|
|
|
59
|
-
`--new`, `--profile`, and `--ephemeral` are rejected outright when combined
|
|
60
|
-
with `--sim`, so if a command needs one of them you are starting a second sim
|
|
61
|
-
whether you meant to or not. if yours died, run `sootsim list --all` and adopt
|
|
62
|
-
an open unlocked sim with `sootsim use <id>` before you consider opening one.
|
|
63
|
-
always close yours when you finish.
|
|
64
52
|
- **never retry-loop `sootsim open` against a wedged or slow stack.** a
|
|
65
|
-
connect
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
against a wedged mobile stack drove a load emergency). reuse ONE persistent
|
|
70
|
-
sim tab and reload — never new-tab-per-attempt — and launch GPU chrome
|
|
71
|
-
(`channel:'chrome'`), not the bundled `headless_shell`.
|
|
53
|
+
connect timeout means diagnose the stack. do not retry. if the first launch
|
|
54
|
+
never connects, each retry can create another browser tree before there is a
|
|
55
|
+
saved sim to reuse. verify the development server and SootSim runtime, then
|
|
56
|
+
retry once after fixing the cause.
|
|
72
57
|
|
|
73
58
|
## first move
|
|
74
59
|
|
|
@@ -151,16 +136,11 @@ sootsim close <id> # release it cleanly
|
|
|
151
136
|
Run `sootsim runtime install`, then retry `sootsim open <port>`.
|
|
152
137
|
|
|
153
138
|
**`sootsim open` times out ("timed out waiting for opened sim to connect").**
|
|
154
|
-
do
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
dist that never connects, and a wedged one-dev logging unhandled rejections
|
|
160
|
-
won't boot a fresh sim). if load is high, let it settle before ONE more try. if
|
|
161
|
-
the stack is wedged, restart it cleanly rather than reopening. always launch GPU
|
|
162
|
-
chrome (`channel:'chrome'`), never the bundled `headless_shell` — software
|
|
163
|
-
rendering pegs every core for the canvas's whole lifetime.
|
|
139
|
+
do not retry in a loop. verify the development server first: `__soot/` must
|
|
140
|
+
respond and the engine watchers must be running. inspect the driver diagnostic
|
|
141
|
+
path printed by the failed command. restart the broken development stack at its
|
|
142
|
+
supervisor, then retry once. SootSim owns the isolated browser profile and its
|
|
143
|
+
process tree; do not open the shell URL through an operating-system browser.
|
|
164
144
|
|
|
165
145
|
**bridge disconnects mid-sim.** the WebSocket dropped (laptop slept,
|
|
166
146
|
network blip). re-pin: `sootsim use <id>`. the daemon retries reconnection
|
package/src/bridge-constants.ts
CHANGED
|
@@ -71,6 +71,21 @@ function closestLocalDevBaseHttpPort(httpPort: number): number | null {
|
|
|
71
71
|
return closest?.basePort ?? null
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/** the vite dev shell that shares a world with a bridge port. the two are one
|
|
75
|
+
* offset apart by construction (bridge 7668 + N, shell 5173 + N), so a caller
|
|
76
|
+
* that pinned an explicit bridge port can name its own shell directly. that
|
|
77
|
+
* matters because the dev-bridge lockfile records only the stack that
|
|
78
|
+
* heartbeated last: on a machine running several worktree stacks it routinely
|
|
79
|
+
* names someone else's shell, and a page opened there while we poll our own
|
|
80
|
+
* bridge never connects. null when the port is not a local dev bridge port. */
|
|
81
|
+
export function resolveSootsimShellBaseUrlForBridgePort(
|
|
82
|
+
bridgePort: number,
|
|
83
|
+
): string | null {
|
|
84
|
+
const offset = bridgePort - DEFAULT_SOOTSIM_BRIDGE_PORT
|
|
85
|
+
if (offset < 0 || offset > MAX_LOCAL_DEV_PORT_OFFSET) return null
|
|
86
|
+
return `http://localhost:${SOOTSIM_SHELL_DEV_HTTP_BASE_PORT + offset}/`
|
|
87
|
+
}
|
|
88
|
+
|
|
74
89
|
export function resolveSootsimBridgePortForRuntimeLocation(
|
|
75
90
|
input: {
|
|
76
91
|
explicitPort?: unknown
|
package/src/host/bridge-host.ts
CHANGED
|
@@ -939,22 +939,6 @@ export class SootSimBridgeHost {
|
|
|
939
939
|
return
|
|
940
940
|
}
|
|
941
941
|
|
|
942
|
-
if (msg.type === 'bridge:open') {
|
|
943
|
-
if (typeof msg.url !== 'string' || !msg.url) {
|
|
944
|
-
throw new Error('bridge:open requires a url')
|
|
945
|
-
}
|
|
946
|
-
await this.openUrl(msg.url, { newWindow: msg.newWindow === true })
|
|
947
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
948
|
-
ws.send(
|
|
949
|
-
JSON.stringify({
|
|
950
|
-
id: msg.id,
|
|
951
|
-
result: { ok: true, url: msg.url },
|
|
952
|
-
}),
|
|
953
|
-
)
|
|
954
|
-
}
|
|
955
|
-
return
|
|
956
|
-
}
|
|
957
|
-
|
|
958
942
|
if (msg.type === 'bridge:claim') {
|
|
959
943
|
const targetSim = await this.waitForSim(msg.simId)
|
|
960
944
|
const outcome = this.tryAcquireLease(ws, targetSim, {
|
package/src/host/open-url.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { existsSync } from 'fs'
|
|
3
|
-
import { homedir } from 'os'
|
|
4
|
-
import { join } from 'path'
|
|
1
|
+
import { spawn } from 'child_process'
|
|
5
2
|
|
|
6
|
-
export type OpenUrlLaunchVia = '
|
|
3
|
+
export type OpenUrlLaunchVia = 'system'
|
|
7
4
|
|
|
8
5
|
export interface OpenUrlOptions {
|
|
9
|
-
newWindow?: boolean
|
|
10
6
|
detached?: boolean
|
|
11
7
|
background?: boolean
|
|
12
8
|
}
|
|
13
9
|
|
|
14
10
|
export interface OpenUrlCommandOptions extends OpenUrlOptions {
|
|
15
11
|
platform?: NodeJS.Platform
|
|
16
|
-
chromiumBinary?: string | null
|
|
17
12
|
}
|
|
18
13
|
|
|
19
14
|
export interface OpenUrlCommand {
|
|
@@ -28,97 +23,6 @@ export interface OpenUrlLaunchResult extends OpenUrlCommand {
|
|
|
28
23
|
attachUrl: string
|
|
29
24
|
}
|
|
30
25
|
|
|
31
|
-
const MAC_CHROMIUM_CANDIDATES = [
|
|
32
|
-
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
33
|
-
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
34
|
-
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
35
|
-
'~/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
36
|
-
'/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
|
|
37
|
-
'~/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
|
|
38
|
-
'/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
|
|
39
|
-
'~/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
|
|
40
|
-
'/Applications/Arc.app/Contents/MacOS/Arc',
|
|
41
|
-
'~/Applications/Arc.app/Contents/MacOS/Arc',
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
const LINUX_CHROMIUM_CANDIDATES = [
|
|
45
|
-
'/usr/bin/google-chrome',
|
|
46
|
-
'/usr/bin/chromium',
|
|
47
|
-
'/usr/bin/chromium-browser',
|
|
48
|
-
'/usr/bin/microsoft-edge',
|
|
49
|
-
'/usr/bin/brave-browser',
|
|
50
|
-
'/snap/bin/chromium',
|
|
51
|
-
]
|
|
52
|
-
|
|
53
|
-
const WINDOWS_CHROMIUM_CANDIDATES = [
|
|
54
|
-
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
55
|
-
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
|
|
56
|
-
'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
|
|
57
|
-
'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
|
|
58
|
-
'C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe',
|
|
59
|
-
'C:\\Program Files (x86)\\BraveSoftware\\Brave-Browser\\Application\\brave.exe',
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
const UNIX_CHROMIUM_COMMANDS = [
|
|
63
|
-
'google-chrome',
|
|
64
|
-
'chromium',
|
|
65
|
-
'chromium-browser',
|
|
66
|
-
'microsoft-edge',
|
|
67
|
-
'brave-browser',
|
|
68
|
-
]
|
|
69
|
-
|
|
70
|
-
const WINDOWS_CHROMIUM_COMMANDS = ['chrome', 'msedge', 'brave']
|
|
71
|
-
|
|
72
|
-
function expandHome(candidate: string): string {
|
|
73
|
-
return candidate.startsWith('~/') ? join(homedir(), candidate.slice(2)) : candidate
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function firstExisting(candidates: string[]): string | null {
|
|
77
|
-
for (const candidate of candidates) {
|
|
78
|
-
const expanded = expandHome(candidate)
|
|
79
|
-
if (existsSync(expanded)) return expanded
|
|
80
|
-
}
|
|
81
|
-
return null
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function lookupExecutable(command: string, platform: NodeJS.Platform): string | null {
|
|
85
|
-
try {
|
|
86
|
-
const tool = platform === 'win32' ? 'where' : 'which'
|
|
87
|
-
const out = execFileSync(tool, [command], {
|
|
88
|
-
encoding: 'utf8',
|
|
89
|
-
timeout: 1500,
|
|
90
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
91
|
-
}).trim()
|
|
92
|
-
return out ? out.split(/\r?\n/)[0] : null
|
|
93
|
-
} catch {
|
|
94
|
-
return null
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function resolveChromiumBinary(
|
|
99
|
-
platform: NodeJS.Platform = process.platform,
|
|
100
|
-
): string | null {
|
|
101
|
-
const envCandidate = process.env.CHROME_PATH || process.env.CHROMIUM_PATH
|
|
102
|
-
if (envCandidate && existsSync(envCandidate)) return envCandidate
|
|
103
|
-
|
|
104
|
-
const direct =
|
|
105
|
-
platform === 'darwin'
|
|
106
|
-
? firstExisting(MAC_CHROMIUM_CANDIDATES)
|
|
107
|
-
: platform === 'win32'
|
|
108
|
-
? firstExisting(WINDOWS_CHROMIUM_CANDIDATES)
|
|
109
|
-
: firstExisting(LINUX_CHROMIUM_CANDIDATES)
|
|
110
|
-
if (direct) return direct
|
|
111
|
-
|
|
112
|
-
const commands =
|
|
113
|
-
platform === 'win32' ? WINDOWS_CHROMIUM_COMMANDS : UNIX_CHROMIUM_COMMANDS
|
|
114
|
-
for (const command of commands) {
|
|
115
|
-
const found = lookupExecutable(command, platform)
|
|
116
|
-
if (found) return found
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return null
|
|
120
|
-
}
|
|
121
|
-
|
|
122
26
|
export function buildOpenUrlCommand(
|
|
123
27
|
url: string,
|
|
124
28
|
options: OpenUrlCommandOptions = {},
|
|
@@ -126,10 +30,6 @@ export function buildOpenUrlCommand(
|
|
|
126
30
|
if (!url) throw new Error('openUrl requires a url')
|
|
127
31
|
|
|
128
32
|
const platform = options.platform ?? process.platform
|
|
129
|
-
if (options.newWindow) {
|
|
130
|
-
return buildChromiumUrlCommand(url, { ...options, platform, newWindow: true })
|
|
131
|
-
}
|
|
132
|
-
|
|
133
33
|
if (platform === 'darwin') {
|
|
134
34
|
return {
|
|
135
35
|
command: 'open',
|
|
@@ -153,38 +53,6 @@ export function buildOpenUrlCommand(
|
|
|
153
53
|
}
|
|
154
54
|
}
|
|
155
55
|
|
|
156
|
-
export function buildChromiumUrlCommand(
|
|
157
|
-
url: string,
|
|
158
|
-
options: OpenUrlCommandOptions = {},
|
|
159
|
-
): OpenUrlCommand {
|
|
160
|
-
if (!url) throw new Error('openUrl requires a url')
|
|
161
|
-
|
|
162
|
-
const platform = options.platform ?? process.platform
|
|
163
|
-
const chromiumBinary =
|
|
164
|
-
'chromiumBinary' in options ? options.chromiumBinary : resolveChromiumBinary(platform)
|
|
165
|
-
if (!chromiumBinary) {
|
|
166
|
-
throw new Error('browser launch requires Chrome, Chromium, Edge, Brave, or Arc')
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// the chromium driver always launches a visible system browser window.
|
|
170
|
-
// headless is intentionally NOT supported here: bare `chrome --headless=new
|
|
171
|
-
// <url>` renders the page once and exits — nothing keeps the process (and
|
|
172
|
-
// therefore the sim) alive without an attached CDP client. headless
|
|
173
|
-
// automation is the playwright driver's job. see chromium.ts `launch`.
|
|
174
|
-
const args: string[] = []
|
|
175
|
-
if (options.newWindow !== false) {
|
|
176
|
-
args.push('--new-window')
|
|
177
|
-
}
|
|
178
|
-
args.push(url)
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
command: chromiumBinary,
|
|
182
|
-
args,
|
|
183
|
-
via: 'chromium',
|
|
184
|
-
target: chromiumBinary,
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
56
|
async function spawnLaunch(
|
|
189
57
|
command: string,
|
|
190
58
|
args: string[],
|
|
@@ -216,19 +84,6 @@ export async function launchUrl(
|
|
|
216
84
|
}
|
|
217
85
|
}
|
|
218
86
|
|
|
219
|
-
export async function launchChromiumUrl(
|
|
220
|
-
url: string,
|
|
221
|
-
options: OpenUrlCommandOptions = {},
|
|
222
|
-
): Promise<OpenUrlLaunchResult> {
|
|
223
|
-
const command = buildChromiumUrlCommand(url, options)
|
|
224
|
-
const pid = await spawnLaunch(command.command, command.args, options.detached ?? true)
|
|
225
|
-
return {
|
|
226
|
-
...command,
|
|
227
|
-
pid,
|
|
228
|
-
attachUrl: url,
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
87
|
export async function openUrl(url: string, options: OpenUrlOptions = {}): Promise<void> {
|
|
233
88
|
await launchUrl(url, options)
|
|
234
89
|
}
|
package/src/plist.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import plistModule from '@expo/plist'
|
|
2
|
+
|
|
3
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
4
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function resolvePlistParser(value: unknown): ((source: string) => unknown) | null {
|
|
8
|
+
if (!isRecord(value)) return null
|
|
9
|
+
const direct = value.parse
|
|
10
|
+
if (typeof direct === 'function') {
|
|
11
|
+
return (source) => Reflect.apply(direct, value, [source])
|
|
12
|
+
}
|
|
13
|
+
const nested = value.default
|
|
14
|
+
if (!isRecord(nested)) return null
|
|
15
|
+
const nestedParse = nested.parse
|
|
16
|
+
return typeof nestedParse === 'function'
|
|
17
|
+
? (source) => Reflect.apply(nestedParse, nested, [source])
|
|
18
|
+
: null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// bun consumes @expo/plist's default export directly, while the compiled commonjs
|
|
22
|
+
// scanner receives the package namespace with that export nested under default.
|
|
23
|
+
const parsePlist = resolvePlistParser(plistModule)
|
|
24
|
+
|
|
25
|
+
export function parsePlistSource(source: string): unknown {
|
|
26
|
+
return parsePlist?.(source) ?? null
|
|
27
|
+
}
|
package/src/settings-bundle.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile, readdir, stat } from 'node:fs/promises'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
|
-
import
|
|
3
|
+
import { parsePlistSource } from './plist.ts'
|
|
4
4
|
import type { AppPreferenceValue, AppSettingsSpecifier } from '@sootsim/globals'
|
|
5
5
|
|
|
6
6
|
const MAX_PLIST_BYTES = 1_048_576
|
|
@@ -26,26 +26,6 @@ function isRecord(value: unknown): value is PlistRecord {
|
|
|
26
26
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function resolvePlistParser(value: unknown): ((source: string) => unknown) | null {
|
|
30
|
-
if (!isRecord(value)) return null
|
|
31
|
-
const direct = value.parse
|
|
32
|
-
if (typeof direct === 'function') {
|
|
33
|
-
return (source) => Reflect.apply(direct, value, [source])
|
|
34
|
-
}
|
|
35
|
-
const nested = value.default
|
|
36
|
-
if (!isRecord(nested)) return null
|
|
37
|
-
const nestedParse = nested.parse
|
|
38
|
-
return typeof nestedParse === 'function'
|
|
39
|
-
? (source) => Reflect.apply(nestedParse, nested, [source])
|
|
40
|
-
: null
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// bun consumes @expo/plist's default export directly, while the compiled commonjs
|
|
44
|
-
// scanner receives the package namespace with that export nested under
|
|
45
|
-
// `default`. resolve the parser once at this package boundary so source and
|
|
46
|
-
// published scanners execute the same implementation.
|
|
47
|
-
const parsePlist = resolvePlistParser(plistModule)
|
|
48
|
-
|
|
49
29
|
function optionalString(value: unknown): string | undefined {
|
|
50
30
|
return typeof value === 'string' ? value : undefined
|
|
51
31
|
}
|
|
@@ -196,11 +176,10 @@ async function settingsBundleFingerprint(bundlePath: string): Promise<string | n
|
|
|
196
176
|
|
|
197
177
|
async function parsePlistRecord(path: string): Promise<PlistRecord | null> {
|
|
198
178
|
try {
|
|
199
|
-
if (!parsePlist) return null
|
|
200
179
|
const metadata = await stat(path)
|
|
201
180
|
if (!metadata.isFile() || metadata.size > MAX_PLIST_BYTES) return null
|
|
202
181
|
const source = await readFile(path, 'utf8')
|
|
203
|
-
const parsed =
|
|
182
|
+
const parsed = parsePlistSource(source)
|
|
204
183
|
return isRecord(parsed) ? parsed : null
|
|
205
184
|
} catch {
|
|
206
185
|
return null
|
|
@@ -31,6 +31,7 @@ export const skill: SootSimSkill = {
|
|
|
31
31
|
const { DEFAULT_SOOTSIM_BRIDGE_PORT } = await import('../../bridge-constants')
|
|
32
32
|
const { SootSimBridgeHost } = await import('../../host/bridge-host')
|
|
33
33
|
const { createBridge } = await import('../../../cli/ws-bridge')
|
|
34
|
+
const { runOpenCommand } = await import('../../../cli/commands/control')
|
|
34
35
|
const { SootSimBridgeFlowRunner } =
|
|
35
36
|
await import('../../../cli/bridge-flow-runner')
|
|
36
37
|
|
|
@@ -44,7 +45,9 @@ export const skill: SootSimSkill = {
|
|
|
44
45
|
const steps = yaml.parse(stepsBody)
|
|
45
46
|
const beforeIds = new Set(host.listSims().map((sim) => sim.id))
|
|
46
47
|
|
|
47
|
-
await
|
|
48
|
+
await runOpenCommand([context.url, '--new', '--headless', '--no-describe'], {
|
|
49
|
+
port: DEFAULT_SOOTSIM_BRIDGE_PORT,
|
|
50
|
+
})
|
|
48
51
|
|
|
49
52
|
let simId: string | null = null
|
|
50
53
|
for (let i = 0; i < 60; i++) {
|
package/src/vite-plugin.ts
CHANGED
|
@@ -54,6 +54,7 @@ const compatRoot = path.resolve(sootsimRoot, '..', 'compat')
|
|
|
54
54
|
// renames of either package don't break things.
|
|
55
55
|
const engineRoot = path.resolve(workspaceRoot, 'packages/sootsim-engine')
|
|
56
56
|
const rnShimPath = path.resolve(engineRoot, 'src/react-native/index.ts')
|
|
57
|
+
const rnWebShimPath = path.resolve(engineRoot, 'src/react-native-web.ts')
|
|
57
58
|
const reactBridgePath = path.resolve(compatRoot, 'src/react-bridge.ts')
|
|
58
59
|
const sootsimBrowserSourceAliases = [
|
|
59
60
|
{
|
|
@@ -1007,7 +1008,7 @@ function sootsimConfigPlugin(
|
|
|
1007
1008
|
})),
|
|
1008
1009
|
{ find: /^react-native\/Libraries\/.*/, replacement: rnDeepStubDefault },
|
|
1009
1010
|
{ find: 'react-native', replacement: rnShimPath },
|
|
1010
|
-
{ find: 'react-native-web', replacement:
|
|
1011
|
+
{ find: 'react-native-web', replacement: rnWebShimPath },
|
|
1011
1012
|
// resolved-path alias for the synthetic specifier the
|
|
1012
1013
|
// @react-navigation/native compat wrapper imports from. terminates
|
|
1013
1014
|
// the wrapper's own real-package import without re-entering the
|