@mearl/setup 2.8.2 → 2.9.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 +16 -3
- package/dist/browserExtension.js +4 -3
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +149 -121
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
Mearl 环境的一次性安装、更新、卸载与配置工具。无需先全局安装 setup:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npx @mearl/setup [install] [--local] [--cloud|--qoder-cloud] [--mcp]
|
|
7
|
-
npx @mearl/setup <update|uninstall> [--local] [--cloud|--qoder-cloud] [--mcp]
|
|
8
|
-
npx @mearl/setup extension [--
|
|
6
|
+
npx @mearl/setup [install] [--local] [--cloud|--qoder-cloud] [--mcp]
|
|
7
|
+
npx @mearl/setup <update|uninstall> [--local] [--cloud|--qoder-cloud] [--mcp]
|
|
8
|
+
npx @mearl/setup extension [--full] [--force]
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
配置 AgentBay API Key 时只需执行一次下面的命令,然后在隐藏输入提示中粘贴 Key。
|
|
@@ -51,6 +51,10 @@ npx @mearl/setup agentbay
|
|
|
51
51
|
加载;后续更新由 `mearl check --local --json` 确认当前扩展仍来自 setup 管理目录后,直接替换
|
|
52
52
|
文件并通过 Native Host 触发扩展 reload。固定目录和 manifest 中的 `key` 保证扩展 ID
|
|
53
53
|
与浏览器存储保持稳定。
|
|
54
|
+
- `extension --full` 会先安装或更新扩展,再复用 local profile 安装
|
|
55
|
+
`@mearl/client`、安装并初始化 `@mearl/native-host`,最后安装全局 Mearl Skill。
|
|
56
|
+
两部分都会尽量执行;任一步失败时命令返回非零退出码,已安装的部分会保留。它不安装
|
|
57
|
+
cloud 或 MCP 环境。
|
|
54
58
|
- 下载前会通过 CDP 检查同 ID 插件的来源。未安装,或来源为固定目录中的 `UNPACKED`
|
|
55
59
|
插件时可继续;Chrome 商店、企业策略或其他本地目录安装的版本默认保持不变。只有显式
|
|
56
60
|
传入 `--force` 才会接管并替换这些版本。
|
|
@@ -61,6 +65,12 @@ npx @mearl/setup agentbay
|
|
|
61
65
|
npx @mearl/setup extension
|
|
62
66
|
```
|
|
63
67
|
|
|
68
|
+
首次使用时可一次安装完整的本地浏览器环境:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx @mearl/setup extension --full
|
|
72
|
+
```
|
|
73
|
+
|
|
64
74
|
首次安装、旧版 setup 扩展首次迁移,或 Native Host reload 不可用时才需要 CDP。如果
|
|
65
75
|
Chrome 尚未开放 CDP,交互命令会提示打开
|
|
66
76
|
`chrome://inspect/#remote-debugging`、启用远程调试并允许连接,然后等待安装完成。无 TTY
|
|
@@ -127,6 +137,9 @@ npx @mearl/setup update
|
|
|
127
137
|
# 独立安装或更新到最新浏览器扩展
|
|
128
138
|
npx @mearl/setup extension
|
|
129
139
|
|
|
140
|
+
# 安装完整本地环境(client、Native Host、Skill 和扩展)
|
|
141
|
+
npx @mearl/setup extension --full
|
|
142
|
+
|
|
130
143
|
# 只卸载本地环境
|
|
131
144
|
npx @mearl/setup uninstall --local
|
|
132
145
|
|
package/dist/browserExtension.js
CHANGED
|
@@ -488,11 +488,12 @@ export async function installMearlBrowserExtension(options = {}, dependencies =
|
|
|
488
488
|
const setupManagedAtStart = setupManagedBrowserIds.length > 0;
|
|
489
489
|
let cdpSession;
|
|
490
490
|
const inspectOnce = async () => {
|
|
491
|
-
if (!useSharedCdpSession)
|
|
492
|
-
return inspectInstallation(chromeUserDataDirectory);
|
|
491
|
+
if (!useSharedCdpSession) {
|
|
492
|
+
return options.force ? null : inspectInstallation(chromeUserDataDirectory);
|
|
493
|
+
}
|
|
493
494
|
const candidate = await connectCdp(chromeUserDataDirectory);
|
|
494
495
|
try {
|
|
495
|
-
const installed = await candidate.inspectInstallation();
|
|
496
|
+
const installed = options.force ? null : await candidate.inspectInstallation();
|
|
496
497
|
cdpSession?.close();
|
|
497
498
|
cdpSession = candidate;
|
|
498
499
|
return installed;
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,132 +1,142 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
+
import { Command, CommanderError } from 'commander';
|
|
3
4
|
import { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
|
|
4
5
|
import { installMearlBrowserExtension } from './browserExtension.js';
|
|
5
6
|
import { runMearlLifecycle } from './lifecycle.js';
|
|
6
7
|
import { isMearlReleaseLine } from './version.js';
|
|
7
8
|
const require = createRequire(import.meta.url);
|
|
8
9
|
export const SETUP_VERSION = require('../package.json').version;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const baseParsedArgs = () => ({
|
|
11
|
+
agentBayReadStdin: false,
|
|
12
|
+
help: false,
|
|
13
|
+
skipSkill: false,
|
|
14
|
+
yes: false,
|
|
15
|
+
});
|
|
16
|
+
function commanderError(error) {
|
|
17
|
+
const message = error.message.replace(/^error:\s*/i, '');
|
|
18
|
+
if (error.code === 'commander.unknownCommand') {
|
|
19
|
+
return new Error(message.replace(/^unknown command/i, 'Unknown operation'));
|
|
12
20
|
}
|
|
13
|
-
if (
|
|
14
|
-
return
|
|
15
|
-
agentBayReadStdin: false,
|
|
16
|
-
help: false,
|
|
17
|
-
skipSkill: false,
|
|
18
|
-
version: true,
|
|
19
|
-
yes: false,
|
|
20
|
-
};
|
|
21
|
+
if (error.code === 'commander.unknownOption') {
|
|
22
|
+
return new Error(message.replace(/^unknown option/i, 'Unknown option'));
|
|
21
23
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
return new Error(message);
|
|
25
|
+
}
|
|
26
|
+
export function parseSetupArgs(argv) {
|
|
27
|
+
let parsed;
|
|
28
|
+
const program = new Command()
|
|
29
|
+
.name('mearl-setup')
|
|
30
|
+
.version(SETUP_VERSION, '-v, --version')
|
|
31
|
+
.exitOverride()
|
|
32
|
+
.configureOutput({ writeErr: () => { }, writeOut: () => { } });
|
|
33
|
+
const addLifecycleCommand = (operation) => {
|
|
34
|
+
const command = new Command(operation)
|
|
35
|
+
.exitOverride()
|
|
36
|
+
.configureOutput({ writeErr: () => { }, writeOut: () => { } })
|
|
37
|
+
.option('--local')
|
|
38
|
+
.option('--cloud')
|
|
39
|
+
.option('--qoder-cloud')
|
|
40
|
+
.option('--mcp')
|
|
41
|
+
.option('--target-line <major.minor.x>')
|
|
42
|
+
.option('--skip-skill')
|
|
43
|
+
.option('-y, --yes')
|
|
44
|
+
.option('--non-interactive')
|
|
45
|
+
.action(() => {
|
|
46
|
+
const options = command.opts();
|
|
47
|
+
if (options.cloud && options.qoderCloud) {
|
|
48
|
+
throw new Error('--cloud and --qoder-cloud cannot be used together');
|
|
49
|
+
}
|
|
50
|
+
if (options.targetLine && !isMearlReleaseLine(options.targetLine)) {
|
|
51
|
+
throw new Error('--target-line requires a valid Mearl release line such as 2.3.x');
|
|
52
|
+
}
|
|
53
|
+
if (operation === 'uninstall' && options.targetLine) {
|
|
54
|
+
throw new Error('--target-line is not available for uninstall');
|
|
55
|
+
}
|
|
56
|
+
const profiles = [];
|
|
57
|
+
if (options.local)
|
|
58
|
+
profiles.push('local');
|
|
59
|
+
if (options.cloud || options.qoderCloud)
|
|
60
|
+
profiles.push('cloud');
|
|
61
|
+
if (options.mcp)
|
|
62
|
+
profiles.push('mcp');
|
|
63
|
+
parsed = {
|
|
64
|
+
...baseParsedArgs(),
|
|
30
65
|
operation,
|
|
31
|
-
|
|
32
|
-
|
|
66
|
+
...(options.cloud || options.qoderCloud
|
|
67
|
+
? { cloudMode: options.qoderCloud ? 'qoder' : 'websocket' }
|
|
68
|
+
: {}),
|
|
69
|
+
...(profiles.length > 0 ? { profiles } : {}),
|
|
70
|
+
skipSkill: options.skipSkill === true,
|
|
71
|
+
targetLine: options.targetLine,
|
|
72
|
+
yes: options.yes === true || options.nonInteractive === true,
|
|
33
73
|
};
|
|
74
|
+
});
|
|
75
|
+
program.addCommand(command);
|
|
76
|
+
};
|
|
77
|
+
addLifecycleCommand('install');
|
|
78
|
+
addLifecycleCommand('update');
|
|
79
|
+
addLifecycleCommand('uninstall');
|
|
80
|
+
const extension = new Command('extension')
|
|
81
|
+
.exitOverride()
|
|
82
|
+
.configureOutput({ writeErr: () => { }, writeOut: () => { } })
|
|
83
|
+
.option('--full')
|
|
84
|
+
.option('--force')
|
|
85
|
+
.option('-y, --yes')
|
|
86
|
+
.option('--non-interactive')
|
|
87
|
+
.action(() => {
|
|
88
|
+
const options = extension.opts();
|
|
89
|
+
parsed = {
|
|
90
|
+
...baseParsedArgs(),
|
|
91
|
+
...(options.force ? { force: true } : {}),
|
|
92
|
+
...(options.full ? { full: true } : {}),
|
|
93
|
+
operation: 'extension',
|
|
94
|
+
yes: options.yes === true || options.nonInteractive === true,
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
program.addCommand(extension);
|
|
98
|
+
const agentbay = new Command('agentbay')
|
|
99
|
+
.exitOverride()
|
|
100
|
+
.configureOutput({ writeErr: () => { }, writeOut: () => { } })
|
|
101
|
+
.argument('[api-key]')
|
|
102
|
+
.option('--stdin')
|
|
103
|
+
.option('--key-file <path>')
|
|
104
|
+
.action((apiKey) => {
|
|
105
|
+
const options = agentbay.opts();
|
|
106
|
+
if (apiKey) {
|
|
107
|
+
throw new Error('API keys are not accepted as command-line arguments');
|
|
34
108
|
}
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
agentBayReadStdin: true,
|
|
38
|
-
help: false,
|
|
39
|
-
operation,
|
|
40
|
-
skipSkill: false,
|
|
41
|
-
yes: false,
|
|
42
|
-
};
|
|
109
|
+
if (options.stdin && options.keyFile) {
|
|
110
|
+
throw new Error('--stdin and --key-file cannot be used together');
|
|
43
111
|
}
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
agentBayKeyFile: argv[2],
|
|
47
|
-
agentBayReadStdin: false,
|
|
48
|
-
help: false,
|
|
49
|
-
operation,
|
|
50
|
-
skipSkill: false,
|
|
51
|
-
yes: false,
|
|
52
|
-
};
|
|
112
|
+
if (options.keyFile !== undefined && !options.keyFile.trim()) {
|
|
113
|
+
throw new Error('--key-file requires a non-empty path');
|
|
53
114
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const unknown = argv.slice(1).find(arg => !accepted.has(arg));
|
|
60
|
-
if (unknown)
|
|
61
|
-
throw new Error(`Unknown option: ${unknown}`);
|
|
62
|
-
return {
|
|
63
|
-
agentBayReadStdin: false,
|
|
64
|
-
help: false,
|
|
65
|
-
...(argv.slice(1).includes('--force') ? { force: true } : {}),
|
|
66
|
-
operation,
|
|
67
|
-
skipSkill: false,
|
|
68
|
-
yes: argv.slice(1).some(arg => confirmationFlags.has(arg)),
|
|
115
|
+
parsed = {
|
|
116
|
+
...baseParsedArgs(),
|
|
117
|
+
...(options.keyFile ? { agentBayKeyFile: options.keyFile } : {}),
|
|
118
|
+
agentBayReadStdin: options.stdin === true,
|
|
119
|
+
operation: 'agentbay',
|
|
69
120
|
};
|
|
121
|
+
});
|
|
122
|
+
program.addCommand(agentbay);
|
|
123
|
+
const normalizedArgv = argv[0] === undefined || argv[0].startsWith('-') ? ['install', ...argv] : argv;
|
|
124
|
+
try {
|
|
125
|
+
program.parse(['node', 'mearl-setup', ...normalizedArgv]);
|
|
70
126
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
let cloudMode;
|
|
79
|
-
for (let index = optionStart; index < argv.length; index += 1) {
|
|
80
|
-
const arg = argv[index];
|
|
81
|
-
if (arg === '--yes' || arg === '-y' || arg === '--non-interactive') {
|
|
82
|
-
yes = true;
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
if (arg === '--skip-skill') {
|
|
86
|
-
skipSkill = true;
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
if (arg === '--target-line') {
|
|
90
|
-
const value = argv[index + 1];
|
|
91
|
-
if (!isMearlReleaseLine(value)) {
|
|
92
|
-
throw new Error('--target-line requires a valid Mearl release line such as 2.3.x');
|
|
93
|
-
}
|
|
94
|
-
targetLine = value;
|
|
95
|
-
index += 1;
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (arg === '--cloud' || arg === '--qoder-cloud') {
|
|
99
|
-
const requestedMode = arg === '--qoder-cloud' ? 'qoder' : 'websocket';
|
|
100
|
-
if (cloudMode && cloudMode !== requestedMode) {
|
|
101
|
-
throw new Error('--cloud and --qoder-cloud cannot be used together');
|
|
102
|
-
}
|
|
103
|
-
cloudMode = requestedMode;
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (error instanceof CommanderError) {
|
|
129
|
+
if (error.code === 'commander.helpDisplayed')
|
|
130
|
+
return { ...baseParsedArgs(), help: true };
|
|
131
|
+
if (error.code === 'commander.version')
|
|
132
|
+
return { ...baseParsedArgs(), version: true };
|
|
133
|
+
throw commanderError(error);
|
|
104
134
|
}
|
|
105
|
-
|
|
106
|
-
? 'local'
|
|
107
|
-
: arg === '--cloud' || arg === '--qoder-cloud'
|
|
108
|
-
? 'cloud'
|
|
109
|
-
: arg === '--mcp'
|
|
110
|
-
? 'mcp'
|
|
111
|
-
: undefined;
|
|
112
|
-
if (!candidate)
|
|
113
|
-
throw new Error(`Unknown option: ${arg}`);
|
|
114
|
-
if (!profiles.includes(candidate))
|
|
115
|
-
profiles.push(candidate);
|
|
135
|
+
throw error;
|
|
116
136
|
}
|
|
117
|
-
if (
|
|
118
|
-
throw new Error('
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
agentBayReadStdin: false,
|
|
122
|
-
help: false,
|
|
123
|
-
operation,
|
|
124
|
-
...(cloudMode ? { cloudMode } : {}),
|
|
125
|
-
...(profiles.length > 0 ? { profiles } : {}),
|
|
126
|
-
skipSkill,
|
|
127
|
-
targetLine,
|
|
128
|
-
yes,
|
|
129
|
-
};
|
|
137
|
+
if (!parsed)
|
|
138
|
+
throw new Error('No setup operation was selected');
|
|
139
|
+
return parsed;
|
|
130
140
|
}
|
|
131
141
|
function printHelp(toError = false) {
|
|
132
142
|
const output = `Mearl Setup v${SETUP_VERSION}
|
|
@@ -134,14 +144,14 @@ function printHelp(toError = false) {
|
|
|
134
144
|
Usage:
|
|
135
145
|
npx @mearl/setup [install] [--local] [--cloud|--qoder-cloud] [--mcp] [--yes]
|
|
136
146
|
npx @mearl/setup <update|uninstall> [--local] [--cloud|--qoder-cloud] [--mcp] [--yes]
|
|
137
|
-
npx @mearl/setup extension [--force] [--yes]
|
|
147
|
+
npx @mearl/setup extension [--full] [--force] [--yes]
|
|
138
148
|
npx @mearl/setup agentbay [--stdin | --key-file <path>]
|
|
139
149
|
|
|
140
150
|
Behavior:
|
|
141
151
|
install Default command; defaults to --local when no environment is specified
|
|
142
152
|
update Auto-detects installed environments from the current PATH
|
|
143
153
|
uninstall Auto-detects installed environments and asks for confirmation
|
|
144
|
-
extension
|
|
154
|
+
extension Install or update the browser extension; --full also installs the local environment
|
|
145
155
|
agentbay Save or update AGENTBAY_KEY without changing other Mearl settings
|
|
146
156
|
|
|
147
157
|
Options:
|
|
@@ -155,6 +165,7 @@ Options:
|
|
|
155
165
|
--target-line <major.minor.x>
|
|
156
166
|
Install the latest packages from one release line, such as 2.3.x
|
|
157
167
|
--skip-skill Keep the independently managed Mearl Skill unchanged
|
|
168
|
+
--full Also install @mearl/client, @mearl/native-host, and the Mearl Skill
|
|
158
169
|
--force Replace an existing store, managed, or other local Mearl extension
|
|
159
170
|
--yes, -y Run without prompts and accept detected defaults
|
|
160
171
|
--non-interactive
|
|
@@ -171,6 +182,7 @@ Examples:
|
|
|
171
182
|
npx @mearl/setup --local --cloud
|
|
172
183
|
npx @mearl/setup update
|
|
173
184
|
npx @mearl/setup extension
|
|
185
|
+
npx @mearl/setup extension --full
|
|
174
186
|
npx @mearl/setup agentbay
|
|
175
187
|
npx @mearl/setup --qoder-cloud --yes
|
|
176
188
|
npx @mearl/setup update --local --target-line 2.3.x --skip-skill
|
|
@@ -228,6 +240,7 @@ export async function main(argv = process.argv.slice(2), interactiveTerminal = p
|
|
|
228
240
|
console.error('Non-interactive input detected. Re-run with --yes or --non-interactive.');
|
|
229
241
|
return 2;
|
|
230
242
|
}
|
|
243
|
+
let extensionInstalled = false;
|
|
231
244
|
try {
|
|
232
245
|
const result = await (dependencies.installBrowserExtension ?? installMearlBrowserExtension)({
|
|
233
246
|
force: parsed.force,
|
|
@@ -240,20 +253,35 @@ export async function main(argv = process.argv.slice(2), interactiveTerminal = p
|
|
|
240
253
|
: `Browser extension v${result.version} installed or reloaded through CDP.`);
|
|
241
254
|
console.error(`Extension ID: ${result.extensionId}`);
|
|
242
255
|
console.error(`Extension directory: ${result.path}`);
|
|
243
|
-
|
|
256
|
+
extensionInstalled = true;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
console.error(result.filesUpdated
|
|
260
|
+
? `Browser extension v${result.version} files were updated, but the extension was not reloaded.`
|
|
261
|
+
: `Browser extension v${result.version} was not installed.`);
|
|
262
|
+
if (result.cdpError)
|
|
263
|
+
console.error(`CDP installation pending: ${result.cdpError}`);
|
|
264
|
+
console.error('Open chrome://inspect/#remote-debugging, enable remote debugging, approve the connection, then rerun: npx @mearl/setup extension');
|
|
244
265
|
}
|
|
245
|
-
console.error(result.filesUpdated
|
|
246
|
-
? `Browser extension v${result.version} files were updated, but the extension was not reloaded.`
|
|
247
|
-
: `Browser extension v${result.version} was not installed.`);
|
|
248
|
-
if (result.cdpError)
|
|
249
|
-
console.error(`CDP installation pending: ${result.cdpError}`);
|
|
250
|
-
console.error('Open chrome://inspect/#remote-debugging, enable remote debugging, approve the connection, then rerun: npx @mearl/setup extension');
|
|
251
|
-
return 1;
|
|
252
266
|
}
|
|
253
267
|
catch (error) {
|
|
254
268
|
console.error(error instanceof Error ? error.message : String(error));
|
|
255
|
-
return 1;
|
|
256
269
|
}
|
|
270
|
+
if (!parsed.full)
|
|
271
|
+
return extensionInstalled ? 0 : 1;
|
|
272
|
+
let localInstalled = false;
|
|
273
|
+
try {
|
|
274
|
+
localInstalled = await (dependencies.runLifecycle ?? runMearlLifecycle)({
|
|
275
|
+
operation: 'install',
|
|
276
|
+
profiles: ['local'],
|
|
277
|
+
skipSkill: false,
|
|
278
|
+
yes: parsed.yes,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
283
|
+
}
|
|
284
|
+
return extensionInstalled && localInstalled ? 0 : 1;
|
|
257
285
|
}
|
|
258
286
|
if (!interactiveTerminal && !parsed.yes && parsed.operation !== 'update') {
|
|
259
287
|
console.error('Non-interactive input detected. Re-run with --yes or --non-interactive.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/setup",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.1",
|
|
4
4
|
"description": "One-shot environment setup and configuration for Mearl",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -56,15 +56,16 @@
|
|
|
56
56
|
],
|
|
57
57
|
"license": "ISC",
|
|
58
58
|
"engines": {
|
|
59
|
-
"node": ">=
|
|
59
|
+
"node": ">=22.12.0"
|
|
60
60
|
},
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"registry": "https://registry.npmjs.org"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
+
"commander": "^15.0.0",
|
|
65
66
|
"extract-zip": "^2.0.1",
|
|
66
67
|
"ws": "^8.18.3",
|
|
67
|
-
"@mearl/client": "2.
|
|
68
|
+
"@mearl/client": "2.9.1"
|
|
68
69
|
},
|
|
69
70
|
"devDependencies": {
|
|
70
71
|
"@types/node": "^24.9.1",
|