android-midscene-automation 0.1.4 → 0.1.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/package.json +1 -1
- package/server/http-api.ts +9 -1
- package/src/App.vue +52 -7
package/package.json
CHANGED
package/server/http-api.ts
CHANGED
|
@@ -94,6 +94,10 @@ function writeNdjson(res: ServerResponse, event: RunGeneratedScriptEvent | { typ
|
|
|
94
94
|
res.write(`${JSON.stringify(event)}\n`);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
function npxCommand() {
|
|
98
|
+
return process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
99
|
+
}
|
|
100
|
+
|
|
97
101
|
type AndroidDevice = {
|
|
98
102
|
id: string;
|
|
99
103
|
status: string;
|
|
@@ -506,7 +510,7 @@ export function createApiMiddleware() {
|
|
|
506
510
|
};
|
|
507
511
|
}
|
|
508
512
|
|
|
509
|
-
playgroundProcess = spawn(
|
|
513
|
+
playgroundProcess = spawn(npxCommand(), ['--yes', '@midscene/android-playground'], {
|
|
510
514
|
cwd: appPath(),
|
|
511
515
|
env: {
|
|
512
516
|
...process.env,
|
|
@@ -515,6 +519,10 @@ export function createApiMiddleware() {
|
|
|
515
519
|
stdio: 'ignore',
|
|
516
520
|
detached: false,
|
|
517
521
|
});
|
|
522
|
+
playgroundProcess.once('error', (error) => {
|
|
523
|
+
console.warn(`Android Playground 启动失败:${error.message}`);
|
|
524
|
+
playgroundProcess = null;
|
|
525
|
+
});
|
|
518
526
|
playgroundProcess.once('exit', () => {
|
|
519
527
|
playgroundProcess = null;
|
|
520
528
|
});
|
package/src/App.vue
CHANGED
|
@@ -72,6 +72,7 @@ const playgroundFrameUrl = ref('');
|
|
|
72
72
|
const playgroundFrameSignature = ref('');
|
|
73
73
|
const devicePreviewUrl = ref('');
|
|
74
74
|
const devicePreviewMode = ref<'stream' | 'screenshot' | ''>('');
|
|
75
|
+
const backendOffline = shallowRef(false);
|
|
75
76
|
const androidDevices = ref<AndroidDevice[]>([]);
|
|
76
77
|
const deviceInterfaceSize = reactive({ width: 0, height: 0 });
|
|
77
78
|
const deviceDebug = reactive({
|
|
@@ -116,6 +117,17 @@ let appiumPreviewTimer: number | null = null;
|
|
|
116
117
|
let executionProgressTimer: number | null = null;
|
|
117
118
|
let scriptRunAbortController: AbortController | null = null;
|
|
118
119
|
|
|
120
|
+
const isBackendFetchError = (error: unknown) => {
|
|
121
|
+
const message = error instanceof Error ? error.message : String(error || '');
|
|
122
|
+
return error instanceof TypeError || /Failed to fetch|NetworkError|Load failed/i.test(message);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const stopPlaygroundPollTimer = () => {
|
|
126
|
+
if (!playgroundPollTimer) return;
|
|
127
|
+
window.clearInterval(playgroundPollTimer);
|
|
128
|
+
playgroundPollTimer = null;
|
|
129
|
+
};
|
|
130
|
+
|
|
119
131
|
const form = reactive({
|
|
120
132
|
promptTitle: '',
|
|
121
133
|
testName: '',
|
|
@@ -739,10 +751,20 @@ const testModel = async (key: 'midscene' | 'scriptOptimizer') => {
|
|
|
739
751
|
};
|
|
740
752
|
|
|
741
753
|
const loadAndroidDevices = async () => {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
754
|
+
if (backendOffline.value) return;
|
|
755
|
+
try {
|
|
756
|
+
const payload = await api.getAndroidDevices();
|
|
757
|
+
backendOffline.value = false;
|
|
758
|
+
androidDevices.value = payload.devices || [];
|
|
759
|
+
playgroundDeviceId.value = payload.currentDeviceId || '';
|
|
760
|
+
playgroundAvailable.value = androidDevices.value.some((item) => item.status === 'device');
|
|
761
|
+
} catch (error) {
|
|
762
|
+
if (isBackendFetchError(error)) {
|
|
763
|
+
markBackendOffline();
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
throw error;
|
|
767
|
+
}
|
|
746
768
|
};
|
|
747
769
|
|
|
748
770
|
const loadDeviceInterface = async () => {
|
|
@@ -757,6 +779,7 @@ const loadDeviceInterface = async () => {
|
|
|
757
779
|
};
|
|
758
780
|
|
|
759
781
|
const loadAdbPreview = () => {
|
|
782
|
+
if (backendOffline.value) return;
|
|
760
783
|
if (!playgroundDeviceId.value) {
|
|
761
784
|
devicePreviewUrl.value = '';
|
|
762
785
|
devicePreviewMode.value = '';
|
|
@@ -786,6 +809,7 @@ const refreshDevicePreview = () => {
|
|
|
786
809
|
};
|
|
787
810
|
|
|
788
811
|
const startAppiumPreviewTimer = () => {
|
|
812
|
+
if (backendOffline.value) return;
|
|
789
813
|
if (appiumPreviewTimer) return;
|
|
790
814
|
loadAdbPreview();
|
|
791
815
|
appiumPreviewTimer = window.setInterval(loadAdbPreview, 1000);
|
|
@@ -797,7 +821,22 @@ const stopAppiumPreviewTimer = () => {
|
|
|
797
821
|
appiumPreviewTimer = null;
|
|
798
822
|
};
|
|
799
823
|
|
|
824
|
+
const markBackendOffline = () => {
|
|
825
|
+
if (backendOffline.value) return;
|
|
826
|
+
backendOffline.value = true;
|
|
827
|
+
playgroundAvailable.value = false;
|
|
828
|
+
playgroundFrameUrl.value = '';
|
|
829
|
+
playgroundFrameSignature.value = '';
|
|
830
|
+
devicePreviewUrl.value = '';
|
|
831
|
+
devicePreviewMode.value = '';
|
|
832
|
+
playgroundPreviewError.value = '本地服务已断开,请重新运行启动命令后刷新页面。';
|
|
833
|
+
errorMessage.value = playgroundPreviewError.value;
|
|
834
|
+
stopPlaygroundPollTimer();
|
|
835
|
+
stopAppiumPreviewTimer();
|
|
836
|
+
};
|
|
837
|
+
|
|
800
838
|
const loadPlaygroundStatus = async () => {
|
|
839
|
+
if (backendOffline.value) return;
|
|
801
840
|
if (!playgroundDeviceId.value) {
|
|
802
841
|
playgroundAvailable.value = false;
|
|
803
842
|
playgroundPreviewError.value = '';
|
|
@@ -844,6 +883,10 @@ const loadPlaygroundStatus = async () => {
|
|
|
844
883
|
loadAdbPreview();
|
|
845
884
|
}
|
|
846
885
|
} catch (error) {
|
|
886
|
+
if (isBackendFetchError(error)) {
|
|
887
|
+
markBackendOffline();
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
847
890
|
playgroundAvailable.value = false;
|
|
848
891
|
playgroundFrameUrl.value = '';
|
|
849
892
|
playgroundFrameSignature.value = '';
|
|
@@ -1207,15 +1250,17 @@ onMounted(async () => {
|
|
|
1207
1250
|
await Promise.allSettled([loadConfig(), loadAppPresets(), loadAndroidDevices(), loadSavedScripts(), loadModelUsageRecords()]);
|
|
1208
1251
|
await loadPlaygroundStatus();
|
|
1209
1252
|
playgroundPollTimer = window.setInterval(() => {
|
|
1253
|
+
if (backendOffline.value) {
|
|
1254
|
+
stopPlaygroundPollTimer();
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1210
1257
|
void loadAndroidDevices();
|
|
1211
1258
|
void loadPlaygroundStatus();
|
|
1212
1259
|
}, 2500);
|
|
1213
1260
|
});
|
|
1214
1261
|
|
|
1215
1262
|
onUnmounted(() => {
|
|
1216
|
-
|
|
1217
|
-
window.clearInterval(playgroundPollTimer);
|
|
1218
|
-
}
|
|
1263
|
+
stopPlaygroundPollTimer();
|
|
1219
1264
|
stopAppiumPreviewTimer();
|
|
1220
1265
|
stopExecutionProgressTimer();
|
|
1221
1266
|
scriptRunAbortController?.abort();
|