easyai 1.4.0 → 1.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11409804e9d550fb82f6b2d4735c288383430a08f0c8d552c5402359e8eace69
4
- data.tar.gz: 3d4afb321b908167b5023b6ea88b0f41d04f352c24dc05243ca439f4d6080ffe
3
+ metadata.gz: 3be8a56581e7cb06127691c7f1b3d19c28d038f8b5744b69af93a937cafa6f0a
4
+ data.tar.gz: a73f942ea6819a885b0e65ef176f18c97c4a550bb9b11e1c3ad98b997ed0b5a2
5
5
  SHA512:
6
- metadata.gz: f1e5f980648640cea13741fdf15abb62153295c61a21c4fee74ca37431cc81addbf09a507c51591db2cedac86ca057916aa205bd190cad56910f2e5e5e1a3ee0
7
- data.tar.gz: a857e396b2f0a2db95369cff98b13ec72ebdd3f3396e4da471d8f0b8a44b6614688c6cccb22f1e2f0ac53dfaf9d257db605eb504c9248e6b1e1e8a997c0a946d
6
+ metadata.gz: 1eefce32542ce00cd0bf52776c66ee2d694080b72634924ec73e8ede5b2fee3be07159f06d5a7176a0fd74837d3966585602f7cbf9708496bba383e4823028f5
7
+ data.tar.gz: 0066ffe5863fcc9d4198bbf0e85d7719a4567c9b13dcb5b0129949541f504d3455a8ac42a4fcf84793e995a89366486321cbbec93d105fb038da75745a65fd05
data/AGENTS.md ADDED
@@ -0,0 +1,40 @@
1
+ # Repository Guidelines
2
+
3
+ ## 项目结构与模块组织
4
+ - Ruby 源码位于 `lib/` 根目录,核心入口在 `lib/easyai.rb`,基础能力集中于 `lib/easyai/base`,命令定义在 `lib/easyai/command` 及其子目录(如 `claude.rb`、`gemini.rb`)。
5
+ - 配置读取逻辑保存在 `lib/easyai/config`,认证与第三方服务脚本位于 `lib/easyai/auth`; 版本号定义于 `lib/easyai/version.rb` 并由 `easyai.gemspec` 消费。
6
+ - CLI 启动脚本是 `bin/easyai`;打包产物默认输出在项目根目录。辅助脚本 `test_local.sh` 和 `release_remote.sh` 承担本地测试与正式发布,请保留可执行权限并在修改后记录变更。
7
+ - 未来添加测试文件时,统一放在 `spec/` 目录,可按模块建立 `spec/command`、`spec/base` 等子目录,以便快速定位;示例数据建议放入 `spec/support/fixtures`。
8
+
9
+ ## 架构概览
10
+ - `EasyAI::EasyAIApp` 负责命令调度、启动横幅与版本检查,所有 CLI 子命令最终由 `EasyAI::Command` 树处理。
11
+ - 版本检测与更新提示依赖 `Base::VersionChecker` 和 `Base::UpdateNotifier`,与网络交互相关的逻辑集中于这些模块,方便替换或打桩测试。
12
+ - 新增功能时优先扩展现有命令类;若需共享逻辑,请在 `lib/easyai/base` 内创建独立模块,避免在命令类中堆积杂项。
13
+
14
+ ## 构建、测试与开发命令
15
+ - `bundle install`:安装 Gemfile 中的开发依赖(Rake、RSpec 等),首次克隆或更新依赖后必须执行。
16
+ - `bundle exec rspec`:运行整个测试套件;可通过 `bundle exec rspec spec/command/claude_spec.rb` 定位单个文件调试,或使用 `--format documentation` 获取更详细输出。
17
+ - `bundle exec rake build` 或 `gem build easyai.gemspec`:构建 gem 包,常用于发布前的本地验证;`bundle exec rake install` 可直接将构建产物安装到本地。
18
+ - `./test_local.sh`:自动执行打包并安装到本地 Ruby 环境,适合冒烟回归;`./release_remote.sh` 覆盖提交流程、分支同步和 `gem push`,仅在确认要发布时运行,并保证当前分支已同步远程。
19
+
20
+ ## 语言与风格规范
21
+ - 本项目要求所有代码、注释、提交信息与文档统一使用中文,PR 描述与讨论亦应遵循;如必须引用英文原文,请附中文解释,确保沟通无障碍。
22
+ - 采用 Ruby 标准两空格缩进,方法与变量命名使用 `snake_case`;常量使用 `SCREAMING_SNAKE_CASE`,模块或类名使用驼峰式。
23
+ - CLI 提示语保持简洁友好,若涉及多语言输出,应保证中文为默认选项;共享样式请复用现有 `colored2` 渲染逻辑,避免重复实现颜色方案。
24
+ - 新增命令时,参照现有文件创建 `lib/easyai/command/<name>.rb`,并在 `lib/easyai.rb` 中显式 `require`,确保自动加载链条完整;必要时补充文档说明。
25
+
26
+ ## 测试准则
27
+ - 使用 RSpec 进行行为驱动测试,文件命名为 `<feature>_spec.rb`;`describe` 中强调用户视角,例如 CLI 参数解析或远程更新提示。
28
+ - 对加密、配置清理等高风险逻辑,建立 fixture(建议放在 `spec/support/fixtures`)并测试成功与失败路径;必要时模拟外部命令退出码。
29
+ - 目标是保持修改行的单元测试覆盖率不下降;若引入重要分支逻辑,请添加针对性例子并在 PR 中说明覆盖情况。
30
+ - 提交前至少运行一次 `bundle exec rspec`;若修改了打包或权限脚本,请额外执行 `./test_local.sh` 验证安装流程,并记录输出结果。
31
+
32
+ ## 提交与合并请求规范
33
+ - 提交消息遵循 `feat:`、`fix:`、`refactor:`、`res x.y.z` 等前缀,小写冒号后加空格,主题控制在 72 字符以内,正文补充动机与影响面。
34
+ - PR 需概述变更动机、列出涉及的命令或配置、附上测试结果(命令行输出或截图),并关联相关 Issue;若影响用户体验,请描述回滚方案。
35
+ - 发布版本需修改 `lib/easyai/version.rb` 与 `easyai.gemspec` 中的引用,并在说明中标记兼容性影响、所需依赖版本与升级指南;合入后同步更新 README。
36
+
37
+ ## 安全与配置提示
38
+ - 禁止提交 API 密钥、用户凭证及本地配置文件;可通过 `.env` 或 `.gitignore` 排除的 JSON 进行本地调试,并在 README 或本指南注明所需环境变量与示例格式。
39
+ - 检查脚本中的 `chmod`、`chown`、`chgrp` 等命令,确认对目标机器安全;新增外部依赖(例如 npm、pip 包或系统二进制)时,请在 README 与本文件同步更新使用说明,并说明安装步骤与权限要求。
40
+ - 如需访问网络服务,请遵循最小权限原则,优先使用环境变量而非硬编码;在 Pull Request 中披露安全假设与可能的风险点。
data/CLAUDE.md CHANGED
@@ -105,7 +105,7 @@ AI 工具的认证令牌获取优先级:
105
105
 
