@dsh-remote/relay 3.1.3 → 3.1.5

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.
@@ -3726,7 +3726,7 @@ var require_websocket_server = __commonJS({
3726
3726
  // src/main.ts
3727
3727
  import { readFileSync as readFileSync2 } from "node:fs";
3728
3728
 
3729
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/external.js
3729
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/external.js
3730
3730
  var external_exports = {};
3731
3731
  __export(external_exports, {
3732
3732
  $brand: () => $brand,
@@ -3989,7 +3989,7 @@ __export(external_exports, {
3989
3989
  xor: () => xor
3990
3990
  });
3991
3991
 
3992
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/index.js
3992
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/index.js
3993
3993
  var core_exports2 = {};
3994
3994
  __export(core_exports2, {
3995
3995
  $ZodAny: () => $ZodAny,
@@ -4306,7 +4306,7 @@ __export(core_exports2, {
4306
4306
  withParser: () => withParser
4307
4307
  });
4308
4308
 
4309
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/util.js
4309
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/util.js
4310
4310
  var util_exports = {};
4311
4311
  __export(util_exports, {
4312
4312
  BIGINT_FORMAT_RANGES: () => BIGINT_FORMAT_RANGES,
@@ -4377,6 +4377,7 @@ __export(util_exports, {
4377
4377
  required: () => required,
4378
4378
  safeExtend: () => safeExtend,
4379
4379
  shallowClone: () => shallowClone,
4380
+ sizeCaps: () => sizeCaps,
4380
4381
  slugify: () => slugify,
4381
4382
  stringifyPrimitive: () => stringifyPrimitive,
4382
4383
  toZod: () => toZod,
@@ -4896,6 +4897,18 @@ function prefixIssues(path, issues) {
4896
4897
  function unwrapMessage(message) {
4897
4898
  return typeof message === "string" ? message : message?.message;
4898
4899
  }
4900
+ function sizeCaps(defChecks) {
4901
+ const caps = [];
4902
+ for (const ch of defChecks ?? []) {
4903
+ const d = ch._zod.def;
4904
+ if (!/_(length|size)$|_equals$/.test(d.check))
4905
+ break;
4906
+ const max = d.maximum ?? d.length ?? d.size;
4907
+ if (max >= 0)
4908
+ caps.push([max, ch]);
4909
+ }
4910
+ return caps;
4911
+ }
4899
4912
  function attachSchema(issues, start, inst) {
4900
4913
  var _a3;
4901
4914
  for (let i = start; i < issues.length; i++) {
@@ -5149,7 +5162,7 @@ function constantCatch(value) {
5149
5162
  return fn;
5150
5163
  }
5151
5164
 
5152
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/core.js
5165
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/core.js
5153
5166
  var _a;
5154
5167
  var NEVER = /* @__PURE__ */ Object.freeze({
5155
5168
  status: "aborted"
@@ -5271,7 +5284,7 @@ function config(newConfig) {
5271
5284
  return globalConfig;
5272
5285
  }
5273
5286
 
5274
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/errors.js
5287
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/errors.js
5275
5288
  function _getMessage() {
5276
5289
  const internals = this._zod;
5277
5290
  internals.message ?? (internals.message = JSON.stringify(internals.def, jsonStringifyReplacer, 2));
@@ -5463,7 +5476,7 @@ function prettifyError(error62) {
5463
5476
  return lines.join("\n");
5464
5477
  }
5465
5478
 
5466
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/parse.js
5479
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/parse.js
5467
5480
  function finalizeParams(callee, params) {
5468
5481
  return { callee: params?.callee ?? callee, Err: params?.Err };
5469
5482
  }
@@ -5623,7 +5636,7 @@ var _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {
5623
5636
  };
5624
5637
  var safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync($ZodRealError);
5625
5638
 
5626
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/regexes.js
5639
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/regexes.js
5627
5640
  var regexes_exports = {};
5628
5641
  __export(regexes_exports, {
5629
5642
  anyString: () => anyString,
@@ -5795,7 +5808,7 @@ var sha512_hex = /^[0-9a-fA-F]{128}$/;
5795
5808
  var sha512_base64 = /* @__PURE__ */ fixedBase64(86, "==");
5796
5809
  var sha512_base64url = /* @__PURE__ */ fixedBase64url(86);
5797
5810
 
5798
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/checks.js
5811
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/checks.js
5799
5812
  var $ZodCheck = /* @__PURE__ */ $constructor("$ZodCheck", (inst, def) => {
5800
5813
  var _a3;
5801
5814
  inst._zod ?? (inst._zod = {});
@@ -6271,7 +6284,7 @@ var $ZodCheckOverwrite = /* @__PURE__ */ $constructor("$ZodCheckOverwrite", (ins
6271
6284
  };
6272
6285
  });
6273
6286
 
6274
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/doc.js
6287
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/doc.js
6275
6288
  var Doc = class {
6276
6289
  constructor(args = [], closed = {}) {
6277
6290
  this.content = [];
@@ -6312,14 +6325,14 @@ ${content.join("\n")}
6312
6325
  }
6313
6326
  };
6314
6327
 
6315
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/versions.js
6328
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/versions.js
6316
6329
  var version = {
6317
6330
  major: 4,
6318
6331
  minor: 6,
6319
- patch: 5
6332
+ patch: 6
6320
6333
  };
6321
6334
 
6322
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/schemas.js
6335
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/schemas.js
6323
6336
  var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
6324
6337
  var _a3;
6325
6338
  inst ?? (inst = {});
@@ -7072,6 +7085,31 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
7072
7085
  return payload;
7073
7086
  };
7074
7087
  });
7088
+ function capSize(inst, size) {
7089
+ const caps = sizeCaps(inst._zod.def.checks);
7090
+ if (!caps.length)
7091
+ return;
7092
+ const cap = Math.min(...caps.map((c) => c[0]));
7093
+ const parse3 = inst._zod.parse;
7094
+ inst._zod.parse = (payload, ctx) => {
7095
+ const n = size(payload.value);
7096
+ if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
7097
+ for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
7098
+ if (n > max && ch._zod.def.when(payload)) {
7099
+ const start = payload.issues.length;
7100
+ ch._zod.check(payload);
7101
+ payload.issues[start].continue = false;
7102
+ attachSchema(payload.issues, start, inst);
7103
+ return payload;
7104
+ }
7105
+ }
7106
+ }
7107
+ return parse3(payload, ctx);
7108
+ };
7109
+ }
7110
+ var arraySize = (input2) => Array.isArray(input2) ? input2.length : -1;
7111
+ var mapSize = (input2) => input2 instanceof Map ? input2.size : -1;
7112
+ var setSize = (input2) => input2 instanceof Set ? input2.size : -1;
7075
7113
  function handleArrayResult(result, final, index) {
7076
7114
  if (result.issues.length) {
7077
7115
  final.issues.push(...prefixIssues(index, result.issues));
@@ -7115,6 +7153,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
7115
7153
  }
7116
7154
  return payload;
7117
7155
  };
7156
+ capSize(inst, arraySize);
7118
7157
  });
7119
7158
  function handlePropertyResult(result, final, key, input2, optin, optout) {
7120
7159
  const isPresent = key in input2;
@@ -8071,6 +8110,7 @@ var $ZodMap = /* @__PURE__ */ $constructor("$ZodMap", (inst, def) => {
8071
8110
  return Promise.all(proms).then(() => payload);
8072
8111
  return payload;
8073
8112
  };
8113
+ capSize(inst, mapSize);
8074
8114
  });
8075
8115
  function handleMapResult(keyResult, valueResult, final, key, input2, inst, ctx) {
8076
8116
  if (keyResult.issues.length) {
@@ -8137,6 +8177,7 @@ var $ZodSet = /* @__PURE__ */ $constructor("$ZodSet", (inst, def) => {
8137
8177
  return Promise.all(proms).then(() => payload);
8138
8178
  return payload;
8139
8179
  };
8180
+ capSize(inst, setSize);
8140
8181
  });
8141
8182
  function handleSetResult(result, final) {
8142
8183
  if (result.issues.length) {
@@ -8733,7 +8774,7 @@ function handleRefineResult(result, payload, input2, inst) {
8733
8774
  }
8734
8775
  }
8735
8776
 
8736
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/memoizer.js
8777
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/memoizer.js
8737
8778
  var $ZodCyclicError = class extends Error {
8738
8779
  constructor() {
8739
8780
  super(`Cannot parse a reference cycle that closes through a transform`);
@@ -9010,7 +9051,7 @@ function isBackEdge(ctx, value) {
9010
9051
  return backEdges !== void 0 && isRef(value) && backEdges.has(value);
9011
9052
  }
9012
9053
 
9013
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/index.js
9054
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/index.js
9014
9055
  var locales_exports = {};
9015
9056
  __export(locales_exports, {
9016
9057
  ar: () => ar_default,
@@ -9078,7 +9119,7 @@ __export(locales_exports, {
9078
9119
  zhTW: () => zh_TW_default
9079
9120
  });
9080
9121
 
9081
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ar.js
9122
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ar.js
9082
9123
  var error = () => {
9083
9124
  const Sizable = {
9084
9125
  string: { unit: "\u062D\u0631\u0641", verb: "\u0623\u0646 \u064A\u062D\u0648\u064A" },
@@ -9190,7 +9231,7 @@ function ar_default() {
9190
9231
  };
9191
9232
  }
9192
9233
 
9193
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/az.js
9234
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/az.js
9194
9235
  var error2 = () => {
9195
9236
  const Sizable = {
9196
9237
  string: { unit: "simvol", verb: "olmal\u0131d\u0131r" },
@@ -9301,7 +9342,7 @@ function az_default() {
9301
9342
  };
9302
9343
  }
9303
9344
 
9304
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/be.js
9345
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/be.js
9305
9346
  function getBelarusianPlural(count, one, few, many) {
9306
9347
  const absCount = Math.abs(count);
9307
9348
  const lastDigit = absCount % 10;
@@ -9470,7 +9511,7 @@ function be_default() {
9470
9511
  };
9471
9512
  }
9472
9513
 
9473
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/bg.js
9514
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/bg.js
9474
9515
  var error4 = () => {
9475
9516
  const Sizable = {
9476
9517
  string: { unit: "\u0441\u0438\u043C\u0432\u043E\u043B\u0430", verb: "\u0434\u0430 \u0441\u044A\u0434\u044A\u0440\u0436\u0430" },
@@ -9596,7 +9637,7 @@ function bg_default() {
9596
9637
  };
9597
9638
  }
9598
9639
 
9599
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/bn.js
9640
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/bn.js
9600
9641
  var error5 = () => {
9601
9642
  const Sizable = {
9602
9643
  string: { unit: "\u0985\u0995\u09CD\u09B7\u09B0", verb: "\u09A5\u09BE\u0995\u09A4\u09C7 \u09B9\u09AC\u09C7" },
@@ -9710,7 +9751,7 @@ function bn_default() {
9710
9751
  };
9711
9752
  }
9712
9753
 
9713
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ca.js
9754
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ca.js
9714
9755
  var error6 = () => {
9715
9756
  const Sizable = {
9716
9757
  string: { unit: "car\xE0cters", verb: "contenir" },
@@ -9824,7 +9865,7 @@ function ca_default() {
9824
9865
  };
9825
9866
  }
9826
9867
 
9827
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ckb.js
9868
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ckb.js
9828
9869
  var error7 = () => {
9829
9870
  const Sizable = {
9830
9871
  string: { unit: "\u067E\u06CC\u062A", verb: "\u0628\u06CE\u062A" },
@@ -9957,7 +9998,7 @@ function ckb_default() {
9957
9998
  };
9958
9999
  }
9959
10000
 
9960
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/cs.js
10001
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/cs.js
9961
10002
  var error8 = () => {
9962
10003
  const Sizable = {
9963
10004
  string: { unit: "znak\u016F", verb: "m\xEDt" },
@@ -10074,7 +10115,7 @@ function cs_default() {
10074
10115
  };
10075
10116
  }
10076
10117
 
10077
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/da.js
10118
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/da.js
10078
10119
  var error9 = () => {
10079
10120
  const Sizable = {
10080
10121
  string: { unit: "tegn", verb: "havde" },
@@ -10195,7 +10236,7 @@ function da_default() {
10195
10236
  };
10196
10237
  }
10197
10238
 
10198
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/de.js
10239
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/de.js
10199
10240
  var error10 = () => {
10200
10241
  const Sizable = {
10201
10242
  string: { unit: "Zeichen", verb: "zu haben" },
@@ -10309,7 +10350,7 @@ function de_default() {
10309
10350
  };
10310
10351
  }
10311
10352
 
10312
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/el.js
10353
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/el.js
10313
10354
  var error11 = () => {
10314
10355
  const Sizable = {
10315
10356
  string: { unit: "\u03C7\u03B1\u03C1\u03B1\u03BA\u03C4\u03AE\u03C1\u03B5\u03C2", verb: "\u03BD\u03B1 \u03AD\u03C7\u03B5\u03B9" },
@@ -10422,7 +10463,7 @@ function el_default() {
10422
10463
  };
10423
10464
  }
10424
10465
 
10425
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/en.js
10466
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/en.js
10426
10467
  var error12 = () => {
10427
10468
  const Sizable = {
10428
10469
  string: { unit: "characters", verb: "to have" },
@@ -10547,7 +10588,7 @@ function en_default() {
10547
10588
  };
10548
10589
  }
10549
10590
 
10550
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/eo.js
10591
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/eo.js
10551
10592
  var error13 = () => {
10552
10593
  const Sizable = {
10553
10594
  string: { unit: "karaktrojn", verb: "havi" },
@@ -10662,7 +10703,7 @@ function eo_default() {
10662
10703
  };
10663
10704
  }
10664
10705
 
10665
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/es.js
10706
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/es.js
10666
10707
  var error14 = () => {
10667
10708
  const Sizable = {
10668
10709
  string: { unit: "caracteres", verb: "tener" },
@@ -10799,7 +10840,7 @@ function es_default() {
10799
10840
  };
10800
10841
  }
10801
10842
 
10802
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/fa.js
10843
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/fa.js
10803
10844
  var error15 = () => {
10804
10845
  const Sizable = {
10805
10846
  string: { unit: "\u06A9\u0627\u0631\u0627\u06A9\u062A\u0631", verb: "\u062F\u0627\u0634\u062A\u0647 \u0628\u0627\u0634\u062F" },
@@ -10919,7 +10960,7 @@ function fa_default() {
10919
10960
  };
10920
10961
  }
10921
10962
 
10922
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/fi.js
10963
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/fi.js
10923
10964
  var error16 = () => {
10924
10965
  const Sizable = {
10925
10966
  string: { unit: "merkki\xE4", subject: "merkkijonon" },
@@ -11037,7 +11078,7 @@ function fi_default() {
11037
11078
  };
11038
11079
  }
11039
11080
 
11040
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/fr.js
11081
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/fr.js
11041
11082
  var error17 = () => {
11042
11083
  const Sizable = {
11043
11084
  string: { unit: "caract\xE8res", verb: "avoir" },
@@ -11167,7 +11208,7 @@ function fr_default() {
11167
11208
  };
11168
11209
  }
11169
11210
 
11170
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/fr-CA.js
11211
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/fr-CA.js
11171
11212
  var error18 = () => {
11172
11213
  const Sizable = {
11173
11214
  string: { unit: "caract\xE8res", verb: "avoir" },
@@ -11280,7 +11321,7 @@ function fr_CA_default() {
11280
11321
  };
11281
11322
  }
11282
11323
 
11283
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/gu.js
11324
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/gu.js
11284
11325
  var error19 = () => {
11285
11326
  const Sizable = {
11286
11327
  string: { unit: "\u0A85\u0A95\u0ACD\u0AB7\u0AB0", verb: "\u0AB9\u0ACB\u0AB5\u0ABE \u0A9C\u0ACB\u0A88\u0A8F" },
@@ -11394,7 +11435,7 @@ function gu_default() {
11394
11435
  };
11395
11436
  }
11396
11437
 
11397
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/he.js
11438
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/he.js
11398
11439
  var error20 = () => {
11399
11440
  const TypeNames = {
11400
11441
  string: { label: "\u05DE\u05D7\u05E8\u05D5\u05D6\u05EA", gender: "f" },
@@ -11596,7 +11637,7 @@ function he_default() {
11596
11637
  };
11597
11638
  }
11598
11639
 
11599
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/hi.js
11640
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/hi.js
11600
11641
  var error21 = () => {
11601
11642
  const Sizable = {
11602
11643
  string: { unit: "\u0905\u0915\u094D\u0937\u0930", verb: "\u0930\u0916\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F" },
@@ -11708,7 +11749,7 @@ function hi_default() {
11708
11749
  };
11709
11750
  }
11710
11751
 
11711
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/hr.js
11752
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/hr.js
11712
11753
  var error22 = () => {
11713
11754
  const Sizable = {
11714
11755
  string: { unit: "znakova", verb: "imati" },
@@ -11835,7 +11876,7 @@ function hr_default() {
11835
11876
  };
11836
11877
  }
11837
11878
 
11838
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/hu.js
11879
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/hu.js
11839
11880
  var error23 = () => {
11840
11881
  const Sizable = {
11841
11882
  string: { unit: "karakter", verb: "legyen" },
@@ -11949,7 +11990,7 @@ function hu_default() {
11949
11990
  };
11950
11991
  }
11951
11992
 
11952
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/hy.js
11993
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/hy.js
11953
11994
  function getArmenianPlural(count, one, many) {
11954
11995
  return Math.abs(count) === 1 ? one : many;
11955
11996
  }
@@ -12108,7 +12149,7 @@ function hy_default() {
12108
12149
  };
12109
12150
  }
12110
12151
 
12111
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/id.js
12152
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/id.js
12112
12153
  var error25 = () => {
12113
12154
  const Sizable = {
12114
12155
  string: { unit: "karakter", verb: "memiliki" },
@@ -12220,7 +12261,7 @@ function id_default() {
12220
12261
  };
12221
12262
  }
12222
12263
 
12223
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/is.js
12264
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/is.js
12224
12265
  var error26 = () => {
12225
12266
  const Sizable = {
12226
12267
  string: { unit: "stafi", verb: "a\xF0 hafa" },
@@ -12335,7 +12376,7 @@ function is_default() {
12335
12376
  };
12336
12377
  }
12337
12378
 
12338
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/it.js
12379
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/it.js
12339
12380
  var error27 = () => {
12340
12381
  const Sizable = {
12341
12382
  string: { unit: "caratteri", verb: "avere" },
@@ -12449,7 +12490,7 @@ function it_default() {
12449
12490
  };
12450
12491
  }
12451
12492
 
12452
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ja.js
12493
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ja.js
12453
12494
  var error28 = () => {
12454
12495
  const Sizable = {
12455
12496
  string: { unit: "\u6587\u5B57", verb: "\u3067\u3042\u308B" },
@@ -12562,7 +12603,7 @@ function ja_default() {
12562
12603
  };
12563
12604
  }
12564
12605
 
12565
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ka.js
12606
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ka.js
12566
12607
  var error29 = () => {
12567
12608
  const Sizable = {
12568
12609
  string: { unit: "\u10E1\u10D8\u10DB\u10D1\u10DD\u10DA\u10DD", verb: "\u10E3\u10DC\u10D3\u10D0 \u10E8\u10D4\u10D8\u10EA\u10D0\u10D5\u10D3\u10D4\u10E1" },
@@ -12680,7 +12721,7 @@ function ka_default() {
12680
12721
  };
12681
12722
  }
12682
12723
 
12683
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/km.js
12724
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/km.js
12684
12725
  var error30 = () => {
12685
12726
  const Sizable = {
12686
12727
  string: { unit: "\u178F\u17BD\u17A2\u1780\u17D2\u179F\u179A", verb: "\u1782\u17BD\u179A\u1798\u17B6\u1793" },
@@ -12796,12 +12837,12 @@ function km_default() {
12796
12837
  };
12797
12838
  }
12798
12839
 
12799
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/kh.js
12840
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/kh.js
12800
12841
  function kh_default() {
12801
12842
  return km_default();
12802
12843
  }
12803
12844
 
12804
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/kn.js
12845
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/kn.js
12805
12846
  var error31 = () => {
12806
12847
  const Sizable = {
12807
12848
  string: { unit: "\u0C85\u0C95\u0CCD\u0CB7\u0CB0\u0C97\u0CB3\u0CC1", verb: "\u0CB9\u0CCA\u0C82\u0CA6\u0CB2\u0CC1" },
@@ -12917,7 +12958,7 @@ function kn_default() {
12917
12958
  };
12918
12959
  }
12919
12960
 
12920
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ko.js
12961
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ko.js
12921
12962
  var error32 = () => {
12922
12963
  const Sizable = {
12923
12964
  string: { unit: "\uBB38\uC790", verb: "to have" },
@@ -13034,7 +13075,7 @@ function ko_default() {
13034
13075
  };
13035
13076
  }
13036
13077
 
13037
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/lt.js
13078
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/lt.js
13038
13079
  var capitalizeFirstCharacter = (text) => {
13039
13080
  return text.charAt(0).toUpperCase() + text.slice(1);
13040
13081
  };
@@ -13242,7 +13283,7 @@ function lt_default() {
13242
13283
  };
13243
13284
  }
13244
13285
 
13245
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/mk.js
13286
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/mk.js
13246
13287
  var error34 = () => {
13247
13288
  const Sizable = {
13248
13289
  string: { unit: "\u0437\u043D\u0430\u0446\u0438", verb: "\u0434\u0430 \u0438\u043C\u0430\u0430\u0442" },
@@ -13357,7 +13398,7 @@ function mk_default() {
13357
13398
  };
13358
13399
  }
13359
13400
 
13360
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ms.js
13401
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ms.js
13361
13402
  var error35 = () => {
13362
13403
  const Sizable = {
13363
13404
  string: { unit: "aksara", verb: "mempunyai" },
@@ -13470,7 +13511,7 @@ function ms_default() {
13470
13511
  };
13471
13512
  }
13472
13513
 
13473
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ne.js
13514
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ne.js
13474
13515
  var error36 = () => {
13475
13516
  const Sizable = {
13476
13517
  string: { unit: "\u0905\u0915\u094D\u0937\u0930", verb: "\u0939\u0941\u0928\u0941\u092A\u0930\u094D\u091B" },
@@ -13582,7 +13623,7 @@ function ne_default() {
13582
13623
  };
13583
13624
  }
13584
13625
 
13585
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/nl.js
13626
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/nl.js
13586
13627
  var error37 = () => {
13587
13628
  const Sizable = {
13588
13629
  string: { unit: "tekens", verb: "heeft" },
@@ -13698,7 +13739,7 @@ function nl_default() {
13698
13739
  };
13699
13740
  }
13700
13741
 
13701
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/nn.js
13742
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/nn.js
13702
13743
  var error38 = () => {
13703
13744
  const Sizable = {
13704
13745
  string: { unit: "teikn", verb: "\xE5 ha" },
@@ -13812,7 +13853,7 @@ function nn_default() {
13812
13853
  };
13813
13854
  }
13814
13855
 
13815
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/no.js
13856
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/no.js
13816
13857
  var error39 = () => {
13817
13858
  const Sizable = {
13818
13859
  string: { unit: "tegn", verb: "\xE5 ha" },
@@ -13926,7 +13967,7 @@ function no_default() {
13926
13967
  };
13927
13968
  }
13928
13969
 
13929
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ota.js
13970
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ota.js
13930
13971
  var error40 = () => {
13931
13972
  const Sizable = {
13932
13973
  string: { unit: "harf", verb: "olmal\u0131d\u0131r" },
@@ -14041,7 +14082,7 @@ function ota_default() {
14041
14082
  };
14042
14083
  }
14043
14084
 
14044
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ps.js
14085
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ps.js
14045
14086
  var error41 = () => {
14046
14087
  const Sizable = {
14047
14088
  string: { unit: "\u062A\u0648\u06A9\u064A", verb: "\u0648\u0644\u0631\u064A" },
@@ -14161,7 +14202,7 @@ function ps_default() {
14161
14202
  };
14162
14203
  }
14163
14204
 
14164
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/pl.js
14205
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/pl.js
14165
14206
  var error42 = () => {
14166
14207
  const Sizable = {
14167
14208
  string: { unit: "znak\xF3w", verb: "mie\u0107" },
@@ -14276,7 +14317,7 @@ function pl_default() {
14276
14317
  };
14277
14318
  }
14278
14319
 
14279
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/pt.js
14320
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/pt.js
14280
14321
  var error43 = () => {
14281
14322
  const Sizable = {
14282
14323
  string: { unit: "caracteres" },
@@ -14420,7 +14461,7 @@ function pt_default() {
14420
14461
  };
14421
14462
  }
14422
14463
 
14423
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/pt-BR.js
14464
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/pt-BR.js
14424
14465
  var error44 = () => {
14425
14466
  const Sizable = {
14426
14467
  string: { unit: "caracteres" },
@@ -14565,7 +14606,7 @@ function pt_BR_default() {
14565
14606
  };
14566
14607
  }
14567
14608
 
14568
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ro.js
14609
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ro.js
14569
14610
  var error45 = () => {
14570
14611
  const Sizable = {
14571
14612
  string: { unit: "caractere", verb: "s\u0103 aib\u0103" },
@@ -14688,7 +14729,7 @@ function ro_default() {
14688
14729
  };
14689
14730
  }
14690
14731
 
14691
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ru.js
14732
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ru.js
14692
14733
  function getRussianPlural(count, one, few, many) {
14693
14734
  const absCount = Math.abs(count);
14694
14735
  const lastDigit = absCount % 10;
@@ -14857,7 +14898,7 @@ function ru_default() {
14857
14898
  };
14858
14899
  }
14859
14900
 
14860
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/sk.js
14901
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/sk.js
14861
14902
  var error47 = () => {
14862
14903
  const Sizable = {
14863
14904
  string: { unit: "znakov", verb: "ma\u0165" },
@@ -14974,7 +15015,7 @@ function sk_default() {
14974
15015
  };
14975
15016
  }
14976
15017
 
14977
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/sl.js
15018
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/sl.js
14978
15019
  var error48 = () => {
14979
15020
  const Sizable = {
14980
15021
  string: { unit: "znakov", verb: "imeti" },
@@ -15089,7 +15130,7 @@ function sl_default() {
15089
15130
  };
15090
15131
  }
15091
15132
 
15092
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/sv.js
15133
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/sv.js
15093
15134
  var error49 = () => {
15094
15135
  const Sizable = {
15095
15136
  string: { unit: "tecken", verb: "att ha" },
@@ -15205,7 +15246,7 @@ function sv_default() {
15205
15246
  };
15206
15247
  }
15207
15248
 
15208
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ta.js
15249
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ta.js
15209
15250
  var error50 = () => {
15210
15251
  const Sizable = {
15211
15252
  string: { unit: "\u0B8E\u0BB4\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1\u0B95\u0BCD\u0B95\u0BB3\u0BCD", verb: "\u0B95\u0BCA\u0BA3\u0BCD\u0B9F\u0BBF\u0BB0\u0BC1\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD" },
@@ -15321,7 +15362,7 @@ function ta_default() {
15321
15362
  };
15322
15363
  }
15323
15364
 
15324
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/tg.js
15365
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/tg.js
15325
15366
  var error51 = () => {
15326
15367
  const Sizable = {
15327
15368
  string: { unit: "\u0430\u043B\u043E\u043C\u0430\u0442", verb: "\u0434\u043E\u0448\u0442\u0430 \u0431\u043E\u0448\u0430\u0434" },
@@ -15438,7 +15479,7 @@ function tg_default() {
15438
15479
  };
15439
15480
  }
15440
15481
 
15441
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/th.js
15482
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/th.js
15442
15483
  var error52 = () => {
15443
15484
  const Sizable = {
15444
15485
  string: { unit: "\u0E15\u0E31\u0E27\u0E2D\u0E31\u0E01\u0E29\u0E23", verb: "\u0E04\u0E27\u0E23\u0E21\u0E35" },
@@ -15554,7 +15595,7 @@ function th_default() {
15554
15595
  };
15555
15596
  }
15556
15597
 
15557
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/tk.js
15598
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/tk.js
15558
15599
  var error53 = () => {
15559
15600
  const Sizable = {
15560
15601
  string: { unit: "simwol", verb: "bolmaly" },
@@ -15662,7 +15703,7 @@ function tk_default() {
15662
15703
  };
15663
15704
  }
15664
15705
 
15665
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/tr.js
15706
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/tr.js
15666
15707
  var error54 = () => {
15667
15708
  const Sizable = {
15668
15709
  string: { unit: "karakter", verb: "olmal\u0131" },
@@ -15773,7 +15814,7 @@ function tr_default() {
15773
15814
  };
15774
15815
  }
15775
15816
 
15776
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/uk.js
15817
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/uk.js
15777
15818
  var error55 = () => {
15778
15819
  const Sizable = {
15779
15820
  string: { unit: "\u0441\u0438\u043C\u0432\u043E\u043B\u0456\u0432", verb: "\u043C\u0430\u0442\u0438\u043C\u0435" },
@@ -15887,12 +15928,12 @@ function uk_default() {
15887
15928
  };
15888
15929
  }
15889
15930
 
15890
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ua.js
15931
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ua.js
15891
15932
  function ua_default() {
15892
15933
  return uk_default();
15893
15934
  }
15894
15935
 
15895
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ur.js
15936
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/ur.js
15896
15937
  var error56 = () => {
15897
15938
  const Sizable = {
15898
15939
  string: { unit: "\u062D\u0631\u0648\u0641", verb: "\u06C1\u0648\u0646\u0627" },
@@ -16008,7 +16049,7 @@ function ur_default() {
16008
16049
  };
16009
16050
  }
16010
16051
 
16011
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/uz.js
16052
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/uz.js
16012
16053
  var error57 = () => {
16013
16054
  const Sizable = {
16014
16055
  string: { unit: "belgi", verb: "bo\u2018lishi kerak" },
@@ -16122,7 +16163,7 @@ function uz_default() {
16122
16163
  };
16123
16164
  }
16124
16165
 
16125
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/vi.js
16166
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/vi.js
16126
16167
  var error58 = () => {
16127
16168
  const Sizable = {
16128
16169
  string: { unit: "k\xFD t\u1EF1", verb: "c\xF3" },
@@ -16236,7 +16277,7 @@ function vi_default() {
16236
16277
  };
16237
16278
  }
16238
16279
 
16239
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/zh-CN.js
16280
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/zh-CN.js
16240
16281
  var error59 = () => {
16241
16282
  const Sizable = {
16242
16283
  string: { unit: "\u5B57\u7B26", verb: "\u5305\u542B" },
@@ -16351,7 +16392,7 @@ function zh_CN_default() {
16351
16392
  };
16352
16393
  }
16353
16394
 
16354
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/zh-TW.js
16395
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/zh-TW.js
16355
16396
  var error60 = () => {
16356
16397
  const Sizable = {
16357
16398
  string: { unit: "\u5B57\u5143", verb: "\u64C1\u6709" },
@@ -16464,7 +16505,7 @@ function zh_TW_default() {
16464
16505
  };
16465
16506
  }
16466
16507
 
16467
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/yo.js
16508
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/yo.js
16468
16509
  var error61 = () => {
16469
16510
  const Sizable = {
16470
16511
  string: { unit: "\xE0mi", verb: "n\xED" },
@@ -16577,7 +16618,7 @@ function yo_default() {
16577
16618
  };
16578
16619
  }
16579
16620
 
16580
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/registries.js
16621
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/registries.js
16581
16622
  var _a2;
16582
16623
  var $output = /* @__PURE__ */ Symbol("ZodOutput");
16583
16624
  var $input = /* @__PURE__ */ Symbol("ZodInput");
@@ -16627,7 +16668,7 @@ function registry() {
16627
16668
  (_a2 = globalThis).__zod_globalRegistry ?? (_a2.__zod_globalRegistry = registry());
16628
16669
  var globalRegistry = globalThis.__zod_globalRegistry;
16629
16670
 
16630
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/compile.js
16671
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/compile.js
16631
16672
  var INVALID = /* @__PURE__ */ Symbol.for("zod.compile.invalid");
16632
16673
  var FALLBACK_FLAG = /* @__PURE__ */ Symbol.for("zod.compile.fallback");
16633
16674
  var ZodCompileAsyncError = class extends Error {
@@ -17667,9 +17708,15 @@ function generateNullableCheck(doc, ctx, schema, accessor, buildsValue = true) {
17667
17708
  doc.write(`}`);
17668
17709
  return outputVar;
17669
17710
  }
17711
+ function generateSizeGate(doc, schema, size) {
17712
+ for (const [max, check2] of sizeCaps(schema._zod.def.checks)) {
17713
+ doc.write(`if (${size} > ${numericOperand(max, check2._zod.def.check)}) return INVALID;`);
17714
+ }
17715
+ }
17670
17716
  function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
17671
17717
  const def = schema._zod.def;
17672
17718
  doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
17719
+ generateSizeGate(doc, schema, `${accessor}.length`);
17673
17720
  const outputVar = buildsValue ? newVar(ctx) : null;
17674
17721
  const iVar = newVar(ctx);
17675
17722
  const elemVar = newVar(ctx);
@@ -18069,6 +18116,7 @@ function literalPropertyKey(ctx, key) {
18069
18116
  function generateMapCheck(doc, ctx, schema, accessor) {
18070
18117
  const def = schema._zod.def;
18071
18118
  doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
18119
+ generateSizeGate(doc, schema, `${accessor}.size`);
18072
18120
  const outputVar = newVar(ctx);
18073
18121
  const kVar = newVar(ctx);
18074
18122
  const valVar = newVar(ctx);
@@ -18085,6 +18133,7 @@ function generateMapCheck(doc, ctx, schema, accessor) {
18085
18133
  function generateSetCheck(doc, ctx, schema, accessor) {
18086
18134
  const def = schema._zod.def;
18087
18135
  doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
18136
+ generateSizeGate(doc, schema, `${accessor}.size`);
18088
18137
  const outputVar = newVar(ctx);
18089
18138
  const valVar = newVar(ctx);
18090
18139
  doc.write(`const ${outputVar} = new Set();`);
@@ -18229,7 +18278,7 @@ function generateTransformCheck(doc, ctx, schema, accessor) {
18229
18278
  return accessor;
18230
18279
  }
18231
18280
 
18232
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/api.js
18281
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/api.js
18233
18282
  function snapshotChecks(def) {
18234
18283
  if (def.checks)
18235
18284
  def.checks = [...def.checks];
@@ -19288,7 +19337,7 @@ function _stringFormat(Class2, format, fnOrRegex, _params = {}) {
19288
19337
  return inst;
19289
19338
  }
19290
19339
 
19291
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/to-json-schema.js
19340
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/to-json-schema.js
19292
19341
  function assignProps(target, ...sources) {
19293
19342
  for (const source of sources) {
19294
19343
  for (const key of Reflect.ownKeys(source)) {
@@ -19818,7 +19867,7 @@ var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) =
19818
19867
  return finalize(ctx, schema);
19819
19868
  };
19820
19869
 
19821
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/json-schema-processors.js
19870
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/json-schema-processors.js
19822
19871
  var narrowMin = (agg, key, value) => {
19823
19872
  if (agg[key] === void 0 || value > agg[key])
19824
19873
  agg[key] = value;
@@ -20566,7 +20615,7 @@ function toJSONSchema(input2, params) {
20566
20615
  return finalize(ctx, input2);
20567
20616
  }
20568
20617
 
20569
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/json-schema-generator.js
20618
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/json-schema-generator.js
20570
20619
  var JSONSchemaGenerator = class {
20571
20620
  /** @deprecated Access via ctx instead */
20572
20621
  get metadataRegistry() {
@@ -20644,10 +20693,10 @@ var JSONSchemaGenerator = class {
20644
20693
  }
20645
20694
  };
20646
20695
 
20647
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/json-schema.js
20696
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/json-schema.js
20648
20697
  var json_schema_exports = {};
20649
20698
 
20650
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/schemas.js
20699
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/schemas.js
20651
20700
  var schemas_exports2 = {};
20652
20701
  __export(schemas_exports2, {
20653
20702
  ZodAny: () => ZodAny,
@@ -20828,7 +20877,7 @@ __export(schemas_exports2, {
20828
20877
  xor: () => xor
20829
20878
  });
20830
20879
 
20831
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/checks.js
20880
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/checks.js
20832
20881
  var checks_exports2 = {};
20833
20882
  __export(checks_exports2, {
20834
20883
  endsWith: () => _endsWith,
@@ -20863,7 +20912,7 @@ __export(checks_exports2, {
20863
20912
  uppercase: () => _uppercase
20864
20913
  });
20865
20914
 
20866
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/errors.js
20915
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/errors.js
20867
20916
  var _installedErrorProtos = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);
20868
20917
  function _lazyMethod(proto, key, make) {
20869
20918
  Object.defineProperty(proto, key, {
@@ -20909,7 +20958,7 @@ var ZodRealError = /* @__PURE__ */ $constructor("ZodError", initializer2, void 0
20909
20958
  Parent: Error
20910
20959
  });
20911
20960
 
20912
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/parse.js
20961
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/parse.js
20913
20962
  var parse2 = /* @__PURE__ */ _parse(ZodRealError);
20914
20963
  var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
20915
20964
  var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
@@ -20923,7 +20972,7 @@ var safeDecode2 = /* @__PURE__ */ _safeDecode(ZodRealError);
20923
20972
  var safeEncodeAsync2 = /* @__PURE__ */ _safeEncodeAsync(ZodRealError);
20924
20973
  var safeDecodeAsync2 = /* @__PURE__ */ _safeDecodeAsync(ZodRealError);
20925
20974
 
20926
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/schemas.js
20975
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/schemas.js
20927
20976
  function _ensureDefaultLocale() {
20928
20977
  if (!globalConfig.localeError)
20929
20978
  config(en_default());
@@ -22388,7 +22437,7 @@ function preprocess(fn, schema) {
22388
22437
  });
22389
22438
  }
22390
22439
 
22391
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/compat.js
22440
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/compat.js
22392
22441
  var ZodIssueCode = {
22393
22442
  invalid_type: "invalid_type",
22394
22443
  too_big: "too_big",
@@ -22414,7 +22463,7 @@ var ZodFirstPartyTypeKind;
22414
22463
  /* @__PURE__ */ (function(ZodFirstPartyTypeKind2) {
22415
22464
  })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
22416
22465
 
22417
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/iso.js
22466
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/iso.js
22418
22467
  var iso_exports = {};
22419
22468
  __export(iso_exports, {
22420
22469
  ZodISODate: () => ZodISODate,
@@ -22439,7 +22488,7 @@ function duration2(params) {
22439
22488
  return _isoDuration(ZodISODuration, params);
22440
22489
  }
22441
22490
 
22442
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/from-json-schema.js
22491
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/from-json-schema.js
22443
22492
  var z = {
22444
22493
  ...schemas_exports2,
22445
22494
  ...checks_exports2,
@@ -23172,7 +23221,7 @@ function fromJSONSchema(schema, params) {
23172
23221
  return convertSchema(normalized, ctx);
23173
23222
  }
23174
23223
 
23175
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/visit.js
23224
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/visit.js
23176
23225
  var RESOLVING = /* @__PURE__ */ Symbol("z.visit/resolving");
23177
23226
  function visit(schema, fnOrHandlers) {
23178
23227
  const fn = typeof fnOrHandlers === "function" ? fnOrHandlers : (node2, rewritten) => {
@@ -23327,7 +23376,7 @@ function visit(schema, fnOrHandlers) {
23327
23376
  return run(schema);
23328
23377
  }
23329
23378
 
23330
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/deep-partial.js
23379
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/deep-partial.js
23331
23380
  function deepPartial(schema) {
23332
23381
  return visit(schema, {
23333
23382
  object: (s) => s.partial(),
@@ -23339,7 +23388,7 @@ function deepPartial(schema) {
23339
23388
  });
23340
23389
  }
23341
23390
 
23342
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/in-out.js
23391
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/in-out.js
23343
23392
  function withChecks(side, checks) {
23344
23393
  if (!checks?.length)
23345
23394
  return side;
@@ -23369,7 +23418,7 @@ function output(schema) {
23369
23418
  });
23370
23419
  }
23371
23420
 
23372
- // node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/coerce.js
23421
+ // node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/coerce.js
23373
23422
  var coerce_exports = {};
23374
23423
  __export(coerce_exports, {
23375
23424
  bigint: () => bigint3,
@@ -23394,7 +23443,7 @@ function date4(params) {
23394
23443
  return _coercedDate(ZodDate, params);
23395
23444
  }
23396
23445
 
23397
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.7/node_modules/@dsh-remote/protocol/dist/src/wire/limits.js
23446
+ // node_modules/.pnpm/@dsh-remote+protocol@3.1.4/node_modules/@dsh-remote/protocol/dist/src/wire/limits.js
23398
23447
  var MAX_RELAY_MESSAGE_BYTES = 1024 * 1024;
23399
23448
  var MAX_CIPHERTEXT_BYTES = MAX_RELAY_MESSAGE_BYTES - 8 * 1024;
23400
23449
  var MAX_IMAGE_ATTACHMENTS = 4;
@@ -23403,13 +23452,11 @@ var MAX_ATTACHMENT_BYTES = 512 * 1024;
23403
23452
  var MAX_ATTACHMENT_TOTAL_BYTES = 512 * 1024;
23404
23453
  var IDEMPOTENCY_WINDOW_MS = 5 * 60 * 1e3;
23405
23454
 
23406
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.7/node_modules/@dsh-remote/protocol/dist/src/wire/frames.js
23455
+ // node_modules/.pnpm/@dsh-remote+protocol@3.1.4/node_modules/@dsh-remote/protocol/dist/src/wire/frames.js
23407
23456
  var PROTOCOL_VERSION = 1;
23408
- var MIN_SUPPORTED_PROTOCOL = 1;
23409
23457
  var PAIRING_TOKEN_RE = /^\d{6}$/;
23410
23458
  var nonEmpty = external_exports.string().min(1);
23411
23459
  var pairingToken = external_exports.string().regex(PAIRING_TOKEN_RE);
23412
- var capabilityList = external_exports.array(external_exports.string().min(1).max(64)).max(64);
23413
23460
  var base64Text = external_exports.string().min(1).regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/);
23414
23461
  var clientMeta = external_exports.object({
23415
23462
  /**
@@ -23424,12 +23471,8 @@ var clientMeta = external_exports.object({
23424
23471
  var helloFrame = external_exports.object({
23425
23472
  t: external_exports.literal("hello"),
23426
23473
  role: external_exports.enum(["host", "client"]),
23427
- protocol: external_exports.number().int().positive().optional(),
23428
- /**
23429
- * 本端支持的能力(规范 §5.3)。**可选**:老对端不带,老中继也照旧工作——
23430
- * 缺省按"只支持基线"处理,而不是按"什么都不支持"拒绝。
23431
- */
23432
- capabilities: capabilityList.optional(),
23474
+ /** **必填**:两端必须同版本(单版本制,见 `negotiate.ts`)。 */
23475
+ protocol: external_exports.number().int().positive(),
23433
23476
  /** host 的出站认证凭据,与中继的 `DRC_HOST_TOKEN` 定时安全比较。上限防超长串白占内存。 */
23434
23477
  token: external_exports.string().min(1).max(512).optional(),
23435
23478
  hostId: nonEmpty.optional(),
@@ -23494,9 +23537,7 @@ var helloOkFrame = external_exports.object({
23494
23537
  /** 权威 clientId:小程序会采纳它并覆盖本地安装 id(F2/§3.6)。 */
23495
23538
  clientId: external_exports.string().min(1).max(128).optional(),
23496
23539
  hostId: nonEmpty.optional(),
23497
- protocol: external_exports.number().int().positive().optional(),
23498
- /** 中继自己支持的能力(规范 §5.3)。端点据此知道自己能向它要什么。 */
23499
- capabilities: capabilityList.optional()
23540
+ protocol: external_exports.number().int().positive()
23500
23541
  });
23501
23542
  var pairReadyFrame = external_exports.object({
23502
23543
  t: external_exports.literal("pair-ready"),
@@ -23541,11 +23582,11 @@ var pairFailFrame = external_exports.object({
23541
23582
  * 症状不是"多扫一次码":B 的 PSK 被丢弃意味着那条配对通道作废,
23542
23583
  * 而这正是文件头引用的那起「取错 PSK 全线解不开」事故的前置条件。
23543
23584
  *
23544
- * ## 兼容
23585
+ * ## 为什么可选
23545
23586
  *
23546
- * **可选**:更老的中继不发它,主机侧必须继续按"当前展示的那张"兜底
23547
- * (`onPairFail` 的注释里写着这条兜底的原因)。发它的是中继、读它的是主机,
23548
- * 同一时刻两端版本必然一致——所以"新主机 + 老中继"才是需要兜底的那一侧。
23587
+ * **中继不总能知道是哪一张**:帧本身坏掉那条分支(`server.ts` 里 `bad_frame` 那条)
23588
+ * 手上没有 token 可点名。缺省时主机按"当前展示的那张"兜底
23589
+ * (`onPairFail` 的注释里写着这条兜底的原因)。
23549
23590
  */
23550
23591
  pairingToken: external_exports.string().min(1).max(16).optional()
23551
23592
  });
@@ -23568,11 +23609,12 @@ var peerLeftFrame = external_exports.object({
23568
23609
  * **再也不会带这个 convId 回来**。主机留着这条会话就是一条永远清不掉的幽灵。
23569
23610
  * - 手机 socket 断了(切后台、断网):D3 要求会话留着,回前台还要用它。
23570
23611
  *
23571
- * 不带这个标记 = 断线(老中继行为,保持 D3);带 true = 主动解配,主机应当把
23572
- * 会话一并作废,于是 pill 从「手机离线」回到「未配对」(2026-10-05 用户报:
23573
- * 「mp 端解除配对,dsh 端执行的是手机离线」)。
23612
+ * 不带这个标记 = **断线**(D3:会话留着,回前台还要用);带 true = 主动解配,
23613
+ * 主机应当把会话一并作废,于是 pill 从「手机离线」回到「未配对」
23614
+ * (2026-10-05 用户报:「mp 端解除配对,dsh 端执行的是手机离线」)。
23574
23615
  *
23575
- * 可选是刻意的:老中继不会带这个字段,主机必须仍按「断线」处理,不能假定。
23616
+ * 可选是**语义**上的:它表达"这次离开是断线还是解配",缺省必须是断线
23617
+ * (主机不许假定是解配 —— 那会把用户的一条会话凭空作废)。
23576
23618
  */
23577
23619
  unpaired: external_exports.boolean().optional()
23578
23620
  });
@@ -23609,12 +23651,12 @@ var errorCodes = [
23609
23651
  */
23610
23652
  "history_window_exceeded",
23611
23653
  /**
23612
- * 对端的对端版本落在 `[MIN_SUPPORTED_PROTOCOL, 本端]` 之外(规范 §5.2 V3)。
23654
+ * 对端报的协议版本与本端**不相等**(含没报,规范 §5.2 V3)。
23613
23655
  *
23614
23656
  * 为什么不复用 `bad_frame`:它是**版本问题**而不是形状问题,而它唯一该触发的
23615
23657
  * 动作是"把版本说清楚"——一句"帧形状非法"会让用户去查一个根本不存在的问题。
23616
- * 加它不破坏任何老实现:老 client 见到不认识的码会走兜底展示(`message || code`),
23617
- * 所以中继 MUST 同时带上一句中文 message(规范 §12.3 E1)。
23658
+ * 不认识的码在客户端会走兜底展示(`message || code`),所以中继 MUST 同时带上一句
23659
+ * 中文 message(规范 §12.3 E1)—— 反过来,"没带 message"就是把英文码弹给用户。
23618
23660
  */
23619
23661
  "unsupported_protocol",
23620
23662
  "internal"
@@ -23660,11 +23702,14 @@ var Budget = class {
23660
23702
  windowMs;
23661
23703
  windowAt = 0;
23662
23704
  left = 0;
23705
+ /** 有没有真的开过窗口(见 `take` 的第 ② 条)。 */
23706
+ armed = false;
23663
23707
  /** 取一个配额;成功 true,用尽 false。 */
23664
23708
  take(now) {
23665
- if (now - this.windowAt >= this.windowMs || now < this.windowAt) {
23709
+ if (!this.armed || now - this.windowAt >= this.windowMs || now < this.windowAt) {
23666
23710
  this.windowAt = now;
23667
23711
  this.left = this.perWindow;
23712
+ this.armed = true;
23668
23713
  }
23669
23714
  if (this.left <= 0) return false;
23670
23715
  this.left -= 1;
@@ -23818,6 +23863,9 @@ function loadConfig(env, version2) {
23818
23863
  pairStatusBudgetPerSec: integer2(env.DRC_PAIR_STATUS_PER_SEC, 5, "DRC_PAIR_STATUS_PER_SEC", problems),
23819
23864
  maxPendingPairs: integer2(env.DRC_MAX_PENDING_PAIRS, 1e3, "DRC_MAX_PENDING_PAIRS", problems),
23820
23865
  maxHostTokens: integer2(env.DRC_MAX_HOST_TOKENS, 500, "DRC_MAX_HOST_TOKENS", problems),
23866
+ // D6:会话表是唯一曾经没有上界的一张表,而每次认领都要同步写它(成本 ∝ 表大小)。
23867
+ // 默认与依据见 `RelayConfig.maxConversations` 的注释。
23868
+ maxConversations: integer2(env.DRC_MAX_CONVERSATIONS, 5e3, "DRC_MAX_CONVERSATIONS", problems),
23821
23869
  claimMinIntervalMs: claimMinIntervalMs(env, problems),
23822
23870
  conversationIdleTtlMs: integer2(env.DRC_CONV_IDLE_TTL_MS, 7 * 24 * 3600 * 1e3, "DRC_CONV_IDLE_TTL_MS", problems),
23823
23871
  // P2-⑤(2026-10-04 用户拍板 30 分钟):远小于 7 天的空闲 TTL。
@@ -23882,7 +23930,7 @@ var import_subprotocol = __toESM(require_subprotocol(), 1);
23882
23930
  var import_websocket = __toESM(require_websocket(), 1);
23883
23931
  var import_websocket_server = __toESM(require_websocket_server(), 1);
23884
23932
 
23885
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.7/node_modules/@dsh-remote/protocol/dist/src/identity/ids.js
23933
+ // node_modules/.pnpm/@dsh-remote+protocol@3.1.4/node_modules/@dsh-remote/protocol/dist/src/identity/ids.js
23886
23934
  import { randomBytes, randomUUID } from "node:crypto";
23887
23935
  var CONVERSATION_ID_PREFIX = "c_";
23888
23936
  function newConversationId() {
@@ -23892,10 +23940,52 @@ function newHostId() {
23892
23940
  return randomBytes(4).toString("hex");
23893
23941
  }
23894
23942
 
23895
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.7/node_modules/@dsh-remote/protocol/dist/src/platform/sleep.js
23943
+ // node_modules/.pnpm/@dsh-remote+protocol@3.1.4/node_modules/@dsh-remote/protocol/dist/src/wire/negotiate.js
23944
+ function negotiateProtocol(peerProtocol, own2 = PROTOCOL_VERSION) {
23945
+ if (peerProtocol === void 0 || peerProtocol === null) {
23946
+ return {
23947
+ ok: false,
23948
+ reason: "missing",
23949
+ peer: peerProtocol,
23950
+ message: `\u5BF9\u65B9\u6CA1\u6709\u62A5\u534F\u8BAE\u7248\u672C\uFF08\u672C\u7AEF\u4E3A ${own2}\uFF09\uFF1A\u628A\u5BF9\u7AEF\u5347\u5230\u540C\u4E00\u7248\u672C\u518D\u8BD5`
23951
+ };
23952
+ }
23953
+ if (typeof peerProtocol !== "number" || !Number.isInteger(peerProtocol) || peerProtocol <= 0) {
23954
+ return {
23955
+ ok: false,
23956
+ reason: "invalid",
23957
+ peer: peerProtocol,
23958
+ message: `\u5BF9\u65B9\u62A5\u544A\u7684\u534F\u8BAE\u7248\u672C\u4E0D\u662F\u6B63\u6574\u6570\uFF08\u6536\u5230 ${JSON.stringify(peerProtocol) ?? "undefined"}\uFF09`
23959
+ };
23960
+ }
23961
+ if (peerProtocol !== own2) {
23962
+ return {
23963
+ ok: false,
23964
+ reason: "mismatch",
23965
+ peer: peerProtocol,
23966
+ /**
23967
+ * ⚠️ 这句话**会被原样弹到用户手机上**(中继 `acceptProtocol` 显式带 message,
23968
+ * 而客户端的处理是 `f.message || f.code`)。所以人称必须从**读它的人**出发:
23969
+ * 收这条错的那端才是要被升级的那端。写"请把本端升级"时,手机用户读到的是
23970
+ * "请升级我的手机",而真正落后的是中继/主机那一侧 —— 两个方向都试过,
23971
+ * 都会指错人。⇒ 只陈述事实(本端 X、对方 Y),由人来判断升哪一端。
23972
+ */
23973
+ message: `\u534F\u8BAE\u7248\u672C\u5BF9\u4E0D\u4E0A\uFF1A\u8FD9\u4E00\u7AEF\u662F ${own2}\uFF0C\u5BF9\u65B9\u662F ${peerProtocol}\u3002\u4E24\u7AEF\u5FC5\u987B\u662F\u540C\u4E00\u7248\u672C`
23974
+ };
23975
+ }
23976
+ return { ok: true, version: peerProtocol };
23977
+ }
23978
+
23979
+ // node_modules/.pnpm/@dsh-remote+protocol@3.1.4/node_modules/@dsh-remote/protocol/dist/src/wire/errors.js
23980
+ var WAIT_HINTABLE = /* @__PURE__ */ new Set(["rate_limited", "pair_table_full"]);
23981
+ function wantsRetryAfter(code) {
23982
+ return typeof code === "string" && WAIT_HINTABLE.has(code);
23983
+ }
23984
+
23985
+ // node_modules/.pnpm/@dsh-remote+protocol@3.1.4/node_modules/@dsh-remote/protocol/dist/src/platform/sleep.js
23896
23986
  var SLEEP_BACKENDS = ["caffeinate", "systemd-inhibit", "unsupported"];
23897
23987
 
23898
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.7/node_modules/@dsh-remote/protocol/dist/src/wire/payloads.js
23988
+ // node_modules/.pnpm/@dsh-remote+protocol@3.1.4/node_modules/@dsh-remote/protocol/dist/src/wire/payloads.js
23899
23989
  var SESSION_STATES = [
23900
23990
  "idle",
23901
23991
  "running",
@@ -23965,9 +24055,18 @@ var evPermissionRequest = external_exports.object({
23965
24055
  var evPermissionResolved = external_exports.object({
23966
24056
  t: external_exports.literal("ev.permission_resolved"),
23967
24057
  requestId: nonEmpty2,
23968
- sessionId: nonEmpty2.optional(),
23969
- /** 谁收的场。手机只按"要不要收掉这张卡"读它,所以不做必填。 */
23970
- by: external_exports.enum(["desktop", "cancelled"]).optional()
24058
+ /**
24059
+ * ⚠️ **必填**(2026-10-11 晚):句柄在生产端(`runtime.ts` 的 `voidStaleCard`)
24060
+ * 从来都是给全的,而"可选"在这里只有一个后果 —— 一次串台的收场会把别的会话的卡收掉。
24061
+ */
24062
+ sessionId: nonEmpty2,
24063
+ /**
24064
+ * 谁收的场。
24065
+ *
24066
+ * ⚠️ **必填**(2026-10-11 晚):原来写的是"手机只按'要不要收掉这张卡'读它,所以不做必填",
24067
+ * 而生产端一直都给 —— 形状只有一种,那就不留"缺了也算合法"的第二条路。
24068
+ */
24069
+ by: external_exports.enum(["desktop", "cancelled"])
23971
24070
  });
23972
24071
  var questionItem = external_exports.object({
23973
24072
  id: nonEmpty2,
@@ -23984,19 +24083,26 @@ var evQuestionRequest = external_exports.object({
23984
24083
  /**
23985
24084
  * 这张提问卡什么时候作废(ISO 字符串)。
23986
24085
  *
23987
- * 为什么审批有而提问没有过:主机侧的提问等待本来就带超时(`questionTimeoutMs`,默认 300s),
23988
- * 但手机上那张卡**没有任何倒计时**——用户看不见自己按的按钮什么时候失效,
23989
- * 而这违反伞仓 `docs/PRODUCT.md` §3 第 3 条「超时必须可见」。
23990
- * 与审批那条同一个形状:可选、ISO 字符串、由主机每次发问时现算。
24086
+ * ⚠️ **2026-10-11 起:这个字段由宿主决定,我们不自己算。**
24087
+ *
24088
+ * 主机侧追问题时**带不带期限,由那一代内核说了算**(实测它给的是"不带")。
24089
+ * 带来就填、不带就**省掉这个键** —— 手机据此决定要不要起倒计时,
24090
+ * 而它只在主机真的会到点结算时才有意义。
24091
+ *
24092
+ * 曾经这里写的是"主机侧的提问等待本来就带超时(默认 300s)",而那个上限是
24093
+ * **插件自己加的**(`approvalTimeoutSec` 那个旋钮同批删除)。
24094
+ * 它造成过真实故障:页面退后台 → 事件退订 → 卡帧到了 client 没到界面 →
24095
+ * 用户回到页面时主机早已判超时,于是"手机上点不动"。
23991
24096
  */
23992
24097
  expiresAt: external_exports.string().optional()
23993
24098
  });
23994
24099
  var evQuestionResolved = external_exports.object({
23995
24100
  t: external_exports.literal("ev.question_resolved"),
23996
24101
  requestId: nonEmpty2,
23997
- sessionId: nonEmpty2.optional(),
23998
- /** 谁收的场。手机只按"要不要收掉这张卡"读它,所以不做必填。 */
23999
- by: external_exports.enum(["desktop", "cancelled"]).optional()
24102
+ /** 必填,理由同 `evPermissionResolved.sessionId`(收场必须精确到会话)。 */
24103
+ sessionId: nonEmpty2,
24104
+ /** 必填,理由同 `evPermissionResolved.by`。 */
24105
+ by: external_exports.enum(["desktop", "cancelled"])
24000
24106
  });
24001
24107
  var evRunState = external_exports.object({
24002
24108
  t: external_exports.literal("ev.run_state"),
@@ -24054,8 +24160,8 @@ var evHostInfo = external_exports.object({
24054
24160
  t: external_exports.literal("ev.host_info"),
24055
24161
  /**
24056
24162
  * 主机自述。这里**没有** `sessionId`,将来也不许加(与 `ev.session_changed` /
24057
- * `ev.keep_awake_state` 同一条 F11):能力面是主机级的,按会话分装会把
24058
- * "这台主机支持什么"变成"这条通道支持什么",而手机只想知道前者。
24163
+ * `ev.keep_awake_state` 同一条 F11):限额是主机级的,按会话分装会把
24164
+ * "这台主机能收多少"变成"这条通道能收多少",而手机只想知道前者。
24059
24165
  */
24060
24166
  host: external_exports.object({
24061
24167
  platform: nonEmpty2,
@@ -24066,8 +24172,6 @@ var evHostInfo = external_exports.object({
24066
24172
  carrier: external_exports.string().optional(),
24067
24173
  dshVersion: external_exports.string().optional()
24068
24174
  }),
24069
- /** §17.4 的能力 id 列表。生产端取自 `CAPABILITY_IDS`(L1)。 */
24070
- caps: capabilityList,
24071
24175
  limits: hostLimits,
24072
24176
  state: external_exports.object({
24073
24177
  /** 防休眠走哪条系统后端;`unsupported` 是如实上报,不是错误。 */
@@ -24091,7 +24195,8 @@ var evModel = external_exports.object({
24091
24195
  * 模型是**按会话**的,不带 sessionId 就无法过滤——任何一条别的会话的
24092
24196
  * 模型帧都会覆盖本会话的显示。
24093
24197
  *
24094
- * 必填:老主机不带这个字段时 mp 端宁可忽略这一帧,也不显示错的。
24198
+ * **必填**(2026-10-11 晚):不带会话归属的模型帧在 mp 端一律丢弃 ——
24199
+ * 宁可这一帧不算数,也不能把别的会话的模型名显示到本会话顶栏。
24095
24200
  */
24096
24201
  sessionId: nonEmpty2,
24097
24202
  provider: nonEmpty2.optional(),
@@ -24126,8 +24231,9 @@ var evSessionHistory = external_exports.object({
24126
24231
  * 而空页没有任何 seq 可取——于是手机要么记一个陈旧的值(下次重复收到已看过的条目),
24127
24232
  * 要么记 0(下次把整段历史重拉一遍)。这个字段让"你现在到哪了"始终有答案。
24128
24233
  *
24129
- * ⚠️ 它**不是**能力位那样的"可选支持":老主机不发这个字段时,手机看到 `undefined`
24130
- * 就退回 v2 的重拉一页(能力位 `drc.payload.resume` 决定走哪条路)。
24234
+ * ⚠️ 可选的含义是"**这一页报不出游标**"(内核这一代给不出来),不是"对端可能不支持":
24235
+ * 手机拿到 `undefined` 就按"已经到顶"收工(`client.js` 的 `next === null` 那一支),
24236
+ * 不记一个错的游标。(原来这里还提能力位 `drc.payload.resume` —— 那套机制已删除。)
24131
24237
  */
24132
24238
  latestSeq: external_exports.number().int().nonnegative().optional()
24133
24239
  });
@@ -24174,8 +24280,7 @@ var cmdSendPrompt = external_exports.object({
24174
24280
  sessionId: nonEmpty2,
24175
24281
  text: external_exports.string(),
24176
24282
  /**
24177
- * 随消息的图片附件。**可选**:老版本手机不带这个字段,老主机看到也不认
24178
- * (zod 默认 strip 未知键,而 text 照旧)——所以这是向后兼容的增补,不升协议版本。
24283
+ * 随消息的图片附件。**可选**:不带 = 这条消息没有图片(一个真实状态)。
24179
24284
  * 上限取 `limits.ts` 的 `MAX_IMAGE_ATTACHMENTS`(4):再多对"看清楚这张图"没有帮助,
24180
24285
  * 只是把帧撑爆。**同一个数**还在小程序(`MAX_ATTACH`)与主机侧(`uploads.ts`)各写一遍,
24181
24286
  * 靠 `e2e/wire-surface.test.mjs` 逐处比对钉住——改动必须三处同时发生。
@@ -24306,145 +24411,7 @@ var PAYLOAD_TYPES = {
24306
24411
  evSessionHistory: "ev.session_history"
24307
24412
  };
24308
24413
 
24309
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.7/node_modules/@dsh-remote/protocol/dist/src/wire/registry.js
24310
- var ENDPOINT_FRAME_NAMES = [
24311
- "hello",
24312
- "pair-begin",
24313
- "pair-begin-client",
24314
- "resync",
24315
- "enc",
24316
- "enc-batch",
24317
- "session-leave",
24318
- "ping"
24319
- ];
24320
- var RELAY_FRAME_NAMES = [
24321
- "hello-ok",
24322
- "pair-ready",
24323
- "paired",
24324
- "pair-fail",
24325
- "peer-joined",
24326
- "peer-left",
24327
- "error",
24328
- "pong",
24329
- "enc",
24330
- "enc-batch"
24331
- ];
24332
- var FRAME_TYPES = [
24333
- .../* @__PURE__ */ new Set([...ENDPOINT_FRAME_NAMES, ...RELAY_FRAME_NAMES])
24334
- ];
24335
- var FRAME_TYPE_SET = new Set(FRAME_TYPES);
24336
- function isKnownFrameType(name) {
24337
- return typeof name === "string" && FRAME_TYPE_SET.has(name);
24338
- }
24339
- var CMD_TYPES = [
24340
- PAYLOAD_TYPES.cmdSendPrompt,
24341
- PAYLOAD_TYPES.cmdAnswer,
24342
- PAYLOAD_TYPES.cmdResolvePermission,
24343
- PAYLOAD_TYPES.cmdInterrupt,
24344
- PAYLOAD_TYPES.cmdListSessions,
24345
- PAYLOAD_TYPES.cmdKeepAwake,
24346
- PAYLOAD_TYPES.cmdSessionHistory,
24347
- PAYLOAD_TYPES.cmdNewSession,
24348
- PAYLOAD_TYPES.cmdGetPending,
24349
- PAYLOAD_TYPES.cmdArchiveSession
24350
- ];
24351
- var EV_TYPES = [
24352
- PAYLOAD_TYPES.evSessionChanged,
24353
- PAYLOAD_TYPES.evMessageDelta,
24354
- PAYLOAD_TYPES.evToolEvent,
24355
- PAYLOAD_TYPES.evPermissionRequest,
24356
- PAYLOAD_TYPES.evPermissionResolved,
24357
- PAYLOAD_TYPES.evQuestionRequest,
24358
- PAYLOAD_TYPES.evQuestionResolved,
24359
- PAYLOAD_TYPES.evRunState,
24360
- PAYLOAD_TYPES.evTodo,
24361
- PAYLOAD_TYPES.evRetry,
24362
- PAYLOAD_TYPES.evCompaction,
24363
- PAYLOAD_TYPES.evKeepAwakeState,
24364
- PAYLOAD_TYPES.evHostInfo,
24365
- PAYLOAD_TYPES.evModel,
24366
- PAYLOAD_TYPES.evResult,
24367
- PAYLOAD_TYPES.evSessionHistory
24368
- ];
24369
- var PAYLOAD_TYPE_SET = /* @__PURE__ */ new Set([...CMD_TYPES, ...EV_TYPES]);
24370
- var CAPABILITY_IDS = [
24371
- "drc.v1",
24372
- "drc.pairing.qr",
24373
- "drc.pairing.token",
24374
- "drc.payload.attachments",
24375
- "drc.payload.history",
24376
- /**
24377
- * 支持 `cmd.session_history.since` 向前补差量 + `ev.session_history.latestSeq`。
24378
- *
24379
- * **为什么是能力位而不是版本号**:协议 v1 的帧名与载荷名冻结(规范 §5.2 V1),
24380
- * 加这个字段属"加性变更";而手机要知道**对端是否真的会回 `latestSeq`**,
24381
- * 才能决定走补差量还是退回重拉一页。版本号表达不了"这一项有没有"。
24382
- *
24383
- * 老主机缺这一位时,手机**必须**退回重拉一页而不是发 `since`——
24384
- * 那是可验证的降级路径(反向判据见 `tests/resume-capability.test.ts`)。
24385
- */
24386
- "drc.payload.resume",
24387
- "drc.payload.new-session.workspace",
24388
- "drc.payload.get-pending",
24389
- "drc.payload.keep-awake",
24390
- "drc.payload.archive-session",
24391
- "drc.payload.model",
24392
- "drc.payload.retry-compaction",
24393
- "drc.cmd.idempotency",
24394
- "drc.data.enc-batch",
24395
- "drc.host.resync",
24396
- "drc.host.info",
24397
- "drc.crypto.v2"
24398
- ];
24399
- var CAPABILITY_SET = new Set(CAPABILITY_IDS);
24400
-
24401
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.7/node_modules/@dsh-remote/protocol/dist/src/wire/negotiate.js
24402
- function negotiateProtocol(peerProtocol, own2 = PROTOCOL_VERSION, min = MIN_SUPPORTED_PROTOCOL) {
24403
- if (peerProtocol === void 0 || peerProtocol === null) {
24404
- return verdictFor(1, own2, min, true);
24405
- }
24406
- if (typeof peerProtocol !== "number" || !Number.isInteger(peerProtocol) || peerProtocol <= 0) {
24407
- return {
24408
- ok: false,
24409
- reason: "invalid",
24410
- peer: peerProtocol,
24411
- message: `\u5BF9\u65B9\u62A5\u544A\u7684\u534F\u8BAE\u7248\u672C\u4E0D\u662F\u6B63\u6574\u6570\uFF08\u6536\u5230 ${JSON.stringify(peerProtocol) ?? "undefined"}\uFF09`
24412
- };
24413
- }
24414
- return verdictFor(peerProtocol, own2, min, false);
24415
- }
24416
- function verdictFor(peer, own2, min, missing) {
24417
- if (peer < min) {
24418
- return {
24419
- ok: false,
24420
- reason: "too_old",
24421
- peer,
24422
- message: `\u5BF9\u65B9\u534F\u8BAE\u7248\u672C ${peer} \u592A\u65E7\uFF0C\u672C\u7AEF\u6700\u4F4E\u652F\u6301 ${min}\uFF1B\u8BF7\u628A\u5BF9\u7AEF\u5347\u7EA7\u540E\u518D\u8BD5`
24423
- };
24424
- }
24425
- if (peer > own2) {
24426
- return {
24427
- ok: false,
24428
- reason: "too_new",
24429
- peer,
24430
- // ⚠️ 这句话**会被原样弹到用户手机上**(中继 `acceptProtocol` 显式带 message,
24431
- // 而客户端的处理是 `f.message || f.code`)。所以人称必须从**读它的人**出发:
24432
- // 收这条错的一端是"对方",而被要求升级的是它自己 —— 写"请把本端升级"时,
24433
- // 手机用户读到的是"请升级我的手机",而真正要升级的是中继/主机那一侧。
24434
- // 同理"对方"在中继的日志里指的是手机。两个方向都试过,读起来都指错了人。
24435
- message: `\u672C\u7AEF\u534F\u8BAE\u7248\u672C ${own2} \u6BD4\u5BF9\u65B9\u65E7\uFF08\u5BF9\u65B9\u4E3A ${peer}\uFF09\uFF1B\u8BF7\u628A\u672C\u7AEF\u5347\u7EA7\u5230 ${peer} \u6216\u66F4\u9AD8\u540E\u518D\u8BD5`
24436
- };
24437
- }
24438
- return { ok: true, version: peer, missing };
24439
- }
24440
-
24441
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.7/node_modules/@dsh-remote/protocol/dist/src/wire/errors.js
24442
- var WAIT_HINTABLE = /* @__PURE__ */ new Set(["rate_limited", "pair_table_full"]);
24443
- function wantsRetryAfter(code) {
24444
- return typeof code === "string" && WAIT_HINTABLE.has(code);
24445
- }
24446
-
24447
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.7/node_modules/@dsh-remote/protocol/dist/src/wire/outbound.js
24414
+ // node_modules/.pnpm/@dsh-remote+protocol@3.1.4/node_modules/@dsh-remote/protocol/dist/src/wire/outbound.js
24448
24415
  function helloOkForClient(clientId, protocol) {
24449
24416
  return { t: "hello-ok", role: "client", clientId, ...protocol === void 0 ? {} : { protocol } };
24450
24417
  }
@@ -24522,7 +24489,69 @@ function encBatchToRelay(sessionId, items) {
24522
24489
  return { t: "enc-batch", sessionId, items: items.map((item) => ({ ...item })) };
24523
24490
  }
24524
24491
 
24525
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.7/node_modules/@dsh-remote/protocol/dist/src/wire/classify.js
24492
+ // node_modules/.pnpm/@dsh-remote+protocol@3.1.4/node_modules/@dsh-remote/protocol/dist/src/wire/registry.js
24493
+ var ENDPOINT_FRAME_NAMES = [
24494
+ "hello",
24495
+ "pair-begin",
24496
+ "pair-begin-client",
24497
+ "resync",
24498
+ "enc",
24499
+ "enc-batch",
24500
+ "session-leave",
24501
+ "ping"
24502
+ ];
24503
+ var RELAY_FRAME_NAMES = [
24504
+ "hello-ok",
24505
+ "pair-ready",
24506
+ "paired",
24507
+ "pair-fail",
24508
+ "peer-joined",
24509
+ "peer-left",
24510
+ "error",
24511
+ "pong",
24512
+ "enc",
24513
+ "enc-batch"
24514
+ ];
24515
+ var FRAME_TYPES = [
24516
+ .../* @__PURE__ */ new Set([...ENDPOINT_FRAME_NAMES, ...RELAY_FRAME_NAMES])
24517
+ ];
24518
+ var FRAME_TYPE_SET = new Set(FRAME_TYPES);
24519
+ function isKnownFrameType(name) {
24520
+ return typeof name === "string" && FRAME_TYPE_SET.has(name);
24521
+ }
24522
+ var CMD_TYPES = [
24523
+ PAYLOAD_TYPES.cmdSendPrompt,
24524
+ PAYLOAD_TYPES.cmdAnswer,
24525
+ PAYLOAD_TYPES.cmdResolvePermission,
24526
+ PAYLOAD_TYPES.cmdInterrupt,
24527
+ PAYLOAD_TYPES.cmdListSessions,
24528
+ PAYLOAD_TYPES.cmdKeepAwake,
24529
+ PAYLOAD_TYPES.cmdSessionHistory,
24530
+ PAYLOAD_TYPES.cmdNewSession,
24531
+ PAYLOAD_TYPES.cmdGetPending,
24532
+ PAYLOAD_TYPES.cmdArchiveSession
24533
+ ];
24534
+ var EV_TYPES = [
24535
+ PAYLOAD_TYPES.evSessionChanged,
24536
+ PAYLOAD_TYPES.evMessageDelta,
24537
+ PAYLOAD_TYPES.evToolEvent,
24538
+ PAYLOAD_TYPES.evPermissionRequest,
24539
+ PAYLOAD_TYPES.evPermissionResolved,
24540
+ PAYLOAD_TYPES.evQuestionRequest,
24541
+ PAYLOAD_TYPES.evQuestionResolved,
24542
+ PAYLOAD_TYPES.evRunState,
24543
+ PAYLOAD_TYPES.evTodo,
24544
+ PAYLOAD_TYPES.evRetry,
24545
+ PAYLOAD_TYPES.evCompaction,
24546
+ PAYLOAD_TYPES.evKeepAwakeState,
24547
+ PAYLOAD_TYPES.evHostInfo,
24548
+ PAYLOAD_TYPES.evModel,
24549
+ PAYLOAD_TYPES.evResult,
24550
+ PAYLOAD_TYPES.evSessionHistory
24551
+ ];
24552
+ var PAYLOAD_TYPE_SET = /* @__PURE__ */ new Set([...CMD_TYPES, ...EV_TYPES]);
24553
+
24554
+ // node_modules/.pnpm/@dsh-remote+protocol@3.1.4/node_modules/@dsh-remote/protocol/dist/src/wire/classify.js
24526
24555
  function frameTypeOf(value) {
24527
24556
  if (typeof value !== "object" || value === null || Array.isArray(value))
24528
24557
  return void 0;
@@ -24615,12 +24644,11 @@ import {
24615
24644
  writeFileSync
24616
24645
  } from "node:fs";
24617
24646
  import { dirname } from "node:path";
24618
- var STATE_FILE_VERSION = 1;
24647
+ var STATE_FILE_VERSION = 2;
24619
24648
  var CONVERSATION_ID_RE = new RegExp(`^${CONVERSATION_ID_PREFIX}[0-9a-f]{12}$`);
24620
24649
  var HOST_TOKEN_RE = /^[0-9a-f]{48}$/;
24621
24650
  var MAX_HOST_ID_LEN = 200;
24622
24651
  function sanitizeHostTokens(raw) {
24623
- if (!Array.isArray(raw)) return { hostTokens: [], dropped: 0 };
24624
24652
  const hostTokens = [];
24625
24653
  const seen = /* @__PURE__ */ new Set();
24626
24654
  for (const item of raw) {
@@ -24628,7 +24656,10 @@ function sanitizeHostTokens(raw) {
24628
24656
  continue;
24629
24657
  }
24630
24658
  const entry = item;
24631
- if (typeof entry.hostId !== "string" || entry.hostId.length > MAX_HOST_ID_LEN || entry.hostId !== "" && entry.hostId.trim() !== entry.hostId || !isNonEmptyString(entry.token) || !HOST_TOKEN_RE.test(entry.token) || !isTimestamp(entry.issuedAt) || !isTimestamp(entry.lastUsedAt) || seen.has(entry.hostId)) {
24659
+ if (typeof entry.hostId !== "string" || // ⚠️ **空 hostId 一律丢**(2026-10-11 晚):那曾是"未绑定槽位"的键,而它的令牌
24660
+ // 对任意主机都算数。旧状态文件里可能真有一条这样的记录 —— 读进来就等于把
24661
+ // 那把万能钥匙又装回内存,所以在这一层直接筛掉(不是靠 `hostTokenAccepted` 单独兜)。
24662
+ entry.hostId === "" || entry.hostId.length > MAX_HOST_ID_LEN || entry.hostId.trim() !== entry.hostId || !isNonEmptyString(entry.token) || !HOST_TOKEN_RE.test(entry.token) || !isTimestamp(entry.issuedAt) || !isTimestamp(entry.lastUsedAt) || seen.has(entry.hostId)) {
24632
24663
  continue;
24633
24664
  }
24634
24665
  seen.add(entry.hostId);
@@ -24697,12 +24728,12 @@ function readStateFile(path, log) {
24697
24728
  return void 0;
24698
24729
  }
24699
24730
  const file2 = parsed;
24700
- if (file2.version !== STATE_FILE_VERSION) {
24701
- log.warn("state file version mismatch, starting empty", { path, found: String(file2.version) });
24702
- return void 0;
24703
- }
24704
24731
  const { conversations, dropped } = sanitize(file2.conversations);
24705
24732
  if (dropped > 0) log.warn("state file entries dropped", { path, dropped, kept: conversations.length });
24733
+ if (!Array.isArray(file2.hostTokens)) {
24734
+ log.warn("state file has no hostTokens array, starting empty", { path });
24735
+ return void 0;
24736
+ }
24706
24737
  const hostTokenResult = sanitizeHostTokens(file2.hostTokens);
24707
24738
  if (hostTokenResult.dropped > 0) {
24708
24739
  log.warn("state file host tokens dropped", {
@@ -24717,13 +24748,12 @@ function readStateFile(path, log) {
24717
24748
  dropped,
24718
24749
  hostTokens: hostTokenResult.hostTokens.length
24719
24750
  });
24720
- const restored = {
24751
+ return {
24721
24752
  version: STATE_FILE_VERSION,
24722
24753
  savedAt: isTimestamp(file2.savedAt) ? file2.savedAt : 0,
24723
- conversations
24754
+ conversations,
24755
+ hostTokens: hostTokenResult.hostTokens
24724
24756
  };
24725
- if (hostTokenResult.hostTokens.length > 0) restored.hostTokens = hostTokenResult.hostTokens;
24726
- return restored;
24727
24757
  }
24728
24758
  function snapshotState(state, now) {
24729
24759
  const conversations = [];
@@ -24742,9 +24772,7 @@ function snapshotState(state, now) {
24742
24772
  for (const [hostId, entry] of state.hostTokens) {
24743
24773
  hostTokens.push({ hostId, token: entry.token, issuedAt: entry.issuedAt, lastUsedAt: entry.lastUsedAt });
24744
24774
  }
24745
- const snapshot = { version: STATE_FILE_VERSION, savedAt: now, conversations };
24746
- if (hostTokens.length > 0) snapshot.hostTokens = hostTokens;
24747
- return snapshot;
24775
+ return { version: STATE_FILE_VERSION, savedAt: now, conversations, hostTokens };
24748
24776
  }
24749
24777
  function restoreState(path, state, log, now) {
24750
24778
  const file2 = readStateFile(path, log);
@@ -24766,7 +24794,7 @@ function restoreState(path, state, log, now) {
24766
24794
  if (clamped > 0) {
24767
24795
  log.warn("state file timestamps in the future, clamped to now", { path, clamped });
24768
24796
  }
24769
- for (const entry of file2.hostTokens ?? []) {
24797
+ for (const entry of file2.hostTokens) {
24770
24798
  state.hostTokens.set(entry.hostId, {
24771
24799
  token: entry.token,
24772
24800
  issuedAt: entry.issuedAt,
@@ -24826,20 +24854,25 @@ function writeStateFile(path, snapshot, log) {
24826
24854
 
24827
24855
  // src/state.ts
24828
24856
  var WS_OPEN = 1;
24829
- var UNBOUND_HOST_TOKEN_KEY = "";
24830
24857
  var RelayState = class {
24831
24858
  hosts = /* @__PURE__ */ new Map();
24832
24859
  clients = /* @__PURE__ */ new Map();
24833
24860
  pendingPairs = /* @__PURE__ */ new Map();
24834
24861
  conversations = /* @__PURE__ */ new Map();
24835
- /** 按 hostId 一机一枚(外加 `UNBOUND_HOST_TOKEN_KEY` 那个未绑定槽位)。 */
24862
+ /** 按 hostId 一机一枚(键一定是非空 hostId,见 `HostTokenEntry`)。 */
24836
24863
  hostTokens = /* @__PURE__ */ new Map();
24837
24864
  /** 测试可注入固定时钟;默认用真实时间。 */
24838
24865
  clock;
24839
24866
  maxPendingPairsValue;
24867
+ /**
24868
+ * 会话表上界(D6)。见 `RelayConfig.maxConversations` 的注释:这是"每次认领同步整表写"
24869
+ * 那条成本链的**唯一**封口。
24870
+ */
24871
+ maxConversationsValue;
24840
24872
  constructor(options = {}) {
24841
24873
  this.clock = options.now ?? (() => Date.now());
24842
24874
  this.maxPendingPairsValue = options.maxPendingPairs ?? 1e3;
24875
+ this.maxConversationsValue = options.maxConversations ?? 5e3;
24843
24876
  }
24844
24877
  now() {
24845
24878
  return this.clock();
@@ -24904,6 +24937,27 @@ var RelayState = class {
24904
24937
  });
24905
24938
  return { ok: true, replaced: existing !== void 0, full: false };
24906
24939
  }
24940
+ /**
24941
+ * 回收**已空**的会话,从最旧的一条开始,直到表回到上界以下(D6)。
24942
+ *
24943
+ * "最旧"按 `emptySince ?? lastActivityAt` 算——即"它被放弃得有多久",
24944
+ * 而不是"会话建立得有多早":一条建立了很久但直到刚才才被放弃的会话,
24945
+ * 比一条刚建立就被放弃的更该回收。
24946
+ *
24947
+ * 返回回收了几条(调用方拿它写日志/计数——回收这件事必须留痕,
24948
+ * 否则"表为什么突然小了一截"只能靠猜)。
24949
+ */
24950
+ evictAbandonedConversations() {
24951
+ if (this.conversations.size < this.maxConversationsValue) return 0;
24952
+ const abandoned = [...this.conversations.entries()].filter(([, conv]) => conv.clients.size === 0).sort((a, b) => (a[1].emptySince ?? a[1].lastActivityAt) - (b[1].emptySince ?? b[1].lastActivityAt));
24953
+ let dropped = 0;
24954
+ for (const [conversationId] of abandoned) {
24955
+ if (this.conversations.size < this.maxConversationsValue) break;
24956
+ this.conversations.delete(conversationId);
24957
+ dropped += 1;
24958
+ }
24959
+ return dropped;
24960
+ }
24907
24961
  /** 还能被认领的码数(`/api/pair-status` 与主机侧「还有没有码可发」问的都是它)。 */
24908
24962
  countClaimablePairs() {
24909
24963
  let n = 0;
@@ -24918,6 +24972,12 @@ var RelayState = class {
24918
24972
  if (pending.used) return { ok: false, reason: "already_used" };
24919
24973
  const host = this.hosts.get(pending.hostId);
24920
24974
  if (!host || host.ws.readyState !== WS_OPEN) return { ok: false, reason: "host_offline" };
24975
+ if (this.conversations.size >= this.maxConversationsValue) {
24976
+ this.evictAbandonedConversations();
24977
+ if (this.conversations.size >= this.maxConversationsValue) {
24978
+ return { ok: false, reason: "table_full" };
24979
+ }
24980
+ }
24921
24981
  pending.used = true;
24922
24982
  const detached = this.leaveAll(clientId);
24923
24983
  const conversationId = newConversationId();
@@ -25271,11 +25331,16 @@ function tokenMatches(expected, got) {
25271
25331
  return timingSafeEqual(a, b);
25272
25332
  }
25273
25333
  var CLAIM_TRACK_MAX = 1e4;
25334
+ var PERSIST_COALESCE_MS = 200;
25274
25335
  var MAX_HOST_ID_LEN2 = 200;
25275
25336
  var HOST_ID_RE = /^[A-Za-z0-9._:-]+$/;
25276
25337
  function createRelay(config2) {
25277
25338
  const log = new Log(config2.logLevel);
25278
- const state = new RelayState({ maxPendingPairs: config2.maxPendingPairs });
25339
+ const state = new RelayState({
25340
+ maxPendingPairs: config2.maxPendingPairs,
25341
+ // D6:会话表上界(缺了它,每次认领的同步整表写就成了无界的成本放大)。
25342
+ maxConversations: config2.maxConversations
25343
+ });
25279
25344
  const startedAt = Date.now();
25280
25345
  const pairBudget = new Budget(config2.pairGlobalBudgetPerSec);
25281
25346
  const pairStatusBudget = new Budget(config2.pairStatusBudgetPerSec);
@@ -25284,6 +25349,8 @@ function createRelay(config2) {
25284
25349
  const lastClaimAt = /* @__PURE__ */ new Map();
25285
25350
  let sweepTimer;
25286
25351
  let pingTimer;
25352
+ let persistPending = false;
25353
+ let persistTimer;
25287
25354
  let lastPingAt = 0;
25288
25355
  let lastCountersAt = 0;
25289
25356
  function newHostToken() {
@@ -25301,15 +25368,9 @@ function createRelay(config2) {
25301
25368
  }
25302
25369
  function hostTokenAccepted(hostId, got) {
25303
25370
  if (!got) return false;
25304
- const now = Date.now();
25305
25371
  const perHost = state.hostTokens.get(hostId);
25306
25372
  if (perHost && tokenMatches(perHost.token, got)) {
25307
- state.markHostTokenUsed(hostId, now);
25308
- return true;
25309
- }
25310
- const unbound = state.hostTokens.get(UNBOUND_HOST_TOKEN_KEY);
25311
- if (unbound && tokenMatches(unbound.token, got)) {
25312
- state.markHostTokenUsed(UNBOUND_HOST_TOKEN_KEY, now);
25373
+ state.markHostTokenUsed(hostId, Date.now());
25313
25374
  return true;
25314
25375
  }
25315
25376
  return config2.hostToken !== "" && tokenMatches(config2.hostToken, got);
@@ -25325,6 +25386,11 @@ function createRelay(config2) {
25325
25386
  }
25326
25387
  }
25327
25388
  function persistNow() {
25389
+ persistPending = false;
25390
+ if (persistTimer !== void 0) {
25391
+ clearTimeout(persistTimer);
25392
+ persistTimer = void 0;
25393
+ }
25328
25394
  if (!persistence.enabled) return false;
25329
25395
  const ok = writeStateFile(config2.stateFile, snapshotState(state, Date.now()), log);
25330
25396
  if (ok) {
@@ -25336,6 +25402,17 @@ function createRelay(config2) {
25336
25402
  return ok;
25337
25403
  }
25338
25404
  function markStateChanged() {
25405
+ if (!persistence.enabled) return;
25406
+ persistPending = true;
25407
+ if (persistTimer !== void 0) return;
25408
+ persistTimer = setTimeout(() => {
25409
+ persistTimer = void 0;
25410
+ flushPendingPersist();
25411
+ }, PERSIST_COALESCE_MS);
25412
+ persistTimer.unref?.();
25413
+ }
25414
+ function flushPendingPersist() {
25415
+ if (!persistPending) return;
25339
25416
  persistNow();
25340
25417
  }
25341
25418
  const MAX_PING_BUCKETS = 1024;
@@ -25423,21 +25500,6 @@ function createRelay(config2) {
25423
25500
  peer.hostId = void 0;
25424
25501
  }
25425
25502
  function handleHello(peer, frame) {
25426
- if (peer.protocol === void 0 && !peer.countedProtocol) {
25427
- peer.countedProtocol = true;
25428
- if (frame.protocol === void 0) peersWithoutProtocol++;
25429
- else peerProtocols.set(frame.protocol, (peerProtocols.get(frame.protocol) ?? 0) + 1);
25430
- } else if (peer.protocol !== frame.protocol) {
25431
- if (peer.protocol === void 0) peersWithoutProtocol = Math.max(0, peersWithoutProtocol - 1);
25432
- else {
25433
- const left = (peerProtocols.get(peer.protocol) ?? 1) - 1;
25434
- if (left > 0) peerProtocols.set(peer.protocol, left);
25435
- else peerProtocols.delete(peer.protocol);
25436
- }
25437
- if (frame.protocol === void 0) peersWithoutProtocol++;
25438
- else peerProtocols.set(frame.protocol, (peerProtocols.get(frame.protocol) ?? 0) + 1);
25439
- }
25440
- peer.protocol = frame.protocol;
25441
25503
  if (frame.role === "host") {
25442
25504
  const hostId = frame.hostId?.trim() || newHostId();
25443
25505
  if (!config2.hostToken && state.hostTokens.size === 0) {
@@ -25520,9 +25582,24 @@ function createRelay(config2) {
25520
25582
  }
25521
25583
  const claimed = state.claim(frame.pairingToken, peer.clientId);
25522
25584
  if (!claimed.ok) {
25585
+ const wireReason = claimed.reason === "table_full" ? "invalid_or_expired" : claimed.reason;
25586
+ if (claimed.reason === "table_full") {
25587
+ counters.rejectedPairs += 1;
25588
+ const now = Date.now();
25589
+ if (now - peer.tableFullWarnedAt >= 1e3) {
25590
+ peer.tableFullWarnedAt = now;
25591
+ log.warn("conversation table full, refusing new pairings (D6)", {
25592
+ clientId: peer.clientId,
25593
+ conversations: state.conversations.size,
25594
+ max: config2.maxConversations
25595
+ });
25596
+ }
25597
+ sendToPeer(peer, pairFail(wireReason, frame.pairingToken));
25598
+ return;
25599
+ }
25523
25600
  peer.pairAttempts += 1;
25524
25601
  counters.rejectedPairs += 1;
25525
- sendToPeer(peer, pairFail(claimed.reason, frame.pairingToken));
25602
+ sendToPeer(peer, pairFail(wireReason, frame.pairingToken));
25526
25603
  log.info("pair failed", {
25527
25604
  clientId: peer.clientId,
25528
25605
  reason: claimed.reason,
@@ -25607,8 +25684,6 @@ function createRelay(config2) {
25607
25684
  for (const sock of clients) sendTo(sock, encBatchToClient(frame.sessionId, items));
25608
25685
  }
25609
25686
  let lastResyncPersistAt = 0;
25610
- const peerProtocols = /* @__PURE__ */ new Map();
25611
- let peersWithoutProtocol = 0;
25612
25687
  function acceptProtocol(peer, peerProtocol) {
25613
25688
  const verdict = negotiateProtocol(peerProtocol);
25614
25689
  if (verdict.ok) return true;
@@ -25722,10 +25797,9 @@ function createRelay(config2) {
25722
25797
  notedNonMember: false,
25723
25798
  notedPairTableFull: false,
25724
25799
  pairBudgetWarnedAt: 0,
25800
+ tableFullWarnedAt: 0,
25725
25801
  backpressure: new BackpressureGate(),
25726
25802
  alive: true,
25727
- // 观测面计数的闩锁(见 Peer.countedProtocol):连接为单位,不是 hello 为单位。
25728
- countedProtocol: false,
25729
25803
  // 轮转序号取模分配:**均匀是刻意的**。哪怕重启风暴里 10k 条连接在几十秒内
25730
25804
  // 全建起来,它们也会摊到所有桶上;若改成"分给下一个将要轮到的桶",
25731
25805
  // 风暴会把它们全塞进同一两个桶,等于把刚拆掉的突发又造回来(C7 的场景)。
@@ -25800,12 +25874,6 @@ function createRelay(config2) {
25800
25874
  });
25801
25875
  ws.on("close", () => {
25802
25876
  peers.delete(ws);
25803
- if (peer.protocol === void 0) peersWithoutProtocol = Math.max(0, peersWithoutProtocol - 1);
25804
- else {
25805
- const left = (peerProtocols.get(peer.protocol) ?? 1) - 1;
25806
- if (left > 0) peerProtocols.set(peer.protocol, left);
25807
- else peerProtocols.delete(peer.protocol);
25808
- }
25809
25877
  bucketAt(peer.pingBucket).delete(peer);
25810
25878
  if (peer.role === "client" && peer.clientId) {
25811
25879
  for (const notice of state.clientGone(peer.clientId, ws)) {
@@ -25899,18 +25967,12 @@ function createRelay(config2) {
25899
25967
  stateWriteFailures: persistence.failures,
25900
25968
  lastPingAgo: lastPingAt === 0 ? -1 : Math.round((Date.now() - lastPingAt) / 1e3),
25901
25969
  shuttingDown,
25902
- // 协议版本协商的观测面(2026-10-07)。有了版本闸之后,"为什么这个对端连不上"
25903
- // 就成了新的排障问题,而没有这一项时它只能去翻日志。
25904
- //
25905
- // 报的是**对端报的最小/最大版本**与"没报过版本"的连接数,两个方向缺一不可:
25906
- // 只报最大值的话,一个全是老小程序的环境看起来与新版本一样正常。
25907
- protocolSelf: PROTOCOL_VERSION,
25908
- protocolMin: MIN_SUPPORTED_PROTOCOL,
25909
- peerProtocolMin: peerProtocols.size === 0 ? -1 : Math.min(...peerProtocols.keys()),
25910
- peerProtocolMax: peerProtocols.size === 0 ? -1 : Math.max(...peerProtocols.keys()),
25911
- peersNoProtocol: peersWithoutProtocol
25970
+ // 本端说的协议版本(单版本制:它是一个常量,但排障时要能一眼看到"这一个中继
25971
+ // 说的是哪一版"——版本对不上的现场表现是"连上了、什么都不发生",没有任何一层会报错)。
25972
+ protocolSelf: PROTOCOL_VERSION
25912
25973
  };
25913
25974
  }
25975
+ const BODY_TOO_LARGE = "body_too_large";
25914
25976
  function readBody(req, limit) {
25915
25977
  return new Promise((resolve, reject) => {
25916
25978
  const chunks = [];
@@ -25918,8 +25980,8 @@ function createRelay(config2) {
25918
25980
  req.on("data", (chunk) => {
25919
25981
  size += chunk.length;
25920
25982
  if (size > limit) {
25921
- reject(new Error("body_too_large"));
25922
- req.destroy();
25983
+ req.pause();
25984
+ reject(Object.assign(new Error(BODY_TOO_LARGE), { code: BODY_TOO_LARGE }));
25923
25985
  return;
25924
25986
  }
25925
25987
  chunks.push(chunk);
@@ -25950,7 +26012,18 @@ function createRelay(config2) {
25950
26012
  }
25951
26013
  rememberClaim(ip, now);
25952
26014
  let body = {};
25953
- const raw = await readBody(req, CLAIM_BODY_MAX_BYTES);
26015
+ let raw;
26016
+ try {
26017
+ raw = await readBody(req, CLAIM_BODY_MAX_BYTES);
26018
+ } catch (error62) {
26019
+ if (error62?.code === BODY_TOO_LARGE) {
26020
+ res.statusCode = 413;
26021
+ res.setHeader("connection", "close");
26022
+ res.end(JSON.stringify({ error: "body_too_large", limitBytes: CLAIM_BODY_MAX_BYTES }));
26023
+ return;
26024
+ }
26025
+ throw error62;
26026
+ }
25954
26027
  if (raw.trim() !== "") {
25955
26028
  try {
25956
26029
  const parsed = JSON.parse(raw);
@@ -25961,24 +26034,29 @@ function createRelay(config2) {
25961
26034
  }
25962
26035
  }
25963
26036
  const rawHostId = typeof body.hostId === "string" ? body.hostId.trim() : "";
25964
- if (rawHostId !== "" && (rawHostId.length > MAX_HOST_ID_LEN2 || !HOST_ID_RE.test(rawHostId))) {
26037
+ if (rawHostId === "") {
26038
+ res.statusCode = 400;
26039
+ res.end(
26040
+ JSON.stringify({
26041
+ error: "host_id_required",
26042
+ hint: 'POST {"hostId":"<\u672C\u673A\u8EAB\u4EFD>"} \u2014\u2014 2026-10-11 \u8D77\u4EE4\u724C\u6309 hostId \u4E00\u673A\u4E00\u679A\uFF0C\u6CA1\u6709\u8EAB\u4EFD\u5C31\u4E0D\u7B7E'
26043
+ })
26044
+ );
26045
+ return;
26046
+ }
26047
+ if (rawHostId.length > MAX_HOST_ID_LEN2 || !HOST_ID_RE.test(rawHostId)) {
25965
26048
  res.statusCode = 400;
25966
26049
  res.end(
25967
26050
  JSON.stringify({ error: "bad_host_id", hint: `hostId \u6700\u591A ${MAX_HOST_ID_LEN2} \u5B57\u7B26\uFF0C\u53EA\u5141\u8BB8\u5B57\u6BCD\u6570\u5B57\u4E0E ._:-` })
25968
26051
  );
25969
26052
  return;
25970
26053
  }
25971
- const slot = rawHostId === "" ? UNBOUND_HOST_TOKEN_KEY : rawHostId;
25972
26054
  const token = newHostToken();
25973
- state.issueHostToken(slot, token, now);
26055
+ state.issueHostToken(rawHostId, token, now);
25974
26056
  const pruned = state.pruneHostTokens(config2.maxHostTokens);
25975
26057
  if (pruned > 0) log.warn("host tokens pruned", { dropped: pruned, kept: state.hostTokens.size });
25976
26058
  markStateChanged();
25977
- log.info(rawHostId === "" ? "host token claimed (unbound)" : "host token claimed", {
25978
- ip,
25979
- hostId: rawHostId,
25980
- hostTokens: state.hostTokens.size
25981
- });
26059
+ log.info("host token claimed", { ip, hostId: rawHostId, hostTokens: state.hostTokens.size });
25982
26060
  res.end(JSON.stringify({ token, hostId: rawHostId, rotatedAt: new Date(now).toISOString() }));
25983
26061
  }
25984
26062
  function httpHandler(req, res) {
@@ -26186,7 +26264,7 @@ function registerSignalHandlers(shutdown) {
26186
26264
 
26187
26265
  // src/main.ts
26188
26266
  function readVersion() {
26189
- if ("3.1.3") return "3.1.3";
26267
+ if ("3.1.5") return "3.1.5";
26190
26268
  try {
26191
26269
  const pkg = JSON.parse(readFileSync2(new URL("../../package.json", import.meta.url), "utf8"));
26192
26270
  if (typeof pkg.version === "string") return pkg.version;