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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "android-midscene-automation",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "android-midscene-automation": "./bin/android-midscene-automation.js"
@@ -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('npx', ['--yes', '@midscene/android-playground'], {
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
- const payload = await api.getAndroidDevices();
743
- androidDevices.value = payload.devices || [];
744
- playgroundDeviceId.value = payload.currentDeviceId || '';
745
- playgroundAvailable.value = androidDevices.value.some((item) => item.status === 'device');
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
- if (playgroundPollTimer) {
1217
- window.clearInterval(playgroundPollTimer);
1218
- }
1263
+ stopPlaygroundPollTimer();
1219
1264
  stopAppiumPreviewTimer();
1220
1265
  stopExecutionProgressTimer();
1221
1266
  scriptRunAbortController?.abort();