peachsim 0.1.577 → 0.1.579

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.
Files changed (178) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +11 -9
  3. package/cli/app-catalog.ts +77 -0
  4. package/cli/app-fonts.ts +4 -20
  5. package/cli/app-splash.ts +2 -18
  6. package/cli/bridge-flow-runner.ts +505 -139
  7. package/cli/cloud-client.ts +1 -4
  8. package/cli/cloud-session.ts +1 -4
  9. package/cli/cloud-test-result-files.ts +3 -0
  10. package/cli/cloud-test-tunnel.ts +2 -5
  11. package/cli/command-registry.ts +2 -0
  12. package/cli/commands/agent.ts +149 -23
  13. package/cli/commands/assert.ts +1 -4
  14. package/cli/commands/box/peach-command.ts +62 -0
  15. package/cli/commands/box/plane-share.ts +0 -70
  16. package/cli/commands/box/rnx-command.ts +1 -60
  17. package/cli/commands/box/search-command.ts +14 -0
  18. package/cli/commands/box.ts +2 -2
  19. package/cli/commands/control.ts +121 -27
  20. package/cli/commands/cpu-profile.ts +23 -10
  21. package/cli/commands/daemon.ts +1 -9
  22. package/cli/commands/detox.ts +26 -18
  23. package/cli/commands/device.ts +129 -1
  24. package/cli/commands/film.ts +25 -16
  25. package/cli/commands/flow.ts +9 -2
  26. package/cli/commands/inspect/actions.ts +75 -1
  27. package/cli/commands/inspect/core.ts +81 -2
  28. package/cli/commands/inspect/describe.ts +2 -6
  29. package/cli/commands/inspect/env.ts +1 -1
  30. package/cli/commands/inspect/settling.ts +20 -3
  31. package/cli/commands/inspect/shared.ts +1 -4
  32. package/cli/commands/inspect/sleep.ts +1 -1
  33. package/cli/commands/inspect.ts +16 -49
  34. package/cli/commands/install-desktop.ts +8 -6
  35. package/cli/commands/login.ts +3 -5
  36. package/cli/commands/maestro-generate.ts +1 -4
  37. package/cli/commands/maestro.ts +38 -21
  38. package/cli/commands/no-bridge-hint.ts +0 -14
  39. package/cli/commands/permissions.ts +2 -2
  40. package/cli/commands/platform.ts +13 -6
  41. package/cli/commands/screenshot.ts +6 -1
  42. package/cli/commands/skills.ts +1 -1
  43. package/cli/commands/test.ts +6 -2
  44. package/cli/commands/three-mode.ts +162 -19
  45. package/cli/commands/upload.ts +3 -18
  46. package/cli/current-sim.ts +0 -1
  47. package/cli/desktop-companion.ts +40 -8
  48. package/cli/drivers/playwright-provisioning.ts +1 -4
  49. package/cli/flow-export.ts +1 -8
  50. package/cli/flow-session.ts +0 -4
  51. package/cli/hints.ts +1 -16
  52. package/cli/inspect-notice-state.ts +0 -4
  53. package/cli/main.ts +2 -4
  54. package/cli/outbound-endpoints.ts +1 -24
  55. package/cli/parse-args.ts +1 -2
  56. package/cli/rnx-in-process.ts +3 -1
  57. package/cli/run-rnx-core.ts +4 -2
  58. package/cli/run-rnx.ts +18 -10
  59. package/cli/send-to-box.ts +1 -4
  60. package/cli/telemetry.ts +1 -1
  61. package/cli/ws-bridge.ts +1 -5
  62. package/detox/gestures.ts +20 -6
  63. package/detox/index.ts +379 -123
  64. package/detox/navigation-lifecycle.ts +22 -13
  65. package/dist-lib/agent-daemon-client.cjs +21 -15
  66. package/dist-lib/agent-events.cjs +1 -1
  67. package/dist-lib/agent-identity.cjs +3 -17
  68. package/dist-lib/agent-sessions.cjs +5 -4
  69. package/dist-lib/attached-projects.cjs +1 -77
  70. package/dist-lib/auth/shared-session.cjs +1 -1
  71. package/dist-lib/backend-origin.cjs +11 -1
  72. package/dist-lib/beta.cjs +1 -1
  73. package/dist-lib/beta.mjs +1 -1
  74. package/dist-lib/bridge-constants.cjs +1 -1
  75. package/dist-lib/bridge-contract-input.cjs +7 -5
  76. package/dist-lib/bridge-contract-input.mjs +7 -5
  77. package/dist-lib/bridge-contract.cjs +1 -1
  78. package/dist-lib/bridge-contract.mjs +1 -1
  79. package/dist-lib/capture-contract.cjs +30 -7
  80. package/dist-lib/capture-contract.mjs +30 -7
  81. package/dist-lib/cli-constants.cjs +1 -1
  82. package/dist-lib/cloud-contract.cjs +1 -1
  83. package/dist-lib/cloud-contract.mjs +1 -1
  84. package/dist-lib/cloud-test-args.cjs +1 -1
  85. package/dist-lib/cloud-test-args.mjs +1 -1
  86. package/dist-lib/cloud.cjs +17 -11
  87. package/dist-lib/cloud.mjs +17 -11
  88. package/dist-lib/config.cjs +1 -1
  89. package/dist-lib/detox/index.cjs +321 -122
  90. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  91. package/dist-lib/home-paths.cjs +1 -1
  92. package/dist-lib/host/bridge-host.cjs +427 -212
  93. package/dist-lib/host/fetch-proxy-handler.cjs +5 -28
  94. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  95. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  96. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  97. package/dist-lib/host/websocket-proxy.cjs +1 -1
  98. package/dist-lib/index.cjs +315 -58
  99. package/dist-lib/jump-to-source-babel.cjs +1 -1
  100. package/dist-lib/jump-to-source-native.cjs +1 -1
  101. package/dist-lib/menu.cjs +67 -31
  102. package/dist-lib/menu.mjs +64 -28
  103. package/dist-lib/metro-fingerprint-registry.cjs +8 -4
  104. package/dist-lib/metro-fingerprint-registry.mjs +6 -4
  105. package/dist-lib/metro-production-bundle.cjs +2 -2
  106. package/dist-lib/metro-production-bundle.mjs +2 -2
  107. package/dist-lib/metro.cjs +8 -4
  108. package/dist-lib/profiles.cjs +1 -1
  109. package/dist-lib/public-brand.cjs +7 -4
  110. package/dist-lib/react-native-host-modules.cjs +1 -1
  111. package/dist-lib/react-native-host-modules.mjs +1 -1
  112. package/dist-lib/render-mode.cjs +3 -13
  113. package/dist-lib/scripts/dev-server-scanner.cjs +5 -17
  114. package/dist-lib/sdk.cjs +129 -10
  115. package/dist-lib/sdk.mjs +127 -10
  116. package/dist-lib/skills.cjs +1635 -1121
  117. package/dist-lib/swift-compile.cjs +277 -70
  118. package/dist-lib/swift-compile.mjs +275 -70
  119. package/dist-lib/swift-package.cjs +17 -5
  120. package/dist-lib/swift-package.mjs +15 -4
  121. package/dist-lib/swift-submit.cjs +32 -8
  122. package/dist-lib/swift-submit.mjs +29 -7
  123. package/dist-lib/swift.cjs +30912 -577
  124. package/dist-lib/vite-kotlin.cjs +356 -0
  125. package/dist-lib/vite-swift.cjs +1743 -0
  126. package/dist-lib/vite.cjs +208 -1039
  127. package/kotlin/README.md +18 -0
  128. package/kotlin/Sources/androidx/annotation/Keep.kt +4 -0
  129. package/kotlin/Sources/dev/onejs/one/OneNativeSourceDispatch.kt +7 -0
  130. package/kotlin/Sources/dev/onejs/one/source/OneModule.kt +4 -0
  131. package/kotlin/Sources/org/json/JSON.kt +244 -0
  132. package/kotlin/toolchain.json +19 -0
  133. package/package.json +14 -2
  134. package/scripts/dev-server-scanner.ts +0 -18
  135. package/scripts/optional-demo-registry.ts +0 -17
  136. package/skills/contrast/SKILL.md +2 -2
  137. package/skills/peach-setup/SKILL.md +16 -0
  138. package/src/agent-daemon-client.ts +36 -22
  139. package/src/agent-identity.ts +0 -15
  140. package/src/agent-prompt.ts +12 -0
  141. package/src/attached-projects.ts +0 -72
  142. package/src/backend-origin.ts +10 -0
  143. package/src/bridge-contract-input.ts +2 -5
  144. package/src/bridge-contract.ts +2 -0
  145. package/src/capture-contract.ts +53 -11
  146. package/src/cloud.ts +4 -9
  147. package/src/dev-server-open.ts +0 -99
  148. package/src/home-paths.ts +2 -3
  149. package/src/host/agent-host.ts +82 -36
  150. package/src/host/bridge-host.ts +5 -11
  151. package/src/host/fetch-proxy-handler.ts +19 -43
  152. package/src/is-record.ts +4 -0
  153. package/src/menu.ts +82 -37
  154. package/src/metro-fingerprint-registry.ts +1 -4
  155. package/src/metro-production-bundle.ts +1 -1
  156. package/src/plist.ts +1 -4
  157. package/src/public-brand.ts +5 -3
  158. package/src/render-mode.ts +0 -12
  159. package/src/runtime-assets.ts +9 -2
  160. package/src/screenshots/compose.ts +7 -1
  161. package/src/screenshots/frame-compose.ts +21 -10
  162. package/src/screenshots/schema.ts +1 -4
  163. package/src/sim-client.ts +2 -4
  164. package/src/skills/builtin/maestro-test.ts +1 -4
  165. package/src/sleep.ts +4 -0
  166. package/src/swift-compile.ts +209 -47
  167. package/src/swift-icu.ts +140 -0
  168. package/src/swift-package.ts +26 -2
  169. package/src/swift-submit.ts +84 -4
  170. package/src/swift.ts +2 -0
  171. package/src/vite-plugin-kotlin.ts +376 -0
  172. package/src/vite-plugin-one.ts +224 -36
  173. package/src/vite-plugin-swift.ts +382 -96
  174. package/src/vite-plugin.ts +121 -134
  175. package/src/vite.d.ts +10 -0
  176. package/cli/resolve-assets.ts +0 -63
  177. package/src/rolldown-plugin-swift.ts +0 -244
  178. package/src/team-machine-prompt.ts +0 -73