106
106
  **ConfigManager** - 业务配置逻辑:
107
107
  - 支持多工具类型(claude、gemini、gpt)
108
- - 支持多认证平台(claude_auth、kimi_auth、deepseek_auth)
108
+ - 支持多认证平台(claude_auth、aliqwen_auth、kimi_auth、deepseek_auth)
109
109
  - 智能平台选择和用户交互
110
110
  - 复杂加密配置的密码验证机制
111
111
 
@@ -2,6 +2,7 @@ require 'json'
2
2
  require 'etc'
3
3
  require 'open3'
4
4
  require 'fileutils'
5
+ require_relative '../base/system_info'
5
6
 
6
7
  module EasyAI
7
8
  module Auth
@@ -77,11 +78,11 @@ module EasyAI
77
78
 
78
79
  # 辅助方法
79
80
  def self.macos?
80
- RUBY_PLATFORM.include?('darwin')
81
+ Base::SystemInfo.macos?
81
82
  end
82
83
 
83
84
  def self.current_user
84
- @current_user ||= Etc.getlogin || ENV['USER'] || ENV['LOGNAME']
85
+ Base::SystemInfo.current_user
85
86
  end
86
87
 
87
88
  def self.log_verbose(message)
@@ -360,54 +361,7 @@ module EasyAI
360
361
  end
361
362
 
362
363
  def self.get_shell_files
363
- shell_files = []
364
- current_shell = ENV['SHELL']&.split('/')&.last || 'bash'
365
-
366
- if Gem.win_platform?
367
- # Windows 平台配置
368
- powershell_profile = File.expand_path("~/Documents/WindowsPowerShell/Microsoft.PowerShell_profile.ps1")
369
- powershell_core_profile = File.expand_path("~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1")
370
-
371
- # 检查哪个 PowerShell 配置文件存在或需要创建
372
- if File.exist?(File.dirname(powershell_core_profile))
373
- shell_files << powershell_core_profile
374
- elsif File.exist?(File.dirname(powershell_profile))
375
- shell_files << powershell_profile
376
- else
377
- # 默认使用 PowerShell Core 配置
378
- ps_dir = File.dirname(powershell_core_profile)
379
- FileUtils.mkdir_p(ps_dir) unless File.exist?(ps_dir)
380
- shell_files << powershell_core_profile
381
- end
382
- else
383
- # Unix/Linux/macOS 平台配置
384
- case current_shell
385
- when 'zsh'
386
- shell_files << File.expand_path("~/.zshrc")
387
- when 'bash'
388
- bash_profile = File.expand_path("~/.bash_profile")
389
- bashrc = File.expand_path("~/.bashrc")
390
- shell_files << (File.exist?(bash_profile) ? bash_profile : bashrc)
391
- when 'fish'
392
- fish_config = File.expand_path("~/.config/fish/config.fish")
393
- shell_files << fish_config
394
-
395
- # 确保 fish 配置目录存在
396
- fish_dir = File.dirname(fish_config)
397
- unless File.exist?(fish_dir)
398
- FileUtils.mkdir_p(fish_dir)
399
- puts " 创建目录: #{fish_dir}"
400
- end
401
- else
402
- shell_files << File.expand_path("~/.profile")
403
- end
404
-
405
- # 总是添加 ~/.profile 作为备份
406
- profile_file = File.expand_path("~/.profile")
407
- shell_files << profile_file unless shell_files.include?(profile_file)
408
- end
409
-
410
- shell_files
364
+ Base::SystemInfo.shell_config_files
411
365
  end
