dsh-wechat-remote 1.7.8 → 1.7.10

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.
@@ -1,8 +1,8 @@
1
1
  /* Generated from the shared plugin installation sources. */
2
2
 
3
3
  // src/install-control.ts
4
- import fs2 from "node:fs";
5
- import path5 from "node:path";
4
+ import fs3 from "node:fs";
5
+ import path6 from "node:path";
6
6
  import http from "node:http";
7
7
 
8
8
  // src/dsh-runtime.ts
@@ -617,6 +617,84 @@ function withPresentationProjections(block) {
617
617
  return { ...source, values };
618
618
  }
619
619
 
620
+ // src/dsh-host-contract.ts
621
+ import fs from "node:fs";
622
+ import path4 from "node:path";
623
+ var contracts = /* @__PURE__ */ new WeakMap();
624
+ function runningDshVersion(entry = process.argv[1]) {
625
+ if (!entry) return;
626
+ let directory;
627
+ try {
628
+ directory = path4.dirname(fs.realpathSync(entry));
629
+ } catch {
630
+ return;
631
+ }
632
+ for (let depth = 0; depth < 8; depth++) {
633
+ try {
634
+ const manifest = JSON.parse(fs.readFileSync(path4.join(directory, "package.json"), "utf8"));
635
+ if (manifest.name === "@deepseek-ai/dsh") return manifest.version;
636
+ } catch {
637
+ }
638
+ const parent = path4.dirname(directory);
639
+ if (parent === directory) break;
640
+ directory = parent;
641
+ }
642
+ }
643
+ function usesDuplexEvents(version) {
644
+ if (!version) return false;
645
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:-[\w.-]+)?$/.exec(version);
646
+ if (!match) throw new Error("\u65E0\u6CD5\u8BC6\u522B\u5F53\u524D DSH \u4E8B\u4EF6\u534F\u8BAE\u7248\u672C");
647
+ const [, major, minor, patch] = match.map(Number);
648
+ return major > 0 || minor > 1 || minor === 1 && patch >= 7;
649
+ }
650
+ function openHostEvents(gateway, endpoint, payload, signal) {
651
+ let contract = contracts.get(gateway);
652
+ if (!contract) {
653
+ contract = { duplex: usesDuplexEvents(runningDshVersion()) };
654
+ contracts.set(gateway, contract);
655
+ }
656
+ signal.throwIfAborted();
657
+ const empty = { async *[Symbol.asyncIterator]() {
658
+ } };
659
+ return contract.duplex ? gateway.wireStream.open(endpoint, payload, empty, void 0, signal) : gateway.wireStream.open(endpoint, payload, signal);
660
+ }
661
+ function projectedSubagentCatalog(parent, projection, rows) {
662
+ const entries = projection?.values?.subagentCatalog;
663
+ if (!Array.isArray(entries) || !Array.isArray(rows)) throw new Error("DSH \u5B50\u4EE3\u7406\u76EE\u5F55\u6682\u4E0D\u53EF\u8BFB\u53D6");
664
+ const byId = new Map(rows.map((row) => [row.sessionId, row]));
665
+ const parents = new Set(rows.filter((row) => row.origin === "subagent").map((row) => row.parentSessionId));
666
+ const ids = /* @__PURE__ */ new Set();
667
+ return { parentAvailable: byId.get(parent)?.agentAvailable === true, entries: entries.map((entry) => {
668
+ if (typeof entry.id !== "string" || !entry.id || ids.has(entry.id)) throw new Error("DSH \u5B50\u4EE3\u7406\u76EE\u5F55\u4E0D\u5B8C\u6574");
669
+ ids.add(entry.id);
670
+ if (entry.mode !== "one-shot" && entry.mode !== "continuable") return { id: entry.id, kind: "diagnostic", reason: "unsupported" };
671
+ const row = byId.get(entry.id);
672
+ if (row && (row.origin !== "subagent" || row.parentSessionId !== parent)) throw new Error("DSH \u5B50\u4EE3\u7406\u5F52\u5C5E\u4E0D\u4E00\u81F4");
673
+ return {
674
+ id: entry.id,
675
+ kind: "child",
676
+ mode: entry.mode,
677
+ ...typeof entry.label === "string" ? { label: entry.label } : {},
678
+ activity: row?.running === true ? "running" : "inactive",
679
+ hasChildren: parents.has(entry.id)
680
+ };
681
+ }) };
682
+ }
683
+ async function invokeHostRemote(ctx, gateway, request) {
684
+ if (request.namespace !== "subagents" || request.method !== "list") return gateway.invoke(request);
685
+ const registry = ctx.get("typert")?.local;
686
+ if (registry?.get("subagents/list") || registry?.hasSeen?.("subagents/list") || !usesDuplexEvents(runningDshVersion())) return gateway.invoke(request);
687
+ const parent = request.args.parentSessionId;
688
+ if (typeof parent !== "string" || !parent || parent.length > 256) throw new Error("\u7236\u4F1A\u8BDD\u6807\u8BC6\u65E0\u6548");
689
+ request.signal?.throwIfAborted();
690
+ const [projection, listing] = await Promise.all([
691
+ gateway.invoke({ namespace: "session", method: "projections", args: { request: { sessionId: parent } }, signal: request.signal }),
692
+ gateway.invoke({ namespace: "session", method: "list", args: { _request: {} }, signal: request.signal })
693
+ ]);
694
+ request.signal?.throwIfAborted();
695
+ return projectedSubagentCatalog(parent, projection, listing?.items);
696
+ }
697
+
620
698
  // src/dsh-protocol-compat.ts
