@solazah/solazah-runtime 0.1.136 → 0.1.140

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solazah/solazah-runtime",
3
- "version": "0.1.136",
3
+ "version": "0.1.140",
4
4
  "main": "index.cjs",
5
5
  "module": "index.js",
6
6
  "types": "types/index.d.ts",
@@ -64,6 +64,8 @@ export interface CopilotOptions extends CopilotChatOptions {
64
64
  tools?: any[];
65
65
  skills?: string[];
66
66
  chatModel?: any;
67
+ /** 本轮模型看得了图:主进程按模型标签定,决定要不要给 agent 装 view_image */
68
+ vision?: boolean;
67
69
  toolCallId?: string;
68
70
  /**
69
71
  * 编辑历史消息后重新生成:
package/types/File.d.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import { default as IpcRenderer } from './IpcRenderer';
2
- export type WorkspaceCategory = "document" | "image" | "audio" | "video" | "blob";
2
+ /**
3
+ * 文件分类。前五个是工作空间下的分类目录(文档/图片/音频/视频/.blob):**工作空间就是会同步的那一份**,
4
+ * 也会进知识库索引。`devicespace` 是账号目录下与工作空间同级的设备空间,不同步、不进索引、不跟人换机,
5
+ * 放只属于这台设备的东西(桌面录制、整屏采集、执行记录、本机缓存),成品仍放工作空间分类。
6
+ *
7
+ * 设备空间按插件分格,你只给格子内的相对路径 —— 插件名由宿主按调用方视图反查后自己拼,
8
+ * 所以既看不到别的插件的东西,也不必自己拼命名空间;卸载插件时宿主整格删掉。
9
+ */
10
+ export type FileCategory = "document" | "image" | "audio" | "video" | "blob" | "devicespace";
3
11
  export interface FileEntry {
4
12
  name: string;
5
13
  path: string;
@@ -22,7 +30,7 @@ export interface FileListOptions {
22
30
  workspaceId?: string | null;
23
31
  }
24
32
  /**
25
- * window.file —— 工作空间分类目录(文档/图片/音频/视频/.blob)内的通用文件读写。
33
+ * window.file —— 分类目录内的通用文件读写:工作空间的文档/图片/音频/视频/.blob,外加设备空间 devicespace。
26
34
  * 寻址按「分类 + 相对路径」,主进程把路径钉死在分类目录内。watch 用每订阅独立频道接收变更,
27
35
  * 返回退订闭包(off 跨 contextBridge 失效,清理一律调返回值)。
28
36
  */
@@ -33,148 +41,148 @@ export default class File {
33
41
  /**
34
42
  * 读取分类目录下某个文本文件的全部内容(UTF-8)。
35
43
  *
36
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
44
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
37
45
  * @param relPath 相对该分类目录的路径,`/` 分隔;越界会被拦截。
38
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
46
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
39
47
  * @returns 文件的 UTF-8 文本内容。
40
48
  * @throws 文件不存在、路径越界、分类未知或工作空间不可达时抛错。
41
49
  */
42
- read: (category: WorkspaceCategory, relPath: string, workspaceId?: string | null) => Promise<string>;
50
+ read: (category: FileCategory, relPath: string, workspaceId?: string | null) => Promise<string>;
43
51
  /**
44
52
  * 把文本内容写入分类目录下的相对路径,父目录不存在则自动创建(UTF-8 覆盖写)。
45
53
  *
46
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
54
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
47
55
  * @param relPath 相对该分类目录的路径,`/` 分隔;越界会被拦截。
48
56
  * @param content 要写入的 UTF-8 文本内容。
49
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
57
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
50
58
  * @returns 无返回。
51
59
  * @throws 路径越界、分类未知或工作空间不可达时抛错。
52
60
  */
53
- write: (category: WorkspaceCategory, relPath: string, content: string, workspaceId?: string | null) => Promise<void>;
61
+ write: (category: FileCategory, relPath: string, content: string, workspaceId?: string | null) => Promise<void>;
54
62
  /**
55
63
  * 读取分类目录下某个文件的二进制内容。
56
64
  *
57
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
65
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
58
66
  * @param relPath 相对该分类目录的路径,`/` 分隔;越界会被拦截。
59
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
67
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
60
68
  * @returns 文件的二进制内容(Buffer)。
61
69
  * @throws 文件不存在、路径越界、分类未知或工作空间不可达时抛错。
62
70
  */
63
- readBinary: (category: WorkspaceCategory, relPath: string, workspaceId?: string | null) => Promise<Uint8Array>;
71
+ readBinary: (category: FileCategory, relPath: string, workspaceId?: string | null) => Promise<Uint8Array>;
64
72
  /**
65
73
  * 把二进制数据写入分类目录下的相对路径,父目录不存在则自动创建(覆盖写)。
66
74
  *
67
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
75
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
68
76
  * @param relPath 相对该分类目录的路径,`/` 分隔;越界会被拦截。
69
77
  * @param data 要写入的二进制数据(Uint8Array)。
70
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
78
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
71
79
  * @returns 无返回。
72
80
  * @throws 路径越界、分类未知或工作空间不可达时抛错。
73
81
  */
74
- writeBinary: (category: WorkspaceCategory, relPath: string, data: Uint8Array, workspaceId?: string | null) => Promise<void>;
82
+ writeBinary: (category: FileCategory, relPath: string, data: Uint8Array, workspaceId?: string | null) => Promise<void>;
75
83
  /**
76
84
  * 列出分类目录(或其下子目录)的直接子项;软链会被跳过。
77
85
  *
78
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
86
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
79
87
  * @param relPath 相对该分类目录的子目录路径,`/` 分隔;默认 `""` 即分类根目录。越界会被拦截。
80
88
  * @param options 可选配置:`recursive` 为 `true` 时深度展开所有后代;`workspaceId` 指定工作空间。
81
89
  * @returns 子项列表(FileEntry[]),目录不存在时返回空数组。
82
90
  * @throws 路径越界、分类未知或工作空间不可达时抛错。
83
91
  */
84
- list: (category: WorkspaceCategory, relPath?: string, options?: FileListOptions) => Promise<FileEntry[]>;
92
+ list: (category: FileCategory, relPath?: string, options?: FileListOptions) => Promise<FileEntry[]>;
85
93
  /**
86
94
  * 取某个文件或目录的元信息(是否目录、大小、修改时间、创建时间)。
87
95
  *
88
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
96
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
89
97
  * @param relPath 相对该分类目录的路径,`/` 分隔;越界会被拦截。
90
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
98
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
91
99
  * @returns 文件元信息(FileStat);文件/目录不存在时返回 `null`。
92
100
  * @throws 路径越界、分类未知或工作空间不可达时抛错。
93
101
  */
94
- stat: (category: WorkspaceCategory, relPath: string, workspaceId?: string | null) => Promise<FileStat | null>;
102
+ stat: (category: FileCategory, relPath: string, workspaceId?: string | null) => Promise<FileStat | null>;
95
103
  /**
96
104
  * 判断分类目录下的文件或目录是否存在。
97
105
  *
98
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
106
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
99
107
  * @param relPath 相对该分类目录的路径,`/` 分隔;越界会被拦截。
100
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
108
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
101
109
  * @returns 存在返回 `true`,否则返回 `false`。
102
110
  * @throws 路径越界、分类未知或工作空间不可达时抛错。
103
111
  */
104
- exists: (category: WorkspaceCategory, relPath: string, workspaceId?: string | null) => Promise<boolean>;
112
+ exists: (category: FileCategory, relPath: string, workspaceId?: string | null) => Promise<boolean>;
105
113
  /**
106
114
  * 在分类目录下创建目录(递归创建缺失的父级)。
107
115
  *
108
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
116
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
109
117
  * @param relPath 相对该分类目录的路径,`/` 分隔;越界会被拦截。
110
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
118
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
111
119
  * @returns 无返回。
112
120
  * @throws 路径越界、分类未知或工作空间不可达时抛错。
113
121
  */
114
- mkdir: (category: WorkspaceCategory, relPath: string, workspaceId?: string | null) => Promise<void>;
122
+ mkdir: (category: FileCategory, relPath: string, workspaceId?: string | null) => Promise<void>;
115
123
  /**
116
124
  * 删除分类目录下的文件或目录(递归、强制)。
117
125
  *
118
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
126
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
119
127
  * @param relPath 相对该分类目录的路径,`/` 分隔;**不能为空**,否则拒绝删除分类根目录。
120
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
128
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
121
129
  * @returns 无返回。
122
130
  * @throws relPath 为空时抛 `FILE_BAD_PATH`(400);路径越界、分类未知或工作空间不可达时抛错。
123
131
  */
124
- remove: (category: WorkspaceCategory, relPath: string, workspaceId?: string | null) => Promise<void>;
132
+ remove: (category: FileCategory, relPath: string, workspaceId?: string | null) => Promise<void>;
125
133
  /**
126
134
  * 在同一分类目录内把文件或目录从 fromRel 移至 toRel(自动创建目标父目录)。
127
135
  *
128
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
136
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
129
137
  * @param fromRel 源路径,相对该分类目录,`/` 分隔;越界会被拦截。
130
138
  * @param toRel 目标路径,相对该分类目录,`/` 分隔;越界会被拦截。
131
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
139
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
132
140
  * @returns 无返回。
133
141
  * @throws 源/目标路径越界、分类未知或工作空间不可达时抛错。
134
142
  */
135
- rename: (category: WorkspaceCategory, fromRel: string, toRel: string, workspaceId?: string | null) => Promise<void>;
143
+ rename: (category: FileCategory, fromRel: string, toRel: string, workspaceId?: string | null) => Promise<void>;
136
144
  /**
137
145
  * 把「分类 + 相对路径」解析成宿主机上的绝对路径。
138
146
  *
139
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
147
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
140
148
  * @param relPath 相对该分类目录的路径,`/` 分隔;默认 `""` 即分类根目录。越界会被拦截。
141
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
149
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
142
150
  * @returns 宿主机绝对路径。
143
151
  * @throws 路径越界、分类未知或工作空间不可达时抛错。
144
152
  */
145
- resolve: (category: WorkspaceCategory, relPath?: string, workspaceId?: string | null) => Promise<string>;
153
+ resolve: (category: FileCategory, relPath?: string, workspaceId?: string | null) => Promise<string>;
146
154
  /**
147
155
  * 把「分类 + 相对路径」解析成 `file://` URL,供 `<img>` / `<video>` 等直接引用。
148
156
  *
149
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
157
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
150
158
  * @param relPath 相对该分类目录的路径,`/` 分隔;默认 `""` 即分类根目录。越界会被拦截。
151
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
159
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
152
160
  * @returns `file://` 协议 URL 字符串。
153
161
  * @throws 路径越界、分类未知或工作空间不可达时抛错。
154
162
  */
155
- fileUrl: (category: WorkspaceCategory, relPath?: string, workspaceId?: string | null) => Promise<string>;
163
+ fileUrl: (category: FileCategory, relPath?: string, workspaceId?: string | null) => Promise<string>;
156
164
  /**
157
165
  * 计算从 fromRel(目录)到 toRel 的相对路径(`/` 分隔),跨分类可用——用于笔记内引用附件算可移植的相对引用。
158
166
  *
159
- * @param fromCategory 源分类目录:`document` / `image` / `audio` / `video` / `blob`。
167
+ * @param fromCategory 源分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
160
168
  * @param fromRel 源路径(目录),相对 fromCategory,`/` 分隔;越界会被拦截。
161
- * @param toCategory 目标分类目录:`document` / `image` / `audio` / `video` / `blob`。
169
+ * @param toCategory 目标分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
162
170
  * @param toRel 目标路径,相对 toCategory,`/` 分隔;越界会被拦截。
163
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
171
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
164
172
  * @returns `/` 分隔的相对路径字符串。
165
173
  * @throws 路径越界、分类未知或工作空间不可达时抛错。
166
174
  */
167
- relativePath: (fromCategory: WorkspaceCategory, fromRel: string, toCategory: WorkspaceCategory, toRel: string, workspaceId?: string | null) => Promise<string>;
175
+ relativePath: (fromCategory: FileCategory, fromRel: string, toCategory: FileCategory, toRel: string, workspaceId?: string | null) => Promise<string>;
168
176
  /**
169
177
  * 订阅分类目录的文件变更通知(流式推送而非直接返回)。
170
178
  * 建立订阅后,目录内任何文件/子目录的增删改都会通过 IPC 频道 `file.watch.<watchId>` 推送给调用方所在渲染进程;
171
179
  * 同一 watchId 重复订阅时先关闭旧 watcher。渲染进程销毁时自动关闭其所有 watcher。
172
180
  *
173
- * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`。
181
+ * @param category 分类目录:`document` / `image` / `audio` / `video` / `blob`,或设备空间 `devicespace`。
174
182
  * @param watchId 订阅标识,用于后续 `file.unwatch` 取消订阅;也作为推送频道名的一部分。
175
- * @param workspaceId 目标工作空间;省略则用当前默认工作空间。
183
+ * @param workspaceId 目标工作空间;省略则用当前默认工作空间。`devicespace` 忽略它。
176
184
  * @returns 无返回(Promise resolve 表示订阅已建立)。
177
185
  * @throws 分类未知或工作空间不可达时抛错。
178
186
  */
179
- watch: (category: WorkspaceCategory, onChange: (change: FileChange) => void, workspaceId?: string | null) => (() => void);
187
+ watch: (category: FileCategory, onChange: (change: FileChange) => void, workspaceId?: string | null) => (() => void);
180
188
  }
package/types/Plugin.d.ts CHANGED
@@ -79,6 +79,31 @@ export default class Plugin {
79
79
  * @returns 无返回。
80
80
  */
81
81
  floating: (options?: any) => Promise<any>;
82
+ /**
83
+ * 注册全局快捷键:系统级热键,焦点在别的程序上也触发;按下时本视图收到 `plugin.onShortcut` 事件
84
+ * (`window.ipcRenderer.on("plugin.onShortcut", (_e, id) => …)`),参数是这里给的 id。
85
+ * 同一 id 重复注册是覆盖;视图销毁时全部注销。被别的程序或宿主占着的键注册不上,返回值里对应 false。
86
+ *
87
+ * @param items `[{id, accelerator}]`,accelerator 用 Electron 写法(F10、CommandOrControl+Shift+K)。
88
+ * @returns `{id: 是否注册成功}`。
89
+ */
90
+ registerShortcuts: (items: Array<{
91
+ id: string;
92
+ accelerator: string;
93
+ }>) => Promise<Record<string, boolean>>;
94
+ /**
95
+ * 声明发起方窗口正忙(在录制、在执行任务):收起期间不被「收起超时自动关」回收,
96
+ * 否则藏在后台干活的视图会连窗口一起被关掉,活也就没了。忙完调 false 解除;窗口关掉时自动解除。
97
+ *
98
+ * @param busy 忙还是不忙。
99
+ */
100
+ setBusy: (busy: boolean) => Promise<void>;
101
+ /**
102
+ * 注销本视图注册的全局快捷键;不给 ids 就全部注销。
103
+ *
104
+ * @param ids 要注销的快捷键 id。
105
+ */
106
+ unregisterShortcuts: (ids?: string[]) => Promise<void>;
82
107
  /**
83
108
  * 收起发起方插件窗口。发起方是主窗口时回到启动器主页;
84
109
  * floating 浮窗做 cloak(不驱逐渲染表面,再唤起不白屏),普通窗口真正 hide()。
@@ -93,6 +118,13 @@ export default class Plugin {
93
118
  * @returns 切换后的置顶状态(boolean)。
94
119
  */
95
120
  toggleAlwaysOnTop: () => Promise<any>;
121
+ /**
122
+ * 把插件自己的窗口从截屏、录屏里排除(Windows 的 WDA_EXCLUDEFROMCAPTURE):屏幕上照常可见,
123
+ * 截图与录制帧里没有它。录自己的操作时开,免得录制页盖住画面、OCR 读到一堆自己的字。
124
+ *
125
+ * @param enabled 开或关。
126
+ */
127
+ setContentProtection: (enabled: boolean) => Promise<void>;
96
128
  /**
97
129
  * 取发起方插件自身的完整信息。
98
130
  * 通过 webContents 逆向定位插件,返回插件实体字段 + 其全部命令(commands 数组)+
@@ -4,6 +4,9 @@ export default class PluginCommand {
4
4
  constructor(transport?: IpcRenderer);
5
5
  /**
6
6
  * 按命令名打开对应插件命令:先查找命令,记录一次使用,然后委托 PluginRunner 打开。
7
+ * standard 窗口默认是开合切换(已可见且聚焦时再打开即收起);payload 里两个宿主认的字段改这个行为:
8
+ * `reveal: true` 一律唤起并聚焦(把收起的窗口叫回来用它),`showInactive: true` 唤起但不抢焦点
9
+ * (别的窗口正在被自动化操作时用)。其余字段原样送到 plugin.onEnter。
7
10
  *
8
11
  * @param commandName 命令名称。
9
12
  * @param payload 进入参数,透传给 PluginRunner。
@@ -0,0 +1,31 @@
1
+ import { default as IpcRenderer } from './IpcRenderer';
2
+ export interface ElevationStatus {
3
+ /** 此刻发出的输入与截屏带管理员权限:宿主自己提权,或提权助手活着 */
4
+ elevated: boolean;
5
+ /** 提权助手进程活着 */
6
+ helper: boolean;
7
+ /** 宿主进程本身就带管理员权限 */
8
+ hostElevated: boolean;
9
+ }
10
+ export default class SystemElevation {
11
+ private transport;
12
+ constructor(transport?: IpcRenderer);
13
+ /**
14
+ * 让接下来的输入注入、截屏、找图找字、窗口唤起、起进程都带管理员权限。
15
+ * 宿主自己提权就直接返回;否则拉起提权助手进程,第一次弹系统授权框(UAC),之后整个会话都不再问。
16
+ * 以管理员身份运行的程序(游戏平台、游戏客户端、任务管理器等)只认同级或更高权限发来的输入与截屏,
17
+ * 录制、回放它们之前先调这个。
18
+ *
19
+ * @returns `{elevated, helper, hostElevated}`。
20
+ * @throws 用户在授权框上取消、助手起不来时抛错。
21
+ */
22
+ ensure: () => Promise<ElevationStatus>;
23
+ /**
24
+ * 此刻的权限状态,不会弹授权框。
25
+ */
26
+ status: () => Promise<ElevationStatus>;
27
+ /**
28
+ * 让提权助手退出;之后的输入与截屏回到宿主自己执行,下一次 ensure 会再弹一次授权框。
29
+ */
30
+ release: () => Promise<void>;
31
+ }
@@ -6,6 +6,8 @@ export interface RecorderStartOptions {
6
6
  stopKey?: number;
7
7
  /** 时长上限,缺省 10 分钟 */
8
8
  maxMs?: number;
9
+ /** 以管理员身份录制:经 UAC 拉起助手进程,能录到提权程序(游戏等)的画面与操作;用户在授权框上取消就抛错 */
10
+ elevated?: boolean;
9
11
  }
10
12
  export interface RecorderStatus {
11
13
  supported: boolean;
@@ -14,47 +16,63 @@ export interface RecorderStatus {
14
16
  elapsedMs: number;
15
17
  events: number;
16
18
  frames: number;
17
- /** 运行中的反作弊进程名;有它在时低层钩子整体收不到输入 */
18
- antiCheat: string[];
19
+ /** 这次录制带管理员权限 */
20
+ elevated: boolean;
21
+ }
22
+ export interface RecorderCapabilities {
23
+ supported: boolean;
24
+ /** 宿主自己就带管理员权限,不需要再授权 */
25
+ elevated: boolean;
26
+ /** 此刻以管理员身份运行的窗口标题:不授权就录不到它们 */
27
+ elevatedWindows: string[];
19
28
  }
20
29
  export interface RecorderResult {
21
30
  id: string;
22
31
  /** 轨迹目录绝对路径 */
23
32
  dir: string;
24
- /** 轨迹目录在工作空间「图片」目录下的相对路径:scripts/_recordings/<id> */
33
+ /** 轨迹目录在本插件设备空间下的相对路径:recordings/<id> */
25
34
  relPath: string;
26
35
  events: number;
27
36
  frames: number;
28
37
  durationMs: number;
29
38
  stoppedBy: "hotkey" | "api" | "timeout";
39
+ elevated: boolean;
30
40
  }
31
41
  export default class SystemRecorder {
32
42
  private transport;
33
43
  constructor(transport?: IpcRenderer);
34
44
  /**
35
45
  * 开始录制用户操作:低层钩子收鼠标键盘、定时截主屏,每个按下/抬起/按键带上前后帧与那一刻的前台窗口。
36
- * 只记事实,不做任何手势归并;录制期间每一拍向发起方视图推 `recorder.progress`(RecorderStatus)。
37
- * 产物落在工作空间「图片」目录 scripts/_recordings/<id>/:trajectory.json + frames/*.png。
46
+ * 只记事实,不做任何手势归并;录制期间每一拍向发起方视图推 `recorder.progress`(RecorderStatus),
47
+ * 收尾(F12、超时、stop)写完轨迹后推一次 `recorder.stopped`(RecorderResult),用 ipcRenderer.on 接;
48
+ * 录制没有产物就中断了(管理员助手退出)只推一次 active=false 的 `recorder.progress`。
49
+ * 产物落在**本插件的设备空间**里 recordings/<id>/:trajectory.json + frames/*.jpg —— 录到的是整屏画面
50
+ * 与窗口标题,只属于这台设备,不进工作空间;用 window.file 的 `devicespace` 分类按同样的相对路径读写。
38
51
  *
39
- * @param options `{fps?, stopKey?, maxMs?}`,缺省 4 帧/秒、F12 结束、10 分钟上限。
40
- * @returns `{id, dir, relPath, antiCheat}`;antiCheat 非空表示有反作弊进程在跑,钩子会整体收不到输入。
41
- * @throws 平台不支持(目前仅 Windows)、已有录制在进行、钩子安装失败时抛错。
52
+ * @param options `{fps?, stopKey?, maxMs?, elevated?}`,缺省 4 帧/秒、F12 结束、10 分钟上限;
53
+ * elevated 为真时经 UAC 申请管理员权限,先用 capabilities() 看要不要。
54
+ * @returns `{id, dir, relPath, elevated}`。
55
+ * @throws 平台不支持(目前仅 Windows)、已有录制在进行、钩子安装失败、用户在授权框上取消时抛错。
42
56
  */
43
57
  start: (options?: RecorderStartOptions) => Promise<{
44
58
  id: string;
45
59
  dir: string;
46
60
  relPath: string;
47
- antiCheat: string[];
61
+ elevated: boolean;
48
62
  }>;
49
63
  /**
50
64
  * 结束录制并写出轨迹。按 stopKey 结束的录制不必再调。
51
65
  *
52
- * @returns `{id, dir, relPath, events, frames, durationMs, stoppedBy}`。
66
+ * @returns `{id, dir, relPath, events, frames, durationMs, stoppedBy, elevated}`。
53
67
  * @throws 没有进行中的录制时抛错。
54
68
  */
55
69
  stop: () => Promise<RecorderResult>;
56
70
  /**
57
- * 录制状态:是否支持、是否进行中、已录事件数与帧数、运行中的反作弊进程。
71
+ * 录制状态:是否支持、是否进行中、已录事件数与帧数、是否带管理员权限。
58
72
  */
59
73
  status: () => Promise<RecorderStatus>;
74
+ /**
75
+ * 开录前问一次:宿主是否已带管理员权限、此刻有哪些以管理员身份运行的窗口(游戏等,不授权就录不到它们)。
76
+ */
77
+ capabilities: () => Promise<RecorderCapabilities>;
60
78
  }
@@ -22,11 +22,46 @@ export interface WaitOptions {
22
22
  timeoutMs?: number;
23
23
  /** 缺省 500 */
24
24
  intervalMs?: number;
25
+ /** 取消记号:中途调 cancelWait(token) 就以 cancelled 收尾,不再等到超时;同一记号可盖住一次任务里的全部等待 */
26
+ token?: string;
25
27
  }
26
28
  export interface FindTextOptions {
27
29
  searchRegion?: ScreenRect;
28
30
  /** OCR 检测缩放比,小于 1 更快但更粗 */
29
31
  detRatio?: number;
32
+ /** OCR 前把画面放大几倍:字高不到 14 像素时 1.5 能把认错的字认回来,代价三成时间 */
33
+ zoom?: number;
34
+ }
35
+ /**
36
+ * 找图的结果。找不到不是异常:found 为假时 best 是搜索区域里最像的那块与它的分数,对着 threshold 看是阈值太严还是模板过时;
37
+ * 区域比模板还小时 best 为 null。attempts 是查了几次(找一次就是 1),cancelled 说明是被取消记号中断的。
38
+ */
39
+ export interface ImageSearch {
40
+ found: boolean;
41
+ rect: ScreenRect | null;
42
+ best: (ScreenRect & {
43
+ score: number;
44
+ }) | null;
45
+ threshold: number;
46
+ attempts: number;
47
+ timeoutMs: number | null;
48
+ cancelled: boolean;
49
+ }
50
+ /** 找字的结果。found 为假时 visibleTexts 是这次 OCR 认到的全部文字,据此改目标词而不是盲试。 */
51
+ export interface TextSearch {
52
+ found: boolean;
53
+ rect: ScreenRect | null;
54
+ visibleTexts: string[];
55
+ attempts: number;
56
+ timeoutMs: number | null;
57
+ cancelled: boolean;
58
+ }
59
+ /** 等图像消失的结果。 */
60
+ export interface GoneWait {
61
+ gone: boolean;
62
+ attempts: number;
63
+ timeoutMs: number;
64
+ cancelled: boolean;
30
65
  }
31
66
  export default class SystemScreen {
32
67
  private transport;
@@ -63,14 +98,13 @@ export default class SystemScreen {
63
98
  */
64
99
  getCursorPoint: () => Promise<any>;
65
100
  /**
66
- * 在当前屏幕中查找与模板图片匹配的区域,返回屏幕原生坐标系里的矩形。
101
+ * 在当前屏幕中找一次模板图片。
67
102
  *
68
103
  * @param base64String 模板图片的 Base64(支持 `data:image/...;base64,` 前缀)。
69
104
  * @param options `{confidence?, searchRegion?, scale?}`;scale 省略时按常见显示缩放自适应。
70
- * @returns 匹配矩形 `{x, y, width, height}`。
71
- * @throws 未达阈值时抛错,信息里带最佳候选分数与位置。
105
+ * @returns 结果对象;找不到不是异常,看 found 与 best。
72
106
  */
73
- find: (base64String: string, options?: FindImageOptions) => Promise<ScreenRect>;
107
+ find: (base64String: string, options?: FindImageOptions) => Promise<ImageSearch>;
74
108
  /**
75
109
  * 找出屏幕上模板图片的所有命中,按匹配分数降序。
76
110
  *
@@ -80,50 +114,53 @@ export default class SystemScreen {
80
114
  */
81
115
  findAll: (base64String: string, options?: FindImageOptions) => Promise<ScreenRect[]>;
82
116
  /**
83
- * 轮询屏幕直到模板图片出现,返回其矩形。
117
+ * 轮询屏幕直到模板图片出现。
84
118
  *
85
119
  * @param base64String 模板图片的 Base64。
86
- * @param options 查找参数外加 `{timeoutMs?, intervalMs?}`,缺省 5000 / 500。
87
- * @returns 匹配矩形 `{x, y, width, height}`。
88
- * @throws 超时仍未出现时抛错。
120
+ * @param options 查找参数外加 `{timeoutMs?, intervalMs?, token?}`,缺省 5000 / 500。
121
+ * @returns 同 find;超时或被取消时 found 为假,attempts 是查了几次。
89
122
  */
90
- waitFor: (base64String: string, options?: FindImageOptions & WaitOptions) => Promise<ScreenRect>;
123
+ waitFor: (base64String: string, options?: FindImageOptions & WaitOptions) => Promise<ImageSearch>;
91
124
  /**
92
125
  * 轮询屏幕直到模板图片消失。
93
126
  *
94
127
  * @param base64String 模板图片的 Base64。
95
- * @param options 查找参数外加 `{timeoutMs?, intervalMs?}`。
96
- * @returns 无返回。
97
- * @throws 超时仍在屏幕上时抛错。
128
+ * @param options 查找参数外加 `{timeoutMs?, intervalMs?, token?}`。
129
+ * @returns `{gone, attempts, timeoutMs, cancelled}`;超时仍在屏幕上时 gone 为假,不抛错。
98
130
  */
99
- waitUntilGone: (base64String: string, options?: FindImageOptions & WaitOptions) => Promise<void>;
131
+ waitUntilGone: (base64String: string, options?: FindImageOptions & WaitOptions) => Promise<GoneWait>;
100
132
  /**
101
- * 用 OCR 在屏幕上找一段文字,返回该词的矩形。
133
+ * 用 OCR 在屏幕上找一次文字。
102
134
  *
103
135
  * @param text 目标文字,整词匹配。
104
- * @param options `{searchRegion?, detRatio?}`。
105
- * @returns 文字矩形 `{x, y, width, height}`。
106
- * @throws 未找到时抛错,信息里带当前屏幕可见的文字列表。
136
+ * @param options `{searchRegion?, detRatio?, zoom?}`。
137
+ * @returns 结果对象;找不到不是异常,visibleTexts 是这次认到的全部文字。
107
138
  */
108
- findText: (text: string, options?: FindTextOptions) => Promise<ScreenRect>;
139
+ findText: (text: string, options?: FindTextOptions) => Promise<TextSearch>;
109
140
  /**
110
- * 轮询屏幕直到指定文字出现,返回该词的矩形。
141
+ * 轮询屏幕直到指定文字出现。
111
142
  *
112
143
  * @param text 目标文字,整词匹配。
113
- * @param options `{searchRegion?, detRatio?, timeoutMs?, intervalMs?}`。
114
- * @returns 文字矩形 `{x, y, width, height}`。
115
- * @throws 超时仍未出现时抛错,信息里带当前屏幕可见的文字列表。
144
+ * @param options `{searchRegion?, detRatio?, zoom?, timeoutMs?, intervalMs?, token?}`。
145
+ * @returns 同 findText;超时或被取消时 found 为假,visibleTexts 是最后一次认到的文字。
116
146
  */
117
- waitForText: (text: string, options?: FindTextOptions & WaitOptions) => Promise<ScreenRect>;
147
+ waitForText: (text: string, options?: FindTextOptions & WaitOptions) => Promise<TextSearch>;
148
+ /**
149
+ * 取消带该记号的等待(等图、等文字、等窗口、等图像消失):它们下一拍就以 cancelled(等窗口是 null)收尾,不再等到超时。
150
+ * 记号是调用方在 WaitOptions.token 里给的字符串,同一个记号可以盖住一次任务执行里的全部等待。
151
+ */
152
+ cancelWait: (token: string) => Promise<void>;
118
153
  /**
119
154
  * 对显示器(或其中一块区域)做 OCR,返回每段文字及其屏幕坐标矩形。
120
155
  *
121
- * @param options `{display?, region?}`,region 为该显示器物理像素坐标;缺省光标所在显示器整屏。
156
+ * @param options `{display?, region?, image?, zoom?}`,region 为该显示器物理像素坐标;缺省光标所在显示器整屏;zoom 大于 1 先放大再认。
122
157
  * @returns `[{text, score, x, y, width, height}]`,坐标已加上显示器原点。
123
158
  */
124
159
  recognizeText: (options?: {
125
160
  display?: number;
126
161
  region?: ScreenRect;
162
+ image?: string;
163
+ zoom?: number;
127
164
  }) => Promise<Array<ScreenRect & {
128
165
  text: string;
129
166
  score: number;
@@ -140,6 +177,7 @@ export default class SystemScreen {
140
177
  textBoxes?: ScreenRect[];
141
178
  minSide?: number;
142
179
  maxSide?: number;
180
+ image?: string;
143
181
  }) => Promise<Array<ScreenRect & {
144
182
  pixels: number;
145
183
  }>>;
@@ -160,6 +198,39 @@ export default class SystemScreen {
160
198
  score: number;
161
199
  scale: number;
162
200
  }>>;
201
+ /**
202
+ * 对一帧给定图片做一批分析:若干模板各自匹配、若干区域各自 OCR,图片只解码一次,同一区域只 OCR 一次。
203
+ * 每一拍截一帧、拿这一帧判断全部条件的循环靠它把 IPC 压到每拍一次。
204
+ *
205
+ * @param frame 被分析图片的 Base64(整个显示器的截图)。
206
+ * @param request `{templates?: [{id, image, region?, scale?, minScore?, maxResults?}], texts?: [{id, region?, detRatio?, zoom?}]}`,region 相对图片左上角。
207
+ * @returns `{templates: {id: [{x, y, width, height, score, scale}]}, texts: {id: [{text, score, x, y, width, height}]}}`,坐标相对图片左上角。
208
+ */
209
+ analyzeFrame: (frame: string, request?: {
210
+ templates?: Array<{
211
+ id: string;
212
+ image: string;
213
+ region?: ScreenRect;
214
+ scale?: number;
215
+ minScore?: number;
216
+ maxResults?: number;
217
+ }>;
218
+ texts?: Array<{
219
+ id: string;
220
+ region?: ScreenRect;
221
+ detRatio?: number;
222
+ zoom?: number;
223
+ }>;
224
+ }) => Promise<{
225
+ templates: Record<string, Array<ScreenRect & {
226
+ score: number;
227
+ scale: number;
228
+ }>>;
229
+ texts: Record<string, Array<ScreenRect & {
230
+ text: string;
231
+ score: number;
232
+ }>>;
233
+ }>;
163
234
  /**
164
235
  * 两张同尺寸截图的差异包围框。
165
236
  *
@@ -130,6 +130,7 @@ export default class SystemWindow {
130
130
  execPath?: string;
131
131
  timeoutMs?: number;
132
132
  intervalMs?: number;
133
+ token?: string;
133
134
  }) => Promise<{
134
135
  id: number;
135
136
  title: string;