Sign In

@mearl/setup

Package Overview
Dependencies
Maintainers
2
Versions
11
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.7.1
to
2.7.2
+4
-4
dist/cli.d.ts
#!/usr/bin/env node
import { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin } from './agentBay.js';
import { type MearlLifecycleOperation } from './lifecycle.js';
import { runMearlLifecycle, type MearlLifecycleOperation } from './lifecycle.js';
import type { MearlProfile } from './profiles.js';
import { createQoderPairingInfo } from './qoder.js';
export declare const SETUP_VERSION: string;

@@ -10,4 +9,5 @@ interface ParsedArgs {

agentBayReadStdin: boolean;
cloudMode?: 'websocket' | 'qoder';
help: boolean;
operation?: MearlLifecycleOperation | 'agentbay' | 'qoder';
operation?: MearlLifecycleOperation | 'agentbay';
profiles?: MearlProfile[];

@@ -24,3 +24,3 @@ skipSkill: boolean;

readAgentBayKeyStdin?: typeof readAgentBayApiKeyStdin;
createQoderPairing?: typeof createQoderPairingInfo;
runLifecycle?: typeof runMearlLifecycle;
}

@@ -27,0 +27,0 @@ export declare function parseSetupArgs(argv: string[]): ParsedArgs;

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

import { runMearlLifecycle } from './lifecycle.js';
import { createQoderPairingInfo, formatQoderPairingOutput } from './qoder.js';
import { isMearlReleaseLine } from './version.js';

