android-midscene-automation 0.2.4 → 1.1.1

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.
Files changed (525) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +159 -29
  3. package/artemis-main/.dockerignore +15 -0
  4. package/artemis-main/.env.example +34 -0
  5. package/artemis-main/CONTRIBUTING.md +35 -0
  6. package/artemis-main/Dockerfile +68 -0
  7. package/artemis-main/LICENSE +202 -0
  8. package/artemis-main/Makefile +115 -0
  9. package/artemis-main/README.md +322 -0
  10. package/artemis-main/README_CN.md +320 -0
  11. package/artemis-main/apps/__init__.py +15 -0
  12. package/artemis-main/apps/admin_console/__init__.py +19 -0
  13. package/artemis-main/apps/admin_console/core/__init__.py +13 -0
  14. package/artemis-main/apps/admin_console/core/config.py +43 -0
  15. package/artemis-main/apps/admin_console/core/security.py +178 -0
  16. package/artemis-main/apps/admin_console/core/state.py +243 -0
  17. package/artemis-main/apps/admin_console/database/__init__.py +13 -0
  18. package/artemis-main/apps/admin_console/database/connection.py +60 -0
  19. package/artemis-main/apps/admin_console/database/repositories/__init__.py +13 -0
  20. package/artemis-main/apps/admin_console/database/repositories/session_repository.py +564 -0
  21. package/artemis-main/apps/admin_console/database/repositories/step_repository.py +512 -0
  22. package/artemis-main/apps/admin_console/database/repositories/trace_repository.py +133 -0
  23. package/artemis-main/apps/admin_console/index.html +3787 -0
  24. package/artemis-main/apps/admin_console/replay_manager.py +2585 -0
  25. package/artemis-main/apps/admin_console/routers/__init__.py +13 -0
  26. package/artemis-main/apps/admin_console/routers/media.py +198 -0
  27. package/artemis-main/apps/admin_console/routers/replay.py +91 -0
  28. package/artemis-main/apps/admin_console/routers/sessions.py +239 -0
  29. package/artemis-main/apps/admin_console/routers/steps.py +109 -0
  30. package/artemis-main/apps/admin_console/routers/stream.py +56 -0
  31. package/artemis-main/apps/admin_console/routers/system.py +761 -0
  32. package/artemis-main/apps/admin_console/routers/tasks.py +517 -0
  33. package/artemis-main/apps/admin_console/schemas/__init__.py +13 -0
  34. package/artemis-main/apps/admin_console/schemas/task_schema.py +47 -0
  35. package/artemis-main/apps/admin_console/server.py +508 -0
  36. package/artemis-main/apps/admin_console/services/__init__.py +13 -0
  37. package/artemis-main/apps/admin_console/services/device_stream_service.py +137 -0
  38. package/artemis-main/apps/admin_console/services/ipc_service.py +262 -0
  39. package/artemis-main/apps/admin_console/services/media_service.py +623 -0
  40. package/artemis-main/apps/admin_console/services/model_service.py +148 -0
  41. package/artemis-main/apps/admin_console/services/task_preset_catalog.py +514 -0
  42. package/artemis-main/apps/admin_console/services/task_queue_service.py +1719 -0
  43. package/artemis-main/apps/admin_console/services/worker_process_io.py +144 -0
  44. package/artemis-main/apps/showcase_ui/README.md +31 -0
  45. package/artemis-main/apps/showcase_ui/angular.json +132 -0
  46. package/artemis-main/apps/showcase_ui/package-lock.json +9472 -0
  47. package/artemis-main/apps/showcase_ui/package.json +39 -0
  48. package/artemis-main/apps/showcase_ui/proxy.conf.json +23 -0
  49. package/artemis-main/apps/showcase_ui/public/favicon.ico +0 -0
  50. package/artemis-main/apps/showcase_ui/public/logo.png +0 -0
  51. package/artemis-main/apps/showcase_ui/public/logo.svg +56 -0
  52. package/artemis-main/apps/showcase_ui/src/app/app.component.html +24 -0
  53. package/artemis-main/apps/showcase_ui/src/app/app.component.scss +49 -0
  54. package/artemis-main/apps/showcase_ui/src/app/app.component.spec.ts +42 -0
  55. package/artemis-main/apps/showcase_ui/src/app/app.component.ts +32 -0
  56. package/artemis-main/apps/showcase_ui/src/app/app.config.ts +29 -0
  57. package/artemis-main/apps/showcase_ui/src/app/app.routes.ts +27 -0
  58. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream-startup.spec.ts +108 -0
  59. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +1436 -0
  60. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.scss +3973 -0
  61. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts +2235 -0
  62. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.html +380 -0
  63. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.scss +1171 -0
  64. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.ts +270 -0
  65. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.html +433 -0
  66. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.scss +987 -0
  67. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.ts +632 -0
  68. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.html +130 -0
  69. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.scss +406 -0
  70. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.ts +74 -0
  71. package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.scss +115 -0
  72. package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.ts +52 -0
  73. package/artemis-main/apps/showcase_ui/src/app/core/data/smart-tasks.data.ts +334 -0
  74. package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.spec.ts +55 -0
  75. package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.ts +61 -0
  76. package/artemis-main/apps/showcase_ui/src/app/core/i18n/translate.pipe.ts +11 -0
  77. package/artemis-main/apps/showcase_ui/src/app/core/i18n/zh-cn.ts +651 -0
  78. package/artemis-main/apps/showcase_ui/src/app/core/models/markdown.model.ts +43 -0
  79. package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.spec.ts +47 -0
  80. package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.ts +165 -0
  81. package/artemis-main/apps/showcase_ui/src/app/core/models/session.model.ts +85 -0
  82. package/artemis-main/apps/showcase_ui/src/app/core/models/stream.model.ts +246 -0
  83. package/artemis-main/apps/showcase_ui/src/app/core/models/system.model.ts +118 -0
  84. package/artemis-main/apps/showcase_ui/src/app/core/services/task-recommendation.service.ts +111 -0
  85. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.html +2130 -0
  86. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.scss +5204 -0
  87. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.ts +1189 -0
  88. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.html +39 -0
  89. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.scss +151 -0
  90. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.ts +58 -0
  91. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.html +201 -0
  92. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.scss +886 -0
  93. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.ts +324 -0
  94. package/artemis-main/apps/showcase_ui/src/app/services/agent.service.spec.ts +418 -0
  95. package/artemis-main/apps/showcase_ui/src/app/services/agent.service.ts +2012 -0
  96. package/artemis-main/apps/showcase_ui/src/app/services/system.service.spec.ts +173 -0
  97. package/artemis-main/apps/showcase_ui/src/app/services/system.service.ts +620 -0
  98. package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.spec.ts +513 -0
  99. package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.ts +1015 -0
  100. package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.spec.ts +85 -0
  101. package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.ts +662 -0
  102. package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.spec.ts +70 -0
  103. package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.ts +383 -0
  104. package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.spec.ts +107 -0
  105. package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.ts +123 -0
  106. package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.spec.ts +92 -0
  107. package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.ts +116 -0
  108. package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.spec.ts +640 -0
  109. package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.ts +1021 -0
  110. package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.spec.ts +375 -0
  111. package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.ts +1419 -0
  112. package/artemis-main/apps/showcase_ui/src/index.html +37 -0
  113. package/artemis-main/apps/showcase_ui/src/main.ts +22 -0
  114. package/artemis-main/apps/showcase_ui/src/styles.scss +60 -0
  115. package/artemis-main/apps/showcase_ui/tsconfig.app.json +23 -0
  116. package/artemis-main/apps/showcase_ui/tsconfig.json +27 -0
  117. package/artemis-main/apps/showcase_ui/tsconfig.spec.json +23 -0
  118. package/artemis-main/artemis/__init__.py +44 -0
  119. package/artemis-main/artemis/__main__.py +18 -0
  120. package/artemis-main/artemis/_legacy_imports.py +94 -0
  121. package/artemis-main/artemis/_version.py +30 -0
  122. package/artemis-main/artemis/agents/__init__.py +17 -0
  123. package/artemis-main/artemis/agents/checker/checker.json +9 -0
  124. package/artemis-main/artemis/agents/checker/checker.py +859 -0
  125. package/artemis-main/artemis/agents/diagnoser/diagnoser.json +23 -0
  126. package/artemis-main/artemis/agents/diagnoser/diagnoser.py +608 -0
  127. package/artemis-main/artemis/agents/explorer/constants.py +52 -0
  128. package/artemis-main/artemis/agents/explorer/explorer.json +57 -0
  129. package/artemis-main/artemis/agents/explorer/explorer.py +473 -0
  130. package/artemis-main/artemis/agents/explorer/geometry.py +73 -0
  131. package/artemis-main/artemis/agents/explorer/native_runner.py +878 -0
  132. package/artemis-main/artemis/agents/explorer/perception_tools.py +632 -0
  133. package/artemis-main/artemis/agents/explorer/run_setup.py +403 -0
  134. package/artemis-main/artemis/agents/explorer/screen_index.py +266 -0
  135. package/artemis-main/artemis/agents/explorer/tiers.py +110 -0
  136. package/artemis-main/artemis/agents/explorer/tool_declarations.py +394 -0
  137. package/artemis-main/artemis/agents/explorer/universal_runner.py +317 -0
  138. package/artemis-main/artemis/agents/flash/context_compressor.py +452 -0
  139. package/artemis-main/artemis/agents/flash/flash_runner.md +51 -0
  140. package/artemis-main/artemis/agents/flash/flash_summarizer.md +53 -0
  141. package/artemis-main/artemis/agents/flash/flash_summarizer_single.md +54 -0
  142. package/artemis-main/artemis/agents/flash/runner.py +1167 -0
  143. package/artemis-main/artemis/agents/flash/summarizer.py +511 -0
  144. package/artemis-main/artemis/agents/history_analyzer/history_analyzer.md +8 -0
  145. package/artemis-main/artemis/agents/history_analyzer/history_analyzer.py +182 -0
  146. package/artemis-main/artemis/agents/image_processor/image_processor.md +74 -0
  147. package/artemis-main/artemis/agents/image_processor/image_processor.py +322 -0
  148. package/artemis-main/artemis/agents/log_analyzer/log_analyzer.md +15 -0
  149. package/artemis-main/artemis/agents/log_analyzer/log_analyzer.py +403 -0
  150. package/artemis-main/artemis/agents/log_analyzer/log_reader_sub_agent.md +8 -0
  151. package/artemis-main/artemis/agents/log_analyzer/output_analyzer.md +11 -0
  152. package/artemis-main/artemis/agents/log_analyzer/output_analyzer.py +163 -0
  153. package/artemis-main/artemis/agents/object_detector/object_detector.json +9 -0
  154. package/artemis-main/artemis/agents/object_detector/object_detector.py +211 -0
  155. package/artemis-main/artemis/agents/operator/operator.json +3 -0
  156. package/artemis-main/artemis/agents/operator/operator.py +1394 -0
  157. package/artemis-main/artemis/agents/operator/prompts.py +1064 -0
  158. package/artemis-main/artemis/agents/outputter/outputter.md +36 -0
  159. package/artemis-main/artemis/agents/outputter/outputter.py +552 -0
  160. package/artemis-main/artemis/agents/planner/planner.json +32 -0
  161. package/artemis-main/artemis/agents/planner/planner.py +436 -0
  162. package/artemis-main/artemis/agents/prompt_assembly.py +79 -0
  163. package/artemis-main/artemis/agents/summarizer/summarizer.py +190 -0
  164. package/artemis-main/artemis/agents/validator/action_execution.py +98 -0
  165. package/artemis-main/artemis/agents/validator/categories.py +33 -0
  166. package/artemis-main/artemis/agents/validator/execution_loop.py +488 -0
  167. package/artemis-main/artemis/agents/validator/incidents.py +120 -0
  168. package/artemis-main/artemis/agents/validator/pixel_safety_net.md +15 -0
  169. package/artemis-main/artemis/agents/validator/precondition_pixel.py +320 -0
  170. package/artemis-main/artemis/agents/validator/precondition_xml.py +672 -0
  171. package/artemis-main/artemis/agents/validator/tool_declarations.py +332 -0
  172. package/artemis-main/artemis/agents/validator/validator.py +188 -0
  173. package/artemis-main/artemis/agents/video_analyzer/__init__.py +15 -0
  174. package/artemis-main/artemis/agents/video_analyzer/audio_native.py +543 -0
  175. package/artemis-main/artemis/agents/video_analyzer/blackboard.py +821 -0
  176. package/artemis-main/artemis/agents/video_analyzer/chunk_agentic.py +455 -0
  177. package/artemis-main/artemis/agents/video_analyzer/chunk_conversation.py +491 -0
  178. package/artemis-main/artemis/agents/video_analyzer/chunk_native.py +437 -0
  179. package/artemis-main/artemis/agents/video_analyzer/conflict_resolution.py +137 -0
  180. package/artemis-main/artemis/agents/video_analyzer/gemini_files.py +162 -0
  181. package/artemis-main/artemis/agents/video_analyzer/native_coordinator.py +541 -0
  182. package/artemis-main/artemis/agents/video_analyzer/reliability.py +128 -0
  183. package/artemis-main/artemis/agents/video_analyzer/universal_engine.py +487 -0
  184. package/artemis-main/artemis/agents/video_analyzer/universal_tools.py +323 -0
  185. package/artemis-main/artemis/agents/video_analyzer/video_analyzer.md +51 -0
  186. package/artemis-main/artemis/agents/video_analyzer/video_analyzer.py +875 -0
  187. package/artemis-main/artemis/agents/video_analyzer/video_sub_agent.md +15 -0
  188. package/artemis-main/artemis/agents/video_analyzer/video_sub_agent_audio.md +12 -0
  189. package/artemis-main/artemis/clients/accessibility_client.py +438 -0
  190. package/artemis-main/artemis/clients/screen_client_factory.py +403 -0
  191. package/artemis-main/artemis/config/__init__.py +309 -0
  192. package/artemis-main/artemis/config/agent.py +1098 -0
  193. package/artemis-main/artemis/config/constants.py +151 -0
  194. package/artemis-main/artemis/config/core/__init__.py +24 -0
  195. package/artemis-main/artemis/config/core/engine.py +118 -0
  196. package/artemis-main/artemis/config/core/interpolator.py +78 -0
  197. package/artemis-main/artemis/config/llm.py +267 -0
  198. package/artemis-main/artemis/config/paths.py +244 -0
  199. package/artemis-main/artemis/config/runtime.py +197 -0
  200. package/artemis-main/artemis/config/settings.py +315 -0
  201. package/artemis-main/artemis/constants.py +47 -0
  202. package/artemis-main/artemis/context.py +289 -0
  203. package/artemis-main/artemis/controllers/__init__.py +41 -0
  204. package/artemis-main/artemis/controllers/controller_factory.py +41 -0
  205. package/artemis-main/artemis/controllers/device_controller.py +29 -0
  206. package/artemis-main/artemis/controllers/types.py +37 -0
  207. package/artemis-main/artemis/controllers/unified_controller.py +649 -0
  208. package/artemis-main/artemis/core/__init__.py +21 -0
  209. package/artemis-main/artemis/core/diagnostics/__init__.py +90 -0
  210. package/artemis-main/artemis/core/diagnostics/adb_keys.py +270 -0
  211. package/artemis-main/artemis/core/diagnostics/adb_server_connection.py +330 -0
  212. package/artemis-main/artemis/core/diagnostics/device_smoke.py +364 -0
  213. package/artemis-main/artemis/core/diagnostics/emulator_manager.py +501 -0
  214. package/artemis-main/artemis/core/diagnostics/engine.py +438 -0
  215. package/artemis-main/artemis/core/diagnostics/hierarchy_parity.py +216 -0
  216. package/artemis-main/artemis/core/diagnostics/probes/adb_probe.py +887 -0
  217. package/artemis-main/artemis/core/diagnostics/probes/base.py +45 -0
  218. package/artemis-main/artemis/core/diagnostics/probes/credentials_probe.py +296 -0
  219. package/artemis-main/artemis/core/diagnostics/probes/host_probe.py +427 -0
  220. package/artemis-main/artemis/core/diagnostics/probes/runtime_probe.py +169 -0
  221. package/artemis-main/artemis/core/diagnostics/probes/toolchain_probe.py +139 -0
  222. package/artemis-main/artemis/core/diagnostics/readiness.py +83 -0
  223. package/artemis-main/artemis/core/diagnostics/schema.py +118 -0
  224. package/artemis-main/artemis/core/tool_declaration.py +43 -0
  225. package/artemis-main/artemis/core/tool_failure.py +56 -0
  226. package/artemis-main/artemis/data_engine/context_vars.py +24 -0
  227. package/artemis-main/artemis/data_engine/engine.py +1615 -0
  228. package/artemis-main/artemis/data_engine/history_reader.py +142 -0
  229. package/artemis-main/artemis/data_engine/models.py +131 -0
  230. package/artemis-main/artemis/data_engine/storage.py +1245 -0
  231. package/artemis-main/artemis/data_engine/trace.py +727 -0
  232. package/artemis-main/artemis/drivers/__init__.py +48 -0
  233. package/artemis-main/artemis/drivers/android/adb_driver.py +507 -0
  234. package/artemis-main/artemis/drivers/android/hierarchy.py +64 -0
  235. package/artemis-main/artemis/drivers/android/input_ime.py +120 -0
  236. package/artemis-main/artemis/drivers/android/recorder.py +57 -0
  237. package/artemis-main/artemis/drivers/base.py +290 -0
  238. package/artemis-main/artemis/drivers/cloud/cloud_driver.py +117 -0
  239. package/artemis-main/artemis/drivers/factory.py +80 -0
  240. package/artemis-main/artemis/drivers/mock/mock_driver.py +198 -0
  241. package/artemis-main/artemis/drivers/types.py +67 -0
  242. package/artemis-main/artemis/graph/checkpoints.py +1243 -0
  243. package/artemis-main/artemis/graph/graph.py +1055 -0
  244. package/artemis-main/artemis/graph/perception.py +234 -0
  245. package/artemis-main/artemis/graph/state.py +151 -0
  246. package/artemis-main/artemis/graph/visibility.py +238 -0
  247. package/artemis-main/artemis/interfaces/cli/__init__.py +19 -0
  248. package/artemis-main/artemis/interfaces/cli/commands/__init__.py +15 -0
  249. package/artemis-main/artemis/interfaces/cli/commands/batch.py +278 -0
  250. package/artemis-main/artemis/interfaces/cli/commands/doctor.py +468 -0
  251. package/artemis-main/artemis/interfaces/cli/commands/helper.py +226 -0
  252. package/artemis-main/artemis/interfaces/cli/commands/init.py +193 -0
  253. package/artemis-main/artemis/interfaces/cli/commands/mcp.py +862 -0
  254. package/artemis-main/artemis/interfaces/cli/commands/run.py +463 -0
  255. package/artemis-main/artemis/interfaces/cli/commands/server.py +151 -0
  256. package/artemis-main/artemis/interfaces/cli/commands/server_lifecycle.py +328 -0
  257. package/artemis-main/artemis/interfaces/cli/commands/trace.py +111 -0
  258. package/artemis-main/artemis/interfaces/cli/commands/ui.py +404 -0
  259. package/artemis-main/artemis/interfaces/cli/main.py +109 -0
  260. package/artemis-main/artemis/interfaces/sdk/__init__.py +31 -0
  261. package/artemis-main/artemis/interfaces/sdk/client.py +19 -0
  262. package/artemis-main/artemis/interfaces/sdk/task.py +72 -0
  263. package/artemis-main/artemis/llm/__init__.py +25 -0
  264. package/artemis-main/artemis/llm/fake_model.py +179 -0
  265. package/artemis-main/artemis/llm/google/__init__.py +57 -0
  266. package/artemis-main/artemis/llm/google/provider.py +185 -0
  267. package/artemis-main/artemis/llm/google/usage.py +148 -0
  268. package/artemis-main/artemis/llm/parser.py +32 -0
  269. package/artemis-main/artemis/llm/reliability.py +267 -0
  270. package/artemis-main/artemis/llm/router.py +389 -0
  271. package/artemis-main/artemis/llm/schemas.py +36 -0
  272. package/artemis-main/artemis/llm/structured.py +196 -0
  273. package/artemis-main/artemis/main.py +58 -0
  274. package/artemis-main/artemis/mcp/__init__.py +20 -0
  275. package/artemis-main/artemis/mcp/action_executor.py +760 -0
  276. package/artemis-main/artemis/mcp/action_manifest.py +320 -0
  277. package/artemis-main/artemis/mcp/action_names.py +172 -0
  278. package/artemis-main/artemis/mcp/action_server.py +202 -0
  279. package/artemis-main/artemis/mcp/action_session.py +245 -0
  280. package/artemis-main/artemis/mcp/action_specs.py +868 -0
  281. package/artemis-main/artemis/mcp/action_types.py +86 -0
  282. package/artemis-main/artemis/mcp/actuators/__init__.py +21 -0
  283. package/artemis-main/artemis/mcp/actuators/adb.py +414 -0
  284. package/artemis-main/artemis/mcp/actuators/base.py +113 -0
  285. package/artemis-main/artemis/mcp/actuators/mock.py +70 -0
  286. package/artemis-main/artemis/mcp/adb_server.py +490 -0
  287. package/artemis-main/artemis/mcp/observation.py +133 -0
  288. package/artemis-main/artemis/mcp/xml_search_server.py +297 -0
  289. package/artemis-main/artemis/memory/__init__.py +90 -0
  290. package/artemis-main/artemis/memory/chunking.py +1843 -0
  291. package/artemis-main/artemis/memory/context_policy.py +226 -0
  292. package/artemis-main/artemis/memory/step_capsule.md +73 -0
  293. package/artemis-main/artemis/memory/step_memory.py +281 -0
  294. package/artemis-main/artemis/memory/transcript.py +798 -0
  295. package/artemis-main/artemis/platform/__init__.py +61 -0
  296. package/artemis-main/artemis/platform/base.py +104 -0
  297. package/artemis-main/artemis/platform/darwin.py +184 -0
  298. package/artemis-main/artemis/platform/linux.py +207 -0
  299. package/artemis-main/artemis/platform/windows.py +193 -0
  300. package/artemis-main/artemis/resources/__init__.py +46 -0
  301. package/artemis-main/artemis/runtime/__init__.py +108 -0
  302. package/artemis-main/artemis/runtime/adb_endpoint.py +173 -0
  303. package/artemis-main/artemis/runtime/awake_lease.py +283 -0
  304. package/artemis-main/artemis/runtime/awake_service.py +343 -0
  305. package/artemis-main/artemis/runtime/cancel_requests.py +254 -0
  306. package/artemis-main/artemis/runtime/daemon_client.py +422 -0
  307. package/artemis-main/artemis/runtime/device_lock.py +1013 -0
  308. package/artemis-main/artemis/runtime/device_pool.py +478 -0
  309. package/artemis-main/artemis/runtime/helper_manager.py +1030 -0
  310. package/artemis-main/artemis/runtime/process_probe.py +98 -0
  311. package/artemis-main/artemis/runtime/server_lifecycle.py +435 -0
  312. package/artemis-main/artemis/runtime/supervisor.py +121 -0
  313. package/artemis-main/artemis/runtime/trace_store.py +375 -0
  314. package/artemis-main/artemis/sdk/__init__.py +26 -0
  315. package/artemis-main/artemis/sdk/agent.py +485 -0
  316. package/artemis-main/artemis/sdk/builders/__init__.py +29 -0
  317. package/artemis-main/artemis/sdk/builders/agent_config_builder.py +474 -0
  318. package/artemis-main/artemis/sdk/constants.py +15 -0
  319. package/artemis-main/artemis/sdk/run_outcome.py +58 -0
  320. package/artemis-main/artemis/sdk/types/__init__.py +61 -0
  321. package/artemis-main/artemis/sdk/types/agent.py +89 -0
  322. package/artemis-main/artemis/sdk/utils.py +28 -0
  323. package/artemis-main/artemis/services/llm.py +1040 -0
  324. package/artemis-main/artemis/services/token_meter.py +267 -0
  325. package/artemis-main/artemis/telemetry/__init__.py +35 -0
  326. package/artemis-main/artemis/telemetry/models.py +64 -0
  327. package/artemis-main/artemis/telemetry/replay.py +34 -0
  328. package/artemis-main/artemis/telemetry/storage/json_storage.py +47 -0
  329. package/artemis-main/artemis/telemetry/tracer.py +85 -0
  330. package/artemis-main/artemis/toolchain/__init__.py +52 -0
  331. package/artemis-main/artemis/toolchain/descriptors.py +92 -0
  332. package/artemis-main/artemis/toolchain/resolver.py +185 -0
  333. package/artemis-main/artemis/tools/base.py +232 -0
  334. package/artemis-main/artemis/tools/command_tool.py +1240 -0
  335. package/artemis-main/artemis/tools/committee_prompts.json +5 -0
  336. package/artemis-main/artemis/tools/committee_tool.py +422 -0
  337. package/artemis-main/artemis/tools/diagnoser_submit_answer_tool.py +87 -0
  338. package/artemis-main/artemis/tools/diagnostic_tool.py +120 -0
  339. package/artemis-main/artemis/tools/explorer_tool.py +581 -0
  340. package/artemis-main/artemis/tools/history/__init__.py +398 -0
  341. package/artemis-main/artemis/tools/history/replay.py +120 -0
  342. package/artemis-main/artemis/tools/history/screenshot.py +139 -0
  343. package/artemis-main/artemis/tools/history/search.py +563 -0
  344. package/artemis-main/artemis/tools/image_processor_tool.py +124 -0
  345. package/artemis-main/artemis/tools/index.py +47 -0
  346. package/artemis-main/artemis/tools/log_tool.py +108 -0
  347. package/artemis-main/artemis/tools/mobile/launch_app.py +132 -0
  348. package/artemis-main/artemis/tools/mobile/log_utils.py +138 -0
  349. package/artemis-main/artemis/tools/mobile/ocr.py +202 -0
  350. package/artemis-main/artemis/tools/mobile/read_hierarchy.py +108 -0
  351. package/artemis-main/artemis/tools/mobile/read_logs.py +116 -0
  352. package/artemis-main/artemis/tools/mobile/search_logs.py +260 -0
  353. package/artemis-main/artemis/tools/object_detection_tool.py +244 -0
  354. package/artemis-main/artemis/tools/scratchpad.py +819 -0
  355. package/artemis-main/artemis/tools/system/adb_shell.py +31 -0
  356. package/artemis-main/artemis/tools/tool_wrapper.py +270 -0
  357. package/artemis-main/artemis/tools/video_tool.py +261 -0
  358. package/artemis-main/artemis/tools/wait_tool.py +97 -0
  359. package/artemis-main/artemis/utils/app_launch_utils.py +207 -0
  360. package/artemis-main/artemis/utils/coordinates.py +606 -0
  361. package/artemis-main/artemis/utils/credentials_validator.py +201 -0
  362. package/artemis-main/artemis/utils/cv_canvas.py +189 -0
  363. package/artemis-main/artemis/utils/cython_compat.py +45 -0
  364. package/artemis-main/artemis/utils/element_hit_test.py +80 -0
  365. package/artemis-main/artemis/utils/image_diff.py +335 -0
  366. package/artemis-main/artemis/utils/image_hash.py +80 -0
  367. package/artemis-main/artemis/utils/notes.py +470 -0
  368. package/artemis-main/artemis/utils/ocr_api.py +105 -0
  369. package/artemis-main/artemis/utils/ocr_xml_fusion.py +318 -0
  370. package/artemis-main/artemis/utils/plan_grammar.py +513 -0
  371. package/artemis-main/artemis/utils/python_executor.py +213 -0
  372. package/artemis-main/artemis/utils/startup_progress.py +60 -0
  373. package/artemis-main/artemis/utils/task_tree.py +1174 -0
  374. package/artemis-main/artemis/utils/text.py +37 -0
  375. package/artemis-main/artemis/utils/ui_filter.py +651 -0
  376. package/artemis-main/artemis/utils/video.py +685 -0
  377. package/artemis-main/artemis/utils/visualization.py +802 -0
  378. package/artemis-main/config/artemis.example.jsonc +12 -0
  379. package/artemis-main/mcp_server/README.md +111 -0
  380. package/artemis-main/mcp_server/__init__.py +21 -0
  381. package/artemis-main/mcp_server/__main__.py +20 -0
  382. package/artemis-main/mcp_server/background/__init__.py +15 -0
  383. package/artemis-main/mcp_server/background/task_runner.py +453 -0
  384. package/artemis-main/mcp_server/base.py +49 -0
  385. package/artemis-main/mcp_server/notifiers/__init__.py +74 -0
  386. package/artemis-main/mcp_server/notifiers/agentapi.py +289 -0
  387. package/artemis-main/mcp_server/notifiers/base.py +45 -0
  388. package/artemis-main/mcp_server/notifiers/composite.py +87 -0
  389. package/artemis-main/mcp_server/notifiers/desktop.py +108 -0
  390. package/artemis-main/mcp_server/notifiers/file.py +69 -0
  391. package/artemis-main/mcp_server/notifiers/script.py +90 -0
  392. package/artemis-main/mcp_server/notifiers/webhook.py +87 -0
  393. package/artemis-main/mcp_server/rules.md +68 -0
  394. package/artemis-main/mcp_server/server.py +106 -0
  395. package/artemis-main/mcp_server/tools/__init__.py +29 -0
  396. package/artemis-main/mcp_server/tools/device_state.py +107 -0
  397. package/artemis-main/mcp_server/tools/diagnose.py +1249 -0
  398. package/artemis-main/mcp_server/tools/inspect_trace.py +394 -0
  399. package/artemis-main/mcp_server/tools/task_manager.py +627 -0
  400. package/artemis-main/mcp_server/tools/task_runner.py +548 -0
  401. package/artemis-main/mcp_server/utils/__init__.py +15 -0
  402. package/artemis-main/mcp_server/utils/device_utils.py +180 -0
  403. package/artemis-main/mcp_server/utils/env_utils.py +65 -0
  404. package/artemis-main/packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk +0 -0
  405. package/artemis-main/packages/artemis-accessibility-helper/app/build.gradle.kts +35 -0
  406. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/AndroidManifest.xml +49 -0
  407. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/A11yNode.java +274 -0
  408. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/ArtemisAccessibilityService.java +227 -0
  409. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/CommandServer.java +463 -0
  410. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/DisplayUtils.java +111 -0
  411. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/GestureController.java +232 -0
  412. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/HierarchyDumper.java +832 -0
  413. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenReceiver.java +36 -0
  414. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenStore.java +38 -0
  415. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/XmlUtils.java +102 -0
  416. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/values/strings.xml +4 -0
  417. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/xml/accessibility_service_config.xml +10 -0
  418. package/artemis-main/packages/artemis-accessibility-helper/build.gradle.kts +3 -0
  419. package/artemis-main/packages/artemis-accessibility-helper/build_apk.sh +215 -0
  420. package/artemis-main/packages/artemis-accessibility-helper/debug.keystore +0 -0
  421. package/artemis-main/packages/artemis-accessibility-helper/gradle.properties +2 -0
  422. package/artemis-main/packages/artemis-accessibility-helper/helper_manifest.json +7 -0
  423. package/artemis-main/packages/artemis-accessibility-helper/settings.gradle.kts +17 -0
  424. package/artemis-main/packages/artemis-client/DESIGN.md +89 -0
  425. package/artemis-main/packages/artemis-client/README.md +112 -0
  426. package/artemis-main/packages/artemis-client/examples/basic.py +30 -0
  427. package/artemis-main/packages/artemis-client/pyproject.toml +36 -0
  428. package/artemis-main/packages/artemis-client/src/artemis_client/__init__.py +45 -0
  429. package/artemis-main/packages/artemis-client/src/artemis_client/client.py +401 -0
  430. package/artemis-main/packages/artemis-client/src/artemis_client/errors.py +66 -0
  431. package/artemis-main/packages/artemis-client/src/artemis_client/models.py +212 -0
  432. package/artemis-main/packages/artemis-client/src/artemis_client/py.typed +1 -0
  433. package/artemis-main/packages/artemis-client/src/artemis_client/transport.py +134 -0
  434. package/artemis-main/packages/artemis-client/tests/test_client.py +289 -0
  435. package/artemis-main/packages/artemis-client/tests/test_transport.py +85 -0
  436. package/artemis-main/pyproject.toml +259 -0
  437. package/artemis-main/pyright-core.json +17 -0
  438. package/artemis-main/scripts/check_dependency_sources.py +68 -0
  439. package/artemis-main/scripts/install_deps.ps1 +520 -0
  440. package/artemis-main/scripts/install_deps.sh +533 -0
  441. package/artemis-main/scripts/quality_ratchet.py +97 -0
  442. package/artemis-main/scripts/real_device_video_acceptance.py +483 -0
  443. package/artemis-main/scripts/run_edge_case_part.py +327 -0
  444. package/artemis-main/scripts/run_ui.py +242 -0
  445. package/artemis-main/scripts/start.ps1 +500 -0
  446. package/artemis-main/scripts/test_all_edge_cases.py +269 -0
  447. package/artemis-main/scripts/verify_concurrency_and_devices.py +385 -0
  448. package/artemis-main/setup.py +164 -0
  449. package/artemis-main/start.bat +35 -0
  450. package/artemis-main/start.sh +423 -0
  451. package/artemis-main/third_party/mobile_use/LICENSE +190 -0
  452. package/artemis-main/third_party/mobile_use/METADATA +27 -0
  453. package/artemis-main/third_party/mobile_use/NOTICE +12 -0
  454. package/artemis-main/third_party/mobile_use/__init__.py +19 -0
  455. package/artemis-main/third_party/mobile_use/agents/__init__.py +13 -0
  456. package/artemis-main/third_party/mobile_use/agents/hopper/__init__.py +13 -0
  457. package/artemis-main/third_party/mobile_use/agents/hopper/hopper.md +22 -0
  458. package/artemis-main/third_party/mobile_use/agents/hopper/hopper.py +80 -0
  459. package/artemis-main/third_party/mobile_use/clients/__init__.py +13 -0
  460. package/artemis-main/third_party/mobile_use/clients/ui_automator_client.py +482 -0
  461. package/artemis-main/third_party/mobile_use/config/__init__.py +13 -0
  462. package/artemis-main/third_party/mobile_use/config/llm.py +183 -0
  463. package/artemis-main/third_party/mobile_use/config/output.py +184 -0
  464. package/artemis-main/third_party/mobile_use/context.py +119 -0
  465. package/artemis-main/third_party/mobile_use/controllers/__init__.py +13 -0
  466. package/artemis-main/third_party/mobile_use/controllers/platform_specific_commands_controller.py +151 -0
  467. package/artemis-main/third_party/mobile_use/controllers/types.py +92 -0
  468. package/artemis-main/third_party/mobile_use/controllers/unified_controller.py +183 -0
  469. package/artemis-main/third_party/mobile_use/graph.py +54 -0
  470. package/artemis-main/third_party/mobile_use/llm_service.py +193 -0
  471. package/artemis-main/third_party/mobile_use/main.py +130 -0
  472. package/artemis-main/third_party/mobile_use/sdk/__init__.py +13 -0
  473. package/artemis-main/third_party/mobile_use/sdk/agent.py +1060 -0
  474. package/artemis-main/third_party/mobile_use/sdk/builders/__init__.py +13 -0
  475. package/artemis-main/third_party/mobile_use/sdk/builders/agent_config_builder.py +225 -0
  476. package/artemis-main/third_party/mobile_use/sdk/builders/index.py +33 -0
  477. package/artemis-main/third_party/mobile_use/sdk/builders/task_request_builder.py +243 -0
  478. package/artemis-main/third_party/mobile_use/sdk/types/__init__.py +13 -0
  479. package/artemis-main/third_party/mobile_use/sdk/types/agent.py +98 -0
  480. package/artemis-main/third_party/mobile_use/sdk/types/exceptions.py +109 -0
  481. package/artemis-main/third_party/mobile_use/sdk/types/task.py +234 -0
  482. package/artemis-main/third_party/mobile_use/testing.py +59 -0
  483. package/artemis-main/third_party/mobile_use/tools/__init__.py +13 -0
  484. package/artemis-main/third_party/mobile_use/tools/index.py +44 -0
  485. package/artemis-main/third_party/mobile_use/tools/mobile/__init__.py +13 -0
  486. package/artemis-main/third_party/mobile_use/tools/mobile/launch_app.py +89 -0
  487. package/artemis-main/third_party/mobile_use/tools/tool_wrapper.py +52 -0
  488. package/artemis-main/third_party/mobile_use/utils/__init__.py +13 -0
  489. package/artemis-main/third_party/mobile_use/utils/app_launch_utils.py +217 -0
  490. package/artemis-main/third_party/mobile_use/utils/cli_helpers.py +60 -0
  491. package/artemis-main/third_party/mobile_use/utils/decorators.py +158 -0
  492. package/artemis-main/third_party/mobile_use/utils/file.py +31 -0
  493. package/artemis-main/third_party/mobile_use/utils/logger.py +216 -0
  494. package/artemis-main/third_party/mobile_use/utils/media.py +213 -0
  495. package/artemis-main/third_party/mobile_use/utils/shell_utils.py +39 -0
  496. package/artemis-main/third_party/mobile_use/utils/video.py +398 -0
  497. package/artemis-main/uv.lock +4908 -0
  498. package/bin/android-midscene-automation.js +1 -5
  499. package/bin/artemis-config.mjs +15 -0
  500. package/bin/artemis-embedded.py +56 -0
  501. package/bin/start-artemis.mjs +30 -0
  502. package/bin/start-dev.mjs +112 -0
  503. package/bin/test_artemis_embedded.py +44 -0
  504. package/package.json +4 -2
  505. package/server/artemis.ts +34 -0
  506. package/server/http-api.ts +12 -0
  507. package/src/App.vue +10 -5
  508. package/src/api.ts +5 -0
  509. package/src/appium-recorder/AppiumPage.vue +1 -1
  510. package/src/appium-recorder/components/ComponentInspector.vue +1 -1
  511. package/src/appium-recorder/components/FlowNodeSearch.vue +3 -3
  512. package/src/appium-recorder/components/ImageCheckDialog.vue +3 -3
  513. package/src/appium-recorder/components/RecorderWorkspace.vue +4 -4
  514. package/src/appium-recorder/flow-appearance.ts +4 -4
  515. package/src/components/AppSidebar.vue +8 -7
  516. package/src/components/MidsceneMaintenanceNotice.vue +17 -0
  517. package/src/components/analytics/AnalysisCharts.vue +4 -4
  518. package/src/components/analytics/ReportAnalysis.vue +1 -1
  519. package/src/components/generator/TestCaseFileUpload.vue +1 -1
  520. package/src/components/help/HelpCenter.vue +56 -37
  521. package/src/pages/AiGeneratorPage.vue +1 -1
  522. package/src/pages/ArtemisPage.vue +72 -0
  523. package/src/pages/ConfigPage.vue +18 -19
  524. package/src/style.css +340 -121
  525. package/src/types.ts +1 -1
