agent-mobile 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 +21 -0
- package/README.md +226 -0
- package/dist/commands/close.d.ts +3 -0
- package/dist/commands/close.d.ts.map +1 -0
- package/dist/commands/close.js +16 -0
- package/dist/commands/close.js.map +1 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +33 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/fill.d.ts +3 -0
- package/dist/commands/fill.d.ts.map +1 -0
- package/dist/commands/fill.js +40 -0
- package/dist/commands/fill.js.map +1 -0
- package/dist/commands/open.d.ts +3 -0
- package/dist/commands/open.d.ts.map +1 -0
- package/dist/commands/open.js +23 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/screenshot.d.ts +3 -0
- package/dist/commands/screenshot.d.ts.map +1 -0
- package/dist/commands/screenshot.js +24 -0
- package/dist/commands/screenshot.js.map +1 -0
- package/dist/commands/snapshot.d.ts +3 -0
- package/dist/commands/snapshot.d.ts.map +1 -0
- package/dist/commands/snapshot.js +27 -0
- package/dist/commands/snapshot.js.map +1 -0
- package/dist/commands/swipe.d.ts +3 -0
- package/dist/commands/swipe.d.ts.map +1 -0
- package/dist/commands/swipe.js +61 -0
- package/dist/commands/swipe.js.map +1 -0
- package/dist/commands/tap.d.ts +3 -0
- package/dist/commands/tap.d.ts.map +1 -0
- package/dist/commands/tap.js +50 -0
- package/dist/commands/tap.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/appium.d.ts +10 -0
- package/dist/lib/appium.d.ts.map +1 -0
- package/dist/lib/appium.js +127 -0
- package/dist/lib/appium.js.map +1 -0
- package/dist/lib/server.d.ts +26 -0
- package/dist/lib/server.d.ts.map +1 -0
- package/dist/lib/server.js +165 -0
- package/dist/lib/server.js.map +1 -0
- package/dist/lib/session.d.ts +19 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/session.js +39 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/snapshot.d.ts +11 -0
- package/dist/lib/snapshot.d.ts.map +1 -0
- package/dist/lib/snapshot.js +136 -0
- package/dist/lib/snapshot.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 agent-mobile contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# agent-mobile
|
|
2
|
+
|
|
3
|
+
Mobile automation CLI for AI agents - control iOS simulators with simple commands.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g agent-mobile
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
That's it. Appium and drivers are bundled - no separate setup required.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
- **macOS** with Xcode installed
|
|
16
|
+
- **iOS Simulator** booted (the tool will tell you if one isn't running)
|
|
17
|
+
|
|
18
|
+
### First Time Setup
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 1. Boot a simulator
|
|
22
|
+
xcrun simctl boot "iPhone 16 Pro"
|
|
23
|
+
open -a Simulator
|
|
24
|
+
|
|
25
|
+
# 2. Check everything is ready
|
|
26
|
+
agent-mobile doctor
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Launch an app
|
|
33
|
+
agent-mobile open com.apple.Preferences
|
|
34
|
+
|
|
35
|
+
# Get interactive elements with refs
|
|
36
|
+
agent-mobile snapshot
|
|
37
|
+
|
|
38
|
+
# Tap an element by ref
|
|
39
|
+
agent-mobile tap @e1
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
### open
|
|
45
|
+
|
|
46
|
+
Launch an iOS app by bundle ID.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
agent-mobile open <bundleId> [options]
|
|
50
|
+
|
|
51
|
+
# Options:
|
|
52
|
+
# -d, --device <name> Device name (default: "iPhone Simulator")
|
|
53
|
+
|
|
54
|
+
# Examples:
|
|
55
|
+
agent-mobile open com.apple.Preferences
|
|
56
|
+
agent-mobile open com.apple.calculator -d "iPhone 15 Pro"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### snapshot
|
|
60
|
+
|
|
61
|
+
Get UI elements with refs for interaction.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
agent-mobile snapshot [options]
|
|
65
|
+
|
|
66
|
+
# Options:
|
|
67
|
+
# -a, --all Show all elements, not just interactive
|
|
68
|
+
|
|
69
|
+
# Output example:
|
|
70
|
+
# @e1 button "General"
|
|
71
|
+
# @e2 button "Display & Brightness"
|
|
72
|
+
# @e3 switch "Airplane Mode" [off]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### tap
|
|
76
|
+
|
|
77
|
+
Tap an element by ref or coordinates.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
agent-mobile tap <target>
|
|
81
|
+
|
|
82
|
+
# Examples:
|
|
83
|
+
agent-mobile tap @e1 # Tap by ref
|
|
84
|
+
agent-mobile tap 100,200 # Tap by coordinates
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### fill
|
|
88
|
+
|
|
89
|
+
Fill text into an input field.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
agent-mobile fill <ref> <text> [options]
|
|
93
|
+
|
|
94
|
+
# Options:
|
|
95
|
+
# --no-clear Do not clear existing text
|
|
96
|
+
|
|
97
|
+
# Examples:
|
|
98
|
+
agent-mobile fill @e1 "Hello World"
|
|
99
|
+
agent-mobile fill @e2 "append this" --no-clear
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### swipe
|
|
103
|
+
|
|
104
|
+
Swipe in a direction.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
agent-mobile swipe <direction> [options]
|
|
108
|
+
|
|
109
|
+
# Directions: up, down, left, right
|
|
110
|
+
# Options:
|
|
111
|
+
# --distance <percent> Swipe distance (1-100, default: 50)
|
|
112
|
+
|
|
113
|
+
# Examples:
|
|
114
|
+
agent-mobile swipe down
|
|
115
|
+
agent-mobile swipe up --distance 75
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### screenshot
|
|
119
|
+
|
|
120
|
+
Take a screenshot of the current screen.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
agent-mobile screenshot [filename]
|
|
124
|
+
|
|
125
|
+
# Default filename: screenshot.png
|
|
126
|
+
|
|
127
|
+
# Examples:
|
|
128
|
+
agent-mobile screenshot
|
|
129
|
+
agent-mobile screenshot my-screen.png
|
|
130
|
+
agent-mobile screenshot /tmp/debug.png
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### close
|
|
134
|
+
|
|
135
|
+
Close the current Appium session.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
agent-mobile close
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### doctor
|
|
142
|
+
|
|
143
|
+
Check system requirements and diagnose issues.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
agent-mobile doctor
|
|
147
|
+
|
|
148
|
+
# Output:
|
|
149
|
+
# ✓ Xcode: Xcode 16.0
|
|
150
|
+
# ✓ Simulator: iPhone 16 Pro (booted)
|
|
151
|
+
# ✓ Appium: Running on port 4723
|
|
152
|
+
# ✓ XCUITest Driver: Installed
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## For Agents
|
|
156
|
+
|
|
157
|
+
**Copy this section to your AI assistant (Claude, Cursor, GPT, etc.):**
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
To automate iOS simulators, use `agent-mobile`. Install with:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npm install -g agent-mobile
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Basic workflow:
|
|
168
|
+
```bash
|
|
169
|
+
agent-mobile open com.apple.Preferences # Launch app
|
|
170
|
+
agent-mobile snapshot # Get elements with refs
|
|
171
|
+
agent-mobile tap @e1 # Tap by ref
|
|
172
|
+
agent-mobile fill @e2 "text" # Fill input
|
|
173
|
+
agent-mobile swipe down # Swipe
|
|
174
|
+
agent-mobile screenshot /tmp/s.png # Screenshot
|
|
175
|
+
agent-mobile close # End session
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Prerequisites: macOS with Xcode, iOS Simulator booted (`xcrun simctl boot "iPhone 16 Pro"`).
|
|
179
|
+
|
|
180
|
+
Run `agent-mobile doctor` to check setup.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## AI Integration
|
|
185
|
+
|
|
186
|
+
agent-mobile is designed for AI coding assistants. The refs pattern (`@e1`, `@e2`, etc.) provides stable element identifiers that AI tools can easily parse and use.
|
|
187
|
+
|
|
188
|
+
### Claude Code
|
|
189
|
+
|
|
190
|
+
When using Claude Code in a project with agent-mobile, the `.claude/skills/agent-mobile/SKILL.md` file provides auto-discovery. Claude will know how to:
|
|
191
|
+
|
|
192
|
+
- Launch apps and navigate UI
|
|
193
|
+
- Read element refs from snapshots
|
|
194
|
+
- Interact with elements by ref
|
|
195
|
+
- Take screenshots for visual debugging
|
|
196
|
+
|
|
197
|
+
### Example AI Workflow
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
User: "Open Settings and turn on Airplane Mode"
|
|
201
|
+
|
|
202
|
+
AI runs:
|
|
203
|
+
1. agent-mobile open com.apple.Preferences
|
|
204
|
+
2. agent-mobile snapshot
|
|
205
|
+
→ sees: @e3 switch "Airplane Mode" [off]
|
|
206
|
+
3. agent-mobile tap @e3
|
|
207
|
+
4. agent-mobile snapshot
|
|
208
|
+
→ confirms: @e3 switch "Airplane Mode" [on]
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Troubleshooting
|
|
212
|
+
|
|
213
|
+
Run `agent-mobile doctor` to diagnose issues.
|
|
214
|
+
|
|
215
|
+
**No simulator booted:**
|
|
216
|
+
```bash
|
|
217
|
+
xcrun simctl boot "iPhone 16 Pro"
|
|
218
|
+
open -a Simulator
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Xcode not installed:**
|
|
222
|
+
Install Xcode from the App Store and open it once to accept the license.
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"close.d.ts","sourceRoot":"","sources":["../../src/commands/close.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,YAAY,SAWrB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { closeSession } from '../lib/appium.js';
|
|
3
|
+
export const closeCommand = new Command('close')
|
|
4
|
+
.description('Close the current session\n\nExample:\n agent-mobile close')
|
|
5
|
+
.action(async () => {
|
|
6
|
+
try {
|
|
7
|
+
await closeSession();
|
|
8
|
+
console.log('Session closed');
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
const err = error;
|
|
12
|
+
console.error(`Error: ${err.message}`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=close.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"close.js","sourceRoot":"","sources":["../../src/commands/close.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,6DAA6D,CAAC;KAC1E,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,IAAI,CAAC;QACH,MAAM,YAAY,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAc,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,aAAa,SAiCtB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { runDoctor } from '../lib/server.js';
|
|
3
|
+
export const doctorCommand = new Command('doctor')
|
|
4
|
+
.description('Check system requirements and diagnose issues\n\nExample:\n agent-mobile doctor')
|
|
5
|
+
.action(async () => {
|
|
6
|
+
console.log('Checking system requirements...\n');
|
|
7
|
+
const result = await runDoctor();
|
|
8
|
+
const check = (ok) => ok ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
|
|
9
|
+
console.log(`${check(result.xcode.ok)} Xcode: ${result.xcode.message}`);
|
|
10
|
+
console.log(`${check(result.simulator.ok)} Simulator: ${result.simulator.message}`);
|
|
11
|
+
console.log(`${check(result.appium.ok)} Appium: ${result.appium.message}`);
|
|
12
|
+
console.log(`${check(result.xcuitest.ok)} XCUITest Driver: ${result.xcuitest.message}`);
|
|
13
|
+
const allOk = result.xcode.ok && result.simulator.ok;
|
|
14
|
+
console.log('');
|
|
15
|
+
if (allOk) {
|
|
16
|
+
console.log('\x1b[32mAll checks passed!\x1b[0m Ready to use agent-mobile.');
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
console.log('\x1b[33mSome issues found.\x1b[0m Fix the items marked with ✗ above.');
|
|
20
|
+
if (!result.xcode.ok) {
|
|
21
|
+
console.log('\nTo install Xcode:');
|
|
22
|
+
console.log(' 1. Open App Store');
|
|
23
|
+
console.log(' 2. Search for "Xcode"');
|
|
24
|
+
console.log(' 3. Install and open once to accept license');
|
|
25
|
+
}
|
|
26
|
+
if (!result.simulator.ok && result.xcode.ok) {
|
|
27
|
+
console.log('\nTo boot a simulator:');
|
|
28
|
+
console.log(' xcrun simctl boot "iPhone 16 Pro"');
|
|
29
|
+
console.log(' open -a Simulator');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,kFAAkF,CAAC;KAC/F,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IAEjD,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IAEjC,MAAM,KAAK,GAAG,CAAC,EAAW,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,kBAAkB,CAAC;IAE5E,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,WAAW,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,eAAe,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;IACpF,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3E,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,qBAAqB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAExF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;IAErD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;IAC9E,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,sEAAsE,CAAC,CAAC;QACpF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;YACnC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;YACnC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;YACvC,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;QAC9D,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;YACtC,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fill.d.ts","sourceRoot":"","sources":["../../src/commands/fill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,WAAW,SAwCpB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getDriver } from '../lib/appium.js';
|
|
3
|
+
import { getRef } from '../lib/session.js';
|
|
4
|
+
export const fillCommand = new Command('fill')
|
|
5
|
+
.description('Fill text into input by ref\n\nExamples:\n agent-mobile fill @e1 "Hello World"\n agent-mobile fill @e2 "append" --no-clear')
|
|
6
|
+
.argument('<ref>', 'Element ref (e.g., @e1)')
|
|
7
|
+
.argument('<text>', 'Text to enter')
|
|
8
|
+
.option('--no-clear', 'Do not clear existing text')
|
|
9
|
+
.action(async (ref, text, options) => {
|
|
10
|
+
try {
|
|
11
|
+
const driver = await getDriver();
|
|
12
|
+
if (!ref.startsWith('@')) {
|
|
13
|
+
console.error('Error: Ref must start with @ (e.g., @e1)');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const refData = getRef(ref);
|
|
17
|
+
if (!refData) {
|
|
18
|
+
console.error(`Error: Ref ${ref} not found. Run: agent-mobile snapshot`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
// Check if it's an input type
|
|
22
|
+
const inputTypes = ['textField', 'secureTextField', 'textView', 'searchField'];
|
|
23
|
+
if (!inputTypes.includes(refData.type)) {
|
|
24
|
+
console.error(`Error: ${ref} is not a text input (type: ${refData.type})`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const element = await driver.$(refData.xpath);
|
|
28
|
+
if (options.clear) {
|
|
29
|
+
await element.clearValue();
|
|
30
|
+
}
|
|
31
|
+
await element.setValue(text);
|
|
32
|
+
console.log(`Filled ${ref} with "${text}"`);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const err = error;
|
|
36
|
+
console.error(`Error: ${err.message}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=fill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fill.js","sourceRoot":"","sources":["../../src/commands/fill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,8HAA8H,CAAC;KAC3I,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAC;KAC5C,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;KACnC,MAAM,CAAC,YAAY,EAAE,4BAA4B,CAAC;KAClD,MAAM,CAAC,KAAK,EAAE,GAAW,EAAE,IAAY,EAAE,OAA2B,EAAE,EAAE;IACvE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QAEjC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,cAAc,GAAG,wCAAwC,CAAC,CAAC;YACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,8BAA8B;QAC9B,MAAM,UAAU,GAAG,CAAC,WAAW,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;QAC/E,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,+BAA+B,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;YAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAE9C,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;QAC7B,CAAC;QAED,MAAM,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,UAAU,IAAI,GAAG,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAc,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../src/commands/open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,WAAW,SAkBpB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createSession } from '../lib/appium.js';
|
|
3
|
+
export const openCommand = new Command('open')
|
|
4
|
+
.description('Launch an iOS app by bundle ID\n\nExamples:\n agent-mobile open com.apple.Preferences\n agent-mobile open com.apple.calculator -d "iPhone 15 Pro"')
|
|
5
|
+
.argument('<bundleId>', 'App bundle ID (e.g., com.apple.calculator)')
|
|
6
|
+
.option('-d, --device <name>', 'Device name', 'iPhone Simulator')
|
|
7
|
+
.action(async (bundleId, options) => {
|
|
8
|
+
try {
|
|
9
|
+
const driver = await createSession({
|
|
10
|
+
bundleId,
|
|
11
|
+
deviceName: options.device,
|
|
12
|
+
});
|
|
13
|
+
console.log(`Session started: ${driver.sessionId}`);
|
|
14
|
+
console.log(`App: ${bundleId}`);
|
|
15
|
+
console.log(`Device: ${options.device}`);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
const err = error;
|
|
19
|
+
console.error(`Error: ${err.message}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=open.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.js","sourceRoot":"","sources":["../../src/commands/open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,qJAAqJ,CAAC;KAClK,QAAQ,CAAC,YAAY,EAAE,4CAA4C,CAAC;KACpE,MAAM,CAAC,qBAAqB,EAAE,aAAa,EAAE,kBAAkB,CAAC;KAChE,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,OAA2B,EAAE,EAAE;IAC9D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC;YACjC,QAAQ;YACR,UAAU,EAAE,OAAO,CAAC,MAAM;SAC3B,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,WAAW,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAc,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,iBAAiB,SAmB1B,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getDriver } from '../lib/appium.js';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
export const screenshotCommand = new Command('screenshot')
|
|
6
|
+
.description('Take a screenshot\n\nExamples:\n agent-mobile screenshot\n agent-mobile screenshot /tmp/debug.png')
|
|
7
|
+
.argument('[filename]', 'Output filename', 'screenshot.png')
|
|
8
|
+
.action(async (filename) => {
|
|
9
|
+
try {
|
|
10
|
+
const driver = await getDriver();
|
|
11
|
+
const screenshot = await driver.takeScreenshot();
|
|
12
|
+
const filepath = path.isAbsolute(filename)
|
|
13
|
+
? filename
|
|
14
|
+
: path.resolve(process.cwd(), filename);
|
|
15
|
+
fs.writeFileSync(filepath, screenshot, 'base64');
|
|
16
|
+
console.log(`Screenshot saved: ${filepath}`);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
const err = error;
|
|
20
|
+
console.error(`Error: ${err.message}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
//# sourceMappingURL=screenshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,OAAO,CAAC,YAAY,CAAC;KACvD,WAAW,CAAC,qGAAqG,CAAC;KAClH,QAAQ,CAAC,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,CAAC;KAC3D,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;IACjC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,cAAc,EAAE,CAAC;QAEjD,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YACxC,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;QAE1C,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAc,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/commands/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,eAAe,SAqBxB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getDriver } from '../lib/appium.js';
|
|
3
|
+
import { takeSnapshot } from '../lib/snapshot.js';
|
|
4
|
+
export const snapshotCommand = new Command('snapshot')
|
|
5
|
+
.description('Get UI elements with refs\n\nExamples:\n agent-mobile snapshot\n agent-mobile snapshot -a')
|
|
6
|
+
.option('-a, --all', 'Show all elements, not just interactive')
|
|
7
|
+
.action(async (options) => {
|
|
8
|
+
try {
|
|
9
|
+
const driver = await getDriver();
|
|
10
|
+
const result = await takeSnapshot(driver, {
|
|
11
|
+
interactive: !options.all,
|
|
12
|
+
all: options.all,
|
|
13
|
+
});
|
|
14
|
+
if (result.count === 0) {
|
|
15
|
+
console.log('No elements found');
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
console.log(result.text);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
const err = error;
|
|
23
|
+
console.error(`Error: ${err.message}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/commands/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;KACnD,WAAW,CAAC,6FAA6F,CAAC;KAC1G,MAAM,CAAC,WAAW,EAAE,yCAAyC,CAAC;KAC9D,MAAM,CAAC,KAAK,EAAE,OAA0B,EAAE,EAAE;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE;YACxC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG;YACzB,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAc,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swipe.d.ts","sourceRoot":"","sources":["../../src/commands/swipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,YAAY,SA+DrB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getDriver } from '../lib/appium.js';
|
|
3
|
+
export const swipeCommand = new Command('swipe')
|
|
4
|
+
.description('Swipe in a direction\n\nExamples:\n agent-mobile swipe down\n agent-mobile swipe up --distance 75')
|
|
5
|
+
.argument('<direction>', 'Direction: up, down, left, right')
|
|
6
|
+
.option('--distance <percent>', 'Swipe distance as percentage of screen', '50')
|
|
7
|
+
.action(async (direction, options) => {
|
|
8
|
+
try {
|
|
9
|
+
const validDirections = ['up', 'down', 'left', 'right'];
|
|
10
|
+
if (!validDirections.includes(direction)) {
|
|
11
|
+
console.error('Error: Invalid direction. Use: up, down, left, right');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const distance = parseInt(options.distance, 10);
|
|
15
|
+
if (isNaN(distance) || distance < 1 || distance > 100) {
|
|
16
|
+
console.error('Error: Distance must be a number between 1 and 100');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const driver = await getDriver();
|
|
20
|
+
const { width, height } = await driver.getWindowSize();
|
|
21
|
+
const centerX = Math.floor(width / 2);
|
|
22
|
+
const centerY = Math.floor(height / 2);
|
|
23
|
+
const distanceFraction = distance / 100;
|
|
24
|
+
let startX = centerX;
|
|
25
|
+
let startY = centerY;
|
|
26
|
+
let endX = centerX;
|
|
27
|
+
let endY = centerY;
|
|
28
|
+
switch (direction) {
|
|
29
|
+
case 'up':
|
|
30
|
+
startY = Math.floor(height * 0.7);
|
|
31
|
+
endY = Math.floor(height * (0.7 - distanceFraction * 0.5));
|
|
32
|
+
break;
|
|
33
|
+
case 'down':
|
|
34
|
+
startY = Math.floor(height * 0.3);
|
|
35
|
+
endY = Math.floor(height * (0.3 + distanceFraction * 0.5));
|
|
36
|
+
break;
|
|
37
|
+
case 'left':
|
|
38
|
+
startX = Math.floor(width * 0.8);
|
|
39
|
+
endX = Math.floor(width * (0.8 - distanceFraction * 0.6));
|
|
40
|
+
break;
|
|
41
|
+
case 'right':
|
|
42
|
+
startX = Math.floor(width * 0.2);
|
|
43
|
+
endX = Math.floor(width * (0.2 + distanceFraction * 0.6));
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
await driver
|
|
47
|
+
.action('pointer', { parameters: { pointerType: 'touch' } })
|
|
48
|
+
.move({ x: startX, y: startY })
|
|
49
|
+
.down()
|
|
50
|
+
.move({ x: endX, y: endY, duration: 300 })
|
|
51
|
+
.up()
|
|
52
|
+
.perform();
|
|
53
|
+
console.log(`Swiped ${direction}`);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
const err = error;
|
|
57
|
+
console.error(`Error: ${err.message}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=swipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swipe.js","sourceRoot":"","sources":["../../src/commands/swipe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap.d.ts","sourceRoot":"","sources":["../../src/commands/tap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,UAAU,SAgDnB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getDriver } from '../lib/appium.js';
|
|
3
|
+
import { getRef } from '../lib/session.js';
|
|
4
|
+
export const tapCommand = new Command('tap')
|
|
5
|
+
.description('Tap element by ref (@e1) or coordinates (x,y)\n\nExamples:\n agent-mobile tap @e1\n agent-mobile tap 100,200')
|
|
6
|
+
.argument('<target>', 'Element ref or coordinates')
|
|
7
|
+
.action(async (target) => {
|
|
8
|
+
try {
|
|
9
|
+
const driver = await getDriver();
|
|
10
|
+
if (target.startsWith('@')) {
|
|
11
|
+
// Tap by ref
|
|
12
|
+
const ref = getRef(target);
|
|
13
|
+
if (!ref) {
|
|
14
|
+
console.error(`Error: Ref ${target} not found. Run: agent-mobile snapshot`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const element = await driver.$(ref.xpath);
|
|
18
|
+
await element.click();
|
|
19
|
+
const label = ref.label ? `: "${ref.label}"` : '';
|
|
20
|
+
console.log(`Tapped ${target} (${ref.type}${label})`);
|
|
21
|
+
}
|
|
22
|
+
else if (target.includes(',')) {
|
|
23
|
+
// Tap by coordinates
|
|
24
|
+
const [xStr, yStr] = target.split(',');
|
|
25
|
+
const x = parseInt(xStr, 10);
|
|
26
|
+
const y = parseInt(yStr, 10);
|
|
27
|
+
if (isNaN(x) || isNaN(y)) {
|
|
28
|
+
console.error('Error: Invalid coordinates. Use format: x,y (e.g., 100,200)');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
await driver
|
|
32
|
+
.action('pointer', { parameters: { pointerType: 'touch' } })
|
|
33
|
+
.move({ x, y })
|
|
34
|
+
.down()
|
|
35
|
+
.up()
|
|
36
|
+
.perform();
|
|
37
|
+
console.log(`Tapped (${x}, ${y})`);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.error('Error: Invalid target. Use ref (@e1) or coordinates (x,y)');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
const err = error;
|
|
46
|
+
console.error(`Error: ${err.message}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=tap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap.js","sourceRoot":"","sources":["../../src/commands/tap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC;KACzC,WAAW,CAAC,gHAAgH,CAAC;KAC7H,QAAQ,CAAC,UAAU,EAAE,4BAA4B,CAAC;KAClD,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QAEjC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,aAAa;YACb,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAO,CAAC,KAAK,CAAC,cAAc,MAAM,wCAAwC,CAAC,CAAC;gBAC5E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC1C,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YAEtB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,UAAU,MAAM,KAAK,GAAG,CAAC,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC;QACxD,CAAC;aAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,qBAAqB;YACrB,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACvC,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC7B,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAE7B,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;gBAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,MAAM;iBACT,MAAM,CAAC,SAAS,EAAE,EAAE,UAAU,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;iBAC3D,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;iBACd,IAAI,EAAE;iBACN,EAAE,EAAE;iBACJ,OAAO,EAAE,CAAC;YAEb,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;YAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAc,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|