@@ -8,14 +8,16 @@
8
8
 
9
9
  import fs from 'fs'
10
10
  import { randomUUID } from 'node:crypto'
11
+ import { createRequire } from 'node:module'
11
12
  import path from 'path'
13
+ import { swiftPackageDirectories } from 'vxrn'
14
+ import { WebSocket, WebSocketServer } from 'ws'
12
15
  import { DEFAULT_SOOTSIM_BRIDGE_PORT } from './bridge-constants'
13
16
  import {
14
17
  launchRnxDesktop,
15
18
  resolveRnxDesktopAppName,
16
19
  resolveRnxDesktopLaunchTarget,
17
20
  tagRnxBundleForPlugin,
18
- type RnxDesktopOpenOption,
19
21
  } from './dev-server-open'
20
22
  import { isDaemonLockfileFresh, readDaemonLockfile } from './home-paths'
21
23
  import { handleReplacementModuleRequest } from './host/replacement-module-handler'
@@ -26,21 +28,23 @@ import {
26
28
  serveRuntimeFile,
27
29
  SOOTSIM_RUNTIME_MISSING_MESSAGE,
28
30
  } from './runtime-assets'
31
+ import type { PeachPluginOptions } from './vite'
29
32
  import type { Plugin } from 'vite'
30
33
 
