baiduwangpan-cli 1.1.5 → 1.7.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.
@@ -1,340 +1,340 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * postinstall.js — 自动下载 BaiduPCS-Go 二进制
5
- *
6
- * 跨平台支持: Windows / macOS / Linux (x64, arm64, 386)
7
- * 下载加速: 镜像(ghfast/ghproxy...) 优先 → GitHub 兜底(大陆网络 GitHub 常 DNS 污染)
8
- * 解压方式: Windows (PowerShell) / Unix (unzip / python3 / tar)
9
- *
10
- * 1.1.0 已知问题修复:
11
- * - 镜像顺序: 国内网络优先用 ghfast/ghproxy,GitHub 放最后
12
- * - download() 补 file stream error 处理:第一个镜像部分下载失败时不再直接崩溃,
13
- * 确保 5 个源按顺序 fallback(此前"只跑 1 行就抛出")
14
- * - 全部失败时打印每个源的完整下载地址,便于手动下载
15
- * - 发现版本子目录(BaiduPCS-Go-vX/)里的二进制时提升到固定路径(修复后无需手改 config)
16
- */
17
-
18
- const https = require("https");
19
- const http = require("http");
20
- const fs = require("fs");
21
- const path = require("path");
22
- const os = require("os");
23
- const { spawnSync, execSync } = require("child_process");
24
-
25
- const PKG_ROOT = path.join(__dirname, "..");
26
- const REPO = "qjfoidnh/BaiduPCS-Go";
27
- const VERSION = "v4.0.1";
28
-
29
- // GitHub 下载加速镜像(按优先级排列):大陆网络优先镜像,GitHub 放最后兜底
30
- const MIRRORS = [
31
- (asset) => `https://ghfast.top/https://github.com/${REPO}/releases/download/${VERSION}/${asset}`,
32
- (asset) => `https://ghproxy.net/https://github.com/${REPO}/releases/download/${VERSION}/${asset}`,
33
- (asset) => `https://mirror.ghproxy.com/https://github.com/${REPO}/releases/download/${VERSION}/${asset}`,
34
- (asset) => `https://gh-proxy.com/https://github.com/${REPO}/releases/download/${VERSION}/${asset}`,
35
- (asset) => `https://github.com/${REPO}/releases/download/${VERSION}/${asset}`,
36
- ];
37
-
38
- // ── 平台检测 ────────────────────────────────────────────
39
-
40
- function getAssetName() {
41
- const platform = os.platform();
42
- const arch = os.arch();
43
-
44
- let osName, archName;
45
-
46
- switch (platform) {
47
- case "win32": osName = "windows"; break;
48
- case "darwin": osName = "darwin-osx"; break;
49
- case "linux": osName = "linux"; break;
50
- case "freebsd": osName = "freebsd"; break;
51
- default: throw new Error(`Unsupported platform: ${platform}`);
52
- }
53
-
54
- switch (arch) {
55
- case "x64":
56
- archName = osName === "windows" ? "x64" : "amd64";
57
- break;
58
- case "arm64":
59
- case "aarch64":
60
- archName = "arm64";
61
- break;
62
- case "ia32":
63
- case "x32":
64
- case "arm":
65
- archName = (osName === "windows") ? "x86" : "386";
66
- break;
67
- case "mips":
68
- archName = osName === "linux" ? "mips" : arch;
69
- break;
70
- case "mipsel":
71
- archName = osName === "linux" ? "mipsle" : arch;
72
- break;
73
- default:
74
- throw new Error(`Unsupported arch: ${arch}`);
75
- }
76
-
77
- return { assetName: `BaiduPCS-Go-${VERSION}-${osName}-${archName}.zip`, osName, archName };
78
- }
79
-
80
- function getBinaryName() {
81
- return os.platform() === "win32" ? "BaiduPCS-Go.exe" : "BaiduPCS-Go";
82
- }
83
-
84
- // ── 下载 ────────────────────────────────────────────────
85
-
86
- function download(url, dest, timeout = 60000) {
87
- return new Promise((resolve, reject) => {
88
- const file = fs.createWriteStream(dest);
89
- let settled = false;
90
-
91
- const timer = setTimeout(() => {
92
- if (!settled) {
93
- settled = true;
94
- try { file.close(); } catch {}
95
- fs.unlink(dest, () => {});
96
- reject(new Error("timeout"));
97
- }
98
- }, timeout);
99
-
100
- // 统一失败路径:清定时器 + 关流 + 删残留文件。file stream 的 error 也走这里,
101
- // 防止第一个镜像部分下载失败后 file 抛 EPIPE/ENOSPC 未捕获导致整个 postinstall 崩溃
102
- // (1.1.0 实测"只跑 1 行就抛出",fallback 没机会执行)。
103
- const fail = (err) => {
104
- if (settled) return;
105
- settled = true;
106
- clearTimeout(timer);
107
- try { file.close(); } catch {}
108
- fs.unlink(dest, () => {});
109
- reject(err);
110
- };
111
-
112
- file.on("error", (e) => fail(e));
113
-
114
- const req = (url, redirects = 0) => {
115
- if (redirects > 5) {
116
- fail(new Error("too many redirects"));
117
- return;
118
- }
119
-
120
- const client = url.startsWith("https") ? https : http;
121
- let r;
122
- try {
123
- r = client.get(url, { timeout: 15000 }, (res) => {
124
- // 处理重定向
125
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
126
- res.resume();
127
- req(res.headers.location, redirects + 1);
128
- return;
129
- }
130
- if (res.statusCode !== 200) {
131
- res.resume();
132
- fail(new Error(`HTTP ${res.statusCode}`));
133
- return;
134
- }
135
- res.pipe(file);
136
- file.on("finish", () => {
137
- file.close(() => {
138
- if (!settled) {
139
- settled = true;
140
- clearTimeout(timer);
141
- resolve();
142
- }
143
- });
144
- });
145
- });
146
- } catch (e) {
147
- fail(e);
148
- return;
149
- }
150
-
151
- r.on("error", (e) => fail(e));
152
-
153
- r.on("timeout", () => {
154
- if (!settled) {
155
- r.destroy();
156
- fail(new Error("connection timeout"));
157
- }
158
- });
159
- };
160
-
161
- req(url);
162
- });
163
- }
164
-
165
- async function downloadWithMirrors(assetName, dest) {
166
- const errors = [];
167
- for (let i = 0; i < MIRRORS.length; i++) {
168
- const url = MIRRORS[i](assetName);
169
- const label = i === MIRRORS.length - 1 ? "GitHub" : new URL(MIRRORS[i]("x")).hostname;
170
- process.stdout.write(` bdp: trying ${label}...`);
171
- try {
172
- await download(url, dest, 90000);
173
- console.log(" OK");
174
- return { ok: true, url, label };
175
- } catch (e) {
176
- const msg = e.message || String(e);
177
- errors.push(`${label}: ${msg}`);
178
- console.log(` failed (${msg})`);
179
- if (fs.existsSync(dest)) {
180
- try { fs.unlinkSync(dest); } catch {}
181
- }
182
- // GitHub DNS 污染提示
183
- if (/ENOTFOUND|EAI_AGAIN|ETIMEDOUT/i.test(msg) && label === "GitHub") {
184
- console.log(" bdp: (GitHub 解析失败——大陆网络常见 DNS 污染,前面的镜像通常可用)");
185
- }
186
- }
187
- }
188
- return { ok: false, errors };
189
- }
190
-
191
- // ── 解压 ────────────────────────────────────────────────
192
-
193
- function extractZip(zipPath, destDir) {
194
- const platform = os.platform();
195
-
196
- // Windows: PowerShell
197
- if (platform === "win32") {
198
- try {
199
- execSync(
200
- `powershell -NoProfile -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`,
201
- { stdio: "ignore", timeout: 30000 }
202
- );
203
- return true;
204
- } catch { /* fall through */ }
205
- }
206
-
207
- // Unix: unzip
208
- const unzip = spawnSync("unzip", ["-o", "-q", zipPath, "-d", destDir], { stdio: "ignore", timeout: 30000 });
209
- if (unzip.status === 0) return true;
210
-
211
- // Fallback: python3
212
- try {
213
- execSync(
214
- `python3 -c "import zipfile,sys; zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])" "${zipPath}" "${destDir}"`,
215
- { stdio: "ignore", timeout: 30000 }
216
- );
217
- return true;
218
- } catch { /* fall through */ }
219
-
220
- // Fallback: python
221
- try {
222
- execSync(
223
- `python -c "import zipfile,sys; zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])" "${zipPath}" "${destDir}"`,
224
- { stdio: "ignore", timeout: 30000 }
225
- );
226
- return true;
227
- } catch { /* fall through */ }
228
-
229
- return false;
230
- }
231
-
232
- // ── 主逻辑 ──────────────────────────────────────────────
233
-
234
- function findExistingBinary(binName) {
235
- const binPath = path.join(PKG_ROOT, binName);
236
- if (fs.existsSync(binPath)) return { path: binPath, versioned: false };
237
- // 版本子目录(上次 postinstall 失败后手动解压的常见位置)
238
- try {
239
- for (const entry of fs.readdirSync(PKG_ROOT, { withFileTypes: true })) {
240
- if (entry.isDirectory() && entry.name.includes("BaiduPCS-Go")) {
241
- const p = path.join(PKG_ROOT, entry.name, binName);
242
- if (fs.existsSync(p)) return { path: p, versioned: true };
243
- }
244
- }
245
- } catch {}
246
- return null;
247
- }
248
-
249
- async function main() {
250
- const binName = getBinaryName();
251
- const binPath = path.join(PKG_ROOT, binName);
252
-
253
- // 已存在则跳过;版本子目录里的二进制提升到固定路径(CLI 固定路径优先,且不依赖版本号)
254
- const existing = findExistingBinary(binName);
255
- if (existing) {
256
- if (existing.versioned) {
257
- try {
258
- fs.copyFileSync(existing.path, binPath);
259
- if (os.platform() !== "win32") { try { fs.chmodSync(binPath, 0o755); } catch {} }
260
- console.log(` bdp: ✅ BaiduPCS-Go promoted from ${path.basename(path.dirname(existing.path))} to fixed path.`);
261
- } catch (e) {
262
- console.log(` bdp: ⚠️ 版本子目录已发现但提升失败: ${e.message}`);
263
- }
264
- } else {
265
- console.log(" bdp: BaiduPCS-Go already exists, skipping.");
266
- }
267
- return;
268
- }
269
-
270
- const { assetName } = getAssetName();
271
- const zipPath = path.join(PKG_ROOT, assetName);
272
-
273
- console.log("");
274
- console.log(" bdp: Installing BaiduPCS-Go (百度网盘 CLI 引擎)...");
275
-
276
- // 下载(镜像优先 → GitHub 兜底)
277
- const result = await downloadWithMirrors(assetName, zipPath);
278
- if (!result.ok) {
279
- console.warn(" bdp: ⚠️ 所有下载源失败(大陆网络常见:GitHub DNS 污染 / 镜像暂时抽风)。");
280
- console.warn(` bdp: 请手动下载 ${assetName}(任一地址均可):`);
281
- for (const m of MIRRORS) console.warn(` bdp: ${m(assetName)}`);
282
- console.warn(` bdp: 解压后把 ${binName} 放到: ${PKG_ROOT}`);
283
- console.warn(" bdp: (或版本子目录,如 BaiduPCS-Go-v4.0.1-windows-x64/,CLI 会自动发现)");
284
- console.warn(" bdp: 也可直接确保 BaiduPCS-Go 在 PATH 中(包仍可正常使用)。");
285
- console.warn("");
286
- process.exit(0); // 不阻断 npm install
287
- }
288
-
289
- // 解压
290
- console.log(" bdp: Extracting...");
291
- if (!extractZip(zipPath, PKG_ROOT)) {
292
- console.warn(` bdp: ⚠️ 解压失败。请手动解压: ${zipPath}`);
293
- process.exit(0);
294
- }
295
-
296
- // 查找并移动二进制
297
- let found = false;
298
- function searchDir(dir) {
299
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
300
- if (entry.name === binName) {
301
- const src = path.join(dir, entry.name);
302
- if (!found) {
303
- fs.copyFileSync(src, binPath);
304
- found = true;
305
- }
306
- }
307
- if (entry.isDirectory() && entry.name.includes("BaiduPCS-Go")) {
308
- searchDir(path.join(dir, entry.name));
309
- }
310
- }
311
- }
312
- searchDir(PKG_ROOT);
313
-
314
- if (found) {
315
- // Unix 设置可执行权限
316
- if (os.platform() !== "win32") {
317
- try { fs.chmodSync(binPath, 0o755); } catch {}
318
- }
319
- console.log(` bdp: ✅ BaiduPCS-Go installed.`);
320
- } else {
321
- console.warn(" bdp: ⚠️ 解压后未找到二进制。");
322
- console.warn(` bdp: 请手动解压并把 ${binName} 放到: ${PKG_ROOT}`);
323
- }
324
-
325
- // 清理 zip 和解压目录
326
- try { fs.unlinkSync(zipPath); } catch {}
327
- for (const entry of fs.readdirSync(PKG_ROOT, { withFileTypes: true })) {
328
- if (entry.isDirectory() && entry.name.includes("BaiduPCS-Go-v")) {
329
- try { fs.rmSync(path.join(PKG_ROOT, entry.name), { recursive: true }); } catch {}
330
- }
331
- }
332
-
333
- console.log("");
334
- }
335
-
336
- main().catch((e) => {
337
- console.warn(` bdp: ⚠️ Post-install error: ${e.message}`);
338
- console.warn(" bdp: The package will still work if BaiduPCS-Go is in your PATH.");
339
- process.exit(0); // 永不阻断 npm install
340
- });
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * postinstall.js — 自动下载 BaiduPCS-Go 二进制
5
+ *
6
+ * 跨平台支持: Windows / macOS / Linux (x64, arm64, 386)
7
+ * 下载加速: 镜像(ghfast/ghproxy...) 优先 → GitHub 兜底(大陆网络 GitHub 常 DNS 污染)
8
+ * 解压方式: Windows (PowerShell) / Unix (unzip / python3 / tar)
9
+ *
10
+ * 1.1.0 已知问题修复:
11
+ * - 镜像顺序: 国内网络优先用 ghfast/ghproxy,GitHub 放最后
12
+ * - download() 补 file stream error 处理:第一个镜像部分下载失败时不再直接崩溃,
13
+ * 确保 5 个源按顺序 fallback(此前"只跑 1 行就抛出")
14
+ * - 全部失败时打印每个源的完整下载地址,便于手动下载
15
+ * - 发现版本子目录(BaiduPCS-Go-vX/)里的二进制时提升到固定路径(修复后无需手改 config)
16
+ */
17
+
18
+ const https = require("https");
19
+ const http = require("http");
20
+ const fs = require("fs");
21
+ const path = require("path");
22
+ const os = require("os");
23
+ const { spawnSync, execSync } = require("child_process");
24
+
25
+ const PKG_ROOT = path.join(__dirname, "..");
26
+ const REPO = "qjfoidnh/BaiduPCS-Go";
27
+ const VERSION = "v4.0.1";
28
+
29
+ // GitHub 下载加速镜像(按优先级排列):大陆网络优先镜像,GitHub 放最后兜底
30
+ const MIRRORS = [
31
+ (asset) => `https://ghfast.top/https://github.com/${REPO}/releases/download/${VERSION}/${asset}`,
32
+ (asset) => `https://ghproxy.net/https://github.com/${REPO}/releases/download/${VERSION}/${asset}`,
33
+ (asset) => `https://mirror.ghproxy.com/https://github.com/${REPO}/releases/download/${VERSION}/${asset}`,
34
+ (asset) => `https://gh-proxy.com/https://github.com/${REPO}/releases/download/${VERSION}/${asset}`,
35
+ (asset) => `https://github.com/${REPO}/releases/download/${VERSION}/${asset}`,
36
+ ];
37
+
38
+ // ── 平台检测 ────────────────────────────────────────────
39
+
40
+ function getAssetName() {
41
+ const platform = os.platform();
42
+ const arch = os.arch();
43
+
44
+ let osName, archName;
45
+
46
+ switch (platform) {
47
+ case "win32": osName = "windows"; break;
48
+ case "darwin": osName = "darwin-osx"; break;
49
+ case "linux": osName = "linux"; break;
50
+ case "freebsd": osName = "freebsd"; break;
51
+ default: throw new Error(`Unsupported platform: ${platform}`);
52
+ }
53
+
54
+ switch (arch) {
55
+ case "x64":
56
+ archName = osName === "windows" ? "x64" : "amd64";
57
+ break;
58
+ case "arm64":
59
+ case "aarch64":
60
+ archName = "arm64";
61
+ break;
62
+ case "ia32":
63
+ case "x32":
64
+ case "arm":
65
+ archName = (osName === "windows") ? "x86" : "386";
66
+ break;
67
+ case "mips":
68
+ archName = osName === "linux" ? "mips" : arch;
69
+ break;
70
+ case "mipsel":
71
+ archName = osName === "linux" ? "mipsle" : arch;
72
+ break;
73
+ default:
74
+ throw new Error(`Unsupported arch: ${arch}`);
75
+ }
76
+
77
+ return { assetName: `BaiduPCS-Go-${VERSION}-${osName}-${archName}.zip`, osName, archName };
78
+ }
79
+
80
+ function getBinaryName() {
81
+ return os.platform() === "win32" ? "BaiduPCS-Go.exe" : "BaiduPCS-Go";
82
+ }
83
+
84
+ // ── 下载 ────────────────────────────────────────────────
85
+
86
+ function download(url, dest, timeout = 60000) {
87
+ return new Promise((resolve, reject) => {
88
+ const file = fs.createWriteStream(dest);
89
+ let settled = false;
90
+
91
+ const timer = setTimeout(() => {
92
+ if (!settled) {
93
+ settled = true;
94
+ try { file.close(); } catch {}
95
+ fs.unlink(dest, () => {});
96
+ reject(new Error("timeout"));
97
+ }
98
+ }, timeout);
99
+
100
+ // 统一失败路径:清定时器 + 关流 + 删残留文件。file stream 的 error 也走这里,
101
+ // 防止第一个镜像部分下载失败后 file 抛 EPIPE/ENOSPC 未捕获导致整个 postinstall 崩溃
102
+ // (1.1.0 实测"只跑 1 行就抛出",fallback 没机会执行)。
103
+ const fail = (err) => {
104
+ if (settled) return;
105
+ settled = true;
106
+ clearTimeout(timer);
107
+ try { file.close(); } catch {}
108
+ fs.unlink(dest, () => {});
109
+ reject(err);
110
+ };
111
+
112
+ file.on("error", (e) => fail(e));
113
+
114
+ const req = (url, redirects = 0) => {
115
+ if (redirects > 5) {
116
+ fail(new Error("too many redirects"));
117
+ return;
118
+ }
119
+
120
+ const client = url.startsWith("https") ? https : http;
121
+ let r;
122
+ try {
123
+ r = client.get(url, { timeout: 15000 }, (res) => {
124
+ // 处理重定向
125
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
126
+ res.resume();
127
+ req(res.headers.location, redirects + 1);
128
+ return;
129
+ }
130
+ if (res.statusCode !== 200) {
131
+ res.resume();
132
+ fail(new Error(`HTTP ${res.statusCode}`));
133
+ return;
134
+ }
135
+ res.pipe(file);
136
+ file.on("finish", () => {
137
+ file.close(() => {
138
+ if (!settled) {
139
+ settled = true;
140
+ clearTimeout(timer);
141
+ resolve();
142
+ }
143
+ });
144
+ });
145
+ });
146
+ } catch (e) {
147
+ fail(e);
148
+ return;
149
+ }
150
+
151
+ r.on("error", (e) => fail(e));
152
+
153
+ r.on("timeout", () => {
154
+ if (!settled) {
155
+ r.destroy();
156
+ fail(new Error("connection timeout"));
157
+ }
158
+ });
159
+ };
160
+
161
+ req(url);
162
+ });
163
+ }
164
+
165
+ async function downloadWithMirrors(assetName, dest) {
166
+ const errors = [];
167
+ for (let i = 0; i < MIRRORS.length; i++) {
168
+ const url = MIRRORS[i](assetName);
169
+ const label = i === MIRRORS.length - 1 ? "GitHub" : new URL(MIRRORS[i]("x")).hostname;
170
+ process.stdout.write(` bdp: trying ${label}...`);
171
+ try {
172
+ await download(url, dest, 90000);
173
+ console.log(" OK");
174
+ return { ok: true, url, label };
175
+ } catch (e) {
176
+ const msg = e.message || String(e);
177
+ errors.push(`${label}: ${msg}`);
178
+ console.log(` failed (${msg})`);
179
+ if (fs.existsSync(dest)) {
180
+ try { fs.unlinkSync(dest); } catch {}
181
+ }
182
+ // GitHub DNS 污染提示
183
+ if (/ENOTFOUND|EAI_AGAIN|ETIMEDOUT/i.test(msg) && label === "GitHub") {
184
+ console.log(" bdp: (GitHub 解析失败——大陆网络常见 DNS 污染,前面的镜像通常可用)");
185
+ }
186
+ }
187
+ }
188
+ return { ok: false, errors };
189
+ }
190
+
191
+ // ── 解压 ────────────────────────────────────────────────
192
+
193
+ function extractZip(zipPath, destDir) {
194
+ const platform = os.platform();
195
+
196
+ // Windows: PowerShell
197
+ if (platform === "win32") {
198
+ try {
199
+ execSync(
200
+ `powershell -NoProfile -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`,
201
+ { stdio: "ignore", timeout: 30000 }
202
+ );
203
+ return true;
204
+ } catch { /* fall through */ }
205
+ }
206
+
207
+ // Unix: unzip
208
+ const unzip = spawnSync("unzip", ["-o", "-q", zipPath, "-d", destDir], { stdio: "ignore", timeout: 30000 });
209
+ if (unzip.status === 0) return true;
210
+
211
+ // Fallback: python3
212
+ try {
213
+ execSync(
214
+ `python3 -c "import zipfile,sys; zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])" "${zipPath}" "${destDir}"`,
215
+ { stdio: "ignore", timeout: 30000 }
216
+ );
217
+ return true;
218
+ } catch { /* fall through */ }
219
+
220
+ // Fallback: python
221
+ try {
222
+ execSync(
223
+ `python -c "import zipfile,sys; zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])" "${zipPath}" "${destDir}"`,
224
+ { stdio: "ignore", timeout: 30000 }
225
+ );
226
+ return true;
227
+ } catch { /* fall through */ }
228
+
229
+ return false;
230
+ }
231
+
232
+ // ── 主逻辑 ──────────────────────────────────────────────
233
+
234
+ function findExistingBinary(binName) {
235
+ const binPath = path.join(PKG_ROOT, binName);
236
+ if (fs.existsSync(binPath)) return { path: binPath, versioned: false };
237
+ // 版本子目录(上次 postinstall 失败后手动解压的常见位置)
238
+ try {
239
+ for (const entry of fs.readdirSync(PKG_ROOT, { withFileTypes: true })) {
240
+ if (entry.isDirectory() && entry.name.includes("BaiduPCS-Go")) {
241
+ const p = path.join(PKG_ROOT, entry.name, binName);
242
+ if (fs.existsSync(p)) return { path: p, versioned: true };
243
+ }
244
+ }
245
+ } catch {}
246
+ return null;
247
+ }
248
+
249
+ async function main() {
250
+ const binName = getBinaryName();
251
+ const binPath = path.join(PKG_ROOT, binName);
252
+
253
+ // 已存在则跳过;版本子目录里的二进制提升到固定路径(CLI 固定路径优先,且不依赖版本号)
254
+ const existing = findExistingBinary(binName);
255
+ if (existing) {
256
+ if (existing.versioned) {
257
+ try {
258
+ fs.copyFileSync(existing.path, binPath);
259
+ if (os.platform() !== "win32") { try { fs.chmodSync(binPath, 0o755); } catch {} }
260
+ console.log(` bdp: ✅ BaiduPCS-Go promoted from ${path.basename(path.dirname(existing.path))} to fixed path.`);
261
+ } catch (e) {
262
+ console.log(` bdp: ⚠️ 版本子目录已发现但提升失败: ${e.message}`);
263
+ }
264
+ } else {
265
+ console.log(" bdp: BaiduPCS-Go already exists, skipping.");
266
+ }
267
+ return;
268
+ }
269
+
270
+ const { assetName } = getAssetName();
271
+ const zipPath = path.join(PKG_ROOT, assetName);
272
+
273
+ console.log("");
274
+ console.log(" bdp: Installing BaiduPCS-Go (百度网盘 CLI 引擎)...");
275
+
276
+ // 下载(镜像优先 → GitHub 兜底)
277
+ const result = await downloadWithMirrors(assetName, zipPath);
278
+ if (!result.ok) {
279
+ console.warn(" bdp: ⚠️ 所有下载源失败(大陆网络常见:GitHub DNS 污染 / 镜像暂时抽风)。");
280
+ console.warn(` bdp: 请手动下载 ${assetName}(任一地址均可):`);
281
+ for (const m of MIRRORS) console.warn(` bdp: ${m(assetName)}`);
282
+ console.warn(` bdp: 解压后把 ${binName} 放到: ${PKG_ROOT}`);
283
+ console.warn(" bdp: (或版本子目录,如 BaiduPCS-Go-v4.0.1-windows-x64/,CLI 会自动发现)");
284
+ console.warn(" bdp: 也可直接确保 BaiduPCS-Go 在 PATH 中(包仍可正常使用)。");
285
+ console.warn("");
286
+ process.exit(0); // 不阻断 npm install
287
+ }
288
+
289
+ // 解压
290
+ console.log(" bdp: Extracting...");
291
+ if (!extractZip(zipPath, PKG_ROOT)) {
292
+ console.warn(` bdp: ⚠️ 解压失败。请手动解压: ${zipPath}`);
293
+ process.exit(0);
294
+ }
295
+
296
+ // 查找并移动二进制
297
+ let found = false;
298
+ function searchDir(dir) {
299
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
300
+ if (entry.name === binName) {
301
+ const src = path.join(dir, entry.name);
302
+ if (!found) {
303
+ fs.copyFileSync(src, binPath);
304
+ found = true;
305
+ }
306
+ }
307
+ if (entry.isDirectory() && entry.name.includes("BaiduPCS-Go")) {
308
+ searchDir(path.join(dir, entry.name));
309
+ }
310
+ }
311
+ }
312
+ searchDir(PKG_ROOT);
313
+
314
+ if (found) {
315
+ // Unix 设置可执行权限
316
+ if (os.platform() !== "win32") {
317
+ try { fs.chmodSync(binPath, 0o755); } catch {}
318
+ }
319
+ console.log(` bdp: ✅ BaiduPCS-Go installed.`);
320
+ } else {
321
+ console.warn(" bdp: ⚠️ 解压后未找到二进制。");
322
+ console.warn(` bdp: 请手动解压并把 ${binName} 放到: ${PKG_ROOT}`);
323
+ }
324
+
325
+ // 清理 zip 和解压目录
326
+ try { fs.unlinkSync(zipPath); } catch {}
327
+ for (const entry of fs.readdirSync(PKG_ROOT, { withFileTypes: true })) {
328
+ if (entry.isDirectory() && entry.name.includes("BaiduPCS-Go-v")) {
329
+ try { fs.rmSync(path.join(PKG_ROOT, entry.name), { recursive: true }); } catch {}
330
+ }
331
+ }
332
+
333
+ console.log("");
334
+ }
335
+
336
+ main().catch((e) => {
337
+ console.warn(` bdp: ⚠️ Post-install error: ${e.message}`);
338
+ console.warn(" bdp: The package will still work if BaiduPCS-Go is in your PATH.");
339
+ process.exit(0); // 永不阻断 npm install
340
+ });