@sciagent/cli 1.3.15 → 1.3.17

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 CHANGED
@@ -25,7 +25,7 @@ const https = require('https');
25
25
  const http = require('http');
26
26
 
27
27
  // 当前版本号 - 与 postinstall.js 和 package.json 保持同步
28
- const CURRENT_VERSION = '1.3.15';
28
+ const CURRENT_VERSION = '1.3.17';
29
29
 
30
30
  // Releases 下载源配置(优先级从高到低)
31
31
  // JihuLab 通用包仓库(国内 CDN,速度快)作为主源
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sciagent/cli",
3
- "version": "1.3.15",
3
+ "version": "1.3.17",
4
4
  "description": "SciAgent CLI - AI Research Assistant",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -27,7 +27,7 @@ const ARCH_MAP = {
27
27
  };
28
28
 
29
29
  // 当前版本号 - 每次发布时同步更新
30
- const CURRENT_VERSION = '1.3.15';
30
+ const CURRENT_VERSION = '1.3.17';
31
31
 
32
32
  // GitHub Releases 回退版本列表(当当前版本的 release 不存在时尝试)
33
33
  const GITHUB_FALLBACK_VERSIONS = ['1.1.3', '1.0.50', '1.0.48', '1.0.46', '1.0.40', '1.0.38', '1.0.36'];
@@ -185,6 +185,57 @@ function _removeBinaryFiles(binDir, binPath) {
185
185
  return allRemoved;
186
186
  }
187
187
 
