@agent-wechat/cli 1.0.0-beta.8 → 1.0.0-beta.9
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 +18 -1
- package/native-release.json +32 -8
- package/package.json +1 -1
- package/scripts/native-check-package.mjs +2 -2
- package/scripts/native-runtime.mjs +24 -12
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.
|
|
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
|
|
@@ -88,3 +103,5 @@ Deployment examples and the deliberate release preparation sequence are in [depl
|
|
|
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.
|
package/native-release.json
CHANGED
|
@@ -1,29 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.9",
|
|
4
4
|
"repository": "thisnick/agent-wechat",
|
|
5
|
-
"sourceCommit": "
|
|
5
|
+
"sourceCommit": "6b2034a16697731d08b70d6051382469f630cc79",
|
|
6
6
|
"sourceTreeDirty": false,
|
|
7
7
|
"assets": [
|
|
8
8
|
{
|
|
9
|
-
"name": "wx-v1.0.0-beta.
|
|
9
|
+
"name": "wx-v1.0.0-beta.9-darwin-arm64",
|
|
10
|
+
"platform": "darwin",
|
|
11
|
+
"arch": "arm64",
|
|
12
|
+
"rustTarget": "aarch64-apple-darwin",
|
|
13
|
+
"bytes": 23752464,
|
|
14
|
+
"sha256": "9e0ceb7aad4d00ab027630f4457e2d2f4b696226987242643e0e9c14801a2abb"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "wx-v1.0.0-beta.9-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":
|
|
16
|
-
"sha256": "
|
|
23
|
+
"bytes": 27797968,
|
|
24
|
+
"sha256": "0e21f34c74a5dd6ebe8375f3ffb1c66fd5cdde48b67ac2e4033af8e74acc5e0f"
|
|
17
25
|
},
|
|
18
26
|
{
|
|
19
|
-
"name": "wx-v1.0.0-beta.
|
|
27
|
+
"name": "wx-v1.0.0-beta.9-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":
|
|
26
|
-
"sha256": "
|
|
33
|
+
"bytes": 29494920,
|
|
34
|
+
"sha256": "dad1fd67b3f9581455c01e2d932193864e9f7c9640fdf7bea5a3bd7cd69f5147"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "wx-v1.0.0-beta.9-win32-arm64.exe",
|
|
38
|
+
"platform": "win32",
|
|
39
|
+
"arch": "arm64",
|
|
40
|
+
"rustTarget": "aarch64-pc-windows-msvc",
|
|
41
|
+
"bytes": 21413888,
|
|
42
|
+
"sha256": "c23179ceb347e258ccf6229cbd2b92ab3eaffba6761f4a0532eafce9f1049be7"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "wx-v1.0.0-beta.9-win32-x64.exe",
|
|
46
|
+
"platform": "win32",
|
|
47
|
+
"arch": "x64",
|
|
48
|
+
"rustTarget": "x86_64-pc-windows-msvc",
|
|
49
|
+
"bytes": 25868800,
|
|
50
|
+
"sha256": "ffd236eff7e496c82b5009e3993d33a5ed04726a5aa37407f19b38870c4c8d96"
|
|
27
51
|
}
|
|
28
52
|
]
|
|
29
53
|
}
|
package/package.json
CHANGED
|
@@ -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
|
|
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
|
|
24
|
-
|
|
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
|
|
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
|
|
42
|
-
if (!expected || seen.has(asset
|
|
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
|
|
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
|
|
51
|
-
if (!asset) throw new Error(`Native manifest has no asset for ${target
|
|
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,
|
|
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 = () => {
|
|
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
|
}
|