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.
- package/CHANGELOG.md +10 -0
- package/README.md +159 -29
- package/artemis-main/.dockerignore +15 -0
- package/artemis-main/.env.example +34 -0
- package/artemis-main/CONTRIBUTING.md +35 -0
- package/artemis-main/Dockerfile +68 -0
- package/artemis-main/LICENSE +202 -0
- package/artemis-main/Makefile +115 -0
- package/artemis-main/README.md +322 -0
- package/artemis-main/README_CN.md +320 -0
- package/artemis-main/apps/__init__.py +15 -0
- package/artemis-main/apps/admin_console/__init__.py +19 -0
- package/artemis-main/apps/admin_console/core/__init__.py +13 -0
- package/artemis-main/apps/admin_console/core/config.py +43 -0
- package/artemis-main/apps/admin_console/core/security.py +178 -0
- package/artemis-main/apps/admin_console/core/state.py +243 -0
- package/artemis-main/apps/admin_console/database/__init__.py +13 -0
- package/artemis-main/apps/admin_console/database/connection.py +60 -0
- package/artemis-main/apps/admin_console/database/repositories/__init__.py +13 -0
- package/artemis-main/apps/admin_console/database/repositories/session_repository.py +564 -0
- package/artemis-main/apps/admin_console/database/repositories/step_repository.py +512 -0
- package/artemis-main/apps/admin_console/database/repositories/trace_repository.py +133 -0
- package/artemis-main/apps/admin_console/index.html +3787 -0
- package/artemis-main/apps/admin_console/replay_manager.py +2585 -0
- package/artemis-main/apps/admin_console/routers/__init__.py +13 -0
- package/artemis-main/apps/admin_console/routers/media.py +198 -0
- package/artemis-main/apps/admin_console/routers/replay.py +91 -0
- package/artemis-main/apps/admin_console/routers/sessions.py +239 -0
- package/artemis-main/apps/admin_console/routers/steps.py +109 -0
- package/artemis-main/apps/admin_console/routers/stream.py +56 -0
- package/artemis-main/apps/admin_console/routers/system.py +761 -0
- package/artemis-main/apps/admin_console/routers/tasks.py +517 -0
- package/artemis-main/apps/admin_console/schemas/__init__.py +13 -0
- package/artemis-main/apps/admin_console/schemas/task_schema.py +47 -0
- package/artemis-main/apps/admin_console/server.py +508 -0
- package/artemis-main/apps/admin_console/services/__init__.py +13 -0
- package/artemis-main/apps/admin_console/services/device_stream_service.py +137 -0
- package/artemis-main/apps/admin_console/services/ipc_service.py +262 -0
- package/artemis-main/apps/admin_console/services/media_service.py +623 -0
- package/artemis-main/apps/admin_console/services/model_service.py +148 -0
- package/artemis-main/apps/admin_console/services/task_preset_catalog.py +514 -0
- package/artemis-main/apps/admin_console/services/task_queue_service.py +1719 -0
- package/artemis-main/apps/admin_console/services/worker_process_io.py +144 -0
- package/artemis-main/apps/showcase_ui/README.md +31 -0
- package/artemis-main/apps/showcase_ui/angular.json +132 -0
- package/artemis-main/apps/showcase_ui/package-lock.json +9472 -0
- package/artemis-main/apps/showcase_ui/package.json +39 -0
- package/artemis-main/apps/showcase_ui/proxy.conf.json +23 -0
- package/artemis-main/apps/showcase_ui/public/favicon.ico +0 -0
- package/artemis-main/apps/showcase_ui/public/logo.png +0 -0
- package/artemis-main/apps/showcase_ui/public/logo.svg +56 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.html +24 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.scss +49 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.spec.ts +42 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.ts +32 -0
- package/artemis-main/apps/showcase_ui/src/app/app.config.ts +29 -0
- package/artemis-main/apps/showcase_ui/src/app/app.routes.ts +27 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream-startup.spec.ts +108 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +1436 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.scss +3973 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts +2235 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.html +380 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.scss +1171 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.ts +270 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.html +433 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.scss +987 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.ts +632 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.html +130 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.scss +406 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.ts +74 -0
- package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.scss +115 -0
- package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.ts +52 -0
- package/artemis-main/apps/showcase_ui/src/app/core/data/smart-tasks.data.ts +334 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.spec.ts +55 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.ts +61 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/translate.pipe.ts +11 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/zh-cn.ts +651 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/markdown.model.ts +43 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.spec.ts +47 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.ts +165 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/session.model.ts +85 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/stream.model.ts +246 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/system.model.ts +118 -0
- package/artemis-main/apps/showcase_ui/src/app/core/services/task-recommendation.service.ts +111 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.html +2130 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.scss +5204 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.ts +1189 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.html +39 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.scss +151 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.ts +58 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.html +201 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.scss +886 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.ts +324 -0
- package/artemis-main/apps/showcase_ui/src/app/services/agent.service.spec.ts +418 -0
- package/artemis-main/apps/showcase_ui/src/app/services/agent.service.ts +2012 -0
- package/artemis-main/apps/showcase_ui/src/app/services/system.service.spec.ts +173 -0
- package/artemis-main/apps/showcase_ui/src/app/services/system.service.ts +620 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.spec.ts +513 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.ts +1015 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.spec.ts +85 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.ts +662 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.spec.ts +70 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.ts +383 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.spec.ts +107 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.ts +123 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.spec.ts +92 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.ts +116 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.spec.ts +640 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.ts +1021 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.spec.ts +375 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.ts +1419 -0
- package/artemis-main/apps/showcase_ui/src/index.html +37 -0
- package/artemis-main/apps/showcase_ui/src/main.ts +22 -0
- package/artemis-main/apps/showcase_ui/src/styles.scss +60 -0
- package/artemis-main/apps/showcase_ui/tsconfig.app.json +23 -0
- package/artemis-main/apps/showcase_ui/tsconfig.json +27 -0
- package/artemis-main/apps/showcase_ui/tsconfig.spec.json +23 -0
- package/artemis-main/artemis/__init__.py +44 -0
- package/artemis-main/artemis/__main__.py +18 -0
- package/artemis-main/artemis/_legacy_imports.py +94 -0
- package/artemis-main/artemis/_version.py +30 -0
- package/artemis-main/artemis/agents/__init__.py +17 -0
- package/artemis-main/artemis/agents/checker/checker.json +9 -0
- package/artemis-main/artemis/agents/checker/checker.py +859 -0
- package/artemis-main/artemis/agents/diagnoser/diagnoser.json +23 -0
- package/artemis-main/artemis/agents/diagnoser/diagnoser.py +608 -0
- package/artemis-main/artemis/agents/explorer/constants.py +52 -0
- package/artemis-main/artemis/agents/explorer/explorer.json +57 -0
- package/artemis-main/artemis/agents/explorer/explorer.py +473 -0
- package/artemis-main/artemis/agents/explorer/geometry.py +73 -0
- package/artemis-main/artemis/agents/explorer/native_runner.py +878 -0
- package/artemis-main/artemis/agents/explorer/perception_tools.py +632 -0
- package/artemis-main/artemis/agents/explorer/run_setup.py +403 -0
- package/artemis-main/artemis/agents/explorer/screen_index.py +266 -0
- package/artemis-main/artemis/agents/explorer/tiers.py +110 -0
- package/artemis-main/artemis/agents/explorer/tool_declarations.py +394 -0
- package/artemis-main/artemis/agents/explorer/universal_runner.py +317 -0
- package/artemis-main/artemis/agents/flash/context_compressor.py +452 -0
- package/artemis-main/artemis/agents/flash/flash_runner.md +51 -0
- package/artemis-main/artemis/agents/flash/flash_summarizer.md +53 -0
- package/artemis-main/artemis/agents/flash/flash_summarizer_single.md +54 -0
- package/artemis-main/artemis/agents/flash/runner.py +1167 -0
- package/artemis-main/artemis/agents/flash/summarizer.py +511 -0
- package/artemis-main/artemis/agents/history_analyzer/history_analyzer.md +8 -0
- package/artemis-main/artemis/agents/history_analyzer/history_analyzer.py +182 -0
- package/artemis-main/artemis/agents/image_processor/image_processor.md +74 -0
- package/artemis-main/artemis/agents/image_processor/image_processor.py +322 -0
- package/artemis-main/artemis/agents/log_analyzer/log_analyzer.md +15 -0
- package/artemis-main/artemis/agents/log_analyzer/log_analyzer.py +403 -0
- package/artemis-main/artemis/agents/log_analyzer/log_reader_sub_agent.md +8 -0
- package/artemis-main/artemis/agents/log_analyzer/output_analyzer.md +11 -0
- package/artemis-main/artemis/agents/log_analyzer/output_analyzer.py +163 -0
- package/artemis-main/artemis/agents/object_detector/object_detector.json +9 -0
- package/artemis-main/artemis/agents/object_detector/object_detector.py +211 -0
- package/artemis-main/artemis/agents/operator/operator.json +3 -0
- package/artemis-main/artemis/agents/operator/operator.py +1394 -0
- package/artemis-main/artemis/agents/operator/prompts.py +1064 -0
- package/artemis-main/artemis/agents/outputter/outputter.md +36 -0
- package/artemis-main/artemis/agents/outputter/outputter.py +552 -0
- package/artemis-main/artemis/agents/planner/planner.json +32 -0
- package/artemis-main/artemis/agents/planner/planner.py +436 -0
- package/artemis-main/artemis/agents/prompt_assembly.py +79 -0
- package/artemis-main/artemis/agents/summarizer/summarizer.py +190 -0
- package/artemis-main/artemis/agents/validator/action_execution.py +98 -0
- package/artemis-main/artemis/agents/validator/categories.py +33 -0
- package/artemis-main/artemis/agents/validator/execution_loop.py +488 -0
- package/artemis-main/artemis/agents/validator/incidents.py +120 -0
- package/artemis-main/artemis/agents/validator/pixel_safety_net.md +15 -0
- package/artemis-main/artemis/agents/validator/precondition_pixel.py +320 -0
- package/artemis-main/artemis/agents/validator/precondition_xml.py +672 -0
- package/artemis-main/artemis/agents/validator/tool_declarations.py +332 -0
- package/artemis-main/artemis/agents/validator/validator.py +188 -0
- package/artemis-main/artemis/agents/video_analyzer/__init__.py +15 -0
- package/artemis-main/artemis/agents/video_analyzer/audio_native.py +543 -0
- package/artemis-main/artemis/agents/video_analyzer/blackboard.py +821 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_agentic.py +455 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_conversation.py +491 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_native.py +437 -0
- package/artemis-main/artemis/agents/video_analyzer/conflict_resolution.py +137 -0
- package/artemis-main/artemis/agents/video_analyzer/gemini_files.py +162 -0
- package/artemis-main/artemis/agents/video_analyzer/native_coordinator.py +541 -0
- package/artemis-main/artemis/agents/video_analyzer/reliability.py +128 -0
- package/artemis-main/artemis/agents/video_analyzer/universal_engine.py +487 -0
- package/artemis-main/artemis/agents/video_analyzer/universal_tools.py +323 -0
- package/artemis-main/artemis/agents/video_analyzer/video_analyzer.md +51 -0
- package/artemis-main/artemis/agents/video_analyzer/video_analyzer.py +875 -0
- package/artemis-main/artemis/agents/video_analyzer/video_sub_agent.md +15 -0
- package/artemis-main/artemis/agents/video_analyzer/video_sub_agent_audio.md +12 -0
- package/artemis-main/artemis/clients/accessibility_client.py +438 -0
- package/artemis-main/artemis/clients/screen_client_factory.py +403 -0
- package/artemis-main/artemis/config/__init__.py +309 -0
- package/artemis-main/artemis/config/agent.py +1098 -0
- package/artemis-main/artemis/config/constants.py +151 -0
- package/artemis-main/artemis/config/core/__init__.py +24 -0
- package/artemis-main/artemis/config/core/engine.py +118 -0
- package/artemis-main/artemis/config/core/interpolator.py +78 -0
- package/artemis-main/artemis/config/llm.py +267 -0
- package/artemis-main/artemis/config/paths.py +244 -0
- package/artemis-main/artemis/config/runtime.py +197 -0
- package/artemis-main/artemis/config/settings.py +315 -0
- package/artemis-main/artemis/constants.py +47 -0
- package/artemis-main/artemis/context.py +289 -0
- package/artemis-main/artemis/controllers/__init__.py +41 -0
- package/artemis-main/artemis/controllers/controller_factory.py +41 -0
- package/artemis-main/artemis/controllers/device_controller.py +29 -0
- package/artemis-main/artemis/controllers/types.py +37 -0
- package/artemis-main/artemis/controllers/unified_controller.py +649 -0
- package/artemis-main/artemis/core/__init__.py +21 -0
- package/artemis-main/artemis/core/diagnostics/__init__.py +90 -0
- package/artemis-main/artemis/core/diagnostics/adb_keys.py +270 -0
- package/artemis-main/artemis/core/diagnostics/adb_server_connection.py +330 -0
- package/artemis-main/artemis/core/diagnostics/device_smoke.py +364 -0
- package/artemis-main/artemis/core/diagnostics/emulator_manager.py +501 -0
- package/artemis-main/artemis/core/diagnostics/engine.py +438 -0
- package/artemis-main/artemis/core/diagnostics/hierarchy_parity.py +216 -0
- package/artemis-main/artemis/core/diagnostics/probes/adb_probe.py +887 -0
- package/artemis-main/artemis/core/diagnostics/probes/base.py +45 -0
- package/artemis-main/artemis/core/diagnostics/probes/credentials_probe.py +296 -0
- package/artemis-main/artemis/core/diagnostics/probes/host_probe.py +427 -0
- package/artemis-main/artemis/core/diagnostics/probes/runtime_probe.py +169 -0
- package/artemis-main/artemis/core/diagnostics/probes/toolchain_probe.py +139 -0
- package/artemis-main/artemis/core/diagnostics/readiness.py +83 -0
- package/artemis-main/artemis/core/diagnostics/schema.py +118 -0
- package/artemis-main/artemis/core/tool_declaration.py +43 -0
- package/artemis-main/artemis/core/tool_failure.py +56 -0
- package/artemis-main/artemis/data_engine/context_vars.py +24 -0
- package/artemis-main/artemis/data_engine/engine.py +1615 -0
- package/artemis-main/artemis/data_engine/history_reader.py +142 -0
- package/artemis-main/artemis/data_engine/models.py +131 -0
- package/artemis-main/artemis/data_engine/storage.py +1245 -0
- package/artemis-main/artemis/data_engine/trace.py +727 -0
- package/artemis-main/artemis/drivers/__init__.py +48 -0
- package/artemis-main/artemis/drivers/android/adb_driver.py +507 -0
- package/artemis-main/artemis/drivers/android/hierarchy.py +64 -0
- package/artemis-main/artemis/drivers/android/input_ime.py +120 -0
- package/artemis-main/artemis/drivers/android/recorder.py +57 -0
- package/artemis-main/artemis/drivers/base.py +290 -0
- package/artemis-main/artemis/drivers/cloud/cloud_driver.py +117 -0
- package/artemis-main/artemis/drivers/factory.py +80 -0
- package/artemis-main/artemis/drivers/mock/mock_driver.py +198 -0
- package/artemis-main/artemis/drivers/types.py +67 -0
- package/artemis-main/artemis/graph/checkpoints.py +1243 -0
- package/artemis-main/artemis/graph/graph.py +1055 -0
- package/artemis-main/artemis/graph/perception.py +234 -0
- package/artemis-main/artemis/graph/state.py +151 -0
- package/artemis-main/artemis/graph/visibility.py +238 -0
- package/artemis-main/artemis/interfaces/cli/__init__.py +19 -0
- package/artemis-main/artemis/interfaces/cli/commands/__init__.py +15 -0
- package/artemis-main/artemis/interfaces/cli/commands/batch.py +278 -0
- package/artemis-main/artemis/interfaces/cli/commands/doctor.py +468 -0
- package/artemis-main/artemis/interfaces/cli/commands/helper.py +226 -0
- package/artemis-main/artemis/interfaces/cli/commands/init.py +193 -0
- package/artemis-main/artemis/interfaces/cli/commands/mcp.py +862 -0
- package/artemis-main/artemis/interfaces/cli/commands/run.py +463 -0
- package/artemis-main/artemis/interfaces/cli/commands/server.py +151 -0
- package/artemis-main/artemis/interfaces/cli/commands/server_lifecycle.py +328 -0
- package/artemis-main/artemis/interfaces/cli/commands/trace.py +111 -0
- package/artemis-main/artemis/interfaces/cli/commands/ui.py +404 -0
- package/artemis-main/artemis/interfaces/cli/main.py +109 -0
- package/artemis-main/artemis/interfaces/sdk/__init__.py +31 -0
- package/artemis-main/artemis/interfaces/sdk/client.py +19 -0
- package/artemis-main/artemis/interfaces/sdk/task.py +72 -0
- package/artemis-main/artemis/llm/__init__.py +25 -0
- package/artemis-main/artemis/llm/fake_model.py +179 -0
- package/artemis-main/artemis/llm/google/__init__.py +57 -0
- package/artemis-main/artemis/llm/google/provider.py +185 -0
- package/artemis-main/artemis/llm/google/usage.py +148 -0
- package/artemis-main/artemis/llm/parser.py +32 -0
- package/artemis-main/artemis/llm/reliability.py +267 -0
- package/artemis-main/artemis/llm/router.py +389 -0
- package/artemis-main/artemis/llm/schemas.py +36 -0
- package/artemis-main/artemis/llm/structured.py +196 -0
- package/artemis-main/artemis/main.py +58 -0
- package/artemis-main/artemis/mcp/__init__.py +20 -0
- package/artemis-main/artemis/mcp/action_executor.py +760 -0
- package/artemis-main/artemis/mcp/action_manifest.py +320 -0
- package/artemis-main/artemis/mcp/action_names.py +172 -0
- package/artemis-main/artemis/mcp/action_server.py +202 -0
- package/artemis-main/artemis/mcp/action_session.py +245 -0
- package/artemis-main/artemis/mcp/action_specs.py +868 -0
- package/artemis-main/artemis/mcp/action_types.py +86 -0
- package/artemis-main/artemis/mcp/actuators/__init__.py +21 -0
- package/artemis-main/artemis/mcp/actuators/adb.py +414 -0
- package/artemis-main/artemis/mcp/actuators/base.py +113 -0
- package/artemis-main/artemis/mcp/actuators/mock.py +70 -0
- package/artemis-main/artemis/mcp/adb_server.py +490 -0
- package/artemis-main/artemis/mcp/observation.py +133 -0
- package/artemis-main/artemis/mcp/xml_search_server.py +297 -0
- package/artemis-main/artemis/memory/__init__.py +90 -0
- package/artemis-main/artemis/memory/chunking.py +1843 -0
- package/artemis-main/artemis/memory/context_policy.py +226 -0
- package/artemis-main/artemis/memory/step_capsule.md +73 -0
- package/artemis-main/artemis/memory/step_memory.py +281 -0
- package/artemis-main/artemis/memory/transcript.py +798 -0
- package/artemis-main/artemis/platform/__init__.py +61 -0
- package/artemis-main/artemis/platform/base.py +104 -0
- package/artemis-main/artemis/platform/darwin.py +184 -0
- package/artemis-main/artemis/platform/linux.py +207 -0
- package/artemis-main/artemis/platform/windows.py +193 -0
- package/artemis-main/artemis/resources/__init__.py +46 -0
- package/artemis-main/artemis/runtime/__init__.py +108 -0
- package/artemis-main/artemis/runtime/adb_endpoint.py +173 -0
- package/artemis-main/artemis/runtime/awake_lease.py +283 -0
- package/artemis-main/artemis/runtime/awake_service.py +343 -0
- package/artemis-main/artemis/runtime/cancel_requests.py +254 -0
- package/artemis-main/artemis/runtime/daemon_client.py +422 -0
- package/artemis-main/artemis/runtime/device_lock.py +1013 -0
- package/artemis-main/artemis/runtime/device_pool.py +478 -0
- package/artemis-main/artemis/runtime/helper_manager.py +1030 -0
- package/artemis-main/artemis/runtime/process_probe.py +98 -0
- package/artemis-main/artemis/runtime/server_lifecycle.py +435 -0
- package/artemis-main/artemis/runtime/supervisor.py +121 -0
- package/artemis-main/artemis/runtime/trace_store.py +375 -0
- package/artemis-main/artemis/sdk/__init__.py +26 -0
- package/artemis-main/artemis/sdk/agent.py +485 -0
- package/artemis-main/artemis/sdk/builders/__init__.py +29 -0
- package/artemis-main/artemis/sdk/builders/agent_config_builder.py +474 -0
- package/artemis-main/artemis/sdk/constants.py +15 -0
- package/artemis-main/artemis/sdk/run_outcome.py +58 -0
- package/artemis-main/artemis/sdk/types/__init__.py +61 -0
- package/artemis-main/artemis/sdk/types/agent.py +89 -0
- package/artemis-main/artemis/sdk/utils.py +28 -0
- package/artemis-main/artemis/services/llm.py +1040 -0
- package/artemis-main/artemis/services/token_meter.py +267 -0
- package/artemis-main/artemis/telemetry/__init__.py +35 -0
- package/artemis-main/artemis/telemetry/models.py +64 -0
- package/artemis-main/artemis/telemetry/replay.py +34 -0
- package/artemis-main/artemis/telemetry/storage/json_storage.py +47 -0
- package/artemis-main/artemis/telemetry/tracer.py +85 -0
- package/artemis-main/artemis/toolchain/__init__.py +52 -0
- package/artemis-main/artemis/toolchain/descriptors.py +92 -0
- package/artemis-main/artemis/toolchain/resolver.py +185 -0
- package/artemis-main/artemis/tools/base.py +232 -0
- package/artemis-main/artemis/tools/command_tool.py +1240 -0
- package/artemis-main/artemis/tools/committee_prompts.json +5 -0
- package/artemis-main/artemis/tools/committee_tool.py +422 -0
- package/artemis-main/artemis/tools/diagnoser_submit_answer_tool.py +87 -0
- package/artemis-main/artemis/tools/diagnostic_tool.py +120 -0
- package/artemis-main/artemis/tools/explorer_tool.py +581 -0
- package/artemis-main/artemis/tools/history/__init__.py +398 -0
- package/artemis-main/artemis/tools/history/replay.py +120 -0
- package/artemis-main/artemis/tools/history/screenshot.py +139 -0
- package/artemis-main/artemis/tools/history/search.py +563 -0
- package/artemis-main/artemis/tools/image_processor_tool.py +124 -0
- package/artemis-main/artemis/tools/index.py +47 -0
- package/artemis-main/artemis/tools/log_tool.py +108 -0
- package/artemis-main/artemis/tools/mobile/launch_app.py +132 -0
- package/artemis-main/artemis/tools/mobile/log_utils.py +138 -0
- package/artemis-main/artemis/tools/mobile/ocr.py +202 -0
- package/artemis-main/artemis/tools/mobile/read_hierarchy.py +108 -0
- package/artemis-main/artemis/tools/mobile/read_logs.py +116 -0
- package/artemis-main/artemis/tools/mobile/search_logs.py +260 -0
- package/artemis-main/artemis/tools/object_detection_tool.py +244 -0
- package/artemis-main/artemis/tools/scratchpad.py +819 -0
- package/artemis-main/artemis/tools/system/adb_shell.py +31 -0
- package/artemis-main/artemis/tools/tool_wrapper.py +270 -0
- package/artemis-main/artemis/tools/video_tool.py +261 -0
- package/artemis-main/artemis/tools/wait_tool.py +97 -0
- package/artemis-main/artemis/utils/app_launch_utils.py +207 -0
- package/artemis-main/artemis/utils/coordinates.py +606 -0
- package/artemis-main/artemis/utils/credentials_validator.py +201 -0
- package/artemis-main/artemis/utils/cv_canvas.py +189 -0
- package/artemis-main/artemis/utils/cython_compat.py +45 -0
- package/artemis-main/artemis/utils/element_hit_test.py +80 -0
- package/artemis-main/artemis/utils/image_diff.py +335 -0
- package/artemis-main/artemis/utils/image_hash.py +80 -0
- package/artemis-main/artemis/utils/notes.py +470 -0
- package/artemis-main/artemis/utils/ocr_api.py +105 -0
- package/artemis-main/artemis/utils/ocr_xml_fusion.py +318 -0
- package/artemis-main/artemis/utils/plan_grammar.py +513 -0
- package/artemis-main/artemis/utils/python_executor.py +213 -0
- package/artemis-main/artemis/utils/startup_progress.py +60 -0
- package/artemis-main/artemis/utils/task_tree.py +1174 -0
- package/artemis-main/artemis/utils/text.py +37 -0
- package/artemis-main/artemis/utils/ui_filter.py +651 -0
- package/artemis-main/artemis/utils/video.py +685 -0
- package/artemis-main/artemis/utils/visualization.py +802 -0
- package/artemis-main/config/artemis.example.jsonc +12 -0
- package/artemis-main/mcp_server/README.md +111 -0
- package/artemis-main/mcp_server/__init__.py +21 -0
- package/artemis-main/mcp_server/__main__.py +20 -0
- package/artemis-main/mcp_server/background/__init__.py +15 -0
- package/artemis-main/mcp_server/background/task_runner.py +453 -0
- package/artemis-main/mcp_server/base.py +49 -0
- package/artemis-main/mcp_server/notifiers/__init__.py +74 -0
- package/artemis-main/mcp_server/notifiers/agentapi.py +289 -0
- package/artemis-main/mcp_server/notifiers/base.py +45 -0
- package/artemis-main/mcp_server/notifiers/composite.py +87 -0
- package/artemis-main/mcp_server/notifiers/desktop.py +108 -0
- package/artemis-main/mcp_server/notifiers/file.py +69 -0
- package/artemis-main/mcp_server/notifiers/script.py +90 -0
- package/artemis-main/mcp_server/notifiers/webhook.py +87 -0
- package/artemis-main/mcp_server/rules.md +68 -0
- package/artemis-main/mcp_server/server.py +106 -0
- package/artemis-main/mcp_server/tools/__init__.py +29 -0
- package/artemis-main/mcp_server/tools/device_state.py +107 -0
- package/artemis-main/mcp_server/tools/diagnose.py +1249 -0
- package/artemis-main/mcp_server/tools/inspect_trace.py +394 -0
- package/artemis-main/mcp_server/tools/task_manager.py +627 -0
- package/artemis-main/mcp_server/tools/task_runner.py +548 -0
- package/artemis-main/mcp_server/utils/__init__.py +15 -0
- package/artemis-main/mcp_server/utils/device_utils.py +180 -0
- package/artemis-main/mcp_server/utils/env_utils.py +65 -0
- package/artemis-main/packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk +0 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/build.gradle.kts +35 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/AndroidManifest.xml +49 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/A11yNode.java +274 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/ArtemisAccessibilityService.java +227 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/CommandServer.java +463 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/DisplayUtils.java +111 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/GestureController.java +232 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/HierarchyDumper.java +832 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenReceiver.java +36 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenStore.java +38 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/XmlUtils.java +102 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/values/strings.xml +4 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/xml/accessibility_service_config.xml +10 -0
- package/artemis-main/packages/artemis-accessibility-helper/build.gradle.kts +3 -0
- package/artemis-main/packages/artemis-accessibility-helper/build_apk.sh +215 -0
- package/artemis-main/packages/artemis-accessibility-helper/debug.keystore +0 -0
- package/artemis-main/packages/artemis-accessibility-helper/gradle.properties +2 -0
- package/artemis-main/packages/artemis-accessibility-helper/helper_manifest.json +7 -0
- package/artemis-main/packages/artemis-accessibility-helper/settings.gradle.kts +17 -0
- package/artemis-main/packages/artemis-client/DESIGN.md +89 -0
- package/artemis-main/packages/artemis-client/README.md +112 -0
- package/artemis-main/packages/artemis-client/examples/basic.py +30 -0
- package/artemis-main/packages/artemis-client/pyproject.toml +36 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/__init__.py +45 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/client.py +401 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/errors.py +66 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/models.py +212 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/py.typed +1 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/transport.py +134 -0
- package/artemis-main/packages/artemis-client/tests/test_client.py +289 -0
- package/artemis-main/packages/artemis-client/tests/test_transport.py +85 -0
- package/artemis-main/pyproject.toml +259 -0
- package/artemis-main/pyright-core.json +17 -0
- package/artemis-main/scripts/check_dependency_sources.py +68 -0
- package/artemis-main/scripts/install_deps.ps1 +520 -0
- package/artemis-main/scripts/install_deps.sh +533 -0
- package/artemis-main/scripts/quality_ratchet.py +97 -0
- package/artemis-main/scripts/real_device_video_acceptance.py +483 -0
- package/artemis-main/scripts/run_edge_case_part.py +327 -0
- package/artemis-main/scripts/run_ui.py +242 -0
- package/artemis-main/scripts/start.ps1 +500 -0
- package/artemis-main/scripts/test_all_edge_cases.py +269 -0
- package/artemis-main/scripts/verify_concurrency_and_devices.py +385 -0
- package/artemis-main/setup.py +164 -0
- package/artemis-main/start.bat +35 -0
- package/artemis-main/start.sh +423 -0
- package/artemis-main/third_party/mobile_use/LICENSE +190 -0
- package/artemis-main/third_party/mobile_use/METADATA +27 -0
- package/artemis-main/third_party/mobile_use/NOTICE +12 -0
- package/artemis-main/third_party/mobile_use/__init__.py +19 -0
- package/artemis-main/third_party/mobile_use/agents/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/hopper.md +22 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/hopper.py +80 -0
- package/artemis-main/third_party/mobile_use/clients/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/clients/ui_automator_client.py +482 -0
- package/artemis-main/third_party/mobile_use/config/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/config/llm.py +183 -0
- package/artemis-main/third_party/mobile_use/config/output.py +184 -0
- package/artemis-main/third_party/mobile_use/context.py +119 -0
- package/artemis-main/third_party/mobile_use/controllers/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/controllers/platform_specific_commands_controller.py +151 -0
- package/artemis-main/third_party/mobile_use/controllers/types.py +92 -0
- package/artemis-main/third_party/mobile_use/controllers/unified_controller.py +183 -0
- package/artemis-main/third_party/mobile_use/graph.py +54 -0
- package/artemis-main/third_party/mobile_use/llm_service.py +193 -0
- package/artemis-main/third_party/mobile_use/main.py +130 -0
- package/artemis-main/third_party/mobile_use/sdk/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/agent.py +1060 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/agent_config_builder.py +225 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/index.py +33 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/task_request_builder.py +243 -0
- package/artemis-main/third_party/mobile_use/sdk/types/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/types/agent.py +98 -0
- package/artemis-main/third_party/mobile_use/sdk/types/exceptions.py +109 -0
- package/artemis-main/third_party/mobile_use/sdk/types/task.py +234 -0
- package/artemis-main/third_party/mobile_use/testing.py +59 -0
- package/artemis-main/third_party/mobile_use/tools/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/tools/index.py +44 -0
- package/artemis-main/third_party/mobile_use/tools/mobile/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/tools/mobile/launch_app.py +89 -0
- package/artemis-main/third_party/mobile_use/tools/tool_wrapper.py +52 -0
- package/artemis-main/third_party/mobile_use/utils/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/utils/app_launch_utils.py +217 -0
- package/artemis-main/third_party/mobile_use/utils/cli_helpers.py +60 -0
- package/artemis-main/third_party/mobile_use/utils/decorators.py +158 -0
- package/artemis-main/third_party/mobile_use/utils/file.py +31 -0
- package/artemis-main/third_party/mobile_use/utils/logger.py +216 -0
- package/artemis-main/third_party/mobile_use/utils/media.py +213 -0
- package/artemis-main/third_party/mobile_use/utils/shell_utils.py +39 -0
- package/artemis-main/third_party/mobile_use/utils/video.py +398 -0
- package/artemis-main/uv.lock +4908 -0
- package/bin/android-midscene-automation.js +1 -5
- package/bin/artemis-config.mjs +15 -0
- package/bin/artemis-embedded.py +56 -0
- package/bin/start-artemis.mjs +30 -0
- package/bin/start-dev.mjs +112 -0
- package/bin/test_artemis_embedded.py +44 -0
- package/package.json +4 -2
- package/server/artemis.ts +34 -0
- package/server/http-api.ts +12 -0
- package/src/App.vue +10 -5
- package/src/api.ts +5 -0
- package/src/appium-recorder/AppiumPage.vue +1 -1
- package/src/appium-recorder/components/ComponentInspector.vue +1 -1
- package/src/appium-recorder/components/FlowNodeSearch.vue +3 -3
- package/src/appium-recorder/components/ImageCheckDialog.vue +3 -3
- package/src/appium-recorder/components/RecorderWorkspace.vue +4 -4
- package/src/appium-recorder/flow-appearance.ts +4 -4
- package/src/components/AppSidebar.vue +8 -7
- package/src/components/MidsceneMaintenanceNotice.vue +17 -0
- package/src/components/analytics/AnalysisCharts.vue +4 -4
- package/src/components/analytics/ReportAnalysis.vue +1 -1
- package/src/components/generator/TestCaseFileUpload.vue +1 -1
- package/src/components/help/HelpCenter.vue +56 -37
- package/src/pages/AiGeneratorPage.vue +1 -1
- package/src/pages/ArtemisPage.vue +72 -0
- package/src/pages/ConfigPage.vue +18 -19
- package/src/style.css +340 -121
- 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, [
|
|
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": "
|
|
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
|
-
"
|
|
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
|
+
}
|
package/server/http-api.ts
CHANGED
|
@@ -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[] = ['
|
|
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: '
|
|
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: '
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
57
|
-
.flow-node-search__empty { padding: 16px 0; text-align: center; color:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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 = '#
|
|
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 = '#
|
|
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">
|
|
13
|
-
<span v-if="!collapsed" class="layout-sidebar__brand"
|
|
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-
|
|
18
|
-
|
|
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
|
|
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>
|