dsh-remote-control 2.0.0 → 2.0.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.
@@ -75,12 +75,17 @@ function pillLabel(status) {
75
75
  if (waiting > 0) return { text: `\u7B49 ${waiting} \u4EF6\u4E8B`, tone: "wait" };
76
76
  return { text: "\u5DF2\u914D\u5BF9", tone: "on" };
77
77
  }
78
+ if (pairingCount(status) > 0) return { text: "\u624B\u673A\u79BB\u7EBF", tone: "wait" };
78
79
  return { text: "\u672A\u914D\u5BF9", tone: "off" };
79
80
  }
80
81
  function waitingCount(status) {
81
82
  const count = status?.waiting;
82
83
  return typeof count === "number" && count > 0 ? Math.min(Math.floor(count), 99) : 0;
83
84
  }
85
+ function pairingCount(status) {
86
+ const count = status?.pairings;
87
+ return typeof count === "number" ? count : 0;
88
+ }
84
89
  function humanDuration(sec) {
85
90
  const total = typeof sec === "number" && sec > 0 ? Math.floor(sec) : 0;
86
91
  if (total < 60) return `${total} \u79D2`;
@@ -236,7 +241,7 @@ function buildPill(root, deps) {
236
241
  let expiresInMs = 0;
237
242
  let lastStatus;
238
243
  let disposed = false;
239
- const pairedNow = () => typeof lastStatus?.paired === "number" ? lastStatus.paired : 0;
244
+ const pairingNow = () => pairingCount(lastStatus);
240
245
  const write = () => {
241
246
  const next = pillLabel(lastStatus);
242
247
  label.textContent = next.text;
@@ -268,11 +273,16 @@ function buildPill(root, deps) {
268
273
  head.appendChild(stateText);
269
274
  const actions = doc.createElement("div");
270
275
  actions.className = "drc-actions";
271
- const action = pairedNow() > 0 ? { label: "\u9000\u51FA\u914D\u5BF9", run: requestUnpair } : { label: "\u5237\u65B0", run: requestPairing };
276
+ const action = pairingNow() > 0 ? { label: "\u9000\u51FA\u914D\u5BF9", run: requestUnpair } : { label: "\u5237\u65B0", run: requestPairing };
272
277
  actions.appendChild(buttonOf(action.label, () => void action.run()));
273
278
  head.appendChild(actions);
274
279
  return head;
275
280
  };
281
+ const offlineNote = () => {
282
+ if (!lastStatus) return null;
283
+ if (pillLabel(lastStatus).text !== "\u624B\u673A\u79BB\u7EBF") return null;
284
+ return text("p", "drc-note", "\u914D\u5BF9\u8FD8\u5728\uFF1A\u5C0F\u7A0B\u5E8F\u56DE\u524D\u53F0\u4F1A\u81EA\u52A8\u91CD\u8FDE\uFF0C\u4E0D\u7528\u91CD\u65B0\u626B\u7801\u3002");
285
+ };
276
286
  const infoRows = () => {
277
287
  if (!lastStatus) return [];
278
288
  const rows = [
@@ -305,14 +315,16 @@ function buildPill(root, deps) {
305
315
  panel.appendChild(text("p", "drc-code", view.token));
306
316
  panel.appendChild(text("p", "drc-note", qrNote()));
307
317
  } else if (view.kind === "unavailable" || view.kind === "failed") {
308
- const note = view.kind === "unavailable" ? text("p", "drc-note", view.reason) : (() => {
318
+ const note2 = view.kind === "unavailable" ? text("p", "drc-note", view.reason) : (() => {
309
319
  const node = text("p", "drc-note", `\u914D\u5BF9\u8BF7\u6C42\u6CA1\u6210\u529F\uFF1A${view.detail}`);
310
320
  node.setAttribute("data-kind", "failed");
311
321
  return node;
312
322
  })();
313
- panel.appendChild(note);
323
+ panel.appendChild(note2);
314
324
  }
315
325
  for (const row of infoRows()) panel.appendChild(row);
326
+ const note = offlineNote();
327
+ if (note) panel.appendChild(note);
316
328
  };
317
329
  const closePanel = () => {
318
330
  if (countTimer !== void 0) {
@@ -373,7 +385,7 @@ function buildPill(root, deps) {
373
385
  warn("\u9000\u51FA\u914D\u5BF9\u8BF7\u6C42\u5931\u8D25\uFF08\u9762\u677F\u4F1A\u9760\u4E0B\u4E00\u6B21\u8F6E\u8BE2\u5BF9\u8D26\uFF09", error);
374
386
  }
375
387
  if (disposed || !panel) return;
376
- if (lastStatus) lastStatus = { ...lastStatus, paired: 0 };
388
+ if (lastStatus) lastStatus = { ...lastStatus, paired: 0, pairings: 0 };
377
389
  write();
378
390
  view = { kind: "loading" };
379
391
  paint();
@@ -388,7 +400,7 @@ function buildPill(root, deps) {
388
400
  panel.tabIndex = -1;
389
401
  root.appendChild(panel);
390
402
  button.setAttribute("aria-expanded", "true");
391
- if (pairedNow() > 0) {
403
+ if (pairingNow() > 0) {
392
404
  view = { kind: "info" };
393
405
  paint();
394
406
  } else {
@@ -430,7 +442,7 @@ function buildPill(root, deps) {
430
442
  const body = await response.json?.();
431
443
  if (!body || typeof body !== "object") return;
432
444
  lastStatus = body;
433
- if (view.kind === "qr" && pairedNow() > 0) {
445
+ if (view.kind === "qr" && pairingNow() > 0) {
434
446
  view = { kind: "info" };
435
447
  if (countTimer !== void 0) {
436
448
  clearInterval(countTimer);
@@ -7486,10 +7486,10 @@ var require_segments = __commonJS({
7486
7486
  const segs = getSegmentsFromString(data, Utils.isKanjiModeEnabled());
7487
7487
  const nodes = buildNodes(segs);
7488
7488
  const graph = buildGraph(nodes, version2);
7489
- const path6 = dijkstra.find_path(graph.map, "start", "end");
7489
+ const path7 = dijkstra.find_path(graph.map, "start", "end");
7490
7490
  const optimizedSegs = [];
7491
- for (let i = 1; i < path6.length - 1; i++) {
7492
- optimizedSegs.push(graph.table[path6[i]].node);
7491
+ for (let i = 1; i < path7.length - 1; i++) {
7492
+ optimizedSegs.push(graph.table[path7[i]].node);
7493
7493
  }
7494
7494
  return exports.fromArray(mergeSegments(optimizedSegs));
7495
7495
  };
@@ -9925,7 +9925,7 @@ var require_utils2 = __commonJS({
9925
9925
  // ../../node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/png.js
9926
9926
  var require_png2 = __commonJS({
9927
9927
  "../../node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/png.js"(exports) {
9928
- var fs = __require("fs");
9928
+ var fs2 = __require("fs");
9929
9929
  var PNG = require_png().PNG;
9930
9930
  var Utils = require_utils2();
9931
9931
  exports.render = function render(qrData, options) {
@@ -9966,7 +9966,7 @@ var require_png2 = __commonJS({
9966
9966
  });
9967
9967
  png.pack();
9968
9968
  };
9969
- exports.renderToFile = function renderToFile(path6, qrData, options, cb) {
9969
+ exports.renderToFile = function renderToFile(path7, qrData, options, cb) {
9970
9970
  if (typeof cb === "undefined") {
9971
9971
  cb = options;
9972
9972
  options = void 0;
@@ -9977,7 +9977,7 @@ var require_png2 = __commonJS({
9977
9977
  called = true;
9978
9978
  cb.apply(null, args);
9979
9979
  };
9980
- const stream = fs.createWriteStream(path6);
9980
+ const stream = fs2.createWriteStream(path7);
9981
9981
  stream.on("error", done);
9982
9982
  stream.on("close", done);
9983
9983
  exports.renderToFileStream(stream, qrData, options);
@@ -10039,14 +10039,14 @@ var require_utf8 = __commonJS({
10039
10039
  }
10040
10040
  return output2;
10041
10041
  };
10042
- exports.renderToFile = function renderToFile(path6, qrData, options, cb) {
10042
+ exports.renderToFile = function renderToFile(path7, qrData, options, cb) {
10043
10043
  if (typeof cb === "undefined") {
10044
10044
  cb = options;
10045
10045
  options = void 0;
10046
10046
  }
10047
- const fs = __require("fs");
10047
+ const fs2 = __require("fs");
10048
10048
  const utf82 = exports.render(qrData, options);
10049
- fs.writeFile(path6, utf82, cb);
10049
+ fs2.writeFile(path7, utf82, cb);
10050
10050
  };
10051
10051
  }
10052
10052
  });
@@ -10167,7 +10167,7 @@ var require_svg_tag = __commonJS({
10167
10167
  return str;
10168
10168
  }
10169
10169
  function qrToPath(data, size, margin) {
10170
- let path6 = "";
10170
+ let path7 = "";
10171
10171
  let moveBy = 0;
10172
10172
  let newRow = false;
10173
10173
  let lineLength = 0;
@@ -10178,19 +10178,19 @@ var require_svg_tag = __commonJS({
10178
10178
  if (data[i]) {
10179
10179
  lineLength++;
10180
10180
  if (!(i > 0 && col > 0 && data[i - 1])) {
10181
- path6 += newRow ? svgCmd("M", col + margin, 0.5 + row + margin) : svgCmd("m", moveBy, 0);
10181
+ path7 += newRow ? svgCmd("M", col + margin, 0.5 + row + margin) : svgCmd("m", moveBy, 0);
10182
10182
  moveBy = 0;
10183
10183
  newRow = false;
10184
10184
  }
10185
10185
  if (!(col + 1 < size && data[i + 1])) {
10186
- path6 += svgCmd("h", lineLength);
10186
+ path7 += svgCmd("h", lineLength);
10187
10187
  lineLength = 0;
10188
10188
  }
10189
10189
  } else {
10190
10190
  moveBy++;
10191
10191
  }
10192
10192
  }
10193
- return path6;
10193
+ return path7;
10194
10194
  }
10195
10195
  exports.render = function render(qrData, options, cb) {
10196
10196
  const opts = Utils.getOptions(options);
@@ -10198,10 +10198,10 @@ var require_svg_tag = __commonJS({
10198
10198
  const data = qrData.modules.data;
10199
10199
  const qrcodesize = size + opts.margin * 2;
10200
10200
  const bg = !opts.color.light.a ? "" : "<path " + getColorAttrib(opts.color.light, "fill") + ' d="M0 0h' + qrcodesize + "v" + qrcodesize + 'H0z"/>';
10201
- const path6 = "<path " + getColorAttrib(opts.color.dark, "stroke") + ' d="' + qrToPath(data, size, opts.margin) + '"/>';
10201
+ const path7 = "<path " + getColorAttrib(opts.color.dark, "stroke") + ' d="' + qrToPath(data, size, opts.margin) + '"/>';
10202
10202
  const viewBox = 'viewBox="0 0 ' + qrcodesize + " " + qrcodesize + '"';
10203
10203
  const width = !opts.width ? "" : 'width="' + opts.width + '" height="' + opts.width + '" ';
10204
- const svgTag = '<svg xmlns="http://www.w3.org/2000/svg" ' + width + viewBox + ' shape-rendering="crispEdges">' + bg + path6 + "</svg>\n";
10204
+ const svgTag = '<svg xmlns="http://www.w3.org/2000/svg" ' + width + viewBox + ' shape-rendering="crispEdges">' + bg + path7 + "</svg>\n";
10205
10205
  if (typeof cb === "function") {
10206
10206
  cb(null, svgTag);
10207
10207
  }
@@ -10215,15 +10215,15 @@ var require_svg = __commonJS({
10215
10215
  "../../node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/svg.js"(exports) {
10216
10216
  var svgTagRenderer = require_svg_tag();
10217
10217
  exports.render = svgTagRenderer.render;
10218
- exports.renderToFile = function renderToFile(path6, qrData, options, cb) {
10218
+ exports.renderToFile = function renderToFile(path7, qrData, options, cb) {
10219
10219
  if (typeof cb === "undefined") {
10220
10220
  cb = options;
10221
10221
  options = void 0;
10222
10222
  }
10223
- const fs = __require("fs");
10223
+ const fs2 = __require("fs");
10224
10224
  const svgTag = exports.render(qrData, options);
10225
10225
  const xmlStr = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' + svgTag;
10226
- fs.writeFile(path6, xmlStr, cb);
10226
+ fs2.writeFile(path7, xmlStr, cb);
10227
10227
  };
10228
10228
  }
10229
10229
  });
@@ -10381,8 +10381,8 @@ var require_server = __commonJS({
10381
10381
  cb
10382
10382
  };
10383
10383
  }
10384
- function getTypeFromFilename(path6) {
10385
- return path6.slice((path6.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
10384
+ function getTypeFromFilename(path7) {
10385
+ return path7.slice((path7.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
10386
10386
  }
10387
10387
  function getRendererFromType(type) {
10388
10388
  switch (type) {
@@ -10446,17 +10446,17 @@ var require_server = __commonJS({
10446
10446
  const renderer = getRendererFromType(params.opts.type);
10447
10447
  return render(renderer.renderToBuffer, text, params);
10448
10448
  };
10449
- exports.toFile = function toFile(path6, text, opts, cb) {
10450
- if (typeof path6 !== "string" || !(typeof text === "string" || typeof text === "object")) {
10449
+ exports.toFile = function toFile(path7, text, opts, cb) {
10450
+ if (typeof path7 !== "string" || !(typeof text === "string" || typeof text === "object")) {
10451
10451
  throw new Error("Invalid argument");
10452
10452
  }
10453
10453
  if (arguments.length < 3 && !canPromise()) {
10454
10454
  throw new Error("Too few arguments provided");
10455
10455
  }
10456
10456
  const params = checkParams(text, opts, cb);
10457
- const type = params.opts.type || getTypeFromFilename(path6);
10457
+ const type = params.opts.type || getTypeFromFilename(path7);
10458
10458
  const renderer = getRendererFromType(type);
10459
- const renderToFile = renderer.renderToFile.bind(null, path6);
10459
+ const renderToFile = renderer.renderToFile.bind(null, path7);
10460
10460
  return render(renderToFile, text, params);
10461
10461
  };
10462
10462
  exports.toFileStream = function toFileStream(stream, text, opts) {
@@ -10655,9 +10655,9 @@ var init_mock_kernel = __esm({
10655
10655
  });
10656
10656
 
10657
10657
  // src/index.ts
10658
- import path5 from "node:path";
10658
+ import path6 from "node:path";
10659
10659
 
10660
- // ../../node_modules/.pnpm/dsh-remote-wire@1.2.0/node_modules/dsh-remote-wire/dist/src/crypto/bytes.js
10660
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/crypto/bytes.js
10661
10661
  import { Buffer as Buffer2 } from "node:buffer";
10662
10662
  var encoder = new TextEncoder();
10663
10663
  var decoder = new TextDecoder("utf-8", { fatal: false });
@@ -10684,7 +10684,7 @@ function concat(parts) {
10684
10684
  return out;
10685
10685
  }
10686
10686
 
10687
- // ../../node_modules/.pnpm/dsh-remote-wire@1.2.0/node_modules/dsh-remote-wire/dist/src/crypto/keys.js
10687
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/crypto/keys.js
10688
10688
  import { createHash, randomBytes } from "node:crypto";
10689
10689
  var KDF_NAMESPACE = "dsh-rc/v1";
10690
10690
  var UNIT_SEPARATOR = 31;
@@ -10714,7 +10714,7 @@ function randomNonce() {
10714
10714
  return new Uint8Array(randomBytes(NONCE_BYTES));
10715
10715
  }
10716
10716
 
10717
- // ../../node_modules/.pnpm/dsh-remote-wire@1.2.0/node_modules/dsh-remote-wire/dist/src/crypto/record.js
10717
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/crypto/record.js
10718
10718
  var import_tweetnacl = __toESM(require_nacl_fast(), 1);
10719
10719
  var MAC_BYTES = 16;
10720
10720
  var MIN_RECORD_BYTES = NONCE_BYTES + MAC_BYTES;
@@ -10747,7 +10747,7 @@ function open(key, record2) {
10747
10747
  }
10748
10748
  }
10749
10749
 
10750
- // ../../node_modules/.pnpm/dsh-remote-wire@1.2.0/node_modules/dsh-remote-wire/dist/src/identity/pairing.js
10750
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/identity/pairing.js
10751
10751
  import { randomBytes as randomBytes2 } from "node:crypto";
10752
10752
  var URI_PREFIX = "dshr:";
10753
10753
  var TOKEN_RE = /^\d{6}$/;
@@ -11593,10 +11593,10 @@ function mergeDefs(...defs) {
11593
11593
  function cloneDef(schema) {
11594
11594
  return mergeDefs(schema._zod.def);
11595
11595
  }
11596
- function getElementAtPath(obj, path6) {
11597
- if (!path6)
11596
+ function getElementAtPath(obj, path7) {
11597
+ if (!path7)
11598
11598
  return obj;
11599
- return path6.reduce((acc, key) => acc?.[key], obj);
11599
+ return path7.reduce((acc, key) => acc?.[key], obj);
11600
11600
  }
11601
11601
  function promiseAllObject(promisesObj) {
11602
11602
  const keys = Object.keys(promisesObj);
@@ -11936,11 +11936,11 @@ function explicitlyAborted(x, startIndex = 0) {
11936
11936
  }
11937
11937
  return false;
11938
11938
  }
11939
- function prefixIssues(path6, issues) {
11939
+ function prefixIssues(path7, issues) {
11940
11940
  return issues.map((iss) => {
11941
11941
  var _a3;
11942
11942
  (_a3 = iss).path ?? (_a3.path = []);
11943
- iss.path.unshift(path6);
11943
+ iss.path.unshift(path7);
11944
11944
  return iss;
11945
11945
  });
11946
11946
  }
@@ -12390,16 +12390,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
12390
12390
  }
12391
12391
  function formatError(error62, mapper = (issue2) => issue2.message) {
12392
12392
  const fieldErrors = { _errors: [] };
12393
- const processError = (error63, path6 = []) => {
12393
+ const processError = (error63, path7 = []) => {
12394
12394
  for (const issue2 of error63.issues) {
12395
12395
  if (issue2.code === "invalid_union" && issue2.errors.length) {
12396
- issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
12396
+ issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
12397
12397
  } else if (issue2.code === "invalid_key") {
12398
- processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
12398
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
12399
12399
  } else if (issue2.code === "invalid_element") {
12400
- processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
12400
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
12401
12401
  } else {
12402
- const fullpath = [...path6, ...issue2.path];
12402
+ const fullpath = [...path7, ...issue2.path];
12403
12403
  if (fullpath.length === 0) {
12404
12404
  fieldErrors._errors.push(mapper(issue2));
12405
12405
  } else {
@@ -12438,17 +12438,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
12438
12438
  }
12439
12439
  function treeifyError(error62, mapper = (issue2) => issue2.message) {
12440
12440
  const result2 = { errors: [] };
12441
- const processError = (error63, path6 = []) => {
12441
+ const processError = (error63, path7 = []) => {
12442
12442
  var _a3;
12443
12443
  for (const issue2 of error63.issues) {
12444
12444
  if (issue2.code === "invalid_union" && issue2.errors.length) {
12445
- issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
12445
+ issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
12446
12446
  } else if (issue2.code === "invalid_key") {
12447
- processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
12447
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
12448
12448
  } else if (issue2.code === "invalid_element") {
12449
- processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
12449
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
12450
12450
  } else {
12451
- const fullpath = [...path6, ...issue2.path];
12451
+ const fullpath = [...path7, ...issue2.path];
12452
12452
  if (fullpath.length === 0) {
12453
12453
  result2.errors.push(mapper(issue2));
12454
12454
  continue;
@@ -12487,8 +12487,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
12487
12487
  }
12488
12488
  function toDotPath(_path) {
12489
12489
  const segs = [];
12490
- const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12491
- for (const seg of path6) {
12490
+ const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12491
+ for (const seg of path7) {
12492
12492
  if (typeof seg === "number")
12493
12493
  segs.push(`[${seg}]`);
12494
12494
  else if (typeof seg === "symbol")
@@ -29590,13 +29590,13 @@ function resolveRef(ref, ctx) {
29590
29590
  if (!ref.startsWith("#")) {
29591
29591
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
29592
29592
  }
29593
- const path6 = ref.slice(1).split("/").filter(Boolean);
29594
- if (path6.length === 0) {
29593
+ const path7 = ref.slice(1).split("/").filter(Boolean);
29594
+ if (path7.length === 0) {
29595
29595
  return ctx.rootSchema;
29596
29596
  }
29597
29597
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
29598
- if (path6[0] === defsKey) {
29599
- const key = path6[1] === void 0 ? void 0 : decodeJSONPointerSegment(path6[1]);
29598
+ if (path7[0] === defsKey) {
29599
+ const key = path7[1] === void 0 ? void 0 : decodeJSONPointerSegment(path7[1]);
29600
29600
  if (!key || !ctx.defs[key]) {
29601
29601
  throw new Error(`Reference not found: ${ref}`);
29602
29602
  }
@@ -30445,7 +30445,7 @@ function date4(params) {
30445
30445
  return _coercedDate(ZodDate, params);
30446
30446
  }
30447
30447
 
30448
- // ../../node_modules/.pnpm/dsh-remote-wire@1.2.0/node_modules/dsh-remote-wire/dist/src/wire/frames.js
30448
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/wire/frames.js
30449
30449
  var PAIRING_TOKEN_RE = /^\d{6}$/;
30450
30450
  var nonEmpty = external_exports.string().min(1);
30451
30451
  var pairingToken = external_exports.string().regex(PAIRING_TOKEN_RE);
@@ -30604,7 +30604,7 @@ function parseRelayFrameText(raw) {
30604
30604
  }
30605
30605
  }
30606
30606
 
30607
- // ../../node_modules/.pnpm/dsh-remote-wire@1.2.0/node_modules/dsh-remote-wire/dist/src/wire/payloads.js
30607
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/wire/payloads.js
30608
30608
  var SESSION_STATES = [
30609
30609
  "idle",
30610
30610
  "running",
@@ -30767,12 +30767,28 @@ var evPayload = external_exports.discriminatedUnion("t", [
30767
30767
  evResult,
30768
30768
  evSessionHistory
30769
30769
  ]);
30770
+ var imageAttachment = external_exports.object({
30771
+ /** 落盘用的文件名(mp 侧生成,形如 `img-<ms>-<n>.jpg`;主机侧还会再收敛一次字符集)。 */
30772
+ name: nonEmpty2,
30773
+ mediaType: external_exports.literal("image/jpeg"),
30774
+ /** base64 字节,**不带** `data:` 前缀。 */
30775
+ data: nonEmpty2,
30776
+ /** 压缩后的像素尺寸,主机写旁车元数据用(排错时对得上)。 */
30777
+ width: external_exports.number().int().positive().optional(),
30778
+ height: external_exports.number().int().positive().optional()
30779
+ });
30770
30780
  var cmdSendPrompt = external_exports.object({
30771
30781
  t: external_exports.literal("cmd.send_prompt"),
30772
30782
  cmdId: nonEmpty2,
30773
30783
  /** DSH 会话 id,不是配对通道 id。 */
30774
30784
  sessionId: nonEmpty2,
30775
- text: external_exports.string()
30785
+ text: external_exports.string(),
30786
+ /**
30787
+ * 随消息的图片附件。**可选**:老版本手机不带这个字段,老主机看到也不认
30788
+ * (zod 默认 strip 未知键,而 text 照旧)——所以这是向后兼容的增补,不升协议版本。
30789
+ * 上限 4 张:再多对"看清楚这张图"没有帮助,只是把帧撑爆。
30790
+ */
30791
+ images: external_exports.array(imageAttachment).max(4).optional()
30776
30792
  });
30777
30793
  var answerItem = external_exports.object({
30778
30794
  questionId: nonEmpty2,
@@ -30861,7 +30877,7 @@ var PAYLOAD_TYPES = {
30861
30877
  evSessionHistory: "ev.session_history"
30862
30878
  };
30863
30879
 
30864
- // ../../node_modules/.pnpm/dsh-remote-wire@1.2.0/node_modules/dsh-remote-wire/dist/src/wire/outbound.js
30880
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/wire/outbound.js
30865
30881
  function sessionChanged(sessions, reason) {
30866
30882
  return { t: PAYLOAD_TYPES.evSessionChanged, sessions, ...reason === void 0 ? {} : { reason } };
30867
30883
  }
@@ -30920,7 +30936,7 @@ function sessionHistory(args) {
30920
30936
  };
30921
30937
  }
30922
30938
 
30923
- // ../../node_modules/.pnpm/dsh-remote-wire@1.2.0/node_modules/dsh-remote-wire/dist/src/platform/sleep.js
30939
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/platform/sleep.js
30924
30940
  function detectBackend(platform) {
30925
30941
  if (platform === "darwin")
30926
30942
  return "caffeinate";
@@ -30961,6 +30977,81 @@ function buildSleepCommand(platform, ownerPid, keepDisplay = false) {
30961
30977
  // src/core/runtime.ts
30962
30978
  import { randomUUID } from "node:crypto";
30963
30979
 
30980
+ // src/shell/uploads.ts
30981
+ import fs from "node:fs";
30982
+ import path from "node:path";
30983
+ function safeSegment(raw, fallback, maxLen = 96) {
30984
+ const cleaned = String(raw || "").replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^[._-]+/, "").slice(0, maxLen).replace(/[._-]+$/, "");
30985
+ return cleaned === "" ? fallback : cleaned;
30986
+ }
30987
+ function decodeBase64(data) {
30988
+ if (!/^[A-Za-z0-9+/=\s]+$/.test(data)) return null;
30989
+ const buf = Buffer.from(data, "base64");
30990
+ return buf.length > 0 ? buf : null;
30991
+ }
30992
+ function looksLikeJpeg(buf) {
30993
+ return buf.length > 3 && buf[0] === 255 && buf[1] === 216 && buf[2] === 255;
30994
+ }
30995
+ function saveImageAttachments(options) {
30996
+ const maxCount = options.maxCount ?? 4;
30997
+ const maxBytes = options.maxBytesPerImage ?? 4 * 1024 * 1024;
30998
+ const images = options.images || [];
30999
+ if (images.length === 0) return { ok: true, saved: [], dir: options.dir };
31000
+ if (images.length > maxCount) {
31001
+ return { ok: false, message: `\u4E00\u6B21\u6700\u591A\u5E26 ${maxCount} \u5F20\u56FE\u7247\uFF0C\u6536\u5230 ${images.length} \u5F20` };
31002
+ }
31003
+ const decoded = [];
31004
+ for (const [i, img] of images.entries()) {
31005
+ if (img.mediaType !== "image/jpeg") {
31006
+ return { ok: false, message: `\u7B2C ${i + 1} \u5F20\u4E0D\u662F jpeg\uFF08${img.mediaType || "\u7A7A"}\uFF09\uFF0C\u4E3B\u673A\u53EA\u6536 jpeg` };
31007
+ }
31008
+ const buf = decodeBase64(img.data);
31009
+ if (!buf) return { ok: false, message: `\u7B2C ${i + 1} \u5F20\u7684\u89E3\u7801\u5931\u8D25\uFF08\u4E0D\u662F\u5408\u6CD5 base64\uFF09` };
31010
+ if (buf.length > maxBytes) {
31011
+ return {
31012
+ ok: false,
31013
+ message: `\u7B2C ${i + 1} \u5F20\u6709 ${Math.round(buf.length / 1024)}KB\uFF0C\u8D85\u8FC7\u5355\u5F20\u4E0A\u9650 ${Math.round(maxBytes / 1024)}KB`
31014
+ };
31015
+ }
31016
+ if (!looksLikeJpeg(buf)) {
31017
+ return { ok: false, message: `\u7B2C ${i + 1} \u5F20\u7684\u5185\u5BB9\u4E0D\u662F jpeg\uFF08\u6587\u4EF6\u5934\u5BF9\u4E0D\u4E0A\uFF09` };
31018
+ }
31019
+ decoded.push({ img, buf });
31020
+ }
31021
+ const sessionDir = path.join(options.dir, safeSegment(options.sessionId, "session"));
31022
+ const written = [];
31023
+ try {
31024
+ fs.mkdirSync(sessionDir, { recursive: true });
31025
+ for (const [i, one] of decoded.entries()) {
31026
+ const base = safeSegment(one.img.name, `img-${i + 1}.jpg`).replace(/\.jpg$/i, "") + ".jpg";
31027
+ let name = base;
31028
+ let n = 1;
31029
+ while (fs.existsSync(path.join(sessionDir, name))) {
31030
+ name = base.replace(/\.jpg$/, `-${++n}.jpg`);
31031
+ }
31032
+ const file2 = path.join(sessionDir, name);
31033
+ fs.writeFileSync(file2, one.buf, { mode: 384 });
31034
+ written.push({ name, path: file2, bytes: one.buf.length });
31035
+ }
31036
+ } catch (error62) {
31037
+ return {
31038
+ ok: false,
31039
+ message: `\u56FE\u7247\u843D\u76D8\u5931\u8D25\uFF1A${String(error62?.message ?? error62).slice(0, 160)}`
31040
+ };
31041
+ }
31042
+ return { ok: true, saved: written, dir: sessionDir };
31043
+ }
31044
+ function appendImageNote(text, saved) {
31045
+ if (saved.length === 0) return text;
31046
+ const lines = saved.map((img, i) => `${i + 1}. ${img.path}`);
31047
+ const note = `
31048
+
31049
+ [\u56FE\u7247\u9644\u4EF6 ${saved.length} \u5F20\uFF0C\u5DF2\u5B58\u5230\u672C\u673A]
31050
+ ${lines.join("\n")}`;
31051
+ const head = String(text || "");
31052
+ return head === "" ? note.replace(/^\n\n/, "") : head + note;
31053
+ }
31054
+
30964
31055
  // src/core/window.ts
30965
31056
  var DEFAULT_WINDOW_OPTIONS = {
30966
31057
  windowMs: 120,
@@ -31131,7 +31222,11 @@ var DEFAULTS = {
31131
31222
  listingRefreshMs: 15e3,
31132
31223
  approvalTimeoutMs: 18e4,
31133
31224
  questionTimeoutMs: 3e5,
31134
- unarchiveOnPrompt: true
31225
+ unarchiveOnPrompt: true,
31226
+ // 图片附件:默认**不收**(uploadDir 空 = 拒收)。要放开必须在插件配置里显式
31227
+ // 给一个目录——落盘是往用户磁盘写文件,不该由库默认值悄悄代劳。
31228
+ uploadDir: "",
31229
+ maxImageBytes: 4 * 1024 * 1024
31135
31230
  };
31136
31231
  var LISTING_LIMIT = 100;
31137
31232
  var HISTORY_LIMIT = 40;
@@ -31218,7 +31313,32 @@ var HostRuntime = class {
31218
31313
  return;
31219
31314
  }
31220
31315
  }
31221
- const sent = await this.kernel.sendPrompt(cmd.sessionId, cmd.text);
31316
+ let text = cmd.text;
31317
+ const images = Array.isArray(cmd.images) ? cmd.images : [];
31318
+ if (images.length > 0) {
31319
+ if (!this.options.uploadDir) {
31320
+ reply(false, { message: "\u8FD9\u53F0\u4E3B\u673A\u6CA1\u914D\u56FE\u7247\u843D\u76D8\u76EE\u5F55\uFF08uploadDir\uFF09\uFF0C\u6536\u4E0D\u4E86\u56FE\u7247\u9644\u4EF6" });
31321
+ return;
31322
+ }
31323
+ const saved = saveImageAttachments({
31324
+ images,
31325
+ dir: this.options.uploadDir,
31326
+ sessionId: cmd.sessionId,
31327
+ maxBytesPerImage: this.options.maxImageBytes
31328
+ });
31329
+ if (!saved.ok) {
31330
+ reply(false, { message: saved.message });
31331
+ return;
31332
+ }
31333
+ this.options.log?.("\u56FE\u7247\u9644\u4EF6\u843D\u76D8", {
31334
+ sessionId: cmd.sessionId,
31335
+ images: saved.saved.length,
31336
+ bytes: saved.saved.reduce((sum, img) => sum + img.bytes, 0),
31337
+ dir: saved.dir
31338
+ });
31339
+ text = appendImageNote(text, saved.saved);
31340
+ }
31341
+ const sent = await this.kernel.sendPrompt(cmd.sessionId, text);
31222
31342
  reply(sent.ok, sent.message ? { message: sent.message } : {});
31223
31343
  if (sent.ok) {
31224
31344
  this.sleep.markActive();
@@ -31567,9 +31687,11 @@ var HostRuntime = class {
31567
31687
  * 再补一帧 `ev.run_state` 兜住"手机上装的是老版小程序"这一档。
31568
31688
  *
31569
31689
  * 为什么要两条而不是一条:`ev.permission_resolved` / `ev.question_resolved` 是按
31570
- * `requestId` 收单的(一次答完不会误收别的会话、别的请求那张卡),但**用户手机上现在装的
31571
- * 那一版还不认这两帧**——小程序的分发是一串 `if (p.t === …)`,认不出来的 `t` 静默忽略。
31572
- * 而 `ev.run_state` 正好是它早就认的那条"挂着的审批/提问作废"信号
31690
+ * `requestId` 收单的(一次答完不会误收别的会话、别的请求那张卡),这是**正路**——
31691
+ * mp 1.0.1 起就认这两帧(`pages/chat/chat.js` 的 `_onPermissionResolved` /
31692
+ * `_onQuestionResolved`)。而 `ev.run_state` 是它从第一版就认的粗粒度信号
31693
+ * (整会话两张卡一起收),发它只为兜住 **1.0.1 以前**的安装存量——那一档小程序
31694
+ * 的分发是一串 `if (p.t === …)`,认不出来的 `t` 静默忽略。
31573
31695
  * (`pages/chat/chat.js` 的 `_onRunState` 同时清 `pendingPermission` 与 `pendingQuestion`,
31574
31696
  * 并停掉那条还在走的本地倒数)。所以两条都发:新版手机收到精确帧就精收一张,
31575
31697
  * 老版手机靠 `run_state` 也能当场收卡,不用等用户重新上传小程序。
@@ -33218,12 +33340,6 @@ function createServicesKernel(services, options) {
33218
33340
  modelFace: modelFaceSummary(),
33219
33341
  workspaceRegistry: typeof services.workspaceRegistry === "object",
33220
33342
  userQuestions: typeof services.userQuestions === "object",
33221
- // 这个键**已经不生效**了:提问现在跟审批一样是 waterfall 参与者,两端同弹、
33222
- // 谁先答谁算,不再有"接管/取代桌面"这回事(那条 `registerProvider` 单提供者
33223
- // 路线在这一代宿主上根本不存在)。这里照实把配置值报出来,真正的状态看
33224
- // `questionsFace`。(删键要动 config.ts/index.ts,那两棵树此刻有并行会话在飞的改动。)
33225
- takeOverQuestions: options.takeOverQuestions === true,
33226
- takeOverQuestionsInert: options.takeOverQuestions === true,
33227
33343
  archivedSessions: archivedIds().size,
33228
33344
  hasOn: typeof services.on === "function",
33229
33345
  // 真机排错的第一现场:内核到底发了哪些事件类型、其中哪些我们没认。
@@ -33576,7 +33692,7 @@ function createOneShotTimers(clock) {
33576
33692
 
33577
33693
  // src/shell/status.ts
33578
33694
  import { mkdirSync, renameSync, writeFileSync } from "node:fs";
33579
- import path from "node:path";
33695
+ import path2 from "node:path";
33580
33696
  var StatusFile = class {
33581
33697
  constructor(file2, clock, refreshMs = 3e3) {
33582
33698
  this.file = file2;
@@ -33590,7 +33706,7 @@ var StatusFile = class {
33590
33706
  write(snapshot) {
33591
33707
  if (!this.file) return;
33592
33708
  try {
33593
- mkdirSync(path.dirname(this.file), { recursive: true });
33709
+ mkdirSync(path2.dirname(this.file), { recursive: true });
33594
33710
  const tmp = `${this.file}.tmp-${process.pid}`;
33595
33711
  writeFileSync(
33596
33712
  tmp,
@@ -33633,7 +33749,7 @@ var StatusFile = class {
33633
33749
  // src/shell/host-id.ts
33634
33750
  import { randomBytes as randomBytes4 } from "node:crypto";
33635
33751
  import { chmodSync, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
33636
- import path2 from "node:path";
33752
+ import path3 from "node:path";
33637
33753
  var HOST_ID = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/;
33638
33754
  function newHostId() {
33639
33755
  return `drc-${randomBytes4(6).toString("hex")}`;
@@ -33641,7 +33757,7 @@ function newHostId() {
33641
33757
  function resolveHostId(dir, configured) {
33642
33758
  if (configured) return configured;
33643
33759
  if (!dir) return newHostId();
33644
- const file2 = path2.join(dir, "host-id");
33760
+ const file2 = path3.join(dir, "host-id");
33645
33761
  let existing;
33646
33762
  try {
33647
33763
  existing = readFileSync(file2, "utf8").trim();
@@ -33673,7 +33789,7 @@ function resolveHostId(dir, configured) {
33673
33789
  // src/shell/pair-store.ts
33674
33790
  import { chmodSync as chmodSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
33675
33791
  import { createHash as createHash2 } from "node:crypto";
33676
- import path3 from "node:path";
33792
+ import path4 from "node:path";
33677
33793
  var storedConversationSchema = external_exports.object({
33678
33794
  id: external_exports.string().min(1),
33679
33795
  /** 配对 PSK(base64)。两把方向密钥在恢复时由它现派生,不落盘。 */
@@ -33691,14 +33807,14 @@ var storeSchema = external_exports.object({
33691
33807
  savedAt: external_exports.number().finite().nonnegative(),
33692
33808
  conversations: external_exports.array(storedConversationSchema)
33693
33809
  });
33694
- function safeSegment(hostId) {
33810
+ function safeSegment2(hostId) {
33695
33811
  const cleaned = hostId.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 48);
33696
33812
  const tag = createHash2("sha256").update(hostId).digest("hex").slice(0, 8);
33697
33813
  return `${cleaned || "host"}-${tag}`;
33698
33814
  }
33699
33815
  function defaultPairStoreFile(statusFile, hostId) {
33700
33816
  if (!statusFile) return "";
33701
- return path3.join(path3.dirname(statusFile), `conversations-${safeSegment(hostId)}.json`);
33817
+ return path4.join(path4.dirname(statusFile), `conversations-${safeSegment2(hostId)}.json`);
33702
33818
  }
33703
33819
  var PairStore = class {
33704
33820
  constructor(options) {
@@ -33768,7 +33884,7 @@ var PairStore = class {
33768
33884
  const body = JSON.stringify({ version: 1, hostId, savedAt: now, conversations }) + "\n";
33769
33885
  const tmp = `${file2}.tmp-${process.pid}`;
33770
33886
  try {
33771
- mkdirSync3(path3.dirname(file2), { recursive: true });
33887
+ mkdirSync3(path4.dirname(file2), { recursive: true });
33772
33888
  writeFileSync3(tmp, body, { encoding: "utf8", mode: 384 });
33773
33889
  chmodSync2(tmp, 384);
33774
33890
  renameSync2(tmp, file2);
@@ -33797,7 +33913,7 @@ function safeJsonParse(raw) {
33797
33913
 
33798
33914
  // src/shell/config.ts
33799
33915
  import { homedir } from "node:os";
33800
- import path4 from "node:path";
33916
+ import path5 from "node:path";
33801
33917
  var DEFAULT_CONFIG = {
33802
33918
  enabled: true,
33803
33919
  serverUrl: "ws://127.0.0.1:8787",
@@ -33807,12 +33923,11 @@ var DEFAULT_CONFIG = {
33807
33923
  hostLabel: "dsh-host",
33808
33924
  keepAwake: { enabled: true, idleReleaseSec: 300, keepDisplay: false },
33809
33925
  mockBridge: false,
33810
- statusFile: path4.join(homedir(), ".dsh", "dsh-remote-control", "status.json"),
33926
+ statusFile: path5.join(homedir(), ".dsh", "dsh-remote-control", "status.json"),
33811
33927
  unarchiveOnPrompt: true,
33812
33928
  pairOnStartSec: 0,
33813
33929
  pairTtlMs: 12e4,
33814
33930
  carrierGraceMs: 5e3,
33815
- takeOverQuestions: false,
33816
33931
  approvalTimeoutSec: 180,
33817
33932
  listingRefreshSec: 15,
33818
33933
  conversationIdleTtlSec: 86400,
@@ -33820,6 +33935,9 @@ var DEFAULT_CONFIG = {
33820
33935
  restoredIdleTtlSec: 604800,
33821
33936
  // 密钥簿落盘:默认跟着 statusFile 走(见 `resolvePairStoreFile`)。
33822
33937
  pairStoreFile: "",
33938
+ // 图片附件落盘:默认 status.json 同目录的 uploads/(见 `resolveUploadDir`)。
33939
+ uploadDir: "",
33940
+ maxImageBytes: 4 * 1024 * 1024,
33823
33941
  // 配对的唯一入口。关掉它 = 这台主机**没有**配对入口(`/drc pair` 与文本二维码都在
33824
33942
  // 2026-10-03 删掉了),所以 index.ts 会把它记成一条 warn 而不是安静地什么都不做。
33825
33943
  pill: { enabled: true }
@@ -33831,7 +33949,17 @@ function resolvePairStoreFile(config2, hostId) {
33831
33949
  if (!configured) return defaultPairStoreFile(config2.statusFile, hostId);
33832
33950
  if (configured.startsWith("/")) return configured;
33833
33951
  if (!config2.statusFile) return configured;
33834
- return path4.join(path4.dirname(config2.statusFile), configured);
33952
+ return path5.join(path5.dirname(config2.statusFile), configured);
33953
+ }
33954
+ function resolveUploadDir(config2) {
33955
+ const configured = config2.uploadDir.trim();
33956
+ if (!configured) {
33957
+ const base = config2.statusFile ? path5.dirname(config2.statusFile) : path5.join(homedir(), ".dsh", "dsh-remote-control");
33958
+ return path5.join(base, "uploads");
33959
+ }
33960
+ if (configured.startsWith("/")) return configured;
33961
+ if (!config2.statusFile) return configured;
33962
+ return path5.join(path5.dirname(config2.statusFile), configured);
33835
33963
  }
33836
33964
  var loopback = /^wss?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:|\/|$)/;
33837
33965
  var wsUrl = /^wss?:\/\/.+/;
@@ -33860,7 +33988,6 @@ function readConfig(injected, env = process.env) {
33860
33988
  if (fromEnv("DRC_PAIR_ON_START_SEC")) merged.pairOnStartSec = envSeconds(env.DRC_PAIR_ON_START_SEC);
33861
33989
  if (fromEnv("DRC_PILL")) merged.pill.enabled = envFlag(env.DRC_PILL, merged.pill.enabled);
33862
33990
  if (fromEnv("DRC_MOCK_BRIDGE") === "1") merged.mockBridge = true;
33863
- if (fromEnv("DRC_TAKE_OVER_QUESTIONS") === "1") merged.takeOverQuestions = true;
33864
33991
  if (merged.hostTokenEnv) merged.hostToken = env[merged.hostTokenEnv] ?? merged.hostToken;
33865
33992
  return merged;
33866
33993
  }
@@ -33935,13 +34062,6 @@ function validateConfig(config2) {
33935
34062
  if (!config2.statusFile) {
33936
34063
  problems.push({ level: "warn", field: "statusFile", message: "\u4E3A\u7A7A\u5C06\u5173\u95ED\u72B6\u6001\u5FEB\u7167\uFF1BGUI \u5BBF\u4E3B\u91CC\u8FD9\u662F\u552F\u4E00\u7684\u6392\u9519\u5165\u53E3" });
33937
34064
  }
33938
- if (config2.takeOverQuestions) {
33939
- problems.push({
33940
- level: "warn",
33941
- field: "takeOverQuestions",
33942
- message: "\u63D0\u95EE\u63A5\u7BA1\u4F1A**\u53D6\u4EE3\u684C\u9762 UI \u7684\u63D0\u95EE\u80FD\u529B**\uFF08ctx.userQuestions \u53EA\u5141\u8BB8\u4E00\u4E2A\u6D3B\u8DC3 provider\uFF09\uFF1B\u624B\u673A\u7AEF\u4E0D\u5728\u7EBF\u65F6\u63D0\u95EE\u4F1A\u76F4\u63A5\u5931\u8D25"
33943
- });
33944
- }
33945
34065
  return problems;
33946
34066
  }
33947
34067
  function redact(value) {
@@ -34098,6 +34218,7 @@ function pillStatusHandler(deps) {
34098
34218
  json2(response, 200, {
34099
34219
  relay: status.relay,
34100
34220
  paired: status.paired,
34221
+ pairings: status.pairings,
34101
34222
  serverUrl: status.serverUrl,
34102
34223
  version: status.version,
34103
34224
  waiting: status.waiting,
@@ -34261,7 +34382,7 @@ function startPill(ctx, deps) {
34261
34382
  }
34262
34383
 
34263
34384
  // src/version.ts
34264
- var PLUGIN_VERSION = true ? "2.0.0" : "dev";
34385
+ var PLUGIN_VERSION = true ? "2.0.1" : "dev";
34265
34386
 
34266
34387
  // src/index.ts
34267
34388
  var SERVICE_NAMES = [
@@ -34317,7 +34438,7 @@ function applyInner(ctx, injected) {
34317
34438
  const clock = systemClock;
34318
34439
  const oneShot = createOneShotTimers(clock);
34319
34440
  const status = new StatusFile(config2.statusFile, clock);
34320
- const hostId = resolveHostId(config2.statusFile ? path5.dirname(config2.statusFile) : "", config2.hostId);
34441
+ const hostId = resolveHostId(config2.statusFile ? path6.dirname(config2.statusFile) : "", config2.hostId);
34321
34442
  const log = (message, fields = {}) => {
34322
34443
  try {
34323
34444
  process.stdout.write(
@@ -34441,6 +34562,8 @@ function applyInner(ctx, injected) {
34441
34562
  runtime = new HostRuntime(port, transport, sleep, clock, {
34442
34563
  listingRefreshMs: config2.listingRefreshSec * 1e3,
34443
34564
  unarchiveOnPrompt: config2.unarchiveOnPrompt,
34565
+ uploadDir: resolveUploadDir(config2),
34566
+ maxImageBytes: config2.maxImageBytes,
34444
34567
  approvalTimeoutMs: config2.approvalTimeoutSec * 1e3,
34445
34568
  log: (message, fields) => log(message, fields)
34446
34569
  });
@@ -34571,12 +34694,14 @@ function applyInner(ctx, injected) {
34571
34694
  function currentPairing() {
34572
34695
  const shown = active.pairing;
34573
34696
  if (!shown) return null;
34574
- if (!slots.resolveFor(shown.token)) return null;
34575
- return { qr: shown.qr, token: shown.token, expiresAt: shown.expiresAt };
34697
+ const slot = slots.resolveFor(shown.token);
34698
+ if (!slot) return null;
34699
+ return { qr: shown.qr, token: shown.token, expiresAt: slot.expiresAt };
34576
34700
  }
34577
34701
  function ensureFreshPairing() {
34578
34702
  const fresh = currentPairing() ?? createPairing();
34579
34703
  if (!fresh) return null;
34704
+ if (fresh.expiresAt <= Date.now()) return createPairing();
34580
34705
  return { qr: fresh.qr, token: fresh.token, expiresAt: fresh.expiresAt };
34581
34706
  }
34582
34707
  function pillStatus() {
@@ -34584,6 +34709,11 @@ function applyInner(ctx, injected) {
34584
34709
  return {
34585
34710
  relay: relay ? relayState() : "idle",
34586
34711
  paired: relay?.clientCount ?? 0,
34712
+ // 配对簿里几条会话 = 配对还在不在。为什么必须与 paired 分开:pair-store 之后
34713
+ // 配对按 D3 长存、落盘,手机退后台只是 socket 断——只用 clientCount 回答 配没配上,
34714
+ // 用户把小程序放进后台的每一分钟,桌面那颗 pill 都会说 未配对,点开还会烧一张
34715
+ // 不需要的新码(2026-10-04 用户报的误解)。
34716
+ pairings: relay?.conversationCount ?? 0,
34587
34717
  serverUrl: config2.serverUrl,
34588
34718
  version: PLUGIN_VERSION,
34589
34719
  waiting: waiting.count,
@@ -34625,7 +34755,7 @@ function applyInner(ctx, injected) {
34625
34755
  return;
34626
34756
  }
34627
34757
  decided = true;
34628
- start(createServicesKernel(collected, { log, takeOverQuestions: config2.takeOverQuestions, clock }));
34758
+ start(createServicesKernel(collected, { log, clock }));
34629
34759
  return;
34630
34760
  }
34631
34761
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-remote-control",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "DSH Remote Control host plugin: pairs a running DeepSeek Harness with the DRC relay so a WeChat Mini Program can send prompts, watch streamed output, and answer approvals. Payload-level E2E encryption; the relay never sees plaintext. Pairing is one click on the status-bar pill (three same-origin routes + a browser half).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,7 +53,7 @@
53
53
  "provenance": true
54
54
  },
55
55
  "dependencies": {
56
- "dsh-remote-wire": "^1.2.0",
56
+ "dsh-remote-wire": "^1.3.0",
57
57
  "qrcode": "^1.5.4",
58
58
  "tweetnacl": "^1.0.3",
59
59
  "ws": "^8.18.0",