@mearl/setup 2.7.1 → 2.7.2

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
@@ -3,8 +3,8 @@
3
3
  Mearl 环境的一次性安装、更新、卸载与配置工具。无需先全局安装 setup:
4
4
 
5
5
  ```bash
6
- npx @mearl/setup [install] [--local] [--cloud] [--mcp] [--yes]
7
- npx @mearl/setup <update|uninstall> [--local] [--cloud] [--mcp] [--yes]
6
+ npx @mearl/setup [install] [--local] [--cloud|--qoder-cloud] [--mcp] [--yes]
7
+ npx @mearl/setup <update|uninstall> [--local] [--cloud|--qoder-cloud] [--mcp] [--yes]
8
8
  ```
9
9
 
10
10
  配置 AgentBay API Key 时只需执行一次下面的命令,然后在隐藏输入提示中粘贴 Key。
@@ -17,9 +17,10 @@ npx @mearl/setup agentbay
17
17
  无交互环境可使用 `--stdin` 或 `--key-file <path>`。为避免 Key 进入 shell history 和
18
18
  进程参数,不接受把 Key 直接写在命令行中。
19
19
 
20
- Qoder Cloud Agent 仅在常规云连接失败、Mearl Skill 进入 Qoder 异常接管分支时使用
21
- `npx @mearl/setup qoder --yes` 生成一次性配对指令。它不安装额外隧道,也不会把 Qoder
22
- PAT 写入配对 URL;正常安装和连接流程无需执行该命令。
20
+ 在 Qoder Cloud Agent 环境中,使用 `npx @mearl/setup --qoder-cloud --yes` 选择 Qoder
21
+ 配对模式。它与 `--cloud` 安装同一套 cloud-server、client 和 Skill,只把 cloud-server
22
+ 模式记录为 Qoder;`mearl-cloud-server` 的命令交互保持不变,`start` 和 `restart` 输出新的
23
+ 配对指令后退出,不监听端口或保留守护进程。配对 URL 不包含 Qoder PAT。
23
24
 
24
25
  ## 行为
25
26
 
@@ -30,10 +31,12 @@ PAT 写入配对 URL;正常安装和连接流程无需执行该命令。
30
31
  - `update` 和 `uninstall` 未指定环境时,从当前 `PATH` 中的
31
32
  `mearl-native-host`、`mearl-cloud-server`、`mearl-mcp-server` 自动检测,可同时处理
32
33
  多个已安装环境。
33
- - `--local`、`--cloud` 与 `--mcp` 可以组合,显式传入时只处理选中的环境。
34
+ - `--local`、`--cloud` 与 `--mcp` 可以组合,显式传入时只处理选中的环境;
35
+ `--qoder-cloud` 选择相同的 cloud 环境并记录 Qoder 配对模式,不能与 `--cloud` 同时使用。
34
36
  - 已有环境沿用其原包管理器和全局安装前缀;首次安装沿用启动 setup 的包管理器。
35
- - cloud 环境统一管理 `@mearl/cloud-server` 与 `@mearl/client`:首次安装后启动
36
- Server,更新后仅在 Server 原本运行时重启,卸载前停止。
37
+ - cloud 环境统一管理 `@mearl/cloud-server` 与 `@mearl/client`:WebSocket 模式首次安装后
38
+ 启动 Server,更新后仅在 Server 原本运行时重启;Qoder 模式由相同的 `start` 命令生成
39
+ 新配对但不起服务;卸载前均调用 `stop`。
37
40
  - local/cloud 共用一份 `@mearl/client`;组合安装只处理一次,卸载单个环境时若另一个环境仍在使用则保留。
38
41
  - 从 2.5.x 更新 cloud 环境时会先卸载旧 `@mearl/cloud-client`,避免它遗留的 `mearl` bin 与统一 client 冲突。
39
42
  - local/cloud 共用的全局 mearl skill 通过 `ali-skills` 的 JSON 清单定位,只处理一次。
package/dist/cli.d.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin } from './agentBay.js';
3
- import { type MearlLifecycleOperation } from './lifecycle.js';
3
+ import { runMearlLifecycle, type MearlLifecycleOperation } from './lifecycle.js';
4
4
  import type { MearlProfile } from './profiles.js';
