peachsim 0.1.527 → 0.1.570
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 +26 -26
- package/cli/app-fonts.ts +1 -1
- package/cli/app-splash.ts +1 -1
- package/cli/bin.ts +1 -1
- package/cli/bridge-diagnostics.ts +1 -1
- package/cli/bridge-flow-runner.ts +57 -5
- package/cli/cloud-client.ts +273 -6
- package/cli/cloud-dispatch.ts +22 -2
- package/cli/cloud-session.ts +60 -4
- package/cli/cloud-test-tunnel.ts +354 -0
- package/cli/commands/box/bundle-plane.ts +2 -2
- package/cli/commands/box/checkout-plane.ts +1 -1
- package/cli/commands/box/rnx-command.ts +2 -2
- package/cli/commands/box/search-command.ts +2 -2
- package/cli/commands/box.ts +8 -8
- package/cli/commands/compat.ts +1 -1
- package/cli/commands/control.ts +22 -3
- package/cli/commands/detox.ts +1 -1
- package/cli/commands/device.ts +1 -1
- package/cli/commands/diagnose.ts +1 -1
- package/cli/commands/film.ts +1 -1
- package/cli/commands/flow.ts +60 -3
- package/cli/commands/inspect/actions.ts +1 -1
- package/cli/commands/inspect/core.ts +6 -7
- package/cli/commands/inspect/find.ts +2 -1
- package/cli/commands/inspect/resolve-target.ts +5 -3
- package/cli/commands/inspect/shared.ts +1 -1
- package/cli/commands/inspect/wait-event.ts +1 -1
- package/cli/commands/inspect/wait-selector.ts +15 -4
- package/cli/commands/inspect.ts +19 -14
- package/cli/commands/maestro.ts +464 -43
- package/cli/commands/permissions.ts +1 -1
- package/cli/commands/platform.ts +205 -8
- package/cli/commands/preview.ts +2 -2
- package/cli/commands/record.ts +2 -2
- package/cli/commands/report-issue.ts +2 -2
- package/cli/commands/screenshot-layers.ts +1 -1
- package/cli/commands/screenshot.ts +1 -1
- package/cli/commands/setup.ts +2 -2
- package/cli/commands/test.ts +440 -115
- package/cli/commands/three-mode.ts +1 -1
- package/cli/commands/upload.ts +5 -5
- package/cli/commands/what-happened.ts +1 -1
- package/cli/desktop-companion.ts +3 -3
- package/cli/drivers/playwright-sim-host.ts +2 -2
- package/cli/flow-file.ts +7 -0
- package/cli/flow-live-status.ts +65 -8
- package/cli/help-core.ts +3 -3
- package/cli/hidden-runtime-alias.ts +1 -1
- package/cli/maestro-report.ts +178 -0
- package/cli/main.ts +1 -1
- package/cli/outbound-endpoints.ts +185 -136
- package/cli/parse-args.ts +1 -1
- package/cli/registry.ts +1 -1
- package/cli/resolve-assets.ts +1 -1
- package/cli/run-registry.ts +8 -0
- package/cli/send-to-box.ts +4 -4
- package/cli/ws-bridge.ts +3 -5
- package/detox/expectations.ts +1 -1
- package/detox/gestures.ts +2 -2
- package/detox/index.ts +71 -29
- package/detox/jest-preset.cjs +3 -3
- package/detox/jest-setup-after-env.cjs +1 -1
- package/dist-lib/agent-daemon-client.cjs +2 -2
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +2 -2
- package/dist-lib/attached-projects.cjs +2 -2
- 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/capture-contract.cjs +94 -3
- package/dist-lib/capture-contract.mjs +93 -3
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud.cjs +2 -2
- package/dist-lib/cloud.mjs +2 -2
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +106 -25
- package/dist-lib/detox/jest-preset.cjs +3 -3
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +137 -61
- package/dist-lib/host/fetch-proxy-handler.cjs +28 -2
- package/dist-lib/host/fetch-proxy-overrides.cjs +33 -3
- package/dist-lib/host/fetch-proxy-overrides.mjs +30 -2
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +209 -16
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/menu.cjs +2 -2
- package/dist-lib/menu.mjs +2 -2
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +3 -3
- package/dist-lib/metro-production-bundle.mjs +3 -3
- package/dist-lib/metro.cjs +8 -8
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +2 -2
- 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 +31 -15
- package/dist-lib/sdk.cjs +3 -2
- package/dist-lib/sdk.mjs +3 -2
- package/dist-lib/skills.cjs +362 -163
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +583 -16
- package/dist-lib/vite.cjs +29 -14
- package/package.json +9 -9
- package/scripts/dev-server-scanner.ts +41 -14
- package/scripts/optional-demo-registry.ts +3 -3
- package/skills/contrast/SKILL.md +9 -9
- package/skills/rnx-debug/SKILL.md +14 -4
- package/skills/rnx-perf/SKILL.md +5 -5
- package/skills/rnx-setup/SKILL.md +11 -11
- package/skills/rnx-test/SKILL.md +11 -11
- package/skills/rnx-visual/SKILL.md +6 -6
- package/src/agent-sessions.ts +3 -3
- package/src/attached-projects.ts +1 -1
- package/src/bridge-contract-input.ts +1 -1
- package/src/bridge-contract.ts +23 -5
- package/src/capture-contract.ts +145 -3
- package/src/config.ts +2 -2
- package/src/connect.ts +2 -2
- package/src/home-paths.ts +3 -3
- package/src/host/bridge-host.ts +2 -2
- package/src/host/fetch-proxy-handler.ts +4 -3
- package/src/host/fetch-proxy-overrides.ts +41 -0
- package/src/host/plane-host.ts +2 -2
- package/src/index.ts +3 -0
- package/src/metro-plugin.ts +4 -4
- package/src/metro-production-bundle.ts +2 -2
- package/src/native-seam-manifest.ts +6 -6
- package/src/public-brand.ts +1 -1
- package/src/rolldown-plugin-swift.ts +8 -5
- package/src/runtime-assets.ts +8 -6
- package/src/screenshots/frame-compose.ts +24 -56
- package/src/screenshots/org-deck.ts +1 -1
- package/src/screenshots/registry.ts +4 -4
- package/src/screenshots/schema.ts +1 -1
- package/src/sdk.ts +2 -2
- package/src/settings-bundle.ts +1 -1
- package/src/sim-client.ts +2 -4
- package/src/skills/builtin/compat-check.ts +1 -1
- package/src/vite-plugin-one.ts +1 -1
- package/src/vite-plugin-swift.ts +11 -6
- package/src/vite-plugin.ts +30 -17
- package/src/worklets-babel.ts +2 -2
package/dist-lib/vite.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.570 | (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;
|
|
@@ -622,7 +622,7 @@ function findMonorepoRoot() {
|
|
|
622
622
|
if (_monorepoRootCache !== void 0) return _monorepoRootCache;
|
|
623
623
|
let dir = import_path.default.dirname((0, import_url.fileURLToPath)(__sootsim_import_meta_url));
|
|
624
624
|
for (let i = 0; i < 12 && dir !== import_path.default.dirname(dir); i++) {
|
|
625
|
-
if (import_fs.default.existsSync(import_path.default.join(dir, "packages/
|
|
625
|
+
if (import_fs.default.existsSync(import_path.default.join(dir, "packages/peach-engine/package.json")) && import_fs.default.existsSync(import_path.default.join(dir, "public/sootsim"))) {
|
|
626
626
|
_monorepoRootCache = dir;
|
|
627
627
|
return dir;
|
|
628
628
|
}
|
|
@@ -636,7 +636,7 @@ function resolveDevCheckoutRuntimeRoot() {
|
|
|
636
636
|
const root = findMonorepoRoot();
|
|
637
637
|
if (!root) return null;
|
|
638
638
|
const devBuild = import_path.default.join(root, "public/sootsim");
|
|
639
|
-
if (import_fs.default.existsSync(import_path.default.join(devBuild, "index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine/index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine-tenant/worker.js"))) {
|
|
639
|
+
if (import_fs.default.existsSync(import_path.default.join(devBuild, "index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine/index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine-tenant/worker.js")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine-tenant/worker.prod.js"))) {
|
|
640
640
|
return devBuild;
|
|
641
641
|
}
|
|
642
642
|
return null;
|
|
@@ -851,6 +851,15 @@ function enumerateBundleJson(paths) {
|
|
|
851
851
|
return found;
|
|
852
852
|
}
|
|
853
853
|
|
|
854
|
+
// ../sootsim-swift/src/entryModule.ts
|
|
855
|
+
function swiftEntryModuleEsm(args) {
|
|
856
|
+
return [
|
|
857
|
+
`import { createSwiftEntry } from 'peachsim/swift'`,
|
|
858
|
+
`export default createSwiftEntry({ url: ${JSON.stringify(args.url)}, hash: ${JSON.stringify(args.hash)}, rootId: ${JSON.stringify(args.rootId)}, resources: ${JSON.stringify(args.resources)}, bundleFiles: ${JSON.stringify(args.bundleFiles)} })`,
|
|
859
|
+
""
|
|
860
|
+
].join("\n");
|
|
861
|
+
}
|
|
862
|
+
|
|
854
863
|
// ../sootsim-swift/toolchain.json
|
|
855
864
|
var toolchain_default = {
|
|
856
865
|
release: "swift-6.3-RELEASE",
|
|
@@ -1174,7 +1183,9 @@ function swiftPlugin(options = {}) {
|
|
|
1174
1183
|
try {
|
|
1175
1184
|
const built = buildSwiftResourceTable(project, serverOrigin());
|
|
1176
1185
|
resources = built.table;
|
|
1177
|
-
for (const watched of built.watched)
|
|
1186
|
+
for (const watched of built.watched) {
|
|
1187
|
+
this.addWatchFile(import_node_path4.default.join(packagePath, ...watched.split(import_node_path4.default.posix.sep)));
|
|
1188
|
+
}
|
|
1178
1189
|
} catch (error) {
|
|
1179
1190
|
this.warn(
|
|
1180
1191
|
`swift resources for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -1197,11 +1208,13 @@ function swiftPlugin(options = {}) {
|
|
|
1197
1208
|
`swift bundle files for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
1198
1209
|
);
|
|
1199
1210
|
}
|
|
1200
|
-
return
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1211
|
+
return swiftEntryModuleEsm({
|
|
1212
|
+
url: artifactUrl(project, artifact),
|
|
1213
|
+
hash: artifact.hash,
|
|
1214
|
+
rootId: entryRootId(packagePath, file),
|
|
1215
|
+
resources,
|
|
1216
|
+
bundleFiles
|
|
1217
|
+
});
|
|
1205
1218
|
},
|
|
1206
1219
|
configureServer(server) {
|
|
1207
1220
|
devServer = server;
|
|
@@ -1430,11 +1443,13 @@ function swiftRemotePlugin(options) {
|
|
|
1430
1443
|
this.warn(error instanceof Error ? error.message : String(error));
|
|
1431
1444
|
}
|
|
1432
1445
|
const address = project.address;
|
|
1433
|
-
return
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1446
|
+
return swiftEntryModuleEsm({
|
|
1447
|
+
url: address.url,
|
|
1448
|
+
hash: address.hash,
|
|
1449
|
+
rootId: entryRootId(packagePath, file),
|
|
1450
|
+
resources: address.imageUrls ?? {},
|
|
1451
|
+
bundleFiles: pack.resources
|
|
1452
|
+
});
|
|
1438
1453
|
}
|
|
1439
1454
|
};
|
|
1440
1455
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peachsim",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Vite and Metro plugins, testing drivers, and SDK exports for
|
|
3
|
+
"version": "0.1.570",
|
|
4
|
+
"description": "Vite and Metro plugins, testing drivers, and SDK exports for peach.",
|
|
5
5
|
"author": "Tamagui LLC",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/sootbean/soot.git",
|
|
10
|
-
"directory": "packages/
|
|
10
|
+
"directory": "packages/peachsim"
|
|
11
11
|
},
|
|
12
12
|
"type": "module",
|
|
13
13
|
"publishConfig": {
|
|
@@ -275,14 +275,14 @@
|
|
|
275
275
|
},
|
|
276
276
|
"devDependencies": {
|
|
277
277
|
"@contrast/runtime-delivery": "workspace:*",
|
|
278
|
-
"@
|
|
279
|
-
"@
|
|
280
|
-
"@
|
|
281
|
-
"@
|
|
278
|
+
"@peach/box": "workspace:*",
|
|
279
|
+
"@peach/globals": "workspace:*",
|
|
280
|
+
"@peach/skills": "workspace:*",
|
|
281
|
+
"@peach/compat": "workspace:*",
|
|
282
282
|
"@types/ws": "^8.5.13",
|
|
283
283
|
"contrast-kit": "workspace:*",
|
|
284
|
-
"
|
|
285
|
-
"
|
|
284
|
+
"peach-cloud-box": "workspace:*",
|
|
285
|
+
"peach-engine": "workspace:*",
|
|
286
286
|
"typescript": "7.0.2",
|
|
287
287
|
"vite": "^8.2.2",
|
|
288
288
|
"yaml": "^2.8.3"
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
readAppSettingsBundle,
|
|
36
36
|
} from '../src/settings-bundle.ts'
|
|
37
37
|
import { loadOptionalDemoApps, optionalDemoAppsSync } from './optional-demo-registry.ts'
|
|
38
|
-
import type { AppSettingsSpecifier } from '@
|
|
38
|
+
import type { AppSettingsSpecifier } from '@peach/globals'
|
|
39
39
|
|
|
40
40
|
// how long past a command's own budget we wait before deciding its exit event is
|
|
41
41
|
// never coming. generous, because this only fires when the child is already
|
|
@@ -629,7 +629,7 @@ async function resolveLocalIconPath(
|
|
|
629
629
|
localIconPathCache.set(port, candidate)
|
|
630
630
|
// the identified result caches as a strong entry and won't re-probe on
|
|
631
631
|
// its own; drop it so the next scan republishes with the working url.
|
|
632
|
-
|
|
632
|
+
invalidatePortCache(port)
|
|
633
633
|
return
|
|
634
634
|
}
|
|
635
635
|
}
|
|
@@ -828,7 +828,7 @@ async function fetchManifestEnrichment(
|
|
|
828
828
|
})
|
|
829
829
|
// the nameless result cached as a strong entry and won't re-probe on its own;
|
|
830
830
|
// drop it so the next scan re-runs probePort and publishes the enriched name.
|
|
831
|
-
|
|
831
|
+
invalidatePortCache(result.port)
|
|
832
832
|
}
|
|
833
833
|
|
|
834
834
|
// ports confirmed to be running a non-sootsim dev server — safe to skip the
|
|
@@ -959,16 +959,22 @@ export async function probePort(
|
|
|
959
959
|
)
|
|
960
960
|
? 'one'
|
|
961
961
|
: 'expo'
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
962
|
+
// enrichWithManifest kicks the local-icon shape probe; without it the
|
|
963
|
+
// optimistic metro icon URL publishes forever and the confirmed shape
|
|
964
|
+
// never lands. every other identified return wraps it too.
|
|
965
|
+
return enrichWithManifest(
|
|
966
|
+
applyManifest(
|
|
967
|
+
{
|
|
968
|
+
port,
|
|
969
|
+
framework,
|
|
970
|
+
bundleUrl: withRuntimeConfig(port, launchUrl, owner.cwd),
|
|
971
|
+
hmrUrl: `${buildLoopbackProbeBaseUrl('ws:', probeHostname, port)}/hot`,
|
|
972
|
+
lastSeen: Date.now(),
|
|
973
|
+
...owner,
|
|
974
|
+
},
|
|
975
|
+
manifestRes,
|
|
976
|
+
buildIconProxyUrl,
|
|
977
|
+
),
|
|
972
978
|
buildIconProxyUrl,
|
|
973
979
|
)
|
|
974
980
|
}
|
|
@@ -1123,6 +1129,17 @@ interface PortCacheEntry {
|
|
|
1123
1129
|
}
|
|
1124
1130
|
const portCache = new Map<number, PortCacheEntry>()
|
|
1125
1131
|
|
|
1132
|
+
// out-of-band completions (the icon-shape probe, manifest enrichment) land
|
|
1133
|
+
// while a scan's own probeAll is still in flight, and the scan's cache write
|
|
1134
|
+
// would resurrect the entry they just dropped. stamp every such invalidation
|
|
1135
|
+
// so a scan only caches ports whose verdicts are newer than its own start.
|
|
1136
|
+
const portCacheInvalidatedAt = new Map<number, number>()
|
|
1137
|
+
|
|
1138
|
+
function invalidatePortCache(port: number): void {
|
|
1139
|
+
portCache.delete(port)
|
|
1140
|
+
portCacheInvalidatedAt.set(port, Date.now())
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1126
1143
|
// negative-cache holds a node/bun port that isn't a bundler (e.g. Contrast's own
|
|
1127
1144
|
// api server, Zero, SQLite sync host, a dev tool). at scan interval 3s and a 1.5s ttl
|
|
1128
1145
|
// we used to re-probe every scan, which hits `/` on the port every 3s — that
|
|
@@ -1166,6 +1183,7 @@ export function __resetScannerCache() {
|
|
|
1166
1183
|
knownOne.clear()
|
|
1167
1184
|
enrichmentCache.clear()
|
|
1168
1185
|
enrichmentInFlight.clear()
|
|
1186
|
+
portCacheInvalidatedAt.clear()
|
|
1169
1187
|
__resetAppSettingsBundleCacheForTests()
|
|
1170
1188
|
}
|
|
1171
1189
|
|
|
@@ -1223,6 +1241,9 @@ export async function scanDevServers(
|
|
|
1223
1241
|
for (const p of [...localIconPathCache.keys()]) {
|
|
1224
1242
|
if (!currentPorts.has(p)) localIconPathCache.delete(p)
|
|
1225
1243
|
}
|
|
1244
|
+
for (const p of [...portCacheInvalidatedAt.keys()]) {
|
|
1245
|
+
if (!currentPorts.has(p)) portCacheInvalidatedAt.delete(p)
|
|
1246
|
+
}
|
|
1226
1247
|
|
|
1227
1248
|
const results: DiscoveredServer[] = []
|
|
1228
1249
|
const toProbe: ListeningProcess[] = []
|
|
@@ -1251,6 +1272,7 @@ export async function scanDevServers(
|
|
|
1251
1272
|
}
|
|
1252
1273
|
|
|
1253
1274
|
if (toProbe.length > 0) {
|
|
1275
|
+
const scanStartedAt = Date.now()
|
|
1254
1276
|
const probed = await Promise.all(
|
|
1255
1277
|
toProbe.map((p) => probePort(p.port, opts.buildIconProxyUrl, p)),
|
|
1256
1278
|
)
|
|
@@ -1258,7 +1280,12 @@ export async function scanDevServers(
|
|
|
1258
1280
|
const { port, pid } = toProbe[i]
|
|
1259
1281
|
// only cache when we have a real pid to key the entry against.
|
|
1260
1282
|
// fallback-list ports (pid 0) always reprobe so we catch state changes.
|
|
1261
|
-
|
|
1283
|
+
// skip ports an out-of-band completion invalidated mid-scan, so the
|
|
1284
|
+
// next scan re-probes instead of re-caching this scan's stale verdict.
|
|
1285
|
+
const invalidatedAt = portCacheInvalidatedAt.get(port) ?? 0
|
|
1286
|
+
if (pid !== 0 && invalidatedAt < scanStartedAt) {
|
|
1287
|
+
portCache.set(port, { pid, result, cachedAt: Date.now() })
|
|
1288
|
+
}
|
|
1262
1289
|
if (result) results.push(result)
|
|
1263
1290
|
})
|
|
1264
1291
|
}
|
|
@@ -20,8 +20,8 @@ import { basename, dirname, join } from 'node:path'
|
|
|
20
20
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
21
21
|
|
|
22
22
|
// this module is emitted at three different depths — the source at
|
|
23
|
-
// packages/
|
|
24
|
-
// and the electron main bundle at packages/
|
|
23
|
+
// packages/peachsim/scripts/, the built CLI at packages/peachsim/dist-cli/chunks/,
|
|
24
|
+
// and the electron main bundle at packages/peach-engine/dist-electron/ — so a
|
|
25
25
|
// relative './demo-app-registry.ts' is only correct from the source copy. from
|
|
26
26
|
// the other two it points at a file that does not exist, the catch below
|
|
27
27
|
// swallows that, and every demo app then boots with no runtimeConfig at all
|
|
@@ -29,7 +29,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
|
29
29
|
// never reaches it). the one description that holds from every emit site is the
|
|
30
30
|
// ancestor that holds the registry at its repo path, which in-repo is the
|
|
31
31
|
// monorepo root.
|
|
32
|
-
const REGISTRY_REPO_PATH = 'packages/
|
|
32
|
+
const REGISTRY_REPO_PATH = 'packages/peachsim/scripts/demo-app-registry.ts'
|
|
33
33
|
|
|
34
34
|
// the walk must never leave the installed package. `sootsim` publishes this
|
|
35
35
|
// loader's source, so on a published install it runs from
|
package/skills/contrast/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: contrast
|
|
3
3
|
description: >-
|
|
4
|
-
index of all Contrast and
|
|
4
|
+
index of all Contrast and Peach agent skills and CLI commands.
|
|
5
5
|
auto-generated from the CLI info registry — do not edit by hand.
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -12,7 +12,7 @@ overview of all available skills and CLI commands in this repo.
|
|
|
12
12
|
> this file is auto-generated by `bun scripts/generate-contrast-skill.ts`.
|
|
13
13
|
> to update, edit the source registries and re-run the generator.
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Peach CLI commands
|
|
16
16
|
|
|
17
17
|
the `rnx` CLI is the operational surface for booting shells, driving
|
|
18
18
|
automation, inspecting the render tree, and managing sims.
|
|
@@ -87,17 +87,17 @@ run `rnx --help` to see the full list under each group.
|
|
|
87
87
|
- `-t, --theme <scheme>` — override light, dark, or auto color scheme
|
|
88
88
|
- `--headless` — run browser automation without a visible window where supported
|
|
89
89
|
|
|
90
|
-
## bundled
|
|
90
|
+
## bundled Peach agent skills
|
|
91
91
|
|
|
92
|
-
skills for working with the
|
|
92
|
+
skills for working with the Peach native preview engine:
|
|
93
93
|
|
|
94
94
|
| skill | description |
|
|
95
95
|
| --- | --- |
|
|
96
|
-
| `/rnx-debug` | Debug rendering, animation, gesture, performance, and accessibility issues in a React Native app running under
|
|
97
|
-
| `/rnx-perf` | Optimize
|
|
98
|
-
| `/rnx-setup` | Get
|
|
99
|
-
| `/rnx-test` | Run automated tests against your React Native app under
|
|
100
|
-
| `/rnx-visual` | Pixel-level rendering comparison between
|
|
96
|
+
| `/rnx-debug` | Debug rendering, animation, gesture, performance, and accessibility issues in a React Native app running under Peach. Reach for this whenever an animation looks wrong (janky, stutters, snaps, only updates once, or doesn't move at all), a swipe/transition/gesture misbehaves or feels broken, scrolling drops frames or feels slow, the UI renders the wrong tree/props, or a screen reader can't reach something — and drive the rnx CLI debug tooling first, not screenshots or ad-hoc logging. |
|
|
97
|
+
| `/rnx-perf` | Optimize Peach engine and app performance — scroll jank, dropped frames, slow animation, idle CPU, memory growth, and "smooth normally but laggy under load" (video calls, busy machines). Covers measuring on the right thread with the right browser, proving each perf tier actually engages via its counters, a CPU-contention test harness, and the map of existing perf machinery so you improve it instead of reinventing it. For general app debugging load /rnx-debug first. |
|
|
98
|
+
| `/rnx-setup` | Get Peach connected to your React Native app with no project setup |
|
|
99
|
+
| `/rnx-test` | Run automated tests against your React Native app under Peach with Maestro YAML or Detox/Jest |
|
|
100
|
+
| `/rnx-visual` | Pixel-level rendering comparison between Peach and native iOS — fonts, gradients, shadows, image fidelity |
|
|
101
101
|
|
|
102
102
|
## automatic settling
|
|
103
103
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rnx-debug
|
|
3
|
-
description: Debug rendering, animation, gesture, performance, and accessibility issues in a React Native app running under
|
|
3
|
+
description: Debug rendering, animation, gesture, performance, and accessibility issues in a React Native app running under Peach. Reach for this whenever an animation looks wrong (janky, stutters, snaps, only updates once, or doesn't move at all), a swipe/transition/gesture misbehaves or feels broken, scrolling drops frames or feels slow, the UI renders the wrong tree/props, or a screen reader can't reach something — and drive the rnx CLI debug tooling first, not screenshots or ad-hoc logging.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Peach debug
|
|
7
7
|
|
|
8
|
-
the foundational skill for "something is wrong with my app in
|
|
8
|
+
the foundational skill for "something is wrong with my app in Peach".
|
|
9
9
|
covers debugging, performance, and accessibility — three branches that
|
|
10
10
|
share the same CLI primitives, the same auto-settling contract, and the
|
|
11
11
|
same diff/snapshot workflow. start here for any rendering, interaction,
|
|
@@ -49,7 +49,7 @@ regression test. a pile of ad-hoc logs helps no one and gets deleted.
|
|
|
49
49
|
|
|
50
50
|
## anti-patterns (apply to every branch)
|
|
51
51
|
|
|
52
|
-
- **`a1`/`a2`/`b0` style ids mean
|
|
52
|
+
- **`a1`/`a2`/`b0` style ids mean Peach sims, never browser tabs.** when a user
|
|
53
53
|
says "look at a3", that's the `rnx list` sim id. do not open chrome
|
|
54
54
|
MCP unless the user explicitly says "use chrome" — the canvas has no
|
|
55
55
|
DOM to inspect anyway.
|
|
@@ -76,6 +76,16 @@ regression test. a pile of ad-hoc logs helps no one and gets deleted.
|
|
|
76
76
|
them*. a "fixed" behavior with a new console error is not fixed.
|
|
77
77
|
- **"found a testID" is not done.** done is: reliable repro + identified
|
|
78
78
|
root cause (specific node + prop, or event + handler) + verified fix.
|
|
79
|
+
- **an empty `find --pressable` is not proof there is no affordance.** it
|
|
80
|
+
lists registered touch responders, and a tamagui `styled()` view whose
|
|
81
|
+
`onPress` comes from the call site is tappable without ever appearing there.
|
|
82
|
+
before concluding a screen has no way out, corroborate with `get a11y` or an
|
|
83
|
+
actual `do tap-text`.
|
|
84
|
+
- **after an unexplained restart, re-establish which sim you are on.** a sim
|
|
85
|
+
that reconnected, or a dev stack that came back on a port another process now
|
|
86
|
+
holds, is a fresh instrument; a port answering 200 says something is
|
|
87
|
+
listening, not that it is yours. every Peach command prints its own `→ <sim>`
|
|
88
|
+
line, so reading that line is the check.
|
|
79
89
|
|
|
80
90
|
## the timeline ("what happened")
|
|
81
91
|
|
package/skills/rnx-perf/SKILL.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rnx-perf
|
|
3
|
-
description: Optimize
|
|
3
|
+
description: Optimize Peach engine and app performance — scroll jank, dropped frames, slow animation, idle CPU, memory growth, and "smooth normally but laggy under load" (video calls, busy machines). Covers measuring on the right thread with the right browser, proving each perf tier actually engages via its counters, a CPU-contention test harness, and the map of existing perf machinery so you improve it instead of reinventing it. For general app debugging load /rnx-debug first.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Peach performance
|
|
7
7
|
|
|
8
|
-
optimize performance in
|
|
8
|
+
optimize performance in Peach: the shell frame pipeline, the compositor
|
|
9
9
|
tiers, and the guest app. this skill exists because every real perf win in
|
|
10
10
|
this engine followed the same loop — measure on the right thread, read the
|
|
11
11
|
counters that prove machinery engaged, fix the one thing the counters
|
|
@@ -384,7 +384,7 @@ tier fire on feeds came entirely from rejection counters.
|
|
|
384
384
|
- `Animated.Value` on `width`/`height` forces layout per frame — use
|
|
385
385
|
`transform` (scale/translate).
|
|
386
386
|
- per-frame React work from reanimated-style code is an engine conformance
|
|
387
|
-
bug (valid worklet apps must not commit per frame) — fix
|
|
387
|
+
bug (valid worklet apps must not commit per frame) — fix Peach core,
|
|
388
388
|
never add a divergent fast-path (upstream alignment overrides perf; see
|
|
389
389
|
the `shouldFreezeOffscreenStackContent` incident).
|
|
390
390
|
- render-storm loops (poll → remount) show up as commit-rate repaints and
|
|
@@ -396,4 +396,4 @@ done means: a named hot path + the counter or split that indicts it + the
|
|
|
396
396
|
same capture re-run showing the number moved + no new errors (`get errors`)
|
|
397
397
|
+ nothing torn down left running (close sims you opened, kill load loops).
|
|
398
398
|
for engine changes, run the affected integration tests exposed by the installed
|
|
399
|
-
|
|
399
|
+
Peach version and capture stable screenshots of surfaces you touched.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rnx-setup
|
|
3
|
-
description: Get
|
|
3
|
+
description: Get Peach connected to your React Native app with no project setup
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Peach setup
|
|
7
7
|
|
|
8
|
-
you want to drive a React Native app with
|
|
8
|
+
you want to drive a React Native app with Peach from the CLI. this skill
|
|
9
9
|
takes you from a running development server to a connected, pinned sim that
|
|
10
|
-
every other
|
|
10
|
+
every other Peach skill assumes exists. Peach requires no project setup or per-app
|
|
11
11
|
install. if
|
|
12
12
|
`rnx describe` already
|
|
13
13
|
returns a tree, you're past this skill — load `/rnx-debug` for
|
|
@@ -21,7 +21,7 @@ debugging or `/rnx-test` for writing tests.
|
|
|
21
21
|
- **don't switch sims mid-investigation.** pin once with `rnx use
|
|
22
22
|
<id>` and keep it pinned. an unpinned command refuses to choose when more
|
|
23
23
|
than one driveable sim is live.
|
|
24
|
-
- **an empty `rnx list` is not "broken
|
|
24
|
+
- **an empty `rnx list` is not "broken Peach".** it usually means the
|
|
25
25
|
daemon is down, the dev server port is wrong, or the runtime is missing.
|
|
26
26
|
walk the recovery checklist before reinstalling.
|
|
27
27
|
- **keep one sim for your whole session.** the first `rnx open <port>`
|
|
@@ -53,7 +53,7 @@ debugging or `/rnx-test` for writing tests.
|
|
|
53
53
|
- **never retry-loop `rnx open` against a wedged or slow stack.** a
|
|
54
54
|
connect timeout means diagnose the stack. do not retry. if the first launch
|
|
55
55
|
never connects, each retry can create another browser tree before there is a
|
|
56
|
-
saved sim to reuse. verify the development server and
|
|
56
|
+
saved sim to reuse. verify the development server and Peach runtime, then
|
|
57
57
|
retry once after fixing the cause.
|
|
58
58
|
|
|
59
59
|
## first move
|
|
@@ -69,7 +69,7 @@ rnx describe # smoke test — should print a render tree
|
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
the background service keeps the local bridge and runtime ready between
|
|
72
|
-
commands. without it,
|
|
72
|
+
commands. without it, Peach still works by starting an on-demand bridge, but
|
|
73
73
|
inspect, interaction, and test commands pay repeated startup cost. if the user
|
|
74
74
|
declined it during onboarding, `rnx daemon install` is the direct way to enable
|
|
75
75
|
it. CI does not need or install the service.
|
|
@@ -100,8 +100,8 @@ rnx open https://my-app.local # load a hosted bundle
|
|
|
100
100
|
## compatibility feedback requires approval
|
|
101
101
|
|
|
102
102
|
run `rnx compat --json` from the app project root during setup. if the scan
|
|
103
|
-
identifies a plausible missing
|
|
104
|
-
evidence that behavior is missing or broken specifically in
|
|
103
|
+
identifies a plausible missing Peach seam, or the running app gives concrete
|
|
104
|
+
evidence that behavior is missing or broken specifically in Peach, summarize
|
|
105
105
|
the package, expected behavior, actual behavior, and runtime evidence for the
|
|
106
106
|
user. then offer to send that finding with `rnx report-issue`.
|
|
107
107
|
|
|
@@ -133,7 +133,7 @@ if the daemon is not installed: run `rnx daemon install`. if the registered
|
|
|
133
133
|
daemon is down, run `rnx daemon restart`, or `rnx serve` in another shell for
|
|
134
134
|
a foreground bridge. if no runtime is installed, `rnx open` installs it
|
|
135
135
|
before launching. if open errors with "port unreachable": confirm your dev
|
|
136
|
-
server is actually serving — the bundler has to be up before
|
|
136
|
+
server is actually serving — the bundler has to be up before Peach can
|
|
137
137
|
attach.
|
|
138
138
|
|
|
139
139
|
**two sims appear unexpectedly.** an earlier `rnx claim` didn't
|
|
@@ -151,7 +151,7 @@ Run `rnx runtime install`, then retry `rnx open <port>`.
|
|
|
151
151
|
do not retry in a loop. verify the development server first: `__soot/` must
|
|
152
152
|
respond and the engine watchers must be running. inspect the driver diagnostic
|
|
153
153
|
path printed by the failed command. restart the broken development stack at its
|
|
154
|
-
supervisor, then retry once.
|
|
154
|
+
supervisor, then retry once. Peach owns the isolated browser profile and its
|
|
155
155
|
process tree; do not open the shell URL through an operating-system browser.
|
|
156
156
|
|
|
157
157
|
**bridge disconnects mid-sim.** the WebSocket dropped (laptop slept,
|
package/skills/rnx-test/SKILL.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rnx-test
|
|
3
|
-
description: Run automated tests against your React Native app under
|
|
3
|
+
description: Run automated tests against your React Native app under Peach with Maestro YAML or Detox/Jest
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Peach tests
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Peach has two test runners. Maestro owns every YAML test, whether it was
|
|
9
9
|
hand-authored, generated from a goal, or recorded from live CLI actions.
|
|
10
|
-
Detox runs existing imperative Jest suites through the
|
|
10
|
+
Detox runs existing imperative Jest suites through the Peach driver.
|
|
11
11
|
|
|
12
12
|
> needs a connected, pinned sim. if `rnx describe` errors, load
|
|
13
13
|
> `/rnx-setup` first.
|
|
@@ -165,7 +165,7 @@ rnx maestro test .maestro/login.yaml
|
|
|
165
165
|
```
|
|
166
166
|
|
|
167
167
|
no test rewrites, no detox config, no simulator. the runtime auto-launches
|
|
168
|
-
|
|
168
|
+
a Peach shell if one isn't running.
|
|
169
169
|
|
|
170
170
|
```sh
|
|
171
171
|
rnx maestro # auto-discover .maestro/ or maestro/
|
|
@@ -190,10 +190,10 @@ interpolation.
|
|
|
190
190
|
|
|
191
191
|
### partial
|
|
192
192
|
|
|
193
|
-
- `clearKeychain` — no keychain in
|
|
193
|
+
- `clearKeychain` — no keychain in Peach; logs a warning and continues.
|
|
194
194
|
if a test depends on keychain state being reset, add an explicit
|
|
195
195
|
in-app "sign out" step instead.
|
|
196
|
-
- `when.platform` —
|
|
196
|
+
- `when.platform` — Peach emulates iOS; android branches are skipped.
|
|
197
197
|
- `openLink`: app routing works; there is no OS-level browser fallback.
|
|
198
198
|
|
|
199
199
|
`stopApp` keeps the guest runtime stopped. The next `launchApp` starts a fresh
|
|
@@ -216,7 +216,7 @@ these throw `unsupported maestro verb: X`:
|
|
|
216
216
|
- **"missing environment variable for flow placeholder: FOO"** — pass
|
|
217
217
|
`--env FOO=bar` or export it. pick one mechanism per repo and document
|
|
218
218
|
it in the flow directory's README.
|
|
219
|
-
- **flow can't find an element** —
|
|
219
|
+
- **flow can't find an element** — Peach's matcher uses testID + visible
|
|
220
220
|
text. UIKit-only labels that real maestro scrapes from the host OS won't
|
|
221
221
|
resolve here; switch to testID.
|
|
222
222
|
- **flow runs but nothing renders** — confirm the bundler your flow
|
|
@@ -225,7 +225,7 @@ these throw `unsupported maestro verb: X`:
|
|
|
225
225
|
|
|
226
226
|
## branch — detox drop-in
|
|
227
227
|
|
|
228
|
-
an existing detox suite runs against
|
|
228
|
+
an existing detox suite runs against Peach with **one line** of jest
|
|
229
229
|
config and zero test changes.
|
|
230
230
|
|
|
231
231
|
`import { by, device, element, expect, waitFor } from 'detox'` is
|
|
@@ -271,7 +271,7 @@ rnx detox init # scaffold config + e2e/example.test.ts
|
|
|
271
271
|
|
|
272
272
|
### compat surface
|
|
273
273
|
|
|
274
|
-
| detox API |
|
|
274
|
+
| detox API | Peach |
|
|
275
275
|
| ---------------------------------- | ------- |
|
|
276
276
|
| `element(by.id(...))` | supported |
|
|
277
277
|
| `element(by.text(...))` | supported — but prefer `by.id` |
|
|
@@ -291,7 +291,7 @@ rnx detox init # scaffold config + e2e/example.test.ts
|
|
|
291
291
|
|
|
292
292
|
### troubleshooting
|
|
293
293
|
|
|
294
|
-
- **"no
|
|
294
|
+
- **"no Peach shell reachable"** — start one yourself
|
|
295
295
|
(`rnx open <port>`) or pass `--port <n>`.
|
|
296
296
|
- **"jest cannot find module 'detox'"** — the preset isn't wired. confirm
|
|
297
297
|
`preset: 'rnxsim/detox'` is in the jest config jest
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rnx-visual
|
|
3
|
-
description: Pixel-level rendering comparison between
|
|
3
|
+
description: Pixel-level rendering comparison between Peach and native iOS — fonts, gradients, shadows, image fidelity
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Peach visual fidelity
|
|
7
7
|
|
|
8
|
-
pixel-level rendering fidelity: does the
|
|
8
|
+
pixel-level rendering fidelity: does the Peach canvas match native iOS
|
|
9
9
|
for text, gradients, shadows, blur, and image rendering? the `rnx`
|
|
10
10
|
CLI gives you two primitives — **canvas capture** (`rnx screenshot`)
|
|
11
11
|
and **structural diff** (`rnx debug snapshot`/`diff`). pixel
|
|
@@ -23,7 +23,7 @@ the capture step is the hard part and it's fully reproducible.
|
|
|
23
23
|
diff` is faster, deterministic, and tells you *why*.
|
|
24
24
|
- visual change (color, gradient, shadow, font metric, blur, image)? →
|
|
25
25
|
continue here.
|
|
26
|
-
- comparing
|
|
26
|
+
- comparing Peach to your iOS simulator for fidelity? → continue.
|
|
27
27
|
- regressing your own app against itself across commits? → continue.
|
|
28
28
|
|
|
29
29
|
## anti-patterns (read first)
|
|
@@ -64,7 +64,7 @@ screen you want to inspect and capture it with a distinct output filename.
|
|
|
64
64
|
|
|
65
65
|
capture both sides to PNGs, then diff with any standard image tool.
|
|
66
66
|
|
|
67
|
-
**
|
|
67
|
+
**Peach vs Peach (regression across your own commits):**
|
|
68
68
|
|
|
69
69
|
```sh
|
|
70
70
|
rnx screenshot -o before.png # baseline, before your change
|
|
@@ -74,7 +74,7 @@ npx pixelmatch before.png after.png diff.png 0.1 # writes diff.png
|
|
|
74
74
|
# or: magick compare before.png after.png diff.png
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
**
|
|
77
|
+
**Peach vs native iOS (fidelity to the real thing):**
|
|
78
78
|
|
|
79
79
|
```sh
|
|
80
80
|
# native side: lifecycle and UI use xcodebuildmcp with one claimed simulator
|
package/src/agent-sessions.ts
CHANGED
|
@@ -163,7 +163,7 @@ function tryWorkspaceSootsim(): RnxSelfInvocation | null {
|
|
|
163
163
|
|
|
164
164
|
/** locate the workspace sootsim package directory. tries `require.resolve`
|
|
165
165
|
* first (works in most contexts), then walks up from this module file
|
|
166
|
-
* looking for `packages/
|
|
166
|
+
* looking for `packages/peachsim/package.json` (works when sootsim is
|
|
167
167
|
* loaded via vite's native TS resolution, where require.resolve doesn't
|
|
168
168
|
* know about the workspace). */
|
|
169
169
|
function resolveSootsimPackageDir(): string | null {
|
|
@@ -172,8 +172,8 @@ function resolveSootsimPackageDir(): string | null {
|
|
|
172
172
|
const resolved = require.resolve(`${rnxPublicBrand.packageName}/package.json`)
|
|
173
173
|
return path.dirname(resolved)
|
|
174
174
|
} catch {}
|
|
175
|
-
// walk up from this file: .../packages/
|
|
176
|
-
// → .../packages/
|
|
175
|
+
// walk up from this file: .../packages/peachsim/src/agent-sessions.ts
|
|
176
|
+
// → .../packages/peachsim/. the module path is whatever the runtime
|
|
177
177
|
// gave us; when bundled by esbuild this will be the bundle path, not
|
|
178
178
|
// the source path, which is why we try require.resolve first.
|
|
179
179
|
const here = fileFromImportMeta()
|
package/src/attached-projects.ts
CHANGED
|
@@ -501,7 +501,7 @@ export function deletePreviewAttachment(id: string): void {
|
|
|
501
501
|
|
|
502
502
|
// --- seed from demo registry ---
|
|
503
503
|
|
|
504
|
-
/** seed the store from packages/
|
|
504
|
+
/** seed the store from packages/peachsim/scripts/demo-app-registry. only runs
|
|
505
505
|
* when the store is completely empty (never re-seeds) and only adds apps
|
|
506
506
|
* whose `dir` exists on disk (concern 6.4 — demo dirs vary per machine). */
|
|
507
507
|
export async function seedFromDemoAppRegistry(): Promise<void> {
|
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
SimStateOptions,
|
|
13
13
|
} from './bridge-contract'
|
|
14
14
|
import type { SimPerformStep, SimScreenshotOptions } from './sim-client'
|
|
15
|
-
import type { SootSimReplayTarget } from '@
|
|
15
|
+
import type { SootSimReplayTarget } from '@peach/globals'
|
|
16
16
|
|
|
17
17
|
/** the caller sent a value this bridge argument cannot be read from. */
|
|
18
18
|
export class BridgeInputError extends Error {
|