easyai 1.0.2 → 1.0.3

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,550 @@
1
+ require 'json'
2
+ require 'tmpdir'
3
+ require 'fileutils'
4
+ require 'open3'
5
+ require_relative '../base/file_crypto'
6
+ require_relative '../base/system_keychain'
7
+ require_relative '../auth/jpslogin'
8
+
9
+ module EasyAI
10
+ class ConfigManager
11
+ REPO_URL = "https://gitee.com/goodtools/EasyAISetting.git"
12
+ EASYAI_DIR = File.expand_path('~/.easyai')
13
+ CONFIG_REPO_DIR = File.join(EASYAI_DIR, 'EasyAISetting')
14
+
15
+ # 类变量初始化
16
+ @@no_keychain = false
17
+
18
+ # 管理配置仓库:如果存在则更新,不存在则下载
19
+ def self.manage_config_repo
20
+ # 确保 ~/.easyai 目录存在
21
+ FileUtils.mkdir_p(EASYAI_DIR) unless Dir.exist?(EASYAI_DIR)
22
+
23
+ if Dir.exist?(CONFIG_REPO_DIR)
24
+ update_config_repo
25
+ else
26
+ download_config_repo
27
+ end
28
+ end
29
+
30
+ # 更新现有的配置仓库
31
+ def self.update_config_repo
32
+ begin
33
+ # 检查是否是有效的 git 仓库
34
+ unless Dir.exist?(File.join(CONFIG_REPO_DIR, '.git'))
35
+ FileUtils.rm_rf(CONFIG_REPO_DIR)
36
+ return download_config_repo
37
+ end
38
+
39
+ # 切换到仓库目录并拉取更新
40
+ Dir.chdir(CONFIG_REPO_DIR) do
41
+ # 重置本地更改
42
+ reset_success = system('git reset --hard HEAD', out: File::NULL, err: File::NULL)
43
+ unless reset_success
44
+ FileUtils.rm_rf(CONFIG_REPO_DIR)
45
+ return download_config_repo
46
+ end
47
+
48
+ # 拉取最新更新,捕获错误信息
49
+ stdout, stderr, status = Open3.capture3('git pull origin master')
50
+
51
+ unless status.success?
52
+ puts "⚠️ 配置更新失败,使用缓存配置" if stderr && !stderr.empty?
53
+ FileUtils.rm_rf(CONFIG_REPO_DIR)
54
+ return download_config_repo
55
+ end
56
+ end
57
+
58
+ return true
59
+
60
+ rescue => e
61
+ FileUtils.rm_rf(CONFIG_REPO_DIR) if Dir.exist?(CONFIG_REPO_DIR)
62
+ return download_config_repo
63
+ end
64
+ end
65
+
66
+ # 下载配置仓库到固定位置
67
+ def self.download_config_repo
68
+ puts "正在下载配置仓库到 #{CONFIG_REPO_DIR}..."
69
+
70
+ begin
71
+ # 克隆仓库到固定位置,捕获错误信息
72
+ stdout, stderr, status = Open3.capture3("git clone --quiet #{REPO_URL} #{CONFIG_REPO_DIR}")
73
+
74
+ unless status.success?
75
+ puts "✗ 配置仓库下载失败"
76
+ puts " 错误信息: #{stderr.chomp}" if stderr && !stderr.empty?
77
+ puts " 请检查网络连接、git 安装状态"
78
+ return false
79
+ end
80
+
81
+ puts "✓ 配置仓库下载成功"
82
+ return true
83
+
84
+ rescue => e
85
+ puts "✗ 下载配置仓库失败: #{e.message}"
86
+ return false
87
+ end
88
+ end
89
+
90
+ # 从本地配置仓库加载配置
91
+ def self.load_from_local_repo(user_name = nil, options = {})
92
+ # 首先管理配置仓库
93
+ return nil unless manage_config_repo
94
+
95
+ # 设置全局选项
96
+ @@no_keychain = options[:no_keychain] || false
97
+
98
+ puts "正在从本地配置仓库加载配置..."
99
+
100
+ begin
101
+ # 检查 index.json 文件,支持加密版本
102
+ index_file = File.join(CONFIG_REPO_DIR, "index.json")
103
+ encrypted_index_file = File.join(CONFIG_REPO_DIR, "index.json.encrypted")
104
+
105
+ # 优先尝试加密的 index 文件
106
+ if File.exist?(encrypted_index_file)
107
+ users = parse_encrypted_index_file(encrypted_index_file)
108
+ elsif File.exist?(index_file)
109
+ users = parse_index_file(index_file)
110
+ else
111
+ puts "未找到 index.json 或 index.json.encrypted,使用默认配置"
112
+ config_file = File.join(CONFIG_REPO_DIR, "claude_setting.json")
113
+ encrypted_config_file = File.join(CONFIG_REPO_DIR, "claude_setting.json.encrypted")
114
+
115
+ if File.exist?(encrypted_config_file)
116
+ return load_encrypted_config_file(encrypted_config_file)
117
+ elsif File.exist?(config_file)
118
+ return load_config_file(config_file)
119
+ else
120
+ puts "✗ 未找到配置文件"
121
+ return nil
122
+ end
123
+ end
124
+
125
+ return nil if users.nil?
126
+
127
+ if users.empty?
128
+ puts "✗ index.json 中未找到用户"
129
+ return nil
130
+ end
131
+
132
+ # 获取用户名:优先使用提供的用户名,否则通过 JPS 登录获取
133
+ if user_name
134
+ selected_user = user_name
135
+ else
136
+ # 统一使用 JPS 登录获取用户名
137
+ selected_user = get_username_from_jps
138
+ if selected_user.nil? || selected_user.strip.empty?
139
+ puts "✗ JPS 登录失败,无法获取用户名"
140
+ return nil
141
+ end
142
+ end
143
+
144
+ # 查找用户配置文件
145
+ config_filename = find_user_config(users, selected_user.strip)
146
+
147
+ if config_filename.nil?
148
+ puts "✗ 用户 '#{selected_user}' 未找到"
149
+ puts " 可用用户: #{users.keys.join(', ')}"
150
+ return nil
151
+ end
152
+
153
+ # 加载配置文件,支持加密版本
154
+ config_filename += '.json' unless config_filename.end_with?('.json')
155
+ config_file = File.join(CONFIG_REPO_DIR, config_filename)
156
+ encrypted_config_file = File.join(CONFIG_REPO_DIR, "#{config_filename}.encrypted")
157
+
158
+ puts "✓ 正在为 #{selected_user} 加载配置"
159
+
160
+ # 优先尝试加密文件
161
+ if File.exist?(encrypted_config_file)
162
+ load_encrypted_config_file(encrypted_config_file)
163
+ elsif File.exist?(config_file)
164
+ load_config_file(config_file)
165
+ else
166
+ puts "✗ 配置文件未找到: #{config_filename}"
167
+ return nil
168
+ end
169
+
170
+ rescue => e
171
+ puts "✗ 加载本地配置失败: #{e.message}"
172
+ nil
173
+ end
174
+ end
175
+
176
+ def self.download_user_config(user_name = nil, options = {})
177
+ # 优先尝试使用本地配置仓库
178
+ config = load_from_local_repo(user_name, options)
179
+ return config if config
180
+
181
+ # 如果本地仓库失败,回退到临时下载方式
182
+ puts "本地配置仓库不可用,使用临时下载..."
183
+ download_user_config_temp(user_name, options)
184
+ end
185
+
186
+ # 原有的临时下载方式(作为备用方案)
187
+ def self.download_user_config_temp(user_name = nil, options = {})
188
+ puts "正在获取配置文件..."
189
+
190
+ # 设置全局选项
191
+ @@no_keychain = options[:no_keychain] || false
192
+
193
+ begin
194
+ # 创建临时目录
195
+ temp_dir = Dir.mktmpdir("EasyAISetting")
196
+
197
+ # 克隆仓库
198
+ clone_cmd = "git clone --depth 1 --quiet #{REPO_URL} #{temp_dir}"
199
+ success = system(clone_cmd, out: File::NULL, err: File::NULL)
200
+
201
+ unless success
202
+ puts "✗ 配置文件获取失败"
203
+ puts " 请检查网络连接、git 安装,或使用本地配置文件"
204
+ cleanup_temp_dir(temp_dir)
205
+ return nil
206
+ end
207
+
208
+ puts "✓ 配置文件获取成功"
209
+
210
+ # 检查 index.json 文件,支持加密版本
211
+ index_file = File.join(temp_dir, "index.json")
212
+ encrypted_index_file = File.join(temp_dir, "index.json.encrypted")
213
+
214
+ # 优先尝试加密的 index 文件
215
+ if File.exist?(encrypted_index_file)
216
+ users = parse_encrypted_index_file(encrypted_index_file)
217
+ elsif File.exist?(index_file)
218
+ users = parse_index_file(index_file)
219
+ else
220
+ puts "未找到 index.json 或 index.json.encrypted,使用默认配置"
221
+ config_file = File.join(temp_dir, "claude_setting.json")
222
+ encrypted_config_file = File.join(temp_dir, "claude_setting.json.encrypted")
223
+
224
+ if File.exist?(encrypted_config_file)
225
+ config_content = load_encrypted_config_file(encrypted_config_file)
226
+ elsif File.exist?(config_file)
227
+ config_content = load_config_file(config_file)
228
+ else
229
+ puts "✗ 未找到配置文件"
230
+ cleanup_temp_dir(temp_dir)
231
+ return nil
232
+ end
233
+
234
+ cleanup_temp_dir(temp_dir)
235
+ return config_content
236
+ end
237
+
238
+ return nil if users.nil?
239
+
240
+ if users.empty?
241
+ puts "✗ index.json 中未找到用户"
242
+ cleanup_temp_dir(temp_dir)
243
+ return nil
244
+ end
245
+
246
+ # 获取用户名:优先使用提供的用户名,否则通过 JPS 登录获取
247
+ if user_name
248
+ selected_user = user_name
249
+ else
250
+ # 统一使用 JPS 登录获取用户名
251
+ selected_user = get_username_from_jps
252
+ if selected_user.nil? || selected_user.strip.empty?
253
+ puts "✗ JPS 登录失败,无法获取用户名"
254
+ cleanup_temp_dir(temp_dir)
255
+ return nil
256
+ end
257
+ end
258
+
259
+ # 查找用户配置文件
260
+ config_filename = find_user_config(users, selected_user.strip)
261
+
262
+ if config_filename.nil?
263
+ puts "✗ 用户 '#{selected_user}' 未找到"
264
+ puts " 可用用户: #{users.keys.join(', ')}"
265
+ cleanup_temp_dir(temp_dir)
266
+ return nil
267
+ end
268
+
269
+ # 加载配置文件,支持加密版本
270
+ config_filename += '.json' unless config_filename.end_with?('.json')
271
+ config_file = File.join(temp_dir, config_filename)
272
+ encrypted_config_file = File.join(temp_dir, "#{config_filename}.encrypted")
273
+
274
+ puts "✓ 正在为 #{selected_user} 加载配置"
275
+
276
+ # 优先尝试加密文件
277
+ if File.exist?(encrypted_config_file)
278
+ config_content = load_encrypted_config_file(encrypted_config_file)
279
+ elsif File.exist?(config_file)
280
+ config_content = load_config_file(config_file)
281
+ else
282
+ puts "✗ 配置文件未找到: #{config_filename}"
283
+ cleanup_temp_dir(temp_dir)
284
+ return nil
285
+ end
286
+
287
+ cleanup_temp_dir(temp_dir)
288
+ config_content
289
+
290
+ rescue => e
291
+ puts "✗ 配置获取失败: #{e.message}"
292
+ cleanup_temp_dir(temp_dir) if temp_dir
293
+ nil
294
+ end
295
+ end
296
+
297
+ private
298
+
299
+ def self.cleanup_temp_dir(temp_dir)
300
+ return unless temp_dir && Dir.exist?(temp_dir)
301
+ FileUtils.rm_rf(temp_dir)
302
+ end
303
+
304
+ def self.parse_index_file(index_file)
305
+ begin
306
+ index_content = File.read(index_file)
307
+ JSON.parse(index_content)
308
+ rescue JSON::ParserError => e
309
+ puts "✗ 解析 index.json 失败: #{e.message}"
310
+ nil
311
+ rescue => e
312
+ puts "✗ 读取 index.json 失败: #{e.message}"
313
+ nil
314
+ end
315
+ end
316
+
317
+ # 通过 JPS 登录获取用户名
318
+ def self.get_username_from_jps
319
+ begin
320
+ jps_login = Auth::JPSLogin.new
321
+
322
+ if jps_login.login
323
+ username = jps_login.get_username
324
+ if username && !username.empty?
325
+ puts "👤 用户: #{username}"
326
+ return username
327
+ end
328
+ end
329
+ rescue => e
330
+ puts "❌ JPS 登录失败: #{e.message}"
331
+ end
332
+
333
+ return nil
334
+ end
335
+
336
+ def self.find_user_config(users, user_input_clean)
337
+ users.each do |name, filename|
338
+ if name.downcase == user_input_clean.downcase
339
+ return filename
340
+ end
341
+ end
342
+ nil
343
+ end
344
+
345
+ def self.load_config_file(config_file)
346
+ begin
347
+ config_content = File.read(config_file)
348
+ JSON.parse(config_content)
349
+ rescue JSON::ParserError => e
350
+ puts "✗ 解析配置文件失败: #{e.message}"
351
+ nil
352
+ rescue => e
353
+ puts "✗ 读取配置文件失败: #{e.message}"
354
+ nil
355
+ end
356
+ end
357
+
358
+ def self.load_encrypted_config_file(encrypted_config_file)
359
+ puts "正在解密配置文件..."
360
+
361
+ # 使用新的密码验证机制
362
+ result = get_and_validate_password(encrypted_config_file, "请输入解密密码: ")
363
+ return nil unless result
364
+
365
+ begin
366
+ # 解析 JSON
367
+ JSON.parse(result[:content])
368
+ rescue JSON::ParserError => e
369
+ puts "✗ 解析解密后的配置文件失败: #{e.message}"
370
+ puts "文件可能已损坏或不是有效的JSON格式"
371
+ nil
372
+ end
373
+ end
374
+
375
+ def self.parse_encrypted_index_file(encrypted_index_file)
376
+ puts "正在解密 index.json..."
377
+
378
+ # 使用新的密码验证机制
379
+ result = get_and_validate_password(encrypted_index_file, "请输入解密密码: ")
380
+ return nil unless result
381
+
382
+ begin
383
+ # 解析 JSON
384
+ JSON.parse(result[:content])
385
+ rescue JSON::ParserError => e
386
+ puts "✗ 解析解密后的 index.json 失败: #{e.message}"
387
+ puts "文件可能已损坏或不是有效的JSON格式"
388
+ nil
389
+ end
390
+ end
391
+
392
+ # 智能密码获取和验证:包含错误处理和重试机制
393
+ def self.get_and_validate_password(encrypted_file_path, prompt, max_retries = 3)
394
+ retries = 0
395
+
396
+ loop do
397
+ # 获取密码(系统存储或用户输入)
398
+ password_info = get_decryption_password_basic(prompt, retries > 0)
399
+ return nil unless password_info
400
+
401
+ # 尝试解密验证密码
402
+ begin
403
+ decrypted_content = decrypt_file_content(encrypted_file_path, password_info[:password])
404
+
405
+ # 解密成功,保存密码(如果不是从系统获取且未禁用存储)
406
+ unless password_info[:from_system] || @@no_keychain
407
+ if Base::SystemKeychain.store_password(password_info[:password])
408
+ puts "✓ 密码已安全存储,下次使用将自动获取"
409
+ end
410
+ end
411
+
412
+ return { content: decrypted_content, password_info: password_info }
413
+
414
+ rescue => e
415
+ retries += 1
416
+
417
+ # 分析错误类型并提供针对性提示
418
+ error_type = analyze_decryption_error(e)
419
+
420
+ # 处理系统密码错误
421
+ if password_info[:from_system]
422
+ puts "✗ 系统存储的密码无效"
423
+ puts " 错误详情:#{e.message}" if error_type[:show_details]
424
+ puts " 错误原因:#{error_type[:reason]}"
425
+ puts "正在清除无效的系统存储密码..."
426
+ Base::SystemKeychain.delete_stored_password
427
+ puts "✓ 已清除系统存储的无效密码"
428
+ puts "请重新输入正确的解密密码"
429
+ else
430
+ puts "✗ 解密失败"
431
+ puts " 错误详情:#{e.message}" if error_type[:show_details]
432
+ puts " 错误原因:#{error_type[:reason]}"
433
+ puts " 建议:#{error_type[:suggestion]}"
434
+ end
435
+
436
+ # 检查是否达到最大重试次数
437
+ if retries >= max_retries
438
+ puts "\n已达到最大重试次数 (#{max_retries})"
439
+ puts "可能的解决方案:"
440
+ puts " 1. 确认密码是否正确"
441
+ puts " 2. 检查文件是否完整下载"
442
+ puts " 3. 联系管理员确认密码是否已更改"
443
+
444
+ if ask_user_continue?
445
+ retries = 0 # 重置重试次数,继续尝试
446
+ puts "\n继续尝试解密..."
447
+ else
448
+ puts "用户选择退出解密"
449
+ return nil
450
+ end
451
+ else
452
+ remaining = max_retries - retries
453
+ puts "还可以重试 #{remaining} 次 (#{retries}/#{max_retries})"
454
+ puts "提示:按 Ctrl+C 可随时退出\n"
455
+ end
456
+ end
457
+ end
458
+ end
459
+
460
+ # 基础密码获取:先从系统获取,失败则提示用户输入
461
+ def self.get_decryption_password_basic(prompt, is_retry = false)
462
+ # 如果是重试,或者禁用了密码存储,直接提示用户输入
463
+ if is_retry || @@no_keychain
464
+ if @@no_keychain
465
+ puts "已禁用自动密码存储,请手动输入密码"
466
+ end
467
+ password = Base::FileCrypto.get_password(prompt)
468
+ return password ? { password: password, from_system: false } : nil
469
+ end
470
+
471
+ # 首先尝试从系统获取存储的密码
472
+ stored_password = Base::SystemKeychain.get_stored_password
473
+
474
+ if stored_password && !stored_password.empty?
475
+ puts "使用系统存储的密码进行解密..."
476
+ return { password: stored_password, from_system: true }
477
+ end
478
+
479
+ # 如果没有存储的密码,提示用户输入
480
+ password = Base::FileCrypto.get_password(prompt)
481
+ return password ? { password: password, from_system: false } : nil
482
+ end
483
+
484
+ # 分析解密错误类型,提供针对性建议
485
+ def self.analyze_decryption_error(error)
486
+ case error.message
487
+ when /bad decrypt/i, /wrong final block length/i, /invalid padding/i
488
+ {
489
+ reason: "密码不正确或文件已损坏",
490
+ suggestion: "请确认密码是否正确,或检查文件完整性",
491
+ show_details: false
492
+ }
493
+ when /not supported by device/i
494
+ {
495
+ reason: "系统不支持此加密算法或文件格式错误",
496
+ suggestion: "请检查文件是否为正确的加密文件",
497
+ show_details: true
498
+ }
499
+ when /no such file/i, /cannot open/i
500
+ {
501
+ reason: "文件不存在或无法访问",
502
+ suggestion: "请检查文件路径和权限",
503
+ show_details: false
504
+ }
505
+ when /invalid base64/i
506
+ {
507
+ reason: "文件格式错误或已损坏",
508
+ suggestion: "请重新下载配置文件",
509
+ show_details: false
510
+ }
511
+ else
512
+ {
513
+ reason: "未知的解密错误",
514
+ suggestion: "请尝试重新下载文件或联系技术支持",
515
+ show_details: true
516
+ }
517
+ end
518
+ end
519
+
520
+ # 询问用户是否继续尝试
521
+ def self.ask_user_continue?
522
+ puts "\n是否继续尝试解密?"
523
+ puts " y/yes - 继续尝试"
524
+ puts " n/no - 退出解密 (默认)"
525
+ print "> "
526
+
527
+ begin
528
+ response = STDIN.gets&.chomp&.downcase
529
+ return ['y', 'yes'].include?(response)
530
+ rescue Interrupt
531
+ puts "\n\n用户中断操作"
532
+ return false
533
+ rescue => e
534
+ puts "无法读取用户输入: #{e.message}"
535
+ return false
536
+ end
537
+ end
538
+
539
+ def self.decrypt_file_content(encrypted_file_path, password)
540
+ # 生成密钥
541
+ key = Base::FileCrypto.generate_key(password)
542
+
543
+ # 读取加密文件内容
544
+ encrypted_content = File.read(encrypted_file_path)
545
+
546
+ # 解密内容
547
+ Base::FileCrypto.aes_128_ecb_decrypt(key, encrypted_content)
548
+ end
549
+ end
550
+ end
@@ -1,3 +1,3 @@
1
1
  module EasyAI
