@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.
Files changed (180) hide show
  1. package/README.md +670 -0
  2. package/bin/dev.js +16 -0
  3. package/bin/run.js +8 -0
  4. package/dist/base-command.d.ts +17 -0
  5. package/dist/base-command.d.ts.map +1 -0
  6. package/dist/base-command.js +111 -0
  7. package/dist/base-command.js.map +1 -0
  8. package/dist/commands/build.d.ts +20 -0
  9. package/dist/commands/build.d.ts.map +1 -0
  10. package/dist/commands/build.js +83 -0
  11. package/dist/commands/build.js.map +1 -0
  12. package/dist/commands/connect/android.d.ts +15 -0
  13. package/dist/commands/connect/android.d.ts.map +1 -0
  14. package/dist/commands/connect/android.js +80 -0
  15. package/dist/commands/connect/android.js.map +1 -0
  16. package/dist/commands/delete/android.d.ts +14 -0
  17. package/dist/commands/delete/android.d.ts.map +1 -0
  18. package/dist/commands/delete/android.js +22 -0
  19. package/dist/commands/delete/android.js.map +1 -0
  20. package/dist/commands/delete/asset.d.ts +14 -0
  21. package/dist/commands/delete/asset.d.ts.map +1 -0
  22. package/dist/commands/delete/asset.js +22 -0
  23. package/dist/commands/delete/asset.js.map +1 -0
  24. package/dist/commands/delete/index.d.ts +14 -0
  25. package/dist/commands/delete/index.d.ts.map +1 -0
  26. package/dist/commands/delete/index.js +47 -0
  27. package/dist/commands/delete/index.js.map +1 -0
  28. package/dist/commands/delete/ios.d.ts +14 -0
  29. package/dist/commands/delete/ios.d.ts.map +1 -0
  30. package/dist/commands/delete/ios.js +24 -0
  31. package/dist/commands/delete/ios.js.map +1 -0
  32. package/dist/commands/delete/xcode.d.ts +14 -0
  33. package/dist/commands/delete/xcode.d.ts.map +1 -0
  34. package/dist/commands/delete/xcode.js +22 -0
  35. package/dist/commands/delete/xcode.js.map +1 -0
  36. package/dist/commands/exec/element-tree.d.ts +14 -0
  37. package/dist/commands/exec/element-tree.d.ts.map +1 -0
  38. package/dist/commands/exec/element-tree.js +51 -0
  39. package/dist/commands/exec/element-tree.js.map +1 -0
  40. package/dist/commands/exec/install-app.d.ts +16 -0
  41. package/dist/commands/exec/install-app.d.ts.map +1 -0
  42. package/dist/commands/exec/install-app.js +75 -0
  43. package/dist/commands/exec/install-app.js.map +1 -0
  44. package/dist/commands/exec/launch-app.d.ts +16 -0
  45. package/dist/commands/exec/launch-app.d.ts.map +1 -0
  46. package/dist/commands/exec/launch-app.js +49 -0
  47. package/dist/commands/exec/launch-app.js.map +1 -0
  48. package/dist/commands/exec/list-apps.d.ts +14 -0
  49. package/dist/commands/exec/list-apps.d.ts.map +1 -0
  50. package/dist/commands/exec/list-apps.js +45 -0
  51. package/dist/commands/exec/list-apps.js.map +1 -0
  52. package/dist/commands/exec/log.d.ts +17 -0
  53. package/dist/commands/exec/log.d.ts.map +1 -0
  54. package/dist/commands/exec/log.js +80 -0
  55. package/dist/commands/exec/log.js.map +1 -0
  56. package/dist/commands/exec/open-url.d.ts +15 -0
  57. package/dist/commands/exec/open-url.d.ts.map +1 -0
  58. package/dist/commands/exec/open-url.js +35 -0
  59. package/dist/commands/exec/open-url.js.map +1 -0
  60. package/dist/commands/exec/press-key.d.ts +16 -0
  61. package/dist/commands/exec/press-key.d.ts.map +1 -0
  62. package/dist/commands/exec/press-key.js +41 -0
  63. package/dist/commands/exec/press-key.js.map +1 -0
  64. package/dist/commands/exec/record.d.ts +17 -0
  65. package/dist/commands/exec/record.d.ts.map +1 -0
  66. package/dist/commands/exec/record.js +78 -0
  67. package/dist/commands/exec/record.js.map +1 -0
  68. package/dist/commands/exec/screenshot.d.ts +15 -0
  69. package/dist/commands/exec/screenshot.d.ts.map +1 -0
  70. package/dist/commands/exec/screenshot.js +65 -0
  71. package/dist/commands/exec/screenshot.js.map +1 -0
  72. package/dist/commands/exec/scroll.d.ts +16 -0
  73. package/dist/commands/exec/scroll.d.ts.map +1 -0
  74. package/dist/commands/exec/scroll.js +50 -0
  75. package/dist/commands/exec/scroll.js.map +1 -0
  76. package/dist/commands/exec/tap-element.d.ts +18 -0
  77. package/dist/commands/exec/tap-element.d.ts.map +1 -0
  78. package/dist/commands/exec/tap-element.js +91 -0
  79. package/dist/commands/exec/tap-element.js.map +1 -0
  80. package/dist/commands/exec/tap.d.ts +22 -0
  81. package/dist/commands/exec/tap.d.ts.map +1 -0
  82. package/dist/commands/exec/tap.js +41 -0
  83. package/dist/commands/exec/tap.js.map +1 -0
  84. package/dist/commands/exec/terminate-app.d.ts +15 -0
  85. package/dist/commands/exec/terminate-app.d.ts.map +1 -0
  86. package/dist/commands/exec/terminate-app.js +39 -0
  87. package/dist/commands/exec/terminate-app.js.map +1 -0
  88. package/dist/commands/exec/type.d.ts +16 -0
  89. package/dist/commands/exec/type.d.ts.map +1 -0
  90. package/dist/commands/exec/type.js +43 -0
  91. package/dist/commands/exec/type.js.map +1 -0
  92. package/dist/commands/get/android.d.ts +19 -0
  93. package/dist/commands/get/android.d.ts.map +1 -0
  94. package/dist/commands/get/android.js +69 -0
  95. package/dist/commands/get/android.js.map +1 -0
  96. package/dist/commands/get/asset.d.ts +18 -0
  97. package/dist/commands/get/asset.d.ts.map +1 -0
  98. package/dist/commands/get/asset.js +74 -0
  99. package/dist/commands/get/asset.js.map +1 -0
  100. package/dist/commands/get/ios.d.ts +19 -0
  101. package/dist/commands/get/ios.d.ts.map +1 -0
  102. package/dist/commands/get/ios.js +69 -0
  103. package/dist/commands/get/ios.js.map +1 -0
  104. package/dist/commands/get/xcode.d.ts +17 -0
  105. package/dist/commands/get/xcode.d.ts.map +1 -0
  106. package/dist/commands/get/xcode.js +65 -0
  107. package/dist/commands/get/xcode.js.map +1 -0
  108. package/dist/commands/login.d.ts +7 -0
  109. package/dist/commands/login.d.ts.map +1 -0
  110. package/dist/commands/login.js +18 -0
  111. package/dist/commands/login.js.map +1 -0
  112. package/dist/commands/logout.d.ts +7 -0
  113. package/dist/commands/logout.d.ts.map +1 -0
  114. package/dist/commands/logout.js +14 -0
  115. package/dist/commands/logout.js.map +1 -0
  116. package/dist/commands/pull.d.ts +16 -0
  117. package/dist/commands/pull.d.ts.map +1 -0
  118. package/dist/commands/pull.js +71 -0
  119. package/dist/commands/pull.js.map +1 -0
  120. package/dist/commands/push.d.ts +15 -0
  121. package/dist/commands/push.d.ts.map +1 -0
  122. package/dist/commands/push.js +40 -0
  123. package/dist/commands/push.js.map +1 -0
  124. package/dist/commands/run/android.d.ts +24 -0
  125. package/dist/commands/run/android.d.ts.map +1 -0
  126. package/dist/commands/run/android.js +183 -0
  127. package/dist/commands/run/android.js.map +1 -0
  128. package/dist/commands/run/ios.d.ts +23 -0
  129. package/dist/commands/run/ios.d.ts.map +1 -0
  130. package/dist/commands/run/ios.js +129 -0
  131. package/dist/commands/run/ios.js.map +1 -0
  132. package/dist/commands/run/xcode.d.ts +19 -0
  133. package/dist/commands/run/xcode.d.ts.map +1 -0
  134. package/dist/commands/run/xcode.js +81 -0
  135. package/dist/commands/run/xcode.js.map +1 -0
  136. package/dist/commands/session/start.d.ts +15 -0
  137. package/dist/commands/session/start.d.ts.map +1 -0
  138. package/dist/commands/session/start.js +97 -0
  139. package/dist/commands/session/start.js.map +1 -0
  140. package/dist/commands/session/status.d.ts +11 -0
  141. package/dist/commands/session/status.d.ts.map +1 -0
  142. package/dist/commands/session/status.js +64 -0
  143. package/dist/commands/session/status.js.map +1 -0
  144. package/dist/commands/session/stop.d.ts +15 -0
  145. package/dist/commands/session/stop.d.ts.map +1 -0
  146. package/dist/commands/session/stop.js +67 -0
  147. package/dist/commands/session/stop.js.map +1 -0
  148. package/dist/commands/sync.d.ts +19 -0
  149. package/dist/commands/sync.d.ts.map +1 -0
  150. package/dist/commands/sync.js +80 -0
  151. package/dist/commands/sync.js.map +1 -0
  152. package/dist/index.d.ts +2 -0
  153. package/dist/index.d.ts.map +1 -0
  154. package/dist/index.js +6 -0
  155. package/dist/index.js.map +1 -0
  156. package/dist/lib/auth.d.ts +2 -0
  157. package/dist/lib/auth.d.ts.map +1 -0
  158. package/dist/lib/auth.js +60 -0
  159. package/dist/lib/auth.js.map +1 -0
  160. package/dist/lib/config.d.ts +21 -0
  161. package/dist/lib/config.d.ts.map +1 -0
  162. package/dist/lib/config.js +101 -0
  163. package/dist/lib/config.js.map +1 -0
  164. package/dist/lib/daemon-client.d.ts +9 -0
  165. package/dist/lib/daemon-client.d.ts.map +1 -0
  166. package/dist/lib/daemon-client.js +81 -0
  167. package/dist/lib/daemon-client.js.map +1 -0
  168. package/dist/lib/daemon.d.ts +40 -0
  169. package/dist/lib/daemon.d.ts.map +1 -0
  170. package/dist/lib/daemon.js +427 -0
  171. package/dist/lib/daemon.js.map +1 -0
  172. package/dist/lib/formatting.d.ts +3 -0
  173. package/dist/lib/formatting.d.ts.map +1 -0
  174. package/dist/lib/formatting.js +31 -0
  175. package/dist/lib/formatting.js.map +1 -0
  176. package/dist/lib/instance-client-factory.d.ts +23 -0
  177. package/dist/lib/instance-client-factory.d.ts.map +1 -0
  178. package/dist/lib/instance-client-factory.js +62 -0
  179. package/dist/lib/instance-client-factory.js.map +1 -0
  180. 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,8 @@
1
+ #!/usr/bin/env node
2
+
3
+ const oclif = require('@oclif/core');
4
+
5
+ oclif
6
+ .execute({ dir: __dirname, args: process.argv.slice(2) })
7
+ .then(oclif.flush)
8
+ .catch(oclif.Errors.handle);
@@ -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"}