@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 +11 -8
- package/dist/cli.d.ts +4 -4
- package/dist/cli.js +18 -34
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -1
- package/dist/lifecycle.d.ts +1 -0
- package/dist/lifecycle.js +12 -7
- package/package.json +1 -5
- package/dist/qoder.d.ts +0 -18
- package/dist/qoder.js +0 -32
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
|
|
21
|
-
`
|
|
22
|
-
|
|
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'
|
|
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
|
-
|
|
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
|
|
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';
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -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'],
|
|
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
|
-
|
|
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,
|
|
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.
|
|
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
|
-
}
|