Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b584dae80a | ||
|
|
bf6247703d |
No files matched your search
@@ -2,15 +2,27 @@
|
||||
|
||||
## Project Overview
|
||||
|
||||
本仓库是基于 ARTEMIS 扩展的 Android AI 自动化测试平台,提供简体中文网页、OpenAI Responses 模型调用、批量任务、历史检索及 Word/Excel 报告。后端使用 Python 3.12+、FastAPI、LangGraph,前端使用 Angular,AI 客户端通过 MCP 接入。
|
||||
本仓库是基于 ARTEMIS 扩展的自动化测试平台,提供简体中文网页、Flash/Pro 执行、交互探索、插件跨端操作、精选任务、批量任务及 Word/Excel 报告。后端使用 Python 3.12+、FastAPI、LangGraph,前端使用 Angular;模型统一使用 OpenAI Responses,AI 客户端通过轻量 MCP 桥接用户启动的主程序。
|
||||
|
||||
## Scope
|
||||
|
||||
- 本文件适用于整个仓库;子目录的 AGENTS.md 在对应目录内优先。
|
||||
- 当前任务的用户指令优先于仓库指南。保留用户已有修改,只处理本次任务范围。
|
||||
- 本文件指导开发;使用软件执行 Android 测试时,阅读 [自动化 Skill](skills/artemis-use/SKILL.md)。
|
||||
- 主程序与 `Artemis-Plugins` 是独立仓库。跨仓库任务分别检查分支、未提交改动和远端状态;发布文档或修复时,不夹带已有未发布提交。必要时使用隔离工作树,只移植本次提交。
|
||||
- 本文件指导开发;使用软件执行设备或跨端任务、排查任务时,阅读 [自动化 Skill](skills/artemis-use/SKILL.md)。
|
||||
- 以下是项目原生命令;宿主要求 RTK 等包装器时按宿主规则调用,不把本机路径或工具写成项目安装前提。
|
||||
|
||||
## Read When Needed
|
||||
|
||||
| 修改范围 | 先读 |
|
||||
| --- | --- |
|
||||
| 执行、验收、探索、插件或证据的职责边界 | [DESIGN.md 的架构与行为约定](DESIGN.md#system-architecture) |
|
||||
| 网页布局、样式或交互 | [视觉与交互设计](DESIGN.md#colors)及[前端指南](apps/showcase_ui/AGENTS.md) |
|
||||
| 探索、示范、用例提炼与复跑 | [探索说明](docs/exploration-mode.md) |
|
||||
| 插件协议、适配或市场协同 | [插件开发规范](docs/plugin-development.md)及插件仓库自身的 AGENTS.md、DESIGN.md |
|
||||
| 远程 Python SDK | [SDK 设计](packages/artemis-client/DESIGN.md),保持标准库薄客户端边界 |
|
||||
| 继续历史开发、确认版本或发布 | [迁入时的开发快照](docs/development-handoff.md)、[2.0.1 发行说明](docs/releases/2.0.1.md),再核对实时 Git 状态;`docs/plans/` 保留各自阶段的状态 |
|
||||
|
||||
## Quick Commands
|
||||
|
||||
在仓库根目录执行,前端命令见局部指南:
|
||||
@@ -29,45 +41,39 @@
|
||||
|
||||
Windows 首次安装使用 `start.bat`,国内镜像使用 `start-cn.bat`。启动只提示源码更新;升级使用 `update.bat` / `update-cn.bat`,会终止本项目运行进程。开发中可传入 `-SkipUpdate` 跳过更新提示。
|
||||
|
||||
## Key Paths
|
||||
|
||||
- `artemis/agents/`、`artemis/runtime/`:Flash/Pro 执行、设备与任务运行时。
|
||||
- `artemis/llm/`、`artemis/services/llm.py`:模型请求、超时、重试与恢复。
|
||||
- `artemis/config/`、`.env.example`:运行配置、环境变量及默认值。
|
||||
- `config/artemis.jsonc`、`artemis/resources/config/artemis.jsonc`:开发与打包使用的配置,默认值变更须同步。
|
||||
- `apps/admin_console/`:HTTP API、统一队列、批次、历史及报告服务。
|
||||
- `apps/showcase_ui/`:Angular 网页;遵循 [前端指南](apps/showcase_ui/AGENTS.md)。
|
||||
- `mcp_server/`:MCP 工具与客户端使用规则。
|
||||
- `packages/artemis-client/`:远程 Python SDK;`skills/artemis-use/`:随源码分发的 AI 使用技能。
|
||||
- `scripts/`:安装、镜像、升级、检查及打包脚本。
|
||||
- `tests/unit/`、`tests/tools/`、`packages/artemis-client/tests/`:默认测试范围。
|
||||
|
||||
## Architecture Boundaries
|
||||
|
||||
- 模型传输只使用 OpenAI Responses,不恢复 Gemini 专用调用或 Chat Completions 分支。API 模型名与 Flash/Pro 执行模式分开处理。
|
||||
- 网页和工作区 MCP 工具复用后台配置、队列、历史和报告服务;不要另建调度器或第二套状态存储。
|
||||
- 批次创建与执行分开;保持 request_id 幂等性。同设备串行,失败项不自动重跑;模型重试不能变成业务用例重提。
|
||||
- 区分“执行完成”与“验证通过”。报告结论必须有实际证据,保留未验证、失败、跳过和中断状态。
|
||||
- 网页、MCP 与 SDK 复用后台配置、队列、历史和报告服务;不另建调度器或第二套状态存储。MCP 不自行启动完整后台,主程序离线时仍可握手并明确提示连接状态。
|
||||
- 批次创建与执行分开;保持 `request_id` 幂等性。同设备及共享桌面资源按现有锁串行,任务固定插件版本;失败项不自动重跑,模型重试不能变成业务用例重提,结果不明的写操作不盲目重发。
|
||||
- 最终验收使用独立总预算,协调单次模型超时与重试;不能套用中间检查点的 180 秒时限。重试保留已取得的工具证据,用户停止或探索新指导仍可中断。
|
||||
- 区分执行完成、工具成功和验证通过。正式结论必须经过完整解析与校验,取消或截断的流式内容不能显示为通过;报告、网页、MCP 与人工复验后的有效结论保持一致。
|
||||
- 示范的实际来源贯穿分析、澄清、规划、执行、验收与用例提炼。补充网址不等于更换平台;用户明确要求改端或跨端时再按要求执行,手机证据不能替代电脑证据。
|
||||
- 人工示范遵循窗口/屏幕录制配置。窗口模式跟随前台窗口,无有效窗口时保留缺口,不扩大到多屏桌面;普通录屏在只读最终验收前停止,恢复操作时再追加片段。
|
||||
- 探索主动执行预算耗尽后保留进度并暂停,不直接判失败;探索成功、用例提炼成功和固定版本复跑通过分别记录。收藏保存独立模板与配置,不依赖原任务一直存在。
|
||||
- 历史压缩、视觉摘要与工具上下文调整须保留关键文件回执和来源,维护稳定的多轮前缀;缓存率使用实际用量加权统计,不把缓存输入当作消失的 token,也不承诺固定收益。
|
||||
- 设备动作回放已移除;只读历史步骤、截图和上下文回忆仍保留,不因 replay 命名误删这些能力。
|
||||
- 密钥仅通过网页或环境配置写入,读取配置不得回显;MCP 不接受密钥。全局配置修改影响后续任务。
|
||||
- 默认 high、请求超时 300 秒、失败后重试 5 次、间隔 10 秒,视频分析与录屏关闭。接口不支持思考强度时明确报错,不静默降级。
|
||||
- 密钥仅通过网页或环境配置写入,读取配置不得回显;MCP 不接受密钥。全局配置修改影响后续任务,已保存配置优先于新安装默认值。
|
||||
- 新安装普通任务默认 `gpt-6-luna / high`,探索默认 `gpt-6.1-sol / high`;请求超时 300 秒、失败后重试 5 次、间隔 10 秒,视频分析与普通录屏关闭。接口不支持思考强度时明确报错,不静默降级。
|
||||
- 业务环境恢复由任务提示词提出,不新增默认业务回滚流程;连接、录像、子进程和资源锁的运行时收尾仍然必需。
|
||||
|
||||
## Conventions
|
||||
|
||||
- Python 使用绝对导入、4 空格缩进和双引号,由 Ruff 检查;不要放宽质量基线来掩盖新增问题。
|
||||
- 新增可调参数集中进入配置层,并更新环境示例、网页/MCP 入口和必要文档,避免散落硬编码。
|
||||
- 新增可调参数集中进入配置层,并更新环境示例、网页/MCP 入口和必要文档;默认值同步 `config/artemis.jsonc` 与 `artemis/resources/config/artemis.jsonc`。
|
||||
- MCP 工具增删时同步工具注册、CLI 安装白名单、测试、Skill 和能力文档。
|
||||
- 同步 Codex MCP/Skill 时保留已有工具白名单、禁用状态和审批策略;异常时保留原配置并告警,不生成非法 TOML 或阻断网页启动。
|
||||
- Windows PowerShell 启动脚本保持兼容 Windows PowerShell 5.1,避免编码导致解析失败。升级只做 Git 快进,保护已有配置和本地修改。
|
||||
- `update*.bat` 保持精简的 ASCII 入口;升级程序及其辅助脚本先复制到项目外执行。更新后通过新版 `start.ps1 -PrepareOnly -SkipUpdate` 同步环境,保留此内部调用约定,避免继续运行旧版安装函数。
|
||||
- 前端遵循现有简体中文文案与视觉样式;不直接编辑构建产物。
|
||||
- 前端遵循 DESIGN.md 与局部指南,复用现有组件和简体中文文案;不直接编辑构建产物。页面仍显示旧效果时,先核对实际服务的源码目录及构建来源。
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- Python 行为改动运行默认测试、Ruff 两项检查、质量基线和核心类型检查;前端改动遵循局部指南的测试与构建要求。
|
||||
- 文档改动核对命令、相对链接和功能说明;仅文档变更无需启动设备或调用付费模型。
|
||||
- 文档改动核对命令、相对链接和功能说明;历史测试数量与未测范围见对应版本记录,不能冒充本次验证。仅文档变更无需启动设备或调用付费模型。
|
||||
- 默认测试不依赖设备、密钥或私有服务;外部测试按现有 android、integration、e2e、cloud、manual 标记分层,不默认执行全量外部测试。
|
||||
- 设备验证沿用用户指定设备;多设备且未指定时先确认。编写应用交互测试前,先通过 ARTEMIS 探索实际页面和操作路径。
|
||||
- 提交前检查 `git diff --check` 和暂存范围;报告实际执行的验证及未完成项,不将未运行的检查写成通过。
|
||||
- 提交前检查 `git diff --check` 和暂存范围,只暂存本次具体路径;报告实际执行的验证及未完成项,不将未运行的检查写成通过。
|
||||
|
||||
## Boundaries
|
||||
|
||||
@@ -79,5 +85,6 @@ Windows 首次安装使用 `start.bat`,国内镜像使用 `start-cn.bat`。启
|
||||
## References
|
||||
|
||||
- [用户入口](README.md):安装、配置与日常使用。
|
||||
- [设计约定](DESIGN.md):当前架构、行为与网页视觉规则。
|
||||
- [AI 自动化](docs/ai-automation.md):MCP、Skill 及升级行为。
|
||||
- [工作区说明](docs/automation-workspace.md):报告、批量导入和历史检索。
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
name: ARTEMIS 控制台
|
||||
colors:
|
||||
canvas: "#f8f9fa"
|
||||
surface: "#ffffff"
|
||||
text: "#1e293b"
|
||||
text-strong: "#0f172a"
|
||||
text-secondary: "#475569"
|
||||
text-muted: "#64748b"
|
||||
text-control: "#334155"
|
||||
border: "#e2e8f0"
|
||||
border-control: "#dbe3ec"
|
||||
primary: "#1a73e8"
|
||||
primary-hover: "#1557b0"
|
||||
primary-soft: "#eff6ff"
|
||||
exploration-action: "#4f46e5"
|
||||
exploration-soft: "#eef2ff"
|
||||
focus: "#a5b4fc"
|
||||
success: "#047857"
|
||||
success-soft: "#ecfdf5"
|
||||
warning: "#92400e"
|
||||
warning-soft: "#fffbeb"
|
||||
error: "#b91c1c"
|
||||
error-soft: "#fef2f2"
|
||||
rounded:
|
||||
compact: 8px
|
||||
control: 10px
|
||||
card: 16px
|
||||
composer: 22px
|
||||
spacing:
|
||||
tight: 4px
|
||||
icon: 6px
|
||||
small: 8px
|
||||
group: 12px
|
||||
section: 16px
|
||||
workspace: 24px
|
||||
components:
|
||||
model-selector:
|
||||
backgroundColor: "{colors.surface}"
|
||||
textColor: "{colors.text-control}"
|
||||
rounded: "{rounded.control}"
|
||||
demonstration-control:
|
||||
height: 36px
|
||||
exploration-composer:
|
||||
backgroundColor: "{colors.surface}"
|
||||
rounded: "{rounded.composer}"
|
||||
---
|
||||
|
||||
# ARTEMIS 设计与交互约定
|
||||
|
||||
## Overview
|
||||
|
||||
ARTEMIS 面向使用自然语言执行、探索和复跑测试的人。当前产品包含 Flash、Pro、探索三种入口,按需调用安卓设备和电脑插件,提供批次、精选任务、历史、报告及证据。用户应能看懂当前在做什么、何时需要回答、哪些结果已有证据。
|
||||
|
||||
本文记录主程序的职责边界及 `apps/showcase_ui` 的现有视觉与交互规则,不提出新的主题改版。远程 SDK 的协议边界见[局部设计](packages/artemis-client/DESIGN.md);独立插件仓库维护适配器设计和构建规则。阶段性进度见[开发快照](docs/development-handoff.md),发行状态见[2.0.1 说明](docs/releases/2.0.1.md)。
|
||||
|
||||
## System architecture
|
||||
|
||||
| 层 | 职责与源码入口 |
|
||||
| --- | --- |
|
||||
| 网页、MCP、远程 SDK | 请求建模、交互与结果读取。`apps/showcase_ui`、`mcp_server`、`packages/artemis-client` 共同使用后台服务;MCP 和薄 SDK 不另启完整后台 |
|
||||
| HTTP 与业务服务 | `apps/admin_console` 管理配置、统一队列、批次、收藏、历史、人工复验和导出;`TaskQueueService` 及现有资源锁负责执行准入与互斥 |
|
||||
| 任务与模型 | `artemis/agents`、`artemis/runtime` 执行 Flash/Pro;`artemis/exploration` 管理交流、预算、示范与用例版本;`artemis/llm`、`artemis/services/llm.py` 统一 Responses 请求 |
|
||||
| 插件与证据 | `artemis/plugins` 管理安装、会话、版本固定、来源、截图、录像和附件;插件执行工具动作,宿主管理任务生命周期和最终验收 |
|
||||
| 配置与交付 | `artemis/config`、两份 `artemis.jsonc` 和环境配置提供默认值;`scripts` 负责启动、镜像、独立升级和源码打包 |
|
||||
|
||||
请求重试、业务执行与最终验收分别处理。提交使用稳定标识;创建批次或探索草稿不自动执行,查询不产生设备动作。执行准入和恢复遵守现有资源锁,任务固定所用插件版本,不能通过另建队列绕开互斥。
|
||||
|
||||
最终验收有独立总预算,按请求超时、重试和剩余任务时间协调,保留取证结果;完整有效的结构化结论才更新正式裁定。`completed`、工具成功、探索通过、用例提炼和复跑通过各自表达自己的状态。
|
||||
|
||||
示范来源保存在元数据中,贯穿分析、提问、规划、执行、验收和用例生成。补充网址不会隐式把电脑示范改到安卓;用户明确改端或提出跨端步骤时按其要求执行。证据必须对应真实来源、窗口、采集时间与操作。
|
||||
|
||||
录像使用用户选择的范围。人工示范的窗口模式跟随前台窗口,仅保留一路,无有效窗口时保留缺口;普通录屏在只读验收前停止。恢复操作再追加片段,时间映射以实际录像为准,不借用另一来源或时间的画面。
|
||||
|
||||
收藏保存独立模板与执行配置,用例复跑固定版本;删除原任务不破坏已保存模板。历史与导出的有效结论合并人工复验,不能用新裁定改写旧证据。
|
||||
|
||||
普通任务新安装默认 `gpt-6-luna / high`,探索单独使用 `gpt-6.1-sol / high`,保留已有配置。上下文参照上限 300k、默认历史压缩预算 80k,均不表示整条任务累计 token 限额。优化稳定前缀时保留关键文件回执、路径、校验和来源;费用收益依实际网关用量及计价判断。
|
||||
|
||||
## Colors
|
||||
|
||||
整体使用浅灰画布、白色内容面和深色正文。首页主要操作使用蓝色,探索发送及相关主操作保留现有靛色;绿色、琥珀色、红色用于结果与需要处理的状态。新控件复用所在区域的语义色。
|
||||
|
||||
| 用途 | 值与来源 |
|
||||
| --- | --- |
|
||||
| 画布 / 正文 | `#f8f9fa` / `#1e293b`,来自[全局样式](apps/showcase_ui/src/styles.scss) |
|
||||
| 首页操作 / 悬停 | `#1a73e8` / `#1557b0`,来自[首页样式](apps/showcase_ui/src/app/pages/home/home.component.scss) |
|
||||
| 探索操作 / 浅色状态面 | `#4f46e5` / `#eef2ff`,来自[探索面板](apps/showcase_ui/src/app/components/exploration-panel/exploration-panel.component.scss) |
|
||||
| 正文层级 / 边框 | 强调 `#0f172a`、次级 `#475569`、辅助 `#64748b`;常规边框 `#e2e8f0` |
|
||||
| 成功、等待、错误 | 深色状态文字配对应浅色面;同时给出文字、图标或说明,结论不只依靠颜色 |
|
||||
|
||||
加载、空状态、断开、未验证与失败使用明确文案。执行完成可以有完成标识,测试裁定仍需独立展示;不能让绿色完成态替代测试通过结论。
|
||||
|
||||
## Typography
|
||||
|
||||
沿用全局字体栈:`Google Sans`、`Google Sans Text`、`Inter`、系统无衬线字体;中文使用系统可用字形。代码、路径及请求诊断使用现有等宽样式,保留换行与复制能力。
|
||||
|
||||
模型控件正文为 13px,探索输入正文为 14px,用例标题为 18px;辅助信息依现有组件使用 11–12px。保留字号、字重、留白形成的层次,不通过大段加粗堆叠状态。Markdown、表格、长链接和中文长标题应换行,不撑破容器。
|
||||
|
||||
图标沿用 Material Symbols Outlined,常规全局尺寸 20px;组件局部按原有尺寸调整。图标与相邻文字垂直居中,独立图标按钮提供可访问名称。
|
||||
|
||||
## Layout
|
||||
|
||||
- 首页负责创建任务与访问精选模板;工作区主区域展示当前任务的交流、执行步骤和结果,底部输入框始终服务当前任务。
|
||||
- 编辑、模型设置和复跑参数按需展开,默认保持任务时间线可读。精选卡片在收起状态下等高,长标题、不同状态和展开内容不会遮挡操作入口。
|
||||
- 通常以 4、6、8、12、16、24px 组织内部间距;沿用组件现有网格和断点。子容器允许收缩,长内容使用 `min-width: 0` 及合适换行。
|
||||
- 探索面板在 600px 以下收紧外边距并允许操作换行;模型选择控件宽度跟随容器。窄屏仍可辨认状态、选择来源和发送指导。
|
||||
- 列表后台刷新保留滚动、选择和折叠状态;首次加载与静默更新分开,避免周期性加载提示挤动内容。实时连接复用现有页面可见性管理。
|
||||
|
||||
## Elevation
|
||||
|
||||
卡片使用细边框和克制的阴影区分区域。首页现有玻璃效果、渐变和按钮悬停保留在原有区域,正文、报告、表单和选项保持清晰背景及文字对比。
|
||||
|
||||
探索输入区沿用 `0 6px 24px #0f172a08` 的阴影;焦点反馈沿用所在控件的轮廓和浅色描边。全局 CDK overlay 层级为 1200,新增弹层先使用既有 overlay 机制,不任意叠加更大的层级。
|
||||
|
||||
## Shapes
|
||||
|
||||
模型输入与选择框圆角 10px;紧凑控件常用 8px,内容卡片按组件使用 12–16px,探索输入区为 22px。保留现有徽标、标签与胶囊形控件的局部形状;这些值不要求把全部历史控件改成同一圆角。
|
||||
|
||||
## Components
|
||||
|
||||
| 组件或状态 | 必须维持的交互与尺寸 | 实现依据 |
|
||||
| --- | --- | --- |
|
||||
| 模型选择 | 预设选择加自定义输入;主模型、兜底和精选任务复用同一组件。最小高度 40px、内边距 10px 12px、圆角 10px,保留焦点与禁用态 | [model-selector](apps/showcase_ui/src/app/components/model-selector/model-selector.component.ts) |
|
||||
| 人工示范控件 | 来源标签与控件水平对齐,选择框及开始按钮均为 36px 高、垂直居中;窄屏可换行。准备、录制、分析分别显示 | [exploration-panel](apps/showcase_ui/src/app/components/exploration-panel/exploration-panel.component.scss) |
|
||||
| 探索建议 | 推荐内容为用户口吻的可编辑回复;点击只填草稿,用户发送后才继续任务 | [探索组件](apps/showcase_ui/src/app/components/exploration-panel/exploration-panel.component.ts) |
|
||||
| 历史标题与星标 | 星标只占标题,第二行状态与编号正常左对齐;状态徽标沿用 20px 高及统一内边距 | [task-history](apps/showcase_ui/src/app/components/task-history/task-history.component.ts) |
|
||||
| 验收状态 | 展示准备证据、核对、生成与校验等阶段及可用的耗时、预算、重试诊断;未完成的流式文字不变为正式通过标签 | [agent-stream](apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html) |
|
||||
| 截图、录像与文件 | 显示真实来源和缺口;按时间定位并保留跳转前的播放/暂停状态,文件成功以真实保存回执为准 | [工作区说明](docs/automation-workspace.md) |
|
||||
| 保存和错误反馈 | 配置加载、保存、空值、失败与密钥已配置状态有明确提示;空密钥保留已有值,连接测试与保存分开 | [模型配置](apps/showcase_ui/src/app/components/model-settings/model-settings.component.ts) |
|
||||
|
||||
这些规则对应已接受的现有行为。改动后结合实际层叠样式、渲染尺寸和相关状态检查,不能只核对一处声明。
|
||||
|
||||
## Do's and Don'ts
|
||||
|
||||
- 复用现有组件、API 类型、中文文案和状态语义。新增状态先说明它与执行、验收或人工操作的关系。
|
||||
- 保持实际来源与证据一致,缺失时说明原因;不使用手机画面替代电脑操作,也不把工具成功当作业务通过。
|
||||
- 保留可见焦点、禁用反馈和减少动画偏好;录制红点表示实际录制,等待或执行中使用各自文案。
|
||||
- 源码已更新而页面仍旧时,先核对服务目录、构建与缓存;不通过反复增加样式覆盖掩盖旧构建问题。
|
||||
- 视觉变化验证代表性宽度、长标题、加载、等待回答、错误与展开状态;文档格式检查不等于渲染验证。开发检查按[前端指南](apps/showcase_ui/AGENTS.md)执行。
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
基于 [ARTEMIS](https://github.com/google/artemis) 扩展的 Android AI 自动化测试平台。用自然语言描述任务,即可通过简体中文网页或 MCP 驱动真机、模拟器执行操作并收集结果。模型统一使用 **OpenAI Responses API**。
|
||||
|
||||
**2.0.0 已发布**:新增交互探索、插件扩展和精选任务,增强跨端证据与 AI 接入。完整变化及升级步骤见 [2.0.0 发行说明](docs/releases/2.0.0.md)。
|
||||
**2.0.1 已发布**:修复 Python 3.12 全新安装时网页服务无法启动的问题,并统一不同 Python 版本的 MCP 工具描述。修复与升级步骤见 [2.0.1 发行说明](docs/releases/2.0.1.md);探索、插件和精选任务等功能见 [2.0.0 发行说明](docs/releases/2.0.0.md)。
|
||||
|
||||
## 主要功能
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Independent favorite snapshots dispatched through the existing task services."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import deepcopy
|
||||
import asyncio
|
||||
import hashlib
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
## Local Purpose
|
||||
|
||||
本目录维护简体中文 Angular 网页,包括设备与任务操作、模型配置、报告、批量导入和历史检索。
|
||||
本目录维护简体中文 Angular 网页,包括 Flash/Pro/探索、人工示范、插件、精选任务、模型配置、批次、历史、报告与跨端证据。
|
||||
|
||||
## Inherits
|
||||
|
||||
继承仓库根目录 AGENTS.md;本文件仅补充前端规则,当前用户指令优先。
|
||||
|
||||
修改 UI 前阅读根目录 [DESIGN.md](../../DESIGN.md) 的视觉与交互约定;本应用没有独立的局部 DESIGN.md。SDK 的同名文件描述远程协议,不是网页设计来源。
|
||||
|
||||
## Local Commands
|
||||
|
||||
在本目录执行,包管理器使用 npm 和现有 package-lock.json:
|
||||
@@ -34,6 +36,11 @@ npm run build
|
||||
- 使用相邻的 .spec.ts 测试交互和请求数据;不要只测试实现细节。
|
||||
- 后端字段调整需同时核对类型、请求和响应;设备序列号、报告格式、批次编号等不能在前端猜测。
|
||||
- UI 有明显变化时在浏览器验证实际布局和关键操作,构建通过不能代替可读性检查。
|
||||
- 工作区底部输入框服务当前任务;探索建议只填入草稿,用户发送后再提交。用例编辑和配置按需展开,复跑固定保存版本。
|
||||
- 主模型、兜底模型及精选任务复用现有模型选择组件;收藏使用已保存的独立配置,设备离线时给出可操作的选择提示。
|
||||
- 星标仅占标题区域,后续状态行正常左对齐;人工示范来源与按钮保持等高、居中,窄屏可换行。执行、等待回答、示范准备/录制/分析、最终验收及未验证各有明确文字。
|
||||
- 电脑步骤使用电脑证据,录像缺口明确展示。播放器按真实来源和时间定位,跳转保留原播放/暂停状态。
|
||||
- 列表后台刷新保留选中项、滚动及折叠状态,不用反复插入的加载行挤动布局;实时连接沿用现有共享机制和页面可见性管理。
|
||||
|
||||
## Local Risks
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "artemis-showcase-ui",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "artemis-showcase-ui",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"dependencies": {
|
||||
"@angular/cdk": "^22.1.5",
|
||||
"@angular/common": "^22.1.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "artemis-showcase-ui",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve --proxy-config proxy.conf.json",
|
||||
|
||||
@@ -145,16 +145,16 @@ class ReadinessEngine:
|
||||
device check.
|
||||
"""
|
||||
cacheable = categories is None
|
||||
request_started = time.monotonic()
|
||||
cache_at_request = self._report_cache
|
||||
if cacheable and not force_refresh:
|
||||
cached = self._cached_report(self._REPORT_CACHE_TTL_SECONDS)
|
||||
if cached is not None:
|
||||
return cached
|
||||
|
||||
async with self._report_lock:
|
||||
# A refresh that completed while this caller waited satisfies even a
|
||||
# forced request that began before it, coalescing concurrent clicks.
|
||||
if cacheable and self._report_cache_time >= request_started:
|
||||
# A replacement report completed while this caller waited. Compare
|
||||
# the snapshot itself: sequential requests can share a clock tick.
|
||||
if cacheable and self._report_cache is not cache_at_request:
|
||||
cached = self._cached_report(float("inf"))
|
||||
if cached is not None:
|
||||
return cached
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Durable manual-demonstration commands, sharing the automation transaction."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ The queue remains the sole scheduler. These records describe a conversation and
|
||||
its immutable evidence/case versions, not a second execution state machine.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from contextlib import contextmanager
|
||||
import hashlib
|
||||
|
||||
@@ -27,7 +27,9 @@ if _REPO_ROOT not in sys.path:
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
from adbutils import AdbClient
|
||||
from mcp.server.fastmcp import Context, FastMCP
|
||||
from mcp.server.fastmcp import Context
|
||||
|
||||
from artemis.mcp.tool_server import ArtemisFastMCP
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -86,7 +88,7 @@ def configure_stdio_mode() -> None:
|
||||
|
||||
|
||||
# Create minimal MCP server
|
||||
mcp = FastMCP("Android_ADB_Controller")
|
||||
mcp = ArtemisFastMCP("Android_ADB_Controller")
|
||||
|
||||
_GLOBAL_CONTROLLER = None
|
||||
_CONTROLLERS: dict[str, Any] = {}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Keep ARTEMIS tool descriptions stable across supported Python versions."""
|
||||
|
||||
from textwrap import dedent
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
|
||||
class ArtemisFastMCP(FastMCP):
|
||||
"""Normalize docstring indentation before FastMCP publishes tool metadata."""
|
||||
|
||||
def add_tool(
|
||||
self,
|
||||
fn,
|
||||
name=None,
|
||||
title=None,
|
||||
description=None,
|
||||
annotations=None,
|
||||
icons=None,
|
||||
meta=None,
|
||||
structured_output=None,
|
||||
):
|
||||
if not description:
|
||||
# Python 3.13+ removes this indentation at compilation; 3.12 leaves
|
||||
# it in __doc__. Retain blank lines, including the final newline,
|
||||
# so the existing catalog and pinned schemas stay byte-for-byte equal.
|
||||
first, separator, body = (fn.__doc__ or "").expandtabs().partition("\n")
|
||||
description = first.lstrip() + separator + dedent(body)
|
||||
super().add_tool(
|
||||
fn,
|
||||
name=name,
|
||||
title=title,
|
||||
description=description,
|
||||
annotations=annotations,
|
||||
icons=icons,
|
||||
meta=meta,
|
||||
structured_output=structured_output,
|
||||
)
|
||||
@@ -0,0 +1,146 @@
|
||||
# 开发交接:2026-10-19
|
||||
|
||||
本文件保留 2026-10-19 从《Artemis开发主线-1017-2》迁入时的开发快照。历史结论已结合代码、发行说明和当时的本地 Git 引用核对;后续开发以[仓库指南](../AGENTS.md)、[设计约定](../DESIGN.md)和实时 Git 状态为准,以下提交号与分叉计数不随后续提交自动更新。
|
||||
|
||||
**迁入时插件仓库有本地改动及分叉,不能用该工作目录代表已发布版本。** 后续发布应明确本次范围,保留尚未归并的开发现场。
|
||||
|
||||
## 当前仓库与版本
|
||||
|
||||
两个项目是工作区中的独立 Git 仓库:主程序目录 `artemis`,相邻插件目录 `Artemis-Plugins`。后续分别检查各自的分支、改动和验证结果。
|
||||
|
||||
| 项目 | 本次核实的基线 | 工作区现场 |
|
||||
| --- | --- | --- |
|
||||
| 主程序 | `main`、本地 `eapil/main`、`v2.0.0` 标签解引用均为 `973ccae` | 已跟踪文件无修改;根目录存在未跟踪的 `.angular/` 缓存,不纳入提交 |
|
||||
| 插件 | 本地 `main` 为 `1eb77e6`,本地 `origin/main` 为 `cf110b7`;本地分支领先 3 个、落后 6 个提交 | 有 OCU 修改、未跟踪的 Cua 适配源码及构建、滚动、基准测试文件,全部保留 |
|
||||
|
||||
以上远端分支名称指本地已有的跟踪引用,本次没有联网刷新。后续修改前需重新读取 Git 状态。
|
||||
|
||||
主程序 2.0.0 曾重新发布:首次合并提交为 `f4cb55c`,当前应以包含后续修复的 **`973ccae`** 为准。完整说明见 [2.0.0 发行说明](releases/2.0.0.md)。
|
||||
|
||||
插件存在三种不同状态,后续必须明确区分:
|
||||
|
||||
- 本地 `catalog.json` 仍列出 OCU `1.0.0`,本地 Cua 文档仍描述 `0.1.2` 候选。
|
||||
- 本地 `origin/main` 的市场索引已改为 **Cua `0.36.0+artemis.4`**;OCU 已从市场入口下架,旧安装和历史保留。
|
||||
- 用户机器实际安装的插件版本需另行查询,不能由仓库版本推定。
|
||||
|
||||
## 已完成的开发主线
|
||||
|
||||
| 范围 | 当前能力与约定 | 主要说明 |
|
||||
| --- | --- | --- |
|
||||
| 中文与模型调用 | 简体中文网页;模型传输统一 OpenAI Responses,移除 Gemini 原生分支;支持视觉、工具调用、流式及结构化结果 | [用户入口](../README.md) |
|
||||
| 可保存配置 | 网页保存接口、模型、思考强度、超时重试和功能开关;保留已有配置,密钥查询只返回配置状态 | [配置示例](../.env.example) |
|
||||
| 批量、历史与报告 | Excel 模板、校验与预览、按用例配置、循环与选中执行、暂停恢复;历史搜索分页、人工复验;Word、Excel、ZIP 与证据导出 | [工作区说明](automation-workspace.md) |
|
||||
| 探索 | 持续交流、补充指导、人工示范、预算暂停继续;成功后提炼一个可编辑版本的用例,供 Flash/Pro 复跑或加入批次 | [探索说明](exploration-mode.md) |
|
||||
| 插件与跨端证据 | 安装、授权、启用插件;统一资源调度、步骤来源、截图、录像、文件附件与证据索引 | [插件说明](plugins.md)、[开发规范](plugin-development.md) |
|
||||
| 精选任务 | 收藏任务、探索用例和批次;独立保存模板与执行配置,首页复跑;旧收藏按实际记录补全配置 | [2.0.0 发行说明](releases/2.0.0.md) |
|
||||
| MCP 与 Skill | 轻量连接用户已启动的主程序;覆盖执行、探索、收藏、批次、诊断与导出;启动时同步官方 Skill,并保留客户端工具策略 | [AI 接入](ai-automation.md)、[随仓库分发的 Skill](../skills/artemis-use/SKILL.md) |
|
||||
| 性能与诊断 | 稳定多轮上下文与缓存前缀,减少重复摘要和无关手机截图;请求阶段与实际用量可追踪;复用未变化的终态资料包 | [2.0.0 发行说明](releases/2.0.0.md) |
|
||||
| 安装与升级 | 官方源/国内镜像启动,独立快进升级,保护配置与历史,使用源码打包脚本 | [仓库指南](../AGENTS.md) |
|
||||
|
||||
设备动作回放曾在早期实现,随后已按用户要求移除。历史步骤、截图、只读回看与上下文回忆仍保留;不能因文件中出现 `replay` 就把这些能力当作待删代码,也不恢复旧动作回放方案。
|
||||
|
||||
## 最近修复及其原因
|
||||
|
||||
- **最终验收与请求预算冲突**:原先整段验收在 180 秒时取消,比单次请求 300 秒还短,使请求重试没有机会发生。现在最终验收有独立总预算,协调模型超时与重试,保留已有工具证据;只重试失败的模型请求,不重复设备业务动作。任务本身的执行时限仍有效。
|
||||
- **完整结论后才显示通过**:有效结构化结果直接校验,减少重复模型格式转换;取消或截断的流式内容会丢弃。页面与下载诊断区分验收阶段、请求重试耗尽、总预算耗尽和用户中断。
|
||||
- **电脑示范错误转到安卓执行**:实际示范来源贯穿分析、澄清、规划、执行、验收和用例提炼;规划同时获得可用插件信息。补充网址不等于更换平台,手机成功不能代替电脑验收。
|
||||
- **窗口示范错误录制双屏**:人工示范遵循窗口/屏幕配置;窗口模式跟随当前前台窗口,只保留一路录像。无有效窗口时保留缺口,不扩大到整个桌面。
|
||||
- **探索建议与布局**:建议使用用户口吻,点击先填入草稿,发送后才继续;星标只占标题位置,示范来源与按钮等高对齐。
|
||||
|
||||
历史上多次出现服务仍从旧工作树加载构建的问题。遇到“源码已改但页面没变”,先核对服务的实际源码目录和构建来源。本次确认当前虚拟环境中的 `artemis` 解析到主仓库;未检查或重启正在运行的服务。
|
||||
|
||||
## 默认设置与持续约束
|
||||
|
||||
| 设置 | 新安装默认值或上限 |
|
||||
| --- | --- |
|
||||
| 普通任务模型 / 思考强度 | `gpt-6-luna / high` |
|
||||
| 探索模型 / 思考强度 | `gpt-6.1-sol / high`,单独保存 |
|
||||
| 单次模型请求 | 300 秒;失败后最多重试 5 次,间隔 10 秒 |
|
||||
| 视频分析 / 普通任务录屏 | 默认关闭;人工示范录制独立管理 |
|
||||
| 探索主动执行预算 | 每段 1800 秒;等待回答和人工示范不计入,耗尽后暂停,可继续 |
|
||||
| 人工示范时长 | 默认上限 900 秒,可延长;超时保存并暂停 |
|
||||
| 上下文参照上限 / 默认历史压缩预算 | 300k / 80k token;不是整条任务累计用量限制 |
|
||||
| 高效录像留档 | 默认长边 1280、15 fps;编码与回退遵循现有配置 |
|
||||
|
||||
已保存配置优先,不能把这些默认值强制写回用户环境。旧模型已从预设移除,仍可自定义输入;接口不支持指定思考强度时明确报错。
|
||||
|
||||
后续实现持续遵守:
|
||||
|
||||
- 网页、MCP、SDK 复用后台配置、队列、历史与报告服务;不另建业务调度器或第二套状态存储。MCP 连接用户启动的主程序。
|
||||
- 创建批次与执行分开,提交保持 `request_id` 幂等;同设备和共享桌面资源按现有锁串行,任务固定插件版本。
|
||||
- 模型请求重试不变成业务用例重提;结果未知的写操作先核对,不盲目重发。
|
||||
- 执行结束、工具成功、独立业务验证、AI 最终裁定分别记录。缺少电脑证据、报告或关键检查项时不能直接判通过,也不改写旧任务结论来代替复测。
|
||||
- 示范和截图保留实际来源;控件、截图编号、窗口几何或 DPI 变化后重新观察。录屏质量设置不降低模型定位截图质量。
|
||||
- 只读最终验收前停止普通录屏;无录像时段明确保留缺口,历史丢失的画面不能补造。
|
||||
- 业务环境恢复由任务提示词提出;不新增默认业务回滚系统。连接关闭、录制收尾、进程回收和资源锁释放仍由运行时负责。
|
||||
- 密钥不进入 MCP 参数、查询结果、日志或报告。保护用户配置、历史与运行现场;新代码不包含个人设备号、绝对目录或凭据。
|
||||
- 变更配置默认值时同步开发和打包配置;变更 MCP 工具时同步注册、安装白名单、测试、Skill 和能力文档。
|
||||
|
||||
缓存命中率按 API 实际用量加权统计,不能承诺固定命中率或把缓存输入当作消失的 token。历史公告中的“约 81%”是条件计算:假设输入命中率 90%、缓存输入单价为普通输入的 10%,输入费用可下降约 81%;不是总 token 或总账单的固定降幅。Cua 小样本中的约 26.1% token 差异也不能外推为普遍收益。
|
||||
|
||||
## 协同开发入口
|
||||
|
||||
| 修改目标 | 主仓库入口 |
|
||||
| --- | --- |
|
||||
| 模型传输、超时、重试与请求诊断 | `artemis/llm/`、`artemis/services/llm.py`、`apps/admin_console/services/request_diagnostics.py` |
|
||||
| 默认值与配置保存 | `artemis/config/`、`config/artemis.jsonc`、`artemis/resources/config/artemis.jsonc`、`apps/admin_console/services/model_settings_service.py` |
|
||||
| 探索、示范与用例提炼 | `artemis/exploration/`、`apps/admin_console/services/exploration_service.py` |
|
||||
| 插件运行、截图、录像与附件 | `artemis/plugins/`、`apps/admin_console/routers/plugins.py` |
|
||||
| 队列、批次、收藏、报告 | `apps/admin_console/services/`,优先复用现有服务 |
|
||||
| 网页 | `apps/showcase_ui/`,先读[局部指南](../apps/showcase_ui/AGENTS.md) |
|
||||
| MCP、远程 SDK、AI 使用规则 | `mcp_server/`、`packages/artemis-client/`、`skills/artemis-use/` |
|
||||
| 插件适配包与市场 | 插件仓库的 `plugins/`、`scripts/`、`tests/`、`catalog.json`;先明确比较的是本地现场还是发布引用 |
|
||||
|
||||
下一次修改插件前,先比较分叉提交及全部本地改动,再按具体任务选择保留、迁移和合并范围。可在插件目录只读查看:
|
||||
|
||||
```powershell
|
||||
rtk proxy git status --short --branch
|
||||
rtk proxy git log --left-right --oneline main...origin/main
|
||||
rtk proxy git diff --stat
|
||||
rtk proxy git show origin/main:plugins/cua-driver/RELEASE.md
|
||||
```
|
||||
|
||||
## 验证基线与仍需实测的范围
|
||||
|
||||
以下是**原开发记录及重新发行说明保存的结果,本次没有重跑**:后端默认测试 2943 项通过、2 项跳过、8 项按标记排除;前端 290 项通过,生产构建、Ruff 两项、质量基线与核心类型检查通过。最近的示范来源和录制范围修复未调用付费模型完整复跑业务任务。
|
||||
|
||||
后续不能据此宣称以下范围已全部验证:
|
||||
|
||||
- 用户原先失败的完整手机/电脑业务流程,以及最近修复后的真实示范、恢复、执行与验收闭环。
|
||||
- 多 DPI、多显示器、复杂控制台、跨端长任务、长时间资源开销及并发压力。
|
||||
- 安卓与电脑同时示范的完整组合、真实 15 分钟示范上限,以及外部 Agent 经安装后的 MCP 完整操作链。
|
||||
- 网关缓存路由、辅助摘要调用的缓存收益和稳定费用降幅,需同任务实测及请求级记录支持。
|
||||
|
||||
Cua 仍为 Windows x64 预览。前台操作会占用用户桌面;PNG 保存成功不等于有效观察,少量通过任务也不证明所有浏览器、窗口或业务均可靠。
|
||||
|
||||
旧开发工作树和 `artifacts/worktree-archive` 已按用户要求删除,本次确认目录不存在。继续使用当前主目录及相邻插件目录,不重建旧归档作为开发前提。
|
||||
|
||||
涉及 Python 行为修改时,在主仓库执行规定检查:
|
||||
|
||||
```powershell
|
||||
rtk proxy uv run pytest
|
||||
rtk proxy uv run ruff format --check .
|
||||
rtk proxy uv run ruff check .
|
||||
rtk proxy uv run python scripts/quality_ratchet.py
|
||||
rtk proxy uv run pyright --project pyright-core.json
|
||||
rtk proxy git diff --check
|
||||
```
|
||||
|
||||
前端修改在 `apps/showcase_ui` 执行,并验证实际布局与关键交互:
|
||||
|
||||
```powershell
|
||||
rtk proxy npm test -- --watch=false --browsers=ChromeHeadless
|
||||
rtk proxy npm run build
|
||||
```
|
||||
|
||||
本机外部命令沿用 RTK 包装;PowerShell cmdlet 和控制流不套 RTK。默认测试不调用设备、私有服务或付费模型。实际应用交互先通过 ARTEMIS 探索,设备沿用用户指定对象。
|
||||
|
||||
## 本次交接的核对依据
|
||||
|
||||
| 证据 | 结论 | 后续使用方式 |
|
||||
| --- | --- | --- |
|
||||
| 用户迁入记录、[发行说明](releases/2.0.0.md)、主仓库 Git 日志及标签解引用 | 重发后的 2.0.0 基线为 `973ccae`,早期“未提交/未发布”状态已被后续记录覆盖 | 从当前主目录继续开发,按新需求修改 |
|
||||
| 插件仓库 `git status`、分叉日志及 `git show origin/main:catalog.json` | 本地 OCU/Cua 候选现场与发布的 Cua `.4` 不一致 | 保留现场,先比较再处理分叉,不把旧候选文档当作最新市场状态 |
|
||||
| 当前源码定位、配置默认值、[仓库指南](../AGENTS.md)与[前端指南](../apps/showcase_ui/AGENTS.md) | 架构边界、入口与验证命令可继续沿用 | 新任务先读对应局部规则,按变更范围验证 |
|
||||
|
||||
`docs/plans/` 是阶段性设计记录,其中“未发布”“待验证”等文字保留各自时点含义;当前发布状态以发行说明、对应版本源码及实际验证记录为准。
|
||||
@@ -0,0 +1,27 @@
|
||||
# ARTEMIS-Eapil 2.0.1
|
||||
|
||||
2.0.1 修复 Windows 全新安装使用 Python 3.12 时,网页构建成功后管理服务立即退出的问题。已有 2.0.0 安装也建议升级;探索、插件和精选任务等功能继续沿用 [2.0.0](2.0.0.md)。
|
||||
|
||||
## 修复内容
|
||||
|
||||
- **全新安装可以启动网页服务**:探索存储、人工示范和收藏服务中的 `list` 方法遮蔽了同名内置类型,导致 Python 3.12 在加载后续类型注解时抛出 `TypeError: 'function' object is not subscriptable`。统一延迟求值,并增加冷启动与运行时类型注解回归,覆盖较新 Python 延迟暴露同一问题的情况。
|
||||
- **诊断强制刷新按请求生效**:连续请求落在同一个时钟刻度时,仍会重新扫描;并发请求继续合并,避免重复检查。
|
||||
- **MCP 工具描述跨版本一致**:统一 Python 3.12 与较新版本编译后文档字符串的缩进差异,保持工具目录、固定协议快照及现有显式描述一致。
|
||||
|
||||
本版不要求升级 Python,也不更改已有配置、任务历史或插件版本。
|
||||
|
||||
## 升级与安装
|
||||
|
||||
1. 先结束正在运行的任务,再运行 `update.bat`;国内网络使用 `update-cn.bat`。升级程序会停止当前项目的服务与任务进程,并同步依赖环境。
|
||||
2. 完成后运行 `start.bat` / `start-cn.bat`,等待网页构建并打开页面。启动脚本只检查更新,不代替升级脚本。
|
||||
3. 全新安装下载 `artemis-eapil-2.0.1-source.zip`,解压后运行上述启动脚本。用附件 `SHA256SUMS.txt` 校验下载完整性。
|
||||
|
||||
源码包保留浅层 Git 更新能力,不包含依赖环境、用户密钥、任务历史或预编译网页。升级主程序不会自动升级已安装插件。
|
||||
|
||||
## 发布验证
|
||||
|
||||
- Python 3.12 与 3.14 的默认后端测试分别为 **2952 项通过、6 项跳过、8 项按标记排除**。新增启动回归、同一时钟刻度下的诊断刷新回归及跨版本 MCP 描述回归;现有工具目录与协议快照无需修改。
|
||||
- 在 Python 3.12 隔离环境运行真实服务生命周期,首页、API 文档、收藏和探索接口均返回 HTTP 200,服务正常关闭。仅替代硬件预热和防休眠入口,数据库、队列、IPC、MCP 会话及 HTTP 服务沿用真实实现。
|
||||
- 前端 **290 项测试通过**,2.0.1 生产构建成功。Ruff 格式与静态检查、质量基线和核心类型检查通过。
|
||||
|
||||
本次未调用付费模型或执行设备业务任务,未替用户升级或重启现有安装。
|
||||
+2
-2
@@ -16,7 +16,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from artemis.mcp.tool_server import ArtemisFastMCP
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -30,7 +30,7 @@ except Exception as exc:
|
||||
logger.debug("FastMCP Settings.model_rebuild skipped: %s", exc, exc_info=True)
|
||||
|
||||
# Define the shared FastMCP instance for external IDE and agent clients.
|
||||
mcp = FastMCP(
|
||||
mcp = ArtemisFastMCP(
|
||||
"artemis",
|
||||
instructions=(
|
||||
"ARTEMIS executes Android, computer and mixed tests with Flash, Pro or interactive exploration. The user starts the service; MCP only connects. Use the official artemis-use Skill for workflows and plugin development. "
|
||||
|
||||
@@ -11,9 +11,9 @@ import logging
|
||||
import threading
|
||||
from typing import get_type_hints
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
from artemis.mcp.tool_server import ArtemisFastMCP
|
||||
from artemis.service_registry import TOKEN_HEADER
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ def create_shared_mcp():
|
||||
from mcp_server import tools
|
||||
|
||||
executor = ToolExecutor()
|
||||
shared = FastMCP(
|
||||
shared = ArtemisFastMCP(
|
||||
"artemis",
|
||||
instructions=definitions.instructions,
|
||||
streamable_http_path="/",
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Artemis Thin Client Design
|
||||
|
||||
This document describes the remote SDK, not the web console's visual design.
|
||||
The shared runtime and UI boundaries are documented in the repository's
|
||||
[DESIGN.md](../../DESIGN.md). Current behavior below includes the 2.0 exploration
|
||||
adapter; versioned remote endpoints remain a protocol evolution proposal.
|
||||
|
||||
## Goals
|
||||
|
||||
Developer machines install only a small remote SDK, while the host connected to
|
||||
@@ -34,9 +39,36 @@ session database, the client reconstructs its state from `queue` and
|
||||
Experimental parameters live in `options`. Older servers ignore unknown fields,
|
||||
while newer servers can consume them without requiring an immediate SDK release.
|
||||
|
||||
## Phase 2: Stable Remote API
|
||||
### Guided exploration in 2.0
|
||||
|
||||
The server should expose versioned endpoints dedicated to remote clients:
|
||||
`ArtemisClient.exploration()` uses the existing `/api/explorations` API shared
|
||||
with the web workspace and MCP. It supports read-only list/get/case queries,
|
||||
session lifecycle and messages, manual demonstrations, immutable case versions,
|
||||
generation, reruns, and batch-draft creation. It adds no scheduler or local agent.
|
||||
|
||||
Mutations require a stable `request_id`, except pause/stop. Reusing a request ID
|
||||
must not repeat the business operation. Read operations reject mutation fields;
|
||||
creating a batch draft does not start it. Case generation returns a job whose
|
||||
state is read from the exploration session, rather than blocking the client for
|
||||
the entire model call.
|
||||
|
||||
Execution status, test verdict, case generation, and rerun results are separate.
|
||||
`TaskResult.succeeded` currently reflects execution status only; it does not
|
||||
prove that the test passed. Consumers must inspect the server's effective test
|
||||
result and evidence, including `runs[].test_result` for exploration, and retain
|
||||
unknown response fields through `raw`. A client polling timeout does not cancel
|
||||
the server task; stopping it is a separate explicit operation.
|
||||
|
||||
The host owns source metadata, demonstration recording scope, execution budgets,
|
||||
model retries, final acceptance, and artifact persistence. The client passes the
|
||||
requested settings through the shared API and never reconstructs missing PC
|
||||
evidence from Android screenshots or restarts a case after a request failure.
|
||||
|
||||
## Phase 2: Proposed stable remote API
|
||||
|
||||
The following endpoints are an evolution proposal, not the current submission
|
||||
API. Do not replace the working compatibility adapter with these routes until
|
||||
the host implements and advertises them:
|
||||
|
||||
```text
|
||||
POST /api/v1/tasks
|
||||
@@ -48,8 +80,9 @@ GET /api/v1/devices
|
||||
GET /api/v1/capabilities
|
||||
```
|
||||
|
||||
The administration API and remote SDK API should remain separate. The remote API
|
||||
uses a consistent error envelope:
|
||||
A future remote surface can separate administration access from task access
|
||||
while delegating to the same backend services and scheduler. The proposed error
|
||||
envelope is:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -66,7 +99,7 @@ uses a consistent error envelope:
|
||||
1. `/api/v1` may add optional request and response fields, but must not change existing field semantics.
|
||||
2. The client ignores unknown response fields and preserves the original response in each model's `raw` attribute.
|
||||
3. Every new request field must have a server-side default.
|
||||
4. The SDK checks `/api/v1/capabilities` before using a new feature.
|
||||
4. `capabilities()` probes `/api/v1/capabilities` and falls back to the known baseline on 404; future feature gating must not pretend that this fallback advertises new host features.
|
||||
5. Breaking changes ship under `/api/v2`; they are never applied in place.
|
||||
6. The SDK and server follow semantic versioning independently.
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
|
||||
[project]
|
||||
name = "artemis"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
description = "Autonomous AI Assistant for Mobile Automation"
|
||||
|
||||
authors = [
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"""Unit tests for Artemis System Diagnostics & Readiness Engine."""
|
||||
|
||||
import asyncio
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
import pytest
|
||||
@@ -173,7 +174,13 @@ async def test_dashboard_reuses_recent_confirmed_state_on_one_timeout(monkeypatc
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_readiness_engine_coalesces_concurrent_full_scans():
|
||||
@pytest.mark.parametrize("same_clock_tick", [False, True])
|
||||
async def test_readiness_engine_coalesces_concurrent_full_scans(same_clock_tick, monkeypatch):
|
||||
if same_clock_tick:
|
||||
monkeypatch.setattr(
|
||||
"artemis.core.diagnostics.engine.time",
|
||||
SimpleNamespace(monotonic=lambda: 100.0, time=lambda: 100.0),
|
||||
)
|
||||
engine = ReadinessEngine()
|
||||
result = ProbeResult(
|
||||
id="test_probe",
|
||||
@@ -200,7 +207,13 @@ async def test_readiness_engine_coalesces_concurrent_full_scans():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_readiness_engine_reuses_cache_until_forced():
|
||||
@pytest.mark.parametrize("same_clock_tick", [False, True])
|
||||
async def test_readiness_engine_reuses_cache_until_forced(same_clock_tick, monkeypatch):
|
||||
if same_clock_tick:
|
||||
monkeypatch.setattr(
|
||||
"artemis.core.diagnostics.engine.time",
|
||||
SimpleNamespace(monotonic=lambda: 100.0, time=lambda: 100.0),
|
||||
)
|
||||
engine = ReadinessEngine()
|
||||
result = ProbeResult(
|
||||
id="test_probe",
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
from mcp.types import ToolAnnotations
|
||||
import pytest
|
||||
|
||||
from artemis.mcp.tool_server import ArtemisFastMCP
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("registration", ["decorator", "add_tool"])
|
||||
@pytest.mark.parametrize(
|
||||
("docstring", "expected"),
|
||||
[
|
||||
("Summary.\n\n Details.\n Nested.\n ", "Summary.\n\nDetails.\n Nested.\n"),
|
||||
("Summary.\n\nDetails.\n Nested.\n", "Summary.\n\nDetails.\n Nested.\n"),
|
||||
("One line.", "One line."),
|
||||
],
|
||||
)
|
||||
async def test_tool_descriptions_match_across_docstring_formats(registration, docstring, expected):
|
||||
server = ArtemisFastMCP("description-test")
|
||||
|
||||
def probe(count: int = 1) -> str:
|
||||
return str(count)
|
||||
|
||||
probe.__doc__ = docstring
|
||||
if registration == "decorator":
|
||||
assert server.tool()(probe) is probe
|
||||
else:
|
||||
server.add_tool(probe)
|
||||
(tool,) = await server.list_tools()
|
||||
assert tool.description == expected
|
||||
assert tool.inputSchema["properties"]["count"]["type"] == "integer"
|
||||
assert probe.__doc__ == docstring
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_explicit_tool_metadata_is_preserved():
|
||||
server = ArtemisFastMCP("description-test")
|
||||
|
||||
def probe() -> str:
|
||||
"""The fallback docstring."""
|
||||
return "ok"
|
||||
|
||||
description = "Explicit description.\n Keep this indentation.\n"
|
||||
server.add_tool(
|
||||
probe,
|
||||
"renamed_probe",
|
||||
"Probe title",
|
||||
description,
|
||||
annotations=ToolAnnotations(readOnlyHint=True),
|
||||
meta={"source": "test"},
|
||||
structured_output=False,
|
||||
)
|
||||
(tool,) = await server.list_tools()
|
||||
assert tool.name == "renamed_probe"
|
||||
assert tool.title == "Probe title"
|
||||
assert tool.description == description
|
||||
assert tool.annotations.readOnlyHint is True
|
||||
assert tool.meta == {"source": "test"}
|
||||
@@ -0,0 +1,61 @@
|
||||
"""Cold imports and runtime annotations must work on the installer's Python."""
|
||||
|
||||
from importlib import import_module
|
||||
import inspect
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
from typing import get_type_hints
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("module_name", "class_name"),
|
||||
[
|
||||
("artemis.exploration.store", "ExplorationStore"),
|
||||
("artemis.exploration.demonstrations", "Demonstrations"),
|
||||
("apps.admin_console.services.favorites", "FavoriteService"),
|
||||
],
|
||||
)
|
||||
def test_startup_service_annotations_do_not_shadow_builtin_collections(module_name, class_name):
|
||||
service = getattr(import_module(module_name), class_name)
|
||||
# Python 3.14 defers these errors until annotations are read. Evaluating them
|
||||
# also detects the import failure seen immediately on Python 3.12 and 3.13.
|
||||
assert get_type_hints(service.list)["return"] == list[dict]
|
||||
for method in vars(service).values():
|
||||
if inspect.isfunction(method):
|
||||
get_type_hints(method)
|
||||
|
||||
|
||||
def test_ui_server_cold_import_serves_empty_workspace(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("ARTEMIS_APP_DIR", str(tmp_path / "app"))
|
||||
monkeypatch.setenv("ARTEMIS_TRACES_DIR", str(tmp_path / "traces"))
|
||||
code = dedent("""
|
||||
from fastapi.testclient import TestClient
|
||||
from apps.admin_console.server import app
|
||||
|
||||
# No lifespan: validate startup imports and HTTP routes without device
|
||||
# discovery, background workers, or a running user's service.
|
||||
client = TestClient(app, base_url="http://localhost")
|
||||
try:
|
||||
for path in ("/api/automation/favorites", "/api/explorations"):
|
||||
response = client.get(path)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == [], response.text
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert "/api/explorations" in response.json()["paths"]
|
||||
finally:
|
||||
client.close()
|
||||
""")
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-X", "utf8", "-c", code],
|
||||
cwd=Path(__file__).resolve().parents[2],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
)
|
||||
assert result.returncode == 0, result.stdout + result.stderr
|
||||
Reference in new issue
Block a user