@aiscene/android 1.3.6 → 1.6.0-cache
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/README.md +3 -7
- package/bin/.yadb-version +1 -0
- package/bin/midscene-android +2 -0
- package/bin/scrcpy-server +0 -0
- package/bin/scrcpy-server.version +1 -0
- package/bin/yadb +0 -0
- package/dist/es/cli.mjs +1872 -0
- package/dist/es/index.mjs +126 -69
- package/dist/es/mcp-server.mjs +71 -69
- package/dist/lib/cli.js +1892 -0
- package/dist/lib/index.js +131 -67
- package/dist/lib/mcp-server.js +71 -68
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/index.d.ts +204 -0
- package/package.json +12 -13
package/dist/lib/index.js
CHANGED
|
@@ -24,24 +24,26 @@ var __webpack_modules__ = {
|
|
|
24
24
|
return obj;
|
|
25
25
|
}
|
|
26
26
|
const debugScrcpy = (0, _midscene_shared_logger__rspack_import_3.getDebug)('android:scrcpy');
|
|
27
|
+
const warnScrcpy = (0, _midscene_shared_logger__rspack_import_3.getDebug)('android:scrcpy', {
|
|
28
|
+
console: true
|
|
29
|
+
});
|
|
27
30
|
const NAL_TYPE_IDR = 5;
|
|
28
31
|
const NAL_TYPE_SPS = 7;
|
|
29
32
|
const NAL_TYPE_PPS = 8;
|
|
30
33
|
const NAL_TYPE_MASK = 0x1f;
|
|
31
|
-
const START_CODE_4_BYTE = Buffer.from([
|
|
32
|
-
0x00,
|
|
33
|
-
0x00,
|
|
34
|
-
0x00,
|
|
35
|
-
0x01
|
|
36
|
-
]);
|
|
37
34
|
const DEFAULT_MAX_SIZE = 0;
|
|
38
|
-
const DEFAULT_VIDEO_BIT_RATE =
|
|
35
|
+
const DEFAULT_VIDEO_BIT_RATE = 100000000;
|
|
36
|
+
const MAX_VIDEO_BIT_RATE = 100000000;
|
|
39
37
|
const DEFAULT_IDLE_TIMEOUT_MS = 30000;
|
|
40
38
|
const MAX_KEYFRAME_WAIT_MS = 5000;
|
|
41
39
|
const FRESH_FRAME_TIMEOUT_MS = 300;
|
|
42
40
|
const KEYFRAME_POLL_INTERVAL_MS = 200;
|
|
43
41
|
const MAX_SCAN_BYTES = 1000;
|
|
44
42
|
const CONNECTION_WAIT_MS = 1000;
|
|
43
|
+
const BUSY_LOOP_WINDOW_MS = 1000;
|
|
44
|
+
const BUSY_LOOP_MAX_READS = 500;
|
|
45
|
+
const BUSY_LOOP_COOLDOWN_MS = 50;
|
|
46
|
+
const BUSY_LOOP_WARN_INTERVAL_MS = 5000;
|
|
45
47
|
const DEFAULT_SCRCPY_CONFIG = {
|
|
46
48
|
enabled: false,
|
|
47
49
|
maxSize: DEFAULT_MAX_SIZE,
|
|
@@ -81,21 +83,21 @@ var __webpack_modules__ = {
|
|
|
81
83
|
try {
|
|
82
84
|
this.isConnecting = true;
|
|
83
85
|
debugScrcpy('Starting scrcpy connection...');
|
|
84
|
-
const { AdbScrcpyClient,
|
|
86
|
+
const { AdbScrcpyClient, AdbScrcpyOptions3_3_3 } = await import("@yume-chan/adb-scrcpy");
|
|
85
87
|
const { ReadableStream } = await import("@yume-chan/stream-extra");
|
|
86
|
-
const {
|
|
87
|
-
this.h264SearchConfigFn = h264SearchConfiguration;
|
|
88
|
+
const { DefaultServerPath } = await import("@yume-chan/scrcpy");
|
|
88
89
|
const serverBinPath = this.resolveServerBinPath();
|
|
89
90
|
await AdbScrcpyClient.pushServer(this.adb, ReadableStream.from((0, node_fs__rspack_import_0.createReadStream)(serverBinPath)));
|
|
90
|
-
const scrcpyOptions = new
|
|
91
|
+
const scrcpyOptions = new AdbScrcpyOptions3_3_3({
|
|
91
92
|
audio: false,
|
|
92
93
|
control: false,
|
|
93
94
|
maxSize: this.options.maxSize,
|
|
94
95
|
videoBitRate: this.options.videoBitRate,
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
maxFps: 10,
|
|
97
|
+
sendFrameMeta: true,
|
|
98
|
+
videoCodecOptions: 'i-frame-interval=0,bitrate-mode=2'
|
|
97
99
|
});
|
|
98
|
-
this.scrcpyClient = await AdbScrcpyClient.start(this.adb, DefaultServerPath,
|
|
100
|
+
this.scrcpyClient = await AdbScrcpyClient.start(this.adb, DefaultServerPath, scrcpyOptions);
|
|
99
101
|
const videoStreamPromise = this.scrcpyClient.videoStream;
|
|
100
102
|
if (!videoStreamPromise) throw new Error('Scrcpy client did not provide video stream');
|
|
101
103
|
this.videoStream = await videoStreamPromise;
|
|
@@ -123,7 +125,8 @@ var __webpack_modules__ = {
|
|
|
123
125
|
}
|
|
124
126
|
getFfmpegPath() {
|
|
125
127
|
try {
|
|
126
|
-
const
|
|
128
|
+
const dynamicRequire = (0, node_module__rspack_import_1.createRequire)(__rslib_import_meta_url__);
|
|
129
|
+
const ffmpegInstaller = dynamicRequire('@ffmpeg-installer/ffmpeg');
|
|
127
130
|
debugScrcpy(`Using ffmpeg from npm package: ${ffmpegInstaller.path}`);
|
|
128
131
|
return ffmpegInstaller.path;
|
|
129
132
|
} catch (error) {
|
|
@@ -138,22 +141,47 @@ var __webpack_modules__ = {
|
|
|
138
141
|
this.consumeFramesLoop(reader);
|
|
139
142
|
}
|
|
140
143
|
async consumeFramesLoop(reader) {
|
|
144
|
+
let readCount = 0;
|
|
145
|
+
let windowStart = Date.now();
|
|
146
|
+
let lastBusyWarn = 0;
|
|
147
|
+
let totalReads = 0;
|
|
141
148
|
try {
|
|
142
149
|
while(true){
|
|
143
150
|
const { done, value } = await reader.read();
|
|
144
151
|
if (done) break;
|
|
152
|
+
totalReads++;
|
|
153
|
+
readCount++;
|
|
154
|
+
const now = Date.now();
|
|
155
|
+
const elapsed = now - windowStart;
|
|
156
|
+
if (elapsed >= BUSY_LOOP_WINDOW_MS) {
|
|
157
|
+
const readsPerSec = readCount / elapsed * 1000;
|
|
158
|
+
if (readCount > BUSY_LOOP_MAX_READS) {
|
|
159
|
+
if (now - lastBusyWarn >= BUSY_LOOP_WARN_INTERVAL_MS) {
|
|
160
|
+
warnScrcpy(`[CPU-DIAG] Possible busy loop detected! ${readCount} reads in ${elapsed}ms (${readsPerSec.toFixed(0)} reads/sec). Total reads: ${totalReads}. Throttling with ${BUSY_LOOP_COOLDOWN_MS}ms delay.`);
|
|
161
|
+
lastBusyWarn = now;
|
|
162
|
+
}
|
|
163
|
+
await new Promise((resolve)=>setTimeout(resolve, BUSY_LOOP_COOLDOWN_MS));
|
|
164
|
+
} else debugScrcpy(`[CPU-DIAG] Frame loop stats: ${readCount} reads in ${elapsed}ms (${readsPerSec.toFixed(1)} reads/sec), total: ${totalReads}`);
|
|
165
|
+
readCount = 0;
|
|
166
|
+
windowStart = Date.now();
|
|
167
|
+
}
|
|
145
168
|
this.processFrame(value);
|
|
146
169
|
}
|
|
147
170
|
} catch (error) {
|
|
148
|
-
debugScrcpy(`Frame consumer error: ${error}`);
|
|
171
|
+
debugScrcpy(`Frame consumer error (total reads: ${totalReads}): ${error}`);
|
|
149
172
|
await this.disconnect();
|
|
150
173
|
}
|
|
174
|
+
debugScrcpy(`Frame consumer loop ended normally (total reads: ${totalReads})`);
|
|
151
175
|
}
|
|
152
176
|
processFrame(packet) {
|
|
177
|
+
if ('configuration' === packet.type) {
|
|
178
|
+
this.spsHeader = Buffer.from(packet.data);
|
|
179
|
+
debugScrcpy(`Received SPS/PPS configuration: ${this.spsHeader.length}B`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
153
182
|
const frameBuffer = Buffer.from(packet.data);
|
|
154
|
-
const
|
|
155
|
-
if (
|
|
156
|
-
if (actualKeyFrame && this.spsHeader) {
|
|
183
|
+
const isKeyFrame = detectH264KeyFrame(frameBuffer);
|
|
184
|
+
if (isKeyFrame && this.spsHeader) {
|
|
157
185
|
this.lastRawKeyframe = frameBuffer;
|
|
158
186
|
if (this.keyframeResolvers.length > 0) {
|
|
159
187
|
const combined = Buffer.concat([
|
|
@@ -164,23 +192,7 @@ var __webpack_modules__ = {
|
|
|
164
192
|
}
|
|
165
193
|
}
|
|
166
194
|
}
|
|
167
|
-
|
|
168
|
-
if (!this.h264SearchConfigFn) return;
|
|
169
|
-
try {
|
|
170
|
-
const config = this.h264SearchConfigFn(new Uint8Array(frameBuffer));
|
|
171
|
-
if (!config.sequenceParameterSet || !config.pictureParameterSet) return;
|
|
172
|
-
this.spsHeader = Buffer.concat([
|
|
173
|
-
START_CODE_4_BYTE,
|
|
174
|
-
Buffer.from(config.sequenceParameterSet),
|
|
175
|
-
START_CODE_4_BYTE,
|
|
176
|
-
Buffer.from(config.pictureParameterSet)
|
|
177
|
-
]);
|
|
178
|
-
debugScrcpy(`Extracted SPS/PPS: SPS=${config.sequenceParameterSet.length}B, PPS=${config.pictureParameterSet.length}B, total=${this.spsHeader.length}B`);
|
|
179
|
-
} catch (error) {
|
|
180
|
-
debugScrcpy(`Failed to extract SPS/PPS from keyframe: ${error}`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
async getScreenshotPng() {
|
|
195
|
+
async getScreenshotJpeg() {
|
|
184
196
|
const perfStart = Date.now();
|
|
185
197
|
const t1 = Date.now();
|
|
186
198
|
await this.ensureConnected();
|
|
@@ -210,7 +222,7 @@ var __webpack_modules__ = {
|
|
|
210
222
|
this.resetIdleTimer();
|
|
211
223
|
debugScrcpy(`Decoding H.264 stream: ${keyframeBuffer.length} bytes (${frameSource})`);
|
|
212
224
|
const t4 = Date.now();
|
|
213
|
-
const result = await this.
|
|
225
|
+
const result = await this.decodeH264ToJpeg(keyframeBuffer);
|
|
214
226
|
const decodeTime = Date.now() - t4;
|
|
215
227
|
const totalTime = Date.now() - perfStart;
|
|
216
228
|
debugScrcpy(`Performance: total=${totalTime}ms (connect=${connectTime}ms, spsWait=${spsWaitTime}ms, frameWait=${frameWaitTime}ms[${frameSource}], decode=${decodeTime}ms)`);
|
|
@@ -273,7 +285,7 @@ var __webpack_modules__ = {
|
|
|
273
285
|
return false;
|
|
274
286
|
}
|
|
275
287
|
}
|
|
276
|
-
async
|
|
288
|
+
async decodeH264ToJpeg(h264Buffer) {
|
|
277
289
|
const { spawn } = await import("node:child_process");
|
|
278
290
|
return new Promise((resolve, reject)=>{
|
|
279
291
|
const ffmpegArgs = [
|
|
@@ -286,7 +298,9 @@ var __webpack_modules__ = {
|
|
|
286
298
|
'-f',
|
|
287
299
|
'image2pipe',
|
|
288
300
|
'-vcodec',
|
|
289
|
-
'
|
|
301
|
+
'mjpeg',
|
|
302
|
+
'-q:v',
|
|
303
|
+
'5',
|
|
290
304
|
'-loglevel',
|
|
291
305
|
'error',
|
|
292
306
|
'pipe:1'
|
|
@@ -309,13 +323,13 @@ var __webpack_modules__ = {
|
|
|
309
323
|
});
|
|
310
324
|
ffmpeg.on('close', (code)=>{
|
|
311
325
|
if (0 === code && chunks.length > 0) {
|
|
312
|
-
const
|
|
313
|
-
debugScrcpy(`FFmpeg decode successful,
|
|
314
|
-
resolve(
|
|
326
|
+
const jpegBuffer = Buffer.concat(chunks);
|
|
327
|
+
debugScrcpy(`FFmpeg decode successful, JPEG size: ${jpegBuffer.length} bytes`);
|
|
328
|
+
resolve(jpegBuffer);
|
|
315
329
|
} else {
|
|
316
330
|
const errorMsg = stderrOutput || `FFmpeg exited with code ${code}`;
|
|
317
331
|
debugScrcpy(`FFmpeg decode failed: ${errorMsg}`);
|
|
318
|
-
reject(new Error(`H.264 to
|
|
332
|
+
reject(new Error(`H.264 to JPEG decode failed: ${errorMsg}`));
|
|
319
333
|
}
|
|
320
334
|
});
|
|
321
335
|
ffmpeg.on('error', (error)=>{
|
|
@@ -347,7 +361,6 @@ var __webpack_modules__ = {
|
|
|
347
361
|
this.spsHeader = null;
|
|
348
362
|
this.lastRawKeyframe = null;
|
|
349
363
|
this.isInitialized = false;
|
|
350
|
-
this.h264SearchConfigFn = null;
|
|
351
364
|
this.keyframeResolvers = [];
|
|
352
365
|
if (reader) try {
|
|
353
366
|
reader.cancel();
|
|
@@ -375,20 +388,19 @@ var __webpack_modules__ = {
|
|
|
375
388
|
_define_property(this, "keyframeResolvers", []);
|
|
376
389
|
_define_property(this, "lastRawKeyframe", null);
|
|
377
390
|
_define_property(this, "videoResolution", null);
|
|
378
|
-
_define_property(this, "h264SearchConfigFn", null);
|
|
379
391
|
_define_property(this, "streamReader", null);
|
|
380
392
|
this.adb = adb;
|
|
393
|
+
const requestedBitRate = options.videoBitRate ?? DEFAULT_VIDEO_BIT_RATE;
|
|
394
|
+
const clampedBitRate = Math.min(requestedBitRate, MAX_VIDEO_BIT_RATE);
|
|
395
|
+
if (requestedBitRate > MAX_VIDEO_BIT_RATE) warnScrcpy(`videoBitRate ${requestedBitRate} exceeds maximum ${MAX_VIDEO_BIT_RATE}, clamped to ${clampedBitRate}`);
|
|
381
396
|
this.options = {
|
|
382
397
|
maxSize: options.maxSize ?? DEFAULT_MAX_SIZE,
|
|
383
|
-
videoBitRate:
|
|
398
|
+
videoBitRate: clampedBitRate,
|
|
384
399
|
idleTimeoutMs: options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS
|
|
385
400
|
};
|
|
386
401
|
}
|
|
387
402
|
}
|
|
388
403
|
},
|
|
389
|
-
"@ffmpeg-installer/ffmpeg" (module) {
|
|
390
|
-
module.exports = require("@ffmpeg-installer/ffmpeg");
|
|
391
|
-
},
|
|
392
404
|
"@midscene/shared/logger" (module) {
|
|
393
405
|
module.exports = require("@midscene/shared/logger");
|
|
394
406
|
},
|
|
@@ -446,10 +458,12 @@ var __webpack_exports__ = {};
|
|
|
446
458
|
(()=>{
|
|
447
459
|
__webpack_require__.r(__webpack_exports__);
|
|
448
460
|
__webpack_require__.d(__webpack_exports__, {
|
|
461
|
+
AndroidMidsceneTools: ()=>AndroidMidsceneTools,
|
|
449
462
|
getConnectedDevices: ()=>getConnectedDevices,
|
|
450
463
|
overrideAIConfig: ()=>env_namespaceObject.overrideAIConfig,
|
|
451
464
|
AndroidAgent: ()=>AndroidAgent,
|
|
452
465
|
agentFromAdbDevice: ()=>agentFromAdbDevice,
|
|
466
|
+
ScrcpyDeviceAdapter: ()=>ScrcpyDeviceAdapter,
|
|
453
467
|
AndroidDevice: ()=>AndroidDevice
|
|
454
468
|
});
|
|
455
469
|
const external_node_assert_namespaceObject = require("node:assert");
|
|
@@ -496,18 +510,13 @@ var __webpack_exports__ = {};
|
|
|
496
510
|
resolveConfig(deviceInfo) {
|
|
497
511
|
if (this.resolvedConfig) return this.resolvedConfig;
|
|
498
512
|
const config = this.scrcpyConfig;
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
const physicalMax = Math.max(deviceInfo.physicalWidth, deviceInfo.physicalHeight);
|
|
502
|
-
const scale = this.screenshotResizeScale ?? 1 / deviceInfo.dpr;
|
|
503
|
-
maxSize = Math.round(physicalMax * scale);
|
|
504
|
-
debugAdapter(`Auto-calculated maxSize: ${maxSize} (physical=${physicalMax}, scale=${scale.toFixed(3)}, ${void 0 !== this.screenshotResizeScale ? 'from screenshotResizeScale' : 'from 1/dpr'})`);
|
|
505
|
-
}
|
|
513
|
+
const maxSize = config?.maxSize ?? scrcpy_manager.o.maxSize;
|
|
514
|
+
const videoBitRate = config?.videoBitRate ?? scrcpy_manager.o.videoBitRate;
|
|
506
515
|
this.resolvedConfig = {
|
|
507
516
|
enabled: this.isEnabled(),
|
|
508
517
|
maxSize,
|
|
509
518
|
idleTimeoutMs: config?.idleTimeoutMs ?? scrcpy_manager.o.idleTimeoutMs,
|
|
510
|
-
videoBitRate
|
|
519
|
+
videoBitRate
|
|
511
520
|
};
|
|
512
521
|
return this.resolvedConfig;
|
|
513
522
|
}
|
|
@@ -542,8 +551,8 @@ var __webpack_exports__ = {};
|
|
|
542
551
|
}
|
|
543
552
|
async screenshotBase64(deviceInfo) {
|
|
544
553
|
const manager = await this.ensureManager(deviceInfo);
|
|
545
|
-
const screenshotBuffer = await manager.
|
|
546
|
-
return (0, img_namespaceObject.createImgBase64ByFormat)('
|
|
554
|
+
const screenshotBuffer = await manager.getScreenshotJpeg();
|
|
555
|
+
return (0, img_namespaceObject.createImgBase64ByFormat)('jpeg', screenshotBuffer.toString('base64'));
|
|
547
556
|
}
|
|
548
557
|
getResolution() {
|
|
549
558
|
return this.manager?.getResolution() ?? null;
|
|
@@ -554,8 +563,7 @@ var __webpack_exports__ = {};
|
|
|
554
563
|
debugAdapter(`Using scrcpy resolution: ${resolution.width}x${resolution.height}`);
|
|
555
564
|
return {
|
|
556
565
|
width: resolution.width,
|
|
557
|
-
height: resolution.height
|
|
558
|
-
dpr: deviceInfo.dpr
|
|
566
|
+
height: resolution.height
|
|
559
567
|
};
|
|
560
568
|
}
|
|
561
569
|
getScalingRatio(physicalWidth) {
|
|
@@ -574,16 +582,14 @@ var __webpack_exports__ = {};
|
|
|
574
582
|
}
|
|
575
583
|
this.resolvedConfig = null;
|
|
576
584
|
}
|
|
577
|
-
constructor(deviceId, scrcpyConfig
|
|
585
|
+
constructor(deviceId, scrcpyConfig){
|
|
578
586
|
_define_property(this, "deviceId", void 0);
|
|
579
587
|
_define_property(this, "scrcpyConfig", void 0);
|
|
580
|
-
_define_property(this, "screenshotResizeScale", void 0);
|
|
581
588
|
_define_property(this, "manager", void 0);
|
|
582
589
|
_define_property(this, "resolvedConfig", void 0);
|
|
583
590
|
_define_property(this, "initFailed", void 0);
|
|
584
591
|
this.deviceId = deviceId;
|
|
585
592
|
this.scrcpyConfig = scrcpyConfig;
|
|
586
|
-
this.screenshotResizeScale = screenshotResizeScale;
|
|
587
593
|
this.manager = null;
|
|
588
594
|
this.resolvedConfig = null;
|
|
589
595
|
this.initFailed = false;
|
|
@@ -810,7 +816,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
810
816
|
});
|
|
811
817
|
}
|
|
812
818
|
getScrcpyAdapter() {
|
|
813
|
-
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig
|
|
819
|
+
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig);
|
|
814
820
|
return this.scrcpyAdapter;
|
|
815
821
|
}
|
|
816
822
|
async getDevicePhysicalInfo() {
|
|
@@ -1056,8 +1062,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1056
1062
|
const logicalHeight = Math.round(height * scale);
|
|
1057
1063
|
return {
|
|
1058
1064
|
width: logicalWidth,
|
|
1059
|
-
height: logicalHeight
|
|
1060
|
-
dpr: this.devicePixelRatio
|
|
1065
|
+
height: logicalHeight
|
|
1061
1066
|
};
|
|
1062
1067
|
}
|
|
1063
1068
|
async cacheFeatureForPoint(center, options) {
|
|
@@ -1836,15 +1841,74 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1836
1841
|
await device.connect();
|
|
1837
1842
|
return new AndroidAgent(device, opts);
|
|
1838
1843
|
}
|
|
1844
|
+
const mcp_namespaceObject = require("@midscene/shared/mcp");
|
|
1845
|
+
const debug = (0, logger_.getDebug)('mcp:android-tools');
|
|
1846
|
+
class AndroidMidsceneTools extends mcp_namespaceObject.BaseMidsceneTools {
|
|
1847
|
+
createTemporaryDevice() {
|
|
1848
|
+
return new AndroidDevice('temp-for-action-space', {});
|
|
1849
|
+
}
|
|
1850
|
+
async ensureAgent(deviceId) {
|
|
1851
|
+
if (this.agent && deviceId) {
|
|
1852
|
+
try {
|
|
1853
|
+
await this.agent.destroy?.();
|
|
1854
|
+
} catch (error) {
|
|
1855
|
+
debug('Failed to destroy agent during cleanup:', error);
|
|
1856
|
+
}
|
|
1857
|
+
this.agent = void 0;
|
|
1858
|
+
}
|
|
1859
|
+
if (this.agent) return this.agent;
|
|
1860
|
+
debug('Creating Android agent with deviceId:', deviceId || 'auto-detect');
|
|
1861
|
+
const agent = await agentFromAdbDevice(deviceId, {
|
|
1862
|
+
autoDismissKeyboard: false
|
|
1863
|
+
});
|
|
1864
|
+
this.agent = agent;
|
|
1865
|
+
return agent;
|
|
1866
|
+
}
|
|
1867
|
+
preparePlatformTools() {
|
|
1868
|
+
return [
|
|
1869
|
+
{
|
|
1870
|
+
name: 'android_connect',
|
|
1871
|
+
description: 'Connect to Android device via ADB. If deviceId not provided, uses the first available device.',
|
|
1872
|
+
schema: {
|
|
1873
|
+
deviceId: core_namespaceObject.z.string().optional().describe('Android device ID (from adb devices)')
|
|
1874
|
+
},
|
|
1875
|
+
handler: async ({ deviceId })=>{
|
|
1876
|
+
const agent = await this.ensureAgent(deviceId);
|
|
1877
|
+
const screenshot = await agent.page.screenshotBase64();
|
|
1878
|
+
return {
|
|
1879
|
+
content: [
|
|
1880
|
+
{
|
|
1881
|
+
type: 'text',
|
|
1882
|
+
text: `Connected to Android device${deviceId ? `: ${deviceId}` : ' (auto-detected)'}`
|
|
1883
|
+
},
|
|
1884
|
+
...this.buildScreenshotContent(screenshot)
|
|
1885
|
+
],
|
|
1886
|
+
isError: false
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
},
|
|
1890
|
+
{
|
|
1891
|
+
name: 'android_disconnect',
|
|
1892
|
+
description: 'Disconnect from current Android device and release ADB resources',
|
|
1893
|
+
schema: {},
|
|
1894
|
+
handler: this.createDisconnectHandler('Android device')
|
|
1895
|
+
}
|
|
1896
|
+
];
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1839
1899
|
})();
|
|
1840
1900
|
exports.AndroidAgent = __webpack_exports__.AndroidAgent;
|
|
1841
1901
|
exports.AndroidDevice = __webpack_exports__.AndroidDevice;
|
|
1902
|
+
exports.AndroidMidsceneTools = __webpack_exports__.AndroidMidsceneTools;
|
|
1903
|
+
exports.ScrcpyDeviceAdapter = __webpack_exports__.ScrcpyDeviceAdapter;
|
|
1842
1904
|
exports.agentFromAdbDevice = __webpack_exports__.agentFromAdbDevice;
|
|
1843
1905
|
exports.getConnectedDevices = __webpack_exports__.getConnectedDevices;
|
|
1844
1906
|
exports.overrideAIConfig = __webpack_exports__.overrideAIConfig;
|
|
1845
1907
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
1846
1908
|
"AndroidAgent",
|
|
1847
1909
|
"AndroidDevice",
|
|
1910
|
+
"AndroidMidsceneTools",
|
|
1911
|
+
"ScrcpyDeviceAdapter",
|
|
1848
1912
|
"agentFromAdbDevice",
|
|
1849
1913
|
"getConnectedDevices",
|
|
1850
1914
|
"overrideAIConfig"
|