@agent-wechat/cli 1.0.0-beta.1 → 1.0.0-beta.11

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @agent-wechat/cli
2
2
 
3
- `wx` is the native Rust client and foreground daemon. The npm package installs a matching release executable; it does not implement the native protocol in JavaScript. The supported distribution targets are Linux x64 and ARM64 with glibc 2.36+. macOS, Windows and musl do not have a native release. Clients on other systems can use the HTTP API of a supported Linux daemon. `wx --json version` and `/v1/status` expose the runtime and account capabilities; unsupported official schemas fail explicitly.
3
+ `wx` is the native Rust client and foreground daemon. The npm package installs a matching release executable; it does not implement the native protocol in JavaScript. Linux x64/ARM64 with glibc 2.36+ is validated. The release matrix also builds macOS ARM64 and native Windows x64/ARM64 candidates; native-machine account acceptance is pending. musl remains unsupported. Official-app profile takeover is Linux-only. Clients on other systems can use the HTTP API of a supported Linux daemon. `wx --json version` and `/v1/status` expose the runtime and account capabilities; unsupported official schemas fail explicitly.
4
4
 
5
5
  ## Installation
6
6
 
@@ -51,6 +51,21 @@ wx auth login --official=/absolute/official-profile
51
51
 
52
52
  The daemon binds the stored identity, attempts saved-device login and derives verified database keys after authentication. The official app stays closed while this daemon owns the files.
53
53
 
54
+ Recall an own message using its opaque message ID and exact decimal-string
55
+ server ID from history or a verified send receipt:
56
+
57
+ ```bash
58
+ wx --account example messages recall MESSAGE_ID --server-id SERVER_ID
59
+ wx --account example operations get OPERATION_ID
60
+ ```
61
+
62
+ Use `--detach` to return the durable operation immediately. A private receipt is
63
+ written before submission; the same target uses a stable default idempotency
64
+ key. An uncertain result exits8 and must be inspected rather than automatically
65
+ resubmitted. WeChat decides eligibility, and stopping the watcher does not cancel
66
+ the recall. Recalled messages remain as placeholders; their media is unavailable
67
+ through the daemon. Raw journals, cached files and backups are retained.
68
+
54
69
  Lower-level registration and experimental profile copies are also available:
55
70
 
