Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be6750a104 | ||
|
|
910dde6302 | ||
|
|
5d14bcffbc |
No files matched your search
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
ARTEMIS 3.x 测试分支使用独立 `catalog-v5.json`:Cua `0.38.0+artemis.2`、task-review `1.1.1` 与 artemis-device-test `0.5.0+artemis.7`。旧索引和 `<3.0.0` 约束不变;测试包保留各自未测范围,不代替 Rust 业务验收。来源与边界见 [3.0 测试市场说明](docs/rust3-compatibility.md)。
|
||||||
|
|
||||||
本仓库维护 ARTEMIS 插件适配与市场分发。当前发布基线是 Cua Driver 0.38.0 的 Windows x64 适配,市场版本为 `0.38.0+artemis.1`,保留预览标识。后续版本以目标分支的清单、市场索引、来源记录和发行说明为准。
|
本仓库维护 ARTEMIS 插件适配与市场分发。当前发布基线是 Cua Driver 0.38.0 的 Windows x64 适配,市场版本为 `0.38.0+artemis.1`,保留预览标识。后续版本以目标分支的清单、市场索引、来源记录和发行说明为准。
|
||||||
|
|
||||||
这是适配器架构与行为设计,插件没有独立产品网页或通用配色。主程序网页的视觉规则由其根目录 DESIGN.md 管理;包内 [SKILL.md](plugins/cua-driver/SKILL.md) 指导调用方如何使用实际工具,不代替宿主权限或证据校验。
|
这是适配器架构与行为设计,插件没有独立产品网页或通用配色。主程序网页的视觉规则由其根目录 DESIGN.md 管理;包内 [SKILL.md](plugins/cua-driver/SKILL.md) 指导调用方如何使用实际工具,不代替宿主权限或证据校验。
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# Artemis Plugins
|
# Artemis Plugins
|
||||||
|
|
||||||
|
ARTEMIS 3.x 使用独立测试分支 `codex/rust3-plugin-market` 和 `catalog-v5.json`,包含 Cua `0.38.0+artemis.2`、任务人工评审 `1.1.1`、device-test `0.5.0+artemis.7`。三个新版本各自记录验证范围;device-test 使用独立 Python worker。构建输入、来源和下载校验要求见 [3.0 测试市场说明](docs/rust3-compatibility.md)。下文保留对应 2.x 发行的历史说明。
|
||||||
|
|
||||||
ARTEMIS 的插件市场。配套 ARTEMIS 2.2.0 发布的电脑控制插件为 **Cua 0.38.0+artemis.1**,支持 Windows x64,通过窗口截图与可访问性树操作电脑应用和浏览器窗口,复用 ARTEMIS 的调度、资源互斥、步骤、截图附件和录像。
|
ARTEMIS 的插件市场。配套 ARTEMIS 2.2.0 发布的电脑控制插件为 **Cua 0.38.0+artemis.1**,支持 Windows x64,通过窗口截图与可访问性树操作电脑应用和浏览器窗口,复用 ARTEMIS 的调度、资源互斥、步骤、截图附件和录像。
|
||||||
|
|
||||||
在 ARTEMIS 的「插件」页面安装「电脑控制 · Cua」,授权并启用即可。安卓任务可根据提示词按需调用电脑控制,不需要单独配置 Codex MCP 或安装 Go、Node、Python。
|
在 ARTEMIS 的「插件」页面安装「电脑控制 · Cua」,授权并启用即可。安卓任务可根据提示词按需调用电脑控制,不需要单独配置 Codex MCP 或安装 Go、Node、Python。
|
||||||
|
|||||||
+624
@@ -0,0 +1,624 @@
|
|||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"manifest": {
|
||||||
|
"schema_version": 2,
|
||||||
|
"id": "cua-driver",
|
||||||
|
"name": "电脑控制 · Cua",
|
||||||
|
"version": "0.38.0+artemis.2",
|
||||||
|
"description": "通过 Cua Driver 的窗口截图与可访问性树操作 Windows 应用及浏览器窗口;复用 ARTEMIS 的任务调度、步骤证据与录像。",
|
||||||
|
"artemis_version": ">=3.0.0,<4.0.0",
|
||||||
|
"platforms": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"architectures": [
|
||||||
|
"amd64"
|
||||||
|
],
|
||||||
|
"source": "https://github.com/trycua/cua",
|
||||||
|
"license": "MIT",
|
||||||
|
"server": {
|
||||||
|
"transport": "stdio",
|
||||||
|
"command": "${plugin_dir}/bin/artemis-cua-adapter.exe"
|
||||||
|
},
|
||||||
|
"skill": "SKILL.md",
|
||||||
|
"targets": [
|
||||||
|
"computer",
|
||||||
|
"mixed"
|
||||||
|
],
|
||||||
|
"resources": [
|
||||||
|
"desktop:local"
|
||||||
|
],
|
||||||
|
"observation": {
|
||||||
|
"tool": "list_windows",
|
||||||
|
"arguments": {
|
||||||
|
"on_screen_only": true
|
||||||
|
},
|
||||||
|
"refresh_tools": [
|
||||||
|
"get_window_state"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"kind": "visual",
|
||||||
|
"recording": "windows",
|
||||||
|
"tools": {
|
||||||
|
"list_windows": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": true,
|
||||||
|
"idempotent": true,
|
||||||
|
"target_argument": "",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"get_window_state": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": true,
|
||||||
|
"idempotent": true,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"verify_state": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"click": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"drag": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type_text": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"press_key": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"set_value": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"scroll": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"save_screenshot": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image",
|
||||||
|
"file"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"click_at": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bring_to_front": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"target_argument": "window_id",
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"image",
|
||||||
|
"state"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required_tools": [
|
||||||
|
"list_windows",
|
||||||
|
"get_window_state",
|
||||||
|
"save_screenshot"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"release_stage": "preview",
|
||||||
|
"archive_url": "https://git.playones.com/chenmingxuan/Artemis-Plugins/releases/download/rust3-plugin-preview-20261101/cua-driver-0.38.0+artemis.2-windows-amd64.zip",
|
||||||
|
"sha256": "86a26b95618ebff90b59ab8be4d9a5985ef83a6d372405cfdd7ab5ee77ad3308",
|
||||||
|
"verification": "Windows x64 / ARTEMIS 3.x 候选。Go 确定性回归、vet、桥接构建和最终 ZIP 输入/回读校验通过;Driver/UIA/SDK/游标工具原样复用已发布 0.38.0+artemis.1 的固定文件与来源。3.0 实际握手、窗口发现、截图、动作、录像及业务任务待宿主统一验收;未进行性能测试。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"manifest": {
|
||||||
|
"schema_version": 3,
|
||||||
|
"id": "task-review",
|
||||||
|
"name": "任务人工评审",
|
||||||
|
"version": "1.1.1",
|
||||||
|
"description": "在任务回放中查看导入的原始用例,逐条填写用例执行准确性与测试结果正确性,按需填写备注并随报告导出。",
|
||||||
|
"artemis_version": ">=3.0.0,<4.0.0",
|
||||||
|
"platforms": [
|
||||||
|
"win32",
|
||||||
|
"linux",
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"architectures": [
|
||||||
|
"amd64",
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"targets": [],
|
||||||
|
"capabilities": {
|
||||||
|
"kind": "other",
|
||||||
|
"recording": "none",
|
||||||
|
"tools": {}
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": "requirements",
|
||||||
|
"title": "原始用例",
|
||||||
|
"side": "left",
|
||||||
|
"kind": "original_case",
|
||||||
|
"width": {
|
||||||
|
"preferred": 340,
|
||||||
|
"minimum": 240,
|
||||||
|
"maximum": 480
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "review",
|
||||||
|
"title": "人工评审",
|
||||||
|
"side": "right",
|
||||||
|
"kind": "form",
|
||||||
|
"width": {
|
||||||
|
"preferred": 360,
|
||||||
|
"minimum": 260,
|
||||||
|
"maximum": 520
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"id": "execution_accuracy",
|
||||||
|
"label": "用例执行准确性",
|
||||||
|
"description": "核对实际操作是否符合用例步骤。",
|
||||||
|
"kind": "choice",
|
||||||
|
"options": [
|
||||||
|
"通过",
|
||||||
|
"失败"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "execution_note",
|
||||||
|
"label": "用例执行准确性备注",
|
||||||
|
"kind": "text",
|
||||||
|
"max_length": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "result_correctness",
|
||||||
|
"label": "测试结果正确性",
|
||||||
|
"description": "核对测试结果是否符合用例的预期结果。",
|
||||||
|
"kind": "choice",
|
||||||
|
"options": [
|
||||||
|
"通过",
|
||||||
|
"失败"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "result_note",
|
||||||
|
"label": "测试结果正确性备注",
|
||||||
|
"kind": "text",
|
||||||
|
"max_length": 2000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"release_stage": "preview",
|
||||||
|
"archive_url": "https://git.playones.com/chenmingxuan/Artemis-Plugins/releases/download/rust3-plugin-preview-20261101/task-review-1.1.1.zip",
|
||||||
|
"sha256": "37647b77c7a1384f64ed861f72050858052c187eb7c19cfb338516d5e24e9e0f",
|
||||||
|
"verification": "ARTEMIS 3.x 声明式工作区候选,无运行进程或额外依赖。清单字段/面板及最终 ZIP 回读校验通过;实际界面、自动保存、导航与报告导出待 3.0 宿主统一验收,不沿用 2.4 的历史测试结论。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"manifest": {
|
||||||
|
"schema_version": 2,
|
||||||
|
"id": "artemis-device-test",
|
||||||
|
"name": "家庭安防多工具测试",
|
||||||
|
"version": "0.5.0+artemis.7",
|
||||||
|
"description": "集成 artemis-device-test Skill:PATH/统一目录自动发现工具及匹配位数的 MediaInfo DLL、按能力独立自检、本机网卡枚举、357 条用例路由、媒体/抓包/串口、V1/V2 设备及联合证据。PC/网站复用视觉插件,手机沿用 Artemis;工具成功不等于用例通过。",
|
||||||
|
"artemis_version": ">=3.0.0,<4.0.0",
|
||||||
|
"platforms": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"architectures": [
|
||||||
|
"amd64"
|
||||||
|
],
|
||||||
|
"license": "LicenseRef-Yitong-Artemis-Market; Apache-2.0-Adapter",
|
||||||
|
"server": {
|
||||||
|
"transport": "stdio",
|
||||||
|
"command": "${config.python}",
|
||||||
|
"args": [
|
||||||
|
"-B",
|
||||||
|
"${plugin_dir}/server.py"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"ARTEMIS_DEVICE_TEST_PROFILE": "${config.profile}",
|
||||||
|
"ARTEMIS_DEVICE_TEST_TOOL_ROOT": "${config.tool_root}",
|
||||||
|
"ARTEMIS_DEVICE_TEST_ENV_FILE": "${config.rtsp_env_file}",
|
||||||
|
"ARTEMIS_DEVICE_TEST_TIMEOUT": "${config.operation_timeout_seconds}",
|
||||||
|
"ARTEMIS_DEVICE_TEST_ALLOW_FIRMWARE_WRITE": "${config.allow_firmware_write}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"python": {
|
||||||
|
"label": "插件 Python 解释器(独立准备)",
|
||||||
|
"description": "使用独立 Windows x64 Python,预先安装本插件 requirements.txt。Rust 主程序不提供 Python;安装插件不会运行 pip。",
|
||||||
|
"default": "python",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"label": "本机工具 profile JSON",
|
||||||
|
"description": "可选,固定 tools/runtimes/libraries 路径;不保存凭据。留空时自动发现统一目录和宿主 PATH;显式错误路径不回退。",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"tool_root": {
|
||||||
|
"label": "已解压工具目录",
|
||||||
|
"description": "可选,本机已解压工具目录绝对路径;自动发现 EXE/匹配位数的 MediaInfo.dll,以及 binwalk-python/Scripts/python.exe。不安装程序。",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"rtsp_env_file": {
|
||||||
|
"label": "本机 RTSP 环境文件",
|
||||||
|
"description": "可选,受控 .env 文件的绝对路径;仅按工具指定的环境变量名读取,不回显凭据。",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"operation_timeout_seconds": {
|
||||||
|
"label": "单次操作预算(秒)",
|
||||||
|
"description": "默认45,范围10..3300;宿主插件工具时限至少大10秒,并受任务剩余预算约束。",
|
||||||
|
"default": "45"
|
||||||
|
},
|
||||||
|
"allow_firmware_write": {
|
||||||
|
"label": "允许显式固件写入",
|
||||||
|
"description": "默认 false。仅当前用例明确授权升级时设置 true;工具还要求 execute、完整身份、ROM SHA-256 和审阅摘要。",
|
||||||
|
"default": "false"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"skill": "SKILL.md",
|
||||||
|
"targets": [
|
||||||
|
"android",
|
||||||
|
"mixed",
|
||||||
|
"computer"
|
||||||
|
],
|
||||||
|
"resources": [
|
||||||
|
"desktop:local",
|
||||||
|
"lab:device-test"
|
||||||
|
],
|
||||||
|
"capabilities": {
|
||||||
|
"kind": "other",
|
||||||
|
"recording": "none",
|
||||||
|
"required_tools": [
|
||||||
|
"environment_doctor",
|
||||||
|
"lookup_case",
|
||||||
|
"probe_media",
|
||||||
|
"media_info",
|
||||||
|
"analyze_pcap",
|
||||||
|
"scan_ports",
|
||||||
|
"scan_firmware",
|
||||||
|
"device_capabilities",
|
||||||
|
"list_network_interfaces",
|
||||||
|
"discover_devices",
|
||||||
|
"identify_device",
|
||||||
|
"device_info",
|
||||||
|
"inspect_firmware",
|
||||||
|
"firmware_preflight",
|
||||||
|
"verify_firmware",
|
||||||
|
"capture_packets",
|
||||||
|
"capture_serial",
|
||||||
|
"upgrade_firmware",
|
||||||
|
"import_evidence",
|
||||||
|
"bundle_evidence"
|
||||||
|
],
|
||||||
|
"tools": {
|
||||||
|
"environment_doctor": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lookup_case": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"probe_media": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "input_file"
|
||||||
|
},
|
||||||
|
"media_info": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "input_file"
|
||||||
|
},
|
||||||
|
"analyze_pcap": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "input_file"
|
||||||
|
},
|
||||||
|
"scan_ports": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "host"
|
||||||
|
},
|
||||||
|
"scan_firmware": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "input_file"
|
||||||
|
},
|
||||||
|
"device_capabilities": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"list_network_interfaces": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"discover_devices": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"identify_device": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "host"
|
||||||
|
},
|
||||||
|
"device_info": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "host"
|
||||||
|
},
|
||||||
|
"inspect_firmware": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "rom_file"
|
||||||
|
},
|
||||||
|
"firmware_preflight": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "host"
|
||||||
|
},
|
||||||
|
"verify_firmware": {
|
||||||
|
"effect": "read",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": true,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "host"
|
||||||
|
},
|
||||||
|
"capture_packets": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "host"
|
||||||
|
},
|
||||||
|
"capture_serial": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "port"
|
||||||
|
},
|
||||||
|
"upgrade_firmware": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "host"
|
||||||
|
},
|
||||||
|
"import_evidence": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "input_file"
|
||||||
|
},
|
||||||
|
"bundle_evidence": {
|
||||||
|
"effect": "write",
|
||||||
|
"observation": false,
|
||||||
|
"idempotent": false,
|
||||||
|
"cancellation": "unknown",
|
||||||
|
"evidence": [
|
||||||
|
"text",
|
||||||
|
"state",
|
||||||
|
"file"
|
||||||
|
],
|
||||||
|
"target_argument": "run_id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"release_stage": "preview",
|
||||||
|
"archive_url": "https://git.playones.com/chenmingxuan/Artemis-Plugins/releases/download/rust3-plugin-preview-20261101/artemis-device-test-0.5.0+artemis.7-windows-amd64.zip",
|
||||||
|
"sha256": "2af0a60b4e991fc8def707bd562268330790f276528fe66627de817f296c0174",
|
||||||
|
"verification": "ARTEMIS 3.x Windows x64 候选;独立 Python/MCP/dotenv 与外部工具由使用者准备。固定源码、MARKET 内容摘要和最终 ZIP 回读校验;完整许可及证据准入由 SDK 独立预检。Rust 安装、握手、取消、设备与业务验收范围以包内验证记录为准,不沿用旧宿主结论。"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# ARTEMIS 3.0 独立测试市场
|
||||||
|
|
||||||
|
测试分支 `codex/rust3-plugin-market` 基于 `987b5ff2d8c3516cf87e2f1abcac854732a8ccee`,不包含原插件开发目录的未发布提交。旧 `catalog.json`、`catalog-v3.json`、`catalog-v4.json` 和 2.x 附件保留。
|
||||||
|
|
||||||
|
| 当前插件 | 新不可变测试版本 | 宿主范围 | 用户运行依赖 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Cua | 0.38.0+artemis.2 | >=3.0.0,<4.0.0 | 自包含 Windows x64 Go 桥、Rust Driver/UIA、SDK DLL、游标工具 |
|
||||||
|
| task-review | 1.1.1 | >=3.0.0,<4.0.0 | 声明式任务评审,无额外进程 |
|
||||||
|
| artemis-device-test | 0.5.0+artemis.7 | >=3.0.0,<4.0.0 | 独立 Python、requirements.txt 和所需外部工具;安装不自动运行 pip 或授权 |
|
||||||
|
|
||||||
|
旧 OCU 已退休,源码和历史附件保留;内置 Android 能力随主程序提供,无需市场插件。
|
||||||
|
|
||||||
|
构建命令(输出目录和索引文件必须不存在):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/package_rust3_compat.py --cua-base-archive <已校验的.1输入ZIP> --go <便携Go目录>/bin/go.exe --output <新候选目录> --catalog-output <新索引文件> --release-tag rust3-plugin-preview-20261101
|
||||||
|
```
|
||||||
|
|
||||||
|
Cua `.1` 输入 SHA256 必须为 `c007da16d58c4165a1a344113e650e68bd92a69ad3ce4ca8285818c5426dc0f4`。Go 源码除适配版本常量外逐文件对比,Driver/UIA/SDK 和游标工具复用已发布输入;provenance 区分复用和新编译。构建在独立缓存内运行 Go test/vet/build,不运行桌面或业务任务。
|
||||||
|
|
||||||
|
device-test 所有 Python 源码字节保留;RUST3-SOURCE.json 记录原提交和逐文件摘要。组件许可及材料/文件绑定通过 authoring SDK 检查;MARKET.json 的 Rust 业务项目明确为 unverified,原 2.x 结果不外推。测试市场以 preview 展示,不是正式审核通过声明。安装、启用与 profile 授权分离。
|
||||||
|
|
||||||
|
三个 ZIP、SHA256SUMS、catalog 与 bundle-map 必须匹配;HTTPS 下载地址共用指定预览发行标签。先固定源码并验证最终包,再上传新标签附件、回读 SHA,最后提交测试索引;不覆盖旧附件。下载和 Rust 网页联调范围在本分支另行记录。
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Rust 3 插件市场预览验证(2026-11-01)
|
||||||
|
|
||||||
|
固定源码:5d14bcffbc0cb6389c89d0e54b33895d5ab5f57c。标签 rust3-plugin-preview-20261101 指向该提交;构建时工作区干净。
|
||||||
|
|
||||||
|
已运行:打包器 6 项离线回归;Go test/vet/build;三个最终 ZIP 清单与索引一致性、SHA256 回读;device-test 包解压后 SDK 许可、材料/文件摘要绑定检查。12 项 Rust 业务准入仍为 unverified,未按正式审核通过发布。
|
||||||
|
|
||||||
|
预览发行已回读为 draft=false、prerelease=true。三个 ZIP 和四份配套材料共 7 个附件均已通过无认证 HTTPS 下载及 SHA256 对比;build-receipt.json 的 published=false 是构建时状态,发行状态以此记录和实际远端为准。
|
||||||
|
|
||||||
|
| 附件 | SHA256 |
|
||||||
|
| --- | --- |
|
||||||
|
| cua-driver-0.38.0+artemis.2-windows-amd64.zip | 86a26b95618ebff90b59ab8be4d9a5985ef83a6d372405cfdd7ab5ee77ad3308 |
|
||||||
|
| task-review-1.1.1.zip | 37647b77c7a1384f64ed861f72050858052c187eb7c19cfb338516d5e24e9e0f |
|
||||||
|
| artemis-device-test-0.5.0+artemis.7-windows-amd64.zip | 2af0a60b4e991fc8def707bd562268330790f276528fe66627de817f296c0174 |
|
||||||
|
| SHA256SUMS.txt | 30e44c5ab0b715c24a71dc21b396bbfc51aa9a3e5e4a1ae2ea5caa35309bc508 |
|
||||||
|
| catalog-v5.json | feedce75d6de176beb0d9151e51821b9bacb07c86de1db998f6892ab68f0eb45 |
|
||||||
|
| bundle-map.json | 2a838beb0a602d8910bc23f6ede5219bbfd2336ed0832ba9fa46e42f1e77c911 |
|
||||||
|
| build-receipt.json | 4dd61b18853584719373192babe2d4d026bc2ec87d086065eb6fb591b43c9a93 |
|
||||||
|
|
||||||
|
[预览发行](https://git.playones.com/chenmingxuan/Artemis-Plugins/releases/tag/rust3-plugin-preview-20261101)
|
||||||
|
|
||||||
|
Rust 3.0.0 宿主已通过 `/api/plugins/catalog` 严格解析此测试分支的三个条目,并在实际网页的插件市场显示三个开发预览及安装入口。仅验证读取与展示,未安装、启用或扩大 profile 授权;未运行付费模型、设备/桌面业务或录像联调。安装不自动执行 pip。
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
> Rust 3 测试市场版本 `0.5.0+artemis.7`;独立 Python/MCP/dotenv 与外部工具由使用者准备。验证边界见 [RUST3-VERIFICATION.md](RUST3-VERIFICATION.md)。下文保留原 2.x 版本的历史范围。
|
||||||
|
|
||||||
# 内部团队首次部署(Windows x64)
|
# 内部团队首次部署(Windows x64)
|
||||||
|
|
||||||
接收方先准备当前用例需要的工具,随后插件自动发现和检查。无需逐项填写程序路径。插件 ZIP 包含适配代码、原 Skill、通用样例和说明,不携带第三方程序、驱动、本机 profile、账号或测试数据。
|
接收方先准备当前用例需要的工具,随后插件自动发现和检查。无需逐项填写程序路径。插件 ZIP 包含适配代码、原 Skill、通用样例和说明,不携带第三方程序、驱动、本机 profile、账号或测试数据。
|
||||||
|
|||||||
@@ -1,125 +1,137 @@
|
|||||||
{
|
{
|
||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"plugin_id": "artemis-device-test",
|
"plugin_id": "artemis-device-test",
|
||||||
"plugin_version": "0.5.0+artemis.6",
|
"plugin_version": "0.5.0+artemis.7",
|
||||||
"content_sha256": "83f19450c4b0bdf4bad9389770e3ab35f81855d4e9f5d2975934e395308aabaf",
|
"content_sha256": "5ad107572dd67bba856d2a19c1c8d49b8c7189c95d6ed021e0af2961d1343412",
|
||||||
"traits": {
|
"traits": {
|
||||||
"uses_subprocesses": true,
|
"uses_subprocesses": true,
|
||||||
"file_inputs_for_writes": true
|
"file_inputs_for_writes": true
|
||||||
},
|
},
|
||||||
"components": [
|
"components": [
|
||||||
{
|
{
|
||||||
"name": "原 Skill、协议资料与用例路由",
|
"name": "原 Skill、协议资料与用例路由",
|
||||||
"paths": [
|
"paths": [
|
||||||
"skill"
|
"skill"
|
||||||
],
|
],
|
||||||
"license": "LicenseRef-Yitong-Artemis-Market",
|
"license": "LicenseRef-Yitong-Artemis-Market",
|
||||||
"permission": "permitted",
|
"permission": "permitted",
|
||||||
"license_file": "LICENSE-ORIGINAL-SKILL.md",
|
"license_file": "LICENSE-ORIGINAL-SKILL.md",
|
||||||
"source_file": "SOURCE.md"
|
"source_file": "SOURCE.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ARTEMIS 适配层和适配修复",
|
"name": "ARTEMIS 适配层和适配修复",
|
||||||
"paths": [
|
"paths": [
|
||||||
"dependency_checks.py",
|
"dependency_checks.py",
|
||||||
"dependency_runner.py",
|
"dependency_runner.py",
|
||||||
"DEPLOYMENT.md",
|
"DEPLOYMENT.md",
|
||||||
"device_runtime.py",
|
"device_runtime.py",
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"LICENSE-ORIGINAL-SKILL.md",
|
"LICENSE-ORIGINAL-SKILL.md",
|
||||||
"network_inventory.py",
|
"network_inventory.py",
|
||||||
"plugin.json",
|
"plugin.json",
|
||||||
"README.md",
|
"README.md",
|
||||||
"RELEASE.md",
|
"RELEASE.md",
|
||||||
"requirements.txt",
|
"requirements.txt",
|
||||||
"server.py",
|
"server.py",
|
||||||
"SKILL.md",
|
"SKILL.md",
|
||||||
"source-manifest.json",
|
"source-manifest.json",
|
||||||
"SOURCE.md",
|
"SOURCE.md",
|
||||||
"VERIFICATION.md",
|
"VERIFICATION.md",
|
||||||
"skill/scripts/process_lifecycle.py",
|
"skill/scripts/process_lifecycle.py",
|
||||||
"skill/tests/test_lifecycle_review.py",
|
"skill/tests/test_lifecycle_review.py",
|
||||||
"skill/tests/test_review_regressions.py",
|
"skill/tests/test_review_regressions.py",
|
||||||
"skill/ruff.toml"
|
"skill/ruff.toml"
|
||||||
],
|
],
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"permission": "permitted",
|
"permission": "permitted",
|
||||||
"license_file": "LICENSE",
|
"license_file": "LICENSE",
|
||||||
"source_file": "SOURCE.md"
|
"source_file": "SOURCE.md"
|
||||||
}
|
},
|
||||||
],
|
{
|
||||||
"checks": {
|
"name": "Rust 3 preview materials",
|
||||||
"installation": {
|
"paths": [
|
||||||
"status": "passed",
|
"UPSTREAM-MARKET.json",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"RUST3-SOURCE.json",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"RUST3-VERIFICATION.md"
|
||||||
"notes": "实际宿主 API 安装、临时目录旧版升级和禁用/配置保留已验证;新装不自动授权。"
|
],
|
||||||
},
|
"license": "Apache-2.0",
|
||||||
"tool_contract": {
|
"permission": "permitted",
|
||||||
"status": "passed",
|
"license_file": "LICENSE",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"source_file": "SOURCE.md"
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
}
|
||||||
"notes": "真实 stdio MCP 握手和全部 20 项定义检查;8 次隔离安全调用、10 份附件;134 项单元回归。"
|
],
|
||||||
},
|
"checks": {
|
||||||
"failure_partial": {
|
"installation": {
|
||||||
"status": "passed",
|
"status": "unverified",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
"notes": "清理核验故障及泛化 I/O 异常保留执行事实;未知写副作用不重试;缺失证据保持 inconclusive。"
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
},
|
},
|
||||||
"timeout_cancel": {
|
"tool_contract": {
|
||||||
"status": "passed",
|
"status": "unverified",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
"notes": "合成工具超时及真实 MCP 自检取消;取消后工具和后代约 0.03 秒退出,未操作真实设备。"
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
},
|
},
|
||||||
"process_tree": {
|
"failure_partial": {
|
||||||
"status": "passed",
|
"status": "unverified",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
"notes": "Windows Job 合成进程验证正常/非零/超时/超限/拥有者退出及清理异常;不声称其他系统通过。"
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
},
|
},
|
||||||
"file_binding": {
|
"timeout_cancel": {
|
||||||
"status": "passed",
|
"status": "unverified",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
"notes": "固件批准字节保护快照对抗原路径替换/截断/增长;V2 模拟协议;没有真实烧录。"
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
},
|
},
|
||||||
"resource_conflicts": {
|
"process_tree": {
|
||||||
"status": "passed",
|
"status": "unverified",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
"notes": "临时锁目录验证 desktop:local 和 lab:device-test 各自互斥;运行时阻止固定版本更新和卸载。"
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
},
|
},
|
||||||
"evidence_export": {
|
"file_binding": {
|
||||||
"status": "passed",
|
"status": "unverified",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
"notes": "正常宿主离线任务导入并校验摘要;8 份附件读回;Word/Excel/全部资料 ZIP 容器与附件字节核验。"
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
},
|
},
|
||||||
"history_uninstall": {
|
"resource_conflicts": {
|
||||||
"status": "passed",
|
"status": "unverified",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
"notes": "独立临时目录卸载后原历史附件字节仍可读;未删除用户任务或历史。"
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
},
|
},
|
||||||
"secret_redaction": {
|
"evidence_export": {
|
||||||
"status": "passed",
|
"status": "unverified",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
"notes": "合成凭据输出脱敏及源码审查通过;未将个人配置、原始报告、抓包、环境目录打包。"
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
},
|
},
|
||||||
"real_task": {
|
"history_uninstall": {
|
||||||
"status": "passed",
|
"status": "unverified",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
"notes": "正常 ARTEMIS Pro/computer 离线证据任务完成,AI 正式结论 passed、5/5;暂停恢复实测;真实设备能力未测。"
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
},
|
},
|
||||||
"source_review": {
|
"secret_redaction": {
|
||||||
"status": "passed",
|
"status": "unverified",
|
||||||
"evidence_file": "VERIFICATION.md",
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
"notes": "原候选 ZIP 摘要、当前文件来源清单、未改动 vendor 摘要及公司市场分发确认记录已核对;适配层 Apache-2.0。"
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
}
|
},
|
||||||
}
|
"real_task": {
|
||||||
}
|
"status": "unverified",
|
||||||
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
|
},
|
||||||
|
"source_review": {
|
||||||
|
"status": "unverified",
|
||||||
|
"evidence_file": "RUST3-VERIFICATION.md",
|
||||||
|
"evidence_sha256": "f65a272cd20c198353b35033b2c782ada2f98dc7979d96e5983a52e665effe7c",
|
||||||
|
"notes": "Rust 3 测试市场预览:静态清单、组件许可、文件摘要及组包已核对;此项真实宿主/设备验证未运行,原2.x结果不外推。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,58 +1,60 @@
|
|||||||
# 家庭安防多工具测试插件
|
> Rust 3 测试市场版本 `0.5.0+artemis.7`;独立 Python/MCP/dotenv 与外部工具由使用者准备。验证边界见 [RUST3-VERIFICATION.md](RUST3-VERIFICATION.md)。下文保留原 2.x 版本的历史范围。
|
||||||
|
|
||||||
将用户提供的 `artemis-device-test 0.5.0-internal` 接入 Artemis 2.0 插件系统,版本为 `0.5.0+artemis.6`。提供 20 个真实 MCP 工具、357 条保留用例路由和按需参考资料。工具成功、协议完成、业务通过分别记录。
|
# 家庭安防多工具测试插件
|
||||||
|
|
||||||
## 安装
|
将用户提供的 `artemis-device-test 0.5.0-internal` 接入 Artemis 2.0 插件系统,版本为 `0.5.0+artemis.6`。提供 20 个真实 MCP 工具、357 条保留用例路由和按需参考资料。工具成功、协议完成、业务通过分别记录。
|
||||||
|
|
||||||
团队接收方先读 [首次部署与自检](DEPLOYMENT.md):准备完整依赖后,把目录加入 PATH 或只设置一次 `tool_root`,无需逐个填路径。保留 MediaInfo DLL 后端,自动发现匹配位数的 DLL;各能力独立报告就绪情况。插件仅依赖 `requirements.txt` 中的 MCP 与 dotenv,不附带 FFmpeg、Wireshark、Nmap、Binwalk、MediaInfo、厂商软件或驱动,安装器也不自动安装 Python requirements。
|
## 安装
|
||||||
|
|
||||||
维护者在 Artemis-Plugins 仓库根目录,从已提交的源码打包:
|
团队接收方先读 [首次部署与自检](DEPLOYMENT.md):准备完整依赖后,把目录加入 PATH 或只设置一次 `tool_root`,无需逐个填路径。保留 MediaInfo DLL 后端,自动发现匹配位数的 DLL;各能力独立报告就绪情况。插件仅依赖 `requirements.txt` 中的 MCP 与 dotenv,不附带 FFmpeg、Wireshark、Nmap、Binwalk、MediaInfo、厂商软件或驱动,安装器也不自动安装 Python requirements。
|
||||||
|
|
||||||
```powershell
|
维护者在 Artemis-Plugins 仓库根目录,从已提交的源码打包:
|
||||||
python scripts/build_device_test.py --ref HEAD
|
|
||||||
```
|
```powershell
|
||||||
|
python scripts/build_device_test.py --ref HEAD
|
||||||
ZIP、SHA-256 和源码提交记录写入 `artifacts/device-test/`,ZIP 根层包含 `plugin.json`。宿主开发检查命令见 ARTEMIS 仓库的 `docs/plugin-development.md`。在网页“插件 → 高级接入”导入 ZIP 或本目录,打开设置填写 Python 的绝对路径,授权后启用。安装使用平台正常接口,不修改任务、历史或原 Skill,不要求重启平台。
|
```
|
||||||
|
|
||||||
新装默认未启用。插件不提供画面观察,纯电脑任务须同时选择已授权的 Cua 等视觉插件;Android 任务仍使用 Artemis 原有手机能力。插件不在任务中递归调用 `mobile_run_task`。
|
ZIP、SHA-256 和源码提交记录写入 `artifacts/device-test/`,ZIP 根层包含 `plugin.json`。宿主开发检查命令见 ARTEMIS 仓库的 `docs/plugin-development.md`。在网页“插件 → 高级接入”导入 ZIP 或本目录,打开设置填写 Python 的绝对路径,授权后启用。安装使用平台正常接口,不修改任务、历史或原 Skill,不要求重启平台。
|
||||||
|
|
||||||
## 本机设置
|
新装默认未启用。插件不提供画面观察,纯电脑任务须同时选择已授权的 Cua 等视觉插件;Android 任务仍使用 Artemis 原有手机能力。插件不在任务中递归调用 `mobile_run_task`。
|
||||||
|
|
||||||
| 字段 | 设置 |
|
## 本机设置
|
||||||
| --- | --- |
|
|
||||||
| `python` | 当前 Artemis 环境的 Python 绝对路径;不要误用未安装 MCP 的系统 Python |
|
| 字段 | 设置 |
|
||||||
| `profile` | 可选,本机工具配置 JSON 路径;结构见 `skill/assets/profile.example.json` |
|
| --- | --- |
|
||||||
| `tool_root` | 可选,统一的已解压工具目录;profile 优先,其次该目录,再次宿主 PATH;MediaInfo 也支持自动发现 |
|
| `python` | 当前 Artemis 环境的 Python 绝对路径;不要误用未安装 MCP 的系统 Python |
|
||||||
| `rtsp_env_file` | 可选,本机受控 `.env` 文件路径;需要 RTSP 时保存 `DUT_RTSP_URL` 等变量,工具只传变量名 |
|
| `profile` | 可选,本机工具配置 JSON 路径;结构见 `skill/assets/profile.example.json` |
|
||||||
| `operation_timeout_seconds` | 默认 45 秒,10..3300;宿主 `ARTEMIS_PLUGIN_TOOL_TIMEOUT_SECONDS` 至少比它大 10 秒,默认宿主 60 秒 |
|
| `tool_root` | 可选,统一的已解压工具目录;profile 优先,其次该目录,再次宿主 PATH;MediaInfo 也支持自动发现 |
|
||||||
| `allow_firmware_write` | 默认 `false`;当前用例明确授权升级时才设为 `true`,调用仍需要 `execute=true` 等完整条件 |
|
| `rtsp_env_file` | 可选,本机受控 `.env` 文件路径;需要 RTSP 时保存 `DUT_RTSP_URL` 等变量,工具只传变量名 |
|
||||||
|
| `operation_timeout_seconds` | 默认 45 秒,10..3300;宿主 `ARTEMIS_PLUGIN_TOOL_TIMEOUT_SECONDS` 至少比它大 10 秒,默认宿主 60 秒 |
|
||||||
profile 不保存密码或 URL。GUI 和网站没有伪造的业务 API;环境诊断列为需要真实界面/会话验证,安装包存在不能当成业务可用。原资料中的环境安装、人工登录和厂商专用能力边界仍适用。
|
| `allow_firmware_write` | 默认 `false`;当前用例明确授权升级时才设为 `true`,调用仍需要 `execute=true` 等完整条件 |
|
||||||
|
|
||||||
本包仅声明 Windows x64,未声称其他平台通过验证。标准资源为 `desktop:local`、`lab:device-test`,与电脑插件共用桌面锁,并保守串行共享实验台。任务若只需手机且希望独立并行,可明确不选此插件。
|
profile 不保存密码或 URL。GUI 和网站没有伪造的业务 API;环境诊断列为需要真实界面/会话验证,安装包存在不能当成业务可用。原资料中的环境安装、人工登录和厂商专用能力边界仍适用。
|
||||||
|
|
||||||
## 使用示例
|
本包仅声明 Windows x64,未声称其他平台通过验证。标准资源为 `desktop:local`、`lab:device-test`,与电脑插件共用桌面锁,并保守串行共享实验台。任务若只需手机且希望独立并行,可明确不选此插件。
|
||||||
|
|
||||||
在 Artemis 正常任务中描述:
|
## 使用示例
|
||||||
|
|
||||||
```text
|
在 Artemis 正常任务中描述:
|
||||||
使用家庭安防多工具测试插件,先检查本机工具和用例路由。
|
|
||||||
按本次指定的完整用例、摄像机 SN/实际上报型号、协议和手机序列号执行。
|
```text
|
||||||
先只读验证;缺失项记录为阻塞,不升级、不恢复出厂、不重提失败业务。
|
使用家庭安防多工具测试插件,先检查本机工具和用例路由。
|
||||||
PC/网站通过已授权电脑插件观察,已有登录态可复用,未登录交给人工完成。
|
按本次指定的完整用例、摄像机 SN/实际上报型号、协议和手机序列号执行。
|
||||||
将实际观测、未验证项和仍需人工核验项保存到任务证据,生成独立联合资料包。
|
先只读验证;缺失项记录为阻塞,不升级、不恢复出厂、不重提失败业务。
|
||||||
```
|
PC/网站通过已授权电脑插件观察,已有登录态可复用,未登录交给人工完成。
|
||||||
|
将实际观测、未验证项和仍需人工核验项保存到任务证据,生成独立联合资料包。
|
||||||
工具与操作规则见 [插件 Skill](SKILL.md)。报告工具只接受本任务附件名;外部本机证据先用 `import_evidence` 复制校验。独立 ZIP 内保留可移植 Markdown/JSON 和证据相对路径;导出的单独 Markdown 与 JSON 用于预览,以 ZIP 为完整交付。单附件不超过 64 MiB,联合包输入总计不超过 48 MiB。
|
```
|
||||||
|
|
||||||
查询当前局域网设备可直接描述“通过配置工具查询当前局域网内的设备,核对身份和版本”。插件先用 `list_network_interfaces` 读取本机已连接的物理 IPv4 接口,再在实际接口上分别进行 V1/V2 只读发现;用户指定协议或接口时遵守该范围。无需预先知道摄像机 SN、型号或打开配置工具 GUI。`environment_doctor` 的 GUI `manual-interface` 状态不阻塞独立协议适配器。接口枚举不会发送设备发现包;实际发现和身份查询仍由正常 Artemis 任务持有资源后执行。
|
工具与操作规则见 [插件 Skill](SKILL.md)。报告工具只接受本任务附件名;外部本机证据先用 `import_evidence` 复制校验。独立 ZIP 内保留可移植 Markdown/JSON 和证据相对路径;导出的单独 Markdown 与 JSON 用于预览,以 ZIP 为完整交付。单附件不超过 64 MiB,联合包输入总计不超过 48 MiB。
|
||||||
|
|
||||||
`artemis.2` 补齐本机网卡入口;`artemis.3` 增加可迁移的 DLL/运行时发现与分层自检。`environment_doctor(check_functional=true)` 会检查程序启动、DLL 加载及固定本地视频/PCAP/固件样本;接口与 COM 只枚举,不抓包或打开串口。默认自检仅发现文件。修改系统 PATH 后需从新环境重新启动 Artemis,再在插件内检查。保留原 Skill 资料与 vendor 协议实现;固件适配器、工具运行器及相关测试的修改见来源清单,固件写入条件不变。
|
查询当前局域网设备可直接描述“通过配置工具查询当前局域网内的设备,核对身份和版本”。插件先用 `list_network_interfaces` 读取本机已连接的物理 IPv4 接口,再在实际接口上分别进行 V1/V2 只读发现;用户指定协议或接口时遵守该范围。无需预先知道摄像机 SN、型号或打开配置工具 GUI。`environment_doctor` 的 GUI `manual-interface` 状态不阻塞独立协议适配器。接口枚举不会发送设备发现包;实际发现和身份查询仍由正常 Artemis 任务持有资源后执行。
|
||||||
|
|
||||||
## 验证与来源
|
`artemis.2` 补齐本机网卡入口;`artemis.3` 增加可迁移的 DLL/运行时发现与分层自检。`environment_doctor(check_functional=true)` 会检查程序启动、DLL 加载及固定本地视频/PCAP/固件样本;接口与 COM 只枚举,不抓包或打开串口。默认自检仅发现文件。修改系统 PATH 后需从新环境重新启动 Artemis,再在插件内检查。保留原 Skill 资料与 vendor 协议实现;固件适配器、工具运行器及相关测试的修改见来源清单,固件写入条件不变。
|
||||||
|
|
||||||
[VERIFICATION.md](VERIFICATION.md) 记录本次实际验证层次;[source-manifest.json](source-manifest.json) 保存导入文件的原始 SHA-256。固件适配器、工具运行器及相关测试已修订,其余原始资料按来源清单记录;不包含缓存、个人配置、账号、原始测试证据、模型 Key 或原始参考源码 ZIP。
|
## 验证与来源
|
||||||
|
|
||||||
[SOURCE.md](SOURCE.md) 说明来源与许可。[RELEASE.md](RELEASE.md) 说明本修订的发布范围;以市场索引和对应不可变发行附件确认实际分发状态。
|
[VERIFICATION.md](VERIFICATION.md) 记录本次实际验证层次;[source-manifest.json](source-manifest.json) 保存导入文件的原始 SHA-256。固件适配器、工具运行器及相关测试已修订,其余原始资料按来源清单记录;不包含缓存、个人配置、账号、原始测试证据、模型 Key 或原始参考源码 ZIP。
|
||||||
|
|
||||||
`artemis.6` 将环境自检与工具执行合并到同一 Windows Job 生命周期,限制管道输出;执行后清理异常保留已启动事实,回执返回未知或部分执行。市场记录见 `MARKET.json`,其通过范围以验证文档为准,不代表实机能力全部通过。
|
[SOURCE.md](SOURCE.md) 说明来源与许可。[RELEASE.md](RELEASE.md) 说明本修订的发布范围;以市场索引和对应不可变发行附件确认实际分发状态。
|
||||||
|
|
||||||
|
`artemis.6` 将环境自检与工具执行合并到同一 Windows Job 生命周期,限制管道输出;执行后清理异常保留已启动事实,回执返回未知或部分执行。市场记录见 `MARKET.json`,其通过范围以验证文档为准,不代表实机能力全部通过。
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"source_commit": "987b5ff2d8c3516cf87e2f1abcac854732a8ccee",
|
||||||
|
"source_version": "0.5.0+artemis.6",
|
||||||
|
"version": "0.5.0+artemis.7",
|
||||||
|
"all_python_source_bytes_preserved": true,
|
||||||
|
"python_files_sha256": {
|
||||||
|
"dependency_checks.py": "0ea7db6b7f0a816a75e737b25e7bd2592303c69196ac6b9827b58fe18d10ab5d",
|
||||||
|
"dependency_runner.py": "3bbb648faab09fbc41065b43d89213f9fb29a274961bc9fd1a8ebd018f979ef2",
|
||||||
|
"device_runtime.py": "e17b51ddf9c871a03bd1b44a35e0b0a5c98847ad9e8b586c64b89380e9d15ff6",
|
||||||
|
"network_inventory.py": "a915a9acd05db5804421a812570dd852427bae571677600f271939e7bdf5b041",
|
||||||
|
"server.py": "3a8c481579dd38fbbc923d14c406ef98f7d16a9bea4cd3c1a4d774438fedd0a6",
|
||||||
|
"skill/scripts/device_adapter.py": "409f2a0d6f8e85b006aa950d0d8c53d5d0a8e4e713b61155a9a8a23e6205cf25",
|
||||||
|
"skill/scripts/evidence.py": "e85dae725d39a1fb862d59a8e423b51f6d6cd3fe63d0ce1138ab651e24a0e098",
|
||||||
|
"skill/scripts/process_lifecycle.py": "0106bc81a675e595a98f4c3f3201b8021e1f574ed3486873a6550b56b5d9e5cb",
|
||||||
|
"skill/scripts/tool_runner.py": "10f4e4085a6ea06bbd89cfdabc065c048b99bcf96617ea2f64c34eeaf7e612cd",
|
||||||
|
"skill/scripts/v1_protocol.py": "c512e2cb99a0ba9cd70ade1429a2e96b236c9026dea5a96dcaf0b5051c6972a0",
|
||||||
|
"skill/tests/test_device_adapter.py": "c83ebf61d5065eb4113a6ba7985fc0f5c336dcb7d1e47ec09ec15c188849284b",
|
||||||
|
"skill/tests/test_evidence.py": "d6cbbfab48af03958e1716631d1a1e6f034b56082cd113c32fbcf0b6f20cb6b0",
|
||||||
|
"skill/tests/test_lifecycle_review.py": "02adc99eec0d612c6e4e70e85dc778b60ced8903d6ff01884f87a815ea5c132f",
|
||||||
|
"skill/tests/test_review_regressions.py": "574e054005175ce33b4f1eabba135af61bb758372f467d043f209b3c0d326830",
|
||||||
|
"skill/tests/test_tool_runner.py": "44b52cbd0ab7880ae06958a7707a45875deba9bfdebc0ed1fbff5f6177659ec2",
|
||||||
|
"skill/tests/test_v1_protocol.py": "e727d725507efdc436829f75f87108fbea75b0b883394bd859cb340abdb8df6f",
|
||||||
|
"skill/tests/test_v1_upgrade.py": "17403f6835bcb1f2295d195fb2be8b3de94da0b01751b0ce60100ceba3d8ac67",
|
||||||
|
"skill/scripts/vendor/device_upgrade.py": "b50c12c982a6944a2112e22dfed87c0fdfdc311e6c6ff43a5ce9fa2a726b00ed",
|
||||||
|
"skill/scripts/vendor/v2_discover.py": "6d0783c2a4f34397b61f4d1a65f97c2300f492caf8c60c38b48c246a75774158"
|
||||||
|
},
|
||||||
|
"scope": "Rust 3 preview metadata, independent worker instructions, truthful verification materials"
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Rust 3 测试市场预览
|
||||||
|
|
||||||
|
版本 `0.5.0+artemis.7`,宿主范围 `>=3.0.0,<4.0.0`。
|
||||||
|
基于插件仓库提交 `987b5ff2d8c3516cf87e2f1abcac854732a8ccee` 的 `.6` 源码;所有 Python 运行源码字节保留,仅调整兼容范围、版本、独立解释器说明和验证材料。
|
||||||
|
|
||||||
|
静态准入验证覆盖清单、工具声明、组件分发许可、所有材料/文件 SHA256 和确定性 ZIP。
|
||||||
|
当前预览不宣称 Rust 下真实 MCP、取消收尾、外部工具、设备写操作或 AI 业务验收通过;原 VERIFICATION.md 和 UPSTREAM-MARKET.json 的 2.x 结果不充当 Rust 3 结果。
|
||||||
|
MARKET.json 对这些项目明确标记 unverified;测试索引以 preview 展示,不是正式审核通过声明。
|
||||||
|
|
||||||
|
主程序不依赖 Python;本插件是独立 worker,使用者须自行准备 Python、requirements.txt 和所需外部工具。安装与授权分开,安装不自动启用。
|
||||||
|
默认操作预算45秒,主程序插件超时至少多10秒。RTSP 秘密只通过受控配置文件读取;固件写入默认关闭,未知结果不得重试。
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
> Rust 3 测试市场版本 `0.5.0+artemis.7`;独立 Python/MCP/dotenv 与外部工具由使用者准备。验证边界见 [RUST3-VERIFICATION.md](RUST3-VERIFICATION.md)。下文保留原 2.x 版本的历史范围。
|
||||||
|
|
||||||
---
|
---
|
||||||
name: artemis-device-test
|
name: artemis-device-test
|
||||||
description: 在 Artemis 任务中联合媒体、抓包、串口、V1/V2 设备协议和独立证据包执行家庭安防测试;手机使用本任务的 Android 工具,PC/网站使用已授权电脑控制插件。
|
description: 在 Artemis 任务中联合媒体、抓包、串口、V1/V2 设备协议和独立证据包执行家庭安防测试;手机使用本任务的 Android 工具,PC/网站使用已授权电脑控制插件。
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"plugin_id": "artemis-device-test",
|
||||||
|
"plugin_version": "0.5.0+artemis.6",
|
||||||
|
"content_sha256": "83f19450c4b0bdf4bad9389770e3ab35f81855d4e9f5d2975934e395308aabaf",
|
||||||
|
"traits": {
|
||||||
|
"uses_subprocesses": true,
|
||||||
|
"file_inputs_for_writes": true
|
||||||
|
},
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "原 Skill、协议资料与用例路由",
|
||||||
|
"paths": [
|
||||||
|
"skill"
|
||||||
|
],
|
||||||
|
"license": "LicenseRef-Yitong-Artemis-Market",
|
||||||
|
"permission": "permitted",
|
||||||
|
"license_file": "LICENSE-ORIGINAL-SKILL.md",
|
||||||
|
"source_file": "SOURCE.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ARTEMIS 适配层和适配修复",
|
||||||
|
"paths": [
|
||||||
|
"dependency_checks.py",
|
||||||
|
"dependency_runner.py",
|
||||||
|
"DEPLOYMENT.md",
|
||||||
|
"device_runtime.py",
|
||||||
|
"LICENSE",
|
||||||
|
"LICENSE-ORIGINAL-SKILL.md",
|
||||||
|
"network_inventory.py",
|
||||||
|
"plugin.json",
|
||||||
|
"README.md",
|
||||||
|
"RELEASE.md",
|
||||||
|
"requirements.txt",
|
||||||
|
"server.py",
|
||||||
|
"SKILL.md",
|
||||||
|
"source-manifest.json",
|
||||||
|
"SOURCE.md",
|
||||||
|
"VERIFICATION.md",
|
||||||
|
"skill/scripts/process_lifecycle.py",
|
||||||
|
"skill/tests/test_lifecycle_review.py",
|
||||||
|
"skill/tests/test_review_regressions.py",
|
||||||
|
"skill/ruff.toml"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"permission": "permitted",
|
||||||
|
"license_file": "LICENSE",
|
||||||
|
"source_file": "SOURCE.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"installation": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "实际宿主 API 安装、临时目录旧版升级和禁用/配置保留已验证;新装不自动授权。"
|
||||||
|
},
|
||||||
|
"tool_contract": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "真实 stdio MCP 握手和全部 20 项定义检查;8 次隔离安全调用、10 份附件;134 项单元回归。"
|
||||||
|
},
|
||||||
|
"failure_partial": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "清理核验故障及泛化 I/O 异常保留执行事实;未知写副作用不重试;缺失证据保持 inconclusive。"
|
||||||
|
},
|
||||||
|
"timeout_cancel": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "合成工具超时及真实 MCP 自检取消;取消后工具和后代约 0.03 秒退出,未操作真实设备。"
|
||||||
|
},
|
||||||
|
"process_tree": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "Windows Job 合成进程验证正常/非零/超时/超限/拥有者退出及清理异常;不声称其他系统通过。"
|
||||||
|
},
|
||||||
|
"file_binding": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "固件批准字节保护快照对抗原路径替换/截断/增长;V2 模拟协议;没有真实烧录。"
|
||||||
|
},
|
||||||
|
"resource_conflicts": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "临时锁目录验证 desktop:local 和 lab:device-test 各自互斥;运行时阻止固定版本更新和卸载。"
|
||||||
|
},
|
||||||
|
"evidence_export": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "正常宿主离线任务导入并校验摘要;8 份附件读回;Word/Excel/全部资料 ZIP 容器与附件字节核验。"
|
||||||
|
},
|
||||||
|
"history_uninstall": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "独立临时目录卸载后原历史附件字节仍可读;未删除用户任务或历史。"
|
||||||
|
},
|
||||||
|
"secret_redaction": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "合成凭据输出脱敏及源码审查通过;未将个人配置、原始报告、抓包、环境目录打包。"
|
||||||
|
},
|
||||||
|
"real_task": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "正常 ARTEMIS Pro/computer 离线证据任务完成,AI 正式结论 passed、5/5;暂停恢复实测;真实设备能力未测。"
|
||||||
|
},
|
||||||
|
"source_review": {
|
||||||
|
"status": "passed",
|
||||||
|
"evidence_file": "VERIFICATION.md",
|
||||||
|
"evidence_sha256": "4ca98fe306e266a491636bcd4f1de3fd792e452e765b73d658017ebb4084bf6a",
|
||||||
|
"notes": "原候选 ZIP 摘要、当前文件来源清单、未改动 vendor 摘要及公司市场分发确认记录已核对;适配层 Apache-2.0。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,336 +1,336 @@
|
|||||||
{
|
{
|
||||||
"schema_version": 2,
|
"schema_version": 2,
|
||||||
"id": "artemis-device-test",
|
"id": "artemis-device-test",
|
||||||
"name": "家庭安防多工具测试",
|
"name": "家庭安防多工具测试",
|
||||||
"version": "0.5.0+artemis.6",
|
"version": "0.5.0+artemis.7",
|
||||||
"description": "集成 artemis-device-test Skill:PATH/统一目录自动发现工具及匹配位数的 MediaInfo DLL、按能力独立自检、本机网卡枚举、357 条用例路由、媒体/抓包/串口、V1/V2 设备及联合证据。PC/网站复用视觉插件,手机沿用 Artemis;工具成功不等于用例通过。",
|
"description": "集成 artemis-device-test Skill:PATH/统一目录自动发现工具及匹配位数的 MediaInfo DLL、按能力独立自检、本机网卡枚举、357 条用例路由、媒体/抓包/串口、V1/V2 设备及联合证据。PC/网站复用视觉插件,手机沿用 Artemis;工具成功不等于用例通过。",
|
||||||
"artemis_version": ">=2.0.0,<3.0.0",
|
"artemis_version": ">=3.0.0,<4.0.0",
|
||||||
"platforms": [
|
"platforms": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"architectures": [
|
"architectures": [
|
||||||
"amd64"
|
"amd64"
|
||||||
],
|
],
|
||||||
"license": "LicenseRef-Yitong-Artemis-Market; Apache-2.0-Adapter",
|
"license": "LicenseRef-Yitong-Artemis-Market; Apache-2.0-Adapter",
|
||||||
"server": {
|
"server": {
|
||||||
"transport": "stdio",
|
"transport": "stdio",
|
||||||
"command": "${config.python}",
|
"command": "${config.python}",
|
||||||
"args": [
|
"args": [
|
||||||
"-B",
|
"-B",
|
||||||
"${plugin_dir}/server.py"
|
"${plugin_dir}/server.py"
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"ARTEMIS_DEVICE_TEST_PROFILE": "${config.profile}",
|
"ARTEMIS_DEVICE_TEST_PROFILE": "${config.profile}",
|
||||||
"ARTEMIS_DEVICE_TEST_TOOL_ROOT": "${config.tool_root}",
|
"ARTEMIS_DEVICE_TEST_TOOL_ROOT": "${config.tool_root}",
|
||||||
"ARTEMIS_DEVICE_TEST_ENV_FILE": "${config.rtsp_env_file}",
|
"ARTEMIS_DEVICE_TEST_ENV_FILE": "${config.rtsp_env_file}",
|
||||||
"ARTEMIS_DEVICE_TEST_TIMEOUT": "${config.operation_timeout_seconds}",
|
"ARTEMIS_DEVICE_TEST_TIMEOUT": "${config.operation_timeout_seconds}",
|
||||||
"ARTEMIS_DEVICE_TEST_ALLOW_FIRMWARE_WRITE": "${config.allow_firmware_write}"
|
"ARTEMIS_DEVICE_TEST_ALLOW_FIRMWARE_WRITE": "${config.allow_firmware_write}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"python": {
|
"python": {
|
||||||
"label": "Artemis Python 解释器",
|
"label": "插件 Python 解释器(独立准备)",
|
||||||
"description": "填写已安装 MCP 依赖的 Python 绝对路径。源码环境可使用项目 .venv/Scripts/python.exe。",
|
"description": "使用独立 Windows x64 Python,预先安装本插件 requirements.txt。Rust 主程序不提供 Python;安装插件不会运行 pip。",
|
||||||
"default": "python",
|
"default": "python",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"label": "本机工具 profile JSON",
|
"label": "本机工具 profile JSON",
|
||||||
"description": "可选,固定 tools/runtimes/libraries 路径;不保存凭据。留空时自动发现统一目录和宿主 PATH;显式错误路径不回退。",
|
"description": "可选,固定 tools/runtimes/libraries 路径;不保存凭据。留空时自动发现统一目录和宿主 PATH;显式错误路径不回退。",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"tool_root": {
|
"tool_root": {
|
||||||
"label": "已解压工具目录",
|
"label": "已解压工具目录",
|
||||||
"description": "可选,本机已解压工具目录绝对路径;自动发现 EXE/匹配位数的 MediaInfo.dll,以及 binwalk-python/Scripts/python.exe。不安装程序。",
|
"description": "可选,本机已解压工具目录绝对路径;自动发现 EXE/匹配位数的 MediaInfo.dll,以及 binwalk-python/Scripts/python.exe。不安装程序。",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"rtsp_env_file": {
|
"rtsp_env_file": {
|
||||||
"label": "本机 RTSP 环境文件",
|
"label": "本机 RTSP 环境文件",
|
||||||
"description": "可选,受控 .env 文件的绝对路径;仅按工具指定的环境变量名读取,不回显凭据。",
|
"description": "可选,受控 .env 文件的绝对路径;仅按工具指定的环境变量名读取,不回显凭据。",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"operation_timeout_seconds": {
|
"operation_timeout_seconds": {
|
||||||
"label": "单次操作预算(秒)",
|
"label": "单次操作预算(秒)",
|
||||||
"description": "默认 45。10..3300;增加前须将宿主 ARTEMIS_PLUGIN_TOOL_TIMEOUT_SECONDS 设得至少大 10 秒。任务总时限同样生效。",
|
"description": "默认45,范围10..3300;宿主插件工具时限至少大10秒,并受任务剩余预算约束。",
|
||||||
"default": "45"
|
"default": "45"
|
||||||
},
|
},
|
||||||
"allow_firmware_write": {
|
"allow_firmware_write": {
|
||||||
"label": "允许显式固件写入",
|
"label": "允许显式固件写入",
|
||||||
"description": "默认 false。仅当前用例明确授权升级时设置 true;工具还要求 execute、完整身份、ROM SHA-256 和审阅摘要。",
|
"description": "默认 false。仅当前用例明确授权升级时设置 true;工具还要求 execute、完整身份、ROM SHA-256 和审阅摘要。",
|
||||||
"default": "false"
|
"default": "false"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"skill": "SKILL.md",
|
"skill": "SKILL.md",
|
||||||
"targets": [
|
"targets": [
|
||||||
"android",
|
"android",
|
||||||
"mixed",
|
"mixed",
|
||||||
"computer"
|
"computer"
|
||||||
],
|
],
|
||||||
"resources": [
|
"resources": [
|
||||||
"desktop:local",
|
"desktop:local",
|
||||||
"lab:device-test"
|
"lab:device-test"
|
||||||
],
|
],
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"kind": "other",
|
"kind": "other",
|
||||||
"recording": "none",
|
"recording": "none",
|
||||||
"required_tools": [
|
"required_tools": [
|
||||||
"environment_doctor",
|
"environment_doctor",
|
||||||
"lookup_case",
|
"lookup_case",
|
||||||
"probe_media",
|
"probe_media",
|
||||||
"media_info",
|
"media_info",
|
||||||
"analyze_pcap",
|
"analyze_pcap",
|
||||||
"scan_ports",
|
"scan_ports",
|
||||||
"scan_firmware",
|
"scan_firmware",
|
||||||
"device_capabilities",
|
"device_capabilities",
|
||||||
"list_network_interfaces",
|
"list_network_interfaces",
|
||||||
"discover_devices",
|
"discover_devices",
|
||||||
"identify_device",
|
"identify_device",
|
||||||
"device_info",
|
"device_info",
|
||||||
"inspect_firmware",
|
"inspect_firmware",
|
||||||
"firmware_preflight",
|
"firmware_preflight",
|
||||||
"verify_firmware",
|
"verify_firmware",
|
||||||
"capture_packets",
|
"capture_packets",
|
||||||
"capture_serial",
|
"capture_serial",
|
||||||
"upgrade_firmware",
|
"upgrade_firmware",
|
||||||
"import_evidence",
|
"import_evidence",
|
||||||
"bundle_evidence"
|
"bundle_evidence"
|
||||||
],
|
],
|
||||||
"tools": {
|
"tools": {
|
||||||
"environment_doctor": {
|
"environment_doctor": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lookup_case": {
|
"lookup_case": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"probe_media": {
|
"probe_media": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "input_file"
|
"target_argument": "input_file"
|
||||||
},
|
},
|
||||||
"media_info": {
|
"media_info": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "input_file"
|
"target_argument": "input_file"
|
||||||
},
|
},
|
||||||
"analyze_pcap": {
|
"analyze_pcap": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "input_file"
|
"target_argument": "input_file"
|
||||||
},
|
},
|
||||||
"scan_ports": {
|
"scan_ports": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "host"
|
"target_argument": "host"
|
||||||
},
|
},
|
||||||
"scan_firmware": {
|
"scan_firmware": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "input_file"
|
"target_argument": "input_file"
|
||||||
},
|
},
|
||||||
"device_capabilities": {
|
"device_capabilities": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"list_network_interfaces": {
|
"list_network_interfaces": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"discover_devices": {
|
"discover_devices": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"identify_device": {
|
"identify_device": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "host"
|
"target_argument": "host"
|
||||||
},
|
},
|
||||||
"device_info": {
|
"device_info": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "host"
|
"target_argument": "host"
|
||||||
},
|
},
|
||||||
"inspect_firmware": {
|
"inspect_firmware": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "rom_file"
|
"target_argument": "rom_file"
|
||||||
},
|
},
|
||||||
"firmware_preflight": {
|
"firmware_preflight": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "host"
|
"target_argument": "host"
|
||||||
},
|
},
|
||||||
"verify_firmware": {
|
"verify_firmware": {
|
||||||
"effect": "read",
|
"effect": "read",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": true,
|
"idempotent": true,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "host"
|
"target_argument": "host"
|
||||||
},
|
},
|
||||||
"capture_packets": {
|
"capture_packets": {
|
||||||
"effect": "write",
|
"effect": "write",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": false,
|
"idempotent": false,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "host"
|
"target_argument": "host"
|
||||||
},
|
},
|
||||||
"capture_serial": {
|
"capture_serial": {
|
||||||
"effect": "write",
|
"effect": "write",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": false,
|
"idempotent": false,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "port"
|
"target_argument": "port"
|
||||||
},
|
},
|
||||||
"upgrade_firmware": {
|
"upgrade_firmware": {
|
||||||
"effect": "write",
|
"effect": "write",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": false,
|
"idempotent": false,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "host"
|
"target_argument": "host"
|
||||||
},
|
},
|
||||||
"import_evidence": {
|
"import_evidence": {
|
||||||
"effect": "write",
|
"effect": "write",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": false,
|
"idempotent": false,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "input_file"
|
"target_argument": "input_file"
|
||||||
},
|
},
|
||||||
"bundle_evidence": {
|
"bundle_evidence": {
|
||||||
"effect": "write",
|
"effect": "write",
|
||||||
"observation": false,
|
"observation": false,
|
||||||
"idempotent": false,
|
"idempotent": false,
|
||||||
"cancellation": "unknown",
|
"cancellation": "unknown",
|
||||||
"evidence": [
|
"evidence": [
|
||||||
"text",
|
"text",
|
||||||
"state",
|
"state",
|
||||||
"file"
|
"file"
|
||||||
],
|
],
|
||||||
"target_argument": "run_id"
|
"target_argument": "run_id"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
# 构建 0.38.0+artemis.1
|
# 构建 0.38.0+artemis.2
|
||||||
|
|
||||||
开发机需要 Python、Go、上游固定的 Rust 工具链、Windows C++ x64 工具链及对应 Spectre 运行库。用户安装插件 ZIP 不需要这些工具。不会运行 Cua 全局安装器。
|
本修订只调整 ARTEMIS 3.x 兼容声明与桥接握手版本,使用 `scripts/package_rust3_compat.py`。构建机需要 Python 3.12+ 标准库、官方 Windows amd64 便携 Go,以及已校验的 `.1` 插件 ZIP;最终用户无需这些开发工具。完整命令见仓库 `docs/rust3-compatibility.md`。
|
||||||
|
|
||||||
1. 获取官方 `cua-driver-rs-v0.38.0` 源码,核对 HEAD 为 `85e2b1318bd77ccf3cfa88ccdf875a4bb7eaed19`。保留完整源码;稀疏检出至少包括 `libs/cua-driver/rust`、`libs/cua`、`libs/images/sandbox-images.json`。
|
打包器核对已发布 `0.38.0+artemis.1` ZIP SHA256 为 `c007da16d58c4165a1a344113e650e68bd92a69ad3ce4ca8285818c5426dc0f4`,逐文件比较桥接源码、工具与补丁,仅允许版本常量变化。Driver、UIA helper、SDK DLL 和游标工具直接保留已发布字节;不运行 Cargo、不重新构建或下载底层驱动。
|
||||||
2. 在上游源码根目录应用本插件的 `patches/cua-driver-0.38.0-untitled-shell.patch`。补丁只修改 Windows 精确窗口解析,不放宽普通窗口发现。
|
|
||||||
3. 下载官方 Windows x64 ZIP。在具备上述编译依赖的终端,从 Artemis-Plugins 根目录执行:
|
|
||||||
|
|
||||||
```sh
|
Go 在独立缓存内运行测试、vet,并以 `CGO_ENABLED=0`、`-trimpath`、`-buildvcs=false` 构建新桥接程序。清单、包名、MCP 握手和来源记录统一为 `.2`。原 MIT 许可与 Go 标准库 BSD 许可随包保留。ZIP 条目使用固定顺序、时间与权限,拒绝覆盖既有候选。
|
||||||
python scripts/build_cua_driver.py <官方ZIP> --upstream-source <上游源码根目录> --go <Go可执行文件>
|
|
||||||
```
|
|
||||||
|
|
||||||
构建脚本校验官方 ZIP SHA256、上游 HEAD、唯一修改文件与补丁内容,使用 `cargo build --release --locked` 重新构建 Driver、UIA helper 和 SDK DLL,再运行 Go 测试并构建适配器。仅游标主题工具沿用官方原包二进制。
|
`provenance.json` 保留固定上游 `85e2b1318bd77ccf3cfa88ccdf875a4bb7eaed19`、官方 ZIP SHA256 和原兼容补丁信息,再记录复用包/二进制、新桥和源码树摘要。未提交源码明确标记 dirty,不能当作已经固定的新提交。`scripts/build_cua_driver.py` 仍是完整上游重建入口,本轮没有运行它。
|
||||||
|
|
||||||
`provenance.json` 区分官方 ZIP 来源、兼容补丁及重新构建文件,记录各自 SHA256。打包后先运行确定性测试及授权的隔离窗口回归,再安装到本机或发布。市场索引与发布附件需另行更新,不由构建脚本自动修改。
|
打包与 ZIP 回读不代表 Rust 宿主联调完成;当前范围见 [VERIFICATION.md](VERIFICATION.md)。不自动安装、授权、推送或发布。
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
# Cua 电脑控制 0.38.0+artemis.1
|
# Cua 电脑控制 0.38.0+artemis.2(3.0 候选)
|
||||||
|
|
||||||
|
2026-11-01:新增 ARTEMIS `>=3.0.0,<4.0.0` 专用不可变包。适配器仅更新版本常量,清单/MCP 握手统一为 `.2`;工具、窗口身份、坐标、焦点、回执和子进程行为保持 `.1` 实现。Driver、UIA、SDK DLL 和游标工具原样复用已校验的 `.1` 发行文件,不声称本次重新编译底层 Rust 运行库。来源细节见 `provenance.json` 与 [BUILD.md](BUILD.md)。
|
||||||
|
|
||||||
|
用户运行插件不需要 Python、Go、Rust 或 Node。现有 Windows x64、`desktop:local` 与 computer/mixed 范围不变;保留原许可和录制能力声明,但本轮不扩展或测试录像。旧 `.1` 包、发行附件及旧索引 `<3.0.0` 限制保留。新 `catalog-v5.json` 是待发布输入,实际宿主联调由主项目统一验证,不将以下 2.x 记录当作本次 3.0 结果。
|
||||||
|
|
||||||
|
## 固定运行库的历史来源:0.38.0+artemis.1
|
||||||
|
|
||||||
2026-10-27,与 ARTEMIS 2.2.0 配套发布。同步官方 [Cua Driver 0.38.0](https://github.com/trycua/cua/releases/tag/cua-driver-rs-v0.38.0),替换原 0.36.0 驱动。插件仍为 Windows x64 开发预览。既有任务与旧安装包保留原版本,主程序升级不会自动更新或授权插件;当前任务结束后在插件市场单独更新。
|
2026-10-27,与 ARTEMIS 2.2.0 配套发布。同步官方 [Cua Driver 0.38.0](https://github.com/trycua/cua/releases/tag/cua-driver-rs-v0.38.0),替换原 0.36.0 驱动。插件仍为 Windows x64 开发预览。既有任务与旧安装包保留原版本,主程序升级不会自动更新或授权插件;当前任务结束后在插件市场单独更新。
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# Cua 验证记录
|
# Cua 验证记录
|
||||||
|
|
||||||
|
## 0.38.0+artemis.2 / ARTEMIS 3.x 候选(2026-11-01)
|
||||||
|
|
||||||
|
仅变更宿主版本约束与 Go 桥接版本常量,工具定义和动作实现保留。打包器要求 Go test/vet/build 成功,核对 `.1` 原包 SHA256、源码差异、固定运行库与兼容补丁,再逐条回读最终 ZIP;实际执行凭据见候选目录 `build-receipt.json`。底层 Driver/UIA/SDK/游标工具复用已发布字节,没有在本轮重新构建。
|
||||||
|
|
||||||
|
本次没有运行 Rust 宿主握手/发现、真实截图/动作、页面、设备、录像、业务或性能测试。3.0 安装、授权与正常任务联调由主项目统一验证,用户在功能完成后集中验收页面。候选没有推送或发布。
|
||||||
|
|
||||||
|
以下为旧宿主和旧插件的历史记录,只说明复用实现的来源,不作为 3.0 测试结果。
|
||||||
|
|
||||||
## 0.38.0+artemis.1 发布核对(2026-10-27)
|
## 0.38.0+artemis.1 发布核对(2026-10-27)
|
||||||
|
|
||||||
重新执行 Go 测试、vet 和构建,核对最终包内源码、兼容补丁及运行库 SHA256。10 月 26 日主程序集中回归中,此插件参与仅 PC 输入、双击、清空和两轮 PC 到 Android 的校验码传递,均有两端独立证据;该组正式任务共 9 项通过,另有准备探索超出指定页面范围而失败,未计入通过数。模型统一 gpt-6-luna/high,未将历史 OCU 对照性能数据作为此次发行性能结论。
|
重新执行 Go 测试、vet 和构建,核对最终包内源码、兼容补丁及运行库 SHA256。10 月 26 日主程序集中回归中,此插件参与仅 PC 输入、双击、清空和两轮 PC 到 Android 的校验码传递,均有两端独立证据;该组正式任务共 9 项通过,另有准备探索超出指定页面范围而失败,未计入通过数。模型统一 gpt-6-luna/high,未将历史 OCU 对照性能数据作为此次发行性能结论。
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
//go:embed tools.json
|
//go:embed tools.json
|
||||||
var toolsJSON []byte
|
var toolsJSON []byte
|
||||||
|
|
||||||
const adapterVersion = "0.38.0+artemis.1"
|
const adapterVersion = "0.38.0+artemis.2"
|
||||||
|
|
||||||
type obj = map[string]any
|
type obj = map[string]any
|
||||||
type request struct {
|
type request struct {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
"schema_version": 2,
|
"schema_version": 2,
|
||||||
"id": "cua-driver",
|
"id": "cua-driver",
|
||||||
"name": "电脑控制 · Cua",
|
"name": "电脑控制 · Cua",
|
||||||
"version": "0.38.0+artemis.1",
|
"version": "0.38.0+artemis.2",
|
||||||
"description": "通过 Cua Driver 的窗口截图与可访问性树操作 Windows 应用及浏览器窗口;复用 ARTEMIS 的任务调度、步骤证据与录像。",
|
"description": "通过 Cua Driver 的窗口截图与可访问性树操作 Windows 应用及浏览器窗口;复用 ARTEMIS 的任务调度、步骤证据与录像。",
|
||||||
"artemis_version": ">=2.0.0,<3.0.0",
|
"artemis_version": ">=3.0.0,<4.0.0",
|
||||||
"platforms": [
|
"platforms": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 任务人工评审 1.1.0
|
# 任务人工评审 1.1.1
|
||||||
|
|
||||||
需要 ARTEMIS 2.4.0 或更高的 2.x 版本。升级主程序后,可在「插件 → 高级接入」导入本目录或发行 ZIP。2.2.x 不支持此接口。
|
本候选面向 ARTEMIS 3.x(`>=3.0.0,<4.0.0`),使用原有 schema 3 声明,不含 MCP、Python、脚本或运行进程。2.x 用户继续使用旧索引中的 1.1.0,不修改旧包兼容范围。
|
||||||
|
|
||||||
安装并启用后,下载的批量模板在原七列后附加“预置条件、操作步骤、预期结果”;也兼容旧七列表格。未启用插件时模板和校验仍保持七列。新增三列不进入 AI 提示词。
|
安装并启用后,下载的批量模板在原七列后附加“预置条件、操作步骤、预期结果”;也兼容旧七列表格。未启用插件时模板和校验仍保持七列。新增三列不进入 AI 提示词。
|
||||||
|
|
||||||
@@ -10,8 +10,10 @@
|
|||||||
|
|
||||||
`workspace.panels[].width` 声明首选、最小和最大宽度(CSS 像素);宿主根据播放器和屏幕空间调整布局,窄屏可低于声明的最小值以避免溢出。默认收起、左右独立展开;窄屏一次展开一侧;单侧展开始终保持在回放左边或右边。
|
`workspace.panels[].width` 声明首选、最小和最大宽度(CSS 像素);宿主根据播放器和屏幕空间调整布局,窄屏可低于声明的最小值以避免溢出。默认收起、左右独立展开;窄屏一次展开一侧;单侧展开始终保持在回放左边或右边。
|
||||||
|
|
||||||
在仓库根目录运行 `python scripts/build_task_review.py` 生成安装 ZIP 和 SHA256SUMS.txt。产物默认在 `artifacts/task-review/`,不提交构建产物。发行标签为 `task-review-v1.1.0`,安装包及 SHA256 以 catalog-v4.json 为准。
|
3.0 联合候选使用 `python scripts/package_rust3_compat.py --help` 所示入口,输出新 ZIP、SHA256 与未发布的 `catalog-v5.json`,详见仓库 `docs/rust3-compatibility.md`。独立声明式打包仍可运行 `python scripts/build_task_review.py --output <新的输出目录>`。计划发行标签为 `task-review-v1.1.1`;当前未发布,不能将索引中的计划 URL 当作已有附件。
|
||||||
|
|
||||||
回放标题栏的侧栏按钮控制左右展开;两项评审使用「通过 / 失败」分段按钮,再次点击已选项可清空。导出保留相同中文值。
|
回放标题栏的侧栏按钮控制左右展开;两项评审使用「通过 / 失败」分段按钮,再次点击已选项可清空。导出保留相同中文值。
|
||||||
|
|
||||||
Flash/Pro/探索及安卓、电脑、跨端任务均可显示面板,步骤和视频模式均支持。右侧上一条/下一条按同批次已结束任务的执行顺序切换;独立任务使用当前历史列表。上一条/下一条位于保存评审下方。选择结果后立即自动保存,备注停止输入 600 毫秒后保存;切换前先保存,失败或冲突时保留当前任务和输入,手动保存可重试。打开或读取任务不触发保存,面板展开状态保持。备注框缩为一行起始高度,可手动拉高。原字段 ID 不变,1.0.0 评审继续读取。
|
Flash/Pro/探索及安卓、电脑、跨端任务均可显示面板,步骤和视频模式均支持。右侧上一条/下一条按同批次已结束任务的执行顺序切换;独立任务使用当前历史列表。上一条/下一条位于保存评审下方。选择结果后立即自动保存,备注停止输入 600 毫秒后保存;切换前先保存,失败或冲突时保留当前任务和输入,手动保存可重试。打开或读取任务不触发保存,面板展开状态保持。备注框缩为一行起始高度,可手动拉高。四个稳定字段 ID 不变,3.x 同一原生数据库中的评审由宿主保留。
|
||||||
|
|
||||||
|
以上是插件声明与宿主应提供的工作区行为。3.0 实际页面、自动保存、导航及报告导出由主项目统一验收;本候选不把 2.4 的历史验证记为 3.0 已通过。不会导入 Python 历史数据。
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
# 任务人工评审 1.1.0
|
# 任务人工评审 1.1.1(3.0 候选)
|
||||||
|
|
||||||
需要 ARTEMIS 2.4.0+(2.x)。先更新宿主并重启,再通过「插件 → 高级接入」导入 task-review-1.1.0.zip 并启用。
|
使用新的不可变安装包支持 ARTEMIS `>=3.0.0,<4.0.0`。清单仍为 schema 3,原始用例/评审面板、四个字段 ID、中文选项、选填属性和报告声明保持不变,无额外运行依赖。
|
||||||
|
|
||||||
- 启用插件后,批量模板附加预置条件、操作步骤、预期结果。未启用时仍是七列;新增列不进入模型提示词。
|
旧 `task-review-v1.1.0` 附件与 `catalog-v4.json` 的 `<3.0.0` 限制继续保留,未通过修改旧索引宣称旧包兼容。新包仅进入 `catalog-v5.json`,安装和启用由用户操作,不自动迁移 Python 历史数据。
|
||||||
- 左侧展示原始用例,用例编号和标题置顶;历史缺失字段明确提示。
|
|
||||||
- 「AI执行准确性」改为「用例执行准确性」,保留字段 ID 和旧评审值。
|
|
||||||
- 备注框缩短;增加上一条、下一条,按钮位于保存评审下方;用户修改后自动保存,切换前保存,失败留在当前任务,连续修改不会被旧回执覆盖。
|
|
||||||
- 面板适用于所有执行模式、设备目标及步骤/视频视图。
|
|
||||||
|
|
||||||
发行标签为 task-review-v1.1.0。验证范围与结果见 [验证记录](VERIFICATION.md)。
|
计划标签 `task-review-v1.1.1`,当前是未发布候选。声明式接口已按 3.0 宿主协议准备,实际页面、保存/导航及报告导出待主项目本轮验证,见 [验证记录](VERIFICATION.md)。
|
||||||
@@ -1,40 +1,9 @@
|
|||||||
# 任务人工评审 1.1.0 验证
|
# 任务人工评审 1.1.1 / ARTEMIS 3.x 候选验证
|
||||||
|
|
||||||
日期:2026-10-28;宿主 ARTEMIS 2.4.0。本轮在 Windows 隔离环境完成。
|
日期:2026-11-01。清单为 schema 3,兼容范围 `>=3.0.0,<4.0.0`。保留原始用例、评审面板,以及 execution_accuracy、execution_note、result_correctness、result_note 四个字段,不新增运行进程、MCP、模型或设备能力。
|
||||||
|
|
||||||
- 宿主发布前最终默认确定性测试:3267 passed、2 skipped、8 deselected。
|
候选打包器检查版本、兼容范围、面板、稳定字段 ID、无 MCP 进程和零工具声明;产出新 ZIP 后逐条回读,记录最终 SHA256 和嵌入索引的清单一致性。实际执行结果以同目录 `build-receipt.json` 为准。
|
||||||
- 前端 ChromeHeadless:347 项通过;生产构建通过。
|
|
||||||
- 宿主 Ruff 格式/静态检查、质量基线、核心 Pyright、Git diff 空白检查通过。
|
|
||||||
- 新旧导入模板及插件启用门控、原始用例与 AI 提示词隔离、批次评审顺序通过。
|
|
||||||
- 结果选择立即自动保存、备注 600ms 防抖、清空、连续修改串行修订、旧回执与旧读取不覆盖新内容、关闭前保存、切换前保存、失败留在当前项通过。
|
|
||||||
- Windows Chrome 实际浏览器隔离夹具验证:视频视图中原始用例与评审面板;导航位于保存下方;自动保存、切换往返、宽屏/窄屏、深浅色;未出现页面脚本异常。
|
|
||||||
- 清单通过宿主 check_plugin.py 检查,字段 ID 保持不变。
|
|
||||||
- 2.4.0 通过独立 catalog-v4.json 获取 1.1.0;保留 catalog-v3.json 的 1.0.0,使用 2.3.0 的严格面板模型验证旧索引仍可解析。官方旧地址迁移与自定义地址保留的回归通过。
|
|
||||||
|
|
||||||
浏览器使用拦截 API 的示例数据,未填写真实任务的人工评审,未操作用户设备或调用模型。未做 macOS/Linux 实测,未运行原 192 项批次或高并发设备压力测试。
|
Rust 宿主提供声明式表单接口。实际页面、自动保存、清空、冲突处理、导航及 Word/Excel/资料包报告待主项目 3.0 本轮统一验证;本次未操作页面、设备或业务任务,也未做性能测试。人工结果仍只能由用户填写,不改变模型原判或任务状态。
|
||||||
|
|
||||||
以下为旧版历史记录,不能作为本轮验证结果。
|
1.1.0 在 ARTEMIS 2.4.0 的历史验证属于旧宿主,不作为 3.0 通过证据。旧发行包、标签及 catalog-v4.json 的 `<3.0.0` 约束保留;本次不迁移或兼容 Python 历史数据库。当前候选未推送、未发布。
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 任务人工评审 1.0.0 验证记录
|
|
||||||
|
|
||||||
日期:2026-10-27。发布目标:task-review-v1.0.0。
|
|
||||||
|
|
||||||
宿主:ARTEMIS 2.3.0。插件基于独立插件仓库 b780a60,未夹带其他未发布插件变更;清单最低宿主版本为 2.3.0,2.2.x 不支持。
|
|
||||||
|
|
||||||
## 已验证
|
|
||||||
|
|
||||||
- 宿主默认确定性测试:3197 passed,2 skipped,8 deselected。
|
|
||||||
- 前端 ChromeHeadless:336 项通过;生产构建通过。
|
|
||||||
- 宿主 Ruff 格式/静态检查、质量基线、核心 Pyright 通过。
|
|
||||||
- 清单和字段验证、仅启用插件贡献字段、纯界面插件不参与执行选择。
|
|
||||||
- 原始任务提示词 Markdown、部分填写、清空、失败重试保留输入、修订冲突、历史记录、切换任务不串写。
|
|
||||||
- 运行中只读,终态可评审;禁用/卸载隐藏贡献,重装恢复,删除任务/批次清理;复跑不继承。
|
|
||||||
- Word/Excel 单任务和批次汇总、ZIP 报告包含已启用贡献,未填为空;评审和插件状态变更使旧缓存失效。
|
|
||||||
- 使用生产前端与隔离示例任务进行浏览器验证:标题栏左右开关、普通浮窗/影院模式、浅色/深色、单侧在左右展开;600/740px 容器左右布局的浏览器回归通过。
|
|
||||||
- 实际通过网页保存中文「通过」,生成 Word/Excel 并确认值为中文,另一未填结果为空。
|
|
||||||
|
|
||||||
## 范围
|
|
||||||
|
|
||||||
验证使用 Windows 本地环境与临时数据库,不操作用户设备、业务应用或现有任务,不调用模型。未在 macOS/Linux 实测,不将此记录视为真实业务用例通过或市场发布证明。此插件无运行进程和 MCP,因此不需要设备动作、取消进程或录像验收。
|
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
"schema_version": 3,
|
"schema_version": 3,
|
||||||
"id": "task-review",
|
"id": "task-review",
|
||||||
"name": "任务人工评审",
|
"name": "任务人工评审",
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"description": "在任务回放中查看导入的原始用例,逐条填写用例执行准确性与测试结果正确性,按需填写备注并随报告导出。",
|
"description": "在任务回放中查看导入的原始用例,逐条填写用例执行准确性与测试结果正确性,按需填写备注并随报告导出。",
|
||||||
"artemis_version": ">=2.4.0,<3.0.0",
|
"artemis_version": ">=3.0.0,<4.0.0",
|
||||||
"platforms": [
|
"platforms": [
|
||||||
"win32",
|
"win32",
|
||||||
"linux",
|
"linux",
|
||||||
|
|||||||
@@ -0,0 +1,627 @@
|
|||||||
|
"""Assemble immutable Rust 3 plugin candidates from reviewed local inputs.
|
||||||
|
|
||||||
|
Only the small, dependency-free Go adapter is rebuilt. This script never runs
|
||||||
|
Cargo, npm, a Cua session, a device tool, an installer, or a network download.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
import stat
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path, PurePosixPath
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
CUA = ROOT / "plugins/cua-driver"
|
||||||
|
REVIEW = ROOT / "plugins/task-review"
|
||||||
|
DEVICE_TEST = ROOT / "plugins/artemis-device-test"
|
||||||
|
BASE_VERSION = "0.38.0+artemis.1"
|
||||||
|
CUA_VERSION = "0.38.0+artemis.2"
|
||||||
|
REVIEW_VERSION = "1.1.1"
|
||||||
|
DEVICE_TEST_VERSION = "0.5.0+artemis.7"
|
||||||
|
DEFAULT_RELEASE_TAG = "rust3-plugin-preview-20261101"
|
||||||
|
HOST_RANGE = ">=3.0.0,<4.0.0"
|
||||||
|
BASE_SHA256 = "c007da16d58c4165a1a344113e650e68bd92a69ad3ce4ca8285818c5426dc0f4"
|
||||||
|
RELEASES = "https://git.playones.com/chenmingxuan/Artemis-Plugins/releases/download"
|
||||||
|
BASE_URL = (
|
||||||
|
f"{RELEASES}/cua-driver-v{BASE_VERSION}/cua-driver-{BASE_VERSION}-windows-amd64.zip"
|
||||||
|
)
|
||||||
|
RUNTIME = (
|
||||||
|
"cua-driver.exe",
|
||||||
|
"cua-driver-uia.exe",
|
||||||
|
"cua_driver_sdk.dll",
|
||||||
|
"cua-cursor-theme.exe",
|
||||||
|
)
|
||||||
|
ZIP_DATE = (2026, 11, 1, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def sha256(data):
|
||||||
|
return hashlib.sha256(data).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def encoded(value):
|
||||||
|
return (json.dumps(value, ensure_ascii=False, indent=2) + "\n").encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def read_json(path):
|
||||||
|
return json.loads(path.read_text(encoding="utf-8-sig"))
|
||||||
|
|
||||||
|
|
||||||
|
def require(condition, message):
|
||||||
|
if not condition:
|
||||||
|
raise ValueError(message)
|
||||||
|
|
||||||
|
|
||||||
|
def file_bytes(path):
|
||||||
|
require(
|
||||||
|
path.is_file() and not path.is_symlink(),
|
||||||
|
f"Missing or linked input: {path.name}",
|
||||||
|
)
|
||||||
|
return path.read_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
def x64(data, name):
|
||||||
|
require(len(data) >= 64 and data[:2] == b"MZ", f"Not a PE binary: {name}")
|
||||||
|
offset = struct.unpack_from("<I", data, 0x3C)[0]
|
||||||
|
require(
|
||||||
|
offset + 6 <= len(data) and data[offset : offset + 4] == b"PE\0\0",
|
||||||
|
f"Invalid PE: {name}",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
struct.unpack_from("<H", data, offset + 4)[0] == 0x8664,
|
||||||
|
f"Not Windows x64: {name}",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def exclusive(path, data):
|
||||||
|
with path.open("xb") as destination:
|
||||||
|
destination.write(data)
|
||||||
|
|
||||||
|
|
||||||
|
def write_zip(path, entries):
|
||||||
|
require(not path.exists(), f"Refusing to overwrite an archive: {path.name}")
|
||||||
|
with zipfile.ZipFile(path, "x", compression=zipfile.ZIP_DEFLATED) as archive:
|
||||||
|
for name, data in sorted(entries.items()):
|
||||||
|
parts = PurePosixPath(name)
|
||||||
|
require(
|
||||||
|
not parts.is_absolute()
|
||||||
|
and ".." not in parts.parts
|
||||||
|
and "\\" not in name,
|
||||||
|
"Unsafe archive entry",
|
||||||
|
)
|
||||||
|
info = zipfile.ZipInfo(name, date_time=ZIP_DATE)
|
||||||
|
info.create_system = 3
|
||||||
|
info.compress_type = zipfile.ZIP_DEFLATED
|
||||||
|
info.external_attr = 0o100644 << 16
|
||||||
|
archive.writestr(info, data)
|
||||||
|
with zipfile.ZipFile(path) as archive:
|
||||||
|
require(
|
||||||
|
archive.testzip() is None and set(archive.namelist()) == set(entries),
|
||||||
|
"Archive readback failed",
|
||||||
|
)
|
||||||
|
for name, data in entries.items():
|
||||||
|
require(
|
||||||
|
sha256(archive.read(name)) == sha256(data),
|
||||||
|
f"Archive input differs: {name}",
|
||||||
|
)
|
||||||
|
return sha256(path.read_bytes())
|
||||||
|
|
||||||
|
|
||||||
|
def source_files(directory):
|
||||||
|
files = sorted(directory.glob("*.go"))
|
||||||
|
files += [directory / "go.mod", directory / "tools.json"]
|
||||||
|
files += sorted((directory / "patches").glob("*.patch"))
|
||||||
|
return {path.relative_to(directory).as_posix(): file_bytes(path) for path in files}
|
||||||
|
|
||||||
|
|
||||||
|
def source_digest(files):
|
||||||
|
return sha256(encoded({name: sha256(data) for name, data in sorted(files.items())}))
|
||||||
|
|
||||||
|
|
||||||
|
def release_url(tag, filename):
|
||||||
|
require(
|
||||||
|
isinstance(tag, str)
|
||||||
|
and re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._+-]{0,127}", tag),
|
||||||
|
"Release tag must be a single safe tag name",
|
||||||
|
)
|
||||||
|
require(PurePosixPath(filename).name == filename, "Invalid release filename")
|
||||||
|
return f"{RELEASES}/{quote(tag, safe='')}/{quote(filename, safe='-._+')}"
|
||||||
|
|
||||||
|
|
||||||
|
def device_test_files(directory):
|
||||||
|
"""Freeze distributable bytes; the root SDK preflight owns market admission."""
|
||||||
|
require(
|
||||||
|
not directory.is_symlink() and not directory.is_junction(), "Linked plugin root"
|
||||||
|
)
|
||||||
|
directory = directory.resolve(strict=True)
|
||||||
|
require(directory.is_dir(), "Device-test source must be a directory")
|
||||||
|
entries, seen, total = {}, set(), 0
|
||||||
|
forbidden = {
|
||||||
|
"__pycache__",
|
||||||
|
".git",
|
||||||
|
"node_modules",
|
||||||
|
".venv",
|
||||||
|
"venv",
|
||||||
|
".pytest_cache",
|
||||||
|
".ruff_cache",
|
||||||
|
}
|
||||||
|
for path in sorted(directory.rglob("*")):
|
||||||
|
name = path.relative_to(directory).as_posix()
|
||||||
|
require(
|
||||||
|
not path.is_symlink()
|
||||||
|
and not path.is_junction()
|
||||||
|
and path.resolve().is_relative_to(directory),
|
||||||
|
f"Linked device-test input: {name}",
|
||||||
|
)
|
||||||
|
require(name.casefold() not in seen, "Case-colliding device-test inputs")
|
||||||
|
seen.add(name.casefold())
|
||||||
|
require(len(seen) <= 20000, "Too many device-test inputs")
|
||||||
|
require(
|
||||||
|
not any(
|
||||||
|
part.casefold() in forbidden
|
||||||
|
for part in path.relative_to(directory).parts
|
||||||
|
),
|
||||||
|
f"Non-distributable device-test input: {name}",
|
||||||
|
)
|
||||||
|
if path.is_dir():
|
||||||
|
continue
|
||||||
|
lower = path.name.casefold()
|
||||||
|
require(
|
||||||
|
stat.S_ISREG(path.stat().st_mode)
|
||||||
|
and not lower.endswith((".pyc", ".pyo"))
|
||||||
|
and lower != ".env"
|
||||||
|
and not (
|
||||||
|
lower.startswith(".env.")
|
||||||
|
and not lower.endswith((".example", ".sample", ".template"))
|
||||||
|
),
|
||||||
|
f"Non-distributable device-test file: {name}",
|
||||||
|
)
|
||||||
|
data = file_bytes(path)
|
||||||
|
total += len(data)
|
||||||
|
require(total <= 512 * 1024 * 1024, "Device-test source exceeds size limit")
|
||||||
|
entries[name] = data
|
||||||
|
for name in (
|
||||||
|
"plugin.json",
|
||||||
|
"LICENSE",
|
||||||
|
"SOURCE.md",
|
||||||
|
"MARKET.json",
|
||||||
|
"VERIFICATION.md",
|
||||||
|
):
|
||||||
|
require(
|
||||||
|
bool(entries.get(name)),
|
||||||
|
f"Missing device-test distribution material: {name}",
|
||||||
|
)
|
||||||
|
manifest = json.loads(entries["plugin.json"])
|
||||||
|
require(
|
||||||
|
manifest["id"] == "artemis-device-test"
|
||||||
|
and manifest["version"] == DEVICE_TEST_VERSION
|
||||||
|
and manifest["artemis_version"] == HOST_RANGE,
|
||||||
|
"Unexpected device-test identity or compatibility",
|
||||||
|
)
|
||||||
|
require(bool(manifest.get("license", "").strip()), "Missing device-test license")
|
||||||
|
market = json.loads(entries["MARKET.json"])
|
||||||
|
hashes = {
|
||||||
|
name: sha256(data) for name, data in entries.items() if name != "MARKET.json"
|
||||||
|
}
|
||||||
|
fingerprint = sha256(
|
||||||
|
json.dumps(
|
||||||
|
hashes, ensure_ascii=False, sort_keys=True, separators=(",", ":")
|
||||||
|
).encode("utf-8")
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
market["plugin_id"] == manifest["id"]
|
||||||
|
and market["plugin_version"] == manifest["version"]
|
||||||
|
and market["content_sha256"] == fingerprint,
|
||||||
|
"Device-test MARKET identity or content hash differs; rerun SDK preflight",
|
||||||
|
)
|
||||||
|
return manifest, entries
|
||||||
|
|
||||||
|
|
||||||
|
def bundle_map(entries, filenames, catalog):
|
||||||
|
return {
|
||||||
|
"schema_version": 1,
|
||||||
|
"catalog": "catalog-v5.json",
|
||||||
|
"catalog_sha256": sha256(catalog),
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"id": entry["manifest"]["id"],
|
||||||
|
"version": entry["manifest"]["version"],
|
||||||
|
"file": filename,
|
||||||
|
"archive_url": entry["archive_url"],
|
||||||
|
"sha256": entry["sha256"],
|
||||||
|
}
|
||||||
|
for entry, filename in zip(entries, filenames, strict=True)
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def check_contract(cua, review, sources):
|
||||||
|
require(
|
||||||
|
cua["id"] == "cua-driver" and cua["version"] == CUA_VERSION,
|
||||||
|
"Unexpected Cua version",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
review["id"] == "task-review" and review["version"] == REVIEW_VERSION,
|
||||||
|
"Unexpected review version",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
cua["artemis_version"] == review["artemis_version"] == HOST_RANGE,
|
||||||
|
"Host compatibility differs",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
cua["server"]
|
||||||
|
== {
|
||||||
|
"transport": "stdio",
|
||||||
|
"command": "${plugin_dir}/bin/artemis-cua-adapter.exe",
|
||||||
|
},
|
||||||
|
"Unexpected Cua startup",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
cua["resources"] == ["desktop:local"]
|
||||||
|
and cua["targets"] == ["computer", "mixed"],
|
||||||
|
"Cua resource scope differs",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
re.search(
|
||||||
|
rb'const adapterVersion = "' + re.escape(CUA_VERSION.encode()) + rb'"',
|
||||||
|
sources["main.go"],
|
||||||
|
),
|
||||||
|
"MCP handshake version differs",
|
||||||
|
)
|
||||||
|
tools = json.loads(sources["tools.json"])
|
||||||
|
actual = {tool["name"]: tool for tool in tools}
|
||||||
|
declared = cua["capabilities"]["tools"]
|
||||||
|
require(
|
||||||
|
len(actual) == len(tools) and set(actual) == set(declared),
|
||||||
|
"Tool manifest differs from embedded catalog",
|
||||||
|
)
|
||||||
|
for name, spec in declared.items():
|
||||||
|
tool = actual[name]
|
||||||
|
require(
|
||||||
|
not spec.get("observation")
|
||||||
|
or tool.get("annotations", {}).get("readOnlyHint") is True,
|
||||||
|
"Observation is not read-only",
|
||||||
|
)
|
||||||
|
target = spec.get("target_argument")
|
||||||
|
require(
|
||||||
|
not target or target in tool["inputSchema"].get("properties", {}),
|
||||||
|
"Target argument is absent",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
review["schema_version"] == 3 and review["targets"] == [],
|
||||||
|
"Review must remain declarative",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
not review.get("server")
|
||||||
|
and not review.get("skill")
|
||||||
|
and not review["capabilities"]["tools"],
|
||||||
|
"Review must not start a process",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
{p["kind"] for p in review["workspace"]["panels"]} == {"original_case", "form"},
|
||||||
|
"Review panels differ",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
[f["id"] for f in review["workspace"]["fields"]]
|
||||||
|
== [
|
||||||
|
"execution_accuracy",
|
||||||
|
"execution_note",
|
||||||
|
"result_correctness",
|
||||||
|
"result_note",
|
||||||
|
],
|
||||||
|
"Review field IDs differ",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def base_runtime(path, sources):
|
||||||
|
data = file_bytes(path)
|
||||||
|
require(sha256(data) == BASE_SHA256, "Published Cua base archive hash mismatch")
|
||||||
|
with zipfile.ZipFile(io.BytesIO(data)) as base:
|
||||||
|
names = base.namelist()
|
||||||
|
require(len(names) == len(set(names)), "Duplicate base archive entries")
|
||||||
|
require(
|
||||||
|
all(
|
||||||
|
not PurePosixPath(n).is_absolute()
|
||||||
|
and ".." not in PurePosixPath(n).parts
|
||||||
|
and "\\" not in n
|
||||||
|
for n in names
|
||||||
|
),
|
||||||
|
"Unsafe base archive path",
|
||||||
|
)
|
||||||
|
manifest = json.loads(base.read("plugin.json"))
|
||||||
|
require(
|
||||||
|
manifest["version"] == BASE_VERSION and manifest["id"] == "cua-driver",
|
||||||
|
"Wrong published base version",
|
||||||
|
)
|
||||||
|
for name, content in sources.items():
|
||||||
|
previous = base.read("source/" + name).replace(b"\r\n", b"\n")
|
||||||
|
expected = content.replace(b"\r\n", b"\n")
|
||||||
|
if name == "main.go":
|
||||||
|
expected = expected.replace(CUA_VERSION.encode(), BASE_VERSION.encode())
|
||||||
|
require(
|
||||||
|
previous == expected,
|
||||||
|
f"Go behavior differs from the reviewed base: {name}",
|
||||||
|
)
|
||||||
|
runtime = {"bin/" + name: base.read("bin/" + name) for name in RUNTIME}
|
||||||
|
provenance = json.loads(base.read("provenance.json"))
|
||||||
|
require(
|
||||||
|
provenance["plugin_version"] == BASE_VERSION,
|
||||||
|
"Published provenance version differs",
|
||||||
|
)
|
||||||
|
for name, expected in provenance["compatibility_patch"][
|
||||||
|
"rebuilt_binaries"
|
||||||
|
].items():
|
||||||
|
require(
|
||||||
|
sha256(runtime["bin/" + name]) == expected,
|
||||||
|
"Published runtime differs from provenance",
|
||||||
|
)
|
||||||
|
license_text = base.read("LICENSE.upstream")
|
||||||
|
require(
|
||||||
|
license_text == file_bytes(CUA / "LICENSE.upstream"),
|
||||||
|
"Upstream license changed",
|
||||||
|
)
|
||||||
|
for name, binary in runtime.items():
|
||||||
|
x64(binary, name)
|
||||||
|
return runtime, provenance
|
||||||
|
|
||||||
|
|
||||||
|
def build_adapter(go, output):
|
||||||
|
build = output / ".build"
|
||||||
|
env = {
|
||||||
|
**os.environ,
|
||||||
|
"CGO_ENABLED": "0",
|
||||||
|
"GOOS": "windows",
|
||||||
|
"GOARCH": "amd64",
|
||||||
|
"GOTOOLCHAIN": "local",
|
||||||
|
"GOENV": "off",
|
||||||
|
"GOPROXY": "off",
|
||||||
|
"GOSUMDB": "off",
|
||||||
|
"GOTELEMETRY": "off",
|
||||||
|
"GOFLAGS": "-mod=readonly",
|
||||||
|
}
|
||||||
|
for key, name in (
|
||||||
|
("GOCACHE", "cache"),
|
||||||
|
("GOMODCACHE", "modules"),
|
||||||
|
("GOPATH", "path"),
|
||||||
|
("GOTMPDIR", "temp"),
|
||||||
|
):
|
||||||
|
folder = build / name
|
||||||
|
folder.mkdir(parents=True, exist_ok=True)
|
||||||
|
env[key] = str(folder)
|
||||||
|
version = subprocess.check_output(
|
||||||
|
[str(go), "version"], env=env, text=True, timeout=30
|
||||||
|
).strip()
|
||||||
|
require(
|
||||||
|
re.fullmatch(r"go version go[0-9.]+ windows/amd64", version),
|
||||||
|
"Expected native, stable Windows amd64 Go",
|
||||||
|
)
|
||||||
|
commands = (
|
||||||
|
["test", "-count=1", "./..."],
|
||||||
|
["vet", "./..."],
|
||||||
|
[
|
||||||
|
"build",
|
||||||
|
"-trimpath",
|
||||||
|
"-buildvcs=false",
|
||||||
|
"-ldflags=-s -w",
|
||||||
|
"-o",
|
||||||
|
str(build / "artemis-cua-adapter.exe"),
|
||||||
|
".",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
for command in commands:
|
||||||
|
subprocess.run([str(go), *command], cwd=CUA, env=env, check=True, timeout=300)
|
||||||
|
binary = file_bytes(build / "artemis-cua-adapter.exe")
|
||||||
|
x64(binary, "artemis-cua-adapter.exe")
|
||||||
|
go_license = file_bytes(go.parent.parent / "LICENSE")
|
||||||
|
return binary, go_license, version
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--cua-base-archive", type=Path, required=True)
|
||||||
|
parser.add_argument("--go", type=Path, required=True)
|
||||||
|
parser.add_argument(
|
||||||
|
"--output",
|
||||||
|
type=Path,
|
||||||
|
required=True,
|
||||||
|
help="New empty candidate directory; never overwrite an earlier candidate",
|
||||||
|
)
|
||||||
|
parser.add_argument("--catalog-output", type=Path, default=ROOT / "catalog-v5.json")
|
||||||
|
parser.add_argument("--device-test-source", type=Path, default=DEVICE_TEST)
|
||||||
|
parser.add_argument("--release-tag", default=DEFAULT_RELEASE_TAG)
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
release_url(args.release_tag, "preflight.zip")
|
||||||
|
output = args.output.resolve()
|
||||||
|
require(
|
||||||
|
not output.exists() or not any(output.iterdir()),
|
||||||
|
"Candidate output must be new or empty",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
not args.catalog_output.exists(),
|
||||||
|
"Refusing to overwrite catalog-v5; use a new --catalog-output for a later candidate",
|
||||||
|
)
|
||||||
|
cua, review = read_json(CUA / "plugin.json"), read_json(REVIEW / "plugin.json")
|
||||||
|
sources = source_files(CUA)
|
||||||
|
check_contract(cua, review, sources)
|
||||||
|
device_test, device_entries = device_test_files(args.device_test_source)
|
||||||
|
runtime, previous = base_runtime(args.cua_base_archive.resolve(), sources)
|
||||||
|
output.mkdir(parents=True, exist_ok=True)
|
||||||
|
adapter, go_license, go_version = build_adapter(args.go.resolve(), output)
|
||||||
|
require(source_files(CUA) == sources, "Adapter source changed during build")
|
||||||
|
require(
|
||||||
|
device_test_files(args.device_test_source) == (device_test, device_entries),
|
||||||
|
"Device-test source changed during build",
|
||||||
|
)
|
||||||
|
head = subprocess.check_output(
|
||||||
|
["git", "rev-parse", "HEAD"], cwd=ROOT, text=True
|
||||||
|
).strip()
|
||||||
|
dirty = bool(
|
||||||
|
subprocess.check_output(
|
||||||
|
["git", "status", "--porcelain=v1", "--untracked-files=normal"], cwd=ROOT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
provenance = dict(previous)
|
||||||
|
provenance.update(
|
||||||
|
{
|
||||||
|
"plugin_version": CUA_VERSION,
|
||||||
|
"adapter_revision": 2,
|
||||||
|
"host_compatibility": HOST_RANGE,
|
||||||
|
"runtime_reuse": {
|
||||||
|
"plugin_version": BASE_VERSION,
|
||||||
|
"archive_url": BASE_URL,
|
||||||
|
"archive_sha256": BASE_SHA256,
|
||||||
|
"binaries": {name: sha256(data) for name, data in runtime.items()},
|
||||||
|
},
|
||||||
|
"adapter_build": {
|
||||||
|
"go_version": go_version,
|
||||||
|
"cgo_enabled": False,
|
||||||
|
"sha256": sha256(adapter),
|
||||||
|
"source_files_sha256": source_digest(sources),
|
||||||
|
"checkout_commit_at_build": head,
|
||||||
|
"checkout_dirty": dirty,
|
||||||
|
"fixed_source_commit_claimed": False,
|
||||||
|
},
|
||||||
|
"verification_scope": "Go test/vet/build and archive contracts only. No Rust host handshake, UI, recording, device, business or performance validation in this build.",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
cua_entries = dict(runtime)
|
||||||
|
cua_entries.update(
|
||||||
|
{
|
||||||
|
"bin/artemis-cua-adapter.exe": adapter,
|
||||||
|
"LICENSE.Go": go_license,
|
||||||
|
"provenance.json": encoded(provenance),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for name in (
|
||||||
|
"plugin.json",
|
||||||
|
"SKILL.md",
|
||||||
|
"LICENSE.upstream",
|
||||||
|
"RELEASE.md",
|
||||||
|
"VERIFICATION.md",
|
||||||
|
"BUILD.md",
|
||||||
|
):
|
||||||
|
cua_entries[name] = file_bytes(CUA / name)
|
||||||
|
cua_entries.update({"source/" + name: data for name, data in sources.items()})
|
||||||
|
review_entries = {
|
||||||
|
name: file_bytes(REVIEW / name)
|
||||||
|
for name in (
|
||||||
|
"plugin.json",
|
||||||
|
"README.md",
|
||||||
|
"LICENSE",
|
||||||
|
"VERIFICATION.md",
|
||||||
|
"RELEASE.md",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
require(
|
||||||
|
json.loads(cua_entries["plugin.json"]) == cua
|
||||||
|
and json.loads(review_entries["plugin.json"]) == review,
|
||||||
|
"Manifest changed during build",
|
||||||
|
)
|
||||||
|
cua_name = f"cua-driver-{CUA_VERSION}-windows-amd64.zip"
|
||||||
|
review_name = f"task-review-{REVIEW_VERSION}.zip"
|
||||||
|
device_name = f"artemis-device-test-{DEVICE_TEST_VERSION}-windows-amd64.zip"
|
||||||
|
cua_sha = write_zip(output / cua_name, cua_entries)
|
||||||
|
review_sha = write_zip(output / review_name, review_entries)
|
||||||
|
device_sha = write_zip(output / device_name, device_entries)
|
||||||
|
sums = f"{cua_sha} {cua_name}\n{review_sha} {review_name}\n{device_sha} {device_name}\n".encode(
|
||||||
|
"ascii"
|
||||||
|
)
|
||||||
|
exclusive(output / "SHA256SUMS.txt", sums)
|
||||||
|
entries = [
|
||||||
|
{
|
||||||
|
"manifest": cua,
|
||||||
|
"release_stage": "preview",
|
||||||
|
"archive_url": release_url(args.release_tag, cua_name),
|
||||||
|
"sha256": cua_sha,
|
||||||
|
"verification": "Windows x64 / ARTEMIS 3.x 候选。Go 确定性回归、vet、桥接构建和最终 ZIP 输入/回读校验通过;Driver/UIA/SDK/游标工具原样复用已发布 0.38.0+artemis.1 的固定文件与来源。3.0 实际握手、窗口发现、截图、动作、录像及业务任务待宿主统一验收;未进行性能测试。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"manifest": review,
|
||||||
|
"release_stage": "preview",
|
||||||
|
"archive_url": release_url(args.release_tag, review_name),
|
||||||
|
"sha256": review_sha,
|
||||||
|
"verification": "ARTEMIS 3.x 声明式工作区候选,无运行进程或额外依赖。清单字段/面板及最终 ZIP 回读校验通过;实际界面、自动保存、导航与报告导出待 3.0 宿主统一验收,不沿用 2.4 的历史测试结论。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"manifest": device_test,
|
||||||
|
"release_stage": "preview",
|
||||||
|
"archive_url": release_url(args.release_tag, device_name),
|
||||||
|
"sha256": device_sha,
|
||||||
|
"verification": "ARTEMIS 3.x Windows x64 候选;独立 Python/MCP/dotenv 与外部工具由使用者准备。固定源码、MARKET 内容摘要和最终 ZIP 回读校验;完整许可及证据准入由 SDK 独立预检。Rust 安装、握手、取消、设备与业务验收范围以包内验证记录为准,不沿用旧宿主结论。",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
catalog = encoded({"schema_version": 1, "plugins": entries})
|
||||||
|
exclusive(output / "catalog-v5.candidate.json", catalog)
|
||||||
|
exclusive(output / "catalog-v5.json", catalog)
|
||||||
|
mapping = encoded(
|
||||||
|
bundle_map(entries, [cua_name, review_name, device_name], catalog)
|
||||||
|
)
|
||||||
|
exclusive(output / "bundle-map.json", mapping)
|
||||||
|
exclusive(args.catalog_output.resolve(), catalog)
|
||||||
|
receipt = {
|
||||||
|
"schema_version": 1,
|
||||||
|
"candidate_only": True,
|
||||||
|
"published": False,
|
||||||
|
"release_tag": args.release_tag,
|
||||||
|
"python_version": platform.python_version(),
|
||||||
|
"go_version": go_version,
|
||||||
|
"source_commit_at_build": head,
|
||||||
|
"source_dirty": dirty,
|
||||||
|
"adapter_source_sha256": source_digest(sources),
|
||||||
|
"device_test_source_sha256": source_digest(device_entries),
|
||||||
|
"base_archive_sha256": BASE_SHA256,
|
||||||
|
"archives": [
|
||||||
|
{"file": cua_name, "sha256": cua_sha},
|
||||||
|
{"file": review_name, "sha256": review_sha},
|
||||||
|
{"file": device_name, "sha256": device_sha},
|
||||||
|
],
|
||||||
|
"catalog_sha256": sha256(catalog),
|
||||||
|
"bundle_map_sha256": sha256(mapping),
|
||||||
|
"checks": [
|
||||||
|
"go test -count=1 ./...",
|
||||||
|
"go vet ./...",
|
||||||
|
"go build",
|
||||||
|
"manifest/tools/source/runtime contract",
|
||||||
|
"final ZIP readback",
|
||||||
|
"device-test MARKET identity/content hash (SDK admission is separate)",
|
||||||
|
],
|
||||||
|
"not_run": [
|
||||||
|
"Cargo",
|
||||||
|
"npm",
|
||||||
|
"Rust host handshake",
|
||||||
|
"device/UI/recording/business/performance tests",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
exclusive(output / "build-receipt.json", encoded(receipt))
|
||||||
|
print(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"candidate_directory": str(output),
|
||||||
|
"archives": receipt["archives"],
|
||||||
|
"catalog": str(args.catalog_output.resolve()),
|
||||||
|
"published": False,
|
||||||
|
},
|
||||||
|
ensure_ascii=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except (
|
||||||
|
ValueError,
|
||||||
|
OSError,
|
||||||
|
subprocess.SubprocessError,
|
||||||
|
zipfile.BadZipFile,
|
||||||
|
KeyError,
|
||||||
|
) as error:
|
||||||
|
sys.exit(f"Candidate assembly failed: {error}")
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
"""Offline packager regressions; Go compilation and SDK admission are separate."""
|
||||||
|
|
||||||
|
import importlib.util
|
||||||
|
import json
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
SPEC = importlib.util.spec_from_file_location(
|
||||||
|
"package_rust3_compat",
|
||||||
|
Path(__file__).resolve().parents[1] / "scripts/package_rust3_compat.py",
|
||||||
|
)
|
||||||
|
packager = importlib.util.module_from_spec(SPEC)
|
||||||
|
SPEC.loader.exec_module(packager)
|
||||||
|
|
||||||
|
|
||||||
|
class Rust3PackagerTests(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.temporary = tempfile.TemporaryDirectory()
|
||||||
|
self.addCleanup(self.temporary.cleanup)
|
||||||
|
self.root = Path(self.temporary.name)
|
||||||
|
self.plugin = self.root / "device-test"
|
||||||
|
self.plugin.mkdir()
|
||||||
|
self.manifest = {
|
||||||
|
"schema_version": 2,
|
||||||
|
"id": "artemis-device-test",
|
||||||
|
"version": packager.DEVICE_TEST_VERSION,
|
||||||
|
"artemis_version": packager.HOST_RANGE,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
}
|
||||||
|
(self.plugin / "plugin.json").write_bytes(packager.encoded(self.manifest))
|
||||||
|
for name in ("LICENSE", "SOURCE.md", "VERIFICATION.md", "server.py"):
|
||||||
|
(self.plugin / name).write_bytes((name + "\n").encode())
|
||||||
|
self.update_market()
|
||||||
|
|
||||||
|
def update_market(self):
|
||||||
|
hashes = {
|
||||||
|
path.relative_to(self.plugin).as_posix(): packager.sha256(path.read_bytes())
|
||||||
|
for path in self.plugin.rglob("*")
|
||||||
|
if path.is_file() and path.name != "MARKET.json"
|
||||||
|
}
|
||||||
|
fingerprint = packager.sha256(
|
||||||
|
json.dumps(
|
||||||
|
hashes, ensure_ascii=False, sort_keys=True, separators=(",", ":")
|
||||||
|
).encode()
|
||||||
|
)
|
||||||
|
(self.plugin / "MARKET.json").write_bytes(
|
||||||
|
packager.encoded(
|
||||||
|
{
|
||||||
|
"plugin_id": self.manifest["id"],
|
||||||
|
"plugin_version": self.manifest["version"],
|
||||||
|
"content_sha256": fingerprint,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def assemble(self, name, tag=packager.DEFAULT_RELEASE_TAG):
|
||||||
|
output = self.root / name
|
||||||
|
catalog = self.root / (name + "-catalog.json")
|
||||||
|
with (
|
||||||
|
patch.object(
|
||||||
|
packager,
|
||||||
|
"base_runtime",
|
||||||
|
return_value=({}, {"plugin_version": packager.BASE_VERSION}),
|
||||||
|
),
|
||||||
|
patch.object(
|
||||||
|
packager,
|
||||||
|
"build_adapter",
|
||||||
|
return_value=(b"adapter fixture", b"Go license fixture", "go fixture"),
|
||||||
|
),
|
||||||
|
patch.object(
|
||||||
|
packager.subprocess, "check_output", side_effect=["f" * 40, b""]
|
||||||
|
),
|
||||||
|
patch("builtins.print"),
|
||||||
|
):
|
||||||
|
packager.main(
|
||||||
|
[
|
||||||
|
"--cua-base-archive",
|
||||||
|
str(self.root / "base.zip"),
|
||||||
|
"--go",
|
||||||
|
str(self.root / "go.exe"),
|
||||||
|
"--output",
|
||||||
|
str(output),
|
||||||
|
"--catalog-output",
|
||||||
|
str(catalog),
|
||||||
|
"--device-test-source",
|
||||||
|
str(self.plugin),
|
||||||
|
"--release-tag",
|
||||||
|
tag,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return output, catalog
|
||||||
|
|
||||||
|
def test_three_archives_catalog_and_bundle_bind_the_same_final_bytes(self):
|
||||||
|
output, catalog_path = self.assemble("candidate")
|
||||||
|
catalog = json.loads(catalog_path.read_bytes())
|
||||||
|
mapping = json.loads((output / "bundle-map.json").read_bytes())
|
||||||
|
self.assertEqual(len(catalog["plugins"]), 3)
|
||||||
|
self.assertEqual(len(mapping["packages"]), 3)
|
||||||
|
self.assertEqual(
|
||||||
|
mapping["catalog_sha256"], packager.sha256(catalog_path.read_bytes())
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
(output / mapping["catalog"]).read_bytes(), catalog_path.read_bytes()
|
||||||
|
)
|
||||||
|
for entry, record in zip(catalog["plugins"], mapping["packages"], strict=True):
|
||||||
|
archive = output / record["file"]
|
||||||
|
self.assertEqual(entry["sha256"], packager.sha256(archive.read_bytes()))
|
||||||
|
self.assertEqual(record["sha256"], entry["sha256"])
|
||||||
|
self.assertEqual(record["archive_url"], entry["archive_url"])
|
||||||
|
self.assertIn(
|
||||||
|
"/" + packager.DEFAULT_RELEASE_TAG + "/", entry["archive_url"]
|
||||||
|
)
|
||||||
|
self.assertTrue(entry["archive_url"].startswith(packager.RELEASES + "/"))
|
||||||
|
with zipfile.ZipFile(archive) as package:
|
||||||
|
self.assertEqual(
|
||||||
|
json.loads(package.read("plugin.json")), entry["manifest"]
|
||||||
|
)
|
||||||
|
device = mapping["packages"][2]
|
||||||
|
self.assertEqual(device["version"], "0.5.0+artemis.7")
|
||||||
|
with zipfile.ZipFile(output / device["file"]) as package:
|
||||||
|
self.assertEqual(
|
||||||
|
package.read("server.py"), (self.plugin / "server.py").read_bytes()
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
package.read("MARKET.json"), (self.plugin / "MARKET.json").read_bytes()
|
||||||
|
)
|
||||||
|
sums = (output / "SHA256SUMS.txt").read_text("ascii").splitlines()
|
||||||
|
self.assertEqual(len(sums), 3)
|
||||||
|
|
||||||
|
def test_repeated_candidate_is_deterministic_and_custom_tag_is_shared(self):
|
||||||
|
first, _ = self.assemble("first", "rust3-preview-test")
|
||||||
|
second, _ = self.assemble("second", "rust3-preview-test")
|
||||||
|
for name in ("SHA256SUMS.txt", "catalog-v5.json", "bundle-map.json"):
|
||||||
|
self.assertEqual((first / name).read_bytes(), (second / name).read_bytes())
|
||||||
|
catalog = json.loads((first / "catalog-v5.json").read_bytes())
|
||||||
|
self.assertTrue(
|
||||||
|
all("/rust3-preview-test/" in e["archive_url"] for e in catalog["plugins"])
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_rejects_stale_market_hash_and_mismatched_version(self):
|
||||||
|
(self.plugin / "server.py").write_bytes(b"changed")
|
||||||
|
with self.assertRaisesRegex(ValueError, "MARKET identity or content hash"):
|
||||||
|
packager.device_test_files(self.plugin)
|
||||||
|
self.manifest["version"] = "0.5.0+artemis.6"
|
||||||
|
(self.plugin / "plugin.json").write_bytes(packager.encoded(self.manifest))
|
||||||
|
self.update_market()
|
||||||
|
with self.assertRaisesRegex(ValueError, "identity or compatibility"):
|
||||||
|
packager.device_test_files(self.plugin)
|
||||||
|
|
||||||
|
def test_rejects_missing_distribution_material_and_runtime_files(self):
|
||||||
|
(self.plugin / "LICENSE").unlink()
|
||||||
|
with self.assertRaisesRegex(ValueError, "distribution material"):
|
||||||
|
packager.device_test_files(self.plugin)
|
||||||
|
(self.plugin / "LICENSE").write_bytes(b"license")
|
||||||
|
(self.plugin / ".env").write_bytes(b"private fixture")
|
||||||
|
with self.assertRaisesRegex(ValueError, "Non-distributable"):
|
||||||
|
packager.device_test_files(self.plugin)
|
||||||
|
|
||||||
|
def test_source_change_during_build_fails_before_any_archive(self):
|
||||||
|
output = self.root / "changing"
|
||||||
|
|
||||||
|
def change_source(*_):
|
||||||
|
(self.plugin / "server.py").write_bytes(b"changed during build")
|
||||||
|
return b"adapter", b"license", "fixture"
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch.object(packager, "base_runtime", return_value=({}, {})),
|
||||||
|
patch.object(packager, "build_adapter", side_effect=change_source),
|
||||||
|
self.assertRaisesRegex(ValueError, "MARKET identity or content hash"),
|
||||||
|
):
|
||||||
|
packager.main(
|
||||||
|
[
|
||||||
|
"--cua-base-archive",
|
||||||
|
"base.zip",
|
||||||
|
"--go",
|
||||||
|
"go.exe",
|
||||||
|
"--output",
|
||||||
|
str(output),
|
||||||
|
"--catalog-output",
|
||||||
|
str(self.root / "catalog.json"),
|
||||||
|
"--device-test-source",
|
||||||
|
str(self.plugin),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertEqual(list(output.glob("*.zip")), [])
|
||||||
|
|
||||||
|
def test_refuses_existing_outputs_and_unsafe_release_tags(self):
|
||||||
|
output, catalog = self.assemble("existing")
|
||||||
|
original = catalog.read_bytes()
|
||||||
|
with self.assertRaisesRegex(ValueError, "new or empty"):
|
||||||
|
packager.main(
|
||||||
|
[
|
||||||
|
"--cua-base-archive",
|
||||||
|
"base.zip",
|
||||||
|
"--go",
|
||||||
|
"go.exe",
|
||||||
|
"--output",
|
||||||
|
str(output),
|
||||||
|
"--catalog-output",
|
||||||
|
str(catalog),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertEqual(catalog.read_bytes(), original)
|
||||||
|
with self.assertRaisesRegex(ValueError, "Refusing to overwrite an archive"):
|
||||||
|
packager.write_zip(output / "task-review-1.1.1.zip", {"file": b"changed"})
|
||||||
|
for tag in ("../main", "https://invalid", "", "x#fragment", "x y"):
|
||||||
|
with self.subTest(tag=tag), self.assertRaises(ValueError):
|
||||||
|
packager.release_url(tag, "plugin.zip")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in new issue
Block a user