2
- VERSION = '1.0.2'
2
+ VERSION = '1.0.3'
3
3
  end
data/lib/easyai.rb CHANGED
@@ -1,75 +1,87 @@
1
- require 'claide'
2
- require 'yaml'
3
- require 'fileutils'
4
- require 'colored2'
5
1
  require 'easyai/version'
2
+ require 'easyai/config/config'
3
+ require 'easyai/command'
4
+ require 'easyai/command/claude'
5
+ require 'easyai/command/gemini'
6
+ require 'easyai/command/gpt'
7
+ require 'easyai/command/utils'
8
+ require 'easyai/command/clean'
9
+ require 'easyai/command/update'
10
+ require 'easyai/base/version_checker'
6
11
 
7
12
  module EasyAI
8
-
9
- class Command < CLAide::Command
10
- self.abstract_command = true
11
- self.command = 'easyai'
12
- self.version = VERSION
13
- self.description = '简化 AI 命令行工具的 CLI 包装器,支持 Claude、Gemini 和 GPT'
14
-
15
- def self.options
16
- [
17
- ['--config', '显示配置文件路径'],
18
- ['--setup', '交互式设置配置']
19
- ].concat(super)
20
- end
21
-
22
- def initialize(argv)
23
- @config_flag = argv.flag?('config')
24
- @setup_flag = argv.flag?('setup')
25
- super
26
- end
27
-
28
- def run
29
- if @config_flag
30
- puts config_file_path
31
- return
32
- end
13
+ # EasyAI 应用程序主类
14
+ class EasyAIApp
15
+ def run(argv)
16
+ # 解析参数以确定是否显示启动标志
17
+ require 'claide'
18
+ require 'colored2'
19
+ coerced_argv = CLAide::ARGV.coerce(argv)
33
20
 
