peachsim 0.1.630 → 0.1.632
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 +9 -1
- package/cli/app-fonts.ts +23 -4
- package/cli/bridge-flow-runner.ts +35 -28
- package/cli/command-registry.ts +1 -0
- package/cli/commands/compat.ts +11 -60
- package/cli/commands/control.ts +12 -5
- package/cli/commands/detect.ts +289 -26
- package/cli/commands/inspect/core.ts +1 -1
- package/cli/commands/inspect.ts +4 -0
- package/cli/commands/maestro.ts +8 -2
- package/cli/commands/project-report.ts +386 -0
- package/cli/commands/projectReportContract.ts +42 -0
- package/cli/commands/report-issue.ts +38 -54
- package/cli/commands/setup.ts +6 -4
- package/cli/commands/telemetry.ts +8 -3
- package/cli/commands/test.ts +374 -133
- package/cli/drivers/playwright-core-pin.ts +10 -0
- package/cli/drivers/playwright-provisioning.ts +36 -1
- package/cli/drivers/playwright.ts +26 -19
- package/cli/main.ts +11 -1
- package/cli/outbound-endpoints.ts +2 -1
- package/cli/privacy.ts +1 -2
- package/cli/runtime-diagnostics.ts +51 -0
- package/cli/shell-init.ts +6 -1
- package/cli/telemetry.ts +78 -7
- package/cli/ws-bridge.ts +8 -2
- package/detox/index.ts +3 -8
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- 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-test-args.cjs +17 -5
- package/dist-lib/cloud-test-args.mjs +17 -5
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +2 -7
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +13 -5
- package/dist-lib/host/bridge-host.cjs +157 -113
- 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 +11 -2
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +1 -1
- package/dist-lib/menu.cjs +11 -1
- package/dist-lib/menu.mjs +11 -1
- 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 +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +1 -1
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +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 +3 -1
- package/dist-lib/sdk.mjs +2 -1
- package/dist-lib/skills.cjs +700 -329
- package/dist-lib/swift-compile.cjs +1 -1
- package/dist-lib/swift-compile.mjs +1 -1
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +443 -173
- package/dist-lib/vite-swift.cjs +1 -1
- package/dist-lib/vite.cjs +1 -1
- package/package.json +2 -1
- package/skills/contrast/SKILL.md +1 -0
- package/src/cloud-test-args.ts +18 -6
- package/src/home-paths.ts +19 -9
- package/src/menu.ts +11 -0
package/README.md
CHANGED
|
@@ -254,7 +254,15 @@ through an unreachable IPv4 address. `peach open` starts a local bridge when no
|
|
|
254
254
|
daemon is reachable. The background service is strongly recommended for local
|
|
255
255
|
agent work because inspect, interaction, and test commands reuse one ready bridge
|
|
256
256
|
and runtime. Enable it at any time with `peach daemon install`; it is never required
|
|
257
|
-
in CI.
|
|
257
|
+
in CI.
|
|
258
|
+
|
|
259
|
+
Sandboxed or agent runners may stop child processes when a command ends,
|
|
260
|
+
including a bridge started by `peach open`. Keep `peach serve` running in a
|
|
261
|
+
persistent terminal or background task outside that command's lifetime, then
|
|
262
|
+
run `peach open` and later commands against it. When using a custom bridge port,
|
|
263
|
+
pass the same `--port <n>` to `peach serve` and the CLI commands.
|
|
264
|
+
|
|
265
|
+
The packaged Desktop app is optional and available through first-run
|
|
258
266
|
onboarding or `peach desktop install`. A downloaded app starts its own bundled
|
|
259
267
|
daemon on first launch. An installed runtime can open while the daemon checks
|
|
260
268
|
for updates; a first install waits for the runtime download to finish. The app
|
package/cli/app-fonts.ts
CHANGED
|
@@ -139,6 +139,16 @@ export function discoverNativeLinkedAppFonts(opts: {
|
|
|
139
139
|
collectFontFiles(root, relativeRoot.split(path.sep).join('/'))
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
// the bundle path is the entry relative to metro's root, so a bundle at
|
|
143
|
+
// /apps/app/index.bundle served for /repo/apps/app puts that root at /repo.
|
|
144
|
+
const bundleDir = path.posix
|
|
145
|
+
.dirname(new URL(opts.bundleUrl).pathname)
|
|
146
|
+
.replace(/^\/+/, '')
|
|
147
|
+
const appRelativeDir = bundleDir.split('/').join(path.sep)
|
|
148
|
+
const metroRootCandidates = [selectedAppDir]
|
|
149
|
+
if (bundleDir && selectedAppDir.endsWith(path.sep + appRelativeDir)) {
|
|
150
|
+
metroRootCandidates.push(selectedAppDir.slice(0, -appRelativeDir.length - 1))
|
|
151
|
+
}
|
|
142
152
|
const vectorIconRequire = createRequire(path.join(selectedAppDir, 'package.json'))
|
|
143
153
|
for (const dependencyName of vectorIconDependencies) {
|
|
144
154
|
try {
|
|
@@ -150,10 +160,19 @@ export function discoverNativeLinkedAppFonts(opts: {
|
|
|
150
160
|
const packageRoot = entryPath.slice(0, markerIndex + marker.length - 1)
|
|
151
161
|
const fontsDir =
|
|
152
162
|
dependencyName === 'react-native-vector-icons' ? 'Fonts' : 'fonts'
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
163
|
+
// the package can sit outside the app (a hoisted workspace
|
|
164
|
+
// node_modules or a pnpm store). metro resolves asset paths against
|
|
165
|
+
// its config.projectRoot, which is the app in expo's monorepo setup
|
|
166
|
+
// but the repo root when metro.config sets it there, so offer the
|
|
167
|
+
// path from each candidate root; the reachability check keeps the
|
|
168
|
+
// one metro actually serves.
|
|
169
|
+
const fontsRoot = path.join(packageRoot, fontsDir)
|
|
170
|
+
for (const metroRoot of metroRootCandidates) {
|
|
171
|
+
collectFontFiles(
|
|
172
|
+
fontsRoot,
|
|
173
|
+
path.relative(metroRoot, fontsRoot).split(path.sep).join('/'),
|
|
174
|
+
)
|
|
175
|
+
}
|
|
157
176
|
} catch {}
|
|
158
177
|
}
|
|
159
178
|
}
|
|
@@ -2710,38 +2710,45 @@ export class SootSimBridgeFlowRunner {
|
|
|
2710
2710
|
}
|
|
2711
2711
|
|
|
2712
2712
|
try {
|
|
2713
|
-
//
|
|
2714
|
-
//
|
|
2715
|
-
//
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
step.
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2713
|
+
// simple commands have a wall-clock watchdog. bounded operations keep
|
|
2714
|
+
// their own lookup, settle and bridge deadlines, so the watchdog cannot
|
|
2715
|
+
// report a completed step while its action continues in the background.
|
|
2716
|
+
const hasBoundedWait = !!(
|
|
2717
|
+
// taps combine bounded lookup, settling and delivery. a wall-clock
|
|
2718
|
+
// watchdog can finish the step while that action is still running.
|
|
2719
|
+
// let these operations finish before starting the next command.
|
|
2720
|
+
(
|
|
2721
|
+
step.tapOn ||
|
|
2722
|
+
step.doubleTapOn ||
|
|
2723
|
+
step.longPressOn ||
|
|
2724
|
+
step.extendedWaitUntil ||
|
|
2725
|
+
step.waitFor ||
|
|
2726
|
+
step.scrollUntilVisible ||
|
|
2727
|
+
step.launchApp ||
|
|
2728
|
+
step.runFlow ||
|
|
2729
|
+
// retry reruns its body on failure, so its wall time is the body's
|
|
2730
|
+
// times the retry count — the 10s no-progress watchdog is the wrong
|
|
2731
|
+
// budget and would report "bridge probably hung" for a slow retry.
|
|
2732
|
+
step.retry ||
|
|
2733
|
+
// runScript http calls are synchronous with a 5-minute upstream
|
|
2734
|
+
// timeout — the 10s watchdog would kill legitimate setup scripts.
|
|
2735
|
+
step.runScript ||
|
|
2736
|
+
// takeScreenshot self-governs: it retries a bounded visual settle that
|
|
2737
|
+
// already fails with its own reason. sharing the watchdog's 10s budget
|
|
2738
|
+
// meant the watchdog always won that race, reporting "bridge probably
|
|
2739
|
+
// hung" for a screen that was merely still animating.
|
|
2740
|
+
step.takeScreenshot ||
|
|
2741
|
+
// assertions wait out maestro's lookup window, longer than 10s, and
|
|
2742
|
+
// every query they make times out on its own, so they report their
|
|
2743
|
+
// own failure instead of the watchdog's guess.
|
|
2744
|
+
step.assertVisible ||
|
|
2745
|
+
step.assertNotVisible
|
|
2746
|
+
)
|
|
2740
2747
|
)
|
|
2741
2748
|
const STEP_WATCHDOG_MS = 10_000
|
|
2742
2749
|
const body = this.runStepInner(step)
|
|
2743
2750
|
let screenshotPath: string | undefined
|
|
2744
|
-
if (
|
|
2751
|
+
if (hasBoundedWait) {
|
|
2745
2752
|
const result = await body
|
|
2746
2753
|
if (typeof result === 'string') screenshotPath = result
|
|
2747
2754
|
} else {
|
package/cli/command-registry.ts
CHANGED
package/cli/commands/compat.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// peach compat — scan dependencies with the shared compatibility engine.
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { dirname, join, relative, resolve } from 'node:path'
|
|
3
|
+
import { relative, resolve } from 'node:path'
|
|
5
4
|
import {
|
|
6
5
|
COMPAT_RESULT_CAVEAT,
|
|
7
6
|
formatDetailedScanLines,
|
|
@@ -9,13 +8,13 @@ import {
|
|
|
9
8
|
scanDeps,
|
|
10
9
|
type ScanResult,
|
|
11
10
|
} from '@peach/compat/web'
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
11
|
+
import {
|
|
12
|
+
findProjectRoot,
|
|
13
|
+
scanWorkspace,
|
|
14
|
+
installedManifest,
|
|
15
|
+
readProjectManifest,
|
|
16
|
+
} from './detect'
|
|
17
|
+
import type { ProjectManifest } from './projectReportContract'
|
|
19
18
|
|
|
20
19
|
export async function runCompat(args: string[]): Promise<void> {
|
|
21
20
|
if (args.includes('--help') || args.includes('-h')) {
|
|
@@ -41,7 +40,7 @@ flags:
|
|
|
41
40
|
const packageName = args.find(
|
|
42
41
|
(arg, index) => !arg.startsWith('-') && (appIndex < 0 || index !== appIndex + 1),
|
|
43
42
|
)
|
|
44
|
-
const pkg =
|
|
43
|
+
const pkg = readProjectManifest(appDir)
|
|
45
44
|
if (!pkg) {
|
|
46
45
|
console.error(` no package.json found in ${appDir}`)
|
|
47
46
|
process.exitCode = 1
|
|
@@ -65,7 +64,7 @@ flags:
|
|
|
65
64
|
const workspaceRoot = findProjectRoot(appDir)
|
|
66
65
|
const elsewhere = scanWorkspace(workspaceRoot).apps.flatMap((app) => {
|
|
67
66
|
if (resolve(app.dir) === appDir) return []
|
|
68
|
-
const manifest =
|
|
67
|
+
const manifest = readProjectManifest(app.dir)
|
|
69
68
|
if (!manifest) return []
|
|
70
69
|
const result = scanDeps(installedManifest(app.dir, manifest))
|
|
71
70
|
return result.packages.length > 0 ? [{ dir: app.dir, result }] : []
|
|
@@ -114,57 +113,9 @@ function report(args: string[], result: ScanResult): void {
|
|
|
114
113
|
console.log(`\n ${COMPAT_RESULT_CAVEAT}\n`)
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
// the declared range is often not a version: pnpm and bun catalogs write
|
|
118
|
-
// `catalog:`, a workspace dependency writes `workspace:*`, and a pinned tarball
|
|
119
|
-
// writes a URL. none of those match a verified version range, so a whole app
|
|
120
|
-
// reports as unverified. the installed tree holds the version that will
|
|
121
|
-
// actually run, so ask it, and keep the declared range for anything the project
|
|
122
|
-
// has not installed.
|
|
123
|
-
function installedManifest(appDir: string, pkg: PackageManifest): PackageManifest {
|
|
124
|
-
const resolveVersion = (name: string, declared: string): string => {
|
|
125
|
-
let dir = appDir
|
|
126
|
-
while (true) {
|
|
127
|
-
const manifest = join(dir, 'node_modules', name, 'package.json')
|
|
128
|
-
if (existsSync(manifest)) {
|
|
129
|
-
try {
|
|
130
|
-
const parsed: unknown = JSON.parse(readFileSync(manifest, 'utf8'))
|
|
131
|
-
if (parsed && typeof parsed === 'object' && 'version' in parsed) {
|
|
132
|
-
const version = parsed.version
|
|
133
|
-
if (typeof version === 'string') return version
|
|
134
|
-
}
|
|
135
|
-
} catch {}
|
|
136
|
-
return declared
|
|
137
|
-
}
|
|
138
|
-
const parent = dirname(dir)
|
|
139
|
-
if (parent === dir) return declared
|
|
140
|
-
dir = parent
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
const mapped = (deps: Record<string, string> | undefined) =>
|
|
144
|
-
deps &&
|
|
145
|
-
Object.fromEntries(
|
|
146
|
-
Object.entries(deps).map(([name, declared]) => [
|
|
147
|
-
name,
|
|
148
|
-
resolveVersion(name, declared),
|
|
149
|
-
]),
|
|
150
|
-
)
|
|
151
|
-
return {
|
|
152
|
-
name: pkg.name,
|
|
153
|
-
dependencies: mapped(pkg.dependencies),
|
|
154
|
-
devDependencies: mapped(pkg.devDependencies),
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function readPackageJson(appDir: string): PackageManifest | null {
|
|
159
|
-
const packagePath = resolve(appDir, 'package.json')
|
|
160
|
-
if (!existsSync(packagePath)) return null
|
|
161
|
-
const parsed: unknown = JSON.parse(readFileSync(packagePath, 'utf8'))
|
|
162
|
-
return parsed && typeof parsed === 'object' ? parsed : null
|
|
163
|
-
}
|
|
164
|
-
|
|
165
116
|
function scanOnePackage(
|
|
166
117
|
appDir: string,
|
|
167
|
-
pkg:
|
|
118
|
+
pkg: ProjectManifest,
|
|
168
119
|
packageName: string,
|
|
169
120
|
): ScanResult {
|
|
170
121
|
const installed = installedManifest(appDir, pkg)
|
package/cli/commands/control.ts
CHANGED
|
@@ -73,7 +73,9 @@ import {
|
|
|
73
73
|
} from '../current-sim'
|
|
74
74
|
import { getDriver } from '../drivers/registry'
|
|
75
75
|
import { openUrl as openUrlInBrowser } from '../open-url'
|
|
76
|
+
import { diagnosticsAllowedForCurrentProcess } from '../privacy'
|
|
76
77
|
import { rethrowIfExit, rnxExit } from '../run-rnx'
|
|
78
|
+
import { collectRuntimeDiagnostics } from '../runtime-diagnostics'
|
|
77
79
|
import { rnxSelfInvocation } from '../self-invocation'
|
|
78
80
|
import {
|
|
79
81
|
BridgeSimLockedError,
|
|
@@ -695,11 +697,13 @@ async function filterReachableAppFonts(
|
|
|
695
697
|
}
|
|
696
698
|
}),
|
|
697
699
|
)
|
|
698
|
-
|
|
700
|
+
// a font offered from more than one candidate metro root keeps the first
|
|
701
|
+
// url metro served.
|
|
702
|
+
const reachable = new Map<string, DiscoveredAppFontSpec>()
|
|
699
703
|
for (const spec of results) {
|
|
700
|
-
if (spec !== null) reachable.
|
|
704
|
+
if (spec !== null && !reachable.has(spec.family)) reachable.set(spec.family, spec)
|
|
701
705
|
}
|
|
702
|
-
return reachable
|
|
706
|
+
return [...reachable.values()]
|
|
703
707
|
}
|
|
704
708
|
|
|
705
709
|
async function resolveShellUrl(
|
|
@@ -1293,7 +1297,8 @@ function printAgentListenHint(quiet: boolean): void {
|
|
|
1293
1297
|
// a splash; the caller can `peach describe` again a second later. for
|
|
1294
1298
|
// warm loads it's the real screen. guarded by --no-describe.
|
|
1295
1299
|
async function maybeDescribeAfterOpen(wsPort: number, simId: string, args: string[]) {
|
|
1296
|
-
|
|
1300
|
+
const noDescribe = args.includes('--no-describe')
|
|
1301
|
+
if (noDescribe && !diagnosticsAllowedForCurrentProcess()) return
|
|
1297
1302
|
const prevQuiet = process.env.SOOTSIM_QUIET_TARGET_NOTICE
|
|
1298
1303
|
process.env.SOOTSIM_QUIET_TARGET_NOTICE = '1'
|
|
1299
1304
|
try {
|
|
@@ -1302,7 +1307,7 @@ async function maybeDescribeAfterOpen(wsPort: number, simId: string, args: strin
|
|
|
1302
1307
|
// seconds on a still-routing guest app, which is exactly what we want
|
|
1303
1308
|
// to skip for the open-side describe. caller can `peach describe`
|
|
1304
1309
|
// again later for post-routing state.
|
|
1305
|
-
await waitForTreeStable(wsPort, simId, { stableMs: 150, maxMs: 400 })
|
|
1310
|
+
if (!noDescribe) await waitForTreeStable(wsPort, simId, { stableMs: 150, maxMs: 400 })
|
|
1306
1311
|
const bridge = createBridge(wsPort, {
|
|
1307
1312
|
commandTimeoutMs: 3000,
|
|
1308
1313
|
simId,
|
|
@@ -1310,6 +1315,7 @@ async function maybeDescribeAfterOpen(wsPort: number, simId: string, args: strin
|
|
|
1310
1315
|
simIdSource: 'flag',
|
|
1311
1316
|
})
|
|
1312
1317
|
try {
|
|
1318
|
+
if (noDescribe) return
|
|
1313
1319
|
let readyProbe: Awaited<ReturnType<typeof inspectWaitReady>> | null = null
|
|
1314
1320
|
try {
|
|
1315
1321
|
readyProbe = await inspectWaitReady(bridge, 1)
|
|
@@ -1341,6 +1347,7 @@ async function maybeDescribeAfterOpen(wsPort: number, simId: string, args: strin
|
|
|
1341
1347
|
console.log('')
|
|
1342
1348
|
console.log(result.tree)
|
|
1343
1349
|
} finally {
|
|
1350
|
+
await collectRuntimeDiagnostics(bridge)
|
|
1344
1351
|
bridge.close()
|
|
1345
1352
|
}
|
|
1346
1353
|
} catch {
|