@dsh-remote/relay 3.1.9 → 3.2.1

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
@@ -18,21 +18,22 @@ curl -s http://127.0.0.1:8787/healthz
18
18
 
19
19
  ### 常用配置
20
20
 
21
- | 变量 | 必填 | 默认 | 说明 |
22
- | ----------------------------- | ---- | ----------- | -------------------------------------------------------------------------------------------------------- |
23
- | `DRC_HOST_TOKEN` | ✅ | — | 主机出站认证凭据。`openssl rand -hex 32` 生成。**与主机插件那份必须逐字一致** |
24
- | `DRC_PORT` | | `8787` | 监听端口。`0` = 让系统分配(测试与容器用) |
25
- | `DRC_BIND` | | `127.0.0.1` | 绑定地址。默认只绑回环:TLS 由反代终止。**只有自己就是边缘(容器直接发布端口)时才设 `0.0.0.0`** |
26
- | `DRC_PUBLIC_URL` | | 空 | `/api/info` 返回的对外地址。公网填 `wss://你的域名` |
27
- | `DRC_LOG_LEVEL` | | `info` | `debug` / `info` / `warn` / `error` / `silent`。**区分大小写** |
28
- | `DRC_PAIR_TTL_MS` | | `120000` | 配对码的**服务端权威**寿命(毫秒) |
29
- | `DRC_MP_APPID` | | — | 小程序 appid。与 `DRC_MP_SECRET` 同时配置才启用 `GET /code`(生成小程序码);不配则插件回退旧 `dshr:` 码 |
30
- | `DRC_MP_SECRET` | | — | 小程序 secret。**只放在中继**,永不进插件包;换了 secret 记得同步这里 |
31
- | `DRC_STATE_FILE` | | 空(关闭) | 会话表落盘路径。默认关闭 = 纯内存;配上后启动先读它、会话表有增删时写回 |
32
- | `DRC_STATE_SAVE_MS` | | `60000` | 周期补写状态文件的间隔(只为刷新 `lastActivityAt`) |
33
- | `DRC_MAX_BUFFERED_BYTES` | | `1048576` | 慢消费者阈值:发送缓冲区**连续**超限达**本角色窗口**才 1008 断开 |
34
- | `DRC_SLOW_CONSUMER_HOST_MS` | | `10000` | **主机**侧慢消费者窗口(毫秒)。主机卡住就是所有人卡住,所以窗口短 |
35
- | `DRC_SLOW_CONSUMER_CLIENT_MS` | | `45000` | **客户端**侧慢消费者窗口(毫秒)。**必须大于手机自己的重连周期**(≈42.5 秒) |
21
+ | 变量 | 必填 | 默认 | 说明 |
22
+ | ----------------------------- | ---- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
23
+ | `DRC_HOST_TOKEN` | ✅ | — | 主机出站认证凭据。`openssl rand -hex 32` 生成。**与主机插件那份必须逐字一致** |
24
+ | `DRC_PORT` | | `8787` | 监听端口。`0` = 让系统分配(测试与容器用) |
25
+ | `DRC_BIND` | | `127.0.0.1` | 绑定地址。默认只绑回环:TLS 由反代终止。**只有自己就是边缘(容器直接发布端口)时才设 `0.0.0.0`** |
26
+ | `DRC_PUBLIC_URL` | | 空 | `/api/info` 返回的对外地址。公网填 `wss://你的域名` |
27
+ | `DRC_LOG_LEVEL` | | `info` | `debug` / `info` / `warn` / `error` / `silent`。**区分大小写** |
28
+ | `DRC_PAIR_TTL_MS` | | `120000` | 配对码的**服务端权威**寿命(毫秒) |
29
+ | `DRC_MP_APPID` | | — | 小程序 appid。与 `DRC_MP_SECRET` 同时配置才启用 `GET /code`(生成小程序码);不配则插件回退旧 `dshr:` 码 |
30
+ | `DRC_MP_SECRET` | | — | 小程序 secret。**只放在中继**,永不进插件包;换了 secret 记得同步这里 |
31
+ | `DRC_MP_ENV` | | `release` | 小程序码打开哪一版:`release` / `trial` / `develop`。**新版只上了体验版时必须设 `trial`**,否则码把用户带进老版本(能扫进去、配对必失败) |
32
+ | `DRC_STATE_FILE` | | 空(关闭) | 会话表落盘路径。默认关闭 = 纯内存;配上后启动先读它、会话表有增删时写回 |
33
+ | `DRC_STATE_SAVE_MS` | | `60000` | 周期补写状态文件的间隔(只为刷新 `lastActivityAt`) |
34
+ | `DRC_MAX_BUFFERED_BYTES` | | `1048576` | 慢消费者阈值:发送缓冲区**连续**超限达**本角色窗口**才 1008 断开 |
35
+ | `DRC_SLOW_CONSUMER_HOST_MS` | | `10000` | **主机**侧慢消费者窗口(毫秒)。主机卡住就是所有人卡住,所以窗口短 |
36
+ | `DRC_SLOW_CONSUMER_CLIENT_MS` | | `45000` | **客户端**侧慢消费者窗口(毫秒)。**必须大于手机自己的重连周期**(≈42.5 秒) |
36
37
 
