Skip to content

Commit a7f58db

Browse files
committed
fix(postinstall): use system tar instead of buggy custom parser
1 parent 4b5058a commit a7f58db

1 file changed

Lines changed: 159 additions & 19 deletions

File tree

scripts/postinstall.js

Lines changed: 159 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,177 @@
11
#!/usr/bin/env node
22
/**
3-
* Postinstall script: triggers the nuwax-codex JS launcher so it downloads
4-
* the native binary from Alibaba Cloud OSS at install time. The launcher's
5-
* `ensureBinary()` call caches the binary under `~/.nuwax-codex-cache/`.
6-
*
7-
* After this completes, calls like `nuwax-codex-acp-ts` (which internally
8-
* spawn `nuwax-codex app-server`) will be instant — no lazy download on
3+
* Postinstall script: pre-downloads the native `nuwax-codex` binary from
4+
* Alibaba Cloud OSS at install time. After this completes, the ACP bridge
5+
* can spawn `nuwax-codex app-server` instantly without a lazy download on
96
* first use.
107
*/
118

12-
import { spawnSync } from "node:child_process";
139
import { createRequire } from "node:module";
10+
import { execSync } from "node:child_process";
11+
import {
12+
createWriteStream,
13+
existsSync,
14+
mkdirSync,
15+
chmodSync,
16+
unlinkSync,
17+
} from "node:fs";
18+
import { homedir } from "node:os";
19+
import { join, dirname } from "node:path";
1420

1521
const require = createRequire(import.meta.url);
1622

17-
let launcherPath;
23+
// Resolve nuwax-codex package version — the binary version must match.
24+
let CODEX_VERSION;
1825
try {
19-
launcherPath = require.resolve("nuwax-codex/bin/nuwax-codex.js");
26+
CODEX_VERSION = require("nuwax-codex/package.json").version;
2027
} catch {
21-
// nuwax-codex not installed as dependency — nothing to pre-download
22-
process.exit(0);
28+
process.exit(0); // nuwax-codex not installed — nothing to pre-download
2329
}
2430

25-
process.stderr.write("Pre-downloading nuwax-codex binary …\n");
31+
const OSS_CDN_BASE =
32+
"https://nuwa-packages.oss-rg-china-mainland.aliyuncs.com/nuwax-codex";
2633

