Sign In

@mearl/setup

Package Overview
Dependencies
Maintainers
2
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mearl/setup - npm Package Compare versions

Comparing version
2.3.0
to
2.4.0
+21
dist/version.d.ts
export declare const MEARL_RELEASE_VERSION_MAX_LENGTH = 64;
export interface MearlReleaseVersion {
raw: string;
major: number;
minor: number;
patch: number;
channel: 'stable' | 'beta';
beta?: number;
}
export interface MearlReleaseLine {
raw: string;
major: number;
minor: number;
}
export declare function parseMearlReleaseVersion(value: unknown): MearlReleaseVersion | null;
export declare function isMearlReleaseVersion(value: unknown): value is string;
export declare function parseMearlReleaseLine(value: unknown): MearlReleaseLine | null;
export declare function isMearlReleaseLine(value: unknown): value is string;
export declare function mearlReleaseLineForVersion(version: string): string;
export declare function isMearlReleaseVersionInLine(version: string, releaseLine: string): boolean;
export declare function compareMearlReleaseLines(leftVersion: string, rightVersion: string): -1 | 0 | 1;
export const MEARL_RELEASE_VERSION_MAX_LENGTH = 64;
const RELEASE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-beta\.(0|[1-9]\d*))?$/;
const RELEASE_LINE_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.x$/;
const RELEASE_COMPONENT_MAXIMUM = 65_535;
function parseComponent(value) {
const component = Number(value);
return Number.isSafeInteger(component) && component <= RELEASE_COMPONENT_MAXIMUM
? component
: null;
}
export function parseMearlReleaseVersion(value) {
if (typeof value !== 'string' || value.length > MEARL_RELEASE_VERSION_MAX_LENGTH)
return null;
const match = RELEASE_VERSION_PATTERN.exec(value);
if (!match)
return null;
const major = parseComponent(match[1]);
const minor = parseComponent(match[2]);
const patch = parseComponent(match[3]);
const beta = match[4] === undefined ? undefined : parseComponent(match[4]);
if (major === null || minor === null || patch === null || beta === null)
return null;
return {
raw: value,
major,
minor,
patch,
channel: beta === undefined ? 'stable' : 'beta',
...(beta === undefined ? {} : { beta }),
};
}
export function isMearlReleaseVersion(value) {
return parseMearlReleaseVersion(value) !== null;
}
export function parseMearlReleaseLine(value) {
if (typeof value !== 'string' || value.length > MEARL_RELEASE_VERSION_MAX_LENGTH)
return null;
const match = RELEASE_LINE_PATTERN.exec(value);
if (!match)
return null;
const major = parseComponent(match[1]);
const minor = parseComponent(match[2]);
if (major === null || minor === null)
return null;
return { raw: value, major, minor };
}
export function isMearlReleaseLine(value) {
return parseMearlReleaseLine(value) !== null;
}
export function mearlReleaseLineForVersion(version) {
const parsed = parseMearlReleaseVersion(version);
if (!parsed)
throw new Error('A valid Mearl release version is required');
return `${parsed.major}.${parsed.minor}.x`;
}
export function isMearlReleaseVersionInLine(version, releaseLine) {
const parsedVersion = parseMearlReleaseVersion(version);
const parsedLine = parseMearlReleaseLine(releaseLine);
return (parsedVersion !== null &&
parsedLine !== null &&
parsedVersion.major === parsedLine.major &&
parsedVersion.minor === parsedLine.minor);
}
export function compareMearlReleaseLines(leftVersion, rightVersion) {
const left = parseMearlReleaseVersion(leftVersion);
const right = parseMearlReleaseVersion(rightVersion);
if (!left || !right) {
throw new Error('Mearl release-line comparison requires valid release versions');
}
for (const key of ['major', 'minor']) {
if (left[key] < right[key])
return -1;
if (left[key] > right[key])
return 1;
}
return 0;
}
+2
-0

@@ -8,2 +8,4 @@ #!/usr/bin/env node

profile?: MearlProfile;
skipSkill: boolean;
targetLine?: string;
yes: boolean;

@@ -10,0 +12,0 @@ }

@@ -5,5 +5,6 @@ #!/usr/bin/env node

