nuwax-codex 0.17.8 → 0.18.0
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/bin/nuwax-codex.js +68 -14
- package/bin/postinstall.js +50 -10
- package/package.json +1 -1
package/bin/nuwax-codex.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// run and caches it locally. No platform-specific npm packages needed.
|
|
5
5
|
|
|
6
6
|
import { spawnSync } from "node:child_process";
|
|
7
|
-
import { createWriteStream, existsSync, mkdirSync, chmodSync } from "node:fs";
|
|
7
|
+
import { createWriteStream, existsSync, mkdirSync, chmodSync, renameSync, rmSync, cpSync } from "node:fs";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { join, dirname } from "node:path";
|
|
10
10
|
import { familySync } from "detect-libc";
|
|
@@ -24,10 +24,16 @@ function getTargetTriple() {
|
|
|
24
24
|
return a === "arm64" ? "aarch64-apple-darwin" : "x86_64-apple-darwin";
|
|
25
25
|
}
|
|
26
26
|
if (p === "linux") {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
? "x86_64-unknown-linux-musl"
|
|
30
|
-
|
|
27
|
+
const musl = familySync() === "musl";
|
|
28
|
+
if (a === "x64") {
|
|
29
|
+
return musl ? "x86_64-unknown-linux-musl" : "x86_64-unknown-linux-gnu";
|
|
30
|
+
}
|
|
31
|
+
if (a === "arm64") {
|
|
32
|
+
// Release builds aarch64-unknown-linux-musl; glibc arm64 maps to it
|
|
33
|
+
// (static musl binaries run on glibc hosts).
|
|
34
|
+
return "aarch64-unknown-linux-musl";
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Unsupported Linux arch: ${a}`);
|
|
31
37
|
}
|
|
32
38
|
if (p === "win32") {
|
|
33
39
|
return a === "arm64" ? "aarch64-pc-windows-msvc" : "x86_64-pc-windows-msvc";
|
|
@@ -46,7 +52,9 @@ function getBinaryName() {
|
|
|
46
52
|
// -- download & cache -------------------------------------------------------
|
|
47
53
|
|
|
48
54
|
function cacheDir() {
|
|
49
|
-
|
|
55
|
+
// Include the target triple: sharing HOME across glibc/musl containers
|
|
56
|
+
// or architectures would otherwise execute the wrong binary.
|
|
57
|
+
return join(homedir(), ".nuwax-codex-cache", VERSION, getTargetTriple());
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
function cachedBinaryPath() {
|
|
@@ -67,6 +75,12 @@ async function downloadBinary(url, outPath) {
|
|
|
67
75
|
let downloaded = 0;
|
|
68
76
|
const reader = res.body.getReader();
|
|
69
77
|
const ws = createWriteStream(outPath);
|
|
78
|
+
// Surface write errors (EACCES/ENOSPC/EMFILE) as promise rejections
|
|
79
|
+
// instead of unhandled 'error' events that kill the process before the
|
|
80
|
+
// outer catch can make the install fault-tolerant.
|
|
81
|
+
const writeFailed = new Promise((_, reject) => {
|
|
82
|
+
ws.on("error", reject);
|
|
83
|
+
});
|
|
70
84
|
const logInterval = setInterval(() => {
|
|
71
85
|
if (total > 0) {
|
|
72
86
|
process.stderr.write(
|
|
@@ -81,6 +95,11 @@ async function downloadBinary(url, outPath) {
|
|
|
81
95
|
if (done) break;
|
|
82
96
|
ws.write(value);
|
|
83
97
|
downloaded += value.length;
|
|
98
|
+
// A failed write rejects `writeFailed`; race so it escapes as a
|
|
99
|
+
// normal rejection the outer catch can tolerate.
|
|
100
|
+
await Promise.race([writeFailed.then(() => {}, () => {
|
|
101
|
+
throw new Error(`Failed to write ${outPath} (disk full or permission denied)`);
|
|
102
|
+
}), Promise.resolve()]);
|
|
84
103
|
}
|
|
85
104
|
} finally {
|
|
86
105
|
clearInterval(logInterval);
|
|
@@ -161,16 +180,47 @@ async function ensureBinary() {
|
|
|
161
180
|
console.error(` ${url}`);
|
|
162
181
|
|
|
163
182
|
const dir = cacheDir();
|
|
164
|
-
mkdirSync(dir, { recursive: true });
|
|
165
|
-
const archivePath = join(dir, `nuwax-codex.${ext}`);
|
|
166
183
|
|
|
167
|
-
|
|
184
|
+
// Atomic install (#38): download+extract into unique staging, verify,
|
|
185
|
+
// then rename into final cache. A crash mid-extract leaves only the
|
|
186
|
+
// staging dir; the cache never holds a partial install.
|
|
187
|
+
const lockDir = `${dir}.lock-${process.pid}-${Date.now()}`;
|
|
188
|
+
mkdirSync(lockDir, { recursive: true });
|
|
189
|
+
const stagingDir = join(lockDir, "stage");
|
|
190
|
+
mkdirSync(stagingDir, { recursive: true });
|
|
191
|
+
const archivePath = join(lockDir, `nuwax-codex.${ext}`);
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
await downloadBinary(url, archivePath);
|
|
195
|
+
|
|
196
|
+
console.error(" Extracting …");
|
|
197
|
+
if (ext === "tar.gz") {
|
|
198
|
+
await extractTarGz(archivePath, stagingDir);
|
|
199
|
+
} else {
|
|
200
|
+
await extractZip(archivePath, stagingDir);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const stagedBinary = join(stagingDir, getBinaryName());
|
|
204
|
+
if (!existsSync(stagedBinary)) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`Extraction completed but ${getBinaryName()} not found in staging directory`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
168
209
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
210
|
+
mkdirSync(dirname(dir), { recursive: true });
|
|
211
|
+
try {
|
|
212
|
+
renameSync(stagingDir, dir);
|
|
213
|
+
} catch (e) {
|
|
214
|
+
if (e.code === "ENOTEMPTY" || e.code === "EEXIST") {
|
|
215
|
+
if (!existsSync(cached)) throw e;
|
|
216
|
+
} else if (e.code === "EXDEV") {
|
|
217
|
+
cpSync(stagingDir, dir, { recursive: true });
|
|
218
|
+
} else {
|
|
219
|
+
throw e;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} finally {
|
|
223
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
174
224
|
}
|
|
175
225
|
|
|
176
226
|
if (!existsSync(cached)) {
|
|
@@ -183,9 +233,13 @@ async function ensureBinary() {
|
|
|
183
233
|
|
|
184
234
|
function run() {
|
|
185
235
|
ensureBinary().then((binaryPath) => {
|
|
236
|
+
// Tag the child as installed via the nuwax npm package so codex's
|
|
237
|
+
// update flow targets nuwax-codex (not upstream @openai/codex).
|
|
238
|
+
const env = { ...process.env, CODEX_INSTALL_SOURCE: "npm_nuwax" };
|
|
186
239
|
const result = spawnSync(binaryPath, process.argv.slice(2), {
|
|
187
240
|
stdio: "inherit",
|
|
188
241
|
windowsHide: true,
|
|
242
|
+
env,
|
|
189
243
|
});
|
|
190
244
|
if (result.error) {
|
|
191
245
|
console.error(`Failed to execute ${binaryPath}:`, result.error);
|
package/bin/postinstall.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Postinstall script: pre-downloads the native `nuwax-codex` binary from
|
|
3
3
|
// Alibaba Cloud OSS so the first CLI invocation is instant.
|
|
4
4
|
|
|
5
|
-
import { createWriteStream, existsSync, mkdirSync, chmodSync } from "node:fs";
|
|
5
|
+
import { createWriteStream, existsSync, mkdirSync, chmodSync, renameSync, rmSync, cpSync } from "node:fs";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { join, dirname } from "node:path";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
@@ -43,7 +43,9 @@ function getBinaryName() {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function cacheDir() {
|
|
46
|
-
|
|
46
|
+
// Include the target triple: sharing HOME across glibc/musl containers
|
|
47
|
+
// or architectures would otherwise execute the wrong binary.
|
|
48
|
+
return join(homedir(), ".nuwax-codex-cache", VERSION, getTargetTriple());
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
function cachedBinaryPath() {
|
|
@@ -66,6 +68,12 @@ async function downloadBinary(url, outPath) {
|
|
|
66
68
|
let downloaded = 0;
|
|
67
69
|
const reader = res.body.getReader();
|
|
68
70
|
const ws = createWriteStream(outPath);
|
|
71
|
+
// Surface write errors (EACCES/ENOSPC/EMFILE) as promise rejections
|
|
72
|
+
// instead of unhandled 'error' events that kill the process before the
|
|
73
|
+
// outer catch can make the install fault-tolerant.
|
|
74
|
+
const writeFailed = new Promise((_, reject) => {
|
|
75
|
+
ws.on("error", reject);
|
|
76
|
+
});
|
|
69
77
|
const logInterval = setInterval(() => {
|
|
70
78
|
if (total > 0) {
|
|
71
79
|
process.stderr.write(
|
|
@@ -79,6 +87,11 @@ async function downloadBinary(url, outPath) {
|
|
|
79
87
|
if (done) break;
|
|
80
88
|
ws.write(value);
|
|
81
89
|
downloaded += value.length;
|
|
90
|
+
// A failed write rejects `writeFailed`; race so it escapes as a
|
|
91
|
+
// normal rejection the outer catch can tolerate.
|
|
92
|
+
await Promise.race([writeFailed.then(() => {}, () => {
|
|
93
|
+
throw new Error(`Failed to write ${outPath} (disk full or permission denied)`);
|
|
94
|
+
}), Promise.resolve()]);
|
|
82
95
|
}
|
|
83
96
|
} finally {
|
|
84
97
|
clearInterval(logInterval);
|
|
@@ -165,16 +178,43 @@ async function main() {
|
|
|
165
178
|
process.stderr.write(` ${url}\n`);
|
|
166
179
|
|
|
167
180
|
const dir = cacheDir();
|
|
168
|
-
mkdirSync(dir, { recursive: true });
|
|
169
|
-
const archivePath = join(dir, `nuwax-codex.${ext}`);
|
|
170
181
|
|
|
171
|
-
|
|
182
|
+
// Atomic install (#38): staging directory + verify + rename commit.
|
|
183
|
+
const lockDir = `${dir}.lock-${process.pid}-${Date.now()}`;
|
|
184
|
+
mkdirSync(lockDir, { recursive: true });
|
|
185
|
+
const stagingDir = join(lockDir, "stage");
|
|
186
|
+
mkdirSync(stagingDir, { recursive: true });
|
|
187
|
+
const archivePath = join(lockDir, `nuwax-codex.${ext}`);
|
|
172
188
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
189
|
+
try {
|
|
190
|
+
await downloadBinary(url, archivePath);
|
|
191
|
+
|
|
192
|
+
process.stderr.write(" Extracting …\n");
|
|
193
|
+
if (ext === "tar.gz") {
|
|
194
|
+
await extractTarGz(archivePath, stagingDir);
|
|
195
|
+
} else {
|
|
196
|
+
await extractZip(archivePath, stagingDir);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const stagedBinary = join(stagingDir, getBinaryName());
|
|
200
|
+
if (!existsSync(stagedBinary)) {
|
|
201
|
+
throw new Error(`Extraction completed but ${getBinaryName()} not found in staging`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
mkdirSync(dirname(dir), { recursive: true });
|
|
205
|
+
try {
|
|
206
|
+
renameSync(stagingDir, dir);
|
|
207
|
+
} catch (e) {
|
|
208
|
+
if (e.code === "ENOTEMPTY" || e.code === "EEXIST") {
|
|
209
|
+
if (!existsSync(cached)) throw e;
|
|
210
|
+
} else if (e.code === "EXDEV") {
|
|
211
|
+
cpSync(stagingDir, dir, { recursive: true });
|
|
212
|
+
} else {
|
|
213
|
+
throw e;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} finally {
|
|
217
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
178
218
|
}
|
|
179
219
|
|
|
180
220
|
if (existsSync(cached)) {
|