refactor(protocol): 拆分 1281 行的 capabilities.ts(纯搬移,公共 API 不变)
## 结果
capabilities.ts 1281 → 12 行(仅门面:re-export + findCapability)
capabilities/names.ts 55 行 标准能力名 CapabilityNames + 类型
capabilities/schemas.ts 11 行 通用 schema 片段(filterSchema)
capabilities/manifest/system.ts 303 行 系统面 9 项
capabilities/manifest/graphics.ts 413 行 图形 12 项(screen/window/input)
capabilities/manifest/files.ts 121 行 文件 4 项
capabilities/manifest/software.ts 62 行 软件 2 项
capabilities/manifest/automation.ts 329 行 自动化 12 项(task/clip/event/net/macro/group)
capabilities/manifest/index.ts 20 行 聚合为单一 CAPABILITY_MANIFEST
## 做法(可复用的纯搬移流程)
1. 先扫「清单数组区间内是否夹带顶层声明」→ 无 ✓(否则按函数切分会漏)
2. 用花括号配对切出 39 个描述符(断言数量必须一致,避免静默丢项)
3. 按**能力名字符串前缀**分组(不是枚举成员名——成员名与字符串前缀并不一致,
例如 AuditList→system.audit.list、AgentRestart→system.agent.restart)
4. 聚合文件保持对外单一 CAPABILITY_MANIFEST;facade 保持全部导出不变
## 副作用(正面)
清单顺序由「历史上陆续追加的混杂顺序」变为**按域分组**(系统→软件→图形→文件→自动化),
CLI/MCP 的能力展示更易读。已真机确认无功能影响。
## 验证
- 构建 6 包全过;单测 124/124(含清单守护:数量=39、逐项 name/schema 校验);e2e 26/26
- 真机:部署后能力 39 项 ✓、CLI 能力矩阵正常打印(含风险图标)✓
This commit is contained in:
1 parent
caa5b8a2c1
commit
4c6190285c
9 files changed
+1322
-1276
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,329 @@
|
||||
import type { CapabilityDescriptor } from '../../messages.js';
|
||||
import { CapabilityNames } from '../names.js';
|
||||
import { filterSchema } from '../schemas.js';
|
||||
|
||||
/** automation 组能力清单 */
|
||||
export const AUTOMATION_CAPABILITIES: CapabilityDescriptor[] = [
|
||||
{
|
||||
name: CapabilityNames.TaskList,
|
||||
version: '1.0',
|
||||
description: '列出当前后台异步任务(system.shell.exec 以 async:true 启动的命令)。',
|
||||
risk: 'low',
|
||||
params_schema: { type: 'object', properties: {}, additionalProperties: false },
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
tasks: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
task_id: { type: 'string' },
|
||||
command: { type: 'string', description: '命令(sha256 摘要后前 8 位)' },
|
||||
state: { type: 'string', description: 'running | done | killed | failed' },
|
||||
started_at: { type: 'integer' },
|
||||
duration_ms: { type: 'integer' },
|
||||
exit_code: { type: 'integer' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.TaskGet,
|
||||
version: '1.0',
|
||||
description:
|
||||
'查询异步任务状态与输出。任务结束后仍可查询(保留最近 N 条),输出支持增量读取(offset)。',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
task_id: { type: 'string' },
|
||||
stream: { type: 'string', enum: ['stdout', 'stderr'], default: 'stdout' },
|
||||
offset: {
|
||||
type: 'integer',
|
||||
description: '从输出的第 offset 字节开始读(增量拉取)',
|
||||
default: 0,
|
||||
},
|
||||
max_bytes: { type: 'integer', default: 262144 },
|
||||
},
|
||||
required: ['task_id'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
task_id: { type: 'string' },
|
||||
state: { type: 'string' },
|
||||
exit_code: { type: 'integer' },
|
||||
started_at: { type: 'integer' },
|
||||
duration_ms: { type: 'integer' },
|
||||
data: { type: 'string', description: '本次读取到的输出片段' },
|
||||
offset: { type: 'integer', description: '已读到的字节位置(下次续读用)' },
|
||||
total_bytes: { type: 'integer' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.TaskKill,
|
||||
version: '1.0',
|
||||
description: '强制终止一个后台异步任务(杀整棵进程树)。',
|
||||
risk: 'medium',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: { task_id: { type: 'string' } },
|
||||
required: ['task_id'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: { killed: { type: 'boolean' }, task_id: { type: 'string' } },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.ClipGet,
|
||||
version: '1.1',
|
||||
description:
|
||||
'读取被控端剪贴板。format=auto(默认)优先取图片、无图则取文本;text 仅取文本;image 仅取图片。',
|
||||
risk: 'medium',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
format: { type: 'string', enum: ['auto', 'text', 'image'], default: 'auto' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: { type: 'string', description: 'text | image' },
|
||||
text: { type: 'string' },
|
||||
format: { type: 'string', description: '图片格式(png)' },
|
||||
image_base64: { type: 'string' },
|
||||
bytes: { type: 'integer' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.ClipSet,
|
||||
version: '1.1',
|
||||
description:
|
||||
'向被控端剪贴板写入文本或图片(Windows: Set-Clipboard / SetImage;macOS: pbcopy)。' +
|
||||
'图片以 Base64 PNG 传入(image_base64),内部经临时文件传递以规避命令行长度限制。',
|
||||
risk: 'medium',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
text: { type: 'string', description: '文本(与 image_base64 二选一)' },
|
||||
image_base64: { type: 'string', description: 'PNG 图片的 Base64(与 text 二选一)' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: { type: 'string' },
|
||||
written: { type: 'integer', description: '写入的字符数(文本)' },
|
||||
written_bytes: { type: 'integer', description: '写入的字节数(图片)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.NetApply,
|
||||
version: '1.0',
|
||||
description:
|
||||
'**网络变更两阶段提交**(commit-confirm):备份当前网络配置 → 应用变更 → 注册一个 OS 级' +
|
||||
'一次性计划任务到点自动回滚(不依赖 agent 进程存活)→ 控制端调用 system.net.confirm 确认即提交。' +
|
||||
'用于远程改 IP / 切 DHCP 这类「改错就失联」的操作 —— 未确认则自动回滚,不会把自己关在门外。',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
mode: { type: 'string', enum: ['static', 'dhcp', 'command'], description: '变更类型;command=自定义命令(POSIX 仅支持此项)' },
|
||||
interface: { type: 'string', description: '网卡别名(如「Ethernet」);不填则自动取带默认网关的那块' },
|
||||
ip: { type: 'string', description: 'mode=static:新 IPv4' },
|
||||
mask: { type: 'string', description: 'mode=static:子网掩码(如 255.255.255.0)' },
|
||||
gateway: { type: 'string', description: 'mode=static:网关(可选)' },
|
||||
dns: { type: 'array', items: { type: 'string' }, description: 'mode=static:DNS 列表(可选)' },
|
||||
command: { type: 'string', description: 'mode=command:要执行的变更命令' },
|
||||
confirm_within_ms: {
|
||||
type: 'integer',
|
||||
minimum: 15000,
|
||||
maximum: 600000,
|
||||
default: 60000,
|
||||
description: '确认窗口;逾期自动回滚(默认 60 秒)',
|
||||
},
|
||||
},
|
||||
required: ['mode'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
applied: { type: 'boolean' },
|
||||
backup_path: { type: 'string', description: '变更前配置备份(netsh dump,可用 netsh -f 恢复)' },
|
||||
rollback_scheduled: { type: 'boolean' },
|
||||
task_name: { type: 'string' },
|
||||
rollback_at: { type: 'integer', description: '自动回滚时间戳(Unix 毫秒)' },
|
||||
confirm_within_ms: { type: 'integer' },
|
||||
before: { type: 'object', description: '变更前地址信息' },
|
||||
after: { type: 'object', description: '变更后地址信息' },
|
||||
hint: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.NetConfirm,
|
||||
version: '1.0',
|
||||
description:
|
||||
'确认(提交)网络变更:取消自动回滚任务。控制端通常在新地址上重连成功后调用。' +
|
||||
'不传 task_name 则取消全部待确认项。',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: { task_name: { type: 'string', description: 'net.apply 返回的任务名;省略则取消全部' } },
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
confirmed: { type: 'integer' },
|
||||
cancelled: { type: 'array', items: { type: 'string' } },
|
||||
remaining: { type: 'array', items: { type: 'string' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.NetStatus,
|
||||
version: '1.0',
|
||||
description: '查看网络现状:各网卡地址、待确认的变更(含剩余秒数)、历史备份文件。',
|
||||
risk: 'low',
|
||||
params_schema: { type: 'object', properties: {}, additionalProperties: false },
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
interfaces: { type: 'array', items: { type: 'object' } },
|
||||
pending: { type: 'array', items: { type: 'object' } },
|
||||
pending_count: { type: 'integer' },
|
||||
backups: { type: 'array', items: { type: 'object' } },
|
||||
note: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.EventWatch,
|
||||
version: '1.0',
|
||||
description:
|
||||
'订阅被控端事件:file=文件变动(增删改)、process=进程启停、net=监听端口开闭。' +
|
||||
'被控端通过 event 通知主动推送(无需轮询);事件同时进入环形缓冲,可用 event.poll 拉取。',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
kind: { type: 'string', enum: ['file', 'process', 'net'] },
|
||||
path: { type: 'string', description: 'file 类型:监控的目录/文件路径' },
|
||||
pattern: { type: 'string', description: '可选:文件名 glob(如 *.log)或进程名子串' },
|
||||
recursive: { type: 'boolean', default: false, description: 'file 类型:是否递归子目录' },
|
||||
interval_ms: {
|
||||
type: 'integer',
|
||||
minimum: 1000,
|
||||
maximum: 60000,
|
||||
default: 5000,
|
||||
description: 'process/net 类型的采样间隔',
|
||||
},
|
||||
},
|
||||
required: ['kind'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
watch_id: { type: 'string' },
|
||||
kind: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.EventUnwatch,
|
||||
version: '1.0',
|
||||
description: '取消一个事件订阅(连接断开时也会自动清理)。',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: { watch_id: { type: 'string' } },
|
||||
required: ['watch_id'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: { removed: { type: 'boolean' } },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.EventList,
|
||||
version: '1.0',
|
||||
description: '列出当前生效的事件订阅及其已产生事件数。',
|
||||
risk: 'low',
|
||||
params_schema: { type: 'object', properties: {}, additionalProperties: false },
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
watches: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
watch_id: { type: 'string' },
|
||||
kind: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
events: { type: 'integer' },
|
||||
created_at: { type: 'integer' },
|
||||
},
|
||||
},
|
||||
},
|
||||
buffered: { type: 'integer', description: '环形缓冲中的事件总数' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.EventPoll,
|
||||
version: '1.0',
|
||||
description:
|
||||
'拉取已缓冲的事件(适合无法接收推送的调用方,如 MCP:先 watch 再 poll)。' +
|
||||
'since 传入上次返回的 next_cursor 可增量拉取。',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
limit: { type: 'integer', minimum: 1, maximum: 500, default: 50 },
|
||||
since: { type: 'integer', description: '游标(上次返回的 next_cursor)' },
|
||||
watch_id: { type: 'string', description: '可选:只看某订阅' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
events: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
seq: { type: 'integer' },
|
||||
watch_id: { type: 'string' },
|
||||
kind: { type: 'string' },
|
||||
ts: { type: 'integer' },
|
||||
action: { type: 'string' },
|
||||
target: { type: 'string' },
|
||||
detail: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
next_cursor: { type: 'integer' },
|
||||
dropped: { type: 'integer', description: '因缓冲上限被丢弃的事件数' },
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,121 @@
|
||||
import type { CapabilityDescriptor } from '../../messages.js';
|
||||
import { CapabilityNames } from '../names.js';
|
||||
import { filterSchema } from '../schemas.js';
|
||||
|
||||
/** files 组能力清单 */
|
||||
export const FILES_CAPABILITIES: CapabilityDescriptor[] = [
|
||||
{
|
||||
name: CapabilityNames.FsList,
|
||||
version: '1.0',
|
||||
description: '列出目录内容(支持 glob 过滤与递归)',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string', minLength: 1 },
|
||||
pattern: { type: 'string', description: 'glob 过滤,如 "*.log"' },
|
||||
recursive: { type: 'boolean', default: false },
|
||||
max_entries: { type: 'integer', default: 500, minimum: 1, maximum: 5000 },
|
||||
},
|
||||
required: ['path'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
entries: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
path: { type: 'string' },
|
||||
type: { type: 'string', enum: ['file', 'dir', 'other'] },
|
||||
size: { type: 'integer' },
|
||||
mtime: { type: 'integer' },
|
||||
},
|
||||
},
|
||||
},
|
||||
total: { type: 'integer' },
|
||||
truncated: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.FsStat,
|
||||
version: '1.0',
|
||||
description: '获取文件或目录的元信息',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: { path: { type: 'string', minLength: 1 } },
|
||||
required: ['path'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string' },
|
||||
type: { type: 'string', enum: ['file', 'dir', 'other'] },
|
||||
size: { type: 'integer' },
|
||||
mtime: { type: 'integer' },
|
||||
exists: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.FsRead,
|
||||
version: '1.0',
|
||||
description: '读取文件内容(分块:用 offset/max_bytes 续读,eof 标识结束)',
|
||||
risk: 'medium',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string', minLength: 1 },
|
||||
encoding: { type: 'string', enum: ['utf8', 'base64'], default: 'utf8' },
|
||||
offset: { type: 'integer', default: 0, minimum: 0, description: '起始字节偏移' },
|
||||
max_bytes: { type: 'integer', default: 1048576, minimum: 1, maximum: 8388608, description: '单次读取上限' },
|
||||
},
|
||||
required: ['path'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: { type: 'string' },
|
||||
encoding: { type: 'string' },
|
||||
offset: { type: 'integer' },
|
||||
bytes: { type: 'integer', description: '本次实际读取字节数' },
|
||||
total_bytes: { type: 'integer' },
|
||||
eof: { type: 'boolean' },
|
||||
sha256: { type: 'string', description: '整文件摘要(仅首次读取时返回)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.FsWrite,
|
||||
version: '1.0',
|
||||
description: '写入文件(分块:append=true 追加;写入走临时文件 + 原子重命名)',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string', minLength: 1 },
|
||||
data: { type: 'string' },
|
||||
encoding: { type: 'string', enum: ['utf8', 'base64'], default: 'utf8' },
|
||||
append: { type: 'boolean', default: false },
|
||||
create_dirs: { type: 'boolean', default: false },
|
||||
},
|
||||
required: ['path', 'data'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
written: { type: 'integer', description: '本次写入字节数' },
|
||||
total_bytes: { type: 'integer', description: '写入后的文件总大小' },
|
||||
path: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,413 @@
|
||||
import type { CapabilityDescriptor } from '../../messages.js';
|
||||
import { CapabilityNames } from '../names.js';
|
||||
import { filterSchema } from '../schemas.js';
|
||||
|
||||
/** graphics 组能力清单 */
|
||||
export const GRAPHICS_CAPABILITIES: CapabilityDescriptor[] = [
|
||||
{
|
||||
name: CapabilityNames.ScreenInfo,
|
||||
version: '1.0',
|
||||
description: '获取显示器信息(分辨率、缩放、主屏标识)',
|
||||
risk: 'low',
|
||||
params_schema: { type: 'object', properties: {}, additionalProperties: false },
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
displays: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'integer' },
|
||||
name: { type: 'string' },
|
||||
width: { type: 'integer' },
|
||||
height: { type: 'integer' },
|
||||
is_primary: { type: 'boolean' },
|
||||
scale: { type: 'number' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.ScreenCapture,
|
||||
version: '1.0',
|
||||
description: '截取屏幕,返回 base64 图片(支持区域与缩放)',
|
||||
risk: 'medium',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
display_id: { type: 'integer', default: 0, minimum: 0, description: '0 = 主屏' },
|
||||
format: { type: 'string', enum: ['png', 'jpeg'], default: 'jpeg' },
|
||||
quality: { type: 'integer', default: 85, minimum: 1, maximum: 100, description: '仅 jpeg 生效' },
|
||||
scale: { type: 'number', default: 1, minimum: 0.1, maximum: 1 },
|
||||
region: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
x: { type: 'integer' },
|
||||
y: { type: 'integer' },
|
||||
width: { type: 'integer', minimum: 1 },
|
||||
height: { type: 'integer', minimum: 1 },
|
||||
},
|
||||
required: ['x', 'y', 'width', 'height'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
image: { type: 'string', description: 'base64 编码的图片' },
|
||||
format: { type: 'string' },
|
||||
width: { type: 'integer' },
|
||||
height: { type: 'integer' },
|
||||
bytes: { type: 'integer' },
|
||||
captured_at: { type: 'integer' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.MouseMove,
|
||||
version: '1.0',
|
||||
description: '移动鼠标到指定屏幕坐标',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
x: { type: 'integer', minimum: 0 },
|
||||
y: { type: 'integer', minimum: 0 },
|
||||
duration_ms: { type: 'integer', default: 0, minimum: 0, maximum: 5000, description: '平滑移动耗时' },
|
||||
},
|
||||
required: ['x', 'y'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: { moved: { type: 'boolean' }, x: { type: 'integer' }, y: { type: 'integer' } },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.MouseClick,
|
||||
version: '1.0',
|
||||
description: '鼠标点击(可选先移动到指定坐标)',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
x: { type: 'integer', minimum: 0 },
|
||||
y: { type: 'integer', minimum: 0 },
|
||||
button: { type: 'string', enum: ['left', 'right', 'middle'], default: 'left' },
|
||||
count: { type: 'integer', default: 1, minimum: 1, maximum: 3 },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
clicked: { type: 'boolean' },
|
||||
x: { type: 'integer' },
|
||||
y: { type: 'integer' },
|
||||
button: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.MouseScroll,
|
||||
version: '1.0',
|
||||
description: '滚动鼠标滚轮(正数向上、负数向下)',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
delta: { type: 'integer', description: '滚动格数' },
|
||||
x: { type: 'integer', minimum: 0 },
|
||||
y: { type: 'integer', minimum: 0 },
|
||||
},
|
||||
required: ['delta'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: { scrolled: { type: 'boolean' }, delta: { type: 'integer' } },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.MouseDrag,
|
||||
version: '1.0',
|
||||
description:
|
||||
'鼠标拖拽:从 (from_x,from_y) 按下并拖到 (to_x,to_y) 释放。' +
|
||||
'用于拖动文件/窗口、框选文本、滑动条。整段动作在单次调用内完成,步数按距离自适应。',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
from_x: { type: 'integer' },
|
||||
from_y: { type: 'integer' },
|
||||
to_x: { type: 'integer' },
|
||||
to_y: { type: 'integer' },
|
||||
button: { type: 'string', enum: ['left', 'right', 'middle'], default: 'left' },
|
||||
steps: { type: 'integer', minimum: 1, maximum: 200, description: '插值步数(默认按距离自适应)' },
|
||||
step_delay_ms: { type: 'integer', minimum: 0, maximum: 200, default: 12 },
|
||||
},
|
||||
required: ['from_x', 'from_y', 'to_x', 'to_y'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
dragged: { type: 'boolean' },
|
||||
from: { type: 'object', properties: { x: { type: 'integer' }, y: { type: 'integer' } } },
|
||||
to: { type: 'object', properties: { x: { type: 'integer' }, y: { type: 'integer' } } },
|
||||
button: { type: 'string' },
|
||||
steps: { type: 'integer' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.KeyType,
|
||||
version: '1.0',
|
||||
description: '输入一段文本(逐字符模拟键盘)',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
text: { type: 'string', minLength: 1, maxLength: 4096 },
|
||||
interval_ms: { type: 'integer', default: 10, minimum: 0, maximum: 1000 },
|
||||
},
|
||||
required: ['text'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: { typed: { type: 'boolean' }, length: { type: 'integer' } },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.KeyPress,
|
||||
version: '1.1',
|
||||
description:
|
||||
'按下按键。keys=["ctrl","c"] 表示和弦;配 repeat=3 可连按 3 次;' +
|
||||
'sequence=[["ctrl","c"],["ctrl","v"]] 表示按键序列',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
keys: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
minItems: 1,
|
||||
maxItems: 4,
|
||||
description: '和弦按键列表,如 ["ctrl","shift","esc"]',
|
||||
},
|
||||
repeat: { type: 'integer', minimum: 1, maximum: 50, default: 1, description: '和弦重复次数' },
|
||||
sequence: {
|
||||
type: 'array',
|
||||
items: { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 4 },
|
||||
description: '和弦序列,如 [["up"],["up"],["enter"]];提供时忽略 keys/repeat',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: { pressed: { type: 'boolean' }, keys: { type: 'array', items: { type: 'string' } } },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.WindowList,
|
||||
version: '1.0',
|
||||
description:
|
||||
'列出当前可见的顶层窗口(标题、所属进程、精确矩形、是否前台窗口)。Windows 用 Win32,macOS 用 AppleScript(需辅助功能权限)。' +
|
||||
'配合 screen.capture 可在**已知窗口范围内**相对定位,避免全屏猜坐标。',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
title_pattern: { type: 'string', description: '可选:按标题正则过滤' },
|
||||
limit: { type: 'integer', default: 50 },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
windows: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
hwnd: { type: 'string', description: '窗口句柄(十六进制)' },
|
||||
title: { type: 'string' },
|
||||
process: { type: 'string' },
|
||||
pid: { type: 'integer' },
|
||||
x: { type: 'integer' },
|
||||
y: { type: 'integer' },
|
||||
width: { type: 'integer' },
|
||||
height: { type: 'integer' },
|
||||
is_foreground: { type: 'boolean' },
|
||||
is_minimized: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.WindowFocus,
|
||||
version: '1.0',
|
||||
description: '将指定窗口(按标题正则或句柄)置前并聚焦,返回其精确矩形。',
|
||||
risk: 'medium',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
title: { type: 'string', description: '窗口标题(正则,匹配第一个)' },
|
||||
hwnd: { type: 'string', description: '或直接给窗口句柄' },
|
||||
wait_ms: {
|
||||
type: 'integer',
|
||||
minimum: 0,
|
||||
maximum: 30000,
|
||||
default: 0,
|
||||
description: '等待窗口出现(应用启动有延迟时用;仅 title 模式有效)',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
hwnd: { type: 'string' },
|
||||
title: { type: 'string' },
|
||||
x: { type: 'integer' },
|
||||
y: { type: 'integer' },
|
||||
width: { type: 'integer' },
|
||||
height: { type: 'integer' },
|
||||
focused: { type: 'boolean' },
|
||||
activated_by: { type: 'string', description: '激活方式:api(SetForegroundWindow)| click(标题栏点击兜底)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.ScreenFind,
|
||||
version: '1.1',
|
||||
description:
|
||||
'在界面中查找 UI 元素并返回其**屏幕坐标**。Windows:UIA 优先,找不到自动降级 OCR(截图识别,支持自绘 UI)。' +
|
||||
'macOS:统一走 Vision OCR(无 UIA 等价物)。返回中心点坐标,直接喂给 input.mouse.click。',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
text: { type: 'string', description: '要查找的文本(匹配元素 Name,支持子串)' },
|
||||
window: { type: 'string', description: '可选:限定在该窗口标题(正则)内查找' },
|
||||
control_type: {
|
||||
type: 'string',
|
||||
description: '可选:限定控件类型,如 Button / MenuItem / Edit / Text / ListItem(仅 UIA)',
|
||||
},
|
||||
method: {
|
||||
type: 'string',
|
||||
enum: ['auto', 'uia', 'ocr', 'image'],
|
||||
default: 'auto',
|
||||
description:
|
||||
'auto=UIA 优先+OCR 兜底;uia=仅控件树;ocr=仅文字识别;image=仅图像模板匹配(需 template)',
|
||||
},
|
||||
template: {
|
||||
type: 'string',
|
||||
description:
|
||||
'method=image 时必填:被控端上的模板图片路径(png/jpg/bmp)。' +
|
||||
'用于纯图标/无文字控件 —— UIA 无控件树、OCR 无文字可读的场景',
|
||||
},
|
||||
threshold: {
|
||||
type: 'number',
|
||||
minimum: 0.3,
|
||||
maximum: 0.999,
|
||||
default: 0.85,
|
||||
description: 'method=image 的匹配阈值(零均值归一化互相关,越高越严格)',
|
||||
},
|
||||
limit: { type: 'integer', default: 20 },
|
||||
wait_ms: {
|
||||
type: 'integer',
|
||||
minimum: 0,
|
||||
maximum: 30000,
|
||||
default: 0,
|
||||
description: '等待元素出现的上限毫秒(0=只查一次)。界面有动画/加载时用',
|
||||
},
|
||||
interval_ms: { type: 'integer', minimum: 100, maximum: 2000, default: 400 },
|
||||
region: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
x: { type: 'integer' },
|
||||
y: { type: 'integer' },
|
||||
width: { type: 'integer' },
|
||||
height: { type: 'integer' },
|
||||
},
|
||||
description: '可选:只在该屏幕区域查找(OCR/图像匹配更快更准;UIA 忽略此参数)',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
engine: { type: 'string', description: '实际使用的引擎:uia | ocr' },
|
||||
waited_ms: { type: 'integer', description: '实际等待时长(便于诊断是否命中等待窗口)' },
|
||||
matches: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
control_type: { type: 'string' },
|
||||
automation_id: { type: 'string' },
|
||||
x: { type: 'integer', description: '中心点 X(屏幕坐标)' },
|
||||
y: { type: 'integer', description: '中心点 Y(屏幕坐标)' },
|
||||
left: { type: 'integer' },
|
||||
top: { type: 'integer' },
|
||||
width: { type: 'integer' },
|
||||
height: { type: 'integer' },
|
||||
window_title: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.ScreenRecord,
|
||||
version: '1.0',
|
||||
description:
|
||||
'录制屏幕为帧序列(JPEG),检测到 ffmpeg 时额外封装 mp4。产物留在被控端目录,' +
|
||||
'用 fs.read / pull 取回。用于复现「间歇性」问题、记录 GUI 操作过程。',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
duration_ms: { type: 'integer', minimum: 1000, maximum: 60000, default: 5000 },
|
||||
fps: { type: 'integer', minimum: 1, maximum: 10, default: 2 },
|
||||
scale: { type: 'number', minimum: 0.1, maximum: 1, default: 0.5, description: '全屏录制时的缩放' },
|
||||
region: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
x: { type: 'integer' }, y: { type: 'integer' },
|
||||
width: { type: 'integer' }, height: { type: 'integer' },
|
||||
},
|
||||
description: '可选:只录制该区域(多屏时用负坐标)',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
dir: { type: 'string', description: '帧序列目录(被控端路径)' },
|
||||
frames: { type: 'integer' },
|
||||
requested_frames: { type: 'integer' },
|
||||
elapsed_ms: { type: 'integer' },
|
||||
fps: { type: 'integer' },
|
||||
video_path: { type: 'string', description: '有 ffmpeg 时的 mp4 路径' },
|
||||
frames_only: { type: 'boolean' },
|
||||
message: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,20 @@
|
||||
// 能力清单聚合(按组拆分后在此汇总,对外仍是单一 CAPABILITY_MANIFEST)
|
||||
import type { CapabilityDescriptor } from '../../messages.js';
|
||||
import { AUTOMATION_CAPABILITIES } from './automation.js';
|
||||
import { FILES_CAPABILITIES } from './files.js';
|
||||
import { GRAPHICS_CAPABILITIES } from './graphics.js';
|
||||
import { SOFTWARE_CAPABILITIES } from './software.js';
|
||||
import { SYSTEM_CAPABILITIES } from './system.js';
|
||||
|
||||
/**
|
||||
* v1 标准能力清单(39 项)。
|
||||
* 被控端按此声明并在 auth_ok 中返回;控制端据此展示与校验。
|
||||
* 顺序即 CLI/MCP 的展示顺序(系统面 → 软件 → 图形 → 文件 → 自动化)。
|
||||
*/
|
||||
export const CAPABILITY_MANIFEST: CapabilityDescriptor[] = [
|
||||
...SYSTEM_CAPABILITIES,
|
||||
...SOFTWARE_CAPABILITIES,
|
||||
...GRAPHICS_CAPABILITIES,
|
||||
...FILES_CAPABILITIES,
|
||||
...AUTOMATION_CAPABILITIES,
|
||||
];
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { CapabilityDescriptor } from '../../messages.js';
|
||||
import { CapabilityNames } from '../names.js';
|
||||
import { filterSchema } from '../schemas.js';
|
||||
|
||||
/** software 组能力清单 */
|
||||
export const SOFTWARE_CAPABILITIES: CapabilityDescriptor[] = [
|
||||
{
|
||||
name: CapabilityNames.AppList,
|
||||
version: '1.0',
|
||||
description: '列出已安装软件(读取注册表卸载项 + winget list)',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: { filter: filterSchema },
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
apps: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
version: { type: 'string' },
|
||||
publisher: { type: 'string' },
|
||||
source: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.AppInstall,
|
||||
version: '1.0',
|
||||
description: '安装软件(优先 winget 静默安装)',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
package: { type: 'string', minLength: 1, description: 'winget 包 ID 或软件名' },
|
||||
id: { type: 'string', description: '精确 winget ID(优先使用)' },
|
||||
silent: { type: 'boolean', default: true },
|
||||
timeout_ms: { type: 'integer', default: 600000, minimum: 10000, maximum: 1800000 },
|
||||
},
|
||||
required: ['package'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
installed: { type: 'boolean' },
|
||||
name: { type: 'string' },
|
||||
version: { type: 'string' },
|
||||
source: { type: 'string' },
|
||||
detail: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,303 @@
|
||||
import type { CapabilityDescriptor } from '../../messages.js';
|
||||
import { CapabilityNames } from '../names.js';
|
||||
import { filterSchema } from '../schemas.js';
|
||||
|
||||
/** system 组能力清单 */
|
||||
export const SYSTEM_CAPABILITIES: CapabilityDescriptor[] = [
|
||||
{
|
||||
name: CapabilityNames.SystemInfo,
|
||||
version: '1.0',
|
||||
description: '获取 Windows 系统基本信息(主机名、OS、CPU、内存、开机时长)',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
fields: {
|
||||
type: 'array',
|
||||
description: '可选:只返回指定字段',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
hostname: { type: 'string' },
|
||||
os: { type: 'string' },
|
||||
os_version: { type: 'string' },
|
||||
arch: { type: 'string' },
|
||||
cpu_model: { type: 'string' },
|
||||
cpu_cores: { type: 'integer' },
|
||||
memory_total: { type: 'integer', description: '字节' },
|
||||
uptime_sec: { type: 'integer' },
|
||||
is_admin: { type: 'boolean' },
|
||||
pid: { type: 'integer', description: '被控端进程 PID(v11)' },
|
||||
agent_script: { type: 'string', description: '被控端入口脚本路径,供一键升级定位(v11)' },
|
||||
agent_home: { type: 'string', description: '被控端数据目录 NODEAGENT_HOME(v11)' },
|
||||
node_path: { type: 'string', description: '被控端 Node 可执行文件路径(v11)' },
|
||||
ps_helper: {
|
||||
type: 'object',
|
||||
description: 'PowerShell 常驻助手状态(v15):spawned/hits/failures/consecutiveFailures/avg_ms 等',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.SystemStatus,
|
||||
version: '1.0',
|
||||
description: '获取资源状态:CPU 占用、内存、磁盘、网络适配器',
|
||||
risk: 'low',
|
||||
params_schema: { type: 'object', properties: {}, additionalProperties: false },
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
cpu_pct: { type: 'number' },
|
||||
memory_used: { type: 'integer' },
|
||||
memory_total: { type: 'integer' },
|
||||
memory_pct: { type: 'number' },
|
||||
disks: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
drive: { type: 'string' },
|
||||
total: { type: 'integer' },
|
||||
free: { type: 'integer' },
|
||||
used_pct: { type: 'number' },
|
||||
},
|
||||
},
|
||||
},
|
||||
net: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
adapter: { type: 'string' },
|
||||
ip: { type: 'string' },
|
||||
up: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.ProcessList,
|
||||
version: '1.0',
|
||||
description: '列出运行中的进程,可按 CPU/内存/名称排序',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sort_by: { type: 'string', enum: ['cpu', 'memory', 'pid', 'name'], default: 'cpu' },
|
||||
limit: { type: 'integer', default: 50, minimum: 1, maximum: 500 },
|
||||
filter: filterSchema,
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
processes: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
pid: { type: 'integer' },
|
||||
name: { type: 'string' },
|
||||
cpu_pct: { type: 'number' },
|
||||
memory_bytes: { type: 'integer' },
|
||||
started_at: { type: 'integer' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.ServiceList,
|
||||
version: '1.0',
|
||||
description: '列出 Windows 服务及运行状态',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
filter: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name_pattern: { type: 'string' },
|
||||
state: { type: 'string', enum: ['running', 'stopped', 'paused'] },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
limit: { type: 'integer', default: 100, minimum: 1, maximum: 1000 },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
services: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
display_name: { type: 'string' },
|
||||
state: { type: 'string' },
|
||||
start_type: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.ShellExec,
|
||||
version: '1.1',
|
||||
description: '执行 PowerShell / cmd 命令并返回退出码与输出;async:true 时转后台任务立即返回 task_id',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string', minLength: 1, description: '要执行的命令' },
|
||||
shell: { type: 'string', enum: ['powershell', 'cmd'], default: 'powershell' },
|
||||
cwd: { type: 'string', description: '工作目录(可选)' },
|
||||
timeout_ms: { type: 'integer', default: 30000, minimum: 1000, maximum: 300000 },
|
||||
async: { type: 'boolean', default: false, description: '后台执行,立即返回 task_id(用 system.task.* 管理)' },
|
||||
wait_forever: { type: 'boolean', default: false, description: '仅 async 模式:不设超时(timeout_ms 失效)' },
|
||||
},
|
||||
required: ['command'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
exit_code: { type: 'integer' },
|
||||
stdout: { type: 'string' },
|
||||
stderr: { type: 'string' },
|
||||
duration_ms: { type: 'integer' },
|
||||
truncated: { type: 'boolean' },
|
||||
killed: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.AuditList,
|
||||
version: '1.0',
|
||||
description: '查询被控端审计日志:谁在何时调用了什么能力、结果如何',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
limit: { type: 'integer', default: 50, minimum: 1, maximum: 1000 },
|
||||
since: { type: 'integer', description: '仅返回该时间戳(Unix 毫秒)之后的记录' },
|
||||
client_id: { type: 'string', description: '按调用方筛选' },
|
||||
type: { type: 'string', enum: ['invoke', 'auth', 'acl', 'agent'], description: '按事件类别筛选' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
entries: { type: 'array', items: { type: 'object' } },
|
||||
total: { type: 'integer', description: '本次读取的总条数(未截断前)' },
|
||||
file: { type: 'string', description: '审计文件路径' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.AgentRestart,
|
||||
version: '1.0',
|
||||
description:
|
||||
'受控重启被控端自身(延时后由独立进程/计划任务拉起,重启后自动沿用原配置)。' +
|
||||
'解决「配置变更后无法让自身生效」的问题——不会因进程树清理而中断。',
|
||||
risk: 'high',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
delay_ms: {
|
||||
type: 'integer',
|
||||
minimum: 1,
|
||||
maximum: 3600000,
|
||||
description: '延时多少毫秒后重启(默认 2000,给调用方留出返回结果的时间)',
|
||||
default: 2000,
|
||||
},
|
||||
reason: { type: 'string', description: '可选:重启原因(写入审计日志)' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
scheduled: { type: 'boolean', description: '重启任务是否已排入' },
|
||||
delay_ms: { type: 'integer' },
|
||||
mechanism: { type: 'string', description: '使用的机制:scheduled-task / detached-spawn' },
|
||||
message: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.AuditVerify,
|
||||
version: '1.0',
|
||||
description:
|
||||
'校验审计日志链完整性(v11 链式哈希):逐条重算哈希并比对 prev 链接,' +
|
||||
'可发现条目被篡改、删除或替换。轮转跨文件连续校验。',
|
||||
risk: 'low',
|
||||
params_schema: { type: 'object', properties: {}, additionalProperties: false },
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
ok: { type: 'boolean' },
|
||||
checked: { type: 'integer', description: '参与校验的条目数' },
|
||||
legacy: { type: 'integer', description: '无链字段的历史条目数(跳过校验)' },
|
||||
broken_at: {
|
||||
type: 'object',
|
||||
description: '首个异常位置(ok=false 时存在)',
|
||||
properties: {
|
||||
file: { type: 'string' },
|
||||
line: { type: 'integer' },
|
||||
reason: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CapabilityNames.Metrics,
|
||||
version: '1.0',
|
||||
description:
|
||||
'按 PRD 2.2 汇总成功指标(数据源为审计日志):闭环成功率、装软件成功率、P95 时延、安全拦截率,' +
|
||||
'并给出与目标阈值的达标判定;同时返回按能力细分统计。',
|
||||
risk: 'low',
|
||||
params_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
since: { type: 'integer', description: '可选:只统计该时间戳(Unix 毫秒)之后的审计条目' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
returns_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
close_loop: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
attempts: { type: 'integer' },
|
||||
ok: { type: 'integer' },
|
||||
failed: { type: 'integer' },
|
||||
success_rate: { type: 'number' },
|
||||
},
|
||||
},
|
||||
app_install: { type: 'object' },
|
||||
latency: { type: 'object' },
|
||||
security: { type: 'object' },
|
||||
verdict: { type: 'object', description: '四项指标的达标判定' },
|
||||
targets: { type: 'object' },
|
||||
by_capability: { type: 'object' },
|
||||
window: { type: 'object' },
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,55 @@
|
||||
// 标准能力名(从 capabilities.ts 拆出)
|
||||
|
||||
/** nodeagent v1 标准能力名。 */
|
||||
export const CapabilityNames = {
|
||||
SystemInfo: 'system.info',
|
||||
SystemStatus: 'system.status',
|
||||
ProcessList: 'system.process.list',
|
||||
ServiceList: 'system.service.list',
|
||||
ShellExec: 'system.shell.exec',
|
||||
AppList: 'app.list',
|
||||
AppInstall: 'app.install',
|
||||
// v2 图形接管
|
||||
ScreenInfo: 'screen.info',
|
||||
ScreenCapture: 'screen.capture',
|
||||
MouseMove: 'input.mouse.move',
|
||||
MouseClick: 'input.mouse.click',
|
||||
MouseScroll: 'input.mouse.scroll',
|
||||
MouseDrag: 'input.mouse.drag',
|
||||
KeyType: 'input.key.type',
|
||||
KeyPress: 'input.key.press',
|
||||
// v3+ 审计
|
||||
AuditList: 'system.audit.list',
|
||||
// v5 文件传输
|
||||
FsList: 'fs.list',
|
||||
FsStat: 'fs.stat',
|
||||
FsRead: 'fs.read',
|
||||
FsWrite: 'fs.write',
|
||||
// v7 自持能力(受控重启)
|
||||
AgentRestart: 'system.agent.restart',
|
||||
// v8 GUI 语义(窗口与元素定位)
|
||||
WindowList: 'window.list',
|
||||
WindowFocus: 'window.focus',
|
||||
ScreenFind: 'screen.find',
|
||||
ScreenRecord: 'screen.record',
|
||||
// v10 异步任务与剪贴板
|
||||
TaskList: 'system.task.list',
|
||||
TaskGet: 'system.task.get',
|
||||
TaskKill: 'system.task.kill',
|
||||
ClipGet: 'clip.get',
|
||||
ClipSet: 'clip.set',
|
||||
// v11 安全加固
|
||||
AuditVerify: 'system.audit.verify',
|
||||
Metrics: 'system.metrics',
|
||||
// v16 网络变更两阶段提交
|
||||
NetApply: 'system.net.apply',
|
||||
NetConfirm: 'system.net.confirm',
|
||||
NetStatus: 'system.net.status',
|
||||
// v12 事件订阅与监控
|
||||
EventWatch: 'event.watch',
|
||||
EventUnwatch: 'event.unwatch',
|
||||
EventList: 'event.list',
|
||||
EventPoll: 'event.poll',
|
||||
} as const;
|
||||
|
||||
export type CapabilityName = (typeof CapabilityNames)[keyof typeof CapabilityNames];
|
||||
@@ -0,0 +1,11 @@
|
||||
// 通用 schema 片段(从 capabilities.ts 拆出)
|
||||
import type { JsonSchema } from '../messages.js';
|
||||
|
||||
/** 名称过滤片段(app.list 等复用) */
|
||||
export const filterSchema: JsonSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name_pattern: { type: 'string', description: '名称匹配(子串,不区分大小写)' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
};
|
||||
Reference in new issue
Block a user