31
- export interface RnxPluginOptions {
32
- // custom bundle URL (default: auto-detect from One's metro)
33
- bundleUrl?: string
34
- // path prefix (default: '/__soot')
35
- prefix?: string
36
- // disable sootsim
37
- enabled?: boolean
38
- // launch the peach simulator loading this server's bundle once the dev
39
- // server is listening. skipped when CI or PEACH_NO_OPEN is set.
40
- open?: RnxDesktopOpenOption
41
- }
34
+ const SWIFT_HMR_PATH = '/__peach/swift-hmr'
35
+ // every swift route sits under this prefix. the swift host (compiler client,
36
+ // resource packer, module glue) loads on the first request for one, so an app
37
+ // that imports no swift never evaluates it.
38
+ const SWIFT_ROUTE_PREFIX = '/__peach/swift'
39
+ let swiftHost: Promise<typeof import('peachsim/vite-swift')> | undefined
40
+ const loadSwiftHost = () => (swiftHost ??= import('peachsim/vite-swift'))
41
+ // the Kotlin host (toolchain fetch, compile, artifact serving) loads the same
42
+ // way, on the first /__peach/kotlin request from an Android device
43
+ const KOTLIN_ROUTE_PREFIX = '/__peach/kotlin'
44
+ let kotlinHost: Promise<typeof import('peachsim/vite-kotlin')> | undefined
45
+ const loadKotlinHost = () => (kotlinHost ??= import('peachsim/vite-kotlin'))
42
46
 
