@mearl/setup 2.14.0 → 2.15.1

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 CHANGED
@@ -12,24 +12,17 @@ npx @mearl/setup plugin uninstall <name>
12
12
  npx @mearl/setup provider <install|update|uninstall> <package|id>
13
13
  ```
14
14
 
15
- 配置 AgentBay API Key 时只需执行一次下面的命令,然后在隐藏输入提示中粘贴 Key。
16
- 重复执行会更新现有 Key,并保留 `~/.mearl/.env` 中的其他配置:
17
-
18
- ```bash
19
- npx @mearl/setup agentbay
20
- ```
21
-
22
- 无交互环境可使用 `--stdin` 或 `--key-file <path>`。为避免 Key 进入 shell history 和
23
- 进程参数,不接受把 Key 直接写在命令行中。
15
+ 浏览器 Provider 独立安装和演进。安装后运行 `mearl <providerId>` 查看该版本声明的能力、
16
+ 配置和启动参数。
24
17
 
25
18
  在 Qoder Cloud Agent 环境中,使用 `npx @mearl/setup --qoder-cloud --yes` 选择 Qoder
26
19
  配对模式。它与 `--cloud` 安装同一套 cloud-server、client 和 Skill,只把 cloud-server
27
20
  模式记录为 Qoder;`mearl-cloud-server` 的命令交互保持不变,`start` 和 `restart` 输出新的
28
21
  配对指令后退出,不监听端口或保留守护进程。配对 URL 不包含 Qoder PAT。
29
22
 
30
- ## 安装与注册 Skill CLI 插件
23
+ ## 安装与注册 Skill 插件
31
24
 
32
- 一个命令安装 skill 并注册它的 CLI 能力:
25
+ 一个命令安装 skill 并注册它的 CLI、SDK 和 MCP 能力:
33
26
 
34
27
  ```bash
35
28
  npx @mearl/setup plugin install trip/mearl --skill yuque-doc-fetch --yes
@@ -41,9 +34,16 @@ mearl yuque read "https://aliyuque.antfin.com/group/book/doc"
41
34
  setup 从 `Installed … skill(s)` 成功区块提取路径,仅注册本次安装且包含 `scripts/main.mjs` 的 skill。
42
35
  普通 skill 正常安装。输出格式无法识别时提示手动注册,不扫描其他 skill 或猜测安装结果。
43
36
 
37
+ 默认 `install` 和 `update` 完成后,setup 会用最多 5 秒的 `npm ping` 检查阿里内网 registry。
38
+ 可访问时通过上述 Ali Skills 链路依次自动安装并注册 `buc-request` 与 `tdbank-account`;该 registry 不用于下载插件。
39
+ 探测不可用或插件安装失败不会破坏 Mearl 主环境的安装;`--skip-skill` 和
40
+ `--target-line` 安装不执行自动插件步骤。
41
+
42
+ Mearl client 内置官方插件名称与安装来源目录。CLI、SDK、MCP 或另一个插件首次调用尚未注册的官方插件时,会通过内部幂等流程安装并重试一次;未知名称不会触发安装。
43
+
44
44
  自动注册默认使用安装目录名;入口可用 `export const name = 'yuque'` 指定简短命令名。
45
45
  重装会刷新同名绑定。每个入口必须默认导出 `register(app)`;完整协议见
46
- [CLI 插件规范](../skills/mearl-skill-creator/references/cli-plugin.md)。
46
+ [Skill 插件规范](../skills/mearl-skill-creator/references/cli-plugin.md)。
47
47
 
48
48
  已通过其他渠道分发的 skill 或自定义入口,可以直接传入路径注册:
49
49
 
@@ -57,7 +57,7 @@ path 接受目录或具体文件;目录默认读取 `scripts/main.mjs`,相
57
57
  两种注册流程都会检查本机 `@mearl/client` 和 `@mearl/native-host`:缺失或低于当前 setup 版本时,
58
58
  复用已有包管理器安装 setup 的对应版本,更新后再次验证;更高版本保持不动。
59
59
  native-host 会幂等初始化 Skills 模式。基础环境补齐或初始化失败时保留已安装的 skill,并停止注册。
60
- 注册后可在任意目录运行 `mearl <name> --help`,日常业务调用不执行安装或版本检查。
60
+ 注册后可在任意目录运行 `mearl <name> --help`,也可通过 `@mearl/client` 的 `invokePlugin` 或 MCP 的 `mearl_plugin` 调用。已注册插件的日常调用不执行安装或版本检查;只有缺失的官方插件会触发一次按需安装。
61
61
 
62
62
  不再需要插件时执行:
63
63
 
@@ -211,11 +211,9 @@ npx @mearl/setup uninstall --local
211
211
  # 自动检测并无交互卸载
212
212
  npx @mearl/setup uninstall --yes
213
213
 