412
366
 
413
367
  def self.get_token_patterns(shell_file)
@@ -0,0 +1,255 @@
1
+ require 'rbconfig'
2
+ require 'etc'
3
+
4
+ module EasyAI
5
+ module Base
6
+ class SystemInfo
7
+ class << self
8
+ # === 平台检测 ===
9
+ def platform
10
+ @platform ||= detect_platform
11
+ end
12
+
13
+ def macos?
14
+ platform == :macos
15
+ end
16
+
17
+ def windows?
18
+ platform == :windows
19
+ end
20
+
21
+ def linux?
22
+ platform == :linux
23
+ end
24
+
25
+ # === 用户和环境 ===
26
+ def current_user
27
+ @current_user ||= Etc.getlogin || ENV['USER'] || ENV['LOGNAME']
28
+ end
29
+
30
+ def current_shell
31
+ @current_shell ||= ENV['SHELL']&.split('/')&.last || 'bash'
32
+ end
33
+
34
+ def shell_config_files
35
+ @shell_config_files ||= detect_shell_config_files
36
+ end
37
+
38
+ # === 地区和语言检测(仅在需要时调用)===
39
+ def region_info
40
+ @region_info ||= detect_region_info
41
+ end
42
+
43
+ def is_us_region?
44
+ region_info[:is_us]
45
+ end
46
+
47
+ # 检查是否为英语环境
48
+ def is_english_environment?
49
+ info = region_info
50
+
51
+ case platform
52
+ when :macos
53
+ # macOS: 检查 locale 和首选语言
54
+ locale_english = info[:locale].to_s.start_with?('en_') || info[:locale].to_s.start_with?('en.')
55
+ apple_locale_english = info[:apple_locale].to_s.start_with?('en_')
56
+ first_lang_english = info[:apple_languages].to_s.start_with?('en')
57
+
58
+ # 任一为英语即可
59
+ locale_english || apple_locale_english || first_lang_english
60
+ when :windows
61
+ # Windows: 检查系统和用户地区
62
+ system_english = info[:system_locale].to_s.start_with?('en-')
63
+ user_english = info[:user_locale].to_s.start_with?('en-')
64
+ locale_english = info[:locale].to_s.start_with?('en_') || info[:locale].to_s.start_with?('en.')
65
+
66
+ # 任一为英语即可
67
+ system_english || user_english || locale_english
68
+ else
69
+ # Linux/其他: 检查 LANG 环境变量
70
+ locale = info[:locale].to_s
71
+ locale.start_with?('en_') || locale.start_with?('en.')
72
+ end
73
+ end
74
+
75
+ # 检查是否满足 claude_auth 的环境要求
76
+ def meets_claude_auth_requirements?
77
+ !windows? && is_us_region? && is_english_environment?
78
+ end
79
+
80
+ # 获取 claude_auth 环境检查详情
81
+ def claude_auth_check_details
82
+ {
83
+ os_supported: !windows?,
84
+ os_name: platform.to_s.capitalize,
85
+ is_us_region: is_us_region?,
86
+ region: region_info[:region],
87
+ is_english: is_english_environment?,
88
+ locale: region_info[:locale],
89
+ meets_requirements: meets_claude_auth_requirements?
90
+ }
91
+ end
92
+
93
+ # === 命令检测 ===
94
+ def which_command(cmd)
95
+ if windows?
96
+ system("where #{cmd} >nul 2>&1")
97
+ else
98
+ system("which #{cmd} > /dev/null 2>&1")
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def detect_platform
105
+ os = RbConfig::CONFIG['host_os']
106
+ case os
107
+ when /darwin/i
108
+ :macos
109
+ when /mswin|mingw|cygwin/i
110
+ :windows
111
+ when /linux/i
112
+ :linux
113
+ when /bsd/i
114
+ :bsd
115
+ else
116
+ # 回退到 RUBY_PLATFORM
117
+ case RUBY_PLATFORM
118
+ when /darwin/
119
+ :macos
120
+ when /mswin|mingw|cygwin/
121
+ :windows
122
+ when /linux/
123
+ :linux
124
+ else
125
+ :unknown
126
+ end
127
+ end
128
+ end
129
+
130
+ def detect_shell_config_files
131
+ files = []
132
+
133
+ case current_shell
134
+ when 'zsh'
135
+ files << File.expand_path("~/.zshrc")
136
+ when 'bash'
137
+ bash_profile = File.expand_path("~/.bash_profile")
138
+ bashrc = File.expand_path("~/.bashrc")
139
+ files << (File.exist?(bash_profile) ? bash_profile : bashrc)
140
+ when 'fish'
141
+ fish_config = File.expand_path("~/.config/fish/config.fish")
142
+ files << fish_config
143
+
144
+ # 确保 fish 配置目录存在
145
+ fish_dir = File.dirname(fish_config)
146
+ unless File.exist?(fish_dir)
147
+ require 'fileutils'
148
+ FileUtils.mkdir_p(fish_dir)
149
+ end
150
+ else
151
+ files << File.expand_path("~/.profile")
152
+ end
153
+
154
+ # 总是添加 ~/.profile 作为备份
155
+ profile_file = File.expand_path("~/.profile")
156
+ files << profile_file unless files.include?(profile_file)
157
+
158
+ files
159
+ end
160
+
161
+ def detect_region_info
162
+ case platform
163
+ when :macos
164
+ detect_macos_region
165
+ when :windows
166
+ detect_windows_region
167
+ when :linux
168
+ detect_linux_region
169
+ else
170
+ detect_generic_region
171
+ end
172
+ end
173
+
174
+ def detect_macos_region
175
+ locale = ENV['LANG'] || ENV['LC_ALL'] || ''
176
+ apple_locale = `defaults read -g AppleLocale 2>/dev/null`.chomp
177
+ apple_languages = `defaults read -g AppleLanguages 2>/dev/null`.chomp
178
+
179
+ # 解析语言列表
180
+ first_language = apple_languages.match(/"([^"]+)"/)&.captures&.first || ''
181
+
182
+ # 判断是否为美国地区
183
+ is_us = apple_locale.start_with?('en_US') ||
184
+ locale.start_with?('en_US') ||
185
+ first_language.start_with?('en-US')
186
+
187
+ {
188
+ locale: locale,
189
+ apple_locale: apple_locale,
190
+ apple_languages: first_language,
191
+ region: apple_locale.split('_').last || 'Unknown',
192
+ is_us: is_us,
193
+ platform: 'macOS'
194
+ }
195
+ end
196
+
197
+ def detect_windows_region
198
+ locale = ENV['LANG'] || ''
199
+
200
+ # Windows PowerShell 命令获取地区
201
+ system_locale = `powershell -Command "Get-WinSystemLocale | Select-Object -ExpandProperty Name" 2>nul`.chomp
202
+ user_locale = `powershell -Command "Get-Culture | Select-Object -ExpandProperty Name" 2>nul`.chomp
203
+
204
+ # 判断是否为美国地区
205
+ is_us = system_locale.start_with?('en-US') ||
206
+ user_locale.start_with?('en-US') ||
207
+ locale.start_with?('en_US')
208
+
209
+ {
210
+ locale: locale,
211
+ system_locale: system_locale,
212
+ user_locale: user_locale,
213
+ region: (system_locale.split('-').last || user_locale.split('-').last || 'Unknown'),
214
+ is_us: is_us,
215
+ platform: 'Windows'
216
+ }
217
+ end
218
+
219
+ def detect_linux_region
220
+ locale = ENV['LANG'] || ENV['LC_ALL'] || ''
221
+
222
+ # 尝试读取 /etc/locale.conf
223
+ locale_conf = File.exist?('/etc/locale.conf') ? File.read('/etc/locale.conf').strip : ''
224
+
225
+ # 使用 localectl 命令
226
+ system_locale = `localectl status 2>/dev/null | grep "System Locale" | cut -d= -f2`.chomp
227
+
228
+ # 判断是否为美国地区
229
+ is_us = locale.start_with?('en_US') ||
230
+ system_locale.start_with?('en_US') ||
231
+ locale_conf.include?('en_US')
232
+
233
+ {
234
+ locale: locale,
235
+ system_locale: system_locale || locale_conf,
236
+ region: locale.split('_').last&.split('.').first || 'Unknown',
237
+ is_us: is_us,
238
+ platform: 'Linux'
239
+ }
240
+ end
241
+
242
+ def detect_generic_region
243
+ locale = ENV['LANG'] || ENV['LC_ALL'] || 'Unknown'
244
+
245
+ {
246
+ locale: locale,
247
+ region: locale.split('_').last&.split('.').first || 'Unknown',
248
+ is_us: locale.start_with?('en_US'),
249
+ platform: 'Unknown'
250
+ }
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
@@ -1,5 +1,6 @@
1
1
  require 'rbconfig'
