peachsim 0.1.627 → 0.1.628
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/cli/bin.ts +6 -2
- package/cli/bridge-flow-runner.ts +79 -72
- package/cli/commands/cleanup.ts +6 -6
- package/cli/commands/control.ts +4 -5
- package/cli/commands/detox.ts +25 -11
- package/cli/commands/flow.ts +3 -2
- package/cli/commands/inspect.ts +22 -37
- package/cli/commands/mode.ts +11 -7
- package/cli/commands/report-issue.ts +1 -1
- package/cli/commands/setup.ts +16 -11
- package/cli/commands/show-touches-mode.ts +33 -0
- package/cli/commands/skills.ts +16 -0
- package/cli/commands/test.ts +8 -1
- package/cli/commands/welcome.ts +3 -3
- package/cli/drivers/playwright-sim-host.ts +6 -5
- package/cli/help-core.ts +9 -4
- package/cli/internal-child.ts +14 -2
- package/cli/levenshtein.ts +27 -0
- package/cli/main.ts +28 -18
- package/cli/ws-bridge.ts +3 -2
- package/detox/index.ts +234 -7
- 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 +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- 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 +170 -13
- package/dist-lib/dev-bundle-resolution.cjs +2 -2
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +42 -37
- 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 +44 -5
- 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 +7 -1
- package/dist-lib/menu.mjs +7 -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 +43 -4
- 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 +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +140 -149
- 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 +7234 -7069
- package/dist-lib/vite-kotlin.cjs +1 -1
- package/dist-lib/vite-swift.cjs +1 -1
- package/dist-lib/vite.cjs +43 -4
- package/package.json +1 -1
- package/skills/contrast/SKILL.md +1 -1
- package/src/dev-bundle-resolution.ts +1 -1
- package/src/host/bridge-host.ts +2 -34
- package/src/menu.ts +7 -0
- package/src/native-seam-manifest.ts +4 -16
- package/src/runtime-assets.ts +59 -2
- package/src/vite-plugin.ts +723 -25
package/dist-lib/vite-kotlin.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.628 | (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/dist-lib/vite-swift.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.628 | (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/dist-lib/vite.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.628 | (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;
|
|
@@ -661,7 +661,11 @@ var MIME_TYPES = {
|
|
|
661
661
|
".woff": "font/woff",
|
|
662
662
|
".woff2": "font/woff2",
|
|
663
663
|
".mp3": "audio/mpeg",
|
|
664
|
-
".wav": "audio/wav"
|
|
664
|
+
".wav": "audio/wav",
|
|
665
|
+
".mp4": "video/mp4",
|
|
666
|
+
".m4v": "video/mp4",
|
|
667
|
+
".mov": "video/quicktime",
|
|
668
|
+
".webm": "video/webm"
|
|
665
669
|
};
|
|
666
670
|
var ROOT_RUNTIME_PATHS = [
|
|
667
671
|
"/assets/",
|
|
@@ -675,7 +679,7 @@ var ROOT_RUNTIME_PATHS = [
|
|
|
675
679
|
"/spike/",
|
|
676
680
|
"/test-wallpaper.jpg",
|
|
677
681
|
"/preview-sw.js",
|
|
678
|
-
"/
|
|
682
|
+
"/peach.svg",
|
|
679
683
|
"/bedrock-sqlite.wasm",
|
|
680
684
|
"/bedrock-sqlite.js",
|
|
681
685
|
"/sootsim/"
|
|
@@ -759,7 +763,42 @@ function serveRuntimeFile(req, res, fullPath) {
|
|
|
759
763
|
res.end();
|
|
760
764
|
return;
|
|
761
765
|
}
|
|
762
|
-
|
|
766
|
+
sendFileWithRanges(req, res, fullPath, stat.size, {});
|
|
767
|
+
}
|
|
768
|
+
function sendFileWithRanges(req, res, filePath, total, headers) {
|
|
769
|
+
const head = (req?.method || "GET").toUpperCase() === "HEAD";
|
|
770
|
+
const range = /^bytes=(\d*)-(\d*)$/.exec(req?.headers?.range || "");
|
|
771
|
+
const invalid = range && range[1] && range[2] && Number(range[2]) < Number(range[1]);
|
|
772
|
+
if (range && (range[1] || range[2]) && !invalid) {
|
|
773
|
+
const [, startRaw, endRaw] = range;
|
|
774
|
+
const start = startRaw ? Number(startRaw) : Math.max(0, total - Number(endRaw));
|
|
775
|
+
const end = startRaw && endRaw ? Math.min(Number(endRaw), total - 1) : total - 1;
|
|
776
|
+
if (start > end || start >= total) {
|
|
777
|
+
res.writeHead(416, { ...headers, "Content-Range": `bytes */${total}` });
|
|
778
|
+
res.end();
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
res.writeHead(206, {
|
|
782
|
+
...headers,
|
|
783
|
+
"Content-Range": `bytes ${start}-${end}/${total}`,
|
|
784
|
+
"Accept-Ranges": "bytes",
|
|
785
|
+
"Content-Length": String(end - start + 1)
|
|
786
|
+
});
|
|
787
|
+
if (head) res.end();
|
|
788
|
+
else streamFile(res, import_fs.default.createReadStream(filePath, { start, end }));
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
res.writeHead(200, {
|
|
792
|
+
...headers,
|
|
793
|
+
"Accept-Ranges": "bytes",
|
|
794
|
+
"Content-Length": String(total)
|
|
795
|
+
});
|
|
796
|
+
if (head || total === 0) res.end();
|
|
797
|
+
else streamFile(res, import_fs.default.createReadStream(filePath, { start: 0, end: total - 1 }));
|
|
798
|
+
}
|
|
799
|
+
function streamFile(res, stream) {
|
|
800
|
+
stream.on("error", (error) => res.destroy(error));
|
|
801
|
+
stream.pipe(res);
|
|
763
802
|
}
|
|
764
803
|
|
|
765
804
|
// src/vite-plugin-one.ts
|
package/package.json
CHANGED
package/skills/contrast/SKILL.md
CHANGED
|
@@ -35,7 +35,7 @@ automation, inspecting the render tree, and managing sims.
|
|
|
35
35
|
| `peach do` | drive the app (tap, type, scroll, swipe, key, reload, …) |
|
|
36
36
|
| `peach wait` | block until the runtime reaches a known state |
|
|
37
37
|
| `peach shell` | drive shell state and simulator chrome |
|
|
38
|
-
| `peach mode` | toggle screenshot and 3D display modes |
|
|
38
|
+
| `peach mode` | toggle screenshot, touch indicators, and 3D display modes |
|
|
39
39
|
| `peach device` | inspect or change the live device preset for a sim |
|
|
40
40
|
| `peach network` | inspect live network traffic from the running peach worker |
|
|
41
41
|
| `peach logs` | inspect live console output from the running peach worker |
|
|
@@ -20,7 +20,7 @@ export interface ResolvedDevBundle {
|
|
|
20
20
|
|
|
21
21
|
function buildUnresolvedTargetError(targetLabel: string): Error {
|
|
22
22
|
return new Error(
|
|
23
|
-
`could not resolve a native bundle for ${targetLabel}. pass an explicit bundle URL or open Connect and choose the app there.`,
|
|
23
|
+
`could not resolve a native bundle for ${targetLabel}. start the app's dev server (for example npx expo start), pass an explicit bundle URL, or open Connect and choose the app there.`,
|
|
24
24
|
)
|
|
25
25
|
}
|
|
26
26
|
|
package/src/host/bridge-host.ts
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
resolveDevCheckoutRuntimeRoot,
|
|
42
42
|
resolveRuntimeRootForHostname,
|
|
43
43
|
runtimeVersionFromHostname,
|
|
44
|
+
sendFileWithRanges,
|
|
44
45
|
} from '../runtime-assets.ts'
|
|
45
46
|
import { rnxRuntime } from '../runtime-delivery.ts'
|
|
46
47
|
import { AgentHost, type AgentHostOptions } from './agent-host.ts'
|
|
@@ -1304,43 +1305,10 @@ export class SootSimBridgeHost {
|
|
|
1304
1305
|
|
|
1305
1306
|
const contentType =
|
|
1306
1307
|
HTTP_MIME_TYPES[path.extname(filePath).toLowerCase()] || 'application/octet-stream'
|
|
1307
|
-
|
|
1308
|
-
const range = /^bytes=(\d*)-(\d*)$/.exec(req.headers.range || '')
|
|
1309
|
-
if (range) {
|
|
1310
|
-
const [, startRaw, endRaw] = range
|
|
1311
|
-
const start = startRaw ? Number(startRaw) : 0
|
|
1312
|
-
const end = endRaw ? Math.min(Number(endRaw), total - 1) : total - 1
|
|
1313
|
-
if (!Number.isFinite(start) || start > end || start >= total) {
|
|
1314
|
-
res.writeHead(416, { ...cors, 'Content-Range': `bytes */${total}` })
|
|
1315
|
-
res.end()
|
|
1316
|
-
return
|
|
1317
|
-
}
|
|
1318
|
-
res.writeHead(206, {
|
|
1319
|
-
...cors,
|
|
1320
|
-
'Content-Type': contentType,
|
|
1321
|
-
'Content-Range': `bytes ${start}-${end}/${total}`,
|
|
1322
|
-
'Accept-Ranges': 'bytes',
|
|
1323
|
-
'Content-Length': String(end - start + 1),
|
|
1324
|
-
})
|
|
1325
|
-
if (method === 'HEAD') {
|
|
1326
|
-
res.end()
|
|
1327
|
-
return
|
|
1328
|
-
}
|
|
1329
|
-
fs.createReadStream(filePath, { start, end }).pipe(res)
|
|
1330
|
-
return
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
res.writeHead(200, {
|
|
1308
|
+
sendFileWithRanges(req, res, filePath, stats.size, {
|
|
1334
1309
|
...cors,
|
|
1335
1310
|
'Content-Type': contentType,
|
|
1336
|
-
'Accept-Ranges': 'bytes',
|
|
1337
|
-
'Content-Length': String(total),
|
|
1338
1311
|
})
|
|
1339
|
-
if (method === 'HEAD') {
|
|
1340
|
-
res.end()
|
|
1341
|
-
return
|
|
1342
|
-
}
|
|
1343
|
-
fs.createReadStream(filePath).pipe(res)
|
|
1344
1312
|
}
|
|
1345
1313
|
|
|
1346
1314
|
private handleServerScan(res: ServerResponse) {
|
package/src/menu.ts
CHANGED
|
@@ -80,6 +80,7 @@ export interface SootSimMenuState {
|
|
|
80
80
|
// can reach.
|
|
81
81
|
developmentMode: boolean
|
|
82
82
|
showBezels: boolean
|
|
83
|
+
showTouches: boolean
|
|
83
84
|
showMenuBar: boolean
|
|
84
85
|
showRail: boolean
|
|
85
86
|
solidWindow: boolean
|
|
@@ -493,6 +494,12 @@ function windowMenu(state: SootSimMenuState): SootSimMenuDef {
|
|
|
493
494
|
checked: state.showBezels,
|
|
494
495
|
action: 'toggle-bezels',
|
|
495
496
|
},
|
|
497
|
+
{
|
|
498
|
+
label: 'Show Touches',
|
|
499
|
+
type: 'checkbox',
|
|
500
|
+
checked: state.showTouches,
|
|
501
|
+
action: 'toggle-touches',
|
|
502
|
+
},
|
|
496
503
|
{
|
|
497
504
|
label: 'Show Menu Bar',
|
|
498
505
|
type: 'checkbox',
|
|
@@ -275,25 +275,13 @@ export const NATIVE_SEAM_MANIFEST: NativeSeamEntry[] = [
|
|
|
275
275
|
},
|
|
276
276
|
{
|
|
277
277
|
pkg: 'expo-image',
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
// the view-component seam translates expo-image's prop shape
|
|
282
|
-
// (contentFit, tintColor, source) onto sootsim's existing
|
|
283
|
-
// <sootsim-image> host. no new canvas component is needed — sootsim's
|
|
284
|
-
// image-loader + canvaskit-renderer already handle resizeMode,
|
|
285
|
-
// tintColor, and onLoad/onError lifecycle.
|
|
286
|
-
seamBasenames: ['ExpoImage'],
|
|
287
|
-
target: seamPath('expo-image'),
|
|
288
|
-
notes:
|
|
289
|
-
'expo-image native view — thin prop translator onto sootsim-image host (contentFit→resizeMode, tintColor passthrough); upstream Image.tsx + ImageBackground + useImage resolve from node_modules unchanged',
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
pkg: 'expo-image',
|
|
278
|
+
// src/ImageModule.ts → requireNativeModule('ExpoImage'). the native view
|
|
279
|
+
// is src/ExpoImage.tsx's requireNativeViewManager('ExpoImage') call inside
|
|
280
|
+
// a real JS component, so it comes from expo-image-register instead.
|
|
293
281
|
seamBasenames: ['ImageModule'],
|
|
294
282
|
target: seamPath('expo-image-module'),
|
|
295
283
|
notes:
|
|
296
|
-
'expo-image native module — prefetch/loadAsync route through
|
|
284
|
+
'expo-image native module — prefetch/loadAsync/readFromCacheAsync route through the engine image loader, blurhash and thumbhash generation run the swift encoders, cache configuration has nothing to set in a browser',
|
|
297
285
|
},
|
|
298
286
|
{
|
|
299
287
|
pkg: '@notifee/react-native',
|
package/src/runtime-assets.ts
CHANGED
|
@@ -7,6 +7,7 @@ import path from 'path'
|
|
|
7
7
|
import { fileURLToPath } from 'url'
|
|
8
8
|
import { isValidRuntimeVersion } from '@contrast/runtime-delivery'
|
|
9
9
|
import { readActiveRuntime, runtimeDir } from './home-paths.ts'
|
|
10
|
+
import type { ServerResponse } from 'http'
|
|
10
11
|
|
|
11
12
|
export const SOOTSIM_RUNTIME_MISSING_MESSAGE =
|
|
12
13
|
'[peach] no engine runtime installed. run `peach runtime install`'
|
|
@@ -97,6 +98,10 @@ const MIME_TYPES: Record<string, string> = {
|
|
|
97
98
|
'.woff2': 'font/woff2',
|
|
98
99
|
'.mp3': 'audio/mpeg',
|
|
99
100
|
'.wav': 'audio/wav',
|
|
101
|
+
'.mp4': 'video/mp4',
|
|
102
|
+
'.m4v': 'video/mp4',
|
|
103
|
+
'.mov': 'video/quicktime',
|
|
104
|
+
'.webm': 'video/webm',
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
const ROOT_RUNTIME_PATHS = [
|
|
@@ -111,7 +116,7 @@ const ROOT_RUNTIME_PATHS = [
|
|
|
111
116
|
'/spike/',
|
|
112
117
|
'/test-wallpaper.jpg',
|
|
113
118
|
'/preview-sw.js',
|
|
114
|
-
'/
|
|
119
|
+
'/peach.svg',
|
|
115
120
|
'/bedrock-sqlite.wasm',
|
|
116
121
|
'/bedrock-sqlite.js',
|
|
117
122
|
'/sootsim/',
|
|
@@ -238,5 +243,57 @@ export function serveRuntimeFile(req: any, res: any, fullPath: string) {
|
|
|
238
243
|
res.end()
|
|
239
244
|
return
|
|
240
245
|
}
|
|
241
|
-
|
|
246
|
+
sendFileWithRanges(req, res, fullPath, stat.size, {})
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// a file body that honours a single `bytes=` range. a media element can only
|
|
250
|
+
// seek a resource whose server answers ranges, so any file a <video> or
|
|
251
|
+
// <audio> might load goes through here.
|
|
252
|
+
export function sendFileWithRanges(
|
|
253
|
+
req: { method?: string; headers?: { range?: string } } | undefined,
|
|
254
|
+
res: ServerResponse,
|
|
255
|
+
filePath: string,
|
|
256
|
+
total: number,
|
|
257
|
+
headers: Record<string, string>,
|
|
258
|
+
) {
|
|
259
|
+
const head = (req?.method || 'GET').toUpperCase() === 'HEAD'
|
|
260
|
+
const range = /^bytes=(\d*)-(\d*)$/.exec(req?.headers?.range || '')
|
|
261
|
+
// a last byte before the first makes the header invalid, which a server
|
|
262
|
+
// ignores (RFC 9110 14.1.1)
|
|
263
|
+
const invalid = range && range[1] && range[2] && Number(range[2]) < Number(range[1])
|
|
264
|
+
if (range && (range[1] || range[2]) && !invalid) {
|
|
265
|
+
const [, startRaw, endRaw] = range
|
|
266
|
+
// a suffix range (bytes=-N) is the last N bytes
|
|
267
|
+
const start = startRaw ? Number(startRaw) : Math.max(0, total - Number(endRaw))
|
|
268
|
+
const end = startRaw && endRaw ? Math.min(Number(endRaw), total - 1) : total - 1
|
|
269
|
+
if (start > end || start >= total) {
|
|
270
|
+
res.writeHead(416, { ...headers, 'Content-Range': `bytes */${total}` })
|
|
271
|
+
res.end()
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
res.writeHead(206, {
|
|
275
|
+
...headers,
|
|
276
|
+
'Content-Range': `bytes ${start}-${end}/${total}`,
|
|
277
|
+
'Accept-Ranges': 'bytes',
|
|
278
|
+
'Content-Length': String(end - start + 1),
|
|
279
|
+
})
|
|
280
|
+
if (head) res.end()
|
|
281
|
+
else streamFile(res, fs.createReadStream(filePath, { start, end }))
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
res.writeHead(200, {
|
|
285
|
+
...headers,
|
|
286
|
+
'Accept-Ranges': 'bytes',
|
|
287
|
+
'Content-Length': String(total),
|
|
288
|
+
})
|
|
289
|
+
// the body stops at the stat'd size so a file a rebuild is still growing
|
|
290
|
+
// cannot run past its Content-Length
|
|
291
|
+
if (head || total === 0) res.end()
|
|
292
|
+
else streamFile(res, fs.createReadStream(filePath, { start: 0, end: total - 1 }))
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// the status line is already out, so a read failure can only cut the body
|
|
296
|
+
function streamFile(res: ServerResponse, stream: fs.ReadStream) {
|
|
297
|
+
stream.on('error', (error) => res.destroy(error))
|
|
298
|
+
stream.pipe(res)
|
|
242
299
|
}
|