56
71
  ```bash
@@ -81,10 +96,12 @@ python3 scripts/native-export-smoke.py artifacts/native/wx-vVERSION-linux-x64-gn
81
96
 
82
97
  Source-workspace postinstall defers release installation; run the Rust binary from its target directory or use an explicit trusted executable override. The local release builder tests the Rust workspace, verifies executable identity and creates versioned assets; it does not publish, deploy or log an account in. The package smoke test packs/unpacks an actual npm tarball and installs from local artifacts into a temporary directory.
83
98
 
84
- Deployment examples and the deliberate release preparation sequence are in [deploy/native](../../deploy/native/README.md). The previous container CLI and Wechaty packages are retired; see the [migration guide](../../docs/src/content/docs/getting-started/migration.mdx).
99
+ Deployment examples and the deliberate release preparation sequence are in [deploy/native](../../deploy/native/README.md). The previous container CLI and Wechaty packages are retired; see the [migration guide](https://thisnick.github.io/agent-wechat/setup/migrate/).
85
100
 
86
101
  ## Package helper scripts
87
102
 
88
103
  `native-install.mjs` runs during npm installation, or through `wx-native-install` for offline/disabled-script installs. It selects the host architecture, checks the npm-bundled hash and verifies executable identity before atomic installation. `native-launcher.mjs` only runs an already installed executable.
89
104
 
90
105
  `native-check-package.mjs` is the prepack/prepublish gate. It checks version/source/target/checksum metadata; before publication it additionally requires both architecture assets to exist and match the package's manifest. A local pack smoke can use one architecture fragment; publication requires the complete matrix. These helpers belong to the existing CLI package, not additional npm packages.
106
+
107
+ Candidate platform validation and remaining profile boundaries are tracked in [the acceptance checklist](../../dev-notes/native-platform-acceptance.md). Windows executables are named `wx.exe`; the npm launcher chooses the OS/CPU-specific asset. A native ARM64 Node installation selects the native Windows ARM64 executable.
@@ -80,3 +80,212 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
80
80
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
81
81
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
82
82
 
83
+
84
+ WXGF still-preview decoding
85
+
86
+ WXGF partition discovery uses the approach from sjzar/chatlog, `pkg/util/dat2img/wxgf.go`, commit a16b689f4e7120f3aa3a1bfb42853113101068f1 (Apache License 2.0). The Rust implementation adds bounded parsing, HEVC validation, process limits and still-preview semantics; it does not implement the upstream animation conversion. See LICENSE-CHATLOG.
87
+
88
+ Source: https://github.com/sjzar/chatlog/blob/a16b689f4e7120f3aa3a1bfb42853113101068f1/pkg/util/dat2img/wxgf.go
89
+
90
+ Apache License
91
+ Version 2.0, January 2004
92
+ http://www.apache.org/licenses/
93
+
94
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
95
+
96
+ 1. Definitions.
97
+
98
+ "License" shall mean the terms and conditions for use, reproduction,
99
+ and distribution as defined by Sections 1 through 9 of this document.
100
+
101
+ "Licensor" shall mean the copyright owner or entity authorized by
102
+ the copyright owner that is granting the License.
103
+
104
+ "Legal Entity" shall mean the union of the acting entity and all
105
+ other entities that control, are controlled by, or are under common
106
+ control with that entity. For the purposes of this definition,
107
+ "control" means (i) the power, direct or indirect, to cause the
108
+ direction or management of such entity, whether by contract or
109
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
110
+ outstanding shares, or (iii) beneficial ownership of such entity.
111
+
112
+ "You" (or "Your") shall mean an individual or Legal Entity
113
+ exercising permissions granted by this License.
114
+
115
+ "Source" form shall mean the preferred form for making modifications,
116
+ including but not limited to software source code, documentation
117
+ source, and configuration files.
118
+
119
+ "Object" form shall mean any form resulting from mechanical
120
+ transformation or translation of a Source form, including but
121
+ not limited to compiled object code, generated documentation,
122
+ and conversions to other media types.
123
+
124
+ "Work" shall mean the work of authorship, whether in Source or
125
+ Object form, made available under the License, as indicated by a
126
+ copyright notice that is included in or attached to the work
127
+ (an example is provided in the Appendix below).
128
+
129
+ "Derivative Works" shall mean any work, whether in Source or Object
130
+ form, that is based on (or derived from) the Work and for which the
131
+ editorial revisions, annotations, elaborations, or other modifications
132
+ represent, as a whole, an original work of authorship. For the purposes
133
+ of this License, Derivative Works shall not include works that remain
134
+ separable from, or merely link (or bind by name) to the interfaces of,
135
+ the Work and Derivative Works thereof.
136
+
137
+ "Contribution" shall mean any work of authorship, including
138
+ the original version of the Work and any modifications or additions
139
+ to that Work or Derivative Works thereof, that is intentionally
140
+ submitted to Licensor for inclusion in the Work by the copyright owner
141
+ or by an individual or Legal Entity authorized to submit on behalf of
142
+ the copyright owner. For the purposes of this definition, "submitted"
143
+ means any form of electronic, verbal, or written communication sent
144
+ to the Licensor or its representatives, including but not limited to
145
+ communication on electronic mailing lists, source code control systems,
146
+ and issue tracking systems that are managed by, or on behalf of, the
147
+ Licensor for the purpose of discussing and improving the Work, but
148
+ excluding communication that is conspicuously marked or otherwise
149
+ designated in writing by the copyright owner as "Not a Contribution."
150
+
151
+ "Contributor" shall mean Licensor and any individual or Legal Entity
152
+ on behalf of whom a Contribution has been received by Licensor and
153
+ subsequently incorporated within the Work.
154
+
155
+ 2. Grant of Copyright License. Subject to the terms and conditions of
156
+ this License, each Contributor hereby grants to You a perpetual,
157
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
158
+ copyright license to reproduce, prepare Derivative Works of,
159
+ publicly display, publicly perform, sublicense, and distribute the
160
+ Work and such Derivative Works in Source or Object form.
161
+
162
+ 3. Grant of Patent License. Subject to the terms and conditions of
163
+ this License, each Contributor hereby grants to You a perpetual,
164
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
165
+ (except as stated in this section) patent license to make, have made,
166
+ use, offer to sell, sell, import, and otherwise transfer the Work,
167
+ where such license applies only to those patent claims licensable
168
+ by such Contributor that are necessarily infringed by their
169
+ Contribution(s) alone or by combination of their Contribution(s)
170
+ with the Work to which such Contribution(s) was submitted. If You
171
+ institute patent litigation against any entity (including a
172
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
173
+ or a Contribution incorporated within the Work constitutes direct
174
+ or contributory patent infringement, then any patent licenses
175
+ granted to You under this License for that Work shall terminate
176
+ as of the date such litigation is filed.
177
+
178
+ 4. Redistribution. You may reproduce and distribute copies of the
179
+ Work or Derivative Works thereof in any medium, with or without
180
+ modifications, and in Source or Object form, provided that You
181
+ meet the following conditions:
182
+
183
+ (a) You must give any other recipients of the Work or
184
+ Derivative Works a copy of this License; and
185
+
186
+ (b) You must cause any modified files to carry prominent notices
187
+ stating that You changed the files; and
188
+
189
+ (c) You must retain, in the Source form of any Derivative Works
190
+ that You distribute, all copyright, patent, trademark, and
191
+ attribution notices from the Source form of the Work,
192
+ excluding those notices that do not pertain to any part of
193
+ the Derivative Works; and
194
+
195
+ (d) If the Work includes a "NOTICE" text file as part of its
196
+ distribution, then any Derivative Works that You distribute must
197
+ include a readable copy of the attribution notices contained
198
+ within such NOTICE file, excluding those notices that do not
199
+ pertain to any part of the Derivative Works, in at least one
200
+ of the following places: within a NOTICE text file distributed
201
+ as part of the Derivative Works; within the Source form or
202
+ documentation, if provided along with the Derivative Works; or,
203
+ within a display generated by the Derivative Works, if and
204
+ wherever such third-party notices normally appear. The contents
205
+ of the NOTICE file are for informational purposes only and
206
+ do not modify the License. You may add Your own attribution
207
+ notices within Derivative Works that You distribute, alongside
208
+ or as an addendum to the NOTICE text from the Work, provided
209
+ that such additional attribution notices cannot be construed
210
+ as modifying the License.
211
+
212
+ You may add Your own copyright statement to Your modifications and
213
+ may provide additional or different license terms and conditions
214
+ for use, reproduction, or distribution of Your modifications, or
215
+ for any such Derivative Works as a whole, provided Your use,
216
+ reproduction, and distribution of the Work otherwise complies with
217
+ the conditions stated in this License.
218
+
219
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
220
+ any Contribution intentionally submitted for inclusion in the Work
221
+ by You to the Licensor shall be under the terms and conditions of
222
+ this License, without any additional terms or conditions.
223
+ Notwithstanding the above, nothing herein shall supersede or modify
224
+ the terms of any separate license agreement you may have executed
225
+ with Licensor regarding such Contributions.
226
+
227
+ 6. Trademarks. This License does not grant permission to use the trade
228
+ names, trademarks, service marks, or product names of the Licensor,
229
+ except as required for reasonable and customary use in describing the
230
+ origin of the Work and reproducing the content of the NOTICE file.
231
+
232
+ 7. Disclaimer of Warranty. Unless required by applicable law or
233
+ agreed to in writing, Licensor provides the Work (and each
234
+ Contributor provides its Contributions) on an "AS IS" BASIS,
235
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
236
+ implied, including, without limitation, any warranties or conditions
237
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
238
+ PARTICULAR PURPOSE. You are solely responsible for determining the
239
+ appropriateness of using or redistributing the Work and assume any
240
+ risks associated with Your exercise of permissions under this License.
241
+
242
+ 8. Limitation of Liability. In no event and under no legal theory,
243
+ whether in tort (including negligence), contract, or otherwise,
244
+ unless required by applicable law (such as deliberate and grossly
245
+ negligent acts) or agreed to in writing, shall any Contributor be
246
+ liable to You for damages, including any direct, indirect, special,
247
+ incidental, or consequential damages of any character arising as a
248
+ result of this License or out of the use or inability to use the
249
+ Work (including but not limited to damages for loss of goodwill,
250
+ work stoppage, computer failure or malfunction, or any and all
251
+ other commercial damages or losses), even if such Contributor
252
+ has been advised of the possibility of such damages.
253
+
254
+ 9. Accepting Warranty or Additional Liability. While redistributing
255
+ the Work or Derivative Works thereof, You may choose to offer,
256
+ and charge a fee for, acceptance of support, warranty, indemnity,
257
+ or other liability obligations and/or rights consistent with this
258
+ License. However, in accepting such obligations, You may act only
259
+ on Your own behalf and on Your sole responsibility, not on behalf
260
+ of any other Contributor, and only if You agree to indemnify,
261
+ defend, and hold each Contributor harmless for any liability
262
+ incurred by, or claims asserted against, such Contributor by reason
263
+ of your accepting any such warranty or additional liability.
264
+
265
+ END OF TERMS AND CONDITIONS
266
+
267
+ APPENDIX: How to apply the Apache License to your work.
268
+
269
+ To apply the Apache License to your work, attach the following
270
+ boilerplate notice, with the fields enclosed by brackets "[]"
271
+ replaced with your own identifying information. (Don't include
272
+ the brackets!) The text should be enclosed in the appropriate
273
+ comment syntax for the file format. We also recommend that a
274
+ file or class name and description of purpose be included on the
275
+ same "printed page" as the copyright notice for easier
276
+ identification within third-party archives.
277
+
278
+ Copyright [yyyy] [name of copyright owner]
279
+
280
+ Licensed under the Apache License, Version 2.0 (the "License");
281
+ you may not use this file except in compliance with the License.
282
+ You may obtain a copy of the License at
283
+
284
+ http://www.apache.org/licenses/LICENSE-2.0
285
+
286
+ Unless required by applicable law or agreed to in writing, software
287
+ distributed under the License is distributed on an "AS IS" BASIS,
288
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
289
+ See the License for the specific language governing permissions and
290
+ limitations under the License.
291
+
@@ -1,29 +1,53 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.11",
4
4
  "repository": "thisnick/agent-wechat",
5
- "sourceCommit": "568f9beb0fb69d9faddea256f27861c3a49debf3",
5
+ "sourceCommit": "897ad87cf569f40e53994331e654045ae1c42d4b",
6
6
  "sourceTreeDirty": false,
7
7
  "assets": [
8
8
  {
9
- "name": "wx-v1.0.0-beta.1-linux-arm64-gnu",
9
+ "name": "wx-v1.0.0-beta.11-darwin-arm64",
10
+ "platform": "darwin",
11
+ "arch": "arm64",
12
+ "rustTarget": "aarch64-apple-darwin",
13
+ "bytes": 23787520,
14
+ "sha256": "04f6a4a79fcd9fae5e54185ea0d5806a1aefae35905a4c0ab86822f32e106071"
15
+ },
16
+ {
17
+ "name": "wx-v1.0.0-beta.11-linux-arm64-gnu",
10
18
  "platform": "linux",
11
19
  "arch": "arm64",
12
20
  "libc": "gnu",
13
21
  "minimumGlibc": "2.36",
14
22
  "rustTarget": "aarch64-unknown-linux-gnu",
15
- "bytes": 26298336,
16
- "sha256": "707853940eccdbb17ba3813c0da617e4f2c4e19afb5839c3a4a8b1c90aae8733"
23
+ "bytes": 27798784,
24
+ "sha256": "37a152891dce9d38fec3358e4a7f835668aeb2e9398cc010a81a0ca15b795e5d"
17
25
  },
18
26
  {
19
- "name": "wx-v1.0.0-beta.1-linux-x64-gnu",
27
+ "name": "wx-v1.0.0-beta.11-linux-x64-gnu",
20
28
  "platform": "linux",
21
29
  "arch": "x64",
22
30
  "libc": "gnu",
23
31
  "minimumGlibc": "2.36",
24
32
  "rustTarget": "x86_64-unknown-linux-gnu",
25
- "bytes": 27969496,
26
- "sha256": "d5c39f0c936ed43b05b1a47dd64c6cd670042d2b818a2824a3e683552ec2a786"
33
+ "bytes": 29524648,
34
+ "sha256": "f98a50d4f508abfa27c480c2ffa0859dc024d5aba84e6586c222b2c4e38ab75f"
35
+ },
36
+ {
37
+ "name": "wx-v1.0.0-beta.11-win32-arm64.exe",
38
+ "platform": "win32",
39
+ "arch": "arm64",
40
+ "rustTarget": "aarch64-pc-windows-msvc",
41
+ "bytes": 21442048,
42
+ "sha256": "990933a24c35e06e82c827f8cd28ac75131354d37ad84d85457bfc163c635e10"
43
+ },
44
+ {
45
+ "name": "wx-v1.0.0-beta.11-win32-x64.exe",
46
+ "platform": "win32",
47
+ "arch": "x64",
48
+ "rustTarget": "x86_64-pc-windows-msvc",
49
+ "bytes": 25890304,
50
+ "sha256": "025a4d5232f76666013f997e6b7f7976ae8e19cd0ffbd01107d967457c6d281d"
27
51
  }
28
52
  ]
29
53
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-wechat/cli",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.11",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "wx": "./scripts/native-launcher.mjs",
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // prepack checks bundled metadata; prepublishOnly additionally verifies the
3
3
  // complete, already published executable matrix before npm can be published.
4
- import { fetchBytes, manifestName, readManifest, releaseUrl, validateManifest, verifyBytes } from './native-runtime.mjs';
4
+ import { fetchBytes, targetKey, manifestName, readManifest, releaseUrl, validateManifest, verifyBytes } from './native-runtime.mjs';
5
5
  const { manifest, asset } = await readManifest();
6
6
  if (process.env.GITHUB_SHA && manifest.sourceCommit !== process.env.GITHUB_SHA) throw new Error('Native manifest source commit differs from publication commit');
7
7
  if (process.argv.includes('--publication')) {
@@ -13,7 +13,7 @@ if (process.argv.includes('--publication')) {
13
13
  validateManifest(remote, manifest.version, { requireAllTargets: true });
14
14
  if (remote.sourceCommit !== manifest.sourceCommit || remote.sourceTreeDirty === true) throw new Error('Prepared release differs from this npm package');
15
15
  for (const item of manifest.assets) {
16
- const published = remote.assets.find(candidate => candidate.arch === item.arch);
16
+ const published = remote.assets.find(candidate => targetKey(candidate) === targetKey(item));
17
17
  if (published.sha256 !== item.sha256 || published.bytes !== item.bytes) throw new Error('Prepared release differs from this npm package');
18
18
  verifyBytes(await fetchBytes(releaseUrl(manifest.version, item.name), item.bytes), item);
19
19
  }
@@ -12,20 +12,28 @@ export const releaseRepository = 'thisnick/agent-wechat';
12
12
  export const supportedTargets = Object.freeze([
13
13
  Object.freeze({ platform: 'linux', arch: 'x64', libc: 'gnu', minimumGlibc: '2.36', rustTarget: 'x86_64-unknown-linux-gnu' }),
14
14
  Object.freeze({ platform: 'linux', arch: 'arm64', libc: 'gnu', minimumGlibc: '2.36', rustTarget: 'aarch64-unknown-linux-gnu' }),
15
+ Object.freeze({ platform: 'darwin', arch: 'arm64', rustTarget: 'aarch64-apple-darwin' }),
16
+ Object.freeze({ platform: 'win32', arch: 'x64', rustTarget: 'x86_64-pc-windows-msvc' }),
17
+ Object.freeze({ platform: 'win32', arch: 'arm64', rustTarget: 'aarch64-pc-windows-msvc' }),
15
18
  ]);
16
19
  export const supportedTarget = supportedTargets[0];
17
20
  const maxBinaryBytes = 256 * 1024 * 1024;
18
21
  const allowedDownloadHosts = new Set(['github.com', 'release-assets.githubusercontent.com', 'objects.githubusercontent.com']);
19
22
 
23
+ export function targetKey(target) { return [target.platform, target.arch, target.libc].filter(Boolean).join('-'); }
24
+ export function executableName(target = targetFor()) { return target.platform === 'win32' ? 'wx.exe' : 'wx'; }
20
25
  export function targetFor({ platform = process.platform, arch = process.arch, glibc = process.report?.getReport().header.glibcVersionRuntime } = {}) {
21
- const [major, minor] = String(glibc ?? '').split('.').map(Number);
22
26
  const target = supportedTargets.find(item => item.platform === platform && item.arch === arch);
23
- if (!target || !glibc || !Number.isFinite(major) || !Number.isFinite(minor) || major < 2 || (major === 2 && minor < 36)) {
24
- throw new Error('Native wx requires Linux x64 or ARM64 with glibc 2.36 or newer. Install a supported native runtime or connect remotely to its API.');
27
+ if (!target) throw new Error(`Unsupported native wx platform: ${platform}-${arch}`);
28
+ if (platform === 'linux') {
29
+ const [major, minor] = String(glibc ?? '').split('.').map(Number);
30
+ if (!glibc || !Number.isFinite(major) || !Number.isFinite(minor) || major < 2 || (major === 2 && minor < 36)) {
31
+ throw new Error('Native wx on Linux requires glibc 2.36 or newer; musl is unsupported.');
32
+ }
25
33
  }
26
34
  return target;
27
35
  }
28
- export function assetName(version, target = targetFor()) { return `wx-v${version}-${target.platform}-${target.arch}-${target.libc}`; }
36
+ export function assetName(version, target = targetFor()) { return `wx-v${version}-${targetKey(target)}${target.platform === 'win32' ? '.exe' : ''}`; }
29
37
  export function manifestName(version) { return `wx-v${version}-checksums.json`; }
30
38
  export function releaseUrl(version, name) {
31
39
  return `https://github.com/${releaseRepository}/releases/download/v${version}/${name}`;
