@mearl/setup 2.3.0 → 2.5.0
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 +31 -1
- package/dist/agentBay.d.ts +23 -0
- package/dist/agentBay.js +114 -0
- package/dist/cli.d.ts +13 -2
- package/dist/cli.js +101 -5
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/lifecycle.d.ts +2 -0
- package/dist/lifecycle.js +35 -5
- package/dist/packageManager.d.ts +6 -2
- package/dist/packageManager.js +45 -10
- package/dist/process.d.ts +1 -0
- package/dist/process.js +3 -3
- package/dist/profiles.js +1 -1
- package/dist/version.d.ts +21 -0
- package/dist/version.js +77 -0
- package/package.json +14 -2
package/README.md
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
# @mearl/setup
|
|
2
2
|
|
|
3
|
-
Mearl
|
|
3
|
+
Mearl 环境的一次性安装、更新、卸载与配置工具。无需先全局安装 setup:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npx @mearl/setup <install|update|uninstall> [--local|--cloud|--mcp] [--yes]
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
+
配置 AgentBay API Key 时只需执行一次下面的命令,然后在隐藏输入提示中粘贴 Key。
|
|
10
|
+
重复执行会更新现有 Key,并保留 `~/.mearl/.env` 中的其他配置:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx @mearl/setup agentbay
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
无交互环境可使用 `--stdin` 或 `--key-file <path>`。为避免 Key 进入 shell history 和
|
|
17
|
+
进程参数,不接受把 Key 直接写在命令行中。
|
|
18
|
+
|
|
9
19
|
## 行为
|
|
10
20
|
|
|
11
21
|
- `install` 未指定环境时默认 `--local`。
|
|
@@ -14,10 +24,24 @@ npx @mearl/setup <install|update|uninstall> [--local|--cloud|--mcp] [--yes]
|
|
|
14
24
|
多个已安装环境。
|
|
15
25
|
- 显式传入 `--local`、`--cloud` 或 `--mcp` 时只处理指定环境。
|
|
16
26
|
- 已有环境沿用其原包管理器和全局安装前缀;首次安装沿用启动 setup 的包管理器。
|
|
27
|
+
- cloud 环境统一管理 `@mearl/cloud-server` 与 `@mearl/cloud-client`:首次安装后启动
|
|
28
|
+
Server,更新后仅在 Server 原本运行时重启,卸载前停止。
|
|
17
29
|
- local/cloud 共用的全局 mearl skill 通过 `ali-skills` 的 JSON 清单定位,只处理一次。
|
|
18
30
|
- 卸载一个环境但另一个仍依赖全局 mearl skill 时会保留 skill。
|
|
19
31
|
- `--mcp` 的客户端配置和清理由 `@mearl/mcp-server` 自己的命令完成。
|
|
20
32
|
|
|
33
|
+
## 跟随浏览器扩展自动更新
|
|
34
|
+
|
|
35
|
+
浏览器扩展与 Native Host 首次建联时会交换版本。每次扩展后台启动只检查一次;当扩展
|
|
36
|
+
进入更高的 `major.minor` release line 时,旧 Host 会在保持当前连接可用的前提下,调用
|
|
37
|
+
对应 `major.minor.x` 范围内最新的 `@mearl/setup`,把 `@mearl/native-host` 与
|
|
38
|
+
`@mearl/client` 一起更新到该范围内的最新版本。成功后 Host 退出,扩展通过已有重连机制
|
|
39
|
+
启动新版本。只有两个本地 CLI 版本一致且都属于扩展的 release line 时,更新才会被视为成功。
|
|
40
|
+
|
|
41
|
+
自动更新不会降级较新的 Host,也不会改动 cloud、MCP 或独立安装的 Mearl Skill。
|
|
42
|
+
目标 npm 版本尚未发布、网络不可用或更新失败时,旧 Host 会继续运行;下一次扩展后台
|
|
43
|
+
生命周期可重新尝试,也可手动执行 `npx @mearl/setup update --local`。
|
|
44
|
+
|
|
21
45
|
## 人工与 Agent 使用
|
|
22
46
|
|
|
23
47
|
- 人工在终端执行时不加 `--yes`,需要选择 Skill 安装目标或 MCP 客户端时会显示交互提示。
|
|
@@ -47,6 +71,12 @@ npx @mearl/setup uninstall --local
|
|
|
47
71
|
|
|
48
72
|
# 自动检测并无交互卸载
|
|
49
73
|
npx @mearl/setup uninstall --yes
|
|
74
|
+
|
|
75
|
+
# 配置或更新 AgentBay API Key
|
|
76
|
+
npx @mearl/setup agentbay
|
|
77
|
+
|
|
78
|
+
# 无交互配置(从权限受控的文件读取)
|
|
79
|
+
npx @mearl/setup agentbay --key-file ./agentbay.key
|
|
50
80
|
```
|
|
51
81
|
|
|
52
82
|
原有的 npm/npx 安装方式和各包 CLI 保持可用;setup 只是统一的便利入口。
|
|
@@ -0,0 +1,23 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
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
|
+
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin } from './agentBay.js';
|
|
2
3
|
import { type MearlLifecycleOperation } from './lifecycle.js';
|
|
3
4
|
import type { MearlProfile } from './profiles.js';
|
|
4
5
|
interface ParsedArgs {
|
|
6
|
+
agentBayKeyFile?: string;
|
|
7
|
+
agentBayReadStdin: boolean;
|
|
5
8
|
help: boolean;
|
|
6
|
-
operation?: MearlLifecycleOperation;
|
|
9
|
+
operation?: MearlLifecycleOperation | 'agentbay';
|
|
7
10
|
profile?: MearlProfile;
|
|
11
|
+
skipSkill: boolean;
|
|
12
|
+
targetLine?: string;
|
|
8
13
|
yes: boolean;
|
|
9
14
|
}
|
|
15
|
+
export interface SetupCliDependencies {
|
|
16
|
+
configureAgentBay?: typeof configureAgentBayApiKey;
|
|
17
|
+
promptAgentBayKey?: typeof promptAgentBayApiKey;
|
|
18
|
+
readAgentBayKeyFile?: typeof readAgentBayApiKeyFile;
|
|
19
|
+
readAgentBayKeyStdin?: typeof readAgentBayApiKeyStdin;
|
|
20
|
+
}
|
|
10
21
|
export declare function parseSetupArgs(argv: string[]): ParsedArgs;
|
|
11
|
-
export declare function main(argv?: string[], interactiveTerminal?: boolean): Promise<number>;
|
|
22
|
+
export declare function main(argv?: string[], interactiveTerminal?: boolean, dependencies?: SetupCliDependencies): Promise<number>;
|
|
12
23
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -1,22 +1,71 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
|
|
4
5
|
import { runMearlLifecycle } from './lifecycle.js';
|
|
6
|
+
import { isMearlReleaseLine } from './version.js';
|
|
5
7
|
export function parseSetupArgs(argv) {
|
|
6
8
|
if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {
|
|
7
|
-
return { help: true, yes: false };
|
|
9
|
+
return { agentBayReadStdin: false, help: true, skipSkill: false, yes: false };
|
|
8
10
|
}
|
|
9
11
|
const operation = argv[0];
|
|
12
|
+
if (operation === 'agentbay') {
|
|
13
|
+
if (argv.length === 1) {
|
|
14
|
+
return {
|
|
15
|
+
agentBayReadStdin: false,
|
|
16
|
+
help: false,
|
|
17
|
+
operation,
|
|
18
|
+
skipSkill: false,
|
|
19
|
+
yes: false,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (argv.length === 2 && argv[1] === '--stdin') {
|
|
23
|
+
return {
|
|
24
|
+
agentBayReadStdin: true,
|
|
25
|
+
help: false,
|
|
26
|
+
operation,
|
|
27
|
+
skipSkill: false,
|
|
28
|
+
yes: false,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (argv.length === 3 && argv[1] === '--key-file' && argv[2]?.trim()) {
|
|
32
|
+
return {
|
|
33
|
+
agentBayKeyFile: argv[2],
|
|
34
|
+
agentBayReadStdin: false,
|
|
35
|
+
help: false,
|
|
36
|
+
operation,
|
|
37
|
+
skipSkill: false,
|
|
38
|
+
yes: false,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
throw new Error('Usage: npx @mearl/setup agentbay [--stdin | --key-file <path>]. API keys are not accepted as command-line arguments.');
|
|
42
|
+
}
|
|
10
43
|
if (operation !== 'install' && operation !== 'update' && operation !== 'uninstall') {
|
|
11
44
|
throw new Error(`Unknown operation: ${operation}`);
|
|
12
45
|
}
|
|
13
46
|
let profile;
|
|
47
|
+
let skipSkill = false;
|
|
48
|
+
let targetLine;
|
|
14
49
|
let yes = false;
|
|
15
|
-
for (
|
|
50
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
51
|
+
const arg = argv[index];
|
|
16
52
|
if (arg === '--yes' || arg === '-y' || arg === '--non-interactive') {
|
|
17
53
|
yes = true;
|
|
18
54
|
continue;
|
|
19
55
|
}
|
|
56
|
+
if (arg === '--skip-skill') {
|
|
57
|
+
skipSkill = true;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (arg === '--target-line') {
|
|
61
|
+
const value = argv[index + 1];
|
|
62
|
+
if (!isMearlReleaseLine(value)) {
|
|
63
|
+
throw new Error('--target-line requires a valid Mearl release line such as 2.3.x');
|
|
64
|
+
}
|
|
65
|
+
targetLine = value;
|
|
66
|
+
index += 1;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
20
69
|
const candidate = arg === '--local'
|
|
21
70
|
? 'local'
|
|
22
71
|
: arg === '--cloud'
|
|
@@ -31,23 +80,41 @@ export function parseSetupArgs(argv) {
|
|
|
31
80
|
}
|
|
32
81
|
profile = candidate;
|
|
33
82
|
}
|
|
34
|
-
|
|
83
|
+
if (operation === 'uninstall' && targetLine) {
|
|
84
|
+
throw new Error('--target-line is not available for uninstall');
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
agentBayReadStdin: false,
|
|
88
|
+
help: false,
|
|
89
|
+
operation,
|
|
90
|
+
profile,
|
|
91
|
+
skipSkill,
|
|
92
|
+
targetLine,
|
|
93
|
+
yes,
|
|
94
|
+
};
|
|
35
95
|
}
|
|
36
96
|
function printHelp() {
|
|
37
97
|
console.error(`Mearl Setup
|
|
38
98
|
|
|
39
99
|
Usage:
|
|
40
100
|
npx @mearl/setup <install|update|uninstall> [--local|--cloud|--mcp] [--yes]
|
|
101
|
+
npx @mearl/setup agentbay [--stdin | --key-file <path>]
|
|
41
102
|
|
|
42
103
|
Behavior:
|
|
43
104
|
install Defaults to --local when no environment is specified
|
|
44
105
|
update Auto-detects installed environments from the current PATH
|
|
45
106
|
uninstall Auto-detects installed environments and asks for confirmation
|
|
107
|
+
agentbay Save or update AGENTBAY_KEY without changing other Mearl settings
|
|
46
108
|
|
|
47
109
|
Options:
|
|
48
110
|
--local Manage @mearl/native-host and @mearl/client
|
|
49
|
-
--cloud Manage @mearl/cloud-client
|
|
111
|
+
--cloud Manage @mearl/cloud-server and @mearl/cloud-client
|
|
50
112
|
--mcp Manage @mearl/mcp-server and its configuration
|
|
113
|
+
--stdin Read the AgentBay API key from standard input
|
|
114
|
+
--key-file Read the AgentBay API key from a local file
|
|
115
|
+
--target-line <major.minor.x>
|
|
116
|
+
Install the latest packages from one release line, such as 2.3.x
|
|
117
|
+
--skip-skill Keep the independently managed Mearl Skill unchanged
|
|
51
118
|
--yes, -y Run without prompts and accept detected defaults
|
|
52
119
|
--non-interactive
|
|
53
120
|
Alias for --yes, intended for Agents and CI
|
|
@@ -58,9 +125,11 @@ Examples:
|
|
|
58
125
|
npx @mearl/setup install --yes
|
|
59
126
|
npx @mearl/setup install --cloud
|
|
60
127
|
npx @mearl/setup update
|
|
128
|
+
npx @mearl/setup agentbay
|
|
129
|
+
npx @mearl/setup update --local --target-line 2.3.x --skip-skill
|
|
61
130
|
npx @mearl/setup uninstall --local`);
|
|
62
131
|
}
|
|
63
|
-
export async function main(argv = process.argv.slice(2), interactiveTerminal = process.stdin.isTTY === true && process.stdout.isTTY === true) {
|
|
132
|
+
export async function main(argv = process.argv.slice(2), interactiveTerminal = process.stdin.isTTY === true && process.stdout.isTTY === true, dependencies = {}) {
|
|
64
133
|
let parsed;
|
|
65
134
|
try {
|
|
66
135
|
parsed = parseSetupArgs(argv);
|
|
@@ -74,6 +143,31 @@ export async function main(argv = process.argv.slice(2), interactiveTerminal = p
|
|
|
74
143
|
printHelp();
|
|
75
144
|
return 0;
|
|
76
145
|
}
|
|
146
|
+
if (parsed.operation === 'agentbay') {
|
|
147
|
+
try {
|
|
148
|
+
const configureAgentBay = dependencies.configureAgentBay ?? configureAgentBayApiKey;
|
|
149
|
+
let apiKey;
|
|
150
|
+
if (parsed.agentBayKeyFile) {
|
|
151
|
+
apiKey = (dependencies.readAgentBayKeyFile ?? readAgentBayApiKeyFile)(parsed.agentBayKeyFile);
|
|
152
|
+
}
|
|
153
|
+
else if (parsed.agentBayReadStdin) {
|
|
154
|
+
apiKey = await (dependencies.readAgentBayKeyStdin ?? readAgentBayApiKeyStdin)();
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
if (!interactiveTerminal) {
|
|
158
|
+
throw new Error('Interactive input is unavailable. Re-run with --stdin or --key-file <path>.');
|
|
159
|
+
}
|
|
160
|
+
apiKey = await (dependencies.promptAgentBayKey ?? promptAgentBayApiKey)();
|
|
161
|
+
}
|
|
162
|
+
const result = configureAgentBay(apiKey);
|
|
163
|
+
console.error(`AgentBay API key saved to ${result.envFile}.`);
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
168
|
+
return 1;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
77
171
|
if (!interactiveTerminal && !parsed.yes && parsed.operation !== 'update') {
|
|
78
172
|
console.error('Non-interactive input detected. Re-run with --yes or --non-interactive.');
|
|
79
173
|
return 2;
|
|
@@ -82,6 +176,8 @@ export async function main(argv = process.argv.slice(2), interactiveTerminal = p
|
|
|
82
176
|
const ok = await runMearlLifecycle({
|
|
83
177
|
operation: parsed.operation,
|
|
84
178
|
profile: parsed.profile,
|
|
179
|
+
skipSkill: parsed.skipSkill,
|
|
180
|
+
targetLine: parsed.targetLine,
|
|
85
181
|
yes: parsed.yes,
|
|
86
182
|
});
|
|
87
183
|
return ok ? 0 : 1;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';
|
|
2
|
+
export { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
|
|
3
|
+
export type { AgentBayConfigurationOptions, AgentBayConfigurationResult, AgentBaySecretInput, AgentBaySecretOutput, } from './agentBay.js';
|
|
2
4
|
export type { DetectedGlobalPackage, GlobalPackageCommand, GlobalPackageManager, PackageManagerInstallation, PackageOperation, PackageDetectionOptions, } from './packageManager.js';
|
|
3
5
|
export { runMearlLifecycle } from './lifecycle.js';
|
|
4
6
|
export type { MearlLifecycleDependencies, MearlLifecycleOperation, MearlLifecycleOptions, } from './lifecycle.js';
|
|
5
7
|
export { MEARL_PROFILES, detectMearlProfiles } from './profiles.js';
|
|
6
8
|
export type { DetectedMearlProfiles, MearlProfile, MearlProfileConfig } from './profiles.js';
|
|
7
9
|
export { MEARL_NATIVE_HOST_NAMES, resolveNativeHostDirectory, uninstallNativeHostArtifacts, } from './nativeHost.js';
|
|
10
|
+
export { compareMearlReleaseLines, isMearlReleaseLine, isMearlReleaseVersion, isMearlReleaseVersionInLine, mearlReleaseLineForVersion, parseMearlReleaseLine, parseMearlReleaseVersion, } from './version.js';
|
|
11
|
+
export type { MearlReleaseLine, MearlReleaseVersion } from './version.js';
|
|
8
12
|
export type { NativeHostCleanupDependencies, NativeHostCleanupOptions, NativeHostIntegrationMode, } from './nativeHost.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';
|
|
2
|
+
export { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
|
|
2
3
|
export { runMearlLifecycle } from './lifecycle.js';
|
|
3
4
|
export { MEARL_PROFILES, detectMearlProfiles } from './profiles.js';
|
|
4
5
|
export { MEARL_NATIVE_HOST_NAMES, resolveNativeHostDirectory, uninstallNativeHostArtifacts, } from './nativeHost.js';
|
|
6
|
+
export { compareMearlReleaseLines, isMearlReleaseLine, isMearlReleaseVersion, isMearlReleaseVersionInLine, mearlReleaseLineForVersion, parseMearlReleaseLine, parseMearlReleaseVersion, } from './version.js';
|
package/dist/lifecycle.d.ts
CHANGED
package/dist/lifecycle.js
CHANGED
|
@@ -7,6 +7,7 @@ import { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageM
|
|
|
7
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
|
+
import { isMearlReleaseLine } from './version.js';
|
|
10
11
|
async function confirmInTerminal(message) {
|
|
11
12
|
if (!input.isTTY || !output.isTTY)
|
|
12
13
|
return false;
|
|
@@ -27,10 +28,10 @@ function siblingExecutable(anchor, name, existsSync) {
|
|
|
27
28
|
}
|
|
28
29
|
return null;
|
|
29
30
|
}
|
|
30
|
-
async function runLifecycleCommand(runner, command, args, env, label, writeLine) {
|
|
31
|
+
async function runLifecycleCommand(runner, command, args, env, label, writeLine, capture = false) {
|
|
31
32
|
try {
|
|
32
33
|
return await runner(command, args, {
|
|
33
|
-
capture
|
|
34
|
+
capture,
|
|
34
35
|
env: prependCommandPath(command, env),
|
|
35
36
|
});
|
|
36
37
|
}
|
|
@@ -58,6 +59,31 @@ async function runHook(profile, operation, detected, runner, existsSync, env, wr
|
|
|
58
59
|
executable = siblingExecutable(detected.executable, 'mearl-mcp-configure', existsSync);
|
|
59
60
|
args = ['--skip-mcp', '--global-install'];
|
|
60
61
|
}
|
|
62
|
+
else if (profile === 'cloud') {
|
|
63
|
+
executable = siblingExecutable(detected.executable, 'mearl-cloud-server', existsSync);
|
|
64
|
+
if (!executable) {
|
|
65
|
+
writeLine(`Hook: ${profile} ${operation} command was not found.`);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (operation === 'uninstall') {
|
|
69
|
+
args = ['stop'];
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const status = await runLifecycleCommand(runner, executable, ['status'], env, 'Hook: cloud status', writeLine, true);
|
|
73
|
+
if (!status)
|
|
74
|
+
return false;
|
|
75
|
+
if (status.code !== 0) {
|
|
76
|
+
writeLine(`Hook: cloud status failed (exit ${status.code}).`);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const running = /\[CloudServer\]\s+Status:\s+running\b/.test(status.stdout);
|
|
80
|
+
if (operation === 'update' && !running) {
|
|
81
|
+
writeLine('Hook: cloud update skipped because cloud-server is not running.');
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
args = [running ? 'restart' : 'start'];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
61
87
|
else {
|
|
62
88
|
return true;
|
|
63
89
|
}
|
|
@@ -85,6 +111,9 @@ function operationVerb(operation) {
|
|
|
85
111
|
return operation === 'uninstall' ? 'uninstall' : operation === 'update' ? 'update' : 'install';
|
|
86
112
|
}
|
|
87
113
|
export async function runMearlLifecycle(options, dependencies = {}) {
|
|
114
|
+
if (options.targetLine && !isMearlReleaseLine(options.targetLine)) {
|
|
115
|
+
throw new Error('targetLine must be a valid Mearl release line such as 2.3.x');
|
|
116
|
+
}
|
|
88
117
|
const env = options.env ?? process.env;
|
|
89
118
|
const writeLine = dependencies.writeLine ?? (line => console.error(line));
|
|
90
119
|
const existsSync = dependencies.existsSync ?? fs.existsSync;
|
|
@@ -158,7 +187,7 @@ export async function runMearlLifecycle(options, dependencies = {}) {
|
|
|
158
187
|
continue;
|
|
159
188
|
}
|
|
160
189
|
const packageOperation = options.operation === 'uninstall' ? 'uninstall' : options.operation;
|
|
161
|
-
const command = buildGlobalPackageCommand(installation, packageOperation, config.packages);
|
|
190
|
+
const command = buildGlobalPackageCommand(installation, packageOperation, config.packages, options.targetLine);
|
|
162
191
|
writeLine(`Packages (${profile}): ${command.manager}`);
|
|
163
192
|
const result = await runLifecycleCommand(runner, command.command, command.args, env, `Packages (${profile})`, writeLine);
|
|
164
193
|
if (!result) {
|
|
@@ -170,7 +199,8 @@ export async function runMearlLifecycle(options, dependencies = {}) {
|
|
|
170
199
|
packagesOk = false;
|
|
171
200
|
continue;
|
|
172
201
|
}
|
|
173
|
-
if (options.operation !== 'uninstall' &&
|
|
202
|
+
if (options.operation !== 'uninstall' &&
|
|
203
|
+
(profile === 'local' || profile === 'cloud' || profile === 'mcp')) {
|
|
174
204
|
const refreshed = detectPackage(config.anchorPackage, config.anchorBins, {
|
|
175
205
|
env: prependCommandPath(command.command, env),
|
|
176
206
|
}) ?? current;
|
|
@@ -182,7 +212,7 @@ export async function runMearlLifecycle(options, dependencies = {}) {
|
|
|
182
212
|
}
|
|
183
213
|
let skillOk = true;
|
|
184
214
|
const selectedUsesSkill = selected.some(profile => MEARL_PROFILES[profile].usesSkill);
|
|
185
|
-
if (packagesOk && selectedUsesSkill) {
|
|
215
|
+
if (packagesOk && selectedUsesSkill && options.skipSkill !== true) {
|
|
186
216
|
try {
|
|
187
217
|
if (options.operation === 'uninstall') {
|
|
188
218
|
const remainingSkillProfiles = ['local', 'cloud'].filter(profile => detected.has(profile) && !selected.includes(profile));
|
package/dist/packageManager.d.ts
CHANGED
|
@@ -3,8 +3,12 @@ export type PackageOperation = 'install' | 'update' | 'uninstall';
|
|
|
3
3
|
export interface PackageManagerInstallation {
|
|
4
4
|
manager: GlobalPackageManager;
|
|
5
5
|
command: string;
|
|
6
|
+
/** Directory containing globally installed package executables. */
|
|
7
|
+
binDirectory?: string;
|
|
6
8
|
}
|
|
7
|
-
export interface GlobalPackageCommand
|
|
9
|
+
export interface GlobalPackageCommand {
|
|
10
|
+
manager: GlobalPackageManager;
|
|
11
|
+
command: string;
|
|
8
12
|
args: string[];
|
|
9
13
|
}
|
|
10
14
|
export interface DetectedGlobalPackage extends PackageManagerInstallation {
|
|
@@ -21,7 +25,7 @@ export interface PackageDetectionOptions {
|
|
|
21
25
|
realpathSync?: (filePath: string) => string;
|
|
22
26
|
}
|
|
23
27
|
export declare function resolvePackageManagerInstallation(packageRoot: string, packageName: string, options?: PackageDetectionOptions): PackageManagerInstallation | null;
|
|
24
|
-
export declare function buildGlobalPackageCommand(installation: PackageManagerInstallation, operation: PackageOperation, packages: string[]): GlobalPackageCommand;
|
|
28
|
+
export declare function buildGlobalPackageCommand(installation: PackageManagerInstallation, operation: PackageOperation, packages: string[], targetSelector?: string): GlobalPackageCommand;
|
|
25
29
|
export declare function resolveInvokingPackageManager(options?: {
|
|
26
30
|
env?: NodeJS.ProcessEnv;
|
|
27
31
|
existsSync?: (filePath: string) => boolean;
|
package/dist/packageManager.js
CHANGED
|
@@ -29,26 +29,48 @@ export function resolvePackageManagerInstallation(packageRoot, packageName, opti
|
|
|
29
29
|
...executableNames('pnpm', platform).map(name => path.join(pnpmHome, name)),
|
|
30
30
|
...executableNames('pnpm', platform).map(name => path.join(path.dirname(processExecPath), name)),
|
|
31
31
|
], existsSync) ?? fallbackCommand('pnpm', platform);
|
|
32
|
-
return { manager: 'pnpm', command };
|
|
32
|
+
return { manager: 'pnpm', command, binDirectory: pnpmHome };
|
|
33
33
|
}
|
|
34
34
|
const bunMarker = `/.bun/install/global/node_modules/${lowerPackageName}`;
|
|
35
35
|
if (lowerRoot.endsWith(bunMarker)) {
|
|
36
36
|
const bunHome = normalizedRoot.slice(0, lowerRoot.indexOf('/.bun/'));
|
|
37
37
|
const command = firstExisting(executableNames('bun', platform).map(name => path.join(bunHome, '.bun', 'bin', name)), existsSync) ?? fallbackCommand('bun', platform);
|
|
38
|
-
return {
|
|
38
|
+
return {
|
|
39
|
+
manager: 'bun',
|
|
40
|
+
command,
|
|
41
|
+
binDirectory: path.join(bunHome, '.bun', 'bin'),
|
|
42
|
+
};
|
|
39
43
|
}
|
|
40
44
|
if ((lowerRoot.includes('/.config/yarn/global/node_modules/') ||
|
|
41
45
|
lowerRoot.includes('/.yarn/global/node_modules/') ||
|
|
42
46
|
lowerRoot.includes('/yarn/global/node_modules/') ||
|
|
43
47
|
lowerRoot.includes('/yarn/data/global/node_modules/')) &&
|
|
44
48
|
lowerRoot.endsWith(`/node_modules/${lowerPackageName}`)) {
|
|
45
|
-
|
|
49
|
+
const layout = [
|
|
50
|
+
{ marker: '/.config/yarn/global/', binSegments: ['.yarn', 'bin'] },
|
|
51
|
+
{ marker: '/.yarn/global/', binSegments: ['.yarn', 'bin'] },
|
|
52
|
+
{ marker: '/yarn/data/global/', binSegments: ['yarn', 'bin'] },
|
|
53
|
+
{ marker: '/yarn/global/', binSegments: ['yarn', 'bin'] },
|
|
54
|
+
].find(candidate => lowerRoot.includes(candidate.marker));
|
|
55
|
+
const prefix = normalizedRoot.slice(0, lowerRoot.indexOf(layout.marker));
|
|
56
|
+
const binDirectory = path.join(prefix, ...layout.binSegments);
|
|
57
|
+
const command = firstExisting([
|
|
58
|
+
...executableNames('yarn', platform).map(name => path.join(binDirectory, name)),
|
|
59
|
+
...executableNames('yarn', platform).map(name => path.join(path.dirname(processExecPath), name)),
|
|
60
|
+
], existsSync) ??
|
|
61
|
+
findExecutable('yarn', { env, existsSync, platform }) ??
|
|
62
|
+
fallbackCommand('yarn', platform);
|
|
63
|
+
return { manager: 'yarn', command, binDirectory };
|
|
46
64
|
}
|
|
47
65
|
if (lowerRoot.includes('/.volta/tools/image/packages/') &&
|
|
48
66
|
lowerRoot.endsWith(`/lib/node_modules/${lowerPackageName}`)) {
|
|
49
67
|
const voltaHome = normalizedRoot.slice(0, lowerRoot.indexOf('/.volta/'));
|
|
50
68
|
const command = firstExisting(executableNames('volta', platform).map(name => path.join(voltaHome, '.volta', 'bin', name)), existsSync) ?? fallbackCommand('volta', platform);
|
|
51
|
-
return {
|
|
69
|
+
return {
|
|
70
|
+
manager: 'volta',
|
|
71
|
+
command,
|
|
72
|
+
binDirectory: path.join(voltaHome, '.volta', 'bin'),
|
|
73
|
+
};
|
|
52
74
|
}
|
|
53
75
|
const npmMarker = `/lib/node_modules/${lowerPackageName}`;
|
|
54
76
|
if (lowerRoot.endsWith(npmMarker)) {
|
|
@@ -57,18 +79,25 @@ export function resolvePackageManagerInstallation(packageRoot, packageName, opti
|
|
|
57
79
|
...executableNames('npm', platform).map(name => path.join(prefix, 'bin', name)),
|
|
58
80
|
...executableNames('npm', platform).map(name => path.join(path.dirname(processExecPath), name)),
|
|
59
81
|
], existsSync) ?? fallbackCommand('npm', platform);
|
|
60
|
-
return { manager: 'npm', command };
|
|
82
|
+
return { manager: 'npm', command, binDirectory: path.join(prefix, 'bin') };
|
|
61
83
|
}
|
|
62
84
|
if (platform === 'win32' && env.APPDATA) {
|
|
63
85
|
const npmRoot = normalizePath(path.join(env.APPDATA, 'npm', 'node_modules', packageName));
|
|
64
86
|
if (lowerRoot === npmRoot.toLowerCase()) {
|
|
65
|
-
|
|
87
|
+
const binDirectory = path.join(env.APPDATA, 'npm');
|
|
88
|
+
const command = firstExisting([
|
|
89
|
+
...executableNames('npm', platform).map(name => path.join(binDirectory, name)),
|
|
90
|
+
...executableNames('npm', platform).map(name => path.join(path.dirname(processExecPath), name)),
|
|
91
|
+
], existsSync) ?? fallbackCommand('npm', platform);
|
|
92
|
+
return { manager: 'npm', command, binDirectory };
|
|
66
93
|
}
|
|
67
94
|
}
|
|
68
95
|
return null;
|
|
69
96
|
}
|
|
70
|
-
export function buildGlobalPackageCommand(installation, operation, packages) {
|
|
71
|
-
const targets = operation === 'uninstall'
|
|
97
|
+
export function buildGlobalPackageCommand(installation, operation, packages, targetSelector = 'latest') {
|
|
98
|
+
const targets = operation === 'uninstall'
|
|
99
|
+
? packages
|
|
100
|
+
: packages.map(packageName => `${packageName}@${targetSelector}`);
|
|
72
101
|
let args;
|
|
73
102
|
switch (installation.manager) {
|
|
74
103
|
case 'npm':
|
|
@@ -91,7 +120,7 @@ export function buildGlobalPackageCommand(installation, operation, packages) {
|
|
|
91
120
|
args = operation === 'uninstall' ? ['uninstall', ...targets] : ['install', ...targets];
|
|
92
121
|
break;
|
|
93
122
|
}
|
|
94
|
-
return {
|
|
123
|
+
return { manager: installation.manager, command: installation.command, args };
|
|
95
124
|
}
|
|
96
125
|
export function resolveInvokingPackageManager(options = {}) {
|
|
97
126
|
const env = options.env ?? process.env;
|
|
@@ -194,7 +223,13 @@ export function detectGlobalPackage(packageName, binNames, options = {}) {
|
|
|
194
223
|
});
|
|
195
224
|
if (!installation)
|
|
196
225
|
continue;
|
|
197
|
-
return {
|
|
226
|
+
return {
|
|
227
|
+
packageName,
|
|
228
|
+
packageRoot,
|
|
229
|
+
executable,
|
|
230
|
+
...installation,
|
|
231
|
+
binDirectory: path.dirname(executable),
|
|
232
|
+
};
|
|
198
233
|
}
|
|
199
234
|
return null;
|
|
200
235
|
}
|
package/dist/process.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export declare function versionManagerExecutableCandidates(home: string, names:
|
|
|
26
26
|
export declare function resolveSpawnCommand(executable: string, args: string[], platform?: NodeJS.Platform, commandInterpreter?: string | undefined): SpawnCommand;
|
|
27
27
|
export declare function prependCommandPath(command: string, env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
28
28
|
export declare function prependNodePath(env?: NodeJS.ProcessEnv, nodeExecutable?: string): NodeJS.ProcessEnv;
|
|
29
|
+
export declare function prependPathDirectory(directory: string, env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
29
30
|
export declare function findExecutable(name: string, options?: {
|
|
30
31
|
env?: NodeJS.ProcessEnv;
|
|
31
32
|
existsSync?: (filePath: string) => boolean;
|
package/dist/process.js
CHANGED
|
@@ -57,12 +57,12 @@ export function resolveSpawnCommand(executable, args, platform = process.platfor
|
|
|
57
57
|
export function prependCommandPath(command, env = process.env) {
|
|
58
58
|
if (!path.isAbsolute(command))
|
|
59
59
|
return env;
|
|
60
|
-
return
|
|
60
|
+
return prependPathDirectory(path.dirname(command), env);
|
|
61
61
|
}
|
|
62
62
|
export function prependNodePath(env = process.env, nodeExecutable = process.execPath) {
|
|
63
|
-
return
|
|
63
|
+
return prependPathDirectory(path.dirname(nodeExecutable), env);
|
|
64
64
|
}
|
|
65
|
-
function
|
|
65
|
+
export function prependPathDirectory(directory, env = process.env) {
|
|
66
66
|
const pathKey = Object.keys(env).find(key => key.toLowerCase() === 'path') || 'PATH';
|
|
67
67
|
const inherited = env[pathKey] || '';
|
|
68
68
|
const entries = inherited.split(path.delimiter).filter(Boolean);
|
package/dist/profiles.js
CHANGED
|
@@ -13,7 +13,7 @@ export const MEARL_PROFILES = {
|
|
|
13
13
|
label: 'cloud agent',
|
|
14
14
|
anchorPackage: '@mearl/cloud-client',
|
|
15
15
|
anchorBins: ['mearl-cloud'],
|
|
16
|
-
packages: ['@mearl/cloud-client'],
|
|
16
|
+
packages: ['@mearl/cloud-server', '@mearl/cloud-client'],
|
|
17
17
|
usesSkill: true,
|
|
18
18
|
},
|
|
19
19
|
mcp: {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const MEARL_RELEASE_VERSION_MAX_LENGTH = 64;
|
|
2
|
+
export interface MearlReleaseVersion {
|
|
3
|
+
raw: string;
|
|
4
|
+
major: number;
|
|
5
|
+
minor: number;
|
|
6
|
+
patch: number;
|
|
7
|
+
channel: 'stable' | 'beta';
|
|
8
|
+
beta?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface MearlReleaseLine {
|
|
11
|
+
raw: string;
|
|
12
|
+
major: number;
|
|
13
|
+
minor: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function parseMearlReleaseVersion(value: unknown): MearlReleaseVersion | null;
|
|
16
|
+
export declare function isMearlReleaseVersion(value: unknown): value is string;
|
|
17
|
+
export declare function parseMearlReleaseLine(value: unknown): MearlReleaseLine | null;
|
|
18
|
+
export declare function isMearlReleaseLine(value: unknown): value is string;
|
|
19
|
+
export declare function mearlReleaseLineForVersion(version: string): string;
|
|
20
|
+
export declare function isMearlReleaseVersionInLine(version: string, releaseLine: string): boolean;
|
|
21
|
+
export declare function compareMearlReleaseLines(leftVersion: string, rightVersion: string): -1 | 0 | 1;
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export const MEARL_RELEASE_VERSION_MAX_LENGTH = 64;
|
|
2
|
+
const RELEASE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-beta\.(0|[1-9]\d*))?$/;
|
|
3
|
+
const RELEASE_LINE_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.x$/;
|
|
4
|
+
const RELEASE_COMPONENT_MAXIMUM = 65_535;
|
|
5
|
+
function parseComponent(value) {
|
|
6
|
+
const component = Number(value);
|
|
7
|
+
return Number.isSafeInteger(component) && component <= RELEASE_COMPONENT_MAXIMUM
|
|
8
|
+
? component
|
|
9
|
+
: null;
|
|
10
|
+
}
|
|
11
|
+
export function parseMearlReleaseVersion(value) {
|
|
12
|
+
if (typeof value !== 'string' || value.length > MEARL_RELEASE_VERSION_MAX_LENGTH)
|
|
13
|
+
return null;
|
|
14
|
+
const match = RELEASE_VERSION_PATTERN.exec(value);
|
|
15
|
+
if (!match)
|
|
16
|
+
return null;
|
|
17
|
+
const major = parseComponent(match[1]);
|
|
18
|
+
const minor = parseComponent(match[2]);
|
|
19
|
+
const patch = parseComponent(match[3]);
|
|
20
|
+
const beta = match[4] === undefined ? undefined : parseComponent(match[4]);
|
|
21
|
+
if (major === null || minor === null || patch === null || beta === null)
|
|
22
|
+
return null;
|
|
23
|
+
return {
|
|
24
|
+
raw: value,
|
|
25
|
+
major,
|
|
26
|
+
minor,
|
|
27
|
+
patch,
|
|
28
|
+
channel: beta === undefined ? 'stable' : 'beta',
|
|
29
|
+
...(beta === undefined ? {} : { beta }),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function isMearlReleaseVersion(value) {
|
|
33
|
+
return parseMearlReleaseVersion(value) !== null;
|
|
34
|
+
}
|
|
35
|
+
export function parseMearlReleaseLine(value) {
|
|
36
|
+
if (typeof value !== 'string' || value.length > MEARL_RELEASE_VERSION_MAX_LENGTH)
|
|
37
|
+
return null;
|
|
38
|
+
const match = RELEASE_LINE_PATTERN.exec(value);
|
|
39
|
+
if (!match)
|
|
40
|
+
return null;
|
|
41
|
+
const major = parseComponent(match[1]);
|
|
42
|
+
const minor = parseComponent(match[2]);
|
|
43
|
+
if (major === null || minor === null)
|
|
44
|
+
return null;
|
|
45
|
+
return { raw: value, major, minor };
|
|
46
|
+
}
|
|
47
|
+
export function isMearlReleaseLine(value) {
|
|
48
|
+
return parseMearlReleaseLine(value) !== null;
|
|
49
|
+
}
|
|
50
|
+
export function mearlReleaseLineForVersion(version) {
|
|
51
|
+
const parsed = parseMearlReleaseVersion(version);
|
|
52
|
+
if (!parsed)
|
|
53
|
+
throw new Error('A valid Mearl release version is required');
|
|
54
|
+
return `${parsed.major}.${parsed.minor}.x`;
|
|
55
|
+
}
|
|
56
|
+
export function isMearlReleaseVersionInLine(version, releaseLine) {
|
|
57
|
+
const parsedVersion = parseMearlReleaseVersion(version);
|
|
58
|
+
const parsedLine = parseMearlReleaseLine(releaseLine);
|
|
59
|
+
return (parsedVersion !== null &&
|
|
60
|
+
parsedLine !== null &&
|
|
61
|
+
parsedVersion.major === parsedLine.major &&
|
|
62
|
+
parsedVersion.minor === parsedLine.minor);
|
|
63
|
+
}
|
|
64
|
+
export function compareMearlReleaseLines(leftVersion, rightVersion) {
|
|
65
|
+
const left = parseMearlReleaseVersion(leftVersion);
|
|
66
|
+
const right = parseMearlReleaseVersion(rightVersion);
|
|
67
|
+
if (!left || !right) {
|
|
68
|
+
throw new Error('Mearl release-line comparison requires valid release versions');
|
|
69
|
+
}
|
|
70
|
+
for (const key of ['major', 'minor']) {
|
|
71
|
+
if (left[key] < right[key])
|
|
72
|
+
return -1;
|
|
73
|
+
if (left[key] > right[key])
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/setup",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "One-shot
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"description": "One-shot environment setup and configuration for Mearl",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -18,9 +18,21 @@
|
|
|
18
18
|
"types": "./dist/process.d.ts",
|
|
19
19
|
"import": "./dist/process.js"
|
|
20
20
|
},
|
|
21
|
+
"./package-manager": {
|
|
22
|
+
"types": "./dist/packageManager.d.ts",
|
|
23
|
+
"import": "./dist/packageManager.js"
|
|
24
|
+
},
|
|
21
25
|
"./native-host": {
|
|
22
26
|
"types": "./dist/nativeHost.d.ts",
|
|
23
27
|
"import": "./dist/nativeHost.js"
|
|
28
|
+
},
|
|
29
|
+
"./agentbay": {
|
|
30
|
+
"types": "./dist/agentBay.d.ts",
|
|
31
|
+
"import": "./dist/agentBay.js"
|
|
32
|
+
},
|
|
33
|
+
"./version": {
|
|
34
|
+
"types": "./dist/version.d.ts",
|
|
35
|
+
"import": "./dist/version.js"
|
|
24
36
|
}
|
|
25
37
|
},
|
|
26
38
|
"bin": {
|