@mearl/setup
Advanced tools
+3
-1
@@ -5,2 +5,3 @@ #!/usr/bin/env node | ||
| import type { MearlProfile } from './profiles.js'; | ||
| export declare const SETUP_VERSION: string; | ||
| interface ParsedArgs { | ||
@@ -11,5 +12,6 @@ agentBayKeyFile?: string; | ||
| operation?: MearlLifecycleOperation | 'agentbay'; | ||
| profile?: MearlProfile; | ||
| profiles?: MearlProfile[]; | ||
| skipSkill: boolean; | ||
| targetLine?: string; | ||
| version?: boolean; | ||
| yes: boolean; | ||
@@ -16,0 +18,0 @@ } |
+43
-17
| #!/usr/bin/env node | ||
| import path from 'node:path'; | ||
| import { createRequire } from 'node:module'; | ||
| import { fileURLToPath } from 'node:url'; | ||
@@ -7,7 +8,20 @@ import { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js'; | ||
| import { isMearlReleaseLine } from './version.js'; | ||
| const require = createRequire(import.meta.url); | ||
| export const SETUP_VERSION = require('../package.json').version; | ||
| export function parseSetupArgs(argv) { | ||
| if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) { | ||
| if (argv.includes('--help') || argv.includes('-h')) { | ||
| return { agentBayReadStdin: false, help: true, skipSkill: false, yes: false }; | ||
| } | ||
| const operation = argv[0]; | ||
| if (argv.includes('--version') || argv.includes('-v')) { | ||
| return { | ||
| agentBayReadStdin: false, | ||
| help: false, | ||
| skipSkill: false, | ||
| version: true, | ||
| yes: false, | ||
| }; | ||
| } | ||
| const firstArg = argv[0]; | ||
| const operation = firstArg === undefined || firstArg.startsWith('-') ? 'install' : firstArg; | ||
| const optionStart = operation === firstArg ? 1 : 0; | ||
| if (operation === 'agentbay') { | ||
@@ -47,7 +61,7 @@ if (argv.length === 1) { | ||
| } | ||
| let profile; | ||
| const profiles = []; | ||
| let skipSkill = false; | ||
| let targetLine; | ||
| let yes = false; | ||
| for (let index = 1; index < argv.length; index += 1) { | ||
| for (let index = optionStart; index < argv.length; index += 1) { | ||
| const arg = argv[index]; | ||
@@ -80,6 +94,4 @@ if (arg === '--yes' || arg === '-y' || arg === '--non-interactive') { | ||
| throw new Error(`Unknown option: ${arg}`); | ||
| if (profile && profile !== candidate) { | ||
| throw new Error('Choose only one of --local, --cloud, or --mcp'); | ||
| } | ||
| profile = candidate; | ||
| if (!profiles.includes(candidate)) | ||
| profiles.push(candidate); | ||
| } | ||
@@ -93,3 +105,3 @@ if (operation === 'uninstall' && targetLine) { | ||
| operation, | ||
| profile, | ||
| ...(profiles.length > 0 ? { profiles } : {}), | ||
| skipSkill, | ||
@@ -100,11 +112,12 @@ targetLine, | ||
| } | ||
| function printHelp() { | ||
| console.error(`Mearl Setup | ||
| function printHelp(toError = false) { | ||
| const output = `Mearl Setup v${SETUP_VERSION} | ||
| Usage: | ||
| npx @mearl/setup <install|update|uninstall> [--local|--cloud|--mcp] [--yes] | ||
| npx @mearl/setup [install] [--local] [--cloud] [--mcp] [--yes] | ||
| npx @mearl/setup <update|uninstall> [--local] [--cloud] [--mcp] [--yes] | ||
| npx @mearl/setup agentbay [--stdin | --key-file <path>] | ||
| Behavior: | ||
| install Defaults to --local when no environment is specified | ||
| install Default command; defaults to --local when no environment is specified | ||
| update Auto-detects installed environments from the current PATH | ||
@@ -116,3 +129,3 @@ uninstall Auto-detects installed environments and asks for confirmation | ||
| --local Manage @mearl/native-host and @mearl/client | ||
| --cloud Manage @mearl/cloud-server and @mearl/cloud-client | ||
| --cloud Manage @mearl/cloud-server and the unified @mearl/client | ||
| --mcp Manage @mearl/mcp-server and its configuration | ||
@@ -127,12 +140,21 @@ --stdin Read the AgentBay API key from standard input | ||
| Alias for --yes, intended for Agents and CI | ||
| --version, -v | ||
| Show version | ||
| --help, -h Show this help | ||
| Examples: | ||
| npx @mearl/setup | ||
| npx @mearl/setup --yes | ||
| npx @mearl/setup install | ||
| npx @mearl/setup install --yes | ||
| npx @mearl/setup install --cloud | ||
| npx @mearl/setup install --local --cloud | ||
| npx @mearl/setup update | ||
| npx @mearl/setup agentbay | ||
| npx @mearl/setup update --local --target-line 2.3.x --skip-skill | ||
| npx @mearl/setup uninstall --local`); | ||
| npx @mearl/setup uninstall --local`; | ||
| if (toError) | ||
| console.error(output); | ||
| else | ||
| console.log(output); | ||
| } | ||
@@ -146,3 +168,3 @@ export async function main(argv = process.argv.slice(2), interactiveTerminal = process.stdin.isTTY === true && process.stdout.isTTY === true, dependencies = {}) { | ||
| console.error(error instanceof Error ? error.message : String(error)); | ||
| printHelp(); | ||
| printHelp(true); | ||
| return 2; | ||
@@ -154,2 +176,6 @@ } | ||
| } | ||
| if (parsed.version) { | ||
| console.log(SETUP_VERSION); | ||
| return 0; | ||
| } | ||
| if (parsed.operation === 'agentbay') { | ||
@@ -187,3 +213,3 @@ try { | ||
| operation: parsed.operation, | ||
| profile: parsed.profile, | ||
| profiles: parsed.profiles, | ||
| skipSkill: parsed.skipSkill, | ||
@@ -190,0 +216,0 @@ targetLine: parsed.targetLine, |
@@ -9,3 +9,5 @@ import { runGlobalMearlSkillOperation } from './aliSkills.js'; | ||
| operation: MearlLifecycleOperation; | ||
| /** @deprecated Use profiles when selecting one or more composable environments. */ | ||
| profile?: MearlProfile; | ||
| profiles?: MearlProfile[]; | ||
| yes?: boolean; | ||
@@ -12,0 +14,0 @@ targetLine?: string; |
+72
-13
@@ -11,2 +11,6 @@ import fs from 'node:fs'; | ||
| import { isMearlReleaseLine } from './version.js'; | ||
| const LEGACY_CLOUD_CLIENT = '@mearl/cloud-client'; | ||
| const UNIFIED_CLIENT = '@mearl/client'; | ||
| const PROFILE_ORDER = ['local', 'cloud', 'mcp']; | ||
| const CLIENT_PROFILES = ['local', 'cloud']; | ||
| async function confirmInTerminal(message) { | ||
@@ -44,2 +48,14 @@ if (!input.isTTY || !output.isTTY) | ||
| } | ||
| async function runPackageOperation(installation, operation, packages, targetLine, runner, env, label, writeLine) { | ||
| const command = buildGlobalPackageCommand(installation, operation, packages, targetLine); | ||
| writeLine(`${label}: ${command.manager}`); | ||
| const result = await runLifecycleCommand(runner, command.command, command.args, env, label, writeLine); | ||
| if (!result) | ||
| return { command, ok: false }; | ||
| if (result.code !== 0) { | ||
| writeLine(`${label}: failed (exit ${result.code}).`); | ||
| return { command, ok: false }; | ||
| } | ||
| return { command, ok: true }; | ||
| } | ||
| async function runHook(profile, operation, detected, runner, existsSync, env, writeLine, yes) { | ||
@@ -106,7 +122,9 @@ let executable; | ||
| function selectedProfiles(operation, requested, detected) { | ||
| if (requested) | ||
| return [requested]; | ||
| if (requested?.length) { | ||
| const requestedSet = new Set(requested); | ||
| return PROFILE_ORDER.filter(profile => requestedSet.has(profile)); | ||
| } | ||
| if (operation === 'install') | ||
| return ['local']; | ||
| return [...detected.keys()]; | ||
| return PROFILE_ORDER.filter(profile => detected.has(profile)); | ||
| } | ||
@@ -132,3 +150,4 @@ function operationVerb(operation) { | ||
| const detected = detectProfiles(env); | ||
| const selected = selectedProfiles(options.operation, options.profile, detected); | ||
| const requestedProfiles = options.profiles ?? (options.profile ? [options.profile] : undefined); | ||
| const selected = selectedProfiles(options.operation, requestedProfiles, detected); | ||
| if (selected.length === 0) { | ||
@@ -164,2 +183,34 @@ writeLine('No installed Mearl environment was detected on the current PATH.'); | ||
| let packagesOk = true; | ||
| if (selected.includes('cloud')) { | ||
| const legacyCloudClient = detectPackage(LEGACY_CLOUD_CLIENT, ['mearl-cloud'], { env }); | ||
| if (legacyCloudClient?.packageName === LEGACY_CLOUD_CLIENT) { | ||
| const command = buildGlobalPackageCommand(legacyCloudClient, 'uninstall', [ | ||
| LEGACY_CLOUD_CLIENT, | ||
| ]); | ||
| writeLine(`Migration (cloud): removing ${LEGACY_CLOUD_CLIENT}.`); | ||
| const result = await runLifecycleCommand(runner, command.command, command.args, env, 'Migration (cloud)', writeLine); | ||
| if (!result || result.code !== 0) { | ||
| if (result) | ||
| writeLine(`Migration (cloud): failed (exit ${result.code}).`); | ||
| writeLine('Mearl setup incomplete. Review the messages above.'); | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| const selectedUsesClient = selected.some(profile => CLIENT_PROFILES.includes(profile)); | ||
| const detectedClientCandidate = selectedUsesClient | ||
| ? detectPackage(UNIFIED_CLIENT, ['mearl'], { env }) | ||
| : null; | ||
| const detectedClient = detectedClientCandidate?.packageName === UNIFIED_CLIENT ? detectedClientCandidate : null; | ||
| if (selectedUsesClient && options.operation !== 'uninstall') { | ||
| const clientInstallation = detectedClient ?? | ||
| detected.get('local') ?? | ||
| detected.get('cloud') ?? | ||
| resolveInvokingManager(env); | ||
| const sharedClient = await runPackageOperation(clientInstallation, options.operation, [UNIFIED_CLIENT], options.targetLine, runner, env, 'Packages (shared client)', writeLine); | ||
| if (!sharedClient.ok) { | ||
| writeLine('Mearl setup incomplete. Review the messages above.'); | ||
| return false; | ||
| } | ||
| } | ||
| for (const profile of selected) { | ||
@@ -194,12 +245,9 @@ const config = MEARL_PROFILES[profile]; | ||
| } | ||
| const packageOperation = options.operation === 'uninstall' ? 'uninstall' : options.operation; | ||
| const command = buildGlobalPackageCommand(installation, packageOperation, config.packages, options.targetLine); | ||
| writeLine(`Packages (${profile}): ${command.manager}`); | ||
| const result = await runLifecycleCommand(runner, command.command, command.args, env, `Packages (${profile})`, writeLine); | ||
| if (!result) { | ||
| packagesOk = false; | ||
| const packageOperation = options.operation; | ||
| const packages = config.packages.filter(packageName => packageName !== UNIFIED_CLIENT); | ||
| if (packages.length === 0) { | ||
| continue; | ||
| } | ||
| if (result.code !== 0) { | ||
| writeLine(`Packages (${profile}): failed (exit ${result.code}).`); | ||
| const packageResult = await runPackageOperation(installation, packageOperation, packages, options.targetLine, runner, env, `Packages (${profile})`, writeLine); | ||
| if (!packageResult.ok) { | ||
| packagesOk = false; | ||
@@ -211,3 +259,3 @@ continue; | ||
| const refreshed = detectPackage(config.anchorPackage, config.anchorBins, { | ||
| env: prependCommandPath(command.command, env), | ||
| env: prependCommandPath(packageResult.command.command, env), | ||
| }) ?? current; | ||
@@ -220,2 +268,13 @@ if (!refreshed || | ||
| } | ||
| if (selectedUsesClient && options.operation === 'uninstall') { | ||
| const remainingClientProfiles = CLIENT_PROFILES.filter(profile => detected.has(profile) && !selected.includes(profile)); | ||
| if (remainingClientProfiles.length > 0) { | ||
| writeLine(`Packages (shared client): kept for ${remainingClientProfiles.join(', ')}.`); | ||
| } | ||
| else if (packagesOk && detectedClient) { | ||
| const sharedClient = await runPackageOperation(detectedClient, 'uninstall', [UNIFIED_CLIENT], undefined, runner, env, 'Packages (shared client)', writeLine); | ||
| if (!sharedClient.ok) | ||
| packagesOk = false; | ||
| } | ||
| } | ||
| let skillOk = true; | ||
@@ -222,0 +281,0 @@ const selectedUsesSkill = selected.some(profile => MEARL_PROFILES[profile].usesSkill); |
+3
-3
@@ -14,5 +14,5 @@ import { detectGlobalPackage } from './packageManager.js'; | ||
| label: 'cloud agent', | ||
| anchorPackage: '@mearl/cloud-client', | ||
| anchorBins: ['mearl-cloud'], | ||
| packages: ['@mearl/cloud-server', '@mearl/cloud-client'], | ||
| anchorPackage: '@mearl/cloud-server', | ||
| anchorBins: ['mearl-cloud-server'], | ||
| packages: ['@mearl/cloud-server', '@mearl/client'], | ||
| usesSkill: true, | ||
@@ -19,0 +19,0 @@ }, |
+1
-1
| { | ||
| "name": "@mearl/setup", | ||
| "version": "2.5.1", | ||
| "version": "2.6.0", | ||
| "description": "One-shot environment setup and configuration for Mearl", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+16
-4
@@ -6,3 +6,4 @@ # @mearl/setup | ||
| ```bash | ||
| npx @mearl/setup <install|update|uninstall> [--local|--cloud|--mcp] [--yes] | ||
| npx @mearl/setup [install] [--local] [--cloud] [--mcp] [--yes] | ||
| npx @mearl/setup <update|uninstall> [--local] [--cloud] [--mcp] [--yes] | ||
| ``` | ||
@@ -22,10 +23,15 @@ | ||
| - `install` 可以省略;直接执行 `npx @mearl/setup` 等同于 `npx @mearl/setup install`。 | ||
| 以选项开头时也默认安装,例如 `npx @mearl/setup --cloud --yes`。 | ||
| - `install` 未指定环境时默认 `--local`。 | ||
| - `-h` / `--help` 显示帮助,`-v` / `--version` 显示 setup 版本。 | ||
| - `update` 和 `uninstall` 未指定环境时,从当前 `PATH` 中的 | ||
| `mearl-native-host`、`mearl-cloud`、`mearl-mcp-server` 自动检测,可同时处理 | ||
| `mearl-native-host`、`mearl-cloud-server`、`mearl-mcp-server` 自动检测,可同时处理 | ||
| 多个已安装环境。 | ||
| - 显式传入 `--local`、`--cloud` 或 `--mcp` 时只处理指定环境。 | ||
| - `--local`、`--cloud` 与 `--mcp` 可以组合,显式传入时只处理选中的环境。 | ||
| - 已有环境沿用其原包管理器和全局安装前缀;首次安装沿用启动 setup 的包管理器。 | ||
| - cloud 环境统一管理 `@mearl/cloud-server` 与 `@mearl/cloud-client`:首次安装后启动 | ||
| - cloud 环境统一管理 `@mearl/cloud-server` 与 `@mearl/client`:首次安装后启动 | ||
| Server,更新后仅在 Server 原本运行时重启,卸载前停止。 | ||
| - local/cloud 共用一份 `@mearl/client`;组合安装只处理一次,卸载单个环境时若另一个环境仍在使用则保留。 | ||
| - 从 2.5.x 更新 cloud 环境时会先卸载旧 `@mearl/cloud-client`,避免它遗留的 `mearl` bin 与统一 client 冲突。 | ||
| - local/cloud 共用的全局 mearl skill 通过 `ali-skills` 的 JSON 清单定位,只处理一次。 | ||
@@ -60,2 +66,5 @@ - 卸载一个环境但另一个仍依赖全局 mearl skill 时会保留 skill。 | ||
| # 默认安装本地浏览器环境 | ||
| npx @mearl/setup | ||
| # 与上一条等价 | ||
| npx @mearl/setup install | ||
@@ -69,2 +78,5 @@ | ||
| # 同一台机器同时作为本地浏览器宿主和云端 Agent | ||
| npx @mearl/setup install --local --cloud | ||
| # 自动更新当前 PATH 中检测到的全部 Mearl 环境 | ||
@@ -71,0 +83,0 @@ npx @mearl/setup update |
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
76876
7.15%1649
5.71%95
14.46%26
4%