2
2
  require 'fileutils'
3
+ require_relative 'system_info'
3
4
 
4
5
  module EasyAI
5
6
  module Base
@@ -9,7 +10,7 @@ module EasyAI
9
10
 
10
11
  # 跨平台密码存储
11
12
  def self.store_password(password)
12
- case detect_platform
13
+ case SystemInfo.platform
13
14
  when :macos
14
15
  store_password_macos(password)
15
16
  when :windows
@@ -24,7 +25,7 @@ module EasyAI
24
25
 
25
26
  # 跨平台密码获取
26
27
  def self.get_stored_password
27
- case detect_platform
28
+ case SystemInfo.platform
28
29
  when :macos
29
30
  get_password_macos
30
31
  when :windows
@@ -38,7 +39,7 @@ module EasyAI
38
39
 
39
40
  # 跨平台密码删除
40
41
  def self.delete_stored_password
41
- case detect_platform
42
+ case SystemInfo.platform
42
43
  when :macos
43
44
  delete_password_macos
44
45
  when :windows
@@ -50,19 +51,9 @@ module EasyAI
50
51
  end
51
52
  end
52
53
 
53
- # 检测操作系统平台
54
+ # 检测操作系统平台(已迁移到 SystemInfo)
54
55
  def self.detect_platform