621
699
  var SINGLE_REQUEST_METHODS = /* @__PURE__ */ new Set([
622
700
  "session.attachment",
@@ -817,8 +895,8 @@ function resolveTypertGateway(ctx) {
817
895
  const candidate = ctx.get("typertGateway");
818
896
  if (!candidate || typeof candidate.invoke !== "function" || typeof candidate.stream !== "function") return null;
819
897
  return {
820
- wireStream: candidate.wireStream,
821
- invoke: (request) => candidate.invoke(request),
898
+ wireStream: candidate.wireStream ? { open: (endpoint, payload, signal) => openHostEvents(candidate, endpoint, payload, signal) } : void 0,
899
+ invoke: (request) => invokeHostRemote(ctx, candidate, request),
822
900
  stream: (request) => candidate.stream(request),
823
901
  commandAttachmentField: () => {
824
902
  const registry = ctx.get("typert");
@@ -912,41 +990,65 @@ async function historyValue(gateway, request, signal) {
912
990
  const sessionId = typeof request.sessionId === "string" ? request.sessionId : "";
913
991
  if (!sessionId) throw new Error("session.history requires sessionId");
914
992
  const maxMessages = Number.isSafeInteger(request.maxMessages) ? Math.max(1, Math.min(30, Number(request.maxMessages))) : 8;
915
- const address = await resolveDshSessionAddress(gateway, sessionId, signal);
916
- const first = recordOf(await firstStreamFrame(
917
- gateway,
918
- "session",
919
- "follow",
920
- { request: { address, maxMessages } },
921
- signal
922
- ));
923
- if (first?.type !== "snapshot" || !Number.isSafeInteger(first.cursor)) {
924
- throw new Error("session/follow returned an invalid opening snapshot");
925
- }
926
- if (Number.isSafeInteger(request.beforeSeq)) {
993
+ return createHistoryPageReader(gateway, sessionId, signal)({
994
+ maxMessages,
995
+ ...Number.isSafeInteger(request.beforeSeq) ? { beforeSeq: Number(request.beforeSeq) } : {}
996
+ });
997
+ }
998
+ function createHistoryPageReader(gateway, sessionId, signal) {
999
+ let address;
1000
+ let opening;
1001
+ const readPage = async (request, throughSeq) => {
1002
+ const resolved = await (address ??= resolveDshSessionAddress(gateway, sessionId, signal));
1003
+ signal.throwIfAborted();
927
1004
  const page = recordOf(await gateway.invoke({
928
1005
  namespace: "session",
929
1006
  method: "page",
930
- args: {
931
- request: {
932
- address,
933
- throughSeq: first.cursor,
934
- beforeSeq: request.beforeSeq,
935
- maxMessages
936
- }
937
- },
1007
+ args: { request: { address: resolved, throughSeq, beforeSeq: request.beforeSeq, maxMessages: request.maxMessages } },
938
1008
  signal
939
1009
  })) ?? {};
1010
+ signal.throwIfAborted();
1011
+ return { events: historyEvents(page.records), hasMore: page.hasMore === true };
1012
+ };
1013
+ return async (request) => {
1014
+ signal.throwIfAborted();
1015
+ if (!opening && request.beforeSeq !== void 0) {
1016
+ return readPage(request, request.beforeSeq - 1);
1017
+ }
1018
+ if (!opening) {
1019
+ opening = (async () => {
1020
+ const resolved = await (address ??= resolveDshSessionAddress(gateway, sessionId, signal));
1021
+ signal.throwIfAborted();
1022
+ const maxMessages2 = request.maxMessages;
1023
+ const first2 = recordOf(await firstStreamFrame(gateway, "session", "follow", {
1024
+ request: { address: resolved, maxMessages: maxMessages2 }
1025
+ }, signal));
1026
+ signal.throwIfAborted();
1027
+ if (first2?.type !== "snapshot" || !Number.isSafeInteger(first2.cursor) || Number(first2.cursor) < -1) {
1028
+ throw new Error("session/follow returned an invalid opening snapshot");
1029
+ }
1030
+ return { first: first2, maxMessages: maxMessages2 };
1031
+ })();
1032
+ }
1033
+ const { first, maxMessages } = await opening;
1034
+ signal.throwIfAborted();
1035
+ const latest = request.beforeSeq === void 0;
1036
+ if (latest && request.maxMessages === maxMessages) {
1037
+ return {
1038
+ events: historyEvents(first.records),
1039
+ hasMore: first.hasMore === true,
1040
+ projections: withPresentationProjections(first.projections),
1041
+ historyEndSeq: first.cursor
1042
+ };
1043
+ }
1044
+ const page = await readPage(request, Number(first.cursor));
940
1045
  return {
941
- events: historyEvents(page.records),
942
- hasMore: page.hasMore === true
1046
+ ...page,
1047
+ ...latest ? {
1048
+ projections: withPresentationProjections(first.projections),
1049
+ historyEndSeq: first.cursor
1050
+ } : {}
943
1051
  };
944
- }
945
- return {
946
- events: historyEvents(first.records),
947
- hasMore: first.hasMore === true,
948
- projections: withPresentationProjections(first.projections),
949
- historyEndSeq: first.cursor
950
1052
  };
951
1053
  }
952
1054
  async function workspaceValue(gateway, signal) {
@@ -1050,8 +1152,8 @@ async function invokeLegacyRpc(gateway, request, options) {
1050
1152
  }
1051
1153
 
1052
1154
  // src/install-lifecycle.ts
1053
- import fs from "node:fs";
1054
- import path4 from "node:path";
1155
+ import fs2 from "node:fs";
1156
+ import path5 from "node:path";
1055
1157
  import { execFileSync as execFileSync2, spawn } from "node:child_process";
1056
1158
  var serviceName = (s) => /^[A-Za-z0-9_.@-]{1,180}$/.test(s);
1057
1159
  function run(command, args) {
@@ -1060,13 +1162,13 @@ function run(command, args) {
1060
1162
  function validateManager(value) {
1061
1163
  if (value.kind === "process") return;
1062
1164
  if (value.kind === "systemd" && process.platform === "linux" && serviceName(value.unit) && value.unit.endsWith(".service")) return;
1063
- if (value.kind === "launchd" && process.platform === "darwin" && serviceName(value.label) && /^gui\/\d+$/.test(value.domain) && value.domain === `gui/${process.getuid?.()}` && path4.isAbsolute(value.plist) && fs.statSync(value.plist).isFile()) return;
1165
+ if (value.kind === "launchd" && process.platform === "darwin" && serviceName(value.label) && /^gui\/\d+$/.test(value.domain) && value.domain === `gui/${process.getuid?.()}` && path5.isAbsolute(value.plist) && fs2.statSync(value.plist).isFile()) return;
1064
1166
  throw new Error("\u65E0\u6CD5\u786E\u8BA4\u539F\u540E\u53F0\u670D\u52A1\uFF0C\u672A\u505C\u6B62 DSH\u3002");
1065
1167
  }
1066
1168
  function currentHostManager() {
1067
1169
  if (process.env.PM2_HOME || process.env.NODE_APP_INSTANCE || process.env.KUBERNETES_SERVICE_HOST || process.env.container) throw new Error("\u6B64\u540E\u53F0\u7BA1\u7406\u65B9\u5F0F\u5C1A\u4E0D\u652F\u6301\u81EA\u52A8\u91CD\u542F\u3002");
1068
1170
  if (process.platform === "linux") {
1069
- const group = fs.readFileSync("/proc/self/cgroup", "utf8");
1171
+ const group = fs2.readFileSync("/proc/self/cgroup", "utf8");
1070
1172
  const units = group.split(/[\n/]/).filter((s) => serviceName(s) && s.endsWith(".service"));
1071
1173
  const unit = units.at(-1);
1072
1174
  if (unit && !/^user@\d+\.service$/.test(unit)) {
@@ -1095,17 +1197,17 @@ function currentHostManager() {
1095
1197
  }
1096
1198
  function startUpdateWorker(manager, directory, executable) {
1097
1199
  validateManager(manager);
1098
- const id = path4.basename(directory);
1200
+ const id = path5.basename(directory);
1099
1201
  if (!/^[a-f0-9]{32}$/.test(id)) throw new Error("\u65E0\u6548\u7684\u66F4\u65B0\u4EFB\u52A1");
1100
- const args = [path4.join(directory, "update-worker.js"), path4.join(directory, "job.json")];
1202
+ const args = [path5.join(directory, "update-worker.js"), path5.join(directory, "job.json")];
1101
1203
  if (manager.kind === "systemd") {
1102
1204
  run("systemd-run", ["--user", "--quiet", "--collect", `--unit=dsh-wechat-update-${id}`, "--property=Type=exec", `--working-directory=${directory}`, executable, ...args]);
1103
1205
  } else if (manager.kind === "launchd") {
1104
- run("/bin/launchctl", ["submit", "-l", `dsh.wechat.update.${id}`, "-o", path4.join(directory, "worker.log"), "-e", path4.join(directory, "worker.log"), "--", executable, ...args]);
1206
+ run("/bin/launchctl", ["submit", "-l", `dsh.wechat.update.${id}`, "-o", path5.join(directory, "worker.log"), "-e", path5.join(directory, "worker.log"), "--", executable, ...args]);
1105
1207
  } else {
1106
- const log = fs.openSync(path4.join(directory, "worker.log"), "a", 384);
1208
+ const log = fs2.openSync(path5.join(directory, "worker.log"), "a", 384);
1107
1209
  const child = spawn(executable, args, { cwd: directory, env: process.env, windowsHide: true, detached: true, stdio: ["ignore", log, log] });
1108
- fs.closeSync(log);
1210
+ fs2.closeSync(log);
1109
1211
  child.unref();
1110
1212
  child.on("error", () => {
1111
1213
  });
@@ -1133,6 +1235,17 @@ function assertNativeUpdateCapabilities(context) {
1133
1235
  }
1134
1236
 
1135
1237
  // src/install-control.ts
1238
+ async function nativeInstallSessions(ctx, signal) {
1239
+ const controller = ctx.get("sessionController");
1240
+ if (typeof controller?.list !== "function") throw new Error("DSH \u539F\u751F\u4F1A\u8BDD\u68C0\u67E5\u4E0D\u53EF\u7528\uFF0C\u672A\u505C\u6B62\u8282\u70B9\u3002");
1241
+ signal.throwIfAborted();
1242
+ const value = await controller.list({}, signal);
1243
+ signal.throwIfAborted();
1244
+ if (!Array.isArray(value?.items) || value.items.some((s) => typeof s.sessionId !== "string" || !s.sessionId || typeof s.running !== "boolean") || new Set(value.items.map((s) => s.sessionId)).size !== value.items.length) {
1245
+ throw new Error("DSH \u539F\u751F\u4F1A\u8BDD\u72B6\u6001\u4E0D\u5B8C\u6574\uFF0C\u672A\u505C\u6B62\u8282\u70B9\u3002");
1246
+ }
1247
+ return value;
1248
+ }
1136
1249
  async function quiesceNativeHost(ctx, read, disposing) {
1137
1250
  const items = (await read("session.list")).items;
1138
1251
  if (!Array.isArray(items) || items.some((s) => s.running !== false)) throw new Error("\u8BF7\u7B49\u5F85\u8FD0\u884C\u4E2D\u7684\u4F1A\u8BDD\u7ED3\u675F\u540E\u518D\u66F4\u65B0\u3002");
@@ -1146,11 +1259,11 @@ async function quiesceNativeHost(ctx, read, disposing) {
1146
1259
  async function createInstallControl(context, config) {
1147
1260
  const ctx = context.root;
1148
1261
  assertNativeUpdateCapabilities(ctx);
1149
- const home = adapterDshHome(), id = path5.basename(config.directory);
1150
- if (!/^[a-f0-9]{32}$/.test(id) || !/^[a-f0-9]{48}$/.test(config.token) || path5.dirname(config.directory) !== path5.join(home, "harness-remote-updates") || fs2.realpathSync(config.directory) !== config.directory) throw new Error("\u5B89\u88C5\u63A7\u5236\u8BF7\u6C42\u4E0D\u5C5E\u4E8E\u5F53\u524D DSH\u3002");
1151
- const scope = resolveAgentProfileScope("", process.argv, home), profile = path5.join(home, "profiles", scope);
1152
- const cli = fs2.realpathSync(process.argv[1]);
1153
- const manifest = JSON.parse(fs2.readFileSync(path5.resolve(cli, "../../package.json"), "utf8"));
1262
+ const home = adapterDshHome(), id = path6.basename(config.directory);
1263
+ if (!/^[a-f0-9]{32}$/.test(id) || !/^[a-f0-9]{48}$/.test(config.token) || path6.dirname(config.directory) !== path6.join(home, "harness-remote-updates") || fs3.realpathSync(config.directory) !== config.directory) throw new Error("\u5B89\u88C5\u63A7\u5236\u8BF7\u6C42\u4E0D\u5C5E\u4E8E\u5F53\u524D DSH\u3002");
1264
+ const scope = resolveAgentProfileScope("", process.argv, home), profile = path6.join(home, "profiles", scope);
1265
+ const cli = fs3.realpathSync(process.argv[1]);
1266
+ const manifest = JSON.parse(fs3.readFileSync(path6.resolve(cli, "../../package.json"), "utf8"));
1154
1267
  if (manifest.name !== "@deepseek-ai/dsh" || process.execArgv.length) throw new Error("\u6B64 DSH \u542F\u52A8\u65B9\u5F0F\u5C1A\u4E0D\u652F\u6301\u81EA\u52A8\u66F4\u65B0\u3002");
1155
1268
  const manager = currentHostManager(), webPort = resolveDshWebRuntime(ctx, process.env).port;
1156
1269
  const ports = deriveGatePorts(scope, loadAgentDescriptor().agentInstanceId);
@@ -1159,6 +1272,9 @@ async function createInstallControl(context, config) {
1159
1272
  let launched = false, quiesced = false;
1160
1273
  const read = async (method, payload = {}) => {
1161
1274
  if (!["session.list", "session.history"].includes(method)) throw new Error("\u4E0D\u652F\u6301\u7684\u5B89\u88C5\u68C0\u67E5");
1275
+ if (method === "session.list" && typeof ctx.get("sessionController")?.list === "function") {
1276
+ return nativeInstallSessions(ctx, AbortSignal.timeout(1e4));
1277
+ }
1162
1278
  const request = { type: "client-request", rpcId: "installer-read", method, payload };
1163
1279
  const response = gateway ? await invokeLegacyRpc(gateway, request, { signal: AbortSignal.timeout(1e4), describeHost: () => ({}) }) : await (await fetch(`http://127.0.0.1:${webPort}/api/${method}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(request), signal: AbortSignal.timeout(1e4), redirect: "error" })).json();
1164
1280
  if (!response.result?.ok) throw new Error("\u65E0\u6CD5\u9A8C\u8BC1 DSH \u4F1A\u8BDD\u72B6\u6001\uFF0C\u672A\u505C\u6B62\u8282\u70B9\u3002");
@@ -1166,7 +1282,7 @@ async function createInstallControl(context, config) {
1166
1282
  };
1167
1283
  const version = () => {
1168
1284
  try {
1169
- return JSON.parse(fs2.readFileSync(path5.join(profile, "node_modules/@harness-remote/dsh-wechat-remote/package.json"), "utf8")).version;
1285
+ return JSON.parse(fs3.readFileSync(path6.join(profile, "node_modules/@harness-remote/dsh-wechat-remote/package.json"), "utf8")).version;
1170
1286
  } catch {
1171
1287
  return "0.0.0";
1172
1288
  }
@@ -1206,9 +1322,14 @@ async function createInstallControl(context, config) {
1206
1322
  quiesced
1207
1323
  });
1208
1324
  if (req.url === "/read" && !quiesced) return json(200, await read(input.method, input.payload));
1325
+ if (req.url === "/health" && !quiesced) {
1326
+ if (!gateway) throw new Error("DSH \u4E1A\u52A1\u63A5\u53E3\u5C1A\u672A\u5C31\u7EEA");
1327
+ await gateway.invoke({ namespace: "session", method: "list", args: { _request: {} }, signal: AbortSignal.timeout(1e4) });
1328
+ return json(200, { ready: true });
1329
+ }
1209
1330
  if (req.url === "/launch" && !launched && !quiesced) {
1210
- const filename = path5.join(config.directory, "job.json");
1211
- const job = JSON.parse(fs2.readFileSync(filename, "utf8"));
1331
+ const filename = path6.join(config.directory, "job.json");
1332
+ const job = JSON.parse(fs3.readFileSync(filename, "utf8"));
1212
1333
  if (job.id !== id || job.controlOrigin !== origin || job.statusToken !== config.token || job.parentPid !== process.pid || job.home !== home || job.profile !== profile || job.cli !== cli || job.pnpm !== config.pnpm) throw new Error("\u5B89\u88C5\u76EE\u6807\u53D1\u751F\u53D8\u5316");
1213
1334
  startUpdateWorker(manager, config.directory, process.execPath);
1214
1335
  launched = true;
@@ -1253,7 +1374,7 @@ async function createInstallControl(context, config) {
1253
1374
  server.closeAllConnections();
1254
1375
  server.close();
1255
1376
  }
1256
- writePrivateJsonAtomic(path5.join(config.directory, "control-ready.json"), { origin, pid: process.pid });
1377
+ writePrivateJsonAtomic(path6.join(config.directory, "control-ready.json"), { origin, pid: process.pid });
1257
1378
  return { origin, close };
1258
1379
  }
1259
1380
  var apply = async (ctx, config) => {
@@ -1264,5 +1385,6 @@ export {
1264
1385
  apply,
1265
1386
  createInstallControl,
1266
1387
  inject,
1388
+ nativeInstallSessions,
1267
1389
  quiesceNativeHost
1268
1390
  };
@@ -19,7 +19,8 @@ function backupProfile(profile, backup) {
19
19
  filter(source, target) {
20
20
  if (!fs.lstatSync(source).isSymbolicLink()) return true;
21
21
  const type = process.platform === "win32" ? fs.statSync(source, { throwIfNoEntry: false })?.isDirectory() ? "junction" : "file" : void 0;
22
- fs.symlinkSync(fs.readlinkSync(source), target, type);
22
+ const link = fs.readlinkSync(source);
23
+ fs.symlinkSync(type === "junction" ? path.resolve(path.dirname(source), link) : link, target, type);
23
24
  return false;
24
25
  }
25
26
  });