@sciagent/cli 1.0.84 → 1.0.86
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/sciagent.js +95 -79
- package/package.json +7 -7
- package/scripts/postinstall.js +133 -56
package/bin/sciagent.js
CHANGED
|
@@ -3,17 +3,27 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* SciAgent CLI 薄壳脚本
|
|
5
5
|
* 自动识别平台并调用对应的预编译二进制文件
|
|
6
|
+
*
|
|
7
|
+
* 二进制查找策略:
|
|
8
|
+
* 1. %LOCALAPPDATA%\sciagent\bin\ (Windows) 或 ~/.sciagent/bin/ (Linux/Mac)
|
|
9
|
+
* - 带严格版本校验(.version文件必须匹配CURRENT_VERSION)
|
|
10
|
+
* - 版本不匹配时自动从极狐GitLab下载正确版本
|
|
11
|
+
* 2. 本地开发目录(仅开发模式)
|
|
12
|
+
* 3. 未找到时自动从极狐GitLab下载
|
|
13
|
+
*
|
|
14
|
+
* 注意:不再从npm optionalDependencies中查找二进制,因为:
|
|
15
|
+
* - npm缓存中的旧包可能包含旧版二进制
|
|
16
|
+
* - npm可能修改package.json版本号来匹配请求,但二进制文件仍是旧的
|
|
17
|
+
* - 超过250MB的包无法发布到npm
|
|
6
18
|
*/
|
|
7
19
|
|
|
8
20
|
const { spawn, execSync } = require('child_process');
|
|
9
21
|
const path = require('path');
|
|
10
22
|
const fs = require('fs');
|
|
11
23
|
const os = require('os');
|
|
12
|
-
const https = require('https');
|
|
13
|
-
const http = require('http');
|
|
14
24
|
|
|
15
25
|
// 当前版本号 - 与 postinstall.js 和 package.json 保持同步
|
|
16
|
-
const CURRENT_VERSION = '1.0.
|
|
26
|
+
const CURRENT_VERSION = '1.0.86';
|
|
17
27
|
|
|
18
28
|
// 极狐GitLab下载配置
|
|
19
29
|
const JIHULAB_URL = 'https://jihulab.com';
|
|
@@ -50,6 +60,15 @@ function compareVersions(a, b) {
|
|
|
50
60
|
return 0;
|
|
51
61
|
}
|
|
52
62
|
|
|
63
|
+
/**
|
|
64
|
+
* 获取二进制安装目录
|
|
65
|
+
*/
|
|
66
|
+
function getInstallDir(platform) {
|
|
67
|
+
return platform === 'win32'
|
|
68
|
+
? path.join(process.env.LOCALAPPDATA || os.homedir(), 'sciagent', 'bin')
|
|
69
|
+
: path.join(os.homedir(), '.sciagent', 'bin');
|
|
70
|
+
}
|
|
71
|
+
|
|
53
72
|
/**
|
|
54
73
|
* 获取当前平台的二进制文件路径
|
|
55
74
|
*/
|
|
@@ -69,102 +88,90 @@ function getBinaryPath() {
|
|
|
69
88
|
process.exit(1);
|
|
70
89
|
}
|
|
71
90
|
|
|
72
|
-
// 构建包名
|
|
73
|
-
const packageName = `@sciagent/cli-${platform}-${arch}`;
|
|
74
|
-
|
|
75
|
-
// 尝试从node_modules中查找
|
|
76
91
|
const binName = platform === 'win32' ? 'sciagent.exe' : 'sciagent';
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const stats = fs.statSync(homeBinPath);
|
|
92
|
+
const installDir = getInstallDir(platform);
|
|
93
|
+
const binPath = path.join(installDir, binName);
|
|
94
|
+
const versionFile = path.join(installDir, '.version');
|
|
95
|
+
|
|
96
|
+
// 检查已安装的二进制
|
|
97
|
+
if (fs.existsSync(binPath)) {
|
|
98
|
+
const stats = fs.statSync(binPath);
|
|
85
99
|
if (stats.size > 10 * 1024 * 1024) {
|
|
86
|
-
//
|
|
87
|
-
const versionFile = path.join(homeBinDir, '.version');
|
|
100
|
+
// 读取版本文件
|
|
88
101
|
if (fs.existsSync(versionFile)) {
|
|
89
102
|
const installedVersion = fs.readFileSync(versionFile, 'utf8').trim();
|
|
90
103
|
const cmp = compareVersions(installedVersion, CURRENT_VERSION);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
console.log(`[INFO]
|
|
95
|
-
|
|
104
|
+
|
|
105
|
+
if (cmp === 0) {
|
|
106
|
+
// 版本精确匹配 → 直接使用
|
|
107
|
+
console.log(`[INFO] SciAgent v${installedVersion} (installed)`);
|
|
108
|
+
return binPath;
|
|
109
|
+
} else if (cmp < 0) {
|
|
110
|
+
// 版本过低 → 自动下载新版本
|
|
111
|
+
console.log(`[INFO] SciAgent version outdated: v${installedVersion} → v${CURRENT_VERSION}`);
|
|
112
|
+
console.log(`[INFO] Downloading new version from JiHuLab...`);
|
|
113
|
+
try { fs.unlinkSync(binPath); fs.unlinkSync(versionFile); } catch (e) {}
|
|
96
114
|
const downloaded = downloadBinary(platform, arch, CURRENT_VERSION);
|
|
97
115
|
if (downloaded) return downloaded;
|
|
98
116
|
console.error(`[ERROR] Failed to download v${CURRENT_VERSION}. Please run: npm install -g @sciagent/cli`);
|
|
99
117
|
process.exit(1);
|
|
100
|
-
} else
|
|
101
|
-
//
|
|
102
|
-
console.log(`[INFO]
|
|
118
|
+
} else {
|
|
119
|
+
// 版本更高 → 允许运行(用户可能手动安装了新版)
|
|
120
|
+
console.log(`[INFO] SciAgent v${installedVersion} (newer than package v${CURRENT_VERSION})`);
|
|
121
|
+
return binPath;
|
|
103
122
|
}
|
|
104
|
-
// cmp === 0 → 版本匹配,正常使用
|
|
105
123
|
} else {
|
|
106
|
-
//
|
|
107
|
-
console.log(`[INFO]
|
|
108
|
-
try { fs.unlinkSync(
|
|
124
|
+
// 没有.version文件 → 可能是旧安装,重新下载
|
|
125
|
+
console.log(`[INFO] SciAgent binary found but version unknown, downloading v${CURRENT_VERSION}...`);
|
|
126
|
+
try { fs.unlinkSync(binPath); } catch (e) {}
|
|
109
127
|
const downloaded = downloadBinary(platform, arch, CURRENT_VERSION);
|
|
110
128
|
if (downloaded) return downloaded;
|
|
111
129
|
console.error(`[ERROR] Failed to download v${CURRENT_VERSION}. Please run: npm install -g @sciagent/cli`);
|
|
112
130
|
process.exit(1);
|
|
113
131
|
}
|
|
114
|
-
|
|
132
|
+
} else {
|
|
133
|
+
// 文件太小,损坏
|
|
134
|
+
console.log(`[INFO] SciAgent binary too small (${(stats.size / 1024).toFixed(0)} KB), removing...`);
|
|
135
|
+
try { fs.unlinkSync(binPath); } catch (e) {}
|
|
115
136
|
}
|
|
116
137
|
}
|
|
117
138
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
// 必须严格校验版本,版本不匹配则跳过并触发下载
|
|
139
|
+
// 检查 npm optionalDependencies 包路径
|
|
140
|
+
const packageName = `@sciagent/cli-${platform}-${arch}`;
|
|
121
141
|
try {
|
|
122
|
-
const
|
|
123
|
-
if (fs.existsSync(
|
|
124
|
-
const stats = fs.statSync(
|
|
142
|
+
const npmBinPath = require.resolve(`${packageName}/bin/${binName}`);
|
|
143
|
+
if (fs.existsSync(npmBinPath)) {
|
|
144
|
+
const stats = fs.statSync(npmBinPath);
|
|
125
145
|
if (stats.size > 10 * 1024 * 1024) {
|
|
126
|
-
|
|
146
|
+
console.log(`[INFO] SciAgent v${CURRENT_VERSION} (npm package)`);
|
|
147
|
+
// 复制到本地安装目录以便后续版本检查
|
|
127
148
|
try {
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (cmp !== 0) {
|
|
133
|
-
// 版本不匹配(无论高低)→ 跳过,下载正确版本
|
|
134
|
-
console.log(`[INFO] npm package binary version mismatch: ${packageName}@${pkgVersion} != ${CURRENT_VERSION}, will download correct version`);
|
|
135
|
-
// 不return,继续到下载逻辑
|
|
136
|
-
} else {
|
|
137
|
-
// 版本精确匹配,使用
|
|
138
|
-
console.log(`[INFO] Using npm package binary: ${packageName}@${pkgVersion}`);
|
|
139
|
-
return packagePath;
|
|
140
|
-
}
|
|
149
|
+
fs.mkdirSync(installDir, { recursive: true });
|
|
150
|
+
const localCopy = path.join(installDir, binName);
|
|
151
|
+
fs.copyFileSync(npmBinPath, localCopy);
|
|
152
|
+
fs.writeFileSync(path.join(installDir, '.version'), CURRENT_VERSION);
|
|
141
153
|
} catch (e) {
|
|
142
|
-
//
|
|
143
|
-
console.log(`[INFO] Cannot read package version from ${packageName}, skipping npm binary`);
|
|
154
|
+
// 复制失败不影响运行,直接使用npm包路径
|
|
144
155
|
}
|
|
145
|
-
|
|
146
|
-
console.log(`[INFO] npm package binary too small (${(stats.size / 1024).toFixed(0)} KB), likely a stub`);
|
|
156
|
+
return npmBinPath;
|
|
147
157
|
}
|
|
148
158
|
}
|
|
149
159
|
} catch (e) {
|
|
150
|
-
//
|
|
160
|
+
// npm 包未安装
|
|
151
161
|
}
|
|
152
162
|
|
|
153
|
-
//
|
|
163
|
+
// 开发模式:从本地packages目录查找
|
|
154
164
|
const localPath = path.join(__dirname, '..', 'packages', `sciagent-${platform}-${arch}`, 'bin', binName);
|
|
155
165
|
if (fs.existsSync(localPath)) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (fs.existsSync(binPath)) {
|
|
162
|
-
return binPath;
|
|
166
|
+
const stats = fs.statSync(localPath);
|
|
167
|
+
if (stats.size > 10 * 1024 * 1024) {
|
|
168
|
+
console.log(`[INFO] SciAgent (dev mode)`);
|
|
169
|
+
return localPath;
|
|
170
|
+
}
|
|
163
171
|
}
|
|
164
172
|
|
|
165
|
-
//
|
|
166
|
-
console.log(
|
|
167
|
-
console.log(` Attempting to download v${CURRENT_VERSION} from JiHuLab...`);
|
|
173
|
+
// 未找到二进制文件 - 自动从极狐GitLab下载
|
|
174
|
+
console.log(`[INFO] SciAgent binary not found, downloading v${CURRENT_VERSION} from JiHuLab...`);
|
|
168
175
|
|
|
169
176
|
const downloaded = downloadBinary(platform, arch, CURRENT_VERSION);
|
|
170
177
|
if (downloaded && fs.existsSync(downloaded)) {
|
|
@@ -190,9 +197,7 @@ function downloadBinary(platform, arch, version) {
|
|
|
190
197
|
`?access_token=${JIHULAB_DOWNLOAD_TOKEN}`
|
|
191
198
|
);
|
|
192
199
|
|
|
193
|
-
const installDir = platform
|
|
194
|
-
? path.join(process.env.LOCALAPPDATA || os.homedir(), 'sciagent', 'bin')
|
|
195
|
-
: path.join(os.homedir(), '.sciagent', 'bin');
|
|
200
|
+
const installDir = getInstallDir(platform);
|
|
196
201
|
fs.mkdirSync(installDir, { recursive: true });
|
|
197
202
|
const targetPath = path.join(installDir, binName);
|
|
198
203
|
|
|
@@ -200,21 +205,32 @@ function downloadBinary(platform, arch, version) {
|
|
|
200
205
|
console.log(` Target: ${targetPath}`);
|
|
201
206
|
|
|
202
207
|
try {
|
|
203
|
-
// 同步下载(使用curl或PowerShell)
|
|
204
208
|
if (platform === 'win32') {
|
|
205
209
|
// Windows: 使用 PowerShell 下载
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
210
|
+
// 将URL和目标路径写入临时PS1脚本文件,避免命令行引号/特殊字符问题
|
|
211
|
+
const tmpScript = path.join(os.tmpdir(), 'sciagent-download.ps1');
|
|
212
|
+
const scriptContent = [
|
|
213
|
+
'$ProgressPreference = "SilentlyContinue"',
|
|
214
|
+
`$uri = "${downloadUrl}"`,
|
|
215
|
+
`$out = "${targetPath}"`,
|
|
216
|
+
'Write-Host " Downloading from $uri..."',
|
|
217
|
+
'Invoke-WebRequest -Uri $uri -OutFile $out -UseBasicParsing',
|
|
218
|
+
'if (Test-Path $out) { $s = (Get-Item $out).Length; Write-Host " Downloaded: $s bytes" } else { Write-Error "File not created"; exit 1 }'
|
|
219
|
+
].join('\r\n');
|
|
220
|
+
fs.writeFileSync(tmpScript, scriptContent, 'utf8');
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
execSync(`powershell -NoProfile -ExecutionPolicy Bypass -File "${tmpScript}"`, {
|
|
224
|
+
stdio: 'inherit',
|
|
225
|
+
timeout: 600000
|
|
226
|
+
});
|
|
227
|
+
} finally {
|
|
228
|
+
try { fs.unlinkSync(tmpScript); } catch (e) {}
|
|
229
|
+
}
|
|
214
230
|
} else {
|
|
215
231
|
// Linux/macOS: 使用 curl
|
|
216
232
|
execSync(`curl -fsSL -o '${targetPath}' '${downloadUrl}'`, {
|
|
217
|
-
stdio: '
|
|
233
|
+
stdio: 'inherit',
|
|
218
234
|
timeout: 600000
|
|
219
235
|
});
|
|
220
236
|
fs.chmodSync(targetPath, 0o755);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sciagent/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.86",
|
|
4
4
|
"description": "SciAgent CLI - AI Research Assistant",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"postinstall": "node scripts/postinstall.js"
|
|
16
16
|
},
|
|
17
17
|
"optionalDependencies": {
|
|
18
|
-
"@sciagent/cli-linux-x64": "1.0.
|
|
19
|
-
"@sciagent/cli-linux-arm64": "1.0.
|
|
20
|
-
"@sciagent/cli-darwin-x64": "1.0.
|
|
21
|
-
"@sciagent/cli-darwin-arm64": "1.0.
|
|
22
|
-
"@sciagent/cli-win32-x64": "1.0.
|
|
23
|
-
"@sciagent/cli-win32-arm64": "1.0.
|
|
18
|
+
"@sciagent/cli-linux-x64": "1.0.86",
|
|
19
|
+
"@sciagent/cli-linux-arm64": "1.0.86",
|
|
20
|
+
"@sciagent/cli-darwin-x64": "1.0.86",
|
|
21
|
+
"@sciagent/cli-darwin-arm64": "1.0.86",
|
|
22
|
+
"@sciagent/cli-win32-x64": "1.0.86",
|
|
23
|
+
"@sciagent/cli-win32-arm64": "1.0.86"
|
|
24
24
|
},
|
|
25
25
|
"keywords": [
|
|
26
26
|
"ai",
|
package/scripts/postinstall.js
CHANGED
|
@@ -27,7 +27,7 @@ const ARCH_MAP = {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
// 当前版本号 - 每次发布时同步更新
|
|
30
|
-
const CURRENT_VERSION = '1.0.
|
|
30
|
+
const CURRENT_VERSION = '1.0.86';
|
|
31
31
|
|
|
32
32
|
// GitHub Releases 回退版本列表(当当前版本的 release 不存在时尝试)
|
|
33
33
|
const GITHUB_FALLBACK_VERSIONS = ['1.0.50', '1.0.48', '1.0.46', '1.0.40', '1.0.38', '1.0.36'];
|
|
@@ -182,81 +182,118 @@ function checkCodebuddyBinaryInstalled() {
|
|
|
182
182
|
function downloadFile(url, destPath, timeout = 120000) {
|
|
183
183
|
return new Promise((resolve, reject) => {
|
|
184
184
|
const protocol = url.startsWith('https') ? https : http;
|
|
185
|
+
|
|
186
|
+
// 确保目标目录存在
|
|
187
|
+
const destDir = path.dirname(destPath);
|
|
188
|
+
if (!fs.existsSync(destDir)) {
|
|
189
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
190
|
+
}
|
|
191
|
+
|
|
185
192
|
const file = fs.createWriteStream(destPath);
|
|
186
193
|
let completed = false;
|
|
194
|
+
let downloadedSize = 0;
|
|
187
195
|
|
|
188
196
|
const timer = setTimeout(() => {
|
|
189
197
|
if (!completed) {
|
|
198
|
+
completed = true;
|
|
190
199
|
file.close();
|
|
191
200
|
try { fs.unlinkSync(destPath); } catch (e) {}
|
|
192
|
-
reject(new Error(
|
|
201
|
+
reject(new Error(`Download timeout after ${timeout / 1000}s (${(downloadedSize / (1024 * 1024)).toFixed(1)} MB downloaded)`));
|
|
193
202
|
}
|
|
194
203
|
}, timeout);
|
|
195
204
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
downloadFile(response.headers.location, destPath, timeout).then(resolve).catch(reject);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (response.statusCode !== 200) {
|
|
210
|
-
file.close();
|
|
211
|
-
try { fs.unlinkSync(destPath); } catch (e) {}
|
|
212
|
-
clearTimeout(timer);
|
|
213
|
-
reject(new Error(`HTTP ${response.statusCode}`));
|
|
205
|
+
function doRequest(requestUrl, redirectCount = 0) {
|
|
206
|
+
if (redirectCount > 5) {
|
|
207
|
+
if (!completed) {
|
|
208
|
+
completed = true;
|
|
209
|
+
clearTimeout(timer);
|
|
210
|
+
file.close();
|
|
211
|
+
try { fs.unlinkSync(destPath); } catch (e) {}
|
|
212
|
+
reject(new Error('Too many redirects'));
|
|
213
|
+
}
|
|
214
214
|
return;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
217
|
+
const request = protocol.get(requestUrl, {
|
|
218
|
+
headers: { 'User-Agent': 'sciagent-cli/1.0' },
|
|
219
|
+
timeout: 60000
|
|
220
|
+
}, (response) => {
|
|
221
|
+
// Handle redirects
|
|
222
|
+
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
223
|
+
response.resume(); // drain the response
|
|
224
|
+
let location = response.headers.location;
|
|
225
|
+
// Handle relative redirects
|
|
226
|
+
if (location.startsWith('/')) {
|
|
227
|
+
const parsed = new URL(requestUrl);
|
|
228
|
+
location = `${parsed.protocol}//${parsed.host}${location}`;
|
|
229
|
+
}
|
|
230
|
+
doRequest(location, redirectCount + 1);
|
|
231
|
+
return;
|
|
227
232
|
}
|
|
233
|
+
|
|
234
|
+
if (response.statusCode !== 200) {
|
|
235
|
+
response.resume(); // drain the response
|
|
236
|
+
if (!completed) {
|
|
237
|
+
completed = true;
|
|
238
|
+
clearTimeout(timer);
|
|
239
|
+
file.close();
|
|
240
|
+
try { fs.unlinkSync(destPath); } catch (e) {}
|
|
241
|
+
reject(new Error(`HTTP ${response.statusCode} for ${requestUrl}`));
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const totalSize = parseInt(response.headers['content-length'], 10);
|
|
247
|
+
|
|
248
|
+
response.on('data', (chunk) => {
|
|
249
|
+
downloadedSize += chunk.length;
|
|
250
|
+
if (totalSize) {
|
|
251
|
+
const percent = Math.floor((downloadedSize / totalSize) * 100);
|
|
252
|
+
const mb = (downloadedSize / (1024 * 1024)).toFixed(1);
|
|
253
|
+
const totalMb = (totalSize / (1024 * 1024)).toFixed(1);
|
|
254
|
+
process.stdout.write(`\r 下载进度: ${percent}% (${mb}/${totalMb} MB)`);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
response.pipe(file);
|
|
259
|
+
|
|
260
|
+
file.on('finish', () => {
|
|
261
|
+
if (!completed) {
|
|
262
|
+
completed = true;
|
|
263
|
+
clearTimeout(timer);
|
|
264
|
+
file.close();
|
|
265
|
+
console.log(); // New line after progress
|
|
266
|
+
resolve();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
file.on('error', (err) => {
|
|
271
|
+
if (!completed) {
|
|
272
|
+
completed = true;
|
|
273
|
+
clearTimeout(timer);
|
|
274
|
+
file.close();
|
|
275
|
+
try { fs.unlinkSync(destPath); } catch (e) {}
|
|
276
|
+
reject(err);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
228
279
|
});
|
|
229
280
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
281
|
+
request.on('error', (err) => {
|
|
282
|
+
if (!completed) {
|
|
283
|
+
completed = true;
|
|
284
|
+
clearTimeout(timer);
|
|
285
|
+
file.close();
|
|
286
|
+
try { fs.unlinkSync(destPath); } catch (e) {}
|
|
287
|
+
reject(new Error(`Network error: ${err.message} (${(downloadedSize / (1024 * 1024)).toFixed(1)} MB downloaded)`));
|
|
288
|
+
}
|
|
238
289
|
});
|
|
239
290
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
clearTimeout(timer);
|
|
243
|
-
file.close();
|
|
244
|
-
try { fs.unlinkSync(destPath); } catch (e) {}
|
|
245
|
-
reject(err);
|
|
291
|
+
request.on('timeout', () => {
|
|
292
|
+
request.destroy();
|
|
246
293
|
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
request.on('error', (err) => {
|
|
250
|
-
completed = true;
|
|
251
|
-
clearTimeout(timer);
|
|
252
|
-
file.close();
|
|
253
|
-
try { fs.unlinkSync(destPath); } catch (e) {}
|
|
254
|
-
reject(err);
|
|
255
|
-
});
|
|
294
|
+
}
|
|
256
295
|
|
|
257
|
-
|
|
258
|
-
request.destroy();
|
|
259
|
-
});
|
|
296
|
+
doRequest(url);
|
|
260
297
|
});
|
|
261
298
|
}
|
|
262
299
|
|
|
@@ -519,6 +556,46 @@ async function downloadFromJiHuLab(platform, arch, version) {
|
|
|
519
556
|
fs.mkdirSync(installDir, { recursive: true });
|
|
520
557
|
const targetPath = path.join(installDir, binName);
|
|
521
558
|
|
|
559
|
+
// Windows: 优先使用 PowerShell(更可靠的大文件下载)
|
|
560
|
+
if (platform === 'win32') {
|
|
561
|
+
try {
|
|
562
|
+
const tmpScript = path.join(os.tmpdir(), 'sciagent-download.ps1');
|
|
563
|
+
const scriptContent = [
|
|
564
|
+
'$ProgressPreference = "SilentlyContinue"',
|
|
565
|
+
`$uri = "${downloadUrl}"`,
|
|
566
|
+
`$out = "${targetPath}"`,
|
|
567
|
+
'Write-Host " [JiHuLab] Downloading from JiHuLab..."',
|
|
568
|
+
'Invoke-WebRequest -Uri $uri -OutFile $out -UseBasicParsing',
|
|
569
|
+
'if (Test-Path $out) { $s = (Get-Item $out).Length; Write-Host " [JiHuLab] Downloaded: $s bytes" } else { Write-Error "File not created"; exit 1 }'
|
|
570
|
+
].join('\r\n');
|
|
571
|
+
fs.writeFileSync(tmpScript, scriptContent, 'utf8');
|
|
572
|
+
|
|
573
|
+
try {
|
|
574
|
+
execSync(`powershell -NoProfile -ExecutionPolicy Bypass -File "${tmpScript}"`, {
|
|
575
|
+
stdio: 'inherit',
|
|
576
|
+
timeout: 600000
|
|
577
|
+
});
|
|
578
|
+
} finally {
|
|
579
|
+
try { fs.unlinkSync(tmpScript); } catch (e) {}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// 验证下载
|
|
583
|
+
if (fs.existsSync(targetPath)) {
|
|
584
|
+
const stats = fs.statSync(targetPath);
|
|
585
|
+
if (stats.size > 10 * 1024 * 1024) {
|
|
586
|
+
console.log(` [OK] JiHuLab download success: ${targetPath} (${(stats.size / (1024 * 1024)).toFixed(1)} MB)`);
|
|
587
|
+
fs.writeFileSync(path.join(installDir, '.version'), version);
|
|
588
|
+
return true;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
console.log(` [WARN] PowerShell download validation failed, trying Node.js...`);
|
|
593
|
+
} catch (e) {
|
|
594
|
+
console.log(` [WARN] PowerShell download failed: ${e.message}, trying Node.js...`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// 通用方式: Node.js https 下载
|
|
522
599
|
try {
|
|
523
600
|
await downloadFile(downloadUrl, targetPath, 600000); // 10 分钟超时
|
|
524
601
|
|