@sciagent/cli 1.0.85 → 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 +46 -11
- package/package.json +7 -7
- package/scripts/postinstall.js +133 -56
package/bin/sciagent.js
CHANGED
|
@@ -23,7 +23,7 @@ const fs = require('fs');
|
|
|
23
23
|
const os = require('os');
|
|
24
24
|
|
|
25
25
|
// 当前版本号 - 与 postinstall.js 和 package.json 保持同步
|
|
26
|
-
const CURRENT_VERSION = '1.0.
|
|
26
|
+
const CURRENT_VERSION = '1.0.86';
|
|
27
27
|
|
|
28
28
|
// 极狐GitLab下载配置
|
|
29
29
|
const JIHULAB_URL = 'https://jihulab.com';
|
|
@@ -136,6 +136,30 @@ function getBinaryPath() {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
// 检查 npm optionalDependencies 包路径
|
|
140
|
+
const packageName = `@sciagent/cli-${platform}-${arch}`;
|
|
141
|
+
try {
|
|
142
|
+
const npmBinPath = require.resolve(`${packageName}/bin/${binName}`);
|
|
143
|
+
if (fs.existsSync(npmBinPath)) {
|
|
144
|
+
const stats = fs.statSync(npmBinPath);
|
|
145
|
+
if (stats.size > 10 * 1024 * 1024) {
|
|
146
|
+
console.log(`[INFO] SciAgent v${CURRENT_VERSION} (npm package)`);
|
|
147
|
+
// 复制到本地安装目录以便后续版本检查
|
|
148
|
+
try {
|
|
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);
|
|
153
|
+
} catch (e) {
|
|
154
|
+
// 复制失败不影响运行,直接使用npm包路径
|
|
155
|
+
}
|
|
156
|
+
return npmBinPath;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} catch (e) {
|
|
160
|
+
// npm 包未安装
|
|
161
|
+
}
|
|
162
|
+
|
|
139
163
|
// 开发模式:从本地packages目录查找
|
|
140
164
|
const localPath = path.join(__dirname, '..', 'packages', `sciagent-${platform}-${arch}`, 'bin', binName);
|
|
141
165
|
if (fs.existsSync(localPath)) {
|
|
@@ -181,21 +205,32 @@ function downloadBinary(platform, arch, version) {
|
|
|
181
205
|
console.log(` Target: ${targetPath}`);
|
|
182
206
|
|
|
183
207
|
try {
|
|
184
|
-
// 同步下载(使用curl或PowerShell)
|
|
185
208
|
if (platform === 'win32') {
|
|
186
209
|
// Windows: 使用 PowerShell 下载
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
+
}
|
|
195
230
|
} else {
|
|
196
231
|
// Linux/macOS: 使用 curl
|
|
197
232
|
execSync(`curl -fsSL -o '${targetPath}' '${downloadUrl}'`, {
|
|
198
|
-
stdio: '
|
|
233
|
+
stdio: 'inherit',
|
|
199
234
|
timeout: 600000
|
|
200
235
|
});
|
|
201
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
|
|