5
- import { createQoderPairingInfo } from './qoder.js';
6
5
  export declare const SETUP_VERSION: string;
7
6
  interface ParsedArgs {
8
7
  agentBayKeyFile?: string;
9
8
  agentBayReadStdin: boolean;
9
+ cloudMode?: 'websocket' | 'qoder';
10
10
  help: boolean;
11
- operation?: MearlLifecycleOperation | 'agentbay' | 'qoder';
11
+ operation?: MearlLifecycleOperation | 'agentbay';
12
12
  profiles?: MearlProfile[];
13
13
  skipSkill: boolean;
14
14
  targetLine?: string;
@@ -20,7 +20,7 @@ export interface SetupCliDependencies {
20
20
  promptAgentBayKey?: typeof promptAgentBayApiKey;
21
21
  readAgentBayKeyFile?: typeof readAgentBayApiKeyFile;
22
22
  readAgentBayKeyStdin?: typeof readAgentBayApiKeyStdin;
23
- createQoderPairing?: typeof createQoderPairingInfo;
23
+ runLifecycle?: typeof runMearlLifecycle;
24
24
  }
25
25
  export declare function parseSetupArgs(argv: string[]): ParsedArgs;
26
26
  export declare function main(argv?: string[], interactiveTerminal?: boolean, dependencies?: SetupCliDependencies): Promise<number>;
package/dist/cli.js CHANGED
@@ -2,7 +2,6 @@
2
2
  import { createRequire } from 'node:module';
3
3
  import { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
4
4
  import { runMearlLifecycle } from './lifecycle.js';
5
- import { createQoderPairingInfo, formatQoderPairingOutput } from './qoder.js';
6
5
  import { isMearlReleaseLine } from './version.js';
7
6
  const require = createRequire(import.meta.url);
8
7
  export const SETUP_VERSION = require('../package.json').version;
@@ -53,20 +52,6 @@ export function parseSetupArgs(argv) {
53
52
  }
54
53
  throw new Error('Usage: npx @mearl/setup agentbay [--stdin | --key-file <path>]. API keys are not accepted as command-line arguments.');
55
54
  }
56
- if (operation === 'qoder') {
57
- const options = argv.slice(1);
58
- const allowed = new Set(['--yes', '-y', '--non-interactive']);
59
- if (options.some(option => !allowed.has(option))) {
60
- throw new Error('Usage: npx @mearl/setup qoder [--yes]');
61
- }
62
- return {
63
- agentBayReadStdin: false,
64
- help: false,
65
- operation,
66
- skipSkill: false,
67
- yes: options.length > 0,
68
- };
69
- }
70
55
  if (operation !== 'install' && operation !== 'update' && operation !== 'uninstall') {
71
56
  throw new Error(`Unknown operation: ${operation}`);
72
57
  }
@@ -74,6 +59,7 @@ export function parseSetupArgs(argv) {
74
59
  let skipSkill = false;
75
60
  let targetLine;
76
61
  let yes = false;
62
+ let cloudMode;
77
63
  for (let index = optionStart; index < argv.length; index += 1) {
78
64
  const arg = argv[index];
79
65
  if (arg === '--yes' || arg === '-y' || arg === '--non-interactive') {
@@ -93,9 +79,16 @@ export function parseSetupArgs(argv) {
93
79
  index += 1;
94
80
  continue;
95
81
  }
82
+ if (arg === '--cloud' || arg === '--qoder-cloud') {
83
+ const requestedMode = arg === '--qoder-cloud' ? 'qoder' : 'websocket';
84
+ if (cloudMode && cloudMode !== requestedMode) {
85
+ throw new Error('--cloud and --qoder-cloud cannot be used together');
86
+ }
87
+ cloudMode = requestedMode;
88
+ }
96
89
  const candidate = arg === '--local'
97
90
  ? 'local'
98
- : arg === '--cloud'
91
+ : arg === '--cloud' || arg === '--qoder-cloud'
99
92
  ? 'cloud'
100
93
  : arg === '--mcp'
101
94
  ? 'mcp'
@@ -112,6 +105,7 @@ export function parseSetupArgs(argv) {
112
105
  agentBayReadStdin: false,
113
106
  help: false,
114
107
  operation,
108
+ ...(cloudMode ? { cloudMode } : {}),
115
109
  ...(profiles.length > 0 ? { profiles } : {}),
116
110
  skipSkill,
117
111
  targetLine,
@@ -122,21 +116,21 @@ function printHelp(toError = false) {
122
116
  const output = `Mearl Setup v${SETUP_VERSION}
123
117
 
124
118
  Usage:
125
- npx @mearl/setup [install] [--local] [--cloud] [--mcp] [--yes]
126
- npx @mearl/setup <update|uninstall> [--local] [--cloud] [--mcp] [--yes]
119
+ npx @mearl/setup [install] [--local] [--cloud|--qoder-cloud] [--mcp] [--yes]
120
+ npx @mearl/setup <update|uninstall> [--local] [--cloud|--qoder-cloud] [--mcp] [--yes]
127
121
  npx @mearl/setup agentbay [--stdin | --key-file <path>]
128
- npx @mearl/setup qoder [--yes]
129
122
 
130
123
  Behavior:
131
124
  install Default command; defaults to --local when no environment is specified
132
125
  update Auto-detects installed environments from the current PATH
133
126
  uninstall Auto-detects installed environments and asks for confirmation
134
127
  agentbay Save or update AGENTBAY_KEY without changing other Mearl settings
135
- qoder Create a Qoder client-side tool pairing request for troubleshooting
136
128
 
137
129
  Options:
138
130
  --local Manage @mearl/native-host and @mearl/client
139
- --cloud Manage @mearl/cloud-server and the unified @mearl/client
131
+ --cloud Manage cloud packages in WebSocket server mode
132
+ --qoder-cloud
133
+ Manage the same cloud packages in Qoder pairing mode
140
134
  --mcp Manage @mearl/mcp-server and its configuration
141
135
  --stdin Read the AgentBay API key from standard input
142
136
  --key-file Read the AgentBay API key from a local file
@@ -158,7 +152,7 @@ Examples:
158
152
  npx @mearl/setup --local --cloud
159
153
  npx @mearl/setup update
160
154
  npx @mearl/setup agentbay
161
- npx @mearl/setup qoder --yes
155
+ npx @mearl/setup --qoder-cloud --yes
162
156
  npx @mearl/setup update --local --target-line 2.3.x --skip-skill
163
157
  npx @mearl/setup uninstall --local`;
164
158
  if (toError)
@@ -209,25 +203,15 @@ export async function main(argv = process.argv.slice(2), interactiveTerminal = p
209
203
  return 1;
210
204
  }
211
205
  }
212
- if (parsed.operation === 'qoder') {
213
- try {
214
- const pairing = (dependencies.createQoderPairing ?? createQoderPairingInfo)();
215
- console.log(formatQoderPairingOutput(pairing));
216
- return 0;
217
- }
218
- catch (error) {
219
- console.error(error instanceof Error ? error.message : String(error));
220
- return 1;
221
- }
222
- }
223
206
  if (!interactiveTerminal && !parsed.yes && parsed.operation !== 'update') {
224
207
  console.error('Non-interactive input detected. Re-run with --yes or --non-interactive.');
225
208
  return 2;
226
209
  }
227
210
  try {
228
- const ok = await runMearlLifecycle({
211
+ const ok = await (dependencies.runLifecycle ?? runMearlLifecycle)({
229
212
  operation: parsed.operation,
230
213
  profiles: parsed.profiles,
214
+ cloudMode: parsed.cloudMode,
231
215
  skipSkill: parsed.skipSkill,
232
216
  targetLine: parsed.targetLine,
233
217
  yes: parsed.yes,
package/dist/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  export { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';
2
2
  export { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
3
3
  export type { AgentBayConfigurationOptions, AgentBayConfigurationResult, AgentBaySecretInput, AgentBaySecretOutput, } from './agentBay.js';
4
- export { createQoderPairingInfo, formatQoderPairingOutput } from './qoder.js';
5
- export type { QoderPairingInfo, QoderPairingOptions } from './qoder.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,6 +1,5 @@
1
1
  export { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';
2
2
  export { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
3
- export { createQoderPairingInfo, formatQoderPairingOutput } from './qoder.js';
4
3
  export { runMearlLifecycle } from './lifecycle.js';
5
4
  export { MEARL_PROFILES, detectMearlProfiles } from './profiles.js';
6
5
  export { MEARL_NATIVE_HOST_NAMES, resolveNativeHostDirectory, uninstallNativeHostArtifacts, } from './nativeHost.js';
@@ -6,6 +6,7 @@ import { type CommandRunner } from './process.js';
6
6
  export type MearlLifecycleOperation = 'install' | 'update' | 'uninstall';
7
7
  export interface MearlLifecycleOptions {
8
8
  operation: MearlLifecycleOperation;
9
+ cloudMode?: 'websocket' | 'qoder';
9
10
  /** @deprecated Use profiles when selecting one or more composable environments. */
10
11
  profile?: MearlProfile;
11
12
  profiles?: MearlProfile[];
package/dist/lifecycle.js CHANGED
@@ -56,9 +56,10 @@ async function runPackageOperation(installation, operation, packages, targetLine
56
56
  }
57
57
  return { command, ok: true };
58
58
  }
59
- async function runHook(profile, operation, detected, runner, existsSync, env, writeLine, yes) {
59
+ async function runHook(profile, operation, detected, runner, existsSync, env, writeLine, yes, cloudMode) {
60
60
  let executable;
61
61
  let args;
62
+ let hookEnv = env;
62
63
  if (profile === 'local' && operation !== 'uninstall') {
63
64
  executable = detected.executable;
64
65
  args = ['--init', '--mode', 'skills'];
@@ -81,11 +82,14 @@ async function runHook(profile, operation, detected, runner, existsSync, env, wr
81
82
  writeLine(`Hook: ${profile} ${operation} command was not found.`);
82
83
  return false;
83
84
  }
85
+ const requestedMode = operation === 'uninstall' ? 'websocket' : cloudMode;
86
+ const commandEnv = requestedMode ? { ...env, MEARL_CLOUD_MODE: requestedMode } : env;
87
+ hookEnv = commandEnv;
84
88
  if (operation === 'uninstall') {
85
89
  args = ['stop'];
86
90
  }
87
91
  else {
88
- const status = await runLifecycleCommand(runner, executable, ['status'], env, 'Hook: cloud status', writeLine, true);
92
+ const status = await runLifecycleCommand(runner, executable, ['status'], commandEnv, 'Hook: cloud status', writeLine, true);
89
93
  if (!status)
90
94
  return false;
91
95
  if (status.code !== 0) {
@@ -93,11 +97,12 @@ async function runHook(profile, operation, detected, runner, existsSync, env, wr
93
97
  return false;
94
98
  }
95
99
  const running = /\[CloudServer\]\s+Status:\s+running\b/.test(status.stdout);
96
- if (operation === 'update' && !running) {
100
+ const qoderMode = cloudMode === 'qoder' || /\[CloudServer\]\s+Mode:\s+qoder\b/.test(status.stdout);
101
+ if (operation === 'update' && !running && !qoderMode) {
97
102
  writeLine('Hook: cloud update skipped because cloud-server is not running.');
98
103
  return true;
99
104
  }
100
- args = [running ? 'restart' : 'start'];
105
+ args = [running && !qoderMode ? 'restart' : 'start'];
101
106
  }
102
107
  }
103
108
  else {
@@ -107,7 +112,7 @@ async function runHook(profile, operation, detected, runner, existsSync, env, wr
107
112
  writeLine(`Hook: ${profile} ${operation} command was not found.`);
108
113
  return false;
109
114
  }
110
- const result = await runLifecycleCommand(runner, executable, args, env, `Hook: ${profile} ${operation}`, writeLine);
115
+ const result = await runLifecycleCommand(runner, executable, args, hookEnv, `Hook: ${profile} ${operation}`, writeLine);
111
116
  if (!result)
112
117
  return false;
113
118
  if (result.code !== 0) {
@@ -233,7 +238,7 @@ export async function runMearlLifecycle(options, dependencies = {}) {
233
238
  }
234
239
  }
235
240
  if (options.operation === 'uninstall' &&
236
- !(await runHook(profile, options.operation, current, runner, existsSync, env, writeLine, options.yes === true))) {
241
+ !(await runHook(profile, options.operation, current, runner, existsSync, env, writeLine, options.yes === true, options.cloudMode))) {
237
242
  packagesOk = false;
238
243
  continue;
239
244
  }
@@ -253,7 +258,7 @@ export async function runMearlLifecycle(options, dependencies = {}) {
253
258
  env: prependCommandPath(packageResult.command.command, env),
254
259
  }) ?? current;
255
260
  if (!refreshed ||
256
- !(await runHook(profile, options.operation, refreshed, runner, existsSync, env, writeLine, options.yes === true))) {
261
+ !(await runHook(profile, options.operation, refreshed, runner, existsSync, env, writeLine, options.yes === true, options.cloudMode))) {
257
262
  packagesOk = false;
258
263
  }
259
264
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/setup",
3
- "version": "2.7.1",
3
+ "version": "2.7.2",
4
4
  "description": "One-shot environment setup and configuration for Mearl",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,10 +30,6 @@
30
30
  "types": "./dist/agentBay.d.ts",
31
31
  "import": "./dist/agentBay.js"
32
32
  },
33
- "./qoder": {
34
- "types": "./dist/qoder.d.ts",
35
- "import": "./dist/qoder.js"
36
- },
37
33
  "./version": {
38
34
  "types": "./dist/version.d.ts",
39
35
  "import": "./dist/version.js"
package/dist/qoder.d.ts DELETED
@@ -1,18 +0,0 @@
1
- export interface QoderPairingInfo {
2
- pairingCode: string;
3
- createdAt: number;
4
- connectorUrl: string;
5
- toolInput: {
6
- action: 'pair';
7
- payload: {
8
- code: string;
9
- };
10
- };
11
- }
12
- export interface QoderPairingOptions {
13
- now?: () => number;
14
- createCode?: () => string;
15
- }
16
- /** Create a short-lived correlation handle without embedding Qoder credentials in the URL. */
17
- export declare function createQoderPairingInfo(options?: QoderPairingOptions): QoderPairingInfo;
18
- export declare function formatQoderPairingOutput(info: QoderPairingInfo): string;
package/dist/qoder.js DELETED
@@ -1,32 +0,0 @@
1
- import { randomBytes } from 'node:crypto';
2
- /** Create a short-lived correlation handle without embedding Qoder credentials in the URL. */
3
- export function createQoderPairingInfo(options = {}) {
4
- const pairingCode = (options.createCode ?? (() => randomBytes(24).toString('base64url')))();
5
- if (!/^[A-Za-z0-9_-]{16,128}$/.test(pairingCode)) {
6
- throw new Error('Qoder pairing code must be 16-128 URL-safe characters');
7
- }
8
- const createdAt = (options.now ?? Date.now)();
9
- if (!Number.isSafeInteger(createdAt) ||
10
- createdAt < 1_000_000_000_000 ||
11
- createdAt > 9_999_999_999_999) {
12
- throw new Error('Qoder pairing timestamp must be a 13-digit epoch millisecond value');
13
- }
14
- return {
15
- pairingCode,
16
- createdAt,
17
- connectorUrl: `qoder://pair/${pairingCode}?created=${createdAt}`,
18
- toolInput: { action: 'pair', payload: { code: pairingCode } },
19
- };
20
- }
21
- export function formatQoderPairingOutput(info) {
22
- const connectorCommand = `npx @mearl/cloud-connector "${info.connectorUrl}"`;
23
- return `[Qoder] Mearl local-browser pairing is ready.
24
-
25
- Show the user this command and ask them to run it on the computer that owns the browser:
26
- ${connectorCommand}
27
-
28
- Then immediately invoke the Qoder client-side custom tool named "mearl" with this input:
29
- ${JSON.stringify(info.toolInput)}
30
-
31
- The connector resolves QODER_PERSONAL_ACCESS_TOKEN from the local environment, ~/.mearl/.env, or supported local credential storage. Never add the PAT to the pairing URL.`;
32
- }