55
- os = RbConfig::CONFIG['host_os']
56
- case os
57
- when /darwin/i
58
- :macos
59
- when /mswin|mingw|cygwin/i
60
- :windows
61
- when /linux/i
62
- :linux
63
- else
64
- :unknown
65
- end
56
+ SystemInfo.platform
66
57
  end
67
58
 
68
59
  private
@@ -3,6 +3,7 @@ require 'uri'
3
3
  require_relative '../config/config'
4
4
  require_relative '../auth/authclaude'
5
5
  require_relative '../base/update_notifier'
6
+ require_relative '../base/system_info'
6
7
 
7
8
  module EasyAI
8
9
  class Command
@@ -99,6 +100,13 @@ module EasyAI
99
100
  print_success("配置加载成功") if remote_config
100
101
  end
101
102
 
103
+ # 检查是否使用 claude_auth,如果是则进行环境检查
104
+ if remote_config && remote_config['_config_path']&.include?('claude_auth')
105
+ unless check_claude_auth_environment
106
+ exit 1
107
+ end
108
+ end
109
+
102
110
  # 如果远程配置获取失败,回退到本地配置
103
111
  if remote_config.nil?
104
112
  print_warning("使用本地配置")
@@ -167,8 +175,83 @@ module EasyAI
167
175
  end
168
176
 
169
177
  def claude_available?
170
- # 使用统一的跨平台命令检测
171
- EasyAI::CrossPlatform.command_exists?('claude')
178
+ # 跨平台命令检测
179
+ Base::SystemInfo.which_command('claude')
180
+ end
181
+
182
+ # 检查 claude_auth 环境要求
183
+ def check_claude_auth_environment
184
+ check_details = Base::SystemInfo.claude_auth_check_details
185
+
186
+ puts "\n🔍 系统环境检查(Claude 官方认证)"
187
+ puts "─" * 60
188
+
189
+ # 1. 操作系统检查
190
+ if check_details[:os_supported]
191
+ puts " ✓ 操作系统: #{check_details[:os_name]}".green
192
+ else
193
+ puts " ❌ 操作系统: #{check_details[:os_name]}(不支持)".red
194
+ end
195
+
196
+ # 2. 显示地区和语言信息
197
+ region_info = Base::SystemInfo.region_info
198
+ if Base::SystemInfo.macos?
199
+ puts " 系统地区: #{region_info[:apple_locale]}"
200
+ puts " 首选语言: #{region_info[:apple_languages]}"
201
+ elsif Base::SystemInfo.windows?
202
+ puts " 系统地区: #{region_info[:system_locale]}"
203
+ puts " 用户地区: #{region_info[:user_locale]}"
204
+ else
205
+ puts " 系统语言: #{region_info[:locale]}"
206
+ end
207
+
208
+ # 3. 地区检查
209
+ if check_details[:is_us_region]
210
+ puts " ✓ 地区检查: 美国地区".green
211
+ else
212
+ puts " ❌ 地区检查: 非美国地区(#{check_details[:region]})".red
213
+ end
214
+
215
+ # 4. 语言检查
216
+ if check_details[:is_english]
217
+ puts " ✓ 语言检查: 英语".green
218
+ else
219
+ puts " ❌ 语言检查: 非英语语言".red
220
+ end
221
+
222
+ puts "─" * 60
223
+
224
+ # 如果检查失败,显示错误信息和解决方案
225
+ unless check_details[:meets_requirements]
226
+ puts
227
+ puts " ⛔ 环境检查未通过".red
228
+ puts
229
+
230
+ failure_reasons = []
231
+ failure_reasons << "Claude 官方认证不支持 Windows 系统" unless check_details[:os_supported]
232
+ failure_reasons << "Claude 官方认证要求美国地区" unless check_details[:is_us_region]
233
+ failure_reasons << "Claude 官方认证要求英语语言环境" unless check_details[:is_english]
234
+
235
+ puts " 失败原因:".yellow
236
+ failure_reasons.each_with_index do |reason, index|
237
+ puts " #{index + 1}. #{reason}".yellow
238
+ end
239
+
240
+ puts
241
+ puts " 解决方案:".cyan
242
+ puts " 1. 使用其他认证平台: easyai claude --platform".cyan
243
+ puts " 2. 切换到 macOS/Linux 系统(如使用 Windows)".cyan unless check_details[:os_supported]
244
+ puts " 3. 切换系统地区设置到美国(英语)".cyan if !check_details[:is_us_region] || !check_details[:is_english]
245
+ puts " 4. 联系管理员获取其他认证方式".cyan
246
+ puts
247
+
248
+ return false
249
+ end
250
+
251
+ puts " ✅ 所有检查通过".green
252
+ puts
253
+
254
+ true
172
255
  end