188
+ /**
189
+ * 校验 Windows PE 可执行文件架构(AMD64 = 0x8664)
190
+ * 解析 MZ 头 → 0x3C 处 PE 偏移 → "PE\0\0" 签名 → Machine 字段。
191
+ * 非 Windows 或非 .exe 返回 true(跳过校验)。
192
+ * 返回 { ok: boolean, desc: string }。校验失败时会给出可操作提示。
193
+ */
194
+ function checkPeArchitecture(binaryPath) {
195
+ if (process.platform !== 'win32') return { ok: true, desc: 'non-win32 skip' };
196
+ let fd = null;
197
+ try {
198
+ fd = fs.openSync(binaryPath, 'r');
199
+ const mz = Buffer.alloc(2);
200
+ fs.readSync(fd, mz, 0, 2, 0);
201
+ if (mz.toString('latin1') !== 'MZ') {
202
+ return { ok: false, desc: '文件损坏:缺少 MZ 头(不是有效的 EXE,可能下载/解压不完整)' };
203
+ }
204
+ const peOffBuf = Buffer.alloc(4);
205
+ fs.readSync(fd, peOffBuf, 0, 4, 0x3C);
206
+ const peOffset = peOffBuf.readUInt32LE(0);
207
+ const peSig = Buffer.alloc(4);
208
+ fs.readSync(fd, peSig, 0, 4, peOffset);
209
+ if (peSig.toString('latin1') !== 'PE\u0000\u0000') {
210
+ return { ok: false, desc: '文件损坏:缺少 PE 签名(可能下载/解压不完整)' };
211
+ }
212
+ const machineBuf = Buffer.alloc(2);
213
+ fs.readSync(fd, machineBuf, 0, 2, peOffset + 4);
214
+ const machine = machineBuf.readUInt16LE(0);
215
+ if (machine === 0x8664) return { ok: true, desc: 'AMD64 (x64)' };
216
+ if (machine === 0x14C) return { ok: false, desc: '架构不匹配:EXE 是 32 位 (i386),当前系统是 64 位(WinError 216)' };
217
+ if (machine === 0xAA64) return { ok: false, desc: `架构不匹配:EXE 是 ARM64,当前系统是 AMD64(WinError 216)` };
218
+ return { ok: false, desc: `架构不匹配:未知 Machine=0x${machine.toString(16).toUpperCase().padStart(4, '0')}(WinError 216)` };
219
+ } catch (e) {
220
+ return { ok: false, desc: `无法读取 EXE 头: ${e.message}` };
221
+ } finally {
222
+ if (fd !== null) { try { fs.closeSync(fd); } catch (e) {} }
223
+ }
224
+ }
225
+
226
+ /**
227
+ * 校验 SDK 二进制文件是否有效(大小 + PE 架构 + 完整性)
228
+ * 校验失败时返回 false(调用方应删除重下)
229
+ */
230
+ function validateCodebuddyBinary(binaryPath) {
231
+ if (!fs.existsSync(binaryPath)) return { ok: false, desc: '文件不存在' };
232
+ const stats = fs.statSync(binaryPath);
233
+ if (stats.size <= 10 * 1024 * 1024) {
234
+ return { ok: false, desc: `文件太小 (${(stats.size / 1024).toFixed(0)} KB < 10 MB),可能是损坏文件` };
235
+ }
236
+ return checkPeArchitecture(binaryPath);
237
+ }
238
+
188
239
  function checkCodebuddyBinaryInstalled() {
189
240
  const binDir = getCodebuddyBinDir();
190
241
  const binaryName = BINARY_NAMES[process.platform] || 'codebuddy-headless';
@@ -194,6 +245,16 @@ function checkCodebuddyBinaryInstalled() {
194
245
  const stats = fs.statSync(binaryPath);
195
246
  // 检查文件大小是否合理(至少10MB,防止损坏的文件)
196
247
  if (stats.size > 10 * 1024 * 1024) {
248
+ // 额外校验 PE 架构与完整性(防 WinError 216/1392)
249
+ const check = checkPeArchitecture(binaryPath);
250
+ if (!check.ok) {
251
+ console.log(` ⚠️ 已存在的 SDK 校验失败: ${check.desc}`);
252
+ console.log(` 删除 ${binaryPath} 并重新下载...`);
253
+ try { fs.unlinkSync(binaryPath); } catch (e) {
254
+ console.log(` 删除失败: ${e.message}`);
255
+ }
256
+ return { installed: false };
257
+ }
197
258
  return { installed: true, path: binaryPath, size: stats.size };
198
259
  }
199
260
  }
@@ -468,9 +529,18 @@ sys.exit(1)
468
529
 
469
530
  if (fs.existsSync(targetPath)) {
470
531
  const stats = fs.statSync(targetPath);
532
+ // 提取后立即校验 PE 架构与完整性(防 WinError 216/1392)
533
+ const check = validateCodebuddyBinary(targetPath);
534
+ if (!check.ok) {
535
+ console.log(` ⚠️ 提取的 SDK 二进制校验失败: ${check.desc}`);
536
+ try { fs.unlinkSync(targetPath); } catch (e) {}
537
+ try { fs.unlinkSync(wheelPath); fs.unlinkSync(scriptPath); fs.rmdirSync(tmpDir); } catch (e) {}
538
+ throw new Error(`SDK 二进制校验失败: ${check.desc}`);
539
+ }
471
540
  console.log(`✅ CodeBuddy SDK 安装成功!(PyPI wheel)`);
472
541
  console.log(` 路径: ${targetPath}`);
473
542
  console.log(` 大小: ${(stats.size / (1024 * 1024)).toFixed(1)} MB`);
543
+ console.log(` 架构: ${check.desc}`);
474
544
 
475
545
  try { fs.unlinkSync(wheelPath); fs.unlinkSync(scriptPath); fs.rmdirSync(tmpDir); } catch (e) {}
476
546
  return true;
@@ -538,8 +608,16 @@ sys.exit(1)
538
608
  if (platform !== 'win32') {
539
609
  fs.chmodSync(targetPath, 0o755);
540
610
  }
611
+ // 校验 PE 架构与完整性(防 WinError 216/1392)
612
+ const check = checkPeArchitecture(targetPath);
613
+ if (!check.ok) {
614
+ console.log(` ⚠️ 下载的 SDK 二进制校验失败: ${check.desc}`);
615
+ try { fs.unlinkSync(targetPath); } catch (e) {}
616
+ throw new Error(`SDK 二进制校验失败: ${check.desc}`);
617
+ }
541
618
  console.log(`✅ CodeBuddy SDK 安装成功!(Releases 服务器)`);
542
619
  console.log(` 路径: ${targetPath}`);
620
+ console.log(` 架构: ${check.desc}`);
543
621
  return true;
544
622
  }
545
623
  }