peachsim 0.1.527

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 (324) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +640 -0
  3. package/cli/app-config.ts +82 -0
  4. package/cli/app-fonts.ts +388 -0
  5. package/cli/app-project.ts +59 -0
  6. package/cli/app-splash.ts +171 -0
  7. package/cli/app-state-reset.ts +26 -0
  8. package/cli/auth.ts +184 -0
  9. package/cli/bin.ts +59 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2806 -0
  12. package/cli/browser-backend.ts +44 -0
  13. package/cli/browser-evals.ts +96 -0
  14. package/cli/cli-update.ts +261 -0
  15. package/cli/cloud-client.ts +1530 -0
  16. package/cli/cloud-dispatch.ts +206 -0
  17. package/cli/cloud-session.ts +227 -0
  18. package/cli/command-contract.ts +221 -0
  19. package/cli/command-registry.ts +304 -0
  20. package/cli/commands/agent-wrapper.ts +1048 -0
  21. package/cli/commands/agent.ts +439 -0
  22. package/cli/commands/app-fonts.ts +99 -0
  23. package/cli/commands/assert.ts +542 -0
  24. package/cli/commands/auth.ts +71 -0
  25. package/cli/commands/box/bundle-plane.ts +71 -0
  26. package/cli/commands/box/checkout-plane.ts +256 -0
  27. package/cli/commands/box/plane-share.ts +208 -0
  28. package/cli/commands/box/rnx-command.ts +60 -0
  29. package/cli/commands/box/search-command.ts +157 -0
  30. package/cli/commands/box.ts +925 -0
  31. package/cli/commands/camera.ts +266 -0
  32. package/cli/commands/cleanup.ts +169 -0
  33. package/cli/commands/compat.ts +176 -0
  34. package/cli/commands/config.ts +34 -0
  35. package/cli/commands/control.ts +2154 -0
  36. package/cli/commands/cpu-profile.ts +941 -0
  37. package/cli/commands/daemon-mac-app.ts +165 -0
  38. package/cli/commands/daemon.ts +800 -0
  39. package/cli/commands/debug.ts +751 -0
  40. package/cli/commands/desktop.ts +39 -0
  41. package/cli/commands/detect.ts +201 -0
  42. package/cli/commands/detox.ts +481 -0
  43. package/cli/commands/device.ts +134 -0
  44. package/cli/commands/diagnose.ts +589 -0
  45. package/cli/commands/do-chain.ts +408 -0
  46. package/cli/commands/electron.ts +94 -0
  47. package/cli/commands/film.ts +379 -0
  48. package/cli/commands/flow.ts +1240 -0
  49. package/cli/commands/inspect/actions.ts +516 -0
  50. package/cli/commands/inspect/core.ts +2086 -0
  51. package/cli/commands/inspect/count.ts +17 -0
  52. package/cli/commands/inspect/describe.ts +119 -0
  53. package/cli/commands/inspect/env.ts +25 -0
  54. package/cli/commands/inspect/find.ts +169 -0
  55. package/cli/commands/inspect/get-layout.ts +53 -0
  56. package/cli/commands/inspect/keyboard.ts +54 -0
  57. package/cli/commands/inspect/list.ts +175 -0
  58. package/cli/commands/inspect/memory.ts +215 -0
  59. package/cli/commands/inspect/redaction.ts +39 -0
  60. package/cli/commands/inspect/resolve-target.ts +77 -0
  61. package/cli/commands/inspect/screens.ts +78 -0
  62. package/cli/commands/inspect/settle.ts +32 -0
  63. package/cli/commands/inspect/settling.ts +197 -0
  64. package/cli/commands/inspect/shared.ts +259 -0
  65. package/cli/commands/inspect/sleep.ts +15 -0
  66. package/cli/commands/inspect/tree.ts +23 -0
  67. package/cli/commands/inspect/url.ts +17 -0
  68. package/cli/commands/inspect/wait-event.ts +211 -0
  69. package/cli/commands/inspect/wait-idle.ts +25 -0
  70. package/cli/commands/inspect/wait-ready.ts +87 -0
  71. package/cli/commands/inspect/wait-selector.ts +59 -0
  72. package/cli/commands/inspect/wait.ts +32 -0
  73. package/cli/commands/inspect.ts +4738 -0
  74. package/cli/commands/install-cli.ts +275 -0
  75. package/cli/commands/install-desktop.ts +385 -0
  76. package/cli/commands/login.ts +357 -0
  77. package/cli/commands/logout.ts +24 -0
  78. package/cli/commands/maestro-generate.ts +375 -0
  79. package/cli/commands/maestro.ts +536 -0
  80. package/cli/commands/mode.ts +58 -0
  81. package/cli/commands/no-bridge-hint.ts +80 -0
  82. package/cli/commands/perf.ts +67 -0
  83. package/cli/commands/permissions.ts +203 -0
  84. package/cli/commands/platform.ts +410 -0
  85. package/cli/commands/preview.ts +103 -0
  86. package/cli/commands/profile.ts +106 -0
  87. package/cli/commands/react.ts +353 -0
  88. package/cli/commands/record.ts +1435 -0
  89. package/cli/commands/report-issue.ts +305 -0
  90. package/cli/commands/reset.ts +89 -0
  91. package/cli/commands/runtime.ts +348 -0
  92. package/cli/commands/screenshot-command.ts +106 -0
  93. package/cli/commands/screenshot-layers.ts +144 -0
  94. package/cli/commands/screenshot-mode.ts +38 -0
  95. package/cli/commands/screenshot.ts +492 -0
  96. package/cli/commands/screenshots-capture.ts +608 -0
  97. package/cli/commands/screenshots.ts +127 -0
  98. package/cli/commands/serve.ts +139 -0
  99. package/cli/commands/setup.ts +416 -0
  100. package/cli/commands/shell-boolean-mode.ts +82 -0
  101. package/cli/commands/skills.ts +468 -0
  102. package/cli/commands/slides.ts +357 -0
  103. package/cli/commands/state.ts +94 -0
  104. package/cli/commands/storage.ts +58 -0
  105. package/cli/commands/telemetry.ts +55 -0
  106. package/cli/commands/test.ts +535 -0
  107. package/cli/commands/three-mode.ts +771 -0
  108. package/cli/commands/timeline.ts +121 -0
  109. package/cli/commands/upgrade.ts +167 -0
  110. package/cli/commands/upload.ts +1203 -0
  111. package/cli/commands/version.ts +63 -0
  112. package/cli/commands/welcome.ts +17 -0
  113. package/cli/commands/what-happened.ts +328 -0
  114. package/cli/current-sim.ts +217 -0
  115. package/cli/desktop-companion.ts +315 -0
  116. package/cli/drivers/electron.ts +70 -0
  117. package/cli/drivers/index.ts +20 -0
  118. package/cli/drivers/playwright-provisioning.ts +184 -0
  119. package/cli/drivers/playwright-sim-host.ts +537 -0
  120. package/cli/drivers/playwright.ts +209 -0
  121. package/cli/drivers/registry.ts +65 -0
  122. package/cli/drivers/types.ts +102 -0
  123. package/cli/flow-export.ts +55 -0
  124. package/cli/flow-file.ts +142 -0
  125. package/cli/flow-live-status.ts +126 -0
  126. package/cli/flow-session.ts +140 -0
  127. package/cli/help-core.ts +143 -0
  128. package/cli/help.ts +44 -0
  129. package/cli/hidden-runtime-alias.ts +19 -0
  130. package/cli/hints.ts +214 -0
  131. package/cli/inspect-notice-state.ts +114 -0
  132. package/cli/internal-child.ts +176 -0
  133. package/cli/maestro-js.ts +323 -0
  134. package/cli/main.ts +704 -0
  135. package/cli/open-url.ts +8 -0
  136. package/cli/outbound-endpoints.ts +830 -0
  137. package/cli/parent-pid.ts +204 -0
  138. package/cli/parse-args.ts +189 -0
  139. package/cli/privacy.ts +53 -0
  140. package/cli/prompt.ts +56 -0
  141. package/cli/recording-access.ts +108 -0
  142. package/cli/registry.ts +2 -0
  143. package/cli/resolve-assets.ts +63 -0
  144. package/cli/rnx-in-process.ts +98 -0
  145. package/cli/run-registry.ts +226 -0
  146. package/cli/run-rnx-core.ts +148 -0
  147. package/cli/run-rnx.ts +137 -0
  148. package/cli/runtime-notes.ts +66 -0
  149. package/cli/runtime-summary.ts +25 -0
  150. package/cli/secure-file.ts +31 -0
  151. package/cli/self-invocation.ts +58 -0
  152. package/cli/send-to-box.ts +109 -0
  153. package/cli/setup-repository.ts +191 -0
  154. package/cli/shell-init.ts +98 -0
  155. package/cli/standalone.ts +4 -0
  156. package/cli/telemetry.ts +84 -0
  157. package/cli/ui.ts +68 -0
  158. package/cli/ws-bridge.ts +1020 -0
  159. package/detox/attempt-events.cjs +23 -0
  160. package/detox/colors.ts +54 -0
  161. package/detox/config-loader.ts +135 -0
  162. package/detox/element-types.ts +48 -0
  163. package/detox/expectations.ts +534 -0
  164. package/detox/gestures.ts +501 -0
  165. package/detox/index.ts +2694 -0
  166. package/detox/jest-preset.cjs +29 -0
  167. package/detox/jest-setup-after-env.cjs +22 -0
  168. package/detox/matchers.ts +30 -0
  169. package/detox/motion-change.cjs +109 -0
  170. package/detox/motion-change.d.cts +35 -0
  171. package/detox/navigation-lifecycle.ts +290 -0
  172. package/detox/navigation.ts +43 -0
  173. package/detox/proof-frame.cjs +114 -0
  174. package/detox/record-block-execution.cjs +78 -0
  175. package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
  176. package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
  177. package/detox/screenshots/color-test-rest-norngh.png +0 -0
  178. package/detox/screenshots/theme-blue-toggle.png +0 -0
  179. package/detox/screenshots/theme-blue.png +0 -0
  180. package/detox/screenshots/theme-red-toggle.png +0 -0
  181. package/detox/screenshots/theme-red.png +0 -0
  182. package/dist-lib/agent-daemon-client.cjs +502 -0
  183. package/dist-lib/agent-events.cjs +48 -0
  184. package/dist-lib/agent-identity.cjs +53 -0
  185. package/dist-lib/agent-sessions.cjs +759 -0
  186. package/dist-lib/attached-projects.cjs +526 -0
  187. package/dist-lib/auth/shared-session.cjs +208 -0
  188. package/dist-lib/backend-origin.cjs +88 -0
  189. package/dist-lib/beta.cjs +41 -0
  190. package/dist-lib/beta.mjs +13 -0
  191. package/dist-lib/bridge-constants.cjs +161 -0
  192. package/dist-lib/bridge-contract-input.cjs +451 -0
  193. package/dist-lib/bridge-contract-input.mjs +414 -0
  194. package/dist-lib/bridge-contract.cjs +130 -0
  195. package/dist-lib/bridge-contract.mjs +94 -0
  196. package/dist-lib/capture-contract.cjs +220 -0
  197. package/dist-lib/capture-contract.mjs +190 -0
  198. package/dist-lib/cli-constants.cjs +32 -0
  199. package/dist-lib/cloud-contract.cjs +194 -0
  200. package/dist-lib/cloud-contract.mjs +145 -0
  201. package/dist-lib/cloud.cjs +670 -0
  202. package/dist-lib/cloud.mjs +639 -0
  203. package/dist-lib/config.cjs +96 -0
  204. package/dist-lib/detox/index.cjs +3789 -0
  205. package/dist-lib/detox/jest-preset.cjs +29 -0
  206. package/dist-lib/dev-bundle-resolution.cjs +290 -0
  207. package/dist-lib/home-paths.cjs +638 -0
  208. package/dist-lib/host/bridge-host.cjs +10903 -0
  209. package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
  210. package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
  211. package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
  212. package/dist-lib/host/replacement-module-handler.cjs +469 -0
  213. package/dist-lib/host/websocket-proxy.cjs +298 -0
  214. package/dist-lib/index.cjs +4268 -0
  215. package/dist-lib/jump-to-source-babel.cjs +93 -0
  216. package/dist-lib/jump-to-source-native.cjs +112 -0
  217. package/dist-lib/menu.cjs +646 -0
  218. package/dist-lib/menu.mjs +604 -0
  219. package/dist-lib/metro-fingerprint-registry.cjs +186 -0
  220. package/dist-lib/metro-fingerprint-registry.mjs +156 -0
  221. package/dist-lib/metro-production-bundle.cjs +329 -0
  222. package/dist-lib/metro-production-bundle.mjs +295 -0
  223. package/dist-lib/metro.cjs +904 -0
  224. package/dist-lib/profiles.cjs +246 -0
  225. package/dist-lib/public-brand.cjs +39 -0
  226. package/dist-lib/react-native-host-modules.cjs +51 -0
  227. package/dist-lib/react-native-host-modules.mjs +25 -0
  228. package/dist-lib/render-mode.cjs +54 -0
  229. package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
  230. package/dist-lib/sdk.cjs +2433 -0
  231. package/dist-lib/sdk.mjs +2336 -0
  232. package/dist-lib/skills.cjs +39132 -0
  233. package/dist-lib/swift-submit.cjs +92 -0
  234. package/dist-lib/swift-submit.mjs +65 -0
  235. package/dist-lib/swift.cjs +1646 -0
  236. package/dist-lib/vite.cjs +1591 -0
  237. package/package.json +290 -0
  238. package/scripts/dev-server-scanner.ts +1291 -0
  239. package/scripts/optional-demo-registry.ts +113 -0
  240. package/skills/contrast/SKILL.md +165 -0
  241. package/skills/rnx-debug/SKILL.md +433 -0
  242. package/skills/rnx-perf/SKILL.md +399 -0
  243. package/skills/rnx-setup/SKILL.md +172 -0
  244. package/skills/rnx-test/SKILL.md +321 -0
  245. package/skills/rnx-visual/SKILL.md +126 -0
  246. package/src/agent-daemon-client.ts +410 -0
  247. package/src/agent-events.ts +71 -0
  248. package/src/agent-identity.ts +23 -0
  249. package/src/agent-prompt.ts +71 -0
  250. package/src/agent-sessions.ts +580 -0
  251. package/src/app-url.ts +39 -0
  252. package/src/attached-projects.ts +539 -0
  253. package/src/auth/shared-session.ts +235 -0
  254. package/src/backend-origin.ts +70 -0
  255. package/src/beta.ts +19 -0
  256. package/src/bridge-constants.ts +175 -0
  257. package/src/bridge-contract-input.ts +502 -0
  258. package/src/bridge-contract.ts +558 -0
  259. package/src/browser-cache.ts +16 -0
  260. package/src/capture-contract.ts +587 -0
  261. package/src/cli-constants.ts +1 -0
  262. package/src/cli-version.ts +40 -0
  263. package/src/cloud-contract.ts +274 -0
  264. package/src/cloud.ts +762 -0
  265. package/src/codex-client.ts +234 -0
  266. package/src/config.ts +132 -0
  267. package/src/connect.ts +101 -0
  268. package/src/dev-bundle-resolution.ts +346 -0
  269. package/src/dev-server-open.ts +198 -0
  270. package/src/disk-cleanup.ts +540 -0
  271. package/src/flow-timeout-scale.ts +15 -0
  272. package/src/home-paths.ts +806 -0
  273. package/src/host/agent-host.ts +666 -0
  274. package/src/host/bridge-host.ts +2788 -0
  275. package/src/host/fetch-proxy-handler.ts +449 -0
  276. package/src/host/fetch-proxy-overrides.ts +51 -0
  277. package/src/host/open-url.ts +89 -0
  278. package/src/host/plane-host.ts +203 -0
  279. package/src/host/replacement-module-handler.ts +158 -0
  280. package/src/host/websocket-proxy.ts +315 -0
  281. package/src/index.ts +71 -0
  282. package/src/jump-to-source-babel.ts +135 -0
  283. package/src/jump-to-source-native.ts +102 -0
  284. package/src/menu.ts +777 -0
  285. package/src/metro-fingerprint-registry.ts +302 -0
  286. package/src/metro-plugin.ts +341 -0
  287. package/src/metro-production-bundle.ts +471 -0
  288. package/src/native-dev-bundle-url.ts +118 -0
  289. package/src/native-seam-manifest.ts +341 -0
  290. package/src/playwright-host-log.ts +16 -0
  291. package/src/plist.ts +27 -0
  292. package/src/poll-until.ts +123 -0
  293. package/src/profiles.ts +179 -0
  294. package/src/public-brand.ts +9 -0
  295. package/src/react-native-host-modules.ts +26 -0
  296. package/src/render-mode.ts +27 -0
  297. package/src/rolldown-plugin-swift.ts +240 -0
  298. package/src/runtime-assets.ts +233 -0
  299. package/src/runtime-delivery.ts +54 -0
  300. package/src/screenshots/compose.ts +422 -0
  301. package/src/screenshots/frame-compose.ts +514 -0
  302. package/src/screenshots/orchestrate.ts +256 -0
  303. package/src/screenshots/org-deck.ts +91 -0
  304. package/src/screenshots/registry.ts +58 -0
  305. package/src/screenshots/schema.ts +364 -0
  306. package/src/sdk.ts +15 -0
  307. package/src/settings-bundle.ts +418 -0
  308. package/src/shell-bridge-identity.ts +15 -0
  309. package/src/sim-client.ts +697 -0
  310. package/src/skills/builtin/a11y-review.ts +132 -0
  311. package/src/skills/builtin/compat-check.ts +71 -0
  312. package/src/skills/builtin/maestro-test.ts +121 -0
  313. package/src/skills/builtin/perf-profile.ts +90 -0
  314. package/src/skills/builtin/screenshot-all.ts +52 -0
  315. package/src/skills/builtin/visual-diff.ts +100 -0
  316. package/src/skills/registry.ts +107 -0
  317. package/src/skills/types.ts +41 -0
  318. package/src/swift-submit.ts +185 -0
  319. package/src/swift.ts +7 -0
  320. package/src/team-machine-prompt.ts +73 -0
  321. package/src/vite-plugin-one.ts +264 -0
  322. package/src/vite-plugin-swift.ts +620 -0
  323. package/src/vite-plugin.ts +1946 -0
  324. package/src/worklets-babel.ts +250 -0