173
256
 
174
257
  def load_local_config(config_path)
@@ -3,6 +3,7 @@ require 'yaml'
3
3
  require 'fileutils'
4
4
  require 'etc'
5
5
  require 'open3'
6
+ require_relative '../base/system_info'
6
7
 
7
8
  module EasyAI
8
9
  class Command
@@ -140,14 +141,11 @@ module EasyAI
140
141
  end
141
142
 
142
143
  # Keychain 认证信息 - authclaude.rb 中 configure_keychain 设置的
143
- if RUBY_PLATFORM.include?('darwin') && system('which security > /dev/null 2>&1')
144
+ if Base::SystemInfo.macos?
144
145
  keychain_entries = get_claude_keychain_entries
145
146
  keychain_entries.each do |service_name|
146
147
  puts " • macOS Keychain: #{service_name} (Claude认证信息)"
147
148
  end
148
- elsif !Gem.win_platform? && system('which secret-tool > /dev/null 2>&1')
149
- # Linux Secret Service 支持
150
- puts " • Linux Secret Service: Claude认证信息"
151
149
  end
152
150
 
153
151
  puts
@@ -277,18 +275,9 @@ module EasyAI
277
275
  end
278
276
 
279
277
  def clean_keychain_claude
280
- # 检查是否是 macOS 并且 security 命令可用
281
- if RUBY_PLATFORM.include?('darwin') && system('which security > /dev/null 2>&1')
282
- clean_keychain_claude_macos
283
- elsif !Gem.win_platform? && system('which secret-tool > /dev/null 2>&1')
284
- # Linux Secret Service 支持
285
- clean_keychain_claude_linux
286
- end
287
- end
278
+ return unless Base::SystemInfo.macos?
288
279
 
289
- def clean_keychain_claude_macos
290
-
291
- account_name = Etc.getlogin || ENV['USER'] || ENV['LOGNAME']
280
+ account_name = Base::SystemInfo.current_user
292
281
  keychain_entries = get_claude_keychain_entries
293
282
 
294
283
  if keychain_entries.empty?
@@ -317,19 +306,6 @@ module EasyAI
317
306
  end
318
307
  end
319
308
 
320
- def clean_keychain_claude_linux
321
- # Linux Secret Service 清理
322
- service_name = "Claude Code-credentials"
323
- account_name = Etc.getlogin || ENV['USER'] || ENV['LOGNAME']
324
-
325
- cmd = "secret-tool clear service '#{service_name}' username '#{account_name}' 2>/dev/null"
326
- if system(cmd)
327
- puts " ✓ 已清理Linux Secret Service: #{service_name}"
328
- end
329
- rescue => e
330
- puts " ✗ 清理Linux Secret Service失败: #{e.message}"
331
- end
332
-
333
309
  def has_claude_json_config?(claude_file)
334
310
  return false unless File.exist?(claude_file)
335
311
 
@@ -352,10 +328,10 @@ module EasyAI
352
328
  end
353
329
 
354
330
  def get_claude_keychain_entries
355
- return [] unless RUBY_PLATFORM.include?('darwin') && system('which security > /dev/null 2>&1')
331
+ return [] unless Base::SystemInfo.macos?
356
332
 
357
333
  entries = []
358
- account_name = Etc.getlogin || ENV['USER'] || ENV['LOGNAME']
334
+ account_name = Base::SystemInfo.current_user
359
335
 
360
336
  # 定义所有可能的 Claude 相关服务名
361
337
  # 基于 authclaude.rb 和常见的 Claude 应用服务名
@@ -392,37 +368,26 @@ module EasyAI
392
368
 
393
369
  def get_shell_files
394
370
  shell_files = []
395
- if Gem.win_platform?
396
- # Windows 平台配置
397
- powershell_profile = File.expand_path("~/Documents/WindowsPowerShell/Microsoft.PowerShell_profile.ps1")
398
- powershell_core_profile = File.expand_path("~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1")
399
-
400
- # 检查哪个 PowerShell 配置文件存在
401
- shell_files << powershell_core_profile if File.exist?(powershell_core_profile)
402
- shell_files << powershell_profile if File.exist?(powershell_profile)
371
+ current_shell = Base::SystemInfo.current_shell
372
+
373
+ case current_shell
374
+ when 'zsh'
375
+ shell_files << File.expand_path("~/.zshrc")
376
+ when 'bash'
377
+ bash_profile = File.expand_path("~/.bash_profile")
378
+ bashrc = File.expand_path("~/.bashrc")
379
+ shell_files << (File.exist?(bash_profile) ? bash_profile : bashrc)
380
+ when 'fish'
381
+ fish_config = File.expand_path("~/.config/fish/config.fish")
382
+ shell_files << fish_config
403
383
  else