214
- # 配置或更新 AgentBay API Key
215
- npx @mearl/setup agentbay
216
-
217
- # 无交互配置(从权限受控的文件读取)
218
- npx @mearl/setup agentbay --key-file ./agentbay.key
214
+ # 安装可选浏览器 Provider
215
+ npx @mearl/setup provider install @ali/mearl-provider-brow
216
+ npx @mearl/setup provider install @ali/mearl-provider-agentbay
219
217
  ```
220
218
 
221
219
  原有 Mearl 环境包的 npm/npx 安装方式和 CLI 保持可用;setup 是统一的便利入口。
package/dist/cli.d.ts CHANGED
@@ -1,22 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin } from './agentBay.js';
3
2
  import { installMearlBrowserExtension } from './browserExtension.js';
4
3
  import { runMearlLifecycle, type MearlLifecycleOperation } from './lifecycle.js';
5
4
  import type { MearlProfile } from './profiles.js';
6
- import { installPlugins, setupPlugin, uninstallPlugin } from './plugin.js';
5
+ import { ensureOfficialPlugin, installPlugins, setupPlugin, uninstallPlugin } from './plugin.js';
7
6
  import { installProviderPackage, uninstallProviderPackage, updateProviderPackage } from './provider.js';
8
7
  export declare const SETUP_VERSION: string;
9
8
  interface ParsedArgs {
10
- agentBayKeyFile?: string;
11
- agentBayReadStdin: boolean;
12
9
  cloudMode?: 'websocket' | 'qoder';
13
10
  force?: boolean;
14
11
  full?: boolean;
15
12
  help: boolean;
16
- operation?: MearlLifecycleOperation | 'agentbay' | 'extension' | 'plugin' | 'provider';
13
+ operation?: MearlLifecycleOperation | 'extension' | 'plugin' | 'provider';
17
14
  pluginName?: string;
18
15
  pluginPath?: string;
19
16
  pluginInstallArgs?: string[];
17
+ pluginEnsureName?: string;
20
18
  pluginUninstallName?: string;
21
19
  providerCommand?: 'install' | 'uninstall' | 'update';
22
20
  providerTarget?: string;
@@ -28,12 +26,9 @@ interface ParsedArgs {
28
26
  }
29
27
  export interface SetupCliDependencies {
30
28
  registerPlugin?: typeof setupPlugin;
29
+ ensureOfficialPlugin?: typeof ensureOfficialPlugin;
31
30
  installPlugins?: typeof installPlugins;
32
31
  uninstallPlugin?: typeof uninstallPlugin;
33
- configureAgentBay?: typeof configureAgentBayApiKey;
34
- promptAgentBayKey?: typeof promptAgentBayApiKey;
35
- readAgentBayKeyFile?: typeof readAgentBayApiKeyFile;
36
- readAgentBayKeyStdin?: typeof readAgentBayApiKeyStdin;
37
32
  installBrowserExtension?: typeof installMearlBrowserExtension;
38
33
  installProvider?: typeof installProviderPackage;
39
34
  runLifecycle?: typeof runMearlLifecycle;
package/dist/cli.js CHANGED
@@ -1,16 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { Command, CommanderError } from 'commander';
4
- import { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
5
4
  import { installMearlBrowserExtension } from './browserExtension.js';
6
5
  import { runMearlLifecycle } from './lifecycle.js';
7
6
  import { isMearlReleaseLine } from './version.js';
8
- import { installPlugins, setupPlugin, uninstallPlugin } from './plugin.js';
7
+ import { ensureOfficialPlugin, installPlugins, setupPlugin, uninstallPlugin } from './plugin.js';
9
8
  import { installProviderPackage, uninstallProviderPackage, updateProviderPackage, } from './provider.js';
10
9
  const require = createRequire(import.meta.url);
11
10
  export const SETUP_VERSION = require('../package.json').version;
12
11
  const baseParsedArgs = () => ({
13
- agentBayReadStdin: false,
14
12
  help: false,
15
13
  skipSkill: false,
16
14
  yes: false,
@@ -43,6 +41,12 @@ export function parseSetupArgs(argv) {
43
41
  if (argv[0] === 'plugin' && argv[1] === 'install') {
44
42
  return { ...baseParsedArgs(), operation: 'plugin', pluginInstallArgs: argv.slice(2) };
45
43
  }
44
+ if (argv[0] === 'plugin' && argv[1] === 'ensure') {
45
+ if (argv.length !== 3 || !argv[2]) {
46
+ throw new Error('plugin ensure requires exactly one official plugin name');
47
+ }
48
+ return { ...baseParsedArgs(), operation: 'plugin', pluginEnsureName: argv[2] };
49
+ }
46
50
  if (argv[0] === 'plugin' && argv[1] === 'uninstall') {
47
51
  if (argv.length !== 3 || !argv[2]) {
48
52
  throw new Error('plugin uninstall requires exactly one plugin name');
@@ -128,31 +132,6 @@ export function parseSetupArgs(argv) {
128
132
  };
129
133
  });
130
134
  program.addCommand(extension);
131
- const agentbay = new Command('agentbay')
132
- .exitOverride()
133
- .configureOutput({ writeErr: () => { }, writeOut: () => { } })
134
- .argument('[api-key]')
135
- .option('--stdin')
136
- .option('--key-file <path>')
137
- .action((apiKey) => {
138
- const options = agentbay.opts();
139
- if (apiKey) {
140
- throw new Error('API keys are not accepted as command-line arguments');
141
- }
142
- if (options.stdin && options.keyFile) {
143
- throw new Error('--stdin and --key-file cannot be used together');
144
- }
145
- if (options.keyFile !== undefined && !options.keyFile.trim()) {
146
- throw new Error('--key-file requires a non-empty path');
147
- }
148
- parsed = {
149
- ...baseParsedArgs(),
150
- ...(options.keyFile ? { agentBayKeyFile: options.keyFile } : {}),
151
- agentBayReadStdin: options.stdin === true,
152
- operation: 'agentbay',
153
- };
154
- });
155
- program.addCommand(agentbay);
156
135
  const normalizedArgv = argv[0] === undefined || argv[0].startsWith('-') ? ['install', ...argv] : argv;
157
136
  try {
158
137
  program.parse(['node', 'mearl-setup', ...normalizedArgv]);
@@ -182,14 +161,12 @@ Usage:
182
161
  npx @mearl/setup plugin install <ali-skills add arguments...>
183
162
  npx @mearl/setup plugin uninstall <name>
184
163
  npx @mearl/setup provider <install|update|uninstall> <package|id>
185
- npx @mearl/setup agentbay [--stdin | --key-file <path>]
186
164
 
187
165
  Behavior:
188
166
  install Default command; defaults to --local when no environment is specified
189
167
  update Auto-detects installed environments from the current PATH
190
168
  uninstall Auto-detects installed environments and asks for confirmation
191
169
  extension Install or update the browser extension; --full also installs the local environment
192
- agentbay Save or update AGENTBAY_KEY without changing other Mearl settings
193
170
  plugin Register, install, or uninstall a CLI plugin
194
171
  provider Install npm Provider packages and manage their Mearl registration
195
172
 
@@ -199,8 +176,6 @@ Options:
199
176
  --qoder-cloud
200
177
  Manage the same cloud packages in Qoder pairing mode
201
178
  --mcp Manage @mearl/mcp-server and its configuration
202
- --stdin Read the AgentBay API key from standard input
203
- --key-file Read the AgentBay API key from a local file
204
179
  --target-line <major.minor.x>
205
180
  Install the latest packages from one release line, such as 2.3.x
206
181
  --skip-skill Keep the independently managed Mearl Skill unchanged
@@ -224,7 +199,7 @@ Examples:
224
199
  npx @mearl/setup extension --full
225
200
  npx @mearl/setup plugin uninstall yuque
226
201
  npx @mearl/setup provider install @ali/mearl-provider-brow
227
- npx @mearl/setup agentbay
202
+ npx @mearl/setup provider install @ali/mearl-provider-agentbay
228
203
  npx @mearl/setup --qoder-cloud --yes
229
204
  npx @mearl/setup update --local --target-line 2.3.x --skip-skill
230
205
  npx @mearl/setup uninstall --local`;
