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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/dist/commands/close.d.ts +3 -0
  4. package/dist/commands/close.d.ts.map +1 -0
  5. package/dist/commands/close.js +16 -0
  6. package/dist/commands/close.js.map +1 -0
  7. package/dist/commands/doctor.d.ts +3 -0
  8. package/dist/commands/doctor.d.ts.map +1 -0
  9. package/dist/commands/doctor.js +33 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/fill.d.ts +3 -0
  12. package/dist/commands/fill.d.ts.map +1 -0
  13. package/dist/commands/fill.js +40 -0
  14. package/dist/commands/fill.js.map +1 -0
  15. package/dist/commands/open.d.ts +3 -0
  16. package/dist/commands/open.d.ts.map +1 -0
  17. package/dist/commands/open.js +23 -0
  18. package/dist/commands/open.js.map +1 -0
  19. package/dist/commands/screenshot.d.ts +3 -0
  20. package/dist/commands/screenshot.d.ts.map +1 -0
  21. package/dist/commands/screenshot.js +24 -0
  22. package/dist/commands/screenshot.js.map +1 -0
  23. package/dist/commands/snapshot.d.ts +3 -0
  24. package/dist/commands/snapshot.d.ts.map +1 -0
  25. package/dist/commands/snapshot.js +27 -0
  26. package/dist/commands/snapshot.js.map +1 -0
  27. package/dist/commands/swipe.d.ts +3 -0
  28. package/dist/commands/swipe.d.ts.map +1 -0
  29. package/dist/commands/swipe.js +61 -0
  30. package/dist/commands/swipe.js.map +1 -0
  31. package/dist/commands/tap.d.ts +3 -0
  32. package/dist/commands/tap.d.ts.map +1 -0
  33. package/dist/commands/tap.js +50 -0
  34. package/dist/commands/tap.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +32 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/lib/appium.d.ts +10 -0
  40. package/dist/lib/appium.d.ts.map +1 -0
  41. package/dist/lib/appium.js +127 -0
  42. package/dist/lib/appium.js.map +1 -0
  43. package/dist/lib/server.d.ts +26 -0
  44. package/dist/lib/server.d.ts.map +1 -0
  45. package/dist/lib/server.js +165 -0
  46. package/dist/lib/server.js.map +1 -0
  47. package/dist/lib/session.d.ts +19 -0
  48. package/dist/lib/session.d.ts.map +1 -0
  49. package/dist/lib/session.js +39 -0
  50. package/dist/lib/session.js.map +1 -0
  51. package/dist/lib/snapshot.d.ts +11 -0
  52. package/dist/lib/snapshot.d.ts.map +1 -0
  53. package/dist/lib/snapshot.js +136 -0
  54. package/dist/lib/snapshot.js.map +1 -0
  55. package/package.json +54 -0
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { readFileSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+ import { openCommand } from './commands/open.js';
7
+ import { snapshotCommand } from './commands/snapshot.js';
8
+ import { tapCommand } from './commands/tap.js';
9
+ import { fillCommand } from './commands/fill.js';
10
+ import { swipeCommand } from './commands/swipe.js';
11
+ import { screenshotCommand } from './commands/screenshot.js';
12
+ import { closeCommand } from './commands/close.js';
13
+ import { doctorCommand } from './commands/doctor.js';
14
+ // Read version from package.json dynamically
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
18
+ const program = new Command();
19
+ program
20
+ .name('agent-mobile')
21
+ .description('Mobile automation CLI for AI agents - control iOS simulators\n\nCommands:\n open Launch an iOS app by bundle ID\n snapshot Get UI elements with refs (@e1, @e2, ...)\n tap Tap element by ref or coordinates\n fill Fill text into input by ref\n swipe Swipe in a direction\n screenshot Take a screenshot\n close Close the current session\n doctor Check system requirements\n\nWorkflow:\n 1. agent-mobile open com.apple.Preferences\n 2. agent-mobile snapshot\n 3. agent-mobile tap @e1')
22
+ .version(packageJson.version);
23
+ program.addCommand(openCommand);
24
+ program.addCommand(snapshotCommand);
25
+ program.addCommand(tapCommand);
26
+ program.addCommand(fillCommand);
27
+ program.addCommand(swipeCommand);
28
+ program.addCommand(screenshotCommand);
29
+ program.addCommand(closeCommand);
30
+ program.addCommand(doctorCommand);
31
+ program.parse();
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,6CAA6C;AAC7C,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAE7F,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,cAAc,CAAC;KACpB,WAAW,CAAC,uhBAAuhB,CAAC;KACpiB,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AAEhC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAElC,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type Browser } from 'webdriverio';
2
+ export interface OpenOptions {
3
+ bundleId: string;
4
+ deviceName?: string;
5
+ }
6
+ export declare function createSession(options: OpenOptions): Promise<Browser>;
7
+ export declare function getDriver(): Promise<Browser>;
8
+ export declare function closeSession(): Promise<void>;
9
+ export declare function validateSession(): Promise<boolean>;
10
+ //# sourceMappingURL=appium.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appium.d.ts","sourceRoot":"","sources":["../../src/lib/appium.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAuBnD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CA2C1E;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAgDlD;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAUlD;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAQxD"}
@@ -0,0 +1,127 @@
1
+ import { remote } from 'webdriverio';
2
+ import { execSync } from 'child_process';
3
+ import { readSession, writeSession, deleteSession } from './session.js';
4
+ import { ensureAppiumReady } from './server.js';
5
+ let driver = null;
6
+ function getAppiumUrl() {
7
+ const host = process.env.APPIUM_HOST || 'localhost';
8
+ const port = process.env.APPIUM_PORT || '4723';
9
+ return `http://${host}:${port}`;
10
+ }
11
+ function getBootedSimulatorUdid() {
12
+ try {
13
+ const output = execSync('xcrun simctl list devices booted', { encoding: 'utf-8' });
14
+ const match = output.match(/\(([A-F0-9-]{36})\) \(Booted\)/);
15
+ return match ? match[1] : null;
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ export async function createSession(options) {
22
+ // Auto-setup: ensure Appium is running and driver is installed
23
+ await ensureAppiumReady();
24
+ const appiumUrl = getAppiumUrl();
25
+ const udid = process.env.SIMULATOR_UDID || getBootedSimulatorUdid();
26
+ if (!udid) {
27
+ throw new Error('No booted iOS simulator found. Boot one with: xcrun simctl boot "iPhone 16 Pro"');
28
+ }
29
+ try {
30
+ driver = await remote({
31
+ hostname: process.env.APPIUM_HOST || 'localhost',
32
+ port: parseInt(process.env.APPIUM_PORT || '4723'),
33
+ capabilities: {
34
+ platformName: 'iOS',
35
+ 'appium:automationName': 'XCUITest',
36
+ 'appium:deviceName': options.deviceName || 'iPhone Simulator',
37
+ 'appium:udid': udid,
38
+ 'appium:bundleId': options.bundleId,
39
+ 'appium:noReset': true,
40
+ },
41
+ logLevel: 'silent',
42
+ });
43
+ const session = {
44
+ sessionId: driver.sessionId,
45
+ appiumUrl,
46
+ deviceName: options.deviceName || 'iPhone Simulator',
47
+ bundleId: options.bundleId,
48
+ refs: {},
49
+ };
50
+ writeSession(session);
51
+ return driver;
52
+ }
53
+ catch (error) {
54
+ const err = error;
55
+ if (err.message.includes('ECONNREFUSED')) {
56
+ throw new Error(`Cannot connect to Appium at ${appiumUrl}. Run: agent-mobile doctor`);
57
+ }
58
+ throw error;
59
+ }
60
+ }
61
+ export async function getDriver() {
62
+ if (driver) {
63
+ return driver;
64
+ }
65
+ const session = readSession();
66
+ if (!session) {
67
+ throw new Error('No active session. Run: agent-mobile open <bundle-id>');
68
+ }
69
+ // Auto-setup: ensure Appium is running
70
+ await ensureAppiumReady();
71
+ const appiumUrl = getAppiumUrl();
72
+ const udid = process.env.SIMULATOR_UDID || getBootedSimulatorUdid() || undefined;
73
+ try {
74
+ // Try to reconnect to existing session
75
+ driver = await remote({
76
+ hostname: process.env.APPIUM_HOST || 'localhost',
77
+ port: parseInt(process.env.APPIUM_PORT || '4723'),
78
+ capabilities: {
79
+ platformName: 'iOS',
80
+ 'appium:automationName': 'XCUITest',
81
+ 'appium:deviceName': session.deviceName,
82
+ 'appium:udid': udid,
83
+ 'appium:bundleId': session.bundleId,
84
+ 'appium:noReset': true,
85
+ },
86
+ logLevel: 'silent',
87
+ });
88
+ // Update session with new session ID
89
+ session.sessionId = driver.sessionId;
90
+ writeSession(session);
91
+ return driver;
92
+ }
93
+ catch (error) {
94
+ const err = error;
95
+ if (err.message.includes('ECONNREFUSED')) {
96
+ throw new Error(`Cannot connect to Appium at ${appiumUrl}. Run: agent-mobile doctor`);
97
+ }
98
+ if (err.message.includes('invalid session id') || err.message.includes('session not created')) {
99
+ deleteSession();
100
+ throw new Error('Session expired. Run: agent-mobile open <bundle-id>');
101
+ }
102
+ throw error;
103
+ }
104
+ }
105
+ export async function closeSession() {
106
+ if (driver) {
107
+ try {
108
+ await driver.deleteSession();
109
+ }
110
+ catch {
111
+ // Ignore errors during cleanup
112
+ }
113
+ driver = null;
114
+ }
115
+ deleteSession();
116
+ }
117
+ export async function validateSession() {
118
+ try {
119
+ const d = await getDriver();
120
+ await d.getPageSource();
121
+ return true;
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ }
127
+ //# sourceMappingURL=appium.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appium.js","sourceRoot":"","sources":["../../src/lib/appium.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export declare function isAppiumRunning(): Promise<boolean>;
2
+ export declare function isXCUITestDriverInstalled(): boolean;
3
+ export declare function installXCUITestDriver(): Promise<void>;
4
+ export declare function startAppiumServer(): Promise<void>;
5
+ export declare function stopAppiumServer(): Promise<void>;
6
+ export declare function ensureAppiumReady(): Promise<void>;
7
+ export interface DoctorResult {
8
+ xcode: {
9
+ ok: boolean;
10
+ message: string;
11
+ };
12
+ simulator: {
13
+ ok: boolean;
14
+ message: string;
15
+ };
16
+ appium: {
17
+ ok: boolean;
18
+ message: string;
19
+ };
20
+ xcuitest: {
21
+ ok: boolean;
22
+ message: string;
23
+ };
24
+ }
25
+ export declare function runDoctor(): Promise<DoctorResult>;
26
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/lib/server.ts"],"names":[],"mappings":"AAkBA,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAUxD;AAED,wBAAgB,yBAAyB,IAAI,OAAO,CAYnD;AAED,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAY3D;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqCvD;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBtD;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAUvD;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,SAAS,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,MAAM,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,QAAQ,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5C;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC,CA+CvD"}
@@ -0,0 +1,165 @@
1
+ import { spawn, execSync } from 'child_process';
2
+ import { existsSync, writeFileSync, readFileSync, unlinkSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ const PID_FILE = join(tmpdir(), 'agent-mobile-appium.pid');
6
+ const LOG_FILE = join(tmpdir(), 'agent-mobile-appium.log');
7
+ let appiumProcess = null;
8
+ function getAppiumPort() {
9
+ return parseInt(process.env.APPIUM_PORT || '4723');
10
+ }
11
+ function getAppiumHost() {
12
+ return process.env.APPIUM_HOST || '127.0.0.1';
13
+ }
14
+ export async function isAppiumRunning() {
15
+ const port = getAppiumPort();
16
+ const host = getAppiumHost();
17
+ try {
18
+ const response = await fetch(`http://${host}:${port}/status`);
19
+ return response.ok;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
25
+ export function isXCUITestDriverInstalled() {
26
+ try {
27
+ const output = execSync('npx appium driver list --installed --json 2>/dev/null', {
28
+ encoding: 'utf-8',
29
+ timeout: 30000,
30
+ });
31
+ const drivers = JSON.parse(output);
32
+ return 'xcuitest' in drivers;
33
+ }
34
+ catch {
35
+ // If we can't check, try anyway
36
+ return true;
37
+ }
38
+ }
39
+ export async function installXCUITestDriver() {
40
+ console.log('Installing XCUITest driver (first-time setup)...');
41
+ try {
42
+ execSync('npx appium driver install xcuitest', {
43
+ encoding: 'utf-8',
44
+ stdio: 'inherit',
45
+ timeout: 300000, // 5 minutes
46
+ });
47
+ console.log('XCUITest driver installed successfully');
48
+ }
49
+ catch (error) {
50
+ throw new Error('Failed to install XCUITest driver. Run manually: npx appium driver install xcuitest');
51
+ }
52
+ }
53
+ export async function startAppiumServer() {
54
+ const port = getAppiumPort();
55
+ const host = getAppiumHost();
56
+ // Check if already running
57
+ if (await isAppiumRunning()) {
58
+ return;
59
+ }
60
+ console.log('Starting Appium server...');
61
+ // Use npx to run the bundled appium
62
+ appiumProcess = spawn('npx', ['appium', '--address', host, '--port', String(port), '--relaxed-security'], {
63
+ detached: true,
64
+ stdio: ['ignore', 'pipe', 'pipe'],
65
+ env: { ...process.env },
66
+ });
67
+ // Save PID for later cleanup
68
+ if (appiumProcess.pid) {
69
+ writeFileSync(PID_FILE, String(appiumProcess.pid));
70
+ }
71
+ // Don't let the parent wait for this process
72
+ appiumProcess.unref();
73
+ // Wait for server to be ready
74
+ const maxAttempts = 30;
75
+ for (let i = 0; i < maxAttempts; i++) {
76
+ await new Promise(resolve => setTimeout(resolve, 1000));
77
+ if (await isAppiumRunning()) {
78
+ console.log('Appium server ready');
79
+ return;
80
+ }
81
+ }
82
+ throw new Error('Appium server failed to start. Run manually: npx appium');
83
+ }
84
+ export async function stopAppiumServer() {
85
+ // Try to stop the process we started
86
+ if (appiumProcess && !appiumProcess.killed) {
87
+ appiumProcess.kill();
88
+ appiumProcess = null;
89
+ }
90
+ // Try to stop via PID file
91
+ if (existsSync(PID_FILE)) {
92
+ try {
93
+ const pid = parseInt(readFileSync(PID_FILE, 'utf-8'));
94
+ process.kill(pid, 'SIGTERM');
95
+ }
96
+ catch {
97
+ // Process may already be dead
98
+ }
99
+ try {
100
+ unlinkSync(PID_FILE);
101
+ }
102
+ catch {
103
+ // Ignore
104
+ }
105
+ }
106
+ }
107
+ export async function ensureAppiumReady() {
108
+ // Check and install XCUITest driver if needed
109
+ if (!isXCUITestDriverInstalled()) {
110
+ await installXCUITestDriver();
111
+ }
112
+ // Start Appium if not running
113
+ if (!(await isAppiumRunning())) {
114
+ await startAppiumServer();
115
+ }
116
+ }
117
+ export async function runDoctor() {
118
+ const result = {
119
+ xcode: { ok: false, message: '' },
120
+ simulator: { ok: false, message: '' },
121
+ appium: { ok: false, message: '' },
122
+ xcuitest: { ok: false, message: '' },
123
+ };
124
+ // Check Xcode
125
+ try {
126
+ const xcodeVersion = execSync('xcodebuild -version 2>/dev/null | head -1', { encoding: 'utf-8' }).trim();
127
+ result.xcode = { ok: true, message: xcodeVersion };
128
+ }
129
+ catch {
130
+ result.xcode = { ok: false, message: 'Xcode not found. Install from App Store.' };
131
+ }
132
+ // Check Simulator
133
+ try {
134
+ const output = execSync('xcrun simctl list devices booted 2>/dev/null', { encoding: 'utf-8' });
135
+ const bootedMatch = output.match(/\(([A-F0-9-]{36})\) \(Booted\)/);
136
+ if (bootedMatch) {
137
+ // Get device name
138
+ const nameMatch = output.match(/^\s+(.+) \([A-F0-9-]{36}\) \(Booted\)/m);
139
+ const deviceName = nameMatch ? nameMatch[1] : 'Unknown';
140
+ result.simulator = { ok: true, message: `${deviceName} (booted)` };
141
+ }
142
+ else {
143
+ result.simulator = { ok: false, message: 'No simulator booted. Run: xcrun simctl boot "iPhone 16 Pro"' };
144
+ }
145
+ }
146
+ catch {
147
+ result.simulator = { ok: false, message: 'Cannot check simulators. Is Xcode installed?' };
148
+ }
149
+ // Check Appium
150
+ if (await isAppiumRunning()) {
151
+ result.appium = { ok: true, message: 'Running on port ' + getAppiumPort() };
152
+ }
153
+ else {
154
+ result.appium = { ok: true, message: 'Not running (will auto-start)' };
155
+ }
156
+ // Check XCUITest driver
157
+ if (isXCUITestDriverInstalled()) {
158
+ result.xcuitest = { ok: true, message: 'Installed' };
159
+ }
160
+ else {
161
+ result.xcuitest = { ok: true, message: 'Not installed (will auto-install)' };
162
+ }
163
+ return result;
164
+ }
165
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/lib/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export interface ElementRef {
2
+ xpath: string;
3
+ type: string;
4
+ label?: string;
5
+ value?: string;
6
+ }
7
+ export interface SessionData {
8
+ sessionId: string;
9
+ appiumUrl: string;
10
+ deviceName: string;
11
+ bundleId: string;
12
+ refs: Record<string, ElementRef>;
13
+ }
14
+ export declare function readSession(): SessionData | null;
15
+ export declare function writeSession(session: SessionData): void;
16
+ export declare function updateRefs(refs: Record<string, ElementRef>): void;
17
+ export declare function deleteSession(): void;
18
+ export declare function getRef(ref: string): ElementRef | undefined;
19
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAClC;AAED,wBAAgB,WAAW,IAAI,WAAW,GAAG,IAAI,CAUhD;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAEvD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI,CAMjE;AAED,wBAAgB,aAAa,IAAI,IAAI,CAQpC;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAG1D"}
@@ -0,0 +1,39 @@
1
+ import * as fs from 'fs';
2
+ const SESSION_FILE = '/tmp/agent-mobile-session.json';
3
+ export function readSession() {
4
+ try {
5
+ if (!fs.existsSync(SESSION_FILE)) {
6
+ return null;
7
+ }
8
+ const data = fs.readFileSync(SESSION_FILE, 'utf-8');
9
+ return JSON.parse(data);
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ export function writeSession(session) {
16
+ fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
17
+ }
18
+ export function updateRefs(refs) {
19
+ const session = readSession();
20
+ if (session) {
21
+ session.refs = refs;
22
+ writeSession(session);
23
+ }
24
+ }
25
+ export function deleteSession() {
26
+ try {
27
+ if (fs.existsSync(SESSION_FILE)) {
28
+ fs.unlinkSync(SESSION_FILE);
29
+ }
30
+ }
31
+ catch {
32
+ // Ignore errors during cleanup
33
+ }
34
+ }
35
+ export function getRef(ref) {
36
+ const session = readSession();
37
+ return session?.refs[ref];
38
+ }
39
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,MAAM,YAAY,GAAG,gCAAgC,CAAC;AAiBtD,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgB,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAoB;IAC/C,EAAE,CAAC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAgC;IACzD,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;QACpB,YAAY,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAChC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,GAAW;IAChC,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,OAAO,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Browser } from 'webdriverio';
2
+ export interface SnapshotOptions {
3
+ interactive?: boolean;
4
+ all?: boolean;
5
+ }
6
+ export interface SnapshotResult {
7
+ text: string;
8
+ count: number;
9
+ }
10
+ export declare function takeSnapshot(driver: Browser, options?: SnapshotOptions): Promise<SnapshotResult>;
11
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/lib/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AA6H3C,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,OAAO,EACf,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CA2CzB"}
@@ -0,0 +1,136 @@
1
+ import { updateRefs } from './session.js';
2
+ // Map iOS element types to short names
3
+ const TYPE_MAP = {
4
+ XCUIElementTypeButton: 'button',
5
+ XCUIElementTypeStaticText: 'staticText',
6
+ XCUIElementTypeTextField: 'textField',
7
+ XCUIElementTypeSecureTextField: 'secureTextField',
8
+ XCUIElementTypeTextView: 'textView',
9
+ XCUIElementTypeSwitch: 'switch',
10
+ XCUIElementTypeSlider: 'slider',
11
+ XCUIElementTypeCell: 'cell',
12
+ XCUIElementTypeImage: 'image',
13
+ XCUIElementTypeLink: 'link',
14
+ XCUIElementTypeSearchField: 'searchField',
15
+ XCUIElementTypeNavigationBar: 'navBar',
16
+ XCUIElementTypeTabBar: 'tabBar',
17
+ XCUIElementTypeTable: 'table',
18
+ XCUIElementTypeCollectionView: 'collectionView',
19
+ XCUIElementTypeScrollView: 'scrollView',
20
+ XCUIElementTypeOther: 'other',
21
+ XCUIElementTypeApplication: 'application',
22
+ XCUIElementTypeWindow: 'window',
23
+ };
24
+ function mapType(iosType) {
25
+ return TYPE_MAP[iosType] || iosType.replace('XCUIElementType', '').toLowerCase();
26
+ }
27
+ function escapeXPathString(str) {
28
+ if (!str.includes("'")) {
29
+ return `'${str}'`;
30
+ }
31
+ if (!str.includes('"')) {
32
+ return `"${str}"`;
33
+ }
34
+ // Use concat for strings with both quotes
35
+ const parts = str.split("'");
36
+ return `concat('${parts.join("', \"'\", '")}')`;
37
+ }
38
+ function parseAccessibilityTree(xml) {
39
+ const elements = [];
40
+ // Match iOS element tags with attributes
41
+ const elementRegex = /<(XCUIElementType\w+)\s+([^>]*)(?:\/>|>)/g;
42
+ let match;
43
+ const xpathCounts = {};
44
+ while ((match = elementRegex.exec(xml)) !== null) {
45
+ const [, tagName, attrs] = match;
46
+ // Parse attributes
47
+ const getAttr = (name) => {
48
+ const attrMatch = attrs.match(new RegExp(`${name}="([^"]*)"`));
49
+ return attrMatch ? attrMatch[1] : undefined;
50
+ };
51
+ const enabled = getAttr('enabled') !== 'false';
52
+ const visible = getAttr('visible') !== 'false';
53
+ const accessible = getAttr('accessible') === 'true';
54
+ const name = getAttr('name');
55
+ const label = getAttr('label') || name;
56
+ const value = getAttr('value');
57
+ // Build XPath - prefer @name attribute for stability, fall back to position
58
+ let xpath;
59
+ if (name) {
60
+ xpath = `//${tagName}[@name=${escapeXPathString(name)}]`;
61
+ }
62
+ else {
63
+ xpathCounts[tagName] = (xpathCounts[tagName] || 0) + 1;
64
+ xpath = `//${tagName}[${xpathCounts[tagName]}]`;
65
+ }
66
+ elements.push({
67
+ type: mapType(tagName),
68
+ name: name || undefined,
69
+ label: label || undefined,
70
+ value: value || undefined,
71
+ enabled,
72
+ visible,
73
+ accessible,
74
+ xpath,
75
+ });
76
+ }
77
+ return elements;
78
+ }
79
+ function isInteractive(el) {
80
+ if (!el.enabled || !el.visible)
81
+ return false;
82
+ // Interactive types
83
+ const interactiveTypes = [
84
+ 'button',
85
+ 'textField',
86
+ 'secureTextField',
87
+ 'textView',
88
+ 'switch',
89
+ 'slider',
90
+ 'cell',
91
+ 'link',
92
+ 'searchField',
93
+ ];
94
+ if (interactiveTypes.includes(el.type))
95
+ return true;
96
+ // Elements with labels that are accessible
97
+ if (el.accessible && el.label)
98
+ return true;
99
+ return false;
100
+ }
101
+ export async function takeSnapshot(driver, options = {}) {
102
+ const showAll = options.all === true;
103
+ const interactiveOnly = options.interactive !== false && !showAll;
104
+ const pageSource = await driver.getPageSource();
105
+ const elements = parseAccessibilityTree(pageSource);
106
+ // Filter elements
107
+ const filtered = interactiveOnly ? elements.filter(isInteractive) : elements;
108
+ // Assign refs and build output
109
+ const refs = {};
110
+ const lines = [];
111
+ filtered.forEach((el, index) => {
112
+ const ref = `@e${index + 1}`;
113
+ refs[ref] = {
114
+ xpath: el.xpath,
115
+ type: el.type,
116
+ label: el.label,
117
+ value: el.value,
118
+ };
119
+ // Build compact output line
120
+ let line = `${ref} ${el.type}`;
121
+ if (el.label) {
122
+ line += ` "${el.label}"`;
123
+ }
124
+ if (el.value) {
125
+ line += ` [${el.value}]`;
126
+ }
127
+ lines.push(line);
128
+ });
129
+ // Persist refs to session
130
+ updateRefs(refs);
131
+ return {
132
+ text: lines.join('\n'),
133
+ count: lines.length,
134
+ };
135
+ }
136
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/lib/snapshot.ts"],"names":[],"mappings":"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"}