34
- if @setup_flag
35
- setup_config
36
- return
21
+ # 如果不是帮助或版本查询,显示启动标志
22
+ unless coerced_argv.flag?('help') || coerced_argv.flag?('version')
23
+ show_banner
37
24
  end
38
25
 
39
- super
26
+ # 在运行命令前进行版本检查
27
+ check_version_before_run(argv)
28
+
29
+ # 运行命令
30
+ EasyAI::Command.run(argv)
40
31
  end
41
32
 
42
33
  private
43
34
 
44
- def config_file_path
45
- @config_file_path ||= File.expand_path('~/.easyai/config.yml')
35
+ # 显示启动标志
36
+ def show_banner
37
+ require 'colored2'
38
+
39
+ # 简化的标题
40
+ title = "EasyAI v#{VERSION}"
41
+ box_width = 60
42
+
43
+ # 美化的启动标志
44
+ puts
45
+ puts "╔#{'═' * box_width}╗".cyan
46
+ puts "║#{title.center(box_width).green}║".cyan
47
+ puts "╚#{'═' * box_width}╝".cyan
48
+ puts
46
49
  end
47
50
 
48
- def setup_config
49
- puts "正在设置 EasyAI 配置...".green
50
- FileUtils.mkdir_p(File.dirname(config_file_path))
51
+ # 检查版本和更新
52
+ def check_version_before_run(argv)
53
+ # 解析参数以确定是否需要跳过版本检查
54
+ require 'claide'
55
+ require 'colored2'
56
+ coerced_argv = CLAide::ARGV.coerce(argv)
51
57
 