43
- export function rnxPlugin(options: RnxPluginOptions = {}): Plugin {
47
+ export function rnxPlugin(options: PeachPluginOptions = {}): Plugin {
44
48
  const prefix = options.prefix || '/__soot'
45
49
 
46
50
  return {
@@ -60,24 +64,223 @@ export function rnxPlugin(options: RnxPluginOptions = {}): Plugin {
60
64
 
61
65
  configureServer(server) {
62
66
  if (options.enabled === false) return
63
- const bundleUrl =
64
- options.bundleUrl ||
65
- '/node_modules/one/metro-entry.bundle?platform=ios&dev=true&minify=false'
66
- const appName = resolveRnxDesktopAppName(options.open, server.config.root)
67
- const replacementToken = randomUUID()
67
+ let swiftPackages: Map<string, string> | undefined
68
+ // the workspace SwiftUI module beside peachsim compiles locally; an
69
+ // npm install has none (null), and its packages compile on Contrast.
70
+ let swiftModuleRoot: string | null | undefined
71
+ const moduleRootFor = (): string | undefined => {
72
+ if (swiftModuleRoot === undefined) {
73
+ const peachsimPackage = createRequire(
74
+ path.join(server.config.root, 'package.json'),
75
+ ).resolve('peachsim/package.json')
76
+ const moduleRoot = path.resolve(
77
+ path.dirname(peachsimPackage),
78
+ '../sootsim-swift',
79
+ )
80
+ swiftModuleRoot = fs.existsSync(path.join(moduleRoot, 'toolchain.json'))
81
+ ? moduleRoot
82
+ : null
83
+ }
84
+ return swiftModuleRoot ?? undefined
85
+ }
86
+ const activeSwiftPackages = new Set<string>()
87
+ const packageMap = () => {
88
+ swiftPackages ??= swiftPackageDirectories(server.config.root)
89
+ return swiftPackages
90
+ }
91
+ const wsOptions = server.config.server.ws
92
+ const httpServer =
93
+ server.httpServer ??
94
+ (wsOptions && typeof wsOptions === 'object' ? wsOptions.server : undefined)
95
+ const swiftSockets = httpServer
96
+ ? new WebSocketServer({ noServer: true })
97
+ : undefined
98
+ const hmrPath = SWIFT_HMR_PATH
68
99
  const configuredPort = server.config.server.port || 8081
69
100
  const listeningPort = (): number => {
70
101
  const address = server.httpServer?.address()
71
102
  return address && typeof address === 'object' ? address.port : configuredPort
72
103
  }
73
- const bundleForPort = (port: number): string =>
74
- tagRnxBundleForPlugin(
75
- bundleUrl,
104
+ const devOrigin = () => `http://localhost:${listeningPort()}`
105
+ const sendSwiftChange = (change: object) => {
106
+ const message = JSON.stringify(change)
107
+ for (const client of swiftSockets?.clients ?? []) {
108
+ if (client.readyState === WebSocket.OPEN) client.send(message)
109
+ }
110
+ }
111
+ if (httpServer && swiftSockets) {
112
+ httpServer.on('upgrade', (req, socket, head) => {
113
+ if (new URL(req.url ?? '/', 'http://localhost').pathname !== SWIFT_HMR_PATH)
114
+ return
115
+ swiftSockets.handleUpgrade(req, socket, head, (client) => {
116
+ swiftSockets.emit('connection', client, req)
117
+ })
118
+ })
119
+ httpServer.once('close', () => {
120
+ for (const client of swiftSockets.clients) client.terminate()
121
+ swiftSockets.close()
122
+ })
123
+ }
124
+ server.watcher.on('add', (file) => {
125
+ if (path.basename(file) === 'Package.swift') swiftPackages = undefined
126
+ })
127
+ server.watcher.on('unlink', (file) => {
128
+ if (path.basename(file) === 'Package.swift') swiftPackages = undefined
129
+ })
130
+ // a Kotlin source edit reaches the next call: the seam drops the module
131
+ // it imported and asks for the app's Kotlin again
132
+ let kotlinModuleRoot: string | undefined
133
+ server.watcher.on('all', (_event, file) => {
134
+ if (!kotlinModuleRoot) return
135
+ if (file.endsWith('.kt') || file.startsWith(`${kotlinModuleRoot}${path.sep}`))
136
+ sendSwiftChange({ type: 'kotlin', packageName: 'kotlin' })
137
+ })
138
+ server.middlewares.use((req, res, next) => {
139
+ const pathname = (req.url ?? '').split('?')[0]
140
+ if (!pathname.startsWith(KOTLIN_ROUTE_PREFIX)) return next()
141
+ loadKotlinHost()
142
+ .then(async (host) => {
143
+ if (host.serveKotlinArtifact(pathname, res)) return
144
+ if (pathname !== '/__peach/kotlin-package') return next()
145
+ if (!kotlinModuleRoot) {
146
+ const moduleRoot = path.join(
147
+ path.dirname(
148
+ createRequire(path.join(server.config.root, 'package.json')).resolve(
149
+ 'peachsim/package.json',
150
+ ),
151
+ ),
152
+ 'kotlin',
153
+ )
154
+ kotlinModuleRoot = moduleRoot
155
+ server.watcher.add(path.join(moduleRoot, 'Sources'))
156
+ }
157
+ try {
158
+ const artifact = await host.kotlinArtifact(
159
+ server.config.root,
160
+ kotlinModuleRoot,
161
+ devOrigin(),
162
+ )
163
+ res.setHeader('access-control-allow-origin', '*')
164
+ res.setHeader('content-type', 'application/json')
165
+ res.end(JSON.stringify({ ...artifact, hmrPath }))
166
+ } catch (error) {
167
+ res.statusCode = 500
168
+ res.setHeader('access-control-allow-origin', '*')
169
+ res.end(error instanceof Error ? error.message : String(error))
170
+ }
171
+ })
172
+ .catch(next)
173
+ })
174
+ server.watcher.on('change', (file) => {
175
+ if (activeSwiftPackages.size === 0) return
176
+ const moduleRoot = moduleRootFor()
177
+ let packages: Map<string, string>
178
+ try {
179
+ packages = packageMap()
180
+ } catch (error) {
181
+ for (const packageName of activeSwiftPackages) {
182
+ sendSwiftChange({
183
+ type: 'error',
184
+ packageName,
185
+ message: error instanceof Error ? error.message : String(error),
186
+ })
187
+ }
188
+ return
189
+ }
190
+ for (const packageName of activeSwiftPackages) {
191
+ const packagePath = packages.get(packageName)
192
+ if (!packagePath) continue
193
+ if (
194
+ !file.startsWith(`${packagePath}${path.sep}`) &&
195
+ !(moduleRoot && file.startsWith(`${moduleRoot}${path.sep}`))
196
+ )
197
+ continue
198
+ void loadSwiftHost()
199
+ .then((host) =>
200
+ host.swiftPackageMetadata(
201
+ packagePath,
202
+ packageName,
203
+ moduleRoot,
204
+ devOrigin(),
205
+ ),
206
+ )
207
+ .then((metadata) => {
208
+ sendSwiftChange({
209
+ type: 'package',
210
+ packageName,
211
+ metadata: { ...metadata, hmrPath },
212
+ })
213
+ })
214
+ .catch((error: unknown) => {
215
+ sendSwiftChange({
216
+ type: 'error',
217
+ packageName,
218
+ message: error instanceof Error ? error.message : String(error),
219
+ })
220
+ })
221
+ }
222
+ })
223
+ server.middlewares.use((req, res, next) => {
224
+ const pathname = (req.url ?? '').split('?')[0]
225
+ if (!pathname.startsWith(SWIFT_ROUTE_PREFIX)) return next()
226
+ loadSwiftHost()
227
+ .then((host) => {
228
+ if (host.serveSwiftPackageAsset(pathname, res)) return
229
+ const prefix = '/__peach/swift-package/'
230
+ if (!pathname.startsWith(prefix)) return next()
231
+ if (!swiftSockets) {
232
+ res.statusCode = 501
233
+ res.end('Peach Swift hot reload requires a Vite HTTP server')
234
+ return
235
+ }
236
+ const packageName = pathname.slice(prefix.length)
237
+ let packagePath: string | undefined
238
+ try {
239
+ packagePath = packageMap().get(packageName)
240
+ } catch (error) {
241
+ res.statusCode = 500
242
+ res.end(error instanceof Error ? error.message : String(error))
243
+ return
244
+ }
245
+ if (!packagePath) {
246
+ res.statusCode = 404
247
+ res.end(`swift package ${packageName} was not found`)
248
+ return
249
+ }
250
+ const moduleRoot = moduleRootFor()
251
+ activeSwiftPackages.add(packageName)
252
+ server.watcher.add(host.swiftPackageWatchFiles(packagePath, moduleRoot))
253
+ void host
254
+ .swiftPackageMetadata(packagePath, packageName, moduleRoot, devOrigin())
255
+ .then((metadata) => {
256
+ res.setHeader('access-control-allow-origin', '*')
257
+ res.setHeader('content-type', 'application/json')
258
+ res.end(JSON.stringify({ ...metadata, hmrPath }))
259
+ })
260
+ .catch((error: unknown) => {
261
+ res.statusCode = 500
262
+ res.end(error instanceof Error ? error.message : String(error))
263
+ })
264
+ })
265
+ .catch(next)
266
+ })
267
+ const bundleUrl =
268
+ options.bundleUrl ||
269
+ '/node_modules/one/metro-entry.bundle?platform=ios&dev=true&minify=false'
270
+ const appName = resolveRnxDesktopAppName(options.open, server.config.root)
271
+ const replacementToken = randomUUID()
272
+ const bundleForPort = (port: number): string => {
273
+ const fullBundleUrl = bundleUrl.startsWith('http')
274
+ ? bundleUrl
275
+ : `http://localhost:${port}${bundleUrl.startsWith('/') ? '' : '/'}${bundleUrl}`
276
+ return tagRnxBundleForPlugin(
277
+ fullBundleUrl,
76
278
  `http://localhost:${port}`,
77
279
  replacementToken,
78
280
  server.config.root,
79
281
  appName,
80
282
  )
283
+ }
81
284
 
82
285
  // build the shell HTML, injecting the bundle URL (so main.tsx loads this
83
286
  // app's bundle) AND window.__sootsimBridgePort. the bridge-port injection
@@ -247,18 +450,3 @@ export function rnxPlugin(options: RnxPluginOptions = {}): Plugin {
247
450
 
248
451
  export const sootsimPlugin = rnxPlugin
249
452
  export default rnxPlugin
250
-
251
- // the `.swift` transform is opt-in per project: an app that never imports a
252
- // `.swift` file has no reason to watch for one, and an app that ships a native
253
- // ios/ tree (every swift file in it is a pod or an xcode target, not app
254
- // source) would otherwise rebuild the wrong package on every edit. a local
255
- // vite app registers `swiftPlugin` in both the bundler that builds its native
256
- // bundle and the main dev server that serves the artifact; a One app on the
257
- // rolldown native engine registers `swiftRemotePlugin` through
258
- // `native.bundlerOptions.plugins` instead, which submits to
259
- // `/api/swift-compile` and needs no local toolchain and no serving.
260
- export { swiftPlugin, type SwiftPluginOptions } from './vite-plugin-swift.ts'
261
- export {
262
- swiftRemotePlugin,
263
- type SwiftRemotePluginOptions,
264
- } from './rolldown-plugin-swift.ts'