@sciagent/cli 1.0.34 → 1.0.38

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sciagent/cli",
3
- "version": "1.0.34",
3
+ "version": "1.0.38",
4
4
  "description": "SciAgent CLI - AI Research Assistant",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -19,7 +19,7 @@
19
19
  "@sciagent/cli-linux-arm64": "1.0.33",
20
20
  "@sciagent/cli-darwin-x64": "1.0.33",
21
21
  "@sciagent/cli-darwin-arm64": "1.0.33",
22
- "@sciagent/cli-win32-x64": "1.0.34",
22
+ "@sciagent/cli-win32-x64": "1.0.36",
23
23
  "@sciagent/cli-win32-arm64": "1.0.33"
24
24
  },
25
25
  "keywords": [
@@ -2,12 +2,16 @@
2
2
 
3
3
  /**
4
4
  * SciAgent CLI postinstall 脚本
5
- * 自动检测并安装平台特定的二进制包
5
+ * 1. 自动检测并安装平台特定的二进制包
6
+ * 2. 自动下载 CodeBuddy SDK 二进制文件(使用国内镜像源)
6
7
  */
7
8
 
8
9
  const { execSync } = require('child_process');
9
10
  const path = require('path');
10
11
  const fs = require('fs');
12
+ const https = require('https');
13
+ const http = require('http');
14
+ const os = require('os');
11
15
 
12
16
  // 平台和架构映射
13
17
  const PLATFORM_MAP = {
@@ -23,7 +27,49 @@ const ARCH_MAP = {
23
27
  };
24
28
 
25
29
  // 当前版本号 - 每次发布时同步更新
26
- const CURRENT_VERSION = '1.0.33';
30
+ const CURRENT_VERSION = '1.0.38';
31
+
32
+ // PyPI 镜像源列表(国内优先)
33
+ const PYPI_MIRRORS = [
34
+ 'https://mirrors.cloud.tencent.com/pypi/simple', // 腾讯云
35
+ 'https://mirrors.aliyun.com/pypi/simple', // 阿里云
36
+ 'https://pypi.tuna.tsinghua.edu.cn/simple', // 清华
37
+ 'https://pypi.org/simple' // 官方(备用)
38
+ ];
39
+
40
+ // PyPI 下载URL的镜像(直接下载文件)
41
+ const PYPI_DOWNLOAD_MIRRORS = [
42
+ 'https://mirrors.cloud.tencent.com/pypi/packages', // 腾讯云
43
+ 'https://mirrors.aliyun.com/pypi/packages', // 阿里云
44
+ 'https://pypi.tuna.tsinghua.edu.cn/packages', // 清华
45
+ 'https://files.pythonhosted.org/packages' // 官方(备用)
46
+ ];
47
+
48
+ // PyPI wheel 平台标识映射
49
+ const PYPI_PLATFORM_MAP = {
50
+ 'win32-x64': 'win_amd64',
51
+ 'darwin-arm64': 'macosx_11_0_arm64',
52
+ 'darwin-x64': 'macosx_10_12_x86_64',
53
+ 'linux-x64': 'manylinux_2_17_x86_64',
54
+ 'linux-arm64': 'manylinux_2_17_aarch64'
55
+ };
56
+
57
+ // 二进制文件名
58
+ const BINARY_NAMES = {
59
+ win32: 'codebuddy-headless.exe',
60
+ darwin: 'codebuddy-headless',
61
+ linux: 'codebuddy-headless'
62
+ };
63
+
64
+ function getCodebuddyBinDir() {
65
+ const platform = process.platform;
66
+ if (platform === 'win32') {
67
+ const base = process.env.LOCALAPPDATA || os.homedir();
68
+ return path.join(base, 'sciagent', 'bin');
69
+ } else {
70
+ return path.join(os.homedir(), '.sciagent', 'bin');
71
+ }
72
+ }
27
73
 
28
74
  function checkBinaryInstalled(platform, arch) {
29
75
  const packageName = `@sciagent/cli-${platform}-${arch}`;
@@ -37,43 +83,328 @@ function checkBinaryInstalled(platform, arch) {
37
83
  }
38
84
  }
39
85
 
40
- function installBinaryPackage(platform, arch) {
41
- const packageName = `@sciagent/cli-${platform}-${arch}@${CURRENT_VERSION}`;
86
+ function checkCodebuddyBinaryInstalled() {
87
+ const binDir = getCodebuddyBinDir();
88
+ const binaryName = BINARY_NAMES[process.platform] || 'codebuddy-headless';
89
+ const binaryPath = path.join(binDir, binaryName);
90
+
91
+ if (fs.existsSync(binaryPath)) {
92
+ const stats = fs.statSync(binaryPath);
93
+ // 检查文件大小是否合理(至少10MB,防止损坏的文件)
94
+ if (stats.size > 10 * 1024 * 1024) {
95
+ return { installed: true, path: binaryPath, size: stats.size };
96
+ }
97
+ }
98
+ return { installed: false };
99
+ }
100
+
101
+ function downloadFile(url, destPath, timeout = 120000) {
102
+ return new Promise((resolve, reject) => {
103
+ const protocol = url.startsWith('https') ? https : http;
104
+ const file = fs.createWriteStream(destPath);
105
+ let completed = false;
106
+
107
+ const timer = setTimeout(() => {
108
+ if (!completed) {
109
+ file.close();
110
+ try { fs.unlinkSync(destPath); } catch (e) {}
111
+ reject(new Error('Download timeout'));
112
+ }
113
+ }, timeout);
114
+
115
+ const request = protocol.get(url, {
116
+ headers: { 'User-Agent': 'sciagent-cli/1.0' },
117
+ timeout: 30000
118
+ }, (response) => {
119
+ // Handle redirects
120
+ if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
121
+ file.close();
122
+ try { fs.unlinkSync(destPath); } catch (e) {}
123
+ clearTimeout(timer);
124
+ downloadFile(response.headers.location, destPath, timeout).then(resolve).catch(reject);
125
+ return;
126
+ }
127
+
128
+ if (response.statusCode !== 200) {
129
+ file.close();
130
+ try { fs.unlinkSync(destPath); } catch (e) {}
131
+ clearTimeout(timer);
132
+ reject(new Error(`HTTP ${response.statusCode}`));
133
+ return;
134
+ }
135
+
136
+ const totalSize = parseInt(response.headers['content-length'], 10);
137
+ let downloadedSize = 0;
138
+
139
+ response.on('data', (chunk) => {
140
+ downloadedSize += chunk.length;
141
+ if (totalSize) {
142
+ const percent = Math.floor((downloadedSize / totalSize) * 100);
143
+ const mb = (downloadedSize / (1024 * 1024)).toFixed(1);
144
+ const totalMb = (totalSize / (1024 * 1024)).toFixed(1);
145
+ process.stdout.write(`\r 下载进度: ${percent}% (${mb}/${totalMb} MB)`);
146
+ }
147
+ });
148
+
149
+ response.pipe(file);
150
+
151
+ file.on('finish', () => {
152
+ completed = true;
153
+ clearTimeout(timer);
154
+ file.close();
155
+ console.log(); // New line after progress
156
+ resolve();
157
+ });
158
+
159
+ file.on('error', (err) => {
160
+ completed = true;
161
+ clearTimeout(timer);
162
+ file.close();
163
+ try { fs.unlinkSync(destPath); } catch (e) {}
164
+ reject(err);
165
+ });
166
+ });
167
+
168
+ request.on('error', (err) => {
169
+ completed = true;
170
+ clearTimeout(timer);
171
+ file.close();
172
+ try { fs.unlinkSync(destPath); } catch (e) {}
173
+ reject(err);
174
+ });
175
+
176
+ request.on('timeout', () => {
177
+ request.destroy();
178
+ });
179
+ });
180
+ }
181
+
182
+ async function getLatestSdkVersion() {
183
+ // 尝试从多个镜像获取版本信息
184
+ const mirrors = [
185
+ 'https://mirrors.cloud.tencent.com/pypi/pypi/codebuddy-agent-sdk/json',
186
+ 'https://mirrors.aliyun.com/pypi/pypi/codebuddy-agent-sdk/json',
187
+ 'https://pypi.tuna.tsinghua.edu.cn/pypi/codebuddy-agent-sdk/json',
188
+ 'https://pypi.org/pypi/codebuddy-agent-sdk/json'
189
+ ];
190
+
191
+ for (const url of mirrors) {
192
+ try {
193
+ const version = await new Promise((resolve, reject) => {
194
+ const protocol = url.startsWith('https') ? https : http;
195
+ protocol.get(url, {
196
+ headers: { 'User-Agent': 'sciagent-cli/1.0' },
197
+ timeout: 15000
198
+ }, (response) => {
199
+ // Handle redirects
200
+ if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
201
+ protocol.get(response.headers.location, {
202
+ headers: { 'User-Agent': 'sciagent-cli/1.0' },
203
+ timeout: 15000
204
+ }, (res2) => {
205
+ let data = '';
206
+ res2.on('data', (chunk) => { data += chunk; });
207
+ res2.on('end', () => {
208
+ try {
209
+ const json = JSON.parse(data);
210
+ resolve(json.info.version);
211
+ } catch (e) {
212
+ reject(new Error('Parse error'));
213
+ }
214
+ });
215
+ }).on('error', reject);
216
+ return;
217
+ }
218
+
219
+ let data = '';
220
+ response.on('data', (chunk) => { data += chunk; });
221
+ response.on('end', () => {
222
+ try {
223
+ const json = JSON.parse(data);
224
+ resolve(json.info.version);
225
+ } catch (e) {
226
+ reject(new Error('Parse error'));
227
+ }
228
+ });
229
+ }).on('error', reject);
230
+ });
231
+
232
+ if (version) return version;
233
+ } catch (e) {
234
+ // 继续尝试下一个镜像
235
+ }
236
+ }
42
237
 
43
- console.log(`\n Installing platform binary: ${packageName}`);
44
- console.log(' This may take a moment (downloading ~180MB)...\n');
238
+ throw new Error('无法获取SDK版本信息,请检查网络连接');
239
+ }
240
+
241
+ async function installCodebuddySdk() {
242
+ const existing = checkCodebuddyBinaryInstalled();
243
+ if (existing.installed) {
244
+ console.log(`✅ CodeBuddy SDK 已安装: ${existing.path}`);
245
+ return true;
246
+ }
247
+
248
+ const platform = process.platform;
249
+ const arch = process.arch === 'arm64' ? 'arm64' : 'x64';
250
+ const platformKey = `${platform}-${arch}`;
251
+ const platformTag = PYPI_PLATFORM_MAP[platformKey];
252
+
253
+ if (!platformTag) {
254
+ console.log(`⚠️ 不支持的平台: ${platformKey},跳过 CodeBuddy SDK 安装`);
255
+ return false;
256
+ }
257
+
258
+ console.log('');
259
+ console.log('📦 正在安装 CodeBuddy SDK...');
45
260
 
46
261
  try {
47
- // Determine npm global prefix for proper global install
48
- let npmCmd = 'npm';
49
- let installArgs = ['install', '-g', packageName];
262
+ // 获取最新版本
263
+ console.log(' 正在获取版本信息...');
264
+ const version = await getLatestSdkVersion();
265
+ console.log(` 版本: ${version}`);
266
+ console.log(` 平台: ${platformTag}`);
50
267
 
51
- // Check if we're in a global install context
52
- const isGlobal = process.env.npm_config_global === 'true' ||
53
- process.env.npm_lifecycle_event === 'postinstall';
268
+ // 构建 wheel 文件名
269
+ const wheelFilename = `codebuddy_agent_sdk-${version}-py3-none-${platformTag}.whl`;
54
270
 
55
- if (isGlobal) {
56
- // Use npm install -g for global installs
57
- installArgs = ['install', '-g', packageName];
58
- } else {
59
- // Local install
60
- installArgs = ['install', packageName];
271
+ // 创建临时目录
272
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sciagent-sdk-'));
273
+ const wheelPath = path.join(tmpDir, wheelFilename);
274
+
275
+ // 尝试从多个镜像下载
276
+ let downloaded = false;
277
+ const binaryName = BINARY_NAMES[platform] || 'codebuddy-headless';
278
+ const binDir = getCodebuddyBinDir();
279
+ const targetPath = path.join(binDir, binaryName);
280
+
281
+ for (const mirror of PYPI_DOWNLOAD_MIRRORS) {
282
+ const downloadUrl = `${mirror}/source/c/codebuddy-agent-sdk/${wheelFilename}`;
283
+
284
+ try {
285
+ console.log(` 尝试下载: ${mirror.split('/')[2]}...`);
286
+ await downloadFile(downloadUrl, wheelPath);
287
+ downloaded = true;
288
+ console.log(' ✅ 下载成功');
289
+ break;
290
+ } catch (e) {
291
+ console.log(` ❌ ${mirror.split('/')[2]}: ${e.message}`);
292
+ continue;
293
+ }
61
294
  }
62
295
 
63
- execSync(`${npmCmd} ${installArgs.join(' ')}`, {
64
- stdio: 'inherit',
65
- timeout: 300000 // 5 minutes timeout
66
- });
296
+ if (!downloaded) {
297
+ console.error('❌ 所有镜像源下载失败');
298
+ console.error(' 请检查网络连接,或稍后运行 "sciagent install-sdk" 手动安装');
299
+ return false;
300
+ }
301
+
302
+ // 提取二进制文件
303
+ console.log(' 正在提取二进制文件...');
304
+ fs.mkdirSync(binDir, { recursive: true });
305
+
306
+ // 使用 Python 解压 wheel 文件
307
+ const extractScript = `
308
+ import zipfile, sys, os
309
+ wheel_path = sys.argv[1]
310
+ target_dir = sys.argv[2]
311
+ binary_name = sys.argv[3]
312
+
313
+ with zipfile.ZipFile(wheel_path, 'r') as zf:
314
+ for name in zf.namelist():
315
+ if binary_name in name and '/bin/' in name:
316
+ with zf.open(name) as src:
317
+ target_path = os.path.join(target_dir, binary_name)
318
+ with open(target_path, 'wb') as dst:
319
+ dst.write(src.read())
320
+ if sys.platform != 'win32':
321
+ os.chmod(target_path, 0o755)
322
+ print(f'Extracted: {target_path}')
323
+ sys.exit(0)
324
+ print(f'Error: {binary_name} not found in wheel')
325
+ sys.exit(1)
326
+ `;
327
+
328
+ const scriptPath = path.join(tmpDir, 'extract.py');
329
+ fs.writeFileSync(scriptPath, extractScript);
330
+
331
+ try {
332
+ execSync(`python3 "${scriptPath}" "${wheelPath}" "${binDir}" "${binaryName}"`, {
333
+ stdio: 'inherit',
334
+ timeout: 120000
335
+ });
336
+ } catch (e) {
337
+ // 如果python3失败,尝试python
338
+ execSync(`python "${scriptPath}" "${wheelPath}" "${binDir}" "${binaryName}"`, {
339
+ stdio: 'inherit',
340
+ timeout: 120000
341
+ });
342
+ }
343
+
344
+ // 验证安装
345
+ if (fs.existsSync(targetPath)) {
346
+ const stats = fs.statSync(targetPath);
347
+ const sizeMb = (stats.size / (1024 * 1024)).toFixed(1);
348
+ console.log(`✅ CodeBuddy SDK 安装成功!`);
349
+ console.log(` 路径: ${targetPath}`);
350
+ console.log(` 大小: ${sizeMb} MB`);
351
+
352
+ // 清理临时文件
353
+ try {
354
+ fs.unlinkSync(wheelPath);
355
+ fs.unlinkSync(scriptPath);
356
+ fs.rmdirSync(tmpDir);
357
+ } catch (e) {}
358
+
359
+ return true;
360
+ } else {
361
+ console.error('❌ 安装验证失败');
362
+ return false;
363
+ }
67
364
 
68
- return true;
69
365
  } catch (e) {
70
- console.error(`\n ❌ Failed to install ${packageName}`);
71
- console.error(` Error: ${e.message}\n`);
366
+ console.error(`❌ CodeBuddy SDK 安装失败: ${e.message}`);
367
+ console.error(' 你可以稍后运行 "sciagent install-sdk" 手动安装');
72
368
  return false;
73
369
  }
74
370
  }
75
371
 
76
- function main() {
372
+ function installBinaryPackage(platform, arch) {
373
+ // 版本回退列表:先尝试当前版本,再尝试已知存在的版本
374
+ const FALLBACK_VERSIONS = [CURRENT_VERSION, '1.0.36', '1.0.33'];
375
+
376
+ let npmCmd = 'npm';
377
+ const isGlobal = process.env.npm_config_global === 'true' ||
378
+ process.env.npm_lifecycle_event === 'postinstall';
379
+
380
+ for (const version of FALLBACK_VERSIONS) {
381
+ const packageName = `@sciagent/cli-${platform}-${arch}@${version}`;
382
+ console.log(`\n Installing platform binary: ${packageName}`);
383
+ console.log(' This may take a moment...\n');
384
+
385
+ try {
386
+ let installArgs = ['install', '-g', packageName];
387
+
388
+ if (!isGlobal) {
389
+ installArgs = ['install', packageName];
390
+ }
391
+
392
+ execSync(`${npmCmd} ${installArgs.join(' ')}`, {
393
+ stdio: 'inherit',
394
+ timeout: 300000
395
+ });
396
+
397
+ return true;
398
+ } catch (e) {
399
+ console.log(` ⚠️ ${packageName} not available, trying next version...`);
400
+ }
401
+ }
402
+
403
+ console.error(`\n ❌ Failed to install platform binary for ${platform}-${arch}`);
404
+ return false;
405
+ }
406
+
407
+ async function main() {
77
408
  const platform = PLATFORM_MAP[process.platform];
78
409
  const arch = ARCH_MAP[process.arch];
79
410
 
@@ -87,7 +418,6 @@ function main() {
87
418
  console.log(` Node.js: ${process.version}`);
88
419
  console.log('');
89
420
 
90
- // 检查平台支持
91
421
  if (!platform || !arch) {
92
422
  console.error('❌ Unsupported platform or architecture');
93
423
  console.error(` Platform: ${process.platform}`);
@@ -98,7 +428,7 @@ function main() {
98
428
  process.exit(1);
99
429
  }
100
430
 
101
- // 检查对应的二进制包是否已安装
431
+ // 检查 SciAgent CLI 二进制
102
432
  const packageName = `@sciagent/cli-${platform}-${arch}`;
103
433
  const result = checkBinaryInstalled(platform, arch);
104
434
 
@@ -108,7 +438,6 @@ function main() {
108
438
  } else {
109
439
  console.log(`⚠️ Platform binary not found: ${packageName}`);
110
440
 
111
- // 尝试自动安装
112
441
  const success = installBinaryPackage(platform, arch);
113
442
 
114
443
  if (!success) {
@@ -123,7 +452,6 @@ function main() {
123
452
  process.exit(1);
124
453
  }
125
454
 
126
- // Verify installation
127
455
  const verifyResult = checkBinaryInstalled(platform, arch);
128
456
  if (verifyResult.installed) {
129
457
  console.log(`\n✅ Platform binary installed successfully: ${packageName}`);
@@ -134,11 +462,15 @@ function main() {
134
462
  }
135
463
  }
136
464
 
465
+ // 安装 CodeBuddy SDK(使用国内镜像)
466
+ await installCodebuddySdk();
467
+
137
468
  console.log('');
138
469
  console.log('Usage:');
139
470
  console.log(' sciagent # Start with default ports');
140
471
  console.log(' sciagent --port 8080 # Custom proxy port');
141
472
  console.log(' sciagent --no-browser # Don\'t open browser');
473
+ console.log(' sciagent install-sdk # Install CodeBuddy SDK manually');
142
474
  console.log(' sciagent --help # Show help');
143
475
  console.log('');
144
476
  console.log('Documentation: https://gitee.com/garva/research-agent');
@@ -146,4 +478,7 @@ function main() {
146
478
  }
147
479
 
148
480
  // 运行主函数
149
- main();
481
+ main().catch(err => {
482
+ console.error('Post install error:', err.message);
483
+ process.exit(1);
484
+ });