@@ -1,15 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from 'node:child_process';
3
- import { createRequire } from 'node:module';
4
3
  import fs from 'node:fs';
5
4
  import os from 'node:os';
6
5
  import path from 'node:path';
7
6
  import { fileURLToPath } from 'node:url';
8
7
 
9
- const require = createRequire(import.meta.url);
10
8
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
11
- const viteRoot = path.dirname(require.resolve('vite/package.json'));
12
- const viteBin = path.join(viteRoot, 'bin', 'vite.js');
13
9
  const args = process.argv.slice(2);
14
10
  const userRoot = process.cwd();
15
11
  const githubUrl = 'https://github.com/oooooooko/android-midscene-automation';
@@ -90,7 +86,7 @@ console.log(`\nGitHub 源码 / 二次修改:${githubUrl}`);
90
86
  console.log(`问题反馈 / 功能建议:${githubUrl}/issues\n`);
91
87
  console.log(`本地数据目录:${dataRoot}\n`);
92
88
 
93
- const child = spawn(process.execPath, [viteBin, ...args], {
89
+ const child = spawn(process.execPath, [path.join(packageRoot, 'bin/start-dev.mjs'), ...args], {
94
90
  cwd: packageRoot,
95
91
  env: {
96
92
  ...process.env,
@@ -0,0 +1,15 @@
1
+ import { constants, copyFileSync, existsSync, mkdirSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+
4
+ export function ensureArtemisConfig(projectDir) {
5
+ const configDir = resolve(projectDir, 'config');
6
+ const target = resolve(configDir, 'artemis.jsonc');
7
+ if (existsSync(target)) return;
8
+ mkdirSync(configDir, { recursive: true });
9
+ try {
10
+ copyFileSync(resolve(configDir, 'artemis.example.jsonc'), target, constants.COPYFILE_EXCL);
11
+ } catch (error) {
12
+ // Never overwrite a user's configuration, including concurrent first starts.
13
+ if (error.code !== 'EEXIST') throw error;
14
+ }
15
+ }
@@ -0,0 +1,56 @@
1
+ """Launch an unmodified Artemis checkout with an explicit frame-ancestor allowlist."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+ import sys
6
+ from urllib.parse import urlsplit
7
+
8
+
9
+ def frame_policy(origins: str) -> str:
10
+ allowed = []
11
+ for value in origins.split(','):
12
+ origin = value.strip()
13
+ parsed = urlsplit(origin)
14
+ if (parsed.scheme not in {'http', 'https'} or not parsed.hostname
15
+ or parsed.username or parsed.password or parsed.path not in {'', '/'}
16
+ or parsed.query or parsed.fragment
17
+ or any(char in origin for char in " \t\r\n;*'")):
18
+ raise ValueError(f'Invalid ARTEMIS_EMBED_ORIGINS origin: {origin!r}')
19
+ allowed.append(f'{parsed.scheme}://{parsed.netloc}')
20
+ return "frame-ancestors 'self' " + ' '.join(allowed)
21
+
22
+
23
+ class EmbedBoundary:
24
+ def __init__(self, app, policy: str):
25
+ self.app = app
26
+ self.policy = policy.encode('ascii')
27
+
28
+ async def __call__(self, scope, receive, send):
29
+ async def send_headers(message):
30
+ if message['type'] == 'http.response.start':
31
+ # Replace only the framing restriction; retain Host/Origin checks
32
+ # and all other headers installed by Artemis's inner middleware.
33
+ headers = [(key, value) for key, value in message.get('headers', [])
34
+ if key.lower() != b'x-frame-options']
35
+ # Multiple CSP headers are enforced together by the browser.
36
+ headers.append((b'content-security-policy', self.policy))
37
+ message = {**message, 'headers': headers}
38
+ await send(message)
39
+
40
+ await self.app(scope, receive, send_headers)
41
+
42
+
43
+ if __name__ == '__main__':
44
+ policy = frame_policy(os.environ.get(
45
+ 'ARTEMIS_EMBED_ORIGINS',
46
+ 'http://127.0.0.1:5173,http://localhost:5173,'
47
+ 'http://127.0.0.1:4173,http://localhost:4173',
48
+ ))
49
+ sys.path.insert(0, str(Path(os.environ['ARTEMIS_PROJECT_DIR']).resolve()))
50
+ from apps.admin_console.server import app, run_ui_server
51
+ from artemis.interfaces.cli.commands.ui import ensure_showcase_built
52
+ from rich.console import Console
53
+
54
+ ensure_showcase_built(Console())
55
+ app.add_middleware(EmbedBoundary, policy=policy)
56
+ run_ui_server(host='127.0.0.1', port=int(os.environ.get('ARTEMIS_PORT', '8000')))
@@ -0,0 +1,30 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { ensureArtemisConfig } from './artemis-config.mjs';
6
+
7
+ const binDir = dirname(fileURLToPath(import.meta.url));
8
+ const projectDir = resolve(process.env.ARTEMIS_PROJECT_DIR || resolve(binDir, '../artemis-main'));
9
+ if (!existsSync(resolve(projectDir, 'pyproject.toml'))) {
10
+ console.error('找不到 Artemis 源码。请将源码放在 artemis-main,或设置 ARTEMIS_PROJECT_DIR。');
11
+ process.exit(1);
12
+ }
13
+
14
+ ensureArtemisConfig(projectDir);
15
+
16
+ const child = spawn('uv', ['run', '--project', projectDir, 'python', resolve(binDir, 'artemis-embedded.py')], {
17
+ cwd: projectDir,
18
+ stdio: 'inherit',
19
+ env: { ...process.env, ARTEMIS_PROJECT_DIR: projectDir },
20
+ });
21
+ child.once('error', error => {
22
+ console.error(`Artemis 启动失败,请确认已安装 uv:${error.message}`);
23
+ process.exitCode = 1;
24
+ });
25
+ for (const signal of ['SIGINT', 'SIGTERM']) {
26
+ process.on(signal, () => child.kill(signal));
27
+ }
28
+ child.once('exit', (code, signal) => {
29
+ process.exitCode = code ?? (signal ? 1 : 0);
30
+ });
@@ -0,0 +1,112 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import { dirname, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const require = createRequire(import.meta.url);
8
+ const binDir = dirname(fileURLToPath(import.meta.url));
9
+ const projectRoot = resolve(binDir, '..');
10
+ const args = process.argv.slice(2);
11
+ const portIndex = args.indexOf('--port');
12
+ const vitePort = args.find(arg => arg.startsWith('--port='))?.slice(7)
13
+ || (portIndex >= 0 ? args[portIndex + 1] : '5173');
14
+ const artemisPort = process.env.ARTEMIS_PORT || '8000';
15
+ const localArtemisUrl = `http://127.0.0.1:${artemisPort}`;
16
+ const env = {
17
+ ...process.env,
18
+ ARTEMIS_URL: process.env.ARTEMIS_URL || localArtemisUrl,
19
+ ARTEMIS_EMBED_ORIGINS: process.env.ARTEMIS_EMBED_ORIGINS
20
+ || `http://127.0.0.1:${vitePort},http://localhost:${vitePort},http://127.0.0.1:4173,http://localhost:4173`,
21
+ };
22
+ const children = new Set();
23
+ let stopping = false;
24
+ let killTimer;
25
+
26
+ function signalChild(child, signal) {
27
+ try {
28
+ if (!child.devProcessGroup) child.kill(signal);
29
+ else process.kill(-child.pid, signal);
30
+ } catch (error) {
31
+ if (error.code !== 'ESRCH') console.error(`停止开发服务失败:${error.message}`);
32
+ }
33
+ }
34
+
35
+ function stop(code = 0) {
36
+ if (stopping) return;
37
+ stopping = true;
38
+ process.exitCode = code;
39
+ for (const child of children) signalChild(child, 'SIGTERM');
40
+ // uv and Python belong to the launcher group, so descendants are cleaned too.
41
+ killTimer = setTimeout(() => {
42
+ for (const child of children) signalChild(child, 'SIGKILL');
43
+ }, 5000);
44
+ killTimer.unref();
45
+ }
46
+
47
+ function start(script, scriptArgs, required) {
48
+ const devProcessGroup = !required && process.platform !== 'win32';
49
+ const child = spawn(process.execPath, [script, ...scriptArgs], {
50
+ cwd: projectRoot, env, stdio: 'inherit', detached: devProcessGroup,
51
+ });
52
+ child.devProcessGroup = devProcessGroup;
53
+ children.add(child);
54
+ child.once('error', error => {
55
+ console.error(`${required ? '主项目' : 'Artemis'} 启动失败:${error.message}`);
56
+ children.delete(child);
57
+ if (required) stop(1);
58
+ });
59
+ child.once('exit', code => {
60
+ // Clean any descendants even if their direct parent has already exited.
61
+ signalChild(child, 'SIGTERM');
62
+ children.delete(child);
63
+ if (!children.size) clearTimeout(killTimer);
64
+ if (stopping) return;
65
+ if (required) stop(code ?? 1);
66
+ else console.warn('Artemis 服务已退出,主项目继续运行;排查上方日志后可执行 npm run artemis 重试。');
67
+ });
68
+ }
69
+
70
+ // npm may forward a signal already delivered by the terminal; keep both handlers.
71
+ process.on('SIGINT', () => stop());
72
+ process.on('SIGTERM', () => stop());
73
+ process.on('exit', () => {
74
+ for (const child of children) signalChild(child, 'SIGTERM');
75
+ });
76
+
77
+ async function startArtemis() {
78
+ if (env.ARTEMIS_AUTO_START === '0') return;
79
+ try {
80
+ const response = await fetch(new URL('/api/status', env.ARTEMIS_URL), {
81
+ signal: AbortSignal.timeout(1500), redirect: 'error',
82
+ });
83
+ const data = await response.json();
84
+ if (response.ok && data.model_info) {
85
+ console.log(`复用已运行的 Artemis:${env.ARTEMIS_URL}`);
86
+ return;
87
+ }
88
+ } catch { /* No running Artemis; try the local launcher below. */ }
89
+ if (stopping) return;
90
+ const target = new URL(env.ARTEMIS_URL);
91
+ if (!['127.0.0.1', 'localhost'].includes(target.hostname)
92
+ || target.protocol !== 'http:' || (target.port || '80') !== artemisPort) {
93
+ console.warn('ARTEMIS_URL 指向外部或不同端口的服务,请单独启动该服务;主项目继续运行。');
94
+ return;
95
+ }
96
+ const projectDir = resolve(env.ARTEMIS_PROJECT_DIR || resolve(projectRoot, 'artemis-main'));
97
+ if (!existsSync(resolve(projectDir, 'pyproject.toml'))) {
98
+ console.warn('未找到 Artemis 源码,跳过自动启动。请准备 artemis-main 或设置 ARTEMIS_PROJECT_DIR;主项目继续运行。');
99
+ return;
100
+ }
101
+ console.log(`正在自动启动 Artemis:${localArtemisUrl}`);
102
+ start(resolve(binDir, 'start-artemis.mjs'), [], false);
103
+ }
104
+
105
+ const viteBin = resolve(dirname(require.resolve('vite/package.json')), 'bin/vite.js');
106
+ // Keep the port fixed so iframe origins match the actual dev server.
107
+ start(viteBin, ['--host', '127.0.0.1', '--strictPort', ...args], true);
108
+ try {
109
+ await startArtemis();
110
+ } catch (error) {
111
+ console.warn(`Artemis 自动启动失败:${error.message};主项目继续运行。`);
112
+ }
@@ -0,0 +1,44 @@
1
+ import asyncio
2
+ import importlib.util
3
+ from pathlib import Path
4
+ import unittest
5
+
6
+ spec = importlib.util.spec_from_file_location('embedded', Path(__file__).with_name('artemis-embedded.py'))
7
+ embedded = importlib.util.module_from_spec(spec)
8
+ spec.loader.exec_module(embedded)
9
+
10
+
11
+ class EmbedTests(unittest.TestCase):
12
+ def test_only_explicit_origins(self):
13
+ self.assertEqual(embedded.frame_policy('http://localhost:5173/'),
14
+ "frame-ancestors 'self' http://localhost:5173")
15
+ for origin in ['*', 'https://*.example.com', 'http://localhost:5173;script-src *',
16
+ 'http://user:pass@localhost:5173', 'http://localhost/path', 'null']:
17
+ with self.subTest(origin=origin), self.assertRaises(ValueError):
18
+ embedded.frame_policy(origin)
19
+
20
+ def test_preserves_origin_rejection_and_other_headers(self):
21
+ async def run():
22
+ async def app(scope, receive, send):
23
+ await send({'type': 'http.response.start', 'status': 403, 'headers': [
24
+ (b'x-frame-options', b'DENY'), (b'x-content-type-options', b'nosniff'),
25
+ (b'content-security-policy', b"script-src 'self'"),
26
+ ]})
27
+ await send({'type': 'http.response.body', 'body': b'Forbidden'})
28
+ messages = []
29
+ async def send(message):
30
+ messages.append(message)
31
+ await embedded.EmbedBoundary(app, embedded.frame_policy('http://localhost:5173'))(
32
+ {'type': 'http'}, None, send)
33
+ self.assertEqual(messages[0]['status'], 403)
34
+ self.assertEqual(messages[1]['body'], b'Forbidden')
35
+ headers = messages[0]['headers']
36
+ self.assertIn((b'x-content-type-options', b'nosniff'), headers)
37
+ self.assertIn((b'content-security-policy', b"script-src 'self'"), headers)
38
+ self.assertNotIn((b'x-frame-options', b'DENY'), headers)
39
+ self.assertIn((b'content-security-policy', b"frame-ancestors 'self' http://localhost:5173"), headers)
40
+ asyncio.run(run())
41
+
42
+
43
+ if __name__ == '__main__':
44
+ unittest.main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "android-midscene-automation",
3
- "version": "0.2.4",
3
+ "version": "1.1.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/oooooooko/android-midscene-automation.git"
@@ -14,6 +14,7 @@
14
14
  "android-midscene-automation": "bin/android-midscene-automation.js"
15
15
  },
16
16
  "files": [
17
+ "artemis-main",
17
18
  "bin",
18
19
  "index.html",
19
20
  "remote-agent",
@@ -29,7 +30,8 @@
29
30
  "CHANGELOG.md"
30
31
  ],
31
32
  "scripts": {
32
- "dev": "vite --host 127.0.0.1",
33
+ "artemis": "node bin/start-artemis.mjs",
34
+ "dev": "node bin/start-dev.mjs",
33
35
  "prepack": "node bin/sync-readme-docs.mjs",
34
36
  "remote-agent": "tsx remote-agent/index.ts",
35
37
  "build": "vue-tsc -b && vite build",
@@ -0,0 +1,34 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import { appPath } from './paths';
4
+
5
+ export async function getArtemisVersion() {
6
+ try {
7
+ const projectDir = process.env.ARTEMIS_PROJECT_DIR || appPath('artemis-main');
8
+ const source = await readFile(resolve(projectDir, 'pyproject.toml'), 'utf8');
9
+ const project = source.split(/^\[project\]\s*$/m)[1]?.split(/^\[/m)[0] || '';
10
+ const version = project.match(/^version\s*=\s*["']([^"']+)["']/m)?.[1];
11
+ return { version: version || '', message: version ? '本地 Artemis 源码版本' : '未找到 Artemis 版本信息' };
12
+ } catch {
13
+ return { version: '', message: '未找到本地 Artemis 源码,请检查 ARTEMIS_PROJECT_DIR' };
14
+ }
15
+ }
16
+
17
+ export async function getArtemisStatus() {
18
+ const configuredUrl = process.env.ARTEMIS_URL || 'http://127.0.0.1:8000';
19
+ try {
20
+ const url = new URL(configuredUrl);
21
+ const response = await fetch(new URL('/api/status', url), {
22
+ signal: AbortSignal.timeout(3000),
23
+ redirect: 'error',
24
+ });
25
+ const data = await response.json() as { model_info?: unknown };
26
+ if (!response.ok || !data.model_info) throw new Error('服务响应不是 Artemis 控制台');
27
+ const policy = response.headers.get('content-security-policy') || '';
28
+ const ancestors = response.headers.get('x-frame-options')
29
+ ? [] : policy.match(/(?:^|;)\s*frame-ancestors\s+([^;]+)/)?.[1].split(/\s+/) || [];
30
+ return { url: url.origin, available: true, ancestors, message: '' };
31
+ } catch {
32
+ return { url: '', available: false, ancestors: [], message: '未连接到 Artemis,请先在项目根目录运行 npm run artemis,并检查 ARTEMIS_URL 配置。' };
33
+ }
34
+ }
@@ -1,4 +1,5 @@
1
1
  import { getTestAnalytics } from './analytics';
2
+ import { getArtemisStatus, getArtemisVersion } from './artemis';
2
3
  import { readBody, readRawBody, requestErrorStatus } from './request-body';
3
4
  import { canManageConfig, publicConfig, resolveSavedModelKey } from './config-access';
4
5
  import { resolveReportSummary } from '../src/appium-recorder/report-summary';
@@ -615,6 +616,17 @@ export function createApiMiddleware() {
615
616
  const requestUrl = new URL(req.url || '/', 'http://localhost');
616
617
  const pathname = requestUrl.pathname;
617
618
  const allowLocalDevice = isLocalRequest(req);
619
+ if (['/api/artemis/status', '/api/artemis/version'].includes(pathname) && req.method === 'GET') {
620
+ res.setHeader('Content-Type', 'application/json; charset=utf-8');
621
+ res.setHeader('Cache-Control', 'no-store');
622
+ if (!canManageConfig(req)) {
623
+ res.statusCode = 403;
624
+ res.end(JSON.stringify({ message: 'Artemis 集成仅允许在本机页面访问' }));
625
+ } else {
626
+ res.end(JSON.stringify(await (pathname.endsWith('/version') ? getArtemisVersion() : getArtemisStatus())));
627
+ }
628
+ return;
629
+ }
618
630
  if ((pathname === '/api/config' || pathname.startsWith('/api/config/') || pathname === '/api/test-model') && !canManageConfig(req)) {
619
631
  res.statusCode = 403;
620
632
  res.setHeader('Content-Type', 'application/json; charset=utf-8');
package/src/App.vue CHANGED
@@ -36,9 +36,11 @@ import { useDevicePreview } from './composables/use-device-preview';
36
36
  import { useScriptExecution } from './composables/use-script-execution';
37
37
  import { useInitialResource } from './composables/use-initial-resource';
38
38
  import AppSidebar from './components/AppSidebar.vue';
39
+ import MidsceneMaintenanceNotice from './components/MidsceneMaintenanceNotice.vue';
39
40
  const AnalysisPage = defineAsyncComponent(() => import('./pages/AnalysisPage.vue'));
40
41
  const AutomationPage = defineAsyncComponent(() => import('./pages/AutomationPage.vue'));
41
42
  const AppiumPage = defineAsyncComponent(() => import('./appium-recorder/AppiumPage.vue'));
43
+ const ArtemisPage = defineAsyncComponent(() => import('./pages/ArtemisPage.vue'));
42
44
  const ConfigPage = defineAsyncComponent(() => import('./pages/ConfigPage.vue'));
43
45
  const GeneratorPage = defineAsyncComponent(() => import('./pages/GeneratorPage.vue'));
44
46
  const HelpCenter = defineAsyncComponent(() => import('./components/help/HelpCenter.vue'));
@@ -50,7 +52,7 @@ import type {
50
52
  } from './types';
51
53
 
52
54
  const ACTIVE_MENU_STORAGE_KEY = 'android-midscene-automation:active-menu';
53
- const menuKeys: MenuKey[] = ['analysis', 'generator', 'automation', 'config', 'appium', 'about'];
55
+ const menuKeys: MenuKey[] = ['appium', 'artemis', 'generator', 'automation', 'analysis', 'config', 'about'];
54
56
  const storedMenu = window.localStorage.getItem(ACTIVE_MENU_STORAGE_KEY) as MenuKey | null;
55
57
  const activeMenu = ref<MenuKey>(storedMenu && menuKeys.includes(storedMenu) ? storedMenu : 'generator');
56
58
  const activeGeneratorMode = ref<GeneratorMode>('ai');
@@ -120,10 +122,11 @@ async function closeMenu(key: MenuKey) {
120
122
 
121
123
  const sidebarCollapsed = shallowRef(false);
122
124
  const menuItems = [
123
- { key: 'analysis', label: '分析页', icon: DataAnalysis },
125
+ { key: 'appium', label: 'Appium', icon: Monitor },
126
+ { key: 'artemis', label: 'Artemis', icon: Monitor },
124
127
  { key: 'generator', label: '测试脚本生成', icon: Operation },
125
128
  { key: 'automation', label: '自动化测试', icon: Monitor },
126
- { key: 'appium', label: 'Appium', icon: Monitor },
129
+ { key: 'analysis', label: '分析页', icon: DataAnalysis },
127
130
  { key: 'config', label: '参数配置', icon: Setting },
128
131
  { key: 'about', label: '关于', icon: InfoFilled },
129
132
  ] as const;
@@ -875,6 +878,7 @@ onUnmounted(() => {
875
878
  </div>
876
879
  </nav>
877
880
 
881
+ <MidsceneMaintenanceNotice v-if="activeMenu === 'generator' || activeMenu === 'automation'" />
878
882
  <div v-if="connectionError" class="connection-status" role="status">
879
883
  <span>{{ connectionError }}</span><el-button link type="primary" :loading="reconnecting" @click="reconnect">重新连接</el-button>
880
884
  </div>
@@ -885,12 +889,12 @@ onUnmounted(() => {
885
889
  <main
886
890
  class="page-body"
887
891
  :class="{
888
- 'page-body--fixed': activeMenu === 'generator' && activeGeneratorMode === 'ai',
889
892
  'page-body--appium': activeMenu === 'appium',
890
893
  'page-body--config': activeMenu === 'config',
894
+ 'page-body--artemis': activeMenu === 'artemis',
891
895
  }"
892
896
  >
893
- <el-alert v-if="activeMenu !== 'appium' && activeMenu !== 'config' && activeMenu !== 'about' && errorMessage" type="error" :closable="false" show-icon class="page-alert">
897
+ <el-alert v-if="activeMenu !== 'appium' && activeMenu !== 'artemis' && activeMenu !== 'config' && activeMenu !== 'about' && errorMessage" type="error" :closable="false" show-icon class="page-alert">
894
898
  <template #title>{{ errorMessage }}</template>
895
899
  </el-alert>
896
900
 
@@ -1006,6 +1010,7 @@ onUnmounted(() => {
1006
1010
  />
1007
1011
 
1008
1012
  <HelpCenter v-if="visitedMenus.has('about')" v-show="activeMenu === 'about'" :active="activeMenu === 'about'" />
1013
+ <ArtemisPage v-if="visitedMenus.has('artemis')" v-show="activeMenu === 'artemis'" :active="activeMenu === 'artemis'" />
1009
1014
  </main>
1010
1015
  </div>
1011
1016
 
package/src/api.ts CHANGED
@@ -48,6 +48,11 @@ export async function getAppiumVersion() {
48
48
  return readJson<AppiumVersionInfo>(response);
49
49
  }
50
50
 
51
+ export async function getArtemisVersion() {
52
+ const response = await fetch(`${APP_BASE}/api/artemis/version`, { signal: AbortSignal.timeout(8000) });
53
+ return readJson<{ version: string; message: string }>(response);
54
+ }
55
+
51
56
  // 读取已保存的脚本列表,自动化测试页面左侧脚本列表使用。
52
57
  export async function getScripts() {
53
58
  const response = await fetch(`${APP_BASE}/api/scripts`);
@@ -2658,7 +2658,7 @@ watch(
2658
2658
  :deep(.appium-recorder-card--workbench > .el-card__body) { overflow: hidden; }
2659
2659
  :deep(.appium-workbench-tabs > .el-tabs__content) { overflow: hidden; }
2660
2660
  :deep(.appium-workbench-tabs .el-tab-pane) { height: 100%; overflow: auto; }
2661
- .appium-workbench { height: 100%; box-sizing: border-box; grid-template-rows: auto minmax(0, 1fr); gap: 10px; overflow: hidden; padding: 12px; }
2661
+ .appium-workbench { height: 100%; box-sizing: border-box; grid-template-rows: auto minmax(0, 1fr); gap: 12px; overflow: hidden; padding: 8px 16px 16px; }
2662
2662
  .appium-workbench__flow { display: flex; flex-direction: column; min-height: 0; }
2663
2663
  .appium-workbench__flow h3 { margin-bottom: 6px; }
2664
2664
  .appium-workbench__flow :deep(.appium-recorded-steps-panel) { display: flex; flex-direction: column; flex: 1; min-height: 0; }
@@ -27,7 +27,7 @@ defineEmits<{ refresh: []; select: [node: AppiumNode] }>();
27
27
  .component-inspector :deep(.appium-tree-container) { flex: 1; min-height: 0; }
28
28
  .component-inspector :deep(.appium-node-summary) { flex: none; }
29
29
  .panel-header { font-size: 14px; gap: 4px; }
30
- .component-inspector__activity { flex: none; display: flex; min-width: 0; gap: 6px; padding-top: 8px; border-top: 1px solid #e4e7ed; font-size: 12px; color: #606266; }
30
+ .component-inspector__activity { flex: none; display: flex; min-width: 0; gap: 6px; padding-top: 8px; border-top: 1px solid var(--ui-border); font-size: 12px; color: var(--ui-text-secondary); }
31
31
  .component-inspector__activity > span { flex-shrink: 0; }
32
32
  .component-inspector__activity code { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
33
33
  </style>
@@ -49,12 +49,12 @@ function move(direction: number) {
49
49
  .flow-node-search__navigation > span:first-child { margin-right: auto; font-size: 12px; }
50
50
  .flow-node-search__navigation .el-button { width: 26px; height: 26px; min-height: 26px; padding: 0; }
51
51
  .flow-node-search__results { max-height: min(320px, 45vh); overflow: auto; }
52
- .flow-node-search__result { display: flex; flex-direction: column; text-align: left; gap: 4px; width: 100%; border: 0; border-bottom: 1px solid #e4e7ed; padding: 10px 8px; background: #fff; color: #303133; cursor: pointer; overflow-wrap: anywhere; }
52
+ .flow-node-search__result { display: flex; flex-direction: column; text-align: left; gap: 4px; width: 100%; border: 0; border-bottom: 1px solid var(--ui-border); padding: 10px 8px; background: #fff; color: var(--ui-text); cursor: pointer; overflow-wrap: anywhere; }
53
53
  .flow-node-search__result[aria-current="true"] { background: #e6f4ff; }
54
54
  .flow-node-search__result:hover { background: #f0f7fc; }
55
55
  .flow-node-search__result strong { font-size: 13px; }
56
- .flow-node-search__result span, .flow-node-search__result small { font-size: 12px; color: #606266; }
57
- .flow-node-search__empty { padding: 16px 0; text-align: center; color: #606266; }
56
+ .flow-node-search__result span, .flow-node-search__result small { font-size: 12px; color: var(--ui-text-secondary); }
57
+ .flow-node-search__empty { padding: 16px 0; text-align: center; color: var(--ui-text-secondary); }
58
58
  </style>
59
59
 
60
60
  <style>
@@ -155,17 +155,17 @@ async function upload(event: Event) {
155
155
  .image-check-dialog .el-select { flex: 1; }
156
156
  .image-check-dialog .el-input-number { width: 100%; }
157
157
  .image-check-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 12px; }
158
- .image-check-help { margin-left: 8px; color: #606266; }
158
+ .image-check-help { margin-left: 8px; color: var(--ui-text-secondary); }
159
159
  .image-check-grid .el-form-item__label { display: flex; align-items: center; min-height: 20px; }
160
160
  .image-check-threshold-label { display: inline-flex; align-items: center; gap: 4px; }
161
161
  .image-check-dialog .el-button.image-check-threshold-button { flex: 0 0 20px; width: 20px; height: 20px; min-height: 20px; padding: 0; font-size: 14px; }
162
162
  .image-check-threshold-help { max-width: min(300px, calc(100vw - 40px)); font-size: 12px; line-height: 1.7; overflow-wrap: anywhere; }
163
163
  .image-check-strict-warning { margin-top: 6px; }
164
164
  .image-check-template { display: grid; gap: 6px; margin-bottom: 16px; min-width: 0; }
165
- .image-check-size { color: #606266; font-size: 12px; }
165
+ .image-check-size { color: var(--ui-text-secondary); font-size: 12px; }
166
166
  .image-check-region-warning { margin-bottom: 16px; }
167
167
  .image-check-template .el-image, .image-check-empty { height: 96px; width: 100%; background: #eff5f3; border: 1px solid #d5dfdb; border-radius: 4px; }
168
- .image-check-empty { display: grid; place-items: center; color: #606266; }
168
+ .image-check-empty { display: grid; place-items: center; color: var(--ui-text-secondary); }
169
169
  @media (max-width: 480px) {
170
170
  .image-check-grid { grid-template-columns: minmax(0, 1fr); }
171
171
  }
@@ -114,19 +114,19 @@ defineExpose({ showTree, showPreview });
114
114
  <style scoped>
115
115
  .recorder-workspace { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; gap: 8px; }
116
116
  .recorder-workspace__toolbar { display: flex; align-items: center; flex: none; gap: 8px; }
117
- .recorder-workspace__tools { display: flex; }
118
- .recorder-workspace__tools .el-button { width: 24px; height: 24px; min-height: 24px; padding: 0; font-size: 12px; border-radius: 4px; }
117
+ .recorder-workspace__tools { display: flex; gap: 6px; }
118
+ .recorder-workspace__tools .el-button { width: 24px; height: 24px; min-height: 24px; padding: 0; font-size: 12px; border-radius: 50%; }
119
119
  .recorder-workspace__panels { display: grid; position: relative; flex: 1; min-height: 0; }
120
120
  .recorder-workspace__panel, .recorder-workspace__main { position: relative; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
121
121
  .recorder-workspace__panel :deep(> .el-card), .recorder-workspace__main :deep(> .el-card) { flex: 1; min-width: 0; min-height: 0; }
122
122
  .recorder-workspace__panel :deep(.el-card__header) { padding: 12px 46px 12px 12px; min-height: 56px; }
123
123
  .recorder-workspace__panel :deep(.el-card__body) { padding: 10px; }
124
124
  .recorder-workspace__collapse { position: absolute; right: 9px; top: 13px; }
125
- .recorder-workspace__rail { display: flex; flex-direction: column; align-items: center; gap: 12px; border: 1px solid #dce3e8; border-radius: 4px; background: #fff; color: #475569; padding: 14px 4px; cursor: pointer; }
125
+ .recorder-workspace__rail { display: flex; flex-direction: column; align-items: center; gap: 12px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius-control); background: var(--ui-surface); color: #475569; padding: 14px 4px; cursor: pointer; }
126
126
  .recorder-workspace__rail span { writing-mode: vertical-rl; font-size: 13px; }
127
127
  .recorder-workspace__separator { cursor: col-resize; touch-action: none; user-select: none; }
128
128
  .recorder-workspace__separator:hover, .recorder-workspace__separator:focus-visible { background: #b3d8ff; outline: none; }
129
129
  .recorder-workspace__separator.is-disabled { pointer-events: none; }
130
- .recorder-workspace__panel--drawer { position: absolute; inset: 0 auto 0 0; width: min(340px, 100%); z-index: 22; background: #fff; box-shadow: 3px 0 16px #0002; }
130
+ .recorder-workspace__panel--drawer { position: absolute; inset: 0 auto 0 0; width: min(340px, 100%); z-index: 22; background: var(--ui-surface); box-shadow: var(--ui-shadow-popover); }
131
131
  .recorder-workspace__backdrop { position: absolute; inset: 0; border: 0; background: #0003; z-index: 21; }
132
132
  </style>
@@ -1,10 +1,10 @@
1
1
  import type { InjectionKey, Ref } from 'vue';
2
2
 
3
- export const DEFAULT_FLOW_BACKGROUND = '#d4e8dd';
4
- export const FLOW_BACKGROUND_PRESETS = ['#d4e8dd', '#d5e6f5', '#e4dcf2', '#f2dedc', '#d5eae9', '#e8e4cc'];
3
+ export const DEFAULT_FLOW_BACKGROUND = '#f8fafc';
4
+ export const FLOW_BACKGROUND_PRESETS = ['#f8fafc', '#d4e8dd', '#d5e6f5', '#e4dcf2', '#f2dedc', '#d5eae9', '#e8e4cc'];
5
5
  export const flowBackgroundKey: InjectionKey<Readonly<Ref<string>>> = Symbol('flowBackground');
6
- export const DEFAULT_FLOW_LINE_COLOR = '#719985';
7
- export const FLOW_LINE_COLOR_PRESETS = ['#719985', '#4b83c4', '#8b6bb1', '#c78a46', '#7b8794', '#374151'];
6
+ export const DEFAULT_FLOW_LINE_COLOR = '#94a3b8';
7
+ export const FLOW_LINE_COLOR_PRESETS = ['#94a3b8', '#719985', '#4b83c4', '#8b6bb1', '#c78a46', '#7b8794', '#374151'];
8
8
  export const flowLineColorKey: InjectionKey<Readonly<Ref<string>>> = Symbol('flowLineColor');
9
9
 
10
10
  export function isHexColor(value: unknown): value is string {
@@ -9,21 +9,22 @@ defineEmits<{ select: [key: string] }>();
9
9
  <template>
10
10
  <aside class="layout-sidebar" aria-label="主导航">
11
11
  <div class="layout-sidebar__top">
12
- <div class="layout-sidebar__logo" aria-hidden="true">M</div>
13
- <span v-if="!collapsed" class="layout-sidebar__brand">Midscene <span>Admin</span></span>
12
+ <div class="layout-sidebar__logo" aria-hidden="true">A</div>
13
+ <span v-if="!collapsed" class="layout-sidebar__brand" title="移动自动化控制台">移动自动化<span>控制台</span></span>
14
14
  </div>
15
15
  <el-menu :default-active="activeMenu" :default-openeds="['overview', 'midscene']" :collapse="collapsed"
16
16
  :collapse-transition="false" class="layout-menu" @select="$emit('select', $event)">
17
- <el-sub-menu index="overview" aria-label="概览">
18
- <template #title><el-icon><Grid /></el-icon><span>概览</span></template>
19
- <el-menu-item index="analysis" aria-label="分析页"><el-icon><DataAnalysis /></el-icon><template #title>分析页</template></el-menu-item>
20
- </el-sub-menu>
17
+ <el-menu-item index="appium" aria-label="Appium"><el-icon><Monitor /></el-icon><template #title>Appium</template></el-menu-item>
18
+ <el-menu-item index="artemis" aria-label="Artemis"><el-icon><Monitor /></el-icon><template #title>Artemis</template></el-menu-item>
21
19
  <el-sub-menu index="midscene" aria-label="Midscene">
22
20
  <template #title><el-icon><Operation /></el-icon><span>Midscene</span></template>
23
21
  <el-menu-item index="generator" aria-label="测试脚本生成"><el-icon><Operation /></el-icon><template #title>测试脚本生成</template></el-menu-item>
24
22
  <el-menu-item index="automation" aria-label="自动化测试"><el-icon><Monitor /></el-icon><template #title>自动化测试</template></el-menu-item>
25
23
  </el-sub-menu>
26
- <el-menu-item index="appium" aria-label="Appium"><el-icon><Monitor /></el-icon><template #title>Appium</template></el-menu-item>
24
+ <el-sub-menu index="overview" aria-label="概览">
25
+ <template #title><el-icon><Grid /></el-icon><span>概览</span></template>
26
+ <el-menu-item index="analysis" aria-label="分析页"><el-icon><DataAnalysis /></el-icon><template #title>分析页</template></el-menu-item>
27
+ </el-sub-menu>
27
28
  <el-menu-item index="config" aria-label="参数配置"><el-icon><Setting /></el-icon><template #title>参数配置</template></el-menu-item>
28
29
  <el-menu-item index="about" aria-label="关于"><el-icon><InfoFilled /></el-icon><template #title>关于</template></el-menu-item>
29
30
  </el-menu>
@@ -0,0 +1,17 @@
1
+ <template>
2
+ <div class="midscene-maintenance">
3
+ <el-alert title="Midscene 维护说明" type="warning" :closable="false" show-icon>
4
+ 本项目中的 Midscene 集成已停止日常维护,现有功能仍保留。后续可能根据情况升级依赖版本,但不再进行深入测试,建议使用 Midscene 官方版本。
5
+ <a href="https://midscenejs.com/zh/" target="_blank" rel="noopener noreferrer">查看官方文档 ↗</a>
6
+ </el-alert>
7
+ </div>
8
+ </template>
9
+
10
+ <style scoped>
11
+ .midscene-maintenance { flex: none; padding: 8px 20px 0; }
12
+ .midscene-maintenance :deep(.el-alert) { padding: 6px 12px; }
13
+ .midscene-maintenance :deep(.el-alert__title) { font-size: 13px; line-height: 19px; }
14
+ .midscene-maintenance :deep(.el-alert__description) { margin-top: 2px; font-size: 12px; line-height: 18px; }
15
+ .midscene-maintenance :deep(.el-alert__icon) { font-size: 16px; width: 16px; }
16
+ .midscene-maintenance a { margin-left: 8px; color: var(--ui-primary); text-decoration: underline; white-space: nowrap; }
17
+ </style>