@@ -256,6 +231,13 @@ export async function main(argv = process.argv.slice(2), interactiveTerminal = p
256
231
  if (parsed.pluginInstallArgs) {
257
232
  return await (dependencies.installPlugins ?? installPlugins)(parsed.pluginInstallArgs);
258
233
  }
234
+ if (parsed.pluginEnsureName) {
235
+ const result = await (dependencies.ensureOfficialPlugin ?? ensureOfficialPlugin)(parsed.pluginEnsureName);
236
+ console.error(result === 'installed'
237
+ ? `Official plugin ${parsed.pluginEnsureName} installed.`
238
+ : `Official plugin ${parsed.pluginEnsureName} is already registered.`);
239
+ return 0;
240
+ }
259
241
  if (parsed.pluginUninstallName) {
260
242
  const result = await (dependencies.uninstallPlugin ?? uninstallPlugin)(parsed.pluginUninstallName);
261
243
  if (!result.removed) {
@@ -307,31 +289,6 @@ export async function main(argv = process.argv.slice(2), interactiveTerminal = p
307
289
  return 1;
308
290
  }
309
291
  }
310
- if (parsed.operation === 'agentbay') {
311
- try {
312
- const configureAgentBay = dependencies.configureAgentBay ?? configureAgentBayApiKey;
313
- let apiKey;
314
- if (parsed.agentBayKeyFile) {
315
- apiKey = (dependencies.readAgentBayKeyFile ?? readAgentBayApiKeyFile)(parsed.agentBayKeyFile);
316
- }
317
- else if (parsed.agentBayReadStdin) {
318
- apiKey = await (dependencies.readAgentBayKeyStdin ?? readAgentBayApiKeyStdin)();
319
- }
320
- else {
321
- if (!interactiveTerminal) {
322
- throw new Error('Interactive input is unavailable. Re-run with --stdin or --key-file <path>.');
323
- }
324
- apiKey = await (dependencies.promptAgentBayKey ?? promptAgentBayApiKey)();
325
- }
326
- const result = configureAgentBay(apiKey);
327
- console.error(`AgentBay API key saved to ${result.envFile}.`);
328
- return 0;
329
- }
330
- catch (error) {
331
- console.error(error instanceof Error ? error.message : String(error));
332
- return 1;
333
- }
334
- }
335
292
  if (parsed.operation === 'extension') {
336
293
  if (!interactiveTerminal && !parsed.yes) {
337
294
  console.error('Non-interactive input detected. Re-run with --yes or --non-interactive.');
package/dist/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  export { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';
2
- export { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
3
2
  export { BrowserExtensionConflictError, extensionArchiveUrl, fetchBrowserExtensionRelease, findLatestExtensionVersion, installMearlBrowserExtension, inspectMearlExtensionViaCdp, loadUnpackedViaCdp, MEARL_EXTENSION_DIRECTORY_NAME, MEARL_EXTENSION_DOWNLOAD_PAGE, MEARL_EXTENSION_ID, MEARL_EXTENSION_REMOTE_CONFIG_URL, resolveChromeUserDataDirectory, resolveMearlExtensionDirectory, updateSetupManagedBrowserExtensionFiles, } from './browserExtension.js';
4
3
  export type { BrowserExtensionDependencies, BrowserExtensionInstallOptions, BrowserExtensionInstallResult, BrowserExtensionRelease, InstalledBrowserExtension, SetupManagedBrowserExtensionUpdateOptions, SetupManagedBrowserExtensionUpdateResult, } from './browserExtension.js';
5
- export type { AgentBayConfigurationOptions, AgentBayConfigurationResult, AgentBaySecretInput, AgentBaySecretOutput, } from './agentBay.js';
6
4
  export type { DetectedGlobalPackage, GlobalPackageCommand, GlobalPackageManager, PackageManagerInstallation, PackageOperation, PackageDetectionOptions, } from './packageManager.js';
7
5
  export { runMearlLifecycle } from './lifecycle.js';
8
6
  export type { MearlLifecycleDependencies, MearlLifecycleOperation, MearlLifecycleOptions, } from './lifecycle.js';
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  export { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';
2
- export { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
3
2
  export { BrowserExtensionConflictError, extensionArchiveUrl, fetchBrowserExtensionRelease, findLatestExtensionVersion, installMearlBrowserExtension, inspectMearlExtensionViaCdp, loadUnpackedViaCdp, MEARL_EXTENSION_DIRECTORY_NAME, MEARL_EXTENSION_DOWNLOAD_PAGE, MEARL_EXTENSION_ID, MEARL_EXTENSION_REMOTE_CONFIG_URL, resolveChromeUserDataDirectory, resolveMearlExtensionDirectory, updateSetupManagedBrowserExtensionFiles, } from './browserExtension.js';
4
3
  export { runMearlLifecycle } from './lifecycle.js';
5
4
  export { MEARL_PROFILES, detectMearlProfiles } from './profiles.js';
@@ -0,0 +1,21 @@
1
+ import { findPluginRegistration } from '@mearl/client/plugin';
2
+ import { installPlugins, uninstallPlugin } from './plugin.js';
3
+ import { findExecutable, type CommandRunner } from './process.js';
4
+ export declare const ALIBABA_NPM_REGISTRY = "https://registry.anpm.alibaba-inc.com/";
5
+ export declare const ALIBABA_INTERNAL_PLUGIN_NAMES: string[];
6
+ export interface InternalPluginOptions {
7
+ env?: NodeJS.ProcessEnv;
8
+ runCommand?: CommandRunner;
9
+ writeLine?: (line: string) => void;
10
+ }
11
+ export interface InternalPluginDependencies {
12
+ findExecutable?: typeof findExecutable;
13
+ findPluginRegistration?: typeof findPluginRegistration;
14
+ installPlugins?: typeof installPlugins;
15
+ probeAlibabaRegistry?: (options: InternalPluginOptions) => Promise<boolean>;
16
+ uninstallPlugin?: typeof uninstallPlugin;
17
+ }
18
+ export declare function canAccessAlibabaNpmRegistry(options?: InternalPluginOptions, dependencies?: Pick<InternalPluginDependencies, 'findExecutable'>): Promise<boolean>;
19
+ /** Uses the internal registry only as an intranet probe; Ali Skills installs the plugins. */
20
+ export declare function installAlibabaInternalPlugins(options?: InternalPluginOptions, dependencies?: InternalPluginDependencies): Promise<'installed' | 'skipped' | 'failed'>;
21
+ export declare function uninstallAlibabaInternalPlugins(options?: InternalPluginOptions, dependencies?: InternalPluginDependencies): Promise<'uninstalled' | 'skipped' | 'failed'>;
@@ -0,0 +1,97 @@
1
+ import { dirname } from 'node:path';
2
+ import { findOfficialPlugin, findPluginRegistration, listOfficialPluginNames, OFFICIAL_PLUGIN_MANAGER, } from '@mearl/client/plugin';
3
+ import { installPlugins, uninstallPlugin } from './plugin.js';
4
+ import { findExecutable, prependCommandPath, runCommand } from './process.js';
5
+ export const ALIBABA_NPM_REGISTRY = 'https://registry.anpm.alibaba-inc.com/';
6
+ export const ALIBABA_INTERNAL_PLUGIN_NAMES = listOfficialPluginNames('alibaba');
7
+ export async function canAccessAlibabaNpmRegistry(options = {}, dependencies = {}) {
8
+ const env = options.env ?? process.env;
9
+ const runner = options.runCommand ?? runCommand;
10
+ const npm = (dependencies.findExecutable ?? findExecutable)('npm', {
11
+ env,
12
+ extraDirectories: [dirname(process.execPath)],
13
+ });
14
+ if (!npm)
15
+ return false;
16
+ try {
17
+ const result = await runner(npm, [
18
+ 'ping',
19
+ `--registry=${ALIBABA_NPM_REGISTRY}`,
20
+ '--fetch-retries=0',
21
+ '--fetch-timeout=3000',
22
+ '--loglevel=silent',
23
+ ], {
24
+ capture: true,
25
+ env: prependCommandPath(npm, env),
26
+ timeoutMs: 5_000,
27
+ });
28
+ return result.code === 0;
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ /** Uses the internal registry only as an intranet probe; Ali Skills installs the plugins. */
35
+ export async function installAlibabaInternalPlugins(options = {}, dependencies = {}) {
36
+ const writeLine = options.writeLine ?? (line => console.error(line));
37
+ const registrationFor = dependencies.findPluginRegistration ?? findPluginRegistration;
38
+ const candidates = ALIBABA_INTERNAL_PLUGIN_NAMES.filter(name => {
39
+ const registration = registrationFor(name);
40
+ if (registration && registration.managedBy !== OFFICIAL_PLUGIN_MANAGER) {
41
+ writeLine(`${name} plugin: existing user registration kept.`);
42
+ return false;
43
+ }
44
+ return true;
45
+ });
46
+ if (candidates.length === 0)
47
+ return 'skipped';
48
+ const probe = dependencies.probeAlibabaRegistry ?? (input => canAccessAlibabaNpmRegistry(input));
49
+ if (!(await probe(options)))
50
+ return 'skipped';
51
+ let installed = false;
52
+ let failed = false;
53
+ for (const name of candidates) {
54
+ const definition = findOfficialPlugin(name);
55
+ try {
56
+ const code = await (dependencies.installPlugins ?? installPlugins)([...definition.installArgs], options.runCommand, { registrationManagedBy: OFFICIAL_PLUGIN_MANAGER });
57
+ if (code !== 0) {
58
+ failed = true;
59
+ writeLine(`${name} plugin: install failed (exit ${code}).`);
60
+ continue;
61
+ }
62
+ installed = true;
63
+ writeLine(`${name} plugin: installed and registered.`);
64
+ }
65
+ catch (error) {
66
+ failed = true;
67
+ writeLine(`${name} plugin: ${error instanceof Error ? error.message : String(error)}; skipped.`);
68
+ }
69
+ }
70
+ return failed ? 'failed' : installed ? 'installed' : 'skipped';
71
+ }
72
+ export async function uninstallAlibabaInternalPlugins(options = {}, dependencies = {}) {
73
+ const writeLine = options.writeLine ?? (line => console.error(line));
74
+ const registrationFor = dependencies.findPluginRegistration ?? findPluginRegistration;
75
+ let uninstalled = false;
76
+ let failed = false;
77
+ for (const name of [...ALIBABA_INTERNAL_PLUGIN_NAMES].reverse()) {
78
+ try {
79
+ const registration = registrationFor(name);
80
+ if (registration?.managedBy !== OFFICIAL_PLUGIN_MANAGER) {
81
+ if (registration)
82
+ writeLine(`${name} plugin: existing user registration kept.`);
83
+ continue;
84
+ }
85
+ const result = await (dependencies.uninstallPlugin ?? uninstallPlugin)(name, options.runCommand);
86
+ if (!result.removed)
87
+ continue;
88
+ uninstalled = true;
89
+ writeLine(`${name} plugin: uninstalled.`);
90
+ }
91
+ catch (error) {
92
+ failed = true;
93
+ writeLine(`${name} plugin: ${error instanceof Error ? error.message : String(error)}; uninstall failed.`);
94
+ }
95
+ }
96
+ return failed ? 'failed' : uninstalled ? 'uninstalled' : 'skipped';
97
+ }
@@ -3,6 +3,7 @@ import { detectGlobalPackage, type PackageManagerInstallation } from './packageM
3
3
  import { type DetectedMearlProfiles, type MearlProfile } from './profiles.js';
4
4
  import { uninstallNativeHostArtifacts } from './nativeHost.js';
5
5
  import { type CommandRunner } from './process.js';
6
+ import { installAlibabaInternalPlugins, uninstallAlibabaInternalPlugins } from './internalPlugins.js';
6
7
  export type MearlLifecycleOperation = 'install' | 'update' | 'uninstall';
7
8
  export interface MearlLifecycleOptions {
8
9
  operation: MearlLifecycleOperation;
@@ -21,9 +22,11 @@ export interface MearlLifecycleDependencies {
21
22
  detectPackage?: typeof detectGlobalPackage;
22
23
  detectProfiles?: (env: NodeJS.ProcessEnv) => DetectedMearlProfiles;
23
24
  existsSync?: (filePath: string) => boolean;
25
+ installInternalPlugins?: typeof installAlibabaInternalPlugins;
24
26
  resolveInvokingManager?: (env: NodeJS.ProcessEnv) => PackageManagerInstallation;
25
27
  runCommand?: CommandRunner;
26
28
  runSkillOperation?: typeof runGlobalMearlSkillOperation;
29
+ uninstallInternalPlugins?: typeof uninstallAlibabaInternalPlugins;
27
30
  writeLine?: (line: string) => void;
28
31
  }
29
32
  export declare function runMearlLifecycle(options: MearlLifecycleOptions, dependencies?: MearlLifecycleDependencies): Promise<boolean>;
package/dist/lifecycle.js CHANGED
@@ -8,6 +8,7 @@ import { MEARL_PROFILES, detectMearlProfiles, } from './profiles.js';
8
8
  import { uninstallNativeHostArtifacts } from './nativeHost.js';
9
9
  import { executableNames, prependCommandPath, runCommand, } from './process.js';
10
10
  import { isMearlReleaseLine } from './version.js';
11
+ import { installAlibabaInternalPlugins, uninstallAlibabaInternalPlugins, } from './internalPlugins.js';
11
12
  const LEGACY_CLOUD_CLIENT = '@mearl/cloud-client';
12
13
  const UNIFIED_CLIENT = '@mearl/client';
13
14
  const PROFILE_ORDER = ['local', 'cloud', 'mcp'];
@@ -202,6 +203,9 @@ export async function runMearlLifecycle(options, dependencies = {}) {
202
203
  ? detectPackage(UNIFIED_CLIENT, ['mearl'], { env })
203
204
  : null;
204
205
  const detectedClient = detectedClientCandidate?.packageName === UNIFIED_CLIENT ? detectedClientCandidate : null;
206
+ const remainingClientProfiles = selectedUsesClient && options.operation === 'uninstall'
207
+ ? CLIENT_PROFILES.filter(profile => detected.has(profile) && !selected.includes(profile))
208
+ : [];
205
209
  if (selectedUsesClient && options.operation !== 'uninstall') {
206
210
  const clientInstallation = detectedClient ??
207
211
  detected.get('local') ??
@@ -264,7 +268,6 @@ export async function runMearlLifecycle(options, dependencies = {}) {
264
268
  }
265
269
  }
266
270
  if (selectedUsesClient && options.operation === 'uninstall') {
267
- const remainingClientProfiles = CLIENT_PROFILES.filter(profile => detected.has(profile) && !selected.includes(profile));
268
271
  if (remainingClientProfiles.length > 0) {
269
272
  writeLine(`Packages (shared client): kept for ${remainingClientProfiles.join(', ')}.`);
270
273
  }
@@ -306,7 +309,41 @@ export async function runMearlLifecycle(options, dependencies = {}) {
306
309
  skillOk = false;
307
310
  }
308
311
  }
309
- if (packagesOk && skillOk) {
312
+ let internalPluginsOk = true;
313
+ if (packagesOk && skillOk && selectedUsesClient && options.skipSkill !== true) {
314
+ if (options.operation === 'uninstall') {
315
+ if (remainingClientProfiles.length > 0) {
316
+ writeLine(`Internal plugins: kept for ${remainingClientProfiles.join(', ')}.`);
317
+ }
318
+ else {
319
+ try {
320
+ internalPluginsOk =
321
+ (await (dependencies.uninstallInternalPlugins ?? uninstallAlibabaInternalPlugins)({
322
+ env,
323
+ runCommand: runner,
324
+ writeLine,
325
+ })) !== 'failed';
326
+ }
327
+ catch (error) {
328
+ writeLine(`Internal plugins: ${error instanceof Error ? error.message : String(error)}; uninstall failed.`);
329
+ internalPluginsOk = false;
330
+ }
331
+ }
332
+ }
333
+ else if (options.targetLine === undefined) {
334
+ try {
335
+ await (dependencies.installInternalPlugins ?? installAlibabaInternalPlugins)({
336
+ env,
337
+ runCommand: runner,
338
+ writeLine,
339
+ });
340
+ }
341
+ catch (error) {
342
+ writeLine(`Internal plugins: ${error instanceof Error ? error.message : String(error)}; skipped.`);
343
+ }
344
+ }
345
+ }
346
+ if (packagesOk && skillOk && internalPluginsOk) {
310
347
  writeLine('Mearl setup complete.');
311
348
  return true;
312
349
  }
package/dist/plugin.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type CommandRunner } from './process.js';
2
2
  export declare function setupPlugin(name: string, target: string): Promise<string>;
3
+ export declare function ensureOfficialPlugin(name: string, runner?: CommandRunner): Promise<'existing' | 'installed'>;
3
4
  export interface PluginUninstallResult {
4
5
  entryMissing?: boolean;
5
6
  removed: boolean;
@@ -9,4 +10,6 @@ export interface PluginUninstallResult {
9
10
  export declare function uninstallPlugin(name: string, runner?: CommandRunner): Promise<PluginUninstallResult>;
10
11
  /** Ali Skills 的 Installed 区块列出成功路径;计数校验避免输出变化导致漏注册。 */
11
12
  export declare function parseInstalledPluginPaths(output: string): string[];
12
- export declare function installPlugins(args: string[], runner?: CommandRunner): Promise<number>;
13
+ export declare function installPlugins(args: string[], runner?: CommandRunner, options?: {
14
+ registrationManagedBy?: string;
15
+ }): Promise<number>;
package/dist/plugin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, mkdirSync, rmdirSync, statSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
4
- import { assertPluginName, findPluginEntry, listPluginNames, loadPlugin, registerPlugin, resolvePluginEntry, unregisterPlugin, } from '@mearl/client/plugin';
4
+ import { assertPluginName, findOfficialPlugin, findPluginEntry, findPluginRegistration, listPluginNames, loadPlugin, OFFICIAL_PLUGIN_MANAGER, registerPlugin, resolvePluginEntry, unregisterPlugin, } from '@mearl/client/plugin';
5
5
  import { ensurePluginRuntime } from './pluginRuntime.js';
6
6
  import { resolveAliSkillsInvocation, stripAnsi } from './aliSkills.js';
7
7
  import { findExecutable, prependNodePath, runCommand } from './process.js';
@@ -21,6 +21,22 @@ export async function setupPlugin(name, target) {
21
21
  await ensurePluginRuntime();
22
22
  return registerPlugin(name, entry, plugin.description);
23
23
  }
24
+ export async function ensureOfficialPlugin(name, runner = runCommand) {
25
+ const definition = findOfficialPlugin(name);
26
+ if (!definition)
27
+ throw new Error(`未知官方插件: ${name}`);
28
+ if (findPluginRegistration(name))
29
+ return 'existing';
30
+ const code = await installPlugins([...definition.installArgs], runner, {
31
+ registrationManagedBy: OFFICIAL_PLUGIN_MANAGER,
32
+ });
33
+ if (code !== 0)
34
+ throw new Error(`官方插件 ${name} 安装失败 (exit ${code})`);
35
+ const registration = findPluginRegistration(name);
36
+ if (!registration)
37
+ throw new Error(`官方插件 ${name} 安装完成后仍未注册`);
38
+ return 'installed';
39
+ }
24
40
  function globalAliSkill(entry) {
25
41
  const globalSkillsDirectory = join(homedir(), '.agents', 'skills');
26
42
  const relativeEntry = relative(globalSkillsDirectory, resolve(entry));
@@ -137,7 +153,7 @@ function permitsEmptyInstallResult(args, output) {
137
153
  args.some(argument => ['--help', '-h', '--list'].includes(argument)) ||
138
154
  /\b(?:installation\s+)?cancel(?:led|ed)\b/i.test(output));
139
155
  }
140
- export async function installPlugins(args, runner = runCommand) {
156
+ export async function installPlugins(args, runner = runCommand, options = {}) {
141
157
  if (args.includes('--')) {
142
158
  throw new Error('plugin install 不支持 -- 参数分隔符,以确保全局 symlink 安装选项生效。');
143
159
  }
@@ -178,7 +194,7 @@ export async function installPlugins(args, runner = runCommand) {
178
194
  }
179
195
  await ensurePluginRuntime(runner);
180
196
  for (const [name, { entry, description }] of plugins) {
181
- registerPlugin(name, entry, description);
197
+ registerPlugin(name, entry, description, { managedBy: options.registrationManagedBy });
182
198
  console.error(`Plugin ${name} registered: ${entry}\nRun: mearl ${name} --help`);
183
199
  }
184
200
  return 0;
package/dist/provider.js CHANGED
@@ -11,9 +11,10 @@ export function parseProviderPackageSpec(value) {
11
11
  }
12
12
  return { packageName: nameMatch[1], target: nameMatch[2] || 'latest' };
13
13
  }
14
- function providerManager(packageName, env, dependencies) {
15
- const resolved = (dependencies.resolveInvokingManager ??
16
- (childEnv => resolveInvokingPackageManager({ env: childEnv })))(env);
14
+ function providerManager(packageName, env, dependencies, resolvedManager) {
15
+ const resolved = resolvedManager ??
16
+ (dependencies.resolveInvokingManager ??
17
+ (childEnv => resolveInvokingPackageManager({ env: childEnv })))(env);
17
18
  if (!packageName.startsWith('@ali/') || resolved.command.includes('tnpm'))
18
19
  return resolved;
19
20
  const tnpm = (dependencies.findExecutable ?? findExecutable)('tnpm', {
@@ -96,7 +97,8 @@ export async function updateProviderPackage(selector, dependencies = {}) {
96
97
  const current = findRegisteredProvider(selector, listProviders);
97
98
  const env = process.env;
98
99
  const runner = dependencies.runCommand ?? runCommand;
99
- const installation = (dependencies.resolvePackageManager ?? resolvePackageManagerInstallation)(current.packageRoot, current.packageName) ?? providerManager(current.packageName, env, dependencies);
100
+ const detectedManager = (dependencies.resolvePackageManager ?? resolvePackageManagerInstallation)(current.packageRoot, current.packageName) ?? undefined;
101
+ const installation = providerManager(current.packageName, env, dependencies, detectedManager);
100
102
  (dependencies.stopProvider ?? stopProvider)(current.providerId);
101
103
  await runPackageCommand(installation, 'update', current.packageName, 'latest', runner, env);
102
104
  const manifest = (dependencies.registerPackage ?? registerProviderPackage)(current.packageRoot);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/setup",
3
- "version": "2.14.0",
3
+ "version": "2.15.1",
4
4
  "description": "One-shot environment setup and configuration for Mearl",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,10 +26,6 @@
26
26
  "types": "./dist/nativeHost.d.ts",
27
27
  "import": "./dist/nativeHost.js"
28
28
  },
29
- "./agentbay": {
30
- "types": "./dist/agentBay.d.ts",
31
- "import": "./dist/agentBay.js"
32
- },
33
29
  "./version": {
34
30
  "types": "./dist/version.d.ts",
35
31
  "import": "./dist/version.js"
@@ -65,8 +61,8 @@
65
61
  "commander": "^15.0.0",
66
62
  "extract-zip": "^2.0.1",
67
63
  "ws": "^8.18.3",
68
- "@mearl/client": "2.14.0",
69
- "@mearl/provider": "2.14.0"
64
+ "@mearl/client": "2.15.1",
65
+ "@mearl/provider": "2.15.1"
70
66
  },
71
67
  "devDependencies": {
72
68
  "@types/node": "^24.9.1",
@@ -1,23 +0,0 @@
1
- export interface AgentBayConfigurationOptions {
2
- envFile?: string;
3
- }
4
- export interface AgentBayConfigurationResult {
5
- envFile: string;
6
- updated: boolean;
7
- }
8
- export interface AgentBaySecretInput {
9
- isRaw?: boolean;
10
- isTTY?: boolean;
11
- on(event: 'data', listener: (chunk: Buffer | string) => void): unknown;
12
- pause(): unknown;
13
- removeListener(event: 'data', listener: (chunk: Buffer | string) => void): unknown;
14
- resume(): unknown;
15
- setRawMode?(mode: boolean): unknown;
16
- }
17
- export interface AgentBaySecretOutput {
18
- write(value: string): unknown;
19
- }
20
- export declare function readAgentBayApiKeyFile(file: string): string;
21
- export declare function readAgentBayApiKeyStdin(input?: NodeJS.ReadableStream): Promise<string>;
22
- export declare function promptAgentBayApiKey(input?: AgentBaySecretInput, output?: AgentBaySecretOutput): Promise<string>;
23
- export declare function configureAgentBayApiKey(apiKey: string, options?: AgentBayConfigurationOptions): AgentBayConfigurationResult;
package/dist/agentBay.js DELETED
@@ -1,114 +0,0 @@
1
- import fs from 'node:fs';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- const AGENTBAY_ENV_KEY = 'AGENTBAY_KEY';
5
- const AGENTBAY_ENV_LINE = /^\s*(?:export\s+)?AGENTBAY_KEY\s*=/;
6
- function normalizeApiKey(apiKey) {
7
- const value = apiKey.trim();
8
- if (!value)
9
- throw new Error('AgentBay API key is required.');
10
- if (/[\r\n\0]/.test(value)) {
11
- throw new Error('AgentBay API key must be a single-line value.');
12
- }
13
- return value;
14
- }
15
- export function readAgentBayApiKeyFile(file) {
16
- const resolved = path.resolve(file);
17
- return normalizeApiKey(fs.readFileSync(resolved, 'utf8'));
18
- }
19
- export async function readAgentBayApiKeyStdin(input = process.stdin) {
20
- let value = '';
21
- for await (const chunk of input)
22
- value += chunk.toString();
23
- return normalizeApiKey(value);
24
- }
25
- export function promptAgentBayApiKey(input = process.stdin, output = process.stderr) {
26
- if (!input.isTTY || !input.setRawMode) {
27
- return Promise.reject(new Error('A TTY is required to enter the AgentBay API key securely.'));
28
- }
29
- const setRawMode = input.setRawMode.bind(input);
30
- return new Promise((resolvePrompt, rejectPrompt) => {
31
- const wasRaw = input.isRaw === true;
32
- let value = '';
33
- let settled = false;
34
- const finish = (error) => {
35
- if (settled)
36
- return;
37
- settled = true;
38
- input.removeListener('data', onData);
39
- setRawMode(wasRaw);
40
- input.pause();
41
- output.write('\n');
42
- if (error)
43
- rejectPrompt(error);
44
- else {
45
- try {
46
- resolvePrompt(normalizeApiKey(value));
47
- }
48
- catch (validationError) {
49
- rejectPrompt(validationError instanceof Error ? validationError : new Error(String(validationError)));
50
- }
51
- }
52
- };
53
- const onData = (chunk) => {
54
- for (const character of chunk.toString()) {
55
- if (character === '\u0003') {
56
- finish(new Error('AgentBay API key input cancelled.'));
57
- return;
58
- }
59
- if (character === '\r' || character === '\n') {
60
- finish();
61
- return;
62
- }
63
- if (character === '\u007f' || character === '\b') {
64
- value = Array.from(value).slice(0, -1).join('');
65
- continue;
66
- }
67
- if (character >= ' ')
68
- value += character;
69
- }
70
- };
71
- output.write('AgentBay API key: ');
72
- setRawMode(true);
73
- input.resume();
74
- input.on('data', onData);
75
- });
76
- }
77
- function updateEnvContent(content, apiKey) {
78
- const newline = content.includes('\r\n') ? '\r\n' : '\n';
79
- const lines = content ? content.split(/\r?\n/) : [];
80
- if (lines.at(-1) === '')
81
- lines.pop();
82
- const entry = `${AGENTBAY_ENV_KEY}=${apiKey}`;
83
- const output = [];
84
- let updated = false;
85
- for (const line of lines) {
86
- if (!AGENTBAY_ENV_LINE.test(line)) {
87
- output.push(line);
88
- continue;
89
- }
90
- if (!updated)
91
- output.push(entry);
92
- updated = true;
93
- }
94
- if (!updated)
95
- output.push(entry);
96
- return { content: `${output.join(newline)}${newline}`, updated };
97
- }
98
- export function configureAgentBayApiKey(apiKey, options = {}) {
99
- const value = normalizeApiKey(apiKey);
100
- const envFile = options.envFile ?? path.join(os.homedir(), '.mearl', '.env');
101
- let current = '';
102
- try {
103
- current = fs.readFileSync(envFile, 'utf8');
104
- }
105
- catch (error) {
106
- if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT'))
107
- throw error;
108
- }
109
- const next = updateEnvContent(current, value);
110
- fs.mkdirSync(path.dirname(envFile), { recursive: true, mode: 0o700 });
111
- fs.writeFileSync(envFile, next.content, { encoding: 'utf8', mode: 0o600 });
112
- fs.chmodSync(envFile, 0o600);
113
- return { envFile, updated: next.updated };
114
- }