@mearl/cloud-server 2.9.4 → 2.9.6

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
@@ -23,6 +23,7 @@ npx @mearl/setup --qoder-cloud --yes
23
23
  ### 命令行方式
24
24
 
25
25
  Server **默认以后台守护进程方式运行**,启动后立即返回,并打印本地机器需要执行的连接命令。
26
+ POSIX 环境下后台进程会进入独立 session,可在启动命令结束后继续运行。
26
27
  Qoder 模式保持相同的 `start/status/restart/stop/logs` 交互;`start` 和 `restart` 生成新的
27
28
  pair/connector 信息后退出,不监听端口或创建守护进程。
28
29
 
@@ -94,6 +95,13 @@ mearl-cloud-server --foreground
94
95
 
95
96
  - `MEARL_TOKEN` - 鉴权密钥(覆盖 --token 选项)
96
97
  - `CLOUD_SERVER_HOST` - 公网主机名(默认自动获取)
98
+ - `OPENSANDBOX_ID` - 非空时启用 OpenSandbox 环境自适应
99
+ - `AONE_SANDBOX_ENDPOINT_TEMPLATE` - OpenSandbox 注入的入口模板;必须包含 `{port}`,server 会用 connector 端口替换并生成 `wss://` 连接地址
100
+
101
+ OpenSandbox 中 connector listener 会绑定 `0.0.0.0`,公开地址以平台注入的 endpoint
102
+ template 为准,不会从容器 hostname 推导路由前缀。
103
+ OpenSandbox 与通过 `AONE_SANDBOX_ID` 及本地端口映射接口识别的 AONE Sandbox
104
+ 是两种独立环境,server 会分别选择对应的入口解析方式。
97
105
 
98
106
  ### 编程方式
99
107
 
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -1,9 +1,10 @@
1
1
  // src/index.ts
2
2
  import WebSocket, { WebSocketServer } from "ws";
3
3
  import { createServer as createHttpServer } from "node:http";
4
- import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
4
+ import { chmodSync } from "node:fs";
5
5
  import { randomBytes } from "node:crypto";
6
6
  import { networkInterfaces } from "node:os";
7
+ import { writeRecord } from "@mearl/daemon-core";
7
8
 
8
9
  // src/paths.ts
9
10
  import { join } from "node:path";
@@ -13,6 +14,25 @@ var CONFIG_FILE = join(CONFIG_DIR, "cloud-server.json");
13
14
  var MODE_FILE = join(CONFIG_DIR, "cloud-server-mode.json");
14
15
  var LOG_FILE = join(CONFIG_DIR, "cloud-server.log");
15
16
 