@@ -38,17 +46,17 @@ export function validateManifest(manifest, version, { target = targetFor(), requ
38
46
  }
39
47
  const seen = new Set();
40
48
  for (const asset of manifest.assets) {
41
- const expected = supportedTargets.find(item => item.arch === asset.arch);
42
- if (!expected || seen.has(asset.arch) || Object.entries(expected).some(([key, value]) => asset[key] !== value)
49
+ const expected = supportedTargets.find(item => targetKey(item) === targetKey(asset));
50
+ if (!expected || seen.has(targetKey(asset)) || Object.entries(expected).some(([key, value]) => asset[key] !== value)
43
51
  || asset.name !== assetName(version, expected) || !/^[a-f0-9]{64}$/.test(asset.sha256 ?? '')
44
52
  || !Number.isSafeInteger(asset.bytes) || asset.bytes < 1 || asset.bytes > maxBinaryBytes) {
45
53
  throw new Error('Invalid native release asset, duplicate or unsupported target');
46
54
  }
47
- seen.add(asset.arch);
55
+ seen.add(targetKey(asset));
48
56
  }
49
57
  if (requireAllTargets && seen.size !== supportedTargets.length) throw new Error('Native publication requires every supported target');
50
- const asset = manifest.assets.find(item => item.arch === target.arch);
51
- if (!asset) throw new Error(`Native manifest has no asset for ${target.arch}`);
58
+ const asset = manifest.assets.find(item => targetKey(item) === targetKey(target));
59
+ if (!asset) throw new Error(`Native manifest has no asset for ${targetKey(target)}`);
52
60
  return asset;
53
61
  }
54
62
  export async function readManifest(root = packageRoot) {
@@ -112,7 +120,7 @@ export async function inspectBinary(path, version) {
112
120
  }
113
121
  return value;
114
122
  }
115
- export function installPath(root, version, target = targetFor()) { return join(root, 'native', version, `${target.platform}-${target.arch}-${target.libc}`, 'wx'); }
123
+ export function installPath(root, version, target = targetFor()) { return join(root, 'native', version, targetKey(target), executableName(target)); }
116
124
  export async function installNative({ root = packageRoot, artifactDir, fetchOptions, downloadUrl, inspect = inspectBinary } = {}) {
117
125
  targetFor();
118
126
  const { version, asset } = await readManifest(root);
@@ -135,7 +143,7 @@ export async function installNative({ root = packageRoot, artifactDir, fetchOpti
135
143
  verifyBytes(bytes, asset);
136
144
  const destination = installPath(root, version);
137
145
  await mkdir(dirname(destination), { recursive: true, mode: 0o755 });
138
- const temporary = `${destination}.${process.pid}.${createHash('sha256').update(randomUUID()).digest('hex').slice(0, 16)}.tmp`;
146
+ const temporary = `${destination}.${process.pid}.${createHash('sha256').update(randomUUID()).digest('hex').slice(0, 16)}.tmp${process.platform === 'win32' ? '.exe' : ''}`;
139
147
  try {
140
148
  await writeFile(temporary, bytes, { flag: 'wx', mode: 0o755 });
141
149
  await chmod(temporary, 0o755);
@@ -166,7 +174,11 @@ export function runNative(path, args, { spawnImpl = spawn, processObject = proce
166
174
  const child = spawnImpl(path, args, { stdio: 'inherit', shell: false, windowsHide: true });
167
175
  const handlers = new Map();
168
176
  for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
169
- const handler = () => { if (!child.killed) child.kill(signal); };
177
+ const handler = () => {
178
+ // Windows broadcasts console Ctrl+C to both processes; Node's kill(SIGINT)
179
+ // would terminate the child before Rust can finish graceful shutdown.
180
+ if (!child.killed && !(process.platform === 'win32' && signal === 'SIGINT')) child.kill(signal);
181
+ };
170
182
  handlers.set(signal, handler);
171
183
  processObject.on(signal, handler);
172
184
  }