@limrun/cli 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/README.md +670 -0
- package/bin/dev.js +16 -0
- package/bin/run.js +8 -0
- package/dist/base-command.d.ts +17 -0
- package/dist/base-command.d.ts.map +1 -0
- package/dist/base-command.js +111 -0
- package/dist/base-command.js.map +1 -0
- package/dist/commands/build.d.ts +20 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +83 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/connect/android.d.ts +15 -0
- package/dist/commands/connect/android.d.ts.map +1 -0
- package/dist/commands/connect/android.js +80 -0
- package/dist/commands/connect/android.js.map +1 -0
- package/dist/commands/delete/android.d.ts +14 -0
- package/dist/commands/delete/android.d.ts.map +1 -0
- package/dist/commands/delete/android.js +22 -0
- package/dist/commands/delete/android.js.map +1 -0
- package/dist/commands/delete/asset.d.ts +14 -0
- package/dist/commands/delete/asset.d.ts.map +1 -0
- package/dist/commands/delete/asset.js +22 -0
- package/dist/commands/delete/asset.js.map +1 -0
- package/dist/commands/delete/index.d.ts +14 -0
- package/dist/commands/delete/index.d.ts.map +1 -0
- package/dist/commands/delete/index.js +47 -0
- package/dist/commands/delete/index.js.map +1 -0
- package/dist/commands/delete/ios.d.ts +14 -0
- package/dist/commands/delete/ios.d.ts.map +1 -0
- package/dist/commands/delete/ios.js +24 -0
- package/dist/commands/delete/ios.js.map +1 -0
- package/dist/commands/delete/xcode.d.ts +14 -0
- package/dist/commands/delete/xcode.d.ts.map +1 -0
- package/dist/commands/delete/xcode.js +22 -0
- package/dist/commands/delete/xcode.js.map +1 -0
- package/dist/commands/exec/element-tree.d.ts +14 -0
- package/dist/commands/exec/element-tree.d.ts.map +1 -0
- package/dist/commands/exec/element-tree.js +51 -0
- package/dist/commands/exec/element-tree.js.map +1 -0
- package/dist/commands/exec/install-app.d.ts +16 -0
- package/dist/commands/exec/install-app.d.ts.map +1 -0
- package/dist/commands/exec/install-app.js +75 -0
- package/dist/commands/exec/install-app.js.map +1 -0
- package/dist/commands/exec/launch-app.d.ts +16 -0
- package/dist/commands/exec/launch-app.d.ts.map +1 -0
- package/dist/commands/exec/launch-app.js +49 -0
- package/dist/commands/exec/launch-app.js.map +1 -0
- package/dist/commands/exec/list-apps.d.ts +14 -0
- package/dist/commands/exec/list-apps.d.ts.map +1 -0
- package/dist/commands/exec/list-apps.js +45 -0
- package/dist/commands/exec/list-apps.js.map +1 -0
- package/dist/commands/exec/log.d.ts +17 -0
- package/dist/commands/exec/log.d.ts.map +1 -0
- package/dist/commands/exec/log.js +80 -0
- package/dist/commands/exec/log.js.map +1 -0
- package/dist/commands/exec/open-url.d.ts +15 -0
- package/dist/commands/exec/open-url.d.ts.map +1 -0
- package/dist/commands/exec/open-url.js +35 -0
- package/dist/commands/exec/open-url.js.map +1 -0
- package/dist/commands/exec/press-key.d.ts +16 -0
- package/dist/commands/exec/press-key.d.ts.map +1 -0
- package/dist/commands/exec/press-key.js +41 -0
- package/dist/commands/exec/press-key.js.map +1 -0
- package/dist/commands/exec/record.d.ts +17 -0
- package/dist/commands/exec/record.d.ts.map +1 -0
- package/dist/commands/exec/record.js +78 -0
- package/dist/commands/exec/record.js.map +1 -0
- package/dist/commands/exec/screenshot.d.ts +15 -0
- package/dist/commands/exec/screenshot.d.ts.map +1 -0
- package/dist/commands/exec/screenshot.js +65 -0
- package/dist/commands/exec/screenshot.js.map +1 -0
- package/dist/commands/exec/scroll.d.ts +16 -0
- package/dist/commands/exec/scroll.d.ts.map +1 -0
- package/dist/commands/exec/scroll.js +50 -0
- package/dist/commands/exec/scroll.js.map +1 -0
- package/dist/commands/exec/tap-element.d.ts +18 -0
- package/dist/commands/exec/tap-element.d.ts.map +1 -0
- package/dist/commands/exec/tap-element.js +91 -0
- package/dist/commands/exec/tap-element.js.map +1 -0
- package/dist/commands/exec/tap.d.ts +22 -0
- package/dist/commands/exec/tap.d.ts.map +1 -0
- package/dist/commands/exec/tap.js +41 -0
- package/dist/commands/exec/tap.js.map +1 -0
- package/dist/commands/exec/terminate-app.d.ts +15 -0
- package/dist/commands/exec/terminate-app.d.ts.map +1 -0
- package/dist/commands/exec/terminate-app.js +39 -0
- package/dist/commands/exec/terminate-app.js.map +1 -0
- package/dist/commands/exec/type.d.ts +16 -0
- package/dist/commands/exec/type.d.ts.map +1 -0
- package/dist/commands/exec/type.js +43 -0
- package/dist/commands/exec/type.js.map +1 -0
- package/dist/commands/get/android.d.ts +19 -0
- package/dist/commands/get/android.d.ts.map +1 -0
- package/dist/commands/get/android.js +69 -0
- package/dist/commands/get/android.js.map +1 -0
- package/dist/commands/get/asset.d.ts +18 -0
- package/dist/commands/get/asset.d.ts.map +1 -0
- package/dist/commands/get/asset.js +74 -0
- package/dist/commands/get/asset.js.map +1 -0
- package/dist/commands/get/ios.d.ts +19 -0
- package/dist/commands/get/ios.d.ts.map +1 -0
- package/dist/commands/get/ios.js +69 -0
- package/dist/commands/get/ios.js.map +1 -0
- package/dist/commands/get/xcode.d.ts +17 -0
- package/dist/commands/get/xcode.d.ts.map +1 -0
- package/dist/commands/get/xcode.js +65 -0
- package/dist/commands/get/xcode.js.map +1 -0
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +18 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +7 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +14 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/pull.d.ts +16 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +71 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +15 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +40 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/run/android.d.ts +24 -0
- package/dist/commands/run/android.d.ts.map +1 -0
- package/dist/commands/run/android.js +183 -0
- package/dist/commands/run/android.js.map +1 -0
- package/dist/commands/run/ios.d.ts +23 -0
- package/dist/commands/run/ios.d.ts.map +1 -0
- package/dist/commands/run/ios.js +129 -0
- package/dist/commands/run/ios.js.map +1 -0
- package/dist/commands/run/xcode.d.ts +19 -0
- package/dist/commands/run/xcode.d.ts.map +1 -0
- package/dist/commands/run/xcode.js +81 -0
- package/dist/commands/run/xcode.js.map +1 -0
- package/dist/commands/session/start.d.ts +15 -0
- package/dist/commands/session/start.d.ts.map +1 -0
- package/dist/commands/session/start.js +97 -0
- package/dist/commands/session/start.js.map +1 -0
- package/dist/commands/session/status.d.ts +11 -0
- package/dist/commands/session/status.d.ts.map +1 -0
- package/dist/commands/session/status.js +64 -0
- package/dist/commands/session/status.js.map +1 -0
- package/dist/commands/session/stop.d.ts +15 -0
- package/dist/commands/session/stop.d.ts.map +1 -0
- package/dist/commands/session/stop.js +67 -0
- package/dist/commands/session/stop.js.map +1 -0
- package/dist/commands/sync.d.ts +19 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +80 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/auth.d.ts +2 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +60 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/config.d.ts +21 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +101 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/daemon-client.d.ts +9 -0
- package/dist/lib/daemon-client.d.ts.map +1 -0
- package/dist/lib/daemon-client.js +81 -0
- package/dist/lib/daemon-client.js.map +1 -0
- package/dist/lib/daemon.d.ts +40 -0
- package/dist/lib/daemon.d.ts.map +1 -0
- package/dist/lib/daemon.js +427 -0
- package/dist/lib/daemon.js.map +1 -0
- package/dist/lib/formatting.d.ts +3 -0
- package/dist/lib/formatting.d.ts.map +1 -0
- package/dist/lib/formatting.js +31 -0
- package/dist/lib/formatting.js.map +1 -0
- package/dist/lib/instance-client-factory.d.ts +23 -0
- package/dist/lib/instance-client-factory.d.ts.map +1 -0
- package/dist/lib/instance-client-factory.js +62 -0
- package/dist/lib/instance-client-factory.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,670 @@
|
|
|
1
|
+
# @limrun/cli
|
|
2
|
+
|
|
3
|
+
The official command-line interface for [Limrun](https://limrun.com) — create and control cloud mobile sandboxes for Android, iOS, and Xcode.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# npm
|
|
9
|
+
npm install -g @limrun/cli
|
|
10
|
+
|
|
11
|
+
# npx (no install)
|
|
12
|
+
npx @limrun/cli <command>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Authentication
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Log in via browser (stores API key in ~/.lim/config.yaml)
|
|
19
|
+
lim login
|
|
20
|
+
|
|
21
|
+
# Or provide an API key directly
|
|
22
|
+
lim --api-key <YOUR_KEY> get android
|
|
23
|
+
|
|
24
|
+
# Or use an environment variable
|
|
25
|
+
export LIM_API_KEY=<YOUR_KEY>
|
|
26
|
+
lim get android
|
|
27
|
+
|
|
28
|
+
# Log out (removes stored API key)
|
|
29
|
+
lim logout
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The CLI stores configuration in `~/.lim/config.yaml`. This file is compatible with the Go-based `lim` CLI — if you've already logged in with the Go version, the TypeScript CLI will use the same credentials.
|
|
33
|
+
|
|
34
|
+
## Global Flags
|
|
35
|
+
|
|
36
|
+
Every command supports these flags:
|
|
37
|
+
|
|
38
|
+
| Flag | Description |
|
|
39
|
+
|------|-------------|
|
|
40
|
+
| `--api-key <value>` | API key (also reads `LIM_API_KEY` env var) |
|
|
41
|
+
| `--json` | Output as JSON instead of human-readable tables |
|
|
42
|
+
| `--help` | Show help for any command |
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
- [Instance Management](#instance-management) — Create, list, and delete Android/iOS/Xcode instances
|
|
47
|
+
- [Asset Management](#asset-management) — Upload and download files (APKs, IPAs, etc.)
|
|
48
|
+
- [Sessions](#sessions) — Persistent connections for fast, interactive device control
|
|
49
|
+
- [Device Interaction](#device-interaction) — Screenshots, tapping, typing, scrolling, and more
|
|
50
|
+
- [Xcode Build Pipeline](#xcode-build-pipeline) — Sync code and run xcodebuild remotely
|
|
51
|
+
- [Connectivity](#connectivity) — ADB tunnel for Android debugging
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### Instance Management
|
|
56
|
+
|
|
57
|
+
#### Create Instances
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Android instance with ADB tunnel and scrcpy streaming
|
|
61
|
+
lim run android
|
|
62
|
+
|
|
63
|
+
# Android with apps pre-installed
|
|
64
|
+
lim run android --install ./my-app.apk --install ./another.apk
|
|
65
|
+
|
|
66
|
+
# Android with custom settings
|
|
67
|
+
lim run android --region us-west --display-name "CI Test" --label env=ci --rm
|
|
68
|
+
|
|
69
|
+
# iOS instance
|
|
70
|
+
lim run ios
|
|
71
|
+
|
|
72
|
+
# iOS with specific device model
|
|
73
|
+
lim run ios --model ipad --rm
|
|
74
|
+
|
|
75
|
+
# iOS with pre-installed app from asset storage
|
|
76
|
+
lim run ios --install-asset my-app.ipa
|
|
77
|
+
|
|
78
|
+
# Xcode build sandbox
|
|
79
|
+
lim run xcode --rm --hard-timeout 1h
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Common flags for `run` commands:**
|
|
83
|
+
|
|
84
|
+
| Flag | Description |
|
|
85
|
+
|------|-------------|
|
|
86
|
+
| `--rm` | Auto-delete the instance on exit (Ctrl+C) |
|
|
87
|
+
| `--region <value>` | Region for the instance (e.g. `us-west`) |
|
|
88
|
+
| `--display-name <value>` | Human-readable name |
|
|
89
|
+
| `--label <key=value>` | Labels (repeatable). Used for filtering and reuse |
|
|
90
|
+
| `--hard-timeout <duration>` | Max lifetime (e.g. `1m`, `10m`, `3h`). Default: none |
|
|
91
|
+
| `--inactivity-timeout <duration>` | Idle timeout. Default: `3m` |
|
|
92
|
+
| `--reuse-if-exists` | Reuse an existing instance with matching labels/region |
|
|
93
|
+
| `--install <file>` | Local file to install (auto-uploads, repeatable) |
|
|
94
|
+
| `--install-asset <name>` | Asset name to install (repeatable) |
|
|
95
|
+
|
|
96
|
+
**Android-specific flags:**
|
|
97
|
+
|
|
98
|
+
| Flag | Description |
|
|
99
|
+
|------|-------------|
|
|
100
|
+
| `--[no-]connect` | Start ADB tunnel (default: true) |
|
|
101
|
+
| `--[no-]stream` | Launch scrcpy for visual control (default: true) |
|
|
102
|
+
| `--adb-path <path>` | Path to `adb` binary (default: `adb`) |
|
|
103
|
+
|
|
104
|
+
**iOS-specific flags:**
|
|
105
|
+
|
|
106
|
+
| Flag | Description |
|
|
107
|
+
|------|-------------|
|
|
108
|
+
| `--model <iphone\|ipad\|watch>` | Simulator device model |
|
|
109
|
+
| `--xcode` | Attach a Xcode build sandbox to the iOS instance |
|
|
110
|
+
|
|
111
|
+
#### List Instances
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# List ready instances
|
|
115
|
+
lim get android
|
|
116
|
+
lim get ios
|
|
117
|
+
lim get xcode
|
|
118
|
+
|
|
119
|
+
# Get a specific instance by ID
|
|
120
|
+
lim get android android_abc123
|
|
121
|
+
|
|
122
|
+
# Show all states (not just ready)
|
|
123
|
+
lim get ios --all
|
|
124
|
+
|
|
125
|
+
# Filter by state, region, or labels
|
|
126
|
+
lim get android --state creating
|
|
127
|
+
lim get ios --region us-west
|
|
128
|
+
lim get android --label-selector env=prod,team=mobile
|
|
129
|
+
|
|
130
|
+
# JSON output for scripting
|
|
131
|
+
lim get android --json
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Filtering flags:**
|
|
135
|
+
|
|
136
|
+
| Flag | Description |
|
|
137
|
+
|------|-------------|
|
|
138
|
+
| `--all` | Show instances in all states |
|
|
139
|
+
| `--state <value>` | Filter by state (`unknown`, `creating`, `ready`, `terminated`) |
|
|
140
|
+
| `--region <value>` | Filter by region |
|
|
141
|
+
| `--label-selector <value>` | Filter by labels (e.g. `env=prod,team=mobile`) |
|
|
142
|
+
|
|
143
|
+
#### Delete Instances
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Delete by type
|
|
147
|
+
lim delete android android_abc123
|
|
148
|
+
lim delete ios ios_abc123
|
|
149
|
+
lim delete xcode xcode_abc123
|
|
150
|
+
|
|
151
|
+
# Auto-detect type from ID prefix
|
|
152
|
+
lim delete android_abc123
|
|
153
|
+
lim delete ios_abc123
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### Asset Management
|
|
159
|
+
|
|
160
|
+
Assets are files (APKs, IPAs, configs, etc.) stored in Limrun's cloud storage for use with instances.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Upload a file
|
|
164
|
+
lim push ./my-app.apk
|
|
165
|
+
lim push ./my-app.ipa -n custom-name
|
|
166
|
+
|
|
167
|
+
# Download a file
|
|
168
|
+
lim pull asset_abc123
|
|
169
|
+
lim pull my-app.apk
|
|
170
|
+
lim pull asset_abc123 -o ./downloads
|
|
171
|
+
|
|
172
|
+
# List assets
|
|
173
|
+
lim get asset
|
|
174
|
+
lim get asset --name my-app
|
|
175
|
+
lim get asset --download-url
|
|
176
|
+
|
|
177
|
+
# Get a specific asset
|
|
178
|
+
lim get asset asset_abc123
|
|
179
|
+
|
|
180
|
+
# Delete an asset
|
|
181
|
+
lim delete asset asset_abc123
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### Sessions
|
|
187
|
+
|
|
188
|
+
Sessions keep a persistent WebSocket connection to an instance in the background, making all `exec` commands near-instant (~50ms instead of ~2s per command).
|
|
189
|
+
|
|
190
|
+
#### Why Sessions?
|
|
191
|
+
|
|
192
|
+
Without a session, every `exec` command creates a new connection:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
lim exec screenshot ios_abc123 # ~2s (connect + auth + screenshot + disconnect)
|
|
196
|
+
lim exec tap ios_abc123 100 200 # ~2s (connect + auth + tap + disconnect)
|
|
197
|
+
lim exec element-tree ios_abc123 # ~2s (connect + auth + fetch + disconnect)
|
|
198
|
+
# Total: ~6s for 3 commands
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
With a session, the connection is created once and reused:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
lim session start ios_abc123 # ~2s (one-time connection setup)
|
|
205
|
+
lim exec screenshot ios_abc123 # ~50ms (reuses connection)
|
|
206
|
+
lim exec tap ios_abc123 100 200 # ~50ms (reuses connection)
|
|
207
|
+
lim exec element-tree ios_abc123 # ~50ms (reuses connection)
|
|
208
|
+
lim session stop # instant cleanup
|
|
209
|
+
# Total: ~2.15s for 3 commands
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
This makes sessions essential for interactive workflows, AI agent loops, and any scenario where you run multiple `exec` commands against the same instance.
|
|
213
|
+
|
|
214
|
+
#### Session Commands
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Start sessions (one per instance, can run multiple)
|
|
218
|
+
lim session start ios_abc123
|
|
219
|
+
lim session start ios_def456
|
|
220
|
+
lim session start android_ghi789
|
|
221
|
+
|
|
222
|
+
# Check all active sessions
|
|
223
|
+
lim session status
|
|
224
|
+
lim session status --json
|
|
225
|
+
|
|
226
|
+
# Stop a specific session
|
|
227
|
+
lim session stop ios_abc123
|
|
228
|
+
|
|
229
|
+
# Stop all sessions at once
|
|
230
|
+
lim session stop --all
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
If only one session is active, `lim session stop` (no ID) stops it automatically.
|
|
234
|
+
|
|
235
|
+
#### How It Works
|
|
236
|
+
|
|
237
|
+
Each `lim session start <ID>` spawns an independent background daemon that:
|
|
238
|
+
- Holds a persistent WebSocket connection to that specific instance
|
|
239
|
+
- Listens on its own Unix socket at `/tmp/lim-sessions/<instance-id>/`
|
|
240
|
+
- All `exec` commands automatically detect the matching session and route through it
|
|
241
|
+
- Multiple sessions run in parallel with no shared state
|
|
242
|
+
|
|
243
|
+
#### Example: Interactive Testing
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
lim run ios --model iphone
|
|
247
|
+
lim session start ios_abc123
|
|
248
|
+
|
|
249
|
+
# Fast interaction loop — each command takes ~50ms
|
|
250
|
+
lim exec launch-app ios_abc123 com.example.myapp
|
|
251
|
+
lim exec element-tree ios_abc123 | jq '.tree'
|
|
252
|
+
lim exec tap-element ios_abc123 --label "Login"
|
|
253
|
+
lim exec type ios_abc123 "user@example.com"
|
|
254
|
+
lim exec tap-element ios_abc123 --label "Submit"
|
|
255
|
+
lim exec screenshot ios_abc123 -o after-login.png
|
|
256
|
+
|
|
257
|
+
lim session stop ios_abc123
|
|
258
|
+
lim delete ios_abc123
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### Example: Multi-Device AI Agent
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Create two instances and start sessions for both
|
|
265
|
+
lim run ios --model iphone
|
|
266
|
+
lim run ios --model ipad
|
|
267
|
+
lim session start ios_phone_123
|
|
268
|
+
lim session start ios_tablet_456
|
|
269
|
+
|
|
270
|
+
# Agent controls both devices in parallel — ~50ms per command
|
|
271
|
+
lim exec launch-app ios_phone_123 com.example.myapp
|
|
272
|
+
lim exec launch-app ios_tablet_456 com.example.myapp
|
|
273
|
+
|
|
274
|
+
lim exec screenshot ios_phone_123 -o phone.png
|
|
275
|
+
lim exec screenshot ios_tablet_456 -o tablet.png
|
|
276
|
+
|
|
277
|
+
lim exec tap ios_phone_123 200 400
|
|
278
|
+
lim exec element-tree ios_tablet_456 --json > tablet-tree.json
|
|
279
|
+
|
|
280
|
+
# Clean up all sessions
|
|
281
|
+
lim session stop --all
|
|
282
|
+
lim delete ios_phone_123
|
|
283
|
+
lim delete ios_tablet_456
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### Example: Automated Test Matrix
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
# Spin up devices, start sessions, run tests, tear down
|
|
290
|
+
DEVICES=("iphone" "ipad")
|
|
291
|
+
IDS=()
|
|
292
|
+
|
|
293
|
+
for model in "${DEVICES[@]}"; do
|
|
294
|
+
ID=$(lim run ios --model $model --json | jq -r '.metadata.id')
|
|
295
|
+
lim session start $ID
|
|
296
|
+
IDS+=($ID)
|
|
297
|
+
done
|
|
298
|
+
|
|
299
|
+
# Run tests against all devices
|
|
300
|
+
for ID in "${IDS[@]}"; do
|
|
301
|
+
lim exec launch-app $ID com.example.myapp
|
|
302
|
+
lim exec screenshot $ID -o "test_${ID}.png"
|
|
303
|
+
done
|
|
304
|
+
|
|
305
|
+
# Tear down
|
|
306
|
+
lim session stop --all
|
|
307
|
+
for ID in "${IDS[@]}"; do
|
|
308
|
+
lim delete $ID
|
|
309
|
+
done
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
### Device Interaction
|
|
315
|
+
|
|
316
|
+
The `exec` commands let you interact with running Android and iOS instances directly from the command line. These commands auto-detect the platform from the instance ID prefix. When a [session](#sessions) is active, commands route through it automatically for near-instant execution.
|
|
317
|
+
|
|
318
|
+
#### Screenshots
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
# Save to file
|
|
322
|
+
lim exec screenshot ios_abc123 -o screenshot.png
|
|
323
|
+
|
|
324
|
+
# Output base64 to stdout (for piping)
|
|
325
|
+
lim exec screenshot ios_abc123
|
|
326
|
+
|
|
327
|
+
# JSON output with metadata
|
|
328
|
+
lim exec screenshot ios_abc123 --json
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
#### Tapping
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
# Tap at coordinates
|
|
335
|
+
lim exec tap ios_abc123 100 200
|
|
336
|
+
|
|
337
|
+
# Tap an element by accessibility selector
|
|
338
|
+
lim exec tap-element ios_abc123 --label "Submit"
|
|
339
|
+
lim exec tap-element ios_abc123 --accessibility-id btn_ok
|
|
340
|
+
|
|
341
|
+
# Android: tap by resource ID or text
|
|
342
|
+
lim exec tap-element android_abc123 --resource-id com.example:id/button
|
|
343
|
+
lim exec tap-element android_abc123 --text "Sign In"
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### Text Input
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
# Type text into the focused field
|
|
350
|
+
lim exec type ios_abc123 "Hello World"
|
|
351
|
+
|
|
352
|
+
# Type and press Enter (iOS)
|
|
353
|
+
lim exec type ios_abc123 "search query" --press-enter
|
|
354
|
+
|
|
355
|
+
# Press a key
|
|
356
|
+
lim exec press-key ios_abc123 enter
|
|
357
|
+
lim exec press-key ios_abc123 a --modifier shift
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### Scrolling
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
lim exec scroll ios_abc123 down --amount 500
|
|
364
|
+
lim exec scroll android_abc123 up --amount 300
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
#### UI Inspection
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
# Get the element/accessibility tree
|
|
371
|
+
lim exec element-tree ios_abc123
|
|
372
|
+
lim exec element-tree android_abc123
|
|
373
|
+
|
|
374
|
+
# Pipe to jq for filtering
|
|
375
|
+
lim exec element-tree ios_abc123 | jq '.'
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### App Management (iOS)
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
# Install an app from local file (auto-uploads)
|
|
382
|
+
lim exec install-app ios_abc123 ./MyApp.ipa
|
|
383
|
+
|
|
384
|
+
# Install from URL
|
|
385
|
+
lim exec install-app ios_abc123 https://example.com/app.ipa
|
|
386
|
+
|
|
387
|
+
# Launch / terminate
|
|
388
|
+
lim exec launch-app ios_abc123 com.example.myapp
|
|
389
|
+
lim exec launch-app ios_abc123 com.example.myapp --mode RelaunchIfRunning
|
|
390
|
+
lim exec terminate-app ios_abc123 com.example.myapp
|
|
391
|
+
|
|
392
|
+
# List installed apps
|
|
393
|
+
lim exec list-apps ios_abc123
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
#### Open URLs
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
# Open web URL (opens in browser on the device)
|
|
400
|
+
lim exec open-url ios_abc123 https://example.com
|
|
401
|
+
|
|
402
|
+
# Open deep link
|
|
403
|
+
lim exec open-url ios_abc123 myapp://settings
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
#### Log Streaming (iOS)
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
# Tail recent logs
|
|
410
|
+
lim exec log ios_abc123 com.example.myapp --lines 50
|
|
411
|
+
|
|
412
|
+
# Stream logs continuously (Ctrl+C to stop)
|
|
413
|
+
lim exec log ios_abc123 com.example.myapp -f
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
#### Video Recording
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
# Start recording
|
|
420
|
+
lim exec record ios_abc123 start
|
|
421
|
+
lim exec record ios_abc123 start --quality 8
|
|
422
|
+
|
|
423
|
+
# Stop and save
|
|
424
|
+
lim exec record ios_abc123 stop -o recording.mp4
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
### Xcode Build Pipeline
|
|
430
|
+
|
|
431
|
+
Build and test iOS apps remotely using cloud Xcode sandboxes. The `sync` and `build` commands work with both standalone Xcode instances and iOS instances that have Xcode sandbox enabled.
|
|
432
|
+
|
|
433
|
+
#### Option A: iOS Instance with Xcode Sandbox (Recommended)
|
|
434
|
+
|
|
435
|
+
This gives you a simulator **and** a build environment in one instance — the built app is automatically installed on the simulator.
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
# 1. Create iOS instance with Xcode sandbox
|
|
439
|
+
lim run ios --xcode
|
|
440
|
+
# Output:
|
|
441
|
+
# Instance ID: ios_abc123
|
|
442
|
+
# Xcode Sandbox: https://...limrun.net/v1/sandbox_.../xcode
|
|
443
|
+
# (sandbox URL is cached locally for sync/build to use)
|
|
444
|
+
|
|
445
|
+
# 2. Sync your project code to the Xcode sandbox
|
|
446
|
+
lim sync ios_abc123 ./MyProject
|
|
447
|
+
|
|
448
|
+
# 3. Build — the app is auto-installed on the simulator
|
|
449
|
+
lim build ios_abc123 --scheme MyApp --workspace MyApp.xcworkspace
|
|
450
|
+
|
|
451
|
+
# 4. Start a session for fast device interaction
|
|
452
|
+
lim session start ios_abc123
|
|
453
|
+
|
|
454
|
+
# 5. Test the built app on the simulator (~50ms per command)
|
|
455
|
+
lim exec launch-app ios_abc123 com.example.myapp
|
|
456
|
+
lim exec element-tree ios_abc123 | jq '.'
|
|
457
|
+
lim exec screenshot ios_abc123 -o built-app.png
|
|
458
|
+
|
|
459
|
+
# 6. Clean up
|
|
460
|
+
lim session stop ios_abc123
|
|
461
|
+
lim delete ios_abc123
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
> **Note:** The Xcode sandbox URL is only returned when the instance is created — not on subsequent `get` calls. The CLI caches it locally at `~/.lim/instances/` so that `sync` and `build` can find it. This means `sync`/`build` must run on the same machine where `run ios --xcode` was executed.
|
|
465
|
+
|
|
466
|
+
#### Option B: Standalone Xcode Instance
|
|
467
|
+
|
|
468
|
+
Use this when you only need to build (no simulator needed), or when you want to attach a simulator separately.
|
|
469
|
+
|
|
470
|
+
```bash
|
|
471
|
+
# 1. Create a standalone Xcode instance
|
|
472
|
+
lim run xcode --rm
|
|
473
|
+
|
|
474
|
+
# 2. Sync and build
|
|
475
|
+
lim sync xcode_abc123 ./MyProject
|
|
476
|
+
lim build xcode_abc123 --scheme MyApp --workspace MyApp.xcworkspace
|
|
477
|
+
|
|
478
|
+
# 3. Upload build artifact
|
|
479
|
+
lim build xcode_abc123 --scheme MyApp --upload my-app-build
|
|
480
|
+
|
|
481
|
+
# 4. Download the artifact
|
|
482
|
+
lim pull my-app-build -o ./build-output
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
#### Sync Options
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
# Watch mode (re-syncs on file changes, default)
|
|
489
|
+
lim sync ios_abc123 ./MyProject --watch
|
|
490
|
+
|
|
491
|
+
# One-shot sync (no watch)
|
|
492
|
+
lim sync ios_abc123 ./MyProject --no-watch
|
|
493
|
+
|
|
494
|
+
# Sync without installing
|
|
495
|
+
lim sync ios_abc123 ./MyProject --no-install
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
The sync automatically ignores build artifacts (`build/`, `DerivedData/`, `.build/`), dependency folders (`Pods/`, `Carthage/Build/`, `.swiftpm/`), and user-specific files (`xcuserdata/`, `.dSYM/`).
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
### Connectivity
|
|
503
|
+
|
|
504
|
+
#### Android ADB Tunnel
|
|
505
|
+
|
|
506
|
+
Connect to a running Android instance for `adb` access:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
# Connect to an existing instance
|
|
510
|
+
lim connect android android_abc123
|
|
511
|
+
|
|
512
|
+
# With custom adb path
|
|
513
|
+
lim connect android android_abc123 --adb-path /usr/local/bin/adb
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
The tunnel stays open until you press Ctrl+C. While connected, you can use `adb` commands in another terminal.
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
## Configuration
|
|
521
|
+
|
|
522
|
+
The CLI reads configuration from multiple sources (in order of precedence):
|
|
523
|
+
|
|
524
|
+
1. Command-line flags (`--api-key`)
|
|
525
|
+
2. Environment variables (`LIM_API_KEY`, `LIM_API_ENDPOINT`, `LIM_CONSOLE_ENDPOINT`)
|
|
526
|
+
3. Config file (`~/.lim/config.yaml`)
|
|
527
|
+
|
|
528
|
+
**Config file keys:**
|
|
529
|
+
|
|
530
|
+
| Key | Default | Description |
|
|
531
|
+
|-----|---------|-------------|
|
|
532
|
+
| `api-key` | — | Your Limrun API key |
|
|
533
|
+
| `api-endpoint` | `https://api.limrun.com` | API base URL |
|
|
534
|
+
| `console-endpoint` | `https://console.limrun.com` | Console URL (for login) |
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## JSON Output
|
|
539
|
+
|
|
540
|
+
All commands support `--json` for machine-readable output, making the CLI suitable for scripting and AI agent automation:
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
# Get instance details as JSON
|
|
544
|
+
lim get ios ios_abc123 --json
|
|
545
|
+
|
|
546
|
+
# Parse with jq
|
|
547
|
+
lim get android --json | jq '.[].metadata.id'
|
|
548
|
+
|
|
549
|
+
# Use in scripts
|
|
550
|
+
INSTANCE_ID=$(lim run ios --json | jq -r '.metadata.id')
|
|
551
|
+
lim exec screenshot $INSTANCE_ID -o test.png
|
|
552
|
+
lim delete $INSTANCE_ID
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## Workflows
|
|
558
|
+
|
|
559
|
+
### CI Testing: Install and Verify an App
|
|
560
|
+
|
|
561
|
+
```bash
|
|
562
|
+
INSTANCE_ID="ios_..."
|
|
563
|
+
|
|
564
|
+
# Create instance and start session for fast commands
|
|
565
|
+
lim run ios --install ./build/MyApp.ipa
|
|
566
|
+
lim session start $INSTANCE_ID
|
|
567
|
+
|
|
568
|
+
# Verify — each command takes ~50ms with session
|
|
569
|
+
lim exec launch-app $INSTANCE_ID com.example.myapp
|
|
570
|
+
sleep 2
|
|
571
|
+
lim exec element-tree $INSTANCE_ID | grep "Welcome"
|
|
572
|
+
lim exec screenshot $INSTANCE_ID -o test-result.png
|
|
573
|
+
|
|
574
|
+
# Clean up
|
|
575
|
+
lim session stop $INSTANCE_ID
|
|
576
|
+
lim delete $INSTANCE_ID
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### AI Agent Automation
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
# Create instance
|
|
583
|
+
INSTANCE=$(lim run ios --model iphone --json)
|
|
584
|
+
ID=$(echo $INSTANCE | jq -r '.metadata.id')
|
|
585
|
+
|
|
586
|
+
# Start session — all exec commands now run in ~50ms
|
|
587
|
+
lim session start $ID
|
|
588
|
+
|
|
589
|
+
# Agent can interact at high speed
|
|
590
|
+
lim exec tap $ID 200 400
|
|
591
|
+
lim exec type $ID "test@example.com"
|
|
592
|
+
lim exec tap-element $ID --label "Sign In"
|
|
593
|
+
lim exec screenshot $ID -o result.png
|
|
594
|
+
lim exec element-tree $ID --json > ui-state.json
|
|
595
|
+
|
|
596
|
+
# Tail logs (non-streaming works through session too)
|
|
597
|
+
lim exec log $ID com.example.myapp --lines 20
|
|
598
|
+
|
|
599
|
+
# Clean up
|
|
600
|
+
lim session stop $ID
|
|
601
|
+
lim delete $ID
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### Remote Build + Test on iOS Simulator
|
|
605
|
+
|
|
606
|
+
```bash
|
|
607
|
+
# Single instance: Xcode sandbox + iOS simulator
|
|
608
|
+
ID=$(lim run ios --xcode --json | jq -r '.metadata.id')
|
|
609
|
+
|
|
610
|
+
# Sync, build, and test
|
|
611
|
+
lim sync $ID ./MyiOSProject --no-watch
|
|
612
|
+
lim build $ID --scheme MyApp --workspace MyApp.xcworkspace
|
|
613
|
+
|
|
614
|
+
# Verify the built app on the simulator
|
|
615
|
+
lim session start $ID
|
|
616
|
+
lim exec launch-app $ID com.example.myapp
|
|
617
|
+
sleep 2
|
|
618
|
+
lim exec element-tree $ID | grep "Welcome"
|
|
619
|
+
lim exec screenshot $ID -o test-result.png
|
|
620
|
+
lim session stop
|
|
621
|
+
|
|
622
|
+
lim delete $ID
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### Build-Only with Artifact Upload
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
lim run xcode --rm --reuse-if-exists --label project=myapp
|
|
629
|
+
XCODE_ID="xcode_..."
|
|
630
|
+
|
|
631
|
+
lim sync $XCODE_ID ./MyiOSProject --no-watch
|
|
632
|
+
lim build $XCODE_ID --scheme MyApp --workspace MyApp.xcworkspace --upload myapp-latest
|
|
633
|
+
lim pull myapp-latest -o ./build-output
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
## Development
|
|
639
|
+
|
|
640
|
+
### Setup
|
|
641
|
+
|
|
642
|
+
```bash
|
|
643
|
+
cd packages/cli
|
|
644
|
+
npm install
|
|
645
|
+
npm run build
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
### Run commands during development
|
|
649
|
+
|
|
650
|
+
```bash
|
|
651
|
+
# After making changes, rebuild and run
|
|
652
|
+
npm run build && node bin/run.js <command>
|
|
653
|
+
|
|
654
|
+
# Or use watch mode in one terminal, run in another
|
|
655
|
+
npx tsc --watch # Terminal 1
|
|
656
|
+
node bin/run.js get ios # Terminal 2
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
### Link globally
|
|
660
|
+
|
|
661
|
+
```bash
|
|
662
|
+
npm link
|
|
663
|
+
|
|
664
|
+
# Now `lim` works anywhere on your machine
|
|
665
|
+
lim --help
|
|
666
|
+
lim get android
|
|
667
|
+
|
|
668
|
+
# Unlink when done
|
|
669
|
+
npm unlink -g @limrun/cli
|
|
670
|
+
```
|
package/bin/dev.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// For development: registers ts-node so we can run from src/ directly
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const project = path.join(__dirname, '..', 'tsconfig.json');
|
|
6
|
+
|
|
7
|
+
require('ts-node').register({ project });
|
|
8
|
+
|
|
9
|
+
const oclif = require('@oclif/core');
|
|
10
|
+
|
|
11
|
+
oclif.settings.debug = true;
|
|
12
|
+
|
|
13
|
+
oclif
|
|
14
|
+
.execute({ development: true, dir: __dirname, args: process.argv.slice(2) })
|
|
15
|
+
.then(oclif.flush)
|
|
16
|
+
.catch(oclif.Errors.handle);
|
package/bin/run.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import Limrun from '@limrun/api';
|
|
3
|
+
export declare abstract class BaseCommand extends Command {
|
|
4
|
+
static baseFlags: {
|
|
5
|
+
'api-key': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
6
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
8
|
+
private _client?;
|
|
9
|
+
protected get client(): Limrun;
|
|
10
|
+
private _parsedFlags?;
|
|
11
|
+
protected get parsedFlags(): Record<string, unknown> | undefined;
|
|
12
|
+
protected setParsedFlags(flags: Record<string, unknown>): void;
|
|
13
|
+
protected withAuth<T>(fn: () => Promise<T>): Promise<T>;
|
|
14
|
+
protected outputTable(headers: string[], rows: string[][]): void;
|
|
15
|
+
protected outputJson(data: unknown): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=base-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-command.d.ts","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAC7C,OAAO,MAA+B,MAAM,aAAa,CAAC;AAO1D,8BAAsB,WAAY,SAAQ,OAAO;IAC/C,MAAM,CAAC,SAAS;;;MASd;IAEF,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB,SAAS,KAAK,MAAM,IAAI,MAAM,CAc7B;IAED,OAAO,CAAC,YAAY,CAAC,CAA0B;IAE/C,SAAS,KAAK,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAE/D;IAED,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;cAI9C,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAiB7D,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI;IAgBhE,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;CAG1C"}
|