import { runMearlLifecycle } from './lifecycle.js';
import { isMearlReleaseLine } from './version.js';
export function parseSetupArgs(argv) {
if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {
return { help: true, yes: false };
return { help: true, skipSkill: false, yes: false };
}

@@ -15,4 +16,7 @@ const operation = argv[0];

let profile;
let skipSkill = false;
let targetLine;
let yes = false;
for (const arg of argv.slice(1)) {
for (let index = 1; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === '--yes' || arg === '-y' || arg === '--non-interactive') {

@@ -22,2 +26,15 @@ yes = true;

}
if (arg === '--skip-skill') {
skipSkill = true;
continue;
}
if (arg === '--target-line') {
const value = argv[index + 1];
if (!isMearlReleaseLine(value)) {
throw new Error('--target-line requires a valid Mearl release line such as 2.3.x');
}
targetLine = value;
index += 1;
continue;
}
const candidate = arg === '--local'

@@ -37,3 +54,6 @@ ? 'local'

}
return { help: false, operation, profile, yes };
if (operation === 'uninstall' && targetLine) {
throw new Error('--target-line is not available for uninstall');
}
return { help: false, operation, profile, skipSkill, targetLine, yes };
}

@@ -55,2 +75,5 @@ function printHelp() {

--mcp Manage @mearl/mcp-server and its configuration
--target-line <major.minor.x>
Install the latest packages from one release line, such as 2.3.x
--skip-skill Keep the independently managed Mearl Skill unchanged
--yes, -y Run without prompts and accept detected defaults

@@ -66,2 +89,3 @@ --non-interactive

npx @mearl/setup update
npx @mearl/setup update --local --target-line 2.3.x --skip-skill
npx @mearl/setup uninstall --local`);

@@ -91,2 +115,4 @@ }

profile: parsed.profile,
skipSkill: parsed.skipSkill,
targetLine: parsed.targetLine,
yes: parsed.yes,

@@ -93,0 +119,0 @@ });

@@ -8,2 +8,4 @@ export { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';

export { MEARL_NATIVE_HOST_NAMES, resolveNativeHostDirectory, uninstallNativeHostArtifacts, } from './nativeHost.js';
export { compareMearlReleaseLines, isMearlReleaseLine, isMearlReleaseVersion, isMearlReleaseVersionInLine, mearlReleaseLineForVersion, parseMearlReleaseLine, parseMearlReleaseVersion, } from './version.js';
export type { MearlReleaseLine, MearlReleaseVersion } from './version.js';
export type { NativeHostCleanupDependencies, NativeHostCleanupOptions, NativeHostIntegrationMode, } from './nativeHost.js';

@@ -5,1 +5,2 @@ export { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';

export { MEARL_NATIVE_HOST_NAMES, resolveNativeHostDirectory, uninstallNativeHostArtifacts, } from './nativeHost.js';
export { compareMearlReleaseLines, isMearlReleaseLine, isMearlReleaseVersion, isMearlReleaseVersionInLine, mearlReleaseLineForVersion, parseMearlReleaseLine, parseMearlReleaseVersion, } from './version.js';

@@ -11,2 +11,4 @@ import { runGlobalMearlSkillOperation } from './aliSkills.js';

yes?: boolean;
targetLine?: string;
skipSkill?: boolean;
env?: NodeJS.ProcessEnv;

@@ -13,0 +15,0 @@ }

+6
-2

@@ -10,2 +10,3 @@ import fs from 'node:fs';

import { executableNames, prependCommandPath, runCommand, } from './process.js';
import { isMearlReleaseLine } from './version.js';
async function confirmInTerminal(message) {

@@ -89,2 +90,5 @@ if (!input.isTTY || !output.isTTY)

export async function runMearlLifecycle(options, dependencies = {}) {
if (options.targetLine && !isMearlReleaseLine(options.targetLine)) {
throw new Error('targetLine must be a valid Mearl release line such as 2.3.x');
}
const env = options.env ?? process.env;

@@ -163,3 +167,3 @@ const writeLine = dependencies.writeLine ?? (line => console.error(line));

const packageOperation = options.operation === 'uninstall' ? 'uninstall' : options.operation;
const command = buildGlobalPackageCommand(installation, packageOperation, config.packages);
const command = buildGlobalPackageCommand(installation, packageOperation, config.packages, options.targetLine);
writeLine(`Packages (${profile}): ${command.manager}`);

@@ -188,3 +192,3 @@ const result = await runLifecycleCommand(runner, command.command, command.args, env, `Packages (${profile})`, writeLine);

const selectedUsesSkill = selected.some(profile => MEARL_PROFILES[profile].usesSkill);
if (packagesOk && selectedUsesSkill) {
if (packagesOk && selectedUsesSkill && options.skipSkill !== true) {
try {

@@ -191,0 +195,0 @@ if (options.operation === 'uninstall') {

@@ -6,4 +6,8 @@ export type GlobalPackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'volta';

command: string;
/** Directory containing globally installed package executables. */
binDirectory?: string;
}
export interface GlobalPackageCommand extends PackageManagerInstallation {
export interface GlobalPackageCommand {
manager: GlobalPackageManager;
command: string;
args: string[];

@@ -25,3 +29,3 @@ }

export declare function resolvePackageManagerInstallation(packageRoot: string, packageName: string, options?: PackageDetectionOptions): PackageManagerInstallation | null;
export declare function buildGlobalPackageCommand(installation: PackageManagerInstallation, operation: PackageOperation, packages: string[]): GlobalPackageCommand;
export declare function buildGlobalPackageCommand(installation: PackageManagerInstallation, operation: PackageOperation, packages: string[], targetSelector?: string): GlobalPackageCommand;
export declare function resolveInvokingPackageManager(options?: {

@@ -28,0 +32,0 @@ env?: NodeJS.ProcessEnv;

@@ -32,3 +32,3 @@ import fs from 'node:fs';

], existsSync) ?? fallbackCommand('pnpm', platform);
return { manager: 'pnpm', command };
return { manager: 'pnpm', command, binDirectory: pnpmHome };
}

@@ -39,3 +39,7 @@ const bunMarker = `/.bun/install/global/node_modules/${lowerPackageName}`;

const command = firstExisting(executableNames('bun', platform).map(name => path.join(bunHome, '.bun', 'bin', name)), existsSync) ?? fallbackCommand('bun', platform);
return { manager: 'bun', command };
return {
manager: 'bun',
command,
binDirectory: path.join(bunHome, '.bun', 'bin'),
};
}

@@ -47,3 +51,17 @@ if ((lowerRoot.includes('/.config/yarn/global/node_modules/') ||

lowerRoot.endsWith(`/node_modules/${lowerPackageName}`)) {
return { manager: 'yarn', command: fallbackCommand('yarn', platform) };
const layout = [
{ marker: '/.config/yarn/global/', binSegments: ['.yarn', 'bin'] },
{ marker: '/.yarn/global/', binSegments: ['.yarn', 'bin'] },
{ marker: '/yarn/data/global/', binSegments: ['yarn', 'bin'] },
{ marker: '/yarn/global/', binSegments: ['yarn', 'bin'] },
].find(candidate => lowerRoot.includes(candidate.marker));
const prefix = normalizedRoot.slice(0, lowerRoot.indexOf(layout.marker));
const binDirectory = path.join(prefix, ...layout.binSegments);
const command = firstExisting([
...executableNames('yarn', platform).map(name => path.join(binDirectory, name)),
...executableNames('yarn', platform).map(name => path.join(path.dirname(processExecPath), name)),
], existsSync) ??
findExecutable('yarn', { env, existsSync, platform }) ??
fallbackCommand('yarn', platform);
return { manager: 'yarn', command, binDirectory };
}

@@ -54,3 +72,7 @@ if (lowerRoot.includes('/.volta/tools/image/packages/') &&

const command = firstExisting(executableNames('volta', platform).map(name => path.join(voltaHome, '.volta', 'bin', name)), existsSync) ?? fallbackCommand('volta', platform);
return { manager: 'volta', command };
return {
manager: 'volta',
command,
binDirectory: path.join(voltaHome, '.volta', 'bin'),
};
}

@@ -64,3 +86,3 @@ const npmMarker = `/lib/node_modules/${lowerPackageName}`;

], existsSync) ?? fallbackCommand('npm', platform);
return { manager: 'npm', command };
return { manager: 'npm', command, binDirectory: path.join(prefix, 'bin') };
}

@@ -70,3 +92,8 @@ if (platform === 'win32' && env.APPDATA) {

if (lowerRoot === npmRoot.toLowerCase()) {
return { manager: 'npm', command: fallbackCommand('npm', platform) };
const binDirectory = path.join(env.APPDATA, 'npm');
const command = firstExisting([
...executableNames('npm', platform).map(name => path.join(binDirectory, name)),
...executableNames('npm', platform).map(name => path.join(path.dirname(processExecPath), name)),
], existsSync) ?? fallbackCommand('npm', platform);
return { manager: 'npm', command, binDirectory };
}

@@ -76,4 +103,6 @@ }

}
export function buildGlobalPackageCommand(installation, operation, packages) {
const targets = operation === 'uninstall' ? packages : packages.map(packageName => `${packageName}@latest`);
export function buildGlobalPackageCommand(installation, operation, packages, targetSelector = 'latest') {
const targets = operation === 'uninstall'
? packages
: packages.map(packageName => `${packageName}@${targetSelector}`);
let args;

@@ -101,3 +130,3 @@ switch (installation.manager) {

}
return { ...installation, args };
return { manager: installation.manager, command: installation.command, args };
}

@@ -205,5 +234,11 @@ export function resolveInvokingPackageManager(options = {}) {

continue;
return { packageName, packageRoot, executable, ...installation };
return {
packageName,
packageRoot,
executable,
...installation,
binDirectory: path.dirname(executable),
};
}
return null;
}

@@ -29,2 +29,3 @@ import fs from 'node:fs';

export declare function prependNodePath(env?: NodeJS.ProcessEnv, nodeExecutable?: string): NodeJS.ProcessEnv;
export declare function prependPathDirectory(directory: string, env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
export declare function findExecutable(name: string, options?: {

@@ -31,0 +32,0 @@ env?: NodeJS.ProcessEnv;

@@ -60,8 +60,8 @@ import fs from 'node:fs';

return env;
return prependPath(path.dirname(command), env);
return prependPathDirectory(path.dirname(command), env);
}
export function prependNodePath(env = process.env, nodeExecutable = process.execPath) {
return prependPath(path.dirname(nodeExecutable), env);
return prependPathDirectory(path.dirname(nodeExecutable), env);
}
function prependPath(directory, env) {
export function prependPathDirectory(directory, env = process.env) {
const pathKey = Object.keys(env).find(key => key.toLowerCase() === 'path') || 'PATH';

@@ -68,0 +68,0 @@ const inherited = env[pathKey] || '';

{
"name": "@mearl/setup",
"version": "2.3.0",
"version": "2.4.0",
"description": "One-shot installer, updater, and uninstaller for Mearl environments",

@@ -21,5 +21,13 @@ "type": "module",

},
"./package-manager": {
"types": "./dist/packageManager.d.ts",
"import": "./dist/packageManager.js"
},
"./native-host": {
"types": "./dist/nativeHost.d.ts",
"import": "./dist/nativeHost.js"
},
"./version": {
"types": "./dist/version.d.ts",
"import": "./dist/version.js"
}

@@ -26,0 +34,0 @@ },

@@ -21,2 +21,14 @@ # @mearl/setup

## 跟随浏览器扩展自动更新
浏览器扩展与 Native Host 首次建联时会交换版本。每次扩展后台启动只检查一次;当扩展
进入更高的 `major.minor` release line 时,旧 Host 会在保持当前连接可用的前提下,调用
对应 `major.minor.x` 范围内最新的 `@mearl/setup`,把 `@mearl/native-host` 与
`@mearl/client` 一起更新到该范围内的最新版本。成功后 Host 退出,扩展通过已有重连机制
启动新版本。只有两个本地 CLI 版本一致且都属于扩展的 release line 时,更新才会被视为成功。
自动更新不会降级较新的 Host,也不会改动 cloud、MCP 或独立安装的 Mearl Skill。
目标 npm 版本尚未发布、网络不可用或更新失败时,旧 Host 会继续运行;下一次扩展后台
生命周期可重新尝试,也可手动执行 `npx @mearl/setup update --local`。
## 人工与 Agent 使用

@@ -23,0 +35,0 @@