dsh-remote-control 2.0.1 → 2.0.2

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.
@@ -73,10 +73,10 @@ function pillLabel(status) {
73
73
  if (paired > 0) {
74
74
  const waiting = waitingCount(status);
75
75
  if (waiting > 0) return { text: `\u7B49 ${waiting} \u4EF6\u4E8B`, tone: "wait" };
76
- return { text: "\u5DF2\u914D\u5BF9", tone: "on" };
76
+ return { text: "\u5DF2\u8FDE\u63A5", tone: "on" };
77
77
  }
78
78
  if (pairingCount(status) > 0) return { text: "\u624B\u673A\u79BB\u7EBF", tone: "wait" };
79
- return { text: "\u672A\u914D\u5BF9", tone: "off" };
79
+ return { text: "\u8FDC\u7A0B\u672A\u8FDE\u63A5", tone: "off" };
80
80
  }
81
81
  function waitingCount(status) {
82
82
  const count = status?.waiting;
@@ -86,6 +86,7 @@ function pairingCount(status) {
86
86
  const count = status?.pairings;
87
87
  return typeof count === "number" ? count : 0;
88
88
  }
89
+ var GUIDE_LINE = "\u6253\u5F00\u5C0F\u7A0B\u5E8F\u626B\u8FD9\u4E2A\u7801 \u8FDC\u7A0B\u63A7\u5236\u8FD9\u53F0\u7535\u8111";
89
90
  function humanDuration(sec) {
90
91
  const total = typeof sec === "number" && sec > 0 ? Math.floor(sec) : 0;
91
92
  if (total < 60) return `${total} \u79D2`;
@@ -96,7 +97,7 @@ function waitingRow(status) {
96
97
  const count = waitingCount(status);
97
98
  if (count === 0) return "";
98
99
  const oldest = typeof status?.waitingOldestSec === "number" ? status.waitingOldestSec : 0;
99
- return `${count} \u4EF6${oldest > 0 ? ` \xB7 \u6700\u4E45 ${humanDuration(oldest)}` : ""}`.slice(0, 40);
100
+ return `${count} \u4EF6${oldest > 0 ? ` \u6700\u4E45 ${humanDuration(oldest)}` : ""}`.slice(0, 40);
100
101
  }
101
102
  function relayAddress(url) {
102
103
  if (typeof url !== "string" || url.trim() === "") return "";
@@ -109,7 +110,7 @@ function relayRow(status) {
109
110
  }
110
111
  function relayStateRow(status) {
111
112
  const relay = status?.relay;
112
- if (relay === "online") return "\u5DF2\u8FDE\u63A5";
113
+ if (relay === "online") return "\u5DF2\u5C31\u7EEA";
113
114
  if (relay === "connecting") return "\u8FDE\u63A5\u4E2D";
114
115
  if (relay === "offline") return "\u5DF2\u65AD\u5F00";
115
116
  if (relay === "idle") return "\u672A\u542F\u52A8";
@@ -165,7 +166,10 @@ var CSS = `
165
166
  .drc-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
166
167
  .drc-qr { display: block; width: 200px; height: 200px; margin: 8px auto 0; image-rendering: pixelated; }
167
168
  .drc-code { margin: 8px 0 0; text-align: center; font-weight: 600; font-size: 14px; letter-spacing: 2px; }
168
- .drc-note { margin: 4px 0 0; text-align: center; color: var(--dsw-alias-label-tertiary); }
169
+ /* \u5F15\u5BFC\u884C\uFF1A\u6BD4 note \u91CD\u4E00\u6863\uFF08\u7B2C\u4E00\u6B21\u7528\u7684\u4EBA\u8981\u770B\u6E05\uFF09\uFF0C\u4F46\u4E0D\u52A0\u56FE\u6807\u4E0D\u52A0\u5E95\u8272\u2014\u2014
170
+ \u8FD9\u4E00\u5C4F\u7684\u4E3B\u89D2\u662F\u4E8C\u7EF4\u7801\uFF0C\u591A\u4E00\u4E2A\u8272\u5757\u5C31\u6210\u4E86\u63A8\u9500\u800C\u4E0D\u662F\u5F15\u5BFC\u3002 */
171
+ .drc-guide { margin: 8px 0 0; text-align: center; color: var(--dsw-alias-label-secondary, #5f6368); font-size: 12px; }
172
+ .drc-note { margin: 4px 0 0; text-align: center; color: var(--dsw-alias-label-tertiary); }
169
173
  .drc-note[data-kind="failed"] { color: var(--dsw-alias-state-error-primary, #ff4d4f); }
170
174
  `;
171
175
  function isSlots(value) {
@@ -263,7 +267,7 @@ function buildPill(root, deps) {
263
267
  node.addEventListener("click", onClick);
264
268
  return node;
265
269
  };
266
- const qrNote = () => expiresInMs > 0 ? `\u626B\u7801\u914D\u5BF9 \xB7 ${secondsText(expiresInMs)}\u540E\u8FC7\u671F` : "\u914D\u5BF9\u7801\u5DF2\u8FC7\u671F\uFF0C\u70B9\u53F3\u4E0A\u89D2\u300C\u5237\u65B0\u300D\u91CD\u65B0\u751F\u6210";
270
+ const qrNote = () => expiresInMs > 0 ? `\u5FAE\u4FE1\u626B\u7801\u914D\u5BF9 ${secondsText(expiresInMs)}\u540E\u8FC7\u671F` : "\u914D\u5BF9\u7801\u5DF2\u8FC7\u671F \u70B9\u53F3\u4E0A\u89D2\u5237\u65B0\u91CD\u65B0\u751F\u6210";
267
271
  const header = () => {
268
272
  const head = doc.createElement("div");
269
273
  head.className = "drc-head";
@@ -281,7 +285,7 @@ function buildPill(root, deps) {
281
285
  const offlineNote = () => {
282
286
  if (!lastStatus) return null;
283
287
  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");
288
+ return text("p", "drc-note", "\u914D\u5BF9\u8FD8\u5728 \u5C0F\u7A0B\u5E8F\u56DE\u524D\u53F0\u4F1A\u81EA\u52A8\u91CD\u8FDE \u4E0D\u7528\u91CD\u65B0\u626B\u7801");
285
289
  };
286
290
  const infoRows = () => {
287
291
  if (!lastStatus) return [];
@@ -305,13 +309,14 @@ function buildPill(root, deps) {
305
309
  panel.replaceChildren();
306
310
  panel.appendChild(header());
307
311
  if (view.kind === "loading") {
308
- panel.appendChild(text("p", "drc-note", "\u6B63\u5728\u751F\u6210\u914D\u5BF9\u7801\u2026"));
312
+ panel.appendChild(text("p", "drc-note", "\u6B63\u5728\u751F\u6210\u914D\u5BF9\u7801"));
309
313
  } else if (view.kind === "qr") {
310
314
  const image = doc.createElement("img");
311
315
  image.className = "drc-qr";
312
316
  image.setAttribute("alt", "\u914D\u5BF9\u4E8C\u7EF4\u7801");
313
317
  image.src = view.imageSrc;
314
318
  panel.appendChild(image);
319
+ panel.appendChild(text("p", "drc-guide", GUIDE_LINE));
315
320
  panel.appendChild(text("p", "drc-code", view.token));
316
321
  panel.appendChild(text("p", "drc-note", qrNote()));
317
322
  } else if (view.kind === "unavailable" || view.kind === "failed") {
@@ -10538,6 +10538,22 @@ var init_mock_kernel = __esm({
10538
10538
  if (found.state === "archived") return { ok: false, message: "\u4F1A\u8BDD\u5DF2\u5F52\u6863\uFF0C\u9700\u5148\u6062\u590D" };
10539
10539
  this.running.add(sessionId);
10540
10540
  this.emit({ kind: "run-state", sessionId, state: "running" });
10541
+ this.emit({
10542
+ kind: "todo",
10543
+ sessionId,
10544
+ todos: [
10545
+ { content: "\u590D\u73B0\u7528\u6237\u62A5\u7684\u95EE\u9898", status: "completed" },
10546
+ { content: "\u6539\u5B8C\u8DD1\u4E00\u904D\u672C\u5730\u5168\u94FE\u8DEF", status: "pending" }
10547
+ ]
10548
+ });
10549
+ this.emit({
10550
+ kind: "todo",
10551
+ sessionId,
10552
+ todos: [
10553
+ { content: "\u590D\u73B0\u7528\u6237\u62A5\u7684\u95EE\u9898", status: "completed" },
10554
+ { content: "\u6539\u5B8C\u8DD1\u4E00\u904D\u672C\u5730\u5168\u94FE\u8DEF", status: "in_progress" }
10555
+ ]
10556
+ });
10541
10557
  if (this.options.autoApproval !== false && this.sink) {
10542
10558
  const decision = await this.sink.approval({ sessionId, action: "bash echo", reason: "mock \u5BA1\u6279" });
10543
10559
  this.emit({
@@ -10657,7 +10673,7 @@ var init_mock_kernel = __esm({
10657
10673
  // src/index.ts
10658
10674
  import path6 from "node:path";
10659
10675
 
10660
- // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/crypto/bytes.js
10676
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.4.0/node_modules/dsh-remote-wire/dist/src/crypto/bytes.js
10661
10677
  import { Buffer as Buffer2 } from "node:buffer";
10662
10678
  var encoder = new TextEncoder();
10663
10679
  var decoder = new TextDecoder("utf-8", { fatal: false });
@@ -10684,7 +10700,7 @@ function concat(parts) {
10684
10700
  return out;
10685
10701
  }
10686
10702
 
10687
- // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/crypto/keys.js
10703
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.4.0/node_modules/dsh-remote-wire/dist/src/crypto/keys.js
10688
10704
  import { createHash, randomBytes } from "node:crypto";
10689
10705
  var KDF_NAMESPACE = "dsh-rc/v1";
10690
10706
  var UNIT_SEPARATOR = 31;
@@ -10714,7 +10730,7 @@ function randomNonce() {
10714
10730
  return new Uint8Array(randomBytes(NONCE_BYTES));
10715
10731
  }
10716
10732
 
10717
- // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/crypto/record.js
10733
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.4.0/node_modules/dsh-remote-wire/dist/src/crypto/record.js
10718
10734
  var import_tweetnacl = __toESM(require_nacl_fast(), 1);
10719
10735
  var MAC_BYTES = 16;
10720
10736
  var MIN_RECORD_BYTES = NONCE_BYTES + MAC_BYTES;
@@ -10747,7 +10763,7 @@ function open(key, record2) {
10747
10763
  }
10748
10764
  }
10749
10765
 
10750
- // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/identity/pairing.js
10766
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.4.0/node_modules/dsh-remote-wire/dist/src/identity/pairing.js
10751
10767
  import { randomBytes as randomBytes2 } from "node:crypto";
10752
10768
  var URI_PREFIX = "dshr:";
10753
10769
  var TOKEN_RE = /^\d{6}$/;
@@ -30445,7 +30461,7 @@ function date4(params) {
30445
30461
  return _coercedDate(ZodDate, params);
30446
30462
  }
30447
30463
 
30448
- // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/wire/frames.js
30464
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.4.0/node_modules/dsh-remote-wire/dist/src/wire/frames.js
30449
30465
  var PAIRING_TOKEN_RE = /^\d{6}$/;
30450
30466
  var nonEmpty = external_exports.string().min(1);
30451
30467
  var pairingToken = external_exports.string().regex(PAIRING_TOKEN_RE);
@@ -30604,7 +30620,7 @@ function parseRelayFrameText(raw) {
30604
30620
  }
30605
30621
  }
30606
30622
 
30607
- // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/wire/payloads.js
30623
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.4.0/node_modules/dsh-remote-wire/dist/src/wire/payloads.js
30608
30624
  var SESSION_STATES = [
30609
30625
  "idle",
30610
30626
  "running",
@@ -30711,6 +30727,16 @@ var evRunState = external_exports.object({
30711
30727
  state: external_exports.enum(["running", "idle"]),
30712
30728
  detail: external_exports.string().optional()
30713
30729
  });
30730
+ var todoItem = external_exports.object({
30731
+ /** 待办正文。内核侧不限长,协议侧截到 200 字(面板高度有限,超长的部分看不见)。 */
30732
+ content: external_exports.string(),
30733
+ status: external_exports.enum(["pending", "in_progress", "completed"])
30734
+ });
30735
+ var evTodo = external_exports.object({
30736
+ t: external_exports.literal("ev.todo"),
30737
+ sessionId: nonEmpty2.optional(),
30738
+ todos: external_exports.array(todoItem)
30739
+ });
30714
30740
  var evKeepAwakeState = external_exports.object({
30715
30741
  t: external_exports.literal("ev.keep_awake_state"),
30716
30742
  enabled: external_exports.boolean(),
@@ -30762,6 +30788,7 @@ var evPayload = external_exports.discriminatedUnion("t", [
30762
30788
  evQuestionRequest,
30763
30789
  evQuestionResolved,
30764
30790
  evRunState,
30791
+ evTodo,
30765
30792
  evKeepAwakeState,
30766
30793
  evModel,
30767
30794
  evResult,
@@ -30871,13 +30898,14 @@ var PAYLOAD_TYPES = {
30871
30898
  evQuestionRequest: "ev.question_request",
30872
30899
  evQuestionResolved: "ev.question_resolved",
30873
30900
  evRunState: "ev.run_state",
30901
+ evTodo: "ev.todo",
30874
30902
  evKeepAwakeState: "ev.keep_awake_state",
30875
30903
  evModel: "ev.model",
30876
30904
  evResult: "ev.result",
30877
30905
  evSessionHistory: "ev.session_history"
30878
30906
  };
30879
30907
 
30880
- // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/wire/outbound.js
30908
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.4.0/node_modules/dsh-remote-wire/dist/src/wire/outbound.js
30881
30909
  function sessionChanged(sessions, reason) {
30882
30910
  return { t: PAYLOAD_TYPES.evSessionChanged, sessions, ...reason === void 0 ? {} : { reason } };
30883
30911
  }
@@ -30902,6 +30930,9 @@ function questionResolved(args) {
30902
30930
  function runState(args) {
30903
30931
  return { t: PAYLOAD_TYPES.evRunState, ...args };
30904
30932
  }
30933
+ function todoList(args) {
30934
+ return { t: PAYLOAD_TYPES.evTodo, ...args };
30935
+ }
30905
30936
  function keepAwakeState(args) {
30906
30937
  return { t: PAYLOAD_TYPES.evKeepAwakeState, ...args };
30907
30938
  }
@@ -30936,7 +30967,7 @@ function sessionHistory(args) {
30936
30967
  };
30937
30968
  }
30938
30969
 
30939
- // ../../node_modules/.pnpm/dsh-remote-wire@1.3.0/node_modules/dsh-remote-wire/dist/src/platform/sleep.js
30970
+ // ../../node_modules/.pnpm/dsh-remote-wire@1.4.0/node_modules/dsh-remote-wire/dist/src/platform/sleep.js
30940
30971
  function detectBackend(platform) {
30941
30972
  if (platform === "darwin")
30942
30973
  return "caffeinate";
@@ -31476,6 +31507,11 @@ var HostRuntime = class {
31476
31507
  if (event.state === "running") this.sleep.markActive();
31477
31508
  void this.pushSessions("run-state");
31478
31509
  return;
31510
+ case "todo":
31511
+ this.window.flushSession(event.sessionId);
31512
+ this.broadcast(todoList({ todos: event.todos, sessionId: event.sessionId }));
31513
+ this.sleep.markActive();
31514
+ return;
31479
31515
  case "title":
31480
31516
  void this.pushSessions("title");
31481
31517
  return;
@@ -32904,8 +32940,18 @@ function createServicesKernel(services, options) {
32904
32940
  return { ok: false, message: `sessionController.commands \u4E0A\u6CA1\u6709 create()\uFF08\u6210\u5458\uFF1A${shapeOf(commands)}\uFF09` };
32905
32941
  }
32906
32942
  try {
32943
+ let cwd = String(options.newSessionCwd ?? "").trim();
32944
+ if (!cwd) {
32945
+ try {
32946
+ const newest = (await listSessions(1))[0];
32947
+ cwd = String(newest?.summary?.workspace ?? "").trim();
32948
+ } catch {
32949
+ }
32950
+ }
32951
+ const request = cwd === "" ? {} : { cwd };
32952
+ if (cwd === "") log("new session without explicit cwd; host default applies", {});
32907
32953
  const made = await withTimeout(
32908
- Promise.resolve(create.call(commands, {})),
32954
+ Promise.resolve(create.call(commands, request)),
32909
32955
  options.createTimeoutMs ?? CREATE_TIMEOUT_DEFAULT
32910
32956
  );
32911
32957
  const id = typeof made?.sessionId === "string" ? made.sessionId : "";
@@ -33374,7 +33420,10 @@ var MAPPED_SESSION_EVENTS = /* @__PURE__ */ new Set([
33374
33420
  "turn/end",
33375
33421
  "session/title",
33376
33422
  "approval/asked",
33377
- "approval/decided"
33423
+ "approval/decided",
33424
+ // 内核的 todo/write 在真机的 eventTypes 里(status.json 的 unmappedEventTypes 曾一直记着它),
33425
+ // 但没人转发到手机——于是手机上一整轮"现在在干什么"都是空白。2026-10-05 补上。
33426
+ "todo/write"
33378
33427
  ]);
33379
33428
  function sessionEventKernelEvents(input2) {
33380
33429
  const { sessionId, type, data } = input2;
@@ -33421,6 +33470,21 @@ function sessionEventKernelEvents(input2) {
33421
33470
  }
33422
33471
  ];
33423
33472
  }
33473
+ case "todo/write": {
33474
+ const raw = Array.isArray(data.todos) ? data.todos : [];
33475
+ const todos = [];
33476
+ for (const item of raw) {
33477
+ if (todos.length >= 50) break;
33478
+ const content = typeof item?.content === "string" ? item.content.trim() : "";
33479
+ if (!content) continue;
33480
+ const status = item?.status;
33481
+ todos.push({
33482
+ content: content.slice(0, 200),
33483
+ status: status === "in_progress" || status === "completed" || status === "pending" ? status : "pending"
33484
+ });
33485
+ }
33486
+ return [{ kind: "todo", sessionId, todos }];
33487
+ }
33424
33488
  case "tool/result": {
33425
33489
  const message = data.message;
33426
33490
  const callId = data.callId ?? message?.toolCallId ?? message?.source?.callId ?? "";
@@ -33938,6 +34002,7 @@ var DEFAULT_CONFIG = {
33938
34002
  // 图片附件落盘:默认 status.json 同目录的 uploads/(见 `resolveUploadDir`)。
33939
34003
  uploadDir: "",
33940
34004
  maxImageBytes: 4 * 1024 * 1024,
34005
+ newSessionCwd: "",
33941
34006
  // 配对的唯一入口。关掉它 = 这台主机**没有**配对入口(`/drc pair` 与文本二维码都在
33942
34007
  // 2026-10-03 删掉了),所以 index.ts 会把它记成一条 warn 而不是安静地什么都不做。
33943
34008
  pill: { enabled: true }
@@ -33988,6 +34053,7 @@ function readConfig(injected, env = process.env) {
33988
34053
  if (fromEnv("DRC_PAIR_ON_START_SEC")) merged.pairOnStartSec = envSeconds(env.DRC_PAIR_ON_START_SEC);
33989
34054
  if (fromEnv("DRC_PILL")) merged.pill.enabled = envFlag(env.DRC_PILL, merged.pill.enabled);
33990
34055
  if (fromEnv("DRC_MOCK_BRIDGE") === "1") merged.mockBridge = true;
34056
+ if (fromEnv("DRC_NEW_SESSION_CWD")) merged.newSessionCwd = env.DRC_NEW_SESSION_CWD;
33991
34057
  if (merged.hostTokenEnv) merged.hostToken = env[merged.hostTokenEnv] ?? merged.hostToken;
33992
34058
  return merged;
33993
34059
  }
@@ -34382,7 +34448,7 @@ function startPill(ctx, deps) {
34382
34448
  }
34383
34449
 
34384
34450
  // src/version.ts
34385
- var PLUGIN_VERSION = true ? "2.0.1" : "dev";
34451
+ var PLUGIN_VERSION = true ? "2.0.2" : "dev";
34386
34452
 
34387
34453
  // src/index.ts
34388
34454
  var SERVICE_NAMES = [
@@ -34755,7 +34821,13 @@ function applyInner(ctx, injected) {
34755
34821
  return;
34756
34822
  }
34757
34823
  decided = true;
34758
- start(createServicesKernel(collected, { log, clock }));
34824
+ start(
34825
+ createServicesKernel(collected, {
34826
+ log,
34827
+ clock,
34828
+ newSessionCwd: config2.newSessionCwd
34829
+ })
34830
+ );
34759
34831
  return;
34760
34832
  }
34761
34833
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-remote-control",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
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.3.0",
56
+ "dsh-remote-wire": "^1.4.0",
57
57
  "qrcode": "^1.5.4",
58
58
  "tweetnacl": "^1.0.3",
59
59
  "ws": "^8.18.0",