@@ -0,0 +1,234 @@
1
+ // minimal client for `codex app-server` — a long-lived child process that
2
+ // speaks line-delimited JSON-RPC 2.0 over stdio.
3
+ //
4
+ // scope: just the subset sootsim needs for the attached-projects agent
5
+ // wrapper. each connection owns exactly one spawned app-server child; caller
6
+ // is responsible for lifecycle. bidirectional: client can issue requests +
7
+ // receive server-initiated notifications. we treat every method we don't
8
+ // recognize as an observable event that we pass straight to the handler map.
9
+
10
+ import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'
11
+ import readline from 'node:readline'
12
+
13
+ export interface CodexClientOptions {
14
+ /** resolved path to the codex binary (via `which codex` or user override). */
15
+ bin: string
16
+ /** working directory for the spawned process. defaults to process.cwd(). */
17
+ cwd?: string
18
+ /** extra environment overrides forwarded to the child. */
19
+ env?: Record<string, string | undefined>
20
+ }
21
+
22
+ export type CodexNotificationHandler = (params: unknown) => void
23
+
24
+ export interface CodexClient {
25
+ /** promise that resolves when the child exits. use to await shutdown. */
26
+ readonly exited: Promise<{ code: number | null; signal: NodeJS.Signals | null }>
27
+ /** register a handler for a server-sent notification method. returns
28
+ * a dispose fn. multiple handlers per method are allowed. */
29
+ on(method: string, handler: CodexNotificationHandler): () => void
30
+ /** send a JSON-RPC request and await its response. rejects on error
31
+ * response or child death. */
32
+ request<TResult = unknown>(method: string, params?: unknown): Promise<TResult>
33
+ /** send a JSON-RPC notification (no id, no response). */
34
+ notify(method: string, params?: unknown): void
35
+ /** close stdin (graceful shutdown), then SIGTERM after timeout. */
36
+ shutdown(timeoutMs?: number): Promise<void>
37
+ /** force-kill immediately. */
38
+ kill(signal?: NodeJS.Signals): void
39
+ }
40
+
41
+ interface JsonRpcResponse {
42
+ jsonrpc?: '2.0'
43
+ id?: number | string | null
44
+ result?: unknown
45
+ error?: { code: number; message: string; data?: unknown }
46
+ method?: string
47
+ params?: unknown
48
+ }
49
+
50
+ export class CodexRpcError extends Error {
51
+ code: number
52
+ data: unknown
53
+ constructor(message: string, code: number, data?: unknown) {
54
+ super(message)
55
+ this.name = 'CodexRpcError'
56
+ this.code = code
57
+ this.data = data
58
+ }
59
+ }
60
+
61
+ export function spawnCodexClient(options: CodexClientOptions): CodexClient {
62
+ const child: ChildProcessWithoutNullStreams = spawn(options.bin, ['app-server'], {
63
+ cwd: options.cwd,
64
+ env: { ...process.env, ...options.env },
65
+ stdio: ['pipe', 'pipe', 'pipe'],
66
+ })
67
+
68
+ const pending = new Map<
69
+ number,
70
+ {
71
+ resolve: (value: unknown) => void
72
+ reject: (err: unknown) => void
73
+ method: string
74
+ }
75
+ >()
76
+ const handlers = new Map<string, Set<CodexNotificationHandler>>()
77
+ let nextId = 1
78
+ let closed = false
79
+
80
+ // the one channel child output reaches a caller on. carries the
81
+ // app-server's own stderr and any pipe failure against it.
82
+ function reportChildText(text: string): void {
83
+ const set = handlers.get('__stderr__')
84
+ if (!set) return
85
+ for (const h of set) {
86
+ try {
87
+ h({ text })
88
+ } catch {}
89
+ }
90
+ }
91
+
92
+ // an 'error' event with no listener is an uncaught exception, so a child
93
+ // that fails to spawn or a write against a dead app-server would take the
94
+ // hosting process down with exit 1 instead of letting it shut down. a
95
+ // failed spawn is a closed client: requests reject with the real reason
96
+ // rather than hanging until their timeout.
97
+ child.on('error', (err) => {
98
+ closed = true
99
+ reportChildText(`[codex-spawn] ${err.message}\n`)
100
+ })
101
+ child.stdin.on('error', (err) => {
102
+ reportChildText(`[codex-stdin] ${err.message}\n`)
103
+ })
104
+
105
+ const exited = new Promise<{
106
+ code: number | null
107
+ signal: NodeJS.Signals | null
108
+ }>((resolve) => {
109
+ child.on('close', (code, signal) => {
110
+ closed = true
111
+ const err = new Error(
112
+ `codex app-server exited (code=${code}, signal=${signal ?? ''})`,
113
+ )
114
+ for (const { reject } of pending.values()) reject(err)
115
+ pending.clear()
116
+ resolve({ code, signal })
117
+ })
118
+ })
119
+
120
+ // stdout carries JSON-RPC messages, one per line.
121
+ const rl = readline.createInterface({ input: child.stdout, crlfDelay: Infinity })
122
+ rl.on('line', (line) => {
123
+ const trimmed = line.trim()
124
+ if (!trimmed) return
125
+ let msg: JsonRpcResponse
126
+ try {
127
+ msg = JSON.parse(trimmed) as JsonRpcResponse
128
+ } catch {
129
+ // app-server occasionally emits non-JSON warnings; ignore.
130
+ return
131
+ }
132
+ if (msg.id != null && (msg.result !== undefined || msg.error !== undefined)) {
133
+ const idNum = typeof msg.id === 'string' ? Number(msg.id) : msg.id
134
+ const entry = idNum != null ? pending.get(idNum) : undefined
135
+ if (!entry) return
136
+ pending.delete(idNum!)
137
+ if (msg.error) {
138
+ entry.reject(new CodexRpcError(msg.error.message, msg.error.code, msg.error.data))
139
+ } else {
140
+ entry.resolve(msg.result)
141
+ }
142
+ return
143
+ }
144
+ if (msg.method) {
145
+ const set = handlers.get(msg.method)
146
+ if (!set) return
147
+ for (const h of set) {
148
+ try {
149
+ h(msg.params)
150
+ } catch (err) {
151
+ // a buggy handler must not kill the bridge, but it MUST be visible —
152
+ // silent swallowing turned a notification-handler typo into a
153
+ // "nothing happens" bug once already.
154
+ console.error(
155
+ `[codex-client] handler for "${msg.method}" threw:`,
156
+ err instanceof Error ? (err.stack ?? err.message) : err,
157
+ )
158
+ }
159
+ }
160
+ }
161
+ })
162
+
163
+ // stderr is diagnostic only; surfaced via the "stderr" notification channel
164
+ // so callers can forward it into transcripts if they want.
165
+ child.stderr.setEncoding('utf8')
166
+ child.stderr.on('data', (chunk: string) => {
167
+ reportChildText(chunk)
168
+ })
169
+
170
+ function write(msg: unknown): void {
171
+ if (closed) return
172
+ try {
173
+ child.stdin.write(JSON.stringify(msg) + '\n')
174
+ } catch {
175
+ // if stdin has been closed, subsequent writes will throw; ignore.
176
+ }
177
+ }
178
+
179
+ return {
180
+ exited,
181
+ on(method, handler) {
182
+ let set = handlers.get(method)
183
+ if (!set) {
184
+ set = new Set()
185
+ handlers.set(method, set)
186
+ }
187
+ set.add(handler)
188
+ return () => {
189
+ set?.delete(handler)
190
+ }
191
+ },
192
+ request<TResult>(method: string, params?: unknown): Promise<TResult> {
193
+ if (closed) {
194
+ return Promise.reject(new Error(`codex app-server closed; cannot call ${method}`))
195
+ }
196
+ const id = nextId++
197
+ return new Promise<TResult>((resolve, reject) => {
198
+ pending.set(id, {
199
+ resolve: (v) => resolve(v as TResult),
200
+ reject,
201
+ method,
202
+ })
203
+ write({ jsonrpc: '2.0', id, method, params })
204
+ })
205
+ },
206
+ notify(method, params) {
207
+ write({ jsonrpc: '2.0', method, params })
208
+ },
209
+ async shutdown(timeoutMs = 1500) {
210
+ if (closed) return
211
+ try {
212
+ child.stdin.end()
213
+ } catch {}
214
+ const t = setTimeout(() => {
215
+ if (!closed) {
216
+ try {
217
+ child.kill('SIGTERM')
218
+ } catch {}
219
+ }
220
+ }, timeoutMs)
221
+ try {
222
+ await exited
223
+ } finally {
224
+ clearTimeout(t)
225
+ }
226
+ },
227
+ kill(signal = 'SIGTERM') {
228
+ if (closed) return
229
+ try {
230
+ child.kill(signal)
231
+ } catch {}
232
+ },
233
+ }
234
+ }
package/src/config.ts ADDED
@@ -0,0 +1,132 @@
1
+ // rnx.config.ts — per-app module resolution, turbo modules, env, state, and settings.
2
+ //
3
+ // usage:
4
+ // import { defineConfig } from 'rnxsim/config'
5
+ // export default defineConfig({ modules: { ... }, env: { ... } })
6
+
7
+ import type { RevenueCatTestStoreConfig } from '@sootsim/compat/revenuecat-test-store'
8
+
9
+ export type ModuleResolution =
10
+ // an empty module under `modules`; under `nativeModules` / `turboModules` a
11
+ // present-but-inert native module whose every method call is a no-op.
12
+ | 'noop'
13
+ | false // disable builtin stub, use original
14
+ | { use: string } // redirect to existing compat stub
15
+ | { file: string } // resolve to a file relative to the config
16
+ | { inline: Record<string, any> } // inline stub object
17
+
18
+ export type NativeModuleResolution = ModuleResolution | Record<string, any> // direct native module object for non-url configs
19
+
20
+ export interface RNXConfig<Settings extends Record<string, any> = Record<string, any>> {
21
+ // host-only engine selection. omitted means the current stable runtime.
22
+ runtimeVersion?: string
23
+ modules?: Record<string, ModuleResolution>
24
+ turboModules?: Record<string, NativeModuleResolution>
25
+ nativeModules?: Record<string, NativeModuleResolution>
26
+ env?: Record<string, string>
27
+ // generic per-sim network rewrites for the guest app's runtime fetches.
28
+ // `remap` keys/values are `host:port` (or bare `host`) — a tenant fetch whose
29
+ // url matches a key has its host:port swapped for the value before the
30
+ // request is issued. exists for third-party e2e suites that hardcode a port
31
+ // (e.g. bluesky's flows type `http://localhost:3000`) which collides with a
32
+ // host service; map it to the real bound port without editing the app.
33
+ network?: {
34
+ remap?: Record<string, string>
35
+ }
36
+ settings?: Partial<Settings>
37
+ initialState?: {
38
+ authenticated?: boolean
39
+ locale?: string
40
+ colorScheme?: 'light' | 'dark' | 'auto'
41
+ featureFlags?: Record<string, boolean | string | number>
42
+ // opt-in RevenueCat Test Store catalog consumed by the RNPurchases seam.
43
+ // omitting it preserves the empty-project result with purchases unavailable.
44
+ revenueCat?: RevenueCatTestStoreConfig
45
+ [key: string]: any
46
+ }
47
+ }
48
+
49
+ export const RNX_CONFIG_QUERY_PARAM = 'rnxConfig'
50
+
51
+ export function defineConfig<Settings extends Record<string, any> = Record<string, any>>(
52
+ config: RNXConfig<Settings>,
53
+ ): RNXConfig<Settings> {
54
+ return config
55
+ }
56
+
57
+ function hasOwnKeys(value: Record<string, unknown> | undefined): boolean {
58
+ return !!value && Object.keys(value).length > 0
59
+ }
60
+
61
+ export function hasRNXConfig<Settings extends Record<string, any> = Record<string, any>>(
62
+ config?: RNXConfig<Settings> | null,
63
+ ): config is RNXConfig<Settings> {
64
+ if (!config) return false
65
+ return (
66
+ (typeof config.runtimeVersion === 'string' && config.runtimeVersion.length > 0) ||
67
+ hasOwnKeys(config.modules as Record<string, unknown> | undefined) ||
68
+ hasOwnKeys(config.turboModules as Record<string, unknown> | undefined) ||
69
+ hasOwnKeys(config.nativeModules as Record<string, unknown> | undefined) ||
70
+ hasOwnKeys(config.env as Record<string, unknown> | undefined) ||
71
+ hasOwnKeys(config.network?.remap as Record<string, unknown> | undefined) ||
72
+ hasOwnKeys(config.settings as Record<string, unknown> | undefined) ||
73
+ hasOwnKeys(config.initialState as Record<string, unknown> | undefined)
74
+ )
75
+ }
76
+
77
+ export function mergeRNXConfig<
78
+ Settings extends Record<string, any> = Record<string, any>,
79
+ >(
80
+ base?: RNXConfig<Settings> | null,
81
+ override?: RNXConfig<Settings> | null,
82
+ ): RNXConfig<Settings> | undefined {
83
+ if (!hasRNXConfig(base) && !hasRNXConfig(override)) return undefined
84
+ if (!hasRNXConfig(base)) return override ? { ...override } : undefined
85
+ if (!hasRNXConfig(override)) return base ? { ...base } : undefined
86
+
87
+ return {
88
+ runtimeVersion: override.runtimeVersion ?? base.runtimeVersion,
89
+ modules: { ...(base.modules || {}), ...(override.modules || {}) },
90
+ turboModules: { ...(base.turboModules || {}), ...(override.turboModules || {}) },
91
+ nativeModules: {
92
+ ...(base.nativeModules || {}),
93
+ ...(override.nativeModules || {}),
94
+ },
95
+ env: { ...(base.env || {}), ...(override.env || {}) },
96
+ network: {
97
+ remap: { ...(base.network?.remap || {}), ...(override.network?.remap || {}) },
98
+ },
99
+ settings: {
100
+ ...(base.settings || {}),
101
+ ...(override.settings || {}),
102
+ } as Partial<Settings>,
103
+ initialState: { ...(base.initialState || {}), ...(override.initialState || {}) },
104
+ }
105
+ }
106
+
107
+ export function readRNXConfigFromSearchParams<
108
+ Settings extends Record<string, any> = Record<string, any>,
109
+ >(searchParams: URLSearchParams): RNXConfig<Settings> | undefined {
110
+ const raw = searchParams.get(RNX_CONFIG_QUERY_PARAM)
111
+ if (!raw) return undefined
112
+ try {
113
+ const parsed = JSON.parse(raw) as RNXConfig<Settings>
114
+ return hasRNXConfig(parsed) ? parsed : undefined
115
+ } catch (error) {
116
+ const message = error instanceof Error ? error.message : String(error)
117
+ console.warn(`[rnx] invalid ${RNX_CONFIG_QUERY_PARAM}: ${message}`)
118
+ return undefined
119
+ }
120
+ }
121
+
122
+ export function applyRNXConfigToUrl<
123
+ Settings extends Record<string, any> = Record<string, any>,
124
+ >(url: string, config?: RNXConfig<Settings> | null): string {
125
+ const parsed = new URL(url)
126
+ if (hasRNXConfig(config)) {
127
+ parsed.searchParams.set(RNX_CONFIG_QUERY_PARAM, JSON.stringify(config))
128
+ } else {
129
+ parsed.searchParams.delete(RNX_CONFIG_QUERY_PARAM)
130
+ }
131
+ return parsed.toString()
132
+ }
package/src/connect.ts ADDED
@@ -0,0 +1,101 @@
1
+ // `connect()` — the node entrypoint of the rnxsim SDK. resolves the running
2
+ // bridge world the same way the CLI does (development bridge registry, then
3
+ // daemon, then default port), picks a sim, and returns a connected SimClient
4
+ // over the shared ws transport. node-only by design: sim-client.ts stays
5
+ // browser-safe.
6
+
7
+ import {
8
+ createCloudBridgeForParsed,
9
+ resolveCloudSessionForParsed,
10
+ } from '../cli/cloud-client'
11
+ import { readCurrentSimId } from '../cli/current-sim'
12
+ import { createBridge, resolveBridgeWorld, type BridgeSimInfo } from '../cli/ws-bridge'
13
+ import { SimClient, SimConnectError } from './sim-client'
14
+
15
+ export interface ConnectOptions {
16
+ /** ws bridge port. default: the resolved bridge world (lockfiles). */
17
+ port?: number
18
+ /** sim id to attach to (as shown by `rnx list`). default: the primary sim. */
19
+ sim?: string
20
+ /** per-command bridge timeout in ms. default 15000. */
21
+ timeoutMs?: number
22
+ }
23
+
24
+ function pickSim(
25
+ sims: BridgeSimInfo[],
26
+ wanted: string | undefined,
27
+ port: number,
28
+ ): BridgeSimInfo {
29
+ const open = sims.filter((sim) => sim.readyState === 'open')
30
+ if (wanted) {
31
+ const match = open.find((sim) => sim.id === wanted)
32
+ if (!match) {
33
+ throw new SimConnectError(
34
+ `no sim connected with id ${wanted} on bridge :${port}` +
35
+ (open.length
36
+ ? ` — connected: ${open.map((sim) => sim.id).join(', ')}`
37
+ : ' — no sims connected; open one with `rnx open <port>`'),
38
+ )
39
+ }
40
+ return match
41
+ }
42
+ // same session affinity as the CLI: a sim pinned by `rnx open` /
43
+ // `rnx use` in this terminal wins over the primary election, so a script
44
+ // run next to CLI commands drives the sim the terminal is working with. a
45
+ // stale pin (sim gone) falls through to the primary.
46
+ const pinned = readCurrentSimId()
47
+ if (pinned) {
48
+ const match = open.find((sim) => sim.id === pinned)
49
+ if (match) return match
50
+ }
51
+ const primary = open.find((sim) => sim.isPrimary) ?? open[0]
52
+ if (!primary) {
53
+ throw new SimConnectError(
54
+ `no sims connected on bridge :${port} — open one with \`rnx open <port>\``,
55
+ )
56
+ }
57
+ return primary
58
+ }
59
+
60
+ export async function connect(options: ConnectOptions = {}): Promise<SimClient> {
61
+ const target = {
62
+ simId: options.sim,
63
+ simIdSource: options.sim ? 'flag' : 'none',
64
+ } satisfies Parameters<typeof resolveCloudSessionForParsed>[0]
65
+ const cloud = resolveCloudSessionForParsed(target)
66
+ if (cloud) {
67
+ const bridge = createCloudBridgeForParsed(target)
68
+ if (!bridge)
69
+ throw new SimConnectError('remote simulator session disappeared while connecting')
70
+ return new SimClient(bridge, cloud.simId, {
71
+ commandTimeoutMs: options.timeoutMs ?? 15_000,
72
+ })
73
+ }
74
+
75
+ const port = options.port ?? resolveBridgeWorld().bridgePort
76
+ const commandTimeoutMs = options.timeoutMs ?? 15_000
77
+
78
+ // discovery socket: list sims and pick the target. closed before the pinned
79
+ // client socket is opened so a failed connect never leaks a connection.
80
+ const discovery = createBridge(port, { commandTimeoutMs, cliLabel: 'sdk' })
81
+ let picked: BridgeSimInfo
82
+ try {
83
+ picked = pickSim(await discovery.listSims(), options.sim, port)
84
+ } catch (error) {
85
+ discovery.close()
86
+ if (error instanceof SimConnectError) throw error
87
+ const message = error instanceof Error ? error.message : String(error)
88
+ throw new SimConnectError(`${message} — is the rnx daemon or dev shell running?`)
89
+ }
90
+ discovery.close()
91
+
92
+ // pinned socket: every command targets exactly the picked sim, immune to the
93
+ // CLI's saved-pin re-resolution.
94
+ const bridge = createBridge(port, {
95
+ commandTimeoutMs,
96
+ cliLabel: 'sdk',
97
+ simId: picked.id,
98
+ simIdSource: 'flag',
99
+ })
100
+ return new SimClient(bridge, picked.id, { commandTimeoutMs })
101
+ }