simdeck 0.1.0
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 +13 -0
- package/README.md +240 -0
- package/bin/simdeck.mjs +50 -0
- package/build/simdeck-bin +0 -0
- package/client/dist/assets/index-BL9Mcd6u.js +9 -0
- package/client/dist/assets/index-Cu4TL413.css +1 -0
- package/client/dist/assets/simulatorStream.worker-CH72C_tF.js +22 -0
- package/client/dist/index.html +28 -0
- package/package.json +82 -0
- package/packages/simdeck-test/dist/index.d.ts +75 -0
- package/packages/simdeck-test/dist/index.js +376 -0
- package/scripts/experimental/swiftui-preview.mjs +1438 -0
- package/scripts/postinstall.mjs +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2026 Dj
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# SimDeck
|
|
2
|
+
|
|
3
|
+
SimDeck is a developer tool built for streamlining mobile app development for coding agents.
|
|
4
|
+
Drive iOS Simulator apps from the CLI, browser, and automated tests on macOS.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npm i -g simdeck@latest
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
After installing the CLI, install the Codex skill so agents know the stable
|
|
11
|
+
SimDeck workflow:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npx skills add NativeScript/SimDeck --skill simdeck -a codex -g
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
For VS Code, install the `nativescript.simdeck` extension to open the simulator
|
|
18
|
+
view inside the editor.
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- WebTransport streaming server in Rust, plus experimental WebRTC for runner previews, using hardware encoded HEVC/H.264 video
|
|
23
|
+
- Simulator control & inspection using private accessibility APIs
|
|
24
|
+
- CoreSimulator chrome asset rendering for device bezels
|
|
25
|
+
- NativeScript and React Native runtime inspector plugins, plus a native UIKit inspector framework for other apps
|
|
26
|
+
- Project daemon reuse: normal CLI commands automatically start and reuse one warm native host per project.
|
|
27
|
+
- Optional macOS LaunchAgent service for an always-on local SimDeck daemon.
|
|
28
|
+
- `simdeck/test` for fast JS/TS app tests that can query accessibility state and drive simulator controls.
|
|
29
|
+
|
|
30
|
+
## Documentation
|
|
31
|
+
|
|
32
|
+
Full documentation lives at [simdeck.nativescript.org](https://simdeck.nativescript.org/), with guides, the CLI reference, the REST API, the WebTransport video pipeline, and the inspector protocols.
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
simdeck
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This starts a workspace-local foreground daemon, prints local and LAN browser URLs, and stops when you press Ctrl-C.
|
|
41
|
+
To focus a specific simulator by name or UDID, pass it as the only argument:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
simdeck "iPhone 17 Pro Max"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Use `simdeck ui --open` or `simdeck daemon start` when you want a reusable background daemon instead.
|
|
48
|
+
The no-subcommand lifecycle shortcuts are `simdeck -d` for detached start, `simdeck -k` to kill the background daemon, and `simdeck -r` to restart it.
|
|
49
|
+
|
|
50
|
+
CLI commands automatically use the same warm daemon:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
simdeck list
|
|
54
|
+
simdeck tap <udid> 0.5 0.5 --normalized
|
|
55
|
+
simdeck describe <udid> --format agent --max-depth 2
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## CLI
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
simdeck list
|
|
62
|
+
simdeck boot <udid>
|
|
63
|
+
simdeck shutdown <udid>
|
|
64
|
+
simdeck erase <udid>
|
|
65
|
+
simdeck install <udid> /path/to/App.app
|
|
66
|
+
simdeck uninstall <udid> com.example.App
|
|
67
|
+
simdeck open-url <udid> https://example.com
|
|
68
|
+
simdeck launch <udid> com.apple.Preferences
|
|
69
|
+
simdeck toggle-appearance <udid>
|
|
70
|
+
simdeck pasteboard set <udid> "hello"
|
|
71
|
+
simdeck pasteboard get <udid>
|
|
72
|
+
simdeck screenshot <udid> --output screen.png
|
|
73
|
+
simdeck describe <udid>
|
|
74
|
+
simdeck describe <udid> --format agent --max-depth 4
|
|
75
|
+
simdeck describe <udid> --point 120,240
|
|
76
|
+
simdeck tap <udid> 120 240
|
|
77
|
+
simdeck tap <udid> --label "Continue" --wait-timeout-ms 5000
|
|
78
|
+
simdeck swipe <udid> 200 700 200 200
|
|
79
|
+
simdeck gesture <udid> scroll-down
|
|
80
|
+
simdeck pinch <udid> --start-distance 160 --end-distance 80
|
|
81
|
+
simdeck rotate-gesture <udid> --radius 100 --degrees 90
|
|
82
|
+
simdeck touch <udid> 0.5 0.5 --phase began --normalized
|
|
83
|
+
simdeck touch <udid> 120 240 --down --up --delay-ms 800
|
|
84
|
+
simdeck key <udid> enter
|
|
85
|
+
simdeck key-sequence <udid> --keycodes h,e,l,l,o
|
|
86
|
+
simdeck key-combo <udid> --modifiers cmd --key a
|
|
87
|
+
simdeck type <udid> "hello"
|
|
88
|
+
simdeck type <udid> --file message.txt
|
|
89
|
+
simdeck button <udid> lock --duration-ms 1000
|
|
90
|
+
simdeck batch <udid> --step "tap --label Continue" --step "type 'hello'"
|
|
91
|
+
simdeck dismiss-keyboard <udid>
|
|
92
|
+
simdeck home <udid>
|
|
93
|
+
simdeck app-switcher <udid>
|
|
94
|
+
simdeck rotate-left <udid>
|
|
95
|
+
simdeck rotate-right <udid>
|
|
96
|
+
simdeck chrome-profile <udid>
|
|
97
|
+
simdeck logs <udid> --seconds 30 --limit 200
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`describe` uses the project daemon to prefer React Native, NativeScript, or
|
|
101
|
+
UIKit in-app inspectors, then falls back to the built-in private CoreSimulator
|
|
102
|
+
accessibility bridge. Use `--format agent` or `--format compact-json` for
|
|
103
|
+
lower-token hierarchy dumps. Coordinate commands accept screen coordinates from
|
|
104
|
+
the accessibility tree by default; pass `--normalized` to send `0.0..1.0`
|
|
105
|
+
coordinates directly.
|
|
106
|
+
|
|
107
|
+
## Daemon
|
|
108
|
+
|
|
109
|
+
Manage the project daemon explicitly when needed:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
simdeck daemon start
|
|
113
|
+
simdeck daemon status
|
|
114
|
+
simdeck daemon stop
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`simdeck daemon` manages the normal per-project warm process. For an always-on
|
|
118
|
+
daemon that is available after login, use the macOS user service commands:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
simdeck service on
|
|
122
|
+
simdeck service off
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
This uses a LaunchAgent, keeps the server bound to localhost by default, and is
|
|
126
|
+
best for agents or editor integrations that should be able to open SimDeck
|
|
127
|
+
without first starting a project daemon.
|
|
128
|
+
|
|
129
|
+
Use software H.264 when macOS screen recording starves the hardware encoder:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
simdeck daemon start --video-codec h264-software
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
For LAN browser access:
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
simdeck ui --bind 0.0.0.0 --advertise-host 192.168.1.50 --open
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Restart the CoreSimulator service layer when `simctl` reports a stale service
|
|
142
|
+
version or the live display gets stuck before the first frame:
|
|
143
|
+
|
|
144
|
+
```sh
|
|
145
|
+
simdeck core-simulator restart
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
You can also start or stop the CoreSimulator service layer explicitly:
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
simdeck core-simulator start
|
|
152
|
+
simdeck core-simulator shutdown
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The daemon exposes HTTP on the requested port and WebTransport on `port + 1`.
|
|
156
|
+
The browser bootstrap comes from `GET /api/health`, which returns the WebTransport URL template,
|
|
157
|
+
certificate hash, and packet version needed by the client.
|
|
158
|
+
The served browser UI receives the generated API access token automatically.
|
|
159
|
+
|
|
160
|
+
## JS/TS Tests
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { connect } from "simdeck/test";
|
|
164
|
+
|
|
165
|
+
const sim = await connect();
|
|
166
|
+
try {
|
|
167
|
+
await sim.tap("<udid>", 0.5, 0.5);
|
|
168
|
+
await sim.waitFor("<udid>", { label: "Continue" });
|
|
169
|
+
await sim.screenshot("<udid>");
|
|
170
|
+
} finally {
|
|
171
|
+
sim.close();
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`connect()` starts the project daemon when needed, reuses it when it is already
|
|
176
|
+
healthy, and only stops daemons it started itself.
|
|
177
|
+
|
|
178
|
+
## NativeScript Inspector
|
|
179
|
+
|
|
180
|
+
NativeScript apps can connect directly to the running server from JS and expose
|
|
181
|
+
their NativeScript logical hierarchy plus raw UIKit backing views without
|
|
182
|
+
linking the Swift inspector framework:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { startSimDeckInspector } from "@nativescript/simdeck-inspector";
|
|
186
|
+
|
|
187
|
+
if (__DEV__) {
|
|
188
|
+
startSimDeckInspector({ port: 4310 });
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The runtime connects to `GET /api/inspector/connect` as a WebSocket. The Rust
|
|
193
|
+
server prefers connected NativeScript inspectors for hierarchy requests and
|
|
194
|
+
falls back to the Swift TCP inspector or the built-in native accessibility
|
|
195
|
+
bridge when no matching app inspector is available.
|
|
196
|
+
|
|
197
|
+
## React Native Inspector
|
|
198
|
+
|
|
199
|
+
React Native apps can expose their component tree and Metro dev-mode source
|
|
200
|
+
locations with the React Native inspector package:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
import { AppRegistry } from "react-native";
|
|
204
|
+
import { startSimDeckReactNativeInspector } from "react-native-simdeck";
|
|
205
|
+
import App from "./App";
|
|
206
|
+
|
|
207
|
+
if (__DEV__) {
|
|
208
|
+
startSimDeckReactNativeInspector({ port: 4310 });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
AppRegistry.registerComponent("Example", () => App);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Call it before `AppRegistry.registerComponent(...)` so the package can capture
|
|
215
|
+
React Fiber commits.
|
|
216
|
+
|
|
217
|
+
## VS Code
|
|
218
|
+
|
|
219
|
+
Install the `nativescript.simdeck` extension from the VS Code Marketplace, then
|
|
220
|
+
run `SimDeck: Open Simulator View` from the Command Palette. The extension
|
|
221
|
+
opens the simulator inside a VS Code panel and auto-starts the local daemon
|
|
222
|
+
when it is not already reachable.
|
|
223
|
+
|
|
224
|
+
## SimDeck Cloud
|
|
225
|
+
|
|
226
|
+
SimDeck Cloud uses the same server binary as its GitHub Actions provider. The
|
|
227
|
+
provider workflow starts `simdeck serve` on the runner, exposes it through a
|
|
228
|
+
tunnel, and lets the hosted control plane connect to the simulator with a
|
|
229
|
+
one-time access token.
|
|
230
|
+
|
|
231
|
+
## Contributing
|
|
232
|
+
|
|
233
|
+
Contributors should read [CONTRIBUTING.md](CONTRIBUTING.md) for local build
|
|
234
|
+
instructions, the dev workflow, and architecture notes.
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
Copyright 2026 Dj
|
|
239
|
+
|
|
240
|
+
Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE).
|
package/bin/simdeck.mjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const packageRoot = path.resolve(
|
|
9
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
10
|
+
"..",
|
|
11
|
+
);
|
|
12
|
+
const binaryPath = path.join(packageRoot, "build", "simdeck-bin");
|
|
13
|
+
|
|
14
|
+
if (process.platform !== "darwin") {
|
|
15
|
+
console.error("simdeck only supports macOS.");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!existsSync(binaryPath)) {
|
|
20
|
+
console.error(
|
|
21
|
+
"simdeck native binary is missing. Reinstall the npm package or run `npm run build:cli` from a source checkout.",
|
|
22
|
+
);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
27
|
+
cwd: process.cwd(),
|
|
28
|
+
stdio: "inherit",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
child.on("error", (error) => {
|
|
32
|
+
console.error(error.message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
37
|
+
process.once(signal, () => {
|
|
38
|
+
if (!child.killed) {
|
|
39
|
+
child.kill(signal);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
child.on("exit", (code, signal) => {
|
|
45
|
+
if (signal) {
|
|
46
|
+
process.kill(process.pid, signal);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
process.exit(code ?? 1);
|
|
50
|
+
});
|
|
Binary file
|