37
38
  ### 端点
38
39
 
@@ -66,6 +66,18 @@ services:
66
66
  DRC_MAX_CONNS: ${DRC_MAX_CONNS:-200}
67
67
  DRC_CONV_IDLE_TTL_MS: ${DRC_CONV_IDLE_TTL_MS:-604800000}
68
68
  DRC_HOST_GRACE_MS: ${DRC_HOST_GRACE_MS:-120000}
69
+ # 小程序码签发(2026-10-21「双码合一」):**只有中继持有小程序的 appSecret**,
70
+ # 主机插件那份永远拿不到它。两个都配了才启用 `GET /code?token=`;
71
+ # 不配(自部署常态)那条路由回 501,主机自动回落到旧 `dshr:` 码。
72
+ #
73
+ # ⚠️ **必须显式转发**:compose 只把这里列出的变量送进容器,
74
+ # 写在 `deploy/docker/.env` 里而这一行没有 ⇒ 进程读到的是 undefined ⇒ 501,
75
+ # 而日志一字不提(2026-10-21 上线当场踩到:healthz 已经 3.1.9、/code 仍 501)。
76
+ DRC_MP_APPID: ${DRC_MP_APPID:-}
77
+ DRC_MP_SECRET: ${DRC_MP_SECRET:-}
78
+ # 码打开哪一版:release / trial / develop(缺省 release)。
79
+ # 新版只上了体验版时必须设 trial,否则码把用户带进老版本:能扫进去、配对必失败。
80
+ DRC_MP_ENV: ${DRC_MP_ENV:-}
69
81
 
70
82
  volumes:
71
83
  - ${DRC_DATA_DIR:-relay-data}:/data
@@ -23443,7 +23443,7 @@ function date4(params) {
23443
23443
  return _coercedDate(ZodDate, params);
23444
23444
  }
23445
23445
 
23446
- // node_modules/.pnpm/@dsh-remote+protocol@3.1.9/node_modules/@dsh-remote/protocol/dist/src/wire/limits.js
23446
+ // node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/limits.js
23447
23447
  var MAX_RELAY_MESSAGE_BYTES = 1024 * 1024;
23448
23448
  var MAX_CIPHERTEXT_BYTES = MAX_RELAY_MESSAGE_BYTES - 8 * 1024;
23449
23449
  var MAX_IMAGE_ATTACHMENTS = 4;
@@ -23452,7 +23452,7 @@ var MAX_ATTACHMENT_BYTES = 512 * 1024;
23452
23452
  var MAX_ATTACHMENT_TOTAL_BYTES = 512 * 1024;
23453
23453
  var IDEMPOTENCY_WINDOW_MS = 5 * 60 * 1e3;
23454
23454
 
23455
- // node_modules/.pnpm/@dsh-remote+protocol@3.1.9/node_modules/@dsh-remote/protocol/dist/src/wire/frames.js
23455
+ // node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/frames.js
23456
23456
  var PROTOCOL_VERSION = 1;
23457
23457
  var PAIRING_TOKEN_RE = /^\d{6}$/;
23458
23458
  var nonEmpty = external_exports.string().min(1);
