easyai 1.1.0 → 1.1.1
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 +4 -4
- data/CLAUDE.md +72 -6
- data/easyai.gemspec +1 -1
- data/lib/easyai/auth/jpslogin.rb +20 -2
- data/lib/easyai/base/system_keychain.rb +50 -32
- data/lib/easyai/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60e6aec7d6d0d2475a75e26f61f21f1f5354b75ba828006293cdcdd3a6961f8a
|
4
|
+
data.tar.gz: bd0c31dccfcb0918707972a72415382b6ca778015ae67932626e4ad40933b747
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31725ddd0f5c1ce5e3acda536b8c502addefc67271abb0b719521349fd3f3d840f2c41151982fbaca3044d34e6e110ef5e663155ee5c71ff636a6eb5af6b749a
|
7
|
+
data.tar.gz: 3045e64aa95c96a0ca58934f080dc6e82512121348290db4b4d8859b0af016f343893a8208f281f6be33af75afd4e2160fbc04451adf8f89ff9d62a21b202719
|
data/CLAUDE.md
CHANGED
@@ -12,10 +12,17 @@ EasyAI 是一个 Ruby CLI 工具,作为 AI 命令行工具(Claude、Gemini
|
|
12
12
|
|
13
13
|
代码库采用基于 CLAide 的模块化架构:
|
14
14
|
|
15
|
-
-
|
15
|
+
- **应用入口 (`lib/easyai.rb`)**:`EasyAIApp` 类负责启动标志显示、版本检查和命令执行
|
16
|
+
- **主命令 (`lib/easyai/command.rb`)**:抽象基础命令,处理全局选项和帮助信息
|
16
17
|
- **AI 工具子命令 (`lib/easyai/command/*.rb`)**:每个 AI 工具(claude、gemini、gpt)都有自己的命令类,继承自主命令
|
17
18
|
- **工具命令 (`lib/easyai/command/utils/`)**:实用工具集合,包括文件加密解密功能
|
18
|
-
-
|
19
|
+
- **认证模块 (`lib/easyai/auth/`)**:处理 AI 工具的认证配置,如 `authclaude.rb`
|
20
|
+
- **配置管理 (`lib/easyai/config/`)**:管理配置文件的加载、保存和远程下载
|
21
|
+
- **基础模块 (`lib/easyai/base/`)**:
|
22
|
+
- `file_crypto.rb`:文件加密解密核心实现
|
23
|
+
- `system_keychain.rb`:系统钥匙串访问
|
24
|
+
- `version_checker.rb`:版本检查和更新
|
25
|
+
- `update_notifier.rb`:更新通知显示
|
19
26
|
- **环境隔离**:使用 Ruby 的 `exec()` 替换当前进程为目标 AI 工具,在注入环境变量的同时保持交互性
|
20
27
|
- **跨平台支持**:处理 Unix(`which`)和 Windows(`where`)之间的命令检测差异
|
21
28
|
|
@@ -37,6 +44,10 @@ gem install easyai-x.x.x.gem # 安装特定版本
|
|
37
44
|
# 测试加密解密功能
|
38
45
|
EASYAI_TEST_PASSWORD=testdir123 easyai utils encry /tmp/test_dir
|
39
46
|
EASYAI_TEST_PASSWORD=testdir123 easyai utils decry /tmp/test_dir_encrypted
|
47
|
+
|
48
|
+
# 调试模式
|
49
|
+
EASYAI_DEBUG=1 ruby bin/easyai claude # 启用调试信息输出
|
50
|
+
EASYAI_SKIP_FORCE_UPDATE=1 easyai claude # 跳过版本更新检查
|
40
51
|
```
|
41
52
|
|
42
53
|
### 配置管理
|
@@ -48,6 +59,9 @@ easyai --setup
|
|
48
59
|
easyai --config
|
49
60
|
|
50
61
|
# 配置文件:~/.easyai/config.yml
|
62
|
+
|
63
|
+
# 使用本地配置文件
|
64
|
+
easyai claude ./config.json # 加载指定的 JSON 配置文件
|
51
65
|
```
|
52
66
|
|
53
67
|
### 工具命令
|
@@ -89,14 +103,42 @@ easyai clean # 清理临时文件和缓存
|
|
89
103
|
- **密码管理**:支持交互式密码输入和环境变量密码(`EASYAI_TEST_PASSWORD`)
|
90
104
|
- **错误处理**:完整的错误处理和用户友好提示
|
91
105
|
|
106
|
+
### 认证优先级机制
|
107
|
+
AI 工具的认证令牌获取优先级(`lib/easyai/auth/authclaude.rb`):
|
108
|
+
1. **远程配置**:首先尝试下载远程配置文件
|
109
|
+
2. **本地配置**:如果指定了本地 JSON 配置文件,加载该文件
|
110
|
+
3. **系统钥匙串**:从系统钥匙串获取(macOS Keychain)
|
111
|
+
4. **环境变量**:从环境变量读取(如 `CLAUDE_CODE_OAUTH_TOKEN`)
|
112
|
+
5. **交互输入**:最后提示用户输入并保存到配置文件
|
113
|
+
|
114
|
+
### 版本更新机制
|
115
|
+
项目包含智能版本管理系统 (`lib/easyai/base/version_checker.rb`):
|
116
|
+
- **启动检查**:在 `EasyAIApp#run` 中自动执行版本检查
|
117
|
+
- **强制更新**:`check_force_update!` 方法会阻塞执行直到检查完成
|
118
|
+
- **同步检查**:`check_sync` 方法复用已获取的版本信息
|
119
|
+
- **更新通知**:`UpdateNotifier` 模块负责显示更新提示
|
120
|
+
- **跳过机制**:
|
121
|
+
- 设置 `EASYAI_SKIP_FORCE_UPDATE=1` 环境变量跳过更新检查
|
122
|
+
- `--help`、`--version` 命令自动跳过更新检查
|
123
|
+
- **更新命令**:`easyai update` 执行 `gem update easyai`
|
124
|
+
|
92
125
|
## 版本管理
|
93
126
|
|
94
127
|
版本在 `lib/easyai/version.rb` 中定义,自动被以下文件引用:
|
95
|
-
- `easyai.gemspec` 通过 `require_relative`
|
96
|
-
-
|
97
|
-
-
|
128
|
+
- `easyai.gemspec` 通过 `require_relative 'lib/easyai/version'`
|
129
|
+
- `lib/easyai/command.rb` 通过 `self.version = VERSION`
|
130
|
+
- `lib/easyai.rb` 在启动标志中使用 `VERSION`
|
131
|
+
- 发布脚本 (`test_local.sh`, `release_remote.sh`) 通过正则提取版本号
|
98
132
|
|
99
|
-
只需更新 `lib/easyai/version.rb`
|
133
|
+
只需更新 `lib/easyai/version.rb` 中的 `VERSION` 常量 - 其他引用是自动的。
|
134
|
+
|
135
|
+
### 发布流程
|
136
|
+
`release_remote.sh` 脚本完成以下步骤:
|
137
|
+
1. 提交所有未提交的更改
|
138
|
+
2. 合并到 master 分支
|
139
|
+
3. 构建和安装 gem
|
140
|
+
4. 创建并推送 Git 标签(格式:v版本号)
|
141
|
+
5. 发布到 RubyGems
|
100
142
|
|
101
143
|
## 依赖管理
|
102
144
|
|
@@ -104,6 +146,7 @@ easyai clean # 清理临时文件和缓存
|
|
104
146
|
- **运行时依赖**:
|
105
147
|
- `claide ~> 1.0`:CLI 框架
|
106
148
|
- `colored2 ~> 3.1`:终端颜色输出
|
149
|
+
- `webrick ~> 1.9`:Web 服务器(用于版本检查,使用 1.9.1)
|
107
150
|
- **开发依赖**:
|
108
151
|
- `bundler ~> 2.0`:依赖管理
|
109
152
|
- `rake ~> 13.0`:构建工具
|
@@ -117,6 +160,8 @@ easyai clean # 清理临时文件和缓存
|
|
117
160
|
3. **测试流程**:使用 `./test_local.sh` 进行本地测试,确保功能正常后再发布
|
118
161
|
4. **跨平台兼容性**:添加新功能时要考虑 Windows、Linux、macOS 的兼容性
|
119
162
|
5. **环境隔离**:确保不影响用户的系统环境变量,所有配置都应在子进程中生效
|
163
|
+
6. **错误处理**:所有命令都应有完善的错误处理和用户友好的错误提示
|
164
|
+
7. **调试信息**:使用 `ENV['EASYAI_DEBUG']` 控制调试信息输出
|
120
165
|
|
121
166
|
## 中文化要求
|
122
167
|
|
@@ -183,6 +228,22 @@ DESC
|
|
183
228
|
|
184
229
|
## 技术实现注意事项
|
185
230
|
|
231
|
+
### 启动流程
|
232
|
+
1. **入口点**:`bin/easyai` 调用 `EasyAI::EasyAIApp.new.run(ARGV)`
|
233
|
+
2. **启动标志**:`EasyAIApp#show_banner` 显示版本信息(跳过 --help 和 --version)
|
234
|
+
3. **版本检查**:`EasyAIApp#check_version_before_run` 执行更新检查
|
235
|
+
- 强制更新检查:`Base::VersionChecker.check_force_update!`
|
236
|
+
- 同步检查:`Base::VersionChecker.check_sync`
|
237
|
+
- 更新通知:`Base::UpdateNotifier.maybe_show_notification`
|
238
|
+
4. **命令执行**:`EasyAI::Command.run(argv)` 处理实际命令
|
239
|
+
|
240
|
+
### 输出格式规范
|
241
|
+
各命令使用统一的输出格式(参考 `lib/easyai/command/claude.rb`):
|
242
|
+
- `print_status(icon_text, detail)`:状态信息,图标左对齐,详情用 cyan 色
|
243
|
+
- `print_success(message)`:成功信息,绿色勾号
|
244
|
+
- `print_warning(message)`:警告信息,黄色警告符
|
245
|
+
- `print_error(message)`:错误信息,红色叉号
|
246
|
+
|
186
247
|
### CLAide 框架相关
|
187
248
|
|
188
249
|
#### 1. --help 参数处理
|
@@ -233,6 +294,11 @@ end
|
|
233
294
|
- 父类的 `validate!` 会处理 help 标志
|
234
295
|
- 如果检测到 help 标志,会调用 `help!` 抛出异常,阻止 `run` 方法执行
|
235
296
|
|
297
|
+
#### 4. 抽象命令设置
|
298
|
+
使用 `abstract_command = true` 标记抽象命令:
|
299
|
+
- `EasyAI::Command` 是抽象基类
|
300
|
+
- `EasyAI::Command::Utils` 也是抽象命令,包含子命令 Encry 和 Decry
|
301
|
+
|
236
302
|
### 命令类继承结构
|
237
303
|
```
|
238
304
|
CLAide::Command
|
data/easyai.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_dependency 'claide', '~> 1.0'
|
22
22
|
spec.add_dependency 'colored2', '~> 3.1'
|
23
|
-
spec.add_dependency 'webrick', '~> 1.
|
23
|
+
spec.add_dependency 'webrick', '~> 1.9'
|
24
24
|
|
25
25
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
26
26
|
spec.add_development_dependency 'rake', '~> 13.0'
|
data/lib/easyai/auth/jpslogin.rb
CHANGED
@@ -78,6 +78,16 @@ module EasyAI
|
|
78
78
|
|
79
79
|
private
|
80
80
|
|
81
|
+
# 获取跨平台的 null 设备路径
|
82
|
+
def get_null_device
|
83
|
+
case RbConfig::CONFIG['host_os']
|
84
|
+
when /mswin|mingw|cygwin/
|
85
|
+
'NUL'
|
86
|
+
else
|
87
|
+
'/dev/null'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
81
91
|
# 完整的授权和登录流程
|
82
92
|
def authorize_and_login
|
83
93
|
# 构建授权 URL
|
@@ -150,7 +160,8 @@ module EasyAI
|
|
150
160
|
def open_browser(url)
|
151
161
|
case RbConfig::CONFIG['host_os']
|
152
162
|
when /mswin|mingw|cygwin/
|
153
|
-
|
163
|
+
# Windows: 使用双引号包围URL避免参数解析问题
|
164
|
+
system("start \"\" \"#{url}\"")
|
154
165
|
when /darwin/
|
155
166
|
system("open", url)
|
156
167
|
when /linux|bsd/
|
@@ -175,14 +186,21 @@ module EasyAI
|
|
175
186
|
begin
|
176
187
|
server = WEBrick::HTTPServer.new(
|
177
188
|
Port: @server_port,
|
178
|
-
|
189
|
+
BindAddress: '127.0.0.1',
|
190
|
+
Logger: WEBrick::Log.new(get_null_device),
|
179
191
|
AccessLog: []
|
180
192
|
)
|
181
193
|
rescue Errno::EADDRINUSE
|
182
194
|
puts "✗ 端口 #{@server_port} 仍被占用,无法启动服务器"
|
183
195
|
return nil
|
196
|
+
rescue Errno::ENOENT
|
197
|
+
puts "✗ 启动服务器失败: 系统找不到指定的路径或文件"
|
198
|
+
return nil
|
184
199
|
rescue => e
|
185
200
|
puts "✗ 启动服务器失败: #{e.message}"
|
201
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
202
|
+
puts " 提示: 如果是Windows系统,请确保防火墙允许Ruby访问网络"
|
203
|
+
end
|
186
204
|
return nil
|
187
205
|
end
|
188
206
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rbconfig'
|
2
|
+
require 'fileutils'
|
2
3
|
|
3
4
|
module EasyAI
|
4
5
|
module Base
|
@@ -120,15 +121,30 @@ module EasyAI
|
|
120
121
|
# 使用 cmdkey 命令存储密码
|
121
122
|
target_name = "#{SERVICE_NAME}_#{ACCOUNT_NAME}"
|
122
123
|
cmd = "cmdkey /add:#{target_name} /user:#{ACCOUNT_NAME} /pass:\"#{password}\""
|
123
|
-
|
124
|
+
|
124
125
|
success = system(cmd + " >nul 2>&1")
|
125
|
-
|
126
|
+
|
127
|
+
# 同时保存到文件作为备用
|
128
|
+
keyring_dir = File.expand_path('~/.easyai/.keyring')
|
129
|
+
FileUtils.mkdir_p(keyring_dir) unless Dir.exist?(keyring_dir)
|
130
|
+
|
131
|
+
keyring_file = File.join(keyring_dir, 'windows_credential')
|
132
|
+
|
133
|
+
begin
|
134
|
+
require 'base64'
|
135
|
+
encoded_password = Base64.strict_encode64(password)
|
136
|
+
File.write(keyring_file, encoded_password)
|
137
|
+
File.chmod(0600, keyring_file) rescue nil # 尝试设置权限,Windows 可能不支持
|
138
|
+
rescue => e
|
139
|
+
# 忽略文件写入错误
|
140
|
+
end
|
141
|
+
|
126
142
|
if success
|
127
143
|
puts "✓ 密码已安全存储到 Windows 凭据管理器"
|
128
144
|
else
|
129
145
|
puts "⚠ 无法存储密码到 Windows 凭据管理器"
|
130
146
|
end
|
131
|
-
|
147
|
+
|
132
148
|
success
|
133
149
|
rescue => e
|
134
150
|
puts "⚠ Windows 凭据存储出错: #{e.message}"
|
@@ -138,33 +154,29 @@ module EasyAI
|
|
138
154
|
# Windows 凭据管理器密码获取
|
139
155
|
def self.get_password_windows
|
140
156
|
target_name = "#{SERVICE_NAME}_#{ACCOUNT_NAME}"
|
141
|
-
|
157
|
+
|
142
158
|
# 首先检查凭据是否存在
|
143
|
-
check_cmd = "cmdkey /list:#{target_name} >nul
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
# 如果无法获取密码但凭据存在,返回特殊标识
|
166
|
-
return nil if password.empty? || password == "CREDENTIAL_EXISTS_BUT_CANNOT_RETRIEVE"
|
167
|
-
password
|
159
|
+
check_cmd = "cmdkey /list:#{target_name} 2>nul"
|
160
|
+
check_output = `#{check_cmd}`
|
161
|
+
return nil unless check_output.include?(target_name)
|
162
|
+
|
163
|
+
# 使用备用文件存储方式
|
164
|
+
# 因为 Windows 凭据管理器的密码获取需要特殊权限或 CredentialManager 模块
|
165
|
+
# 我们使用加密文件作为备用方案
|
166
|
+
keyring_file = File.expand_path('~/.easyai/.keyring/windows_credential')
|
167
|
+
|
168
|
+
if File.exist?(keyring_file)
|
169
|
+
begin
|
170
|
+
require 'base64'
|
171
|
+
encoded_password = File.read(keyring_file).chomp
|
172
|
+
Base64.strict_decode64(encoded_password)
|
173
|
+
rescue => e
|
174
|
+
nil
|
175
|
+
end
|
176
|
+
else
|
177
|
+
# 提示用户重新输入密码
|
178
|
+
nil
|
179
|
+
end
|
168
180
|
rescue => e
|
169
181
|
nil
|
170
182
|
end
|
@@ -173,13 +185,19 @@ module EasyAI
|
|
173
185
|
def self.delete_password_windows
|
174
186
|
target_name = "#{SERVICE_NAME}_#{ACCOUNT_NAME}"
|
175
187
|
cmd = "cmdkey /delete:#{target_name}"
|
176
|
-
|
188
|
+
|
177
189
|
success = system(cmd + " >nul 2>&1")
|
178
|
-
|
190
|
+
|
191
|
+
# 同时删除备用文件
|
192
|
+
keyring_file = File.expand_path('~/.easyai/.keyring/windows_credential')
|
193
|
+
if File.exist?(keyring_file)
|
194
|
+
File.delete(keyring_file) rescue nil
|
195
|
+
end
|
196
|
+
|
179
197
|
if success
|
180
198
|
puts "✓ 已从 Windows 凭据管理器删除存储的密码"
|
181
199
|
end
|
182
|
-
|
200
|
+
|
183
201
|
success
|
184
202
|
rescue => e
|
185
203
|
false
|
data/lib/easyai/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: easyai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wade
|
@@ -43,14 +43,14 @@ dependencies:
|
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '1.
|
46
|
+
version: '1.9'
|
47
47
|
type: :runtime
|
48
48
|
prerelease: false
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '1.
|
53
|
+
version: '1.9'
|
54
54
|
- !ruby/object:Gem::Dependency
|
55
55
|
name: bundler
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|