17
+ // src/sandbox.ts
18
+ var PORT_PLACEHOLDER = "{port}";
19
+ function resolveOpenSandboxEndpoint(port, env = process.env) {
20
+ if (!env.OPENSANDBOX_ID?.trim()) return null;
21
+ const template = env.AONE_SANDBOX_ENDPOINT_TEMPLATE?.trim();
22
+ if (!template?.includes(PORT_PLACEHOLDER)) return null;
23
+ const rendered = template.replaceAll(PORT_PLACEHOLDER, String(port));
24
+ const endpoint = /^[a-z][a-z\d+.-]*:\/\//i.test(rendered) ? rendered : `https://${rendered}`;
25
+ try {
26
+ const parsed = new URL(endpoint);
27
+ if (!["http:", "https:", "ws:", "wss:"].includes(parsed.protocol) || !parsed.hostname) {
28
+ return null;
29
+ }
30
+ return parsed.toString();
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+
16
36
  // src/index.ts
17
37
  import {
18
38
  isAgentHelloMessage,
@@ -484,6 +504,15 @@ var CloudServer = class {
484
504
  return "localhost";
485
505
  }
486
506
  async getSandboxPublicUrl(port) {
507
+ if (process.env.OPENSANDBOX_ID?.trim()) {
508
+ const endpoint = resolveOpenSandboxEndpoint(port);
509
+ if (!endpoint) {
510
+ console.warn(
511
+ "[CloudServer] OpenSandbox detected, but AONE_SANDBOX_ENDPOINT_TEMPLATE is missing, invalid, or does not contain {port}."
512
+ );
513
+ }
514
+ return endpoint;
515
+ }
487
516
  if (!process.env.AONE_SANDBOX_ID) {
488
517
  return null;
489
518
  }
@@ -507,14 +536,8 @@ var CloudServer = class {
507
536
  }
508
537
  writeConfigFile(info) {
509
538
  try {
510
- if (!existsSync(CONFIG_DIR)) {
511
- mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
512
- }
513
539
  const config = { server: info.localUrl, ...info };
514
- writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), {
515
- encoding: "utf-8",
516
- mode: 384
517
- });
540
+ writeRecord(CONFIG_FILE, config);
518
541
  if (process.platform !== "win32") {
519
542
  chmodSync(CONFIG_DIR, 448);
520
543
  chmodSync(CONFIG_FILE, 384);
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  import type { CloudServerOptions } from './index.js';
3
2
  import { type CloudServerCommand } from './qoder.js';
4
3
  export interface ParsedCloudServerArgs {
package/dist/cli.js CHANGED
@@ -4,16 +4,14 @@ import {
4
4
  CloudServer,
5
5
  LOG_FILE,
6
6
  MODE_FILE
7
- } from "./chunks/chunk-DCTZIDJ7.js";
7
+ } from "./chunks/chunk-AFZKMZDL.js";
8
8
 
9
9
  // src/cli.ts
10
- import path from "node:path";
11
- import { fileURLToPath as fileURLToPath2 } from "node:url";
12
10
  import { Command, CommanderError, InvalidArgumentError } from "commander";
13
11
 
14
12
  // src/daemon.ts
15
13
  import { fileURLToPath } from "node:url";
16
- import { createDaemon, readRecord, removeRecord } from "@mearl/daemon-core";
14
+ import { createDaemon, readRecord, removeRecordIfPid } from "@mearl/daemon-core";
17
15
  var SCRIPT_PATH = fileURLToPath(import.meta.url);
18
16
  function formatConnectInfo(record) {
19
17
  const config = record;
@@ -71,7 +69,7 @@ async function runForeground(options) {
71
69
  console.log(`
72
70
  [CloudServer] Received ${signal}, shutting down...`);
73
71
  await server.stop();
74
- removeRecord(CONFIG_FILE);
72
+ removeRecordIfPid(CONFIG_FILE, process.pid);
75
73
  process.exit(0);
76
74
  };
77
75
  process.on("SIGINT", () => void shutdown("SIGINT"));
@@ -178,7 +176,7 @@ async function runQoderCloudServerCommand(command, dependencies) {
178
176
  }
179
177
 
180
178
  // src/cli.ts
181
- var CLOUD_SERVER_VERSION = true ? "2.9.4" : "unknown";
179
+ var CLOUD_SERVER_VERSION = true ? "2.9.6" : "unknown";
182
180
  var COMMANDS = ["start", "stop", "restart", "status", "logs"];
183
181
  var START_OPTION_NAMES = /* @__PURE__ */ new Set([
184
182
  "foreground",
@@ -355,16 +353,11 @@ async function main(argv = process.argv.slice(2)) {
355
353
  }
356
354
  return 0;
357
355
  }
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
- });
356
+
357
+ // src/bin.ts
358
+ try {
359
+ process.exitCode = await main();
360
+ } catch (error) {
361
+ console.error("[CloudServer] Fatal error:", error);
362
+ process.exitCode = 1;
366
363
  }
367
- export {
368
- main,
369
- parseCloudServerArgs
370
- };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CloudServer
3
- } from "./chunks/chunk-DCTZIDJ7.js";
3
+ } from "./chunks/chunk-AFZKMZDL.js";
4
4
  export {
5
5
  CloudServer
6
6
  };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Resolve the ingress endpoint injected into an OpenSandbox environment.
3
+ * The template is authoritative because its routing prefix cannot be derived
4
+ * from the container hostname.
5
+ */
6
+ export declare function resolveOpenSandboxEndpoint(port: number, env?: NodeJS.ProcessEnv): string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/cloud-server",
3
- "version": "2.9.4",
3
+ "version": "2.9.6",
4
4
  "description": "Cloud WebSocket server for Mearl — bridges cloud agents to local connectors",
5
5
  "type": "module",
6
6
  "engines": {
@@ -35,9 +35,9 @@
35
35
  "dependencies": {
36
36
  "commander": "^15.0.0",
37
37
  "ws": "^8.18.0",
38
- "@mearl/client": "2.9.4",
39
- "@mearl/daemon-core": "2.9.4",
40
- "@mearl/cloud-types": "2.9.4"
38
+ "@mearl/client": "2.9.6",
39
+ "@mearl/daemon-core": "2.9.6",
40
+ "@mearl/cloud-types": "2.9.6"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^24.9.1",