nuwax-codex 0.17.5 → 0.17.7
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 +46 -55
- package/bin/postinstall.js +47 -47
- package/package.json +7 -2
package/bin/nuwax-codex.js
CHANGED
|
@@ -7,8 +7,6 @@ import { spawnSync } from "node:child_process";
|
|
|
7
7
|
import { createWriteStream, existsSync, mkdirSync, chmodSync } from "node:fs";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { join, dirname } from "node:path";
|
|
10
|
-
import { pipeline } from "node:stream/promises";
|
|
11
|
-
import { createGunzip } from "node:zlib";
|
|
12
10
|
import { familySync } from "detect-libc";
|
|
13
11
|
import { createRequire } from "node:module";
|
|
14
12
|
|
|
@@ -88,6 +86,13 @@ async function downloadBinary(url, outPath) {
|
|
|
88
86
|
clearInterval(logInterval);
|
|
89
87
|
ws.end();
|
|
90
88
|
}
|
|
89
|
+
// 等待写流的文件描述符真正释放、数据全部落盘后再返回。解压阶段(tar / yauzl)
|
|
90
|
+
// 要读取这个归档文件,若 Node 仍持有写句柄,数据未必已刷盘、在 Windows 上还可能
|
|
91
|
+
// 被独占打开拒绝。ws.end() 只是结束写入,fd 在 ‘close’ 事件时才异步关闭,必须 await。
|
|
92
|
+
await new Promise((resolve, reject) => {
|
|
93
|
+
ws.once("close", resolve);
|
|
94
|
+
ws.once("error", reject);
|
|
95
|
+
});
|
|
91
96
|
process.stderr.write("\n");
|
|
92
97
|
|
|
93
98
|
if (process.platform !== "win32") {
|
|
@@ -96,62 +101,48 @@ async function downloadBinary(url, outPath) {
|
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
async function extractTarGz(archivePath, destDir) {
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
const { writeFileSync } = await import("node:fs");
|
|
106
|
-
|
|
107
|
-
const gunzip = createGunzip();
|
|
108
|
-
const rs = createReadStream(archivePath);
|
|
109
|
-
let buffer = Buffer.alloc(0);
|
|
110
|
-
|
|
111
|
-
await pipeline(
|
|
112
|
-
rs,
|
|
113
|
-
gunzip,
|
|
114
|
-
new Transform({
|
|
115
|
-
transform(chunk, _enc, cb) {
|
|
116
|
-
buffer = Buffer.concat([buffer, chunk]);
|
|
117
|
-
while (buffer.length >= 512) {
|
|
118
|
-
// Parse tar header
|
|
119
|
-
const name = buffer.toString("utf8", 0, 100).replace(/\0.*$/, "");
|
|
120
|
-
const sizeStr = buffer.toString("utf8", 124, 136).replace(/\0.*$/, "");
|
|
121
|
-
const size = parseInt(sizeStr, 8);
|
|
122
|
-
if (isNaN(size) || size < 0) break;
|
|
123
|
-
|
|
124
|
-
const totalSize = Math.ceil((512 + size) / 512) * 512;
|
|
125
|
-
if (buffer.length < totalSize) break;
|
|
126
|
-
|
|
127
|
-
if (name && !name.endsWith("/") && size > 0) {
|
|
128
|
-
const fileData = buffer.subarray(512, 512 + size);
|
|
129
|
-
const outPath = join(destDir, name);
|
|
130
|
-
mkdirSync(dirname(outPath), { recursive: true });
|
|
131
|
-
writeFileSync(outPath, fileData);
|
|
132
|
-
if (process.platform !== "win32") {
|
|
133
|
-
chmodSync(outPath, 0o755);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
buffer = buffer.subarray(totalSize);
|
|
138
|
-
}
|
|
139
|
-
cb();
|
|
140
|
-
},
|
|
141
|
-
}),
|
|
142
|
-
);
|
|
104
|
+
// 用成熟的 npm `tar` 库流式解压。仓库原先手写 tar 解析器,会把整个 entry 攒进
|
|
105
|
+
// 一个 Buffer 再落盘,对 codex 这种 ~280MB 的单文件归档是 O(n²) 内存拷贝
|
|
106
|
+
// (实测 15s 都写不出一个文件),在 macOS/Linux 上表现为“卡在 Extracting”。
|
|
107
|
+
// `tar` 走流式管道、内存恒定,并按归档内记录的 mode 还原可执行位(已验证 0o755)。
|
|
108
|
+
const tar = await import("tar");
|
|
109
|
+
await tar.x({ file: archivePath, cwd: destDir, gzip: true });
|
|
143
110
|
}
|
|
144
111
|
|
|
145
112
|
async function extractZip(archivePath, destDir) {
|
|
146
|
-
//
|
|
147
|
-
// Windows
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
)
|
|
113
|
+
// Windows 上用纯 JS 的 yauzl 流式解压。原实现调用 PowerShell Expand-Archive,
|
|
114
|
+
// 两个硬伤:老版本 Windows PowerShell(5.0 前)没有该 cmdlet;它用独占 FileStream
|
|
115
|
+
// 打开 zip,极易撞“正由另一进程使用”(Node 写句柄未释放 / Defender 实时扫描)。
|
|
116
|
+
// yauzl 经 Node fs 以共享只读方式读取,绕开这两类问题,且逐条目流式落盘、内存恒定。
|
|
117
|
+
const yauzl = await import("yauzl");
|
|
118
|
+
const { createWriteStream, mkdirSync } = await import("node:fs");
|
|
119
|
+
const { dirname, join } = await import("node:path");
|
|
120
|
+
|
|
121
|
+
await new Promise((resolve, reject) => {
|
|
122
|
+
yauzl.open(archivePath, { lazyEntries: true, autoClose: true }, (err, zipfile) => {
|
|
123
|
+
if (err) return reject(err);
|
|
124
|
+
zipfile.on("error", reject);
|
|
125
|
+
zipfile.on("entry", (entry) => {
|
|
126
|
+
const outPath = join(destDir, entry.fileName);
|
|
127
|
+
if (/\/$/.test(entry.fileName)) {
|
|
128
|
+
mkdirSync(outPath, { recursive: true });
|
|
129
|
+
zipfile.readEntry();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
133
|
+
zipfile.openReadStream(entry, (e, readStream) => {
|
|
134
|
+
if (e) return reject(e);
|
|
135
|
+
const ws = createWriteStream(outPath);
|
|
136
|
+
readStream.on("error", reject);
|
|
137
|
+
ws.on("error", reject);
|
|
138
|
+
ws.on("close", () => zipfile.readEntry());
|
|
139
|
+
readStream.pipe(ws);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
zipfile.on("close", resolve);
|
|
143
|
+
zipfile.readEntry();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
155
146
|
}
|
|
156
147
|
|
|
157
148
|
// -- main -------------------------------------------------------------------
|
package/bin/postinstall.js
CHANGED
|
@@ -2,13 +2,9 @@
|
|
|
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
|
|
5
|
+
import { createWriteStream, existsSync, mkdirSync, chmodSync } from "node:fs";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { join, dirname } from "node:path";
|
|
8
|
-
import { createGunzip } from "node:zlib";
|
|
9
|
-
import { pipeline } from "node:stream/promises";
|
|
10
|
-
import { Transform } from "node:stream";
|
|
11
|
-
import { createReadStream } from "node:fs";
|
|
12
8
|
import { createRequire } from "node:module";
|
|
13
9
|
|
|
14
10
|
const require = createRequire(import.meta.url);
|
|
@@ -88,6 +84,13 @@ async function downloadBinary(url, outPath) {
|
|
|
88
84
|
clearInterval(logInterval);
|
|
89
85
|
ws.end();
|
|
90
86
|
}
|
|
87
|
+
// 等待写流的文件描述符真正释放、数据全部落盘后再返回。解压阶段(tar / yauzl)
|
|
88
|
+
// 要读取这个归档文件,若 Node 仍持有写句柄,数据未必已刷盘、在 Windows 上还可能
|
|
89
|
+
// 被独占打开拒绝。ws.end() 只是结束写入,fd 在 ‘close’ 事件时才异步关闭,必须 await。
|
|
90
|
+
await new Promise((resolve, reject) => {
|
|
91
|
+
ws.once("close", resolve);
|
|
92
|
+
ws.once("error", reject);
|
|
93
|
+
});
|
|
91
94
|
process.stderr.write("\n");
|
|
92
95
|
if (process.platform !== "win32") {
|
|
93
96
|
chmodSync(outPath, 0o755);
|
|
@@ -99,51 +102,48 @@ async function downloadBinary(url, outPath) {
|
|
|
99
102
|
// ---------------------------------------------------------------------------
|
|
100
103
|
|
|
101
104
|
async function extractTarGz(archivePath, destDir) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
await
|
|
107
|
-
|
|
108
|
-
gunzip,
|
|
109
|
-
new Transform({
|
|
110
|
-
transform(chunk, _enc, cb) {
|
|
111
|
-
buffer = Buffer.concat([buffer, chunk]);
|
|
112
|
-
while (buffer.length >= 512) {
|
|
113
|
-
const name = buffer.toString("utf8", 0, 100).replace(/\0.*$/, "");
|
|
114
|
-
const sizeStr = buffer.toString("utf8", 124, 136).replace(/\0.*$/, "");
|
|
115
|
-
const size = parseInt(sizeStr, 8);
|
|
116
|
-
if (isNaN(size) || size < 0) break;
|
|
117
|
-
const totalSize = Math.ceil((512 + size) / 512) * 512;
|
|
118
|
-
if (buffer.length < totalSize) break;
|
|
119
|
-
if (name && !name.endsWith("/") && size > 0) {
|
|
120
|
-
const fileData = buffer.subarray(512, 512 + size);
|
|
121
|
-
const outPath = join(destDir, name);
|
|
122
|
-
mkdirSync(dirname(outPath), { recursive: true });
|
|
123
|
-
writeFileSync(outPath, fileData);
|
|
124
|
-
if (process.platform !== "win32") {
|
|
125
|
-
chmodSync(outPath, 0o755);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
buffer = buffer.subarray(totalSize);
|
|
129
|
-
}
|
|
130
|
-
cb();
|
|
131
|
-
},
|
|
132
|
-
}),
|
|
133
|
-
);
|
|
105
|
+
// 用成熟的 npm `tar` 库流式解压。仓库原先手写 tar 解析器,会把整个 entry 攒进
|
|
106
|
+
// 一个 Buffer 再落盘,对 codex 这种 ~280MB 的单文件归档是 O(n²) 内存拷贝
|
|
107
|
+
// (实测 15s 都写不出一个文件),在 macOS/Linux 上表现为“卡在 Extracting”。
|
|
108
|
+
// `tar` 走流式管道、内存恒定,并按归档内记录的 mode 还原可执行位(已验证 0o755)。
|
|
109
|
+
const tar = await import("tar");
|
|
110
|
+
await tar.x({ file: archivePath, cwd: destDir, gzip: true });
|
|
134
111
|
}
|
|
135
112
|
|
|
136
113
|
async function extractZip(archivePath, destDir) {
|
|
137
|
-
//
|
|
138
|
-
// Windows
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
114
|
+
// Windows 上用纯 JS 的 yauzl 流式解压。原实现调用 PowerShell Expand-Archive,
|
|
115
|
+
// 两个硬伤:老版本 Windows PowerShell(5.0 前)没有该 cmdlet;它用独占 FileStream
|
|
116
|
+
// 打开 zip,极易撞“正由另一进程使用”(Node 写句柄未释放 / Defender 实时扫描)。
|
|
117
|
+
// yauzl 经 Node fs 以共享只读方式读取,绕开这两类问题,且逐条目流式落盘、内存恒定。
|
|
118
|
+
const yauzl = await import("yauzl");
|
|
119
|
+
const { createWriteStream, mkdirSync } = await import("node:fs");
|
|
120
|
+
const { dirname, join } = await import("node:path");
|
|
121
|
+
|
|
122
|
+
await new Promise((resolve, reject) => {
|
|
123
|
+
yauzl.open(archivePath, { lazyEntries: true, autoClose: true }, (err, zipfile) => {
|
|
124
|
+
if (err) return reject(err);
|
|
125
|
+
zipfile.on("error", reject);
|
|
126
|
+
zipfile.on("entry", (entry) => {
|
|
127
|
+
const outPath = join(destDir, entry.fileName);
|
|
128
|
+
if (/\/$/.test(entry.fileName)) {
|
|
129
|
+
mkdirSync(outPath, { recursive: true });
|
|
130
|
+
zipfile.readEntry();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
134
|
+
zipfile.openReadStream(entry, (e, readStream) => {
|
|
135
|
+
if (e) return reject(e);
|
|
136
|
+
const ws = createWriteStream(outPath);
|
|
137
|
+
readStream.on("error", reject);
|
|
138
|
+
ws.on("error", reject);
|
|
139
|
+
ws.on("close", () => zipfile.readEntry());
|
|
140
|
+
readStream.pipe(ws);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
zipfile.on("close", resolve);
|
|
144
|
+
zipfile.readEntry();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuwax-codex",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Fork of OpenAI Codex CLI with domestic (Chinese) LLM support via genai",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"llm",
|
|
23
23
|
"genai"
|
|
24
24
|
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"postinstall": "node bin/postinstall.js"
|
|
27
|
+
},
|
|
25
28
|
"bin": {
|
|
26
29
|
"nuwax-codex": "bin/nuwax-codex.js"
|
|
27
30
|
},
|
|
@@ -32,6 +35,8 @@
|
|
|
32
35
|
"node": ">=20"
|
|
33
36
|
},
|
|
34
37
|
"dependencies": {
|
|
35
|
-
"detect-libc": "^2.0.0"
|
|
38
|
+
"detect-libc": "^2.0.0",
|
|
39
|
+
"tar": "^7.0.0",
|
|
40
|
+
"yauzl": "^3.1.0"
|
|
36
41
|
}
|
|
37
42
|
}
|