android-midscene-automation 0.2.4 → 1.1.0

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 +7 -0
  2. package/README.md +35 -0
  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
@@ -0,0 +1,36 @@
1
+ package com.artemis.helper;
2
+
3
+ import android.content.BroadcastReceiver;
4
+ import android.content.Context;
5
+ import android.content.Intent;
6
+ import android.util.Log;
7
+
8
+ /**
9
+ * Receives the host's session token.
10
+ *
11
+ * The receiver is guarded in the manifest by
12
+ * {@code android.permission.WRITE_SECURE_SETTINGS}, a permission ordinary apps
13
+ * cannot hold but the adb shell user does, so only something with adb access to
14
+ * the device can set (or clear) the token:
15
+ *
16
+ * <pre>
17
+ * adb shell am broadcast -n com.artemis.helper/.TokenReceiver \
18
+ * -a com.artemis.helper.SET_TOKEN --es token &lt;hex&gt;
19
+ * </pre>
20
+ */
21
+ public class TokenReceiver extends BroadcastReceiver {
22
+
23
+ private static final String TAG = "ArtemisTokenReceiver";
24
+ public static final String ACTION_SET_TOKEN = "com.artemis.helper.SET_TOKEN";
25
+ public static final String EXTRA_TOKEN = "token";
26
+
27
+ @Override
28
+ public void onReceive(Context context, Intent intent) {
29
+ if (intent == null || !ACTION_SET_TOKEN.equals(intent.getAction())) {
30
+ return;
31
+ }
32
+ String value = intent.getStringExtra(EXTRA_TOKEN);
33
+ TokenStore.set(value);
34
+ Log.i(TAG, TokenStore.isSet() ? "Session token set" : "Session token cleared");
35
+ }
36
+ }
@@ -0,0 +1,38 @@
1
+ package com.artemis.helper;
2
+
3
+ import java.nio.charset.StandardCharsets;
4
+ import java.security.MessageDigest;
5
+
6
+ /**
7
+ * The session token the host pushed for this process lifetime.
8
+ *
9
+ * The loopback server is reachable by every app on the device, so requests are
10
+ * only served when they carry this token. It lives in memory only: a killed or
11
+ * re-bound service starts without one and answers 401 until the host pushes it
12
+ * again (which the host does on every attach and on every 401).
13
+ */
14
+ public final class TokenStore {
15
+
16
+ private static volatile String token;
17
+
18
+ private TokenStore() {}
19
+
20
+ public static void set(String value) {
21
+ token = (value == null || value.isEmpty()) ? null : value;
22
+ }
23
+
24
+ public static boolean isSet() {
25
+ return token != null;
26
+ }
27
+
28
+ /** Constant-time comparison so a local app cannot guess the token byte by byte. */
29
+ public static boolean matches(String candidate) {
30
+ String current = token;
31
+ if (current == null || candidate == null) {
32
+ return false;
33
+ }
34
+ return MessageDigest.isEqual(
35
+ current.getBytes(StandardCharsets.UTF_8),
36
+ candidate.getBytes(StandardCharsets.UTF_8));
37
+ }
38
+ }
@@ -0,0 +1,102 @@
1
+ package com.artemis.helper;
2
+
3
+ /**
4
+ * High-performance XML serialization and character sanitization utilities.
5
+ * Ensures the output strictly conforms to the W3C XML 1.0 specification,
6
+ * preventing XML parsing errors (such as ET.ParseError in Python) caused by
7
+ * unprintable control characters, NUL bytes, or unescaped entities.
8
+ */
9
+ public final class XmlUtils {
10
+
11
+ private XmlUtils() {}
12
+
13
+ /**
14
+ * Appends an escaped attribute to the StringBuilder: {@code name="value"}.
15
+ * If the value is null or empty, appends {@code name=""}.
16
+ */
17
+ public static void appendAttribute(StringBuilder sb, String name, CharSequence value) {
18
+ sb.append(' ').append(name).append("=\"");
19
+ if (value != null && value.length() > 0) {
20
+ escapeXmlAttr(sb, value);
21
+ }
22
+ sb.append('"');
23
+ }
24
+
25
+ /**
26
+ * Appends a boolean attribute: {@code name="true"} or {@code name="false"}.
27
+ */
28
+ public static void appendBooleanAttribute(StringBuilder sb, String name, boolean value) {
29
+ sb.append(' ').append(name).append(value ? "=\"true\"" : "=\"false\"");
30
+ }
31
+
32
+ /**
33
+ * Appends an integer attribute: {@code name="123"}.
34
+ */
35
+ public static void appendIntAttribute(StringBuilder sb, String name, int value) {
36
+ sb.append(' ').append(name).append("=\"").append(value).append('"');
37
+ }
38
+
39
+ /**
40
+ * Escapes and sanitizes an attribute value according to XML 1.0 rules.
41
+ * Drops illegal XML characters (e.g. ASCII control characters 0x00-0x08, 0x0B, 0x0C, 0x0E-0x1F)
42
+ * and escapes standard entities (&amp;, &lt;, &gt;, &quot;, &apos;).
43
+ */
44
+ public static void escapeXmlAttr(StringBuilder sb, CharSequence text) {
45
+ final int len = text.length();
46
+ for (int i = 0; i < len; i++) {
47
+ char c = text.charAt(i);
48
+ switch (c) {
49
+ case '&':
50
+ sb.append("&amp;");
51
+ break;
52
+ case '<':
53
+ sb.append("&lt;");
54
+ break;
55
+ case '>':
56
+ sb.append("&gt;");
57
+ break;
58
+ case '"':
59
+ sb.append("&quot;");
60
+ break;
61
+ case '\'':
62
+ sb.append("&apos;");
63
+ break;
64
+ case '\t':
65
+ sb.append("&#9;");
66
+ break;
67
+ case '\n':
68
+ sb.append("&#10;");
69
+ break;
70
+ case '\r':
71
+ sb.append("&#13;");
72
+ break;
73
+ default:
74
+ // XML 1.0 legal character range check:
75
+ // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
76
+ if (c >= 0x20 && c <= 0xD7FF) {
77
+ sb.append(c);
78
+ } else if (c >= 0xE000 && c <= 0xFFFD) {
79
+ sb.append(c);
80
+ } else if (Character.isHighSurrogate(c)) {
81
+ if (i + 1 < len && Character.isLowSurrogate(text.charAt(i + 1))) {
82
+ sb.append(c).append(text.charAt(i + 1));
83
+ i++;
84
+ }
85
+ }
86
+ // Characters outside these ranges (e.g. \u0000 through \u0008, \u000B, \u000C, \u000E-\u001F,
87
+ // unpaired surrogates, \uFFFE, \uFFFF) are invalid XML 1.0 and silently omitted to guarantee well-formedness.
88
+ break;
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Sanitizes a string for safe XML attribute inclusion, returning the escaped string.
95
+ */
96
+ public static String escapeXmlAttr(CharSequence text) {
97
+ if (text == null || text.length() == 0) return "";
98
+ StringBuilder sb = new StringBuilder(text.length() + 16);
99
+ escapeXmlAttr(sb, text);
100
+ return sb.toString();
101
+ }
102
+ }
@@ -0,0 +1,4 @@
1
+ <resources>
2
+ <string name="app_name">Artemis Accessibility Helper</string>
3
+ <string name="accessibility_service_description">Lightweight non-exclusive accessibility helper for Artemis UI automation and testing.</string>
4
+ </resources>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:accessibilityEventTypes="typeWindowStateChanged"
4
+ android:accessibilityFeedbackType="feedbackGeneric"
5
+ android:notificationTimeout="100"
6
+ android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows|flagReportViewIds"
7
+ android:canRetrieveWindowContent="true"
8
+ android:canPerformGestures="true"
9
+ android:canTakeScreenshot="true"
10
+ android:description="@string/accessibility_service_description" />
@@ -0,0 +1,3 @@
1
+ plugins {
2
+ id("com.android.application") version "8.5.0" apply false
3
+ }
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env bash
2
+ # Standalone build for ArtemisAccessibilityHelper.apk (no Gradle daemon).
3
+ #
4
+ # Outputs, next to this script:
5
+ # ArtemisAccessibilityHelper.apk the signed, zipaligned artifact
6
+ # helper_manifest.json {version_code, version_name, sha256, built_at}
7
+ #
8
+ # The Python runtime (artemis/runtime/helper_manager.py) reads helper_manifest.json
9
+ # to decide whether a device needs an install or an upgrade, so the manifest must
10
+ # always be regenerated together with the APK. The version is sourced from
11
+ # app/build.gradle.kts (defaultConfig.versionCode / versionName) so the Gradle
12
+ # build and this script cannot drift.
13
+ set -e
14
+
15
+ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ cd "$DIR"
17
+
18
+ _to_posix() {
19
+ if command -v cygpath >/dev/null 2>&1; then
20
+ cygpath -u "$1"
21
+ else
22
+ printf '%s' "$1"
23
+ fi
24
+ }
25
+
26
+ # 1. Locate Android SDK
27
+ SDK_ROOT="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
28
+ if [ -n "$SDK_ROOT" ]; then
29
+ SDK_ROOT="$(_to_posix "$SDK_ROOT")"
30
+ fi
31
+ if [ -z "$SDK_ROOT" ] || [ ! -d "$SDK_ROOT" ]; then
32
+ for candidate in \
33
+ "$HOME/Library/Android/sdk" \
34
+ "$HOME/Android/Sdk" \
35
+ /opt/android-sdk \
36
+ /usr/local/share/android-sdk \
37
+ "$( [ -n "$LOCALAPPDATA" ] && _to_posix "$LOCALAPPDATA/Android/Sdk" )"; do
38
+ if [ -n "$candidate" ] && [ -d "$candidate" ]; then
39
+ SDK_ROOT="$candidate"
40
+ break
41
+ fi
42
+ done
43
+ fi
44
+
45
+ if [ -z "$SDK_ROOT" ] || [ ! -d "$SDK_ROOT" ]; then
46
+ echo "Error: Android SDK not found. Set ANDROID_HOME." >&2
47
+ exit 1
48
+ fi
49
+
50
+ BUILD_TOOLS="$(find "$SDK_ROOT/build-tools" -maxdepth 1 -mindepth 1 | sort -V | tail -n 1)"
51
+ PLATFORM="$(find "$SDK_ROOT/platforms" -maxdepth 1 -mindepth 1 | sort -V | tail -n 1)"
52
+
53
+ if [ -z "$BUILD_TOOLS" ] || [ ! -d "$BUILD_TOOLS" ]; then
54
+ echo "Error: No build-tools found under $SDK_ROOT/build-tools" >&2
55
+ exit 1
56
+ fi
57
+
58
+ if [ -z "$PLATFORM" ] || [ ! -f "$PLATFORM/android.jar" ]; then
59
+ echo "Error: android.jar not found under $SDK_ROOT/platforms" >&2
60
+ exit 1
61
+ fi
62
+
63
+ # 2. Locate Java compiler and runtime
64
+ if [ -z "$JAVA_HOME" ]; then
65
+ for candidate in \
66
+ "/Applications/Android Studio.app/Contents/jbr/Contents/Home" \
67
+ "$(_to_posix "${PROGRAMFILES:-/c/Program Files}/Android/Android Studio/jbr")" \
68
+ "/opt/android-studio/jbr"; do
69
+ if [ -n "$candidate" ] && [ -x "$candidate/bin/javac" ] || [ -x "$candidate/bin/javac.exe" ]; then
70
+ export JAVA_HOME="$candidate"
71
+ break
72
+ fi
73
+ done
74
+ fi
75
+ if [ -n "$JAVA_HOME" ]; then
76
+ export PATH="$JAVA_HOME/bin:$PATH"
77
+ fi
78
+
79
+ if ! command -v javac >/dev/null 2>&1; then
80
+ echo "Error: javac not found in PATH or JAVA_HOME" >&2
81
+ exit 1
82
+ fi
83
+
84
+ PYTHON_BIN="${PYTHON:-}"
85
+ if [ -z "$PYTHON_BIN" ]; then
86
+ # The Windows Store "python" stub is on PATH but exits 49; prefer the project venv.
87
+ for candidate in "$DIR/../../.venv/Scripts/python.exe" "$DIR/../../.venv/bin/python" python3 python; do
88
+ if "$candidate" -c "import zipfile" >/dev/null 2>&1; then
89
+ PYTHON_BIN="$candidate"
90
+ break
91
+ fi
92
+ done
93
+ fi
94
+ if [ -z "$PYTHON_BIN" ]; then
95
+ echo "Error: python3 not found (needed to package the DEX and write helper_manifest.json)" >&2
96
+ exit 1
97
+ fi
98
+
99
+ # Build-tools ship as .exe / .bat wrappers on Windows; resolve the right name once.
100
+ _tool() {
101
+ local base="$BUILD_TOOLS/$1"
102
+ for suffix in "" ".exe" ".bat"; do
103
+ if [ -f "$base$suffix" ]; then
104
+ printf '%s' "$base$suffix"
105
+ return 0
106
+ fi
107
+ done
108
+ echo "Error: $1 not found under $BUILD_TOOLS" >&2
109
+ exit 1
110
+ }
111
+ AAPT2="$(_tool aapt2)"
112
+ D8="$(_tool d8)"
113
+ ZIPALIGN="$(_tool zipalign)"
114
+ APKSIGNER="$(_tool apksigner)"
115
+
116
+ # 3. Version: single source is app/build.gradle.kts
117
+ VERSION_CODE="$(sed -n 's/^[[:space:]]*versionCode[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' app/build.gradle.kts | head -n 1)"
118
+ VERSION_NAME="$(sed -n 's/^[[:space:]]*versionName[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' app/build.gradle.kts | head -n 1)"
119
+ if [ -z "$VERSION_CODE" ] || [ -z "$VERSION_NAME" ]; then
120
+ echo "Error: could not read versionCode / versionName from app/build.gradle.kts" >&2
121
+ exit 1
122
+ fi
123
+
124
+ echo "=================================================="
125
+ echo " Building ArtemisAccessibilityHelper.apk"
126
+ echo " Version: $VERSION_NAME (code $VERSION_CODE)"
127
+ echo " Build Tools: $(basename "$BUILD_TOOLS")"
128
+ echo " Platform: $(basename "$PLATFORM")"
129
+ echo " Java: $(javac -version 2>&1)"
130
+ echo "=================================================="
131
+
132
+ rm -rf build
133
+ mkdir -p build/gen build/obj build/apk build/res_compiled
134
+
135
+ echo "-> Compiling resources..."
136
+ "$AAPT2" compile --dir app/src/main/res -o build/res_compiled/res.zip
137
+
138
+ echo "-> Linking APK..."
139
+ "$AAPT2" link \
140
+ -I "$PLATFORM/android.jar" \
141
+ --manifest app/src/main/AndroidManifest.xml \
142
+ --min-sdk-version 24 \
143
+ --target-sdk-version 35 \
144
+ --version-code "$VERSION_CODE" \
145
+ --version-name "$VERSION_NAME" \
146
+ --java build/gen \
147
+ -o build/apk/unaligned.apk \
148
+ build/res_compiled/res.zip
149
+
150
+ echo "-> Compiling Java sources..."
151
+ JAVA_FILES="$(find build/gen app/src/main/java -name "*.java")"
152
+ javac -encoding UTF-8 \
153
+ -source 8 -target 8 \
154
+ -Xlint:-options \
155
+ -cp "$PLATFORM/android.jar" \
156
+ -d build/obj \
157
+ $JAVA_FILES
158
+
159
+ echo "-> Dexing with D8..."
160
+ CLASS_FILES="$(find build/obj -name "*.class")"
161
+ "$D8" \
162
+ --lib "$PLATFORM/android.jar" \
163
+ --output build/apk/ \
164
+ --min-api 24 \
165
+ $CLASS_FILES
166
+
167
+ echo "-> Packaging DEX..."
168
+ "$PYTHON_BIN" - <<'PY'
169
+ import zipfile
170
+ with zipfile.ZipFile("build/apk/unaligned.apk", "a", compression=zipfile.ZIP_DEFLATED) as apk:
171
+ apk.write("build/apk/classes.dex", "classes.dex")
172
+ PY
173
+
174
+ echo "-> Zipaligning..."
175
+ "$ZIPALIGN" -f -p 4 build/apk/unaligned.apk build/apk/aligned.apk
176
+
177
+ echo "-> Signing APK..."
178
+ if [ ! -f debug.keystore ]; then
179
+ echo "Creating persistent debug.keystore..."
180
+ keytool -genkey -v -keystore debug.keystore \
181
+ -storepass android -alias androiddebugkey -keypass android \
182
+ -keyalg RSA -keysize 2048 -validity 10000 \
183
+ -dname "CN=Artemis Debug,O=Artemis,C=US"
184
+ fi
185
+
186
+ "$APKSIGNER" sign \
187
+ --ks debug.keystore \
188
+ --ks-pass pass:android \
189
+ --key-pass pass:android \
190
+ --ks-key-alias androiddebugkey \
191
+ --out ArtemisAccessibilityHelper.apk \
192
+ build/apk/aligned.apk
193
+
194
+ echo "-> Writing helper_manifest.json..."
195
+ VERSION_CODE="$VERSION_CODE" VERSION_NAME="$VERSION_NAME" "$PYTHON_BIN" - <<'PY'
196
+ import datetime, hashlib, json, os
197
+ digest = hashlib.sha256(open("ArtemisAccessibilityHelper.apk", "rb").read()).hexdigest()
198
+ manifest = {
199
+ "package": "com.artemis.helper",
200
+ "version_code": int(os.environ["VERSION_CODE"]),
201
+ "version_name": os.environ["VERSION_NAME"],
202
+ "sha256": digest,
203
+ "built_at": datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0).isoformat(),
204
+ }
205
+ with open("helper_manifest.json", "w", encoding="utf-8", newline="\n") as fh:
206
+ json.dump(manifest, fh, indent=2)
207
+ fh.write("\n")
208
+ print(json.dumps(manifest, indent=2))
209
+ PY
210
+
211
+ echo "=================================================="
212
+ echo " Build SUCCESSFUL!"
213
+ echo " Artifact: $DIR/ArtemisAccessibilityHelper.apk"
214
+ ls -lh ArtemisAccessibilityHelper.apk
215
+ echo "=================================================="
@@ -0,0 +1,2 @@
1
+ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
2
+ android.useAndroidX=true
@@ -0,0 +1,7 @@
1
+ {
2
+ "package": "com.artemis.helper",
3
+ "version_code": 6,
4
+ "version_name": "1.2.0",
5
+ "sha256": "133e373fe03615c251cd74b03a38539c844d09a074954bca2d2dde6684561969",
6
+ "built_at": "2026-09-10T23:13:11+00:00"
7
+ }
@@ -0,0 +1,17 @@
1
+ pluginManagement {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ gradlePluginPortal()
6
+ }
7
+ }
8
+ dependencyResolutionManagement {
9
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10
+ repositories {
11
+ google()
12
+ mavenCentral()
13
+ }
14
+ }
15
+
16
+ rootProject.name = "artemis-accessibility-helper"
17
+ include(":app")
@@ -0,0 +1,89 @@
1
+ # Artemis Thin Client Design
2
+
3
+ ## Goals
4
+
5
+ Developer machines install only a small remote SDK, while the host connected to
6
+ Android devices runs the full Artemis stack. The SDK must:
7
+
8
+ - have no dependency on ADB, device drivers, agents, LLMs, web services, or image libraries;
9
+ - never attempt to start a local Artemis daemon;
10
+ - remain compatible with the existing server API;
11
+ - allow the server to add agent and device capabilities independently; and
12
+ - provide explicit capability negotiation and errors as the protocol evolves.
13
+
14
+ ## Boundary
15
+
16
+ The client owns request modeling, HTTP communication, status polling, error
17
+ normalization, and result models. Device discovery, task scheduling, concurrency
18
+ control, model selection, execution, logs, videos, and artifact generation all
19
+ belong to the server.
20
+
21
+ The distribution is named `artemis-client`, while the Python import is
22
+ `artemis_client`. This prevents it from shadowing the full runtime's top-level
23
+ `artemis` package. The full runtime can depend on this package later and
24
+ re-export the client from `artemis` for compatibility.
25
+
26
+ ## Phase 1: Compatibility Adapter
27
+
28
+ The first release calls the current `/api/*` administration endpoints directly.
29
+ The client generates a task ID and maps it to the server's existing `session_id`
30
+ to make retries idempotent. While a task is queued but not yet visible in the
31
+ session database, the client reconstructs its state from `queue` and
32
+ `active_tasks` in `/api/status`.
33
+
34
+ Experimental parameters live in `options`. Older servers ignore unknown fields,
35
+ while newer servers can consume them without requiring an immediate SDK release.
36
+
37
+ ## Phase 2: Stable Remote API
38
+
39
+ The server should expose versioned endpoints dedicated to remote clients:
40
+
41
+ ```text
42
+ POST /api/v1/tasks
43
+ GET /api/v1/tasks/{task_id}
44
+ DELETE /api/v1/tasks/{task_id}
45
+ GET /api/v1/tasks/{task_id}/events
46
+ GET /api/v1/tasks/{task_id}/artifacts
47
+ GET /api/v1/devices
48
+ GET /api/v1/capabilities
49
+ ```
50
+
51
+ The administration API and remote SDK API should remain separate. The remote API
52
+ uses a consistent error envelope:
53
+
54
+ ```json
55
+ {
56
+ "error": {
57
+ "code": "DEVICE_OFFLINE",
58
+ "message": "The selected device is offline",
59
+ "retryable": false
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## Compatibility Rules
65
+
66
+ 1. `/api/v1` may add optional request and response fields, but must not change existing field semantics.
67
+ 2. The client ignores unknown response fields and preserves the original response in each model's `raw` attribute.
68
+ 3. Every new request field must have a server-side default.
69
+ 4. The SDK checks `/api/v1/capabilities` before using a new feature.
70
+ 5. Breaking changes ship under `/api/v2`; they are never applied in place.
71
+ 6. The SDK and server follow semantic versioning independently.
72
+
73
+ ## Security
74
+
75
+ The current daemon exposes administration and credential-related endpoints and
76
+ must not listen on a public interface without protection. Use an SSH or VPN tunnel
77
+ in the short term. In production, place a reverse proxy in front of the daemon,
78
+ expose only the remote API, and enforce HTTPS, bearer-token authentication,
79
+ request-size limits, rate limits, and audit logging. Tokens are accepted only
80
+ through arguments or environment variables and are never persisted by the SDK.
81
+
82
+ ## Future Work
83
+
84
+ - SSE event subscriptions with reconnection support;
85
+ - artifact manifests and streaming downloads;
86
+ - batch task submission;
87
+ - server and API version constraints;
88
+ - retries limited to network failures and idempotent requests; and
89
+ - OpenAPI contract tests to prevent server and SDK drift.
@@ -0,0 +1,112 @@
1
+ # Artemis Client
2
+
3
+ `artemis-client` is the remote Python SDK that runs on a developer machine. It
4
+ communicates with the Artemis daemon on a device host over HTTP and contains no
5
+ ADB, device-driver, agent, LLM, or image-processing code.
6
+
7
+ Runtime dependencies: **zero**. HTTP, JSON, and asynchronous adapters use only
8
+ the Python standard library.
9
+
10
+ ## Installation
11
+
12
+ Install from a local checkout:
13
+
14
+ ```powershell
15
+ uv add --editable C:\path\to\artemis\packages\artemis-client
16
+ ```
17
+
18
+ Install from the Git repository subdirectory:
19
+
20
+ ```powershell
21
+ uv add "artemis-client @ git+https://github.com/google/artemis.git#subdirectory=packages/artemis-client"
22
+ ```
23
+
24
+ ## Connect to a Device Host
25
+
26
+ The current Artemis daemon administration API does not include built-in remote
27
+ authentication. Do not expose its port directly to the public internet. During
28
+ the initial rollout, connect through an SSH tunnel:
29
+
30
+ ```powershell
31
+ ssh -L 8000:127.0.0.1:8000 user@artemis-host
32
+ ```
33
+
34
+ Then connect the SDK to the local tunnel endpoint:
35
+
36
+ ```python
37
+ from artemis_client import ArtemisClient
38
+
39
+ client = ArtemisClient("http://127.0.0.1:8000")
40
+ ```
41
+
42
+ For production, place a reverse proxy in front of the daemon that enforces HTTPS
43
+ and bearer-token authentication and exposes only task-related APIs.
44
+
45
+ ## Usage
46
+
47
+ ```python
48
+ import asyncio
49
+ import os
50
+
51
+ from artemis_client import ArtemisClient
52
+
53
+
54
+ async def main() -> None:
55
+ client = ArtemisClient(
56
+ os.environ["ARTEMIS_BASE_URL"],
57
+ token=os.environ.get("ARTEMIS_TOKEN"),
58
+ )
59
+
60
+ handle = await client.submit(
61
+ "Open the Battery page in Settings and verify the battery percentage",
62
+ profile="flash",
63
+ device_serial="emulator-5554",
64
+ )
65
+ print("queued:", handle.task_id)
66
+
67
+ result = await client.wait_for_task(handle.task_id, timeout=600)
68
+ if not result.succeeded:
69
+ raise RuntimeError(result.error or result.status)
70
+
71
+
72
+ asyncio.run(main())
73
+ ```
74
+
75
+ You can also submit a task and wait for it in one call:
76
+
77
+ ```python
78
+ result = await client.run("Open Android Settings and check the Wi-Fi status")
79
+ ```
80
+
81
+ Main methods:
82
+
83
+ - `health()` quickly checks whether the server and scheduler are alive.
84
+ - `readiness()` runs full device and toolchain diagnostics and may take longer.
85
+ - `capabilities()` negotiates API features and falls back for older servers.
86
+ - `list_devices()` lists devices available on the host.
87
+ - `submit()` submits an idempotent task and returns immediately.
88
+ - `get_task()` retrieves a task.
89
+ - `wait_for_task()` waits for a terminal state.
90
+ - `run()` submits a task and waits for completion.
91
+ - `stop()` stops one task.
92
+
93
+ By default, `submit()` generates a UUID on the client and sends it as the current
94
+ server's `session_id`. Network retries reuse the same `task_id`, preventing the
95
+ server from creating duplicate tasks.
96
+
97
+ ## Compatibility
98
+
99
+ Version 0.1 supports these current Artemis daemon endpoints:
100
+
101
+ - `POST /api/run`
102
+ - `GET /api/sessions/{session_id}`
103
+ - `GET /api/status`
104
+ - `POST /api/stop`
105
+ - `GET /api/devices`
106
+ - `GET /api/system/readiness`
107
+
108
+ Capability discovery first attempts the future `GET /api/v1/capabilities`
109
+ endpoint. If an older server returns 404, the SDK uses its known baseline
110
+ capability set.
111
+
112
+ See [DESIGN.md](DESIGN.md) for the architecture and protocol evolution plan.
@@ -0,0 +1,30 @@
1
+ """Run one task against an Artemis host."""
2
+
3
+ import asyncio
4
+ import os
5
+
6
+ from artemis_client import ArtemisClient
7
+
8
+
9
+ async def main() -> None:
10
+ client = ArtemisClient(
11
+ os.environ.get("ARTEMIS_BASE_URL", "http://127.0.0.1:8000"),
12
+ token=os.environ.get("ARTEMIS_TOKEN"),
13
+ )
14
+
15
+ devices = await client.list_devices()
16
+ if not devices:
17
+ raise RuntimeError("The Artemis host did not report an Android device")
18
+
19
+ result = await client.run(
20
+ "Open the Battery page in Android Settings and verify the battery percentage.",
21
+ profile="flash",
22
+ device_serial=devices[0].serial,
23
+ )
24
+ if not result.succeeded:
25
+ raise RuntimeError(result.error or f"Task ended with status {result.status}")
26
+ print(f"Task {result.task_id} completed on {result.device_serial}")
27
+
28
+
29
+ if __name__ == "__main__":
30
+ asyncio.run(main())