easyai 1.0.6 → 1.0.9
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/lib/easyai/base/update_notifier.rb +58 -33
- data/lib/easyai/base/version_checker.rb +74 -51
- data/lib/easyai/command/claude.rb +0 -2
- data/lib/easyai/version.rb +1 -1
- data/lib/easyai.rb +7 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e62a795c1d9f823731c5efa0fdd2ccccece744164374c222cf42039eeab1d394
|
4
|
+
data.tar.gz: 8a937a49e3b619094a9af1beae643b9ede84f82e0f5cd38777e74e9f369711ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32ac98c88d9cf7ae7045a461d49d40f187c050f6d83c484c7a00c605b38ae94527f95ffc82229f70afd50e9ca3a8f6c79c6359eabad5173dbf0c2134d7e1a5c9
|
7
|
+
data.tar.gz: 2d0e460bdd981bd8b411557657b25ea7c2a62fb5f270be3af0fb20922245a4e063dff7afd8099bca1e9cf3ce6fbc440b1775081ac94b836254c52736b269ca06
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'time'
|
3
|
+
require 'colored2'
|
3
4
|
|
4
5
|
module EasyAI
|
5
6
|
module Base
|
6
7
|
class UpdateNotifier
|
7
|
-
|
8
|
+
VERSION_INFO_FILE = File.expand_path('~/.easyai/version_info.yml')
|
8
9
|
|
9
10
|
class << self
|
10
11
|
# 智能决定是否显示更新通知
|
@@ -13,7 +14,7 @@ module EasyAI
|
|
13
14
|
return unless should_show_notification?
|
14
15
|
|
15
16
|
notification = load_notification
|
16
|
-
return unless notification && notification['
|
17
|
+
return unless notification && notification['new_version_available']
|
17
18
|
|
18
19
|
# 使用简洁的单行提醒
|
19
20
|
show_brief_notification(notification)
|
@@ -25,7 +26,7 @@ module EasyAI
|
|
25
26
|
return if ENV['EASYAI_NO_UPDATE_CHECK']
|
26
27
|
|
27
28
|
notification = load_notification
|
28
|
-
return unless notification && notification['
|
29
|
+
return unless notification && notification['new_version_available']
|
29
30
|
return if notification['shown_today']
|
30
31
|
|
31
32
|
# 退出时显示稍微详细的信息
|
@@ -39,76 +40,100 @@ module EasyAI
|
|
39
40
|
notification = load_notification
|
40
41
|
return false unless notification
|
41
42
|
|
42
|
-
#
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
# 只要当前版本低于最新版本就提醒
|
44
|
+
current = EasyAI::VERSION # 始终使用实际运行的版本
|
45
|
+
latest = notification['latest_version']
|
46
|
+
|
47
|
+
# 使用版本比较逻辑
|
48
|
+
return newer_version?(latest, current) if latest && current
|
49
|
+
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
# 版本比较:检查 latest 是否比 current 新
|
54
|
+
def newer_version?(latest, current)
|
55
|
+
return false unless latest && current
|
56
|
+
|
57
|
+
# 解析版本号 (major.minor.patch)
|
58
|
+
latest_parts = latest.split('.').map(&:to_i)
|
59
|
+
current_parts = current.split('.').map(&:to_i)
|
60
|
+
|
61
|
+
# 比较版本号
|
62
|
+
(0..2).each do |i|
|
63
|
+
latest_part = latest_parts[i] || 0
|
64
|
+
current_part = current_parts[i] || 0
|
46
65
|
|
47
|
-
|
48
|
-
if
|
49
|
-
return hours_since > 6 # 主版本更新:6小时提醒一次
|
50
|
-
elsif minor_update?(notification)
|
51
|
-
return hours_since > 24 # 次版本更新:24小时提醒一次
|
52
|
-
else
|
53
|
-
return hours_since > 72 # 补丁更新:72小时提醒一次
|
54
|
-
end
|
66
|
+
return true if latest_part > current_part
|
67
|
+
return false if latest_part < current_part
|
55
68
|
end
|
56
69
|
|
57
|
-
|
70
|
+
false
|
58
71
|
end
|
59
72
|
|
60
73
|
def major_update?(notification)
|
61
|
-
return false unless notification['latest_version']
|
74
|
+
return false unless notification['latest_version']
|
62
75
|
|
63
76
|
latest_major = notification['latest_version'].split('.')[0].to_i
|
64
|
-
current_major =
|
77
|
+
current_major = EasyAI::VERSION.split('.')[0].to_i
|
65
78
|
|
66
79
|
latest_major > current_major
|
67
80
|
end
|
68
81
|
|
69
82
|
def minor_update?(notification)
|
70
|
-
return false unless notification['latest_version']
|
83
|
+
return false unless notification['latest_version']
|
71
84
|
|
72
85
|
latest_parts = notification['latest_version'].split('.').map(&:to_i)
|
73
|
-
current_parts =
|
86
|
+
current_parts = EasyAI::VERSION.split('.').map(&:to_i)
|
74
87
|
|
75
88
|
latest_parts[0] == current_parts[0] && latest_parts[1] > current_parts[1]
|
76
89
|
end
|
77
90
|
|
78
91
|
def show_brief_notification(notification)
|
79
92
|
latest = notification['latest_version']
|
80
|
-
current =
|
93
|
+
current = EasyAI::VERSION
|
81
94
|
|
82
|
-
#
|
95
|
+
# 醒目的框形提醒
|
83
96
|
if major_update?(notification)
|
84
|
-
|
97
|
+
# 主版本更新使用红色警告框
|
98
|
+
puts ""
|
99
|
+
puts "┌─────────────────────────────────────────────────────┐".red
|
100
|
+
puts "│ 🚨 重要更新: EasyAI #{current} → #{latest} │".red
|
101
|
+
puts "│ 更新命令: gem update easyai │".red
|
102
|
+
puts "└─────────────────────────────────────────────────────┘".red
|
85
103
|
else
|
86
|
-
|
104
|
+
# 普通更新使用黄色提示框
|
105
|
+
puts ""
|
106
|
+
puts "┌─────────────────────────────────────────────────────┐".yellow
|
107
|
+
puts "│ 📦 新版本可用: EasyAI #{current} → #{latest} │".yellow
|
108
|
+
puts "│ 更新命令: gem update easyai │".yellow
|
109
|
+
puts "└─────────────────────────────────────────────────────┘".yellow
|
87
110
|
end
|
111
|
+
puts ""
|
88
112
|
end
|
89
113
|
|
90
114
|
def show_exit_notification(notification)
|
91
115
|
latest = notification['latest_version']
|
92
|
-
current =
|
116
|
+
current = EasyAI::VERSION
|
93
117
|
|
94
118
|
puts "\n" + "─" * 50
|
95
119
|
puts "📦 EasyAI 更新提醒".yellow
|
96
120
|
puts " 当前版本: #{current}"
|
97
121
|
puts " 最新版本: #{latest}".green
|
98
122
|
puts " 更新命令: gem update easyai"
|
99
|
-
puts "
|
123
|
+
puts " 安装命令: gem install easyai"
|
100
124
|
puts "─" * 50
|
101
125
|
end
|
102
126
|
|
103
127
|
def load_notification
|
104
|
-
return nil unless File.exist?(
|
105
|
-
YAML.load_file(
|
128
|
+
return nil unless File.exist?(VERSION_INFO_FILE)
|
129
|
+
YAML.load_file(VERSION_INFO_FILE) rescue nil
|
106
130
|
end
|
107
131
|
|
108
132
|
def update_shown_time
|
109
|
-
|
110
|
-
|
111
|
-
|
133
|
+
# 读取完整的版本信息,只更新显示时间
|
134
|
+
data = load_notification || {}
|
135
|
+
data['last_shown_at'] = Time.now.to_s
|
136
|
+
save_notification(data)
|
112
137
|
end
|
113
138
|
|
114
139
|
def mark_as_shown_today
|
@@ -118,8 +143,8 @@ module EasyAI
|
|
118
143
|
end
|
119
144
|
|
120
145
|
def save_notification(data)
|
121
|
-
FileUtils.mkdir_p(File.dirname(
|
122
|
-
File.write(
|
146
|
+
FileUtils.mkdir_p(File.dirname(VERSION_INFO_FILE))
|
147
|
+
File.write(VERSION_INFO_FILE, data.to_yaml)
|
123
148
|
rescue
|
124
149
|
nil
|
125
150
|
end
|
@@ -9,11 +9,28 @@ module EasyAI
|
|
9
9
|
module Base
|
10
10
|
class VersionChecker
|
11
11
|
RUBYGEMS_API = 'https://rubygems.org/api/v1/gems/easyai.json'
|
12
|
-
|
13
|
-
CHECK_INTERVAL =
|
12
|
+
VERSION_INFO_FILE = File.expand_path('~/.easyai/version_info.yml')
|
13
|
+
CHECK_INTERVAL = 5 * 60 * 60 # 5小时检查一次
|
14
14
|
|
15
15
|
class << self
|
16
|
-
#
|
16
|
+
# 同步检查版本(在当前进程中执行)
|
17
|
+
def check_sync(cached_latest_version = nil)
|
18
|
+
puts " 🔄 执行同步版本检查...".cyan if ENV['EASYAI_DEBUG']
|
19
|
+
|
20
|
+
return unless should_check?
|
21
|
+
|
22
|
+
puts " 📡 当前进程中检查版本...".green if ENV['EASYAI_DEBUG']
|
23
|
+
|
24
|
+
# 如果已经有缓存的版本信息,直接使用,避免重复网络请求
|
25
|
+
if cached_latest_version
|
26
|
+
puts " ♻️ 复用已获取的版本: #{cached_latest_version}".green if ENV['EASYAI_DEBUG']
|
27
|
+
check_and_cache_with_version(cached_latest_version)
|
28
|
+
else
|
29
|
+
check_and_cache
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# 异步检查版本(不阻塞主程序)- 保留备用
|
17
34
|
def check_async
|
18
35
|
puts " 🔮 启动异步版本检查...".cyan if ENV['EASYAI_DEBUG']
|
19
36
|
|
@@ -41,10 +58,10 @@ module EasyAI
|
|
41
58
|
return false if ENV['EASYAI_SKIP_FORCE_UPDATE'] # 紧急跳过选项
|
42
59
|
|
43
60
|
# 检查缓存
|
44
|
-
if File.exist?(
|
45
|
-
cache = YAML.load_file(
|
61
|
+
if File.exist?(VERSION_INFO_FILE)
|
62
|
+
cache = YAML.load_file(VERSION_INFO_FILE) rescue {}
|
46
63
|
latest = cache['latest_version']
|
47
|
-
current =
|
64
|
+
current = EasyAI::VERSION # 直接使用实际版本
|
48
65
|
|
49
66
|
return needs_major_or_minor_update?(latest, current)
|
50
67
|
end
|
@@ -54,13 +71,13 @@ module EasyAI
|
|
54
71
|
false
|
55
72
|
end
|
56
73
|
|
57
|
-
#
|
74
|
+
# 强制更新检查(同步),返回获取到的最新版本
|
58
75
|
def check_force_update!
|
59
76
|
puts " 📌 执行强制更新检查...".cyan if ENV['EASYAI_DEBUG']
|
60
77
|
|
61
78
|
# 如果缓存中已有需要强制更新的信息,直接使用
|
62
79
|
if require_force_update?
|
63
|
-
cache = YAML.load_file(
|
80
|
+
cache = YAML.load_file(VERSION_INFO_FILE) rescue {}
|
64
81
|
show_force_update_message(cache['latest_version'], EasyAI::VERSION)
|
65
82
|
exit(1)
|
66
83
|
end
|
@@ -75,9 +92,17 @@ module EasyAI
|
|
75
92
|
show_force_update_message(latest, EasyAI::VERSION)
|
76
93
|
exit(1)
|
77
94
|
end
|
95
|
+
return latest # 返回获取到的版本,供后续使用
|
78
96
|
else
|
79
97
|
puts " 💾 使用缓存(未过期)".cyan if ENV['EASYAI_DEBUG']
|
98
|
+
# 从缓存中获取版本信息
|
99
|
+
if File.exist?(VERSION_INFO_FILE)
|
100
|
+
cache = YAML.load_file(VERSION_INFO_FILE) rescue {}
|
101
|
+
return cache['latest_version']
|
102
|
+
end
|
80
103
|
end
|
104
|
+
|
105
|
+
nil
|
81
106
|
end
|
82
107
|
|
83
108
|
# 同步检查版本(用于 --check-update 命令)
|
@@ -99,9 +124,9 @@ module EasyAI
|
|
99
124
|
|
100
125
|
# 显示缓存的更新提醒(如果有)
|
101
126
|
def show_cached_reminder
|
102
|
-
return unless File.exist?(
|
127
|
+
return unless File.exist?(VERSION_INFO_FILE)
|
103
128
|
|
104
|
-
cache = YAML.load_file(
|
129
|
+
cache = YAML.load_file(VERSION_INFO_FILE) rescue {}
|
105
130
|
return unless cache['new_version_available']
|
106
131
|
return if cache['reminder_shown_at'] &&
|
107
132
|
Time.now - Time.parse(cache['reminder_shown_at']) < 3600 # 1小时内不重复提醒
|
@@ -124,8 +149,8 @@ module EasyAI
|
|
124
149
|
return false if ENV['EASYAI_NO_UPDATE_CHECK'] # 允许用户禁用
|
125
150
|
|
126
151
|
# 检查缓存文件
|
127
|
-
if File.exist?(
|
128
|
-
cache = YAML.load_file(
|
152
|
+
if File.exist?(VERSION_INFO_FILE)
|
153
|
+
cache = YAML.load_file(VERSION_INFO_FILE) rescue {}
|
129
154
|
last_check = cache['last_check_at']
|
130
155
|
if last_check
|
131
156
|
time_since_check = Time.now - Time.parse(last_check)
|
@@ -140,67 +165,66 @@ module EasyAI
|
|
140
165
|
latest = fetch_latest_version
|
141
166
|
return unless latest
|
142
167
|
|
168
|
+
check_and_cache_with_version(latest)
|
169
|
+
rescue => e
|
170
|
+
# 静默失败,不影响主程序
|
171
|
+
nil
|
172
|
+
end
|
173
|
+
|
174
|
+
# 使用已知的最新版本更新缓存
|
175
|
+
def check_and_cache_with_version(latest)
|
176
|
+
return unless latest
|
177
|
+
|
178
|
+
# 不记录 current_version,运行时直接使用 EasyAI::VERSION 比较
|
143
179
|
current = EasyAI::VERSION
|
144
180
|
is_new = newer_version?(latest, current)
|
145
181
|
|
182
|
+
# 读取现有文件以保留提醒显示时间
|
183
|
+
existing_data = {}
|
184
|
+
if File.exist?(VERSION_INFO_FILE)
|
185
|
+
existing_data = YAML.load_file(VERSION_INFO_FILE) rescue {}
|
186
|
+
end
|
187
|
+
|
146
188
|
cache = {
|
147
189
|
'last_check_at' => Time.now.to_s,
|
148
190
|
'latest_version' => latest,
|
149
|
-
'
|
150
|
-
'
|
191
|
+
'new_version_available' => is_new,
|
192
|
+
'last_shown_at' => existing_data['last_shown_at'], # 保留上次显示时间
|
193
|
+
'shown_today' => existing_data['shown_today'] # 保留今日显示标记
|
151
194
|
}
|
152
195
|
|
153
196
|
save_cache(cache)
|
154
|
-
|
155
|
-
# 同时更新通知文件
|
156
|
-
if is_new
|
157
|
-
update_notification(latest, current)
|
158
|
-
end
|
159
197
|
rescue => e
|
160
198
|
# 静默失败,不影响主程序
|
161
199
|
nil
|
162
200
|
end
|
163
201
|
|
164
|
-
def update_notification(latest, current)
|
165
|
-
notification_file = File.expand_path('~/.easyai/update_notification.yml')
|
166
|
-
notification = {
|
167
|
-
'available' => true,
|
168
|
-
'latest_version' => latest,
|
169
|
-
'current_version' => current,
|
170
|
-
'checked_at' => Time.now.to_s
|
171
|
-
}
|
172
|
-
|
173
|
-
# 保留上次显示时间
|
174
|
-
if File.exist?(notification_file)
|
175
|
-
old_data = YAML.load_file(notification_file) rescue {}
|
176
|
-
notification['last_shown_at'] = old_data['last_shown_at']
|
177
|
-
notification['shown_today'] = old_data['shown_today']
|
178
|
-
end
|
179
|
-
|
180
|
-
FileUtils.mkdir_p(File.dirname(notification_file))
|
181
|
-
File.write(notification_file, notification.to_yaml)
|
182
|
-
rescue
|
183
|
-
nil
|
184
|
-
end
|
185
202
|
|
186
203
|
def fetch_latest_version
|
187
204
|
uri = URI(RUBYGEMS_API)
|
188
205
|
|
189
|
-
|
206
|
+
# 创建 HTTP 连接,禁用代理
|
207
|
+
http = Net::HTTP.new(uri.host, uri.port, nil, nil) # 后两个参数为 proxy_addr, proxy_port,设为 nil 禁用代理
|
190
208
|
http.use_ssl = true
|
191
|
-
http.open_timeout =
|
192
|
-
http.read_timeout =
|
209
|
+
http.open_timeout = 3 # 快速超时
|
210
|
+
http.read_timeout = 3
|
211
|
+
|
212
|
+
puts " 🌐 直连 RubyGems API (跳过代理)...".cyan if ENV['EASYAI_DEBUG']
|
193
213
|
|
194
214
|
request = Net::HTTP::Get.new(uri)
|
195
215
|
response = http.request(request)
|
196
216
|
|
197
217
|
if response.code == '200'
|
198
218
|
data = JSON.parse(response.body)
|
199
|
-
data['version']
|
219
|
+
version = data['version']
|
220
|
+
puts " ✅ 获取到最新版本: #{version}".green if ENV['EASYAI_DEBUG']
|
221
|
+
version
|
200
222
|
else
|
223
|
+
puts " ❌ API 响应错误: #{response.code}".yellow if ENV['EASYAI_DEBUG']
|
201
224
|
nil
|
202
225
|
end
|
203
226
|
rescue => e
|
227
|
+
puts " ⚠️ 版本获取失败: #{e.message}".yellow if ENV['EASYAI_DEBUG']
|
204
228
|
nil # 网络错误时静默失败
|
205
229
|
end
|
206
230
|
|
@@ -241,16 +265,16 @@ module EasyAI
|
|
241
265
|
|
242
266
|
# 是否应该立即检查
|
243
267
|
def should_check_immediately?
|
244
|
-
return true unless File.exist?(
|
268
|
+
return true unless File.exist?(VERSION_INFO_FILE)
|
245
269
|
|
246
|
-
cache = YAML.load_file(
|
270
|
+
cache = YAML.load_file(VERSION_INFO_FILE) rescue {}
|
247
271
|
last_check = cache['last_check_at']
|
248
272
|
|
249
|
-
#
|
273
|
+
# 如果没有检查记录或缓存超过指定时间间隔,立即检查
|
250
274
|
return true unless last_check
|
251
275
|
|
252
276
|
time_since_check = Time.now - Time.parse(last_check)
|
253
|
-
time_since_check >
|
277
|
+
time_since_check > CHECK_INTERVAL
|
254
278
|
end
|
255
279
|
|
256
280
|
# 更新缓存(强制更新场景)
|
@@ -258,7 +282,6 @@ module EasyAI
|
|
258
282
|
cache = {
|
259
283
|
'last_check_at' => Time.now.to_s,
|
260
284
|
'latest_version' => latest,
|
261
|
-
'current_version' => current,
|
262
285
|
'new_version_available' => true,
|
263
286
|
'force_update_required' => true
|
264
287
|
}
|
@@ -266,8 +289,8 @@ module EasyAI
|
|
266
289
|
end
|
267
290
|
|
268
291
|
def save_cache(data)
|
269
|
-
FileUtils.mkdir_p(File.dirname(
|
270
|
-
File.write(
|
292
|
+
FileUtils.mkdir_p(File.dirname(VERSION_INFO_FILE))
|
293
|
+
File.write(VERSION_INFO_FILE, data.to_yaml)
|
271
294
|
rescue => e
|
272
295
|
# 静默失败
|
273
296
|
nil
|
data/lib/easyai/version.rb
CHANGED
data/lib/easyai.rb
CHANGED
@@ -8,6 +8,7 @@ require 'easyai/command/utils'
|
|
8
8
|
require 'easyai/command/clean'
|
9
9
|
require 'easyai/command/update'
|
10
10
|
require 'easyai/base/version_checker'
|
11
|
+
require 'easyai/base/update_notifier'
|
11
12
|
|
12
13
|
module EasyAI
|
13
14
|
# EasyAI 应用程序主类
|
@@ -72,10 +73,13 @@ module EasyAI
|
|
72
73
|
return if skip_check
|
73
74
|
|
74
75
|
# 强制更新检查(会阻塞执行)
|
75
|
-
Base::VersionChecker.check_force_update!
|
76
|
+
latest_version = Base::VersionChecker.check_force_update!
|
76
77
|
|
77
|
-
#
|
78
|
-
Base::VersionChecker.
|
78
|
+
# 同步更新检查(在当前进程中执行,复用已获取的版本)
|
79
|
+
Base::VersionChecker.check_sync(latest_version)
|
80
|
+
|
81
|
+
# 显示更新提醒(如果有新版本)
|
82
|
+
Base::UpdateNotifier.maybe_show_notification
|
79
83
|
|
80
84
|
puts "✅ #{'版本检查完成'.green}" if ENV['EASYAI_DEBUG']
|
81
85
|
rescue => e
|