appium-desktop-driver 1.2.0 → 1.4.1
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 +201 -201
- package/README.md +648 -609
- package/build/lib/commands/app.js +31 -31
- package/build/lib/commands/device.js +57 -57
- package/build/lib/commands/element.js +11 -11
- package/build/lib/commands/extension.d.ts +1 -0
- package/build/lib/commands/extension.d.ts.map +1 -1
- package/build/lib/commands/extension.js +34 -14
- package/build/lib/commands/extension.js.map +1 -1
- package/build/lib/commands/functions.js +187 -187
- package/build/lib/commands/index.d.ts +4 -3
- package/build/lib/commands/index.d.ts.map +1 -1
- package/build/lib/driver.js +5 -5
- package/build/lib/mcp/index.js +2 -2
- package/build/lib/mcp/index.js.map +1 -1
- package/build/lib/mcp/tools/window.d.ts.map +1 -1
- package/build/lib/mcp/tools/window.js +13 -0
- package/build/lib/mcp/tools/window.js.map +1 -1
- package/build/lib/powershell/elements.js +265 -265
- package/build/lib/winapi/user32.d.ts +6 -0
- package/build/lib/winapi/user32.d.ts.map +1 -1
- package/build/lib/winapi/user32.js +47 -24
- package/build/lib/winapi/user32.js.map +1 -1
- package/npm-shrinkwrap.json +5951 -0
- package/package.json +71 -71
- package/ROADMAP.md +0 -175
- package/build/lib/mcp/appium-manager.d.ts +0 -8
- package/build/lib/mcp/appium-manager.d.ts.map +0 -1
- package/build/lib/mcp/appium-manager.js +0 -160
- package/build/lib/mcp/appium-manager.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "appium-desktop-driver",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Appium driver for Windows",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"appium",
|
|
7
|
-
"desktop",
|
|
8
|
-
"uiautomation",
|
|
9
|
-
"powershell",
|
|
10
|
-
"automated testing",
|
|
11
|
-
"windows",
|
|
12
|
-
"verisoft"
|
|
13
|
-
],
|
|
14
|
-
"main": "build/lib/driver.js",
|
|
15
|
-
"bin": {
|
|
16
|
-
"
|
|
17
|
-
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "tsc -b",
|
|
20
|
-
"watch": "tsc -b --watch",
|
|
21
|
-
"lint": "eslint .",
|
|
22
|
-
"test": "npx vitest run",
|
|
23
|
-
"test:e2e": "npx vitest run --config vitest.e2e.config.ts",
|
|
24
|
-
"mcp:start": "node build/lib/mcp/index.js"
|
|
25
|
-
},
|
|
26
|
-
"author": "VeriSoft",
|
|
27
|
-
"license": "Apache-2.0",
|
|
28
|
-
"repository": {
|
|
29
|
-
"type": "git",
|
|
30
|
-
"url": "https://github.com/verisoft-ai/appium-desktop-driver.git"
|
|
31
|
-
},
|
|
32
|
-
"bugs": {
|
|
33
|
-
"url": "https://github.com/verisoft-ai/appium-desktop-driver/issues"
|
|
34
|
-
},
|
|
35
|
-
"peerDependencies": {
|
|
36
|
-
"appium": "^3.0.0-rc.2"
|
|
37
|
-
},
|
|
38
|
-
"dependencies": {
|
|
39
|
-
"@appium/base-driver": "^10.1.0",
|
|
40
|
-
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
41
|
-
"bezier-easing": "^2.1.0",
|
|
42
|
-
"ffmpeg-static": "^5.2.0",
|
|
43
|
-
"koffi": "^2.14.1",
|
|
44
|
-
"webdriverio": "^9.0.0",
|
|
45
|
-
"xpath-analyzer": "^3.0.1",
|
|
46
|
-
"zod": "^4.3.6"
|
|
47
|
-
},
|
|
48
|
-
"appium": {
|
|
49
|
-
"driverName": "desktopdriver",
|
|
50
|
-
"automationName": "DesktopDriver",
|
|
51
|
-
"platformNames": [
|
|
52
|
-
"Windows"
|
|
53
|
-
],
|
|
54
|
-
"mainClass": "NovaWindowsDriver"
|
|
55
|
-
},
|
|
56
|
-
"devDependencies": {
|
|
57
|
-
"@appium/eslint-config-appium-ts": "^2.0.3",
|
|
58
|
-
"@appium/tsconfig": "^1.1.0",
|
|
59
|
-
"@appium/types": "^1.1.0",
|
|
60
|
-
"@eslint/js": "^9.38.0",
|
|
61
|
-
"@semantic-release/changelog": "^6.0.3",
|
|
62
|
-
"@semantic-release/git": "^10.0.1",
|
|
63
|
-
"@types/node": "^24.8.1",
|
|
64
|
-
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
65
|
-
"eslint": "^9.38.0",
|
|
66
|
-
"semantic-release": "^25.0.1",
|
|
67
|
-
"typescript": "^5.9.3",
|
|
68
|
-
"typescript-eslint": "^8.46.1",
|
|
69
|
-
"vitest": "^2.1.0"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "appium-desktop-driver",
|
|
3
|
+
"version": "1.4.1",
|
|
4
|
+
"description": "Appium driver for Windows",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"appium",
|
|
7
|
+
"desktop",
|
|
8
|
+
"uiautomation",
|
|
9
|
+
"powershell",
|
|
10
|
+
"automated testing",
|
|
11
|
+
"windows",
|
|
12
|
+
"verisoft"
|
|
13
|
+
],
|
|
14
|
+
"main": "build/lib/driver.js",
|
|
15
|
+
"bin": {
|
|
16
|
+
"desktop-driver-mcp": "build/lib/mcp/index.js"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -b",
|
|
20
|
+
"watch": "tsc -b --watch",
|
|
21
|
+
"lint": "eslint .",
|
|
22
|
+
"test": "npx vitest run",
|
|
23
|
+
"test:e2e": "npx vitest run --config vitest.e2e.config.ts",
|
|
24
|
+
"mcp:start": "node build/lib/mcp/index.js"
|
|
25
|
+
},
|
|
26
|
+
"author": "VeriSoft",
|
|
27
|
+
"license": "Apache-2.0",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/verisoft-ai/appium-desktop-driver.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/verisoft-ai/appium-desktop-driver/issues"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"appium": "^3.0.0-rc.2"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@appium/base-driver": "^10.1.0",
|
|
40
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
41
|
+
"bezier-easing": "^2.1.0",
|
|
42
|
+
"ffmpeg-static": "^5.2.0",
|
|
43
|
+
"koffi": "^2.14.1",
|
|
44
|
+
"webdriverio": "^9.0.0",
|
|
45
|
+
"xpath-analyzer": "^3.0.1",
|
|
46
|
+
"zod": "^4.3.6"
|
|
47
|
+
},
|
|
48
|
+
"appium": {
|
|
49
|
+
"driverName": "desktopdriver",
|
|
50
|
+
"automationName": "DesktopDriver",
|
|
51
|
+
"platformNames": [
|
|
52
|
+
"Windows"
|
|
53
|
+
],
|
|
54
|
+
"mainClass": "NovaWindowsDriver"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@appium/eslint-config-appium-ts": "^2.0.3",
|
|
58
|
+
"@appium/tsconfig": "^1.1.0",
|
|
59
|
+
"@appium/types": "^1.1.0",
|
|
60
|
+
"@eslint/js": "^9.38.0",
|
|
61
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
62
|
+
"@semantic-release/git": "^10.0.1",
|
|
63
|
+
"@types/node": "^24.8.1",
|
|
64
|
+
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
65
|
+
"eslint": "^9.38.0",
|
|
66
|
+
"semantic-release": "^25.0.1",
|
|
67
|
+
"typescript": "^5.9.3",
|
|
68
|
+
"typescript-eslint": "^8.46.1",
|
|
69
|
+
"vitest": "^2.1.0"
|
|
70
|
+
}
|
|
71
|
+
}
|
package/ROADMAP.md
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
# Roadmap
|
|
2
|
-
|
|
3
|
-
## Tier 1 — Complete What's Started
|
|
4
|
-
|
|
5
|
-
These are partially implemented or explicitly TODO'd in the code.
|
|
6
|
-
|
|
7
|
-
### 1. Touch & Pen Input
|
|
8
|
-
|
|
9
|
-
- [ ] `lib/commands/actions.ts:51-52` throws `NotImplementedError` for
|
|
10
|
-
touch/pen pointer types
|
|
11
|
-
- [ ] Enable stylus pressure simulation for drawing apps, touchscreen
|
|
12
|
-
device testing
|
|
13
|
-
|
|
14
|
-
### 2. Device Command Stubs
|
|
15
|
-
|
|
16
|
-
- [x] `lib/commands/device.ts` has stubs for: `pushFile`, `pullFile`,
|
|
17
|
-
`pullFolder`, `hideKeyboard`, `isKeyboardShown`, `activateApp`,
|
|
18
|
-
`terminateApp`, `isAppInstalled`
|
|
19
|
-
- [x] These are already in the Appium protocol — just need Windows
|
|
20
|
-
implementations
|
|
21
|
-
|
|
22
|
-
### 3. Cached Element Support
|
|
23
|
-
|
|
24
|
-
- [ ] `pushCacheRequest` exists but cached elements are not actually
|
|
25
|
-
usable yet
|
|
26
|
-
- [ ] Would significantly improve performance for large UI trees
|
|
27
|
-
|
|
28
|
-
### 4. Configurable Window Retry Parameters
|
|
29
|
-
|
|
30
|
-
- [x] `lib/commands/app.ts:113,136,137` — retries & sleep are hardcoded
|
|
31
|
-
at 20 × 500ms
|
|
32
|
-
- [x] Should be session capabilities: `ms:windowSwitchRetries`,
|
|
33
|
-
`ms:windowSwitchInterval`
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## Tier 2 — High-Value New Features
|
|
38
|
-
|
|
39
|
-
### 5. .NET Backend (Replace PowerShell)
|
|
40
|
-
|
|
41
|
-
- [ ] README explicitly mentions this as a planned migration
|
|
42
|
-
- [ ] Would dramatically improve: startup time, reliability, Unicode
|
|
43
|
-
support, complex tree traversal
|
|
44
|
-
- [ ] Suggested: embed a .NET named-pipe server as a sidecar process
|
|
45
|
-
|
|
46
|
-
### 6. Multi-Monitor Support
|
|
47
|
-
|
|
48
|
-
- [ ] Currently no way to target a specific display
|
|
49
|
-
- [ ] Add capability: `ms:targetMonitor` (index or device name)
|
|
50
|
-
- [ ] Affects: screenshot, `getWindowRect`, mouse coordinate
|
|
51
|
-
normalization
|
|
52
|
-
|
|
53
|
-
### 7. UI Automation Event Subscription
|
|
54
|
-
|
|
55
|
-
- [ ] Currently purely polling-based
|
|
56
|
-
- [ ] UIA supports native event callbacks: focus changed, property
|
|
57
|
-
changed, window opened/closed, structure changed
|
|
58
|
-
- [ ] Would enable `waitForElement` without polling, and event-driven
|
|
59
|
-
test patterns
|
|
60
|
-
- [ ] MCP equivalent: `subscribe_to_events` / `wait_for_event` tools
|
|
61
|
-
|
|
62
|
-
### 8. XPath 2.0 / Ancestor Axis Support
|
|
63
|
-
|
|
64
|
-
- [ ] Current XPath 1.0 (`lib/xpath/`) lacks `ancestor::`,
|
|
65
|
-
`following-sibling::`, etc.
|
|
66
|
-
- [ ] Many real-world locator patterns rely on these
|
|
67
|
-
|
|
68
|
-
### 9. OCR-Based Element Finding
|
|
69
|
-
|
|
70
|
-
- [ ] New locator strategy: `-windows ocr` using Windows.Media.Ocr API
|
|
71
|
-
(built into Win10+)
|
|
72
|
-
- [ ] Find elements by visible text when no accessibility properties
|
|
73
|
-
exist (games, legacy apps)
|
|
74
|
-
- [ ] MCP tool: `find_by_text_ocr`
|
|
75
|
-
|
|
76
|
-
### 10. Image-Based Element Matching
|
|
77
|
-
|
|
78
|
-
- [ ] New locator: `-windows image` (template matching)
|
|
79
|
-
- [ ] Appium already defines this strategy in the protocol
|
|
80
|
-
- [ ] Use OpenCV/sharp for pixel-based matching — useful when UIA tree
|
|
81
|
-
is flat/inaccessible
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## Tier 3 — Developer & AI Experience
|
|
86
|
-
|
|
87
|
-
### 11. MCP Session Pooling
|
|
88
|
-
|
|
89
|
-
- [ ] MCP server currently manages one session at a time
|
|
90
|
-
- [ ] Pool of sessions would allow parallel AI agent workflows on the
|
|
91
|
-
same machine
|
|
92
|
-
|
|
93
|
-
### 12. Structured Page Source (JSON)
|
|
94
|
-
|
|
95
|
-
- [ ] `getPageSource()` returns XML — hard to parse in AI contexts
|
|
96
|
-
- [ ] Add `windows: getPageSourceJson` extension command + MCP tool
|
|
97
|
-
returning a clean JSON tree with suggested selectors ranked by
|
|
98
|
-
stability
|
|
99
|
-
|
|
100
|
-
### 13. Visual Snapshot Tool (MCP)
|
|
101
|
-
|
|
102
|
-
- [ ] Combined screenshot + annotated element map (bounding boxes +
|
|
103
|
-
selectors overlaid)
|
|
104
|
-
- [ ] Single MCP call that gives the AI both visual and structural
|
|
105
|
-
context simultaneously
|
|
106
|
-
|
|
107
|
-
### 14. Accessibility Audit Command
|
|
108
|
-
|
|
109
|
-
- [ ] `windows: auditAccessibility` — traverse the UIA tree and report:
|
|
110
|
-
missing names, non-interactive controls with keyboard handlers,
|
|
111
|
-
invisible but enabled elements
|
|
112
|
-
- [ ] Useful for teams building accessible Windows apps
|
|
113
|
-
|
|
114
|
-
### 15. App Performance Telemetry
|
|
115
|
-
|
|
116
|
-
- [ ] Tap Windows Performance Counters: CPU %, memory, GDI objects,
|
|
117
|
-
handle count per PID
|
|
118
|
-
- [ ] Extension command: `windows: getAppMetrics`
|
|
119
|
-
- [ ] MCP tool: `get_app_performance`
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Tier 4 — Ecosystem & Reliability
|
|
124
|
-
|
|
125
|
-
### 16. Retry / Wait DSL
|
|
126
|
-
|
|
127
|
-
- [ ] Currently callers must implement their own polling
|
|
128
|
-
- [ ] Built-in `waitUntil` semantics: `windows: waitForElement`,
|
|
129
|
-
`windows: waitForCondition`
|
|
130
|
-
- [ ] Configurable timeout + interval, avoids sleep-based hacks in test
|
|
131
|
-
code
|
|
132
|
-
|
|
133
|
-
### 17. Session Recording & Replay
|
|
134
|
-
|
|
135
|
-
- [ ] Record all commands with timing to a JSON log
|
|
136
|
-
- [ ] Replay capability for regression testing without writing test
|
|
137
|
-
scripts
|
|
138
|
-
|
|
139
|
-
### 18. DPI-Aware Coordinate Handling
|
|
140
|
-
|
|
141
|
-
- [ ] `lib/winapi/user32.ts:727` — `getResolutionScalingFactor()`
|
|
142
|
-
exists but is unused
|
|
143
|
-
- [ ] Apply it consistently across mouse coordinates, element rects,
|
|
144
|
-
screenshots
|
|
145
|
-
- [ ] Critical for multi-DPI setups (Surface devices, 4K + 1080p mixed)
|
|
146
|
-
|
|
147
|
-
### 19. Remote Windows Support (WinRM/SSH)
|
|
148
|
-
|
|
149
|
-
- [ ] Currently assumes local process execution
|
|
150
|
-
- [ ] Long-term: route PowerShell/pipe commands over WinRM for remote
|
|
151
|
-
desktop automation
|
|
152
|
-
|
|
153
|
-
---
|
|
154
|
-
|
|
155
|
-
## Priority Summary
|
|
156
|
-
|
|
157
|
-
| Priority | Feature | Effort | Impact | Status |
|
|
158
|
-
| --- | --- | --- | --- | --- |
|
|
159
|
-
| P0 | Device command stubs | Low | High | [x] |
|
|
160
|
-
| P0 | Configurable window retry | Low | Medium | [x] |
|
|
161
|
-
| P1 | Touch/pen input | Medium | High | [ ] |
|
|
162
|
-
| P1 | DPI-aware coordinates | Medium | High | [ ] |
|
|
163
|
-
| P1 | `waitForElement` DSL | Medium | High | [ ] |
|
|
164
|
-
| P1 | Structured JSON page source (MCP) | Low | High | [ ] |
|
|
165
|
-
| P2 | UI Automation events | High | High | [ ] |
|
|
166
|
-
| P2 | XPath 2.0 / ancestor axis | Medium | Medium | [ ] |
|
|
167
|
-
| P2 | Visual snapshot MCP tool | Medium | High | [ ] |
|
|
168
|
-
| P2 | App performance telemetry | Medium | Medium | [ ] |
|
|
169
|
-
| P3 | OCR-based locator | High | High | [ ] |
|
|
170
|
-
| P3 | Image-based locator | High | Medium | [ ] |
|
|
171
|
-
| P3 | .NET backend migration | Very High | Very High | [ ] |
|
|
172
|
-
| P3 | Multi-monitor support | Medium | Medium | [ ] |
|
|
173
|
-
| P4 | MCP session pooling | High | Medium | [ ] |
|
|
174
|
-
| P4 | Session recording/replay | High | Medium | [ ] |
|
|
175
|
-
| P4 | Accessibility audit | Medium | Medium | [ ] |
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"appium-manager.d.ts","sourceRoot":"","sources":["../../../lib/mcp/appium-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAyD7C,qBAAa,aAAa;IACtB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAS;IAElB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAwD/C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAsBlC"}
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.AppiumManager = void 0;
|
|
37
|
-
const fs = __importStar(require("node:fs"));
|
|
38
|
-
const http = __importStar(require("node:http"));
|
|
39
|
-
const path = __importStar(require("node:path"));
|
|
40
|
-
const node_child_process_1 = require("node:child_process");
|
|
41
|
-
const POLL_INTERVAL_MS = 500;
|
|
42
|
-
const STARTUP_TIMEOUT_MS = 30_000;
|
|
43
|
-
const SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
44
|
-
function isAppiumReady(host, port) {
|
|
45
|
-
return new Promise((resolve) => {
|
|
46
|
-
const req = http.get({ hostname: host, port, path: '/status', timeout: 2000 }, (res) => {
|
|
47
|
-
let body = '';
|
|
48
|
-
res.on('data', (chunk) => { body += chunk; });
|
|
49
|
-
res.on('end', () => {
|
|
50
|
-
try {
|
|
51
|
-
const json = JSON.parse(body);
|
|
52
|
-
resolve(json?.value?.ready === true);
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
resolve(false);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
req.on('error', () => resolve(false));
|
|
60
|
-
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
function waitForAppium(host, port) {
|
|
64
|
-
return new Promise((resolve, reject) => {
|
|
65
|
-
const deadline = Date.now() + STARTUP_TIMEOUT_MS;
|
|
66
|
-
const poll = async () => {
|
|
67
|
-
if (await isAppiumReady(host, port)) {
|
|
68
|
-
resolve();
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
if (Date.now() >= deadline) {
|
|
72
|
-
reject(new Error(`Appium did not become ready within ${STARTUP_TIMEOUT_MS / 1000}s on ${host}:${port}`));
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
setTimeout(poll, POLL_INTERVAL_MS);
|
|
76
|
-
};
|
|
77
|
-
poll();
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
function resolveAppiumBinary(configBinary) {
|
|
81
|
-
if (configBinary) {
|
|
82
|
-
return configBinary;
|
|
83
|
-
}
|
|
84
|
-
// Prefer a local node_modules/.bin/appium (3 levels up from build/lib/mcp/)
|
|
85
|
-
const localBin = path.resolve(__dirname, '..', '..', '..', 'node_modules', '.bin', 'appium');
|
|
86
|
-
if (fs.existsSync(localBin)) {
|
|
87
|
-
return localBin;
|
|
88
|
-
}
|
|
89
|
-
// Fall back to appium on the system PATH (global install: npm install -g appium)
|
|
90
|
-
return 'appium';
|
|
91
|
-
}
|
|
92
|
-
class AppiumManager {
|
|
93
|
-
process = null;
|
|
94
|
-
managed = false;
|
|
95
|
-
async ensureRunning(config) {
|
|
96
|
-
const { appiumHost: host, appiumPort: port } = config;
|
|
97
|
-
if (await isAppiumReady(host, port)) {
|
|
98
|
-
process.stderr.write(`[MCP] Appium already running on ${host}:${port}\n`);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (!config.appiumAutoStart) {
|
|
102
|
-
throw new Error(`Appium is not running on ${host}:${port}.\n` +
|
|
103
|
-
`Start it with: appium --port ${port}\n` +
|
|
104
|
-
`Or set APPIUM_AUTO_START=true to start it automatically.`);
|
|
105
|
-
}
|
|
106
|
-
const binary = resolveAppiumBinary(config.appiumBinary);
|
|
107
|
-
process.stderr.write(`[MCP] Starting Appium: ${binary} --port ${port} --address ${host}\n`);
|
|
108
|
-
const child = (0, node_child_process_1.spawn)(binary, ['--port', String(port), '--address', host], {
|
|
109
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
110
|
-
shell: process.platform === 'win32',
|
|
111
|
-
});
|
|
112
|
-
// Wait for either a successful spawn or an early error.
|
|
113
|
-
// The 'spawn' event (Node ≥ 15.1) fires only after the OS has confirmed the
|
|
114
|
-
// process started; 'error' fires if it never starts (binary not found, etc.).
|
|
115
|
-
// Using setImmediate here would be a race — resolve could win before 'error' fires.
|
|
116
|
-
await new Promise((resolve, reject) => {
|
|
117
|
-
child.once('error', (err) => {
|
|
118
|
-
reject(new Error(`Failed to spawn Appium binary at "${binary}": ${err.message}\n` +
|
|
119
|
-
`Install Appium globally with: npm install -g appium\n` +
|
|
120
|
-
`Or set APPIUM_BINARY to the full path of the appium executable.`));
|
|
121
|
-
});
|
|
122
|
-
child.once('spawn', resolve);
|
|
123
|
-
});
|
|
124
|
-
this.process = child;
|
|
125
|
-
this.managed = true;
|
|
126
|
-
this.process.stdout?.on('data', (data) => {
|
|
127
|
-
process.stderr.write(`[Appium] ${data}`);
|
|
128
|
-
});
|
|
129
|
-
this.process.stderr?.on('data', (data) => {
|
|
130
|
-
process.stderr.write(`[Appium] ${data}`);
|
|
131
|
-
});
|
|
132
|
-
this.process.on('exit', (code) => {
|
|
133
|
-
process.stderr.write(`[MCP] Appium process exited with code ${code}\n`);
|
|
134
|
-
});
|
|
135
|
-
await waitForAppium(host, port);
|
|
136
|
-
process.stderr.write(`[MCP] Appium ready on ${host}:${port}\n`);
|
|
137
|
-
}
|
|
138
|
-
async shutdown() {
|
|
139
|
-
if (!this.managed || !this.process) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
process.stderr.write('[MCP] Stopping Appium...\n');
|
|
143
|
-
this.process.kill('SIGTERM');
|
|
144
|
-
const child = this.process;
|
|
145
|
-
await new Promise((resolve) => {
|
|
146
|
-
const timeout = setTimeout(() => {
|
|
147
|
-
child.kill('SIGKILL');
|
|
148
|
-
resolve();
|
|
149
|
-
}, SHUTDOWN_TIMEOUT_MS);
|
|
150
|
-
child.on('exit', () => {
|
|
151
|
-
clearTimeout(timeout);
|
|
152
|
-
resolve();
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
this.process = null;
|
|
156
|
-
this.managed = false;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
exports.AppiumManager = AppiumManager;
|
|
160
|
-
//# sourceMappingURL=appium-manager.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"appium-manager.js","sourceRoot":"","sources":["../../../lib/mcp/appium-manager.ts"],"names":[],"mappings":"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"}
|