27-
const result = spawnSync(process.execPath, [launcherPath, "--version"], {
28-
stdio: "inherit",
29-
timeout: 180_000, // 3 minutes for OSS download + extraction
30-
});
34+
// ---------------------------------------------------------------------------
35+
// Platform helpers
36+
// ---------------------------------------------------------------------------
37+
38+
function getTargetTriple() {
39+
const p = process.platform;
40+
const a = process.arch;
41+
if (p === "darwin") {
42+
return a === "arm64" ? "aarch64-apple-darwin" : "x86_64-apple-darwin";
43+
}
44+
if (p === "linux") {
45+
if (a !== "x64") throw new Error("Unsupported Linux arch: " + a);
46+
let family = "gnu";
47+
try {
48+
const { familySync } = require("detect-libc");
49+
family = familySync() === "musl" ? "musl" : "gnu";
50+
} catch {
51+
/* musl detection best-effort */
52+
}
53+
return `x86_64-unknown-linux-${family}`;
54+
}
55+
if (p === "win32") {
56+
return a === "arm64"
57+
? "aarch64-pc-windows-msvc"
58+
: "x86_64-pc-windows-msvc";
59+
}
60+
throw new Error("Unsupported platform: " + p);
61+
}
62+
63+
function getArchiveExt() {
64+
return process.platform === "win32" ? "zip" : "tar.gz";
65+
}
66+
67+
function getBinaryName() {
68+
return process.platform === "win32" ? "nuwax-codex.exe" : "nuwax-codex";
69+
}
70+
71+
// ---------------------------------------------------------------------------
72+
// Main
73+
// ---------------------------------------------------------------------------
74+
75+
function cacheDir() {
76+
return join(homedir(), ".nuwax-codex-cache", CODEX_VERSION);
77+
}
78+
79+
function cachedBinaryPath() {
80+
return join(cacheDir(), getBinaryName());
81+
}
82+
83+
async function download(url, outPath) {
84+
mkdirSync(dirname(outPath), { recursive: true });
85+
const res = await fetch(url, { redirect: "follow" });
86+
if (!res.ok) {
87+
throw new Error(
88+
`Download failed: HTTP ${res.status} ${res.statusText}\nURL: ${url}`,
89+
);
90+
}
91+
const total = parseInt(res.headers.get("content-length") || "0", 10);
92+
let downloaded = 0;
93+
const reader = res.body.getReader();
94+
const ws = createWriteStream(outPath);
95+
const logInterval = setInterval(() => {
96+
if (total > 0) {
97+
process.stderr.write(
98+
`\r Downloading nuwax-codex ${CODEX_VERSION}${((downloaded / total) * 100).toFixed(0)}%`,
99+
);
100+
}
101+
}, 500);
102+
try {
103+
while (true) {
104+
const { done, value } = await reader.read();
105+
if (done) break;
106+
ws.write(value);
107+
downloaded += value.length;
108+
}
109+
} finally {
110+
clearInterval(logInterval);
111+
ws.end();
112+
}
113+
process.stderr.write("\n");
114+
}
115+
116+
async function main() {
117+
const cached = cachedBinaryPath();
118+
if (existsSync(cached)) {
119+
process.stderr.write(
120+
`nuwax-codex ${CODEX_VERSION} already cached, skipping.\n`,
121+
);
122+
return;
123+
}
124+
125+
const target = getTargetTriple();
126+
const ext = getArchiveExt();
127+
const url = `${OSS_CDN_BASE}/v${CODEX_VERSION}/nuwax-codex-${CODEX_VERSION}-${target}.${ext}`;
31128

32-
if (result.status !== 0) {
33-
// Don't fail the install — the binary will be downloaded on first run
34129
process.stderr.write(
35-
`Warning: nuwax-codex pre-download exited with status ${result.status}. It will retry on first use.\n`,
130+
`Pre-downloading nuwax-codex ${CODEX_VERSION} for ${target} …\n ${url}\n`,
36131
);
132+
133+
const dir = cacheDir();
134+
mkdirSync(dir, { recursive: true });
135+
const archivePath = join(dir, `nuwax-codex.${ext}`);
136+
137+
// 1. Download
138+
await download(url, archivePath);
139+
140+
// 2. Extract using system tools (reliable, no custom tar-parser bugs)
141+
process.stderr.write(" Extracting …\n");
142+
if (ext === "tar.gz") {
143+
execSync(`tar xzf "${archivePath}" -C "${dir}"`, { stdio: "inherit" });
144+
} else {
145+
// Windows: use PowerShell Expand-Archive
146+
execSync(
147+
`powershell -NoProfile -Command "Expand-Archive -Force '${archivePath}' '${dir}'"`,
148+
{ stdio: "inherit" },
149+
);
150+
}
151+
152+
// 3. Clean up archive
153+
try {
154+
unlinkSync(archivePath);
155+
} catch {
156+
/* best-effort */
157+
}
158+
159+
// 4. Ensure executable
160+
if (process.platform !== "win32") {
161+
chmodSync(cached, 0o755);
162+
}
163+
164+
if (existsSync(cached)) {
165+
process.stderr.write(`✓ nuwax-codex ${CODEX_VERSION} ready.\n`);
166+
} else {
167+
process.stderr.write(
168+
`⚠ Download completed but binary not found at ${cached} — it will retry on first run.\n`,
169+
);
170+
}
37171
}
172+
173+
main().catch((err) => {
174+
process.stderr.write(`⚠ nuwax-codex pre-download skipped: ${err.message}\n`);
175+
process.stderr.write(` The binary will be downloaded on first use instead.\n`);
176+
// Never fail the install
177+
});

0 commit comments

Comments
 (0)