easyai 1.2.0 → 1.3.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 +4 -4
- data/CLAUDE.md +87 -220
- data/easyai.gemspec +1 -0
- data/lib/easyai/auth/authclaude.rb +260 -135
- data/lib/easyai/auth/jpsloginhelper.rb +98 -0
- data/lib/easyai/base/cross_platform.rb +149 -0
- data/lib/easyai/base/file_crypto.rb +50 -9
- data/lib/easyai/base/system_keychain.rb +52 -18
- data/lib/easyai/command/claude.rb +76 -51
- data/lib/easyai/command/clean.rb +56 -20
- data/lib/easyai/command/gemini.rb +2 -6
- data/lib/easyai/command/gpt.rb +17 -21
- data/lib/easyai/command/setup.rb +494 -0
- data/lib/easyai/config/config.rb +284 -595
- data/lib/easyai/config/easyai_config.rb +258 -0
- data/lib/easyai/version.rb +1 -1
- data/lib/easyai.rb +32 -3
- metadata +27 -4
- data/lib/easyai/auth/jpslogin.rb +0 -655
data/lib/easyai/command/clean.rb
CHANGED
@@ -140,11 +140,14 @@ module EasyAI
|
|
140
140
|
end
|
141
141
|
|
142
142
|
# Keychain 认证信息 - authclaude.rb 中 configure_keychain 设置的
|
143
|
-
if RUBY_PLATFORM.include?('darwin')
|
143
|
+
if RUBY_PLATFORM.include?('darwin') && system('which security > /dev/null 2>&1')
|
144
144
|
keychain_entries = get_claude_keychain_entries
|
145
145
|
keychain_entries.each do |service_name|
|
146
146
|
puts " • macOS Keychain: #{service_name} (Claude认证信息)"
|
147
147
|
end
|
148
|
+
elsif !Gem.win_platform? && system('which secret-tool > /dev/null 2>&1')
|
149
|
+
# Linux Secret Service 支持
|
150
|
+
puts " • Linux Secret Service: Claude认证信息"
|
148
151
|
end
|
149
152
|
|
150
153
|
puts
|
@@ -274,7 +277,16 @@ module EasyAI
|
|
274
277
|
end
|
275
278
|
|
276
279
|
def clean_keychain_claude
|
277
|
-
|
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
|
288
|
+
|
289
|
+
def clean_keychain_claude_macos
|
278
290
|
|
279
291
|
account_name = Etc.getlogin || ENV['USER'] || ENV['LOGNAME']
|
280
292
|
keychain_entries = get_claude_keychain_entries
|
@@ -305,6 +317,19 @@ module EasyAI
|
|
305
317
|
end
|
306
318
|
end
|
307
319
|
|
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
|
+
|
308
333
|
def has_claude_json_config?(claude_file)
|
309
334
|
return false unless File.exist?(claude_file)
|
310
335
|
|
@@ -327,7 +352,7 @@ module EasyAI
|
|
327
352
|
end
|
328
353
|
|
329
354
|
def get_claude_keychain_entries
|
330
|
-
return [] unless RUBY_PLATFORM.include?('darwin')
|
355
|
+
return [] unless RUBY_PLATFORM.include?('darwin') && system('which security > /dev/null 2>&1')
|
331
356
|
|
332
357
|
entries = []
|
333
358
|
account_name = Etc.getlogin || ENV['USER'] || ENV['LOGNAME']
|
@@ -367,26 +392,37 @@ module EasyAI
|
|
367
392
|
|
368
393
|
def get_shell_files
|
369
394
|
shell_files = []
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
shell_files << (File.exist?(bash_profile) ? bash_profile : bashrc)
|
379
|
-
when 'fish'
|
380
|
-
fish_config = File.expand_path("~/.config/fish/config.fish")
|
381
|
-
shell_files << fish_config
|
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)
|
382
403
|
else
|
383
|
-
|
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
424
|
end
|
385
425
|
|
386
|
-
# 总是添加 ~/.profile 作为备份
|
387
|
-
profile_file = File.expand_path("~/.profile")
|
388
|
-
shell_files << profile_file unless shell_files.include?(profile_file)
|
389
|
-
|
390
426
|
shell_files
|
391
427
|
end
|
392
428
|
|
@@ -46,12 +46,8 @@ module EasyAI
|
|
46
46
|
private
|
47
47
|
|
48
48
|
def gemini_available?
|
49
|
-
#
|
50
|
-
|
51
|
-
system('where gemini >nul 2>&1')
|
52
|
-
else
|
53
|
-
system('which gemini > /dev/null 2>&1')
|
54
|
-
end
|
49
|
+
# 使用统一的跨平台命令检测
|
50
|
+
EasyAI::CrossPlatform.command_exists?('gemini')
|
55
51
|
end
|
56
52
|
end
|
57
53
|
end
|
data/lib/easyai/command/gpt.rb
CHANGED
@@ -127,12 +127,8 @@ module EasyAI
|
|
127
127
|
private
|
128
128
|
|
129
129
|
def gpt_available?
|
130
|
-
#
|
131
|
-
|
132
|
-
system('where openai >nul 2>&1')
|
133
|
-
else
|
134
|
-
system('which openai > /dev/null 2>&1')
|
135
|
-
end
|
130
|
+
# 使用统一的跨平台命令检测
|
131
|
+
EasyAI::CrossPlatform.command_exists?('openai')
|
136
132
|
end
|
137
133
|
|
138
134
|
def load_local_config(config_file)
|
@@ -198,16 +194,6 @@ module EasyAI
|
|
198
194
|
simplified_proxies = proxy_urls.map { |url| mask_url(url) }
|
199
195
|
print_status("🌐 代理已配置", simplified_proxies.join(', '))
|
200
196
|
|
201
|
-
if @verbose_mode
|
202
|
-
# verbose 模式:显示更多状态信息(但不显示敏感内容)
|
203
|
-
puts " 代理类型: #{proxy_urls.uniq.count == 1 ? '统一代理' : '分离代理'}"
|
204
|
-
proxy_urls.uniq.each do |url|
|
205
|
-
uri = URI(url) rescue nil
|
206
|
-
if uri
|
207
|
-
puts " 代理服务器: #{uri.host}:#{uri.port}"
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
197
|
end
|
212
198
|
end
|
213
199
|
|
@@ -228,11 +214,21 @@ module EasyAI
|
|
228
214
|
def mask_url(url)
|
229
215
|
return url unless url
|
230
216
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
217
|
+
begin
|
218
|
+
uri = URI(url)
|
219
|
+
|
220
|
+
# 隐藏IP地址的前两段,显示后两段
|
221
|
+
masked_host = uri.host
|
222
|
+
if uri.host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
|
223
|
+
masked_host = "*.*.#{$3}.#{$4}"
|
224
|
+
end
|
225
|
+
|
226
|
+
if uri.password
|
227
|
+
"#{uri.scheme}://***@#{masked_host}:#{uri.port}"
|
228
|
+
else
|
229
|
+
"#{uri.scheme}://#{masked_host}:#{uri.port}"
|
230
|
+
end
|
231
|
+
rescue URI::InvalidURIError
|
236
232
|
url
|
237
233
|
end
|
238
234
|
end
|
@@ -0,0 +1,494 @@
|
|
1
|
+
require_relative '../config/easyai_config'
|
2
|
+
require_relative '../base/system_keychain'
|
3
|
+
require 'colored2'
|
4
|
+
require 'json'
|
5
|
+
require 'open3'
|
6
|
+
|
7
|
+
module EasyAI
|
8
|
+
class Command
|
9
|
+
class Setup < Command
|
10
|
+
self.summary = '初始化配置环境'
|
11
|
+
self.description = <<-DESC
|
12
|
+
初始化 EasyAI 配置环境,自动下载并设置配置仓库。
|
13
|
+
|
14
|
+
主要功能:
|
15
|
+
* 自动下载配置仓库
|
16
|
+
* 验证配置完整性
|
17
|
+
* 解密配置文件(如需要)
|
18
|
+
* 显示可用用户列表
|
19
|
+
* 管理密码存储
|
20
|
+
|
21
|
+
使用示例:
|
22
|
+
$ easyai setup # 标准初始化
|
23
|
+
$ easyai setup --force # 强制重新下载配置
|
24
|
+
$ easyai setup --branch dev # 使用开发分支
|
25
|
+
$ easyai setup --verify # 仅验证配置状态
|
26
|
+
$ easyai setup --list-users # 列出可用用户
|
27
|
+
DESC
|
28
|
+
|
29
|
+
def self.options
|
30
|
+
[
|
31
|
+
['--force', '强制重新下载配置仓库'],
|
32
|
+
['--branch=BRANCH', '指定配置仓库分支 (默认: master)'],
|
33
|
+
['--verify', '仅验证现有配置,不进行修改'],
|
34
|
+
['--list-users', '列出所有可用用户'],
|
35
|
+
['--clean', '清理所有临时解密文件'],
|
36
|
+
['--no-password', '跳过密码设置'],
|
37
|
+
].concat(super)
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(argv)
|
41
|
+
@force = argv.flag?('force')
|
42
|
+
@branch = argv.option('branch') || 'master'
|
43
|
+
@verify_only = argv.flag?('verify')
|
44
|
+
@list_users = argv.flag?('list-users')
|
45
|
+
@clean = argv.flag?('clean')
|
46
|
+
@skip_password = argv.flag?('no-password')
|
47
|
+
super
|
48
|
+
|
49
|
+
@config_dir = File.expand_path('~/.easyai')
|
50
|
+
@repo_dir = File.join(@config_dir, 'EasyAISetting')
|
51
|
+
end
|
52
|
+
|
53
|
+
def run
|
54
|
+
show_welcome_banner
|
55
|
+
|
56
|
+
if @verify_only
|
57
|
+
verify_configuration
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
if @clean
|
62
|
+
cleanup_files
|
63
|
+
return
|
64
|
+
end
|
65
|
+
|
66
|
+
if @list_users
|
67
|
+
show_available_users
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
# 执行主要设置流程
|
72
|
+
perform_setup
|
73
|
+
|
74
|
+
# 显示完成信息
|
75
|
+
show_completion_info
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def show_welcome_banner
|
81
|
+
puts
|
82
|
+
puts "🚀 " + "EasyAI 配置环境初始化".green.bold
|
83
|
+
puts "=" * 60
|
84
|
+
puts
|
85
|
+
end
|
86
|
+
|
87
|
+
def perform_setup
|
88
|
+
step_counter = 0
|
89
|
+
|
90
|
+
# 步骤1: 检查并创建目录
|
91
|
+
step_counter += 1
|
92
|
+
print_step(step_counter, "检查配置目录")
|
93
|
+
ensure_directories
|
94
|
+
|
95
|
+
# 步骤2: 下载或更新配置仓库
|
96
|
+
step_counter += 1
|
97
|
+
print_step(step_counter, "下载配置仓库")
|
98
|
+
if @force || !repo_exists?
|
99
|
+
download_repo
|
100
|
+
else
|
101
|
+
update_repo
|
102
|
+
end
|
103
|
+
|
104
|
+
# 步骤3: 处理加密文件
|
105
|
+
step_counter += 1
|
106
|
+
print_step(step_counter, "处理配置文件")
|
107
|
+
handle_encrypted_files unless @skip_password
|
108
|
+
|
109
|
+
# 步骤4: 验证配置
|
110
|
+
step_counter += 1
|
111
|
+
print_step(step_counter, "验证配置完整性")
|
112
|
+
verify_basic_config
|
113
|
+
|
114
|
+
# 步骤5: 显示可用用户
|
115
|
+
step_counter += 1
|
116
|
+
print_step(step_counter, "加载用户列表")
|
117
|
+
show_available_users(compact: true)
|
118
|
+
end
|
119
|
+
|
120
|
+
def print_step(number, description)
|
121
|
+
puts "\n" + "步骤 #{number}:".cyan + " #{description}"
|
122
|
+
puts "-" * 40
|
123
|
+
end
|
124
|
+
|
125
|
+
def ensure_directories
|
126
|
+
unless Dir.exist?(@config_dir)
|
127
|
+
FileUtils.mkdir_p(@config_dir)
|
128
|
+
puts " ✓ 创建配置目录: #{@config_dir}"
|
129
|
+
else
|
130
|
+
puts " ✓ 配置目录已存在: #{@config_dir}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def repo_exists?
|
135
|
+
Dir.exist?(@repo_dir) && Dir.exist?(File.join(@repo_dir, '.git'))
|
136
|
+
end
|
137
|
+
|
138
|
+
def download_repo
|
139
|
+
if repo_exists? && @force
|
140
|
+
puts " 正在删除现有仓库..."
|
141
|
+
FileUtils.rm_rf(@repo_dir)
|
142
|
+
end
|
143
|
+
|
144
|
+
puts " 正在从 Gitee 下载配置仓库 (#{@branch} 分支)..."
|
145
|
+
|
146
|
+
# Gitee 不需要代理,临时清除代理环境变量
|
147
|
+
output = nil
|
148
|
+
success = false
|
149
|
+
|
150
|
+
if Gem.win_platform?
|
151
|
+
# Windows: 保存并清除环境变量
|
152
|
+
old_env = {}
|
153
|
+
['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy'].each do |key|
|
154
|
+
old_env[key] = ENV[key]
|
155
|
+
ENV.delete(key)
|
156
|
+
end
|
157
|
+
|
158
|
+
begin
|
159
|
+
cmd = "git clone --depth 1 --branch #{@branch} #{EasyAIConfig::REPO_URL} #{@repo_dir} 2>&1"
|
160
|
+
puts " 执行命令: #{cmd}" if ENV['EASYAI_DEBUG']
|
161
|
+
output = `#{cmd}`
|
162
|
+
success = $?.success?
|
163
|
+
ensure
|
164
|
+
# 恢复环境变量
|
165
|
+
old_env.each { |k, v| ENV[k] = v if v }
|
166
|
+
end
|
167
|
+
else
|
168
|
+
# Unix/Linux/macOS: 使用 env -u
|
169
|
+
cmd = "env -u HTTP_PROXY -u HTTPS_PROXY -u http_proxy -u https_proxy git clone --depth 1 --branch #{@branch} #{EasyAIConfig::REPO_URL} #{@repo_dir} 2>&1"
|
170
|
+
puts " 执行命令: #{cmd}" if ENV['EASYAI_DEBUG']
|
171
|
+
output = `#{cmd}`
|
172
|
+
success = $?.success?
|
173
|
+
end
|
174
|
+
|
175
|
+
if success
|
176
|
+
puts " ✓ 配置仓库下载成功"
|
177
|
+
else
|
178
|
+
puts " ✗ 下载失败: #{output}".red
|
179
|
+
exit 1
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def update_repo
|
184
|
+
puts " 检查配置仓库更新..."
|
185
|
+
|
186
|
+
Dir.chdir(@repo_dir) do
|
187
|
+
# 获取当前分支
|
188
|
+
current_branch = `git branch --show-current`.chomp
|
189
|
+
|
190
|
+
if current_branch != @branch
|
191
|
+
puts " 切换到 #{@branch} 分支..."
|
192
|
+
# 不使用代理
|
193
|
+
if Gem.win_platform?
|
194
|
+
# Windows: 临时清除环境变量
|
195
|
+
old_env = {}
|
196
|
+
['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy'].each do |key|
|
197
|
+
old_env[key] = ENV[key]
|
198
|
+
ENV.delete(key)
|
199
|
+
end
|
200
|
+
|
201
|
+
begin
|
202
|
+
system("git fetch origin #{@branch} > nul 2>&1")
|
203
|
+
ensure
|
204
|
+
old_env.each { |k, v| ENV[k] = v if v }
|
205
|
+
end
|
206
|
+
else
|
207
|
+
system("env -u HTTP_PROXY -u HTTPS_PROXY -u http_proxy -u https_proxy git fetch origin #{@branch} > /dev/null 2>&1")
|
208
|
+
end
|
209
|
+
system("git checkout #{@branch} > /dev/null 2>&1")
|
210
|
+
end
|
211
|
+
|
212
|
+
# 拉取更新(不使用代理)
|
213
|
+
output = nil
|
214
|
+
if Gem.win_platform?
|
215
|
+
# Windows: 临时清除环境变量
|
216
|
+
old_env = {}
|
217
|
+
['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy'].each do |key|
|
218
|
+
old_env[key] = ENV[key]
|
219
|
+
ENV.delete(key)
|
220
|
+
end
|
221
|
+
|
222
|
+
begin
|
223
|
+
output = `git pull --force 2>&1`
|
224
|
+
ensure
|
225
|
+
old_env.each { |k, v| ENV[k] = v if v }
|
226
|
+
end
|
227
|
+
else
|
228
|
+
output = `env -u HTTP_PROXY -u HTTPS_PROXY -u http_proxy -u https_proxy git pull --force 2>&1`
|
229
|
+
end
|
230
|
+
|
231
|
+
if $?.success?
|
232
|
+
if output.include?("Already up to date")
|
233
|
+
puts " ✓ 配置已是最新版本"
|
234
|
+
else
|
235
|
+
puts " ✓ 配置已更新到最新版本"
|
236
|
+
end
|
237
|
+
else
|
238
|
+
puts " ⚠️ 更新失败,使用现有配置"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def handle_encrypted_files
|
244
|
+
encrypted_files = Dir.glob(File.join(@repo_dir, '**/*.encrypted'))
|
245
|
+
|
246
|
+
if encrypted_files.empty?
|
247
|
+
puts " ✓ 未发现加密文件"
|
248
|
+
return
|
249
|
+
end
|
250
|
+
|
251
|
+
puts " 发现 #{encrypted_files.length} 个加密文件"
|
252
|
+
|
253
|
+
# 获取密码
|
254
|
+
password = get_setup_password
|
255
|
+
return unless password
|
256
|
+
|
257
|
+
# 测试密码
|
258
|
+
if test_password(password, encrypted_files.first)
|
259
|
+
puts " ✓ 密码验证成功"
|
260
|
+
|
261
|
+
# 保存到钥匙串
|
262
|
+
if should_save_password?
|
263
|
+
if Base::SystemKeychain.store_password(password)
|
264
|
+
puts " ✓ 密码已保存到系统钥匙串"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
else
|
268
|
+
puts " ✗ 密码验证失败".red
|
269
|
+
puts " 提示: 配置文件将在使用时解密"
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def get_setup_password
|
274
|
+
# 优先使用环境变量
|
275
|
+
if ENV['EASYAI_TEST_PASSWORD']
|
276
|
+
puts " 使用环境变量中的密码"
|
277
|
+
return ENV['EASYAI_TEST_PASSWORD']
|
278
|
+
end
|
279
|
+
|
280
|
+
# 检查钥匙串
|
281
|
+
stored_password = Base::SystemKeychain.get_stored_password
|
282
|
+
if stored_password && !stored_password.empty?
|
283
|
+
puts " 使用系统钥匙串中的密码"
|
284
|
+
return stored_password
|
285
|
+
end
|
286
|
+
|
287
|
+
# 提示用户输入
|
288
|
+
puts " 配置文件已加密,请输入解密密码"
|
289
|
+
print " 密码: "
|
290
|
+
|
291
|
+
begin
|
292
|
+
require 'io/console'
|
293
|
+
password = STDIN.noecho(&:gets)&.chomp
|
294
|
+
puts
|
295
|
+
password
|
296
|
+
rescue
|
297
|
+
nil
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def test_password(password, test_file)
|
302
|
+
begin
|
303
|
+
require_relative '../base/file_crypto'
|
304
|
+
key = Base::FileCrypto.generate_key(password)
|
305
|
+
content = File.read(test_file)
|
306
|
+
Base::FileCrypto.aes_128_ecb_decrypt(key, content)
|
307
|
+
true
|
308
|
+
rescue
|
309
|
+
false
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
def should_save_password?
|
314
|
+
print " 是否保存密码到系统钥匙串?(y/n): "
|
315
|
+
response = STDIN.gets&.chomp&.downcase
|
316
|
+
response == 'y' || response == 'yes'
|
317
|
+
end
|
318
|
+
|
319
|
+
def verify_basic_config
|
320
|
+
required_files = [
|
321
|
+
'index.json.encrypted',
|
322
|
+
'jps_client_config.json.encrypted'
|
323
|
+
]
|
324
|
+
|
325
|
+
missing_files = []
|
326
|
+
required_files.each do |file|
|
327
|
+
path = File.join(@repo_dir, file)
|
328
|
+
if File.exist?(path)
|
329
|
+
puts " ✓ #{file}"
|
330
|
+
else
|
331
|
+
missing_files << file
|
332
|
+
puts " ✗ #{file} (缺失)".red
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
if missing_files.empty?
|
337
|
+
puts " ✓ 核心配置文件完整"
|
338
|
+
else
|
339
|
+
puts " ⚠️ 缺少 #{missing_files.length} 个配置文件".yellow
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def show_available_users(compact: false)
|
344
|
+
# 初始化配置
|
345
|
+
EasyAIConfig.initialize(verbose: false)
|
346
|
+
|
347
|
+
# 获取 index 配置
|
348
|
+
index_config = EasyAIConfig.get_config('index', verbose: false)
|
349
|
+
|
350
|
+
unless index_config
|
351
|
+
puts " ✗ 无法加载用户配置".red
|
352
|
+
return
|
353
|
+
end
|
354
|
+
|
355
|
+
if compact
|
356
|
+
user_count = count_users(index_config)
|
357
|
+
puts " ✓ 发现 #{user_count} 个可用用户配置"
|
358
|
+
else
|
359
|
+
display_users_table(index_config)
|
360
|
+
end
|
361
|
+
rescue => e
|
362
|
+
puts " ✗ 加载用户列表失败: #{e.message}".red
|
363
|
+
end
|
364
|
+
|
365
|
+
def count_users(config)
|
366
|
+
count = 0
|
367
|
+
|
368
|
+
if config.is_a?(Hash)
|
369
|
+
%w[claude gemini gpt].each do |tool|
|
370
|
+
next unless config[tool]
|
371
|
+
|
372
|
+
if tool == "claude" && config[tool].is_a?(Hash)
|
373
|
+
config[tool].each do |_auth_type, users|
|
374
|
+
count += users.keys.length if users.is_a?(Hash)
|
375
|
+
end
|
376
|
+
elsif config[tool].is_a?(Hash)
|
377
|
+
count += config[tool].keys.length
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
count
|
383
|
+
end
|
384
|
+
|
385
|
+
def display_users_table(config)
|
386
|
+
puts "\n可用用户列表:"
|
387
|
+
puts "=" * 60
|
388
|
+
|
389
|
+
%w[claude gemini gpt].each do |tool|
|
390
|
+
next unless config[tool]
|
391
|
+
|
392
|
+
puts "\n#{tool.upcase}:"
|
393
|
+
|
394
|
+
if tool == "claude" && config[tool].is_a?(Hash)
|
395
|
+
config[tool].each do |auth_type, users|
|
396
|
+
next unless users.is_a?(Hash)
|
397
|
+
puts " #{auth_type}:"
|
398
|
+
users.each do |name, _file|
|
399
|
+
puts " - #{name}"
|
400
|
+
end
|
401
|
+
end
|
402
|
+
elsif config[tool].is_a?(Hash)
|
403
|
+
config[tool].each do |name, _file|
|
404
|
+
puts " - #{name}"
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
def verify_configuration
|
411
|
+
puts "\n验证配置状态..."
|
412
|
+
puts "=" * 60
|
413
|
+
|
414
|
+
# 检查目录
|
415
|
+
puts "\n目录检查:"
|
416
|
+
puts " 配置目录: #{@config_dir} - " + (Dir.exist?(@config_dir) ? "✓".green : "✗".red)
|
417
|
+
puts " 仓库目录: #{@repo_dir} - " + (repo_exists? ? "✓".green : "✗".red)
|
418
|
+
|
419
|
+
# 检查 Git 状态
|
420
|
+
if repo_exists?
|
421
|
+
puts "\nGit 状态:"
|
422
|
+
Dir.chdir(@repo_dir) do
|
423
|
+
branch = `git branch --show-current`.chomp
|
424
|
+
puts " 当前分支: #{branch}"
|
425
|
+
|
426
|
+
status = `git status --short`
|
427
|
+
if status.empty?
|
428
|
+
puts " 工作区: 干净 ✓".green
|
429
|
+
else
|
430
|
+
puts " 工作区: 有修改 ⚠️".yellow
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
# 检查配置文件
|
436
|
+
puts "\n配置文件:"
|
437
|
+
verify_basic_config
|
438
|
+
|
439
|
+
# 检查密码
|
440
|
+
puts "\n密码状态:"
|
441
|
+
if ENV['EASYAI_TEST_PASSWORD']
|
442
|
+
puts " 环境变量密码: ✓".green
|
443
|
+
elsif Base::SystemKeychain.get_stored_password
|
444
|
+
puts " 系统钥匙串密码: ✓".green
|
445
|
+
else
|
446
|
+
puts " 未设置密码 ⚠️".yellow
|
447
|
+
end
|
448
|
+
|
449
|
+
# 尝试加载用户
|
450
|
+
show_available_users(compact: true)
|
451
|
+
end
|
452
|
+
|
453
|
+
def cleanup_files
|
454
|
+
puts "\n清理临时文件..."
|
455
|
+
puts "=" * 60
|
456
|
+
|
457
|
+
# 清理解密的 JSON 文件
|
458
|
+
json_files = Dir.glob(File.join(@repo_dir, '**/*.json'))
|
459
|
+
cleanup_count = 0
|
460
|
+
|
461
|
+
json_files.each do |file|
|
462
|
+
encrypted_file = "#{file}.encrypted"
|
463
|
+
if File.exist?(encrypted_file)
|
464
|
+
FileUtils.rm_f(file)
|
465
|
+
cleanup_count += 1
|
466
|
+
puts " 删除: #{File.basename(file)}"
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
if cleanup_count > 0
|
471
|
+
puts "\n ✓ 清理了 #{cleanup_count} 个临时文件".green
|
472
|
+
else
|
473
|
+
puts " ✓ 没有需要清理的文件".green
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
def show_completion_info
|
478
|
+
puts "\n" + "=" * 60
|
479
|
+
puts "✅ " + "配置初始化完成!".green.bold
|
480
|
+
puts
|
481
|
+
puts "您可以开始使用以下命令:"
|
482
|
+
puts " • easyai claude".cyan + " - 启动 Claude"
|
483
|
+
puts " • easyai gemini".cyan + " - 启动 Gemini"
|
484
|
+
puts " • easyai gpt".cyan + " - 启动 GPT"
|
485
|
+
puts
|
486
|
+
puts "其他有用的命令:"
|
487
|
+
puts " • easyai setup --verify".cyan + " - 验证配置状态"
|
488
|
+
puts " • easyai setup --list-users".cyan + " - 查看可用用户"
|
489
|
+
puts " • easyai setup --clean".cyan + " - 清理临时文件"
|
490
|
+
puts
|
491
|
+
end
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|