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.
@@ -5,6 +5,12 @@ import { createHash } from "node:crypto";
5
5
  import { gunzipSync } from "node:zlib";
6
6
 
7
7
  // src/update-policy.ts
8
+ var versionPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/;
9
+ function validVersion(value) {
10
+ if (typeof value !== "string" || value.length > 80) return false;
11
+ const match = versionPattern.exec(value);
12
+ return Boolean(match && !(match[4] || "").split(".").some((p) => /^0\d+$/.test(p)) && (!value.includes("+") || /^[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*$/.test(value.split("+")[1])));
13
+ }
8
14
  function trustedReleaseAsset(asset, version) {
9
15
  if (!asset || !/^[a-f0-9]{64}$/.test(asset.sha256) || !Number.isSafeInteger(asset.bytes) || asset.bytes < 1 || asset.bytes > 32 * 1024 * 1024) return false;
10
16
  try {
@@ -14,55 +20,119 @@ function trustedReleaseAsset(asset, version) {
14
20
  return false;
15
21
  }
16
22
  }
23
+ function trustedNpmInstaller(source) {
24
+ if (!source || !validVersion(source.version) || !/^[a-f0-9]{64}$/.test(source.sha256) || !Number.isSafeInteger(source.bytes) || source.bytes < 1 || source.bytes > 32 * 1024 * 1024) return false;
25
+ return source.url === `https://registry.npmjs.org/dsh-wechat-remote/-/dsh-wechat-remote-${source.version}.tgz`;
26
+ }
17
27
 
18
28
  // src/update-download.ts
19
- async function boundedFetch(url, maxBytes, fetcher = fetch) {
29
+ var DownloadUnavailableError = class extends Error {
30
+ constructor(message = "\u66F4\u65B0\u5305\u6682\u65F6\u65E0\u6CD5\u4E0B\u8F7D\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\uFF1B\u5F53\u524D\u63D2\u4EF6\u5C1A\u672A\u66FF\u6362", options) {
31
+ super(message, options);
32
+ this.name = "DownloadUnavailableError";
33
+ }
34
+ };
35
+ var githubHosts = ["github.com", "release-assets.githubusercontent.com", "objects.githubusercontent.com"];
36
+ async function fetchBytes(url, maxBytes, fetcher, hosts, deadline, options) {
20
37
  let next = url;
21
38
  for (let i = 0; i < 5; i++) {
39
+ options.signal?.throwIfAborted();
22
40
  const u = new URL(next);
23
- if (u.protocol !== "https:" || u.username || u.password || u.port || !["github.com", "release-assets.githubusercontent.com", "objects.githubusercontent.com", "relay.xyxfood.xyz"].includes(u.hostname)) throw new Error("\u66F4\u65B0\u4E0B\u8F7D\u6765\u6E90\u4E0D\u53D7\u4FE1\u4EFB");
24
- const res = await fetcher(next, { signal: AbortSignal.timeout(maxBytes <= 256 * 1024 ? 1e4 : 6e4), redirect: "manual" });
25
- if ([301, 302, 303, 307, 308].includes(res.status)) {
26
- await res.body?.cancel();
27
- const location = res.headers.get("location");
28
- if (!location) throw new Error("\u66F4\u65B0\u6765\u6E90\u91CD\u5B9A\u5411\u65E0\u6548");
29
- next = new URL(location, next).href;
30
- continue;
31
- }
32
- if (!res.ok || !res.body) {
33
- await res.body?.cancel();
34
- throw new Error("\u66F4\u65B0\u670D\u52A1\u6682\u4E0D\u53EF\u7528");
35
- }
36
- if (Number(res.headers.get("content-length")) > maxBytes) {
37
- await res.body.cancel();
38
- throw new Error("\u66F4\u65B0\u54CD\u5E94\u8D85\u8FC7\u5927\u5C0F\u9650\u5236");
39
- }
40
- const reader = res.body.getReader(), chunks = [];
41
- let size = 0;
41
+ if (u.protocol !== "https:" || u.username || u.password || u.port || !hosts.includes(u.hostname)) throw new Error("\u66F4\u65B0\u4E0B\u8F7D\u6765\u6E90\u4E0D\u53D7\u4FE1\u4EFB");
42
+ const remaining = deadline - Date.now();
43
+ if (remaining <= 0) throw new DownloadUnavailableError();
44
+ const controller = new AbortController();
45
+ const cancel = () => controller.abort(options.signal.reason);
46
+ options.signal?.addEventListener("abort", cancel, { once: true });
47
+ const totalTimer = setTimeout(() => controller.abort(new Error("\u66F4\u65B0\u4E0B\u8F7D\u8D85\u65F6")), remaining);
48
+ let activityTimer = setTimeout(() => controller.abort(new Error("\u66F4\u65B0\u6765\u6E90\u54CD\u5E94\u8D85\u65F6")), Math.min(remaining, options.responseTimeoutMs ?? 1e4));
49
+ let reader;
42
50
  try {
51
+ let res;
52
+ try {
53
+ res = await fetcher(next, { signal: controller.signal, redirect: "manual" });
54
+ } catch (cause) {
55
+ options.signal?.throwIfAborted();
56
+ throw new DownloadUnavailableError(void 0, { cause });
57
+ } finally {
58
+ clearTimeout(activityTimer);
59
+ }
60
+ if ([301, 302, 303, 307, 308].includes(res.status)) {
61
+ await res.body?.cancel();
62
+ const location = res.headers.get("location");
63
+ if (!location) throw new Error("\u66F4\u65B0\u6765\u6E90\u91CD\u5B9A\u5411\u65E0\u6548");
64
+ next = new URL(location, next).href;
65
+ continue;
66
+ }
67
+ if (!res.ok || !res.body) {
68
+ await res.body?.cancel();
69
+ throw new DownloadUnavailableError();
70
+ }
71
+ if (Number(res.headers.get("content-length")) > maxBytes) {
72
+ await res.body.cancel();
73
+ throw new Error("\u66F4\u65B0\u54CD\u5E94\u8D85\u8FC7\u5927\u5C0F\u9650\u5236");
74
+ }
75
+ reader = res.body.getReader();
76
+ activityTimer = setTimeout(() => controller.abort(new Error("\u66F4\u65B0\u4E0B\u8F7D\u957F\u65F6\u95F4\u6CA1\u6709\u8FDB\u5C55")), options.idleTimeoutMs ?? 15e3);
77
+ const chunks = [];
78
+ let size = 0;
43
79
  for (; ; ) {
44
- const { done, value } = await reader.read();
80
+ let part;
81
+ try {
82
+ part = await reader.read();
83
+ } catch (cause) {
84
+ options.signal?.throwIfAborted();
85
+ throw new DownloadUnavailableError(void 0, { cause });
86
+ }
87
+ const { done, value } = part;
45
88
  if (done) break;
46
89
  size += value.length;
47
90
  if (size > maxBytes) throw new Error("\u66F4\u65B0\u54CD\u5E94\u8D85\u8FC7\u5927\u5C0F\u9650\u5236");
48
91
  chunks.push(value);
92
+ if (value.length) activityTimer.refresh();
49
93
  }
94
+ options.signal?.throwIfAborted();
95
+ return Buffer.concat(chunks);
50
96
  } catch (error) {
51
- await reader.cancel();
97
+ try {
98
+ await reader?.cancel();
99
+ } catch {
100
+ }
52
101
  throw error;
102
+ } finally {
103
+ clearTimeout(totalTimer);
104
+ clearTimeout(activityTimer);
105
+ options.signal?.removeEventListener("abort", cancel);
106
+ reader?.releaseLock();
53
107
  }
54
- return Buffer.concat(chunks);
55
108
  }
56
109
  throw new Error("\u66F4\u65B0\u6765\u6E90\u91CD\u5B9A\u5411\u8FC7\u591A");
57
110
  }
58
- function auditArchive(archive, release) {
59
- if (!release.asset || archive.length !== release.asset.bytes || createHash("sha256").update(archive).digest("hex") !== release.asset.sha256) throw new Error("\u66F4\u65B0\u5305\u6821\u9A8C\u5931\u8D25\uFF0C\u672A\u4FEE\u6539\u5F53\u524D\u63D2\u4EF6");
111
+ async function boundedFetch(url, maxBytes, fetcher = fetch) {
112
+ return fetchBytes(
113
+ url,
114
+ maxBytes,
115
+ fetcher,
116
+ [...githubHosts, "relay.xyxfood.xyz"],
117
+ Date.now() + (maxBytes <= 256 * 1024 ? 1e4 : 6e4),
118
+ {}
119
+ );
120
+ }
121
+ function verifyBytes(archive, expected) {
122
+ if (!expected || archive.length !== expected.bytes || createHash("sha256").update(archive).digest("hex") !== expected.sha256) throw new Error("\u66F4\u65B0\u5305\u6821\u9A8C\u5931\u8D25\uFF0C\u672A\u4FEE\u6539\u5F53\u524D\u63D2\u4EF6");
123
+ }
124
+ function visitArchive(archive, visit) {
60
125
  const tar = gunzipSync(archive, { maxOutputLength: 64 * 1024 * 1024 });
61
- const seen = /* @__PURE__ */ new Set();
62
- let manifest, entries = 0;
126
+ if (tar.length % 512 !== 0) throw new Error("\u66F4\u65B0\u5305\u8BB0\u5F55\u4E0D\u5B8C\u6574");
127
+ const seen = /* @__PURE__ */ new Set(), files = /* @__PURE__ */ new Set();
128
+ let entries = 0, ended = false;
63
129
  for (let offset = 0; offset + 512 <= tar.length; ) {
64
130
  const header = tar.subarray(offset, offset + 512);
65
- if (header.every((b) => b === 0)) break;
131
+ if (header.every((b) => b === 0)) {
132
+ if (tar.subarray(offset).some((b) => b !== 0)) throw new Error("\u66F4\u65B0\u5305\u7ED3\u675F\u6807\u8BB0\u65E0\u6548");
133
+ ended = true;
134
+ break;
135
+ }
66
136
  if (++entries > 5e3) throw new Error("\u66F4\u65B0\u5305\u6587\u4EF6\u6570\u91CF\u5F02\u5E38");
67
137
  const field = (start, size2) => header.subarray(start, start + size2).toString("utf8").replace(/\0.*$/s, "");
68
138
  const name = (field(345, 155) ? field(345, 155) + "/" : "") + field(0, 100);
@@ -73,26 +143,82 @@ function auditArchive(archive, release) {
73
143
  const sumText = field(148, 8).trim();
74
144
  const sum = [...header].reduce((n, b, i) => n + (i >= 148 && i < 156 ? 32 : b), 0);
75
145
  if (!/^[0-7]+$/.test(sumText) || parseInt(sumText, 8) !== sum) throw new Error("\u66F4\u65B0\u5305\u5934\u6821\u9A8C\u5931\u8D25");
76
- if (!name.startsWith("package/") || /[\\:\x00-\x1f]/.test(name) || name.split("/").some((part) => part === ".." || part === ".") || name.split("/").some((part) => /[. ]$/.test(part) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part)) || !["0", "", "5"].includes(type) || seen.has(name.toLowerCase()) || !Number.isSafeInteger(size) || size < 0 || offset + 512 + size > tar.length) throw new Error("\u66F4\u65B0\u5305\u5305\u542B\u4E0D\u5B89\u5168\u7684\u8DEF\u5F84\u6216\u6587\u4EF6\u7C7B\u578B");
146
+ if (!name.startsWith("package/") || /[\\:\x00-\x1f]/.test(name) || name.split("/").some((part) => part === ".." || part === ".") || name.split("/").some((part) => /[. ]$/.test(part) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part)) || !["0", "", "5"].includes(type) || seen.has(name.toLowerCase()) || !Number.isSafeInteger(size) || size < 0 || type === "5" && size !== 0 || offset + 512 + Math.ceil(size / 512) * 512 > tar.length) throw new Error("\u66F4\u65B0\u5305\u5305\u542B\u4E0D\u5B89\u5168\u7684\u8DEF\u5F84\u6216\u6587\u4EF6\u7C7B\u578B");
77
147
  seen.add(name.toLowerCase());
78
- if (name === "package/package.json") {
79
- if (size > 65536) throw new Error("\u66F4\u65B0\u5305\u6E05\u5355\u8FC7\u5927");
80
- manifest = JSON.parse(tar.subarray(offset + 512, offset + 512 + size).toString("utf8"));
148
+ if (type !== "5") {
149
+ files.add(name);
150
+ visit(name, tar.subarray(offset + 512, offset + 512 + size));
81
151
  }
82
152
  offset += 512 + Math.ceil(size / 512) * 512;
83
153
  }
84
- if (!manifest || manifest.name !== "@harness-remote/dsh-wechat-remote" || manifest.version !== release.version || !seen.has("package/lib/index.js") || !seen.has("package/lib/client.js")) throw new Error("\u66F4\u65B0\u5305\u540D\u79F0\u3001\u7248\u672C\u6216\u5165\u53E3\u4E0D\u5339\u914D");
154
+ if (!ended) throw new Error("\u66F4\u65B0\u5305\u7F3A\u5C11\u7ED3\u675F\u6807\u8BB0");
155
+ return files;
156
+ }
157
+ function readManifest(data) {
158
+ if (data.length > 65536) throw new Error("\u66F4\u65B0\u5305\u6E05\u5355\u8FC7\u5927");
159
+ return JSON.parse(data.toString("utf8"));
160
+ }
161
+ function auditArchive(archive, release) {
162
+ verifyBytes(archive, release.asset);
163
+ let manifest;
164
+ const files = visitArchive(archive, (name, data) => {
165
+ if (name === "package/package.json") manifest = readManifest(data);
166
+ });
167
+ if (!manifest || manifest.name !== "@harness-remote/dsh-wechat-remote" || manifest.version !== release.version || !files.has("package/lib/index.js") || !files.has("package/lib/client.js")) throw new Error("\u66F4\u65B0\u5305\u540D\u79F0\u3001\u7248\u672C\u6216\u5165\u53E3\u4E0D\u5339\u914D");
85
168
  const scripts = manifest.scripts;
86
169
  if (["preinstall", "install", "postinstall", "prepare"].some((name) => scripts?.[name])) throw new Error("\u66F4\u65B0\u5305\u5305\u542B\u4E0D\u5141\u8BB8\u7684\u5B89\u88C5\u811A\u672C");
87
170
  }
88
- async function downloadRelease(release, fetcher = fetch) {
171
+ function pluginFromInstaller(archive, release) {
172
+ if (!trustedNpmInstaller(release.npmInstaller)) throw new Error("\u5907\u7528\u5B89\u88C5\u5305\u6765\u6E90\u4E0D\u53D7\u4FE1\u4EFB");
173
+ verifyBytes(archive, release.npmInstaller);
174
+ let manifest, plugin;
175
+ visitArchive(archive, (name, data) => {
176
+ if (name === "package/package.json") manifest = readManifest(data);
177
+ if (name === "package/assets/plugin.tgz") {
178
+ if (data.length !== release.asset?.bytes) throw new Error("\u5185\u7F6E\u63D2\u4EF6\u5927\u5C0F\u4E0D\u5339\u914D");
179
+ plugin = Buffer.from(data);
180
+ }
181
+ });
182
+ if (!manifest || manifest.name !== "dsh-wechat-remote" || manifest.version !== release.npmInstaller.version || !plugin) throw new Error("\u5907\u7528\u5B89\u88C5\u5305\u540D\u79F0\u3001\u7248\u672C\u6216\u5185\u7F6E\u63D2\u4EF6\u4E0D\u5339\u914D");
183
+ auditArchive(plugin, release);
184
+ return plugin;
185
+ }
186
+ async function downloadNpmRelease(release, fetcher = fetch, options = {}) {
187
+ if (!trustedReleaseAsset(release.asset, release.version) || !trustedNpmInstaller(release.npmInstaller)) throw new Error("\u5907\u7528\u5B89\u88C5\u5305\u6765\u6E90\u4E0D\u53D7\u4FE1\u4EFB");
188
+ const source = release.npmInstaller;
189
+ const body = await fetchBytes(source.url, source.bytes, fetcher, ["registry.npmjs.org"], Date.now() + (options.timeoutMs ?? 6e4), options);
190
+ options.signal?.throwIfAborted();
191
+ return pluginFromInstaller(body, release);
192
+ }
193
+ async function downloadRelease(release, fetcher = fetch, options = {}) {
89
194
  if (!trustedReleaseAsset(release.asset, release.version)) throw new Error("\u6682\u65E0\u53EF\u9A8C\u8BC1\u7684\u6B63\u5F0F\u66F4\u65B0\u5305");
90
- const body = await boundedFetch(release.asset.url, release.asset.bytes, fetcher);
195
+ if (release.npmInstaller !== void 0 && !trustedNpmInstaller(release.npmInstaller)) throw new Error("\u5907\u7528\u5B89\u88C5\u5305\u6765\u6E90\u4E0D\u53D7\u4FE1\u4EFB");
196
+ const deadline = Date.now() + (options.timeoutMs ?? 12e4);
197
+ let body;
198
+ try {
199
+ body = await fetchBytes(
200
+ release.asset.url,
201
+ release.asset.bytes,
202
+ fetcher,
203
+ githubHosts,
204
+ Math.min(deadline, Date.now() + 6e4),
205
+ options
206
+ );
207
+ } catch (error) {
208
+ options.signal?.throwIfAborted();
209
+ if (!(error instanceof DownloadUnavailableError) || !release.npmInstaller) throw error;
210
+ const remaining = deadline - Date.now();
211
+ if (remaining <= 0) throw error;
212
+ return downloadNpmRelease(release, fetcher, { ...options, timeoutMs: Math.min(remaining, 6e4) });
213
+ }
91
214
  auditArchive(body, release);
92
215
  return body;
93
216
  }
94
217
  export {
218
+ DownloadUnavailableError,
95
219
  auditArchive,
96
220
  boundedFetch,
97
- downloadRelease
221
+ downloadNpmRelease,
222
+ downloadRelease,
223
+ pluginFromInstaller
98
224
  };
@@ -35,13 +35,20 @@ function trustedReleaseAsset(asset, version) {
35
35
  return false;
36
36
  }
37
37
  }
38
+ function trustedNpmInstaller(source) {
39
+ if (!source || !validVersion(source.version) || !/^[a-f0-9]{64}$/.test(source.sha256) || !Number.isSafeInteger(source.bytes) || source.bytes < 1 || source.bytes > 32 * 1024 * 1024) return false;
40
+ return source.url === `https://registry.npmjs.org/dsh-wechat-remote/-/dsh-wechat-remote-${source.version}.tgz`;
41
+ }
38
42
  function validateCatalog(value) {
39
43
  const c = value;
40
44
  const strings = (v, max = 100) => Array.isArray(v) && v.length <= max && v.every((s) => typeof s === "string" && s.length > 0 && s.length <= 100);
41
45
  if (!c || c.schemaVersion !== 1 || typeof c.revision !== "string" || !/^[\w.-]{1,80}$/.test(c.revision) || !Number.isSafeInteger(c.issuedAt) || !Number.isSafeInteger(c.expiresAt) || c.expiresAt <= c.issuedAt || c.expiresAt - c.issuedAt > 32 * 864e5 || !Array.isArray(c.releases) || c.releases.length > 100 || !Array.isArray(c.blocked) || c.blocked.length > 100 || !strings(c.retiredDsh)) throw new Error("Invalid update catalog");
42
46
  const versions = /* @__PURE__ */ new Set();
43
47
  for (const r of c.releases) {
44
- if (!r || !validVersion(r.version) || versions.has(r.version) || !["stable", "preview"].includes(r.channel) || r.channel === "stable" && versionPattern.exec(r.version)[4] || !strings(r.dsh) || !r.dsh.length || !r.dsh.every(validVersion) || !strings(r.platforms, 3) || !r.platforms.length || !r.platforms.every((p) => ["windows", "macos", "linux"].includes(p)) || !strings(r.architectures, 32) || !r.architectures.length || !r.architectures.every((a) => /^[a-z0-9_-]{1,32}$/.test(a)) || r.asset && !trustedReleaseAsset(r.asset, r.version)) throw new Error("Invalid release entry");
48
+ if (!r || !validVersion(r.version) || versions.has(r.version) || !["stable", "preview"].includes(r.channel) || r.channel === "stable" && versionPattern.exec(r.version)[4] || !strings(r.dsh) || !r.dsh.every(validVersion) || !strings(r.platforms, 3) || !r.platforms.every((p) => ["windows", "macos", "linux"].includes(p)) || !strings(r.architectures, 32) || !r.architectures.every((a) => /^[a-z0-9_-]{1,32}$/.test(a)) || r.asset && !trustedReleaseAsset(r.asset, r.version) || r.npmInstaller !== void 0 && (!r.asset || !trustedNpmInstaller(r.npmInstaller))) throw new Error("Invalid release entry");
49
+ const hasEvidence = r.dsh.length > 0 && r.platforms.length > 0 && r.architectures.length > 0;
50
+ const pendingPreview = r.channel === "preview" && Boolean(versionPattern.exec(r.version)[4]) && r.dsh.length === 0 && r.platforms.length === 0 && r.architectures.length === 0;
51
+ if (!hasEvidence && !pendingPreview) throw new Error("Invalid release evidence");
45
52
  versions.add(r.version);
46
53
  if (r.targets !== void 0) {
47
54
  if (!Array.isArray(r.targets) || !r.targets.length || r.targets.length > 12) throw new Error("Invalid release targets");
@@ -141,6 +148,7 @@ export {
141
148
  assessUpdate,
142
149
  compareVersions,
143
150
  releaseMatches,
151
+ trustedNpmInstaller,
144
152
  trustedReleaseAsset,
145
153
  validateCatalog
146
154
  };
@@ -92,7 +92,8 @@ function backupProfile(profile, backup) {
92
92
  filter(source, target) {
93
93
  if (!fs.lstatSync(source).isSymbolicLink()) return true;
94
94
  const type = process.platform === "win32" ? fs.statSync(source, { throwIfNoEntry: false })?.isDirectory() ? "junction" : "file" : void 0;
95
- fs.symlinkSync(fs.readlinkSync(source), target, type);
95
+ const link = fs.readlinkSync(source);
96
+ fs.symlinkSync(type === "junction" ? path.resolve(path.dirname(source), link) : link, target, type);
96
97
  return false;
97
98
  }
98
99
  });
@@ -403,7 +404,7 @@ function durableSnapshot(job) {
403
404
  }
404
405
  if (fs4.existsSync(job.stateFile)) {
405
406
  const state = JSON.parse(fs4.readFileSync(job.stateFile, "utf8"));
406
- result["$binding"] = createHash2("sha256").update(JSON.stringify([state.token, state.wechatBindings])).digest("hex");
407
+ result["$binding"] = createHash2("sha256").update(JSON.stringify([state.token, state.publicIdentityNodeId])).digest("hex");
407
408
  }
408
409
  return result;
409
410
  }
@@ -563,6 +564,7 @@ async function executeUpdate(job, progress, quiesce) {
563
564
  const emit = (phase, n, message) => progress({ phase, progress: n, message, terminal: false });
564
565
  let stopped = false, disposed = false, modified = false, newChild;
565
566
  let before = {}, sessionIds = [], readableIds = [];
567
+ let preexistingFailure = false;
566
568
  try {
567
569
  emit("preparing", 25, "\u51C6\u5907\u5B89\u88C5\u5DE5\u5177\uFF0C\u5F53\u524D\u8282\u70B9\u4ECD\u53EF\u4F7F\u7528");
568
570
  const runtime = await pinInstallRuntime({ executable: job.executable, cli: job.pnpm, version: INSTALL_PNPM_VERSION }, job.directory);
@@ -571,6 +573,13 @@ async function executeUpdate(job, progress, quiesce) {
571
573
  emit("checking", 50, "\u786E\u8BA4\u4F1A\u8BDD\u7A7A\u95F2\u5E76\u4FDD\u5B58\u72B6\u6001");
572
574
  const old = job.controlOrigin ? await control(job, "describe") : await describe(job);
573
575
  if (old.pluginVersion !== job.previousVersion) throw new Error("\u5F53\u524D\u63D2\u4EF6\u5728\u68C0\u67E5\u540E\u53D1\u751F\u53D8\u5316");
576
+ if (job.controlOrigin && job.previousVersion !== "0.0.0") {
577
+ try {
578
+ preexistingFailure = (await control(job, "health")).ready !== true;
579
+ } catch {
580
+ preexistingFailure = true;
581
+ }
582
+ }
574
583
  const list = (await beforeRpc(job, "session.list")).items;
575
584
  if (!Array.isArray(list) || list.some((s) => s.running !== false)) throw new Error("\u8BF7\u7B49\u5F85\u5168\u90E8\u4F1A\u8BDD\u7ED3\u675F\u540E\u518D\u66F4\u65B0");
576
585
  sessionIds = list.map((s) => s.sessionId).sort();
@@ -642,7 +651,7 @@ async function executeUpdate(job, progress, quiesce) {
642
651
  fs4.renameSync(job.profile, path4.join(job.directory, "profile-failed"));
643
652
  fs4.renameSync(previous, job.profile);
644
653
  }
645
- if (job.previousVersion === "0.0.0") {
654
+ if (job.previousVersion === "0.0.0" || preexistingFailure) {
646
655
  const recovery = await import(pathToFileURL(path4.join(job.directory, "native-recovery.js")).href);
647
656
  await recovery.verifyNativeRestore(job, () => start(job), sessionIds, readableIds);
648
657
  } else {
@@ -663,7 +672,7 @@ async function executeUpdate(job, progress, quiesce) {
663
672
  return { phase: "attention", progress: 100, message: "\u81EA\u52A8\u6062\u590D\u672A\u5B8C\u6210\u3002\u5907\u4EFD\u5DF2\u4FDD\u7559\uFF0C\u8BF7\u6309\u4E3B\u673A\u66F4\u65B0\u8BB0\u5F55\u6062\u590D\uFF1B\u4E0D\u8981\u5220\u9664\u8282\u70B9\u6216\u6570\u636E\u3002", terminal: true, ok: false, rollback: false };
664
673
  }
665
674
  }
666
- return { phase: "failed", progress: 100, message: (error instanceof Error ? error.message : "\u66F4\u65B0\u5931\u8D25") + (rollback ? "\uFF1B\u5DF2\u6062\u590D\u539F\u63D2\u4EF6\u3002" : "\uFF1B\u5F53\u524D\u63D2\u4EF6\u672A\u66FF\u6362\u3002"), terminal: true, ok: false, rollback };
675
+ return { phase: "failed", progress: 100, message: (error instanceof Error ? error.message : "\u66F4\u65B0\u5931\u8D25") + (rollback ? preexistingFailure ? "\uFF1B\u5DF2\u6062\u590D\u5B89\u88C5\u524D\u72B6\u6001\uFF0C\u539F\u6709\u63D2\u4EF6\u6545\u969C\u5C1A\u672A\u4FEE\u590D\uFF0C\u6570\u636E\u5DF2\u4FDD\u7559\u3002" : "\uFF1B\u5DF2\u6062\u590D\u539F\u63D2\u4EF6\u3002" : "\uFF1B\u5F53\u524D\u63D2\u4EF6\u672A\u66FF\u6362\u3002"), terminal: true, ok: false, rollback };
667
676
  }
668
677
  }
669
678
  async function workerMain(filename) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-wechat-remote",
3
- "version": "1.7.8",
3
+ "version": "1.7.10",
4
4
  "private": false,
5
5
  "description": "Agent远程管理助手:DSH 微信连接插件的一键安装与升级",
6
6
  "type": "module",