@mearl/client 3.0.0-beta.3 → 3.0.0-beta.5
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/README.md +94 -15
- package/dist/check.d.ts +5 -6
- package/dist/check.js +3 -6
- package/dist/cli.js +15 -43
- package/dist/commandHelp.d.ts +3 -0
- package/dist/commandHelp.js +79 -0
- package/dist/commands.d.ts +24 -14
- package/dist/commands.js +23 -18
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/nativeHost.js +1 -1
- package/dist/plugins/clients/pluginHostConnection.d.ts +2 -0
- package/dist/plugins/clients/pluginHostConnection.js +3 -1
- package/dist/plugins/pluginRegistry.d.ts +1 -1
- package/dist/plugins/pluginRegistry.js +12 -4
- package/dist/plugins/runtime/pluginCommandWorker.js +8 -6
- package/dist/plugins/runtime/pluginExecutor.d.ts +5 -0
- package/dist/plugins/runtime/pluginExecutor.js +15 -2
- package/dist/plugins/runtime/pluginExecutorProtocol.d.ts +4 -0
- package/dist/plugins/runtime/pluginExecutorSupervisor.js +2 -1
- package/dist/plugins/runtime/pluginHostServer.js +4 -0
- package/dist/plugins/runtime/pluginPageManager.js +0 -4
- package/dist/plugins/runtime/pluginRevision.js +0 -51
- package/dist/plugins/runtime/pluginStateStore.js +3 -8
- package/dist/plugins/runtime/pluginTaskManager.js +3 -7
- package/dist/plugins/sdk/plugin.js +1 -1
- package/dist/plugins/sdk/pluginModelDefinition.js +0 -6
- package/dist/plugins/sdk/pluginPageDefinition.d.ts +0 -1
- package/dist/plugins/sdk/pluginPageDefinition.js +2 -18
- package/dist/plugins/sdk/pluginRecoveryDefinition.js +0 -3
- package/dist/plugins/sdk/pluginStateDefinition.d.ts +0 -1
- package/dist/plugins/sdk/pluginStateDefinition.js +0 -11
- package/dist/runtimePaths.d.ts +1 -0
- package/dist/runtimePaths.js +1 -0
- package/dist/script.d.ts +30 -0
- package/dist/script.js +260 -0
- package/dist/scriptCli.d.ts +1 -0
- package/dist/scriptCli.js +94 -0
- package/dist/scriptErrors.d.ts +8 -0
- package/dist/scriptErrors.js +21 -0
- package/dist/scriptWorker.d.ts +1 -0
- package/dist/scriptWorker.js +50 -0
- package/dist/sdkMethods.d.ts +3 -0
- package/dist/sdkMethods.js +9 -0
- package/dist/socket.js +2 -1
- package/package.json +10 -8
package/README.md
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
## 安装
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm
|
|
12
|
+
npm install @mearl/client
|
|
13
13
|
# 或
|
|
14
|
-
pnpm
|
|
14
|
+
pnpm add @mearl/client
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
## SDK 使用
|
|
@@ -60,7 +60,7 @@ mearl check
|
|
|
60
60
|
mearl check --local --json
|
|
61
61
|
|
|
62
62
|
# 更新本地环境
|
|
63
|
-
npx
|
|
63
|
+
npx @mearl/setup update --local
|
|
64
64
|
|
|
65
65
|
# 查看某个 action 的帮助
|
|
66
66
|
mearl <action> --help
|
|
@@ -74,6 +74,77 @@ mearl browser_list
|
|
|
74
74
|
mearl browser_release
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
### Agent 脚本
|
|
78
|
+
|
|
79
|
+
`mearl run` 从 stdin 或 `--file <path>` 读取 JavaScript,预注入 `mearl` SDK,支持顶层 `await`、条件、循环和数据处理,无需在任务目录安装依赖:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
mearl run --session new <<'JS'
|
|
83
|
+
const tab = await mearl.tabOpen({ url: 'https://example.com', reuse: 'prefer' });
|
|
84
|
+
console.log({ tabId: tab.tabId, session: mearl.session });
|
|
85
|
+
console.log(await mearl.pageSnapshot({ tabId: tab.tabId, mode: 'viewport' }));
|
|
86
|
+
JS
|
|
87
|
+
|
|
88
|
+
mearl run --session '<sessionId>' --file /absolute/path/next-step.mjs
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
方法名与 SDK 一致,例如 `mearl.pageClick(params, options?)`、`mearl.getRequests(params, options?)`、
|
|
92
|
+
`mearl.tabList(options?)`,通用调用使用 `mearl.invoke(action, payload, options?)`。
|
|
93
|
+
参数和返回值沿用普通 action,可查 `mearl <action> --help`;动作失败仍以 `action.success`、
|
|
94
|
+
`stage` 和 `attempt` 表达,由脚本判断后续逻辑。`console.log` 输出必要结果,`console.error` 输出诊断;
|
|
95
|
+
返回数据不会自动打印。`run_actions` 适合无需判断的紧密序列;脚本适合结果可由代码判断的组合流程。
|
|
96
|
+
需要模型理解页面时,输出最小观察并在下一轮继续。
|
|
97
|
+
|
|
98
|
+
脚本用 `help` 查询现有命令定义,返回文本由调用方按需输出;CLI `--help` 与脚本共用同一份参数、结果说明、约束和示例。
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
console.log(await mearl.help()); // 方法与已注册插件清单
|
|
102
|
+
console.log(await mearl.help('pageClick')); // 也接受 page_click
|
|
103
|
+
console.log(await mearl.help({ plugin: 'notes', command: 'search' }));
|
|
104
|
+
|
|
105
|
+
const result = await mearl.invokePlugin('notes', { command: 'search', input: { query: 'project' } });
|
|
106
|
+
console.log(mearl.structure(result));
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`structure(value, options?)` 同步提取已取得结果的结构,叶子值替换为类型;默认深度 5、每对象 50 个字段、
|
|
110
|
+
每数组 1 个样本、总计 2000 个节点,可通过 `maxDepth`、`maxKeys`、`arraySamples`、`maxNodes` 调整。
|
|
111
|
+
输出包含数组总长度与样本,遇到循环引用、访问器或资源限制会标记;样本不保证覆盖所有可选字段或不同数组元素,
|
|
112
|
+
业务含义以命令帮助为准。普通 SDK 同样导出 `structure`,它与请求 `keysOnly` 使用相同的提取逻辑;
|
|
113
|
+
`keysOnly` 也遵循上述默认字段和节点上限,超出部分会标记截断。
|
|
114
|
+
插件帮助在隔离进程中加载注册信息,不执行所查询的业务命令。
|
|
115
|
+
|
|
116
|
+
已注册 Skill 命令也可直接组合,命令名和输入来自该插件的 `--help`:
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
const result = await mearl.invokePlugin('notes', {
|
|
120
|
+
command: 'search',
|
|
121
|
+
input: { query: 'project' },
|
|
122
|
+
});
|
|
123
|
+
console.log(result);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
脚本的 `--browser`、`--session`、`--connector`、`--local`、`--cdp` 由浏览器 API 和插件调用继承;
|
|
127
|
+
单次调用的 `options` 可覆盖。`--server` 指定 cloud-server。`--session new` 成功建立会话后,
|
|
128
|
+
后续调用自动使用返回的 ID,`mearl.session` 和 CLI stderr 同时提供会话信息。
|
|
129
|
+
先 await 建立会话的浏览器调用,再执行并发或 Skill 命令。脚本结束断开 SDK 连接,控制会话保留;
|
|
130
|
+
按任务生命周期显式调用 `mearl.browserRelease`。每轮重建 JS 变量,使用已返回的 `sessionId` 和 `tabId` 继续任务。
|
|
131
|
+
|
|
132
|
+
每次脚本在独立 Node.js 进程运行,继承调用方的工作目录和环境,拥有该宿主的 Node.js 权限;
|
|
133
|
+
它不是安全沙箱。用 `await import('node:fs/promises')` 等动态 import 加载模块;
|
|
134
|
+
`--file` 的内容也作为脚本函数执行,不支持静态 `import` / `export`。本地模块建议使用绝对文件 URL。
|
|
135
|
+
`window` / `document` 仅在 `mearl.pageEval` 的页面表达式中可用。插件 registry、本地文件和身份认证均属于运行 CLI 的宿主。
|
|
136
|
+
|
|
137
|
+
`--timeout <seconds>` 限制整个脚本,默认 60 秒;单次 API 的 `options.timeoutSec` 限制该调用。
|
|
138
|
+
stdout 与 stderr 合计最多 256 KiB,源码最多 1 MiB。await 所有 SDK 调用;漏写 await、超时、取消或脚本异常以退出码 1 结束,
|
|
139
|
+
输入错误以 2 结束。取消和超时停止脚本及其进程组,并向仍在等待的 Skill 调用传递取消信号;已派发的浏览器或业务操作可能继续生效,
|
|
140
|
+
错误中的 `pendingActions` / `mayHaveLateEffects` 用于判断是否需要核验现场,不能直接重跑整段脚本。
|
|
141
|
+
Skill 命令使用既有隔离执行器,stdout/stderr 计入脚本输出上限;异常诊断也会按大小裁剪。
|
|
142
|
+
执行结果的 `success` 仅表示脚本正常完成,动作结果与业务成功仍需由代码检查。
|
|
143
|
+
|
|
144
|
+
SDK 宿主可从 `@mearl/client/script` 导入 `executeScript(code, options)`,得到
|
|
145
|
+
`{ success, stdout, stderr, session?, error?, executionStopped, mayHaveLateEffects, pendingActions }`。
|
|
146
|
+
它支持 `signal` 取消和 `executionTimeoutSec`,与 CLI、MCP 共用执行器。
|
|
147
|
+
|
|
77
148
|
### 控制会话与文件
|
|
78
149
|
|
|
79
150
|
多步骤任务首次 `tab_open` 或 `page_snapshot` 使用 `--session new`,响应在原结果旁附带 `session`,后续用其 `sessionId` 自动路由,无需额外创建/查询 action。Session 绑定一个浏览器与传输;页面首次使用自动关联,复用页面与新建页面分别记录。同一页面只允许一个会话控制,旧调用不能绕过占用;Session 不隔离 Cookie 或存储。
|
|
@@ -93,6 +164,8 @@ mearl browser_release --session '<sessionId>' --payload '{"closeCreatedTabs":tru
|
|
|
93
164
|
|
|
94
165
|
用户在 Popup 的浏览器管理页接管、交还或结束会话。交接后排队旧请求和批次后续步骤停止;已派发动作可能仍然生效。交还后重新取快照。CDP-only 环境可在用户明确确认后用同一 `browser_control` 的 `operation:"resume"` 交还,无需额外 action。会话跨 CLI 连接保留,宿主进程退出后失效;不会根据空闲时间清理用户正在操作的会话。
|
|
95
166
|
|
|
167
|
+
扩展中新开的后台页面默认加入折叠的 `Mearl` 标签组,前台新页进入展开的 `Mearl · 待操作` 组。两个管理组统一复用同一个已有窗口,同名管理组会合并;页面打开的后续标签也收进该窗口,并继承源组的展开状态。工作窗口关闭后重新选择已有窗口,不额外创建窗口。显式传入 `tab_open.group` 时使用指定分组;复用普通页面时保留原窗口和分组。接管会话只把管理组内的会话页面移入待操作组;交还后,仍在待操作组内的会话页面收回 `Mearl` 组。用户正在查看组内其他页面,或窗口没有其他可显示页面时,保留展开状态。
|
|
168
|
+
|
|
96
169
|
带 Session 的释放只影响该会话,默认保留页面及环境;`closeCreatedTabs` 只关闭新建页,`keepTabIds` 保留结果页,复用的用户页面始终保留。`preserveEnvironment:false` 清理这些页面的环境。活动会话存在时,浏览器级释放与关闭会被拒绝。
|
|
97
170
|
|
|
98
171
|
`page_download` 先监听再点击,返回浏览器文件产物;CLI `--output` 或 SDK `saveArtifact(artifact, absolutePath, { session })` 以 256 KiB 分块导出到调用方。`page_upload.files` 接受调用方绝对路径 `{filePath}` 或浏览器 `{artifactId}`,`filePaths` 仍指浏览器宿主路径。单文件最多 256 MiB;保存到调用方的结果独立于临时产物。会话释放清理临时产物,`keepArtifactIds` 可保留指定产物;无会话产物空闲一小时回收,宿主退出清理暂存。
|
|
@@ -105,7 +178,7 @@ Provider 浏览器与扩展、CDP 浏览器一起出现在 `browser_list` 中,
|
|
|
105
178
|
全局 `browserId` 参与路由。Provider 的安装、更新和卸载由 `@mearl/setup` 管理:
|
|
106
179
|
|
|
107
180
|
```bash
|
|
108
|
-
npx
|
|
181
|
+
npx @mearl/setup provider install @example/mearl-provider-device
|
|
109
182
|
|
|
110
183
|
# 查看 Provider 当前版本的能力、参数与使用流程
|
|
111
184
|
mearl example-device --help
|
|
@@ -118,8 +191,8 @@ mearl browser_list
|
|
|
118
191
|
mearl tab_list --browser '<browserId>'
|
|
119
192
|
mearl page_snapshot --payload '{"tabId":12345}' --browser '<browserId>'
|
|
120
193
|
|
|
121
|
-
npx
|
|
122
|
-
npx
|
|
194
|
+
npx @mearl/setup provider update brow
|
|
195
|
+
npx @mearl/setup provider uninstall brow
|
|
123
196
|
```
|
|
124
197
|
|
|
125
198
|
`browser_launch` 创建 `pairing` 实例时会直接返回 `pairing.url` 和可选的配对码;Agent 应把 URL 直接渲染为
|
|
@@ -150,6 +223,10 @@ stdout 在管道或重定向时默认输出紧凑 JSON,在交互终端默认
|
|
|
150
223
|
需要同时读取全文时显式传 `includeBody:true`。省略正文时不返回 `postData` / `responseBody` 字段;
|
|
151
224
|
未指定字段时默认返回正文。
|
|
152
225
|
|
|
226
|
+
`send_request` 默认携带所选浏览器中匹配完整目标 URL 的普通 Cookie,以及目标 URL 第一方站点对应的 CHIPS Cookie。例如 `https://app.example.com/` 使用 `https://example.com` 分区,不依赖已打开页面。站点按协议和公共后缀列表(含私有后缀)确定;排除其他站点、其他协议、跨站祖先及不透明分区。保留不同路径下的同名 Cookie,不额外拼入父站点的 host-only Cookie。嵌入其他站点的第三方分区、页面签名或完整页面身份仍需使用目标页面中的 `page_eval` 请求。
|
|
227
|
+
响应中的 `Set-Cookie` 会更新原浏览器,后续调用可以使用刷新后的登录态;`withCookies:false` 同时关闭自动携带与响应回写。扩展由 Chrome 处理响应 Cookie;CDP 的 Node 请求在返回前通过原页面的浏览器连接回写,支持 HttpOnly、默认路径、过期时间和删除。CDP 响应存在 `Set-Cookie` 时附带 `cookieSync: { updatedCount, rejectedCount, failedCount }`,不返回 Cookie 值;无法确定分区的 Partitioned 响应 Cookie 计入拒绝数。同步失败不撤销已完成的 HTTP 操作,应检查状态而非直接重发业务请求。MTop 普通请求及流式请求使用相同的响应刷新机制。
|
|
228
|
+
扩展的临时凭据注入仅匹配原始完整请求 URL,重定向由 Chrome 按新 URL 选择 Cookie。CDP 的 Node 请求(含 MTop 流式请求)不自动跟随重定向,避免将原 URL 的 Cookie 或签名传到其他地址。
|
|
229
|
+
|
|
153
230
|
Mearl 默认聚合当前机器和已配置 cloud-server 下的浏览器,并通过 `browser_list` 返回的全局
|
|
154
231
|
`browserId` 自动路由。以下覆盖参数仅在限定宿主或连接、传输排障时使用,因此不出现在默认 help:
|
|
155
232
|
|
|
@@ -165,7 +242,7 @@ Mearl 默认聚合当前机器和已配置 cloud-server 下的浏览器,并通
|
|
|
165
242
|
client、native-host、cloud-connector 和扩展的最新发布版本。发布版本查询最长等待约 1.5 秒,
|
|
166
243
|
无法访问发布源时会提示无法确认更新,不影响浏览器可用性检查。`--json` 输出机器可读的检查结果,
|
|
167
244
|
与 `--updates` 同用时包含更新状态。发现更新时会提示适用的命令:client/native-host 使用
|
|
168
|
-
`npx
|
|
245
|
+
`npx @mearl/setup update`,setup 管理的扩展使用 `npx @mearl/setup extension`。
|
|
169
246
|
|
|
170
247
|
全局安装的 `mearl` CLI 默认在每天首次使用后启动独立后台进程,查询最新发布版本,并比较
|
|
171
248
|
client、native-host、cloud-server 和 MCP server 的已安装版本。发现更新时调用
|
|
@@ -190,9 +267,9 @@ client、native-host、cloud-server 和 MCP server 的已安装版本。发现
|
|
|
190
267
|
安装 skill 并自动注册命令,或直接注册已有入口:
|
|
191
268
|
|
|
192
269
|
```bash
|
|
193
|
-
npx
|
|
194
|
-
npx
|
|
195
|
-
npx
|
|
270
|
+
npx @mearl/setup plugin install https://github.com/F-loat/mearl --skill browser-tab-manager --yes
|
|
271
|
+
npx @mearl/setup plugin /absolute/path/to/browser-tab-manager
|
|
272
|
+
npx @mearl/setup plugin uninstall tabs
|
|
196
273
|
mearl tabs summary
|
|
197
274
|
mearl tabs --payload '{"command":"summary","input":{}}'
|
|
198
275
|
mearl tabs --help
|
|
@@ -234,14 +311,14 @@ const result = await invokePlugin(
|
|
|
234
311
|
|
|
235
312
|
MCP Server 通过固定工具 `plugin_invoke` 接受同样的 `plugin`、`command` 和 `input`,不会把插件子命令动态扩展成 MCP 工具。SDK 与 MCP 不接受任意入口路径;本地受管理目录中的插件未注册时,会通过匹配当前 client 版本的 setup 自动安装并重试一次。目录由安装宿主显式保存,核心没有组织专属名单;其他未注册名称返回 `PLUGIN_NOT_REGISTERED`。
|
|
236
313
|
|
|
237
|
-
长命令使用本机持久任务服务。服务按注册目录隔离并自动启动,CLI、SDK、MCP 与工具箱共享任务记录;调用方退出不取消执行。服务重启后将未完成任务标记为 `interrupted
|
|
314
|
+
长命令使用本机持久任务服务。服务按注册目录隔离并自动启动,CLI、SDK、MCP 与工具箱共享任务记录;调用方退出不取消执行。服务重启后将未完成任务标记为 `interrupted`,不自动重放。后台任务固定接受时的源码快照、工作目录与环境;第三方 JS 依赖应预先打包进 Skill,状态文件放在源码目录之外。快照只固定源码和相对资源,不预解析 JS 依赖;缺失依赖和语法错误由执行器加载代码时报告。
|
|
238
315
|
|
|
239
316
|
```ts
|
|
240
317
|
import { PluginTaskClient } from '@mearl/client';
|
|
241
318
|
|
|
242
319
|
const tasks = new PluginTaskClient();
|
|
243
320
|
try {
|
|
244
|
-
const accepted = await tasks.start('example', { command: '
|
|
321
|
+
const accepted = await tasks.start('example', { command: 'search', input: {} }, {
|
|
245
322
|
executionTimeoutMs: 120000,
|
|
246
323
|
idempotencyKey: 'document-123',
|
|
247
324
|
});
|
|
@@ -254,6 +331,8 @@ try {
|
|
|
254
331
|
|
|
255
332
|
`wait` 超时或其 signal 中止只结束等待;`cancel(taskId)` 才取消执行。浏览器任务需显式传 `browser`,名称会在接受前解析为固定 ID。`get/list` 返回任务快照与结果引用,`list` 返回有界分页及 `nextCursor`;`events` 返回游标、缺口标记和最新快照,`subscribe` 封装长轮询。`result(taskId)` 分块读取并校验完整业务结果,最大 64 MiB;终态记录和结果保留 7 天。
|
|
256
333
|
|
|
334
|
+
插件宿主握手与 `tasks.status()` 返回 `version/buildId`,构建标识用于定位运行代码,兼容性由握手中的协议版本决定。协议相同时,即使版本、构建标识不同或旧宿主未提供标识,任务、模型、页面和状态请求仍可继续使用现有服务,不中断活动任务或页面实例。需要切换到新构建时,确认没有活动任务及页面操作后调用 `tasks.stopHost()`,下次业务调用将启动当前构建;重新注册插件不会重启宿主。本地修改客户端或宿主代码后执行根目录 `pnpm build`,同步生成指纹并构建双方产物;仅运行 `tsc --watch` 不会更新构建指纹。
|
|
335
|
+
|
|
257
336
|
任务能力由 SDK `PluginTaskClient` 与工具箱 `host.task.*` 使用。对外命令由具体插件按业务需要提供,CLI 使用 `mearl <plugin> <command>`,MCP 使用 `plugin_invoke`。任务、模型、状态、页面实例和宿主诊断不单独暴露为全局 CLI/MCP 命令。仅注册工具箱内部命令的插件不会列入 CLI 插件列表;直接运行其名称会提示工具的打开位置。工具页可用 `window.mearl.waitTask(taskId)`、`readTaskResult(taskId)`、`listTasks()`,范围限定为所属插件的工具箱任务。
|
|
258
337
|
|
|
259
338
|
`tasks.diagnose({ taskId?, plugin?, instanceId?, includeErrorMessages?, includeLocalPaths? })` 读取当前调用范围内的有界诊断报告。默认不包含业务输入、提示词、进度正文、检查点数据、结果正文、完整 URL、环境变量和凭证,也省略错误消息及本地路径;后两项可显式选择包含。报告的 coverage 和历史 gap 表示采集范围及保留历史限制。
|
|
@@ -445,7 +524,7 @@ Apache-2.0
|
|
|
445
524
|
|
|
446
525
|
### 插件页面实例
|
|
447
526
|
|
|
448
|
-
`PluginPageClient` 通过本地插件服务管理 attach/get/list/send/detach/events/activation。插件导出独立 `pageRuntime` 声明并用 `app.page()`
|
|
527
|
+
`PluginPageClient` 通过本地插件服务管理 attach/get/list/send/detach/events/activation。插件导出独立 `pageRuntime` 声明并用 `app.page()` 调用。宿主用 Node 内置编译器检查 code 的表达式语法,不执行代码;代码必须在网页中求值得到自包含函数,非函数或外部引用错误在页面运行时报告。扩展与纯 CDP 共用实例、事件、激活和文档运行时;实例绑定 documentId,同插件同文档的所有入口共享初始化,导航后旧实例失效,启用的激活规则负责重建。`frame` 接受 `main`、`business` 或 `page_frames` 返回的 frame ID(扩展为数字,CDP 为字符串)。外部 Provider 仍须明确声明 `page_runtime` 能力。
|
|
449
528
|
|
|
450
529
|
显式手动 `attach` 复用自动激活的实例时,实例转为手动归属,初始化与在途任务继续复用;随后收窄自动激活范围不会清理该实例。
|
|
451
530
|
|
|
@@ -479,7 +558,7 @@ try {
|
|
|
479
558
|
} finally { models.disconnect(); }
|
|
480
559
|
```
|
|
481
560
|
|
|
482
|
-
`start` 返回持久 taskId;`run` 的 signal 取消模型执行并等待清理。`app.model(request, { onProgress })` / `context.model(request, { onProgress })` 可订阅当前这一次调用的 `model.delta`,并发调用的回调互相隔离;Python 使用 `context.model(request, on_progress=callback)`。增量 `data` 含 `delta/index/partial`,可用时带 `itemId/phase`;按消息隔离、跳过 commentary,序号缺失时回退到最终结果。增量只供临时展示,最终结果来自成功 turn
|
|
561
|
+
`start` 返回持久 taskId;`run` 的 signal 取消模型执行并等待清理。`app.model(request, { onProgress })` / `context.model(request, { onProgress })` 可订阅当前这一次调用的 `model.delta`,并发调用的回调互相隔离;Python 使用 `context.model(request, on_progress=callback)`。增量 `data` 含 `delta/index/partial`,可用时带 `itemId/phase`;按消息隔离、跳过 commentary,序号缺失时回退到最终结果。增量只供临时展示,最终结果来自成功 turn 的完整消息;outputSchema 透传模型 Provider,宿主解析 JSON,插件负责验证结果结构与业务约束;临时显示必须在失败时撤回,不能直接作为缓存或业务成功依据。模型目录与执行通过 `PluginModelClient` 或插件上下文使用。工具页使用 `host.model.capabilities/list/start` 和 `waitTask`。
|
|
483
562
|
|
|
484
563
|
插件入口声明状态集合,然后从命令上下文访问:
|
|
485
564
|
|
|
@@ -499,7 +578,7 @@ SDK `PluginStateClient.get/getMany/list/transact` 与工具箱 `host.state.*`
|
|
|
499
578
|
|
|
500
579
|
### 显式任务恢复
|
|
501
580
|
|
|
502
|
-
插件声明 `taskRecovery`(每个命令的 checkpointVersion/schema)并注册 `app.recoveryCommand(command, handler)`,命令通过 `context.checkpoint(data, expectedSequence)` 保存最多 64 KiB 的恢复依据。检查点数据、版本、宿主 schemaHash、序号和事件与任务记录原子写入;写入必须 CAS。
|
|
581
|
+
插件声明 `taskRecovery`(每个命令的 checkpointVersion/schema)并注册 `app.recoveryCommand(command, handler)`,命令通过 `context.checkpoint(data, expectedSequence)` 保存最多 64 KiB 的恢复依据。检查点数据、版本、宿主 schemaHash、序号和事件与任务记录原子写入;写入必须 CAS。schema 用于声明身份和格式兼容比较,检查点及迁移数据的结构由插件验证。
|
|
503
582
|
|
|
504
583
|
`tasks.checkpoint(taskId)` 读取检查点;`tasks.resume(taskId, { environment?, revision? })` 为终态根命令建立新 attempt,保持旧任务终态,固定原安装、源码快照、输入、目录和路由。默认提交调用进程的新环境,旧环境不持久化。恢复入口读取 `context.recovery`,先核对外部操作状态;宿主不重放原 handler,也不保证外部提交与检查点原子。
|
|
505
584
|
|
package/dist/check.d.ts
CHANGED
|
@@ -15,13 +15,12 @@ export interface ProviderVersionInfo {
|
|
|
15
15
|
version: string;
|
|
16
16
|
protocolVersion: number;
|
|
17
17
|
}
|
|
18
|
-
export
|
|
19
|
-
kind: 'sandbox-novnc';
|
|
20
|
-
environment: 'opensandbox' | 'aone';
|
|
21
|
-
port: number;
|
|
22
|
-
} | {
|
|
18
|
+
export interface BrowserAccessCapability {
|
|
23
19
|
kind: string;
|
|
24
|
-
|
|
20
|
+
label?: string;
|
|
21
|
+
environment?: string;
|
|
22
|
+
port?: number;
|
|
23
|
+
}
|
|
25
24
|
export type BrowserAccess = BrowserAccessCapability & {
|
|
26
25
|
url: string;
|
|
27
26
|
};
|
package/dist/check.js
CHANGED
|
@@ -46,10 +46,7 @@ function formatError(error) {
|
|
|
46
46
|
return error instanceof Error ? error.message : String(error);
|
|
47
47
|
}
|
|
48
48
|
function browserAccessLabel(access) {
|
|
49
|
-
|
|
50
|
-
return access.environment === 'opensandbox' ? 'OpenSandbox noVNC' : 'AONE Sandbox noVNC';
|
|
51
|
-
}
|
|
52
|
-
return access.kind;
|
|
49
|
+
return access.label || access.kind;
|
|
53
50
|
}
|
|
54
51
|
async function getBrowserAccess(client, browser, capability, timeoutSec) {
|
|
55
52
|
const result = (await client.invoke('get_browser_access_url', {}, { timeoutSec, browser: browser.browserId }));
|
|
@@ -414,10 +411,10 @@ export async function runUnifiedCheck(options, dependencies = {}) {
|
|
|
414
411
|
details: [
|
|
415
412
|
...available.map(update => `${update.component}: ${update.installed} → ${update.latest}`),
|
|
416
413
|
...(packageUpdateAvailable
|
|
417
|
-
? ['Update on the affected machine: npx
|
|
414
|
+
? ['Update on the affected machine: npx @mearl/setup update']
|
|
418
415
|
: []),
|
|
419
416
|
...(extensionUpdateAvailable
|
|
420
|
-
? ['Update a setup-managed extension: npx
|
|
417
|
+
? ['Update a setup-managed extension: npx @mearl/setup extension']
|
|
421
418
|
: []),
|
|
422
419
|
...(connectorUpdateAvailable
|
|
423
420
|
? ['Update cloud-connector with its package manager on the connector machine.']
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { resolveActionTimeoutSec } from './actionTimeouts.js';
|
|
|
6
6
|
import { runUnifiedCheck, parseCheckTimeout } from './check.js';
|
|
7
7
|
import { commandInput, findCommandSuggestions, formatCommandSuggestions, } from './commandSuggestions.js';
|
|
8
8
|
import { COMMANDS, COMMAND_MAP } from './commands.js';
|
|
9
|
+
import { formatCommandHelp } from './commandHelp.js';
|
|
9
10
|
import { CLIENT_VERSION } from './socket.js';
|
|
10
11
|
import { UnifiedClient } from './unified.js';
|
|
11
12
|
import { findPluginEntry, findPluginRegistration, findOfficialPlugin, listPluginNames, listOfficialPluginNames, loadRegisteredPlugin, PluginError, } from './plugins/sdk/plugin.js';
|
|
@@ -21,7 +22,7 @@ const CATEGORIES = [
|
|
|
21
22
|
title: '网络请求',
|
|
22
23
|
names: ['send_request', 'send_mtop_request', 'request_domain_permission'],
|
|
23
24
|
},
|
|
24
|
-
{ title: '标签页', names: ['tab_open', 'tab_list', 'tab_group', 'tab_close'] },
|
|
25
|
+
{ title: '标签页', names: ['tab_open', 'tab_list', 'tab_group', 'tab_close', 'get_capabilities'] },
|
|
25
26
|
{
|
|
26
27
|
title: '页面感知',
|
|
27
28
|
names: ['page_snapshot', 'page_inspect', 'page_screenshot', 'page_frames'],
|
|
@@ -59,6 +60,11 @@ const CATEGORIES = [
|
|
|
59
60
|
},
|
|
60
61
|
];
|
|
61
62
|
const BUILTIN_COMMANDS = [
|
|
63
|
+
{
|
|
64
|
+
name: 'run',
|
|
65
|
+
description: '执行预注入 mearl SDK 的 Node.js 脚本',
|
|
66
|
+
examples: ['run --file task.mjs'],
|
|
67
|
+
},
|
|
62
68
|
{
|
|
63
69
|
name: 'check',
|
|
64
70
|
description: '检查指定浏览器,未指定时检查全部已连接浏览器',
|
|
@@ -120,6 +126,7 @@ function printUsage(commandName, version) {
|
|
|
120
126
|
` ${commandName} <action> [选项]`,
|
|
121
127
|
` ${commandName} <action> --help`,
|
|
122
128
|
` ${commandName} check [选项]`,
|
|
129
|
+
` ${commandName} run [--file <path>] [选项]`,
|
|
123
130
|
'',
|
|
124
131
|
'常用选项:',
|
|
125
132
|
' --payload <json> 直接传入 JSON 参数',
|
|
@@ -135,6 +142,7 @@ function printUsage(commandName, version) {
|
|
|
135
142
|
' -v, --version 查看版本',
|
|
136
143
|
'',
|
|
137
144
|
'内置命令:',
|
|
145
|
+
` ${'run'.padEnd(TOP_LEVEL_COMMAND_NAME_WIDTH)}执行 Node.js 脚本,支持浏览器 API 与 Skill 命令`,
|
|
138
146
|
` ${'check'.padEnd(TOP_LEVEL_COMMAND_NAME_WIDTH)}检查指定浏览器,未指定时检查全部已连接浏览器`,
|
|
139
147
|
];
|
|
140
148
|
for (const category of CATEGORIES) {
|
|
@@ -229,7 +237,7 @@ function printProviderHelp(commandName, provider, extensionHelp) {
|
|
|
229
237
|
lines.push(` ${mode}(必填:${requiredFields})`);
|
|
230
238
|
});
|
|
231
239
|
}
|
|
232
|
-
lines.push('', '发现与检查:', ` ${commandName} browser_list`, ` ${commandName} check --browser <browserId>`, '', '管理:', ` npx
|
|
240
|
+
lines.push('', '发现与检查:', ` ${commandName} browser_list`, ` ${commandName} check --browser <browserId>`, '', '管理:', ` npx @mearl/setup provider update ${provider.providerId}`, ` npx @mearl/setup provider uninstall ${provider.providerId}`);
|
|
233
241
|
if (browserType.configuration) {
|
|
234
242
|
lines.push('', 'Provider 配置(在 Mearl 浏览器管理器中填写):');
|
|
235
243
|
const configurationRequired = new Set(browserType.configuration.inputSchema.required ?? []);
|
|
@@ -300,46 +308,6 @@ function printAdvancedCommandHelp(commandName, action) {
|
|
|
300
308
|
'结果包含 count 与 connectors;每项包含 ID、名称、可选版本、连接时间和最后心跳。',
|
|
301
309
|
].join('\n'));
|
|
302
310
|
}
|
|
303
|
-
function printCommandHelp(commandName, command) {
|
|
304
|
-
const payloadUsage = command.params?.length ? ' [--payload <json> | --payload-file <path>]' : '';
|
|
305
|
-
const lines = [
|
|
306
|
-
`${command.name}`,
|
|
307
|
-
` ${command.description}`,
|
|
308
|
-
'',
|
|
309
|
-
'用法:',
|
|
310
|
-
` ${commandName} ${command.name}${payloadUsage} [常用选项]`,
|
|
311
|
-
'',
|
|
312
|
-
];
|
|
313
|
-
if (command.params?.length) {
|
|
314
|
-
lines.push('参数(--payload JSON 字段):');
|
|
315
|
-
for (const parameter of command.params) {
|
|
316
|
-
const required = parameter.required ? '必填' : '可选';
|
|
317
|
-
lines.push(` ${parameter.name.padEnd(22)}${required.padEnd(6)}${parameter.type.padEnd(28)} ${parameter.description}`);
|
|
318
|
-
for (const detail of parameter.details ?? [])
|
|
319
|
-
lines.push(` ${detail}`);
|
|
320
|
-
}
|
|
321
|
-
lines.push('');
|
|
322
|
-
}
|
|
323
|
-
if (command.name === 'browser_list') {
|
|
324
|
-
lines.push('目标范围(按需):', ' --local 只列出当前机器上的浏览器', ' --connector <id|名称> 只列出指定远端 connector 的浏览器', '');
|
|
325
|
-
}
|
|
326
|
-
else if (command.name === 'browser_launch') {
|
|
327
|
-
lines.push('宿主选择(本机与远端同时可用时):', ' --local 在当前机器启动', ' --connector <id|名称> 在指定远端 connector 启动', ' --browser <id|名称> 在控制浏览器所在宿主启动,并可作为 Cookie/UA 来源', '');
|
|
328
|
-
}
|
|
329
|
-
if (command.notes?.length) {
|
|
330
|
-
lines.push('约束与行为:', ...command.notes.map(note => ` ${note}`), '');
|
|
331
|
-
}
|
|
332
|
-
if (command.result?.length) {
|
|
333
|
-
lines.push('结果:', ...command.result.map(line => ` ${line}`), '');
|
|
334
|
-
}
|
|
335
|
-
if (command.examples?.length) {
|
|
336
|
-
lines.push('示例:');
|
|
337
|
-
for (const example of command.examples)
|
|
338
|
-
lines.push(` ${commandName} ${example}`);
|
|
339
|
-
}
|
|
340
|
-
lines.push('', `运行 ${commandName} --help 查看常用选项。`);
|
|
341
|
-
console.error(lines.join('\n'));
|
|
342
|
-
}
|
|
343
311
|
function printUnknownCommand(commandName, args) {
|
|
344
312
|
const input = commandInput(args) || args[0] || '';
|
|
345
313
|
const suggestions = findCommandSuggestions(args, SUGGESTABLE_COMMANDS);
|
|
@@ -605,6 +573,10 @@ function writeOutput(action, result, outputPath) {
|
|
|
605
573
|
export async function runMearlCli(args, options = {}) {
|
|
606
574
|
const commandName = options.commandName ?? 'mearl';
|
|
607
575
|
const clientVersion = options.clientVersion ?? CLIENT_VERSION;
|
|
576
|
+
if (args[0] === 'run') {
|
|
577
|
+
const { runScriptCli } = await import('./scriptCli.js');
|
|
578
|
+
return runScriptCli(args.slice(1), commandName);
|
|
579
|
+
}
|
|
608
580
|
if (args[0] === 'capture_checkpoint')
|
|
609
581
|
args = ['tab_checkpoint', ...args.slice(1)];
|
|
610
582
|
const provider = installedProvider(args[0]);
|
|
@@ -641,7 +613,7 @@ export async function runMearlCli(args, options = {}) {
|
|
|
641
613
|
if (args.includes('--help') || args.includes('-h')) {
|
|
642
614
|
const command = COMMAND_MAP.get(args[0]);
|
|
643
615
|
if (command)
|
|
644
|
-
|
|
616
|
+
console.error(formatCommandHelp(commandName, command));
|
|
645
617
|
else if (args[0] === 'check')
|
|
646
618
|
printCheckHelp(commandName);
|
|
647
619
|
else if (ADVANCED_BUILTIN_COMMANDS.has(args[0])) {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { COMMANDS, COMMAND_MAP } from './commands.js';
|
|
2
|
+
import { SDK_METHOD_ACTIONS, SDK_OPTIONS_ONLY_METHODS } from './sdkMethods.js';
|
|
3
|
+
import { listPluginNames } from './plugins/pluginRegistry.js';
|
|
4
|
+
const HELPER_HELP = {
|
|
5
|
+
invoke: 'invoke(action, payload?, options?) — 调用浏览器 action,返回普通 action 的原始结果',
|
|
6
|
+
invokePlugin: 'invokePlugin(name, { command, input }, options?) — 调用已注册 Skill 命令,返回该命令的原始结果',
|
|
7
|
+
help: 'help(methodOrAction?) / help({ plugin, command? }) — 返回共享命令帮助文本;无参数时列出方法;用 console.log 输出',
|
|
8
|
+
structure: 'structure(value, { maxDepth=5, maxKeys=50, arraySamples=1, maxNodes=2000 }?) — 同步返回有界结构样本;叶子值替换为类型,标注数组样本、循环引用和截断;不能证明未采样内容或业务语义',
|
|
9
|
+
saveArtifact: 'saveArtifact(artifact, absolutePath, options?) — 保存浏览器文件产物,返回保存结果',
|
|
10
|
+
selectBrowser: 'selectBrowser({ browser? }, options?) — 选择默认目标;返回 { selected };browser 使用 browserList 返回的 ID 或名称,空字符串清除默认目标',
|
|
11
|
+
};
|
|
12
|
+
export function getCommandHelp(target) {
|
|
13
|
+
if (!target)
|
|
14
|
+
return [
|
|
15
|
+
...COMMANDS.map((command) => `${[...SDK_METHOD_ACTIONS].find(([, action]) => action === command.name)?.[0] ?? command.name} (${command.name}) — ${command.summary ?? command.description}`),
|
|
16
|
+
...Object.values(HELPER_HELP),
|
|
17
|
+
...listPluginNames().map(plugin => `Skill ${plugin} — help({ plugin: '${plugin}' })`),
|
|
18
|
+
].join('\n');
|
|
19
|
+
if (Object.hasOwn(HELPER_HELP, target))
|
|
20
|
+
return HELPER_HELP[target];
|
|
21
|
+
const resolved = SDK_METHOD_ACTIONS.get(target) ?? target;
|
|
22
|
+
const action = resolved === 'capture_checkpoint' ? 'tab_checkpoint' : resolved;
|
|
23
|
+
if (action === 'browser_select_browser')
|
|
24
|
+
return HELPER_HELP.selectBrowser;
|
|
25
|
+
const command = COMMAND_MAP.get(action);
|
|
26
|
+
if (!command)
|
|
27
|
+
throw new Error(`Unknown command or SDK method: ${target}`);
|
|
28
|
+
return formatCommandHelp('mearl', command);
|
|
29
|
+
}
|
|
30
|
+
export function formatCommandHelp(commandName, command) {
|
|
31
|
+
const payloadUsage = command.params?.length ? ' [--payload <json> | --payload-file <path>]' : '';
|
|
32
|
+
const lines = [
|
|
33
|
+
`${command.name}`,
|
|
34
|
+
` ${command.description}`,
|
|
35
|
+
'',
|
|
36
|
+
'用法:',
|
|
37
|
+
` ${commandName} ${command.name}${payloadUsage} [常用选项]`,
|
|
38
|
+
'',
|
|
39
|
+
];
|
|
40
|
+
const method = [...SDK_METHOD_ACTIONS].find(([, action]) => action === command.name)?.[0];
|
|
41
|
+
if (method)
|
|
42
|
+
lines.push('SDK:', ...(method === 'browserRelease'
|
|
43
|
+
? [
|
|
44
|
+
' await mearl.browserRelease(options?)',
|
|
45
|
+
' await mearl.browserRelease(params, options)',
|
|
46
|
+
]
|
|
47
|
+
: [
|
|
48
|
+
` await mearl.${method}(${SDK_OPTIONS_ONLY_METHODS.has(method) ? 'options?' : 'params, options?'})`,
|
|
49
|
+
]), '');
|
|
50
|
+
if (command.params?.length) {
|
|
51
|
+
lines.push('参数(--payload JSON 字段):');
|
|
52
|
+
for (const parameter of command.params) {
|
|
53
|
+
const required = parameter.required ? '必填' : '可选';
|
|
54
|
+
lines.push(` ${parameter.name.padEnd(22)}${required.padEnd(6)}${parameter.type.padEnd(28)} ${parameter.description}`);
|
|
55
|
+
for (const detail of parameter.details ?? [])
|
|
56
|
+
lines.push(` ${detail}`);
|
|
57
|
+
}
|
|
58
|
+
lines.push('');
|
|
59
|
+
}
|
|
60
|
+
if (command.name === 'browser_list') {
|
|
61
|
+
lines.push('目标范围(按需):', ' --local 只列出当前机器上的浏览器', ' --connector <id|名称> 只列出指定远端 connector 的浏览器', '');
|
|
62
|
+
}
|
|
63
|
+
else if (command.name === 'browser_launch') {
|
|
64
|
+
lines.push('宿主选择(本机与远端同时可用时):', ' --local 在当前机器启动', ' --connector <id|名称> 在指定远端 connector 启动', ' --browser <id|名称> 在控制浏览器所在宿主启动,并可作为 Cookie/UA 来源', '');
|
|
65
|
+
}
|
|
66
|
+
if (command.notes?.length) {
|
|
67
|
+
lines.push('约束与行为:', ...command.notes.map(note => ` ${note}`), '');
|
|
68
|
+
}
|
|
69
|
+
if (command.result?.length) {
|
|
70
|
+
lines.push('结果:', ...command.result.map(line => ` ${line}`), '');
|
|
71
|
+
}
|
|
72
|
+
if (command.examples?.length) {
|
|
73
|
+
lines.push('示例:');
|
|
74
|
+
for (const example of command.examples)
|
|
75
|
+
lines.push(` ${commandName} ${example}`);
|
|
76
|
+
}
|
|
77
|
+
lines.push('', `运行 ${commandName} --help 查看常用选项。`);
|
|
78
|
+
return lines.join('\n');
|
|
79
|
+
}
|