appium-mcp 1.71.0 → 1.71.2
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/CHANGELOG.md +12 -0
- package/dist/command.d.ts.map +1 -1
- package/dist/command.js +14 -1
- package/dist/command.js.map +1 -1
- package/dist/devicemanager/adb-manager.d.ts +6 -6
- package/dist/devicemanager/adb-manager.d.ts.map +1 -1
- package/dist/devicemanager/adb-manager.js +15 -15
- package/dist/devicemanager/adb-manager.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/scripts/simple-index-documentation.js +1 -1
- package/dist/scripts/simple-index-documentation.js.map +1 -1
- package/dist/scripts/simple-query-documentation.js +1 -1
- package/dist/scripts/simple-query-documentation.js.map +1 -1
- package/dist/session-store.d.ts +16 -0
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +7 -3
- package/dist/session-store.js.map +1 -1
- package/dist/tests/session-store.test.js +0 -19
- package/dist/tests/session-store.test.js.map +1 -1
- package/dist/tests/test-setup-wda.js +1 -1
- package/dist/tests/test-setup-wda.js.map +1 -1
- package/dist/tools/documentation/reasoning-rag.d.ts +16 -16
- package/dist/tools/documentation/reasoning-rag.d.ts.map +1 -1
- package/dist/tools/documentation/reasoning-rag.js +100 -100
- package/dist/tools/documentation/reasoning-rag.js.map +1 -1
- package/dist/tools/documentation/sentence-transformers-embeddings.d.ts +8 -8
- package/dist/tools/documentation/sentence-transformers-embeddings.d.ts.map +1 -1
- package/dist/tools/documentation/sentence-transformers-embeddings.js +36 -36
- package/dist/tools/documentation/sentence-transformers-embeddings.js.map +1 -1
- package/dist/tools/interactions/keyboard.d.ts.map +1 -1
- package/dist/tools/interactions/keyboard.js +19 -19
- package/dist/tools/interactions/keyboard.js.map +1 -1
- package/dist/tools/session/attach-session.d.ts.map +1 -1
- package/dist/tools/session/attach-session.js +19 -19
- package/dist/tools/session/attach-session.js.map +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/command.ts +30 -0
- package/src/devicemanager/adb-manager.ts +18 -18
- package/src/index.ts +1 -1
- package/src/resources/submodules.zip +0 -0
- package/src/scripts/simple-index-documentation.ts +1 -1
- package/src/scripts/simple-query-documentation.ts +1 -1
- package/src/session-store.ts +8 -4
- package/src/tools/documentation/reasoning-rag.ts +153 -153
- package/src/tools/documentation/sentence-transformers-embeddings.ts +50 -50
- package/src/tools/interactions/keyboard.ts +26 -26
- package/src/tools/session/attach-session.ts +23 -23
|
@@ -92,6 +92,24 @@ export class ADBManager {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Get ADB instance for specific device operations
|
|
97
|
+
* This method ensures we reuse the singleton instance
|
|
98
|
+
* @param udid Optional device UDID for device-specific operations
|
|
99
|
+
* @returns Promise<ADB> The ADB instance
|
|
100
|
+
*/
|
|
101
|
+
public async getADBForDevice(udid?: string): Promise<ADB> {
|
|
102
|
+
if (!this.isADBInitialized()) {
|
|
103
|
+
await this.initialize({ udid });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!this.adbInstance) {
|
|
107
|
+
throw new Error('ADB instance not available');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return this.adbInstance;
|
|
111
|
+
}
|
|
112
|
+
|
|
95
113
|
/**
|
|
96
114
|
* Create ADB instance with proper error handling
|
|
97
115
|
* @param options ADB configuration options
|
|
@@ -118,24 +136,6 @@ export class ADBManager {
|
|
|
118
136
|
throw new Error(`ADB initialization failed: ${error}`);
|
|
119
137
|
}
|
|
120
138
|
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Get ADB instance for specific device operations
|
|
124
|
-
* This method ensures we reuse the singleton instance
|
|
125
|
-
* @param udid Optional device UDID for device-specific operations
|
|
126
|
-
* @returns Promise<ADB> The ADB instance
|
|
127
|
-
*/
|
|
128
|
-
public async getADBForDevice(udid?: string): Promise<ADB> {
|
|
129
|
-
if (!this.isADBInitialized()) {
|
|
130
|
-
await this.initialize({ udid });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (!this.adbInstance) {
|
|
134
|
-
throw new Error('ADB instance not available');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return this.adbInstance;
|
|
138
|
-
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
/**
|
package/src/index.ts
CHANGED
|
Binary file
|
package/src/session-store.ts
CHANGED
|
@@ -109,10 +109,7 @@ export function setSession(
|
|
|
109
109
|
// `getAppiumSessionCapabilities()` may return metadata fields without the
|
|
110
110
|
// `appium:` prefix, so accept both namespaced and non-namespaced variants.
|
|
111
111
|
const metadata: SessionMetadata = {
|
|
112
|
-
platform:
|
|
113
|
-
(capabilities.platformName as string | undefined) ??
|
|
114
|
-
(capabilities['appium:platformName'] as string | undefined) ??
|
|
115
|
-
null,
|
|
112
|
+
platform: (capabilities.platformName as string | undefined) ?? null,
|
|
116
113
|
automationName:
|
|
117
114
|
(capabilities['appium:automationName'] as string | undefined) ??
|
|
118
115
|
(capabilities.automationName as string | undefined) ??
|
|
@@ -215,6 +212,13 @@ export function setCurrentContext(
|
|
|
215
212
|
return true;
|
|
216
213
|
}
|
|
217
214
|
|
|
215
|
+
export function getSessionInfo(sessionId: string | null): SessionInfo | null {
|
|
216
|
+
if (!sessionId) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
return sessions.get(sessionId) ?? null;
|
|
220
|
+
}
|
|
221
|
+
|
|
218
222
|
export function getCurrentContext(sessionId?: string): string | null {
|
|
219
223
|
const id = sessionId ?? activeSessionId;
|
|
220
224
|
if (!id) {
|
|
@@ -60,7 +60,159 @@ export class ReasoningRAG {
|
|
|
60
60
|
private isInitialized: boolean = false;
|
|
61
61
|
|
|
62
62
|
constructor() {
|
|
63
|
-
this.initializeTransformers();
|
|
63
|
+
void this.initializeTransformers();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Enhanced RAG query with reasoning capabilities
|
|
68
|
+
*/
|
|
69
|
+
async queryWithReasoning(
|
|
70
|
+
query: string,
|
|
71
|
+
options: {
|
|
72
|
+
topK?: number;
|
|
73
|
+
reasoningTasks?: ReasoningTask[];
|
|
74
|
+
customConfigs?: ReasoningConfig[];
|
|
75
|
+
} = {}
|
|
76
|
+
): Promise<EnhancedRAGResponse> {
|
|
77
|
+
const {
|
|
78
|
+
topK = 50,
|
|
79
|
+
reasoningTasks = ['summarization', 'question-answering'],
|
|
80
|
+
customConfigs,
|
|
81
|
+
} = options;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
log.info(`Starting reasoning-enhanced RAG query: "${query}"`);
|
|
85
|
+
|
|
86
|
+
// Step 1: Retrieve relevant chunks using existing RAG
|
|
87
|
+
log.info(`Retrieving top ${topK} relevant chunks...`);
|
|
88
|
+
const retrievedChunks = await queryVectorStore(query, topK);
|
|
89
|
+
|
|
90
|
+
if (!retrievedChunks || retrievedChunks.length === 0) {
|
|
91
|
+
return {
|
|
92
|
+
query,
|
|
93
|
+
retrievedChunks: [],
|
|
94
|
+
reasoningResults: [],
|
|
95
|
+
summary: 'No relevant information found in the documentation.',
|
|
96
|
+
answer: 'No relevant information found to answer your query.',
|
|
97
|
+
sources: [],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
log.info(`Retrieved ${retrievedChunks.length} chunks for reasoning`);
|
|
102
|
+
|
|
103
|
+
// Step 2: Configure reasoning models
|
|
104
|
+
const configs: ReasoningConfig[] = customConfigs || [
|
|
105
|
+
// Summarization using T5
|
|
106
|
+
{
|
|
107
|
+
task: 'summarization',
|
|
108
|
+
modelName: 'Xenova/t5-small',
|
|
109
|
+
maxLength: 150,
|
|
110
|
+
minLength: 30,
|
|
111
|
+
},
|
|
112
|
+
// Question answering using DistilBERT
|
|
113
|
+
{
|
|
114
|
+
task: 'question-answering',
|
|
115
|
+
modelName: 'Xenova/distilbert-base-cased-distilled-squad',
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
// Filter configs based on requested tasks
|
|
120
|
+
const filteredConfigs = configs.filter((config) =>
|
|
121
|
+
reasoningTasks.includes(config.task)
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Step 3: Perform reasoning on retrieved chunks
|
|
125
|
+
log.info(
|
|
126
|
+
`Performing reasoning with ${filteredConfigs.length} different models...`
|
|
127
|
+
);
|
|
128
|
+
const reasoningResults = await this.processChunksWithReasoning(
|
|
129
|
+
retrievedChunks,
|
|
130
|
+
filteredConfigs,
|
|
131
|
+
query
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Step 4: Generate comprehensive summary
|
|
135
|
+
log.info('Generating comprehensive summary...');
|
|
136
|
+
const summary = await this.generateComprehensiveSummary(
|
|
137
|
+
reasoningResults,
|
|
138
|
+
query
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Step 5: Extract best answer from reasoning results
|
|
142
|
+
const qaResults = reasoningResults.filter(
|
|
143
|
+
(result) =>
|
|
144
|
+
result.metadata.task === 'question-answering' &&
|
|
145
|
+
!result.metadata.error
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const bestAnswer =
|
|
149
|
+
qaResults.length > 0
|
|
150
|
+
? qaResults.sort(
|
|
151
|
+
(a, b) => (b.confidence || 0) - (a.confidence || 0)
|
|
152
|
+
)[0].reasoningOutput
|
|
153
|
+
: summary;
|
|
154
|
+
|
|
155
|
+
// Step 6: Extract sources
|
|
156
|
+
const sources = retrievedChunks
|
|
157
|
+
.map(
|
|
158
|
+
(doc: any) =>
|
|
159
|
+
doc.metadata?.relativePath ||
|
|
160
|
+
doc.metadata?.filename ||
|
|
161
|
+
doc.metadata?.source
|
|
162
|
+
)
|
|
163
|
+
.filter(
|
|
164
|
+
(source: any, index: number, arr: any[]) =>
|
|
165
|
+
source && arr.indexOf(source) === index
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
log.info(
|
|
169
|
+
`Reasoning-enhanced RAG completed. Generated ${reasoningResults.length} reasoning results from ${sources.length} sources`
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
query,
|
|
174
|
+
retrievedChunks,
|
|
175
|
+
reasoningResults,
|
|
176
|
+
summary,
|
|
177
|
+
answer: bestAnswer,
|
|
178
|
+
sources,
|
|
179
|
+
};
|
|
180
|
+
} catch (error) {
|
|
181
|
+
log.error('Error in reasoning-enhanced RAG:', error);
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Reasoning-enhanced RAG failed: ${error instanceof Error ? error.message : String(error)}`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get available reasoning models and their capabilities
|
|
190
|
+
*/
|
|
191
|
+
getAvailableModels(): Record<ReasoningTask, string[]> {
|
|
192
|
+
return {
|
|
193
|
+
summarization: [
|
|
194
|
+
'Xenova/t5-small',
|
|
195
|
+
'Xenova/t5-base',
|
|
196
|
+
'Xenova/bart-large-cnn',
|
|
197
|
+
],
|
|
198
|
+
'question-answering': [
|
|
199
|
+
'Xenova/distilbert-base-cased-distilled-squad',
|
|
200
|
+
'Xenova/roberta-base-squad2',
|
|
201
|
+
],
|
|
202
|
+
analysis: ['Xenova/gpt2', 'Xenova/distilgpt2'],
|
|
203
|
+
classification: [
|
|
204
|
+
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
|
|
205
|
+
'Xenova/bert-base-uncased',
|
|
206
|
+
],
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Clean up resources
|
|
212
|
+
*/
|
|
213
|
+
async cleanup(): Promise<void> {
|
|
214
|
+
this.models.clear();
|
|
215
|
+
log.info('Reasoning RAG resources cleaned up');
|
|
64
216
|
}
|
|
65
217
|
|
|
66
218
|
/**
|
|
@@ -277,158 +429,6 @@ export class ReasoningRAG {
|
|
|
277
429
|
|
|
278
430
|
return comprehensiveSummary;
|
|
279
431
|
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Enhanced RAG query with reasoning capabilities
|
|
283
|
-
*/
|
|
284
|
-
async queryWithReasoning(
|
|
285
|
-
query: string,
|
|
286
|
-
options: {
|
|
287
|
-
topK?: number;
|
|
288
|
-
reasoningTasks?: ReasoningTask[];
|
|
289
|
-
customConfigs?: ReasoningConfig[];
|
|
290
|
-
} = {}
|
|
291
|
-
): Promise<EnhancedRAGResponse> {
|
|
292
|
-
const {
|
|
293
|
-
topK = 50,
|
|
294
|
-
reasoningTasks = ['summarization', 'question-answering'],
|
|
295
|
-
customConfigs,
|
|
296
|
-
} = options;
|
|
297
|
-
|
|
298
|
-
try {
|
|
299
|
-
log.info(`Starting reasoning-enhanced RAG query: "${query}"`);
|
|
300
|
-
|
|
301
|
-
// Step 1: Retrieve relevant chunks using existing RAG
|
|
302
|
-
log.info(`Retrieving top ${topK} relevant chunks...`);
|
|
303
|
-
const retrievedChunks = await queryVectorStore(query, topK);
|
|
304
|
-
|
|
305
|
-
if (!retrievedChunks || retrievedChunks.length === 0) {
|
|
306
|
-
return {
|
|
307
|
-
query,
|
|
308
|
-
retrievedChunks: [],
|
|
309
|
-
reasoningResults: [],
|
|
310
|
-
summary: 'No relevant information found in the documentation.',
|
|
311
|
-
answer: 'No relevant information found to answer your query.',
|
|
312
|
-
sources: [],
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
log.info(`Retrieved ${retrievedChunks.length} chunks for reasoning`);
|
|
317
|
-
|
|
318
|
-
// Step 2: Configure reasoning models
|
|
319
|
-
const configs: ReasoningConfig[] = customConfigs || [
|
|
320
|
-
// Summarization using T5
|
|
321
|
-
{
|
|
322
|
-
task: 'summarization',
|
|
323
|
-
modelName: 'Xenova/t5-small',
|
|
324
|
-
maxLength: 150,
|
|
325
|
-
minLength: 30,
|
|
326
|
-
},
|
|
327
|
-
// Question answering using DistilBERT
|
|
328
|
-
{
|
|
329
|
-
task: 'question-answering',
|
|
330
|
-
modelName: 'Xenova/distilbert-base-cased-distilled-squad',
|
|
331
|
-
},
|
|
332
|
-
];
|
|
333
|
-
|
|
334
|
-
// Filter configs based on requested tasks
|
|
335
|
-
const filteredConfigs = configs.filter((config) =>
|
|
336
|
-
reasoningTasks.includes(config.task)
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
// Step 3: Perform reasoning on retrieved chunks
|
|
340
|
-
log.info(
|
|
341
|
-
`Performing reasoning with ${filteredConfigs.length} different models...`
|
|
342
|
-
);
|
|
343
|
-
const reasoningResults = await this.processChunksWithReasoning(
|
|
344
|
-
retrievedChunks,
|
|
345
|
-
filteredConfigs,
|
|
346
|
-
query
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
// Step 4: Generate comprehensive summary
|
|
350
|
-
log.info('Generating comprehensive summary...');
|
|
351
|
-
const summary = await this.generateComprehensiveSummary(
|
|
352
|
-
reasoningResults,
|
|
353
|
-
query
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
// Step 5: Extract best answer from reasoning results
|
|
357
|
-
const qaResults = reasoningResults.filter(
|
|
358
|
-
(result) =>
|
|
359
|
-
result.metadata.task === 'question-answering' &&
|
|
360
|
-
!result.metadata.error
|
|
361
|
-
);
|
|
362
|
-
|
|
363
|
-
const bestAnswer =
|
|
364
|
-
qaResults.length > 0
|
|
365
|
-
? qaResults.sort(
|
|
366
|
-
(a, b) => (b.confidence || 0) - (a.confidence || 0)
|
|
367
|
-
)[0].reasoningOutput
|
|
368
|
-
: summary;
|
|
369
|
-
|
|
370
|
-
// Step 6: Extract sources
|
|
371
|
-
const sources = retrievedChunks
|
|
372
|
-
.map(
|
|
373
|
-
(doc: any) =>
|
|
374
|
-
doc.metadata?.relativePath ||
|
|
375
|
-
doc.metadata?.filename ||
|
|
376
|
-
doc.metadata?.source
|
|
377
|
-
)
|
|
378
|
-
.filter(
|
|
379
|
-
(source: any, index: number, arr: any[]) =>
|
|
380
|
-
source && arr.indexOf(source) === index
|
|
381
|
-
);
|
|
382
|
-
|
|
383
|
-
log.info(
|
|
384
|
-
`Reasoning-enhanced RAG completed. Generated ${reasoningResults.length} reasoning results from ${sources.length} sources`
|
|
385
|
-
);
|
|
386
|
-
|
|
387
|
-
return {
|
|
388
|
-
query,
|
|
389
|
-
retrievedChunks,
|
|
390
|
-
reasoningResults,
|
|
391
|
-
summary,
|
|
392
|
-
answer: bestAnswer,
|
|
393
|
-
sources,
|
|
394
|
-
};
|
|
395
|
-
} catch (error) {
|
|
396
|
-
log.error('Error in reasoning-enhanced RAG:', error);
|
|
397
|
-
throw new Error(
|
|
398
|
-
`Reasoning-enhanced RAG failed: ${error instanceof Error ? error.message : String(error)}`
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
* Get available reasoning models and their capabilities
|
|
405
|
-
*/
|
|
406
|
-
getAvailableModels(): Record<ReasoningTask, string[]> {
|
|
407
|
-
return {
|
|
408
|
-
summarization: [
|
|
409
|
-
'Xenova/t5-small',
|
|
410
|
-
'Xenova/t5-base',
|
|
411
|
-
'Xenova/bart-large-cnn',
|
|
412
|
-
],
|
|
413
|
-
'question-answering': [
|
|
414
|
-
'Xenova/distilbert-base-cased-distilled-squad',
|
|
415
|
-
'Xenova/roberta-base-squad2',
|
|
416
|
-
],
|
|
417
|
-
analysis: ['Xenova/gpt2', 'Xenova/distilgpt2'],
|
|
418
|
-
classification: [
|
|
419
|
-
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
|
|
420
|
-
'Xenova/bert-base-uncased',
|
|
421
|
-
],
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Clean up resources
|
|
427
|
-
*/
|
|
428
|
-
async cleanup(): Promise<void> {
|
|
429
|
-
this.models.clear();
|
|
430
|
-
log.info('Reasoning RAG resources cleaned up');
|
|
431
|
-
}
|
|
432
432
|
}
|
|
433
433
|
|
|
434
434
|
// Export a singleton instance
|
|
@@ -21,56 +21,6 @@ export class SentenceTransformersEmbeddings {
|
|
|
21
21
|
this.modelName = options.modelName || 'Xenova/all-MiniLM-L6-v2';
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
* Initialize the transformers library dynamically
|
|
26
|
-
*/
|
|
27
|
-
private async initializeTransformers(): Promise<void> {
|
|
28
|
-
if (this.transformers) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
// Use eval to avoid CommonJS/ESM conflict during compilation
|
|
34
|
-
const importTransformers = new Function(
|
|
35
|
-
'return import("@xenova/transformers")'
|
|
36
|
-
);
|
|
37
|
-
this.transformers = await importTransformers();
|
|
38
|
-
} catch (error) {
|
|
39
|
-
log.error('Error importing @xenova/transformers:', error);
|
|
40
|
-
throw new Error(
|
|
41
|
-
`Failed to import @xenova/transformers: ${error instanceof Error ? error.message : String(error)}`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Initialize the model lazily
|
|
48
|
-
*/
|
|
49
|
-
private async initializeModel(): Promise<void> {
|
|
50
|
-
if (this.isInitialized && this.model) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
await this.initializeTransformers();
|
|
55
|
-
|
|
56
|
-
log.info(`Initializing sentence-transformers model: ${this.modelName}`);
|
|
57
|
-
try {
|
|
58
|
-
this.model = await this.transformers.pipeline(
|
|
59
|
-
'feature-extraction',
|
|
60
|
-
this.modelName
|
|
61
|
-
);
|
|
62
|
-
this.isInitialized = true;
|
|
63
|
-
log.info(
|
|
64
|
-
`Successfully initialized sentence-transformers model: ${this.modelName}`
|
|
65
|
-
);
|
|
66
|
-
} catch (error) {
|
|
67
|
-
log.error('Error initializing sentence-transformers model:', error);
|
|
68
|
-
throw new Error(
|
|
69
|
-
`Failed to initialize sentence-transformers model: ${error instanceof Error ? error.message : String(error)}`
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
24
|
/**
|
|
75
25
|
* Generate embeddings for a single text (LangChain interface)
|
|
76
26
|
*/
|
|
@@ -141,4 +91,54 @@ export class SentenceTransformersEmbeddings {
|
|
|
141
91
|
);
|
|
142
92
|
}
|
|
143
93
|
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Initialize the transformers library dynamically
|
|
97
|
+
*/
|
|
98
|
+
private async initializeTransformers(): Promise<void> {
|
|
99
|
+
if (this.transformers) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
// Use eval to avoid CommonJS/ESM conflict during compilation
|
|
105
|
+
const importTransformers = new Function(
|
|
106
|
+
'return import("@xenova/transformers")'
|
|
107
|
+
);
|
|
108
|
+
this.transformers = await importTransformers();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
log.error('Error importing @xenova/transformers:', error);
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Failed to import @xenova/transformers: ${error instanceof Error ? error.message : String(error)}`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Initialize the model lazily
|
|
119
|
+
*/
|
|
120
|
+
private async initializeModel(): Promise<void> {
|
|
121
|
+
if (this.isInitialized && this.model) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await this.initializeTransformers();
|
|
126
|
+
|
|
127
|
+
log.info(`Initializing sentence-transformers model: ${this.modelName}`);
|
|
128
|
+
try {
|
|
129
|
+
this.model = await this.transformers.pipeline(
|
|
130
|
+
'feature-extraction',
|
|
131
|
+
this.modelName
|
|
132
|
+
);
|
|
133
|
+
this.isInitialized = true;
|
|
134
|
+
log.info(
|
|
135
|
+
`Successfully initialized sentence-transformers model: ${this.modelName}`
|
|
136
|
+
);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
log.error('Error initializing sentence-transformers model:', error);
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Failed to initialize sentence-transformers model: ${error instanceof Error ? error.message : String(error)}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
144
|
}
|
|
@@ -30,32 +30,6 @@ const schema = z.object({
|
|
|
30
30
|
|
|
31
31
|
type KeyboardArgs = z.infer<typeof schema>;
|
|
32
32
|
|
|
33
|
-
async function handleHide(
|
|
34
|
-
sessionId: string | undefined,
|
|
35
|
-
keys: string[] | undefined
|
|
36
|
-
): Promise<ContentResult> {
|
|
37
|
-
const resolved = resolveDriver(sessionId);
|
|
38
|
-
if (!resolved.ok) {
|
|
39
|
-
return resolved.result;
|
|
40
|
-
}
|
|
41
|
-
const { driver } = resolved;
|
|
42
|
-
|
|
43
|
-
const params = keys && keys.length > 0 ? { keys } : {};
|
|
44
|
-
await execute(driver, 'mobile: hideKeyboard', params);
|
|
45
|
-
return textResult('Keyboard dismissed successfully.');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function handleIsShown(sessionId?: string): Promise<ContentResult> {
|
|
49
|
-
const resolved = resolveDriver(sessionId);
|
|
50
|
-
if (!resolved.ok) {
|
|
51
|
-
return resolved.result;
|
|
52
|
-
}
|
|
53
|
-
const { driver } = resolved;
|
|
54
|
-
|
|
55
|
-
const keyboardShown = await execute(driver, 'mobile: isKeyboardShown', {});
|
|
56
|
-
return textResult(JSON.stringify({ keyboardShown }, null, 2));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
33
|
export default function keyboard(server: FastMCP): void {
|
|
60
34
|
server.addTool({
|
|
61
35
|
name: 'appium_mobile_keyboard',
|
|
@@ -90,3 +64,29 @@ export default function keyboard(server: FastMCP): void {
|
|
|
90
64
|
},
|
|
91
65
|
});
|
|
92
66
|
}
|
|
67
|
+
|
|
68
|
+
async function handleHide(
|
|
69
|
+
sessionId: string | undefined,
|
|
70
|
+
keys: string[] | undefined
|
|
71
|
+
): Promise<ContentResult> {
|
|
72
|
+
const resolved = resolveDriver(sessionId);
|
|
73
|
+
if (!resolved.ok) {
|
|
74
|
+
return resolved.result;
|
|
75
|
+
}
|
|
76
|
+
const { driver } = resolved;
|
|
77
|
+
|
|
78
|
+
const params = keys && keys.length > 0 ? { keys } : {};
|
|
79
|
+
await execute(driver, 'mobile: hideKeyboard', params);
|
|
80
|
+
return textResult('Keyboard dismissed successfully.');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function handleIsShown(sessionId?: string): Promise<ContentResult> {
|
|
84
|
+
const resolved = resolveDriver(sessionId);
|
|
85
|
+
if (!resolved.ok) {
|
|
86
|
+
return resolved.result;
|
|
87
|
+
}
|
|
88
|
+
const { driver } = resolved;
|
|
89
|
+
|
|
90
|
+
const keyboardShown = await execute(driver, 'mobile: isKeyboardShown', {});
|
|
91
|
+
return textResult(JSON.stringify({ keyboardShown }, null, 2));
|
|
92
|
+
}
|
|
@@ -38,29 +38,6 @@ const METADATA_FIELDS = [
|
|
|
38
38
|
['deviceName', 'appium:deviceName', 'appium:deviceName'],
|
|
39
39
|
] as const;
|
|
40
40
|
|
|
41
|
-
/**
|
|
42
|
-
* Read capabilities from a WebdriverIO client method when available.
|
|
43
|
-
*
|
|
44
|
-
* @param client - Attached WebdriverIO client for the target Appium session.
|
|
45
|
-
* @param methodName - Capability reader to invoke on the client.
|
|
46
|
-
* @returns Parsed capabilities, or `undefined` when the method is missing or fails.
|
|
47
|
-
*/
|
|
48
|
-
async function readClientCapabilities(
|
|
49
|
-
client: Client,
|
|
50
|
-
methodName: 'getAppiumSessionCapabilities' | 'getSession'
|
|
51
|
-
): Promise<SessionCapabilities | undefined> {
|
|
52
|
-
const method = client[methodName];
|
|
53
|
-
if (typeof method !== 'function') {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
return readCapabilities(await method.call(client));
|
|
59
|
-
} catch {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
41
|
/**
|
|
65
42
|
* Attach MCP Appium to an existing remote Appium session without taking
|
|
66
43
|
* ownership of the underlying session lifecycle.
|
|
@@ -154,3 +131,26 @@ export async function attachSessionAction(args: {
|
|
|
154
131
|
);
|
|
155
132
|
}
|
|
156
133
|
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Read capabilities from a WebdriverIO client method when available.
|
|
137
|
+
*
|
|
138
|
+
* @param client - Attached WebdriverIO client for the target Appium session.
|
|
139
|
+
* @param methodName - Capability reader to invoke on the client.
|
|
140
|
+
* @returns Parsed capabilities, or `undefined` when the method is missing or fails.
|
|
141
|
+
*/
|
|
142
|
+
async function readClientCapabilities(
|
|
143
|
+
client: Client,
|
|
144
|
+
methodName: 'getAppiumSessionCapabilities' | 'getSession'
|
|
145
|
+
): Promise<SessionCapabilities | undefined> {
|
|
146
|
+
const method = client[methodName];
|
|
147
|
+
if (typeof method !== 'function') {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
return readCapabilities(await method.call(client));
|
|
153
|
+
} catch {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
}
|