@mearl/cloud-server
Advanced tools
+12
-1
| #!/usr/bin/env node | ||
| export {}; | ||
| import type { CloudServerOptions } from './index.js'; | ||
| import { type CloudServerCommand } from './qoder.js'; | ||
| export interface ParsedCloudServerArgs { | ||
| command: CloudServerCommand; | ||
| daemonArgs: string[]; | ||
| foreground: boolean; | ||
| help: boolean; | ||
| options: CloudServerOptions; | ||
| version: boolean; | ||
| } | ||
| export declare function parseCloudServerArgs(argv: string[]): ParsedCloudServerArgs; | ||
| export declare function main(argv?: string[]): Promise<number>; |
+146
-79
@@ -9,2 +9,7 @@ #!/usr/bin/env node | ||
| // src/cli.ts | ||
| import path from "node:path"; | ||
| import { fileURLToPath as fileURLToPath2 } from "node:url"; | ||
| import { Command, CommanderError, InvalidArgumentError } from "commander"; | ||
| // src/daemon.ts | ||
@@ -18,7 +23,7 @@ import { fileURLToPath } from "node:url"; | ||
| if (config.port) lines.push(` port: ${config.port}`); | ||
| const command2 = config.publicUrl ? `npx @mearl/cloud-connector start "${config.publicUrl}"` : config.connectorCommand; | ||
| if (command2) { | ||
| const command = config.publicUrl ? `npx @mearl/cloud-connector start "${config.publicUrl}"` : config.connectorCommand; | ||
| if (command) { | ||
| lines.push(""); | ||
| lines.push(" Connect from your local machine:"); | ||
| lines.push(` ${command2}`); | ||
| lines.push(` ${command}`); | ||
| } | ||
@@ -152,5 +157,5 @@ return lines.length ? lines.join("\n") : null; | ||
| } | ||
| async function runQoderCloudServerCommand(command2, dependencies) { | ||
| async function runQoderCloudServerCommand(command, dependencies) { | ||
| const writeLine = dependencies.writeLine ?? console.log; | ||
| switch (command2) { | ||
| switch (command) { | ||
| case "start": | ||
@@ -179,20 +184,14 @@ case "restart": | ||
| // src/cli.ts | ||
| var CLOUD_SERVER_VERSION = true ? "2.8.2" : "unknown"; | ||
| var argv = process.argv.slice(2); | ||
| if (argv.includes("--version") || argv.includes("-v")) { | ||
| console.log(CLOUD_SERVER_VERSION); | ||
| process.exit(0); | ||
| } | ||
| if (argv.includes("--help") || argv.includes("-h")) { | ||
| showHelp(); | ||
| process.exit(0); | ||
| } | ||
| var CLOUD_SERVER_VERSION = true ? "2.9.0" : "unknown"; | ||
| var COMMANDS = ["start", "stop", "restart", "status", "logs"]; | ||
| var command = "start"; | ||
| var rest = argv; | ||
| if (argv[0] && COMMANDS.includes(argv[0])) { | ||
| command = argv[0]; | ||
| rest = argv.slice(1); | ||
| } | ||
| var foreground = rest.includes("--foreground") || rest.includes("-f"); | ||
| var START_OPTION_NAMES = /* @__PURE__ */ new Set([ | ||
| "foreground", | ||
| "port", | ||
| "agentPort", | ||
| "path", | ||
| "token", | ||
| "heartbeat", | ||
| "maxConnections", | ||
| "allowRemoteAgent" | ||
| ]); | ||
| function showHelp() { | ||
@@ -212,3 +211,3 @@ console.log(`mearl-cloud-server v${CLOUD_SERVER_VERSION} | ||
| Options: | ||
| --foreground, -f Run in the foreground instead of daemonizing | ||
| --foreground, -f Run start in the foreground instead of daemonizing | ||
| --port <port> Connector (network) port (default: 8080) | ||
@@ -220,58 +219,116 @@ --agent-port <port> Agent port, bound to 127.0.0.1 only (default: port + 1) | ||
| --max-connections <n> Max concurrent connections (default: 100) | ||
| --allow-remote-agent Allow command-issuing agents to connect from the | ||
| network (default: off; agents must be local) | ||
| --allow-remote-agent Allow command-issuing agents to connect from the network | ||
| --version, -v Show version | ||
| --help, -h Show this help message | ||
| Environment Variables: | ||
| MEARL_TOKEN Authentication token (overrides --token if set) | ||
| CLOUD_SERVER_HOST Host name for WebSocket URL (default: localhost) | ||
| Examples: | ||
| # Start in the background (default) and print the connector command | ||
| mearl-cloud-server | ||
| # Check status / connect info, then stop | ||
| mearl-cloud-server status | ||
| mearl-cloud-server stop | ||
| # Start with a custom port and token | ||
| mearl-cloud-server start --port 9000 --token your-secret-token | ||
| # Run in the foreground (e.g. under systemd / Docker / PM2) | ||
| mearl-cloud-server --foreground | ||
| `); | ||
| } | ||
| function parseOptions(args) { | ||
| const options = {}; | ||
| for (let i = 0; i < args.length; i++) { | ||
| const arg = args[i]; | ||
| if (arg === "--port" && i + 1 < args.length) { | ||
| options.port = parseInt(args[++i], 10); | ||
| } else if (arg === "--agent-port" && i + 1 < args.length) { | ||
| options.agentPort = parseInt(args[++i], 10); | ||
| } else if (arg === "--path" && i + 1 < args.length) { | ||
| options.path = args[++i]; | ||
| } else if (arg === "--token" && i + 1 < args.length) { | ||
| options.token = args[++i]; | ||
| } else if (arg === "--heartbeat" && i + 1 < args.length) { | ||
| options.heartbeatTimeout = parseInt(args[++i], 10); | ||
| } else if (arg === "--max-connections" && i + 1 < args.length) { | ||
| options.maxConnections = parseInt(args[++i], 10); | ||
| } else if (arg === "--allow-remote-agent") { | ||
| options.allowRemoteAgent = true; | ||
| function integerOption(label, minimum, maximum = Number.MAX_SAFE_INTEGER) { | ||
| return (value) => { | ||
| const parsed = Number(value); | ||
| if (!Number.isInteger(parsed) || parsed < minimum || parsed > maximum) { | ||
| throw new InvalidArgumentError(`${label} must be an integer from ${minimum} to ${maximum}`); | ||
| } | ||
| return parsed; | ||
| }; | ||
| } | ||
| function optionUsed(program, name) { | ||
| return program.getOptionValueSource(name) === "cli"; | ||
| } | ||
| function assertAllowedOptions(program, command) { | ||
| const allowed = command === "start" || command === "restart" ? START_OPTION_NAMES : /* @__PURE__ */ new Set(); | ||
| for (const option of program.options) { | ||
| const name = option.attributeName(); | ||
| if (optionUsed(program, name) && !allowed.has(name) && name !== "help" && name !== "version") { | ||
| throw new Error(`${option.long ?? option.flags} is not available for ${command}`); | ||
| } | ||
| } | ||
| if (!options.token) { | ||
| const envToken = process.env.MEARL_TOKEN; | ||
| if (envToken) { | ||
| options.token = envToken; | ||
| if (command === "restart" && optionUsed(program, "foreground")) { | ||
| throw new Error("--foreground is only available for start"); | ||
| } | ||
| } | ||
| function daemonArgs(program, options) { | ||
| const args = []; | ||
| const add = (name, flag) => { | ||
| const value = options[name]; | ||
| if (optionUsed(program, String(name)) && value !== void 0) args.push(flag, String(value)); | ||
| }; | ||
| add("port", "--port"); | ||
| add("agentPort", "--agent-port"); | ||
| add("path", "--path"); | ||
| add("token", "--token"); | ||
| add("heartbeat", "--heartbeat"); | ||
| add("maxConnections", "--max-connections"); | ||
| if (options.allowRemoteAgent) args.push("--allow-remote-agent"); | ||
| return args; | ||
| } | ||
| function parseCloudServerArgs(argv) { | ||
| const program = new Command().name("mearl-cloud-server").helpOption(false).allowExcessArguments(false).exitOverride().configureOutput({ writeErr: () => { | ||
| }, writeOut: () => { | ||
| } }).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"); | ||
| try { | ||
| program.parse(["node", "mearl-cloud-server", ...argv]); | ||
| } catch (error) { | ||
| if (error instanceof CommanderError) { | ||
| throw new Error(error.message.replace(/^error:\s*/i, ""), { cause: error }); | ||
| } | ||
| throw error; | ||
| } | ||
| return options; | ||
| const cliOptions = program.opts(); | ||
| if (cliOptions.help || cliOptions.version) { | ||
| return { | ||
| command: "start", | ||
| daemonArgs: [], | ||
| foreground: false, | ||
| help: cliOptions.help === true, | ||
| options: {}, | ||
| version: cliOptions.version === true | ||
| }; | ||
| } | ||
| const commandArg = program.processedArgs[0]; | ||
| if (commandArg && !COMMANDS.includes(commandArg)) { | ||
| throw new Error(`Unknown command: ${commandArg}`); | ||
| } | ||
| const command = commandArg ?? "start"; | ||
| assertAllowedOptions(program, command); | ||
| if (cliOptions.token !== void 0 && cliOptions.token.length === 0) { | ||
| throw new Error("--token requires a non-empty value"); | ||
| } | ||
| const options = { | ||
| ...cliOptions.port !== void 0 ? { port: cliOptions.port } : {}, | ||
| ...cliOptions.agentPort !== void 0 ? { agentPort: cliOptions.agentPort } : {}, | ||
| ...cliOptions.path !== void 0 ? { path: cliOptions.path } : {}, | ||
| ...cliOptions.token !== void 0 ? { token: cliOptions.token } : process.env.MEARL_TOKEN ? { token: process.env.MEARL_TOKEN } : {}, | ||
| ...cliOptions.heartbeat !== void 0 ? { heartbeatTimeout: cliOptions.heartbeat } : {}, | ||
| ...cliOptions.maxConnections !== void 0 ? { maxConnections: cliOptions.maxConnections } : {}, | ||
| ...cliOptions.allowRemoteAgent ? { allowRemoteAgent: true } : {} | ||
| }; | ||
| return { | ||
| command, | ||
| daemonArgs: daemonArgs(program, cliOptions), | ||
| foreground: cliOptions.foreground === true, | ||
| help: false, | ||
| options, | ||
| version: false | ||
| }; | ||
| } | ||
| async function main() { | ||
| async function main(argv = process.argv.slice(2)) { | ||
| let parsed; | ||
| try { | ||
| parsed = parseCloudServerArgs(argv); | ||
| } catch (error) { | ||
| console.error(`Error: ${error instanceof Error ? error.message : String(error)}`); | ||
| showHelp(); | ||
| return 2; | ||
| } | ||
| if (parsed.help) { | ||
| showHelp(); | ||
| return 0; | ||
| } | ||
| if (parsed.version) { | ||
| console.log(CLOUD_SERVER_VERSION); | ||
| return 0; | ||
| } | ||
| const mode = resolveCloudServerMode(); | ||
| if (mode === "qoder") { | ||
| await runQoderCloudServerCommand(command, { | ||
| await runQoderCloudServerCommand(parsed.command, { | ||
| isServerRunning: isDaemonRunning, | ||
@@ -282,11 +339,11 @@ stopServer: stopDaemon, | ||
| }); | ||
| return; | ||
| return 0; | ||
| } | ||
| if (foreground) { | ||
| await runForeground(parseOptions(rest)); | ||
| return; | ||
| if (parsed.foreground) { | ||
| await runForeground(parsed.options); | ||
| return 0; | ||
| } | ||
| switch (command) { | ||
| switch (parsed.command) { | ||
| case "start": | ||
| await startDaemon(rest); | ||
| await startDaemon(parsed.daemonArgs); | ||
| break; | ||
@@ -297,3 +354,3 @@ case "stop": | ||
| case "restart": | ||
| await restartDaemon(rest); | ||
| await restartDaemon(parsed.daemonArgs); | ||
| break; | ||
@@ -307,6 +364,16 @@ case "status": | ||
| } | ||
| return 0; | ||
| } | ||
| main().catch((error) => { | ||
| console.error("[CloudServer] Fatal error:", error); | ||
| process.exit(1); | ||
| }); | ||
| var currentFile = fileURLToPath2(import.meta.url); | ||
| if (process.argv[1] && path.resolve(process.argv[1]) === currentFile) { | ||
| main().then((code) => { | ||
| process.exitCode = code; | ||
| }).catch((error) => { | ||
| console.error("[CloudServer] Fatal error:", error); | ||
| process.exitCode = 1; | ||
| }); | ||
| } | ||
| export { | ||
| main, | ||
| parseCloudServerArgs | ||
| }; |
+6
-5
| { | ||
| "name": "@mearl/cloud-server", | ||
| "version": "2.8.2", | ||
| "version": "2.9.0", | ||
| "description": "Cloud WebSocket server for Mearl — bridges cloud agents to local connectors", | ||
| "type": "module", | ||
| "engines": { | ||
| "node": ">=18" | ||
| "node": ">=22.12.0" | ||
| }, | ||
@@ -36,6 +36,7 @@ "main": "dist/index.js", | ||
| "dependencies": { | ||
| "commander": "^15.0.0", | ||
| "ws": "^8.18.0", | ||
| "@mearl/client": "2.8.2", | ||
| "@mearl/cloud-types": "2.8.2", | ||
| "@mearl/daemon-core": "2.8.2" | ||
| "@mearl/client": "2.9.0", | ||
| "@mearl/daemon-core": "2.9.0", | ||
| "@mearl/cloud-types": "2.9.0" | ||
| }, | ||
@@ -42,0 +43,0 @@ "devDependencies": { |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
45996
7.89%1071
8.4%5
25%11
10%+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
Updated
Updated
Updated