52
- config = {}
58
+ # 如果是以下情况,跳过版本检查:
59
+ # 1. 请求帮助信息 (--help)
60
+ # 2. 显示版本信息 (--version)
61
+ # 3. 主动检查更新 (--check-update)
62
+ skip_check = coerced_argv.flag?('help') ||
63
+ coerced_argv.flag?('version') ||
64
+ coerced_argv.flag?('check-update')
53
65
 
54
- print "Claude API 令牌 (ANTHROPIC_API_KEY): "
55
- config['claude_token'] = STDIN.gets.chomp
66
+ if ENV['EASYAI_DEBUG']
67
+ puts "📊 版本检查状态:".cyan
68
+ puts " • 当前版本: v#{EasyAI::VERSION}".green
69
+ puts " • 跳过检查: #{skip_check ? '是(--help/--version)'.yellow : '否'.cyan}"
70
+ end
56
71
 
57
- print "Gemini API 令牌 (GOOGLE_API_KEY): "
58
- config['gemini_token'] = STDIN.gets.chomp
72
+ return if skip_check
59
73
 
60
- print "OpenAI API 令牌 (OPENAI_API_KEY): "
61
- config['openai_token'] = STDIN.gets.chomp
74
+ # 强制更新检查(会阻塞执行)
75
+ Base::VersionChecker.check_force_update!
62
76
 
63
- print "HTTP 代理 (可选): "
64
- proxy = STDIN.gets.chomp
65
- config['http_proxy'] = proxy unless proxy.empty?
77
+ # 异步更新检查(不阻塞执行)
78
+ Base::VersionChecker.check_async
66
79
 
67
- File.write(config_file_path, config.to_yaml)
68
- puts "配置已保存到 #{config_file_path}".green
80
+ puts "✅ #{'版本检查完成'.green}" if ENV['EASYAI_DEBUG']
81
+ rescue => e
82
+ # 版本检查失败不应该影响命令执行
83
+ puts "⚠️ 版本检查出错: #{e.message}".yellow
84
+ puts e.backtrace.join("\n") if ENV['EASYAI_DEBUG']
69
85
  end
70
86
  end
71
- end
72
-
73
- require 'easyai/claude'
74
- require 'easyai/gemini'
75
- require 'easyai/gpt'
87
+ end