@@ -23878,6 +23878,7 @@ function loadConfig(env, version2) {
23878
23878
  hostToken,
23879
23879
  mpAppId: (env.DRC_MP_APPID ?? "").trim(),
23880
23880
  mpSecret: (env.DRC_MP_SECRET ?? "").trim(),
23881
+ mpEnv: (env.DRC_MP_ENV ?? "").trim(),
23881
23882
  port: portNumber(env.DRC_PORT, 8787, "DRC_PORT", problems),
23882
23883
  // 默认只绑回环:TLS 由反代终止,中继没理由出现在局域网里。
23883
23884
  // 只有自己就是边缘(容器直接发布端口)时才设 DRC_BIND=0.0.0.0。
@@ -23964,7 +23965,7 @@ var import_subprotocol = __toESM(require_subprotocol(), 1);
23964
23965
  var import_websocket = __toESM(require_websocket(), 1);
23965
23966
  var import_websocket_server = __toESM(require_websocket_server(), 1);
23966
23967
 
23967
- // node_modules/.pnpm/@dsh-remote+protocol@3.1.9/node_modules/@dsh-remote/protocol/dist/src/identity/ids.js
23968
+ // node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/identity/ids.js
23968
23969
  import { randomBytes, randomUUID } from "node:crypto";
23969
23970
  var CONVERSATION_ID_PREFIX = "c_";
23970
23971
  function newConversationId() {
@@ -23974,7 +23975,7 @@ function newHostId() {
23974
23975
  return randomBytes(4).toString("hex");
23975
23976
  }
23976
23977
 
23977
- // node_modules/.pnpm/@dsh-remote+protocol@3.1.9/node_modules/@dsh-remote/protocol/dist/src/wire/negotiate.js
23978
+ // node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/negotiate.js
23978
23979
  function negotiateProtocol(peerProtocol, own2 = PROTOCOL_VERSION) {
23979
23980
  if (peerProtocol === void 0 || peerProtocol === null) {
23980
23981
  return {
@@ -24016,9 +24017,15 @@ var CODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit";
24016
24017
  var LANDING_PAGE = "pages/sessions/sessions";
24017
24018
  var TOKEN_SKEW_MS = 6e4;
24018
24019
  var DEFAULT_TOKEN_TTL_MS = 7e6;
24020
+ var ENV_VERSIONS = ["release", "trial", "develop"];
24021
+ function normalizeEnvVersion(value) {
24022
+ const normalized = (value ?? "").trim().toLowerCase();
24023
+ return ENV_VERSIONS.includes(normalized) ? normalized : "release";
24024
+ }
24019
24025
  function createMpCodeIssuer(options) {
24020
24026
  const appId = options.appId.trim();
24021
24027
  const secret = options.secret.trim();
24028
+ const envVersion = normalizeEnvVersion(options.envVersion);
24022
24029
  const doFetch = options.fetch ?? ((...args) => fetch(...args));
24023
24030
  const now = options.now ?? (() => Date.now());
24024
24031
  let cachedToken;
@@ -24043,7 +24050,17 @@ function createMpCodeIssuer(options) {
24043
24050
  const response = await doFetch(`${CODE_URL}?access_token=${encodeURIComponent(accessToken)}`, {
24044
24051
  method: "POST",
24045
24052
  headers: { "content-type": "application/json" },
24046
- body: JSON.stringify({ scene: pairingToken2, page: LANDING_PAGE })
24053
+ body: JSON.stringify({
24054
+ scene: pairingToken2,
24055
+ page: LANDING_PAGE,
24056
+ /**
24057
+ * `check_path: false`(默认 true):true 时 `page` 必须是**已发布版本里存在**的页面。
24058
+ * 3.2 的首页路径没变,但排错期(`env_version=trial/develop`)里那份页面可能还没发布,
24059
+ * 开着它就会得到 41030 invalid page —— 而症状只是"码生成失败",看不出是版本的事。
24060
+ */
24061
+ check_path: false,
24062
+ env_version: envVersion
24063
+ })
24047
24064
  });
24048
24065
  const bytes = new Uint8Array(await response.arrayBuffer());
24049
24066
  if (bytes.length > 2 && bytes[0] === 255 && bytes[1] === 216) return bytes;
@@ -24078,16 +24095,16 @@ function createMpCodeIssuer(options) {
24078
24095
  };
24079
24096
  }
24080
24097
 
24081
- // node_modules/.pnpm/@dsh-remote+protocol@3.1.9/node_modules/@dsh-remote/protocol/dist/src/wire/errors.js
24098
+ // node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/errors.js
24082
24099
  var WAIT_HINTABLE = /* @__PURE__ */ new Set(["rate_limited", "pair_table_full"]);
24083
24100
  function wantsRetryAfter(code) {
24084
24101
  return typeof code === "string" && WAIT_HINTABLE.has(code);
24085
24102
  }
24086
24103
 
24087
- // node_modules/.pnpm/@dsh-remote+protocol@3.1.9/node_modules/@dsh-remote/protocol/dist/src/platform/sleep.js
24104
+ // node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/platform/sleep.js
24088
24105
  var SLEEP_BACKENDS = ["caffeinate", "systemd-inhibit", "unsupported"];
24089
24106
 
24090
- // node_modules/.pnpm/@dsh-remote+protocol@3.1.9/node_modules/@dsh-remote/protocol/dist/src/wire/payloads.js
24107
+ // node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/payloads.js
24091
24108
  var SESSION_STATES = [
24092
24109
  "idle",
24093
24110
  "running",
@@ -24513,7 +24530,7 @@ var PAYLOAD_TYPES = {
24513
24530
  evSessionHistory: "ev.session_history"
24514
24531
  };
24515
24532
 
24516
- // node_modules/.pnpm/@dsh-remote+protocol@3.1.9/node_modules/@dsh-remote/protocol/dist/src/wire/outbound.js
24533
+ // node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/outbound.js
24517
24534
  function helloOkForClient(clientId, protocol) {
24518
24535
  return { t: "hello-ok", role: "client", clientId, ...protocol === void 0 ? {} : { protocol } };
24519
24536
  }
@@ -24600,7 +24617,7 @@ function encBatchToRelay(sessionId, items) {
24600
24617
  return { t: "enc-batch", sessionId, items: items.map((item) => ({ ...item })) };
24601
24618
  }
24602
24619
 
24603
- // node_modules/.pnpm/@dsh-remote+protocol@3.1.9/node_modules/@dsh-remote/protocol/dist/src/wire/registry.js
24620
+ // node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/registry.js
24604
24621
  var ENDPOINT_FRAME_NAMES = [
24605
24622
  "hello",
24606
24623
  "pair-begin",
@@ -24665,7 +24682,7 @@ var EV_TYPES = [
24665
24682
  ];
24666
24683
  var PAYLOAD_TYPE_SET = /* @__PURE__ */ new Set([...CMD_TYPES, ...EV_TYPES]);
24667
24684
 
24668
- // node_modules/.pnpm/@dsh-remote+protocol@3.1.9/node_modules/@dsh-remote/protocol/dist/src/wire/classify.js
24685
+ // node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/classify.js
24669
24686
  function frameTypeOf(value) {
24670
24687
  if (typeof value !== "object" || value === null || Array.isArray(value))
24671
24688
  return void 0;
@@ -26281,7 +26298,7 @@ function createRelay(config2) {
26281
26298
  }
26282
26299
  }
26283
26300
  }
26284
- const mpCodes = createMpCodeIssuer({ appId: config2.mpAppId, secret: config2.mpSecret });
26301
+ const mpCodes = createMpCodeIssuer({ appId: config2.mpAppId, secret: config2.mpSecret, envVersion: config2.mpEnv });
26285
26302
  const http = createServer(httpHandler);
26286
26303
  http.on("error", (error62) => {
26287
26304
  log.warn("http server error", { message: String(error62?.message ?? error62) });
@@ -26433,7 +26450,7 @@ function registerSignalHandlers(shutdown) {
26433
26450
 
26434
26451
  // src/main.ts
26435
26452
  function readVersion() {
26436
- if ("3.1.9") return "3.1.9";
26453
+ if ("3.2.1") return "3.2.1";
26437
26454
  try {
26438
26455
  const pkg = JSON.parse(readFileSync2(new URL("../../package.json", import.meta.url), "utf8"));
26439
26456
  if (typeof pkg.version === "string") return pkg.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-remote/relay",
3
- "version": "3.1.9",
3
+ "version": "3.2.1",
4
4
  "description": "DSH Remote Control 的零知识 WebSocket 中继:只做路由,不碰载荷明文。可自托管的单文件产物。",
5
5
  "type": "module",
6
6
  "license": "MIT",