stigmergy 1.1.6 → 1.2.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.
@@ -0,0 +1,403 @@
1
+ /**
2
+ * Stigmergy CLI Upgrade Manager
3
+ * 管理所有 AI CLI 工具的升级和依赖更新
4
+ */
5
+
6
+ const { spawn, spawnSync } = require('child_process');
7
+ const path = require('path');
8
+ const os = require('os');
9
+ const fs = require('fs/promises');
10
+ const chalk = require('chalk');
11
+ const semver = require('semver');
12
+ const { CLI_TOOLS } = require('./cli_tools');
13
+ const { errorHandler } = require('./error_handler');
14
+
15
+ class UpgradeManager {
16
+ constructor() {
17
+ this.cliTools = CLI_TOOLS;
18
+ this.cacheDir = path.join(os.homedir(), '.stigmergy', 'cache');
19
+ this.upgradeLog = path.join(os.homedir(), '.stigmergy', 'upgrade.log');
20
+ }
21
+
22
+ async initialize() {
23
+ await fs.mkdir(this.cacheDir, { recursive: true });
24
+ }
25
+
26
+ /**
27
+ * 检查 CLI 工具的当前版本和最新版本
28
+ */
29
+ async checkVersions() {
30
+ const versions = {};
31
+ const errors = [];
32
+
33
+ for (const [toolName, toolConfig] of Object.entries(this.cliTools)) {
34
+ try {
35
+ console.log(`🔍 Checking ${toolName}...`);
36
+
37
+ // 获取当前版本
38
+ const currentVersion = await this.getCurrentVersion(toolName, toolConfig);
39
+
40
+ // 获取最新版本
41
+ const latestVersion = await this.getLatestVersion(toolName, toolConfig);
42
+
43
+ versions[toolName] = {
44
+ current: currentVersion,
45
+ latest: latestVersion,
46
+ needsUpgrade: semver.gt(latestVersion, currentVersion),
47
+ config: toolConfig
48
+ };
49
+
50
+ const status = versions[toolName].needsUpgrade ? '🔺' : '✅';
51
+ console.log(`${status} ${toolName}: ${currentVersion} → ${latestVersion}`);
52
+
53
+ } catch (error) {
54
+ errors.push({ tool: toolName, error: error.message });
55
+ console.log(`❌ ${toolName}: ${error.message}`);
56
+ }
57
+ }
58
+
59
+ return { versions, errors };
60
+ }
61
+
62
+ /**
63
+ * 获取当前安装的版本
64
+ */
65
+ async getCurrentVersion(toolName, toolConfig) {
66
+ try {
67
+ const result = spawnSync(toolConfig.version, {
68
+ shell: true,
69
+ encoding: 'utf8',
70
+ stdio: ['pipe', 'pipe', 'pipe']
71
+ });
72
+
73
+ if (result.error) {
74
+ throw new Error(`Tool not found: ${result.error.message}`);
75
+ }
76
+
77
+ if (result.status !== 0) {
78
+ throw new Error(`Version command failed: ${result.stderr}`);
79
+ }
80
+
81
+ // 从输出中提取版本号
82
+ const versionMatch = result.stdout.match(/(\d+\.\d+\.\d+)/);
83
+ if (versionMatch) {
84
+ return versionMatch[1];
85
+ }
86
+
87
+ throw new Error('Could not parse version from output');
88
+ } catch (error) {
89
+ return 'Not installed';
90
+ }
91
+ }
92
+
93
+ /**
94
+ * 获取最新可用版本
95
+ */
96
+ async getLatestVersion(toolName, toolConfig) {
97
+ try {
98
+ // 从 npm 注册表获取最新版本
99
+ const packageName = this.extractPackageName(toolConfig.install);
100
+ if (!packageName) {
101
+ throw new Error('Could not extract package name');
102
+ }
103
+
104
+ const result = spawnSync('npm', ['view', packageName, 'version'], {
105
+ shell: true,
106
+ encoding: 'utf8',
107
+ stdio: ['pipe', 'pipe', 'pipe']
108
+ });
109
+
110
+ if (result.status !== 0) {
111
+ throw new Error(`npm view failed: ${result.stderr}`);
112
+ }
113
+
114
+ const latestVersion = result.stdout.trim();
115
+ if (latestVersion) {
116
+ return latestVersion;
117
+ }
118
+
119
+ throw new Error('No version information available');
120
+ } catch (error) {
121
+ return 'Unknown';
122
+ }
123
+ }
124
+
125
+ /**
126
+ * 从安装命令中提取包名
127
+ */
128
+ extractPackageName(installCommand) {
129
+ const match = installCommand.match(/npm install -g (.+)/);
130
+ if (match) {
131
+ return match[1];
132
+ }
133
+ return null;
134
+ }
135
+
136
+ /**
137
+ * 检查过时的依赖和警告
138
+ */
139
+ async checkDeprecations() {
140
+ const deprecations = [];
141
+
142
+ try {
143
+ // 检查 npm 警告
144
+ const packageJsonPath = path.join(process.cwd(), 'package.json');
145
+ if (await this.fileExists(packageJsonPath)) {
146
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
147
+
148
+ // 检查各个依赖的版本
149
+ for (const [dep, version] of Object.entries(packageJson.dependencies || {})) {
150
+ const issues = await this.checkDependencyIssues(dep, version);
151
+ if (issues.length > 0) {
152
+ deprecations.push({ dependency: dep, version, issues });
153
+ }
154
+ }
155
+ }
156
+
157
+ // 检查 ImportProcessor 错误
158
+ const importProcessorErrors = await this.checkImportProcessorErrors();
159
+ if (importProcessorErrors.length > 0) {
160
+ deprecations.push({
161
+ type: 'ImportProcessor',
162
+ issues: importProcessorErrors
163
+ });
164
+ }
165
+
166
+ } catch (error) {
167
+ deprecations.push({
168
+ type: 'General',
169
+ issues: [error.message]
170
+ });
171
+ }
172
+
173
+ return deprecations;
174
+ }
175
+
176
+ /**
177
+ * 检查特定依赖的问题
178
+ */
179
+ async checkDependencyIssues(dependency, version) {
180
+ const issues = [];
181
+
182
+ // 已知的过时包列表
183
+ const deprecatedPackages = {
184
+ 'inflight': 'Use lru-cache instead',
185
+ 'rimraf': 'Use rimraf v4+',
186
+ 'glob@7': 'Use glob v9+',
187
+ 'eslint@8': 'Use eslint v9+'
188
+ };
189
+
190
+ for (const [deprecated, reason] of Object.entries(deprecatedPackages)) {
191
+ if (dependency === deprecated || dependency.startsWith(deprecated + '@')) {
192
+ issues.push(`Deprecated: ${reason}`);
193
+ }
194
+ }
195
+
196
+ return issues;
197
+ }
198
+
199
+ /**
200
+ * 检查 ImportProcessor 相关错误
201
+ */
202
+ async checkImportProcessorErrors() {
203
+ const errors = [];
204
+
205
+ try {
206
+ // 检查常见的 ImportProcessor 位置
207
+ const commonPaths = [
208
+ path.join(os.homedir(), 'AppData', 'Roaming', 'npm', 'node_modules'),
209
+ path.join(os.homedir(), '.npm', 'modules'),
210
+ '/usr/local/lib/node_modules'
211
+ ];
212
+
213
+ for (const npmPath of commonPaths) {
214
+ try {
215
+ const importProcessorPath = path.join(npmPath, '**', '*ImportProcessor*');
216
+ const result = spawnSync('find', [importProcessorPath], {
217
+ shell: true,
218
+ encoding: 'utf8'
219
+ });
220
+
221
+ if (result.stdout.trim()) {
222
+ errors.push(`Found ImportProcessor in: ${npmPath}`);
223
+ }
224
+ } catch (error) {
225
+ // 忽略路径不存在的错误
226
+ }
227
+ }
228
+
229
+ } catch (error) {
230
+ errors.push(`Error checking ImportProcessor: ${error.message}`);
231
+ }
232
+
233
+ return errors;
234
+ }
235
+
236
+ /**
237
+ * 生成升级计划
238
+ */
239
+ async generateUpgradePlan(options = {}) {
240
+ const { dryRun = false, force = false } = options;
241
+
242
+ console.log('📋 Generating upgrade plan...');
243
+
244
+ const { versions, errors } = await this.checkVersions();
245
+ const deprecations = await this.checkDeprecations();
246
+
247
+ const plan = {
248
+ timestamp: new Date().toISOString(),
249
+ upgrades: [],
250
+ fixes: [],
251
+ warnings: [],
252
+ errors: []
253
+ };
254
+
255
+ // 添加 CLI 工具升级计划
256
+ for (const [toolName, info] of Object.entries(versions)) {
257
+ if (info.needsUpgrade || force) {
258
+ plan.upgrades.push({
259
+ tool: toolName,
260
+ from: info.current,
261
+ to: info.latest,
262
+ command: info.config.install
263
+ });
264
+ }
265
+ }
266
+
267
+ // 添加修复计划
268
+ for (const deprecation of deprecations) {
269
+ if (deprecation.type === 'ImportProcessor') {
270
+ plan.fixes.push({
271
+ type: 'ImportProcessor',
272
+ description: 'Remove or reinstall affected CLI tools',
273
+ actions: [
274
+ 'npm uninstall -g @google/gemini-cli',
275
+ 'npm cache clean --force',
276
+ 'npm install -g @google/gemini-cli@latest'
277
+ ]
278
+ });
279
+ } else {
280
+ plan.fixes.push({
281
+ type: 'Dependency',
282
+ dependency: deprecation.dependency,
283
+ description: deprecation.issues.join(', '),
284
+ actions: [`Update ${deprecation.dependency} to latest version`]
285
+ });
286
+ }
287
+ }
288
+
289
+ // 添加警告
290
+ if (errors.length > 0) {
291
+ plan.warnings = errors;
292
+ }
293
+
294
+ return plan;
295
+ }
296
+
297
+ /**
298
+ * 执行升级
299
+ */
300
+ async executeUpgrade(plan, options = {}) {
301
+ const { dryRun = false, force = false } = options;
302
+
303
+ console.log('🚀 Executing upgrade plan...');
304
+
305
+ const results = {
306
+ successful: [],
307
+ failed: [],
308
+ skipped: []
309
+ };
310
+
311
+ if (dryRun) {
312
+ console.log('🔍 DRY RUN MODE - No actual changes will be made');
313
+ }
314
+
315
+ // 升级 CLI 工具
316
+ for (const upgrade of plan.upgrades) {
317
+ if (!dryRun) {
318
+ try {
319
+ console.log(`⬆️ Upgrading ${upgrade.tool}...`);
320
+
321
+ const result = spawnSync('npm', ['install', '-g', upgrade.command.split(' ').pop()], {
322
+ shell: true,
323
+ stdio: 'inherit'
324
+ });
325
+
326
+ if (result.status === 0) {
327
+ results.successful.push(upgrade);
328
+ console.log(`✅ ${upgrade.tool} upgraded successfully`);
329
+ } else {
330
+ results.failed.push({ ...upgrade, error: 'Installation failed' });
331
+ console.log(`❌ ${upgrade.tool} upgrade failed`);
332
+ }
333
+ } catch (error) {
334
+ results.failed.push({ ...upgrade, error: error.message });
335
+ console.log(`❌ ${upgrade.tool} upgrade failed: ${error.message}`);
336
+ }
337
+ } else {
338
+ console.log(`🔍 Would upgrade ${upgrade.tool}: ${upgrade.from} → ${upgrade.to}`);
339
+ results.successful.push(upgrade);
340
+ }
341
+ }
342
+
343
+ // 修复问题
344
+ for (const fix of plan.fixes) {
345
+ if (!dryRun) {
346
+ try {
347
+ console.log(`🔧 Fixing ${fix.type} issues...`);
348
+
349
+ if (fix.type === 'ImportProcessor' && force) {
350
+ // 执行 ImportProcessor 修复
351
+ for (const action of fix.actions) {
352
+ if (action.includes('npm')) {
353
+ const result = spawnSync(action, { shell: true, stdio: 'inherit' });
354
+ if (result.status !== 0) {
355
+ throw new Error(`Failed to execute: ${action}`);
356
+ }
357
+ }
358
+ }
359
+ results.successful.push(fix);
360
+ }
361
+ } catch (error) {
362
+ results.failed.push({ ...fix, error: error.message });
363
+ }
364
+ } else {
365
+ console.log(`🔍 Would fix ${fix.type}: ${fix.description}`);
366
+ results.successful.push(fix);
367
+ }
368
+ }
369
+
370
+ return results;
371
+ }
372
+
373
+ /**
374
+ * 记录升级日志
375
+ */
376
+ async logUpgrade(plan, results) {
377
+ const logEntry = {
378
+ timestamp: new Date().toISOString(),
379
+ plan,
380
+ results
381
+ };
382
+
383
+ try {
384
+ await fs.appendFile(this.upgradeLog, JSON.stringify(logEntry, null, 2) + '\n');
385
+ } catch (error) {
386
+ console.warn('Warning: Could not write upgrade log:', error.message);
387
+ }
388
+ }
389
+
390
+ /**
391
+ * 辅助方法:检查文件是否存在
392
+ */
393
+ async fileExists(filePath) {
394
+ try {
395
+ await fs.access(filePath);
396
+ return true;
397
+ } catch {
398
+ return false;
399
+ }
400
+ }
401
+ }
402
+
403
+ module.exports = UpgradeManager;