@@ -57,16 +56,2 @@ const require = createRequire(import.meta.url);

}
if (operation === 'qoder') {
const options = argv.slice(1);
const allowed = new Set(['--yes', '-y', '--non-interactive']);
if (options.some(option => !allowed.has(option))) {
throw new Error('Usage: npx @mearl/setup qoder [--yes]');
}
return {
agentBayReadStdin: false,
help: false,
operation,
skipSkill: false,
yes: options.length > 0,
};
}
if (operation !== 'install' && operation !== 'update' && operation !== 'uninstall') {

@@ -79,2 +64,3 @@ throw new Error(`Unknown operation: ${operation}`);

let yes = false;
let cloudMode;
for (let index = optionStart; index < argv.length; index += 1) {

@@ -99,5 +85,12 @@ const arg = argv[index];

}
if (arg === '--cloud' || arg === '--qoder-cloud') {
const requestedMode = arg === '--qoder-cloud' ? 'qoder' : 'websocket';
if (cloudMode && cloudMode !== requestedMode) {
throw new Error('--cloud and --qoder-cloud cannot be used together');
}
cloudMode = requestedMode;
}
const candidate = arg === '--local'
? 'local'
: arg === '--cloud'
: arg === '--cloud' || arg === '--qoder-cloud'
? 'cloud'

@@ -119,2 +112,3 @@ : arg === '--mcp'

operation,
...(cloudMode ? { cloudMode } : {}),
...(profiles.length > 0 ? { profiles } : {}),

@@ -130,6 +124,5 @@ skipSkill,

Usage:
npx @mearl/setup [install] [--local] [--cloud] [--mcp] [--yes]
npx @mearl/setup <update|uninstall> [--local] [--cloud] [--mcp] [--yes]
npx @mearl/setup [install] [--local] [--cloud|--qoder-cloud] [--mcp] [--yes]
npx @mearl/setup <update|uninstall> [--local] [--cloud|--qoder-cloud] [--mcp] [--yes]
npx @mearl/setup agentbay [--stdin | --key-file <path>]
npx @mearl/setup qoder [--yes]

@@ -141,7 +134,8 @@ Behavior:

agentbay Save or update AGENTBAY_KEY without changing other Mearl settings
qoder Create a Qoder client-side tool pairing request for troubleshooting
Options:
--local Manage @mearl/native-host and @mearl/client
--cloud Manage @mearl/cloud-server and the unified @mearl/client
--cloud Manage cloud packages in WebSocket server mode
--qoder-cloud
Manage the same cloud packages in Qoder pairing mode
--mcp Manage @mearl/mcp-server and its configuration

@@ -168,3 +162,3 @@ --stdin Read the AgentBay API key from standard input

npx @mearl/setup agentbay
npx @mearl/setup qoder --yes
npx @mearl/setup --qoder-cloud --yes
npx @mearl/setup update --local --target-line 2.3.x --skip-skill

@@ -220,13 +214,2 @@ npx @mearl/setup uninstall --local`;

}
if (parsed.operation === 'qoder') {
try {
const pairing = (dependencies.createQoderPairing ?? createQoderPairingInfo)();
console.log(formatQoderPairingOutput(pairing));
return 0;
}
catch (error) {
console.error(error instanceof Error ? error.message : String(error));
return 1;
}
}
if (!interactiveTerminal && !parsed.yes && parsed.operation !== 'update') {

@@ -237,5 +220,6 @@ console.error('Non-interactive input detected. Re-run with --yes or --non-interactive.');

try {
const ok = await runMearlLifecycle({
const ok = await (dependencies.runLifecycle ?? runMearlLifecycle)({
operation: parsed.operation,
profiles: parsed.profiles,
cloudMode: parsed.cloudMode,
skipSkill: parsed.skipSkill,

@@ -242,0 +226,0 @@ targetLine: parsed.targetLine,

export { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';
export { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
export type { AgentBayConfigurationOptions, AgentBayConfigurationResult, AgentBaySecretInput, AgentBaySecretOutput, } from './agentBay.js';
export { createQoderPairingInfo, formatQoderPairingOutput } from './qoder.js';
export type { QoderPairingInfo, QoderPairingOptions } from './qoder.js';
export type { DetectedGlobalPackage, GlobalPackageCommand, GlobalPackageManager, PackageManagerInstallation, PackageOperation, PackageDetectionOptions, } from './packageManager.js';

@@ -7,0 +5,0 @@ export { runMearlLifecycle } from './lifecycle.js';

export { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';
export { configureAgentBayApiKey, promptAgentBayApiKey, readAgentBayApiKeyFile, readAgentBayApiKeyStdin, } from './agentBay.js';
export { createQoderPairingInfo, formatQoderPairingOutput } from './qoder.js';
export { runMearlLifecycle } from './lifecycle.js';

@@ -5,0 +4,0 @@ export { MEARL_PROFILES, detectMearlProfiles } from './profiles.js';

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

operation: MearlLifecycleOperation;
cloudMode?: 'websocket' | 'qoder';
/** @deprecated Use profiles when selecting one or more composable environments. */

@@ -11,0 +12,0 @@ profile?: MearlProfile;

@@ -59,5 +59,6 @@ import fs from 'node:fs';

}
async function runHook(profile, operation, detected, runner, existsSync, env, writeLine, yes) {
async function runHook(profile, operation, detected, runner, existsSync, env, writeLine, yes, cloudMode) {
let executable;
let args;
let hookEnv = env;
if (profile === 'local' && operation !== 'uninstall') {

@@ -85,2 +86,5 @@ executable = detected.executable;

}
const requestedMode = operation === 'uninstall' ? 'websocket' : cloudMode;
const commandEnv = requestedMode ? { ...env, MEARL_CLOUD_MODE: requestedMode } : env;
hookEnv = commandEnv;
if (operation === 'uninstall') {

@@ -90,3 +94,3 @@ args = ['stop'];

else {
const status = await runLifecycleCommand(runner, executable, ['status'], env, 'Hook: cloud status', writeLine, true);
const status = await runLifecycleCommand(runner, executable, ['status'], commandEnv, 'Hook: cloud status', writeLine, true);
if (!status)

@@ -99,7 +103,8 @@ return false;

const running = /\[CloudServer\]\s+Status:\s+running\b/.test(status.stdout);
if (operation === 'update' && !running) {
const qoderMode = cloudMode === 'qoder' || /\[CloudServer\]\s+Mode:\s+qoder\b/.test(status.stdout);
if (operation === 'update' && !running && !qoderMode) {
writeLine('Hook: cloud update skipped because cloud-server is not running.');
return true;
}
args = [running ? 'restart' : 'start'];
args = [running && !qoderMode ? 'restart' : 'start'];
}

@@ -114,3 +119,3 @@ }

}
const result = await runLifecycleCommand(runner, executable, args, env, `Hook: ${profile} ${operation}`, writeLine);
const result = await runLifecycleCommand(runner, executable, args, hookEnv, `Hook: ${profile} ${operation}`, writeLine);
if (!result)

@@ -241,3 +246,3 @@ return false;

if (options.operation === 'uninstall' &&
!(await runHook(profile, options.operation, current, runner, existsSync, env, writeLine, options.yes === true))) {
!(await runHook(profile, options.operation, current, runner, existsSync, env, writeLine, options.yes === true, options.cloudMode))) {
packagesOk = false;

@@ -262,3 +267,3 @@ continue;

if (!refreshed ||
!(await runHook(profile, options.operation, refreshed, runner, existsSync, env, writeLine, options.yes === true))) {
!(await runHook(profile, options.operation, refreshed, runner, existsSync, env, writeLine, options.yes === true, options.cloudMode))) {
packagesOk = false;

@@ -265,0 +270,0 @@ }

{
"name": "@mearl/setup",
"version": "2.7.1",
"version": "2.7.2",
"description": "One-shot environment setup and configuration for Mearl",

@@ -33,6 +33,2 @@ "type": "module",

},
"./qoder": {
"types": "./dist/qoder.d.ts",
"import": "./dist/qoder.js"
},
"./version": {

@@ -39,0 +35,0 @@ "types": "./dist/version.d.ts",

@@ -6,4 +6,4 @@ # @mearl/setup

```bash
npx @mearl/setup [install] [--local] [--cloud] [--mcp] [--yes]
npx @mearl/setup <update|uninstall> [--local] [--cloud] [--mcp] [--yes]
npx @mearl/setup [install] [--local] [--cloud|--qoder-cloud] [--mcp] [--yes]
npx @mearl/setup <update|uninstall> [--local] [--cloud|--qoder-cloud] [--mcp] [--yes]
```

@@ -21,5 +21,6 @@

Qoder Cloud Agent 仅在常规云连接失败、Mearl Skill 进入 Qoder 异常接管分支时使用
`npx @mearl/setup qoder --yes` 生成一次性配对指令。它不安装额外隧道,也不会把 Qoder
PAT 写入配对 URL;正常安装和连接流程无需执行该命令。
在 Qoder Cloud Agent 环境中,使用 `npx @mearl/setup --qoder-cloud --yes` 选择 Qoder
配对模式。它与 `--cloud` 安装同一套 cloud-server、client 和 Skill,只把 cloud-server
模式记录为 Qoder;`mearl-cloud-server` 的命令交互保持不变,`start` 和 `restart` 输出新的
配对指令后退出,不监听端口或保留守护进程。配对 URL 不包含 Qoder PAT。

@@ -35,6 +36,8 @@ ## 行为

多个已安装环境。
- `--local`、`--cloud` 与 `--mcp` 可以组合,显式传入时只处理选中的环境。
- `--local`、`--cloud` 与 `--mcp` 可以组合,显式传入时只处理选中的环境;
`--qoder-cloud` 选择相同的 cloud 环境并记录 Qoder 配对模式,不能与 `--cloud` 同时使用。
- 已有环境沿用其原包管理器和全局安装前缀;首次安装沿用启动 setup 的包管理器。
- cloud 环境统一管理 `@mearl/cloud-server` 与 `@mearl/client`:首次安装后启动
Server,更新后仅在 Server 原本运行时重启,卸载前停止。
- cloud 环境统一管理 `@mearl/cloud-server` 与 `@mearl/client`:WebSocket 模式首次安装后
启动 Server,更新后仅在 Server 原本运行时重启;Qoder 模式由相同的 `start` 命令生成
新配对但不起服务;卸载前均调用 `stop`。
- local/cloud 共用一份 `@mearl/client`;组合安装只处理一次,卸载单个环境时若另一个环境仍在使用则保留。

@@ -41,0 +44,0 @@ - 从 2.5.x 更新 cloud 环境时会先卸载旧 `@mearl/cloud-client`,避免它遗留的 `mearl` bin 与统一 client 冲突。

export interface QoderPairingInfo {
pairingCode: string;
createdAt: number;
connectorUrl: string;
toolInput: {
action: 'pair';
payload: {
code: string;
};
};
}
export interface QoderPairingOptions {
now?: () => number;
createCode?: () => string;
}
/** Create a short-lived correlation handle without embedding Qoder credentials in the URL. */
export declare function createQoderPairingInfo(options?: QoderPairingOptions): QoderPairingInfo;
export declare function formatQoderPairingOutput(info: QoderPairingInfo): string;
import { randomBytes } from 'node:crypto';
/** Create a short-lived correlation handle without embedding Qoder credentials in the URL. */
export function createQoderPairingInfo(options = {}) {
const pairingCode = (options.createCode ?? (() => randomBytes(24).toString('base64url')))();
if (!/^[A-Za-z0-9_-]{16,128}$/.test(pairingCode)) {
throw new Error('Qoder pairing code must be 16-128 URL-safe characters');
}
const createdAt = (options.now ?? Date.now)();
if (!Number.isSafeInteger(createdAt) ||
createdAt < 1_000_000_000_000 ||
createdAt > 9_999_999_999_999) {
throw new Error('Qoder pairing timestamp must be a 13-digit epoch millisecond value');
}
return {
pairingCode,
createdAt,
connectorUrl: `qoder://pair/${pairingCode}?created=${createdAt}`,
toolInput: { action: 'pair', payload: { code: pairingCode } },
};
}
export function formatQoderPairingOutput(info) {
const connectorCommand = `npx @mearl/cloud-connector "${info.connectorUrl}"`;
return `[Qoder] Mearl local-browser pairing is ready.
Show the user this command and ask them to run it on the computer that owns the browser:
${connectorCommand}
Then immediately invoke the Qoder client-side custom tool named "mearl" with this input:
${JSON.stringify(info.toolInput)}
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.`;
}