appium-mcp 1.8.1 → 1.8.3
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/.prettierrc +1 -2
- package/CHANGELOG.md +12 -0
- package/dist/devicemanager/ios-manager.js +2 -2
- package/dist/devicemanager/ios-manager.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/locators/element-filter.d.ts.map +1 -1
- package/dist/locators/element-filter.js +4 -3
- package/dist/locators/element-filter.js.map +1 -1
- package/dist/locators/generate-all-locators.js +1 -1
- package/dist/locators/generate-all-locators.js.map +1 -1
- package/dist/locators/locator-generation.js +11 -11
- package/dist/locators/locator-generation.js.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +4 -2
- package/dist/session-store.js.map +1 -1
- package/dist/tests/__mocks__/@appium/support.d.ts.map +1 -1
- package/dist/tests/__mocks__/@appium/support.js +20 -21
- package/dist/tests/__mocks__/@appium/support.js.map +1 -1
- package/dist/tests/generate-all-locators.test.js +6 -10
- package/dist/tests/generate-all-locators.test.js.map +1 -1
- package/dist/tests/test-setup-wda.js +26 -16
- package/dist/tests/test-setup-wda.js.map +1 -1
- package/dist/tools/documentation/reasoning-rag.js +9 -9
- package/dist/tools/documentation/reasoning-rag.js.map +1 -1
- package/dist/tools/documentation/simple-pdf-indexer.js +14 -14
- package/dist/tools/documentation/simple-pdf-indexer.js.map +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/interactions/drag-and-drop.js +1 -1
- package/dist/tools/interactions/drag-and-drop.js.map +1 -1
- package/dist/tools/interactions/long-press.js +2 -2
- package/dist/tools/interactions/long-press.js.map +1 -1
- package/dist/tools/ios/boot-simulator.js +1 -1
- package/dist/tools/ios/boot-simulator.js.map +1 -1
- package/dist/tools/ios/install-wda.js +3 -4
- package/dist/tools/ios/install-wda.js.map +1 -1
- package/dist/tools/ios/setup-wda.d.ts.map +1 -1
- package/dist/tools/ios/setup-wda.js +2 -3
- package/dist/tools/ios/setup-wda.js.map +1 -1
- package/dist/tools/navigations/scroll-to-element.d.ts.map +1 -1
- package/dist/tools/navigations/scroll-to-element.js +16 -9
- package/dist/tools/navigations/scroll-to-element.js.map +1 -1
- package/dist/tools/navigations/scroll.js +6 -6
- package/dist/tools/navigations/scroll.js.map +1 -1
- package/dist/tools/navigations/swipe.js +2 -2
- package/dist/tools/navigations/swipe.js.map +1 -1
- package/dist/tools/session/create-session.js +1 -1
- package/dist/tools/session/create-session.js.map +1 -1
- package/dist/tools/session/select-device.js +4 -4
- package/dist/tools/session/select-device.js.map +1 -1
- package/dist/tools/test-generation/generate-tests.d.ts.map +1 -1
- package/dist/tools/test-generation/generate-tests.js +10 -12
- package/dist/tools/test-generation/generate-tests.js.map +1 -1
- package/dist/ui/mcp-ui-utils.js +28 -28
- package/dist/ui/mcp-ui-utils.js.map +1 -1
- package/eslint.config.js +6 -32
- package/package.json +3 -6
- package/server.json +2 -2
- package/src/devicemanager/ios-manager.ts +2 -2
- package/src/index.ts +1 -1
- package/src/locators/element-filter.ts +5 -3
- package/src/locators/generate-all-locators.ts +1 -1
- package/src/locators/locator-generation.ts +11 -11
- package/src/server.ts +2 -2
- package/src/session-store.ts +6 -2
- package/src/tests/__mocks__/@appium/support.ts +3 -4
- package/src/tests/generate-all-locators.test.ts +12 -13
- package/src/tests/test-setup-wda.ts +24 -16
- package/src/tools/documentation/reasoning-rag.ts +10 -10
- package/src/tools/documentation/simple-pdf-indexer.ts +23 -21
- package/src/tools/index.ts +1 -1
- package/src/tools/interactions/drag-and-drop.ts +1 -1
- package/src/tools/interactions/long-press.ts +2 -2
- package/src/tools/ios/boot-simulator.ts +1 -1
- package/src/tools/ios/install-wda.ts +4 -4
- package/src/tools/ios/setup-wda.ts +2 -3
- package/src/tools/navigations/scroll-to-element.ts +19 -9
- package/src/tools/navigations/scroll.ts +6 -6
- package/src/tools/navigations/swipe.ts +2 -2
- package/src/tools/session/create-session.ts +1 -1
- package/src/tools/session/select-device.ts +4 -4
- package/src/tools/test-generation/generate-tests.ts +10 -12
- package/src/types/appium-xcuitest-driver.d.ts +1 -1
- package/src/ui/mcp-ui-utils.ts +28 -28
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -39,9 +39,11 @@ function matchesAttributeFilters(
|
|
|
39
39
|
): boolean {
|
|
40
40
|
if (requireAttributes.length > 0) {
|
|
41
41
|
const hasRequiredAttr = requireAttributes.some(
|
|
42
|
-
attr => element.attributes && element.attributes[attr]
|
|
42
|
+
(attr) => element.attributes && element.attributes[attr]
|
|
43
43
|
);
|
|
44
|
-
if (!hasRequiredAttr)
|
|
44
|
+
if (!hasRequiredAttr) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
if (
|
|
@@ -87,7 +89,7 @@ function isInteractableElement(
|
|
|
87
89
|
];
|
|
88
90
|
|
|
89
91
|
return (
|
|
90
|
-
interactableTags.some(tag => element.tagName.includes(tag)) ||
|
|
92
|
+
interactableTags.some((tag) => element.tagName.includes(tag)) ||
|
|
91
93
|
element.attributes?.clickable === 'true' ||
|
|
92
94
|
element.attributes?.focusable === 'true'
|
|
93
95
|
);
|
|
@@ -104,7 +104,7 @@ function traverseAndProcessElements(
|
|
|
104
104
|
|
|
105
105
|
// Recursively process children (even if parent was filtered out)
|
|
106
106
|
if (element.children && element.children.length > 0) {
|
|
107
|
-
element.children.forEach(child =>
|
|
107
|
+
element.children.forEach((child) =>
|
|
108
108
|
traverseAndProcessElements(
|
|
109
109
|
child,
|
|
110
110
|
sourceXML,
|
|
@@ -82,7 +82,7 @@ export function getSimpleSuggestedLocators(
|
|
|
82
82
|
isNative: boolean = true
|
|
83
83
|
): Record<string, string> {
|
|
84
84
|
const res: Record<string, string> = {};
|
|
85
|
-
for (
|
|
85
|
+
for (const [strategyAlias, strategy] of SIMPLE_STRATEGY_MAPPINGS) {
|
|
86
86
|
// accessibility id is only supported in native context
|
|
87
87
|
if (!(strategy === 'accessibility id' && !isNative)) {
|
|
88
88
|
const value = attributes[strategyAlias];
|
|
@@ -103,7 +103,7 @@ export function getComplexSuggestedLocators(
|
|
|
103
103
|
isNative: boolean,
|
|
104
104
|
automationName: string
|
|
105
105
|
): Record<string, string> {
|
|
106
|
-
|
|
106
|
+
const complexLocators: Record<string, string | null> = {};
|
|
107
107
|
const domNode = findDOMNodeByPath(path, sourceDoc);
|
|
108
108
|
if (isNative) {
|
|
109
109
|
switch (automationName) {
|
|
@@ -342,7 +342,7 @@ export function getOptimalXPath(
|
|
|
342
342
|
];
|
|
343
343
|
const attrPairsPermutations: [string, string][] = attrsForPairs.flatMap(
|
|
344
344
|
(v1, i) =>
|
|
345
|
-
attrsForPairs.slice(i + 1).map(v2 => [v1, v2] as [string, string])
|
|
345
|
+
attrsForPairs.slice(i + 1).map((v2) => [v1, v2] as [string, string])
|
|
346
346
|
);
|
|
347
347
|
|
|
348
348
|
const cases = [
|
|
@@ -405,7 +405,7 @@ export function getOptimalXPath(
|
|
|
405
405
|
|
|
406
406
|
// If there's more than one sibling, append the index
|
|
407
407
|
if (childNodes.length > 1) {
|
|
408
|
-
|
|
408
|
+
const index = childNodes.indexOf(domNode);
|
|
409
409
|
xpath += `[${index + 1}]`;
|
|
410
410
|
}
|
|
411
411
|
}
|
|
@@ -440,7 +440,7 @@ export function getOptimalClassChain(
|
|
|
440
440
|
// BASE CASE #2: If this node has a unique class chain based on attributes, return it
|
|
441
441
|
let classChain: string, othersWithAttr: XMLNode[];
|
|
442
442
|
|
|
443
|
-
for (
|
|
443
|
+
for (const attrName of CHECKED_CLASS_CHAIN_ATTRIBUTES) {
|
|
444
444
|
const attrValue = (domNode as XMLElement).getAttribute?.(attrName);
|
|
445
445
|
if (_.isEmpty(attrValue)) {
|
|
446
446
|
continue;
|
|
@@ -462,7 +462,7 @@ export function getOptimalClassChain(
|
|
|
462
462
|
|
|
463
463
|
// If the attribute isn't actually unique, get its index too
|
|
464
464
|
if (othersWithAttr.length > 1) {
|
|
465
|
-
|
|
465
|
+
const index = othersWithAttr.indexOf(domNode);
|
|
466
466
|
classChain = `${classChain}[${index + 1}]`;
|
|
467
467
|
}
|
|
468
468
|
return classChain;
|
|
@@ -484,7 +484,7 @@ export function getOptimalClassChain(
|
|
|
484
484
|
|
|
485
485
|
// If there's more than one sibling, append the index
|
|
486
486
|
if (childNodes.length > 1) {
|
|
487
|
-
|
|
487
|
+
const index = childNodes.indexOf(domNode);
|
|
488
488
|
classChain += `[${index + 1}]`;
|
|
489
489
|
}
|
|
490
490
|
}
|
|
@@ -513,11 +513,11 @@ export function getOptimalPredicateString(
|
|
|
513
513
|
}
|
|
514
514
|
|
|
515
515
|
// BASE CASE #2: Check all attributes and try to find the best way
|
|
516
|
-
|
|
517
|
-
|
|
516
|
+
const xpathAttributes: string[] = [];
|
|
517
|
+
const predicateString: string[] = [];
|
|
518
518
|
let othersWithAttr: XMLNode[];
|
|
519
519
|
|
|
520
|
-
for (
|
|
520
|
+
for (const attrName of CHECKED_PREDICATE_ATTRIBUTES) {
|
|
521
521
|
const attrValue = (domNode as XMLElement).getAttribute?.(attrName);
|
|
522
522
|
if (_.isEmpty(attrValue)) {
|
|
523
523
|
continue;
|
|
@@ -579,7 +579,7 @@ export function getOptimalUiAutomatorSelector(
|
|
|
579
579
|
// BASE CASE #3: If looking for an element that is not inside
|
|
580
580
|
// the last direct child of the hierarchy, return null
|
|
581
581
|
const lastHierarchyChildIndex = (hierarchyChildren.length - 1).toString();
|
|
582
|
-
|
|
582
|
+
const pathArray = path.split('.');
|
|
583
583
|
const requestedHierarchyChildIndex = pathArray[0];
|
|
584
584
|
if (requestedHierarchyChildIndex !== lastHierarchyChildIndex) {
|
|
585
585
|
return null;
|
package/src/server.ts
CHANGED
|
@@ -15,11 +15,11 @@ registerResources(server);
|
|
|
15
15
|
registerTools(server);
|
|
16
16
|
|
|
17
17
|
// Handle client connection and disconnection events
|
|
18
|
-
server.on('connect', event => {
|
|
18
|
+
server.on('connect', (event) => {
|
|
19
19
|
log.info('Client connected:', event.session);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
server.on('disconnect', async event => {
|
|
22
|
+
server.on('disconnect', async (event) => {
|
|
23
23
|
log.info('Client disconnected:', event.session);
|
|
24
24
|
// Only try to clean up if there's an active session
|
|
25
25
|
if (hasActiveSession()) {
|
package/src/session-store.ts
CHANGED
|
@@ -97,8 +97,12 @@ export async function safeDeleteSession(): Promise<boolean> {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
export const getPlatformName = (driver: any): string => {
|
|
100
|
-
if (driver instanceof AndroidUiautomator2Driver)
|
|
101
|
-
|
|
100
|
+
if (driver instanceof AndroidUiautomator2Driver) {
|
|
101
|
+
return PLATFORM.android;
|
|
102
|
+
}
|
|
103
|
+
if (driver instanceof XCUITestDriver) {
|
|
104
|
+
return PLATFORM.ios;
|
|
105
|
+
}
|
|
102
106
|
|
|
103
107
|
if ((driver as Client).isAndroid) {
|
|
104
108
|
return PLATFORM.android;
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// This avoids the ESM/CommonJS mismatch with uuid dependency
|
|
3
3
|
|
|
4
4
|
export const logger = {
|
|
5
|
-
getLogger: (name: string) =>
|
|
5
|
+
getLogger: (name: string) =>
|
|
6
6
|
// Simple logger implementation for tests
|
|
7
7
|
// No-op functions that match the logger interface
|
|
8
|
-
|
|
8
|
+
({
|
|
9
9
|
debug: (message: string, ...args: any[]) => {
|
|
10
10
|
// Silent in tests by default
|
|
11
11
|
},
|
|
@@ -21,8 +21,7 @@ export const logger = {
|
|
|
21
21
|
trace: (message: string, ...args: any[]) => {
|
|
22
22
|
// Silent in tests by default
|
|
23
23
|
},
|
|
24
|
-
}
|
|
25
|
-
},
|
|
24
|
+
}),
|
|
26
25
|
};
|
|
27
26
|
|
|
28
27
|
// Export other commonly used utilities from @appium/support if needed
|
|
@@ -75,9 +75,9 @@ describe('generateAllElementLocators', () => {
|
|
|
75
75
|
// If the filter works, either the result will be empty (if no buttons found)
|
|
76
76
|
// or all elements will be buttons
|
|
77
77
|
if (result.length > 0) {
|
|
78
|
-
expect(
|
|
79
|
-
|
|
80
|
-
);
|
|
78
|
+
expect(
|
|
79
|
+
result.every((element) => element.tagName.includes('Button'))
|
|
80
|
+
).toBe(true);
|
|
81
81
|
}
|
|
82
82
|
});
|
|
83
83
|
|
|
@@ -88,7 +88,7 @@ describe('generateAllElementLocators', () => {
|
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
// Verify no Button elements are included
|
|
91
|
-
expect(result.every(element => !element.tagName.includes('Button'))).toBe(
|
|
91
|
+
expect(result.every((element) => !element.tagName.includes('Button'))).toBe(
|
|
92
92
|
true
|
|
93
93
|
);
|
|
94
94
|
});
|
|
@@ -129,12 +129,11 @@ describe('generateAllElementLocators', () => {
|
|
|
129
129
|
];
|
|
130
130
|
|
|
131
131
|
expect(
|
|
132
|
-
result.every(
|
|
133
|
-
|
|
134
|
-
interactableTags.some(tag => element.tagName.includes(tag)) ||
|
|
132
|
+
result.every(
|
|
133
|
+
(element) =>
|
|
134
|
+
interactableTags.some((tag) => element.tagName.includes(tag)) ||
|
|
135
135
|
element.clickable === true
|
|
136
|
-
|
|
137
|
-
})
|
|
136
|
+
)
|
|
138
137
|
).toBe(true);
|
|
139
138
|
}
|
|
140
139
|
});
|
|
@@ -160,9 +159,9 @@ describe('generateAllElementLocators', () => {
|
|
|
160
159
|
];
|
|
161
160
|
|
|
162
161
|
expect(
|
|
163
|
-
result.every(element =>
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
result.every((element) =>
|
|
163
|
+
interactableTags.some((tag) => element.tagName.includes(tag))
|
|
164
|
+
)
|
|
166
165
|
).toBe(true);
|
|
167
166
|
}
|
|
168
167
|
});
|
|
@@ -174,6 +173,6 @@ describe('generateAllElementLocators', () => {
|
|
|
174
173
|
});
|
|
175
174
|
|
|
176
175
|
// Verify all elements are clickable
|
|
177
|
-
expect(result.every(element => element.clickable === true)).toBe(true);
|
|
176
|
+
expect(result.every((element) => element.clickable === true)).toBe(true);
|
|
178
177
|
});
|
|
179
178
|
});
|
|
@@ -31,14 +31,18 @@ async function downloadFile(url: string, destPath: string): Promise<void> {
|
|
|
31
31
|
const file = fs.createWriteStream(destPath);
|
|
32
32
|
|
|
33
33
|
https
|
|
34
|
-
.get(url, response => {
|
|
34
|
+
.get(url, async (response) => {
|
|
35
35
|
// Handle redirects
|
|
36
36
|
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
37
37
|
file.close();
|
|
38
38
|
fs.unlinkSync(destPath);
|
|
39
|
-
|
|
40
|
-
.
|
|
41
|
-
|
|
39
|
+
try {
|
|
40
|
+
await downloadFile(response.headers.location!, destPath);
|
|
41
|
+
resolve();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
reject(err);
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
if (response.statusCode !== 200) {
|
|
@@ -55,7 +59,7 @@ async function downloadFile(url: string, destPath: string): Promise<void> {
|
|
|
55
59
|
);
|
|
56
60
|
let downloadedSize = 0;
|
|
57
61
|
|
|
58
|
-
response.on('data', chunk => {
|
|
62
|
+
response.on('data', (chunk) => {
|
|
59
63
|
downloadedSize += chunk.length;
|
|
60
64
|
const percent = ((downloadedSize / totalSize) * 100).toFixed(1);
|
|
61
65
|
process.stdout.write(
|
|
@@ -71,7 +75,7 @@ async function downloadFile(url: string, destPath: string): Promise<void> {
|
|
|
71
75
|
resolve();
|
|
72
76
|
});
|
|
73
77
|
})
|
|
74
|
-
.on('error', err => {
|
|
78
|
+
.on('error', (err) => {
|
|
75
79
|
file.close();
|
|
76
80
|
fs.unlinkSync(destPath);
|
|
77
81
|
reject(err);
|
|
@@ -102,10 +106,10 @@ async function getLatestWDAVersion(): Promise<string> {
|
|
|
102
106
|
};
|
|
103
107
|
|
|
104
108
|
https
|
|
105
|
-
.get(options, response => {
|
|
109
|
+
.get(options, (response) => {
|
|
106
110
|
let data = '';
|
|
107
111
|
|
|
108
|
-
response.on('data', chunk => {
|
|
112
|
+
response.on('data', (chunk) => {
|
|
109
113
|
data += chunk;
|
|
110
114
|
});
|
|
111
115
|
|
|
@@ -124,7 +128,7 @@ async function getLatestWDAVersion(): Promise<string> {
|
|
|
124
128
|
}
|
|
125
129
|
});
|
|
126
130
|
})
|
|
127
|
-
.on('error', err => {
|
|
131
|
+
.on('error', (err) => {
|
|
128
132
|
reject(err);
|
|
129
133
|
});
|
|
130
134
|
});
|
|
@@ -163,7 +167,7 @@ async function main() {
|
|
|
163
167
|
// Show app contents
|
|
164
168
|
const appContents = fs.readdirSync(appPath);
|
|
165
169
|
console.log('\n📋 Cached App bundle contents:');
|
|
166
|
-
appContents.forEach(item => {
|
|
170
|
+
appContents.forEach((item) => {
|
|
167
171
|
console.log(` - ${item}`);
|
|
168
172
|
});
|
|
169
173
|
|
|
@@ -204,7 +208,7 @@ async function main() {
|
|
|
204
208
|
// List contents
|
|
205
209
|
console.log('\n📋 Extracted contents:');
|
|
206
210
|
const contents = fs.readdirSync(extractDir);
|
|
207
|
-
contents.forEach(item => {
|
|
211
|
+
contents.forEach((item) => {
|
|
208
212
|
const itemPath = path.join(extractDir, item);
|
|
209
213
|
const isDir = fs.statSync(itemPath).isDirectory();
|
|
210
214
|
console.log(` ${isDir ? '📁' : '📄'} ${item}`);
|
|
@@ -218,7 +222,7 @@ async function main() {
|
|
|
218
222
|
// Show app contents
|
|
219
223
|
const appContents = fs.readdirSync(appPath);
|
|
220
224
|
console.log('\n App bundle contents:');
|
|
221
|
-
appContents.forEach(item => {
|
|
225
|
+
appContents.forEach((item) => {
|
|
222
226
|
console.log(` - ${item}`);
|
|
223
227
|
});
|
|
224
228
|
} else {
|
|
@@ -241,7 +245,11 @@ async function main() {
|
|
|
241
245
|
}
|
|
242
246
|
|
|
243
247
|
// Run main function
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
})
|
|
248
|
+
(async () => {
|
|
249
|
+
try {
|
|
250
|
+
await main();
|
|
251
|
+
} catch (error: any) {
|
|
252
|
+
console.error('\n💥 Unexpected error:', error);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
})();
|
|
@@ -220,8 +220,8 @@ export class ReasoningRAG {
|
|
|
220
220
|
for (let i = 0; i < chunks.length; i += batchSize) {
|
|
221
221
|
const batch = chunks.slice(i, i + batchSize);
|
|
222
222
|
|
|
223
|
-
const batchPromises = batch.flatMap(chunk =>
|
|
224
|
-
configs.map(config =>
|
|
223
|
+
const batchPromises = batch.flatMap((chunk) =>
|
|
224
|
+
configs.map((config) =>
|
|
225
225
|
this.performReasoning(chunk.pageContent, config, query)
|
|
226
226
|
)
|
|
227
227
|
);
|
|
@@ -249,16 +249,16 @@ export class ReasoningRAG {
|
|
|
249
249
|
): Promise<string> {
|
|
250
250
|
// Extract all reasoning outputs
|
|
251
251
|
const summaries = reasoningResults
|
|
252
|
-
.filter(result => result.metadata.task === 'summarization')
|
|
253
|
-
.map(result => result.reasoningOutput);
|
|
252
|
+
.filter((result) => result.metadata.task === 'summarization')
|
|
253
|
+
.map((result) => result.reasoningOutput);
|
|
254
254
|
|
|
255
255
|
const analyses = reasoningResults
|
|
256
|
-
.filter(result => result.metadata.task === 'analysis')
|
|
257
|
-
.map(result => result.reasoningOutput);
|
|
256
|
+
.filter((result) => result.metadata.task === 'analysis')
|
|
257
|
+
.map((result) => result.reasoningOutput);
|
|
258
258
|
|
|
259
259
|
const qaResults = reasoningResults
|
|
260
|
-
.filter(result => result.metadata.task === 'question-answering')
|
|
261
|
-
.map(result => result.reasoningOutput);
|
|
260
|
+
.filter((result) => result.metadata.task === 'question-answering')
|
|
261
|
+
.map((result) => result.reasoningOutput);
|
|
262
262
|
|
|
263
263
|
// Combine all insights
|
|
264
264
|
let comprehensiveSummary = `## Query: ${query}\n\n`;
|
|
@@ -332,7 +332,7 @@ export class ReasoningRAG {
|
|
|
332
332
|
];
|
|
333
333
|
|
|
334
334
|
// Filter configs based on requested tasks
|
|
335
|
-
const filteredConfigs = configs.filter(config =>
|
|
335
|
+
const filteredConfigs = configs.filter((config) =>
|
|
336
336
|
reasoningTasks.includes(config.task)
|
|
337
337
|
);
|
|
338
338
|
|
|
@@ -355,7 +355,7 @@ export class ReasoningRAG {
|
|
|
355
355
|
|
|
356
356
|
// Step 5: Extract best answer from reasoning results
|
|
357
357
|
const qaResults = reasoningResults.filter(
|
|
358
|
-
result =>
|
|
358
|
+
(result) =>
|
|
359
359
|
result.metadata.task === 'question-answering' &&
|
|
360
360
|
!result.metadata.error
|
|
361
361
|
);
|
|
@@ -72,7 +72,7 @@ async function saveDocuments(
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// Serialize the new documents
|
|
75
|
-
const serializedNew = documents.map(doc => ({
|
|
75
|
+
const serializedNew = documents.map((doc) => ({
|
|
76
76
|
pageContent: doc.pageContent,
|
|
77
77
|
metadata: doc.metadata,
|
|
78
78
|
}));
|
|
@@ -357,12 +357,12 @@ export async function indexAllMarkdownFiles(
|
|
|
357
357
|
// Add file metadata to each document
|
|
358
358
|
const filename = path.basename(markdownFile);
|
|
359
359
|
const relativePath = path.relative(dirPath, markdownFile);
|
|
360
|
-
documents.forEach(doc => {
|
|
360
|
+
documents.forEach((doc) => {
|
|
361
361
|
doc.metadata = {
|
|
362
362
|
...doc.metadata,
|
|
363
363
|
source: markdownFile,
|
|
364
|
-
filename
|
|
365
|
-
relativePath
|
|
364
|
+
filename,
|
|
365
|
+
relativePath,
|
|
366
366
|
};
|
|
367
367
|
});
|
|
368
368
|
|
|
@@ -482,25 +482,27 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
482
482
|
if (indexSingleFile) {
|
|
483
483
|
// Index a single Markdown file
|
|
484
484
|
log.info(`Indexing single Markdown file: ${markdownPath}`);
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
.
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
});
|
|
485
|
+
try {
|
|
486
|
+
await indexMarkdown(markdownPath, chunkSize, chunkOverlap);
|
|
487
|
+
process.exit(0);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
log.error('Indexing failed:', error);
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
493
492
|
} else {
|
|
494
493
|
// Index all Markdown files in the directory
|
|
495
494
|
log.info(`Indexing all Markdown files in directory: ${markdownPath}`);
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
495
|
+
try {
|
|
496
|
+
const indexedFiles = await indexAllMarkdownFiles(
|
|
497
|
+
markdownPath,
|
|
498
|
+
chunkSize,
|
|
499
|
+
chunkOverlap
|
|
500
|
+
);
|
|
501
|
+
log.info(`Successfully indexed ${indexedFiles.length} Markdown files`);
|
|
502
|
+
process.exit(0);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
log.error('Indexing failed:', error);
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
505
507
|
}
|
|
506
508
|
}
|
package/src/tools/index.ts
CHANGED
|
@@ -69,7 +69,7 @@ export default function registerTools(server: FastMCP): void {
|
|
|
69
69
|
JSON.stringify(obj, (key, value) => {
|
|
70
70
|
if (
|
|
71
71
|
key &&
|
|
72
|
-
SENSITIVE_KEYS.some(k => key.toLowerCase().includes(k))
|
|
72
|
+
SENSITIVE_KEYS.some((k) => key.toLowerCase().includes(k))
|
|
73
73
|
) {
|
|
74
74
|
return '[REDACTED]';
|
|
75
75
|
}
|
|
@@ -28,7 +28,7 @@ async function performDragAndDrop(
|
|
|
28
28
|
{ type: 'pointerMove', duration: 0, x: startX, y: startY },
|
|
29
29
|
{ type: 'pointerDown', button: 0 },
|
|
30
30
|
{ type: 'pause', duration: longPressDuration },
|
|
31
|
-
{ type: 'pointerMove', duration
|
|
31
|
+
{ type: 'pointerMove', duration, x: endX, y: endY },
|
|
32
32
|
{ type: 'pause', duration: DROP_PAUSE_DURATION_MS },
|
|
33
33
|
{ type: 'pointerUp', button: 0 },
|
|
34
34
|
],
|
|
@@ -54,7 +54,7 @@ export default function longPress(server: FastMCP): void {
|
|
|
54
54
|
actions: [
|
|
55
55
|
{ type: 'pointerMove', duration: 0, x, y },
|
|
56
56
|
{ type: 'pointerDown', button: 0 },
|
|
57
|
-
{ type: 'pause', duration
|
|
57
|
+
{ type: 'pause', duration },
|
|
58
58
|
{ type: 'pointerUp', button: 0 },
|
|
59
59
|
],
|
|
60
60
|
},
|
|
@@ -85,7 +85,7 @@ export default function longPress(server: FastMCP): void {
|
|
|
85
85
|
actions: [
|
|
86
86
|
{ type: 'pointerMove', duration: 0, x, y },
|
|
87
87
|
{ type: 'pointerDown', button: 0 },
|
|
88
|
-
{ type: 'pause', duration
|
|
88
|
+
{ type: 'pause', duration },
|
|
89
89
|
{ type: 'pointerUp', button: 0 },
|
|
90
90
|
],
|
|
91
91
|
},
|
|
@@ -34,7 +34,7 @@ export default function bootSimulator(server: any): void {
|
|
|
34
34
|
const simulators = await iosManager.listSimulators();
|
|
35
35
|
|
|
36
36
|
// Find the simulator with the given UDID
|
|
37
|
-
const simulator = simulators.find(sim => sim.udid === udid);
|
|
37
|
+
const simulator = simulators.find((sim) => sim.udid === udid);
|
|
38
38
|
|
|
39
39
|
if (!simulator) {
|
|
40
40
|
throw new Error(
|
|
@@ -28,7 +28,7 @@ async function getLatestWDAVersion(): Promise<string> {
|
|
|
28
28
|
|
|
29
29
|
const entries = await readdir(wdaCacheDir);
|
|
30
30
|
const versions = await Promise.all(
|
|
31
|
-
entries.map(async dir => {
|
|
31
|
+
entries.map(async (dir) => {
|
|
32
32
|
const dirPath = path.join(wdaCacheDir, dir);
|
|
33
33
|
const stats = await stat(dirPath);
|
|
34
34
|
return stats.isDirectory() ? dir : null;
|
|
@@ -37,10 +37,10 @@ async function getLatestWDAVersion(): Promise<string> {
|
|
|
37
37
|
|
|
38
38
|
const filteredVersions = versions
|
|
39
39
|
.filter((v): v is string => v !== null)
|
|
40
|
-
.sort((a, b) =>
|
|
40
|
+
.sort((a, b) =>
|
|
41
41
|
// Simple version comparison - you might want to use semver for more complex versions
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
b.localeCompare(a, undefined, { numeric: true })
|
|
43
|
+
);
|
|
44
44
|
|
|
45
45
|
if (filteredVersions.length === 0) {
|
|
46
46
|
throw new Error(
|
|
@@ -6,8 +6,7 @@ import { exec } from 'child_process';
|
|
|
6
6
|
import { promisify } from 'util';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { access, mkdir, unlink } from 'fs/promises';
|
|
9
|
-
import { constants } from 'fs';
|
|
10
|
-
import { createWriteStream } from 'fs';
|
|
9
|
+
import { constants, createWriteStream } from 'fs';
|
|
11
10
|
import { pipeline } from 'stream/promises';
|
|
12
11
|
import os from 'os';
|
|
13
12
|
import axios from 'axios';
|
|
@@ -196,7 +195,7 @@ export default function setupWDA(server: any): void {
|
|
|
196
195
|
text: `${JSON.stringify(
|
|
197
196
|
{
|
|
198
197
|
version: wdaVersion,
|
|
199
|
-
platform
|
|
198
|
+
platform,
|
|
200
199
|
architecture: archStr,
|
|
201
200
|
wdaAppPath: appPath,
|
|
202
201
|
wdaCachePath: `~/.cache/appium-mcp/wda/${wdaVersion}`,
|
|
@@ -24,7 +24,7 @@ const getValue = (xpath: string, expression: string): string => {
|
|
|
24
24
|
// Extracts the value from an XPath expression.
|
|
25
25
|
let start = xpath.indexOf(expression) + expression.length;
|
|
26
26
|
start = xpath.indexOf("'", start) + 1;
|
|
27
|
-
|
|
27
|
+
const end = xpath.indexOf("'", start);
|
|
28
28
|
return xpath.substring(start, end);
|
|
29
29
|
};
|
|
30
30
|
|
|
@@ -33,26 +33,30 @@ const transformXPath = (
|
|
|
33
33
|
): { strategy: string; selector: string } => {
|
|
34
34
|
// normalize xpath expression by replacing " by '
|
|
35
35
|
xpath = xpath.replace(/"/g, "'");
|
|
36
|
-
if (xpath.includes('@text='))
|
|
36
|
+
if (xpath.includes('@text=')) {
|
|
37
37
|
return { strategy: 'text', selector: getValue(xpath, '@text=') };
|
|
38
|
+
}
|
|
38
39
|
|
|
39
|
-
if (xpath.includes('@content-desc='))
|
|
40
|
+
if (xpath.includes('@content-desc=')) {
|
|
40
41
|
return {
|
|
41
42
|
strategy: 'description',
|
|
42
43
|
selector: getValue(xpath, '@content-desc='),
|
|
43
44
|
};
|
|
45
|
+
}
|
|
44
46
|
|
|
45
|
-
if (xpath.includes('contains(@text,'))
|
|
47
|
+
if (xpath.includes('contains(@text,')) {
|
|
46
48
|
return {
|
|
47
49
|
strategy: 'textContains',
|
|
48
50
|
selector: getValue(xpath, 'contains(@text,'),
|
|
49
51
|
};
|
|
52
|
+
}
|
|
50
53
|
|
|
51
|
-
if (xpath.includes('contains(@content-desc,'))
|
|
54
|
+
if (xpath.includes('contains(@content-desc,')) {
|
|
52
55
|
return {
|
|
53
56
|
strategy: 'descriptionContains',
|
|
54
57
|
selector: getValue(xpath, 'contains(@content-desc,'),
|
|
55
58
|
};
|
|
59
|
+
}
|
|
56
60
|
|
|
57
61
|
throw new Error(
|
|
58
62
|
`Unsupported XPath expression: ${xpath}. Supported expressions are: @text, @content-desc, contains(@text, ...), contains(@content-desc, ...)`
|
|
@@ -63,9 +67,15 @@ const transformLocator = (
|
|
|
63
67
|
strategy: string,
|
|
64
68
|
selector: string
|
|
65
69
|
): { strategy: string; selector: string } => {
|
|
66
|
-
if (strategy === 'id')
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
if (strategy === 'id') {
|
|
71
|
+
return { strategy: 'resourceId', selector };
|
|
72
|
+
}
|
|
73
|
+
if (strategy === 'xpath') {
|
|
74
|
+
return transformXPath(selector);
|
|
75
|
+
}
|
|
76
|
+
if (strategy === 'class name') {
|
|
77
|
+
return { strategy: 'className', selector };
|
|
78
|
+
}
|
|
69
79
|
|
|
70
80
|
return { strategy, selector };
|
|
71
81
|
};
|
|
@@ -103,7 +113,7 @@ async function performiOSScroll(driver: any, direction: string): Promise<void> {
|
|
|
103
113
|
const { width, height } = await driver.getWindowRect();
|
|
104
114
|
|
|
105
115
|
await driver.execute('mobile: scroll', {
|
|
106
|
-
direction
|
|
116
|
+
direction,
|
|
107
117
|
startX: width / 2,
|
|
108
118
|
startY: direction === 'up' ? height * 0.3 : height * 0.7,
|
|
109
119
|
endX: width / 2,
|
|
@@ -72,18 +72,18 @@ export default function scroll(server: any): void {
|
|
|
72
72
|
? await (driver as Client).executeScript('mobile: scroll', [
|
|
73
73
|
{
|
|
74
74
|
direction: args.direction,
|
|
75
|
-
startX
|
|
76
|
-
startY
|
|
75
|
+
startX,
|
|
76
|
+
startY,
|
|
77
77
|
endX: startX,
|
|
78
|
-
endY
|
|
78
|
+
endY,
|
|
79
79
|
},
|
|
80
80
|
])
|
|
81
81
|
: await (driver as any).execute('mobile: scroll', {
|
|
82
82
|
direction: args.direction,
|
|
83
|
-
startX
|
|
84
|
-
startY
|
|
83
|
+
startX,
|
|
84
|
+
startY,
|
|
85
85
|
endX: startX,
|
|
86
|
-
endY
|
|
86
|
+
endY,
|
|
87
87
|
});
|
|
88
88
|
} else {
|
|
89
89
|
throw new Error(
|
|
@@ -67,7 +67,7 @@ async function performAndroidSwipe(
|
|
|
67
67
|
{ type: 'pointerMove', duration: 0, x: startX, y: startY },
|
|
68
68
|
{ type: 'pointerDown', button: 0 },
|
|
69
69
|
{ type: 'pause', duration: 250 },
|
|
70
|
-
{ type: 'pointerMove', duration
|
|
70
|
+
{ type: 'pointerMove', duration, x: endX, y: endY },
|
|
71
71
|
{ type: 'pointerUp', button: 0 },
|
|
72
72
|
],
|
|
73
73
|
},
|
|
@@ -102,7 +102,7 @@ async function performiOSSwipe(
|
|
|
102
102
|
{ type: 'pointerMove', duration: 0, x: startX, y: startY },
|
|
103
103
|
{ type: 'pointerDown', button: 0 },
|
|
104
104
|
{ type: 'pause', duration: 200 },
|
|
105
|
-
{ type: 'pointerMove', duration
|
|
105
|
+
{ type: 'pointerMove', duration, x: endX, y: endY },
|
|
106
106
|
{ type: 'pause', duration: 50 },
|
|
107
107
|
{ type: 'pointerUp', button: 0 },
|
|
108
108
|
],
|