easyai 1.7.0 → 2.0.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/AGENTS.md +10 -8
- data/CLAUDE.md +211 -126
- data/README.md +176 -36
- data/easyai.gemspec +12 -9
- data/lib/easyai/base/secret_masker.rb +39 -0
- data/lib/easyai/base/system_info.rb +17 -203
- data/lib/easyai/command/ai_tool_base.rb +218 -0
- data/lib/easyai/command/backup/claude.rb +124 -0
- data/lib/easyai/command/backup.rb +23 -0
- data/lib/easyai/command/claude.rb +72 -357
- data/lib/easyai/command/clean.rb +90 -395
- data/lib/easyai/command/codex.rb +39 -0
- data/lib/easyai/command/gemini.rb +23 -41
- data/lib/easyai/command/restore/claude.rb +150 -0
- data/lib/easyai/command/restore.rb +24 -0
- data/lib/easyai/command/setup.rb +487 -378
- data/lib/easyai/command/update.rb +39 -188
- data/lib/easyai/command/utils.rb +2 -7
- data/lib/easyai/command.rb +1 -3
- data/lib/easyai/config/local_config.rb +161 -0
- data/lib/easyai/version.rb +1 -1
- data/lib/easyai.rb +29 -35
- metadata +20 -37
- data/lib/easyai/auth/authclaude.rb +0 -519
- data/lib/easyai/auth/jpsloginhelper.rb +0 -98
- data/lib/easyai/base/system_keychain.rb +0 -283
- data/lib/easyai/command/gpt.rb +0 -259
- data/lib/easyai/command/utils/export.rb +0 -263
- data/lib/easyai/config/config.rb +0 -357
- data/lib/easyai/config/easyai_config.rb +0 -258
|
@@ -1,210 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
3
|
require 'colored2'
|
|
4
|
+
require_relative '../command'
|
|
4
5
|
|
|
5
6
|
module EasyAI
|
|
6
7
|
class Command
|
|
8
|
+
# 通过 RubyGems 自更新 EasyAI 自身。
|
|
9
|
+
#
|
|
10
|
+
# 设计要点(详见 docs/设计文档.md §2.7):
|
|
11
|
+
# - 用 system('gem', 'update', 'easyai') 触发 RubyGems 自更新
|
|
12
|
+
# - 不用 exec,便于子进程结束后继续打印中文提示
|
|
13
|
+
# - 启动时 EasyAIApp 识别到子命令为 update 会跳过强制版本阻塞,避免死循环
|
|
7
14
|
class Update < Command
|
|
8
|
-
self.summary = '
|
|
15
|
+
self.summary = '通过 RubyGems 更新 EasyAI 到最新发布版本'
|
|
9
16
|
self.description = <<-DESC
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
主要功能:
|
|
13
|
-
|
|
14
|
-
* 从远程仓库拉取最新代码
|
|
15
|
-
|
|
16
|
-
* 自动构建 gem 包
|
|
17
|
-
|
|
18
|
-
* 安装到本地系统
|
|
19
|
-
|
|
20
|
-
使用示例:
|
|
17
|
+
通过 `gem update easyai` 将 EasyAI 升级到 RubyGems 上的最新版本,
|
|
18
|
+
等价于手动执行该命令,但用 system 而非 exec,子进程结束后会打印中文成功 / 失败提示。
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
如果当前与 RubyGems 网络不通导致启动时被强制版本检查阻塞,可临时跳过:
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
$ EASYAI_SKIP_FORCE_UPDATE=1 easyai <子命令>
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
使用示例:
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
$ easyai update # 静默模式,仅打印必要提示
|
|
27
|
+
$ easyai update --verbose # 显示 gem 命令的完整输出
|
|
28
|
+
DESC
|
|
30
29
|
|
|
31
30
|
def self.options
|
|
32
31
|
[
|
|
33
|
-
['--
|
|
34
|
-
['--keep-source', '保留下载的源代码'],
|
|
35
|
-
['--verbose', '显示详细输出']
|
|
32
|
+
['--verbose', '显示 gem update 命令的完整输出']
|
|
36
33
|
].concat(super)
|
|
37
34
|
end
|
|
38
35
|
|
|
39
|
-
def initialize(argv)
|
|
40
|
-
@branch = argv.option('branch') || 'master'
|
|
41
|
-
@keep_source = argv.flag?('keep-source')
|
|
42
|
-
@verbose = argv.flag?('verbose')
|
|
43
|
-
super
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def validate!
|
|
47
|
-
super
|
|
48
|
-
help! '未找到 git 命令。请先安装 git。' unless git_available?
|
|
49
|
-
end
|
|
50
|
-
|
|
51
36
|
def run
|
|
52
|
-
puts "
|
|
53
|
-
puts "
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
# 创建临时目录
|
|
66
|
-
temp_dir = Dir.mktmpdir('easyai-update-')
|
|
67
|
-
|
|
68
|
-
begin
|
|
69
|
-
# 克隆仓库
|
|
70
|
-
clone_repository(temp_dir)
|
|
71
|
-
|
|
72
|
-
# 执行安装脚本
|
|
73
|
-
install_from_source(temp_dir)
|
|
74
|
-
|
|
75
|
-
# 显示成功信息
|
|
76
|
-
show_success_message
|
|
77
|
-
|
|
78
|
-
rescue => e
|
|
79
|
-
puts "\n❌ 更新失败: #{e.message}".red
|
|
80
|
-
puts " 请检查网络连接或稍后重试".red
|
|
81
|
-
exit 1
|
|
82
|
-
ensure
|
|
83
|
-
# 清理临时文件
|
|
84
|
-
cleanup_temp_files(temp_dir) unless @keep_source
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
private
|
|
89
|
-
|
|
90
|
-
def git_available?
|
|
91
|
-
system('which git > /dev/null 2>&1')
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def confirm_update?
|
|
95
|
-
print "确认从 Gitee 安装开发版本?[y/N]: ".yellow
|
|
96
|
-
begin
|
|
97
|
-
response = STDIN.gets
|
|
98
|
-
return false unless response
|
|
99
|
-
response = response.chomp.downcase
|
|
100
|
-
response == 'y' || response == 'yes'
|
|
101
|
-
rescue => e
|
|
102
|
-
puts "\n❌ 无法读取输入: #{e.message}".red
|
|
103
|
-
false
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def clone_repository(temp_dir)
|
|
108
|
-
repo_url = 'https://gitee.com/goodtools/EasyAI.git'
|
|
109
|
-
clone_path = File.join(temp_dir, 'EasyAI')
|
|
110
|
-
|
|
111
|
-
puts "📥 正在下载源代码...".cyan
|
|
112
|
-
puts " 仓库: #{repo_url}"
|
|
113
|
-
puts " 分支: #{@branch}"
|
|
114
|
-
|
|
115
|
-
# 构建 git clone 命令
|
|
116
|
-
git_cmd = "git clone --depth 1 --branch #{@branch} #{repo_url} #{clone_path}"
|
|
117
|
-
git_cmd += " > /dev/null 2>&1" unless @verbose
|
|
118
|
-
|
|
119
|
-
# 执行克隆
|
|
120
|
-
success = system(git_cmd)
|
|
121
|
-
|
|
122
|
-
unless success
|
|
123
|
-
raise "无法克隆仓库。请检查网络连接和分支名称。"
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
puts " ✓ 源代码下载完成".green
|
|
127
|
-
|
|
128
|
-
clone_path
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def install_from_source(temp_dir)
|
|
132
|
-
source_dir = File.join(temp_dir, 'EasyAI')
|
|
133
|
-
|
|
134
|
-
unless File.exist?(source_dir)
|
|
135
|
-
raise "源代码目录不存在: #{source_dir}"
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# 检查 test_local.sh 是否存在
|
|
139
|
-
install_script = File.join(source_dir, 'test_local.sh')
|
|
140
|
-
unless File.exist?(install_script)
|
|
141
|
-
raise "安装脚本不存在: test_local.sh"
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
puts "\n🔧 正在构建并安装...".cyan
|
|
145
|
-
|
|
146
|
-
# 切换到源代码目录并执行安装脚本
|
|
147
|
-
Dir.chdir(source_dir) do
|
|
148
|
-
# 确保脚本可执行
|
|
149
|
-
File.chmod(0755, 'test_local.sh')
|
|
150
|
-
|
|
151
|
-
# 执行安装脚本
|
|
152
|
-
install_cmd = "./test_local.sh"
|
|
153
|
-
install_cmd += " > /dev/null 2>&1" unless @verbose
|
|
154
|
-
|
|
155
|
-
success = system(install_cmd)
|
|
156
|
-
|
|
157
|
-
unless success
|
|
158
|
-
raise "安装脚本执行失败"
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
puts " ✓ 安装完成".green
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def cleanup_temp_files(temp_dir)
|
|
166
|
-
if @keep_source
|
|
167
|
-
puts "\n📁 源代码保留在: #{temp_dir}".cyan
|
|
168
|
-
else
|
|
169
|
-
puts "\n🧹 正在清理临时文件...".cyan
|
|
170
|
-
FileUtils.rm_rf(temp_dir)
|
|
171
|
-
puts " ✓ 清理完成".green
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
def show_success_message
|
|
176
|
-
# 获取安装后的版本
|
|
177
|
-
installed_version = get_installed_version
|
|
178
|
-
|
|
179
|
-
puts "\n" + "─" * 60
|
|
180
|
-
puts "✅ #{'开发版本安装成功!'.green}"
|
|
181
|
-
puts "─" * 60
|
|
182
|
-
|
|
183
|
-
if installed_version
|
|
184
|
-
puts "📌 当前版本: #{installed_version}"
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
puts "\n提示:".cyan
|
|
188
|
-
puts " • 这是开发版本,可能包含未发布的功能"
|
|
189
|
-
puts " • 如需回退到稳定版本: gem install easyai"
|
|
190
|
-
puts " • 查看版本信息: easyai --version"
|
|
191
|
-
|
|
192
|
-
if @branch != 'master'
|
|
193
|
-
puts "\n📝 已安装分支: #{@branch}".yellow
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def get_installed_version
|
|
198
|
-
# 尝试获取已安装的版本
|
|
199
|
-
output = `easyai --version 2>&1`
|
|
200
|
-
if output =~ /EasyAI 版本:\s*(\S+)/
|
|
201
|
-
$1
|
|
37
|
+
puts "开始更新 EasyAI...".cyan
|
|
38
|
+
puts "正在执行:gem update easyai".cyan
|
|
39
|
+
|
|
40
|
+
ok = if @verbose
|
|
41
|
+
system('gem', 'update', 'easyai')
|
|
42
|
+
else
|
|
43
|
+
system('gem', 'update', 'easyai', out: File::NULL, err: File::NULL)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if ok
|
|
47
|
+
puts "✓ 更新完成".green
|
|
48
|
+
puts "请重新打开终端,然后运行 `easyai --version` 查看新版本".cyan
|
|
202
49
|
else
|
|
203
|
-
|
|
50
|
+
puts "✗ 更新失败".red
|
|
51
|
+
puts "请尝试以下排查方式:".yellow
|
|
52
|
+
puts " • 检查网络连接是否能访问 https://rubygems.org"
|
|
53
|
+
puts " • 确认 RubyGems 源可用:gem sources -l"
|
|
54
|
+
puts " • 权限不足时尝试:sudo gem update easyai"
|
|
55
|
+
puts " • 重新执行并加 --verbose 查看详细输出"
|
|
56
|
+
exit($?.respond_to?(:exitstatus) && $?.exitstatus ? $?.exitstatus : 1)
|
|
204
57
|
end
|
|
205
|
-
rescue
|
|
206
|
-
nil
|
|
207
58
|
end
|
|
208
59
|
end
|
|
209
60
|
end
|
|
210
|
-
end
|
|
61
|
+
end
|
data/lib/easyai/command/utils.rb
CHANGED
|
@@ -4,22 +4,18 @@ module EasyAI
|
|
|
4
4
|
self.summary = '实用工具集合'
|
|
5
5
|
self.description = <<-DESC
|
|
6
6
|
提供各种实用工具。
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
可用命令:
|
|
9
9
|
|
|
10
10
|
* encry - 文件加密工具
|
|
11
11
|
|
|
12
12
|
* decry - 文件解密工具
|
|
13
13
|
|
|
14
|
-
* export - 导出 AI 配置信息
|
|
15
|
-
|
|
16
14
|
使用示例:
|
|
17
15
|
|
|
18
16
|
$ easyai utils encry file.txt # 加密文件
|
|
19
17
|
|
|
20
18
|
$ easyai utils decry file.encrypted # 解密文件
|
|
21
|
-
|
|
22
|
-
$ easyai utils export # 导出配置
|
|
23
19
|
DESC
|
|
24
20
|
|
|
25
21
|
self.abstract_command = true
|
|
@@ -33,5 +29,4 @@ end
|
|
|
33
29
|
|
|
34
30
|
# 加载子命令
|
|
35
31
|
require_relative 'utils/encry'
|
|
36
|
-
require_relative 'utils/decry'
|
|
37
|
-
require_relative 'utils/export'
|
|
32
|
+
require_relative 'utils/decry'
|
data/lib/easyai/command.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
require 'claide'
|
|
2
|
-
require 'yaml'
|
|
3
2
|
require 'fileutils'
|
|
4
3
|
require 'colored2'
|
|
5
|
-
require_relative 'base/system_keychain'
|
|
6
4
|
require_relative 'base/version_checker'
|
|
7
5
|
|
|
8
6
|
module EasyAI
|
|
@@ -11,7 +9,7 @@ module EasyAI
|
|
|
11
9
|
self.abstract_command = true
|
|
12
10
|
self.command = 'easyai'
|
|
13
11
|
self.version = VERSION
|
|
14
|
-
self.description = '简化 AI 命令行工具的 CLI 包装器,支持 Claude、Gemini 和
|
|
12
|
+
self.description = '简化 AI 命令行工具的 CLI 包装器,支持 Claude、Gemini 和 Codex'
|
|
15
13
|
self.plugin_prefixes = %w(easyai)
|
|
16
14
|
|
|
17
15
|
# 定义帮助标志访问器(类似 pindo)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
module EasyAI
|
|
7
|
+
module Config
|
|
8
|
+
# 本地配置门面:~/.easyai/config.json 的唯一读写入口
|
|
9
|
+
module LocalConfig
|
|
10
|
+
SCHEMA_VERSION = '2.0.0'
|
|
11
|
+
|
|
12
|
+
class Error < StandardError; end
|
|
13
|
+
class NotFoundError < Error; end
|
|
14
|
+
class ParseError < Error; end
|
|
15
|
+
class IncompatibleVersionError < Error; end
|
|
16
|
+
class ToolNotConfiguredError < Error; end
|
|
17
|
+
class PlatformNotFoundError < Error; end
|
|
18
|
+
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
# 注意:必须每次动态 expand_path,避免常量在加载期 freeze 路径,
|
|
22
|
+
# 让 spec 中切换 ENV['HOME'] 后定位失败
|
|
23
|
+
def config_path
|
|
24
|
+
File.expand_path('~/.easyai/config.json')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def exists?
|
|
28
|
+
File.file?(config_path)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def load
|
|
32
|
+
path = config_path
|
|
33
|
+
raise NotFoundError, "未找到本地配置文件 #{path},请运行 easyai setup" unless File.file?(path)
|
|
34
|
+
|
|
35
|
+
raw = File.read(path)
|
|
36
|
+
data = begin
|
|
37
|
+
JSON.parse(raw)
|
|
38
|
+
rescue JSON::ParserError => e
|
|
39
|
+
raise ParseError,
|
|
40
|
+
"配置解析失败 #{path}: #{e.message};可运行 easyai setup --reset 重新生成"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
version = data['version']
|
|
44
|
+
unless version == SCHEMA_VERSION
|
|
45
|
+
raise IncompatibleVersionError,
|
|
46
|
+
"配置版本 #{version.inspect} 不被支持,期望 #{SCHEMA_VERSION}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
data
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def save(cfg)
|
|
53
|
+
path = config_path
|
|
54
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
55
|
+
File.write(path, JSON.pretty_generate(cfg))
|
|
56
|
+
File.chmod(0o600, path) unless Gem.win_platform?
|
|
57
|
+
path
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
PLATFORM_DISPLAY_NAMES = {
|
|
61
|
+
'claude_official' => 'Claude 官方',
|
|
62
|
+
'deepseek' => 'DeepSeek',
|
|
63
|
+
'glm' => 'GLM(Coding Plan)',
|
|
64
|
+
'kimi' => 'Kimi',
|
|
65
|
+
'minimax' => 'MiniMax'
|
|
66
|
+
}.freeze
|
|
67
|
+
|
|
68
|
+
def platform_display_name(key)
|
|
69
|
+
PLATFORM_DISPLAY_NAMES.fetch(key, key)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def available_platforms(tool)
|
|
73
|
+
return [] unless exists?
|
|
74
|
+
|
|
75
|
+
cfg = load
|
|
76
|
+
platforms = cfg.dig(tool, 'platforms')
|
|
77
|
+
platforms.is_a?(Hash) ? platforms.keys : []
|
|
78
|
+
rescue Error
|
|
79
|
+
[]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# 解析平台配置。选定平台后会 yield 平台名(如果给了 block),
|
|
83
|
+
# 让上层(AIToolBase)负责打印 UI 提示,保持 LocalConfig 的输出最小化。
|
|
84
|
+
def resolve_platform(tool:, platform: nil, verbose: false)
|
|
85
|
+
cfg = load
|
|
86
|
+
tool_cfg = cfg[tool]
|
|
87
|
+
platforms = tool_cfg.is_a?(Hash) ? tool_cfg['platforms'] : nil
|
|
88
|
+
if !platforms.is_a?(Hash) || platforms.empty?
|
|
89
|
+
raise ToolNotConfiguredError,
|
|
90
|
+
"工具 #{tool} 未配置任何平台,请运行 easyai setup --tool=#{tool}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
available = platforms.keys
|
|
94
|
+
selected =
|
|
95
|
+
if platform && !platform.empty?
|
|
96
|
+
unless available.include?(platform)
|
|
97
|
+
raise PlatformNotFoundError,
|
|
98
|
+
"工具 #{tool} 未配置平台 #{platform}(可用:#{available.join(', ')})"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
platform
|
|
102
|
+
elsif available.size == 1
|
|
103
|
+
only = available.first
|
|
104
|
+
puts "使用平台 #{platform_display_name(only)}" if verbose
|
|
105
|
+
only
|
|
106
|
+
else
|
|
107
|
+
interactive_select(tool, available)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
yield(platform_display_name(selected)) if block_given?
|
|
111
|
+
platforms[selected]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def upsert_platform(tool, platform, data)
|
|
115
|
+
cfg = exists? ? load : { 'version' => SCHEMA_VERSION }
|
|
116
|
+
cfg['version'] ||= SCHEMA_VERSION
|
|
117
|
+
cfg[tool] ||= { 'platforms' => {} }
|
|
118
|
+
cfg[tool]['platforms'] ||= {}
|
|
119
|
+
cfg[tool]['platforms'][platform] = data
|
|
120
|
+
save(cfg)
|
|
121
|
+
cfg
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def delete_platform(tool, platform)
|
|
125
|
+
return unless exists?
|
|
126
|
+
|
|
127
|
+
cfg = load
|
|
128
|
+
platforms = cfg.dig(tool, 'platforms')
|
|
129
|
+
return cfg unless platforms.is_a?(Hash) && platforms.key?(platform)
|
|
130
|
+
|
|
131
|
+
platforms.delete(platform)
|
|
132
|
+
cfg.delete(tool) if platforms.empty?
|
|
133
|
+
save(cfg)
|
|
134
|
+
cfg
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# ----- private helpers -----
|
|
138
|
+
|
|
139
|
+
def interactive_select(tool, available)
|
|
140
|
+
puts "请选择平台(工具:#{tool}):"
|
|
141
|
+
available.each_with_index do |name, idx|
|
|
142
|
+
puts " #{idx + 1}) #{platform_display_name(name)}"
|
|
143
|
+
end
|
|
144
|
+
print '请输入序号 (q 取消): '
|
|
145
|
+
input = $stdin.gets
|
|
146
|
+
raise Interrupt, '用户取消平台选择' if input.nil?
|
|
147
|
+
|
|
148
|
+
input = input.chomp.strip
|
|
149
|
+
raise Interrupt, '用户取消平台选择' if input.empty? || input.casecmp('q').zero?
|
|
150
|
+
|
|
151
|
+
idx = Integer(input, 10) rescue nil
|
|
152
|
+
if idx.nil? || idx < 1 || idx > available.size
|
|
153
|
+
raise Interrupt, "非法输入:#{input}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
available[idx - 1]
|
|
157
|
+
end
|
|
158
|
+
private_class_method :interactive_select
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
data/lib/easyai/version.rb
CHANGED
data/lib/easyai.rb
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
require 'easyai/version'
|
|
2
|
-
require 'easyai/config/
|
|
3
|
-
require 'easyai/config/easyai_config'
|
|
2
|
+
require 'easyai/config/local_config'
|
|
4
3
|
require 'easyai/command'
|
|
4
|
+
require 'easyai/command/ai_tool_base'
|
|
5
5
|
require 'easyai/command/claude'
|
|
6
6
|
require 'easyai/command/gemini'
|
|
7
|
-
require 'easyai/command/
|
|
7
|
+
require 'easyai/command/codex'
|
|
8
8
|
require 'easyai/command/utils'
|
|
9
9
|
require 'easyai/command/clean'
|
|
10
10
|
require 'easyai/command/update'
|
|
11
11
|
require 'easyai/command/setup'
|
|
12
|
+
require 'easyai/command/backup'
|
|
13
|
+
require 'easyai/command/restore'
|
|
12
14
|
require 'easyai/base/version_checker'
|
|
13
15
|
require 'easyai/base/update_notifier'
|
|
14
16
|
require 'easyai/base/system_info'
|
|
@@ -30,14 +32,8 @@ module EasyAI
|
|
|
30
32
|
# 在运行命令前进行版本检查
|
|
31
33
|
check_version_before_run(argv)
|
|
32
34
|
|
|
33
|
-
# 设置退出时清理
|
|
34
|
-
setup_cleanup_handler
|
|
35
|
-
|
|
36
35
|
# 运行命令
|
|
37
36
|
EasyAI::Command.run(argv)
|
|
38
|
-
ensure
|
|
39
|
-
# 确保清理临时文件
|
|
40
|
-
cleanup_temp_files
|
|
41
37
|
end
|
|
42
38
|
|
|
43
39
|
private
|
|
@@ -64,21 +60,38 @@ module EasyAI
|
|
|
64
60
|
require 'claide'
|
|
65
61
|
require 'colored2'
|
|
66
62
|
coerced_argv = CLAide::ARGV.coerce(argv)
|
|
67
|
-
|
|
63
|
+
|
|
64
|
+
# 识别第一个非选项位置参数(子命令名)
|
|
65
|
+
# 注意:CLAide::ARGV#arguments 返回剩余的位置参数列表
|
|
66
|
+
first_subcommand = coerced_argv.arguments.first
|
|
67
|
+
|
|
68
68
|
# 如果是以下情况,跳过版本检查:
|
|
69
69
|
# 1. 请求帮助信息 (--help)
|
|
70
70
|
# 2. 显示版本信息 (--version)
|
|
71
71
|
# 3. 主动检查更新 (--check-update)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
# 4. 紧急跳过开关 (EASYAI_SKIP_FORCE_UPDATE=1)
|
|
73
|
+
# 5. 子命令为 update —— 避免"过期必须更新 → update 命令本身被阻塞"死循环
|
|
74
|
+
skip_reason =
|
|
75
|
+
if coerced_argv.flag?('help')
|
|
76
|
+
'--help'
|
|
77
|
+
elsif coerced_argv.flag?('version')
|
|
78
|
+
'--version'
|
|
79
|
+
elsif coerced_argv.flag?('check-update')
|
|
80
|
+
'--check-update'
|
|
81
|
+
elsif ENV['EASYAI_SKIP_FORCE_UPDATE']
|
|
82
|
+
'EASYAI_SKIP_FORCE_UPDATE=1'
|
|
83
|
+
elsif first_subcommand == 'update'
|
|
84
|
+
'子命令为 update'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
skip_check = !skip_reason.nil?
|
|
88
|
+
|
|
76
89
|
if ENV['EASYAI_DEBUG']
|
|
77
90
|
puts "📊 版本检查状态:".cyan
|
|
78
91
|
puts " • 当前版本: v#{EasyAI::VERSION}".green
|
|
79
|
-
puts " • 跳过检查: #{skip_check ?
|
|
92
|
+
puts " • 跳过检查: #{skip_check ? "是(#{skip_reason})".yellow : '否'.cyan}"
|
|
80
93
|
end
|
|
81
|
-
|
|
94
|
+
|
|
82
95
|
return if skip_check
|
|
83
96
|
|
|
84
97
|
# 强制更新检查(会阻塞执行)
|
|
@@ -97,24 +110,5 @@ module EasyAI
|
|
|
97
110
|
puts e.backtrace.join("\n") if ENV['EASYAI_DEBUG']
|
|
98
111
|
end
|
|
99
112
|
|
|
100
|
-
# 设置清理处理器
|
|
101
|
-
def setup_cleanup_handler
|
|
102
|
-
# 注册信号处理器,确保 Ctrl+C 等信号时也能清理
|
|
103
|
-
%w[INT TERM].each do |signal|
|
|
104
|
-
Signal.trap(signal) do
|
|
105
|
-
cleanup_temp_files
|
|
106
|
-
exit(130) # 标准的 Ctrl+C 退出码
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# 清理临时文件
|
|
112
|
-
def cleanup_temp_files
|
|
113
|
-
verbose = ENV['EASYAI_DEBUG'] || ENV['EASYAI_VERBOSE']
|
|
114
|
-
EasyAIConfig.cleanup(verbose)
|
|
115
|
-
rescue => e
|
|
116
|
-
# 清理失败不影响退出
|
|
117
|
-
puts "⚠️ 清理临时文件失败: #{e.message}" if ENV['EASYAI_DEBUG']
|
|
118
|
-
end
|
|
119
113
|
end
|
|
120
114
|
end
|
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:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Wade
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: claide
|
|
@@ -38,39 +38,19 @@ dependencies:
|
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '3.1'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
41
|
+
name: base64
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
46
|
+
version: '0.2'
|
|
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: '
|
|
54
|
-
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: jpsclient
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - "~>"
|
|
59
|
-
- !ruby/object:Gem::Version
|
|
60
|
-
version: '1.4'
|
|
61
|
-
- - ">="
|
|
62
|
-
- !ruby/object:Gem::Version
|
|
63
|
-
version: 1.4.0
|
|
64
|
-
type: :runtime
|
|
65
|
-
prerelease: false
|
|
66
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
67
|
-
requirements:
|
|
68
|
-
- - "~>"
|
|
69
|
-
- !ruby/object:Gem::Version
|
|
70
|
-
version: '1.4'
|
|
71
|
-
- - ">="
|
|
72
|
-
- !ruby/object:Gem::Version
|
|
73
|
-
version: 1.4.0
|
|
53
|
+
version: '0.2'
|
|
74
54
|
- !ruby/object:Gem::Dependency
|
|
75
55
|
name: bundler
|
|
76
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -113,8 +93,10 @@ dependencies:
|
|
|
113
93
|
- - "~>"
|
|
114
94
|
- !ruby/object:Gem::Version
|
|
115
95
|
version: '3.0'
|
|
116
|
-
description:
|
|
117
|
-
|
|
96
|
+
description: |
|
|
97
|
+
EasyAI 是一个 Ruby CLI 包装器,为 Anthropic Claude、Google Gemini、OpenAI Codex 三家命令行工具提供统一入口。
|
|
98
|
+
v2.0 起聚焦单机本地化:无远程下发链路,通过单一 ~/.easyai/config.json 集中管理多家 CLI 与多平台凭证、代理;
|
|
99
|
+
运行时仅向子进程注入环境变量,不污染当前 shell。
|
|
118
100
|
email:
|
|
119
101
|
- wade@example.com
|
|
120
102
|
executables:
|
|
@@ -128,27 +110,28 @@ files:
|
|
|
128
110
|
- bin/easyai
|
|
129
111
|
- easyai.gemspec
|
|
130
112
|
- lib/easyai.rb
|
|
131
|
-
- lib/easyai/auth/authclaude.rb
|
|
132
|
-
- lib/easyai/auth/jpsloginhelper.rb
|
|
133
113
|
- lib/easyai/base/cross_platform.rb
|
|
134
114
|
- lib/easyai/base/file_crypto.rb
|
|
115
|
+
- lib/easyai/base/secret_masker.rb
|
|
135
116
|
- lib/easyai/base/system_info.rb
|
|
136
|
-
- lib/easyai/base/system_keychain.rb
|
|
137
117
|
- lib/easyai/base/update_notifier.rb
|
|
138
118
|
- lib/easyai/base/version_checker.rb
|
|
139
119
|
- lib/easyai/command.rb
|
|
120
|
+
- lib/easyai/command/ai_tool_base.rb
|
|
121
|
+
- lib/easyai/command/backup.rb
|
|
122
|
+
- lib/easyai/command/backup/claude.rb
|
|
140
123
|
- lib/easyai/command/claude.rb
|
|
141
124
|
- lib/easyai/command/clean.rb
|
|
125
|
+
- lib/easyai/command/codex.rb
|
|
142
126
|
- lib/easyai/command/gemini.rb
|
|
143
|
-
- lib/easyai/command/
|
|
127
|
+
- lib/easyai/command/restore.rb
|
|
128
|
+
- lib/easyai/command/restore/claude.rb
|
|
144
129
|
- lib/easyai/command/setup.rb
|
|
145
130
|
- lib/easyai/command/update.rb
|
|
146
131
|
- lib/easyai/command/utils.rb
|
|
147
132
|
- lib/easyai/command/utils/decry.rb
|
|
148
133
|
- lib/easyai/command/utils/encry.rb
|
|
149
|
-
- lib/easyai/
|
|
150
|
-
- lib/easyai/config/config.rb
|
|
151
|
-
- lib/easyai/config/easyai_config.rb
|
|
134
|
+
- lib/easyai/config/local_config.rb
|
|
152
135
|
- lib/easyai/version.rb
|
|
153
136
|
homepage: https://github.com/wade/easyai
|
|
154
137
|
licenses:
|
|
@@ -161,14 +144,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
161
144
|
requirements:
|
|
162
145
|
- - ">="
|
|
163
146
|
- !ruby/object:Gem::Version
|
|
164
|
-
version:
|
|
147
|
+
version: 3.0.0
|
|
165
148
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
149
|
requirements:
|
|
167
150
|
- - ">="
|
|
168
151
|
- !ruby/object:Gem::Version
|
|
169
152
|
version: '0'
|
|
170
153
|
requirements: []
|
|
171
|
-
rubygems_version: 3.6.
|
|
154
|
+
rubygems_version: 3.6.9
|
|
172
155
|
specification_version: 4
|
|
173
|
-
summary:
|
|
156
|
+
summary: Claude / Gemini / Codex 三家 AI CLI 的本地化统一入口
|
|
174
157
|
test_files: []
|