404
- # Unix/Linux/macOS 平台配置
405
- current_shell = ENV['SHELL']&.split('/')&.last || 'bash'
406
-
407
- case current_shell
408
- when 'zsh'
409
- shell_files << File.expand_path("~/.zshrc")
410
- when 'bash'
411
- bash_profile = File.expand_path("~/.bash_profile")
412
- bashrc = File.expand_path("~/.bashrc")
413
- shell_files << (File.exist?(bash_profile) ? bash_profile : bashrc)
414
- when 'fish'
415
- fish_config = File.expand_path("~/.config/fish/config.fish")
416
- shell_files << fish_config
417
- else
418
- shell_files << File.expand_path("~/.profile")
419
- end
420
-
421
- # 总是添加 ~/.profile 作为备份
422
- profile_file = File.expand_path("~/.profile")
423
- shell_files << profile_file unless shell_files.include?(profile_file)
384
+ shell_files << File.expand_path("~/.profile")
424
385
  end
425
386
 
387
+ # 总是添加 ~/.profile 作为备份
388
+ profile_file = File.expand_path("~/.profile")
389
+ shell_files << profile_file unless shell_files.include?(profile_file)
390
+
426
391
  shell_files
427
392
  end
428
393
 
@@ -1,3 +1,5 @@
1
+ require_relative '../base/system_info'
2
+
1
3
  module EasyAI
2
4
  class Command
3
5
  class Gemini < Command
@@ -46,8 +48,8 @@ module EasyAI
46
48
  private
47
49
 
48
50
  def gemini_available?
49
- # 使用统一的跨平台命令检测
50
- EasyAI::CrossPlatform.command_exists?('gemini')
51
+ # 跨平台命令检测
52
+ Base::SystemInfo.which_command('gemini')
51
53
  end
52
54
  end
53
55
  end
@@ -2,6 +2,7 @@ require 'json'
2
2
  require 'uri'
3
3
  require 'colored2'
4
4
  require_relative '../config/config'
5
+ require_relative '../base/system_info'
5
6
 
6
7
  module EasyAI
7
8
  class Command
@@ -127,8 +128,8 @@ module EasyAI
127
128
  private
128
129
 
129
130
  def gpt_available?
130
- # 使用统一的跨平台命令检测
131
- EasyAI::CrossPlatform.command_exists?('openai')
131
+ # 跨平台命令检测
132
+ Base::SystemInfo.which_command('openai')
132
133
  end
133
134
 
134
135
  def load_local_config(config_file)
@@ -4,6 +4,7 @@ require 'open3'
4
4
  require 'fileutils'
5
5
  require_relative '../../config/config'
6
6
  require_relative '../../auth/authclaude'
7
+ require_relative '../../base/system_info'
7
8
 
8
9
  module EasyAI
9
10
  class Command
@@ -92,7 +93,7 @@ module EasyAI
92
93
  end
93
94
 
94
95
  # 2. 从 Keychain 读取凭证(macOS)
95
- if RUBY_PLATFORM.include?('darwin')
96
+ if Base::SystemInfo.macos?
96
97
  print_status("🔐 读取 Keychain", "Claude Code-credentials") if @verbose
97
98
 
98
99
  keychain_data = read_keychain_credentials
@@ -197,10 +198,10 @@ module EasyAI
197
198
  end
198
199
 
199
200
  def read_keychain_credentials
200
- return nil unless RUBY_PLATFORM.include?('darwin')
201
+ return nil unless Base::SystemInfo.macos?
201
202
 
202
203
  service_name = "Claude Code-credentials"
203
- account_name = Etc.getlogin || ENV['USER'] || ENV['LOGNAME']
204
+ account_name = Base::SystemInfo.current_user
204
205
 
205
206
  # 获取 Keychain 中的密码
