@mearl/setup 3.0.0-beta.0 → 3.0.0-beta.10
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 +174 -156
- package/README.zh-CN.md +260 -0
- package/dist/browserExtension.js +24 -8
- package/dist/browserPreset.d.ts +20 -0
- package/dist/browserPreset.js +82 -0
- package/dist/cli.d.ts +4 -3
- package/dist/cli.js +43 -12
- package/dist/lifecycle.d.ts +10 -2
- package/dist/lifecycle.js +70 -13
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +52 -14
- package/dist/preset.d.ts +16 -5
- package/dist/preset.js +51 -14
- package/dist/presetStore.d.ts +2 -48
- package/dist/presetStore.js +1 -252
- package/dist/setupPreset.d.ts +9 -0
- package/dist/setupPreset.js +14 -0
- package/dist/setupVersion.js +1 -1
- package/dist/skillSourceReceipts.d.ts +25 -0
- package/dist/skillSourceReceipts.js +94 -0
- package/dist/skillSources.d.ts +10 -22
- package/dist/skillSources.js +73 -97
- package/dist/skills.js +8 -6
- package/package.json +20 -10
- package/dist/presetUpgrade.d.ts +0 -13
- package/dist/presetUpgrade.js +0 -42
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# @mearl/setup
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [简体中文](README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
3.0 当前以 beta 发行。使用 `@beta` 入口安装同版本的 setup、运行包和扩展;正式版使用 `latest` 标签。
|
|
6
|
+
|
|
7
|
+
Mearl 环境的一次性安装、更新、卸载与配置工具。无需先全局安装 setup:
|
|
8
|
+
|
|
9
|
+
要求 Node.js >= 20.18.0。安装前检查 `node --version`;3.0 beta 继续显式使用 `@beta`,
|
|
10
|
+
避免 npm 在未指定版本时按旧 Node 的兼容性选到历史安装器。
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx @mearl/setup@beta [install] [--local] [--cloud] [--cloud-mode <id>] [--mcp]
|
|
14
|
+
npx @mearl/setup@beta update [--local] [--cloud] [--cloud-mode <id>] [--mcp]
|
|
15
|
+
npx @mearl/setup@beta uninstall [--local] [--cloud] [--cloud-mode <id>] [--mcp] [--full]
|
|
16
|
+
npx @mearl/setup@beta extension [--full] [--force]
|
|
17
|
+
npx @mearl/setup@beta plugin [name] <path>
|
|
18
|
+
npx @mearl/setup@beta plugin install <skills add 参数...>
|
|
19
|
+
npx @mearl/setup@beta plugin uninstall <name>
|
|
20
|
+
npx @mearl/setup@beta provider <install|update|uninstall> <package|id>
|
|
21
|
+
npx @mearl/setup@beta preset repair [--replace-skill <name> ...]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
浏览器 Provider 独立安装和演进。安装后运行 `mearl <providerId>` 查看该版本声明的能力、
|
|
25
|
+
配置和启动参数。
|
|
26
|
+
|
|
27
|
+
可选云协议先通过 preset 安装对应能力,再用 `--cloud-mode <id>` 选择。Qoder 配对见 [`@mearl/plugin-qoder`](../plugin-qoder/README.zh-CN.md);setup 不默认包含或安装它。
|
|
28
|
+
|
|
29
|
+
## 安装与注册 Skill 插件
|
|
30
|
+
|
|
31
|
+
一个命令安装 skill 并注册它的 CLI、SDK 和 MCP 能力:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx @mearl/setup@beta plugin install https://github.com/mearlhq/mearl --skill browser-tab-manager --yes
|
|
35
|
+
mearl tabs summary
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
安装参数传给固定版本的公共 Skills CLI(当前 skills@1.5.0,兼容项目的 Node 最低版本),使用全局共享目录。公共 CLI 的默认 symlink 和复制模式都可注册;复制模式通过 list --global --json 确认本次成功结果对应的 canonical path,不猜测目录,也不扫描其他 Skills。自定义 CLI 可通过 MEARL_SKILLS_PATH 指定。参数不接受截断后置选项的 -- 分隔符。
|
|
39
|
+
|
|
40
|
+
安装宿主提供的本地插件目录决定哪些能力由 preset 管理。install/update 维护目录中的托管注册,保留用户自行注册的插件。卸载最后一个依赖环境时移除托管插件与 Mearl Skill;其他环境需要时保留。配置准备、宿主同步和实际安装状态共用 [安装合同](../../docs/preset-installation.zh-CN.md)。
|
|
41
|
+
|
|
42
|
+
Native preset 批次新建的插件注册使用 setup 托管归属,后续通过 Agent 更新仍保留该归属;同目录已有的用户注册保持原归属,自定义用户别名也会保留。托管卸载遇到用户别名共用同一 Skill 时,只清除托管注册并保留 Skill 文件。
|
|
43
|
+
|
|
44
|
+
GUI 和 CLI 使用同一套 preset 安装器及覆盖规则。已保存的安装尚未完成时,运行 `preset repair` 从接受的本地资源重试,不重新下载 preset,也不隐式覆盖冲突来源。显式重复指定 `--replace-skill` 即授权本次替换这些 Skill 的来源或同名旧插件注册:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx @mearl/setup@beta preset repair --replace-skill example-skill --replace-skill another-skill
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
已安装的 Skill 仅修复注册;来源冲突项重新安装。未列出的冲突仍会阻止安装,声明外的工具名与变更后的安装身份也会被拒绝。覆盖授权不会保留到后续普通修复或自动更新。缺少浏览器权限时仍需在扩展中授权。输出沿用 preset JSON,已就绪退出 `0`、仍有待完成项退出 `3`、安装失败退出 `1`,无效参数退出 `2`。
|
|
51
|
+
|
|
52
|
+
自动注册默认使用安装目录名;入口可声明 `name` 指定简短命令名。
|
|
53
|
+
重装会刷新同名绑定。JavaScript 入口必须默认导出 `register(app)`,Python 入口必须导出 `register(app)` 函数;完整协议见
|
|
54
|
+
[插件运行时合同](../../docs/plugin-runtime.zh-CN.md)。
|
|
55
|
+
|
|
56
|
+
已通过其他渠道分发的 skill 或自定义入口,可以直接传入路径注册:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx @mearl/setup@beta plugin /absolute/path/to/browser-tab-manager
|
|
60
|
+
npx @mearl/setup@beta plugin custom ./skills/custom/entry.mjs
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
path 接受目录或具体文件;目录默认读取 `scripts/main.mjs` 或 `scripts/main.py`,相对路径基于调用工作目录解析。插件名优先使用显式 name,其次读取入口的 `name` metadata,最后回退到 Skill 目录名;常规注册只传 path,显式 name 仅用于覆盖或兼容旧入口。Python 插件要求 `python3 >= 3.9`,可通过 `MEARL_PYTHON` 指定解释器路径;setup 只验证入口可加载,不安装 Python,也不执行 `pip install`。
|
|
64
|
+
命令检查入口注册函数,然后保存绝对入口路径,同名注册更新绑定。
|
|
65
|
+
两种注册流程都会检查本机 `@mearl/client` 和 `@mearl/native-host`:缺失或低于当前 setup 版本时,
|
|
66
|
+
复用已有包管理器安装 setup 的对应版本,更新后再次验证;更高版本保持不动。
|
|
67
|
+
native-host 会幂等初始化 Skills 模式。基础环境补齐或初始化失败时保留已安装的 skill,并停止注册。
|
|
68
|
+
JavaScript 插件可导出 `setup({ runCommand })`,在注册前准备 Skill 自己的依赖。两种安装入口都会等待执行完成,失败时停止注册。首次使用的依赖检查由 Skill 自行处理,见 [client 契约](../client/README.zh-CN.md#skill-插件)。
|
|
69
|
+
|
|
70
|
+
注册后可在任意目录运行 `mearl <name> --help`,也可通过 `@mearl/client` 的 `invokePlugin` 或 MCP 的 `plugin_invoke` 工具调用。日常调用不更新 Skill 源码;已选目录中的缺失插件会触发一次按需安装。Skill 自身的依赖由安装钩子和首次使用检查准备。
|
|
71
|
+
|
|
72
|
+
不再需要插件时执行:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx @mearl/setup@beta plugin uninstall tabs
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
若入口位于 `~/.agents/skills/<skill>/`,setup 会先执行
|
|
79
|
+
`skills remove <skill> --global --yes`,成功后清除该 Skill 对应的全部插件注册;
|
|
80
|
+
入口目录已被手动删除时会让 Skills 仍能识别本次移除,从而一并清理其跟踪记录。
|
|
81
|
+
移除失败时保留注册以便重试。手工注册的其他路径只解除 Mearl 注册,不删除源码。
|
|
82
|
+
重复卸载未注册名称会成功返回,不产生改动。
|
|
83
|
+
|
|
84
|
+
## 管理浏览器 Provider
|
|
85
|
+
|
|
86
|
+
Provider 由 npm 包分发,但包本身无需暴露 CLI 或安装脚本。setup 使用当前 npm 包管理器
|
|
87
|
+
完成全局包生命周期,再读取包内的 `package.json#mearl.provider` 元数据登记协议入口、
|
|
88
|
+
可创建的浏览器类型名称及其表单参数:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx @mearl/setup@beta provider install @example/mearl-provider-device
|
|
92
|
+
npx @mearl/setup@beta provider update example-device
|
|
93
|
+
npx @mearl/setup@beta provider uninstall example-device
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
安装参数接受包名、版本或 dist-tag;更新和卸载可使用 providerId 或完整包名。核心 @mearl 包固定使用公共 npm registry;其余 scope 沿用调用方的包管理器配置。安装沿用启动 setup 的包管理器,
|
|
97
|
+
更新和卸载沿用当前 Provider 的原包管理器。Provider 登记只保存
|
|
98
|
+
经过校验的包名、安装目录和包内入口;不接受包自行注入启动命令、参数、工作目录或环境变量。
|
|
99
|
+
卸载仅在包管理器成功移除包后清除登记,以便失败时可以重试。
|
|
100
|
+
|
|
101
|
+
## 行为
|
|
102
|
+
|
|
103
|
+
- Mearl 自有 npm 包的安装和更新固定使用 `https://registry.npmjs.org/`,与最新版本查询使用同一来源;第三方包沿用当前包管理器的源配置。
|
|
104
|
+
- `install` 可以省略;直接执行 `npx @mearl/setup@beta` 等同于 `npx @mearl/setup@beta install`。
|
|
105
|
+
以选项开头时也默认安装,例如 `npx @mearl/setup@beta --cloud --yes`。
|
|
106
|
+
- `install` 未指定环境时默认 `--local`。
|
|
107
|
+
- `-h` / `--help` 显示帮助,`-v` / `--version` 显示 setup 版本。
|
|
108
|
+
- `update` 和 `uninstall` 未指定环境时,从当前 `PATH` 中的
|
|
109
|
+
`mearl-native-host`、`mearl-cloud-server`、`mearl-mcp-server` 自动检测,可同时处理
|
|
110
|
+
多个已安装环境。
|
|
111
|
+
- `--local`、`--cloud` 与 `--mcp` 可以组合,显式传入时只处理选中的环境;
|
|
112
|
+
`--cloud-mode <id>` 同时选择 cloud 环境,模式实现必须来自已安装能力。
|
|
113
|
+
- 已有环境沿用其原包管理器和全局安装前缀;首次安装沿用启动 setup 的包管理器。
|
|
114
|
+
- `uninstall` 也识别 `npm link` 或 `dev:link` 注册到 npm 全局目录的开发软链,
|
|
115
|
+
通过原 npm 前缀移除包与命令链接,保留仓库源码。安装和更新的自动检测仅处理常规全局安装。
|
|
116
|
+
- cloud 环境统一管理 `@mearl/cloud-server` 与 `@mearl/client`:WebSocket 模式首次安装后
|
|
117
|
+
启动 Server,更新后仅在 Server 原本运行时重启;其他 mode 由安装的 `cloud.server.<id>` 处理;卸载前均调用 `stop`。
|
|
118
|
+
- local/cloud 共用一份 `@mearl/client`;组合安装只处理一次,卸载单个环境时若另一个环境仍在使用则保留。
|
|
119
|
+
- 从 2.5.x 更新 cloud 环境时会先卸载旧 `@mearl/cloud-client`,避免它遗留的 `mearl` bin 与统一 client 冲突。
|
|
120
|
+
- local/cloud 共用的全局 mearl skill 通过 `skills` 的 JSON 清单定位,只处理一次。
|
|
121
|
+
- 安装和更新通过 `skills add` 从公开仓库重新安装全局 mearl skill,并关联当前检测到的 Agent;更新须确认本次安装完成并核对全局 JSON 清单,不能仅依据第三方更新命令的退出码。
|
|
122
|
+
可用 `--skip-skill` 跳过。
|
|
123
|
+
- 卸载一个环境但另一个仍依赖全局 mearl skill 时会保留 skill。
|
|
124
|
+
- 已选择 preset 的自动维护保留用户注册;托管卸载遇到同目录的用户别名时只移除托管注册并保留共享 Skill。
|
|
125
|
+
- `uninstall --full` 在环境卸载之外,还会逐项卸载所有已注册 CLI 插件与浏览器 Provider。
|
|
126
|
+
Skills 安装的插件会同时移除 Skill;手工注册的外部插件只清除注册、保留源码;Provider
|
|
127
|
+
只有在包管理器成功卸载 npm 包后才清除注册。单项失败不阻止其他项继续清理,命令最终返回非零。
|
|
128
|
+
该选项不删除 Chrome 扩展;`extension --full` 仍表示安装扩展及完整本地环境。
|
|
129
|
+
- `uninstall --full` 在所有环境、插件与 Provider 成功移除后统一收尾:停止插件执行服务,
|
|
130
|
+
确认没有活跃或退出状态未明的任务,再删除任务历史、源码快照、服务日志和已结束宿主的页面会话文件。只卸载部分环境、单项失败或服务重新启动时保留共享缓存。
|
|
131
|
+
即使包和注册已移除,也可以再次运行该命令完成缓存收尾。自定义 `MEARL_PLUGIN_DIR` 只清理该注册表的执行缓存。
|
|
132
|
+
记事本、迭代记录、配置与身份、插件持久状态、Provider 私有数据、浏览器配置及用户导出的设计稿均保留;
|
|
133
|
+
CDP 标签 ID 计数也保留,防止旧请求命中新标签。
|
|
134
|
+
- `--mcp` 的客户端配置和清理由 `@mearl/mcp-server` 自己的命令完成。
|
|
135
|
+
- 默认的 `install` / `update` 不处理浏览器扩展。`extension` 是独立命令:从
|
|
136
|
+
`https://github.com/mearlhq/mearl/releases/download/v<version>/` 读取同版本 beta 扩展发行元数据(正式 setup 使用 `releases/latest/download/`)并下载对应的 `mearl-chrome-v*.zip`,原子解压到
|
|
137
|
+
`~/.mearl/chrome-extension`。首次安装通过浏览器级 CDP 的 `Extensions.loadUnpacked`
|
|
138
|
+
加载;后续更新由 `mearl check --local --json` 确认当前扩展仍来自 setup 管理目录后,直接替换
|
|
139
|
+
文件并通过 Native Host 触发扩展 reload。固定目录和 manifest 中的 `key` 保证扩展 ID
|
|
140
|
+
与浏览器存储保持稳定。
|
|
141
|
+
- `extension --full` 会先安装或更新扩展,再复用 local profile 安装
|
|
142
|
+
`@mearl/client`、安装并初始化 `@mearl/native-host`,最后安装全局 Mearl Skill。
|
|
143
|
+
两部分都会尽量执行;任一步失败时命令返回非零退出码,已安装的部分会保留。它不安装
|
|
144
|
+
cloud 或 MCP 环境。
|
|
145
|
+
- 下载前会通过 CDP 检查同 ID 插件的来源。未安装,或来源为固定目录中的 `UNPACKED`
|
|
146
|
+
插件时可继续;Chrome 商店、企业策略或其他本地目录安装的版本默认保持不变。只有显式
|
|
147
|
+
传入 `--force` 才会接管并替换这些版本。
|
|
148
|
+
|
|
149
|
+
## 安装或更新浏览器扩展
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npx @mearl/setup@beta extension
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
首次使用时可一次安装完整的本地浏览器环境:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npx @mearl/setup@beta extension --full
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
首次安装、旧版 setup 扩展首次迁移,或 Native Host reload 不可用时才需要 CDP。如果
|
|
162
|
+
Chrome 尚未开放 CDP,交互命令会提示打开
|
|
163
|
+
`chrome://inspect/#remote-debugging`、启用远程调试并允许连接,然后等待安装完成。面向人的
|
|
164
|
+
安装指引不要添加 `--yes`。无 TTY 环境使用 `npx @mearl/setup@beta extension --yes`;它只尝试
|
|
165
|
+
一次,CDP 不可用时立即中断。无法确认 setup 管理来源且 CDP 不可用时不会下载或修改插件
|
|
166
|
+
目录,打开 CDP 后重新执行同一命令即可。Chrome 版本过低、缺少扩展安装所需的 CDP 方法时,
|
|
167
|
+
命令会中断并提示升级 Chrome。
|
|
168
|
+
|
|
169
|
+
该命令每次都读取下载页上的最新插件版本,不要求与 setup、CLI 或 Native Host 同版本。
|
|
170
|
+
CDP 来源检查和 `Extensions.loadUnpacked` 复用同一条连接,避免一次安装重复请求授权。
|
|
171
|
+
安装成功后目录内会保存 setup 管理标记;`mearl check --local --json` 同时从当前运行扩展读取该
|
|
172
|
+
标记,只有两侧路径一致时才允许无 CDP 更新。如果用户后来改装 Chrome 商店版本,运行中
|
|
173
|
+
的扩展不会返回 setup 标记,命令会重新通过 CDP 检查来源,并在未传 `--force` 时保持商店
|
|
174
|
+
版本不变。
|
|
175
|
+
|
|
176
|
+
setup 管理的扩展会读取远端 `config.json` 检查新版本,但不会静默更新。发现更高版本后,
|
|
177
|
+
DevTools 面板的设置按钮会显示红点,快捷设置中提供“立即更新”和对应版本的“更新日志”。
|
|
178
|
+
只有用户点击“立即更新”后,Native Host 才会重新校验远端版本、setup 管理标记、下载地址、
|
|
179
|
+
可选的 SHA256 和压缩包 manifest,再原子替换目录并 reload。该入口不会回退到 CDP;商店版、
|
|
180
|
+
企业策略版和其他本地目录版本不会进入 setup 更新链路。对于 Chrome 自身管理并已下载完成的
|
|
181
|
+
更新,扩展只被动监听 `runtime.onUpdateAvailable`,沿用相同的更新提示并由用户点击 reload;不会
|
|
182
|
+
调用 `runtime.requestUpdateCheck()` 主动检查。
|
|
183
|
+
|
|
184
|
+
## 跟随浏览器扩展自动更新
|
|
185
|
+
|
|
186
|
+
浏览器扩展与 Native Host 首次建联时会交换版本。每次扩展后台启动只检查一次;当扩展
|
|
187
|
+
进入更高的 `major.minor` release line 时,旧 Host 会在保持当前连接可用的前提下,调用
|
|
188
|
+
对应 `major.minor.x` 范围内最新的 `@mearl/setup`,把 `@mearl/native-host` 与
|
|
189
|
+
`@mearl/client` 一起更新到该范围内的最新版本。成功后 Host 退出,扩展通过已有重连机制
|
|
190
|
+
启动新版本。只有两个本地 CLI 版本一致且都属于扩展的 release line 时,更新才会被视为成功。
|
|
191
|
+
|
|
192
|
+
自动更新不会降级较新的 Host,也不会改动 cloud、MCP 或独立安装的 Mearl Skill。
|
|
193
|
+
目标 npm 版本尚未发布、网络不可用或更新失败时,旧 Host 会继续运行;下一次扩展后台
|
|
194
|
+
生命周期可重新尝试,也可手动执行 `npx @mearl/setup@beta update --local`。
|
|
195
|
+
|
|
196
|
+
## 人工与 Agent 使用
|
|
197
|
+
|
|
198
|
+
- 人工在终端执行时不加 `--yes`,需要选择 Skill 安装目标或 MCP 客户端时会显示交互提示。
|
|
199
|
+
- Agent、CI 或其他无 TTY 环境应加 `--yes`(也可写
|
|
200
|
+
`--non-interactive`)。首次安装会选择已检测到的 Agent / MCP 客户端,卸载会清理
|
|
201
|
+
所有已检测到的相关配置。
|
|
202
|
+
- 无 TTY 环境执行 `install` 或 `uninstall` 却未加上述参数时,会在任何改动前退出并提示正确命令。
|
|
203
|
+
- 退出码 `0` 表示完整成功,`1` 表示执行未完成,`2` 表示参数或交互模式不适用。
|
|
204
|
+
|
|
205
|
+
## 示例
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# 默认安装本地浏览器环境
|
|
209
|
+
npx @mearl/setup
|
|
210
|
+
|
|
211
|
+
# 与上一条等价
|
|
212
|
+
npx @mearl/setup@beta install
|
|
213
|
+
|
|
214
|
+
# Agent / CI 无交互安装
|
|
215
|
+
npx @mearl/setup@beta --yes
|
|
216
|
+
|
|
217
|
+
# 安装云端 Agent 环境
|
|
218
|
+
npx @mearl/setup@beta --cloud
|
|
219
|
+
|
|
220
|
+
# 同一台机器同时作为本地浏览器宿主和云端 Agent
|
|
221
|
+
npx @mearl/setup@beta --local --cloud
|
|
222
|
+
|
|
223
|
+
# 自动更新当前 PATH 中检测到的全部 Mearl 环境
|
|
224
|
+
npx @mearl/setup@beta update
|
|
225
|
+
|
|
226
|
+
# 独立安装或更新到最新浏览器扩展
|
|
227
|
+
npx @mearl/setup@beta extension
|
|
228
|
+
|
|
229
|
+
# 安装完整本地环境(client、Native Host、Skill 和扩展)
|
|
230
|
+
npx @mearl/setup@beta extension --full
|
|
231
|
+
|
|
232
|
+
# 只卸载本地环境
|
|
233
|
+
npx @mearl/setup@beta uninstall --local
|
|
234
|
+
|
|
235
|
+
# 自动检测并无交互卸载
|
|
236
|
+
npx @mearl/setup@beta uninstall --yes
|
|
237
|
+
|
|
238
|
+
# 卸载环境,并清理全部 CLI 插件与 Provider
|
|
239
|
+
npx @mearl/setup@beta uninstall --full
|
|
240
|
+
|
|
241
|
+
# 安装可选浏览器 Provider
|
|
242
|
+
npx @mearl/setup@beta provider install @example/mearl-provider-device
|
|
243
|
+
npx @mearl/setup@beta provider install @example/mearl-provider-cloud
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
原有 Mearl 环境包的 npm/npx 安装方式和 CLI 保持可用;setup 是统一的便利入口。
|
|
247
|
+
|
|
248
|
+
## Preset 安装
|
|
249
|
+
|
|
250
|
+
`mearl-setup preset apply <source-url-or-path>` 准备确切资源、激活本机 Node 选择,并向兼容扩展同步同一批浏览器资源;`preset update` 保留用户覆盖,`preset status` 只读检查实际就绪状态。退出码 3 表示存在待同步/安装组件。完整扩展、默认设置、Skills/Provider 和网页一键安装使用同一实际状态核验,见 [本机安装合同](../../docs/preset-installation.zh-CN.md)。
|
|
251
|
+
|
|
252
|
+
install/update 在替换包前保存选中 Preset 的确切资源和期望选择,运行时初始化后同步宿主并安装 Skills;`extension --full` 在替换扩展前完成同一准备流程。已有本机选择优先保留,显式 `--preset` 用于更新或修复资源。配置或资源不可用时停止包变更;部分升级后的重试使用已保存记录。卸载不准备或安装 Preset。
|
|
253
|
+
|
|
254
|
+
默认目录为公共站点的 `installation.json`,可通过 `MEARL_INSTALLATION_CONFIG_URL` 指定其他 HTTPS 目录(本地验证允许回环 HTTP)。目录采用网页安装的校验合同;本机与浏览器未全部确认时报告 pending。
|
|
255
|
+
|
|
256
|
+
## 浏览器 preset 与发行入口
|
|
257
|
+
|
|
258
|
+
主动执行 setup 时,先沿用本机已选 preset;本机未选择时,在运行时初始化后、安装 Skill 前采用已连接浏览器的受信 preset。显式 `--preset <source>` 优先。多个浏览器的选择不同、来源不受信或状态读取失败时要求显式选择。首次连接最多等待约一分钟;浏览器关闭时只安装基础环境并说明重试方式。普通连接和后台核心更新不会自动安装首次本机 preset。
|
|
259
|
+
|
|
260
|
+
发行包可设置本机进程环境变量 `MEARL_SETUP_DEFAULT_PRESET` 提供默认清单来源;默认值仅在两端均无选择时生效,插件、Provider、卸载等子命令保持原行为。安装与状态核验仍由公共安装器负责。
|
package/dist/browserExtension.js
CHANGED
|
@@ -6,15 +6,15 @@ import path from 'node:path';
|
|
|
6
6
|
import { createHash, randomUUID } from 'node:crypto';
|
|
7
7
|
import { runUnifiedCheck } from '@mearl/client/check';
|
|
8
8
|
import { CLIENT_VERSION, invoke as invokeLocalMearl } from '@mearl/client/local';
|
|
9
|
-
import
|
|
9
|
+
import AdmZip from 'adm-zip';
|
|
10
10
|
import WebSocket from 'ws';
|
|
11
11
|
import { compareMearlReleaseVersions, isMearlReleaseVersion } from './version.js';
|
|
12
12
|
export const MEARL_EXTENSION_DOWNLOAD_PAGE = SETUP_VERSION.includes('-beta.')
|
|
13
|
-
? `https://github.com/
|
|
14
|
-
: 'https://github.com/
|
|
13
|
+
? `https://github.com/mearlhq/mearl/releases/tag/v${SETUP_VERSION}`
|
|
14
|
+
: 'https://github.com/mearlhq/mearl/releases/latest';
|
|
15
15
|
export const MEARL_EXTENSION_REMOTE_CONFIG_URL = SETUP_VERSION.includes('-beta.')
|
|
16
|
-
? `https://github.com/
|
|
17
|
-
: 'https://github.com/
|
|
16
|
+
? `https://github.com/mearlhq/mearl/releases/download/v${SETUP_VERSION}/config.json`
|
|
17
|
+
: 'https://github.com/mearlhq/mearl/releases/latest/download/config.json';
|
|
18
18
|
export const MEARL_EXTENSION_DIRECTORY_NAME = 'chrome-extension';
|
|
19
19
|
export const MEARL_EXTENSION_ID = 'aoehhjnofngknnjefamjbplchbolghkm';
|
|
20
20
|
export const MEARL_EXTENSION_SETUP_MARKER = '.mearl-setup.json';
|
|
@@ -52,7 +52,7 @@ export function extensionArchiveUrl(version) {
|
|
|
52
52
|
if (!isMearlReleaseVersion(version)) {
|
|
53
53
|
throw new Error(`Invalid Mearl browser extension version: ${version}`);
|
|
54
54
|
}
|
|
55
|
-
return `https://github.com/
|
|
55
|
+
return `https://github.com/mearlhq/mearl/releases/download/v${version}/mearl-chrome-v${version}.zip`;
|
|
56
56
|
}
|
|
57
57
|
function validateExtensionRelease(release) {
|
|
58
58
|
if (!isMearlReleaseVersion(release.version)) {
|
|
@@ -414,6 +414,22 @@ function readExtensionManifest(directory) {
|
|
|
414
414
|
}
|
|
415
415
|
return parsed;
|
|
416
416
|
}
|
|
417
|
+
async function extractExtensionArchive(archivePath, destination) {
|
|
418
|
+
const archive = new AdmZip(archivePath);
|
|
419
|
+
// Extension releases contain ordinary files and directories, never symlinks.
|
|
420
|
+
// Validate every entry before writing anything into the fresh staging directory.
|
|
421
|
+
for (const entry of archive.getEntries()) {
|
|
422
|
+
const name = entry.entryName;
|
|
423
|
+
const type = (entry.header.attr >>> 16) & 0o170000;
|
|
424
|
+
if (name.includes('\\') || name.includes('\0') ||
|
|
425
|
+
path.posix.isAbsolute(name) || path.win32.isAbsolute(name) ||
|
|
426
|
+
name.split('/').some(part => part === '..' || part === '.') ||
|
|
427
|
+
(type !== 0 && type !== 0o100000 && type !== 0o040000)) {
|
|
428
|
+
throw new Error('Extension archive contains an unsafe entry');
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
await archive.extractAllToAsync(destination, false);
|
|
432
|
+
}
|
|
417
433
|
async function downloadAndReplaceExtension(releaseInput, targetDirectory, fetchImpl, extractArchive) {
|
|
418
434
|
const release = validateExtensionRelease(releaseInput);
|
|
419
435
|
const parentDirectory = path.dirname(targetDirectory);
|
|
@@ -462,7 +478,7 @@ export async function updateSetupManagedBrowserExtensionFiles(options, dependenc
|
|
|
462
478
|
}
|
|
463
479
|
const fetchImpl = dependencies.fetch ?? fetch;
|
|
464
480
|
const extractArchive = dependencies.extractArchive ??
|
|
465
|
-
|
|
481
|
+
extractExtensionArchive;
|
|
466
482
|
const release = await downloadAndReplaceExtension(options.release, targetDirectory, fetchImpl, extractArchive);
|
|
467
483
|
return { ...release, path: targetDirectory };
|
|
468
484
|
}
|
|
@@ -470,7 +486,7 @@ export async function installMearlBrowserExtension(options = {}, dependencies =
|
|
|
470
486
|
const connectCdp = dependencies.connectCdp ?? connectBrowserExtensionViaCdp;
|
|
471
487
|
const fetchImpl = dependencies.fetch ?? fetch;
|
|
472
488
|
const extractArchive = dependencies.extractArchive ??
|
|
473
|
-
|
|
489
|
+
extractExtensionArchive;
|
|
474
490
|
const loadUnpacked = dependencies.loadUnpacked ?? loadUnpackedViaCdp;
|
|
475
491
|
const inspectInstallation = dependencies.inspectInstallation ?? inspectMearlExtensionViaCdp;
|
|
476
492
|
const inspectInstalled = dependencies.inspectInstalled ?? inspectSetupManagedExtension;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { invokeLocalHost, listLiveBrowsers } from '@mearl/client/local';
|
|
2
|
+
import { loadInstallationConfig } from '@mearl/preset/installation';
|
|
3
|
+
import { createNodePresetLoader } from './preset.js';
|
|
4
|
+
import type { StagedPresetInstallation } from './preset.js';
|
|
5
|
+
interface Options {
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
waitMs?: number;
|
|
8
|
+
writeLine?: (line: string) => void;
|
|
9
|
+
}
|
|
10
|
+
interface Dependencies {
|
|
11
|
+
browsers?: typeof listLiveBrowsers;
|
|
12
|
+
invoke?: typeof invokeLocalHost;
|
|
13
|
+
configuration?: () => ReturnType<typeof loadInstallationConfig>;
|
|
14
|
+
load?: ReturnType<typeof createNodePresetLoader>;
|
|
15
|
+
now?: () => number;
|
|
16
|
+
sleep?: (ms: number) => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/** Explicit setup may complete a trusted browser selection before installing any Skills. */
|
|
19
|
+
export declare function prepareBrowserSetupPreset(options?: Options, dependencies?: Dependencies): Promise<StagedPresetInstallation | undefined>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { invokeLocalHost, listLiveBrowsers } from '@mearl/client/local';
|
|
2
|
+
import { installationOriginCovered, loadInstallationConfig, matchesInstallationOrigin, trustedInstallationPreset } from '@mearl/preset/installation';
|
|
3
|
+
import { createNodePresetLoader, PresetInstaller, resolveInstallationConfigUrl } from './preset.js';
|
|
4
|
+
/** Explicit setup may complete a trusted browser selection before installing any Skills. */
|
|
5
|
+
export async function prepareBrowserSetupPreset(options = {}, dependencies = {}) {
|
|
6
|
+
const env = options.env ?? process.env;
|
|
7
|
+
const directory = env.MEARL_CAPABILITY_DIR;
|
|
8
|
+
const current = await new PresetInstaller({ directory }).store.current();
|
|
9
|
+
if (current.activation || current.receipt)
|
|
10
|
+
return;
|
|
11
|
+
const now = dependencies.now ?? Date.now;
|
|
12
|
+
const deadline = now() + (options.waitMs ?? 0);
|
|
13
|
+
const browsers = dependencies.browsers ?? listLiveBrowsers;
|
|
14
|
+
const invoke = dependencies.invoke ?? invokeLocalHost;
|
|
15
|
+
const sleep = dependencies.sleep ?? (ms => new Promise(resolve => setTimeout(resolve, ms)));
|
|
16
|
+
const configuration = dependencies.configuration ?? (() => {
|
|
17
|
+
const url = resolveInstallationConfigUrl(env.MEARL_INSTALLATION_CONFIG_URL);
|
|
18
|
+
return loadInstallationConfig(url, url.origin, AbortSignal.timeout(5_000));
|
|
19
|
+
});
|
|
20
|
+
const read = async (timeoutSec = 5) => {
|
|
21
|
+
const targets = browsers().filter(browser => !browser.provider && browser.transport === 'extension' && (browser.status === undefined || browser.status === 'connected') && (!env.MEARL_SOCKET_PATH || browser.socketPath === env.MEARL_SOCKET_PATH));
|
|
22
|
+
if (!targets.length)
|
|
23
|
+
return;
|
|
24
|
+
return (await Promise.all(targets.map(async (browser) => {
|
|
25
|
+
let value;
|
|
26
|
+
try {
|
|
27
|
+
value = await invoke('preset_browser_status', {}, { socketPath: browser.socketPath, timeoutSec });
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new Error(`Cannot read preset from ${browser.browserId}. Reconnect the extension and retry setup, or use --preset <source>.`);
|
|
31
|
+
}
|
|
32
|
+
if (value?.error || typeof value?.browserReady !== 'boolean' || !Object.hasOwn(value, 'browserRevision') || (value.presetId !== undefined && (typeof value.presetId !== 'string' || !value.presetId || typeof value.presetSource !== 'string' || typeof value.browserRevision !== 'string' || value.browserReady !== true)))
|
|
33
|
+
throw new Error(`Browser preset is unavailable in ${browser.browserId}. Reconnect the extension and retry setup, or use --preset <source>.`);
|
|
34
|
+
if (!value.presetId)
|
|
35
|
+
return [];
|
|
36
|
+
return [{ browserId: browser.browserId, presetId: value.presetId, presetSource: value.presetSource, browserRevision: value.browserRevision }];
|
|
37
|
+
}))).flat();
|
|
38
|
+
};
|
|
39
|
+
let selected = await read();
|
|
40
|
+
if (!selected && now() < deadline)
|
|
41
|
+
options.writeLine?.('Waiting for the Mearl extension to connect and checking its installed preset…');
|
|
42
|
+
while (!selected && now() < deadline) {
|
|
43
|
+
await sleep(Math.min(1000, deadline - now()));
|
|
44
|
+
if (now() < deadline)
|
|
45
|
+
selected = await read(Math.max(0.1, Math.min(5, (deadline - now()) / 1000)));
|
|
46
|
+
}
|
|
47
|
+
if (!selected) {
|
|
48
|
+
options.writeLine?.('No connected Mearl extension was found. Continuing without a browser preset; connect and rerun setup to adopt it, or use --preset <source> to select a preset.');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (!selected.length)
|
|
52
|
+
return;
|
|
53
|
+
const accepted = selected;
|
|
54
|
+
const first = accepted[0];
|
|
55
|
+
if (accepted.some(value => value.presetId !== first.presetId || value.presetSource !== first.presetSource))
|
|
56
|
+
throw new Error('Connected browsers use different presets. Select one explicitly with --preset <source>.');
|
|
57
|
+
const trusted = trustedInstallationPreset(await configuration(), first.presetId, first.presetSource);
|
|
58
|
+
if (!trusted)
|
|
59
|
+
throw new Error('The browser preset is not in the trusted installation catalog. Install it explicitly with --preset <source>.');
|
|
60
|
+
const load = dependencies.load ?? createNodePresetLoader();
|
|
61
|
+
const installer = new PresetInstaller({ directory, load: (source, resourceOptions) => {
|
|
62
|
+
const url = new URL(source);
|
|
63
|
+
if (url.protocol !== 'https:' || url.username || url.password || !trusted.origins.some(origin => matchesInstallationOrigin(origin, url)))
|
|
64
|
+
throw new Error('Browser preset resource is outside the trusted installation catalog');
|
|
65
|
+
return load(source, resourceOptions);
|
|
66
|
+
} });
|
|
67
|
+
options.writeLine?.(`Using the browser's trusted preset: ${first.presetId}`);
|
|
68
|
+
return installer.stage(first.presetSource, {
|
|
69
|
+
expectedId: first.presetId, expectedRevision: null, signal: AbortSignal.timeout(120_000),
|
|
70
|
+
validatePrepared: async (prepared) => {
|
|
71
|
+
if (prepared.preset.permissions.origins.some(origin => !installationOriginCovered(origin, trusted.origins)))
|
|
72
|
+
throw new Error('Browser preset permissions exceed the trusted installation catalog');
|
|
73
|
+
},
|
|
74
|
+
confirm: async () => {
|
|
75
|
+
if (JSON.stringify(trustedInstallationPreset(await configuration(), first.presetId, first.presetSource)) !== JSON.stringify(trusted))
|
|
76
|
+
throw new Error('Browser preset trust changed during setup');
|
|
77
|
+
const latest = await read();
|
|
78
|
+
if (!latest || latest.length !== accepted.length || accepted.some(value => !latest.some(other => value.browserId === other.browserId && value.presetId === other.presetId && value.presetSource === other.presetSource && value.browserRevision === other.browserRevision)))
|
|
79
|
+
throw new Error('The browser preset changed during setup. Retry or use --preset <source>.');
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -5,16 +5,17 @@ import type { MearlProfile } from './profiles.js';
|
|
|
5
5
|
import { ensureOfficialPlugin, installPlugins, setupPlugin, uninstallPlugin } from './plugin.js';
|
|
6
6
|
import { installProviderPackage, uninstallProviderPackage, updateProviderPackage } from './provider.js';
|
|
7
7
|
import { runPresetCommand } from './preset.js';
|
|
8
|
-
import { prepareSetupPreset } from './
|
|
8
|
+
import { prepareSetupPreset } from './setupPreset.js';
|
|
9
9
|
export { SETUP_VERSION } from './setupVersion.js';
|
|
10
10
|
interface ParsedArgs {
|
|
11
|
-
cloudMode?:
|
|
11
|
+
cloudMode?: string;
|
|
12
12
|
force?: boolean;
|
|
13
13
|
full?: boolean;
|
|
14
14
|
help: boolean;
|
|
15
15
|
operation?: MearlLifecycleOperation | 'extension' | 'plugin' | 'provider' | 'preset';
|
|
16
|
-
presetCommand?: 'apply' | 'update' | 'status';
|
|
16
|
+
presetCommand?: 'apply' | 'update' | 'status' | 'repair';
|
|
17
17
|
presetSource?: string;
|
|
18
|
+
presetReplaceSkills?: string[];
|
|
18
19
|
pluginName?: string;
|
|
19
20
|
pluginPath?: string;
|
|
20
21
|
pluginInstallArgs?: string[];
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { isMearlReleaseLine } from './version.js';
|
|
|
6
6
|
import { ensureOfficialPlugin, installPlugins, setupPlugin, uninstallPlugin } from './plugin.js';
|
|
7
7
|
import { installProviderPackage, uninstallProviderPackage, updateProviderPackage, } from './provider.js';
|
|
8
8
|
import { runPresetCommand } from './preset.js';
|
|
9
|
-
import { prepareSetupPreset } from './
|
|
9
|
+
import { prepareSetupPreset } from './setupPreset.js';
|
|
10
10
|
import { SETUP_VERSION, SETUP_COMMAND } from './setupVersion.js';
|
|
11
11
|
export { SETUP_VERSION } from './setupVersion.js';
|
|
12
12
|
const baseParsedArgs = () => ({
|
|
@@ -26,11 +26,33 @@ function commanderError(error) {
|
|
|
26
26
|
}
|
|
27
27
|
export function parseSetupArgs(argv) {
|
|
28
28
|
if (argv[0] === 'preset') {
|
|
29
|
+
if (argv[1] === 'repair') {
|
|
30
|
+
const command = new Command('preset repair')
|
|
31
|
+
.exitOverride()
|
|
32
|
+
.configureOutput({ writeErr: () => { }, writeOut: () => { } })
|
|
33
|
+
.allowExcessArguments(false)
|
|
34
|
+
.option('--replace-skill <name>', 'Replace the named Skill source or conflicting plugin registration', (name, previous) => [...previous, name], []);
|
|
35
|
+
try {
|
|
36
|
+
command.parse(argv.slice(2), { from: 'user' });
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (error instanceof CommanderError) {
|
|
40
|
+
if (error.code === 'commander.helpDisplayed')
|
|
41
|
+
return { ...baseParsedArgs(), help: true };
|
|
42
|
+
throw commanderError(error);
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
const names = command.opts().replaceSkill;
|
|
47
|
+
if (names.length > 256 || new Set(names).size !== names.length || names.some(name => !/^[a-z][a-z0-9-]{0,79}$/.test(name)))
|
|
48
|
+
throw new Error('--replace-skill requires unique valid Skill names (up to 256)');
|
|
49
|
+
return { ...baseParsedArgs(), operation: 'preset', presetCommand: 'repair', ...(names.length ? { presetReplaceSkills: names } : {}) };
|
|
50
|
+
}
|
|
29
51
|
if (argv[1] === 'apply' && argv.length === 3 && argv[2])
|
|
30
52
|
return { ...baseParsedArgs(), operation: 'preset', presetCommand: 'apply', presetSource: argv[2] };
|
|
31
53
|
if ((argv[1] === 'update' || argv[1] === 'status') && argv.length === 2)
|
|
32
54
|
return { ...baseParsedArgs(), operation: 'preset', presetCommand: argv[1] };
|
|
33
|
-
throw new Error('preset requires apply <source-url-or-path>, update, or status');
|
|
55
|
+
throw new Error('preset requires apply <source-url-or-path>, update, repair [--replace-skill <name>], or status');
|
|
34
56
|
}
|
|
35
57
|
if (argv[0] === 'provider') {
|
|
36
58
|
const command = argv[1];
|
|
@@ -87,7 +109,7 @@ export function parseSetupArgs(argv) {
|
|
|
87
109
|
.configureOutput({ writeErr: () => { }, writeOut: () => { } })
|
|
88
110
|
.option('--local')
|
|
89
111
|
.option('--cloud')
|
|
90
|
-
.option('--
|
|
112
|
+
.option('--cloud-mode <id>')
|
|
91
113
|
.option('--mcp')
|
|
92
114
|
.option('--target-line <major.minor.x>')
|
|
93
115
|
.option('--skip-skill')
|
|
@@ -98,9 +120,8 @@ export function parseSetupArgs(argv) {
|
|
|
98
120
|
command.option('--full');
|
|
99
121
|
command.action(() => {
|
|
100
122
|
const options = command.opts();
|
|
101
|
-
if (options.
|
|
102
|
-
throw new Error('--cloud
|
|
103
|
-
}
|
|
123
|
+
if (options.cloudMode !== undefined && !/^[a-z][a-z0-9-]{0,63}$/.test(options.cloudMode))
|
|
124
|
+
throw new Error('--cloud-mode requires a valid installed mode ID');
|
|
104
125
|
if (options.targetLine && !isMearlReleaseLine(options.targetLine)) {
|
|
105
126
|
throw new Error('--target-line requires a valid Mearl release line such as 2.3.x');
|
|
106
127
|
}
|
|
@@ -114,15 +135,15 @@ export function parseSetupArgs(argv) {
|
|
|
114
135
|
const profiles = [];
|
|
115
136
|
if (options.local)
|
|
116
137
|
profiles.push('local');
|
|
117
|
-
if (options.cloud || options.
|
|
138
|
+
if (options.cloud || options.cloudMode)
|
|
118
139
|
profiles.push('cloud');
|
|
119
140
|
if (options.mcp)
|
|
120
141
|
profiles.push('mcp');
|
|
121
142
|
parsed = {
|
|
122
143
|
...baseParsedArgs(),
|
|
123
144
|
operation,
|
|
124
|
-
...(options.cloud || options.
|
|
125
|
-
? { cloudMode: options.
|
|
145
|
+
...(options.cloud || options.cloudMode
|
|
146
|
+
? { cloudMode: options.cloudMode ?? 'websocket' }
|
|
126
147
|
: {}),
|
|
127
148
|
...(profiles.length > 0 ? { profiles } : {}),
|
|
128
149
|
...(options.full ? { full: true } : {}),
|
|
@@ -187,6 +208,7 @@ Environment commands:
|
|
|
187
208
|
Integration commands:
|
|
188
209
|
preset apply <source> Apply a preset, synchronize hosts and install declared components
|
|
189
210
|
preset update Update the selected preset, preserving user overrides
|
|
211
|
+
preset repair Retry the installed preset using its accepted resources
|
|
190
212
|
preset status Report actual readiness (exit 3 when synchronization is pending)
|
|
191
213
|
plugin install <arguments...> Install Skills and register their CLI plugins
|
|
192
214
|
plugin uninstall <name> Uninstall one CLI plugin
|
|
@@ -201,7 +223,7 @@ Browser extension:
|
|
|
201
223
|
Environment options:
|
|
202
224
|
--local Manage @mearl/native-host and @mearl/client
|
|
203
225
|
--cloud Manage cloud packages in WebSocket server mode
|
|
204
|
-
--
|
|
226
|
+
--cloud-mode <id> Manage cloud packages using an installed mode
|
|
205
227
|
--mcp Manage @mearl/mcp-server and its configuration
|
|
206
228
|
--target-line <major.minor.x> Use the latest packages in one release line
|
|
207
229
|
--preset <source> Apply a preset after installing or updating the environment
|
|
@@ -214,6 +236,9 @@ Extension options:
|
|
|
214
236
|
--full Install the extension and complete local environment
|
|
215
237
|
--force Replace an extension managed by another source
|
|
216
238
|
|
|
239
|
+
Preset repair options:
|
|
240
|
+
--replace-skill <name> Replace one Skill source or conflicting registration; repeat for more
|
|
241
|
+
|
|
217
242
|
General options:
|
|
218
243
|
--yes, -y Accept prompts and detected defaults
|
|
219
244
|
--non-interactive Alias for --yes, intended for Agents and CI
|
|
@@ -228,6 +253,7 @@ Examples:
|
|
|
228
253
|
npx @mearl/setup uninstall --full
|
|
229
254
|
npx @mearl/setup plugin uninstall yuque
|
|
230
255
|
npx @mearl/setup provider install @example/mearl-provider-brow
|
|
256
|
+
npx @mearl/setup preset repair --replace-skill example-skill
|
|
231
257
|
npx @mearl/setup extension --full`;
|
|
232
258
|
console.log(output.replaceAll('npx @mearl/setup', SETUP_COMMAND));
|
|
233
259
|
}
|
|
@@ -235,7 +261,9 @@ async function runSetupLifecycle(options, dependencies) {
|
|
|
235
261
|
let preset;
|
|
236
262
|
const callback = options.operation === 'uninstall' ? undefined : { onPresetStatus: (status) => { preset = status; } };
|
|
237
263
|
const lifecycle = dependencies.runLifecycle ?? runMearlLifecycle;
|
|
238
|
-
const
|
|
264
|
+
const defaultPresetSource = process.env.MEARL_SETUP_DEFAULT_PRESET;
|
|
265
|
+
const configured = defaultPresetSource ? { ...options, defaultPresetSource } : options;
|
|
266
|
+
const ok = callback ? await lifecycle(configured, callback) : await lifecycle(configured);
|
|
239
267
|
if (!ok)
|
|
240
268
|
return 1;
|
|
241
269
|
if (!preset) {
|
|
@@ -269,7 +297,10 @@ export async function main(argv = process.argv.slice(2), interactiveTerminal = p
|
|
|
269
297
|
}
|
|
270
298
|
if (parsed.operation === 'preset') {
|
|
271
299
|
try {
|
|
272
|
-
const
|
|
300
|
+
const run = dependencies.runPreset ?? runPresetCommand;
|
|
301
|
+
const result = parsed.presetReplaceSkills
|
|
302
|
+
? await run(parsed.presetCommand, parsed.presetSource, { replaceSkills: parsed.presetReplaceSkills })
|
|
303
|
+
: await run(parsed.presetCommand, parsed.presetSource);
|
|
273
304
|
console.log(JSON.stringify(result, null, 2));
|
|
274
305
|
return result.ready ? 0 : 3;
|
|
275
306
|
}
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -5,17 +5,22 @@ import { detectGlobalPackage, type PackageManagerInstallation, type PackageDetec
|
|
|
5
5
|
import { type DetectedMearlProfiles, type MearlProfile } from './profiles.js';
|
|
6
6
|
import { uninstallNativeHostArtifacts } from './nativeHost.js';
|
|
7
7
|
import { type CommandRunner } from '@mearl/daemon-core';
|
|
8
|
+
import { readInstallationManagement } from '@mearl/daemon-core/installation';
|
|
9
|
+
import { claimPresetInstallation } from './presetInstallationLease.js';
|
|
8
10
|
import { installManagedPlugins, uninstallManagedPlugins } from './managedPlugins.js';
|
|
9
11
|
import { uninstallPlugin } from './plugin.js';
|
|
10
12
|
import { uninstallProviderPackage } from './provider.js';
|
|
11
13
|
import { cleanupUninstalledRuntime } from './runtimeCleanup.js';
|
|
12
|
-
import { prepareSetupPreset } from './
|
|
14
|
+
import { prepareSetupPreset } from './setupPreset.js';
|
|
15
|
+
import { prepareBrowserSetupPreset } from './browserPreset.js';
|
|
13
16
|
import type { PresetInstallStatus } from './preset.js';
|
|
14
17
|
export type MearlLifecycleOperation = 'install' | 'update' | 'uninstall';
|
|
15
18
|
export interface MearlLifecycleOptions {
|
|
16
19
|
presetSource?: string;
|
|
20
|
+
/** Distribution default used only when neither local nor browser selection exists. */
|
|
21
|
+
defaultPresetSource?: string;
|
|
17
22
|
operation: MearlLifecycleOperation;
|
|
18
|
-
cloudMode?:
|
|
23
|
+
cloudMode?: string;
|
|
19
24
|
/** Uninstall every registered CLI plugin and Provider in addition to selected environments. */
|
|
20
25
|
full?: boolean;
|
|
21
26
|
/** @deprecated Use profiles when selecting one or more composable environments. */
|
|
@@ -27,6 +32,9 @@ export interface MearlLifecycleOptions {
|
|
|
27
32
|
env?: NodeJS.ProcessEnv;
|
|
28
33
|
}
|
|
29
34
|
export interface MearlLifecycleDependencies {
|
|
35
|
+
readManagement?: typeof readInstallationManagement;
|
|
36
|
+
claimLifecycle?: () => ReturnType<typeof claimPresetInstallation>;
|
|
37
|
+
prepareBrowserPreset?: typeof prepareBrowserSetupPreset;
|
|
30
38
|
preparePreset?: typeof prepareSetupPreset;
|
|
31
39
|
onPresetStatus?: (status: PresetInstallStatus) => void;
|
|
32
40
|
/** Prepare the selected manager after packages are usable and before Skill operations. */
|