@mearl/cloud-server 2.8.2 → 2.9.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/dist/cli.d.ts +12 -1
- package/dist/cli.js +146 -79
- package/package.json +6 -5
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import type { CloudServerOptions } from './index.js';
|
|
3
|
+
import { type CloudServerCommand } from './qoder.js';
|
|
4
|
+
export interface ParsedCloudServerArgs {
|
|
5
|
+
command: CloudServerCommand;
|
|
6
|
+
daemonArgs: string[];
|
|
7
|
+
foreground: boolean;
|
|
8
|
+
help: boolean;
|
|
9
|
+
options: CloudServerOptions;
|
|
10
|
+
version: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function parseCloudServerArgs(argv: string[]): ParsedCloudServerArgs;
|
|
13
|
+
export declare function main(argv?: string[]): Promise<number>;
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
MODE_FILE
|
|
7
7
|
} from "./chunks/chunk-DCTZIDJ7.js";
|
|
8
8
|
|
|
9
|
+
// src/cli.ts
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12
|
+
import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
13
|
+
|
|
9
14
|
// src/daemon.ts
|
|
10
15
|
import { fileURLToPath } from "node:url";
|
|
11
16
|
import { createDaemon, readRecord, removeRecord } from "@mearl/daemon-core";
|
|
@@ -14,11 +19,11 @@ function formatConnectInfo(record) {
|
|
|
14
19
|
const config = record;
|
|
15
20
|
const lines = [];
|
|
16
21
|
if (config.port) lines.push(` port: ${config.port}`);
|
|
17
|
-
const
|
|
18
|
-
if (
|
|
22
|
+
const command = config.publicUrl ? `npx @mearl/cloud-connector start "${config.publicUrl}"` : config.connectorCommand;
|
|
23
|
+
if (command) {
|
|
19
24
|
lines.push("");
|
|
20
25
|
lines.push(" Connect from your local machine:");
|
|
21
|
-
lines.push(` ${
|
|
26
|
+
lines.push(` ${command}`);
|
|
22
27
|
}
|
|
23
28
|
return lines.length ? lines.join("\n") : null;
|
|
24
29
|
}
|
|
@@ -147,9 +152,9 @@ function formatQoderPairingOutput(info) {
|
|
|
147
152
|
|
|
148
153
|
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.`;
|
|
149
154
|
}
|
|
150
|
-
async function runQoderCloudServerCommand(
|
|
155
|
+
async function runQoderCloudServerCommand(command, dependencies) {
|
|
151
156
|
const writeLine = dependencies.writeLine ?? console.log;
|
|
152
|
-
switch (
|
|
157
|
+
switch (command) {
|
|
153
158
|
case "start":
|
|
154
159
|
case "restart":
|
|
155
160
|
if (dependencies.isServerRunning()) await dependencies.stopServer();
|
|
@@ -173,24 +178,18 @@ async function runQoderCloudServerCommand(command2, dependencies) {
|
|
|
173
178
|
}
|
|
174
179
|
|
|
175
180
|
// src/cli.ts
|
|
176
|
-
var CLOUD_SERVER_VERSION = true ? "2.
|
|
177
|
-
var argv = process.argv.slice(2);
|
|
178
|
-
if (argv.includes("--version") || argv.includes("-v")) {
|
|
179
|
-
console.log(CLOUD_SERVER_VERSION);
|
|
180
|
-
process.exit(0);
|
|
181
|
-
}
|
|
182
|
-
if (argv.includes("--help") || argv.includes("-h")) {
|
|
183
|
-
showHelp();
|
|
184
|
-
process.exit(0);
|
|
185
|
-
}
|
|
181
|
+
var CLOUD_SERVER_VERSION = true ? "2.9.0" : "unknown";
|
|
186
182
|
var COMMANDS = ["start", "stop", "restart", "status", "logs"];
|
|
187
|
-
var
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
183
|
+
var START_OPTION_NAMES = /* @__PURE__ */ new Set([
|
|
184
|
+
"foreground",
|
|
185
|
+
"port",
|
|
186
|
+
"agentPort",
|
|
187
|
+
"path",
|
|
188
|
+
"token",
|
|
189
|
+
"heartbeat",
|
|
190
|
+
"maxConnections",
|
|
191
|
+
"allowRemoteAgent"
|
|
192
|
+
]);
|
|
194
193
|
function showHelp() {
|
|
195
194
|
console.log(`mearl-cloud-server v${CLOUD_SERVER_VERSION}
|
|
196
195
|
|
|
@@ -205,89 +204,147 @@ Commands:
|
|
|
205
204
|
logs Print the tail of the server log
|
|
206
205
|
|
|
207
206
|
Options:
|
|
208
|
-
--foreground, -f Run in the foreground instead of daemonizing
|
|
207
|
+
--foreground, -f Run start in the foreground instead of daemonizing
|
|
209
208
|
--port <port> Connector (network) port (default: 8080)
|
|
210
209
|
--agent-port <port> Agent port, bound to 127.0.0.1 only (default: port + 1)
|
|
211
210
|
--path <path> WebSocket path (default: /ws)
|
|
212
211
|
--token <token> Authentication token (auto-generated if not specified)
|
|
213
212
|
--heartbeat <seconds> Heartbeat timeout in seconds (default: 90)
|
|
214
213
|
--max-connections <n> Max concurrent connections (default: 100)
|
|
215
|
-
--allow-remote-agent Allow command-issuing agents to connect from the
|
|
216
|
-
network (default: off; agents must be local)
|
|
214
|
+
--allow-remote-agent Allow command-issuing agents to connect from the network
|
|
217
215
|
--version, -v Show version
|
|
218
216
|
--help, -h Show this help message
|
|
219
|
-
|
|
220
|
-
Environment Variables:
|
|
221
|
-
MEARL_TOKEN Authentication token (overrides --token if set)
|
|
222
|
-
CLOUD_SERVER_HOST Host name for WebSocket URL (default: localhost)
|
|
223
|
-
|
|
224
|
-
Examples:
|
|
225
|
-
# Start in the background (default) and print the connector command
|
|
226
|
-
mearl-cloud-server
|
|
227
|
-
|
|
228
|
-
# Check status / connect info, then stop
|
|
229
|
-
mearl-cloud-server status
|
|
230
|
-
mearl-cloud-server stop
|
|
231
|
-
|
|
232
|
-
# Start with a custom port and token
|
|
233
|
-
mearl-cloud-server start --port 9000 --token your-secret-token
|
|
234
|
-
|
|
235
|
-
# Run in the foreground (e.g. under systemd / Docker / PM2)
|
|
236
|
-
mearl-cloud-server --foreground
|
|
237
217
|
`);
|
|
238
218
|
}
|
|
239
|
-
function
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
219
|
+
function integerOption(label, minimum, maximum = Number.MAX_SAFE_INTEGER) {
|
|
220
|
+
return (value) => {
|
|
221
|
+
const parsed = Number(value);
|
|
222
|
+
if (!Number.isInteger(parsed) || parsed < minimum || parsed > maximum) {
|
|
223
|
+
throw new InvalidArgumentError(`${label} must be an integer from ${minimum} to ${maximum}`);
|
|
224
|
+
}
|
|
225
|
+
return parsed;
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function optionUsed(program, name) {
|
|
229
|
+
return program.getOptionValueSource(name) === "cli";
|
|
230
|
+
}
|
|
231
|
+
function assertAllowedOptions(program, command) {
|
|
232
|
+
const allowed = command === "start" || command === "restart" ? START_OPTION_NAMES : /* @__PURE__ */ new Set();
|
|
233
|
+
for (const option of program.options) {
|
|
234
|
+
const name = option.attributeName();
|
|
235
|
+
if (optionUsed(program, name) && !allowed.has(name) && name !== "help" && name !== "version") {
|
|
236
|
+
throw new Error(`${option.long ?? option.flags} is not available for ${command}`);
|
|
257
237
|
}
|
|
258
238
|
}
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
239
|
+
if (command === "restart" && optionUsed(program, "foreground")) {
|
|
240
|
+
throw new Error("--foreground is only available for start");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function daemonArgs(program, options) {
|
|
244
|
+
const args = [];
|
|
245
|
+
const add = (name, flag) => {
|
|
246
|
+
const value = options[name];
|
|
247
|
+
if (optionUsed(program, String(name)) && value !== void 0) args.push(flag, String(value));
|
|
248
|
+
};
|
|
249
|
+
add("port", "--port");
|
|
250
|
+
add("agentPort", "--agent-port");
|
|
251
|
+
add("path", "--path");
|
|
252
|
+
add("token", "--token");
|
|
253
|
+
add("heartbeat", "--heartbeat");
|
|
254
|
+
add("maxConnections", "--max-connections");
|
|
255
|
+
if (options.allowRemoteAgent) args.push("--allow-remote-agent");
|
|
256
|
+
return args;
|
|
257
|
+
}
|
|
258
|
+
function parseCloudServerArgs(argv) {
|
|
259
|
+
const program = new Command().name("mearl-cloud-server").helpOption(false).allowExcessArguments(false).exitOverride().configureOutput({ writeErr: () => {
|
|
260
|
+
}, writeOut: () => {
|
|
261
|
+
} }).argument("[command]").option("-f, --foreground").option("--port <port>", "connector port", integerOption("port", 1, 65535)).option("--agent-port <port>", "agent port", integerOption("agent-port", 1, 65535)).option("--path <path>").option("--token <token>").option("--heartbeat <seconds>", "heartbeat timeout", integerOption("heartbeat", 1)).option("--max-connections <n>", "connection limit", integerOption("max-connections", 1)).option("--allow-remote-agent").option("-v, --version").option("-h, --help");
|
|
262
|
+
try {
|
|
263
|
+
program.parse(["node", "mearl-cloud-server", ...argv]);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (error instanceof CommanderError) {
|
|
266
|
+
throw new Error(error.message.replace(/^error:\s*/i, ""), { cause: error });
|
|
263
267
|
}
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
const cliOptions = program.opts();
|
|
271
|
+
if (cliOptions.help || cliOptions.version) {
|
|
272
|
+
return {
|
|
273
|
+
command: "start",
|
|
274
|
+
daemonArgs: [],
|
|
275
|
+
foreground: false,
|
|
276
|
+
help: cliOptions.help === true,
|
|
277
|
+
options: {},
|
|
278
|
+
version: cliOptions.version === true
|
|
279
|
+
};
|
|
264
280
|
}
|
|
265
|
-
|
|
281
|
+
const commandArg = program.processedArgs[0];
|
|
282
|
+
if (commandArg && !COMMANDS.includes(commandArg)) {
|
|
283
|
+
throw new Error(`Unknown command: ${commandArg}`);
|
|
284
|
+
}
|
|
285
|
+
const command = commandArg ?? "start";
|
|
286
|
+
assertAllowedOptions(program, command);
|
|
287
|
+
if (cliOptions.token !== void 0 && cliOptions.token.length === 0) {
|
|
288
|
+
throw new Error("--token requires a non-empty value");
|
|
289
|
+
}
|
|
290
|
+
const options = {
|
|
291
|
+
...cliOptions.port !== void 0 ? { port: cliOptions.port } : {},
|
|
292
|
+
...cliOptions.agentPort !== void 0 ? { agentPort: cliOptions.agentPort } : {},
|
|
293
|
+
...cliOptions.path !== void 0 ? { path: cliOptions.path } : {},
|
|
294
|
+
...cliOptions.token !== void 0 ? { token: cliOptions.token } : process.env.MEARL_TOKEN ? { token: process.env.MEARL_TOKEN } : {},
|
|
295
|
+
...cliOptions.heartbeat !== void 0 ? { heartbeatTimeout: cliOptions.heartbeat } : {},
|
|
296
|
+
...cliOptions.maxConnections !== void 0 ? { maxConnections: cliOptions.maxConnections } : {},
|
|
297
|
+
...cliOptions.allowRemoteAgent ? { allowRemoteAgent: true } : {}
|
|
298
|
+
};
|
|
299
|
+
return {
|
|
300
|
+
command,
|
|
301
|
+
daemonArgs: daemonArgs(program, cliOptions),
|
|
302
|
+
foreground: cliOptions.foreground === true,
|
|
303
|
+
help: false,
|
|
304
|
+
options,
|
|
305
|
+
version: false
|
|
306
|
+
};
|
|
266
307
|
}
|
|
267
|
-
async function main() {
|
|
308
|
+
async function main(argv = process.argv.slice(2)) {
|
|
309
|
+
let parsed;
|
|
310
|
+
try {
|
|
311
|
+
parsed = parseCloudServerArgs(argv);
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
314
|
+
showHelp();
|
|
315
|
+
return 2;
|
|
316
|
+
}
|
|
317
|
+
if (parsed.help) {
|
|
318
|
+
showHelp();
|
|
319
|
+
return 0;
|
|
320
|
+
}
|
|
321
|
+
if (parsed.version) {
|
|
322
|
+
console.log(CLOUD_SERVER_VERSION);
|
|
323
|
+
return 0;
|
|
324
|
+
}
|
|
268
325
|
const mode = resolveCloudServerMode();
|
|
269
326
|
if (mode === "qoder") {
|
|
270
|
-
await runQoderCloudServerCommand(command, {
|
|
327
|
+
await runQoderCloudServerCommand(parsed.command, {
|
|
271
328
|
isServerRunning: isDaemonRunning,
|
|
272
329
|
stopServer: stopDaemon,
|
|
273
330
|
printStatus: statusDaemon,
|
|
274
331
|
printLogs
|
|
275
332
|
});
|
|
276
|
-
return;
|
|
333
|
+
return 0;
|
|
277
334
|
}
|
|
278
|
-
if (foreground) {
|
|
279
|
-
await runForeground(
|
|
280
|
-
return;
|
|
335
|
+
if (parsed.foreground) {
|
|
336
|
+
await runForeground(parsed.options);
|
|
337
|
+
return 0;
|
|
281
338
|
}
|
|
282
|
-
switch (command) {
|
|
339
|
+
switch (parsed.command) {
|
|
283
340
|
case "start":
|
|
284
|
-
await startDaemon(
|
|
341
|
+
await startDaemon(parsed.daemonArgs);
|
|
285
342
|
break;
|
|
286
343
|
case "stop":
|
|
287
344
|
await stopDaemon();
|
|
288
345
|
break;
|
|
289
346
|
case "restart":
|
|
290
|
-
await restartDaemon(
|
|
347
|
+
await restartDaemon(parsed.daemonArgs);
|
|
291
348
|
break;
|
|
292
349
|
case "status":
|
|
293
350
|
statusDaemon();
|
|
@@ -296,8 +353,18 @@ async function main() {
|
|
|
296
353
|
printLogs();
|
|
297
354
|
break;
|
|
298
355
|
}
|
|
356
|
+
return 0;
|
|
357
|
+
}
|
|
358
|
+
var currentFile = fileURLToPath2(import.meta.url);
|
|
359
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === currentFile) {
|
|
360
|
+
main().then((code) => {
|
|
361
|
+
process.exitCode = code;
|
|
362
|
+
}).catch((error) => {
|
|
363
|
+
console.error("[CloudServer] Fatal error:", error);
|
|
364
|
+
process.exitCode = 1;
|
|
365
|
+
});
|
|
299
366
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
367
|
+
export {
|
|
368
|
+
main,
|
|
369
|
+
parseCloudServerArgs
|
|
370
|
+
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/cloud-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "Cloud WebSocket server for Mearl — bridges cloud agents to local connectors",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
7
|
+
"node": ">=22.12.0"
|
|
8
8
|
},
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
@@ -33,10 +33,11 @@
|
|
|
33
33
|
"registry": "https://registry.npmjs.org"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
+
"commander": "^15.0.0",
|
|
36
37
|
"ws": "^8.18.0",
|
|
37
|
-
"@mearl/client": "2.
|
|
38
|
-
"@mearl/
|
|
39
|
-
"@mearl/
|
|
38
|
+
"@mearl/client": "2.9.0",
|
|
39
|
+
"@mearl/daemon-core": "2.9.0",
|
|
40
|
+
"@mearl/cloud-types": "2.9.0"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/node": "^24.9.1",
|