206
207
  cmd = [
@@ -83,12 +83,36 @@ module EasyAI
83
83
  end
84
84
 
85
85
  def get_user_config_content(config_filename)
86
- # 直接使用 get_config 获取配置内容,它会自动处理解密和清理
87
- config = EasyAIConfig.get_config(config_filename, verbose: @verbose)
88
- return config if config
86
+ # 获取配置文件路径
87
+ config_path = EasyAIConfig.get_config_path(config_filename, verbose: @verbose)
88
+ return nil unless config_path
89
+
90
+ # 读取配置内容
91
+ begin
92
+ config_content = File.read(config_path)
93
+ config = JSON.parse(config_content)
94
+
95
+ # 添加配置文件路径信息(用于判断认证类型)
96
+ config['_config_path'] = config_filename if config
97
+
98
+ # 删除解密后的文件
99
+ if File.exist?(config_path) && config_path.end_with?('.json')
100
+ encrypted_path = "#{config_path}.encrypted"
101
+ # 只有存在对应的加密文件时才删除解密文件
102
+ if File.exist?(encrypted_path)
103
+ FileUtils.rm_f(config_path)
104
+ puts " 已清理临时解密文件: #{File.basename(config_path)}" if @verbose
105
+ end
106
+ end
89
107
 
90
- puts "✗ 无法加载配置文件: #{config_filename}" if @verbose
91
- nil
108
+ config
109
+ rescue JSON::ParserError => e
110
+ puts "✗ 解析配置文件失败: #{e.message}" if @verbose
111
+ nil
112
+ rescue => e
113
+ puts "✗ 读取配置文件失败: #{e.message}" if @verbose
114
+ nil
115
+ end
92
116
  end
93
117
 
94
118
  # JPS 登录获取用户名
@@ -198,15 +222,9 @@ module EasyAI
198
222
 
199
223
  # 专门处理 Claude 的用户配置查找
200
224
  def find_claude_user_config(claude_config, user_input_clean)
201
- auth_priority = ["claude_auth", "kimi_auth", "deepseek_auth"]
202
-
203
- # Windows 平台处理:从优先级中移除 claude_auth
204
- if Gem.win_platform?
205
- auth_priority = ["kimi_auth", "deepseek_auth"]
206
- puts " ⚠ Windows 平台跳过 claude_auth,使用其他认证平台" if @verbose
207
- end
225
+ auth_priority = ["claude_auth", "aliqwen_auth", "kimi_auth", "deepseek_auth"]
208
226
 
209
- if @platform && !@platform.empty?
227
+ if @platform
210
228
  # 指定了平台
211
229
  return find_claude_config_by_platform(claude_config, @platform, user_input_clean, auth_priority)
212
230
  else
@@ -216,13 +234,6 @@ module EasyAI
216
234
  end
217
235
 
218
236
  def find_claude_config_by_platform(claude_config, platform, user_input_clean, auth_priority)
219
- # Windows 平台特殊处理 claude_auth
220
- if Gem.win_platform? && platform == "claude_auth"
221
- puts "✗ Windows 平台不支持 claude_auth" if @verbose
222
- puts " 请选择其他认证平台:kimi_auth 或 deepseek_auth" if @verbose
223
- return nil
224
- end
225
-
226
237
  unless auth_priority.include?(platform)
227
238
  puts "✗ 认证平台 '#{platform}' 不存在" if @verbose
228
239
  puts " 支持的平台: #{auth_priority.join(', ')}" if @verbose
@@ -276,14 +287,7 @@ module EasyAI
276
287
  available_platforms = []
277
288
 
278
289
  # 检查用户在哪些平台有配置
279
- platforms_to_check = ["claude_auth", "kimi_auth", "deepseek_auth"]
280
-
281
- # Windows 平台跳过 claude_auth
282
- if Gem.win_platform?
283
- platforms_to_check = ["kimi_auth", "deepseek_auth"]
284
- end
285
-
286
- platforms_to_check.each do |auth_type|
290
+ ["claude_auth", "aliqwen_auth", "kimi_auth", "deepseek_auth"].each do |auth_type|
287
291
  if find_in_auth_type(claude_config, auth_type, user_input_clean)
288
292
  available_platforms << auth_type
289
293
  end
@@ -309,6 +313,7 @@ module EasyAI
309
313
  available_platforms.each_with_index do |platform, index|
310
314
  platform_name = case platform
311
315
  when "claude_auth" then "Claude 官方认证"
316
+ when "aliqwen_auth" then "阿里通义千问认证"
312
317
  when "kimi_auth" then "Kimi 认证"
313
318
  when "deepseek_auth" then "Deepseek 认证"
314
319
  else platform
@@ -1,3 +1,3 @@
1
1
  module EasyAI
2
- VERSION = '1.4.0'
2
+ VERSION = '1.5.0'
3
3
  end
data/lib/easyai.rb CHANGED
@@ -11,7 +11,7 @@ require 'easyai/command/update'
11
11
  require 'easyai/command/setup'
12
12
  require 'easyai/base/version_checker'
13
13
  require 'easyai/base/update_notifier'
14
- require 'easyai/base/cross_platform'
14
+ require 'easyai/base/system_info'
15
15
 
16
16
  module EasyAI
17
17
  # EasyAI 应用程序主类
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easyai
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wade
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-09-22 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: claide
@@ -122,6 +122,7 @@ executables:
122
122
  extensions: []
123
123
  extra_rdoc_files: []
124
124
  files:
125
+ - AGENTS.md
125
126
  - CLAUDE.md
126
127
  - README.md
127
128
  - bin/easyai
@@ -131,6 +132,7 @@ files:
131
132
  - lib/easyai/auth/jpsloginhelper.rb
132
133
  - lib/easyai/base/cross_platform.rb
133
134
  - lib/easyai/base/file_crypto.rb
135
+ - lib/easyai/base/system_info.rb
134
136
  - lib/easyai/base/system_keychain.rb
135
137
  - lib/easyai/base/update_notifier.rb
136
138
  - lib/easyai/base/version_checker.rb
@@ -166,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
166
168
  - !ruby/object:Gem::Version
167
169
  version: '0'
168
170
  requirements: []
169
- rubygems_version: 3.6.3
171
+ rubygems_version: 3.6.9
170
172
  specification_version: 4
171
173
  summary: Easy AI CLI tool wrapper
172
174
  test_files: []