peachsim 0.1.578 → 0.1.580
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/LICENSE +1 -1
- package/README.md +11 -9
- package/cli/app-catalog.ts +77 -0
- package/cli/app-fonts.ts +4 -20
- package/cli/app-splash.ts +2 -18
- package/cli/bridge-flow-runner.ts +505 -139
- package/cli/cloud-client.ts +1 -4
- package/cli/cloud-session.ts +1 -4
- package/cli/cloud-test-result-files.ts +3 -0
- package/cli/cloud-test-tunnel.ts +2 -5
- package/cli/command-registry.ts +2 -0
- package/cli/commands/agent.ts +149 -23
- package/cli/commands/assert.ts +1 -4
- package/cli/commands/box/peach-command.ts +62 -0
- package/cli/commands/box/plane-share.ts +0 -70
- package/cli/commands/box/rnx-command.ts +1 -60
- package/cli/commands/box/search-command.ts +14 -0
- package/cli/commands/box.ts +2 -2
- package/cli/commands/control.ts +121 -27
- package/cli/commands/cpu-profile.ts +23 -10
- package/cli/commands/daemon.ts +1 -9
- package/cli/commands/detox.ts +26 -18
- package/cli/commands/device.ts +129 -1
- package/cli/commands/film.ts +25 -16
- package/cli/commands/flow.ts +9 -2
- package/cli/commands/inspect/actions.ts +75 -1
- package/cli/commands/inspect/core.ts +81 -2
- package/cli/commands/inspect/describe.ts +2 -6
- package/cli/commands/inspect/env.ts +1 -1
- package/cli/commands/inspect/settling.ts +20 -3
- package/cli/commands/inspect/shared.ts +1 -4
- package/cli/commands/inspect/sleep.ts +1 -1
- package/cli/commands/inspect.ts +16 -49
- package/cli/commands/install-desktop.ts +8 -6
- package/cli/commands/login.ts +3 -5
- package/cli/commands/maestro-generate.ts +1 -4
- package/cli/commands/maestro.ts +38 -21
- package/cli/commands/no-bridge-hint.ts +0 -14
- package/cli/commands/permissions.ts +2 -2
- package/cli/commands/platform.ts +13 -6
- package/cli/commands/screenshot.ts +6 -1
- package/cli/commands/skills.ts +1 -1
- package/cli/commands/test.ts +6 -2
- package/cli/commands/three-mode.ts +162 -19
- package/cli/commands/upload.ts +3 -18
- package/cli/current-sim.ts +0 -1
- package/cli/desktop-companion.ts +40 -8
- package/cli/drivers/playwright-provisioning.ts +1 -4
- package/cli/flow-export.ts +1 -8
- package/cli/flow-session.ts +0 -4
- package/cli/hints.ts +1 -16
- package/cli/inspect-notice-state.ts +0 -4
- package/cli/main.ts +2 -4
- package/cli/outbound-endpoints.ts +1 -24
- package/cli/parse-args.ts +1 -2
- package/cli/rnx-in-process.ts +3 -1
- package/cli/run-rnx-core.ts +4 -2
- package/cli/run-rnx.ts +18 -10
- package/cli/send-to-box.ts +1 -4
- package/cli/telemetry.ts +1 -1
- package/cli/ws-bridge.ts +1 -5
- package/detox/gestures.ts +20 -6
- package/detox/index.ts +379 -123
- package/detox/navigation-lifecycle.ts +22 -13
- package/dist-lib/agent-daemon-client.cjs +21 -15
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +3 -17
- package/dist-lib/agent-sessions.cjs +5 -4
- package/dist-lib/attached-projects.cjs +1 -77
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +11 -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 +7 -5
- package/dist-lib/bridge-contract-input.mjs +7 -5
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +30 -7
- package/dist-lib/capture-contract.mjs +30 -7
- 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 +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +17 -11
- package/dist-lib/cloud.mjs +17 -11
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +321 -122
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +427 -212
- package/dist-lib/host/fetch-proxy-handler.cjs +5 -28
- 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 +315 -58
- 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 +67 -31
- package/dist-lib/menu.mjs +64 -28
- package/dist-lib/metro-fingerprint-registry.cjs +8 -4
- package/dist-lib/metro-fingerprint-registry.mjs +6 -4
- package/dist-lib/metro-production-bundle.cjs +2 -2
- package/dist-lib/metro-production-bundle.mjs +2 -2
- package/dist-lib/metro.cjs +8 -4
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +7 -4
- 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 +3 -13
- package/dist-lib/scripts/dev-server-scanner.cjs +5 -17
- package/dist-lib/sdk.cjs +129 -10
- package/dist-lib/sdk.mjs +127 -10
- package/dist-lib/skills.cjs +1635 -1121
- package/dist-lib/swift-compile.cjs +277 -70
- package/dist-lib/swift-compile.mjs +275 -70
- package/dist-lib/swift-package.cjs +17 -5
- package/dist-lib/swift-package.mjs +15 -4
- package/dist-lib/swift-submit.cjs +32 -8
- package/dist-lib/swift-submit.mjs +29 -7
- package/dist-lib/swift.cjs +30912 -577
- package/dist-lib/vite-kotlin.cjs +356 -0
- package/dist-lib/vite-swift.cjs +1743 -0
- package/dist-lib/vite.cjs +208 -1039
- package/kotlin/README.md +18 -0
- package/kotlin/Sources/androidx/annotation/Keep.kt +4 -0
- package/kotlin/Sources/dev/onejs/one/OneNativeSourceDispatch.kt +7 -0
- package/kotlin/Sources/dev/onejs/one/source/OneModule.kt +4 -0
- package/kotlin/Sources/org/json/JSON.kt +244 -0
- package/kotlin/toolchain.json +19 -0
- package/package.json +14 -2
- package/scripts/dev-server-scanner.ts +0 -18
- package/scripts/optional-demo-registry.ts +0 -17
- package/skills/contrast/SKILL.md +2 -2
- package/skills/peach-setup/SKILL.md +16 -0
- package/src/agent-daemon-client.ts +36 -22
- package/src/agent-identity.ts +0 -15
- package/src/agent-prompt.ts +12 -0
- package/src/attached-projects.ts +0 -72
- package/src/backend-origin.ts +10 -0
- package/src/bridge-contract-input.ts +2 -5
- package/src/bridge-contract.ts +2 -0
- package/src/capture-contract.ts +53 -11
- package/src/cloud.ts +4 -9
- package/src/dev-server-open.ts +0 -99
- package/src/home-paths.ts +2 -3
- package/src/host/agent-host.ts +82 -36
- package/src/host/bridge-host.ts +5 -11
- package/src/host/fetch-proxy-handler.ts +19 -43
- package/src/is-record.ts +4 -0
- package/src/menu.ts +82 -37
- package/src/metro-fingerprint-registry.ts +1 -4
- package/src/metro-production-bundle.ts +1 -1
- package/src/plist.ts +1 -4
- package/src/public-brand.ts +5 -3
- package/src/render-mode.ts +0 -12
- package/src/runtime-assets.ts +9 -2
- package/src/screenshots/compose.ts +7 -1
- package/src/screenshots/frame-compose.ts +21 -10
- package/src/screenshots/schema.ts +1 -4
- package/src/sim-client.ts +2 -4
- package/src/skills/builtin/maestro-test.ts +1 -4
- package/src/sleep.ts +4 -0
- package/src/swift-compile.ts +209 -47
- package/src/swift-icu.ts +140 -0
- package/src/swift-package.ts +26 -2
- package/src/swift-submit.ts +84 -4
- package/src/swift.ts +2 -0
- package/src/vite-plugin-kotlin.ts +376 -0
- package/src/vite-plugin-one.ts +224 -36
- package/src/vite-plugin-swift.ts +382 -96
- package/src/vite-plugin.ts +121 -134
- package/src/vite.d.ts +10 -0
- package/cli/resolve-assets.ts +0 -63
- package/src/rolldown-plugin-swift.ts +0 -244
- package/src/team-machine-prompt.ts +0 -73
package/cli/flow-export.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { mkdirSync, writeFileSync } from 'node:fs'
|
|
|
11
11
|
import { dirname, resolve } from 'node:path'
|
|
12
12
|
import yaml from 'yaml'
|
|
13
13
|
import { validateFlowSteps } from './flow-file'
|
|
14
|
-
import {
|
|
14
|
+
import { readFlowSession } from './flow-session'
|
|
15
15
|
|
|
16
16
|
export function exportFlowSession(outputPath?: string) {
|
|
17
17
|
const state = readFlowSession()
|
|
@@ -46,10 +46,3 @@ export function exportFlowSession(outputPath?: string) {
|
|
|
46
46
|
outputPath: resolvedOutputPath,
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
export function endFlowSession(outputPath?: string) {
|
|
51
|
-
const result = exportFlowSession(outputPath)
|
|
52
|
-
if (!result.active) return result
|
|
53
|
-
finalizeFlowSession()
|
|
54
|
-
return result
|
|
55
|
-
}
|
package/cli/flow-session.ts
CHANGED
|
@@ -62,10 +62,6 @@ export function readFlowSession(): FlowSessionState | null {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
export function hasActiveFlowSession() {
|
|
66
|
-
return readFlowSession() !== null
|
|
67
|
-
}
|
|
68
|
-
|
|
69
65
|
export function startFlowSession() {
|
|
70
66
|
const now = new Date().toISOString()
|
|
71
67
|
const state: FlowSessionState = {
|
package/cli/hints.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// suppression is keyed on `getCliIdentityKey()` so two agents / two terminals
|
|
15
15
|
// don't share shown-sets.
|
|
16
16
|
|
|
17
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
|
|
18
18
|
import { tmpdir } from 'os'
|
|
19
19
|
import { dirname, join } from 'path'
|
|
20
20
|
import { getCliIdentityKey } from './current-sim'
|
|
@@ -136,21 +136,6 @@ export function maybeHint(id: string, ...args: unknown[]): boolean {
|
|
|
136
136
|
return true
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
export function resetHintsForIdentity() {
|
|
140
|
-
rmSync(identityStatePath(), { force: true })
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function resetGlobalHints() {
|
|
144
|
-
rmSync(globalStatePath(), { force: true })
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function listHints(): Array<{ id: string; frequency: HintFrequency }> {
|
|
148
|
-
return Array.from(registry.entries()).map(([id, spec]) => ({
|
|
149
|
-
id,
|
|
150
|
-
frequency: spec.frequency,
|
|
151
|
-
}))
|
|
152
|
-
}
|
|
153
|
-
|
|
154
139
|
// register the repo-wide hints here. keeping them centralised makes it easy
|
|
155
140
|
// to audit wording + frequency policy in one place.
|
|
156
141
|
defineHint<[number]>('app-still-loading', {
|
package/cli/main.ts
CHANGED
|
@@ -242,7 +242,6 @@ const port = parsed.globalFlags['port'] as number | undefined
|
|
|
242
242
|
const verbose = parsed.verbose
|
|
243
243
|
const device = parsed.globalFlags['device'] as string | undefined
|
|
244
244
|
const theme = parsed.globalFlags['theme'] as string | undefined
|
|
245
|
-
const homeScreen = parsed.globalFlags['home-screen'] as string | undefined
|
|
246
245
|
const driver = parsed.globalFlags['driver'] as string | undefined
|
|
247
246
|
const headless = (parsed.globalFlags['headless'] as boolean | undefined) === true
|
|
248
247
|
const globalSimTarget = (parsed.globalFlags['sim'] ??
|
|
@@ -252,13 +251,12 @@ const commandArgsWithSim = globalSimTarget
|
|
|
252
251
|
? ['--sim', globalSimTarget, ...parsed.commandArgs]
|
|
253
252
|
: parsed.commandArgs
|
|
254
253
|
|
|
255
|
-
// apply device/theme
|
|
256
|
-
if (device || theme
|
|
254
|
+
// apply device/theme to settings store if provided
|
|
255
|
+
if (device || theme) {
|
|
257
256
|
const { settingsStore } = await import('peach-engine/settings/store')
|
|
258
257
|
const overrides: Record<string, any> = {}
|
|
259
258
|
if (device) overrides.deviceModel = device
|
|
260
259
|
if (theme) overrides.colorScheme = theme
|
|
261
|
-
if (homeScreen) overrides.homeScreenVersion = homeScreen
|
|
262
260
|
settingsStore.apply(overrides)
|
|
263
261
|
}
|
|
264
262
|
|
|
@@ -128,7 +128,7 @@ export const OUTBOUND_ENDPOINTS: Record<string, OutboundEndpointDeclaration> = {
|
|
|
128
128
|
peach_cloud_sim: {
|
|
129
129
|
category: 'explicit-user-action',
|
|
130
130
|
destination:
|
|
131
|
-
'https://
|
|
131
|
+
'https://peachsim.com by default, or the test or disposable origin named by PEACH_CLOUD_ORIGIN',
|
|
132
132
|
preConsent: false,
|
|
133
133
|
payload:
|
|
134
134
|
'the Metro JavaScript bundle selected by peach ios --remote, then structured simulator commands and lifecycle requests',
|
|
@@ -856,26 +856,3 @@ export function detectOutboundCalls(repositoryRoot: string): DetectedOutboundCal
|
|
|
856
856
|
.map(([key, count]) => ({ key, count }))
|
|
857
857
|
.sort((a, b) => a.key.localeCompare(b.key))
|
|
858
858
|
}
|
|
859
|
-
|
|
860
|
-
export function undeclaredOutboundCalls(repositoryRoot: string): string[] {
|
|
861
|
-
const detected = detectOutboundCalls(repositoryRoot)
|
|
862
|
-
const failures: string[] = []
|
|
863
|
-
const seen = new Set<string>()
|
|
864
|
-
for (const call of detected) {
|
|
865
|
-
seen.add(call.key)
|
|
866
|
-
const declaration = DECLARED_OUTBOUND_CALLS[call.key]
|
|
867
|
-
if (!declaration) {
|
|
868
|
-
failures.push(`undeclared: ${call.key} (count ${call.count})`)
|
|
869
|
-
continue
|
|
870
|
-
}
|
|
871
|
-
if (declaration.count !== call.count) {
|
|
872
|
-
failures.push(
|
|
873
|
-
`count changed: ${call.key} (declared ${declaration.count}, found ${call.count})`,
|
|
874
|
-
)
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
for (const key of Object.keys(DECLARED_OUTBOUND_CALLS)) {
|
|
878
|
-
if (!seen.has(key)) failures.push(`stale declaration: ${key}`)
|
|
879
|
-
}
|
|
880
|
-
return failures
|
|
881
|
-
}
|
package/cli/parse-args.ts
CHANGED
|
@@ -77,8 +77,7 @@ export const HYPHENATED_GROUPING_VERBS = new Set(['do', 'get', 'wait', 'shell',
|
|
|
77
77
|
|
|
78
78
|
// known commands — if first positional arg matches, it's a command.
|
|
79
79
|
// every entry here must have a `cliCommandMetas` entry in
|
|
80
|
-
// `packages/peach-skills/src/cli/meta.ts
|
|
81
|
-
// allowlist in `test/sootsimCliRegistry.test.ts`), so help, website docs,
|
|
80
|
+
// `packages/peach-skills/src/cli/meta.ts`, so help, website docs,
|
|
82
81
|
// and agent skills can never drift from what the parser accepts.
|
|
83
82
|
export const COMMANDS = new Set(RNX_NODE_COMMAND_NAMES)
|
|
84
83
|
|
package/cli/rnx-in-process.ts
CHANGED
|
@@ -86,10 +86,12 @@ export async function runRnxInProcess(
|
|
|
86
86
|
})
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
export const runPeachInProcess = runRnxInProcess
|
|
90
|
+
|
|
89
91
|
// the transport contract and the command's own name, re-exported so a host
|
|
90
92
|
// that only wants to register this entry and supply a bridge (a Box Durable
|
|
91
93
|
// Object, say) has one module to import.
|
|
92
|
-
export { rnxPublicBrand } from '../src/public-brand'
|
|
94
|
+
export { peachPublicBrand, rnxPublicBrand } from '../src/public-brand'
|
|
93
95
|
export type {
|
|
94
96
|
BridgeClaimResult,
|
|
95
97
|
BridgeSimInfo,
|
package/cli/run-rnx-core.ts
CHANGED
|
@@ -110,12 +110,14 @@ export async function runRnxCore(
|
|
|
110
110
|
options.backend.kind,
|
|
111
111
|
)
|
|
112
112
|
if (!availability.available) {
|
|
113
|
-
captured.writer.stderr(
|
|
113
|
+
captured.writer.stderr(
|
|
114
|
+
`${availability.reason ?? `peach ${command} is unavailable`}\n`,
|
|
115
|
+
)
|
|
114
116
|
return result(captured, 1)
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
if (signal.aborted) {
|
|
118
|
-
captured.writer.stderr('
|
|
120
|
+
captured.writer.stderr('peach: command cancelled\n')
|
|
119
121
|
return result(captured, 1)
|
|
120
122
|
}
|
|
121
123
|
|
package/cli/run-rnx.ts
CHANGED
|
@@ -28,39 +28,45 @@ export interface RnxRunResult {
|
|
|
28
28
|
exitCode: number
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export type PeachRunResult = RnxRunResult
|
|
32
|
+
|
|
31
33
|
interface RnxSinks {
|
|
32
34
|
stdout: string[]
|
|
33
35
|
stderr: string[]
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
// thrown by `
|
|
38
|
+
// thrown by `peachExit` in place of process.exit. every catch that reports an
|
|
37
39
|
// error must re-throw this before reporting, or it turns a deliberate exit
|
|
38
40
|
// into a spurious "command failed" line with the sentinel's own text.
|
|
39
|
-
export class
|
|
41
|
+
export class PeachExit extends Error {
|
|
40
42
|
readonly code: number
|
|
41
43
|
|
|
42
44
|
constructor(code: number) {
|
|
43
45
|
super(`peach exited with code ${code}`)
|
|
44
|
-
this.name = '
|
|
46
|
+
this.name = 'PeachExit'
|
|
45
47
|
this.code = code
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
export const RnxExit = PeachExit
|
|
52
|
+
|
|
49
53
|
const sinks = new AsyncLocalStorage<RnxSinks>()
|
|
50
54
|
|
|
51
55
|
// stop a command's deliberate exit from being reported as a failure. the
|
|
52
56
|
// guard every reporting catch needs, in one place so the call sites stay one
|
|
53
57
|
// line.
|
|
54
58
|
export function rethrowIfExit(error: unknown): void {
|
|
55
|
-
if (error instanceof
|
|
59
|
+
if (error instanceof PeachExit) throw error
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
// the in-process replacement for process.exit. returns `never` exactly as
|
|
59
63
|
// process.exit does, so it substitutes without changing any call site's types.
|
|
60
|
-
export function
|
|
61
|
-
throw new
|
|
64
|
+
export function peachExit(code: number): never {
|
|
65
|
+
throw new PeachExit(code)
|
|
62
66
|
}
|
|
63
67
|
|
|
68
|
+
export const rnxExit: (code: number) => never = peachExit
|
|
69
|
+
|
|
64
70
|
function write(stream: 'stdout' | 'stderr', text: string): boolean {
|
|
65
71
|
const current = sinks.getStore()
|
|
66
72
|
if (!current) return false
|
|
@@ -112,20 +118,20 @@ function installCapture(): void {
|
|
|
112
118
|
}
|
|
113
119
|
}
|
|
114
120
|
|
|
115
|
-
// run one
|
|
121
|
+
// run one peach invocation and return what it wrote plus its exit code, instead
|
|
116
122
|
// of writing to the terminal and ending the process. `argv` is the command and
|
|
117
123
|
// its arguments with no node/script prefix, e.g. ['describe', '--json'].
|
|
118
|
-
export async function
|
|
124
|
+
export async function runPeach(
|
|
119
125
|
argv: string[],
|
|
120
126
|
dispatch: (argv: string[]) => Promise<number>,
|
|
121
|
-
): Promise<
|
|
127
|
+
): Promise<PeachRunResult> {
|
|
122
128
|
installCapture()
|
|
123
129
|
const current: RnxSinks = { stdout: [], stderr: [] }
|
|
124
130
|
const exitCode = await sinks.run(current, async () => {
|
|
125
131
|
try {
|
|
126
132
|
return await dispatch(argv)
|
|
127
133
|
} catch (error) {
|
|
128
|
-
if (error instanceof
|
|
134
|
+
if (error instanceof PeachExit) return error.code
|
|
129
135
|
throw error
|
|
130
136
|
}
|
|
131
137
|
})
|
|
@@ -135,3 +141,5 @@ export async function runRnx(
|
|
|
135
141
|
exitCode,
|
|
136
142
|
}
|
|
137
143
|
}
|
|
144
|
+
|
|
145
|
+
export const runRnx = runPeach
|
package/cli/send-to-box.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { createCloudBox } from './cloud-client.ts'
|
|
|
16
16
|
import type { StorageSnapshot } from '../../peach-engine/src/preview/storage-snapshot-contract.ts'
|
|
17
17
|
import type { DeviceModel } from '../../peach-engine/src/settings/devices.ts'
|
|
18
18
|
import type { BridgeCommandType } from '../src/bridge-contract.ts'
|
|
19
|
+
import { isRecord } from '../src/is-record'
|
|
19
20
|
|
|
20
21
|
export interface SendToBoxOptions {
|
|
21
22
|
/** runs one bridge command against the sim running the app. */
|
|
@@ -44,10 +45,6 @@ export interface SendToBoxResult {
|
|
|
44
45
|
warnings: string[]
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
48
|
-
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
48
|
// the box opens on the device the sender was looking at. a remote simulator is
|
|
52
49
|
// iOS only, so an Android sim sends to the default iPhone rather than refusing.
|
|
53
50
|
function cloudDevice(capturedModel: unknown): DeviceModel {
|
package/cli/telemetry.ts
CHANGED
package/cli/ws-bridge.ts
CHANGED
|
@@ -416,11 +416,7 @@ export function formatLockOwner(owner: string): string {
|
|
|
416
416
|
const source = owner.slice(0, colon)
|
|
417
417
|
const value = owner.slice(colon + 1)
|
|
418
418
|
const short = value.length > 12 ? `${value.slice(0, 8)}…` : value
|
|
419
|
-
if (
|
|
420
|
-
source === 'TM_SESSION' ||
|
|
421
|
-
source === 'CLAUDE_CODE_SESSION_ID' ||
|
|
422
|
-
source === 'CODEX_THREAD_ID'
|
|
423
|
-
) {
|
|
419
|
+
if (source === 'CLAUDE_CODE_SESSION_ID' || source === 'CODEX_THREAD_ID') {
|
|
424
420
|
return `agent ${short}`
|
|
425
421
|
}
|
|
426
422
|
return `${source} ${short}`
|
package/detox/gestures.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import { scrollTouchSlop } from '../../peach-engine/src/engine/scroll-platform'
|
|
1
2
|
import { pollUntil } from '../src/poll-until'
|
|
2
3
|
// pull in @peach/globals so window.__sootsimTest and
|
|
3
4
|
// window.__sootsimFindInteractiveCanvas are typed inside page.evaluate
|
|
4
5
|
// bodies. all sootsim-namespaced globals are declared canonically there —
|
|
5
|
-
// add new entries to packages/
|
|
6
|
+
// add new entries to packages/peach-globals/src/index.ts, not here.
|
|
6
7
|
import type {} from '@peach/globals'
|
|
7
8
|
import type { Page } from 'playwright'
|
|
8
9
|
|
|
10
|
+
export const DETOX_PLATFORM =
|
|
11
|
+
process.env.PEACH_PLATFORM === 'android' ? ('android' as const) : ('ios' as const)
|
|
12
|
+
|
|
9
13
|
// shape of the surface-metrics bridge response we care about. the bridge
|
|
10
14
|
// types it as `Record<string, unknown> | null` (the contract is open) so we
|
|
11
15
|
// narrow at the call sites by casting to this local view.
|
|
@@ -333,25 +337,28 @@ export async function dragScrollNode(
|
|
|
333
337
|
const startSootSimY = node.absolutePosition.y + node.layout.height * normalizedStartY
|
|
334
338
|
let endSootSimX = startSootSimX
|
|
335
339
|
let endSootSimY = startSootSimY
|
|
340
|
+
// detox scrolls the content by exactly `pixels`, so the drag also covers the
|
|
341
|
+
// touch slop the scroll view consumes before it moves.
|
|
342
|
+
const travel = pixels + scrollTouchSlop(DETOX_PLATFORM, 1)
|
|
336
343
|
|
|
337
344
|
switch (direction) {
|
|
338
345
|
case 'down':
|
|
339
|
-
endSootSimY -=
|
|
346
|
+
endSootSimY -= travel
|
|
340
347
|
break
|
|
341
348
|
case 'up':
|
|
342
|
-
endSootSimY +=
|
|
349
|
+
endSootSimY += travel
|
|
343
350
|
break
|
|
344
351
|
case 'left':
|
|
345
|
-
endSootSimX +=
|
|
352
|
+
endSootSimX += travel
|
|
346
353
|
break
|
|
347
354
|
case 'right':
|
|
348
|
-
endSootSimX -=
|
|
355
|
+
endSootSimX -= travel
|
|
349
356
|
break
|
|
350
357
|
}
|
|
351
358
|
|
|
352
359
|
const startPage = await sootsimToPage(page, startSootSimX, startSootSimY)
|
|
353
360
|
const endPage = await sootsimToPage(page, endSootSimX, endSootSimY)
|
|
354
|
-
const steps = Math.max(16, Math.ceil(
|
|
361
|
+
const steps = Math.max(16, Math.ceil(travel / 8))
|
|
355
362
|
|
|
356
363
|
await page.mouse.move(startPage.x, startPage.y)
|
|
357
364
|
await page.mouse.down()
|
|
@@ -364,6 +371,13 @@ export async function dragScrollNode(
|
|
|
364
371
|
await page.waitForTimeout(24)
|
|
365
372
|
}
|
|
366
373
|
|
|
374
|
+
// detox's scroll never flings on device: ios turns deceleration off for the
|
|
375
|
+
// action's drag and android's linear swiper waits out any fling. this drag
|
|
376
|
+
// runs right at the momentum floor, so lifting straight off the last move
|
|
377
|
+
// flings or settles by event timing. resting past the release estimator's
|
|
378
|
+
// idle cutoff lifts with zero velocity, and the content stops where the
|
|
379
|
+
// drag ended.
|
|
380
|
+
await page.waitForTimeout(300)
|
|
367
381
|
await page.mouse.up()
|
|
368
382
|
await page.waitForTimeout(180)
|
|
369
383
|
}
|