@midscene/android 1.10.6-beta-20260717061640.0 → 1.10.6
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/dist/es/cli.mjs +62 -26
- package/dist/es/index.mjs +68 -31
- package/dist/lib/cli.js +61 -25
- package/dist/lib/index.js +67 -30
- package/dist/types/index.d.ts +13 -5
- package/package.json +4 -4
package/dist/es/cli.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { createDefaultMobileActions, defineAction } from "@midscene/core/device"
|
|
|
14
14
|
import { getTmpFile, sleep } from "@midscene/core/utils";
|
|
15
15
|
import { MIDSCENE_ADB_PATH, MIDSCENE_ADB_REMOTE_HOST, MIDSCENE_ADB_REMOTE_PORT, MIDSCENE_ANDROID_IME_STRATEGY, globalConfigManager } from "@midscene/shared/env";
|
|
16
16
|
import { createImgBase64ByFormat, validateScreenshotBuffer } from "@midscene/shared/img";
|
|
17
|
-
import { ADB
|
|
17
|
+
import { ADB, getSdkRootFromEnv } from "appium-adb";
|
|
18
18
|
var __webpack_modules__ = {
|
|
19
19
|
"./src/scrcpy-manager.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
20
20
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -678,6 +678,35 @@ const defaultAppNameMapping = {
|
|
|
678
678
|
var external_node_fs_ = __webpack_require__("node:fs");
|
|
679
679
|
var external_node_module_ = __webpack_require__("node:module");
|
|
680
680
|
var external_node_path_ = __webpack_require__("node:path");
|
|
681
|
+
const warnAdb = (0, logger_.getDebug)('android:adb', {
|
|
682
|
+
console: true
|
|
683
|
+
});
|
|
684
|
+
async function adb_createAndroidAdb({ adbExecTimeout, deviceId, deviceOptions }) {
|
|
685
|
+
const androidAdbPath = deviceOptions?.androidAdbPath || globalConfigManager.getEnvConfigValue(MIDSCENE_ADB_PATH);
|
|
686
|
+
const remoteAdbHost = deviceOptions?.remoteAdbHost || globalConfigManager.getEnvConfigValue(MIDSCENE_ADB_REMOTE_HOST);
|
|
687
|
+
const remoteAdbPort = deviceOptions?.remoteAdbPort || globalConfigManager.getEnvConfigValue(MIDSCENE_ADB_REMOTE_PORT);
|
|
688
|
+
const adbOptions = {
|
|
689
|
+
udid: deviceId,
|
|
690
|
+
adbExecTimeout,
|
|
691
|
+
remoteAdbHost: remoteAdbHost || void 0,
|
|
692
|
+
remoteAdbPort: remoteAdbPort ? Number(remoteAdbPort) : void 0
|
|
693
|
+
};
|
|
694
|
+
if (androidAdbPath) return new ADB({
|
|
695
|
+
...adbOptions,
|
|
696
|
+
executable: {
|
|
697
|
+
path: androidAdbPath,
|
|
698
|
+
defaultArgs: []
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
const sdkRoot = getSdkRootFromEnv();
|
|
702
|
+
if (sdkRoot) try {
|
|
703
|
+
return await ADB.createADB(adbOptions);
|
|
704
|
+
} catch (error) {
|
|
705
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
706
|
+
warnAdb(`Unable to initialize adb from Android SDK at "${sdkRoot}", falling back to adb from PATH: ${message}`);
|
|
707
|
+
}
|
|
708
|
+
return new ADB(adbOptions);
|
|
709
|
+
}
|
|
681
710
|
var scrcpy_manager = __webpack_require__("./src/scrcpy-manager.ts");
|
|
682
711
|
function _define_property(obj, key, value) {
|
|
683
712
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -691,6 +720,10 @@ function _define_property(obj, key, value) {
|
|
|
691
720
|
}
|
|
692
721
|
const debugAdapter = (0, logger_.getDebug)('android:scrcpy-adapter');
|
|
693
722
|
const SCRCPY_RETRY_COOLDOWN_MS = 5000;
|
|
723
|
+
const DEFAULT_ADB_SERVER_ENDPOINT = {
|
|
724
|
+
host: '127.0.0.1',
|
|
725
|
+
port: 5037
|
|
726
|
+
};
|
|
694
727
|
class ScrcpyDeviceAdapter {
|
|
695
728
|
isEnabled() {
|
|
696
729
|
if (!this.isConfigured()) return false;
|
|
@@ -749,10 +782,8 @@ class ScrcpyDeviceAdapter {
|
|
|
749
782
|
const { Adb, AdbServerClient } = await import("@yume-chan/adb");
|
|
750
783
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
751
784
|
const { ScrcpyScreenshotManager: ScrcpyManager } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "./src/scrcpy-manager.ts"));
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
port: 5037
|
|
755
|
-
}));
|
|
785
|
+
const adbServerEndpoint = await this.resolveAdbServerEndpoint();
|
|
786
|
+
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector(adbServerEndpoint));
|
|
756
787
|
const adb = new Adb(await adbClient.createTransport({
|
|
757
788
|
serial: this.deviceId
|
|
758
789
|
}));
|
|
@@ -832,15 +863,17 @@ class ScrcpyDeviceAdapter {
|
|
|
832
863
|
this.resolvedConfig = null;
|
|
833
864
|
this.clearFailure();
|
|
834
865
|
}
|
|
835
|
-
constructor(deviceId, scrcpyConfig){
|
|
866
|
+
constructor(deviceId, scrcpyConfig, resolveAdbServerEndpoint = ()=>DEFAULT_ADB_SERVER_ENDPOINT){
|
|
836
867
|
_define_property(this, "deviceId", void 0);
|
|
837
868
|
_define_property(this, "scrcpyConfig", void 0);
|
|
869
|
+
_define_property(this, "resolveAdbServerEndpoint", void 0);
|
|
838
870
|
_define_property(this, "manager", void 0);
|
|
839
871
|
_define_property(this, "resolvedConfig", void 0);
|
|
840
872
|
_define_property(this, "lastError", void 0);
|
|
841
873
|
_define_property(this, "retryAfter", void 0);
|
|
842
874
|
this.deviceId = deviceId;
|
|
843
875
|
this.scrcpyConfig = scrcpyConfig;
|
|
876
|
+
this.resolveAdbServerEndpoint = resolveAdbServerEndpoint;
|
|
844
877
|
this.manager = null;
|
|
845
878
|
this.resolvedConfig = null;
|
|
846
879
|
this.lastError = null;
|
|
@@ -983,18 +1016,10 @@ class AndroidDevice {
|
|
|
983
1016
|
let error = null;
|
|
984
1017
|
debugDevice(`Initializing ADB with device ID: ${this.deviceId}`);
|
|
985
1018
|
try {
|
|
986
|
-
|
|
987
|
-
const remoteAdbHost = this.options?.remoteAdbHost || globalConfigManager.getEnvConfigValue(MIDSCENE_ADB_REMOTE_HOST);
|
|
988
|
-
const remoteAdbPort = this.options?.remoteAdbPort || globalConfigManager.getEnvConfigValue(MIDSCENE_ADB_REMOTE_PORT);
|
|
989
|
-
this.adb = new external_appium_adb_ADB({
|
|
990
|
-
udid: this.deviceId,
|
|
1019
|
+
this.adb = await adb_createAndroidAdb({
|
|
991
1020
|
adbExecTimeout: 60000,
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
defaultArgs: []
|
|
995
|
-
} : void 0,
|
|
996
|
-
remoteAdbHost: remoteAdbHost || void 0,
|
|
997
|
-
remoteAdbPort: remoteAdbPort ? Number(remoteAdbPort) : void 0
|
|
1021
|
+
deviceId: this.deviceId,
|
|
1022
|
+
deviceOptions: this.options
|
|
998
1023
|
});
|
|
999
1024
|
const size = await this.getScreenSize();
|
|
1000
1025
|
this.description = `
|
|
@@ -1029,8 +1054,9 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1029
1054
|
} catch (error) {
|
|
1030
1055
|
const methodName = String(prop);
|
|
1031
1056
|
const deviceId = this.deviceId;
|
|
1032
|
-
|
|
1033
|
-
|
|
1057
|
+
const adbExecutable = target.executable.path;
|
|
1058
|
+
debugDevice(`ADB error with device ${deviceId} when calling ${methodName} (ADB executable: ${adbExecutable}): ${error}`);
|
|
1059
|
+
throw new Error(`ADB error with device ${deviceId} when calling ${methodName} (ADB executable: ${adbExecutable}), please check https://midscenejs.com/integrate-with-android.html#faq : ${error.message}`, {
|
|
1034
1060
|
cause: error
|
|
1035
1061
|
});
|
|
1036
1062
|
}
|
|
@@ -1049,7 +1075,13 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1049
1075
|
return adapter.getStatus();
|
|
1050
1076
|
}
|
|
1051
1077
|
getScrcpyAdapter() {
|
|
1052
|
-
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig)
|
|
1078
|
+
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig, async ()=>{
|
|
1079
|
+
const adb = await this.getAdb();
|
|
1080
|
+
return {
|
|
1081
|
+
host: adb.adbHost ?? '127.0.0.1',
|
|
1082
|
+
port: adb.adbPort ?? 5037
|
|
1083
|
+
};
|
|
1084
|
+
});
|
|
1053
1085
|
return this.scrcpyAdapter;
|
|
1054
1086
|
}
|
|
1055
1087
|
async openScrcpyFrameSource() {
|
|
@@ -2081,17 +2113,21 @@ const createPlatformActions = (device)=>({
|
|
|
2081
2113
|
})
|
|
2082
2114
|
});
|
|
2083
2115
|
const debugUtils = (0, logger_.getDebug)('android:utils');
|
|
2084
|
-
async function getConnectedDevices() {
|
|
2116
|
+
async function getConnectedDevices(deviceOptions) {
|
|
2117
|
+
let adbExecutable;
|
|
2085
2118
|
try {
|
|
2086
|
-
const adb = await
|
|
2087
|
-
adbExecTimeout: 60000
|
|
2119
|
+
const adb = await adb_createAndroidAdb({
|
|
2120
|
+
adbExecTimeout: 60000,
|
|
2121
|
+
deviceOptions
|
|
2088
2122
|
});
|
|
2123
|
+
adbExecutable = adb.executable.path;
|
|
2089
2124
|
const devices = await adb.getConnectedDevices();
|
|
2090
2125
|
debugUtils(`Found ${devices.length} connected devices: `, devices);
|
|
2091
2126
|
return devices;
|
|
2092
2127
|
} catch (error) {
|
|
2093
2128
|
console.error('Failed to get device list:', error);
|
|
2094
|
-
|
|
2129
|
+
const adbExecutableContext = adbExecutable ? ` (ADB executable: ${adbExecutable})` : '';
|
|
2130
|
+
throw new Error(`Unable to get connected Android device list${adbExecutableContext}, please check https://midscenejs.com/integrate-with-android.html#faq : ${error.message}`, {
|
|
2095
2131
|
cause: error
|
|
2096
2132
|
});
|
|
2097
2133
|
}
|
|
@@ -2147,7 +2183,7 @@ class AndroidAgent extends Agent {
|
|
|
2147
2183
|
}
|
|
2148
2184
|
async function agentFromAdbDevice(deviceId, opts) {
|
|
2149
2185
|
if (!deviceId) {
|
|
2150
|
-
const devices = await getConnectedDevices();
|
|
2186
|
+
const devices = await getConnectedDevices(opts);
|
|
2151
2187
|
if (0 === devices.length) throw new Error('No Android devices found. Please connect an Android device and ensure ADB is properly configured. Run `adb devices` to verify device connection.');
|
|
2152
2188
|
deviceId = devices[0].udid;
|
|
2153
2189
|
debugAgent('deviceId not specified, will use the first device (id = %s)', deviceId);
|
|
@@ -2276,7 +2312,7 @@ class AndroidMidsceneTools extends BaseMidsceneTools {
|
|
|
2276
2312
|
const tools = new AndroidMidsceneTools();
|
|
2277
2313
|
runToolsCLI(tools, 'midscene-android', {
|
|
2278
2314
|
stripPrefix: 'android_',
|
|
2279
|
-
version: "1.10.6
|
|
2315
|
+
version: "1.10.6",
|
|
2280
2316
|
extraCommands: createReportCliCommands()
|
|
2281
2317
|
}).catch((e)=>{
|
|
2282
2318
|
process.exit(reportCLIError(e));
|
package/dist/es/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { getTmpFile, sleep } from "@midscene/core/utils";
|
|
|
10
10
|
import { MIDSCENE_ADB_PATH, MIDSCENE_ADB_REMOTE_HOST, MIDSCENE_ADB_REMOTE_PORT, MIDSCENE_ANDROID_IME_STRATEGY, globalConfigManager, overrideAIConfig } from "@midscene/shared/env";
|
|
11
11
|
import { createImgBase64ByFormat, validateScreenshotBuffer } from "@midscene/shared/img";
|
|
12
12
|
import { mergeAndNormalizeAppNameMapping, normalizeForComparison, repeat } from "@midscene/shared/utils";
|
|
13
|
-
import { ADB } from "appium-adb";
|
|
13
|
+
import { ADB, getSdkRootFromEnv } from "appium-adb";
|
|
14
14
|
import { Agent } from "@midscene/core/agent";
|
|
15
15
|
import { agentBehaviorInitArgShape, extractAgentBehaviorInitArgs, getAgentInitArgsSignature, shouldRebuildAgentForInitArgs } from "@midscene/shared/agent-tools/agent-behavior-init-args";
|
|
16
16
|
import { BaseMidsceneTools } from "@midscene/shared/agent-tools/base-tools";
|
|
@@ -542,6 +542,35 @@ var external_node_fs_ = __webpack_require__("node:fs");
|
|
|
542
542
|
var external_node_module_ = __webpack_require__("node:module");
|
|
543
543
|
var external_node_path_ = __webpack_require__("node:path");
|
|
544
544
|
var logger_ = __webpack_require__("@midscene/shared/logger");
|
|
545
|
+
const warnAdb = (0, logger_.getDebug)('android:adb', {
|
|
546
|
+
console: true
|
|
547
|
+
});
|
|
548
|
+
async function createAndroidAdb({ adbExecTimeout, deviceId, deviceOptions }) {
|
|
549
|
+
const androidAdbPath = deviceOptions?.androidAdbPath || globalConfigManager.getEnvConfigValue(MIDSCENE_ADB_PATH);
|
|
550
|
+
const remoteAdbHost = deviceOptions?.remoteAdbHost || globalConfigManager.getEnvConfigValue(MIDSCENE_ADB_REMOTE_HOST);
|
|
551
|
+
const remoteAdbPort = deviceOptions?.remoteAdbPort || globalConfigManager.getEnvConfigValue(MIDSCENE_ADB_REMOTE_PORT);
|
|
552
|
+
const adbOptions = {
|
|
553
|
+
udid: deviceId,
|
|
554
|
+
adbExecTimeout,
|
|
555
|
+
remoteAdbHost: remoteAdbHost || void 0,
|
|
556
|
+
remoteAdbPort: remoteAdbPort ? Number(remoteAdbPort) : void 0
|
|
557
|
+
};
|
|
558
|
+
if (androidAdbPath) return new ADB({
|
|
559
|
+
...adbOptions,
|
|
560
|
+
executable: {
|
|
561
|
+
path: androidAdbPath,
|
|
562
|
+
defaultArgs: []
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
const sdkRoot = getSdkRootFromEnv();
|
|
566
|
+
if (sdkRoot) try {
|
|
567
|
+
return await ADB.createADB(adbOptions);
|
|
568
|
+
} catch (error) {
|
|
569
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
570
|
+
warnAdb(`Unable to initialize adb from Android SDK at "${sdkRoot}", falling back to adb from PATH: ${message}`);
|
|
571
|
+
}
|
|
572
|
+
return new ADB(adbOptions);
|
|
573
|
+
}
|
|
545
574
|
const EMPTY_ADB_SHELL_STDOUT = '<empty>';
|
|
546
575
|
const MAX_RUN_ADB_SHELL_STDOUT = 200;
|
|
547
576
|
function normalizeShellStream(value) {
|
|
@@ -594,6 +623,10 @@ function _define_property(obj, key, value) {
|
|
|
594
623
|
}
|
|
595
624
|
const debugAdapter = (0, logger_.getDebug)('android:scrcpy-adapter');
|
|
596
625
|
const SCRCPY_RETRY_COOLDOWN_MS = 5000;
|
|
626
|
+
const DEFAULT_ADB_SERVER_ENDPOINT = {
|
|
627
|
+
host: '127.0.0.1',
|
|
628
|
+
port: 5037
|
|
629
|
+
};
|
|
597
630
|
class ScrcpyDeviceAdapter {
|
|
598
631
|
isEnabled() {
|
|
599
632
|
if (!this.isConfigured()) return false;
|
|
@@ -652,10 +685,8 @@ class ScrcpyDeviceAdapter {
|
|
|
652
685
|
const { Adb, AdbServerClient } = await import("@yume-chan/adb");
|
|
653
686
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
654
687
|
const { ScrcpyScreenshotManager: ScrcpyManager } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "./src/scrcpy-manager.ts"));
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
port: 5037
|
|
658
|
-
}));
|
|
688
|
+
const adbServerEndpoint = await this.resolveAdbServerEndpoint();
|
|
689
|
+
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector(adbServerEndpoint));
|
|
659
690
|
const adb = new Adb(await adbClient.createTransport({
|
|
660
691
|
serial: this.deviceId
|
|
661
692
|
}));
|
|
@@ -735,15 +766,17 @@ class ScrcpyDeviceAdapter {
|
|
|
735
766
|
this.resolvedConfig = null;
|
|
736
767
|
this.clearFailure();
|
|
737
768
|
}
|
|
738
|
-
constructor(deviceId, scrcpyConfig){
|
|
769
|
+
constructor(deviceId, scrcpyConfig, resolveAdbServerEndpoint = ()=>DEFAULT_ADB_SERVER_ENDPOINT){
|
|
739
770
|
_define_property(this, "deviceId", void 0);
|
|
740
771
|
_define_property(this, "scrcpyConfig", void 0);
|
|
772
|
+
_define_property(this, "resolveAdbServerEndpoint", void 0);
|
|
741
773
|
_define_property(this, "manager", void 0);
|
|
742
774
|
_define_property(this, "resolvedConfig", void 0);
|
|
743
775
|
_define_property(this, "lastError", void 0);
|
|
744
776
|
_define_property(this, "retryAfter", void 0);
|
|
745
777
|
this.deviceId = deviceId;
|
|
746
778
|
this.scrcpyConfig = scrcpyConfig;
|
|
779
|
+
this.resolveAdbServerEndpoint = resolveAdbServerEndpoint;
|
|
747
780
|
this.manager = null;
|
|
748
781
|
this.resolvedConfig = null;
|
|
749
782
|
this.lastError = null;
|
|
@@ -886,18 +919,10 @@ class AndroidDevice {
|
|
|
886
919
|
let error = null;
|
|
887
920
|
debugDevice(`Initializing ADB with device ID: ${this.deviceId}`);
|
|
888
921
|
try {
|
|
889
|
-
|
|
890
|
-
const remoteAdbHost = this.options?.remoteAdbHost || globalConfigManager.getEnvConfigValue(MIDSCENE_ADB_REMOTE_HOST);
|
|
891
|
-
const remoteAdbPort = this.options?.remoteAdbPort || globalConfigManager.getEnvConfigValue(MIDSCENE_ADB_REMOTE_PORT);
|
|
892
|
-
this.adb = new ADB({
|
|
893
|
-
udid: this.deviceId,
|
|
922
|
+
this.adb = await createAndroidAdb({
|
|
894
923
|
adbExecTimeout: 60000,
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
defaultArgs: []
|
|
898
|
-
} : void 0,
|
|
899
|
-
remoteAdbHost: remoteAdbHost || void 0,
|
|
900
|
-
remoteAdbPort: remoteAdbPort ? Number(remoteAdbPort) : void 0
|
|
924
|
+
deviceId: this.deviceId,
|
|
925
|
+
deviceOptions: this.options
|
|
901
926
|
});
|
|
902
927
|
const size = await this.getScreenSize();
|
|
903
928
|
this.description = `
|
|
@@ -932,8 +957,9 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
932
957
|
} catch (error) {
|
|
933
958
|
const methodName = String(prop);
|
|
934
959
|
const deviceId = this.deviceId;
|
|
935
|
-
|
|
936
|
-
|
|
960
|
+
const adbExecutable = target.executable.path;
|
|
961
|
+
debugDevice(`ADB error with device ${deviceId} when calling ${methodName} (ADB executable: ${adbExecutable}): ${error}`);
|
|
962
|
+
throw new Error(`ADB error with device ${deviceId} when calling ${methodName} (ADB executable: ${adbExecutable}), please check https://midscenejs.com/integrate-with-android.html#faq : ${error.message}`, {
|
|
937
963
|
cause: error
|
|
938
964
|
});
|
|
939
965
|
}
|
|
@@ -952,7 +978,13 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
952
978
|
return adapter.getStatus();
|
|
953
979
|
}
|
|
954
980
|
getScrcpyAdapter() {
|
|
955
|
-
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig)
|
|
981
|
+
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig, async ()=>{
|
|
982
|
+
const adb = await this.getAdb();
|
|
983
|
+
return {
|
|
984
|
+
host: adb.adbHost ?? '127.0.0.1',
|
|
985
|
+
port: adb.adbPort ?? 5037
|
|
986
|
+
};
|
|
987
|
+
});
|
|
956
988
|
return this.scrcpyAdapter;
|
|
957
989
|
}
|
|
958
990
|
async openScrcpyFrameSource() {
|
|
@@ -2107,35 +2139,40 @@ async function withTimeout(promise, timeoutMs, label) {
|
|
|
2107
2139
|
if (timer) clearTimeout(timer);
|
|
2108
2140
|
}
|
|
2109
2141
|
}
|
|
2110
|
-
async function createAdbForDetailLookup() {
|
|
2111
|
-
return await
|
|
2112
|
-
adbExecTimeout: DETAIL_LOOKUP_ADB_TIMEOUT_MS
|
|
2142
|
+
async function createAdbForDetailLookup(deviceOptions) {
|
|
2143
|
+
return await createAndroidAdb({
|
|
2144
|
+
adbExecTimeout: DETAIL_LOOKUP_ADB_TIMEOUT_MS,
|
|
2145
|
+
deviceOptions
|
|
2113
2146
|
});
|
|
2114
2147
|
}
|
|
2115
|
-
async function getConnectedDevices() {
|
|
2148
|
+
async function getConnectedDevices(deviceOptions) {
|
|
2149
|
+
let adbExecutable;
|
|
2116
2150
|
try {
|
|
2117
|
-
const adb = await
|
|
2118
|
-
adbExecTimeout: 60000
|
|
2151
|
+
const adb = await createAndroidAdb({
|
|
2152
|
+
adbExecTimeout: 60000,
|
|
2153
|
+
deviceOptions
|
|
2119
2154
|
});
|
|
2155
|
+
adbExecutable = adb.executable.path;
|
|
2120
2156
|
const devices = await adb.getConnectedDevices();
|
|
2121
2157
|
debugUtils(`Found ${devices.length} connected devices: `, devices);
|
|
2122
2158
|
return devices;
|
|
2123
2159
|
} catch (error) {
|
|
2124
2160
|
console.error('Failed to get device list:', error);
|
|
2125
|
-
|
|
2161
|
+
const adbExecutableContext = adbExecutable ? ` (ADB executable: ${adbExecutable})` : '';
|
|
2162
|
+
throw new Error(`Unable to get connected Android device list${adbExecutableContext}, please check https://midscenejs.com/integrate-with-android.html#faq : ${error.message}`, {
|
|
2126
2163
|
cause: error
|
|
2127
2164
|
});
|
|
2128
2165
|
}
|
|
2129
2166
|
}
|
|
2130
|
-
async function getConnectedDevicesWithDetails() {
|
|
2131
|
-
const devices = await getConnectedDevices();
|
|
2167
|
+
async function getConnectedDevicesWithDetails(deviceOptions) {
|
|
2168
|
+
const devices = await getConnectedDevices(deviceOptions);
|
|
2132
2169
|
if (0 === devices.length) return [];
|
|
2133
2170
|
return await Promise.all(devices.map(async (device)=>{
|
|
2134
2171
|
const detailedDevice = {
|
|
2135
2172
|
...device
|
|
2136
2173
|
};
|
|
2137
2174
|
try {
|
|
2138
|
-
const adb = await createAdbForDetailLookup();
|
|
2175
|
+
const adb = await createAdbForDetailLookup(deviceOptions);
|
|
2139
2176
|
adb.setDeviceId(device.udid);
|
|
2140
2177
|
const [modelResult, brandResult, sizeResult, densityResult] = await Promise.allSettled([
|
|
2141
2178
|
withTimeout(adb.shell([
|
|
@@ -2213,7 +2250,7 @@ class AndroidAgent extends Agent {
|
|
|
2213
2250
|
}
|
|
2214
2251
|
async function agentFromAdbDevice(deviceId, opts) {
|
|
2215
2252
|
if (!deviceId) {
|
|
2216
|
-
const devices = await getConnectedDevices();
|
|
2253
|
+
const devices = await getConnectedDevices(opts);
|
|
2217
2254
|
if (0 === devices.length) throw new Error('No Android devices found. Please connect an Android device and ensure ADB is properly configured. Run `adb devices` to verify device connection.');
|
|
2218
2255
|
deviceId = devices[0].udid;
|
|
2219
2256
|
debugAgent('deviceId not specified, will use the first device (id = %s)', deviceId);
|
package/dist/lib/cli.js
CHANGED
|
@@ -693,6 +693,35 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
693
693
|
const env_namespaceObject = require("@midscene/shared/env");
|
|
694
694
|
const img_namespaceObject = require("@midscene/shared/img");
|
|
695
695
|
const external_appium_adb_namespaceObject = require("appium-adb");
|
|
696
|
+
const warnAdb = (0, logger_.getDebug)('android:adb', {
|
|
697
|
+
console: true
|
|
698
|
+
});
|
|
699
|
+
async function adb_createAndroidAdb({ adbExecTimeout, deviceId, deviceOptions }) {
|
|
700
|
+
const androidAdbPath = deviceOptions?.androidAdbPath || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ADB_PATH);
|
|
701
|
+
const remoteAdbHost = deviceOptions?.remoteAdbHost || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ADB_REMOTE_HOST);
|
|
702
|
+
const remoteAdbPort = deviceOptions?.remoteAdbPort || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ADB_REMOTE_PORT);
|
|
703
|
+
const adbOptions = {
|
|
704
|
+
udid: deviceId,
|
|
705
|
+
adbExecTimeout,
|
|
706
|
+
remoteAdbHost: remoteAdbHost || void 0,
|
|
707
|
+
remoteAdbPort: remoteAdbPort ? Number(remoteAdbPort) : void 0
|
|
708
|
+
};
|
|
709
|
+
if (androidAdbPath) return new external_appium_adb_namespaceObject.ADB({
|
|
710
|
+
...adbOptions,
|
|
711
|
+
executable: {
|
|
712
|
+
path: androidAdbPath,
|
|
713
|
+
defaultArgs: []
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
const sdkRoot = (0, external_appium_adb_namespaceObject.getSdkRootFromEnv)();
|
|
717
|
+
if (sdkRoot) try {
|
|
718
|
+
return await external_appium_adb_namespaceObject.ADB.createADB(adbOptions);
|
|
719
|
+
} catch (error) {
|
|
720
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
721
|
+
warnAdb(`Unable to initialize adb from Android SDK at "${sdkRoot}", falling back to adb from PATH: ${message}`);
|
|
722
|
+
}
|
|
723
|
+
return new external_appium_adb_namespaceObject.ADB(adbOptions);
|
|
724
|
+
}
|
|
696
725
|
var scrcpy_manager = __webpack_require__("./src/scrcpy-manager.ts");
|
|
697
726
|
function _define_property(obj, key, value) {
|
|
698
727
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -706,6 +735,10 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
706
735
|
}
|
|
707
736
|
const debugAdapter = (0, logger_.getDebug)('android:scrcpy-adapter');
|
|
708
737
|
const SCRCPY_RETRY_COOLDOWN_MS = 5000;
|
|
738
|
+
const DEFAULT_ADB_SERVER_ENDPOINT = {
|
|
739
|
+
host: '127.0.0.1',
|
|
740
|
+
port: 5037
|
|
741
|
+
};
|
|
709
742
|
class ScrcpyDeviceAdapter {
|
|
710
743
|
isEnabled() {
|
|
711
744
|
if (!this.isConfigured()) return false;
|
|
@@ -764,10 +797,8 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
764
797
|
const { Adb, AdbServerClient } = await import("@yume-chan/adb");
|
|
765
798
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
766
799
|
const { ScrcpyScreenshotManager: ScrcpyManager } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "./src/scrcpy-manager.ts"));
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
port: 5037
|
|
770
|
-
}));
|
|
800
|
+
const adbServerEndpoint = await this.resolveAdbServerEndpoint();
|
|
801
|
+
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector(adbServerEndpoint));
|
|
771
802
|
const adb = new Adb(await adbClient.createTransport({
|
|
772
803
|
serial: this.deviceId
|
|
773
804
|
}));
|
|
@@ -847,15 +878,17 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
847
878
|
this.resolvedConfig = null;
|
|
848
879
|
this.clearFailure();
|
|
849
880
|
}
|
|
850
|
-
constructor(deviceId, scrcpyConfig){
|
|
881
|
+
constructor(deviceId, scrcpyConfig, resolveAdbServerEndpoint = ()=>DEFAULT_ADB_SERVER_ENDPOINT){
|
|
851
882
|
_define_property(this, "deviceId", void 0);
|
|
852
883
|
_define_property(this, "scrcpyConfig", void 0);
|
|
884
|
+
_define_property(this, "resolveAdbServerEndpoint", void 0);
|
|
853
885
|
_define_property(this, "manager", void 0);
|
|
854
886
|
_define_property(this, "resolvedConfig", void 0);
|
|
855
887
|
_define_property(this, "lastError", void 0);
|
|
856
888
|
_define_property(this, "retryAfter", void 0);
|
|
857
889
|
this.deviceId = deviceId;
|
|
858
890
|
this.scrcpyConfig = scrcpyConfig;
|
|
891
|
+
this.resolveAdbServerEndpoint = resolveAdbServerEndpoint;
|
|
859
892
|
this.manager = null;
|
|
860
893
|
this.resolvedConfig = null;
|
|
861
894
|
this.lastError = null;
|
|
@@ -998,18 +1031,10 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
998
1031
|
let error = null;
|
|
999
1032
|
debugDevice(`Initializing ADB with device ID: ${this.deviceId}`);
|
|
1000
1033
|
try {
|
|
1001
|
-
|
|
1002
|
-
const remoteAdbHost = this.options?.remoteAdbHost || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ADB_REMOTE_HOST);
|
|
1003
|
-
const remoteAdbPort = this.options?.remoteAdbPort || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ADB_REMOTE_PORT);
|
|
1004
|
-
this.adb = new external_appium_adb_namespaceObject.ADB({
|
|
1005
|
-
udid: this.deviceId,
|
|
1034
|
+
this.adb = await adb_createAndroidAdb({
|
|
1006
1035
|
adbExecTimeout: 60000,
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
defaultArgs: []
|
|
1010
|
-
} : void 0,
|
|
1011
|
-
remoteAdbHost: remoteAdbHost || void 0,
|
|
1012
|
-
remoteAdbPort: remoteAdbPort ? Number(remoteAdbPort) : void 0
|
|
1036
|
+
deviceId: this.deviceId,
|
|
1037
|
+
deviceOptions: this.options
|
|
1013
1038
|
});
|
|
1014
1039
|
const size = await this.getScreenSize();
|
|
1015
1040
|
this.description = `
|
|
@@ -1044,8 +1069,9 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1044
1069
|
} catch (error) {
|
|
1045
1070
|
const methodName = String(prop);
|
|
1046
1071
|
const deviceId = this.deviceId;
|
|
1047
|
-
|
|
1048
|
-
|
|
1072
|
+
const adbExecutable = target.executable.path;
|
|
1073
|
+
debugDevice(`ADB error with device ${deviceId} when calling ${methodName} (ADB executable: ${adbExecutable}): ${error}`);
|
|
1074
|
+
throw new Error(`ADB error with device ${deviceId} when calling ${methodName} (ADB executable: ${adbExecutable}), please check https://midscenejs.com/integrate-with-android.html#faq : ${error.message}`, {
|
|
1049
1075
|
cause: error
|
|
1050
1076
|
});
|
|
1051
1077
|
}
|
|
@@ -1064,7 +1090,13 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1064
1090
|
return adapter.getStatus();
|
|
1065
1091
|
}
|
|
1066
1092
|
getScrcpyAdapter() {
|
|
1067
|
-
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig)
|
|
1093
|
+
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig, async ()=>{
|
|
1094
|
+
const adb = await this.getAdb();
|
|
1095
|
+
return {
|
|
1096
|
+
host: adb.adbHost ?? '127.0.0.1',
|
|
1097
|
+
port: adb.adbPort ?? 5037
|
|
1098
|
+
};
|
|
1099
|
+
});
|
|
1068
1100
|
return this.scrcpyAdapter;
|
|
1069
1101
|
}
|
|
1070
1102
|
async openScrcpyFrameSource() {
|
|
@@ -2096,17 +2128,21 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
2096
2128
|
})
|
|
2097
2129
|
});
|
|
2098
2130
|
const debugUtils = (0, logger_.getDebug)('android:utils');
|
|
2099
|
-
async function getConnectedDevices() {
|
|
2131
|
+
async function getConnectedDevices(deviceOptions) {
|
|
2132
|
+
let adbExecutable;
|
|
2100
2133
|
try {
|
|
2101
|
-
const adb = await
|
|
2102
|
-
adbExecTimeout: 60000
|
|
2134
|
+
const adb = await adb_createAndroidAdb({
|
|
2135
|
+
adbExecTimeout: 60000,
|
|
2136
|
+
deviceOptions
|
|
2103
2137
|
});
|
|
2138
|
+
adbExecutable = adb.executable.path;
|
|
2104
2139
|
const devices = await adb.getConnectedDevices();
|
|
2105
2140
|
debugUtils(`Found ${devices.length} connected devices: `, devices);
|
|
2106
2141
|
return devices;
|
|
2107
2142
|
} catch (error) {
|
|
2108
2143
|
console.error('Failed to get device list:', error);
|
|
2109
|
-
|
|
2144
|
+
const adbExecutableContext = adbExecutable ? ` (ADB executable: ${adbExecutable})` : '';
|
|
2145
|
+
throw new Error(`Unable to get connected Android device list${adbExecutableContext}, please check https://midscenejs.com/integrate-with-android.html#faq : ${error.message}`, {
|
|
2110
2146
|
cause: error
|
|
2111
2147
|
});
|
|
2112
2148
|
}
|
|
@@ -2162,7 +2198,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
2162
2198
|
}
|
|
2163
2199
|
async function agentFromAdbDevice(deviceId, opts) {
|
|
2164
2200
|
if (!deviceId) {
|
|
2165
|
-
const devices = await getConnectedDevices();
|
|
2201
|
+
const devices = await getConnectedDevices(opts);
|
|
2166
2202
|
if (0 === devices.length) throw new Error('No Android devices found. Please connect an Android device and ensure ADB is properly configured. Run `adb devices` to verify device connection.');
|
|
2167
2203
|
deviceId = devices[0].udid;
|
|
2168
2204
|
debugAgent('deviceId not specified, will use the first device (id = %s)', deviceId);
|
|
@@ -2291,7 +2327,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
2291
2327
|
const tools = new AndroidMidsceneTools();
|
|
2292
2328
|
(0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-android', {
|
|
2293
2329
|
stripPrefix: 'android_',
|
|
2294
|
-
version: "1.10.6
|
|
2330
|
+
version: "1.10.6",
|
|
2295
2331
|
extraCommands: (0, core_namespaceObject.createReportCliCommands)()
|
|
2296
2332
|
}).catch((e)=>{
|
|
2297
2333
|
process.exit((0, cli_namespaceObject.reportCLIError)(e));
|
package/dist/lib/index.js
CHANGED
|
@@ -575,6 +575,35 @@ var __webpack_exports__ = {};
|
|
|
575
575
|
var logger_ = __webpack_require__("@midscene/shared/logger");
|
|
576
576
|
const shared_utils_namespaceObject = require("@midscene/shared/utils");
|
|
577
577
|
const external_appium_adb_namespaceObject = require("appium-adb");
|
|
578
|
+
const warnAdb = (0, logger_.getDebug)('android:adb', {
|
|
579
|
+
console: true
|
|
580
|
+
});
|
|
581
|
+
async function createAndroidAdb({ adbExecTimeout, deviceId, deviceOptions }) {
|
|
582
|
+
const androidAdbPath = deviceOptions?.androidAdbPath || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ADB_PATH);
|
|
583
|
+
const remoteAdbHost = deviceOptions?.remoteAdbHost || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ADB_REMOTE_HOST);
|
|
584
|
+
const remoteAdbPort = deviceOptions?.remoteAdbPort || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ADB_REMOTE_PORT);
|
|
585
|
+
const adbOptions = {
|
|
586
|
+
udid: deviceId,
|
|
587
|
+
adbExecTimeout,
|
|
588
|
+
remoteAdbHost: remoteAdbHost || void 0,
|
|
589
|
+
remoteAdbPort: remoteAdbPort ? Number(remoteAdbPort) : void 0
|
|
590
|
+
};
|
|
591
|
+
if (androidAdbPath) return new external_appium_adb_namespaceObject.ADB({
|
|
592
|
+
...adbOptions,
|
|
593
|
+
executable: {
|
|
594
|
+
path: androidAdbPath,
|
|
595
|
+
defaultArgs: []
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
const sdkRoot = (0, external_appium_adb_namespaceObject.getSdkRootFromEnv)();
|
|
599
|
+
if (sdkRoot) try {
|
|
600
|
+
return await external_appium_adb_namespaceObject.ADB.createADB(adbOptions);
|
|
601
|
+
} catch (error) {
|
|
602
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
603
|
+
warnAdb(`Unable to initialize adb from Android SDK at "${sdkRoot}", falling back to adb from PATH: ${message}`);
|
|
604
|
+
}
|
|
605
|
+
return new external_appium_adb_namespaceObject.ADB(adbOptions);
|
|
606
|
+
}
|
|
578
607
|
const EMPTY_ADB_SHELL_STDOUT = '<empty>';
|
|
579
608
|
const MAX_RUN_ADB_SHELL_STDOUT = 200;
|
|
580
609
|
function normalizeShellStream(value) {
|
|
@@ -627,6 +656,10 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
627
656
|
}
|
|
628
657
|
const debugAdapter = (0, logger_.getDebug)('android:scrcpy-adapter');
|
|
629
658
|
const SCRCPY_RETRY_COOLDOWN_MS = 5000;
|
|
659
|
+
const DEFAULT_ADB_SERVER_ENDPOINT = {
|
|
660
|
+
host: '127.0.0.1',
|
|
661
|
+
port: 5037
|
|
662
|
+
};
|
|
630
663
|
class ScrcpyDeviceAdapter {
|
|
631
664
|
isEnabled() {
|
|
632
665
|
if (!this.isConfigured()) return false;
|
|
@@ -685,10 +718,8 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
685
718
|
const { Adb, AdbServerClient } = await import("@yume-chan/adb");
|
|
686
719
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
687
720
|
const { ScrcpyScreenshotManager: ScrcpyManager } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "./src/scrcpy-manager.ts"));
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
port: 5037
|
|
691
|
-
}));
|
|
721
|
+
const adbServerEndpoint = await this.resolveAdbServerEndpoint();
|
|
722
|
+
const adbClient = new AdbServerClient(new AdbServerNodeTcpConnector(adbServerEndpoint));
|
|
692
723
|
const adb = new Adb(await adbClient.createTransport({
|
|
693
724
|
serial: this.deviceId
|
|
694
725
|
}));
|
|
@@ -768,15 +799,17 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
768
799
|
this.resolvedConfig = null;
|
|
769
800
|
this.clearFailure();
|
|
770
801
|
}
|
|
771
|
-
constructor(deviceId, scrcpyConfig){
|
|
802
|
+
constructor(deviceId, scrcpyConfig, resolveAdbServerEndpoint = ()=>DEFAULT_ADB_SERVER_ENDPOINT){
|
|
772
803
|
_define_property(this, "deviceId", void 0);
|
|
773
804
|
_define_property(this, "scrcpyConfig", void 0);
|
|
805
|
+
_define_property(this, "resolveAdbServerEndpoint", void 0);
|
|
774
806
|
_define_property(this, "manager", void 0);
|
|
775
807
|
_define_property(this, "resolvedConfig", void 0);
|
|
776
808
|
_define_property(this, "lastError", void 0);
|
|
777
809
|
_define_property(this, "retryAfter", void 0);
|
|
778
810
|
this.deviceId = deviceId;
|
|
779
811
|
this.scrcpyConfig = scrcpyConfig;
|
|
812
|
+
this.resolveAdbServerEndpoint = resolveAdbServerEndpoint;
|
|
780
813
|
this.manager = null;
|
|
781
814
|
this.resolvedConfig = null;
|
|
782
815
|
this.lastError = null;
|
|
@@ -919,18 +952,10 @@ ${stdout ? truncateAdbShellStream(stdout, 'stdout') : EMPTY_ADB_SHELL_STDOUT}`;
|
|
|
919
952
|
let error = null;
|
|
920
953
|
debugDevice(`Initializing ADB with device ID: ${this.deviceId}`);
|
|
921
954
|
try {
|
|
922
|
-
|
|
923
|
-
const remoteAdbHost = this.options?.remoteAdbHost || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ADB_REMOTE_HOST);
|
|
924
|
-
const remoteAdbPort = this.options?.remoteAdbPort || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ADB_REMOTE_PORT);
|
|
925
|
-
this.adb = new external_appium_adb_namespaceObject.ADB({
|
|
926
|
-
udid: this.deviceId,
|
|
955
|
+
this.adb = await createAndroidAdb({
|
|
927
956
|
adbExecTimeout: 60000,
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
defaultArgs: []
|
|
931
|
-
} : void 0,
|
|
932
|
-
remoteAdbHost: remoteAdbHost || void 0,
|
|
933
|
-
remoteAdbPort: remoteAdbPort ? Number(remoteAdbPort) : void 0
|
|
957
|
+
deviceId: this.deviceId,
|
|
958
|
+
deviceOptions: this.options
|
|
934
959
|
});
|
|
935
960
|
const size = await this.getScreenSize();
|
|
936
961
|
this.description = `
|
|
@@ -965,8 +990,9 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
965
990
|
} catch (error) {
|
|
966
991
|
const methodName = String(prop);
|
|
967
992
|
const deviceId = this.deviceId;
|
|
968
|
-
|
|
969
|
-
|
|
993
|
+
const adbExecutable = target.executable.path;
|
|
994
|
+
debugDevice(`ADB error with device ${deviceId} when calling ${methodName} (ADB executable: ${adbExecutable}): ${error}`);
|
|
995
|
+
throw new Error(`ADB error with device ${deviceId} when calling ${methodName} (ADB executable: ${adbExecutable}), please check https://midscenejs.com/integrate-with-android.html#faq : ${error.message}`, {
|
|
970
996
|
cause: error
|
|
971
997
|
});
|
|
972
998
|
}
|
|
@@ -985,7 +1011,13 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
985
1011
|
return adapter.getStatus();
|
|
986
1012
|
}
|
|
987
1013
|
getScrcpyAdapter() {
|
|
988
|
-
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig)
|
|
1014
|
+
if (!this.scrcpyAdapter) this.scrcpyAdapter = new ScrcpyDeviceAdapter(this.deviceId, this.options?.scrcpyConfig, async ()=>{
|
|
1015
|
+
const adb = await this.getAdb();
|
|
1016
|
+
return {
|
|
1017
|
+
host: adb.adbHost ?? '127.0.0.1',
|
|
1018
|
+
port: adb.adbPort ?? 5037
|
|
1019
|
+
};
|
|
1020
|
+
});
|
|
989
1021
|
return this.scrcpyAdapter;
|
|
990
1022
|
}
|
|
991
1023
|
async openScrcpyFrameSource() {
|
|
@@ -2141,35 +2173,40 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
2141
2173
|
if (timer) clearTimeout(timer);
|
|
2142
2174
|
}
|
|
2143
2175
|
}
|
|
2144
|
-
async function createAdbForDetailLookup() {
|
|
2145
|
-
return await
|
|
2146
|
-
adbExecTimeout: DETAIL_LOOKUP_ADB_TIMEOUT_MS
|
|
2176
|
+
async function createAdbForDetailLookup(deviceOptions) {
|
|
2177
|
+
return await createAndroidAdb({
|
|
2178
|
+
adbExecTimeout: DETAIL_LOOKUP_ADB_TIMEOUT_MS,
|
|
2179
|
+
deviceOptions
|
|
2147
2180
|
});
|
|
2148
2181
|
}
|
|
2149
|
-
async function getConnectedDevices() {
|
|
2182
|
+
async function getConnectedDevices(deviceOptions) {
|
|
2183
|
+
let adbExecutable;
|
|
2150
2184
|
try {
|
|
2151
|
-
const adb = await
|
|
2152
|
-
adbExecTimeout: 60000
|
|
2185
|
+
const adb = await createAndroidAdb({
|
|
2186
|
+
adbExecTimeout: 60000,
|
|
2187
|
+
deviceOptions
|
|
2153
2188
|
});
|
|
2189
|
+
adbExecutable = adb.executable.path;
|
|
2154
2190
|
const devices = await adb.getConnectedDevices();
|
|
2155
2191
|
debugUtils(`Found ${devices.length} connected devices: `, devices);
|
|
2156
2192
|
return devices;
|
|
2157
2193
|
} catch (error) {
|
|
2158
2194
|
console.error('Failed to get device list:', error);
|
|
2159
|
-
|
|
2195
|
+
const adbExecutableContext = adbExecutable ? ` (ADB executable: ${adbExecutable})` : '';
|
|
2196
|
+
throw new Error(`Unable to get connected Android device list${adbExecutableContext}, please check https://midscenejs.com/integrate-with-android.html#faq : ${error.message}`, {
|
|
2160
2197
|
cause: error
|
|
2161
2198
|
});
|
|
2162
2199
|
}
|
|
2163
2200
|
}
|
|
2164
|
-
async function getConnectedDevicesWithDetails() {
|
|
2165
|
-
const devices = await getConnectedDevices();
|
|
2201
|
+
async function getConnectedDevicesWithDetails(deviceOptions) {
|
|
2202
|
+
const devices = await getConnectedDevices(deviceOptions);
|
|
2166
2203
|
if (0 === devices.length) return [];
|
|
2167
2204
|
return await Promise.all(devices.map(async (device)=>{
|
|
2168
2205
|
const detailedDevice = {
|
|
2169
2206
|
...device
|
|
2170
2207
|
};
|
|
2171
2208
|
try {
|
|
2172
|
-
const adb = await createAdbForDetailLookup();
|
|
2209
|
+
const adb = await createAdbForDetailLookup(deviceOptions);
|
|
2173
2210
|
adb.setDeviceId(device.udid);
|
|
2174
2211
|
const [modelResult, brandResult, sizeResult, densityResult] = await Promise.allSettled([
|
|
2175
2212
|
withTimeout(adb.shell([
|
|
@@ -2247,7 +2284,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
2247
2284
|
}
|
|
2248
2285
|
async function agentFromAdbDevice(deviceId, opts) {
|
|
2249
2286
|
if (!deviceId) {
|
|
2250
|
-
const devices = await getConnectedDevices();
|
|
2287
|
+
const devices = await getConnectedDevices(opts);
|
|
2251
2288
|
if (0 === devices.length) throw new Error('No Android devices found. Please connect an Android device and ensure ADB is properly configured. Run `adb devices` to verify device connection.');
|
|
2252
2289
|
deviceId = devices[0].udid;
|
|
2253
2290
|
debugAgent('deviceId not specified, will use the first device (id = %s)', deviceId);
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AbstractInterface } from '@midscene/core/device';
|
|
2
2
|
import type { ActionParam } from '@midscene/core';
|
|
3
3
|
import type { ActionReturn } from '@midscene/core';
|
|
4
|
-
import { ADB } from 'appium-adb';
|
|
4
|
+
import type { ADB } from 'appium-adb';
|
|
5
5
|
import type { Adb } from '@yume-chan/adb';
|
|
6
6
|
import { Agent } from '@midscene/core/agent';
|
|
7
7
|
import { AgentBehaviorInitArgs } from '@midscene/shared/agent-tools/agent-behavior-init-args';
|
|
@@ -9,7 +9,7 @@ import { AgentOpt } from '@midscene/core/agent';
|
|
|
9
9
|
import { AndroidDeviceInputOpt } from '@midscene/core/device';
|
|
10
10
|
import { AndroidDeviceOpt } from '@midscene/core/device';
|
|
11
11
|
import { BaseMidsceneTools } from '@midscene/shared/agent-tools/base-tools';
|
|
12
|
-
import { Device } from 'appium-adb';
|
|
12
|
+
import type { Device } from 'appium-adb';
|
|
13
13
|
import { DeviceAction } from '@midscene/core';
|
|
14
14
|
import type { ElementInfo } from '@midscene/shared/extractor';
|
|
15
15
|
import { InitArgSpec } from '@midscene/shared/agent-tools/base-tools';
|
|
@@ -22,6 +22,11 @@ import type { ToolDefinition } from '@midscene/shared/agent-tools/types';
|
|
|
22
22
|
|
|
23
23
|
declare type ActionArgs<T extends DeviceAction> = [ActionParam<T>] extends [undefined] ? [] : [ActionParam<T>];
|
|
24
24
|
|
|
25
|
+
declare interface AdbServerEndpoint {
|
|
26
|
+
host: string;
|
|
27
|
+
port: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
export declare function agentFromAdbDevice(deviceId?: string, opts?: AndroidAgentOpt & AndroidDeviceOpt): Promise<AndroidAgent>;
|
|
26
31
|
|
|
27
32
|
export declare class AndroidAgent extends Agent<AndroidDevice> {
|
|
@@ -321,9 +326,9 @@ declare interface DevicePhysicalInfo {
|
|
|
321
326
|
isCurrentOrientation?: boolean;
|
|
322
327
|
}
|
|
323
328
|
|
|
324
|
-
export declare function getConnectedDevices(): Promise<Device[]>;
|
|
329
|
+
export declare function getConnectedDevices(deviceOptions?: AndroidDeviceOpt): Promise<Device[]>;
|
|
325
330
|
|
|
326
|
-
export declare function getConnectedDevicesWithDetails(): Promise<AndroidConnectedDevice[]>;
|
|
331
|
+
export declare function getConnectedDevicesWithDetails(deviceOptions?: AndroidDeviceOpt): Promise<AndroidConnectedDevice[]>;
|
|
327
332
|
|
|
328
333
|
export { overrideAIConfig }
|
|
329
334
|
|
|
@@ -341,6 +346,8 @@ declare interface RawKeyframe {
|
|
|
341
346
|
capturedAt: number;
|
|
342
347
|
}
|
|
343
348
|
|
|
349
|
+
declare type ResolveAdbServerEndpoint = () => AdbServerEndpoint | Promise<AdbServerEndpoint>;
|
|
350
|
+
|
|
344
351
|
declare interface ResolvedScrcpyConfig {
|
|
345
352
|
enabled: boolean;
|
|
346
353
|
maxSize: number;
|
|
@@ -369,11 +376,12 @@ declare interface ScrcpyConfig {
|
|
|
369
376
|
export declare class ScrcpyDeviceAdapter {
|
|
370
377
|
private deviceId;
|
|
371
378
|
private scrcpyConfig;
|
|
379
|
+
private resolveAdbServerEndpoint;
|
|
372
380
|
private manager;
|
|
373
381
|
private resolvedConfig;
|
|
374
382
|
private lastError;
|
|
375
383
|
private retryAfter;
|
|
376
|
-
constructor(deviceId: string, scrcpyConfig: ScrcpyConfig | undefined);
|
|
384
|
+
constructor(deviceId: string, scrcpyConfig: ScrcpyConfig | undefined, resolveAdbServerEndpoint?: ResolveAdbServerEndpoint);
|
|
377
385
|
isEnabled(): boolean;
|
|
378
386
|
getStatus(): ScrcpyStatus;
|
|
379
387
|
private isConfigured;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midscene/android",
|
|
3
|
-
"version": "1.10.6
|
|
3
|
+
"version": "1.10.6",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/web-infra-dev/midscene.git",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"@yume-chan/stream-extra": "2.1.0",
|
|
42
42
|
"appium-adb": "12.12.1",
|
|
43
43
|
"sharp": "^0.34.3",
|
|
44
|
-
"@midscene/shared": "1.10.6
|
|
45
|
-
"@midscene/core": "1.10.6
|
|
44
|
+
"@midscene/shared": "1.10.6",
|
|
45
|
+
"@midscene/core": "1.10.6"
|
|
46
46
|
},
|
|
47
47
|
"optionalDependencies": {
|
|
48
48
|
"@ffmpeg-installer/ffmpeg": "^1.1.0"
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"undici": "^6.0.0",
|
|
57
57
|
"vitest": "3.0.5",
|
|
58
58
|
"zod": "^3.25.1",
|
|
59
|
-
"@midscene/playground": "1.10.6
|
|
59
|
+
"@midscene/playground": "1.10.6"
|
|
60
60
|
},
|
|
61
61
|
"license": "MIT",
|
|
62
62
|
"scripts": {
|